@intentic/sandbox-contract 1.231.0 → 1.233.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 +13 -2
- package/dist/agent-catalog.d.ts +2 -1
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +1 -1
- package/dist/agent-catalog.js.map +1 -1
- package/dist/chores/chores.d.ts +1 -1
- package/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/probes.d.ts +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/stack.d.ts.map +1 -1
- package/dist/chores/stack.js +2 -1
- package/dist/chores/stack.js.map +1 -1
- package/dist/chores/verdict.d.ts +1 -1
- package/dist/chores/verdict.d.ts.map +1 -1
- package/dist/command-classes.d.ts +14 -0
- package/dist/command-classes.d.ts.map +1 -0
- package/dist/command-classes.js +168 -0
- package/dist/command-classes.js.map +1 -0
- package/dist/contracts/activity.contract.js +1 -1
- package/dist/contracts/activity.contract.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +51 -2
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +13 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +24 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +5 -1
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/automations.contract.js +2 -1
- package/dist/contracts/automations.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +63 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +3 -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 +2 -1
- package/dist/contracts/chores.contract.js.map +1 -1
- package/dist/contracts/ci.contract.js +2 -1
- package/dist/contracts/ci.contract.js.map +1 -1
- package/dist/contracts/claude.contract.js +2 -1
- package/dist/contracts/claude.contract.js.map +1 -1
- package/dist/contracts/cursor.contract.js +2 -1
- package/dist/contracts/cursor.contract.js.map +1 -1
- package/dist/contracts/drafts.contract.d.ts.map +1 -1
- package/dist/contracts/drafts.contract.js +2 -1
- package/dist/contracts/drafts.contract.js.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js +2 -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 +2 -1
- package/dist/contracts/exit.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +42 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js +4 -1
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.js +4 -1
- package/dist/contracts/git.contract.js.map +1 -1
- package/dist/contracts/grok.contract.d.ts.map +1 -1
- package/dist/contracts/grok.contract.js +2 -1
- package/dist/contracts/grok.contract.js.map +1 -1
- package/dist/contracts/history.contract.d.ts.map +1 -1
- package/dist/contracts/history.contract.js +2 -1
- package/dist/contracts/history.contract.js.map +1 -1
- package/dist/contracts/host.contract.d.ts +11 -0
- package/dist/contracts/host.contract.d.ts.map +1 -1
- package/dist/contracts/host.contract.js +4 -1
- package/dist/contracts/host.contract.js.map +1 -1
- package/dist/contracts/intentic.contract.d.ts.map +1 -1
- package/dist/contracts/intentic.contract.js +2 -1
- package/dist/contracts/intentic.contract.js.map +1 -1
- package/dist/contracts/inventory.contract.js +1 -1
- package/dist/contracts/inventory.contract.js.map +1 -1
- package/dist/contracts/logs.contract.d.ts.map +1 -1
- package/dist/contracts/logs.contract.js +1 -1
- package/dist/contracts/logs.contract.js.map +1 -1
- package/dist/contracts/loops.contract.js +2 -1
- package/dist/contracts/loops.contract.js.map +1 -1
- package/dist/contracts/panels.contract.d.ts.map +1 -1
- package/dist/contracts/panels.contract.js +2 -1
- package/dist/contracts/panels.contract.js.map +1 -1
- package/dist/contracts/personas.contract.js +2 -1
- package/dist/contracts/personas.contract.js.map +1 -1
- package/dist/contracts/ports.contract.d.ts.map +1 -1
- package/dist/contracts/ports.contract.js +2 -1
- package/dist/contracts/ports.contract.js.map +1 -1
- package/dist/contracts/prepush.contract.d.ts.map +1 -1
- package/dist/contracts/prepush.contract.js +2 -1
- package/dist/contracts/prepush.contract.js.map +1 -1
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.js +2 -1
- package/dist/contracts/providers.contract.js.map +1 -1
- package/dist/contracts/public.contract.d.ts.map +1 -1
- package/dist/contracts/public.contract.js +2 -1
- package/dist/contracts/public.contract.js.map +1 -1
- package/dist/contracts/push.contract.d.ts.map +1 -1
- package/dist/contracts/push.contract.js +2 -1
- package/dist/contracts/push.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +151 -43
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +14 -1
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +2 -1
- package/dist/contracts/secrets.contract.js.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 +20 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +3 -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 +2 -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 +2 -1
- package/dist/contracts/skills.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +35 -16
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +6 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.js +4 -1
- package/dist/contracts/translator.contract.js.map +1 -1
- package/dist/contracts/usage.contract.js +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 +2 -1
- package/dist/contracts/vpn.contract.js.map +1 -1
- package/dist/contracts/webext.contract.d.ts +45 -0
- package/dist/contracts/webext.contract.d.ts.map +1 -0
- package/dist/contracts/webext.contract.js +12 -0
- package/dist/contracts/webext.contract.js.map +1 -0
- package/dist/contracts/workflows.contract.js +2 -1
- package/dist/contracts/workflows.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +2 -2
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +7 -1
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/definition-action.d.ts +7 -0
- package/dist/definition-action.d.ts.map +1 -0
- package/dist/definition-action.js +3 -0
- package/dist/definition-action.js.map +1 -0
- package/dist/definition.d.ts +1064 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +93 -0
- package/dist/definition.js.map +1 -0
- package/dist/events.d.ts +109 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +30 -1
- package/dist/events.js.map +1 -1
- package/dist/fast-tier.d.ts +1 -1
- package/dist/fast-tier.d.ts.map +1 -1
- package/dist/history-state.d.ts.map +1 -1
- package/dist/history-state.js +1 -0
- package/dist/history-state.js.map +1 -1
- package/dist/index.d.ts +371 -103
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -1
- package/dist/index.js.map +1 -1
- package/dist/listener-protocol.js +1 -1
- package/dist/listener-protocol.js.map +1 -1
- package/dist/quick-model.d.ts +1 -1
- package/dist/quick-model.d.ts.map +1 -1
- package/dist/runner-protocol.d.ts +18 -0
- package/dist/runner-protocol.d.ts.map +1 -1
- package/dist/runner-protocol.js +8 -4
- package/dist/runner-protocol.js.map +1 -1
- package/dist/schemas/activity.d.ts +112 -0
- package/dist/schemas/activity.d.ts.map +1 -0
- package/dist/schemas/activity.js +70 -0
- package/dist/schemas/activity.js.map +1 -0
- package/dist/schemas/agent.d.ts +189 -0
- package/dist/schemas/agent.d.ts.map +1 -0
- package/dist/schemas/agent.js +170 -0
- package/dist/schemas/agent.js.map +1 -0
- package/dist/schemas/agents.d.ts +690 -0
- package/dist/schemas/agents.d.ts.map +1 -0
- package/dist/schemas/agents.js +314 -0
- package/dist/schemas/agents.js.map +1 -0
- package/dist/schemas/automations.d.ts +736 -0
- package/dist/schemas/automations.d.ts.map +1 -0
- package/dist/schemas/automations.js +218 -0
- package/dist/schemas/automations.js.map +1 -0
- package/dist/schemas/capabilities.d.ts +753 -0
- package/dist/schemas/capabilities.d.ts.map +1 -0
- package/dist/schemas/capabilities.js +269 -0
- package/dist/schemas/capabilities.js.map +1 -0
- package/dist/schemas/ci.d.ts +179 -0
- package/dist/schemas/ci.d.ts.map +1 -0
- package/dist/schemas/ci.js +99 -0
- package/dist/schemas/ci.js.map +1 -0
- package/dist/schemas/claude-gate.d.ts +13 -0
- package/dist/schemas/claude-gate.d.ts.map +1 -0
- package/dist/schemas/claude-gate.js +8 -0
- package/dist/schemas/claude-gate.js.map +1 -0
- package/dist/schemas/codebase-health.d.ts +56 -0
- package/dist/schemas/codebase-health.d.ts.map +1 -0
- package/dist/schemas/codebase-health.js +43 -0
- package/dist/schemas/codebase-health.js.map +1 -0
- package/dist/schemas/computers.d.ts +386 -0
- package/dist/schemas/computers.d.ts.map +1 -0
- package/dist/schemas/computers.js +107 -0
- package/dist/schemas/computers.js.map +1 -0
- package/dist/schemas/drafts.d.ts +84 -0
- package/dist/schemas/drafts.d.ts.map +1 -0
- package/dist/schemas/drafts.js +44 -0
- package/dist/schemas/drafts.js.map +1 -0
- package/dist/schemas/environment.d.ts +349 -0
- package/dist/schemas/environment.d.ts.map +1 -0
- package/dist/schemas/environment.js +111 -0
- package/dist/schemas/environment.js.map +1 -0
- package/dist/schemas/exit.d.ts +151 -0
- package/dist/schemas/exit.d.ts.map +1 -0
- package/dist/schemas/exit.js +115 -0
- package/dist/schemas/exit.js.map +1 -0
- package/dist/schemas/extension-updates.d.ts +1014 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -0
- package/dist/schemas/extension-updates.js +197 -0
- package/dist/schemas/extension-updates.js.map +1 -0
- package/dist/schemas/fast-mode.d.ts +8 -0
- package/dist/schemas/fast-mode.d.ts.map +1 -0
- package/dist/schemas/fast-mode.js +3 -0
- package/dist/schemas/fast-mode.js.map +1 -0
- package/dist/schemas/git-history.d.ts +238 -0
- package/dist/schemas/git-history.d.ts.map +1 -0
- package/dist/schemas/git-history.js +152 -0
- package/dist/schemas/git-history.js.map +1 -0
- package/dist/schemas/git.d.ts +471 -0
- package/dist/schemas/git.d.ts.map +1 -0
- package/dist/schemas/git.js +204 -0
- package/dist/schemas/git.js.map +1 -0
- package/dist/schemas/history.d.ts +100 -0
- package/dist/schemas/history.d.ts.map +1 -0
- package/dist/schemas/history.js +54 -0
- package/dist/schemas/history.js.map +1 -0
- package/dist/schemas/hosts.d.ts +41 -0
- package/dist/schemas/hosts.d.ts.map +1 -0
- package/dist/schemas/hosts.js +18 -0
- package/dist/schemas/hosts.js.map +1 -0
- package/dist/schemas/intentic.d.ts +5 -0
- package/dist/schemas/intentic.d.ts.map +1 -0
- package/dist/schemas/intentic.js +3 -0
- package/dist/schemas/intentic.js.map +1 -0
- package/dist/schemas/internal.d.ts +8 -0
- package/dist/schemas/internal.d.ts.map +1 -0
- package/dist/schemas/internal.js +12 -0
- package/dist/schemas/internal.js.map +1 -0
- package/dist/schemas/inventory.d.ts +171 -0
- package/dist/schemas/inventory.d.ts.map +1 -0
- package/dist/schemas/inventory.js +51 -0
- package/dist/schemas/inventory.js.map +1 -0
- package/dist/schemas/logs.d.ts +67 -0
- package/dist/schemas/logs.d.ts.map +1 -0
- package/dist/schemas/logs.js +54 -0
- package/dist/schemas/logs.js.map +1 -0
- package/dist/schemas/loops.d.ts +415 -0
- package/dist/schemas/loops.d.ts.map +1 -0
- package/dist/schemas/loops.js +188 -0
- package/dist/schemas/loops.js.map +1 -0
- package/dist/schemas/maintenance.d.ts +557 -0
- package/dist/schemas/maintenance.d.ts.map +1 -0
- package/dist/schemas/maintenance.js +201 -0
- package/dist/schemas/maintenance.js.map +1 -0
- package/dist/schemas/marketplace.d.ts +64 -0
- package/dist/schemas/marketplace.d.ts.map +1 -0
- package/dist/schemas/marketplace.js +17 -0
- package/dist/schemas/marketplace.js.map +1 -0
- package/dist/schemas/panels.d.ts +61 -0
- package/dist/schemas/panels.d.ts.map +1 -0
- package/dist/schemas/panels.js +51 -0
- package/dist/schemas/panels.js.map +1 -0
- package/dist/schemas/personas.d.ts +122 -0
- package/dist/schemas/personas.d.ts.map +1 -0
- package/dist/schemas/personas.js +102 -0
- package/dist/schemas/personas.js.map +1 -0
- package/dist/schemas/plan-limits.d.ts +172 -0
- package/dist/schemas/plan-limits.d.ts.map +1 -0
- package/dist/schemas/plan-limits.js +126 -0
- package/dist/schemas/plan-limits.js.map +1 -0
- package/dist/schemas/ports.d.ts +71 -0
- package/dist/schemas/ports.d.ts.map +1 -0
- package/dist/schemas/ports.js +42 -0
- package/dist/schemas/ports.js.map +1 -0
- package/dist/schemas/provider-oauth.d.ts +128 -0
- package/dist/schemas/provider-oauth.d.ts.map +1 -0
- package/dist/schemas/provider-oauth.js +91 -0
- package/dist/schemas/provider-oauth.js.map +1 -0
- package/dist/schemas/provider-subscriptions.d.ts +9 -0
- package/dist/schemas/provider-subscriptions.d.ts.map +1 -0
- package/dist/schemas/provider-subscriptions.js +3 -0
- package/dist/schemas/provider-subscriptions.js.map +1 -0
- package/dist/schemas/public.d.ts +32 -0
- package/dist/schemas/public.d.ts.map +1 -0
- package/dist/schemas/public.js +29 -0
- package/dist/schemas/public.js.map +1 -0
- package/dist/schemas/push.d.ts +55 -0
- package/dist/schemas/push.d.ts.map +1 -0
- package/dist/schemas/push.js +60 -0
- package/dist/schemas/push.js.map +1 -0
- package/dist/schemas/secrets.d.ts +86 -0
- package/dist/schemas/secrets.d.ts.map +1 -0
- package/dist/schemas/secrets.js +51 -0
- package/dist/schemas/secrets.js.map +1 -0
- package/dist/schemas/sessions.d.ts +32 -0
- package/dist/schemas/sessions.d.ts.map +1 -0
- package/dist/schemas/sessions.js +11 -0
- package/dist/schemas/sessions.js.map +1 -0
- package/dist/schemas/settings.d.ts +548 -0
- package/dist/schemas/settings.d.ts.map +1 -0
- package/dist/schemas/settings.js +259 -0
- package/dist/schemas/settings.js.map +1 -0
- package/dist/schemas/share.d.ts +49 -0
- package/dist/schemas/share.d.ts.map +1 -0
- package/dist/schemas/share.js +26 -0
- package/dist/schemas/share.js.map +1 -0
- package/dist/schemas/shared.d.ts +22 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +16 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/system.d.ts +86 -0
- package/dist/schemas/system.d.ts.map +1 -0
- package/dist/schemas/system.js +64 -0
- package/dist/schemas/system.js.map +1 -0
- package/dist/schemas/terminal.d.ts +230 -0
- package/dist/schemas/terminal.d.ts.map +1 -0
- package/dist/schemas/terminal.js +133 -0
- package/dist/schemas/terminal.js.map +1 -0
- package/dist/schemas/usage.d.ts +113 -0
- package/dist/schemas/usage.d.ts.map +1 -0
- package/dist/schemas/usage.js +91 -0
- package/dist/schemas/usage.js.map +1 -0
- package/dist/schemas/vpn.d.ts +229 -0
- package/dist/schemas/vpn.d.ts.map +1 -0
- package/dist/schemas/vpn.js +115 -0
- package/dist/schemas/vpn.js.map +1 -0
- package/dist/schemas/webext.d.ts +98 -0
- package/dist/schemas/webext.d.ts.map +1 -0
- package/dist/schemas/webext.js +36 -0
- package/dist/schemas/webext.js.map +1 -0
- package/dist/schemas/workflows.d.ts +881 -0
- package/dist/schemas/workflows.d.ts.map +1 -0
- package/dist/schemas/workflows.js +164 -0
- package/dist/schemas/workflows.js.map +1 -0
- package/dist/schemas/workspace-repos.d.ts +143 -0
- package/dist/schemas/workspace-repos.d.ts.map +1 -0
- package/dist/schemas/workspace-repos.js +87 -0
- package/dist/schemas/workspace-repos.js.map +1 -0
- package/dist/schemas/workspace-search.d.ts +170 -0
- package/dist/schemas/workspace-search.d.ts.map +1 -0
- package/dist/schemas/workspace-search.js +85 -0
- package/dist/schemas/workspace-search.js.map +1 -0
- package/dist/schemas/workspace-setup.d.ts +48 -0
- package/dist/schemas/workspace-setup.d.ts.map +1 -0
- package/dist/schemas/workspace-setup.js +26 -0
- package/dist/schemas/workspace-setup.js.map +1 -0
- package/dist/schemas/workspace-tree.d.ts +120 -0
- package/dist/schemas/workspace-tree.d.ts.map +1 -0
- package/dist/schemas/workspace-tree.js +111 -0
- package/dist/schemas/workspace-tree.js.map +1 -0
- package/dist/webext-links.d.ts +13 -0
- package/dist/webext-links.d.ts.map +1 -0
- package/dist/webext-links.js +24 -0
- package/dist/webext-links.js.map +1 -0
- package/dist/webext-protocol.d.ts +8 -0
- package/dist/webext-protocol.d.ts.map +1 -0
- package/dist/webext-protocol.js +7 -0
- package/dist/webext-protocol.js.map +1 -0
- package/dist/workflow-faults.d.ts +1 -1
- package/dist/workflow-faults.d.ts.map +1 -1
- package/dist/workspace-state.d.ts +11 -2
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +4 -2
- package/dist/workspace-state.js.map +1 -1
- package/package.json +17 -6
- package/src/agent-catalog.test.ts +1 -1
- package/src/agent-catalog.ts +4 -11
- package/src/chores/chores.ts +1 -1
- package/src/chores/probes.ts +1 -1
- package/src/chores/stack.test.ts +19 -0
- package/src/chores/stack.ts +19 -3
- package/src/chores/verdict.test.ts +11 -1
- package/src/chores/verdict.ts +1 -1
- package/src/command-classes.test.ts +378 -0
- package/src/command-classes.ts +378 -0
- package/src/contracts/activity.contract.ts +1 -1
- package/src/contracts/agent.contract.ts +22 -12
- package/src/contracts/agents.contract.ts +6 -6
- package/src/contracts/automations.contract.ts +2 -2
- package/src/contracts/capabilities.contract.ts +3 -4
- package/src/contracts/chores.contract.ts +2 -1
- package/src/contracts/ci.contract.ts +2 -2
- package/src/contracts/claude.contract.ts +2 -2
- package/src/contracts/cursor.contract.ts +2 -2
- package/src/contracts/drafts.contract.ts +2 -1
- package/src/contracts/endpoints.contract.ts +2 -1
- package/src/contracts/exit.contract.ts +2 -1
- package/src/contracts/extensions.contract.ts +5 -5
- package/src/contracts/git.contract.ts +19 -18
- package/src/contracts/grok.contract.ts +2 -1
- package/src/contracts/history.contract.ts +2 -1
- package/src/contracts/host.contract.ts +4 -1
- package/src/contracts/intentic.contract.ts +2 -1
- package/src/contracts/inventory.contract.ts +1 -1
- package/src/contracts/logs.contract.ts +7 -1
- package/src/contracts/loops.contract.ts +2 -2
- package/src/contracts/panels.contract.ts +2 -1
- package/src/contracts/personas.contract.ts +3 -3
- package/src/contracts/ports.contract.ts +3 -2
- package/src/contracts/prepush.contract.ts +2 -1
- package/src/contracts/providers.contract.ts +2 -1
- package/src/contracts/public.contract.ts +3 -2
- package/src/contracts/push.contract.ts +2 -1
- package/src/contracts/runner.contract.ts +33 -1
- package/src/contracts/secrets.contract.ts +2 -1
- package/src/contracts/sessions.contract.ts +1 -1
- package/src/contracts/settings.contract.ts +3 -9
- package/src/contracts/share.contract.ts +3 -2
- package/src/contracts/skills.contract.ts +2 -1
- package/src/contracts/system.contract.ts +6 -9
- package/src/contracts/translator.contract.ts +4 -1
- package/src/contracts/usage.contract.ts +1 -1
- package/src/contracts/vpn.contract.ts +2 -8
- package/src/contracts/webext.contract.ts +37 -0
- package/src/contracts/workflows.contract.ts +2 -2
- package/src/contracts/workspace.contract.ts +10 -12
- package/src/conversation-ids.test.ts +1 -1
- package/src/definition-action.ts +8 -0
- package/src/definition.ts +235 -0
- package/src/events.ts +128 -22
- package/src/fast-tier.ts +1 -1
- package/src/history-state.ts +5 -0
- package/src/host-protocol.ts +1 -1
- package/src/hostnames.test.ts +2 -2
- package/src/index.ts +61 -1
- package/src/listener-protocol.ts +1 -1
- package/src/quick-model.ts +1 -1
- package/src/runner-protocol.ts +35 -5
- package/src/schemas/activity.ts +123 -0
- package/src/schemas/agent.ts +409 -0
- package/src/schemas/agents.ts +802 -0
- package/src/schemas/automations.ts +510 -0
- package/src/schemas/capabilities.ts +729 -0
- package/src/schemas/ci.ts +235 -0
- package/src/schemas/claude-gate.ts +13 -0
- package/src/schemas/codebase-health.ts +64 -0
- package/src/schemas/computers.ts +287 -0
- package/src/schemas/drafts.ts +108 -0
- package/src/schemas/environment.ts +276 -0
- package/src/schemas/exit.ts +222 -0
- package/src/schemas/extension-updates.ts +322 -0
- package/src/schemas/fast-mode.ts +8 -0
- package/src/schemas/git-history.ts +265 -0
- package/src/schemas/git.ts +430 -0
- package/src/schemas/history.ts +112 -0
- package/src/schemas/hosts.ts +37 -0
- package/src/schemas/intentic.ts +2 -0
- package/src/schemas/internal.ts +17 -0
- package/src/schemas/inventory.ts +68 -0
- package/src/schemas/logs.ts +96 -0
- package/src/schemas/loops.ts +401 -0
- package/src/schemas/maintenance.ts +421 -0
- package/src/schemas/marketplace.ts +23 -0
- package/src/schemas/panels.ts +99 -0
- package/src/schemas/personas.ts +281 -0
- package/src/schemas/plan-limits.ts +255 -0
- package/src/schemas/ports.ts +84 -0
- package/src/schemas/provider-oauth.ts +191 -0
- package/src/schemas/provider-subscriptions.ts +11 -0
- package/src/schemas/public.ts +52 -0
- package/src/schemas/push.ts +112 -0
- package/src/schemas/secrets.ts +68 -0
- package/src/schemas/sessions.ts +16 -0
- package/src/schemas/settings.ts +932 -0
- package/src/schemas/share.ts +71 -0
- package/src/schemas/shared.ts +32 -0
- package/src/schemas/system.ts +156 -0
- package/src/schemas/terminal.ts +286 -0
- package/src/schemas/usage.ts +303 -0
- package/src/{schemas.test.ts → schemas/version-seam.test.ts} +4 -2
- package/src/schemas/vpn.ts +215 -0
- package/src/schemas/webext.ts +79 -0
- package/src/schemas/workflows.ts +413 -0
- package/src/schemas/workspace-repos.ts +123 -0
- package/src/schemas/workspace-search.ts +141 -0
- package/src/schemas/workspace-setup.ts +43 -0
- package/src/schemas/workspace-tree.ts +234 -0
- package/src/title.test.ts +1 -1
- package/src/webext-links.ts +80 -0
- package/src/webext-protocol.ts +27 -0
- package/src/workflow-faults.test.ts +1 -1
- package/src/workflow-faults.ts +1 -1
- package/src/workspace-state.test.ts +7 -3
- package/src/workspace-state.ts +27 -13
- package/dist/schemas.d.ts +0 -9800
- package/dist/schemas.d.ts.map +0 -1
- package/dist/schemas.js +0 -4252
- package/dist/schemas.js.map +0 -1
- package/src/schemas.ts +0 -9999
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Personas: the named faces a sandbox shows the outside world — which accounts each speaks for, what a
|
|
2
|
+
// session wearing one may do, and where it works.
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { entryId } from "./internal.js";
|
|
5
|
+
import { SkillDraftSchema, SkillNameSchema, SystemPromptModeSchema } from "./settings.js";
|
|
6
|
+
/* A NAMED PERSONA THE SANDBOX SHOWS THE OUTSIDE WORLD, "work-reddit", "the studio account", and the layer
|
|
7
|
+
* that decides which connected accounts a given turn may act through.
|
|
8
|
+
*
|
|
9
|
+
* IT ANSWERS FOUR QUESTIONS AND NO MORE: who it speaks as, what it may do, where it works, and what it is told.
|
|
10
|
+
* Making one is then a name, a few accounts, some switches and, only if you want one, a prompt. That is the
|
|
11
|
+
* whole of what an owner is deciding, and short enough that they finish.
|
|
12
|
+
*
|
|
13
|
+
* NO PUBLISH-OR-DRAFT SWITCH. It read as a lock and was a sentence: it asked the turn to route outward things
|
|
14
|
+
* through the approvals queue and could not stop it posting. The queue is the mechanism, and a control whose
|
|
15
|
+
* label promises more than it delivers is worse than no control, it is the one an owner trusts.
|
|
16
|
+
*
|
|
17
|
+
* THE FOURTH QUESTION IS THE SYSTEM PROMPT, NOT A TONE NOTE, and the difference is why the field that used to
|
|
18
|
+
* sit here was removed and this one is not it. What was removed was a paragraph on how a persona WRITES:
|
|
19
|
+
* optional, answered by almost nobody, and shaping nothing a person could see afterwards. `systemPromptMode`
|
|
20
|
+
* is the same setting the sandbox has, asked per card, it replaces the whole prompt, and with the kit folder
|
|
21
|
+
* beside it (persona-kit.ts) a card can carry its own skills and tools too. That is a persona being a working
|
|
22
|
+
* posture rather than a label, and it shows: a release-notes writer and a code reviewer are two prompts, not
|
|
23
|
+
* two adjectives.
|
|
24
|
+
*
|
|
25
|
+
* THE CARD AND THE KEYS ARE DELIBERATELY SEPARATE. This is the card: a name, the accounts it speaks for, what a
|
|
26
|
+
* session wearing it may do, where it works. It carries NO credential, which is what lets it be the one thing under
|
|
27
|
+
* .intentic that is committed and reviewed like the workspace's instructions are (see personas-store.ts for
|
|
28
|
+
* the exclude carve-out that makes that true). The keys, the logged-in browser profile, its cookies, its
|
|
29
|
+
* passkey, stay where they already are: private to the sandbox, never exported without an explicit opt-in. So a
|
|
30
|
+
* cloned workspace arrives listing its personas, each visibly unconnected, waiting for one sign-in apiece.
|
|
31
|
+
*
|
|
32
|
+
* WHAT IT IS NOT is a security boundary. A chat still reaches every connected account by default (that is the
|
|
33
|
+
* owner's chosen posture, a chat has a human in the room), and an agent with a shell can reach a token whatever
|
|
34
|
+
* this file says. What it prevents is the mistake this codebase already names as the one that cannot be undone:
|
|
35
|
+
* a post from the wrong account. Where nobody is watching, an unattended wake, it is a real fence, because
|
|
36
|
+
* there the resolver's default is NOTHING rather than everything (see turnPersona in personas.ts). */
|
|
37
|
+
/* WHAT A PERSONA MAY DO, the shelves, one switch each, and the half of the card that bounds the turn rather
|
|
38
|
+
* than the account it speaks for.
|
|
39
|
+
*
|
|
40
|
+
* SHELVES, NOT TOOL NAMES. Every field here is a phrase a person decides about ("run commands", "read the
|
|
41
|
+
* web"), never the name of a tool. Tool names drift with every runtime upgrade, one power answers to several of
|
|
42
|
+
* them, and a connector is not a tool at all, it is a shell command plus a credential. Naming the shelf means
|
|
43
|
+
* a tool added next month lands inside an answer the owner already gave, and a card written today still means
|
|
44
|
+
* what it said after the SDK renames something.
|
|
45
|
+
*
|
|
46
|
+
* TWO STRENGTHS, AND THE DIFFERENCE IS VISIBLE FROM HERE. Everything capability-shaped (`connectors`,
|
|
47
|
+
* `computers`, `mcp`, and the accounts in `capabilities`) is enforced by ABSENCE, the credential is never
|
|
48
|
+
* injected, the server never mounted, the browser never launched, which is the same mechanism the account
|
|
49
|
+
* filter already uses and needs no cooperation from the model. The plain switches are enforced by taking the
|
|
50
|
+
* tools out of the turn's context, which holds for every tool the harness owns and cannot reach a program the
|
|
51
|
+
* agent runs for itself.
|
|
52
|
+
*
|
|
53
|
+
* WHICH IS WHY `shell` IS THE ONE THAT DECIDES. A session with a shell can read a credential this card never
|
|
54
|
+
* granted it, so switching it off is what turns the rest of these into a fence; leaving it on leaves them a
|
|
55
|
+
* strong default. The card's own UI says so at the switch, see PersonaForm.vue, because a limit that is
|
|
56
|
+
* weaker than it looks is worse than no limit at all.
|
|
57
|
+
*
|
|
58
|
+
* PERMISSIVE BY DEFAULT, deliberately, and the opposite of the account rule directly below it. An unrepeatable
|
|
59
|
+
* public post is worth defaulting to nothing for; an over-powered turn inside a container the owner can throw
|
|
60
|
+
* away is not, and it is the same reasoning that makes bypassPermissions this sandbox's default posture. So an
|
|
61
|
+
* absent `powers` means today's full toolbox, and a workspace that never opens this notices nothing. */
|
|
62
|
+
export const PersonaPowersSchema = z.object({
|
|
63
|
+
// "read" is look-and-search only; "write" adds creating and changing; "none" takes both away.
|
|
64
|
+
files: z
|
|
65
|
+
.enum(["none", "read", "write"])
|
|
66
|
+
.default("write")
|
|
67
|
+
.describe("What it may do with files: nothing, look and search, or also create and change."),
|
|
68
|
+
// Shell commands, and with them the terminals, the test runs, and every CLI on the image. See the header:
|
|
69
|
+
// this is the switch the others' strength depends on.
|
|
70
|
+
shell: z
|
|
71
|
+
.boolean()
|
|
72
|
+
.default(true)
|
|
73
|
+
.describe(
|
|
74
|
+
"Whether it may run commands, and with them the terminals, the test runs and every tool on the image. The switch the strength of the others depends on.",
|
|
75
|
+
),
|
|
76
|
+
/* The JS execution backend (AgentCapabilities.execution): the model writes a script instead of a command
|
|
77
|
+
* line, run in a permission-fenced Node subprocess. Its fence is REAL where the shell's is not, reads and
|
|
78
|
+
* writes follow the `files` answer, and it can start no other program unless `shell` is also on, with one
|
|
79
|
+
* stated gap: the fence cannot cut the network, so a script can fetch whatever `web` says. */
|
|
80
|
+
code: z
|
|
81
|
+
.boolean()
|
|
82
|
+
.default(true)
|
|
83
|
+
.describe(
|
|
84
|
+
"Whether it may write and run a script rather than a command line. Its fence is real where the shell's is not: reads and writes follow the files answer, and it can start no other program unless commands are allowed too. The one stated gap is that the fence cannot cut the network.",
|
|
85
|
+
),
|
|
86
|
+
// Fetch a page, run a search.
|
|
87
|
+
web: z.boolean().default(true).describe("Whether it may fetch a page or run a search."),
|
|
88
|
+
// The credential-free browser. The SIGNED-IN browsers are `capabilities` below, a different question, and
|
|
89
|
+
// the reason this one is safe to leave on: it holds nobody's account.
|
|
90
|
+
browser: z.boolean().default(true),
|
|
91
|
+
// Spawn sub-agents and run workflows.
|
|
92
|
+
delegate: z.boolean().default(true),
|
|
93
|
+
/* Change the sandbox itself: its settings and manifests, and the public outbox that publishes a file to
|
|
94
|
+
* anyone with the link. Enforced as a refusal on the paths that carry those, not as a tool switch, there
|
|
95
|
+
* is no "install a capability" tool to take away, only files that mean it. */
|
|
96
|
+
sandbox: z.boolean().default(true),
|
|
97
|
+
/* The connected accounts and services this persona may reach, BY ID. Absent means every one of them, which
|
|
98
|
+
* is what a card that has never thought about it should get; an empty list means none. That tri-state is the
|
|
99
|
+
* whole reason these are optional rather than defaulted arrays, "all" and "none" are both real answers and
|
|
100
|
+
* an empty default could only spell one of them. */
|
|
101
|
+
connectors: z.array(entryId).max(100).optional(),
|
|
102
|
+
computers: z.array(entryId).max(50).optional(),
|
|
103
|
+
mcp: z.array(entryId).max(50).optional(),
|
|
104
|
+
});
|
|
105
|
+
export type PersonaPowers = z.infer<typeof PersonaPowersSchema>;
|
|
106
|
+
/* WHERE A PERSONA WORKS, the third question after who it is and what it may do.
|
|
107
|
+
*
|
|
108
|
+
* `folders` is the one field here that promises less than it looks like it promises, and the card says so where
|
|
109
|
+
* it is set: it is enforced by refusing file tool calls that point outside, which stops a misread instruction
|
|
110
|
+
* and an honest mistake, and does not stop a shell. The workspace-wide fence is the container. */
|
|
111
|
+
/* WHERE A SESSION WEARING THIS CARD WORKS, the folder it opens in, and the folders its file tools may touch.
|
|
112
|
+
*
|
|
113
|
+
* There is no placement field, and that is a decision rather than an omission. A card used to be able to ask
|
|
114
|
+
* for the SHARED tree instead of its own copy; every surface already defaults to a private worktree
|
|
115
|
+
* (conversation.ts), so the setting existed only to opt out of the isolation that makes parallel work safe,
|
|
116
|
+
* expressed in three words ("whatever started it", "its own copy", "the shared workspace") that a reader had no
|
|
117
|
+
* way to choose between. A persona starts where it is told and works in its own copy. */
|
|
118
|
+
export const PersonaWorkspaceSchema = z.object({
|
|
119
|
+
// The repo (or folder) under the workspace a session starts in. Absent ⇒ the workspace root, as today.
|
|
120
|
+
startIn: z.string().max(200).optional().describe("Which folder a conversation opens in."),
|
|
121
|
+
// Workspace-relative folders the file tools may touch. Absent ⇒ anywhere under the workspace.
|
|
122
|
+
folders: z.array(z.string().min(1)).max(50).optional().describe("Which folders it may touch at all. Absent means the whole workspace."),
|
|
123
|
+
});
|
|
124
|
+
export type PersonaWorkspace = z.infer<typeof PersonaWorkspaceSchema>;
|
|
125
|
+
export const PersonaSchema = z.object({
|
|
126
|
+
id: entryId.describe("The persona's id."),
|
|
127
|
+
// What the owner calls it in the composer chip. Absent ⇒ surfaces read the id, which is already human-chosen.
|
|
128
|
+
label: z.string().max(60).optional().describe("What to call it on screen. Absent falls back to the id, which somebody chose anyway."),
|
|
129
|
+
/* The capability ids this persona acts THROUGH, the logged-in browser accounts (and, later, the credential
|
|
130
|
+
* connectors) that are its hands. Ids rather than platforms, because "two accounts of one site" is the whole
|
|
131
|
+
* problem: `reddit-work` and `reddit-personal` are two capabilities and exactly one of them belongs here.
|
|
132
|
+
*
|
|
133
|
+
* An id naming a capability that isn't connected is not an error, it is a card describing an account this
|
|
134
|
+
* sandbox has yet to sign into, which is precisely what a freshly cloned workspace looks like. */
|
|
135
|
+
capabilities: z
|
|
136
|
+
.array(entryId)
|
|
137
|
+
.max(50)
|
|
138
|
+
.describe(
|
|
139
|
+
"Which connected accounts are its hands. Named individually rather than by site, because two accounts on one site is the whole problem this solves. Naming one that is not connected yet is not an error: it is a card describing an account this sandbox has still to sign into.",
|
|
140
|
+
),
|
|
141
|
+
/* Which workspace repos prefer this persona, so a chat opened on a project starts with the right chip already
|
|
142
|
+
* selected. A PREFERENCE, not a fence, the owner's chosen chat default is still "every account", and it
|
|
143
|
+
* lives on the card rather than in each project's own config so that one account named by three repos stays
|
|
144
|
+
* one definition instead of three that drift. */
|
|
145
|
+
repos: z
|
|
146
|
+
.array(z.string().min(1))
|
|
147
|
+
.max(50)
|
|
148
|
+
.optional()
|
|
149
|
+
.describe(
|
|
150
|
+
"Which repositories prefer this persona, so a conversation opened on one starts with the right choice already made. A preference rather than a fence.",
|
|
151
|
+
),
|
|
152
|
+
// What a session wearing this card may do, and where it works. Both absent ⇒ the full toolbox and the whole
|
|
153
|
+
// workspace, so a card written before these existed keeps behaving exactly as it did.
|
|
154
|
+
powers: PersonaPowersSchema.optional().describe(
|
|
155
|
+
"What a conversation wearing it may do. Absent means the full toolbox, so a card written before this existed behaves exactly as it did.",
|
|
156
|
+
),
|
|
157
|
+
workspace: PersonaWorkspaceSchema.optional().describe("Where it works. Absent means the whole workspace."),
|
|
158
|
+
/* WHICH SYSTEM PROMPT A SESSION WEARING THIS CARD RUNS ON, the same three bases the sandbox chooses
|
|
159
|
+
* between, asked per card. ABSENT is the fourth answer and the default: follow the sandbox, which is what
|
|
160
|
+
* every card meant before this field existed and what almost every card will go on meaning.
|
|
161
|
+
*
|
|
162
|
+
* Absent rather than a fifth enum value spelling the same thing. "inherit" and "not set" would be two
|
|
163
|
+
* spellings of one answer, and the surface that offers four options maps its first to leaving this off.
|
|
164
|
+
*
|
|
165
|
+
* THE TEXT IS NOT HERE. Under "custom" it is `PROMPT.md` in the card's own kit folder
|
|
166
|
+
* (personas/persona-kit.ts), for two reasons that point the same way: a system prompt is prose, and prose
|
|
167
|
+
* belongs in a file where it diffs line by line rather than as one escaped string inside a record nobody
|
|
168
|
+
* writes by hand, and the kit is already where that persona's skills and tools live, so there is one folder
|
|
169
|
+
* to look in rather than a field here and a directory there.
|
|
170
|
+
*
|
|
171
|
+
* "custom" with no PROMPT.md written yet falls back to the sandbox's answer rather than running the turn on
|
|
172
|
+
* an empty prompt: the card is half-made, and a half-made card should behave like the one it was before
|
|
173
|
+
* somebody started editing it. */
|
|
174
|
+
systemPromptMode: SystemPromptModeSchema.optional(),
|
|
175
|
+
});
|
|
176
|
+
export type Persona = z.infer<typeof PersonaSchema>;
|
|
177
|
+
/* THE ONE CARD ID THE PRODUCT NAMES ITSELF, the read-only persona a public web chat answers through.
|
|
178
|
+
*
|
|
179
|
+
* Nothing else is stock: a fresh workspace has no personas at all, and every card on the Personas page is one
|
|
180
|
+
* the owner wrote. This id is the exception because a Front Desk is driven by a stranger with nobody watching, so
|
|
181
|
+
* it is the one wake whose bounds cannot be left to the prompt's wording, the daemon writes the card the moment
|
|
182
|
+
* a Front Desk is saved (personas/front-desk.ts) and the automations form fills a blank Front Desk persona with it.
|
|
183
|
+
*
|
|
184
|
+
* It lives HERE because those two are in different packages and must agree exactly. A literal in each would
|
|
185
|
+
* drift into a Front Desk pinned to a card nobody creates, and turnPersona answers a missing card by denying
|
|
186
|
+
* everything, a public chat that cannot even read, which is safe and useless.
|
|
187
|
+
*
|
|
188
|
+
* It is FRONT DESK and not "visitor": the card is who answers the people who arrive, not the person arriving. */
|
|
189
|
+
export const FRONT_DESK_PERSONA = "front-desk";
|
|
190
|
+
/* HOW BOUNDED A CARD IS, in one phrase, for the row badge on the Personas page and for the sentence under the
|
|
191
|
+
* automations composer's persona picker.
|
|
192
|
+
*
|
|
193
|
+
* It lives in the contract rather than in either surface because those two are in different packages and would
|
|
194
|
+
* otherwise each grow their own vocabulary for the same card: a workspace where the Personas page says
|
|
195
|
+
* "Read-only" and the automation under it says "3 limits" is one where the reader cannot tell whether they are
|
|
196
|
+
* looking at the same thing.
|
|
197
|
+
*
|
|
198
|
+
* TWO NAMED SHAPES AND THEN A COUNT. "Read-only" and "no shell" are the two people actually reach for, so they
|
|
199
|
+
* get words; everything else gets a number, because listing four switched-off shelves in a badge produces a line
|
|
200
|
+
* nobody reads and buries the one fact that matters, that this card is limited at all. */
|
|
201
|
+
export const personaBounds = (persona: Persona): string => {
|
|
202
|
+
const powers = persona.powers;
|
|
203
|
+
if (powers === undefined) {
|
|
204
|
+
return "Full powers";
|
|
205
|
+
}
|
|
206
|
+
const resolved = PersonaPowersSchema.parse(powers);
|
|
207
|
+
if (resolved.files === "read" && !resolved.shell) {
|
|
208
|
+
return "Read-only";
|
|
209
|
+
}
|
|
210
|
+
if (!resolved.shell) {
|
|
211
|
+
return "No shell";
|
|
212
|
+
}
|
|
213
|
+
const limits = [
|
|
214
|
+
resolved.files === "none",
|
|
215
|
+
!resolved.code,
|
|
216
|
+
!resolved.web,
|
|
217
|
+
!resolved.browser,
|
|
218
|
+
!resolved.delegate,
|
|
219
|
+
!resolved.sandbox,
|
|
220
|
+
resolved.connectors !== undefined,
|
|
221
|
+
resolved.computers !== undefined,
|
|
222
|
+
resolved.mcp !== undefined,
|
|
223
|
+
].filter(Boolean).length;
|
|
224
|
+
return limits === 0 ? "Full powers" : `${limits} limit${limits === 1 ? "" : "s"}`;
|
|
225
|
+
};
|
|
226
|
+
export const PersonaIdParamSchema = z.object({ id: entryId.describe("Which persona.") });
|
|
227
|
+
/* Every persona, plus which of the accounts they name this sandbox is actually signed into. The second half is
|
|
228
|
+
* what makes the list honest on a freshly cloned workspace: every card is present and most of them cannot act
|
|
229
|
+
* yet, and a surface that showed only the cards would present a persona that is one login away from working as
|
|
230
|
+
* though it already did. Ids the manifest has no capability for at all are `connected: false` too, a card may
|
|
231
|
+
* name an account nobody has added here. */
|
|
232
|
+
export const PersonasListSchema = z.object({
|
|
233
|
+
personas: z.array(PersonaSchema).describe("The characters an agent can wear."),
|
|
234
|
+
connected: z
|
|
235
|
+
.array(z.string())
|
|
236
|
+
.describe(
|
|
237
|
+
"Which accounts are actually connected right now, so a persona naming one that has since been disconnected can be shown as broken rather than as working.",
|
|
238
|
+
),
|
|
239
|
+
});
|
|
240
|
+
/* A PERSONA'S KIT, as one read, the prompt it runs on and the skills it carries.
|
|
241
|
+
*
|
|
242
|
+
* ONE ROUTE FOR BOTH because they are one folder and one screen: the card's editor draws them together, and two
|
|
243
|
+
* requests to render one section is two chances for it to arrive half-drawn. The skills come back as name and
|
|
244
|
+
* description only, for the same reason the sandbox's own skill list does, a body runs to thousands of words
|
|
245
|
+
* and a group of one-line rows should not cost a hundred kilobytes to draw.
|
|
246
|
+
*
|
|
247
|
+
* An empty prompt is a card with no PROMPT.md, which is every card until somebody writes one. It is "" rather
|
|
248
|
+
* than absent because the field behind it is a textarea, and a textarea's empty value is "". */
|
|
249
|
+
export const PersonaKitSchema = z.object({
|
|
250
|
+
prompt: z
|
|
251
|
+
.string()
|
|
252
|
+
.describe("What this persona is told, on top of everything else. Empty means it simply follows the sandbox's own instructions."),
|
|
253
|
+
skills: z
|
|
254
|
+
.array(
|
|
255
|
+
z.object({
|
|
256
|
+
name: z.string().describe("The skill's name."),
|
|
257
|
+
description: z.string().describe("What it is for."),
|
|
258
|
+
}),
|
|
259
|
+
)
|
|
260
|
+
.describe(
|
|
261
|
+
"Skills only this persona's conversations can reach. A different question from what the agent knows generally, with a different answer.",
|
|
262
|
+
),
|
|
263
|
+
});
|
|
264
|
+
export type PersonaKit = z.infer<typeof PersonaKitSchema>;
|
|
265
|
+
export const PersonaPromptSchema = PersonaIdParamSchema.extend({
|
|
266
|
+
prompt: z
|
|
267
|
+
.string()
|
|
268
|
+
.max(20000)
|
|
269
|
+
.describe(
|
|
270
|
+
"What to tell this persona. Sending an empty one removes it entirely rather than storing a blank, so the persona falls back to the sandbox's own instructions.",
|
|
271
|
+
),
|
|
272
|
+
});
|
|
273
|
+
export const PersonaSkillSchema = PersonaIdParamSchema.extend(SkillDraftSchema.shape);
|
|
274
|
+
export const PersonaSkillNameSchema = PersonaIdParamSchema.extend({ name: SkillNameSchema.describe("Which skill.") });
|
|
275
|
+
// One kit skill's instructions, for editing it, the same split the sandbox's own skills make between a listing
|
|
276
|
+
// and a body, and for the same reason.
|
|
277
|
+
export const PersonaSkillBodySchema = z.object({
|
|
278
|
+
name: z.string().describe("The skill's name."),
|
|
279
|
+
description: z.string().describe("What it is for."),
|
|
280
|
+
body: z.string().describe("The skill itself, in full."),
|
|
281
|
+
});
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { EditorContextSchema } from "./agent.js";
|
|
3
|
+
// Declared ABOVE both account shapes because both carry it: headroom is one idea in this product, not a Claude
|
|
4
|
+
// idea that other providers imitate. A native account (OauthAccount) and a routed subscription
|
|
5
|
+
// (TranslatorAccount) differ in who holds the credential and how the reading is taken, never in what a
|
|
6
|
+
// reading IS, so every surface that draws a percentage reads this one type and no other.
|
|
7
|
+
|
|
8
|
+
// One plan-limit pool. `kind` is the provider's own key ('five_hour' | 'seven_day' | 'seven_day_opus' |
|
|
9
|
+
// 'seven_day_sonnet' | 'model:Fable' | …) rather than an enum we'd have to keep in step with the provider: an
|
|
10
|
+
// unrecognised pool is shown under its raw key, which is far better than being silently folded into a
|
|
11
|
+
// neighbour. `label` is the provider's OWN display name where it supplies one (the per-model buckets do), it
|
|
12
|
+
// wins over anything we'd infer, because the model names in a plan's limits are the provider's to rename.
|
|
13
|
+
// `resetsAt` is epoch SECONDS (matching the SDK's frame).
|
|
14
|
+
export const UsageWindowSchema = z.object({
|
|
15
|
+
kind: z.string(),
|
|
16
|
+
label: z.string().optional(),
|
|
17
|
+
utilization: z.number(), // 0-100
|
|
18
|
+
resetsAt: z.number().optional(),
|
|
19
|
+
});
|
|
20
|
+
export type UsageWindow = z.infer<typeof UsageWindowSchema>;
|
|
21
|
+
// An account's headroom: EVERY window the provider reports, read together, plus when the reading was taken.
|
|
22
|
+
// All of them, not the binding one, because "which pool is binding" changes between turns and a reader
|
|
23
|
+
// comparing accounts needs the same pools on every row. How the reading is TAKEN is per provider and stops at
|
|
24
|
+
// the daemon's readers: Claude's rides the turn's own stream, ChatGPT's, Google's and Kimi's are pulled through
|
|
25
|
+
// CLIProxyAPI's credential-scoped management call. All of them are control requests, so none costs tokens.
|
|
26
|
+
//
|
|
27
|
+
// Within one window utilization only climbs, so an un-reset window stays a valid FLOOR however old it is; past
|
|
28
|
+
// its `resetsAt` it describes a pool that no longer exists and the store drops it. `measuredAt` is epoch MS
|
|
29
|
+
// (matching connectedAt), deliberately a different unit from the windows' seconds.
|
|
30
|
+
export const AccountUsageSchema = z.object({
|
|
31
|
+
windows: z.array(UsageWindowSchema),
|
|
32
|
+
measuredAt: z.number(),
|
|
33
|
+
});
|
|
34
|
+
export type AccountUsage = z.infer<typeof AccountUsageSchema>;
|
|
35
|
+
/* THE LAST TIME A PROVIDER ACTUALLY REFUSED A TURN, the other half of "can I run on this", and the half no
|
|
36
|
+
* meter can supply.
|
|
37
|
+
*
|
|
38
|
+
* A snapshot above is POLLED and therefore always a floor: read at turn end (Claude) or on a five-minute sweep
|
|
39
|
+
* (the routed subscriptions), and account-wide, so every other client on the plan spends the same pools without
|
|
40
|
+
* this sandbox hearing about it. A refusal is the opposite kind of fact, observed, exact, and timestamped by
|
|
41
|
+
* the only event that proves the plan said no. Between them they answer a question neither can alone: a green
|
|
42
|
+
* meter beside "refused a turn 4 minutes ago" means the reading is stale, not that the account has room.
|
|
43
|
+
*
|
|
44
|
+
* Keyed by PROVIDER rather than by account, because that is the resolution the daemon honestly has. A native
|
|
45
|
+
* Claude turn knows which account served it and names it; a routed turn does not. CLIProxyAPI picks the auth
|
|
46
|
+
* file itself and only refuses once every credential it holds is cooling down, which makes the refusal a fact
|
|
47
|
+
* about the provider in the first place.
|
|
48
|
+
*
|
|
49
|
+
* `kind` is read off what the provider SAID, not off the frame code the harness filed it under, because those
|
|
50
|
+
* two disagree: Kimi answers a spent plan with `403 You've reached your usage limit for this billing cycle`,
|
|
51
|
+
* which the CLI prints under "Failed to authenticate" and the stream codes as a refused credential. Sending
|
|
52
|
+
* someone to reconnect a perfectly good account is the cost of believing the code over the sentence. */
|
|
53
|
+
export const ProviderRefusalSchema = z.object({
|
|
54
|
+
// Epoch MS, matching AccountUsage.measuredAt, the two are read side by side.
|
|
55
|
+
at: z.number().describe("When it refused, in milliseconds."),
|
|
56
|
+
/* Three ways a plan says no, kept apart because WHAT ANSWERS EACH is different and a screen that conflates
|
|
57
|
+
* them tells the user to do the wrong thing. A spent allowance is answered by a later reading with room in
|
|
58
|
+
* it; a refused credential by the account being read at all through it; and an entitlement refusal, an
|
|
59
|
+
* organization that has turned Claude Code off for this seat, by NOTHING either of those can produce. Its
|
|
60
|
+
* token authenticates and its usage endpoint answers with real pools the whole time it cannot run a turn,
|
|
61
|
+
* so filing it as `auth` let the very next quota sweep dismiss it and leave a full green ring over an
|
|
62
|
+
* account that refused everything asked of it. Only a turn that actually runs settles this one. */
|
|
63
|
+
kind: z
|
|
64
|
+
.enum(["limit", "auth", "entitlement"])
|
|
65
|
+
.describe(
|
|
66
|
+
"Three different noes, kept apart because what fixes each is different. A spent allowance is answered by waiting; a refused credential by signing in again; and an entitlement refusal, where somebody has switched this off for your seat, by neither of those. That last one authenticates fine and reports healthy limits the whole time it refuses everything.",
|
|
67
|
+
),
|
|
68
|
+
// The provider's own sentence, verbatim. It is the only part that says WHICH pool or WHICH credential.
|
|
69
|
+
message: z.string().describe("The provider's own words, verbatim. The only part that says which limit or which credential."),
|
|
70
|
+
// The account that was serving, when the daemon knows it (native turns only, see above).
|
|
71
|
+
account: z.string().optional().describe("Which account was serving, where that is known."),
|
|
72
|
+
});
|
|
73
|
+
export type ProviderRefusal = z.infer<typeof ProviderRefusalSchema>;
|
|
74
|
+
export const ProviderRefusalsSchema = z.object({
|
|
75
|
+
refusals: z
|
|
76
|
+
.record(z.string(), ProviderRefusalSchema)
|
|
77
|
+
.describe(
|
|
78
|
+
"The most recent refusal per provider. Read alongside an account's usage: that says how full it was when last checked, this says whether it has since started saying no.",
|
|
79
|
+
),
|
|
80
|
+
});
|
|
81
|
+
export type ProviderRefusals = z.infer<typeof ProviderRefusalsSchema>;
|
|
82
|
+
// One connected subscription in the translator. `name` is CLIProxyAPI's auth-file name, the stable store key a
|
|
83
|
+
// disconnect addresses, and `label` the sign-in identity it reported (the account email, else the file name).
|
|
84
|
+
export const TranslatorAccountSchema = z.object({
|
|
85
|
+
name: z.string(),
|
|
86
|
+
label: z.string(),
|
|
87
|
+
// The same headroom an OauthAccount carries, on the same field, for the same reason: the account rows are
|
|
88
|
+
// one list to the reader. Optional because a provider whose quota this sandbox cannot read (Grok),
|
|
89
|
+
// or one that did not answer, must still render as the connected account it is, with a dot instead of a
|
|
90
|
+
// ring.
|
|
91
|
+
usage: AccountUsageSchema.optional(),
|
|
92
|
+
});
|
|
93
|
+
export type TranslatorAccount = z.infer<typeof TranslatorAccountSchema>;
|
|
94
|
+
// Which routed-provider subscriptions are connected in the translator, per provider, a LIST per provider, not
|
|
95
|
+
// a flag: CLIProxyAPI holds any number of auth files per provider side by side and balances requests across
|
|
96
|
+
// them, so connecting a second ChatGPT or Google account is more headroom, and each is disconnectable on its
|
|
97
|
+
// own. Drives the account rows in Sandbox ▸ Agent.
|
|
98
|
+
export const TranslatorAccountsSchema = z.object({
|
|
99
|
+
codex: z.array(TranslatorAccountSchema),
|
|
100
|
+
grok: z.array(TranslatorAccountSchema),
|
|
101
|
+
kimi: z.array(TranslatorAccountSchema),
|
|
102
|
+
gemini: z.array(TranslatorAccountSchema),
|
|
103
|
+
});
|
|
104
|
+
export type TranslatorAccounts = z.infer<typeof TranslatorAccountsSchema>;
|
|
105
|
+
// The side-channel body that un-parks a turn waiting on the user. Every interactive card, plan approval,
|
|
106
|
+
// clarifying questions, a per-tool permission prompt, parks on the SAME registry keyed by `requestId`, so
|
|
107
|
+
// one route resolves all three; the `kind` says which card answered and carries its payload.
|
|
108
|
+
export const AgentReplySchema = z.discriminatedUnion("kind", [
|
|
109
|
+
// ExitPlanMode approval. Approving carries NO posture: an approved plan executes under bypassPermissions,
|
|
110
|
+
// set on the SDK session by the gate that raised the card. The container is the isolation boundary, so a
|
|
111
|
+
// plan the user has read and approved is exactly the point where per-tool prompts stop earning their
|
|
112
|
+
// interruption, landing anywhere else means approving a plan to run `git log` and then being asked whether
|
|
113
|
+
// `git log` may run. Rejection feedback loops back into the model as the denial reason.
|
|
114
|
+
z.object({
|
|
115
|
+
kind: z.literal("plan").describe("Answering a plan the agent proposed."),
|
|
116
|
+
requestId: z.string().min(1).describe("Which card you are answering, from the frame that raised it."),
|
|
117
|
+
approve: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.describe(
|
|
120
|
+
"Whether to go ahead. Approving means the plan then runs without asking again per tool, because being asked whether a plan you just approved may run its first command is not a question worth having.",
|
|
121
|
+
),
|
|
122
|
+
feedback: z.string().optional().describe("Why not, which goes back to the model as the reason."),
|
|
123
|
+
}),
|
|
124
|
+
// AskUserQuestion picks: question text → chosen option label(s) (+ any free-text "Other"). `cancelled`
|
|
125
|
+
// is the dismissal, which tells the model to proceed on sensible defaults rather than leaving it parked.
|
|
126
|
+
z.object({
|
|
127
|
+
kind: z.literal("question").describe("Answering a question the agent asked."),
|
|
128
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
129
|
+
answers: z
|
|
130
|
+
.record(z.string(), z.array(z.string()))
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("What you chose, keyed by the question, with the chosen labels or your own words."),
|
|
133
|
+
cancelled: z
|
|
134
|
+
.boolean()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("Dismissing it instead, which tells the agent to carry on using sensible defaults rather than leaving it waiting."),
|
|
137
|
+
}),
|
|
138
|
+
// A per-tool permission prompt. 'once' allows this call only; 'always' allows the whole TOOL for the rest
|
|
139
|
+
// of the session (plus the SDK's own narrower suggestions), which is what the card's label promises;
|
|
140
|
+
// 'deny' blocks it and feeds `feedback` back as the reason.
|
|
141
|
+
z.object({
|
|
142
|
+
kind: z.literal("permission").describe("Answering a request to use a tool."),
|
|
143
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
144
|
+
decision: z
|
|
145
|
+
.enum(["once", "always", "deny"])
|
|
146
|
+
.describe("Once allows this call alone; always allows that whole tool for the rest of the conversation; no blocks it."),
|
|
147
|
+
feedback: z.string().optional().describe("Why not, which goes back to the model as the reason."),
|
|
148
|
+
}),
|
|
149
|
+
// A browser help request (the agent parked mid-sign-in on something only a person can clear, a captcha, a
|
|
150
|
+
// password it does not hold). `helped: true` is "done, hand back": the user took control of the agent's
|
|
151
|
+
// browser, fixed the step, and the turn continues from the page as they left it. `helped: false` is "can't
|
|
152
|
+
// help now", the agent is told so and moves on rather than waiting forever. `note` rides back to the model
|
|
153
|
+
// either way ("typed the password, don't touch the remember-me box").
|
|
154
|
+
z.object({
|
|
155
|
+
kind: z
|
|
156
|
+
.literal("browser_help")
|
|
157
|
+
.describe("Answering a request for help in the agent's browser: a captcha, a password it does not hold, a check on your phone."),
|
|
158
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
159
|
+
helped: z
|
|
160
|
+
.boolean()
|
|
161
|
+
.describe(
|
|
162
|
+
"Whether you cleared it. Yes means the turn carries on from the page as you left it; no tells the agent so, and it moves on rather than waiting for ever.",
|
|
163
|
+
),
|
|
164
|
+
note: z.string().optional().describe("Anything the agent should know, which goes back to it either way."),
|
|
165
|
+
}),
|
|
166
|
+
// A terminal help request, the same two answers as the browser's, for a command parked at a prompt only a
|
|
167
|
+
// person can answer. `helped: true` is "typed it, carry on"; false is "can't right now". `note` rides back
|
|
168
|
+
// either way, and on `helped` the daemon adds what the pane SAYS to the tool result: the user answering the
|
|
169
|
+
// prompt is exactly the moment the agent cannot see, and it would otherwise have to ask them how it went.
|
|
170
|
+
z.object({
|
|
171
|
+
kind: z
|
|
172
|
+
.literal("terminal_help")
|
|
173
|
+
.describe("Answering a request for help at a terminal: a code to type, a confirmation only a person can give."),
|
|
174
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
175
|
+
helped: z
|
|
176
|
+
.boolean()
|
|
177
|
+
.describe(
|
|
178
|
+
"Whether you did it. Yes also hands the agent what the terminal now says, because a person answering a prompt is exactly the moment the agent cannot see.",
|
|
179
|
+
),
|
|
180
|
+
note: z.string().optional().describe("Anything the agent should know, which goes back to it either way."),
|
|
181
|
+
}),
|
|
182
|
+
// A premium service run's yes or no. The click is the ONLY way the spend can happen, the daemon holds the
|
|
183
|
+
// agent's run request parked until this settles it (platform/service-offer.ts), so `approve` carries no
|
|
184
|
+
// qualifiers: one true releases exactly one run, and anything else charges nothing.
|
|
185
|
+
z.object({
|
|
186
|
+
kind: z.literal("service_offer").describe("Answering a request to spend on a paid service."),
|
|
187
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
188
|
+
approve: z
|
|
189
|
+
.boolean()
|
|
190
|
+
.describe("Yes releases exactly one run. Anything else charges nothing. This click is the only way the spend can happen."),
|
|
191
|
+
}),
|
|
192
|
+
// A missing-capability ask's yes or no. `connect: true` is "I'll set it up", it opens the card's setup
|
|
193
|
+
// and keeps the agent's request parked while the daemon watches for the connection to come live
|
|
194
|
+
// (capabilities/capability-offer.ts); false tells the agent to continue without it. The click decides
|
|
195
|
+
// only the WATCHING: nothing is connected by the reply itself, the setup is the owner's own flow.
|
|
196
|
+
z.object({
|
|
197
|
+
kind: z.literal("capability_offer").describe("Answering a request to connect something the agent needs."),
|
|
198
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
199
|
+
connect: z
|
|
200
|
+
.boolean()
|
|
201
|
+
.describe(
|
|
202
|
+
"Yes keeps the agent waiting while you set it up, and it carries on the moment the connection comes alive. No tells it to continue without. The reply itself connects nothing: setting it up is still your own doing.",
|
|
203
|
+
),
|
|
204
|
+
}),
|
|
205
|
+
// A USDC payment's yes or no. The click is the ONLY way the money can move, the daemon holds the agent's
|
|
206
|
+
// `wallet fetch` parked until this settles it (wallet/payment-offer.ts), so `approve` carries no
|
|
207
|
+
// qualifiers: one true releases exactly one payment, and anything else spends nothing.
|
|
208
|
+
z.object({
|
|
209
|
+
kind: z.literal("payment_offer").describe("Answering a request to pay for something."),
|
|
210
|
+
requestId: z.string().min(1).describe("Which card you are answering."),
|
|
211
|
+
approve: z
|
|
212
|
+
.boolean()
|
|
213
|
+
.describe("Yes releases exactly one payment. Anything else spends nothing. This click is the only way the money can move."),
|
|
214
|
+
}),
|
|
215
|
+
]);
|
|
216
|
+
export type AgentReply = z.infer<typeof AgentReplySchema>;
|
|
217
|
+
// Steering: a user message delivered INTO the running turn (injected between tool calls, Claude Code style),
|
|
218
|
+
// keyed by the conversation whose turn is in flight. NOT_FOUND when no steerable turn is running, the client
|
|
219
|
+
// then holds the message in its queue and sends it as the next turn instead. Carries everything a turn's own
|
|
220
|
+
// prompt can carry (files, the editor-context chip), because "add more while it works" is worth nothing if it
|
|
221
|
+
// only takes bare text: the daemon folds the same notes into the injected message that a fresh turn gets.
|
|
222
|
+
export const SteerSchema = z
|
|
223
|
+
.object({
|
|
224
|
+
conversationId: z.string().min(1).describe("Which running conversation to interrupt."),
|
|
225
|
+
text: z.string().max(20_000).describe("What to say to it. It arrives mid-turn without stopping the turn."),
|
|
226
|
+
attachments: z
|
|
227
|
+
.array(z.string().min(1))
|
|
228
|
+
.max(20)
|
|
229
|
+
.optional()
|
|
230
|
+
.describe("Files to send with it, as workspace paths. A screenshot dropped in mid-turn with no words is a legitimate thing to send."),
|
|
231
|
+
editorContext: EditorContextSchema.optional().describe("What you have open, folded in so that pointing words resolve."),
|
|
232
|
+
})
|
|
233
|
+
// An attachment-only steer (a screenshot dropped in mid-turn) is legal; an entirely empty one is not.
|
|
234
|
+
.refine((steer) => steer.text.trim().length > 0 || (steer.attachments?.length ?? 0) > 0, {
|
|
235
|
+
message: "text or attachments required",
|
|
236
|
+
});
|
|
237
|
+
// True cancel for the conversation's in-flight turn, aborts the agent daemon-side, unlike closing the
|
|
238
|
+
// /agent fetch (which sends no cancel frame).
|
|
239
|
+
export const StopTurnSchema = z.object({ conversationId: z.string().min(1).describe("Which conversation's running turn to cancel.") });
|
|
240
|
+
/* RUN THE HELD TURN AGAIN, and it carries a conversation id and NOTHING else, which is the entire point of it
|
|
241
|
+
* existing as its own route rather than as a flag on a turn.
|
|
242
|
+
*
|
|
243
|
+
* A spent allowance leaves a turn stranded that the daemon still holds in full: the prompt, the attachments, the
|
|
244
|
+
* model, the effort, the mode, the worktree, the session that holds whatever it managed to do. Every one of
|
|
245
|
+
* those is on the turn the daemon already has, and a client that re-derived them from its own transcript would
|
|
246
|
+
* be re-deriving them from the STRIPPED copy it renders (no preamble notes, no attachment note, no model), which
|
|
247
|
+
* is how a re-send comes to run a different turn from the one it claims to repeat.
|
|
248
|
+
*
|
|
249
|
+
* So the caller says only WHICH conversation, and the daemon re-runs the turn it kept. What comes back is an
|
|
250
|
+
* ordinary StartedTurn, and the caller then attaches to it exactly as it would to a turn somebody else started
|
|
251
|
+
* (the resume note on the prompt is what tells an attaching window to reuse the bubble that is already there
|
|
252
|
+
* instead of drawing the same message twice). */
|
|
253
|
+
export const ResumeTurnSchema = z.object({
|
|
254
|
+
conversationId: z.string().min(1).describe("Which conversation's held turn to run again."),
|
|
255
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// ports: every listening TCP socket in the sandbox + explicit port forwarding
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// Anything run in a terminal (a turbo TUI fanning out dev servers, `python -m http.server`, an agent's ad-hoc
|
|
4
|
+
// process) binds ports the daemon never assigned, the panel machinery can't see them. The /ports routes are
|
|
5
|
+
// the generic complement: `list` reports the live listeners (procfs scan, on demand), `forward` makes one
|
|
6
|
+
// reachable at port-<slot>-<sandboxId>.<zone> through the preview proxy. Forwarding is an explicit gesture,
|
|
7
|
+
// previews are public, so nothing is exposed until the owner (or an agent acting for them) asks.
|
|
8
|
+
|
|
9
|
+
export const PortSummarySchema = z.object({
|
|
10
|
+
port: z.number().describe("The port number."),
|
|
11
|
+
// The loopback address the listener actually answers at inside the sandbox, a `localhost` bind can land
|
|
12
|
+
// on ::1 only (Vite). The preview proxy and the desktop mirror (Mutagen forward) both dial this.
|
|
13
|
+
host: z
|
|
14
|
+
.enum(["127.0.0.1", "::1"])
|
|
15
|
+
.describe("Which loopback address it actually answers on. Some tools bind only one of the two, and anything dialling it has to know which."),
|
|
16
|
+
// Whether the proxy can actually reach the listener at `host`. False for a bind to a loopback alias like
|
|
17
|
+
// Docker's embedded DNS (127.0.0.11), which answers only at its own address, not 127.0.0.1, such rows are
|
|
18
|
+
// listed for transparency but the Ports view hides Preview and forwarding them is refused.
|
|
19
|
+
forwardable: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.describe(
|
|
22
|
+
"Whether it can be exposed at all. Some listeners answer only at their own address and nowhere else; those are listed for honesty and refused for forwarding.",
|
|
23
|
+
),
|
|
24
|
+
// Which bucket the Ports view files it under: `workspace` = user-run (dev servers in repos, terminal
|
|
25
|
+
// processes, published container ports), the previewable set; `system` = the sandbox's own machinery
|
|
26
|
+
// (agent runtimes, translator, dockerd, sshd), listed for transparency but nobody previews it.
|
|
27
|
+
kind: z
|
|
28
|
+
.enum(["workspace", "system"])
|
|
29
|
+
.describe("Whether somebody's own work put it there, or the sandbox's own machinery did. Only the first kind is worth previewing."),
|
|
30
|
+
/* WHAT IS ON THIS PORT, IN WORDS: resolved by the daemon (ports/port-identity.ts), because the two facts
|
|
31
|
+
* that attribute a listener (the panel key → extension index, the workspace root) exist there and nowhere
|
|
32
|
+
* else. `title` is what a person would call it ("Vite dev server", "Sandbox service", "Container port"),
|
|
33
|
+
* `purpose` is the one sentence a row shows under it, and `origin` says who put it there, which is what
|
|
34
|
+
* the reader is really asking when they ask what a port is: mine, my agent's, or the box's own.
|
|
35
|
+
*
|
|
36
|
+
* All three are required. A listener nothing can explain still gets a name ("Unclaimed port") and a
|
|
37
|
+
* sentence that says so out loud, because the alternative (a raw argv, or nothing) is what made this
|
|
38
|
+
* view unreadable, and the button beside the row publishes the port to the internet. */
|
|
39
|
+
title: z
|
|
40
|
+
.string()
|
|
41
|
+
.describe(
|
|
42
|
+
"What a person would call it. Always present: a listener nothing can explain is still named, because the button beside it publishes the port to the internet.",
|
|
43
|
+
),
|
|
44
|
+
purpose: z.string().describe("One sentence about what it is for, including when the honest answer is that nothing could work it out."),
|
|
45
|
+
origin: z
|
|
46
|
+
.enum(["terminal", "agent", "panel", "extension", "container", "sandbox", "unknown"])
|
|
47
|
+
.describe("Who put it there, which is the question somebody is really asking: mine, my agent's, or the box's own."),
|
|
48
|
+
// The owning process, resolved from procfs; absent when no /proc/*/fd entry matched the socket's inode.
|
|
49
|
+
pid: z.number().optional().describe("The process holding it. Absent when nothing could be matched to the socket."),
|
|
50
|
+
// How the row is labeled: the process argv joined with spaces ("node /work/app/node_modules/.bin/vite"),
|
|
51
|
+
// falling back to the kernel `comm` name when argv is empty, or a synthesized name for attributable
|
|
52
|
+
// infrastructure the pid walk can't reach ("Docker embedded DNS"). Absent only when wholly unattributable.
|
|
53
|
+
command: z.string().optional().describe("The command behind it, as it was run. Absent only when nothing could be attributed at all."),
|
|
54
|
+
// The process working directory (how the UI attributes a port to a repo).
|
|
55
|
+
cwd: z.string().optional().describe("Where it is running from, which is how a port gets attributed to a repository."),
|
|
56
|
+
// The tmux session the listener descends from, the terminal to watch it in or stop it from. Absent when
|
|
57
|
+
// nothing in its ancestry is a pane (a daemon-managed runtime, a published container's docker-proxy), which
|
|
58
|
+
// is the honest "you cannot reach this from here" rather than a terminal that would open onto nothing.
|
|
59
|
+
session: z
|
|
60
|
+
.string()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe(
|
|
63
|
+
'The terminal it came from, to watch it in or stop it from. Absent when nothing in its ancestry is one, which is the honest "you cannot reach this from here".',
|
|
64
|
+
),
|
|
65
|
+
forwarded: z.boolean().describe("Whether it is currently reachable from outside."),
|
|
66
|
+
// https://port-<slot>-<sandboxId>.<zone>; present only while forwarded AND the sandbox has a zone + id.
|
|
67
|
+
previewUrl: z.string().optional().describe("Where to open it. Present only while forwarded, and only on a sandbox that has an outside address."),
|
|
68
|
+
});
|
|
69
|
+
export type PortSummary = z.infer<typeof PortSummarySchema>;
|
|
70
|
+
export const PortsListSchema = z.object({
|
|
71
|
+
ports: z
|
|
72
|
+
.array(PortSummarySchema)
|
|
73
|
+
.describe("Everything listening inside the sandbox right now, read fresh each time rather than from a register the sandbox keeps."),
|
|
74
|
+
});
|
|
75
|
+
export type PortsList = z.infer<typeof PortsListSchema>;
|
|
76
|
+
export const PortParamSchema = z.object({ port: z.number().int().min(1).max(65535).describe("Which port.") });
|
|
77
|
+
// `previewUrl` is absent on a loopback/no-tunnel sandbox, the slot is mapped, but no public hostname exists.
|
|
78
|
+
export const PortForwardResultSchema = z.object({
|
|
79
|
+
previewUrl: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Where it can now be reached. Absent on a sandbox with no outside address, where the mapping exists but has no public name."),
|
|
83
|
+
});
|
|
84
|
+
export type PortForwardResult = z.infer<typeof PortForwardResultSchema>;
|