#ifndef CST_AGENT_H #define CST_AGENT_H #include "ctl.h" #include #include #ifdef __cplusplus extern "C" { #endif #define CST_AGENT_MAX_MSG 256 #define CST_AGENT_MAX_CONTENT (512 * 1024) #define CST_DEFAULT_MAX_STEPS 30 #define CST_DEFAULT_TOOL_OUT_CAP (16 * 1024) typedef enum { CST_ROLE_SYSTEM = 0, CST_ROLE_USER, CST_ROLE_ASSISTANT, CST_ROLE_TOOL } cst_role_t; /* How tool calls travel between model and runtime. * native-eos : OpenAI tools[]; execute after the stream ends (baseline). * native-inc : OpenAI tools[]; execute as soon as one call's arguments are * complete (next index started / finish_reason). * ctl : CTL frames in plain text, length-prefixed args; execute on * confirmed `end` while the stream is still running. * ctl-heredoc: same, prompt teaches `arg < text; execute after the stream. */ typedef enum { CST_PROTO_NATIVE_EOS = 0, CST_PROTO_NATIVE_INC, CST_PROTO_CTL, CST_PROTO_CTL_HEREDOC, CST_PROTO_XML } cst_protocol_t; const char *cst_protocol_name(cst_protocol_t p); int cst_protocol_parse(const char *s, cst_protocol_t *out); int cst_protocol_is_native(cst_protocol_t p); int cst_protocol_is_ctl(cst_protocol_t p); /* Text protocols carry calls/results in message text (no tools[]). */ int cst_protocol_is_text(cst_protocol_t p); /* Eager protocols execute a call during the stream, at confirmation time. */ int cst_protocol_is_eager(cst_protocol_t p); /* One file carried by a message (see cst_attach.h): an image, or a document * whose stored payload is its text. The payload lives beside the session; * this is the metadata plus the name of the stored copy. */ typedef struct { char id[32]; /* unique inside the session ("img-1a2b3c4d") */ char name[256]; /* display name (original file name) */ char mime[32]; /* image/png, ...; application/pdf for a PDF whose text * layer the stored file holds; text/plain for text */ char rel[256]; /* file name inside the session's attachment directory */ long long bytes; /* stored size (the text, for documents) */ int width, height; /* pixels, 0 when the header could not be read */ int pages; /* PDF page count, 0 for anything else */ char detail[8]; /* auto | low | high (wire hint, "" = auto) */ char origin[32]; /* user | view_image | mcp (for display only) */ /* A delivered file (deliver_file): the front end shows it as a card the * person can open or share, and no request ever carries it. The stored copy * is the file as it was, not a text reduction -- see cst_attach.h. */ int for_user; } cst_attachment_t; typedef struct { cst_role_t role; char *content; char tool_call_id[CTL_MAX_ID]; /* tool results (role=tool, or text-protocol result frames sent as role=user) */ char tool_name[CTL_MAX_NAME]; char *tool_calls_json; /* assistant: OpenAI tool_calls array JSON, or NULL */ char *meta_json; /* optional JSON object (kind, protocol, ...) */ cst_attachment_t *attach; /* images shown with this message, or NULL */ int nattach; char *reasoning; /* assistant: reasoning_content as streamed, or NULL */ int prefix; /* wire only: assistant prefix the model continues */ } cst_message_t; typedef struct { char workspace_root[4096]; char session_path[4096]; char api_base[512]; char api_key[512]; char model[128]; char provider[32]; char effort[16]; /* low | medium | high | max */ char mode[16]; /* coding | general (CST_DEFAULT_MODE) */ cst_protocol_t protocol; int max_steps; /* model calls per user turn */ size_t tool_out_cap; /* bytes of one tool output sent to the model */ /* What the model is asked to do (Chat Completions fields). 0 = the field * is not sent at all and the provider's own default stands; that is what a * config that was not told otherwise has, so a CLI run is unchanged. * temperature and top_p are doubles, max_tokens the completion cap. */ double temperature; double top_p; int max_tokens; int headless; int confirm_shell; /* confirm run_shell */ int confirm_write; /* confirm file-changing tools */ int confirm_net; /* confirm web_fetch */ int auto_approve; /* headless / CST_AUTO_APPROVE=1 / --yolo */ int approval; /* cst_approval_t (interactive sessions) */ int plan_mode; /* read-only tools + planning note */ int tool_profile; /* cst_tool_profile_t (CST_TOOLS) */ char tools_off[1024]; /* ",name,name," tools switched off (/tools off) */ /* Sub-agents (task tool). */ int depth; /* 0 = top-level; sub-agents run at 1 */ int orchestrate; /* coordinator delegates write/shell work */ struct cst_runtime *runtime; /* V2 task controller; parent-owned */ int orchestrate_override; /* CLI: 1 force on, -1 force off */ int subagent_steps, subagent_tasks, subagent_total; void *worker_budget; /* owned by the top-level agent */ int (*request_gate)(void *); /* reserve budget before each HTTP attempt */ void *request_gate_ud; unsigned long task_revision; char cache_namespace[128]; int read_only; /* no write / shell tools */ char tools_only[1024]; /* ",a,b," allowlist; empty = every tool */ char worker_scope[2048]; /* isolated worker's JSON path allowlist */ const char *role_prompt; /* role instructions (not owned) */ char undo_sid[128]; /* snapshot into this session's undo... */ char undo_tag[64]; /* ...under this turn tag (the parent's) */ } cst_agent_config_t; /* ask: confirm per tool; auto: sandbox/rules decide, ask on doubt; yolo: never * ask (same as auto_approve). */ typedef enum { CST_APPROVAL_ASK = 0, CST_APPROVAL_AUTO, CST_APPROVAL_YOLO } cst_approval_t; #define CST_DEFAULT_APPROVAL CST_APPROVAL_AUTO const char *cst_approval_name(int a); int cst_approval_parse(const char *s, int *out); /* Working mode: how the system prompt frames the agent. "coding" (default) * is code framed; "general" is for tasks that are not about a codebase * (documents, data, research, everyday files). Orthogonal to plan mode. */ #define CST_DEFAULT_MODE "coding" int cst_mode_valid(const char *m); /* Canonical name of m, or the default when m is unknown / empty. */ const char *cst_mode_name(const char *m); 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); /* Tool output: role=tool, or a text-protocol user message answering a call. */ int cst_message_is_tool_result(const cst_message_t *m); /* The transcript as Markdown, malloc'd (NULL on OOM). Tool output and call * arguments are cut at 4000 bytes. Takes the session lock. */ char *cst_session_export_md(cst_session_t *s, const char *title, const char *sid, const char *model); /* Readers on another thread must hold the lock while using pointers returned * by cst_session_get (append may reallocate). Append locks internally. */ void cst_session_lock(cst_session_t *s); void cst_session_unlock(cst_session_t *s); /* --- 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); /* A fresh session id: a uuid v4, so two machines sharing a workspace cannot * hand out the same one (a timestamp can). id_sz >= 37. */ int cst_agent_store_new_id(char *id_out, size_t id_sz); /* The transcript of an id. A session owns a directory under the store * (/session.jsonl, with its attachments and sub-agent transcripts inside * it); an id that still has the older flat file (.jsonl) keeps it until * cst_agent_store_migrate() moves it. A new id gets its directory made here, * so the caller can open the file next. */ 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); /* The id a session path belongs to: /.jsonl (the flat layout) or * //session.jsonl (a session's own directory). 0 / -1. */ int cst_agent_store_id_of_path(const char *session_path, char *out, size_t out_sz); typedef struct { char id[128]; long long mtime; int messages; char title[128]; /* set via /rename; empty when none */ char first_user[128]; /* first user message, one line */ } cst_session_info_t; int cst_agent_store_info(const char *workspace, const char *id, cst_session_info_t *info); int cst_agent_store_title_set(const char *workspace, const char *id, const char *title); int cst_agent_store_delete(const char *workspace, const char *id); /* Move every flat session of the store into the per-session layout: the * transcript to /session.jsonl, the title and the shared attachment * directory (.title, attachments//) along with it. Sessions that are * already directories are left alone, so running it twice is harmless. * *moved_out / *failed_out (optional) count what happened; 0 / -1. */ int cst_agent_store_migrate(const char *workspace, int *moved_out, int *failed_out, char *err, size_t err_sz); /* --- tools --- */ typedef struct { char *out; size_t out_len; int exit_code; /* Images the tool produced for the model (view_image, image-returning MCP * tools). cst_tool_result_free releases them. */ cst_attachment_t *attach; int nattach; } cst_tool_result_t; void cst_tool_result_free(cst_tool_result_t *r); int cst_tool_known(const char *name); int cst_tool_is_shell(const char *name); int cst_tool_is_write(const char *name); /* OpenAI tools[] array for every tool (cJSON*, caller frees). */ void *cst_tools_schema(void); 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); /* sandboxed: confine the command with the OS sandbox (cst_sandbox.h). */ int cst_tool_run_shell_ex(const char *workspace_root, const ctl_tool_call_t *call, cst_tool_result_t *r, int sandboxed); 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. Returns -1 when * args_json is not a JSON object (call still gets id/name). */ int cst_tool_call_from_native(ctl_tool_call_t *out, const char *id, const char *name, const char *args_json); void cst_tool_call_clear(ctl_tool_call_t *c); /* Apply unified-diff hunks (text after the ---/+++ header) to one buffer. * All hunks must be complete and locate unambiguously, else nothing changes. * On success *out is malloc'd. */ int cst_patch_apply_buffer(const char *orig, size_t olen, const char *hunks, size_t hlen, char **out, size_t *out_len, int *nhunks, char *err, size_t err_sz); /* --- LLM --- */ typedef void (*cst_llm_on_delta_fn)(const char *data, size_t len, void *ud); /* args_json == NULL means the arguments were not valid JSON / too large. */ typedef void (*cst_llm_on_native_tool_fn)(const char *id, const char *name, const char *args_json, void *ud); typedef void (*cst_llm_on_native_open_fn)(int index, const char *id, const char *name, void *ud); typedef struct { long http_status; char finish_reason[32]; long prompt_tokens; int cache_reported; long completion_tokens; long cached_tokens; /* prompt tokens read from the provider's cache */ long cache_miss_tokens; /* prompt tokens billed at the full input price */ long cache_write_tokens; /* prompt tokens written to the cache (Anthropic) */ double t_first_byte_ms; /* relative to request start; -1 if none */ double t_end_ms; int client_stopped; /* stream ended by stop_flag (not an error) */ int retries; char error[512]; /* in-stream or HTTP error message */ } cst_llm_stats_t; typedef struct { const char *api_base; const char *api_key; const char *model; const char *reasoning_effort; /* NULL = not sent */ int (*request_gate)(void *); void *request_gate_ud; const cst_message_t *msgs; int nmsgs; cst_llm_on_delta_fn on_delta; /* Fired once per call, as soon as that call's arguments are complete. */ cst_llm_on_native_tool_fn on_native_tool; cst_llm_on_native_open_fn on_native_open; void *userdata; int enable_tools; /* send tools[] schema */ const cst_agent_config_t *tool_cfg; /* active tool set; NULL = all */ const char *tools_json; /* tools[] sent verbatim (frozen); NULL = tool_cfg */ int tool_choice_none; /* "tool_choice":"none" */ int send_reasoning; /* assistant reasoning goes back as reasoning_content */ int cache_style; /* CST_CACHE_* hints to add */ const char *cache_key; /* prompt_cache_key (CST_CACHE_OPENAI) */ const char *cache_ttl; /* cache_control ttl (CST_CACHE_ANTHROPIC); NULL = 5m */ int images_off; /* the model does not take images: send a note instead */ size_t tool_out_cap; /* wire cap for role=tool content; 0 = default */ double temperature; /* 0 = not sent */ double top_p; /* 0 = not sent */ int max_tokens; /* 0 = not sent */ const char *session_path; /* where stored images live; NULL = none */ volatile int *cancel_flag; /* non-zero → abort (error "cancelled") */ volatile int *stop_flag; /* non-zero → stop reading, not an error */ cst_llm_stats_t *stats; /* optional */ } cst_llm_request_t; int cst_llm_chat_stream(const cst_llm_request_t *req, char *err, size_t err_sz); /* GET {api_base}/models (10 s timeout). Returns the number of model ids * written, CST_MODELS_AUTH when the key is rejected (401/403), or -1 on * network / format errors. */ #define CST_MODELS_AUTH (-2) int cst_llm_list_models(const char *api_base, const char *api_key, char out[][128], int max_out, char *err, size_t err_sz); /* JSON request body (malloc'd) exactly as sent on the wire. */ char *cst_llm_build_body(const cst_llm_request_t *req); /* Reset the CST_RECORD_SSE / CST_REPLAY_SSE sequence counter. */ void cst_llm_trace_reset(void); /* Set CURLOPT_CAINFO on a CURL * when a CA bundle file is known. */ void cst_curl_apply_ca(void *curl); /* on_permission answers */ #define CST_PERM_DENY 0 #define CST_PERM_ONCE 1 #define CST_PERM_ALWAYS_TOOL 2 /* saved to .agent/permissions.json */ #define CST_PERM_ALWAYS_PREFIX 3 /* run_shell: allow this command prefix */ /* on_permission detail prefix when a sandboxed run_shell looked blocked and * the question is whether to rerun it outside the sandbox. The rest of the * detail is the tail of the sandboxed output. */ #define CST_PERM_SANDBOX "[sandbox] " /* Live UI / observability hooks for a turn. Every callback may be NULL. * Remote clients prefer the HTTP/SSE mirror (docs/webui.md); embedded * clients fill this struct (TUI, FFI). */ 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 a CST_PERM_* answer. NULL → deny unless auto_approve. preview is * the diff a write call would make, the full command for run_shell, or * NULL. */ int (*on_permission)(const char *tool_name, const char *detail, const char *preview, void *ud); /* ask_user tool. Fill ans and return 0, or -1 when dismissed. NULL means * nobody can answer (headless). */ int (*on_ask_user)(const char *question, const char *options, char *ans, size_t ans_sz, void *ud); /* todo_write replaced the task list (one item per line). */ void (*on_todo)(const char *text, void *ud); /* Full tool output, fired right after execution (before it is persisted). */ void (*on_tool_result)(const ctl_tool_call_t *call, const char *out, size_t len, int exit_code, void *ud); /* Images a tool produced (view_image, MCP image content), fired right after * the call ran and before the result is persisted. Only a call that made at * least one image is reported, so a front end can show it at once instead of * reading the store. */ void (*on_tool_images)(const ctl_tool_call_t *call, const cst_attachment_t *list, int n, void *ud); /* Turn lifecycle (once around cst_agent_run_turn_ex). */ void (*on_turn_begin)(const char *user_text, void *ud); /* rc 0 = ok; non-zero = failure or cancel. err may be NULL or "". */ void (*on_turn_end)(int rc, const char *err, void *ud); /* Structured error while a turn runs. kind is a short stable token * (llm | cancel | internal | …); message is human-readable. */ void (*on_error)(const char *kind, const char *message, 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_steer(cst_agent_t *a, const char *message, int readonly, int delegation); int cst_agent_control(cst_agent_t *a, int readonly, int delegation, int paused); 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); /* Re-root the agent at another directory. Per-workspace state (permission * rules, metrics, project notes) follows, the system prompt is rebuilt and the * token budget is re-measured. The session is not touched: the caller decides * whether to keep it or start a new one. */ int cst_agent_set_workspace(cst_agent_t *a, const char *path, char *err, size_t err_sz); int cst_agent_compact(cst_agent_t *a, char *err, size_t err_sz); /* Bare id of the current session (file name without .jsonl). */ void cst_agent_session_id(const cst_agent_t *a, char *out, size_t out_sz); /* Transcript path of the current session (images are stored beside it). */ const char *cst_agent_session_path(const cst_agent_t *a); /* Images picked for the next user message (/attach). They ride along with the * next cst_agent_run_turn*() call and are then part of the session. * cst_agent_attach_file copies the file into the session store; *err gets a * short reason on failure (-1). */ int cst_agent_attach_file(cst_agent_t *a, const char *path, char *err, size_t err_sz); /* Same for bytes already in memory (clipboard); name is display-only. */ int cst_agent_attach_bytes(cst_agent_t *a, const char *name, const char *data, size_t n, char *err, size_t err_sz); /* Whether the current model is shown images: 1 yes, -1 unknown (tried; a * 400 turns sending off), 0 no (images go as text notes). */ int cst_agent_sees_images(const cst_agent_t *a); int cst_agent_pending_count(const cst_agent_t *a); /* One line " (, 12 KB)"; -1 when i is out of range. */ int cst_agent_pending_describe(const cst_agent_t *a, int i, char *out, size_t out_sz); /* The stored name of pending attachment i ("img-1a2b3c4d.png"), or NULL when * i is out of range. The file is already in the session store, so a front end * can show the image before the message is sent. */ const char *cst_agent_pending_rel(const cst_agent_t *a, int i); /* 1 when pending attachment i is an image, 0 otherwise (or out of range). */ int cst_agent_pending_is_image(const cst_agent_t *a, int i); void cst_agent_pending_clear(cst_agent_t *a); /* Latest todo_write list of this session, or NULL. */ const char *cst_agent_todo(const cst_agent_t *a); /* AGENTS.md (or CST.md) text injected into the system prompt, or NULL. */ const char *cst_agent_notes(const cst_agent_t *a); void cst_agent_reload_notes(cst_agent_t *a); /* Roll back the file changes of the newest undoable turn. */ int cst_agent_undo(cst_agent_t *a, char *report, size_t report_sz); int cst_agent_undo_available(const cst_agent_t *a); /* Persistent permission rules (see cst_perms.h). */ struct cst_perms *cst_agent_perms(cst_agent_t *a); /* /retry: remove the last user prompt and its replies; caller frees. */ char *cst_agent_rewind_last_turn(cst_agent_t *a); /* Session id and turn tag that /undo snapshots of the running turn use. */ void cst_agent_undo_key(const cst_agent_t *a, char *sid, size_t sid_sz, char *tag, size_t tag_sz); /* Last prompt_tokens reported by the model (0 if unknown). */ long cst_agent_last_prompt_tokens(const cst_agent_t *a); /* Token use since the session was opened, and of the latest turn. */ typedef struct { int requests; int cache_known_requests; long prompt, cached, miss, write, out; double cost; /* in currency; valid when priced */ int priced; /* every request had a known price */ char currency[8]; } cst_usage_t; void cst_agent_usage(const cst_agent_t *a, cst_usage_t *session, cst_usage_t *turn); /* "[cst] ..." notes the agent appends when Plan mode, approval, branch, * date or AGENTS.md change mid-session (the system prompt stays frozen). */ int cst_message_is_note(const cst_message_t *m); const char *cst_agent_system_prompt(void); const char *cst_agent_system_prompt_for(cst_protocol_t p); /* Prompt for this config's tool set and protocol, plus optional project * instructions (AGENTS.md). malloc'd. */ char *cst_agent_build_prompt(const cst_agent_config_t *cfg, cst_protocol_t p, const char *project_notes); /* Per-provider API keys in /credentials (mode 0600), one * ".api_key=..." line each. */ int cst_creds_load(const char *provider, char *out, size_t out_sz); int cst_creds_save(const char *provider, const char *key); /* Saved key, then the provider's env variable. Returns 0 when found; * *from (optional) gets "saved" or the variable name. */ int cst_creds_resolve(const char *provider, char *out, size_t out_sz, const char **from); /* Hidden console prompt (no echo). */ int cst_creds_prompt_console(const char *provider, char *out, size_t out_sz); /* Generic hidden prompt for passwords / secrets (TTY only). */ int cst_prompt_secret(const char *prompt, char *out, size_t out_sz); /* Last used provider, model per provider, effort and working mode, in * /settings.json. */ #define CST_SETTINGS_MAX_MODELS 16 #define CST_SETTINGS_MAX_TRUSTED 32 #define CST_SETTINGS_MAX_PARAMS 16 /* Generation parameters remembered per provider ("params" in settings.json): * what the user set for that provider's models. 0 = not set, the field is * not sent. */ typedef struct { char provider[32]; double temperature; double top_p; int max_tokens; } cst_gen_params_t; typedef struct { char provider[32]; char effort[16]; char mode[16]; /* coding | general, "" = not chosen yet */ char approval[8]; /* "" = not chosen yet */ /* legacy_workers | parliament — parliament replaces in-process subagents. */ char collaboration[24]; int show_thinking; /* TUI: Thought blocks start expanded */ /* workspaces whose .agent/plugins and .agent/mcp.json may run */ int ntrusted; char trusted[CST_SETTINGS_MAX_TRUSTED][512]; int nmodels; struct { char provider[32]; char model[128]; } models[CST_SETTINGS_MAX_MODELS]; int nparams; cst_gen_params_t params[CST_SETTINGS_MAX_PARAMS]; } cst_settings_t; int cst_settings_load(cst_settings_t *s); int cst_settings_save(const cst_settings_t *s); const char *cst_settings_model(const cst_settings_t *s, const char *provider); /* The remembered parameters of a provider, or NULL when it has none. */ const cst_gen_params_t *cst_settings_params(const cst_settings_t *s, const char *provider); /* Remember parameters for a provider; all-zero (or NULL) forgets them. */ int cst_settings_remember_params(const char *provider, const cst_gen_params_t *p); /* Record cfg's provider, model, effort and mode as the last used ones. */ int cst_settings_remember(const cst_agent_config_t *cfg); /* Record an approval mode the user picked (CST_APPROVAL_*). */ int cst_settings_remember_approval(int approval); int cst_settings_remember_thinking(int show_thinking); /* Record the working mode the user picked (coding | general). */ int cst_settings_remember_mode(const char *mode); /* Workspace trust for project plugins / MCP servers (by real path). */ int cst_settings_trusted(const char *workspace); int cst_settings_trust(const char *workspace, int on); /* 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); /* Provider presets (base URL + default model). */ typedef struct { const char *name; const char *api_base; const char *model; const char *key_env; /* provider-specific key variable, e.g. DEEPSEEK_API_KEY */ } cst_provider_t; const cst_provider_t *cst_provider_find(const char *name); const cst_provider_t *cst_provider_default(void); int cst_provider_count(void); const cst_provider_t *cst_provider_at(int i); /* Context window of a model in tokens (CST_CTX_TOKENS overrides). */ long cst_model_context_tokens(const char *model); /* How a provider caches prompt prefixes. * auto : implicit prefix cache, nothing to send (DeepSeek, MiniMax...) * openai : implicit, routed by "prompt_cache_key" * anthropic : explicit "cache_control" breakpoints on content parts */ typedef enum { CST_CACHE_AUTO = 0, CST_CACHE_OPENAI, CST_CACHE_ANTHROPIC } cst_cache_style_t; typedef struct { int cache_style; /* cst_cache_style_t */ int reasoning_passback; /* history keeps reasoning_content (DeepSeek) */ int prefix_completion; /* assistant "prefix": true via /beta */ int vision; /* 1 takes images, 0 text only, -1 unknown */ } cst_model_caps_t; /* CST_NO_CACHE_HINTS=1 forces CST_CACHE_AUTO. */ void cst_model_caps(const char *provider, const char *api_base, const char *model, cst_model_caps_t *out); /* Prices per million tokens. CST_PRICE="miss,hit,out[,write[,currency]]" * overrides the built-in table. */ typedef struct { char currency[8]; /* "¥", "$" */ double miss, hit, write, out; int offpeak_half; /* DeepSeek: half price outside Beijing peak hours */ } cst_price_t; /* 0 when a price is known for model, -1 otherwise. */ int cst_model_price(const char *model, cst_price_t *out); /* DeepSeek peak hours: Beijing time Mon-Fri 9:00-12:00 and 14:00-18:00. */ int cst_price_is_peak(time_t when); double cst_price_cost(const cst_price_t *p, long miss, long hit, long write, long out, time_t when); /* Reasoning effort: low | medium | high | max. */ #define CST_DEFAULT_EFFORT "medium" int cst_effort_valid(const char *effort); /* Value for "reasoning_effort", or NULL when this provider/model does not * take the parameter. */ const char *cst_effort_wire(const char *provider, const char *model, const char *effort); /* 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 */