@intentic/sandbox-contract 1.312.0 → 1.313.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 +26 -3
- package/dist/chores/index.d.ts +0 -1
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +0 -1
- package/dist/chores/index.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +57 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +436 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +11 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +123 -96
- package/dist/contracts/agents.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/device.contract.d.ts +19 -4
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +1 -1
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +11 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.d.ts +2 -0
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.d.ts +2 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +138 -0
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +1 -0
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +59 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +12 -8
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +2 -2
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +139 -19
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +72 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +85 -14
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.d.ts.map +1 -1
- package/dist/documents/index.js +1 -0
- package/dist/documents/index.js.map +1 -1
- package/dist/documents/read.d.ts +24 -0
- package/dist/documents/read.d.ts.map +1 -0
- package/dist/documents/read.js +81 -0
- package/dist/documents/read.js.map +1 -0
- package/dist/events/agent-events.d.ts +235 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +3 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/child-run.d.ts +9 -0
- package/dist/events/child-run.d.ts.map +1 -0
- package/dist/events/child-run.js +41 -0
- package/dist/events/child-run.js.map +1 -0
- package/dist/events/errands.d.ts +5 -0
- package/dist/events/errands.d.ts.map +1 -0
- package/dist/events/errands.js +19 -0
- package/dist/events/errands.js.map +1 -0
- package/dist/events/requests.d.ts +152 -0
- package/dist/events/requests.d.ts.map +1 -1
- package/dist/events/requests.js +26 -1
- package/dist/events/requests.js.map +1 -1
- package/dist/events/system-events.d.ts +8 -12
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +387 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +3 -0
- package/dist/events/transcript.js.map +1 -1
- package/dist/front/browser-wire.d.ts +6 -0
- package/dist/front/browser-wire.d.ts.map +1 -0
- package/dist/front/browser-wire.js +4 -0
- package/dist/front/browser-wire.js.map +1 -0
- package/dist/front/front-wire.d.ts +6 -0
- package/dist/front/front-wire.d.ts.map +1 -0
- package/dist/front/front-wire.js +5 -0
- package/dist/front/front-wire.js.map +1 -0
- package/dist/front/generated/browser-wire.d.ts +19 -0
- package/dist/front/generated/browser-wire.d.ts.map +1 -0
- package/dist/front/generated/browser-wire.js +2 -0
- package/dist/front/generated/browser-wire.js.map +1 -0
- package/dist/front/generated/wire.d.ts +36 -30
- package/dist/front/generated/wire.d.ts.map +1 -1
- package/dist/ids/conversation-ids.d.ts +1 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +2 -2
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/ids/session-names.d.ts +3 -0
- package/dist/ids/session-names.d.ts.map +1 -1
- package/dist/ids/session-names.js +3 -0
- package/dist/ids/session-names.js.map +1 -1
- package/dist/index.d.ts +1036 -168
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/models/agent-catalog.d.ts.map +1 -1
- package/dist/models/agent-catalog.js +4 -6
- package/dist/models/agent-catalog.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts +2 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +7 -1
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/plan-pools.d.ts +22 -1
- package/dist/models/plan-pools.d.ts.map +1 -1
- package/dist/models/plan-pools.js +86 -0
- package/dist/models/plan-pools.js.map +1 -1
- package/dist/policy/request-status.d.ts +1 -1
- package/dist/policy/request-status.d.ts.map +1 -1
- package/dist/policy/request-status.js +4 -11
- package/dist/policy/request-status.js.map +1 -1
- package/dist/policy/reserved-servers.d.ts.map +1 -1
- package/dist/policy/reserved-servers.js +2 -1
- package/dist/policy/reserved-servers.js.map +1 -1
- package/dist/policy/turned-away.d.ts.map +1 -1
- package/dist/policy/turned-away.js +1 -0
- package/dist/policy/turned-away.js.map +1 -1
- package/dist/protocol/edge-verdict.d.ts +2 -1
- package/dist/protocol/edge-verdict.d.ts.map +1 -1
- package/dist/protocol/edge-verdict.js +1 -1
- package/dist/protocol/edge-verdict.js.map +1 -1
- package/dist/protocol/ingress-contract.d.ts +1 -1
- package/dist/protocol/ingress-contract.d.ts.map +1 -1
- package/dist/protocol/ingress-contract.js +1 -1
- package/dist/protocol/ingress-contract.js.map +1 -1
- package/dist/protocol/raw-routes.d.ts +18 -40
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +5 -16
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/route-meta.d.ts +20 -0
- package/dist/protocol/route-meta.d.ts.map +1 -1
- package/dist/protocol/route-meta.js +16 -0
- package/dist/protocol/route-meta.js.map +1 -1
- package/dist/protocol/tunnel-bulk.d.ts +2 -0
- package/dist/protocol/tunnel-bulk.d.ts.map +1 -0
- package/dist/protocol/tunnel-bulk.js +3 -0
- package/dist/protocol/tunnel-bulk.js.map +1 -0
- package/dist/protocol/x402.d.ts +17 -0
- package/dist/protocol/x402.d.ts.map +1 -0
- package/dist/protocol/x402.js +37 -0
- package/dist/protocol/x402.js.map +1 -0
- package/dist/schemas/agent.d.ts +25 -0
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +14 -2
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +17 -18
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +16 -3
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +4 -6
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/devices.d.ts +132 -7
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +43 -18
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +22 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/extension-updates.js +4 -0
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/git/git.d.ts +3 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/hosts.d.ts +29 -5
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +20 -7
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +2 -12
- package/dist/schemas/keep-warm.d.ts.map +1 -1
- package/dist/schemas/keep-warm.js +8 -10
- package/dist/schemas/keep-warm.js.map +1 -1
- package/dist/schemas/metrics.d.ts +22 -0
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +18 -3
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/personas.d.ts +3 -0
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +9 -0
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +140 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +47 -1
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +57 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +6 -1
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +28 -1
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +35 -1
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +184 -4
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +31 -23
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/speaker.d.ts +36 -0
- package/dist/schemas/speaker.d.ts.map +1 -0
- package/dist/schemas/speaker.js +62 -0
- package/dist/schemas/speaker.js.map +1 -0
- package/dist/schemas/state-plan.d.ts +46 -0
- package/dist/schemas/state-plan.d.ts.map +1 -0
- package/dist/schemas/state-plan.js +31 -0
- package/dist/schemas/state-plan.js.map +1 -0
- package/dist/schemas/system.d.ts +48 -8
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +10 -14
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workspace/mainline.d.ts +206 -64
- package/dist/schemas/workspace/mainline.d.ts.map +1 -1
- package/dist/schemas/workspace/mainline.js +73 -25
- package/dist/schemas/workspace/mainline.js.map +1 -1
- package/dist/state/contract-lock.d.ts +1 -0
- package/dist/state/contract-lock.d.ts.map +1 -1
- package/dist/state/contract-lock.js +47 -1
- package/dist/state/contract-lock.js.map +1 -1
- package/dist/state/definition.d.ts +24 -16
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/fix-stance.d.ts +1 -1
- package/dist/state/fix-stance.d.ts.map +1 -1
- package/dist/state/fix-stance.js +2 -1
- package/dist/state/fix-stance.js.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +2 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/landed-commit.d.ts +1 -0
- package/dist/state/landed-commit.d.ts.map +1 -1
- package/dist/state/landed-commit.js +2 -0
- package/dist/state/landed-commit.js.map +1 -1
- package/dist/state/red-streak.d.ts +7 -0
- package/dist/state/red-streak.d.ts.map +1 -0
- package/dist/state/red-streak.js +11 -0
- package/dist/state/red-streak.js.map +1 -0
- package/dist/state/workspace-state.d.ts +1 -0
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +6 -2
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/shell-regions.d.ts +1 -0
- package/dist/text/shell-regions.d.ts.map +1 -1
- package/dist/text/shell-regions.js +1 -1
- package/dist/text/shell-regions.js.map +1 -1
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +14 -7
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +28 -29
- package/src/chores/index.ts +0 -1
- package/src/contracts/agent.contract.ts +14 -0
- package/src/contracts/capabilities.contract.ts +2 -2
- package/src/contracts/device.contract.ts +5 -3
- package/src/contracts/secrets.contract.ts +2 -0
- package/src/contracts/settings.contract.ts +2 -1
- package/src/documents/conversions.ts +3 -3
- package/src/documents/index.ts +3 -1
- package/src/documents/read.ts +114 -0
- package/src/events/agent-events.ts +5 -0
- package/src/events/child-run.test.ts +81 -0
- package/src/events/child-run.ts +73 -0
- package/src/events/errands.test.ts +22 -0
- package/src/events/errands.ts +27 -0
- package/src/events/requests.ts +40 -1
- package/src/events/transcript.ts +8 -0
- package/src/front/browser-wire.ts +20 -0
- package/src/front/front-wire.ts +15 -0
- package/src/front/generated/browser-wire.json +138 -0
- package/src/front/generated/browser-wire.ts +16 -0
- package/src/front/generated/front-wire.json +793 -0
- package/src/front/generated/tunnel.json +38 -0
- package/src/front/generated/wire.ts +21 -24
- package/src/front/wire-manifests.test.ts +61 -0
- package/src/ids/conversation-ids.ts +6 -5
- package/src/ids/session-names.ts +7 -0
- package/src/index.ts +8 -1
- package/src/models/agent-catalog.test.ts +6 -4
- package/src/models/agent-catalog.ts +4 -6
- package/src/models/agent-runtimes.ts +22 -7
- package/src/models/capability-ledger.test.ts +2 -0
- package/src/models/plan-pools.test.ts +121 -2
- package/src/models/plan-pools.ts +152 -2
- package/src/policy/request-status.ts +4 -12
- package/src/policy/reserved-servers.test.ts +6 -0
- package/src/policy/reserved-servers.ts +13 -10
- package/src/policy/turned-away.ts +3 -0
- package/src/protocol/edge-verdict.ts +13 -11
- package/src/protocol/ingress-contract.fixture.json +13 -0
- package/src/protocol/ingress-contract.ts +9 -5
- package/src/protocol/raw-routes.ts +10 -21
- package/src/protocol/route-meta.ts +36 -1
- package/src/protocol/tunnel-bulk.test.ts +37 -0
- package/src/protocol/tunnel-bulk.ts +7 -0
- package/src/protocol/x402.test.ts +29 -0
- package/src/protocol/x402.ts +61 -0
- package/src/schemas/agent.ts +28 -3
- package/src/schemas/agents.ts +32 -6
- package/src/schemas/automations.ts +4 -2
- package/src/schemas/devices.ts +106 -39
- package/src/schemas/extension-updates.ts +6 -0
- package/src/schemas/hosts.ts +62 -19
- package/src/schemas/ic-shape-args.test.ts +64 -0
- package/src/schemas/keep-warm.test.ts +6 -13
- package/src/schemas/keep-warm.ts +15 -20
- package/src/schemas/metrics.ts +26 -3
- package/src/schemas/personas.ts +15 -0
- package/src/schemas/providers/plan-limits.ts +71 -1
- package/src/schemas/providers/provider-oauth.ts +10 -1
- package/src/schemas/settings-history.ts +50 -3
- package/src/schemas/settings.test.ts +54 -1
- package/src/schemas/settings.ts +49 -31
- package/src/schemas/speaker.ts +89 -0
- package/src/schemas/state-plan.test.ts +69 -0
- package/src/schemas/state-plan.ts +47 -0
- package/src/schemas/system.ts +20 -16
- package/src/schemas/workspace/mainline.test.ts +28 -0
- package/src/schemas/workspace/mainline.ts +111 -44
- package/src/state/contract-lock.test.ts +65 -4
- package/src/state/contract-lock.ts +70 -2
- package/src/state/fix-stance.test.ts +8 -0
- package/src/state/fix-stance.ts +3 -3
- package/src/state/history-state.ts +5 -1
- package/src/state/landed-commit.test.ts +12 -1
- package/src/state/landed-commit.ts +5 -0
- package/src/state/red-streak.test.ts +28 -0
- package/src/state/red-streak.ts +28 -0
- package/src/state/workspace-state.test.ts +7 -1
- package/src/state/workspace-state.ts +10 -2
- package/src/text/shell-regions.test.ts +38 -0
- package/src/text/shell-regions.ts +3 -3
- package/src/text/transcript-fold.test.ts +13 -0
- package/src/text/transcript-fold.ts +17 -8
- package/dist/chores/fix-deps.d.ts +0 -10
- package/dist/chores/fix-deps.d.ts.map +0 -1
- package/dist/chores/fix-deps.js +0 -19
- package/dist/chores/fix-deps.js.map +0 -1
- package/dist/front/terminal-frames.d.ts +0 -17
- package/dist/front/terminal-frames.d.ts.map +0 -1
- package/dist/front/terminal-frames.js +0 -79
- package/dist/front/terminal-frames.js.map +0 -1
- package/dist/protocol/tunnel-lanes.d.ts +0 -10
- package/dist/protocol/tunnel-lanes.d.ts.map +0 -1
- package/dist/protocol/tunnel-lanes.js +0 -16
- package/dist/protocol/tunnel-lanes.js.map +0 -1
- package/dist/protocol/upgrade-relay.d.ts +0 -4
- package/dist/protocol/upgrade-relay.d.ts.map +0 -1
- package/dist/protocol/upgrade-relay.js +0 -25
- package/dist/protocol/upgrade-relay.js.map +0 -1
- package/src/chores/fix-deps.ts +0 -23
- package/src/front/terminal-frames.fixture.json +0 -55
- package/src/front/terminal-frames.test.ts +0 -63
- package/src/front/terminal-frames.ts +0 -102
- package/src/protocol/tunnel-lanes.json +0 -136
- package/src/protocol/tunnel-lanes.test.ts +0 -30
- package/src/protocol/tunnel-lanes.ts +0 -32
- package/src/protocol/upgrade-relay.ts +0 -28
package/src/schemas/agent.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TurnErrandSchema } from "./speaker.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { CONVERSATION_ID } from "../ids/conversation-ids.js";
|
|
3
4
|
import { ModelRoleSchema } from "../models/model-roles.js";
|
|
@@ -129,6 +130,9 @@ export type ForkedFrom = z.infer<typeof ForkedFromSchema>;
|
|
|
129
130
|
// The turn's fields before the cross-field refinements below, so a subset (TurnProfileSchema) can be picked from them.
|
|
130
131
|
const AgentTurnFieldsSchema = z.object({
|
|
131
132
|
prompt: z.string().describe("What to say to the agent. May be empty if you are only attaching files."),
|
|
133
|
+
errand: TurnErrandSchema.optional().describe(
|
|
134
|
+
"What the words are for, when the app or the sandbox composed them rather than a person typing them: a land conflict to resolve, a red main line to fix. Shown as the sandbox's words, not yours. Leave it out for your own words.",
|
|
135
|
+
),
|
|
132
136
|
// Minted by the sender so a send whose answer was lost can be sent again without being delivered twice.
|
|
133
137
|
messageId: z
|
|
134
138
|
.string()
|
|
@@ -164,8 +168,14 @@ const AgentTurnFieldsSchema = z.object({
|
|
|
164
168
|
// Which agentic loop runs the turn, absent = provider's own; "claude-code" forces the SDK loop for any
|
|
165
169
|
// provider.
|
|
166
170
|
harness: AgentHarnessSchema.optional().describe("Which agentic loop runs the turn. Leave it out to use each provider's own."),
|
|
167
|
-
//
|
|
168
|
-
|
|
171
|
+
// Intent, not a guess: absent continues on the conversation's own account, or picks by serviceability where there is
|
|
172
|
+
// none on this provider yet (agent/providers/accounts/routing.ts).
|
|
173
|
+
account: z
|
|
174
|
+
.string()
|
|
175
|
+
.optional()
|
|
176
|
+
.describe(
|
|
177
|
+
"Which of that provider's connected accounts pays for the turn. Leave it out to continue on the account the conversation runs on, or, for its first turn on this provider, to take whichever account can serve with the most room. To move a running conversation, use `switchAccount`.",
|
|
178
|
+
),
|
|
169
179
|
// Distinct from `account` (who pays): whose name the turn posts as; absent means something different for each
|
|
170
180
|
// mode.
|
|
171
181
|
actsAs: entryId.optional().describe("Which persona the turn speaks as out in the world. Not the same as which account pays for it."),
|
|
@@ -317,6 +327,19 @@ export const AgentTurnSchema = AgentTurnFieldsSchema
|
|
|
317
327
|
message: 'forkOf.files "then" requires isolated',
|
|
318
328
|
});
|
|
319
329
|
export type AgentTurn = z.infer<typeof AgentTurnSchema>;
|
|
330
|
+
// What a turn naming no provider or loop runs on, stated once. Applied where a request comes in (the daemon's turn port),
|
|
331
|
+
// so nothing downstream defaults them again: the engine takes a turn with both named (withRuntimeDefaults).
|
|
332
|
+
/** A turn with its provider and loop named, as the engine takes it past the door. */
|
|
333
|
+
export type RoutedAgentTurn = AgentTurn & { readonly agent: AgentProvider; readonly harness: AgentHarness };
|
|
334
|
+
export const DEFAULT_PROVIDER: AgentProvider = "claude";
|
|
335
|
+
export const DEFAULT_HARNESS: AgentHarness = "native";
|
|
336
|
+
export const withRuntimeDefaults = <T extends { readonly agent?: AgentProvider | undefined; readonly harness?: AgentHarness | undefined }>(
|
|
337
|
+
turn: T,
|
|
338
|
+
): T & { readonly agent: AgentProvider; readonly harness: AgentHarness } => ({
|
|
339
|
+
...turn,
|
|
340
|
+
agent: turn.agent ?? DEFAULT_PROVIDER,
|
|
341
|
+
harness: turn.harness ?? DEFAULT_HARNESS,
|
|
342
|
+
});
|
|
320
343
|
// Which turn this is, as opposed to what it says: who serves it and how (agent, harness, account, model and its knobs),
|
|
321
344
|
// as whom (actsAs), where (isolated), whether anyone watches (unattended) and for which job (runRole). A turn that
|
|
322
345
|
// continues another (a resume, a wake, a nudge, a child's follow-up) carries this whole, never a hand-picked part.
|
|
@@ -357,7 +380,9 @@ export const AgentRunPickSchema = z
|
|
|
357
380
|
account: z
|
|
358
381
|
.string()
|
|
359
382
|
.optional()
|
|
360
|
-
.describe(
|
|
383
|
+
.describe(
|
|
384
|
+
"Which connected account of that provider pays, by its daemon-minted id. Leave it out to take whichever account can serve with the most room.",
|
|
385
|
+
),
|
|
361
386
|
harness: AgentHarnessSchema.optional().describe("Which agentic loop runs it. Leave it out to use the provider's own."),
|
|
362
387
|
effort: z
|
|
363
388
|
.string()
|
package/src/schemas/agents.ts
CHANGED
|
@@ -56,7 +56,7 @@ export const AgentAttentionSchema = z.object({
|
|
|
56
56
|
plan: z.boolean().describe("It has proposed a plan and is waiting for a yes."),
|
|
57
57
|
question: z.boolean().describe("It has asked you something."),
|
|
58
58
|
permission: z.boolean().describe("It wants to use a tool it needs permission for."),
|
|
59
|
-
// A missing capability (capabilities/capability-offer.ts); lets the lane say "setup needed" rather than a generic
|
|
59
|
+
// A missing capability (capabilities/offers/capability-offer.ts); lets the lane say "setup needed" rather than a generic
|
|
60
60
|
// pause.
|
|
61
61
|
capability: z.boolean().describe("It needs something connected that is not connected yet."),
|
|
62
62
|
// A gated credential parked on a named person's click; the one pause the board's reader may not be able to clear
|
|
@@ -136,6 +136,12 @@ export const LandedMessageSchema = z.object({
|
|
|
136
136
|
.string()
|
|
137
137
|
.optional()
|
|
138
138
|
.describe("Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent."),
|
|
139
|
+
// Present only when the conversation declared an exception a check should grant this change (`Allow: <check> — <reason>`);
|
|
140
|
+
// the check after the land and the push read it off the commit.
|
|
141
|
+
allows: z
|
|
142
|
+
.array(z.string())
|
|
143
|
+
.optional()
|
|
144
|
+
.describe("Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent."),
|
|
139
145
|
});
|
|
140
146
|
export type LandedMessage = z.infer<typeof LandedMessageSchema>;
|
|
141
147
|
// One model's turn in the drafting walk (agent/role-model.ts tries connected models in order), in spend order:
|
|
@@ -258,6 +264,12 @@ export const AgentSummarySchema = z.object({
|
|
|
258
264
|
// Both latched at the first turn, so a board scoped to one project can say which conversations are its own.
|
|
259
265
|
startIn: z.string().optional().describe("Which folder it opened in, relative to the workspace root. Absent means the root."),
|
|
260
266
|
actsAs: z.string().optional().describe("Which persona its first turn acted as. Absent for an ordinary chat."),
|
|
267
|
+
lastActsAs: z
|
|
268
|
+
.string()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe(
|
|
271
|
+
"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.",
|
|
272
|
+
),
|
|
261
273
|
// Per-conversation, so opening it restores its own choices rather than another tab's; `fast` here is what was asked
|
|
262
274
|
// for, not what was served.
|
|
263
275
|
model: z
|
|
@@ -356,10 +368,12 @@ export const AgentSummarySchema = z.object({
|
|
|
356
368
|
.describe(
|
|
357
369
|
"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.",
|
|
358
370
|
),
|
|
359
|
-
|
|
360
|
-
.
|
|
371
|
+
keepableUntil: z
|
|
372
|
+
.number()
|
|
361
373
|
.optional()
|
|
362
|
-
.describe(
|
|
374
|
+
.describe(
|
|
375
|
+
"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.",
|
|
376
|
+
),
|
|
363
377
|
})
|
|
364
378
|
.optional()
|
|
365
379
|
.describe(
|
|
@@ -405,7 +419,7 @@ export const AgentSummarySchema = z.object({
|
|
|
405
419
|
"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.",
|
|
406
420
|
),
|
|
407
421
|
attention: AgentAttentionSchema.describe("Which kinds of waiting-for-you it is doing."),
|
|
408
|
-
// Only the causes that still hold, re-read live (
|
|
422
|
+
// Only the causes that still hold, re-read live (conversations/land/standing.ts), never the stored report's own list: a
|
|
409
423
|
// blocker the user has since cleared is not something to offer them an action about.
|
|
410
424
|
conflictCauses: z
|
|
411
425
|
.array(LandConflictReasonSchema)
|
|
@@ -482,6 +496,14 @@ export const AgentSummarySchema = z.object({
|
|
|
482
496
|
.describe(
|
|
483
497
|
"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.",
|
|
484
498
|
),
|
|
499
|
+
// The one reading of "runs again by itself" (the daemon's awaitingWake, conversation-state.ts): status, fixStance and
|
|
500
|
+
// the card read it, never the watch list, which is only what the card lists. Absent from a daemon older than it.
|
|
501
|
+
awaitingWake: z
|
|
502
|
+
.boolean()
|
|
503
|
+
.optional()
|
|
504
|
+
.describe(
|
|
505
|
+
"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.",
|
|
506
|
+
),
|
|
485
507
|
// Outside conditions a conversation is parked on; a finished-looking card can restart itself when one fires, and an
|
|
486
508
|
// armed watch keeps a hosted machine (and its bill) awake. The check command itself never rides the wire, since it
|
|
487
509
|
// may hold a secret.
|
|
@@ -508,7 +530,7 @@ export const AgentSummarySchema = z.object({
|
|
|
508
530
|
startedAt: z.number().describe("When it started, in milliseconds."),
|
|
509
531
|
endedAt: z.number().optional().describe("When the command exited, in milliseconds. Absent while it runs."),
|
|
510
532
|
exitCode: z.number().int().optional().describe("The code it exited with. Absent while it runs, or when its exit left none."),
|
|
511
|
-
// What the turn that left it running decided about it (agent/tools/job-fates.ts): one of these, or none
|
|
533
|
+
// What the turn that left it running decided about it (agent/tools/jobs/job-fates.ts): one of these, or none
|
|
512
534
|
// while its turn is still going.
|
|
513
535
|
watch: z
|
|
514
536
|
.string()
|
|
@@ -545,6 +567,10 @@ export const AgentSummarySchema = z.object({
|
|
|
545
567
|
),
|
|
546
568
|
});
|
|
547
569
|
export type AgentSummary = z.infer<typeof AgentSummarySchema>;
|
|
570
|
+
// Whether a conversation runs again by itself: the daemon's own reading, or, from a daemon older than it, whether a watch
|
|
571
|
+
// is armed, which was all that one could say.
|
|
572
|
+
export const awaitsWake = (agent: { readonly awaitingWake?: boolean | undefined; readonly watches?: readonly unknown[] | undefined }): boolean =>
|
|
573
|
+
agent.awaitingWake ?? (agent.watches?.length ?? 0) > 0;
|
|
548
574
|
// One armed watch as a card carries it; derived from AgentSummarySchema.watches so the two shapes can't drift apart.
|
|
549
575
|
export type AgentWatch = NonNullable<AgentSummary["watches"]>[number];
|
|
550
576
|
// One background job as a card carries it.
|
|
@@ -297,8 +297,10 @@ export const AutomationSchema = z.object({
|
|
|
297
297
|
.describe(
|
|
298
298
|
"Which models this automation may run on, best first. Required, and nothing is chosen for you: work that fires while nobody is watching spends a real allowance, so it names the models it spends rather than inheriting one. Tried in order, so a spent account does not silently stop the job.",
|
|
299
299
|
),
|
|
300
|
-
// Absent means the
|
|
301
|
-
//
|
|
300
|
+
// Absent means the daemon's pick: the account its conversation runs on for a continuing thread (moved off one that
|
|
301
|
+
// can no longer serve), else the one that can serve with the most room. Named, it is a pin, run whatever its state,
|
|
302
|
+
// so a refused account says so rather than being swapped; pinning matters more here than for a chat, since nobody
|
|
303
|
+
// is watching to notice a stuck one.
|
|
302
304
|
account: z.string().optional().describe("Which account pays for it."),
|
|
303
305
|
// Absent means the wake reaches no logged-in account at all, the strictest default here, since nobody is at the
|
|
304
306
|
// composer when it fires.
|
package/src/schemas/devices.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// What one of the user's own machines is running.
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { parseHostConnection, type DeviceFacts, DeviceFactsSchema, MachineIdSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
|
|
4
4
|
import { DEV_VERSION } from "../state/versions.js";
|
|
5
5
|
// Desktop-sync report shape shared by the agent, daemon and browser, produced only by the agent's own `deviceReport`.
|
|
6
6
|
// The agent never reports `sandboxes`; the docker half is filled in by whoever reads the report, scoped to the reader's
|
|
@@ -9,14 +9,32 @@ import { DEV_VERSION } from "../state/versions.js";
|
|
|
9
9
|
// One sandbox's resource share as docker currently enforces it, read off the container by the machine agent.
|
|
10
10
|
// `overlayRuntime` is the environment's locked demand; `hostRuntime` is the owner's addition; `privileged`/`gpu` are
|
|
11
11
|
// docker's enforced truth.
|
|
12
|
-
//
|
|
13
|
-
// the
|
|
12
|
+
// A delta against what runs: absent means leave it, `null` on the two caps means back to the default; at least one key
|
|
13
|
+
// must be set. What the old `reshape` op carries (`ic sandbox reshape` flags); `set-shape` carries a whole shape instead.
|
|
14
14
|
export const SandboxResourcesAskFieldsSchema = z.object({
|
|
15
15
|
memoryGib: z.int().positive().nullable().optional(),
|
|
16
16
|
cpus: z.int().positive().nullable().optional(),
|
|
17
17
|
privileged: z.boolean().optional(),
|
|
18
18
|
gpu: z.boolean().optional(),
|
|
19
19
|
});
|
|
20
|
+
// A sandbox's shape, WHOLE: the owner's own ask for its share of the machine, every field a value. `null` on a cap is
|
|
21
|
+
// the contract's default (the share derived from the engine, or every core). The switches are the owner's ask, not
|
|
22
|
+
// docker's answer: a directive the approved environment demands rides beside it (`overlayRuntime`), and a host
|
|
23
|
+
// without the NVIDIA runtime drops the GPU. `ic` stores it, validates it against the image's run contract when it is
|
|
24
|
+
// saved, and prints it in `ic sandbox list --json`; nobody else merges anything into it.
|
|
25
|
+
export const SandboxShapeSchema = z.object({
|
|
26
|
+
memoryGib: z.int().positive().nullable(),
|
|
27
|
+
cpus: z.int().positive().nullable(),
|
|
28
|
+
privileged: z.boolean(),
|
|
29
|
+
gpu: z.boolean(),
|
|
30
|
+
});
|
|
31
|
+
export type SandboxShape = z.infer<typeof SandboxShapeSchema>;
|
|
32
|
+
// When a `set-shape` takes effect: `now` restarts the sandbox onto it; `nextRestart` saves it in ic's record and
|
|
33
|
+
// restarts nothing, and the next restart through ic applies it — Restart or Start from any door, an update, a rollback
|
|
34
|
+
// or a rebuild. Docker restarting the container by itself (after a crash or a reboot) does not: only ic recreates one.
|
|
35
|
+
export const SandboxShapeWhenSchema = z.enum(["now", "nextRestart"]);
|
|
36
|
+
export type SandboxShapeWhen = z.infer<typeof SandboxShapeWhenSchema>;
|
|
37
|
+
|
|
20
38
|
export const SandboxResourcesSchema = z.object({
|
|
21
39
|
// The cgroup memory ceiling in bytes; absent when docker imposes none (the hosted shape).
|
|
22
40
|
memoryBytes: z.number().optional(),
|
|
@@ -26,14 +44,19 @@ export const SandboxResourcesSchema = z.object({
|
|
|
26
44
|
gpu: z.boolean(),
|
|
27
45
|
hostRuntime: z.array(z.string()),
|
|
28
46
|
overlayRuntime: z.array(z.string()),
|
|
29
|
-
//
|
|
30
|
-
|
|
47
|
+
// The shape the container runs with (the owner's ask it carries). Absent from an agent older than `set-shape`.
|
|
48
|
+
shape: SandboxShapeSchema.optional(),
|
|
49
|
+
// The shape saved for the sandbox's next restart through ic, and not yet in force. Absent when nothing is saved.
|
|
50
|
+
desired: SandboxShapeSchema.optional(),
|
|
51
|
+
// READ-ONLY, from agents older than `set-shape`: their saved share, a delta against what runs. Nothing new writes
|
|
52
|
+
// it; kept one release so a newer page can still show what an older agent saved. Remove with `later`.
|
|
31
53
|
saved: SandboxResourcesAskFieldsSchema.optional(),
|
|
32
54
|
});
|
|
33
55
|
export type SandboxResources = z.infer<typeof SandboxResourcesSchema>;
|
|
34
56
|
|
|
35
57
|
// Fields also exported separately for callers that compose them and enforce the at-least-one rule themselves.
|
|
36
|
-
|
|
58
|
+
// Strict like every device input (DeviceSandboxFlowSchema says why): it is only ever sent to a device.
|
|
59
|
+
export const SandboxResourcesAskSchema = SandboxResourcesAskFieldsSchema.strict().refine((ask) => Object.values(ask).some((value) => value !== undefined), {
|
|
37
60
|
message: "a reshape must change at least one thing",
|
|
38
61
|
});
|
|
39
62
|
export type SandboxResourcesAsk = z.infer<typeof SandboxResourcesAskSchema>;
|
|
@@ -50,10 +73,14 @@ export const DeviceSandboxSchema = z.object({
|
|
|
50
73
|
tunnelRunning: z.boolean().optional(),
|
|
51
74
|
// Absent when the reader only ran a cheap `docker ps` listing without inspecting the container.
|
|
52
75
|
resources: SandboxResourcesSchema.optional(),
|
|
76
|
+
// The update `ic sandbox prepare` built and left waiting for this sandbox; absent when nothing is staged.
|
|
77
|
+
staged: z.object({ image: z.string(), version: z.string().optional(), channel: z.string().optional() }).optional(),
|
|
53
78
|
});
|
|
54
79
|
export type DeviceSandbox = z.infer<typeof DeviceSandboxSchema>;
|
|
55
80
|
// One operation on one sandbox, streamed as lines ending in a `result` or `error` frame. `prepare` builds the pending
|
|
56
|
-
// update without touching the container
|
|
81
|
+
// update without touching the container. `set-shape` sets its resources and privileges, now or for the next restart;
|
|
82
|
+
// `forget-shape` drops a shape saved for the next restart. `start`/`restart` apply a saved shape (all through `ic`).
|
|
83
|
+
// `reshape` is the older spelling of `set-shape`, kept one release for pages served by older sandboxes.
|
|
57
84
|
// `runner-up`/`runner-remove` act on a runner (this sandbox's own container), not a person's sandbox; gated by the
|
|
58
85
|
// `sandboxes` switch, not removal, since a runner holds no separate workspace to lose.
|
|
59
86
|
export const DeviceSandboxOpSchema = z.enum([
|
|
@@ -65,6 +92,8 @@ export const DeviceSandboxOpSchema = z.enum([
|
|
|
65
92
|
"rebuild",
|
|
66
93
|
"rollback",
|
|
67
94
|
"reshape",
|
|
95
|
+
"set-shape",
|
|
96
|
+
"forget-shape",
|
|
68
97
|
"remove",
|
|
69
98
|
"logs",
|
|
70
99
|
// The two ops that redeem a fresh setup code; every other one recreates a container from its own existing env.
|
|
@@ -76,7 +105,11 @@ export const DeviceSandboxOpSchema = z.enum([
|
|
|
76
105
|
"runner-remove",
|
|
77
106
|
]);
|
|
78
107
|
export type DeviceSandboxOp = z.infer<typeof DeviceSandboxOpSchema>;
|
|
79
|
-
|
|
108
|
+
// STRICT, like every input a device is sent: an agent rejects a field it does not know instead of dropping it and doing
|
|
109
|
+
// something else (an older agent once dropped `later` and restarted a sandbox it had been asked to leave running). New
|
|
110
|
+
// behaviour is a new `op`, which an older agent refuses by name, and the daemon refuses an op the agent has not
|
|
111
|
+
// advertised before sending it (device-reports.ts).
|
|
112
|
+
export const DeviceSandboxFlowSchema = z.strictObject({
|
|
80
113
|
op: DeviceSandboxOpSchema,
|
|
81
114
|
// Sandbox slug, the runner's name for the runner ops (as `/system/runners`/`ic runner list` know it), or for
|
|
82
115
|
// `create` the name the claim is about to produce — the one op where the slug is an expectation rather than a
|
|
@@ -84,11 +117,14 @@ export const DeviceSandboxFlowSchema = z.object({
|
|
|
84
117
|
slug: z.string().min(1),
|
|
85
118
|
// Approved overlay's sha256, required only by `rebuild`; only content matching it is ever built.
|
|
86
119
|
hash: z.string().optional(),
|
|
87
|
-
//
|
|
88
|
-
//
|
|
120
|
+
// `set-shape` only, both required by it: the whole shape, and when it takes effect.
|
|
121
|
+
// Strict too, unlike the same shape read off a report (where a newer ic's extra field must not fail an older
|
|
122
|
+
// reader): an order carrying a field this agent does not know is refused rather than carried out without it.
|
|
123
|
+
shape: z.strictObject(SandboxShapeSchema.shape).optional(),
|
|
124
|
+
when: SandboxShapeWhenSchema.optional(),
|
|
125
|
+
// READ-ONLY for one release, from pages served by sandboxes older than `set-shape`: the old `reshape` op's delta,
|
|
126
|
+
// and `later` to save it for the next restart instead. Nothing new sends either.
|
|
89
127
|
resources: SandboxResourcesAskSchema.optional(),
|
|
90
|
-
// `reshape` only: save `resources` for the sandbox's next restart instead of restarting it now, replacing
|
|
91
|
-
// whatever was saved before (`ic sandbox reshape --later`).
|
|
92
128
|
later: z.boolean().optional(),
|
|
93
129
|
// `runner-up` only, daemon-filled, never by the caller: the browser never holds the pairing credential.
|
|
94
130
|
parentUrl: z.string().optional(),
|
|
@@ -104,6 +140,26 @@ export const DeviceSandboxFlowSchema = z.object({
|
|
|
104
140
|
overlayHash: z.string().optional(),
|
|
105
141
|
});
|
|
106
142
|
export type DeviceSandboxFlow = z.infer<typeof DeviceSandboxFlowSchema>;
|
|
143
|
+
// The `ic` argv for the shape and power ops, the one place any caller spells them: the machine agent runs it and the
|
|
144
|
+
// Devices view prints it for a person to type when the app's route to the machine is shut. The shape travels as the
|
|
145
|
+
// contract's own object, one `--set <field>=<JSON value>` per field, so no caller maps a field onto a flag of its own:
|
|
146
|
+
// ic reads the pairs back into the contract's shape and refuses a field it does not know, as every device input does.
|
|
147
|
+
// `when` is the contract's own word too. A field left out keeps what ic already has for it (the shape saved for the
|
|
148
|
+
// next restart, else what runs): the Devices view always sends the whole shape, a model's tool call may name one
|
|
149
|
+
// field. Nothing is judged here; ic checks the shape against the image's run contract. Needs an ic that takes `--set`
|
|
150
|
+
// (the `set-shape` feature is advertised only then).
|
|
151
|
+
export type SandboxShapeFields = { readonly [K in keyof SandboxShape]?: SandboxShape[K] | undefined };
|
|
152
|
+
export const icShapeArgs = (slug: string, fields: SandboxShapeFields, when: SandboxShapeWhen): string[] => [
|
|
153
|
+
"sandbox",
|
|
154
|
+
"shape",
|
|
155
|
+
slug,
|
|
156
|
+
...Object.entries(fields).flatMap(([field, value]) => (value === undefined ? [] : ["--set", `${field}=${JSON.stringify(value)}`])),
|
|
157
|
+
"--when",
|
|
158
|
+
when,
|
|
159
|
+
];
|
|
160
|
+
export const icForgetShapeArgs = (slug: string): string[] => ["sandbox", "shape", slug, "--forget"];
|
|
161
|
+
// Power goes through ic too, so a Start or Restart from any door applies the shape saved for the next restart.
|
|
162
|
+
export const icPowerArgs = (op: "start" | "stop" | "restart", slug: string): string[] => ["sandbox", op, slug];
|
|
107
163
|
// Adds the device id to the flow input; the daemon looks up machines by id.
|
|
108
164
|
export const DeviceSandboxFlowInputSchema = DeviceSandboxFlowSchema.extend({ id: z.string().min(1) });
|
|
109
165
|
export type DeviceSandboxFlowInput = z.infer<typeof DeviceSandboxFlowInputSchema>;
|
|
@@ -123,7 +179,7 @@ export type DeviceFlowLine = z.infer<typeof DeviceFlowLineSchema>;
|
|
|
123
179
|
// dropping one is always somebody's decision.
|
|
124
180
|
export const DeviceAgentOpSchema = z.enum(["upgrade", "restart", "forget-unreachable"]);
|
|
125
181
|
export type DeviceAgentOp = z.infer<typeof DeviceAgentOpSchema>;
|
|
126
|
-
export const DeviceAgentFlowSchema = z.
|
|
182
|
+
export const DeviceAgentFlowSchema = z.strictObject({ op: DeviceAgentOpSchema });
|
|
127
183
|
export type DeviceAgentFlow = z.infer<typeof DeviceAgentFlowSchema>;
|
|
128
184
|
// Adds the device id to the flow input.
|
|
129
185
|
export const DeviceAgentFlowInputSchema = DeviceAgentFlowSchema.extend({ id: z.string().min(1) });
|
|
@@ -362,8 +418,10 @@ export const AGENT_STALL_AFTER_MS = 60_000;
|
|
|
362
418
|
export const agentStalled = (agent: DeviceAgent, now: number): boolean =>
|
|
363
419
|
agent.running && agent.lastTickAt !== undefined && now - agent.lastTickAt > AGENT_STALL_AFTER_MS;
|
|
364
420
|
export const DeviceReportSchema = z.object({
|
|
365
|
-
//
|
|
366
|
-
//
|
|
421
|
+
// The computer this reading is from (MachineIdSchema): what a sync enrollment is stamped with when its agent posts,
|
|
422
|
+
// and so what joins a machine seen through sync to the same machine seen through its card. Absent from an older agent.
|
|
423
|
+
machineId: MachineIdSchema.optional(),
|
|
424
|
+
// OS hostname, for the owner to read; not a join key (a WSL distro inherits the Windows machine's name).
|
|
367
425
|
hostname: z.string(),
|
|
368
426
|
os: z.string(),
|
|
369
427
|
// Present only inside a WSL distro; the same fact rides the connect-time facts, which no scope can withhold.
|
|
@@ -420,9 +478,8 @@ export const DeviceGapSchema = z.enum([
|
|
|
420
478
|
"unreported",
|
|
421
479
|
]);
|
|
422
480
|
export type DeviceGap = z.infer<typeof DeviceGapSchema>;
|
|
423
|
-
// A machine may be reachable via desktop sync and a host capability at once; the two are
|
|
424
|
-
// the environment
|
|
425
|
-
// or when the environments positively disagree.
|
|
481
|
+
// A machine may be reachable via desktop sync and a host capability at once; the two are one row when both say the same
|
|
482
|
+
// `machineId` and the same environment, and separate rows whenever either has not said (an agent too old to carry it).
|
|
426
483
|
// `machine` is the enrollment's name for the box (the ssh key's comment): what reports are filed under and what the
|
|
427
484
|
// revoke route takes. Two machines sharing a key comment share one enrollment identity.
|
|
428
485
|
export const DeviceSyncSchema = z.object({
|
|
@@ -431,6 +488,10 @@ export const DeviceSyncSchema = z.object({
|
|
|
431
488
|
mode: z.enum(["sync", "mirror"]),
|
|
432
489
|
// When this machine last used its enrollment; absent means never, not merely unknown, and is not healthy.
|
|
433
490
|
seenAt: z.number().optional(),
|
|
491
|
+
// The computer and the environment on it that hold this enrollment, as its agent said when enrolling or in a report
|
|
492
|
+
// since; absent until an agent new enough to say has done so.
|
|
493
|
+
machineId: MachineIdSchema.optional(),
|
|
494
|
+
environment: z.string().optional(),
|
|
434
495
|
});
|
|
435
496
|
export type DeviceSync = z.infer<typeof DeviceSyncSchema>;
|
|
436
497
|
export const DeviceSchema = z.object({
|
|
@@ -440,8 +501,14 @@ export const DeviceSchema = z.object({
|
|
|
440
501
|
label: z.string(),
|
|
441
502
|
// Desktop-sync enrollment with this sandbox; absent when reached only via a host capability.
|
|
442
503
|
sync: DeviceSyncSchema.optional(),
|
|
443
|
-
// The host
|
|
504
|
+
// The host connection's key, when this machine is also a connected device. Absent otherwise.
|
|
444
505
|
hostId: z.string().optional(),
|
|
506
|
+
// The card that connection is of, beside the key rather than parsed out of it: the machine's name, a label. Absent
|
|
507
|
+
// with `hostId`, and from a daemon older than the field (`deviceCard` reads the key then).
|
|
508
|
+
card: z.string().optional(),
|
|
509
|
+
// The computer this row is an environment of, from whichever door has said (MachineIdSchema): what `machinesOf`
|
|
510
|
+
// groups by. Absent when no door has, which leaves the row a computer of its own.
|
|
511
|
+
machineId: MachineIdSchema.optional(),
|
|
445
512
|
// Host-capability liveness; absent when there is no host capability, not the same as offline.
|
|
446
513
|
online: z.boolean().optional(),
|
|
447
514
|
// Carried beside the report so a device with no report still has an OS; `platform` is the normalised slug, `facts`
|
|
@@ -480,7 +547,7 @@ export const deviceHostname = (device: Device): string | undefined => device.fac
|
|
|
480
547
|
// and that is all an environment nobody has reached since this daemon booted can say about itself: liveness resets on
|
|
481
548
|
// restart, so facts and reports are absent on a side that is merely asleep.
|
|
482
549
|
export const deviceEnvironment = (device: Device): string | undefined =>
|
|
483
|
-
environmentOf(device.facts, device.report) ?? (device.hostId === undefined ? undefined :
|
|
550
|
+
environmentOf(device.facts, device.report) ?? (device.hostId === undefined ? undefined : parseHostConnection(device.hostId).environment);
|
|
484
551
|
|
|
485
552
|
const WSL_PREFIX = "wsl:";
|
|
486
553
|
export const isWslDevice = (device: Device): boolean => deviceEnvironment(device)?.startsWith(WSL_PREFIX) === true;
|
|
@@ -495,24 +562,25 @@ export const deviceDistro = (device: Device): string | undefined => {
|
|
|
495
562
|
const byEnvironment = (a: Device, b: Device): number =>
|
|
496
563
|
Number(isWslDevice(a)) - Number(isWslDevice(b)) || (deviceDistro(a) ?? a.label).localeCompare(deviceDistro(b) ?? b.label);
|
|
497
564
|
|
|
498
|
-
const hostnameKey = (device: Device): string | undefined => deviceHostname(device)?.toLowerCase();
|
|
499
|
-
|
|
500
565
|
// The card a device's door hangs off: one card is one computer, so two doors naming the same card are one machine
|
|
501
566
|
// whatever either has said about itself — which is the whole of what an environment that has never connected says.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
567
|
+
// The row's own label, else its key read by the one parser there is (a row from a daemon older than `card`).
|
|
568
|
+
export const deviceCard = (device: Pick<Device, "card" | "hostId">): string | undefined =>
|
|
569
|
+
device.card ?? (device.hostId === undefined ? undefined : parseHostConnection(device.hostId).card);
|
|
570
|
+
|
|
571
|
+
// The computer a device is on, by whichever door said: its row, its connect-time facts, its report, its sync enrollment.
|
|
572
|
+
export const deviceMachineId = (device: Device): string | undefined =>
|
|
573
|
+
device.machineId ?? device.facts?.machineId ?? device.report?.machineId ?? device.sync?.machineId;
|
|
574
|
+
|
|
575
|
+
// What makes two devices one computer: a shared card, or a shared machine id. Nothing else: a hostname is shared by a
|
|
576
|
+
// PC and its WSL distros and by two unrelated machines with the same name, so it joined things by accident.
|
|
577
|
+
const tokensOf = (device: Device): string[] => {
|
|
578
|
+
const card = deviceCard(device);
|
|
579
|
+
const machine = deviceMachineId(device);
|
|
580
|
+
return [...(card === undefined ? [] : [`card:${card.toLowerCase()}`]), ...(machine === undefined ? [] : [`machine:${machine}`])];
|
|
513
581
|
};
|
|
514
582
|
|
|
515
|
-
// Components over both joins at once: a device holding a card token and a
|
|
583
|
+
// Components over both joins at once: a device holding a card token and a machine token is the evidence that merges
|
|
516
584
|
// the machines those tokens named, so a distro connected under a card of its own still lands on the PC it runs on.
|
|
517
585
|
interface Component {
|
|
518
586
|
readonly tokens: Set<string>;
|
|
@@ -520,10 +588,9 @@ interface Component {
|
|
|
520
588
|
}
|
|
521
589
|
|
|
522
590
|
const componentsOf = (devices: readonly Device[]): Component[] => {
|
|
523
|
-
const distroNames = new Set(devices.filter(isWslDevice).map(hostnameKey).filter((name) => name !== undefined));
|
|
524
591
|
const components: Component[] = [];
|
|
525
592
|
for (const device of devices) {
|
|
526
|
-
const tokens = tokensOf(device
|
|
593
|
+
const tokens = tokensOf(device);
|
|
527
594
|
const [head, ...merged] = components.filter((component) => tokens.some((token) => component.tokens.has(token)));
|
|
528
595
|
if (head === undefined) {
|
|
529
596
|
components.push({ tokens: new Set(tokens), devices: [device] });
|
|
@@ -544,7 +611,7 @@ const componentsOf = (devices: readonly Device[]): Component[] => {
|
|
|
544
611
|
|
|
545
612
|
// Addressed by the card of the side that owns the screen, an address that does not change when a second environment
|
|
546
613
|
// connects — unlike a row key, which is a hostname another environment can take first. An uncarded fold has only the
|
|
547
|
-
//
|
|
614
|
+
// machine id its doors share, and an uncarded lone device its own key. Called what the owner calls it: the name the
|
|
548
615
|
// leading environment carries, unless that is nothing but its door id, which reads as a PC named after one of its
|
|
549
616
|
// own sides.
|
|
550
617
|
const machineOf = (environments: readonly Device[]): Machine => {
|
|
@@ -553,8 +620,8 @@ const machineOf = (environments: readonly Device[]): Machine => {
|
|
|
553
620
|
if (first === undefined) {
|
|
554
621
|
return { key: "", label: "", environments };
|
|
555
622
|
}
|
|
556
|
-
const named = environments.map(
|
|
557
|
-
const label = first.label === first.hostId ? (
|
|
623
|
+
const named = environments.map(deviceCard).find((key) => key !== undefined) ?? (rest.length === 0 ? undefined : deviceMachineId(first));
|
|
624
|
+
const label = first.label === first.hostId ? (deviceCard(first) ?? first.label) : first.label;
|
|
558
625
|
return { key: named ?? first.key, label, environments };
|
|
559
626
|
};
|
|
560
627
|
|
|
@@ -177,6 +177,12 @@ export const ExtensionSummarySchema = z.object({
|
|
|
177
177
|
})
|
|
178
178
|
.optional()
|
|
179
179
|
.describe("Present only for an extension that ships a server half."),
|
|
180
|
+
problems: z
|
|
181
|
+
.array(z.string())
|
|
182
|
+
.optional()
|
|
183
|
+
.describe(
|
|
184
|
+
"Declarations in its manifest the sandbox refused at load, each a sentence saying what and why, such as a listener for a provider another extension already owns. The rest of it still loads. Absent when nothing was refused.",
|
|
185
|
+
),
|
|
180
186
|
// Update lifecycle, present only for a git-installed extension (absent for builtin/workspace):
|
|
181
187
|
// update: the badge.
|
|
182
188
|
// advisory: the alarm.
|