Initial CST-Agent: portable Linux/Termux/Windows coding agent.

Vendored sashidown, OpenAI-compatible env (OPENAI_*), and bootstrap scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-09-07 22:42:32 +08:00
co-authored by Cursor
commit 1f1fcba7ad
75 changed files with 14940 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
#ifndef CST_AGENT_H
#define CST_AGENT_H
#include "ctl.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CST_AGENT_MAX_MSG 256
#define CST_AGENT_MAX_CONTENT (512 * 1024)
#define CST_AGENT_CTX_WINDOW 40 /* max non-system msgs sent to LLM */
typedef enum {
CST_ROLE_SYSTEM = 0,
CST_ROLE_USER,
CST_ROLE_ASSISTANT,
CST_ROLE_TOOL
} cst_role_t;
typedef struct {
cst_role_t role;
char *content;
char tool_call_id[CTL_MAX_ID]; /* for tool results */
char tool_name[CTL_MAX_NAME];
char *tool_calls_json; /* assistant: OpenAI tool_calls array JSON, or NULL */
} cst_message_t;
typedef struct {
char workspace_root[4096];
char session_path[4096];
char api_base[512];
char api_key[512];
char model[128];
int headless;
int confirm_shell; /* confirm run_shell */
int confirm_write; /* confirm write_file / apply_patch */
int auto_approve; /* headless / CST_AUTO_APPROVE=1 */
} cst_agent_config_t;
typedef struct cst_session cst_session_t;
typedef struct cst_agent cst_agent_t;
/* --- sandbox --- */
int cst_sandbox_resolve(const char *workspace_root, const char *rel_or_abs,
char *out, size_t out_sz);
/* --- session (append-only JSONL) --- */
cst_session_t *cst_session_open(const char *path);
void cst_session_close(cst_session_t *s);
int cst_session_append(cst_session_t *s, const cst_message_t *msg);
int cst_session_count(const cst_session_t *s);
const cst_message_t *cst_session_get(const cst_session_t *s, int i);
const char *cst_session_path(const cst_session_t *s);
int cst_session_clear(cst_session_t *s, int keep_system);
int cst_session_rewrite(cst_session_t *s, const cst_message_t *msgs, int n);
/* --- workspace .agent/sessions store (OpenCode-like) --- */
int cst_agent_store_ensure(const char *workspace);
int cst_agent_store_dir(const char *workspace, char *out, size_t out_sz);
int cst_agent_store_new_id(char *id_out, size_t id_sz);
int cst_agent_store_path_for_id(const char *workspace, const char *id,
char *out, size_t out_sz);
int cst_agent_store_set_current(const char *workspace, const char *id);
int cst_agent_store_get_current(const char *workspace, char *id_out,
size_t id_sz);
int cst_agent_store_resolve_default(const char *workspace, char *path_out,
size_t path_sz, char *id_out,
size_t id_sz);
int cst_agent_store_list(const char *workspace, char ids[][128], int max_ids);
/* --- tools --- */
typedef struct {
char *out;
size_t out_len;
int exit_code;
} cst_tool_result_t;
void cst_tool_result_free(cst_tool_result_t *r);
int cst_tool_read_file(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r);
int cst_tool_run_shell(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r);
int cst_tool_apply_patch(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r);
int cst_tool_write_file(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r);
int cst_tool_dispatch(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r);
/* Build ctl_tool_call from native OpenAI tool_call fields. */
int cst_tool_call_from_native(ctl_tool_call_t *out, const char *id,
const char *name, const char *args_json);
/* --- LLM --- */
typedef void (*cst_llm_on_delta_fn)(const char *data, size_t len, void *ud);
typedef void (*cst_llm_on_native_tool_fn)(const char *id, const char *name,
const char *args_json, void *ud);
typedef struct {
const char *api_base;
const char *api_key;
const char *model;
const cst_message_t *msgs;
int nmsgs;
cst_llm_on_delta_fn on_delta;
cst_llm_on_native_tool_fn on_native_tool; /* fired once per completed call */
void *userdata;
int enable_tools; /* send tools[] schema */
int tool_choice_none; /* "tool_choice":"none" */
volatile int *cancel_flag; /* non-zero → abort curl */
} cst_llm_request_t;
int cst_llm_chat_stream(const cst_llm_request_t *req, char *err, size_t err_sz);
/* Live UI / observability hooks for a turn */
typedef struct {
void (*on_status)(const char *msg, void *ud);
void (*on_assistant_delta)(const char *text, size_t len, void *ud);
void (*on_tool_event)(const char *phase, const ctl_tool_call_t *call,
const char *detail, void *ud);
void (*on_think_delta)(const char *text, size_t len, void *ud);
/* Return 1=allow, 0=deny. NULL → use config auto_approve / defaults. */
int (*on_permission)(const char *tool_name, const char *detail, void *ud);
void *userdata;
} cst_agent_hooks_t;
/* --- agent loop --- */
cst_agent_t *cst_agent_create(const cst_agent_config_t *cfg);
void cst_agent_destroy(cst_agent_t *a);
int cst_agent_run_turn(cst_agent_t *a, const char *user_text, char *err,
size_t err_sz);
int cst_agent_run_turn_ex(cst_agent_t *a, const char *user_text,
const cst_agent_hooks_t *hooks, char *err,
size_t err_sz);
cst_session_t *cst_agent_session(cst_agent_t *a);
const cst_agent_config_t *cst_agent_config(const cst_agent_t *a);
cst_agent_config_t *cst_agent_config_mut(cst_agent_t *a);
void cst_agent_request_cancel(cst_agent_t *a);
int cst_agent_new_session(cst_agent_t *a, char *id_out, size_t id_sz);
int cst_agent_switch_session(cst_agent_t *a, const char *id);
int cst_agent_compact(cst_agent_t *a, char *err, size_t err_sz);
const char *cst_agent_system_prompt(void);
/* Credentials: zenity + ~/.config/cst-agent/credentials (0600) */
int cst_creds_load_api_key(char *out, size_t out_sz);
int cst_creds_save_api_key(const char *key);
int cst_creds_prompt_api_key(char *out, size_t out_sz);
int cst_creds_ensure_api_key(char *out, size_t out_sz);
/* JSON baseline parser for experiments (buffer until valid object). */
typedef struct {
int ok;
char name[CTL_MAX_NAME];
char arguments_json[65536];
} cst_json_tool_t;
int cst_json_try_parse_tool(const char *buf, size_t len, cst_json_tool_t *out);
/* Built-in / CST_MODELS model picker list. Writes up to max_out names.
* Always includes `current` if non-empty and not already listed. */
int cst_models_list(const char *current, char out[][128], int max_out);
int cst_tui_run(cst_agent_t *agent);
#ifdef __cplusplus
}
#endif
#endif /* CST_AGENT_H */