@pennixrv/trellis 0.7.1 → 0.7.2-beta.2
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 +10 -7
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/ablate.d.ts.map +1 -1
- package/dist/commands/ablate.js +2 -0
- package/dist/commands/ablate.js.map +1 -1
- package/dist/commands/backup.d.ts +4 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +75 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +705 -681
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +49 -7
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/task-progress.d.ts +1 -0
- package/dist/commands/task-progress.d.ts.map +1 -1
- package/dist/commands/task-progress.js +67 -25
- package/dist/commands/task-progress.js.map +1 -1
- package/dist/commands/teamwork/adapters/acp.d.ts +20 -0
- package/dist/commands/teamwork/adapters/acp.d.ts.map +1 -0
- package/dist/commands/teamwork/adapters/acp.js +268 -0
- package/dist/commands/teamwork/adapters/acp.js.map +1 -0
- package/dist/commands/teamwork/adapters/claude.d.ts +1 -0
- package/dist/commands/teamwork/adapters/claude.d.ts.map +1 -1
- package/dist/commands/teamwork/adapters/claude.js +5 -0
- package/dist/commands/teamwork/adapters/claude.js.map +1 -1
- package/dist/commands/teamwork/adapters/codex.d.ts +3 -2
- package/dist/commands/teamwork/adapters/codex.d.ts.map +1 -1
- package/dist/commands/teamwork/adapters/codex.js +14 -15
- package/dist/commands/teamwork/adapters/codex.js.map +1 -1
- package/dist/commands/teamwork/adapters/index.d.ts +23 -1
- package/dist/commands/teamwork/adapters/index.d.ts.map +1 -1
- package/dist/commands/teamwork/adapters/index.js +163 -24
- package/dist/commands/teamwork/adapters/index.js.map +1 -1
- package/dist/commands/teamwork/agent-loader.d.ts +2 -1
- package/dist/commands/teamwork/agent-loader.d.ts.map +1 -1
- package/dist/commands/teamwork/agent-loader.js +2 -1
- package/dist/commands/teamwork/agent-loader.js.map +1 -1
- package/dist/commands/teamwork/artifact.d.ts +42 -14
- package/dist/commands/teamwork/artifact.d.ts.map +1 -1
- package/dist/commands/teamwork/artifact.js +188 -85
- package/dist/commands/teamwork/artifact.js.map +1 -1
- package/dist/commands/teamwork/batch-reservation.d.ts +16 -0
- package/dist/commands/teamwork/batch-reservation.d.ts.map +1 -0
- package/dist/commands/teamwork/batch-reservation.js +43 -0
- package/dist/commands/teamwork/batch-reservation.js.map +1 -0
- package/dist/commands/teamwork/batch.d.ts +23 -0
- package/dist/commands/teamwork/batch.d.ts.map +1 -0
- package/dist/commands/teamwork/batch.js +415 -0
- package/dist/commands/teamwork/batch.js.map +1 -0
- package/dist/commands/teamwork/context-loader.d.ts +1 -1
- package/dist/commands/teamwork/context-loader.d.ts.map +1 -1
- package/dist/commands/teamwork/context-loader.js +19 -6
- package/dist/commands/teamwork/context-loader.js.map +1 -1
- package/dist/commands/teamwork/coordination.d.ts +88 -0
- package/dist/commands/teamwork/coordination.d.ts.map +1 -0
- package/dist/commands/teamwork/coordination.js +153 -0
- package/dist/commands/teamwork/coordination.js.map +1 -0
- package/dist/commands/teamwork/coordinator.d.ts +33 -6
- package/dist/commands/teamwork/coordinator.d.ts.map +1 -1
- package/dist/commands/teamwork/coordinator.js +948 -342
- package/dist/commands/teamwork/coordinator.js.map +1 -1
- package/dist/commands/teamwork/create.d.ts +1 -0
- package/dist/commands/teamwork/create.d.ts.map +1 -1
- package/dist/commands/teamwork/create.js +2 -1
- package/dist/commands/teamwork/create.js.map +1 -1
- package/dist/commands/teamwork/dev-parse-trace.d.ts +2 -1
- package/dist/commands/teamwork/dev-parse-trace.d.ts.map +1 -1
- package/dist/commands/teamwork/dev-parse-trace.js +2 -0
- package/dist/commands/teamwork/dev-parse-trace.js.map +1 -1
- package/dist/commands/teamwork/foreground-wait.d.ts +6 -0
- package/dist/commands/teamwork/foreground-wait.d.ts.map +1 -0
- package/dist/commands/teamwork/foreground-wait.js +65 -0
- package/dist/commands/teamwork/foreground-wait.js.map +1 -0
- package/dist/commands/teamwork/guard.d.ts +2 -2
- package/dist/commands/teamwork/guard.d.ts.map +1 -1
- package/dist/commands/teamwork/guard.js +25 -7
- package/dist/commands/teamwork/guard.js.map +1 -1
- package/dist/commands/teamwork/index.d.ts.map +1 -1
- package/dist/commands/teamwork/index.js +163 -10
- package/dist/commands/teamwork/index.js.map +1 -1
- package/dist/commands/teamwork/kill.d.ts +2 -0
- package/dist/commands/teamwork/kill.d.ts.map +1 -1
- package/dist/commands/teamwork/kill.js +79 -38
- package/dist/commands/teamwork/kill.js.map +1 -1
- package/dist/commands/teamwork/messages.d.ts.map +1 -1
- package/dist/commands/teamwork/messages.js +3 -0
- package/dist/commands/teamwork/messages.js.map +1 -1
- package/dist/commands/teamwork/plan.d.ts +22 -0
- package/dist/commands/teamwork/plan.d.ts.map +1 -0
- package/dist/commands/teamwork/plan.js +288 -0
- package/dist/commands/teamwork/plan.js.map +1 -0
- package/dist/commands/teamwork/process-identity.d.ts +3 -0
- package/dist/commands/teamwork/process-identity.d.ts.map +1 -0
- package/dist/commands/teamwork/process-identity.js +24 -0
- package/dist/commands/teamwork/process-identity.js.map +1 -0
- package/dist/commands/teamwork/profiles.d.ts +43 -6
- package/dist/commands/teamwork/profiles.d.ts.map +1 -1
- package/dist/commands/teamwork/profiles.js +138 -110
- package/dist/commands/teamwork/profiles.js.map +1 -1
- package/dist/commands/teamwork/prompt.d.ts +4 -0
- package/dist/commands/teamwork/prompt.d.ts.map +1 -0
- package/dist/commands/teamwork/prompt.js +33 -0
- package/dist/commands/teamwork/prompt.js.map +1 -0
- package/dist/commands/teamwork/result.d.ts +55 -0
- package/dist/commands/teamwork/result.d.ts.map +1 -0
- package/dist/commands/teamwork/result.js +94 -0
- package/dist/commands/teamwork/result.js.map +1 -0
- package/dist/commands/teamwork/rm.d.ts.map +1 -1
- package/dist/commands/teamwork/rm.js +64 -64
- package/dist/commands/teamwork/rm.js.map +1 -1
- package/dist/commands/teamwork/run.d.ts +5 -0
- package/dist/commands/teamwork/run.d.ts.map +1 -1
- package/dist/commands/teamwork/run.js +120 -24
- package/dist/commands/teamwork/run.js.map +1 -1
- package/dist/commands/teamwork/runtime.d.ts +9 -0
- package/dist/commands/teamwork/runtime.d.ts.map +1 -0
- package/dist/commands/teamwork/runtime.js +401 -0
- package/dist/commands/teamwork/runtime.js.map +1 -0
- package/dist/commands/teamwork/send.d.ts +1 -0
- package/dist/commands/teamwork/send.d.ts.map +1 -1
- package/dist/commands/teamwork/send.js +2 -1
- package/dist/commands/teamwork/send.js.map +1 -1
- package/dist/commands/teamwork/spawn.d.ts +10 -3
- package/dist/commands/teamwork/spawn.d.ts.map +1 -1
- package/dist/commands/teamwork/spawn.js +42 -27
- package/dist/commands/teamwork/spawn.js.map +1 -1
- package/dist/commands/teamwork/store/lock.js +28 -4
- package/dist/commands/teamwork/store/lock.js.map +1 -1
- package/dist/commands/teamwork/supervisor/inbox.d.ts +1 -0
- package/dist/commands/teamwork/supervisor/inbox.d.ts.map +1 -1
- package/dist/commands/teamwork/supervisor/inbox.js +28 -9
- package/dist/commands/teamwork/supervisor/inbox.js.map +1 -1
- package/dist/commands/teamwork/supervisor/stdout.d.ts.map +1 -1
- package/dist/commands/teamwork/supervisor/stdout.js +2 -3
- package/dist/commands/teamwork/supervisor/stdout.js.map +1 -1
- package/dist/commands/teamwork/supervisor/warning.d.ts.map +1 -1
- package/dist/commands/teamwork/supervisor/warning.js +2 -3
- package/dist/commands/teamwork/supervisor/warning.js.map +1 -1
- package/dist/commands/teamwork/supervisor.d.ts +5 -0
- package/dist/commands/teamwork/supervisor.d.ts.map +1 -1
- package/dist/commands/teamwork/supervisor.js +21 -12
- package/dist/commands/teamwork/supervisor.js.map +1 -1
- package/dist/commands/teamwork/teammate-adapters.d.ts +0 -5
- package/dist/commands/teamwork/teammate-adapters.d.ts.map +1 -1
- package/dist/commands/teamwork/teammate-adapters.js +9 -56
- package/dist/commands/teamwork/teammate-adapters.js.map +1 -1
- package/dist/commands/teamwork/teammate-config.d.ts +5 -1
- package/dist/commands/teamwork/teammate-config.d.ts.map +1 -1
- package/dist/commands/teamwork/teammate-config.js +62 -19
- package/dist/commands/teamwork/teammate-config.js.map +1 -1
- package/dist/commands/teamwork/teammate-launch.d.ts +15 -6
- package/dist/commands/teamwork/teammate-launch.d.ts.map +1 -1
- package/dist/commands/teamwork/teammate-launch.js +129 -22
- package/dist/commands/teamwork/teammate-launch.js.map +1 -1
- package/dist/commands/teamwork/wait.d.ts +2 -0
- package/dist/commands/teamwork/wait.d.ts.map +1 -1
- package/dist/commands/teamwork/wait.js +44 -3
- package/dist/commands/teamwork/wait.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +92 -84
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +635 -592
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +94 -89
- package/dist/commands/workflow.js.map +1 -1
- package/dist/configurators/shared.js +2 -2
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +14 -32
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +3 -10
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -13
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.7.1-beta.0.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.1.json +186 -0
- package/dist/migrations/manifests/0.7.1-beta.10.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.11.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.12.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.13.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.14.json +11 -0
- package/dist/migrations/manifests/0.7.1-beta.15.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.16.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.17.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.18.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.19.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.2.json +522 -0
- package/dist/migrations/manifests/0.7.1-beta.3.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.4.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.5.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.6.json +10 -0
- package/dist/migrations/manifests/0.7.1-beta.7.json +8 -0
- package/dist/migrations/manifests/0.7.1-beta.8.json +481 -0
- package/dist/migrations/manifests/0.7.1-beta.9.json +191 -0
- package/dist/migrations/manifests/0.7.2-beta.0.json +11 -0
- package/dist/migrations/manifests/0.7.2-beta.1.json +11 -0
- package/dist/migrations/manifests/0.7.2-beta.2.json +11 -0
- package/dist/templates/claude/hooks/statusline.py +9 -4
- package/dist/templates/codex/hooks/session-start.py +14 -11
- package/dist/templates/codex/hooks.json +10 -0
- package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +2 -0
- package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +2 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +3 -2
- 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/context-injection.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-teamwork.md +14 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -2
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +8 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +4 -2
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-task-implementation/workflow.md +6 -0
- package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +18 -3
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +34 -3
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +37 -26
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +128 -14
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +152 -20
- package/dist/templates/common/commands/start.md +2 -2
- package/dist/templates/common/skills/before-dev.md +1 -4
- package/dist/templates/common/skills/break-loop.md +7 -18
- package/dist/templates/common/skills/check.md +7 -11
- package/dist/templates/common/skills/{brainstorm.md → request-intake.md} +5 -3
- package/dist/templates/common/skills/update-spec.md +5 -7
- package/dist/templates/copilot/hooks/session-start.py +14 -11
- package/dist/templates/copilot/index.d.ts +1 -6
- package/dist/templates/copilot/index.d.ts.map +1 -1
- package/dist/templates/copilot/index.js +1 -14
- package/dist/templates/copilot/index.js.map +1 -1
- package/dist/templates/extract.js +4 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/markdown/spec/backend/index.md.txt +4 -35
- package/dist/templates/markdown/spec/frontend/index.md.txt +4 -36
- package/dist/templates/omp/extensions/trellis/index.ts.txt +2 -2
- package/dist/templates/opencode/lib/session-utils.js +301 -209
- package/dist/templates/pi/extensions/trellis/index.ts.txt +17 -1
- package/dist/templates/shared-hooks/guard-teamwork-wait.py +58 -0
- package/dist/templates/shared-hooks/index.d.ts +1 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +1 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-workflow-state.py +8 -3
- package/dist/templates/shared-hooks/session-start.py +16 -12
- package/dist/templates/snow/SNOW.md +1 -1
- package/dist/templates/trellis/agents/delegated-worktree-primary-agent.md +9 -0
- package/dist/templates/trellis/agents/profiles/architecture.md +9 -0
- package/dist/templates/trellis/agents/profiles/diagnosis.md +9 -0
- package/dist/templates/trellis/agents/profiles/exploration.md +9 -0
- package/dist/templates/trellis/agents/profiles/research.md +9 -0
- package/dist/templates/trellis/agents/profiles/review.md +9 -0
- package/dist/templates/trellis/agents/profiles/verification.md +9 -0
- package/dist/templates/trellis/agents/teammate-profiles.json +56 -4
- package/dist/templates/trellis/agents/teammate.md +4 -52
- package/dist/templates/trellis/config.yaml +2 -5
- package/dist/templates/trellis/index.d.ts +4 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +22 -4
- 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/active_task.py +3 -47
- package/dist/templates/trellis/scripts/common/config.py +5 -0
- package/dist/templates/trellis/scripts/common/paths.py +9 -25
- package/dist/templates/trellis/scripts/common/safe_commit.py +8 -8
- package/dist/templates/trellis/scripts/common/task_context.py +11 -51
- package/dist/templates/trellis/scripts/common/task_continuity.py +191 -60
- package/dist/templates/trellis/scripts/common/task_layout.py +80 -0
- package/dist/templates/trellis/scripts/common/task_move.py +345 -0
- package/dist/templates/trellis/scripts/common/task_store.py +51 -57
- package/dist/templates/trellis/scripts/common/task_utils.py +23 -19
- package/dist/templates/trellis/scripts/common/task_workflow.py +395 -49
- package/dist/templates/trellis/scripts/common/tasks.py +6 -5
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +13 -3
- package/dist/templates/trellis/scripts/task.py +76 -24
- package/dist/templates/trellis/teamwork.json +10 -0
- package/dist/templates/trellis/workflow.md +11 -2
- package/dist/utils/ablation-store.d.ts +1 -1
- package/dist/utils/ablation-store.d.ts.map +1 -1
- package/dist/utils/ablation-store.js +58 -24
- package/dist/utils/ablation-store.js.map +1 -1
- package/dist/utils/asset-backup.d.ts +138 -0
- package/dist/utils/asset-backup.d.ts.map +1 -0
- package/dist/utils/asset-backup.js +525 -0
- package/dist/utils/asset-backup.js.map +1 -0
- package/dist/utils/atomic-write.d.ts +1 -1
- package/dist/utils/atomic-write.d.ts.map +1 -1
- package/dist/utils/atomic-write.js +6 -3
- package/dist/utils/atomic-write.js.map +1 -1
- package/dist/utils/manifest-prune.d.ts +4 -1
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +17 -6
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +4 -19
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +9 -45
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts +5 -4
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +24 -17
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +3 -2
- package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -13
- package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -170
- package/dist/templates/copilot/prompts/check.prompt.md +0 -110
- package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
- package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -22
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
- package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
- package/dist/templates/copilot/prompts/record-session.prompt.md +0 -58
- package/dist/templates/copilot/prompts/start.prompt.md +0 -65
- package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { parseFrozenTeammate, } from "./teammate-launch.js";
|
|
5
|
+
import { assertCoordinationMembers, } from "./coordination.js";
|
|
6
|
+
import { REASONING_EFFORTS } from "./profiles.js";
|
|
7
|
+
import { teammateContentSchema } from "./result.js";
|
|
4
8
|
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
|
|
9
|
+
const SECRET = /(-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|gh[pousr])_[A-Za-z0-9]{20,}\b|\bBearer\s+(?!<(?:redacted|token|placeholder)>(?:\s|[.,;:!?)]|$))\S+)/i;
|
|
6
10
|
function id(value, field) {
|
|
7
11
|
if (typeof value !== "string" || !ID.test(value))
|
|
8
12
|
throw new Error(`${field} must be a safe id`);
|
|
@@ -45,7 +49,7 @@ function relativePathList(value, field) {
|
|
|
45
49
|
throw new Error(`${field} must not contain duplicates`);
|
|
46
50
|
return result;
|
|
47
51
|
}
|
|
48
|
-
function safePath(root, candidate) {
|
|
52
|
+
export function safePath(root, candidate) {
|
|
49
53
|
const absoluteRoot = path.resolve(root);
|
|
50
54
|
const lexical = path.resolve(root, candidate);
|
|
51
55
|
const relative = path.relative(absoluteRoot, lexical);
|
|
@@ -70,7 +74,7 @@ function attemptArtifactPath(taskRoot, briefPath, attemptId, fileName) {
|
|
|
70
74
|
throw new Error("attempt id must be safe");
|
|
71
75
|
return safePath(taskRoot, path.join(path.dirname(briefPath), "attempts", attemptId, fileName));
|
|
72
76
|
}
|
|
73
|
-
function writeImmutableJson(filePath, value) {
|
|
77
|
+
function writeImmutableJson(filePath, value, idempotent = false) {
|
|
74
78
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
75
79
|
const temporary = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`);
|
|
76
80
|
try {
|
|
@@ -88,6 +92,10 @@ function writeImmutableJson(filePath, value) {
|
|
|
88
92
|
}
|
|
89
93
|
catch (error) {
|
|
90
94
|
if (error.code === "EEXIST") {
|
|
95
|
+
if (idempotent &&
|
|
96
|
+
fs.lstatSync(filePath).isFile() &&
|
|
97
|
+
fs.readFileSync(filePath, "utf8") === JSON.stringify(value) + "\n")
|
|
98
|
+
return;
|
|
91
99
|
throw new Error(`artifact already exists: ${filePath}`);
|
|
92
100
|
}
|
|
93
101
|
throw error;
|
|
@@ -100,9 +108,7 @@ function canonicalJson(value) {
|
|
|
100
108
|
if (Array.isArray(value))
|
|
101
109
|
return `[${value.map(canonicalJson).join(",")}]`;
|
|
102
110
|
if (value && typeof value === "object") {
|
|
103
|
-
const entries = Object.entries(value)
|
|
104
|
-
.filter(([key]) => key !== "plan_hash")
|
|
105
|
-
.sort(([a], [b]) => a.localeCompare(b));
|
|
111
|
+
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
106
112
|
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
107
113
|
}
|
|
108
114
|
return JSON.stringify(value);
|
|
@@ -111,14 +117,20 @@ function withoutFields(value, fields) {
|
|
|
111
117
|
return Object.fromEntries(Object.entries(value).filter(([field]) => !fields.includes(field)));
|
|
112
118
|
}
|
|
113
119
|
export function computeTeamworkPlanHash(value) {
|
|
114
|
-
|
|
120
|
+
const contents = value && typeof value === "object" && !Array.isArray(value)
|
|
121
|
+
? withoutFields(value, ["plan_hash"])
|
|
122
|
+
: value;
|
|
123
|
+
return crypto
|
|
124
|
+
.createHash("sha256")
|
|
125
|
+
.update(canonicalJson(contents))
|
|
126
|
+
.digest("hex");
|
|
115
127
|
}
|
|
116
|
-
export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
128
|
+
export function validateTeamworkPlan(input, taskRoot, expectedContext, options = {}) {
|
|
117
129
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
118
130
|
throw new Error("Teamwork plan must be an object");
|
|
119
131
|
const value = input;
|
|
120
|
-
if (value.schema_version !==
|
|
121
|
-
throw new Error("Teamwork plan schema_version must be
|
|
132
|
+
if (value.schema_version !== 2)
|
|
133
|
+
throw new Error("Teamwork plan schema_version must be 2; construct a new frozen plan");
|
|
122
134
|
const taskId = id(value.task_id, "task_id");
|
|
123
135
|
const batchId = id(value.batch_id, "batch_id");
|
|
124
136
|
const sessionId = id(value.session_id, "session_id");
|
|
@@ -126,6 +138,7 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
126
138
|
const taskRouteRevision = boundedText(value.task_route_revision, "task_route_revision");
|
|
127
139
|
const inputRevision = boundedText(value.input_revision, "input_revision");
|
|
128
140
|
const planSummary = boundedText(value.plan_summary, "plan_summary");
|
|
141
|
+
const coordinationDigest = digest(value.coordination_digest, "coordination_digest");
|
|
129
142
|
const coordinationMode = value.coordination_mode;
|
|
130
143
|
if (coordinationMode !== "general" &&
|
|
131
144
|
coordinationMode !== "advisor" &&
|
|
@@ -147,13 +160,13 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
147
160
|
const deadlineAt = Date.parse(deadline);
|
|
148
161
|
if (Number.isNaN(deadlineAt))
|
|
149
162
|
throw new Error("deadline must be an ISO date");
|
|
150
|
-
if (deadlineAt <= Date.now())
|
|
163
|
+
if (!options.allowExpired && deadlineAt <= Date.now())
|
|
151
164
|
throw new Error("deadline must be in the future");
|
|
152
165
|
const assignmentValue = value.assignments;
|
|
153
166
|
if (!Array.isArray(assignmentValue) || assignmentValue.length === 0)
|
|
154
167
|
throw new Error("Teamwork plan assignments must be non-empty");
|
|
155
|
-
if (coordinationMode === "advisor" && assignmentValue.length
|
|
156
|
-
throw new Error("advisor mode requires
|
|
168
|
+
if (coordinationMode === "advisor" && assignmentValue.length > 3)
|
|
169
|
+
throw new Error("advisor mode requires one to three assignments");
|
|
157
170
|
if (coordinationMode === "committee" &&
|
|
158
171
|
(assignmentValue.length < 2 || assignmentValue.length > 3))
|
|
159
172
|
throw new Error("committee mode requires two or three assignments");
|
|
@@ -188,8 +201,10 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
188
201
|
else if (coordinationMode === "committee") {
|
|
189
202
|
throw new Error(`assignments[${index}] must declare an evaluation group`);
|
|
190
203
|
}
|
|
191
|
-
if (item.role_id !==
|
|
192
|
-
throw new Error(`assignments[${index}].role_id
|
|
204
|
+
if (item.role_id !== undefined)
|
|
205
|
+
throw new Error(`assignments[${index}].role_id is retired; teammate is an internal execution type`);
|
|
206
|
+
const execution = parseFrozenTeammate(item.execution);
|
|
207
|
+
const selectionReason = boundedText(item.selection_reason, `assignments[${index}].selection_reason`);
|
|
193
208
|
const provider = item.provider;
|
|
194
209
|
if (provider !== "claude" &&
|
|
195
210
|
provider !== "codex" &&
|
|
@@ -205,7 +220,9 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
205
220
|
throw new Error(`assignments[${index}].max_retries must be non-negative`);
|
|
206
221
|
const briefPath = boundedText(item.brief_path, `assignments[${index}].brief_path`);
|
|
207
222
|
const scope = boundedText(item.scope, `assignments[${index}].scope`);
|
|
208
|
-
const lens =
|
|
223
|
+
const lens = item.lens === undefined
|
|
224
|
+
? undefined
|
|
225
|
+
: boundedText(item.lens, `assignments[${index}].lens`);
|
|
209
226
|
const summary = boundedText(item.summary, `assignments[${index}].summary`);
|
|
210
227
|
const briefDigest = digest(item.brief_digest, `assignments[${index}].brief_digest`);
|
|
211
228
|
const sourceDigest = digest(item.source_digest, `assignments[${index}].source_digest`);
|
|
@@ -222,22 +239,31 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
222
239
|
if (path.relative(path.resolve(taskRoot), absoluteBrief) !==
|
|
223
240
|
expectedRelative)
|
|
224
241
|
throw new Error(`assignments[${index}].brief_path must be ${expectedRelative}`);
|
|
225
|
-
const
|
|
242
|
+
const prepared = options.briefContents?.get(briefPath);
|
|
243
|
+
if (options.briefContents && prepared === undefined)
|
|
244
|
+
throw new Error(`missing prepared brief: ${briefPath}`);
|
|
245
|
+
const briefBytes = prepared === undefined
|
|
246
|
+
? fs.readFileSync(absoluteBrief)
|
|
247
|
+
: Buffer.from(prepared);
|
|
226
248
|
if (briefBytes.byteLength > 64 * 1024)
|
|
227
249
|
throw new Error(`brief for assignment '${assignmentId}' exceeds 65536 bytes`);
|
|
228
250
|
const brief = JSON.parse(briefBytes.toString("utf8"));
|
|
229
251
|
if (!brief || typeof brief !== "object" || Array.isArray(brief))
|
|
230
252
|
throw new Error(`brief for assignment '${assignmentId}' must be an object`);
|
|
253
|
+
if (typeof brief.input !== "string" ||
|
|
254
|
+
brief.input.trim() === "" ||
|
|
255
|
+
brief.input.length > 32768 ||
|
|
256
|
+
brief.input.includes("\0"))
|
|
257
|
+
throw new Error("brief.input must be nonempty task text of at most 32768 characters");
|
|
231
258
|
briefs.set(assignmentId, brief);
|
|
232
259
|
rejectSecrets(brief, `brief for assignment '${assignmentId}'`);
|
|
233
260
|
if (computeTeamworkPlanHash(brief) !== briefDigest)
|
|
234
261
|
throw new Error(`brief.${assignmentId} digest does not match the Teamwork plan`);
|
|
235
262
|
for (const [field, expected] of Object.entries({
|
|
236
|
-
schema_version:
|
|
263
|
+
schema_version: 2,
|
|
237
264
|
task_id: taskId,
|
|
238
265
|
batch_id: batchId,
|
|
239
266
|
assignment_id: assignmentId,
|
|
240
|
-
role_id: "teammate",
|
|
241
267
|
provider,
|
|
242
268
|
summary,
|
|
243
269
|
scope,
|
|
@@ -247,6 +273,16 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
247
273
|
if (brief[field] !== expected)
|
|
248
274
|
throw new Error(`brief.${field} does not match the Teamwork plan`);
|
|
249
275
|
}
|
|
276
|
+
if (brief.role_id !== undefined)
|
|
277
|
+
throw new Error("brief.role_id is retired");
|
|
278
|
+
if (canonicalJson(brief.execution) !== canonicalJson(item.execution))
|
|
279
|
+
throw new Error("brief.execution does not match the frozen plan");
|
|
280
|
+
if (brief.selection_reason !== selectionReason)
|
|
281
|
+
throw new Error("brief.selection_reason does not match the frozen plan");
|
|
282
|
+
if (execution.profile.provider !== provider ||
|
|
283
|
+
execution.profile.profile !== profile ||
|
|
284
|
+
execution.profile.model !== model)
|
|
285
|
+
throw new Error("assignment provider, profile, or model does not match frozen execution");
|
|
250
286
|
const briefSourceDigest = digest(brief.source_digest, `brief.${assignmentId}.source_digest`);
|
|
251
287
|
if (briefSourceDigest !== sourceDigest)
|
|
252
288
|
throw new Error(`brief.${assignmentId}.source_digest does not match the Teamwork plan`);
|
|
@@ -279,6 +315,9 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
279
315
|
summary,
|
|
280
316
|
scope,
|
|
281
317
|
lens,
|
|
318
|
+
execution,
|
|
319
|
+
selectionReason,
|
|
320
|
+
taskInput: brief.input,
|
|
282
321
|
briefDigest,
|
|
283
322
|
sourceDigest,
|
|
284
323
|
...(contextFiles ? { contextFiles } : {}),
|
|
@@ -289,6 +328,7 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
289
328
|
rejectSecrets(assignment, `assignments[${index}]`);
|
|
290
329
|
return assignment;
|
|
291
330
|
});
|
|
331
|
+
assertCoordinationMembers(coordinationMode, assignments);
|
|
292
332
|
if (coordinationMode === "committee") {
|
|
293
333
|
const groups = new Map();
|
|
294
334
|
for (const assignment of assignments) {
|
|
@@ -315,6 +355,8 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
315
355
|
"lens",
|
|
316
356
|
"brief_path",
|
|
317
357
|
"brief_digest",
|
|
358
|
+
"execution",
|
|
359
|
+
"selection_reason",
|
|
318
360
|
];
|
|
319
361
|
const briefFields = [
|
|
320
362
|
"assignment_id",
|
|
@@ -322,6 +364,8 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
322
364
|
"profile",
|
|
323
365
|
"model",
|
|
324
366
|
"lens",
|
|
367
|
+
"execution",
|
|
368
|
+
"selection_reason",
|
|
325
369
|
];
|
|
326
370
|
const first = members[0];
|
|
327
371
|
if (!first)
|
|
@@ -348,12 +392,32 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
348
392
|
if (new Set(protectedTargets).size !== protectedTargets.length)
|
|
349
393
|
throw new Error("protected_targets must not contain duplicates");
|
|
350
394
|
const requestedCapacityValue = value.requested_capacity;
|
|
395
|
+
let retryOf;
|
|
396
|
+
if (value.retry_of !== undefined) {
|
|
397
|
+
const retry = value.retry_of;
|
|
398
|
+
const assignmentIds = retry?.assignment_ids;
|
|
399
|
+
if (!retry ||
|
|
400
|
+
typeof retry !== "object" ||
|
|
401
|
+
!Array.isArray(assignmentIds) ||
|
|
402
|
+
assignmentIds.length !== assignments.length ||
|
|
403
|
+
new Set(assignmentIds).size !== assignments.length ||
|
|
404
|
+
assignments.some((entry) => !assignmentIds.includes(entry.assignmentId)))
|
|
405
|
+
throw new Error("Retry lineage must contain the exact selected assignment set");
|
|
406
|
+
retryOf = {
|
|
407
|
+
batchId: id(retry.batch_id, "retry_of.batch_id"),
|
|
408
|
+
planHash: digest(retry.plan_hash, "retry_of.plan_hash"),
|
|
409
|
+
assignmentIds: assignmentIds.map((entry) => id(entry, "retry_of.assignment_ids")),
|
|
410
|
+
approvalRef: boundedText(retry.approval_ref, "retry_of.approval_ref"),
|
|
411
|
+
};
|
|
412
|
+
if (retryOf.batchId === batchId)
|
|
413
|
+
throw new Error("Retry must use a new batch identity");
|
|
414
|
+
}
|
|
351
415
|
if (requestedCapacityValue !== undefined &&
|
|
352
416
|
(!Number.isInteger(requestedCapacityValue) ||
|
|
353
417
|
requestedCapacityValue <= 0))
|
|
354
418
|
throw new Error("requested_capacity must be a positive integer");
|
|
355
419
|
const plan = {
|
|
356
|
-
schemaVersion:
|
|
420
|
+
schemaVersion: 2,
|
|
357
421
|
taskId,
|
|
358
422
|
batchId,
|
|
359
423
|
sessionId,
|
|
@@ -362,6 +426,11 @@ export function validateTeamworkPlan(input, taskRoot, expectedContext) {
|
|
|
362
426
|
inputRevision,
|
|
363
427
|
planSummary,
|
|
364
428
|
coordinationMode,
|
|
429
|
+
coordinationDigest,
|
|
430
|
+
...(retryOf ? { retryOf } : {}),
|
|
431
|
+
...(value.coordination_snapshot
|
|
432
|
+
? { coordinationSnapshot: value.coordination_snapshot }
|
|
433
|
+
: {}),
|
|
365
434
|
planHash: boundedText(value.plan_hash, "plan_hash"),
|
|
366
435
|
...(requestedCapacityValue !== undefined
|
|
367
436
|
? { requestedCapacity: requestedCapacityValue }
|
|
@@ -380,8 +449,31 @@ export function validateTeammateReport(input, identity, expectedSourceDigest) {
|
|
|
380
449
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
381
450
|
throw new Error("teammate report must be an object");
|
|
382
451
|
const value = input;
|
|
383
|
-
if (value.schema_version !== 1)
|
|
384
|
-
throw new Error("teammate report schema_version must be 1");
|
|
452
|
+
if (value.schema_version !== 1 && value.schema_version !== 2)
|
|
453
|
+
throw new Error("teammate report schema_version must be 1 or 2");
|
|
454
|
+
if (value.schema_version === 2) {
|
|
455
|
+
const allowed = new Set([
|
|
456
|
+
...Object.keys(teammateContentSchema.shape),
|
|
457
|
+
"task_id",
|
|
458
|
+
"batch_id",
|
|
459
|
+
"assignment_id",
|
|
460
|
+
"attempt_id",
|
|
461
|
+
"teammate_id",
|
|
462
|
+
"plan_hash",
|
|
463
|
+
"phase_id",
|
|
464
|
+
"task_route_revision",
|
|
465
|
+
"input_revision",
|
|
466
|
+
"source_digest",
|
|
467
|
+
]);
|
|
468
|
+
if (Object.keys(value).some((key) => !allowed.has(key)))
|
|
469
|
+
throw new Error("teammate report contains unknown fields");
|
|
470
|
+
}
|
|
471
|
+
const content = value.schema_version === 2
|
|
472
|
+
? teammateContentSchema.parse(Object.fromEntries(Object.keys(teammateContentSchema.shape).map((key) => [
|
|
473
|
+
key,
|
|
474
|
+
value[key],
|
|
475
|
+
])))
|
|
476
|
+
: undefined;
|
|
385
477
|
for (const key of [
|
|
386
478
|
"task_id",
|
|
387
479
|
"batch_id",
|
|
@@ -434,11 +526,11 @@ export function validateTeammateReport(input, identity, expectedSourceDigest) {
|
|
|
434
526
|
summary: boundedText(item.summary, `report.evidence[${index}].summary`),
|
|
435
527
|
};
|
|
436
528
|
});
|
|
437
|
-
if (
|
|
438
|
-
throw new Error(
|
|
439
|
-
}
|
|
529
|
+
if (evidence.length === 0)
|
|
530
|
+
throw new Error("report.evidence must include at least one item");
|
|
440
531
|
const report = {
|
|
441
|
-
schemaVersion:
|
|
532
|
+
schemaVersion: value.schema_version,
|
|
533
|
+
...(content ? { content } : {}),
|
|
442
534
|
taskId: identity.taskId,
|
|
443
535
|
batchId: identity.batchId,
|
|
444
536
|
assignmentId: identity.assignmentId,
|
|
@@ -504,8 +596,8 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
504
596
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
505
597
|
throw new Error("teammate claim must be an object");
|
|
506
598
|
const value = input;
|
|
507
|
-
if (value.schema_version !==
|
|
508
|
-
throw new Error("teammate claim schema_version must be
|
|
599
|
+
if (value.schema_version !== 2)
|
|
600
|
+
throw new Error("teammate claim schema_version must be 2");
|
|
509
601
|
for (const key of [
|
|
510
602
|
"task_id",
|
|
511
603
|
"batch_id",
|
|
@@ -541,7 +633,7 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
541
633
|
if (!Number.isInteger(value.slot) || value.slot < 0)
|
|
542
634
|
throw new Error("claim.slot must be non-negative");
|
|
543
635
|
const snapshotKeys = [
|
|
544
|
-
"
|
|
636
|
+
"configuration",
|
|
545
637
|
"provider",
|
|
546
638
|
"profile",
|
|
547
639
|
"model",
|
|
@@ -555,8 +647,8 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
555
647
|
const hasSnapshot = snapshotKeys.some((key) => value[key] !== undefined);
|
|
556
648
|
let execution;
|
|
557
649
|
if (hasSnapshot) {
|
|
558
|
-
if (value.role_id !==
|
|
559
|
-
throw new Error("claim.role_id
|
|
650
|
+
if (value.role_id !== undefined)
|
|
651
|
+
throw new Error("claim.role_id is retired");
|
|
560
652
|
const provider = value.provider;
|
|
561
653
|
if (provider !== "claude" &&
|
|
562
654
|
provider !== "codex" &&
|
|
@@ -574,9 +666,7 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
574
666
|
throw new Error("claim.model must be bounded single-line text");
|
|
575
667
|
const reasoningEffort = value.reasoning_effort;
|
|
576
668
|
if (reasoningEffort !== undefined &&
|
|
577
|
-
reasoningEffort
|
|
578
|
-
reasoningEffort !== "high" &&
|
|
579
|
-
reasoningEffort !== "xhigh") {
|
|
669
|
+
!REASONING_EFFORTS.includes(reasoningEffort)) {
|
|
580
670
|
throw new Error("claim.reasoning_effort is invalid");
|
|
581
671
|
}
|
|
582
672
|
if (!value.options ||
|
|
@@ -605,10 +695,13 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
605
695
|
}
|
|
606
696
|
execution = {
|
|
607
697
|
roleId: "teammate",
|
|
698
|
+
configuration: id(value.configuration, "claim.configuration"),
|
|
608
699
|
provider,
|
|
609
700
|
...(profile ? { profile } : {}),
|
|
610
701
|
...(model ? { model } : {}),
|
|
611
|
-
...(reasoningEffort
|
|
702
|
+
...(reasoningEffort
|
|
703
|
+
? { reasoningEffort: reasoningEffort }
|
|
704
|
+
: {}),
|
|
612
705
|
options,
|
|
613
706
|
...(value.profile_digest === undefined
|
|
614
707
|
? {}
|
|
@@ -621,7 +714,7 @@ export function validateTeammateClaim(input, identity, expectedPlanHash, expecte
|
|
|
621
714
|
};
|
|
622
715
|
}
|
|
623
716
|
const claim = {
|
|
624
|
-
schemaVersion:
|
|
717
|
+
schemaVersion: 2,
|
|
625
718
|
...identity,
|
|
626
719
|
planHash,
|
|
627
720
|
...(briefDigest ? { briefDigest } : {}),
|
|
@@ -680,7 +773,7 @@ export function validateTeammateDisposition(input, identity, expectedSourceDiges
|
|
|
680
773
|
export function writeTeammateClaim(input) {
|
|
681
774
|
const execution = input.launch
|
|
682
775
|
? {
|
|
683
|
-
|
|
776
|
+
configuration: input.launch.configuration,
|
|
684
777
|
provider: input.launch.provider,
|
|
685
778
|
...(input.launch.profile ? { profile: input.launch.profile } : {}),
|
|
686
779
|
...(input.launch.model ? { model: input.launch.model } : {}),
|
|
@@ -697,7 +790,7 @@ export function writeTeammateClaim(input) {
|
|
|
697
790
|
}
|
|
698
791
|
: undefined;
|
|
699
792
|
const claim = validateTeammateClaim({
|
|
700
|
-
schema_version:
|
|
793
|
+
schema_version: 2,
|
|
701
794
|
task_id: input.identity.taskId,
|
|
702
795
|
batch_id: input.identity.batchId,
|
|
703
796
|
assignment_id: input.identity.assignmentId,
|
|
@@ -724,7 +817,7 @@ export function writeTeammateClaim(input) {
|
|
|
724
817
|
status: claim.status,
|
|
725
818
|
...(claim.execution
|
|
726
819
|
? {
|
|
727
|
-
|
|
820
|
+
configuration: claim.execution.configuration,
|
|
728
821
|
provider: claim.execution.provider,
|
|
729
822
|
...(claim.execution.profile
|
|
730
823
|
? { profile: claim.execution.profile }
|
|
@@ -789,8 +882,13 @@ export function appendTeammateWorklog(input) {
|
|
|
789
882
|
}
|
|
790
883
|
}
|
|
791
884
|
export function writeTeammateReport(input) {
|
|
885
|
+
if (input.content &&
|
|
886
|
+
(input.content.status !== input.status ||
|
|
887
|
+
JSON.stringify(input.content.evidence) !== JSON.stringify(input.evidence)))
|
|
888
|
+
throw new Error("report content status/evidence must match the submitted report");
|
|
792
889
|
const report = validateTeammateReport({
|
|
793
|
-
|
|
890
|
+
...input.content,
|
|
891
|
+
schema_version: input.content ? 2 : 1,
|
|
794
892
|
task_id: input.identity.taskId,
|
|
795
893
|
batch_id: input.identity.batchId,
|
|
796
894
|
assignment_id: input.identity.assignmentId,
|
|
@@ -805,6 +903,7 @@ export function writeTeammateReport(input) {
|
|
|
805
903
|
evidence: input.evidence,
|
|
806
904
|
}, input.identity, input.sourceDigest);
|
|
807
905
|
writeImmutableJson(attemptArtifactPath(input.taskRoot, input.briefPath, input.identity.attemptId, "report.json"), {
|
|
906
|
+
...report.content,
|
|
808
907
|
schema_version: report.schemaVersion,
|
|
809
908
|
task_id: report.taskId,
|
|
810
909
|
batch_id: report.batchId,
|
|
@@ -852,57 +951,61 @@ export function writeTeammateDisposition(input) {
|
|
|
852
951
|
status: disposition.status,
|
|
853
952
|
source_digest: disposition.sourceDigest,
|
|
854
953
|
...(disposition.reason ? { reason: disposition.reason } : {}),
|
|
855
|
-
|
|
954
|
+
...(input.review ? { review: input.review } : {}),
|
|
955
|
+
}, input.review !== undefined);
|
|
956
|
+
}
|
|
957
|
+
export class TeammateReportReadError extends Error {
|
|
958
|
+
kind;
|
|
959
|
+
constructor(kind, message) {
|
|
960
|
+
super(message);
|
|
961
|
+
this.kind = kind;
|
|
962
|
+
this.name = "TeammateReportReadError";
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
function isMissingFile(error) {
|
|
966
|
+
return (error !== null &&
|
|
967
|
+
typeof error === "object" &&
|
|
968
|
+
"code" in error &&
|
|
969
|
+
error.code === "ENOENT");
|
|
856
970
|
}
|
|
857
971
|
export function readTeammateReport(input) {
|
|
858
972
|
if (!ID.test(input.attemptId))
|
|
859
973
|
throw new Error("attempt id must be safe");
|
|
860
|
-
|
|
861
|
-
|
|
974
|
+
let reportPath;
|
|
975
|
+
try {
|
|
976
|
+
reportPath = safePath(input.taskRoot, path.join(path.dirname(input.briefPath), "attempts", input.attemptId, "report.json"));
|
|
977
|
+
}
|
|
978
|
+
catch {
|
|
979
|
+
throw new TeammateReportReadError("storage", "teammate report path is unsafe");
|
|
980
|
+
}
|
|
981
|
+
let stat;
|
|
982
|
+
try {
|
|
983
|
+
stat = fs.lstatSync(reportPath);
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
if (isMissingFile(error))
|
|
987
|
+
throw new TeammateReportReadError("missing", "teammate report is missing");
|
|
988
|
+
throw new TeammateReportReadError("storage", "could not inspect teammate report");
|
|
989
|
+
}
|
|
862
990
|
if (!stat.isFile() || stat.size > 128 * 1024)
|
|
863
|
-
throw new
|
|
864
|
-
|
|
865
|
-
|
|
991
|
+
throw new TeammateReportReadError("storage", "teammate report must be a regular file under 131072 bytes");
|
|
992
|
+
let raw;
|
|
993
|
+
try {
|
|
994
|
+
raw = fs.readFileSync(reportPath, "utf8");
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
if (isMissingFile(error))
|
|
998
|
+
throw new TeammateReportReadError("missing", "teammate report is missing");
|
|
999
|
+
throw new TeammateReportReadError("storage", "could not read teammate report");
|
|
1000
|
+
}
|
|
1001
|
+
let report;
|
|
1002
|
+
try {
|
|
1003
|
+
report = JSON.parse(raw);
|
|
1004
|
+
}
|
|
1005
|
+
catch {
|
|
1006
|
+
throw new TeammateReportReadError("incomplete", "teammate report is not complete JSON");
|
|
1007
|
+
}
|
|
866
1008
|
rejectSecrets(report, "teammate report");
|
|
867
1009
|
return validateTeammateReport(report, { ...input.identity, attemptId: input.attemptId }, input.sourceDigest);
|
|
868
1010
|
}
|
|
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
1011
|
//# sourceMappingURL=artifact.js.map
|