dsh-pstack 0.0.0-stage → 0.2.0-beta.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.
Files changed (166) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +133 -2
  3. package/assets/agents/comment-sicko.md +32 -0
  4. package/assets/agents/poteto-agent.md +8 -0
  5. package/assets/docs/guide/01-setup.md +64 -0
  6. package/assets/docs/guide/02-poteto-mode.md +139 -0
  7. package/assets/docs/guide/03-understand.md +79 -0
  8. package/assets/docs/guide/04-design.md +133 -0
  9. package/assets/docs/guide/05-build-and-clean.md +83 -0
  10. package/assets/docs/guide/06-verify-and-ship.md +152 -0
  11. package/assets/docs/guide/07-overnight.md +120 -0
  12. package/assets/docs/guide/08-principles.md +72 -0
  13. package/assets/docs/guide/09-make-it-yours.md +96 -0
  14. package/assets/docs/guide/10-recipes-and-pitfalls.md +180 -0
  15. package/assets/docs/guide/README.md +38 -0
  16. package/assets/docs/guide/images/design.jpg +0 -0
  17. package/assets/docs/guide/images/overnight.jpg +0 -0
  18. package/assets/docs/guide/images/recipes.jpg +0 -0
  19. package/assets/docs/guide/images/router.jpg +0 -0
  20. package/assets/docs/guide/images/understanding.jpg +0 -0
  21. package/assets/docs/guide/images/verification.jpg +0 -0
  22. package/assets/docs/harness/dsh.md +60 -0
  23. package/assets/docs/pstack-models.example.md +54 -0
  24. package/assets/skills/architect/SKILL.md +81 -0
  25. package/assets/skills/architect/references/design-red-flags.md +57 -0
  26. package/assets/skills/architect/references/rationale-template.md +35 -0
  27. package/assets/skills/architect/references/runner-prompt.md +20 -0
  28. package/assets/skills/arena/SKILL.md +50 -0
  29. package/assets/skills/automate-me/SKILL.md +106 -0
  30. package/assets/skills/benchmark-checklist/SKILL.md +39 -0
  31. package/assets/skills/blast-radius/SKILL.md +52 -0
  32. package/assets/skills/bro/SKILL.md +7 -0
  33. package/assets/skills/correct/SKILL.md +32 -0
  34. package/assets/skills/create-formal-verification-skill/SKILL.md +43 -0
  35. package/assets/skills/create-verification-skill/SKILL.md +46 -0
  36. package/assets/skills/create-verification-skill/references/feature-map-example/README.md +47 -0
  37. package/assets/skills/create-verification-skill/references/feature-map-example/create-note.md +39 -0
  38. package/assets/skills/create-verification-skill/references/feature-map-example/search.md +45 -0
  39. package/assets/skills/figure-it-out/SKILL.md +54 -0
  40. package/assets/skills/how/SKILL.md +38 -0
  41. package/assets/skills/how/references/explainer-prompt.md +55 -0
  42. package/assets/skills/how/references/explorer-prompt.md +35 -0
  43. package/assets/skills/in-your-own-words/SKILL.md +7 -0
  44. package/assets/skills/interrogate/SKILL.md +71 -0
  45. package/assets/skills/interrogate/references/code-quality-review.md +47 -0
  46. package/assets/skills/interrogate/references/lead-judgment.md +39 -0
  47. package/assets/skills/interrogate/references/reviewer-prompt.md +62 -0
  48. package/assets/skills/interrogate/references/rubric.md +77 -0
  49. package/assets/skills/ketchup/SKILL.md +45 -0
  50. package/assets/skills/maintain-formal-verification-skill/SKILL.md +60 -0
  51. package/assets/skills/maintain-verification-skill/SKILL.md +41 -0
  52. package/assets/skills/no-comments/SKILL.md +26 -0
  53. package/assets/skills/poteto-help/SKILL.md +162 -0
  54. package/assets/skills/poteto-help/references/prompting.md +51 -0
  55. package/assets/skills/poteto-help/references/recipes.md +51 -0
  56. package/assets/skills/poteto-mode/SKILL.md +151 -0
  57. package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
  58. package/assets/skills/poteto-mode/playbooks/autonomous-run.md +15 -0
  59. package/assets/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
  60. package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +12 -0
  61. package/assets/skills/poteto-mode/playbooks/babysit.md +23 -0
  62. package/assets/skills/poteto-mode/playbooks/bug-fix.md +15 -0
  63. package/assets/skills/poteto-mode/playbooks/eval.md +25 -0
  64. package/assets/skills/poteto-mode/playbooks/feature.md +21 -0
  65. package/assets/skills/poteto-mode/playbooks/hillclimb.md +21 -0
  66. package/assets/skills/poteto-mode/playbooks/investigation.md +14 -0
  67. package/assets/skills/poteto-mode/playbooks/multi-phase-plan.md +155 -0
  68. package/assets/skills/poteto-mode/playbooks/opening-a-pr.md +34 -0
  69. package/assets/skills/poteto-mode/playbooks/orchestrate.md +112 -0
  70. package/assets/skills/poteto-mode/playbooks/pause-safely.md +10 -0
  71. package/assets/skills/poteto-mode/playbooks/perf-issue.md +25 -0
  72. package/assets/skills/poteto-mode/playbooks/prototype.md +14 -0
  73. package/assets/skills/poteto-mode/playbooks/refactoring.md +16 -0
  74. package/assets/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
  75. package/assets/skills/poteto-mode/playbooks/session-pickup.md +11 -0
  76. package/assets/skills/poteto-mode/playbooks/shipping.md +12 -0
  77. package/assets/skills/poteto-mode/playbooks/trace-forensics.md +14 -0
  78. package/assets/skills/poteto-mode/playbooks/visual-parity.md +11 -0
  79. package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +20 -0
  80. package/assets/skills/poteto-mode/references/bugbot-triage.md +142 -0
  81. package/assets/skills/poteto-mode/scripts/check-plan.mjs +220 -0
  82. package/assets/skills/poteto-mode/scripts/check-plan.test.mjs +343 -0
  83. package/assets/skills/poteto-mode/scripts/orch/orch.mjs +4282 -0
  84. package/assets/skills/poteto-mode/scripts/orch/orch.test.mjs +2042 -0
  85. package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +5101 -0
  86. package/assets/skills/poteto-mode/scripts/watch-pr/github.test.mjs +1177 -0
  87. package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +1441 -0
  88. package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +4601 -0
  89. package/assets/skills/poteto-mode/scripts/worktree-audit.mjs +675 -0
  90. package/assets/skills/principle-attack-the-premise/SKILL.md +21 -0
  91. package/assets/skills/principle-boundary-discipline/SKILL.md +34 -0
  92. package/assets/skills/principle-build-the-lever/SKILL.md +23 -0
  93. package/assets/skills/principle-encode-lessons-in-structure/SKILL.md +11 -0
  94. package/assets/skills/principle-exhaust-the-design-space/SKILL.md +9 -0
  95. package/assets/skills/principle-experience-first/SKILL.md +19 -0
  96. package/assets/skills/principle-explain-the-number/SKILL.md +17 -0
  97. package/assets/skills/principle-fix-root-causes/SKILL.md +21 -0
  98. package/assets/skills/principle-foundational-thinking/SKILL.md +21 -0
  99. package/assets/skills/principle-guard-the-context-window/SKILL.md +12 -0
  100. package/assets/skills/principle-laziness-protocol/SKILL.md +10 -0
  101. package/assets/skills/principle-make-operations-idempotent/SKILL.md +22 -0
  102. package/assets/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +14 -0
  103. package/assets/skills/principle-minimize-reader-load/SKILL.md +18 -0
  104. package/assets/skills/principle-model-the-domain/SKILL.md +24 -0
  105. package/assets/skills/principle-never-block-on-the-human/SKILL.md +15 -0
  106. package/assets/skills/principle-outcome-oriented-execution/SKILL.md +21 -0
  107. package/assets/skills/principle-prove-it-works/SKILL.md +24 -0
  108. package/assets/skills/principle-redesign-from-first-principles/SKILL.md +16 -0
  109. package/assets/skills/principle-separate-before-serializing-shared-state/SKILL.md +16 -0
  110. package/assets/skills/principle-sequence-verifiable-units/SKILL.md +17 -0
  111. package/assets/skills/principle-subtract-before-you-add/SKILL.md +18 -0
  112. package/assets/skills/principle-test-behavior-not-implementation/SKILL.md +20 -0
  113. package/assets/skills/principle-type-system-discipline/SKILL.md +13 -0
  114. package/assets/skills/recall/SKILL.md +37 -0
  115. package/assets/skills/reflect/SKILL.md +69 -0
  116. package/assets/skills/reflect/references/divergent-reviewer.md +38 -0
  117. package/assets/skills/reflect/references/judgment-reviewer.md +37 -0
  118. package/assets/skills/reflect/references/synthesizer.md +56 -0
  119. package/assets/skills/reflect/references/tooling-reviewer.md +48 -0
  120. package/assets/skills/setup-pstack/SKILL.md +67 -0
  121. package/assets/skills/sharpen/SKILL.md +16 -0
  122. package/assets/skills/show-me-your-work/SKILL.md +84 -0
  123. package/assets/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
  124. package/assets/skills/show-me-your-work/scripts/log.mjs +108 -0
  125. package/assets/skills/swarm/SKILL.md +43 -0
  126. package/assets/skills/tdd/SKILL.md +42 -0
  127. package/assets/skills/teach/SKILL.md +23 -0
  128. package/assets/skills/technical-writing/SKILL.md +108 -0
  129. package/assets/skills/typescript-best-practices/SKILL.md +34 -0
  130. package/assets/skills/typescript-best-practices/references/patterns.md +328 -0
  131. package/assets/skills/unslop/SKILL.md +62 -0
  132. package/assets/skills/why/SKILL.md +100 -0
  133. package/assets/skills/why/references/epistemics.md +71 -0
  134. package/assets/skills/why/references/investigator-prompt.md +84 -0
  135. package/assets/skills/why/references/source-playbook.md +17 -0
  136. package/assets/skills/why/references/sources/code-archaeology.md +51 -0
  137. package/assets/skills/why/references/sources/databricks.md +70 -0
  138. package/assets/skills/why/references/sources/datadog.md +97 -0
  139. package/assets/skills/why/references/sources/incident-postmortem.md +5 -0
  140. package/assets/skills/why/references/sources/linear.md +48 -0
  141. package/assets/skills/why/references/sources/notion.md +55 -0
  142. package/assets/skills/why/references/sources/sentry.md +66 -0
  143. package/assets/skills/why/references/sources/slack.md +17 -0
  144. package/assets/skills/why/references/synthesizer-prompt.md +129 -0
  145. package/cordis.patch.yml +39 -0
  146. package/dist/frontmatter-BQX3ekwP.mjs +229 -0
  147. package/dist/frontmatter-BQX3ekwP.mjs.map +1 -0
  148. package/dist/history.d.mts +429 -0
  149. package/dist/history.mjs +1129 -0
  150. package/dist/history.mjs.map +1 -0
  151. package/dist/host-contract-o47JH_r8.d.mts +527 -0
  152. package/dist/index.d.mts +278 -0
  153. package/dist/index.mjs +272 -0
  154. package/dist/index.mjs.map +1 -0
  155. package/dist/paths-D_JlVmBk.mjs +42 -0
  156. package/dist/paths-D_JlVmBk.mjs.map +1 -0
  157. package/dist/script.d.mts +193 -0
  158. package/dist/script.mjs +782 -0
  159. package/dist/script.mjs.map +1 -0
  160. package/dist/subagent.d.mts +336 -0
  161. package/dist/subagent.mjs +1402 -0
  162. package/dist/subagent.mjs.map +1 -0
  163. package/locale/en.json +6 -0
  164. package/locale/zh.json +6 -0
  165. package/package.json +106 -3
  166. package/static/icon.png +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script.mjs","names":["spawnChild"],"sources":["../scripts/runtime-manifest.ts","../src/script.ts"],"sourcesContent":["/**\n * The shipped script runtime, as one table.\n *\n * pstack ships five CLIs and five test bundles. None of them may require Bun,\n * bash, a `node_modules` tree beside them, or an install step: the npm package\n * installs into a profile whose workspace resolves no peer dependencies, and the\n * Desktop build runs the same package under Electron, where `node` is not on\n * `PATH`.\n *\n * So every shipped script is a single-file Node bundle produced by\n * `scripts/build-runtime.ts` and copied into `assets/` by `scripts/build-assets.ts`.\n * This module is the one place that says what ships and where it lands; the\n * bundler, the asset generator, and the checks read it instead of keeping their\n * own copy.\n *\n * Two source origins:\n *\n * - `upstream` — the pinned upstream TypeScript, rewritten by\n * `RUNTIME_SOURCE_PATCHES` before it is bundled. `orch`, `watch-pr`,\n * `check-plan` and their tests stay upstream sources so they keep tracking their\n * original instead of becoming a fork of it.\n * - `authored` — this repository's own Node implementation under\n * `scripts/runtime/`. `log` and `worktree-audit` are here because their upstream\n * form is a shell script; their behaviour is specified by that script and\n * re-implemented.\n */\n\n/** Where a shipped artifact's source text comes from. */\nexport type RuntimeSourceKind = \"authored\" | \"upstream\";\n\nexport interface RuntimeSource {\n readonly kind: RuntimeSourceKind;\n /** Repository-relative POSIX path. */\n readonly path: string;\n}\n\nexport interface RuntimeEntry {\n /** Stable id, used in logs and by the Host tool's `id` enum. */\n readonly id: string;\n /** A runnable CLI, or a test bundle run by `node --test`. */\n readonly kind: \"cli\" | \"test\";\n /** Bundler entry file. */\n readonly entry: RuntimeSource;\n /** Output path relative to `runtime/`. */\n readonly out: string;\n /** The same path below `assets/`. */\n readonly assetPath: string;\n /** Whether the artifact is meant to be executed directly. */\n readonly executable: boolean;\n /**\n * Every source the build touches, entry first.\n *\n * The bundler follows imports itself, so this is not a module list: it is the\n * set staged before the build and the set whose patches must all land. A file\n * missing here would either fail to resolve or silently skip its rewrite.\n */\n readonly sources: readonly RuntimeSource[];\n /** One line for `build-runtime` output. */\n readonly summary: string;\n}\n\n/** Upstream script directories whose contents the built artifacts replace. */\nexport const REPLACED_SCRIPT_DIRECTORIES: readonly string[] = [\n \"skills/poteto-mode/scripts/\",\n \"skills/show-me-your-work/scripts/\",\n];\n\n/** The directory holding the authored runtime sources. */\nexport const AUTHORED_ROOT = \"scripts/runtime/\";\n\n/** Support modules the ported tests import instead of `bun:test`. */\nexport const TEST_SUPPORT: readonly RuntimeSource[] = [\n { kind: \"authored\", path: `${AUTHORED_ROOT}tests/expect.ts` },\n { kind: \"authored\", path: `${AUTHORED_ROOT}tests/support.ts` },\n];\n\n/**\n * Where the support modules land inside the staged source tree.\n *\n * `support.ts` re-exports from `./expect.ts`, so the pair has to stay adjacent.\n * Staging them at the root of `poteto-mode/scripts` lets `orch/` and `watch-pr/`\n * both reach them through `../support.ts` with one copy, instead of one copy per\n * test directory.\n */\nexport const TEST_SUPPORT_STAGED_PREFIX = \"skills/poteto-mode/scripts/\";\n\n/** `TEST_SUPPORT` as entry sources, so a test entry declares what it imports. */\nconst TEST_SUPPORT_ROOTS: readonly RuntimeSource[] = TEST_SUPPORT;\n\nconst upstreamScript = (path: string): RuntimeSource => ({\n kind: \"upstream\",\n path: `skills/poteto-mode/scripts/${path}`,\n});\n\n/** CLI artifacts, in the order the reviews and the prose name them. */\nexport const RUNTIME_CLIS: readonly RuntimeEntry[] = [\n {\n id: \"check-plan\",\n kind: \"cli\",\n entry: upstreamScript(\"check-plan.mjs\"),\n out: \"check-plan.mjs\",\n assetPath: \"skills/poteto-mode/scripts/check-plan.mjs\",\n executable: true,\n sources: [upstreamScript(\"check-plan.mjs\")],\n summary: \"plan-shape checker\",\n },\n {\n id: \"orch\",\n kind: \"cli\",\n entry: upstreamScript(\"orch/orch.ts\"),\n out: \"orch/orch.mjs\",\n assetPath: \"skills/poteto-mode/scripts/orch/orch.mjs\",\n executable: true,\n sources: [upstreamScript(\"orch/orch.ts\"), upstreamScript(\"orch/store.ts\")],\n summary: \"orchestration bookkeeping store\",\n },\n {\n id: \"watch-pr\",\n kind: \"cli\",\n entry: upstreamScript(\"watch-pr/watch-pr\"),\n out: \"watch-pr/watch-pr.mjs\",\n assetPath: \"skills/poteto-mode/scripts/watch-pr/watch-pr.mjs\",\n executable: true,\n sources: [\n upstreamScript(\"watch-pr/watch-pr\"),\n upstreamScript(\"watch-pr/cli.ts\"),\n upstreamScript(\"watch-pr/github.ts\"),\n upstreamScript(\"watch-pr/policy.ts\"),\n upstreamScript(\"watch-pr/render.ts\"),\n upstreamScript(\"watch-pr/types.ts\"),\n ],\n summary: \"pull-request watcher\",\n },\n {\n id: \"worktree-audit\",\n kind: \"cli\",\n entry: { kind: \"authored\", path: `${AUTHORED_ROOT}worktree-audit.ts` },\n out: \"worktree-audit.mjs\",\n assetPath: \"skills/poteto-mode/scripts/worktree-audit.mjs\",\n executable: true,\n sources: [{ kind: \"authored\", path: `${AUTHORED_ROOT}worktree-audit.ts` }],\n summary: \"read-only worktree audit\",\n },\n {\n id: \"log\",\n kind: \"cli\",\n entry: { kind: \"authored\", path: `${AUTHORED_ROOT}log.ts` },\n out: \"log.mjs\",\n assetPath: \"skills/show-me-your-work/scripts/log.mjs\",\n executable: true,\n sources: [{ kind: \"authored\", path: `${AUTHORED_ROOT}log.ts` }],\n summary: \"decision-log row appender\",\n },\n];\n\n/** Test bundles, run by `node --test` from a copy with no install step. */\nexport const RUNTIME_TESTS: readonly RuntimeEntry[] = [\n {\n id: \"check-plan-test\",\n kind: \"test\",\n entry: upstreamScript(\"check-plan.test.ts\"),\n out: \"check-plan.test.mjs\",\n assetPath: \"skills/poteto-mode/scripts/check-plan.test.mjs\",\n executable: false,\n sources: [upstreamScript(\"check-plan.test.ts\")],\n summary: \"check-plan tests\",\n },\n {\n id: \"orch-test\",\n kind: \"test\",\n entry: upstreamScript(\"orch/orch.test.ts\"),\n out: \"orch/orch.test.mjs\",\n assetPath: \"skills/poteto-mode/scripts/orch/orch.test.mjs\",\n executable: false,\n sources: [\n upstreamScript(\"orch/orch.test.ts\"),\n upstreamScript(\"orch/store.ts\"),\n ...TEST_SUPPORT_ROOTS,\n ],\n summary: \"orch tests\",\n },\n {\n id: \"watch-pr-cli-test\",\n kind: \"test\",\n entry: upstreamScript(\"watch-pr/cli.test.ts\"),\n out: \"watch-pr/cli.test.mjs\",\n assetPath: \"skills/poteto-mode/scripts/watch-pr/cli.test.mjs\",\n executable: false,\n sources: [\n upstreamScript(\"watch-pr/cli.test.ts\"),\n upstreamScript(\"watch-pr/cli.ts\"),\n upstreamScript(\"watch-pr/github.ts\"),\n upstreamScript(\"watch-pr/policy.ts\"),\n upstreamScript(\"watch-pr/render.ts\"),\n upstreamScript(\"watch-pr/types.ts\"),\n upstreamScript(\"watch-pr/fakes.test-helper.ts\"),\n ...TEST_SUPPORT_ROOTS,\n ],\n summary: \"watch-pr argument and rendering tests\",\n },\n {\n id: \"watch-pr-github-test\",\n kind: \"test\",\n entry: upstreamScript(\"watch-pr/github.test.ts\"),\n out: \"watch-pr/github.test.mjs\",\n assetPath: \"skills/poteto-mode/scripts/watch-pr/github.test.mjs\",\n executable: false,\n sources: [\n upstreamScript(\"watch-pr/github.test.ts\"),\n upstreamScript(\"watch-pr/github.ts\"),\n upstreamScript(\"watch-pr/types.ts\"),\n upstreamScript(\"watch-pr/fakes.test-helper.ts\"),\n ...TEST_SUPPORT_ROOTS,\n ],\n summary: \"watch-pr GitHub reader tests\",\n },\n {\n id: \"watch-pr-policy-test\",\n kind: \"test\",\n entry: upstreamScript(\"watch-pr/policy.test.ts\"),\n out: \"watch-pr/policy.test.mjs\",\n assetPath: \"skills/poteto-mode/scripts/watch-pr/policy.test.mjs\",\n executable: false,\n sources: [\n upstreamScript(\"watch-pr/policy.test.ts\"),\n upstreamScript(\"watch-pr/policy.ts\"),\n upstreamScript(\"watch-pr/github.ts\"),\n upstreamScript(\"watch-pr/types.ts\"),\n upstreamScript(\"watch-pr/fakes.test-helper.ts\"),\n ...TEST_SUPPORT_ROOTS,\n ],\n summary: \"watch-pr policy and queue tests\",\n },\n];\n\n/** Every artifact, CLIs first. */\nexport const RUNTIME_ENTRIES: readonly RuntimeEntry[] = [...RUNTIME_CLIS, ...RUNTIME_TESTS];\n\n/** The shebang every emitted artifact starts with. */\nexport const SHEBANG = \"#!/usr/bin/env node\\n\";\n\n/** True when a repository-relative upstream path is replaced by a built artifact. */\nexport function isReplacedUpstreamScript(upstreamRelativePath: string): boolean {\n return REPLACED_SCRIPT_DIRECTORIES.some((prefix) => upstreamRelativePath.startsWith(prefix));\n}\n\n/** Look up one entry by id. */\nexport function runtimeEntry(id: string): RuntimeEntry {\n const entry = RUNTIME_ENTRIES.find((candidate) => candidate.id === id);\n if (entry === undefined) throw new Error(`unknown runtime entry \"${id}\"`);\n return entry;\n}\n","/**\n * `pstack_script`: run a bundled pstack script through the Host's own runtime.\n *\n * Every shipped CLI is a Node artefact inside this package. A model could always\n * write `node <path> …` into a shell, and that is exactly what breaks in the\n * Desktop build: Electron runs the same CLI, so `node` is not on `PATH` there.\n *\n * So the entry point is a tool, not a command. The tool spawns the artefact with\n * `process.execPath` — whichever binary is already running this Host, Node or\n * Electron — and passes `ELECTRON_RUN_AS_NODE=1` only when it is Electron. That is\n * what makes the npm CLI and the Desktop build behave the same without depending on\n * anything in `PATH`.\n *\n * The tool never accepts a path or a command string. `id` is a closed enum over the\n * shipped artefacts, resolved from this package, so a call cannot reach a script the\n * package does not contain. It is not a general shell: there is no `sh -c`, no glob\n * expansion by a shell, and no way to name another executable.\n */\n\nimport { spawn as spawnChild, type ChildProcess } from \"node:child_process\";\n\nimport type {\n JobId,\n JobsHost,\n JsonSchemaNode,\n SessionId,\n ToolDefinition,\n ToolRunContext,\n ToolsHost,\n WorkingDirectoryHost,\n} from \"./host-contract.ts\";\nimport { RUNTIME_CLIS } from \"../scripts/runtime-manifest.ts\";\nimport { ASSETS_ROOT } from \"./paths.ts\";\nimport { isAbsolute, join, resolve } from \"node:path\";\n\n/** Model-facing tool name. */\nexport const TOOL_NAME = \"pstack_script\";\n\n/** The shipped scripts this tool may run, in display order. */\nexport const SCRIPT_IDS = RUNTIME_CLIS.map((entry) => entry.id);\n\nexport type ScriptId = (typeof SCRIPT_IDS)[number];\n\n/** Default wall-clock limit for a foreground run. */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n\n/** Bounds a caller may set for one foreground run. */\nexport const MAX_TIMEOUT_MS = 600_000;\n\n/** Default cap on captured bytes per stream. */\nexport const DEFAULT_OUTPUT_LIMIT_BYTES = 64 * 1024;\n\n/** Raised for an argument problem, before anything is spawned. */\nexport class ScriptInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ScriptInputError\";\n }\n}\n\n/** Raised when a shipped artefact is missing from this install. */\nexport class ScriptMissingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ScriptMissingError\";\n }\n}\n\n/** One parsed call. */\nexport interface ScriptArgs {\n readonly id: ScriptId;\n readonly args: readonly string[];\n readonly cwd?: string;\n readonly background: boolean;\n readonly timeoutMs?: number;\n readonly outputLimitBytes?: number;\n}\n\n/** What a foreground run produced. */\nexport interface ScriptResult {\n readonly kind: \"foreground\";\n readonly id: ScriptId;\n readonly exitCode: number;\n readonly signal: NodeJS.Signals | null;\n readonly timedOut: boolean;\n readonly stdout: string;\n readonly stderr: string;\n /** True when a stream was cut off at the limit. */\n readonly stdoutTruncated: boolean;\n readonly stderrTruncated: boolean;\n readonly durationMs: number;\n}\n\n/** What a background run produced. */\nexport interface ScriptJobResult {\n readonly kind: \"background\";\n readonly id: ScriptId;\n readonly jobId: JobId;\n readonly status: string;\n}\n\n/** Either outcome, as the model sees it. */\nexport type ScriptOutcome = ScriptResult | ScriptJobResult;\n\n/** Absolute path of one shipped artefact. */\nexport function scriptPath(id: ScriptId): string {\n const entry = RUNTIME_CLIS.find((candidate) => candidate.id === id);\n if (entry === undefined) throw new ScriptInputError(`unknown script id \"${String(id)}\"`);\n // `assetPath` is relative to the generated tree, e.g.\n // `skills/poteto-mode/scripts/orch/orch.mjs`, which is exactly what `ASSETS_ROOT`\n // is the parent of.\n return join(ASSETS_ROOT, ...entry.assetPath.split(\"/\"));\n}\n\n/**\n * The environment a child gets.\n *\n * The one rule is the Electron selector. Under Electron, `process.execPath` is the\n * Desktop binary, which starts a browser window unless `ELECTRON_RUN_AS_NODE=1` tells\n * it to behave as Node. Under plain Node the variable is inert — Node ignores it —\n * so setting it unconditionally would also work; it is conditional here so the\n * environment of a normal run stays exactly the caller's, and the one code path that\n * needs it is visible.\n */\nexport function scriptEnvironment(\n base: NodeJS.ProcessEnv = process.env,\n // Only the field this decision reads is named, so a test can pass the two shapes\n // that matter without fabricating a full `process.versions`. The cast is because\n // `ProcessVersions` types `electron` as an optional literal this file does not own.\n versions: { readonly electron?: string } = process.versions as { readonly electron?: string },\n): NodeJS.ProcessEnv {\n if (versions.electron === undefined) return { ...base };\n return { ...base, ELECTRON_RUN_AS_NODE: \"1\" };\n}\n\n/** Validate the raw tool arguments. */\nexport function parseScriptArgs(raw: unknown): ScriptArgs {\n if (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n throw new ScriptInputError(\"arguments must be an object\");\n }\n const record = raw as Record<string, unknown>;\n\n for (const key of Object.keys(record)) {\n if (![\"id\", \"args\", \"cwd\", \"background\", \"timeoutMs\", \"outputLimitBytes\"].includes(key)) {\n throw new ScriptInputError(`unknown argument \"${key}\"`);\n }\n }\n\n const id = record[\"id\"];\n if (typeof id !== \"string\") throw new ScriptInputError('\"id\" must be a string');\n if (!(SCRIPT_IDS as readonly string[]).includes(id)) {\n throw new ScriptInputError(\n `\"id\" must be one of ${SCRIPT_IDS.join(\", \")}; this tool runs bundled scripts only`,\n );\n }\n\n const rawArgs = record[\"args\"];\n if (rawArgs !== undefined && !Array.isArray(rawArgs)) {\n throw new ScriptInputError('\"args\" must be an array of strings');\n }\n const args: string[] = [];\n for (const value of (rawArgs ?? []) as readonly unknown[]) {\n if (typeof value !== \"string\") throw new ScriptInputError('\"args\" must contain only strings');\n args.push(value);\n }\n\n const cwd = record[\"cwd\"];\n if (cwd !== undefined && (typeof cwd !== \"string\" || cwd.trim() === \"\")) {\n throw new ScriptInputError('\"cwd\" must be a non-empty string when present');\n }\n\n const background = record[\"background\"] ?? false;\n if (typeof background !== \"boolean\") throw new ScriptInputError('\"background\" must be a boolean');\n\n const timeoutMs = record[\"timeoutMs\"];\n if (timeoutMs !== undefined) {\n if (typeof timeoutMs !== \"number\" || !Number.isInteger(timeoutMs) || timeoutMs <= 0) {\n throw new ScriptInputError('\"timeoutMs\" must be a positive integer');\n }\n if (timeoutMs > MAX_TIMEOUT_MS) {\n throw new ScriptInputError(`\"timeoutMs\" must be at most ${MAX_TIMEOUT_MS}`);\n }\n }\n\n const outputLimitBytes = record[\"outputLimitBytes\"];\n if (outputLimitBytes !== undefined) {\n if (\n typeof outputLimitBytes !== \"number\" ||\n !Number.isInteger(outputLimitBytes) ||\n outputLimitBytes <= 0\n ) {\n throw new ScriptInputError('\"outputLimitBytes\" must be a positive integer');\n }\n }\n\n return {\n id: id as ScriptId,\n args,\n ...(cwd === undefined ? {} : { cwd: cwd as string }),\n background,\n ...(timeoutMs === undefined ? {} : { timeoutMs: timeoutMs as number }),\n ...(outputLimitBytes === undefined\n ? {}\n : { outputLimitBytes: outputLimitBytes as number }),\n };\n}\n\n/**\n * Resolve the directory the child runs in.\n *\n * The tool's `cwd` argument is documented as resolving against \"this session's\n * directory\", so `sessionCwd` — the value `ctx.workingDirectory.ensure()` returns —\n * is the base, not the Host process's working directory.\n *\n * - omitted -> the session directory\n * - relative -> joined onto the session directory\n * - absolute -> used as-is\n *\n * With no Session (a call outside an Agent) there is no session directory to\n * anchor to; the argument is then passed through and `resolve` normalises it\n * against the Host process, which is the only directory that exists.\n */\nexport function resolveWorkingDirectory(\n requested: string | undefined,\n sessionCwd: string | undefined,\n): string | undefined {\n if (requested === undefined) return sessionCwd;\n if (isAbsolute(requested)) return resolve(requested);\n return sessionCwd === undefined ? resolve(requested) : resolve(join(sessionCwd, requested));\n}\n\n/**\n * Run one script in the foreground.\n *\n * A nonzero exit, a signal, a timeout, and truncated output are all returned rather\n * than raised: the caller needs to see what the script said before it failed, and an\n * exception would discard the captured output.\n */\nexport async function runForeground(\n args: ScriptArgs,\n options: {\n readonly execPath?: string;\n readonly environment?: NodeJS.ProcessEnv;\n /** Already resolved by the caller; see `resolveWorkingDirectory`. */\n readonly cwd?: string;\n readonly signal?: AbortSignal;\n } = {},\n): Promise<ScriptResult> {\n const path = scriptPath(args.id);\n const started = Date.now();\n const timeoutMs = args.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const limit = args.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;\n\n return await new Promise<ScriptResult>((resolve, reject) => {\n const child = spawnChild(options.execPath ?? process.execPath, [path, ...args.args], {\n cwd: args.cwd ?? options.cwd,\n env: options.environment ?? scriptEnvironment(),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n windowsHide: true,\n signal: options.signal,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let stdoutTruncated = false;\n let stderrTruncated = false;\n let timedOut = false;\n\n const collect = (chunk: Buffer, to: \"out\" | \"err\"): void => {\n const text = chunk.toString(\"utf8\");\n if (to === \"out\") {\n if (stdout.length >= limit) {\n stdoutTruncated = true;\n return;\n }\n stdout += text.slice(0, limit - stdout.length);\n if (stdout.length >= limit) stdoutTruncated = true;\n return;\n }\n if (stderr.length >= limit) {\n stderrTruncated = true;\n return;\n }\n stderr += text.slice(0, limit - stderr.length);\n if (stderr.length >= limit) stderrTruncated = true;\n };\n\n child.stdout?.on(\"data\", (chunk: Buffer) => collect(chunk, \"out\"));\n child.stderr?.on(\"data\", (chunk: Buffer) => collect(chunk, \"err\"));\n\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, timeoutMs);\n\n child.once(\"error\", (error) => {\n clearTimeout(timer);\n reject(error);\n });\n\n child.once(\"close\", (code, signal) => {\n clearTimeout(timer);\n resolve({\n kind: \"foreground\",\n id: args.id,\n exitCode: code ?? (signal === null ? 1 : 1),\n signal,\n timedOut,\n stdout,\n stderr,\n stdoutTruncated,\n stderrTruncated,\n durationMs: Date.now() - started,\n });\n });\n });\n}\n\n/**\n * Start a script as a background job.\n *\n * `jobs` is required and passing nothing is a refusal with a reason. A job whose\n * output nobody can read would be worse than a failed call: the model would report a\n * task as started and no notice would ever arrive.\n */\nexport async function runBackground(\n args: ScriptArgs,\n options: {\n readonly jobs?: JobsHost | undefined;\n readonly caller?: SessionId;\n readonly execPath?: string;\n readonly environment?: NodeJS.ProcessEnv;\n readonly cwd?: string;\n },\n): Promise<ScriptJobResult> {\n const jobs = options.jobs;\n if (jobs === undefined) {\n throw new ScriptInputError(\n \"background: true needs the Host jobs service, and this composition has none. \" +\n \"Run the call in the foreground (omit background), or ask the operator to mount a job controller.\",\n );\n }\n\n const path = scriptPath(args.id);\n const execPath = options.execPath ?? process.execPath;\n const environment = options.environment ?? scriptEnvironment();\n // Already resolved by the caller; see `resolveWorkingDirectory`.\n const cwd = options.cwd;\n\n /**\n * Settles when the child either started or failed to start, or when the registry\n * never invoked the producer at all.\n *\n * `run` fills this in, so the caller can await the real startup outcome instead of\n * guessing at one with a timer. `child_process` emits exactly one of `spawn` or\n * `error`:\n *\n * - `spawn` fires once the child has been created, before any stdio or exit event.\n * - `error` fires instead when creation failed (ENOENT, EACCES, EMFILE), and no\n * `spawn` ever follows.\n *\n * So these two events are mutually exclusive and jointly exhaustive for \"did it\n * start\", which is exactly the question the tool has to answer. Measured on this\n * Node: a missing binary reports ENOENT through `error`, while a real binary —\n * including one that exits immediately or runs slowly — reports `spawn` with no\n * error.\n *\n * `state` exists because the promise must never be able to hang. Two cases would\n * otherwise leave it pending forever:\n *\n * - a registry that registers a job without calling `run` (a stub, or a future\n * controller with a different contract). `dsh-jobs-local` does call it\n * synchronously (`hooks = spec.run(handle)`), but the tool cannot assume which\n * implementation it is talking to — and hanging is far worse than reporting\n * \"started\", because the model never gets an answer at all.\n * - `run` throwing synchronously, which would skip both event handlers.\n *\n * Both are treated as \"started\" only in the sense of \"do not claim a failure we did\n * not observe\": a genuinely failed job still settles as `failed` through `done`, and\n * the Host's job tools report that.\n */\n let reportStarted: ((failure: string | null) => void) | undefined;\n const state: { settled: boolean; ran: boolean } = { settled: false, ran: false };\n const started = new Promise<string | null>((settle) => {\n reportStarted = (failure) => {\n if (state.settled) return;\n state.settled = true;\n settle(failure);\n };\n });\n\n const jobId = jobs.start({\n kind: \"bash\",\n label: `pstack_script ${args.id}`,\n ...(options.caller === undefined ? {} : { owner: options.caller }),\n outputLimitBytes: args.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES,\n run: (job) => {\n state.ran = true;\n const settleStarted = reportStarted as (failure: string | null) => void;\n let child: ChildProcess;\n try {\n child = spawnChild(execPath, [path, ...args.args], {\n cwd,\n env: environment,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n windowsHide: true,\n });\n } catch (error) {\n // A synchronous throw from `spawn` (an invalid argument, or a platform\n // failure that does not become an event) would otherwise leave the caller\n // waiting forever, because no listener was ever attached.\n settleStarted((error as NodeJS.ErrnoException).code ?? \"spawn-threw\");\n throw error;\n }\n\n child.stdout?.on(\"data\", (chunk: Buffer) => job.append(chunk.toString(\"utf8\"), { channel: \"stdout\" }));\n child.stderr?.on(\"data\", (chunk: Buffer) => job.append(chunk.toString(\"utf8\"), { channel: \"stderr\" }));\n\n // Report the startup outcome to the caller waiting above. `once` on both, and\n // the promise settles on whichever arrives first, so a late `error` after a\n // successful `spawn` cannot overwrite the answer.\n child.once(\"spawn\", () => settleStarted(null));\n child.once(\"error\", (error) => {\n // Only the error code. `child_process`'s `error.message` embeds the host's\n // absolute path to the artefact, and the model has no business seeing the\n // host layout.\n settleStarted((error as NodeJS.ErrnoException).code ?? \"spawn-failed\");\n });\n\n const done = new Promise<{ status: \"completed\" | \"killed\" | \"failed\"; detail?: string }>(\n (settle) => {\n child.once(\"error\", (error) => {\n settle({\n status: \"failed\",\n detail: (error as NodeJS.ErrnoException).code ?? \"spawn-failed\",\n });\n });\n child.once(\"close\", (code, signal) => {\n if (signal !== null) settle({ status: \"killed\", detail: `signal ${signal}` });\n else if (code === 0) settle({ status: \"completed\" });\n else settle({ status: \"failed\", detail: `exit code ${code ?? \"unknown\"}` });\n });\n },\n );\n\n return {\n cancel: (reason?: string) => {\n child.kill(\"SIGTERM\");\n if (reason !== undefined) job.updateProgress(`cancelling: ${reason}`);\n },\n done,\n };\n },\n });\n\n // Wait for the real startup outcome, not a guess.\n //\n // Three attempts preceded this, and each failed differently:\n //\n // 1. A fixed 250 ms wait. Failures before it were caught, failures after it were\n // reported to the model as \"started\" and never corrected.\n // 2. One microtask (`await Promise.resolve()`). `spawn` reports ENOENT\n // asynchronously, so this caught nothing at all — measured: after one microtask\n // the error had not fired yet, and it appeared ~100 ms later. That made the tool\n // report `running` for a job that never started, which is worse than the timer.\n // 3. This: race `spawn` against `error`, which are mutually exclusive and jointly\n // exhaustive for \"did the child start\". No duration is involved, so no\n // latency assumption can be wrong.\n //\n // If the registry never invoked the producer, the promise would never settle, so\n // that case is answered up front rather than awaited. `dsh-jobs-local` calls\n // `spec.run` synchronously, but a stub registry need not, and hanging is the one\n // outcome the model can do nothing with.\n /**\n * Turn a reported start failure into the tool error.\n *\n * The code only. A foreground run is how the caller sees the actual error, and\n * naming the code keeps host paths out of the model's context.\n */\n const failStart = (reason: string): never => {\n throw new ScriptMissingError(\n `background job ${jobId} failed to start (${reason}). ` +\n \"Run the same script in the foreground to see its error.\",\n );\n };\n\n if (!state.ran) {\n // The registry registered a job without invoking the producer, so there is no\n // child to observe. Report what the registry itself says, and refuse to present a\n // terminal failure as a started job.\n const view = jobs.get(jobId, options.caller);\n if (view.status === \"failed\") failStart(\"the job controller reported a failed start\");\n return { kind: \"background\", id: args.id, jobId, status: view.status };\n }\n\n const startFailure = await started;\n if (startFailure !== null) failStart(startFailure);\n\n // A controller may also settle the job as failed after `spawn` but before this\n // call returns. That is still a start failure from the model's point of view: it\n // has been told nothing it could act on yet.\n const view = jobs.get(jobId, options.caller);\n if (view.status === \"failed\") failStart(\"the job controller reported a failed start\");\n return { kind: \"background\", id: args.id, jobId, status: view.status };\n}\n\n/**\n * Render one captured stream as a fenced block, byte for byte.\n *\n * `trimEnd` used to run here, which is wrong for two reasons:\n *\n * - A stream of only whitespace passed the `!== \"\"` test and then rendered as an\n * empty-looking `stdout:` line, so a reader concluded the script printed nothing.\n * - `check-plan`'s contract includes exact line counts and blank-line separators, so\n * a trimmed block is a different document — and the model reads this text as the\n * script's output.\n *\n * A fence keeps every byte visible, including trailing blank lines. The fence is\n * lengthened past the longest backtick run in the content so it can never be closed\n * early by the content itself. Nothing inside is escaped or reinterpreted.\n */\nexport function renderCapturedStream(text: string): readonly string[] {\n if (text === \"\") return [];\n const longestRun = Math.max(0, ...(text.match(/`+/g) ?? []).map((run) => run.length));\n const fence = \"`\".repeat(Math.max(3, longestRun + 1));\n // Strip exactly one final newline: `join(\"\\n\")` puts it back, and keeping it would\n // add a visible blank line that the script did not print.\n const body = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n return [fence, body, fence];\n}\n\n/** Render one outcome for the model. */\nexport function renderScriptOutcome(outcome: ScriptOutcome): string {\n if (outcome.kind === \"background\") {\n return [\n `started ${outcome.id} as background job ${outcome.jobId} (status: ${outcome.status}).`,\n \"Read or stop it through the Host's job tools; this tool does not poll.\",\n ].join(\"\\n\");\n }\n\n const lines = [\n `${outcome.id} exited with code ${outcome.exitCode}` +\n `${outcome.signal === null ? \"\" : ` (signal ${outcome.signal})`}` +\n `${outcome.timedOut ? ` after the timeout` : \"\"} in ${outcome.durationMs}ms`,\n ];\n // Omitted only when the stream is genuinely empty; a stream of blank lines is\n // shown, so \"printed nothing\" and \"printed newlines\" stay distinguishable.\n if (outcome.stdout !== \"\") lines.push(\"\", \"stdout (exact bytes):\", ...renderCapturedStream(outcome.stdout));\n if (outcome.stderr !== \"\") lines.push(\"\", \"stderr (exact bytes):\", ...renderCapturedStream(outcome.stderr));\n if (outcome.stdoutTruncated) lines.push(\"\", \"stdout was truncated at the output limit.\");\n if (outcome.stderrTruncated) lines.push(\"\", \"stderr was truncated at the output limit.\");\n if (outcome.exitCode !== 0 || outcome.signal !== null || outcome.timedOut) {\n lines.push(\"\", \"This run failed. Read the output above; do not report success.\");\n }\n return lines.join(\"\\n\");\n}\n\nconst TOOL_DESCRIPTION = [\n \"Run one of the scripts bundled with pstack, using the Host's own Node runtime.\",\n \"\",\n \"Use this instead of a shell for the pstack scripts. It works in both deployments: the npm CLI and\",\n \"the Desktop app, where `node` is not on PATH because the Desktop process is Electron.\",\n \"\",\n \"`id` selects a bundled script by name; only those names are accepted, so this is not a general\",\n \"shell and takes no path or command string. `args` are passed to the script verbatim.\",\n \"\",\n \"The run starts in this session's directory, so a relative path in `args` lands inside the\",\n \"workspace or worktree the session is using.\",\n \"\",\n \"**This tool does not go through command approval, and the Host's sandbox policy does not apply**\",\n \"to it. It spawns a Node process directly instead of asking `ctx.shell` to run a command, which is\",\n \"what keeps it working where no `node` is on PATH. The scripts are this package's own audited\",\n \"artefacts and `id` cannot name anything else, but the consequence is real: unlike a bash call, no\",\n \"approval prompt is shown and no filesystem policy is enforced on what the script writes. Say so\",\n \"when a task depends on that difference.\",\n \"\",\n \"A nonzero exit, a signal, a timeout, and truncated output all come back in the result with the\",\n \"captured output, so a failed run is visible rather than silent.\",\n \"\",\n \"Set `background: true` for a long run. That needs the Host jobs service; without it the call is\",\n \"refused with a reason instead of hanging. Background runs return a job id and a settlement notice\",\n \"arrives later — do not poll.\",\n].join(\"\\n\");\n\n/** The model-facing JSON Schema for `parameters`. */\nexport function buildScriptParameters(): Record<string, unknown> {\n const properties: Record<string, unknown> = {\n id: {\n type: \"string\",\n enum: [...SCRIPT_IDS],\n description: \"Which bundled pstack script to run. Not a path: only these names are accepted.\",\n },\n args: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Arguments passed to the script verbatim, one array entry per argument.\",\n },\n cwd: {\n type: \"string\",\n description:\n \"Working directory for the script. Relative paths resolve against this session's directory; an absolute path is used as-is. Omitted runs in this session's directory.\",\n },\n background: {\n type: \"boolean\",\n description:\n \"Run as a Host background job and return its id. Requires the jobs service; a composition without it refuses the call.\",\n },\n timeoutMs: {\n type: \"integer\",\n description: `Foreground wall-clock limit; 1 or greater, and values below 1 are rejected. Defaults to ${DEFAULT_TIMEOUT_MS}; values above ${MAX_TIMEOUT_MS} are rejected.`,\n },\n outputLimitBytes: {\n type: \"integer\",\n description: `Per-stream capture limit; 1 or greater, and values below 1 are rejected. Defaults to ${DEFAULT_OUTPUT_LIMIT_BYTES}.`,\n },\n };\n\n return {\n type: \"object\",\n additionalProperties: false,\n properties,\n required: [\"id\"],\n };\n}\n\n/**\n * The declared shape of one outcome, exported so tests can validate real results.\n *\n * Split by `kind` instead of one flat object. The flat form could only require\n * `kind` and `id`, so a foreground result that omitted `exitCode` was still\n * \"valid\" and the schema proved nothing about the fields the model reads.\n *\n * `signal` is nullable because `child_process` reports `null` whenever the child\n * exited on its own — that is every successful run. Declaring it `string` made\n * the registry reject the tool's own normal output: `createSuccessResult()`\n * validates before rendering, so **every** clean exit surfaced to the model as\n * `tool \"pstack_script\" returned invalid output`.\n */\nexport const OUTPUT_SCHEMA: JsonSchemaNode = {\n oneOf: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n kind: { type: \"string\", const: \"foreground\" },\n id: { type: \"string\", description: \"Which bundled script ran.\" },\n exitCode: { type: \"integer\", description: \"The child's exit code.\" },\n signal: {\n oneOf: [{ type: \"string\" }, { type: \"null\" }],\n description: \"Terminating signal, or null when the script exited on its own.\",\n },\n timedOut: {\n type: \"boolean\",\n description: \"True when this tool's own timeout killed the run.\",\n },\n stdout: { type: \"string\" },\n stderr: { type: \"string\" },\n stdoutTruncated: { type: \"boolean\" },\n stderrTruncated: { type: \"boolean\" },\n durationMs: { type: \"integer\" },\n },\n required: [\n \"kind\",\n \"id\",\n \"exitCode\",\n \"signal\",\n \"timedOut\",\n \"stdout\",\n \"stderr\",\n \"stdoutTruncated\",\n \"stderrTruncated\",\n \"durationMs\",\n ],\n },\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n kind: { type: \"string\", const: \"background\" },\n id: { type: \"string\", description: \"Which bundled script started.\" },\n jobId: { type: \"string\", description: \"The Host job id to read or stop.\" },\n status: { type: \"string\", description: \"Job status when the call returned.\" },\n },\n required: [\"kind\", \"id\", \"jobId\", \"status\"],\n },\n ],\n};\n\n/** The slices of the Cordis context this tool needs. */\nexport interface PstackScriptHost {\n readonly tools: ToolsHost;\n /** Resolves the Session's current directory; see `WorkingDirectoryHost`. */\n readonly workingDirectory: WorkingDirectoryHost;\n /**\n * Optional, and read per call rather than snapshotted.\n *\n * A profile may mount a job controller after this row, so a value read once in\n * `apply` would stay `undefined` forever. `ctx.get(\"jobs\")` is rc.3's\n * optional-service read: it returns `undefined` instead of throwing when the\n * service is absent, which is why `jobs` is not declared in `inject`.\n */\n readonly getJobs?: () => JobsHost | undefined;\n}\n\n/** Build the tool for one row. */\nexport function createPstackScriptTool(host: PstackScriptHost): ToolDefinition {\n return {\n name: TOOL_NAME,\n description: TOOL_DESCRIPTION,\n parameters: buildScriptParameters(),\n output: {\n schema: OUTPUT_SCHEMA,\n render: (_args, value) => [{ type: \"text\", text: renderScriptOutcome(value as ScriptOutcome) }],\n },\n // Two runs of the same script may write the same store, so this is not marked\n // concurrency-safe; the Host then serializes them the way it serializes any tool.\n isConcurrencySafe: () => false,\n /**\n * The Host's last-resort budget for one call.\n *\n * `DEFAULT_TIMEOUT_MS` is the tool's own timer for a foreground run, and it fires\n * first in every normal case: it kills the child and returns a `timedOut` result the\n * model can read. This budget is larger on purpose, so it only ever wins when the\n * tool's own machinery failed to return at all — a hung `await`, a timer that never\n * ran — and the alternative would be a session blocked with no answer.\n *\n * `MAX_TIMEOUT_MS` rather than a new number: it is already the ceiling the tool\n * accepts for a caller-supplied `timeoutMs`, so the Host budget can never be the\n * thing that cuts short a run the caller was allowed to request.\n *\n * It does not bound a background job. The wrapper disposes its timer when `execute`\n * settles, which is milliseconds after `jobs.start` returns.\n */\n timeoutMs: MAX_TIMEOUT_MS,\n async execute(rawArgs, exec: ToolRunContext) {\n const args = parseScriptArgs(rawArgs);\n // Ask the Host where this Session currently is, rather than inheriting the\n // Host process's directory. `ensure` is the same seam `dsh-tool-bash` uses.\n const sessionCwd =\n exec.agent === undefined\n ? undefined\n : await host.workingDirectory.ensure(exec.agent, exec.signal);\n const cwd = resolveWorkingDirectory(args.cwd, sessionCwd);\n if (args.background) {\n return await runBackground(args, {\n jobs: host.getJobs?.(),\n ...(cwd === undefined ? {} : { cwd }),\n ...(exec.agent === undefined ? {} : { caller: exec.agent.id }),\n });\n }\n return await runForeground(args, {\n signal: exec.signal,\n ...(cwd === undefined ? {} : { cwd }),\n });\n },\n };\n}\n\n/** Cordis row name, used by Loader diagnostics. */\nexport const name = \"pstack-script\";\n\n/**\n * The hard dependencies.\n *\n * `tools` is the registry this row registers into; `workingDirectory` is what\n * turns the tool's `cwd` argument and its omitted case into the Session's\n * directory. Both are always present in a profile that has an Agent at all.\n *\n * `jobs` is deliberately absent: a composition without a job controller still\n * registers the tool, and only `background: true` is refused. Declaring `jobs`\n * here would keep the whole fiber inactive in every profile that has no job\n * controller, which is most of them.\n */\nexport const inject = [\"tools\", \"workingDirectory\"];\n\n/**\n * Register `pstack_script` for this row.\n *\n * `inject` is what makes `ctx.tools` readable at all: without it Cordis runs\n * `apply` immediately and the service proxy throws\n * `cannot get property \"tools\" without inject`, so the row would register nothing\n * in any profile.\n */\nexport function apply(ctx: PstackScriptHost & { get?: (name: string) => JobsHost | undefined }): void {\n ctx.tools.register(\n createPstackScriptTool({\n tools: ctx.tools,\n workingDirectory: ctx.workingDirectory,\n // Optional-service read, so a controller mounted later is still seen.\n getJobs: () => ctx.get?.(\"jobs\"),\n }),\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,336 @@
1
+ import { f as SubagentCapabilities, g as ToolDefinition, h as SubagentsHost, n as AgentOptions, o as JsonSchemaNode, r as AgentsHost, s as LlmHost, t as Agent, v as ToolsHost } from "./host-contract-o47JH_r8.mjs";
2
+ //#region scripts/role-manifest.d.ts
3
+ /**
4
+ * The one pstack role manifest.
5
+ *
6
+ * Every consumer of the role vocabulary reads this module: `src/roles.ts` for
7
+ * exact lookup and panel classification, `src/models.ts` for value shapes,
8
+ * `scripts/build-assets.ts` for the generated example file, and the
9
+ * `setup-pstack` skill text, which points at that example instead of listing the
10
+ * roles again. A second literal role list anywhere is a bug.
11
+ *
12
+ * Keys are matched exactly after normalization. Commas are part of some keys
13
+ * (`feature, refactoring`), so nothing here may be split on a comma.
14
+ *
15
+ * This module has no Node imports: the Host bundle imports it directly.
16
+ */
17
+ /** Whether a role holds one route or a comma-separated list of routes. */
18
+ type RoleValueShape = "single" | "list";
19
+ interface RoleSpec {
20
+ /** Exact key as written in the role file. Commas belong to the key. */
21
+ readonly key: string;
22
+ readonly shape: RoleValueShape;
23
+ /**
24
+ * A list role that automatically starts one child per entry. Only the three
25
+ * panel roles set this; every other list role selects one entry.
26
+ */
27
+ readonly autoPanel: boolean;
28
+ /** Skills and playbooks that read this role. */
29
+ readonly consumers: readonly string[];
30
+ readonly summary: string;
31
+ }
32
+ //#endregion
33
+ //#region src/models.d.ts
34
+ /** One resolved child route. */
35
+ type ChildRoute = {
36
+ readonly kind: "inherit";
37
+ } | {
38
+ readonly kind: "route";
39
+ readonly provider: string;
40
+ readonly model: string;
41
+ readonly effort?: string;
42
+ };
43
+ /** How one role's value expands into children. */
44
+ type RouteSelectionMode = "single" | "panel" | "selected";
45
+ interface RouteSelection {
46
+ readonly role: RoleSpec;
47
+ readonly mode: RouteSelectionMode;
48
+ /** Whether the role came from the file or from the inherit-parent fallback. */
49
+ readonly configured: boolean;
50
+ readonly routes: readonly ChildRoute[];
51
+ /** How many entries the role holds, before selection. */
52
+ readonly total: number;
53
+ readonly roleIndex?: number;
54
+ readonly issues: readonly string[];
55
+ }
56
+ //#endregion
57
+ //#region src/effort.d.ts
58
+ /**
59
+ * The `agentOptions` one (route, effort override) pair contributes.
60
+ *
61
+ * The two independent halves are easy to get wrong in opposite directions:
62
+ *
63
+ * - an inherit-parent route with an override must still send `reasoningEffort`,
64
+ * which means `{ reasoningEffort }` on its own is a real `agentOptions` value and
65
+ * the backend still has to support the capability;
66
+ * - an override always wins over the role line, because the caller asked for this
67
+ * delegation specifically.
68
+ */
69
+ export declare function agentOptionsFor(route: ChildRoute, effortOverride?: string): AgentOptions | undefined;
70
+ //#endregion
71
+ //#region src/subagent.d.ts
72
+ /** Cordis row name, used by Loader diagnostics. */
73
+ export declare const name = "pstack-subagent";
74
+ /**
75
+ * Delegation needs the tool registry, the subagent registry, and the LLM adapters.
76
+ *
77
+ * `agents` is deliberately **not** here. It is used only to reach a child this tool
78
+ * has just started, and a composition without it still delegates: the children keep
79
+ * the effort their request carried and cannot change it afterwards. Declaring it as
80
+ * a hard dependency would make the whole row inert in such a profile, so the tool
81
+ * would not exist at all — the failure `pstack-history` already paid for once with
82
+ * `sessionQuery`. It is read per call through `ctx.get("agents")` instead, which is
83
+ * why a controller mounted after this row is still seen.
84
+ */
85
+ export declare const inject: string[];
86
+ /** Model-facing tool name. */
87
+ export declare const TOOL_NAME = "pstack_subagent";
88
+ /** Backend used when the row does not name one. */
89
+ export declare const DEFAULT_SUBAGENT_PROVIDER = "spawn";
90
+ /** Agent type used when the call does not name one. */
91
+ export declare const DEFAULT_AGENT_KIND = "poteto-agent";
92
+ /** The child kinds this tool knows how to brief. */
93
+ export declare const AGENT_KINDS: readonly ["poteto-agent", "comment-sicko", "general"];
94
+ export type AgentKind = (typeof AGENT_KINDS)[number];
95
+ /** One exact route a row may authorize for explicit child selection. */
96
+ export interface AllowedModelRoute {
97
+ readonly provider: string;
98
+ readonly model: string;
99
+ }
100
+ /** Row configuration. */
101
+ export interface PstackSubagentConfig {
102
+ /** `ctx.subagents` provider to start children on. */
103
+ readonly provider: string;
104
+ /**
105
+ * This row's delegation-depth cap. A call may lower it and may never exceed it.
106
+ * Omitted means the Host setting decides.
107
+ */
108
+ readonly maxDepth?: number;
109
+ /**
110
+ * Exact routes this row authorizes. Empty means no explicit route limit. This is
111
+ * pstack's own check and is unrelated to the native session `allowedModels`
112
+ * policy, which this plugin does not read.
113
+ */
114
+ readonly allowedModels: readonly AllowedModelRoute[];
115
+ /** Non-empty allowlist; also what makes `agentPreset` appear in the schema. */
116
+ readonly allowedAgentPresets: readonly string[];
117
+ }
118
+ /** What a call asked for, after argument validation. */
119
+ export interface DelegationArgs {
120
+ readonly description: string;
121
+ readonly prompt: string;
122
+ readonly agent: AgentKind;
123
+ readonly role?: string;
124
+ readonly cwd?: string;
125
+ readonly agentPreset?: string;
126
+ readonly maxDepth?: number;
127
+ readonly roleIndex?: number;
128
+ /**
129
+ * Effort for every child this call starts, overriding each role line's value.
130
+ *
131
+ * Validated per entry against the model that entry will actually request, so a
132
+ * panel with one incompatible entry starts nothing rather than half its children.
133
+ */
134
+ readonly reasoningEffort?: string;
135
+ }
136
+ /** One child this call started. */
137
+ export interface ChildRecord {
138
+ readonly childId: string;
139
+ readonly label: string;
140
+ readonly agent: string;
141
+ readonly role: string;
142
+ readonly route: string;
143
+ /** The effort this child runs at, when any applies. */
144
+ readonly effort?: string;
145
+ readonly maxDepth?: number;
146
+ }
147
+ export interface DelegationResult {
148
+ readonly kind: "activation" | "activations";
149
+ readonly children: readonly ChildRecord[];
150
+ readonly diagnostics: readonly string[];
151
+ }
152
+ /** Raised for a configuration or argument problem, before any child starts. */
153
+ export declare class DelegationError extends Error {
154
+ constructor(message: string);
155
+ }
156
+ /** Raised when a child starts but a later entry in the same call fails. */
157
+ export declare class PartialDelegationError extends DelegationError {
158
+ readonly startedChildren: readonly ChildRecord[];
159
+ constructor(message: string, startedChildren: readonly ChildRecord[]);
160
+ }
161
+ /** Raised when a required agent preamble is missing or unusable. */
162
+ export declare class PreambleError extends DelegationError {
163
+ constructor(message: string);
164
+ }
165
+ /** The live services and locations one delegation reads. */
166
+ export interface SubagentRuntime {
167
+ readonly subagents: SubagentsHost;
168
+ readonly llm: LlmHost;
169
+ /**
170
+ * The Agent registry, used to reach a child this tool just started.
171
+ *
172
+ * Optional because a row can be composed without the `agents` service and a
173
+ * delegation still works there: the children then keep the effort their request
174
+ * carried and simply cannot change it afterwards. Making it required would turn a
175
+ * missing service into a row that registers no tool at all.
176
+ */
177
+ readonly agents?: AgentsHost;
178
+ /** `$DSH_HOME`, home of the role file. */
179
+ readonly dshHome: string;
180
+ /** Absolute package assets root, home of the agent preambles. */
181
+ readonly assetsRoot: string;
182
+ }
183
+ interface StandardSchemaIssue {
184
+ readonly message: string;
185
+ readonly path?: readonly (string | number)[];
186
+ }
187
+ type StandardSchemaResult = {
188
+ readonly value: PstackSubagentConfig;
189
+ readonly issues?: undefined;
190
+ } | {
191
+ readonly issues: readonly StandardSchemaIssue[];
192
+ };
193
+ /**
194
+ * Minimal Standard Schema for this row, so an omitted `config` is valid and a
195
+ * typo fails at load instead of at the first delegation.
196
+ */
197
+ export declare const Config: {
198
+ "~standard": {
199
+ version: number;
200
+ vendor: string;
201
+ validate(value: unknown): StandardSchemaResult;
202
+ };
203
+ };
204
+ /**
205
+ * Read one agent preamble from the package assets by absolute path.
206
+ *
207
+ * A missing or empty preamble blocks that agent type. It never degrades into a
208
+ * plain child, because a `comment-sicko` or `poteto-agent` child without its
209
+ * instructions is a different agent wearing the name.
210
+ */
211
+ export declare function loadAgentPreamble(agent: AgentKind, assetsRoot: string): string | undefined;
212
+ /** One absolute path the Host injects into a child brief. */
213
+ export interface InjectedPath {
214
+ readonly label: string;
215
+ readonly path: string;
216
+ }
217
+ /**
218
+ * Compose the self-contained prompt one child receives.
219
+ *
220
+ * The preambles used to locate things themselves; the Host injects the absolute
221
+ * paths instead, so a child never scans a plugin directory or guesses a version.
222
+ */
223
+ export declare function composeChildPrompt(input: {
224
+ readonly preamble?: string;
225
+ readonly injectedPaths?: readonly InjectedPath[];
226
+ readonly task: string;
227
+ }): string;
228
+ /** One planned child, before any Host call. */
229
+ export interface ChildPlan {
230
+ readonly index: number;
231
+ readonly total: number;
232
+ readonly label: string;
233
+ readonly agent: AgentKind;
234
+ readonly role: string;
235
+ readonly route: ChildRoute;
236
+ readonly routeLabel: string;
237
+ /** The effort this child will actually run at: the override when present, else the role line's. */
238
+ readonly effort?: string;
239
+ readonly agentOptions?: AgentOptions;
240
+ readonly agentPreset?: string;
241
+ readonly cwd?: string;
242
+ readonly maxDepth?: number;
243
+ }
244
+ /** The decided plan for one call. */
245
+ export interface DelegationPlan {
246
+ readonly agent: AgentKind;
247
+ readonly prompt: string;
248
+ readonly provider: string;
249
+ readonly role: string;
250
+ readonly mode: RouteSelection["mode"];
251
+ readonly roleIndex?: number;
252
+ readonly roleTotal: number;
253
+ /** The call-wide effort override, when it supplied one. */
254
+ readonly reasoningEffort?: string;
255
+ readonly children: readonly ChildPlan[];
256
+ readonly diagnostics: readonly string[];
257
+ }
258
+ /** The text form of one route, as reported back to the model. */
259
+ export declare function routeLabel(route: ChildRoute): string;
260
+ /**
261
+ * The route text a caller sees when an effort override wins.
262
+ *
263
+ * `routeLabel` describes the role file, which stops being the whole truth once the
264
+ * caller overrode the effort. Reporting the file's value there would tell the model
265
+ * its override had been ignored.
266
+ */
267
+ export declare function plannedRouteLabel(route: ChildRoute, effortOverride?: string): string;
268
+ /**
269
+ * Take the smallest of the requested depth, this row's cap, and the Host setting.
270
+ *
271
+ * A request above the row cap is an error rather than a silent clamp, so a caller
272
+ * cannot believe it asked for two levels when the row allows one.
273
+ */
274
+ export declare function resolveDelegationDepth(configured: number | undefined, requested: number | undefined, hostLimit: number | undefined): number | undefined;
275
+ /**
276
+ * Validate the model-facing arguments.
277
+ *
278
+ * The tool schema is advisory at the model boundary, so every constraint is
279
+ * re-checked here and reported with the reason.
280
+ */
281
+ export declare function parseDelegationArgs(raw: unknown, config?: PstackSubagentConfig): DelegationArgs;
282
+ /**
283
+ * Resolve the role file, the routes, the prompt, and the depth into one plan.
284
+ *
285
+ * Pure with respect to the Host: it reads the role file and the packaged preamble,
286
+ * and it makes no service call.
287
+ */
288
+ export declare function planDelegation(args: DelegationArgs, runtime: SubagentRuntime, config?: PstackSubagentConfig): DelegationPlan;
289
+ /** Check the backend can enforce what this call asks for. */
290
+ export declare function assertProviderCapabilities(capabilities: SubagentCapabilities, providerName: string, asks: {
291
+ readonly agentOptions: boolean;
292
+ readonly agentPreset: boolean;
293
+ readonly maxDepth: boolean;
294
+ }): void;
295
+ /**
296
+ * Start the planned children.
297
+ *
298
+ * Everything that can be rejected has already been rejected. A failure at entry N
299
+ * keeps children 1..N-1 running and reports their ids, because the runtime owns
300
+ * those activations and this tool cannot roll them back.
301
+ */
302
+ export declare function startPlannedChildren(parent: Agent, plan: DelegationPlan, runtime: SubagentRuntime, config: PstackSubagentConfig, signal: AbortSignal): Promise<DelegationResult>;
303
+ /** Full delegation: plan, validate, start. */
304
+ export declare function delegate(parent: Agent, args: DelegationArgs, runtime: SubagentRuntime, config?: PstackSubagentConfig, signal?: AbortSignal): Promise<DelegationResult>;
305
+ export declare const OUTPUT_SCHEMA: JsonSchemaNode;
306
+ /** Render one delegation result for the model. */
307
+ export declare function renderDelegationResult(result: DelegationResult): string;
308
+ /** Build the model-facing tool for one row. */
309
+ export declare function createPstackSubagentTool(runtime: SubagentRuntime, config?: PstackSubagentConfig): ToolDefinition;
310
+ /**
311
+ * The slices of the Cordis context this row uses.
312
+ *
313
+ * `agents` is optional in both directions: it is absent from `inject` and read per
314
+ * call below, so a profile that mounts no agent registry still registers this tool.
315
+ * See the `inject` comment for why a hard dependency is the wrong shape here.
316
+ */
317
+ export interface PstackSubagentHost {
318
+ readonly tools: ToolsHost;
319
+ readonly subagents: SubagentsHost;
320
+ readonly llm: LlmHost;
321
+ }
322
+ /** Build the runtime a real Host call reads. */
323
+ export declare function hostRuntime(ctx: PstackSubagentHost, getAgentRegistry?: () => AgentsHost | undefined): SubagentRuntime;
324
+ /**
325
+ * Register `pstack_subagent` for this row.
326
+ *
327
+ * @param ctx - context providing the tool registry, the subagent registry, the LLM
328
+ * adapters, and optionally `ctx.get` for the agent registry.
329
+ * @param config - validated row config.
330
+ */
331
+ export declare function apply(ctx: PstackSubagentHost & {
332
+ get?: (name: string) => AgentsHost | undefined;
333
+ }, config?: PstackSubagentConfig): void;
334
+ //#endregion
335
+ export {};
336
+ //# sourceMappingURL=subagent.d.mts.map