@intentic/sandbox-contract 1.248.0 → 1.249.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -15
- package/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/digest.d.ts.map +1 -1
- package/dist/chores/digest.js.map +1 -1
- package/dist/chores/extension-update.d.ts.map +1 -1
- package/dist/chores/extension-update.js.map +1 -1
- package/dist/chores/fix-deps.d.ts.map +1 -1
- package/dist/chores/fix-deps.js.map +1 -1
- package/dist/chores/index.d.ts +2 -2
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +1 -1
- package/dist/chores/index.js.map +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js.map +1 -1
- package/dist/chores/prompt.d.ts.map +1 -1
- package/dist/chores/prompt.js.map +1 -1
- package/dist/chores/stack.d.ts.map +1 -1
- package/dist/chores/stack.js.map +1 -1
- package/dist/chores/verdict.d.ts +7 -1
- package/dist/chores/verdict.d.ts.map +1 -1
- package/dist/chores/verdict.js +8 -0
- package/dist/chores/verdict.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/accounts.contract.js.map +1 -1
- package/dist/contracts/activity.contract.d.ts +1 -0
- package/dist/contracts/activity.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +12 -205
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +2 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +228 -54
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +11 -2
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/automations.contract.d.ts +35 -17
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.js +10 -1
- package/dist/contracts/automations.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +0 -12
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +1 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/chores.contract.d.ts.map +1 -1
- package/dist/contracts/chores.contract.js.map +1 -1
- package/dist/contracts/ci.contract.d.ts +9 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js.map +1 -1
- package/dist/contracts/exit.contract.d.ts.map +1 -1
- package/dist/contracts/exit.contract.js +1 -1
- package/dist/contracts/exit.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/git.contract.d.ts +45 -6
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.js +9 -9
- package/dist/contracts/git.contract.js.map +1 -1
- package/dist/contracts/host.contract.d.ts +2 -0
- package/dist/contracts/host.contract.d.ts.map +1 -1
- package/dist/contracts/host.contract.js.map +1 -1
- package/dist/contracts/intentic.contract.js +1 -1
- package/dist/contracts/intentic.contract.js.map +1 -1
- package/dist/contracts/logs.contract.d.ts.map +1 -1
- package/dist/contracts/logs.contract.js.map +1 -1
- package/dist/contracts/loops.contract.d.ts.map +1 -1
- package/dist/contracts/loops.contract.js.map +1 -1
- package/dist/contracts/personas.contract.d.ts +1 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.js +1 -1
- package/dist/contracts/personas.contract.js.map +1 -1
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +97 -171
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +2 -2
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/safety.contract.d.ts.map +1 -1
- package/dist/contracts/safety.contract.js +1 -1
- package/dist/contracts/safety.contract.js.map +1 -1
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +2 -41
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.js +1 -1
- package/dist/contracts/sessions.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +8 -18
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/share.contract.d.ts.map +1 -1
- package/dist/contracts/share.contract.js.map +1 -1
- package/dist/contracts/skills.contract.d.ts.map +1 -1
- package/dist/contracts/skills.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +49 -73
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +12 -2
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/usage.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.js.map +1 -1
- package/dist/contracts/vpn.contract.d.ts.map +1 -1
- package/dist/contracts/vpn.contract.js +1 -1
- package/dist/contracts/vpn.contract.js.map +1 -1
- package/dist/contracts/webext.contract.d.ts.map +1 -1
- package/dist/contracts/webext.contract.js.map +1 -1
- package/dist/contracts/workflows.contract.d.ts +7 -6
- package/dist/contracts/workflows.contract.d.ts.map +1 -1
- package/dist/contracts/workflows.contract.js +12 -3
- package/dist/contracts/workflows.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +1237 -0
- package/dist/events/agent-events.d.ts.map +1 -0
- package/dist/events/agent-events.js +226 -0
- package/dist/events/agent-events.js.map +1 -0
- package/dist/events/cards.d.ts +404 -0
- package/dist/events/cards.d.ts.map +1 -0
- package/dist/events/cards.js +179 -0
- package/dist/events/cards.js.map +1 -0
- package/dist/events/resume.d.ts +23 -0
- package/dist/events/resume.d.ts.map +1 -0
- package/dist/events/resume.js +31 -0
- package/dist/events/resume.js.map +1 -0
- package/dist/events/system-events.d.ts +615 -0
- package/dist/events/system-events.d.ts.map +1 -0
- package/dist/events/system-events.js +64 -0
- package/dist/events/system-events.js.map +1 -0
- package/dist/events/transcript.d.ts +1755 -0
- package/dist/events/transcript.d.ts.map +1 -0
- package/dist/events/transcript.js +205 -0
- package/dist/events/transcript.js.map +1 -0
- package/dist/ids/conversation-ids.d.ts +8 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -0
- package/dist/{conversation-ids.js → ids/conversation-ids.js} +13 -0
- package/dist/ids/conversation-ids.js.map +1 -0
- package/dist/ids/hostnames.d.ts.map +1 -0
- package/dist/ids/hostnames.js.map +1 -0
- package/dist/ids/session-names.d.ts.map +1 -0
- package/dist/ids/session-names.js.map +1 -0
- package/dist/ids/share-paths.d.ts.map +1 -0
- package/dist/ids/share-paths.js.map +1 -0
- package/dist/{tunnel-ids.d.ts → ids/tunnel-ids.d.ts} +1 -0
- package/dist/ids/tunnel-ids.d.ts.map +1 -0
- package/dist/{tunnel-ids.js → ids/tunnel-ids.js} +1 -0
- package/dist/ids/tunnel-ids.js.map +1 -0
- package/dist/index.d.ts +423 -450
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -46
- package/dist/index.js.map +1 -1
- package/dist/{agent-catalog.d.ts → models/agent-catalog.d.ts} +2 -2
- package/dist/models/agent-catalog.d.ts.map +1 -0
- package/dist/models/agent-catalog.js.map +1 -0
- package/dist/models/agent-runtimes.d.ts.map +1 -0
- package/dist/models/agent-runtimes.js.map +1 -0
- package/dist/{fast-tier.d.ts → models/fast-tier.d.ts} +1 -1
- package/dist/models/fast-tier.d.ts.map +1 -0
- package/dist/models/fast-tier.js.map +1 -0
- package/dist/models/model-order.d.ts.map +1 -0
- package/dist/models/model-order.js.map +1 -0
- package/dist/{model-pins.d.ts → models/model-pins.d.ts} +1 -1
- package/dist/models/model-pins.d.ts.map +1 -0
- package/dist/models/model-pins.js.map +1 -0
- package/dist/{model-roles.d.ts → models/model-roles.d.ts} +0 -33
- package/dist/models/model-roles.d.ts.map +1 -0
- package/dist/{model-roles.js → models/model-roles.js} +0 -35
- package/dist/models/model-roles.js.map +1 -0
- package/dist/{plan-pools.d.ts → models/plan-pools.d.ts} +1 -1
- package/dist/models/plan-pools.d.ts.map +1 -0
- package/dist/models/plan-pools.js.map +1 -0
- package/dist/models/prompt-complexity.d.ts.map +1 -0
- package/dist/models/prompt-complexity.js.map +1 -0
- package/dist/models/provider-specs.d.ts.map +1 -0
- package/dist/models/provider-specs.js.map +1 -0
- package/dist/policy/approvals-execution.d.ts.map +1 -0
- package/dist/policy/approvals-execution.js.map +1 -0
- package/dist/{batch-runs.d.ts → policy/batch-runs.d.ts} +5 -1
- package/dist/policy/batch-runs.d.ts.map +1 -0
- package/dist/{batch-runs.js → policy/batch-runs.js} +10 -7
- package/dist/policy/batch-runs.js.map +1 -0
- package/dist/policy/capability-env.d.ts.map +1 -0
- package/dist/policy/capability-env.js.map +1 -0
- package/dist/policy/capability-secrets.d.ts.map +1 -0
- package/dist/policy/capability-secrets.js.map +1 -0
- package/dist/{card-status.d.ts → policy/card-status.d.ts} +2 -2
- package/dist/policy/card-status.d.ts.map +1 -0
- package/dist/{card-status.js → policy/card-status.js} +1 -5
- package/dist/policy/card-status.js.map +1 -0
- package/dist/{command-classes.d.ts → policy/command-classes.d.ts} +6 -2
- package/dist/policy/command-classes.d.ts.map +1 -0
- package/dist/{command-classes.js → policy/command-classes.js} +48 -11
- package/dist/policy/command-classes.js.map +1 -0
- package/dist/{command-run.d.ts → policy/command-run.d.ts} +1 -1
- package/dist/policy/command-run.d.ts.map +1 -0
- package/dist/policy/command-run.js.map +1 -0
- package/dist/policy/control-scopes.d.ts +16 -0
- package/dist/policy/control-scopes.d.ts.map +1 -0
- package/dist/policy/control-scopes.js +26 -0
- package/dist/policy/control-scopes.js.map +1 -0
- package/dist/policy/credential-material.d.ts.map +1 -0
- package/dist/policy/credential-material.js.map +1 -0
- package/dist/policy/needs-action.d.ts.map +1 -0
- package/dist/policy/needs-action.js.map +1 -0
- package/dist/policy/output-fields.d.ts.map +1 -0
- package/dist/policy/output-fields.js.map +1 -0
- package/dist/policy/overlay-lint.d.ts.map +1 -0
- package/dist/policy/overlay-lint.js.map +1 -0
- package/dist/policy/owner-ticket.d.ts.map +1 -0
- package/dist/{owner-ticket.js → policy/owner-ticket.js} +1 -1
- package/dist/policy/owner-ticket.js.map +1 -0
- package/dist/{safety-policy.d.ts → policy/safety-policy.d.ts} +6 -5
- package/dist/policy/safety-policy.d.ts.map +1 -0
- package/dist/{safety-policy.js → policy/safety-policy.js} +12 -12
- package/dist/policy/safety-policy.js.map +1 -0
- package/dist/policy/search-globs.d.ts.map +1 -0
- package/dist/policy/search-globs.js.map +1 -0
- package/dist/protocol/container-requirements.d.ts +20 -0
- package/dist/protocol/container-requirements.d.ts.map +1 -0
- package/dist/protocol/container-requirements.js +23 -0
- package/dist/protocol/container-requirements.js.map +1 -0
- package/dist/{host-protocol.d.ts → protocol/host-protocol.d.ts} +1 -0
- package/dist/protocol/host-protocol.d.ts.map +1 -0
- package/dist/{host-protocol.js → protocol/host-protocol.js} +1 -0
- package/dist/protocol/host-protocol.js.map +1 -0
- package/dist/protocol/ingress-contract.d.ts.map +1 -0
- package/dist/{ingress-contract.js → protocol/ingress-contract.js} +1 -1
- package/dist/protocol/ingress-contract.js.map +1 -0
- package/dist/protocol/ingress-protocol.d.ts.map +1 -0
- package/dist/protocol/ingress-protocol.js.map +1 -0
- package/dist/protocol/listener-protocol.d.ts.map +1 -0
- package/dist/{listener-protocol.js → protocol/listener-protocol.js} +1 -1
- package/dist/protocol/listener-protocol.js.map +1 -0
- package/dist/{peer-dial.d.ts → protocol/peer-dial.d.ts} +4 -1
- package/dist/protocol/peer-dial.d.ts.map +1 -0
- package/dist/{peer-dial.js → protocol/peer-dial.js} +51 -10
- package/dist/protocol/peer-dial.js.map +1 -0
- package/dist/protocol/peer-mcp-server.d.ts.map +1 -0
- package/dist/protocol/peer-mcp-server.js.map +1 -0
- package/dist/protocol/request-id.d.ts.map +1 -0
- package/dist/protocol/request-id.js.map +1 -0
- package/dist/protocol/routes.d.ts.map +1 -0
- package/dist/protocol/routes.js.map +1 -0
- package/dist/{runner-protocol.d.ts → protocol/runner-protocol.d.ts} +1 -0
- package/dist/protocol/runner-protocol.d.ts.map +1 -0
- package/dist/{runner-protocol.js → protocol/runner-protocol.js} +2 -1
- package/dist/protocol/runner-protocol.js.map +1 -0
- package/dist/protocol/sse.d.ts.map +1 -0
- package/dist/protocol/sse.js.map +1 -0
- package/dist/{terminal-protocol.d.ts → protocol/terminal-protocol.d.ts} +1 -3
- package/dist/protocol/terminal-protocol.d.ts.map +1 -0
- package/dist/protocol/terminal-protocol.js.map +1 -0
- package/dist/protocol/webext-links.d.ts.map +1 -0
- package/dist/protocol/webext-links.js.map +1 -0
- package/dist/{webext-protocol.d.ts → protocol/webext-protocol.d.ts} +1 -0
- package/dist/protocol/webext-protocol.d.ts.map +1 -0
- package/dist/{webext-protocol.js → protocol/webext-protocol.js} +1 -0
- package/dist/protocol/webext-protocol.js.map +1 -0
- package/dist/schemas/activity.d.ts +2 -0
- package/dist/schemas/activity.d.ts.map +1 -1
- package/dist/schemas/activity.js +4 -0
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agent.d.ts +16 -4
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +21 -4
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +17 -4
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +15 -4
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/approvals.d.ts.map +1 -1
- package/dist/schemas/approvals.js.map +1 -1
- package/dist/schemas/automations.d.ts +54 -28
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js +26 -8
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/capabilities.d.ts +0 -8
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +0 -4
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/ci.d.ts +10 -0
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +9 -1
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/codebase-health.d.ts.map +1 -1
- package/dist/schemas/codebase-health.js.map +1 -1
- package/dist/schemas/devices.d.ts +90 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +10 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/engines.d.ts.map +1 -1
- package/dist/schemas/engines.js.map +1 -1
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/exit.d.ts.map +1 -1
- package/dist/schemas/exit.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/git-history.d.ts.map +1 -1
- package/dist/schemas/git-history.js.map +1 -1
- package/dist/schemas/git.d.ts +80 -16
- package/dist/schemas/git.d.ts.map +1 -1
- package/dist/schemas/git.js +37 -25
- package/dist/schemas/git.js.map +1 -1
- package/dist/schemas/history.d.ts.map +1 -1
- package/dist/schemas/history.js.map +1 -1
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/inventory.d.ts.map +1 -1
- package/dist/schemas/inventory.js.map +1 -1
- package/dist/schemas/issues.d.ts +0 -1
- package/dist/schemas/issues.d.ts.map +1 -1
- package/dist/schemas/issues.js +0 -1
- package/dist/schemas/issues.js.map +1 -1
- package/dist/schemas/logs.d.ts.map +1 -1
- package/dist/schemas/logs.js.map +1 -1
- package/dist/schemas/loops.d.ts.map +1 -1
- package/dist/schemas/loops.js +1 -1
- package/dist/schemas/loops.js.map +1 -1
- package/dist/schemas/maintenance.d.ts.map +1 -1
- package/dist/schemas/maintenance.js.map +1 -1
- package/dist/schemas/marketplace.d.ts +0 -4
- package/dist/schemas/marketplace.d.ts.map +1 -1
- package/dist/schemas/panels.d.ts.map +1 -1
- package/dist/schemas/panels.js.map +1 -1
- package/dist/schemas/personas.d.ts +2 -1
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +6 -2
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/plan-limits.d.ts +2 -4
- package/dist/schemas/plan-limits.d.ts.map +1 -1
- package/dist/schemas/plan-limits.js +5 -8
- package/dist/schemas/plan-limits.js.map +1 -1
- package/dist/schemas/ports.d.ts.map +1 -1
- package/dist/schemas/ports.js.map +1 -1
- package/dist/schemas/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/provider-oauth.js.map +1 -1
- package/dist/schemas/provider-subscriptions.d.ts +1 -1
- package/dist/schemas/provider-subscriptions.d.ts.map +1 -1
- package/dist/schemas/provider-subscriptions.js +1 -1
- package/dist/schemas/provider-subscriptions.js.map +1 -1
- package/dist/schemas/public.d.ts.map +1 -1
- package/dist/schemas/public.js.map +1 -1
- package/dist/schemas/push.d.ts.map +1 -1
- package/dist/schemas/push.js.map +1 -1
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings.d.ts +6 -9
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +16 -5
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/share.d.ts.map +1 -1
- package/dist/schemas/share.js.map +1 -1
- package/dist/schemas/shared.d.ts +4 -0
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/shared.js +3 -0
- package/dist/schemas/shared.js.map +1 -1
- package/dist/schemas/system.d.ts +6 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +10 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/usage.d.ts.map +1 -1
- package/dist/schemas/usage.js.map +1 -1
- package/dist/schemas/vpn.d.ts.map +1 -1
- package/dist/schemas/vpn.js.map +1 -1
- package/dist/schemas/webext.d.ts.map +1 -1
- package/dist/schemas/webext.js.map +1 -1
- package/dist/schemas/workflows.d.ts +66 -9
- package/dist/schemas/workflows.d.ts.map +1 -1
- package/dist/schemas/workflows.js +6 -5
- package/dist/schemas/workflows.js.map +1 -1
- package/dist/schemas/workspace-repos.d.ts.map +1 -1
- package/dist/schemas/workspace-repos.js.map +1 -1
- package/dist/schemas/workspace-search.d.ts.map +1 -1
- package/dist/schemas/workspace-search.js.map +1 -1
- package/dist/schemas/workspace-setup.d.ts.map +1 -1
- package/dist/schemas/workspace-setup.js.map +1 -1
- package/dist/schemas/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace-tree.js.map +1 -1
- package/dist/state/arrival.d.ts.map +1 -0
- package/dist/{arrival.js → state/arrival.js} +1 -1
- package/dist/state/arrival.js.map +1 -0
- package/dist/state/contract-lock.d.ts.map +1 -0
- package/dist/{contract-lock.js → state/contract-lock.js} +1 -1
- package/dist/state/contract-lock.js.map +1 -0
- package/dist/{definition.d.ts → state/definition.d.ts} +21 -17
- package/dist/state/definition.d.ts.map +1 -0
- package/dist/{definition.js → state/definition.js} +4 -4
- package/dist/state/definition.js.map +1 -0
- package/dist/state/history-state.d.ts.map +1 -0
- package/dist/{history-state.js → state/history-state.js} +1 -0
- package/dist/state/history-state.js.map +1 -0
- package/dist/state/runtime-state.d.ts.map +1 -0
- package/dist/state/runtime-state.js.map +1 -0
- package/dist/state/starter.d.ts.map +1 -0
- package/dist/state/starter.js.map +1 -0
- package/dist/state/state-portability.d.ts.map +1 -0
- package/dist/state/state-portability.js.map +1 -0
- package/dist/state/versions.d.ts.map +1 -0
- package/dist/state/versions.js.map +1 -0
- package/dist/{workspace-state.d.ts → state/workspace-state.d.ts} +8 -0
- package/dist/state/workspace-state.d.ts.map +1 -0
- package/dist/{workspace-state.js → state/workspace-state.js} +25 -0
- package/dist/state/workspace-state.js.map +1 -0
- package/dist/{documents.d.ts → text/documents.d.ts} +1 -1
- package/dist/text/documents.d.ts.map +1 -0
- package/dist/{documents.js → text/documents.js} +1 -1
- package/dist/text/documents.js.map +1 -0
- package/dist/text/embed.d.ts.map +1 -0
- package/dist/text/embed.js.map +1 -0
- package/dist/text/mentions.d.ts.map +1 -0
- package/dist/text/mentions.js.map +1 -0
- package/dist/text/model-answer.d.ts +3 -0
- package/dist/text/model-answer.d.ts.map +1 -0
- package/dist/text/model-answer.js +3 -0
- package/dist/text/model-answer.js.map +1 -0
- package/dist/text/path-refs.d.ts.map +1 -0
- package/dist/text/path-refs.js.map +1 -0
- package/dist/{shell-regions.d.ts → text/shell-regions.d.ts} +1 -1
- package/dist/text/shell-regions.d.ts.map +1 -0
- package/dist/text/shell-regions.js.map +1 -0
- package/dist/text/title.d.ts.map +1 -0
- package/dist/text/title.js.map +1 -0
- package/dist/{transcript-fold.d.ts → text/transcript-fold.d.ts} +2 -1
- package/dist/text/transcript-fold.d.ts.map +1 -0
- package/dist/{transcript-fold.js → text/transcript-fold.js} +2 -16
- package/dist/text/transcript-fold.js.map +1 -0
- package/dist/text/whisper.d.ts +3 -0
- package/dist/text/whisper.d.ts.map +1 -0
- package/dist/text/whisper.js +11 -0
- package/dist/text/whisper.js.map +1 -0
- package/dist/{workflow-faults.d.ts → text/workflow-faults.d.ts} +1 -1
- package/dist/text/workflow-faults.d.ts.map +1 -0
- package/dist/{workflow-faults.js → text/workflow-faults.js} +1 -1
- package/dist/text/workflow-faults.js.map +1 -0
- package/package.json +70 -70
- package/src/chores/chores.test.ts +4 -10
- package/src/chores/chores.ts +148 -392
- package/src/chores/digest.ts +8 -28
- package/src/chores/extension-update.ts +3 -8
- package/src/chores/fix-deps.ts +4 -18
- package/src/chores/index.ts +2 -2
- package/src/chores/probes.test.ts +7 -43
- package/src/chores/probes.ts +62 -167
- package/src/chores/prompt.ts +10 -34
- package/src/chores/stack.test.ts +6 -45
- package/src/chores/stack.ts +27 -118
- package/src/chores/verdict.test.ts +101 -80
- package/src/chores/verdict.ts +54 -99
- package/src/contracts/accounts.contract.ts +4 -19
- package/src/contracts/agent.contract.ts +7 -22
- package/src/contracts/agents.contract.ts +25 -71
- package/src/contracts/automations.contract.ts +14 -30
- package/src/contracts/capabilities.contract.ts +8 -35
- package/src/contracts/chores.contract.ts +4 -15
- package/src/contracts/endpoints.contract.ts +5 -22
- package/src/contracts/exit.contract.ts +7 -30
- package/src/contracts/extensions.contract.ts +8 -30
- package/src/contracts/git.contract.ts +25 -49
- package/src/contracts/host.contract.ts +9 -44
- package/src/contracts/intentic.contract.ts +1 -1
- package/src/contracts/logs.contract.ts +3 -13
- package/src/contracts/loops.contract.ts +9 -40
- package/src/contracts/personas.contract.ts +10 -35
- package/src/contracts/providers.contract.ts +4 -17
- package/src/contracts/runner.contract.ts +12 -39
- package/src/contracts/safety.contract.ts +5 -14
- package/src/contracts/secrets.contract.ts +4 -22
- package/src/contracts/sessions.contract.ts +1 -1
- package/src/contracts/settings.contract.ts +4 -10
- package/src/contracts/share.contract.ts +3 -9
- package/src/contracts/skills.contract.ts +4 -15
- package/src/contracts/system.contract.ts +26 -52
- package/src/contracts/usage.contract.ts +7 -21
- package/src/contracts/vpn.contract.ts +9 -16
- package/src/contracts/webext.contract.ts +9 -25
- package/src/contracts/workflows.contract.ts +34 -58
- package/src/contracts/workspace.contract.ts +19 -50
- package/src/events/agent-events.ts +345 -0
- package/src/events/cards.ts +286 -0
- package/src/{events.test.ts → events/resume.test.ts} +4 -19
- package/src/events/resume.ts +60 -0
- package/src/events/system-events.ts +139 -0
- package/src/events/transcript.ts +320 -0
- package/src/{conversation-ids.test.ts → ids/conversation-ids.test.ts} +25 -13
- package/src/ids/conversation-ids.ts +178 -0
- package/src/ids/hostnames.ts +129 -0
- package/src/ids/session-names.ts +29 -0
- package/src/ids/share-paths.ts +43 -0
- package/src/{tunnel-ids.test.ts → ids/tunnel-ids.test.ts} +1 -14
- package/src/ids/tunnel-ids.ts +29 -0
- package/src/index.ts +71 -87
- package/src/{agent-catalog.test.ts → models/agent-catalog.test.ts} +19 -93
- package/src/models/agent-catalog.ts +174 -0
- package/src/models/agent-runtimes.ts +201 -0
- package/src/models/capability-ledger.test.ts +84 -0
- package/src/{fast-tier.test.ts → models/fast-tier.test.ts} +7 -18
- package/src/models/fast-tier.ts +36 -0
- package/src/{model-order.test.ts → models/model-order.test.ts} +19 -40
- package/src/models/model-order.ts +163 -0
- package/src/{model-pins.test.ts → models/model-pins.test.ts} +12 -46
- package/src/models/model-pins.ts +57 -0
- package/src/models/model-roles.test.ts +38 -0
- package/src/models/model-roles.ts +180 -0
- package/src/{plan-pools.test.ts → models/plan-pools.test.ts} +6 -11
- package/src/models/plan-pools.ts +65 -0
- package/src/{prompt-complexity.test.ts → models/prompt-complexity.test.ts} +8 -60
- package/src/models/prompt-complexity.ts +215 -0
- package/src/{provider-specs.test.ts → models/provider-specs.test.ts} +18 -47
- package/src/models/provider-specs.ts +272 -0
- package/src/policy/approvals-execution.ts +58 -0
- package/src/{batch-runs.test.ts → policy/batch-runs.test.ts} +4 -20
- package/src/policy/batch-runs.ts +137 -0
- package/src/policy/capability-secrets.ts +5 -0
- package/src/{card-status.ts → policy/card-status.ts} +11 -26
- package/src/{command-classes.test.ts → policy/command-classes.test.ts} +11 -118
- package/src/policy/command-classes.ts +395 -0
- package/src/policy/command-run.ts +65 -0
- package/src/policy/control-scopes.ts +38 -0
- package/src/{credential-material.test.ts → policy/credential-material.test.ts} +4 -26
- package/src/policy/credential-material.ts +91 -0
- package/src/policy/needs-action.ts +5 -0
- package/src/policy/output-fields.ts +86 -0
- package/src/policy/overlay-lint.ts +100 -0
- package/src/{owner-ticket.test.ts → policy/owner-ticket.test.ts} +1 -1
- package/src/{owner-ticket.ts → policy/owner-ticket.ts} +9 -31
- package/src/policy/safety-policy.test.ts +84 -0
- package/src/policy/safety-policy.ts +142 -0
- package/src/policy/search-globs.ts +60 -0
- package/src/protocol/container-requirements.test.ts +72 -0
- package/src/protocol/container-requirements.ts +58 -0
- package/src/protocol/host-protocol.ts +29 -0
- package/src/protocol/ingress-contract.ts +103 -0
- package/src/{ingress-protocol.test.ts → protocol/ingress-protocol.test.ts} +25 -90
- package/src/protocol/ingress-protocol.ts +441 -0
- package/src/protocol/listener-protocol.ts +75 -0
- package/src/{peer-dial.test.ts → protocol/peer-dial.test.ts} +52 -1
- package/src/protocol/peer-dial.ts +207 -0
- package/src/{peer-mcp-server.ts → protocol/peer-mcp-server.ts} +16 -41
- package/src/protocol/request-id.ts +5 -0
- package/src/{routes.test.ts → protocol/routes.test.ts} +14 -22
- package/src/protocol/routes.ts +159 -0
- package/src/protocol/runner-protocol.ts +197 -0
- package/src/protocol/terminal-protocol.ts +13 -0
- package/src/protocol/webext-links.ts +50 -0
- package/src/protocol/webext-protocol.ts +24 -0
- package/src/schemas/activity.ts +22 -30
- package/src/schemas/agent.ts +130 -238
- package/src/schemas/agents.ts +165 -483
- package/src/schemas/approvals.ts +23 -71
- package/src/schemas/automations.ts +142 -250
- package/src/schemas/capabilities.ts +127 -414
- package/src/schemas/ci.ts +56 -108
- package/src/schemas/codebase-health.ts +7 -11
- package/src/schemas/devices.ts +98 -321
- package/src/schemas/engines.ts +16 -44
- package/src/schemas/environment.ts +41 -91
- package/src/schemas/exit.ts +24 -81
- package/src/schemas/extension-updates.ts +42 -87
- package/src/schemas/git-history.ts +30 -84
- package/src/schemas/git.ts +141 -256
- package/src/schemas/history.ts +14 -40
- package/src/schemas/hosts.ts +9 -15
- package/src/schemas/inventory.ts +8 -10
- package/src/schemas/issues.ts +66 -134
- package/src/schemas/logs.ts +15 -34
- package/src/schemas/loops.ts +46 -163
- package/src/schemas/maintenance.ts +47 -172
- package/src/schemas/panels.ts +20 -45
- package/src/schemas/personas.ts +50 -200
- package/src/schemas/plan-limits.ts +58 -197
- package/src/schemas/ports.ts +14 -32
- package/src/schemas/provider-oauth.ts +23 -76
- package/src/schemas/provider-subscriptions.ts +4 -13
- package/src/schemas/public.ts +4 -15
- package/src/schemas/push.ts +8 -36
- package/src/schemas/secrets.ts +20 -64
- package/src/schemas/settings.ts +175 -600
- package/src/schemas/share.ts +14 -32
- package/src/schemas/shared.ts +14 -13
- package/src/schemas/system.ts +46 -78
- package/src/schemas/terminal.ts +41 -120
- package/src/schemas/usage.ts +44 -233
- package/src/schemas/version-seam.test.ts +9 -26
- package/src/schemas/vpn.ts +27 -64
- package/src/schemas/webext.ts +22 -53
- package/src/schemas/workflows.ts +57 -183
- package/src/schemas/workspace-repos.ts +12 -24
- package/src/schemas/workspace-search.ts +17 -35
- package/src/schemas/workspace-setup.ts +4 -11
- package/src/schemas/workspace-tree.ts +32 -91
- package/src/state/arrival.ts +109 -0
- package/src/state/contract-lock.test.ts +17 -0
- package/src/state/contract-lock.ts +49 -0
- package/src/state/definition.ts +143 -0
- package/src/state/history-state.ts +103 -0
- package/src/{runtime-state.test.ts → state/runtime-state.test.ts} +3 -10
- package/src/state/runtime-state.ts +62 -0
- package/src/state/starter.ts +5 -0
- package/src/state/state-portability.ts +27 -0
- package/src/state/versions.ts +26 -0
- package/src/{workspace-state.test.ts → state/workspace-state.test.ts} +65 -172
- package/src/state/workspace-state.ts +669 -0
- package/src/{documents.test.ts → text/documents.test.ts} +1 -1
- package/src/text/documents.ts +42 -0
- package/src/text/embed.ts +132 -0
- package/src/text/mentions.ts +21 -0
- package/src/text/model-answer.ts +9 -0
- package/src/text/path-refs.ts +39 -0
- package/src/text/shell-regions.ts +224 -0
- package/src/{title.test.ts → text/title.test.ts} +13 -32
- package/src/text/title.ts +202 -0
- package/src/{transcript-fold.test.ts → text/transcript-fold.test.ts} +5 -83
- package/src/{transcript-fold.ts → text/transcript-fold.ts} +65 -170
- package/src/text/whisper.test.ts +19 -0
- package/src/text/whisper.ts +17 -0
- package/src/{workflow-faults.test.ts → text/workflow-faults.test.ts} +6 -24
- package/src/{workflow-faults.ts → text/workflow-faults.ts} +20 -60
- package/dist/agent-catalog.d.ts.map +0 -1
- package/dist/agent-catalog.js.map +0 -1
- package/dist/agent-runtimes.d.ts.map +0 -1
- package/dist/agent-runtimes.js.map +0 -1
- package/dist/approvals-execution.d.ts.map +0 -1
- package/dist/approvals-execution.js.map +0 -1
- package/dist/arrival.d.ts.map +0 -1
- package/dist/arrival.js.map +0 -1
- package/dist/batch-runs.d.ts.map +0 -1
- package/dist/batch-runs.js.map +0 -1
- package/dist/capability-env.d.ts.map +0 -1
- package/dist/capability-env.js.map +0 -1
- package/dist/capability-secrets.d.ts.map +0 -1
- package/dist/capability-secrets.js.map +0 -1
- package/dist/card-status.d.ts.map +0 -1
- package/dist/card-status.js.map +0 -1
- package/dist/command-classes.d.ts.map +0 -1
- package/dist/command-classes.js.map +0 -1
- package/dist/command-run.d.ts.map +0 -1
- package/dist/command-run.js.map +0 -1
- package/dist/contract-lock.d.ts.map +0 -1
- package/dist/contract-lock.js.map +0 -1
- package/dist/conversation-ids.d.ts +0 -4
- package/dist/conversation-ids.d.ts.map +0 -1
- package/dist/conversation-ids.js.map +0 -1
- package/dist/credential-material.d.ts.map +0 -1
- package/dist/credential-material.js.map +0 -1
- package/dist/definition.d.ts.map +0 -1
- package/dist/definition.js.map +0 -1
- package/dist/documents.d.ts.map +0 -1
- package/dist/documents.js.map +0 -1
- package/dist/embed.d.ts.map +0 -1
- package/dist/embed.js.map +0 -1
- package/dist/events.d.ts +0 -4336
- package/dist/events.d.ts.map +0 -1
- package/dist/events.js +0 -725
- package/dist/events.js.map +0 -1
- package/dist/fast-tier.d.ts.map +0 -1
- package/dist/fast-tier.js.map +0 -1
- package/dist/history-state.d.ts.map +0 -1
- package/dist/history-state.js.map +0 -1
- package/dist/host-protocol.d.ts.map +0 -1
- package/dist/host-protocol.js.map +0 -1
- package/dist/hostnames.d.ts.map +0 -1
- package/dist/hostnames.js.map +0 -1
- package/dist/ingress-contract.d.ts.map +0 -1
- package/dist/ingress-contract.js.map +0 -1
- package/dist/ingress-protocol.d.ts.map +0 -1
- package/dist/ingress-protocol.js.map +0 -1
- package/dist/listener-protocol.d.ts.map +0 -1
- package/dist/listener-protocol.js.map +0 -1
- package/dist/mentions.d.ts.map +0 -1
- package/dist/mentions.js.map +0 -1
- package/dist/model-order.d.ts.map +0 -1
- package/dist/model-order.js.map +0 -1
- package/dist/model-pins.d.ts.map +0 -1
- package/dist/model-pins.js.map +0 -1
- package/dist/model-roles.d.ts.map +0 -1
- package/dist/model-roles.js.map +0 -1
- package/dist/needs-action.d.ts.map +0 -1
- package/dist/needs-action.js.map +0 -1
- package/dist/output-fields.d.ts.map +0 -1
- package/dist/output-fields.js.map +0 -1
- package/dist/overlay-lint.d.ts.map +0 -1
- package/dist/overlay-lint.js.map +0 -1
- package/dist/owner-ticket.d.ts.map +0 -1
- package/dist/owner-ticket.js.map +0 -1
- package/dist/path-refs.d.ts.map +0 -1
- package/dist/path-refs.js.map +0 -1
- package/dist/peer-dial.d.ts.map +0 -1
- package/dist/peer-dial.js.map +0 -1
- package/dist/peer-mcp-server.d.ts.map +0 -1
- package/dist/peer-mcp-server.js.map +0 -1
- package/dist/plan-pools.d.ts.map +0 -1
- package/dist/plan-pools.js.map +0 -1
- package/dist/prompt-complexity.d.ts.map +0 -1
- package/dist/prompt-complexity.js.map +0 -1
- package/dist/provider-specs.d.ts.map +0 -1
- package/dist/provider-specs.js.map +0 -1
- package/dist/request-id.d.ts.map +0 -1
- package/dist/request-id.js.map +0 -1
- package/dist/routes.d.ts.map +0 -1
- package/dist/routes.js.map +0 -1
- package/dist/runner-protocol.d.ts.map +0 -1
- package/dist/runner-protocol.js.map +0 -1
- package/dist/runtime-state.d.ts.map +0 -1
- package/dist/runtime-state.js.map +0 -1
- package/dist/safety-policy.d.ts.map +0 -1
- package/dist/safety-policy.js.map +0 -1
- package/dist/search-globs.d.ts.map +0 -1
- package/dist/search-globs.js.map +0 -1
- package/dist/session-names.d.ts.map +0 -1
- package/dist/session-names.js.map +0 -1
- package/dist/share-paths.d.ts.map +0 -1
- package/dist/share-paths.js.map +0 -1
- package/dist/shell-regions.d.ts.map +0 -1
- package/dist/shell-regions.js.map +0 -1
- package/dist/sse.d.ts.map +0 -1
- package/dist/sse.js.map +0 -1
- package/dist/starter.d.ts.map +0 -1
- package/dist/starter.js.map +0 -1
- package/dist/state-portability.d.ts.map +0 -1
- package/dist/state-portability.js.map +0 -1
- package/dist/terminal-protocol.d.ts.map +0 -1
- package/dist/terminal-protocol.js.map +0 -1
- package/dist/title.d.ts.map +0 -1
- package/dist/title.js.map +0 -1
- package/dist/transcript-fold.d.ts.map +0 -1
- package/dist/transcript-fold.js.map +0 -1
- package/dist/tunnel-ids.d.ts.map +0 -1
- package/dist/tunnel-ids.js.map +0 -1
- package/dist/versions.d.ts.map +0 -1
- package/dist/versions.js.map +0 -1
- package/dist/webext-links.d.ts.map +0 -1
- package/dist/webext-links.js.map +0 -1
- package/dist/webext-protocol.d.ts.map +0 -1
- package/dist/webext-protocol.js.map +0 -1
- package/dist/workflow-faults.d.ts.map +0 -1
- package/dist/workflow-faults.js.map +0 -1
- package/dist/workspace-state.d.ts.map +0 -1
- package/dist/workspace-state.js.map +0 -1
- package/src/agent-catalog.ts +0 -316
- package/src/agent-runtimes.ts +0 -419
- package/src/approvals-execution.ts +0 -96
- package/src/arrival.ts +0 -160
- package/src/batch-runs.ts +0 -188
- package/src/capability-ledger.test.ts +0 -140
- package/src/capability-secrets.ts +0 -20
- package/src/command-classes.ts +0 -617
- package/src/command-run.ts +0 -78
- package/src/contract-lock.test.ts +0 -23
- package/src/contract-lock.ts +0 -66
- package/src/conversation-ids.ts +0 -207
- package/src/credential-material.ts +0 -181
- package/src/definition.ts +0 -207
- package/src/documents.ts +0 -67
- package/src/embed.ts +0 -164
- package/src/events.ts +0 -1870
- package/src/fast-tier.ts +0 -72
- package/src/history-state.ts +0 -174
- package/src/host-protocol.ts +0 -36
- package/src/hostnames.ts +0 -180
- package/src/ingress-contract.ts +0 -157
- package/src/ingress-protocol.ts +0 -625
- package/src/listener-protocol.ts +0 -96
- package/src/mentions.ts +0 -25
- package/src/model-order.ts +0 -262
- package/src/model-pins.ts +0 -132
- package/src/model-roles.test.ts +0 -52
- package/src/model-roles.ts +0 -320
- package/src/needs-action.ts +0 -14
- package/src/output-fields.ts +0 -111
- package/src/overlay-lint.ts +0 -116
- package/src/path-refs.ts +0 -59
- package/src/peer-dial.ts +0 -163
- package/src/plan-pools.ts +0 -92
- package/src/prompt-complexity.ts +0 -334
- package/src/provider-specs.ts +0 -432
- package/src/request-id.ts +0 -41
- package/src/routes.ts +0 -219
- package/src/runner-protocol.ts +0 -239
- package/src/runtime-state.ts +0 -140
- package/src/safety-policy.test.ts +0 -88
- package/src/safety-policy.ts +0 -258
- package/src/search-globs.ts +0 -76
- package/src/session-names.ts +0 -44
- package/src/share-paths.ts +0 -68
- package/src/shell-regions.ts +0 -289
- package/src/starter.ts +0 -13
- package/src/state-portability.ts +0 -56
- package/src/terminal-protocol.ts +0 -16
- package/src/title.ts +0 -267
- package/src/tunnel-ids.ts +0 -57
- package/src/versions.ts +0 -48
- package/src/webext-links.ts +0 -90
- package/src/webext-protocol.ts +0 -27
- package/src/workspace-state.ts +0 -1090
- /package/dist/{hostnames.d.ts → ids/hostnames.d.ts} +0 -0
- /package/dist/{hostnames.js → ids/hostnames.js} +0 -0
- /package/dist/{session-names.d.ts → ids/session-names.d.ts} +0 -0
- /package/dist/{session-names.js → ids/session-names.js} +0 -0
- /package/dist/{share-paths.d.ts → ids/share-paths.d.ts} +0 -0
- /package/dist/{share-paths.js → ids/share-paths.js} +0 -0
- /package/dist/{agent-catalog.js → models/agent-catalog.js} +0 -0
- /package/dist/{agent-runtimes.d.ts → models/agent-runtimes.d.ts} +0 -0
- /package/dist/{agent-runtimes.js → models/agent-runtimes.js} +0 -0
- /package/dist/{fast-tier.js → models/fast-tier.js} +0 -0
- /package/dist/{model-order.d.ts → models/model-order.d.ts} +0 -0
- /package/dist/{model-order.js → models/model-order.js} +0 -0
- /package/dist/{model-pins.js → models/model-pins.js} +0 -0
- /package/dist/{plan-pools.js → models/plan-pools.js} +0 -0
- /package/dist/{prompt-complexity.d.ts → models/prompt-complexity.d.ts} +0 -0
- /package/dist/{prompt-complexity.js → models/prompt-complexity.js} +0 -0
- /package/dist/{provider-specs.d.ts → models/provider-specs.d.ts} +0 -0
- /package/dist/{provider-specs.js → models/provider-specs.js} +0 -0
- /package/dist/{approvals-execution.d.ts → policy/approvals-execution.d.ts} +0 -0
- /package/dist/{approvals-execution.js → policy/approvals-execution.js} +0 -0
- /package/dist/{capability-env.d.ts → policy/capability-env.d.ts} +0 -0
- /package/dist/{capability-env.js → policy/capability-env.js} +0 -0
- /package/dist/{capability-secrets.d.ts → policy/capability-secrets.d.ts} +0 -0
- /package/dist/{capability-secrets.js → policy/capability-secrets.js} +0 -0
- /package/dist/{command-run.js → policy/command-run.js} +0 -0
- /package/dist/{credential-material.d.ts → policy/credential-material.d.ts} +0 -0
- /package/dist/{credential-material.js → policy/credential-material.js} +0 -0
- /package/dist/{needs-action.d.ts → policy/needs-action.d.ts} +0 -0
- /package/dist/{needs-action.js → policy/needs-action.js} +0 -0
- /package/dist/{output-fields.d.ts → policy/output-fields.d.ts} +0 -0
- /package/dist/{output-fields.js → policy/output-fields.js} +0 -0
- /package/dist/{overlay-lint.d.ts → policy/overlay-lint.d.ts} +0 -0
- /package/dist/{overlay-lint.js → policy/overlay-lint.js} +0 -0
- /package/dist/{owner-ticket.d.ts → policy/owner-ticket.d.ts} +0 -0
- /package/dist/{search-globs.d.ts → policy/search-globs.d.ts} +0 -0
- /package/dist/{search-globs.js → policy/search-globs.js} +0 -0
- /package/dist/{ingress-contract.d.ts → protocol/ingress-contract.d.ts} +0 -0
- /package/dist/{ingress-protocol.d.ts → protocol/ingress-protocol.d.ts} +0 -0
- /package/dist/{ingress-protocol.js → protocol/ingress-protocol.js} +0 -0
- /package/dist/{listener-protocol.d.ts → protocol/listener-protocol.d.ts} +0 -0
- /package/dist/{peer-mcp-server.d.ts → protocol/peer-mcp-server.d.ts} +0 -0
- /package/dist/{peer-mcp-server.js → protocol/peer-mcp-server.js} +0 -0
- /package/dist/{request-id.d.ts → protocol/request-id.d.ts} +0 -0
- /package/dist/{request-id.js → protocol/request-id.js} +0 -0
- /package/dist/{routes.d.ts → protocol/routes.d.ts} +0 -0
- /package/dist/{routes.js → protocol/routes.js} +0 -0
- /package/dist/{sse.d.ts → protocol/sse.d.ts} +0 -0
- /package/dist/{sse.js → protocol/sse.js} +0 -0
- /package/dist/{terminal-protocol.js → protocol/terminal-protocol.js} +0 -0
- /package/dist/{webext-links.d.ts → protocol/webext-links.d.ts} +0 -0
- /package/dist/{webext-links.js → protocol/webext-links.js} +0 -0
- /package/dist/{arrival.d.ts → state/arrival.d.ts} +0 -0
- /package/dist/{contract-lock.d.ts → state/contract-lock.d.ts} +0 -0
- /package/dist/{history-state.d.ts → state/history-state.d.ts} +0 -0
- /package/dist/{runtime-state.d.ts → state/runtime-state.d.ts} +0 -0
- /package/dist/{runtime-state.js → state/runtime-state.js} +0 -0
- /package/dist/{starter.d.ts → state/starter.d.ts} +0 -0
- /package/dist/{starter.js → state/starter.js} +0 -0
- /package/dist/{state-portability.d.ts → state/state-portability.d.ts} +0 -0
- /package/dist/{state-portability.js → state/state-portability.js} +0 -0
- /package/dist/{versions.d.ts → state/versions.d.ts} +0 -0
- /package/dist/{versions.js → state/versions.js} +0 -0
- /package/dist/{embed.d.ts → text/embed.d.ts} +0 -0
- /package/dist/{embed.js → text/embed.js} +0 -0
- /package/dist/{mentions.d.ts → text/mentions.d.ts} +0 -0
- /package/dist/{mentions.js → text/mentions.js} +0 -0
- /package/dist/{path-refs.d.ts → text/path-refs.d.ts} +0 -0
- /package/dist/{path-refs.js → text/path-refs.js} +0 -0
- /package/dist/{shell-regions.js → text/shell-regions.js} +0 -0
- /package/dist/{title.d.ts → text/title.d.ts} +0 -0
- /package/dist/{title.js → text/title.js} +0 -0
- /package/src/{hostnames.test.ts → ids/hostnames.test.ts} +0 -0
- /package/src/{share-paths.test.ts → ids/share-paths.test.ts} +0 -0
- /package/src/{capability-env.ts → policy/capability-env.ts} +0 -0
- /package/src/{overlay-lint.test.ts → policy/overlay-lint.test.ts} +0 -0
- /package/src/{search-globs.test.ts → policy/search-globs.test.ts} +0 -0
- /package/src/{ingress-contract.test.ts → protocol/ingress-contract.test.ts} +0 -0
- /package/src/{peer-mcp-server.test.ts → protocol/peer-mcp-server.test.ts} +0 -0
- /package/src/{sse.ts → protocol/sse.ts} +0 -0
- /package/src/{versions.test.ts → state/versions.test.ts} +0 -0
- /package/src/{embed.test.ts → text/embed.test.ts} +0 -0
- /package/src/{mentions.test.ts → text/mentions.test.ts} +0 -0
- /package/src/{path-refs.test.ts → text/path-refs.test.ts} +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// What the search box's second field means: VSCode's "files to include" grammar, read the same way everywhere it runs
|
|
2
|
+
// (the daemon, the recorded demo):
|
|
3
|
+
// - a bare name matches both the file and the folder (`p` and `p/**`)
|
|
4
|
+
// - a path matches at any depth unless it starts with `./` or `/` (anchored to the root)
|
|
5
|
+
// - a leading dot is an extension shorthand (`.ts` becomes `*.ts`); a trailing slash is dropped
|
|
6
|
+
// - commas split patterns, except inside `{...}` or `[...]`
|
|
7
|
+
// - a leading `!` excludes; this app's own addition, not VSCode's
|
|
8
|
+
|
|
9
|
+
// VSCode's splitGlobAware: the split character is inert inside a brace group or character class.
|
|
10
|
+
const splitPatterns = (include: string, splitChar: string): string[] => {
|
|
11
|
+
const segments: string[] = [];
|
|
12
|
+
let current = ``;
|
|
13
|
+
let inBraces = false;
|
|
14
|
+
let inBrackets = false;
|
|
15
|
+
for (const char of include) {
|
|
16
|
+
if (char === splitChar && !inBraces && !inBrackets) {
|
|
17
|
+
segments.push(current);
|
|
18
|
+
current = ``;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
inBraces = char === `{` ? true : char === `}` ? false : inBraces;
|
|
22
|
+
inBrackets = char === `[` ? true : char === `]` ? false : inBrackets;
|
|
23
|
+
current += char;
|
|
24
|
+
}
|
|
25
|
+
segments.push(current);
|
|
26
|
+
return segments.map((segment) => segment.trim()).filter((segment) => segment !== ``);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// One typed segment maps to two globs: the file itself, and everything under a folder of that name. An anchored form
|
|
30
|
+
// (`./p`, `/p`) keeps its `./` prefix, since that's how the engine tells "anchored" from "any depth".
|
|
31
|
+
const expand = (segment: string): string[] => {
|
|
32
|
+
const trimmed = segment.replace(/\/+$/, ``);
|
|
33
|
+
if (trimmed === ``) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
const anchored = /^\.?\//.test(trimmed);
|
|
37
|
+
if (anchored) {
|
|
38
|
+
const path = trimmed.replace(/^\.?\//, ``).replace(/^\/+/, ``);
|
|
39
|
+
return path === `` ? [] : [`./${path}`, `./${path}/**`];
|
|
40
|
+
}
|
|
41
|
+
// ".ts" is how people write an extension filter; VSCode reads it as "*.ts" rather than a hidden file.
|
|
42
|
+
const pattern = trimmed.startsWith(`.`) ? `*${trimmed}` : trimmed;
|
|
43
|
+
return [`**/${pattern}`, `**/${pattern}/**`];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export interface IncludeGlobs {
|
|
47
|
+
// Files the search is limited to, empty means the whole workspace. OR'ed: any pattern matching admits.
|
|
48
|
+
readonly globs: readonly string[];
|
|
49
|
+
// Files kept out of it, whatever the includes said.
|
|
50
|
+
readonly notGlobs: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const includeGlobs = (include: string | undefined): IncludeGlobs => {
|
|
54
|
+
const segments = splitPatterns(include ?? ``, `,`);
|
|
55
|
+
return {
|
|
56
|
+
globs: segments.filter((segment) => !segment.startsWith(`!`)).flatMap(expand),
|
|
57
|
+
// A bare "!" excludes nothing; without this it would expand into a glob that matches everything.
|
|
58
|
+
notGlobs: segments.filter((segment) => segment.startsWith(`!`)).flatMap((segment) => expand(segment.slice(1))),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { CONTAINER_REQUIREMENTS, containerDrift } from "./container-requirements.js";
|
|
3
|
+
|
|
4
|
+
// Healthy baseline: every case below removes one piece of it to produce a gap.
|
|
5
|
+
const CURRENT = {
|
|
6
|
+
SANDBOX_PUBLIC_URL: "https://sandbox-0738cd6b5027.sbx.intentic.dev",
|
|
7
|
+
SANDBOX_GRANT: "ig1.eyJzdWIiOiIwNzM4Y2Q2YjUwMjcifQ.sig",
|
|
8
|
+
INGRESS_URL: "https://ingress.intentic.dev",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe(`containerDrift`, () => {
|
|
12
|
+
it(`says nothing about a container set up by a current release`, () => {
|
|
13
|
+
expect(containerDrift(CURRENT)).toEqual([]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it(`names both keys a pre-migration container is missing, and what that costs`, () => {
|
|
17
|
+
const gaps = containerDrift({ SANDBOX_PUBLIC_URL: CURRENT.SANDBOX_PUBLIC_URL });
|
|
18
|
+
expect(gaps).toHaveLength(1);
|
|
19
|
+
expect(gaps[0]?.key).toBe(`reachability`);
|
|
20
|
+
expect(gaps[0]?.missing).toEqual([`SANDBOX_GRANT`, `INGRESS_URL`]);
|
|
21
|
+
expect(gaps[0]?.repair).toContain(`setup command`);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it(`reports only the half that is actually gone`, () => {
|
|
25
|
+
expect(containerDrift({ ...CURRENT, INGRESS_URL: undefined })[0]?.missing).toEqual([`INGRESS_URL`]);
|
|
26
|
+
expect(containerDrift({ ...CURRENT, SANDBOX_GRANT: undefined })[0]?.missing).toEqual([`SANDBOX_GRANT`]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Blank must count as missing: it is indistinguishable from a dropped value replayed through `-e NAME=`.
|
|
30
|
+
it(`counts blank and whitespace-only values as missing, not as set`, () => {
|
|
31
|
+
expect(containerDrift({ ...CURRENT, SANDBOX_GRANT: `` })[0]?.missing).toEqual([`SANDBOX_GRANT`]);
|
|
32
|
+
expect(containerDrift({ ...CURRENT, SANDBOX_GRANT: ` ` })[0]?.missing).toEqual([`SANDBOX_GRANT`]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// No public address means no evidence of intent, so nothing is reported as missing.
|
|
36
|
+
it(`asks nothing of a sandbox that offers no evidence it was meant to be reachable`, () => {
|
|
37
|
+
expect(containerDrift({})).toEqual([]);
|
|
38
|
+
expect(containerDrift({ CONNECT_TOKEN: `tok`, WORKSPACE_ROOT: `/work` })).toEqual([]);
|
|
39
|
+
expect(containerDrift({ SANDBOX_PUBLIC_URL: `` })).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// A hosted Fly microVM has no grant by design, so this reports nothing on it.
|
|
43
|
+
it(`stays quiet on a hosted machine, which serves its address without dialling out`, () => {
|
|
44
|
+
expect(containerDrift({ SANDBOX_PUBLIC_URL: CURRENT.SANDBOX_PUBLIC_URL, SANDBOX_VM: `1` })).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe(`CONTAINER_REQUIREMENTS`, () => {
|
|
49
|
+
it(`keys every row uniquely, so a UI can key and dedupe on it`, () => {
|
|
50
|
+
const keys = CONTAINER_REQUIREMENTS.map((requirement) => requirement.key);
|
|
51
|
+
expect(new Set(keys).size).toBe(keys.length);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Overlapping `given`/`requires` would make the row impossible to fire.
|
|
55
|
+
// Empty `given` would make the row fire on every sandbox in the fleet.
|
|
56
|
+
it(`states evidence that is separate from what it demands`, () => {
|
|
57
|
+
for (const requirement of CONTAINER_REQUIREMENTS) {
|
|
58
|
+
expect(requirement.given.length).toBeGreaterThan(0);
|
|
59
|
+
expect(requirement.requires.length).toBeGreaterThan(0);
|
|
60
|
+
expect(requirement.given.filter((name) => requirement.requires.includes(name))).toEqual([]);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Rendered verbatim in the product, so an empty string is a blank card, not just a missing test value.
|
|
65
|
+
it(`words every row for the person who has to fix it`, () => {
|
|
66
|
+
for (const requirement of CONTAINER_REQUIREMENTS) {
|
|
67
|
+
expect(requirement.enables.length).toBeGreaterThan(0);
|
|
68
|
+
expect(requirement.lost.length).toBeGreaterThan(0);
|
|
69
|
+
expect(requirement.repair.length).toBeGreaterThan(0);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// A row belongs only if its keys come from setup, their absence is settled, and other env proves intent.
|
|
2
|
+
// Add a row here, add it to `ic sandbox doctor`'s `classify_public` too: the two tables are not kept in sync.
|
|
3
|
+
|
|
4
|
+
/** `enables` and `lost` render verbatim to the user: word them for the product, not the env. */
|
|
5
|
+
export interface ContainerRequirement {
|
|
6
|
+
readonly key: string;
|
|
7
|
+
readonly requires: readonly string[];
|
|
8
|
+
// Absent `given` evidence means this sandbox never had the capability: not a fault, just unconfigured.
|
|
9
|
+
readonly given: readonly string[];
|
|
10
|
+
// Row stays silent if any `unless` key is present: an alternate mechanism already satisfies the evidence.
|
|
11
|
+
readonly unless?: readonly string[];
|
|
12
|
+
readonly enables: string;
|
|
13
|
+
readonly lost: string;
|
|
14
|
+
readonly repair: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Undefined and empty env values are equivalent: both count as missing. */
|
|
18
|
+
export type ContainerEnv = Readonly<Record<string, string | undefined>>;
|
|
19
|
+
|
|
20
|
+
const present = (env: ContainerEnv, name: string): boolean => (env[name] ?? "").trim() !== "";
|
|
21
|
+
|
|
22
|
+
export const CONTAINER_REQUIREMENTS: readonly ContainerRequirement[] = [
|
|
23
|
+
// A pre-migration container has the public name but nothing to serve it with: 502 at the edge, forever.
|
|
24
|
+
{
|
|
25
|
+
key: "reachability",
|
|
26
|
+
requires: ["SANDBOX_GRANT", "INGRESS_URL"],
|
|
27
|
+
given: ["SANDBOX_PUBLIC_URL"],
|
|
28
|
+
// A Fly microVM's edge replays directly to it, so it holds no grant by design.
|
|
29
|
+
unless: ["SANDBOX_VM"],
|
|
30
|
+
enables: "reaching this sandbox at its public address, from anywhere",
|
|
31
|
+
lost:
|
|
32
|
+
"Its public address answers 502 to everyone, including this browser when it is not on the same machine as the sandbox. " +
|
|
33
|
+
"On that machine the workspace still works, over a direct connection that never leaves it — which is why this can go unnoticed for a long time.",
|
|
34
|
+
repair: "Re-run this sandbox's setup command. It carries a freshly signed grant, and it keeps the workspace, its history and its Docker engine.",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export interface ContainerGap {
|
|
39
|
+
readonly key: string;
|
|
40
|
+
readonly missing: readonly string[];
|
|
41
|
+
readonly enables: string;
|
|
42
|
+
readonly lost: string;
|
|
43
|
+
readonly repair: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Empty means every requirement is met or not applicable; non-empty always means a setup rerun is needed. */
|
|
47
|
+
export const containerDrift = (env: ContainerEnv): readonly ContainerGap[] =>
|
|
48
|
+
CONTAINER_REQUIREMENTS.filter(
|
|
49
|
+
(requirement) => requirement.given.every((name) => present(env, name)) && !(requirement.unless ?? []).some((name) => present(env, name)),
|
|
50
|
+
)
|
|
51
|
+
.map((requirement) => ({
|
|
52
|
+
key: requirement.key,
|
|
53
|
+
missing: requirement.requires.filter((name) => !present(env, name)),
|
|
54
|
+
enables: requirement.enables,
|
|
55
|
+
lost: requirement.lost,
|
|
56
|
+
repair: requirement.repair,
|
|
57
|
+
}))
|
|
58
|
+
.filter((gap) => gap.missing.length > 0);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// The handshake on /system/hosts/connect, the one message on this socket that isn't oRPC. Proving whose socket it is
|
|
4
|
+
// can't itself be an oRPC call, so the machine's first frame is plain JSON; the daemon verifies it and only then
|
|
5
|
+
// attaches the typed link. Anything before that link exists is this frame or a closed socket.
|
|
6
|
+
|
|
7
|
+
// Shared with the daemon's peer bridge, which answers the handshake itself when the machine is asleep.
|
|
8
|
+
export const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
9
|
+
|
|
10
|
+
/* HOW OFTEN THIS DOOR PINGS A CONNECTED MACHINE, read by both sides of the socket: the daemon's hub pings on
|
|
11
|
+
* it (hosts/host-peer.ts) and the agent presumes a link dead after a few of them pass in silence
|
|
12
|
+
* (peer-dial.ts's peerLinkSilenceMs). Frequent enough to stay inside the idle timeout of every tunnel and proxy
|
|
13
|
+
* in the path, and it is the failure of a ping that tells the daemon a lid closed without a close frame ever
|
|
14
|
+
* arriving. One number, because the two sides disagreeing about it is a device that is either dropped while
|
|
15
|
+
* healthy or believed alive for as long as its socket stays open. */
|
|
16
|
+
export const HOST_HEARTBEAT_MS = 30_000;
|
|
17
|
+
|
|
18
|
+
export const HostHelloSchema = z.object({
|
|
19
|
+
type: z.literal("hello"),
|
|
20
|
+
// Sent in the first frame, never the URL (a WebSocket has no headers, and a URL gets logged everywhere).
|
|
21
|
+
token: z.string(),
|
|
22
|
+
// The build the machine runs, surfaced so an old binary is visible rather than mysteriously missing a tool.
|
|
23
|
+
version: z.string(),
|
|
24
|
+
});
|
|
25
|
+
export type HostHello = z.infer<typeof HostHelloSchema>;
|
|
26
|
+
|
|
27
|
+
// The URL the machine's agent dials, given the sandbox's public URL. Carries no credential (the token rides the hello
|
|
28
|
+
// frame); one place builds it, so the agent and the daemon route can't disagree.
|
|
29
|
+
export const hostConnectUrl = (sandboxUrl: string): string => `${sandboxUrl.replace(/^http/, "ws").replace(/\/$/, "")}/system/hosts/connect`;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createPrivateKey, createPublicKey, sign as edSign, verify as edVerify } from "node:crypto";
|
|
2
|
+
import { SANDBOX_ID } from "../ids/tunnel-ids.js";
|
|
3
|
+
|
|
4
|
+
// A sandbox proves identity with a signed grant; a hostname's embedded id resolves ownership by parsing, not a registry
|
|
5
|
+
// lookup. Platform, ingress and daemon must agree on every string here. Routing is per h2 stream, since one TLS
|
|
6
|
+
// connection can carry multiple sandboxes' hosts.
|
|
7
|
+
|
|
8
|
+
// The reachability grant
|
|
9
|
+
|
|
10
|
+
// Version prefix: an ingress that doesn't recognize it refuses the tunnel rather than negotiating a shape.
|
|
11
|
+
const GRANT_PREFIX = "ig1";
|
|
12
|
+
|
|
13
|
+
const base64url = (bytes: Buffer): string => bytes.toString("base64url");
|
|
14
|
+
|
|
15
|
+
// Signed claim; wire fields are `sub` (12-hex sandbox id) and `iat` (seconds). No expiry: the grant lives for a
|
|
16
|
+
// container's whole life, and revocation is deleting the sandbox row.
|
|
17
|
+
export interface ReachabilityGrant {
|
|
18
|
+
readonly sandboxId: string;
|
|
19
|
+
readonly issuedAt: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Mints a grant: an Ed25519 signature over the JSON `{sub, iat}` payload, with a PEM/PKCS8 private key.
|
|
23
|
+
export const mintReachabilityGrant = (privateKeyPem: string, sandboxId: string, issuedAtMs: number): string => {
|
|
24
|
+
if (!SANDBOX_ID.test(sandboxId)) {
|
|
25
|
+
throw new Error(`a reachability grant names a 12-hex sandbox id, got "${sandboxId}"`);
|
|
26
|
+
}
|
|
27
|
+
const payload = Buffer.from(JSON.stringify({ sub: sandboxId, iat: Math.floor(issuedAtMs / 1000) }), "utf8");
|
|
28
|
+
const signature = edSign(null, payload, createPrivateKey(privateKeyPem));
|
|
29
|
+
return `${GRANT_PREFIX}.${base64url(payload)}.${base64url(signature)}`;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Verifies a grant against the platform's public key; any malformed shape returns undefined instead of throwing.
|
|
33
|
+
export const verifyReachabilityGrant = (publicKeyPem: string, token: string): ReachabilityGrant | undefined => {
|
|
34
|
+
const parts = token.split(".");
|
|
35
|
+
if (parts.length !== 3 || parts[0] !== GRANT_PREFIX) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const payload = Buffer.from(parts[1] as string, "base64url");
|
|
40
|
+
const signature = Buffer.from(parts[2] as string, "base64url");
|
|
41
|
+
if (!edVerify(null, payload, createPublicKey(publicKeyPem), signature)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const parsed = JSON.parse(payload.toString("utf8")) as { sub?: unknown; iat?: unknown };
|
|
45
|
+
if (typeof parsed.sub !== "string" || !SANDBOX_ID.test(parsed.sub) || typeof parsed.iat !== "number") {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return { sandboxId: parsed.sub, issuedAt: parsed.iat };
|
|
49
|
+
} catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Host to owner routing
|
|
55
|
+
|
|
56
|
+
// Resolves which sandbox owns a Host: the leftmost DNS label is `sandbox-<id>` or ends in `-<id>` (12-hex); anything
|
|
57
|
+
// else, including the loopback `<id>.local.<zone>` label, returns undefined.
|
|
58
|
+
export const hostOwnerId = (host: string): string | undefined => {
|
|
59
|
+
const label = host.split(":")[0]?.split(".")[0] ?? "";
|
|
60
|
+
const match = /-([0-9a-f]{12})$/.exec(label);
|
|
61
|
+
return match === null ? undefined : match[1];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Wire constants
|
|
65
|
+
|
|
66
|
+
// Tunnel door on the ingress; versioned so a v2 session shape adds a new path instead of replacing this one.
|
|
67
|
+
export const INGRESS_TUNNEL_PATH = "/tunnel/v1";
|
|
68
|
+
|
|
69
|
+
// Grant travels as a header on the tunnel upgrade; this connection is never a browser's.
|
|
70
|
+
export const INGRESS_GRANT_HEADER = "x-intentic-grant";
|
|
71
|
+
|
|
72
|
+
// Env vars every lane hands the daemon; the daemon reads them directly, not the entrypoint.
|
|
73
|
+
export const ENV_INGRESS_URL = "INGRESS_URL";
|
|
74
|
+
export const ENV_SANDBOX_GRANT = "SANDBOX_GRANT";
|
|
75
|
+
|
|
76
|
+
// The daemon-side surface
|
|
77
|
+
|
|
78
|
+
// Daemon-side tunnel behavior this contract requires: dial, register, forward to the loopback listener, and reconnect
|
|
79
|
+
// forever with backoff, never giving up. `close()` is for shutdown and tests.
|
|
80
|
+
export interface IngressTunnelOptions {
|
|
81
|
+
// e.g. https://ingress.<zone>; the daemon derives the wss:// door itself via INGRESS_TUNNEL_PATH.
|
|
82
|
+
readonly url: string;
|
|
83
|
+
readonly grant: string;
|
|
84
|
+
// The daemon's own loopback listener; every h2 stream lands there as a plain HTTP/1.1 request or upgrade.
|
|
85
|
+
readonly targetPort: number;
|
|
86
|
+
readonly log: (message: string, error?: unknown) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface IngressTunnelHandle {
|
|
90
|
+
readonly close: () => Promise<void>;
|
|
91
|
+
// For /health and the boot log: whether the tunnel currently holds a registered session.
|
|
92
|
+
readonly connected: () => boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type StartIngressTunnel = (options: IngressTunnelOptions) => IngressTunnelHandle;
|
|
96
|
+
|
|
97
|
+
// What the ingress side must also honor, enforced by ingress-protocol tests:
|
|
98
|
+
// - Register: verify the grant offline; if PLATFORM_URL is set, check the sandbox exists (GET /api/reachability/<id>)
|
|
99
|
+
// and cache the answer, failing open if the platform doesn't respond. A 404 refuses the tunnel.
|
|
100
|
+
// - Displacement: a new tunnel for an id closes the old session (code 4001) and takes the registration.
|
|
101
|
+
// - Liveness: WebSocket ping every 15s; a peer silent for 45s is unregistered.
|
|
102
|
+
// - Routing: host maps via hostOwnerId to a registered tunnel; no tunnel answers 502 naming the sandbox label.
|
|
103
|
+
// - The tunnel door and any host without a sandbox id are served directly by the ingress, never routed.
|
|
@@ -5,29 +5,16 @@ import { type Duplex, duplexPair } from "node:stream";
|
|
|
5
5
|
import { afterAll, beforeAll, expect, test } from "vitest";
|
|
6
6
|
import { openIngressSession, serveIngressSession } from "./ingress-protocol.js";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* node http client ─h1→ FRONT server (the ingress) ─h2 stream→ duplex pair ─h2 session→ DAEMON half ─h1→ TARGET
|
|
11
|
-
*
|
|
12
|
-
* Driven by node's own http client rather than by calling the exported functions with hand-built objects,
|
|
13
|
-
* because every property worth pinning here is a property of the BYTES: that a 4MB body survives, that a
|
|
14
|
-
* response arrives in pieces instead of being buffered whole, that a WebSocket's `Sec-WebSocket-Accept` is the
|
|
15
|
-
* daemon's own and not a recomputation, that a half-close reaches the far end. A fake IncomingMessage proves
|
|
16
|
-
* none of those, and each of them is a way this file can be wrong while type-checking perfectly.
|
|
17
|
-
*
|
|
18
|
-
* The front server IS the shape the ingress uses (request → forwardRequest, upgrade → forwardUpgrade, a 502
|
|
19
|
-
* when either rejects with nothing yet said to the browser), so a regression in the promise contract fails
|
|
20
|
-
* here rather than in the ingress package alone. */
|
|
8
|
+
// Drives the full ingress-to-daemon chain (front server, duplex pair, target) through node's real http client, in
|
|
9
|
+
// process, so streaming, half-close and header identity are pinned as bytes, not shapes.
|
|
21
10
|
|
|
22
11
|
const listen = async (server: Server): Promise<number> => {
|
|
23
12
|
await new Promise<void>((resolve) => void server.listen(0, "127.0.0.1", resolve));
|
|
24
13
|
return (server.address() as AddressInfo).port;
|
|
25
14
|
};
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* is never opened fails as the suite's own hang bound, which is the correct report — "the bytes never arrived"
|
|
30
|
-
* is the failure, and no duration in this file would be measuring anything else. */
|
|
16
|
+
// Coordinates a fixed order between two halves without measuring time: the near end waits on the far end's signal; an
|
|
17
|
+
// unopened gate fails as the suite's own hang timeout.
|
|
31
18
|
const gate = <T = void>(): { readonly open: (value: T) => void; readonly opened: Promise<T> } => {
|
|
32
19
|
let open = (_value: T): void => {};
|
|
33
20
|
const opened = new Promise<T>((resolve) => {
|
|
@@ -38,8 +25,8 @@ const gate = <T = void>(): { readonly open: (value: T) => void; readonly opened:
|
|
|
38
25
|
|
|
39
26
|
const HOST = "sandbox-0123456789ab.sbx.test";
|
|
40
27
|
|
|
41
|
-
// What the target saw, as
|
|
42
|
-
//
|
|
28
|
+
// What the target saw, mirrored back as its own response, so assertions read a real server's view rather than the
|
|
29
|
+
// proxy's bookkeeping.
|
|
43
30
|
interface Seen {
|
|
44
31
|
readonly method: string;
|
|
45
32
|
readonly url: string;
|
|
@@ -62,14 +49,12 @@ const firstChunk = gate();
|
|
|
62
49
|
const secondSent = gate();
|
|
63
50
|
const uploadStarted = gate();
|
|
64
51
|
|
|
65
|
-
// 64KB
|
|
66
|
-
// that has not completed.
|
|
52
|
+
// 64KB per write, keeping node's write queue non-empty so a reset lands on an unfinished write.
|
|
67
53
|
const FLOOD_CHUNK = Buffer.alloc(64 * 1024, 7);
|
|
68
54
|
|
|
69
55
|
type Route = (request: IncomingMessage, response: ServerResponse) => void | Promise<void>;
|
|
70
56
|
|
|
71
|
-
// One
|
|
72
|
-
// looking for "what does a cancelled request do" should find one function, not the fifth branch of one.
|
|
57
|
+
// One route per property under test, not a branch of one handler, so each behavior has its own function.
|
|
73
58
|
const routes: Record<string, Route> = {
|
|
74
59
|
"/seen": (request, response) => {
|
|
75
60
|
const seen: Seen = {
|
|
@@ -87,8 +72,7 @@ const routes: Record<string, Route> = {
|
|
|
87
72
|
response.writeHead(200, { "content-type": "application/octet-stream", "x-sha256": digest(bytes) });
|
|
88
73
|
response.end(bytes);
|
|
89
74
|
},
|
|
90
|
-
//
|
|
91
|
-
// response before forwarding it deadlocks here instead of passing.
|
|
75
|
+
// Second write waits for the client to read the first; a proxy that buffers the whole response deadlocks here.
|
|
92
76
|
"/drip": async (_request, response) => {
|
|
93
77
|
response.writeHead(200, { "content-type": "text/event-stream" });
|
|
94
78
|
response.write("one");
|
|
@@ -97,22 +81,20 @@ const routes: Record<string, Route> = {
|
|
|
97
81
|
response.end();
|
|
98
82
|
secondSent.open();
|
|
99
83
|
},
|
|
100
|
-
//
|
|
84
|
+
// Mirror of /drip: the first body chunk must arrive here before the client sends the rest.
|
|
101
85
|
"/slurp": async (request, response) => {
|
|
102
86
|
request.once("data", () => uploadStarted.open());
|
|
103
87
|
const bytes = await bodyOf(request);
|
|
104
88
|
response.writeHead(200, { "content-type": "text/plain" });
|
|
105
89
|
response.end(bytes.toString("utf8"));
|
|
106
90
|
},
|
|
107
|
-
// Never answered
|
|
108
|
-
// this server is still holding — "aborted", never the "ended" of an exchange that completed.
|
|
91
|
+
// Never answered; asserts a client abort surfaces as "aborted", not the "ended" of a completed exchange.
|
|
109
92
|
"/hangup": (_request, response) => {
|
|
110
93
|
response.writeHead(200, { "content-type": "text/event-stream" });
|
|
111
94
|
response.write("open");
|
|
112
95
|
response.on("close", () => cancelled.open(response.writableEnded ? "ended" : "aborted"));
|
|
113
96
|
},
|
|
114
|
-
//
|
|
115
|
-
// that the loopback bridge exists to survive.
|
|
97
|
+
// Large enough that writes are still pending when the client resets mid-stream.
|
|
116
98
|
"/flood": (_request, response) => {
|
|
117
99
|
response.writeHead(200, { "content-type": "application/octet-stream" });
|
|
118
100
|
const pump = (): void => {
|
|
@@ -139,9 +121,8 @@ const target = createServer((request: IncomingMessage, response: ServerResponse)
|
|
|
139
121
|
|
|
140
122
|
const cancelled = gate<string>();
|
|
141
123
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* upgrading, which is the other branch of the daemon's CONNECT handling. */
|
|
124
|
+
// Hand-written HTTP/1.1 upgrade carrying the 101 head plus the raw bytes after it. `/refuse` answers instead of
|
|
125
|
+
// upgrading.
|
|
145
126
|
target.on("upgrade", (request: IncomingMessage, socket: Duplex, head: Buffer) => {
|
|
146
127
|
if ((request.url ?? "") === "/refuse") {
|
|
147
128
|
socket.end("HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 7\r\n\r\nno dice");
|
|
@@ -152,8 +133,7 @@ target.on("upgrade", (request: IncomingMessage, socket: Duplex, head: Buffer) =>
|
|
|
152
133
|
"HTTP/1.1 101 Switching Protocols",
|
|
153
134
|
"Upgrade: websocket",
|
|
154
135
|
"Connection: Upgrade",
|
|
155
|
-
//
|
|
156
|
-
// proves the head travelled rather than being reconstructed by the proxy.
|
|
136
|
+
// A value only this server can produce; proves the head travelled rather than being reconstructed.
|
|
157
137
|
`Sec-WebSocket-Accept: ${digest(Buffer.from(String(request.headers["sec-websocket-key"])))}`,
|
|
158
138
|
`X-Seen-Host: ${String(request.headers.host)}`,
|
|
159
139
|
`X-Seen-Path: ${String(request.url)}`,
|
|
@@ -165,12 +145,11 @@ target.on("upgrade", (request: IncomingMessage, socket: Duplex, head: Buffer) =>
|
|
|
165
145
|
socket.write(head);
|
|
166
146
|
}
|
|
167
147
|
socket.on("data", (chunk: Buffer) => void socket.write(Buffer.concat([Buffer.from("echo:"), chunk])));
|
|
168
|
-
// A
|
|
169
|
-
// completes.
|
|
148
|
+
// A far-end FIN must arrive here or the close handshake never completes.
|
|
170
149
|
socket.on("end", () => void socket.end("bye"));
|
|
171
150
|
});
|
|
172
151
|
|
|
173
|
-
//
|
|
152
|
+
// One session; every request is routed through it.
|
|
174
153
|
const front = async (
|
|
175
154
|
targetPort: number,
|
|
176
155
|
): Promise<{ readonly server: Server; readonly poison: (bytes: Buffer) => void; readonly close: () => void }> => {
|
|
@@ -194,8 +173,7 @@ const front = async (
|
|
|
194
173
|
});
|
|
195
174
|
return {
|
|
196
175
|
server,
|
|
197
|
-
//
|
|
198
|
-
// what a wedged peer or a half-open socket that came back wrong looks like from the ingress's side.
|
|
176
|
+
// Writes garbage onto the wire the edge reads, simulating a wedged or corrupted peer.
|
|
199
177
|
poison: (bytes: Buffer) => void daemonSide.write(bytes),
|
|
200
178
|
close: () => {
|
|
201
179
|
session.close();
|
|
@@ -208,9 +186,8 @@ const front = async (
|
|
|
208
186
|
let edge: Awaited<ReturnType<typeof front>>;
|
|
209
187
|
let edgePort = 0;
|
|
210
188
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
* the process was dying is exactly the report that hid this the first time. */
|
|
189
|
+
// Node-internal assertion failures surface only as an uncaughtException with none of our frames on the stack; caught
|
|
190
|
+
// here or not at all.
|
|
214
191
|
const uncaught: string[] = [];
|
|
215
192
|
|
|
216
193
|
beforeAll(async () => {
|
|
@@ -245,7 +222,7 @@ const call = (
|
|
|
245
222
|
test("a request round-trips with its authority, path and method, and no hop-by-hop header crosses", async () => {
|
|
246
223
|
const answer = await call("/seen?q=1", {
|
|
247
224
|
method: "PUT",
|
|
248
|
-
//
|
|
225
|
+
// Three headers that must not cross a hop, plus two that must survive it.
|
|
249
226
|
headers: { "x-custom": "kept", "x-forwarded-proto": "https", connection: "close", upgrade: "h2c", "keep-alive": "timeout=99" },
|
|
250
227
|
body: Buffer.from("hi"),
|
|
251
228
|
});
|
|
@@ -253,16 +230,9 @@ test("a request round-trips with its authority, path and method, and no hop-by-h
|
|
|
253
230
|
expect(answer.status).toBe(201);
|
|
254
231
|
expect(answer.headers["x-target"]).toBe("yes");
|
|
255
232
|
const seen = JSON.parse(answer.body.toString("utf8")) as Seen;
|
|
256
|
-
// The Host the browser used is what the daemon's own listener sees — how a preview, a forwarded port and
|
|
257
|
-
// the daemon itself are told apart inside the container.
|
|
258
233
|
expect(seen).toMatchObject({ method: "PUT", url: "/seen?q=1", host: HOST });
|
|
259
234
|
expect(seen.headerNames).toContain("x-custom");
|
|
260
235
|
expect(seen.headerNames).toContain("x-forwarded-proto");
|
|
261
|
-
/* Hop-by-hop headers describe ONE hop and are re-derived on each, never forwarded. `connection` is the
|
|
262
|
-
* assertion that says so by value: the browser sent `close`, and what reaches the target is the `keep-alive`
|
|
263
|
-
* of the daemon's own loopback hop. Asserting merely that the target sees no `connection` would be asserting
|
|
264
|
-
* something false — node writes one for its own hop — and would pass just as well if the browser's value had
|
|
265
|
-
* been forwarded and then overwritten. */
|
|
266
236
|
expect(seen.connection).toBe("keep-alive");
|
|
267
237
|
expect(seen.headerNames).not.toContain("upgrade");
|
|
268
238
|
expect(seen.headerNames).not.toContain("keep-alive");
|
|
@@ -274,8 +244,6 @@ test("a multi-megabyte body survives in both directions, byte for byte", async (
|
|
|
274
244
|
const answer = await call("/echo", { method: "POST", body: payload });
|
|
275
245
|
|
|
276
246
|
expect(answer.status).toBe(200);
|
|
277
|
-
// The target's own digest of what it received, and ours of what came back: one assertion per direction,
|
|
278
|
-
// and neither can pass on a truncated or re-ordered stream.
|
|
279
247
|
expect(answer.headers["x-sha256"]).toBe(digest(payload));
|
|
280
248
|
expect(digest(answer.body)).toBe(digest(payload));
|
|
281
249
|
});
|
|
@@ -298,8 +266,6 @@ test("a response is streamed, not buffered: the client reads chunk one before th
|
|
|
298
266
|
await secondSent.opened;
|
|
299
267
|
|
|
300
268
|
expect(chunks.join("")).toBe("onetwo");
|
|
301
|
-
// Two writes, two reads: coalesced into one would mean the proxy held the first until the body was
|
|
302
|
-
// complete, which is the failure this asserts against.
|
|
303
269
|
expect(chunks.length).toBeGreaterThan(1);
|
|
304
270
|
});
|
|
305
271
|
|
|
@@ -336,8 +302,6 @@ test("an upgrade splices raw bytes, carries the far end's own handshake head, an
|
|
|
336
302
|
});
|
|
337
303
|
|
|
338
304
|
expect(upgraded.status).toBe(101);
|
|
339
|
-
// Computed by the target from the key the browser sent, and therefore proof that the original request
|
|
340
|
-
// headers reached it through the CONNECT envelope AND that its answer came back verbatim.
|
|
341
305
|
expect(upgraded.headers["sec-websocket-accept"]).toBe(digest(Buffer.from(key)));
|
|
342
306
|
expect(upgraded.headers["x-seen-host"]).toBe(HOST);
|
|
343
307
|
expect(upgraded.headers["x-seen-path"]).toBe("/socket");
|
|
@@ -350,7 +314,6 @@ test("an upgrade splices raw bytes, carries the far end's own handshake head, an
|
|
|
350
314
|
upgraded.socket.on("end", () => resolve(read));
|
|
351
315
|
});
|
|
352
316
|
upgraded.socket.write("abc");
|
|
353
|
-
// Half-close: the far end must see the FIN, answer on the still-open direction, and then end.
|
|
354
317
|
upgraded.socket.end();
|
|
355
318
|
|
|
356
319
|
expect(await spliced).toBe("echo:abcbye");
|
|
@@ -386,15 +349,10 @@ test("a browser that gives up cancels the stream all the way to the target", asy
|
|
|
386
349
|
});
|
|
387
350
|
request.destroy();
|
|
388
351
|
|
|
389
|
-
// Without the RST_STREAM this asserts, the target keeps generating a response for a browser that is gone,
|
|
390
|
-
// for as long as the container lives.
|
|
391
352
|
await expect(cancelled.opened).resolves.toBe("aborted");
|
|
392
353
|
});
|
|
393
354
|
|
|
394
355
|
test("a tunnel whose target is not listening fails the exchange rather than answering for it", async () => {
|
|
395
|
-
// A port nothing serves: the daemon half cannot reach a listener, so the exchange must fail in a way the
|
|
396
|
-
// ingress can turn into its own 502 — the body naming the host label is the ingress's to write, not this
|
|
397
|
-
// module's.
|
|
398
356
|
const dead = createServer();
|
|
399
357
|
const deadPort = await listen(dead);
|
|
400
358
|
await new Promise<void>((resolve) => void dead.close(() => resolve()));
|
|
@@ -402,8 +360,7 @@ test("a tunnel whose target is not listening fails the exchange rather than answ
|
|
|
402
360
|
const [edgeSide, daemonSide] = duplexPair();
|
|
403
361
|
const daemon = await serveIngressSession(daemonSide, { targetPort: deadPort });
|
|
404
362
|
const session = await openIngressSession(edgeSide);
|
|
405
|
-
//
|
|
406
|
-
// inside a catch nothing awaits: it is free to write a status, so nothing was said to the browser first.
|
|
363
|
+
// True if a status was already written before the rejection handler runs.
|
|
407
364
|
const said = gate<boolean>();
|
|
408
365
|
const server = createServer((request, response) => {
|
|
409
366
|
void session.forwardRequest(request, response).catch(() => {
|
|
@@ -429,23 +386,11 @@ test("a tunnel whose target is not listening fails the exchange rather than answ
|
|
|
429
386
|
server.close();
|
|
430
387
|
});
|
|
431
388
|
|
|
432
|
-
/* THE REGRESSION THIS MODULE'S TRANSPORT EXISTS FOR. Reset a batch of streams that are mid-write and the h2
|
|
433
|
-
* session emits control frames on top of writes that have not completed — which, run directly over a Duplex,
|
|
434
|
-
* is node's one-write-per-turn JSStreamSocket invariant and an ERR_INTERNAL_ASSERTION out of an internal
|
|
435
|
-
* callback. Measured twice over: the process died, AND the RST_STREAM never went out, so the cancellation
|
|
436
|
-
* never reached the container.
|
|
437
|
-
*
|
|
438
|
-
* Driven entirely through the public API, so it keeps pinning the behaviour however the transport is built. If
|
|
439
|
-
* someone removes the loopback bridge because "http2 takes a Duplex", this is the test that goes red. */
|
|
440
389
|
test("a shutdown landing on top of pending writes neither crashes nor wedges the session", async () => {
|
|
441
390
|
const own = await front((target.address() as AddressInfo).port);
|
|
442
391
|
const ownPort = await listen(own.server);
|
|
443
392
|
|
|
444
|
-
|
|
445
|
-
* write queue is genuinely non-empty when the GOAWAY is produced. All eight stay live on purpose: an
|
|
446
|
-
* earlier version of this test reset half of them first and passed against the broken transport, because
|
|
447
|
-
* the resets drained the very pressure the shutdown has to land on top of. (The reset path has its own
|
|
448
|
-
* test above; what is being pinned here is a control frame written over pending data.) */
|
|
393
|
+
// All eight stay live: resetting some first would drain the pending-write pressure the shutdown needs to land on.
|
|
449
394
|
const flooding = Array.from({ length: 8 }, () =>
|
|
450
395
|
new Promise<void>((resolve) => {
|
|
451
396
|
const request = h1Request({ host: "127.0.0.1", port: ownPort, path: "/flood", headers: { host: HOST } }, (response) => {
|
|
@@ -461,19 +406,12 @@ test("a shutdown landing on top of pending writes neither crashes nor wedges the
|
|
|
461
406
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
462
407
|
own.server.close();
|
|
463
408
|
|
|
464
|
-
|
|
465
|
-
* the shutdown frame cannot be written, so `close()` never completes and the tunnel wedges holding every
|
|
466
|
-
* stream on it — which is why a hang bound, rather than a duration, is the right report here. */
|
|
409
|
+
// If the shutdown frame can't be written, this hangs; the suite's timeout is the failure signal, not an assertion.
|
|
467
410
|
expect(uncaught).toStrictEqual([]);
|
|
468
|
-
// And the neighbours are untouched: the shared fixture's session still serves.
|
|
469
411
|
const after = await call("/seen");
|
|
470
412
|
expect(after.status).toBe(201);
|
|
471
413
|
});
|
|
472
414
|
|
|
473
|
-
/* CONTAINMENT: one tunnel's session dying must be one tunnel's problem. The ingress holds every sandbox's
|
|
474
|
-
* session in a single process, so a peer that speaks nonsense — a wedged container, a half-open socket that
|
|
475
|
-
* came back as garbage, anything that makes nghttp2 give up — is the failure most likely to be shared, and it
|
|
476
|
-
* must not be. */
|
|
477
415
|
test("a poisoned session dies alone and leaves another tunnel serving", async () => {
|
|
478
416
|
const targetPort = (target.address() as AddressInfo).port;
|
|
479
417
|
const poisoned = await front(targetPort);
|
|
@@ -493,15 +431,12 @@ test("a poisoned session dies alone and leaves another tunnel serving", async ()
|
|
|
493
431
|
expect(await through(poisonedPort)).toBe(201);
|
|
494
432
|
expect(await through(healthyPort)).toBe(201);
|
|
495
433
|
|
|
496
|
-
// Not
|
|
434
|
+
// Not a valid h2 preface; the session must fail rather than parse it.
|
|
497
435
|
poisoned.poison(Buffer.from("this is not a PRI * HTTP/2.0 preface, nor anything else nghttp2 accepts"));
|
|
498
436
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
499
437
|
|
|
500
|
-
// The dead session refuses new streams, which the front turns into its 502 — the tunnel is gone, and that
|
|
501
|
-
// is a routing fact rather than a crash.
|
|
502
438
|
expect(await through(poisonedPort)).toBe(502);
|
|
503
439
|
expect(uncaught).toStrictEqual([]);
|
|
504
|
-
// The whole point: the other tunnel never noticed.
|
|
505
440
|
expect(await through(healthyPort)).toBe(201);
|
|
506
441
|
|
|
507
442
|
poisoned.close();
|