@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
|
@@ -1,18 +1,36 @@
|
|
|
1
|
+
import { opencodeSkillWarning } from './opencode-session-error.js';
|
|
2
|
+
import { summarizeRunnerStderr } from './runner-stderr.js';
|
|
3
|
+
import { finished } from 'node:stream/promises';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
1
5
|
import { fileURLToPath } from 'node:url';
|
|
2
6
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { spawn as nodeSpawn } from 'node:child_process';
|
|
4
7
|
import { parseEffort } from '../contract/index.js';
|
|
5
8
|
import { request as httpRequest } from 'node:http';
|
|
6
9
|
import { InputSubmissions } from './input-submissions.js';
|
|
7
|
-
import { prependSystemPrompt, trackChildExit } from './agent-runner.js';
|
|
10
|
+
import { isSignalTerminationExit, prependSystemPrompt, trackChildExit } from './agent-runner.js';
|
|
11
|
+
import { signalSession, spawnSessionLeader } from './session-process.js';
|
|
8
12
|
import { buildChildEnv } from './agent-env.js';
|
|
9
13
|
import { ciOpenCodeEnv } from '../ci-wait/injection.js';
|
|
10
14
|
import { parseAskRequest } from './ask.js';
|
|
11
|
-
import { AUTO_END_DELAY_MS, DEFAULT_RUN_TIMEOUT_MS } from './runner-runtime.js';
|
|
15
|
+
import { boundOutputDrainAfterExit, AUTO_END_DELAY_MS, DEFAULT_RUN_TIMEOUT_MS } from './runner-runtime.js';
|
|
12
16
|
import { formatModelIdentity, parseModelIdentity } from './model-identity.js';
|
|
13
17
|
import { V1TextCoalescer } from './v1-text-coalescer.js';
|
|
14
18
|
import { createOpencodeUiState, mapOpencodeEvent, opencodeSessionStarted, opencodeTurnStarted, } from './opencode-ui-mapper.js';
|
|
15
19
|
const SERVER_START_TIMEOUT_MS = 30_000;
|
|
20
|
+
/**
|
|
21
|
+
* Pause before the one retry of a server that exited before listening (#872).
|
|
22
|
+
* opencode 1.18.33, started within ~2 s of SIGTERM to the previous server on
|
|
23
|
+
* the same DB, printed `ServeError` and exited; the next start succeeded. The
|
|
24
|
+
* failing start can die well inside that window, so the pause alone spans it;
|
|
25
|
+
* it is only ever paid by a start that already failed.
|
|
26
|
+
*/
|
|
27
|
+
export const SERVE_START_RETRY_DELAY_MS = 2_000;
|
|
28
|
+
/** The URL wait saw the process exit before it printed a URL. */
|
|
29
|
+
class ServeExitedBeforeListening extends Error {
|
|
30
|
+
constructor() {
|
|
31
|
+
super('opencode serve exited before it started listening');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
16
34
|
/** Grace between the teardown SIGTERM and the SIGKILL that follows it. */
|
|
17
35
|
export const KILL_GRACE_MS = 4_000;
|
|
18
36
|
/**
|
|
@@ -20,8 +38,9 @@ export const KILL_GRACE_MS = 4_000;
|
|
|
20
38
|
* the opencode TUI talks to) with an SSE event stream. One server per session,
|
|
21
39
|
* bound to the run's `cwd` (worktree), gives OpenCode the same multi-turn shape
|
|
22
40
|
* as the Claude runner: each `sendMessage` posts another prompt to the same
|
|
23
|
-
* session (history is kept server-side), and `session/abort` cancels.
|
|
24
|
-
*
|
|
41
|
+
* session (history is kept server-side), and `session/abort` cancels.
|
|
42
|
+
* Continue and restart recovery GET /session/{id} and prompt that session; a 404
|
|
43
|
+
* falls back to one fresh session with the full system prompt.
|
|
25
44
|
*
|
|
26
45
|
* Auth = the host's opencode config/logins. The agent runs autonomously
|
|
27
46
|
* (auto-approved permissions); this adapter does not map `spec.allowedTools`.
|
|
@@ -32,8 +51,8 @@ export const KILL_GRACE_MS = 4_000;
|
|
|
32
51
|
* What `opencode serve` receives from each `AgentRunSpec` field (#284). The
|
|
33
52
|
* server auto-approves permissions and this adapter maps no per-tool
|
|
34
53
|
* allowlist, so `allowedTools`/`bashAllowlist` are declared dropped rather than
|
|
35
|
-
* mapped (spec 2026-07-17-permission-modes)
|
|
36
|
-
*
|
|
54
|
+
* mapped (spec 2026-07-17-permission-modes). Resume GETs `/session/{id}` and
|
|
55
|
+
* prompts it; a 404 opens one fresh session. Held against the recorded HTTP
|
|
37
56
|
* requests by the harness parity matrix.
|
|
38
57
|
*/
|
|
39
58
|
export const OPENCODE_SPEC_SUPPORT = {
|
|
@@ -43,32 +62,36 @@ export const OPENCODE_SPEC_SUPPORT = {
|
|
|
43
62
|
images: { honored: false, reason: 'the adapter posts text parts only; image blocks are dropped' },
|
|
44
63
|
cwd: { honored: true, via: 'opencode serve spawn cwd; the session is bound to it' },
|
|
45
64
|
allowedTools: { honored: false, reason: 'permissions are auto-approved server-side and no per-tool allowlist is mapped' },
|
|
46
|
-
restrictNativeDelegation: { honored: true, via: 'POST /session permission rule denying task (D1)' },
|
|
65
|
+
restrictNativeDelegation: { honored: true, via: 'POST /session permission rule denying task (D1); PATCH /session/{id} permission on resume when the deny is absent — the deny persists on the session once added (a later unrestricted Continue cannot remove it; fails closed)' },
|
|
47
66
|
bashAllowlist: { honored: false, reason: 'no per-tool allowlist is mapped, so no command-prefix restriction either' },
|
|
48
67
|
additionalDirectories: { honored: false, reason: 'the server works from cwd; no extra-root mapping' },
|
|
49
68
|
env: { honored: true, via: 'merged over the child env through buildChildEnv' },
|
|
50
69
|
model: { honored: true, via: 'prompt_async model { providerID, modelID }, split from provider/model' },
|
|
51
70
|
effort: { honored: true, via: 'prompt_async variant, canonical level' },
|
|
52
71
|
timeoutMs: { honored: true, via: 'wall-clock kill switch on the child process' },
|
|
53
|
-
sessionId: { honored:
|
|
54
|
-
resume: { honored:
|
|
72
|
+
sessionId: { honored: true, via: 'GET /session/{id} when resume is set; a fresh POST /session mints its own id' },
|
|
73
|
+
resume: { honored: true, via: 'GET /session/{id} in place of POST /session; 404 falls back to a fresh session' },
|
|
74
|
+
resumeFallbackSystemPrompt: { honored: true, via: 'opening prependSystemPrompt of a fresh POST /session when GET /session/{id} returns 404 on resume' },
|
|
55
75
|
};
|
|
56
76
|
/** How long an idle session may sit on steered input before it counts as a lost wake. */
|
|
57
77
|
export const OPENCODE_LOST_WAKE_GRACE_MS = 2_000;
|
|
58
78
|
export class OpencodeServerRunner {
|
|
59
79
|
backend = 'opencode';
|
|
60
80
|
specSupport = OPENCODE_SPEC_SUPPORT;
|
|
81
|
+
systemPromptOnResume = 'in-thread';
|
|
61
82
|
inputDelivery = {
|
|
62
83
|
mode: 'steer', consumption: 'observable',
|
|
63
84
|
via: 'prompt_async while busy; assistant message.updated parentID at consumption',
|
|
64
85
|
};
|
|
65
86
|
bin;
|
|
66
87
|
timeoutMs;
|
|
88
|
+
serverStartTimeoutMs;
|
|
67
89
|
lastSession = null;
|
|
68
90
|
constructor(opts = {}) {
|
|
69
91
|
this.bin = opts.bin ?? process.env.CEZ_OPENCODE_BIN ?? (process.env.CEZ_DRY_RUN === '1'
|
|
70
92
|
? fileURLToPath(new URL('../../scripts/mock-opencode-serve.mjs', import.meta.url)) : 'opencode');
|
|
71
93
|
this.timeoutMs = opts.timeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;
|
|
94
|
+
this.serverStartTimeoutMs = opts.serverStartTimeoutMs ?? SERVER_START_TIMEOUT_MS;
|
|
72
95
|
}
|
|
73
96
|
run(spec, onEvent) {
|
|
74
97
|
return this.startSession(spec, onEvent, { autoEndAfterFirstTurn: true }).result;
|
|
@@ -77,7 +100,7 @@ export class OpencodeServerRunner {
|
|
|
77
100
|
this.lastSession?.interrupt();
|
|
78
101
|
}
|
|
79
102
|
startSession(spec, onEvent, opts = {}) {
|
|
80
|
-
const session = new OpencodeSession(this.bin, this.timeoutMs, spec, onEvent, opts);
|
|
103
|
+
const session = new OpencodeSession(this.bin, this.timeoutMs, this.serverStartTimeoutMs, spec, onEvent, opts);
|
|
81
104
|
this.lastSession = session;
|
|
82
105
|
return session;
|
|
83
106
|
}
|
|
@@ -85,20 +108,27 @@ export class OpencodeServerRunner {
|
|
|
85
108
|
/** One live `opencode serve` process driving a single session. */
|
|
86
109
|
class OpencodeSession {
|
|
87
110
|
bin;
|
|
111
|
+
serverStartTimeoutMs;
|
|
88
112
|
spec;
|
|
89
113
|
onEvent;
|
|
90
114
|
opts;
|
|
91
115
|
result;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
116
|
+
/** The live server process. A start retry replaces it (#872), so every
|
|
117
|
+
* liveness, pid, signal and stderr read goes through this field. */
|
|
118
|
+
serve;
|
|
119
|
+
/** The first start, once the retry spawned for it ALSO exited before
|
|
120
|
+
* listening on its own (#872) — the one case that names both attempts. */
|
|
121
|
+
failedStart;
|
|
122
|
+
/** The retry's own spawn threw synchronously (EAGAIN, EMFILE…), so no second
|
|
123
|
+
* process exists and the first start's exit alone would misname the failure. */
|
|
124
|
+
retrySpawnError;
|
|
125
|
+
/** Aborted by teardown; cancels a pending start retry. */
|
|
126
|
+
startRetry = new AbortController();
|
|
96
127
|
serverOpen = true;
|
|
128
|
+
exitFailure;
|
|
97
129
|
baseUrl;
|
|
98
130
|
sessionId;
|
|
99
131
|
ready;
|
|
100
|
-
resolveExit;
|
|
101
|
-
exited;
|
|
102
132
|
sse = new AbortController();
|
|
103
133
|
toolCalls = [];
|
|
104
134
|
textChunks = [];
|
|
@@ -161,42 +191,20 @@ class OpencodeSession {
|
|
|
161
191
|
/** Bumped by `discardQueuedMessages` so a `prompt()` waiter that captured
|
|
162
192
|
* the previous generation returns instead of posting after idle. */
|
|
163
193
|
queuedPromptGeneration = 0;
|
|
194
|
+
/** Human `prompt()` waiters in the current generation that have not posted yet (#486). */
|
|
195
|
+
humanPromptWaiters = 0;
|
|
164
196
|
autoEndTimer;
|
|
165
|
-
spawnFailed = null;
|
|
166
197
|
timedOut = false;
|
|
167
198
|
openingPromptFailed = false;
|
|
168
|
-
|
|
169
|
-
signalled = false;
|
|
170
|
-
constructor(bin, timeoutMs, spec, onEvent, opts) {
|
|
199
|
+
constructor(bin, timeoutMs, serverStartTimeoutMs, spec, onEvent, opts) {
|
|
171
200
|
this.bin = bin;
|
|
201
|
+
this.serverStartTimeoutMs = serverStartTimeoutMs;
|
|
172
202
|
this.spec = spec;
|
|
173
203
|
this.onEvent = onEvent;
|
|
174
204
|
this.opts = opts;
|
|
175
|
-
|
|
176
|
-
const port = 40000 + Math.floor(Math.random() * 20000);
|
|
177
|
-
try {
|
|
178
|
-
this.child = nodeSpawn(bin, ['serve', '--hostname', '127.0.0.1', '--port', String(port)], {
|
|
179
|
-
cwd: spec.cwd,
|
|
180
|
-
env: buildChildEnv({ backend: 'opencode', extraEnv: ciOpenCodeEnv(spec) }),
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
throw wrapSpawnError(err, bin);
|
|
185
|
-
}
|
|
186
|
-
this.hasExited = trackChildExit(this.child);
|
|
187
|
-
this.child.on('error', (err) => {
|
|
188
|
-
this.spawnFailed = wrapSpawnError(err, bin);
|
|
189
|
-
});
|
|
190
|
-
this.exited = new Promise((resolve) => {
|
|
191
|
-
this.resolveExit = resolve;
|
|
192
|
-
});
|
|
193
|
-
this.child.once('exit', () => this.resolveExit());
|
|
194
|
-
this.child.once('close', () => this.resolveExit());
|
|
195
|
-
const stderrChunks = [];
|
|
196
|
-
this.child.stderr.setEncoding('utf8');
|
|
197
|
-
this.child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
|
|
205
|
+
this.serve = this.spawnServe();
|
|
198
206
|
// The server prints its URL on stdout once listening.
|
|
199
|
-
const urlReady = this.
|
|
207
|
+
const urlReady = this.startServer();
|
|
200
208
|
const limitMs = spec.timeoutMs ?? timeoutMs;
|
|
201
209
|
let deadline;
|
|
202
210
|
if (limitMs > 0) {
|
|
@@ -214,12 +222,12 @@ class OpencodeSession {
|
|
|
214
222
|
try {
|
|
215
223
|
await this.ready;
|
|
216
224
|
// Live for the whole session; the SSE loop runs until end()/interrupt.
|
|
217
|
-
await this.exited;
|
|
225
|
+
await this.serve.exited;
|
|
218
226
|
}
|
|
219
227
|
catch (err) {
|
|
220
228
|
if (!this.timedOut && !this.openingPromptFailed) {
|
|
221
229
|
const message = err instanceof Error ? err.message : String(err);
|
|
222
|
-
this.
|
|
230
|
+
this.exitFailure = `opencode: ${message}`;
|
|
223
231
|
}
|
|
224
232
|
}
|
|
225
233
|
finally {
|
|
@@ -232,9 +240,49 @@ class OpencodeSession {
|
|
|
232
240
|
this.agentRequest?.abort();
|
|
233
241
|
this.terminate();
|
|
234
242
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
const serve = this.serve;
|
|
244
|
+
await serve.exited;
|
|
245
|
+
await serve.outputDrained;
|
|
246
|
+
if (serve.spawnFailed) {
|
|
247
|
+
this.emit({ type: 'error', message: serve.spawnFailed.message });
|
|
248
|
+
throw serve.spawnFailed;
|
|
249
|
+
}
|
|
250
|
+
// SSE closure can precede the process exit event. Choose one authoritative
|
|
251
|
+
// error after settlement, before the synthetic turn-end, retaining the code.
|
|
252
|
+
const code = serve.child.exitCode;
|
|
253
|
+
const crashed = code !== null && code !== 0 && !(serve.signalled && isSignalTerminationExit(code));
|
|
254
|
+
if (!this.timedOut && (crashed || this.exitFailure)) {
|
|
255
|
+
const stderr = serve.stderrChunks.join('');
|
|
256
|
+
const detail = summarizeRunnerStderr(stderr);
|
|
257
|
+
if (this.retrySpawnError) {
|
|
258
|
+
if (stderr.trim())
|
|
259
|
+
this.emit({ type: 'note', message: `opencode serve stderr:\n${stderr}` });
|
|
260
|
+
this.emit({
|
|
261
|
+
type: 'error',
|
|
262
|
+
message: `opencode serve exited before listening (${describeEarlyExit(serve)}) and its retry could not start: ${this.retrySpawnError.message}`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
else if (this.failedStart) {
|
|
266
|
+
// #872: the retry failed too — one error naming both attempts.
|
|
267
|
+
const attempts = [this.failedStart, serve];
|
|
268
|
+
const notes = attempts.map((attempt, i) => {
|
|
269
|
+
const text = attempt.stderrChunks.join('');
|
|
270
|
+
return text.trim() ? `opencode serve stderr (attempt ${i + 1}):\n${text}` : '';
|
|
271
|
+
}).filter(Boolean);
|
|
272
|
+
if (notes.length)
|
|
273
|
+
this.emit({ type: 'note', message: notes.join('\n') });
|
|
274
|
+
const described = attempts.map((attempt, i) => `attempt ${i + 1}: ${describeEarlyExit(attempt)}`);
|
|
275
|
+
this.emit({ type: 'error', message: `opencode serve exited before listening on both attempts — ${described.join('; ')}` });
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
if (stderr.trim())
|
|
279
|
+
this.emit({ type: 'note', message: `opencode serve stderr:\n${stderr}` });
|
|
280
|
+
const message = crashed
|
|
281
|
+
? `opencode serve exited with code ${code}${detail ? ` — ${detail}` : ''}`
|
|
282
|
+
: this.exitFailure;
|
|
283
|
+
this.emit({ type: 'error', message });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
238
286
|
// Timeout/interrupt can cut the SSE feed before its `session.idle` —
|
|
239
287
|
// close the turn (idempotent) so consumers still see the v1 boundary,
|
|
240
288
|
// and recover prose buffered mid-part.
|
|
@@ -261,7 +309,7 @@ class OpencodeSession {
|
|
|
261
309
|
return this.serverOpen;
|
|
262
310
|
}
|
|
263
311
|
get pid() {
|
|
264
|
-
return this.child.pid;
|
|
312
|
+
return this.serve.child.pid;
|
|
265
313
|
}
|
|
266
314
|
sendAgentMessage(content, inputIds = []) {
|
|
267
315
|
// #505: a busy session is steered with prompt_async; only a pending native question,
|
|
@@ -333,6 +381,13 @@ class OpencodeSession {
|
|
|
333
381
|
discardQueuedMessages() {
|
|
334
382
|
this.queuedPromptGeneration += 1;
|
|
335
383
|
this.queuedQuestionMessages.length = 0;
|
|
384
|
+
this.humanPromptWaiters = 0; // #486: must read false immediately, not when waiters resume
|
|
385
|
+
}
|
|
386
|
+
holdsHumanInput() {
|
|
387
|
+
return this.humanPromptWaiters > 0 || this.queuedQuestionMessages.length > 0;
|
|
388
|
+
}
|
|
389
|
+
heldHumanInputCount() {
|
|
390
|
+
return this.humanPromptWaiters + this.queuedQuestionMessages.length;
|
|
336
391
|
}
|
|
337
392
|
deliverPrompt(text) {
|
|
338
393
|
this.pendingPromptRequests += 1;
|
|
@@ -399,35 +454,136 @@ class OpencodeSession {
|
|
|
399
454
|
* server it was written for: one that installs its own SIGTERM handler stayed
|
|
400
455
|
* alive with `killed = true` and `exitCode === null`, outliving the whole
|
|
401
456
|
* window (#858, the same defect #844 fixed for the other two backends). Every
|
|
402
|
-
* caller here is followed by `await
|
|
457
|
+
* caller here is followed by `await serve.exited`, so a server that survived
|
|
403
458
|
* SIGTERM did not just leak — it hung the session's result forever.
|
|
404
459
|
*
|
|
405
460
|
* One teardown per session: all three call sites can run for the same session
|
|
406
461
|
* (`interrupt()` on the deadline, then the result promise's `finally`), and
|
|
407
462
|
* once SIGTERM is out with SIGKILL armed there is nothing a second pass adds.
|
|
408
463
|
* The old `!child.killed` test deduplicated this as a side effect of being
|
|
409
|
-
* wrong; `signalled` keeps that property on purpose.
|
|
464
|
+
* wrong; `signalled` keeps that property on purpose. It lives on the process,
|
|
465
|
+
* not the session: only the live one is ever signalled, and a start retry
|
|
466
|
+
* (#872) replaces it only after the first exited unsignalled.
|
|
467
|
+
*
|
|
468
|
+
* Teardown also cancels a pending start retry, so no second server spawns
|
|
469
|
+
* after `end()`, `interrupt()` or the deadline.
|
|
410
470
|
*/
|
|
411
471
|
terminate() {
|
|
412
|
-
|
|
472
|
+
this.startRetry.abort();
|
|
473
|
+
const serve = this.serve;
|
|
474
|
+
if (serve.signalled || serve.hasExited())
|
|
413
475
|
return;
|
|
414
|
-
|
|
415
|
-
|
|
476
|
+
serve.signalled = true;
|
|
477
|
+
signalSession(serve.child, 'SIGTERM');
|
|
416
478
|
setTimeout(() => {
|
|
417
|
-
if (
|
|
479
|
+
if (serve.hasExited())
|
|
418
480
|
return;
|
|
419
|
-
|
|
481
|
+
signalSession(serve.child, 'SIGKILL');
|
|
420
482
|
}, KILL_GRACE_MS).unref?.();
|
|
421
483
|
}
|
|
422
484
|
// ---- server lifecycle ---------------------------------------------------
|
|
423
|
-
|
|
485
|
+
/** Spawn one `opencode serve` on a random high port and wire its lifecycle.
|
|
486
|
+
* A synchronous spawn throw is wrapped; an async spawn error is latched on
|
|
487
|
+
* the process record. */
|
|
488
|
+
spawnServe() {
|
|
489
|
+
// Random high port; the actual bound URL is read back from stdout.
|
|
490
|
+
const port = 40000 + Math.floor(Math.random() * 20000);
|
|
491
|
+
let child;
|
|
492
|
+
try {
|
|
493
|
+
child = spawnSessionLeader(this.bin, ['serve', '--hostname', '127.0.0.1', '--port', String(port)], {
|
|
494
|
+
cwd: this.spec.cwd,
|
|
495
|
+
env: buildChildEnv({ backend: 'opencode', extraEnv: ciOpenCodeEnv(this.spec) }),
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
catch (err) {
|
|
499
|
+
throw wrapSpawnError(err, this.bin);
|
|
500
|
+
}
|
|
501
|
+
const hasExited = trackChildExit(child);
|
|
502
|
+
boundOutputDrainAfterExit(child);
|
|
503
|
+
// `exit` settles process liveness, not its pipes. The shared 250ms drain
|
|
504
|
+
// bound prevents an inherited descendant pipe from retaining this session.
|
|
505
|
+
const outputDrained = Promise.all([child.stdout, child.stderr].map(stream => finished(stream, { cleanup: true }).catch(() => undefined)));
|
|
506
|
+
const exited = new Promise((resolve) => {
|
|
507
|
+
child.once('exit', () => resolve());
|
|
508
|
+
child.once('close', () => resolve());
|
|
509
|
+
});
|
|
510
|
+
const serve = {
|
|
511
|
+
child, hasExited, exited, outputDrained, port,
|
|
512
|
+
stderrChunks: [], spawnFailed: null, signalled: false,
|
|
513
|
+
};
|
|
514
|
+
child.on('error', (err) => {
|
|
515
|
+
serve.spawnFailed = wrapSpawnError(err, this.bin);
|
|
516
|
+
});
|
|
517
|
+
child.stderr.setEncoding('utf8');
|
|
518
|
+
child.stderr.on('data', (chunk) => serve.stderrChunks.push(chunk));
|
|
519
|
+
return serve;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Wait for the server URL, retrying once — after `SERVE_START_RETRY_DELAY_MS`,
|
|
523
|
+
* on a fresh port — when the process exited before it printed one (#872).
|
|
524
|
+
* Never retried: a spawn error (the process never ran), the no-URL timeout
|
|
525
|
+
* (it falls back to the requested port), and a session already torn down.
|
|
526
|
+
*/
|
|
527
|
+
async startServer() {
|
|
528
|
+
try {
|
|
529
|
+
return await this.waitForServerUrl(this.serve);
|
|
530
|
+
}
|
|
531
|
+
catch (err) {
|
|
532
|
+
const first = this.serve;
|
|
533
|
+
if (!(err instanceof ServeExitedBeforeListening))
|
|
534
|
+
throw err;
|
|
535
|
+
// Settle the process first: an ENOENT `error` and late stderr both land
|
|
536
|
+
// around `exit`, and the retry decision and its note need them.
|
|
537
|
+
await first.exited;
|
|
538
|
+
await first.outputDrained;
|
|
539
|
+
if (first.spawnFailed || first.child.pid === undefined || !this.serverOpen || this.timedOut)
|
|
540
|
+
throw err;
|
|
541
|
+
const detail = summarizeRunnerStderr(first.stderrChunks.join(''));
|
|
542
|
+
this.emit({
|
|
543
|
+
type: 'note',
|
|
544
|
+
message: `opencode serve exited before listening (${describeExit(first)}); retrying once${detail ? ` — ${detail}` : ''}`,
|
|
545
|
+
});
|
|
546
|
+
try {
|
|
547
|
+
await delay(SERVE_START_RETRY_DELAY_MS, undefined, { signal: this.startRetry.signal });
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
// Torn down during the pause: report the first start's failure as before.
|
|
551
|
+
throw err;
|
|
552
|
+
}
|
|
553
|
+
if (this.startRetry.signal.aborted || !this.serverOpen || this.timedOut)
|
|
554
|
+
throw err;
|
|
555
|
+
let next;
|
|
556
|
+
try {
|
|
557
|
+
next = this.spawnServe();
|
|
558
|
+
}
|
|
559
|
+
catch (spawnErr) {
|
|
560
|
+
this.retrySpawnError = spawnErr instanceof Error ? spawnErr : new Error(String(spawnErr));
|
|
561
|
+
throw spawnErr;
|
|
562
|
+
}
|
|
563
|
+
this.serve = next;
|
|
564
|
+
if (next.child.pid !== undefined)
|
|
565
|
+
this.opts.onPidChange?.(next.child.pid);
|
|
566
|
+
try {
|
|
567
|
+
return await this.waitForServerUrl(next);
|
|
568
|
+
}
|
|
569
|
+
catch (retryErr) {
|
|
570
|
+
// Only an unprompted second early exit names both attempts; a retry
|
|
571
|
+
// torn down by the session reports through the ordinary paths.
|
|
572
|
+
if (retryErr instanceof ServeExitedBeforeListening && !next.signalled)
|
|
573
|
+
this.failedStart = first;
|
|
574
|
+
throw retryErr;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
waitForServerUrl(serve) {
|
|
579
|
+
const { child } = serve;
|
|
424
580
|
return new Promise((resolve, reject) => {
|
|
425
581
|
let buffer = '';
|
|
426
582
|
const timer = setTimeout(() => {
|
|
427
583
|
cleanup();
|
|
428
584
|
// Nothing parsed — try the port we asked for.
|
|
429
|
-
resolve(`http://127.0.0.1:${
|
|
430
|
-
},
|
|
585
|
+
resolve(`http://127.0.0.1:${serve.port}`);
|
|
586
|
+
}, this.serverStartTimeoutMs);
|
|
431
587
|
timer.unref?.();
|
|
432
588
|
const onData = (chunk) => {
|
|
433
589
|
buffer += chunk;
|
|
@@ -439,36 +595,92 @@ class OpencodeSession {
|
|
|
439
595
|
};
|
|
440
596
|
const onExit = () => {
|
|
441
597
|
cleanup();
|
|
442
|
-
reject(new
|
|
598
|
+
reject(new ServeExitedBeforeListening());
|
|
443
599
|
};
|
|
444
600
|
const cleanup = () => {
|
|
445
601
|
clearTimeout(timer);
|
|
446
|
-
|
|
447
|
-
|
|
602
|
+
child.stdout.off('data', onData);
|
|
603
|
+
child.off('exit', onExit);
|
|
448
604
|
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
605
|
+
child.stdout.setEncoding('utf8');
|
|
606
|
+
child.stdout.on('data', onData);
|
|
607
|
+
child.once('exit', onExit);
|
|
452
608
|
});
|
|
453
609
|
}
|
|
610
|
+
nativeDelegationPermission() {
|
|
611
|
+
return { permission: [{ permission: 'task', pattern: '*', action: 'deny' }] };
|
|
612
|
+
}
|
|
613
|
+
/** 404 → `'missing'`. Any other non-OK status fails bootstrap (no fallback). */
|
|
614
|
+
async tryGetSession(id) {
|
|
615
|
+
if (!this.baseUrl)
|
|
616
|
+
throw new Error('opencode server not ready');
|
|
617
|
+
const res = await fetch(`${this.baseUrl}/session/${encodeURIComponent(id)}`, { method: 'GET' });
|
|
618
|
+
if (res.status === 404)
|
|
619
|
+
return 'missing';
|
|
620
|
+
if (!res.ok) {
|
|
621
|
+
const detail = await res.text().catch(() => '');
|
|
622
|
+
throw new Error(`GET /session/${id} → ${res.status} ${detail.slice(0, 200)}`);
|
|
623
|
+
}
|
|
624
|
+
const text = await res.text();
|
|
625
|
+
if (!text)
|
|
626
|
+
return { id };
|
|
627
|
+
try {
|
|
628
|
+
const parsed = JSON.parse(text);
|
|
629
|
+
return isRecord(parsed) ? parsed : { id };
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
return { id };
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async attachSession(id, applyDelegationRule) {
|
|
636
|
+
this.sessionId = id;
|
|
637
|
+
this.emit({ type: 'session', sessionId: id });
|
|
638
|
+
this.emitUi((state) => opencodeSessionStarted(id, state));
|
|
639
|
+
if (applyDelegationRule) {
|
|
640
|
+
await this.http('PATCH', `/session/${encodeURIComponent(id)}`, this.nativeDelegationPermission());
|
|
641
|
+
}
|
|
642
|
+
}
|
|
454
643
|
async bootstrap() {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
644
|
+
const requested = this.spec.resume ? this.spec.sessionId : undefined;
|
|
645
|
+
let resumed = false;
|
|
646
|
+
if (requested) {
|
|
647
|
+
const existing = await this.tryGetSession(requested);
|
|
648
|
+
if (existing === 'missing') {
|
|
649
|
+
this.emit({
|
|
650
|
+
type: 'note',
|
|
651
|
+
message: `OpenCode session ${requested} no longer exists; the continuation runs in a fresh session without the earlier conversation.`,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
const applyDelegationRule = Boolean(this.spec.restrictNativeDelegation)
|
|
656
|
+
&& !hasNativeDelegationDeny(existing.permission);
|
|
657
|
+
await this.attachSession(stringField(existing, 'id') ?? requested, applyDelegationRule);
|
|
658
|
+
resumed = true;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (!resumed) {
|
|
662
|
+
const created = await this.http('POST', '/session', {
|
|
663
|
+
title: 'cezar task',
|
|
664
|
+
// OpenCode 1.18.33 /doc: session.create permission rules. The native task
|
|
665
|
+
// tool checks this permission; other agent/project permissions stay intact.
|
|
666
|
+
...(this.spec.restrictNativeDelegation ? this.nativeDelegationPermission() : {}),
|
|
667
|
+
});
|
|
668
|
+
const sessionId = stringField(created, 'id');
|
|
669
|
+
if (!sessionId)
|
|
670
|
+
throw new Error('opencode did not return a session id');
|
|
671
|
+
await this.attachSession(sessionId, false);
|
|
672
|
+
}
|
|
467
673
|
// The SSE subscription must be LIVE before the first prompt posts —
|
|
468
674
|
// events the server emits while the POST is in flight would otherwise be
|
|
469
675
|
// lost (a race this await closes; the bundled mock made it visible).
|
|
470
676
|
await this.consumeEvents();
|
|
471
|
-
|
|
677
|
+
// Lost-session fallback uses the full skill-inclusive prompt Continue
|
|
678
|
+
// stashed in resumeFallbackSystemPrompt; a true resume re-prepends only
|
|
679
|
+
// extra+handoff (the skill already lives in the thread).
|
|
680
|
+
const systemPrompt = !resumed && requested
|
|
681
|
+
? (this.spec.resumeFallbackSystemPrompt ?? this.spec.systemPrompt)
|
|
682
|
+
: this.spec.systemPrompt;
|
|
683
|
+
const first = prependSystemPrompt(systemPrompt, this.spec.userPrompt);
|
|
472
684
|
await this.prompt(first, 'opening');
|
|
473
685
|
if (this.refusedBeforeOpening)
|
|
474
686
|
queueMicrotask(() => {
|
|
@@ -485,8 +697,14 @@ class OpencodeSession {
|
|
|
485
697
|
// resume in FIFO order; a teardown (`finishTurn` runs on every exit
|
|
486
698
|
// path) releases them into the `serverOpen` check below.
|
|
487
699
|
const generation = this.queuedPromptGeneration;
|
|
700
|
+
const humanWaiter = origin === 'human';
|
|
701
|
+
if (humanWaiter)
|
|
702
|
+
this.humanPromptWaiters += 1;
|
|
488
703
|
while (this.turnActive)
|
|
489
704
|
await this.turnFinished;
|
|
705
|
+
if (humanWaiter && generation === this.queuedPromptGeneration) {
|
|
706
|
+
this.humanPromptWaiters = Math.max(0, this.humanPromptWaiters - 1);
|
|
707
|
+
}
|
|
490
708
|
if (generation !== this.queuedPromptGeneration)
|
|
491
709
|
return;
|
|
492
710
|
// A queued prompt may have started waiting before the preceding idle armed
|
|
@@ -524,21 +742,21 @@ class OpencodeSession {
|
|
|
524
742
|
this.interrupt();
|
|
525
743
|
}
|
|
526
744
|
}
|
|
745
|
+
else if (origin === 'opening') {
|
|
746
|
+
this.emit({ type: 'note', message: `opencode: prompt failed: ${message}` });
|
|
747
|
+
// A dropped ACK can be the first sign of a process crash. Retain HTTP
|
|
748
|
+
// rejection details, but let settlement choose one error before turn-end.
|
|
749
|
+
this.openingPromptFailed = true;
|
|
750
|
+
this.exitFailure = `opencode: ${message}`;
|
|
751
|
+
this.interrupt();
|
|
752
|
+
}
|
|
527
753
|
else {
|
|
528
754
|
this.emit({ type: 'note', message: `opencode: prompt failed: ${message}` });
|
|
529
|
-
if (origin === 'opening') {
|
|
530
|
-
// Opening failure was always fatal. Report it before the synthetic
|
|
531
|
-
// boundary, and do not emit it again from the bootstrap result catch.
|
|
532
|
-
this.openingPromptFailed = true;
|
|
533
|
-
if (!this.timedOut)
|
|
534
|
-
this.emit({ type: 'error', message: `opencode: ${message}` });
|
|
535
|
-
this.interrupt();
|
|
536
|
-
}
|
|
537
755
|
}
|
|
538
|
-
if (!this.providerErrorPending)
|
|
756
|
+
if (!this.providerErrorPending && origin !== 'opening')
|
|
539
757
|
this.finishTurn();
|
|
540
|
-
// Bootstrap still rejects; its result
|
|
541
|
-
//
|
|
758
|
+
// Bootstrap still rejects; its result path reports the latched opening
|
|
759
|
+
// failure. Human follow-ups keep their nonfatal note and turn boundary.
|
|
542
760
|
throw err;
|
|
543
761
|
}
|
|
544
762
|
}
|
|
@@ -701,10 +919,9 @@ class OpencodeSession {
|
|
|
701
919
|
// note: the turn's remaining output is lost, so the step must record a
|
|
702
920
|
// failure (run.ts classifies from v1 `error` only), never pass as an
|
|
703
921
|
// empty success. Then close the turn (flushing what did arrive) and end
|
|
704
|
-
// the session; "Continue"
|
|
922
|
+
// the session; "Continue" GETs this session id on the next server.
|
|
705
923
|
if (this.serverOpen && !this.sse.signal.aborted) {
|
|
706
|
-
this.
|
|
707
|
-
this.finishTurn();
|
|
924
|
+
this.exitFailure = 'opencode: event stream closed unexpectedly';
|
|
708
925
|
this.end();
|
|
709
926
|
}
|
|
710
927
|
}
|
|
@@ -725,7 +942,7 @@ class OpencodeSession {
|
|
|
725
942
|
}
|
|
726
943
|
this.emitUi((state) => mapOpencodeEvent(evt, state));
|
|
727
944
|
const sid = stringField(evt.properties ?? {}, 'sessionID');
|
|
728
|
-
if (evt.type === 'session.error' && (sid === undefined || sid === this.sessionId) && this.agentRequest) {
|
|
945
|
+
if (evt.type === 'session.error' && !opencodeSkillWarning(evt.properties ?? {}) && (sid === undefined || sid === this.sessionId) && this.agentRequest) {
|
|
729
946
|
// HTTP and SSE are independent sockets: the error can overtake a positive
|
|
730
947
|
// prompt ACK. Let that exact request settle before v1 makes RunManager
|
|
731
948
|
// interrupt it. Keep later SSE idle frames behind the same barrier.
|
|
@@ -799,7 +1016,12 @@ class OpencodeSession {
|
|
|
799
1016
|
this.finishTurn();
|
|
800
1017
|
}
|
|
801
1018
|
else if (type === 'session.error') {
|
|
802
|
-
|
|
1019
|
+
const warning = opencodeSkillWarning(props);
|
|
1020
|
+
if (warning) {
|
|
1021
|
+
this.emit({ type: 'note', message: warning });
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
// The wire's failure signal, now that the prompt POST returns
|
|
803
1025
|
// before the turn runs: forward it to v1, whose `error` events are what
|
|
804
1026
|
// run.ts records failed steps from — dropped, the terminal idle would
|
|
805
1027
|
// file a provider/auth failure as a successful step. The idle that
|
|
@@ -1125,6 +1347,15 @@ function clippedString(value, max) {
|
|
|
1125
1347
|
function isRecord(value) {
|
|
1126
1348
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1127
1349
|
}
|
|
1350
|
+
/** OpenCode 1.18.33 PATCHes permission by append (no replace, no dedupe). */
|
|
1351
|
+
function hasNativeDelegationDeny(permission) {
|
|
1352
|
+
if (!Array.isArray(permission))
|
|
1353
|
+
return false;
|
|
1354
|
+
return permission.some((rule) => isRecord(rule)
|
|
1355
|
+
&& rule.permission === 'task'
|
|
1356
|
+
&& rule.pattern === '*'
|
|
1357
|
+
&& rule.action === 'deny');
|
|
1358
|
+
}
|
|
1128
1359
|
function sleep(ms) {
|
|
1129
1360
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1130
1361
|
}
|
|
@@ -1183,6 +1414,16 @@ function safeStringify(value) {
|
|
|
1183
1414
|
return String(value);
|
|
1184
1415
|
}
|
|
1185
1416
|
}
|
|
1417
|
+
/** `code N` or `signal S` for a server process that exited. */
|
|
1418
|
+
function describeExit(serve) {
|
|
1419
|
+
const { exitCode, signalCode } = serve.child;
|
|
1420
|
+
return exitCode !== null ? `code ${exitCode}` : `signal ${signalCode}`;
|
|
1421
|
+
}
|
|
1422
|
+
/** `code N — <stderr summary>` for one start that exited before listening. */
|
|
1423
|
+
function describeEarlyExit(serve) {
|
|
1424
|
+
const detail = summarizeRunnerStderr(serve.stderrChunks.join(''));
|
|
1425
|
+
return `${describeExit(serve)}${detail ? ` — ${detail}` : ''}`;
|
|
1426
|
+
}
|
|
1186
1427
|
function wrapSpawnError(err, bin) {
|
|
1187
1428
|
const code = err?.code;
|
|
1188
1429
|
if (code === 'ENOENT') {
|