@pugi/cli 0.1.0-beta.99 → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -195
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
|
@@ -1,600 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Real subagent execution (β2 S1 —).
|
|
3
|
-
*
|
|
4
|
-
* Replaces the M1 stub in `dispatcher.ts` with a genuine Anvil-backed
|
|
5
|
-
* child engine loop. Given a SubagentTask + an EngineLoopClient, this
|
|
6
|
-
* module:
|
|
7
|
-
*
|
|
8
|
-
* 1. Resolves the role's persona + isolation tier (delegated to
|
|
9
|
-
* `dispatcher.ts` so the policy lives in one file).
|
|
10
|
-
* 2. Optionally provisions a scratch worktree when the role's
|
|
11
|
-
* isolation tier requires write isolation (β2 S5 wiring point).
|
|
12
|
-
* 3. Builds a per-child tools schema gated by the isolation matrix
|
|
13
|
-
* (`isolation-matrix.ts::allowedToolsForRole`) so the child loop
|
|
14
|
-
* cannot see tools its role is forbidden to use.
|
|
15
|
-
* 4. Runs `runEngineLoop` with the role-specific persona, budget,
|
|
16
|
-
* and tools against the supplied EngineLoopClient (each child
|
|
17
|
-
* gets its OWN logical Anvil session — same wire transport, but
|
|
18
|
-
* a fresh transcript so the child cannot read parent context the
|
|
19
|
-
* operator did not authorise).
|
|
20
|
-
* 5. Streams `subagent.tool_call` events back through the parent's
|
|
21
|
-
* session journal as each child tool call fires (β2 S7 cross-
|
|
22
|
-
* agent state surface).
|
|
23
|
-
* 6. Aggregates `filesChanged` + `toolCallCount` + token totals from
|
|
24
|
-
* the child loop and returns them in the SubagentResult.
|
|
25
|
-
*
|
|
26
|
-
* What this module deliberately does NOT do:
|
|
27
|
-
*
|
|
28
|
-
* - Promote the child's worktree back to the parent's main tree.
|
|
29
|
-
* `pugi worktree promote` is operator-driven so the operator can
|
|
30
|
-
* review the diff before it lands; the dispatcher only creates
|
|
31
|
-
* the scratch worktree, runs the child in it, and leaves the
|
|
32
|
-
* handle attached to the SubagentResult.filesChanged for the
|
|
33
|
-
* caller to act on.
|
|
34
|
-
*
|
|
35
|
-
* - Drop the worktree on failure. Same reasoning: the operator
|
|
36
|
-
* should be able to inspect what the child did before the scratch
|
|
37
|
-
* dir disappears. Cleanup happens via `pugi worktree drop` or via
|
|
38
|
-
* the explicit `cleanup` callback returned on the worktree handle.
|
|
39
|
-
*
|
|
40
|
-
* - Run grand-children. The capability matrix (S4) blocks the
|
|
41
|
-
* `agent` tool for every role except orchestrator, so a coder/
|
|
42
|
-
* reviewer/verifier cannot spawn a child of its own. Bounded
|
|
43
|
-
* spawn depth = 1, which keeps the budget rollup tractable.
|
|
44
|
-
*
|
|
45
|
-
* Cross-reference: docs/research/2026-05-26-pugi-cli-consolidated-sprint-plan.md
|
|
46
|
-
* §β2 S1 + S7.
|
|
47
|
-
*/
|
|
48
|
-
import { randomUUID } from 'node:crypto';
|
|
49
|
-
import { relative as relativePath } from 'node:path';
|
|
50
|
-
import { runEngineLoop, } from '@pugi/sdk';
|
|
51
|
-
import { resolveBudget } from '../engine/budgets.js';
|
|
52
|
-
import { buildExecutor, buildToolsSchema, } from '../engine/tool-bridge.js';
|
|
53
|
-
import { systemPromptFor } from '../engine/prompts.js';
|
|
54
|
-
import { getPersonaForRole } from '../agents/registry.js';
|
|
55
|
-
import { FileReadCache } from '../file-cache.js';
|
|
56
|
-
import { loadSettings } from '../settings.js';
|
|
57
|
-
import { openSession } from '../session.js';
|
|
58
|
-
import { createWorktree, } from '../edits/worktree.js';
|
|
59
|
-
import { classifyBash } from '../bash-classifier.js';
|
|
60
|
-
import { allowedToolsForRole, bashIsReadOnlyForRole, capabilitiesForRole, } from './isolation-matrix.js';
|
|
61
|
-
import { budgetForRole, isolationForRole, } from './dispatcher.js';
|
|
62
|
-
/**
|
|
63
|
-
* Drive a real subagent dispatch. Returns the typed SubagentResult plus
|
|
64
|
-
* the optional WorktreeHandle (when isolation === 'worktree') so the
|
|
65
|
-
* caller can wire `pugi worktree promote/drop` follow-ups.
|
|
66
|
-
*
|
|
67
|
-
* Throws when the engine loop crashes uncaught — the caller should
|
|
68
|
-
* translate that into `recordSubagentFailed` on the parent session.
|
|
69
|
-
*/
|
|
70
|
-
export async function runRealDispatch(task, ctx) {
|
|
71
|
-
const persona = getPersonaForRole(task.role);
|
|
72
|
-
const isolation = isolationForRole(task.role);
|
|
73
|
-
const budget = budgetForRole(task.role, task.budget);
|
|
74
|
-
const capabilities = capabilitiesForRole(task.role);
|
|
75
|
-
const now = ctx.now ?? defaultNow;
|
|
76
|
-
const startedAt = Date.now();
|
|
77
|
-
// ---------------------------------------------------------------- //
|
|
78
|
-
// 1. Provision child workspace + session //
|
|
79
|
-
// ---------------------------------------------------------------- //
|
|
80
|
-
// The child's filesystem root depends on the isolation tier:
|
|
81
|
-
//
|
|
82
|
-
// - prompt_only / shared_fs_readonly / shared_fs_serialized →
|
|
83
|
-
// reuse the parent's workspaceRoot. Read isolation is enforced
|
|
84
|
-
// by the capability matrix (no `write` cap → no write tool in
|
|
85
|
-
// the schema). Serialized writes are gated by the file-tools
|
|
86
|
-
// layer's existing in-process lock (per-process serialization
|
|
87
|
-
// is enough at M1 because only one child runs per parent).
|
|
88
|
-
//
|
|
89
|
-
// - worktree → spawn `.pugi/worktrees/<uuid>` via createWorktree
|
|
90
|
-
// and pin the child's workspaceRoot there. The parent's tree is
|
|
91
|
-
// untouched; promote/drop is operator-driven afterwards.
|
|
92
|
-
let childRoot = ctx.workspaceRoot;
|
|
93
|
-
let worktreeHandle;
|
|
94
|
-
// β2a r1 (Codex P1): the original gate required
|
|
95
|
-
// `isolationForRole()` to return `'worktree'` before honoring
|
|
96
|
-
// `ctx.useWorktreeIsolation`. But the role→isolation matrix never
|
|
97
|
-
// returns `'worktree'` for any writer (coders land at
|
|
98
|
-
// `shared_fs_serialized`), so the agentTool's explicit
|
|
99
|
-
// `useWorktreeIsolation: true` request was silently dropped and the
|
|
100
|
-
// coder mutated the parent checkout directly. Fix: treat an explicit
|
|
101
|
-
// `true` as an INDEPENDENT trigger so callers (agentTool with
|
|
102
|
-
// `isolation: 'worktree'`) actually get a scratch worktree.
|
|
103
|
-
//
|
|
104
|
-
// The three states map to:
|
|
105
|
-
// - true → force worktree regardless of role tier (explicit opt-in)
|
|
106
|
-
// - false → skip worktree even if role tier would require it (spec
|
|
107
|
-
// escape hatch + caller opt-out)
|
|
108
|
-
// - undefined → honor role tier default (`'worktree'` → create)
|
|
109
|
-
const useWorktree = ctx.useWorktreeIsolation === true
|
|
110
|
-
? true
|
|
111
|
-
: ctx.useWorktreeIsolation === false
|
|
112
|
-
? false
|
|
113
|
-
: isolation === 'worktree';
|
|
114
|
-
// β2a r2 (Codex P2): compute the EFFECTIVE isolation tier
|
|
115
|
-
// for audit metadata. The role-default `isolation` value is the
|
|
116
|
-
// declarative tier from `isolationForRole()`, but when
|
|
117
|
-
// `ctx.useWorktreeIsolation === true` forces a scratch worktree for a
|
|
118
|
-
// role whose default is `shared_fs_serialized` (coder/release/devops/
|
|
119
|
-
// design_qa), the actual workspace boundary is `worktree`. Emitting
|
|
120
|
-
// the stale role-default in the `subagent.spawned` event was
|
|
121
|
-
// misleading audit consumers (SSE / cabinet UI) into thinking the
|
|
122
|
-
// child mutated the parent tree when it actually mutated a scratch
|
|
123
|
-
// worktree — and vice versa when `false` overrode a `worktree`-tier
|
|
124
|
-
// role into shared_fs. The `isolation` field on the lifecycle event
|
|
125
|
-
// now reflects the ACTUAL workspace decision.
|
|
126
|
-
const isolationEffective = useWorktree
|
|
127
|
-
? 'worktree'
|
|
128
|
-
: isolation;
|
|
129
|
-
if (useWorktree) {
|
|
130
|
-
const wt = createWorktree({ cwd: ctx.workspaceRoot });
|
|
131
|
-
if (!wt.ok) {
|
|
132
|
-
// Translate worktree failure to a clean blocked result instead
|
|
133
|
-
// of throwing — the caller can surface it as
|
|
134
|
-
// subagent.blocked / tool_unavailable. The dispatcher.ts wrapper
|
|
135
|
-
// does the emission so the audit log lines up with other
|
|
136
|
-
// blocked dispatches.
|
|
137
|
-
//
|
|
138
|
-
// β2a r2 (Codex P2): isolation here falls back to the role
|
|
139
|
-
// default because the worktree failed to provision — the child
|
|
140
|
-
// never landed on a `worktree` boundary, so reporting the
|
|
141
|
-
// effective tier would lie. Use the declarative role default.
|
|
142
|
-
ctx.appendEvent({
|
|
143
|
-
id: randomUUID(),
|
|
144
|
-
sessionId: ctx.sessionId,
|
|
145
|
-
timestamp: now(),
|
|
146
|
-
type: 'subagent.spawned',
|
|
147
|
-
taskId: task.id,
|
|
148
|
-
role: task.role,
|
|
149
|
-
personaSlug: persona.slug,
|
|
150
|
-
parentSessionId: ctx.sessionId,
|
|
151
|
-
isolation,
|
|
152
|
-
});
|
|
153
|
-
const blockedResult = {
|
|
154
|
-
taskId: task.id,
|
|
155
|
-
role: task.role,
|
|
156
|
-
personaSlug: persona.slug,
|
|
157
|
-
status: 'blocked',
|
|
158
|
-
summary: `worktree provisioning failed: ${wt.reason}: ${wt.detail}`,
|
|
159
|
-
filesChanged: [],
|
|
160
|
-
toolCallCount: 0,
|
|
161
|
-
tokensIn: 0,
|
|
162
|
-
tokensOut: 0,
|
|
163
|
-
durationMs: Date.now() - startedAt,
|
|
164
|
-
};
|
|
165
|
-
ctx.appendEvent({
|
|
166
|
-
id: randomUUID(),
|
|
167
|
-
sessionId: ctx.sessionId,
|
|
168
|
-
timestamp: now(),
|
|
169
|
-
type: 'subagent.blocked',
|
|
170
|
-
taskId: task.id,
|
|
171
|
-
role: task.role,
|
|
172
|
-
personaSlug: persona.slug,
|
|
173
|
-
reason: 'tool_unavailable',
|
|
174
|
-
detail: `worktree provisioning failed (${wt.reason}): ${wt.detail}`,
|
|
175
|
-
});
|
|
176
|
-
return { result: blockedResult };
|
|
177
|
-
}
|
|
178
|
-
childRoot = wt.value.path;
|
|
179
|
-
worktreeHandle = wt.value;
|
|
180
|
-
}
|
|
181
|
-
const childSession = ctx.childSession ?? openSession(childRoot);
|
|
182
|
-
const settings = loadSettings(childRoot);
|
|
183
|
-
// ---------------------------------------------------------------- //
|
|
184
|
-
// 2. Emit subagent.spawned //
|
|
185
|
-
// ---------------------------------------------------------------- //
|
|
186
|
-
// β2a r2 (Codex P2): use the EFFECTIVE isolation tier so
|
|
187
|
-
// audit consumers see the actual workspace boundary (`worktree` when
|
|
188
|
-
// `ctx.useWorktreeIsolation === true` forced a scratch worktree for
|
|
189
|
-
// a role whose default was `shared_fs_serialized`). The stale
|
|
190
|
-
// role-default emission was a P2 misdirection — operators watching
|
|
191
|
-
// `pugi sessions stream` saw `shared_fs_serialized` while the child
|
|
192
|
-
// was actually mutating `.pugi/worktrees/<uuid>/`.
|
|
193
|
-
ctx.appendEvent({
|
|
194
|
-
id: randomUUID(),
|
|
195
|
-
sessionId: ctx.sessionId,
|
|
196
|
-
timestamp: now(),
|
|
197
|
-
type: 'subagent.spawned',
|
|
198
|
-
taskId: task.id,
|
|
199
|
-
role: task.role,
|
|
200
|
-
personaSlug: persona.slug,
|
|
201
|
-
parentSessionId: ctx.sessionId,
|
|
202
|
-
isolation: isolationEffective,
|
|
203
|
-
});
|
|
204
|
-
// ---------------------------------------------------------------- //
|
|
205
|
-
// 3. Build per-child tools schema + executor //
|
|
206
|
-
// ---------------------------------------------------------------- //
|
|
207
|
-
// The schema is filtered by the capability matrix (S4). We start
|
|
208
|
-
// from buildToolsSchema's default set and intersect with
|
|
209
|
-
// `allowedToolsForRole`. The executor enforces the same set as a
|
|
210
|
-
// defense-in-depth gate — even if a model hallucinates an unknown
|
|
211
|
-
// tool name (or a future schema bug accidentally advertises one),
|
|
212
|
-
// the executor rejects it before any file-tools layer sees it.
|
|
213
|
-
const commandKind = ctx.commandKind ?? commandKindForRole(task.role);
|
|
214
|
-
const allowedTools = allowedToolsForRole(task.role);
|
|
215
|
-
// β2a r1 (Backend Architect P1): the executor's
|
|
216
|
-
// `allowFetch` flag previously only checked the workspace settings
|
|
217
|
-
// (`web.fetch.enabled`). For roles whose capability set DOES NOT
|
|
218
|
-
// include `web_fetch` (every non-orchestrator role today) the
|
|
219
|
-
// schema filter would strip the tool advertisement, but a model
|
|
220
|
-
// that fabricated a `web_fetch` call would still hit the executor
|
|
221
|
-
// with `allowFetch: true` and the call would succeed. Fix: AND the
|
|
222
|
-
// role-capability gate into the flag so a fabricated call also
|
|
223
|
-
// gets refused by the capability gate above; the AND below is the
|
|
224
|
-
// executor-level belt-and-braces.
|
|
225
|
-
const allowFetchForChild = allowedTools.has('web_fetch') && settings.web?.fetch?.enabled === true;
|
|
226
|
-
const schemaOpts = {
|
|
227
|
-
allowFetch: allowFetchForChild,
|
|
228
|
-
};
|
|
229
|
-
const fullSchema = buildToolsSchema(commandKind, schemaOpts);
|
|
230
|
-
const filteredSchema = fullSchema.filter((tool) => allowedTools.has(tool.name));
|
|
231
|
-
const toolCtx = {
|
|
232
|
-
root: childRoot,
|
|
233
|
-
settings,
|
|
234
|
-
session: childSession,
|
|
235
|
-
readCache: new FileReadCache(),
|
|
236
|
-
};
|
|
237
|
-
const rawExecutor = buildExecutor({
|
|
238
|
-
kind: commandKind,
|
|
239
|
-
ctx: toolCtx,
|
|
240
|
-
sessionId: childSession.id,
|
|
241
|
-
workspaceRoot: childRoot,
|
|
242
|
-
interactive: false,
|
|
243
|
-
allowFetch: allowFetchForChild,
|
|
244
|
-
});
|
|
245
|
-
// β2a r1 (Codex P1): roles in the bash_read_only tier
|
|
246
|
-
// (verifier today) need an EXTRA pre-flight on every `bash` call so
|
|
247
|
-
// a write-class command (echo into a file, sed -i, rm) is refused
|
|
248
|
-
// before bashToolSync's permission layer sees it. The bash tool
|
|
249
|
-
// defaults to permission mode `auto`, which permits write_workspace
|
|
250
|
-
// class commands; without this gate the no-edit contract is purely
|
|
251
|
-
// honor-system. Read + build_test are still allowed so test
|
|
252
|
-
// commands work; everything else surfaces a CAPABILITY_REFUSED
|
|
253
|
-
// sentinel back to the model so it can adapt.
|
|
254
|
-
const bashReadOnly = bashIsReadOnlyForRole(task.role);
|
|
255
|
-
const BASH_READ_ONLY_ALLOWED = new Set(['read', 'build_test']);
|
|
256
|
-
// ---------------------------------------------------------------- //
|
|
257
|
-
// 4. Wrap executor with the isolation-matrix refusal gate + //
|
|
258
|
-
// cross-agent event stream (S4 + S7) //
|
|
259
|
-
// ---------------------------------------------------------------- //
|
|
260
|
-
// Every tool call the child makes is:
|
|
261
|
-
// (a) rejected if the role lacks the capability (defense in depth);
|
|
262
|
-
// (b) emitted as a subagent.tool_call event into the parent
|
|
263
|
-
// journal so `pugi sessions stream <id>` surfaces it inline;
|
|
264
|
-
// (c) classified for the filesChanged summary.
|
|
265
|
-
const filesChanged = new Set();
|
|
266
|
-
let childToolCallCount = 0;
|
|
267
|
-
const gatedExecutor = async (input) => {
|
|
268
|
-
if (!allowedTools.has(input.name)) {
|
|
269
|
-
// Note: this is in addition to the schema filter — a model that
|
|
270
|
-
// fabricates a tool call for a name we deliberately did NOT
|
|
271
|
-
// advertise still gets rejected here. The error string is read
|
|
272
|
-
// by runEngineLoop and fed back into the next turn's tool frame.
|
|
273
|
-
throw new Error(`CAPABILITY_REFUSED: role '${task.role}' is not allowed to call '${input.name}' (${capabilities.rationale})`);
|
|
274
|
-
}
|
|
275
|
-
// β2a r1 (Codex P1): bash read-only gate for roles
|
|
276
|
-
// whose capability set is `bash_read_only` (verifier). Pre-classify
|
|
277
|
-
// the command and refuse anything outside read/build_test BEFORE
|
|
278
|
-
// the tool runs — the underlying bashToolSync's permission layer
|
|
279
|
-
// would otherwise wave write_workspace through under the default
|
|
280
|
-
// `auto` mode.
|
|
281
|
-
if (input.name === 'bash' && bashReadOnly) {
|
|
282
|
-
const cmd = extractBashCommand(input.arguments);
|
|
283
|
-
if (cmd !== null) {
|
|
284
|
-
const classification = classifyBash(cmd, {
|
|
285
|
-
workspaceRoot: childRoot,
|
|
286
|
-
additionalDirectories: [],
|
|
287
|
-
});
|
|
288
|
-
if (!BASH_READ_ONLY_ALLOWED.has(classification.class)) {
|
|
289
|
-
throw new Error(`CAPABILITY_REFUSED: role '${task.role}' may only run read/build_test bash commands; '${classification.class}' refused (${classification.reason})`);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
childToolCallCount += 1;
|
|
294
|
-
// β2 S7 cross-agent state: emit subagent.tool_call BEFORE dispatch
|
|
295
|
-
// so the parent journal sees the attempt even if the tool throws.
|
|
296
|
-
ctx.appendEvent({
|
|
297
|
-
id: randomUUID(),
|
|
298
|
-
sessionId: ctx.sessionId,
|
|
299
|
-
timestamp: now(),
|
|
300
|
-
type: 'subagent.tool_call',
|
|
301
|
-
taskId: task.id,
|
|
302
|
-
role: task.role,
|
|
303
|
-
personaSlug: persona.slug,
|
|
304
|
-
toolName: input.name,
|
|
305
|
-
toolCallId: input.callId,
|
|
306
|
-
});
|
|
307
|
-
const result = await rawExecutor(input);
|
|
308
|
-
if (input.name === 'write' || input.name === 'edit') {
|
|
309
|
-
const path = extractPathArg(input.arguments);
|
|
310
|
-
if (path)
|
|
311
|
-
filesChanged.add(path);
|
|
312
|
-
}
|
|
313
|
-
return result;
|
|
314
|
-
};
|
|
315
|
-
// ---------------------------------------------------------------- //
|
|
316
|
-
// 5. Build the engine loop budget envelope from the per-role //
|
|
317
|
-
// SubagentBudget (tokens/dollars/wallClockMs). //
|
|
318
|
-
// ---------------------------------------------------------------- //
|
|
319
|
-
// The engine loop driver uses {maxTokens, maxToolCalls}; we
|
|
320
|
-
// translate the subagent budget into the loop budget shape and
|
|
321
|
-
// fall back to the per-command defaults from `resolveBudget` for
|
|
322
|
-
// the call-count ceiling (subagent budget does not carry that knob).
|
|
323
|
-
const commandBudget = resolveBudget(commandKind, settings, { maxTokens: budget.tokens });
|
|
324
|
-
// ---------------------------------------------------------------- //
|
|
325
|
-
// 6. Drive the child loop //
|
|
326
|
-
// ---------------------------------------------------------------- //
|
|
327
|
-
const hooks = {
|
|
328
|
-
// We deliberately do NOT proxy onTurnStart/onTurnComplete to the
|
|
329
|
-
// parent journal — the child's own session already records those
|
|
330
|
-
// via the per-loop session mirror, and re-emitting them at parent
|
|
331
|
-
// scope would double-count tokens in the cabinet UI.
|
|
332
|
-
//
|
|
333
|
-
// onToolCall + onToolResult are also folded into the gatedExecutor
|
|
334
|
-
// above (the cross-agent event stream) so the loop's hook surface
|
|
335
|
-
// stays empty here.
|
|
336
|
-
};
|
|
337
|
-
let outcome;
|
|
338
|
-
try {
|
|
339
|
-
outcome = await runEngineLoop({
|
|
340
|
-
client: ctx.engineClient,
|
|
341
|
-
executor: gatedExecutor,
|
|
342
|
-
systemPrompt: systemPromptFor(commandKind),
|
|
343
|
-
userPrompt: task.prompt,
|
|
344
|
-
tools: filteredSchema,
|
|
345
|
-
budget: commandBudget,
|
|
346
|
-
personaSlug: persona.slug,
|
|
347
|
-
hooks,
|
|
348
|
-
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
352
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
353
|
-
const failedResult = {
|
|
354
|
-
taskId: task.id,
|
|
355
|
-
role: task.role,
|
|
356
|
-
personaSlug: persona.slug,
|
|
357
|
-
status: 'failed',
|
|
358
|
-
summary: `engine loop crashed: ${message}`,
|
|
359
|
-
filesChanged: Array.from(filesChanged).sort(),
|
|
360
|
-
toolCallCount: childToolCallCount,
|
|
361
|
-
tokensIn: 0,
|
|
362
|
-
tokensOut: 0,
|
|
363
|
-
durationMs: Date.now() - startedAt,
|
|
364
|
-
};
|
|
365
|
-
ctx.appendEvent({
|
|
366
|
-
id: randomUUID(),
|
|
367
|
-
sessionId: ctx.sessionId,
|
|
368
|
-
timestamp: now(),
|
|
369
|
-
type: 'subagent.failed',
|
|
370
|
-
taskId: task.id,
|
|
371
|
-
role: task.role,
|
|
372
|
-
personaSlug: persona.slug,
|
|
373
|
-
error: message,
|
|
374
|
-
});
|
|
375
|
-
return { result: failedResult, ...(worktreeHandle ? { worktreeHandle } : {}) };
|
|
376
|
-
}
|
|
377
|
-
// ---------------------------------------------------------------- //
|
|
378
|
-
// 7. Translate loop outcome → SubagentResult //
|
|
379
|
-
// ---------------------------------------------------------------- //
|
|
380
|
-
const { status, terminalEvent } = translateOutcome(outcome);
|
|
381
|
-
const summary = composeSummary(outcome, status, capabilities.rationale, worktreeHandle, ctx.workspaceRoot);
|
|
382
|
-
const result = {
|
|
383
|
-
taskId: task.id,
|
|
384
|
-
role: task.role,
|
|
385
|
-
personaSlug: persona.slug,
|
|
386
|
-
status,
|
|
387
|
-
summary,
|
|
388
|
-
filesChanged: Array.from(filesChanged).sort(),
|
|
389
|
-
// childToolCallCount is the gate-level count (every tool the child
|
|
390
|
-
// attempted, including refusals). outcome.toolCallCount is what
|
|
391
|
-
// the engine loop actually ran. We use outcome's number because
|
|
392
|
-
// it matches the audit log's tool_call records — refusals were
|
|
393
|
-
// already surfaced as subagent.tool_call events before the throw.
|
|
394
|
-
toolCallCount: outcome.toolCallCount,
|
|
395
|
-
tokensIn: estimateTokensIn(outcome),
|
|
396
|
-
tokensOut: estimateTokensOut(outcome),
|
|
397
|
-
durationMs: Date.now() - startedAt,
|
|
398
|
-
};
|
|
399
|
-
// Emit the terminal event last so an audit-log reader sees the
|
|
400
|
-
// chronological order: spawned → tool_call(s) → completed/blocked/failed.
|
|
401
|
-
if (terminalEvent === 'completed') {
|
|
402
|
-
ctx.appendEvent({
|
|
403
|
-
id: randomUUID(),
|
|
404
|
-
sessionId: ctx.sessionId,
|
|
405
|
-
timestamp: now(),
|
|
406
|
-
type: 'subagent.completed',
|
|
407
|
-
taskId: task.id,
|
|
408
|
-
role: task.role,
|
|
409
|
-
personaSlug: persona.slug,
|
|
410
|
-
toolCallCount: result.toolCallCount,
|
|
411
|
-
tokensIn: result.tokensIn,
|
|
412
|
-
tokensOut: result.tokensOut,
|
|
413
|
-
durationMs: result.durationMs,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
else if (terminalEvent === 'blocked') {
|
|
417
|
-
ctx.appendEvent({
|
|
418
|
-
id: randomUUID(),
|
|
419
|
-
sessionId: ctx.sessionId,
|
|
420
|
-
timestamp: now(),
|
|
421
|
-
type: 'subagent.blocked',
|
|
422
|
-
taskId: task.id,
|
|
423
|
-
role: task.role,
|
|
424
|
-
personaSlug: persona.slug,
|
|
425
|
-
reason: blockedReasonFor(outcome),
|
|
426
|
-
detail: outcome.reason ?? 'engine loop blocked without a specific reason',
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
ctx.appendEvent({
|
|
431
|
-
id: randomUUID(),
|
|
432
|
-
sessionId: ctx.sessionId,
|
|
433
|
-
timestamp: now(),
|
|
434
|
-
type: 'subagent.failed',
|
|
435
|
-
taskId: task.id,
|
|
436
|
-
role: task.role,
|
|
437
|
-
personaSlug: persona.slug,
|
|
438
|
-
error: outcome.reason ?? 'unknown engine loop failure',
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
return { result, ...(worktreeHandle ? { worktreeHandle } : {}) };
|
|
442
|
-
}
|
|
443
|
-
/* ---------------------------------------------------------------- */
|
|
444
|
-
/* Helpers */
|
|
445
|
-
/* ---------------------------------------------------------------- */
|
|
446
|
-
/**
|
|
447
|
-
* Default command kind for a role. Writers default to `code` (20 calls
|
|
448
|
-
* / 30k tokens envelope per β1 Pl9); readers default to `explain` (5 /
|
|
449
|
-
* 20k). The caller can always override via ctx.commandKind.
|
|
450
|
-
*/
|
|
451
|
-
function commandKindForRole(role) {
|
|
452
|
-
switch (role) {
|
|
453
|
-
case 'coder':
|
|
454
|
-
case 'release':
|
|
455
|
-
case 'devops':
|
|
456
|
-
case 'design_qa':
|
|
457
|
-
return 'code';
|
|
458
|
-
case 'verifier':
|
|
459
|
-
// verifier needs bash for tests but should not be writing —
|
|
460
|
-
// `fix` envelope is the right compromise (read + bash + edit-ish
|
|
461
|
-
// tokens, no full build budget).
|
|
462
|
-
return 'fix';
|
|
463
|
-
case 'orchestrator':
|
|
464
|
-
// orchestrator runs in parent context — `code` matches the
|
|
465
|
-
// parent's default and gives it room to delegate.
|
|
466
|
-
return 'code';
|
|
467
|
-
case 'architect':
|
|
468
|
-
case 'reviewer':
|
|
469
|
-
case 'researcher':
|
|
470
|
-
default:
|
|
471
|
-
return 'explain';
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
function translateOutcome(outcome) {
|
|
475
|
-
switch (outcome.status) {
|
|
476
|
-
case 'completed':
|
|
477
|
-
return { status: 'shipped', terminalEvent: 'completed' };
|
|
478
|
-
case 'budget_exhausted':
|
|
479
|
-
case 'tool_refused':
|
|
480
|
-
case 'aborted':
|
|
481
|
-
// `aborted` maps to `blocked` per the same logic as native-pugi.ts:
|
|
482
|
-
// the operator chose the outcome.
|
|
483
|
-
return { status: 'blocked', terminalEvent: 'blocked' };
|
|
484
|
-
case 'failed':
|
|
485
|
-
return { status: 'failed', terminalEvent: 'failed' };
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
function blockedReasonFor(outcome) {
|
|
489
|
-
switch (outcome.status) {
|
|
490
|
-
case 'budget_exhausted':
|
|
491
|
-
return 'budget_exhausted';
|
|
492
|
-
case 'tool_refused':
|
|
493
|
-
return 'plan_mode_refused';
|
|
494
|
-
case 'aborted':
|
|
495
|
-
// Operator abort surfaces as permission_denied — the operator
|
|
496
|
-
// pulled consent, which is the same shape as a permission rule
|
|
497
|
-
// refusing the call.
|
|
498
|
-
return 'permission_denied';
|
|
499
|
-
default:
|
|
500
|
-
return 'tool_unavailable';
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
function composeSummary(outcome, status, rationale, worktreeHandle, workspaceRoot) {
|
|
504
|
-
const finalText = outcome.finalText.trim();
|
|
505
|
-
const lines = [];
|
|
506
|
-
if (finalText) {
|
|
507
|
-
lines.push(finalText);
|
|
508
|
-
}
|
|
509
|
-
else if (outcome.reason) {
|
|
510
|
-
lines.push(`[${outcome.status}] ${outcome.reason}`);
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
lines.push(`[${outcome.status}] no answer returned`);
|
|
514
|
-
}
|
|
515
|
-
if (worktreeHandle) {
|
|
516
|
-
// β2a r1 (Backend Architect P1): emit the worktree
|
|
517
|
-
// path RELATIVE to the workspace root. The summary text flows to
|
|
518
|
-
// the parent transcript and from there to the provider when the
|
|
519
|
-
// operator runs in `studio` / `provider-direct` privacy mode;
|
|
520
|
-
// shipping `/Users/<operator>/Web/...` (absolute) leaked the
|
|
521
|
-
// operator's home directory to the provider on every spawn. The
|
|
522
|
-
// relative form (`.pugi/worktrees/<uuid>`) is enough for the
|
|
523
|
-
// operator's local `pugi worktree promote/drop` commands.
|
|
524
|
-
const relPath = relativePath(workspaceRoot, worktreeHandle.path) || worktreeHandle.path;
|
|
525
|
-
lines.push('');
|
|
526
|
-
lines.push(`worktree: ${relPath}`);
|
|
527
|
-
lines.push(`base: ${worktreeHandle.baseSha}`);
|
|
528
|
-
lines.push(`promote via: pugi worktree promote ${relPath}`);
|
|
529
|
-
lines.push(`drop via: pugi worktree drop ${relPath}`);
|
|
530
|
-
}
|
|
531
|
-
// β2a r1 (Backend Architect P2): the rationale string
|
|
532
|
-
// duplicates the role's capability matrix entry, which on `shipped`
|
|
533
|
-
// outcomes is noise that bloats the parent transcript and provides
|
|
534
|
-
// an extra signal a prompt-injecting child could echo back. Limit
|
|
535
|
-
// it to `blocked` (where it explains the refusal) — failed
|
|
536
|
-
// outcomes already carry the engine loop's own reason field.
|
|
537
|
-
if (status === 'blocked') {
|
|
538
|
-
lines.push('');
|
|
539
|
-
lines.push(`role-capability rationale: ${rationale}`);
|
|
540
|
-
}
|
|
541
|
-
return lines.join('\n');
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* The engine-loop outcome carries a single `tokensUsed` counter (total
|
|
545
|
-
* prompt + completion tokens). We don't have a precise prompt/completion
|
|
546
|
-
* split surfaced from the loop driver — approximate the split as 70/30
|
|
547
|
-
* (typical chat-completion shape for tool-use loops where the model's
|
|
548
|
-
* replies are short tool_call frames). The numbers are surfaced in the
|
|
549
|
-
* cabinet UI as a guideline, not for billing reconciliation (billing
|
|
550
|
-
* lives at the runtime adapter side).
|
|
551
|
-
*/
|
|
552
|
-
function estimateTokensIn(outcome) {
|
|
553
|
-
return Math.round(outcome.tokensUsed * 0.7);
|
|
554
|
-
}
|
|
555
|
-
function estimateTokensOut(outcome) {
|
|
556
|
-
return Math.round(outcome.tokensUsed * 0.3);
|
|
557
|
-
}
|
|
558
|
-
function extractPathArg(raw) {
|
|
559
|
-
if (!raw)
|
|
560
|
-
return null;
|
|
561
|
-
try {
|
|
562
|
-
const parsed = JSON.parse(raw);
|
|
563
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
564
|
-
const path = parsed.path;
|
|
565
|
-
if (typeof path === 'string' && path.length > 0)
|
|
566
|
-
return path;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
catch {
|
|
570
|
-
// bad JSON — ignored.
|
|
571
|
-
}
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
/**
|
|
575
|
-
* β2a r1 : pull the `command` field out of a bash tool
|
|
576
|
-
* call's JSON arguments. Used by the bash read-only gate to feed the
|
|
577
|
-
* classifier. Returns null on bad JSON / missing field so the caller
|
|
578
|
-
* can fail-safe to allow (the underlying tool will surface its own
|
|
579
|
-
* parse error).
|
|
580
|
-
*/
|
|
581
|
-
function extractBashCommand(raw) {
|
|
582
|
-
if (!raw)
|
|
583
|
-
return null;
|
|
584
|
-
try {
|
|
585
|
-
const parsed = JSON.parse(raw);
|
|
586
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
587
|
-
const cmd = parsed.command;
|
|
588
|
-
if (typeof cmd === 'string' && cmd.length > 0)
|
|
589
|
-
return cmd;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
catch {
|
|
593
|
-
// bad JSON — let the tool surface the parse error.
|
|
594
|
-
}
|
|
595
|
-
return null;
|
|
596
|
-
}
|
|
597
|
-
function defaultNow() {
|
|
598
|
-
return new Date().toISOString();
|
|
599
|
-
}
|
|
600
|
-
//# sourceMappingURL=dispatcher-real.js.map
|