@monoes/monomindcli 2.16.12 → 2.16.14
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/.claude/agents/core/coder.md +1 -1
- package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
- package/.claude/agents/github/repo-architect.md +1 -1
- package/.claude/agents/testing/production-validator.md +1 -1
- package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
- package/.claude/commands/github/pr-manager.md +1 -1
- package/.claude/helpers/pick-rank.cjs +34 -4
- package/.claude/skills/monolean-audit/SKILL.md +1 -1
- package/README.md +1 -1
- package/dist/src/commands/cleanup-data.d.ts +30 -0
- package/dist/src/commands/cleanup-data.d.ts.map +1 -0
- package/dist/src/commands/cleanup-data.js +138 -0
- package/dist/src/commands/cleanup-data.js.map +1 -0
- package/dist/src/commands/cleanup-scratch.d.ts +43 -0
- package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
- package/dist/src/commands/cleanup-scratch.js +125 -0
- package/dist/src/commands/cleanup-scratch.js.map +1 -0
- package/dist/src/commands/cleanup.d.ts +2 -39
- package/dist/src/commands/cleanup.d.ts.map +1 -1
- package/dist/src/commands/cleanup.js +5 -247
- package/dist/src/commands/cleanup.js.map +1 -1
- package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
- package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-gitignore-checks.js +141 -0
- package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.js +292 -0
- package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
- package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
- package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-memory-checks.js +298 -0
- package/dist/src/commands/doctor-memory-checks.js.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.js +331 -0
- package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +7 -66
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +10 -1331
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
- package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-routing-checks.js +194 -0
- package/dist/src/commands/doctor-routing-checks.js.map +1 -0
- package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
- package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-worker-checks.js +122 -0
- package/dist/src/commands/doctor-worker-checks.js.map +1 -0
- package/dist/src/commands/hooks-routing-commands.js +1 -1
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +6 -15
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/org-control.d.ts +117 -0
- package/dist/src/commands/org-control.d.ts.map +1 -0
- package/dist/src/commands/org-control.js +274 -0
- package/dist/src/commands/org-control.js.map +1 -0
- package/dist/src/commands/org-lifecycle.d.ts +7 -0
- package/dist/src/commands/org-lifecycle.d.ts.map +1 -0
- package/dist/src/commands/org-lifecycle.js +298 -0
- package/dist/src/commands/org-lifecycle.js.map +1 -0
- package/dist/src/commands/org-manage.d.ts +7 -0
- package/dist/src/commands/org-manage.d.ts.map +1 -0
- package/dist/src/commands/org-manage.js +357 -0
- package/dist/src/commands/org-manage.js.map +1 -0
- package/dist/src/commands/org-memory-command.d.ts +3 -0
- package/dist/src/commands/org-memory-command.d.ts.map +1 -0
- package/dist/src/commands/org-memory-command.js +178 -0
- package/dist/src/commands/org-memory-command.js.map +1 -0
- package/dist/src/commands/org-observe-approvals.d.ts +8 -0
- package/dist/src/commands/org-observe-approvals.d.ts.map +1 -0
- package/dist/src/commands/org-observe-approvals.js +211 -0
- package/dist/src/commands/org-observe-approvals.js.map +1 -0
- package/dist/src/commands/org-observe-checkpoint.d.ts +26 -0
- package/dist/src/commands/org-observe-checkpoint.d.ts.map +1 -0
- package/dist/src/commands/org-observe-checkpoint.js +180 -0
- package/dist/src/commands/org-observe-checkpoint.js.map +1 -0
- package/dist/src/commands/org-observe-config.d.ts +8 -0
- package/dist/src/commands/org-observe-config.d.ts.map +1 -0
- package/dist/src/commands/org-observe-config.js +160 -0
- package/dist/src/commands/org-observe-config.js.map +1 -0
- package/dist/src/commands/org-observe-gates.d.ts +4 -0
- package/dist/src/commands/org-observe-gates.d.ts.map +1 -0
- package/dist/src/commands/org-observe-gates.js +182 -0
- package/dist/src/commands/org-observe-gates.js.map +1 -0
- package/dist/src/commands/org-observe-inbox.d.ts +18 -0
- package/dist/src/commands/org-observe-inbox.d.ts.map +1 -0
- package/dist/src/commands/org-observe-inbox.js +154 -0
- package/dist/src/commands/org-observe-inbox.js.map +1 -0
- package/dist/src/commands/org-observe-logs.d.ts +31 -0
- package/dist/src/commands/org-observe-logs.d.ts.map +1 -0
- package/dist/src/commands/org-observe-logs.js +272 -0
- package/dist/src/commands/org-observe-logs.js.map +1 -0
- package/dist/src/commands/org-observe-questions.d.ts +8 -0
- package/dist/src/commands/org-observe-questions.d.ts.map +1 -0
- package/dist/src/commands/org-observe-questions.js +201 -0
- package/dist/src/commands/org-observe-questions.js.map +1 -0
- package/dist/src/commands/org-observe-report.d.ts +8 -0
- package/dist/src/commands/org-observe-report.d.ts.map +1 -0
- package/dist/src/commands/org-observe-report.js +366 -0
- package/dist/src/commands/org-observe-report.js.map +1 -0
- package/dist/src/commands/org-observe-shared.d.ts +20 -0
- package/dist/src/commands/org-observe-shared.d.ts.map +1 -0
- package/dist/src/commands/org-observe-shared.js +55 -0
- package/dist/src/commands/org-observe-shared.js.map +1 -0
- package/dist/src/commands/org-observe.d.ts +9 -101
- package/dist/src/commands/org-observe.d.ts.map +1 -1
- package/dist/src/commands/org-observe.js +12 -1675
- package/dist/src/commands/org-observe.js.map +1 -1
- package/dist/src/commands/org-poll.d.ts +57 -0
- package/dist/src/commands/org-poll.d.ts.map +1 -0
- package/dist/src/commands/org-poll.js +194 -0
- package/dist/src/commands/org-poll.js.map +1 -0
- package/dist/src/commands/org-run.d.ts +3 -0
- package/dist/src/commands/org-run.d.ts.map +1 -0
- package/dist/src/commands/org-run.js +449 -0
- package/dist/src/commands/org-run.js.map +1 -0
- package/dist/src/commands/org-serve.d.ts +19 -0
- package/dist/src/commands/org-serve.d.ts.map +1 -0
- package/dist/src/commands/org-serve.js +395 -0
- package/dist/src/commands/org-serve.js.map +1 -0
- package/dist/src/commands/org-subcommands-config.d.ts +8 -0
- package/dist/src/commands/org-subcommands-config.d.ts.map +1 -0
- package/dist/src/commands/org-subcommands-config.js +93 -0
- package/dist/src/commands/org-subcommands-config.js.map +1 -0
- package/dist/src/commands/org-subcommands-hil.d.ts +11 -0
- package/dist/src/commands/org-subcommands-hil.d.ts.map +1 -0
- package/dist/src/commands/org-subcommands-hil.js +216 -0
- package/dist/src/commands/org-subcommands-hil.js.map +1 -0
- package/dist/src/commands/org-subcommands-observe.d.ts +12 -0
- package/dist/src/commands/org-subcommands-observe.d.ts.map +1 -0
- package/dist/src/commands/org-subcommands-observe.js +249 -0
- package/dist/src/commands/org-subcommands-observe.js.map +1 -0
- package/dist/src/commands/org-subcommands-runtime.d.ts +12 -0
- package/dist/src/commands/org-subcommands-runtime.d.ts.map +1 -0
- package/dist/src/commands/org-subcommands-runtime.js +163 -0
- package/dist/src/commands/org-subcommands-runtime.js.map +1 -0
- package/dist/src/commands/org.d.ts +3 -151
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +43 -2735
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/never-commit.d.ts +3 -3
- package/dist/src/init/types.d.ts +1 -1
- package/dist/src/init/types.js +1 -1
- package/dist/src/knowledge/document-store.js +1 -1
- package/dist/src/knowledge/document-store.js.map +1 -1
- package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
- package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
- package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.js +408 -0
- package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
- package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
- package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-route.js +251 -0
- package/dist/src/mcp-tools/hooks-route.js.map +1 -0
- package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +6 -1727
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-session.js +243 -0
- package/dist/src/mcp-tools/hooks-session.js.map +1 -0
- package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
- package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-task.js +427 -0
- package/dist/src/mcp-tools/hooks-task.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/memory-tools.js +1 -1
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/memory/bm25-index.d.ts +2 -2
- package/dist/src/memory/bm25-index.js +2 -2
- package/dist/src/memory/embedding-operations.js +2 -2
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/entity-name-key.d.ts +1 -1
- package/dist/src/memory/entity-name-key.js +1 -1
- package/dist/src/memory/hnsw-operations.d.ts +1 -1
- package/dist/src/memory/hnsw-operations.js +1 -1
- package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
- package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-backend.js +363 -0
- package/dist/src/memory/memory-bridge-backend.js.map +1 -0
- package/dist/src/memory/memory-bridge-core.d.ts +42 -0
- package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-core.js +126 -0
- package/dist/src/memory/memory-bridge-core.js.map +1 -0
- package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
- package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-entries.js +227 -0
- package/dist/src/memory/memory-bridge-entries.js.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.js +118 -0
- package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
- package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
- package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-paths.js +335 -0
- package/dist/src/memory/memory-bridge-paths.js.map +1 -0
- package/dist/src/memory/memory-bridge-search.d.ts +48 -0
- package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-search.js +383 -0
- package/dist/src/memory/memory-bridge-search.js.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.js +109 -0
- package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
- package/dist/src/memory/memory-bridge-store.d.ts +58 -0
- package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-store.js +201 -0
- package/dist/src/memory/memory-bridge-store.js.map +1 -0
- package/dist/src/memory/memory-bridge.d.ts +10 -438
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +14 -1787
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-kg-claims.d.ts +52 -0
- package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-claims.js +99 -0
- package/dist/src/memory/memory-kg-claims.js.map +1 -0
- package/dist/src/memory/memory-kg-index.d.ts +86 -0
- package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-index.js +256 -0
- package/dist/src/memory/memory-kg-index.js.map +1 -0
- package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
- package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-ingest.js +361 -0
- package/dist/src/memory/memory-kg-ingest.js.map +1 -0
- package/dist/src/memory/memory-kg-model.d.ts +168 -0
- package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-model.js +158 -0
- package/dist/src/memory/memory-kg-model.js.map +1 -0
- package/dist/src/memory/memory-kg-names.d.ts +64 -0
- package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-names.js +187 -0
- package/dist/src/memory/memory-kg-names.js.map +1 -0
- package/dist/src/memory/memory-kg-promote.d.ts +79 -0
- package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-promote.js +248 -0
- package/dist/src/memory/memory-kg-promote.js.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.js +299 -0
- package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
- package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
- package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rollback.js +193 -0
- package/dist/src/memory/memory-kg-rollback.js.map +1 -0
- package/dist/src/memory/memory-kg-rules.d.ts +64 -0
- package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rules.js +251 -0
- package/dist/src/memory/memory-kg-rules.js.map +1 -0
- package/dist/src/memory/memory-kg-scan.d.ts +118 -0
- package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-scan.js +209 -0
- package/dist/src/memory/memory-kg-scan.js.map +1 -0
- package/dist/src/memory/memory-kg-search.d.ts +80 -0
- package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-search.js +239 -0
- package/dist/src/memory/memory-kg-search.js.map +1 -0
- package/dist/src/memory/memory-kg.d.ts +19 -547
- package/dist/src/memory/memory-kg.d.ts.map +1 -1
- package/dist/src/memory/memory-kg.js +10 -2389
- package/dist/src/memory/memory-kg.js.map +1 -1
- package/dist/src/orgrt/daemon-types.d.ts +196 -0
- package/dist/src/orgrt/daemon-types.d.ts.map +1 -0
- package/dist/src/orgrt/daemon-types.js +38 -0
- package/dist/src/orgrt/daemon-types.js.map +1 -0
- package/dist/src/orgrt/daemon.d.ts +70 -300
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +41 -2536
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/idle-watchdog.d.ts +39 -0
- package/dist/src/orgrt/idle-watchdog.d.ts.map +1 -0
- package/dist/src/orgrt/idle-watchdog.js +289 -0
- package/dist/src/orgrt/idle-watchdog.js.map +1 -0
- package/dist/src/orgrt/org-reload.d.ts +7 -0
- package/dist/src/orgrt/org-reload.d.ts.map +1 -0
- package/dist/src/orgrt/org-reload.js +111 -0
- package/dist/src/orgrt/org-reload.js.map +1 -0
- package/dist/src/orgrt/org-start-steps.d.ts +21 -0
- package/dist/src/orgrt/org-start-steps.d.ts.map +1 -0
- package/dist/src/orgrt/org-start-steps.js +305 -0
- package/dist/src/orgrt/org-start-steps.js.map +1 -0
- package/dist/src/orgrt/org-start.d.ts +7 -0
- package/dist/src/orgrt/org-start.d.ts.map +1 -0
- package/dist/src/orgrt/org-start.js +435 -0
- package/dist/src/orgrt/org-start.js.map +1 -0
- package/dist/src/orgrt/org-state-file.d.ts +8 -0
- package/dist/src/orgrt/org-state-file.d.ts.map +1 -0
- package/dist/src/orgrt/org-state-file.js +116 -0
- package/dist/src/orgrt/org-state-file.js.map +1 -0
- package/dist/src/orgrt/org-stop.d.ts +6 -0
- package/dist/src/orgrt/org-stop.d.ts.map +1 -0
- package/dist/src/orgrt/org-stop.js +332 -0
- package/dist/src/orgrt/org-stop.js.map +1 -0
- package/dist/src/orgrt/org-tools.d.ts +11 -0
- package/dist/src/orgrt/org-tools.d.ts.map +1 -0
- package/dist/src/orgrt/org-tools.js +305 -0
- package/dist/src/orgrt/org-tools.js.map +1 -0
- package/dist/src/orgrt/role-incarnation.d.ts +18 -0
- package/dist/src/orgrt/role-incarnation.d.ts.map +1 -0
- package/dist/src/orgrt/role-incarnation.js +374 -0
- package/dist/src/orgrt/role-incarnation.js.map +1 -0
- package/dist/src/orgrt/role-respawn.d.ts +7 -0
- package/dist/src/orgrt/role-respawn.d.ts.map +1 -0
- package/dist/src/orgrt/role-respawn.js +318 -0
- package/dist/src/orgrt/role-respawn.js.map +1 -0
- package/dist/src/orgrt/role-session-opts.d.ts +389 -0
- package/dist/src/orgrt/role-session-opts.d.ts.map +1 -0
- package/dist/src/orgrt/role-session-opts.js +289 -0
- package/dist/src/orgrt/role-session-opts.js.map +1 -0
- package/dist/src/orgrt/runner-resolve.d.ts +43 -0
- package/dist/src/orgrt/runner-resolve.d.ts.map +1 -0
- package/dist/src/orgrt/runner-resolve.js +77 -0
- package/dist/src/orgrt/runner-resolve.js.map +1 -0
- package/dist/src/orgrt/session-gate.d.ts +34 -0
- package/dist/src/orgrt/session-gate.d.ts.map +1 -0
- package/dist/src/orgrt/session-gate.js +74 -0
- package/dist/src/orgrt/session-gate.js.map +1 -0
- package/dist/src/orgrt/session-prompt.d.ts +31 -0
- package/dist/src/orgrt/session-prompt.d.ts.map +1 -0
- package/dist/src/orgrt/session-prompt.js +115 -0
- package/dist/src/orgrt/session-prompt.js.map +1 -0
- package/dist/src/orgrt/session-run.d.ts +18 -0
- package/dist/src/orgrt/session-run.d.ts.map +1 -0
- package/dist/src/orgrt/session-run.js +429 -0
- package/dist/src/orgrt/session-run.js.map +1 -0
- package/dist/src/orgrt/session-stream.d.ts +21 -0
- package/dist/src/orgrt/session-stream.d.ts.map +1 -0
- package/dist/src/orgrt/session-stream.js +173 -0
- package/dist/src/orgrt/session-stream.js.map +1 -0
- package/dist/src/orgrt/session-types.d.ts +219 -0
- package/dist/src/orgrt/session-types.d.ts.map +1 -0
- package/dist/src/orgrt/session-types.js +2 -0
- package/dist/src/orgrt/session-types.js.map +1 -0
- package/dist/src/orgrt/session-usage.d.ts +29 -0
- package/dist/src/orgrt/session-usage.d.ts.map +1 -0
- package/dist/src/orgrt/session-usage.js +64 -0
- package/dist/src/orgrt/session-usage.js.map +1 -0
- package/dist/src/orgrt/session.d.ts +5 -281
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +8 -1117
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/ui/org-hil.mjs +1 -1
- package/dist/src/ui/routes-org-agents.mjs +318 -0
- package/dist/src/ui/routes-org-approvals.mjs +133 -0
- package/dist/src/ui/routes-org-config.mjs +459 -0
- package/dist/src/ui/routes-org-control.mjs +300 -0
- package/dist/src/ui/routes-org-files.mjs +172 -0
- package/dist/src/ui/routes-org-helpers.mjs +84 -0
- package/dist/src/ui/routes-org-knowledge.mjs +262 -0
- package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
- package/dist/src/ui/routes-org-live.mjs +278 -0
- package/dist/src/ui/routes-org-mastermind.mjs +346 -0
- package/dist/src/ui/routes-org-planning.mjs +398 -0
- package/dist/src/ui/routes-org-runs.mjs +430 -0
- package/dist/src/ui/routes-org-status.mjs +342 -0
- package/dist/src/ui/routes-org-workflows.mjs +212 -0
- package/dist/src/ui/routes-org.mjs +26 -3847
- package/dist/src/ui/server.mjs +1 -1
- package/dist/src/utils/native-error.d.ts +1 -1
- package/dist/src/utils/native-error.js +1 -1
- package/dist/src/utils/paths.d.ts +2 -1
- package/dist/src/utils/paths.d.ts.map +1 -1
- package/dist/src/utils/paths.js +2 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/org-skills/adversarial-reviewer/SKILL.md +1 -1
- package/org-skills/board-deck-builder/SKILL.md +1 -1
- package/org-skills/code-reviewer/SKILL.md +1 -1
- package/org-skills/debugging-wizard/SKILL.md +1 -1
- package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
- package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
- package/package.json +5 -5
|
@@ -1,218 +1,14 @@
|
|
|
1
1
|
// packages/@monomind/cli/src/orgrt/session.ts
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { ClaudeAgentRunner, defaultClaudeRunner } from './agent-runner.js';
|
|
4
|
-
import { CLAUDE_SANDBOX_CWD_ENV, claudeBashTimeoutEnv, claudeSandboxCwdNote, } from './bash-timeout.js';
|
|
5
|
-
import { endpointBriefingLines } from './endpoint-roles.js';
|
|
6
|
-
import { scanInput } from './fence.js';
|
|
7
|
-
import { Mailbox } from './mailbox.js';
|
|
8
|
-
import { summarizeToolOutput } from './policy.js';
|
|
9
|
-
import { FaultRestarts, ProcessFaultError } from './sandbox-fault.js';
|
|
10
|
-
import { sandboxStubPaths, sandboxStubs } from './sandbox-stubs.js';
|
|
11
|
-
import { StateDetector } from './state-detector.js';
|
|
12
|
-
import { linkAbort, queueCancelNotice, TaskCancelledError, trackTaskProcess, } from './task-cancel.js';
|
|
13
|
-
import { MAX_TASK_BRIEF } from './task-dag.js';
|
|
14
|
-
import { orgTaskTool } from './task-tools.js';
|
|
15
|
-
import { MAX_BLOCK_RECHECK_MINUTES, } from './types.js';
|
|
16
|
-
/** How long an SDK stream may stay open with zero messages before we say so.
|
|
17
|
-
* Comfortably longer than a slow first turn, shorter than the idle watchdog's
|
|
18
|
-
* 10-minute window so the specific cause is reported before the generic
|
|
19
|
-
* "boss appears hung". */
|
|
20
|
-
const SILENT_SESSION_MS = 4 * 60_000;
|
|
21
|
-
const CONTEXT_LIMIT_RE = /context.window.limit|context.length.exceeded|maximum.context/i;
|
|
22
2
|
import { createHash } from 'node:crypto';
|
|
23
|
-
import { readFileSync } from 'node:fs';
|
|
24
|
-
import { homedir } from 'node:os';
|
|
25
|
-
import { join } from 'node:path';
|
|
26
|
-
import { ensureAuthorityDirs } from './authority-mask.js';
|
|
27
|
-
import { resolveRoleCostTier } from './cost-tier.js';
|
|
28
3
|
import { CumulativeMeter } from './cumulative-meter.js';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { resolveRoleGitEnforcement, roleAuthorityMask } from './role-sandbox.js';
|
|
4
|
+
import { Mailbox } from './mailbox.js';
|
|
5
|
+
import { FaultRestarts, ProcessFaultError } from './sandbox-fault.js';
|
|
32
6
|
import { mailRouteKey, ROLE_SESSION_KEY, resolveSessionScope, SessionLedger, } from './session-ledger.js';
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* Resolves the extra system-prompt block for a role: its pinned library
|
|
39
|
-
* skills and on-demand skill catalog (skill-library.ts) plus the role's own
|
|
40
|
-
* `instructions_file`, if any — all optional and independent. A missing or
|
|
41
|
-
* unreadable `instructions_file` is skipped (not an error): a role shouldn't
|
|
42
|
-
* fail to start a session over a stale/typo'd custom-file path.
|
|
43
|
-
*/
|
|
44
|
-
export function resolveRoleExtraGuidance(role, projectRoot) {
|
|
45
|
-
const parts = [];
|
|
46
|
-
const skills = roleSkillGuidance(role, projectRoot);
|
|
47
|
-
if (skills)
|
|
48
|
-
parts.push(skills);
|
|
49
|
-
if (role.instructions_file) {
|
|
50
|
-
try {
|
|
51
|
-
const custom = readFileSync(role.instructions_file, 'utf-8').trim();
|
|
52
|
-
if (custom)
|
|
53
|
-
parts.push(custom);
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// missing/unreadable custom instructions file — skip, don't crash session start
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return parts.length ? parts.join('\n\n') : undefined;
|
|
60
|
-
}
|
|
61
|
-
/** Resolve the model string for a role: explicit adapter_config.model wins;
|
|
62
|
-
* otherwise fall back to the vendor/runtime default.
|
|
63
|
-
*
|
|
64
|
-
* Vendor defaults are read straight off VERCEL_PROVIDERS, which already
|
|
65
|
-
* carries a defaultModel per vendor. A second hand-kept table lived here and
|
|
66
|
-
* restated all sixteen of them — two lists of per-vendor defaults that would
|
|
67
|
-
* eventually disagree, which is precisely the drift #252 was. An empty
|
|
68
|
-
* registry default (openai-compatible, which serves arbitrary endpoints) is
|
|
69
|
-
* falsy and so falls through to the runtime switch, as it always did. */
|
|
70
|
-
export function resolveModel(role, runtime, vendor) {
|
|
71
|
-
const explicit = role.adapter_config?.model;
|
|
72
|
-
if (explicit)
|
|
73
|
-
return explicit;
|
|
74
|
-
const vendorDefault = vendor ? VERCEL_PROVIDERS[vendor]?.defaultModel : undefined;
|
|
75
|
-
if (vendorDefault)
|
|
76
|
-
return vendorDefault;
|
|
77
|
-
switch (runtime) {
|
|
78
|
-
case 'claude':
|
|
79
|
-
return DEFAULT_CLAUDE_MODEL;
|
|
80
|
-
// Kimi Code CLI namespaces model ids as <provider>/<model> (its own
|
|
81
|
-
// default_model is "kimi-code/kimi-for-coding-highspeed") — a bare "k3"
|
|
82
|
-
// 404s with "Model \"k3\" is not configured in config.toml".
|
|
83
|
-
case 'kimicode':
|
|
84
|
-
return 'kimi-code/k3';
|
|
85
|
-
case 'opencode':
|
|
86
|
-
return 'glm-5.2'; // opencode is typically paired with a vendor; this is the bare-runtime fallback
|
|
87
|
-
case 'codex':
|
|
88
|
-
return 'gpt-5.6-terra';
|
|
89
|
-
case 'antigravity':
|
|
90
|
-
return 'gemini-3.6-flash-high';
|
|
91
|
-
case 'vercel':
|
|
92
|
-
return 'gpt-5.5';
|
|
93
|
-
default:
|
|
94
|
-
return DEFAULT_CLAUDE_MODEL;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/** The SDK's `canUseTool` gate, composed from two independent layers: PolicyEngine's
|
|
98
|
-
* static config checks (deny/allow lists, path scoping, git level, web allowlist,
|
|
99
|
-
* budget), then — only for calls policy would allow — the human-approval guardrail
|
|
100
|
-
* (`beforeTool`, i.e. daemon.checkApproval) for whatever action names it treats as
|
|
101
|
-
* sensitive (Bash/WebFetch/WebSearch/org_complete). Exported standalone so this
|
|
102
|
-
* composition is unit-testable without spinning up a real SDK session: previously
|
|
103
|
-
* `beforeTool` was wired into SessionOpts but never actually called from here, so
|
|
104
|
-
* none of those sensitive actions ever paused for a human. */
|
|
105
|
-
export function gatedCanUseTool(policy, beforeTool, roleId, fence,
|
|
106
|
-
/** Optional hook invoked whenever this gate denies a tool call — wired to
|
|
107
|
-
* daemon.recordDecision() so denials show up in `org decisions` traces.
|
|
108
|
-
* #290: `kind` names WHICH of this function's four deny paths fired, so a
|
|
109
|
-
* consumer never has to tell a fence block from a routine pending approval
|
|
110
|
-
* by matching the English in the message. */
|
|
111
|
-
onDeny,
|
|
112
|
-
/** ORG-9: reports whether this role has a pending (unresolved) decision gate.
|
|
113
|
-
* org_gate is documented as creating a "hard-blocking" checkpoint, but until
|
|
114
|
-
* this was wired in nothing actually stopped tool use while a gate sat
|
|
115
|
-
* pending — only approvals did that. When set and true, ALL tool calls are
|
|
116
|
-
* denied (not just the sensitive subset approvals gate) until the gate is
|
|
117
|
-
* resolved, matching the "hard-blocking" description. */
|
|
118
|
-
hasPendingGate) {
|
|
119
|
-
return async (toolName, input, meta) => {
|
|
120
|
-
if (hasPendingGate?.()) {
|
|
121
|
-
const decision = {
|
|
122
|
-
behavior: 'deny',
|
|
123
|
-
message: `Tool "${toolName}" is blocked — awaiting gate resolution. A decision gate is pending; wait for a human to approve or reject it via 'monomind org gate-approve/gate-reject'.`,
|
|
124
|
-
};
|
|
125
|
-
onDeny?.(toolName, input, decision, 'gate-pending');
|
|
126
|
-
return decision;
|
|
127
|
-
}
|
|
128
|
-
if (fence) {
|
|
129
|
-
const text = typeof input.command === 'string'
|
|
130
|
-
? input.command
|
|
131
|
-
: typeof input.content === 'string'
|
|
132
|
-
? input.content
|
|
133
|
-
: typeof input.url === 'string'
|
|
134
|
-
? input.url
|
|
135
|
-
: JSON.stringify(input);
|
|
136
|
-
const fenceDecision = await scanInput(fence.instance, text, fence.abortThreshold);
|
|
137
|
-
if (fenceDecision.behavior === 'deny') {
|
|
138
|
-
onDeny?.(toolName, input, fenceDecision, 'fence-block');
|
|
139
|
-
return fenceDecision;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const decision = await policy.decide(toolName, input, meta?.toolUseId);
|
|
143
|
-
if (decision.behavior === 'deny') {
|
|
144
|
-
onDeny?.(toolName, input, decision, 'policy-deny');
|
|
145
|
-
return decision;
|
|
146
|
-
}
|
|
147
|
-
if (!beforeTool)
|
|
148
|
-
return decision;
|
|
149
|
-
const approved = await beforeTool(roleId, toolName, input);
|
|
150
|
-
if (approved === false) {
|
|
151
|
-
const denied = {
|
|
152
|
-
behavior: 'deny',
|
|
153
|
-
message: `Tool "${toolName}" was denied by guardrail approval`,
|
|
154
|
-
};
|
|
155
|
-
onDeny?.(toolName, input, denied, 'approval-denied');
|
|
156
|
-
return denied;
|
|
157
|
-
}
|
|
158
|
-
if (approved === null) {
|
|
159
|
-
const pending = {
|
|
160
|
-
behavior: 'deny',
|
|
161
|
-
message: `Tool "${toolName}" is pending human approval — it will be available once approved or denied via 'monomind org approve/deny'.`,
|
|
162
|
-
};
|
|
163
|
-
onDeny?.(toolName, input, pending, 'approval-pending');
|
|
164
|
-
return pending;
|
|
165
|
-
}
|
|
166
|
-
return decision;
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
/** Role briefing given to each agent session (SDK systemPrompt option).
|
|
170
|
-
* `extraGuidance` carries pre-resolved text the caller already loaded from
|
|
171
|
-
* disk — the role's library skills and/or its own `instructions_file`,
|
|
172
|
-
* if either resolved to something. Kept as a plain string param (not read here)
|
|
173
|
-
* so this function stays synchronous/pure and trivially testable. */
|
|
174
|
-
export function buildRolePrompt(role, def, roster, glossary, extraGuidance,
|
|
175
|
-
/** M2: one line per endpoint role (endpointBriefingLines) — boss only. */
|
|
176
|
-
endpointBriefing) {
|
|
177
|
-
const isCoordinator = role.reports_to == null;
|
|
178
|
-
return [
|
|
179
|
-
`You are agent "${role.id}" (${role.title || role.type}) in the org "${def.name}".`,
|
|
180
|
-
`Org goal: ${def.goal}`,
|
|
181
|
-
isCoordinator ? `You are the coordinator of this org.` : `You report to "${role.reports_to}".`,
|
|
182
|
-
role.responsibilities?.length
|
|
183
|
-
? `Your responsibilities:\n- ${role.responsibilities.join('\n- ')}`
|
|
184
|
-
: '',
|
|
185
|
-
extraGuidance || '',
|
|
186
|
-
`## Communication protocol`,
|
|
187
|
-
`The ONLY way to communicate with other agents is the org_send tool.`,
|
|
188
|
-
`Roster: ${roster.join(', ')}. Address another org's agent as "<org-name>:<role-id>".`,
|
|
189
|
-
endpointBriefing?.length ? `Automations in this org:\n${endpointBriefing.join('\n')}` : '',
|
|
190
|
-
`If you need a human decision, call ask_human with your question, then end your turn - you'll receive the human's answer as a new message when it arrives. Do not call ask_human for anything you can resolve yourself.`,
|
|
191
|
-
`For irreversible or high-risk actions (deployments, deletions, external communications), call org_gate to create a decision gate — a hard-blocking approval checkpoint. End your turn and wait for the human's approval or rejection before proceeding.`,
|
|
192
|
-
`You can structure work as a task DAG: use org_task to create tasks with dependencies, org_task_done to mark them complete, and org_tasks to see the full DAG. Tasks with satisfied dependencies are automatically dispatched to their assignee.`,
|
|
193
|
-
`The work graph is dynamic: call org_task_split when scope expands, org_task_merge when parallel branches converge early, or org_task_cancel when evidence makes a planned task moot. Use org_plan_graph to propose a full work graph in one call when you know the plan upfront. If a task genuinely can't proceed until a specific real-world time — a scheduled long-running process, a deadline someone gave you, anything with a known future unblock time — call org_task_block instead of leaving it idle: it stops the idle watchdog from nudging you about it and automatically resumes the task when the time arrives, instead of you repeatedly re-confirming "still waiting" every idle cycle.`,
|
|
194
|
-
`Before starting substantial work, call org_recall to check what previous runs already learned or delivered - do not redo finished work.`,
|
|
195
|
-
`The user's documents (notes, handbooks, specs) are searchable with knowledge_search - ground your work in them instead of guessing; results labeled [global] come from the user's personal cross-project brain.`,
|
|
196
|
-
`When you receive a message, act on it, then org_send your result to the requester.`,
|
|
197
|
-
isCoordinator
|
|
198
|
-
? `When the org's goal for this run is achieved (or clearly can't be): first call org_learn ONCE with the durable knowledge this run produced, then call org_complete exactly once with the outcome and a concise summary. Then end your turn.`
|
|
199
|
-
: `When your current work is complete and no reply is needed, end your turn without further tool calls.`,
|
|
200
|
-
isCoordinator && glossary?.length
|
|
201
|
-
? `Known entities (reuse these EXACT names in org_learn instead of near-duplicates): ${glossary.slice(0, 40).join(', ')}`
|
|
202
|
-
: '',
|
|
203
|
-
]
|
|
204
|
-
.filter(Boolean)
|
|
205
|
-
.join('\n\n');
|
|
206
|
-
}
|
|
207
|
-
/** The system prompt one session of this role is built with. */
|
|
208
|
-
function rolePromptFor(opts) {
|
|
209
|
-
return buildRolePrompt(expandRolePromptVars(opts.role, promptVarsFor(opts.orgRoot ?? opts.cwd)), (opts.def ?? { name: opts.org, goal: '' }), opts.def?.roles.map((r) => r.id) ?? [opts.role.id], opts.glossary,
|
|
210
|
-
// D7: the loadout's text follows the role's own guidance. With no
|
|
211
|
-
// loadout this is exactly resolveRoleExtraGuidance(role), as before.
|
|
212
|
-
[resolveRoleExtraGuidance(opts.role, opts.orgRoot ?? opts.cwd), opts.loadout?.guidance]
|
|
213
|
-
.filter(Boolean)
|
|
214
|
-
.join('\n\n') || undefined, opts.onComplete ? endpointBriefingLines(opts.def) : undefined);
|
|
215
|
-
}
|
|
7
|
+
import { rolePromptFor } from './session-prompt.js';
|
|
8
|
+
import { runOneSession } from './session-run.js';
|
|
9
|
+
import { queueCancelNotice, TaskCancelledError, trackTaskProcess } from './task-cancel.js';
|
|
10
|
+
export { gatedCanUseTool } from './session-gate.js';
|
|
11
|
+
export { buildRolePrompt, resolveModel, resolveRoleExtraGuidance, } from './session-prompt.js';
|
|
216
12
|
/**
|
|
217
13
|
* Runs a role for the life of the org, transparently restarting the
|
|
218
14
|
* underlying SDK session whenever it ends on its own (`maxTurns` reached)
|
|
@@ -594,911 +390,6 @@ async function runAgentSessionLoop(opts) {
|
|
|
594
390
|
}
|
|
595
391
|
}
|
|
596
392
|
}
|
|
597
|
-
|
|
598
|
-
*
|
|
599
|
-
* `cache_read_input_tokens` and `cache_creation_input_tokens` are siblings
|
|
600
|
-
* of `input_tokens` in the Anthropic API, not subsets of it, and both are
|
|
601
|
-
* billable. Everything below therefore sums all four. */
|
|
602
|
-
function totalTokens(u) {
|
|
603
|
-
return u.input + u.output + u.cacheRead + u.cacheCreation;
|
|
604
|
-
}
|
|
605
|
-
function addTo(target, add) {
|
|
606
|
-
target.input += add.input;
|
|
607
|
-
target.output += add.output;
|
|
608
|
-
target.cacheRead += add.cacheRead;
|
|
609
|
-
target.cacheCreation += add.cacheCreation;
|
|
610
|
-
}
|
|
611
|
-
/** One model turn's own usage, off an 'assistant' (or per-turn 'result')
|
|
612
|
-
* message. */
|
|
613
|
-
function turnBreakdown(m) {
|
|
614
|
-
return {
|
|
615
|
-
input: m.input_tokens ?? 0,
|
|
616
|
-
output: m.output_tokens ?? 0,
|
|
617
|
-
cacheRead: m.cache_read_input_tokens ?? 0,
|
|
618
|
-
cacheCreation: m.cache_creation_input_tokens ?? 0,
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
/** What a 'result' message says this mailbox message consumed.
|
|
622
|
-
*
|
|
623
|
-
* When the runner reports `cumulative_tokens` (the Claude SDK's whole-pipeline
|
|
624
|
-
* `modelUsage`, which unlike `usage` includes Task subagents and sidechains),
|
|
625
|
-
* that value is CUMULATIVE per session — the same lifecycle as
|
|
626
|
-
* `total_cost_usd` — so it is converted to a delta by the meter (see
|
|
627
|
-
* cumulative-meter.ts). Without `cumulative_tokens` the per-turn fields are
|
|
628
|
-
* used as before. */
|
|
629
|
-
function resultBreakdown(m, tokenTotals, sid) {
|
|
630
|
-
const cum = m.cumulative_tokens;
|
|
631
|
-
if (!cum)
|
|
632
|
-
return turnBreakdown(m);
|
|
633
|
-
const now = {
|
|
634
|
-
input: cum.input,
|
|
635
|
-
output: cum.output,
|
|
636
|
-
cacheRead: cum.cache_read,
|
|
637
|
-
cacheCreation: cum.cache_creation,
|
|
638
|
-
};
|
|
639
|
-
return tokenTotals ? tokenTotals.delta(sid, now) : now;
|
|
640
|
-
}
|
|
641
|
-
/** ADR-O001 D1: the four quantities travel separately so every downstream
|
|
642
|
-
* consumer (forwarder → dashboard state.json, reporting, `org costs`) can
|
|
643
|
-
* record real values instead of the 0s they used to persist. `tokens` stays
|
|
644
|
-
* the single billable total. */
|
|
645
|
-
function emitUsage(bus, from, t, costUsd, subtype) {
|
|
646
|
-
bus.emit({
|
|
647
|
-
type: 'usage',
|
|
648
|
-
from,
|
|
649
|
-
data: {
|
|
650
|
-
tokens: totalTokens(t),
|
|
651
|
-
cost_usd: costUsd,
|
|
652
|
-
subtype,
|
|
653
|
-
tokens_in: t.input,
|
|
654
|
-
tokens_out: t.output,
|
|
655
|
-
cache_read: t.cacheRead,
|
|
656
|
-
cache_creation: t.cacheCreation,
|
|
657
|
-
},
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
/** One bounded SDK session for a role; resolves with the SDK's session_id (for
|
|
661
|
-
* resuming on restart) and whether it ended by hitting the turn limit (so the
|
|
662
|
-
* caller can push a continuation) when the stream ends (mailbox closed or
|
|
663
|
-
* maxTurns reached). */
|
|
664
|
-
async function runOneSession(opts, resume, costTotals, progress, tokenTotals, streamOpts, faultWatch, cancelled) {
|
|
665
|
-
const { org, role, bus, policy, mailbox, cwd } = opts;
|
|
666
|
-
// Each call starts a new runner process, whose cumulative totals may or may
|
|
667
|
-
// not continue the previous one's (cumulative-meter.ts).
|
|
668
|
-
costTotals?.newProcess();
|
|
669
|
-
tokenTotals?.newProcess();
|
|
670
|
-
// Read lastMessageId live from opts instead of capturing at session start
|
|
671
|
-
// This ensures chat responses link to the most recent message delivered
|
|
672
|
-
const getLastMessageId = () => (opts.lastMessageId ? opts.lastMessageId() : undefined);
|
|
673
|
-
// Resolve runner. Precedence: explicit runner > queryFn-wrapped > default.
|
|
674
|
-
// queryFn stays supported so daemon.ts / test-loop.ts need no changes.
|
|
675
|
-
const runner = opts.runner ?? (opts.queryFn ? new ClaudeAgentRunner(opts.queryFn) : defaultClaudeRunner);
|
|
676
|
-
const tools = buildOrgTools(opts);
|
|
677
|
-
// M1: provider tools are listed per session start, so a hot-reloaded
|
|
678
|
-
// tool_providers block takes effect at the role's next session.
|
|
679
|
-
const providerSet = opts.buildProviderTools ? await opts.buildProviderTools() : undefined;
|
|
680
|
-
if (providerSet)
|
|
681
|
-
tools.push(...providerSet.tools);
|
|
682
|
-
// Named-provider resolution (`adapter_config.provider`): explicit role
|
|
683
|
-
// provider wins, else the named entry from `monomind providers configure`.
|
|
684
|
-
// The named provider's default model fills in adapter_config.model when the
|
|
685
|
-
// role didn't pin one.
|
|
686
|
-
const prov = resolveRoleProvider(role, opts.orgRoot ?? opts.cwd);
|
|
687
|
-
// ADR-O001 D8: the role's cost tier, when the org declares one. Resolved
|
|
688
|
-
// here — the single choke point where a role's model is decided — so the
|
|
689
|
-
// documented precedence holds in exactly one place:
|
|
690
|
-
// explicit adapter_config.model > tier > named-provider default > runtime
|
|
691
|
-
// The tier's EFFORT is applied even when the model came from an explicit
|
|
692
|
-
// pin: which model to run and how hard to think are separate axes, and
|
|
693
|
-
// silently dropping the effort because a model was pinned would be the
|
|
694
|
-
// "silent downgrade" this decision exists to prevent.
|
|
695
|
-
// Throws (fails the session) rather than guessing when the tier has no
|
|
696
|
-
// entry for this role's provider — daemon.ts validates the whole roster
|
|
697
|
-
// up front so that is normally caught before any token is spent.
|
|
698
|
-
const tier = resolveRoleCostTier({
|
|
699
|
-
role,
|
|
700
|
-
def: opts.def,
|
|
701
|
-
vendor: role.provider?.vendor ?? prov.cfg?.vendor,
|
|
702
|
-
});
|
|
703
|
-
const model = role.adapter_config?.model ??
|
|
704
|
-
tier?.model ??
|
|
705
|
-
prov.defaultModel ??
|
|
706
|
-
resolveModel(role, role.runtime, role.provider?.vendor ?? prov.cfg?.vendor);
|
|
707
|
-
bus.emit({ type: 'status', from: role.id, msg: 'session starting' });
|
|
708
|
-
let sessionId = resume;
|
|
709
|
-
let hitTurnLimit = false;
|
|
710
|
-
let contextLimitFired = false;
|
|
711
|
-
// #budget-realtime: real tokens already accounted for the message CURRENTLY
|
|
712
|
-
// in flight, via the per-assistant-turn accounting below — reset to 0 each
|
|
713
|
-
// time a 'result' message ends one mailbox message and the next one starts.
|
|
714
|
-
// Exists purely so the 'result' branch never re-adds what this branch
|
|
715
|
-
// already added (see there for why it can't just always add).
|
|
716
|
-
let messageTurnTokens = { input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
717
|
-
// Abort hook for the runner (AgentRunArgs.signal): the silent-stream
|
|
718
|
-
// abort below used to call iterator.return() only, which queues behind a
|
|
719
|
-
// subprocess runner blocked in `for await (child.stdout)` — the child was
|
|
720
|
-
// never killed, so every supervisor retry stacked another live CLI.
|
|
721
|
-
//
|
|
722
|
-
// Per attempt, linked one way to the caller's externalAbort (#256): the
|
|
723
|
-
// silent-stream abort used to fire the daemon's slot controller itself,
|
|
724
|
-
// permanently. Every retry then started on an already-aborted signal (a
|
|
725
|
-
// runner honoring it kills its child at once) and the daemon's crash
|
|
726
|
-
// backoff, which races that controller to notice an org stop, resolved
|
|
727
|
-
// immediately - the role burned its retries and crashed. An org stop still
|
|
728
|
-
// aborts the attempt; the attempt's own abort stays its own.
|
|
729
|
-
const abort = new AbortController();
|
|
730
|
-
const external = opts.externalAbort?.signal;
|
|
731
|
-
const onExternalAbort = () => abort.abort(external?.reason);
|
|
732
|
-
if (external?.aborted)
|
|
733
|
-
onExternalAbort();
|
|
734
|
-
else
|
|
735
|
-
external?.addEventListener('abort', onExternalAbort, { once: true });
|
|
736
|
-
// org_task_cancel for this process's task: end it the same way (task-cancel.ts).
|
|
737
|
-
// Already aborted when it landed during the setup awaits above.
|
|
738
|
-
const unlinkCancelled = linkAbort(cancelled, abort);
|
|
739
|
-
try {
|
|
740
|
-
// #258: policy.git enforced where git runs, not only by Bash text
|
|
741
|
-
// classification — guard env for every runtime, OS sandbox + file-tool
|
|
742
|
-
// deny rules for Claude. Throws (session fails) when the role requires
|
|
743
|
-
// the sandbox and it can't start.
|
|
744
|
-
// Before the sandbox is built: it can only mask directories that exist.
|
|
745
|
-
ensureAuthorityDirs(homedir(), process.env);
|
|
746
|
-
const gitEnforcement = resolveRoleGitEnforcement({
|
|
747
|
-
org,
|
|
748
|
-
role,
|
|
749
|
-
cwd,
|
|
750
|
-
orgRoot: opts.orgRoot,
|
|
751
|
-
orgDir: opts.orgDir,
|
|
752
|
-
bus,
|
|
753
|
-
claudeRuntime: runner instanceof ClaudeAgentRunner,
|
|
754
|
-
runtime: role.runtime ?? opts.def?.runtime,
|
|
755
|
-
// The sandbox's mount-point stubs, created once and kept until the run
|
|
756
|
-
// ends, so no other role's process deletes one mid-bind (sandbox-stubs.ts).
|
|
757
|
-
// Held before the deny list is built, which keeps a denied cwd read-only
|
|
758
|
-
// when all of them are in place (sandbox-deny-write.ts).
|
|
759
|
-
holdStubs: (writableRoots) => {
|
|
760
|
-
const paths = sandboxStubPaths({ cwd, home: homedir(), writableRoots, env: process.env });
|
|
761
|
-
sandboxStubs.hold(`${org}:${opts.run ?? ''}`, paths);
|
|
762
|
-
return sandboxStubs.missing(paths);
|
|
763
|
-
},
|
|
764
|
-
});
|
|
765
|
-
// What this session really got, not what the config asked for (policy-git.ts).
|
|
766
|
-
policy.setOsSandboxed(!!gitEnforcement.claudeRestrictions?.sandbox);
|
|
767
|
-
const authorityMask = roleAuthorityMask({
|
|
768
|
-
bus,
|
|
769
|
-
roleId: role.id,
|
|
770
|
-
inSdkSandbox: !!gitEnforcement.claudeRestrictions?.sandbox,
|
|
771
|
-
// vercel runs in-process with no shell; its file tools go through the policy engine.
|
|
772
|
-
inProcess: (role.runtime ?? opts.def?.runtime) === 'vercel',
|
|
773
|
-
cwd,
|
|
774
|
-
orgRoot: opts.orgRoot,
|
|
775
|
-
});
|
|
776
|
-
const stream = runner.run({
|
|
777
|
-
tools,
|
|
778
|
-
// No options = the pre-D3 stream, exactly.
|
|
779
|
-
prompt: streamOpts ? mailbox.stream('', streamOpts) : mailbox.stream(),
|
|
780
|
-
systemPrompt: gitEnforcement.claudeRestrictions?.sandbox
|
|
781
|
-
? `${rolePromptFor(opts)}\n\n${claudeSandboxCwdNote(cwd)}`
|
|
782
|
-
: rolePromptFor(opts),
|
|
783
|
-
model,
|
|
784
|
-
cwd,
|
|
785
|
-
effort: tier?.effort,
|
|
786
|
-
env: {
|
|
787
|
-
...resolveProviderEnv(prov.cfg),
|
|
788
|
-
// D8: how a NON-Claude provider expresses the tier's effort level.
|
|
789
|
-
// Empty for Claude (handled natively by ClaudeAgentRunner) and for a
|
|
790
|
-
// provider that declares no mechanism — which simply ignores effort.
|
|
791
|
-
...(tier?.env ?? {}),
|
|
792
|
-
// Claude Code's 2-minute Bash default is too short for org work.
|
|
793
|
-
...(runner instanceof ClaudeAgentRunner
|
|
794
|
-
? claudeBashTimeoutEnv(opts.def?.run_config?.bash_timeout_ms)
|
|
795
|
-
: {}),
|
|
796
|
-
// Custom-endpoint providers (named-provider path): pin the engine's
|
|
797
|
-
// model env so background/haiku tasks also route to the endpoint's
|
|
798
|
-
// model instead of erroring on an Anthropic-only default.
|
|
799
|
-
...(prov.cfg?.authToken
|
|
800
|
-
? { ANTHROPIC_MODEL: model, ANTHROPIC_SMALL_FAST_MODEL: model }
|
|
801
|
-
: {}),
|
|
802
|
-
...gitEnforcement.env,
|
|
803
|
-
...(gitEnforcement.claudeRestrictions?.sandbox ? CLAUDE_SANDBOX_CWD_ENV : {}),
|
|
804
|
-
// No MONOMIND_HOOK_QUIET / MONOMIND_GRAPH_GATE / MONOMIND_SDK_AGENT
|
|
805
|
-
// here (#249): every CLI hands this env to its shell tool, so they
|
|
806
|
-
// reached every command the role ran and silently muted monomind's
|
|
807
|
-
// own hooks, graph gate and tests inside the role. ClaudeAgentRunner
|
|
808
|
-
// loads no filesystem hooks (settingSources: []). The codex/kimi/
|
|
809
|
-
// opencode hook bridges generated by `monomind init` read the
|
|
810
|
-
// MONOMIND_ORG_ROLE marker below and set the quieting vars on the
|
|
811
|
-
// hook-handler process they spawn — hooks stay quiet, commands don't.
|
|
812
|
-
//
|
|
813
|
-
// Per-role scoping for runners that persist state under the org dir
|
|
814
|
-
// (VercelAgentRunner session files). Without these, session files would
|
|
815
|
-
// land in args.cwd (project root for workspace:'repo') under the literal
|
|
816
|
-
// 'default' roleId, polluting the repo and making files unattributable.
|
|
817
|
-
MONOMIND_ORG_DIR: opts.orgDir ?? opts.cwd,
|
|
818
|
-
MONOMIND_ROLE_ID: role.id,
|
|
819
|
-
// M1: attribution for anything the role runs (C-16).
|
|
820
|
-
MONOMIND_ORG_NAME: org,
|
|
821
|
-
MONOMIND_ORG_ROLE: role.id,
|
|
822
|
-
...(opts.run ? { MONOMIND_ORG_RUN: opts.run } : {}),
|
|
823
|
-
...(opts.orgRoot ? { MONOMIND_ORG_ROOT: opts.orgRoot } : {}),
|
|
824
|
-
},
|
|
825
|
-
maxTurns: opts.maxTurns ?? 30,
|
|
826
|
-
maxToolRounds: role.max_tool_rounds ?? opts.def?.run_config?.max_tool_rounds,
|
|
827
|
-
resume,
|
|
828
|
-
claudeRestrictions: gitEnforcement.claudeRestrictions,
|
|
829
|
-
authorityMask,
|
|
830
|
-
// ADR-O001 D2: tool results are 76% of a role's context mass and nothing
|
|
831
|
-
// bounded them. Under the ORG STATE dir (never the workspace cwd, which
|
|
832
|
-
// may be the repo), and under orgRoot — which file-roots.ts already
|
|
833
|
-
// makes readable to the role's file tools and role-sandbox.ts already
|
|
834
|
-
// makes readable to Bash — so the path in the digest actually resolves
|
|
835
|
-
// when the role decides it needs the full text.
|
|
836
|
-
toolSpillDir: join(opts.orgDir ?? opts.cwd, 'tool-results', role.id.replace(/[^a-zA-Z0-9_.-]/g, '_')),
|
|
837
|
-
canUseTool: gatedCanUseTool(policy, opts.beforeTool, role.id, opts.fence, opts.onDecision
|
|
838
|
-
? (toolName, _input, decision, kind) => opts.onDecision?.(role.id, toolName, decision.message ?? 'denied', kind)
|
|
839
|
-
: undefined, opts.hasPendingGate),
|
|
840
|
-
// test seam forwarded through extras: lets the scripted fake SDK
|
|
841
|
-
// (test-loop.ts) drive org_send and tool calls through the real
|
|
842
|
-
// deliver/policy paths; the real SDK ignores it.
|
|
843
|
-
extras: opts.runner
|
|
844
|
-
? undefined
|
|
845
|
-
: {
|
|
846
|
-
_orgTest: {
|
|
847
|
-
deliver: (to, subject, body) => opts.deliver(role.id, to, subject, body),
|
|
848
|
-
callTool: (name, input) => policy.decide(name, input),
|
|
849
|
-
},
|
|
850
|
-
},
|
|
851
|
-
signal: abort.signal,
|
|
852
|
-
// VercelAgentRunner-only fields — ignored by other runners.
|
|
853
|
-
vendor: role.provider?.vendor,
|
|
854
|
-
providerConfig: role.provider,
|
|
855
|
-
});
|
|
856
|
-
// A silent session is its own failure mode, and until now an unnameable
|
|
857
|
-
// one: nine consecutive cycles of a scheduled org opened all seven streams
|
|
858
|
-
// and yielded NOTHING - no assistant message, no result, no error, and no
|
|
859
|
-
// stream end. The only symptom was the idle watchdog reporting the boss
|
|
860
|
-
// "appears hung" twenty minutes later, which described neither the scope
|
|
861
|
-
// (every role) nor the cause.
|
|
862
|
-
//
|
|
863
|
-
// Naming it used to be all this did: log an audit event at 4 minutes and
|
|
864
|
-
// then keep waiting on the same stuck `for await`, so recovery still
|
|
865
|
-
// depended on the org-wide idle watchdog (10m nudge + 10m stop = 20m of
|
|
866
|
-
// dead time per cycle - and it kills the WHOLE run, not just the stuck
|
|
867
|
-
// session). Only the FIRST pull from the stream is raced against the
|
|
868
|
-
// timeout: once any message has arrived the session is demonstrably
|
|
869
|
-
// alive, so a slow-but-working tool call is never mistaken for a stall.
|
|
870
|
-
// On silence, abandon this attempt (best-effort iterator.return() to
|
|
871
|
-
// signal the SDK) and throw - the caller's crash-retry-with-backoff loop
|
|
872
|
-
// (daemon.ts's `runtime.done`) already knows how to retry a failed
|
|
873
|
-
// session with a fresh query() call and, for the boss, escalate to a
|
|
874
|
-
// whole-org restart if it keeps failing. That gives the SDK several
|
|
875
|
-
// fresh attempts within a single cycle instead of one silent attempt
|
|
876
|
-
// followed by twenty minutes of nothing.
|
|
877
|
-
const openedAt = Date.now();
|
|
878
|
-
const detector = new StateDetector();
|
|
879
|
-
const iterator = stream[Symbol.asyncIterator]();
|
|
880
|
-
const SILENT = Symbol('silent');
|
|
881
|
-
let silentTimer;
|
|
882
|
-
const silentMs = opts.silentSessionMs ?? SILENT_SESSION_MS;
|
|
883
|
-
const firstPull = await Promise.race([
|
|
884
|
-
iterator.next(),
|
|
885
|
-
new Promise((resolve) => {
|
|
886
|
-
silentTimer = setTimeout(() => resolve(SILENT), silentMs);
|
|
887
|
-
silentTimer.unref?.();
|
|
888
|
-
}),
|
|
889
|
-
]);
|
|
890
|
-
clearTimeout(silentTimer);
|
|
891
|
-
if (firstPull === SILENT) {
|
|
892
|
-
bus.emit({
|
|
893
|
-
type: 'audit',
|
|
894
|
-
from: role.id,
|
|
895
|
-
reason: 'session-silent',
|
|
896
|
-
msg: `SDK stream open ${Math.round((Date.now() - openedAt) / 1000)}s with zero messages - aborting this attempt and retrying. Set MONOMIND_DEBUG=1 to log raw message types.`,
|
|
897
|
-
});
|
|
898
|
-
// Kill the runner's subprocess FIRST: iterator.return() below cannot
|
|
899
|
-
// reach a runner blocked in its stdout loop, and the retry would
|
|
900
|
-
// otherwise spawn a second CLI next to the still-running first one.
|
|
901
|
-
abort.abort();
|
|
902
|
-
try {
|
|
903
|
-
await Promise.race([
|
|
904
|
-
iterator.return?.(undefined) ?? Promise.resolve(),
|
|
905
|
-
new Promise((r) => {
|
|
906
|
-
const t = setTimeout(() => r(), 2_000);
|
|
907
|
-
t.unref?.();
|
|
908
|
-
}),
|
|
909
|
-
]);
|
|
910
|
-
}
|
|
911
|
-
catch {
|
|
912
|
-
/* best-effort */
|
|
913
|
-
}
|
|
914
|
-
throw new Error(`org "${org}" role "${role.id}": SDK stream silent for ${Math.round(silentMs / 1000)}s with zero messages`);
|
|
915
|
-
}
|
|
916
|
-
const first = firstPull;
|
|
917
|
-
// Replay the first pulled message, then continue draining normally.
|
|
918
|
-
async function* rest() {
|
|
919
|
-
if (!first.done)
|
|
920
|
-
yield first.value;
|
|
921
|
-
while (true) {
|
|
922
|
-
const r = await iterator.next();
|
|
923
|
-
if (r.done)
|
|
924
|
-
return;
|
|
925
|
-
yield r.value;
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
for await (const m of rest()) {
|
|
929
|
-
if (process.env.MONOMIND_DEBUG) {
|
|
930
|
-
console.error(`[orgrt:${org}/${role.id}] runner message type=${m.type} subtype=${String(m.subtype ?? '-')}`);
|
|
931
|
-
}
|
|
932
|
-
if (cancelled?.aborted)
|
|
933
|
-
throw cancelled.reason;
|
|
934
|
-
mailbox.observeTurn(m.type); // the prompt stream outlives a live turn (#331)
|
|
935
|
-
if (m.session_id) {
|
|
936
|
-
sessionId = m.session_id;
|
|
937
|
-
// P2-13: propagate the session ID back to the daemon so checkpoints
|
|
938
|
-
// can resume the SDK session after a crash/restart.
|
|
939
|
-
opts.onSessionId?.(sessionId);
|
|
940
|
-
}
|
|
941
|
-
const prevState = detector.current();
|
|
942
|
-
const textForDetect = m.type === 'assistant' ? m.text || '' : undefined;
|
|
943
|
-
const newState = detector.onMessage(m.type, m.subtype, textForDetect);
|
|
944
|
-
if (newState !== prevState) {
|
|
945
|
-
bus.emit({
|
|
946
|
-
type: 'status',
|
|
947
|
-
from: role.id,
|
|
948
|
-
reason: 'state-change',
|
|
949
|
-
msg: `${prevState} → ${newState}`,
|
|
950
|
-
data: { from: prevState, to: newState },
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
if (m.type === 'assistant') {
|
|
954
|
-
if (progress)
|
|
955
|
-
progress.replied = true;
|
|
956
|
-
const text = m.text || '';
|
|
957
|
-
if (text.trim()) {
|
|
958
|
-
opts.onOutput?.(text);
|
|
959
|
-
bus.emit({ type: 'chat', from: role.id, msg: text, parentId: getLastMessageId() });
|
|
960
|
-
if (opts.onContextLimit && !contextLimitFired && CONTEXT_LIMIT_RE.test(text)) {
|
|
961
|
-
contextLimitFired = true;
|
|
962
|
-
bus.emit({
|
|
963
|
-
type: 'audit',
|
|
964
|
-
from: role.id,
|
|
965
|
-
reason: 'boss-context-limit',
|
|
966
|
-
msg: 'coordinator context window exhausted — requesting whole-org restart with fresh sessions',
|
|
967
|
-
});
|
|
968
|
-
opts.onContextLimit();
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
// #budget-realtime (HIGH): the SDK's 'result' message arrives once per
|
|
972
|
-
// WHOLE mailbox message in streaming-input mode — with
|
|
973
|
-
// max_turns_per_message defaulting to 100,000, a single message can
|
|
974
|
-
// internally loop through hundreds/thousands of tool-use turns before
|
|
975
|
-
// that 'result' ever arrives, during which policy.used never moved and
|
|
976
|
-
// policy.decide() allowed every one of those turns' tool calls
|
|
977
|
-
// regardless of real spend (the overspend was already done by the time
|
|
978
|
-
// overBudget could ever trip). Each 'assistant' SDK message DOES carry
|
|
979
|
-
// that ONE model turn's real usage (agent-runner.ts reads it off
|
|
980
|
-
// BetaMessage.usage) — accumulate it as turns actually happen and
|
|
981
|
-
// enforce the budget immediately, so overBudget can close the mailbox
|
|
982
|
-
// DURING a runaway message instead of only once it finally completes.
|
|
983
|
-
// (USD budget can't get the same real-time treatment: the SDK only
|
|
984
|
-
// exposes cost as a cumulative total on 'result', not per-turn on
|
|
985
|
-
// 'assistant' — verified against this project's Claude Agent SDK
|
|
986
|
-
// .d.ts, which puts `usage`/token counts on BetaMessage but cost only
|
|
987
|
-
// on SDKResultSuccess.total_cost_usd/modelUsage. overBudgetUsd is
|
|
988
|
-
// still checked below, once per message, same as before this fix.)
|
|
989
|
-
//
|
|
990
|
-
// ADR-O001 D1: the sum must include BOTH cache fields. They are
|
|
991
|
-
// siblings of input_tokens in the Anthropic API, not subsets of it —
|
|
992
|
-
// `input_tokens` is the uncached remainder — and both are billable
|
|
993
|
-
// (~0.1x and ~1.25x input). Omitting them meant the better the cache
|
|
994
|
-
// worked the less the meter saw: on one measured run, 2,765M tokens
|
|
995
|
-
// billed against 8.1M recorded, with input_tokens at 0.0M.
|
|
996
|
-
const turn = turnBreakdown(m);
|
|
997
|
-
const turnTokens = totalTokens(turn);
|
|
998
|
-
if (turnTokens > 0) {
|
|
999
|
-
addTo(messageTurnTokens, turn);
|
|
1000
|
-
policy.addTokenUsage(turn);
|
|
1001
|
-
if (policy.overBudget) {
|
|
1002
|
-
bus.emit({
|
|
1003
|
-
type: 'status',
|
|
1004
|
-
from: role.id,
|
|
1005
|
-
reason: 'budget-exhausted',
|
|
1006
|
-
msg: 'token budget exhausted - closing session',
|
|
1007
|
-
});
|
|
1008
|
-
mailbox.close('token-budget');
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
else if (m.type === 'tool_result') {
|
|
1013
|
-
// #289: the tool call's outcome. Until this event existed, a Bash
|
|
1014
|
-
// running a test suite looked identical on the bus whether the suite
|
|
1015
|
-
// passed, failed, or the binary was missing — one 'allow' at the moment
|
|
1016
|
-
// it started — so every consumer inferred success from the agent's own
|
|
1017
|
-
// narration. `call_id` joins this back to that invocation event; the
|
|
1018
|
-
// body is redacted and capped (policy.ts) so a megabyte of output, or a
|
|
1019
|
-
// credential echoed by a command, never lands in bus.jsonl.
|
|
1020
|
-
const body = summarizeToolOutput(m.text ?? '');
|
|
1021
|
-
const data = {
|
|
1022
|
-
...(m.tool_use_id ? { call_id: m.tool_use_id } : {}),
|
|
1023
|
-
ok: m.is_error !== true,
|
|
1024
|
-
...(typeof m.duration_ms === 'number' ? { duration_ms: m.duration_ms } : {}),
|
|
1025
|
-
output: body.output,
|
|
1026
|
-
...(body.truncated ? { truncated: true } : {}),
|
|
1027
|
-
output_chars: body.output_chars,
|
|
1028
|
-
};
|
|
1029
|
-
bus.emit({
|
|
1030
|
-
type: 'tool_result',
|
|
1031
|
-
from: role.id,
|
|
1032
|
-
...(m.tool ? { tool: m.tool } : {}),
|
|
1033
|
-
data: data,
|
|
1034
|
-
});
|
|
1035
|
-
faultWatch?.observe(m, sessionId);
|
|
1036
|
-
}
|
|
1037
|
-
else if (m.type === 'result') {
|
|
1038
|
-
// ADR-O001 D1: prefer the SDK's `modelUsage` over `usage`. The SDK
|
|
1039
|
-
// documents `usage` as "MAIN AGENT LOOP ONLY — excludes Task
|
|
1040
|
-
// subagent, sidechain, and auxiliary model calls ... Prefer
|
|
1041
|
-
// modelUsage for token/cost accounting"; the measured run made 46
|
|
1042
|
-
// subagent calls this counter never saw. modelUsage is CUMULATIVE per
|
|
1043
|
-
// session (same lifecycle as total_cost_usd, per its own type doc),
|
|
1044
|
-
// so it is converted to a delta here rather than added, exactly as
|
|
1045
|
-
// cost is below. A runner that reports no modelUsage falls back to
|
|
1046
|
-
// the per-turn `usage` fields, which keep their old semantics.
|
|
1047
|
-
const resultTokens = resultBreakdown(m, tokenTotals, m.session_id ?? sessionId ?? '');
|
|
1048
|
-
// Per the SDK's own type docs, a 'result' message's usage is that
|
|
1049
|
-
// message's own (effectively last-turn) usage in streaming-input mode,
|
|
1050
|
-
// NOT a cumulative total across every turn of the mailbox message —
|
|
1051
|
-
// and that last turn was already counted above via its own 'assistant'
|
|
1052
|
-
// message, specifically so overBudget could trip mid-message. Adding
|
|
1053
|
-
// the result's own usage again unconditionally would double-count it.
|
|
1054
|
-
// (A modelUsage-derived delta is per-session-cumulative, so the same
|
|
1055
|
-
// subtraction is exactly right there too: it removes what the
|
|
1056
|
-
// assistant turns of THIS message already contributed and leaves the
|
|
1057
|
-
// subagent/auxiliary volume the main loop never reported.) Only make
|
|
1058
|
-
// up the shortfall (never negative) so a turn whose usage somehow
|
|
1059
|
-
// never reached the 'assistant' branch (e.g. a runner/test double that
|
|
1060
|
-
// doesn't emit per-turn usage) still gets counted at least once.
|
|
1061
|
-
const shortfall = {
|
|
1062
|
-
input: Math.max(0, resultTokens.input - messageTurnTokens.input),
|
|
1063
|
-
output: Math.max(0, resultTokens.output - messageTurnTokens.output),
|
|
1064
|
-
cacheRead: Math.max(0, resultTokens.cacheRead - messageTurnTokens.cacheRead),
|
|
1065
|
-
cacheCreation: Math.max(0, resultTokens.cacheCreation - messageTurnTokens.cacheCreation),
|
|
1066
|
-
};
|
|
1067
|
-
if (totalTokens(shortfall) > 0)
|
|
1068
|
-
policy.addTokenUsage(shortfall);
|
|
1069
|
-
// What this whole mailbox message actually added to the meter: the
|
|
1070
|
-
// per-turn accounting above plus whatever the result topped up. This
|
|
1071
|
-
// is what the 'usage' event reports, so a consumer summing events
|
|
1072
|
-
// lands on the same number as policy.usage.
|
|
1073
|
-
const messageTokens = {
|
|
1074
|
-
input: messageTurnTokens.input + shortfall.input,
|
|
1075
|
-
output: messageTurnTokens.output + shortfall.output,
|
|
1076
|
-
cacheRead: messageTurnTokens.cacheRead + shortfall.cacheRead,
|
|
1077
|
-
cacheCreation: messageTurnTokens.cacheCreation + shortfall.cacheCreation,
|
|
1078
|
-
};
|
|
1079
|
-
messageTurnTokens = { input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
1080
|
-
// Convert the SDK's cumulative total_cost_usd into a per-result
|
|
1081
|
-
// delta before emitting - downstream sums usage events. A new session
|
|
1082
|
-
// id counts in full; a same-process dip (rounding, a provider-side
|
|
1083
|
-
// correction) floors at 0 rather than re-adding the cumulative cost,
|
|
1084
|
-
// which feeds USD budget enforcement (ORG-7). A resume in a new
|
|
1085
|
-
// process is judged by the meter (cumulative-meter.ts).
|
|
1086
|
-
let costDelta = m.cost_usd;
|
|
1087
|
-
if (costTotals && typeof m.cost_usd === 'number' && Number.isFinite(m.cost_usd)) {
|
|
1088
|
-
const sid = m.session_id ?? sessionId ?? '';
|
|
1089
|
-
costDelta = costTotals.delta(sid, { usd: m.cost_usd }).usd;
|
|
1090
|
-
}
|
|
1091
|
-
// ORG-7: accumulate real USD cost so policy.overBudgetUsd (role.budget_usd) is enforceable.
|
|
1092
|
-
if (typeof costDelta === 'number' && Number.isFinite(costDelta))
|
|
1093
|
-
policy.addUsageUsd(costDelta);
|
|
1094
|
-
emitUsage(bus, role.id, messageTokens, costDelta, m.subtype);
|
|
1095
|
-
if (m.subtype && m.subtype !== 'success') {
|
|
1096
|
-
if (m.subtype === 'error_max_turns')
|
|
1097
|
-
hitTurnLimit = true;
|
|
1098
|
-
bus.emit({
|
|
1099
|
-
type: 'audit',
|
|
1100
|
-
from: role.id,
|
|
1101
|
-
reason: 'session-result-error',
|
|
1102
|
-
msg: `turn ended with subtype "${m.subtype}"${m.is_error ? ' (is_error)' : ''} - the role produced no usable output`,
|
|
1103
|
-
});
|
|
1104
|
-
if (opts.circuitBreaker && m.subtype !== 'error_max_turns') {
|
|
1105
|
-
const cb = opts.circuitBreaker;
|
|
1106
|
-
cb.state.failures++;
|
|
1107
|
-
if (cb.state.failures >= cb.threshold) {
|
|
1108
|
-
cb.state.tripped = true;
|
|
1109
|
-
bus.emit({
|
|
1110
|
-
type: 'audit',
|
|
1111
|
-
from: role.id,
|
|
1112
|
-
reason: 'circuit-breaker-tripped',
|
|
1113
|
-
msg: `circuit breaker tripped after ${cb.state.failures} consecutive failures — closing role`,
|
|
1114
|
-
data: { failures: cb.state.failures, threshold: cb.threshold },
|
|
1115
|
-
});
|
|
1116
|
-
mailbox.close();
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
else if (m.subtype === 'success' && opts.circuitBreaker) {
|
|
1121
|
-
opts.circuitBreaker.state.failures = 0;
|
|
1122
|
-
}
|
|
1123
|
-
if (policy.overBudget) {
|
|
1124
|
-
bus.emit({
|
|
1125
|
-
type: 'status',
|
|
1126
|
-
from: role.id,
|
|
1127
|
-
reason: 'budget-exhausted',
|
|
1128
|
-
msg: 'token budget exhausted - closing session',
|
|
1129
|
-
});
|
|
1130
|
-
// #205: tag WHY the mailbox closed. A budget-exhausted boss is
|
|
1131
|
-
// recoverable (raise the budget, resume) — the idle watchdog reads
|
|
1132
|
-
// this to stop reporting it as generic "unreachable" (crash-like).
|
|
1133
|
-
mailbox.close('token-budget');
|
|
1134
|
-
}
|
|
1135
|
-
// ORG-7: parallel USD-budget enforcement, same pattern as the token check above.
|
|
1136
|
-
if (policy.overBudgetUsd) {
|
|
1137
|
-
bus.emit({
|
|
1138
|
-
type: 'status',
|
|
1139
|
-
from: role.id,
|
|
1140
|
-
reason: 'budget-exhausted',
|
|
1141
|
-
msg: 'USD budget exhausted - closing session',
|
|
1142
|
-
});
|
|
1143
|
-
mailbox.close('usd-budget');
|
|
1144
|
-
}
|
|
1145
|
-
// The turn is over: whatever tool calls it was going to make, it has
|
|
1146
|
-
// made. What the role left open is knowable here (decisions.ts's
|
|
1147
|
-
// nudgeOpenTasksAtTurnEnd) instead of only to the idle watchdog.
|
|
1148
|
-
opts.onTurnEnd?.();
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (cancelled?.aborted)
|
|
1152
|
-
throw cancelled.reason;
|
|
1153
|
-
bus.emit({ type: 'status', from: role.id, msg: 'session ended' });
|
|
1154
|
-
return { sessionId, hitTurnLimit };
|
|
1155
|
-
}
|
|
1156
|
-
catch (err) {
|
|
1157
|
-
// The turn in flight never got its 'result', so its metered turns (already
|
|
1158
|
-
// in policy) have no usage event yet. Cost is only on 'result': unknown.
|
|
1159
|
-
if (totalTokens(messageTurnTokens) > 0)
|
|
1160
|
-
emitUsage(bus, role.id, messageTurnTokens, undefined, 'aborted');
|
|
1161
|
-
// Ending the process was the point; sandbox-fault.ts already audited it.
|
|
1162
|
-
if (err instanceof ProcessFaultError)
|
|
1163
|
-
throw err;
|
|
1164
|
-
if (cancelled?.aborted)
|
|
1165
|
-
throw cancelled.reason;
|
|
1166
|
-
// org_complete / org stop close the mailbox and abort every session: a
|
|
1167
|
-
// normal stop, not a failure. The daemon still classifies it.
|
|
1168
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1169
|
-
if ((mailbox.isClosed || (external?.aborted ?? false)) &&
|
|
1170
|
-
(err?.name === 'AbortError' || /\baborted\b/i.test(message))) {
|
|
1171
|
-
bus.emit({
|
|
1172
|
-
type: 'status',
|
|
1173
|
-
from: role.id,
|
|
1174
|
-
reason: 'session-stopped',
|
|
1175
|
-
msg: 'session stopped',
|
|
1176
|
-
});
|
|
1177
|
-
throw err;
|
|
1178
|
-
}
|
|
1179
|
-
// #304: the daemon's role loop catches this same error one step later and
|
|
1180
|
-
// emits the authoritative CLASSIFIED status — crashed / stopped with the
|
|
1181
|
-
// org / terminated by stop — carrying the real error text when it is a
|
|
1182
|
-
// genuine crash (daemon.ts's 'agent-session-crash' audit). This
|
|
1183
|
-
// breadcrumb must not pre-empt that with the raw SDK string: on a
|
|
1184
|
-
// planned stop it announced "Claude Code process aborted by user" about
|
|
1185
|
-
// a stop nobody requested. It deliberately does NOT classify —
|
|
1186
|
-
// session.ts relays, the daemon decides — and it carries a `reason` so
|
|
1187
|
-
// it is filterable; its absence is why every #304/#251 test was
|
|
1188
|
-
// structurally unable to see this event.
|
|
1189
|
-
bus.emit({
|
|
1190
|
-
type: 'status',
|
|
1191
|
-
from: role.id,
|
|
1192
|
-
reason: 'session-error',
|
|
1193
|
-
msg: 'session ended with an error — see the classified status that follows',
|
|
1194
|
-
});
|
|
1195
|
-
throw err;
|
|
1196
|
-
}
|
|
1197
|
-
finally {
|
|
1198
|
-
// Unlink so a long-lived role doesn't pile a listener per attempt onto the
|
|
1199
|
-
// slot controller. Aborting the finished attempt keeps a runner abandoned
|
|
1200
|
-
// mid-stream by a throw from outliving it now that an org stop can no
|
|
1201
|
-
// longer reach it.
|
|
1202
|
-
external?.removeEventListener('abort', onExternalAbort);
|
|
1203
|
-
unlinkCancelled();
|
|
1204
|
-
abort.abort();
|
|
1205
|
-
providerSet?.close();
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
/** Build the org tool surface as platform-agnostic OrgToolDef[]. The handlers
|
|
1209
|
-
* close over sessionOpts callbacks (deliver, recall, remember, …) — same
|
|
1210
|
-
* wiring as the previous inline createSdkMcpServer block, just decoupled from
|
|
1211
|
-
* the Claude SDK's tool() shape so any AgentRunner can host them.
|
|
1212
|
-
*
|
|
1213
|
-
* Behaviour is identical to the old inline definitions: conditional tools are
|
|
1214
|
-
* gated on their callback being present, org_send/ask_human are always added. */
|
|
393
|
+
export { buildOrgTools } from './org-tools.js';
|
|
1215
394
|
export { AUTO_ASSIGNEE } from './task-tools.js';
|
|
1216
|
-
export function buildOrgTools(opts) {
|
|
1217
|
-
const { role, deliver } = opts;
|
|
1218
|
-
const tools = [];
|
|
1219
|
-
const text = (t) => ({ text: t });
|
|
1220
|
-
const searchKnowledge = opts.searchKnowledge;
|
|
1221
|
-
if (searchKnowledge) {
|
|
1222
|
-
tools.push({
|
|
1223
|
-
name: 'knowledge_search',
|
|
1224
|
-
description: "Semantic search over the user's Second Brain: this project's indexed documents plus their personal cross-project global brain. Use to ground work in the user's actual notes, handbooks, and documents.",
|
|
1225
|
-
schema: { query: z.string() },
|
|
1226
|
-
handler: async (args) => text(await searchKnowledge(role.id, args.query)),
|
|
1227
|
-
});
|
|
1228
|
-
}
|
|
1229
|
-
tools.push(...skillTools(role, opts.orgRoot ?? opts.cwd, opts.onSkillLoad));
|
|
1230
|
-
const recall = opts.recall;
|
|
1231
|
-
if (recall) {
|
|
1232
|
-
tools.push({
|
|
1233
|
-
name: 'org_recall',
|
|
1234
|
-
description: "Search this org's accumulated memory from previous runs (outcomes, decisions, learnings). Use before starting work that may already have been done.",
|
|
1235
|
-
schema: { query: z.string() },
|
|
1236
|
-
handler: async (args) => text(await recall(role.id, args.query)),
|
|
1237
|
-
});
|
|
1238
|
-
}
|
|
1239
|
-
const remember = opts.remember;
|
|
1240
|
-
if (remember) {
|
|
1241
|
-
tools.push({
|
|
1242
|
-
name: 'org_remember',
|
|
1243
|
-
description: 'Save a memory for future runs. scope "org" (default) shares it with the whole org; scope "agent" keeps it private to your role. Use for decisions, findings, and state worth recalling later - org_recall searches both.',
|
|
1244
|
-
schema: { content: z.string(), scope: z.enum(['org', 'agent']).optional() },
|
|
1245
|
-
handler: async (args) => text(await remember(role.id, args.content, args.scope ?? 'org')),
|
|
1246
|
-
});
|
|
1247
|
-
}
|
|
1248
|
-
const learn = opts.learn;
|
|
1249
|
-
if (learn) {
|
|
1250
|
-
tools.push({
|
|
1251
|
-
name: 'org_learn',
|
|
1252
|
-
description: "Persist durable knowledge from this run into the org's knowledge graph: entities ({name, type?, description?}), relationships ({source, target, relation, description?}) and reusable rules ({rule, context?}). Entities merge by name across runs - reuse the exact names listed in your briefing. Call once, before org_complete.",
|
|
1253
|
-
schema: {
|
|
1254
|
-
nodes: z
|
|
1255
|
-
.array(strictArgs({
|
|
1256
|
-
name: z.string(),
|
|
1257
|
-
type: z.string().optional(),
|
|
1258
|
-
description: z.string().optional(),
|
|
1259
|
-
}))
|
|
1260
|
-
.optional(),
|
|
1261
|
-
edges: z
|
|
1262
|
-
.array(strictArgs({
|
|
1263
|
-
source: z.string(),
|
|
1264
|
-
target: z.string(),
|
|
1265
|
-
relation: z.string(),
|
|
1266
|
-
description: z.string().optional(),
|
|
1267
|
-
}))
|
|
1268
|
-
.optional(),
|
|
1269
|
-
rules: z.array(strictArgs({ rule: z.string(), context: z.string().optional() })).optional(),
|
|
1270
|
-
},
|
|
1271
|
-
handler: async (args) => text(await learn(role.id, args)),
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
// Gate purely on onComplete: the daemon passes it only to the role its
|
|
1275
|
-
// boss-selection rule picked, so tool availability always matches the
|
|
1276
|
-
// kickoff instruction (reports_to may be non-null for a fallback boss).
|
|
1277
|
-
if (opts.onComplete) {
|
|
1278
|
-
tools.push({
|
|
1279
|
-
name: 'org_complete',
|
|
1280
|
-
description: '⚠️ Ends the run — every agent session shuts down after this. Call exactly once, and only against the org\'s actual, full stated goal (see your briefing), never against just the current batch of dispatched tasks. "achieved" means the WHOLE goal is done, not "everyone assigned so far finished their piece" — a multi-phase or open-ended goal is very rarely achieved in a single run. If the current task batch is clearly done but the goal has more scope left: do NOT call this — use org_task/createTask to dispatch the next phase\'s work instead, so the org keeps making progress instead of stopping short. "achieved" only when the full goal is met; "failed" only when it clearly cannot be — neither requires a blocker (though if this org\'s run_config.completion is set to \'dag\', "achieved" is ALSO refused while org_tasks still has runnable work that is not blocked on a real-world time — check org_tasks first if that setting applies to you). Use outcome "partial" only when you are ending the run with real scope still remaining, and you MUST name why with `blocker`: \'budget\' (a role is genuinely near its token/USD ceiling), \'human\' (an ask_human question or decision gate is actually pending), \'time\' (if the only thing blocking further work is a scheduled process or deadline you know the time of, call org_task_block instead of ending the run so a future run can pick back up automatically — this blocker is refused unless a task is actually blocked that way), or \'external\' (anything else — `blockerDetail` must be a real, substantive explanation; placeholders like "none"/"n/a" and anything under 10 characters are refused, because this is the one blocker nothing else can check, and it is recorded verbatim in the run history under your name). A refusal names which of these to use instead. Before calling, check org_tasks — siblings with in-progress work only get a short drain window to finish before being cut off, so do not call this while others are still mid-build or mid-edit unless the run genuinely cannot continue. The outcome and summary are persisted to the org run history and briefed to the next run. If the run produced a concrete deliverable (a post, document, message, piece of content, code, etc.), summary MUST include that deliverable\'s full text verbatim — not a meta-description of what happened. Someone reading only summary should be able to see the actual result, not just that "a result was produced".',
|
|
1281
|
-
schema: {
|
|
1282
|
-
outcome: z.enum(['achieved', 'partial', 'failed']),
|
|
1283
|
-
summary: z.string(),
|
|
1284
|
-
blocker: z.enum(['budget', 'human', 'external', 'time']).optional(),
|
|
1285
|
-
blockerDetail: z.string().optional(),
|
|
1286
|
-
},
|
|
1287
|
-
handler: async (args) => {
|
|
1288
|
-
const refusal = opts.onComplete?.(role.id, args.outcome, args.summary, args.blocker, args.blockerDetail);
|
|
1289
|
-
// #302: a refusal must not claim the outcome was recorded — nothing
|
|
1290
|
-
// was, and the daemon never emitted the event that would stop the run.
|
|
1291
|
-
if (refusal)
|
|
1292
|
-
return text(refusal);
|
|
1293
|
-
return text(`outcome "${args.outcome}" recorded`);
|
|
1294
|
-
},
|
|
1295
|
-
});
|
|
1296
|
-
}
|
|
1297
|
-
const onRespawnRole = opts.onRespawnRole;
|
|
1298
|
-
if (onRespawnRole) {
|
|
1299
|
-
tools.push({
|
|
1300
|
-
name: 'org_respawn_role',
|
|
1301
|
-
description: "Replace one crashed, exhausted, or unsuitable WORKER role with a fresh session — keeping its role id, workspace, task ownership, and queued messages. Cannot target the coordinator (yourself) or an unknown/removed/not-yet-started role. Omit runtime/model/providerName to keep their current values. Omitted budgetTokens uses the normal per-role allocation for this run; it cannot raise the org-wide token budget. reason is a short operational reason for the audit log; briefing is what the replacement should know to continue the work — it starts a FRESH model session with no memory of the old one's conversation, so include everything it needs.",
|
|
1302
|
-
schema: {
|
|
1303
|
-
roleId: z.string(),
|
|
1304
|
-
runtime: z.string().optional(),
|
|
1305
|
-
model: z.string().optional(),
|
|
1306
|
-
providerName: z.string().optional(),
|
|
1307
|
-
budgetTokens: z.number().optional(),
|
|
1308
|
-
reason: z.string(),
|
|
1309
|
-
briefing: z.string(),
|
|
1310
|
-
},
|
|
1311
|
-
handler: async (args) => {
|
|
1312
|
-
const receipt = await onRespawnRole(role.id, args);
|
|
1313
|
-
return text(JSON.stringify(receipt));
|
|
1314
|
-
},
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
const onListRuntimeOptions = opts.onListRuntimeOptions;
|
|
1318
|
-
if (onListRuntimeOptions) {
|
|
1319
|
-
tools.push({
|
|
1320
|
-
name: 'org_list_runtime_options',
|
|
1321
|
-
description: 'List every runtime this daemon knows how to run a role on (availability, detected binary/version, install hint) and every named provider configured for this project (name and default model only — never keys, tokens, or endpoints). Use before org_respawn_role to pick a real runtime/providerName. "available" means locally resolvable/detected, not that login, quota, or a remote model is valid.',
|
|
1322
|
-
schema: {},
|
|
1323
|
-
handler: async () => text(JSON.stringify(await onListRuntimeOptions())),
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
1326
|
-
const onGate = opts.onGate;
|
|
1327
|
-
if (onGate) {
|
|
1328
|
-
tools.push({
|
|
1329
|
-
name: 'org_gate',
|
|
1330
|
-
description: 'Create a decision gate — a hard-blocking human-approval checkpoint. Use before irreversible actions (deployments, deletions, external comms). The gate pauses your work until a human approves or rejects it. End your turn after calling this; you will receive the resolution as a new message.',
|
|
1331
|
-
schema: { name: z.string(), description: z.string() },
|
|
1332
|
-
handler: async (args) => text(await onGate(role.id, args.name, args.description)),
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
// ADR-O001 D7: the `loadout` argument exists only for an org with a
|
|
1336
|
-
// catalog, so every other org's tool list stays byte-identical.
|
|
1337
|
-
const catalog = opts.loadoutCatalog?.length ? opts.loadoutCatalog : undefined;
|
|
1338
|
-
const loadoutArg = catalog
|
|
1339
|
-
? { loadout: z.enum(catalog.map((l) => l.name)).optional() }
|
|
1340
|
-
: {};
|
|
1341
|
-
// Sent with the dispatch itself (task-provenance.ts dispatchLine), so the
|
|
1342
|
-
// instructions arrive with the task instead of in a follow-up message.
|
|
1343
|
-
const briefArg = z.string().max(MAX_TASK_BRIEF).optional();
|
|
1344
|
-
const orgTask = orgTaskTool(opts, loadoutArg, briefArg);
|
|
1345
|
-
if (orgTask)
|
|
1346
|
-
tools.push(orgTask);
|
|
1347
|
-
const completeTask = opts.completeTask;
|
|
1348
|
-
if (completeTask) {
|
|
1349
|
-
tools.push({
|
|
1350
|
-
name: 'org_task_done',
|
|
1351
|
-
description: opts.requireTaskEvidence
|
|
1352
|
-
? 'Mark a task as completed. This org requires EVIDENCE (run_config.completion_evidence): pass `evidence` with the current commit sha of the work (the HEAD of any worktree of this repository, or any local branch tip), `worktree` naming the worktree you ran in when it is not the org workspace, and one entry per acceptance criterion — the command you actually ran, its real exit code, and its output. A check passes when its exit code equals `expectExit` (default 0): when the criterion is met by a non-zero exit (a lookup that must find nothing → 1, a timeout that must fire → 124), set `expectExit` instead of appending `|| true`, which erases the exit code, and always say why in a one-line `expectReason` ("404 = branch not protected") — `expectExit` without it is refused. `expectExit` is only for a SINGLE-PURPOSE command: on a test suite or any other aggregate runner (vitest, jest, `pnpm test`, `pnpm -r`, a verify script) it is refused outright, because a suite exit code means "at least one of many things failed" and declaring it expected accepts every other failure too — run the one failing test file on its own and declare `expectExit` on that, or exclude the known failure and record the exclusion in `result`. A task whose job is to REPORT (QA, an audit) closes on commands that prove the report exists and is complete (e.g. `test -s <report file>`); the failures it found are findings — put them in `result` and send them to the coordinator, not in `checks`. If you tested something outside a git worktree (a scratch dir, an installed tarball), pin `headSha`/`worktree` to the worktree the artifact was built from. Evidence pinned to a commit that is no longer the head of that work is stale and will be refused, and a refused completion puts the task back in your queue with the reason — but only up to run_config.max_evidence_attempts refused proofs (default 3; a call with no `evidence` at all is refused without counting), after which the task is recorded as failed and escalated to the boss instead of returned to you. Any downstream tasks whose deps are now all done become ready and are dispatched.'
|
|
1353
|
-
: 'Mark a task as completed and optionally provide a result summary. Any downstream tasks whose deps are now all done will become ready and be dispatched.',
|
|
1354
|
-
schema: {
|
|
1355
|
-
taskId: z.string(),
|
|
1356
|
-
result: z.string().optional(),
|
|
1357
|
-
evidence: strictArgs({
|
|
1358
|
-
headSha: z.string(),
|
|
1359
|
-
worktree: z.string().optional(),
|
|
1360
|
-
checks: z
|
|
1361
|
-
.array(strictArgs({
|
|
1362
|
-
command: z.string(),
|
|
1363
|
-
exitCode: z.number().int(),
|
|
1364
|
-
expectExit: z.number().int().optional(),
|
|
1365
|
-
expectReason: z.string().optional(),
|
|
1366
|
-
output: z.string().optional(),
|
|
1367
|
-
}))
|
|
1368
|
-
.default([]),
|
|
1369
|
-
}).optional(),
|
|
1370
|
-
},
|
|
1371
|
-
handler: async (args) => text(completeTask(role.id, args.taskId, args.result, args.evidence)),
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
|
-
const requestReview = opts.requestReview;
|
|
1375
|
-
if (requestReview) {
|
|
1376
|
-
tools.push({
|
|
1377
|
-
name: 'org_review',
|
|
1378
|
-
description: "Ask an artifact-only reviewer for a verdict on a task. You pass ids only: the runtime builds the review from the task's text, its assignee's latest org_task_done evidence (commands, exit codes, output) and its own git diff of base...headSha — nothing you write is added, so there is no summary to give. The reviewer starts cold every time and replies to you with org_send. Refused if the task has no evidence yet.",
|
|
1379
|
-
schema: {
|
|
1380
|
-
taskId: z.string(),
|
|
1381
|
-
reviewer: z.string(),
|
|
1382
|
-
base: z.string().optional().describe("git ref to diff against (default 'main')"),
|
|
1383
|
-
},
|
|
1384
|
-
handler: async (args) => text(requestReview(role.id, args.taskId, args.reviewer, args.base)),
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
const listTasks = opts.listTasks;
|
|
1388
|
-
if (listTasks) {
|
|
1389
|
-
tools.push({
|
|
1390
|
-
name: 'org_tasks',
|
|
1391
|
-
description: 'List all tasks in the DAG with their current status and dependencies. Pass `taskId` to get just that task, including its result and latest evidence.',
|
|
1392
|
-
schema: { taskId: z.string().optional() },
|
|
1393
|
-
handler: async (args) => text(listTasks(args.taskId)),
|
|
1394
|
-
});
|
|
1395
|
-
}
|
|
1396
|
-
const splitTask = opts.splitTask;
|
|
1397
|
-
if (splitTask) {
|
|
1398
|
-
tools.push({
|
|
1399
|
-
name: 'org_task_split',
|
|
1400
|
-
description: 'Split a task into parallel children when scope expands. The parent becomes "split"; children inherit its deps; downstream tasks are rewired to depend on all children.',
|
|
1401
|
-
schema: {
|
|
1402
|
-
parentId: z.string(),
|
|
1403
|
-
children: z.array(strictArgs({ title: z.string(), assignee: z.string() })).min(1),
|
|
1404
|
-
},
|
|
1405
|
-
handler: async (args) => text(splitTask(role.id, args.parentId, args.children ?? [])),
|
|
1406
|
-
});
|
|
1407
|
-
}
|
|
1408
|
-
const mergeTask = opts.mergeTask;
|
|
1409
|
-
if (mergeTask) {
|
|
1410
|
-
tools.push({
|
|
1411
|
-
name: 'org_task_merge',
|
|
1412
|
-
description: 'Merge one task into another when parallel branches converge early. The source becomes "merged"; downstream deps are rewired to the target.',
|
|
1413
|
-
schema: { sourceId: z.string(), targetId: z.string() },
|
|
1414
|
-
handler: async (args) => text(mergeTask(role.id, args.sourceId, args.targetId)),
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
const cancelTask = opts.cancelTask;
|
|
1418
|
-
if (cancelTask) {
|
|
1419
|
-
tools.push({
|
|
1420
|
-
name: 'org_task_cancel',
|
|
1421
|
-
description: 'Cancel a task as moot. The task becomes "cancelled" and unblocks downstream work. Its assignee is told to stop and not to commit or report further work for it; a role that runs one session per task has that session\'s process ended at once.',
|
|
1422
|
-
schema: { taskId: z.string(), reason: z.string().optional() },
|
|
1423
|
-
handler: async (args) => text(cancelTask(role.id, args.taskId, args.reason)),
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
const blockTask = opts.blockTask;
|
|
1427
|
-
if (blockTask) {
|
|
1428
|
-
tools.push({
|
|
1429
|
-
name: 'org_task_block',
|
|
1430
|
-
description: `Mark a task you are actively working (status "running") as blocked on a real-world time, not on other tasks — e.g. a scheduled soak test, a CI run that takes hours, a human-set deadline. Use this INSTEAD of leaving the task "running" with nothing actually happening, and instead of calling org_complete just because there is genuinely nothing to do right now: the idle watchdog will stop nudging you about this task until the time you give arrives, then automatically re-dispatch it to you. Give untilIso as an ISO 8601 date/time (e.g. "2026-08-19T09:00:00Z"). Nothing external wakes a blocked task — not a background command finishing, not a Monitor event, not npm propagation — so never block on a command you started: run waits in the foreground instead, with a command timeout long enough for them. Until the deadline you are woken periodically (every run_config.block_recheck_minutes, default 5; recheckAfterMinutes sets it for this block, 1-${MAX_BLOCK_RECHECK_MINUTES}) to re-check: close the task, re-block it, or report.`,
|
|
1431
|
-
schema: {
|
|
1432
|
-
taskId: z.string(),
|
|
1433
|
-
untilIso: z.string(),
|
|
1434
|
-
reason: z.string().optional(),
|
|
1435
|
-
recheckAfterMinutes: z.number().positive().max(MAX_BLOCK_RECHECK_MINUTES).optional(),
|
|
1436
|
-
},
|
|
1437
|
-
handler: async (args) => text(blockTask(role.id, args.taskId, args.untilIso, args.reason, args.recheckAfterMinutes)),
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
const planGraph = opts.planGraph;
|
|
1441
|
-
if (planGraph) {
|
|
1442
|
-
tools.push({
|
|
1443
|
-
name: 'org_plan_graph',
|
|
1444
|
-
description: 'Propose a full work graph in one call. Each task spec uses a local "name" and references other specs by name in "after", and may carry a "brief" with its instructions exactly as org_task does.' +
|
|
1445
|
-
(catalog ? ' Each spec may select a "loadout" exactly as org_task does.' : ''),
|
|
1446
|
-
schema: {
|
|
1447
|
-
tasks: z
|
|
1448
|
-
.array(strictArgs({
|
|
1449
|
-
name: z.string(),
|
|
1450
|
-
title: z.string(),
|
|
1451
|
-
assignee: z.string(),
|
|
1452
|
-
after: z.array(z.string()).default([]),
|
|
1453
|
-
brief: briefArg,
|
|
1454
|
-
...loadoutArg,
|
|
1455
|
-
}, { deps: 'use `after` with node names' }))
|
|
1456
|
-
.min(1),
|
|
1457
|
-
},
|
|
1458
|
-
handler: async (args) => text(planGraph(role.id, args.tasks ?? [])),
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
tools.push({
|
|
1462
|
-
name: 'org_send',
|
|
1463
|
-
description: 'Send a message to another agent (role id) or another org ("org:role"). This is the only inter-agent channel.' +
|
|
1464
|
-
// D3: only orgs with a task-scoped role see this, so every other org's
|
|
1465
|
-
// tool list (prefix position 0) is unchanged.
|
|
1466
|
-
(opts.def?.roles.some((r) => resolveSessionScope(r, opts.def) === 'task')
|
|
1467
|
-
? " When a message is about a task, start its subject with [task:<id>] so a task-scoped recipient reads it in that task's session."
|
|
1468
|
-
: ''),
|
|
1469
|
-
schema: { to: z.string(), subject: z.string(), message: z.string() },
|
|
1470
|
-
handler: async (args) => {
|
|
1471
|
-
if (opts.beforeTool) {
|
|
1472
|
-
const approved = await opts.beforeTool(role.id, 'org_send', args);
|
|
1473
|
-
if (approved === false)
|
|
1474
|
-
return text('Tool "org_send" was denied by guardrail approval');
|
|
1475
|
-
if (approved === null)
|
|
1476
|
-
return text('Tool "org_send" is pending human approval - you will receive the result when it is approved or denied.');
|
|
1477
|
-
}
|
|
1478
|
-
const receipt = await deliver(role.id, args.to, args.subject, args.message);
|
|
1479
|
-
return text(receipt);
|
|
1480
|
-
},
|
|
1481
|
-
});
|
|
1482
|
-
tools.push({
|
|
1483
|
-
name: 'ask_human',
|
|
1484
|
-
description: 'Ask a human a free-form question. Use only when you genuinely need human judgment. ' +
|
|
1485
|
-
'Set blocking: true ONLY if you cannot continue until it is answered — a blocking question pauses the ' +
|
|
1486
|
-
"org's idle watchdog (for up to an hour; after that the run resumes its normal idle checks either way). " +
|
|
1487
|
-
'If you can keep working while you wait — an FYI, a preference, anything you would describe as "not blocking on this" — ' +
|
|
1488
|
-
'pass blocking: false and carry on; the question is still recorded and answered, it just does not freeze the run. ' +
|
|
1489
|
-
'Defaults to blocking.',
|
|
1490
|
-
schema: { question: z.string(), blocking: z.boolean().optional() },
|
|
1491
|
-
handler: async (args) => {
|
|
1492
|
-
if (!opts.askHuman)
|
|
1493
|
-
return text('ask_human is not available in this session');
|
|
1494
|
-
const receipt = await opts.askHuman(role.id, args.question, args.blocking);
|
|
1495
|
-
return text(receipt);
|
|
1496
|
-
},
|
|
1497
|
-
});
|
|
1498
|
-
// Built-in org tools reject undeclared keys instead of stripping them: a
|
|
1499
|
-
// stripped `deps` on an org_plan_graph node silently dropped every edge.
|
|
1500
|
-
for (const t of tools)
|
|
1501
|
-
t.strict ??= {};
|
|
1502
|
-
return tools;
|
|
1503
|
-
}
|
|
1504
395
|
//# sourceMappingURL=session.js.map
|