dsh-pstack 0.1.0 → 0.2.0-beta.3
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/LICENSE +2 -3
- package/README.md +174 -148
- package/README.zh.md +185 -0
- package/assets/logo.png +0 -0
- package/cordis.patch.yml +103 -14
- package/dist/host-contract-CUPSpeNk.d.mts +22 -0
- package/dist/index.d.mts +87 -14
- package/dist/index.mjs +370 -143
- package/dist/models.d.mts +44 -0
- package/dist/models.mjs +201 -0
- package/dist/paths-BT77khwm.mjs +48 -0
- package/dist/{LICENSE → pstack/LICENSE} +1 -2
- package/dist/pstack/README.md +291 -0
- package/{assets → dist/pstack}/agents/comment-sicko.md +1 -1
- package/dist/pstack/agents/poteto-agent.md +9 -0
- package/{assets → dist/pstack}/docs/guide/01-setup.md +11 -9
- package/dist/{assets → pstack}/docs/guide/02-poteto-mode.md +2 -2
- package/{assets → dist/pstack}/docs/guide/04-design.md +4 -4
- package/dist/{assets → pstack}/docs/guide/05-build-and-clean.md +3 -3
- package/dist/{assets → pstack}/docs/guide/06-verify-and-ship.md +11 -1
- package/{assets → dist/pstack}/docs/guide/07-overnight.md +10 -10
- package/dist/{assets → pstack}/docs/guide/09-make-it-yours.md +4 -4
- package/{assets → dist/pstack}/docs/guide/10-recipes-and-pitfalls.md +6 -4
- package/dist/pstack/docs/guide/images/design.jpg +0 -0
- package/dist/pstack/docs/guide/images/overnight.jpg +0 -0
- package/dist/pstack/docs/guide/images/recipes.jpg +0 -0
- package/dist/pstack/docs/guide/images/router.jpg +0 -0
- package/dist/pstack/docs/guide/images/understanding.jpg +0 -0
- package/dist/pstack/docs/guide/images/verification.jpg +0 -0
- package/dist/pstack/docs/harness/dsh.md +98 -0
- package/{assets → dist/pstack}/skills/architect/SKILL.md +5 -5
- package/dist/pstack/skills/arena/SKILL.md +50 -0
- package/{assets → dist/pstack}/skills/automate-me/SKILL.md +14 -13
- package/dist/{assets → pstack}/skills/benchmark-checklist/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/blast-radius/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/bro/SKILL.md +0 -2
- package/dist/{assets → pstack}/skills/correct/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/create-formal-verification-skill/SKILL.md +5 -3
- package/{assets → dist/pstack}/skills/create-verification-skill/SKILL.md +6 -6
- package/{assets → dist/pstack}/skills/figure-it-out/SKILL.md +4 -4
- package/dist/{assets → pstack}/skills/how/SKILL.md +4 -4
- package/{assets → dist/pstack}/skills/how/references/explainer-prompt.md +1 -1
- package/dist/pstack/skills/in-your-own-words/SKILL.md +7 -0
- package/dist/pstack/skills/interrogate/SKILL.md +33 -0
- package/{assets → dist/pstack}/skills/ketchup/SKILL.md +1 -3
- package/dist/pstack/skills/ketchup-video/SKILL.md +29 -0
- package/{assets → dist/pstack}/skills/maintain-formal-verification-skill/SKILL.md +2 -2
- package/dist/{assets → pstack}/skills/maintain-verification-skill/SKILL.md +3 -3
- package/dist/pstack/skills/make-bot-ui/SKILL.md +117 -0
- package/{assets → dist/pstack}/skills/no-comments/SKILL.md +4 -4
- package/{assets → dist/pstack}/skills/poteto-help/SKILL.md +21 -17
- package/dist/{assets → pstack}/skills/poteto-help/references/prompting.md +1 -1
- package/dist/{assets → pstack}/skills/poteto-help/references/recipes.md +3 -3
- package/{assets → dist/pstack}/skills/poteto-mode/SKILL.md +20 -21
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/authoring-a-skill.md +1 -1
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/autonomous-run.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/autopilot-full.md +7 -7
- package/dist/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +8 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/babysit.md +3 -3
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/bug-fix.md +3 -3
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/eval.md +1 -1
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/feature.md +1 -1
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/hillclimb.md +1 -1
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/multi-phase-plan.md +13 -13
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/opening-a-pr.md +3 -3
- package/dist/pstack/skills/poteto-mode/playbooks/orchestrate.md +25 -0
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/perf-issue.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/prototype.md +1 -1
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/refactoring.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/runtime-forensics.md +1 -1
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/session-pickup.md +1 -1
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/shipping.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/visual-parity.md +1 -1
- package/dist/{assets → pstack}/skills/poteto-mode/playbooks/worktree-cleanup.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/scripts/check-plan.mjs +83 -70
- package/dist/{assets/skills/poteto-mode/scripts/check-plan.test.ts → pstack/skills/poteto-mode/scripts/check-plan.test.mjs} +53 -21
- package/dist/pstack/skills/poteto-mode/scripts/expect.mjs +254 -0
- package/dist/pstack/skills/poteto-mode/scripts/support.mjs +93 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.mjs +123 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +217 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.mjs +119 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.mjs +626 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.test.mjs +521 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.mjs +657 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +523 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/render.mjs +106 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/types.mjs +9 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +5 -0
- package/dist/pstack/skills/poteto-mode/scripts/worktree-audit.mjs +668 -0
- package/dist/pstack/skills/poteto-mode/scripts/worktree-audit.test.mjs +134 -0
- package/{assets → dist/pstack}/skills/principle-attack-the-premise/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-boundary-discipline/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-build-the-lever/SKILL.md +0 -3
- package/dist/pstack/skills/principle-encode-lessons-in-structure/SKILL.md +11 -0
- package/dist/pstack/skills/principle-exhaust-the-design-space/SKILL.md +9 -0
- package/{assets → dist/pstack}/skills/principle-experience-first/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-explain-the-number/SKILL.md +3 -4
- package/dist/{assets → pstack}/skills/principle-fix-root-causes/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-foundational-thinking/SKILL.md +0 -2
- package/dist/{assets → pstack}/skills/principle-guard-the-context-window/SKILL.md +0 -2
- package/dist/{assets → pstack}/skills/principle-laziness-protocol/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-make-operations-idempotent/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-minimize-reader-load/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-model-the-domain/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-never-block-on-the-human/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-outcome-oriented-execution/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-prove-it-works/SKILL.md +3 -3
- package/dist/{assets → pstack}/skills/principle-redesign-from-first-principles/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-separate-before-serializing-shared-state/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-sequence-verifiable-units/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/principle-subtract-before-you-add/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-test-behavior-not-implementation/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/principle-type-system-discipline/SKILL.md +1 -3
- package/dist/{assets → pstack}/skills/recall/SKILL.md +4 -4
- package/dist/pstack/skills/reflect/SKILL.md +71 -0
- package/{assets → dist/pstack}/skills/reflect/references/divergent-reviewer.md +1 -1
- package/{assets → dist/pstack}/skills/reflect/references/judgment-reviewer.md +1 -1
- package/{assets → dist/pstack}/skills/reflect/references/synthesizer.md +3 -3
- package/dist/{assets → pstack}/skills/reflect/references/tooling-reviewer.md +1 -1
- package/dist/pstack/skills/setup-pstack/SKILL.md +80 -0
- package/{assets → dist/pstack}/skills/sharpen/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/show-me-your-work/SKILL.md +3 -3
- package/dist/pstack/skills/show-me-your-work/scripts/log.mjs +106 -0
- package/dist/pstack/skills/show-me-your-work/scripts/log.test.mjs +56 -0
- package/dist/pstack/skills/statechart/SKILL.md +82 -0
- package/dist/pstack/skills/statechart/references/render.md +93 -0
- package/dist/pstack/skills/statechart/scripts/backends/cfg.mjs +31 -0
- package/dist/pstack/skills/statechart/scripts/backends/mermaid.mjs +186 -0
- package/dist/pstack/skills/statechart/scripts/backends/plain-text.mjs +62 -0
- package/dist/pstack/skills/statechart/scripts/backends/report.mjs +84 -0
- package/dist/pstack/skills/statechart/scripts/frontends/config.mjs +268 -0
- package/dist/pstack/skills/statechart/scripts/frontends/dot.mjs +67 -0
- package/dist/pstack/skills/statechart/scripts/frontends/marks.mjs +94 -0
- package/dist/pstack/skills/statechart/scripts/frontends/messages.mjs +48 -0
- package/dist/pstack/skills/statechart/scripts/frontends/spec.mjs +101 -0
- package/dist/pstack/skills/statechart/scripts/frontends/trace.mjs +39 -0
- package/dist/pstack/skills/statechart/scripts/frontends/value.mjs +180 -0
- package/dist/pstack/skills/statechart/scripts/frontends/verdict.mjs +40 -0
- package/dist/pstack/skills/statechart/scripts/frontends/view.mjs +99 -0
- package/dist/pstack/skills/statechart/scripts/ir.mjs +277 -0
- package/dist/pstack/skills/statechart/scripts/passes/annotate.mjs +92 -0
- package/dist/pstack/skills/statechart/scripts/passes/label.mjs +210 -0
- package/dist/pstack/skills/statechart/scripts/passes/lift.mjs +84 -0
- package/dist/pstack/skills/statechart/scripts/passes/loop-check.mjs +47 -0
- package/dist/pstack/skills/statechart/scripts/passes/paths.mjs +72 -0
- package/dist/pstack/skills/statechart/scripts/passes/reach.mjs +135 -0
- package/dist/pstack/skills/statechart/scripts/passes/region-check.mjs +82 -0
- package/dist/pstack/skills/statechart/scripts/passes/regions.mjs +268 -0
- package/dist/pstack/skills/statechart/scripts/pipeline.mjs +72 -0
- package/dist/pstack/skills/statechart/scripts/run.mjs +440 -0
- package/dist/pstack/skills/statechart/scripts/statechart.mjs +155 -0
- package/dist/pstack/skills/statechart/scripts/statechart.test.mjs +198 -0
- package/dist/pstack/skills/statechart/scripts/worker.mjs +31 -0
- package/dist/pstack/skills/swarm/SKILL.md +43 -0
- package/{assets → dist/pstack}/skills/tdd/SKILL.md +1 -3
- package/{assets → dist/pstack}/skills/teach/SKILL.md +3 -3
- package/{assets → dist/pstack}/skills/technical-writing/SKILL.md +2 -2
- package/{assets → dist/pstack}/skills/typescript-best-practices/SKILL.md +1 -2
- package/{assets → dist/pstack}/skills/unslop/SKILL.md +0 -2
- package/dist/{assets → pstack}/skills/why/SKILL.md +9 -9
- package/dist/script.d.mts +91 -0
- package/dist/script.mjs +485 -0
- package/dist/skill.d.mts +38 -0
- package/dist/skill.mjs +183 -0
- package/dist/skills/authored/decision-audit/SKILL.md +99 -0
- package/dist/skills/authored/decision-audit/SKILL.zh.md +99 -0
- package/dist/skills/authored/decision-audit/references/recover-commit.md +17 -0
- package/dist/skills/authored/decision-audit/references/recover-commit.zh.md +17 -0
- package/dist/skills/authored/decision-write/SKILL.md +131 -0
- package/dist/skills/authored/decision-write/SKILL.zh.md +131 -0
- package/dist/skills/authored/decision-write/references/land.md +48 -0
- package/dist/skills/authored/decision-write/references/land.zh.md +48 -0
- package/dist/skills/authored/setup-project-docs/SKILL.md +58 -0
- package/dist/skills/authored/setup-project-docs/SKILL.zh.md +58 -0
- package/dist/skills/authored/setup-project-docs/references/decision-conventions.md +11 -0
- package/dist/skills/authored/setup-project-docs/references/decision-conventions.zh.md +11 -0
- package/dist/skills/vendored/retro/SKILL.md +44 -0
- package/dist/skills/vendored/retro/agents/openai.yaml +5 -0
- package/dist/skills/vendored/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/dist/skills/vendored/writing-for-agents/SKILL.md +81 -0
- package/dist/skills/vendored/writing-for-agents/agents/openai.yaml +3 -0
- package/dist/subagent.d.mts +175 -106
- package/dist/subagent.mjs +1012 -344
- package/locale/en.json +7 -0
- package/locale/zh.json +7 -0
- package/package.json +61 -50
- package/assets/agents/poteto-agent.md +0 -8
- package/assets/docs/guide/02-poteto-mode.md +0 -139
- package/assets/docs/guide/05-build-and-clean.md +0 -83
- package/assets/docs/guide/06-verify-and-ship.md +0 -152
- package/assets/docs/guide/09-make-it-yours.md +0 -100
- package/assets/docs/harness/dsh.md +0 -65
- package/assets/docs/pstack-models.example.md +0 -23
- package/assets/skills/arena/SKILL.md +0 -50
- package/assets/skills/benchmark-checklist/SKILL.md +0 -41
- package/assets/skills/blast-radius/SKILL.md +0 -52
- package/assets/skills/correct/SKILL.md +0 -34
- package/assets/skills/how/SKILL.md +0 -38
- package/assets/skills/in-your-own-words/SKILL.md +0 -9
- package/assets/skills/interrogate/SKILL.md +0 -69
- package/assets/skills/interrogate/references/code-quality-review.md +0 -47
- package/assets/skills/interrogate/references/lead-judgment.md +0 -39
- package/assets/skills/interrogate/references/reviewer-prompt.md +0 -62
- package/assets/skills/interrogate/references/rubric.md +0 -77
- package/assets/skills/maintain-verification-skill/SKILL.md +0 -41
- package/assets/skills/poteto-help/references/prompting.md +0 -51
- package/assets/skills/poteto-help/references/recipes.md +0 -51
- package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +0 -12
- package/assets/skills/poteto-mode/playbooks/autonomous-run.md +0 -13
- package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +0 -12
- package/assets/skills/poteto-mode/playbooks/eval.md +0 -25
- package/assets/skills/poteto-mode/playbooks/opening-a-pr.md +0 -34
- package/assets/skills/poteto-mode/playbooks/orchestrate.md +0 -112
- package/assets/skills/poteto-mode/playbooks/perf-issue.md +0 -25
- package/assets/skills/poteto-mode/playbooks/refactoring.md +0 -16
- package/assets/skills/poteto-mode/playbooks/shipping.md +0 -12
- package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +0 -12
- package/assets/skills/poteto-mode/scripts/bootstrap.ts +0 -62
- package/assets/skills/poteto-mode/scripts/bun.lock +0 -67
- package/assets/skills/poteto-mode/scripts/check-plan.test.ts +0 -96
- package/assets/skills/poteto-mode/scripts/orch/orch.test.ts +0 -761
- package/assets/skills/poteto-mode/scripts/orch/orch.ts +0 -590
- package/assets/skills/poteto-mode/scripts/orch/store.ts +0 -1677
- package/assets/skills/poteto-mode/scripts/package.json +0 -16
- package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.ts +0 -224
- package/assets/skills/poteto-mode/scripts/watch-pr/cli.ts +0 -223
- package/assets/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.ts +0 -129
- package/assets/skills/poteto-mode/scripts/watch-pr/github.test.ts +0 -436
- package/assets/skills/poteto-mode/scripts/watch-pr/github.ts +0 -718
- package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.ts +0 -420
- package/assets/skills/poteto-mode/scripts/watch-pr/policy.ts +0 -836
- package/assets/skills/poteto-mode/scripts/watch-pr/render.ts +0 -169
- package/assets/skills/poteto-mode/scripts/watch-pr/tsconfig.json +0 -13
- package/assets/skills/poteto-mode/scripts/watch-pr/types.compile.ts +0 -93
- package/assets/skills/poteto-mode/scripts/watch-pr/types.ts +0 -413
- package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr +0 -6
- package/assets/skills/poteto-mode/scripts/worktree-audit.sh +0 -127
- package/assets/skills/principle-encode-lessons-in-structure/SKILL.md +0 -13
- package/assets/skills/principle-exhaust-the-design-space/SKILL.md +0 -11
- package/assets/skills/principle-explain-the-number/SKILL.md +0 -19
- package/assets/skills/principle-fix-root-causes/SKILL.md +0 -23
- package/assets/skills/principle-guard-the-context-window/SKILL.md +0 -14
- package/assets/skills/principle-laziness-protocol/SKILL.md +0 -12
- package/assets/skills/principle-make-operations-idempotent/SKILL.md +0 -24
- package/assets/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +0 -16
- package/assets/skills/principle-outcome-oriented-execution/SKILL.md +0 -23
- package/assets/skills/principle-redesign-from-first-principles/SKILL.md +0 -18
- package/assets/skills/principle-sequence-verifiable-units/SKILL.md +0 -19
- package/assets/skills/principle-subtract-before-you-add/SKILL.md +0 -20
- package/assets/skills/recall/SKILL.md +0 -37
- package/assets/skills/reflect/SKILL.md +0 -70
- package/assets/skills/reflect/references/tooling-reviewer.md +0 -48
- package/assets/skills/setup-pstack/SKILL.md +0 -76
- package/assets/skills/show-me-your-work/scripts/log.sh +0 -42
- package/assets/skills/swarm/SKILL.md +0 -41
- package/assets/skills/why/SKILL.md +0 -100
- package/dist/README.md +0 -159
- package/dist/assets/agents/comment-sicko.md +0 -32
- package/dist/assets/agents/poteto-agent.md +0 -8
- package/dist/assets/docs/guide/01-setup.md +0 -64
- package/dist/assets/docs/guide/03-understand.md +0 -79
- package/dist/assets/docs/guide/04-design.md +0 -133
- package/dist/assets/docs/guide/07-overnight.md +0 -122
- package/dist/assets/docs/guide/08-principles.md +0 -72
- package/dist/assets/docs/guide/10-recipes-and-pitfalls.md +0 -180
- package/dist/assets/docs/guide/README.md +0 -38
- package/dist/assets/docs/harness/dsh.md +0 -65
- package/dist/assets/docs/pstack-models.example.md +0 -23
- package/dist/assets/skills/architect/SKILL.md +0 -81
- package/dist/assets/skills/architect/references/design-red-flags.md +0 -57
- package/dist/assets/skills/architect/references/rationale-template.md +0 -35
- package/dist/assets/skills/architect/references/runner-prompt.md +0 -20
- package/dist/assets/skills/arena/SKILL.md +0 -50
- package/dist/assets/skills/automate-me/SKILL.md +0 -105
- package/dist/assets/skills/bro/SKILL.md +0 -9
- package/dist/assets/skills/create-formal-verification-skill/SKILL.md +0 -43
- package/dist/assets/skills/create-verification-skill/SKILL.md +0 -46
- package/dist/assets/skills/create-verification-skill/references/feature-map-example/README.md +0 -47
- package/dist/assets/skills/create-verification-skill/references/feature-map-example/create-note.md +0 -39
- package/dist/assets/skills/create-verification-skill/references/feature-map-example/search.md +0 -45
- package/dist/assets/skills/figure-it-out/SKILL.md +0 -54
- package/dist/assets/skills/how/references/explainer-prompt.md +0 -55
- package/dist/assets/skills/how/references/explorer-prompt.md +0 -35
- package/dist/assets/skills/in-your-own-words/SKILL.md +0 -9
- package/dist/assets/skills/interrogate/SKILL.md +0 -69
- package/dist/assets/skills/interrogate/references/code-quality-review.md +0 -47
- package/dist/assets/skills/interrogate/references/lead-judgment.md +0 -39
- package/dist/assets/skills/interrogate/references/reviewer-prompt.md +0 -62
- package/dist/assets/skills/interrogate/references/rubric.md +0 -77
- package/dist/assets/skills/ketchup/SKILL.md +0 -45
- package/dist/assets/skills/maintain-formal-verification-skill/SKILL.md +0 -60
- package/dist/assets/skills/no-comments/SKILL.md +0 -26
- package/dist/assets/skills/poteto-help/SKILL.md +0 -161
- package/dist/assets/skills/poteto-mode/SKILL.md +0 -153
- package/dist/assets/skills/poteto-mode/playbooks/autopilot-full.md +0 -25
- package/dist/assets/skills/poteto-mode/playbooks/autopilot-stack.md +0 -12
- package/dist/assets/skills/poteto-mode/playbooks/babysit.md +0 -23
- package/dist/assets/skills/poteto-mode/playbooks/bug-fix.md +0 -15
- package/dist/assets/skills/poteto-mode/playbooks/feature.md +0 -21
- package/dist/assets/skills/poteto-mode/playbooks/hillclimb.md +0 -21
- package/dist/assets/skills/poteto-mode/playbooks/investigation.md +0 -14
- package/dist/assets/skills/poteto-mode/playbooks/multi-phase-plan.md +0 -155
- package/dist/assets/skills/poteto-mode/playbooks/orchestrate.md +0 -112
- package/dist/assets/skills/poteto-mode/playbooks/pause-safely.md +0 -10
- package/dist/assets/skills/poteto-mode/playbooks/prototype.md +0 -14
- package/dist/assets/skills/poteto-mode/playbooks/runtime-forensics.md +0 -11
- package/dist/assets/skills/poteto-mode/playbooks/session-pickup.md +0 -11
- package/dist/assets/skills/poteto-mode/playbooks/trace-forensics.md +0 -14
- package/dist/assets/skills/poteto-mode/playbooks/visual-parity.md +0 -11
- package/dist/assets/skills/poteto-mode/references/bugbot-triage.md +0 -142
- package/dist/assets/skills/poteto-mode/scripts/bootstrap.ts +0 -62
- package/dist/assets/skills/poteto-mode/scripts/bun.lock +0 -67
- package/dist/assets/skills/poteto-mode/scripts/check-plan.mjs +0 -205
- package/dist/assets/skills/poteto-mode/scripts/orch/orch.test.ts +0 -761
- package/dist/assets/skills/poteto-mode/scripts/orch/orch.ts +0 -590
- package/dist/assets/skills/poteto-mode/scripts/orch/store.ts +0 -1677
- package/dist/assets/skills/poteto-mode/scripts/package.json +0 -16
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/cli.test.ts +0 -224
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/cli.ts +0 -223
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.ts +0 -129
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/github.test.ts +0 -436
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/github.ts +0 -718
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/policy.test.ts +0 -420
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/policy.ts +0 -836
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/render.ts +0 -169
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/tsconfig.json +0 -13
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/types.compile.ts +0 -93
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/types.ts +0 -413
- package/dist/assets/skills/poteto-mode/scripts/watch-pr/watch-pr +0 -6
- package/dist/assets/skills/poteto-mode/scripts/worktree-audit.sh +0 -127
- package/dist/assets/skills/principle-attack-the-premise/SKILL.md +0 -23
- package/dist/assets/skills/principle-boundary-discipline/SKILL.md +0 -36
- package/dist/assets/skills/principle-build-the-lever/SKILL.md +0 -26
- package/dist/assets/skills/principle-encode-lessons-in-structure/SKILL.md +0 -13
- package/dist/assets/skills/principle-exhaust-the-design-space/SKILL.md +0 -11
- package/dist/assets/skills/principle-experience-first/SKILL.md +0 -21
- package/dist/assets/skills/principle-foundational-thinking/SKILL.md +0 -23
- package/dist/assets/skills/principle-minimize-reader-load/SKILL.md +0 -20
- package/dist/assets/skills/principle-model-the-domain/SKILL.md +0 -26
- package/dist/assets/skills/principle-never-block-on-the-human/SKILL.md +0 -17
- package/dist/assets/skills/principle-prove-it-works/SKILL.md +0 -24
- package/dist/assets/skills/principle-separate-before-serializing-shared-state/SKILL.md +0 -18
- package/dist/assets/skills/principle-test-behavior-not-implementation/SKILL.md +0 -22
- package/dist/assets/skills/principle-type-system-discipline/SKILL.md +0 -15
- package/dist/assets/skills/reflect/SKILL.md +0 -70
- package/dist/assets/skills/reflect/references/divergent-reviewer.md +0 -38
- package/dist/assets/skills/reflect/references/judgment-reviewer.md +0 -37
- package/dist/assets/skills/reflect/references/synthesizer.md +0 -56
- package/dist/assets/skills/setup-pstack/SKILL.md +0 -76
- package/dist/assets/skills/sharpen/SKILL.md +0 -18
- package/dist/assets/skills/show-me-your-work/SKILL.md +0 -84
- package/dist/assets/skills/show-me-your-work/references/decision-log-template.tsv +0 -1
- package/dist/assets/skills/show-me-your-work/scripts/log.sh +0 -42
- package/dist/assets/skills/swarm/SKILL.md +0 -41
- package/dist/assets/skills/tdd/SKILL.md +0 -44
- package/dist/assets/skills/teach/SKILL.md +0 -23
- package/dist/assets/skills/technical-writing/SKILL.md +0 -108
- package/dist/assets/skills/typescript-best-practices/SKILL.md +0 -32
- package/dist/assets/skills/typescript-best-practices/references/patterns.md +0 -328
- package/dist/assets/skills/unslop/SKILL.md +0 -64
- package/dist/assets/skills/why/references/epistemics.md +0 -71
- package/dist/assets/skills/why/references/investigator-prompt.md +0 -84
- package/dist/assets/skills/why/references/source-playbook.md +0 -17
- package/dist/assets/skills/why/references/sources/code-archaeology.md +0 -51
- package/dist/assets/skills/why/references/sources/databricks.md +0 -70
- package/dist/assets/skills/why/references/sources/datadog.md +0 -97
- package/dist/assets/skills/why/references/sources/incident-postmortem.md +0 -5
- package/dist/assets/skills/why/references/sources/linear.md +0 -48
- package/dist/assets/skills/why/references/sources/notion.md +0 -55
- package/dist/assets/skills/why/references/sources/sentry.md +0 -66
- package/dist/assets/skills/why/references/sources/slack.md +0 -17
- package/dist/assets/skills/why/references/synthesizer-prompt.md +0 -129
- package/dist/cordis.patch.yml +0 -20
- package/dist/index.mjs.map +0 -1
- package/dist/paths-Bbai4CaT.mjs +0 -16
- package/dist/paths-Bbai4CaT.mjs.map +0 -1
- package/dist/subagent.mjs.map +0 -1
- /package/{assets → dist/pstack}/docs/guide/03-understand.md +0 -0
- /package/{assets → dist/pstack}/docs/guide/08-principles.md +0 -0
- /package/{assets → dist/pstack}/docs/guide/README.md +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/design-red-flags.md +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/rationale-template.md +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/runner-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/README.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/create-note.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/search.md +0 -0
- /package/{assets → dist/pstack}/skills/how/references/explorer-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/poteto-mode/playbooks/investigation.md +0 -0
- /package/{assets → dist/pstack}/skills/poteto-mode/playbooks/pause-safely.md +0 -0
- /package/{assets → dist/pstack}/skills/poteto-mode/playbooks/trace-forensics.md +0 -0
- /package/{assets → dist/pstack}/skills/poteto-mode/references/bugbot-triage.md +0 -0
- /package/{assets → dist/pstack}/skills/show-me-your-work/references/decision-log-template.tsv +0 -0
- /package/{assets → dist/pstack}/skills/typescript-best-practices/references/patterns.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/epistemics.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/investigator-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/source-playbook.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/code-archaeology.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/databricks.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/datadog.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/incident-postmortem.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/linear.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/notion.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/sentry.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/slack.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/synthesizer-prompt.md +0 -0
package/dist/subagent.mjs
CHANGED
|
@@ -1,436 +1,1104 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
1
|
+
import { a as POTETO_MODE_SKILL_PATH } from "./paths-BT77khwm.mjs";
|
|
2
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
4
|
import { homedir } from "node:os";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
"inherit-parent",
|
|
10
|
-
"auto",
|
|
11
|
-
"parent"
|
|
12
|
-
]);
|
|
5
|
+
// src/internal/effort.ts
|
|
6
|
+
/** Model-facing name of the child's own control. */
|
|
7
|
+
const SET_EFFORT_TOOL = "set_reasoning_effort";
|
|
13
8
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* configured and is not.
|
|
9
|
+
* Resolve the route a child's effort will actually apply to: an explicit route wins,
|
|
10
|
+
* then the session's live request header, then the creation-time options.
|
|
17
11
|
*/
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
function resolveEffortTarget(agent, route) {
|
|
13
|
+
const explicit = route?.kind === "route" ? route : void 0;
|
|
14
|
+
const header = agent?.session.requestHeader()?.config;
|
|
15
|
+
const provider = explicit?.provider ?? header?.provider ?? agent?.options.provider;
|
|
16
|
+
const model = explicit?.model ?? header?.model ?? agent?.options.model;
|
|
17
|
+
return provider !== void 0 && model !== void 0 ? {
|
|
18
|
+
provider,
|
|
19
|
+
model
|
|
20
|
+
} : void 0;
|
|
21
|
+
}
|
|
22
|
+
async function advertisedEfforts(llm, target, signal) {
|
|
23
|
+
return (await llm.resolveModelInfo(target.provider, target.model, signal)).reasoning?.efforts.map((entry) => entry.id) ?? [];
|
|
24
|
+
}
|
|
25
|
+
/** Validate one effort against the route a child will actually request. */
|
|
26
|
+
async function validateEffortFor(llm, route, effort, parent, signal) {
|
|
27
|
+
const target = resolveEffortTarget(parent, route);
|
|
28
|
+
if (target === void 0) throw new Error("cannot resolve the child provider/model route for reasoningEffort. Pass `role` with an exact route, or run the delegation from a session that has already made a request.");
|
|
29
|
+
let efforts;
|
|
30
|
+
try {
|
|
31
|
+
efforts = await advertisedEfforts(llm, target, signal);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`route ${target.provider}/${target.model} could not be resolved for reasoningEffort: ${describeError$2(error)}`, { cause: error });
|
|
34
|
+
}
|
|
35
|
+
if (efforts.length === 0) throw new Error(`${target.provider}/${target.model} advertises no reasoning efforts, so it cannot take reasoningEffort "${effort}". Omit the argument and let the adapter use its own default.`);
|
|
36
|
+
if (!efforts.includes(effort)) throw new Error(`${target.provider}/${target.model} does not support reasoning effort "${effort}". It supports: ${efforts.join(", ")}.`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Install the effort control on one live child.
|
|
40
|
+
*
|
|
41
|
+
* Registration is child-scoped, so the tool disposes with the child. A successful
|
|
42
|
+
* call records the choice through `agent.model.select()`, which DSH applies when
|
|
43
|
+
* it prepares the next step. `agent/request` is deliberately not used: DSH
|
|
44
|
+
* deprecated it for model and effort changes and rejects a request that disagrees
|
|
45
|
+
* with the selection fixed during step preparation.
|
|
46
|
+
*/
|
|
47
|
+
function installEffortControl(child, llm) {
|
|
48
|
+
return child.ctx.tools.register(defineSetEffortTool(child, llm));
|
|
49
|
+
}
|
|
50
|
+
function defineSetEffortTool(child, llm) {
|
|
51
|
+
return {
|
|
52
|
+
name: SET_EFFORT_TOOL,
|
|
53
|
+
description: [
|
|
54
|
+
"Change your reasoning effort for future model requests.",
|
|
55
|
+
"Use it when the task turns out harder or more routine than the role line assumed: raise it for subtle design, concurrency, or algorithm work, lower it once the remaining work is mechanical.",
|
|
56
|
+
"The change applies from your next step; a request already in flight is unaffected, and it lasts for the rest of this child's life."
|
|
57
|
+
].join(" "),
|
|
58
|
+
parameters: {
|
|
59
|
+
type: "object",
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
properties: { effort: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "The reasoning effort to use from your next step onward. Use one of the values this model advertises; the call rejects an unknown one and lists the real values."
|
|
64
|
+
} },
|
|
65
|
+
required: ["effort"]
|
|
66
|
+
},
|
|
67
|
+
output: {
|
|
68
|
+
schema: { type: "string" },
|
|
69
|
+
render: (_args, value) => [{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: typeof value === "string" ? value : JSON.stringify(value)
|
|
72
|
+
}]
|
|
73
|
+
},
|
|
74
|
+
isConcurrencySafe: () => false,
|
|
75
|
+
async execute(rawArgs, exec) {
|
|
76
|
+
const args = rawArgs;
|
|
77
|
+
const value = typeof args.effort === "string" ? args.effort.trim() : "";
|
|
78
|
+
if (value === "") throw new Error("set_reasoning_effort needs a non-empty `effort`. Pass the exact token this model advertises; an empty value would silently re-enable the adapter default.");
|
|
79
|
+
const agent = exec.agent ?? child;
|
|
80
|
+
const target = resolveEffortTarget(agent);
|
|
81
|
+
if (target === void 0) throw new Error("this child has no resolved provider/model route, so reasoning effort cannot be validated against the live adapter metadata");
|
|
82
|
+
let efforts;
|
|
83
|
+
try {
|
|
84
|
+
efforts = await advertisedEfforts(llm, target, exec.signal);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw new Error(`${target.provider}/${target.model} could not be resolved for reasoning effort: ${describeError$2(error)}`, { cause: error });
|
|
87
|
+
}
|
|
88
|
+
if (efforts.length === 0) throw new Error(`${target.provider}/${target.model} advertises no reasoning efforts, so this child has none to change. Your current effort stays as it is.`);
|
|
89
|
+
if (!efforts.includes(value)) throw new Error(`${target.provider}/${target.model} does not support reasoning effort "${value}", so nothing changed. It supports: ${efforts.join(", ")}.`);
|
|
90
|
+
agent.model.select({
|
|
91
|
+
provider: target.provider,
|
|
92
|
+
model: target.model,
|
|
93
|
+
reasoningEffort: value
|
|
94
|
+
});
|
|
95
|
+
return `reasoning effort set to "${value}". It applies from your next step; the request already in flight is unaffected.`;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** The `agentOptions` one (route, effort override) pair contributes. */
|
|
100
|
+
function agentOptionsFor(route, effortOverride) {
|
|
101
|
+
const effort = effortOverride ?? (route.kind === "route" ? route.effort : void 0);
|
|
102
|
+
if (route.kind === "inherit") return effort === void 0 ? void 0 : { reasoningEffort: effort };
|
|
103
|
+
return {
|
|
104
|
+
provider: route.provider,
|
|
105
|
+
model: route.model,
|
|
106
|
+
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** The effort a plan entry will actually run at, override included. */
|
|
110
|
+
function effectiveEffort(route, effortOverride) {
|
|
111
|
+
return effortOverride ?? (route.kind === "route" ? route.effort : void 0);
|
|
112
|
+
}
|
|
113
|
+
function describeError$2(error) {
|
|
114
|
+
if (error instanceof Error) {
|
|
115
|
+
const code = error.code;
|
|
116
|
+
return typeof code === "string" ? `${error.message} (${code})` : error.message;
|
|
117
|
+
}
|
|
118
|
+
return String(error);
|
|
119
|
+
}
|
|
120
|
+
// src/internal/role-manifest.ts
|
|
121
|
+
/**
|
|
122
|
+
* File name the role configuration lives under inside `$DSH_HOME`.
|
|
123
|
+
*
|
|
124
|
+
* It keeps the upstream name `pstack-models.mdc`, so a reader moving from the Cursor docs
|
|
125
|
+
* recognizes it, but stores it directly under `$DSH_HOME`; DSH does not load `.mdc` files
|
|
126
|
+
* on its own, and `pstack_subagent` is the only reader.
|
|
127
|
+
*/
|
|
128
|
+
const PSTACK_MODELS_FILENAME = "pstack-models.mdc";
|
|
129
|
+
/** Values that resolve to "send no `agentOptions` and inherit the parent route". */
|
|
130
|
+
const INHERIT_PARENT_ALIASES = ["inherit-parent", "auto"];
|
|
131
|
+
/** The 17 roles, in manifest order. */
|
|
132
|
+
const ROLE_MANIFEST = [
|
|
133
|
+
{
|
|
134
|
+
key: "feature, refactoring",
|
|
135
|
+
shape: "single",
|
|
136
|
+
autoPanel: false
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
key: "bug-fix",
|
|
140
|
+
shape: "single",
|
|
141
|
+
autoPanel: false
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
key: "perf-issue",
|
|
145
|
+
shape: "single",
|
|
146
|
+
autoPanel: false
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
key: "hillclimb",
|
|
150
|
+
shape: "single",
|
|
151
|
+
autoPanel: false
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
key: "judgment and prose",
|
|
155
|
+
shape: "single",
|
|
156
|
+
autoPanel: false
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: "hardest tasks",
|
|
160
|
+
shape: "single",
|
|
161
|
+
autoPanel: false
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: "how explorer",
|
|
165
|
+
shape: "single",
|
|
166
|
+
autoPanel: false
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
key: "how explainer",
|
|
170
|
+
shape: "single",
|
|
171
|
+
autoPanel: false
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
key: "why investigators",
|
|
175
|
+
shape: "single",
|
|
176
|
+
autoPanel: false
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: "why synthesizer",
|
|
180
|
+
shape: "single",
|
|
181
|
+
autoPanel: false
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
key: "reflect tooling",
|
|
185
|
+
shape: "single",
|
|
186
|
+
autoPanel: false
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: "reflect judgment, divergent, synthesizer",
|
|
190
|
+
shape: "single",
|
|
191
|
+
autoPanel: false
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: "arena runners",
|
|
195
|
+
shape: "list",
|
|
196
|
+
autoPanel: true
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
key: "arena cross-judge pool",
|
|
200
|
+
shape: "list",
|
|
201
|
+
autoPanel: false
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: "swarm workers",
|
|
205
|
+
shape: "list",
|
|
206
|
+
autoPanel: false
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
key: "architect runners",
|
|
210
|
+
shape: "list",
|
|
211
|
+
autoPanel: true
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
key: "interrogate reviewers",
|
|
215
|
+
shape: "list",
|
|
216
|
+
autoPanel: true
|
|
217
|
+
}
|
|
25
218
|
];
|
|
26
|
-
|
|
219
|
+
/** Exact manifest lookup. Callers normalize the key first. */
|
|
220
|
+
function findRoleSpec(key) {
|
|
221
|
+
return ROLE_MANIFEST.find((role) => role.key === key);
|
|
222
|
+
}
|
|
223
|
+
// src/internal/role-config.ts
|
|
27
224
|
/**
|
|
28
|
-
*
|
|
225
|
+
* The pstack role configuration: `$DSH_HOME/pstack-models.mdc`.
|
|
226
|
+
*
|
|
227
|
+
* `pstack_subagent` reads this file again on every delegation, so a change reaches the
|
|
228
|
+
* next child and never a child that already started.
|
|
29
229
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* stored too, so a caller can name just one of them. Both spellings therefore
|
|
34
|
-
* work, and neither can shadow the other: the full key is a different string.
|
|
230
|
+
* Three states stay apart because each needs a different fix: the file is missing (no
|
|
231
|
+
* policy, so every role inherits the parent route), unreadable (a broken install, never
|
|
232
|
+
* treated as "no configuration"), or loaded with per-line issues reported by line.
|
|
35
233
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
234
|
+
/** Absolute path of the role file for one DSH home. */
|
|
235
|
+
function modelsFilePath(dshHome) {
|
|
236
|
+
return join(dshHome, PSTACK_MODELS_FILENAME);
|
|
237
|
+
}
|
|
238
|
+
/** Resolve `$DSH_HOME` the way the Host does: the environment when set, else `~/.dsh`. */
|
|
239
|
+
function resolveDshHome(env = process.env, home = homedir()) {
|
|
240
|
+
const configured = env["DSH_HOME"];
|
|
241
|
+
const path = configured !== void 0 && configured.trim() !== "" ? configured : join(home, ".dsh");
|
|
242
|
+
const expanded = path === "~" ? home : path.startsWith("~/") || path.startsWith("~\\") ? join(home, path.slice(2)) : path;
|
|
243
|
+
return resolve(expanded);
|
|
244
|
+
}
|
|
245
|
+
/** Read the role file. Never throws for a missing or unreadable file. */
|
|
246
|
+
function readRoleConfiguration(path) {
|
|
247
|
+
let text;
|
|
248
|
+
try {
|
|
249
|
+
text = readFileSync(path, "utf8");
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (isMissingFile(error)) return {
|
|
252
|
+
status: "missing",
|
|
253
|
+
path
|
|
254
|
+
};
|
|
255
|
+
return {
|
|
256
|
+
status: "unreadable",
|
|
257
|
+
path,
|
|
258
|
+
reason: describeError$1(error)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return parseRoleConfiguration(text, path);
|
|
262
|
+
}
|
|
263
|
+
/** Parse role-file text. Pure: no file system access. */
|
|
264
|
+
function parseRoleConfiguration(text, path) {
|
|
44
265
|
const issues = [];
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
266
|
+
const roles = /* @__PURE__ */ new Map();
|
|
267
|
+
let budget;
|
|
268
|
+
stripFrontmatter(text).split("\n").forEach((rawLine, index) => {
|
|
269
|
+
const line = index + 1;
|
|
270
|
+
const trimmed = rawLine.trim();
|
|
271
|
+
if (trimmed === "") return;
|
|
272
|
+
if (trimmed.startsWith("#")) {
|
|
273
|
+
const match = /^#\s*budget:\s*(.+)$/i.exec(trimmed);
|
|
274
|
+
if (match !== null) budget = match[1].trim();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const separator = trimmed.indexOf(":");
|
|
51
278
|
if (separator === -1) {
|
|
52
279
|
issues.push({
|
|
53
|
-
line
|
|
54
|
-
message: `expected
|
|
280
|
+
line,
|
|
281
|
+
message: `expected \`<role>: <value>\`, found ${JSON.stringify(trimmed)}`
|
|
55
282
|
});
|
|
56
|
-
|
|
283
|
+
return;
|
|
57
284
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
285
|
+
const rawKey = trimmed.slice(0, separator).trim();
|
|
286
|
+
const rawValue = trimmed.slice(separator + 1).trim();
|
|
287
|
+
const key = normalizeRoleName(rawKey);
|
|
288
|
+
if (key === "") {
|
|
60
289
|
issues.push({
|
|
61
|
-
line
|
|
62
|
-
message: "
|
|
290
|
+
line,
|
|
291
|
+
message: "the role key is empty"
|
|
63
292
|
});
|
|
64
|
-
|
|
293
|
+
return;
|
|
65
294
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const parsed = parseValue(entry.trim(), lineNumber, issues);
|
|
69
|
-
if (parsed !== void 0) values.push(parsed);
|
|
70
|
-
}
|
|
71
|
-
if (values.length === 0) {
|
|
295
|
+
const spec = findRoleSpec(key);
|
|
296
|
+
if (spec === void 0) {
|
|
72
297
|
issues.push({
|
|
73
|
-
line
|
|
74
|
-
message:
|
|
298
|
+
line,
|
|
299
|
+
message: unknownRoleMessage(rawKey, key)
|
|
75
300
|
});
|
|
76
|
-
|
|
301
|
+
return;
|
|
77
302
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
issues
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function parseValue(value, line = 0, issues = []) {
|
|
86
|
-
if (!value) return void 0;
|
|
87
|
-
if (INHERIT.has(value.toLowerCase())) return "inherit-parent";
|
|
88
|
-
const [route, ...rest] = value.split(/\s+/).filter(Boolean);
|
|
89
|
-
if (!route) return void 0;
|
|
90
|
-
const slash = route.indexOf("/");
|
|
91
|
-
if (slash <= 0 || slash === route.length - 1) {
|
|
92
|
-
issues.push({
|
|
303
|
+
const parsed = parseRouteValue(rawValue);
|
|
304
|
+
const entryIssues = [...parsed.issues];
|
|
305
|
+
if (spec.shape === "single" && parsed.routes.length > 1) entryIssues.push("a single-route role cannot hold a comma-separated list");
|
|
306
|
+
for (const issue of entryIssues) issues.push({
|
|
93
307
|
line,
|
|
94
|
-
message:
|
|
308
|
+
message: `${rawKey}: ${issue}`
|
|
95
309
|
});
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (const token of rest) {
|
|
100
|
-
if (EFFORTS.has(token.toLowerCase())) {
|
|
101
|
-
reasoningEffort = token.toLowerCase();
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
issues.push({
|
|
310
|
+
if (parsed.routes.length === 0) return;
|
|
311
|
+
const previous = roles.get(spec.key);
|
|
312
|
+
if (previous !== void 0) issues.push({
|
|
105
313
|
line,
|
|
106
|
-
message: `
|
|
314
|
+
message: `role "${spec.key}" is already configured on line ${previous.line}; line ${line} wins`
|
|
107
315
|
});
|
|
108
|
-
|
|
316
|
+
roles.set(spec.key, {
|
|
317
|
+
line,
|
|
318
|
+
rawKey,
|
|
319
|
+
key: spec.key,
|
|
320
|
+
spec,
|
|
321
|
+
routes: parsed.routes,
|
|
322
|
+
issues: entryIssues
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
return budget === void 0 ? {
|
|
326
|
+
status: "loaded",
|
|
327
|
+
path,
|
|
328
|
+
roles,
|
|
329
|
+
issues
|
|
330
|
+
} : {
|
|
331
|
+
status: "loaded",
|
|
332
|
+
path,
|
|
333
|
+
budget,
|
|
334
|
+
roles,
|
|
335
|
+
issues
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/** Parse a role value: one route, or a comma-separated list of routes. */
|
|
339
|
+
function parseRouteValue(raw) {
|
|
340
|
+
const routes = [];
|
|
341
|
+
const issues = [];
|
|
342
|
+
const segments = raw.split(",");
|
|
343
|
+
segments.forEach((segment, index) => {
|
|
344
|
+
const trimmed = segment.trim();
|
|
345
|
+
if (trimmed === "") {
|
|
346
|
+
if (index !== segments.length - 1) issues.push(`entry ${index + 1} of the list is empty`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const parsed = parseRoute(trimmed);
|
|
350
|
+
if (parsed.route === void 0) {
|
|
351
|
+
issues.push(parsed.issue);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
routes.push(parsed.route);
|
|
355
|
+
});
|
|
356
|
+
if (routes.length === 0 && issues.length === 0) issues.push("the value is empty; use `inherit-parent`, `auto`, or `<provider>/<model> [effort]`");
|
|
109
357
|
return {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
...reasoningEffort ? { reasoningEffort } : {}
|
|
358
|
+
routes,
|
|
359
|
+
issues
|
|
113
360
|
};
|
|
114
361
|
}
|
|
362
|
+
/** Parse one entry of a role value. */
|
|
363
|
+
function parseRoute(raw) {
|
|
364
|
+
const value = raw.trim();
|
|
365
|
+
if (value === "") return { issue: "the value is empty" };
|
|
366
|
+
if (INHERIT_PARENT_ALIASES.includes(value.toLowerCase())) return { route: { kind: "inherit" } };
|
|
367
|
+
const match = /^([^\s/,[]+)\/([^\s/,[\]]+(?:\/[^\s/,[\]]+)*)(?:\s+\[([^[\]]*)\])?$/.exec(value);
|
|
368
|
+
if (match === null) return { issue: `${JSON.stringify(value)} is not a route; use \`inherit-parent\`, \`auto\`, or \`<provider>/<model> [effort]\`` };
|
|
369
|
+
const provider = match[1];
|
|
370
|
+
const model = match[2];
|
|
371
|
+
const effort = match[3];
|
|
372
|
+
if (effort === void 0) return { route: {
|
|
373
|
+
kind: "route",
|
|
374
|
+
provider,
|
|
375
|
+
model
|
|
376
|
+
} };
|
|
377
|
+
const trimmedEffort = effort.trim();
|
|
378
|
+
if (trimmedEffort === "") return { issue: `${JSON.stringify(value)} has empty brackets; write \`<provider>/<model> [effort]\` or drop the brackets` };
|
|
379
|
+
if (/\s/.test(trimmedEffort)) return { issue: `${JSON.stringify(value)} has a multi-word effort; an effort is one adapter-owned token` };
|
|
380
|
+
return { route: {
|
|
381
|
+
kind: "route",
|
|
382
|
+
provider,
|
|
383
|
+
model,
|
|
384
|
+
effort: trimmedEffort
|
|
385
|
+
} };
|
|
386
|
+
}
|
|
387
|
+
/** Raised when a role's configured list cannot be selected from. */
|
|
388
|
+
var RouteSelectionError = class extends Error {
|
|
389
|
+
constructor(message) {
|
|
390
|
+
super(message);
|
|
391
|
+
this.name = "RouteSelectionError";
|
|
392
|
+
}
|
|
393
|
+
};
|
|
115
394
|
/**
|
|
116
|
-
*
|
|
395
|
+
* Expand one role into the routes this call starts.
|
|
117
396
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* `how explorer` must not resolve, because picking a route the user did not name
|
|
122
|
-
* changes both cost and model without saying so. `findNearMisses` reports those
|
|
123
|
-
* for a human instead.
|
|
397
|
+
* Automatic panels start one child per entry. Another list role selects one entry, the
|
|
398
|
+
* first by default; `roleIndex` chooses which. A single role holds exactly one route and
|
|
399
|
+
* rejects `roleIndex`.
|
|
124
400
|
*/
|
|
125
|
-
function
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
401
|
+
function selectRoleRoutes(spec, entry, roleIndex) {
|
|
402
|
+
if (spec.shape === "single" && roleIndex !== void 0) throw new RouteSelectionError(`role "${spec.key}" holds a single route, so \`roleIndex\` does not apply; drop \`roleIndex\` or use a list-valued role (${listRoleKeys().join(", ")})`);
|
|
403
|
+
if (spec.autoPanel && roleIndex !== void 0) throw new RouteSelectionError(`role "${spec.key}" is an automatic panel: it starts one child per entry, so \`roleIndex\` does not apply`);
|
|
404
|
+
if (entry === void 0) return {
|
|
405
|
+
role: spec,
|
|
406
|
+
mode: spec.autoPanel ? "panel" : "single",
|
|
407
|
+
configured: false,
|
|
408
|
+
routes: [{ kind: "inherit" }],
|
|
409
|
+
total: 1
|
|
410
|
+
};
|
|
411
|
+
if (entry.issues.length > 0) throw new RouteSelectionError(`role "${spec.key}" has invalid configuration on line ${entry.line}: ${entry.issues.join("; ")}. Fix the role file before delegating.`);
|
|
412
|
+
if (spec.autoPanel) return {
|
|
413
|
+
role: spec,
|
|
414
|
+
mode: "panel",
|
|
415
|
+
configured: true,
|
|
416
|
+
routes: entry.routes,
|
|
417
|
+
total: entry.routes.length
|
|
418
|
+
};
|
|
419
|
+
if (spec.shape === "list") {
|
|
420
|
+
const index = roleIndex ?? 0;
|
|
421
|
+
if (index >= entry.routes.length) throw new RouteSelectionError(`role "${spec.key}" has ${entry.routes.length} entr${entry.routes.length === 1 ? "y" : "ies"}, so \`roleIndex\` ${index} is out of range; valid indexes are ${entry.routes.map((_route, position) => position).join(", ")}`);
|
|
422
|
+
return {
|
|
423
|
+
role: spec,
|
|
424
|
+
mode: "selected",
|
|
425
|
+
configured: true,
|
|
426
|
+
routes: [entry.routes[index]],
|
|
427
|
+
total: entry.routes.length,
|
|
428
|
+
roleIndex: index
|
|
429
|
+
};
|
|
430
|
+
}
|
|
131
431
|
return {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
432
|
+
role: spec,
|
|
433
|
+
mode: "single",
|
|
434
|
+
configured: true,
|
|
435
|
+
routes: [entry.routes[0]],
|
|
436
|
+
total: entry.routes.length
|
|
135
437
|
};
|
|
136
438
|
}
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
* on the explorer route would look configured while being wrong.
|
|
141
|
-
*/
|
|
142
|
-
function findNearMisses(table, role) {
|
|
143
|
-
const key = role.trim().toLowerCase().replace(/\s+/g, " ");
|
|
144
|
-
if (!key) return [];
|
|
145
|
-
const hits = [];
|
|
146
|
-
for (const candidate of table.keys()) {
|
|
147
|
-
if (candidate === key) continue;
|
|
148
|
-
if (candidate.includes(key) || key.includes(candidate)) {
|
|
149
|
-
hits.push(candidate);
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
for (const part of candidate.split(",").map((item) => item.trim())) {
|
|
153
|
-
if (part === key) continue;
|
|
154
|
-
if (part.includes(key) || key.includes(part)) {
|
|
155
|
-
hits.push(candidate);
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return [...new Set(hits)];
|
|
439
|
+
/** Keys of the list-valued roles, including the automatic panels. */
|
|
440
|
+
function listRoleKeys() {
|
|
441
|
+
return ROLE_MANIFEST.filter((role) => role.shape === "list").map((role) => role.key);
|
|
161
442
|
}
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
*/
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
if (!values || values.length === 0) return void 0;
|
|
173
|
-
const value = values[Math.abs(index) % values.length];
|
|
174
|
-
return value && value !== "inherit-parent" ? value : void 0;
|
|
175
|
-
}
|
|
176
|
-
//#endregion
|
|
177
|
-
//#region src/models.ts
|
|
178
|
-
/**
|
|
179
|
-
* Read `~/.dsh/pstack-models.md`, the role file `/setup-pstack` writes.
|
|
180
|
-
*/
|
|
181
|
-
function dshHome() {
|
|
182
|
-
return process.env.DSH_HOME || join(homedir(), ".dsh");
|
|
443
|
+
/** Raised when a role name does not match the manifest. */
|
|
444
|
+
var RoleNameError = class extends Error {
|
|
445
|
+
constructor(message) {
|
|
446
|
+
super(message);
|
|
447
|
+
this.name = "RoleNameError";
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
/** Normalize a role name for lookup. Case, spacing, spacing around a comma, and a trailing colon are formatting. */
|
|
451
|
+
function normalizeRoleName(raw) {
|
|
452
|
+
return raw.normalize("NFC").trim().toLowerCase().replace(/\s+/g, " ").replace(/\s*,\s*/g, ", ").replace(/:+$/, "").trim();
|
|
183
453
|
}
|
|
184
|
-
|
|
185
|
-
|
|
454
|
+
/** Resolve a role name, or throw a diagnostic that names the near misses. */
|
|
455
|
+
function requireRole(raw) {
|
|
456
|
+
const spec = findRoleSpec(normalizeRoleName(raw));
|
|
457
|
+
if (spec !== void 0) return spec;
|
|
458
|
+
const suggestions = suggestRoles(raw);
|
|
459
|
+
const hint = suggestions.length === 0 ? "No role key is close to it." : `Closest role keys: ${suggestions.map((key) => `"${key}"`).join(", ")}.`;
|
|
460
|
+
throw new RoleNameError(`unknown pstack role ${JSON.stringify(raw)}. ${hint} Role names come from the role manifest, not from free text: pass the exact key, or omit \`role\` to inherit the parent route.`);
|
|
186
461
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
async function loadRoleTable() {
|
|
196
|
-
try {
|
|
197
|
-
const { table, issues } = parseModelConfigWithIssues(await readFile(modelConfigPath(), "utf8"));
|
|
462
|
+
function suggestRoles(raw, limit = 3) {
|
|
463
|
+
const target = normalizeRoleName(raw);
|
|
464
|
+
if (target === "") return [];
|
|
465
|
+
return ROLE_MANIFEST.map((role) => {
|
|
466
|
+
const shared = sharedTokenCount(target, role.key);
|
|
467
|
+
const distance = editDistance(target, role.key);
|
|
468
|
+
const contains = role.key.includes(target) || target.includes(role.key);
|
|
469
|
+
const score = (contains ? 1e3 : 0) + shared * 100 - distance + Math.min(role.key.length, target.length);
|
|
198
470
|
return {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (error.code === "ENOENT") return {
|
|
205
|
-
table: /* @__PURE__ */ new Map(),
|
|
206
|
-
issues: [],
|
|
207
|
-
unreadable: false
|
|
208
|
-
};
|
|
209
|
-
return {
|
|
210
|
-
table: /* @__PURE__ */ new Map(),
|
|
211
|
-
issues: [{
|
|
212
|
-
line: 0,
|
|
213
|
-
message: `could not read ${modelConfigPath()}: ${error.message}`
|
|
214
|
-
}],
|
|
215
|
-
unreadable: true
|
|
471
|
+
key: role.key,
|
|
472
|
+
score,
|
|
473
|
+
distance,
|
|
474
|
+
shared,
|
|
475
|
+
contains
|
|
216
476
|
};
|
|
477
|
+
}).filter((candidate) => candidate.contains || candidate.shared > 0 || candidate.distance <= 4).sort((left, right) => right.score - left.score).slice(0, limit).map((candidate) => candidate.key);
|
|
478
|
+
}
|
|
479
|
+
function unknownRoleMessage(rawKey, key) {
|
|
480
|
+
const suggestions = suggestRoles(key);
|
|
481
|
+
const hint = suggestions.length === 0 ? "no role key is close to it" : `closest role keys: ${suggestions.map((candidate) => `"${candidate}"`).join(", ")}`;
|
|
482
|
+
return `unknown role ${JSON.stringify(rawKey)} (${hint}). A line for a retired or misspelled role is reported rather than ignored.`;
|
|
483
|
+
}
|
|
484
|
+
function sharedTokenCount(left, right) {
|
|
485
|
+
const rightTokens = new Set(tokenize(right));
|
|
486
|
+
return [...new Set(tokenize(left))].filter((token) => rightTokens.has(token)).length;
|
|
487
|
+
}
|
|
488
|
+
function tokenize(value) {
|
|
489
|
+
return value.split(/[^a-z0-9]+/).filter((token) => token !== "");
|
|
490
|
+
}
|
|
491
|
+
/** Levenshtein distance, iterative with one row of state. */
|
|
492
|
+
function editDistance(left, right) {
|
|
493
|
+
if (left === right) return 0;
|
|
494
|
+
if (left === "") return right.length;
|
|
495
|
+
if (right === "") return left.length;
|
|
496
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
497
|
+
for (let row = 1; row <= left.length; row += 1) {
|
|
498
|
+
const current = [row];
|
|
499
|
+
for (let column = 1; column <= right.length; column += 1) {
|
|
500
|
+
const substitution = previous[column - 1] + (left[row - 1] === right[column - 1] ? 0 : 1);
|
|
501
|
+
const deletion = previous[column] + 1;
|
|
502
|
+
const insertion = current[column - 1] + 1;
|
|
503
|
+
current.push(Math.min(substitution, deletion, insertion));
|
|
504
|
+
}
|
|
505
|
+
previous = current;
|
|
506
|
+
}
|
|
507
|
+
return previous[right.length];
|
|
508
|
+
}
|
|
509
|
+
function stripFrontmatter(text) {
|
|
510
|
+
const normalized = text.replaceAll("\r\n", "\n");
|
|
511
|
+
if (!normalized.startsWith("---\n")) return normalized;
|
|
512
|
+
const end = normalized.indexOf("\n---", 3);
|
|
513
|
+
if (end === -1) return normalized;
|
|
514
|
+
const after = normalized.indexOf("\n", end + 1);
|
|
515
|
+
return after === -1 ? "" : normalized.slice(after + 1);
|
|
516
|
+
}
|
|
517
|
+
function isMissingFile(error) {
|
|
518
|
+
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
519
|
+
}
|
|
520
|
+
function describeError$1(error) {
|
|
521
|
+
if (error instanceof Error) {
|
|
522
|
+
const code = error.code;
|
|
523
|
+
return typeof code === "string" ? `${code}: ${error.message}` : error.message;
|
|
217
524
|
}
|
|
525
|
+
return String(error);
|
|
218
526
|
}
|
|
219
|
-
|
|
220
|
-
//#region src/subagent.ts
|
|
527
|
+
// src/subagent.ts
|
|
221
528
|
/**
|
|
222
|
-
* `pstack_subagent`: pstack's delegation
|
|
529
|
+
* `pstack_subagent`: pstack's delegation entry point.
|
|
223
530
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* routes (model choice). This tool joins them back into one call whose shape a
|
|
228
|
-
* ported pstack skill can use unchanged.
|
|
531
|
+
* One call plans one or more fresh children, validates every route and effort before the
|
|
532
|
+
* first child starts, then starts them through `ctx.subagents.startActivation` with
|
|
533
|
+
* parent delivery. Results arrive as completion notices; this module never polls.
|
|
229
534
|
*/
|
|
535
|
+
/** Cordis row name, used by Loader diagnostics. */
|
|
230
536
|
const name = "pstack-subagent";
|
|
231
|
-
|
|
537
|
+
/**
|
|
538
|
+
* Delegation needs the tool registry, the subagent registry, the LLM adapters, and the
|
|
539
|
+
* session directory. `agents` is not here: it is optional and read per call, so a
|
|
540
|
+
* profile without it still delegates and the child simply keeps its starting effort.
|
|
541
|
+
*/
|
|
542
|
+
const inject = [
|
|
543
|
+
"tools",
|
|
544
|
+
"subagents",
|
|
545
|
+
"llm",
|
|
546
|
+
"workingDirectory"
|
|
547
|
+
];
|
|
548
|
+
/** Model-facing tool name. */
|
|
549
|
+
const TOOL_NAME = "pstack_subagent";
|
|
550
|
+
/** Backend used when the row does not name one. */
|
|
551
|
+
const DEFAULT_SUBAGENT_PROVIDER = "spawn";
|
|
552
|
+
/** The child kinds this tool knows how to brief. */
|
|
232
553
|
const AGENT_KINDS = [
|
|
233
554
|
"poteto-agent",
|
|
234
555
|
"comment-sicko",
|
|
235
556
|
"general"
|
|
236
557
|
];
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
558
|
+
const DEFAULT_CONFIG = { provider: DEFAULT_SUBAGENT_PROVIDER };
|
|
559
|
+
/** Raised for a configuration or argument problem, before any child starts. */
|
|
560
|
+
var DelegationError = class extends Error {
|
|
561
|
+
constructor(message) {
|
|
562
|
+
super(message);
|
|
563
|
+
this.name = "DelegationError";
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
/** Raised when a child starts but a later entry in the same call fails. */
|
|
567
|
+
var PartialDelegationError = class extends DelegationError {
|
|
568
|
+
startedChildren;
|
|
569
|
+
constructor(message, startedChildren) {
|
|
570
|
+
super(message);
|
|
571
|
+
this.name = "PartialDelegationError";
|
|
572
|
+
this.startedChildren = startedChildren;
|
|
252
573
|
}
|
|
574
|
+
};
|
|
575
|
+
function isNonEmptyString(value) {
|
|
576
|
+
return typeof value === "string" && value.trim() !== "";
|
|
253
577
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
* the parent normally passes its absolute path. When the parent omits it, this
|
|
257
|
-
* fallback keeps the delegate from drifting into general-purpose style.
|
|
258
|
-
*/
|
|
259
|
-
function composePrompt(input) {
|
|
260
|
-
if (input.kind === "general") return input.prompt;
|
|
261
|
-
const path = input.potetoModePath ?? POTETO_MODE_PATH;
|
|
262
|
-
return [
|
|
263
|
-
input.agentInstructions.trim(),
|
|
264
|
-
`Parent task label: ${input.description}`,
|
|
265
|
-
`Read \`${path}\` in full before any work, including its inline Principles index. Navigate to a leaf \`principle-*\` skill whenever you apply that principle.`,
|
|
266
|
-
input.prompt.trim()
|
|
267
|
-
].filter(Boolean).join("\n\n");
|
|
578
|
+
function isNonNegativeSafeInteger(value) {
|
|
579
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
268
580
|
}
|
|
269
581
|
/**
|
|
270
|
-
*
|
|
271
|
-
* instead of swallowing it.
|
|
582
|
+
* Validate the row configuration without a Schemastery dependency.
|
|
272
583
|
*
|
|
273
|
-
* A
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* report the miss rather than believing the route applied.
|
|
584
|
+
* A typo must fail at activation rather than at the first delegation, and an omitted
|
|
585
|
+
* `config` must be valid. Cordis passes the raw mapping because this row exports no
|
|
586
|
+
* `Config` object; callers that object as `config` are still checked here.
|
|
277
587
|
*/
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
588
|
+
function normalizeConfig(value) {
|
|
589
|
+
if (value === void 0 || value === null) return DEFAULT_CONFIG;
|
|
590
|
+
if (typeof value !== "object" || Array.isArray(value)) throw new Error("pstack-subagent: config must be a mapping");
|
|
591
|
+
const record = value;
|
|
592
|
+
for (const key of Object.keys(record)) if (key !== "provider" && key !== "maxDepth") throw new Error(`pstack-subagent: unknown config field "${key}"`);
|
|
593
|
+
const provider = record["provider"];
|
|
594
|
+
if (provider !== void 0 && !isNonEmptyString(provider)) throw new Error("pstack-subagent: provider must be a non-empty string");
|
|
595
|
+
const maxDepth = record["maxDepth"];
|
|
596
|
+
if (maxDepth !== void 0 && !isNonNegativeSafeInteger(maxDepth)) throw new Error("pstack-subagent: maxDepth must be a non-negative safe integer");
|
|
597
|
+
return {
|
|
598
|
+
provider: isNonEmptyString(provider) ? provider : DEFAULT_SUBAGENT_PROVIDER,
|
|
599
|
+
...isNonNegativeSafeInteger(maxDepth) ? { maxDepth } : {}
|
|
282
600
|
};
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
601
|
+
}
|
|
602
|
+
/** The preset id behind each pstack agent kind. `general` joins the parent preset. */
|
|
603
|
+
const AGENT_PRESETS = {
|
|
604
|
+
"poteto-agent": "poteto-agent",
|
|
605
|
+
"comment-sicko": "comment-sicko"
|
|
606
|
+
};
|
|
607
|
+
/** The preset a child of this kind must mount, or undefined to join the parent preset. */
|
|
608
|
+
function agentPresetFor(agent) {
|
|
609
|
+
return agent === "general" ? void 0 : AGENT_PRESETS[agent];
|
|
610
|
+
}
|
|
611
|
+
/** Compose the self-contained prompt one child receives. */
|
|
612
|
+
function composeChildPrompt(input) {
|
|
613
|
+
const sections = [];
|
|
614
|
+
const injected = input.injectedPaths ?? [];
|
|
615
|
+
if (injected.length > 0) sections.push([
|
|
616
|
+
"Absolute paths injected by `pstack_subagent`. Read them directly; do not search a plugin directory.",
|
|
617
|
+
"",
|
|
618
|
+
...injected.map((entry) => `- ${entry.label}: \`${entry.path}\``)
|
|
619
|
+
].join("\n"));
|
|
620
|
+
if (sections.length === 0) return input.task;
|
|
621
|
+
sections.push(`Task:\n\n${input.task}`);
|
|
622
|
+
return sections.join("\n\n---\n\n");
|
|
623
|
+
}
|
|
624
|
+
/** Validate every required model-facing argument. Null declines an optional setting. */
|
|
625
|
+
function parseDelegationArgs(raw) {
|
|
626
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new DelegationError("arguments must be an object");
|
|
627
|
+
const record = raw;
|
|
628
|
+
const known = [
|
|
629
|
+
"description",
|
|
630
|
+
"prompt",
|
|
631
|
+
"agent",
|
|
632
|
+
"role",
|
|
633
|
+
"roleIndex",
|
|
634
|
+
"reasoningEffort",
|
|
635
|
+
"cwd",
|
|
636
|
+
"maxDepth"
|
|
637
|
+
];
|
|
638
|
+
for (const key of known) if (!Object.hasOwn(record, key)) throw new DelegationError(`missing required argument "${key}"; every argument must be passed explicitly`);
|
|
639
|
+
for (const key of Object.keys(record)) if (!known.includes(key)) throw new DelegationError(`unknown argument "${key}"; accepted arguments are ${known.join(", ")}`);
|
|
640
|
+
const description = record["description"];
|
|
641
|
+
if (!isNonEmptyString(description)) throw new DelegationError("description must be a non-empty string: it becomes the child label");
|
|
642
|
+
const prompt = record["prompt"];
|
|
643
|
+
if (!isNonEmptyString(prompt)) throw new DelegationError("prompt must be a non-empty string");
|
|
644
|
+
const agent = record["agent"];
|
|
645
|
+
if (typeof agent !== "string" || !AGENT_KINDS.includes(agent)) throw new DelegationError(`agent must be one of ${AGENT_KINDS.join(", ")}; choose explicitly, and never pass a DSH agent preset id.`);
|
|
646
|
+
const role = record["role"];
|
|
647
|
+
if (role !== null && !isNonEmptyString(role)) throw new DelegationError("role must be a non-empty string or null");
|
|
648
|
+
const cwd = record["cwd"];
|
|
649
|
+
if (cwd !== null && !isNonEmptyString(cwd)) throw new DelegationError("cwd must be a non-empty string or null");
|
|
650
|
+
const maxDepth = record["maxDepth"];
|
|
651
|
+
if (!isNonNegativeSafeInteger(maxDepth)) throw new DelegationError("maxDepth must be a non-negative safe integer");
|
|
652
|
+
const roleIndex = record["roleIndex"];
|
|
653
|
+
if (roleIndex !== null && !isNonNegativeSafeInteger(roleIndex)) throw new DelegationError("roleIndex must be a non-negative safe integer or null");
|
|
654
|
+
if (roleIndex !== null && role === null) throw new DelegationError("roleIndex requires a non-null role");
|
|
655
|
+
const requestedEffort = record["reasoningEffort"];
|
|
656
|
+
if (requestedEffort !== null && !isNonEmptyString(requestedEffort)) throw new DelegationError("reasoningEffort must be a non-empty string or null");
|
|
657
|
+
if (typeof requestedEffort === "string" && /\s/.test(requestedEffort.trim())) throw new DelegationError(`reasoningEffort ${JSON.stringify(requestedEffort)} contains whitespace. An effort is one token, exactly as the model advertises it.`);
|
|
658
|
+
return {
|
|
659
|
+
description,
|
|
660
|
+
prompt,
|
|
661
|
+
agent,
|
|
662
|
+
role,
|
|
663
|
+
cwd,
|
|
664
|
+
maxDepth,
|
|
665
|
+
roleIndex,
|
|
666
|
+
reasoningEffort: requestedEffort?.trim() ?? null
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
/** Route text as reported to the model. */
|
|
670
|
+
function routeLabel(route, effortOverride) {
|
|
671
|
+
const effort = effectiveEffort(route, effortOverride);
|
|
672
|
+
const base = route.kind === "inherit" ? "inherit-parent" : `${route.provider}/${route.model}`;
|
|
673
|
+
return effort === void 0 ? base : `${base} [${effort}]`;
|
|
674
|
+
}
|
|
675
|
+
/** Take the smallest of the requested depth, this row's cap, and the Host setting. */
|
|
676
|
+
function resolveDelegationDepth(configured, requested, hostLimit) {
|
|
677
|
+
if (requested !== void 0 && !isNonNegativeSafeInteger(requested)) throw new DelegationError("maxDepth must be a non-negative safe integer");
|
|
678
|
+
if (configured !== void 0 && requested !== void 0 && requested > configured) throw new DelegationError(`maxDepth ${requested} exceeds this row's configured cap of ${configured}. Lower the request or raise \`pstack-subagent\` config.maxDepth.`);
|
|
679
|
+
const candidates = [
|
|
680
|
+
configured,
|
|
681
|
+
requested,
|
|
682
|
+
hostLimit
|
|
683
|
+
].filter((value) => value !== void 0);
|
|
684
|
+
return candidates.length === 0 ? void 0 : Math.min(...candidates);
|
|
685
|
+
}
|
|
686
|
+
/** Resolve the role file, routes, prompt, and depth into one plan. Pure of service calls. */
|
|
687
|
+
function planDelegation(args, runtime, config = DEFAULT_CONFIG) {
|
|
688
|
+
const rolePath = modelsFilePath(runtime.dshHome);
|
|
689
|
+
const roleFile = readRoleConfiguration(rolePath);
|
|
690
|
+
if (roleFile.status === "unreadable") throw new DelegationError(`the pstack role file at ${rolePath} exists but cannot be read (${roleFile.reason}). Fix its permissions or delete it, which makes every role inherit the parent route.`);
|
|
691
|
+
const diagnostics = [];
|
|
692
|
+
if (roleFile.status === "loaded") for (const issue of roleFile.issues) diagnostics.push(`${rolePath}${issue.line === void 0 ? "" : `:${issue.line}`}: ${issue.message}`);
|
|
693
|
+
let selection;
|
|
694
|
+
let roleSpec;
|
|
695
|
+
if (args.role !== null) {
|
|
696
|
+
roleSpec = requireRole(args.role);
|
|
697
|
+
const entry = roleFile.status === "loaded" ? roleFile.roles.get(roleSpec.key) : void 0;
|
|
698
|
+
selection = selectRoleRoutes(roleSpec, entry, args.roleIndex ?? void 0);
|
|
699
|
+
}
|
|
700
|
+
const routes = selection?.routes ?? [{ kind: "inherit" }];
|
|
701
|
+
const roleName = roleSpec?.key ?? "(none)";
|
|
702
|
+
if (selection !== void 0) {
|
|
703
|
+
if (!selection.configured) diagnostics.push(`role "${selection.role.key}" has no line in ${rolePath}, so it inherits the parent route. Add a line to route it explicitly.`);
|
|
704
|
+
if (selection.mode === "selected") diagnostics.push(`role "${selection.role.key}" holds ${selection.total} entries and is not an automatic panel; entry ${(selection.roleIndex ?? 0) + 1} was selected. Pass \`roleIndex\` to choose another.`);
|
|
705
|
+
if (selection.role.key === "swarm workers") diagnostics.push("role \"swarm workers\" is a list, not a panel: its first entry is the default route for every worker, and `roleIndex` gives one race arm another entry. Each worker still needs its own pstack_subagent call.");
|
|
706
|
+
}
|
|
707
|
+
const prompt = composeChildPrompt({
|
|
708
|
+
injectedPaths: args.agent === "poteto-agent" ? [{
|
|
709
|
+
label: "`poteto-mode` SKILL.md",
|
|
710
|
+
path: POTETO_MODE_SKILL_PATH
|
|
711
|
+
}] : [],
|
|
712
|
+
task: args.prompt
|
|
713
|
+
});
|
|
714
|
+
const agentPreset = agentPresetFor(args.agent);
|
|
715
|
+
const depth = resolveDelegationDepth(config.maxDepth, args.maxDepth, void 0);
|
|
716
|
+
const total = routes.length;
|
|
717
|
+
const children = routes.map((route, index) => {
|
|
718
|
+
const agentOptions = agentOptionsFor(route, args.reasoningEffort ?? void 0);
|
|
719
|
+
const effort = effectiveEffort(route, args.reasoningEffort ?? void 0);
|
|
287
720
|
return {
|
|
288
|
-
|
|
289
|
-
|
|
721
|
+
index,
|
|
722
|
+
total,
|
|
723
|
+
label: total > 1 ? `${args.description} [${roleName} ${index + 1}/${total}]` : args.description,
|
|
724
|
+
agent: args.agent,
|
|
725
|
+
role: roleName,
|
|
726
|
+
route,
|
|
727
|
+
routeLabel: routeLabel(route, args.reasoningEffort ?? void 0),
|
|
728
|
+
...effort === void 0 ? {} : { effort },
|
|
729
|
+
...agentOptions === void 0 ? {} : { agentOptions },
|
|
730
|
+
...agentPreset === void 0 ? {} : { agentPreset },
|
|
731
|
+
...args.cwd === null ? {} : { cwd: args.cwd },
|
|
732
|
+
...depth === void 0 ? {} : { maxDepth: depth }
|
|
290
733
|
};
|
|
734
|
+
});
|
|
735
|
+
if (args.reasoningEffort !== null) {
|
|
736
|
+
const overwritten = routes.filter((route) => route.kind === "route" && route.effort !== void 0 && route.effort !== args.reasoningEffort);
|
|
737
|
+
if (overwritten.length > 0) diagnostics.push(`reasoningEffort "${args.reasoningEffort}" overrides the effort this call's role line names (${overwritten.map((route) => routeLabel(route)).join(", ")}). The role line is a starting level; pass no reasoningEffort to keep it.`);
|
|
291
738
|
}
|
|
292
|
-
|
|
293
|
-
const lines = [];
|
|
294
|
-
if (unreadable) lines.push(`pstack: ${modelConfigPath()} could not be read; the child inherits the parent route.`);
|
|
295
|
-
else if (table.size === 0) lines.push(`pstack: no role table at ${modelConfigPath()}; the child inherits the parent route. Run /setup-pstack to configure roles.`);
|
|
296
|
-
else {
|
|
297
|
-
lines.push(`pstack: role "${role}" is not in ${modelConfigPath()}; the child inherits the parent route.`);
|
|
298
|
-
if (near.length > 0) lines.push(`pstack: did you mean ${near.map((n) => `"${n}"`).join(" or ")}?`);
|
|
299
|
-
}
|
|
739
|
+
addPanelDiagnostics(children, roleName, selection, rolePath, diagnostics);
|
|
300
740
|
return {
|
|
301
|
-
|
|
302
|
-
|
|
741
|
+
agent: args.agent,
|
|
742
|
+
prompt,
|
|
743
|
+
provider: config.provider,
|
|
744
|
+
role: roleName,
|
|
745
|
+
mode: selection?.mode ?? "single",
|
|
746
|
+
...selection?.roleIndex === void 0 ? {} : { roleIndex: selection.roleIndex },
|
|
747
|
+
roleTotal: selection?.total ?? 1,
|
|
748
|
+
...args.reasoningEffort === null ? {} : { reasoningEffort: args.reasoningEffort },
|
|
749
|
+
children,
|
|
750
|
+
diagnostics
|
|
303
751
|
};
|
|
304
752
|
}
|
|
753
|
+
function addPanelDiagnostics(children, roleName, selection, rolePath, diagnostics) {
|
|
754
|
+
if (selection === void 0 || selection.mode !== "panel") return;
|
|
755
|
+
if (children.length === 1) {
|
|
756
|
+
diagnostics.push(`panel "${roleName}" has one configured entry, so this call started one child and claims no model diversity. Add entries to ${rolePath} for a real panel.`);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const distinct = new Set(children.map((child) => child.routeLabel));
|
|
760
|
+
if (distinct.size > 1) return;
|
|
761
|
+
diagnostics.push(`all ${children.length} entries of panel "${roleName}" resolve to the same route (${[...distinct][0]}), so these children are not a cross-model review.`);
|
|
762
|
+
}
|
|
763
|
+
/** Check the backend can enforce what this call asks for. */
|
|
764
|
+
function assertProviderCapabilities(capabilities, providerName, asks) {
|
|
765
|
+
const missing = [];
|
|
766
|
+
if (asks.agentOptions && !capabilities.agentOptions) missing.push("agentOptions");
|
|
767
|
+
if (asks.agentPreset && !capabilities.agentPreset) missing.push("agentPreset");
|
|
768
|
+
if (asks.maxDepth && !capabilities.depthLimit) missing.push("maxDepth");
|
|
769
|
+
if (missing.length === 0) return;
|
|
770
|
+
throw new DelegationError(`subagent provider "${providerName}" does not support ${missing.join(", ")}. Change the route to \`inherit-parent\`, drop the unsupported argument, or configure a backend that supports it.`);
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Validate every route and effort before anything starts. An inherit-parent entry with
|
|
774
|
+
* no effort carries no agentOptions and is skipped; an override on an inherit route is
|
|
775
|
+
* validated against the parent's live route.
|
|
776
|
+
*/
|
|
777
|
+
async function preflightRoutes(llm, parent, children, roleName, effortOverride, signal) {
|
|
778
|
+
for (const child of children) {
|
|
779
|
+
if (child.agentOptions === void 0) continue;
|
|
780
|
+
if (child.route.kind === "inherit") {
|
|
781
|
+
const effort = effortOverride;
|
|
782
|
+
try {
|
|
783
|
+
await validateEffortFor(llm, void 0, effort, parent, signal);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
throw new DelegationError(`role ${roleName} inherits the parent route, and reasoningEffort "${effort}" failed preflight there: ${describeError(error)} Pass null for reasoningEffort, or name a role whose line holds an exact route.`);
|
|
786
|
+
}
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const route = child.route;
|
|
790
|
+
try {
|
|
791
|
+
await llm.resolveCallConfig({
|
|
792
|
+
provider: route.provider,
|
|
793
|
+
model: route.model,
|
|
794
|
+
...child.effort === void 0 ? {} : { reasoningEffort: child.effort }
|
|
795
|
+
}, signal);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
throw new DelegationError(`role ${roleName} resolves to ${child.routeLabel}, which failed route preflight: ${describeError(error)}. Check the route in ${modelsFilePath("$DSH_HOME")} against the providers this deployment serves.`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
305
801
|
/**
|
|
306
|
-
*
|
|
802
|
+
* Resolve and validate one child's `cwd`.
|
|
307
803
|
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* live delegation picked the wrong model.
|
|
804
|
+
* A child may run inside the session directory or inside an explicitly separate Git
|
|
805
|
+
* worktree. Anything else is refused: this tool does not create isolation, so it will
|
|
806
|
+
* not promise one.
|
|
312
807
|
*/
|
|
313
|
-
function
|
|
314
|
-
if (
|
|
808
|
+
function resolveChildCwd(requested, sessionRoot) {
|
|
809
|
+
if (requested === void 0) return void 0;
|
|
810
|
+
const resolved = isAbsolute(requested) ? resolve(requested) : resolve(sessionRoot, requested);
|
|
811
|
+
let isDirectory = false;
|
|
812
|
+
try {
|
|
813
|
+
isDirectory = statSync(resolved).isDirectory();
|
|
814
|
+
} catch {
|
|
815
|
+
isDirectory = false;
|
|
816
|
+
}
|
|
817
|
+
if (!isDirectory) throw new DelegationError(`cwd ${resolved} does not exist or is not a directory`);
|
|
818
|
+
if (isWithin(sessionRoot, resolved) || isGitWorktree(resolved)) return resolved;
|
|
819
|
+
throw new DelegationError(`cwd ${resolved} is outside this session's directory (${sessionRoot}) and is not a separate Git worktree. Pass a path inside the session directory, or create a worktree first and pass its checkout.`);
|
|
820
|
+
}
|
|
821
|
+
function isWithin(root, target) {
|
|
822
|
+
const rel = relative(root, target);
|
|
823
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
824
|
+
}
|
|
825
|
+
function isGitWorktree(directory) {
|
|
826
|
+
return existsSync(join(directory, ".git"));
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Start the planned children. Everything rejectable has already been rejected; a
|
|
830
|
+
* failure at entry N keeps children 1..N-1 running and reports their ids.
|
|
831
|
+
*/
|
|
832
|
+
async function startPlannedChildren(parent, plan, runtime, config, signal) {
|
|
833
|
+
const provider = runtime.subagents.getProvider(plan.provider);
|
|
834
|
+
if (provider === void 0) {
|
|
835
|
+
const available = runtime.subagents.list();
|
|
836
|
+
throw new DelegationError(`subagent provider "${plan.provider}" is not registered; registered providers: ${available.length === 0 ? "(none)" : available.join(", ")}.`);
|
|
837
|
+
}
|
|
838
|
+
const hostLimit = runtime.subagents.resolveMaxDepth(config.maxDepth);
|
|
839
|
+
const effectiveDepth = resolveDelegationDepth(config.maxDepth, plan.children[0]?.maxDepth, hostLimit);
|
|
840
|
+
assertProviderCapabilities(provider.capabilities, plan.provider, {
|
|
841
|
+
agentOptions: plan.children.some((child) => child.agentOptions !== void 0),
|
|
842
|
+
agentPreset: plan.children.some((child) => child.agentPreset !== void 0),
|
|
843
|
+
maxDepth: effectiveDepth !== void 0
|
|
844
|
+
});
|
|
845
|
+
const sessionRoot = await runtime.workingDirectory.ensure(parent, signal);
|
|
846
|
+
const children = plan.children.map((child) => ({
|
|
847
|
+
...child,
|
|
848
|
+
...effectiveDepth === void 0 ? {} : { maxDepth: effectiveDepth },
|
|
849
|
+
cwd: resolveChildCwd(child.cwd, sessionRoot)
|
|
850
|
+
}));
|
|
851
|
+
await preflightRoutes(runtime.llm, parent, children, plan.role, plan.reasoningEffort, signal);
|
|
852
|
+
if (runtime.subagents.getProvider(plan.provider) !== provider) throw new DelegationError(`subagent provider "${plan.provider}" changed while the child routes were being resolved; retry the delegation`);
|
|
853
|
+
const started = [];
|
|
854
|
+
const diagnostics = [...plan.diagnostics];
|
|
855
|
+
for (const child of children) {
|
|
856
|
+
signal.throwIfAborted();
|
|
857
|
+
try {
|
|
858
|
+
const activation = await runtime.subagents.startActivation({
|
|
859
|
+
provider: plan.provider,
|
|
860
|
+
label: child.label,
|
|
861
|
+
request: {
|
|
862
|
+
prompt: [{
|
|
863
|
+
type: "text",
|
|
864
|
+
text: plan.prompt
|
|
865
|
+
}],
|
|
866
|
+
parent,
|
|
867
|
+
...child.agentPreset === void 0 ? {} : { agentPreset: child.agentPreset },
|
|
868
|
+
...child.cwd === void 0 ? {} : { cwd: child.cwd },
|
|
869
|
+
...child.agentOptions === void 0 ? {} : { agentOptions: child.agentOptions },
|
|
870
|
+
...child.maxDepth === void 0 ? {} : { maxDepth: child.maxDepth }
|
|
871
|
+
},
|
|
872
|
+
signal,
|
|
873
|
+
delivery: "parent"
|
|
874
|
+
});
|
|
875
|
+
const target = runtime.agents === void 0 ? void 0 : runtime.agents.get(activation.childId);
|
|
876
|
+
if (target === void 0) diagnostics.push(`child ${activation.childId} has no live local Agent handle, so it cannot change its own reasoning effort with \`${SET_EFFORT_TOOL}\`. It runs at ${child.effort === void 0 ? "the adapter default" : `"${child.effort}"`} for its whole life.`);
|
|
877
|
+
else installEffortControl(target, runtime.llm);
|
|
878
|
+
started.push({
|
|
879
|
+
childId: activation.childId,
|
|
880
|
+
label: child.label,
|
|
881
|
+
agent: child.agent,
|
|
882
|
+
role: child.role,
|
|
883
|
+
route: child.routeLabel,
|
|
884
|
+
...child.effort === void 0 ? {} : { effort: child.effort },
|
|
885
|
+
...child.maxDepth === void 0 ? {} : { maxDepth: child.maxDepth }
|
|
886
|
+
});
|
|
887
|
+
} catch (error) {
|
|
888
|
+
if (started.length === 0) throw new DelegationError(`starting the ${plan.role === "(none)" ? "child" : `"${plan.role}" child`} "${child.label}" (${child.routeLabel}) failed: ${describeError(error)}`);
|
|
889
|
+
throw new PartialDelegationError(`starting entry ${child.index + 1} of ${child.total} for role "${plan.role}" (${child.label}, ${child.routeLabel}) failed: ${describeError(error)}. The ${started.length} already started: ${started.map((record) => `${record.childId} (${record.label})`).join(", ")}. Those children are still running and are not rolled back.`, started);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
315
892
|
return {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
893
|
+
kind: started.length === 1 ? "activation" : "activations",
|
|
894
|
+
children: started,
|
|
895
|
+
diagnostics
|
|
319
896
|
};
|
|
320
897
|
}
|
|
321
|
-
/**
|
|
322
|
-
function
|
|
323
|
-
return
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
898
|
+
/** Full delegation: plan, validate, start. */
|
|
899
|
+
async function delegate(parent, args, runtime, config = DEFAULT_CONFIG, signal = new AbortController().signal) {
|
|
900
|
+
return startPlannedChildren(parent, planDelegation(args, runtime, config), runtime, config, signal);
|
|
901
|
+
}
|
|
902
|
+
const TOOL_DESCRIPTION = [
|
|
903
|
+
"Delegate self-contained work to one or more fresh child agents.",
|
|
904
|
+
"",
|
|
905
|
+
"Every argument is required. Pass null for role, roleIndex, reasoningEffort, or cwd when that",
|
|
906
|
+
"setting is not used. Choose agent explicitly. maxDepth is a non-negative integer. No argument",
|
|
907
|
+
"has an implicit default.",
|
|
908
|
+
"",
|
|
909
|
+
"Returns the id of every child it started plus the route each one resolved. The children run",
|
|
910
|
+
"in the background: their results arrive later as completion notices in this conversation.",
|
|
911
|
+
"Never wait or poll for them, and keep working on something useful meanwhile.",
|
|
912
|
+
"",
|
|
913
|
+
"`agent` picks a DSH agent preset: `poteto-agent` mounts the pstack implementation delegate",
|
|
914
|
+
"and receives the absolute path of the poteto-mode instructions; `comment-sicko` mounts the",
|
|
915
|
+
"read-only comment reviewer; `general` joins the parent preset with no pstack persona.",
|
|
916
|
+
"",
|
|
917
|
+
"`role` selects a configured pstack role and therefore the child's model route and reasoning",
|
|
918
|
+
"effort. The role names come from the pstack role manifest, not this schema. Null inherits the",
|
|
919
|
+
"parent route. Automatic panel roles start one child per configured entry.",
|
|
920
|
+
"",
|
|
921
|
+
"`reasoningEffort` raises or lowers the effort for this delegation alone, using a token the",
|
|
922
|
+
"target model advertises. An unsupported value fails the call before any child starts,",
|
|
923
|
+
"including every entry of a panel. Null keeps the role line's starting effort. A running child",
|
|
924
|
+
"can change its own effort with `set_reasoning_effort`, which applies from its next step.",
|
|
925
|
+
"",
|
|
926
|
+
"Every prompt is a fresh child that does not see this conversation, so include everything it",
|
|
927
|
+
"needs. A child's own summary is not evidence: review its diff or artifacts yourself."
|
|
928
|
+
].join("\n");
|
|
929
|
+
function buildParameters() {
|
|
930
|
+
return {
|
|
931
|
+
type: "object",
|
|
932
|
+
additionalProperties: false,
|
|
933
|
+
properties: {
|
|
327
934
|
description: {
|
|
328
935
|
type: "string",
|
|
329
|
-
description: "A short
|
|
330
|
-
required: true
|
|
936
|
+
description: "A short 3-5 word label for the child, shown in list_agents and its notices."
|
|
331
937
|
},
|
|
332
938
|
prompt: {
|
|
333
939
|
type: "string",
|
|
334
|
-
description: "The complete, self-contained
|
|
335
|
-
required: true
|
|
940
|
+
description: "The complete, self-contained brief. The child does not see this conversation, so include the task, every prior directive, and the paths or branches it needs."
|
|
336
941
|
},
|
|
337
942
|
agent: {
|
|
338
943
|
type: "string",
|
|
339
|
-
enum: AGENT_KINDS,
|
|
340
|
-
description: "poteto-agent
|
|
944
|
+
enum: [...AGENT_KINDS],
|
|
945
|
+
description: "Required child preset: \"poteto-agent\" for pstack implementation delegates, \"comment-sicko\" for the read-only comment reviewer, or \"general\" to join the parent preset."
|
|
341
946
|
},
|
|
342
947
|
role: {
|
|
343
|
-
type: "string",
|
|
344
|
-
description: "
|
|
948
|
+
type: ["string", "null"],
|
|
949
|
+
description: "Required pstack role key, matched exactly. It selects the child's model route and effort. Null inherits the parent route."
|
|
345
950
|
},
|
|
346
951
|
cwd: {
|
|
347
|
-
type: "string",
|
|
348
|
-
description: "
|
|
952
|
+
type: ["string", "null"],
|
|
953
|
+
description: "Required initial working directory, inside this session's directory or a separate Git worktree. Relative paths resolve against this session's directory. Null inherits the session directory."
|
|
954
|
+
},
|
|
955
|
+
maxDepth: {
|
|
956
|
+
type: "integer",
|
|
957
|
+
description: "Required non-negative delegation-depth limit. It can only lower this row's configured cap."
|
|
958
|
+
},
|
|
959
|
+
roleIndex: {
|
|
960
|
+
type: ["integer", "null"],
|
|
961
|
+
description: "Required entry index for a list-valued role that is not an automatic panel, 0 or greater. Requires a non-null role. Null selects the first entry."
|
|
962
|
+
},
|
|
963
|
+
reasoningEffort: {
|
|
964
|
+
type: ["string", "null"],
|
|
965
|
+
description: "Required reasoning-effort override for every child this call starts. Use one token the target model advertises, or null to keep the role line's starting effort. An unknown token fails the whole call before any child starts."
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
required: [
|
|
969
|
+
"description",
|
|
970
|
+
"prompt",
|
|
971
|
+
"agent",
|
|
972
|
+
"role",
|
|
973
|
+
"roleIndex",
|
|
974
|
+
"reasoningEffort",
|
|
975
|
+
"cwd",
|
|
976
|
+
"maxDepth"
|
|
977
|
+
]
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
const OUTPUT_SCHEMA = {
|
|
981
|
+
type: "object",
|
|
982
|
+
additionalProperties: false,
|
|
983
|
+
properties: {
|
|
984
|
+
kind: {
|
|
985
|
+
type: "string",
|
|
986
|
+
enum: ["activation", "activations"],
|
|
987
|
+
description: "How many children this call started."
|
|
988
|
+
},
|
|
989
|
+
children: {
|
|
990
|
+
type: "array",
|
|
991
|
+
description: "One record per started child, in start order.",
|
|
992
|
+
items: {
|
|
993
|
+
type: "object",
|
|
994
|
+
additionalProperties: false,
|
|
995
|
+
properties: {
|
|
996
|
+
childId: {
|
|
997
|
+
type: "string",
|
|
998
|
+
description: "The child session id."
|
|
999
|
+
},
|
|
1000
|
+
label: {
|
|
1001
|
+
type: "string",
|
|
1002
|
+
description: "The label this call gave the child."
|
|
1003
|
+
},
|
|
1004
|
+
agent: {
|
|
1005
|
+
type: "string",
|
|
1006
|
+
description: "The child brief type."
|
|
1007
|
+
},
|
|
1008
|
+
role: {
|
|
1009
|
+
type: "string",
|
|
1010
|
+
description: "The role key, or `(none)`."
|
|
1011
|
+
},
|
|
1012
|
+
route: {
|
|
1013
|
+
type: "string",
|
|
1014
|
+
description: "`inherit-parent` or `<provider>/<model> [effort]`."
|
|
1015
|
+
},
|
|
1016
|
+
effort: {
|
|
1017
|
+
type: "string",
|
|
1018
|
+
description: "The reasoning effort this child runs at, when one applies."
|
|
1019
|
+
},
|
|
1020
|
+
maxDepth: {
|
|
1021
|
+
type: "integer",
|
|
1022
|
+
description: "The delegation-depth cap the child received."
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
required: [
|
|
1026
|
+
"childId",
|
|
1027
|
+
"label",
|
|
1028
|
+
"agent",
|
|
1029
|
+
"role",
|
|
1030
|
+
"route"
|
|
1031
|
+
]
|
|
349
1032
|
}
|
|
350
1033
|
},
|
|
1034
|
+
diagnostics: {
|
|
1035
|
+
type: "array",
|
|
1036
|
+
description: "Configuration notes, including a panel whose entries share one route.",
|
|
1037
|
+
items: { type: "string" }
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
required: [
|
|
1041
|
+
"kind",
|
|
1042
|
+
"children",
|
|
1043
|
+
"diagnostics"
|
|
1044
|
+
]
|
|
1045
|
+
};
|
|
1046
|
+
/** Render one delegation result for the model. */
|
|
1047
|
+
function renderDelegationResult(result) {
|
|
1048
|
+
const lines = [
|
|
1049
|
+
result.kind === "activation" ? `started 1 child: ${result.children[0]?.childId ?? "(none)"}` : `started ${result.children.length} children:`,
|
|
1050
|
+
...result.children.map((child) => `- ${child.childId} (${child.label}) agent=${child.agent} role=${child.role} route=${child.route}${child.effort === void 0 ? "" : ` effort=${child.effort}`}${child.maxDepth === void 0 ? "" : ` maxDepth=${child.maxDepth}`}`),
|
|
1051
|
+
"Results arrive as completion notices; do not poll for them. Each child can change its own",
|
|
1052
|
+
"effort from its next step with `set_reasoning_effort`."
|
|
1053
|
+
];
|
|
1054
|
+
if (result.diagnostics.length > 0) lines.push("", "Diagnostics:", ...result.diagnostics.map((diagnostic) => `- ${diagnostic}`));
|
|
1055
|
+
return lines.join("\n");
|
|
1056
|
+
}
|
|
1057
|
+
/** Build the model-facing tool for one row. */
|
|
1058
|
+
function createPstackSubagentTool(runtime, config = DEFAULT_CONFIG) {
|
|
1059
|
+
return {
|
|
1060
|
+
name: TOOL_NAME,
|
|
1061
|
+
description: TOOL_DESCRIPTION,
|
|
1062
|
+
parameters: buildParameters(),
|
|
351
1063
|
output: {
|
|
352
|
-
schema:
|
|
1064
|
+
schema: OUTPUT_SCHEMA,
|
|
353
1065
|
render: (_args, value) => [{
|
|
354
1066
|
type: "text",
|
|
355
|
-
text: value
|
|
1067
|
+
text: renderDelegationResult(value)
|
|
356
1068
|
}]
|
|
357
1069
|
},
|
|
358
|
-
|
|
1070
|
+
isConcurrencySafe: () => true,
|
|
1071
|
+
async execute(rawArgs, exec) {
|
|
359
1072
|
const parent = exec.agent;
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
const agentInstructions = await deps.loadAgent(kind);
|
|
363
|
-
const prompt = composePrompt({
|
|
364
|
-
kind,
|
|
365
|
-
description: args.description,
|
|
366
|
-
prompt: args.prompt,
|
|
367
|
-
agentInstructions,
|
|
368
|
-
...deps.potetoModePath ? { potetoModePath: deps.potetoModePath } : {}
|
|
369
|
-
});
|
|
370
|
-
const { route, notes } = await deps.loadRole(args.role);
|
|
371
|
-
const started = `started subagent ${(await deps.start({
|
|
372
|
-
parent,
|
|
373
|
-
label: args.description,
|
|
374
|
-
prompt: [{
|
|
375
|
-
type: "text",
|
|
376
|
-
text: prompt
|
|
377
|
-
}],
|
|
378
|
-
signal: exec.signal,
|
|
379
|
-
route,
|
|
380
|
-
cwd: args.cwd
|
|
381
|
-
})).childId}`;
|
|
382
|
-
return notes.length > 0 ? [started, ...notes].join("\n") : started;
|
|
1073
|
+
if (parent === void 0) throw new DelegationError("pstack_subagent needs a calling Agent, and exec.agent was undefined.");
|
|
1074
|
+
return await delegate(parent, parseDelegationArgs(rawArgs), runtime, config, exec.signal);
|
|
383
1075
|
}
|
|
384
|
-
}
|
|
1076
|
+
};
|
|
385
1077
|
}
|
|
386
|
-
/**
|
|
387
|
-
|
|
388
|
-
*
|
|
389
|
-
* A role is resolved once, at delegation time, into concrete provider options.
|
|
390
|
-
* The child inherits the parent route when the role is absent or resolves to
|
|
391
|
-
* `inherit-parent`, which is why the field is omitted rather than set to a
|
|
392
|
-
* placeholder.
|
|
393
|
-
*/
|
|
394
|
-
function buildActivationRequest(input) {
|
|
395
|
-
const options = routeToAgentOptions(input.route);
|
|
1078
|
+
/** Build the runtime a real Host call reads. */
|
|
1079
|
+
function hostRuntime(ctx, getAgentRegistry = () => void 0) {
|
|
396
1080
|
return {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
1081
|
+
subagents: ctx.subagents,
|
|
1082
|
+
llm: ctx.llm,
|
|
1083
|
+
workingDirectory: ctx.workingDirectory,
|
|
1084
|
+
get agents() {
|
|
1085
|
+
return getAgentRegistry();
|
|
1086
|
+
},
|
|
1087
|
+
dshHome: resolveDshHome()
|
|
401
1088
|
};
|
|
402
1089
|
}
|
|
403
|
-
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
request: buildActivationRequest({
|
|
417
|
-
parent,
|
|
418
|
-
prompt,
|
|
419
|
-
route,
|
|
420
|
-
cwd
|
|
421
|
-
})
|
|
422
|
-
});
|
|
423
|
-
return { childId: String(activation.childId) };
|
|
424
|
-
},
|
|
425
|
-
loadAgent: readAgentInstructions,
|
|
426
|
-
loadRole: resolveRoleArg,
|
|
427
|
-
potetoModePath: POTETO_MODE_PATH,
|
|
428
|
-
...config.defaultAgent ? { defaultAgent: config.defaultAgent } : {},
|
|
429
|
-
...config.toolName ? { toolName: config.toolName } : {}
|
|
430
|
-
});
|
|
431
|
-
ctx.tools.register(tool);
|
|
1090
|
+
/** Register `pstack_subagent` for this row. */
|
|
1091
|
+
function apply(ctx, config) {
|
|
1092
|
+
const resolved = normalizeConfig(config);
|
|
1093
|
+
const runtime = hostRuntime(ctx, () => ctx.get?.("agents"));
|
|
1094
|
+
ctx.tools.register(createPstackSubagentTool(runtime, resolved));
|
|
1095
|
+
}
|
|
1096
|
+
function describeError(error) {
|
|
1097
|
+
if (error instanceof Error) {
|
|
1098
|
+
const code = error.code;
|
|
1099
|
+
const suffix = typeof code === "string" ? ` (${code})` : "";
|
|
1100
|
+
return `${error.message}${suffix}`;
|
|
1101
|
+
}
|
|
1102
|
+
return String(error);
|
|
432
1103
|
}
|
|
433
|
-
|
|
434
|
-
export { apply, buildActivationRequest, composePrompt, createPstackTool, inject, name, readAgentInstructions, resolveRoleArg, routeToAgentOptions, stripFrontmatter };
|
|
435
|
-
|
|
436
|
-
//# sourceMappingURL=subagent.mjs.map
|
|
1104
|
+
export { AGENT_KINDS, AGENT_PRESETS, DEFAULT_SUBAGENT_PROVIDER, DelegationError, PartialDelegationError, TOOL_NAME, agentPresetFor, apply, assertProviderCapabilities, composeChildPrompt, createPstackSubagentTool, delegate, hostRuntime, inject, name, normalizeConfig, parseDelegationArgs, planDelegation, renderDelegationResult, resolveChildCwd, resolveDelegationDepth, routeLabel, startPlannedChildren };
|