@standardagents/skill 0.17.0-next.a4b7340 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -17,9 +17,10 @@ declare function listSkills(): SkillDefinition[];
17
17
  declare function getSkill(name: SkillName): SkillDefinition;
18
18
  declare function resolveSkillDir(name: SkillName): string;
19
19
  declare function resolveSkillFile(name: SkillName, ...segments: string[]): string;
20
+ declare function skillPackageVersion(): Promise<string>;
20
21
  declare function readSkillFile(name: SkillName, ...segments: string[]): Promise<string>;
21
22
  declare function copySkill(name: SkillName, destinationRoot: string, options?: CopySkillOptions): Promise<string>;
22
23
  declare const skillPackageRoot: string;
23
24
  declare const skillsRoot: string;
24
25
 
25
- export { type CopySkillOptions, type SkillDefinition, type SkillName, copySkill, getSkill, listSkills, readSkillFile, resolveSkillDir, resolveSkillFile, skillPackageRoot, skillsRoot };
26
+ export { type CopySkillOptions, type SkillDefinition, type SkillName, copySkill, getSkill, listSkills, readSkillFile, resolveSkillDir, resolveSkillFile, skillPackageRoot, skillPackageVersion, skillsRoot };
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  // src/index.ts
2
- import { cp, mkdir, readFile } from "fs/promises";
2
+ import { cp, mkdir, readFile, writeFile } from "fs/promises";
3
3
  import { fileURLToPath } from "url";
4
4
  import { isAbsolute, relative, resolve } from "path";
5
5
  var PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url));
6
6
  var SKILLS_ROOT = resolve(PACKAGE_ROOT, "skills");
7
+ var VERSION_STAMP_PATTERN = /<!--\s*installed-skill-version:\s*__SKILL_VERSION__\s*-->/g;
7
8
  var SKILL_CATALOG = {
8
9
  agentbuilder: {
9
10
  description: "Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance."
@@ -44,9 +45,26 @@ function resolveSkillDir(name) {
44
45
  function resolveSkillFile(name, ...segments) {
45
46
  return resolveInside(resolveSkillDir(name), segments);
46
47
  }
48
+ var cachedVersion = null;
49
+ function skillPackageVersion() {
50
+ cachedVersion ??= readFile(resolve(PACKAGE_ROOT, "package.json"), "utf8").then(
51
+ (manifest) => JSON.parse(manifest).version
52
+ );
53
+ return cachedVersion;
54
+ }
55
+ async function stampSkillVersion(content) {
56
+ if (!content.includes("__SKILL_VERSION__")) {
57
+ return content;
58
+ }
59
+ const version = await skillPackageVersion();
60
+ return content.replace(
61
+ VERSION_STAMP_PATTERN,
62
+ `<!-- installed-skill-version: ${version} -->`
63
+ );
64
+ }
47
65
  async function readSkillFile(name, ...segments) {
48
66
  const target = segments.length === 0 ? resolveSkillFile(name, "SKILL.md") : resolveSkillFile(name, ...segments);
49
- return readFile(target, "utf8");
67
+ return stampSkillVersion(await readFile(target, "utf8"));
50
68
  }
51
69
  async function copySkill(name, destinationRoot, options = {}) {
52
70
  const sourceDir = resolveSkillDir(name);
@@ -58,6 +76,9 @@ async function copySkill(name, destinationRoot, options = {}) {
58
76
  force: options.overwrite ?? false,
59
77
  errorOnExist: !(options.overwrite ?? false)
60
78
  });
79
+ const entryFile = resolve(targetDir, "SKILL.md");
80
+ const entryContent = await readFile(entryFile, "utf8");
81
+ await writeFile(entryFile, await stampSkillVersion(entryContent), "utf8");
61
82
  return targetDir;
62
83
  }
63
84
  var skillPackageRoot = PACKAGE_ROOT;
@@ -70,6 +91,7 @@ export {
70
91
  resolveSkillDir,
71
92
  resolveSkillFile,
72
93
  skillPackageRoot,
94
+ skillPackageVersion,
73
95
  skillsRoot
74
96
  };
75
97
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cp, mkdir, readFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport { isAbsolute, relative, resolve } from 'node:path'\n\nconst PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url))\nconst SKILLS_ROOT = resolve(PACKAGE_ROOT, 'skills')\n\nconst SKILL_CATALOG = {\n agentbuilder: {\n description:\n 'Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance.',\n },\n} as const\n\nexport type SkillName = keyof typeof SKILL_CATALOG\n\nexport interface SkillDefinition {\n name: SkillName\n description: string\n directory: string\n entryFile: string\n}\n\nexport interface CopySkillOptions {\n overwrite?: boolean\n}\n\nfunction assertSkillName(name: string): asserts name is SkillName {\n if (!(name in SKILL_CATALOG)) {\n throw new Error(\n `Unknown Standard Agents skill \"${name}\". Expected one of: ${Object.keys(SKILL_CATALOG).join(', ')}.`\n )\n }\n}\n\nfunction resolveInside(baseDir: string, segments: readonly string[]): string {\n const candidate = resolve(baseDir, ...segments)\n const relativePath = relative(baseDir, candidate)\n\n if (relativePath.startsWith('..') || isAbsolute(relativePath)) {\n throw new Error('Skill paths must stay within the skill directory.')\n }\n\n return candidate\n}\n\nexport function listSkills(): SkillDefinition[] {\n return (Object.keys(SKILL_CATALOG) as SkillName[]).map((name) => getSkill(name))\n}\n\nexport function getSkill(name: SkillName): SkillDefinition {\n const definition = SKILL_CATALOG[name]\n const directory = resolveSkillDir(name)\n\n return {\n name,\n description: definition.description,\n directory,\n entryFile: resolveSkillFile(name, 'SKILL.md'),\n }\n}\n\nexport function resolveSkillDir(name: SkillName): string {\n assertSkillName(name)\n return resolve(SKILLS_ROOT, name)\n}\n\nexport function resolveSkillFile(name: SkillName, ...segments: string[]): string {\n return resolveInside(resolveSkillDir(name), segments)\n}\n\nexport async function readSkillFile(\n name: SkillName,\n ...segments: string[]\n): Promise<string> {\n const target = segments.length === 0\n ? resolveSkillFile(name, 'SKILL.md')\n : resolveSkillFile(name, ...segments)\n\n return readFile(target, 'utf8')\n}\n\nexport async function copySkill(\n name: SkillName,\n destinationRoot: string,\n options: CopySkillOptions = {}\n): Promise<string> {\n const sourceDir = resolveSkillDir(name)\n const targetRoot = resolve(destinationRoot)\n const targetDir = resolve(targetRoot, name)\n\n await mkdir(targetRoot, { recursive: true })\n await cp(sourceDir, targetDir, {\n recursive: true,\n force: options.overwrite ?? false,\n errorOnExist: !(options.overwrite ?? false),\n })\n\n return targetDir\n}\n\nexport const skillPackageRoot = PACKAGE_ROOT\nexport const skillsRoot = SKILLS_ROOT\n"],"mappings":";AAAA,SAAS,IAAI,OAAO,gBAAgB;AACpC,SAAS,qBAAqB;AAC9B,SAAS,YAAY,UAAU,eAAe;AAE9C,IAAM,eAAe,cAAc,IAAI,IAAI,OAAO,YAAY,GAAG,CAAC;AAClE,IAAM,cAAc,QAAQ,cAAc,QAAQ;AAElD,IAAM,gBAAgB;AAAA,EACpB,cAAc;AAAA,IACZ,aACE;AAAA,EACJ;AACF;AAeA,SAAS,gBAAgB,MAAyC;AAChE,MAAI,EAAE,QAAQ,gBAAgB;AAC5B,UAAM,IAAI;AAAA,MACR,kCAAkC,IAAI,uBAAuB,OAAO,KAAK,aAAa,EAAE,KAAK,IAAI,CAAC;AAAA,IACpG;AAAA,EACF;AACF;AAEA,SAAS,cAAc,SAAiB,UAAqC;AAC3E,QAAM,YAAY,QAAQ,SAAS,GAAG,QAAQ;AAC9C,QAAM,eAAe,SAAS,SAAS,SAAS;AAEhD,MAAI,aAAa,WAAW,IAAI,KAAK,WAAW,YAAY,GAAG;AAC7D,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,SAAO;AACT;AAEO,SAAS,aAAgC;AAC9C,SAAQ,OAAO,KAAK,aAAa,EAAkB,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC;AACjF;AAEO,SAAS,SAAS,MAAkC;AACzD,QAAM,aAAa,cAAc,IAAI;AACrC,QAAM,YAAY,gBAAgB,IAAI;AAEtC,SAAO;AAAA,IACL;AAAA,IACA,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,iBAAiB,MAAM,UAAU;AAAA,EAC9C;AACF;AAEO,SAAS,gBAAgB,MAAyB;AACvD,kBAAgB,IAAI;AACpB,SAAO,QAAQ,aAAa,IAAI;AAClC;AAEO,SAAS,iBAAiB,SAAoB,UAA4B;AAC/E,SAAO,cAAc,gBAAgB,IAAI,GAAG,QAAQ;AACtD;AAEA,eAAsB,cACpB,SACG,UACc;AACjB,QAAM,SAAS,SAAS,WAAW,IAC/B,iBAAiB,MAAM,UAAU,IACjC,iBAAiB,MAAM,GAAG,QAAQ;AAEtC,SAAO,SAAS,QAAQ,MAAM;AAChC;AAEA,eAAsB,UACpB,MACA,iBACA,UAA4B,CAAC,GACZ;AACjB,QAAM,YAAY,gBAAgB,IAAI;AACtC,QAAM,aAAa,QAAQ,eAAe;AAC1C,QAAM,YAAY,QAAQ,YAAY,IAAI;AAE1C,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,GAAG,WAAW,WAAW;AAAA,IAC7B,WAAW;AAAA,IACX,OAAO,QAAQ,aAAa;AAAA,IAC5B,cAAc,EAAE,QAAQ,aAAa;AAAA,EACvC,CAAC;AAED,SAAO;AACT;AAEO,IAAM,mBAAmB;AACzB,IAAM,aAAa;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cp, mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport { isAbsolute, relative, resolve } from 'node:path'\n\nconst PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url))\nconst SKILLS_ROOT = resolve(PACKAGE_ROOT, 'skills')\nconst VERSION_STAMP_PATTERN = /<!--\\s*installed-skill-version:\\s*__SKILL_VERSION__\\s*-->/g\n\nconst SKILL_CATALOG = {\n agentbuilder: {\n description:\n 'Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance.',\n },\n} as const\n\nexport type SkillName = keyof typeof SKILL_CATALOG\n\nexport interface SkillDefinition {\n name: SkillName\n description: string\n directory: string\n entryFile: string\n}\n\nexport interface CopySkillOptions {\n overwrite?: boolean\n}\n\nfunction assertSkillName(name: string): asserts name is SkillName {\n if (!(name in SKILL_CATALOG)) {\n throw new Error(\n `Unknown Standard Agents skill \"${name}\". Expected one of: ${Object.keys(SKILL_CATALOG).join(', ')}.`\n )\n }\n}\n\nfunction resolveInside(baseDir: string, segments: readonly string[]): string {\n const candidate = resolve(baseDir, ...segments)\n const relativePath = relative(baseDir, candidate)\n\n if (relativePath.startsWith('..') || isAbsolute(relativePath)) {\n throw new Error('Skill paths must stay within the skill directory.')\n }\n\n return candidate\n}\n\nexport function listSkills(): SkillDefinition[] {\n return (Object.keys(SKILL_CATALOG) as SkillName[]).map((name) => getSkill(name))\n}\n\nexport function getSkill(name: SkillName): SkillDefinition {\n const definition = SKILL_CATALOG[name]\n const directory = resolveSkillDir(name)\n\n return {\n name,\n description: definition.description,\n directory,\n entryFile: resolveSkillFile(name, 'SKILL.md'),\n }\n}\n\nexport function resolveSkillDir(name: SkillName): string {\n assertSkillName(name)\n return resolve(SKILLS_ROOT, name)\n}\n\nexport function resolveSkillFile(name: SkillName, ...segments: string[]): string {\n return resolveInside(resolveSkillDir(name), segments)\n}\n\nlet cachedVersion: Promise<string> | null = null\n\nexport function skillPackageVersion(): Promise<string> {\n cachedVersion ??= readFile(resolve(PACKAGE_ROOT, 'package.json'), 'utf8').then(\n (manifest) => JSON.parse(manifest).version as string\n )\n\n return cachedVersion\n}\n\nasync function stampSkillVersion(content: string): Promise<string> {\n if (!content.includes('__SKILL_VERSION__')) {\n return content\n }\n\n const version = await skillPackageVersion()\n\n return content.replace(\n VERSION_STAMP_PATTERN,\n `<!-- installed-skill-version: ${version} -->`\n )\n}\n\nexport async function readSkillFile(\n name: SkillName,\n ...segments: string[]\n): Promise<string> {\n const target = segments.length === 0\n ? resolveSkillFile(name, 'SKILL.md')\n : resolveSkillFile(name, ...segments)\n\n return stampSkillVersion(await readFile(target, 'utf8'))\n}\n\nexport async function copySkill(\n name: SkillName,\n destinationRoot: string,\n options: CopySkillOptions = {}\n): Promise<string> {\n const sourceDir = resolveSkillDir(name)\n const targetRoot = resolve(destinationRoot)\n const targetDir = resolve(targetRoot, name)\n\n await mkdir(targetRoot, { recursive: true })\n await cp(sourceDir, targetDir, {\n recursive: true,\n force: options.overwrite ?? false,\n errorOnExist: !(options.overwrite ?? false),\n })\n\n const entryFile = resolve(targetDir, 'SKILL.md')\n const entryContent = await readFile(entryFile, 'utf8')\n await writeFile(entryFile, await stampSkillVersion(entryContent), 'utf8')\n\n return targetDir\n}\n\nexport const skillPackageRoot = PACKAGE_ROOT\nexport const skillsRoot = SKILLS_ROOT\n"],"mappings":";AAAA,SAAS,IAAI,OAAO,UAAU,iBAAiB;AAC/C,SAAS,qBAAqB;AAC9B,SAAS,YAAY,UAAU,eAAe;AAE9C,IAAM,eAAe,cAAc,IAAI,IAAI,OAAO,YAAY,GAAG,CAAC;AAClE,IAAM,cAAc,QAAQ,cAAc,QAAQ;AAClD,IAAM,wBAAwB;AAE9B,IAAM,gBAAgB;AAAA,EACpB,cAAc;AAAA,IACZ,aACE;AAAA,EACJ;AACF;AAeA,SAAS,gBAAgB,MAAyC;AAChE,MAAI,EAAE,QAAQ,gBAAgB;AAC5B,UAAM,IAAI;AAAA,MACR,kCAAkC,IAAI,uBAAuB,OAAO,KAAK,aAAa,EAAE,KAAK,IAAI,CAAC;AAAA,IACpG;AAAA,EACF;AACF;AAEA,SAAS,cAAc,SAAiB,UAAqC;AAC3E,QAAM,YAAY,QAAQ,SAAS,GAAG,QAAQ;AAC9C,QAAM,eAAe,SAAS,SAAS,SAAS;AAEhD,MAAI,aAAa,WAAW,IAAI,KAAK,WAAW,YAAY,GAAG;AAC7D,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,SAAO;AACT;AAEO,SAAS,aAAgC;AAC9C,SAAQ,OAAO,KAAK,aAAa,EAAkB,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC;AACjF;AAEO,SAAS,SAAS,MAAkC;AACzD,QAAM,aAAa,cAAc,IAAI;AACrC,QAAM,YAAY,gBAAgB,IAAI;AAEtC,SAAO;AAAA,IACL;AAAA,IACA,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,iBAAiB,MAAM,UAAU;AAAA,EAC9C;AACF;AAEO,SAAS,gBAAgB,MAAyB;AACvD,kBAAgB,IAAI;AACpB,SAAO,QAAQ,aAAa,IAAI;AAClC;AAEO,SAAS,iBAAiB,SAAoB,UAA4B;AAC/E,SAAO,cAAc,gBAAgB,IAAI,GAAG,QAAQ;AACtD;AAEA,IAAI,gBAAwC;AAErC,SAAS,sBAAuC;AACrD,oBAAkB,SAAS,QAAQ,cAAc,cAAc,GAAG,MAAM,EAAE;AAAA,IACxE,CAAC,aAAa,KAAK,MAAM,QAAQ,EAAE;AAAA,EACrC;AAEA,SAAO;AACT;AAEA,eAAe,kBAAkB,SAAkC;AACjE,MAAI,CAAC,QAAQ,SAAS,mBAAmB,GAAG;AAC1C,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,oBAAoB;AAE1C,SAAO,QAAQ;AAAA,IACb;AAAA,IACA,iCAAiC,OAAO;AAAA,EAC1C;AACF;AAEA,eAAsB,cACpB,SACG,UACc;AACjB,QAAM,SAAS,SAAS,WAAW,IAC/B,iBAAiB,MAAM,UAAU,IACjC,iBAAiB,MAAM,GAAG,QAAQ;AAEtC,SAAO,kBAAkB,MAAM,SAAS,QAAQ,MAAM,CAAC;AACzD;AAEA,eAAsB,UACpB,MACA,iBACA,UAA4B,CAAC,GACZ;AACjB,QAAM,YAAY,gBAAgB,IAAI;AACtC,QAAM,aAAa,QAAQ,eAAe;AAC1C,QAAM,YAAY,QAAQ,YAAY,IAAI;AAE1C,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,GAAG,WAAW,WAAW;AAAA,IAC7B,WAAW;AAAA,IACX,OAAO,QAAQ,aAAa;AAAA,IAC5B,cAAc,EAAE,QAAQ,aAAa;AAAA,EACvC,CAAC;AAED,QAAM,YAAY,QAAQ,WAAW,UAAU;AAC/C,QAAM,eAAe,MAAM,SAAS,WAAW,MAAM;AACrD,QAAM,UAAU,WAAW,MAAM,kBAAkB,YAAY,GAAG,MAAM;AAExE,SAAO;AACT;AAEO,IAAM,mBAAmB;AACzB,IAAM,aAAa;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/skill",
3
- "version": "0.17.0-next.a4b7340",
3
+ "version": "0.17.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -29,6 +29,21 @@ For deeper reference material that this skill links to, see:
29
29
 
30
30
  > If the `agents/*/AGENTS.md` files don't exist in your project yet, run `pnpm exec agents scaffold` to create them.
31
31
 
32
+ ## Keeping this skill up to date
33
+
34
+ <!-- installed-skill-version: __SKILL_VERSION__ -->
35
+
36
+ This skill ships as `@standardagents/skill` on npm, and the version installed here is stamped in the HTML comment above. The first time you use this skill in a conversation, verify it is current:
37
+
38
+ 1. If the stamp above still reads `__SKILL_VERSION__`, this is an unstamped development copy — skip this check.
39
+ 2. Run `npm view @standardagents/skill version`. If the command fails (offline, no npm, no shell access), skip silently. This check must never block, retry, or interrupt real work.
40
+ 3. If the published version is newer than the stamp, tell the user in one short line and offer to update. Updating overwrites this file — including any local edits — so get the user's OK before running:
41
+ - Codex install (file lives under `.codex/skills/`): `npx @standardagents/cli@latest skill --force --agent codex`
42
+ - Claude Code install (file lives under `.claude/agents/`): `npx @standardagents/cli@latest skill --force --agent claude`
43
+ 4. After updating, re-read the new file before continuing.
44
+
45
+ Do this at most once per conversation, then move on to the task at hand.
46
+
32
47
  ---
33
48
 
34
49
  ## Before you write any code
@@ -478,7 +493,7 @@ This is the single biggest gap in most coding-agent-built tools. **Do not write
478
493
  | Read/write config and secrets | `state.env` / `state.envType` / `state.setEnv` | `process.env`, `.env` files |
479
494
  | Run isolated deterministic code | `state.runCode` with explicit bridges | `eval`, child processes, implicit runtime access |
480
495
  | Search files the thread has seen | `state.grepFiles` / `state.findFiles` | Reimplementing search |
481
- | Escalate / report status to the parent | `state.notifyParent` / `state.setStatus` | Custom message bus |
496
+ | Escalate / report status to the parent | `state.notifyParent` / `state.setStatus` / `state.stopSession` | Custom message bus |
482
497
  | Load a sibling prompt / agent / model | `state.loadPrompt` / `state.loadAgent` / `state.loadModel` | Duplicating the definition |
483
498
  | Inspect or message a child thread | `state.children` / `state.getChildThread` | External orchestration |
484
499
  | Inject context the model should see | `state.injectMessage` / `state.queueMessage` | Stuffing the system prompt |
@@ -500,7 +515,7 @@ Resources loadModel, loadPrompt, loadAgent,
500
515
  getChildThread, getParentThread,
501
516
  getPromptNames, getAgentNames, getModelNames
502
517
  Env env, envType, setEnv
503
- Parent notifyParent, setStatus
518
+ Parent notifyParent, setStatus, stopSession
504
519
  Tools queueTool, invokeTool
505
520
  Effects scheduleEffect, getScheduledEffects, removeScheduledEffect
506
521
  Events emit
@@ -564,7 +579,8 @@ By default, `runCode()` executes the `default` export with no args. Use `execute
564
579
  - **`state.runCode`** runs JavaScript or TypeScript in an isolated Dynamic Worker sandbox. The sandbox does not receive `ThreadState`, env, files, network, or host globals implicitly. Pass exact capabilities through `imports`, `globals`, `modules`, and `execute`. It executes exported values/functions; code such as `console.log(fibonacci(5))` can log, but it must still `export default ...` or export a named function/value for the host to receive a result.
565
580
  - **`state.scheduleEffect`** runs a named effect after a delay. It survives restarts. This is your cron, your queue, and your retry timer all in one.
566
581
  - **`state.invokeTool` vs `state.queueTool`** — `invokeTool` runs synchronously and returns the result; `queueTool` schedules the call to run later in the normal tool-call flow. Prefer `queueTool` when the model should see the result as a regular tool call.
567
- - **`state.notifyParent`** — for resumable subagents with `parentCommunication: 'explicit'`, this is the only way the child talks to the parent. Use it sparingly; every notification interrupts the parent.
582
+ - **`state.notifyParent`** — for resumable subagents with `parentCommunication: 'explicit'`, this is the main way the child talks to the parent. Use it sparingly; every delivered notification interrupts the parent. If there is no parent, nothing is queued externally.
583
+ - **`state.stopSession`** — terminally ends the active agent session. Use `state.stopSession({ result, notifyParent, status })` when a tool should return a blocking result, optionally notify the parent, and mark the child status without requiring a session tool call.
568
584
  - **File attachments** use the path convention `/attachments/{filename}.{ext}`. Always use this path when passing files between agents — the runtime copies them across thread filesystems automatically.
569
585
 
570
586
  ### Sandboxed code env bridge pattern
@@ -731,7 +747,9 @@ SubagentToolConfig — entry on a parent prompt's `tools` array
731
747
  maxInstances number cap on concurrent instances; when reached the tool
732
748
  is hidden and new messages route to existing instances
733
749
  parentCommunication 'implicit' | 'explicit' implicit (default) auto-queues completion to parent
734
- explicit requires state.notifyParent() in code
750
+ explicit requires state.notifyParent(),
751
+ state.setStatus(), or state.stopSession()
752
+ in code
735
753
  initUserMessageProperty schema field name tool arg used as child's first user message
736
754
  initAttachmentsProperty schema field name tool arg holding /attachments/* paths to copy to child
737
755
  initAgentNameProperty schema field name tool arg used to tag the child thread (UI label)
@@ -749,7 +767,7 @@ These are correct as written in the spec — internalize them.
749
767
  - Implicitly via `immediate: { ... }` — the child spawns the moment the parent prompt activates, before any LLM step.
750
768
  2. **Children only communicate back to their parents.** Two flavors:
751
769
  - **Implicit**: the child auto-queues a message to the parent when the session ends (via `sessionStop`, `sessionFail`, or `maxSessionTurns`). Default for all subagents.
752
- - **Explicit**: only when `state.notifyParent()` is called. Set with `resumable.parentCommunication: 'explicit'`. Use for high-traffic resumables (e.g., a Slack monitor) where you want control over when the parent is interrupted.
770
+ - **Explicit**: only when code calls `state.notifyParent()`, `state.setStatus()`, or `state.stopSession({ result, notifyParent, status })`. Set with `resumable.parentCommunication: 'explicit'`. Use for high-traffic resumables (e.g., a Slack monitor) where you want control over when the parent is interrupted.
753
771
  3. **Resumable subagents can receive messages from their parents.** `resumable.receives_messages` chooses which side hears them.
754
772
  4. **When a child sends back to the parent, it MUST indicate whether it needs a response** — in plain English, e.g. "After researching, you must respond so I can continue with the email." A research subagent providing FYI info doesn't need a response; a gmail subagent waiting on guidance does.
755
773
  5. **File attachments use path strings.** `/attachments/{filename}.{ext}`. Use this convention everywhere — the runtime copies attachments across thread filesystems automatically when listed in `initAttachmentsProperty`.