@wjarka/cezarion 0.16.0 → 0.17.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/LICENSE +1 -0
- package/README.md +594 -207
- package/dist/agent-config/account-identity.d.ts +3 -19
- package/dist/agent-config/account-identity.js +8 -5
- package/dist/agent-config/account-identity.js.map +1 -1
- package/dist/agent-config/catalog.d.ts +3 -14
- package/dist/agent-config/catalog.js.map +1 -1
- package/dist/agent-config/files.d.ts +2 -6
- package/dist/agent-config/files.js +2 -6
- package/dist/agent-config/files.js.map +1 -1
- package/dist/agent-config/model-settings/types.d.ts +1 -1
- package/dist/agent-config/seed.d.ts +11 -3
- package/dist/agent-config/seed.js +52 -48
- package/dist/agent-config/seed.js.map +1 -1
- package/dist/agent-config/service.js +3 -2
- package/dist/agent-config/service.js.map +1 -1
- package/dist/agent-config/validate.d.ts +1 -4
- package/dist/agent-config/validate.js.map +1 -1
- package/dist/application-update/helper.d.ts +2 -2
- package/dist/application-update/helper.js +7 -12
- package/dist/application-update/helper.js.map +1 -1
- package/dist/application-update/launcher.d.ts +4 -0
- package/dist/application-update/launcher.js +18 -12
- package/dist/application-update/launcher.js.map +1 -1
- package/dist/application-update/service.d.ts +3 -3
- package/dist/application-update/service.js +1 -1
- package/dist/application-update/service.js.map +1 -1
- package/dist/archive-on-merge-setting.d.ts +15 -0
- package/dist/archive-on-merge-setting.js +14 -0
- package/dist/archive-on-merge-setting.js.map +1 -0
- package/dist/artifacts/cli.d.ts +8 -0
- package/dist/artifacts/cli.js +21 -2
- package/dist/artifacts/cli.js.map +1 -1
- package/dist/artifacts/resolve.js +2 -1
- package/dist/artifacts/resolve.js.map +1 -1
- package/dist/artifacts/store.js.map +1 -1
- package/dist/automations/automation-cli.d.ts +59 -0
- package/dist/automations/automation-cli.js +509 -0
- package/dist/automations/automation-cli.js.map +1 -0
- package/dist/automations/builtin-skill.d.ts +7 -0
- package/dist/automations/builtin-skill.js +14 -0
- package/dist/automations/builtin-skill.js.map +1 -0
- package/dist/automations/github-baseline.d.ts +3 -0
- package/dist/automations/github-baseline.js +17 -0
- package/dist/automations/github-baseline.js.map +1 -0
- package/dist/automations/github-poller.d.ts +23 -4
- package/dist/automations/github-poller.js +157 -88
- package/dist/automations/github-poller.js.map +1 -1
- package/dist/automations/prompts.d.ts +12 -0
- package/dist/automations/prompts.js +160 -0
- package/dist/automations/prompts.js.map +1 -0
- package/dist/automations/schedule-runner.d.ts +43 -24
- package/dist/automations/schedule-runner.js +207 -20
- package/dist/automations/schedule-runner.js.map +1 -1
- package/dist/automations/scheduler.d.ts +10 -2
- package/dist/automations/scheduler.js +200 -54
- package/dist/automations/scheduler.js.map +1 -1
- package/dist/automations/service.d.ts +777 -0
- package/dist/automations/service.js +444 -0
- package/dist/automations/service.js.map +1 -0
- package/dist/automations/store.d.ts +17 -7
- package/dist/automations/store.js +88 -17
- package/dist/automations/store.js.map +1 -1
- package/dist/automations/task-template.d.ts +7 -2
- package/dist/automations/task-template.js +93 -44
- package/dist/automations/task-template.js.map +1 -1
- package/dist/automations/tool-adapter.d.ts +6 -0
- package/dist/automations/tool-adapter.js +108 -0
- package/dist/automations/tool-adapter.js.map +1 -0
- package/dist/automations/types.d.ts +1 -405
- package/dist/automations/types.js +1 -218
- package/dist/automations/types.js.map +1 -1
- package/dist/autosave-cleanup.d.ts +26 -0
- package/dist/autosave-cleanup.js +87 -0
- package/dist/autosave-cleanup.js.map +1 -0
- package/dist/autosave-git.d.ts +3 -0
- package/dist/autosave-git.js +67 -35
- package/dist/autosave-git.js.map +1 -1
- package/dist/autosave-group.d.ts +8 -0
- package/dist/autosave-group.js +61 -0
- package/dist/autosave-group.js.map +1 -0
- package/dist/autosave-holders.d.ts +31 -4
- package/dist/autosave-holders.js +92 -7
- package/dist/autosave-holders.js.map +1 -1
- package/dist/autosave-windows.d.ts +14 -0
- package/dist/autosave-windows.js +36 -2
- package/dist/autosave-windows.js.map +1 -1
- package/dist/ci-wait/client.d.ts +20 -3
- package/dist/ci-wait/client.js +40 -44
- package/dist/ci-wait/client.js.map +1 -1
- package/dist/ci-wait/controller.d.ts +622 -36
- package/dist/ci-wait/controller.js +152 -20
- package/dist/ci-wait/controller.js.map +1 -1
- package/dist/ci-wait/errors.d.ts +19 -0
- package/dist/ci-wait/errors.js +180 -2
- package/dist/ci-wait/errors.js.map +1 -1
- package/dist/ci-wait/github.d.ts +25 -1
- package/dist/ci-wait/github.js +76 -9
- package/dist/ci-wait/github.js.map +1 -1
- package/dist/ci-wait/mcp.d.ts +0 -1
- package/dist/ci-wait/mcp.js +0 -2
- package/dist/ci-wait/mcp.js.map +1 -1
- package/dist/ci-wait/supervisor.d.ts +7 -0
- package/dist/ci-wait/supervisor.js +113 -17
- package/dist/ci-wait/supervisor.js.map +1 -1
- package/dist/ci-wait/tools.d.ts +536 -16
- package/dist/ci-wait/tools.js +165 -20
- package/dist/ci-wait/tools.js.map +1 -1
- package/dist/config.d.ts +3 -17
- package/dist/config.js +4 -60
- package/dist/config.js.map +1 -1
- package/dist/contract/active-provider.d.ts +12 -0
- package/dist/contract/agent-profiles.d.ts +2 -10
- package/dist/contract/ask-schema.d.ts +22 -4
- package/dist/contract/ask.d.ts +6 -4
- package/dist/contract/attachments.d.ts +35 -0
- package/dist/contract/attention.d.ts +46 -37
- package/dist/contract/automation-requests.d.ts +308 -0
- package/dist/contract/automation-schedule.d.ts +40 -11
- package/dist/contract/automation-storage.d.ts +512 -0
- package/dist/contract/automations.d.ts +421 -40
- package/dist/contract/ci-wait.d.ts +41 -4
- package/dist/contract/config-requests.d.ts +36 -0
- package/dist/contract/conversations.d.ts +24 -10
- package/dist/contract/defined.d.ts +8 -0
- package/dist/contract/delegation.d.ts +312 -8
- package/dist/contract/events.d.ts +24 -3
- package/dist/contract/github-requests.d.ts +26 -0
- package/dist/contract/github.d.ts +60 -27
- package/dist/contract/handoff.d.ts +43 -0
- package/dist/contract/health.d.ts +2 -2
- package/dist/contract/image-path.d.ts +6 -0
- package/dist/contract/index.d.ts +31 -0
- package/dist/contract/index.js +3756 -1525
- package/dist/contract/live-coordination.d.ts +158 -0
- package/dist/contract/live-limits.d.ts +8 -0
- package/dist/contract/live.d.ts +123 -0
- package/dist/contract/pending-ask.d.ts +58 -0
- package/dist/contract/permissions.d.ts +54 -0
- package/dist/contract/preview.d.ts +33 -0
- package/dist/contract/project-limits.d.ts +33 -0
- package/dist/contract/projects.d.ts +486 -20
- package/dist/contract/provider-requests.d.ts +77 -0
- package/dist/contract/query-requests.d.ts +52 -0
- package/dist/contract/repo-requests.d.ts +12 -0
- package/dist/contract/run-labels.d.ts +8 -0
- package/dist/contract/run-limits.d.ts +8 -0
- package/dist/contract/run-predicates.d.ts +32 -0
- package/dist/contract/run-requests.d.ts +33 -0
- package/dist/contract/run-search.d.ts +193 -0
- package/dist/contract/runs.d.ts +2232 -1894
- package/dist/contract/scheduled-tasks.d.ts +187 -0
- package/dist/contract/skills.d.ts +22 -772
- package/dist/contract/team-skill-ref.d.ts +10 -0
- package/dist/{core → contract}/tool-display.d.ts +4 -10
- package/dist/contract/ui-state-requests.d.ts +60 -0
- package/dist/contract/version.d.ts +3 -0
- package/dist/contract/voice.d.ts +213 -0
- package/dist/contract/waits.d.ts +1204 -0
- package/dist/contract/workflow-file.d.ts +37 -0
- package/dist/contract/workflow-requests.d.ts +7 -0
- package/dist/contract/workflows.d.ts +1 -2
- package/dist/contract/workspace.d.ts +34 -82
- package/dist/contract/zoned-time.d.ts +2 -11
- package/dist/core/agent-env.d.ts +4 -16
- package/dist/core/agent-env.js +14 -16
- package/dist/core/agent-env.js.map +1 -1
- package/dist/core/agent-profiles.d.ts +28 -40
- package/dist/core/agent-profiles.js +56 -51
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-runner.d.ts +88 -44
- package/dist/core/agent-runner.js +17 -18
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/ask.d.ts +3 -0
- package/dist/core/ask.js +12 -0
- package/dist/core/ask.js.map +1 -1
- package/dist/core/backend-detect.js +16 -2
- package/dist/core/backend-detect.js.map +1 -1
- package/dist/core/claude-cli-runner.d.ts +23 -4
- package/dist/core/claude-cli-runner.js +280 -76
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/claude-model-catalog.d.ts +2 -10
- package/dist/core/claude-model-catalog.js +2 -10
- package/dist/core/claude-model-catalog.js.map +1 -1
- package/dist/core/claude-ui-mapper.d.ts +2 -2
- package/dist/core/claude-ui-mapper.js +94 -2
- package/dist/core/claude-ui-mapper.js.map +1 -1
- package/dist/core/codex-app-server-runner.d.ts +3 -4
- package/dist/core/codex-app-server-runner.js +146 -51
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.d.ts +4 -3
- package/dist/core/codex-app-server-transport.js +11 -7
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/codex-approvals.d.ts +12 -0
- package/dist/core/codex-approvals.js +161 -0
- package/dist/core/codex-approvals.js.map +1 -0
- package/dist/core/codex-permissions.d.ts +1 -1
- package/dist/core/codex-ui-mapper.js +45 -1
- package/dist/core/codex-ui-mapper.js.map +1 -1
- package/dist/core/cursor-acp-runner.d.ts +1 -1
- package/dist/core/cursor-acp-runner.js +39 -15
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/core/cursor-cli-flags.d.ts +19 -0
- package/dist/core/cursor-cli-flags.js +47 -0
- package/dist/core/cursor-cli-flags.js.map +1 -0
- package/dist/core/cursor-model-catalog.d.ts +22 -0
- package/dist/core/cursor-model-catalog.js +53 -24
- package/dist/core/cursor-model-catalog.js.map +1 -1
- package/dist/core/cursor-print-capabilities.d.ts +16 -0
- package/dist/core/cursor-print-capabilities.js +141 -0
- package/dist/core/cursor-print-capabilities.js.map +1 -0
- package/dist/core/cursor-print-process.d.ts +31 -0
- package/dist/core/cursor-print-process.js +117 -0
- package/dist/core/cursor-print-process.js.map +1 -0
- package/dist/core/cursor-print-runner.d.ts +31 -0
- package/dist/core/cursor-print-runner.js +555 -0
- package/dist/core/cursor-print-runner.js.map +1 -0
- package/dist/core/cursor-print-tool-catalog.d.ts +10 -0
- package/dist/core/cursor-print-tool-catalog.js +91 -0
- package/dist/core/cursor-print-tool-catalog.js.map +1 -0
- package/dist/core/cursor-print-ui-mapper.d.ts +47 -0
- package/dist/core/cursor-print-ui-mapper.js +553 -0
- package/dist/core/cursor-print-ui-mapper.js.map +1 -0
- package/dist/core/cursor-runner.d.ts +24 -0
- package/dist/core/cursor-runner.js +149 -0
- package/dist/core/cursor-runner.js.map +1 -0
- package/dist/core/cursor-ui-mapper.js +5 -2
- package/dist/core/cursor-ui-mapper.js.map +1 -1
- package/dist/core/defined.d.ts +1 -0
- package/dist/core/defined.js +2 -0
- package/dist/core/defined.js.map +1 -0
- package/dist/core/hook-items.d.ts +14 -0
- package/dist/core/hook-items.js +13 -0
- package/dist/core/hook-items.js.map +1 -0
- package/dist/core/host-model-catalog.d.ts +2 -9
- package/dist/core/host-model-catalog.js +2 -9
- package/dist/core/host-model-catalog.js.map +1 -1
- package/dist/core/managed-session.js +15 -3
- package/dist/core/managed-session.js.map +1 -1
- package/dist/core/model-identity.d.ts +4 -21
- package/dist/core/model-identity.js.map +1 -1
- package/dist/core/model-presets.d.ts +2 -13
- package/dist/core/model-presets.js +2 -13
- package/dist/core/model-presets.js.map +1 -1
- package/dist/core/omp-model-catalog.js.map +1 -1
- package/dist/core/omp-runner.d.ts +6 -7
- package/dist/core/omp-runner.js +92 -33
- package/dist/core/omp-runner.js.map +1 -1
- package/dist/core/omp-ui-mapper.js +1 -1
- package/dist/core/omp-ui-mapper.js.map +1 -1
- package/dist/core/opencode-model-catalog.js.map +1 -1
- package/dist/core/opencode-permissions.d.ts +52 -0
- package/dist/core/opencode-permissions.js +101 -0
- package/dist/core/opencode-permissions.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +10 -9
- package/dist/core/opencode-server-runner.js +286 -28
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/opencode-session-error.d.ts +15 -7
- package/dist/core/opencode-session-error.js +25 -9
- package/dist/core/opencode-session-error.js.map +1 -1
- package/dist/core/opencode-ui-mapper.js +2 -2
- package/dist/core/opencode-ui-mapper.js.map +1 -1
- package/dist/core/pi-model-catalog.js.map +1 -1
- package/dist/core/pi-runner.d.ts +12 -4
- package/dist/core/pi-runner.js +171 -63
- package/dist/core/pi-runner.js.map +1 -1
- package/dist/core/pi-ui-mapper.js +1 -1
- package/dist/core/pi-ui-mapper.js.map +1 -1
- package/dist/core/process-usage.d.ts +5 -0
- package/dist/core/process-usage.js +24 -4
- package/dist/core/process-usage.js.map +1 -1
- package/dist/core/provider-auth.d.ts +16 -107
- package/dist/core/provider-auth.js +16 -132
- package/dist/core/provider-auth.js.map +1 -1
- package/dist/core/runner-factory.d.ts +4 -1
- package/dist/core/runner-factory.js +3 -3
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/runner-runtime.d.ts +22 -0
- package/dist/core/runner-runtime.js +66 -5
- package/dist/core/runner-runtime.js.map +1 -1
- package/dist/core/runner-tool-grants.d.ts +21 -0
- package/dist/core/runner-tool-grants.js +64 -0
- package/dist/core/runner-tool-grants.js.map +1 -0
- package/dist/core/secret-redaction.d.ts +3 -20
- package/dist/core/secret-redaction.js +3 -20
- package/dist/core/secret-redaction.js.map +1 -1
- package/dist/core/session-process.d.ts +22 -2
- package/dist/core/session-process.js +170 -12
- package/dist/core/session-process.js.map +1 -1
- package/dist/core/shell-env.d.ts +2 -15
- package/dist/core/shell-env.js +2 -15
- package/dist/core/shell-env.js.map +1 -1
- package/dist/core/ui-events.d.ts +60 -47
- package/dist/core/ui-events.js +0 -23
- package/dist/core/ui-events.js.map +1 -1
- package/dist/core/usage-limit.js.map +1 -1
- package/dist/data-gitignore.js +3 -1
- package/dist/data-gitignore.js.map +1 -1
- package/dist/delegation/capacity.d.ts +4 -8
- package/dist/delegation/capacity.js +4 -8
- package/dist/delegation/capacity.js.map +1 -1
- package/dist/delegation/cli.js +123 -21
- package/dist/delegation/cli.js.map +1 -1
- package/dist/delegation/context.d.ts +19 -3
- package/dist/delegation/context.js +217 -22
- package/dist/delegation/context.js.map +1 -1
- package/dist/delegation/conversations.d.ts +4 -3
- package/dist/delegation/conversations.js +7 -5
- package/dist/delegation/conversations.js.map +1 -1
- package/dist/delegation/destroy-observation.d.ts +1 -1
- package/dist/delegation/destroy-observation.js.map +1 -1
- package/dist/delegation/effort.js.map +1 -1
- package/dist/delegation/execution-identity.d.ts +1 -1
- package/dist/delegation/execution-identity.js +41 -7
- package/dist/delegation/execution-identity.js.map +1 -1
- package/dist/delegation/idle-test-app.d.ts +4 -0
- package/dist/delegation/idle-test-app.js +174 -0
- package/dist/delegation/idle-test-app.js.map +1 -0
- package/dist/delegation/input.js.map +1 -1
- package/dist/delegation/park-notice.d.ts +42 -0
- package/dist/delegation/park-notice.js +71 -0
- package/dist/delegation/park-notice.js.map +1 -0
- package/dist/delegation/policy.d.ts +2 -0
- package/dist/delegation/policy.js +9 -1
- package/dist/delegation/policy.js.map +1 -1
- package/dist/delegation/process-liveness.d.ts +10 -15
- package/dist/delegation/process-liveness.js +11 -35
- package/dist/delegation/process-liveness.js.map +1 -1
- package/dist/delegation/provision.js +21 -19
- package/dist/delegation/provision.js.map +1 -1
- package/dist/delegation/questions.d.ts +3 -1
- package/dist/delegation/questions.js +10 -5
- package/dist/delegation/questions.js.map +1 -1
- package/dist/delegation/readiness.d.ts +2 -2
- package/dist/delegation/results.d.ts +4 -1
- package/dist/delegation/results.js +49 -18
- package/dist/delegation/results.js.map +1 -1
- package/dist/delegation/retry-backoff.d.ts +8 -0
- package/dist/delegation/retry-backoff.js +8 -0
- package/dist/delegation/retry-backoff.js.map +1 -1
- package/dist/delegation/routes.d.ts +114 -30
- package/dist/delegation/routes.js +4 -1
- package/dist/delegation/routes.js.map +1 -1
- package/dist/delegation/scratch-cleanup.d.ts +4 -5
- package/dist/delegation/scratch-cleanup.js +10 -8
- package/dist/delegation/scratch-cleanup.js.map +1 -1
- package/dist/delegation/service.d.ts +12 -4
- package/dist/delegation/service.js +76 -28
- package/dist/delegation/service.js.map +1 -1
- package/dist/delegation/transport.d.ts +154 -70
- package/dist/delegation/wait.d.ts +11 -0
- package/dist/delegation/wait.js +24 -0
- package/dist/delegation/wait.js.map +1 -1
- package/dist/delegation/windows-test-app.d.ts +5 -0
- package/dist/delegation/windows-test-app.js +63 -0
- package/dist/delegation/windows-test-app.js.map +1 -0
- package/dist/delegation/windows-workspace-identity.d.ts +3 -0
- package/dist/delegation/windows-workspace-identity.js +22 -0
- package/dist/delegation/windows-workspace-identity.js.map +1 -0
- package/dist/delegation/workspace.d.ts +5 -10
- package/dist/delegation/workspace.js +136 -75
- package/dist/delegation/workspace.js.map +1 -1
- package/dist/detached-process.d.ts +103 -0
- package/dist/detached-process.js +301 -0
- package/dist/detached-process.js.map +1 -0
- package/dist/discovery/catalog.d.ts +2 -1
- package/dist/discovery/catalog.js.map +1 -1
- package/dist/discovery/cli.js +3 -1
- package/dist/discovery/cli.js.map +1 -1
- package/dist/git-diff-base.d.ts +6 -61
- package/dist/git-diff-base.js +6 -70
- package/dist/git-diff-base.js.map +1 -1
- package/dist/git-index-copy.d.ts +4 -0
- package/dist/git-index-copy.js +10 -0
- package/dist/git-index-copy.js.map +1 -0
- package/dist/git-worktree-lock.d.ts +2 -4
- package/dist/git-worktree-lock.js +2 -4
- package/dist/git-worktree-lock.js.map +1 -1
- package/dist/git-worktree-release.d.ts +2 -2
- package/dist/git-worktree-release.js.map +1 -1
- package/dist/git-worktree.d.ts +47 -76
- package/dist/git-worktree.js +240 -94
- package/dist/git-worktree.js.map +1 -1
- package/dist/handoff.d.ts +51 -41
- package/dist/handoff.js +130 -78
- package/dist/handoff.js.map +1 -1
- package/dist/index.js +60 -39
- package/dist/index.js.map +1 -1
- package/dist/merge-observations.d.ts +13 -0
- package/dist/merge-observations.js +55 -0
- package/dist/merge-observations.js.map +1 -0
- package/dist/open-url.js +4 -14
- package/dist/open-url.js.map +1 -1
- package/dist/paths.d.ts +10 -13
- package/dist/paths.js +36 -17
- package/dist/paths.js.map +1 -1
- package/dist/planner.d.ts +2 -1
- package/dist/planner.js +9 -6
- package/dist/planner.js.map +1 -1
- package/dist/preview/agent-address.d.ts +34 -0
- package/dist/preview/agent-address.js +51 -0
- package/dist/preview/agent-address.js.map +1 -0
- package/dist/preview/agent-marker.d.ts +17 -0
- package/dist/preview/agent-marker.js +71 -0
- package/dist/preview/agent-marker.js.map +1 -0
- package/dist/preview/cdp-proxy.d.ts +54 -0
- package/dist/preview/cdp-proxy.js +151 -0
- package/dist/preview/cdp-proxy.js.map +1 -0
- package/dist/preview/cdp.d.ts +39 -3
- package/dist/preview/cdp.js +155 -57
- package/dist/preview/cdp.js.map +1 -1
- package/dist/preview/chromium.d.ts +18 -6
- package/dist/preview/chromium.js +112 -25
- package/dist/preview/chromium.js.map +1 -1
- package/dist/preview/dev-server.d.ts +1 -1
- package/dist/preview/dev-server.js +20 -44
- package/dist/preview/dev-server.js.map +1 -1
- package/dist/preview/host.d.ts +70 -3
- package/dist/preview/host.js +314 -28
- package/dist/preview/host.js.map +1 -1
- package/dist/preview/registration.d.ts +10 -5
- package/dist/preview/registration.js +2 -0
- package/dist/preview/registration.js.map +1 -1
- package/dist/preview/session.d.ts +2 -0
- package/dist/preview/session.js +10 -0
- package/dist/preview/session.js.map +1 -1
- package/dist/process-wait/exit-file.d.ts +29 -0
- package/dist/process-wait/exit-file.js +67 -0
- package/dist/process-wait/exit-file.js.map +1 -0
- package/dist/process-wait/supervisor.d.ts +30 -0
- package/dist/process-wait/supervisor.js +71 -0
- package/dist/process-wait/supervisor.js.map +1 -0
- package/dist/release/snapshot.d.ts +6 -6
- package/dist/runs/agent-tmpdir.d.ts +51 -36
- package/dist/runs/agent-tmpdir.js +389 -132
- package/dist/runs/agent-tmpdir.js.map +1 -1
- package/dist/runs/auto-name.d.ts +6 -6
- package/dist/runs/auto-name.js +1 -1
- package/dist/runs/auto-name.js.map +1 -1
- package/dist/runs/client-request.d.ts +3 -3
- package/dist/runs/client-request.js +3 -3
- package/dist/runs/client-request.js.map +1 -1
- package/dist/runs/delegation-state.d.ts +11 -2
- package/dist/runs/delegation-state.js.map +1 -1
- package/dist/runs/event-history-agent-context.d.ts +14 -0
- package/dist/runs/event-history-agent-context.js +105 -0
- package/dist/runs/event-history-agent-context.js.map +1 -0
- package/dist/runs/event-history-context.d.ts +15 -0
- package/dist/runs/event-history-context.js +109 -0
- package/dist/runs/event-history-context.js.map +1 -0
- package/dist/runs/event-history.d.ts +4 -1
- package/dist/runs/event-history.js +29 -130
- package/dist/runs/event-history.js.map +1 -1
- package/dist/runs/history-stream.d.ts +2 -0
- package/dist/runs/history-stream.js +62 -0
- package/dist/runs/history-stream.js.map +1 -0
- package/dist/runs/legacy-index.d.ts +6 -19
- package/dist/runs/legacy-index.js +6 -19
- package/dist/runs/legacy-index.js.map +1 -1
- package/dist/runs/live-item-snapshots.d.ts +13 -0
- package/dist/runs/live-item-snapshots.js +79 -0
- package/dist/runs/live-item-snapshots.js.map +1 -0
- package/dist/runs/loaded-run-healing.d.ts +3 -0
- package/dist/runs/loaded-run-healing.js +17 -0
- package/dist/runs/loaded-run-healing.js.map +1 -0
- package/dist/runs/outcome-commit.d.ts +98 -0
- package/dist/runs/outcome-commit.js +132 -0
- package/dist/runs/outcome-commit.js.map +1 -0
- package/dist/runs/pull-requests.d.ts +14 -0
- package/dist/runs/pull-requests.js +96 -0
- package/dist/runs/pull-requests.js.map +1 -0
- package/dist/runs/raw-record.d.ts +3 -15
- package/dist/runs/raw-record.js +3 -15
- package/dist/runs/raw-record.js.map +1 -1
- package/dist/runs/retention.d.ts +2 -2
- package/dist/runs/retention.js +7 -0
- package/dist/runs/retention.js.map +1 -1
- package/dist/runs/review-gate.d.ts +1 -1
- package/dist/runs/review-gate.js.map +1 -1
- package/dist/runs/run-claims.d.ts +2 -9
- package/dist/runs/run-claims.js.map +1 -1
- package/dist/runs/run-database.d.ts +29 -34
- package/dist/runs/run-database.js +124 -46
- package/dist/runs/run-database.js.map +1 -1
- package/dist/runs/run-index.d.ts +6 -25
- package/dist/runs/run-index.js +25 -38
- package/dist/runs/run-index.js.map +1 -1
- package/dist/runs/run-row.d.ts +5 -34
- package/dist/runs/run-row.js +13 -38
- package/dist/runs/run-row.js.map +1 -1
- package/dist/runs/store.d.ts +1597 -744
- package/dist/runs/store.js +1231 -739
- package/dist/runs/store.js.map +1 -1
- package/dist/runs/task-markers.d.ts +1 -0
- package/dist/runs/task-markers.js +5 -1
- package/dist/runs/task-markers.js.map +1 -1
- package/dist/runs/task-refs.d.ts +3 -3
- package/dist/runs/transcript-facts-fold.d.ts +64 -0
- package/dist/runs/transcript-facts-fold.js +109 -0
- package/dist/runs/transcript-facts-fold.js.map +1 -0
- package/dist/runs/transcript-facts-load.d.ts +11 -0
- package/dist/runs/transcript-facts-load.js +66 -0
- package/dist/runs/transcript-facts-load.js.map +1 -0
- package/dist/runs/transcript-facts-queue.d.ts +30 -0
- package/dist/runs/transcript-facts-queue.js +227 -0
- package/dist/runs/transcript-facts-queue.js.map +1 -0
- package/dist/runs/transcript-facts-worker.d.ts +15 -0
- package/dist/runs/transcript-facts-worker.js +26 -0
- package/dist/runs/transcript-facts-worker.js.map +1 -0
- package/dist/runs/transcript-facts.d.ts +13 -51
- package/dist/runs/transcript-facts.js +86 -202
- package/dist/runs/transcript-facts.js.map +1 -1
- package/dist/runs/ui-event-sink.d.ts +5 -30
- package/dist/runs/ui-event-sink.js +8 -30
- package/dist/runs/ui-event-sink.js.map +1 -1
- package/dist/runs/webhook.d.ts +104 -38
- package/dist/runs/webhook.js +180 -77
- package/dist/runs/webhook.js.map +1 -1
- package/dist/server/app-type.d.ts +2 -17
- package/dist/server/archive-on-merge.d.ts +35 -0
- package/dist/server/archive-on-merge.js +202 -0
- package/dist/server/archive-on-merge.js.map +1 -0
- package/dist/server/capabilities.d.ts +3 -15
- package/dist/server/capabilities.js +6 -19
- package/dist/server/capabilities.js.map +1 -1
- package/dist/server/checkout-progress.d.ts +10 -0
- package/dist/server/checkout-progress.js +27 -0
- package/dist/server/checkout-progress.js.map +1 -0
- package/dist/server/checkout.d.ts +10 -19
- package/dist/server/checkout.js +15 -45
- package/dist/server/checkout.js.map +1 -1
- package/dist/server/cockpit-ownership.d.ts +2 -0
- package/dist/server/cockpit-ownership.js +9 -0
- package/dist/server/cockpit-ownership.js.map +1 -1
- package/dist/server/cold-repo-handles.d.ts +2 -7
- package/dist/server/cold-repo-handles.js +2 -7
- package/dist/server/cold-repo-handles.js.map +1 -1
- package/dist/server/completion-actions.d.ts +30 -0
- package/dist/server/completion-actions.js +204 -0
- package/dist/server/completion-actions.js.map +1 -0
- package/dist/server/create-project.d.ts +71 -0
- package/dist/server/create-project.js +448 -0
- package/dist/server/create-project.js.map +1 -0
- package/dist/server/forge/github-filters.d.ts +7 -1
- package/dist/server/forge/github-filters.js +66 -51
- package/dist/server/forge/github-filters.js.map +1 -1
- package/dist/server/forge/github-merge-requirements.d.ts +18 -0
- package/dist/server/forge/github-merge-requirements.js +137 -0
- package/dist/server/forge/github-merge-requirements.js.map +1 -0
- package/dist/server/forge/github.d.ts +98 -111
- package/dist/server/forge/github.js +625 -306
- package/dist/server/forge/github.js.map +1 -1
- package/dist/server/forge/types.d.ts +25 -22
- package/dist/server/fs-browse.d.ts +3 -28
- package/dist/server/fs-browse.js.map +1 -1
- package/dist/server/git-changes.d.ts +5 -15
- package/dist/server/git-changes.js +3 -50
- package/dist/server/git-changes.js.map +1 -1
- package/dist/server/git.d.ts +3 -1
- package/dist/server/git.js +12 -0
- package/dist/server/git.js.map +1 -1
- package/dist/server/github.d.ts +1 -1
- package/dist/server/github.js +1 -1
- package/dist/server/github.js.map +1 -1
- package/dist/server/launch-key.d.ts +2 -4
- package/dist/server/launch-key.js +2 -4
- package/dist/server/launch-key.js.map +1 -1
- package/dist/server/live-routes.d.ts +68 -0
- package/dist/server/live-routes.js +147 -0
- package/dist/server/live-routes.js.map +1 -0
- package/dist/server/open-in-app.d.ts +4 -13
- package/dist/server/open-in-app.js +5 -41
- package/dist/server/open-in-app.js.map +1 -1
- package/dist/server/open-in-targets.d.ts +42 -0
- package/dist/server/open-in-targets.js +110 -0
- package/dist/server/open-in-targets.js.map +1 -0
- package/dist/server/open-in-terminal.d.ts +3 -0
- package/dist/server/open-in-terminal.js +6 -26
- package/dist/server/open-in-terminal.js.map +1 -1
- package/dist/server/preview-cdp-socket.d.ts +21 -0
- package/dist/server/preview-cdp-socket.js +112 -0
- package/dist/server/preview-cdp-socket.js.map +1 -0
- package/dist/server/preview-socket.d.ts +11 -11
- package/dist/server/preview-socket.js +22 -22
- package/dist/server/preview-socket.js.map +1 -1
- package/dist/server/preview-sockets.d.ts +14 -0
- package/dist/server/preview-sockets.js +18 -0
- package/dist/server/preview-sockets.js.map +1 -0
- package/dist/server/project-context.d.ts +54 -18
- package/dist/server/project-context.js +178 -5
- package/dist/server/project-context.js.map +1 -1
- package/dist/server/provider-action-gate.d.ts +0 -2
- package/dist/server/provider-action-gate.js +0 -21
- package/dist/server/provider-action-gate.js.map +1 -1
- package/dist/server/provider-auth-runtime.d.ts +3 -18
- package/dist/server/provider-auth-runtime.js +3 -18
- package/dist/server/provider-auth-runtime.js.map +1 -1
- package/dist/server/repo-branches.d.ts +3 -9
- package/dist/server/repo-branches.js +6 -22
- package/dist/server/repo-branches.js.map +1 -1
- package/dist/server/run-event-feed.d.ts +20 -0
- package/dist/server/run-event-feed.js +190 -0
- package/dist/server/run-event-feed.js.map +1 -0
- package/dist/server/server-deps.d.ts +37 -0
- package/dist/server/server-deps.js +114 -0
- package/dist/server/server-deps.js.map +1 -0
- package/dist/server/server.d.ts +14082 -11998
- package/dist/server/server.js +785 -1647
- package/dist/server/server.js.map +1 -1
- package/dist/server/start-run-options.d.ts +82 -0
- package/dist/server/start-run-options.js +111 -0
- package/dist/server/start-run-options.js.map +1 -0
- package/dist/server/validators.d.ts +3 -29
- package/dist/server/validators.js +1 -2
- package/dist/server/validators.js.map +1 -1
- package/dist/server/ws.d.ts +3 -23
- package/dist/server/ws.js +3 -23
- package/dist/server/ws.js.map +1 -1
- package/dist/server-install/engine.d.ts +6 -9
- package/dist/server-install/engine.js +2 -9
- package/dist/server-install/engine.js.map +1 -1
- package/dist/server-install/platforms/ubuntu-vps.d.ts +1 -1
- package/dist/server-install/state.d.ts +2 -4
- package/dist/server-install/state.js +2 -4
- package/dist/server-install/state.js.map +1 -1
- package/dist/server-install/steps.d.ts +2 -5
- package/dist/server-install/steps.js +3 -6
- package/dist/server-install/steps.js.map +1 -1
- package/dist/server-install/types.d.ts +3 -6
- package/dist/server-install/types.js +2 -5
- package/dist/server-install/types.js.map +1 -1
- package/dist/server-install/ui.d.ts +2 -8
- package/dist/server-install/ui.js +3 -3
- package/dist/server-install/ui.js.map +1 -1
- package/dist/session-context.d.ts +64 -0
- package/dist/session-context.js +264 -0
- package/dist/session-context.js.map +1 -0
- package/dist/skills-provision.d.ts +62 -0
- package/dist/skills-provision.js +325 -0
- package/dist/skills-provision.js.map +1 -0
- package/dist/skills-remote.d.ts +29 -27
- package/dist/skills-remote.js +236 -100
- package/dist/skills-remote.js.map +1 -1
- package/dist/skills.d.ts +25 -36
- package/dist/skills.js +43 -54
- package/dist/skills.js.map +1 -1
- package/dist/subcommands.d.ts +11 -0
- package/dist/subcommands.js +24 -0
- package/dist/subcommands.js.map +1 -0
- package/dist/task-cli/cli.js +78 -25
- package/dist/task-cli/cli.js.map +1 -1
- package/dist/task-cli/discovery.d.ts +4 -2
- package/dist/task-cli/discovery.js +14 -13
- package/dist/task-cli/discovery.js.map +1 -1
- package/dist/task-cli/hint.d.ts +20 -0
- package/dist/task-cli/hint.js +14 -0
- package/dist/task-cli/hint.js.map +1 -0
- package/dist/task-cli/http.d.ts +1 -0
- package/dist/task-cli/http.js +3 -1
- package/dist/task-cli/http.js.map +1 -1
- package/dist/task-cli/projections.d.ts +14 -11
- package/dist/task-cli/projections.js +15 -11
- package/dist/task-cli/projections.js.map +1 -1
- package/dist/task-cli/watch.d.ts +18 -10
- package/dist/task-cli/watch.js +128 -20
- package/dist/task-cli/watch.js.map +1 -1
- package/dist/ui-state.d.ts +17 -3
- package/dist/ui-state.js +56 -9
- package/dist/ui-state.js.map +1 -1
- package/dist/update-check.d.ts +0 -10
- package/dist/update-check.js +1 -16
- package/dist/update-check.js.map +1 -1
- package/dist/voice/decide.d.ts +32 -0
- package/dist/voice/decide.js +297 -0
- package/dist/voice/decide.js.map +1 -0
- package/dist/voice/decision-log.d.ts +52 -0
- package/dist/voice/decision-log.js +107 -0
- package/dist/voice/decision-log.js.map +1 -0
- package/dist/voice/engine.d.ts +119 -0
- package/dist/voice/engine.js +60 -0
- package/dist/voice/engine.js.map +1 -0
- package/dist/voice/fake-engine.d.ts +10 -0
- package/dist/voice/fake-engine.js +192 -0
- package/dist/voice/fake-engine.js.map +1 -0
- package/dist/voice/jev-engine.d.ts +17 -0
- package/dist/voice/jev-engine.js +110 -0
- package/dist/voice/jev-engine.js.map +1 -0
- package/dist/voice/pending-ask.d.ts +17 -0
- package/dist/voice/pending-ask.js +50 -0
- package/dist/voice/pending-ask.js.map +1 -0
- package/dist/voice/routes.d.ts +115 -0
- package/dist/voice/routes.js +91 -0
- package/dist/voice/routes.js.map +1 -0
- package/dist/voice/sentence.d.ts +21 -0
- package/dist/voice/sentence.js +61 -0
- package/dist/voice/sentence.js.map +1 -0
- package/dist/voice/thresholds.d.ts +28 -0
- package/dist/voice/thresholds.js +29 -0
- package/dist/voice/thresholds.js.map +1 -0
- package/dist/webhooks/cli.d.ts +18 -0
- package/dist/webhooks/cli.js +145 -0
- package/dist/webhooks/cli.js.map +1 -0
- package/dist/webhooks/prompt.d.ts +5 -0
- package/dist/webhooks/prompt.js +26 -0
- package/dist/webhooks/prompt.js.map +1 -0
- package/dist/workflows/active-run.d.ts +167 -0
- package/dist/workflows/active-run.js +21 -0
- package/dist/workflows/active-run.js.map +1 -0
- package/dist/workflows/continuation-admission.d.ts +913 -0
- package/dist/workflows/continuation-admission.js +265 -0
- package/dist/workflows/continuation-admission.js.map +1 -0
- package/dist/workflows/execution-account.d.ts +13 -0
- package/dist/workflows/execution-account.js +18 -0
- package/dist/workflows/execution-account.js.map +1 -0
- package/dist/workflows/input-acknowledgement.d.ts +152 -0
- package/dist/workflows/input-acknowledgement.js +38 -0
- package/dist/workflows/input-acknowledgement.js.map +1 -0
- package/dist/workflows/line-order.d.ts +5 -0
- package/dist/workflows/line-order.js +25 -0
- package/dist/workflows/line-order.js.map +1 -0
- package/dist/workflows/outcome-batches.d.ts +20 -0
- package/dist/workflows/outcome-batches.js +25 -0
- package/dist/workflows/outcome-batches.js.map +1 -0
- package/dist/workflows/question-routing.d.ts +8 -0
- package/dist/workflows/question-routing.js +18 -0
- package/dist/workflows/question-routing.js.map +1 -0
- package/dist/workflows/run-scheduling.d.ts +35 -0
- package/dist/workflows/run-scheduling.js +61 -0
- package/dist/workflows/run-scheduling.js.map +1 -0
- package/dist/workflows/run-states.d.ts +190 -0
- package/dist/workflows/run-states.js +295 -0
- package/dist/workflows/run-states.js.map +1 -0
- package/dist/workflows/run.d.ts +1296 -278
- package/dist/workflows/run.js +5730 -3265
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/turn-end.d.ts +116 -0
- package/dist/workflows/turn-end.js +68 -0
- package/dist/workflows/turn-end.js.map +1 -0
- package/dist/workflows/types.d.ts +35 -130
- package/dist/workflows/types.js +47 -100
- package/dist/workflows/types.js.map +1 -1
- package/dist/workflows/waits.d.ts +74 -0
- package/dist/workflows/waits.js +197 -0
- package/dist/workflows/waits.js.map +1 -0
- package/dist/workspace/agent-accounts.d.ts +5 -38
- package/dist/workspace/agent-accounts.js +14 -40
- package/dist/workspace/agent-accounts.js.map +1 -1
- package/dist/workspace/agent-profiles.d.ts +40 -16
- package/dist/workspace/agent-profiles.js +77 -17
- package/dist/workspace/agent-profiles.js.map +1 -1
- package/dist/workspace/config.d.ts +48 -49
- package/dist/workspace/config.js +68 -72
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/migrations.d.ts +3 -13
- package/dist/workspace/migrations.js.map +1 -1
- package/dist/workspace/projects-cli.d.ts +1 -1
- package/dist/workspace/projects-cli.js.map +1 -1
- package/dist/workspace/projects.d.ts +15 -18
- package/dist/workspace/projects.js +20 -16
- package/dist/workspace/projects.js.map +1 -1
- package/dist/workspace/semaphore.d.ts +11 -45
- package/dist/workspace/semaphore.js +6 -18
- package/dist/workspace/semaphore.js.map +1 -1
- package/dist/workspace/ui-state.d.ts +6 -4
- package/dist/workspace/ui-state.js +6 -4
- package/dist/workspace/ui-state.js.map +1 -1
- package/dist/workspace/webhooks.d.ts +40 -0
- package/dist/workspace/webhooks.js +146 -0
- package/dist/workspace/webhooks.js.map +1 -0
- package/dist/worktree-manifest.d.ts +165 -0
- package/dist/worktree-manifest.js +530 -0
- package/dist/worktree-manifest.js.map +1 -0
- package/dist/worktree-setup.js.map +1 -1
- package/package.json +6 -5
- package/scripts/benchmark-run-store-fixture.ts +7 -3
- package/scripts/check-pack.mjs +3 -1
- package/scripts/inline-contract.mjs +80 -27
- package/scripts/install-as-command.mjs +3 -1
- package/scripts/measure-transcript-facts-health.mjs +87 -0
- package/scripts/measure-transcript-facts-lag.mjs +68 -0
- package/scripts/mock-ci-tool.mjs +49 -6
- package/scripts/mock-claude-model-refusal.mjs +42 -0
- package/scripts/mock-claude.mjs +833 -591
- package/scripts/mock-codex-app-server.mjs +785 -418
- package/scripts/mock-cursor-acp.mjs +296 -125
- package/scripts/mock-cursor-print.mjs +256 -0
- package/scripts/mock-deferred-turn-start.mjs +16 -0
- package/scripts/mock-delivery-control.d.mts +23 -0
- package/scripts/mock-delivery-control.mjs +80 -0
- package/scripts/mock-env-record.mjs +11 -0
- package/scripts/mock-failure-control.d.mts +13 -0
- package/scripts/mock-failure-control.mjs +68 -0
- package/scripts/mock-multi-pr.mjs +7 -0
- package/scripts/mock-nudge.mjs +45 -0
- package/scripts/mock-omp-rpc.mjs +429 -255
- package/scripts/mock-opencode-serve.mjs +972 -557
- package/scripts/mock-pi-rpc.mjs +558 -331
- package/scripts/mock-runner-crash.mjs +18 -2
- package/scripts/mock-session-context.d.mts +5 -0
- package/scripts/mock-session-context.mjs +56 -0
- package/scripts/mock-silent-tail.mjs +10 -3
- package/scripts/pi-default-tools.mjs +22 -0
- package/scripts/pi-project-skills.mjs +59 -0
- package/scripts/probe-cursor-print.mjs +627 -0
- package/scripts/transcript-facts-fixture.mjs +53 -0
- package/scripts/voice-replay.ts +50 -0
- package/scripts/windows-workspace-identity.cs +208 -0
- package/scripts/windows-workspace-identity.ps1 +10 -0
- package/web/dist/assets/alert-dialog-DfctKUMw.js +1 -0
- package/web/dist/assets/button-CeupvoKG.js +41 -0
- package/web/dist/assets/centered-state-hDsyXrnH.js +1 -0
- package/web/dist/assets/cez-idle-D3aj_9lp.js +1 -0
- package/web/dist/assets/diff-BnIXZK3i2.js +3 -0
- package/web/dist/assets/diff-controls-GewpW9aF.js +1 -0
- package/web/dist/assets/diff-view-Byo7gpvW.js +11 -0
- package/web/dist/assets/dropdown-menu-COj_J5wd.js +6 -0
- package/web/dist/assets/eye-B8okNWXU.js +1 -0
- package/web/dist/assets/git-toolbar-DY0z0n4S.js +1 -0
- package/web/dist/assets/github-DwU6ALRK.js +1 -0
- package/web/dist/assets/{image-preview-FOEfqpZP.js → image-preview-C_t9q45f.js} +1 -1
- package/web/dist/assets/index-BOfE8pyD.js +34 -0
- package/web/dist/assets/index-D9HpsmdW.css +1 -0
- package/web/dist/assets/live-protocol-Uce2NwEX.js +64 -0
- package/web/dist/assets/live-worker-BizfZSoh.ts +33 -0
- package/web/dist/assets/markdown-BBDDg-4t.js +1 -0
- package/web/dist/assets/nav-row-styles-AoIhhIwf.js +1 -0
- package/web/dist/assets/popover-D56fhiXj.js +1 -0
- package/web/dist/assets/preview-pane-connected-BmVqfSU5.js +3 -0
- package/web/dist/assets/reference-chip-gQNk-4jc.js +1 -0
- package/web/dist/assets/repo-git-B5ECDUuv.js +1 -0
- package/web/dist/assets/{repo-git-CoKp-g9K.css → repo-git-DVXNOJDT.css} +1 -1
- package/web/dist/assets/run-header-C0Y1NvEM.js +2 -0
- package/web/dist/assets/run-header-CC7VUTg9.css +1 -0
- package/web/dist/assets/skills-DUeTkZ9z.js +1 -0
- package/web/dist/assets/status-dot-1PR0i69P.js +1 -0
- package/web/dist/assets/tab-link-DmWk4l5O.js +1 -0
- package/web/dist/assets/task-changes-DKZXuW89.js +1 -0
- package/web/dist/assets/task-commits-BZ6KxWdF.js +1 -0
- package/web/dist/assets/task-files-DlF7Apcn.js +2 -0
- package/web/dist/assets/task-github-item-CVPo3geZ.js +1 -0
- package/web/dist/assets/task-thread-DtQ6uGfQ.css +1 -0
- package/web/dist/assets/task-thread-mm4-ECyo.js +9 -0
- package/web/dist/assets/thread-state-CIw2BlhT.js +4 -0
- package/web/dist/assets/use-hand-to-agent-state-BCDjzHnG.js +1 -0
- package/web/dist/assets/use-hand-to-agent-state-C7esPJ-N.css +1 -0
- package/web/dist/assets/workflows-D8vYD-ql.js +7 -0
- package/web/dist/assets/zoomable-image-CxQSsfcn.js +1 -0
- package/web/dist/index.html +13 -17
- package/web/dist/live-worker.js +4 -0
- package/dist/core/tool-display.js +0 -179
- package/dist/core/tool-display.js.map +0 -1
- package/dist/runs/human-ask-summary.d.ts +0 -9
- package/dist/runs/human-ask-summary.js +0 -39
- package/dist/runs/human-ask-summary.js.map +0 -1
- package/dist/runs/run-search.d.ts +0 -29
- package/dist/runs/run-search.js +0 -63
- package/dist/runs/run-search.js.map +0 -1
- package/dist/skills-banner.d.ts +0 -22
- package/dist/skills-banner.js +0 -41
- package/dist/skills-banner.js.map +0 -1
- package/dist/skills-update.d.ts +0 -88
- package/dist/skills-update.js +0 -392
- package/dist/skills-update.js.map +0 -1
- package/dist/todos.d.ts +0 -48
- package/dist/todos.js +0 -201
- package/dist/todos.js.map +0 -1
- package/web/dist/assets/agent-accounts-CFOBMVHc.js +0 -1
- package/web/dist/assets/alert-dialog-BrpNUwMt.js +0 -1
- package/web/dist/assets/button-C_rd7CPp.js +0 -43
- package/web/dist/assets/compare-variants-D6WXhvXA.js +0 -1
- package/web/dist/assets/diff-DrEARXTV.js +0 -3
- package/web/dist/assets/diff-controls-B1-j7nFg.js +0 -1
- package/web/dist/assets/diff-view-DvGLjdTk.js +0 -6
- package/web/dist/assets/dropdown-menu-BegOZFrj.js +0 -1
- package/web/dist/assets/format-DpNe3S0A.js +0 -1
- package/web/dist/assets/git-toolbar-Cw4ljmhB.js +0 -1
- package/web/dist/assets/github-CfV5sqL0.js +0 -1
- package/web/dist/assets/index-DgWWlOLy.js +0 -13
- package/web/dist/assets/index-fBFnuvxu.css +0 -1
- package/web/dist/assets/nav-row-styles-vp2JbhuJ.js +0 -1
- package/web/dist/assets/popover-BohP6WTx.js +0 -1
- package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +0 -3
- package/web/dist/assets/preview-state-vuMviq6q.js +0 -1
- package/web/dist/assets/prompt-templates-Cubdut_C.js +0 -15
- package/web/dist/assets/repo-git-Cf4t5PD0.js +0 -1
- package/web/dist/assets/run-diff-BpkUW37Z.js +0 -3
- package/web/dist/assets/run-header-68WHf9wV.js +0 -1
- package/web/dist/assets/run-header-8GObNx6F.css +0 -1
- package/web/dist/assets/search-x-I9Q8pgUa.js +0 -1
- package/web/dist/assets/skills-DFBiAuJG.js +0 -1
- package/web/dist/assets/skills-DiVIgKeU.js +0 -1
- package/web/dist/assets/square-terminal-B6RYYieR.js +0 -1
- package/web/dist/assets/status-dot-CqTJHSP9.js +0 -1
- package/web/dist/assets/tab-link-B07Hu4QX.js +0 -1
- package/web/dist/assets/task-changes--lQF6EYf.js +0 -1
- package/web/dist/assets/task-commits-Cx0qy4DN.js +0 -1
- package/web/dist/assets/task-files-D50s-gKW.js +0 -2
- package/web/dist/assets/task-github-item-DaqmSwYT.js +0 -1
- package/web/dist/assets/task-thread-BPQ64lui.js +0 -7
- package/web/dist/assets/task-thread-zNE2Q9E-.css +0 -1
- package/web/dist/assets/textarea-D20O4Pw0.js +0 -1
- package/web/dist/assets/thread-state-DWcFMrvb.js +0 -4
- package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +0 -1
- package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +0 -1
- package/web/dist/assets/utils-JsL3ZsJf.js +0 -64
- package/web/dist/assets/workflows-jwG7uxqi.js +0 -11
- package/web/dist/assets/zoomable-image-BJJ64KGc.js +0 -1
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// Load only when configured: ordinary mock turns retain their event-loop ordering.
|
|
3
|
+
const failureControl = process.env.CEZ_MOCK_FAILURE_CONTROL === undefined ? undefined
|
|
4
|
+
: (await import('./mock-failure-control.mjs')).readFailureControl();
|
|
5
|
+
const delivery = process.env.CEZ_MOCK_DELIVERY_CONTROL === undefined ? undefined
|
|
6
|
+
: (await import('./mock-delivery-control.mjs')).readDeliveryControl(['reject', 'hold-ack', 'hold-response']);
|
|
2
7
|
// Keep this fixture standalone: lifecycle tests copy the runner into a temp directory.
|
|
3
8
|
import { spawn as watchdogSpawn } from 'node:child_process';
|
|
4
|
-
import { writeFileSync as
|
|
9
|
+
import { renameSync as watchdogRename, writeFileSync as watchdogWrite } from 'node:fs';
|
|
10
|
+
// Publish a pid atomically (temporary sibling, then rename): a test reading it sees no
|
|
11
|
+
// file or a whole pid, never the empty file whose Number() is 0 (hearsay-tools/cezarion#953).
|
|
12
|
+
function watchdogWritePid(file, pid) { const tmp = `${file}.${process.pid}.tmp`; watchdogWrite(tmp, pid); watchdogRename(tmp, file); }
|
|
13
|
+
let inputClosed = false;
|
|
5
14
|
function watchdogStall(prompt) {
|
|
6
15
|
if (!prompt.includes('mock:no-progress')) return false;
|
|
7
|
-
|
|
16
|
+
if (prompt.includes('input-closed')) {
|
|
17
|
+
inputClosed = true;
|
|
18
|
+
watchdogWritePid('input-closed.pid', String(process.pid));
|
|
19
|
+
setTimeout(() => process.exit(0), 300);
|
|
20
|
+
}
|
|
8
21
|
if (prompt.includes('ignore-term')) {
|
|
9
22
|
process.removeAllListeners('SIGTERM');
|
|
10
23
|
process.on('SIGTERM', () => {});
|
|
@@ -17,10 +30,12 @@ function watchdogStall(prompt) {
|
|
|
17
30
|
if (prompt.includes('leftover')) {
|
|
18
31
|
// hearsay-tools/cezarion#890: one child stays in the session's process group and ignores
|
|
19
32
|
// 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)`;
|
|
33
|
+
const leftover = (file) => `process.on('SIGTERM',()=>{}); const fs = require('fs'); fs.writeFileSync(${JSON.stringify(`${file}.tmp`)}, String(process.pid)); fs.renameSync(${JSON.stringify(`${file}.tmp`)}, ${JSON.stringify(file)}); setTimeout(()=>{},20000)`;
|
|
21
34
|
watchdogSpawn(process.execPath, ['-e', leftover('leftover-group.pid')], { stdio: 'ignore' });
|
|
22
35
|
watchdogSpawn(process.execPath, ['-e', leftover('leftover-session.pid')], { stdio: 'ignore', detached: true }).unref();
|
|
23
36
|
}
|
|
37
|
+
// Publish readiness after mode setup: another process may signal us as soon as it sees it.
|
|
38
|
+
watchdogWritePid('watchdog.pid', String(process.pid));
|
|
24
39
|
return true;
|
|
25
40
|
}
|
|
26
41
|
|
|
@@ -32,10 +47,10 @@ function watchdogStall(prompt) {
|
|
|
32
47
|
// arrive over SSE afterwards, so a correct stream (v1 and v2 alike) must
|
|
33
48
|
// take its turn-end from `session.idle`, never from the HTTP response.
|
|
34
49
|
import { createHash } from 'node:crypto';
|
|
35
|
-
// Aliased: owned-input-delivery.testkit.ts prepends its own `existsSync`/`writeFileSync` import,
|
|
36
|
-
// and a second import of the same name is a SyntaxError.
|
|
37
50
|
import { appendFileSync, existsSync as sessionStoreExists, mkdirSync, readFileSync, writeFileSync as sessionStoreWrite } from 'node:fs';
|
|
38
51
|
import { createServer } from 'node:http';
|
|
52
|
+
// Dynamic, like every sibling import here, so a test's copy of this mock can repoint it.
|
|
53
|
+
const { answerNudgeBody, silentNudge } = await import('./mock-nudge.mjs');
|
|
39
54
|
import { tmpdir } from 'node:os';
|
|
40
55
|
import { dirname, join } from 'node:path';
|
|
41
56
|
|
|
@@ -59,6 +74,7 @@ if (failStarts > 0) {
|
|
|
59
74
|
if (process.env.CEZ_MOCK_ARGS_FILE && process.env.OPENCODE_CONFIG_CONTENT) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, JSON.stringify({ type: 'runtime-config', config: JSON.parse(process.env.OPENCODE_CONFIG_CONTENT) }) + '\n');
|
|
60
75
|
if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}')); }
|
|
61
76
|
import * as parityGateFs from 'node:fs';
|
|
77
|
+
import { withoutSessionContextBody } from './mock-session-context.mjs';
|
|
62
78
|
|
|
63
79
|
// #401: let the test observe the actual monitoring park before releasing late wire frames.
|
|
64
80
|
async function afterParityPark(prompt) {
|
|
@@ -86,6 +102,9 @@ const hostname = arg('--hostname', '127.0.0.1');
|
|
|
86
102
|
const DEFAULT_SESSION_ID = 'ses_mock_1';
|
|
87
103
|
let SESSION_ID = DEFAULT_SESSION_ID;
|
|
88
104
|
const MESSAGE_ID = 'msg_mock_1';
|
|
105
|
+
// hearsay-tools/cezarion#1265: the captured FrontmatterError text, verbatim,
|
|
106
|
+
// for a skill whose frontmatter holds `foo: "unterminated`.
|
|
107
|
+
const FRONTMATTER_ERROR = '/home/agent/.agents/skills/bad4/SKILL.md: Failed to parse YAML frontmatter: unexpected end of the stream within a double quoted scalar at line 6, column 1:\n \n ^';
|
|
89
108
|
|
|
90
109
|
function sessionStorePath() {
|
|
91
110
|
if (process.env.CEZ_MOCK_OPENCODE_SESSIONS_FILE) return process.env.CEZ_MOCK_OPENCODE_SESSIONS_FILE;
|
|
@@ -167,11 +186,37 @@ const write = (event) => {
|
|
|
167
186
|
// assistant message names it as parentID. Steers are read just before the turn's
|
|
168
187
|
// idle, unless the turn is `late` (the upstream lost wake: never read).
|
|
169
188
|
let turnActive = false;
|
|
189
|
+
// The heartbeats of a held `mock:ack-only` turn (hearsay-tools/cezarion#1301).
|
|
190
|
+
let heldBeats;
|
|
170
191
|
let turnLate = false;
|
|
171
192
|
let steers = [];
|
|
172
193
|
let steerSerial = 0;
|
|
194
|
+
// CEZ_MOCK_OPENCODE_FOLLOW_ON_MS: the server reads steers in a follow-on run that starts that long
|
|
195
|
+
// after the idle, and a prompt posted in between waits behind that run.
|
|
196
|
+
const followOnMs = Number(process.env.CEZ_MOCK_OPENCODE_FOLLOW_ON_MS ?? 0);
|
|
197
|
+
let followOn = null;
|
|
198
|
+
const runFollowOn = (held) => {
|
|
199
|
+
followOn = sleep(followOnMs).then(() => {
|
|
200
|
+
turnActive = true;
|
|
201
|
+
for (const steer of held) {
|
|
202
|
+
const id = `msg_steer_reply_${++steerSerial}`;
|
|
203
|
+
write({ type: 'message.updated', properties: { info: { ...info({}), id, parentID: steer.userId } } });
|
|
204
|
+
write({ type: 'message.part.updated', properties: { part: { id: `prt_${id}`, messageID: id, sessionID: SESSION_ID, type: 'text', text: steer.text, time: { start: 1, end: 2 } } } });
|
|
205
|
+
}
|
|
206
|
+
turnActive = false;
|
|
207
|
+
followOn = null;
|
|
208
|
+
write({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
209
|
+
});
|
|
210
|
+
};
|
|
173
211
|
const send = (event) => {
|
|
174
212
|
const ownIdle = event?.type === 'session.idle' && (!event.properties?.sessionID || event.properties.sessionID === SESSION_ID);
|
|
213
|
+
if (ownIdle && followOnMs > 0 && steers.length && !turnLate) {
|
|
214
|
+
const held = steers;
|
|
215
|
+
steers = []; turnActive = false;
|
|
216
|
+
write(event);
|
|
217
|
+
runFollowOn(held);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
175
220
|
if (ownIdle) {
|
|
176
221
|
if (!turnLate) for (const steer of steers) {
|
|
177
222
|
const id = `msg_steer_reply_${++steerSerial}`;
|
|
@@ -209,6 +254,31 @@ const sendQuestionPart = (input) =>
|
|
|
209
254
|
},
|
|
210
255
|
},
|
|
211
256
|
});
|
|
257
|
+
/** hearsay-tools/cezarion#1266: OpenCode 1.18.35 `permission.asked`. No `session.idle`
|
|
258
|
+
* follows: the asking tool waits until `POST /permission/{id}/reply` answers it. */
|
|
259
|
+
const pendingPermissionAsks = new Set();
|
|
260
|
+
// hearsay-tools/cezarion#1266 lifecycle scenarios (harness parity P5, P7).
|
|
261
|
+
let failPermissionReplies = 0;
|
|
262
|
+
let classifyPending = false;
|
|
263
|
+
async function waitForMockFile(file) {
|
|
264
|
+
// bound: hang guard for a test that never creates its gate file; the wait itself is the file.
|
|
265
|
+
const deadline = Date.now() + 15_000;
|
|
266
|
+
while (!parityGateFs.existsSync(file)) {
|
|
267
|
+
if (Date.now() > deadline) throw new Error(`${file} was not created`);
|
|
268
|
+
// bound: poll interval; the gate file is written by another process, with no event to await.
|
|
269
|
+
await new Promise(r => setTimeout(r, 10));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const textPart = (id, text) => send({ type: 'message.part.updated', properties: { part: {
|
|
273
|
+
id, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 },
|
|
274
|
+
} } });
|
|
275
|
+
const sendPermissionAsk = (id, permission, patterns, always) => {
|
|
276
|
+
pendingPermissionAsks.add(id);
|
|
277
|
+
send({ type: 'permission.asked', properties: {
|
|
278
|
+
id, sessionID: SESSION_ID, permission, patterns, metadata: {}, always,
|
|
279
|
+
tool: { messageID: MESSAGE_ID, callID: 'call_mock_bash' },
|
|
280
|
+
} });
|
|
281
|
+
};
|
|
212
282
|
// The user message of the prompt being answered: opencode 1.18.32 creates it at the
|
|
213
283
|
// POST and names it as the assistant's parentID (#505 probe).
|
|
214
284
|
let currentUserId;
|
|
@@ -230,13 +300,93 @@ const info = (extra) => ({
|
|
|
230
300
|
let autonomousTurn = 0;
|
|
231
301
|
let autonomousCap = false;
|
|
232
302
|
let autonomousReadinessIdle = false;
|
|
233
|
-
const
|
|
303
|
+
const bodyRoutes = [
|
|
304
|
+
[(req, url) => req.method === 'POST' && /^\/question\/[^/]+\/(reply|reject)$/.test(url), replyToQuestion],
|
|
305
|
+
[(req, url) => req.method === 'POST' && /^\/permission\/([^/]+)\/reply$/.test(url), replyToPermission],
|
|
306
|
+
[(req, url) => req.method === 'GET' && /^\/session\/([^/]+)$/.test(url), replyWithSession],
|
|
307
|
+
[(req, url) => req.method === 'PATCH' && /^\/session\/([^/]+)$/.test(url), replyWithPatchedSession],
|
|
308
|
+
[(req, url) => req.method === 'POST' && url === '/session', replyWithNewSession],
|
|
309
|
+
[matchesSessionPrompt, handleSessionPrompt],
|
|
310
|
+
];
|
|
311
|
+
const earlyReplies = [
|
|
312
|
+
[body => body.includes('mock:steer-tool'), replyWithSteerTool],
|
|
313
|
+
[body => body.includes('mock:steer-late'), replyWithLateSteer],
|
|
314
|
+
[body => body.includes('mock:cezar-tool'), replyWithCezarTool],
|
|
315
|
+
[body => body.includes('mock:ci-wait'), replyWithCiWait],
|
|
316
|
+
[body => body.includes('mock:multi-pr-refs'), replyWithMultiplePrs],
|
|
317
|
+
[body => body.includes('mock:turn-messages:'), replyWithTurnMessages],
|
|
318
|
+
[body => body.includes('mock:agent-echo'), replyWithEcho],
|
|
319
|
+
[body => body.includes('mock:subagent-after-park'), replyWithParkedSubagent],
|
|
320
|
+
[body => body.includes('mock:subagent'), replyWithSubagent],
|
|
321
|
+
];
|
|
322
|
+
const lateReplies = [
|
|
323
|
+
[body => body.includes('mock:split-text'), replyWithSplitText],
|
|
324
|
+
[body => body.includes('mock:autonomous') || body.includes('Continue working autonomously until the task is fully complete.'), replyAutonomously],
|
|
325
|
+
[body => body.includes('mock:silent-tail') || body.includes('mock:tool-tail') || silentNudge(body), replyWithSilentTail],
|
|
326
|
+
[body => body.includes('mock:approval-resolved') || body.includes('mock:approval-turn-end'), replyWithSettledApproval],
|
|
327
|
+
[body => body.includes('mock:approval-reply-fails'), replyWithFailedApprovalReply],
|
|
328
|
+
[body => body.includes('mock:permission-malformed'), replyWithMalformedPermission],
|
|
329
|
+
[body => body.includes('mock:permission-classify'), replyWithClassifyingPermission],
|
|
330
|
+
[body => body.includes('mock:permission-late'), replyWithLatePermission],
|
|
331
|
+
[body => body.includes('mock:permission-default'), replyWithDefaultPermission],
|
|
332
|
+
[body => body.includes('mock:approval'), replyWithApproval],
|
|
333
|
+
[body => body.includes('mock:ask-snapshot'), replyWithAskSnapshot],
|
|
334
|
+
[body => body.includes('mock:ask-prose'), replyWithAskProse],
|
|
335
|
+
[body => body.includes('mock:ask-bad'), replyWithMalformedAsk],
|
|
336
|
+
[body => body.includes('mock:ask'), replyWithAsk],
|
|
337
|
+
[body => body.includes('mock:hold-done'), replyWithHeldDone],
|
|
338
|
+
[body => body.includes('mock:done'), replyWithDone],
|
|
339
|
+
[body => body.includes('mock:hold-ask'), replyWithHeldAsk],
|
|
340
|
+
[body => watchdogStall(body), acknowledgeWatchdog],
|
|
341
|
+
[body => body.includes('mock:busy-progress'), replyWithBusyProgress],
|
|
342
|
+
[body => body.includes('mock:hold-gated'), replyWithGatedHold],
|
|
343
|
+
[body => body.includes('mock:hold'), replyWithHold],
|
|
344
|
+
];
|
|
345
|
+
function matchesSessionPrompt(req, url) {
|
|
346
|
+
const match = req.method === 'POST' && /^\/session\/([^/]+)\/(prompt_async|message)$/.exec(url);
|
|
347
|
+
return match && getSession(match[1]);
|
|
348
|
+
}
|
|
349
|
+
async function dispatchReply(replies, body) {
|
|
350
|
+
for (const [matches, reply] of replies) {
|
|
351
|
+
if (matches(body) && await reply(body) !== false) return true;
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const server = createServer(handleRequest);
|
|
357
|
+
|
|
358
|
+
function handleRequest(req, res) {
|
|
234
359
|
const url = req.url ?? '';
|
|
360
|
+
if (inputClosed && req.method === 'POST') { req.socket.destroy(); return; }
|
|
235
361
|
if (req.method === 'GET' && url === '/question') {
|
|
236
362
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
237
363
|
res.end(JSON.stringify(pendingQuestions));
|
|
238
364
|
return;
|
|
239
365
|
}
|
|
366
|
+
// Only user-written config, as the real server returns it: this user configured `bash: ask`,
|
|
367
|
+
// so `mock:approval` reaches the cockpit and the built-in `mock:permission-default` ask does not.
|
|
368
|
+
if (req.method === 'GET' && url === '/config') {
|
|
369
|
+
const answer = () => {
|
|
370
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
371
|
+
res.end(JSON.stringify({ permission: { bash: 'ask' } }));
|
|
372
|
+
};
|
|
373
|
+
if (!classifyPending) { answer(); return; }
|
|
374
|
+
// P7: hold the config read, settle the ask natively while cezar classifies it, then send a
|
|
375
|
+
// marker ask. Files beside CEZ_MOCK_RELEASE_FILE hand each step to the test and back.
|
|
376
|
+
classifyPending = false;
|
|
377
|
+
const release = process.env.CEZ_MOCK_RELEASE_FILE;
|
|
378
|
+
void (async () => {
|
|
379
|
+
parityGateFs.writeFileSync(`${release}.config-read`, '');
|
|
380
|
+
await waitForMockFile(`${release}.settle`);
|
|
381
|
+
pendingPermissionAsks.delete('per_mock_classify');
|
|
382
|
+
send({ type: 'permission.replied', properties: { sessionID: SESSION_ID, requestID: 'per_mock_classify', reply: 'reject' } });
|
|
383
|
+
textPart('prt_classify_settled', 'OpenCode settled the ask itself.');
|
|
384
|
+
await waitForMockFile(release);
|
|
385
|
+
answer();
|
|
386
|
+
sendPermissionAsk('per_mock_marker', 'bash', ['marker'], ['marker']);
|
|
387
|
+
})();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
240
390
|
if (req.method === 'GET' && url.startsWith('/event')) {
|
|
241
391
|
res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
|
|
242
392
|
sse = res;
|
|
@@ -245,562 +395,821 @@ const server = createServer((req, res) => {
|
|
|
245
395
|
}
|
|
246
396
|
let body = '';
|
|
247
397
|
req.on('data', (chunk) => (body += chunk));
|
|
248
|
-
req.on('end', async () => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
398
|
+
req.on('end', async () => { await handleRequestBody(req, res, url, body); });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function handleSessionPrompt(req, res, url, body, received) {
|
|
402
|
+
const sessionPrompt = /^\/session\/([^/]+)\/(prompt_async|message)$/.exec(url);
|
|
403
|
+
SESSION_ID = sessionPrompt[1];
|
|
404
|
+
recordSessionPrompt(body);
|
|
405
|
+
if (await preparePromptTurn(res, url, body, received)) return;
|
|
406
|
+
await streamPrompt(url, body);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function handleRequestBody(req, res, url, body) {
|
|
410
|
+
const prepared = prepareRequestBody(req, url, body);
|
|
411
|
+
body = prepared.body;
|
|
412
|
+
const route = bodyRoutes.find(([matches]) => matches(req, url));
|
|
413
|
+
if (route) { await route[1](req, res, url, body, prepared.received); return; }
|
|
414
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
415
|
+
res.end('{}');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function prepareRequestBody(req, url, body) {
|
|
419
|
+
if (process.env.CEZ_MOCK_ARGS_FILE) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, `${JSON.stringify({ method: req.method, url, body: body ? JSON.parse(body) : undefined })}\n`);
|
|
420
|
+
// The delivery control also reads the prompt Cezar sent, before a nudge's scripted answer replaces it.
|
|
421
|
+
if (req.method === 'POST' && /\/(prompt_async|message)$/.test(url)) body = withoutSessionContextBody(body);
|
|
422
|
+
const received = body;
|
|
423
|
+
if (req.method === 'POST' && /\/(prompt_async|message)$/.test(url)) body = answerNudgeBody(body);
|
|
424
|
+
|
|
425
|
+
return { body, received };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function replyWithNewSession(req, res, url, body) {
|
|
429
|
+
const parsed = body ? JSON.parse(body) : {};
|
|
430
|
+
SESSION_ID = createSession(parsed);
|
|
431
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
432
|
+
res.end(JSON.stringify({
|
|
433
|
+
id: SESSION_ID,
|
|
434
|
+
title: parsed.title ?? 'cezar task',
|
|
435
|
+
...(Array.isArray(parsed.permission) ? { permission: parsed.permission } : {}),
|
|
436
|
+
}));
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function replyWithPatchedSession(req, res, url, body) {
|
|
441
|
+
const sessionPatch = /^\/session\/([^/]+)$/.exec(url);
|
|
442
|
+
const session = patchSession(sessionPatch[1], body ? JSON.parse(body) : {});
|
|
443
|
+
if (!session) {
|
|
444
|
+
res.writeHead(404, { 'content-type': 'application/json' });
|
|
445
|
+
res.end(JSON.stringify({ error: 'NotFoundError' }));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
449
|
+
res.end(JSON.stringify({
|
|
450
|
+
id: session.id,
|
|
451
|
+
title: session.title,
|
|
452
|
+
...(Array.isArray(session.permission) ? { permission: session.permission } : {}),
|
|
453
|
+
}));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function replyWithSession(req, res, url, body) {
|
|
458
|
+
const sessionGet = /^\/session\/([^/]+)$/.exec(url);
|
|
459
|
+
const session = getSession(sessionGet[1]);
|
|
460
|
+
if (!session) {
|
|
461
|
+
res.writeHead(404, { 'content-type': 'application/json' });
|
|
462
|
+
res.end(JSON.stringify({ error: 'NotFoundError' }));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
466
|
+
res.end(JSON.stringify({
|
|
467
|
+
id: session.id,
|
|
468
|
+
title: session.title,
|
|
469
|
+
...(Array.isArray(session.permission) ? { permission: session.permission } : {}),
|
|
470
|
+
}));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function replyToPermission(req, res, url, body) {
|
|
475
|
+
const permissionReply = /^\/permission\/([^/]+)\/reply$/.exec(url);
|
|
476
|
+
// The reply resumes the held turn, then its own session.idle ends it, as a question reply does.
|
|
477
|
+
const id = decodeURIComponent(permissionReply[1]);
|
|
478
|
+
const reply = body ? JSON.parse(body).reply : undefined;
|
|
479
|
+
// P5: refuse the first answer; the ask stays pending, as a failed reply leaves it.
|
|
480
|
+
if (failPermissionReplies > 0 && pendingPermissionAsks.has(id)) {
|
|
481
|
+
failPermissionReplies -= 1;
|
|
482
|
+
res.writeHead(500, { 'content-type': 'application/json' });
|
|
483
|
+
res.end(JSON.stringify({ error: 'mock: reply refused' }));
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
487
|
+
res.end('true');
|
|
488
|
+
if (pendingPermissionAsks.delete(id)) setTimeout(() => {
|
|
489
|
+
send({ type: 'permission.replied', properties: { sessionID: SESSION_ID, requestID: id, reply } });
|
|
490
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
491
|
+
id: `prt_permission_${id}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
492
|
+
text: `Approval response: ${JSON.stringify({ reply })}\n\nCEZ:DONE`, time: { start: 1, end: 2 },
|
|
493
|
+
} } });
|
|
494
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
495
|
+
}, 30);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function replyToQuestion(req, res, url, body) {
|
|
500
|
+
// Native question reply resumes the held turn, then its own session.idle
|
|
501
|
+
// ends it (same SSE lifecycle shape as the baseline; never the HTTP ack).
|
|
502
|
+
const answered = pendingQuestions.some(question => question.id === 'q_mock_1');
|
|
503
|
+
pendingQuestions.length = 0;
|
|
504
|
+
if (answered && url.endsWith('/reply')) setTimeout(() => {
|
|
505
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
506
|
+
id: 'prt_answer', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: `human answer: ${body}${answerDone ? '\n\nCEZ:DONE' : ''}`,
|
|
507
|
+
} } });
|
|
508
|
+
// #505 review: CEZ_MOCK_OPENCODE_REPLY_HOLD_MS keeps the answered turn running.
|
|
509
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), Number(process.env.CEZ_MOCK_OPENCODE_REPLY_HOLD_MS ?? 0));
|
|
510
|
+
}, 30);
|
|
511
|
+
const acknowledge = () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end('{}'); };
|
|
512
|
+
if (lateQuestionReply) setTimeout(acknowledge, 250);
|
|
513
|
+
else acknowledge();
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async function replyWithSteerTool(body) {
|
|
518
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
519
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_tool', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_steer', tool: 'bash', state: { status: 'running', input: { command: 'wait' } } } } });
|
|
520
|
+
await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
|
|
521
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_tool', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_steer', tool: 'bash', state: { status: 'completed', input: { command: 'wait' }, output: 'waited' } } } });
|
|
522
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function replyWithLateSteer(body) {
|
|
527
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
528
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_late', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'late window: final message already sent', time: { start: 1760000000000, end: 1760000000001 } } } });
|
|
529
|
+
turnLate = true;
|
|
530
|
+
await sleep(300);
|
|
531
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// The raw body is enough to spot a `mock:` marker — the prompt text is
|
|
536
|
+
// inside it whatever part shape the runner used to wrap it.
|
|
537
|
+
async function replyWithCezarTool(body) {
|
|
538
|
+
const { toolPrompt } = await import('./mock-ci-tool.mjs');
|
|
539
|
+
const text = await toolPrompt('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}'), body);
|
|
540
|
+
// A completed assistant message, as on the other runners' wires, so its text (an ask line
|
|
541
|
+
// after the calls, say) reaches the turn end.
|
|
542
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
543
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'tool-result', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 } } } });
|
|
544
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function replyWithCiWait(body) {
|
|
549
|
+
const { ciPrompt } = await import('./mock-ci-tool.mjs');
|
|
550
|
+
const text = await ciPrompt('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}'), body);
|
|
551
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'ci-result', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text } } });
|
|
552
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async function replyWithMultiplePrs(body) {
|
|
557
|
+
const { multiPrText, unrelatedPr } = await import('./mock-multi-pr.mjs');
|
|
558
|
+
const text = multiPrText(JSON.parse(body).parts.map(part => part.text ?? '').join('\n'));
|
|
559
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
560
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'multi-reasoning', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'reasoning', text: unrelatedPr } } });
|
|
561
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'multi-tool', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', tool: 'read', callID: 'multi-read', state: { status: 'completed', input: { filePath: 'other.txt' }, output: unrelatedPr, time: { start: 1, end: 2 } } } } });
|
|
562
|
+
for (const upto of [5, text.length]) send({ type: 'message.part.updated', properties: { part: { id: 'multi-message', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: text.slice(0, upto), ...(upto === text.length ? { time: { start: 1, end: 2 } } : {}) } } });
|
|
563
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function replyWithTurnMessages(body) {
|
|
568
|
+
const { turnMessages } = await import('./mock-turn-messages.mjs');
|
|
569
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
570
|
+
// `mock:channel-missing` → a CEZ marker message arrives as a text part without `time`, then a
|
|
571
|
+
// `time`-only update of the same part with no text (monitoring-channel-order.test.ts).
|
|
572
|
+
const missing = body.includes('mock:channel-missing');
|
|
573
|
+
for (const text of turnMessages(JSON.parse(body).parts.map(part => part.text ?? '').join('\n'))) {
|
|
574
|
+
if (missing) {
|
|
575
|
+
const id = `channel_${++echoSerial}`;
|
|
576
|
+
if (text.includes('CEZ:MONITORING') || text.includes('CEZ:DONE')) {
|
|
577
|
+
send({ type: 'message.part.updated', properties: { part: { id, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text } } });
|
|
578
|
+
send({ type: 'message.part.updated', properties: { part: { id, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', time: { start: 1, end: 2 } } } });
|
|
579
|
+
} else send({ type: 'message.part.updated', properties: { part: { id, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 } } } });
|
|
580
|
+
continue;
|
|
298
581
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
582
|
+
send({ type: 'message.part.updated', properties: { part: { id: `prt_multi_${++echoSerial}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 } } } });
|
|
583
|
+
}
|
|
584
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async function replyWithEcho(body) {
|
|
589
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
590
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
591
|
+
id: `prt_echo_${++echoSerial}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
592
|
+
text: JSON.parse(body).parts.map(part => part.text ?? '').join('\n'),
|
|
593
|
+
} } });
|
|
594
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// SSE child attribution from __fixtures__/opencode/subtask-nested.ndjson.
|
|
599
|
+
async function replyWithParkedSubagent(body) {
|
|
600
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
601
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'park-task', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask', prompt: 'Review', description: 'Review', agent: 'general' } } });
|
|
602
|
+
send({ type: 'message.updated', properties: { info: { ...info({}), id: 'park-child-msg', sessionID: 'park-child', parentID: SESSION_ID, mode: 'subagent' } } });
|
|
603
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'early-child', messageID: 'park-child-msg', sessionID: 'park-child', type: 'text', text: 'Child review started.', time: { start: 1, end: 2 } } } });
|
|
604
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'park-parent', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Watching the child.\nCEZ:MONITORING', time: { start: 1, end: 2 } } } });
|
|
605
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
606
|
+
await afterParityPark(body);
|
|
607
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-read', messageID: 'park-child-msg', sessionID: 'park-child', type: 'tool', tool: 'read', state: { status: 'running', input: { path: 'README.md' } } } } });
|
|
608
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-read', messageID: 'park-child-msg', sessionID: 'park-child', type: 'tool', tool: 'read', state: { status: 'completed', input: { path: 'README.md' }, output: 'Read the file.' } } } });
|
|
609
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-child-text', messageID: 'park-child-msg', sessionID: 'park-child', type: 'text', text: 'Late child text.', time: { start: 3, end: 4 } } } });
|
|
610
|
+
// Closed child scopes are ignored. This passive usage snapshot proves the
|
|
611
|
+
// preceding SSE frames have reached the runner before the test observes it.
|
|
612
|
+
send({ type: 'message.updated', properties: { info: info({ tokens: { input: 424240, output: 2, reasoning: 0, cache: { read: 0, write: 0 } } }) } });
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
async function replyWithSubagent(body) {
|
|
617
|
+
// Wire shape from `__fixtures__/opencode/subtask-nested.ndjson`: a
|
|
618
|
+
// `subtask` part on the parent message, then a child message whose info
|
|
619
|
+
// carries `parentID`, its parts, and the CHILD's own `session.idle`.
|
|
620
|
+
// That child idle arrives first on purpose — the parent turn has to
|
|
621
|
+
// survive it and close only on its own (#5, #600; harness parity S9).
|
|
622
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
623
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
624
|
+
id: 'prt_mock_subt', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask',
|
|
625
|
+
prompt: 'Trace every caller of resolveSession', description: 'Trace resolveSession callers',
|
|
626
|
+
agent: 'general',
|
|
627
|
+
} } });
|
|
628
|
+
send({ type: 'message.updated', properties: { info: {
|
|
629
|
+
...info({}), id: 'msg_mock_child', sessionID: 'ses_mock_child',
|
|
630
|
+
parentID: SESSION_ID, mode: 'subagent',
|
|
631
|
+
} } });
|
|
632
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
633
|
+
id: 'prt_mock_ctxt', messageID: 'msg_mock_child', sessionID: 'ses_mock_child',
|
|
634
|
+
type: 'text', text: 'Two callers: router.ts and session.ts.',
|
|
635
|
+
time: { start: 1760000008800, end: 1760000008900 },
|
|
636
|
+
} } });
|
|
637
|
+
send({ type: 'session.idle', properties: { sessionID: 'ses_mock_child' } });
|
|
638
|
+
// A second child is still running when the parent declares monitoring.
|
|
639
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
640
|
+
id: 'prt_mock_late_subt', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask',
|
|
641
|
+
prompt: 'Review the callers', description: 'Review callers', agent: 'general',
|
|
642
|
+
} } });
|
|
643
|
+
send({ type: 'message.updated', properties: { info: {
|
|
644
|
+
...info({}), id: 'msg_mock_late_child', sessionID: 'ses_mock_late_child',
|
|
645
|
+
parentID: SESSION_ID, mode: 'subagent',
|
|
646
|
+
} } });
|
|
647
|
+
setTimeout(() => {
|
|
648
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
649
|
+
id: 'prt_mock_ptxt', messageID: MESSAGE_ID, sessionID: SESSION_ID,
|
|
650
|
+
type: 'text', text: 'Still working after the sub-agent.\nCEZ:MONITORING',
|
|
651
|
+
time: { start: 1760000009000, end: 1760000009200 },
|
|
652
|
+
} } });
|
|
653
|
+
// #149: foreign-session text after the parent's marker must stay v2-only.
|
|
654
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
655
|
+
id: 'prt_mock_late_child', messageID: 'msg_mock_late_child', sessionID: 'ses_mock_late_child',
|
|
656
|
+
type: 'text', text: 'Child review finished.',
|
|
657
|
+
time: { start: 1760000009200, end: 1760000009300 },
|
|
658
|
+
} } });
|
|
659
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
660
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
661
|
+
}) } });
|
|
662
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
663
|
+
}, 60);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
async function replyWithSplitText(body) {
|
|
668
|
+
// OpenCode streams a text part as successive GROWING snapshots of the
|
|
669
|
+
// same part id; only the final one carries `time.end`. A runner emitting
|
|
670
|
+
// one v1 `text` per snapshot publishes the torn `…CEZ:` prefix, which is
|
|
671
|
+
// #2 on opencode's wire (harness parity S8).
|
|
672
|
+
const full = 'parity split text\n\nCEZ:MONITORING';
|
|
673
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
674
|
+
for (const upto of [17, 24, full.length]) {
|
|
675
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
676
|
+
id: 'prt_mock_split', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
677
|
+
text: full.slice(0, upto),
|
|
678
|
+
...(upto === full.length ? { time: { start: 1760000000500, end: 1760000000600 } } : {}),
|
|
679
|
+
} } });
|
|
680
|
+
}
|
|
681
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
682
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
683
|
+
}) } });
|
|
684
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async function replyAutonomously(body) {
|
|
689
|
+
const { autonomousReply } = await import('./mock-autonomous.mjs');
|
|
690
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
691
|
+
send({ type: 'message.part.updated', properties: { part: { id: `autonomous-${++autonomousTurn}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: autonomousReply(JSON.parse(body).parts.map(part => part.text ?? '').join('\n')), time: { start: 1, end: 2 } } } });
|
|
692
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async function replyWithSilentTail(body) {
|
|
697
|
+
const silent = await import('./mock-silent-tail.mjs');
|
|
698
|
+
const prompt = (() => { try { return JSON.parse(body).parts.map(part => part.text ?? '').join('\n'); } catch { return body; } })();
|
|
699
|
+
silent.noteSilentTailPrompt(prompt);
|
|
700
|
+
if (silent.isFinalMessageNudge(prompt)) { await replyWithFinalNudge(silent); return; }
|
|
701
|
+
if (silent.isSilentTailScenario(prompt)) {
|
|
702
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
703
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-open', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.SILENT_TAIL_OPENING, time: { start: 1, end: 2 } } } });
|
|
704
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-gh', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_silent_gh', tool: 'bash', state: { status: 'completed', input: { command: 'gh issue create' }, output: 'created', time: { start: 1, end: 2 } } } } });
|
|
705
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-rsn', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'reasoning', text: silent.SILENT_TAIL_REASONING, time: { start: 1, end: 2 } } } });
|
|
706
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (silent.isToolTailScenario(prompt)) {
|
|
710
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
711
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'tool-tail-open', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.TOOL_TAIL_OPENING, time: { start: 1, end: 2 } } } });
|
|
712
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'tool-tail-git', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_tool_tail', tool: 'bash', state: { status: 'completed', input: { command: 'git status --short' }, output: ' M src/example.ts', time: { start: 1, end: 2 } } } } });
|
|
713
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
async function replyWithFinalNudge(silent) {
|
|
720
|
+
const kind = silent.finalMessageNudgeKind();
|
|
721
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
722
|
+
if (kind === 'silent') {
|
|
723
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-rsn', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'reasoning', text: silent.SILENT_TAIL_REASONING, time: { start: 1, end: 2 } } } });
|
|
724
|
+
} else {
|
|
725
|
+
if (kind === 'slow-done') {
|
|
726
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-prefix', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.SLOW_DONE_PREFIX, time: { start: 1, end: 2 } } } });
|
|
727
|
+
await silent.sleep(silent.SLOW_DONE_TAIL_MS);
|
|
309
728
|
}
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
type: 'text', text: 'Child review finished.',
|
|
481
|
-
time: { start: 1760000009200, end: 1760000009300 },
|
|
482
|
-
} } });
|
|
483
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
484
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
485
|
-
}) } });
|
|
486
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
487
|
-
}, 60);
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
// #723: native GET /skill diagnostic captured on 1.18.33; upstream
|
|
491
|
-
// packages/opencode/src/skill/index.ts:96-111 skips the unreadable skill.
|
|
492
|
-
if (body.includes('mock:skill-warning')) {
|
|
493
|
-
for (const root of ['.claude', '.agents']) send({ type: 'session.error', properties: {
|
|
494
|
-
error: { name: 'UnknownError', data: { message: `Failed to parse skill /home/agent/${root}/skills/pen-design/SKILL.md` } },
|
|
495
|
-
} });
|
|
496
|
-
}
|
|
497
|
-
if (body.includes('mock:unscoped-provider-failure') || body.includes('mock:scoped-skill-failure')) {
|
|
498
|
-
send({ type: 'session.error', properties: {
|
|
499
|
-
...(body.includes('mock:scoped-skill-failure') ? { sessionID: SESSION_ID } : {}),
|
|
500
|
-
error: { name: 'UnknownError', data: { message: body.includes('mock:scoped-skill-failure')
|
|
501
|
-
? 'Failed to parse skill /skills/required/SKILL.md' : 'provider unavailable — retry after restoring service' } },
|
|
502
|
-
} });
|
|
503
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
if (body.includes('mock:provider-error-early')) {
|
|
507
|
-
// #505 review: the provider rejects before any assistant message exists.
|
|
508
|
-
send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: { name: 'ProviderAuthError', data: { message: 'API key expired' } } } });
|
|
509
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
if (body.includes('mock:provider-error')) {
|
|
513
|
-
// The session-level error frame, wire shape copied verbatim from
|
|
514
|
-
// `__fixtures__/opencode/session-error.ndjson`. #53: a bare upstream
|
|
515
|
-
// rejection used to read like a missing executable, and the run parked
|
|
516
|
-
// as "Needs You" rather than failing (harness parity S7).
|
|
517
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
518
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
519
|
-
id: 'prt_mock_perr', messageID: MESSAGE_ID, sessionID: SESSION_ID,
|
|
520
|
-
type: 'text', text: 'Starting the review',
|
|
521
|
-
} } });
|
|
522
|
-
send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: {
|
|
523
|
-
name: 'ProviderAuthError',
|
|
524
|
-
data: { message: 'API key expired — run `opencode auth login`' },
|
|
525
|
-
} } });
|
|
526
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
if (body.includes('mock:split-text')) {
|
|
530
|
-
// OpenCode streams a text part as successive GROWING snapshots of the
|
|
531
|
-
// same part id; only the final one carries `time.end`. A runner emitting
|
|
532
|
-
// one v1 `text` per snapshot publishes the torn `…CEZ:` prefix, which is
|
|
533
|
-
// #2 on opencode's wire (harness parity S8).
|
|
534
|
-
const full = 'parity split text\n\nCEZ:MONITORING';
|
|
535
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
536
|
-
for (const upto of [17, 24, full.length]) {
|
|
537
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
538
|
-
id: 'prt_mock_split', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
539
|
-
text: full.slice(0, upto),
|
|
540
|
-
...(upto === full.length ? { time: { start: 1760000000500, end: 1760000000600 } } : {}),
|
|
541
|
-
} } });
|
|
542
|
-
}
|
|
543
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
544
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
545
|
-
}) } });
|
|
546
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
if (body.includes('mock:autonomous') || body.includes('Continue working autonomously until the task is fully complete.')) {
|
|
550
|
-
const { autonomousReply } = await import('./mock-autonomous.mjs');
|
|
551
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
552
|
-
send({ type: 'message.part.updated', properties: { part: { id: `autonomous-${++autonomousTurn}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: autonomousReply(JSON.parse(body).parts.map(part => part.text ?? '').join('\n')), time: { start: 1, end: 2 } } } });
|
|
553
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
if (body.includes('mock:silent-tail') || body.includes('mock:tool-tail') || body.includes('Your last turn ended without a message to the user.')) {
|
|
557
|
-
const silent = await import('./mock-silent-tail.mjs');
|
|
558
|
-
const prompt = (() => { try { return JSON.parse(body).parts.map(part => part.text ?? '').join('\n'); } catch { return body; } })();
|
|
559
|
-
silent.noteSilentTailPrompt(prompt);
|
|
560
|
-
if (silent.isFinalMessageNudge(prompt)) {
|
|
561
|
-
const kind = silent.finalMessageNudgeKind();
|
|
562
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
563
|
-
if (kind === 'silent') {
|
|
564
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-rsn', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'reasoning', text: silent.SILENT_TAIL_REASONING, time: { start: 1, end: 2 } } } });
|
|
565
|
-
} else {
|
|
566
|
-
if (kind === 'slow-done') {
|
|
567
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-prefix', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.SLOW_DONE_PREFIX, time: { start: 1, end: 2 } } } });
|
|
568
|
-
await silent.sleep(silent.SLOW_DONE_TAIL_MS);
|
|
569
|
-
}
|
|
570
|
-
const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
|
|
571
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 } } } });
|
|
572
|
-
}
|
|
573
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
if (silent.isSilentTailScenario(prompt)) {
|
|
577
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
578
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-open', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.SILENT_TAIL_OPENING, time: { start: 1, end: 2 } } } });
|
|
579
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-gh', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_silent_gh', tool: 'bash', state: { status: 'completed', input: { command: 'gh issue create' }, output: 'created', time: { start: 1, end: 2 } } } } });
|
|
580
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'silent-rsn', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'reasoning', text: silent.SILENT_TAIL_REASONING, time: { start: 1, end: 2 } } } });
|
|
581
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
if (silent.isToolTailScenario(prompt)) {
|
|
585
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
586
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'tool-tail-open', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: silent.TOOL_TAIL_OPENING, time: { start: 1, end: 2 } } } });
|
|
587
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'tool-tail-git', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_tool_tail', tool: 'bash', state: { status: 'completed', input: { command: 'git status --short' }, output: ' M src/example.ts', time: { start: 1, end: 2 } } } } });
|
|
588
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
// #401: one completed text snapshot, without an earlier streaming part.
|
|
593
|
-
if (body.includes('mock:ask-snapshot')) {
|
|
594
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
595
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'ask-snapshot', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: body.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"}]}]}', time: { start: 1, end: 2 } } } });
|
|
596
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
if (body.includes('mock:ask-prose')) {
|
|
600
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
601
|
-
send({ type: 'message.part.updated', properties: { part: { id: 'ask-prose', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Use `CEZ:ASK {"questions":[]}` in your reply.\n> CEZ:ASK {not valid json', time: { start: 1, end: 2 } } } });
|
|
602
|
-
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
if (body.includes('mock:ask-bad')) {
|
|
606
|
-
// Malformed: `questions` is not an array, so the runner must REJECT the
|
|
607
|
-
// native question and finish the turn rather than wait for an answer to
|
|
608
|
-
// a card that can never render (#6's rejection path, harness parity R4).
|
|
609
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
610
|
-
pendingQuestions.push({ id: 'q_mock_bad', sessionID: SESSION_ID });
|
|
611
|
-
sendQuestionPart({ questions: 'not-an-array' });
|
|
612
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 60);
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
if (body.includes('mock:ask')) {
|
|
616
|
-
lateQuestionReply = body.includes('mock:ask-reply-late');
|
|
617
|
-
answerDone = body.includes('mock:ask-reply-late-done') || body.includes('mock:resume-done');
|
|
618
|
-
// The native question tool (#6). No `session.idle` follows: a real ask
|
|
619
|
-
// holds the turn open until the answer is routed back.
|
|
620
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
621
|
-
pendingQuestions.push({ id: 'q_mock_1', sessionID: SESSION_ID });
|
|
622
|
-
sendQuestionPart({ questions: [{
|
|
623
|
-
header: 'Library',
|
|
624
|
-
question: 'Which test library?',
|
|
625
|
-
options: [
|
|
626
|
-
{ label: 'Vitest', description: 'Use the existing test runner' },
|
|
627
|
-
{ label: 'Node test', description: 'Use node:test' },
|
|
628
|
-
],
|
|
629
|
-
}] });
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
if (body.includes('mock:hold-done')) {
|
|
633
|
-
// Pause, then CEZ:DONE, so a mid-turn sendMessage can queue behind the
|
|
634
|
-
// running turn and race the close (#486).
|
|
635
|
-
await waitForMockRelease(400);
|
|
636
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
637
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
638
|
-
id: 'prt_mock_hold_done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
639
|
-
text: 'parity hold-done: content after the pause\n\nCEZ:DONE',
|
|
640
|
-
time: { start: 1760000000500, end: 1760000000600 },
|
|
641
|
-
} } });
|
|
642
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
643
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
644
|
-
}) } });
|
|
645
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
if (body.includes('mock:done')) {
|
|
649
|
-
// Declares the task complete so the run reaches cezar's review gate; a
|
|
650
|
-
// markerless turn-end correctly parks as `waiting` instead (harness
|
|
651
|
-
// parity R1).
|
|
652
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
653
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
654
|
-
id: 'prt_mock_done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
655
|
-
text: 'parity done: the task is complete\n\nCEZ:DONE',
|
|
656
|
-
time: { start: 1760000000500, end: 1760000000600 },
|
|
657
|
-
} } });
|
|
658
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
659
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
660
|
-
}) } });
|
|
661
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
if (body.includes('mock:hold-ask')) {
|
|
665
|
-
// Portable CEZ:ASK after a pause, so a mid-turn sendMessage can queue
|
|
666
|
-
// and then race the park (run.ts must discard that waiter).
|
|
667
|
-
await waitForMockRelease(250);
|
|
668
|
-
const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which date library should I standardize on?","options":[{"label":"date-fns","description":"Tree-shakeable, functional"},{"label":"Luxon","description":"Immutable, tz-aware"}]}]}';
|
|
669
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
670
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
671
|
-
id: 'prt_mock_hold_ask', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
672
|
-
text: ask,
|
|
673
|
-
time: { start: 1760000000500, end: 1760000000600 },
|
|
674
|
-
} } });
|
|
675
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
676
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
677
|
-
}) } });
|
|
678
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
if (watchdogStall(body)) return;
|
|
682
|
-
if (body.includes('mock:busy-progress')) {
|
|
683
|
-
let ticks = 0;
|
|
684
|
-
const timer = setInterval(() => {
|
|
685
|
-
send({ type: 'server.heartbeat', properties: {} });
|
|
686
|
-
if (++ticks === 24) { clearInterval(timer); send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }); }
|
|
687
|
-
}, 100);
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
|
-
if (body.includes('mock:hold-gated')) {
|
|
691
|
-
await waitForMockRelease(250);
|
|
692
|
-
const gated = 'parity hold-gated: content after the pause';
|
|
693
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
694
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
695
|
-
id: 'prt_mock_hold_gated', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: gated,
|
|
696
|
-
} } });
|
|
697
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
698
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
699
|
-
}) } });
|
|
700
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
if (body.includes('mock:hold')) {
|
|
704
|
-
// The response above is the ack (this is the #4 wire: `prompt_async`
|
|
705
|
-
// resolves immediately, the turn arrives over SSE afterwards). Holding
|
|
706
|
-
// the content behind it is what makes harness parity S2 meaningful.
|
|
707
|
-
setTimeout(() => {
|
|
708
|
-
const held = 'parity hold: content after the pause';
|
|
709
|
-
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
710
|
-
send({ type: 'message.part.updated', properties: { part: {
|
|
711
|
-
id: 'prt_mock_hold', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: held,
|
|
712
|
-
} } });
|
|
713
|
-
send({ type: 'message.updated', properties: { info: info({
|
|
714
|
-
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
715
|
-
}) } });
|
|
716
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
717
|
-
}, 250);
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
729
|
+
const text = kind === 'standing' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE;
|
|
730
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'silent-nudge-done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text, time: { start: 1, end: 2 } } } });
|
|
731
|
+
}
|
|
732
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async function replyWithSettledApproval(body) {
|
|
737
|
+
// P3/P4: OpenCode settles the ask itself, or the turn ends around it, once the test releases it.
|
|
738
|
+
const resolved = body.includes('mock:approval-resolved');
|
|
739
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
740
|
+
sendPermissionAsk('per_mock_1', 'bash', ['git status --short'], ['git status *']);
|
|
741
|
+
await waitForMockRelease();
|
|
742
|
+
pendingPermissionAsks.delete('per_mock_1');
|
|
743
|
+
if (resolved) send({ type: 'permission.replied', properties: { sessionID: SESSION_ID, requestID: 'per_mock_1', reply: 'once' } });
|
|
744
|
+
textPart('prt_approval_settled', resolved ? 'Native approval settled without a cockpit answer.\n\nCEZ:DONE'
|
|
745
|
+
: 'Turn ended with the approval still pending.\n\nCEZ:DONE');
|
|
746
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
async function replyWithFailedApprovalReply(body) {
|
|
751
|
+
failPermissionReplies = 1;
|
|
752
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
753
|
+
sendPermissionAsk('per_mock_1', 'bash', ['git status --short'], ['git status *']);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async function replyWithMalformedPermission(body) {
|
|
758
|
+
// P6: `patterns` must be an array; cezar rejects the ask by its id.
|
|
759
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
760
|
+
pendingPermissionAsks.add('per_mock_bad');
|
|
761
|
+
send({ type: 'permission.asked', properties: {
|
|
762
|
+
id: 'per_mock_bad', sessionID: SESSION_ID, permission: 'bash', patterns: 'git status --short', metadata: {},
|
|
763
|
+
always: ['git status *'], tool: { messageID: MESSAGE_ID, callID: 'call_mock_bash' },
|
|
764
|
+
} });
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async function replyWithClassifyingPermission(body) {
|
|
769
|
+
classifyPending = true;
|
|
770
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
771
|
+
sendPermissionAsk('per_mock_classify', 'bash', ['git status --short'], ['git status *']);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async function replyWithLatePermission(body) {
|
|
776
|
+
// P8: the ask lands right after the idle, inside the runner's auto-end delay. As in real
|
|
777
|
+
// OpenCode, it comes from a run the server started, which the answer resumes and its idle ends.
|
|
778
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
779
|
+
textPart('prt_late_done', 'The turn finished before the late request.\n\nCEZ:DONE');
|
|
780
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
781
|
+
sendPermissionAsk('per_mock_late', 'bash', ['git push'], ['git push *']);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async function replyWithDefaultPermission(body) {
|
|
786
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
787
|
+
sendPermissionAsk('per_mock_default', 'external_directory', ['/outside/*'], ['/outside/*']);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async function replyWithApproval(body) {
|
|
792
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
793
|
+
sendPermissionAsk('per_mock_1', 'bash', ['git status --short'], ['git status *']);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// #401: one completed text snapshot, without an earlier streaming part.
|
|
798
|
+
async function replyWithAskSnapshot(body) {
|
|
799
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
800
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'ask-snapshot', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: body.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"}]}]}', time: { start: 1, end: 2 } } } });
|
|
801
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
async function replyWithAskProse(body) {
|
|
806
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
807
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'ask-prose', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Use `CEZ:ASK {"questions":[]}` in your reply.\n> CEZ:ASK {not valid json', time: { start: 1, end: 2 } } } });
|
|
808
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
async function replyWithMalformedAsk(body) {
|
|
813
|
+
// Malformed: `questions` is not an array, so the runner must REJECT the
|
|
814
|
+
// native question and finish the turn rather than wait for an answer to
|
|
815
|
+
// a card that can never render (#6's rejection path, harness parity R4).
|
|
816
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
817
|
+
pendingQuestions.push({ id: 'q_mock_bad', sessionID: SESSION_ID });
|
|
818
|
+
sendQuestionPart({ questions: 'not-an-array' });
|
|
819
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 60);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async function replyWithAsk(body) {
|
|
824
|
+
lateQuestionReply = body.includes('mock:ask-reply-late');
|
|
825
|
+
answerDone = body.includes('mock:ask-reply-late-done') || body.includes('mock:resume-done');
|
|
826
|
+
// The native question tool (#6). No `session.idle` follows: a real ask
|
|
827
|
+
// holds the turn open until the answer is routed back.
|
|
828
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
829
|
+
pendingQuestions.push({ id: 'q_mock_1', sessionID: SESSION_ID });
|
|
830
|
+
sendQuestionPart({ questions: [{
|
|
831
|
+
header: 'Library',
|
|
832
|
+
question: 'Which test library?',
|
|
833
|
+
options: [
|
|
834
|
+
{ label: 'Vitest', description: 'Use the existing test runner' },
|
|
835
|
+
{ label: 'Node test', description: 'Use node:test' },
|
|
836
|
+
],
|
|
837
|
+
}] });
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
async function replyWithHeldDone(body) {
|
|
842
|
+
// Pause, then CEZ:DONE, so a mid-turn sendMessage can queue behind the
|
|
843
|
+
// running turn and race the close (#486).
|
|
844
|
+
await waitForMockRelease(400);
|
|
845
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
846
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
847
|
+
id: 'prt_mock_hold_done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
848
|
+
text: 'parity hold-done: content after the pause\n\nCEZ:DONE',
|
|
849
|
+
time: { start: 1760000000500, end: 1760000000600 },
|
|
850
|
+
} } });
|
|
851
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
852
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
853
|
+
}) } });
|
|
854
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async function replyWithDone(body) {
|
|
859
|
+
// Declares the task complete so the run reaches cezar's review gate; a
|
|
860
|
+
// markerless turn-end correctly parks as `waiting` instead (harness
|
|
861
|
+
// parity R1).
|
|
862
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
863
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
864
|
+
id: 'prt_mock_done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
865
|
+
text: 'parity done: the task is complete\n\nCEZ:DONE',
|
|
866
|
+
time: { start: 1760000000500, end: 1760000000600 },
|
|
867
|
+
} } });
|
|
868
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
869
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
870
|
+
}) } });
|
|
871
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
async function replyWithHeldAsk(body) {
|
|
876
|
+
// Portable CEZ:ASK after a pause, so a mid-turn sendMessage can queue
|
|
877
|
+
// and then race the park (run.ts must discard that waiter).
|
|
878
|
+
await waitForMockRelease(250);
|
|
879
|
+
const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which date library should I standardize on?","options":[{"label":"date-fns","description":"Tree-shakeable, functional"},{"label":"Luxon","description":"Immutable, tz-aware"}]}]}';
|
|
880
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
881
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
882
|
+
id: 'prt_mock_hold_ask', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
|
|
883
|
+
text: ask,
|
|
884
|
+
time: { start: 1760000000500, end: 1760000000600 },
|
|
885
|
+
} } });
|
|
886
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
887
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
888
|
+
}) } });
|
|
889
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function replyWithBusyProgress(body) {
|
|
894
|
+
let ticks = 0;
|
|
895
|
+
const timer = setInterval(() => {
|
|
896
|
+
send({ type: 'server.heartbeat', properties: {} });
|
|
897
|
+
if (++ticks === 24) {
|
|
898
|
+
clearInterval(timer);
|
|
720
899
|
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
part: { id: 'prt_mock_t1', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Checking the working tree.' },
|
|
725
|
-
},
|
|
726
|
-
});
|
|
727
|
-
send({
|
|
728
|
-
type: 'message.part.updated',
|
|
729
|
-
properties: {
|
|
730
|
-
part: {
|
|
731
|
-
id: 'prt_mock_c1',
|
|
732
|
-
messageID: MESSAGE_ID,
|
|
733
|
-
sessionID: SESSION_ID,
|
|
734
|
-
type: 'tool',
|
|
735
|
-
callID: 'call_mock_1',
|
|
736
|
-
tool: 'bash',
|
|
737
|
-
state: { status: 'pending', input: { command: 'git status --short' }, raw: '{}' },
|
|
738
|
-
},
|
|
739
|
-
},
|
|
740
|
-
});
|
|
741
|
-
send({
|
|
742
|
-
type: 'message.part.updated',
|
|
743
|
-
properties: {
|
|
744
|
-
part: {
|
|
745
|
-
id: 'prt_mock_c1',
|
|
746
|
-
messageID: MESSAGE_ID,
|
|
747
|
-
sessionID: SESSION_ID,
|
|
748
|
-
type: 'tool',
|
|
749
|
-
callID: 'call_mock_1',
|
|
750
|
-
tool: 'bash',
|
|
751
|
-
state: { status: 'running', input: { command: 'git status --short' }, title: 'git status --short', time: { start: 1760000000100 } },
|
|
752
|
-
},
|
|
753
|
-
},
|
|
754
|
-
});
|
|
755
|
-
send({
|
|
756
|
-
type: 'message.part.updated',
|
|
757
|
-
properties: {
|
|
758
|
-
part: {
|
|
759
|
-
id: 'prt_mock_c1',
|
|
760
|
-
messageID: MESSAGE_ID,
|
|
761
|
-
sessionID: SESSION_ID,
|
|
762
|
-
type: 'tool',
|
|
763
|
-
callID: 'call_mock_1',
|
|
764
|
-
tool: 'bash',
|
|
765
|
-
state: {
|
|
766
|
-
status: 'completed',
|
|
767
|
-
input: { command: 'git status --short' },
|
|
768
|
-
output: ' M src/example.ts\n',
|
|
769
|
-
title: 'git status --short',
|
|
770
|
-
metadata: { exit: 0 },
|
|
771
|
-
time: { start: 1760000000100, end: 1760000000400 },
|
|
772
|
-
},
|
|
773
|
-
},
|
|
774
|
-
},
|
|
775
|
-
});
|
|
776
|
-
send({
|
|
777
|
-
type: 'message.updated',
|
|
778
|
-
properties: {
|
|
779
|
-
info: info({ cost: 0.0021, tokens: { input: 1200, output: 300, reasoning: 0, cache: { read: 0, write: 0 } } }),
|
|
780
|
-
},
|
|
781
|
-
});
|
|
782
|
-
setTimeout(() => {
|
|
783
|
-
send({
|
|
784
|
-
type: 'message.part.updated',
|
|
785
|
-
properties: {
|
|
786
|
-
part: {
|
|
787
|
-
id: 'prt_mock_t2',
|
|
788
|
-
messageID: MESSAGE_ID,
|
|
789
|
-
sessionID: SESSION_ID,
|
|
790
|
-
type: 'text',
|
|
791
|
-
text: 'Done.',
|
|
792
|
-
time: { start: 1760000000500, end: 1760000000600 },
|
|
793
|
-
},
|
|
794
|
-
},
|
|
795
|
-
});
|
|
796
|
-
}, 30);
|
|
797
|
-
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 90);
|
|
798
|
-
return;
|
|
900
|
+
textPart('prt_busy_done', 'busy complete\n\nCEZ:DONE');
|
|
901
|
+
send({ type: 'message.updated', properties: { info: info({ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } } }) } });
|
|
902
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
799
903
|
}
|
|
800
|
-
|
|
801
|
-
|
|
904
|
+
}, 100);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
async function replyWithGatedHold(body) {
|
|
909
|
+
await waitForMockRelease(250);
|
|
910
|
+
const gated = 'parity hold-gated: content after the pause';
|
|
911
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
912
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
913
|
+
id: 'prt_mock_hold_gated', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: gated,
|
|
914
|
+
} } });
|
|
915
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
916
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
917
|
+
}) } });
|
|
918
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
async function replyWithHold(body) {
|
|
923
|
+
// The response above is the ack (this is the #4 wire: `prompt_async`
|
|
924
|
+
// resolves immediately, the turn arrives over SSE afterwards). Holding
|
|
925
|
+
// the content behind it is what makes harness parity S2 meaningful.
|
|
926
|
+
setTimeout(() => {
|
|
927
|
+
const held = 'parity hold: content after the pause';
|
|
928
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
929
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
930
|
+
id: 'prt_mock_hold', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: held,
|
|
931
|
+
} } });
|
|
932
|
+
send({ type: 'message.updated', properties: { info: info({
|
|
933
|
+
cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
934
|
+
}) } });
|
|
935
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
936
|
+
}, 250);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function acknowledgeWatchdog() {}
|
|
941
|
+
|
|
942
|
+
// #723: native GET /skill diagnostic captured on 1.18.33; upstream
|
|
943
|
+
// packages/opencode/src/skill/index.ts:96-111 skips the unreadable skill.
|
|
944
|
+
async function replyWithSkillWarning(body) {
|
|
945
|
+
for (const root of ['.claude', '.agents']) send({ type: 'session.error', properties: {
|
|
946
|
+
error: { name: 'UnknownError', data: { message: `Failed to parse skill /home/agent/${root}/skills/pen-design/SKILL.md` } },
|
|
947
|
+
} });
|
|
948
|
+
// hearsay-tools/cezarion#1265: unscoped FrontmatterError frame captured
|
|
949
|
+
// from `opencode serve` 1.18.35 on 2026-10-18; upstream
|
|
950
|
+
// skill/index.ts:105-116 skips the skill.
|
|
951
|
+
send({ type: 'session.error', properties: { error: { name: 'UnknownError', data: { message: FRONTMATTER_ERROR } } } });
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function replyWithFatalControl(body) {
|
|
955
|
+
const fatalControl = Object.entries({
|
|
956
|
+
'mock:scoped-skill-failure': { sessionID: SESSION_ID, message: 'Failed to parse skill /skills/required/SKILL.md' },
|
|
957
|
+
'mock:scoped-frontmatter-failure': { sessionID: SESSION_ID, message: FRONTMATTER_ERROR },
|
|
958
|
+
'mock:unscoped-provider-failure': { message: 'provider unavailable — retry after restoring service' },
|
|
959
|
+
}).find(([marker]) => body.includes(marker))?.[1];
|
|
960
|
+
if (fatalControl) {
|
|
961
|
+
send({ type: 'session.error', properties: {
|
|
962
|
+
...(fatalControl.sessionID ? { sessionID: fatalControl.sessionID } : {}),
|
|
963
|
+
error: { name: 'UnknownError', data: { message: fatalControl.message } },
|
|
964
|
+
} });
|
|
965
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
966
|
+
return true;
|
|
967
|
+
}
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
async function replyWithEarlyProviderError(body) {
|
|
972
|
+
// #505 review: the provider rejects before any assistant message exists.
|
|
973
|
+
send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: { name: 'ProviderAuthError', data: { message: 'API key expired' } } } });
|
|
974
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
async function replyWithNativeFailure(body) {
|
|
979
|
+
const selectedFailure = failureControl?.failureFor(body);
|
|
980
|
+
const nativeFailure = selectedFailure instanceof Promise ? await selectedFailure : selectedFailure;
|
|
981
|
+
if (failureControl ? nativeFailure === 'provider' : body.includes('mock:provider-error')) {
|
|
982
|
+
// The session-level error frame, wire shape copied verbatim from
|
|
983
|
+
// `__fixtures__/opencode/session-error.ndjson`. #53: a bare upstream
|
|
984
|
+
// rejection used to read like a missing executable, and the run parked
|
|
985
|
+
// as "Needs You" rather than failing (harness parity S7).
|
|
986
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
987
|
+
send({ type: 'message.part.updated', properties: { part: {
|
|
988
|
+
id: 'prt_mock_perr', messageID: MESSAGE_ID, sessionID: SESSION_ID,
|
|
989
|
+
type: 'text', text: 'Starting the review',
|
|
990
|
+
} } });
|
|
991
|
+
send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: {
|
|
992
|
+
name: 'ProviderAuthError',
|
|
993
|
+
data: { message: 'API key expired — run `opencode auth login`' },
|
|
994
|
+
} } });
|
|
995
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
996
|
+
return true;
|
|
997
|
+
}
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function replyWithOrdinaryTurn() {
|
|
1002
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
1003
|
+
send({
|
|
1004
|
+
type: 'message.part.updated',
|
|
1005
|
+
properties: {
|
|
1006
|
+
part: { id: 'prt_mock_t1', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Checking the working tree.' },
|
|
1007
|
+
},
|
|
802
1008
|
});
|
|
803
|
-
|
|
1009
|
+
send({
|
|
1010
|
+
type: 'message.part.updated',
|
|
1011
|
+
properties: {
|
|
1012
|
+
part: {
|
|
1013
|
+
id: 'prt_mock_c1',
|
|
1014
|
+
messageID: MESSAGE_ID,
|
|
1015
|
+
sessionID: SESSION_ID,
|
|
1016
|
+
type: 'tool',
|
|
1017
|
+
callID: 'call_mock_1',
|
|
1018
|
+
tool: 'bash',
|
|
1019
|
+
state: { status: 'pending', input: { command: 'git status --short' }, raw: '{}' },
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
});
|
|
1023
|
+
send({
|
|
1024
|
+
type: 'message.part.updated',
|
|
1025
|
+
properties: {
|
|
1026
|
+
part: {
|
|
1027
|
+
id: 'prt_mock_c1',
|
|
1028
|
+
messageID: MESSAGE_ID,
|
|
1029
|
+
sessionID: SESSION_ID,
|
|
1030
|
+
type: 'tool',
|
|
1031
|
+
callID: 'call_mock_1',
|
|
1032
|
+
tool: 'bash',
|
|
1033
|
+
state: { status: 'running', input: { command: 'git status --short' }, title: 'git status --short', time: { start: 1760000000100 } },
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
});
|
|
1037
|
+
send({
|
|
1038
|
+
type: 'message.part.updated',
|
|
1039
|
+
properties: {
|
|
1040
|
+
part: {
|
|
1041
|
+
id: 'prt_mock_c1',
|
|
1042
|
+
messageID: MESSAGE_ID,
|
|
1043
|
+
sessionID: SESSION_ID,
|
|
1044
|
+
type: 'tool',
|
|
1045
|
+
callID: 'call_mock_1',
|
|
1046
|
+
tool: 'bash',
|
|
1047
|
+
state: {
|
|
1048
|
+
status: 'completed',
|
|
1049
|
+
input: { command: 'git status --short' },
|
|
1050
|
+
output: ' M src/example.ts\n',
|
|
1051
|
+
title: 'git status --short',
|
|
1052
|
+
metadata: { exit: 0 },
|
|
1053
|
+
time: { start: 1760000000100, end: 1760000000400 },
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
send({
|
|
1059
|
+
type: 'message.updated',
|
|
1060
|
+
properties: {
|
|
1061
|
+
info: info({ cost: 0.0021, tokens: { input: 1200, output: 300, reasoning: 0, cache: { read: 0, write: 0 } } }),
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
setTimeout(() => {
|
|
1065
|
+
send({
|
|
1066
|
+
type: 'message.part.updated',
|
|
1067
|
+
properties: {
|
|
1068
|
+
part: {
|
|
1069
|
+
id: 'prt_mock_t2',
|
|
1070
|
+
messageID: MESSAGE_ID,
|
|
1071
|
+
sessionID: SESSION_ID,
|
|
1072
|
+
type: 'text',
|
|
1073
|
+
text: 'Done.',
|
|
1074
|
+
time: { start: 1760000000500, end: 1760000000600 },
|
|
1075
|
+
},
|
|
1076
|
+
},
|
|
1077
|
+
});
|
|
1078
|
+
}, 30);
|
|
1079
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 90);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
async function crashAfterAcknowledgement(body) {
|
|
1084
|
+
if (!body.includes('mock:crash-stderr')) return false;
|
|
1085
|
+
const { crashWithStderr } = await import('./mock-runner-crash.mjs');
|
|
1086
|
+
if (body.includes('mock:crash-stderr-clean')) crashWithStderr(body);
|
|
1087
|
+
else { setTimeout(() => crashWithStderr(body), 30); return true; }
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function recordSessionPrompt(body) {
|
|
1092
|
+
try {
|
|
1093
|
+
const text = body ? JSON.parse(body).parts?.map(part => part.text ?? '').join('\n') ?? '' : '';
|
|
1094
|
+
if (text) addPrompt(SESSION_ID, text);
|
|
1095
|
+
} catch {
|
|
1096
|
+
// Keep the scripted turn even when the body is not JSON.
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function deliveryPromptText(body, received) {
|
|
1101
|
+
// A configured control matches the prompt's text parts, never their JSON encoding.
|
|
1102
|
+
let deliveryText = body;
|
|
1103
|
+
// Both the prompt Cezar sent and a nudge's scripted answer match (hearsay-tools/cezarion#1525).
|
|
1104
|
+
const textOf = (raw) => JSON.parse(raw).parts.map(part => part.text ?? '').join('\n');
|
|
1105
|
+
if (delivery) try { deliveryText = received === body ? textOf(body) : `${textOf(received)}\n${textOf(body)}`; } catch { /* not a prompt body */ }
|
|
1106
|
+
return deliveryText;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
async function acknowledgeSteer(body, res) {
|
|
1110
|
+
const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
|
|
1111
|
+
const userId = `msg_steer_user_${++steerSerial}`;
|
|
1112
|
+
// #505 review: acknowledge a steer later than the runner's lost-wake window.
|
|
1113
|
+
const ackDelay = Number(process.env.CEZ_MOCK_OPENCODE_STEER_ACK_MS ?? 0);
|
|
1114
|
+
if (ackDelay > 0) setTimeout(() => { res.writeHead(204); res.end(); }, ackDelay);
|
|
1115
|
+
else { res.writeHead(204); res.end(); }
|
|
1116
|
+
send({ type: 'message.updated', properties: { info: { id: userId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
|
|
1117
|
+
send({ type: 'message.part.updated', properties: { part: { id: `prt_${userId}`, messageID: userId, sessionID: SESSION_ID, type: 'text', text } } });
|
|
1118
|
+
steers.push({ userId, text });
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function acknowledgePrompt(res, body, deliveryText) {
|
|
1123
|
+
// `prompt_async` semantics: acknowledge now, stream the turn over SSE.
|
|
1124
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
1125
|
+
// #426: a nudged turn can finish on SSE before its HTTP acceptance arrives.
|
|
1126
|
+
// Force that ordering in the cap scenario; the first opening stays ordinary.
|
|
1127
|
+
if (autonomousReadinessIdle && body.includes('Continue working autonomously until the task is fully complete.')) {
|
|
1128
|
+
// Keep the portable answer HTTP request unacknowledged while its SSE turn finishes.
|
|
1129
|
+
} else if (autonomousCap && body.includes('Continue working autonomously until the task is fully complete.')) {
|
|
1130
|
+
setTimeout(() => res.end(JSON.stringify({ info: info({}), parts: [] })), 100);
|
|
1131
|
+
} else if (body.includes('f7-delay-ack')) {
|
|
1132
|
+
// F7: SSE turn-end before HTTP ACK so parkAfterAck applies the silent-tail nudge.
|
|
1133
|
+
setTimeout(() => res.end(JSON.stringify({ info: info({}), parts: [] })), 250);
|
|
1134
|
+
} else {
|
|
1135
|
+
const ack = () => res.end(JSON.stringify({ info: info({}), parts: [] }));
|
|
1136
|
+
if (delivery) delivery.ack(deliveryText, ack); else ack();
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function needsSilentPreparation(body) {
|
|
1141
|
+
return body.includes('mock:silent-tail') || silentNudge(body);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
async function prepareSilentTail(body) {
|
|
1145
|
+
if (!needsSilentPreparation(body)) return false;
|
|
1146
|
+
const silent = await import('./mock-silent-tail.mjs');
|
|
1147
|
+
const prompt = (() => { try { return JSON.parse(body).parts.map(part => part.text ?? '').join('\n'); } catch { return body; } })();
|
|
1148
|
+
silent.noteSilentTailPrompt(prompt);
|
|
1149
|
+
if (silent.isAckOnlyNudge(prompt)) {
|
|
1150
|
+
const beats = setInterval(() => { if (sse) sse.write(': heartbeat\n\n'); }, 50);
|
|
1151
|
+
beats.unref?.();
|
|
1152
|
+
return true;
|
|
1153
|
+
}
|
|
1154
|
+
if (silent.isLateNudge(prompt)) await waitForMockRelease(silent.LATE_REPLY_MS);
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function rejectsPrompt(body, deliveryText) {
|
|
1159
|
+
return delivery?.kind === 'reject' ? delivery.rejects(deliveryText) : body.includes('mock:reject-agent-post');
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async function preparePromptTurn(res, url, body, received) {
|
|
1163
|
+
if (body.includes('mock:crash-stderr-pre-ack')) {
|
|
1164
|
+
const { crashWithStderr } = await import('./mock-runner-crash.mjs');
|
|
1165
|
+
crashWithStderr(body);
|
|
1166
|
+
return true;
|
|
1167
|
+
}
|
|
1168
|
+
const deliveryText = deliveryPromptText(body, received);
|
|
1169
|
+
// A configured rejection decides alone: it adds "once" and "until lifted" to the native token.
|
|
1170
|
+
if (rejectsPrompt(body, deliveryText)) {
|
|
1171
|
+
res.writeHead(503, { 'content-type': 'application/json' });
|
|
1172
|
+
res.end(JSON.stringify({ error: 'agent prompt rejected' }));
|
|
1173
|
+
return true;
|
|
1174
|
+
}
|
|
1175
|
+
if (heldBeats && url.endsWith('/prompt_async')) { clearInterval(heldBeats); heldBeats = undefined; turnActive = false; }
|
|
1176
|
+
if (turnActive && url.endsWith('/prompt_async')) { await acknowledgeSteer(body, res); return true; }
|
|
1177
|
+
// A prompt posted while a follow-on run is due runs after it.
|
|
1178
|
+
if (followOn) await followOn;
|
|
1179
|
+
turnActive = true;
|
|
1180
|
+
acknowledgePrompt(res, body, deliveryText);
|
|
1181
|
+
if (delivery) await delivery.holdResponse(deliveryText);
|
|
1182
|
+
// `mock:ack-only` → prompt_async is acknowledged and the session stays busy on heartbeats
|
|
1183
|
+
// with no turn until the next prompt, which it then runs (hearsay-tools/cezarion#1301).
|
|
1184
|
+
if (body.includes('mock:ack-only')) {
|
|
1185
|
+
heldBeats = setInterval(() => { if (sse) sse.write(': heartbeat\n\n'); }, 50);
|
|
1186
|
+
heldBeats.unref?.();
|
|
1187
|
+
return true;
|
|
1188
|
+
}
|
|
1189
|
+
if (body.includes('mock:autonomous-readiness-idle')) autonomousReadinessIdle = true;
|
|
1190
|
+
if (body.includes('mock:autonomous-cap') || body.includes('mock:autonomous-ask-cap')) autonomousCap = true;
|
|
1191
|
+
if (await prepareSilentTail(body)) return true;
|
|
1192
|
+
return false;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
async function streamPrompt(url, body) {
|
|
1196
|
+
// hearsay-tools/cezarion#1112: the acknowledged prompt streams its turn late.
|
|
1197
|
+
if (body.includes('mock:deferred-turn-start')) await (await import('./mock-deferred-turn-start.mjs')).deferredTurnStart();
|
|
1198
|
+
if (url.endsWith('/prompt_async')) {
|
|
1199
|
+
currentUserId = `msg_user_${++steerSerial}`;
|
|
1200
|
+
const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
|
|
1201
|
+
send({ type: 'message.updated', properties: { info: { id: currentUserId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
|
|
1202
|
+
send({ type: 'message.part.updated', properties: { part: { id: `prt_${currentUserId}`, messageID: currentUserId, sessionID: SESSION_ID, type: 'text', text } } });
|
|
1203
|
+
}
|
|
1204
|
+
if (await crashAfterAcknowledgement(body)) return;
|
|
1205
|
+
if (await dispatchReply(earlyReplies, body)) return;
|
|
1206
|
+
if (body.includes('mock:skill-warning')) await replyWithSkillWarning(body);
|
|
1207
|
+
if (replyWithFatalControl(body)) return;
|
|
1208
|
+
if (body.includes('mock:provider-error-early')) { await replyWithEarlyProviderError(body); return; }
|
|
1209
|
+
if (await replyWithNativeFailure(body)) return;
|
|
1210
|
+
if (await dispatchReply(lateReplies, body)) return;
|
|
1211
|
+
replyWithOrdinaryTurn();
|
|
1212
|
+
}
|
|
804
1213
|
|
|
805
1214
|
// Test sessions share a host: let the OS allocate a port instead of racing
|
|
806
1215
|
// the runner’s random requested port against other fixtures.
|
|
@@ -808,6 +1217,12 @@ server.listen(0, hostname, () => {
|
|
|
808
1217
|
// The runner reads the bound URL back from stdout, like the real server.
|
|
809
1218
|
console.log(`opencode server listening on http://${hostname}:${server.address().port}`);
|
|
810
1219
|
});
|
|
1220
|
+
// hearsay-tools/cezarion#1449: the server leads its own session, so a test process that dies
|
|
1221
|
+
// before its cleanup leaves it running. The runner never writes to or closes this stdin pipe,
|
|
1222
|
+
// so end of input means the parent died (SIGKILL included). Exit directly, past any SIGTERM
|
|
1223
|
+
// handler a shutdown scenario replaced.
|
|
1224
|
+
process.stdin.on('end', () => process.exit(0));
|
|
1225
|
+
process.stdin.resume();
|
|
811
1226
|
process.on('SIGTERM', () => {
|
|
812
1227
|
// Expose the end-request/process-exit interval to the root idle regression.
|
|
813
1228
|
if (autonomousReadinessIdle) setTimeout(() => process.exit(0), 250);
|