@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,141 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// The workspace-search wire shape, shared by the daemon's /workspace/search route and the web client.
|
|
3
|
+
// (Implementation detail, not part of the contract: the daemon backs this route with a resident in-process iq
|
|
4
|
+
// engine; the engine is interchangeable behind this shape.) Groups are relevance-ranked (best first, never path
|
|
5
|
+
// order); each hit carries the match-reason tags the fused engines contributed, and the char spans within `text`
|
|
6
|
+
// that matched, so clients highlight without re-finding the needle.
|
|
7
|
+
export const WorkspaceSearchQuerySchema = z.object({
|
|
8
|
+
query: z.string().min(2).max(512).describe("What to look for. Plain words, a pattern, a symbol name, or a question."),
|
|
9
|
+
// Search verbs only, anchor/git verbs (outline, context, log, who, …) are CLI-only surface. Natural language
|
|
10
|
+
// has no verb of its own: `q` classifies the query and answers it semantically when the words call for it.
|
|
11
|
+
mode: z
|
|
12
|
+
.enum(["q", "find", "files", "def", "refs", "sym", "ast"])
|
|
13
|
+
.optional()
|
|
14
|
+
.describe(
|
|
15
|
+
"Narrow the search to one kind: plain text, filenames, definitions, references, symbols, or code structure. Leave it out to blend them, which also answers a question asked in words.",
|
|
16
|
+
),
|
|
17
|
+
includeIgnored: z.stringbool().optional().describe("Search inside installed packages and other ignored folders too."),
|
|
18
|
+
// How `find` reads the query, the three switches every editor's search box has (VSCode: Aa, ab, .*).
|
|
19
|
+
// `literal` treats it as fixed text instead of a regex; `caseSensitive` off means case-INSENSITIVE, not
|
|
20
|
+
// ripgrep's smart case.
|
|
21
|
+
literal: z.stringbool().optional().describe("Treat the query as fixed text rather than a pattern."),
|
|
22
|
+
word: z.stringbool().optional().describe("Match whole words only."),
|
|
23
|
+
caseSensitive: z.stringbool().optional().describe("Whether capitals matter. Off means they do not, rather than being guessed at from the query."),
|
|
24
|
+
// Which FILES the query is asked of, in VSCode's files-to-include grammar, as TYPED, because the reading
|
|
25
|
+
// of it is shared (search-globs.ts) rather than each end guessing: comma-separated patterns, each matched
|
|
26
|
+
// at any depth unless `./` anchors it, a leading `!` excluding instead. Distinct from `includeIgnored`,
|
|
27
|
+
// which decides whether the ignored layers are searched at all, this narrows within what that admitted.
|
|
28
|
+
include: z
|
|
29
|
+
.string()
|
|
30
|
+
.max(512)
|
|
31
|
+
.optional()
|
|
32
|
+
.describe(
|
|
33
|
+
"Which files to ask, in the same grammar an editor's files-to-include box takes: comma-separated patterns, matched at any depth unless anchored, a leading exclamation mark excluding instead.",
|
|
34
|
+
),
|
|
35
|
+
limit: z.coerce.number().int().positive().optional().describe("How many results to return."),
|
|
36
|
+
after: z.string().optional().describe("Resume from the cursor a previous answer handed back."),
|
|
37
|
+
});
|
|
38
|
+
export const WorkspaceSearchTagSchema = z.object({
|
|
39
|
+
kind: z
|
|
40
|
+
.enum(["def", "text", "sem", "bm25", "rerank", "path", "import", "call", "type", "write", "fuzzy", "heuristic"])
|
|
41
|
+
.describe(
|
|
42
|
+
"Why this line matched: the literal text, its meaning, the path, a definition, a call, and so on. Several kinds can agree on one line.",
|
|
43
|
+
),
|
|
44
|
+
score: z.number().optional().describe("How strongly that reason applied."),
|
|
45
|
+
});
|
|
46
|
+
export type WorkspaceSearchTag = z.infer<typeof WorkspaceSearchTagSchema>;
|
|
47
|
+
export const WorkspaceSearchSpanSchema = z.object({
|
|
48
|
+
start: z.number().describe("First character of the match within the line."),
|
|
49
|
+
end: z.number().describe("One past the last."),
|
|
50
|
+
});
|
|
51
|
+
export type WorkspaceSearchSpan = z.infer<typeof WorkspaceSearchSpanSchema>;
|
|
52
|
+
export const WorkspaceSearchHitSchema = z.object({
|
|
53
|
+
line: z.number().describe("Which line, counting from one."),
|
|
54
|
+
text: z.string().describe("The line itself."),
|
|
55
|
+
// Every matched span in `text`, in order, a text search marks all of them, the way an editor does. Empty
|
|
56
|
+
// where the LINE is the match and no span of it is (a semantic or definition hit reports none).
|
|
57
|
+
spans: z
|
|
58
|
+
.array(WorkspaceSearchSpanSchema)
|
|
59
|
+
.describe(
|
|
60
|
+
"Where in the line the matches are, so you can highlight without searching again. Empty when the whole line is the match rather than part of it.",
|
|
61
|
+
),
|
|
62
|
+
tags: z.array(WorkspaceSearchTagSchema).describe("Why it matched."),
|
|
63
|
+
// Enclosing symbol ("createWidget (fn)"), parent-document context so the reader often needs no follow-up.
|
|
64
|
+
context: z
|
|
65
|
+
.string()
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("What it sits inside: the function, the class, the heading. Often enough that you need not open the file."),
|
|
68
|
+
});
|
|
69
|
+
export type WorkspaceSearchHit = z.infer<typeof WorkspaceSearchHitSchema>;
|
|
70
|
+
export const WorkspaceSearchGroupSchema = z.object({
|
|
71
|
+
path: z.string().describe("The file."),
|
|
72
|
+
score: z.number().describe("How well it matched. Groups arrive best first, never in path order."),
|
|
73
|
+
hits: z.array(WorkspaceSearchHitSchema).describe("The matching lines in it."),
|
|
74
|
+
// This file had more matching lines than the engine keeps per file, so `hits` is a floor, a panel showing a
|
|
75
|
+
// per-file count has to say "50+" rather than "50".
|
|
76
|
+
capped: z
|
|
77
|
+
.boolean()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("This file had more matches than are kept per file, so the count is a floor. Say fifty-plus rather than fifty."),
|
|
80
|
+
});
|
|
81
|
+
export type WorkspaceSearchGroup = z.infer<typeof WorkspaceSearchGroupSchema>;
|
|
82
|
+
// `building` = index still filling (progress 0..1, e.g. embeddings pending); `stale` = revalidation was skipped
|
|
83
|
+
// (cursor replay). ageMs = time since the index last matched the disk state.
|
|
84
|
+
export const WorkspaceSearchFreshnessSchema = z.object({
|
|
85
|
+
state: z.enum(["fresh", "building", "stale"]).describe("Whether the index matches what is on disk, is still filling, or has fallen behind."),
|
|
86
|
+
ageMs: z.number().optional().describe("How long since it last matched the disk, in milliseconds."),
|
|
87
|
+
progress: z.number().optional().describe("How far through building it is, from zero to one."),
|
|
88
|
+
// How many files the index has not caught up with, when it is stale. A count is reportable; "stale" alone
|
|
89
|
+
// reads as a warning about the answer, which it almost never is.
|
|
90
|
+
behind: z
|
|
91
|
+
.number()
|
|
92
|
+
.optional()
|
|
93
|
+
.describe(
|
|
94
|
+
"How many files it has not caught up with. Worth showing, because the word stale on its own reads as a warning about the answer, which it almost never is.",
|
|
95
|
+
),
|
|
96
|
+
});
|
|
97
|
+
export type WorkspaceSearchFreshness = z.infer<typeof WorkspaceSearchFreshnessSchema>;
|
|
98
|
+
export const WorkspaceSearchResultSchema = z.object({
|
|
99
|
+
mode: z.string().describe("Which kind of search actually ran, which matters when you let it choose."),
|
|
100
|
+
total: z.number().describe("Matching lines across the whole workspace, not just this page."),
|
|
101
|
+
// Files the query matched in total, which `groups` reports only for the page it carries, the count a
|
|
102
|
+
// results panel puts beside the hit total ("218 results in 61 files").
|
|
103
|
+
files: z.number().describe("Files the query matched in total."),
|
|
104
|
+
shown: z.number().describe("How many of those lines are on this page."),
|
|
105
|
+
groups: z.array(WorkspaceSearchGroupSchema).describe("The results, grouped by file, best first."),
|
|
106
|
+
freshness: WorkspaceSearchFreshnessSchema.describe("Whether the index behind the answer is up to date."),
|
|
107
|
+
truncated: z.boolean().describe("This page is not all of it. Use the cursor."),
|
|
108
|
+
// `total` is a FLOOR: at least one file had more matches than the engine keeps per file. Distinct from
|
|
109
|
+
// `truncated`, which is about this PAGE, a result can be complete on the page and still count partially.
|
|
110
|
+
partial: z
|
|
111
|
+
.boolean()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe(
|
|
114
|
+
"At least one file had more matches than are kept per file, so the total is a floor. Different from the page being truncated: a complete page can still count partially.",
|
|
115
|
+
),
|
|
116
|
+
cursor: z.string().optional().describe("Pass this back as `after` to get the next page."),
|
|
117
|
+
hint: z.string().optional().describe("A suggestion for getting a better answer out of this query."),
|
|
118
|
+
// What the engine did with the query that the query did not ask for, a pattern rerun as literal text
|
|
119
|
+
// because it is not valid regex, grep-style escapes rewritten, a language filter that matched no files. The
|
|
120
|
+
// text surface has always printed this above the results; a JSON caller could not see it at all.
|
|
121
|
+
note: z
|
|
122
|
+
.string()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe(
|
|
125
|
+
"What the engine did that you did not ask for: a pattern rerun as plain text because it was not valid, escapes rewritten, a language filter that matched nothing.",
|
|
126
|
+
),
|
|
127
|
+
// Code-graph neighbors of the top hits (definition anchors + the strongest caller of each).
|
|
128
|
+
related: z.array(z.string()).optional().describe("Places next door to the best results: where each is defined, and whatever calls it most."),
|
|
129
|
+
// Ranked `path:line` anchors that placed but were NOT shown, best first, the answer often sits at rank 5–13,
|
|
130
|
+
// behind groups the budget spent itself on. The text surface has always printed this map; a JSON caller could
|
|
131
|
+
// not see it, so it had to page through `cursor` to learn what the terminal was told up front.
|
|
132
|
+
candidates: z
|
|
133
|
+
.array(z.string())
|
|
134
|
+
.optional()
|
|
135
|
+
.describe(
|
|
136
|
+
"Ranked places that scored but did not make the page, best first. The answer often sits at rank five to thirteen, so this saves paging through to find out.",
|
|
137
|
+
),
|
|
138
|
+
// Run provenance for benchmarking: retrieval stages DISABLED this invocation (absent = full pipeline).
|
|
139
|
+
features: z.array(z.string()).optional().describe("Which stages of the search were switched off for this run. Absent means all of them ran."),
|
|
140
|
+
});
|
|
141
|
+
export type WorkspaceSearchResult = z.infer<typeof WorkspaceSearchResultSchema>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// workspace setup (dependency readiness)
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// One project under /work and whether its dependencies are actually installed. A drop omits node_modules/.venv
|
|
4
|
+
// on purpose, so a freshly imported project is present-but-unusable until this says "ready", the import UI,
|
|
5
|
+
// the agent's post-edit type-check, and the agent's turn context all gate on it.
|
|
6
|
+
// `dir` is root-relative ("" = the workspace root itself); `manager` is the real binary (pnpm/npm/uv/…);
|
|
7
|
+
// `evidence` is the file that decided it ("pnpm-lock.yaml"), so the UI can show WHY, not just what.
|
|
8
|
+
// state: ready | installing | needs-setup | unsupported (manager absent from this sandbox, `manager` names it)
|
|
9
|
+
// | stale, installed ONCE and since outgrown: the manifests declare dependencies that are not on disk,
|
|
10
|
+
// which is what an agent leaves behind when it adds one and does not install it. Same command fixes it,
|
|
11
|
+
// so `missing` (how many names cannot resolve) is what separates the two in the UI's wording.
|
|
12
|
+
export const ProjectSetupSchema = z.object({
|
|
13
|
+
dir: z.string().describe("Where the project is, relative to the workspace root. Empty means the root itself."),
|
|
14
|
+
ecosystem: z.enum(["node", "python"]).describe("Which language's tooling it uses."),
|
|
15
|
+
manager: z.string().describe("The tool that would do the installing."),
|
|
16
|
+
command: z.string().describe("The exact command that would run."),
|
|
17
|
+
evidence: z.string().describe("The file that decided all of the above, so the answer can be checked rather than trusted."),
|
|
18
|
+
state: z
|
|
19
|
+
.enum(["ready", "installing", "needs-setup", "unsupported", "stale"])
|
|
20
|
+
.describe(
|
|
21
|
+
"Ready means its dependencies are really there. Stale means it was installed once and has since outgrown that, which is what an agent leaves behind when it adds a dependency without installing it. Unsupported means this sandbox has no such tool.",
|
|
22
|
+
),
|
|
23
|
+
missing: z.number().optional().describe("How many declared dependencies cannot be found on disk. What separates never-installed from outgrown."),
|
|
24
|
+
});
|
|
25
|
+
export type ProjectSetup = z.infer<typeof ProjectSetupSchema>;
|
|
26
|
+
export const WorkspaceSetupSchema = z.object({
|
|
27
|
+
projects: z.array(ProjectSetupSchema).describe("Every project the sandbox found, and whether each is usable."),
|
|
28
|
+
});
|
|
29
|
+
export type WorkspaceSetup = z.infer<typeof WorkspaceSetupSchema>;
|
|
30
|
+
// Install these projects' dependencies. Dirs already ready, already installing, or whose manager is missing are
|
|
31
|
+
// skipped server-side, so a stale client list can't spawn redundant installs, `started` is what actually ran.
|
|
32
|
+
export const WorkspaceInstallSchema = z.object({
|
|
33
|
+
dirs: z
|
|
34
|
+
.array(z.string().max(500))
|
|
35
|
+
.min(1)
|
|
36
|
+
.max(50)
|
|
37
|
+
.describe(
|
|
38
|
+
"Which projects to install, by folder. Ones already ready, already installing, or with no tool to install them are skipped rather than refused.",
|
|
39
|
+
),
|
|
40
|
+
});
|
|
41
|
+
export const WorkspaceInstallResultSchema = z.object({
|
|
42
|
+
queued: z.array(z.string()).describe("Which of them actually started, which is not necessarily what you asked for."),
|
|
43
|
+
});
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// workspace tree + files
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ConversationIdSchema } from "./agent.js";
|
|
4
|
+
/* WHOSE COPY OF THE WORKSPACE A READ MEANS, the half of a file address that used to be implicit, and wrong.
|
|
5
|
+
*
|
|
6
|
+
* There is not one workspace. There is the shared /work tree, and there is one private checkout per isolated
|
|
7
|
+
* conversation, each holding files that conversation created and versions of files it edited. Every workspace
|
|
8
|
+
* read route named a PATH and nothing else, so it could only ever answer from the shared tree, and an agent
|
|
9
|
+
* that had just written `docs/plan.md` in its own checkout described a file the viewer could not open, while
|
|
10
|
+
* an agent that had EDITED a file got something worse: the shared version, same path, different text, with
|
|
11
|
+
* nothing to say so.
|
|
12
|
+
*
|
|
13
|
+
* So the conversation rides the request. Absent ⇒ the shared tree, which is what every existing caller means
|
|
14
|
+
* and why the field is optional rather than a second set of routes. Present ⇒ that conversation's own
|
|
15
|
+
* checkout, resolved daemon-side in ONE place (workspaceRootFor) so the escape guard, the control-plane
|
|
16
|
+
* denylist and the ignore rules apply to it exactly as they do to /work.
|
|
17
|
+
*
|
|
18
|
+
* A conversation that is not isolated resolves BACK to the shared tree rather than failing: the shared tree
|
|
19
|
+
* genuinely is its tree, and a caller should not have to know which mode a conversation runs in to link to a
|
|
20
|
+
* file in it. */
|
|
21
|
+
export const WorkspaceScopeSchema = z.object({
|
|
22
|
+
agent: ConversationIdSchema.optional().describe(
|
|
23
|
+
"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.",
|
|
24
|
+
),
|
|
25
|
+
});
|
|
26
|
+
export type WorkspaceScope = z.infer<typeof WorkspaceScopeSchema>;
|
|
27
|
+
/* One node of the full /work filesystem tree the agent sees (untracked + generated files included), distinct
|
|
28
|
+
* from the git-tracked listing. `path` is root-relative with forward slashes so it feeds straight back to the
|
|
29
|
+
* file route.
|
|
30
|
+
*
|
|
31
|
+
* Recursive, and the type is declared rather than inferred. Zod's getter form does infer one, but it collapses
|
|
32
|
+
* to `{}` below the first level of nesting, so `entry.children[0].name` type-checked as an index-signature
|
|
33
|
+
* read on both sides of the wire, and the tree walker's own suite was reading a `hidden` field off entries
|
|
34
|
+
* that has never existed there without the compiler minding. The interface is the contract; the schema
|
|
35
|
+
* validates against it, and z.ZodType makes a divergence between the two an error here. */
|
|
36
|
+
/* A SYMLINK, as the tree reports one. Present only on entries that are links; `type` beside it is the type of
|
|
37
|
+
* whatever the link POINTS AT, so a link to a directory expands and a link to a file opens, exactly like the
|
|
38
|
+
* real thing (the model VSCode uses, its FileType carries SymbolicLink as a bit alongside File/Directory,
|
|
39
|
+
* precisely so every consumer can keep asking "file or folder?").
|
|
40
|
+
*
|
|
41
|
+
* `to` is the link's own text, verbatim, `../../.agents/skills/discord`, not the resolved path. That is what
|
|
42
|
+
* the row shows on hover, because it is what the person who made the link wrote and what they would edit.
|
|
43
|
+
*
|
|
44
|
+
* `state` is absent for the ordinary case: it resolves, and it resolves to somewhere inside the workspace.
|
|
45
|
+
* - "broken", nothing at the other end. Listed anyway, because a dangling link is a fact about the
|
|
46
|
+
* workspace worth seeing rather than an entry to hide.
|
|
47
|
+
* - "outside", it resolves to bytes outside the workspace. The daemon will not read, list or descend
|
|
48
|
+
* through it (workspace-files.ts realWithin), so the row is shown and refused, like a locked one. */
|
|
49
|
+
export interface WorkspaceLink {
|
|
50
|
+
readonly to: string;
|
|
51
|
+
readonly state?: "broken" | "outside" | undefined;
|
|
52
|
+
}
|
|
53
|
+
export const WorkspaceLinkSchema = z.object({
|
|
54
|
+
to: z
|
|
55
|
+
.string()
|
|
56
|
+
.describe("What the link says, verbatim, rather than where it ends up. That is what the person who made it wrote, and what they would edit."),
|
|
57
|
+
state: z
|
|
58
|
+
.enum(["broken", "outside"])
|
|
59
|
+
.optional()
|
|
60
|
+
.describe(
|
|
61
|
+
"Absent for an ordinary link. Broken means there is nothing at the other end, and it is listed anyway because a dangling link is worth seeing. Outside means it leads out of the workspace, so it is shown and refused.",
|
|
62
|
+
),
|
|
63
|
+
});
|
|
64
|
+
export interface WorkspaceTreeEntry {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly path: string;
|
|
67
|
+
readonly type: "file" | "dir";
|
|
68
|
+
readonly size?: number | undefined;
|
|
69
|
+
// Ignored-by-tooling (node_modules, .git, .gitignore'd paths, browser profiles): the client grays the row.
|
|
70
|
+
readonly ignored?: boolean | undefined;
|
|
71
|
+
// Set when this entry is a symlink, `type` above is then its TARGET's type. See WorkspaceLink.
|
|
72
|
+
readonly link?: WorkspaceLink | undefined;
|
|
73
|
+
// A DIR without `children` was listed but not descended into, because it's ignored, or because the walk's
|
|
74
|
+
// breadth-first budget stopped above it. Either way the client lazy-loads it via /workspace/children on
|
|
75
|
+
// expand, so "not loaded yet" and "empty directory" (`children: []`) stay distinguishable.
|
|
76
|
+
readonly children?: readonly WorkspaceTreeEntry[] | undefined;
|
|
77
|
+
}
|
|
78
|
+
export const WorkspaceTreeEntrySchema: z.ZodType<WorkspaceTreeEntry> = z.object({
|
|
79
|
+
name: z.string().describe("Just this entry's own name."),
|
|
80
|
+
path: z.string().describe("Its full path from the workspace root, which feeds straight back into the file routes."),
|
|
81
|
+
type: z.enum(["file", "dir"]).describe("What it is. For a link, what it points at, so a link to a folder opens like a folder."),
|
|
82
|
+
size: z.number().optional().describe("Size in bytes, for a file."),
|
|
83
|
+
ignored: z
|
|
84
|
+
.boolean()
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("Tooling ignores it: installed packages, git internals, anything the ignore rules exclude. Usually drawn greyed out."),
|
|
87
|
+
link: WorkspaceLinkSchema.optional().describe("Present when this entry is a link."),
|
|
88
|
+
get children() {
|
|
89
|
+
return z
|
|
90
|
+
.array(WorkspaceTreeEntrySchema)
|
|
91
|
+
.optional()
|
|
92
|
+
.describe(
|
|
93
|
+
"What is inside a folder. Absent means it was not opened, either because it is ignored or because the walk ran out of budget above it, so ask for it separately. An empty list means it really is empty.",
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
export const WorkspaceTreeSchema = z.object({
|
|
98
|
+
root: z.string().describe("The path everything below is relative to."),
|
|
99
|
+
tree: z.array(WorkspaceTreeEntrySchema).describe("The workspace, one entry per file and folder."),
|
|
100
|
+
// How many of the ROOT's own entries the budget cut (0 = complete); per-dir cuts are counted on each dir entry.
|
|
101
|
+
hidden: z.number().describe("How many entries at the top level were cut for size. Zero means the listing is complete."),
|
|
102
|
+
});
|
|
103
|
+
export type WorkspaceTree = z.infer<typeof WorkspaceTreeSchema>;
|
|
104
|
+
// Lazy-load one directory's children, for a dir the tree walk listed but didn't descend into. Child dirs again
|
|
105
|
+
// carry no `children`, so they lazy-load on their own expand. `hidden` = how many entries the cap cut (0 = all
|
|
106
|
+
// listed).
|
|
107
|
+
export const WorkspaceChildrenQuerySchema = WorkspaceScopeSchema.extend({
|
|
108
|
+
path: z.string().min(1).describe("The folder to open, as a workspace path."),
|
|
109
|
+
});
|
|
110
|
+
export const WorkspaceChildrenSchema = z.object({
|
|
111
|
+
entries: z
|
|
112
|
+
.array(WorkspaceTreeEntrySchema)
|
|
113
|
+
.describe("What is directly inside it. Folders in here carry no contents of their own, so they open the same way."),
|
|
114
|
+
hidden: z.number().describe("How many entries were cut for size. Zero means the listing is complete."),
|
|
115
|
+
});
|
|
116
|
+
export type WorkspaceChildren = z.infer<typeof WorkspaceChildrenSchema>;
|
|
117
|
+
// Write routes (delete) and the read they mirror. No scope: a conversation's own checkout is READ-ONLY through
|
|
118
|
+
// the file API, see workspaceRootFor for why the refusal lives daemon-side rather than in each screen.
|
|
119
|
+
export const WorkspaceFileQuerySchema = z.object({ path: z.string().min(1).describe("The file or folder, as a workspace path.") });
|
|
120
|
+
export const WorkspaceMediaTicketQuerySchema = WorkspaceScopeSchema.extend({
|
|
121
|
+
path: z.string().min(1).describe("The media file the ticket should cover."),
|
|
122
|
+
});
|
|
123
|
+
/* The credential a <video>/<audio> element carries to GET /workspace/media, which is the one workspace route a
|
|
124
|
+
* browser cannot put a header on. Minted here, over the ordinary bearer-authenticated contract, and scoped to
|
|
125
|
+
* the single FILE it was asked for, the resolved one, so a ticket minted against a conversation's checkout
|
|
126
|
+
* buys that file and not its shared-tree namesake (see auth/media-tickets.ts for why scope rather than
|
|
127
|
+
* single-use is what bounds it). `expiresAt` is epoch ms so a player can tell a dead ticket from a dead file. */
|
|
128
|
+
export const WorkspaceMediaTicketSchema = z.object({
|
|
129
|
+
ticket: z.string().describe("Hand this to the streaming route in the query string. It buys exactly the one file it was minted for."),
|
|
130
|
+
expiresAt: z.number().describe("When it stops working, in milliseconds, so a player can tell a dead ticket from a dead file."),
|
|
131
|
+
});
|
|
132
|
+
/* A text read is a read of a WINDOW: `offset` is the byte to start at (negative reads that many bytes from the
|
|
133
|
+
* END, which is what following a growing log means, the tail's offset isn't knowable until the size is), and
|
|
134
|
+
* `limit` how many bytes to serve. The daemon clamps `limit` to its own cap, so an omitted or oversized one is
|
|
135
|
+
* the cap rather than the file. Coerced: these arrive as query strings. */
|
|
136
|
+
export const WorkspaceFileReadQuerySchema = WorkspaceScopeSchema.extend({
|
|
137
|
+
path: z.string().min(1).describe("The file to read, as a workspace path."),
|
|
138
|
+
offset: z.coerce
|
|
139
|
+
.number()
|
|
140
|
+
.int()
|
|
141
|
+
.optional()
|
|
142
|
+
.describe(
|
|
143
|
+
"Which byte to start at. A negative number reads that many bytes from the end, which is how you follow a growing log without knowing its size first.",
|
|
144
|
+
),
|
|
145
|
+
limit: z.coerce
|
|
146
|
+
.number()
|
|
147
|
+
.int()
|
|
148
|
+
.min(1)
|
|
149
|
+
.optional()
|
|
150
|
+
.describe(
|
|
151
|
+
"How many bytes to read. Capped by the sandbox, so leaving it out or asking for too much gives you the cap rather than the whole file.",
|
|
152
|
+
),
|
|
153
|
+
});
|
|
154
|
+
// `size` is the whole file; `offset`/`bytes` the byte range `content` decodes from, so the reader can tell a
|
|
155
|
+
// window from a whole file (offset > 0 || offset + bytes < size ⇒ there is more) and ask for the next one.
|
|
156
|
+
export const WorkspaceFilePresentSchema = z.object({
|
|
157
|
+
present: z.literal(true).describe("There is something at that path."),
|
|
158
|
+
path: z.string().describe("The path, as asked for."),
|
|
159
|
+
content: z.string().describe("The bytes of the window you asked for, as text."),
|
|
160
|
+
size: z.number().describe("How large the whole file is. Compare it with the window below to know whether there is more."),
|
|
161
|
+
offset: z.number().describe("Which byte the window starts at."),
|
|
162
|
+
bytes: z.number().describe("How many bytes the window holds."),
|
|
163
|
+
// Which tree answered. Always true when no `agent` was asked for; true DESPITE one when that conversation's
|
|
164
|
+
// checkout doesn't carry the path (see scopedTarget, its checkout is not a superset of /work), which is
|
|
165
|
+
// the one case the reader has to be told about rather than left to assume.
|
|
166
|
+
shared: z
|
|
167
|
+
.boolean()
|
|
168
|
+
.describe(
|
|
169
|
+
"Which tree answered. True when no conversation was named, and also when one was but its own copy has no such file, which is the case a reader has to be told about rather than left to assume.",
|
|
170
|
+
),
|
|
171
|
+
});
|
|
172
|
+
/* NOTHING TO READ AT THAT PATH, an ANSWER, not a failure, which is the whole reason this branch exists.
|
|
173
|
+
*
|
|
174
|
+
* Most reads in this product are "read it if it is there": the file each extension keeps of what its badge has
|
|
175
|
+
* already shown, a repo's documentation index, a run's result, a directory's own UI document. Absent is their
|
|
176
|
+
* ordinary FIRST state, and every one of them already treats it as a value. Answering those with a 404 made the
|
|
177
|
+
* browser log a failed request per read, around a dozen red lines on every page load, none of which meant
|
|
178
|
+
* anything was wrong, and none of which a `catch` can suppress: the log happens in the network stack before any
|
|
179
|
+
* JavaScript sees the response.
|
|
180
|
+
*
|
|
181
|
+
* A read that is REFUSED (an escape, the control plane, a denylisted path) is still an error, because that is a
|
|
182
|
+
* real answer about the caller rather than about the file. */
|
|
183
|
+
export const WorkspaceFileAbsentSchema = z.object({
|
|
184
|
+
present: z
|
|
185
|
+
.literal(false)
|
|
186
|
+
.describe(
|
|
187
|
+
"Nothing there. An answer, not a failure: reading a file that may not exist yet is the ordinary case for half the reads in this product.",
|
|
188
|
+
),
|
|
189
|
+
path: z.string().describe("The path, as asked for."),
|
|
190
|
+
});
|
|
191
|
+
export const WorkspaceFileSchema = z.discriminatedUnion("present", [WorkspaceFilePresentSchema, WorkspaceFileAbsentSchema]);
|
|
192
|
+
// Resolve a file reference an agent (or a compiler, or a terminal) NAMED to the workspace path it means. Prose
|
|
193
|
+
// paths are routinely partial, a model that has been discussing `_editor/web/src` writes
|
|
194
|
+
// `pages/workspace/Foo.vue`, so a clickable mention has to be matched as a path SUFFIX against the real tree,
|
|
195
|
+
// not read as root-relative. `path` is absent when nothing in the workspace ends in that reference.
|
|
196
|
+
export const WorkspaceResolveQuerySchema = WorkspaceScopeSchema.extend({
|
|
197
|
+
path: z
|
|
198
|
+
.string()
|
|
199
|
+
.min(1)
|
|
200
|
+
.max(512)
|
|
201
|
+
.describe(
|
|
202
|
+
"The reference as somebody wrote it. Often only the tail of the real path, which is why this is matched against the tree rather than read as-is.",
|
|
203
|
+
),
|
|
204
|
+
});
|
|
205
|
+
export const WorkspaceResolveSchema = z.object({
|
|
206
|
+
path: z.string().optional().describe("The real path it means. Absent when nothing in the workspace ends that way."),
|
|
207
|
+
});
|
|
208
|
+
// Direct file management over the /work tree (delete / new folder / rename+move / copy). Byte writes + the
|
|
209
|
+
// editor's text save go through the plain POST /workspace/upload route (a body doesn't fit oRPC), not here.
|
|
210
|
+
export const WorkspaceDirSchema = z.object({ path: z.string().min(1).describe("The folder to create. Missing folders above it are created too.") });
|
|
211
|
+
export const WorkspaceMoveSchema = z.object({
|
|
212
|
+
from: z.string().min(1).describe("What to move or copy, as a workspace path."),
|
|
213
|
+
to: z.string().min(1).describe("Where it should end up. Changing only the last part is how you rename something."),
|
|
214
|
+
});
|
|
215
|
+
// Deterministic (no-LLM) classification of the dropped workspace: each repo dir and loose file sorted into one
|
|
216
|
+
// coarse bucket. Read-only, the browser turns it into a proposed layout and applies the accepted moves via the
|
|
217
|
+
// existing /workspace/move route. `reason` records the winning signal (magic:<mime>, ext:<ext>,
|
|
218
|
+
// repository:<marker>, text-content, unknown) so the proposal is explainable.
|
|
219
|
+
export const WorkspaceBucketSchema = z.enum(["repositories", "documents", "media", "archives", "other"]);
|
|
220
|
+
export type WorkspaceBucket = z.infer<typeof WorkspaceBucketSchema>;
|
|
221
|
+
export const WorkspaceClassificationSchema = z.object({
|
|
222
|
+
classifications: z
|
|
223
|
+
.array(
|
|
224
|
+
z.object({
|
|
225
|
+
path: z.string().describe("What was looked at."),
|
|
226
|
+
bucket: WorkspaceBucketSchema.describe("Which bucket it was sorted into."),
|
|
227
|
+
reason: z.string().describe("The signal that decided it, so the proposal can be argued with rather than trusted."),
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
.describe(
|
|
231
|
+
"One entry per repository folder and loose file at the top of the workspace. A read-only proposal: nothing moves until you apply it.",
|
|
232
|
+
),
|
|
233
|
+
});
|
|
234
|
+
export type WorkspaceClassification = z.infer<typeof WorkspaceClassificationSchema>;
|
package/src/title.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import { AgentTurnSchema } from "./schemas.js";
|
|
2
|
+
import { AgentTurnSchema } from "./schemas/agent.js";
|
|
3
3
|
import { deriveTitle } from "./title.js";
|
|
4
4
|
|
|
5
5
|
/* The name a conversation opens under. Every case here is a shape the naive rule (collapse whitespace, cut at
|
package/src/tunnel-ids.test.ts
CHANGED
|
@@ -42,7 +42,9 @@ test("slots are not derivable from the sandbox id: only from the token behind it
|
|
|
42
42
|
expect(portSlotsFromToken(OTHER)).not.toEqual(portSlotsFromToken(TOKEN));
|
|
43
43
|
// No slot leaks the id it will be paired with in `port-<slot>-<sandboxId>`.
|
|
44
44
|
const id = sandboxIdFromToken(TOKEN);
|
|
45
|
-
|
|
45
|
+
// Load-bearing, and not merely tidy: `not.toContain(undefined)` is true of every slot list ever produced,
|
|
46
|
+
// so without a real id the leak check below would pass on a function that had stopped deriving one at all.
|
|
47
|
+
expect(id).toEqual(expect.any(String));
|
|
46
48
|
expect(portSlotsFromToken(TOKEN)).not.toContain(id);
|
|
47
49
|
// And none of them is the old fixed alphabet, which is what made the hostnames guessable.
|
|
48
50
|
expect(portSlotsFromToken(TOKEN).some((slot) => slot.length === 1)).toBe(false);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* THE ZOD-FREE HALF OF THE BROWSER CONNECTOR'S WIRE: three URL builders, one code format, two message names.
|
|
2
|
+
*
|
|
3
|
+
* Split out of webext-protocol.ts for exactly one reason, and it is a real one rather than tidiness. A browser
|
|
4
|
+
* extension's CONTENT SCRIPT — the few lines that run on the sandbox's own page to hand a pairing code over —
|
|
5
|
+
* needs two of these constants and nothing else. Importing them through the contract's barrel pulls in every
|
|
6
|
+
* schema this repo has and the whole of zod with them: measured at 1.1 MB of JavaScript injected into a page,
|
|
7
|
+
* for two strings. Nothing here imports anything, so that file is now a few hundred bytes.
|
|
8
|
+
*
|
|
9
|
+
* The rule for what may live here: no imports, ever. A zod schema in this file would undo the split silently.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// The URL the extension dials, given the sandbox's public URL. Carries no credential — the token rides the
|
|
13
|
+
// hello frame (webext-protocol.ts). One place builds it, so the extension and the daemon route cannot disagree
|
|
14
|
+
// about where the door is.
|
|
15
|
+
export const webextConnectUrl = (sandboxUrl: string): string => `${sandboxUrl.replace(/^http/, "ws").replace(/\/$/, "")}/system/webext/connect`;
|
|
16
|
+
|
|
17
|
+
/* Where the extension POSTS a site's session when the owner asks for one to be handed to the sandbox
|
|
18
|
+
* ("Connect this site"), authenticated by the same enrollment token as a bearer.
|
|
19
|
+
*
|
|
20
|
+
* A SEPARATE DOOR FROM THE SOCKET, deliberately, and this is the load-bearing decision of that feature: the
|
|
21
|
+
* socket's answers are MCP tool results, which means they land in the agent's context. Cookies must never do
|
|
22
|
+
* that. So the payload goes browser → daemon directly, the agent's tool call gets back a sentence and a count,
|
|
23
|
+
* and the one thing that would be catastrophic to leak is the one thing the model never sees. */
|
|
24
|
+
export const webextSessionUrl = (sandboxUrl: string): string => `${sandboxUrl.replace(/\/$/, "")}/system/webext/session`;
|
|
25
|
+
|
|
26
|
+
/* The same door in the other direction: where the extension asks for a sandbox account's session to be LENT to
|
|
27
|
+
* this browser, so the owner can finish a step no remote browser can do — a passkey, a hardware second factor,
|
|
28
|
+
* an employer's SSO that checks the device.
|
|
29
|
+
*
|
|
30
|
+
* A separate path rather than a verb on the one above, because the two are different acts with different
|
|
31
|
+
* refusals ("no such account" versus "that account is not signed in there") and a single endpoint doing both
|
|
32
|
+
* would have to be read twice to know which. Same bearer, and the same rule about the socket: the cookies come
|
|
33
|
+
* back on THIS request, never as an MCP result. */
|
|
34
|
+
export const webextLendUrl = (sandboxUrl: string): string => `${sandboxUrl.replace(/\/$/, "")}/system/webext/lend`;
|
|
35
|
+
|
|
36
|
+
/* ---- the pairing code: the one string that travels from the sandbox's card into the extension ----
|
|
37
|
+
*
|
|
38
|
+
* A connected computer is paired by a shell one-liner, which can carry two values in two environment variables
|
|
39
|
+
* because a terminal is a place where long strings are normal. A browser extension's popup is not: what a
|
|
40
|
+
* person will actually do there is paste ONE thing, once, and anything that asks them to copy a URL into one
|
|
41
|
+
* box and a token into another is a flow that fails on the second box.
|
|
42
|
+
*
|
|
43
|
+
* So both halves ride in one code. It is not encryption and does not pretend to be — base64url of two fields,
|
|
44
|
+
* so that the thing on the clipboard is opaque enough not to be edited by hand, short enough to paste, and
|
|
45
|
+
* carries its own sandbox address, which is the field a person could not possibly be expected to type. The
|
|
46
|
+
* secret in it is the pairing token, which is single-use and expires in ten minutes (webext-store.ts).
|
|
47
|
+
*
|
|
48
|
+
* The prefix is a version marker, and it is here so that a code from an older sandbox meets a clear "this code
|
|
49
|
+
* is from a different version" in the extension rather than a JSON parse error. */
|
|
50
|
+
const PAIRING_PREFIX = "ixb1_";
|
|
51
|
+
|
|
52
|
+
const base64url = (value: string): string => btoa(value).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
53
|
+
|
|
54
|
+
export const webextPairingCode = (pairing: { readonly url: string; readonly token: string }): string =>
|
|
55
|
+
`${PAIRING_PREFIX}${base64url(JSON.stringify({ url: pairing.url, token: pairing.token }))}`;
|
|
56
|
+
|
|
57
|
+
// The extension's side. Undefined for anything that is not one of ours, so a person who pasted the wrong thing
|
|
58
|
+
// is told that rather than watching a connection attempt to nowhere.
|
|
59
|
+
export const parseWebextPairingCode = (code: string): { readonly url: string; readonly token: string } | undefined => {
|
|
60
|
+
const trimmed = code.trim();
|
|
61
|
+
if (!trimmed.startsWith(PAIRING_PREFIX)) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const decoded = JSON.parse(atob(trimmed.slice(PAIRING_PREFIX.length).replaceAll("-", "+").replaceAll("_", "/"))) as {
|
|
66
|
+
url?: unknown;
|
|
67
|
+
token?: unknown;
|
|
68
|
+
};
|
|
69
|
+
// An http(s) URL and a non-empty token, or nothing: the extension is about to open a socket to whatever
|
|
70
|
+
// this says, so "looks like a URL" is checked here rather than by the WebSocket constructor throwing.
|
|
71
|
+
if (typeof decoded.url !== "string" || typeof decoded.token !== "string" || decoded.token === "" || !/^https?:\/\//.test(decoded.url)) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return { url: decoded.url, token: decoded.token };
|
|
75
|
+
} catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/* How the sandbox's own page hands that code over without anybody copying anything: it posts this message on
|
|
81
|
+
* its own window, and the extension's content script (which is only ever loaded on the sandbox's own origins)
|
|
82
|
+
* picks it up and answers.
|
|
83
|
+
*
|
|
84
|
+
* A window message rather than `chrome.runtime.sendMessage` from the page, which would be the obvious route,
|
|
85
|
+
* because that one needs the extension's STORE ID baked into the web app — a value that does not exist until
|
|
86
|
+
* the listing is approved, differs for an unlisted build, and would make a locally-loaded extension
|
|
87
|
+
* un-pairable. A window message needs neither side to know the other's identity: the content script is proof
|
|
88
|
+
* enough that the extension is installed, and the page's origin is proof enough for the extension. */
|
|
89
|
+
export const WEBEXT_PAIR_MESSAGE = "intentic:webext:pair";
|
|
90
|
+
export const WEBEXT_PAIRED_MESSAGE = "intentic:webext:paired";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/* The handshake on /system/webext/connect, the ONE message on that socket that is not oRPC.
|
|
4
|
+
*
|
|
5
|
+
* Same two-phase shape as a connected computer's (host-protocol.ts) and for the same reason: the daemon has
|
|
6
|
+
* nothing to call until it knows whose socket this is, so the proof cannot itself be an oRPC call. The browser
|
|
7
|
+
* extension's first frame carries its enrollment token, the daemon resolves WHICH capability it belongs to, and
|
|
8
|
+
* from that message on every byte is `webextContract` with the EXTENSION serving.
|
|
9
|
+
*
|
|
10
|
+
* WHY A SEPARATE PROTOCOL FROM host's, when the frame is the same two fields: because the thing on the other
|
|
11
|
+
* end is not a computer. It has no shell, no filesystem and no screen; what it has is tabs, origins the person
|
|
12
|
+
* granted one at a time, and a human watching every click. Sharing the host's schema would have meant a card of
|
|
13
|
+
* switches that mean nothing (`roots`, `sandboxRemove`) and an agent told about a home directory it cannot
|
|
14
|
+
* reach. The two connectors are siblings, not one connector with a flag. */
|
|
15
|
+
|
|
16
|
+
export const WebExtHelloSchema = z.object({
|
|
17
|
+
type: z.literal("hello"),
|
|
18
|
+
/* The extension's enrollment token, in the FIRST FRAME, never in the URL: a WebSocket has no headers, and
|
|
19
|
+
* `?token=` would write a durable key into every proxy log between a browser and the sandbox. Until this
|
|
20
|
+
* arrives the socket is anonymous and short-lived. */
|
|
21
|
+
token: z.string(),
|
|
22
|
+
// The extension build the browser is running. Surfaced per connection so an old, un-updated extension is
|
|
23
|
+
// visible rather than mysteriously missing a tool. What the BROWSER is (`describe`) is pulled over the
|
|
24
|
+
// typed link a moment later, so there is one definition of those facts rather than two.
|
|
25
|
+
version: z.string(),
|
|
26
|
+
});
|
|
27
|
+
export type WebExtHello = z.infer<typeof WebExtHelloSchema>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import { type Workflow, WorkflowSchema, type WorkflowStep } from "./schemas.js";
|
|
2
|
+
import { type Workflow, WorkflowSchema, type WorkflowStep } from "./schemas/workflows.js";
|
|
3
3
|
import { workflowFaults, workflowRunFaults } from "./workflow-faults.js";
|
|
4
4
|
|
|
5
5
|
/* The GATE rules. The graph rules beside them are exercised by the scheduler's own integration tests, which
|
package/src/workflow-faults.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { duplicateOutputFieldNames } from "./output-fields.js";
|
|
10
|
-
import type { Workflow, WorkflowStep } from "./schemas.js";
|
|
10
|
+
import type { Workflow, WorkflowStep } from "./schemas/workflows.js";
|
|
11
11
|
|
|
12
12
|
// One id, one node: the scheduler keys needs, runs and the drawn graph by it, so a repeat makes the same node
|
|
13
13
|
// mean two things.
|
|
@@ -100,8 +100,8 @@ describe(`staleQueryKeys`, () => {
|
|
|
100
100
|
// The two lists are unioned flat, not layered: a narrow extension entry under a broad core entry that
|
|
101
101
|
// invalidates nothing must still fire. Without this, every path beneath one of the daemon's
|
|
102
102
|
// machine-state prefixes would be unreachable to extensions.
|
|
103
|
-
const nested: readonly FileContribution[] = [{ path: `${STATE_DIR}/records/sessions/claude/
|
|
104
|
-
expect(staleQueryKeys([`.intentic/records/sessions/claude/
|
|
103
|
+
const nested: readonly FileContribution[] = [{ path: `${STATE_DIR}/records/sessions/claude/plans/`, invalidates: [`plans`] }];
|
|
104
|
+
expect(staleQueryKeys([`.intentic/records/sessions/claude/plans/a-plan.md`], nested)).toEqual([`plans`]);
|
|
105
105
|
// …and a sibling under the same core prefix stays ignored.
|
|
106
106
|
expect(staleQueryKeys([`.intentic/records/sessions/claude/projects/p/session.jsonl`], nested)).toEqual([]);
|
|
107
107
|
});
|
|
@@ -162,7 +162,9 @@ describe(`WORKSPACE_STATE_FILES`, () => {
|
|
|
162
162
|
// the difference visible at review time.
|
|
163
163
|
for (const file of WORKSPACE_STATE_FILES) {
|
|
164
164
|
if (file.invalidates.length === 0) {
|
|
165
|
-
|
|
165
|
+
// A string with something in it. Truthiness would accept the number 1 or an object, and the
|
|
166
|
+
// failure this guards against — a `why` left blank to quiet the check — reads as whitespace.
|
|
167
|
+
expect(file.why, `${file.path} invalidates nothing and must say why`).toEqual(expect.stringMatching(/\S/));
|
|
166
168
|
} else {
|
|
167
169
|
expect(file.why, `${file.path} invalidates queries, so \`why\` is dead weight`).toBeUndefined();
|
|
168
170
|
}
|
|
@@ -198,7 +200,7 @@ describe(`WORKSPACE_STATE_FILES`, () => {
|
|
|
198
200
|
for (const file of WORKSPACE_STATE_FILES) {
|
|
199
201
|
for (const parent of WORKSPACE_STATE_FILES.filter((other) => other !== file && file.path.startsWith(other.path))) {
|
|
200
202
|
expect(
|
|
201
|
-
parent.portability === file.portability && parent.invalidates.join() === file.invalidates.join(),
|
|
203
|
+
parent.portability === file.portability && parent.invalidates.join(",") === file.invalidates.join(","),
|
|
202
204
|
`${file.path} says nothing its parent ${parent.path} doesn't already say`,
|
|
203
205
|
).toBe(false);
|
|
204
206
|
}
|