@wjarka/cezarion 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -46
- package/dist/agent-config/catalog.d.ts +6 -2
- package/dist/agent-config/catalog.js +78 -3
- package/dist/agent-config/catalog.js.map +1 -1
- package/dist/agent-config/model-settings/shared.js +6 -3
- package/dist/agent-config/model-settings/shared.js.map +1 -1
- package/dist/agent-config/models.js +4 -0
- package/dist/agent-config/models.js.map +1 -1
- package/dist/agent-config/validate.js +6 -0
- package/dist/agent-config/validate.js.map +1 -1
- package/dist/artifacts/cli.js +10 -2
- package/dist/artifacts/cli.js.map +1 -1
- package/dist/automations/coordinator.d.ts +7 -0
- package/dist/automations/coordinator.js +22 -2
- package/dist/automations/coordinator.js.map +1 -1
- package/dist/automations/github-poller.d.ts +4 -4
- package/dist/automations/github-poller.js.map +1 -1
- package/dist/automations/schedule-runner.d.ts +130 -0
- package/dist/automations/schedule-runner.js +326 -0
- package/dist/automations/schedule-runner.js.map +1 -0
- package/dist/automations/scheduler.d.ts +38 -7
- package/dist/automations/scheduler.js +90 -22
- package/dist/automations/scheduler.js.map +1 -1
- package/dist/automations/store.d.ts +80 -3
- package/dist/automations/store.js +208 -16
- package/dist/automations/store.js.map +1 -1
- package/dist/automations/task-template.d.ts +42 -6
- package/dist/automations/task-template.js +152 -24
- package/dist/automations/task-template.js.map +1 -1
- package/dist/automations/types.d.ts +142 -6
- package/dist/automations/types.js +64 -6
- package/dist/automations/types.js.map +1 -1
- package/dist/autosave-holders.js +8 -12
- package/dist/autosave-holders.js.map +1 -1
- package/dist/ci-wait/client.d.ts +8 -1
- package/dist/ci-wait/client.js +43 -1
- package/dist/ci-wait/client.js.map +1 -1
- package/dist/ci-wait/controller.d.ts +83 -4
- package/dist/ci-wait/controller.js +76 -11
- package/dist/ci-wait/controller.js.map +1 -1
- package/dist/ci-wait/errors.d.ts +7 -1
- package/dist/ci-wait/errors.js +36 -0
- package/dist/ci-wait/errors.js.map +1 -1
- package/dist/ci-wait/github.d.ts +2 -0
- package/dist/ci-wait/github.js +3 -1
- package/dist/ci-wait/github.js.map +1 -1
- package/dist/ci-wait/mcp.d.ts +2 -98
- package/dist/ci-wait/mcp.js +11 -25
- package/dist/ci-wait/mcp.js.map +1 -1
- package/dist/ci-wait/supervisor.d.ts +2 -1
- package/dist/ci-wait/supervisor.js +2 -1
- package/dist/ci-wait/supervisor.js.map +1 -1
- package/dist/ci-wait/tools.d.ts +307 -0
- package/dist/ci-wait/tools.js +63 -0
- package/dist/ci-wait/tools.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/contract/agent-config.d.ts +5 -0
- package/dist/contract/agent-profiles.d.ts +15 -0
- package/dist/contract/automation-schedule.d.ts +60 -0
- package/dist/contract/automations.d.ts +208 -36
- package/dist/contract/delegation.d.ts +96 -0
- package/dist/contract/discovery.d.ts +7 -0
- package/dist/contract/github.d.ts +1 -0
- package/dist/contract/health.d.ts +6 -0
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.js +1445 -745
- package/dist/contract/preview.d.ts +508 -0
- package/dist/contract/runs.d.ts +716 -15
- package/dist/contract/skills.d.ts +39 -0
- package/dist/contract/workflows.d.ts +6 -0
- package/dist/contract/workspace.d.ts +65 -0
- package/dist/contract/zoned-time.d.ts +35 -0
- package/dist/core/agent-env.d.ts +11 -0
- package/dist/core/agent-env.js +27 -5
- package/dist/core/agent-env.js.map +1 -1
- package/dist/core/agent-profiles.js +7 -0
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-runner.d.ts +49 -1
- package/dist/core/agent-runner.js +2 -1
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/backend-detect.d.ts +3 -2
- package/dist/core/backend-detect.js +27 -1
- package/dist/core/backend-detect.js.map +1 -1
- package/dist/core/claude-cli-runner.d.ts +1 -0
- package/dist/core/claude-cli-runner.js +69 -19
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.d.ts +1 -0
- package/dist/core/codex-app-server-runner.js +200 -25
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.d.ts +7 -3
- package/dist/core/codex-app-server-transport.js +11 -9
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/cursor-acp-runner.d.ts +1 -0
- package/dist/core/cursor-acp-runner.js +24 -9
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/core/host-model-catalog.js +2 -0
- package/dist/core/host-model-catalog.js.map +1 -1
- package/dist/core/managed-session.js +2 -0
- package/dist/core/managed-session.js.map +1 -1
- package/dist/core/model-identity.js +2 -0
- package/dist/core/model-identity.js.map +1 -1
- package/dist/core/model-presets.js +3 -0
- package/dist/core/model-presets.js.map +1 -1
- package/dist/core/omp-model-catalog.d.ts +43 -0
- package/dist/core/omp-model-catalog.js +205 -0
- package/dist/core/omp-model-catalog.js.map +1 -0
- package/dist/core/omp-runner.d.ts +73 -0
- package/dist/core/omp-runner.js +990 -0
- package/dist/core/omp-runner.js.map +1 -0
- package/dist/core/omp-ui-mapper.d.ts +106 -0
- package/dist/core/omp-ui-mapper.js +882 -0
- package/dist/core/omp-ui-mapper.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +18 -4
- package/dist/core/opencode-server-runner.js +337 -96
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/opencode-session-error.d.ts +11 -0
- package/dist/core/opencode-session-error.js +26 -0
- package/dist/core/opencode-session-error.js.map +1 -0
- package/dist/core/opencode-ui-mapper.js +4 -36
- package/dist/core/opencode-ui-mapper.js.map +1 -1
- package/dist/core/pi-runner.d.ts +1 -0
- package/dist/core/pi-runner.js +75 -12
- package/dist/core/pi-runner.js.map +1 -1
- package/dist/core/provider-auth.d.ts +9 -2
- package/dist/core/provider-auth.js +43 -6
- package/dist/core/provider-auth.js.map +1 -1
- package/dist/core/runner-factory.js +3 -0
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/runner-model-catalog.js +1 -0
- package/dist/core/runner-model-catalog.js.map +1 -1
- package/dist/core/runner-stderr.d.ts +2 -0
- package/dist/core/runner-stderr.js +20 -0
- package/dist/core/runner-stderr.js.map +1 -0
- package/dist/core/session-process.d.ts +22 -0
- package/dist/core/session-process.js +202 -0
- package/dist/core/session-process.js.map +1 -0
- package/dist/core/ui-events.d.ts +1 -1
- package/dist/core/usage-limit.js +3 -60
- package/dist/core/usage-limit.js.map +1 -1
- package/dist/data-gitignore.d.ts +6 -0
- package/dist/data-gitignore.js +57 -0
- package/dist/data-gitignore.js.map +1 -0
- package/dist/delegation/capacity.d.ts +19 -0
- package/dist/delegation/capacity.js +34 -0
- package/dist/delegation/capacity.js.map +1 -0
- package/dist/delegation/cli.js +13 -4
- package/dist/delegation/cli.js.map +1 -1
- package/dist/delegation/conversations.d.ts +4 -2
- package/dist/delegation/conversations.js +8 -12
- package/dist/delegation/conversations.js.map +1 -1
- package/dist/delegation/credentials.d.ts +2 -0
- package/dist/delegation/credentials.js +10 -0
- package/dist/delegation/credentials.js.map +1 -1
- package/dist/delegation/destroy-observation.d.ts +42 -0
- package/dist/delegation/destroy-observation.js +115 -0
- package/dist/delegation/destroy-observation.js.map +1 -0
- package/dist/delegation/execution-identity.d.ts +6 -0
- package/dist/delegation/execution-identity.js +1 -1
- package/dist/delegation/execution-identity.js.map +1 -1
- package/dist/delegation/policy.d.ts +3 -2
- package/dist/delegation/policy.js +7 -5
- package/dist/delegation/policy.js.map +1 -1
- package/dist/delegation/process-liveness.d.ts +57 -20
- package/dist/delegation/process-liveness.js +114 -92
- package/dist/delegation/process-liveness.js.map +1 -1
- package/dist/delegation/provision.js +6 -6
- package/dist/delegation/provision.js.map +1 -1
- package/dist/delegation/results.js +4 -2
- package/dist/delegation/results.js.map +1 -1
- package/dist/delegation/retry-backoff.d.ts +23 -0
- package/dist/delegation/retry-backoff.js +18 -0
- package/dist/delegation/retry-backoff.js.map +1 -0
- package/dist/delegation/routes.d.ts +64 -58
- package/dist/delegation/scratch-cleanup.d.ts +29 -0
- package/dist/delegation/scratch-cleanup.js +143 -0
- package/dist/delegation/scratch-cleanup.js.map +1 -0
- package/dist/delegation/service.d.ts +20 -1
- package/dist/delegation/service.js +233 -70
- package/dist/delegation/service.js.map +1 -1
- package/dist/delegation/transport.d.ts +12 -6
- package/dist/delegation/workspace.d.ts +23 -4
- package/dist/delegation/workspace.js +147 -31
- package/dist/delegation/workspace.js.map +1 -1
- package/dist/git-worktree-lock-helper.js +8 -3
- package/dist/git-worktree-lock-helper.js.map +1 -1
- package/dist/git-worktree-lock.d.ts +6 -1
- package/dist/git-worktree-lock.js +10 -4
- package/dist/git-worktree-lock.js.map +1 -1
- package/dist/git-worktree-release.d.ts +20 -0
- package/dist/git-worktree-release.js +27 -0
- package/dist/git-worktree-release.js.map +1 -0
- package/dist/git-worktree.d.ts +8 -2
- package/dist/git-worktree.js +19 -2
- package/dist/git-worktree.js.map +1 -1
- package/dist/handoff.d.ts +2 -2
- package/dist/handoff.js +1 -1
- package/dist/index.js +109 -76
- package/dist/index.js.map +1 -1
- package/dist/paths.d.ts +1 -1
- package/dist/paths.js +5 -1
- package/dist/paths.js.map +1 -1
- package/dist/preview/cdp.d.ts +13 -0
- package/dist/preview/cdp.js +69 -0
- package/dist/preview/cdp.js.map +1 -0
- package/dist/preview/chromium.d.ts +75 -0
- package/dist/preview/chromium.js +394 -0
- package/dist/preview/chromium.js.map +1 -0
- package/dist/preview/dev-server.d.ts +68 -0
- package/dist/preview/dev-server.js +341 -0
- package/dist/preview/dev-server.js.map +1 -0
- package/dist/preview/host.d.ts +154 -0
- package/dist/preview/host.js +681 -0
- package/dist/preview/host.js.map +1 -0
- package/dist/preview/registration.d.ts +63 -0
- package/dist/preview/registration.js +109 -0
- package/dist/preview/registration.js.map +1 -0
- package/dist/preview/session.d.ts +73 -0
- package/dist/preview/session.js +347 -0
- package/dist/preview/session.js.map +1 -0
- package/dist/preview/url.d.ts +6 -0
- package/dist/preview/url.js +17 -0
- package/dist/preview/url.js.map +1 -0
- package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
- package/dist/runs/agent-tmpdir-removal.js +60 -0
- package/dist/runs/agent-tmpdir-removal.js.map +1 -0
- package/dist/runs/agent-tmpdir.d.ts +12 -1
- package/dist/runs/agent-tmpdir.js +123 -34
- package/dist/runs/agent-tmpdir.js.map +1 -1
- package/dist/runs/delegation-state.d.ts +5 -1
- package/dist/runs/delegation-state.js +9 -0
- package/dist/runs/delegation-state.js.map +1 -1
- package/dist/runs/event-history.d.ts +4 -4
- package/dist/runs/event-history.js +97 -117
- package/dist/runs/event-history.js.map +1 -1
- package/dist/runs/history-compressor.d.ts +29 -0
- package/dist/runs/history-compressor.js +116 -0
- package/dist/runs/history-compressor.js.map +1 -0
- package/dist/runs/history-file.d.ts +36 -0
- package/dist/runs/history-file.js +463 -0
- package/dist/runs/history-file.js.map +1 -0
- package/dist/runs/human-ask-summary.js +18 -13
- package/dist/runs/human-ask-summary.js.map +1 -1
- package/dist/runs/legacy-index.d.ts +62 -0
- package/dist/runs/legacy-index.js +168 -0
- package/dist/runs/legacy-index.js.map +1 -0
- package/dist/runs/raw-record.d.ts +52 -0
- package/dist/runs/raw-record.js +145 -0
- package/dist/runs/raw-record.js.map +1 -0
- package/dist/runs/retention.d.ts +13 -6
- package/dist/runs/retention.js +42 -13
- package/dist/runs/retention.js.map +1 -1
- package/dist/runs/run-claims.d.ts +23 -0
- package/dist/runs/run-claims.js +24 -0
- package/dist/runs/run-claims.js.map +1 -0
- package/dist/runs/run-database.d.ts +385 -0
- package/dist/runs/run-database.js +930 -0
- package/dist/runs/run-database.js.map +1 -0
- package/dist/runs/run-index.d.ts +35 -9
- package/dist/runs/run-index.js +200 -25
- package/dist/runs/run-index.js.map +1 -1
- package/dist/runs/run-row.d.ts +44 -0
- package/dist/runs/run-row.js +84 -0
- package/dist/runs/run-row.js.map +1 -0
- package/dist/runs/run-search.d.ts +29 -0
- package/dist/runs/run-search.js +63 -0
- package/dist/runs/run-search.js.map +1 -0
- package/dist/runs/store-open-error.d.ts +24 -0
- package/dist/runs/store-open-error.js +61 -0
- package/dist/runs/store-open-error.js.map +1 -0
- package/dist/runs/store.d.ts +667 -32
- package/dist/runs/store.js +2153 -324
- package/dist/runs/store.js.map +1 -1
- package/dist/runs/transcript-facts.d.ts +77 -0
- package/dist/runs/transcript-facts.js +302 -0
- package/dist/runs/transcript-facts.js.map +1 -0
- package/dist/runs/webhook.js +5 -4
- package/dist/runs/webhook.js.map +1 -1
- package/dist/runs/worker-execution.d.ts +22 -0
- package/dist/runs/worker-execution.js +30 -0
- package/dist/runs/worker-execution.js.map +1 -0
- package/dist/server/capabilities.d.ts +1 -0
- package/dist/server/capabilities.js +2 -0
- package/dist/server/capabilities.js.map +1 -1
- package/dist/server/cockpit-ownership.d.ts +8 -0
- package/dist/server/cockpit-ownership.js +19 -0
- package/dist/server/cockpit-ownership.js.map +1 -1
- package/dist/server/forge/github.d.ts +1 -4
- package/dist/server/forge/github.js +47 -23
- package/dist/server/forge/github.js.map +1 -1
- package/dist/server/git.js +1 -1
- package/dist/server/git.js.map +1 -1
- package/dist/server/open-in-app.js +1 -0
- package/dist/server/open-in-app.js.map +1 -1
- package/dist/server/preview-socket.d.ts +36 -0
- package/dist/server/preview-socket.js +126 -0
- package/dist/server/preview-socket.js.map +1 -0
- package/dist/server/project-context.d.ts +31 -4
- package/dist/server/project-context.js +73 -9
- package/dist/server/project-context.js.map +1 -1
- package/dist/server/provider-action-gate.js +1 -0
- package/dist/server/provider-action-gate.js.map +1 -1
- package/dist/server/repo-branches.d.ts +4 -3
- package/dist/server/repo-branches.js +5 -4
- package/dist/server/repo-branches.js.map +1 -1
- package/dist/server/server.d.ts +4500 -2727
- package/dist/server/server.js +651 -202
- package/dist/server/server.js.map +1 -1
- package/dist/server/upgrade-router.d.ts +23 -0
- package/dist/server/upgrade-router.js +53 -0
- package/dist/server/upgrade-router.js.map +1 -0
- package/dist/server/validators.d.ts +6 -1
- package/dist/server/validators.js +4 -4
- package/dist/server/validators.js.map +1 -1
- package/dist/server/ws.d.ts +16 -16
- package/dist/server/ws.js +56 -54
- package/dist/server/ws.js.map +1 -1
- package/dist/skills-remote.d.ts +14 -0
- package/dist/skills-remote.js +91 -15
- package/dist/skills-remote.js.map +1 -1
- package/dist/task-cli/cli.js +40 -16
- package/dist/task-cli/cli.js.map +1 -1
- package/dist/task-cli/discovery.d.ts +4 -3
- package/dist/task-cli/discovery.js +4 -3
- package/dist/task-cli/discovery.js.map +1 -1
- package/dist/task-cli/http.d.ts +2 -0
- package/dist/task-cli/http.js +10 -4
- package/dist/task-cli/http.js.map +1 -1
- package/dist/task-cli/projections.d.ts +5 -2
- package/dist/task-cli/projections.js +13 -0
- package/dist/task-cli/projections.js.map +1 -1
- package/dist/task-cli/watch.d.ts +15 -2
- package/dist/task-cli/watch.js +31 -9
- package/dist/task-cli/watch.js.map +1 -1
- package/dist/ui-state.d.ts +4 -3
- package/dist/ui-state.js +8 -3
- package/dist/ui-state.js.map +1 -1
- package/dist/workflows/grouped-command.d.ts +31 -0
- package/dist/workflows/grouped-command.js +143 -0
- package/dist/workflows/grouped-command.js.map +1 -0
- package/dist/workflows/run.d.ts +223 -42
- package/dist/workflows/run.js +1316 -398
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/types.d.ts +12 -0
- package/dist/workflows/types.js +20 -0
- package/dist/workflows/types.js.map +1 -1
- package/dist/workspace/agent-accounts.d.ts +5 -1
- package/dist/workspace/agent-accounts.js +1 -0
- package/dist/workspace/agent-accounts.js.map +1 -1
- package/dist/workspace/agent-profiles.js +1 -1
- package/dist/workspace/agent-profiles.js.map +1 -1
- package/dist/workspace/config.d.ts +3 -1
- package/dist/workspace/config.js +1 -0
- package/dist/workspace/config.js.map +1 -1
- package/dist/worktree-setup.d.ts +85 -0
- package/dist/worktree-setup.js +164 -0
- package/dist/worktree-setup.js.map +1 -0
- package/package.json +7 -5
- package/scripts/benchmark-run-store-fixture.ts +428 -0
- package/scripts/benchmark-run-store.ts +743 -0
- package/scripts/benchmark-stuck-destroys.ts +143 -0
- package/scripts/mock-autonomous.mjs +11 -0
- package/scripts/mock-ci-tool.mjs +20 -5
- package/scripts/mock-claude.mjs +110 -1
- package/scripts/mock-codex-app-server.mjs +117 -2
- package/scripts/mock-codex-coalesce-steer.mjs +59 -0
- package/scripts/mock-codex-reverse-steer.mjs +130 -0
- package/scripts/mock-cursor-acp.mjs +75 -1
- package/scripts/mock-omp-rpc.mjs +605 -0
- package/scripts/mock-opencode-serve.mjs +312 -22
- package/scripts/mock-pi-rpc.mjs +109 -3
- package/scripts/mock-runner-crash.mjs +42 -0
- package/scripts/mock-silent-tail.mjs +78 -0
- package/scripts/mock-turn-messages.mjs +5 -0
- package/scripts/omp-ci-wait.mjs +29 -0
- package/scripts/omp-restrict-delegation.yml +4 -0
- package/scripts/pi-ci-wait.mjs +12 -9
- package/scripts/seed-archive-profile.ts +3 -3
- package/web/dist/assets/agent-accounts-CFOBMVHc.js +1 -0
- package/web/dist/assets/alert-dialog-BrpNUwMt.js +1 -0
- package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
- package/web/dist/assets/button-C_rd7CPp.js +43 -0
- package/web/dist/assets/compare-variants-D6WXhvXA.js +1 -0
- package/web/dist/assets/diff-DrEARXTV.js +3 -0
- package/web/dist/assets/diff-controls-B1-j7nFg.js +1 -0
- package/web/dist/assets/{diff-view-DPn6bQ5W.js → diff-view-DvGLjdTk.js} +3 -3
- package/web/dist/assets/dropdown-menu-BegOZFrj.js +1 -0
- package/web/dist/assets/format-DpNe3S0A.js +1 -0
- package/web/dist/assets/git-toolbar-Cw4ljmhB.js +1 -0
- package/web/dist/assets/github-CfV5sqL0.js +1 -0
- package/web/dist/assets/{image-preview-DJ8cgb1Z.js → image-preview-FOEfqpZP.js} +1 -1
- package/web/dist/assets/index-DgWWlOLy.js +13 -0
- package/web/dist/assets/index-fBFnuvxu.css +1 -0
- package/web/dist/assets/{nav-row-styles-3j64kH5s.js → nav-row-styles-vp2JbhuJ.js} +1 -1
- package/web/dist/assets/play-CmVAj_fu.js +1 -0
- package/web/dist/assets/popover-BohP6WTx.js +1 -0
- package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +3 -0
- package/web/dist/assets/preview-state-vuMviq6q.js +1 -0
- package/web/dist/assets/prompt-templates-Cubdut_C.js +15 -0
- package/web/dist/assets/repo-git-Cf4t5PD0.js +1 -0
- package/web/dist/assets/{run-diff-5-RRdcVz.js → run-diff-BpkUW37Z.js} +3 -3
- package/web/dist/assets/run-header-68WHf9wV.js +1 -0
- package/web/dist/assets/skills-DiVIgKeU.js +1 -0
- package/web/dist/assets/square-terminal-B6RYYieR.js +1 -0
- package/web/dist/assets/status-dot-CqTJHSP9.js +1 -0
- package/web/dist/assets/{tab-link-K0hjzEXR.js → tab-link-B07Hu4QX.js} +1 -1
- package/web/dist/assets/task-changes--lQF6EYf.js +1 -0
- package/web/dist/assets/task-commits-Cx0qy4DN.js +1 -0
- package/web/dist/assets/task-files-D50s-gKW.js +2 -0
- package/web/dist/assets/task-github-item-DaqmSwYT.js +1 -0
- package/web/dist/assets/task-thread-BPQ64lui.js +7 -0
- package/web/dist/assets/{task-thread-Bw_nQuTx.css → task-thread-zNE2Q9E-.css} +1 -1
- package/web/dist/assets/textarea-D20O4Pw0.js +1 -0
- package/web/dist/assets/thread-state-DWcFMrvb.js +4 -0
- package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +1 -0
- package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +1 -0
- package/web/dist/assets/{utils-fzNfbPzA.js → utils-JsL3ZsJf.js} +1 -1
- package/web/dist/assets/workflows-jwG7uxqi.js +11 -0
- package/web/dist/assets/{zoomable-image-CDmZ_Rtl.js → zoomable-image-BJJ64KGc.js} +1 -1
- package/web/dist/cezarion-lockup-dark.svg +1 -13
- package/web/dist/cezarion-lockup-light.svg +1 -13
- package/web/dist/cezarion-mark-dark.svg +1 -12
- package/web/dist/cezarion-mark-light.svg +1 -12
- package/web/dist/index.html +16 -21
- package/web/dist/assets/agent-accounts-CU2ZDv24.js +0 -1
- package/web/dist/assets/alert-dialog-C-xYE_dg.js +0 -43
- package/web/dist/assets/chevron-right-BENZ-1ZN.js +0 -1
- package/web/dist/assets/collapsible-DD9J91Iv.js +0 -1
- package/web/dist/assets/compare-variants-5FmqpFSN.js +0 -1
- package/web/dist/assets/diff-controls-Bj0YrioE.js +0 -1
- package/web/dist/assets/diff-rE8rm75J2.js +0 -3
- package/web/dist/assets/diff-stat-BKDCSVfJ.js +0 -1
- package/web/dist/assets/dropdown-menu-Ch5BYQh_.js +0 -1
- package/web/dist/assets/format-dBiNDNVe.js +0 -1
- package/web/dist/assets/git-toolbar-ClVvf5iF.js +0 -1
- package/web/dist/assets/github-BkNOyour.css +0 -1
- package/web/dist/assets/github-DeZMeMwJ.js +0 -1
- package/web/dist/assets/github-item-detail-W5F6GVjF.js +0 -1
- package/web/dist/assets/github-sidebar-model-DcMu-IK2.js +0 -1
- package/web/dist/assets/highlighter-CdHqIwFr.js +0 -3
- package/web/dist/assets/index-CatemBEL.js +0 -8
- package/web/dist/assets/index-MvfD8bZJ.css +0 -1
- package/web/dist/assets/markdown-zdKeLIYA.js +0 -2
- package/web/dist/assets/mobile-run-bar-BCfmFi8V.js +0 -1
- package/web/dist/assets/prompt-templates-CBPp39IU.js +0 -15
- package/web/dist/assets/reference-chip-D6owncJT.js +0 -1
- package/web/dist/assets/repo-git-CtdkZFey.js +0 -1
- package/web/dist/assets/run-header-BcjoDi0W.js +0 -1
- package/web/dist/assets/sheet-ChnYVrp1.js +0 -1
- package/web/dist/assets/skills-C8YKp2vr.js +0 -1
- package/web/dist/assets/task-changes-BW8YdWG8.js +0 -1
- package/web/dist/assets/task-commits-q2kB2y6L.js +0 -1
- package/web/dist/assets/task-files-pPL6_3Cr.js +0 -2
- package/web/dist/assets/task-github-item-BXF5R52U.js +0 -1
- package/web/dist/assets/task-thread-DtkFL3EC.js +0 -10
- package/web/dist/assets/textarea-DxlkE-Yd.js +0 -1
- package/web/dist/assets/token-metrics-DIAjrPlM.js +0 -1
- package/web/dist/assets/use-desktop-CrDny2uG.js +0 -1
- package/web/dist/assets/use-submit-shortcut-KWJsC906.js +0 -1
- package/web/dist/assets/workflows-TmcPEj2K.js +0 -11
- /package/web/dist/assets/{lib-BQXq3kEf2.js → lib-BQXq3kEf.js} +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stuck-destroy benchmark (hearsay-tools/cezarion#879): what permanently stuck worker destroys
|
|
3
|
+
* cost an otherwise idle cezar. Run it unchanged before and after a change to the cleanup loops.
|
|
4
|
+
*
|
|
5
|
+
* npm run bench:stuck-destroys -w @wjarka/cezarion # 20 workers, 10 minutes
|
|
6
|
+
* node --import tsx packages/cezar/scripts/benchmark-stuck-destroys.ts --workers 0 --minutes 10
|
|
7
|
+
*
|
|
8
|
+
* Flags: --workers (20) --minutes (10) --out (a JSON file for the result row).
|
|
9
|
+
*
|
|
10
|
+
* Method:
|
|
11
|
+
* - One process builds what `serve` builds for a project: `RunStore` (keepLive), `RunManager` and
|
|
12
|
+
* `DelegationService`, on a temp Git repo, with `CEZ_DRY_RUN=1` and production cadence (no
|
|
13
|
+
* timer is overridden).
|
|
14
|
+
* - Each worker is settled the way a finished worker is: a complete execution checkpoint, status
|
|
15
|
+
* `review`, a real owned worktree and a local scratch dir. A `sleep` child holds each of the
|
|
16
|
+
* two, so neither can ever be removed: the destroy stays incomplete and the scratch is retained.
|
|
17
|
+
* - Every worker is destroyed once through the human route, as Clean up does. Only then does the
|
|
18
|
+
* window start, so the measurement is the retry and reprobe loops alone.
|
|
19
|
+
* - CPU is this process (`process.cpuUsage`, every thread) plus the children it reaped during the
|
|
20
|
+
* window (`/proc/self/stat` cutime/cstime: the git processes the loops spawn). The holders are
|
|
21
|
+
* reaped only after the window. Event-loop delay is what an HTTP request would wait.
|
|
22
|
+
* - Compare N workers against `--workers 0`; the difference is what the stuck workers add.
|
|
23
|
+
*/
|
|
24
|
+
import { execFileSync, spawn, type ChildProcess } from 'node:child_process';
|
|
25
|
+
import { randomUUID } from 'node:crypto';
|
|
26
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
27
|
+
import { cpus, tmpdir } from 'node:os';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
|
|
30
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
31
|
+
import { parseArgs } from 'node:util';
|
|
32
|
+
import type { RunStore as RunStoreInstance } from '../src/runs/store.ts';
|
|
33
|
+
import type { RunManager as RunManagerInstance } from '../src/workflows/run.ts';
|
|
34
|
+
|
|
35
|
+
process.env.CEZ_DRY_RUN = '1';
|
|
36
|
+
process.env.CEZ_DELEGATION = '1';
|
|
37
|
+
process.env.CEZ_AUTONAME = '0';
|
|
38
|
+
// The repo's own sandbox for ~/.cezar, so a benchmark never touches the user's registry; one this
|
|
39
|
+
// run creates is removed with the rest of its teardown.
|
|
40
|
+
const ownHome = process.env.CEZ_HOME ? undefined : mkdtempSync(join(tmpdir(), 'cez-bench-home-'));
|
|
41
|
+
if (ownHome) process.env.CEZ_HOME = ownHome;
|
|
42
|
+
|
|
43
|
+
const { values } = parseArgs({ options: {
|
|
44
|
+
workers: { type: 'string', default: '20' },
|
|
45
|
+
minutes: { type: 'string', default: '10' },
|
|
46
|
+
out: { type: 'string' },
|
|
47
|
+
} });
|
|
48
|
+
const workers = Number(values.workers), minutes = Number(values.minutes);
|
|
49
|
+
if (!Number.isInteger(workers) || workers < 0 || !(minutes > 0)) throw new Error('--workers must be a whole number >= 0 and --minutes > 0');
|
|
50
|
+
|
|
51
|
+
const { RunStore } = await import('../src/runs/store.ts');
|
|
52
|
+
const { RunManager } = await import('../src/workflows/run.ts');
|
|
53
|
+
const { DelegationService } = await import('../src/delegation/service.ts');
|
|
54
|
+
const { planOwnedWorkspace, ensureOwnedWorkspace } = await import('../src/delegation/workspace.ts');
|
|
55
|
+
const { WorkspaceSemaphore } = await import('../src/workspace/semaphore.ts');
|
|
56
|
+
const { agentTmpDir } = await import('../src/runs/agent-tmpdir.ts');
|
|
57
|
+
|
|
58
|
+
/** Clock ticks of this process's reaped children (`cutime + cstime`), in ms; 0 off Linux. */
|
|
59
|
+
function childrenCpuMs(): number {
|
|
60
|
+
if (process.platform !== 'linux') return 0;
|
|
61
|
+
const stat = readFileSync('/proc/self/stat', 'utf8');
|
|
62
|
+
const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
|
|
63
|
+
// After the comm field, cutime and cstime are fields 16 and 17 of stat(5), i.e. index 13 and 14 here.
|
|
64
|
+
const ticks = Number(fields[13]) + Number(fields[14]);
|
|
65
|
+
const hz = Number(execFileSync('getconf', ['CLK_TCK'], { encoding: 'utf8' }).trim()) || 100;
|
|
66
|
+
return ticks * 1000 / hz;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const git = (cwd: string, ...args: string[]) => execFileSync('git', args, { cwd, encoding: 'utf8' }).trim();
|
|
70
|
+
const root = mkdtempSync(join(tmpdir(), 'cez-bench-stuck-'));
|
|
71
|
+
git(root, 'init', '-q', '-b', 'main');
|
|
72
|
+
git(root, 'config', 'gc.auto', '0');
|
|
73
|
+
git(root, 'config', 'maintenance.auto', 'false');
|
|
74
|
+
git(root, '-c', 'user.name=bench', '-c', 'user.email=bench@local', 'commit', '--allow-empty', '-qm', 'base');
|
|
75
|
+
const sha = git(root, 'rev-parse', 'HEAD');
|
|
76
|
+
const dataDir = join(root, '.ai/cezar');
|
|
77
|
+
|
|
78
|
+
// Teardown runs however the run ends, so a failed setup, destroy or measurement leaves no
|
|
79
|
+
// `sleep` holder and no temporary repository behind.
|
|
80
|
+
const holders: ChildProcess[] = [];
|
|
81
|
+
let store: RunStoreInstance | undefined, manager: RunManagerInstance | undefined, detach: (() => void) | undefined;
|
|
82
|
+
try {
|
|
83
|
+
store = RunStore.open(dataDir, { keepLive: true });
|
|
84
|
+
manager = new RunManager(store, root, { semaphore: new WorkspaceSemaphore({ initial: { maxParallel: 1 } }) });
|
|
85
|
+
const service = new DelegationService();
|
|
86
|
+
detach = service.registerProject({ id: 'bench', root, store, manager });
|
|
87
|
+
const parent = store.createRun({ title: 'parent', task: 'parent', workflow: 'quick-task', steps: [] });
|
|
88
|
+
store.updateRun(parent.id, { status: 'waiting', delegation: { role: 'root', permissions: ['spawn'], receipts: [] } });
|
|
89
|
+
|
|
90
|
+
const hold = (cwd: string) => { holders.push(spawn('sleep', ['86400'], { cwd, stdio: 'ignore' })); };
|
|
91
|
+
const ids: string[] = [];
|
|
92
|
+
for (let i = 0; i < workers; i++) {
|
|
93
|
+
const id = randomUUID();
|
|
94
|
+
const workspace = await planOwnedWorkspace(root, id, sha);
|
|
95
|
+
store.createOwnedRun({ title: `worker ${i}`, task: 'stuck', workflow: 'quick-task', runner: 'claude', steps: [{ id: 'task', name: 'Task', kind: 'agent' }] },
|
|
96
|
+
parent.id, randomUUID(), { role: 'worker', permissions: [], parentRunId: parent.id, workspace }, 'a'.repeat(64));
|
|
97
|
+
const generation = store.commitWorkerExecutionStart(id);
|
|
98
|
+
const created = await ensureOwnedWorkspace(root, store.getRun(id)!);
|
|
99
|
+
store.updateRun(id, { status: 'review', worktreePath: created.path, branch: created.branch });
|
|
100
|
+
if (!store.commitWorkerExecutionComplete(id, generation)) throw new Error(`worker ${id} did not complete`);
|
|
101
|
+
const scratch = agentTmpDir(dataDir, id);
|
|
102
|
+
mkdirSync(scratch, { recursive: true });
|
|
103
|
+
hold(created.path); hold(scratch);
|
|
104
|
+
ids.push(id);
|
|
105
|
+
}
|
|
106
|
+
// What boot does after recovery: arm pending destroys and the scratch reprobes.
|
|
107
|
+
service.armDestroyRetries('bench');
|
|
108
|
+
for (const id of ids) {
|
|
109
|
+
const result = await service.destroyForHuman('bench', id).catch((error: Error) => ({ state: 'error', error: error.message }));
|
|
110
|
+
if (result.state === 'complete') throw new Error(`worker ${id} was not held: its destroy completed`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const delay = monitorEventLoopDelay({ resolution: 10 });
|
|
114
|
+
delay.enable();
|
|
115
|
+
const cpu0 = process.cpuUsage(), children0 = childrenCpuMs(), wall0 = performance.now();
|
|
116
|
+
await sleep(minutes * 60_000);
|
|
117
|
+
const cpu = process.cpuUsage(cpu0), children = childrenCpuMs() - children0, wall = performance.now() - wall0;
|
|
118
|
+
delay.disable();
|
|
119
|
+
|
|
120
|
+
const opened = store;
|
|
121
|
+
const pending = ids.filter(id => { const run = opened.getRun(id); return run?.delegation?.role === 'worker' && run.delegation.destroy?.phase !== 'complete'; }).length;
|
|
122
|
+
const row = {
|
|
123
|
+
workers, minutes, pendingDestroys: pending,
|
|
124
|
+
userMs: Math.round(cpu.user / 1000), systemMs: Math.round(cpu.system / 1000), childrenMs: Math.round(children),
|
|
125
|
+
cpuPercent: Number(((cpu.user + cpu.system) / 1000 / wall * 100).toFixed(2)),
|
|
126
|
+
cpuWithChildrenPercent: Number((((cpu.user + cpu.system) / 1000 + children) / wall * 100).toFixed(2)),
|
|
127
|
+
loopDelayP99Ms: Number((delay.percentile(99) / 1e6).toFixed(1)), loopDelayMaxMs: Number((delay.max / 1e6).toFixed(1)),
|
|
128
|
+
cores: cpus().length, node: process.version,
|
|
129
|
+
};
|
|
130
|
+
console.log('| workers | minutes | pending | user ms | system ms | children ms | CPU % | CPU % incl. children | loop p99 ms | loop max ms |');
|
|
131
|
+
console.log('| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |');
|
|
132
|
+
console.log(`| ${row.workers} | ${row.minutes} | ${row.pendingDestroys} | ${row.userMs} | ${row.systemMs} | ${row.childrenMs} | ${row.cpuPercent} | ${row.cpuWithChildrenPercent} | ${row.loopDelayP99Ms} | ${row.loopDelayMaxMs} |`);
|
|
133
|
+
if (values.out) writeFileSync(values.out, `${JSON.stringify(row, null, 2)}\n`);
|
|
134
|
+
|
|
135
|
+
} finally {
|
|
136
|
+
for (const child of holders) child.kill('SIGKILL');
|
|
137
|
+
detach?.();
|
|
138
|
+
manager?.dispose();
|
|
139
|
+
store?.close();
|
|
140
|
+
rmSync(root, { recursive: true, force: true });
|
|
141
|
+
if (ownHome) rmSync(ownHome, { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
process.exit(0);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Shared responses only; each caller encodes its own native runner wire (#426).
|
|
2
|
+
let cap = false;
|
|
3
|
+
export function autonomousReply(prompt) {
|
|
4
|
+
const askCap = prompt.includes('mock:autonomous-ask-cap') || prompt.includes('mock:autonomous-readiness-idle');
|
|
5
|
+
if (prompt.includes('mock:autonomous-cap') || askCap) cap = true;
|
|
6
|
+
if (askCap) return 'CEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
|
|
7
|
+
if (prompt.startsWith('Continue working autonomously until the task is fully complete.')) {
|
|
8
|
+
return cap ? 'Still working.' : 'Autonomous work finished.\nCEZ:DONE';
|
|
9
|
+
}
|
|
10
|
+
return 'Still working.';
|
|
11
|
+
}
|
package/scripts/mock-ci-tool.mjs
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
// Offline harness fixtures share the real bundled MCP protocol, never a fake receipt.
|
|
2
2
|
import { writeFileSync, existsSync } from 'node:fs';
|
|
3
3
|
import { basename } from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
6
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
7
7
|
|
|
8
8
|
export async function probeCiTool(backend, wire, pr = process.env.CEZ_MOCK_CI_PR) {
|
|
9
9
|
if (!pr) return;
|
|
10
|
+
return probeCezarTool(backend, wire, 'cezar_wait_for_ci', { pr });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Calls one cezar tool the way the real harness would: through the wired MCP server (or Pi/OMP extension).
|
|
14
|
+
export async function probeCezarTool(backend, wire, name, args) {
|
|
10
15
|
let result;
|
|
11
|
-
if (backend === 'pi') {
|
|
16
|
+
if (backend === 'pi' || backend === 'omp') {
|
|
12
17
|
const extensions = wire.flatMap((arg, index) => arg === '--extension' ? [wire[index + 1]] : []);
|
|
13
|
-
const path = extensions.find(path => basename(path) ===
|
|
18
|
+
const path = extensions.find(path => basename(path) === `${backend}-ci-wait.mjs`);
|
|
14
19
|
if (!path) throw new Error('CI extension absent');
|
|
15
20
|
// The real Pi loader compiles TS when running from source; installed script uses JS.
|
|
16
21
|
if (existsSync(new URL('../src/ci-wait/mcp.ts', import.meta.url))) { const { register } = await import('tsx/esm/api'); register(); }
|
|
17
22
|
const { default: extension } = await import(pathToFileURL(path));
|
|
18
23
|
const tools = [];
|
|
19
24
|
extension({ registerTool(tool) { tools.push(tool); } });
|
|
20
|
-
|
|
25
|
+
const tool = tools.find(tool => tool.name === name);
|
|
26
|
+
if (!tool) throw new Error(`${name} not registered`);
|
|
27
|
+
result = { names: tools.map(tool => tool.name), response: await tool.execute('ci-1', args) };
|
|
21
28
|
} else {
|
|
22
29
|
let server; let env;
|
|
23
30
|
if (backend === 'claude') {
|
|
@@ -39,7 +46,7 @@ export async function probeCiTool(backend, wire, pr = process.env.CEZ_MOCK_CI_PR
|
|
|
39
46
|
try {
|
|
40
47
|
await client.connect(new StdioClientTransport({ command: server.command, args: server.args, env, stderr: 'pipe' }));
|
|
41
48
|
const list = await client.listTools();
|
|
42
|
-
result = { names: list.tools.map(tool => tool.name), response: await client.callTool({ name
|
|
49
|
+
result = { names: list.tools.map(tool => tool.name), response: await client.callTool({ name, arguments: args }) };
|
|
43
50
|
} finally { await client.close(); }
|
|
44
51
|
}
|
|
45
52
|
if (process.env.CEZ_MOCK_CI_RESULT) writeFileSync(process.env.CEZ_MOCK_CI_RESULT, JSON.stringify(result));
|
|
@@ -51,3 +58,11 @@ export async function ciPrompt(backend, wire, text) {
|
|
|
51
58
|
const result = await probeCiTool(backend, wire, pr);
|
|
52
59
|
return JSON.stringify(result.response);
|
|
53
60
|
}
|
|
61
|
+
|
|
62
|
+
// `mock:preview-serve <port>` registers the e2e fixture app (`node preview-app.mjs <port>`) as a live preview.
|
|
63
|
+
export async function previewPrompt(backend, wire, text) {
|
|
64
|
+
const port = Number(/mock:preview-serve\s+(\d+)/.exec(text)?.[1]);
|
|
65
|
+
const fixture = fileURLToPath(new URL('../../web/e2e/fixtures/preview-app.mjs', import.meta.url));
|
|
66
|
+
const result = await probeCezarTool(backend, wire, 'cezar_preview_serve', { command: `node "${fixture}" ${port}`, port, label: 'web' });
|
|
67
|
+
return JSON.stringify(result.response);
|
|
68
|
+
}
|
package/scripts/mock-claude.mjs
CHANGED
|
@@ -14,6 +14,13 @@ function watchdogStall(prompt) {
|
|
|
14
14
|
if (prompt.includes('held-pipe')) {
|
|
15
15
|
watchdogSpawn(process.execPath, ['-e', 'setTimeout(() => {}, 5000)'], { stdio: ['ignore', process.stdout, process.stderr] });
|
|
16
16
|
}
|
|
17
|
+
if (prompt.includes('leftover')) {
|
|
18
|
+
// hearsay-tools/cezarion#890: one child stays in the session's process group and ignores
|
|
19
|
+
// SIGTERM; one leaves the group with setsid. Both exit on their own as a cleanup backstop.
|
|
20
|
+
const leftover = (file) => `process.on('SIGTERM',()=>{}); require('fs').writeFileSync(${JSON.stringify(file)}, String(process.pid)); setTimeout(()=>{},20000)`;
|
|
21
|
+
watchdogSpawn(process.execPath, ['-e', leftover('leftover-group.pid')], { stdio: 'ignore' });
|
|
22
|
+
watchdogSpawn(process.execPath, ['-e', leftover('leftover-session.pid')], { stdio: 'ignore', detached: true }).unref();
|
|
23
|
+
}
|
|
17
24
|
return true;
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -27,6 +34,15 @@ import { createInterface } from 'node:readline';
|
|
|
27
34
|
import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
28
35
|
|
|
29
36
|
import * as parityGateFs from 'node:fs';
|
|
37
|
+
async function waitForMockRelease(fallbackMs) {
|
|
38
|
+
const file = process.env.CEZ_MOCK_RELEASE_FILE;
|
|
39
|
+
if (!file) { if (fallbackMs) await sleep(fallbackMs); return; }
|
|
40
|
+
const deadline = Date.now() + 15_000;
|
|
41
|
+
while (!parityGateFs.existsSync(file)) {
|
|
42
|
+
if (Date.now() > deadline) throw new Error('CEZ_MOCK_RELEASE_FILE was not created');
|
|
43
|
+
await sleep(10);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
30
46
|
|
|
31
47
|
// #401: let the test observe the actual monitoring park before releasing late wire frames.
|
|
32
48
|
async function afterParityPark(prompt) {
|
|
@@ -118,7 +134,55 @@ function writeHandoffAndTodo() {
|
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
async function respond(userText, imageCount, uuid) {
|
|
137
|
+
if (userText.includes('mock:crash-stderr')) {
|
|
138
|
+
const { crashWithStderr } = await import('./mock-runner-crash.mjs');
|
|
139
|
+
if (crashWithStderr(userText, '{"type":"assistant","message":')) return;
|
|
140
|
+
}
|
|
121
141
|
emitReplay(uuid, userText);
|
|
142
|
+
if (userText.includes('mock:autonomous') || userText.startsWith('Continue working autonomously until the task is fully complete.')) {
|
|
143
|
+
const { autonomousReply } = await import('./mock-autonomous.mjs');
|
|
144
|
+
const text = autonomousReply(userText);
|
|
145
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } });
|
|
146
|
+
emit({ type: 'result', subtype: 'success', result: text, user_message_uuids: [uuid].filter(Boolean), usage: { input_tokens: 10, output_tokens: 5 } });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (userText.includes('mock:silent-tail') || userText.includes('mock:tool-tail') || userText.includes('Your last turn ended without a message to the user.')) {
|
|
150
|
+
const silent = await import('./mock-silent-tail.mjs');
|
|
151
|
+
silent.noteSilentTailPrompt(userText);
|
|
152
|
+
if (silent.isFinalMessageNudge(userText)) {
|
|
153
|
+
const kind = silent.finalMessageNudgeKind();
|
|
154
|
+
if (kind === 'ack-only') return;
|
|
155
|
+
if (kind === 'late') await silent.sleep(silent.LATE_REPLY_MS);
|
|
156
|
+
if (kind === 'silent') {
|
|
157
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'thinking', thinking: silent.SILENT_TAIL_REASONING }] } });
|
|
158
|
+
emit({ type: 'result', subtype: 'success', result: '', user_message_uuids: [uuid].filter(Boolean), usage: { input_tokens: 10, output_tokens: 5 } });
|
|
159
|
+
} else {
|
|
160
|
+
if (kind === 'slow-done') {
|
|
161
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: silent.SLOW_DONE_PREFIX }] } });
|
|
162
|
+
await silent.sleep(silent.SLOW_DONE_TAIL_MS);
|
|
163
|
+
}
|
|
164
|
+
const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
|
|
165
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } });
|
|
166
|
+
emit({ type: 'result', subtype: 'success', result: text, user_message_uuids: [uuid].filter(Boolean), usage: { input_tokens: 10, output_tokens: 5 } });
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (silent.isSilentTailScenario(userText)) {
|
|
171
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: silent.SILENT_TAIL_OPENING }] } });
|
|
172
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: 'toolu_silent_gh', name: 'Bash', input: { command: 'gh issue create' } }] } });
|
|
173
|
+
emit({ type: 'user', message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_silent_gh', content: 'created' }] } });
|
|
174
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'thinking', thinking: silent.SILENT_TAIL_REASONING }] } });
|
|
175
|
+
emit({ type: 'result', subtype: 'success', result: silent.SILENT_TAIL_OPENING, user_message_uuids: [uuid].filter(Boolean), usage: { input_tokens: 20, output_tokens: 10 } });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (silent.isToolTailScenario(userText)) {
|
|
179
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: silent.TOOL_TAIL_OPENING }] } });
|
|
180
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: 'toolu_tool_tail', name: 'Bash', input: { command: 'git status --short' } }] } });
|
|
181
|
+
emit({ type: 'user', message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_tool_tail', content: ' M src/example.ts' }] } });
|
|
182
|
+
emit({ type: 'result', subtype: 'success', result: silent.TOOL_TAIL_OPENING, user_message_uuids: [uuid].filter(Boolean), usage: { input_tokens: 20, output_tokens: 10 } });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
122
186
|
// `mock:steer-tool` → one tool call; lines written while it runs are consumed after
|
|
123
187
|
// it (replayed with their uuid) and settled by the SAME result, like Claude 2.1.280 (#505).
|
|
124
188
|
if (userText.includes('mock:steer-tool')) {
|
|
@@ -146,8 +210,15 @@ async function respond(userText, imageCount, uuid) {
|
|
|
146
210
|
return;
|
|
147
211
|
}
|
|
148
212
|
// #401: result-only full text; mapResult and v1 use the same fallback.
|
|
213
|
+
if (userText.includes('mock:turn-messages:')) {
|
|
214
|
+
const { turnMessages } = await import('./mock-turn-messages.mjs');
|
|
215
|
+
const messages = turnMessages(userText);
|
|
216
|
+
for (const text of messages) emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } });
|
|
217
|
+
emit({ type: 'result', subtype: 'success', result: messages.join('\n'), usage: { input_tokens: 10, output_tokens: 5 } });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
149
220
|
if (!userText.includes('[cez-namer]') && userText.includes('mock:ask-snapshot')) {
|
|
150
|
-
emit({ type: 'result', subtype: 'success', result: 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}', usage: { input_tokens: 10, output_tokens: 5 } });
|
|
221
|
+
emit({ type: 'result', subtype: 'success', result: userText.includes('mock:ask-snapshot-bad') ? 'CEZ:ASK {not valid json' : 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}', usage: { input_tokens: 10, output_tokens: 5 } });
|
|
151
222
|
return;
|
|
152
223
|
}
|
|
153
224
|
if (!userText.includes('[cez-namer]') && userText.includes('mock:ask-prose')) {
|
|
@@ -161,6 +232,13 @@ async function respond(userText, imageCount, uuid) {
|
|
|
161
232
|
emit({ type: 'result', subtype: 'success', result: text, usage: { input_tokens: 20, output_tokens: 10 } });
|
|
162
233
|
return;
|
|
163
234
|
}
|
|
235
|
+
if (!userText.includes('[cez-namer]') && userText.includes('mock:preview-serve')) {
|
|
236
|
+
const { previewPrompt } = await import('./mock-ci-tool.mjs');
|
|
237
|
+
const text = await previewPrompt('claude', process.argv.slice(2), userText);
|
|
238
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } });
|
|
239
|
+
emit({ type: 'result', subtype: 'success', result: text, usage: { input_tokens: 20, output_tokens: 10 } });
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
164
242
|
turn += 1;
|
|
165
243
|
await sleep(250);
|
|
166
244
|
// `mock:done` anywhere in the message → the reply ends with the CEZ:DONE
|
|
@@ -271,6 +349,37 @@ async function respond(userText, imageCount, uuid) {
|
|
|
271
349
|
emit({ type: 'result', subtype: 'success', result: 'busy complete' });
|
|
272
350
|
return;
|
|
273
351
|
}
|
|
352
|
+
if (userText.includes('mock:hold-done')) {
|
|
353
|
+
await waitForMockRelease(400);
|
|
354
|
+
const held = 'parity hold-done: content after the pause\n\nCEZ:DONE';
|
|
355
|
+
emit({
|
|
356
|
+
type: 'assistant',
|
|
357
|
+
message: { role: 'assistant', content: [{ type: 'text', text: held }], usage: { input_tokens: 20, output_tokens: 10 } },
|
|
358
|
+
});
|
|
359
|
+
emit({ type: 'result', subtype: 'success', result: held, usage: { input_tokens: 20, output_tokens: 10 }, total_cost_usd: 0.0001 });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (userText.includes('mock:hold-ask')) {
|
|
363
|
+
await waitForMockRelease(400);
|
|
364
|
+
const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
|
|
365
|
+
emit({
|
|
366
|
+
type: 'assistant',
|
|
367
|
+
message: { role: 'assistant', content: [{ type: 'text', text: ask }], usage: { input_tokens: 20, output_tokens: 10 } },
|
|
368
|
+
});
|
|
369
|
+
emit({ type: 'result', subtype: 'success', result: ask, usage: { input_tokens: 20, output_tokens: 10 }, total_cost_usd: 0.0001 });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (userText.includes('mock:hold-gated')) {
|
|
373
|
+
await waitForMockRelease(250);
|
|
374
|
+
const gated = 'parity hold-gated: content after the pause';
|
|
375
|
+
emit({
|
|
376
|
+
type: 'assistant',
|
|
377
|
+
message: { role: 'assistant', content: [{ type: 'text', text: gated }], usage: { input_tokens: 20, output_tokens: 10 } },
|
|
378
|
+
});
|
|
379
|
+
await sleep(250);
|
|
380
|
+
emit({ type: 'result', subtype: 'success', result: gated, usage: { input_tokens: 20, output_tokens: 10 }, total_cost_usd: 0.0001 });
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
274
383
|
if (userText.includes('mock:hold')) {
|
|
275
384
|
await sleep(250);
|
|
276
385
|
const held = 'parity hold: content after the pause';
|
|
@@ -14,6 +14,13 @@ function watchdogStall(prompt) {
|
|
|
14
14
|
if (prompt.includes('held-pipe')) {
|
|
15
15
|
watchdogSpawn(process.execPath, ['-e', 'setTimeout(() => {}, 5000)'], { stdio: ['ignore', process.stdout, process.stderr] });
|
|
16
16
|
}
|
|
17
|
+
if (prompt.includes('leftover')) {
|
|
18
|
+
// hearsay-tools/cezarion#890: one child stays in the session's process group and ignores
|
|
19
|
+
// SIGTERM; one leaves the group with setsid. Both exit on their own as a cleanup backstop.
|
|
20
|
+
const leftover = (file) => `process.on('SIGTERM',()=>{}); require('fs').writeFileSync(${JSON.stringify(file)}, String(process.pid)); setTimeout(()=>{},20000)`;
|
|
21
|
+
watchdogSpawn(process.execPath, ['-e', leftover('leftover-group.pid')], { stdio: 'ignore' });
|
|
22
|
+
watchdogSpawn(process.execPath, ['-e', leftover('leftover-session.pid')], { stdio: 'ignore', detached: true }).unref();
|
|
23
|
+
}
|
|
17
24
|
return true;
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -42,12 +49,15 @@ const write = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
|
|
|
42
49
|
// #505: like the real app-server, any active main-thread turn accepts `turn/steer`.
|
|
43
50
|
// Steers accepted by an ordinary scripted turn are read just before it completes:
|
|
44
51
|
// a userMessage item (echoing clientUserMessageId as clientId) and an echo reply.
|
|
52
|
+
let autonomousTurn = 0;
|
|
45
53
|
let activeTurnId = null;
|
|
46
54
|
let pendingSteers = [];
|
|
47
55
|
let steerEchoSerial = 0;
|
|
48
56
|
const emit = (obj) => {
|
|
49
57
|
const ending = (obj.method === 'turn/completed' || obj.method === 'turn/failed') && (!obj.params?.threadId || obj.params.threadId === 'th_mock_1');
|
|
50
|
-
|
|
58
|
+
// #486 review: CEZ_MOCK_CODEX_NO_STEER_ECHO=1 admits the steer (RPC result) but never
|
|
59
|
+
// echoes its userMessage, so an accepted-but-unread follow-up can complete the turn.
|
|
60
|
+
if (ending && obj.method === 'turn/completed' && pendingSteers.length && process.env.CEZ_MOCK_CODEX_NO_STEER_ECHO !== '1') {
|
|
51
61
|
const turnId = activeTurnId ?? 'turn_mock_1';
|
|
52
62
|
for (const entry of pendingSteers.splice(0)) {
|
|
53
63
|
const item = { type: 'userMessage', id: `item_user_generic_${++steerEchoSerial}`, clientId: entry.clientId, content: [{ type: 'text', text: entry.text }] };
|
|
@@ -71,6 +81,15 @@ let ciWire;
|
|
|
71
81
|
let steerTurn = null;
|
|
72
82
|
let steerSerial = 0;
|
|
73
83
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
84
|
+
async function waitForMockRelease(fallbackMs) {
|
|
85
|
+
const file = process.env.CEZ_MOCK_RELEASE_FILE;
|
|
86
|
+
if (!file) { if (fallbackMs) await sleep(fallbackMs); return; }
|
|
87
|
+
const deadline = Date.now() + 15_000;
|
|
88
|
+
while (!parityGateFs.existsSync(file)) {
|
|
89
|
+
if (Date.now() > deadline) throw new Error('CEZ_MOCK_RELEASE_FILE was not created');
|
|
90
|
+
await sleep(10);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
74
93
|
const completeSteerTurn = (turnId) => {
|
|
75
94
|
steerTurn = null;
|
|
76
95
|
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: turnId, status: 'completed' } } });
|
|
@@ -190,6 +209,12 @@ rl.on('line', async (line) => {
|
|
|
190
209
|
emit({ id: msg.id, result: { thread: { id: msg.params?.threadId }, sandbox } });
|
|
191
210
|
}
|
|
192
211
|
} else if (msg.method === 'turn/start') {
|
|
212
|
+
const openingCrashText = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
|
|
213
|
+
if (openingCrashText.includes('mock:crash-stderr-pre-ack')) {
|
|
214
|
+
const { crashWithStderr } = await import('./mock-runner-crash.mjs');
|
|
215
|
+
crashWithStderr(openingCrashText, '{"method":"turn/started","params":');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
193
218
|
if (rejectedPermissionOverride) permissionWarning();
|
|
194
219
|
activeTurnId = 'turn_mock_1';
|
|
195
220
|
// owned-input-delivery.testkit.ts patches the exact `emit(...)` line below; keep it verbatim.
|
|
@@ -197,9 +222,17 @@ rl.on('line', async (line) => {
|
|
|
197
222
|
emit({ id: msg.id, result: { turn: { id: 'turn_mock_1' } } });
|
|
198
223
|
}
|
|
199
224
|
const turnText = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
|
|
225
|
+
let silent;
|
|
226
|
+
if (turnText.includes('mock:silent-tail') || turnText.includes('Your last turn ended without a message to the user.')) {
|
|
227
|
+
silent = await import('./mock-silent-tail.mjs');
|
|
228
|
+
silent.noteSilentTailPrompt(turnText);
|
|
229
|
+
if (silent.isAckOnlyNudge(turnText)) return;
|
|
230
|
+
if (silent.isLateNudge(turnText) || silent.isLateTurnStartNudge(turnText)) await silent.sleep(silent.LATE_REPLY_MS);
|
|
231
|
+
}
|
|
200
232
|
if (!turnText.includes('mock:no-progress-ack-only')) {
|
|
201
233
|
emit({ method: 'turn/started', params: { turn: { id: 'turn_mock_1', status: 'inProgress', items: [] } } });
|
|
202
234
|
}
|
|
235
|
+
if (silent?.isLateTurnStartNudge(turnText)) await silent.sleep(silent.LATE_TURN_START_HOLD_MS);
|
|
203
236
|
// The real app-server records the turn's own input as a userMessage item,
|
|
204
237
|
// echoing clientUserMessageId as clientId (probe 0.155.1, #505).
|
|
205
238
|
const opening = { type: 'userMessage', id: `item_user_open_${++steerEchoSerial}`, clientId: msg.params?.clientUserMessageId ?? null, content: [{ type: 'text', text: turnText }] };
|
|
@@ -207,6 +240,49 @@ rl.on('line', async (line) => {
|
|
|
207
240
|
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
|
|
208
241
|
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
|
|
209
242
|
}
|
|
243
|
+
if (turnText.includes('mock:crash-stderr')) {
|
|
244
|
+
const { crashWithStderr } = await import('./mock-runner-crash.mjs');
|
|
245
|
+
await new Promise(resolve => setTimeout(resolve, 30));
|
|
246
|
+
if (crashWithStderr(turnText, '{"method":"item/commandExecution/outputDelta","params":')) return;
|
|
247
|
+
}
|
|
248
|
+
if (turnText.includes('mock:autonomous') || turnText.startsWith('Continue working autonomously until the task is fully complete.')) {
|
|
249
|
+
const { autonomousReply } = await import('./mock-autonomous.mjs');
|
|
250
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `autonomous-${++autonomousTurn}`, text: autonomousReply(turnText) } } });
|
|
251
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (turnText.includes('mock:silent-tail') || turnText.includes('mock:tool-tail') || turnText.includes('Your last turn ended without a message to the user.')) {
|
|
255
|
+
const silent = await import('./mock-silent-tail.mjs');
|
|
256
|
+
silent.noteSilentTailPrompt(turnText);
|
|
257
|
+
if (silent.isFinalMessageNudge(turnText)) {
|
|
258
|
+
const kind = silent.finalMessageNudgeKind();
|
|
259
|
+
if (kind === 'silent') {
|
|
260
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-nudge-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
|
|
261
|
+
} else {
|
|
262
|
+
if (kind === 'slow-done') {
|
|
263
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-prefix', text: silent.SLOW_DONE_PREFIX } } });
|
|
264
|
+
await silent.sleep(silent.SLOW_DONE_TAIL_MS);
|
|
265
|
+
}
|
|
266
|
+
const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
|
|
267
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-nudge-done', text } } });
|
|
268
|
+
}
|
|
269
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (silent.isSilentTailScenario(turnText)) {
|
|
273
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'silent-open', text: silent.SILENT_TAIL_OPENING } } });
|
|
274
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'silent-gh', command: ['bash', '-lc', 'gh issue create'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
|
|
275
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'reasoning', id: 'silent-rsn', summary: silent.SILENT_TAIL_REASONING, content: silent.SILENT_TAIL_REASONING } } });
|
|
276
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (silent.isToolTailScenario(turnText)) {
|
|
280
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'tool-tail-open', text: silent.TOOL_TAIL_OPENING } } });
|
|
281
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'tool-tail-git', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
|
|
282
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
210
286
|
// Native thread attribution from collab-agent-tool-call.ndjson (#121/#401).
|
|
211
287
|
if (turnText.includes('mock:subagent-after-park')) {
|
|
212
288
|
emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'park-spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_park_child'] } } });
|
|
@@ -219,8 +295,14 @@ rl.on('line', async (line) => {
|
|
|
219
295
|
return;
|
|
220
296
|
}
|
|
221
297
|
// #401: agentMessage completion without deltas (same item envelope as baseline).
|
|
298
|
+
if (turnText.includes('mock:turn-messages:')) {
|
|
299
|
+
const { turnMessages } = await import('./mock-turn-messages.mjs');
|
|
300
|
+
for (const text of turnMessages(turnText)) emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_multi_${++echoSerial}`, text } } });
|
|
301
|
+
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
222
304
|
if (turnText.includes('mock:ask-snapshot')) {
|
|
223
|
-
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-snapshot', text: 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}' } } });
|
|
305
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-snapshot', text: turnText.includes('mock:ask-snapshot-bad') ? 'CEZ:ASK {not valid json' : 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}' } } });
|
|
224
306
|
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
225
307
|
return;
|
|
226
308
|
}
|
|
@@ -293,6 +375,26 @@ rl.on('line', async (line) => {
|
|
|
293
375
|
} });
|
|
294
376
|
return;
|
|
295
377
|
}
|
|
378
|
+
if (turnText.includes('mock:hold-done')) {
|
|
379
|
+
await waitForMockRelease(400);
|
|
380
|
+
const full = 'parity hold-done: content after the pause\n\nCEZ:DONE';
|
|
381
|
+
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: '' } } });
|
|
382
|
+
emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hd1', delta: full } });
|
|
383
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hd1', text: full } } });
|
|
384
|
+
emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
|
|
385
|
+
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (turnText.includes('mock:hold-ask')) {
|
|
389
|
+
await waitForMockRelease(400);
|
|
390
|
+
const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
|
|
391
|
+
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: '' } } });
|
|
392
|
+
emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_ha1', delta: ask } });
|
|
393
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_ha1', text: ask } } });
|
|
394
|
+
emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
|
|
395
|
+
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
296
398
|
if (turnText.includes('mock:done')) {
|
|
297
399
|
// A turn that DECLARES the task complete, so the run reaches cezar's
|
|
298
400
|
// review gate instead of parking for the user. A markerless turn-end
|
|
@@ -315,6 +417,16 @@ rl.on('line', async (line) => {
|
|
|
315
417
|
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
316
418
|
return;
|
|
317
419
|
}
|
|
420
|
+
if (turnText.includes('mock:hold-gated')) {
|
|
421
|
+
await waitForMockRelease(250);
|
|
422
|
+
const gated = 'parity hold-gated: content after the pause';
|
|
423
|
+
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: '' } } });
|
|
424
|
+
emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_hg1', delta: gated } });
|
|
425
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_hg1', text: gated } } });
|
|
426
|
+
emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, last: { totalTokens: 30, inputTokens: 20, outputTokens: 10 } } } });
|
|
427
|
+
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
318
430
|
if (turnText.includes('mock:hold')) {
|
|
319
431
|
// The `turn/start` response and `turn/started` above are the ack. Holding
|
|
320
432
|
// the content AND `turn/completed` behind it is what makes harness parity
|
|
@@ -413,6 +525,9 @@ rl.on('line', async (line) => {
|
|
|
413
525
|
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'inProgress' } } });
|
|
414
526
|
emit({ method: 'item/commandExecution/outputDelta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_c1', delta: ' M src/example.ts\n' } });
|
|
415
527
|
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'commandExecution', id: 'item_c1', command: ['bash', '-lc', 'git status --short'], cwd: '/repo', status: 'completed', exitCode: 0 } } });
|
|
528
|
+
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: '' } } });
|
|
529
|
+
emit({ method: 'item/agentMessage/delta', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', itemId: 'item_m2', delta: 'Done with the first pass.' } });
|
|
530
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'item_m2', text: 'Done with the first pass.' } } });
|
|
416
531
|
emit({ method: 'thread/tokenUsage/updated', params: { threadId: 'th_mock_1', tokenUsage: { total: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 }, last: { totalTokens: 1500, inputTokens: 1200, outputTokens: 300 } } } });
|
|
417
532
|
emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
418
533
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// #486 review: buffers each `turn/steer` RPC response and writes it in the SAME
|
|
3
|
+
// stdout write as the following `turn/completed` (or `turn/failed`) notification,
|
|
4
|
+
// preserving order. Reproduces Codex humanPromptsPending lagging a coalesced chunk.
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
const child = spawn(process.execPath, [join(dirname(fileURLToPath(import.meta.url)), 'mock-codex-app-server.mjs'), ...process.argv.slice(2)], {
|
|
10
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
11
|
+
env: process.env,
|
|
12
|
+
});
|
|
13
|
+
const steerIds = new Set();
|
|
14
|
+
let stdinBuf = '';
|
|
15
|
+
process.stdin.setEncoding('utf8');
|
|
16
|
+
process.stdin.on('data', chunk => {
|
|
17
|
+
child.stdin.write(chunk);
|
|
18
|
+
stdinBuf += chunk;
|
|
19
|
+
let idx;
|
|
20
|
+
while ((idx = stdinBuf.indexOf('\n')) >= 0) {
|
|
21
|
+
const line = stdinBuf.slice(0, idx);
|
|
22
|
+
stdinBuf = stdinBuf.slice(idx + 1);
|
|
23
|
+
try {
|
|
24
|
+
const msg = JSON.parse(line);
|
|
25
|
+
if (msg.method === 'turn/steer' && typeof msg.id === 'number') steerIds.add(msg.id);
|
|
26
|
+
} catch { /* ignore a partial or non-JSON line */ }
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
process.stdin.on('end', () => child.stdin.end());
|
|
30
|
+
|
|
31
|
+
let outBuf = '';
|
|
32
|
+
const held = [];
|
|
33
|
+
child.stdout.setEncoding('utf8');
|
|
34
|
+
child.stdout.on('data', chunk => {
|
|
35
|
+
outBuf += chunk;
|
|
36
|
+
let idx;
|
|
37
|
+
while ((idx = outBuf.indexOf('\n')) >= 0) {
|
|
38
|
+
const line = outBuf.slice(0, idx + 1);
|
|
39
|
+
outBuf = outBuf.slice(idx + 1);
|
|
40
|
+
let msg;
|
|
41
|
+
try { msg = JSON.parse(line); } catch { process.stdout.write(line); continue; }
|
|
42
|
+
if (typeof msg.id === 'number' && steerIds.has(msg.id) && (msg.result !== undefined || msg.error !== undefined)) {
|
|
43
|
+
steerIds.delete(msg.id);
|
|
44
|
+
held.push(line);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if ((msg.method === 'turn/completed' || msg.method === 'turn/failed') && held.length) {
|
|
48
|
+
process.stdout.write(held.splice(0).join('') + line);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
process.stdout.write(line);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
child.on('exit', (code, signal) => {
|
|
55
|
+
if (outBuf) process.stdout.write(outBuf);
|
|
56
|
+
if (held.length) process.stdout.write(held.join(''));
|
|
57
|
+
if (signal) process.kill(process.pid, signal);
|
|
58
|
+
else process.exit(code ?? 0);
|
|
59
|
+
});
|