@intentic/sandbox-contract 1.248.0 → 1.249.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -15
- package/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/digest.d.ts.map +1 -1
- package/dist/chores/digest.js.map +1 -1
- package/dist/chores/extension-update.d.ts.map +1 -1
- package/dist/chores/extension-update.js.map +1 -1
- package/dist/chores/fix-deps.d.ts.map +1 -1
- package/dist/chores/fix-deps.js.map +1 -1
- package/dist/chores/index.d.ts +2 -2
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +1 -1
- package/dist/chores/index.js.map +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js.map +1 -1
- package/dist/chores/prompt.d.ts.map +1 -1
- package/dist/chores/prompt.js.map +1 -1
- package/dist/chores/stack.d.ts.map +1 -1
- package/dist/chores/stack.js.map +1 -1
- package/dist/chores/verdict.d.ts +7 -1
- package/dist/chores/verdict.d.ts.map +1 -1
- package/dist/chores/verdict.js +8 -0
- package/dist/chores/verdict.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/accounts.contract.js.map +1 -1
- package/dist/contracts/activity.contract.d.ts +1 -0
- package/dist/contracts/activity.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +12 -205
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +2 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +228 -54
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +11 -2
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/automations.contract.d.ts +35 -17
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.js +10 -1
- package/dist/contracts/automations.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +0 -12
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +1 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/chores.contract.d.ts.map +1 -1
- package/dist/contracts/chores.contract.js.map +1 -1
- package/dist/contracts/ci.contract.d.ts +9 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js.map +1 -1
- package/dist/contracts/exit.contract.d.ts.map +1 -1
- package/dist/contracts/exit.contract.js +1 -1
- package/dist/contracts/exit.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/git.contract.d.ts +45 -6
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.js +9 -9
- package/dist/contracts/git.contract.js.map +1 -1
- package/dist/contracts/host.contract.d.ts +2 -0
- package/dist/contracts/host.contract.d.ts.map +1 -1
- package/dist/contracts/host.contract.js.map +1 -1
- package/dist/contracts/intentic.contract.js +1 -1
- package/dist/contracts/intentic.contract.js.map +1 -1
- package/dist/contracts/logs.contract.d.ts.map +1 -1
- package/dist/contracts/logs.contract.js.map +1 -1
- package/dist/contracts/loops.contract.d.ts.map +1 -1
- package/dist/contracts/loops.contract.js.map +1 -1
- package/dist/contracts/personas.contract.d.ts +1 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.js +1 -1
- package/dist/contracts/personas.contract.js.map +1 -1
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +97 -171
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +2 -2
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/safety.contract.d.ts.map +1 -1
- package/dist/contracts/safety.contract.js +1 -1
- package/dist/contracts/safety.contract.js.map +1 -1
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +2 -41
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.js +1 -1
- package/dist/contracts/sessions.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +8 -18
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/share.contract.d.ts.map +1 -1
- package/dist/contracts/share.contract.js.map +1 -1
- package/dist/contracts/skills.contract.d.ts.map +1 -1
- package/dist/contracts/skills.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +49 -73
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +12 -2
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/usage.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.js.map +1 -1
- package/dist/contracts/vpn.contract.d.ts.map +1 -1
- package/dist/contracts/vpn.contract.js +1 -1
- package/dist/contracts/vpn.contract.js.map +1 -1
- package/dist/contracts/webext.contract.d.ts.map +1 -1
- package/dist/contracts/webext.contract.js.map +1 -1
- package/dist/contracts/workflows.contract.d.ts +7 -6
- package/dist/contracts/workflows.contract.d.ts.map +1 -1
- package/dist/contracts/workflows.contract.js +12 -3
- package/dist/contracts/workflows.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +1237 -0
- package/dist/events/agent-events.d.ts.map +1 -0
- package/dist/events/agent-events.js +226 -0
- package/dist/events/agent-events.js.map +1 -0
- package/dist/events/cards.d.ts +404 -0
- package/dist/events/cards.d.ts.map +1 -0
- package/dist/events/cards.js +179 -0
- package/dist/events/cards.js.map +1 -0
- package/dist/events/resume.d.ts +23 -0
- package/dist/events/resume.d.ts.map +1 -0
- package/dist/events/resume.js +31 -0
- package/dist/events/resume.js.map +1 -0
- package/dist/events/system-events.d.ts +615 -0
- package/dist/events/system-events.d.ts.map +1 -0
- package/dist/events/system-events.js +64 -0
- package/dist/events/system-events.js.map +1 -0
- package/dist/events/transcript.d.ts +1755 -0
- package/dist/events/transcript.d.ts.map +1 -0
- package/dist/events/transcript.js +205 -0
- package/dist/events/transcript.js.map +1 -0
- package/dist/ids/conversation-ids.d.ts +8 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -0
- package/dist/{conversation-ids.js → ids/conversation-ids.js} +13 -0
- package/dist/ids/conversation-ids.js.map +1 -0
- package/dist/ids/hostnames.d.ts.map +1 -0
- package/dist/ids/hostnames.js.map +1 -0
- package/dist/ids/session-names.d.ts.map +1 -0
- package/dist/ids/session-names.js.map +1 -0
- package/dist/ids/share-paths.d.ts.map +1 -0
- package/dist/ids/share-paths.js.map +1 -0
- package/dist/{tunnel-ids.d.ts → ids/tunnel-ids.d.ts} +1 -0
- package/dist/ids/tunnel-ids.d.ts.map +1 -0
- package/dist/{tunnel-ids.js → ids/tunnel-ids.js} +1 -0
- package/dist/ids/tunnel-ids.js.map +1 -0
- package/dist/index.d.ts +423 -450
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -46
- package/dist/index.js.map +1 -1
- package/dist/{agent-catalog.d.ts → models/agent-catalog.d.ts} +2 -2
- package/dist/models/agent-catalog.d.ts.map +1 -0
- package/dist/models/agent-catalog.js.map +1 -0
- package/dist/models/agent-runtimes.d.ts.map +1 -0
- package/dist/models/agent-runtimes.js.map +1 -0
- package/dist/{fast-tier.d.ts → models/fast-tier.d.ts} +1 -1
- package/dist/models/fast-tier.d.ts.map +1 -0
- package/dist/models/fast-tier.js.map +1 -0
- package/dist/models/model-order.d.ts.map +1 -0
- package/dist/models/model-order.js.map +1 -0
- package/dist/{model-pins.d.ts → models/model-pins.d.ts} +1 -1
- package/dist/models/model-pins.d.ts.map +1 -0
- package/dist/models/model-pins.js.map +1 -0
- package/dist/{model-roles.d.ts → models/model-roles.d.ts} +0 -33
- package/dist/models/model-roles.d.ts.map +1 -0
- package/dist/{model-roles.js → models/model-roles.js} +0 -35
- package/dist/models/model-roles.js.map +1 -0
- package/dist/{plan-pools.d.ts → models/plan-pools.d.ts} +1 -1
- package/dist/models/plan-pools.d.ts.map +1 -0
- package/dist/models/plan-pools.js.map +1 -0
- package/dist/models/prompt-complexity.d.ts.map +1 -0
- package/dist/models/prompt-complexity.js.map +1 -0
- package/dist/models/provider-specs.d.ts.map +1 -0
- package/dist/models/provider-specs.js.map +1 -0
- package/dist/policy/approvals-execution.d.ts.map +1 -0
- package/dist/policy/approvals-execution.js.map +1 -0
- package/dist/{batch-runs.d.ts → policy/batch-runs.d.ts} +5 -1
- package/dist/policy/batch-runs.d.ts.map +1 -0
- package/dist/{batch-runs.js → policy/batch-runs.js} +10 -7
- package/dist/policy/batch-runs.js.map +1 -0
- package/dist/policy/capability-env.d.ts.map +1 -0
- package/dist/policy/capability-env.js.map +1 -0
- package/dist/policy/capability-secrets.d.ts.map +1 -0
- package/dist/policy/capability-secrets.js.map +1 -0
- package/dist/{card-status.d.ts → policy/card-status.d.ts} +2 -2
- package/dist/policy/card-status.d.ts.map +1 -0
- package/dist/{card-status.js → policy/card-status.js} +1 -5
- package/dist/policy/card-status.js.map +1 -0
- package/dist/{command-classes.d.ts → policy/command-classes.d.ts} +6 -2
- package/dist/policy/command-classes.d.ts.map +1 -0
- package/dist/{command-classes.js → policy/command-classes.js} +48 -11
- package/dist/policy/command-classes.js.map +1 -0
- package/dist/{command-run.d.ts → policy/command-run.d.ts} +1 -1
- package/dist/policy/command-run.d.ts.map +1 -0
- package/dist/policy/command-run.js.map +1 -0
- package/dist/policy/control-scopes.d.ts +16 -0
- package/dist/policy/control-scopes.d.ts.map +1 -0
- package/dist/policy/control-scopes.js +26 -0
- package/dist/policy/control-scopes.js.map +1 -0
- package/dist/policy/credential-material.d.ts.map +1 -0
- package/dist/policy/credential-material.js.map +1 -0
- package/dist/policy/needs-action.d.ts.map +1 -0
- package/dist/policy/needs-action.js.map +1 -0
- package/dist/policy/output-fields.d.ts.map +1 -0
- package/dist/policy/output-fields.js.map +1 -0
- package/dist/policy/overlay-lint.d.ts.map +1 -0
- package/dist/policy/overlay-lint.js.map +1 -0
- package/dist/policy/owner-ticket.d.ts.map +1 -0
- package/dist/{owner-ticket.js → policy/owner-ticket.js} +1 -1
- package/dist/policy/owner-ticket.js.map +1 -0
- package/dist/{safety-policy.d.ts → policy/safety-policy.d.ts} +6 -5
- package/dist/policy/safety-policy.d.ts.map +1 -0
- package/dist/{safety-policy.js → policy/safety-policy.js} +12 -12
- package/dist/policy/safety-policy.js.map +1 -0
- package/dist/policy/search-globs.d.ts.map +1 -0
- package/dist/policy/search-globs.js.map +1 -0
- package/dist/protocol/container-requirements.d.ts +20 -0
- package/dist/protocol/container-requirements.d.ts.map +1 -0
- package/dist/protocol/container-requirements.js +23 -0
- package/dist/protocol/container-requirements.js.map +1 -0
- package/dist/{host-protocol.d.ts → protocol/host-protocol.d.ts} +1 -0
- package/dist/protocol/host-protocol.d.ts.map +1 -0
- package/dist/{host-protocol.js → protocol/host-protocol.js} +1 -0
- package/dist/protocol/host-protocol.js.map +1 -0
- package/dist/protocol/ingress-contract.d.ts.map +1 -0
- package/dist/{ingress-contract.js → protocol/ingress-contract.js} +1 -1
- package/dist/protocol/ingress-contract.js.map +1 -0
- package/dist/protocol/ingress-protocol.d.ts.map +1 -0
- package/dist/protocol/ingress-protocol.js.map +1 -0
- package/dist/protocol/listener-protocol.d.ts.map +1 -0
- package/dist/{listener-protocol.js → protocol/listener-protocol.js} +1 -1
- package/dist/protocol/listener-protocol.js.map +1 -0
- package/dist/{peer-dial.d.ts → protocol/peer-dial.d.ts} +4 -1
- package/dist/protocol/peer-dial.d.ts.map +1 -0
- package/dist/{peer-dial.js → protocol/peer-dial.js} +51 -10
- package/dist/protocol/peer-dial.js.map +1 -0
- package/dist/protocol/peer-mcp-server.d.ts.map +1 -0
- package/dist/protocol/peer-mcp-server.js.map +1 -0
- package/dist/protocol/request-id.d.ts.map +1 -0
- package/dist/protocol/request-id.js.map +1 -0
- package/dist/protocol/routes.d.ts.map +1 -0
- package/dist/protocol/routes.js.map +1 -0
- package/dist/{runner-protocol.d.ts → protocol/runner-protocol.d.ts} +1 -0
- package/dist/protocol/runner-protocol.d.ts.map +1 -0
- package/dist/{runner-protocol.js → protocol/runner-protocol.js} +2 -1
- package/dist/protocol/runner-protocol.js.map +1 -0
- package/dist/protocol/sse.d.ts.map +1 -0
- package/dist/protocol/sse.js.map +1 -0
- package/dist/{terminal-protocol.d.ts → protocol/terminal-protocol.d.ts} +1 -3
- package/dist/protocol/terminal-protocol.d.ts.map +1 -0
- package/dist/protocol/terminal-protocol.js.map +1 -0
- package/dist/protocol/webext-links.d.ts.map +1 -0
- package/dist/protocol/webext-links.js.map +1 -0
- package/dist/{webext-protocol.d.ts → protocol/webext-protocol.d.ts} +1 -0
- package/dist/protocol/webext-protocol.d.ts.map +1 -0
- package/dist/{webext-protocol.js → protocol/webext-protocol.js} +1 -0
- package/dist/protocol/webext-protocol.js.map +1 -0
- package/dist/schemas/activity.d.ts +2 -0
- package/dist/schemas/activity.d.ts.map +1 -1
- package/dist/schemas/activity.js +4 -0
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agent.d.ts +16 -4
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +21 -4
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +17 -4
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +15 -4
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/approvals.d.ts.map +1 -1
- package/dist/schemas/approvals.js.map +1 -1
- package/dist/schemas/automations.d.ts +54 -28
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js +26 -8
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/capabilities.d.ts +0 -8
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +0 -4
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/ci.d.ts +10 -0
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +9 -1
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/codebase-health.d.ts.map +1 -1
- package/dist/schemas/codebase-health.js.map +1 -1
- package/dist/schemas/devices.d.ts +90 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +10 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/engines.d.ts.map +1 -1
- package/dist/schemas/engines.js.map +1 -1
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/exit.d.ts.map +1 -1
- package/dist/schemas/exit.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/git-history.d.ts.map +1 -1
- package/dist/schemas/git-history.js.map +1 -1
- package/dist/schemas/git.d.ts +80 -16
- package/dist/schemas/git.d.ts.map +1 -1
- package/dist/schemas/git.js +37 -25
- package/dist/schemas/git.js.map +1 -1
- package/dist/schemas/history.d.ts.map +1 -1
- package/dist/schemas/history.js.map +1 -1
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/inventory.d.ts.map +1 -1
- package/dist/schemas/inventory.js.map +1 -1
- package/dist/schemas/issues.d.ts +0 -1
- package/dist/schemas/issues.d.ts.map +1 -1
- package/dist/schemas/issues.js +0 -1
- package/dist/schemas/issues.js.map +1 -1
- package/dist/schemas/logs.d.ts.map +1 -1
- package/dist/schemas/logs.js.map +1 -1
- package/dist/schemas/loops.d.ts.map +1 -1
- package/dist/schemas/loops.js +1 -1
- package/dist/schemas/loops.js.map +1 -1
- package/dist/schemas/maintenance.d.ts.map +1 -1
- package/dist/schemas/maintenance.js.map +1 -1
- package/dist/schemas/marketplace.d.ts +0 -4
- package/dist/schemas/marketplace.d.ts.map +1 -1
- package/dist/schemas/panels.d.ts.map +1 -1
- package/dist/schemas/panels.js.map +1 -1
- package/dist/schemas/personas.d.ts +2 -1
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +6 -2
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/plan-limits.d.ts +2 -4
- package/dist/schemas/plan-limits.d.ts.map +1 -1
- package/dist/schemas/plan-limits.js +5 -8
- package/dist/schemas/plan-limits.js.map +1 -1
- package/dist/schemas/ports.d.ts.map +1 -1
- package/dist/schemas/ports.js.map +1 -1
- package/dist/schemas/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/provider-oauth.js.map +1 -1
- package/dist/schemas/provider-subscriptions.d.ts +1 -1
- package/dist/schemas/provider-subscriptions.d.ts.map +1 -1
- package/dist/schemas/provider-subscriptions.js +1 -1
- package/dist/schemas/provider-subscriptions.js.map +1 -1
- package/dist/schemas/public.d.ts.map +1 -1
- package/dist/schemas/public.js.map +1 -1
- package/dist/schemas/push.d.ts.map +1 -1
- package/dist/schemas/push.js.map +1 -1
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings.d.ts +6 -9
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +16 -5
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/share.d.ts.map +1 -1
- package/dist/schemas/share.js.map +1 -1
- package/dist/schemas/shared.d.ts +4 -0
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/shared.js +3 -0
- package/dist/schemas/shared.js.map +1 -1
- package/dist/schemas/system.d.ts +6 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +10 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/usage.d.ts.map +1 -1
- package/dist/schemas/usage.js.map +1 -1
- package/dist/schemas/vpn.d.ts.map +1 -1
- package/dist/schemas/vpn.js.map +1 -1
- package/dist/schemas/webext.d.ts.map +1 -1
- package/dist/schemas/webext.js.map +1 -1
- package/dist/schemas/workflows.d.ts +66 -9
- package/dist/schemas/workflows.d.ts.map +1 -1
- package/dist/schemas/workflows.js +6 -5
- package/dist/schemas/workflows.js.map +1 -1
- package/dist/schemas/workspace-repos.d.ts.map +1 -1
- package/dist/schemas/workspace-repos.js.map +1 -1
- package/dist/schemas/workspace-search.d.ts.map +1 -1
- package/dist/schemas/workspace-search.js.map +1 -1
- package/dist/schemas/workspace-setup.d.ts.map +1 -1
- package/dist/schemas/workspace-setup.js.map +1 -1
- package/dist/schemas/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace-tree.js.map +1 -1
- package/dist/state/arrival.d.ts.map +1 -0
- package/dist/{arrival.js → state/arrival.js} +1 -1
- package/dist/state/arrival.js.map +1 -0
- package/dist/state/contract-lock.d.ts.map +1 -0
- package/dist/{contract-lock.js → state/contract-lock.js} +1 -1
- package/dist/state/contract-lock.js.map +1 -0
- package/dist/{definition.d.ts → state/definition.d.ts} +21 -17
- package/dist/state/definition.d.ts.map +1 -0
- package/dist/{definition.js → state/definition.js} +4 -4
- package/dist/state/definition.js.map +1 -0
- package/dist/state/history-state.d.ts.map +1 -0
- package/dist/{history-state.js → state/history-state.js} +1 -0
- package/dist/state/history-state.js.map +1 -0
- package/dist/state/runtime-state.d.ts.map +1 -0
- package/dist/state/runtime-state.js.map +1 -0
- package/dist/state/starter.d.ts.map +1 -0
- package/dist/state/starter.js.map +1 -0
- package/dist/state/state-portability.d.ts.map +1 -0
- package/dist/state/state-portability.js.map +1 -0
- package/dist/state/versions.d.ts.map +1 -0
- package/dist/state/versions.js.map +1 -0
- package/dist/{workspace-state.d.ts → state/workspace-state.d.ts} +8 -0
- package/dist/state/workspace-state.d.ts.map +1 -0
- package/dist/{workspace-state.js → state/workspace-state.js} +25 -0
- package/dist/state/workspace-state.js.map +1 -0
- package/dist/{documents.d.ts → text/documents.d.ts} +1 -1
- package/dist/text/documents.d.ts.map +1 -0
- package/dist/{documents.js → text/documents.js} +1 -1
- package/dist/text/documents.js.map +1 -0
- package/dist/text/embed.d.ts.map +1 -0
- package/dist/text/embed.js.map +1 -0
- package/dist/text/mentions.d.ts.map +1 -0
- package/dist/text/mentions.js.map +1 -0
- package/dist/text/model-answer.d.ts +3 -0
- package/dist/text/model-answer.d.ts.map +1 -0
- package/dist/text/model-answer.js +3 -0
- package/dist/text/model-answer.js.map +1 -0
- package/dist/text/path-refs.d.ts.map +1 -0
- package/dist/text/path-refs.js.map +1 -0
- package/dist/{shell-regions.d.ts → text/shell-regions.d.ts} +1 -1
- package/dist/text/shell-regions.d.ts.map +1 -0
- package/dist/text/shell-regions.js.map +1 -0
- package/dist/text/title.d.ts.map +1 -0
- package/dist/text/title.js.map +1 -0
- package/dist/{transcript-fold.d.ts → text/transcript-fold.d.ts} +2 -1
- package/dist/text/transcript-fold.d.ts.map +1 -0
- package/dist/{transcript-fold.js → text/transcript-fold.js} +2 -16
- package/dist/text/transcript-fold.js.map +1 -0
- package/dist/text/whisper.d.ts +3 -0
- package/dist/text/whisper.d.ts.map +1 -0
- package/dist/text/whisper.js +11 -0
- package/dist/text/whisper.js.map +1 -0
- package/dist/{workflow-faults.d.ts → text/workflow-faults.d.ts} +1 -1
- package/dist/text/workflow-faults.d.ts.map +1 -0
- package/dist/{workflow-faults.js → text/workflow-faults.js} +1 -1
- package/dist/text/workflow-faults.js.map +1 -0
- package/package.json +70 -70
- package/src/chores/chores.test.ts +4 -10
- package/src/chores/chores.ts +148 -392
- package/src/chores/digest.ts +8 -28
- package/src/chores/extension-update.ts +3 -8
- package/src/chores/fix-deps.ts +4 -18
- package/src/chores/index.ts +2 -2
- package/src/chores/probes.test.ts +7 -43
- package/src/chores/probes.ts +62 -167
- package/src/chores/prompt.ts +10 -34
- package/src/chores/stack.test.ts +6 -45
- package/src/chores/stack.ts +27 -118
- package/src/chores/verdict.test.ts +101 -80
- package/src/chores/verdict.ts +54 -99
- package/src/contracts/accounts.contract.ts +4 -19
- package/src/contracts/agent.contract.ts +7 -22
- package/src/contracts/agents.contract.ts +25 -71
- package/src/contracts/automations.contract.ts +14 -30
- package/src/contracts/capabilities.contract.ts +8 -35
- package/src/contracts/chores.contract.ts +4 -15
- package/src/contracts/endpoints.contract.ts +5 -22
- package/src/contracts/exit.contract.ts +7 -30
- package/src/contracts/extensions.contract.ts +8 -30
- package/src/contracts/git.contract.ts +25 -49
- package/src/contracts/host.contract.ts +9 -44
- package/src/contracts/intentic.contract.ts +1 -1
- package/src/contracts/logs.contract.ts +3 -13
- package/src/contracts/loops.contract.ts +9 -40
- package/src/contracts/personas.contract.ts +10 -35
- package/src/contracts/providers.contract.ts +4 -17
- package/src/contracts/runner.contract.ts +12 -39
- package/src/contracts/safety.contract.ts +5 -14
- package/src/contracts/secrets.contract.ts +4 -22
- package/src/contracts/sessions.contract.ts +1 -1
- package/src/contracts/settings.contract.ts +4 -10
- package/src/contracts/share.contract.ts +3 -9
- package/src/contracts/skills.contract.ts +4 -15
- package/src/contracts/system.contract.ts +26 -52
- package/src/contracts/usage.contract.ts +7 -21
- package/src/contracts/vpn.contract.ts +9 -16
- package/src/contracts/webext.contract.ts +9 -25
- package/src/contracts/workflows.contract.ts +34 -58
- package/src/contracts/workspace.contract.ts +19 -50
- package/src/events/agent-events.ts +345 -0
- package/src/events/cards.ts +286 -0
- package/src/{events.test.ts → events/resume.test.ts} +4 -19
- package/src/events/resume.ts +60 -0
- package/src/events/system-events.ts +139 -0
- package/src/events/transcript.ts +320 -0
- package/src/{conversation-ids.test.ts → ids/conversation-ids.test.ts} +25 -13
- package/src/ids/conversation-ids.ts +178 -0
- package/src/ids/hostnames.ts +129 -0
- package/src/ids/session-names.ts +29 -0
- package/src/ids/share-paths.ts +43 -0
- package/src/{tunnel-ids.test.ts → ids/tunnel-ids.test.ts} +1 -14
- package/src/ids/tunnel-ids.ts +29 -0
- package/src/index.ts +71 -87
- package/src/{agent-catalog.test.ts → models/agent-catalog.test.ts} +19 -93
- package/src/models/agent-catalog.ts +174 -0
- package/src/models/agent-runtimes.ts +201 -0
- package/src/models/capability-ledger.test.ts +84 -0
- package/src/{fast-tier.test.ts → models/fast-tier.test.ts} +7 -18
- package/src/models/fast-tier.ts +36 -0
- package/src/{model-order.test.ts → models/model-order.test.ts} +19 -40
- package/src/models/model-order.ts +163 -0
- package/src/{model-pins.test.ts → models/model-pins.test.ts} +12 -46
- package/src/models/model-pins.ts +57 -0
- package/src/models/model-roles.test.ts +38 -0
- package/src/models/model-roles.ts +180 -0
- package/src/{plan-pools.test.ts → models/plan-pools.test.ts} +6 -11
- package/src/models/plan-pools.ts +65 -0
- package/src/{prompt-complexity.test.ts → models/prompt-complexity.test.ts} +8 -60
- package/src/models/prompt-complexity.ts +215 -0
- package/src/{provider-specs.test.ts → models/provider-specs.test.ts} +18 -47
- package/src/models/provider-specs.ts +272 -0
- package/src/policy/approvals-execution.ts +58 -0
- package/src/{batch-runs.test.ts → policy/batch-runs.test.ts} +4 -20
- package/src/policy/batch-runs.ts +137 -0
- package/src/policy/capability-secrets.ts +5 -0
- package/src/{card-status.ts → policy/card-status.ts} +11 -26
- package/src/{command-classes.test.ts → policy/command-classes.test.ts} +11 -118
- package/src/policy/command-classes.ts +395 -0
- package/src/policy/command-run.ts +65 -0
- package/src/policy/control-scopes.ts +38 -0
- package/src/{credential-material.test.ts → policy/credential-material.test.ts} +4 -26
- package/src/policy/credential-material.ts +91 -0
- package/src/policy/needs-action.ts +5 -0
- package/src/policy/output-fields.ts +86 -0
- package/src/policy/overlay-lint.ts +100 -0
- package/src/{owner-ticket.test.ts → policy/owner-ticket.test.ts} +1 -1
- package/src/{owner-ticket.ts → policy/owner-ticket.ts} +9 -31
- package/src/policy/safety-policy.test.ts +84 -0
- package/src/policy/safety-policy.ts +142 -0
- package/src/policy/search-globs.ts +60 -0
- package/src/protocol/container-requirements.test.ts +72 -0
- package/src/protocol/container-requirements.ts +58 -0
- package/src/protocol/host-protocol.ts +29 -0
- package/src/protocol/ingress-contract.ts +103 -0
- package/src/{ingress-protocol.test.ts → protocol/ingress-protocol.test.ts} +25 -90
- package/src/protocol/ingress-protocol.ts +441 -0
- package/src/protocol/listener-protocol.ts +75 -0
- package/src/{peer-dial.test.ts → protocol/peer-dial.test.ts} +52 -1
- package/src/protocol/peer-dial.ts +207 -0
- package/src/{peer-mcp-server.ts → protocol/peer-mcp-server.ts} +16 -41
- package/src/protocol/request-id.ts +5 -0
- package/src/{routes.test.ts → protocol/routes.test.ts} +14 -22
- package/src/protocol/routes.ts +159 -0
- package/src/protocol/runner-protocol.ts +197 -0
- package/src/protocol/terminal-protocol.ts +13 -0
- package/src/protocol/webext-links.ts +50 -0
- package/src/protocol/webext-protocol.ts +24 -0
- package/src/schemas/activity.ts +22 -30
- package/src/schemas/agent.ts +130 -238
- package/src/schemas/agents.ts +165 -483
- package/src/schemas/approvals.ts +23 -71
- package/src/schemas/automations.ts +142 -250
- package/src/schemas/capabilities.ts +127 -414
- package/src/schemas/ci.ts +56 -108
- package/src/schemas/codebase-health.ts +7 -11
- package/src/schemas/devices.ts +98 -321
- package/src/schemas/engines.ts +16 -44
- package/src/schemas/environment.ts +41 -91
- package/src/schemas/exit.ts +24 -81
- package/src/schemas/extension-updates.ts +42 -87
- package/src/schemas/git-history.ts +30 -84
- package/src/schemas/git.ts +141 -256
- package/src/schemas/history.ts +14 -40
- package/src/schemas/hosts.ts +9 -15
- package/src/schemas/inventory.ts +8 -10
- package/src/schemas/issues.ts +66 -134
- package/src/schemas/logs.ts +15 -34
- package/src/schemas/loops.ts +46 -163
- package/src/schemas/maintenance.ts +47 -172
- package/src/schemas/panels.ts +20 -45
- package/src/schemas/personas.ts +50 -200
- package/src/schemas/plan-limits.ts +58 -197
- package/src/schemas/ports.ts +14 -32
- package/src/schemas/provider-oauth.ts +23 -76
- package/src/schemas/provider-subscriptions.ts +4 -13
- package/src/schemas/public.ts +4 -15
- package/src/schemas/push.ts +8 -36
- package/src/schemas/secrets.ts +20 -64
- package/src/schemas/settings.ts +175 -600
- package/src/schemas/share.ts +14 -32
- package/src/schemas/shared.ts +14 -13
- package/src/schemas/system.ts +46 -78
- package/src/schemas/terminal.ts +41 -120
- package/src/schemas/usage.ts +44 -233
- package/src/schemas/version-seam.test.ts +9 -26
- package/src/schemas/vpn.ts +27 -64
- package/src/schemas/webext.ts +22 -53
- package/src/schemas/workflows.ts +57 -183
- package/src/schemas/workspace-repos.ts +12 -24
- package/src/schemas/workspace-search.ts +17 -35
- package/src/schemas/workspace-setup.ts +4 -11
- package/src/schemas/workspace-tree.ts +32 -91
- package/src/state/arrival.ts +109 -0
- package/src/state/contract-lock.test.ts +17 -0
- package/src/state/contract-lock.ts +49 -0
- package/src/state/definition.ts +143 -0
- package/src/state/history-state.ts +103 -0
- package/src/{runtime-state.test.ts → state/runtime-state.test.ts} +3 -10
- package/src/state/runtime-state.ts +62 -0
- package/src/state/starter.ts +5 -0
- package/src/state/state-portability.ts +27 -0
- package/src/state/versions.ts +26 -0
- package/src/{workspace-state.test.ts → state/workspace-state.test.ts} +65 -172
- package/src/state/workspace-state.ts +669 -0
- package/src/{documents.test.ts → text/documents.test.ts} +1 -1
- package/src/text/documents.ts +42 -0
- package/src/text/embed.ts +132 -0
- package/src/text/mentions.ts +21 -0
- package/src/text/model-answer.ts +9 -0
- package/src/text/path-refs.ts +39 -0
- package/src/text/shell-regions.ts +224 -0
- package/src/{title.test.ts → text/title.test.ts} +13 -32
- package/src/text/title.ts +202 -0
- package/src/{transcript-fold.test.ts → text/transcript-fold.test.ts} +5 -83
- package/src/{transcript-fold.ts → text/transcript-fold.ts} +65 -170
- package/src/text/whisper.test.ts +19 -0
- package/src/text/whisper.ts +17 -0
- package/src/{workflow-faults.test.ts → text/workflow-faults.test.ts} +6 -24
- package/src/{workflow-faults.ts → text/workflow-faults.ts} +20 -60
- package/dist/agent-catalog.d.ts.map +0 -1
- package/dist/agent-catalog.js.map +0 -1
- package/dist/agent-runtimes.d.ts.map +0 -1
- package/dist/agent-runtimes.js.map +0 -1
- package/dist/approvals-execution.d.ts.map +0 -1
- package/dist/approvals-execution.js.map +0 -1
- package/dist/arrival.d.ts.map +0 -1
- package/dist/arrival.js.map +0 -1
- package/dist/batch-runs.d.ts.map +0 -1
- package/dist/batch-runs.js.map +0 -1
- package/dist/capability-env.d.ts.map +0 -1
- package/dist/capability-env.js.map +0 -1
- package/dist/capability-secrets.d.ts.map +0 -1
- package/dist/capability-secrets.js.map +0 -1
- package/dist/card-status.d.ts.map +0 -1
- package/dist/card-status.js.map +0 -1
- package/dist/command-classes.d.ts.map +0 -1
- package/dist/command-classes.js.map +0 -1
- package/dist/command-run.d.ts.map +0 -1
- package/dist/command-run.js.map +0 -1
- package/dist/contract-lock.d.ts.map +0 -1
- package/dist/contract-lock.js.map +0 -1
- package/dist/conversation-ids.d.ts +0 -4
- package/dist/conversation-ids.d.ts.map +0 -1
- package/dist/conversation-ids.js.map +0 -1
- package/dist/credential-material.d.ts.map +0 -1
- package/dist/credential-material.js.map +0 -1
- package/dist/definition.d.ts.map +0 -1
- package/dist/definition.js.map +0 -1
- package/dist/documents.d.ts.map +0 -1
- package/dist/documents.js.map +0 -1
- package/dist/embed.d.ts.map +0 -1
- package/dist/embed.js.map +0 -1
- package/dist/events.d.ts +0 -4336
- package/dist/events.d.ts.map +0 -1
- package/dist/events.js +0 -725
- package/dist/events.js.map +0 -1
- package/dist/fast-tier.d.ts.map +0 -1
- package/dist/fast-tier.js.map +0 -1
- package/dist/history-state.d.ts.map +0 -1
- package/dist/history-state.js.map +0 -1
- package/dist/host-protocol.d.ts.map +0 -1
- package/dist/host-protocol.js.map +0 -1
- package/dist/hostnames.d.ts.map +0 -1
- package/dist/hostnames.js.map +0 -1
- package/dist/ingress-contract.d.ts.map +0 -1
- package/dist/ingress-contract.js.map +0 -1
- package/dist/ingress-protocol.d.ts.map +0 -1
- package/dist/ingress-protocol.js.map +0 -1
- package/dist/listener-protocol.d.ts.map +0 -1
- package/dist/listener-protocol.js.map +0 -1
- package/dist/mentions.d.ts.map +0 -1
- package/dist/mentions.js.map +0 -1
- package/dist/model-order.d.ts.map +0 -1
- package/dist/model-order.js.map +0 -1
- package/dist/model-pins.d.ts.map +0 -1
- package/dist/model-pins.js.map +0 -1
- package/dist/model-roles.d.ts.map +0 -1
- package/dist/model-roles.js.map +0 -1
- package/dist/needs-action.d.ts.map +0 -1
- package/dist/needs-action.js.map +0 -1
- package/dist/output-fields.d.ts.map +0 -1
- package/dist/output-fields.js.map +0 -1
- package/dist/overlay-lint.d.ts.map +0 -1
- package/dist/overlay-lint.js.map +0 -1
- package/dist/owner-ticket.d.ts.map +0 -1
- package/dist/owner-ticket.js.map +0 -1
- package/dist/path-refs.d.ts.map +0 -1
- package/dist/path-refs.js.map +0 -1
- package/dist/peer-dial.d.ts.map +0 -1
- package/dist/peer-dial.js.map +0 -1
- package/dist/peer-mcp-server.d.ts.map +0 -1
- package/dist/peer-mcp-server.js.map +0 -1
- package/dist/plan-pools.d.ts.map +0 -1
- package/dist/plan-pools.js.map +0 -1
- package/dist/prompt-complexity.d.ts.map +0 -1
- package/dist/prompt-complexity.js.map +0 -1
- package/dist/provider-specs.d.ts.map +0 -1
- package/dist/provider-specs.js.map +0 -1
- package/dist/request-id.d.ts.map +0 -1
- package/dist/request-id.js.map +0 -1
- package/dist/routes.d.ts.map +0 -1
- package/dist/routes.js.map +0 -1
- package/dist/runner-protocol.d.ts.map +0 -1
- package/dist/runner-protocol.js.map +0 -1
- package/dist/runtime-state.d.ts.map +0 -1
- package/dist/runtime-state.js.map +0 -1
- package/dist/safety-policy.d.ts.map +0 -1
- package/dist/safety-policy.js.map +0 -1
- package/dist/search-globs.d.ts.map +0 -1
- package/dist/search-globs.js.map +0 -1
- package/dist/session-names.d.ts.map +0 -1
- package/dist/session-names.js.map +0 -1
- package/dist/share-paths.d.ts.map +0 -1
- package/dist/share-paths.js.map +0 -1
- package/dist/shell-regions.d.ts.map +0 -1
- package/dist/shell-regions.js.map +0 -1
- package/dist/sse.d.ts.map +0 -1
- package/dist/sse.js.map +0 -1
- package/dist/starter.d.ts.map +0 -1
- package/dist/starter.js.map +0 -1
- package/dist/state-portability.d.ts.map +0 -1
- package/dist/state-portability.js.map +0 -1
- package/dist/terminal-protocol.d.ts.map +0 -1
- package/dist/terminal-protocol.js.map +0 -1
- package/dist/title.d.ts.map +0 -1
- package/dist/title.js.map +0 -1
- package/dist/transcript-fold.d.ts.map +0 -1
- package/dist/transcript-fold.js.map +0 -1
- package/dist/tunnel-ids.d.ts.map +0 -1
- package/dist/tunnel-ids.js.map +0 -1
- package/dist/versions.d.ts.map +0 -1
- package/dist/versions.js.map +0 -1
- package/dist/webext-links.d.ts.map +0 -1
- package/dist/webext-links.js.map +0 -1
- package/dist/webext-protocol.d.ts.map +0 -1
- package/dist/webext-protocol.js.map +0 -1
- package/dist/workflow-faults.d.ts.map +0 -1
- package/dist/workflow-faults.js.map +0 -1
- package/dist/workspace-state.d.ts.map +0 -1
- package/dist/workspace-state.js.map +0 -1
- package/src/agent-catalog.ts +0 -316
- package/src/agent-runtimes.ts +0 -419
- package/src/approvals-execution.ts +0 -96
- package/src/arrival.ts +0 -160
- package/src/batch-runs.ts +0 -188
- package/src/capability-ledger.test.ts +0 -140
- package/src/capability-secrets.ts +0 -20
- package/src/command-classes.ts +0 -617
- package/src/command-run.ts +0 -78
- package/src/contract-lock.test.ts +0 -23
- package/src/contract-lock.ts +0 -66
- package/src/conversation-ids.ts +0 -207
- package/src/credential-material.ts +0 -181
- package/src/definition.ts +0 -207
- package/src/documents.ts +0 -67
- package/src/embed.ts +0 -164
- package/src/events.ts +0 -1870
- package/src/fast-tier.ts +0 -72
- package/src/history-state.ts +0 -174
- package/src/host-protocol.ts +0 -36
- package/src/hostnames.ts +0 -180
- package/src/ingress-contract.ts +0 -157
- package/src/ingress-protocol.ts +0 -625
- package/src/listener-protocol.ts +0 -96
- package/src/mentions.ts +0 -25
- package/src/model-order.ts +0 -262
- package/src/model-pins.ts +0 -132
- package/src/model-roles.test.ts +0 -52
- package/src/model-roles.ts +0 -320
- package/src/needs-action.ts +0 -14
- package/src/output-fields.ts +0 -111
- package/src/overlay-lint.ts +0 -116
- package/src/path-refs.ts +0 -59
- package/src/peer-dial.ts +0 -163
- package/src/plan-pools.ts +0 -92
- package/src/prompt-complexity.ts +0 -334
- package/src/provider-specs.ts +0 -432
- package/src/request-id.ts +0 -41
- package/src/routes.ts +0 -219
- package/src/runner-protocol.ts +0 -239
- package/src/runtime-state.ts +0 -140
- package/src/safety-policy.test.ts +0 -88
- package/src/safety-policy.ts +0 -258
- package/src/search-globs.ts +0 -76
- package/src/session-names.ts +0 -44
- package/src/share-paths.ts +0 -68
- package/src/shell-regions.ts +0 -289
- package/src/starter.ts +0 -13
- package/src/state-portability.ts +0 -56
- package/src/terminal-protocol.ts +0 -16
- package/src/title.ts +0 -267
- package/src/tunnel-ids.ts +0 -57
- package/src/versions.ts +0 -48
- package/src/webext-links.ts +0 -90
- package/src/webext-protocol.ts +0 -27
- package/src/workspace-state.ts +0 -1090
- /package/dist/{hostnames.d.ts → ids/hostnames.d.ts} +0 -0
- /package/dist/{hostnames.js → ids/hostnames.js} +0 -0
- /package/dist/{session-names.d.ts → ids/session-names.d.ts} +0 -0
- /package/dist/{session-names.js → ids/session-names.js} +0 -0
- /package/dist/{share-paths.d.ts → ids/share-paths.d.ts} +0 -0
- /package/dist/{share-paths.js → ids/share-paths.js} +0 -0
- /package/dist/{agent-catalog.js → models/agent-catalog.js} +0 -0
- /package/dist/{agent-runtimes.d.ts → models/agent-runtimes.d.ts} +0 -0
- /package/dist/{agent-runtimes.js → models/agent-runtimes.js} +0 -0
- /package/dist/{fast-tier.js → models/fast-tier.js} +0 -0
- /package/dist/{model-order.d.ts → models/model-order.d.ts} +0 -0
- /package/dist/{model-order.js → models/model-order.js} +0 -0
- /package/dist/{model-pins.js → models/model-pins.js} +0 -0
- /package/dist/{plan-pools.js → models/plan-pools.js} +0 -0
- /package/dist/{prompt-complexity.d.ts → models/prompt-complexity.d.ts} +0 -0
- /package/dist/{prompt-complexity.js → models/prompt-complexity.js} +0 -0
- /package/dist/{provider-specs.d.ts → models/provider-specs.d.ts} +0 -0
- /package/dist/{provider-specs.js → models/provider-specs.js} +0 -0
- /package/dist/{approvals-execution.d.ts → policy/approvals-execution.d.ts} +0 -0
- /package/dist/{approvals-execution.js → policy/approvals-execution.js} +0 -0
- /package/dist/{capability-env.d.ts → policy/capability-env.d.ts} +0 -0
- /package/dist/{capability-env.js → policy/capability-env.js} +0 -0
- /package/dist/{capability-secrets.d.ts → policy/capability-secrets.d.ts} +0 -0
- /package/dist/{capability-secrets.js → policy/capability-secrets.js} +0 -0
- /package/dist/{command-run.js → policy/command-run.js} +0 -0
- /package/dist/{credential-material.d.ts → policy/credential-material.d.ts} +0 -0
- /package/dist/{credential-material.js → policy/credential-material.js} +0 -0
- /package/dist/{needs-action.d.ts → policy/needs-action.d.ts} +0 -0
- /package/dist/{needs-action.js → policy/needs-action.js} +0 -0
- /package/dist/{output-fields.d.ts → policy/output-fields.d.ts} +0 -0
- /package/dist/{output-fields.js → policy/output-fields.js} +0 -0
- /package/dist/{overlay-lint.d.ts → policy/overlay-lint.d.ts} +0 -0
- /package/dist/{overlay-lint.js → policy/overlay-lint.js} +0 -0
- /package/dist/{owner-ticket.d.ts → policy/owner-ticket.d.ts} +0 -0
- /package/dist/{search-globs.d.ts → policy/search-globs.d.ts} +0 -0
- /package/dist/{search-globs.js → policy/search-globs.js} +0 -0
- /package/dist/{ingress-contract.d.ts → protocol/ingress-contract.d.ts} +0 -0
- /package/dist/{ingress-protocol.d.ts → protocol/ingress-protocol.d.ts} +0 -0
- /package/dist/{ingress-protocol.js → protocol/ingress-protocol.js} +0 -0
- /package/dist/{listener-protocol.d.ts → protocol/listener-protocol.d.ts} +0 -0
- /package/dist/{peer-mcp-server.d.ts → protocol/peer-mcp-server.d.ts} +0 -0
- /package/dist/{peer-mcp-server.js → protocol/peer-mcp-server.js} +0 -0
- /package/dist/{request-id.d.ts → protocol/request-id.d.ts} +0 -0
- /package/dist/{request-id.js → protocol/request-id.js} +0 -0
- /package/dist/{routes.d.ts → protocol/routes.d.ts} +0 -0
- /package/dist/{routes.js → protocol/routes.js} +0 -0
- /package/dist/{sse.d.ts → protocol/sse.d.ts} +0 -0
- /package/dist/{sse.js → protocol/sse.js} +0 -0
- /package/dist/{terminal-protocol.js → protocol/terminal-protocol.js} +0 -0
- /package/dist/{webext-links.d.ts → protocol/webext-links.d.ts} +0 -0
- /package/dist/{webext-links.js → protocol/webext-links.js} +0 -0
- /package/dist/{arrival.d.ts → state/arrival.d.ts} +0 -0
- /package/dist/{contract-lock.d.ts → state/contract-lock.d.ts} +0 -0
- /package/dist/{history-state.d.ts → state/history-state.d.ts} +0 -0
- /package/dist/{runtime-state.d.ts → state/runtime-state.d.ts} +0 -0
- /package/dist/{runtime-state.js → state/runtime-state.js} +0 -0
- /package/dist/{starter.d.ts → state/starter.d.ts} +0 -0
- /package/dist/{starter.js → state/starter.js} +0 -0
- /package/dist/{state-portability.d.ts → state/state-portability.d.ts} +0 -0
- /package/dist/{state-portability.js → state/state-portability.js} +0 -0
- /package/dist/{versions.d.ts → state/versions.d.ts} +0 -0
- /package/dist/{versions.js → state/versions.js} +0 -0
- /package/dist/{embed.d.ts → text/embed.d.ts} +0 -0
- /package/dist/{embed.js → text/embed.js} +0 -0
- /package/dist/{mentions.d.ts → text/mentions.d.ts} +0 -0
- /package/dist/{mentions.js → text/mentions.js} +0 -0
- /package/dist/{path-refs.d.ts → text/path-refs.d.ts} +0 -0
- /package/dist/{path-refs.js → text/path-refs.js} +0 -0
- /package/dist/{shell-regions.js → text/shell-regions.js} +0 -0
- /package/dist/{title.d.ts → text/title.d.ts} +0 -0
- /package/dist/{title.js → text/title.js} +0 -0
- /package/src/{hostnames.test.ts → ids/hostnames.test.ts} +0 -0
- /package/src/{share-paths.test.ts → ids/share-paths.test.ts} +0 -0
- /package/src/{capability-env.ts → policy/capability-env.ts} +0 -0
- /package/src/{overlay-lint.test.ts → policy/overlay-lint.test.ts} +0 -0
- /package/src/{search-globs.test.ts → policy/search-globs.test.ts} +0 -0
- /package/src/{ingress-contract.test.ts → protocol/ingress-contract.test.ts} +0 -0
- /package/src/{peer-mcp-server.test.ts → protocol/peer-mcp-server.test.ts} +0 -0
- /package/src/{sse.ts → protocol/sse.ts} +0 -0
- /package/src/{versions.test.ts → state/versions.test.ts} +0 -0
- /package/src/{embed.test.ts → text/embed.test.ts} +0 -0
- /package/src/{mentions.test.ts → text/mentions.test.ts} +0 -0
- /package/src/{path-refs.test.ts → text/path-refs.test.ts} +0 -0
package/src/events.ts
DELETED
|
@@ -1,1870 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { AgentHarnessSchema, AgentProviderSchema, PermissionModeSchema } from "./schemas/agent.js";
|
|
3
|
-
import { AgentSummarySchema, LandConflictSchema } from "./schemas/agents.js";
|
|
4
|
-
import { RateLimitInfoSchema } from "./schemas/claude-gate.js";
|
|
5
|
-
import { FastModeStateSchema } from "./schemas/fast-mode.js";
|
|
6
|
-
import { AccountUsageSchema, AgentReplySchema, ProviderRefusalSchema, UsageWindowSchema } from "./schemas/plan-limits.js";
|
|
7
|
-
import { CredentialGateKindSchema, CredentialGateScopeSchema, CredentialLaneSchema } from "./schemas/secrets.js";
|
|
8
|
-
import { ShareDetailSchema } from "./schemas/share.js";
|
|
9
|
-
import { MemberRoleSchema } from "./schemas/shared.js";
|
|
10
|
-
import { SubagentKindSchema, SubagentStatusSchema, SubagentVerificationSchema } from "./schemas/terminal.js";
|
|
11
|
-
|
|
12
|
-
// The wire shapes streamed from the daemon's event-iterator procedures. This is their canonical home: the
|
|
13
|
-
// daemon yields them and the browser client consumes them from the same schema, so the two can't drift (they
|
|
14
|
-
// used to be hand-duplicated across repos). Schemas, not bare types, because oRPC's `eventIterator(...)`
|
|
15
|
-
// validates each frame against them.
|
|
16
|
-
|
|
17
|
-
// One interactive question the agent asks via the `ask` tool (mirrors AskUserQuestion's input shape).
|
|
18
|
-
export const AskOptionSchema = z.object({
|
|
19
|
-
label: z.string().describe("The choice, in a few words."),
|
|
20
|
-
description: z.string().describe("What picking it means."),
|
|
21
|
-
preview: z.string().optional().describe("Something to look at while deciding: a mock-up, a snippet, a layout."),
|
|
22
|
-
});
|
|
23
|
-
export type AskOption = z.infer<typeof AskOptionSchema>;
|
|
24
|
-
|
|
25
|
-
export const AskQuestionSchema = z.object({
|
|
26
|
-
question: z.string().describe("What the agent is asking."),
|
|
27
|
-
header: z.string().describe("A short label for the question."),
|
|
28
|
-
multiSelect: z.boolean().describe("Whether more than one answer can be picked."),
|
|
29
|
-
options: z.array(AskOptionSchema).describe("The choices offered. A free-text answer is always possible as well."),
|
|
30
|
-
});
|
|
31
|
-
export type AskQuestion = z.infer<typeof AskQuestionSchema>;
|
|
32
|
-
|
|
33
|
-
/* THE PROGRAM A COMMAND CARD IS HOLDING, as the thing it is rather than as prose about it.
|
|
34
|
-
*
|
|
35
|
-
* It used to ride in `description`, the field every other permission ask fills with a sentence, which left the
|
|
36
|
-
* card with no way to know it was holding four hundred characters of shell: it rendered them as a paragraph,
|
|
37
|
-
* wrapped mid-flag, and the fragment that caused the hold was somewhere in the middle of it.
|
|
38
|
-
*
|
|
39
|
-
* `spans` is where the pattern match fired, computed by the classifier at the moment it did (contract's
|
|
40
|
-
* command-classes.ts, matchCommand) and carried rather than re-derived: a browser that re-ran the patterns
|
|
41
|
-
* would be a second classifier, and the day the two disagreed the card would be marking a fragment the daemon
|
|
42
|
-
* never saw. Offsets are into `text` AFTER truncation, so they are always paintable.
|
|
43
|
-
*
|
|
44
|
-
* IT IS NOT A CLAIM ABOUT WHY THE CARD EXISTS, and the card no longer presents it as one. The reason is the
|
|
45
|
-
* judge's sentence in the title; these are the fragments TRIAGE noticed, all of the matched classes' rather
|
|
46
|
-
* than whichever sorts first — the card used to show one class's and label them "Stopped for", so a command
|
|
47
|
-
* that cleaned a build directory on its way to publishing offered `rm -rf …` as its reason under a sentence
|
|
48
|
-
* about npm. Under the hard rule the title DOES name a class, so there the marks are that class's alone.
|
|
49
|
-
*
|
|
50
|
-
* `language` is a Shiki grammar id, and the two are the two execution backends the gate reads (command-gate's
|
|
51
|
-
* EXECUTION_SOURCES): a shell line and a script. */
|
|
52
|
-
export const ProgramAskSchema = z.object({
|
|
53
|
-
text: z.string().describe("What would run."),
|
|
54
|
-
language: z.enum(["bash", "javascript"]).describe("Which of the two backends it is written for, named as the grammar that colours it."),
|
|
55
|
-
truncated: z
|
|
56
|
-
.boolean()
|
|
57
|
-
.describe(
|
|
58
|
-
"Whether this is an excerpt of a longer program, so the card can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count.",
|
|
59
|
-
),
|
|
60
|
-
spans: z
|
|
61
|
-
.array(z.object({ start: z.number().int().nonnegative(), end: z.number().int().nonnegative() }))
|
|
62
|
-
.describe(
|
|
63
|
-
"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping.",
|
|
64
|
-
),
|
|
65
|
-
});
|
|
66
|
-
export type ProgramAsk = z.infer<typeof ProgramAskSchema>;
|
|
67
|
-
|
|
68
|
-
// One per-tool permission prompt (the SDK's canUseTool callback, surfaced as a card). The daemon passes the
|
|
69
|
-
// bridge's own rendered strings through rather than re-deriving them, so the prompt reads exactly as Claude
|
|
70
|
-
// Code words it. `alwaysLabel` is present only when the SDK offered rules to persist, without it the card
|
|
71
|
-
// shows allow-once / deny alone, because there is nothing an "always" answer could remember.
|
|
72
|
-
export const PermissionAskSchema = z.object({
|
|
73
|
-
toolName: z.string().describe("Which tool it wants to use."),
|
|
74
|
-
// "Claude wants to read foo.txt", the full prompt sentence, when the bridge rendered one.
|
|
75
|
-
title: z.string().optional().describe("The whole question, as a sentence, exactly as the runtime words it."),
|
|
76
|
-
// Short noun phrase for the allow button ("Read file").
|
|
77
|
-
displayName: z.string().optional().describe("A short phrase for the button, such as read file."),
|
|
78
|
-
description: z.string().optional().describe("More about what it is asking for."),
|
|
79
|
-
// Why the prompt fired ('rule' | 'mode' | 'classifier' | …), shown as the card's muted subline.
|
|
80
|
-
reason: z.string().optional().describe("Why it is asking at all: a rule, the current mode, something that looked risky."),
|
|
81
|
-
// The file the request is about, when it is about one (workspace-root-relative).
|
|
82
|
-
path: z.string().optional().describe("Which file it concerns, when it concerns one."),
|
|
83
|
-
alwaysLabel: z
|
|
84
|
-
.string()
|
|
85
|
-
.optional()
|
|
86
|
-
.describe(
|
|
87
|
-
"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.",
|
|
88
|
-
),
|
|
89
|
-
program: ProgramAskSchema.optional().describe(
|
|
90
|
-
"The program this card is holding, when the card is about one. Present on a command gate's card and absent on every other permission ask.",
|
|
91
|
-
),
|
|
92
|
-
/* THE JUDGE'S OWN SENTENCE, WHERE THE TITLE IS SOMEBODY ELSE'S. On an ordinary command card the sentence IS
|
|
93
|
-
* the title (the judge read the owner's policy and the program, and its account of why this needs asking is
|
|
94
|
-
* the only account there is), so this is left off rather than printing the same words twice. It carries the
|
|
95
|
-
* sentence on the two cards whose title says something the sentence cannot: the hard rule's, which names the
|
|
96
|
-
* consequence that stopped it, and a machine command's, which names the device.
|
|
97
|
-
*
|
|
98
|
-
* Written by the quick model from the program text and the policy, never by the agent being gated — a card
|
|
99
|
-
* whose persuasive half was authored by the thing it is stopping argues for its own approval, and the turns
|
|
100
|
-
* that raise cards are exactly the ones whose account of themselves may be a stranger's. */
|
|
101
|
-
explain: z
|
|
102
|
-
.string()
|
|
103
|
-
.optional()
|
|
104
|
-
.describe(
|
|
105
|
-
"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.",
|
|
106
|
-
),
|
|
107
|
-
});
|
|
108
|
-
export type PermissionAsk = z.infer<typeof PermissionAskSchema>;
|
|
109
|
-
|
|
110
|
-
/* ONE PRICED SERVICE RUN, OFFERED, the card the daemon raises when the agent asks to run a premium service
|
|
111
|
-
* (platform/service-offer.ts). Everything with a number on it is the PLATFORM's answer, relayed verbatim from
|
|
112
|
-
* the catalog it serves the daemon: the model that asked contributes `request` (the JSON it wants sent) and
|
|
113
|
-
* `why` (its one line of rationale), and nothing else, which is what makes the price on the card impossible
|
|
114
|
-
* to misquote, and the click on it the only way the run can happen. */
|
|
115
|
-
export const ServiceOfferSchema = z.object({
|
|
116
|
-
// The service, as the platform lists it: `<slug>` is what the run names, the rest is the catalog row.
|
|
117
|
-
slug: z.string().describe("Which service."),
|
|
118
|
-
name: z.string().describe("What it is called."),
|
|
119
|
-
publisher: z.string().describe("Who runs it."),
|
|
120
|
-
description: z.string().describe("What it does."),
|
|
121
|
-
creditsPerRun: z
|
|
122
|
-
.number()
|
|
123
|
-
.describe(
|
|
124
|
-
"What one run costs. Stated by the platform rather than by the agent asking, which is what makes the price impossible to misquote.",
|
|
125
|
-
),
|
|
126
|
-
/* Whether the platform still has this listing on probation, a new provider that passed admission's
|
|
127
|
-
* mechanical gates but has not yet served enough runs cleanly to graduate. It rides the card because
|
|
128
|
-
* probation is the honest form of "listed automatically, not vouched for": the member approving the
|
|
129
|
-
* spend is the person who should know that, and the platform is the only party that can say it. */
|
|
130
|
-
probation: z
|
|
131
|
-
.boolean()
|
|
132
|
-
.optional()
|
|
133
|
-
.describe(
|
|
134
|
-
"The listing is new and has not yet served enough runs cleanly to be trusted. The honest form of listed automatically but not vouched for, and the person approving the spend is who should know it.",
|
|
135
|
-
),
|
|
136
|
-
// The owner's meter as the platform stated it with the catalog, what "N left today" renders from. Absent
|
|
137
|
-
// when the platform sent none (it answers a meter only to a member, and membership was already checked
|
|
138
|
-
// before this card went up, so in practice it is present; the field stays honest about the wire).
|
|
139
|
-
credits: z
|
|
140
|
-
.object({
|
|
141
|
-
allowance: z.number().describe("How many credits the period gives."),
|
|
142
|
-
remaining: z.number().describe("How many are left."),
|
|
143
|
-
resetsAt: z.string().describe("When they refill."),
|
|
144
|
-
})
|
|
145
|
-
.optional()
|
|
146
|
-
.describe("Your own meter, as the platform stated it."),
|
|
147
|
-
// The request body the agent wants forwarded, verbatim, shown so the owner can see what leaves.
|
|
148
|
-
request: z.string().describe("Exactly what would be sent, so you can see what leaves before agreeing to it."),
|
|
149
|
-
// The agent's one-line case for spending, the only prose on the card that is the model's.
|
|
150
|
-
why: z.string().optional().describe("The agent's case for spending, and the only words on this card that are the agent's."),
|
|
151
|
-
});
|
|
152
|
-
export type ServiceOffer = z.infer<typeof ServiceOfferSchema>;
|
|
153
|
-
|
|
154
|
-
/* WHAT A SERVICE STREAMS, the provider's event vocabulary, stated once here and imported by everyone who
|
|
155
|
-
* touches it: the platform validates each line of a provider's NDJSON against this before relaying it, the
|
|
156
|
-
* daemon turns `status` events into transcript frames, and the editor renders them under the offer card.
|
|
157
|
-
* A run is `status` lines (each replaces the last, a spinner label, not a log) ending in exactly one
|
|
158
|
-
* `result`, whose `data` is the answer the agent acts on. The union is where future event kinds land when
|
|
159
|
-
* services start streaming richer transcript elements; today's two are the smallest honest set. */
|
|
160
|
-
export const ServiceStreamEventSchema = z.discriminatedUnion(`event`, [
|
|
161
|
-
z.object({
|
|
162
|
-
event: z.literal(`status`).describe("Progress. Each one replaces the last: a label, not a log."),
|
|
163
|
-
text: z.string().describe("What it is doing."),
|
|
164
|
-
}),
|
|
165
|
-
z.object({
|
|
166
|
-
event: z.literal(`result`).describe("The answer. Exactly one of these ends a run."),
|
|
167
|
-
data: z.unknown().describe("The answer itself, in whatever shape that service returns."),
|
|
168
|
-
}),
|
|
169
|
-
]);
|
|
170
|
-
export type ServiceStreamEvent = z.infer<typeof ServiceStreamEventSchema>;
|
|
171
|
-
|
|
172
|
-
/* ONE MISSING CAPABILITY, ASKED FOR, the card the daemon raises when the agent hits something this sandbox
|
|
173
|
-
* is not connected to (capabilities/capability-offer.ts). `card` names the catalog card and `name` is that
|
|
174
|
-
* card's own title, both resolved by the daemon from the catalog it validates the ask against, the model
|
|
175
|
-
* that asked contributes `why` (its one line of rationale) and nothing else, which is what makes the card
|
|
176
|
-
* impossible to misrepresent, and the click on it the only way anything gets connected. */
|
|
177
|
-
export const CapabilityOfferSchema = z.object({
|
|
178
|
-
// The catalog card being asked for, and how the catalog itself titles it ("Notion", "GitHub", "Docker").
|
|
179
|
-
card: z.string().describe("Which connection is being asked for."),
|
|
180
|
-
name: z.string().describe("What it is called, as the catalogue titles it rather than as the agent named it."),
|
|
181
|
-
// The agent's one-line case for connecting it, the only prose on the card that is the model's.
|
|
182
|
-
why: z.string().optional().describe("The agent's case for connecting it, and the only words on this card that are the agent's."),
|
|
183
|
-
});
|
|
184
|
-
export type CapabilityOffer = z.infer<typeof CapabilityOfferSchema>;
|
|
185
|
-
|
|
186
|
-
/* The trailer the PLATFORM appends to every relayed run stream, never provider-authored: it is the ledger
|
|
187
|
-
* speaking after the stream settled. `ok` means the run served and was charged (`remaining` is the meter
|
|
188
|
-
* after); `refunded` means the provider's stream died before its `result` and the charge was reversed. */
|
|
189
|
-
export const ServiceRunReceiptSchema = z.object({
|
|
190
|
-
event: z
|
|
191
|
-
.literal(`receipt`)
|
|
192
|
-
.describe("The last line of a run, added by the platform rather than by the service. The ledger speaking after the fact."),
|
|
193
|
-
outcome: z.enum([`ok`, `refunded`]).describe("Whether it served and was charged, or died before answering and the charge was reversed."),
|
|
194
|
-
credits: z.number().describe("What it cost."),
|
|
195
|
-
remaining: z.number().optional().describe("What is left afterwards."),
|
|
196
|
-
});
|
|
197
|
-
export type ServiceRunReceipt = z.infer<typeof ServiceRunReceiptSchema>;
|
|
198
|
-
|
|
199
|
-
/* ONE OUTBOUND USDC PAYMENT, OFFERED, the card the daemon raises when the agent asks to pay an x402
|
|
200
|
-
* endpoint out of the sandbox wallet (wallet/payment-offer.ts). Every number on it is the daemon's own
|
|
201
|
-
* arithmetic over the ENDPOINT's parsed challenge and the wallet's own ledger, the model that asked
|
|
202
|
-
* contributes `why` (its one line of rationale) and nothing else, which is what makes the price on the card
|
|
203
|
-
* impossible to misquote, and the click on it the only way the money can move. */
|
|
204
|
-
export const PaymentOfferSchema = z.object({
|
|
205
|
-
// The paid resource, as the endpoint's challenge stated it.
|
|
206
|
-
url: z.string().describe("What is being paid for."),
|
|
207
|
-
description: z.string().optional().describe("What the endpoint says it is."),
|
|
208
|
-
// Where the money goes, verbatim off the challenge: recipient address, CAIP-2 network, token contract.
|
|
209
|
-
payTo: z.string().describe("Where the money goes, taken verbatim from the endpoint's own demand."),
|
|
210
|
-
network: z.string().describe("On which network."),
|
|
211
|
-
asset: z.string().describe("In which token."),
|
|
212
|
-
// The token's display name ("USDC"), dollar-pegged, which is what lets every amount below read as USD.
|
|
213
|
-
assetName: z.string().describe("That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."),
|
|
214
|
-
// The exact price in display units ("0.10"), the x402 exact scheme has no ranges, so this is the whole
|
|
215
|
-
// spend, not a ceiling.
|
|
216
|
-
amountUsd: z.string().describe("The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."),
|
|
217
|
-
// The wallet's meter as the daemon's ledger states it, what "spent today / cap" renders from.
|
|
218
|
-
spentTodayUsd: z.string().describe("What has already gone out today."),
|
|
219
|
-
dailyCapUsd: z.string().describe("What may go out in a day."),
|
|
220
|
-
// The agent's one-line case for paying, the only prose on the card that is the model's.
|
|
221
|
-
why: z.string().optional().describe("The agent's case for paying, and the only words on this card that are the agent's."),
|
|
222
|
-
});
|
|
223
|
-
export type PaymentOffer = z.infer<typeof PaymentOfferSchema>;
|
|
224
|
-
|
|
225
|
-
/* ONE GATED CREDENTIAL, ASKED FOR, the card the daemon raises when the agent reaches for a secret or a
|
|
226
|
-
* connected account the owner put behind a named person (secrets/credential-gate.ts).
|
|
227
|
-
*
|
|
228
|
-
* Every field but `why` is the daemon's own: the subject and its approvers come off the gate policy the owner
|
|
229
|
-
* wrote (which lives off the workspace, where the agent cannot edit it), the lane and detail come from the
|
|
230
|
-
* exit that was about to spend the credential, and the scope is the policy's, not the asker's. The model
|
|
231
|
-
* contributes one line of rationale and nothing else, which is what makes the card impossible to
|
|
232
|
-
* misrepresent: a prompt-injected turn can ask for the production password and cannot make the card say it is
|
|
233
|
-
* asking for the staging one.
|
|
234
|
-
*
|
|
235
|
-
* THE APPROVERS ARE ON THE CARD because the card is not addressed to "the owner" the way every other offer
|
|
236
|
-
* here is — it is addressed to a LIST, the server checks the clicker's verified identity against it, and a
|
|
237
|
-
* click from anybody else is refused with the card left standing. So the names have to be visible: a card
|
|
238
|
-
* whose buttons do nothing for the person looking at it must say who it is waiting for. */
|
|
239
|
-
export const CredentialOfferSchema = z.object({
|
|
240
|
-
// The gate's subject: a secret's reference name (`DATABASE_URL`) or a capability id (`reddit`).
|
|
241
|
-
subject: z.string().describe("Which credential is being asked for."),
|
|
242
|
-
kind: CredentialGateKindSchema,
|
|
243
|
-
lane: CredentialLaneSchema,
|
|
244
|
-
// Where it would go, in the reader's terms: the head of the agent's command line, the page's host, or the
|
|
245
|
-
// capability's own name. Reference-form by construction on the secret lanes (resolution is what fires the
|
|
246
|
-
// ask), so this can be shown without leaking anything.
|
|
247
|
-
detail: z
|
|
248
|
-
.string()
|
|
249
|
-
.optional()
|
|
250
|
-
.describe("Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point."),
|
|
251
|
-
// The agent's one-line case, the only prose on the card that is the model's.
|
|
252
|
-
why: z.string().optional().describe("The agent's case for using it, and the only words on this card that are the agent's."),
|
|
253
|
-
approvers: z.array(z.string()).describe("Who may release it. A click from anyone else is refused and leaves the card standing."),
|
|
254
|
-
scope: CredentialGateScopeSchema,
|
|
255
|
-
});
|
|
256
|
-
export type CredentialOffer = z.infer<typeof CredentialOfferSchema>;
|
|
257
|
-
|
|
258
|
-
// One provider-advertised slash command, an ACP agent's available_commands entry, or a Claude Code session's
|
|
259
|
-
// supportedCommands() (its built-ins plus the workspace's own .claude/commands and any plugin/skill commands).
|
|
260
|
-
// `hint` is the argument placeholder the popover shows after the name.
|
|
261
|
-
export const AgentCommandSchema = z.object({
|
|
262
|
-
name: z.string().describe("What to type, without the leading slash."),
|
|
263
|
-
description: z.string().describe("What it does."),
|
|
264
|
-
hint: z.string().optional().describe("What its argument should look like, shown after the name."),
|
|
265
|
-
});
|
|
266
|
-
export type AgentCommand = z.infer<typeof AgentCommandSchema>;
|
|
267
|
-
|
|
268
|
-
// GET /agent/commands, which provider's last-published list to read; absent = claude, matching AgentTurn.
|
|
269
|
-
export const AgentCommandsQuerySchema = z.object({
|
|
270
|
-
agent: AgentProviderSchema.optional().describe("Whose commands to read. Leave it out for Claude."),
|
|
271
|
-
});
|
|
272
|
-
export const AgentCommandsSchema = z.object({
|
|
273
|
-
commands: z.array(AgentCommandSchema).describe("The shortcut commands, as the provider last published them."),
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// One TodoWrite/Task checklist item, surfaced live so the UI shows the agent's plan-of-work (Claude Code style).
|
|
277
|
-
export const TodoItemSchema = z.object({
|
|
278
|
-
content: z.string().describe("The item, as the agent wrote it."),
|
|
279
|
-
status: z.enum(["pending", "in_progress", "completed"]).describe("Where it is."),
|
|
280
|
-
activeForm: z
|
|
281
|
-
.string()
|
|
282
|
-
.optional()
|
|
283
|
-
.describe("How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do."),
|
|
284
|
-
});
|
|
285
|
-
export type TodoItem = z.infer<typeof TodoItemSchema>;
|
|
286
|
-
|
|
287
|
-
// Context-window fill for a conversation: how many tokens the latest request sent vs the model's window, so
|
|
288
|
-
// the UI can warn as the chat nears auto-compaction. Per-conversation, unlike the account-wide usage above.
|
|
289
|
-
export const ContextUsageSchema = z.object({
|
|
290
|
-
tokens: z.number().describe("How much the latest request sent, all told."),
|
|
291
|
-
contextWindow: z.number().describe("How much the model can hold. The gap between these two is how close the conversation is to being compacted."),
|
|
292
|
-
});
|
|
293
|
-
export type ContextUsage = z.infer<typeof ContextUsageSchema>;
|
|
294
|
-
|
|
295
|
-
// ACP-aligned tool taxonomy (Agent Client Protocol's ToolKind, verbatim): what a tool call *does*, driving
|
|
296
|
-
// the card icon and the live-writes bookkeeping regardless of which backend named the tool.
|
|
297
|
-
export const ToolKindSchema = z.enum(["read", "edit", "delete", "move", "search", "execute", "think", "fetch", "other"]);
|
|
298
|
-
export type ToolKind = z.infer<typeof ToolKindSchema>;
|
|
299
|
-
|
|
300
|
-
export const ToolCallStatusSchema = z.enum(["pending", "in_progress", "completed", "failed"]);
|
|
301
|
-
export type ToolCallStatus = z.infer<typeof ToolCallStatusSchema>;
|
|
302
|
-
|
|
303
|
-
// A file a tool call touches. Workspace-root-relative, forward-slash (the tree/file route space), adapters
|
|
304
|
-
// normalize from the turn's cwd. `line` is 1-based.
|
|
305
|
-
export const ToolCallLocationSchema = z.object({
|
|
306
|
-
path: z.string().describe("The file, as a workspace path, whatever directory the tool was run from."),
|
|
307
|
-
line: z.number().optional().describe("Which line, counting from one."),
|
|
308
|
-
});
|
|
309
|
-
export type ToolCallLocation = z.infer<typeof ToolCallLocationSchema>;
|
|
310
|
-
|
|
311
|
-
// Structured tool output (ACP's ToolCallContent diff shape, verbatim). `diff` is hunk-level for Edit-style
|
|
312
|
-
// tools (old_string/new_string) and whole-file for Write; an absent oldText means a new file / unknown
|
|
313
|
-
// previous content. Sides are capped daemon-side; `truncated` marks a clipped side.
|
|
314
|
-
//
|
|
315
|
-
// `image` is a PICTURE THE TOOL PRODUCED, carried as a workspace path rather than as bytes. Browser screenshots
|
|
316
|
-
// already live under .intentic/records/artifacts/browser, and provider-generated images are copied into
|
|
317
|
-
// .intentic/records/artifacts/imagegen, so the client fetches either from /workspace/raw like any other file. Base64 on
|
|
318
|
-
// the wire would bloat the event stream and every stored transcript to show bytes the workspace already serves;
|
|
319
|
-
// the path also keeps the picture openable afterwards. Root-relative, forward-slash: the same route space as
|
|
320
|
-
// ToolCallLocation.
|
|
321
|
-
export const ToolCallContentSchema = z.discriminatedUnion("type", [
|
|
322
|
-
z.object({
|
|
323
|
-
type: z.literal("text").describe("Plain output."),
|
|
324
|
-
text: z.string().describe("What the tool said."),
|
|
325
|
-
}),
|
|
326
|
-
z.object({
|
|
327
|
-
type: z.literal("diff").describe("A change to a file."),
|
|
328
|
-
path: z.string().describe("Which file, as a workspace path."),
|
|
329
|
-
oldText: z.string().optional().describe("What was there. Absent for a new file, or where the previous contents are not known."),
|
|
330
|
-
newText: z.string().describe("What is there now."),
|
|
331
|
-
truncated: z.boolean().optional().describe("One of the two sides was too large to send whole."),
|
|
332
|
-
}),
|
|
333
|
-
z.object({
|
|
334
|
-
type: z.literal("image").describe("A picture the tool produced."),
|
|
335
|
-
path: z
|
|
336
|
-
.string()
|
|
337
|
-
.describe(
|
|
338
|
-
"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards.",
|
|
339
|
-
),
|
|
340
|
-
}),
|
|
341
|
-
]);
|
|
342
|
-
export type ToolCallContent = z.infer<typeof ToolCallContentSchema>;
|
|
343
|
-
|
|
344
|
-
/* WHAT A PARKED CARD IS ABOUT: the document the turn wrote and is now asking a question against.
|
|
345
|
-
*
|
|
346
|
-
* A card asks for a decision; until this it carried no SUBJECT. The commonest shape of a real decision is "I
|
|
347
|
-
* analysed this and wrote it up, now choose", and the write-up went into a file whose card had already folded
|
|
348
|
-
* itself into `Write · +135 −0` twenty tool calls back. So the reader was asked to choose between options
|
|
349
|
-
* describing a document the chat had never shown them.
|
|
350
|
-
*
|
|
351
|
-
* Carried BY VALUE rather than as a path, for the same reason the diff on a tool call is: the bytes are already
|
|
352
|
-
* in hand when the card is raised, a path would make the card's meaning depend on a file that keeps changing
|
|
353
|
-
* under it, and a restored or published transcript has no workspace to go read. The path rides along anyway, so
|
|
354
|
-
* a document past the wire cap still has somewhere to send the reader.
|
|
355
|
-
*
|
|
356
|
-
* Nothing is asked of the MODEL for this. It calls `ask` exactly as before; the daemon knows what the turn
|
|
357
|
-
* wrote, because every write came past it as a frame (documents.ts decides which of them is a document). A
|
|
358
|
-
* harness that can see the answer must not spend prompt on asking the model to repeat it. */
|
|
359
|
-
export const CardDocumentSchema = z.object({
|
|
360
|
-
path: z.string().describe("Where it lives, as a workspace path."),
|
|
361
|
-
title: z.string().describe("What it is called: its opening heading, or its file name."),
|
|
362
|
-
markdown: z.string().describe("The document itself."),
|
|
363
|
-
truncated: z.boolean().optional().describe("It was clipped at the wire cap; the file on disk has more."),
|
|
364
|
-
plan: z.boolean().optional().describe("It is one of the CLI's plan files, written to be approved rather than merely read."),
|
|
365
|
-
});
|
|
366
|
-
export type CardDocument = z.infer<typeof CardDocumentSchema>;
|
|
367
|
-
|
|
368
|
-
/* ONE CARD'S OWN FIELDS, spelled once. Three readers carry the same card and must agree on what it is: the
|
|
369
|
-
* frame that raises it (AgentEventSchema below), the journal entry that keeps a parked one across a restart
|
|
370
|
-
* (ParkedCardSchema), and the record row that keeps it for good (TranscriptRowSchema's card fields). A shape
|
|
371
|
-
* declared inline in each was three shapes with one name. */
|
|
372
|
-
const REQUEST_ID = z.string().describe("What to send back when you answer.");
|
|
373
|
-
const planCard = {
|
|
374
|
-
requestId: REQUEST_ID,
|
|
375
|
-
text: z.string().describe("The plan itself."),
|
|
376
|
-
// Present when the adjacent plan prose POINTS at a document instead of being one: the model wrote the real
|
|
377
|
-
// plan to a file and summarised it there. Absent when the text already is the whole plan.
|
|
378
|
-
document: CardDocumentSchema.optional().describe("The write-up this plan refers to, when the plan itself is a pointer to one."),
|
|
379
|
-
};
|
|
380
|
-
const questionCard = {
|
|
381
|
-
requestId: REQUEST_ID,
|
|
382
|
-
questions: z.array(AskQuestionSchema).describe("What it wants to know."),
|
|
383
|
-
document: CardDocumentSchema.optional().describe("The document this turn wrote and is asking about, so the choice can be read beside it."),
|
|
384
|
-
};
|
|
385
|
-
const permissionCard = { requestId: REQUEST_ID };
|
|
386
|
-
// The agent's browser needs a person: it parked mid-sign-in on something it cannot clear itself (a captcha,
|
|
387
|
-
// a password it does not hold, a phone check). `session` names the browser session on /browsers, the card's
|
|
388
|
-
// one action is going THERE, where the live stage and Take control already are; the Browsers banner and this
|
|
389
|
-
// card resolve the same requestId. `account` is the capability the sign-in is for, so the card can say whose
|
|
390
|
-
// login is stuck even after the browser has navigated somewhere unrecognizable.
|
|
391
|
-
const browserHelpCard = {
|
|
392
|
-
requestId: z.string(),
|
|
393
|
-
session: z.string(),
|
|
394
|
-
account: z.string(),
|
|
395
|
-
message: z.string(),
|
|
396
|
-
};
|
|
397
|
-
// The agent's TERMINAL needs a person: a command it started is sitting at a prompt it cannot answer (a
|
|
398
|
-
// one-time password, a security-key touch, a confirm). `session` names the tmux session on the terminal
|
|
399
|
-
// panel, the card's one action is going THERE, where the live pane and its prompt already are, which is
|
|
400
|
-
// the same division of labour the browser card has with /browsers.
|
|
401
|
-
const terminalHelpCard = {
|
|
402
|
-
requestId: z.string(),
|
|
403
|
-
session: z.string(),
|
|
404
|
-
message: z.string(),
|
|
405
|
-
};
|
|
406
|
-
const serviceOfferCard = { requestId: z.string(), offer: ServiceOfferSchema };
|
|
407
|
-
const capabilityOfferCard = { requestId: z.string(), offer: CapabilityOfferSchema };
|
|
408
|
-
const paymentOfferCard = { requestId: z.string(), offer: PaymentOfferSchema };
|
|
409
|
-
const credentialOfferCard = { requestId: z.string(), offer: CredentialOfferSchema };
|
|
410
|
-
|
|
411
|
-
/* HOW AN OFFER'S ACCEPTED HALF ENDED, the follow-up that lands on the card after the click. Each is the body of
|
|
412
|
-
* the frame that reports it (`service_receipt`, `capability_outcome`, `payment_receipt`) and the field the
|
|
413
|
-
* record keeps it in, one shape for both, so a receipt reopened tomorrow says exactly what the live card said. */
|
|
414
|
-
export const ServiceReceiptSchema = z.object({
|
|
415
|
-
outcome: z.enum(["ok", "refunded", "refused"]),
|
|
416
|
-
credits: z.number(),
|
|
417
|
-
remaining: z.number().optional(),
|
|
418
|
-
});
|
|
419
|
-
export type ServiceReceipt = z.infer<typeof ServiceReceiptSchema>;
|
|
420
|
-
export const CapabilityOutcomeSchema = z.object({
|
|
421
|
-
outcome: z.enum(["connected", "unfinished"]),
|
|
422
|
-
id: z.string().optional(),
|
|
423
|
-
});
|
|
424
|
-
export type CapabilityOutcome = z.infer<typeof CapabilityOutcomeSchema>;
|
|
425
|
-
export const PaymentReceiptSchema = z.object({
|
|
426
|
-
outcome: z.enum(["paid", "failed"]),
|
|
427
|
-
amountUsd: z.string(),
|
|
428
|
-
transaction: z.string().optional(),
|
|
429
|
-
network: z.string().optional(),
|
|
430
|
-
});
|
|
431
|
-
export type PaymentReceipt = z.infer<typeof PaymentReceiptSchema>;
|
|
432
|
-
/* WHO RELEASED A GATED CREDENTIAL, or that a person refused it. `released` carries the approver's own address,
|
|
433
|
-
* read off the VERIFIED identity on the reply rather than off anything the click claimed, which is what makes
|
|
434
|
-
* the row an audit line rather than a rendering. There is no receipt for a card nobody answered: `resolved`
|
|
435
|
-
* already says so, and inventing "refused" for a deadline would put words in a person's mouth. */
|
|
436
|
-
export const CredentialReceiptSchema = z.object({
|
|
437
|
-
outcome: z.enum(["released", "refused"]),
|
|
438
|
-
approvedBy: z.string().optional(),
|
|
439
|
-
});
|
|
440
|
-
export type CredentialReceipt = z.infer<typeof CredentialReceiptSchema>;
|
|
441
|
-
|
|
442
|
-
/* THE THREE RESTORABLE CARDS, named so the turn journal can hold them verbatim: a parked turn's raised cards
|
|
443
|
-
* are written down beside its prompt (sandbox turn-journal.ts), and a daemon death under the park restores the
|
|
444
|
-
* very same frames instead of ending the turn `interrupted`, the card the user was about to answer survives
|
|
445
|
-
* the restart that killed the process holding it. The two handover cards are deliberately not among them:
|
|
446
|
-
* `browser_help`'s Chromium and `terminal_help`'s waiting command both die with the container, so those parks
|
|
447
|
-
* cannot be restored, only reported. */
|
|
448
|
-
const PlanCardSchema = z.object({
|
|
449
|
-
kind: z.literal("plan").describe("The agent has written a plan and is waiting for a yes."),
|
|
450
|
-
...planCard,
|
|
451
|
-
});
|
|
452
|
-
const QuestionCardSchema = z.object({
|
|
453
|
-
kind: z.literal("question").describe("The agent has asked you something and is waiting."),
|
|
454
|
-
...questionCard,
|
|
455
|
-
});
|
|
456
|
-
const PermissionCardSchema = PermissionAskSchema.extend({
|
|
457
|
-
kind: z.literal("permission").describe("The agent wants to use a tool it needs permission for."),
|
|
458
|
-
...permissionCard,
|
|
459
|
-
});
|
|
460
|
-
export const ParkedCardSchema = z.discriminatedUnion("kind", [PlanCardSchema, QuestionCardSchema, PermissionCardSchema]);
|
|
461
|
-
export type ParkedCard = z.infer<typeof ParkedCardSchema>;
|
|
462
|
-
|
|
463
|
-
// ---- transcript cards ----
|
|
464
|
-
/* THE CARDS A TURN PARKED ON, as a transcript row carries them: the card exactly as it was raised, how it was
|
|
465
|
-
* settled, and whatever landed on it afterwards (a permission's late explanation, an offer's stream and
|
|
466
|
-
* receipt). One shape for the live row and the recorded one, because they are the same row: the daemon folds
|
|
467
|
-
* the turn's frames into these rows as they stream (transcript-fold.ts) and writes the same rows down when the
|
|
468
|
-
* turn settles, so a chat reopened tomorrow is the chat that was on screen.
|
|
469
|
-
*
|
|
470
|
-
* The STATUS is settled by the fold, from the reply that released the card (card-status.ts), and rides the
|
|
471
|
-
* row rather than the reply it came from: every reader wants the verdict, and the one derivation lives beside
|
|
472
|
-
* the fold that applies it. `pending` is a card the turn is still parked on; `cancelled` is nobody answering,
|
|
473
|
-
* the turn stopped or died under the card, which is not a decision and does not read back as one. */
|
|
474
|
-
export const PlanStatusSchema = z.enum(["pending", "approved", "rejected", "cancelled"]);
|
|
475
|
-
export type PlanStatus = z.infer<typeof PlanStatusSchema>;
|
|
476
|
-
export const QuestionStatusSchema = z.enum(["pending", "answered", "cancelled"]);
|
|
477
|
-
export type QuestionStatus = z.infer<typeof QuestionStatusSchema>;
|
|
478
|
-
export const PermissionStatusSchema = z.enum(["pending", "allowed", "always", "denied", "cancelled"]);
|
|
479
|
-
export type PermissionStatus = z.infer<typeof PermissionStatusSchema>;
|
|
480
|
-
export const HelpStatusSchema = z.enum(["pending", "helped", "declined", "cancelled"]);
|
|
481
|
-
export type HelpStatus = z.infer<typeof HelpStatusSchema>;
|
|
482
|
-
export const OfferStatusSchema = z.enum(["pending", "approved", "skipped", "cancelled"]);
|
|
483
|
-
export type OfferStatus = z.infer<typeof OfferStatusSchema>;
|
|
484
|
-
// A yes settles the DECISION, not the ask: the owner is now setting the capability up, so the card moves to
|
|
485
|
-
// `connecting` and stays there until the capability_outcome frame says how the setup ended.
|
|
486
|
-
export const CapabilityOfferStatusSchema = z.enum(["pending", "connecting", "skipped", "cancelled"]);
|
|
487
|
-
export type CapabilityOfferStatus = z.infer<typeof CapabilityOfferStatusSchema>;
|
|
488
|
-
|
|
489
|
-
export const TranscriptPlanSchema = z.object({ ...planCard, status: PlanStatusSchema.describe("Where the decision stands.") });
|
|
490
|
-
export type TranscriptPlan = z.infer<typeof TranscriptPlanSchema>;
|
|
491
|
-
export const TranscriptQuestionSchema = z.object({
|
|
492
|
-
...questionCard,
|
|
493
|
-
status: QuestionStatusSchema.describe("Where the answer stands."),
|
|
494
|
-
answers: z
|
|
495
|
-
.record(z.string(), z.array(z.string()))
|
|
496
|
-
.optional()
|
|
497
|
-
.describe("What was chosen, keyed by the question, with the chosen labels or the user's own words."),
|
|
498
|
-
});
|
|
499
|
-
export type TranscriptQuestion = z.infer<typeof TranscriptQuestionSchema>;
|
|
500
|
-
// `explain`, the judge's sentence, lands here through PermissionAskSchema; it is on the card from the moment
|
|
501
|
-
// it is raised, so nothing patches it in afterwards.
|
|
502
|
-
export const TranscriptPermissionSchema = PermissionAskSchema.extend({ ...permissionCard, status: PermissionStatusSchema.describe("Where the decision stands.") });
|
|
503
|
-
export type TranscriptPermission = z.infer<typeof TranscriptPermissionSchema>;
|
|
504
|
-
export const TranscriptBrowserHelpSchema = z.object({ ...browserHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
|
|
505
|
-
export type TranscriptBrowserHelp = z.infer<typeof TranscriptBrowserHelpSchema>;
|
|
506
|
-
export const TranscriptTerminalHelpSchema = z.object({ ...terminalHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
|
|
507
|
-
export type TranscriptTerminalHelp = z.infer<typeof TranscriptTerminalHelpSchema>;
|
|
508
|
-
export const TranscriptServiceOfferSchema = z.object({
|
|
509
|
-
...serviceOfferCard,
|
|
510
|
-
status: OfferStatusSchema.describe("Where the decision stands."),
|
|
511
|
-
events: z.array(ServiceStreamEventSchema).optional().describe("The approved run's stream, in order (the service_event frames)."),
|
|
512
|
-
receipt: ServiceReceiptSchema.optional().describe("How the approved run ended (the service_receipt frame)."),
|
|
513
|
-
});
|
|
514
|
-
export type TranscriptServiceOffer = z.infer<typeof TranscriptServiceOfferSchema>;
|
|
515
|
-
export const TranscriptCapabilityOfferSchema = z.object({
|
|
516
|
-
...capabilityOfferCard,
|
|
517
|
-
status: CapabilityOfferStatusSchema.describe("Where the decision stands."),
|
|
518
|
-
outcome: CapabilityOutcomeSchema.optional().describe("How an accepted ask's setup ended (the capability_outcome frame)."),
|
|
519
|
-
});
|
|
520
|
-
export type TranscriptCapabilityOffer = z.infer<typeof TranscriptCapabilityOfferSchema>;
|
|
521
|
-
export const TranscriptPaymentOfferSchema = z.object({
|
|
522
|
-
...paymentOfferCard,
|
|
523
|
-
status: OfferStatusSchema.describe("Where the decision stands."),
|
|
524
|
-
receipt: PaymentReceiptSchema.optional().describe("How the approved payment ended (the payment_receipt frame)."),
|
|
525
|
-
});
|
|
526
|
-
export type TranscriptPaymentOffer = z.infer<typeof TranscriptPaymentOfferSchema>;
|
|
527
|
-
export const TranscriptCredentialOfferSchema = z.object({
|
|
528
|
-
...credentialOfferCard,
|
|
529
|
-
status: OfferStatusSchema.describe("Where the decision stands."),
|
|
530
|
-
receipt: CredentialReceiptSchema.optional().describe("Who released it, or that somebody refused (the credential_receipt frame)."),
|
|
531
|
-
});
|
|
532
|
-
export type TranscriptCredentialOffer = z.infer<typeof TranscriptCredentialOfferSchema>;
|
|
533
|
-
|
|
534
|
-
// ---- transcript rows ----
|
|
535
|
-
// What a conversation is made of, on every surface: the rows the daemon folds a turn's frames into as they
|
|
536
|
-
// stream (the live chat renders these, patched as they grow), the rows the record keeps once the turn settles,
|
|
537
|
-
// and the rows /agents/{id}/transcript replays into a reopened tab. One shape because it is one thing: a
|
|
538
|
-
// reopened chat REDRAWS the transcript the user was looking at rather than paraphrasing it, so a row keeps the
|
|
539
|
-
// assistant's thinking and the tool cards its block ran, which is also what lets a runtime handoff carry more
|
|
540
|
-
// than bare prose across to a replacement session (see runtime-history.ts).
|
|
541
|
-
//
|
|
542
|
-
// One tool card. A subagent's own calls and its thinking nest under the Agent card that spawned them, so a
|
|
543
|
-
// delegation reads as one unit instead of a flat run of siblings. z.lazy because the shape refers to itself: a
|
|
544
|
-
// subagent that delegates nests one level deeper.
|
|
545
|
-
export const TranscriptToolSchema: z.ZodType<TranscriptTool> = z.lazy(() =>
|
|
546
|
-
z.object({
|
|
547
|
-
id: z.string().describe("The call's id."),
|
|
548
|
-
name: z.string().describe("Which tool."),
|
|
549
|
-
category: ToolKindSchema.describe(
|
|
550
|
-
"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool.",
|
|
551
|
-
),
|
|
552
|
-
status: ToolCallStatusSchema.describe("How it went."),
|
|
553
|
-
target: z.string().optional().describe("What it acted on, in one line: a file, a command, an address."),
|
|
554
|
-
locations: z.array(ToolCallLocationSchema).optional().describe("The files it touched."),
|
|
555
|
-
content: z.array(ToolCallContentSchema).optional().describe("What it produced: text, a change to a file, or a picture."),
|
|
556
|
-
children: z
|
|
557
|
-
.array(TranscriptToolSchema)
|
|
558
|
-
.optional()
|
|
559
|
-
.describe(
|
|
560
|
-
"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.",
|
|
561
|
-
),
|
|
562
|
-
thinking: z.string().optional().describe("What the agent was reasoning about around this call."),
|
|
563
|
-
subagent: TranscriptSubagentSchema.optional().describe(
|
|
564
|
-
"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.",
|
|
565
|
-
),
|
|
566
|
-
}),
|
|
567
|
-
);
|
|
568
|
-
/* THE CHILD A CALL STARTED, on the card whose id the `subagent`/`subagent_update` frames name (that call's own),
|
|
569
|
-
* so no correlation is needed: an Agent card wears its subagent's live state, and a Bash card that turned out
|
|
570
|
-
* to be a `codex exec` wears its delegate's. The identifying fields arrive once (the `subagent` frame), the
|
|
571
|
-
* moving ones (status, spend, what it is doing) replace as each update lands. */
|
|
572
|
-
export const TranscriptSubagentSchema = z.object({
|
|
573
|
-
kind: SubagentKindSchema,
|
|
574
|
-
agentType: z.string().optional(),
|
|
575
|
-
description: z.string().optional(),
|
|
576
|
-
model: z.string().optional(),
|
|
577
|
-
provider: z.string().optional(),
|
|
578
|
-
background: z.boolean().optional(),
|
|
579
|
-
status: SubagentStatusSchema,
|
|
580
|
-
tokens: z.number().optional(),
|
|
581
|
-
toolUses: z.number().optional(),
|
|
582
|
-
lastTool: z.string().optional(),
|
|
583
|
-
summary: z.string().optional(),
|
|
584
|
-
error: z.string().optional(),
|
|
585
|
-
verification: SubagentVerificationSchema.optional(),
|
|
586
|
-
});
|
|
587
|
-
export type TranscriptSubagent = z.infer<typeof TranscriptSubagentSchema>;
|
|
588
|
-
// Mutable, unlike most of this file: the fold settles a card IN PLACE when its result arrives turns later
|
|
589
|
-
// (transcript-fold.ts's `cards` map, readWorkspaceSession's `awaiting`), which is what saves it a second pass.
|
|
590
|
-
export interface TranscriptTool {
|
|
591
|
-
id: string;
|
|
592
|
-
name: string;
|
|
593
|
-
category: ToolKind;
|
|
594
|
-
status: ToolCallStatus;
|
|
595
|
-
target?: string | undefined;
|
|
596
|
-
locations?: ToolCallLocation[] | undefined;
|
|
597
|
-
content?: ToolCallContent[] | undefined;
|
|
598
|
-
children?: TranscriptTool[] | undefined;
|
|
599
|
-
thinking?: string | undefined;
|
|
600
|
-
subagent?: TranscriptSubagent | undefined;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/* ONE NOTE THE DAEMON PUT IN FRONT OF A USER'S MESSAGE, as both audiences see it: the model reads `text`, and
|
|
604
|
-
* the chat draws `title` on a collapsed row that opens to that same `text`. Shared by the live frame and the
|
|
605
|
-
* restored transcript so a note reads identically whether the tab watched it arrive or reopened an hour later. */
|
|
606
|
-
export const TurnNoteSchema = z.object({
|
|
607
|
-
title: z.string().describe("The one line a reader sees, on a row that opens to the text below."),
|
|
608
|
-
text: z.string().describe("The note itself, which is also exactly what the model was told."),
|
|
609
|
-
});
|
|
610
|
-
export type TurnNote = z.infer<typeof TurnNoteSchema>;
|
|
611
|
-
|
|
612
|
-
// End-of-turn accounting (assistant rows only, the last bubble of a turn): what the turn cost, attached where
|
|
613
|
-
// the answer ended so a reader can see what each exchange spent.
|
|
614
|
-
export const TranscriptUsageSchema = z.object({
|
|
615
|
-
costUsd: z.number().optional(),
|
|
616
|
-
inputTokens: z.number().optional(),
|
|
617
|
-
outputTokens: z.number().optional(),
|
|
618
|
-
durationMs: z.number().optional(),
|
|
619
|
-
numTurns: z.number().optional(),
|
|
620
|
-
});
|
|
621
|
-
export type TranscriptUsage = z.infer<typeof TranscriptUsageSchema>;
|
|
622
|
-
|
|
623
|
-
// One row. Each block of the agent's prose is its own, with the tool cards that block introduced, which is what
|
|
624
|
-
// reproduces the way a turn actually unfolded rather than collapsing its whole narration into one bubble with
|
|
625
|
-
// every tool hanging off the end.
|
|
626
|
-
export const TranscriptRowSchema = z.object({
|
|
627
|
-
/* `notice` is neither side of the conversation: it is something that HAPPENED to the turn, a refusal, a
|
|
628
|
-
* landed delta, a compaction, a stop, written down so a reopened conversation says it too. The one that
|
|
629
|
-
* matters most is a refused turn: a provider that answers "your organization has disabled Claude
|
|
630
|
-
* subscription access" produced no assistant text, so a transcript of the two speakers alone ends on the
|
|
631
|
-
* user's message and reads as broken. */
|
|
632
|
-
role: z
|
|
633
|
-
.enum(["user", "assistant", "notice"])
|
|
634
|
-
.describe(
|
|
635
|
-
"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken.",
|
|
636
|
-
),
|
|
637
|
-
text: z.string().describe("The words."),
|
|
638
|
-
/* WHEN THIS TURN WAS SENT, in epoch milliseconds (user rows only), what the chat shows on the bubble it
|
|
639
|
-
* belongs to. The turn's START, not the moment the record was written: a turn that ran for twenty minutes
|
|
640
|
-
* was still sent when the user pressed send, and a stamp taken at settlement would say the conversation
|
|
641
|
-
* happened at the times its answers finished.
|
|
642
|
-
*
|
|
643
|
-
* Only the user's row carries one, because it is the only row whose moment the daemon actually knows. A
|
|
644
|
-
* turn's frames arrive with no clock of their own, so an assistant bubble could only ever be stamped with
|
|
645
|
-
* the whole turn's start or end, a number that says nothing about when that particular block was written.
|
|
646
|
-
* Rows recorded before this existed simply have none, and the chat draws nothing for them. */
|
|
647
|
-
sentAt: z
|
|
648
|
-
.number()
|
|
649
|
-
.optional()
|
|
650
|
-
.describe(
|
|
651
|
-
"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.",
|
|
652
|
-
),
|
|
653
|
-
// Files the user attached to this turn (user rows only) as workspace-relative paths, the uploads alone:
|
|
654
|
-
// a path @-mentioned inline in the text is already visible there and is not drawn as a chip.
|
|
655
|
-
attachments: z.array(z.string()).optional().describe("Files attached to this message, as workspace paths."),
|
|
656
|
-
/* The checkpoint this message can be rewound to (user rows only), and where this message sits in the
|
|
657
|
-
* conversation's record, which is what the rewind route addresses it by. Never stored: both are stamped
|
|
658
|
-
* onto the live row by the turn's own `checkpoint` frame and onto a replayed row by the read that serves
|
|
659
|
-
* it, looked up from the daemon's rewind points, which a rewind rewrites, so a reopened tab offers exactly
|
|
660
|
-
* the turns that are still there to go back to. */
|
|
661
|
-
checkpointId: z
|
|
662
|
-
.string()
|
|
663
|
-
.optional()
|
|
664
|
-
.describe(
|
|
665
|
-
"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.",
|
|
666
|
-
),
|
|
667
|
-
rewindIndex: z
|
|
668
|
-
.number()
|
|
669
|
-
.int()
|
|
670
|
-
.nonnegative()
|
|
671
|
-
.optional()
|
|
672
|
-
.describe("This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint."),
|
|
673
|
-
thinking: z.string().optional().describe("What the agent was reasoning about."),
|
|
674
|
-
tools: z.array(TranscriptToolSchema).optional().describe("The tool calls this part of the turn made."),
|
|
675
|
-
todos: z.array(TodoItemSchema).optional().describe("The agent's task checklist, as of this bubble."),
|
|
676
|
-
usage: TranscriptUsageSchema.optional().describe("What the turn cost, on the bubble its answer ended in."),
|
|
677
|
-
/* What the daemon added to this turn's message (user rows only), the same notes the live `preamble` frame
|
|
678
|
-
* carries, read off that frame by the fold.
|
|
679
|
-
*
|
|
680
|
-
* On the message rather than as a row of its own, and that matters twice: they ARE part of what was
|
|
681
|
-
* sent, and a record row per turn preamble would break the one-row-per-bubble correspondence a branch counts
|
|
682
|
-
* with. */
|
|
683
|
-
notes: z
|
|
684
|
-
.array(TurnNoteSchema)
|
|
685
|
-
.optional()
|
|
686
|
-
.describe(
|
|
687
|
-
"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.",
|
|
688
|
-
),
|
|
689
|
-
/* THE USER WROTE THIS ROW WEARING THE AGENT'S VOICE (assistant rows only), the composer's "as agent" mode
|
|
690
|
-
* appending straight into the record, with no turn behind it (agents.place).
|
|
691
|
-
*
|
|
692
|
-
* The flag exists for exactly one audience: the HUMAN re-reading the transcript, whose bubble carries a
|
|
693
|
-
* quiet mark so that months later their own words don't pass as the agent's. The one reader that must
|
|
694
|
-
* never see it is the agent itself, a placed line reaches the model only through the handoff that seeds a
|
|
695
|
-
* fresh runtime session (agent/runtime-history.ts), which renders role and text alone, so there the line is
|
|
696
|
-
* indistinguishable from anything the agent genuinely said. Keep it that way: rendering this flag into any
|
|
697
|
-
* agent-facing text would break the feature's whole contract. */
|
|
698
|
-
placed: z
|
|
699
|
-
.boolean()
|
|
700
|
-
.optional()
|
|
701
|
-
.describe(
|
|
702
|
-
"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.",
|
|
703
|
-
),
|
|
704
|
-
/* THE ONE-PRESS OFFER A NOTICE CARRIES (notice rows only), named rather than inferred from its words: the
|
|
705
|
-
* landed notice's "keep future work on the branch", the outage notice's "stop resuming these by itself",
|
|
706
|
-
* the terminal a dependency install the daemon just started is running in, and the routed turn's "keep
|
|
707
|
-
* this chat on my pick". A KIND, not a callback: the chat decides what the press does and whether the
|
|
708
|
-
* offer still stands (a chat already holding its pick shows a settled sentence, not a stale button). */
|
|
709
|
-
noticeAction: z
|
|
710
|
-
.enum(["landHold", "outageOptOut", "depsInstall", "tierHold"])
|
|
711
|
-
.optional()
|
|
712
|
-
.describe("A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies."),
|
|
713
|
-
/* A WAIT THIS NOTICE DESCRIBES that had not finished when it was written (notice rows only): the chat draws
|
|
714
|
-
* a spinner over it while the wait is on, and the plain line once it is over. A KIND rather than a boolean
|
|
715
|
-
* because whether the wait is STILL running is a fact about the conversation now, not about a row in a
|
|
716
|
-
* record: the reader pairs the kind with the live state that answers it. */
|
|
717
|
-
noticeWait: z.enum(["credentialRenewal"]).optional().describe("The wait this notice describes, by name, so a reader can say whether it is still on."),
|
|
718
|
-
/* THE CARD THIS BUBBLE PARKED ON (assistant rows only), at most one: a card closes the bubble it lands in,
|
|
719
|
-
* so the next thing the agent says opens a fresh row beneath it. One field per kind rather than one union
|
|
720
|
-
* field, so a reader reaches the card it draws by name. */
|
|
721
|
-
plan: TranscriptPlanSchema.optional().describe("The plan this row asked approval for, and the answer."),
|
|
722
|
-
question: TranscriptQuestionSchema.optional().describe("The questions this row asked, and the picks that answered them."),
|
|
723
|
-
permission: TranscriptPermissionSchema.optional().describe("The tool this row asked permission for, and the decision."),
|
|
724
|
-
browserHelp: TranscriptBrowserHelpSchema.optional().describe("The browser hand-over this row asked for, and how it ended."),
|
|
725
|
-
terminalHelp: TranscriptTerminalHelpSchema.optional().describe("The terminal hand-over this row asked for, and how it ended."),
|
|
726
|
-
serviceOffer: TranscriptServiceOfferSchema.optional().describe("The priced service run this row offered, the decision, and the receipt."),
|
|
727
|
-
capabilityOffer: TranscriptCapabilityOfferSchema.optional().describe("The capability setup this row asked for, the decision, and the outcome."),
|
|
728
|
-
paymentOffer: TranscriptPaymentOfferSchema.optional().describe("The payment this row asked for, the decision, and the receipt."),
|
|
729
|
-
credentialOffer: TranscriptCredentialOfferSchema.optional().describe(
|
|
730
|
-
"The gated credential this row asked to use, who may release it, and who did.",
|
|
731
|
-
),
|
|
732
|
-
});
|
|
733
|
-
export type TranscriptRow = z.infer<typeof TranscriptRowSchema>;
|
|
734
|
-
|
|
735
|
-
/* THE CARD FIELDS A ROW CAN CARRY, as one list, for every reader that has to ask "does this row hold a card":
|
|
736
|
-
* the fold that counts a card-only bubble as a row, the chat's row count (a branch is cut by it), and the
|
|
737
|
-
* surfaces that draw whichever card a bubble is waiting on. */
|
|
738
|
-
export const CARD_FIELDS = [
|
|
739
|
-
"plan",
|
|
740
|
-
"question",
|
|
741
|
-
"permission",
|
|
742
|
-
"browserHelp",
|
|
743
|
-
"terminalHelp",
|
|
744
|
-
"serviceOffer",
|
|
745
|
-
"capabilityOffer",
|
|
746
|
-
"paymentOffer",
|
|
747
|
-
"credentialOffer",
|
|
748
|
-
] as const;
|
|
749
|
-
export type CardField = (typeof CARD_FIELDS)[number];
|
|
750
|
-
export type TranscriptCards = Pick<TranscriptRow, CardField>;
|
|
751
|
-
// Whether a row holds a card at all, answered or not.
|
|
752
|
-
export const holdsCard = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field] !== undefined);
|
|
753
|
-
// Whether a row is holding the turn open on a card nobody has answered.
|
|
754
|
-
export const isAwaitingDecision = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field]?.status === "pending");
|
|
755
|
-
|
|
756
|
-
/* ONE CHANGE TO A RUN'S ROWS, what the attach stream carries while a turn runs. The daemon folds each frame
|
|
757
|
-
* into its rows (transcript-fold.ts) and says what moved, so a client keeps rows, never frames: it applies
|
|
758
|
-
* these to the list it holds and draws it. `index` counts from the run's first row, which the attach head
|
|
759
|
-
* places in the conversation.
|
|
760
|
-
*
|
|
761
|
-
* Prose and thinking arrive as APPENDS to a row rather than as the row again, so the chat can type them out at
|
|
762
|
-
* the pace they are written; a tool card arrives whole (`tool`, by id, replacing an earlier copy of the same
|
|
763
|
-
* id wherever it nests), because its updates are snapshots already; everything else replaces its row. `drop`
|
|
764
|
-
* is the one removal: an assistant row opened for a block that then wrote nothing. */
|
|
765
|
-
export const TranscriptPatchSchema = z.discriminatedUnion("op", [
|
|
766
|
-
z.object({ op: z.literal("append").describe("A new row at the end."), row: TranscriptRowSchema }),
|
|
767
|
-
z.object({ op: z.literal("replace").describe("This row, whole, in place of the one at that index."), index: z.number().int().nonnegative(), row: TranscriptRowSchema }),
|
|
768
|
-
z.object({ op: z.literal("drop").describe("The row at that index is gone: it was opened and never written into."), index: z.number().int().nonnegative() }),
|
|
769
|
-
z.object({ op: z.literal("text").describe("More of the agent's prose, onto that row's text."), index: z.number().int().nonnegative(), text: z.string() }),
|
|
770
|
-
z.object({ op: z.literal("thinking").describe("More of the agent's reasoning, onto that row's thinking."), index: z.number().int().nonnegative(), text: z.string() }),
|
|
771
|
-
z.object({
|
|
772
|
-
op: z.literal("tool").describe("A tool card, whole: new, or the latest state of one already there, matched by id wherever it nests."),
|
|
773
|
-
index: z.number().int().nonnegative(),
|
|
774
|
-
tool: TranscriptToolSchema,
|
|
775
|
-
parent: z.string().optional().describe("The card this one nests under, when it is a delegated subagent's own call."),
|
|
776
|
-
}),
|
|
777
|
-
]);
|
|
778
|
-
export type TranscriptPatch = z.infer<typeof TranscriptPatchSchema>;
|
|
779
|
-
|
|
780
|
-
export const SessionTranscriptSchema = z.object({
|
|
781
|
-
messages: z
|
|
782
|
-
.array(TranscriptRowSchema)
|
|
783
|
-
.describe(
|
|
784
|
-
"The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded.",
|
|
785
|
-
),
|
|
786
|
-
});
|
|
787
|
-
/* HOW A TURN THAT LEFT WORK BEHIND ENDED, as the daemon has it, for whoever asks and however long after.
|
|
788
|
-
*
|
|
789
|
-
* One shape for every ending that leaves finished work behind a live session, because they are one situation
|
|
790
|
-
* with one answer (a press) and they differ only in what can honestly be said about WHEN and what the press
|
|
791
|
-
* DOES. The client folds this straight into its pick-up state (chat/pickUp.ts), which is why the field names
|
|
792
|
-
* line up: a second vocabulary between the two halves is how they came to disagree in the first place.
|
|
793
|
-
*
|
|
794
|
-
* IT USED TO BE A BOOLEAN, and the boolean is what left the longest wait uncovered. One flag could only say
|
|
795
|
-
* "a Stop, or a daemon killed under it", so a spent allowance, the one ending that reliably OUTLIVES the
|
|
796
|
-
* window that hit it, reached a reopened tab as nothing at all: no strip, no countdown, no press, and the user
|
|
797
|
-
* typing the word by hand hours later. It could not say more without these three facts, and each of them
|
|
798
|
-
* changes what the surface may promise:
|
|
799
|
-
*
|
|
800
|
-
* · `resetsAt` is the only honest "not before this" any ending knows, and it is the whole of what a chat
|
|
801
|
-
* reopened the next morning wants to be told;
|
|
802
|
-
* · `held` is what makes the press cheap. The daemon keeps the refused turn whole, so continuing RE-RUNS it
|
|
803
|
-
* and adds nothing to the conversation; without this the same press appends a message reading "Continue",
|
|
804
|
-
* which is exactly the transcript pollution the press exists to prevent, and `ran` separates a turn that
|
|
805
|
-
* got somewhere from one the allowance refused at the door (two different sentences);
|
|
806
|
-
* · `scheduled` says somebody else is already bringing this turn back, so the surface REPORTS a wait instead
|
|
807
|
-
* of offering one, and no local automation races the daemon's own pass for it. */
|
|
808
|
-
export const TurnEndingSchema = z.object({
|
|
809
|
-
reason: z
|
|
810
|
-
.enum(["stopped", "limit", "outage"])
|
|
811
|
-
.describe(
|
|
812
|
-
"Which ending left the work here: a Stop or a daemon killed under the turn, a spent usage allowance, or a provider that refused it.",
|
|
813
|
-
),
|
|
814
|
-
resetsAt: z
|
|
815
|
-
.number()
|
|
816
|
-
.optional()
|
|
817
|
-
.describe("When the spent allowance reopens, in epoch seconds. Absent for every ending that names no instant, and for a provider that publishes none."),
|
|
818
|
-
held: z
|
|
819
|
-
.object({ ran: z.boolean().describe("Whether the held turn got anywhere before it was refused, which is a different sentence from one refused at the door.") })
|
|
820
|
-
.optional()
|
|
821
|
-
.describe("Present when the daemon still holds the refused turn whole, so a press re-runs it rather than appending a message after it."),
|
|
822
|
-
scheduled: z
|
|
823
|
-
.boolean()
|
|
824
|
-
.optional()
|
|
825
|
-
.describe("Whether something other than the user is already booked to send this turn again, so the surface reports the wait instead of offering a press."),
|
|
826
|
-
});
|
|
827
|
-
export type TurnEnding = z.infer<typeof TurnEndingSchema>;
|
|
828
|
-
|
|
829
|
-
/* THE RECORD A REOPENED TAB IS REBUILT FROM: the messages, plus what the session behind them is BOUND to.
|
|
830
|
-
*
|
|
831
|
-
* A provider session is minted on one runtime under one credential, and it resumes only there, so a client
|
|
832
|
-
* deciding whether its next message continues this conversation or starts a fresh one needs all four facts
|
|
833
|
-
* together. The client cannot derive the last three: its tab holds the picks the NEXT turn would use, which
|
|
834
|
-
* after a mid-chat switch are exactly the ones the session does not belong to. Stamping those onto the session
|
|
835
|
-
* is what made switching BACK to the account that minted it announce a fresh session and then retire a
|
|
836
|
-
* perfectly resumable one, spending the whole transcript again on a cold prompt cache.
|
|
837
|
-
*
|
|
838
|
-
* The session fields are all optional, and absent together on a conversation that has no session to resume. */
|
|
839
|
-
export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
|
|
840
|
-
sessionId: z.string().optional().describe("The provider session behind the last turn, when there is one."),
|
|
841
|
-
provider: AgentProviderSchema.optional().describe("Which provider minted that session."),
|
|
842
|
-
harness: AgentHarnessSchema.optional().describe("Which runtime minted it: a session resumes only on the loop that opened it."),
|
|
843
|
-
account: z
|
|
844
|
-
.string()
|
|
845
|
-
.optional()
|
|
846
|
-
.describe("Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn."),
|
|
847
|
-
/* AND HOW THE LAST TURN ENDED, for the endings that leave the client something to OFFER rather than
|
|
848
|
-
* something to draw: work half done behind a session that is perfectly alive, where the only thing missing
|
|
849
|
-
* is somebody saying carry on.
|
|
850
|
-
*
|
|
851
|
-
* It rides the transcript because the offer used to ride the WINDOW. A chat armed the continue press from
|
|
852
|
-
* the stream it was watching when the turn stopped, so the press existed only where somebody had been
|
|
853
|
-
* looking: stop an agent from the board with its chat closed, or reopen the tab on another device, or after
|
|
854
|
-
* a reload that dropped the tab, and the same stopped session came back with no way on but typing the word
|
|
855
|
-
* by hand, which is precisely what the press exists to spare. The daemon is the one party that knows this
|
|
856
|
-
* about a conversation whoever asks and however long after, so it is the one that says it. */
|
|
857
|
-
ending: TurnEndingSchema.optional().describe(
|
|
858
|
-
"How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.",
|
|
859
|
-
),
|
|
860
|
-
/* WHERE THIS PAGE SITS IN THE CONVERSATION. `messages` is the tail, not the whole record: a conversation
|
|
861
|
-
* that ran all week used to be served entire to every tab that opened it and to every card the board warms
|
|
862
|
-
* behind it, which is megabytes over a tunnel to redraw a screenful.
|
|
863
|
-
*
|
|
864
|
-
* `from` is the position of the first message in the WHOLE record, which makes it two things at once: the
|
|
865
|
-
* offset every `rewindIndex` in this page is counted against, and the `before` that asks for the page above
|
|
866
|
-
* it. `more` says whether there is one, so a client can offer to go back without spending a round trip
|
|
867
|
-
* finding out. */
|
|
868
|
-
from: z.number().int().nonnegative().describe("Where the first message sits in the whole record, and the `before` that asks for the page above this one."),
|
|
869
|
-
more: z.boolean().describe("Whether older messages precede this page."),
|
|
870
|
-
});
|
|
871
|
-
|
|
872
|
-
/* WHAT A PUBLISHED CONVERSATION'S PAGE IS HANDED, the whole of it, baked into the page as one JSON block.
|
|
873
|
-
*
|
|
874
|
-
* A share has to keep working with nothing behind it: no daemon, no session, no sandbox that has to still be
|
|
875
|
-
* running when the recipient finally opens the link. So the page carries its conversation rather than fetching
|
|
876
|
-
* it, which also settles the security question by construction, a page with nothing to ask has no way to ask
|
|
877
|
-
* for something it was not given.
|
|
878
|
-
*
|
|
879
|
-
* The messages are the SAME TranscriptRow rows the app replays a reopened tab from, already filtered to the
|
|
880
|
-
* chosen detail level and with every picture path rewritten to the copy published beside the page. That
|
|
881
|
-
* sameness is the point: the shared page renders them with the app's own components, so what a recipient sees
|
|
882
|
-
* is what the owner saw. */
|
|
883
|
-
export const SharePayloadSchema = z.object({
|
|
884
|
-
title: z.string(),
|
|
885
|
-
// When the snapshot was taken, not when the conversation happened, see SharedConversation.sharedAt.
|
|
886
|
-
sharedAt: z.number(),
|
|
887
|
-
detail: ShareDetailSchema,
|
|
888
|
-
messages: z.array(TranscriptRowSchema),
|
|
889
|
-
});
|
|
890
|
-
export type SharePayload = z.infer<typeof SharePayloadSchema>;
|
|
891
|
-
|
|
892
|
-
// One frame from an agent turn, relayed to the UI. `kind`-discriminated. The daemon normalizes the SDK's
|
|
893
|
-
// ~40 SDKMessage types down to this union: high-value block types get a dedicated frame
|
|
894
|
-
// (delta/thinking/tool_call/tool_call_update/todos/usage/rate_limit_info/account_usage/context_usage/init/compact); any SDK message
|
|
895
|
-
// without a UI mapping is dropped. `plan`/`question`/`permission` pause the turn until the user answers on the
|
|
896
|
-
// `POST /agent/reply` side channel, and `resolved` releases the one it names; `mode` reports the live
|
|
897
|
-
// permission posture as the agent changes it.
|
|
898
|
-
// `parentToolUseId` tags frames produced inside a subagent (Task tool); `subagent`/`subagent_update` report the
|
|
899
|
-
// subagent itself, keyed by the same tool_use id those tagged frames carry.
|
|
900
|
-
export const AgentEventSchema = z.discriminatedUnion("kind", [
|
|
901
|
-
/* THE SESSION THIS TURN IS RUNNING, and the credential it belongs to.
|
|
902
|
-
*
|
|
903
|
-
* `account` is the account the daemon RESOLVED for the turn, which is not always the one the request named:
|
|
904
|
-
* a turn that names none is given the connected account with the most headroom (agent/harness-credentials.ts),
|
|
905
|
-
* so "the client's pick" and "who is paying" are different questions and only the daemon can answer the
|
|
906
|
-
* second. It rides here because a session belongs to the credential that minted it — that pairing is what
|
|
907
|
-
* decides whether the next message resumes this session or opens a fresh one — and a client that stamped its
|
|
908
|
-
* own pick onto the session instead would announce a fresh session for the account that actually holds it.
|
|
909
|
-
*
|
|
910
|
-
* Absent when the turn ran on the container's env token or on a translator subscription, where there is no
|
|
911
|
-
* stored account to name. */
|
|
912
|
-
z.object({
|
|
913
|
-
kind: z.literal("session"),
|
|
914
|
-
sessionId: z.string(),
|
|
915
|
-
account: z.string().optional().describe("Which stored account this session belongs to, as the daemon resolved it for the turn."),
|
|
916
|
-
}),
|
|
917
|
-
/* WHERE AN ISOLATED TURN IS STANDING: the conversation's worktree identity, its branch (agent/<id>) and
|
|
918
|
-
* the ROOT repo's short base sha. First frame of the turn, before any provider frames, and again each time
|
|
919
|
-
* the branch MOVES underneath it, which is why `base` names where the branch sits now rather than the
|
|
920
|
-
* moment it was checked out.
|
|
921
|
-
*
|
|
922
|
-
* `unenforced` marks the degraded container: no CAP_SYS_ADMIN, so the turn's worktree could not be
|
|
923
|
-
* bind-mounted over the workspace root and the harness is rewriting tool paths into it instead. That
|
|
924
|
-
* fallback covers what arrives as tool input and not what a subprocess computes for itself, so the
|
|
925
|
-
* operator needs to know, this state used to be one line in the daemon log at boot, and the way it got
|
|
926
|
-
* noticed was files appearing in the main tree from agents that were supposed to be on branches. Repeated
|
|
927
|
-
* on every emission, because it describes the turn and a client rebuilds its standing from the last frame.
|
|
928
|
-
*
|
|
929
|
-
* `sync` reports a rebase (agents/sync.ts) and rides here because this frame is already the turn's "where
|
|
930
|
-
* you are standing" announcement. Present only when the branch was BEHIND the main line, `commits` is how
|
|
931
|
-
* many main-line commits it gained, `blocked` names the repos whose rebase would not apply and was rolled
|
|
932
|
-
* back. Both can be non-empty at once in a multi-repo composition. Two moments produce it: before the turn
|
|
933
|
-
* starts, and after a card the turn parked on is answered, a question or a plan approval waits minutes
|
|
934
|
-
* for a person, and the main line does not stop moving meanwhile. It is a notice and never a question: the
|
|
935
|
-
* user is answering their agent, and the alternative to rebasing is not "stay safe" but "conflict at land
|
|
936
|
-
* time", which interrupts them harder. */
|
|
937
|
-
z.object({
|
|
938
|
-
kind: z.literal("worktree"),
|
|
939
|
-
branch: z.string(),
|
|
940
|
-
base: z.string(),
|
|
941
|
-
unenforced: z.boolean().optional(),
|
|
942
|
-
sync: z.object({ commits: z.number(), blocked: z.array(z.string()) }).optional(),
|
|
943
|
-
// The runner this turn executes on, when the conversation is placed remotely (runners/): the
|
|
944
|
-
// transcript's own statement of where the work is happening. Absent ⇒ this sandbox.
|
|
945
|
-
remote: z.string().optional(),
|
|
946
|
-
}),
|
|
947
|
-
// Emitted after a clean isolated turn whose delta auto-landed (or failed to): landed ⇒ the work is now
|
|
948
|
-
// UNCOMMITTED changes in the main tree (the Changes panel is the review); conflicts ⇒ it stayed safely in
|
|
949
|
-
// the worktree, and each named path carries WHY it would not apply (see LandConflictSchema) so the report
|
|
950
|
-
// can say whether the user's own copy is at risk or the main line simply moved on underneath the agent.
|
|
951
|
-
// held ⇒ auto-land is off for this agent: nothing was applied and nothing failed, the delta is waiting
|
|
952
|
-
// on the branch for a deliberate Land (landed is false, conflicts absent).
|
|
953
|
-
// `deps` rides along when the landed delta left the main tree declaring dependencies it does not have,
|
|
954
|
-
// the residue of an agent adding one without installing it, which every LATER turn would inherit through
|
|
955
|
-
// the overlay it mounts over the main checkout. The daemon reconciles it rather than asking anyone to
|
|
956
|
-
// (workspace/reconcile-deps.ts); this is the receipt, and `deferred` is the honest answer while other turns
|
|
957
|
-
// are still running, since an install cannot touch a tree they are mounted on.
|
|
958
|
-
z.object({
|
|
959
|
-
kind: z.literal("landed"),
|
|
960
|
-
landed: z.boolean(),
|
|
961
|
-
conflicts: z.array(LandConflictSchema).optional(),
|
|
962
|
-
held: z.boolean().optional(),
|
|
963
|
-
deps: z.object({ missing: z.number(), started: z.array(z.string()), deferred: z.boolean() }).optional(),
|
|
964
|
-
}),
|
|
965
|
-
/* WHAT THE DAEMON ADDED TO THE USER'S MESSAGE before the model read it, the exact words, not a summary of
|
|
966
|
-
* them.
|
|
967
|
-
*
|
|
968
|
-
* A turn's prompt is not only what was typed: the daemon prepends notes the model needs and the user did not
|
|
969
|
-
* write (agent/turn-preamble.ts owns the list, a rebase that moved the branch, dependencies that are behind,
|
|
970
|
-
* workspace context retrieved for this very message, where an unenforced runtime's files really live). Those
|
|
971
|
-
* notes change what the agent does, and for a long time the chat's only trace of any of them was one muted
|
|
972
|
-
* line paraphrasing the rebase, so a user watching an agent act on instructions they could not see had no
|
|
973
|
-
* way to find out what those instructions said. This frame is the fix: the note text verbatim, one entry per
|
|
974
|
-
* note, rendered collapsed so it costs a click rather than a scroll.
|
|
975
|
-
*
|
|
976
|
-
* Emitted from the TYPED notes the wire prompt is serialized from at the same point (turn-preamble.ts,
|
|
977
|
-
* composeWirePrompt), so the disclosure and what the model receives cannot drift: a note is in both or in
|
|
978
|
-
* neither, and a note nobody thought to title cannot reach the wire unlabelled.
|
|
979
|
-
*
|
|
980
|
-
* ONE MOMENT, always: the notes went in front of the user's own message before the turn started, so they hang
|
|
981
|
-
* off that message and are stored on it, the transcript fold reads this very frame out of the turn's own
|
|
982
|
-
* frame log (sessions/turn-transcript.ts), which is how a reopened tab still has them. Nothing is injected
|
|
983
|
-
* into a RUNNING turn, the rebase taken while a card sat waiting was the only thing that ever was, and it no
|
|
984
|
-
* longer says anything to the model at all (agent/turn-preamble.ts). */
|
|
985
|
-
z.object({ kind: z.literal("preamble"), notes: z.array(TurnNoteSchema) }),
|
|
986
|
-
// The SDK's init handshake; carries the model it actually resolved for the turn.
|
|
987
|
-
z.object({ kind: z.literal("init"), model: z.string() }),
|
|
988
|
-
// The pre-turn workspace snapshot's id (the attribution-fence "user" capture), emitted once before the
|
|
989
|
-
// provider stream so the client can offer "restore to before this message" on the turn's user bubble.
|
|
990
|
-
// Absent on isolated turns (they snapshot nothing) and when the tree was already clean at turn start.
|
|
991
|
-
/* The workspace checkpoint capturing the state as this turn FOUND it, what "go back to before this
|
|
992
|
-
* message" restores. `index` is the message's position in the conversation's transcript, which the rewind
|
|
993
|
-
* route addresses it by; absent on a turn with no conversation behind it (the bench, a one-shot), where
|
|
994
|
-
* the id still powers a plain restore but there is no message to rewind to. */
|
|
995
|
-
z.object({ kind: z.literal("checkpoint"), id: z.string(), index: z.number().int().nonnegative().optional() }),
|
|
996
|
-
/* A MESSAGE THE USER SENT INTO THE TURN WHILE IT RAN, the mid-turn steer, at the point in the stream where
|
|
997
|
-
* the daemon accepted it (agent/agent-steering.ts).
|
|
998
|
-
*
|
|
999
|
-
* A frame rather than a client-local write, because all three things that were wrong about the steer are the
|
|
1000
|
-
* same missing fact: nothing in the run's log said WHEN it arrived.
|
|
1001
|
-
* - POSITION. The harness injects a steer between tool calls and the model simply keeps writing, with no
|
|
1002
|
-
* `result` in between, so there is no `usage` boundary to retire the open bubble. The sending window
|
|
1003
|
-
* appended the user's words at the END of its transcript while the turn kept typing into the bubble
|
|
1004
|
-
* ABOVE them, and the answer to a question landed over the question.
|
|
1005
|
-
* - EVERY OTHER WINDOW. A run is rendered by any number of attached clients; only the one that posted the
|
|
1006
|
-
* steer knew about it, so the same conversation read differently in two places.
|
|
1007
|
-
* - THE RECORD. The settled turn is written down from this log (sessions/turn-transcript.ts), and one that
|
|
1008
|
-
* never held the steer wrote a transcript the message was missing from entirely, which also put the
|
|
1009
|
-
* client's row count one ahead of the daemon's for the rest of the conversation, and those counts are
|
|
1010
|
-
* what a fork copies a prefix of and a rewind addresses.
|
|
1011
|
-
*
|
|
1012
|
-
* `text` is what the user typed, never the composed prompt: the editor-context and attachment notes the
|
|
1013
|
-
* route wraps around it are protocol, and redrawing them as the user's words is the same lie the stored
|
|
1014
|
-
* prompt is unwrapped to avoid. `attachments` are workspace-relative, like the turn's own. `sentAt` is the
|
|
1015
|
-
* instant the turn took the message, carried so the bubble wears the same clock live and after a reopen,
|
|
1016
|
-
* a turn's own user row is stamped from the daemon's clock too, and a live bubble stamped from the
|
|
1017
|
-
* browser's would visibly jump when the record replaced it. */
|
|
1018
|
-
z.object({ kind: z.literal("steer"), text: z.string(), sentAt: z.number(), attachments: z.array(z.string()).optional() }),
|
|
1019
|
-
z.object({ kind: z.literal("delta"), text: z.string(), parentToolUseId: z.string().optional() }),
|
|
1020
|
-
// The prose block the `delta` frames were writing is finished. A turn emits several: the model says what
|
|
1021
|
-
// it is about to do, runs tools, reports what it found, runs more, then summarizes, each a separate text
|
|
1022
|
-
// block in the SDK stream. Without this boundary the client has no way to tell them apart and glues the
|
|
1023
|
-
// whole turn's narration into one paragraph run, so the client retires its current bubble here and lets
|
|
1024
|
-
// what follows (the tool calls this block introduced, or the next block of prose) open a fresh one.
|
|
1025
|
-
z.object({ kind: z.literal("text_end"), parentToolUseId: z.string().optional() }),
|
|
1026
|
-
z.object({ kind: z.literal("thinking"), text: z.string(), parentToolUseId: z.string().optional() }),
|
|
1027
|
-
// A tool call starting (or, for backends that only report completions, arriving whole). `content` carries
|
|
1028
|
-
// structured output known at call time, an Edit's diff is derived from its input, no result needed.
|
|
1029
|
-
z.object({
|
|
1030
|
-
kind: z.literal("tool_call"),
|
|
1031
|
-
id: z.string(),
|
|
1032
|
-
name: z.string(),
|
|
1033
|
-
category: ToolKindSchema,
|
|
1034
|
-
status: ToolCallStatusSchema,
|
|
1035
|
-
target: z.string().optional(),
|
|
1036
|
-
locations: z.array(ToolCallLocationSchema).optional(),
|
|
1037
|
-
content: z.array(ToolCallContentSchema).optional(),
|
|
1038
|
-
parentToolUseId: z.string().optional(),
|
|
1039
|
-
}),
|
|
1040
|
-
// A later state of a tool call, correlated by `id`. N updates per call: status transitions and/or fresh
|
|
1041
|
-
// content/locations, both REPLACE the prior value (snapshot semantics, not append); absent ⇒ unchanged.
|
|
1042
|
-
z.object({
|
|
1043
|
-
kind: z.literal("tool_call_update"),
|
|
1044
|
-
id: z.string(),
|
|
1045
|
-
status: ToolCallStatusSchema.optional(),
|
|
1046
|
-
content: z.array(ToolCallContentSchema).optional(),
|
|
1047
|
-
locations: z.array(ToolCallLocationSchema).optional(),
|
|
1048
|
-
}),
|
|
1049
|
-
// The agent just started running Bash in its live `agent-<id>` tmux session, the client surfaces that
|
|
1050
|
-
// terminal in the global panel. One per turn (the session is reused across a turn's commands, incl. subagents').
|
|
1051
|
-
z.object({ kind: z.literal("terminal"), session: z.string() }),
|
|
1052
|
-
// The agent just used a browser tool, its Chromium is coming up (or already is) behind a watchable
|
|
1053
|
-
// `browser-<id>` session, and the client surfaces it in the same panel as the terminals. One per turn, for
|
|
1054
|
-
// the same reason: one browser serves every browser call the turn makes.
|
|
1055
|
-
z.object({ kind: z.literal("browser"), session: z.string() }),
|
|
1056
|
-
/* THE AGENT STARTED ANOTHER AGENT, an Agent/Task subagent, or a Codex/Grok CLI it drove from its own Bash
|
|
1057
|
-
* (see SubagentSessionSchema). One `subagent` frame per child, then `subagent_update` as it works: the same
|
|
1058
|
-
* call/update pair `tool_call`/`tool_call_update` uses, and for the same reason, the fields that move
|
|
1059
|
-
* (status, spend, what it is doing) arrive many times and must REPLACE, while the fields that identify it are
|
|
1060
|
-
* said once.
|
|
1061
|
-
*
|
|
1062
|
-
* `id` is the SPAWNING TOOL CALL's id, the same id the client already nests the child's inner frames under
|
|
1063
|
-
* (`parentToolUseId`), so both frames land on the card that spawned the child by the lookup that is already
|
|
1064
|
-
* there (mapToolAnywhere). No second correlation, and nothing to get wrong.
|
|
1065
|
-
*
|
|
1066
|
-
* These exist because the SDK's task messages were dropped. A BACKGROUNDED child (the Agent tool's default)
|
|
1067
|
-
* emits its tool_use and then nothing until its result lands, which for a long child is minutes of a spinner
|
|
1068
|
-
* that cannot say whether anything is happening. */
|
|
1069
|
-
z.object({
|
|
1070
|
-
kind: z.literal("subagent"),
|
|
1071
|
-
id: z.string(),
|
|
1072
|
-
subagentKind: SubagentKindSchema,
|
|
1073
|
-
agentType: z.string().optional(),
|
|
1074
|
-
description: z.string().optional(),
|
|
1075
|
-
model: z.string().optional(),
|
|
1076
|
-
// Which provider serves a `spawned` child (SubagentSessionSchema.provider), absent for an SDK
|
|
1077
|
-
// subagent, whose provider is its parent's.
|
|
1078
|
-
provider: z.string().optional(),
|
|
1079
|
-
background: z.boolean().optional(),
|
|
1080
|
-
}),
|
|
1081
|
-
z.object({
|
|
1082
|
-
kind: z.literal("subagent_update"),
|
|
1083
|
-
id: z.string(),
|
|
1084
|
-
status: SubagentStatusSchema.optional(),
|
|
1085
|
-
tokens: z.number().optional(),
|
|
1086
|
-
toolUses: z.number().optional(),
|
|
1087
|
-
lastTool: z.string().optional(),
|
|
1088
|
-
summary: z.string().optional(),
|
|
1089
|
-
error: z.string().optional(),
|
|
1090
|
-
// Whether anything checked the work the report describes (SubagentVerificationSchema). Rides the frame
|
|
1091
|
-
// that ENDS the child, beside the report itself, so the card carries both at once.
|
|
1092
|
-
verification: SubagentVerificationSchema.optional(),
|
|
1093
|
-
}),
|
|
1094
|
-
z.object({ kind: z.literal("todos"), items: z.array(TodoItemSchema) }),
|
|
1095
|
-
// The provider's own slash commands (ACP available_commands_update), replaced whole each time, the
|
|
1096
|
-
// composer's `/` popover lists them; invoking one is plain `/name …` prompt text (the ACP convention).
|
|
1097
|
-
z.object({ kind: z.literal("commands"), items: z.array(AgentCommandSchema) }),
|
|
1098
|
-
z.object({
|
|
1099
|
-
kind: z.literal("usage"),
|
|
1100
|
-
// The account that served this turn, the client attributes the totals to it (tagged by streamAgent).
|
|
1101
|
-
account: z.string().optional(),
|
|
1102
|
-
costUsd: z.number().optional(),
|
|
1103
|
-
inputTokens: z.number().optional(),
|
|
1104
|
-
outputTokens: z.number().optional(),
|
|
1105
|
-
// Provider prompt-cache buckets for the turn: tokens served from cache (read) and written to cache
|
|
1106
|
-
// (creation). Optional per provider. Codex reports only cached input (read); runtimes/turns that
|
|
1107
|
-
// don't report a bucket omit it. Lets the client show cache hit rate = read / (read + input).
|
|
1108
|
-
cacheReadTokens: z.number().optional(),
|
|
1109
|
-
cacheCreationTokens: z.number().optional(),
|
|
1110
|
-
durationMs: z.number().optional(),
|
|
1111
|
-
numTurns: z.number().optional(),
|
|
1112
|
-
}),
|
|
1113
|
-
// The live gate: the provider's answer to "may this turn run", pushed mid-turn. Drives the rate-limited
|
|
1114
|
-
// notice, not the headroom readouts, see RateLimitInfoSchema.
|
|
1115
|
-
RateLimitInfoSchema.extend({ kind: z.literal("rate_limit_info"), account: z.string().optional() }),
|
|
1116
|
-
/* WHAT SPEED THIS TURN ACTUALLY RAN AT, and when it isn't the one asked for, why. Emitted only when the
|
|
1117
|
-
* answer CHANGES within a turn, so the ordinary case is one frame at init and nothing after it; a turn that
|
|
1118
|
-
* enters cooldown mid-flight (fast mode has its own rate-limit pool, separate from the model's) emits a
|
|
1119
|
-
* second.
|
|
1120
|
-
*
|
|
1121
|
-
* This frame exists because fast mode fails SILENTLY and for a lot of different reasons, the plan is free,
|
|
1122
|
-
* extra usage is off, the model doesn't offer it, the turn is routed through the translator and so isn't
|
|
1123
|
-
* first-party, an env var disables it, the pool is in cooldown. Asking for it and getting standard speed is
|
|
1124
|
-
* indistinguishable, from the outside, from asking for it and getting it: same frames, same text, a bill
|
|
1125
|
-
* that differs by 2x. A toggle whose effect can't be observed is worse than no toggle, so the daemon
|
|
1126
|
-
* reports the harness's own answer rather than the client's assumption.
|
|
1127
|
-
*
|
|
1128
|
-
* `reason` is forwarded VERBATIM as the string the harness reported (SDK: FastModeDisabledReason) rather
|
|
1129
|
-
* than re-typed as an enum here: the set is the vendor's and grows on their schedule, and a reason this
|
|
1130
|
-
* build hasn't heard of should reach the user as an unfamiliar word, not fail schema validation and take
|
|
1131
|
-
* the whole frame with it. The client maps the ones it knows to sentences and shows the rest as-is. */
|
|
1132
|
-
z.object({
|
|
1133
|
-
kind: z.literal("fast_mode"),
|
|
1134
|
-
state: FastModeStateSchema,
|
|
1135
|
-
// Absent when nothing is blocking fast mode, including on `state: "on"`, and on an `off` that simply
|
|
1136
|
-
// wasn't asked for.
|
|
1137
|
-
reason: z.string().optional(),
|
|
1138
|
-
}),
|
|
1139
|
-
/* WHAT THE COMPLEXITY JUDGE MADE OF THIS TURN, emitted once at turn start on every judged turn (that is,
|
|
1140
|
-
* whenever settings.autoTier is not "off"), for the same reason fast_mode exists: a mechanism that can
|
|
1141
|
-
* change what a turn runs on fails silently unless the daemon says what it decided. One tiny frame per
|
|
1142
|
-
* turn, deliberately on the standard verdicts too, because the client's composer preview needs the
|
|
1143
|
-
* conversation's LAST verdict (prompt-complexity.ts `afterHardTurn`) and a frame only on the interesting
|
|
1144
|
-
* turns would leave it guessing on the common ones.
|
|
1145
|
-
*
|
|
1146
|
-
* `tier`/`score`/`rules` are the verdict verbatim (judgeComplexity): the rules are the named-feature
|
|
1147
|
-
* vocabulary of ComplexityRule, carried as strings so a frame from a build with a rule this client hasn't
|
|
1148
|
-
* heard of still parses. `model` is present only when a substitution actually applies to THIS turn, which
|
|
1149
|
-
* is `routed` (mode on, verdict fast, something cheaper published) or `held` (the same turn the user pinned
|
|
1150
|
-
* to their pick, see AgentTurn.tierHold): measure mode never names one because naming it would cost the
|
|
1151
|
-
* catalog read shadow mode exists to avoid.
|
|
1152
|
-
*
|
|
1153
|
-
* `routed` is what HAPPENED, never implied by the verdict: a fast verdict in measure mode, under a hold, or
|
|
1154
|
-
* with nothing cheaper published all run the user's own pick and say `routed: false`. */
|
|
1155
|
-
z.object({
|
|
1156
|
-
kind: z.literal("tier"),
|
|
1157
|
-
tier: z.enum(["fast", "standard"]),
|
|
1158
|
-
score: z.number(),
|
|
1159
|
-
rules: z.array(z.string()),
|
|
1160
|
-
// The cheaper model this turn ran on (routed) or would have run on (held). Absent otherwise.
|
|
1161
|
-
model: z.string().optional(),
|
|
1162
|
-
routed: z.boolean(),
|
|
1163
|
-
// The user pinned this turn to their pick (AgentTurn.tierHold), so a fast verdict moved nothing.
|
|
1164
|
-
held: z.boolean().optional(),
|
|
1165
|
-
}),
|
|
1166
|
-
/* The turn is alive but WAITING on the provider: a request failed transiently (5xx, 529, a dropped socket)
|
|
1167
|
-
* and the harness is retrying it inside this same turn. A status, not a failure, nothing has been lost and
|
|
1168
|
-
* the turn may still finish normally, so the client renders it where "thinking" goes rather than in the
|
|
1169
|
-
* transcript.
|
|
1170
|
-
*
|
|
1171
|
-
* It exists because the retry budget is deliberately long (see CLAUDE_CODE_RETRY_WATCHDOG in
|
|
1172
|
-
* harness-credentials.ts): a turn can now sit silent for minutes riding out an outage, and silence reads as
|
|
1173
|
-
* a hang. The one action a user takes against an apparent hang is Stop, which is the only action that
|
|
1174
|
-
* actually loses the work, so the wait has to be visible, with its own next-attempt clock.
|
|
1175
|
-
*
|
|
1176
|
-
* `attempt` is the harness's own counter and `maxAttempts` is the bound that will actually be honoured,
|
|
1177
|
-
* which on the Claude path is the daemon's own cap on how deep a storm may get rather than the harness's
|
|
1178
|
-
* far longer budget (MAX_IN_TURN_RETRIES in sdk-stream.ts, which ends the turn at the cap and hands the
|
|
1179
|
-
* waiting to the outage breaker). `nextAttemptAt` (epoch ms) is when it will try
|
|
1180
|
-
* again, so the readout counts down instead of freezing on a number nobody can interpret. BOTH are optional
|
|
1181
|
-
* for the same reason, which is that each runtime publishes a different half of the wait and none of them
|
|
1182
|
-
* publishes all of it: Claude's harness reports the delay and the bound, Codex says which attempt it is on
|
|
1183
|
-
* and nothing else (codex-agent.ts), OpenCode names the next instant but no bound (grok-agent.ts). Inventing
|
|
1184
|
-
* the missing half would be a countdown, or a limit, the retry never agreed to. */
|
|
1185
|
-
z.object({
|
|
1186
|
-
kind: z.literal("provider_retry"),
|
|
1187
|
-
attempt: z.number(),
|
|
1188
|
-
maxAttempts: z.number().optional(),
|
|
1189
|
-
nextAttemptAt: z.number().optional(),
|
|
1190
|
-
// The HTTP status behind it when there was one (529 reads as capacity, 429 as a rate limit, 500 as a
|
|
1191
|
-
// fault, the client says which). Absent for a transport failure that never got a response, and for a
|
|
1192
|
-
// runtime that reports the refusal as prose rather than a code (grok-agent.ts reads it back off that).
|
|
1193
|
-
status: z.number().optional(),
|
|
1194
|
-
}),
|
|
1195
|
-
// Every plan-limit pool for the account that served the turn, read from the CLI's usage endpoint once the
|
|
1196
|
-
// turn settles. `account` tags which Claude account it belongs to, so the client keys headroom by account;
|
|
1197
|
-
// absent on an env-token turn, which has no account to attribute it to. No `measuredAt` on the wire: both
|
|
1198
|
-
// readers stamp it on receipt, which is the read time to within the hop.
|
|
1199
|
-
z.object({ kind: z.literal("account_usage"), account: z.string().optional(), windows: z.array(UsageWindowSchema) }),
|
|
1200
|
-
ContextUsageSchema.extend({ kind: z.literal("context_usage") }),
|
|
1201
|
-
z.object({ kind: z.literal("compact"), trigger: z.string(), preTokens: z.number().optional(), postTokens: z.number().optional() }),
|
|
1202
|
-
// The four interactive cards. Each parks the turn until `POST /agent/reply` resolves its `requestId`.
|
|
1203
|
-
PlanCardSchema,
|
|
1204
|
-
QuestionCardSchema,
|
|
1205
|
-
PermissionCardSchema,
|
|
1206
|
-
// The agent's browser needs a person (see browserHelpCard for what the card carries). Not journalled for
|
|
1207
|
-
// restore: the Chromium holding the page dies with the container.
|
|
1208
|
-
z.object({ kind: z.literal("browser_help"), ...browserHelpCard }),
|
|
1209
|
-
// The agent's TERMINAL needs a person (see terminalHelpCard). Not journalled for restore, and for the
|
|
1210
|
-
// browser card's reason one door along: the pane holding the prompt belongs to a process the restart kills.
|
|
1211
|
-
z.object({ kind: z.literal("terminal_help"), ...terminalHelpCard }),
|
|
1212
|
-
/* A premium service run awaiting the owner's click. Raised OUTSIDE the turn generator, the daemon's
|
|
1213
|
-
* services route parks the agent's own `services run` call and pushes this frame into the live run
|
|
1214
|
-
* (platform/service-offer.ts), so unlike the four cards above it is not journalled for restore: its
|
|
1215
|
-
* waiter is the CLI's held connection, which dies with the daemon, and a restored card would offer
|
|
1216
|
-
* buttons nothing is waiting behind. Settles through the same `POST /agent/reply` as every other card. */
|
|
1217
|
-
z.object({ kind: z.literal("service_offer"), ...serviceOfferCard }),
|
|
1218
|
-
/* One event off an approved run's stream, pushed as the provider emits it so the settled card shows the
|
|
1219
|
-
* run living rather than a spinner of unknowable length. Today that is `status` lines; `result` stays off
|
|
1220
|
-
* the transcript on purpose (it is the agent's answer to act on, not the card's to duplicate), the frame
|
|
1221
|
-
* carries the whole union so richer event kinds land here without a contract break. */
|
|
1222
|
-
z.object({ kind: z.literal("service_event"), requestId: z.string(), event: ServiceStreamEventSchema }),
|
|
1223
|
-
/* How an approved run ended, pushed after the platform answered so the card can settle as a receipt
|
|
1224
|
-
* rather than a promise: `ok` served and charged, `refunded` failed to answer and charged nothing,
|
|
1225
|
-
* `refused` the platform said no after the click (a raced-out allowance). `remaining` is the meter after,
|
|
1226
|
-
* when the platform stated one. Skip needs no receipt, nothing happened, and `resolved` already says so. */
|
|
1227
|
-
ServiceReceiptSchema.extend({ kind: z.literal("service_receipt"), requestId: z.string() }),
|
|
1228
|
-
/* A missing capability asking for the owner's setup, the agent hit something this sandbox is not
|
|
1229
|
-
* connected to and raised the card instead of describing manual steps. Raised OUTSIDE the turn generator
|
|
1230
|
-
* exactly like the service offer above (the daemon's ask route parks the agent's `capabilities request`
|
|
1231
|
-
* call and pushes this frame into the live run; capabilities/capability-offer.ts), so it is not
|
|
1232
|
-
* journalled for restore either: its waiter is the CLI's held connection, which dies with the daemon.
|
|
1233
|
-
* Settles through the same `POST /agent/reply` as every other card. */
|
|
1234
|
-
z.object({ kind: z.literal("capability_offer"), ...capabilityOfferCard }),
|
|
1235
|
-
/* How an accepted ask ended, pushed once the daemon stops watching for the connection: `connected`, the
|
|
1236
|
-
* capability came live while the agent waited (`id` is the connected instance, the agent's handle for it)
|
|
1237
|
-
*, or `unfinished`, the setup did not complete while anyone was waiting (the deadline passed, or the
|
|
1238
|
-
* asking command died). A skip needs no outcome frame, nothing was set up, and `resolved` already says
|
|
1239
|
-
* so. It is what settles the card's "waiting for you to finish setup" state on every surface. */
|
|
1240
|
-
CapabilityOutcomeSchema.extend({ kind: z.literal("capability_outcome"), requestId: z.string() }),
|
|
1241
|
-
/* A USDC payment awaiting the owner's click. Raised OUTSIDE the turn generator exactly like the service
|
|
1242
|
-
* offer above (the daemon's wallet route parks the agent's `wallet fetch` call and pushes this frame into
|
|
1243
|
-
* the live run; wallet/payment-offer.ts), so it is not journalled for restore either: its waiter is the
|
|
1244
|
-
* CLI's held connection, which dies with the daemon. Settles through the same `POST /agent/reply`. */
|
|
1245
|
-
z.object({ kind: z.literal("payment_offer"), ...paymentOfferCard }),
|
|
1246
|
-
/* How an approved (or auto-approved) payment ended, pushed after the endpoint answered so the card can
|
|
1247
|
-
* settle as a receipt rather than a promise: `paid`, the endpoint confirmed settlement (`transaction` is
|
|
1248
|
-
* the onchain hash when it stated one); `failed`, the payment was refused or settlement failed, in which
|
|
1249
|
-
* case the signed authorization expires unused and NOTHING left the wallet. A skip needs no receipt,
|
|
1250
|
-
* nothing moved, and `resolved` already says so. */
|
|
1251
|
-
PaymentReceiptSchema.extend({ kind: z.literal("payment_receipt"), requestId: z.string() }),
|
|
1252
|
-
/* A GATED CREDENTIAL awaiting a NAMED person's click, the one card on this stream that is not addressed to
|
|
1253
|
-
* the owner: the daemon holds an exit (a `{{secret:…}}` about to resolve, a browser field about to be
|
|
1254
|
-
* typed into, a connected account about to be mounted) parked until one of the gate's approvers releases
|
|
1255
|
-
* it (secrets/credential-gate.ts). Raised OUTSIDE the turn generator like the offers above — the exits run
|
|
1256
|
-
* inside a PreToolUse hook and inside the daemon's own `secrets request` route — so it is not journalled
|
|
1257
|
-
* for restore: its waiter is a held hook or a held connection, both of which die with the daemon, and the
|
|
1258
|
-
* next use after a restart simply asks again. Settles through the same `POST /agent/reply`, which is where
|
|
1259
|
-
* the clicker's identity is checked against `offer.approvers`. */
|
|
1260
|
-
z.object({ kind: z.literal("credential_offer"), ...credentialOfferCard }),
|
|
1261
|
-
/* WHO RELEASED IT, pushed the moment a person decided, so the settled card names them rather than saying
|
|
1262
|
-
* only that something was approved: `released` with the approver's verified address, or `refused` when a
|
|
1263
|
-
* person said no. Nothing is pushed for a card nobody answered — `resolved` already says that, and a
|
|
1264
|
-
* deadline is not a refusal by anybody. */
|
|
1265
|
-
CredentialReceiptSchema.extend({ kind: z.literal("credential_receipt"), requestId: z.string() }),
|
|
1266
|
-
// The card above named by `requestId` is released, the user answered (or dismissed it, or the turn was
|
|
1267
|
-
// stopped out from under it), so the turn is executing again. Emitted by whoever parked, the moment its
|
|
1268
|
-
// waiter settles, because the park's END is otherwise invisible on this stream: nothing else here says
|
|
1269
|
-
// "that card is done", and it cannot be inferred from the next frame that happens along. Frames DO arrive
|
|
1270
|
-
// while a turn is parked, the pausing tool's own `tool_call` regularly trails its card (the SDK queues
|
|
1271
|
-
// stream messages while dispatching an in-process MCP tool straight off the transport), and a card raised
|
|
1272
|
-
// beside a parallel tool call sits through that tool's whole life. See agents-registry.ts, which reads
|
|
1273
|
-
// this pair as the fleet's "needs you" state.
|
|
1274
|
-
//
|
|
1275
|
-
// `reply` says HOW it settled, and is what a transcript rebuilt from this log freezes the card with: a
|
|
1276
|
-
// reload replays the run from seq 0 and a second window renders it live, so both would otherwise restore
|
|
1277
|
-
// the card pending, offering buttons on a requestId nothing holds any more, under a transcript that has
|
|
1278
|
-
// already moved on. It rides verbatim, exactly as the client POSTed it; absent, nobody answered (the turn
|
|
1279
|
-
// was stopped, or died under the card), which is not a decision and must not replay as one.
|
|
1280
|
-
z.object({ kind: z.literal("resolved"), requestId: z.string(), reply: AgentReplySchema.optional() }),
|
|
1281
|
-
/* There was a `permission_note` frame here: a late sentence raced onto a command card that had already gone
|
|
1282
|
-
* out, because the explanation was optional and the card must not wait for a one-shot helper rung that might
|
|
1283
|
-
* take tens of seconds. It is gone with the setting that made it optional. The judge now decides the
|
|
1284
|
-
* verdict, so the sentence is not a decoration arriving afterwards — it is the REASON THE CARD EXISTS, and
|
|
1285
|
-
* a card cannot be raised before it is known. Nothing races, and `PermissionAsk.explain` is populated at
|
|
1286
|
-
* raise time (guard/command-gate.ts). */
|
|
1287
|
-
// The turn's permission mode, whenever it changes, the user's pick at turn start, then every move the
|
|
1288
|
-
// AGENT makes on its own (EnterPlanMode on a request that needs thinking through, ExitPlanMode once the
|
|
1289
|
-
// user approves). The composer's mode selector follows this, so the UI never lies about the live posture.
|
|
1290
|
-
z.object({ kind: z.literal("mode"), mode: PermissionModeSchema }),
|
|
1291
|
-
// `code` is a machine-readable discriminator for errors the UI reacts to programmatically (dropping a
|
|
1292
|
-
// dead session id so the next send self-heals). Absent on plain failures.
|
|
1293
|
-
z.object({
|
|
1294
|
-
kind: z.literal("error"),
|
|
1295
|
-
message: z.string(),
|
|
1296
|
-
code: z
|
|
1297
|
-
.enum([
|
|
1298
|
-
"session-not-found",
|
|
1299
|
-
"rate_limit",
|
|
1300
|
-
// Codex ran the turn but warned about it (fallback model metadata), a notice, not a failure.
|
|
1301
|
-
"codex-advisory",
|
|
1302
|
-
"codex-reauth",
|
|
1303
|
-
// The Claude subscription credential is dead (revoked, or its refresh token rejected) and only a
|
|
1304
|
-
// reconnect fixes it. Distinct from "no account connected": the account IS there, so the UI can
|
|
1305
|
-
// offer reconnect where the user already is and replay the message that bounced.
|
|
1306
|
-
"claude-reauth",
|
|
1307
|
-
// The API refused this turn's token MID-FLIGHT, nearly always one superseded by a rotation,
|
|
1308
|
-
// which Anthropic retires the moment its successor is minted. Distinct from claude-reauth: the
|
|
1309
|
-
// account is fine and the daemon re-mints on the spot, so this is usually a notice about a turn
|
|
1310
|
-
// that is coming back rather than a request for the user to do anything. `autoResume` says
|
|
1311
|
-
// which of the two: "scheduled" means the re-mint-and-re-run is armed, and its absence means
|
|
1312
|
-
// nothing is coming (the turn was already a resume, or it ran on a credential with nothing to
|
|
1313
|
-
// re-mint from), that is the case where reconnecting really is the fix.
|
|
1314
|
-
"claude-token-refused",
|
|
1315
|
-
/* THE ACCOUNT IS FINE AND STILL NOT ALLOWED TO RUN, an Anthropic organization that has turned
|
|
1316
|
-
* Claude Code off for this seat. The token authenticates, the plan's own usage endpoint answers
|
|
1317
|
-
* with real pools, and every turn is refused anyway, which is why it is its own code rather than
|
|
1318
|
-
* a member of either neighbour: a spent allowance comes back on a clock and a refused credential
|
|
1319
|
-
* comes back on a re-mint, and NEITHER of those is true here. Only an admin re-enabling access
|
|
1320
|
-
* is, so nothing is re-run and nothing asks the user to reconnect, the one recovery that looks
|
|
1321
|
-
* plausible and is guaranteed to waste their time. */
|
|
1322
|
-
"claude-not-entitled",
|
|
1323
|
-
/* The model provider itself failed transiently: 500/502/503, a 529 at capacity, a dropped
|
|
1324
|
-
* socket, and the harness's own in-turn retries did not outlast it. Nothing about the workspace
|
|
1325
|
-
* or the request is wrong, so the daemon remembers the turn and re-runs it on an escalating
|
|
1326
|
-
* backoff (provider-health.ts): the frame is a notice about a turn that is coming back, and
|
|
1327
|
-
* reaches the client as a plain failure only once the attempts are spent.
|
|
1328
|
-
*
|
|
1329
|
-
* ONE 4xx JOINS THEM, the provider refusing a request PARAMETER nothing here sends (its own
|
|
1330
|
-
* cache-retention default, or one a proxy added). It wears a client error's status code and is
|
|
1331
|
-
* still a provider fault: there is no request of the user's to fix, and the same send goes
|
|
1332
|
-
* through moments later, so it recovers the same way (agent/failure-sentences.ts). */
|
|
1333
|
-
"provider-outage",
|
|
1334
|
-
// The platform-owned free-trial pool failed after its bounded key walk. Unlike provider-outage,
|
|
1335
|
-
// this is never auto-resumed: failed calls are refunded and the user's message is held to retry.
|
|
1336
|
-
"trial-unavailable",
|
|
1337
|
-
// The trial answered, but the selected upstream model/request cannot run through this sandbox.
|
|
1338
|
-
"trial-model-unavailable",
|
|
1339
|
-
// This account's platform-owned daily trial allowance is spent until its UTC reset.
|
|
1340
|
-
"trial-exhausted",
|
|
1341
|
-
// The harness read the message as a slash command it doesn't have, and discarded everything
|
|
1342
|
-
// after the name, the model never saw the message. Nothing was processed, so the client holds
|
|
1343
|
-
// the text back instead of leaving the user to retype it (same treatment as claude-reauth).
|
|
1344
|
-
"unknown-command",
|
|
1345
|
-
"grok-model-invalid",
|
|
1346
|
-
"codex-model-invalid",
|
|
1347
|
-
/* THE MODEL IS REAL, LISTED, AND NOT THIS PLAN'S TO RUN. A routed provider's catalog is the set
|
|
1348
|
-
* the vendor publishes, not the set the connected subscription pays for, so a picker row can be
|
|
1349
|
-
* a model the upstream refuses on sight ("Your current subscription does not have access to
|
|
1350
|
-
* kimi-for-coding-highspeed. Upgrade to higher-tier Kimi Code plans.").
|
|
1351
|
-
*
|
|
1352
|
-
* Its own code because every neighbour's recovery is wrong for it. It is not `provider-outage`,
|
|
1353
|
-
* though that is what it wore: the translator answers a refused model with a 503, the harness
|
|
1354
|
-
* reads a 5xx as an outage, and rides it out for the whole in-turn retry budget — two minutes of
|
|
1355
|
-
* a spinner for a request that was refused in five milliseconds and will be refused identically
|
|
1356
|
-
* forever, followed by an auto-resume schedule for a turn that cannot come back. It is not
|
|
1357
|
-
* `*-model-invalid` either: nothing is misspelled and reloading the catalog re-offers the same
|
|
1358
|
-
* row, because the vendor really does serve it — to somebody else.
|
|
1359
|
-
*
|
|
1360
|
-
* What changes the outcome is picking another model or buying the plan, so the sentence is the
|
|
1361
|
-
* upstream's own (it names the tier), the client holds the words, and the daemon files the model
|
|
1362
|
-
* as refused so the picker stops offering it (usage/model-refusals.ts). */
|
|
1363
|
-
"model-unavailable",
|
|
1364
|
-
/* THE MODEL CANNOT HOLD A TURN OF THIS AGENT LOOP, so the daemon refused before sending
|
|
1365
|
-
* (agent/context-budget.ts). Its own code because none of the neighbours describes it: nothing is
|
|
1366
|
-
* disconnected, nothing is spent, nothing comes back on a clock, and re-sending the same request
|
|
1367
|
-
* at the same model fails identically forever. What changes the outcome is the model or the
|
|
1368
|
-
* server's context flag, so the message names both and the client HOLDS the words: they never
|
|
1369
|
-
* reached anything, and losing them to a configuration fact would be the one part of this that
|
|
1370
|
-
* was our fault. */
|
|
1371
|
-
"context-window-too-small",
|
|
1372
|
-
"subscription-required",
|
|
1373
|
-
"agent-busy",
|
|
1374
|
-
/* THE SANDBOX HAS NO MEMORY LEFT TO RUN THIS TURN, refused before anything was spawned
|
|
1375
|
-
* (platform/memory-admission.ts). Its own code because it is the only refusal here that is
|
|
1376
|
-
* about the BOX rather than the request: the prompt, the model and the credential are all
|
|
1377
|
-
* fine, and the identical request succeeds once something inside frees room, which is the
|
|
1378
|
-
* opposite of context-window-too-small next door. Transient without being on a clock, so
|
|
1379
|
-
* there is no resetsAt to offer — what changes the outcome is a turn finishing or a session
|
|
1380
|
-
* closing, and the message says so. The client HOLDS the words for the same reason
|
|
1381
|
-
* context-window-too-small does: they never reached a provider, and losing them to a
|
|
1382
|
-
* capacity fact the user did not cause would be ours to answer for. */
|
|
1383
|
-
"sandbox-memory-low",
|
|
1384
|
-
/* THE LOOP RAN OUT OF ITERATIONS, not out of work: the runtime hit its own turn ceiling and
|
|
1385
|
-
* stopped. Nothing failed, nothing is disconnected and nothing is spent, which is exactly why
|
|
1386
|
-
* it needs a code of its own rather than a sentence: it is the one ending that LOOKS like a
|
|
1387
|
-
* finished turn from the outside, and a ledger that filed it as an ordinary error told a
|
|
1388
|
-
* reader nothing they could act on.
|
|
1389
|
-
*
|
|
1390
|
-
* The recovery is the user's, not the daemon's: whatever the turn was doing is half done, and
|
|
1391
|
-
* re-running it blind would either redo the finished half or resume work nobody looked at. */
|
|
1392
|
-
"turn-cap",
|
|
1393
|
-
/* THE HARNESS ENDED THE TURN WITHOUT SUCCEEDING and did not say why in terms anything here
|
|
1394
|
-
* models: an internal execution error, or a result subtype a later vendor build invented. The
|
|
1395
|
-
* sentence carries the subtype verbatim, because that word is the only thing separating two of
|
|
1396
|
-
* these, and a code that meant "one of several unrelated things" would be worse than none. It
|
|
1397
|
-
* is a real classification all the same: it says the failure came from the LOOP rather than
|
|
1398
|
-
* from the provider, the credential or the request, which rules out every recovery next door. */
|
|
1399
|
-
"harness-incomplete",
|
|
1400
|
-
/* THE ENGINE IS TOO OLD FOR THE MODEL, and the provider says so in the same breath as the
|
|
1401
|
-
* version that would work ("Claude Code 2.1.233 does not support this model; version 2.1.251
|
|
1402
|
-
* or newer is required"). Its own code because the fix is unlike every neighbour's: nothing is
|
|
1403
|
-
* disconnected, nothing is spent, no retry of any length helps, and the thing that has to
|
|
1404
|
-
* change is not the request but the PROGRAM running it (schemas/engines.ts).
|
|
1405
|
-
*
|
|
1406
|
-
* It used to be unfixable from inside a sandbox at all — the engine came with the image, so a
|
|
1407
|
-
* whole fleet failed every turn on this model until a new image reached it. Now the daemon can
|
|
1408
|
-
* install the version the provider named, which is why this frame carries the numbers rather
|
|
1409
|
-
* than only the sentence: `engine` is what the card's Update button acts on. The install is
|
|
1410
|
-
* still a person's decision, because the version that satisfies a floor is by definition one
|
|
1411
|
-
* nobody has blessed yet. */
|
|
1412
|
-
"engine-version-floor",
|
|
1413
|
-
])
|
|
1414
|
-
.optional(),
|
|
1415
|
-
/* engine-version-floor only: which engine is too old, what it is running, and the floor the provider
|
|
1416
|
-
* demanded. On the wire because the recovery is a specific, offerable action — install at or above
|
|
1417
|
-
* `floor` — and a client that had only the sentence would have to parse prose to offer it. */
|
|
1418
|
-
engine: z
|
|
1419
|
-
.object({
|
|
1420
|
-
id: z.string().describe("Which engine (e.g. claude)."),
|
|
1421
|
-
running: z.string().optional().describe("The version that was refused, when the provider named it."),
|
|
1422
|
-
floor: z.string().describe("The lowest version the provider will accept."),
|
|
1423
|
-
})
|
|
1424
|
-
.optional(),
|
|
1425
|
-
// rate_limit only: when the exhausted window reopens (epoch seconds, from the stream's own
|
|
1426
|
-
// rate_limit_event or the account's persisted usage windows). Absent when the reset instant is unknown
|
|
1427
|
-
// (nothing to schedule against).
|
|
1428
|
-
resetsAt: z.number().optional(),
|
|
1429
|
-
/* Where the daemon's resume of THIS turn stands, for the three codes that have one (provider-outage,
|
|
1430
|
-
* claude-token-refused, rate_limit). "scheduled" = the resume is armed and this turn comes back by
|
|
1431
|
-
* itself; "available" = the daemon remembered the failed turn and arming THIS conversation
|
|
1432
|
-
* (AgentSummarySchema's resumeAfterOutage / resumeAfterLimit) picks up that same resume, which is what
|
|
1433
|
-
* the offer banner hangs off, gated codes only, since a credential renewal is never gated on a posture
|
|
1434
|
-
* at all. The two words are read against the effective posture (the conversation's override, else the
|
|
1435
|
-
* sandbox default), so a chat armed on its own says "scheduled" while the unarmed board around it says
|
|
1436
|
-
* "available". Absent means there is nothing automatic to resume: a limit whose reset instant nobody
|
|
1437
|
-
* published has nothing to schedule against, and a refused credential has none once re-minting it has
|
|
1438
|
-
* already been tried and failed.
|
|
1439
|
-
*
|
|
1440
|
-
* A SPENT ALLOWANCE USED TO BE ABSENT HERE BY RULE, and the rule was right about the default and wrong
|
|
1441
|
-
* about the ceiling. The budget is the user's, so nothing fires unless they said so, which is what the
|
|
1442
|
-
* posture is; what the old absence also cost was the case a press cannot reach, a 2am wall on a board
|
|
1443
|
-
* nobody is watching. Both words are now honest for it: unarmed says "available", which is an offer,
|
|
1444
|
-
* and armed says "scheduled", which the card counts down to. */
|
|
1445
|
-
autoResume: z.enum(["scheduled", "available"]).optional(),
|
|
1446
|
-
/* THE DAEMON IS STILL HOLDING THIS EXACT TURN, so the way on is to RE-RUN it rather than to send
|
|
1447
|
-
* something after it. rate_limit only, and the counterpart to `autoResume` rather than a member of it:
|
|
1448
|
-
* that field answers "is a machine bringing this back", which for a spent allowance is a posture the
|
|
1449
|
-
* user sets and defaults to no (the allowance is their own budget to spend, turn-resume.ts). This
|
|
1450
|
-
* answers the question that was never asked, "and if the user says go, what happens", which had exactly
|
|
1451
|
-
* one possible answer for as long as it went unasked: a new user message reading "Continue".
|
|
1452
|
-
*
|
|
1453
|
-
* BOTH ANSWERS RUN THROUGH THIS FIELD, which is why it is not folded into the one above: an armed
|
|
1454
|
-
* conversation's scheduled fire and an unarmed one's press are the same held turn re-run the same way,
|
|
1455
|
-
* and the only difference is who says go.
|
|
1456
|
-
*
|
|
1457
|
-
* That answer was wrong in a way the chat could not show. The press is not a new instruction, it is the
|
|
1458
|
-
* same one again, and appending it said otherwise to the only reader that matters: the provider session
|
|
1459
|
-
* grew one "Continue" per press, each with a synthetic "No response requested." above it, so a chat that
|
|
1460
|
-
* bounced four times handed the model four turns in which it appeared to have declined to answer. With
|
|
1461
|
-
* this field the press re-runs the held turn instead, which is idempotent by construction (a second press
|
|
1462
|
-
* finds a live turn and supersedes nothing) and leaves the transcript one row for one press.
|
|
1463
|
-
*
|
|
1464
|
-
* `ran` is whether the held turn got anywhere before it was refused, and it changes both what the model
|
|
1465
|
-
* is told (RESUME_NOTES.limit vs .refused, and telling a model to carry on from work that never happened
|
|
1466
|
-
* is how it comes to invent some) and what the strip can honestly say. A spent allowance refuses the
|
|
1467
|
-
* FIRST request most of the time, so false is the common case, not the corner. */
|
|
1468
|
-
held: z.object({ ran: z.boolean() }).optional(),
|
|
1469
|
-
/* provider-outage only: the shape of the wait. `retryAt` (epoch seconds) is when the next attempt is
|
|
1470
|
-
* due, not a fixed cadence, because an outage has no reset instant to aim at and hammering a provider
|
|
1471
|
-
* that is down only spends tokens on refusals, so each attempt waits longer than the last
|
|
1472
|
-
* (provider-health.ts owns the schedule).
|
|
1473
|
-
*
|
|
1474
|
-
* `attempt`/`maxAttempts` are on the wire so the notice can say the automation is BOUNDED. A retry that
|
|
1475
|
-
* gives no account of how long it will keep going is the kind users switch back off the week they turn
|
|
1476
|
-
* it on; one that says "attempt 2 of 6" is one they leave on. */
|
|
1477
|
-
outage: z.object({ retryAt: z.number(), attempt: z.number(), maxAttempts: z.number() }).optional(),
|
|
1478
|
-
}),
|
|
1479
|
-
z.object({ kind: z.literal("done") }),
|
|
1480
|
-
]);
|
|
1481
|
-
export type AgentEvent = z.infer<typeof AgentEventSchema>;
|
|
1482
|
-
|
|
1483
|
-
/* THE FRAMES THAT ARE FACTS ABOUT THE TURN rather than words in it: which session it runs, where it stands,
|
|
1484
|
-
* what it costs, how it failed. Everything else an AgentEvent can say is transcript, and reaches a client as
|
|
1485
|
-
* rows and patches (TranscriptPatchSchema) after the daemon has folded it; these reach it as themselves,
|
|
1486
|
-
* because there is nothing to fold, a client keeps them as state beside the transcript. A frame can be both,
|
|
1487
|
-
* a `worktree` that rebased writes a notice AND says where the branch is, so the two lists overlap, and the
|
|
1488
|
-
* fold and this list each take the half that is theirs. */
|
|
1489
|
-
export const TURN_FACT_KINDS = [
|
|
1490
|
-
"session",
|
|
1491
|
-
"worktree",
|
|
1492
|
-
"init",
|
|
1493
|
-
"terminal",
|
|
1494
|
-
"browser",
|
|
1495
|
-
"commands",
|
|
1496
|
-
"usage",
|
|
1497
|
-
"rate_limit_info",
|
|
1498
|
-
"fast_mode",
|
|
1499
|
-
"tier",
|
|
1500
|
-
"provider_retry",
|
|
1501
|
-
"account_usage",
|
|
1502
|
-
"context_usage",
|
|
1503
|
-
"mode",
|
|
1504
|
-
"error",
|
|
1505
|
-
] as const;
|
|
1506
|
-
export type TurnFact = Extract<AgentEvent, { kind: (typeof TURN_FACT_KINDS)[number] }>;
|
|
1507
|
-
export const isTurnFact = (event: AgentEvent): event is TurnFact => (TURN_FACT_KINDS as readonly string[]).includes(event.kind);
|
|
1508
|
-
// The same members AgentEventSchema declares, picked out rather than declared twice: a fact's shape is the
|
|
1509
|
-
// frame's shape, and a second spelling of it would be the drift the list above exists to prevent.
|
|
1510
|
-
type AgentEventMember = (typeof AgentEventSchema.options)[number];
|
|
1511
|
-
const factMembers = AgentEventSchema.options.filter((member) => (TURN_FACT_KINDS as readonly string[]).includes(member.shape.kind.value)) as unknown as [
|
|
1512
|
-
AgentEventMember,
|
|
1513
|
-
...AgentEventMember[],
|
|
1514
|
-
];
|
|
1515
|
-
export const TurnFactSchema = z.discriminatedUnion("kind", factMembers) as unknown as z.ZodType<TurnFact>;
|
|
1516
|
-
|
|
1517
|
-
/* The /agent/attach stream: a head carrying the run's rows so far, then every change to them and every fact
|
|
1518
|
-
* about the turn as each lands, then `end` when the run is over, nothing more coming. A stream that closes
|
|
1519
|
-
* WITHOUT `end` was dropped mid-run; the client re-attaches and takes the head's rows again, whole, which is
|
|
1520
|
-
* what makes attaching idempotent: a window never re-folds what it has already drawn, it replaces it.
|
|
1521
|
-
*
|
|
1522
|
-
* Facts REPLAY on every attach (their seq is at or below the head's), because a window joining late still has
|
|
1523
|
-
* to learn which session the turn runs and where its branch stands; patches are only ever live (their seq is
|
|
1524
|
-
* above the head's), because the head already holds their result. */
|
|
1525
|
-
export const AttachFrameSchema = z.discriminatedUnion("kind", [
|
|
1526
|
-
z.object({
|
|
1527
|
-
kind: z.literal("attached").describe("The first frame, identifying the run you have joined and handing you its transcript so far."),
|
|
1528
|
-
run: z.string().describe("The run's id."),
|
|
1529
|
-
startedAt: z.number().describe("When it started, in milliseconds, so a window joining late can show how long it has been going."),
|
|
1530
|
-
seq: z.number().describe("How many frames the run has produced so far. A fact at or below this number is being replayed; a patch is never."),
|
|
1531
|
-
rows: z
|
|
1532
|
-
.array(TranscriptRowSchema)
|
|
1533
|
-
.describe("The turn's rows as they stand: what was asked, and everything the agent has said and done since. Draw these, then apply the patches that follow."),
|
|
1534
|
-
}),
|
|
1535
|
-
z.object({
|
|
1536
|
-
kind: z.literal("patch").describe("One change to the run's rows."),
|
|
1537
|
-
seq: z.number().describe("Its position in the run, counting from one."),
|
|
1538
|
-
patch: TranscriptPatchSchema,
|
|
1539
|
-
}),
|
|
1540
|
-
z.object({
|
|
1541
|
-
kind: z.literal("fact").describe("One thing about the turn that is not a row: its session, its branch, its cost, a failure."),
|
|
1542
|
-
seq: z.number().describe("Its position in the run, counting from one. At or below the head's number, it is being replayed."),
|
|
1543
|
-
fact: TurnFactSchema,
|
|
1544
|
-
}),
|
|
1545
|
-
z.object({
|
|
1546
|
-
kind: z
|
|
1547
|
-
.literal("end")
|
|
1548
|
-
.describe(
|
|
1549
|
-
"The run is over and every frame has been delivered. A stream that closes without this was dropped mid-run, so re-attach rather than assuming the turn finished.",
|
|
1550
|
-
),
|
|
1551
|
-
}),
|
|
1552
|
-
]);
|
|
1553
|
-
export type AttachFrame = z.infer<typeof AttachFrameSchema>;
|
|
1554
|
-
|
|
1555
|
-
/* WHAT A RESUMED TURN'S PROMPT SAYS IT IS. The daemon re-runs a turn something underneath it killed (turn-resume.ts)
|
|
1556
|
-
* by sending the original prompt again behind one of these sentences, so the model knows what interrupted it.
|
|
1557
|
-
*
|
|
1558
|
-
* They live on the wire rather than in the daemon because the CLIENT has to recognise them too: an attach head
|
|
1559
|
-
* carries the run's prompt verbatim, and a window joining a resumed run would otherwise render the note as a
|
|
1560
|
-
* message the USER wrote, the same words the user already said one run up, with a machine's preamble on them.
|
|
1561
|
-
* Recognising the prefix is what lets that window reuse the bubble that is already there instead. */
|
|
1562
|
-
// The instruction the three whole-turn re-runs share: what follows the note is the original request, repeated.
|
|
1563
|
-
// `answered` deliberately does not carry it, what follows THAT note is not a repetition but the user's answer,
|
|
1564
|
-
// and telling the model to "continue from that point instead of starting over" about words it has never seen
|
|
1565
|
-
// is how a resume reads as the user contradicting themselves.
|
|
1566
|
-
const REPEATED =
|
|
1567
|
-
"The interrupted request is repeated below, where part of it was already completed in this session, continue from that point instead of starting over.";
|
|
1568
|
-
export const RESUME_NOTES = {
|
|
1569
|
-
auth: `The Claude credential that interrupted this conversation has been renewed, and this turn resumed automatically. ${REPEATED}`,
|
|
1570
|
-
outage: `The model provider was briefly unavailable and interrupted this conversation; this turn resumed automatically. ${REPEATED}`,
|
|
1571
|
-
restart: `The sandbox restarted while this turn was running, which stopped it, and this turn resumed automatically once it came back. ${REPEATED}`,
|
|
1572
|
-
/* A SPENT ALLOWANCE STRANDS A TURN IN THREE SHAPES, and they must not share a note.
|
|
1573
|
-
*
|
|
1574
|
-
* `limit` is the mid-turn one and reads like its three neighbours above: the session holds real work, and
|
|
1575
|
-
* carrying on from it is exactly right.
|
|
1576
|
-
*
|
|
1577
|
-
* `switched` is that same mid-turn stranding picked back up on a DIFFERENT account (or provider, or
|
|
1578
|
-
* harness), which is what the composer's account switcher does between the refusal and the press. A session
|
|
1579
|
-
* belongs to the credential that minted it, so this one cannot resume: it opens a fresh session seeded from
|
|
1580
|
-
* the daemon's record. REPEATED's "already completed in this session" is therefore false where it counts —
|
|
1581
|
-
* the work is in the carried-across conversation, not in this session's own history — and a model told to
|
|
1582
|
-
* look for it there finds nothing and starts over silently.
|
|
1583
|
-
*
|
|
1584
|
-
* `refused` is the turn the provider turned away at the door, before the model read one word of it, and it
|
|
1585
|
-
* is the COMMONER of the two, because an allowance that is already spent refuses the first request it is
|
|
1586
|
-
* asked. REPEATED is actively wrong for it: "part of it was already completed in this session, continue from
|
|
1587
|
-
* that point instead of starting over" is an instruction to continue from work that does not exist, and a
|
|
1588
|
-
* model handed that instruction answers it by inventing the work. So it says the opposite, plainly.
|
|
1589
|
-
*
|
|
1590
|
-
* Both are unlike their neighbours in one way worth stating: nothing resumed automatically. A spent
|
|
1591
|
-
* allowance is the user's own budget and stays their call to spend (turn-resume.ts), so what re-ran this
|
|
1592
|
-
* turn was a person pressing Continue. */
|
|
1593
|
-
limit: `The model provider's usage allowance ran out while this turn was running, which stopped it, and it has been sent again. ${REPEATED}`,
|
|
1594
|
-
switched:
|
|
1595
|
-
"The model provider's usage allowance ran out while this turn was running, which stopped it, and it has been sent again on a different account, which starts a fresh session. The conversation so far has been carried across above, including the part of the request that was already completed: continue from that point instead of starting over.",
|
|
1596
|
-
refused:
|
|
1597
|
-
"The model provider refused the previous attempt at this request outright, because its usage allowance was spent: no part of the request below was read or acted on, and nothing has been done towards it. It has been sent again, and starts from the beginning.",
|
|
1598
|
-
// A turn that was PARKED on the user when the daemon died: nothing re-runs at boot, the card is restored
|
|
1599
|
-
// instead, and this is the turn their answer starts (turn-resume.ts). What rides below the note is the
|
|
1600
|
-
// answer itself, so the model picks the session back up at exactly the decision it had handed over.
|
|
1601
|
-
answered:
|
|
1602
|
-
"The sandbox restarted while this conversation was waiting for the user to respond; it is back, and their response follows below: continue from where the session left off.",
|
|
1603
|
-
} as const;
|
|
1604
|
-
|
|
1605
|
-
// The prompt a resume actually sends: the note (each carries its own account of what the words below are),
|
|
1606
|
-
// then them.
|
|
1607
|
-
export const withResumeNote = (prompt: string, note: string): string =>
|
|
1608
|
-
Object.values(RESUME_NOTES).some((known) => prompt.startsWith(known)) ? prompt : `${note}\n\n${prompt}`;
|
|
1609
|
-
|
|
1610
|
-
// The user's own words inside a resumed prompt, the note and its explanation stripped back off. Returns the
|
|
1611
|
-
// prompt unchanged when it is not a resume, so a caller can hand every attach head through it.
|
|
1612
|
-
export const withoutResumeNote = (prompt: string): string => {
|
|
1613
|
-
const note = Object.values(RESUME_NOTES).find((known) => prompt.startsWith(known));
|
|
1614
|
-
return note === undefined ? prompt : prompt.slice(prompt.indexOf("\n\n") + 2);
|
|
1615
|
-
};
|
|
1616
|
-
|
|
1617
|
-
export type ResumeReason = keyof typeof RESUME_NOTES;
|
|
1618
|
-
|
|
1619
|
-
/* HOW A RESUMED TURN READS TO THE PERSON, the same interruption the note above tells the model, said in the
|
|
1620
|
-
* transcript's own voice instead.
|
|
1621
|
-
*
|
|
1622
|
-
* Stripping the note out of the user's words is only half the job, and for years it was the only half anyone
|
|
1623
|
-
* did: what a reopened conversation showed was a paragraph of machine prose stapled to the front of a message
|
|
1624
|
-
* the user had already sent once, directly under their own copy of it. Both halves of that are wrong, it was
|
|
1625
|
-
* never their sentence, and the words under it are a REPEAT rather than something new they said.
|
|
1626
|
-
*
|
|
1627
|
-
* So the two shapes below, which is the whole of what a reader has to be told:
|
|
1628
|
-
*
|
|
1629
|
-
* `notice`, the three whole-turn re-runs. The words under the note are already in the transcript one turn up,
|
|
1630
|
-
* so the repeat is dropped entirely and the interruption takes its place as a muted line, sitting with the
|
|
1631
|
-
* failure line it resolves ("Failed to authenticate…") and reading like every other thing that HAPPENED to a
|
|
1632
|
-
* turn rather than like something anybody typed.
|
|
1633
|
-
*
|
|
1634
|
-
* `note`, the answered case, where what rides under the note is the user's actual answer to a card and belongs
|
|
1635
|
-
* in the transcript as their words. Nothing is dropped; the explanation rides that message as a collapsed row,
|
|
1636
|
-
* the same disclosure every other daemon-written note gets (TurnNote). */
|
|
1637
|
-
export type ResumeDisclosure = { readonly kind: "notice"; readonly text: string } | { readonly kind: "note"; readonly note: TurnNote };
|
|
1638
|
-
|
|
1639
|
-
const RESUME_DISCLOSURES: Record<ResumeReason, ResumeDisclosure> = {
|
|
1640
|
-
auth: { kind: "notice", text: "Claude sign-in renewed, this turn picked up where it left off." },
|
|
1641
|
-
outage: { kind: "notice", text: "The model provider came back, this turn picked up where it left off." },
|
|
1642
|
-
restart: { kind: "notice", text: "The sandbox came back, this turn picked up where it left off." },
|
|
1643
|
-
/* THE THREE NOBODY AUTOMATED, said in the passive voice the other three earn honestly and these do not: a
|
|
1644
|
-
* person pressed Continue. Which is the whole reason these rows exist at all. A press used to append the word
|
|
1645
|
-
* "Continue" as a message of its own, so a chat that bounced off a spent allowance four times read back as
|
|
1646
|
-
* the user saying "Continue" four times to an agent that had answered none of them, and the provider session
|
|
1647
|
-
* the model actually reads accumulated all four (plus a synthetic "No response requested." per press). One
|
|
1648
|
-
* row for one press was never the problem; a row that claims the user said something new is.
|
|
1649
|
-
*
|
|
1650
|
-
* `switched` names the account because that is the fact the reader needs: they pressed the same button they
|
|
1651
|
-
* pressed a minute ago, and the difference between the press that bounced and the press that worked is who
|
|
1652
|
-
* served it. The line is also the only place a retired session is accounted for. */
|
|
1653
|
-
limit: { kind: "notice", text: "Sent again after the allowance ran out mid-turn, picking up where it left off." },
|
|
1654
|
-
switched: { kind: "notice", text: "Sent again on the switched account after the allowance ran out mid-turn, in a fresh session." },
|
|
1655
|
-
refused: { kind: "notice", text: "Sent again after the allowance refused it: nothing had run." },
|
|
1656
|
-
answered: { kind: "note", note: { title: "Picked back up after a sandbox restart", text: RESUME_NOTES.answered } },
|
|
1657
|
-
};
|
|
1658
|
-
|
|
1659
|
-
// What a stored prompt's resume note should be SHOWN as; undefined when the prompt is not a resume at all, so
|
|
1660
|
-
// every reader of a stored prompt can ask without first testing whether it is one.
|
|
1661
|
-
export const resumeDisclosure = (prompt: string): ResumeDisclosure | undefined => {
|
|
1662
|
-
const reason = (Object.keys(RESUME_NOTES) as ResumeReason[]).find((key) => prompt.startsWith(RESUME_NOTES[key]));
|
|
1663
|
-
return reason === undefined ? undefined : RESUME_DISCLOSURES[reason];
|
|
1664
|
-
};
|
|
1665
|
-
|
|
1666
|
-
// One parsed line from `intentic … --output ndjson` (engine events, provider `log`, the terminal `result`).
|
|
1667
|
-
// Open-ended by design, the sandbox consumes the wire shape, not @intentic/engine's types, so a string
|
|
1668
|
-
// `kind` plus arbitrary extra fields pass through. The apply-events tail (intentic.contract `applyEvents`) rides
|
|
1669
|
-
// this same loose shape with three daemon/CLI-minted sentinel kinds alongside the engine ones: {kind:"start"}
|
|
1670
|
-
// (first line, written when the run's file is reset), {kind:"exit",code} (last line, on the CLI process exit),
|
|
1671
|
-
// and {kind:"heartbeat"} (interleaved by the tail while idle to keep the held-open stream alive).
|
|
1672
|
-
export const IntenticLineSchema = z.looseObject({ kind: z.string() });
|
|
1673
|
-
export type IntenticLine = z.infer<typeof IntenticLineSchema>;
|
|
1674
|
-
|
|
1675
|
-
// The daemon's liveness heartbeat frame: the browser holds the events stream open and trips a watchdog if the
|
|
1676
|
-
// frames stop (the tunnel drops the proxied response when the origin dies).
|
|
1677
|
-
export const HeartbeatSchema = z.object({ kind: z.literal("heartbeat") });
|
|
1678
|
-
export type Heartbeat = z.infer<typeof HeartbeatSchema>;
|
|
1679
|
-
|
|
1680
|
-
// One step of the daemon's boot chain. `key` is the stable id the daemon declares it under, `label` the words
|
|
1681
|
-
// the browser shows. A step that FAILED is still a step that finished, the boot chain is log-and-continue by
|
|
1682
|
-
// design (see main.ts), so a failure degrades one subsystem rather than holding the gate closed forever.
|
|
1683
|
-
export const BootStepSchema = z.object({
|
|
1684
|
-
key: z.string(),
|
|
1685
|
-
label: z.string(),
|
|
1686
|
-
state: z.enum(["pending", "running", "done", "failed"]),
|
|
1687
|
-
// Elapsed ms, once the step has finished.
|
|
1688
|
-
ms: z.number().optional(),
|
|
1689
|
-
});
|
|
1690
|
-
export type BootStep = z.infer<typeof BootStepSchema>;
|
|
1691
|
-
|
|
1692
|
-
/* WHERE THE DAEMON IS IN ITS BOOT. The listeners come up before the state they serve has converged (main.ts:
|
|
1693
|
-
* "listen first, converge behind the gate"), which is what stops a restart from reading as an outage, but it
|
|
1694
|
-
* also means the daemon spends the first seconds of every boot both reachable and unable to answer, and until
|
|
1695
|
-
* this frame existed the browser had no way to tell that apart from a healthy sandbox. It painted an operable
|
|
1696
|
-
* workspace off its persisted cache and then parked every request the user made against the readiness gate.
|
|
1697
|
-
*
|
|
1698
|
-
* The step list is declared UP FRONT and sent whole, pending entries included, so the browser can say "4 of 11,
|
|
1699
|
-
* loading the conversation registry" rather than "something is happening", a boot that takes minutes has one
|
|
1700
|
-
* slow step, and naming it is the whole point. Snapshot-not-diff, like every other roster on this stream. */
|
|
1701
|
-
export const BootProgressSchema = z.object({
|
|
1702
|
-
// False only while the chain is still converging. The browser holds every daemon read until this is true.
|
|
1703
|
-
ready: z.boolean(),
|
|
1704
|
-
// Epoch ms the daemon started converging, so the browser can show a total elapsed that survives a reconnect.
|
|
1705
|
-
startedAt: z.number(),
|
|
1706
|
-
steps: z.array(BootStepSchema),
|
|
1707
|
-
});
|
|
1708
|
-
export type BootProgress = z.infer<typeof BootProgressSchema>;
|
|
1709
|
-
|
|
1710
|
-
// Pushed on every step transition and once more when the gate opens. Rides /events, which answers before the
|
|
1711
|
-
// gate precisely so this can be delivered while everything else waits.
|
|
1712
|
-
export const BootSchema = z.object({ kind: z.literal("boot"), ...BootProgressSchema.shape });
|
|
1713
|
-
export type Boot = z.infer<typeof BootSchema>;
|
|
1714
|
-
|
|
1715
|
-
// The stream's first frame: the workspace's stable identity, minted at the first boot of an empty /work. The
|
|
1716
|
-
// browser remembers it per sandbox id and drops that sandbox's persisted query cache when it changes, a wiped
|
|
1717
|
-
// and recreated workspace (cleanup.sh + reconnect keeps the same sandbox id) must not be painted from the
|
|
1718
|
-
// previous workspace's cache. `build` is the same guard against a different axis: the daemon's own compiled
|
|
1719
|
-
// tree, so an image update (or a `pnpm build:sandbox` swap in dev) drops what the browser cached from the
|
|
1720
|
-
// PREVIOUS build instead of hydrating payloads the new one no longer shapes that way.
|
|
1721
|
-
//
|
|
1722
|
-
// It also advertises `routes`, the contract route names (`vpn.list`, `kimi.models`) this daemon actually
|
|
1723
|
-
// implements, from ITS build of the contract. A browser is routinely newer than the daemon it talks to (a
|
|
1724
|
-
// released app plane serves whatever image each user last pulled; in local dev the web app is always ahead of
|
|
1725
|
-
// the last `pnpm build:sandbox`), and that stays fully supported, the browser just compares the two sets so a
|
|
1726
|
-
// route the daemon predates surfaces as a named, explained gap instead of a bare 404 nobody can attribute.
|
|
1727
|
-
//
|
|
1728
|
-
// `shapes` answers the half `routes` structurally cannot: a route BOTH builds have, whose payload changed
|
|
1729
|
-
// between them. Names match, so nothing 404s, the call goes out and a field the browser expects is simply
|
|
1730
|
-
// missing from the answer. It is a map of route name → a fingerprint of that route's input and output schema
|
|
1731
|
-
// (see routes.ts), so a difference is a named route rather than "something, somewhere, moved". Beside `routes`
|
|
1732
|
-
// rather than folded into it: existence covers every route, shape covers only the ones that can be expressed.
|
|
1733
|
-
//
|
|
1734
|
-
// Every added field is optional: a daemon built before one simply says nothing, and the browser's fallback is
|
|
1735
|
-
// the pre-existing behaviour, routes all assumed present, shapes all assumed to agree, the daemon assumed
|
|
1736
|
-
// ready, the cache left alone. That is also why `routes` keeps its bare-string-array shape: an image already in
|
|
1737
|
-
// the wild sends exactly that, and a breaking change here would fail the hello frame's own parse and take the
|
|
1738
|
-
// whole event stream down for precisely the skew this frame exists to describe.
|
|
1739
|
-
export const HelloSchema = z.object({
|
|
1740
|
-
kind: z.literal("hello"),
|
|
1741
|
-
workspaceId: z.string(),
|
|
1742
|
-
routes: z.array(z.string()).optional(),
|
|
1743
|
-
shapes: z.record(z.string(), z.string()).optional(),
|
|
1744
|
-
build: z.string().optional(),
|
|
1745
|
-
boot: BootProgressSchema.optional(),
|
|
1746
|
-
});
|
|
1747
|
-
export type Hello = z.infer<typeof HelloSchema>;
|
|
1748
|
-
|
|
1749
|
-
// The FULL discovered repo set (sorted root-relative ids), pushed whenever it changes, a clone, a scaffold,
|
|
1750
|
-
// or a deleted repo re-frames it. The watcher descent-ignores .git, so no workspaceChanged path pattern can
|
|
1751
|
-
// detect a repo appearing; the daemon diffs its own discovery instead. Snapshot-not-diff, last frame wins.
|
|
1752
|
-
export const ReposChangedSchema = z.object({ kind: z.literal("reposChanged"), repos: z.array(z.string()) });
|
|
1753
|
-
export type ReposChanged = z.infer<typeof ReposChangedSchema>;
|
|
1754
|
-
|
|
1755
|
-
// A batch of workspace paths that just changed on disk (created/edited/deleted), pushed on the same /events
|
|
1756
|
-
// stream as the heartbeat so the browser refreshes the tree + any open file live, the agent edits files
|
|
1757
|
-
// out-of-band (its own Write/Edit/Bash tools), so there's no HTTP mutation to hang an invalidate on. Paths are
|
|
1758
|
-
// root-relative, forward-slash (the tree/file route space). An empty array means "something changed, refetch the
|
|
1759
|
-
// tree", a burst too large to enumerate, or a reconnect recovery where we don't know what was missed.
|
|
1760
|
-
export const WorkspaceChangedSchema = z.object({ kind: z.literal("workspaceChanged"), paths: z.array(z.string()) });
|
|
1761
|
-
export type WorkspaceChanged = z.infer<typeof WorkspaceChangedSchema>;
|
|
1762
|
-
|
|
1763
|
-
/* THE REPOS WHOSE REFS JUST MOVED, a commit, a checkout, a branch or tag, a rebase started or aborted.
|
|
1764
|
-
*
|
|
1765
|
-
* A third push for the same reason as the two above, and the reason is structural: a repo's git dir does not
|
|
1766
|
-
* live under /work at all (it is relocated onto /history so an isolated turn's worktree can stand in for the
|
|
1767
|
-
* workspace root, see git/repo-git-dirs.ts), and the file watcher descent-ignores `.git` besides. So no
|
|
1768
|
-
* `workspaceChanged` path can ever say "a ref moved", and a surface built on the commit graph would otherwise
|
|
1769
|
-
* be exactly as fresh as the last thing the user clicked.
|
|
1770
|
-
*
|
|
1771
|
-
* It matters most for the work the user did NOT do: the agent commits, rebases and lands out-of-band, with no
|
|
1772
|
-
* HTTP mutation in any browser to hang an invalidation on. Ids are root-relative, "root" being the /work repo.
|
|
1773
|
-
* Diff-not-snapshot, unlike reposChanged: this names what moved, and a repo absent from a frame is a repo that
|
|
1774
|
-
* did not move rather than one that stopped existing. */
|
|
1775
|
-
export const RefsChangedSchema = z.object({ kind: z.literal("refsChanged"), repos: z.array(z.string()) });
|
|
1776
|
-
export type RefsChanged = z.infer<typeof RefsChangedSchema>;
|
|
1777
|
-
|
|
1778
|
-
/* WHICH RUNNING THINGS JUST MOVED, a session opened or exited, a dev server bound its port, a browser closed,
|
|
1779
|
-
* a subagent reported in.
|
|
1780
|
-
*
|
|
1781
|
-
* The fourth push, and the one that covers what the other three structurally cannot: none of this state is on
|
|
1782
|
-
* disk, so no `workspaceChanged` path can name it, and none of it is a ref or a repo. Before it, every view of a
|
|
1783
|
-
* running thing polled on its own timer, which is to say each browser asked, forever, a question only the
|
|
1784
|
-
* daemon could answer and almost always answered "no change".
|
|
1785
|
-
*
|
|
1786
|
-
* Diff-not-snapshot, and deliberately thin: the frame carries the DOMAIN that moved, never the roster itself.
|
|
1787
|
-
* Invalidation only reaches a query something is observing, so a tab showing none of these pays a frame and no
|
|
1788
|
-
* request, whereas a roster on the wire would bill every connected browser the full list whether or not
|
|
1789
|
-
* anything on screen renders it. Which query keys a domain stands for is runtime-state.ts's table. */
|
|
1790
|
-
export const RuntimeChangedSchema = z.object({ kind: z.literal("runtimeChanged"), domains: z.array(z.string()) });
|
|
1791
|
-
export type RuntimeChanged = z.infer<typeof RuntimeChangedSchema>;
|
|
1792
|
-
|
|
1793
|
-
// One connected browser tab of a sandbox member. Identity fields come from the caller's verified Google ID
|
|
1794
|
-
// token; activity fields from the tab's own /system/presence reports. No timestamps on the wire, an entry's
|
|
1795
|
-
// lifetime IS its /events connection's lifetime, so there is nothing to age out or compare clocks over.
|
|
1796
|
-
export const PresenceUserSchema = z.object({
|
|
1797
|
-
// Per-CONNECTION id, minted by the browser for each /events attempt, never reused across reconnects.
|
|
1798
|
-
clientId: z.string(),
|
|
1799
|
-
email: z.string(),
|
|
1800
|
-
name: z.string().optional(),
|
|
1801
|
-
picture: z.string().optional(),
|
|
1802
|
-
// The caller's trust tier, resolved by the authorizer at connection time. On the roster so every member
|
|
1803
|
-
// can see who may do what, and so a tab knows its OWN role without an owner-only lookup.
|
|
1804
|
-
role: MemberRoleSchema,
|
|
1805
|
-
idle: z.boolean(),
|
|
1806
|
-
// Route/view name the tab is on ("workspace", "automations", "ext:<id>/<key>", …).
|
|
1807
|
-
view: z.string().optional(),
|
|
1808
|
-
// The chat conversation the tab has active.
|
|
1809
|
-
sessionId: z.string().optional(),
|
|
1810
|
-
// The workspace file the tab has open (root-relative, forward-slash).
|
|
1811
|
-
path: z.string().optional(),
|
|
1812
|
-
});
|
|
1813
|
-
export type PresenceUser = z.infer<typeof PresenceUserSchema>;
|
|
1814
|
-
|
|
1815
|
-
// The FULL roster of connected members, broadcast on every change, snapshots, not diffs, so a reconnecting
|
|
1816
|
-
// browser is consistent from its first frame and ordering never matters (last frame wins).
|
|
1817
|
-
export const PresenceSchema = z.object({ kind: z.literal("presence"), users: z.array(PresenceUserSchema) });
|
|
1818
|
-
export type Presence = z.infer<typeof PresenceSchema>;
|
|
1819
|
-
|
|
1820
|
-
// The FULL fleet roster, broadcast on every registry change, same snapshot-not-diff contract as presence:
|
|
1821
|
-
// a reconnecting browser is consistent from its first frame. NOT simply "last frame wins", though: `rev` is the
|
|
1822
|
-
// registry revision the snapshot was taken at, and the browser applies a frame only if it is newer than the one
|
|
1823
|
-
// it already holds. Snapshots race two other sources of the same fact, an explicit GET /agents and the
|
|
1824
|
-
// browser's own optimistic writes, and an unordered full replace lets the slowest of them win, which is how an
|
|
1825
|
-
// archived card came back. See AgentsListSchema and useAgents.ts.
|
|
1826
|
-
export const AgentsSchema = z.object({ kind: z.literal("agents"), agents: z.array(AgentSummarySchema), rev: z.number() });
|
|
1827
|
-
export type Agents = z.infer<typeof AgentsSchema>;
|
|
1828
|
-
|
|
1829
|
-
/* AN ACCOUNT'S HEADROOM JUST MOVED, the reading itself, keyed the way the daemon's store keys it (a Claude
|
|
1830
|
-
* account id, or `${provider}:${authFile}` for a routed subscription).
|
|
1831
|
-
*
|
|
1832
|
-
* The fifth push, and the one that lets every ring, rail and picker row stop refetching on mount. A reading
|
|
1833
|
-
* lands on the daemon for one of four reasons, a turn settled, a plan refused, a screen asked, a provider
|
|
1834
|
-
* pushed, and until this frame existed only the window that caused it ever heard: every other window drew the
|
|
1835
|
-
* number it had loaded that morning until something in it happened to remount. Snapshot-not-diff per account,
|
|
1836
|
-
* last frame wins, and a browser that missed one simply holds the older reading, which is what `measuredAt`
|
|
1837
|
-
* is for. `usage` absent ⇒ the account's snapshot was cleared (it was disconnected). */
|
|
1838
|
-
export const AccountUsageChangedSchema = z.object({
|
|
1839
|
-
kind: z.literal("accountUsage"),
|
|
1840
|
-
// The provider whose row this account is, because the key alone does not say (a native id is bare).
|
|
1841
|
-
provider: z.string(),
|
|
1842
|
-
account: z.string(),
|
|
1843
|
-
usage: AccountUsageSchema.optional(),
|
|
1844
|
-
});
|
|
1845
|
-
export type AccountUsageChanged = z.infer<typeof AccountUsageChangedSchema>;
|
|
1846
|
-
|
|
1847
|
-
// A provider's last refusal was recorded or settled. The observed half of "can I run on this" (see
|
|
1848
|
-
// ProviderRefusalSchema), pushed for the same reason the reading above is: a refusal at 4am used to reach a
|
|
1849
|
-
// window only when it next reloaded its account rows. `refusal` absent ⇒ settled, nothing stands.
|
|
1850
|
-
export const ProviderRefusalChangedSchema = z.object({ kind: z.literal("providerRefusal"), provider: z.string(), refusal: ProviderRefusalSchema.optional() });
|
|
1851
|
-
export type ProviderRefusalChanged = z.infer<typeof ProviderRefusalChangedSchema>;
|
|
1852
|
-
|
|
1853
|
-
// The /events stream union: the hello identity frame, then liveness heartbeats interleaved with boot progress,
|
|
1854
|
-
// workspace-change batches, repo-set snapshots, ref-move batches, runtime-domain nudges, presence + fleet
|
|
1855
|
-
// roster snapshots, and account headroom / refusal changes. oRPC validates every yielded frame against this,
|
|
1856
|
-
// so all kinds must live here.
|
|
1857
|
-
export const SystemEventSchema = z.discriminatedUnion("kind", [
|
|
1858
|
-
HelloSchema,
|
|
1859
|
-
HeartbeatSchema,
|
|
1860
|
-
BootSchema,
|
|
1861
|
-
WorkspaceChangedSchema,
|
|
1862
|
-
ReposChangedSchema,
|
|
1863
|
-
RefsChangedSchema,
|
|
1864
|
-
RuntimeChangedSchema,
|
|
1865
|
-
PresenceSchema,
|
|
1866
|
-
AgentsSchema,
|
|
1867
|
-
AccountUsageChangedSchema,
|
|
1868
|
-
ProviderRefusalChangedSchema,
|
|
1869
|
-
]);
|
|
1870
|
-
export type SystemEvent = z.infer<typeof SystemEventSchema>;
|