synorch 0.2.0 → 0.4.0-beta.0
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/CHANGELOG.md +55 -1
- package/README.md +338 -28
- package/dist/application/doctor-service.d.ts +16 -0
- package/dist/application/doctor-service.d.ts.map +1 -1
- package/dist/application/doctor-service.js +230 -32
- package/dist/application/doctor-service.js.map +1 -1
- package/dist/application/generated-skill-doctor.d.ts +9 -0
- package/dist/application/generated-skill-doctor.d.ts.map +1 -0
- package/dist/application/generated-skill-doctor.js +433 -0
- package/dist/application/generated-skill-doctor.js.map +1 -0
- package/dist/application/project-discovery.d.ts +26 -1
- package/dist/application/project-discovery.d.ts.map +1 -1
- package/dist/application/project-discovery.js +99 -6
- package/dist/application/project-discovery.js.map +1 -1
- package/dist/application/safe-path.d.ts +19 -0
- package/dist/application/safe-path.d.ts.map +1 -0
- package/dist/application/safe-path.js +35 -0
- package/dist/application/safe-path.js.map +1 -0
- package/dist/application/size-ceiling.d.ts +15 -0
- package/dist/application/size-ceiling.d.ts.map +1 -0
- package/dist/application/size-ceiling.js +32 -0
- package/dist/application/size-ceiling.js.map +1 -0
- package/dist/application/structure-service.d.ts.map +1 -1
- package/dist/application/structure-service.js +10 -1
- package/dist/application/structure-service.js.map +1 -1
- package/dist/build-info.json +4 -0
- package/dist/cli.js +135 -18
- package/dist/cli.js.map +1 -1
- package/dist/domain/canonical-contracts.d.ts +69 -0
- package/dist/domain/canonical-contracts.d.ts.map +1 -0
- package/dist/domain/canonical-contracts.js +92 -0
- package/dist/domain/canonical-contracts.js.map +1 -0
- package/dist/domain/generated-skill.d.ts +55 -0
- package/dist/domain/generated-skill.d.ts.map +1 -0
- package/dist/domain/generated-skill.js +90 -0
- package/dist/domain/generated-skill.js.map +1 -0
- package/dist/domain/generation.d.ts +15 -2
- package/dist/domain/generation.d.ts.map +1 -1
- package/dist/domain/observation-ledger.d.ts +118 -0
- package/dist/domain/observation-ledger.d.ts.map +1 -0
- package/dist/domain/observation-ledger.js +183 -0
- package/dist/domain/observation-ledger.js.map +1 -0
- package/dist/domain/product.d.ts +1 -1
- package/dist/domain/product.d.ts.map +1 -1
- package/dist/domain/product.js +1 -1
- package/dist/domain/product.js.map +1 -1
- package/dist/domain/relative-path.d.ts +21 -0
- package/dist/domain/relative-path.d.ts.map +1 -0
- package/dist/domain/relative-path.js +43 -0
- package/dist/domain/relative-path.js.map +1 -0
- package/dist/domain/skill-packs.d.ts.map +1 -1
- package/dist/domain/skill-packs.js +1 -0
- package/dist/domain/skill-packs.js.map +1 -1
- package/dist/domain/suggest.d.ts +5 -0
- package/dist/domain/suggest.d.ts.map +1 -0
- package/dist/domain/suggest.js +32 -0
- package/dist/domain/suggest.js.map +1 -0
- package/dist/domain/zod-issues.d.ts +21 -0
- package/dist/domain/zod-issues.d.ts.map +1 -0
- package/dist/domain/zod-issues.js +27 -0
- package/dist/domain/zod-issues.js.map +1 -0
- package/dist/harness/auth/api-key.d.ts +13 -0
- package/dist/harness/auth/api-key.d.ts.map +1 -0
- package/dist/harness/auth/api-key.js +87 -0
- package/dist/harness/auth/api-key.js.map +1 -0
- package/dist/harness/auth/claude-bridge.d.ts +22 -0
- package/dist/harness/auth/claude-bridge.d.ts.map +1 -0
- package/dist/harness/auth/claude-bridge.js +92 -0
- package/dist/harness/auth/claude-bridge.js.map +1 -0
- package/dist/harness/auth/command.d.ts +17 -0
- package/dist/harness/auth/command.d.ts.map +1 -0
- package/dist/harness/auth/command.js +223 -0
- package/dist/harness/auth/command.js.map +1 -0
- package/dist/harness/auth/credential-store.d.ts +30 -0
- package/dist/harness/auth/credential-store.d.ts.map +1 -0
- package/dist/harness/auth/credential-store.js +270 -0
- package/dist/harness/auth/credential-store.js.map +1 -0
- package/dist/harness/auth/index.d.ts +13 -0
- package/dist/harness/auth/index.d.ts.map +1 -0
- package/dist/harness/auth/index.js +10 -0
- package/dist/harness/auth/index.js.map +1 -0
- package/dist/harness/auth/json-file.d.ts +8 -0
- package/dist/harness/auth/json-file.d.ts.map +1 -0
- package/dist/harness/auth/json-file.js +57 -0
- package/dist/harness/auth/json-file.js.map +1 -0
- package/dist/harness/auth/jwt.d.ts +11 -0
- package/dist/harness/auth/jwt.d.ts.map +1 -0
- package/dist/harness/auth/jwt.js +37 -0
- package/dist/harness/auth/jwt.js.map +1 -0
- package/dist/harness/auth/keychain.d.ts +39 -0
- package/dist/harness/auth/keychain.d.ts.map +1 -0
- package/dist/harness/auth/keychain.js +191 -0
- package/dist/harness/auth/keychain.js.map +1 -0
- package/dist/harness/auth/locks.d.ts +19 -0
- package/dist/harness/auth/locks.d.ts.map +1 -0
- package/dist/harness/auth/locks.js +102 -0
- package/dist/harness/auth/locks.js.map +1 -0
- package/dist/harness/auth/notices.d.ts +6 -0
- package/dist/harness/auth/notices.d.ts.map +1 -0
- package/dist/harness/auth/notices.js +29 -0
- package/dist/harness/auth/notices.js.map +1 -0
- package/dist/harness/auth/openai-chatgpt.d.ts +32 -0
- package/dist/harness/auth/openai-chatgpt.d.ts.map +1 -0
- package/dist/harness/auth/openai-chatgpt.js +446 -0
- package/dist/harness/auth/openai-chatgpt.js.map +1 -0
- package/dist/harness/auth/profile-state.d.ts +25 -0
- package/dist/harness/auth/profile-state.d.ts.map +1 -0
- package/dist/harness/auth/profile-state.js +77 -0
- package/dist/harness/auth/profile-state.js.map +1 -0
- package/dist/harness/auth/providers.d.ts +27 -0
- package/dist/harness/auth/providers.d.ts.map +1 -0
- package/dist/harness/auth/providers.js +66 -0
- package/dist/harness/auth/providers.js.map +1 -0
- package/dist/harness/auth/resolved-credential.d.ts +11 -0
- package/dist/harness/auth/resolved-credential.d.ts.map +1 -0
- package/dist/harness/auth/resolved-credential.js +35 -0
- package/dist/harness/auth/resolved-credential.js.map +1 -0
- package/dist/harness/cli/appearance.d.ts +28 -0
- package/dist/harness/cli/appearance.d.ts.map +1 -0
- package/dist/harness/cli/appearance.js +123 -0
- package/dist/harness/cli/appearance.js.map +1 -0
- package/dist/harness/cli/args.d.ts +429 -0
- package/dist/harness/cli/args.d.ts.map +1 -0
- package/dist/harness/cli/args.js +372 -0
- package/dist/harness/cli/args.js.map +1 -0
- package/dist/harness/cli/attachments.d.ts +44 -0
- package/dist/harness/cli/attachments.d.ts.map +1 -0
- package/dist/harness/cli/attachments.js +176 -0
- package/dist/harness/cli/attachments.js.map +1 -0
- package/dist/harness/cli/canonical.d.ts +64 -0
- package/dist/harness/cli/canonical.d.ts.map +1 -0
- package/dist/harness/cli/canonical.js +455 -0
- package/dist/harness/cli/canonical.js.map +1 -0
- package/dist/harness/cli/claude-native-approvals.d.ts +45 -0
- package/dist/harness/cli/claude-native-approvals.d.ts.map +1 -0
- package/dist/harness/cli/claude-native-approvals.js +183 -0
- package/dist/harness/cli/claude-native-approvals.js.map +1 -0
- package/dist/harness/cli/command-grants.d.ts +23 -0
- package/dist/harness/cli/command-grants.d.ts.map +1 -0
- package/dist/harness/cli/command-grants.js +76 -0
- package/dist/harness/cli/command-grants.js.map +1 -0
- package/dist/harness/cli/config-command.d.ts +98 -0
- package/dist/harness/cli/config-command.d.ts.map +1 -0
- package/dist/harness/cli/config-command.js +561 -0
- package/dist/harness/cli/config-command.js.map +1 -0
- package/dist/harness/cli/config.d.ts +429 -0
- package/dist/harness/cli/config.d.ts.map +1 -0
- package/dist/harness/cli/config.js +504 -0
- package/dist/harness/cli/config.js.map +1 -0
- package/dist/harness/cli/conversation.d.ts +35 -0
- package/dist/harness/cli/conversation.d.ts.map +1 -0
- package/dist/harness/cli/conversation.js +3755 -0
- package/dist/harness/cli/conversation.js.map +1 -0
- package/dist/harness/cli/doctor.d.ts +32 -0
- package/dist/harness/cli/doctor.d.ts.map +1 -0
- package/dist/harness/cli/doctor.js +329 -0
- package/dist/harness/cli/doctor.js.map +1 -0
- package/dist/harness/cli/effort-command.d.ts +41 -0
- package/dist/harness/cli/effort-command.d.ts.map +1 -0
- package/dist/harness/cli/effort-command.js +133 -0
- package/dist/harness/cli/effort-command.js.map +1 -0
- package/dist/harness/cli/extensions/agents.d.ts +40 -0
- package/dist/harness/cli/extensions/agents.d.ts.map +1 -0
- package/dist/harness/cli/extensions/agents.js +66 -0
- package/dist/harness/cli/extensions/agents.js.map +1 -0
- package/dist/harness/cli/extensions/catalog.d.ts +134 -0
- package/dist/harness/cli/extensions/catalog.d.ts.map +1 -0
- package/dist/harness/cli/extensions/catalog.js +213 -0
- package/dist/harness/cli/extensions/catalog.js.map +1 -0
- package/dist/harness/cli/extensions/claude-home.d.ts +33 -0
- package/dist/harness/cli/extensions/claude-home.d.ts.map +1 -0
- package/dist/harness/cli/extensions/claude-home.js +78 -0
- package/dist/harness/cli/extensions/claude-home.js.map +1 -0
- package/dist/harness/cli/extensions/controller.d.ts +51 -0
- package/dist/harness/cli/extensions/controller.d.ts.map +1 -0
- package/dist/harness/cli/extensions/controller.js +67 -0
- package/dist/harness/cli/extensions/controller.js.map +1 -0
- package/dist/harness/cli/extensions/hooks.d.ts +164 -0
- package/dist/harness/cli/extensions/hooks.d.ts.map +1 -0
- package/dist/harness/cli/extensions/hooks.js +442 -0
- package/dist/harness/cli/extensions/hooks.js.map +1 -0
- package/dist/harness/cli/extensions/index.d.ts +10 -0
- package/dist/harness/cli/extensions/index.d.ts.map +1 -0
- package/dist/harness/cli/extensions/index.js +10 -0
- package/dist/harness/cli/extensions/index.js.map +1 -0
- package/dist/harness/cli/extensions/markdown.d.ts +46 -0
- package/dist/harness/cli/extensions/markdown.d.ts.map +1 -0
- package/dist/harness/cli/extensions/markdown.js +139 -0
- package/dist/harness/cli/extensions/markdown.js.map +1 -0
- package/dist/harness/cli/extensions/plugins.d.ts +160 -0
- package/dist/harness/cli/extensions/plugins.d.ts.map +1 -0
- package/dist/harness/cli/extensions/plugins.js +645 -0
- package/dist/harness/cli/extensions/plugins.js.map +1 -0
- package/dist/harness/cli/extensions/tool-hooks.d.ts +28 -0
- package/dist/harness/cli/extensions/tool-hooks.d.ts.map +1 -0
- package/dist/harness/cli/extensions/tool-hooks.js +63 -0
- package/dist/harness/cli/extensions/tool-hooks.js.map +1 -0
- package/dist/harness/cli/extensions-command.d.ts +64 -0
- package/dist/harness/cli/extensions-command.d.ts.map +1 -0
- package/dist/harness/cli/extensions-command.js +639 -0
- package/dist/harness/cli/extensions-command.js.map +1 -0
- package/dist/harness/cli/help.d.ts +9 -0
- package/dist/harness/cli/help.d.ts.map +1 -0
- package/dist/harness/cli/help.js +191 -0
- package/dist/harness/cli/help.js.map +1 -0
- package/dist/harness/cli/index.d.ts +52 -0
- package/dist/harness/cli/index.d.ts.map +1 -0
- package/dist/harness/cli/index.js +297 -0
- package/dist/harness/cli/index.js.map +1 -0
- package/dist/harness/cli/inspect.d.ts +40 -0
- package/dist/harness/cli/inspect.d.ts.map +1 -0
- package/dist/harness/cli/inspect.js +185 -0
- package/dist/harness/cli/inspect.js.map +1 -0
- package/dist/harness/cli/mcp-command.d.ts +61 -0
- package/dist/harness/cli/mcp-command.d.ts.map +1 -0
- package/dist/harness/cli/mcp-command.js +519 -0
- package/dist/harness/cli/mcp-command.js.map +1 -0
- package/dist/harness/cli/memory-desk.d.ts +28 -0
- package/dist/harness/cli/memory-desk.d.ts.map +1 -0
- package/dist/harness/cli/memory-desk.js +275 -0
- package/dist/harness/cli/memory-desk.js.map +1 -0
- package/dist/harness/cli/model-picker.d.ts +44 -0
- package/dist/harness/cli/model-picker.d.ts.map +1 -0
- package/dist/harness/cli/model-picker.js +267 -0
- package/dist/harness/cli/model-picker.js.map +1 -0
- package/dist/harness/cli/onboarding.d.ts +29 -0
- package/dist/harness/cli/onboarding.d.ts.map +1 -0
- package/dist/harness/cli/onboarding.js +101 -0
- package/dist/harness/cli/onboarding.js.map +1 -0
- package/dist/harness/cli/orchestrate-tool.d.ts +60 -0
- package/dist/harness/cli/orchestrate-tool.d.ts.map +1 -0
- package/dist/harness/cli/orchestrate-tool.js +167 -0
- package/dist/harness/cli/orchestrate-tool.js.map +1 -0
- package/dist/harness/cli/orchestration-view.d.ts +57 -0
- package/dist/harness/cli/orchestration-view.d.ts.map +1 -0
- package/dist/harness/cli/orchestration-view.js +371 -0
- package/dist/harness/cli/orchestration-view.js.map +1 -0
- package/dist/harness/cli/outcome.d.ts +11 -0
- package/dist/harness/cli/outcome.d.ts.map +1 -0
- package/dist/harness/cli/outcome.js +70 -0
- package/dist/harness/cli/outcome.js.map +1 -0
- package/dist/harness/cli/panels/common.d.ts +30 -0
- package/dist/harness/cli/panels/common.d.ts.map +1 -0
- package/dist/harness/cli/panels/common.js +143 -0
- package/dist/harness/cli/panels/common.js.map +1 -0
- package/dist/harness/cli/panels/mcp.d.ts +18 -0
- package/dist/harness/cli/panels/mcp.d.ts.map +1 -0
- package/dist/harness/cli/panels/mcp.js +132 -0
- package/dist/harness/cli/panels/mcp.js.map +1 -0
- package/dist/harness/cli/panels/plugins.d.ts +8 -0
- package/dist/harness/cli/panels/plugins.d.ts.map +1 -0
- package/dist/harness/cli/panels/plugins.js +114 -0
- package/dist/harness/cli/panels/plugins.js.map +1 -0
- package/dist/harness/cli/panels/session.d.ts +54 -0
- package/dist/harness/cli/panels/session.d.ts.map +1 -0
- package/dist/harness/cli/panels/session.js +221 -0
- package/dist/harness/cli/panels/session.js.map +1 -0
- package/dist/harness/cli/panels/skills.d.ts +16 -0
- package/dist/harness/cli/panels/skills.d.ts.map +1 -0
- package/dist/harness/cli/panels/skills.js +146 -0
- package/dist/harness/cli/panels/skills.js.map +1 -0
- package/dist/harness/cli/project-profile.d.ts +87 -0
- package/dist/harness/cli/project-profile.d.ts.map +1 -0
- package/dist/harness/cli/project-profile.js +709 -0
- package/dist/harness/cli/project-profile.js.map +1 -0
- package/dist/harness/cli/project-scope.d.ts +2 -0
- package/dist/harness/cli/project-scope.d.ts.map +1 -0
- package/dist/harness/cli/project-scope.js +55 -0
- package/dist/harness/cli/project-scope.js.map +1 -0
- package/dist/harness/cli/renderers.d.ts +75 -0
- package/dist/harness/cli/renderers.d.ts.map +1 -0
- package/dist/harness/cli/renderers.js +162 -0
- package/dist/harness/cli/renderers.js.map +1 -0
- package/dist/harness/cli/rewind.d.ts +54 -0
- package/dist/harness/cli/rewind.d.ts.map +1 -0
- package/dist/harness/cli/rewind.js +80 -0
- package/dist/harness/cli/rewind.js.map +1 -0
- package/dist/harness/cli/role-policy.d.ts +8 -0
- package/dist/harness/cli/role-policy.d.ts.map +1 -0
- package/dist/harness/cli/role-policy.js +53 -0
- package/dist/harness/cli/role-policy.js.map +1 -0
- package/dist/harness/cli/run-summary.d.ts +16 -0
- package/dist/harness/cli/run-summary.d.ts.map +1 -0
- package/dist/harness/cli/run-summary.js +105 -0
- package/dist/harness/cli/run-summary.js.map +1 -0
- package/dist/harness/cli/runtime.d.ts +182 -0
- package/dist/harness/cli/runtime.d.ts.map +1 -0
- package/dist/harness/cli/runtime.js +999 -0
- package/dist/harness/cli/runtime.js.map +1 -0
- package/dist/harness/cli/scripted-script.d.ts +50 -0
- package/dist/harness/cli/scripted-script.d.ts.map +1 -0
- package/dist/harness/cli/scripted-script.js +110 -0
- package/dist/harness/cli/scripted-script.js.map +1 -0
- package/dist/harness/cli/session-git.d.ts +76 -0
- package/dist/harness/cli/session-git.d.ts.map +1 -0
- package/dist/harness/cli/session-git.js +146 -0
- package/dist/harness/cli/session-git.js.map +1 -0
- package/dist/harness/cli/session.d.ts +22 -0
- package/dist/harness/cli/session.d.ts.map +1 -0
- package/dist/harness/cli/session.js +203 -0
- package/dist/harness/cli/session.js.map +1 -0
- package/dist/harness/cli/slash-commands.d.ts +85 -0
- package/dist/harness/cli/slash-commands.d.ts.map +1 -0
- package/dist/harness/cli/slash-commands.js +114 -0
- package/dist/harness/cli/slash-commands.js.map +1 -0
- package/dist/harness/cli/startup-notices.d.ts +32 -0
- package/dist/harness/cli/startup-notices.d.ts.map +1 -0
- package/dist/harness/cli/startup-notices.js +93 -0
- package/dist/harness/cli/startup-notices.js.map +1 -0
- package/dist/harness/cli/terminal.d.ts +32 -0
- package/dist/harness/cli/terminal.d.ts.map +1 -0
- package/dist/harness/cli/terminal.js +21 -0
- package/dist/harness/cli/terminal.js.map +1 -0
- package/dist/harness/cli/transparency.d.ts +39 -0
- package/dist/harness/cli/transparency.d.ts.map +1 -0
- package/dist/harness/cli/transparency.js +491 -0
- package/dist/harness/cli/transparency.js.map +1 -0
- package/dist/harness/cli/trust.d.ts +40 -0
- package/dist/harness/cli/trust.d.ts.map +1 -0
- package/dist/harness/cli/trust.js +133 -0
- package/dist/harness/cli/trust.js.map +1 -0
- package/dist/harness/cli/usage-stats.d.ts +57 -0
- package/dist/harness/cli/usage-stats.d.ts.map +1 -0
- package/dist/harness/cli/usage-stats.js +341 -0
- package/dist/harness/cli/usage-stats.js.map +1 -0
- package/dist/harness/context/compaction.d.ts +69 -0
- package/dist/harness/context/compaction.d.ts.map +1 -0
- package/dist/harness/context/compaction.js +149 -0
- package/dist/harness/context/compaction.js.map +1 -0
- package/dist/harness/context/context-builder.d.ts +76 -0
- package/dist/harness/context/context-builder.d.ts.map +1 -0
- package/dist/harness/context/context-builder.js +367 -0
- package/dist/harness/context/context-builder.js.map +1 -0
- package/dist/harness/context/history.d.ts +31 -0
- package/dist/harness/context/history.d.ts.map +1 -0
- package/dist/harness/context/history.js +105 -0
- package/dist/harness/context/history.js.map +1 -0
- package/dist/harness/context/index.d.ts +11 -0
- package/dist/harness/context/index.d.ts.map +1 -0
- package/dist/harness/context/index.js +9 -0
- package/dist/harness/context/index.js.map +1 -0
- package/dist/harness/context/instructions.d.ts +30 -0
- package/dist/harness/context/instructions.d.ts.map +1 -0
- package/dist/harness/context/instructions.js +82 -0
- package/dist/harness/context/instructions.js.map +1 -0
- package/dist/harness/context/model-view.d.ts +46 -0
- package/dist/harness/context/model-view.d.ts.map +1 -0
- package/dist/harness/context/model-view.js +136 -0
- package/dist/harness/context/model-view.js.map +1 -0
- package/dist/harness/context/scope.d.ts +9 -0
- package/dist/harness/context/scope.d.ts.map +1 -0
- package/dist/harness/context/scope.js +50 -0
- package/dist/harness/context/scope.js.map +1 -0
- package/dist/harness/context/skills.d.ts +75 -0
- package/dist/harness/context/skills.d.ts.map +1 -0
- package/dist/harness/context/skills.js +109 -0
- package/dist/harness/context/skills.js.map +1 -0
- package/dist/harness/context/tokens.d.ts +10 -0
- package/dist/harness/context/tokens.d.ts.map +1 -0
- package/dist/harness/context/tokens.js +39 -0
- package/dist/harness/context/tokens.js.map +1 -0
- package/dist/harness/contracts/auth.d.ts +214 -0
- package/dist/harness/contracts/auth.d.ts.map +1 -0
- package/dist/harness/contracts/auth.js +120 -0
- package/dist/harness/contracts/auth.js.map +1 -0
- package/dist/harness/contracts/choice.d.ts +115 -0
- package/dist/harness/contracts/choice.d.ts.map +1 -0
- package/dist/harness/contracts/choice.js +115 -0
- package/dist/harness/contracts/choice.js.map +1 -0
- package/dist/harness/contracts/common.d.ts +119 -0
- package/dist/harness/contracts/common.d.ts.map +1 -0
- package/dist/harness/contracts/common.js +66 -0
- package/dist/harness/contracts/common.js.map +1 -0
- package/dist/harness/contracts/digest.d.ts +59 -0
- package/dist/harness/contracts/digest.d.ts.map +1 -0
- package/dist/harness/contracts/digest.js +88 -0
- package/dist/harness/contracts/digest.js.map +1 -0
- package/dist/harness/contracts/errors.d.ts +70 -0
- package/dist/harness/contracts/errors.d.ts.map +1 -0
- package/dist/harness/contracts/errors.js +87 -0
- package/dist/harness/contracts/errors.js.map +1 -0
- package/dist/harness/contracts/events.d.ts +3094 -0
- package/dist/harness/contracts/events.d.ts.map +1 -0
- package/dist/harness/contracts/events.js +426 -0
- package/dist/harness/contracts/events.js.map +1 -0
- package/dist/harness/contracts/evidence.d.ts +286 -0
- package/dist/harness/contracts/evidence.d.ts.map +1 -0
- package/dist/harness/contracts/evidence.js +234 -0
- package/dist/harness/contracts/evidence.js.map +1 -0
- package/dist/harness/contracts/ids.d.ts +77 -0
- package/dist/harness/contracts/ids.d.ts.map +1 -0
- package/dist/harness/contracts/ids.js +126 -0
- package/dist/harness/contracts/ids.js.map +1 -0
- package/dist/harness/contracts/index.d.ts +23 -0
- package/dist/harness/contracts/index.d.ts.map +1 -0
- package/dist/harness/contracts/index.js +23 -0
- package/dist/harness/contracts/index.js.map +1 -0
- package/dist/harness/contracts/jsonl.d.ts +3386 -0
- package/dist/harness/contracts/jsonl.d.ts.map +1 -0
- package/dist/harness/contracts/jsonl.js +87 -0
- package/dist/harness/contracts/jsonl.js.map +1 -0
- package/dist/harness/contracts/memory.d.ts +271 -0
- package/dist/harness/contracts/memory.d.ts.map +1 -0
- package/dist/harness/contracts/memory.js +152 -0
- package/dist/harness/contracts/memory.js.map +1 -0
- package/dist/harness/contracts/model.d.ts +1201 -0
- package/dist/harness/contracts/model.d.ts.map +1 -0
- package/dist/harness/contracts/model.js +386 -0
- package/dist/harness/contracts/model.js.map +1 -0
- package/dist/harness/contracts/packets.d.ts +945 -0
- package/dist/harness/contracts/packets.d.ts.map +1 -0
- package/dist/harness/contracts/packets.js +535 -0
- package/dist/harness/contracts/packets.js.map +1 -0
- package/dist/harness/contracts/panel.d.ts +140 -0
- package/dist/harness/contracts/panel.d.ts.map +1 -0
- package/dist/harness/contracts/panel.js +19 -0
- package/dist/harness/contracts/panel.js.map +1 -0
- package/dist/harness/contracts/paths.d.ts +58 -0
- package/dist/harness/contracts/paths.d.ts.map +1 -0
- package/dist/harness/contracts/paths.js +204 -0
- package/dist/harness/contracts/paths.js.map +1 -0
- package/dist/harness/contracts/policy.d.ts +459 -0
- package/dist/harness/contracts/policy.d.ts.map +1 -0
- package/dist/harness/contracts/policy.js +338 -0
- package/dist/harness/contracts/policy.js.map +1 -0
- package/dist/harness/contracts/projection.d.ts +111 -0
- package/dist/harness/contracts/projection.d.ts.map +1 -0
- package/dist/harness/contracts/projection.js +2 -0
- package/dist/harness/contracts/projection.js.map +1 -0
- package/dist/harness/contracts/renderer.d.ts +291 -0
- package/dist/harness/contracts/renderer.d.ts.map +1 -0
- package/dist/harness/contracts/renderer.js +36 -0
- package/dist/harness/contracts/renderer.js.map +1 -0
- package/dist/harness/contracts/roles.d.ts +28 -0
- package/dist/harness/contracts/roles.d.ts.map +1 -0
- package/dist/harness/contracts/roles.js +36 -0
- package/dist/harness/contracts/roles.js.map +1 -0
- package/dist/harness/contracts/runtime.d.ts +283 -0
- package/dist/harness/contracts/runtime.d.ts.map +1 -0
- package/dist/harness/contracts/runtime.js +13 -0
- package/dist/harness/contracts/runtime.js.map +1 -0
- package/dist/harness/contracts/state.d.ts +124 -0
- package/dist/harness/contracts/state.d.ts.map +1 -0
- package/dist/harness/contracts/state.js +172 -0
- package/dist/harness/contracts/state.js.map +1 -0
- package/dist/harness/contracts/store.d.ts +117 -0
- package/dist/harness/contracts/store.d.ts.map +1 -0
- package/dist/harness/contracts/store.js +54 -0
- package/dist/harness/contracts/store.js.map +1 -0
- package/dist/harness/contracts/tools.d.ts +296 -0
- package/dist/harness/contracts/tools.d.ts.map +1 -0
- package/dist/harness/contracts/tools.js +117 -0
- package/dist/harness/contracts/tools.js.map +1 -0
- package/dist/harness/contracts/views.d.ts +468 -0
- package/dist/harness/contracts/views.d.ts.map +1 -0
- package/dist/harness/contracts/views.js +2 -0
- package/dist/harness/contracts/views.js.map +1 -0
- package/dist/harness/core/driver.d.ts +53 -0
- package/dist/harness/core/driver.d.ts.map +1 -0
- package/dist/harness/core/driver.js +566 -0
- package/dist/harness/core/driver.js.map +1 -0
- package/dist/harness/core/envelope.d.ts +11 -0
- package/dist/harness/core/envelope.d.ts.map +1 -0
- package/dist/harness/core/envelope.js +35 -0
- package/dist/harness/core/envelope.js.map +1 -0
- package/dist/harness/core/index.d.ts +7 -0
- package/dist/harness/core/index.d.ts.map +1 -0
- package/dist/harness/core/index.js +5 -0
- package/dist/harness/core/index.js.map +1 -0
- package/dist/harness/core/projection.d.ts +10 -0
- package/dist/harness/core/projection.d.ts.map +1 -0
- package/dist/harness/core/projection.js +268 -0
- package/dist/harness/core/projection.js.map +1 -0
- package/dist/harness/core/recovery.d.ts +14 -0
- package/dist/harness/core/recovery.d.ts.map +1 -0
- package/dist/harness/core/recovery.js +115 -0
- package/dist/harness/core/recovery.js.map +1 -0
- package/dist/harness/core/stream.d.ts +23 -0
- package/dist/harness/core/stream.d.ts.map +1 -0
- package/dist/harness/core/stream.js +102 -0
- package/dist/harness/core/stream.js.map +1 -0
- package/dist/harness/core/testing.d.ts +80 -0
- package/dist/harness/core/testing.d.ts.map +1 -0
- package/dist/harness/core/testing.js +258 -0
- package/dist/harness/core/testing.js.map +1 -0
- package/dist/harness/mcp/approvals.d.ts +14 -0
- package/dist/harness/mcp/approvals.d.ts.map +1 -0
- package/dist/harness/mcp/approvals.js +60 -0
- package/dist/harness/mcp/approvals.js.map +1 -0
- package/dist/harness/mcp/client.d.ts +74 -0
- package/dist/harness/mcp/client.d.ts.map +1 -0
- package/dist/harness/mcp/client.js +214 -0
- package/dist/harness/mcp/client.js.map +1 -0
- package/dist/harness/mcp/config.d.ts +156 -0
- package/dist/harness/mcp/config.d.ts.map +1 -0
- package/dist/harness/mcp/config.js +180 -0
- package/dist/harness/mcp/config.js.map +1 -0
- package/dist/harness/mcp/errors.d.ts +24 -0
- package/dist/harness/mcp/errors.d.ts.map +1 -0
- package/dist/harness/mcp/errors.js +67 -0
- package/dist/harness/mcp/errors.js.map +1 -0
- package/dist/harness/mcp/index.d.ts +9 -0
- package/dist/harness/mcp/index.d.ts.map +1 -0
- package/dist/harness/mcp/index.js +9 -0
- package/dist/harness/mcp/index.js.map +1 -0
- package/dist/harness/mcp/manager.d.ts +115 -0
- package/dist/harness/mcp/manager.d.ts.map +1 -0
- package/dist/harness/mcp/manager.js +361 -0
- package/dist/harness/mcp/manager.js.map +1 -0
- package/dist/harness/mcp/oauth.d.ts +54 -0
- package/dist/harness/mcp/oauth.d.ts.map +1 -0
- package/dist/harness/mcp/oauth.js +253 -0
- package/dist/harness/mcp/oauth.js.map +1 -0
- package/dist/harness/mcp/tools.d.ts +24 -0
- package/dist/harness/mcp/tools.d.ts.map +1 -0
- package/dist/harness/mcp/tools.js +142 -0
- package/dist/harness/mcp/tools.js.map +1 -0
- package/dist/harness/memory/bootstrap.d.ts +55 -0
- package/dist/harness/memory/bootstrap.d.ts.map +1 -0
- package/dist/harness/memory/bootstrap.js +124 -0
- package/dist/harness/memory/bootstrap.js.map +1 -0
- package/dist/harness/memory/desk.d.ts +74 -0
- package/dist/harness/memory/desk.d.ts.map +1 -0
- package/dist/harness/memory/desk.js +211 -0
- package/dist/harness/memory/desk.js.map +1 -0
- package/dist/harness/memory/graph.d.ts +24 -0
- package/dist/harness/memory/graph.d.ts.map +1 -0
- package/dist/harness/memory/graph.js +92 -0
- package/dist/harness/memory/graph.js.map +1 -0
- package/dist/harness/memory/index.d.ts +12 -0
- package/dist/harness/memory/index.d.ts.map +1 -0
- package/dist/harness/memory/index.js +10 -0
- package/dist/harness/memory/index.js.map +1 -0
- package/dist/harness/memory/markdown-memory-store.d.ts +122 -0
- package/dist/harness/memory/markdown-memory-store.d.ts.map +1 -0
- package/dist/harness/memory/markdown-memory-store.js +419 -0
- package/dist/harness/memory/markdown-memory-store.js.map +1 -0
- package/dist/harness/memory/memory-command.d.ts +22 -0
- package/dist/harness/memory/memory-command.d.ts.map +1 -0
- package/dist/harness/memory/memory-command.js +335 -0
- package/dist/harness/memory/memory-command.js.map +1 -0
- package/dist/harness/memory/memory-index.d.ts +71 -0
- package/dist/harness/memory/memory-index.d.ts.map +1 -0
- package/dist/harness/memory/memory-index.js +206 -0
- package/dist/harness/memory/memory-index.js.map +1 -0
- package/dist/harness/memory/note-format.d.ts +20 -0
- package/dist/harness/memory/note-format.d.ts.map +1 -0
- package/dist/harness/memory/note-format.js +69 -0
- package/dist/harness/memory/note-format.js.map +1 -0
- package/dist/harness/memory/obsidian.d.ts +14 -0
- package/dist/harness/memory/obsidian.d.ts.map +1 -0
- package/dist/harness/memory/obsidian.js +64 -0
- package/dist/harness/memory/obsidian.js.map +1 -0
- package/dist/harness/memory/redaction.d.ts +15 -0
- package/dist/harness/memory/redaction.d.ts.map +1 -0
- package/dist/harness/memory/redaction.js +64 -0
- package/dist/harness/memory/redaction.js.map +1 -0
- package/dist/harness/memory/relations.d.ts +29 -0
- package/dist/harness/memory/relations.d.ts.map +1 -0
- package/dist/harness/memory/relations.js +117 -0
- package/dist/harness/memory/relations.js.map +1 -0
- package/dist/harness/memory/vault.d.ts +35 -0
- package/dist/harness/memory/vault.d.ts.map +1 -0
- package/dist/harness/memory/vault.js +194 -0
- package/dist/harness/memory/vault.js.map +1 -0
- package/dist/harness/orchestration/approval.d.ts +29 -0
- package/dist/harness/orchestration/approval.d.ts.map +1 -0
- package/dist/harness/orchestration/approval.js +86 -0
- package/dist/harness/orchestration/approval.js.map +1 -0
- package/dist/harness/orchestration/artifact-review.d.ts +219 -0
- package/dist/harness/orchestration/artifact-review.d.ts.map +1 -0
- package/dist/harness/orchestration/artifact-review.js +319 -0
- package/dist/harness/orchestration/artifact-review.js.map +1 -0
- package/dist/harness/orchestration/attempt-log.d.ts +53 -0
- package/dist/harness/orchestration/attempt-log.d.ts.map +1 -0
- package/dist/harness/orchestration/attempt-log.js +138 -0
- package/dist/harness/orchestration/attempt-log.js.map +1 -0
- package/dist/harness/orchestration/budget.d.ts +110 -0
- package/dist/harness/orchestration/budget.d.ts.map +1 -0
- package/dist/harness/orchestration/budget.js +197 -0
- package/dist/harness/orchestration/budget.js.map +1 -0
- package/dist/harness/orchestration/capabilities.d.ts +6 -0
- package/dist/harness/orchestration/capabilities.d.ts.map +1 -0
- package/dist/harness/orchestration/capabilities.js +80 -0
- package/dist/harness/orchestration/capabilities.js.map +1 -0
- package/dist/harness/orchestration/claims.d.ts +148 -0
- package/dist/harness/orchestration/claims.d.ts.map +1 -0
- package/dist/harness/orchestration/claims.js +64 -0
- package/dist/harness/orchestration/claims.js.map +1 -0
- package/dist/harness/orchestration/control-plane.d.ts +18 -0
- package/dist/harness/orchestration/control-plane.d.ts.map +1 -0
- package/dist/harness/orchestration/control-plane.js +74 -0
- package/dist/harness/orchestration/control-plane.js.map +1 -0
- package/dist/harness/orchestration/coordinator.d.ts +132 -0
- package/dist/harness/orchestration/coordinator.d.ts.map +1 -0
- package/dist/harness/orchestration/coordinator.js +1628 -0
- package/dist/harness/orchestration/coordinator.js.map +1 -0
- package/dist/harness/orchestration/delegation.d.ts +80 -0
- package/dist/harness/orchestration/delegation.d.ts.map +1 -0
- package/dist/harness/orchestration/delegation.js +81 -0
- package/dist/harness/orchestration/delegation.js.map +1 -0
- package/dist/harness/orchestration/evidence.d.ts +124 -0
- package/dist/harness/orchestration/evidence.d.ts.map +1 -0
- package/dist/harness/orchestration/evidence.js +576 -0
- package/dist/harness/orchestration/evidence.js.map +1 -0
- package/dist/harness/orchestration/factories.d.ts +13 -0
- package/dist/harness/orchestration/factories.d.ts.map +1 -0
- package/dist/harness/orchestration/factories.js +20 -0
- package/dist/harness/orchestration/factories.js.map +1 -0
- package/dist/harness/orchestration/git.d.ts +77 -0
- package/dist/harness/orchestration/git.d.ts.map +1 -0
- package/dist/harness/orchestration/git.js +214 -0
- package/dist/harness/orchestration/git.js.map +1 -0
- package/dist/harness/orchestration/index.d.ts +25 -0
- package/dist/harness/orchestration/index.d.ts.map +1 -0
- package/dist/harness/orchestration/index.js +23 -0
- package/dist/harness/orchestration/index.js.map +1 -0
- package/dist/harness/orchestration/isolation.d.ts +181 -0
- package/dist/harness/orchestration/isolation.d.ts.map +1 -0
- package/dist/harness/orchestration/isolation.js +1357 -0
- package/dist/harness/orchestration/isolation.js.map +1 -0
- package/dist/harness/orchestration/paths.d.ts +18 -0
- package/dist/harness/orchestration/paths.d.ts.map +1 -0
- package/dist/harness/orchestration/paths.js +56 -0
- package/dist/harness/orchestration/paths.js.map +1 -0
- package/dist/harness/orchestration/plan.d.ts +219 -0
- package/dist/harness/orchestration/plan.d.ts.map +1 -0
- package/dist/harness/orchestration/plan.js +537 -0
- package/dist/harness/orchestration/plan.js.map +1 -0
- package/dist/harness/orchestration/planner.d.ts +137 -0
- package/dist/harness/orchestration/planner.d.ts.map +1 -0
- package/dist/harness/orchestration/planner.js +162 -0
- package/dist/harness/orchestration/planner.js.map +1 -0
- package/dist/harness/orchestration/recorder.d.ts +23 -0
- package/dist/harness/orchestration/recorder.d.ts.map +1 -0
- package/dist/harness/orchestration/recorder.js +50 -0
- package/dist/harness/orchestration/recorder.js.map +1 -0
- package/dist/harness/orchestration/scheduler.d.ts +40 -0
- package/dist/harness/orchestration/scheduler.d.ts.map +1 -0
- package/dist/harness/orchestration/scheduler.js +157 -0
- package/dist/harness/orchestration/scheduler.js.map +1 -0
- package/dist/harness/orchestration/testing.d.ts +233 -0
- package/dist/harness/orchestration/testing.d.ts.map +1 -0
- package/dist/harness/orchestration/testing.js +734 -0
- package/dist/harness/orchestration/testing.js.map +1 -0
- package/dist/harness/orchestration/verification-preflight.d.ts +46 -0
- package/dist/harness/orchestration/verification-preflight.d.ts.map +1 -0
- package/dist/harness/orchestration/verification-preflight.js +104 -0
- package/dist/harness/orchestration/verification-preflight.js.map +1 -0
- package/dist/harness/orchestration/worker-control.d.ts +64 -0
- package/dist/harness/orchestration/worker-control.d.ts.map +1 -0
- package/dist/harness/orchestration/worker-control.js +199 -0
- package/dist/harness/orchestration/worker-control.js.map +1 -0
- package/dist/harness/orchestration/worker-manager.d.ts +225 -0
- package/dist/harness/orchestration/worker-manager.d.ts.map +1 -0
- package/dist/harness/orchestration/worker-manager.js +1347 -0
- package/dist/harness/orchestration/worker-manager.js.map +1 -0
- package/dist/harness/orchestration/workspace-digest.d.ts +39 -0
- package/dist/harness/orchestration/workspace-digest.d.ts.map +1 -0
- package/dist/harness/orchestration/workspace-digest.js +147 -0
- package/dist/harness/orchestration/workspace-digest.js.map +1 -0
- package/dist/harness/policy/approval-broker.d.ts +13 -0
- package/dist/harness/policy/approval-broker.d.ts.map +1 -0
- package/dist/harness/policy/approval-broker.js +26 -0
- package/dist/harness/policy/approval-broker.js.map +1 -0
- package/dist/harness/policy/command-classifier.d.ts +29 -0
- package/dist/harness/policy/command-classifier.d.ts.map +1 -0
- package/dist/harness/policy/command-classifier.js +459 -0
- package/dist/harness/policy/command-classifier.js.map +1 -0
- package/dist/harness/policy/command-rules.d.ts +56 -0
- package/dist/harness/policy/command-rules.d.ts.map +1 -0
- package/dist/harness/policy/command-rules.js +588 -0
- package/dist/harness/policy/command-rules.js.map +1 -0
- package/dist/harness/policy/config.d.ts +27 -0
- package/dist/harness/policy/config.d.ts.map +1 -0
- package/dist/harness/policy/config.js +37 -0
- package/dist/harness/policy/config.js.map +1 -0
- package/dist/harness/policy/engine.d.ts +67 -0
- package/dist/harness/policy/engine.d.ts.map +1 -0
- package/dist/harness/policy/engine.js +483 -0
- package/dist/harness/policy/engine.js.map +1 -0
- package/dist/harness/policy/exec-allowlist.d.ts +66 -0
- package/dist/harness/policy/exec-allowlist.d.ts.map +1 -0
- package/dist/harness/policy/exec-allowlist.js +449 -0
- package/dist/harness/policy/exec-allowlist.js.map +1 -0
- package/dist/harness/policy/index.d.ts +10 -0
- package/dist/harness/policy/index.d.ts.map +1 -0
- package/dist/harness/policy/index.js +8 -0
- package/dist/harness/policy/index.js.map +1 -0
- package/dist/harness/policy/secret-patterns.d.ts +12 -0
- package/dist/harness/policy/secret-patterns.d.ts.map +1 -0
- package/dist/harness/policy/secret-patterns.js +48 -0
- package/dist/harness/policy/secret-patterns.js.map +1 -0
- package/dist/harness/policy/shell-parser.d.ts +40 -0
- package/dist/harness/policy/shell-parser.d.ts.map +1 -0
- package/dist/harness/policy/shell-parser.js +278 -0
- package/dist/harness/policy/shell-parser.js.map +1 -0
- package/dist/harness/policy/workspace-trust.d.ts +37 -0
- package/dist/harness/policy/workspace-trust.d.ts.map +1 -0
- package/dist/harness/policy/workspace-trust.js +164 -0
- package/dist/harness/policy/workspace-trust.js.map +1 -0
- package/dist/harness/providers/anthropic-messages.d.ts +48 -0
- package/dist/harness/providers/anthropic-messages.d.ts.map +1 -0
- package/dist/harness/providers/anthropic-messages.js +360 -0
- package/dist/harness/providers/anthropic-messages.js.map +1 -0
- package/dist/harness/providers/assembler.d.ts +29 -0
- package/dist/harness/providers/assembler.d.ts.map +1 -0
- package/dist/harness/providers/assembler.js +130 -0
- package/dist/harness/providers/assembler.js.map +1 -0
- package/dist/harness/providers/authenticated-stream.d.ts +9 -0
- package/dist/harness/providers/authenticated-stream.d.ts.map +1 -0
- package/dist/harness/providers/authenticated-stream.js +81 -0
- package/dist/harness/providers/authenticated-stream.js.map +1 -0
- package/dist/harness/providers/catalog.d.ts +75 -0
- package/dist/harness/providers/catalog.d.ts.map +1 -0
- package/dist/harness/providers/catalog.js +124 -0
- package/dist/harness/providers/catalog.js.map +1 -0
- package/dist/harness/providers/claude-code/adapter.d.ts +96 -0
- package/dist/harness/providers/claude-code/adapter.d.ts.map +1 -0
- package/dist/harness/providers/claude-code/adapter.js +863 -0
- package/dist/harness/providers/claude-code/adapter.js.map +1 -0
- package/dist/harness/providers/claude-code/mcp-relay.d.ts +2 -0
- package/dist/harness/providers/claude-code/mcp-relay.d.ts.map +1 -0
- package/dist/harness/providers/claude-code/mcp-relay.js +27 -0
- package/dist/harness/providers/claude-code/mcp-relay.js.map +1 -0
- package/dist/harness/providers/claude-code/mcp-server.d.ts +40 -0
- package/dist/harness/providers/claude-code/mcp-server.d.ts.map +1 -0
- package/dist/harness/providers/claude-code/mcp-server.js +171 -0
- package/dist/harness/providers/claude-code/mcp-server.js.map +1 -0
- package/dist/harness/providers/claude-code/native.d.ts +36 -0
- package/dist/harness/providers/claude-code/native.d.ts.map +1 -0
- package/dist/harness/providers/claude-code/native.js +153 -0
- package/dist/harness/providers/claude-code/native.js.map +1 -0
- package/dist/harness/providers/claude-code/process.d.ts +48 -0
- package/dist/harness/providers/claude-code/process.d.ts.map +1 -0
- package/dist/harness/providers/claude-code/process.js +128 -0
- package/dist/harness/providers/claude-code/process.js.map +1 -0
- package/dist/harness/providers/codex-app-server.d.ts +8 -0
- package/dist/harness/providers/codex-app-server.d.ts.map +1 -0
- package/dist/harness/providers/codex-app-server.js +45 -0
- package/dist/harness/providers/codex-app-server.js.map +1 -0
- package/dist/harness/providers/effort.d.ts +51 -0
- package/dist/harness/providers/effort.d.ts.map +1 -0
- package/dist/harness/providers/effort.js +108 -0
- package/dist/harness/providers/effort.js.map +1 -0
- package/dist/harness/providers/errors.d.ts +34 -0
- package/dist/harness/providers/errors.d.ts.map +1 -0
- package/dist/harness/providers/errors.js +143 -0
- package/dist/harness/providers/errors.js.map +1 -0
- package/dist/harness/providers/grammar.d.ts +17 -0
- package/dist/harness/providers/grammar.d.ts.map +1 -0
- package/dist/harness/providers/grammar.js +89 -0
- package/dist/harness/providers/grammar.js.map +1 -0
- package/dist/harness/providers/http-stream.d.ts +40 -0
- package/dist/harness/providers/http-stream.d.ts.map +1 -0
- package/dist/harness/providers/http-stream.js +134 -0
- package/dist/harness/providers/http-stream.js.map +1 -0
- package/dist/harness/providers/index.d.ts +20 -0
- package/dist/harness/providers/index.d.ts.map +1 -0
- package/dist/harness/providers/index.js +17 -0
- package/dist/harness/providers/index.js.map +1 -0
- package/dist/harness/providers/responses.d.ts +36 -0
- package/dist/harness/providers/responses.d.ts.map +1 -0
- package/dist/harness/providers/responses.js +435 -0
- package/dist/harness/providers/responses.js.map +1 -0
- package/dist/harness/providers/reviewer-route.d.ts +14 -0
- package/dist/harness/providers/reviewer-route.d.ts.map +1 -0
- package/dist/harness/providers/reviewer-route.js +31 -0
- package/dist/harness/providers/reviewer-route.js.map +1 -0
- package/dist/harness/providers/router.d.ts +50 -0
- package/dist/harness/providers/router.d.ts.map +1 -0
- package/dist/harness/providers/router.js +295 -0
- package/dist/harness/providers/router.js.map +1 -0
- package/dist/harness/providers/scripted.d.ts +22 -0
- package/dist/harness/providers/scripted.d.ts.map +1 -0
- package/dist/harness/providers/scripted.js +111 -0
- package/dist/harness/providers/scripted.js.map +1 -0
- package/dist/harness/providers/sse.d.ts +13 -0
- package/dist/harness/providers/sse.d.ts.map +1 -0
- package/dist/harness/providers/sse.js +82 -0
- package/dist/harness/providers/sse.js.map +1 -0
- package/dist/harness/providers/testing.d.ts +35 -0
- package/dist/harness/providers/testing.d.ts.map +1 -0
- package/dist/harness/providers/testing.js +93 -0
- package/dist/harness/providers/testing.js.map +1 -0
- package/dist/harness/providers/web-search.d.ts +110 -0
- package/dist/harness/providers/web-search.d.ts.map +1 -0
- package/dist/harness/providers/web-search.js +476 -0
- package/dist/harness/providers/web-search.js.map +1 -0
- package/dist/harness/store/blob-store.d.ts +4 -0
- package/dist/harness/store/blob-store.d.ts.map +1 -0
- package/dist/harness/store/blob-store.js +79 -0
- package/dist/harness/store/blob-store.js.map +1 -0
- package/dist/harness/store/durable-file.d.ts +17 -0
- package/dist/harness/store/durable-file.d.ts.map +1 -0
- package/dist/harness/store/durable-file.js +84 -0
- package/dist/harness/store/durable-file.js.map +1 -0
- package/dist/harness/store/event-store.d.ts +56 -0
- package/dist/harness/store/event-store.d.ts.map +1 -0
- package/dist/harness/store/event-store.js +210 -0
- package/dist/harness/store/event-store.js.map +1 -0
- package/dist/harness/store/index.d.ts +8 -0
- package/dist/harness/store/index.d.ts.map +1 -0
- package/dist/harness/store/index.js +4 -0
- package/dist/harness/store/index.js.map +1 -0
- package/dist/harness/store/layout.d.ts +12 -0
- package/dist/harness/store/layout.d.ts.map +1 -0
- package/dist/harness/store/layout.js +27 -0
- package/dist/harness/store/layout.js.map +1 -0
- package/dist/harness/store/lease.d.ts +37 -0
- package/dist/harness/store/lease.d.ts.map +1 -0
- package/dist/harness/store/lease.js +326 -0
- package/dist/harness/store/lease.js.map +1 -0
- package/dist/harness/store/segments.d.ts +49 -0
- package/dist/harness/store/segments.d.ts.map +1 -0
- package/dist/harness/store/segments.js +216 -0
- package/dist/harness/store/segments.js.map +1 -0
- package/dist/harness/store/session-store.d.ts +18 -0
- package/dist/harness/store/session-store.d.ts.map +1 -0
- package/dist/harness/store/session-store.js +319 -0
- package/dist/harness/store/session-store.js.map +1 -0
- package/dist/harness/tools/builtin/control-tools.d.ts +57 -0
- package/dist/harness/tools/builtin/control-tools.d.ts.map +1 -0
- package/dist/harness/tools/builtin/control-tools.js +72 -0
- package/dist/harness/tools/builtin/control-tools.js.map +1 -0
- package/dist/harness/tools/builtin/glob.d.ts +15 -0
- package/dist/harness/tools/builtin/glob.d.ts.map +1 -0
- package/dist/harness/tools/builtin/glob.js +164 -0
- package/dist/harness/tools/builtin/glob.js.map +1 -0
- package/dist/harness/tools/builtin/process-bg.d.ts +72 -0
- package/dist/harness/tools/builtin/process-bg.d.ts.map +1 -0
- package/dist/harness/tools/builtin/process-bg.js +360 -0
- package/dist/harness/tools/builtin/process-bg.js.map +1 -0
- package/dist/harness/tools/builtin/process-tools.d.ts +46 -0
- package/dist/harness/tools/builtin/process-tools.d.ts.map +1 -0
- package/dist/harness/tools/builtin/process-tools.js +263 -0
- package/dist/harness/tools/builtin/process-tools.js.map +1 -0
- package/dist/harness/tools/builtin/read-media.d.ts +10 -0
- package/dist/harness/tools/builtin/read-media.d.ts.map +1 -0
- package/dist/harness/tools/builtin/read-media.js +128 -0
- package/dist/harness/tools/builtin/read-media.js.map +1 -0
- package/dist/harness/tools/builtin/read-tools.d.ts +29 -0
- package/dist/harness/tools/builtin/read-tools.d.ts.map +1 -0
- package/dist/harness/tools/builtin/read-tools.js +329 -0
- package/dist/harness/tools/builtin/read-tools.js.map +1 -0
- package/dist/harness/tools/builtin/shared.d.ts +47 -0
- package/dist/harness/tools/builtin/shared.d.ts.map +1 -0
- package/dist/harness/tools/builtin/shared.js +94 -0
- package/dist/harness/tools/builtin/shared.js.map +1 -0
- package/dist/harness/tools/builtin/todo.d.ts +38 -0
- package/dist/harness/tools/builtin/todo.d.ts.map +1 -0
- package/dist/harness/tools/builtin/todo.js +55 -0
- package/dist/harness/tools/builtin/todo.js.map +1 -0
- package/dist/harness/tools/builtin/web-common.d.ts +18 -0
- package/dist/harness/tools/builtin/web-common.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-common.js +53 -0
- package/dist/harness/tools/builtin/web-common.js.map +1 -0
- package/dist/harness/tools/builtin/web-fetch.d.ts +33 -0
- package/dist/harness/tools/builtin/web-fetch.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-fetch.js +173 -0
- package/dist/harness/tools/builtin/web-fetch.js.map +1 -0
- package/dist/harness/tools/builtin/web-html.d.ts +14 -0
- package/dist/harness/tools/builtin/web-html.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-html.js +314 -0
- package/dist/harness/tools/builtin/web-html.js.map +1 -0
- package/dist/harness/tools/builtin/web-net.d.ts +59 -0
- package/dist/harness/tools/builtin/web-net.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-net.js +347 -0
- package/dist/harness/tools/builtin/web-net.js.map +1 -0
- package/dist/harness/tools/builtin/web-search.d.ts +40 -0
- package/dist/harness/tools/builtin/web-search.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-search.js +60 -0
- package/dist/harness/tools/builtin/web-search.js.map +1 -0
- package/dist/harness/tools/builtin/web-state.d.ts +67 -0
- package/dist/harness/tools/builtin/web-state.d.ts.map +1 -0
- package/dist/harness/tools/builtin/web-state.js +227 -0
- package/dist/harness/tools/builtin/web-state.js.map +1 -0
- package/dist/harness/tools/builtin/write-tools.d.ts +18 -0
- package/dist/harness/tools/builtin/write-tools.d.ts.map +1 -0
- package/dist/harness/tools/builtin/write-tools.js +335 -0
- package/dist/harness/tools/builtin/write-tools.js.map +1 -0
- package/dist/harness/tools/environment.d.ts +9 -0
- package/dist/harness/tools/environment.d.ts.map +1 -0
- package/dist/harness/tools/environment.js +59 -0
- package/dist/harness/tools/environment.js.map +1 -0
- package/dist/harness/tools/file-ledger.d.ts +9 -0
- package/dist/harness/tools/file-ledger.d.ts.map +1 -0
- package/dist/harness/tools/file-ledger.js +26 -0
- package/dist/harness/tools/file-ledger.js.map +1 -0
- package/dist/harness/tools/gateway.d.ts +52 -0
- package/dist/harness/tools/gateway.d.ts.map +1 -0
- package/dist/harness/tools/gateway.js +435 -0
- package/dist/harness/tools/gateway.js.map +1 -0
- package/dist/harness/tools/index.d.ts +20 -0
- package/dist/harness/tools/index.d.ts.map +1 -0
- package/dist/harness/tools/index.js +17 -0
- package/dist/harness/tools/index.js.map +1 -0
- package/dist/harness/tools/patch.d.ts +51 -0
- package/dist/harness/tools/patch.d.ts.map +1 -0
- package/dist/harness/tools/patch.js +450 -0
- package/dist/harness/tools/patch.js.map +1 -0
- package/dist/harness/tools/process.d.ts +28 -0
- package/dist/harness/tools/process.d.ts.map +1 -0
- package/dist/harness/tools/process.js +263 -0
- package/dist/harness/tools/process.js.map +1 -0
- package/dist/harness/tools/redaction.d.ts +26 -0
- package/dist/harness/tools/redaction.d.ts.map +1 -0
- package/dist/harness/tools/redaction.js +77 -0
- package/dist/harness/tools/redaction.js.map +1 -0
- package/dist/harness/tools/registry.d.ts +26 -0
- package/dist/harness/tools/registry.d.ts.map +1 -0
- package/dist/harness/tools/registry.js +57 -0
- package/dist/harness/tools/registry.js.map +1 -0
- package/dist/harness/tools/sandbox.d.ts +23 -0
- package/dist/harness/tools/sandbox.d.ts.map +1 -0
- package/dist/harness/tools/sandbox.js +117 -0
- package/dist/harness/tools/sandbox.js.map +1 -0
- package/dist/harness/tools/testing.d.ts +42 -0
- package/dist/harness/tools/testing.d.ts.map +1 -0
- package/dist/harness/tools/testing.js +143 -0
- package/dist/harness/tools/testing.js.map +1 -0
- package/dist/harness/tools/text-file.d.ts +38 -0
- package/dist/harness/tools/text-file.d.ts.map +1 -0
- package/dist/harness/tools/text-file.js +90 -0
- package/dist/harness/tools/text-file.js.map +1 -0
- package/dist/harness/tools/windows-launch.d.ts +52 -0
- package/dist/harness/tools/windows-launch.d.ts.map +1 -0
- package/dist/harness/tools/windows-launch.js +261 -0
- package/dist/harness/tools/windows-launch.js.map +1 -0
- package/dist/harness/tools/workspace-path.d.ts +34 -0
- package/dist/harness/tools/workspace-path.d.ts.map +1 -0
- package/dist/harness/tools/workspace-path.js +133 -0
- package/dist/harness/tools/workspace-path.js.map +1 -0
- package/dist/harness/tui/approvals.d.ts +44 -0
- package/dist/harness/tui/approvals.d.ts.map +1 -0
- package/dist/harness/tui/approvals.js +127 -0
- package/dist/harness/tui/approvals.js.map +1 -0
- package/dist/harness/tui/auth-interaction.d.ts +36 -0
- package/dist/harness/tui/auth-interaction.d.ts.map +1 -0
- package/dist/harness/tui/auth-interaction.js +76 -0
- package/dist/harness/tui/auth-interaction.js.map +1 -0
- package/dist/harness/tui/choice-modal.d.ts +58 -0
- package/dist/harness/tui/choice-modal.d.ts.map +1 -0
- package/dist/harness/tui/choice-modal.js +244 -0
- package/dist/harness/tui/choice-modal.js.map +1 -0
- package/dist/harness/tui/conversation-view.d.ts +250 -0
- package/dist/harness/tui/conversation-view.d.ts.map +1 -0
- package/dist/harness/tui/conversation-view.js +1115 -0
- package/dist/harness/tui/conversation-view.js.map +1 -0
- package/dist/harness/tui/describe.d.ts +21 -0
- package/dist/harness/tui/describe.d.ts.map +1 -0
- package/dist/harness/tui/describe.js +110 -0
- package/dist/harness/tui/describe.js.map +1 -0
- package/dist/harness/tui/index.d.ts +30 -0
- package/dist/harness/tui/index.d.ts.map +1 -0
- package/dist/harness/tui/index.js +26 -0
- package/dist/harness/tui/index.js.map +1 -0
- package/dist/harness/tui/input/attachments.d.ts +20 -0
- package/dist/harness/tui/input/attachments.d.ts.map +1 -0
- package/dist/harness/tui/input/attachments.js +100 -0
- package/dist/harness/tui/input/attachments.js.map +1 -0
- package/dist/harness/tui/input/autocomplete.d.ts +41 -0
- package/dist/harness/tui/input/autocomplete.d.ts.map +1 -0
- package/dist/harness/tui/input/autocomplete.js +85 -0
- package/dist/harness/tui/input/autocomplete.js.map +1 -0
- package/dist/harness/tui/input/clipboard.d.ts +44 -0
- package/dist/harness/tui/input/clipboard.d.ts.map +1 -0
- package/dist/harness/tui/input/clipboard.js +181 -0
- package/dist/harness/tui/input/clipboard.js.map +1 -0
- package/dist/harness/tui/input/commands.d.ts +23 -0
- package/dist/harness/tui/input/commands.d.ts.map +1 -0
- package/dist/harness/tui/input/commands.js +71 -0
- package/dist/harness/tui/input/commands.js.map +1 -0
- package/dist/harness/tui/input/file-index.d.ts +49 -0
- package/dist/harness/tui/input/file-index.d.ts.map +1 -0
- package/dist/harness/tui/input/file-index.js +170 -0
- package/dist/harness/tui/input/file-index.js.map +1 -0
- package/dist/harness/tui/input/index.d.ts +8 -0
- package/dist/harness/tui/input/index.d.ts.map +1 -0
- package/dist/harness/tui/input/index.js +8 -0
- package/dist/harness/tui/input/index.js.map +1 -0
- package/dist/harness/tui/input/mouse.d.ts +68 -0
- package/dist/harness/tui/input/mouse.d.ts.map +1 -0
- package/dist/harness/tui/input/mouse.js +141 -0
- package/dist/harness/tui/input/mouse.js.map +1 -0
- package/dist/harness/tui/interrupt.d.ts +25 -0
- package/dist/harness/tui/interrupt.d.ts.map +1 -0
- package/dist/harness/tui/interrupt.js +49 -0
- package/dist/harness/tui/interrupt.js.map +1 -0
- package/dist/harness/tui/jsonl-renderer.d.ts +68 -0
- package/dist/harness/tui/jsonl-renderer.d.ts.map +1 -0
- package/dist/harness/tui/jsonl-renderer.js +172 -0
- package/dist/harness/tui/jsonl-renderer.js.map +1 -0
- package/dist/harness/tui/line-source.d.ts +39 -0
- package/dist/harness/tui/line-source.d.ts.map +1 -0
- package/dist/harness/tui/line-source.js +137 -0
- package/dist/harness/tui/line-source.js.map +1 -0
- package/dist/harness/tui/logo-art.d.ts +9 -0
- package/dist/harness/tui/logo-art.d.ts.map +1 -0
- package/dist/harness/tui/logo-art.js +24 -0
- package/dist/harness/tui/logo-art.js.map +1 -0
- package/dist/harness/tui/logo.d.ts +28 -0
- package/dist/harness/tui/logo.d.ts.map +1 -0
- package/dist/harness/tui/logo.js +88 -0
- package/dist/harness/tui/logo.js.map +1 -0
- package/dist/harness/tui/open-browser.d.ts +17 -0
- package/dist/harness/tui/open-browser.d.ts.map +1 -0
- package/dist/harness/tui/open-browser.js +45 -0
- package/dist/harness/tui/open-browser.js.map +1 -0
- package/dist/harness/tui/output-chunks.d.ts +8 -0
- package/dist/harness/tui/output-chunks.d.ts.map +1 -0
- package/dist/harness/tui/output-chunks.js +48 -0
- package/dist/harness/tui/output-chunks.js.map +1 -0
- package/dist/harness/tui/panels/index.d.ts +2 -0
- package/dist/harness/tui/panels/index.d.ts.map +1 -0
- package/dist/harness/tui/panels/index.js +2 -0
- package/dist/harness/tui/panels/index.js.map +1 -0
- package/dist/harness/tui/panels/panel-view.d.ts +98 -0
- package/dist/harness/tui/panels/panel-view.d.ts.map +1 -0
- package/dist/harness/tui/panels/panel-view.js +596 -0
- package/dist/harness/tui/panels/panel-view.js.map +1 -0
- package/dist/harness/tui/pi-tui-renderer.d.ts +363 -0
- package/dist/harness/tui/pi-tui-renderer.d.ts.map +1 -0
- package/dist/harness/tui/pi-tui-renderer.js +2696 -0
- package/dist/harness/tui/pi-tui-renderer.js.map +1 -0
- package/dist/harness/tui/plain-line-renderer.d.ts +102 -0
- package/dist/harness/tui/plain-line-renderer.d.ts.map +1 -0
- package/dist/harness/tui/plain-line-renderer.js +491 -0
- package/dist/harness/tui/plain-line-renderer.js.map +1 -0
- package/dist/harness/tui/render-queue.d.ts +34 -0
- package/dist/harness/tui/render-queue.d.ts.map +1 -0
- package/dist/harness/tui/render-queue.js +92 -0
- package/dist/harness/tui/render-queue.js.map +1 -0
- package/dist/harness/tui/sanitize.d.ts +9 -0
- package/dist/harness/tui/sanitize.d.ts.map +1 -0
- package/dist/harness/tui/sanitize.js +31 -0
- package/dist/harness/tui/sanitize.js.map +1 -0
- package/dist/harness/tui/style.d.ts +54 -0
- package/dist/harness/tui/style.d.ts.map +1 -0
- package/dist/harness/tui/style.js +64 -0
- package/dist/harness/tui/style.js.map +1 -0
- package/dist/harness/tui/terminal-lifecycle.d.ts +49 -0
- package/dist/harness/tui/terminal-lifecycle.d.ts.map +1 -0
- package/dist/harness/tui/terminal-lifecycle.js +100 -0
- package/dist/harness/tui/terminal-lifecycle.js.map +1 -0
- package/dist/harness/tui/theme.d.ts +77 -0
- package/dist/harness/tui/theme.d.ts.map +1 -0
- package/dist/harness/tui/theme.js +501 -0
- package/dist/harness/tui/theme.js.map +1 -0
- package/dist/harness/tui/tool-cards.d.ts +35 -0
- package/dist/harness/tui/tool-cards.d.ts.map +1 -0
- package/dist/harness/tui/tool-cards.js +122 -0
- package/dist/harness/tui/tool-cards.js.map +1 -0
- package/dist/harness/tui/tool-row.d.ts +51 -0
- package/dist/harness/tui/tool-row.d.ts.map +1 -0
- package/dist/harness/tui/tool-row.js +98 -0
- package/dist/harness/tui/tool-row.js.map +1 -0
- package/dist/harness/tui/views/board.d.ts +49 -0
- package/dist/harness/tui/views/board.d.ts.map +1 -0
- package/dist/harness/tui/views/board.js +293 -0
- package/dist/harness/tui/views/board.js.map +1 -0
- package/dist/harness/tui/views/cards.d.ts +8 -0
- package/dist/harness/tui/views/cards.d.ts.map +1 -0
- package/dist/harness/tui/views/cards.js +206 -0
- package/dist/harness/tui/views/cards.js.map +1 -0
- package/dist/harness/tui/views/components.d.ts +101 -0
- package/dist/harness/tui/views/components.d.ts.map +1 -0
- package/dist/harness/tui/views/components.js +186 -0
- package/dist/harness/tui/views/components.js.map +1 -0
- package/dist/harness/tui/views/diff.d.ts +11 -0
- package/dist/harness/tui/views/diff.d.ts.map +1 -0
- package/dist/harness/tui/views/diff.js +150 -0
- package/dist/harness/tui/views/diff.js.map +1 -0
- package/dist/harness/tui/views/graph.d.ts +49 -0
- package/dist/harness/tui/views/graph.d.ts.map +1 -0
- package/dist/harness/tui/views/graph.js +361 -0
- package/dist/harness/tui/views/graph.js.map +1 -0
- package/dist/harness/tui/views/index.d.ts +15 -0
- package/dist/harness/tui/views/index.d.ts.map +1 -0
- package/dist/harness/tui/views/index.js +15 -0
- package/dist/harness/tui/views/index.js.map +1 -0
- package/dist/harness/tui/views/kit.d.ts +112 -0
- package/dist/harness/tui/views/kit.d.ts.map +1 -0
- package/dist/harness/tui/views/kit.js +282 -0
- package/dist/harness/tui/views/kit.js.map +1 -0
- package/dist/harness/tui/views/trust-cards.d.ts +27 -0
- package/dist/harness/tui/views/trust-cards.d.ts.map +1 -0
- package/dist/harness/tui/views/trust-cards.js +181 -0
- package/dist/harness/tui/views/trust-cards.js.map +1 -0
- package/dist/harness/tui/views/usage.d.ts +4 -0
- package/dist/harness/tui/views/usage.d.ts.map +1 -0
- package/dist/harness/tui/views/usage.js +182 -0
- package/dist/harness/tui/views/usage.js.map +1 -0
- package/dist/harness/tui/views/worker.d.ts +47 -0
- package/dist/harness/tui/views/worker.d.ts.map +1 -0
- package/dist/harness/tui/views/worker.js +221 -0
- package/dist/harness/tui/views/worker.js.map +1 -0
- package/dist/harness/tui/welcome.d.ts +97 -0
- package/dist/harness/tui/welcome.d.ts.map +1 -0
- package/dist/harness/tui/welcome.js +271 -0
- package/dist/harness/tui/welcome.js.map +1 -0
- package/dist/harness/tui/wrap.d.ts +26 -0
- package/dist/harness/tui/wrap.d.ts.map +1 -0
- package/dist/harness/tui/wrap.js +38 -0
- package/dist/harness/tui/wrap.js.map +1 -0
- package/dist/infrastructure/build-info.d.ts +5 -0
- package/dist/infrastructure/build-info.d.ts.map +1 -0
- package/dist/infrastructure/build-info.js +21 -0
- package/dist/infrastructure/build-info.js.map +1 -0
- package/dist/infrastructure/frontmatter.d.ts +25 -0
- package/dist/infrastructure/frontmatter.d.ts.map +1 -0
- package/dist/infrastructure/frontmatter.js +75 -0
- package/dist/infrastructure/frontmatter.js.map +1 -0
- package/dist/templates/agent-manifests.d.ts +19 -0
- package/dist/templates/agent-manifests.d.ts.map +1 -0
- package/dist/templates/agent-manifests.js +302 -0
- package/dist/templates/agent-manifests.js.map +1 -0
- package/dist/templates/base-skills.d.ts +23 -0
- package/dist/templates/base-skills.d.ts.map +1 -0
- package/dist/templates/base-skills.js +477 -0
- package/dist/templates/base-skills.js.map +1 -0
- package/dist/templates/skill-creator-skill.d.ts +23 -0
- package/dist/templates/skill-creator-skill.d.ts.map +1 -0
- package/dist/templates/skill-creator-skill.js +235 -0
- package/dist/templates/skill-creator-skill.js.map +1 -0
- package/dist/templates/skill-references.d.ts +13 -0
- package/dist/templates/skill-references.d.ts.map +1 -0
- package/dist/templates/skill-references.js +280 -0
- package/dist/templates/skill-references.js.map +1 -0
- package/dist/templates/structure-templates.d.ts.map +1 -1
- package/dist/templates/structure-templates.js +20 -144
- package/dist/templates/structure-templates.js.map +1 -1
- package/dist/templates/task-conductor-skill.d.ts +1 -1
- package/dist/templates/task-conductor-skill.d.ts.map +1 -1
- package/dist/templates/task-conductor-skill.js +50 -22
- package/dist/templates/task-conductor-skill.js.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,1628 @@
|
|
|
1
|
+
import { canRunCommands, completionPacketSchema, createId, deriveProjectId, digestOf, EXIT_CODES, exitCodeFor, HarnessError, isTerminalState, packetDigest, planTaskSchema, ProviderFailure, RouteBlockedFailure, validateTransition, DEFAULT_ORCHESTRATION_BUDGETS, orchestrationBudgetsSchema, } from "../contracts/index.js";
|
|
2
|
+
import { approvePlan } from "./approval.js";
|
|
3
|
+
import { runArtifactReview } from "./artifact-review.js";
|
|
4
|
+
import { readEvents } from "./attempt-log.js";
|
|
5
|
+
import { commandMentioned } from "./capabilities.js";
|
|
6
|
+
import { createBudgetTracker } from "./budget.js";
|
|
7
|
+
import { createControlPlaneWriter } from "./control-plane.js";
|
|
8
|
+
import { mayComplete } from "./evidence.js";
|
|
9
|
+
import { isLiteralPattern } from "./paths.js";
|
|
10
|
+
import { applyDelta, compileReviewerPacket, compileTaskPacket, createDeltaPacket, deltaNotes, DEFAULT_STEP_FLOORS, isIntegrationReview, refreshPacketSources, reviewerStepLimit, reviewWarranted, runStepLimit, validatePlan, writingTaskCount, } from "./plan.js";
|
|
11
|
+
import { formatVerificationRefusal, preflightVerification } from "./verification-preflight.js";
|
|
12
|
+
import { createRunRecorder, createWorkspaceSourceReader, currentDigests, PACKET_MEDIA_TYPE, } from "./recorder.js";
|
|
13
|
+
import { createDagScheduler, DEFAULT_CONCURRENCY } from "./scheduler.js";
|
|
14
|
+
export const DEFAULT_COORDINATOR_LIMITS = {
|
|
15
|
+
review: "proportional",
|
|
16
|
+
concurrency: DEFAULT_CONCURRENCY,
|
|
17
|
+
maxPlanAttempts: 2,
|
|
18
|
+
maxPlanRevisions: 2,
|
|
19
|
+
maxRetries: DEFAULT_ORCHESTRATION_BUDGETS.triage_retries,
|
|
20
|
+
maxRevisions: DEFAULT_ORCHESTRATION_BUDGETS.review_revisions,
|
|
21
|
+
budgets: DEFAULT_ORCHESTRATION_BUDGETS,
|
|
22
|
+
maxReviewAttempts: 2,
|
|
23
|
+
maxRepackages: 2,
|
|
24
|
+
maxSpawnedTasks: 4,
|
|
25
|
+
stepFloors: DEFAULT_STEP_FLOORS,
|
|
26
|
+
};
|
|
27
|
+
/** Plan tasks whose verification commands run repository code (every non-empty verification list). */
|
|
28
|
+
function needsTrust(plan) {
|
|
29
|
+
return plan.tasks.some((task) => task.verification.length > 0);
|
|
30
|
+
}
|
|
31
|
+
/** A review verdict caused by the reviewer's read scope, not by the work (live run 01M381W6). */
|
|
32
|
+
const SCOPE_LIMITED = /\b(?:read[ -]?scope|dispatch'?s? (?:read )?scope|outside (?:the |my |this |its )?(?:authorized |allowed |permitted )?(?:read )?scope|not (?:allowed|permitted|authorized) to read|cannot (?:be )?read|permits reading only)\b/i;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a non-accepting review only failed criteria as unverifiable because the reviewer believed
|
|
35
|
+
* it could not read what they need. After the workspace-wide read scope this is a harness problem:
|
|
36
|
+
* the review is re-dispatched once with an explicit note instead of failing the task.
|
|
37
|
+
*/
|
|
38
|
+
export function scopeLimitedReview(review) {
|
|
39
|
+
if (review === undefined)
|
|
40
|
+
return false;
|
|
41
|
+
const failing = review.criteria.filter((criterion) => criterion.verdict !== "met");
|
|
42
|
+
if (failing.length === 0 || failing.some((criterion) => criterion.verdict !== "unverifiable"))
|
|
43
|
+
return false;
|
|
44
|
+
if (review.findings.some((finding) => finding.severity === "blocker" && !SCOPE_LIMITED.test(finding.summary)))
|
|
45
|
+
return false;
|
|
46
|
+
return [...failing.map((criterion) => criterion.note ?? ""), ...review.findings.map((finding) => finding.summary)].some((text) => SCOPE_LIMITED.test(text));
|
|
47
|
+
}
|
|
48
|
+
const SCOPE_NOTE = "Harness: an earlier review of this artifact reported criteria as unverifiable because of its read scope. Your read scope is the whole workspace (read-only): read whatever a criterion needs and check it with your own tool calls.";
|
|
49
|
+
const NOTE_LIMIT = 1000;
|
|
50
|
+
/**
|
|
51
|
+
* What the reviewer of a `partial` artifact is told (live run 01M3ABTS): the worker's own caveats,
|
|
52
|
+
* with the rule that a check the worker invented and the sandbox refused proves nothing either way.
|
|
53
|
+
*/
|
|
54
|
+
export function partialCaveats(completion) {
|
|
55
|
+
return [
|
|
56
|
+
`Harness: the worker reported ${completion.status} but produced this change; the harness sent it to you to decide. Check every criterion yourself. A check the worker invented that the sandbox refused is neither evidence nor a defect.`,
|
|
57
|
+
`Worker summary: ${completion.summary}`.slice(0, NOTE_LIMIT),
|
|
58
|
+
...completion.unresolved_risks.slice(0, 5).map((risk) => `Worker caveat: ${risk}`.slice(0, NOTE_LIMIT)),
|
|
59
|
+
...completion.skipped_checks.slice(0, 5).map((check) => `Worker skipped check: ${check.check}: ${check.reason}`.slice(0, NOTE_LIMIT)),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* What the next attempt learns from the previous one (never an identical retry): its status and
|
|
64
|
+
* summary, skipped checks, unresolved items, unevidenced criteria and the orchestrator's guidance.
|
|
65
|
+
*/
|
|
66
|
+
export function retryNotes(completion, criteria, guidance, extra = []) {
|
|
67
|
+
const notes = [
|
|
68
|
+
`Previous attempt ${completion.attempt_id} reported ${completion.status}: ${completion.summary}`,
|
|
69
|
+
...(guidance === undefined ? [] : [`Orchestrator guidance: ${guidance}`]),
|
|
70
|
+
...criteria.flatMap((criterion) => {
|
|
71
|
+
const state = criterionEvidence(completion, criterion.id);
|
|
72
|
+
return state.status === "resolved" ? [] : [`Previous attempt did not evidence ${criterion.id}${state.reason === undefined ? "" : ` (${state.reason})`}`];
|
|
73
|
+
}),
|
|
74
|
+
...completion.skipped_checks.map((check) => `Previous attempt skipped ${check.check}: ${check.reason}`),
|
|
75
|
+
...completion.unresolved_risks.map((risk) => `Previous attempt left unresolved: ${risk}`),
|
|
76
|
+
...extra,
|
|
77
|
+
];
|
|
78
|
+
return notes.map((note) => note.slice(0, NOTE_LIMIT)).slice(0, 20);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether a criterion is evidenced after resolution (ADR-18, F10): `resolved` when at least one of
|
|
82
|
+
* its pointers resolved (or the harness substituted), `unresolved` with the first reason when
|
|
83
|
+
* pointers were listed but none resolved, `missing` when none was listed.
|
|
84
|
+
*/
|
|
85
|
+
export function criterionEvidence(completion, id) {
|
|
86
|
+
const entries = (completion.evidence_resolution ?? []).filter((entry) => entry.criterion_id === id);
|
|
87
|
+
if (entries.some((entry) => entry.status === "resolved"))
|
|
88
|
+
return { status: "resolved" };
|
|
89
|
+
const failure = entries.find((entry) => entry.status === "unresolved");
|
|
90
|
+
if (failure !== undefined)
|
|
91
|
+
return { status: "unresolved", reason: (failure.reason ?? "the pointer does not resolve").slice(0, 300) };
|
|
92
|
+
const listed = completion.acceptance_evidence.some((entry) => entry.criterion_id === id && entry.evidence.length > 0);
|
|
93
|
+
// A completion assembled before ADR-18 carries no resolution record; its listed pointers count as evidenced.
|
|
94
|
+
if (listed)
|
|
95
|
+
return completion.evidence_resolution === undefined ? { status: "resolved" } : { status: "unresolved", reason: "the pointers were not verified" };
|
|
96
|
+
return { status: "missing" };
|
|
97
|
+
}
|
|
98
|
+
const VERIFICATION_FAILURES = ["verification_failed", "review_blocked", "stale_packet"];
|
|
99
|
+
/**
|
|
100
|
+
* The exit code of a run whose tasks did not all complete (cli-and-jsonl.md §5): a verification
|
|
101
|
+
* failure wins, then the first recorded cause (provider/tool → 4, policy → 6, ...); a task without
|
|
102
|
+
* a recorded cause (e.g. cancelled because a dependency failed) counts as a verification failure.
|
|
103
|
+
*/
|
|
104
|
+
export function failedRunExitCode(causes) {
|
|
105
|
+
const recorded = causes.filter((cause) => cause !== undefined);
|
|
106
|
+
const verification = recorded.find((cause) => VERIFICATION_FAILURES.includes(cause));
|
|
107
|
+
return exitCodeFor(verification ?? recorded[0] ?? "verification_failed");
|
|
108
|
+
}
|
|
109
|
+
function providerErrorCode(error) {
|
|
110
|
+
if (error.error.code === "unauthenticated")
|
|
111
|
+
return "auth_required";
|
|
112
|
+
if (error.error.code === "auth_expired")
|
|
113
|
+
return "auth_expired";
|
|
114
|
+
return error.error.code === "cancelled" ? "cancelled" : "provider_failed";
|
|
115
|
+
}
|
|
116
|
+
/** Why planning produced no plan, when the log shows a cause other than an invalid candidate. */
|
|
117
|
+
function planningFailure(events, runId) {
|
|
118
|
+
const mine = events.filter((event) => event.run_id === runId);
|
|
119
|
+
const askCalls = new Set(mine.flatMap((event) => (event.type === "tool/call_proposed" && event.data.tool_name === "ask_user" ? [event.data.tool_call_id] : [])));
|
|
120
|
+
const unanswered = mine.some((event) => event.type === "tool/result_recorded" && askCalls.has(event.data.tool_call_id) && event.data.result.error?.code === "approval_unavailable");
|
|
121
|
+
if (unanswered)
|
|
122
|
+
return "approval_unavailable";
|
|
123
|
+
for (let index = mine.length - 1; index >= 0; index -= 1) {
|
|
124
|
+
const event = mine[index];
|
|
125
|
+
if (event?.type === "model/response_settled")
|
|
126
|
+
return undefined;
|
|
127
|
+
if (event?.type === "model/response_failed")
|
|
128
|
+
return event.data.error.code === "cancelled" ? undefined : "provider_failed";
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
function observe(store, emit) {
|
|
133
|
+
return {
|
|
134
|
+
get sessionId() {
|
|
135
|
+
return store.sessionId;
|
|
136
|
+
},
|
|
137
|
+
get lastSeq() {
|
|
138
|
+
return store.lastSeq;
|
|
139
|
+
},
|
|
140
|
+
async append(draft) {
|
|
141
|
+
const event = await store.append(draft);
|
|
142
|
+
emit(event);
|
|
143
|
+
return event;
|
|
144
|
+
},
|
|
145
|
+
read: (fromSeq, toSeq) => store.read(fromSeq, toSeq),
|
|
146
|
+
close: () => store.close(),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function minDefined(...values) {
|
|
150
|
+
const defined = values.filter((value) => value !== undefined);
|
|
151
|
+
return defined.length === 0 ? undefined : Math.min(...defined);
|
|
152
|
+
}
|
|
153
|
+
function errorCodeOf(error) {
|
|
154
|
+
if (error instanceof HarnessError)
|
|
155
|
+
return error.info.code;
|
|
156
|
+
if (error instanceof ProviderFailure)
|
|
157
|
+
return providerErrorCode(error);
|
|
158
|
+
return "internal";
|
|
159
|
+
}
|
|
160
|
+
/** Resolves the per-task budgets: explicit `budgets` entries, then the legacy `maxRetries`/`maxRevisions`, then the defaults. */
|
|
161
|
+
export function resolveBudgets(limits) {
|
|
162
|
+
return orchestrationBudgetsSchema.parse({
|
|
163
|
+
triage_retries: limits?.budgets?.triage_retries ?? limits?.maxRetries,
|
|
164
|
+
evidence_repairs: limits?.budgets?.evidence_repairs,
|
|
165
|
+
review_revisions: limits?.budgets?.review_revisions ?? limits?.maxRevisions,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
export function createCoordinator(deps) {
|
|
169
|
+
const now = deps.now ?? (() => new Date());
|
|
170
|
+
const platform = deps.platform ?? process.platform;
|
|
171
|
+
const budgets = resolveBudgets(deps.limits);
|
|
172
|
+
const limits = {
|
|
173
|
+
...DEFAULT_COORDINATOR_LIMITS,
|
|
174
|
+
...deps.limits,
|
|
175
|
+
maxRetries: budgets.triage_retries,
|
|
176
|
+
maxRevisions: budgets.review_revisions,
|
|
177
|
+
budgets,
|
|
178
|
+
stepFloors: { ...DEFAULT_STEP_FLOORS, ...deps.limits?.stepFloors },
|
|
179
|
+
};
|
|
180
|
+
const listeners = new Set();
|
|
181
|
+
const steering = [];
|
|
182
|
+
let activeRecorder;
|
|
183
|
+
let runWorkers;
|
|
184
|
+
const fanOut = (event) => {
|
|
185
|
+
for (const listener of listeners) {
|
|
186
|
+
queueMicrotask(() => {
|
|
187
|
+
try {
|
|
188
|
+
listener(event);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const notice = (level, message) => fanOut({ kind: "notice", level, message });
|
|
197
|
+
const run = async (request, signal) => {
|
|
198
|
+
const runId = createId("run");
|
|
199
|
+
const projectId = deriveProjectId(request.workspaceRoot, platform);
|
|
200
|
+
const rawLog = request.resumeSessionId !== undefined
|
|
201
|
+
? await deps.sessions.openForWrite(request.resumeSessionId)
|
|
202
|
+
: await deps.sessions.create({
|
|
203
|
+
session_id: createId("session"),
|
|
204
|
+
project_id: projectId,
|
|
205
|
+
workspace_root: request.workspaceRoot,
|
|
206
|
+
created_at: now().toISOString(),
|
|
207
|
+
title: request.goal.slice(0, 200),
|
|
208
|
+
});
|
|
209
|
+
const log = observe(rawLog, (event) => fanOut({ kind: "session-event", event }));
|
|
210
|
+
const recorder = createRunRecorder(log, deps.blobs, runId);
|
|
211
|
+
activeRecorder = recorder;
|
|
212
|
+
const sources = deps.sources?.(request.workspaceRoot) ?? createWorkspaceSourceReader(request.workspaceRoot);
|
|
213
|
+
let runState = "created";
|
|
214
|
+
const pendingRecords = [];
|
|
215
|
+
const moveRun = async (to, reason) => {
|
|
216
|
+
const check = validateTransition("run", runState, to);
|
|
217
|
+
if (!check.ok)
|
|
218
|
+
throw new HarnessError({ code: "internal", message: check.message, workspace_effect: "unknown", retry_safe: false });
|
|
219
|
+
await recorder.record("run/state_changed", { from: runState, to, reason });
|
|
220
|
+
runState = to;
|
|
221
|
+
};
|
|
222
|
+
const finish = async (status, exitCode, summary, to) => {
|
|
223
|
+
if (runWorkers !== undefined)
|
|
224
|
+
runWorkers.ended = true;
|
|
225
|
+
if (!isTerminalState("run", runState))
|
|
226
|
+
await moveRun(to, summary.slice(0, 500));
|
|
227
|
+
deps.budgetGate?.set(undefined);
|
|
228
|
+
if (deps.delegation?.current()?.runId === runId)
|
|
229
|
+
deps.delegation.set(undefined);
|
|
230
|
+
await Promise.allSettled(pendingRecords);
|
|
231
|
+
activeRecorder = undefined;
|
|
232
|
+
await log.close().catch(() => undefined);
|
|
233
|
+
return { runId, sessionId: log.sessionId, status, exitCode, summary };
|
|
234
|
+
};
|
|
235
|
+
try {
|
|
236
|
+
await recorder.record("run/created", {
|
|
237
|
+
goal: request.goal,
|
|
238
|
+
policy_mode: request.policyMode,
|
|
239
|
+
headless: request.headless,
|
|
240
|
+
budget: {
|
|
241
|
+
...(request.budget.maxWallTimeSeconds === undefined ? {} : { max_wall_time_seconds: request.budget.maxWallTimeSeconds }),
|
|
242
|
+
...(request.budget.maxCostUsd === undefined ? {} : { max_cost_usd: request.budget.maxCostUsd }),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
await moveRun("running", "run started");
|
|
246
|
+
const orchestratorPolicy = deps.policy.compute({
|
|
247
|
+
mode: request.policyMode,
|
|
248
|
+
role: "orchestrator",
|
|
249
|
+
runId,
|
|
250
|
+
taskId: undefined,
|
|
251
|
+
workspaceRoot: request.workspaceRoot,
|
|
252
|
+
taskScope: { owned: [".ai/tasks/**"], read: ["**"], forbidden: [] },
|
|
253
|
+
userConfig: deps.userConfig,
|
|
254
|
+
workspaceConfig: deps.workspaceConfig,
|
|
255
|
+
sandbox: deps.sandbox,
|
|
256
|
+
grants: [],
|
|
257
|
+
});
|
|
258
|
+
await recorder.record("policy/snapshot", { policy: orchestratorPolicy, digest: digestOf(orchestratorPolicy) }, { actor: { kind: "policy" } });
|
|
259
|
+
const trust = deps.workspaceTrust?.();
|
|
260
|
+
const unconfined = orchestratorPolicy.exec_confinement !== "full-sandbox";
|
|
261
|
+
if (unconfined && trust?.trusted === true && trust.source !== undefined) {
|
|
262
|
+
await recorder.record("trust/used", { workspace_root: trust.root, repo_identity: trust.identity, source: trust.source, sandbox_enforcement: deps.sandbox.enforcement }, { actor: { kind: "system" } });
|
|
263
|
+
}
|
|
264
|
+
const ledger = deps.ledger
|
|
265
|
+
? createControlPlaneWriter({ workspaceRoot: request.workspaceRoot, policy: orchestratorPolicy, engine: deps.policy })
|
|
266
|
+
: undefined;
|
|
267
|
+
const orchestratorRoute = await deps.router.resolve({ tier: "orchestrator", role: "orchestrator" }, signal);
|
|
268
|
+
await recorder.record("route/decided", { decision: orchestratorRoute }, { actor: { kind: "system" } });
|
|
269
|
+
const preflightContext = {
|
|
270
|
+
policy: deps.policy,
|
|
271
|
+
mode: request.policyMode,
|
|
272
|
+
runId,
|
|
273
|
+
workspaceRoot: request.workspaceRoot,
|
|
274
|
+
sandbox: deps.sandbox,
|
|
275
|
+
userConfig: deps.userConfig,
|
|
276
|
+
workspaceConfig: deps.workspaceConfig,
|
|
277
|
+
};
|
|
278
|
+
const environmentNoted = new Set();
|
|
279
|
+
/**
|
|
280
|
+
* `validatePlan` plus the plan-time dry run of every verification command (live run 01M37V2J):
|
|
281
|
+
* a command the harness runner would refuse or record `not-run` rejects the candidate with the
|
|
282
|
+
* command, the refusal code and what to use instead. Refusals caused only by the environment
|
|
283
|
+
* (untrusted workspace, required full sandbox) are noticed once, not rejected.
|
|
284
|
+
*/
|
|
285
|
+
const checkPlan = (candidate, expected, include) => {
|
|
286
|
+
const validation = validatePlan(candidate, expected, { proportionalReview: limits.review === "proportional" });
|
|
287
|
+
if (!validation.ok)
|
|
288
|
+
return validation;
|
|
289
|
+
const preflight = preflightVerification(validation.plan.tasks, preflightContext, include);
|
|
290
|
+
for (const refusal of preflight.environment) {
|
|
291
|
+
if (environmentNoted.has(refusal.command))
|
|
292
|
+
continue;
|
|
293
|
+
environmentNoted.add(refusal.command);
|
|
294
|
+
notice("warning", `verification command "${refusal.command.slice(0, 200)}" of ${refusal.taskKey} will not run in this environment (${refusal.code}: ${refusal.reason.slice(0, 300)})`);
|
|
295
|
+
}
|
|
296
|
+
if (preflight.refusals.length === 0)
|
|
297
|
+
return validation;
|
|
298
|
+
return { ok: false, issues: preflight.refusals.map(formatVerificationRefusal) };
|
|
299
|
+
};
|
|
300
|
+
const planId = createId("plan");
|
|
301
|
+
let plan;
|
|
302
|
+
let planDigestValue;
|
|
303
|
+
const feedback = [];
|
|
304
|
+
let planRejections = 0;
|
|
305
|
+
let lastRejection = [];
|
|
306
|
+
const revisionsExhausted = () => planRejections > limits.maxPlanRevisions;
|
|
307
|
+
// While planning, `plan_propose` is validated in the orchestrator's own turn: an unworkable
|
|
308
|
+
// plan comes back as a structured rejection it can fix at once, within the revision limit.
|
|
309
|
+
deps.delegation?.set({
|
|
310
|
+
runId,
|
|
311
|
+
spawn: () => ({ ok: false, code: "execution_failed", message: "no approved plan is active yet; propose the plan with plan_propose" }),
|
|
312
|
+
status: () => ({ ok: false, code: "execution_failed", message: "no approved plan is active yet; propose the plan with plan_propose" }),
|
|
313
|
+
proposePlan(raw, caller) {
|
|
314
|
+
if (caller.role !== "orchestrator" || caller.runId !== runId)
|
|
315
|
+
return { ok: false, code: "policy_denied", message: "only the orchestrator of this run proposes its plan" };
|
|
316
|
+
if (revisionsExhausted()) {
|
|
317
|
+
return { ok: false, code: "policy_denied", message: `the plan revision limit (${limits.maxPlanRevisions}) is reached; the run stops without a plan. End your turn.` };
|
|
318
|
+
}
|
|
319
|
+
const validation = checkPlan({ ...raw, schema_version: 1, plan_id: planId, run_id: runId, version: 1, created_at: now().toISOString() }, { runId, planId, version: 1 });
|
|
320
|
+
if (validation.ok) {
|
|
321
|
+
const approval = request.policyMode === "ask" ? "the runtime now asks the user in its own interface" : "the runtime now approves it under the autonomous policy (audited)";
|
|
322
|
+
const moved = validation.notes ?? [];
|
|
323
|
+
const hints = validation.warnings ?? [];
|
|
324
|
+
return {
|
|
325
|
+
ok: true,
|
|
326
|
+
text: `plan accepted${moved.length > 0 ? ` with harness changes (${moved.join(" ")})` : ""}${hints.length > 0 ? `; harness warnings (non-blocking): ${hints.join(" ")}` : ""}; ${approval}. Do not ask for approval in text. End your turn now.`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
planRejections += 1;
|
|
330
|
+
lastRejection = validation.issues;
|
|
331
|
+
notice("warning", `plan_propose rejected (${planRejections}/${limits.maxPlanRevisions + 1}): ${validation.issues.slice(0, 3).join("; ")}`.slice(0, 1000));
|
|
332
|
+
const next = revisionsExhausted()
|
|
333
|
+
? "This was the last allowed revision; the run stops without a plan. End your turn."
|
|
334
|
+
: `Fix every problem and call plan_propose again with the whole corrected plan (${limits.maxPlanRevisions + 1 - planRejections} revision(s) left).`;
|
|
335
|
+
return { ok: false, code: "invalid_arguments", message: `plan rejected:\n${validation.issues.slice(0, 10).map((issue) => `- ${issue}`).join("\n")}\n${next}` };
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
for (let attempt = 1; attempt <= limits.maxPlanAttempts && plan === undefined && !revisionsExhausted(); attempt += 1) {
|
|
339
|
+
if (signal.aborted)
|
|
340
|
+
return await finish("cancelled", EXIT_CODES.cancelled, "cancelled while planning", "cancelled");
|
|
341
|
+
const rejectionsBefore = planRejections;
|
|
342
|
+
const candidate = await deps.planner.propose({
|
|
343
|
+
runId,
|
|
344
|
+
planId,
|
|
345
|
+
version: 1,
|
|
346
|
+
goal: request.goal,
|
|
347
|
+
workspaceRoot: request.workspaceRoot,
|
|
348
|
+
mode: request.policyMode,
|
|
349
|
+
createdAt: now().toISOString(),
|
|
350
|
+
feedback: [...feedback, ...steering.splice(0)],
|
|
351
|
+
route: orchestratorRoute.route,
|
|
352
|
+
policy: orchestratorPolicy,
|
|
353
|
+
events: log,
|
|
354
|
+
sessionId: log.sessionId,
|
|
355
|
+
}, signal);
|
|
356
|
+
const validation = checkPlan(candidate, { runId, planId, version: 1 });
|
|
357
|
+
if (validation.ok) {
|
|
358
|
+
plan = validation.plan;
|
|
359
|
+
planDigestValue = validation.digest;
|
|
360
|
+
for (const note of validation.notes ?? [])
|
|
361
|
+
notice("info", note);
|
|
362
|
+
for (const warning of validation.warnings ?? [])
|
|
363
|
+
notice("warning", `plan: ${warning}`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// A turn whose plan_propose calls were rejected in-turn already counted them; its last rejection is the useful feedback.
|
|
367
|
+
const rejectedInTurn = planRejections > rejectionsBefore;
|
|
368
|
+
if (!rejectedInTurn)
|
|
369
|
+
planRejections += 1;
|
|
370
|
+
const issues = rejectedInTurn && lastRejection.length > 0 ? lastRejection : validation.issues;
|
|
371
|
+
feedback.splice(0, feedback.length, ...issues);
|
|
372
|
+
notice("warning", `plan candidate ${attempt} rejected: ${issues.slice(0, 5).join("; ")}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (plan === undefined || planDigestValue === undefined) {
|
|
376
|
+
const cause = planningFailure(await readEvents(log), runId);
|
|
377
|
+
const why = cause === "approval_unavailable"
|
|
378
|
+
? "the orchestrator needed an answer from the user, but ask_user is unavailable in this run"
|
|
379
|
+
: cause === "provider_failed"
|
|
380
|
+
? "the orchestrator's model request failed"
|
|
381
|
+
: revisionsExhausted()
|
|
382
|
+
? `the plan was rejected ${planRejections} time(s) (revision limit ${limits.maxPlanRevisions}); last problems: ${feedback.slice(0, 5).join("; ")}`
|
|
383
|
+
: feedback.slice(0, 5).join("; ");
|
|
384
|
+
return await finish("failed", exitCodeFor(cause ?? "verification_failed"), `no valid plan: ${why}`, "failed");
|
|
385
|
+
}
|
|
386
|
+
await recorder.record("plan/proposed", { plan, digest: planDigestValue });
|
|
387
|
+
const recordApproval = async (approval) => {
|
|
388
|
+
await recorder.record("approval/requested", { request: approval.request }, { actor: { kind: "orchestrator", role: "orchestrator" } });
|
|
389
|
+
await recorder.record("approval/decided", { decision: approval.decision }, {
|
|
390
|
+
actor: approval.decision.decided_by === "user" ? { kind: "user" } : approval.decision.decided_by === "orchestrator" ? { kind: "orchestrator", role: "orchestrator" } : { kind: "system" },
|
|
391
|
+
});
|
|
392
|
+
};
|
|
393
|
+
if (request.policyMode === "ask")
|
|
394
|
+
await moveRun("waiting_for_approval", "plan approval requested");
|
|
395
|
+
const approval = await approvePlan({ plan, digest: planDigestValue, mode: request.policyMode, broker: deps.approvals, now }, signal);
|
|
396
|
+
await recordApproval(approval);
|
|
397
|
+
if (!approval.approved) {
|
|
398
|
+
await recorder.record("plan/state_changed", {
|
|
399
|
+
plan_id: plan.plan_id,
|
|
400
|
+
digest: planDigestValue,
|
|
401
|
+
from: "proposed",
|
|
402
|
+
to: "rejected",
|
|
403
|
+
reason: `plan ${approval.decision.outcome} by ${approval.decision.decided_by}`,
|
|
404
|
+
approval_id: approval.request.approval_id,
|
|
405
|
+
});
|
|
406
|
+
const code = approval.decision.outcome === "unavailable" ? "approval_unavailable" : "approval_rejected";
|
|
407
|
+
const cancelled = approval.decision.outcome === "cancelled" && signal.aborted;
|
|
408
|
+
return await finish(cancelled ? "cancelled" : "rejected", cancelled ? EXIT_CODES.cancelled : exitCodeFor(code), `plan not approved: ${approval.decision.outcome} (${approval.decision.decided_by})`, "cancelled");
|
|
409
|
+
}
|
|
410
|
+
await recorder.record("plan/state_changed", {
|
|
411
|
+
plan_id: plan.plan_id,
|
|
412
|
+
digest: planDigestValue,
|
|
413
|
+
from: "proposed",
|
|
414
|
+
to: "approved",
|
|
415
|
+
reason: `approved by ${approval.decision.decided_by} in ${approval.decision.mode} mode`,
|
|
416
|
+
approval_id: approval.request.approval_id,
|
|
417
|
+
});
|
|
418
|
+
if (request.policyMode === "ask")
|
|
419
|
+
await moveRun("running", "plan approved");
|
|
420
|
+
if (request.headless && orchestratorPolicy.exec_confinement === "allowlist" && trust !== undefined && !trust.trusted && needsTrust(plan)) {
|
|
421
|
+
notice("error", `workspace ${trust.root} is not trusted (${trust.reason ?? "no trust record"})`);
|
|
422
|
+
return await finish("rejected", exitCodeFor("approval_unavailable"), "workspace trust unavailable: the plan's verification commands run repository code this sandbox cannot confine, and the workspace is not trusted; run `syn trust` once, or pass --trust-workspace for this run", "cancelled");
|
|
423
|
+
}
|
|
424
|
+
if (ledger !== undefined) {
|
|
425
|
+
await ledger.write(`.ai/tasks/${runId}/plan.json`, `${JSON.stringify(plan, null, 2)}\n`).catch((error) => {
|
|
426
|
+
notice("warning", `ledger write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
const budget = createBudgetTracker({
|
|
430
|
+
scope: "run",
|
|
431
|
+
limits: {
|
|
432
|
+
maxCostUsd: minDefined(request.budget.maxCostUsd, plan.budget.max_cost_usd),
|
|
433
|
+
maxWallTimeSeconds: minDefined(request.budget.maxWallTimeSeconds, plan.budget.max_wall_time_seconds),
|
|
434
|
+
// Never below the steps the run guarantees its attempts (every task's limit, plus its reviewer's).
|
|
435
|
+
maxSteps: runStepLimit(plan, limits.stepFloors),
|
|
436
|
+
maxToolCalls: undefined,
|
|
437
|
+
},
|
|
438
|
+
onExceeded: (exceeded) => {
|
|
439
|
+
pendingRecords.push(recorder.record("budget/exceeded", exceeded, { actor: { kind: "system" } }).catch(() => undefined));
|
|
440
|
+
notice("warning", `budget ${exceeded.metric} reached (${exceeded.action})`);
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
deps.budgetGate?.set(budget);
|
|
444
|
+
const ticker = setInterval(() => budget.check(), deps.budgetTickMs ?? 1000);
|
|
445
|
+
ticker.unref?.();
|
|
446
|
+
const workers = deps.createWorkers({ runId, mode: request.policyMode, workspaceRoot: request.workspaceRoot, projectId, recorder, budgets: limits.budgets, finishWarning: limits.stepFloors.finishWarning }, budget);
|
|
447
|
+
let approvedPlan = plan;
|
|
448
|
+
let approvedDigest = planDigestValue;
|
|
449
|
+
const entries = new Map();
|
|
450
|
+
const newEntry = (task) => ({
|
|
451
|
+
key: task.key,
|
|
452
|
+
taskId: createId("task"),
|
|
453
|
+
plan: task,
|
|
454
|
+
state: "draft",
|
|
455
|
+
route: undefined,
|
|
456
|
+
attempts: [],
|
|
457
|
+
completion: undefined,
|
|
458
|
+
integrated: [],
|
|
459
|
+
summary: "",
|
|
460
|
+
failure: undefined,
|
|
461
|
+
notes: [],
|
|
462
|
+
nextNotes: [],
|
|
463
|
+
userMessages: [],
|
|
464
|
+
reuseFrom: undefined,
|
|
465
|
+
});
|
|
466
|
+
const createTask = async (entry) => {
|
|
467
|
+
await recorder.record("task/created", {
|
|
468
|
+
task_id: entry.taskId,
|
|
469
|
+
plan_id: approvedPlan.plan_id,
|
|
470
|
+
key: entry.key,
|
|
471
|
+
role: entry.plan.role,
|
|
472
|
+
depends_on: entry.plan.depends_on.map((key) => entries.get(key)?.taskId).filter((id) => id !== undefined),
|
|
473
|
+
owned_paths: entry.plan.owned_paths,
|
|
474
|
+
risk: entry.plan.risk,
|
|
475
|
+
}, { taskId: entry.taskId });
|
|
476
|
+
entry.route = await deps.router.resolve({ tier: entry.plan.model_tier, role: entry.plan.role }, signal);
|
|
477
|
+
};
|
|
478
|
+
for (const task of plan.tasks)
|
|
479
|
+
entries.set(task.key, newEntry(task));
|
|
480
|
+
for (const entry of entries.values())
|
|
481
|
+
await createTask(entry);
|
|
482
|
+
const move = async (entry, to, reason) => {
|
|
483
|
+
const check = validateTransition("task", entry.state, to);
|
|
484
|
+
if (!check.ok)
|
|
485
|
+
throw new HarnessError({ code: "internal", message: check.message, workspace_effect: "unknown", retry_safe: false });
|
|
486
|
+
await recorder.record("task/state_changed", { task_id: entry.taskId, from: entry.state, to, reason: reason.slice(0, 1000) }, { taskId: entry.taskId });
|
|
487
|
+
entry.state = to;
|
|
488
|
+
};
|
|
489
|
+
/** K1.7: the main chat's delegation line (task handed to a worker or reviewer attempt). */
|
|
490
|
+
const delegated = async (entry, attemptId) => {
|
|
491
|
+
const record = workers.attempt(attemptId);
|
|
492
|
+
if (record === undefined)
|
|
493
|
+
return;
|
|
494
|
+
await recorder
|
|
495
|
+
.record("task/delegated", {
|
|
496
|
+
task_id: entry.taskId,
|
|
497
|
+
attempt_id: attemptId,
|
|
498
|
+
key: entry.key,
|
|
499
|
+
role: record.packet.role,
|
|
500
|
+
provider_id: record.route.provider_id,
|
|
501
|
+
model_id: record.route.model_id,
|
|
502
|
+
objective: record.packet.objective.slice(0, 4000),
|
|
503
|
+
attempt: Math.max(1, entry.attempts.length),
|
|
504
|
+
}, { taskId: entry.taskId, attemptId, actor: { kind: "orchestrator", role: "orchestrator" } })
|
|
505
|
+
.catch(() => undefined);
|
|
506
|
+
};
|
|
507
|
+
const packetSources = async (entry) => {
|
|
508
|
+
const candidates = new Set();
|
|
509
|
+
for (const pattern of [...entry.plan.owned_paths, ...entry.plan.read_paths])
|
|
510
|
+
if (isLiteralPattern(pattern))
|
|
511
|
+
candidates.add(pattern);
|
|
512
|
+
for (const dependency of entry.plan.depends_on)
|
|
513
|
+
for (const path of entries.get(dependency)?.integrated ?? [])
|
|
514
|
+
candidates.add(path);
|
|
515
|
+
const current = await currentDigests([...candidates], sources);
|
|
516
|
+
return [...current.entries()].flatMap(([path, digest]) => (digest === undefined ? [] : [{ path, digest }]));
|
|
517
|
+
};
|
|
518
|
+
const findings = (entry) => entry.plan.depends_on.flatMap((key) => {
|
|
519
|
+
const dependency = entries.get(key);
|
|
520
|
+
if (dependency?.completion === undefined)
|
|
521
|
+
return [];
|
|
522
|
+
return [
|
|
523
|
+
`Finding from ${key}: ${dependency.completion.summary}`.slice(0, 2000),
|
|
524
|
+
...dependency.completion.recommended_context_updates.map((update) => `Context update from ${key}: ${update}`),
|
|
525
|
+
...dependency.notes.map((note) => `Note from ${key}: ${note}`.slice(0, 2000)),
|
|
526
|
+
];
|
|
527
|
+
});
|
|
528
|
+
/**
|
|
529
|
+
* A blocked route (exhausted quota) is never replaced silently: the router proposes a
|
|
530
|
+
* provider change, the broker asks a human, and only an allowing user decision applies it.
|
|
531
|
+
*/
|
|
532
|
+
const changeProvider = async (entry, failure) => {
|
|
533
|
+
let proposal;
|
|
534
|
+
try {
|
|
535
|
+
proposal = deps.router.proposeProviderChange(failure, { runId, taskId: entry.taskId });
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
await recorder.record("approval/requested", { request: proposal.request }, { taskId: entry.taskId, actor: { kind: "orchestrator", role: "orchestrator" } });
|
|
541
|
+
const decision = await deps.approvals.request(proposal.request, signal);
|
|
542
|
+
await recorder.record("approval/decided", { decision }, { taskId: entry.taskId, actor: decision.decided_by === "user" ? { kind: "user" } : { kind: "system" } });
|
|
543
|
+
try {
|
|
544
|
+
deps.router.applyProviderChange(decision);
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
catch {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
const issueDelta = async (entry, base, notes, evidence) => {
|
|
552
|
+
const delta = createDeltaPacket({ base, createdAt: now().toISOString(), notes: notes.slice(0, 20), evidence, newCriteria: [] });
|
|
553
|
+
const blob = await recorder.putJson(delta, PACKET_MEDIA_TYPE);
|
|
554
|
+
await recorder.record("task/packet_issued", { task_id: entry.taskId, kind: "delta", packet_digest: packetDigest(delta), blob }, { taskId: entry.taskId });
|
|
555
|
+
entry.nextNotes = deltaNotes(delta);
|
|
556
|
+
return applyDelta(base, delta);
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* An attempt the task moves on from (retry, re-package, revision). A worktree is kept so the next
|
|
560
|
+
* attempt reuses it (reset to its base, ADR-19; the task's final cleanup disposes it); any other
|
|
561
|
+
* workspace is disposed at once, as before.
|
|
562
|
+
*/
|
|
563
|
+
const retire = async (entry, attemptId) => {
|
|
564
|
+
if (workers.attempt(attemptId)?.workspace.mode === "worktree") {
|
|
565
|
+
entry.reuseFrom = attemptId;
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
await workers.dispose(attemptId);
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* Reviewer plan tasks that configure the mandatory review of `key` (tier, extra criteria, extra
|
|
572
|
+
* verification): only a reviewer task of that one task. An integration review (two or more
|
|
573
|
+
* dependencies) runs on its own over the combined result and never joins a per-task review.
|
|
574
|
+
*/
|
|
575
|
+
const reviewConfigurations = (key) => approvedPlan.tasks.filter((task) => task.role === "reviewer" && !isIntegrationReview(task) && task.depends_on.includes(key));
|
|
576
|
+
/** The reviewer tasks' criteria, renumbered after the implementation's so ids never collide. */
|
|
577
|
+
const reviewerExtras = (packet, configurations) => {
|
|
578
|
+
let next = Math.max(0, ...packet.acceptance_criteria.map((criterion) => Number(criterion.id.slice(3)))) + 1;
|
|
579
|
+
return configurations.flatMap((task) => task.acceptance_criteria.map((criterion) => ({ id: `AC-${next++}`, statement: `(reviewer task ${task.key}) ${criterion.statement}` })));
|
|
580
|
+
};
|
|
581
|
+
/**
|
|
582
|
+
* A worker's `partial` or self-reported `needs_context` is never retried blindly: the
|
|
583
|
+
* orchestrator is consulted (one turn, serialized with steering) and decides with
|
|
584
|
+
* `task_triage`. Without a triage-capable planner, or without a decision, the harness retries
|
|
585
|
+
* once with the report in the delta packet.
|
|
586
|
+
*/
|
|
587
|
+
const triageReport = async (entry, packet, completion, attemptId, retriesLeft, problems = [], cause = { planCaused: [], verificationOnly: false }, review = undefined) => {
|
|
588
|
+
const fallback = { kind: "retry", guidance: undefined };
|
|
589
|
+
if (deps.planner.triage === undefined || deps.delegation === undefined)
|
|
590
|
+
return fallback;
|
|
591
|
+
const record = workers.attempt(attemptId);
|
|
592
|
+
const acceptable = packet.write_mode !== "owned-paths" && (record?.changeSet?.changes.length ?? 0) === 0;
|
|
593
|
+
// Only a report that never reached verification or review can be sent there.
|
|
594
|
+
const reviewablePaths = packet.write_mode === "owned-paths" && problems.length === 0 && review === undefined && (completion.status === "partial" || completion.status === "needs_context")
|
|
595
|
+
? (record?.changeSet?.changes ?? []).map((change) => change.path)
|
|
596
|
+
: [];
|
|
597
|
+
const pending = {
|
|
598
|
+
reviewablePaths,
|
|
599
|
+
failOffered: false,
|
|
600
|
+
key: entry.key,
|
|
601
|
+
taskId: entry.taskId,
|
|
602
|
+
acceptable,
|
|
603
|
+
criteria: packet.acceptance_criteria.map((criterion) => criterion.id),
|
|
604
|
+
retriesLeft,
|
|
605
|
+
planCaused: cause.planCaused,
|
|
606
|
+
verificationOnly: cause.verificationOnly,
|
|
607
|
+
reviewOverride: review?.override === true,
|
|
608
|
+
decision: undefined,
|
|
609
|
+
};
|
|
610
|
+
await orchestratorTurn(async () => {
|
|
611
|
+
triaging = pending;
|
|
612
|
+
consulting = true;
|
|
613
|
+
try {
|
|
614
|
+
await deps.planner.triage?.({
|
|
615
|
+
runId,
|
|
616
|
+
goal: request.goal,
|
|
617
|
+
planVersion: approvedPlan.version,
|
|
618
|
+
task: { key: entry.key, taskId: entry.taskId, role: packet.role, risk: packet.risk, writeMode: packet.write_mode },
|
|
619
|
+
attempt: entry.attempts.length,
|
|
620
|
+
status: completion.status,
|
|
621
|
+
criteria: packet.acceptance_criteria.map((criterion) => {
|
|
622
|
+
const command = canRunCommands(packet.role) ? undefined : commandMentioned(criterion.statement, packet.verification.commands);
|
|
623
|
+
const state = criterionEvidence(completion, criterion.id);
|
|
624
|
+
return {
|
|
625
|
+
id: criterion.id,
|
|
626
|
+
statement: criterion.statement,
|
|
627
|
+
evidenced: state.status === "resolved",
|
|
628
|
+
evidence: state.status,
|
|
629
|
+
reason: state.reason,
|
|
630
|
+
capabilityNote: command === undefined ? undefined : `it needs "${command}" to run, which a ${packet.role} cannot do`,
|
|
631
|
+
};
|
|
632
|
+
}),
|
|
633
|
+
problems,
|
|
634
|
+
planCaused: cause.planCaused,
|
|
635
|
+
verificationOnly: cause.verificationOnly,
|
|
636
|
+
...(review === undefined ? {} : { reviewOverride: review.override, reviewNote: review.note }),
|
|
637
|
+
ownedPaths: packet.scope.owned_paths,
|
|
638
|
+
harnessChecks: (completion.harness_evidence?.verification ?? []).map((check) => `${check.command}: ${check.status}${check.exit_code === null ? "" : ` (exit ${check.exit_code})`}`),
|
|
639
|
+
workerMessages: workerMessages.splice(0),
|
|
640
|
+
summary: completion.summary,
|
|
641
|
+
skippedChecks: completion.skipped_checks.map((check) => `${check.check}: ${check.reason}`),
|
|
642
|
+
unresolvedRisks: completion.unresolved_risks,
|
|
643
|
+
acceptable,
|
|
644
|
+
...(reviewablePaths.length === 0 ? {} : { reviewablePaths }),
|
|
645
|
+
retriesLeft,
|
|
646
|
+
tasks: [...entries.values()].map(taskLine),
|
|
647
|
+
route: orchestratorRoute.route,
|
|
648
|
+
policy: orchestratorPolicy,
|
|
649
|
+
events: log,
|
|
650
|
+
sessionId: log.sessionId,
|
|
651
|
+
}, signal);
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
notice("warning", `orchestrator triage of ${entry.key} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
655
|
+
}
|
|
656
|
+
finally {
|
|
657
|
+
triaging = undefined;
|
|
658
|
+
consulting = false;
|
|
659
|
+
}
|
|
660
|
+
const extra = spawned.splice(0);
|
|
661
|
+
if (extra.length > 0)
|
|
662
|
+
await applyRevision([], extra, `follow-up tasks from the triage of ${entry.key}`);
|
|
663
|
+
});
|
|
664
|
+
const decided = pending.decision ?? (reviewablePaths.length > 0 ? { kind: "review", guidance: undefined } : fallback);
|
|
665
|
+
notice("info", `triage of ${entry.key} (${completion.status}): ${decided.kind}${pending.decision === undefined ? " (no decision; harness default)" : ""}${reviewablePaths.length > 0 ? `; produced ${reviewablePaths.join(", ")}${pending.failOffered ? ", review was offered before fail" : ""}` : ""}${decided.guidance === undefined ? "" : ` - ${decided.guidance}`}`.slice(0, 1000));
|
|
666
|
+
return decided;
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* The plan's integration review (`isIntegrationReview`): its dependencies each completed through
|
|
670
|
+
* their own verification and review and are integrated, so one reviewer attempt checks the
|
|
671
|
+
* reviewer task's own criteria over the combined workspace. A non-accepting verdict consults
|
|
672
|
+
* the orchestrator (accept with notes / retry the review / fail); it never undoes the
|
|
673
|
+
* integrated, individually reviewed work.
|
|
674
|
+
*/
|
|
675
|
+
const runIntegrationReview = async (entry) => {
|
|
676
|
+
const dependencies = entry.plan.depends_on.flatMap((key) => {
|
|
677
|
+
const dependency = entries.get(key);
|
|
678
|
+
return dependency === undefined ? [] : [{ key, taskId: dependency.taskId, summary: dependency.summary, paths: dependency.integrated, notes: dependency.notes }];
|
|
679
|
+
});
|
|
680
|
+
const covered = entry.plan.depends_on.join(", ");
|
|
681
|
+
const packet = compileTaskPacket({
|
|
682
|
+
plan: approvedPlan,
|
|
683
|
+
planDigest: approvedDigest,
|
|
684
|
+
task: entry.plan,
|
|
685
|
+
taskId: entry.taskId,
|
|
686
|
+
createdAt: now().toISOString(),
|
|
687
|
+
sources: await packetSources(entry),
|
|
688
|
+
findings: findings(entry),
|
|
689
|
+
forbiddenPaths: [],
|
|
690
|
+
preferWorktree: false,
|
|
691
|
+
stepFloor: limits.stepFloors.reviewer,
|
|
692
|
+
...(deps.personas === undefined ? {} : { personas: deps.personas }),
|
|
693
|
+
});
|
|
694
|
+
let retries = 0;
|
|
695
|
+
for (;;) {
|
|
696
|
+
if (signal.aborted) {
|
|
697
|
+
await move(entry, "cancelled", "run cancelled");
|
|
698
|
+
return "failed";
|
|
699
|
+
}
|
|
700
|
+
let outcome;
|
|
701
|
+
try {
|
|
702
|
+
const route = await deps.router.resolve({ tier: entry.plan.model_tier, role: "reviewer" }, signal);
|
|
703
|
+
for (let review = 1; review <= limits.maxReviewAttempts; review += 1) {
|
|
704
|
+
const handle = await workers.dispatchIntegrationReview(packet, dependencies, signal, { route });
|
|
705
|
+
entry.attempts.push(handle.attemptId);
|
|
706
|
+
await delegated(entry, handle.attemptId);
|
|
707
|
+
if (entry.state === "ready")
|
|
708
|
+
await move(entry, "running", `integration review attempt ${handle.attemptId} of ${covered} dispatched`);
|
|
709
|
+
outcome = await handle.result;
|
|
710
|
+
if (outcome.decision !== "invalid")
|
|
711
|
+
break;
|
|
712
|
+
notice("warning", `integration review ${review} of ${entry.key} is invalid: ${outcome.problems.slice(0, 3).join("; ")}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
entry.failure = errorCodeOf(error);
|
|
717
|
+
await move(entry, entry.state === "ready" ? "blocked" : "failed", `integration review dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
718
|
+
return "failed";
|
|
719
|
+
}
|
|
720
|
+
if (outcome === undefined) {
|
|
721
|
+
await move(entry, "failed", "no integration review ran");
|
|
722
|
+
return "failed";
|
|
723
|
+
}
|
|
724
|
+
await move(entry, "verifying", `integration review of ${covered} recorded: ${outcome.decision}`);
|
|
725
|
+
await move(entry, "reviewing", `integration review over the combined result of ${covered}`);
|
|
726
|
+
if (outcome.decision === "accept") {
|
|
727
|
+
entry.summary = `integration review of ${covered} accepted`;
|
|
728
|
+
await move(entry, "completed", entry.summary);
|
|
729
|
+
return "completed";
|
|
730
|
+
}
|
|
731
|
+
const report = completionPacketSchema.parse({
|
|
732
|
+
schema_version: 2,
|
|
733
|
+
task_id: entry.taskId,
|
|
734
|
+
attempt_id: outcome.attemptId,
|
|
735
|
+
packet_digest: packetDigest(packet),
|
|
736
|
+
status: "partial",
|
|
737
|
+
summary: `The integration review of ${covered} did not accept (${outcome.decision}).`,
|
|
738
|
+
changed_paths: [],
|
|
739
|
+
tool_call_ids: [],
|
|
740
|
+
acceptance_evidence: [],
|
|
741
|
+
commands_run: [],
|
|
742
|
+
decisions_made: [],
|
|
743
|
+
skipped_checks: [],
|
|
744
|
+
unresolved_risks: outcome.problems.slice(0, 20).map((problem) => problem.slice(0, 1000)),
|
|
745
|
+
recommended_context_updates: [],
|
|
746
|
+
});
|
|
747
|
+
const decision = await triageReport(entry, packet, report, outcome.attemptId, limits.budgets.triage_retries - retries, outcome.problems, undefined, {
|
|
748
|
+
override: false,
|
|
749
|
+
note: `integration review of ${covered} (already integrated, each individually reviewed): ${outcome.decision}`,
|
|
750
|
+
});
|
|
751
|
+
if (decision.kind === "accept") {
|
|
752
|
+
entry.notes.push(...outcome.problems.slice(0, 10).map((problem) => `integration review finding accepted by the orchestrator: ${problem}`.slice(0, NOTE_LIMIT)));
|
|
753
|
+
entry.summary = `integration review of ${covered} accepted by the orchestrator with notes`;
|
|
754
|
+
await move(entry, "completed", `${entry.summary}: ${outcome.problems.slice(0, 5).join("; ")}${decision.guidance === undefined ? "" : ` (${decision.guidance})`}`);
|
|
755
|
+
return "completed";
|
|
756
|
+
}
|
|
757
|
+
if (decision.kind === "retry" && retries < limits.budgets.triage_retries) {
|
|
758
|
+
retries += 1;
|
|
759
|
+
await move(entry, "changes_requested", `integration review ${outcome.decision}: ${outcome.problems.slice(0, 5).join("; ")}`);
|
|
760
|
+
await move(entry, "ready", `integration review retry ${retries}`);
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
entry.failure = outcome.decision === "block" ? "review_blocked" : "verification_failed";
|
|
764
|
+
await move(entry, "failed", `integration review ${outcome.decision}: ${outcome.problems.slice(0, 5).join("; ")}${decision.kind === "fail" ? " | triage: the orchestrator failed the task" : ""}`);
|
|
765
|
+
return "failed";
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
const runTask = async (entry) => {
|
|
769
|
+
await move(entry, "ready", "plan approved and dependencies completed");
|
|
770
|
+
if (entry.plan.role === "reviewer" && isIntegrationReview(entry.plan))
|
|
771
|
+
return await runIntegrationReview(entry);
|
|
772
|
+
if (entry.plan.role === "reviewer") {
|
|
773
|
+
// A reviewer plan task of one dependency never dispatches: each dependency (standard/high-risk by plan validation) already
|
|
774
|
+
// passed its own independent review, run with this task's tier, extra criteria and verification.
|
|
775
|
+
const covered = entry.plan.depends_on.join(", ");
|
|
776
|
+
await move(entry, "running", `no attempt: reviewer task configures the independent review of ${covered}`);
|
|
777
|
+
await move(entry, "verifying", `dependencies ${covered} completed (through their independent review where one was warranted)`);
|
|
778
|
+
entry.summary = `covered by the checks of ${covered}`;
|
|
779
|
+
await move(entry, "completed", entry.summary);
|
|
780
|
+
return "completed";
|
|
781
|
+
}
|
|
782
|
+
let packet = compileTaskPacket({
|
|
783
|
+
plan: approvedPlan,
|
|
784
|
+
planDigest: approvedDigest,
|
|
785
|
+
task: entry.plan,
|
|
786
|
+
taskId: entry.taskId,
|
|
787
|
+
createdAt: now().toISOString(),
|
|
788
|
+
sources: await packetSources(entry),
|
|
789
|
+
findings: findings(entry),
|
|
790
|
+
forbiddenPaths: [],
|
|
791
|
+
preferWorktree: deps.preferWorktree ?? true,
|
|
792
|
+
stepFloor: limits.stepFloors.worker,
|
|
793
|
+
...(deps.personas === undefined ? {} : { personas: deps.personas }),
|
|
794
|
+
});
|
|
795
|
+
let retries = 0;
|
|
796
|
+
let revisions = 0;
|
|
797
|
+
let repackages = 0;
|
|
798
|
+
let seed;
|
|
799
|
+
for (;;) {
|
|
800
|
+
if (signal.aborted) {
|
|
801
|
+
await move(entry, "cancelled", "run cancelled");
|
|
802
|
+
return "failed";
|
|
803
|
+
}
|
|
804
|
+
const admission = budget.exhausted();
|
|
805
|
+
if (!admission.ok) {
|
|
806
|
+
budget.admit();
|
|
807
|
+
await move(entry, "cancelled", `budget ${admission.metric} exhausted`);
|
|
808
|
+
return "failed";
|
|
809
|
+
}
|
|
810
|
+
let handle;
|
|
811
|
+
try {
|
|
812
|
+
if (entry.attempts.length > 0)
|
|
813
|
+
entry.route = await deps.router.resolve({ tier: entry.plan.model_tier, role: entry.plan.role }, signal);
|
|
814
|
+
handle = await workers.dispatch(packet, signal, {
|
|
815
|
+
...(entry.route === undefined ? {} : { route: entry.route }),
|
|
816
|
+
...(seed === undefined ? {} : { seedArtifact: seed }),
|
|
817
|
+
...(entry.nextNotes.length === 0 ? {} : { notes: entry.nextNotes }),
|
|
818
|
+
...(entry.reuseFrom === undefined ? {} : { reuseAttempt: entry.reuseFrom }),
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
catch (error) {
|
|
822
|
+
if (errorCodeOf(error) === "stale_packet" && repackages < limits.maxRepackages) {
|
|
823
|
+
repackages += 1;
|
|
824
|
+
packet = refreshPacketSources(packet, await currentDigests(packet.context.sources.map((source) => source.path), sources), now().toISOString());
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (error instanceof RouteBlockedFailure && (await changeProvider(entry, error))) {
|
|
828
|
+
entry.route = await deps.router.resolve({ tier: entry.plan.model_tier, role: entry.plan.role }, signal);
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
entry.failure = error instanceof RouteBlockedFailure ? "provider_failed" : error instanceof HarnessError || error instanceof ProviderFailure ? errorCodeOf(error) : entry.failure;
|
|
832
|
+
await move(entry, "blocked", `dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
833
|
+
return "failed";
|
|
834
|
+
}
|
|
835
|
+
entry.attempts.push(handle.attemptId);
|
|
836
|
+
await delegated(entry, handle.attemptId);
|
|
837
|
+
entry.nextNotes = [];
|
|
838
|
+
entry.reuseFrom = undefined;
|
|
839
|
+
await move(entry, "running", `attempt ${handle.attemptId} dispatched`);
|
|
840
|
+
const completion = await handle.completion;
|
|
841
|
+
entry.completion = completion;
|
|
842
|
+
entry.summary = completion.summary;
|
|
843
|
+
entry.failure = workers.attempt(handle.attemptId)?.failure;
|
|
844
|
+
seed = undefined;
|
|
845
|
+
if (userCancelled.has(handle.attemptId)) {
|
|
846
|
+
// K1.7: the user cancelled this attempt; that is intent, not a failure to retry.
|
|
847
|
+
await workers.revert(handle.attemptId, signal).catch(() => undefined);
|
|
848
|
+
await retire(entry, handle.attemptId);
|
|
849
|
+
await move(entry, "cancelled", "cancelled by you");
|
|
850
|
+
return "failed";
|
|
851
|
+
}
|
|
852
|
+
const retry = async (reason, notes) => {
|
|
853
|
+
if (retries >= limits.budgets.triage_retries)
|
|
854
|
+
return false;
|
|
855
|
+
retries += 1;
|
|
856
|
+
await workers.revert(handle.attemptId, signal);
|
|
857
|
+
await retire(entry, handle.attemptId);
|
|
858
|
+
await move(entry, "retry_pending", reason);
|
|
859
|
+
packet = await issueDelta(entry, packet, notes, []);
|
|
860
|
+
await move(entry, "ready", `retry ${retries} with a new attempt`);
|
|
861
|
+
return true;
|
|
862
|
+
};
|
|
863
|
+
if (completion.status === "needs_context" && workers.attempt(handle.attemptId)?.stale !== undefined) {
|
|
864
|
+
await move(entry, "needs_context", completion.summary);
|
|
865
|
+
if (repackages >= limits.maxRepackages) {
|
|
866
|
+
entry.failure = "stale_packet";
|
|
867
|
+
await move(entry, "cancelled", "context could not be refreshed within the repackage limit");
|
|
868
|
+
return "failed";
|
|
869
|
+
}
|
|
870
|
+
repackages += 1;
|
|
871
|
+
await workers.revert(handle.attemptId, signal);
|
|
872
|
+
await retire(entry, handle.attemptId);
|
|
873
|
+
packet = refreshPacketSources(packet, await currentDigests(packet.context.sources.map((source) => source.path), sources), now().toISOString());
|
|
874
|
+
await move(entry, "ready", "re-packaged with fresh sources");
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (completion.status === "blocked") {
|
|
878
|
+
await move(entry, "blocked", completion.summary);
|
|
879
|
+
return "failed";
|
|
880
|
+
}
|
|
881
|
+
let waived;
|
|
882
|
+
// Live run 01M3ABTS: a worker that wrote its owned file but reported partial (its own extra check was
|
|
883
|
+
// refused) goes to harness verification and independent review with its caveats; the reviewer decides.
|
|
884
|
+
const produced = packet.write_mode === "owned-paths" ? (workers.attempt(handle.attemptId)?.changeSet?.changes ?? []).map((change) => change.path) : [];
|
|
885
|
+
let partialArtifact = completion.status === "partial" && produced.length > 0;
|
|
886
|
+
if (partialArtifact)
|
|
887
|
+
notice("info", `${entry.key} reported partial but produced ${produced.join(", ")}: it goes to verification and independent review with the worker's caveats`);
|
|
888
|
+
if (!partialArtifact && (completion.status === "partial" || completion.status === "needs_context")) {
|
|
889
|
+
const claimedContext = completion.status === "needs_context";
|
|
890
|
+
if (claimedContext)
|
|
891
|
+
await move(entry, "needs_context", completion.summary);
|
|
892
|
+
const decision = await triageReport(entry, packet, completion, handle.attemptId, limits.budgets.triage_retries - retries);
|
|
893
|
+
const notes = retryNotes(completion, packet.acceptance_criteria, decision.guidance);
|
|
894
|
+
if (decision.kind === "review") {
|
|
895
|
+
partialArtifact = true;
|
|
896
|
+
if (claimedContext)
|
|
897
|
+
await move(entry, "running", "triage: the orchestrator sent the produced change to review");
|
|
898
|
+
}
|
|
899
|
+
else if (decision.kind === "accept") {
|
|
900
|
+
waived = decision.waived;
|
|
901
|
+
if (claimedContext)
|
|
902
|
+
await move(entry, "running", "triage: the orchestrator accepted the findings");
|
|
903
|
+
}
|
|
904
|
+
else if (decision.kind === "retry" && retries < limits.budgets.triage_retries) {
|
|
905
|
+
if (claimedContext) {
|
|
906
|
+
retries += 1;
|
|
907
|
+
await workers.revert(handle.attemptId, signal);
|
|
908
|
+
await retire(entry, handle.attemptId);
|
|
909
|
+
packet = await issueDelta(entry, packet, notes, []);
|
|
910
|
+
await move(entry, "ready", `retry ${retries} with a new attempt and the previous report`);
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
await move(entry, "failed", `attempt ${completion.status}: ${completion.summary}`);
|
|
914
|
+
if (await retry("retrying with the previous report", notes))
|
|
915
|
+
continue;
|
|
916
|
+
return "failed";
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
entry.failure ??= "verification_failed";
|
|
920
|
+
const why = decision.kind === "fail" ? `triage: the orchestrator failed the task${decision.guidance === undefined ? "" : ` (${decision.guidance})`}` : "no retries left";
|
|
921
|
+
await move(entry, claimedContext ? "cancelled" : "failed", `attempt ${completion.status}: ${completion.summary} | ${why}`);
|
|
922
|
+
return "failed";
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
else if (!partialArtifact && completion.status !== "completed") {
|
|
926
|
+
await move(entry, "failed", `attempt ${completion.status}: ${completion.summary}`);
|
|
927
|
+
if (await retry("retrying after a failed attempt", retryNotes(completion, packet.acceptance_criteria, undefined)))
|
|
928
|
+
continue;
|
|
929
|
+
return "failed";
|
|
930
|
+
}
|
|
931
|
+
const verifyAttempt = (options) => workers.verify(handle.attemptId, partialArtifact ? { ...options, partialArtifact: true } : options);
|
|
932
|
+
await move(entry, "verifying", partialArtifact
|
|
933
|
+
? `${completion.status} report with a produced change; the reviewer decides`
|
|
934
|
+
: waived === undefined
|
|
935
|
+
? "completion received"
|
|
936
|
+
: `triage accepted the findings${waived.length > 0 ? `; waived ${waived.join(", ")}` : ""}`);
|
|
937
|
+
let verification = await verifyAttempt(waived === undefined ? undefined : { waivedCriteria: waived });
|
|
938
|
+
let waivedCommands = [];
|
|
939
|
+
if (verification.decision === "revise" && waived === undefined) {
|
|
940
|
+
// ADR-18 D2: the in-session repairs are spent (the worker manager repaired within its budget); the work
|
|
941
|
+
// is never dropped silently: the orchestrator decides with the resolved evidence and what is still wrong.
|
|
942
|
+
// Plan-caused verification problems never went to the worker; the orchestrator may waive or replace them.
|
|
943
|
+
const planCaused = verification.planCaused ?? [];
|
|
944
|
+
const planProblems = new Set(verification.planProblems ?? []);
|
|
945
|
+
const verificationOnly = planCaused.length > 0 && verification.problems.every((problem) => planProblems.has(problem));
|
|
946
|
+
const decision = await triageReport(entry, packet, completion, handle.attemptId, limits.budgets.triage_retries - retries, verification.problems, { planCaused, verificationOnly });
|
|
947
|
+
if (decision.kind === "accept" && decision.waivedCommands !== undefined) {
|
|
948
|
+
waivedCommands = decision.waivedCommands;
|
|
949
|
+
verification = await verifyAttempt({ waivedCommands });
|
|
950
|
+
entry.notes.push(...waivedCommands.map((command) => `verification "${command}" could not run and was waived by the orchestrator in triage${decision.guidance === undefined ? "" : ` (${decision.guidance})`}`.slice(0, NOTE_LIMIT)));
|
|
951
|
+
}
|
|
952
|
+
else if (decision.kind === "retry" && decision.verification !== undefined && retries < limits.budgets.triage_retries) {
|
|
953
|
+
const artifactBytes = workers.attempt(handle.attemptId)?.changeSet?.artifactBytes;
|
|
954
|
+
if (!(await reviseVerification(entry, decision.verification))) {
|
|
955
|
+
entry.failure = "verification_failed";
|
|
956
|
+
await move(entry, "failed", `verification could not run (plan-caused) and its revision was not approved: ${planCaused.join("; ")}`);
|
|
957
|
+
return "failed";
|
|
958
|
+
}
|
|
959
|
+
retries += 1;
|
|
960
|
+
entry.failure = undefined;
|
|
961
|
+
await move(entry, "failed", `verification could not run (plan-caused: ${planCaused.join("; ")}); the orchestrator revised it`);
|
|
962
|
+
seed = artifactBytes;
|
|
963
|
+
await workers.revert(handle.attemptId, signal);
|
|
964
|
+
await retire(entry, handle.attemptId);
|
|
965
|
+
await move(entry, "retry_pending", "triage: verification revised in plan");
|
|
966
|
+
packet = compileTaskPacket({
|
|
967
|
+
plan: approvedPlan,
|
|
968
|
+
planDigest: approvedDigest,
|
|
969
|
+
task: entry.plan,
|
|
970
|
+
taskId: entry.taskId,
|
|
971
|
+
createdAt: now().toISOString(),
|
|
972
|
+
sources: await packetSources(entry),
|
|
973
|
+
findings: findings(entry),
|
|
974
|
+
forbiddenPaths: [],
|
|
975
|
+
preferWorktree: deps.preferWorktree ?? true,
|
|
976
|
+
stepFloor: limits.stepFloors.worker,
|
|
977
|
+
...(deps.personas === undefined ? {} : { personas: deps.personas }),
|
|
978
|
+
});
|
|
979
|
+
entry.nextNotes = [
|
|
980
|
+
`The orchestrator replaced this task's verification (it could not run: ${planCaused.join("; ")}) with: ${decision.verification.join("; ")}.`.slice(0, NOTE_LIMIT),
|
|
981
|
+
"Your previous change is already in your workspace: do not redo it. Make the new verification pass (a check script it runs must be inside your owned paths), run it, then report.",
|
|
982
|
+
...(decision.guidance === undefined ? [] : [`Orchestrator guidance: ${decision.guidance}`.slice(0, NOTE_LIMIT)]),
|
|
983
|
+
];
|
|
984
|
+
await move(entry, "ready", `retry ${retries} with the revised verification`);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
else if (decision.kind === "accept") {
|
|
988
|
+
waived = decision.waived;
|
|
989
|
+
verification = await verifyAttempt({ waivedCriteria: waived });
|
|
990
|
+
}
|
|
991
|
+
else if (decision.kind === "retry" && retries < limits.budgets.triage_retries) {
|
|
992
|
+
entry.failure = "verification_failed";
|
|
993
|
+
await move(entry, "failed", `verification revise: ${verification.problems.slice(0, 5).join("; ")}`);
|
|
994
|
+
if (await retry("triage: retrying after failed verification", retryNotes(completion, packet.acceptance_criteria, decision.guidance, verification.problems)))
|
|
995
|
+
continue;
|
|
996
|
+
return "failed";
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
entry.failure = "verification_failed";
|
|
1000
|
+
const why = decision.kind === "fail" ? `triage: the orchestrator failed the task${decision.guidance === undefined ? "" : ` (${decision.guidance})`}` : "no retries left";
|
|
1001
|
+
await move(entry, "failed", `verification revise: ${verification.problems.slice(0, 5).join("; ")} | ${why}`);
|
|
1002
|
+
return "failed";
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (waived !== undefined && waived.length > 0 && verification.decision === "pass") {
|
|
1006
|
+
const waivedSet = new Set(waived);
|
|
1007
|
+
entry.notes = packet.acceptance_criteria
|
|
1008
|
+
.filter((criterion) => waivedSet.has(criterion.id))
|
|
1009
|
+
.map((criterion) => `not established by this ${packet.role} (waived by the orchestrator in triage): ${criterion.id} ${criterion.statement}; establish it here if your task depends on it`);
|
|
1010
|
+
}
|
|
1011
|
+
if (verification.decision !== "pass") {
|
|
1012
|
+
entry.failure = "verification_failed";
|
|
1013
|
+
await move(entry, "failed", `verification ${verification.decision}: ${verification.problems.slice(0, 5).join("; ")}`);
|
|
1014
|
+
if (await retry("retrying after failed verification", verification.problems))
|
|
1015
|
+
continue;
|
|
1016
|
+
return "failed";
|
|
1017
|
+
}
|
|
1018
|
+
const record = workers.attempt(handle.attemptId);
|
|
1019
|
+
const artifact = record?.changeSet;
|
|
1020
|
+
const writingTasks = writingTaskCount(approvedPlan);
|
|
1021
|
+
const warranted = limits.review === "always" ? packet.risk !== "trivial" : reviewWarranted(packet, { writingTasks, changes: artifact?.changes ?? [] });
|
|
1022
|
+
if (!warranted && !partialArtifact) {
|
|
1023
|
+
if (artifact !== undefined && artifact.changes.length > 0) {
|
|
1024
|
+
try {
|
|
1025
|
+
await workers.integrate(handle.attemptId, artifact.artifactDigest, signal);
|
|
1026
|
+
entry.integrated = artifact.changes.map((change) => change.path);
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
await move(entry, "failed", `integration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1030
|
+
return "failed";
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
await move(entry, "completed", packet.risk === "trivial" ? "verified; trivial risk needs no review" : `verified; no independent review needed (${writingTasks <= 1 ? "single-task plan" : "new files only"})`);
|
|
1034
|
+
return "completed";
|
|
1035
|
+
}
|
|
1036
|
+
await move(entry, "reviewing", waivedCommands.length === 0 ? "independent review required" : `independent review required; triage waived verification that could not run: ${waivedCommands.join("; ")}`);
|
|
1037
|
+
if (artifact === undefined) {
|
|
1038
|
+
await move(entry, "failed", "no pinned artifact to review");
|
|
1039
|
+
return "failed";
|
|
1040
|
+
}
|
|
1041
|
+
const configurations = reviewConfigurations(entry.key);
|
|
1042
|
+
let outcome = "invalid";
|
|
1043
|
+
let problems = [];
|
|
1044
|
+
let findingsEvidence = [];
|
|
1045
|
+
let blocker = false;
|
|
1046
|
+
let noteOnly = false;
|
|
1047
|
+
let widened = false;
|
|
1048
|
+
for (let review = 1; review <= limits.maxReviewAttempts; review += 1) {
|
|
1049
|
+
const reviewerPacket = compileReviewerPacket({
|
|
1050
|
+
implementation: packet,
|
|
1051
|
+
artifactDigest: artifact.artifactDigest,
|
|
1052
|
+
createdAt: now().toISOString(),
|
|
1053
|
+
reviewerTier: configurations[0]?.model_tier,
|
|
1054
|
+
extraCriteria: reviewerExtras(packet, configurations),
|
|
1055
|
+
extraVerification: configurations.flatMap((task) => task.verification),
|
|
1056
|
+
...(waived === undefined ? {} : { waivedCriteria: waived }),
|
|
1057
|
+
maxSteps: reviewerStepLimit(packet.limits.max_steps, limits.stepFloors),
|
|
1058
|
+
...(widened || partialArtifact ? { notes: [...(widened ? [SCOPE_NOTE] : []), ...(partialArtifact ? partialCaveats(completion) : [])] } : {}),
|
|
1059
|
+
});
|
|
1060
|
+
const reviewerRoute = await deps.router.resolve({ tier: reviewerPacket.model_tier, role: "reviewer", ...(record?.route === undefined ? {} : { implementer: record.route }) }, signal);
|
|
1061
|
+
const reviewHandle = await workers.dispatchReview(reviewerPacket, handle.attemptId, signal, { route: reviewerRoute });
|
|
1062
|
+
entry.attempts.push(reviewHandle.attemptId);
|
|
1063
|
+
await delegated(entry, reviewHandle.attemptId);
|
|
1064
|
+
const result = await reviewHandle.result;
|
|
1065
|
+
if (userCancelled.has(reviewHandle.attemptId)) {
|
|
1066
|
+
await workers.revert(handle.attemptId, signal).catch(() => undefined);
|
|
1067
|
+
await retire(entry, handle.attemptId);
|
|
1068
|
+
await move(entry, "cancelled", "review cancelled by you");
|
|
1069
|
+
return "failed";
|
|
1070
|
+
}
|
|
1071
|
+
outcome = result.verification.decision;
|
|
1072
|
+
problems = result.verification.problems;
|
|
1073
|
+
findingsEvidence = (result.review?.findings ?? []).map((finding) => ({ kind: "review", ref: `review:${finding.id}`, produced_by: "reviewer" }));
|
|
1074
|
+
if (outcome !== "invalid") {
|
|
1075
|
+
if (outcome !== "accept" && !widened && scopeLimitedReview(result.review)) {
|
|
1076
|
+
// After the workspace-wide read scope a scope-limited verdict is a harness problem, not a
|
|
1077
|
+
// finding about the work: re-dispatch once with an explicit note instead of rejecting.
|
|
1078
|
+
widened = true;
|
|
1079
|
+
notice("warning", `the review of ${entry.key} reported criteria unverifiable because of its read scope; re-dispatching it once with the whole workspace readable`);
|
|
1080
|
+
review -= 1;
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
const criteriaProblems = problems.filter((problem) => problem !== "the reviewer requested changes").length;
|
|
1084
|
+
if (result.review !== undefined) {
|
|
1085
|
+
problems = [...problems, ...result.review.findings.map((finding) => `${finding.id} (${finding.severity}): ${finding.summary}`)];
|
|
1086
|
+
}
|
|
1087
|
+
blocker = result.review?.findings.some((finding) => finding.severity === "blocker") ?? false;
|
|
1088
|
+
// P0-A: the harness verification passed; a revise verdict with every criterion met and only
|
|
1089
|
+
// minor/info findings is not worth a revision round: accept with the findings as notes.
|
|
1090
|
+
if (limits.review === "proportional" && outcome === "revise" && criteriaProblems === 0 && !(result.review?.findings ?? []).some((finding) => finding.severity === "blocker" || finding.severity === "major")) {
|
|
1091
|
+
noteOnly = true;
|
|
1092
|
+
outcome = "accept";
|
|
1093
|
+
}
|
|
1094
|
+
break;
|
|
1095
|
+
}
|
|
1096
|
+
notice("warning", `review ${review} of ${entry.key} is invalid: ${problems.slice(0, 3).join("; ")}`);
|
|
1097
|
+
}
|
|
1098
|
+
if (outcome === "accept" && mayComplete(packet, { decision: outcome, problems })) {
|
|
1099
|
+
try {
|
|
1100
|
+
await workers.integrate(handle.attemptId, artifact.artifactDigest, signal);
|
|
1101
|
+
entry.integrated = artifact.changes.map((change) => change.path);
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
await move(entry, "failed", `integration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1105
|
+
return "failed";
|
|
1106
|
+
}
|
|
1107
|
+
if (noteOnly) {
|
|
1108
|
+
entry.notes.push(...problems.slice(0, 10).map((problem) => `minor review finding (not blocking): ${problem}`.slice(0, NOTE_LIMIT)));
|
|
1109
|
+
await move(entry, "completed", `verified; review found only minor issues, accepted with notes: ${problems.slice(0, 3).join("; ")}`.slice(0, 1000));
|
|
1110
|
+
}
|
|
1111
|
+
else
|
|
1112
|
+
await move(entry, "completed", "review accepted and artifact integrated");
|
|
1113
|
+
return "completed";
|
|
1114
|
+
}
|
|
1115
|
+
if (limits.review === "proportional" && outcome === "invalid" && packet.risk !== "high-risk" && !partialArtifact) {
|
|
1116
|
+
// P0-A: the harness verification passed and no reviewer produced a valid verdict (a harness or
|
|
1117
|
+
// reviewer problem, not a finding about the work): accept standard work with a note instead of failing it.
|
|
1118
|
+
try {
|
|
1119
|
+
await workers.integrate(handle.attemptId, artifact.artifactDigest, signal);
|
|
1120
|
+
entry.integrated = artifact.changes.map((change) => change.path);
|
|
1121
|
+
entry.notes.push(`no valid independent review (${problems.slice(0, 2).join("; ")}); accepted on the harness verification`.slice(0, NOTE_LIMIT));
|
|
1122
|
+
notice("warning", `${entry.key}: no valid review (${problems.slice(0, 2).join("; ")}); accepted on the passing harness verification`);
|
|
1123
|
+
await move(entry, "completed", `verified; no valid review, accepted on the harness verification: ${problems.slice(0, 3).join("; ")}`.slice(0, 1000));
|
|
1124
|
+
return "completed";
|
|
1125
|
+
}
|
|
1126
|
+
catch {
|
|
1127
|
+
// Falls through to the failure below (the artifact no longer matches its pin).
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (outcome === "revise" || outcome === "block") {
|
|
1131
|
+
// The harness checks passed (a task reaches review only after verification): a revise or block
|
|
1132
|
+
// verdict is a revise round for the implementer with the reviewer's findings, never a silent
|
|
1133
|
+
// rejection. An exhausted review_revisions budget consults the orchestrator: accept with notes
|
|
1134
|
+
// (no blocker finding), retry (one more revision) or fail.
|
|
1135
|
+
const label = outcome === "block" ? "review blocked" : "review requested changes";
|
|
1136
|
+
if (revisions >= limits.budgets.review_revisions) {
|
|
1137
|
+
const decision = await triageReport(entry, packet, completion, handle.attemptId, limits.budgets.triage_retries - retries, [`the review did not accept after ${revisions} revision(s)`, ...problems], undefined, { override: !blocker, note: `${label} after ${revisions} revision(s); harness verification passed${blocker ? "; a blocker finding stands" : ""}` });
|
|
1138
|
+
if (decision.kind === "accept" && !blocker) {
|
|
1139
|
+
try {
|
|
1140
|
+
await workers.integrate(handle.attemptId, artifact.artifactDigest, signal);
|
|
1141
|
+
entry.integrated = artifact.changes.map((change) => change.path);
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
await move(entry, "failed", `integration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1145
|
+
return "failed";
|
|
1146
|
+
}
|
|
1147
|
+
entry.notes.push(...problems.slice(0, 10).map((problem) => `review finding accepted by the orchestrator: ${problem}`.slice(0, NOTE_LIMIT)));
|
|
1148
|
+
await move(entry, "completed", `accepted by the orchestrator with the review findings as notes${decision.guidance === undefined ? "" : ` (${decision.guidance})`}: ${problems.slice(0, 5).join("; ")}`);
|
|
1149
|
+
return "completed";
|
|
1150
|
+
}
|
|
1151
|
+
if (decision.kind !== "retry" || retries >= limits.budgets.triage_retries) {
|
|
1152
|
+
entry.failure = outcome === "block" ? "review_blocked" : "verification_failed";
|
|
1153
|
+
await move(entry, "failed", `${label}; revision limit reached${decision.kind === "fail" ? "; triage: the orchestrator failed the task" : ""}: ${problems.slice(0, 5).join("; ")}`);
|
|
1154
|
+
return "failed";
|
|
1155
|
+
}
|
|
1156
|
+
retries += 1;
|
|
1157
|
+
}
|
|
1158
|
+
await move(entry, "changes_requested", `${label}: ${problems.slice(0, 5).join("; ") || "address the review findings"}`);
|
|
1159
|
+
revisions += 1;
|
|
1160
|
+
seed = artifact.artifactBytes;
|
|
1161
|
+
await workers.revert(handle.attemptId, signal);
|
|
1162
|
+
await retire(entry, handle.attemptId);
|
|
1163
|
+
packet = await issueDelta(entry, packet, problems.length > 0 ? problems : ["address the review findings"], findingsEvidence);
|
|
1164
|
+
await move(entry, "ready", `revision ${revisions} requested by review`);
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
entry.failure = "verification_failed";
|
|
1168
|
+
await move(entry, "failed", `no valid review: ${problems.slice(0, 5).join("; ")}`);
|
|
1169
|
+
return "failed";
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
const scheduler = createDagScheduler([...entries.values()].map((entry) => ({
|
|
1173
|
+
key: entry.key,
|
|
1174
|
+
dependsOn: entry.plan.depends_on,
|
|
1175
|
+
ownedPaths: entry.plan.owned_paths,
|
|
1176
|
+
provider: entry.route?.route.provider_id,
|
|
1177
|
+
workspace: request.workspaceRoot,
|
|
1178
|
+
})), limits.concurrency);
|
|
1179
|
+
const taskLine = (entry) => `${entry.key} ${entry.taskId} (${entry.plan.role}, ${entry.plan.risk}, owns ${entry.plan.owned_paths.join(", ") || "nothing"}): ${entry.state}${entry.summary === "" ? "" : ` - ${entry.summary.slice(0, 300)}`}`;
|
|
1180
|
+
const revisionCandidate = (planId, extra, notes) => ({
|
|
1181
|
+
...approvedPlan,
|
|
1182
|
+
plan_id: planId,
|
|
1183
|
+
version: approvedPlan.version + 1,
|
|
1184
|
+
tasks: [...approvedPlan.tasks, ...extra],
|
|
1185
|
+
assumptions: [...approvedPlan.assumptions, ...notes.map((note) => `User steering: ${note}`)],
|
|
1186
|
+
created_at: now().toISOString(),
|
|
1187
|
+
});
|
|
1188
|
+
const spawned = [];
|
|
1189
|
+
/** K1.7: what the user told workers directly since the orchestrator's last turn; included in its next consultation or triage. */
|
|
1190
|
+
const workerMessages = [];
|
|
1191
|
+
/** K1.7: attempts the user cancelled; never auto-retried (user intent): the task ends cancelled and the orchestrator is told. */
|
|
1192
|
+
const userCancelled = new Set();
|
|
1193
|
+
let consulting = false;
|
|
1194
|
+
let triaging;
|
|
1195
|
+
// Orchestrator turns (steering consultation, triage) share the run session: one at a time.
|
|
1196
|
+
let orchestratorQueue = Promise.resolve();
|
|
1197
|
+
const orchestratorTurn = (work) => {
|
|
1198
|
+
const next = orchestratorQueue.then(work, work);
|
|
1199
|
+
orchestratorQueue = next.catch(() => undefined);
|
|
1200
|
+
return next;
|
|
1201
|
+
};
|
|
1202
|
+
const deny = (code, message) => ({ ok: false, code, message });
|
|
1203
|
+
deps.delegation?.set({
|
|
1204
|
+
runId,
|
|
1205
|
+
spawn(raw, caller) {
|
|
1206
|
+
if (caller.role !== "orchestrator" || caller.runId !== runId)
|
|
1207
|
+
return deny("policy_denied", "only the orchestrator of this run delegates tasks");
|
|
1208
|
+
if (!consulting)
|
|
1209
|
+
return deny("execution_failed", "task_spawn is accepted while the orchestrator is consulted at a safe boundary (after user steering or during a triage)");
|
|
1210
|
+
if (spawned.length >= limits.maxSpawnedTasks)
|
|
1211
|
+
return deny("policy_denied", `at most ${limits.maxSpawnedTasks} follow-up task(s) may be spawned per run`);
|
|
1212
|
+
const admission = budget.exhausted();
|
|
1213
|
+
if (!admission.ok)
|
|
1214
|
+
return deny("policy_denied", `the run budget is exhausted (${admission.metric} ${admission.used} of ${admission.limit}); no task can be added`);
|
|
1215
|
+
const parsed = planTaskSchema.safeParse(raw);
|
|
1216
|
+
if (!parsed.success)
|
|
1217
|
+
return deny("invalid_arguments", parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; "));
|
|
1218
|
+
if (entries.has(parsed.data.key) || spawned.some((task) => task.key === parsed.data.key))
|
|
1219
|
+
return deny("invalid_arguments", `task key ${parsed.data.key} already exists`);
|
|
1220
|
+
const probeId = createId("plan");
|
|
1221
|
+
const validation = checkPlan(revisionCandidate(probeId, [...spawned, parsed.data], []), { runId, planId: probeId, version: approvedPlan.version + 1 }, (task) => task.key === parsed.data.key);
|
|
1222
|
+
if (!validation.ok)
|
|
1223
|
+
return deny("invalid_arguments", `the task does not fit the plan: ${validation.issues.slice(0, 5).join("; ")}`);
|
|
1224
|
+
spawned.push(parsed.data);
|
|
1225
|
+
return { ok: true, text: `task ${parsed.data.key} accepted for plan v${approvedPlan.version + 1}; it runs only after the revised plan is approved` };
|
|
1226
|
+
},
|
|
1227
|
+
status(task) {
|
|
1228
|
+
const selected = [...entries.values()].filter((entry) => task === undefined || entry.taskId === task || entry.key === task);
|
|
1229
|
+
if (selected.length === 0)
|
|
1230
|
+
return deny("invalid_arguments", `unknown task ${task ?? ""}`);
|
|
1231
|
+
return { ok: true, text: [`plan ${approvedPlan.plan_id} v${approvedPlan.version}`, ...selected.map(taskLine)].join("\n") };
|
|
1232
|
+
},
|
|
1233
|
+
triage(input, caller) {
|
|
1234
|
+
if (caller.role !== "orchestrator" || caller.runId !== runId)
|
|
1235
|
+
return deny("policy_denied", "only the orchestrator of this run triages its tasks");
|
|
1236
|
+
const pending = triaging;
|
|
1237
|
+
if (pending === undefined || (input.task !== pending.key && input.task !== pending.taskId)) {
|
|
1238
|
+
return deny("execution_failed", `no report of task ${input.task} is being triaged${pending === undefined ? "" : `; the report under triage is ${pending.key}`}`);
|
|
1239
|
+
}
|
|
1240
|
+
if (pending.decision !== undefined)
|
|
1241
|
+
return deny("invalid_arguments", `task ${pending.key} is already decided (${pending.decision.kind}); end your turn`);
|
|
1242
|
+
const waive = [...new Set(input.waive_criteria ?? [])];
|
|
1243
|
+
const replacement = [...new Set((input.verification ?? []).map((command) => command.trim()).filter((command) => command !== ""))];
|
|
1244
|
+
if (replacement.length > 0) {
|
|
1245
|
+
if (input.decision !== "retry")
|
|
1246
|
+
return deny("invalid_arguments", "verification (replacement commands) goes with decision retry");
|
|
1247
|
+
if (pending.planCaused.length === 0)
|
|
1248
|
+
return deny("invalid_arguments", `${pending.key} has no plan-caused verification problem; its verification stays as planned`);
|
|
1249
|
+
if (pending.retriesLeft <= 0)
|
|
1250
|
+
return deny("invalid_arguments", `no retries are left for ${pending.key}; choose accept or fail`);
|
|
1251
|
+
const task = approvedPlan.tasks.find((candidate) => candidate.key === pending.key);
|
|
1252
|
+
if (task !== undefined) {
|
|
1253
|
+
const dry = preflightVerification([{ ...task, verification: replacement }], preflightContext);
|
|
1254
|
+
if (dry.refusals.length > 0)
|
|
1255
|
+
return deny("invalid_arguments", `replacement verification rejected:\n${dry.refusals.map((refusal) => `- ${formatVerificationRefusal(refusal)}`).join("\n")}\nFix it and call task_triage again.`);
|
|
1256
|
+
}
|
|
1257
|
+
pending.decision = { kind: "retry", guidance: input.guidance, verification: replacement };
|
|
1258
|
+
return { ok: true, text: `decision for ${pending.key} recorded: retry with the revised verification (${replacement.join("; ")}); the plan is revised and re-approved. End your turn now.` };
|
|
1259
|
+
}
|
|
1260
|
+
if (input.decision === "accept" && pending.reviewOverride) {
|
|
1261
|
+
pending.decision = { kind: "accept", waived: [], guidance: input.guidance };
|
|
1262
|
+
return { ok: true, text: `decision for ${pending.key} recorded: accept; the verified change is integrated with the review findings recorded as notes. End your turn now.` };
|
|
1263
|
+
}
|
|
1264
|
+
if (input.decision === "accept" && pending.verificationOnly && !pending.acceptable) {
|
|
1265
|
+
pending.decision = { kind: "accept", waived: [], guidance: input.guidance, waivedCommands: pending.planCaused };
|
|
1266
|
+
return { ok: true, text: `decision for ${pending.key} recorded: accept; the verification command(s) that could not run are waived (${pending.planCaused.join("; ")}) and the change goes to independent review. End your turn now.` };
|
|
1267
|
+
}
|
|
1268
|
+
if (input.decision === "accept") {
|
|
1269
|
+
if (!pending.acceptable)
|
|
1270
|
+
return deny("invalid_arguments", `accept is only for read-only tasks that changed nothing; ${pending.key} completes only through verification and review. Choose ${pending.reviewablePaths.length > 0 ? "review, " : ""}retry or fail.`);
|
|
1271
|
+
const unknown = waive.filter((id) => !pending.criteria.includes(id));
|
|
1272
|
+
if (unknown.length > 0)
|
|
1273
|
+
return deny("invalid_arguments", `unknown criteria ${unknown.join(", ")}; ${pending.key} has ${pending.criteria.join(", ")}`);
|
|
1274
|
+
if (waive.length >= pending.criteria.length)
|
|
1275
|
+
return deny("invalid_arguments", "at least one criterion must stay evidenced; if nothing useful was found, choose retry or fail");
|
|
1276
|
+
pending.decision = { kind: "accept", waived: waive, guidance: input.guidance };
|
|
1277
|
+
}
|
|
1278
|
+
else if (input.decision === "retry") {
|
|
1279
|
+
if (pending.retriesLeft <= 0)
|
|
1280
|
+
return deny("invalid_arguments", `no retries are left for ${pending.key}; choose ${pending.reviewablePaths.length > 0 ? "review" : "accept"} or fail`);
|
|
1281
|
+
pending.decision = { kind: "retry", guidance: input.guidance };
|
|
1282
|
+
}
|
|
1283
|
+
else if (input.decision === "review") {
|
|
1284
|
+
if (pending.reviewablePaths.length === 0)
|
|
1285
|
+
return deny("invalid_arguments", `review is only for a writing task whose unverified change is waiting; ${pending.key} has none. Choose accept, retry or fail.`);
|
|
1286
|
+
pending.decision = { kind: "review", guidance: input.guidance };
|
|
1287
|
+
return { ok: true, text: `decision for ${pending.key} recorded: review; ${pending.reviewablePaths.join(", ")} goes to harness verification and independent review with the worker's caveats as notes. End your turn now.` };
|
|
1288
|
+
}
|
|
1289
|
+
else if (pending.reviewablePaths.length > 0 && !pending.failOffered) {
|
|
1290
|
+
// Live run 01M3ABTS: a correct index.html was failed because the worker's own extra check was refused.
|
|
1291
|
+
pending.failOffered = true;
|
|
1292
|
+
return deny("invalid_arguments", `${pending.key} produced ${pending.reviewablePaths.join(", ")} in its owned paths. Prefer decision review: the change goes to harness verification and an independent reviewer who decides (a check the worker invented and the sandbox refused is not a reason to fail). Call task_triage again with review, or with fail again to confirm.`);
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
pending.decision = { kind: "fail", guidance: input.guidance };
|
|
1296
|
+
}
|
|
1297
|
+
return { ok: true, text: `decision for ${pending.key} recorded: ${input.decision}${waive.length > 0 ? ` (waived ${waive.join(", ")})` : ""}. End your turn now.` };
|
|
1298
|
+
},
|
|
1299
|
+
});
|
|
1300
|
+
// ---- K1.7: the workers of this run, for the renderer and `/workers` ---------------------------
|
|
1301
|
+
const entryOf = (task) => entries.get(task) ?? [...entries.values()].find((entry) => entry.taskId === task);
|
|
1302
|
+
const liveAttempt = (entry) => {
|
|
1303
|
+
const latest = entry.attempts.at(-1);
|
|
1304
|
+
return latest !== undefined && workers.live(latest) !== undefined ? latest : undefined;
|
|
1305
|
+
};
|
|
1306
|
+
const thisRun = {
|
|
1307
|
+
ended: false,
|
|
1308
|
+
list: () => [...entries.values()].map((entry) => {
|
|
1309
|
+
const latest = entry.attempts.at(-1);
|
|
1310
|
+
const record = latest === undefined ? undefined : workers.attempt(latest);
|
|
1311
|
+
const live = latest === undefined || thisRun.ended ? undefined : workers.live(latest);
|
|
1312
|
+
return {
|
|
1313
|
+
key: entry.key,
|
|
1314
|
+
taskId: entry.taskId,
|
|
1315
|
+
role: record?.packet.role ?? entry.plan.role,
|
|
1316
|
+
state: entry.state,
|
|
1317
|
+
model: record?.route.model_id ?? entry.route?.route.model_id,
|
|
1318
|
+
attempt: entry.attempts.length,
|
|
1319
|
+
live: live !== undefined,
|
|
1320
|
+
paused: live?.paused === true,
|
|
1321
|
+
objective: record?.packet.objective ?? entry.plan.objective,
|
|
1322
|
+
};
|
|
1323
|
+
}),
|
|
1324
|
+
assignment(task) {
|
|
1325
|
+
const entry = entryOf(task);
|
|
1326
|
+
if (entry === undefined)
|
|
1327
|
+
return undefined;
|
|
1328
|
+
const latest = entry.attempts.at(-1);
|
|
1329
|
+
const record = latest === undefined ? undefined : workers.attempt(latest);
|
|
1330
|
+
const packet = record?.packet;
|
|
1331
|
+
const orchestratorNotes = (record?.notes ?? entry.nextNotes).map((text) => ({ from: "orchestrator", text }));
|
|
1332
|
+
return {
|
|
1333
|
+
taskKey: entry.key,
|
|
1334
|
+
objective: packet?.objective ?? entry.plan.objective,
|
|
1335
|
+
owned_paths: packet?.scope.owned_paths ?? entry.plan.owned_paths,
|
|
1336
|
+
acceptance_criteria: packet !== undefined ? packet.acceptance_criteria.map((criterion) => `${criterion.id}: ${criterion.statement}`) : entry.plan.acceptance_criteria.map((criterion, index) => `AC-${index + 1}: ${criterion.statement}`),
|
|
1337
|
+
verification_commands: packet?.verification.commands ?? entry.plan.verification,
|
|
1338
|
+
steering: [...orchestratorNotes, ...entry.userMessages],
|
|
1339
|
+
};
|
|
1340
|
+
},
|
|
1341
|
+
async control(action, task, text) {
|
|
1342
|
+
const entry = entryOf(task);
|
|
1343
|
+
if (entry === undefined)
|
|
1344
|
+
return { ok: false, message: `no worker ${task} in this run (/workers lists them)` };
|
|
1345
|
+
const attemptId = thisRun.ended ? undefined : liveAttempt(entry);
|
|
1346
|
+
if (attemptId === undefined)
|
|
1347
|
+
return { ok: false, message: `${entry.key} is not running (${entry.state.replaceAll("_", " ")}); nothing was sent` };
|
|
1348
|
+
const scope = { taskId: entry.taskId, attemptId, actor: { kind: "user" } };
|
|
1349
|
+
if (action === "message") {
|
|
1350
|
+
const trimmed = text.trim().slice(0, 8000);
|
|
1351
|
+
if (trimmed === "")
|
|
1352
|
+
return { ok: false, message: "the message is empty" };
|
|
1353
|
+
if (!workers.steer(attemptId, trimmed))
|
|
1354
|
+
return { ok: false, message: `${entry.key} finished before the message could be delivered` };
|
|
1355
|
+
entry.userMessages.push({ from: "user", text: trimmed, atMs: now().getTime(), delivered: false });
|
|
1356
|
+
workerMessages.push(`to ${entry.key}: ${trimmed}`.slice(0, NOTE_LIMIT));
|
|
1357
|
+
await recorder.record("task/user_message", { task_id: entry.taskId, attempt_id: attemptId, text: trimmed }, scope);
|
|
1358
|
+
return { ok: true, message: `sent to ${entry.key}; it reads it at its next step (the orchestrator is told too)` };
|
|
1359
|
+
}
|
|
1360
|
+
const applied = action === "pause" ? workers.pause(attemptId) : action === "resume" ? workers.resume(attemptId) : workers.cancel(attemptId, "cancelled by the user");
|
|
1361
|
+
if (!applied)
|
|
1362
|
+
return { ok: false, message: `${entry.key} finished before it could be ${action === "pause" ? "paused" : action === "resume" ? "resumed" : "cancelled"}` };
|
|
1363
|
+
if (action === "cancel")
|
|
1364
|
+
userCancelled.add(attemptId);
|
|
1365
|
+
if (action === "cancel")
|
|
1366
|
+
workerMessages.push(`the user cancelled the running attempt of ${entry.key}`);
|
|
1367
|
+
await recorder.record("attempt/user_control", { attempt_id: attemptId, task_id: entry.taskId, action }, scope);
|
|
1368
|
+
return {
|
|
1369
|
+
ok: true,
|
|
1370
|
+
message: action === "pause"
|
|
1371
|
+
? `${entry.key} pauses after its current step (resume with /worker ${entry.key} --resume)`
|
|
1372
|
+
: action === "resume"
|
|
1373
|
+
? `${entry.key} resumed`
|
|
1374
|
+
: `${entry.key} cancelled; it is not retried and the orchestrator is told`,
|
|
1375
|
+
};
|
|
1376
|
+
},
|
|
1377
|
+
};
|
|
1378
|
+
runWorkers = thisRun;
|
|
1379
|
+
/**
|
|
1380
|
+
* Re-versions the plan with `notes` (user steering) and `extra` follow-up tasks and re-approves
|
|
1381
|
+
* it under the run's policy mode. Only an approved revision supersedes the running plan; tasks
|
|
1382
|
+
* that have not started get its packets, added tasks join the scheduler.
|
|
1383
|
+
*/
|
|
1384
|
+
const applyRevision = async (notes, extra, cause) => {
|
|
1385
|
+
const pending = [...entries.values()].some((entry) => scheduler.state(entry.key) === "pending");
|
|
1386
|
+
if (!pending && extra.length === 0) {
|
|
1387
|
+
notice("info", `steering noted, but no task is left to apply it to: ${notes.join("; ")}`.slice(0, 500));
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const revisionId = createId("plan");
|
|
1391
|
+
const validation = validatePlan(revisionCandidate(revisionId, extra, notes), { runId, planId: revisionId, version: approvedPlan.version + 1 });
|
|
1392
|
+
if (!validation.ok) {
|
|
1393
|
+
notice("warning", `plan revision invalid; the run continues with v${approvedPlan.version}: ${validation.issues.slice(0, 5).join("; ")}`);
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if (!(await approveRevision(validation, cause)))
|
|
1397
|
+
return;
|
|
1398
|
+
for (const task of extra) {
|
|
1399
|
+
const entry = newEntry(task);
|
|
1400
|
+
entries.set(task.key, entry);
|
|
1401
|
+
await createTask(entry);
|
|
1402
|
+
scheduler.add({ key: task.key, dependsOn: task.depends_on, ownedPaths: task.owned_paths, provider: entry.route?.route.provider_id, workspace: request.workspaceRoot });
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
/**
|
|
1406
|
+
* Proposes a validated revision and re-approves it under the run's policy mode; only an
|
|
1407
|
+
* approved revision supersedes the running plan. False when it was not approved.
|
|
1408
|
+
*/
|
|
1409
|
+
const approveRevision = async (validation, cause) => {
|
|
1410
|
+
await recorder.record("plan/proposed", { plan: validation.plan, digest: validation.digest });
|
|
1411
|
+
if (request.policyMode === "ask")
|
|
1412
|
+
await moveRun("waiting_for_approval", `revised plan v${validation.plan.version} approval requested`);
|
|
1413
|
+
const decision = await approvePlan({ plan: validation.plan, digest: validation.digest, mode: request.policyMode, broker: deps.approvals, now }, signal);
|
|
1414
|
+
await recordApproval(decision);
|
|
1415
|
+
if (request.policyMode === "ask") {
|
|
1416
|
+
await moveRun("running", decision.approved ? `revised plan v${validation.plan.version} approved` : `revised plan not approved; continuing with v${approvedPlan.version}`);
|
|
1417
|
+
}
|
|
1418
|
+
if (!decision.approved) {
|
|
1419
|
+
await recorder.record("plan/state_changed", {
|
|
1420
|
+
plan_id: validation.plan.plan_id,
|
|
1421
|
+
digest: validation.digest,
|
|
1422
|
+
from: "proposed",
|
|
1423
|
+
to: "rejected",
|
|
1424
|
+
reason: `revision ${decision.decision.outcome} by ${decision.decision.decided_by}`,
|
|
1425
|
+
approval_id: decision.request.approval_id,
|
|
1426
|
+
});
|
|
1427
|
+
notice("warning", `revised plan v${validation.plan.version} was not approved; the run continues with v${approvedPlan.version}`);
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
await recorder.record("plan/state_changed", {
|
|
1431
|
+
plan_id: approvedPlan.plan_id,
|
|
1432
|
+
digest: approvedDigest,
|
|
1433
|
+
from: "approved",
|
|
1434
|
+
to: "superseded",
|
|
1435
|
+
reason: `superseded by ${validation.plan.plan_id} v${validation.plan.version} after ${cause}`,
|
|
1436
|
+
});
|
|
1437
|
+
await recorder.record("plan/state_changed", {
|
|
1438
|
+
plan_id: validation.plan.plan_id,
|
|
1439
|
+
digest: validation.digest,
|
|
1440
|
+
from: "proposed",
|
|
1441
|
+
to: "approved",
|
|
1442
|
+
reason: `revision approved by ${decision.decision.decided_by} in ${decision.decision.mode} mode`,
|
|
1443
|
+
approval_id: decision.request.approval_id,
|
|
1444
|
+
});
|
|
1445
|
+
approvedPlan = validation.plan;
|
|
1446
|
+
approvedDigest = validation.digest;
|
|
1447
|
+
return true;
|
|
1448
|
+
};
|
|
1449
|
+
/**
|
|
1450
|
+
* A triage revision of one task's verification (plan-caused problems): the plan is re-versioned
|
|
1451
|
+
* with the replacement commands, dry-run like `plan_propose` and re-approved under the run's
|
|
1452
|
+
* policy mode. False when it is invalid or not approved.
|
|
1453
|
+
*/
|
|
1454
|
+
const reviseVerification = async (entry, commands) => {
|
|
1455
|
+
const revisionId = createId("plan");
|
|
1456
|
+
const candidate = {
|
|
1457
|
+
...approvedPlan,
|
|
1458
|
+
plan_id: revisionId,
|
|
1459
|
+
version: approvedPlan.version + 1,
|
|
1460
|
+
tasks: approvedPlan.tasks.map((task) => (task.key === entry.key ? { ...task, verification: [...commands] } : task)),
|
|
1461
|
+
created_at: now().toISOString(),
|
|
1462
|
+
};
|
|
1463
|
+
const validation = checkPlan(candidate, { runId, planId: revisionId, version: approvedPlan.version + 1 }, (task) => task.key === entry.key);
|
|
1464
|
+
if (!validation.ok) {
|
|
1465
|
+
notice("warning", `verification revision of ${entry.key} invalid: ${validation.issues.slice(0, 3).join("; ")}`.slice(0, 1000));
|
|
1466
|
+
return false;
|
|
1467
|
+
}
|
|
1468
|
+
if (!(await approveRevision(validation, `the triage of ${entry.key}'s verification`)))
|
|
1469
|
+
return false;
|
|
1470
|
+
entry.plan = validation.plan.tasks.find((task) => task.key === entry.key) ?? entry.plan;
|
|
1471
|
+
return true;
|
|
1472
|
+
};
|
|
1473
|
+
/**
|
|
1474
|
+
* Applies queued user steering at a safe boundary (no dispatch in flight for the affected
|
|
1475
|
+
* tasks): the orchestrator is consulted once, then the plan is re-versioned with the steering
|
|
1476
|
+
* (and any spawned follow-up tasks) and re-approved under the run's policy mode.
|
|
1477
|
+
*/
|
|
1478
|
+
const revise = () => orchestratorTurn(async () => {
|
|
1479
|
+
const notes = steering.splice(0);
|
|
1480
|
+
if (notes.length === 0)
|
|
1481
|
+
return;
|
|
1482
|
+
if (deps.planner.consult !== undefined) {
|
|
1483
|
+
consulting = true;
|
|
1484
|
+
try {
|
|
1485
|
+
await deps.planner.consult({
|
|
1486
|
+
runId,
|
|
1487
|
+
goal: request.goal,
|
|
1488
|
+
planVersion: approvedPlan.version,
|
|
1489
|
+
steering: notes,
|
|
1490
|
+
workerMessages: workerMessages.splice(0),
|
|
1491
|
+
tasks: [...entries.values()].map(taskLine),
|
|
1492
|
+
route: orchestratorRoute.route,
|
|
1493
|
+
policy: orchestratorPolicy,
|
|
1494
|
+
events: log,
|
|
1495
|
+
sessionId: log.sessionId,
|
|
1496
|
+
}, signal);
|
|
1497
|
+
}
|
|
1498
|
+
catch (error) {
|
|
1499
|
+
notice("warning", `orchestrator consultation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1500
|
+
}
|
|
1501
|
+
finally {
|
|
1502
|
+
consulting = false;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
await applyRevision(notes, spawned.splice(0), "user steering");
|
|
1506
|
+
});
|
|
1507
|
+
const inflight = new Map();
|
|
1508
|
+
let budgetStop = false;
|
|
1509
|
+
try {
|
|
1510
|
+
for (;;) {
|
|
1511
|
+
while (!scheduler.done()) {
|
|
1512
|
+
if (signal.aborted)
|
|
1513
|
+
break;
|
|
1514
|
+
if (!budget.exhausted().ok) {
|
|
1515
|
+
budget.admit();
|
|
1516
|
+
budgetStop = true;
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
if (steering.length > 0)
|
|
1520
|
+
await revise();
|
|
1521
|
+
for (const key of scheduler.startable()) {
|
|
1522
|
+
const entry = entries.get(key);
|
|
1523
|
+
if (entry === undefined)
|
|
1524
|
+
continue;
|
|
1525
|
+
scheduler.start(key);
|
|
1526
|
+
const task = runTask(entry)
|
|
1527
|
+
.catch(async (error) => {
|
|
1528
|
+
const reason = `task error: ${error instanceof Error ? error.message : String(error)}`;
|
|
1529
|
+
for (const to of ["failed", "cancelled"]) {
|
|
1530
|
+
if (validateTransition("task", entry.state, to).ok) {
|
|
1531
|
+
await move(entry, to, reason).catch(() => undefined);
|
|
1532
|
+
break;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return "failed";
|
|
1536
|
+
})
|
|
1537
|
+
.then(async (result) => {
|
|
1538
|
+
if (result === "failed") {
|
|
1539
|
+
for (const attemptId of [...entry.attempts].reverse())
|
|
1540
|
+
await workers.revert(attemptId, signal).catch(() => undefined);
|
|
1541
|
+
}
|
|
1542
|
+
for (const attemptId of entry.attempts)
|
|
1543
|
+
await workers.dispose(attemptId).catch(() => undefined);
|
|
1544
|
+
scheduler.finish(key, result);
|
|
1545
|
+
})
|
|
1546
|
+
.finally(() => inflight.delete(key));
|
|
1547
|
+
inflight.set(key, task);
|
|
1548
|
+
}
|
|
1549
|
+
if (inflight.size === 0)
|
|
1550
|
+
break;
|
|
1551
|
+
await Promise.race(inflight.values());
|
|
1552
|
+
}
|
|
1553
|
+
if (signal.aborted || budgetStop || steering.length === 0)
|
|
1554
|
+
break;
|
|
1555
|
+
await revise();
|
|
1556
|
+
if (scheduler.done())
|
|
1557
|
+
break;
|
|
1558
|
+
}
|
|
1559
|
+
for (const handle of workers.running())
|
|
1560
|
+
if (signal.aborted)
|
|
1561
|
+
handle.cancel("run cancelled");
|
|
1562
|
+
await Promise.allSettled(inflight.values());
|
|
1563
|
+
}
|
|
1564
|
+
finally {
|
|
1565
|
+
clearInterval(ticker);
|
|
1566
|
+
}
|
|
1567
|
+
for (const key of scheduler.skipPending()) {
|
|
1568
|
+
const entry = entries.get(key);
|
|
1569
|
+
if (entry !== undefined && !isTerminalState("task", entry.state)) {
|
|
1570
|
+
await move(entry, "cancelled", signal.aborted ? "run cancelled" : budgetStop ? "budget exhausted" : "a dependency did not complete");
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
const lines = [...entries.values()].map((entry) => {
|
|
1574
|
+
const files = entry.integrated.length > 0 ? ` [integrated: ${entry.integrated.join(", ")}]` : "";
|
|
1575
|
+
return `${entry.key} (${entry.plan.role}): ${entry.state}${files}`;
|
|
1576
|
+
});
|
|
1577
|
+
const allCompleted = [...entries.values()].every((entry) => entry.state === "completed");
|
|
1578
|
+
const report = [`Run ${runId}: ${request.goal}`, ...lines].join("\n");
|
|
1579
|
+
if (ledger !== undefined) {
|
|
1580
|
+
await ledger.write(`.ai/tasks/${runId}/report.md`, `# Run report\n\n${lines.map((line) => `- ${line}`).join("\n")}\n`).catch((error) => {
|
|
1581
|
+
notice("warning", `ledger write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
if (signal.aborted)
|
|
1585
|
+
return await finish("cancelled", EXIT_CODES.cancelled, report, "cancelled");
|
|
1586
|
+
if (budgetStop || (!allCompleted && !budget.exhausted().ok))
|
|
1587
|
+
return await finish("failed", EXIT_CODES.budget, report, "failed");
|
|
1588
|
+
if (!allCompleted) {
|
|
1589
|
+
const causes = [...entries.values()].filter((entry) => entry.state !== "completed").map((entry) => entry.failure);
|
|
1590
|
+
return await finish("failed", failedRunExitCode(causes), report, "failed");
|
|
1591
|
+
}
|
|
1592
|
+
return await finish("succeeded", EXIT_CODES.success, report, "completed");
|
|
1593
|
+
}
|
|
1594
|
+
catch (error) {
|
|
1595
|
+
const code = errorCodeOf(error);
|
|
1596
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1597
|
+
notice("error", message);
|
|
1598
|
+
if (signal.aborted)
|
|
1599
|
+
return await finish("cancelled", EXIT_CODES.cancelled, `cancelled: ${message}`, "cancelled").catch(() => ({ runId, sessionId: log.sessionId, status: "cancelled", exitCode: EXIT_CODES.cancelled, summary: message }));
|
|
1600
|
+
return await finish("failed", exitCodeFor(code), message, "failed").catch(() => ({ runId, sessionId: log.sessionId, status: "failed", exitCode: exitCodeFor(code), summary: message }));
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
const control = (action) => async (task, text = "") => runWorkers === undefined ? { ok: false, message: "no workers have run in this session" } : runWorkers.control(action, task, text);
|
|
1604
|
+
return {
|
|
1605
|
+
run,
|
|
1606
|
+
review: (request, signal) => runArtifactReview({ sessions: deps.sessions, blobs: deps.blobs, createWorkers: deps.createWorkers, emit: (event) => fanOut({ kind: "session-event", event }), now, platform }, request, signal),
|
|
1607
|
+
workers: {
|
|
1608
|
+
list: () => runWorkers?.list() ?? [],
|
|
1609
|
+
assignment: (task) => runWorkers?.assignment(task),
|
|
1610
|
+
message: (task, text) => control("message")(task, text),
|
|
1611
|
+
pause: (task) => control("pause")(task),
|
|
1612
|
+
resume: (task) => control("resume")(task),
|
|
1613
|
+
cancel: (task) => control("cancel")(task),
|
|
1614
|
+
},
|
|
1615
|
+
steer(text) {
|
|
1616
|
+
const trimmed = text.trim();
|
|
1617
|
+
if (trimmed === "")
|
|
1618
|
+
return;
|
|
1619
|
+
steering.push(trimmed);
|
|
1620
|
+
void activeRecorder?.record("steer/queued", { text: trimmed }, { actor: { kind: "user" } }).catch(() => undefined);
|
|
1621
|
+
},
|
|
1622
|
+
onEvent(listener) {
|
|
1623
|
+
listeners.add(listener);
|
|
1624
|
+
return () => listeners.delete(listener);
|
|
1625
|
+
},
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
//# sourceMappingURL=coordinator.js.map
|