Extensions: skills, plugins, MCP, sub-agents; configurable keys
- Skills (SKILL.md discovery, skill tool, /skills /skill) - plugin.json exec tools with permission prompts, timeouts and workspace trust (/plugins) - MCP stdio client: JSON-RPC initialize/tools/list/tools/call, mcp__server__tool, /mcp and sidebar status - task tool with general/explore/plan and .agent/agents/*.md roles, parallel read-only runs, cancel propagation, /agents - Keymap with Ctrl+X leader, keybinds.json overrides and /keys panel - Collapsed thinking, throttled streaming Markdown, leaner tool output, parallel read-only tools - Spawn: close-on-exec pipes and a process-wide fork lock - Tests for keymap, skills, plugins, MCP, sub-agents, shell compaction; docs/plugins.md
This commit is contained in:
+20
-1
@@ -76,7 +76,14 @@ typedef struct {
|
||||
int approval; /* cst_approval_t (interactive sessions) */
|
||||
int plan_mode; /* read-only tools + planning note */
|
||||
int tool_profile; /* cst_tool_profile_t (CST_TOOLS) */
|
||||
unsigned long long tools_off; /* bit i = registry tool i disabled */
|
||||
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 read_only; /* no write / shell tools */
|
||||
char tools_only[1024]; /* ",a,b," allowlist; empty = every tool */
|
||||
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
|
||||
@@ -299,6 +306,9 @@ int cst_agent_undo_available(const cst_agent_t *a);
|
||||
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);
|
||||
|
||||
@@ -323,10 +333,15 @@ int cst_creds_prompt_console(const char *provider, char *out, size_t out_sz);
|
||||
/* Last used provider, model per provider and effort, in
|
||||
* <config>/settings.json. */
|
||||
#define CST_SETTINGS_MAX_MODELS 16
|
||||
#define CST_SETTINGS_MAX_TRUSTED 32
|
||||
typedef struct {
|
||||
char provider[32];
|
||||
char effort[16];
|
||||
char approval[8]; /* "" = not chosen yet */
|
||||
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];
|
||||
@@ -340,6 +355,10 @@ const char *cst_settings_model(const cst_settings_t *s, const char *provider);
|
||||
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);
|
||||
/* 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 {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef CST_EXT_H
|
||||
#define CST_EXT_H
|
||||
|
||||
/* Startup / reload of everything that adds tools at runtime: the skill
|
||||
* tool, plugin exec tools and MCP servers. */
|
||||
|
||||
#include "cst_agent.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Load for cfg's workspace (no-op for `--tools minimal`). One-line summary
|
||||
* in report, e.g. "2 skills · 1 plugin (3 tools) · mcp 1/2". */
|
||||
void cst_extensions_load(const cst_agent_config_t *cfg, char *report,
|
||||
size_t report_sz);
|
||||
/* Summary of the last load (empty when nothing was found). */
|
||||
const char *cst_extensions_report(void);
|
||||
/* Headless: wait for MCP handshakes, then note servers that failed. */
|
||||
void cst_extensions_wait(FILE *log);
|
||||
/* Drop plugin/MCP tools and stop MCP servers. */
|
||||
void cst_extensions_unload(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef CST_KEYMAP_H
|
||||
#define CST_KEYMAP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Key codes: a Unicode code point (control characters included: ctrl+a = 1),
|
||||
* a CST_K_* special key, optionally OR'ed with CST_KEY_ALT. */
|
||||
#define CST_KEY_SPECIAL 0x110000
|
||||
#define CST_KEY_ALT 0x400000
|
||||
enum {
|
||||
CST_K_UP = CST_KEY_SPECIAL,
|
||||
CST_K_DOWN,
|
||||
CST_K_LEFT,
|
||||
CST_K_RIGHT,
|
||||
CST_K_HOME,
|
||||
CST_K_END,
|
||||
CST_K_PGUP,
|
||||
CST_K_PGDN,
|
||||
CST_K_BTAB, /* shift+tab */
|
||||
CST_K_DEL,
|
||||
CST_K_INS,
|
||||
CST_K_CTRL_HOME,
|
||||
CST_K_CTRL_END,
|
||||
CST_K_CTRL_UP,
|
||||
CST_K_CTRL_DOWN,
|
||||
CST_K_ALT_UP,
|
||||
CST_K_ALT_DOWN,
|
||||
CST_K_F1, /* CST_K_F1 + n - 1 for f1..f12 */
|
||||
CST_K_LAST = CST_K_F1 + 11
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CST_KA_NONE = 0,
|
||||
/* general */
|
||||
CST_KA_PALETTE,
|
||||
CST_KA_KEYS,
|
||||
CST_KA_MODE,
|
||||
CST_KA_APPROVAL,
|
||||
CST_KA_NEWLINE,
|
||||
CST_KA_REDRAW,
|
||||
CST_KA_QUIT,
|
||||
/* view */
|
||||
CST_KA_TOGGLE_TOOL,
|
||||
CST_KA_TOGGLE_THOUGHT,
|
||||
CST_KA_THINKING_ALL,
|
||||
CST_KA_DETAILS,
|
||||
CST_KA_TODO,
|
||||
CST_KA_SIDEBAR,
|
||||
CST_KA_SCROLL_UP,
|
||||
CST_KA_SCROLL_DOWN,
|
||||
CST_KA_LINE_UP,
|
||||
CST_KA_LINE_DOWN,
|
||||
CST_KA_TOP,
|
||||
CST_KA_BOTTOM,
|
||||
/* session */
|
||||
CST_KA_NEW_SESSION,
|
||||
CST_KA_SESSIONS,
|
||||
CST_KA_UNDO,
|
||||
CST_KA_RETRY,
|
||||
CST_KA_COMPACT,
|
||||
CST_KA_COPY,
|
||||
CST_KA_EXPORT,
|
||||
/* model */
|
||||
CST_KA_MODEL,
|
||||
CST_KA_PROVIDER,
|
||||
CST_KA_EFFORT,
|
||||
/* extensions */
|
||||
CST_KA_AGENTS,
|
||||
CST_KA_SKILLS,
|
||||
CST_KA_PLUGINS,
|
||||
CST_KA_COUNT
|
||||
} cst_key_action_t;
|
||||
|
||||
typedef struct {
|
||||
const char *name; /* keybinds.json key, e.g. "new_session" */
|
||||
const char *group; /* "General", "View", ... */
|
||||
const char *desc;
|
||||
const char *slash; /* equivalent slash command, or NULL */
|
||||
} cst_key_action_info_t;
|
||||
|
||||
const cst_key_action_info_t *cst_key_action_info(int action);
|
||||
int cst_key_action_find(const char *name); /* CST_KA_NONE when unknown */
|
||||
|
||||
/* "ctrl+p", "shift+tab", "alt+enter", "f1", "pgup", "x" → code; -1 if bad. */
|
||||
int cst_key_parse(const char *s);
|
||||
/* code → canonical name ("ctrl+x", "tab", "alt+up", ...). */
|
||||
const char *cst_key_name(int code, char *buf, size_t n);
|
||||
|
||||
#define CST_KEYMAP_MAX 128
|
||||
typedef struct {
|
||||
int action;
|
||||
int keys[2];
|
||||
int nkeys;
|
||||
int user; /* from keybinds.json */
|
||||
} cst_binding_t;
|
||||
|
||||
typedef struct {
|
||||
int leader;
|
||||
cst_binding_t b[CST_KEYMAP_MAX];
|
||||
int n;
|
||||
} cst_keymap_t;
|
||||
|
||||
void cst_keymap_defaults(cst_keymap_t *km);
|
||||
/* Apply keybinds.json text over the defaults:
|
||||
* {"leader":"ctrl+x", "palette":"ctrl+p", "new_session":["<leader> n","f2"],
|
||||
* "todo":"none"}
|
||||
* Returns 0, or -1 with a message (bad entries are skipped, the rest apply). */
|
||||
int cst_keymap_apply_json(cst_keymap_t *km, const char *json, char *err,
|
||||
size_t err_sz);
|
||||
/* Defaults + <config>/keybinds.json when present. */
|
||||
int cst_keymap_load(cst_keymap_t *km, char *err, size_t err_sz);
|
||||
|
||||
#define CST_KM_NONE 0
|
||||
#define CST_KM_MATCH 1
|
||||
#define CST_KM_PREFIX 2
|
||||
/* seq of 1 or 2 keys → CST_KM_*; *action set on a match. */
|
||||
int cst_keymap_lookup(const cst_keymap_t *km, const int *seq, int n,
|
||||
int *action);
|
||||
/* "ctrl+x n, f2" (or "" when unbound). */
|
||||
void cst_keymap_describe(const cst_keymap_t *km, int action, char *buf,
|
||||
size_t n);
|
||||
/* One line per conflict ("ctrl+o: toggle_tool, toggle_thought"); returns the
|
||||
* number of conflicts. */
|
||||
int cst_keymap_conflicts(const cst_keymap_t *km, char *buf, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef CST_MCP_H
|
||||
#define CST_MCP_H
|
||||
|
||||
/*
|
||||
* MCP (Model Context Protocol) client over stdio: newline-delimited
|
||||
* JSON-RPC 2.0. Each server is spawned with its command/args/env, gets
|
||||
* initialize + notifications/initialized + tools/list, and every tool is
|
||||
* registered as mcp__<server>__<tool> (approved like network tools).
|
||||
*
|
||||
* Config, merged in this order (first name wins):
|
||||
* <ws>/.agent/mcp.json (trusted workspaces only), <config>/mcp.json,
|
||||
* plugin "mcpServers". Format: {"mcpServers": {"name": {"command": "npx",
|
||||
* "args": ["-y", "pkg"], "env": {"K": "V"}, "cwd": "...",
|
||||
* "disabled": false}}}
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CST_MCP_MAX 16
|
||||
|
||||
typedef enum {
|
||||
CST_MCP_OFF = 0,
|
||||
CST_MCP_STARTING,
|
||||
CST_MCP_READY,
|
||||
CST_MCP_FAILED,
|
||||
CST_MCP_EXITED
|
||||
} cst_mcp_state_t;
|
||||
|
||||
typedef struct {
|
||||
char name[64];
|
||||
char source[48]; /* "workspace" | "user" | "plugin:<name>" */
|
||||
char command[256];
|
||||
int state; /* cst_mcp_state_t */
|
||||
int ntools;
|
||||
char error[200];
|
||||
} cst_mcp_info_t;
|
||||
|
||||
/* One server from a JSON object text ({"command":..., "args":[...], ...}).
|
||||
* cwd: default working directory (NULL = workspace). Returns 0, or -1 when
|
||||
* the name exists / the config is invalid. */
|
||||
int cst_mcp_add_config_json(const char *name, const char *server_json,
|
||||
const char *cwd, const char *source);
|
||||
/* Read the mcp.json files for workspace (the workspace one only when
|
||||
* trusted). Returns servers added; *untrusted counts skipped ones. */
|
||||
int cst_mcp_load_configs(const char *workspace, int ws_trusted,
|
||||
int *untrusted);
|
||||
/* Start every configured server in the background; tools appear as each
|
||||
* one finishes its handshake. */
|
||||
void cst_mcp_start_all(const char *workspace);
|
||||
/* Wait up to ms for servers still starting (headless runs). */
|
||||
void cst_mcp_wait_ready(int ms);
|
||||
/* Stop servers, unregister their tools and forget the configs. */
|
||||
void cst_mcp_stop_all(void);
|
||||
int cst_mcp_info(cst_mcp_info_t *out, int max);
|
||||
/* Ready / configured counts (for the sidebar). */
|
||||
void cst_mcp_counts(int *ready, int *total);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef CST_PLUGINS_H
|
||||
#define CST_PLUGINS_H
|
||||
|
||||
/*
|
||||
* Plugins: <config>/plugins/<dir>/plugin.json (always loaded) and
|
||||
* <ws>/.agent/plugins/<dir>/plugin.json (only for trusted workspaces).
|
||||
*
|
||||
* {"name": "jira", "description": "...",
|
||||
* "tools": [{"name": "jira_search", "description": "...",
|
||||
* "parameters": {JSON Schema},
|
||||
* "command": ["python3", "search.py"], (or a shell string)
|
||||
* "perm": "read|write|shell|net", "timeout": 60}],
|
||||
* "skills": "skills",
|
||||
* "mcpServers": {"name": {"command": "...", "args": [], "env": {}}}}
|
||||
*
|
||||
* A tool call runs the command in the workspace with the arguments as JSON
|
||||
* on stdin (CST_PLUGIN_DIR / CST_WORKSPACE in the environment); stdout +
|
||||
* stderr is the result and the exit status its exit code. Relative argv
|
||||
* entries that exist in the plugin directory are made absolute.
|
||||
*/
|
||||
|
||||
#include "os_compat.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CST_PLUGINS_MAX 32
|
||||
|
||||
typedef struct {
|
||||
char name[64];
|
||||
char dir[OS_PATH_MAX];
|
||||
char source[16]; /* "user" | "workspace" */
|
||||
char desc[200];
|
||||
int ntools;
|
||||
int nmcp;
|
||||
int nskills_root; /* 1 when it adds a skills directory */
|
||||
char error[200]; /* load problem, "" when fine */
|
||||
} cst_plugin_info_t;
|
||||
|
||||
/* Load plugins; workspace ones only when ws_trusted. Returns the number of
|
||||
* plugins loaded; *untrusted (optional) counts skipped workspace plugins. */
|
||||
int cst_plugins_load(const char *workspace, int ws_trusted, int *untrusted);
|
||||
void cst_plugins_unload(void);
|
||||
int cst_plugins_info(cst_plugin_info_t *out, int max);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef CST_SKILLS_H
|
||||
#define CST_SKILLS_H
|
||||
|
||||
/*
|
||||
* Skills: directories with a SKILL.md (YAML frontmatter `name`,
|
||||
* `description`, then instructions) plus any helper files. Found in
|
||||
* <ws>/.agent/skills/<dir>/ <ws>/.claude/skills/<dir>/
|
||||
* <config>/skills/<dir>/ plugin skill roots (cst_skills_add_root)
|
||||
* The first skill of a name wins. The system prompt lists name +
|
||||
* description; the `skill` tool loads the rest on demand.
|
||||
*/
|
||||
|
||||
#include "os_compat.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CST_SKILLS_MAX 64
|
||||
#define CST_SKILL_INDEX_MAX 4096
|
||||
|
||||
typedef struct {
|
||||
char name[64];
|
||||
char desc[600];
|
||||
char dir[OS_PATH_MAX];
|
||||
char source[48]; /* "workspace", "claude", "user", "plugin:<name>" */
|
||||
} cst_skill_t;
|
||||
|
||||
/* Skills visible from workspace, sorted by name. Returns the count. */
|
||||
int cst_skills_list(const char *workspace, cst_skill_t *out, int max);
|
||||
/* Extra root (a directory of skill directories), e.g. from a plugin. */
|
||||
void cst_skills_add_root(const char *dir, const char *source);
|
||||
/* Forget the extra roots whose source starts with prefix ("" = all). */
|
||||
void cst_skills_clear_roots(const char *source_prefix);
|
||||
/* "Skills (...):\n- name: description\n" capped at CST_SKILL_INDEX_MAX, or
|
||||
* NULL when there are none. malloc'd. */
|
||||
char *cst_skills_index(const char *workspace);
|
||||
/* SKILL.md body (or one file of the skill) as tool output; *exit_code is 0
|
||||
* or 1. malloc'd. */
|
||||
char *cst_skill_read(const char *workspace, const char *name,
|
||||
const char *file, int *exit_code);
|
||||
/* User message for `/skill name task`: the instructions inlined before the
|
||||
* task. NULL when the skill does not exist. malloc'd. */
|
||||
char *cst_skill_prompt(const char *workspace, const char *name,
|
||||
const char *task);
|
||||
/* Register the `skill` tool (idempotent). */
|
||||
int cst_skills_register_tool(void);
|
||||
|
||||
/* --- YAML-ish frontmatter (also used for agent role files) --- */
|
||||
/* Value of key: plain, quoted, folded (`>`/`|`), `[a, b]` or `- a` lists
|
||||
* (lists come back comma-separated). Returns 1 when found. */
|
||||
int cst_fm_get(const char *text, const char *key, char *out, size_t n);
|
||||
/* Text after the closing `---` (the whole text when there is none). */
|
||||
const char *cst_fm_body(const char *text);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef CST_SUBAGENT_H
|
||||
#define CST_SUBAGENT_H
|
||||
|
||||
/*
|
||||
* Sub-agents: the task(description, prompt, agent?) tool runs one turn of a
|
||||
* child agent with its own context and returns its final report.
|
||||
*
|
||||
* Roles: built-in general / explore / plan, plus Markdown files in
|
||||
* <ws>/.agent/agents/ and <config>/agents/ (the workspace wins, both win
|
||||
* over built-ins). Frontmatter: name, description, model, tools (allowlist),
|
||||
* max_steps, readonly; the body is the role's instructions.
|
||||
*
|
||||
* Transcripts: <ws>/.agent/sessions/<parent-id>/sub-<n>.jsonl, with a
|
||||
* sub-<n>.json summary next to each (not listed as sessions).
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CST_ROLES_MAX 32
|
||||
|
||||
typedef struct {
|
||||
char name[64];
|
||||
char desc[256];
|
||||
char source[16]; /* "builtin" | "workspace" | "user" */
|
||||
char model[128]; /* empty = the parent's */
|
||||
char tools[1024]; /* ",a,b," allowlist; empty = all but task */
|
||||
int max_steps; /* 0 = default */
|
||||
int read_only;
|
||||
char *prompt; /* role instructions (malloc'd) */
|
||||
} cst_agent_role_t;
|
||||
|
||||
/* Roles sorted built-ins first, then by name. Free with cst_roles_free. */
|
||||
int cst_roles_list(const char *workspace, cst_agent_role_t *out, int max);
|
||||
void cst_roles_free(cst_agent_role_t *v, int n);
|
||||
/* System prompt section listing the roles (malloc'd), or NULL. */
|
||||
char *cst_subagent_index(const char *workspace);
|
||||
/* Register the task tool (owner "builtin:task") for workspace. */
|
||||
int cst_subagent_register_tool(const char *workspace);
|
||||
|
||||
typedef struct {
|
||||
char file[64]; /* "sub-3" */
|
||||
char role[64];
|
||||
char desc[160];
|
||||
char status[32]; /* "done" | "error" | "cancelled" | "running" */
|
||||
int steps, tools;
|
||||
double ms;
|
||||
long long mtime;
|
||||
} cst_subagent_rec_t;
|
||||
|
||||
/* This parent session's sub-agent runs, newest first. */
|
||||
int cst_subagent_list(const char *workspace, const char *parent_id,
|
||||
cst_subagent_rec_t *out, int max);
|
||||
/* Full path of a transcript (sub-<n>.jsonl). */
|
||||
int cst_subagent_path(const char *workspace, const char *parent_id,
|
||||
const char *file, char *out, size_t out_sz);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -41,6 +41,13 @@ typedef struct cst_tool_ctx {
|
||||
void (*set_todo)(const char *text, void *ud);
|
||||
void *todo_ud;
|
||||
int sandbox; /* run_shell: confine to the workspace (cst_sandbox.h) */
|
||||
/* Information-dense output (read_file headers and line numbers, cleaned
|
||||
* run_shell output). Off for `--tools minimal` so paper runs see the
|
||||
* original bytes. */
|
||||
int compact;
|
||||
/* The calling agent and its turn hooks (task runs sub-agents). */
|
||||
cst_agent_t *agent;
|
||||
const cst_agent_hooks_t *hooks;
|
||||
} cst_tool_ctx_t;
|
||||
|
||||
typedef int (*cst_tool_run_fn)(const cst_tool_ctx_t *ctx,
|
||||
@@ -51,6 +58,10 @@ typedef int (*cst_tool_run_fn)(const cst_tool_ctx_t *ctx,
|
||||
typedef int (*cst_tool_preview_fn)(const cst_tool_ctx_t *ctx,
|
||||
const ctl_tool_call_t *call, char **out);
|
||||
|
||||
typedef int (*cst_tool_run_ex_fn)(const cst_tool_ctx_t *ctx,
|
||||
const ctl_tool_call_t *call,
|
||||
cst_tool_result_t *r, void *ud);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
@@ -62,8 +73,32 @@ typedef struct {
|
||||
int minimal; /* member of `--tools minimal` (the original four) */
|
||||
cst_tool_run_fn run;
|
||||
cst_tool_preview_fn preview;
|
||||
/* Dynamic tools (skills, plugins, MCP, sub-agents). */
|
||||
const char *schema_json; /* JSON Schema object for the arguments; when set
|
||||
params[] is unused */
|
||||
cst_tool_run_ex_fn run_ex;
|
||||
void *ud;
|
||||
const char *owner; /* "plugin:<name>", "mcp:<server>", ... */
|
||||
/* Non-READ tools that may still join a parallel batch for this call. */
|
||||
int (*parallel)(const ctl_tool_call_t *call, void *ud);
|
||||
} cst_tool_def_t;
|
||||
|
||||
/* Add a tool at runtime (strings are copied, ud is not). Safe while turns
|
||||
* run; returns 0, or -1 when the name is taken or the table is full. */
|
||||
int cst_tool_register(const cst_tool_def_t *def);
|
||||
/* Drop every dynamic tool of owner (call only while no turn runs). */
|
||||
int cst_tool_unregister_owner(const char *owner);
|
||||
/* Arguments of call as a JSON object text, typed by the tool's schema
|
||||
* (numbers/booleans/objects stay JSON). malloc'd. */
|
||||
char *cst_tool_args_json(const cst_tool_def_t *t, const ctl_tool_call_t *call);
|
||||
/* Turn a name into a valid tool name ([A-Za-z0-9_-], at most 63 bytes). */
|
||||
void cst_tool_sanitize_name(const char *in, char *out, size_t out_sz);
|
||||
|
||||
/* Per-config switches by name (/tools on|off). */
|
||||
int cst_tool_disabled(const cst_agent_config_t *cfg, const char *name);
|
||||
void cst_tool_set_disabled(cst_agent_config_t *cfg, const char *name,
|
||||
int off);
|
||||
|
||||
typedef enum { CST_TOOLS_DEFAULT = 0, CST_TOOLS_MINIMAL } cst_tool_profile_t;
|
||||
|
||||
int cst_tool_profile_parse(const char *s, int *out);
|
||||
@@ -95,6 +130,11 @@ int cst_tool_preview(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *call,
|
||||
* Returns the count written into paths. */
|
||||
int cst_tool_targets(const ctl_tool_call_t *call, char paths[][1024], int max);
|
||||
|
||||
/* Shell output cleaned for a model: ANSI escapes removed, `\r` progress
|
||||
* redraws folded to their final state, runs of identical lines collapsed,
|
||||
* very long output cut to head + tail. Returns malloc'd text. */
|
||||
char *cst_shell_compact(const char *s, size_t n, size_t *out_len);
|
||||
|
||||
/* Unified diff (3 lines of context, GNU `diff -u` layout) with
|
||||
* `--- a/<label>` / `+++ b/<label>` headers. Returns malloc'd text; "" when
|
||||
* identical. */
|
||||
|
||||
+31
-1
@@ -71,7 +71,37 @@ int os_run_shell_ex(const char *cwd, const char *cmd, size_t max_out,
|
||||
* killed after the timeout (CST_SHELL_TIMEOUT seconds, default 120) or when
|
||||
* *flag becomes non-zero. Exit code 124 = timeout, 130 = cancelled. */
|
||||
void os_set_cancel_flag(volatile int *flag);
|
||||
void os_set_run_timeout(int seconds);
|
||||
volatile int *os_get_cancel_flag(void);
|
||||
void os_set_run_timeout(int seconds); /* this thread; 0 = default */
|
||||
int os_get_run_timeout(void);
|
||||
|
||||
/* Long-lived child with pipes on stdin/stdout (MCP servers). stderr goes
|
||||
* to stderr_path (NULL = discarded). env_add: "KEY=VALUE" entries added to
|
||||
* the inherited environment (NULL-terminated, may be NULL). */
|
||||
typedef struct {
|
||||
#ifdef _WIN32
|
||||
void *proc, *in_w, *out_r;
|
||||
#else
|
||||
int pid, in_fd, out_fd;
|
||||
#endif
|
||||
} os_proc_t;
|
||||
int os_proc_spawn(const char *cwd, const char *const argv[],
|
||||
const char *const env_add[], const char *stderr_path,
|
||||
os_proc_t *p);
|
||||
int os_proc_write(os_proc_t *p, const char *data, size_t n); /* 0 / -1 */
|
||||
/* Bytes read, 0 at EOF, -1 on error, -2 when nothing came within
|
||||
* timeout_ms. */
|
||||
long os_proc_read(os_proc_t *p, char *buf, size_t n, int timeout_ms);
|
||||
void os_proc_close_stdin(os_proc_t *p);
|
||||
/* Close the pipes, end the process (group) and reap it. */
|
||||
void os_proc_kill(os_proc_t *p);
|
||||
/* This thread's cancel flag (os_set_cancel_flag) is raised. */
|
||||
int os_cancel_requested(void);
|
||||
|
||||
/* Run fn(args[i]) for every i on its own thread and wait for all of them
|
||||
* (falls back to running inline when a thread cannot start). */
|
||||
typedef void (*os_task_fn)(void *arg);
|
||||
void os_run_parallel(os_task_fn fn, void *const args[], int n);
|
||||
|
||||
/* Monotonic milliseconds (arbitrary epoch). */
|
||||
double os_now_ms(void);
|
||||
|
||||
Reference in New Issue
Block a user