@intentic/sandbox-contract 1.317.0 → 1.319.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 +16 -6
- package/dist/chores/chores.js +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/probes.js +1 -1
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +75 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/accounts.contract.js +10 -1
- package/dist/contracts/accounts.contract.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +62 -1
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +830 -93
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +12 -12
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/ci.contract.d.ts +22 -4
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +38 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +2 -0
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts +23 -0
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js +6 -1
- package/dist/contracts/endpoints.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +12 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +2 -0
- package/dist/contracts/needs.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.d.ts +4 -0
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +143 -124
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts +37 -0
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +20 -1
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +4 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +31 -4
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +20 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +102 -464
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +2 -12
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/translator.contract.d.ts +7 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.js.map +1 -1
- package/dist/contracts/webext.contract.d.ts +38 -0
- package/dist/contracts/webext.contract.d.ts.map +1 -1
- package/dist/contracts/webext.contract.js +3 -0
- package/dist/contracts/webext.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +1 -133
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +1 -37
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +21 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +11 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-words.d.ts +1 -0
- package/dist/events/agent-words.d.ts.map +1 -1
- package/dist/events/agent-words.js +1 -1
- package/dist/events/agent-words.js.map +1 -1
- package/dist/events/land-breakage.d.ts.map +1 -1
- package/dist/events/land-breakage.js.map +1 -1
- package/dist/events/resume.d.ts +6 -1
- package/dist/events/resume.d.ts.map +1 -1
- package/dist/events/resume.js +17 -10
- package/dist/events/resume.js.map +1 -1
- package/dist/events/sandbox-notice.d.ts +183 -0
- package/dist/events/sandbox-notice.d.ts.map +1 -0
- package/dist/events/sandbox-notice.js +56 -0
- package/dist/events/sandbox-notice.js.map +1 -0
- package/dist/events/system-events.d.ts +50 -18
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +4 -2
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +24 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +3 -1
- package/dist/events/transcript.js.map +1 -1
- package/dist/front/generated/browser-wire.d.ts +13 -0
- package/dist/front/generated/browser-wire.d.ts.map +1 -1
- package/dist/front/generated/wire.d.ts +4 -0
- package/dist/front/generated/wire.d.ts.map +1 -1
- package/dist/ids/conversation-ids.d.ts +1 -3
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +1 -3
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/ids/project-dir.d.ts +6 -0
- package/dist/ids/project-dir.d.ts.map +1 -0
- package/dist/ids/project-dir.js +43 -0
- package/dist/ids/project-dir.js.map +1 -0
- package/dist/index.d.ts +11543 -11039
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +2 -2
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/model-roles.d.ts +1 -9
- package/dist/models/model-roles.d.ts.map +1 -1
- package/dist/models/model-roles.js +1 -9
- package/dist/models/model-roles.js.map +1 -1
- package/dist/policy/command-classes.d.ts.map +1 -1
- package/dist/policy/command-classes.js +52 -3
- package/dist/policy/command-classes.js.map +1 -1
- package/dist/policy/safety-policy.d.ts +7 -1
- package/dist/policy/safety-policy.d.ts.map +1 -1
- package/dist/policy/safety-policy.js +2 -0
- package/dist/policy/safety-policy.js.map +1 -1
- package/dist/policy/secret-hosts.d.ts +13 -0
- package/dist/policy/secret-hosts.d.ts.map +1 -0
- package/dist/policy/secret-hosts.js +41 -0
- package/dist/policy/secret-hosts.js.map +1 -0
- 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/raw-routes.d.ts +12 -0
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +5 -0
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/routes.d.ts +2 -6
- package/dist/protocol/routes.d.ts.map +1 -1
- package/dist/protocol/routes.js +2 -3
- package/dist/protocol/routes.js.map +1 -1
- package/dist/protocol/stream-of.d.ts +6 -0
- package/dist/protocol/stream-of.d.ts.map +1 -0
- package/dist/protocol/stream-of.js +4 -0
- package/dist/protocol/stream-of.js.map +1 -0
- package/dist/protocol/vitals.d.ts +8 -0
- package/dist/protocol/vitals.d.ts.map +1 -0
- package/dist/protocol/vitals.js +16 -0
- package/dist/protocol/vitals.js.map +1 -0
- package/dist/schemas/activity.js +1 -1
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agent.d.ts +25 -2
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +14 -3
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +67 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +36 -0
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +20 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +2 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +34 -4
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/ci.d.ts +69 -9
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +29 -8
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/context-trim.d.ts +5 -1
- package/dist/schemas/context-trim.d.ts.map +1 -1
- package/dist/schemas/context-trim.js +8 -4
- package/dist/schemas/context-trim.js.map +1 -1
- package/dist/schemas/devices.d.ts +23 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +16 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/environment.d.ts +48 -0
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +15 -0
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +24 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +13 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +11 -1
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +2 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +2 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +4 -0
- package/dist/schemas/keep-warm.d.ts.map +1 -1
- package/dist/schemas/keep-warm.js +15 -5
- package/dist/schemas/keep-warm.js.map +1 -1
- package/dist/schemas/loopback-catch.d.ts +33 -0
- package/dist/schemas/loopback-catch.d.ts.map +1 -0
- package/dist/schemas/loopback-catch.js +20 -0
- package/dist/schemas/loopback-catch.js.map +1 -0
- package/dist/schemas/maintenance.js +1 -1
- package/dist/schemas/maintenance.js.map +1 -1
- package/dist/schemas/needs.d.ts +3 -0
- package/dist/schemas/needs.d.ts.map +1 -1
- package/dist/schemas/needs.js +10 -3
- package/dist/schemas/needs.js.map +1 -1
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +6 -3
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +87 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +20 -0
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/secrets.d.ts +66 -0
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js +50 -0
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +2 -2
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +2 -0
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +42 -4
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +26 -8
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/terminal.d.ts +2 -3
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js +12 -4
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/webext.d.ts +3 -0
- package/dist/schemas/webext.d.ts.map +1 -1
- package/dist/schemas/webext.js +1 -0
- package/dist/schemas/webext.js.map +1 -1
- package/dist/schemas/workflows.js +3 -3
- package/dist/schemas/workflows.js.map +1 -1
- package/dist/schemas/workspace/workspace-tree.d.ts +2 -56
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +7 -15
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +12 -8
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +7 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +0 -3
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +0 -1
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +0 -5
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +0 -6
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/documents.d.ts +3 -0
- package/dist/text/documents.d.ts.map +1 -1
- package/dist/text/documents.js +14 -0
- package/dist/text/documents.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +4 -3
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +137 -31
- package/dist/text/transcript-fold.js.map +1 -1
- package/dist/webext/index.d.ts +1 -0
- package/dist/webext/index.d.ts.map +1 -1
- package/dist/webext/index.js +1 -0
- package/dist/webext/index.js.map +1 -1
- package/package.json +5 -5
- package/src/chores/chores.ts +2 -2
- package/src/chores/probes.ts +1 -1
- package/src/contracts/accounts.contract.ts +12 -0
- package/src/contracts/agents.contract.ts +13 -13
- package/src/contracts/device.contract.ts +5 -0
- package/src/contracts/endpoints.contract.ts +20 -4
- package/src/contracts/secrets.contract.ts +28 -0
- package/src/contracts/settings.contract.ts +29 -0
- package/src/contracts/system.contract.ts +5 -14
- package/src/contracts/translator.contract.ts +2 -1
- package/src/contracts/webext.contract.ts +7 -1
- package/src/contracts/workspace.contract.ts +0 -49
- package/src/events/agent-events.ts +18 -1
- package/src/events/agent-words.ts +4 -1
- package/src/events/land-breakage.ts +3 -4
- package/src/events/resume.test.ts +13 -2
- package/src/events/resume.ts +25 -12
- package/src/events/sandbox-notice.ts +92 -0
- package/src/events/system-events.ts +12 -5
- package/src/events/transcript.ts +7 -1
- package/src/events/verify-nudge.ts +3 -3
- package/src/front/generated/browser-wire.json +95 -0
- package/src/front/generated/browser-wire.ts +36 -0
- package/src/front/generated/front-wire.json +25 -0
- package/src/front/generated/wire.ts +2 -2
- package/src/front/wire-manifests.test.ts +7 -0
- package/src/ids/conversation-ids.test.ts +2 -26
- package/src/ids/conversation-ids.ts +6 -12
- package/src/ids/project-dir.fixture.json +65 -0
- package/src/ids/project-dir.test.ts +60 -0
- package/src/ids/project-dir.ts +70 -0
- package/src/index.ts +13 -4
- package/src/models/agent-catalog.test.ts +7 -1
- package/src/models/agent-runtimes.ts +7 -5
- package/src/models/model-roles.ts +1 -9
- package/src/policy/command-classes.test.ts +39 -0
- package/src/policy/command-classes.ts +70 -3
- package/src/policy/safety-policy.ts +10 -0
- package/src/policy/secret-hosts.test.ts +89 -0
- package/src/policy/secret-hosts.ts +73 -0
- package/src/policy/turned-away.ts +1 -0
- package/src/protocol/raw-routes.ts +8 -1
- package/src/protocol/routes.test.ts +6 -6
- package/src/protocol/routes.ts +2 -9
- package/src/protocol/stream-of.ts +13 -0
- package/src/protocol/vitals.test.ts +46 -0
- package/src/protocol/vitals.ts +38 -0
- package/src/schemas/activity.ts +1 -1
- package/src/schemas/agent.ts +20 -3
- package/src/schemas/agents.ts +58 -4
- package/src/schemas/automations.ts +2 -2
- package/src/schemas/capabilities.ts +44 -7
- package/src/schemas/ci.ts +56 -11
- package/src/schemas/context-trim.ts +11 -6
- package/src/schemas/devices.ts +38 -2
- package/src/schemas/environment.ts +39 -0
- package/src/schemas/git/git.ts +20 -2
- package/src/schemas/hosts.ts +4 -1
- package/src/schemas/keep-warm.ts +19 -6
- package/src/schemas/loopback-catch.ts +45 -0
- package/src/schemas/maintenance.ts +1 -1
- package/src/schemas/needs.ts +12 -3
- package/src/schemas/personas.ts +8 -3
- package/src/schemas/providers/provider-oauth.ts +35 -0
- package/src/schemas/secrets.ts +70 -0
- package/src/schemas/settings-history.ts +3 -0
- package/src/schemas/settings.test.ts +19 -1
- package/src/schemas/settings.ts +59 -16
- package/src/schemas/speaker.ts +5 -5
- package/src/schemas/terminal.ts +19 -6
- package/src/schemas/webext.ts +3 -0
- package/src/schemas/workflows.ts +5 -5
- package/src/schemas/workspace/workspace-tree.ts +12 -20
- package/src/state/fix-attempt-plan.test.ts +1 -1
- package/src/state/fix-stance.ts +1 -1
- package/src/state/history-state.ts +14 -0
- package/src/state/runtime-state.ts +0 -3
- package/src/state/workspace-state.ts +0 -6
- package/src/text/documents.test.ts +25 -1
- package/src/text/documents.ts +24 -0
- package/src/text/title.ts +1 -1
- package/src/text/transcript-fold.test.ts +218 -25
- package/src/text/transcript-fold.ts +166 -45
- package/src/webext/index.ts +1 -0
- package/dist/schemas/workspace/push-checks.d.ts +0 -222
- package/dist/schemas/workspace/push-checks.d.ts.map +0 -1
- package/dist/schemas/workspace/push-checks.js +0 -97
- package/dist/schemas/workspace/push-checks.js.map +0 -1
- package/dist/state/red-streak.d.ts +0 -7
- package/dist/state/red-streak.d.ts.map +0 -1
- package/dist/state/red-streak.js +0 -11
- package/dist/state/red-streak.js.map +0 -1
- package/src/schemas/workspace/push-checks.test.ts +0 -28
- package/src/schemas/workspace/push-checks.ts +0 -155
- package/src/state/red-streak.test.ts +0 -28
- package/src/state/red-streak.ts +0 -28
package/src/schemas/devices.ts
CHANGED
|
@@ -178,8 +178,10 @@ export const icShapeArgs = (slug: string, fields: SandboxShapeFields, when: Sand
|
|
|
178
178
|
export const icForgetShapeArgs = (slug: string): string[] => ["sandbox", "shape", slug, "--forget"];
|
|
179
179
|
// Power goes through ic too, so a Start or Restart from any door applies the shape saved for the next restart.
|
|
180
180
|
export const icPowerArgs = (op: "start" | "stop" | "restart", slug: string): string[] => ["sandbox", op, slug];
|
|
181
|
-
// Adds the device id to the flow input; the daemon looks up machines by id.
|
|
182
|
-
|
|
181
|
+
// Adds the device id to the flow input; the daemon looks up machines by id. `resumeTurns` is the owner saying, as they
|
|
182
|
+
// restart the sandbox relaying this, that the turns it cuts should run again once it is back; the daemon keeps it for
|
|
183
|
+
// its next boot and never forwards it to the machine. A sandbox older than it ignores it, and the cut turns wait.
|
|
184
|
+
export const DeviceSandboxFlowInputSchema = DeviceSandboxFlowSchema.extend({ id: z.string().min(1), resumeTurns: z.boolean().optional() });
|
|
183
185
|
export type DeviceSandboxFlowInput = z.infer<typeof DeviceSandboxFlowInputSchema>;
|
|
184
186
|
// Streamed operation output, matching `IntenticLineSchema`: `line` frames as printed, then one terminal frame, `result`
|
|
185
187
|
// or `error`, carrying the machine's own message rather than a code.
|
|
@@ -374,6 +376,9 @@ export const DevicePairingSchema = z.object({
|
|
|
374
376
|
mode: z.enum(["sync", "mirror"]),
|
|
375
377
|
// Set only for mode "sync", and only for the sandbox being reported to.
|
|
376
378
|
localDir: z.string().optional(),
|
|
379
|
+
// Which sandbox folder `localDir` holds when it is not /work itself: a project pairing's `/work/<name>`, the
|
|
380
|
+
// owner's own folder, which carries no state backup and no git bridge. Absent means /work.
|
|
381
|
+
remoteDir: z.string().optional(),
|
|
377
382
|
// Machine-owned switch (agent's own `sync mirror off`); absent reads as "on", not merely unknown.
|
|
378
383
|
mirroring: z.enum(["on", "off"]).optional(),
|
|
379
384
|
// Mutagen's own status word, kept verbatim rather than reduced to a traffic light.
|
|
@@ -512,6 +517,37 @@ export const DeviceSyncSchema = z.object({
|
|
|
512
517
|
environment: z.string().optional(),
|
|
513
518
|
});
|
|
514
519
|
export type DeviceSync = z.infer<typeof DeviceSyncSchema>;
|
|
520
|
+
|
|
521
|
+
// A public host key as a machine pins it in known_hosts: its type and base64 body, no comment and nothing after.
|
|
522
|
+
export const SshHostKeySchema = z
|
|
523
|
+
.string()
|
|
524
|
+
.regex(/^(?:ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(?:256|384|521)|sk-ssh-ed25519@openssh\.com|sk-ecdsa-sha2-nistp256@openssh\.com) [A-Za-z0-9+/]+={0,2}$/);
|
|
525
|
+
|
|
526
|
+
// POST /system/authorized-key: a machine's agent enrolling its SSH key for desktop sync, redeeming a pairing token.
|
|
527
|
+
// Every field past `key` is optional: an older agent sends none of them and is still enrolled.
|
|
528
|
+
export const SyncEnrollmentRequestSchema = z.object({
|
|
529
|
+
// One authorized_keys line; its comment is the name the enrollment goes by (`DeviceSync.machine`).
|
|
530
|
+
key: z.string(),
|
|
531
|
+
// The computer and the environment on it that are enrolling, so the enrollment joins that machine's card.
|
|
532
|
+
machineId: MachineIdSchema.optional(),
|
|
533
|
+
environment: z.string().min(1).optional(),
|
|
534
|
+
});
|
|
535
|
+
export type SyncEnrollmentRequest = z.infer<typeof SyncEnrollmentRequestSchema>;
|
|
536
|
+
|
|
537
|
+
// The daemon's answer to an enrollment. Every field is optional for the agent reading it, since each arrived in a later
|
|
538
|
+
// daemon than the route: without `syncToken` the agent refuses the enrollment, without `mode` it reads "sync", and
|
|
539
|
+
// without `hostKey` it falls back to recording whatever key the sandbox presents first.
|
|
540
|
+
export const SyncEnrollmentAnswerSchema = z.object({
|
|
541
|
+
ok: z.literal(true).optional(),
|
|
542
|
+
// Authorizes the ports poll, the machine report and the SSH transport, sent as `x-intentic-sync`.
|
|
543
|
+
syncToken: z.string().optional(),
|
|
544
|
+
// What the pairing granted: a member's pairing only ever enrolls "mirror".
|
|
545
|
+
mode: z.enum(["sync", "mirror"]).optional(),
|
|
546
|
+
// The public key the sandbox's sshd presents (an SshHostKeySchema line), for the agent to pin under the pairing's
|
|
547
|
+
// alias. Checked by the agent on its own, so a key it cannot read costs the pin, never the enrollment.
|
|
548
|
+
hostKey: z.string().optional(),
|
|
549
|
+
});
|
|
550
|
+
export type SyncEnrollmentAnswer = z.infer<typeof SyncEnrollmentAnswerSchema>;
|
|
515
551
|
export const DeviceSchema = z.object({
|
|
516
552
|
// Stable row key: the reported hostname when either door produced one, else the name that door knows it by.
|
|
517
553
|
key: z.string(),
|
|
@@ -40,10 +40,17 @@ export const RuntimeInstallSchema = z.object({
|
|
|
40
40
|
declinedAt: z.number().optional(),
|
|
41
41
|
});
|
|
42
42
|
export type RuntimeInstall = z.infer<typeof RuntimeInstallSchema>;
|
|
43
|
+
// A draft the owner already answered (approved, removed or declined), by its tool and the hash of its steps: the same
|
|
44
|
+
// file coming back with an agent's land, which still carries it on its branch, proposes nothing. An agent asking again
|
|
45
|
+
// (`environment propose`) clears its tool's entries.
|
|
46
|
+
export const SettledDraftSchema = z.object({ tool: z.string(), hash: z.string(), at: z.number() });
|
|
47
|
+
export type SettledDraft = z.infer<typeof SettledDraftSchema>;
|
|
43
48
|
export const RuntimeInstallsFileSchema = z.object({
|
|
44
49
|
installs: z.array(RuntimeInstallSchema),
|
|
45
50
|
// The last drift snapshot, persisted so a daemon restart does not blank the card until the next sweep.
|
|
46
51
|
drift: EnvironmentDriftSchema.optional(),
|
|
52
|
+
// Drafts already answered, newest last and capped; absent on a ledger written before removal existed.
|
|
53
|
+
settled: z.array(SettledDraftSchema).optional(),
|
|
47
54
|
});
|
|
48
55
|
export type RuntimeInstallsFile = z.infer<typeof RuntimeInstallsFileSchema>;
|
|
49
56
|
// Ledger entry as the Environment card shows it: recurrence joined with whether it's present in the live container
|
|
@@ -74,6 +81,26 @@ export type EnvironmentRuntimeDecision = z.infer<typeof EnvironmentRuntimeDecisi
|
|
|
74
81
|
// there (SANDBOX_DEV_ROOT), absent on a sandbox that was handed a local image without one.
|
|
75
82
|
export const EnvironmentLocalImageSchema = z.object({ base: z.string(), root: z.string().optional() });
|
|
76
83
|
export type EnvironmentLocalImage = z.infer<typeof EnvironmentLocalImageSchema>;
|
|
84
|
+
// A rebuild the owner asked to start by itself once no agent is mid-turn, kept by the sandbox rather than the page that
|
|
85
|
+
// asked, so a page closed or a phone locked meanwhile does not lose it. Held in the sandbox's memory: a restart of the
|
|
86
|
+
// sandbox for any other reason drops it, and the card offers the rebuild again.
|
|
87
|
+
export const EnvironmentRebuildWaitSchema = z.object({
|
|
88
|
+
// The approved overlay it builds, and the connected device that runs it.
|
|
89
|
+
hash: z.string(),
|
|
90
|
+
host: z.string(),
|
|
91
|
+
requestedAt: z.number(),
|
|
92
|
+
// waiting: until no agent is mid-turn. rebuilding: handed to the device, which restarts this sandbox at the end.
|
|
93
|
+
// failed: the device refused or could not be reached; said until the owner dismisses it or asks again.
|
|
94
|
+
phase: z.enum(["waiting", "rebuilding", "failed"]),
|
|
95
|
+
// Who it waits on, by the title the board shows them under; empty once it is no longer waiting.
|
|
96
|
+
waitingOn: z.array(z.string()),
|
|
97
|
+
// The device's own words for a failed one.
|
|
98
|
+
message: z.string().optional(),
|
|
99
|
+
});
|
|
100
|
+
export type EnvironmentRebuildWait = z.infer<typeof EnvironmentRebuildWaitSchema>;
|
|
101
|
+
// Asks for that rebuild: the approved overlay's hash and the device that rebuilds this sandbox.
|
|
102
|
+
export const EnvironmentRebuildWhenIdleSchema = z.object({ host: z.string().min(1), hash: z.string().min(1) });
|
|
103
|
+
export type EnvironmentRebuildWhenIdle = z.infer<typeof EnvironmentRebuildWhenIdleSchema>;
|
|
77
104
|
export const EnvironmentSchema = z.object({
|
|
78
105
|
proposal: environmentFileSchema.optional(),
|
|
79
106
|
// The owner-approved agent-written custom section (.intentic/config/environment.custom.Dockerfile).
|
|
@@ -89,6 +116,12 @@ export const EnvironmentSchema = z.object({
|
|
|
89
116
|
recurring: z.array(EnvironmentRecurringSchema).optional(),
|
|
90
117
|
// Absent on a sandbox running a published image, which is every sandbox but a dogfooding one.
|
|
91
118
|
localImage: EnvironmentLocalImageSchema.optional(),
|
|
119
|
+
// A rebuild waiting for the agents to be idle, or started or refused once they were; absent when none is asked.
|
|
120
|
+
rebuildWhenIdle: EnvironmentRebuildWaitSchema.optional(),
|
|
121
|
+
// This sandbox can hold a rebuild until no agent is mid-turn (POST /environment/rebuild-when-idle), and resume the
|
|
122
|
+
// turns a restart the owner starts cuts when asked (a device swap's `resumeTurns`). Absent from one too old to,
|
|
123
|
+
// where the rebuild dialog offers only "Rebuild now" and the cut turns wait to be continued, as before.
|
|
124
|
+
waitsForAgents: z.literal(true).optional(),
|
|
92
125
|
});
|
|
93
126
|
export type Environment = z.infer<typeof EnvironmentSchema>;
|
|
94
127
|
export const EnvironmentApproveSchema = z.object({ hash: z.string().min(1) });
|
|
@@ -127,8 +160,14 @@ export const EnvironmentItemSchema = z.object({
|
|
|
127
160
|
detail: z.string().optional(),
|
|
128
161
|
// The block's own instruction lines, for the reader who wants to see exactly what runs.
|
|
129
162
|
commands: z.string().optional(),
|
|
163
|
+
// The custom-section block this row is, which the owner may take out (POST /environment/remove names it). Absent
|
|
164
|
+
// on capability and base rows, and from a sandbox that cannot remove one, so no Remove is offered there.
|
|
165
|
+
block: z.string().optional(),
|
|
130
166
|
});
|
|
131
167
|
export type EnvironmentItem = z.infer<typeof EnvironmentItemSchema>;
|
|
168
|
+
// Takes one tool out of the environment: its approved block, its pending draft, or both.
|
|
169
|
+
export const EnvironmentRemoveSchema = z.object({ block: z.string() });
|
|
170
|
+
export type EnvironmentRemove = z.infer<typeof EnvironmentRemoveSchema>;
|
|
132
171
|
export const EnvironmentContentsSchema = z.object({ items: z.array(EnvironmentItemSchema) });
|
|
133
172
|
export type EnvironmentContents = z.infer<typeof EnvironmentContentsSchema>;
|
|
134
173
|
// A sandbox is four stores: /work, /history, the container, and the AI-provider credential root. A bundle carries only
|
package/src/schemas/git/git.ts
CHANGED
|
@@ -86,7 +86,7 @@ export const PushSchema = RepoParamSchema.extend({
|
|
|
86
86
|
});
|
|
87
87
|
// Who said no to a push, read off git's own words (`pushRefusal`); the three answers ask three different things of the
|
|
88
88
|
// owner.
|
|
89
|
-
// hook: this repo's own pre-push hook refused it;
|
|
89
|
+
// hook: this repo's own pre-push hook refused it; what it printed is about the code, and the terminal holds all of it.
|
|
90
90
|
// remote: the server rejected the refs (non-fast-forward, protected branch); pull first or push elsewhere.
|
|
91
91
|
// transport: it never got there (credentials, unreachable host); a retry is the only useful button.
|
|
92
92
|
export const PushRefusalSchema = z.enum(["hook", "remote", "transport"]);
|
|
@@ -100,7 +100,7 @@ export const PushRunSchema = CommandRunSchema.extend({
|
|
|
100
100
|
.optional()
|
|
101
101
|
.describe("Why not, in git's own words: the last verdict line, for a row that has room for one line. The whole tail is `output`."),
|
|
102
102
|
refusedBy: PushRefusalSchema.optional().describe(
|
|
103
|
-
"Who refused a failed push: this repository's pre-push hook (
|
|
103
|
+
"Who refused a failed push: this repository's own pre-push hook (what it printed is about the code), the remote (pull first), or the transport (credentials, network: retry). Absent while it runs and for a push that went.",
|
|
104
104
|
),
|
|
105
105
|
});
|
|
106
106
|
export type PushRun = z.infer<typeof PushRunSchema>;
|
|
@@ -389,6 +389,17 @@ export const AgentChangeSchema = GitChangeSchema.extend({
|
|
|
389
389
|
),
|
|
390
390
|
});
|
|
391
391
|
export type AgentChange = z.infer<typeof AgentChangeSchema>;
|
|
392
|
+
// One manifest's new dependency names: the file at the delta's anchor against the file as it stands now, so a review
|
|
393
|
+
// can say what the project takes on before it lands.
|
|
394
|
+
export const AddedDependenciesSchema = z.object({
|
|
395
|
+
path: z.string().describe("The manifest, relative to the repository root, such as video/package.json."),
|
|
396
|
+
added: z
|
|
397
|
+
.array(z.string())
|
|
398
|
+
.describe(
|
|
399
|
+
"The names it declares now and did not declare before, sorted. Only new names: a version bump of a dependency already there is not listed.",
|
|
400
|
+
),
|
|
401
|
+
});
|
|
402
|
+
export type AddedDependencies = z.infer<typeof AddedDependenciesSchema>;
|
|
392
403
|
// An agent worktree's delta vs its base, deliberately not RepoChanges: no index here, so sharing that shape would force
|
|
393
404
|
// an empty `staged` and a staging affordance that cannot work on a worktree nobody checks out.
|
|
394
405
|
export const AgentRepoChangesSchema = z.object({
|
|
@@ -401,6 +412,13 @@ export const AgentRepoChangesSchema = z.object({
|
|
|
401
412
|
.describe(
|
|
402
413
|
"The packages of the tree these changes came from, so a review can group by package. Carried with the changes rather than looked up separately, because a package the conversation has just created exists only in its own copy and the shared tree has never heard of it.",
|
|
403
414
|
),
|
|
415
|
+
// Absent rather than empty when nothing was added, the common case.
|
|
416
|
+
addedDependencies: z
|
|
417
|
+
.array(AddedDependenciesSchema)
|
|
418
|
+
.optional()
|
|
419
|
+
.describe(
|
|
420
|
+
"Dependencies the changed manifests here declare that they did not before (package.json, pyproject.toml, requirements.txt), one entry per manifest that gained any. The review is where a new dependency is approved: a conversation installs freely in its own copy, and this is what the project takes on if the work lands. Absent when none was added.",
|
|
421
|
+
),
|
|
404
422
|
});
|
|
405
423
|
export type AgentRepoChanges = z.infer<typeof AgentRepoChangesSchema>;
|
|
406
424
|
// The review, plus why the last land refused: a conflict is found when the turn ends but resolved later on this
|
package/src/schemas/hosts.ts
CHANGED
|
@@ -70,11 +70,14 @@ export type DeviceFacts = z.infer<typeof DeviceFactsSchema>;
|
|
|
70
70
|
// Both features ride that verb, so an agent advertises both or neither.
|
|
71
71
|
// - `rollback-to`: the `rollback` op's `to`, going back to an older version kept on the machine rather than the
|
|
72
72
|
// previous one; advertised only when the agent's `ic sandbox rollback` takes `--to`.
|
|
73
|
-
|
|
73
|
+
// - `loopback-catch`: the `catchLoopback` procedure, a sign-in's loopback redirect caught on this machine
|
|
74
|
+
// (loopback-catch.ts); an agent without it rejects the call as an unknown procedure.
|
|
75
|
+
export const DeviceFeatureSchema = z.enum(["reshape-later", "set-shape", "rollback-to", "loopback-catch"]);
|
|
74
76
|
export type DeviceFeature = z.infer<typeof DeviceFeatureSchema>;
|
|
75
77
|
export const DEVICE_FEATURE_RESHAPE_LATER: DeviceFeature = "reshape-later";
|
|
76
78
|
export const DEVICE_FEATURE_SET_SHAPE: DeviceFeature = "set-shape";
|
|
77
79
|
export const DEVICE_FEATURE_ROLLBACK_TO: DeviceFeature = "rollback-to";
|
|
80
|
+
export const DEVICE_FEATURE_LOOPBACK_CATCH: DeviceFeature = "loopback-catch";
|
|
78
81
|
// The features an agent advertised that this build knows; an unknown one is a newer agent's and means nothing here.
|
|
79
82
|
export const deviceFeatures = (facts: Pick<DeviceFacts, "features"> | undefined): DeviceFeature[] =>
|
|
80
83
|
(facts?.features ?? []).flatMap((feature) => {
|
package/src/schemas/keep-warm.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// clock lives here; how many refreshes a hold may spend is priced by the provider that serves it (runtimes/claude).
|
|
3
3
|
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import type { SandboxNotice } from "../events/sandbox-notice.js";
|
|
5
6
|
|
|
6
7
|
// A cache clock as the daemon publishes it: the last request that touched the entry, and the entry's lifetime.
|
|
7
8
|
export interface CacheClock {
|
|
@@ -95,16 +96,28 @@ const spanLabel = (ms: number): string => {
|
|
|
95
96
|
return hours === 0 ? `${minutes}m` : `${hours}h ${minutes % 60}m`;
|
|
96
97
|
};
|
|
97
98
|
|
|
98
|
-
/** The
|
|
99
|
-
export const
|
|
99
|
+
/** The notice for a turn that picked up a conversation kept warm for it, by code and facts; undefined for any other turn. */
|
|
100
|
+
export const keptWarmNotice = (opening: PromptCacheOpening): Extract<SandboxNotice, { code: "keptWarm" | "keptCold" }> | undefined => {
|
|
100
101
|
const { kept } = opening;
|
|
101
102
|
if (kept === undefined) {
|
|
102
103
|
return undefined;
|
|
103
104
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
const warm = opening.readTokens >= opening.writtenTokens;
|
|
106
|
+
const params = { tokens: tokensLabel(warm ? opening.readTokens : opening.writtenTokens), span: spanLabel(kept.forMs), refreshes: kept.refreshes };
|
|
107
|
+
return warm ? { code: "keptWarm", params } : { code: "keptCold", params };
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** The transcript's line for a turn that picked up a conversation kept warm for it; undefined for any other turn. */
|
|
111
|
+
export const keptWarmLine = (opening: PromptCacheOpening): string | undefined => {
|
|
112
|
+
const notice = keptWarmNotice(opening);
|
|
113
|
+
if (notice === undefined) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const { tokens, span, refreshes } = notice.params;
|
|
117
|
+
const held = `kept warm for ${span} with ${refreshes} ${refreshes === 1 ? "refresh" : "refreshes"}`;
|
|
118
|
+
return notice.code === "keptWarm"
|
|
119
|
+
? `Picked up warm: ${tokens} tokens read from the cache, ${held}.`
|
|
120
|
+
: `Picked up cold despite being ${held}: ${tokens} tokens sent again, because this turn's prompt no longer matched the cache.`;
|
|
108
121
|
};
|
|
109
122
|
|
|
110
123
|
// A turn's prefix, part by part, so two turns that should share a cache can say which part stopped matching.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Loopback catch: how a sign-in whose redirect lands on http://localhost:<port>/… finishes without a paste. The daemon
|
|
2
|
+
// is not on the machine the browser is on, so something that is (a device agent, the browser extension) watches that
|
|
3
|
+
// one address for the attempt's lifetime and hands back where the browser landed. Both peers answer the same
|
|
4
|
+
// procedure (`catchLoopback`) with the same frames, so the daemon drives them identically.
|
|
5
|
+
// Named imports rather than the `z` namespace: the extension bundles this module (see webext.ts).
|
|
6
|
+
import { discriminatedUnion, enum as zEnum, literal, number, object, string } from "zod";
|
|
7
|
+
import type * as z from "zod";
|
|
8
|
+
|
|
9
|
+
// What one catcher is asked to watch. The state is deliberately absent: the daemon checks it on what comes back, so a
|
|
10
|
+
// catcher can hold nothing that tells a real landing from a forged one, and needs nothing to.
|
|
11
|
+
export const LoopbackCatchSchema = object({
|
|
12
|
+
// The attempt's own id, for logs on both ends; not redeemable.
|
|
13
|
+
id: string().min(1),
|
|
14
|
+
// The host the provider redirects to, spelled as its registration spells it: some are registered as
|
|
15
|
+
// `localhost`, some as `127.0.0.1`, and a browser treats the two as different origins.
|
|
16
|
+
host: zEnum(["localhost", "127.0.0.1"]),
|
|
17
|
+
port: number().int().min(1).max(65535),
|
|
18
|
+
// The path only, leading slash included; the query is the grant and is never matched.
|
|
19
|
+
path: string().startsWith("/"),
|
|
20
|
+
// Epoch ms; a catcher stops watching by itself here even if nobody aborts the stream.
|
|
21
|
+
expiresAt: number(),
|
|
22
|
+
// Who the page thanks once the browser lands ("Claude"), so the tab says what just happened.
|
|
23
|
+
title: string().min(1),
|
|
24
|
+
}).strict();
|
|
25
|
+
export type LoopbackCatch = z.infer<typeof LoopbackCatchSchema>;
|
|
26
|
+
|
|
27
|
+
// What a catcher streams back. `busy` ends its part (the port is taken on that machine, or it cannot watch at all);
|
|
28
|
+
// `landed` may come more than once, since anything on the machine can hit a loopback port and only the daemon can
|
|
29
|
+
// tell which hit is the sign-in.
|
|
30
|
+
export const LoopbackCatchEventSchema = discriminatedUnion("type", [
|
|
31
|
+
object({ type: literal("listening") }),
|
|
32
|
+
object({ type: literal("busy"), reason: string() }),
|
|
33
|
+
object({ type: literal("landed"), url: string().min(1) }),
|
|
34
|
+
]);
|
|
35
|
+
export type LoopbackCatchEvent = z.infer<typeof LoopbackCatchEventSchema>;
|
|
36
|
+
|
|
37
|
+
// Advertised in a device's `features` and a browser's facts; only a peer that says it is asked.
|
|
38
|
+
export const LOOPBACK_CATCH_FEATURE = "loopback-catch";
|
|
39
|
+
|
|
40
|
+
// Who is watching for this attempt's landing, for the card to say "finishes by itself on rog".
|
|
41
|
+
export const SignInCatcherSchema = object({
|
|
42
|
+
kind: zEnum(["device", "browser"]),
|
|
43
|
+
label: string().min(1),
|
|
44
|
+
});
|
|
45
|
+
export type SignInCatcher = z.infer<typeof SignInCatcherSchema>;
|
|
@@ -137,7 +137,7 @@ export const MutationScoreSchema = z.object({
|
|
|
137
137
|
.number()
|
|
138
138
|
.describe("The share of injected faults the suite caught. Not a coverage figure: coverage says a line ran, this says an assertion depended on it."),
|
|
139
139
|
killed: z.number().int().nonnegative().describe("Faults the suite caught."),
|
|
140
|
-
survived: z.number().int().nonnegative().describe("Faults it did not: code that can be broken with every test still
|
|
140
|
+
survived: z.number().int().nonnegative().describe("Faults it did not: code that can be broken with every test still passing."),
|
|
141
141
|
// A timeout counts as detected here (Stryker's own convention), not as inconclusive.
|
|
142
142
|
inconclusive: z
|
|
143
143
|
.number()
|
package/src/schemas/needs.ts
CHANGED
|
@@ -256,6 +256,12 @@ export const ConversationGrantSchema = z.object({
|
|
|
256
256
|
capabilities: z.array(z.string()).default([]).describe("Connected capabilities it may use although its persona leaves them out."),
|
|
257
257
|
folders: z.array(z.string()).default([]).describe("Workspace folders its file tools may touch beyond its fence."),
|
|
258
258
|
shelves: z.array(GrantShelfSchema).default([]).describe("Shelves of tools opened for it."),
|
|
259
|
+
// Answered on an install card's "Allow installs for this conversation", read live by the command gate, so it holds
|
|
260
|
+
// from the install that asked onward, this turn included, and across restarts.
|
|
261
|
+
installs: z
|
|
262
|
+
.boolean()
|
|
263
|
+
.default(false)
|
|
264
|
+
.describe("Whether its own dependency installs run without asking, where the owner's setting would otherwise ask first."),
|
|
259
265
|
updatedAt: z.number().describe("When it last changed, in milliseconds."),
|
|
260
266
|
by: z.string().optional().describe("Who last allowed something here."),
|
|
261
267
|
});
|
|
@@ -270,11 +276,12 @@ export const StandingGrantsSchema = z.object({
|
|
|
270
276
|
capabilities: z.array(z.string()).describe("Connected capabilities allowed although its persona leaves them out."),
|
|
271
277
|
folders: z.array(z.string()).describe("Workspace folders its file tools may touch beyond its fence."),
|
|
272
278
|
shelves: z.array(GrantShelfSchema).describe("Shelves of tools opened for it."),
|
|
279
|
+
installs: z.boolean().default(false).describe("Whether its own dependency installs run without asking."),
|
|
273
280
|
by: z.string().optional().describe("Who last allowed one of those."),
|
|
274
281
|
updatedAt: z.number().optional().describe("When one of those last changed, in milliseconds."),
|
|
275
282
|
releases: z
|
|
276
283
|
.array(z.object({ subject: z.string(), approvedBy: z.string(), at: z.number() }))
|
|
277
|
-
.describe("Gated credentials released to it, until
|
|
284
|
+
.describe("Gated credentials released to it, until somebody takes one back."),
|
|
278
285
|
}),
|
|
279
286
|
),
|
|
280
287
|
});
|
|
@@ -282,8 +289,10 @@ export type StandingGrants = z.infer<typeof StandingGrantsSchema>;
|
|
|
282
289
|
|
|
283
290
|
export const GrantRevokeSchema = z.object({
|
|
284
291
|
conversationId: z.string(),
|
|
285
|
-
kind: z
|
|
286
|
-
|
|
292
|
+
kind: z
|
|
293
|
+
.enum(["capability", "folder", "shelf", "release", "install"])
|
|
294
|
+
.describe("Which kind of yes: a grant past the persona or area, a credential's release, or letting its installs run unasked."),
|
|
295
|
+
what: z.string().describe("The capability id, folder, shelf or released credential it named; empty for installs."),
|
|
287
296
|
});
|
|
288
297
|
export type GrantRevoke = z.infer<typeof GrantRevokeSchema>;
|
|
289
298
|
|
package/src/schemas/personas.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { type ModelSource, readyChain } from "../models/model-pins.js";
|
|
5
5
|
import { type ModelPin, ModelPinSchema } from "./agent.js";
|
|
6
6
|
import { entryId } from "./internal.js";
|
|
7
|
-
import { SkillDraftSchema, SkillNameSchema, SystemPromptModeSchema } from "./settings.js";
|
|
7
|
+
import { SkillDraftSchema, SkillNameSchema, SYSTEM_PROMPT_MAX, SystemPromptModeSchema } from "./settings.js";
|
|
8
8
|
// A named face the sandbox shows outward: who it speaks as, what it may do, where it works, what it's told, what it
|
|
9
9
|
// runs on. No credential lives on the card; accounts stay private, per-sandbox. Not a security boundary for a watched
|
|
10
10
|
// chat — a real fence only for an unattended turn, whose resolver defaults to nothing.
|
|
@@ -176,7 +176,12 @@ export interface TurnBriefingFixture {
|
|
|
176
176
|
export const TURN_BRIEFING_FIXTURES: readonly TurnBriefingFixture[] = [
|
|
177
177
|
{ label: "Where this turn's files live", why: "Without it a runtime that is only cwd'd into its branch writes into the shared checkout." },
|
|
178
178
|
{ label: "Who this turn is acting as", why: "The card's own identity, on the runtimes that have nowhere else to put it." },
|
|
179
|
-
|
|
179
|
+
// Kept under a card's own system prompt too (system-prompt.ts `ownPrompt`): writing a prompt withdraws this product's
|
|
180
|
+
// guidance, never the owner's own rules.
|
|
181
|
+
{
|
|
182
|
+
label: "Standing instructions for this workspace",
|
|
183
|
+
why: "Your own AGENTS.md rules, sent even when this card has its own system prompt. Change them in AGENTS.md.",
|
|
184
|
+
},
|
|
180
185
|
{
|
|
181
186
|
label: "Some connected accounts need a person's approval",
|
|
182
187
|
why: "Names who to ask; without it a withheld account reads as simply not connected.",
|
|
@@ -307,7 +312,7 @@ export type PersonaKit = z.infer<typeof PersonaKitSchema>;
|
|
|
307
312
|
export const PersonaPromptSchema = PersonaIdParamSchema.extend({
|
|
308
313
|
prompt: z
|
|
309
314
|
.string()
|
|
310
|
-
.max(
|
|
315
|
+
.max(SYSTEM_PROMPT_MAX)
|
|
311
316
|
.describe(
|
|
312
317
|
"What to tell this persona. Sending an empty one removes it entirely rather than storing a blank, so the persona falls back to the sandbox's own instructions.",
|
|
313
318
|
),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AccountStateSchema, AccountUsageSchema } from "./plan-limits.js";
|
|
3
|
+
import { SignInCatcherSchema } from "../loopback-catch.js";
|
|
3
4
|
import { KeyedProviderSchema } from "./provider-subscriptions.js";
|
|
4
5
|
// Claude uses PKCE paste-back (start → exchange); Codex uses OpenAI's device-code flow (start → poll). A sandbox can
|
|
5
6
|
// hold several accounts per provider; `id` is the store key, `label` the display name. Tokens never ride this shape —
|
|
@@ -91,6 +92,12 @@ export const LoginStartSchema = z.object({
|
|
|
91
92
|
"This attempt's id, for finishing or abandoning it. Not a credential and not redeemable: the proof that completes the sign-in never leaves the sandbox.",
|
|
92
93
|
),
|
|
93
94
|
expiresAt: z.number().describe("When this attempt stops being answerable, in milliseconds, so a card can stop waiting instead of spinning."),
|
|
95
|
+
catchers: z
|
|
96
|
+
.array(SignInCatcherSchema)
|
|
97
|
+
.optional()
|
|
98
|
+
.describe(
|
|
99
|
+
"Who is watching for where the browser lands, on the machine it is on: a device or a browser of yours. Listed means the sign-in finishes by itself once the page is approved there; the paste stays open for a browser anywhere else. Empty or absent means nothing is watching.",
|
|
100
|
+
),
|
|
94
101
|
});
|
|
95
102
|
export type LoginStart = z.infer<typeof LoginStartSchema>;
|
|
96
103
|
// e.g. Z.ai's international vs. mainland plans.
|
|
@@ -111,6 +118,18 @@ export const LoginCompletedSchema = z.object({
|
|
|
111
118
|
"The account it connected, where the sign-in ends here. Absent means keep watching the account list.",
|
|
112
119
|
),
|
|
113
120
|
});
|
|
121
|
+
// One attempt's standing, for a card to watch the attempt itself rather than the account list: a list that already
|
|
122
|
+
// held an account says nothing about whether this sign-in, adding another, has landed.
|
|
123
|
+
export const LoginStatusSchema = z.discriminatedUnion("status", [
|
|
124
|
+
z.object({ status: z.literal("wait") }),
|
|
125
|
+
z.object({
|
|
126
|
+
status: z.literal("ok"),
|
|
127
|
+
account: OauthAccountSchema.optional().describe("The account it connected. Absent where the row lands in the account list a little later."),
|
|
128
|
+
}),
|
|
129
|
+
z.object({ status: z.literal("error"), error: z.string().min(1).describe("Why it failed, to show as it is.") }),
|
|
130
|
+
]);
|
|
131
|
+
export type LoginStatus = z.infer<typeof LoginStatusSchema>;
|
|
132
|
+
export const LoginStatusQuerySchema = z.object({ handshake: z.string().min(1).describe("Which attempt.") });
|
|
114
133
|
// Tidiness, not a security boundary: an unanswered attempt also times out on its own (`expiresAt`).
|
|
115
134
|
export const LoginCancelSchema = z.object({ handshake: z.string().min(1).describe("Which attempt to stop waiting on.") });
|
|
116
135
|
// codex/grok/kimi/gemini via CLIProxyAPI; `flow` is explicit even when a provider's URL already embeds an optional
|
|
@@ -124,6 +143,12 @@ export const TranslatorStartSchema = z.object({
|
|
|
124
143
|
.describe(
|
|
125
144
|
"Which shape this is. A device sign-in finishes by itself and you poll the attempt; a redirect needs the address it landed on handed back. Said outright rather than guessed at from whether a code happens to exist.",
|
|
126
145
|
),
|
|
146
|
+
catchers: z
|
|
147
|
+
.array(SignInCatcherSchema)
|
|
148
|
+
.optional()
|
|
149
|
+
.describe(
|
|
150
|
+
"Who is watching for where the browser lands, on the machine it is on: a device or a browser of yours. Listed means the sign-in finishes by itself once the page is approved there; the paste stays open for a browser anywhere else. Empty or absent means nothing is watching.",
|
|
151
|
+
),
|
|
127
152
|
});
|
|
128
153
|
export const TranslatorStatusSchema = z.discriminatedUnion("status", [
|
|
129
154
|
z.object({ status: z.literal("wait") }),
|
|
@@ -142,6 +167,13 @@ export const TranslatorCompleteSchema = z.object({
|
|
|
142
167
|
// Order is meaningful: the provider's own preference order, never re-ranked.
|
|
143
168
|
export const ModelBadgeSchema = z.enum(["reasoning", "fast"]);
|
|
144
169
|
export type ModelBadge = z.infer<typeof ModelBadgeSchema>;
|
|
170
|
+
// Why a model may run one-shot helper jobs (a commit message, a title) and never an agent turn. Only a positive answer
|
|
171
|
+
// sets it, so a server that says nothing leaves a model usable:
|
|
172
|
+
// no-tool-calls: its server reports a chat template that cannot call tools (llama.cpp's /props `chat_template_caps`),
|
|
173
|
+
// and llama.cpp drops a turn's tools without a word, so the turn would run and do nothing useful
|
|
174
|
+
// instant-tier: the curated local model sold as too small to drive a turn (LOCAL_MODELS' `instant` rung)
|
|
175
|
+
export const HelperOnlySchema = z.enum(["no-tool-calls", "instant-tier"]);
|
|
176
|
+
export type HelperOnly = z.infer<typeof HelperOnlySchema>;
|
|
145
177
|
export const ModelSchema = z.object({
|
|
146
178
|
id: z.string().describe("What to name when asking for this model."),
|
|
147
179
|
label: z.string().describe("What to call it on screen."),
|
|
@@ -156,6 +188,9 @@ export const ModelSchema = z.object({
|
|
|
156
188
|
// The served window, not the training length — memory can clamp it lower. Turns are refused against this. Absent
|
|
157
189
|
// means unknown, never unlimited.
|
|
158
190
|
contextWindow: z.number().optional().describe("How many tokens this model will accept in one request, where the server publishes it."),
|
|
191
|
+
helperOnly: HelperOnlySchema.optional().describe(
|
|
192
|
+
"Set where this model may write commit messages, titles and other one-shot jobs but never run a chat turn, and why: its server says it cannot call tools, or it is the small local model kept for quick jobs. Absent means nothing has said it cannot.",
|
|
193
|
+
),
|
|
159
194
|
// Epoch seconds. A fact about the MODEL, not about any one account: a routed provider balances across credentials,
|
|
160
195
|
// so a model every one of them is benched on is unrunnable however much headroom their rings show. Absent is the
|
|
161
196
|
// ordinary case and means runnable, never "unknown".
|
package/src/schemas/secrets.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// secrets: user-supplied env-var secrets the daemon writes to desired-state/.env
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { SECRET_HOST_MAX, SECRET_HOST_PATTERN_RE, SECRET_HOSTS_MAX } from "../policy/secret-hosts.js";
|
|
3
4
|
// Straight to the sandbox daemon, never through the platform; `apply` reloads .env with no restart. `list` returns keys
|
|
4
5
|
// only; `reveal` is the one owner-only exception that returns a value.
|
|
5
6
|
// An env var name, as a shell exports it. Exported so the browser validates against this rather than restating it:
|
|
@@ -104,6 +105,66 @@ export const CredentialGrantSchema = z.object({
|
|
|
104
105
|
message: z.string().describe("What the grant means in practice, and what to do next."),
|
|
105
106
|
});
|
|
106
107
|
|
|
108
|
+
// The host guard: the second way a secret can need a person, beside a named approver. On, a use goes unasked only when
|
|
109
|
+
// every host its text names is on the secret's list; one aimed anywhere else, or anywhere its text does not show, waits
|
|
110
|
+
// for a person's click, whatever the safety judge says and whether or not it could be asked. Off, the guard never asks.
|
|
111
|
+
// Keyed like a gate: a secret by its name, a connected capability by its id, covering every field of it.
|
|
112
|
+
export const SecretHostSchema = z
|
|
113
|
+
.string()
|
|
114
|
+
.max(SECRET_HOST_MAX)
|
|
115
|
+
.regex(SECRET_HOST_PATTERN_RE)
|
|
116
|
+
.describe(
|
|
117
|
+
"One host, `api.github.com`, or every host under a domain, `*.github.com` (which does not include github.com itself). Lowercase, no scheme or port.",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
export const SecretHostSourceSchema = z
|
|
121
|
+
.enum(["owner", "connector"])
|
|
122
|
+
.describe(
|
|
123
|
+
"Who set it: the owner, or the connector the credential belongs to, whose guard is on with its own service's hosts until the owner changes it.",
|
|
124
|
+
);
|
|
125
|
+
export type SecretHostSource = z.infer<typeof SecretHostSourceSchema>;
|
|
126
|
+
|
|
127
|
+
export const SecretHostGuardSchema = z.object({
|
|
128
|
+
subject: z.string().min(1).describe("Which secret, by the name its reference carries, or which connected capability, by its id."),
|
|
129
|
+
kind: CredentialGateKindSchema,
|
|
130
|
+
guard: z.boolean().describe("On: a use off the list, or whose destination cannot be read, asks a person first. Off: it never asks."),
|
|
131
|
+
hosts: z
|
|
132
|
+
.array(SecretHostSchema)
|
|
133
|
+
.max(SECRET_HOSTS_MAX)
|
|
134
|
+
.describe("Where it goes without asking while the guard is on. Empty with the guard on: every use asks. Kept while it is off, for turning it back on."),
|
|
135
|
+
source: SecretHostSourceSchema,
|
|
136
|
+
});
|
|
137
|
+
export type SecretHostGuard = z.infer<typeof SecretHostGuardSchema>;
|
|
138
|
+
|
|
139
|
+
export const SecretHostGuardsSchema = z.object({
|
|
140
|
+
guards: z
|
|
141
|
+
.array(SecretHostGuardSchema)
|
|
142
|
+
.describe("Every secret whose host guard has been set, or that a connector guards by default. One not listed has its guard off. Names and hosts only, never a value."),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
export const SecretHostGuardSetSchema = z.object({
|
|
146
|
+
subject: z.string().min(1).describe("Which secret, by name, or which connected capability, by id."),
|
|
147
|
+
kind: CredentialGateKindSchema.optional().describe("Whether the subject is a secret or a capability. Worked out from the name when absent."),
|
|
148
|
+
guard: z.boolean().describe("Whether a use off the list, or whose destination cannot be read, must ask a person first."),
|
|
149
|
+
hosts: z
|
|
150
|
+
.array(SecretHostSchema)
|
|
151
|
+
.max(SECRET_HOSTS_MAX)
|
|
152
|
+
.describe(
|
|
153
|
+
"The whole new list. Turning the guard on or taking hosts away is open to anybody who may use secrets; turning it off or adding a host is the owner's to approve.",
|
|
154
|
+
),
|
|
155
|
+
// Filled by the CLI itself, so the model cannot aim the owner's card at somebody else's conversation.
|
|
156
|
+
conversationId: z
|
|
157
|
+
.string()
|
|
158
|
+
.optional()
|
|
159
|
+
.describe("Which conversation to ask the owner in, when the change needs them. The CLI fills this from the running turn."),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export const SecretHostGuardSetResultSchema = z.object({
|
|
163
|
+
guard: z.boolean().describe("Whether the guard is on now."),
|
|
164
|
+
hosts: z.array(z.string()).describe("Where it goes without asking while the guard is on."),
|
|
165
|
+
approvedBy: z.string().optional().describe("Who approved the change, when it needed the owner's click. Absent when nobody had to."),
|
|
166
|
+
});
|
|
167
|
+
|
|
107
168
|
// Across every store: env/generated secrets, capability credentials, AI-provider accounts. Values never ride this
|
|
108
169
|
// shape; `revealable` says whether `reveal` can return one (everything but provider accounts).
|
|
109
170
|
export const SecretInventoryEntrySchema = z.object({
|
|
@@ -151,6 +212,15 @@ export const SecretInventoryEntrySchema = z.object({
|
|
|
151
212
|
})
|
|
152
213
|
.optional()
|
|
153
214
|
.describe("Who has to release this before the agent can use it, and for how long one release lasts. Absent when it is not gated."),
|
|
215
|
+
// Joined from the host guards, the owner's and a connector's own, so the row can say where it may go.
|
|
216
|
+
hosts: z
|
|
217
|
+
.object({
|
|
218
|
+
guard: z.boolean().describe("Whether a use off the list, or whose destination cannot be read, asks a person first."),
|
|
219
|
+
list: z.array(z.string()).describe("The hosts it goes to unasked while the guard is on, each exact or `*.domain`."),
|
|
220
|
+
source: SecretHostSourceSchema,
|
|
221
|
+
})
|
|
222
|
+
.optional()
|
|
223
|
+
.describe("Its host guard. Absent when none was ever set and no connector sets one: the guard is off."),
|
|
154
224
|
});
|
|
155
225
|
export type SecretInventoryEntry = z.infer<typeof SecretInventoryEntrySchema>;
|
|
156
226
|
export const SecretInventorySchema = z.object({
|
|
@@ -85,6 +85,7 @@ export const SETTINGS_HISTORY = [
|
|
|
85
85
|
"quickModel",
|
|
86
86
|
"resumeAfterLimit",
|
|
87
87
|
"resumeAfterOutage",
|
|
88
|
+
"sidecars",
|
|
88
89
|
"terseHoldout",
|
|
89
90
|
"terseOutput",
|
|
90
91
|
"testFaultDetection",
|
|
@@ -107,4 +108,6 @@ export const SETTINGS_HISTORY = [
|
|
|
107
108
|
keepWarmObject,
|
|
108
109
|
// Where the check after landing ran (2026-09-27): nothing checks work after it lands any more, CI does.
|
|
109
110
|
at("offload", drop("landCheck")),
|
|
111
|
+
// The model list for the fix a refused push offered (2026-09-28): a refused push now says why and offers nothing.
|
|
112
|
+
at("modelRoles", drop("pre-push-fix")),
|
|
110
113
|
] as const;
|
|
@@ -23,7 +23,7 @@ describe(`which built-in stands at which moment`, () => {
|
|
|
23
23
|
expect(rule(`agent.landed`, { kind: `command`, command: `pnpm test` }).success).toBe(false);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
test(`there is no push moment: the
|
|
26
|
+
test(`there is no push moment: nothing runs on the way to a push, CI checks what was pushed`, () => {
|
|
27
27
|
expect(rule(`push.starting`, { kind: `command`, command: `pnpm test` }).success).toBe(false);
|
|
28
28
|
});
|
|
29
29
|
});
|
|
@@ -111,3 +111,21 @@ describe(`the folded keep-warm settings`, () => {
|
|
|
111
111
|
expect(convertDocument(SETTINGS_HISTORY, `object`, current, true).changes).toEqual([]);
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
|
+
|
|
115
|
+
// The pre-push fix's model list went with the fix a refused push used to offer (2026-09-28). A settings file that still
|
|
116
|
+
// names it must read, since an unknown role refuses the whole file, and every other job's list stays as its owner set it.
|
|
117
|
+
describe(`the retired pre-push fix role`, () => {
|
|
118
|
+
const pin = { provider: `claude`, model: `claude-opus-5-5` };
|
|
119
|
+
|
|
120
|
+
test(`is dropped from a stored file, leaving every other job's list, and the file reads`, () => {
|
|
121
|
+
const stored = { modelRoles: { "pipeline-fix": [pin], "pre-push-fix": [pin] } };
|
|
122
|
+
expect(SandboxSettingsSchema.safeParse(stored).success).toBe(false);
|
|
123
|
+
const converted = convertDocument(SETTINGS_HISTORY, `object`, stored, true);
|
|
124
|
+
expect(converted.value).toEqual({ modelRoles: { "pipeline-fix": [pin] } });
|
|
125
|
+
expect(SandboxSettingsSchema.parse(converted.value).modelRoles).toEqual({ "pipeline-fix": [pin] });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test(`leaves a file without it alone`, () => {
|
|
129
|
+
expect(convertDocument(SETTINGS_HISTORY, `object`, { modelRoles: { "pipeline-fix": [pin] } }, true).changes).toEqual([]);
|
|
130
|
+
});
|
|
131
|
+
});
|