@pennixrv/trellis 0.7.0-beta.9 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +14 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/ablate.js +1 -1
- package/dist/commands/ablate.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +30 -56
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/task-progress.d.ts +2 -0
- package/dist/commands/task-progress.d.ts.map +1 -1
- package/dist/commands/task-progress.js +37 -30
- package/dist/commands/task-progress.js.map +1 -1
- package/dist/commands/{channel → teamwork}/adapters/claude.d.ts +3 -3
- package/dist/commands/teamwork/adapters/claude.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/claude.js +3 -3
- package/dist/commands/teamwork/adapters/claude.js.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/codex.d.ts +1 -15
- package/dist/commands/teamwork/adapters/codex.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/codex.js +5 -42
- package/dist/commands/teamwork/adapters/codex.js.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/index.d.ts +6 -6
- package/dist/commands/teamwork/adapters/index.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/index.js +16 -52
- package/dist/commands/teamwork/adapters/index.js.map +1 -0
- package/dist/commands/{channel → teamwork}/adapters/types.d.ts +2 -2
- package/dist/commands/teamwork/adapters/types.d.ts.map +1 -0
- package/dist/commands/teamwork/adapters/types.js.map +1 -0
- package/dist/commands/{channel → teamwork}/agent-loader.d.ts +2 -4
- package/dist/commands/teamwork/agent-loader.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/agent-loader.js +4 -35
- package/dist/commands/teamwork/agent-loader.js.map +1 -0
- package/dist/commands/teamwork/artifact.d.ts +177 -0
- package/dist/commands/teamwork/artifact.d.ts.map +1 -0
- package/dist/commands/teamwork/artifact.js +908 -0
- package/dist/commands/teamwork/artifact.js.map +1 -0
- package/dist/commands/{channel → teamwork}/context-loader.d.ts +1 -1
- package/dist/commands/teamwork/context-loader.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/context-loader.js +12 -12
- package/dist/commands/teamwork/context-loader.js.map +1 -0
- package/dist/commands/{channel → teamwork}/context-trust.d.ts +6 -6
- package/dist/commands/teamwork/context-trust.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/context-trust.js +16 -16
- package/dist/commands/teamwork/context-trust.js.map +1 -0
- package/dist/commands/teamwork/context.d.ts +16 -0
- package/dist/commands/teamwork/context.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/context.js +17 -17
- package/dist/commands/teamwork/context.js.map +1 -0
- package/dist/commands/teamwork/coordinator.d.ts +89 -0
- package/dist/commands/teamwork/coordinator.d.ts.map +1 -0
- package/dist/commands/teamwork/coordinator.js +1198 -0
- package/dist/commands/teamwork/coordinator.js.map +1 -0
- package/dist/commands/teamwork/create.d.ts +23 -0
- package/dist/commands/teamwork/create.d.ts.map +1 -0
- package/dist/commands/teamwork/create.js +39 -0
- package/dist/commands/teamwork/create.js.map +1 -0
- package/dist/commands/{channel → teamwork}/dev-parse-trace.d.ts +1 -1
- package/dist/commands/teamwork/dev-parse-trace.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/dev-parse-trace.js +1 -1
- package/dist/commands/teamwork/dev-parse-trace.js.map +1 -0
- package/dist/commands/{channel → teamwork}/guard.d.ts +27 -46
- package/dist/commands/teamwork/guard.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/guard.js +54 -156
- package/dist/commands/teamwork/guard.js.map +1 -0
- package/dist/commands/teamwork/index.d.ts +3 -0
- package/dist/commands/teamwork/index.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/index.js +135 -165
- package/dist/commands/teamwork/index.js.map +1 -0
- package/dist/commands/{channel → teamwork}/interrupt.d.ts +1 -1
- package/dist/commands/teamwork/interrupt.d.ts.map +1 -0
- package/dist/commands/teamwork/interrupt.js +22 -0
- package/dist/commands/teamwork/interrupt.js.map +1 -0
- package/dist/commands/teamwork/kill.d.ts +7 -0
- package/dist/commands/teamwork/kill.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/kill.js +21 -21
- package/dist/commands/teamwork/kill.js.map +1 -0
- package/dist/commands/teamwork/list.d.ts +17 -0
- package/dist/commands/teamwork/list.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/list.js +18 -27
- package/dist/commands/teamwork/list.js.map +1 -0
- package/dist/commands/{channel → teamwork}/messages.d.ts +1 -1
- package/dist/commands/teamwork/messages.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/messages.js +18 -18
- package/dist/commands/teamwork/messages.js.map +1 -0
- package/dist/commands/teamwork/profiles.d.ts +25 -0
- package/dist/commands/teamwork/profiles.d.ts.map +1 -0
- package/dist/commands/teamwork/profiles.js +125 -0
- package/dist/commands/teamwork/profiles.js.map +1 -0
- package/dist/commands/teamwork/rm.d.ts +27 -0
- package/dist/commands/teamwork/rm.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/rm.js +21 -22
- package/dist/commands/teamwork/rm.js.map +1 -0
- package/dist/commands/teamwork/run.d.ts +29 -0
- package/dist/commands/{channel → teamwork}/run.d.ts.map +1 -1
- package/dist/commands/teamwork/run.js +129 -0
- package/dist/commands/teamwork/run.js.map +1 -0
- package/dist/commands/{channel → teamwork}/send.d.ts +1 -1
- package/dist/commands/teamwork/send.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/send.js +7 -7
- package/dist/commands/teamwork/send.js.map +1 -0
- package/dist/commands/{channel → teamwork}/spawn.d.ts +8 -12
- package/dist/commands/teamwork/spawn.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/spawn.js +51 -112
- package/dist/commands/teamwork/spawn.js.map +1 -0
- package/dist/commands/teamwork/store/events.d.ts +22 -0
- package/dist/commands/teamwork/store/events.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/store/events.js +8 -8
- package/dist/commands/teamwork/store/events.js.map +1 -0
- package/dist/commands/teamwork/store/filter.d.ts +3 -0
- package/dist/commands/teamwork/store/filter.d.ts.map +1 -0
- package/dist/commands/teamwork/store/filter.js +2 -0
- package/dist/commands/teamwork/store/filter.js.map +1 -0
- package/dist/commands/{channel → teamwork}/store/lock.d.ts +1 -1
- package/dist/commands/teamwork/store/lock.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/store/lock.js +2 -2
- package/dist/commands/teamwork/store/lock.js.map +1 -0
- package/dist/commands/{channel → teamwork}/store/paths.d.ts +18 -32
- package/dist/commands/teamwork/store/paths.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/store/paths.js +42 -109
- package/dist/commands/teamwork/store/paths.js.map +1 -0
- package/dist/commands/teamwork/store/schema.d.ts +18 -0
- package/dist/commands/teamwork/store/schema.d.ts.map +1 -0
- package/dist/commands/teamwork/store/schema.js +23 -0
- package/dist/commands/teamwork/store/schema.js.map +1 -0
- package/dist/commands/{channel → teamwork}/store/thread-state.d.ts +3 -3
- package/dist/commands/teamwork/store/thread-state.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/store/thread-state.js +1 -1
- package/dist/commands/teamwork/store/thread-state.js.map +1 -0
- package/dist/commands/teamwork/store/watch.d.ts +19 -0
- package/dist/commands/teamwork/store/watch.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/store/watch.js +8 -8
- package/dist/commands/teamwork/store/watch.js.map +1 -0
- package/dist/commands/teamwork/supervisor/idle.d.ts.map +1 -0
- package/dist/commands/teamwork/supervisor/idle.js.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/inbox.d.ts +2 -2
- package/dist/commands/teamwork/supervisor/inbox.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/inbox.js +14 -14
- package/dist/commands/teamwork/supervisor/inbox.js.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/shutdown.d.ts +3 -6
- package/dist/commands/teamwork/supervisor/shutdown.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/shutdown.js +7 -17
- package/dist/commands/teamwork/supervisor/shutdown.js.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/stdout.d.ts +3 -5
- package/dist/commands/teamwork/supervisor/stdout.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/stdout.js +12 -23
- package/dist/commands/teamwork/supervisor/stdout.js.map +1 -0
- package/dist/commands/teamwork/supervisor/turns.d.ts.map +1 -0
- package/dist/commands/teamwork/supervisor/turns.js.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/warning.d.ts +5 -5
- package/dist/commands/teamwork/supervisor/warning.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor/warning.js +6 -6
- package/dist/commands/teamwork/supervisor/warning.js.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor.d.ts +21 -21
- package/dist/commands/teamwork/supervisor.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/supervisor.js +53 -63
- package/dist/commands/teamwork/supervisor.js.map +1 -0
- package/dist/commands/teamwork/teammate-adapters.d.ts +31 -0
- package/dist/commands/teamwork/teammate-adapters.d.ts.map +1 -0
- package/dist/commands/teamwork/teammate-adapters.js +96 -0
- package/dist/commands/teamwork/teammate-adapters.js.map +1 -0
- package/dist/commands/teamwork/teammate-config.d.ts +19 -0
- package/dist/commands/teamwork/teammate-config.d.ts.map +1 -0
- package/dist/commands/teamwork/teammate-config.js +97 -0
- package/dist/commands/teamwork/teammate-config.js.map +1 -0
- package/dist/commands/teamwork/teammate-launch.d.ts +58 -0
- package/dist/commands/teamwork/teammate-launch.d.ts.map +1 -0
- package/dist/commands/teamwork/teammate-launch.js +125 -0
- package/dist/commands/teamwork/teammate-launch.js.map +1 -0
- package/dist/commands/teamwork/text-body.d.ts +13 -0
- package/dist/commands/teamwork/text-body.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/text-body.js +3 -3
- package/dist/commands/teamwork/text-body.js.map +1 -0
- package/dist/commands/teamwork/threads.d.ts +35 -0
- package/dist/commands/teamwork/threads.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/threads.js +19 -19
- package/dist/commands/teamwork/threads.js.map +1 -0
- package/dist/commands/teamwork/title.d.ts +12 -0
- package/dist/commands/teamwork/title.d.ts.map +1 -0
- package/dist/commands/teamwork/title.js +24 -0
- package/dist/commands/teamwork/title.js.map +1 -0
- package/dist/commands/{channel → teamwork}/wait.d.ts +1 -5
- package/dist/commands/teamwork/wait.d.ts.map +1 -0
- package/dist/commands/{channel → teamwork}/wait.js +8 -21
- package/dist/commands/teamwork/wait.js.map +1 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +48 -89
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/workflow.d.ts +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +9 -10
- package/dist/commands/workflow.js.map +1 -1
- package/dist/configurators/claude.d.ts +0 -17
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +2 -61
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +2 -2
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/snow.js +1 -1
- package/dist/configurators/snow.js.map +1 -1
- package/dist/configurators/workflow.d.ts +1 -1
- package/dist/configurators/workflow.js +5 -5
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +1 -1
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +1 -1
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.7.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.17.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.18.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.19.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.20.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.21.json +17 -0
- package/dist/migrations/manifests/0.7.0-beta.22.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.23.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.24.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.25.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.26.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.27.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.28.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.29.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.30.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.31.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.32.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.33.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.34.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.35.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.36.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.37.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.38.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.39.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.40.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.41.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.42.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.43.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.44.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.45.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.46.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.47.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.48.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.49.json +13 -0
- package/dist/migrations/manifests/0.7.0-beta.50.json +12 -0
- package/dist/migrations/manifests/0.7.0-beta.51.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.52.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.53.json +27 -0
- package/dist/migrations/manifests/0.7.0-beta.54.json +8 -0
- package/dist/migrations/manifests/0.7.1.json +9 -0
- package/dist/templates/claude/hooks/statusline.py +0 -7
- package/dist/templates/codex/hooks/session-start.py +31 -38
- package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/SKILL.md +10 -0
- package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +40 -0
- package/dist/templates/common/bundled-skills/trellis-delivery-review-and-closeout/SKILL.md +10 -0
- package/dist/templates/common/bundled-skills/trellis-delivery-review-and-closeout/workflow.md +55 -0
- package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/SKILL.md +10 -0
- package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +33 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +25 -25
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +13 -19
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +27 -75
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +17 -56
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +14 -11
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +0 -33
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-teamwork.md +32 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +25 -111
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +19 -60
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/SKILL.md +10 -0
- package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +36 -0
- package/dist/templates/common/bundled-skills/trellis-task-implementation/SKILL.md +10 -0
- package/dist/templates/common/bundled-skills/trellis-task-implementation/workflow.md +29 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +73 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +29 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +31 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +37 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +78 -0
- package/dist/templates/common/commands/continue.md +9 -49
- package/dist/templates/common/commands/finish-work.md +10 -58
- package/dist/templates/common/commands/start.md +19 -49
- package/dist/templates/common/skills/brainstorm.md +11 -199
- package/dist/templates/copilot/hooks/session-start.py +30 -26
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +7 -160
- package/dist/templates/copilot/prompts/finish-work.prompt.md +11 -88
- package/dist/templates/copilot/prompts/record-session.prompt.md +4 -12
- package/dist/templates/copilot/prompts/start.prompt.md +12 -10
- package/dist/templates/dsh/DSH.md +2 -2
- package/dist/templates/kiro/agents/trellis.json +2 -2
- package/dist/templates/markdown/agents.md +14 -3
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +9 -8
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +1 -1
- package/dist/templates/markdown/workspace-index.md +0 -1
- package/dist/templates/omp/extensions/trellis/index.ts.txt +28 -17
- package/dist/templates/opencode/lib/session-utils.js +20 -25
- package/dist/templates/opencode/lib/trellis-context.js +4 -76
- package/dist/templates/opencode/plugins/inject-subagent-context.js +5 -4
- package/dist/templates/opencode/plugins/inject-workflow-state.js +14 -17
- package/dist/templates/shared-hooks/inject-shell-session-context.py +3 -3
- package/dist/templates/shared-hooks/inject-subagent-context.py +445 -67
- package/dist/templates/shared-hooks/inject-workflow-state.py +44 -36
- package/dist/templates/shared-hooks/session-start.py +21 -33
- package/dist/templates/trellis/agents/delegated-worktree-primary-agent.md +18 -0
- package/dist/templates/trellis/agents/teammate-profiles.json +8 -0
- package/dist/templates/trellis/agents/teammate.md +63 -0
- package/dist/templates/trellis/config.yaml +29 -38
- package/dist/templates/trellis/gitignore.txt +1 -0
- package/dist/templates/trellis/index.d.ts +8 -12
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +14 -25
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +1 -1
- package/dist/templates/trellis/scripts/common/__init__.py +0 -1
- package/dist/templates/trellis/scripts/common/active_task.py +136 -100
- package/dist/templates/trellis/scripts/common/config.py +7 -9
- package/dist/templates/trellis/scripts/common/git_context.py +1 -1
- package/dist/templates/trellis/scripts/common/io.py +19 -0
- package/dist/templates/trellis/scripts/common/paths.py +0 -1
- package/dist/templates/trellis/scripts/common/safe_commit.py +11 -90
- package/dist/templates/trellis/scripts/common/session_context.py +1 -8
- package/dist/templates/trellis/scripts/common/spec_match.py +1 -1
- package/dist/templates/trellis/scripts/common/task_context.py +152 -59
- package/dist/templates/trellis/scripts/common/task_continuity.py +563 -0
- package/dist/templates/trellis/scripts/common/task_store.py +70 -548
- package/dist/templates/trellis/scripts/common/task_utils.py +4 -98
- package/dist/templates/trellis/scripts/common/task_workflow.py +1096 -0
- package/dist/templates/trellis/scripts/common/tasks.py +5 -8
- package/dist/templates/trellis/scripts/common/workflow_phase.py +8 -7
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +3 -3
- package/dist/templates/trellis/scripts/task.py +114 -377
- package/dist/templates/trellis/workflow.md +111 -706
- package/dist/utils/agent-refs.d.ts +5 -5
- package/dist/utils/agent-refs.js +6 -6
- package/dist/utils/file-writer.d.ts +3 -2
- package/dist/utils/file-writer.d.ts.map +1 -1
- package/dist/utils/file-writer.js +3 -2
- package/dist/utils/file-writer.js.map +1 -1
- package/dist/utils/managed-paths.d.ts +0 -7
- package/dist/utils/managed-paths.d.ts.map +1 -1
- package/dist/utils/managed-paths.js +0 -40
- package/dist/utils/managed-paths.js.map +1 -1
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +2 -11
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +3 -3
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/dist/utils/workflow-resolver.d.ts +10 -16
- package/dist/utils/workflow-resolver.d.ts.map +1 -1
- package/dist/utils/workflow-resolver.js +25 -36
- package/dist/utils/workflow-resolver.js.map +1 -1
- package/package.json +2 -2
- package/dist/commands/channel/adapters/claude.d.ts.map +0 -1
- package/dist/commands/channel/adapters/claude.js.map +0 -1
- package/dist/commands/channel/adapters/codex.d.ts.map +0 -1
- package/dist/commands/channel/adapters/codex.js.map +0 -1
- package/dist/commands/channel/adapters/index.d.ts.map +0 -1
- package/dist/commands/channel/adapters/index.js.map +0 -1
- package/dist/commands/channel/adapters/types.d.ts.map +0 -1
- package/dist/commands/channel/adapters/types.js.map +0 -1
- package/dist/commands/channel/agent-loader.d.ts.map +0 -1
- package/dist/commands/channel/agent-loader.js.map +0 -1
- package/dist/commands/channel/context-loader.d.ts.map +0 -1
- package/dist/commands/channel/context-loader.js.map +0 -1
- package/dist/commands/channel/context-trust.d.ts.map +0 -1
- package/dist/commands/channel/context-trust.js.map +0 -1
- package/dist/commands/channel/context.d.ts +0 -16
- package/dist/commands/channel/context.d.ts.map +0 -1
- package/dist/commands/channel/context.js.map +0 -1
- package/dist/commands/channel/create.d.ts +0 -29
- package/dist/commands/channel/create.d.ts.map +0 -1
- package/dist/commands/channel/create.js +0 -47
- package/dist/commands/channel/create.js.map +0 -1
- package/dist/commands/channel/dev-parse-trace.d.ts.map +0 -1
- package/dist/commands/channel/dev-parse-trace.js.map +0 -1
- package/dist/commands/channel/guard.d.ts.map +0 -1
- package/dist/commands/channel/guard.js.map +0 -1
- package/dist/commands/channel/index.d.ts +0 -3
- package/dist/commands/channel/index.d.ts.map +0 -1
- package/dist/commands/channel/index.js.map +0 -1
- package/dist/commands/channel/interrupt.d.ts.map +0 -1
- package/dist/commands/channel/interrupt.js +0 -22
- package/dist/commands/channel/interrupt.js.map +0 -1
- package/dist/commands/channel/kill.d.ts +0 -7
- package/dist/commands/channel/kill.d.ts.map +0 -1
- package/dist/commands/channel/kill.js.map +0 -1
- package/dist/commands/channel/list.d.ts +0 -18
- package/dist/commands/channel/list.d.ts.map +0 -1
- package/dist/commands/channel/list.js.map +0 -1
- package/dist/commands/channel/messages.d.ts.map +0 -1
- package/dist/commands/channel/messages.js.map +0 -1
- package/dist/commands/channel/rm.d.ts +0 -27
- package/dist/commands/channel/rm.d.ts.map +0 -1
- package/dist/commands/channel/rm.js.map +0 -1
- package/dist/commands/channel/run.d.ts +0 -32
- package/dist/commands/channel/run.js +0 -141
- package/dist/commands/channel/run.js.map +0 -1
- package/dist/commands/channel/send.d.ts.map +0 -1
- package/dist/commands/channel/send.js.map +0 -1
- package/dist/commands/channel/spawn.d.ts.map +0 -1
- package/dist/commands/channel/spawn.js.map +0 -1
- package/dist/commands/channel/store/events.d.ts +0 -22
- package/dist/commands/channel/store/events.d.ts.map +0 -1
- package/dist/commands/channel/store/events.js.map +0 -1
- package/dist/commands/channel/store/filter.d.ts +0 -3
- package/dist/commands/channel/store/filter.d.ts.map +0 -1
- package/dist/commands/channel/store/filter.js +0 -2
- package/dist/commands/channel/store/filter.js.map +0 -1
- package/dist/commands/channel/store/lock.d.ts.map +0 -1
- package/dist/commands/channel/store/lock.js.map +0 -1
- package/dist/commands/channel/store/paths.d.ts.map +0 -1
- package/dist/commands/channel/store/paths.js.map +0 -1
- package/dist/commands/channel/store/schema.d.ts +0 -27
- package/dist/commands/channel/store/schema.d.ts.map +0 -1
- package/dist/commands/channel/store/schema.js +0 -34
- package/dist/commands/channel/store/schema.js.map +0 -1
- package/dist/commands/channel/store/thread-state.d.ts.map +0 -1
- package/dist/commands/channel/store/thread-state.js.map +0 -1
- package/dist/commands/channel/store/watch.d.ts +0 -19
- package/dist/commands/channel/store/watch.d.ts.map +0 -1
- package/dist/commands/channel/store/watch.js.map +0 -1
- package/dist/commands/channel/supervisor/idle.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/idle.js.map +0 -1
- package/dist/commands/channel/supervisor/inbox.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/inbox.js.map +0 -1
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/shutdown.js.map +0 -1
- package/dist/commands/channel/supervisor/stdout.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/stdout.js.map +0 -1
- package/dist/commands/channel/supervisor/turns.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/turns.js.map +0 -1
- package/dist/commands/channel/supervisor/warning.d.ts.map +0 -1
- package/dist/commands/channel/supervisor/warning.js.map +0 -1
- package/dist/commands/channel/supervisor.d.ts.map +0 -1
- package/dist/commands/channel/supervisor.js.map +0 -1
- package/dist/commands/channel/text-body.d.ts +0 -13
- package/dist/commands/channel/text-body.d.ts.map +0 -1
- package/dist/commands/channel/text-body.js.map +0 -1
- package/dist/commands/channel/threads.d.ts +0 -39
- package/dist/commands/channel/threads.d.ts.map +0 -1
- package/dist/commands/channel/threads.js.map +0 -1
- package/dist/commands/channel/title.d.ts +0 -12
- package/dist/commands/channel/title.d.ts.map +0 -1
- package/dist/commands/channel/title.js +0 -24
- package/dist/commands/channel/title.js.map +0 -1
- package/dist/commands/channel/wait.d.ts.map +0 -1
- package/dist/commands/channel/wait.js.map +0 -1
- package/dist/commands/channel/workers.d.ts +0 -9
- package/dist/commands/channel/workers.d.ts.map +0 -1
- package/dist/commands/channel/workers.js +0 -31
- package/dist/commands/channel/workers.js.map +0 -1
- package/dist/templates/common/bundled-skills/trellis-channel/SKILL.md +0 -69
- package/dist/templates/common/bundled-skills/trellis-channel/references/command-reference.md +0 -549
- package/dist/templates/common/bundled-skills/trellis-channel/references/forum.md +0 -233
- package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md +0 -237
- package/dist/templates/common/bundled-skills/trellis-channel/references/subnode-work.md +0 -222
- package/dist/templates/common/bundled-skills/trellis-channel/references/workers.md +0 -295
- package/dist/templates/common/bundled-skills/trellis-channel/references/workflows.md +0 -131
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-channel.md +0 -70
- package/dist/templates/common/bundled-skills/trellis-research-record/SKILL.md +0 -14
- package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
- package/dist/templates/trellis/agents/check.md +0 -70
- package/dist/templates/trellis/agents/implement.md +0 -71
- package/dist/templates/trellis/agents/subnode.env +0 -4
- package/dist/templates/trellis/agents/subnode.md +0 -76
- package/dist/templates/trellis/scripts/common/context_projection.py +0 -508
- package/dist/templates/trellis/scripts/common/continuation_record.py +0 -450
- package/dist/templates/trellis/scripts/common/ownership_record.py +0 -439
- package/dist/templates/trellis/scripts/subnode_artifact.py +0 -735
- package/dist/templates/trellis/scripts/workspace_note.py +0 -126
- /package/dist/commands/{channel → teamwork}/adapters/types.js +0 -0
- /package/dist/commands/{channel → teamwork}/supervisor/idle.d.ts +0 -0
- /package/dist/commands/{channel → teamwork}/supervisor/idle.js +0 -0
- /package/dist/commands/{channel → teamwork}/supervisor/turns.d.ts +0 -0
- /package/dist/commands/{channel → teamwork}/supervisor/turns.js +0 -0
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
5
|
+
const SECRET = /(-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|gh[pousr])_[A-Za-z0-9]{20,}\b|\bBearer\s+\S+)/i;
|
|
6
|
+
function id(value, field) {
|
|
7
|
+
if (typeof value !== "string" || !ID.test(value))
|
|
8
|
+
throw new Error(`${field} must be a safe id`);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function boundedText(value, field) {
|
|
12
|
+
if (typeof value !== "string" ||
|
|
13
|
+
value.trim() === "" ||
|
|
14
|
+
value.includes("\0") ||
|
|
15
|
+
value.length > 4096) {
|
|
16
|
+
throw new Error(`${field} must be bounded text`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function digest(value, field) {
|
|
21
|
+
const result = boundedText(value, field);
|
|
22
|
+
if (!/^[a-f0-9]{64}$/i.test(result))
|
|
23
|
+
throw new Error(`${field} must be a SHA-256 digest`);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function relativePath(value, field) {
|
|
27
|
+
const result = boundedText(value, field);
|
|
28
|
+
if (path.isAbsolute(result))
|
|
29
|
+
throw new Error(`${field} must be relative`);
|
|
30
|
+
const normalized = path.normalize(result);
|
|
31
|
+
if (normalized === "." ||
|
|
32
|
+
normalized === ".." ||
|
|
33
|
+
normalized.startsWith(`..${path.sep}`)) {
|
|
34
|
+
throw new Error(`${field} must stay within the project root`);
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
function relativePathList(value, field) {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (!Array.isArray(value))
|
|
42
|
+
throw new Error(`${field} must be an array`);
|
|
43
|
+
const result = value.map((entry, index) => relativePath(entry, `${field}[${index}]`));
|
|
44
|
+
if (new Set(result).size !== result.length)
|
|
45
|
+
throw new Error(`${field} must not contain duplicates`);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function safePath(root, candidate) {
|
|
49
|
+
const absoluteRoot = path.resolve(root);
|
|
50
|
+
const lexical = path.resolve(root, candidate);
|
|
51
|
+
const relative = path.relative(absoluteRoot, lexical);
|
|
52
|
+
if (relative.startsWith("..") || path.isAbsolute(relative))
|
|
53
|
+
throw new Error(`artifact path escapes task root: ${candidate}`);
|
|
54
|
+
let current = absoluteRoot;
|
|
55
|
+
for (const part of relative.split(path.sep).filter(Boolean)) {
|
|
56
|
+
current = path.join(current, part);
|
|
57
|
+
if (fs.existsSync(current) && fs.lstatSync(current).isSymbolicLink()) {
|
|
58
|
+
throw new Error(`artifact path contains a symlink: ${candidate}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return lexical;
|
|
62
|
+
}
|
|
63
|
+
function rejectSecrets(value, field) {
|
|
64
|
+
const raw = JSON.stringify(value);
|
|
65
|
+
if (raw && SECRET.test(raw))
|
|
66
|
+
throw new Error(`${field} appears to contain a credential`);
|
|
67
|
+
}
|
|
68
|
+
function attemptArtifactPath(taskRoot, briefPath, attemptId, fileName) {
|
|
69
|
+
if (!ID.test(attemptId))
|
|
70
|
+
throw new Error("attempt id must be safe");
|
|
71
|
+
return safePath(taskRoot, path.join(path.dirname(briefPath), "attempts", attemptId, fileName));
|
|
72
|
+
}
|
|
73
|
+
function writeImmutableJson(filePath, value) {
|
|
74
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
75
|
+
const temporary = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`);
|
|
76
|
+
try {
|
|
77
|
+
const fd = fs.openSync(temporary, "wx", 0o600);
|
|
78
|
+
try {
|
|
79
|
+
fs.writeFileSync(fd, JSON.stringify(value) + "\n", "utf8");
|
|
80
|
+
fs.fsyncSync(fd);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
fs.closeSync(fd);
|
|
84
|
+
}
|
|
85
|
+
// linkSync is exclusive on the destination and therefore preserves the
|
|
86
|
+
// write-once contract even when another coordinator races this attempt.
|
|
87
|
+
fs.linkSync(temporary, filePath);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error.code === "EEXIST") {
|
|
91
|
+
throw new Error(`artifact already exists: ${filePath}`);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
fs.rmSync(temporary, { force: true });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function canonicalJson(value) {
|
|
100
|
+
if (Array.isArray(value))
|
|
101
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
102
|
+
if (value && typeof value === "object") {
|
|
103
|
+
const entries = Object.entries(value)
|
|
104
|
+
.filter(([key]) => key !== "plan_hash")
|
|
105
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
106
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
107
|
+
}
|
|
108
|
+
return JSON.stringify(value);
|
|
109
|
+
}
|
|
110
|
+
function withoutFields(value, fields) {
|
|
111
|
+
return Object.fromEntries(Object.entries(value).filter(([field]) => !fields.includes(field)));
|
|
112
|
+
}
|
|
113
|
+
export function computeTeamworkPlanHash(value) {
|
|
114
|
+
return crypto.createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
115
|
+
}
|
|
116
|
+
export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
117
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
118
|
+
throw new Error("Teamwork plan must be an object");
|
|
119
|
+
const value = input;
|
|
120
|
+
if (value.schema_version !== 1)
|
|
121
|
+
throw new Error("Teamwork plan schema_version must be 1");
|
|
122
|
+
const taskId = id(value.task_id, "task_id");
|
|
123
|
+
const batchId = id(value.batch_id, "batch_id");
|
|
124
|
+
const sessionId = id(value.session_id, "session_id");
|
|
125
|
+
const phaseId = boundedText(value.phase_id, "phase_id");
|
|
126
|
+
const taskRouteRevision = boundedText(value.task_route_revision, "task_route_revision");
|
|
127
|
+
const inputRevision = boundedText(value.input_revision, "input_revision");
|
|
128
|
+
const planSummary = boundedText(value.plan_summary, "plan_summary");
|
|
129
|
+
const coordinationMode = value.coordination_mode;
|
|
130
|
+
if (coordinationMode !== "general" &&
|
|
131
|
+
coordinationMode !== "advisor" &&
|
|
132
|
+
coordinationMode !== "committee") {
|
|
133
|
+
throw new Error("coordination_mode is unsupported");
|
|
134
|
+
}
|
|
135
|
+
if (expectedContext) {
|
|
136
|
+
for (const [field, expected] of Object.entries({
|
|
137
|
+
task_id: expectedContext.taskId,
|
|
138
|
+
phase_id: expectedContext.phaseId,
|
|
139
|
+
task_route_revision: expectedContext.taskRouteRevision,
|
|
140
|
+
input_revision: expectedContext.inputRevision,
|
|
141
|
+
})) {
|
|
142
|
+
if (value[field] !== expected)
|
|
143
|
+
throw new Error(`Teamwork plan ${field} does not match the current task`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const deadline = boundedText(value.deadline, "deadline");
|
|
147
|
+
const deadlineAt = Date.parse(deadline);
|
|
148
|
+
if (Number.isNaN(deadlineAt))
|
|
149
|
+
throw new Error("deadline must be an ISO date");
|
|
150
|
+
if (deadlineAt <= Date.now())
|
|
151
|
+
throw new Error("deadline must be in the future");
|
|
152
|
+
const assignmentValue = value.assignments;
|
|
153
|
+
if (!Array.isArray(assignmentValue) || assignmentValue.length === 0)
|
|
154
|
+
throw new Error("Teamwork plan assignments must be non-empty");
|
|
155
|
+
if (coordinationMode === "advisor" && assignmentValue.length !== 1)
|
|
156
|
+
throw new Error("advisor mode requires exactly one assignment");
|
|
157
|
+
if (coordinationMode === "committee" &&
|
|
158
|
+
(assignmentValue.length < 2 || assignmentValue.length > 3))
|
|
159
|
+
throw new Error("committee mode requires two or three assignments");
|
|
160
|
+
const seen = new Set();
|
|
161
|
+
const rawAssignments = new Map();
|
|
162
|
+
const briefs = new Map();
|
|
163
|
+
const assignments = assignmentValue.map((raw, index) => {
|
|
164
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
165
|
+
throw new Error(`assignments[${index}] must be an object`);
|
|
166
|
+
const item = raw;
|
|
167
|
+
const assignmentId = id(item.assignment_id, `assignments[${index}].assignment_id`);
|
|
168
|
+
if (seen.has(assignmentId))
|
|
169
|
+
throw new Error(`duplicate assignment id '${assignmentId}'`);
|
|
170
|
+
seen.add(assignmentId);
|
|
171
|
+
rawAssignments.set(assignmentId, item);
|
|
172
|
+
const evaluationGroupValue = item.evaluation_group;
|
|
173
|
+
let evaluationGroup;
|
|
174
|
+
if (evaluationGroupValue !== undefined) {
|
|
175
|
+
if (!evaluationGroupValue ||
|
|
176
|
+
typeof evaluationGroupValue !== "object" ||
|
|
177
|
+
Array.isArray(evaluationGroupValue)) {
|
|
178
|
+
throw new Error(`assignments[${index}].evaluation_group must be an object`);
|
|
179
|
+
}
|
|
180
|
+
const group = evaluationGroupValue;
|
|
181
|
+
if (coordinationMode !== "committee" || group.kind !== "committee")
|
|
182
|
+
throw new Error(`assignments[${index}].evaluation_group is unsupported`);
|
|
183
|
+
evaluationGroup = {
|
|
184
|
+
id: id(group.id, `assignments[${index}].evaluation_group.id`),
|
|
185
|
+
kind: "committee",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
else if (coordinationMode === "committee") {
|
|
189
|
+
throw new Error(`assignments[${index}] must declare an evaluation group`);
|
|
190
|
+
}
|
|
191
|
+
if (item.role_id !== "teammate")
|
|
192
|
+
throw new Error(`assignments[${index}].role_id must be teammate`);
|
|
193
|
+
const provider = item.provider;
|
|
194
|
+
if (provider !== "claude" &&
|
|
195
|
+
provider !== "codex" &&
|
|
196
|
+
provider !== "dsh" &&
|
|
197
|
+
provider !== "copilot") {
|
|
198
|
+
throw new Error(`assignments[${index}].provider is unsupported`);
|
|
199
|
+
}
|
|
200
|
+
const acceptance = item.acceptance;
|
|
201
|
+
if (!Array.isArray(acceptance) || acceptance.length === 0)
|
|
202
|
+
throw new Error(`assignments[${index}].acceptance must be non-empty`);
|
|
203
|
+
const maxRetries = item.max_retries;
|
|
204
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0)
|
|
205
|
+
throw new Error(`assignments[${index}].max_retries must be non-negative`);
|
|
206
|
+
const briefPath = boundedText(item.brief_path, `assignments[${index}].brief_path`);
|
|
207
|
+
const scope = boundedText(item.scope, `assignments[${index}].scope`);
|
|
208
|
+
const lens = boundedText(item.lens, `assignments[${index}].lens`);
|
|
209
|
+
const summary = boundedText(item.summary, `assignments[${index}].summary`);
|
|
210
|
+
const briefDigest = digest(item.brief_digest, `assignments[${index}].brief_digest`);
|
|
211
|
+
const sourceDigest = digest(item.source_digest, `assignments[${index}].source_digest`);
|
|
212
|
+
const contextFiles = relativePathList(item.context_files, `assignments[${index}].context_files`);
|
|
213
|
+
const contextManifests = relativePathList(item.context_manifests, `assignments[${index}].context_manifests`);
|
|
214
|
+
const profile = item.profile === undefined
|
|
215
|
+
? undefined
|
|
216
|
+
: id(item.profile, `assignments[${index}].profile`);
|
|
217
|
+
const model = item.model === undefined
|
|
218
|
+
? undefined
|
|
219
|
+
: boundedText(item.model, `assignments[${index}].model`);
|
|
220
|
+
const absoluteBrief = safePath(taskRoot, briefPath);
|
|
221
|
+
const expectedRelative = path.join("teammates", batchId, assignmentId, "brief.json");
|
|
222
|
+
if (path.relative(path.resolve(taskRoot), absoluteBrief) !==
|
|
223
|
+
expectedRelative)
|
|
224
|
+
throw new Error(`assignments[${index}].brief_path must be ${expectedRelative}`);
|
|
225
|
+
const briefBytes = fs.readFileSync(absoluteBrief);
|
|
226
|
+
if (briefBytes.byteLength > 64 * 1024)
|
|
227
|
+
throw new Error(`brief for assignment '${assignmentId}' exceeds 65536 bytes`);
|
|
228
|
+
const brief = JSON.parse(briefBytes.toString("utf8"));
|
|
229
|
+
if (!brief || typeof brief !== "object" || Array.isArray(brief))
|
|
230
|
+
throw new Error(`brief for assignment '${assignmentId}' must be an object`);
|
|
231
|
+
briefs.set(assignmentId, brief);
|
|
232
|
+
rejectSecrets(brief, `brief for assignment '${assignmentId}'`);
|
|
233
|
+
if (computeTeamworkPlanHash(brief) !== briefDigest)
|
|
234
|
+
throw new Error(`brief.${assignmentId} digest does not match the Teamwork plan`);
|
|
235
|
+
for (const [field, expected] of Object.entries({
|
|
236
|
+
schema_version: 1,
|
|
237
|
+
task_id: taskId,
|
|
238
|
+
batch_id: batchId,
|
|
239
|
+
assignment_id: assignmentId,
|
|
240
|
+
role_id: "teammate",
|
|
241
|
+
provider,
|
|
242
|
+
summary,
|
|
243
|
+
scope,
|
|
244
|
+
lens,
|
|
245
|
+
deadline,
|
|
246
|
+
})) {
|
|
247
|
+
if (brief[field] !== expected)
|
|
248
|
+
throw new Error(`brief.${field} does not match the Teamwork plan`);
|
|
249
|
+
}
|
|
250
|
+
const briefSourceDigest = digest(brief.source_digest, `brief.${assignmentId}.source_digest`);
|
|
251
|
+
if (briefSourceDigest !== sourceDigest)
|
|
252
|
+
throw new Error(`brief.${assignmentId}.source_digest does not match the Teamwork plan`);
|
|
253
|
+
for (const [field, expected] of [
|
|
254
|
+
["context_files", contextFiles],
|
|
255
|
+
["context_manifests", contextManifests],
|
|
256
|
+
]) {
|
|
257
|
+
const actual = relativePathList(brief[field], `brief.${assignmentId}.${field}`);
|
|
258
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected))
|
|
259
|
+
throw new Error(`brief.${field} does not match the Teamwork plan`);
|
|
260
|
+
}
|
|
261
|
+
for (const field of ["profile", "model"]) {
|
|
262
|
+
if (item[field] !== undefined && brief[field] !== item[field])
|
|
263
|
+
throw new Error(`brief.${field} does not match the Teamwork plan`);
|
|
264
|
+
if (item[field] === undefined && brief[field] !== undefined)
|
|
265
|
+
throw new Error(`brief.${field} is not declared by the Teamwork plan`);
|
|
266
|
+
}
|
|
267
|
+
const briefAcceptance = brief.acceptance;
|
|
268
|
+
if (!Array.isArray(briefAcceptance) ||
|
|
269
|
+
JSON.stringify(briefAcceptance) !== JSON.stringify(acceptance))
|
|
270
|
+
throw new Error(`brief acceptance contract does not match assignment '${assignmentId}'`);
|
|
271
|
+
const assignment = {
|
|
272
|
+
assignmentId,
|
|
273
|
+
...(evaluationGroup ? { evaluationGroup } : {}),
|
|
274
|
+
briefPath,
|
|
275
|
+
roleId: "teammate",
|
|
276
|
+
provider,
|
|
277
|
+
...(profile ? { profile } : {}),
|
|
278
|
+
...(model ? { model } : {}),
|
|
279
|
+
summary,
|
|
280
|
+
scope,
|
|
281
|
+
lens,
|
|
282
|
+
briefDigest,
|
|
283
|
+
sourceDigest,
|
|
284
|
+
...(contextFiles ? { contextFiles } : {}),
|
|
285
|
+
...(contextManifests ? { contextManifests } : {}),
|
|
286
|
+
acceptance: acceptance.map((entry, acceptanceIndex) => boundedText(entry, `assignments[${index}].acceptance[${acceptanceIndex}]`)),
|
|
287
|
+
maxRetries: maxRetries,
|
|
288
|
+
};
|
|
289
|
+
rejectSecrets(assignment, `assignments[${index}]`);
|
|
290
|
+
return assignment;
|
|
291
|
+
});
|
|
292
|
+
if (coordinationMode === "committee") {
|
|
293
|
+
const groups = new Map();
|
|
294
|
+
for (const assignment of assignments) {
|
|
295
|
+
const group = assignment.evaluationGroup;
|
|
296
|
+
if (!group)
|
|
297
|
+
throw new Error("committee assignments require an evaluation group");
|
|
298
|
+
const members = groups.get(group.id) ?? [];
|
|
299
|
+
members.push(assignment);
|
|
300
|
+
groups.set(group.id, members);
|
|
301
|
+
}
|
|
302
|
+
if (groups.size !== 1)
|
|
303
|
+
throw new Error("committee assignments must share one evaluation group");
|
|
304
|
+
const members = [...groups.values()][0] ?? [];
|
|
305
|
+
if (members.length < 2 || members.length > 3)
|
|
306
|
+
throw new Error("committee evaluation group requires two or three assignments");
|
|
307
|
+
if (new Set(members.map((assignment) => assignment.lens)).size !==
|
|
308
|
+
members.length)
|
|
309
|
+
throw new Error("committee assignments must use distinct lenses");
|
|
310
|
+
const assignmentFields = [
|
|
311
|
+
"assignment_id",
|
|
312
|
+
"provider",
|
|
313
|
+
"profile",
|
|
314
|
+
"model",
|
|
315
|
+
"lens",
|
|
316
|
+
"brief_path",
|
|
317
|
+
"brief_digest",
|
|
318
|
+
];
|
|
319
|
+
const briefFields = [
|
|
320
|
+
"assignment_id",
|
|
321
|
+
"provider",
|
|
322
|
+
"profile",
|
|
323
|
+
"model",
|
|
324
|
+
"lens",
|
|
325
|
+
];
|
|
326
|
+
const first = members[0];
|
|
327
|
+
if (!first)
|
|
328
|
+
throw new Error("committee has no assignments");
|
|
329
|
+
for (const other of members.slice(1)) {
|
|
330
|
+
const firstRaw = rawAssignments.get(first.assignmentId);
|
|
331
|
+
const otherRaw = rawAssignments.get(other.assignmentId);
|
|
332
|
+
const firstBrief = briefs.get(first.assignmentId);
|
|
333
|
+
const otherBrief = briefs.get(other.assignmentId);
|
|
334
|
+
if (!firstRaw || !otherRaw || !firstBrief || !otherBrief)
|
|
335
|
+
throw new Error("committee is missing frozen artifacts");
|
|
336
|
+
if (canonicalJson(withoutFields(firstRaw, assignmentFields)) !==
|
|
337
|
+
canonicalJson(withoutFields(otherRaw, assignmentFields)))
|
|
338
|
+
throw new Error("committee assignments must share the same assignment contract");
|
|
339
|
+
if (canonicalJson(withoutFields(firstBrief, briefFields)) !==
|
|
340
|
+
canonicalJson(withoutFields(otherBrief, briefFields)))
|
|
341
|
+
throw new Error("committee assignments must share the same brief contract");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (!Array.isArray(value.protected_targets) &&
|
|
345
|
+
value.protected_targets !== undefined)
|
|
346
|
+
throw new Error("protected_targets must be an array");
|
|
347
|
+
const protectedTargets = (value.protected_targets ?? []).map((entry, index) => boundedText(entry, `protected_targets[${index}]`));
|
|
348
|
+
if (new Set(protectedTargets).size !== protectedTargets.length)
|
|
349
|
+
throw new Error("protected_targets must not contain duplicates");
|
|
350
|
+
const requestedCapacityValue = value.requested_capacity;
|
|
351
|
+
if (requestedCapacityValue !== undefined &&
|
|
352
|
+
(!Number.isInteger(requestedCapacityValue) ||
|
|
353
|
+
requestedCapacityValue <= 0))
|
|
354
|
+
throw new Error("requested_capacity must be a positive integer");
|
|
355
|
+
const plan = {
|
|
356
|
+
schemaVersion: 1,
|
|
357
|
+
taskId,
|
|
358
|
+
batchId,
|
|
359
|
+
sessionId,
|
|
360
|
+
phaseId,
|
|
361
|
+
taskRouteRevision,
|
|
362
|
+
inputRevision,
|
|
363
|
+
planSummary,
|
|
364
|
+
coordinationMode,
|
|
365
|
+
planHash: boundedText(value.plan_hash, "plan_hash"),
|
|
366
|
+
...(requestedCapacityValue !== undefined
|
|
367
|
+
? { requestedCapacity: requestedCapacityValue }
|
|
368
|
+
: {}),
|
|
369
|
+
assignments,
|
|
370
|
+
deadline,
|
|
371
|
+
protectedTargets,
|
|
372
|
+
};
|
|
373
|
+
if (!/^[a-f0-9]{64}$/i.test(plan.planHash) ||
|
|
374
|
+
plan.planHash !== computeTeamworkPlanHash(value))
|
|
375
|
+
throw new Error("Teamwork plan hash does not match its immutable contents");
|
|
376
|
+
rejectSecrets(plan, "Teamwork plan");
|
|
377
|
+
return plan;
|
|
378
|
+
}
|
|
379
|
+
export function validateTeammateReport(input, identity, expectedSourceDigest) {
|
|
380
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
381
|
+
throw new Error("teammate report must be an object");
|
|
382
|
+
const value = input;
|
|
383
|
+
if (value.schema_version !== 1)
|
|
384
|
+
throw new Error("teammate report schema_version must be 1");
|
|
385
|
+
for (const key of [
|
|
386
|
+
"task_id",
|
|
387
|
+
"batch_id",
|
|
388
|
+
"assignment_id",
|
|
389
|
+
"attempt_id",
|
|
390
|
+
"teammate_id",
|
|
391
|
+
"plan_hash",
|
|
392
|
+
"phase_id",
|
|
393
|
+
"task_route_revision",
|
|
394
|
+
"input_revision",
|
|
395
|
+
]) {
|
|
396
|
+
const expected = identity[key === "task_id"
|
|
397
|
+
? "taskId"
|
|
398
|
+
: key === "batch_id"
|
|
399
|
+
? "batchId"
|
|
400
|
+
: key === "assignment_id"
|
|
401
|
+
? "assignmentId"
|
|
402
|
+
: key === "attempt_id"
|
|
403
|
+
? "attemptId"
|
|
404
|
+
: key === "teammate_id"
|
|
405
|
+
? "teammateId"
|
|
406
|
+
: key === "plan_hash"
|
|
407
|
+
? "planHash"
|
|
408
|
+
: key === "phase_id"
|
|
409
|
+
? "phaseId"
|
|
410
|
+
: key === "task_route_revision"
|
|
411
|
+
? "taskRouteRevision"
|
|
412
|
+
: "inputRevision"];
|
|
413
|
+
if (value[key] !== expected)
|
|
414
|
+
throw new Error(`report.${key} does not match the assigned identity`);
|
|
415
|
+
}
|
|
416
|
+
const status = value.status;
|
|
417
|
+
if (status !== "complete" &&
|
|
418
|
+
status !== "blocked" &&
|
|
419
|
+
status !== "incomplete" &&
|
|
420
|
+
status !== "error")
|
|
421
|
+
throw new Error("report.status is invalid");
|
|
422
|
+
const sourceDigest = boundedText(value.source_digest, "report.source_digest");
|
|
423
|
+
if (sourceDigest !== expectedSourceDigest)
|
|
424
|
+
throw new Error("report source digest does not match the assigned brief");
|
|
425
|
+
if (!Array.isArray(value.evidence))
|
|
426
|
+
throw new Error("report.evidence must be an array");
|
|
427
|
+
const evidence = value.evidence.map((entry, index) => {
|
|
428
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
429
|
+
throw new Error(`report.evidence[${index}] must be an object`);
|
|
430
|
+
const item = entry;
|
|
431
|
+
return {
|
|
432
|
+
id: id(item.id, `report.evidence[${index}].id`),
|
|
433
|
+
locator: boundedText(item.locator, `report.evidence[${index}].locator`),
|
|
434
|
+
summary: boundedText(item.summary, `report.evidence[${index}].summary`),
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
if (status !== "complete" || evidence.length === 0) {
|
|
438
|
+
throw new Error(`report status '${status}' cannot be accepted as completed work`);
|
|
439
|
+
}
|
|
440
|
+
const report = {
|
|
441
|
+
schemaVersion: 1,
|
|
442
|
+
taskId: identity.taskId,
|
|
443
|
+
batchId: identity.batchId,
|
|
444
|
+
assignmentId: identity.assignmentId,
|
|
445
|
+
attemptId: identity.attemptId,
|
|
446
|
+
teammateId: identity.teammateId,
|
|
447
|
+
planHash: identity.planHash,
|
|
448
|
+
phaseId: identity.phaseId,
|
|
449
|
+
taskRouteRevision: identity.taskRouteRevision,
|
|
450
|
+
inputRevision: identity.inputRevision,
|
|
451
|
+
status,
|
|
452
|
+
sourceDigest,
|
|
453
|
+
evidence,
|
|
454
|
+
};
|
|
455
|
+
rejectSecrets(report, "teammate report");
|
|
456
|
+
return report;
|
|
457
|
+
}
|
|
458
|
+
export function validateTeammateQueueFact(input, identity, expectedPlanHash, expectedBriefDigest, expectedSourceDigest) {
|
|
459
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
460
|
+
throw new Error("teammate queue fact must be an object");
|
|
461
|
+
const value = input;
|
|
462
|
+
if (value.schema_version !== 1)
|
|
463
|
+
throw new Error("teammate queue fact schema_version must be 1");
|
|
464
|
+
for (const key of ["task_id", "batch_id", "assignment_id"]) {
|
|
465
|
+
const expected = identity[key === "task_id"
|
|
466
|
+
? "taskId"
|
|
467
|
+
: key === "batch_id"
|
|
468
|
+
? "batchId"
|
|
469
|
+
: "assignmentId"];
|
|
470
|
+
if (value[key] !== expected)
|
|
471
|
+
throw new Error(`queue.${key} does not match the assigned work`);
|
|
472
|
+
}
|
|
473
|
+
const planHash = digest(value.plan_hash, "queue.plan_hash");
|
|
474
|
+
if (planHash !== expectedPlanHash)
|
|
475
|
+
throw new Error("queue plan hash does not match the batch plan");
|
|
476
|
+
const briefDigest = digest(value.brief_digest, "queue.brief_digest");
|
|
477
|
+
if (briefDigest !== expectedBriefDigest)
|
|
478
|
+
throw new Error("queue brief digest does not match the assigned work");
|
|
479
|
+
const sourceDigest = digest(value.source_digest, "queue.source_digest");
|
|
480
|
+
if (sourceDigest !== expectedSourceDigest)
|
|
481
|
+
throw new Error("queue source digest does not match the assigned brief");
|
|
482
|
+
const briefPath = relativePath(value.brief_path, "queue.brief_path");
|
|
483
|
+
const expectedBriefPath = path.join("teammates", identity.batchId, identity.assignmentId, "brief.json");
|
|
484
|
+
if (briefPath !== expectedBriefPath)
|
|
485
|
+
throw new Error("queue.brief_path is not the canonical work brief path");
|
|
486
|
+
if (value.status !== "queued")
|
|
487
|
+
throw new Error("queue.status must be queued");
|
|
488
|
+
if (!Number.isInteger(value.queue_index) || value.queue_index < 0)
|
|
489
|
+
throw new Error("queue.queue_index must be non-negative");
|
|
490
|
+
const queue = {
|
|
491
|
+
schemaVersion: 1,
|
|
492
|
+
...identity,
|
|
493
|
+
planHash,
|
|
494
|
+
briefDigest,
|
|
495
|
+
sourceDigest,
|
|
496
|
+
briefPath,
|
|
497
|
+
queueIndex: value.queue_index,
|
|
498
|
+
status: "queued",
|
|
499
|
+
};
|
|
500
|
+
rejectSecrets(queue, "teammate queue fact");
|
|
501
|
+
return queue;
|
|
502
|
+
}
|
|
503
|
+
export function validateTeammateClaim(input, identity, expectedPlanHash, expectedSourceDigest, expectedBriefDigest) {
|
|
504
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
505
|
+
throw new Error("teammate claim must be an object");
|
|
506
|
+
const value = input;
|
|
507
|
+
if (value.schema_version !== 1)
|
|
508
|
+
throw new Error("teammate claim schema_version must be 1");
|
|
509
|
+
for (const key of [
|
|
510
|
+
"task_id",
|
|
511
|
+
"batch_id",
|
|
512
|
+
"assignment_id",
|
|
513
|
+
"attempt_id",
|
|
514
|
+
"teammate_id",
|
|
515
|
+
]) {
|
|
516
|
+
const expected = identity[key === "task_id"
|
|
517
|
+
? "taskId"
|
|
518
|
+
: key === "batch_id"
|
|
519
|
+
? "batchId"
|
|
520
|
+
: key === "assignment_id"
|
|
521
|
+
? "assignmentId"
|
|
522
|
+
: key === "attempt_id"
|
|
523
|
+
? "attemptId"
|
|
524
|
+
: "teammateId"];
|
|
525
|
+
if (value[key] !== expected)
|
|
526
|
+
throw new Error(`claim.${key} does not match the assigned identity`);
|
|
527
|
+
}
|
|
528
|
+
const planHash = digest(value.plan_hash, "claim.plan_hash");
|
|
529
|
+
if (planHash !== expectedPlanHash)
|
|
530
|
+
throw new Error("claim plan hash does not match the batch plan");
|
|
531
|
+
const sourceDigest = digest(value.source_digest, "claim.source_digest");
|
|
532
|
+
if (sourceDigest !== expectedSourceDigest)
|
|
533
|
+
throw new Error("claim source digest does not match the assigned brief");
|
|
534
|
+
const briefDigest = value.brief_digest === undefined
|
|
535
|
+
? undefined
|
|
536
|
+
: digest(value.brief_digest, "claim.brief_digest");
|
|
537
|
+
if (expectedBriefDigest && briefDigest !== expectedBriefDigest)
|
|
538
|
+
throw new Error("claim brief digest does not match the assigned work");
|
|
539
|
+
if (value.status !== "issued")
|
|
540
|
+
throw new Error("claim.status must be issued");
|
|
541
|
+
if (!Number.isInteger(value.slot) || value.slot < 0)
|
|
542
|
+
throw new Error("claim.slot must be non-negative");
|
|
543
|
+
const snapshotKeys = [
|
|
544
|
+
"role_id",
|
|
545
|
+
"provider",
|
|
546
|
+
"profile",
|
|
547
|
+
"model",
|
|
548
|
+
"reasoning_effort",
|
|
549
|
+
"options",
|
|
550
|
+
"profile_digest",
|
|
551
|
+
"role_digest",
|
|
552
|
+
"context_digest",
|
|
553
|
+
"capability_digest",
|
|
554
|
+
];
|
|
555
|
+
const hasSnapshot = snapshotKeys.some((key) => value[key] !== undefined);
|
|
556
|
+
let execution;
|
|
557
|
+
if (hasSnapshot) {
|
|
558
|
+
if (value.role_id !== "teammate")
|
|
559
|
+
throw new Error("claim.role_id must be teammate");
|
|
560
|
+
const provider = value.provider;
|
|
561
|
+
if (provider !== "claude" &&
|
|
562
|
+
provider !== "codex" &&
|
|
563
|
+
provider !== "dsh" &&
|
|
564
|
+
provider !== "copilot") {
|
|
565
|
+
throw new Error("claim.provider is unsupported");
|
|
566
|
+
}
|
|
567
|
+
const profile = value.profile === undefined
|
|
568
|
+
? undefined
|
|
569
|
+
: id(value.profile, "claim.profile");
|
|
570
|
+
const model = value.model === undefined
|
|
571
|
+
? undefined
|
|
572
|
+
: boundedText(value.model, "claim.model");
|
|
573
|
+
if (model && (model.length > 256 || /[\r\n]/.test(model)))
|
|
574
|
+
throw new Error("claim.model must be bounded single-line text");
|
|
575
|
+
const reasoningEffort = value.reasoning_effort;
|
|
576
|
+
if (reasoningEffort !== undefined &&
|
|
577
|
+
reasoningEffort !== "medium" &&
|
|
578
|
+
reasoningEffort !== "high" &&
|
|
579
|
+
reasoningEffort !== "xhigh") {
|
|
580
|
+
throw new Error("claim.reasoning_effort is invalid");
|
|
581
|
+
}
|
|
582
|
+
if (!value.options ||
|
|
583
|
+
typeof value.options !== "object" ||
|
|
584
|
+
Array.isArray(value.options))
|
|
585
|
+
throw new Error("claim.options must be an object");
|
|
586
|
+
if (Object.keys(value.options).length > 32)
|
|
587
|
+
throw new Error("claim.options has too many entries");
|
|
588
|
+
const options = {};
|
|
589
|
+
for (const [key, option] of Object.entries(value.options)) {
|
|
590
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(key))
|
|
591
|
+
throw new Error("claim.options has an invalid key");
|
|
592
|
+
if (typeof option !== "string" &&
|
|
593
|
+
typeof option !== "number" &&
|
|
594
|
+
typeof option !== "boolean") {
|
|
595
|
+
throw new Error(`claim.options.${key} must be a scalar`);
|
|
596
|
+
}
|
|
597
|
+
if ((typeof option === "number" && !Number.isFinite(option)) ||
|
|
598
|
+
(typeof option === "string" &&
|
|
599
|
+
(option.trim() === "" ||
|
|
600
|
+
option.length > 256 ||
|
|
601
|
+
/[\r\n\0]/.test(option)))) {
|
|
602
|
+
throw new Error(`claim.options.${key} must be bounded scalar data`);
|
|
603
|
+
}
|
|
604
|
+
options[key] = option;
|
|
605
|
+
}
|
|
606
|
+
execution = {
|
|
607
|
+
roleId: "teammate",
|
|
608
|
+
provider,
|
|
609
|
+
...(profile ? { profile } : {}),
|
|
610
|
+
...(model ? { model } : {}),
|
|
611
|
+
...(reasoningEffort ? { reasoningEffort } : {}),
|
|
612
|
+
options,
|
|
613
|
+
...(value.profile_digest === undefined
|
|
614
|
+
? {}
|
|
615
|
+
: {
|
|
616
|
+
profileDigest: digest(value.profile_digest, "claim.profile_digest"),
|
|
617
|
+
}),
|
|
618
|
+
roleDigest: digest(value.role_digest, "claim.role_digest"),
|
|
619
|
+
contextDigest: digest(value.context_digest, "claim.context_digest"),
|
|
620
|
+
capabilityDigest: digest(value.capability_digest, "claim.capability_digest"),
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const claim = {
|
|
624
|
+
schemaVersion: 1,
|
|
625
|
+
...identity,
|
|
626
|
+
planHash,
|
|
627
|
+
...(briefDigest ? { briefDigest } : {}),
|
|
628
|
+
sourceDigest,
|
|
629
|
+
slot: value.slot,
|
|
630
|
+
status: "issued",
|
|
631
|
+
...(execution ? { execution } : {}),
|
|
632
|
+
};
|
|
633
|
+
rejectSecrets(claim, "teammate claim");
|
|
634
|
+
return claim;
|
|
635
|
+
}
|
|
636
|
+
export function validateTeammateDisposition(input, identity, expectedSourceDigest) {
|
|
637
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
638
|
+
throw new Error("teammate disposition must be an object");
|
|
639
|
+
const value = input;
|
|
640
|
+
if (value.schema_version !== 1)
|
|
641
|
+
throw new Error("teammate disposition schema_version must be 1");
|
|
642
|
+
for (const key of [
|
|
643
|
+
"task_id",
|
|
644
|
+
"batch_id",
|
|
645
|
+
"assignment_id",
|
|
646
|
+
"attempt_id",
|
|
647
|
+
"teammate_id",
|
|
648
|
+
]) {
|
|
649
|
+
const expected = identity[key === "task_id"
|
|
650
|
+
? "taskId"
|
|
651
|
+
: key === "batch_id"
|
|
652
|
+
? "batchId"
|
|
653
|
+
: key === "assignment_id"
|
|
654
|
+
? "assignmentId"
|
|
655
|
+
: key === "attempt_id"
|
|
656
|
+
? "attemptId"
|
|
657
|
+
: "teammateId"];
|
|
658
|
+
if (value[key] !== expected)
|
|
659
|
+
throw new Error(`disposition.${key} does not match the assigned identity`);
|
|
660
|
+
}
|
|
661
|
+
if (value.status !== "accepted" && value.status !== "final_failed") {
|
|
662
|
+
throw new Error("disposition.status is invalid");
|
|
663
|
+
}
|
|
664
|
+
const sourceDigest = digest(value.source_digest, "disposition.source_digest");
|
|
665
|
+
if (sourceDigest !== expectedSourceDigest)
|
|
666
|
+
throw new Error("disposition source digest does not match the assigned brief");
|
|
667
|
+
const reason = value.reason === undefined
|
|
668
|
+
? undefined
|
|
669
|
+
: boundedText(value.reason, "disposition.reason");
|
|
670
|
+
if (value.status === "final_failed" && reason === undefined)
|
|
671
|
+
throw new Error("final_failed disposition requires a reason");
|
|
672
|
+
return {
|
|
673
|
+
schemaVersion: 1,
|
|
674
|
+
...identity,
|
|
675
|
+
status: value.status,
|
|
676
|
+
sourceDigest,
|
|
677
|
+
...(reason ? { reason } : {}),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
export function writeTeammateClaim(input) {
|
|
681
|
+
const execution = input.launch
|
|
682
|
+
? {
|
|
683
|
+
role_id: input.launch.roleId,
|
|
684
|
+
provider: input.launch.provider,
|
|
685
|
+
...(input.launch.profile ? { profile: input.launch.profile } : {}),
|
|
686
|
+
...(input.launch.model ? { model: input.launch.model } : {}),
|
|
687
|
+
...(input.launch.reasoningEffort
|
|
688
|
+
? { reasoning_effort: input.launch.reasoningEffort }
|
|
689
|
+
: {}),
|
|
690
|
+
options: input.launch.options,
|
|
691
|
+
...(input.launch.profileDigest
|
|
692
|
+
? { profile_digest: input.launch.profileDigest }
|
|
693
|
+
: {}),
|
|
694
|
+
role_digest: input.launch.roleDigest,
|
|
695
|
+
context_digest: input.launch.contextDigest,
|
|
696
|
+
capability_digest: input.launch.capability.digest,
|
|
697
|
+
}
|
|
698
|
+
: undefined;
|
|
699
|
+
const claim = validateTeammateClaim({
|
|
700
|
+
schema_version: 1,
|
|
701
|
+
task_id: input.identity.taskId,
|
|
702
|
+
batch_id: input.identity.batchId,
|
|
703
|
+
assignment_id: input.identity.assignmentId,
|
|
704
|
+
attempt_id: input.identity.attemptId,
|
|
705
|
+
teammate_id: input.identity.teammateId,
|
|
706
|
+
plan_hash: input.planHash,
|
|
707
|
+
brief_digest: input.briefDigest,
|
|
708
|
+
source_digest: input.sourceDigest,
|
|
709
|
+
slot: input.slot,
|
|
710
|
+
status: "issued",
|
|
711
|
+
...(execution ?? {}),
|
|
712
|
+
}, input.identity, input.planHash, input.sourceDigest, input.briefDigest);
|
|
713
|
+
writeImmutableJson(attemptArtifactPath(input.taskRoot, input.briefPath, input.identity.attemptId, "claim.json"), {
|
|
714
|
+
schema_version: claim.schemaVersion,
|
|
715
|
+
task_id: claim.taskId,
|
|
716
|
+
batch_id: claim.batchId,
|
|
717
|
+
assignment_id: claim.assignmentId,
|
|
718
|
+
attempt_id: claim.attemptId,
|
|
719
|
+
teammate_id: claim.teammateId,
|
|
720
|
+
plan_hash: claim.planHash,
|
|
721
|
+
...(claim.briefDigest ? { brief_digest: claim.briefDigest } : {}),
|
|
722
|
+
source_digest: claim.sourceDigest,
|
|
723
|
+
slot: claim.slot,
|
|
724
|
+
status: claim.status,
|
|
725
|
+
...(claim.execution
|
|
726
|
+
? {
|
|
727
|
+
role_id: claim.execution.roleId,
|
|
728
|
+
provider: claim.execution.provider,
|
|
729
|
+
...(claim.execution.profile
|
|
730
|
+
? { profile: claim.execution.profile }
|
|
731
|
+
: {}),
|
|
732
|
+
...(claim.execution.model ? { model: claim.execution.model } : {}),
|
|
733
|
+
...(claim.execution.reasoningEffort
|
|
734
|
+
? { reasoning_effort: claim.execution.reasoningEffort }
|
|
735
|
+
: {}),
|
|
736
|
+
options: claim.execution.options,
|
|
737
|
+
...(claim.execution.profileDigest
|
|
738
|
+
? { profile_digest: claim.execution.profileDigest }
|
|
739
|
+
: {}),
|
|
740
|
+
role_digest: claim.execution.roleDigest,
|
|
741
|
+
context_digest: claim.execution.contextDigest,
|
|
742
|
+
capability_digest: claim.execution.capabilityDigest,
|
|
743
|
+
}
|
|
744
|
+
: {}),
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
export function writeTeammateQueueFact(input) {
|
|
748
|
+
const queue = validateTeammateQueueFact({
|
|
749
|
+
schema_version: 1,
|
|
750
|
+
task_id: input.taskId,
|
|
751
|
+
batch_id: input.batchId,
|
|
752
|
+
assignment_id: input.assignmentId,
|
|
753
|
+
plan_hash: input.planHash,
|
|
754
|
+
brief_digest: input.briefDigest,
|
|
755
|
+
source_digest: input.sourceDigest,
|
|
756
|
+
brief_path: input.briefPath,
|
|
757
|
+
queue_index: input.queueIndex,
|
|
758
|
+
status: "queued",
|
|
759
|
+
}, {
|
|
760
|
+
taskId: input.taskId,
|
|
761
|
+
batchId: input.batchId,
|
|
762
|
+
assignmentId: input.assignmentId,
|
|
763
|
+
}, input.planHash, input.briefDigest, input.sourceDigest);
|
|
764
|
+
writeImmutableJson(safePath(input.taskRoot, path.join(path.dirname(input.briefPath), "queue.json")), {
|
|
765
|
+
schema_version: queue.schemaVersion,
|
|
766
|
+
task_id: queue.taskId,
|
|
767
|
+
batch_id: queue.batchId,
|
|
768
|
+
assignment_id: queue.assignmentId,
|
|
769
|
+
plan_hash: queue.planHash,
|
|
770
|
+
brief_digest: queue.briefDigest,
|
|
771
|
+
source_digest: queue.sourceDigest,
|
|
772
|
+
brief_path: queue.briefPath,
|
|
773
|
+
queue_index: queue.queueIndex,
|
|
774
|
+
status: queue.status,
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
export function appendTeammateWorklog(input) {
|
|
778
|
+
const checkpoint = boundedText(input.checkpoint, "worklog.checkpoint");
|
|
779
|
+
rejectSecrets(checkpoint, "worklog.checkpoint");
|
|
780
|
+
const filePath = attemptArtifactPath(input.taskRoot, input.briefPath, input.attemptId, "worklog.md");
|
|
781
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
782
|
+
const fd = fs.openSync(filePath, "a", 0o600);
|
|
783
|
+
try {
|
|
784
|
+
fs.writeFileSync(fd, `${checkpoint}\n`, "utf8");
|
|
785
|
+
fs.fsyncSync(fd);
|
|
786
|
+
}
|
|
787
|
+
finally {
|
|
788
|
+
fs.closeSync(fd);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
export function writeTeammateReport(input) {
|
|
792
|
+
const report = validateTeammateReport({
|
|
793
|
+
schema_version: 1,
|
|
794
|
+
task_id: input.identity.taskId,
|
|
795
|
+
batch_id: input.identity.batchId,
|
|
796
|
+
assignment_id: input.identity.assignmentId,
|
|
797
|
+
attempt_id: input.identity.attemptId,
|
|
798
|
+
teammate_id: input.identity.teammateId,
|
|
799
|
+
plan_hash: input.identity.planHash,
|
|
800
|
+
phase_id: input.identity.phaseId,
|
|
801
|
+
task_route_revision: input.identity.taskRouteRevision,
|
|
802
|
+
input_revision: input.identity.inputRevision,
|
|
803
|
+
status: input.status,
|
|
804
|
+
source_digest: input.sourceDigest,
|
|
805
|
+
evidence: input.evidence,
|
|
806
|
+
}, input.identity, input.sourceDigest);
|
|
807
|
+
writeImmutableJson(attemptArtifactPath(input.taskRoot, input.briefPath, input.identity.attemptId, "report.json"), {
|
|
808
|
+
schema_version: report.schemaVersion,
|
|
809
|
+
task_id: report.taskId,
|
|
810
|
+
batch_id: report.batchId,
|
|
811
|
+
assignment_id: report.assignmentId,
|
|
812
|
+
attempt_id: report.attemptId,
|
|
813
|
+
teammate_id: report.teammateId,
|
|
814
|
+
plan_hash: report.planHash,
|
|
815
|
+
phase_id: report.phaseId,
|
|
816
|
+
task_route_revision: report.taskRouteRevision,
|
|
817
|
+
input_revision: report.inputRevision,
|
|
818
|
+
status: report.status,
|
|
819
|
+
source_digest: report.sourceDigest,
|
|
820
|
+
evidence: report.evidence,
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
export function assertTeammateWriteAllowed(target, protectedTargets) {
|
|
824
|
+
const normalizedTarget = path.normalize(target);
|
|
825
|
+
for (const protectedTarget of protectedTargets) {
|
|
826
|
+
const normalizedProtected = path.normalize(protectedTarget);
|
|
827
|
+
if (normalizedTarget === normalizedProtected ||
|
|
828
|
+
normalizedTarget.startsWith(`${normalizedProtected}${path.sep}`)) {
|
|
829
|
+
throw new Error(`teammate write targets protected path: ${target}`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
export function writeTeammateDisposition(input) {
|
|
834
|
+
const disposition = validateTeammateDisposition({
|
|
835
|
+
schema_version: 1,
|
|
836
|
+
task_id: input.identity.taskId,
|
|
837
|
+
batch_id: input.identity.batchId,
|
|
838
|
+
assignment_id: input.identity.assignmentId,
|
|
839
|
+
attempt_id: input.identity.attemptId,
|
|
840
|
+
teammate_id: input.identity.teammateId,
|
|
841
|
+
status: input.status,
|
|
842
|
+
source_digest: input.sourceDigest,
|
|
843
|
+
...(input.reason ? { reason: input.reason } : {}),
|
|
844
|
+
}, input.identity, input.sourceDigest);
|
|
845
|
+
writeImmutableJson(attemptArtifactPath(input.taskRoot, input.briefPath, input.identity.attemptId, "disposition.json"), {
|
|
846
|
+
schema_version: disposition.schemaVersion,
|
|
847
|
+
task_id: disposition.taskId,
|
|
848
|
+
batch_id: disposition.batchId,
|
|
849
|
+
assignment_id: disposition.assignmentId,
|
|
850
|
+
attempt_id: disposition.attemptId,
|
|
851
|
+
teammate_id: disposition.teammateId,
|
|
852
|
+
status: disposition.status,
|
|
853
|
+
source_digest: disposition.sourceDigest,
|
|
854
|
+
...(disposition.reason ? { reason: disposition.reason } : {}),
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
export function readTeammateReport(input) {
|
|
858
|
+
if (!ID.test(input.attemptId))
|
|
859
|
+
throw new Error("attempt id must be safe");
|
|
860
|
+
const reportPath = safePath(input.taskRoot, path.join(path.dirname(input.briefPath), "attempts", input.attemptId, "report.json"));
|
|
861
|
+
const stat = fs.lstatSync(reportPath);
|
|
862
|
+
if (!stat.isFile() || stat.size > 128 * 1024)
|
|
863
|
+
throw new Error("teammate report must be a regular file under 131072 bytes");
|
|
864
|
+
const raw = fs.readFileSync(reportPath, "utf8");
|
|
865
|
+
const report = JSON.parse(raw);
|
|
866
|
+
rejectSecrets(report, "teammate report");
|
|
867
|
+
return validateTeammateReport(report, { ...input.identity, attemptId: input.attemptId }, input.sourceDigest);
|
|
868
|
+
}
|
|
869
|
+
export function cleanupSettledTeamworkArtifacts(input) {
|
|
870
|
+
const failures = [];
|
|
871
|
+
for (const assignment of input.assignments) {
|
|
872
|
+
try {
|
|
873
|
+
const assignmentDir = safePath(input.taskRoot, path.dirname(assignment.briefPath));
|
|
874
|
+
const queueFile = safePath(input.taskRoot, path.join(assignmentDir, "queue.json"));
|
|
875
|
+
if (fs.existsSync(queueFile))
|
|
876
|
+
fs.unlinkSync(queueFile);
|
|
877
|
+
const attemptsDir = safePath(input.taskRoot, path.join(assignmentDir, "attempts"));
|
|
878
|
+
if (!fs.existsSync(attemptsDir))
|
|
879
|
+
continue;
|
|
880
|
+
for (const attemptId of fs.readdirSync(attemptsDir)) {
|
|
881
|
+
if (!ID.test(attemptId))
|
|
882
|
+
continue;
|
|
883
|
+
const attemptDir = safePath(input.taskRoot, path.join(attemptsDir, attemptId));
|
|
884
|
+
const keepReport = input.acceptedAttempts.get(assignment.assignmentId) === attemptId;
|
|
885
|
+
const transientFiles = [
|
|
886
|
+
"claim.json",
|
|
887
|
+
"disposition.json",
|
|
888
|
+
"worklog.md",
|
|
889
|
+
...(keepReport ? [] : ["report.json"]),
|
|
890
|
+
];
|
|
891
|
+
for (const fileName of transientFiles) {
|
|
892
|
+
const filePath = safePath(input.taskRoot, path.join(attemptDir, fileName));
|
|
893
|
+
if (fs.existsSync(filePath))
|
|
894
|
+
fs.unlinkSync(filePath);
|
|
895
|
+
}
|
|
896
|
+
if (fs.readdirSync(attemptDir).length === 0)
|
|
897
|
+
fs.rmdirSync(attemptDir);
|
|
898
|
+
}
|
|
899
|
+
if (fs.readdirSync(attemptsDir).length === 0)
|
|
900
|
+
fs.rmdirSync(attemptsDir);
|
|
901
|
+
}
|
|
902
|
+
catch (error) {
|
|
903
|
+
failures.push(`${assignment.assignmentId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return failures;
|
|
907
|
+
}
|
|
908
|
+
//# sourceMappingURL=artifact.js.map
|