@wjarka/cezarion 0.10.0-pr30.54
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 +21 -0
- package/README.md +838 -0
- package/dist/agent-config/account-identity.d.ts +44 -0
- package/dist/agent-config/account-identity.js +128 -0
- package/dist/agent-config/account-identity.js.map +1 -0
- package/dist/agent-config/catalog.d.ts +72 -0
- package/dist/agent-config/catalog.js +223 -0
- package/dist/agent-config/catalog.js.map +1 -0
- package/dist/agent-config/files.d.ts +46 -0
- package/dist/agent-config/files.js +119 -0
- package/dist/agent-config/files.js.map +1 -0
- package/dist/agent-config/model-settings/claude.d.ts +2 -0
- package/dist/agent-config/model-settings/claude.js +11 -0
- package/dist/agent-config/model-settings/claude.js.map +1 -0
- package/dist/agent-config/model-settings/codex.d.ts +2 -0
- package/dist/agent-config/model-settings/codex.js +16 -0
- package/dist/agent-config/model-settings/codex.js.map +1 -0
- package/dist/agent-config/model-settings/opencode.d.ts +2 -0
- package/dist/agent-config/model-settings/opencode.js +8 -0
- package/dist/agent-config/model-settings/opencode.js.map +1 -0
- package/dist/agent-config/model-settings/pi.d.ts +12 -0
- package/dist/agent-config/model-settings/pi.js +18 -0
- package/dist/agent-config/model-settings/pi.js.map +1 -0
- package/dist/agent-config/model-settings/shared.d.ts +9 -0
- package/dist/agent-config/model-settings/shared.js +93 -0
- package/dist/agent-config/model-settings/shared.js.map +1 -0
- package/dist/agent-config/model-settings/types.d.ts +9 -0
- package/dist/agent-config/model-settings/types.js +2 -0
- package/dist/agent-config/model-settings/types.js.map +1 -0
- package/dist/agent-config/models.d.ts +9 -0
- package/dist/agent-config/models.js +32 -0
- package/dist/agent-config/models.js.map +1 -0
- package/dist/agent-config/seed.d.ts +7 -0
- package/dist/agent-config/seed.js +90 -0
- package/dist/agent-config/seed.js.map +1 -0
- package/dist/agent-config/service.d.ts +44 -0
- package/dist/agent-config/service.js +71 -0
- package/dist/agent-config/service.js.map +1 -0
- package/dist/agent-config/validate.d.ts +21 -0
- package/dist/agent-config/validate.js +76 -0
- package/dist/agent-config/validate.js.map +1 -0
- package/dist/automations/coordinator.d.ts +26 -0
- package/dist/automations/coordinator.js +66 -0
- package/dist/automations/coordinator.js.map +1 -0
- package/dist/automations/github-poller.d.ts +74 -0
- package/dist/automations/github-poller.js +234 -0
- package/dist/automations/github-poller.js.map +1 -0
- package/dist/automations/scheduler.d.ts +42 -0
- package/dist/automations/scheduler.js +192 -0
- package/dist/automations/scheduler.js.map +1 -0
- package/dist/automations/store.d.ts +65 -0
- package/dist/automations/store.js +298 -0
- package/dist/automations/store.js.map +1 -0
- package/dist/automations/task-template.d.ts +18 -0
- package/dist/automations/task-template.js +95 -0
- package/dist/automations/task-template.js.map +1 -0
- package/dist/automations/types.d.ts +255 -0
- package/dist/automations/types.js +159 -0
- package/dist/automations/types.js.map +1 -0
- package/dist/config.d.ts +77 -0
- package/dist/config.js +242 -0
- package/dist/config.js.map +1 -0
- package/dist/contract/agent-config.d.ts +153 -0
- package/dist/contract/agent-profiles.d.ts +347 -0
- package/dist/contract/automations.d.ts +935 -0
- package/dist/contract/events.d.ts +104 -0
- package/dist/contract/github.d.ts +556 -0
- package/dist/contract/health.d.ts +102 -0
- package/dist/contract/index.d.ts +16 -0
- package/dist/contract/index.js +1895 -0
- package/dist/contract/projects.d.ts +210 -0
- package/dist/contract/repo.d.ts +234 -0
- package/dist/contract/runs.d.ts +1473 -0
- package/dist/contract/skills.d.ts +232 -0
- package/dist/contract/workflows.d.ts +218 -0
- package/dist/contract/workspace.d.ts +579 -0
- package/dist/core/agent-env.d.ts +31 -0
- package/dist/core/agent-env.js +371 -0
- package/dist/core/agent-env.js.map +1 -0
- package/dist/core/agent-model-policy.d.ts +10 -0
- package/dist/core/agent-model-policy.js +36 -0
- package/dist/core/agent-model-policy.js.map +1 -0
- package/dist/core/agent-profiles.d.ts +62 -0
- package/dist/core/agent-profiles.js +90 -0
- package/dist/core/agent-profiles.js.map +1 -0
- package/dist/core/agent-runner.d.ts +211 -0
- package/dist/core/agent-runner.js +68 -0
- package/dist/core/agent-runner.js.map +1 -0
- package/dist/core/ask.d.ts +105 -0
- package/dist/core/ask.js +187 -0
- package/dist/core/ask.js.map +1 -0
- package/dist/core/backend-detect.d.ts +16 -0
- package/dist/core/backend-detect.js +155 -0
- package/dist/core/backend-detect.js.map +1 -0
- package/dist/core/claude-cli-runner.d.ts +59 -0
- package/dist/core/claude-cli-runner.js +483 -0
- package/dist/core/claude-cli-runner.js.map +1 -0
- package/dist/core/claude-ui-mapper.d.ts +80 -0
- package/dist/core/claude-ui-mapper.js +598 -0
- package/dist/core/claude-ui-mapper.js.map +1 -0
- package/dist/core/codex-app-server-runner.d.ts +41 -0
- package/dist/core/codex-app-server-runner.js +584 -0
- package/dist/core/codex-app-server-runner.js.map +1 -0
- package/dist/core/codex-app-server-transport.d.ts +35 -0
- package/dist/core/codex-app-server-transport.js +150 -0
- package/dist/core/codex-app-server-transport.js.map +1 -0
- package/dist/core/codex-model-catalog.d.ts +10 -0
- package/dist/core/codex-model-catalog.js +110 -0
- package/dist/core/codex-model-catalog.js.map +1 -0
- package/dist/core/codex-ui-mapper.d.ts +112 -0
- package/dist/core/codex-ui-mapper.js +766 -0
- package/dist/core/codex-ui-mapper.js.map +1 -0
- package/dist/core/model-identity.d.ts +125 -0
- package/dist/core/model-identity.js +132 -0
- package/dist/core/model-identity.js.map +1 -0
- package/dist/core/model-presets.d.ts +21 -0
- package/dist/core/model-presets.js +81 -0
- package/dist/core/model-presets.js.map +1 -0
- package/dist/core/ndjson.d.ts +6 -0
- package/dist/core/ndjson.js +23 -0
- package/dist/core/ndjson.js.map +1 -0
- package/dist/core/opencode-model-catalog.d.ts +32 -0
- package/dist/core/opencode-model-catalog.js +153 -0
- package/dist/core/opencode-model-catalog.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +32 -0
- package/dist/core/opencode-server-runner.js +882 -0
- package/dist/core/opencode-server-runner.js.map +1 -0
- package/dist/core/opencode-ui-mapper.d.ts +137 -0
- package/dist/core/opencode-ui-mapper.js +800 -0
- package/dist/core/opencode-ui-mapper.js.map +1 -0
- package/dist/core/pi-model-catalog.d.ts +31 -0
- package/dist/core/pi-model-catalog.js +152 -0
- package/dist/core/pi-model-catalog.js.map +1 -0
- package/dist/core/pi-runner.d.ts +24 -0
- package/dist/core/pi-runner.js +403 -0
- package/dist/core/pi-runner.js.map +1 -0
- package/dist/core/pi-ui-mapper.d.ts +36 -0
- package/dist/core/pi-ui-mapper.js +304 -0
- package/dist/core/pi-ui-mapper.js.map +1 -0
- package/dist/core/process-usage.d.ts +66 -0
- package/dist/core/process-usage.js +193 -0
- package/dist/core/process-usage.js.map +1 -0
- package/dist/core/provider-auth.d.ts +154 -0
- package/dist/core/provider-auth.js +509 -0
- package/dist/core/provider-auth.js.map +1 -0
- package/dist/core/provider-availability.d.ts +3 -0
- package/dist/core/provider-availability.js +14 -0
- package/dist/core/provider-availability.js.map +1 -0
- package/dist/core/runner-factory.d.ts +8 -0
- package/dist/core/runner-factory.js +25 -0
- package/dist/core/runner-factory.js.map +1 -0
- package/dist/core/runner-model-catalog.d.ts +29 -0
- package/dist/core/runner-model-catalog.js +78 -0
- package/dist/core/runner-model-catalog.js.map +1 -0
- package/dist/core/secret-redaction.d.ts +37 -0
- package/dist/core/secret-redaction.js +115 -0
- package/dist/core/secret-redaction.js.map +1 -0
- package/dist/core/shell-env.d.ts +28 -0
- package/dist/core/shell-env.js +56 -0
- package/dist/core/shell-env.js.map +1 -0
- package/dist/core/tool-display.d.ts +31 -0
- package/dist/core/tool-display.js +179 -0
- package/dist/core/tool-display.js.map +1 -0
- package/dist/core/ui-events.d.ts +335 -0
- package/dist/core/ui-events.js +25 -0
- package/dist/core/ui-events.js.map +1 -0
- package/dist/core/usage-limit.d.ts +42 -0
- package/dist/core/usage-limit.js +209 -0
- package/dist/core/usage-limit.js.map +1 -0
- package/dist/core/usage.d.ts +11 -0
- package/dist/core/usage.js +15 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/v1-text-coalescer.d.ts +36 -0
- package/dist/core/v1-text-coalescer.js +72 -0
- package/dist/core/v1-text-coalescer.js.map +1 -0
- package/dist/git-diff-base.d.ts +85 -0
- package/dist/git-diff-base.js +180 -0
- package/dist/git-diff-base.js.map +1 -0
- package/dist/git-refs.d.ts +11 -0
- package/dist/git-refs.js +14 -0
- package/dist/git-refs.js.map +1 -0
- package/dist/git-worktree.d.ts +161 -0
- package/dist/git-worktree.js +508 -0
- package/dist/git-worktree.js.map +1 -0
- package/dist/handoff.d.ts +62 -0
- package/dist/handoff.js +154 -0
- package/dist/handoff.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +680 -0
- package/dist/index.js.map +1 -0
- package/dist/install-as-command.d.ts +40 -0
- package/dist/install-as-command.js +52 -0
- package/dist/install-as-command.js.map +1 -0
- package/dist/pack-check.d.ts +17 -0
- package/dist/pack-check.js +27 -0
- package/dist/pack-check.js.map +1 -0
- package/dist/paths.d.ts +140 -0
- package/dist/paths.js +192 -0
- package/dist/paths.js.map +1 -0
- package/dist/planner.d.ts +27 -0
- package/dist/planner.js +268 -0
- package/dist/planner.js.map +1 -0
- package/dist/release/manifests.d.ts +81 -0
- package/dist/release/manifests.js +74 -0
- package/dist/release/manifests.js.map +1 -0
- package/dist/release/snapshot.d.ts +67 -0
- package/dist/release/snapshot.js +97 -0
- package/dist/release/snapshot.js.map +1 -0
- package/dist/release/stable.d.ts +38 -0
- package/dist/release/stable.js +55 -0
- package/dist/release/stable.js.map +1 -0
- package/dist/runs/agent-tmpdir.d.ts +41 -0
- package/dist/runs/agent-tmpdir.js +183 -0
- package/dist/runs/agent-tmpdir.js.map +1 -0
- package/dist/runs/auto-name.d.ts +75 -0
- package/dist/runs/auto-name.js +179 -0
- package/dist/runs/auto-name.js.map +1 -0
- package/dist/runs/event-history.d.ts +50 -0
- package/dist/runs/event-history.js +613 -0
- package/dist/runs/event-history.js.map +1 -0
- package/dist/runs/retention.d.ts +60 -0
- package/dist/runs/retention.js +86 -0
- package/dist/runs/retention.js.map +1 -0
- package/dist/runs/review-gate.d.ts +18 -0
- package/dist/runs/review-gate.js +21 -0
- package/dist/runs/review-gate.js.map +1 -0
- package/dist/runs/run-index.d.ts +19 -0
- package/dist/runs/run-index.js +43 -0
- package/dist/runs/run-index.js.map +1 -0
- package/dist/runs/store.d.ts +446 -0
- package/dist/runs/store.js +1118 -0
- package/dist/runs/store.js.map +1 -0
- package/dist/runs/task-markers.d.ts +28 -0
- package/dist/runs/task-markers.js +74 -0
- package/dist/runs/task-markers.js.map +1 -0
- package/dist/runs/task-refs.d.ts +25 -0
- package/dist/runs/task-refs.js +74 -0
- package/dist/runs/task-refs.js.map +1 -0
- package/dist/runs/ui-event-sink.d.ts +81 -0
- package/dist/runs/ui-event-sink.js +210 -0
- package/dist/runs/ui-event-sink.js.map +1 -0
- package/dist/server/app-type.d.ts +21 -0
- package/dist/server/app-type.js +2 -0
- package/dist/server/app-type.js.map +1 -0
- package/dist/server/capabilities.d.ts +70 -0
- package/dist/server/capabilities.js +152 -0
- package/dist/server/capabilities.js.map +1 -0
- package/dist/server/checkout.d.ts +128 -0
- package/dist/server/checkout.js +296 -0
- package/dist/server/checkout.js.map +1 -0
- package/dist/server/forge/github.d.ts +513 -0
- package/dist/server/forge/github.js +2364 -0
- package/dist/server/forge/github.js.map +1 -0
- package/dist/server/forge/index.d.ts +40 -0
- package/dist/server/forge/index.js +72 -0
- package/dist/server/forge/index.js.map +1 -0
- package/dist/server/forge/types.d.ts +242 -0
- package/dist/server/forge/types.js +2 -0
- package/dist/server/forge/types.js.map +1 -0
- package/dist/server/fs-browse.d.ts +105 -0
- package/dist/server/fs-browse.js +182 -0
- package/dist/server/fs-browse.js.map +1 -0
- package/dist/server/git-changes.d.ts +213 -0
- package/dist/server/git-changes.js +555 -0
- package/dist/server/git-changes.js.map +1 -0
- package/dist/server/git.d.ts +28 -0
- package/dist/server/git.js +112 -0
- package/dist/server/git.js.map +1 -0
- package/dist/server/github.d.ts +9 -0
- package/dist/server/github.js +8 -0
- package/dist/server/github.js.map +1 -0
- package/dist/server/launch-key.d.ts +7 -0
- package/dist/server/launch-key.js +33 -0
- package/dist/server/launch-key.js.map +1 -0
- package/dist/server/open-in-app.d.ts +66 -0
- package/dist/server/open-in-app.js +211 -0
- package/dist/server/open-in-app.js.map +1 -0
- package/dist/server/open-in-terminal.d.ts +70 -0
- package/dist/server/open-in-terminal.js +179 -0
- package/dist/server/open-in-terminal.js.map +1 -0
- package/dist/server/pr.d.ts +7 -0
- package/dist/server/pr.js +7 -0
- package/dist/server/pr.js.map +1 -0
- package/dist/server/project-context.d.ts +120 -0
- package/dist/server/project-context.js +182 -0
- package/dist/server/project-context.js.map +1 -0
- package/dist/server/provider-action-gate.d.ts +8 -0
- package/dist/server/provider-action-gate.js +56 -0
- package/dist/server/provider-action-gate.js.map +1 -0
- package/dist/server/provider-auth-runtime.d.ts +21 -0
- package/dist/server/provider-auth-runtime.js +66 -0
- package/dist/server/provider-auth-runtime.js.map +1 -0
- package/dist/server/server.d.ts +13138 -0
- package/dist/server/server.js +5263 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/static-ui.d.ts +49 -0
- package/dist/server/static-ui.js +92 -0
- package/dist/server/static-ui.js.map +1 -0
- package/dist/server/validators.d.ts +97 -0
- package/dist/server/validators.js +86 -0
- package/dist/server/validators.js.map +1 -0
- package/dist/server/ws.d.ts +85 -0
- package/dist/server/ws.js +218 -0
- package/dist/server/ws.js.map +1 -0
- package/dist/server/wsl.d.ts +25 -0
- package/dist/server/wsl.js +92 -0
- package/dist/server/wsl.js.map +1 -0
- package/dist/server-install/engine.d.ts +50 -0
- package/dist/server-install/engine.js +324 -0
- package/dist/server-install/engine.js.map +1 -0
- package/dist/server-install/platforms/macosx-ngrok.d.ts +6 -0
- package/dist/server-install/platforms/macosx-ngrok.js +345 -0
- package/dist/server-install/platforms/macosx-ngrok.js.map +1 -0
- package/dist/server-install/platforms/ubuntu-vps.d.ts +78 -0
- package/dist/server-install/platforms/ubuntu-vps.js +1016 -0
- package/dist/server-install/platforms/ubuntu-vps.js.map +1 -0
- package/dist/server-install/state.d.ts +52 -0
- package/dist/server-install/state.js +182 -0
- package/dist/server-install/state.js.map +1 -0
- package/dist/server-install/steps.d.ts +109 -0
- package/dist/server-install/steps.js +334 -0
- package/dist/server-install/steps.js.map +1 -0
- package/dist/server-install/strategies.d.ts +4 -0
- package/dist/server-install/strategies.js +20 -0
- package/dist/server-install/strategies.js.map +1 -0
- package/dist/server-install/types.d.ts +287 -0
- package/dist/server-install/types.js +121 -0
- package/dist/server-install/types.js.map +1 -0
- package/dist/server-install/ui.d.ts +42 -0
- package/dist/server-install/ui.js +130 -0
- package/dist/server-install/ui.js.map +1 -0
- package/dist/skills-banner.d.ts +22 -0
- package/dist/skills-banner.js +41 -0
- package/dist/skills-banner.js.map +1 -0
- package/dist/skills-remote.d.ts +84 -0
- package/dist/skills-remote.js +468 -0
- package/dist/skills-remote.js.map +1 -0
- package/dist/skills-update.d.ts +88 -0
- package/dist/skills-update.js +392 -0
- package/dist/skills-update.js.map +1 -0
- package/dist/skills.d.ts +82 -0
- package/dist/skills.js +251 -0
- package/dist/skills.js.map +1 -0
- package/dist/todos.d.ts +48 -0
- package/dist/todos.js +201 -0
- package/dist/todos.js.map +1 -0
- package/dist/ui-state.d.ts +26 -0
- package/dist/ui-state.js +38 -0
- package/dist/ui-state.js.map +1 -0
- package/dist/update-check.d.ts +12 -0
- package/dist/update-check.js +41 -0
- package/dist/update-check.js.map +1 -0
- package/dist/workflows/load.d.ts +15 -0
- package/dist/workflows/load.js +56 -0
- package/dist/workflows/load.js.map +1 -0
- package/dist/workflows/run.d.ts +758 -0
- package/dist/workflows/run.js +3389 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows/types.d.ts +153 -0
- package/dist/workflows/types.js +212 -0
- package/dist/workflows/types.js.map +1 -0
- package/dist/workspace/agent-accounts.d.ts +153 -0
- package/dist/workspace/agent-accounts.js +304 -0
- package/dist/workspace/agent-accounts.js.map +1 -0
- package/dist/workspace/agent-profiles.d.ts +78 -0
- package/dist/workspace/agent-profiles.js +115 -0
- package/dist/workspace/agent-profiles.js.map +1 -0
- package/dist/workspace/config.d.ts +169 -0
- package/dist/workspace/config.js +379 -0
- package/dist/workspace/config.js.map +1 -0
- package/dist/workspace/migrations.d.ts +45 -0
- package/dist/workspace/migrations.js +147 -0
- package/dist/workspace/migrations.js.map +1 -0
- package/dist/workspace/projects-cli.d.ts +25 -0
- package/dist/workspace/projects-cli.js +172 -0
- package/dist/workspace/projects-cli.js.map +1 -0
- package/dist/workspace/projects.d.ts +109 -0
- package/dist/workspace/projects.js +250 -0
- package/dist/workspace/projects.js.map +1 -0
- package/dist/workspace/semaphore.d.ts +179 -0
- package/dist/workspace/semaphore.js +195 -0
- package/dist/workspace/semaphore.js.map +1 -0
- package/dist/workspace/ui-state.d.ts +34 -0
- package/dist/workspace/ui-state.js +53 -0
- package/dist/workspace/ui-state.js.map +1 -0
- package/package.json +70 -0
- package/scripts/check-pack.mjs +46 -0
- package/scripts/inline-contract.mjs +112 -0
- package/scripts/install-as-command.mjs +120 -0
- package/scripts/mock-claude.mjs +520 -0
- package/scripts/mock-pi-rpc.mjs +83 -0
- package/scripts/sync-readme.mjs +20 -0
- package/scripts/test-process-usage.mjs +47 -0
- package/web/dist/assets/alert-dialog-B4Ofsgv7.js +1 -0
- package/web/dist/assets/arrow-down-Dy8dup7j.js +1 -0
- package/web/dist/assets/arrow-left-BJaucbhn.js +1 -0
- package/web/dist/assets/bundle-mjs-BT31bpU6.js +1 -0
- package/web/dist/assets/centered-state-duPW0phQ.js +43 -0
- package/web/dist/assets/chevron-right-Be4A5TCS.js +1 -0
- package/web/dist/assets/chunk-BO2N2NFS-DE6qKn3r.js +131 -0
- package/web/dist/assets/collapsible-DdcNBdSk.js +1 -0
- package/web/dist/assets/commit-list-CgvN9vec.js +1 -0
- package/web/dist/assets/compare-variants-CQA2nd65.js +1 -0
- package/web/dist/assets/core-BCsw8oQw.js +12 -0
- package/web/dist/assets/css-BsVw1vtW.js +1 -0
- package/web/dist/assets/diff-B6Agp5Hl.js +3 -0
- package/web/dist/assets/diff-stat-BoXuGkgq.js +1 -0
- package/web/dist/assets/diff-view-CD8XI-fj.js +6 -0
- package/web/dist/assets/diff-woXpYk--.js +1 -0
- package/web/dist/assets/dropdown-menu-B3SYw-k5.js +1 -0
- package/web/dist/assets/editable-title-B-hjp5wl.js +1 -0
- package/web/dist/assets/ellipsis-vertical-BYwzw0xl.js +1 -0
- package/web/dist/assets/engine-javascript-FQvzRocB.js +141 -0
- package/web/dist/assets/file-BRj5eISx.js +1 -0
- package/web/dist/assets/folder-z4oT5981.js +1 -0
- package/web/dist/assets/git-toolbar-owSEkYld.js +1 -0
- package/web/dist/assets/github-Bz2VgRX3.js +1 -0
- package/web/dist/assets/go-rLFTqkRN.js +1 -0
- package/web/dist/assets/highlighted-body-OFNGDK62-BL-MfFgl.js +1 -0
- package/web/dist/assets/highlighter-CdHqIwFr.js +3 -0
- package/web/dist/assets/html-BY76lXLH.js +1 -0
- package/web/dist/assets/image-preview-h3WFluuy.js +1 -0
- package/web/dist/assets/index-BbcQNT92.css +2 -0
- package/web/dist/assets/index-EtQQQ6Kj.js +7 -0
- package/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/web/dist/assets/javascript-BgS3c2Ky.js +1 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/web/dist/assets/json-qhed-kSA.js +1 -0
- package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/web/dist/assets/jsx-Bz0zcwM4.js +1 -0
- package/web/dist/assets/lib-BQXq3kEf.js +1 -0
- package/web/dist/assets/lib-BxQXEXDF.js +1 -0
- package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
- package/web/dist/assets/markdown-DqBKYPiB.js +2 -0
- package/web/dist/assets/mermaid-GHXKKRXX-C4jxqUUb.js +1 -0
- package/web/dist/assets/new-task-form-BdJKRBJX.js +1 -0
- package/web/dist/assets/pill-BQfmfpYp.js +1 -0
- package/web/dist/assets/project-router-DxgFZY1l.js +1 -0
- package/web/dist/assets/prompt-templates-B6m16gq-.js +15 -0
- package/web/dist/assets/python-gzcpVVnB.js +1 -0
- package/web/dist/assets/react-runtime-CCIEwYL0.js +9 -0
- package/web/dist/assets/refresh-cw-mhSpxf2a.js +1 -0
- package/web/dist/assets/repo-git-CUNRxR3_.js +1 -0
- package/web/dist/assets/rolldown-runtime-QTnfLwEv.js +1 -0
- package/web/dist/assets/run-diff-DsmmDVfx.js +3 -0
- package/web/dist/assets/run-header-BkhrseLJ.js +1 -0
- package/web/dist/assets/rust-Cfkwpbl8.js +1 -0
- package/web/dist/assets/search-x-BqDMB2g8.js +1 -0
- package/web/dist/assets/shellscript-CLZ0U2zV.js +1 -0
- package/web/dist/assets/skill-empty-hint-BWEvTggc.js +1 -0
- package/web/dist/assets/skills-9oR_oHse.js +1 -0
- package/web/dist/assets/skills-DwIxnnWO.js +1 -0
- package/web/dist/assets/sparkles-DIoTQpyx.js +1 -0
- package/web/dist/assets/sql-BsFa4tDR.js +1 -0
- package/web/dist/assets/square-terminal-Cc7CuQxc.js +1 -0
- package/web/dist/assets/tab-link-D1fQgpmO.js +1 -0
- package/web/dist/assets/task-changes-BKeLlDpK.js +1 -0
- package/web/dist/assets/task-commits-gcEFZIa5.js +1 -0
- package/web/dist/assets/task-files-BGAqoXTW.js +2 -0
- package/web/dist/assets/task-thread-DT8aUYTS.js +9 -0
- package/web/dist/assets/textarea-BOVbfy5C.js +1 -0
- package/web/dist/assets/thread-loading-CypW73KD.js +1 -0
- package/web/dist/assets/toml-CcmNWLt0.js +1 -0
- package/web/dist/assets/trash-2-CJq6LHWn.js +1 -0
- package/web/dist/assets/triangle-alert-CjDFvU27.js +1 -0
- package/web/dist/assets/tsx-udAQXfEw.js +1 -0
- package/web/dist/assets/typescript-bsJCZSQ-.js +1 -0
- package/web/dist/assets/upload-CPDZlJeK.js +1 -0
- package/web/dist/assets/use-desktop-5JsLmi0a.js +1 -0
- package/web/dist/assets/use-submit-shortcut-CHVFevty.js +1 -0
- package/web/dist/assets/utils-DiO6zyJd.js +64 -0
- package/web/dist/assets/workflows-rQDFzWRt.js +11 -0
- package/web/dist/assets/yaml-rwi0_p6S.js +1 -0
- package/web/dist/assets/zoomable-image-COWqb11v.js +1 -0
- package/web/dist/index.html +80 -0
- package/web/dist/open-mercato.svg +11 -0
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure opencode SSE-bus → protocol-v2 mapper. `mapOpencodeEvent` folds one
|
|
3
|
+
* parsed `{type, properties}` bus event into `UiEvent`s plus the next mapper
|
|
4
|
+
* state; the runner calls it ALONGSIDE the v1 path (v1 events keep flowing
|
|
5
|
+
* unchanged — including v1's HTTP-response-synthesized `turn-end`; only the
|
|
6
|
+
* v2 stream uses the correct `session.idle` signal, fixing gap §5.9).
|
|
7
|
+
*
|
|
8
|
+
* Contract: `.ai/analysis/cockpit-ui-redesign/agent-event-protocols.md` §4
|
|
9
|
+
* (wire format: Message/Part model, ToolState lifecycle) and §7.1
|
|
10
|
+
* "OpenCode (SSE)" (the mapping). Golden fixtures replaying wire-faithful
|
|
11
|
+
* bus-event sequences live in `__fixtures__/opencode/`.
|
|
12
|
+
*
|
|
13
|
+
* Robustness rule: input is untrusted wire data — the mapper never throws;
|
|
14
|
+
* unknown event/part types map to zero events.
|
|
15
|
+
*
|
|
16
|
+
* State is explicit and treated as immutable: callers thread the returned
|
|
17
|
+
* `state` into the next call. Ids are deterministic — items reuse the wire
|
|
18
|
+
* part ids, turns get `turn_<n>` minted at each prompt POST — so replaying
|
|
19
|
+
* a stored transcript reproduces the exact event sequence.
|
|
20
|
+
*
|
|
21
|
+
* Opencode-specific mechanics baked in:
|
|
22
|
+
* - text/reasoning parts carry the FULL accumulated text on every update;
|
|
23
|
+
* a per-part cursor diffs it into true `item.delta`s (a server-provided
|
|
24
|
+
* `delta` field wins when present — newer servers send it);
|
|
25
|
+
* - tool parts have the ONLY real `pending` phase of the three backends
|
|
26
|
+
* (`pending→pending`, `running→running`, `completed→completed`,
|
|
27
|
+
* `error→failed`);
|
|
28
|
+
* - the SSE bus is server-wide: parts from a foreign session id are a
|
|
29
|
+
* subtask's child session and nest via `parentItemId` (§7.1). A `subtask`
|
|
30
|
+
* part or a top-level `task` tool call opens a scope: the task tool names
|
|
31
|
+
* its child (`metadata.sessionId`) and binds to it outright, while a
|
|
32
|
+
* scope with no wire link queues and child sessions claim queued scopes
|
|
33
|
+
* first-in-first-out as they are heard from (#5). Closed children are
|
|
34
|
+
* tombstoned for the mapper's lifetime; anything foreign outside a
|
|
35
|
+
* subtask scope is dropped.
|
|
36
|
+
*/
|
|
37
|
+
import { toolDisplay } from './tool-display.js';
|
|
38
|
+
export function createOpencodeUiState() {
|
|
39
|
+
return {
|
|
40
|
+
sessionId: null,
|
|
41
|
+
sessionStarted: false,
|
|
42
|
+
turnSeq: 0,
|
|
43
|
+
currentTurnId: null,
|
|
44
|
+
currentTurnMessageIds: new Set(),
|
|
45
|
+
turnErrored: false,
|
|
46
|
+
msgRoles: new Map(),
|
|
47
|
+
cursors: new Map(),
|
|
48
|
+
startedItems: new Set(),
|
|
49
|
+
endedItems: new Set(),
|
|
50
|
+
tools: new Map(),
|
|
51
|
+
lastPlanJson: null,
|
|
52
|
+
subtasks: new Map(),
|
|
53
|
+
unboundSubtasks: [],
|
|
54
|
+
closedSessions: new Set(),
|
|
55
|
+
usageByMessage: new Map(),
|
|
56
|
+
lastUsage: null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Opencode has no wire-level session-start event — the session is created by
|
|
61
|
+
* the runner's `POST /session`, so the runner calls this once the response
|
|
62
|
+
* carries the id (like codex's out-of-band thread/start result).
|
|
63
|
+
*/
|
|
64
|
+
export function opencodeSessionStarted(sessionId, state) {
|
|
65
|
+
if (state.sessionStarted || sessionId === '')
|
|
66
|
+
return { events: [], state };
|
|
67
|
+
return {
|
|
68
|
+
events: [{ type: 'session.started', sessionId, backend: 'opencode' }],
|
|
69
|
+
state: { ...state, sessionId, sessionStarted: true },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Opencode has no wire-level turn-start either — the runner POSTing a prompt
|
|
74
|
+
* IS the turn boundary, so the runner calls this on each prompt POST. The
|
|
75
|
+
* matching `turn.completed` comes from the wire `session.idle` (never from
|
|
76
|
+
* the HTTP prompt response — that is v1's known fidelity gap).
|
|
77
|
+
*/
|
|
78
|
+
export function opencodeTurnStarted(state) {
|
|
79
|
+
const turnId = `turn_${state.turnSeq + 1}`;
|
|
80
|
+
return {
|
|
81
|
+
events: [{ type: 'turn.started', turnId }],
|
|
82
|
+
state: {
|
|
83
|
+
...state,
|
|
84
|
+
turnSeq: state.turnSeq + 1,
|
|
85
|
+
currentTurnId: turnId,
|
|
86
|
+
currentTurnMessageIds: new Set(),
|
|
87
|
+
turnErrored: false,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Fold one parsed SSE bus event into v2 events. Never throws. */
|
|
92
|
+
export function mapOpencodeEvent(evt, state) {
|
|
93
|
+
if (!isRecord(evt) || typeof evt.type !== 'string')
|
|
94
|
+
return { events: [], state };
|
|
95
|
+
const props = isRecord(evt.properties) ? evt.properties : {};
|
|
96
|
+
switch (evt.type) {
|
|
97
|
+
case 'message.updated':
|
|
98
|
+
case 'message.created':
|
|
99
|
+
case 'message.completed':
|
|
100
|
+
return mapMessageInfo(props, state);
|
|
101
|
+
case 'message.part.updated':
|
|
102
|
+
case 'message.part.created':
|
|
103
|
+
return mapPart(props, state);
|
|
104
|
+
case 'session.idle':
|
|
105
|
+
return mapIdle(props, state);
|
|
106
|
+
case 'session.error':
|
|
107
|
+
return mapSessionError(props, state);
|
|
108
|
+
default:
|
|
109
|
+
// server.connected, session.updated, message.part.removed,
|
|
110
|
+
// permission.* (reserved for the permission.* events later), …
|
|
111
|
+
return { events: [], state };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ---- message info → roles + usage.updated -----------------------------------
|
|
115
|
+
function mapMessageInfo(props, state) {
|
|
116
|
+
const info = isRecord(props.info) ? props.info : props;
|
|
117
|
+
const id = str(info.id);
|
|
118
|
+
const role = str(info.role);
|
|
119
|
+
let next = state;
|
|
120
|
+
const messageSession = str(info.sessionID);
|
|
121
|
+
const foreign = messageSession !== undefined && state.sessionId !== null && messageSession !== state.sessionId;
|
|
122
|
+
if (foreign && messageSession !== undefined) {
|
|
123
|
+
next = resolveSubtask(messageSession, next).state;
|
|
124
|
+
}
|
|
125
|
+
if (id !== undefined && role !== undefined && state.msgRoles.get(id) !== role) {
|
|
126
|
+
const msgRoles = new Map(state.msgRoles);
|
|
127
|
+
msgRoles.set(id, role);
|
|
128
|
+
next = { ...next, msgRoles };
|
|
129
|
+
}
|
|
130
|
+
if (id !== undefined && role === 'assistant' && next.currentTurnId !== null && !next.currentTurnMessageIds.has(id)) {
|
|
131
|
+
next = { ...next, currentTurnMessageIds: new Set(next.currentTurnMessageIds).add(id) };
|
|
132
|
+
}
|
|
133
|
+
// Telemetry rides only on assistant messages (cumulative per message).
|
|
134
|
+
if (id !== undefined && role === 'assistant') {
|
|
135
|
+
const usage = tokensToUsage(info.tokens);
|
|
136
|
+
const cost = num(info.cost);
|
|
137
|
+
if (usage || cost !== undefined) {
|
|
138
|
+
const prev = next.usageByMessage.get(id) ?? EMPTY_MESSAGE_USAGE;
|
|
139
|
+
const merged = {
|
|
140
|
+
// Monotonic guard: a stale/zero snapshot never shrinks the record.
|
|
141
|
+
info: usage && (prev.info === null || usage.total >= prev.info.total) ? usage : prev.info,
|
|
142
|
+
infoCost: maxCost(prev.infoCost, cost),
|
|
143
|
+
steps: prev.steps,
|
|
144
|
+
stepsCost: prev.stepsCost,
|
|
145
|
+
};
|
|
146
|
+
const usageByMessage = new Map(next.usageByMessage);
|
|
147
|
+
usageByMessage.set(id, merged);
|
|
148
|
+
return emitUsage({ ...next, usageByMessage });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { events: [], state: next };
|
|
152
|
+
}
|
|
153
|
+
// ---- parts -------------------------------------------------------------------
|
|
154
|
+
function mapPart(props, state) {
|
|
155
|
+
const part = isRecord(props.part) ? props.part : props;
|
|
156
|
+
const type = str(part.type);
|
|
157
|
+
const messageID = str(part.messageID);
|
|
158
|
+
const id = str(part.id) ?? messageID;
|
|
159
|
+
if (type === undefined || id === undefined)
|
|
160
|
+
return { events: [], state };
|
|
161
|
+
// Foreign-session parts nest only under the subtask bound to that child
|
|
162
|
+
// session. A newly seen child claims the oldest pending subtask; with none
|
|
163
|
+
// pending the part has no home, so the event is dropped.
|
|
164
|
+
const partSession = str(part.sessionID);
|
|
165
|
+
const foreign = partSession !== undefined && state.sessionId !== null && partSession !== state.sessionId;
|
|
166
|
+
const resolved = foreign && partSession !== undefined ? resolveSubtask(partSession, state) : undefined;
|
|
167
|
+
if (foreign && resolved?.subtask === undefined)
|
|
168
|
+
return { events: [], state };
|
|
169
|
+
state = resolved?.state ?? state;
|
|
170
|
+
const parentItemId = resolved?.subtask?.id;
|
|
171
|
+
// Only assistant parts surface — the user's own prompt streams as parts
|
|
172
|
+
// over the same feed. Role is known early (message.updated precedes its
|
|
173
|
+
// parts); an unknown role means "not assistant yet" → skip.
|
|
174
|
+
if (messageID !== undefined && state.msgRoles.get(messageID) !== 'assistant') {
|
|
175
|
+
return { events: [], state };
|
|
176
|
+
}
|
|
177
|
+
switch (type) {
|
|
178
|
+
case 'text':
|
|
179
|
+
return mapTextLike(part, id, 'text', props.delta, parentItemId, state);
|
|
180
|
+
case 'reasoning':
|
|
181
|
+
return mapTextLike(part, id, 'reasoning', props.delta, parentItemId, state);
|
|
182
|
+
case 'tool':
|
|
183
|
+
return mapTool(part, id, parentItemId, state);
|
|
184
|
+
case 'patch':
|
|
185
|
+
return mapPatch(part, id, parentItemId, state);
|
|
186
|
+
case 'subtask':
|
|
187
|
+
return mapSubtask(part, id, parentItemId, state);
|
|
188
|
+
case 'step-finish':
|
|
189
|
+
return mapStepFinish(part, messageID, state);
|
|
190
|
+
default:
|
|
191
|
+
// file, snapshot, step-start, agent, retry, compaction, … — nothing to
|
|
192
|
+
// render yet.
|
|
193
|
+
return { events: [], state };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Text/reasoning parts carry the FULL accumulated text each update — the
|
|
197
|
+
* cursor diffs it into true deltas; a server-sent `delta` wins when present.
|
|
198
|
+
* `time.end` marks the part finished → `item.completed` (once). */
|
|
199
|
+
function mapTextLike(part, id, field, serverDelta, parentItemId, state) {
|
|
200
|
+
const full = typeof part.text === 'string' ? part.text : '';
|
|
201
|
+
const events = [];
|
|
202
|
+
let startedItems = state.startedItems;
|
|
203
|
+
let cursors = state.cursors;
|
|
204
|
+
let endedItems = state.endedItems;
|
|
205
|
+
const makeItem = (text) => {
|
|
206
|
+
const item = field === 'text'
|
|
207
|
+
? { kind: 'message', id, role: 'assistant', text }
|
|
208
|
+
: { kind: 'reasoning', id, text };
|
|
209
|
+
if (parentItemId !== undefined)
|
|
210
|
+
item.parentItemId = parentItemId;
|
|
211
|
+
return item;
|
|
212
|
+
};
|
|
213
|
+
if (!startedItems.has(id)) {
|
|
214
|
+
events.push({ type: 'item.started', item: makeItem('') });
|
|
215
|
+
startedItems = new Set(startedItems).add(id);
|
|
216
|
+
}
|
|
217
|
+
const cursor = cursors.get(id) ?? 0;
|
|
218
|
+
const winning = str(serverDelta);
|
|
219
|
+
let delta;
|
|
220
|
+
let nextCursor = cursor;
|
|
221
|
+
if (winning !== undefined) {
|
|
222
|
+
delta = winning;
|
|
223
|
+
// Keep the cursor consistent with the accumulated text so a later
|
|
224
|
+
// full-text-only update doesn't re-emit what the server delta covered.
|
|
225
|
+
nextCursor = Math.max(cursor + winning.length, full.length);
|
|
226
|
+
}
|
|
227
|
+
else if (full.length > cursor) {
|
|
228
|
+
delta = full.slice(cursor);
|
|
229
|
+
nextCursor = full.length;
|
|
230
|
+
}
|
|
231
|
+
if (delta !== undefined) {
|
|
232
|
+
events.push({ type: 'item.delta', itemId: id, field, delta });
|
|
233
|
+
const nextCursors = new Map(cursors);
|
|
234
|
+
nextCursors.set(id, nextCursor);
|
|
235
|
+
cursors = nextCursors;
|
|
236
|
+
}
|
|
237
|
+
const time = isRecord(part.time) ? part.time : undefined;
|
|
238
|
+
if (time && num(time.end) !== undefined && !endedItems.has(id)) {
|
|
239
|
+
events.push({ type: 'item.completed', item: makeItem(full) });
|
|
240
|
+
endedItems = new Set(endedItems).add(id);
|
|
241
|
+
}
|
|
242
|
+
if (events.length === 0)
|
|
243
|
+
return { events, state };
|
|
244
|
+
return { events, state: { ...state, startedItems, cursors, endedItems } };
|
|
245
|
+
}
|
|
246
|
+
/** The §7.1 direct status map — opencode is the only backend with a real
|
|
247
|
+
* pending phase, and its `error` state is v2's `failed`. */
|
|
248
|
+
const STATUS_MAP = {
|
|
249
|
+
pending: 'pending',
|
|
250
|
+
running: 'running',
|
|
251
|
+
completed: 'completed',
|
|
252
|
+
error: 'failed',
|
|
253
|
+
};
|
|
254
|
+
function mapTool(part, id, parentItemId, state) {
|
|
255
|
+
const name = str(part.tool) ?? str(part.name) ?? 'tool';
|
|
256
|
+
const toolState = isRecord(part.state) ? part.state : {};
|
|
257
|
+
const prev = state.tools.get(id);
|
|
258
|
+
const mapped = typeof toolState.status === 'string' ? STATUS_MAP[toolState.status] : undefined;
|
|
259
|
+
const status = mapped ?? prev?.status ?? 'pending';
|
|
260
|
+
const display = toolDisplay(name, toolState.input);
|
|
261
|
+
// The running/completed states carry a live human title ("npm test") — the
|
|
262
|
+
// wire word wins over the derived one (§7.1). The error state has no title
|
|
263
|
+
// field (§4.2), so the last LIVE title carries over instead of regressing.
|
|
264
|
+
// A derived title is re-derived: the pending snapshot's input is often `{}`
|
|
265
|
+
// (arguments still parsing), and "Task" must become "Task: Dig in" once
|
|
266
|
+
// the running snapshot brings the description.
|
|
267
|
+
const wireTitle = str(toolState.title);
|
|
268
|
+
const title = wireTitle ?? (prev?.live ? prev.title : display.title);
|
|
269
|
+
const live = wireTitle !== undefined || (prev?.live ?? false);
|
|
270
|
+
const item = { kind: 'tool', id, name, toolKind: display.toolKind, title, status };
|
|
271
|
+
if (toolState.input !== undefined)
|
|
272
|
+
item.input = toolState.input;
|
|
273
|
+
if (status === 'completed') {
|
|
274
|
+
const output = str(toolState.output);
|
|
275
|
+
if (output !== undefined)
|
|
276
|
+
item.output = output;
|
|
277
|
+
}
|
|
278
|
+
if (status === 'failed') {
|
|
279
|
+
const error = errorText(toolState.error);
|
|
280
|
+
if (error !== undefined)
|
|
281
|
+
item.error = error;
|
|
282
|
+
}
|
|
283
|
+
const metadata = isRecord(toolState.metadata) ? toolState.metadata : undefined;
|
|
284
|
+
const exitCode = metadata ? num(metadata.exit) : undefined;
|
|
285
|
+
if (exitCode !== undefined)
|
|
286
|
+
item.exitCode = exitCode;
|
|
287
|
+
if (parentItemId !== undefined)
|
|
288
|
+
item.parentItemId = parentItemId;
|
|
289
|
+
const events = [];
|
|
290
|
+
const settled = status === 'completed' || status === 'failed';
|
|
291
|
+
if (prev === undefined) {
|
|
292
|
+
// First sight — a part already settled is a lone final snapshot
|
|
293
|
+
// (consumers upsert by id, so it renders fine).
|
|
294
|
+
events.push({ type: settled ? 'item.completed' : 'item.started', item });
|
|
295
|
+
}
|
|
296
|
+
else if (prev.status !== status) {
|
|
297
|
+
events.push({ type: settled ? 'item.completed' : 'item.updated', item });
|
|
298
|
+
}
|
|
299
|
+
else if (prev.title !== title && !settled) {
|
|
300
|
+
// Same state, new live title (running-state metadata updates).
|
|
301
|
+
events.push({ type: 'item.updated', item });
|
|
302
|
+
}
|
|
303
|
+
let next = state;
|
|
304
|
+
if (events.length > 0) {
|
|
305
|
+
const tools = new Map(state.tools);
|
|
306
|
+
tools.set(id, { status, title, live });
|
|
307
|
+
next = { ...next, tools };
|
|
308
|
+
}
|
|
309
|
+
// A task tool call spawns a child session exactly like a `subtask` part
|
|
310
|
+
// does (#5). When the snapshot names that child (`metadata.sessionId`,
|
|
311
|
+
// published as soon as the child exists) the scope binds to it outright;
|
|
312
|
+
// otherwise it queues for the next child session heard from. A task issued
|
|
313
|
+
// from inside a child session opens a scope of its own, nested under the
|
|
314
|
+
// outer one, so its grandchild's output has a home too.
|
|
315
|
+
if (isSubtaskTool(name)) {
|
|
316
|
+
const childSession = metadata ? (str(metadata.sessionId) ?? str(metadata.sessionID)) : undefined;
|
|
317
|
+
const scope = {
|
|
318
|
+
id,
|
|
319
|
+
name,
|
|
320
|
+
title,
|
|
321
|
+
...(item.input !== undefined ? { input: item.input } : {}),
|
|
322
|
+
...(childSession !== undefined ? { childSession } : {}),
|
|
323
|
+
...(parentItemId !== undefined ? { parentItemId } : {}),
|
|
324
|
+
};
|
|
325
|
+
if (!settled) {
|
|
326
|
+
// OpenCode publishes the part before its arguments finish parsing, so
|
|
327
|
+
// the first snapshot is often `{}` and later ones fill the input in —
|
|
328
|
+
// sometimes without moving the status or the derived title, i.e. with
|
|
329
|
+
// no item event. The scope follows every snapshot regardless, or the
|
|
330
|
+
// child's idle would complete it with stale metadata.
|
|
331
|
+
next = prev === undefined ? { ...next, unboundSubtasks: [...next.unboundSubtasks, scope] } : refreshScope(scope, next);
|
|
332
|
+
if (childSession !== undefined)
|
|
333
|
+
next = bindScope(scope, childSession, next);
|
|
334
|
+
}
|
|
335
|
+
else if (events.length > 0) {
|
|
336
|
+
// The tool's own settlement is authoritative (it carries the output), so
|
|
337
|
+
// its scope is released: a later child idle must not re-complete it. A
|
|
338
|
+
// child the wire named is closed too, even one that never spoke —
|
|
339
|
+
// otherwise its late first event would take a sibling's scope.
|
|
340
|
+
next = releaseScope(id, childSession, next);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// todowrite IS the plan (full-replacement semantics, deduplicated).
|
|
344
|
+
if (name.toLowerCase() === 'todowrite') {
|
|
345
|
+
const entries = planEntriesOf(toolState.input);
|
|
346
|
+
if (entries) {
|
|
347
|
+
const json = JSON.stringify(entries);
|
|
348
|
+
if (json !== next.lastPlanJson) {
|
|
349
|
+
events.push({ type: 'plan.updated', entries });
|
|
350
|
+
next = { ...next, lastPlanJson: json };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (events.length === 0 && next === state)
|
|
355
|
+
return { events, state };
|
|
356
|
+
return { events, state: next };
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* todowrite input `{todos:[{content,status,priority}]}` → plan entries
|
|
360
|
+
* (an empty list is a valid plan; malformed rows are filtered).
|
|
361
|
+
*
|
|
362
|
+
* `status` is a free-form string upstream (`Schema.String`, not an enum) — its
|
|
363
|
+
* documented vocabulary is pending|in_progress|completed|cancelled. Rows with
|
|
364
|
+
* an unrecognized status fall back to `pending` rather than being dropped: a
|
|
365
|
+
* todo the agent wrote must stay on screen, and dropping it silently shrinks
|
|
366
|
+
* the plan mid-run for no visible reason.
|
|
367
|
+
*
|
|
368
|
+
* A `pending` tool part carries `input: {}` (opencode publishes the part before
|
|
369
|
+
* the arguments finish parsing), which lands on the `!Array.isArray` guard and
|
|
370
|
+
* maps to zero events — so a streaming part cannot clobber a good plan.
|
|
371
|
+
*/
|
|
372
|
+
function planEntriesOf(input) {
|
|
373
|
+
if (!isRecord(input) || !Array.isArray(input.todos))
|
|
374
|
+
return undefined;
|
|
375
|
+
const entries = [];
|
|
376
|
+
for (const todo of input.todos) {
|
|
377
|
+
if (!isRecord(todo) || typeof todo.content !== 'string')
|
|
378
|
+
continue;
|
|
379
|
+
const status = PLAN_STATUSES.find((s) => s === todo.status) ?? 'pending';
|
|
380
|
+
const entry = { content: todo.content, status };
|
|
381
|
+
if (todo.priority === 'high' || todo.priority === 'medium' || todo.priority === 'low') {
|
|
382
|
+
entry.priority = todo.priority;
|
|
383
|
+
}
|
|
384
|
+
entries.push(entry);
|
|
385
|
+
}
|
|
386
|
+
return entries;
|
|
387
|
+
}
|
|
388
|
+
const PLAN_STATUSES = ['pending', 'in_progress', 'completed', 'cancelled'];
|
|
389
|
+
/** `patch` parts snapshot applied file changes → a completed edit-tool item
|
|
390
|
+
* carrying `diffs` (unified when the wire includes content; the older
|
|
391
|
+
* paths-only shape still yields per-file entries + locations). */
|
|
392
|
+
function mapPatch(part, id, parentItemId, state) {
|
|
393
|
+
if (state.endedItems.has(id))
|
|
394
|
+
return { events: [], state };
|
|
395
|
+
const artifacts = patchArtifacts(part.files);
|
|
396
|
+
if (!artifacts)
|
|
397
|
+
return { events: [], state };
|
|
398
|
+
const { diffs, locations } = artifacts;
|
|
399
|
+
const label = locations.length === 1 ? locations[0]?.path : locations.length > 1 ? `${locations.length} files` : undefined;
|
|
400
|
+
const item = {
|
|
401
|
+
kind: 'tool',
|
|
402
|
+
id,
|
|
403
|
+
name: 'patch',
|
|
404
|
+
toolKind: 'edit',
|
|
405
|
+
title: label !== undefined ? `Edit ${label}` : 'Edit',
|
|
406
|
+
status: 'completed',
|
|
407
|
+
diffs,
|
|
408
|
+
locations,
|
|
409
|
+
};
|
|
410
|
+
if (parentItemId !== undefined)
|
|
411
|
+
item.parentItemId = parentItemId;
|
|
412
|
+
return {
|
|
413
|
+
events: [{ type: 'item.completed', item }],
|
|
414
|
+
state: { ...state, endedItems: new Set(state.endedItems).add(id) },
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
/** `files` as `{path: unifiedDiff}` record (or `[{path, diff}]`) carries the
|
|
418
|
+
* content; the paths-only `string[]` shape still yields diff entries.
|
|
419
|
+
* Opencode sends unified diffs only — old text is unknown, never "empty". */
|
|
420
|
+
function patchArtifacts(files) {
|
|
421
|
+
const diffs = [];
|
|
422
|
+
const push = (path, unified) => {
|
|
423
|
+
const diff = { path, oldText: null };
|
|
424
|
+
if (unified !== undefined)
|
|
425
|
+
diff.unified = unified;
|
|
426
|
+
diffs.push(diff);
|
|
427
|
+
};
|
|
428
|
+
if (Array.isArray(files)) {
|
|
429
|
+
for (const entry of files) {
|
|
430
|
+
if (typeof entry === 'string' && entry !== '')
|
|
431
|
+
push(entry);
|
|
432
|
+
else if (isRecord(entry)) {
|
|
433
|
+
const path = str(entry.path) ?? str(entry.file) ?? str(entry.filename);
|
|
434
|
+
if (path !== undefined)
|
|
435
|
+
push(path, str(entry.diff) ?? str(entry.patch));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else if (isRecord(files)) {
|
|
440
|
+
for (const [path, value] of Object.entries(files)) {
|
|
441
|
+
if (path === '')
|
|
442
|
+
continue;
|
|
443
|
+
if (typeof value === 'string')
|
|
444
|
+
push(path, str(value));
|
|
445
|
+
else if (isRecord(value))
|
|
446
|
+
push(path, str(value.diff) ?? str(value.patch));
|
|
447
|
+
else
|
|
448
|
+
push(path);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (diffs.length === 0)
|
|
452
|
+
return undefined;
|
|
453
|
+
return { diffs, locations: diffs.map((d) => ({ path: d.path })) };
|
|
454
|
+
}
|
|
455
|
+
/** `subtask` parts spawn a child session → a task-kind tool item whose id
|
|
456
|
+
* scopes subsequent foreign-session items via `parentItemId` (§7.1); the
|
|
457
|
+
* child's `session.idle` completes it. */
|
|
458
|
+
function mapSubtask(part, id, parentItemId, state) {
|
|
459
|
+
if (state.startedItems.has(id))
|
|
460
|
+
return { events: [], state };
|
|
461
|
+
const description = str(part.description);
|
|
462
|
+
const display = toolDisplay('task', { description });
|
|
463
|
+
const item = {
|
|
464
|
+
kind: 'tool',
|
|
465
|
+
id,
|
|
466
|
+
name: 'subtask',
|
|
467
|
+
toolKind: display.toolKind,
|
|
468
|
+
title: display.title,
|
|
469
|
+
status: 'running',
|
|
470
|
+
};
|
|
471
|
+
const input = {};
|
|
472
|
+
if (str(part.prompt) !== undefined)
|
|
473
|
+
input.prompt = part.prompt;
|
|
474
|
+
if (description !== undefined)
|
|
475
|
+
input.description = description;
|
|
476
|
+
if (str(part.agent) !== undefined)
|
|
477
|
+
input.agent = part.agent;
|
|
478
|
+
if (Object.keys(input).length > 0)
|
|
479
|
+
item.input = input;
|
|
480
|
+
if (parentItemId !== undefined)
|
|
481
|
+
item.parentItemId = parentItemId;
|
|
482
|
+
const scope = {
|
|
483
|
+
id,
|
|
484
|
+
name: item.name,
|
|
485
|
+
title: item.title,
|
|
486
|
+
...(item.input !== undefined ? { input: item.input } : {}),
|
|
487
|
+
...(parentItemId !== undefined ? { parentItemId } : {}),
|
|
488
|
+
};
|
|
489
|
+
return {
|
|
490
|
+
events: [{ type: 'item.started', item }],
|
|
491
|
+
state: {
|
|
492
|
+
...state,
|
|
493
|
+
startedItems: new Set(state.startedItems).add(id),
|
|
494
|
+
unboundSubtasks: [...state.unboundSubtasks, scope],
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/** `step-finish` parts carry per-LLM-round-trip cost + tokens → summed as
|
|
499
|
+
* usage increments (the larger of this sum and the `message.updated`
|
|
500
|
+
* snapshot wins per message — see `MessageUsage`). */
|
|
501
|
+
function mapStepFinish(part, messageID, state) {
|
|
502
|
+
if (messageID === undefined)
|
|
503
|
+
return { events: [], state };
|
|
504
|
+
const tokens = tokensToUsage(part.tokens);
|
|
505
|
+
const cost = num(part.cost);
|
|
506
|
+
if (!tokens && cost === undefined)
|
|
507
|
+
return { events: [], state };
|
|
508
|
+
const prev = state.usageByMessage.get(messageID) ?? EMPTY_MESSAGE_USAGE;
|
|
509
|
+
const merged = {
|
|
510
|
+
info: prev.info,
|
|
511
|
+
infoCost: prev.infoCost,
|
|
512
|
+
steps: tokens ? addUsage(prev.steps, tokens) : prev.steps,
|
|
513
|
+
stepsCost: cost !== undefined ? (prev.stepsCost ?? 0) + cost : prev.stepsCost,
|
|
514
|
+
};
|
|
515
|
+
const usageByMessage = new Map(state.usageByMessage);
|
|
516
|
+
usageByMessage.set(messageID, merged);
|
|
517
|
+
const currentTurnMessageIds = state.currentTurnId !== null
|
|
518
|
+
? new Set(state.currentTurnMessageIds).add(messageID)
|
|
519
|
+
: state.currentTurnMessageIds;
|
|
520
|
+
return emitUsage({ ...state, usageByMessage, currentTurnMessageIds });
|
|
521
|
+
}
|
|
522
|
+
/** Replace the scope owned by `scope.id`, bound or still pending, with the
|
|
523
|
+
* latest snapshot of its title and input. */
|
|
524
|
+
function refreshScope(scope, state) {
|
|
525
|
+
let changed = false;
|
|
526
|
+
const unboundSubtasks = state.unboundSubtasks.map((s) => {
|
|
527
|
+
if (s.id !== scope.id || sameScope(s, scope))
|
|
528
|
+
return s;
|
|
529
|
+
changed = true;
|
|
530
|
+
return scope;
|
|
531
|
+
});
|
|
532
|
+
const subtasks = new Map(state.subtasks);
|
|
533
|
+
for (const [sessionId, s] of state.subtasks) {
|
|
534
|
+
if (s.id !== scope.id || sameScope(s, scope))
|
|
535
|
+
continue;
|
|
536
|
+
subtasks.set(sessionId, scope);
|
|
537
|
+
changed = true;
|
|
538
|
+
}
|
|
539
|
+
return changed ? { ...state, subtasks, unboundSubtasks } : state;
|
|
540
|
+
}
|
|
541
|
+
function sameScope(a, b) {
|
|
542
|
+
return (a.name === b.name &&
|
|
543
|
+
a.title === b.title &&
|
|
544
|
+
a.childSession === b.childSession &&
|
|
545
|
+
a.parentItemId === b.parentItemId &&
|
|
546
|
+
JSON.stringify(a.input) === JSON.stringify(b.input));
|
|
547
|
+
}
|
|
548
|
+
/** Bind `scope` to the child session the wire named for it: out of the
|
|
549
|
+
* pending queue and into the session map. A scope FIFO had already guessed
|
|
550
|
+
* onto that session goes back to the head of the queue — the wire outranks
|
|
551
|
+
* the guess, and that scope's real child is still to be heard from. */
|
|
552
|
+
function bindScope(scope, childSession, state) {
|
|
553
|
+
if (state.closedSessions.has(childSession))
|
|
554
|
+
return releaseScope(scope.id, undefined, state);
|
|
555
|
+
const current = state.subtasks.get(childSession);
|
|
556
|
+
if (current?.id === scope.id && !state.unboundSubtasks.some((s) => s.id === scope.id))
|
|
557
|
+
return state;
|
|
558
|
+
const unboundSubtasks = state.unboundSubtasks.filter((s) => s.id !== scope.id);
|
|
559
|
+
if (current !== undefined && current.id !== scope.id)
|
|
560
|
+
unboundSubtasks.unshift(current);
|
|
561
|
+
const subtasks = new Map(state.subtasks);
|
|
562
|
+
for (const [sessionId, s] of state.subtasks)
|
|
563
|
+
if (s.id === scope.id && sessionId !== childSession)
|
|
564
|
+
subtasks.delete(sessionId);
|
|
565
|
+
subtasks.set(childSession, scope);
|
|
566
|
+
return { ...state, subtasks, unboundSubtasks };
|
|
567
|
+
}
|
|
568
|
+
/** Drop the scope owned by item `id`, bound or still pending, closing every
|
|
569
|
+
* child session it was bound to plus the one the wire named (if any). */
|
|
570
|
+
function releaseScope(id, childSession, state) {
|
|
571
|
+
const pending = state.unboundSubtasks.filter((scope) => scope.id !== id);
|
|
572
|
+
const bound = [...state.subtasks].filter(([, scope]) => scope.id === id);
|
|
573
|
+
const named = childSession ?? state.unboundSubtasks.find((scope) => scope.id === id)?.childSession;
|
|
574
|
+
const closeNamed = named !== undefined && !state.closedSessions.has(named);
|
|
575
|
+
if (pending.length === state.unboundSubtasks.length && bound.length === 0 && !closeNamed)
|
|
576
|
+
return state;
|
|
577
|
+
const subtasks = new Map(state.subtasks);
|
|
578
|
+
const closedSessions = new Set(state.closedSessions);
|
|
579
|
+
for (const [sessionId] of bound) {
|
|
580
|
+
subtasks.delete(sessionId);
|
|
581
|
+
closedSessions.add(sessionId);
|
|
582
|
+
}
|
|
583
|
+
if (named !== undefined)
|
|
584
|
+
closedSessions.add(named);
|
|
585
|
+
return { ...state, subtasks, unboundSubtasks: pending, closedSessions };
|
|
586
|
+
}
|
|
587
|
+
function isSubtaskTool(name) {
|
|
588
|
+
const lower = name.toLowerCase();
|
|
589
|
+
return lower === 'task' || lower === 'subtask';
|
|
590
|
+
}
|
|
591
|
+
// ---- session lifecycle -------------------------------------------------------
|
|
592
|
+
/** `session.idle` is THE turn-end signal (§4.1). The main session's idle
|
|
593
|
+
* closes the current turn; a foreign idle is the subtask's child session
|
|
594
|
+
* going quiet → the subtask item completes. */
|
|
595
|
+
function mapIdle(props, state) {
|
|
596
|
+
const sid = str(props.sessionID);
|
|
597
|
+
const isMain = sid === undefined || state.sessionId === null || sid === state.sessionId;
|
|
598
|
+
if (!isMain) {
|
|
599
|
+
if (sid === undefined)
|
|
600
|
+
return { events: [], state };
|
|
601
|
+
const resolved = resolveSubtask(sid, state);
|
|
602
|
+
if (resolved.subtask === undefined)
|
|
603
|
+
return { events: [], state };
|
|
604
|
+
const subtasks = new Map(resolved.state.subtasks);
|
|
605
|
+
subtasks.delete(sid);
|
|
606
|
+
return {
|
|
607
|
+
events: [{ type: 'item.completed', item: completedSubtask(resolved.subtask) }],
|
|
608
|
+
state: { ...resolved.state, subtasks, closedSessions: new Set(resolved.state.closedSessions).add(sid) },
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
// Idle with no turn in flight (or a repeated idle) closes nothing.
|
|
612
|
+
if (state.currentTurnId === null)
|
|
613
|
+
return { events: [], state };
|
|
614
|
+
const openSubtasks = [...state.subtasks.values(), ...state.unboundSubtasks];
|
|
615
|
+
const completed = {
|
|
616
|
+
type: 'turn.completed',
|
|
617
|
+
turnId: state.currentTurnId,
|
|
618
|
+
stopReason: state.turnErrored ? 'error' : 'end_turn',
|
|
619
|
+
};
|
|
620
|
+
const turnUsage = usageForMessages(state.currentTurnMessageIds, state.usageByMessage);
|
|
621
|
+
if (turnUsage.usage !== null)
|
|
622
|
+
completed.usage = turnUsage.usage;
|
|
623
|
+
if (turnUsage.cost !== null)
|
|
624
|
+
completed.costUsd = turnUsage.cost;
|
|
625
|
+
return {
|
|
626
|
+
events: [
|
|
627
|
+
...openSubtasks.map((subtask) => ({ type: 'item.completed', item: completedSubtask(subtask) })),
|
|
628
|
+
completed,
|
|
629
|
+
],
|
|
630
|
+
state: {
|
|
631
|
+
...state,
|
|
632
|
+
currentTurnId: null,
|
|
633
|
+
currentTurnMessageIds: new Set(),
|
|
634
|
+
turnErrored: false,
|
|
635
|
+
subtasks: new Map(),
|
|
636
|
+
unboundSubtasks: [],
|
|
637
|
+
// Children the turn settled are over too: a delayed event from one must
|
|
638
|
+
// not bind to a scope the NEXT turn queues.
|
|
639
|
+
closedSessions: new Set([...state.closedSessions, ...state.subtasks.keys()]),
|
|
640
|
+
},
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/** `session.error` → non-fatal `session.error` (the mapper can't know the
|
|
644
|
+
* session died — the runner owns fatality) + the turn closes as 'error'. */
|
|
645
|
+
function mapSessionError(props, state) {
|
|
646
|
+
const sid = str(props.sessionID);
|
|
647
|
+
const foreign = sid !== undefined && state.sessionId !== null && sid !== state.sessionId;
|
|
648
|
+
const resolved = foreign && sid !== undefined ? resolveSubtask(sid, state) : undefined;
|
|
649
|
+
if (foreign && resolved?.subtask === undefined)
|
|
650
|
+
return { events: [], state };
|
|
651
|
+
state = resolved?.state ?? state;
|
|
652
|
+
const message = errorText(props.error) ?? 'opencode session error';
|
|
653
|
+
return {
|
|
654
|
+
events: [{ type: 'session.error', message, fatal: false }],
|
|
655
|
+
state: { ...state, turnErrored: state.currentTurnId !== null ? true : state.turnErrored },
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function resolveSubtask(sessionId, state) {
|
|
659
|
+
const existing = state.subtasks.get(sessionId);
|
|
660
|
+
if (existing !== undefined)
|
|
661
|
+
return { state, subtask: existing };
|
|
662
|
+
// A closed child's late events have no home — never a sibling's scope.
|
|
663
|
+
if (state.closedSessions.has(sessionId))
|
|
664
|
+
return { state };
|
|
665
|
+
// First-in-first-out: the oldest pending subtask claims this child session
|
|
666
|
+
// and the rest stay queued for the children still to come (#5).
|
|
667
|
+
const [subtask, ...rest] = state.unboundSubtasks;
|
|
668
|
+
if (subtask === undefined)
|
|
669
|
+
return { state };
|
|
670
|
+
const subtasks = new Map(state.subtasks);
|
|
671
|
+
subtasks.set(sessionId, subtask);
|
|
672
|
+
return { state: { ...state, subtasks, unboundSubtasks: rest }, subtask };
|
|
673
|
+
}
|
|
674
|
+
function completedSubtask(subtask) {
|
|
675
|
+
const item = {
|
|
676
|
+
kind: 'tool',
|
|
677
|
+
id: subtask.id,
|
|
678
|
+
name: subtask.name,
|
|
679
|
+
toolKind: 'task',
|
|
680
|
+
title: subtask.title,
|
|
681
|
+
status: 'completed',
|
|
682
|
+
};
|
|
683
|
+
if (subtask.input !== undefined)
|
|
684
|
+
item.input = subtask.input;
|
|
685
|
+
if (subtask.parentItemId !== undefined)
|
|
686
|
+
item.parentItemId = subtask.parentItemId;
|
|
687
|
+
return item;
|
|
688
|
+
}
|
|
689
|
+
// ---- telemetry ----------------------------------------------------------------
|
|
690
|
+
const EMPTY_MESSAGE_USAGE = { info: null, infoCost: null, steps: null, stepsCost: null };
|
|
691
|
+
/** `{input, output, reasoning, cache:{read,write}}` → raw TokenUsage
|
|
692
|
+
* (opencode reports no total — the sum of the parts is the total). */
|
|
693
|
+
function tokensToUsage(tokens) {
|
|
694
|
+
if (!isRecord(tokens))
|
|
695
|
+
return undefined;
|
|
696
|
+
const input = num(tokens.input) ?? 0;
|
|
697
|
+
const output = num(tokens.output) ?? 0;
|
|
698
|
+
const reasoning = num(tokens.reasoning);
|
|
699
|
+
const cache = isRecord(tokens.cache) ? tokens.cache : undefined;
|
|
700
|
+
const cacheRead = cache ? num(cache.read) : undefined;
|
|
701
|
+
const cacheWrite = cache ? num(cache.write) : undefined;
|
|
702
|
+
const usage = {
|
|
703
|
+
input,
|
|
704
|
+
output,
|
|
705
|
+
total: input + output + (reasoning ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0),
|
|
706
|
+
};
|
|
707
|
+
if (cacheRead !== undefined)
|
|
708
|
+
usage.cacheRead = cacheRead;
|
|
709
|
+
if (cacheWrite !== undefined)
|
|
710
|
+
usage.cacheWrite = cacheWrite;
|
|
711
|
+
if (reasoning !== undefined)
|
|
712
|
+
usage.reasoning = reasoning;
|
|
713
|
+
return usage;
|
|
714
|
+
}
|
|
715
|
+
function addUsage(a, b) {
|
|
716
|
+
if (a === null)
|
|
717
|
+
return b;
|
|
718
|
+
const sum = { input: a.input + b.input, output: a.output + b.output, total: a.total + b.total };
|
|
719
|
+
const cacheRead = optSum(a.cacheRead, b.cacheRead);
|
|
720
|
+
if (cacheRead !== undefined)
|
|
721
|
+
sum.cacheRead = cacheRead;
|
|
722
|
+
const cacheWrite = optSum(a.cacheWrite, b.cacheWrite);
|
|
723
|
+
if (cacheWrite !== undefined)
|
|
724
|
+
sum.cacheWrite = cacheWrite;
|
|
725
|
+
const reasoning = optSum(a.reasoning, b.reasoning);
|
|
726
|
+
if (reasoning !== undefined)
|
|
727
|
+
sum.reasoning = reasoning;
|
|
728
|
+
return sum;
|
|
729
|
+
}
|
|
730
|
+
function optSum(a, b) {
|
|
731
|
+
if (a === undefined && b === undefined)
|
|
732
|
+
return undefined;
|
|
733
|
+
return (a ?? 0) + (b ?? 0);
|
|
734
|
+
}
|
|
735
|
+
function maxCost(a, b) {
|
|
736
|
+
if (b === undefined)
|
|
737
|
+
return a;
|
|
738
|
+
return a === null ? b : Math.max(a, b);
|
|
739
|
+
}
|
|
740
|
+
/** Session totals = per-message effective usage summed across messages;
|
|
741
|
+
* emitted only when the snapshot actually changed and is non-zero. */
|
|
742
|
+
function emitUsage(state) {
|
|
743
|
+
let usage = { input: 0, output: 0, total: 0 };
|
|
744
|
+
let cost = null;
|
|
745
|
+
for (const mu of state.usageByMessage.values()) {
|
|
746
|
+
const effective = mu.info !== null && (mu.steps === null || mu.info.total >= mu.steps.total) ? mu.info : mu.steps;
|
|
747
|
+
if (effective !== null)
|
|
748
|
+
usage = addUsage(usage, effective);
|
|
749
|
+
const effCost = maxCost(mu.infoCost, mu.stepsCost ?? undefined);
|
|
750
|
+
if (effCost !== null)
|
|
751
|
+
cost = (cost ?? 0) + effCost;
|
|
752
|
+
}
|
|
753
|
+
if (usage.total === 0 && (cost === null || cost === 0))
|
|
754
|
+
return { events: [], state };
|
|
755
|
+
if (state.lastUsage !== null && state.lastUsage.total === usage.total && state.lastUsage.cost === cost) {
|
|
756
|
+
return { events: [], state };
|
|
757
|
+
}
|
|
758
|
+
const event = { type: 'usage.updated', usage };
|
|
759
|
+
if (cost !== null)
|
|
760
|
+
event.costUsd = cost;
|
|
761
|
+
return { events: [event], state: { ...state, lastUsage: { total: usage.total, cost } } };
|
|
762
|
+
}
|
|
763
|
+
function usageForMessages(messageIds, usageByMessage) {
|
|
764
|
+
let usage = null;
|
|
765
|
+
let cost = null;
|
|
766
|
+
for (const id of messageIds) {
|
|
767
|
+
const message = usageByMessage.get(id);
|
|
768
|
+
if (message === undefined)
|
|
769
|
+
continue;
|
|
770
|
+
const effective = message.info !== null && (message.steps === null || message.info.total >= message.steps.total)
|
|
771
|
+
? message.info
|
|
772
|
+
: message.steps;
|
|
773
|
+
if (effective !== null && (effective.input > 0 || effective.output > 0))
|
|
774
|
+
usage = addUsage(usage, effective);
|
|
775
|
+
const effectiveCost = maxCost(message.infoCost, message.stepsCost ?? undefined);
|
|
776
|
+
if (effectiveCost !== null && effectiveCost > 0)
|
|
777
|
+
cost = (cost ?? 0) + effectiveCost;
|
|
778
|
+
}
|
|
779
|
+
return { usage, cost };
|
|
780
|
+
}
|
|
781
|
+
// ---- tiny guards ---------------------------------------------------------------
|
|
782
|
+
function isRecord(value) {
|
|
783
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
784
|
+
}
|
|
785
|
+
function str(value) {
|
|
786
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
787
|
+
}
|
|
788
|
+
function num(value) {
|
|
789
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
790
|
+
}
|
|
791
|
+
/** Opencode error objects are `{name, data:{message}}` (or plain strings). */
|
|
792
|
+
function errorText(error) {
|
|
793
|
+
if (typeof error === 'string')
|
|
794
|
+
return str(error);
|
|
795
|
+
if (!isRecord(error))
|
|
796
|
+
return undefined;
|
|
797
|
+
const data = isRecord(error.data) ? error.data : undefined;
|
|
798
|
+
return str(error.message) ?? (data && str(data.message)) ?? str(error.name);
|
|
799
|
+
}
|
|
800
|
+
//# sourceMappingURL=opencode-ui-mapper.js.map
|