tool-schemas.expected.json 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. {
  2. "initial": [
  3. {
  4. "name": "bash",
  5. "description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. Attempting a command the sandbox may deny is safe and expected: run it and read the marker rather than assuming the denial. When a command is denied and a wider mode would let it succeed, escalate immediately in the same turn — the one sanctioned exception to a denial: retry the exact same command once with `sandbox_permissions` (the narrowest wider mode that suffices) plus a one-sentence `justification`. Do not detour through chat to ask permission first — the approval prompt raised by that retry is how the user consents. If the session states approval prompts are disabled, there is no exception: a denial is final — do not set `sandbox_permissions`. Never escalate speculatively: ground the request in a real denial — normally the one this command just hit; escalating up front is fine only when this session already denied the same access. A rejected escalation is final for that command — stop and explain, never work around it — but it does not forbid attempting or escalating other commands later.",
  6. "parameters": {
  7. "type": "object",
  8. "properties": {
  9. "command": {
  10. "type": "string",
  11. "description": "The bash command to execute."
  12. },
  13. "description": {
  14. "type": "string",
  15. "description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."
  16. },
  17. "timeoutMs": {
  18. "type": "number",
  19. "description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
  20. },
  21. "workdir": {
  22. "type": "string",
  23. "description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
  24. },
  25. "run_in_background": {
  26. "type": "boolean",
  27. "description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."
  28. },
  29. "sandbox_permissions": {
  30. "type": "string",
  31. "description": "The wider sandbox mode this command needs. Only valid as a one-shot retry of a command the sandbox just denied; requires justification and user approval.",
  32. "enum": [
  33. "workspace-write",
  34. "danger-full-access"
  35. ]
  36. },
  37. "justification": {
  38. "type": "string",
  39. "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access."
  40. }
  41. },
  42. "required": [
  43. "command",
  44. "description"
  45. ]
  46. }
  47. },
  48. {
  49. "name": "cordis_inspect",
  50. "description": "Inspect the live Cordis runtime in the current DSH process. Read-only. Sections: `services` (every provided ctx service and the plugin fiber that owns it), `plugins` (all live plugin fibers with their lifecycle states), `tools` (the model-facing tools currently registered, i.e. what you can call), `temporary` (only temporary Plugins created by cordis_mount: id, name, state, provided services, awaited services, and lifetime), `api` (method signatures AND argument/return type shapes for every LIVE service — read this before writing plugin code that calls a service), `events` (every harness event with its dispatch mode and exact signature — pick listener targets here). Temporary Plugins exist only in memory, remain active across later turns, and disappear after cordis_unmount, toolset unload, or DSH restart; they are not restored automatically. The `temporary` section is a subset of `plugins`. Omit `what` to get all six sections. With `what:\"api\"` or `what:\"events\"`, pass an exact `name` to narrow to one service/event and include its original source JSDoc.",
  51. "parameters": {
  52. "type": "object",
  53. "properties": {
  54. "what": {
  55. "type": "string",
  56. "description": "Limit the report to one section. Omit for all sections.",
  57. "enum": [
  58. "services",
  59. "plugins",
  60. "tools",
  61. "temporary",
  62. "api",
  63. "events"
  64. ]
  65. },
  66. "name": {
  67. "type": "string",
  68. "description": "Exact service key or event name whose original JSDoc to include; valid only with what:\"api\" or what:\"events\"."
  69. }
  70. }
  71. }
  72. },
  73. {
  74. "name": "cordis_mount",
  75. "description": "Mount a temporary Cordis Plugin in the current DSH process. This creates an in-memory runtime Plugin, not an installed or configured Plugin. It remains active across later turns until cordis_unmount, toolset unload, or DSH restart. It does not create files, install a package, change cordis.yml or personal/project config, survive restart, or automatically become permanent. To keep it, ask the Agent to implement a normal local, project, or repository Plugin through the regular development workflow. It may affect other sessions in the same process; the sandbox is not a security boundary, and injected services reach the real runtime. `code` runs now as the body of an async JavaScript function in an isolated sandbox and MUST `return` a plugin. Two forms: FUNCTION form `return (ctx) => { … }` — declares no inject, so it can register tools, listen to events, and provide services, but reaching ANY service (e.g. ctx.bash) throws; use it only when you need no services. OBJECT form `return { name?, inject: ['bash', 'llm', …], apply(ctx) { … } }` — declares dependencies, and cordis activates the plugin only after the services exist; PREFER this form. You may reach ONLY the services you list in inject: an undeclared service throws even if it exists, because an undeclared dependency would not be cleaned up if its provider is unmounted. BEFORE calling a service from your code, read cordis_inspect what:\"api\" — it lists method signatures AND the type shapes of their arguments/returns (do not guess a field's type; e.g. a bash run's stdout is an object, not a string). Inside `apply`, use the standard cordis API: `ctx.on(event, listener)` to observe events (see cordis_inspect what:\"events\"), or call `harness.registerTool(ctx, harness.defineTool({ name, description, parameters: { text: { type: 'string', required: true } }, output: { schema: { type: 'string' }, render(_args, value) { return [{ type: 'text', text: value }] } }, async execute(args) { return args.text } }))` to give yourself a new tool — it becomes callable on your NEXT step. Tool parameters: each key IS a property — { type: 'string'|'number'|'integer'|'boolean'|'null'|'object'|'array'|'json', required?: true, description?, enum?, const?, items?, properties? }; every direct DSL object declares additionalProperties: true|false, and oneOf: [schema, schema, ...] replaces type for an exact-one union. A raw JSON-Schema { type: 'object', properties, required?: […] } wrapper is also accepted with open-by-default objects. A tool's `execute` MUST return the lossless JSON value declared by `output.schema`; `output.render(args, value)` separately returns Native/model content blocks. Temporary Plugins can COMPOSE: one Plugin may `ctx.provide('name', value)` a service and another may declare `inject: ['name']` to consume it — the consumer stays pending until the provider exists and returns to pending when the provider is unmounted. Everything registered inside `apply` is cleaned up automatically by cordis_unmount. Sandbox globals: `console` (tagged `[cordis:<id>]`, writes through to the harness terminal), `harness.defineTool`, `harness.registerTool`, `btoa`, `atob`, `TextEncoder`, `TextDecoder`. Node APIs are DISABLED — do filesystem/network/timer work through the cordis services, never Node built-ins: `require`, `setTimeout`/`setInterval`, and `fetch` throw redirect errors; `process` and `Buffer` are undefined. Instead use inject: ['fs'] + ctx.fs for files, inject: ['web'] + ctx.web for HTTP, inject: ['bash'] + ctx.bash for processes, and inject: ['timer'] + ctx.setTimeout/ctx.setInterval for timing (fiber effects, auto-cleaned when unmounted) — cordis_inspect what:\"api\" shows what THIS runtime provides. Write PLAIN JavaScript, not TypeScript (no `as`, no type annotations). Cautions: (1) waterfall events (e.g. tools/pre-execute) hand the listener a trailing `next` callback which MUST be called — returning without `next()` VETOES the call; prefer plain notification events unless you intend to intercept. (2) Never await something that only resolves after the current turn (your code runs INSIDE a tool call of that turn — it would deadlock). (3) Your `ctx` is a restricted façade: you can register tools, observe events, provide/consume services, and use timers, but framework internals (ctx.root, ctx.fiber, ctx.extend, ctx.plugin, …) are withheld. It is not a security boundary though — the services you inject (e.g. ctx.bash) reach the real runtime.",
  76. "parameters": {
  77. "type": "object",
  78. "properties": {
  79. "code": {
  80. "type": "string",
  81. "description": "JavaScript body returning a temporary Plugin; evaluated now and saved nowhere."
  82. }
  83. },
  84. "required": [
  85. "code"
  86. ]
  87. }
  88. },
  89. {
  90. "name": "cordis_unmount",
  91. "description": "Unmount a current-process temporary Plugin created by cordis_mount. Waits for its tools, listeners, services, timers, and other owned effects to clean up completely. Only dyn-N temporary ids are accepted; this cannot remove Loader, configured, or installed Plugins.",
  92. "parameters": {
  93. "type": "object",
  94. "properties": {
  95. "id": {
  96. "type": "string",
  97. "description": "The temporary Plugin id returned by cordis_mount (for example \"dyn-1\"); valid only in this process and invalid after unmount or restart."
  98. }
  99. },
  100. "required": [
  101. "id"
  102. ]
  103. }
  104. },
  105. {
  106. "name": "create_goal",
  107. "description": "Create one persisted same-session completion goal when the current direct human request is a long-running objective that should continue across autonomous goal rounds. You may infer that intent without requiring the user to say \"create a goal\". Do not use this for trivial single-turn work. Execution rejects non-human and subagent authority.",
  108. "parameters": {
  109. "type": "object",
  110. "properties": {
  111. "objective": {
  112. "type": "string",
  113. "description": "The concrete completion objective inferred from the direct human request."
  114. },
  115. "max_goal_rounds": {
  116. "type": "number",
  117. "description": "Optional positive safe-integer limit on automatic continuation rounds."
  118. }
  119. },
  120. "required": [
  121. "objective"
  122. ]
  123. }
  124. },
  125. {
  126. "name": "edit",
  127. "description": "Edit an existing UTF-8 text file by replacing literal text.",
  128. "parameters": {
  129. "type": "object",
  130. "properties": {
  131. "file_path": {
  132. "type": "string",
  133. "description": "Path to edit, resolved by the filesystem backend."
  134. },
  135. "old_string": {
  136. "type": "string",
  137. "description": "Literal text to replace. Must match exactly."
  138. },
  139. "new_string": {
  140. "type": "string",
  141. "description": "Literal replacement text. Use an empty string to delete the match."
  142. },
  143. "replace_all": {
  144. "type": "boolean",
  145. "description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
  146. },
  147. "sandbox_permissions": {
  148. "type": "string",
  149. "description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
  150. "enum": [
  151. "workspace-write",
  152. "danger-full-access"
  153. ]
  154. },
  155. "justification": {
  156. "type": "string",
  157. "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
  158. }
  159. },
  160. "required": [
  161. "file_path",
  162. "old_string",
  163. "new_string"
  164. ]
  165. }
  166. },
  167. {
  168. "name": "get_goal",
  169. "description": "Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal.",
  170. "parameters": {
  171. "type": "object",
  172. "properties": {}
  173. }
  174. },
  175. {
  176. "name": "list_agents",
  177. "description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
  178. "parameters": {
  179. "type": "object",
  180. "properties": {}
  181. }
  182. },
  183. {
  184. "name": "ralph",
  185. "description": "Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.",
  186. "parameters": {
  187. "type": "object",
  188. "properties": {
  189. "objective": {
  190. "type": "string",
  191. "description": "The immutable completion objective for every fresh Ralph round."
  192. },
  193. "maxRounds": {
  194. "type": "number",
  195. "description": "Optional positive safe-integer round cap, bounded by the deployment ceiling."
  196. }
  197. },
  198. "required": [
  199. "objective"
  200. ]
  201. }
  202. },
  203. {
  204. "name": "read",
  205. "description": "Read a UTF-8 text file and return line-numbered content.",
  206. "parameters": {
  207. "type": "object",
  208. "properties": {
  209. "file_path": {
  210. "type": "string",
  211. "description": "Path to read, resolved by the filesystem backend."
  212. },
  213. "offset": {
  214. "type": "number",
  215. "description": "1-based first line to return. Defaults to 1."
  216. },
  217. "limit": {
  218. "type": "number",
  219. "description": "Maximum number of lines to return. Defaults to 2000."
  220. }
  221. },
  222. "required": [
  223. "file_path"
  224. ]
  225. }
  226. },
  227. {
  228. "name": "run_code",
  229. "description": "Execute a TypeScript program against the available tools. Write the BODY of an async function (erasable syntax only; top-level `await` and `return` work) and call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return comes back — curate it.",
  230. "parameters": {
  231. "type": "object",
  232. "properties": {
  233. "code": {
  234. "type": "string",
  235. "description": "The program: the body of an async TypeScript function."
  236. },
  237. "description": {
  238. "type": "string",
  239. "description": "Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI). Examples: \"Count TODO markers across packages\"; \"Read failing test and its fixture\"; \"Rename config key in every cordis.yml\"."
  240. }
  241. },
  242. "required": [
  243. "code",
  244. "description"
  245. ]
  246. }
  247. },
  248. {
  249. "name": "send_message",
  250. "description": "Send a message to a background subagent by its subagent id, continuing the same conversation. It becomes the subagent's next turn: if it is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. This call returns no answer from the subagent — only confirmation that the message was delivered — so use it to give it more work. A failure means the message was NOT delivered.",
  251. "parameters": {
  252. "type": "object",
  253. "properties": {
  254. "subagent_id": {
  255. "type": "string",
  256. "description": "The subagent id returned when the background subagent was started."
  257. },
  258. "message": {
  259. "type": "string",
  260. "description": "The message to deliver to the subagent."
  261. }
  262. },
  263. "required": [
  264. "subagent_id",
  265. "message"
  266. ]
  267. }
  268. },
  269. {
  270. "name": "skill",
  271. "description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
  272. "parameters": {
  273. "type": "object",
  274. "properties": {
  275. "name": {
  276. "type": "string",
  277. "description": "The exact skill name from the available skills list."
  278. }
  279. },
  280. "required": [
  281. "name"
  282. ]
  283. }
  284. },
  285. {
  286. "name": "subagent",
  287. "description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
  288. "parameters": {
  289. "type": "object",
  290. "properties": {
  291. "description": {
  292. "type": "string",
  293. "description": "A short (3-5 word) description of the delegated task, for display."
  294. },
  295. "prompt": {
  296. "type": "string",
  297. "description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
  298. },
  299. "run_in_background": {
  300. "type": "boolean",
  301. "description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
  302. }
  303. },
  304. "required": [
  305. "description",
  306. "prompt"
  307. ]
  308. }
  309. },
  310. {
  311. "name": "subagent_fork",
  312. "description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn), returning only its final result. Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive only its final answer, not its intermediate steps. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
  313. "parameters": {
  314. "type": "object",
  315. "properties": {
  316. "description": {
  317. "type": "string",
  318. "description": "A short (3-5 word) description of the delegated task, for display."
  319. },
  320. "prompt": {
  321. "type": "string",
  322. "description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new."
  323. },
  324. "run_in_background": {
  325. "type": "boolean",
  326. "description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
  327. }
  328. },
  329. "required": [
  330. "description",
  331. "prompt"
  332. ]
  333. }
  334. },
  335. {
  336. "name": "task_kill",
  337. "description": "Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops.",
  338. "parameters": {
  339. "type": "object",
  340. "properties": {
  341. "task_id": {
  342. "type": "string",
  343. "description": "Task id returned by the tool that started the background work."
  344. },
  345. "reason": {
  346. "type": "string",
  347. "description": "Optional short reason, recorded in the log and forwarded to the task."
  348. }
  349. },
  350. "required": [
  351. "task_id"
  352. ]
  353. }
  354. },
  355. {
  356. "name": "task_list",
  357. "description": "List your background tasks (running and finished) with their ids, kinds, and statuses.",
  358. "parameters": {
  359. "type": "object",
  360. "properties": {}
  361. }
  362. },
  363. {
  364. "name": "task_output",
  365. "description": "Read a background task. Stream tasks return only output since the previous read; final-output tasks return their result after settlement. Every response ends with `[status: ...]`. Reads are non-blocking unless `wait: true`, which waits up to the configured cap.",
  366. "parameters": {
  367. "type": "object",
  368. "properties": {
  369. "task_id": {
  370. "type": "string",
  371. "description": "Task id returned by the tool that started the background work."
  372. },
  373. "wait": {
  374. "type": "boolean",
  375. "description": "Block until the task reaches a terminal status or the timeout expires. A timed-out wait returns [status: running] and leaves the task alive."
  376. },
  377. "timeout_ms": {
  378. "type": "number",
  379. "description": "Max wait in milliseconds (only meaningful with wait: true). Defaults to the configured wait timeout; capped by the configured maximum."
  380. }
  381. },
  382. "required": [
  383. "task_id"
  384. ]
  385. }
  386. },
  387. {
  388. "name": "todo_write",
  389. "description": "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (there are no partial updates, no per-item edits). Use it to plan multi-step work and show progress: add one todo per concrete step before you start. Keep AT MOST ONE todo `in_progress` at a time; while work remains, exactly one active task should be `in_progress`. Mark a todo `completed` the moment it is done (do not batch completions), and allow no `in_progress` item only once all work is complete. Skip the list for trivial single-step tasks. Statuses: `pending` (not started), `in_progress` (being worked on now), `completed` (finished).",
  390. "parameters": {
  391. "type": "object",
  392. "properties": {
  393. "todos": {
  394. "type": "array",
  395. "description": "The COMPLETE task list, replacing any previous list.",
  396. "items": {
  397. "type": "object",
  398. "additionalProperties": false,
  399. "properties": {
  400. "content": {
  401. "type": "string",
  402. "description": "What the task is — a short imperative line."
  403. },
  404. "status": {
  405. "type": "string",
  406. "description": "pending (not started) | in_progress (now) | completed (done).",
  407. "enum": [
  408. "pending",
  409. "in_progress",
  410. "completed"
  411. ]
  412. }
  413. },
  414. "required": [
  415. "content",
  416. "status"
  417. ]
  418. }
  419. }
  420. },
  421. "required": [
  422. "todos"
  423. ]
  424. }
  425. },
  426. {
  427. "name": "update_goal",
  428. "description": "Update the exact current goal revision. edit, pause, and resume require a direct top-level human request. During an automatic continuation of the current goal, complete and blocked are also allowed. blocked is rejected before the configured minimum round count; the model remains responsible for judging that the same condition persisted across those rounds and must explain it in blocked_reason.",
  429. "parameters": {
  430. "type": "object",
  431. "properties": {
  432. "goal_id": {
  433. "type": "string",
  434. "description": "Exact id returned by get_goal."
  435. },
  436. "revision": {
  437. "type": "number",
  438. "description": "Exact positive revision returned by get_goal."
  439. },
  440. "action": {
  441. "type": "string",
  442. "description": "edit | pause | resume | complete | blocked",
  443. "enum": [
  444. "edit",
  445. "pause",
  446. "resume",
  447. "complete",
  448. "blocked"
  449. ]
  450. },
  451. "objective": {
  452. "type": "string",
  453. "description": "Replacement objective; valid only with action edit."
  454. },
  455. "max_goal_rounds": {
  456. "type": "number",
  457. "description": "Replacement cap; valid only with action edit."
  458. },
  459. "blocked_reason": {
  460. "type": "string",
  461. "description": "Concrete blocking condition; required only with action blocked."
  462. }
  463. },
  464. "required": [
  465. "goal_id",
  466. "revision",
  467. "action"
  468. ]
  469. }
  470. },
  471. {
  472. "name": "workflow",
  473. "description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nThe workflow's identity rides the `meta` parameter as JSON: required `name` (short kebab-case) and `description` strings, optional `whenToUse` string and `phases` array (`{title, detail?, provider?, model?}`). The `script` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO `export const meta` statement — meta is a parameter, not code), running with top-level await; end with `return <value>` — the value must be JSON-serializable and is this tool's result.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise<any>` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides (either may be provided alone). Anything else (`effort`/`isolation`/`agentType`) is rejected loudly.\n- `pipeline(items, ...stages): Promise<any[]>` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. An ordinary stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise<any[]>` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item `null`.\n\nConstraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.",
  474. "parameters": {
  475. "type": "object",
  476. "properties": {
  477. "script": {
  478. "type": "string",
  479. "description": "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
  480. },
  481. "meta": {
  482. "type": "object",
  483. "description": "The workflow identity block (plain JSON — never code).",
  484. "additionalProperties": true,
  485. "properties": {
  486. "name": {
  487. "type": "string",
  488. "description": "Short kebab-case workflow name."
  489. },
  490. "description": {
  491. "type": "string",
  492. "description": "One-line description of what the workflow does."
  493. },
  494. "whenToUse": {
  495. "type": "string",
  496. "description": "Optional guidance on when this workflow applies."
  497. },
  498. "phases": {
  499. "type": "array",
  500. "description": "Optional phase declarations matched by phase() calls.",
  501. "items": {
  502. "type": "object",
  503. "additionalProperties": true,
  504. "properties": {
  505. "title": {
  506. "type": "string",
  507. "description": "The phase title phase() calls match by exact string."
  508. },
  509. "detail": {
  510. "type": "string",
  511. "description": "Optional one-line description of the phase."
  512. },
  513. "provider": {
  514. "type": "string",
  515. "description": "Optional provider override this phase is expected to use."
  516. },
  517. "model": {
  518. "type": "string",
  519. "description": "Optional model override this phase is expected to use."
  520. }
  521. },
  522. "required": [
  523. "title"
  524. ]
  525. }
  526. }
  527. },
  528. "required": [
  529. "name",
  530. "description"
  531. ]
  532. },
  533. "args": {
  534. "type": "object",
  535. "description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]}).",
  536. "additionalProperties": true
  537. }
  538. },
  539. "required": [
  540. "script",
  541. "meta"
  542. ]
  543. }
  544. },
  545. {
  546. "name": "write",
  547. "description": "Create or fully replace a UTF-8 text file.",
  548. "parameters": {
  549. "type": "object",
  550. "properties": {
  551. "file_path": {
  552. "type": "string",
  553. "description": "Path to write, resolved by the filesystem backend."
  554. },
  555. "content": {
  556. "type": "string",
  557. "description": "Full UTF-8 text content to write."
  558. },
  559. "sandbox_permissions": {
  560. "type": "string",
  561. "description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
  562. "enum": [
  563. "workspace-write",
  564. "danger-full-access"
  565. ]
  566. },
  567. "justification": {
  568. "type": "string",
  569. "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
  570. }
  571. },
  572. "required": [
  573. "file_path",
  574. "content"
  575. ]
  576. }
  577. }
  578. ],
  579. "changes": []
  580. }