@intentic/sandbox-contract 1.232.0 → 1.234.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 +11 -5
- package/dist/agent-catalog.d.ts +4 -2
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +20 -14
- 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/chores.js +36 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/probes.d.ts +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +70 -0
- package/dist/chores/probes.js.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 +17 -0
- package/dist/command-classes.d.ts.map +1 -0
- package/dist/command-classes.js +189 -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 +40 -2
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +4 -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.d.ts +30 -0
- package/dist/contracts/automations.contract.d.ts.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 +17 -0
- 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 +44 -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 +7 -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/issues.contract.d.ts +89 -0
- package/dist/contracts/issues.contract.d.ts.map +1 -0
- package/dist/contracts/issues.contract.js +50 -0
- package/dist/contracts/issues.contract.js.map +1 -0
- 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 +127 -84
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +10 -2
- 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 +32 -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 +31 -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/credential-material.d.ts +2 -0
- package/dist/credential-material.d.ts.map +1 -0
- package/dist/credential-material.js +36 -0
- package/dist/credential-material.js.map +1 -0
- 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 +132 -21
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +29 -7
- package/dist/definition.js.map +1 -1
- package/dist/events.d.ts +101 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +25 -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 +2 -0
- package/dist/history-state.js.map +1 -1
- package/dist/hostnames.d.ts +2 -0
- package/dist/hostnames.d.ts.map +1 -1
- package/dist/hostnames.js +3 -1
- package/dist/hostnames.js.map +1 -1
- package/dist/index.d.ts +411 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -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/runtime-state.d.ts +9 -0
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +3 -0
- package/dist/runtime-state.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 +195 -0
- package/dist/schemas/agent.d.ts.map +1 -0
- package/dist/schemas/agent.js +171 -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 +781 -0
- package/dist/schemas/automations.d.ts.map +1 -0
- package/dist/schemas/automations.js +223 -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 +1016 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -0
- package/dist/schemas/extension-updates.js +199 -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/issues.d.ts +324 -0
- package/dist/schemas/issues.d.ts.map +1 -0
- package/dist/schemas/issues.js +107 -0
- package/dist/schemas/issues.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 +618 -0
- package/dist/schemas/maintenance.d.ts.map +1 -0
- package/dist/schemas/maintenance.js +222 -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 +554 -0
- package/dist/schemas/settings.d.ts.map +1 -0
- package/dist/schemas/settings.js +263 -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 +122 -0
- package/dist/schemas/webext.d.ts.map +1 -0
- package/dist/schemas/webext.js +45 -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 +14 -0
- package/dist/webext-links.d.ts.map +1 -0
- package/dist/webext-links.js +25 -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 +12 -8
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +15 -10
- package/dist/workspace-state.js.map +1 -1
- package/package.json +19 -8
- package/src/agent-catalog.test.ts +56 -55
- package/src/agent-catalog.ts +33 -33
- package/src/capability-ledger.test.ts +11 -2
- package/src/chores/chores.test.ts +1 -1
- package/src/chores/chores.ts +67 -2
- package/src/chores/probes.test.ts +65 -0
- package/src/chores/probes.ts +133 -1
- package/src/chores/stack.test.ts +19 -0
- package/src/chores/stack.ts +19 -3
- package/src/chores/verdict.test.ts +40 -20
- package/src/chores/verdict.ts +1 -1
- package/src/command-classes.test.ts +445 -0
- package/src/command-classes.ts +468 -0
- package/src/contracts/activity.contract.ts +1 -1
- package/src/contracts/agent.contract.ts +4 -13
- 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 +8 -7
- 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/issues.contract.ts +60 -0
- 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 +18 -2
- 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/credential-material.test.ts +120 -0
- package/src/credential-material.ts +100 -0
- package/src/definition-action.ts +8 -0
- package/src/definition.ts +74 -10
- package/src/events.ts +84 -22
- package/src/fast-tier.ts +1 -1
- package/src/history-state.ts +17 -0
- package/src/host-protocol.ts +1 -1
- package/src/hostnames.test.ts +2 -2
- package/src/hostnames.ts +18 -2
- package/src/index.ts +65 -1
- package/src/listener-protocol.ts +1 -1
- package/src/quick-model.ts +1 -1
- package/src/routes.test.ts +4 -2
- package/src/runner-protocol.ts +35 -5
- package/src/runtime-state.ts +16 -0
- package/src/schemas/activity.ts +123 -0
- package/src/schemas/agent.ts +422 -0
- package/src/schemas/agents.ts +802 -0
- package/src/schemas/automations.ts +522 -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 +324 -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/issues.ts +279 -0
- package/src/schemas/logs.ts +96 -0
- package/src/schemas/loops.ts +401 -0
- package/src/schemas/maintenance.ts +471 -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 +964 -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 +288 -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 +108 -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/tunnel-ids.test.ts +3 -1
- package/src/webext-links.ts +90 -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 +6 -4
- package/src/workspace-state.ts +35 -50
- package/dist/schemas.d.ts +0 -9909
- package/dist/schemas.d.ts.map +0 -1
- package/dist/schemas.js +0 -4288
- package/dist/schemas.js.map +0 -1
- package/src/schemas.ts +0 -10090
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
// Capabilities: the one `{ id, kind, config }` entry every connection a sandbox holds is written as.
|
|
2
|
+
// The config arm per kind, the discriminated union over them, and what the Capabilities page reads:
|
|
3
|
+
// each row's live status, the probe behind it, and the connect/rename/sign-in inputs.
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { ExitConfigSchema } from "./exit.js";
|
|
6
|
+
import { entryId } from "./internal.js";
|
|
7
|
+
import { ServiceKindSchema } from "./inventory.js";
|
|
8
|
+
import { VpnConfigSchema } from "./vpn.js";
|
|
9
|
+
// Everything a user adds to a sandbox is a capability with an idempotent apply + a status check. The manifest is
|
|
10
|
+
// the source of truth for what's active; `mcp`-kind entries also feed the agent's MCP servers each turn. DevOps
|
|
11
|
+
// is the capability that scaffolds the intent/desired-state repos, until it's active the sandbox is empty.
|
|
12
|
+
|
|
13
|
+
export const CapabilityKindSchema = z.enum([
|
|
14
|
+
"devops",
|
|
15
|
+
"monorepo",
|
|
16
|
+
"mcp",
|
|
17
|
+
"service",
|
|
18
|
+
"integration",
|
|
19
|
+
"cli",
|
|
20
|
+
"plugin",
|
|
21
|
+
"extension",
|
|
22
|
+
"ssh",
|
|
23
|
+
"vpn",
|
|
24
|
+
"exit",
|
|
25
|
+
"docker",
|
|
26
|
+
"browser",
|
|
27
|
+
"identity",
|
|
28
|
+
"host",
|
|
29
|
+
"webext",
|
|
30
|
+
"agent",
|
|
31
|
+
"endpoint",
|
|
32
|
+
"localmodel",
|
|
33
|
+
"wallet",
|
|
34
|
+
]);
|
|
35
|
+
export type CapabilityKind = z.infer<typeof CapabilityKindSchema>;
|
|
36
|
+
export const CapabilityStateSchema = z.enum(["active", "pending", "error", "inactive"]);
|
|
37
|
+
export type CapabilityState = z.infer<typeof CapabilityStateSchema>;
|
|
38
|
+
// Per-kind config. Secrets (an mcp token) live here and are denylisted like tools.json.
|
|
39
|
+
export const McpConfigSchema = z.object({
|
|
40
|
+
url: z.url().describe("Where the tool server answers."),
|
|
41
|
+
token: z.string().optional().describe("The credential it needs, if any. Stored, never echoed back."),
|
|
42
|
+
});
|
|
43
|
+
export const ServiceConfigSchema = z.object({
|
|
44
|
+
service: ServiceKindSchema.describe("Which service to provision."),
|
|
45
|
+
domain: z.string().min(1).describe("The address it should answer on."),
|
|
46
|
+
on: z.string().min(1).describe("Which machine to put it on."),
|
|
47
|
+
expose: z.string().min(1).describe("How it should be reachable."),
|
|
48
|
+
});
|
|
49
|
+
// External-app credential injected into DEPLOYED apps (i.have.stripe → STRIPE_API_KEY from env). Agent-facing
|
|
50
|
+
// connectors are `cli` capabilities instead (see below), not integrations.
|
|
51
|
+
// Closed, unlike a `cli` provider: this becomes an `i.have.<provider>` entry in deploy.config.ts, and the
|
|
52
|
+
// desired-state resolver only knows the providers in InventoryProviderSchema. So an integration card is NOT
|
|
53
|
+
// extension-contributable, the vocabulary belongs to the deploy engine, not to a manifest.
|
|
54
|
+
export const IntegrationConfigSchema = z.object({
|
|
55
|
+
provider: z.literal("stripe").describe("Which outside service's credential to make available to deployed apps."),
|
|
56
|
+
});
|
|
57
|
+
// A `cli` capability gives the AGENT an authenticated command-line tool (not a deployed-app credential like
|
|
58
|
+
// `integration`): the credential + any non-secret URL are stored here and injected into the agent's env each
|
|
59
|
+
// turn (see cliEnvOf), and an .agents/skills/<id> cheatsheet teaches the agent to use it via curl. The provider
|
|
60
|
+
// data (fields, env, skill, image fragment) is DATA in an installed extension's `contributes.capabilities`, not
|
|
61
|
+
// a per-provider schema arm, so the config is `provider` + arbitrary string fields, validated against the
|
|
62
|
+
// card's declared fields at add-time (see the sandbox's capabilities/contributions.ts) rather than by this schema.
|
|
63
|
+
export const CliConfigSchema = z
|
|
64
|
+
.object({
|
|
65
|
+
provider: z
|
|
66
|
+
.string()
|
|
67
|
+
.min(1)
|
|
68
|
+
.describe(
|
|
69
|
+
"Which tool to give the agent. The rest of the fields are whatever that tool's own card declares it needs, and are checked against it when you connect.",
|
|
70
|
+
),
|
|
71
|
+
})
|
|
72
|
+
.catchall(z.string());
|
|
73
|
+
// A Claude Code plugin from a git repo. The daemon only owns the checkout; the Agent SDK's plugin loader reads
|
|
74
|
+
// its internals (skills/agents/hooks/commands/.mcp.json). `path` = subdirectory for plugins that live inside a
|
|
75
|
+
// marketplace/monorepo checkout. `token` = https auth for private repos (never echoed; becomes hasToken).
|
|
76
|
+
export const PluginConfigSchema = z.object({
|
|
77
|
+
url: z.url().describe("The repository to take the plugin from."),
|
|
78
|
+
// Branch / tag / commit sha to pin; absent = the default branch's HEAD.
|
|
79
|
+
ref: z.string().min(1).optional().describe("A branch, tag or commit to pin to. Leave it out to follow the default branch."),
|
|
80
|
+
path: z
|
|
81
|
+
.string()
|
|
82
|
+
.min(1)
|
|
83
|
+
.refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("Where inside the repository the plugin lives, for one that sits in a larger checkout."),
|
|
86
|
+
token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
|
|
87
|
+
});
|
|
88
|
+
// An intentic extension from a git repo (an intentic-extension.json checkout. UI bundle + agent contributions
|
|
89
|
+
// + processes). Unlike `plugin`, `ref` is a REQUIRED full commit sha: extension code runs trusted in the
|
|
90
|
+
// owner's browser, so the owner approves exactly the code that runs, pin by construction, updates are explicit
|
|
91
|
+
// re-adds at a new sha. `path`/`token` as in PluginConfigSchema.
|
|
92
|
+
export const ExtensionConfigSchema = z.object({
|
|
93
|
+
url: z.url().describe("The repository to take the extension from."),
|
|
94
|
+
ref: z
|
|
95
|
+
.string()
|
|
96
|
+
.regex(/^[0-9a-f]{40}$/, "ref must be a full 40-character commit sha")
|
|
97
|
+
.describe(
|
|
98
|
+
"The exact commit to install, in full. Required rather than optional because extension code runs with your browser's trust: the owner approves precisely the code that runs, and an update is a deliberate re-install at a new commit.",
|
|
99
|
+
),
|
|
100
|
+
path: z
|
|
101
|
+
.string()
|
|
102
|
+
.min(1)
|
|
103
|
+
.refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Where inside the repository the extension lives, for one that sits in a larger checkout."),
|
|
106
|
+
token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
|
|
107
|
+
/* The registry row's tier, copied onto the install by the browse pre-fill. `premium` is what the daemon's
|
|
108
|
+
* two pool duties key off: installing (or updating) donates the owner's credits to the publisher, the
|
|
109
|
+
* gate the apply passes through, and enabling needs the owner's membership. An absent tier means free,
|
|
110
|
+
* donates nothing, and asks for nothing; NO usage is metered or reported either way. Self-declared rather
|
|
111
|
+
* than verified against the registry (the daemon is the owner's own machine; a stripped marker skips a
|
|
112
|
+
* donation the owner was choosing to make, which cheats the creator once, and is exactly the honesty the
|
|
113
|
+
* open-source posture accepts and the docs state). */
|
|
114
|
+
tier: z
|
|
115
|
+
.enum(["free", "premium"])
|
|
116
|
+
.optional()
|
|
117
|
+
.describe(
|
|
118
|
+
"Whether installing this donates credits to its publisher. Absent means free, which donates nothing and asks for nothing. Taken from the listing rather than checked against it, which is the honesty an open-source posture accepts.",
|
|
119
|
+
),
|
|
120
|
+
/* The registry this install's row lives in, copied on by the browse pre-fill like `tier`, what the update
|
|
121
|
+
* check compares the pinned sha against and reads advisories from. Absent (a hand-typed git install) falls
|
|
122
|
+
* back to the official registry: if the extension is listed there, its updates and its blocked-markings
|
|
123
|
+
* concern this owner exactly as much as anyone's. */
|
|
124
|
+
registry: z
|
|
125
|
+
.url()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe(
|
|
128
|
+
"Which registry this install came from, which is what update checks and security advisories are read against. Absent falls back to the official one.",
|
|
129
|
+
),
|
|
130
|
+
});
|
|
131
|
+
// A remote machine the AGENT can reach over SSH. One capability = one machine; the id is its ssh-config Host
|
|
132
|
+
// alias, so the agent runs `ssh <id> "…"`. The handler writes a per-machine config block + a 0600 key/password
|
|
133
|
+
// file under ~/.ssh (see the ssh handler), so, unlike `cli`, nothing is injected into the agent's env, and
|
|
134
|
+
// several machines never collide. Discriminated by auth so exactly one credential shape is required.
|
|
135
|
+
export const SshConfigSchema = z.discriminatedUnion("auth", [
|
|
136
|
+
z.object({
|
|
137
|
+
auth: z.literal("key").describe("Sign in with a key."),
|
|
138
|
+
host: z.string().min(1).describe("The machine's address."),
|
|
139
|
+
port: z.coerce.number().default(22).describe("Which port it listens on."),
|
|
140
|
+
user: z.string().min(1).describe("Which user to connect as."),
|
|
141
|
+
privateKey: z.string().min(1).describe("The private key, whole. Stored with tight permissions and never echoed back."),
|
|
142
|
+
}),
|
|
143
|
+
z.object({
|
|
144
|
+
auth: z.literal("password").describe("Sign in with a password."),
|
|
145
|
+
host: z.string().min(1).describe("The machine's address."),
|
|
146
|
+
port: z.coerce.number().default(22).describe("Which port it listens on."),
|
|
147
|
+
user: z.string().min(1).describe("Which user to connect as."),
|
|
148
|
+
password: z.string().min(1).describe("The password. Stored, never echoed back."),
|
|
149
|
+
}),
|
|
150
|
+
]);
|
|
151
|
+
/* What is OPTIONAL about the in-sandbox Docker Engine. The engine itself takes no configuring, the capability
|
|
152
|
+
* either runs dockerd or it doesn't, so this holds only what a user chooses, and the bar for landing here is
|
|
153
|
+
* that the sandbox works without it. (`--privileged` therefore is not here and never will be: dockerd does not
|
|
154
|
+
* run without it, so a switch would offer a broken sandbox as a choice.)
|
|
155
|
+
*
|
|
156
|
+
* TWO FAMILIES, and which one an option belongs to is the most consequential thing about it, because it is the
|
|
157
|
+
* difference between a five-second change and a five-minute one:
|
|
158
|
+
*
|
|
159
|
+
* IMAGE (`gpu`), rides the environment overlay. Changing it recomposes the Dockerfile, so it costs an
|
|
160
|
+
* owner-approved rebuild and a container recreate. Only `fragment()` may read these.
|
|
161
|
+
* ENGINE (everything below it): /etc/docker/daemon.json, which dockerd reads at start. Changing one
|
|
162
|
+
* rewrites the file and restarts dockerd: no rebuild, no new image, but it DOES stop whatever containers
|
|
163
|
+
* the engine is running, which is why it is disclosed rather than silently applied.
|
|
164
|
+
*
|
|
165
|
+
* Keep the split honest in both directions: an engine option that leaked into the fragment would demand a
|
|
166
|
+
* rebuild for a value dockerd re-reads anyway, and an image option applied by rewriting a file would silently
|
|
167
|
+
* do nothing. The card badges the difference per field (CapabilityField.rebuild).
|
|
168
|
+
*
|
|
169
|
+
* Flat rather than nested, and "on"/"off" rather than booleans, because the capability form carries a flat
|
|
170
|
+
* bag of strings, one spelling of a two-state config across the manifest (the vpn's pfs/aggressive) beats a
|
|
171
|
+
* second one for the same shape. */
|
|
172
|
+
export const DockerConfigSchema = z.object({
|
|
173
|
+
gpu: z.enum(["on", "off"]).default("off"),
|
|
174
|
+
/* A pull-through cache or mirror, for a slow, metered or air-gapped link. The nested engine starts with an
|
|
175
|
+
* empty image store, so the first `docker compose up` in a workspace pulls everything from scratch. */
|
|
176
|
+
registryMirror: z.url().optional(),
|
|
177
|
+
// Registries reachable over plain http or with a self-signed certificate, a LAN registry, or the one a
|
|
178
|
+
// homelab runs beside the sandbox. Space- or comma-separated host:port entries.
|
|
179
|
+
insecureRegistries: z.string().optional(),
|
|
180
|
+
/* The subnet the nested engine carves its container networks out of. Docker's default (172.17/16 and the
|
|
181
|
+
* 172.16/12 pools around it) is the single most common collision with a corporate VPN or a homelab LAN,
|
|
182
|
+
* and the failure it produces is unusually cruel: the sandbox keeps working, dockerd keeps working, and
|
|
183
|
+
* exactly the internal hosts the user was reaching for become unreachable, routed into a bridge instead
|
|
184
|
+
* of down the tunnel. One CIDR, and the pool is carved from it. */
|
|
185
|
+
addressPool: z.string().optional(),
|
|
186
|
+
});
|
|
187
|
+
// A logged-in browser session the AGENT drives via Playwright MCP tools, for social platforms whose APIs can't
|
|
188
|
+
// cover "all the actions" (X reads are paywalled; X community-join and YouTube community-posts have no API). The
|
|
189
|
+
// session lives in a persisted Chromium profile under .intentic/local/browser/<id>, established through the guided-login
|
|
190
|
+
// WebSocket (/system/browser-login) or by the agent signing in itself. Chromium itself rides this kind's
|
|
191
|
+
// Dockerfile fragment, applied on an owner rebuild.
|
|
192
|
+
//
|
|
193
|
+
// ONE CAPABILITY = ONE ACCOUNT, not one platform: several entries may name the same `platform` (reddit-work and
|
|
194
|
+
// reddit-personal), and the ID is what the profile, the login, the passkey and the agent's tool prefix are all
|
|
195
|
+
// keyed by, so each account signs in separately and is disconnected on its own.
|
|
196
|
+
//
|
|
197
|
+
// `platform` is an OPEN slug, not an enum, for the reason `cli`'s `provider` is: a platform is a card, a login URL
|
|
198
|
+
// and a skill in an installed extension's `contributes.capabilities`, so the set of them is not a fact this
|
|
199
|
+
// contract can know. The add route validates it against the contributed entry instead (see contributions.ts).
|
|
200
|
+
//
|
|
201
|
+
// `username`/`password` are the account's SIGN-IN CREDENTIALS, on every card rather than declared per platform
|
|
202
|
+
// (which box a login form wants filled is the same fact everywhere). Both optional: a profile that signed in by
|
|
203
|
+
// hand needs neither, and the password is the entry's SECRET, stored so the daemon can type it into the page on
|
|
204
|
+
// the agent's behalf (the accounts tools), never so the agent can read it. When the agent signs UP it has the
|
|
205
|
+
// daemon generate and store one here, so the credential outlives the profile's cookies.
|
|
206
|
+
//
|
|
207
|
+
// `catchall`, the `cli` precedent, for the card that carries no site at all: a GENERIC browser session, where the
|
|
208
|
+
// page to open and what the account is for are answered on the form instead of pinned in a manifest. A site card
|
|
209
|
+
// pins its URLs and declares no fields; the generic one declares fields and pins nothing, one kind, because
|
|
210
|
+
// nothing downstream of the URLs differs. Which other keys are legal is the CARD's business, checked against its
|
|
211
|
+
// declared fields at add-time (validateContributionConfig), not this schema's.
|
|
212
|
+
//
|
|
213
|
+
// `identity` names the identity capability this account was born from (or was filed under): the account then
|
|
214
|
+
// lives INSIDE that identity's browser, one profile, one set of cookies, which is what makes "Continue with
|
|
215
|
+
// Google" one click instead of a second Google login the platform would block. Absent ⇒ the account keeps its
|
|
216
|
+
// own private profile, exactly as every hand-connected account always has.
|
|
217
|
+
//
|
|
218
|
+
// `purpose` and `openedAt` are the ACCOUNT's own history, core for the same reason `identity` is: what this
|
|
219
|
+
// account was opened for and when are facts about the sandbox's own past, not about any site, and a site card
|
|
220
|
+
// that declared no fields (every one of them, a pinned-URL card declares none) could not carry them otherwise.
|
|
221
|
+
// They are what makes the roster answerable months later, when "do we already have an account here" is asked by
|
|
222
|
+
// a session that was not the one that signed up. Both optional: an account the owner connected by hand has no
|
|
223
|
+
// signup story to tell, and an empty purpose is better than a fabricated one.
|
|
224
|
+
export const BrowserConfigSchema = z
|
|
225
|
+
.object({
|
|
226
|
+
platform: z.string().min(1),
|
|
227
|
+
username: z.string().optional(),
|
|
228
|
+
password: z.string().optional(),
|
|
229
|
+
identity: z.string().optional(),
|
|
230
|
+
purpose: z.string().optional(),
|
|
231
|
+
// ISO-8601 date, stamped when the agent opens the account, absent for one connected by hand.
|
|
232
|
+
openedAt: z.string().optional(),
|
|
233
|
+
/* WHERE THIS ACCOUNT BROWSES FROM: the id of an `exit` capability. Set it and every page this profile
|
|
234
|
+
* opens comes out of that country, with the browser's clock, locale and languages set to match.
|
|
235
|
+
*
|
|
236
|
+
* Only meaningful on an account that owns its OWN profile. An account born from an identity shares
|
|
237
|
+
* that identity's browser, cookies, passkeys and all, so it shares its exit too and this field is
|
|
238
|
+
* ignored for it (see the daemon's browser/browser-exit.ts). That is not a limitation, it is the
|
|
239
|
+
* point: one Google session appearing from Berlin in one tab and Osaka in another is a far louder
|
|
240
|
+
* signal than any address, so the exit belongs to whatever owns the profile. */
|
|
241
|
+
exit: z.string().optional(),
|
|
242
|
+
})
|
|
243
|
+
.catchall(z.string());
|
|
244
|
+
/* ONE EMAIL IDENTITY THE SANDBOX ACTS AS ONLINE, the container platform accounts are born from, and the answer
|
|
245
|
+
* to "who is this sandbox on the internet" being twelve separate logins today.
|
|
246
|
+
*
|
|
247
|
+
* WHAT IT OWNS IS A BROWSER. An identity is one persisted Chromium profile the way a person's own browser is
|
|
248
|
+
* one: Google signed in once (by the OWNER's hand, in the guided window, automated Google logins are exactly
|
|
249
|
+
* what Google blocks), and every account born from it sharing those cookies, so a platform's "Continue with
|
|
250
|
+
* Google" is a click rather than an email round-trip. Browser accounts join it by naming it in their `identity`
|
|
251
|
+
* field; accounts that name no identity keep their own private profile, which is how work and personal stay two
|
|
252
|
+
* containers, two identities, not one profile with a flag.
|
|
253
|
+
*
|
|
254
|
+
* WHY A CAPABILITY AND NOT A PERSONA: this card holds SECRETS (an email password the daemon types but never
|
|
255
|
+
* shows) and a live profile's identity, and the personas file is committed to git precisely because it holds
|
|
256
|
+
* neither (personas-store.ts). A persona is how a session BEHAVES; an identity is who the browser IS SIGNED IN
|
|
257
|
+
* as. A persona may point at accounts that live inside an identity, and neither card needs to know the other
|
|
258
|
+
* exists.
|
|
259
|
+
*
|
|
260
|
+
* `email` is the identity itself, what signup forms get typed into their username box, and how the guided
|
|
261
|
+
* login knows where to start (gmail.com ⇒ accounts.google.com; `loginUrl` overrides for any other provider).
|
|
262
|
+
* `password` is the entry's SECRET, the browser-config precedent: typed by the daemon, never readable.
|
|
263
|
+
* `mailbox` names a connected mail capability (imap, google) the narrow code tool reads, the agent asks for
|
|
264
|
+
* "the latest code from this site" and gets six digits, not an inbox.
|
|
265
|
+
* `openAccounts` is THE consent switch, off by default and a select rather than a boolean (the host-scope
|
|
266
|
+
* precedent, form values arrive as strings): automated signup is against most platforms' terms, so minting
|
|
267
|
+
* accounts unattended is an explicit, per-identity, informed choice, never a silent global default. */
|
|
268
|
+
export const IdentityConfigSchema = z.object({
|
|
269
|
+
email: z.string().min(3),
|
|
270
|
+
password: z.string().optional(),
|
|
271
|
+
mailbox: z.string().optional(),
|
|
272
|
+
loginUrl: z.url().optional(),
|
|
273
|
+
openAccounts: z.enum(["on", "off"]).default("off"),
|
|
274
|
+
/* WHERE THIS IDENTITY LIVES, the id of an `exit` capability. An identity OWNS a browser profile, and every
|
|
275
|
+
* account born from it shares that profile, so setting it here sets it for all of them at once, which is
|
|
276
|
+
* the only coherent place to set it: the shared thing is one browser, and one browser is in one place. */
|
|
277
|
+
exit: z.string().optional(),
|
|
278
|
+
});
|
|
279
|
+
export type IdentityConfig = z.infer<typeof IdentityConfigSchema>;
|
|
280
|
+
/* A connected COMPUTER of the user's own, the inverse of `ssh`, which reaches a server the sandbox can dial.
|
|
281
|
+
* A machine behind NAT can't be dialled, so it dials US: the @intentic/machine agent (installed by a one-liner,
|
|
282
|
+
* enrolled with a single-use pairing token) holds one outbound WebSocket to this daemon and serves an MCP tool
|
|
283
|
+
* surface, shell, files, screenshots, from the far end. The daemon tunnels the agent's JSON-RPC over it and
|
|
284
|
+
* never implements a tool itself, so the machine's capabilities evolve with ITS binary, not with a daemon release.
|
|
285
|
+
*
|
|
286
|
+
* One capability = one machine. The id is the machine's name and namespaces its tools (mcp__laptop__run_command),
|
|
287
|
+
* so several connected machines never collide, the `ssh` precedent. `platform` splits the SKILL pack: a Windows
|
|
288
|
+
* machine is taught PowerShell and a Linux one systemd/D-Bus, and neither carries the other's noise.
|
|
289
|
+
*
|
|
290
|
+
* SCOPES ARE THE GRANT, and they are enforced ON THE MACHINE, never here: the daemon pushes them down on every
|
|
291
|
+
* connect, and the agent refuses out-of-scope calls itself. So a sandbox that is compromised, or an agent talked
|
|
292
|
+
* into it by something it read on the internet, still cannot exceed what the owner ticked. `roots` bounds file
|
|
293
|
+
* reads AND writes to a set of directories (empty ⇒ the user's home).
|
|
294
|
+
*
|
|
295
|
+
* Like a browser `platform`, this is an OPEN slug: an OS is a card plus a skill pack in an installed extension's
|
|
296
|
+
* `contributes.capabilities`, and teaching the agent a new one should not need a daemon release. */
|
|
297
|
+
// on/off rather than a boolean: capability configs arrive from the add form as strings (the vpn autoConnect
|
|
298
|
+
// precedent), and a select is what the form renders for an enum.
|
|
299
|
+
const hostScope = z.enum(["on", "off"]);
|
|
300
|
+
export const HostScopesSchema = z.object({
|
|
301
|
+
// Run commands in a real shell (PowerShell on Windows, the login shell on Linux). Off ⇒ files/screen only.
|
|
302
|
+
shell: hostScope.default("on"),
|
|
303
|
+
// Create, modify and trash files under `roots`. Reads are always allowed within them; this is the write half.
|
|
304
|
+
write: hostScope.default("off"),
|
|
305
|
+
// Capture the screen. Off ⇒ screenshot refuses, and the agent is told so rather than getting a black frame.
|
|
306
|
+
screen: hostScope.default("on"),
|
|
307
|
+
/* Move the pointer, click, type and scroll. GUI work, for the things with no command-line way in. Its own
|
|
308
|
+
* switch rather than part of `screen` because looking and touching are not the same permission: a screenshot
|
|
309
|
+
* is bounded by what is on the display, while one click can confirm a dialog nobody read. Default off, like
|
|
310
|
+
* `write`, and for the same reason, a user who has not thought about it should not discover the agent has
|
|
311
|
+
* been driving their desktop. */
|
|
312
|
+
control: hostScope.default("off"),
|
|
313
|
+
/* Start, stop and restart the Intentic sandboxes running on this machine, the grant that makes one sandbox
|
|
314
|
+
* the machine's supervisor. Its own switch rather than a use of `shell` because it is NARROWER: a user can
|
|
315
|
+
* hand an agent the sandbox fleet without handing it a shell, and the fleet operations are named rather than
|
|
316
|
+
* whatever a model improvises with docker. Default off, like every switch that changes the machine. */
|
|
317
|
+
sandboxes: hostScope.default("off"),
|
|
318
|
+
/* Remove a sandbox from this machine, its container, its network, and the named volumes holding its /work
|
|
319
|
+
* and /history. Its own switch rather than part of `sandboxes` because the two differ in the only way that
|
|
320
|
+
* matters here: everything `sandboxes` grants is undone by doing it again, and this is undone by nothing.
|
|
321
|
+
* A user who delegated "restart my sandboxes when they wedge" did not thereby agree to lose one. */
|
|
322
|
+
sandboxRemove: hostScope.default("off"),
|
|
323
|
+
/* Run a command this machine's agent classifies as destructive: a recursive delete, a formatted disk, a
|
|
324
|
+
* removed Docker volume (command-classes.ts, the same classifier the sandbox's own gate reads).
|
|
325
|
+
*
|
|
326
|
+
* ITS OWN SWITCH UNDER `shell`, and the reason is the asymmetry this whole feature turns on. Inside the
|
|
327
|
+
* sandbox a bad `rm -rf` costs a container that exists to be thrown away, so the gate there can afford to
|
|
328
|
+
* hold only the handful of commands nothing undoes and wave the rest through. This is somebody's laptop.
|
|
329
|
+
* There is no image to recreate it from, no checkpoint, no worktree: `rm -rf ~/projects` is the afternoon
|
|
330
|
+
* everybody remembers. And there is no card to raise either, the machine answers a tool call with a value
|
|
331
|
+
* and cannot park it while somebody thinks, so the honest form of "ask me" here is "refuse until they
|
|
332
|
+
* ticked it", which is exactly what a scope is.
|
|
333
|
+
*
|
|
334
|
+
* Default off, with `shell` default ON, which is the pairing to read carefully: a connected computer runs
|
|
335
|
+
* commands out of the box, because that is what people connect one for, and the ones that delete are the
|
|
336
|
+
* ones they have to say yes to. */
|
|
337
|
+
destructive: hostScope.default("off"),
|
|
338
|
+
// One directory per line. Empty ⇒ the machine's home directory, which is what the agent reports at connect.
|
|
339
|
+
roots: z.string().optional(),
|
|
340
|
+
});
|
|
341
|
+
export type HostScopes = z.infer<typeof HostScopesSchema>;
|
|
342
|
+
export const HostConfigSchema = HostScopesSchema.extend({ platform: z.string().min(1) });
|
|
343
|
+
/* THE USER'S OWN BROWSER, reached through the extension they installed in it: the `webext` capability's config.
|
|
344
|
+
*
|
|
345
|
+
* The sibling of `host` and deliberately not an arm of it. A connected computer runs commands; a connected
|
|
346
|
+
* browser has one power a sandbox's own Chromium can never have, and it is the whole reason this kind exists:
|
|
347
|
+
* it is ALREADY SIGNED IN, as the person, with their passkeys, their hardware second factor, their corporate
|
|
348
|
+
* SSO and their genuine fingerprint. That is the set of sites the sandbox's browser cannot reach at all, and
|
|
349
|
+
* copying a session out of one to fake it is what gets an account locked.
|
|
350
|
+
*
|
|
351
|
+
* WHICH SITES the agent may touch is NOT in here, and that omission is the security design rather than a gap.
|
|
352
|
+
* Origins are Chrome's own optional host permissions, asked for by the extension with the person's hands on
|
|
353
|
+
* the keyboard and revocable in the browser's own UI, so the boundary that matters is enforced by the browser
|
|
354
|
+
* against the extension, one layer below anything a sandbox could reach. What the card carries is the coarser
|
|
355
|
+
* question the sandbox's owner answers once: what KIND of thing may happen on a site they have already allowed.
|
|
356
|
+
*
|
|
357
|
+
* Every switch is enforced IN THE EXTENSION (as every host scope is enforced on the machine): the daemon
|
|
358
|
+
* pushes them on connect and on every edit, and nothing on this side checks one. */
|
|
359
|
+
const webextScope = z.enum(["on", "off"]);
|
|
360
|
+
export const WebExtScopesSchema = z.object({
|
|
361
|
+
// Read a granted page: its elements, its text, its tabs. The floor of usefulness, so it defaults on; with
|
|
362
|
+
// it off the connection is inert and the card says so rather than pretending.
|
|
363
|
+
read: webextScope.default("on"),
|
|
364
|
+
/* Click, type, press keys, navigate. ON by default, unlike a computer's `control`, and the difference is
|
|
365
|
+
* what the two things ARE: driving a desktop is the last resort after every command-line route failed,
|
|
366
|
+
* while driving the page IS this connector — a browser connection that may only look is a worse version
|
|
367
|
+
* of fetching the URL. The grant that actually bounds it is per-site and lives in the browser. */
|
|
368
|
+
act: webextScope.default("on"),
|
|
369
|
+
/* Capture the visible tab as an image. Off by default because it is the one read whose contents nothing
|
|
370
|
+
* here bounds: the page serialization above is a list this extension built and can keep to granted frames,
|
|
371
|
+
* while a screenshot is whatever pixels that window happens to be showing. Worth turning on for canvas
|
|
372
|
+
* apps and PDF viewers, which is exactly when the DOM says nothing. */
|
|
373
|
+
screenshot: webextScope.default("off"),
|
|
374
|
+
/* Hand a site's logged-in session to the sandbox's own browser ("Connect this site"), so a job can carry
|
|
375
|
+
* on overnight with the laptop shut. Off by default and deliberately hard to turn on by accident: it is
|
|
376
|
+
* the only switch here that COPIES a credential rather than borrowing the browser holding it, and some
|
|
377
|
+
* sites answer a session arriving from a new fingerprint by invalidating it. */
|
|
378
|
+
cookies: webextScope.default("off"),
|
|
379
|
+
/* When the person is asked, in the browser, before an action goes through. "sensitive" is the default and
|
|
380
|
+
* the interesting one: a submit on a page that carries a password field, a payment form or a delete
|
|
381
|
+
* confirmation waits for a human click; ordinary navigation and typing do not. "always" makes every action
|
|
382
|
+
* a prompt (correct for a first week, exhausting after it); "never" is the owner saying they will watch
|
|
383
|
+
* instead, which they genuinely can, because they are looking at the tab. */
|
|
384
|
+
confirm: z.enum(["sensitive", "always", "never"]).default("sensitive"),
|
|
385
|
+
});
|
|
386
|
+
export type WebExtScopes = z.infer<typeof WebExtScopesSchema>;
|
|
387
|
+
// Like a host's, `platform` is an OPEN slug naming the card (chrome, firefox): the browser family decides the
|
|
388
|
+
// skill pack's wording and the install link, and teaching the agent a new one should not need a daemon release.
|
|
389
|
+
export const WebExtConfigSchema = WebExtScopesSchema.extend({ platform: z.string().min(1) });
|
|
390
|
+
export type WebExtConfig = z.infer<typeof WebExtConfigSchema>;
|
|
391
|
+
// An ACP (Agent Client Protocol) agent served as a chat provider: the daemon spawns `command` as a long-lived
|
|
392
|
+
// subprocess speaking JSON-RPC over stdio, and the capability id becomes the provider id in the chat picker
|
|
393
|
+
// (see AgentProviderSchema). `command` is split on whitespace, no shell quoting. `env` is a pasted KEY=VALUE
|
|
394
|
+
// block (one per line); credentials ride here, so the whole block is the secret field (echoed as hasSecret),
|
|
395
|
+
// the vpn-conf precedent. `loginCommand` is an interactive login the user completes in a visible terminal
|
|
396
|
+
// (device-code flows); the agent persists credentials in its own store inside the container. `name` is the
|
|
397
|
+
// picker's display label; absent = the id.
|
|
398
|
+
export const AcpAgentConfigSchema = z.object({
|
|
399
|
+
command: z.string().min(1),
|
|
400
|
+
name: z.string().min(1).optional(),
|
|
401
|
+
env: z.string().optional(),
|
|
402
|
+
loginCommand: z.string().min(1).optional(),
|
|
403
|
+
});
|
|
404
|
+
/* A MODEL API THE USER POINTED US AT, one shape for every server that serves models over HTTP, whether it runs
|
|
405
|
+
* beside this container or in another datacentre. There is deliberately NO local/remote axis: an Ollama on the
|
|
406
|
+
* docker host, a vLLM on the GPU box down the hall, a LiteLLM gateway and OpenRouter differ only in the URL, and
|
|
407
|
+
* inventing a distinction would mean two code paths, two cards and two sets of bugs for one concept.
|
|
408
|
+
*
|
|
409
|
+
* `protocol` is the only real fork, and it is about the WIRE, not about where the server lives:
|
|
410
|
+
* openai , the endpoint speaks OpenAI /v1/chat/completions (Ollama, vLLM, llama.cpp, LM Studio, TGI,
|
|
411
|
+
* OpenRouter, most gateways). The Claude Code harness speaks only the Anthropic Messages API, so
|
|
412
|
+
* these are re-served through the bundled translator, which is already in the image for exactly
|
|
413
|
+
* this job (agent/translator.ts). The user's key stays in the translator's config on /history and
|
|
414
|
+
* never reaches the harness, it gets the loopback bearer instead.
|
|
415
|
+
* anthropic, the endpoint already speaks the Anthropic Messages API (LiteLLM's /v1/messages, a Bedrock or
|
|
416
|
+
* Vertex router, a corporate Anthropic gateway). Nothing to translate: the harness is pointed
|
|
417
|
+
* straight at it with the user's own key.
|
|
418
|
+
*
|
|
419
|
+
* `headers` is a pasted `Name: value` block, one per line, the extra headers gateways ask for (a tenant id, a
|
|
420
|
+
* routing hint). The key is the secret field; the header block is not, because it is where non-credential
|
|
421
|
+
* routing metadata lives and hiding it would make a misrouted endpoint undiagnosable. */
|
|
422
|
+
export const EndpointProtocolSchema = z.enum(["openai", "anthropic"]);
|
|
423
|
+
export type EndpointProtocol = z.infer<typeof EndpointProtocolSchema>;
|
|
424
|
+
export const EndpointConfigSchema = z.object({
|
|
425
|
+
// The API root, INCLUDING the version segment the server publishes (…:11434/v1). Taken verbatim rather than
|
|
426
|
+
// normalised: "which suffix does this server want" is the one thing that actually varies between them, and
|
|
427
|
+
// guessing it is how a working URL becomes an unexplainable 404.
|
|
428
|
+
baseUrl: z.url(),
|
|
429
|
+
protocol: EndpointProtocolSchema.default("openai"),
|
|
430
|
+
apiKey: z.string().optional(),
|
|
431
|
+
headers: z.string().optional(),
|
|
432
|
+
});
|
|
433
|
+
/* A MODEL THE SANDBOX RUNS ITSELF, the managed counterpart of `endpoint`. An endpoint points at a server the
|
|
434
|
+
* USER operates; this one names weights, and the daemon does the operating: it downloads the file into the
|
|
435
|
+
* workspace cache, serves it with the image's bundled llama-server on a loopback port it owns, and registers
|
|
436
|
+
* the result exactly as if the user had added an endpoint at that port. Everything downstream (the picker, the
|
|
437
|
+
* translator, quick-model pinning) sees an `endpoint/<id>` provider and never learns the difference, which is
|
|
438
|
+
* why there is no baseUrl here: the URL is derived from the entry's id (the daemon's endpoints/local-model.ts),
|
|
439
|
+
* not a fact anyone typed.
|
|
440
|
+
*
|
|
441
|
+
* `model` is WHICH WEIGHTS, as a Hugging Face path (`owner/repo/file.gguf`, resolved to the repo's own
|
|
442
|
+
* download), so shipping a new recommended model is a catalog-card edit, not a daemon release. The reserved
|
|
443
|
+
* value "custom" defers to `url`, a direct GGUF link for people who know exactly what they want.
|
|
444
|
+
*
|
|
445
|
+
* `gpu` mirrors the docker card's option and rides the same allowlisted `--gpus=all` directive: the ASK lives
|
|
446
|
+
* here, what became of it is SANDBOX_GPU, stamped by the runner (see the docker handler's gpuState). "on"/"off"
|
|
447
|
+
* rather than a boolean for the manifest-wide reason DockerConfigSchema gives.
|
|
448
|
+
*
|
|
449
|
+
* `context`/`contextTokens` are HOW MUCH CONVERSATION the server holds, the `model`/`url` pair's shape for the
|
|
450
|
+
* same reason: a short list of rungs anyone can choose between, and one escape hatch for a person who knows the
|
|
451
|
+
* exact number they want. Resolved to a single token count in exactly one place (the daemon's
|
|
452
|
+
* endpoints/local-model.ts localModelWindow), because the flag llama-server is started with and the number the
|
|
453
|
+
* card promises must never be two opinions. */
|
|
454
|
+
export const LOCAL_MODEL_WINDOWS = ["16384", "32768", "65536", "131072"] as const;
|
|
455
|
+
export type LocalModelWindow = (typeof LOCAL_MODEL_WINDOWS)[number];
|
|
456
|
+
/* THE RUNG A CARD WITH NO OPINION LANDS ON, and the one number in this block that is a product decision rather
|
|
457
|
+
* than an arithmetic one.
|
|
458
|
+
*
|
|
459
|
+
* It is 65,536 because this sandbox runs a TOOL-CALLING AGENT LOOP, and that loop's own fixed cost, its
|
|
460
|
+
* instructions plus one JSON schema per tool it can call, times every capability the owner has connected, is
|
|
461
|
+
* tens of thousands of tokens before the user has typed anything (agent/context-budget.ts holds the measurement
|
|
462
|
+
* and the refusal built on it). A window that cannot hold that cost is not a smaller version of the product; it
|
|
463
|
+
* is a model whose every real turn is refused, which is what the previous flat 32,768 shipped: a 27B model,
|
|
464
|
+
* seventeen gigabytes downloaded, and a first message that died on `36216 tokens exceeds 32768`.
|
|
465
|
+
*
|
|
466
|
+
* So the default is the smallest rung a full turn fits in, and the smaller rungs stay on the list because they
|
|
467
|
+
* are honestly useful: pinned as the quick model (titles, commit messages) a window this size is waste, and the
|
|
468
|
+
* gigabyte it gives back is the difference between running one of these models on an eight-gigabyte laptop and
|
|
469
|
+
* not. What each rung costs in memory is the card's job to say (capability-catalog): roughly 2 GB of quantized
|
|
470
|
+
* cache per 32k of window, on top of the weights. */
|
|
471
|
+
export const LOCAL_MODEL_WINDOW_DEFAULT: LocalModelWindow = "65536";
|
|
472
|
+
/* THE BOUNDS ON THE TYPED NUMBER, and they are bounds against a TYPO rather than against a preference. Below
|
|
473
|
+
* the floor there is no conversation left to have once the loop's own instructions land; above the ceiling is a
|
|
474
|
+
* number no GGUF on offer was trained for, and llama-server would spend minutes reserving a cache for it before
|
|
475
|
+
* failing. Everything between is the owner's call: their machine, their memory. */
|
|
476
|
+
export const LOCAL_MODEL_WINDOW_MIN = 2048;
|
|
477
|
+
export const LOCAL_MODEL_WINDOW_MAX = 1_048_576;
|
|
478
|
+
export const LocalModelConfigSchema = z.object({
|
|
479
|
+
model: z.string().min(1),
|
|
480
|
+
gpu: z.enum(["on", "off"]).default("off"),
|
|
481
|
+
url: z.url().optional(),
|
|
482
|
+
context: z.union([z.enum(LOCAL_MODEL_WINDOWS), z.literal("custom")]).default(LOCAL_MODEL_WINDOW_DEFAULT),
|
|
483
|
+
// Coerced because it arrives from a text field as a string, the ssh card's `port` precedent, and only read
|
|
484
|
+
// when `context` is "custom" (the `url`/`model` relationship exactly).
|
|
485
|
+
contextTokens: z.coerce.number().int().min(LOCAL_MODEL_WINDOW_MIN).max(LOCAL_MODEL_WINDOW_MAX).optional(),
|
|
486
|
+
});
|
|
487
|
+
export type LocalModelConfig = z.infer<typeof LocalModelConfigSchema>;
|
|
488
|
+
/* THE SANDBOX WALLET, a USDC balance the agent can spend on x402-payable endpoints, under owner policy.
|
|
489
|
+
*
|
|
490
|
+
* WHAT IS DELIBERATELY NOT HERE IS A KEY. The signing key lives with the PLATFORM (one wallet per owner,
|
|
491
|
+
* reached with the connect token the agent's grant never covers), the container filesystem is explicitly not
|
|
492
|
+
* a boundary in this codebase's threat model (see the daemon's secret-vault.ts header), so the key does not
|
|
493
|
+
* enter the container at all. `address` is the wallet's PUBLIC address, written back by the handler's apply
|
|
494
|
+
* from the platform's answer, never typed by anyone: it is where the owner sends USDC, and everything the
|
|
495
|
+
* agent may know.
|
|
496
|
+
*
|
|
497
|
+
* POLICY IS THE OWNER'S DELEGATION, and its defaults are the conservative ones: every payment raises an
|
|
498
|
+
* approval card (`autoApproveUnderUsd: "0"`), bounded per payment and per UTC day. The daemon enforces it at
|
|
499
|
+
* the route AND the platform re-validates at the signer, the daemon's check is UX, the signer's is the
|
|
500
|
+
* guarantee, so a compromised container can at worst request what the owner already permitted. Amounts are
|
|
501
|
+
* DECIMAL STRINGS, never floats: the daemon does its arithmetic in the token's atomic units (USDC has six
|
|
502
|
+
* decimals), and a float here would be a rounding bug wearing a type.
|
|
503
|
+
*
|
|
504
|
+
* `allow`/`deny` are hostname lists (comma- or newline-separated). Empty allow = any host, each behind its
|
|
505
|
+
* card; deny wins over allow. One capability per sandbox (singleton card): a second balance would just be a
|
|
506
|
+
* second opinion about the same owner's wallet. */
|
|
507
|
+
const usdAmount = z.string().regex(/^\d+(\.\d{1,6})?$/, "a USD amount like 0.50 (up to six decimals: USDC's own precision)");
|
|
508
|
+
export const WalletNetworkSchema = z.enum(["eip155:8453", "eip155:84532"]);
|
|
509
|
+
export type WalletNetwork = z.infer<typeof WalletNetworkSchema>;
|
|
510
|
+
export const WalletConfigSchema = z.object({
|
|
511
|
+
// The chain payments settle on, CAIP-2. Base mainnet, or Base Sepolia for test mode (faucet USDC, the
|
|
512
|
+
// whole flow, cards, ledger, receipts, with zero real money).
|
|
513
|
+
network: WalletNetworkSchema.default("eip155:8453"),
|
|
514
|
+
// The wallet's public address, the platform's answer at apply time, never a form field.
|
|
515
|
+
address: z.string().optional(),
|
|
516
|
+
// Hard per-payment ceiling: over it the route refuses without raising a card.
|
|
517
|
+
perPaymentMaxUsd: usdAmount.default("1.00"),
|
|
518
|
+
// Payments at or under this settle without a card, inside the daily cap. "0" = every payment is carded.
|
|
519
|
+
autoApproveUnderUsd: usdAmount.default("0"),
|
|
520
|
+
// The UTC-day ceiling across all payments, carded or not.
|
|
521
|
+
dailyCapUsd: usdAmount.default("5.00"),
|
|
522
|
+
allow: z.string().optional(),
|
|
523
|
+
deny: z.string().optional(),
|
|
524
|
+
});
|
|
525
|
+
export type WalletConfig = z.infer<typeof WalletConfigSchema>;
|
|
526
|
+
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
527
|
+
export type ServiceConfig = z.infer<typeof ServiceConfigSchema>;
|
|
528
|
+
export type IntegrationConfig = z.infer<typeof IntegrationConfigSchema>;
|
|
529
|
+
export type CliConfig = z.infer<typeof CliConfigSchema>;
|
|
530
|
+
export type PluginConfig = z.infer<typeof PluginConfigSchema>;
|
|
531
|
+
export type ExtensionConfig = z.infer<typeof ExtensionConfigSchema>;
|
|
532
|
+
export type SshConfig = z.infer<typeof SshConfigSchema>;
|
|
533
|
+
export type DockerConfig = z.infer<typeof DockerConfigSchema>;
|
|
534
|
+
export type BrowserConfig = z.infer<typeof BrowserConfigSchema>;
|
|
535
|
+
export type HostConfig = z.infer<typeof HostConfigSchema>;
|
|
536
|
+
export type AcpAgentConfig = z.infer<typeof AcpAgentConfigSchema>;
|
|
537
|
+
export type EndpointConfig = z.infer<typeof EndpointConfigSchema>;
|
|
538
|
+
export const CapabilitySchema = z.discriminatedUnion("kind", [
|
|
539
|
+
z.object({ id: entryId, kind: z.literal("devops"), config: z.object({}) }),
|
|
540
|
+
// A pnpm+turbo monorepo the user scaffolds as its own repo; the `id` is the repo name. No config, apps are
|
|
541
|
+
// added into it afterwards from its operator panel.
|
|
542
|
+
z.object({ id: entryId, kind: z.literal("monorepo"), config: z.object({}) }),
|
|
543
|
+
z.object({ id: entryId, kind: z.literal("mcp"), config: McpConfigSchema }),
|
|
544
|
+
z.object({ id: entryId, kind: z.literal("service"), config: ServiceConfigSchema }),
|
|
545
|
+
z.object({ id: entryId, kind: z.literal("integration"), config: IntegrationConfigSchema }),
|
|
546
|
+
z.object({ id: entryId, kind: z.literal("cli"), config: CliConfigSchema }),
|
|
547
|
+
z.object({ id: entryId, kind: z.literal("plugin"), config: PluginConfigSchema }),
|
|
548
|
+
z.object({ id: entryId, kind: z.literal("extension"), config: ExtensionConfigSchema }),
|
|
549
|
+
z.object({ id: entryId, kind: z.literal("ssh"), config: SshConfigSchema }),
|
|
550
|
+
// No IFNAMSIZ cap on the id: the tunnel's interface name is DERIVED (see the daemon's vpn/vpn-paths.ts
|
|
551
|
+
// interfaceName) rather than being the id itself, so a descriptive name is free.
|
|
552
|
+
z.object({ id: entryId, kind: z.literal("vpn"), config: VpnConfigSchema }),
|
|
553
|
+
// A geo exit (ExitConfigSchema). Same interface-name derivation as vpn, and deliberately NOT a vpn arm:
|
|
554
|
+
// it routes nothing into the main table, so the full-tunnel warning the vpn kind carries stays true.
|
|
555
|
+
z.object({ id: entryId, kind: z.literal("exit"), config: ExitConfigSchema }),
|
|
556
|
+
// The in-sandbox Docker Engine (baked into the base image, dormant by default). Its `--privileged` runtime
|
|
557
|
+
// directive is not in the config and never will be: dockerd does not work without it (see the handler's
|
|
558
|
+
// isPrivileged), so a switch there would offer a broken sandbox as a choice. What IS optional lives in
|
|
559
|
+
// DockerConfigSchema. No remove, the engine's state (/var/lib/docker) and whatever runs on it make a
|
|
560
|
+
// silent de-privilege more destructive than useful.
|
|
561
|
+
z.object({ id: entryId, kind: z.literal("docker"), config: DockerConfigSchema }),
|
|
562
|
+
z.object({ id: entryId, kind: z.literal("browser"), config: BrowserConfigSchema }),
|
|
563
|
+
// One email identity the sandbox acts as online, the browser-owning container accounts are born from
|
|
564
|
+
// (IdentityConfigSchema). Browser entries join it via their `identity` field.
|
|
565
|
+
z.object({ id: entryId, kind: z.literal("identity"), config: IdentityConfigSchema }),
|
|
566
|
+
z.object({ id: entryId, kind: z.literal("host"), config: HostConfigSchema }),
|
|
567
|
+
// The user's own BROWSER, through the extension installed in it (WebExtConfigSchema). The `host` kind's
|
|
568
|
+
// sibling: one capability per browser, the id namespacing its tools, the switches enforced at the far end.
|
|
569
|
+
// Distinct from `browser`, which is the sandbox's OWN Chromium and a profile this container owns — this
|
|
570
|
+
// one is the person's, already signed into everything, and the sandbox only ever borrows it.
|
|
571
|
+
z.object({ id: entryId, kind: z.literal("webext"), config: WebExtConfigSchema }),
|
|
572
|
+
z.object({ id: entryId, kind: z.literal("agent"), config: AcpAgentConfigSchema }),
|
|
573
|
+
// A model API (EndpointConfigSchema). The id becomes `endpoint/<id>` in the chat picker, the `agent` kind's
|
|
574
|
+
// precedent, with the prefix because these two are the only capability kinds that mint providers and they
|
|
575
|
+
// want opposite ability records (an ACP agent owns its own loop; an endpoint runs the full Claude Code one).
|
|
576
|
+
z.object({ id: entryId, kind: z.literal("endpoint"), config: EndpointConfigSchema }),
|
|
577
|
+
// A model the sandbox downloads and serves itself (LocalModelConfigSchema). Deliberately minting the SAME
|
|
578
|
+
// `endpoint/<id>` provider ids as the endpoint kind: to every consumer it IS an endpoint, one the daemon
|
|
579
|
+
// happens to operate, so a second provider namespace would be a second code path for the same turns.
|
|
580
|
+
z.object({ id: entryId, kind: z.literal("localmodel"), config: LocalModelConfigSchema }),
|
|
581
|
+
// The sandbox's USDC wallet (WalletConfigSchema), one per sandbox; the key never enters the container.
|
|
582
|
+
z.object({ id: entryId, kind: z.literal("wallet"), config: WalletConfigSchema }),
|
|
583
|
+
]);
|
|
584
|
+
export type Capability = z.infer<typeof CapabilitySchema>;
|
|
585
|
+
/* `code` is a credential the owner has to TYPE SOMEWHERE ELSE to finish this connection. WhatsApp's
|
|
586
|
+
* link-a-device code, typed into the phone. It is not part of `detail` because the card does not merely print
|
|
587
|
+
* it: it sets it in a size you can read across a desk, next to a copy button, and replaces it in place when the
|
|
588
|
+
* provider mints a new one. A sentence with a code buried in it cannot be any of those things. */
|
|
589
|
+
export const CapabilityStatusSchema = z.object({
|
|
590
|
+
state: CapabilityStateSchema.describe("Whether it is live, still coming up, broken, or switched off."),
|
|
591
|
+
detail: z.string().optional().describe("What is wrong, in words a person can act on."),
|
|
592
|
+
code: z.string().optional().describe("A short marker for that reason, for anything deciding what to do about it."),
|
|
593
|
+
});
|
|
594
|
+
export type CapabilityStatus = z.infer<typeof CapabilityStatusSchema>;
|
|
595
|
+
/* The list row: manifest entry + live status. Secrets are never returned (an mcp token becomes hasToken).
|
|
596
|
+
*
|
|
597
|
+
* `secrets` NAMES them without carrying them, the config keys this connection is actually holding a credential
|
|
598
|
+
* under. It is what makes an edit form possible at all: `config` is everything the browser may see, so a form
|
|
599
|
+
* seeded from it alone cannot tell "this tunnel has a pre-shared key I'm not allowed to show you" from "this
|
|
600
|
+
* tunnel has no pre-shared key", and both render as an empty required box. Saving one then wipes the
|
|
601
|
+
* credential, which is why changing a routed network used to mean re-typing a key.
|
|
602
|
+
*
|
|
603
|
+
* Keys, never values, and never a boolean per known field: the set is derived from what the entry stores, so a
|
|
604
|
+
* field the user left blank is absent and a card that gained a credential since is present. The form reads it as
|
|
605
|
+
* "show dots, and let blank mean keep" (VAULTED, capability-secrets.ts). */
|
|
606
|
+
export const CapabilitySummarySchema = z.object({
|
|
607
|
+
id: z.string().describe("The connection's id."),
|
|
608
|
+
kind: CapabilityKindSchema.describe("What sort of thing it is."),
|
|
609
|
+
status: CapabilityStatusSchema.describe("Whether it is working."),
|
|
610
|
+
config: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).describe("Its settings, minus anything secret."),
|
|
611
|
+
// Defaulted for the daemon-older-than-browser seam, like `recommendations` below: a required field would
|
|
612
|
+
// fail the whole list parse against a sandbox predating this, taking the page down to hide some dots.
|
|
613
|
+
secrets: z
|
|
614
|
+
.array(z.string())
|
|
615
|
+
.default([])
|
|
616
|
+
.describe("Which credentials it holds, by name. The values are on one route only, and it is not this one."),
|
|
617
|
+
});
|
|
618
|
+
/* A capability the WORKSPACE asks for but the manifest doesn't carry, derived from what is checked out under
|
|
619
|
+
* /work, not from anything the user configured. It exists because the failures it prevents are illegible: a
|
|
620
|
+
* compose-backed dev database (`pnpm db:up`) dies on a missing /var/run/docker.sock, and nothing on that error
|
|
621
|
+
* points at the one-time privileged rebuild that fixes it; a workspace full of GitHub repos gets an agent that
|
|
622
|
+
* cannot read one issue until somebody thinks to go looking for the card.
|
|
623
|
+
*
|
|
624
|
+
* KEYED BY CATALOG CARD, NOT BY KIND, because github, gitlab, komodo and every other connector share the single
|
|
625
|
+
* `cli` kind, a kind cannot say which card to open, and matching on one badged all of them at once.
|
|
626
|
+
*
|
|
627
|
+
* WHAT IS STORED IS WHAT WAS SEEN. `evidence` is the artifact itself, a workspace-relative path, a git remote,
|
|
628
|
+
* rendered verbatim so the claim is checkable rather than believed, and `reason` is the same claim in the user's
|
|
629
|
+
* words with the evidence NOT repeated into it. A recommendation is re-derived on every read rather than
|
|
630
|
+
* remembered, so one whose evidence has since moved simply stops being made.
|
|
631
|
+
*
|
|
632
|
+
* `prefill` is the non-secret config the scan could read (a self-hosted instance url, a Komodo core), it fills
|
|
633
|
+
* the card's form so the user supplies only the credential. Secrets are NEVER in here, even when one is sitting
|
|
634
|
+
* in a checked-in file: the flow points at such a file as evidence, it does not absorb what is in it. */
|
|
635
|
+
export const CapabilityRecommendationSchema = z.object({
|
|
636
|
+
card: z.string().describe("Which connection is being suggested."),
|
|
637
|
+
evidence: z
|
|
638
|
+
.string()
|
|
639
|
+
.describe("What was seen that prompted it: a file, a remote, printed verbatim so the claim can be checked rather than believed."),
|
|
640
|
+
reason: z.string().describe("The same claim in words, without repeating the evidence into it."),
|
|
641
|
+
prefill: z
|
|
642
|
+
.record(z.string(), z.string())
|
|
643
|
+
.describe(
|
|
644
|
+
"Settings the scan could read, to fill the form so you supply only the credential. Never a secret, even when one is sitting in a checked-in file: the suggestion points at such a file, it does not absorb what is in it.",
|
|
645
|
+
),
|
|
646
|
+
});
|
|
647
|
+
export type CapabilityRecommendation = z.infer<typeof CapabilityRecommendationSchema>;
|
|
648
|
+
export const CapabilitiesListSchema = z.object({
|
|
649
|
+
capabilities: z.array(CapabilitySummarySchema).describe("What this sandbox is connected to."),
|
|
650
|
+
// Defaulted for the daemon-older-than-browser seam: the platform's web app talks to whichever sandbox
|
|
651
|
+
// version the user has, and a required field here would fail the parse, taking the whole Capabilities page
|
|
652
|
+
// down on every sandbox predating this route, to hide a badge.
|
|
653
|
+
recommendations: z
|
|
654
|
+
.array(CapabilityRecommendationSchema)
|
|
655
|
+
.default([])
|
|
656
|
+
.describe(
|
|
657
|
+
"Things worth connecting, worked out from what is actually in the workspace rather than from anything you configured. Re-derived on every read, so one whose evidence has moved simply stops being suggested.",
|
|
658
|
+
),
|
|
659
|
+
});
|
|
660
|
+
export const CapabilityIdParamSchema = z.object({ id: z.string().describe("Which connection.") });
|
|
661
|
+
/* One capability's config VERBATIM, secrets included, for the connection route (capabilities.connection).
|
|
662
|
+
* The one read on this surface that does not echo secrets as hasToken booleans, which is exactly why it is
|
|
663
|
+
* never served to a browser: its handler refuses any caller with a member identity, leaving only the daemon's
|
|
664
|
+
* header grants (an extension backend's minted token, which must declare the route in permissions.daemon).
|
|
665
|
+
* The values are the strings the capability stored; the caller knows its own kind's field names. */
|
|
666
|
+
export const CapabilityConnectionSchema = z.object({
|
|
667
|
+
id: z.string().describe("The connection's id."),
|
|
668
|
+
kind: z.string().describe("What sort of thing it is."),
|
|
669
|
+
config: z
|
|
670
|
+
.record(z.string(), z.string())
|
|
671
|
+
.describe("Its settings exactly as stored, credentials included. The field names are its own kind's, which the caller already knows."),
|
|
672
|
+
});
|
|
673
|
+
export type CapabilityConnection = z.infer<typeof CapabilityConnectionSchema>;
|
|
674
|
+
// DELETE /capabilities/recommendations/{card}: the user said this one is not wanted. The EVIDENCE it was
|
|
675
|
+
// declined against is recorded daemon-side rather than sent, so the client cannot dismiss a claim other than the
|
|
676
|
+
// one it was shown, and so the recommendation comes back by itself when the workspace changes under it.
|
|
677
|
+
export const CapabilityCardParamSchema = z.object({ card: z.string().describe("Which suggestion to stop making.") });
|
|
678
|
+
// POST /capabilities/{id}/secret body: replace just the capability's secret field (its key is per-kind, see the
|
|
679
|
+
// sandbox's secretField) and re-run its idempotent apply, the /secrets page's edit path.
|
|
680
|
+
export const CapabilitySecretInputSchema = z.object({
|
|
681
|
+
id: z.string().describe("Which connection."),
|
|
682
|
+
value: z.string().min(1).describe("The new credential. Its other settings are left alone."),
|
|
683
|
+
});
|
|
684
|
+
/* POST /capabilities/{id}/rename body: the name this connection should answer to from now on.
|
|
685
|
+
*
|
|
686
|
+
* A capability's id IS the agent's handle for it, its skill file, its tool prefix, its env suffix, the alias
|
|
687
|
+
* `ssh <name>` resolves, so renaming one is a migration and not a label edit. The shape of a name is therefore
|
|
688
|
+
* the same rule the add form enforces, spelled here because the daemon is the gate: letters and digits to start,
|
|
689
|
+
* then hyphens and underscores. Which KINDS may be renamed at all is the handler's own answer (capability.ts
|
|
690
|
+
* `rename`), not something a schema can say. */
|
|
691
|
+
export const CapabilityRenameSchema = z.object({
|
|
692
|
+
id: z.string(),
|
|
693
|
+
to: z
|
|
694
|
+
.string()
|
|
695
|
+
.min(1)
|
|
696
|
+
.max(60)
|
|
697
|
+
.regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/),
|
|
698
|
+
});
|
|
699
|
+
// POST /capabilities/{id}/login response: the interactive tmux session running the agent's loginCommand,
|
|
700
|
+
// which the web surfaces in the terminal panel for the user to complete the sign-in.
|
|
701
|
+
export const CapabilityLoginSchema = z.object({ session: z.string().describe("The terminal the sign-in is happening in. Attach to it to type.") });
|
|
702
|
+
// GET /capabilities/{id}/otp response: one freshly minted TOTP code off the capability's stored seed, what the
|
|
703
|
+
// in-sandbox `otp` command prints. The seed itself never crosses; secondsRemaining is the caller's cue to
|
|
704
|
+
// re-mint rather than submit a code about to die.
|
|
705
|
+
export const CapabilityOtpSchema = z.object({
|
|
706
|
+
code: z.string().describe("The code."),
|
|
707
|
+
secondsRemaining: z
|
|
708
|
+
.number()
|
|
709
|
+
.describe("How long it lasts. Its expiring is what makes handing one to an agent safe, since the seed behind it is never revealed."),
|
|
710
|
+
});
|
|
711
|
+
/* POST /capabilities/probe response: did these settings actually reach the thing, asked BEFORE they are saved.
|
|
712
|
+
*
|
|
713
|
+
* The answer is a sentence rather than a status code because the reader is standing in front of a form: what
|
|
714
|
+
* they need is either the service's own confirmation ("Reached GitHub, authenticated as ada") or the exact
|
|
715
|
+
* refusal ("GitHub answered 401: the token is not valid"), in the place where the box they would fix still is.
|
|
716
|
+
* That is also the whole point of doing it here: every one of these failures is otherwise discovered after the
|
|
717
|
+
* add, on a card that says "not connected" with nothing about which of six answers was wrong.
|
|
718
|
+
*
|
|
719
|
+
* `ok: false` is a REPORTED failure, not a transport error: the probe ran and the service said no. A card whose
|
|
720
|
+
* settings cannot be checked from here at all answers `checked: false`, which is a different thing from a
|
|
721
|
+
* failure and must never be drawn as one. */
|
|
722
|
+
export const CapabilityProbeSchema = z.object({
|
|
723
|
+
checked: z.boolean().describe("Whether this connection can be tested from here at all. False is not a failure: it is 'no test exists'."),
|
|
724
|
+
ok: z.boolean().describe("Whether the service answered as itself."),
|
|
725
|
+
message: z
|
|
726
|
+
.string()
|
|
727
|
+
.describe("What happened, in the words a person standing in front of the form needs: the service's own answer, or its refusal."),
|
|
728
|
+
});
|
|
729
|
+
export type CapabilityProbe = z.infer<typeof CapabilityProbeSchema>;
|