@intentic/sandbox-contract 1.328.0 → 1.330.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/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +1 -2
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +69 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +8 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +42 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +3 -0
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/runs.contract.d.ts +35 -0
- package/dist/contracts/runs.contract.d.ts.map +1 -0
- package/dist/contracts/runs.contract.js +22 -0
- package/dist/contracts/runs.contract.js.map +1 -0
- package/dist/contracts/settings.contract.d.ts +8 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +42 -30
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +10 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +37 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +11 -1
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/system-events.d.ts +8 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/index.d.ts +204 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol/doors/listener-protocol.d.ts +0 -1
- package/dist/protocol/doors/listener-protocol.d.ts.map +1 -1
- package/dist/protocol/doors/listener-protocol.js +3 -2
- package/dist/protocol/doors/listener-protocol.js.map +1 -1
- package/dist/protocol/raw/raw-routes.d.ts +29 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +8 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/schemas/agents-list.d.ts +4 -0
- package/dist/schemas/agents-list.d.ts.map +1 -1
- package/dist/schemas/agents.d.ts +13 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +4 -3
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/capabilities.d.ts +12 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +1 -0
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/device-artifacts.d.ts +67 -0
- package/dist/schemas/device-artifacts.d.ts.map +1 -0
- package/dist/schemas/device-artifacts.js +61 -0
- package/dist/schemas/device-artifacts.js.map +1 -0
- 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/run-targets.d.ts +82 -0
- package/dist/schemas/run-targets.d.ts.map +1 -0
- package/dist/schemas/run-targets.js +49 -0
- package/dist/schemas/run-targets.js.map +1 -0
- package/dist/schemas/settings.d.ts +8 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +2 -1
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/speech.d.ts +75 -0
- package/dist/schemas/speech.d.ts.map +1 -0
- package/dist/schemas/speech.js +66 -0
- package/dist/schemas/speech.js.map +1 -0
- package/dist/schemas/terminal.d.ts +4 -0
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js +4 -0
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/turn-break.d.ts +7 -1
- package/dist/schemas/turn-break.d.ts.map +1 -1
- package/dist/schemas/turn-break.js +4 -2
- package/dist/schemas/turn-break.js.map +1 -1
- package/dist/schemas/workspace/workspace-graph.d.ts +133 -0
- package/dist/schemas/workspace/workspace-graph.d.ts.map +1 -0
- package/dist/schemas/workspace/workspace-graph.js +83 -0
- package/dist/schemas/workspace/workspace-graph.js.map +1 -0
- package/dist/schemas/workspace/workspace-repos.d.ts +0 -39
- package/dist/schemas/workspace/workspace-repos.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-repos.js +0 -15
- package/dist/schemas/workspace/workspace-repos.js.map +1 -1
- package/dist/state/definition.d.ts +12 -0
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/workspace-state.d.ts +2 -1
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +5 -1
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/transcript-page.d.ts +5 -0
- package/dist/text/transcript-page.d.ts.map +1 -0
- package/dist/text/transcript-page.js +17 -0
- package/dist/text/transcript-page.js.map +1 -0
- package/package.json +5 -5
- package/src/chores/probes.ts +1 -2
- package/src/contracts/device.contract.ts +10 -0
- package/src/contracts/runs.contract.ts +26 -0
- package/src/contracts/system.contract.ts +13 -0
- package/src/contracts/workspace.contract.ts +12 -1
- package/src/index.ts +8 -1
- package/src/models/agent-runtimes.ts +2 -2
- package/src/protocol/doors/listener-protocol.ts +5 -4
- package/src/protocol/raw/raw-routes.ts +15 -0
- package/src/schemas/agents.ts +6 -3
- package/src/schemas/capabilities.ts +4 -0
- package/src/schemas/device-artifacts.ts +99 -0
- package/src/schemas/hosts.ts +4 -1
- package/src/schemas/run-targets.ts +93 -0
- package/src/schemas/settings.ts +6 -1
- package/src/schemas/speech.ts +96 -0
- package/src/schemas/terminal.ts +4 -0
- package/src/schemas/turn-break.ts +18 -9
- package/src/schemas/workspace/workspace-graph.ts +116 -0
- package/src/schemas/workspace/workspace-repos.ts +0 -22
- package/src/state/workspace-state.test.ts +42 -1
- package/src/state/workspace-state.ts +12 -1
- package/src/text/transcript-page.test.ts +39 -0
- package/src/text/transcript-page.ts +37 -0
- package/dist/text/whisper.d.ts +0 -3
- package/dist/text/whisper.d.ts.map +0 -1
- package/dist/text/whisper.js +0 -11
- package/dist/text/whisper.js.map +0 -1
- package/src/text/whisper.test.ts +0 -18
- package/src/text/whisper.ts +0 -17
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { loopsContract } from "./contracts/loops.contract.js";
|
|
|
28
28
|
import { pagesContract } from "./contracts/pages.contract.js";
|
|
29
29
|
import { panelsContract } from "./contracts/panels.contract.js";
|
|
30
30
|
import { portsContract } from "./contracts/ports.contract.js";
|
|
31
|
+
import { runsContract } from "./contracts/runs.contract.js";
|
|
31
32
|
import { publicContract } from "./contracts/public.contract.js";
|
|
32
33
|
import { netdiskContract } from "./contracts/netdisk.contract.js";
|
|
33
34
|
import { providersContract } from "./contracts/providers.contract.js";
|
|
@@ -96,6 +97,7 @@ export * from "./protocol/vitals.js";
|
|
|
96
97
|
export { loopsContract } from "./contracts/loops.contract.js";
|
|
97
98
|
export { panelsContract } from "./contracts/panels.contract.js";
|
|
98
99
|
export { portsContract } from "./contracts/ports.contract.js";
|
|
100
|
+
export { runsContract } from "./contracts/runs.contract.js";
|
|
99
101
|
export { pagesContract, PageAppCallResultSchema, PageAppCallSchema, PageErrorsResultSchema, PageErrorsSchema } from "./contracts/pages.contract.js";
|
|
100
102
|
export { publicContract } from "./contracts/public.contract.js";
|
|
101
103
|
export { providersContract, type RunnableProviders, RunnableProvidersSchema } from "./contracts/providers.contract.js";
|
|
@@ -135,6 +137,7 @@ export * from "./events/child-run.js";
|
|
|
135
137
|
export * from "./policy/turned-away.js";
|
|
136
138
|
export * from "./text/mentions.js";
|
|
137
139
|
export * from "./text/pages.js";
|
|
140
|
+
export * from "./text/transcript-page.js";
|
|
138
141
|
export * from "./protocol/sse.js";
|
|
139
142
|
export * from "./protocol/routes.js";
|
|
140
143
|
export { type ControlReach, defaultFloor, type RouteAccess, type RouteMeta, routeAccess } from "./protocol/route-meta.js";
|
|
@@ -200,6 +203,7 @@ export * from "./policy/fence-paths.js";
|
|
|
200
203
|
export * from "./policy/persona-home.js";
|
|
201
204
|
export * from "./schemas/agents.js";
|
|
202
205
|
export * from "./schemas/speaker.js";
|
|
206
|
+
export * from "./schemas/speech.js";
|
|
203
207
|
export * from "./schemas/approvals.js";
|
|
204
208
|
export * from "./schemas/automations.js";
|
|
205
209
|
export * from "./schemas/agents-list.js";
|
|
@@ -211,6 +215,8 @@ export * from "./schemas/codebase-health.js";
|
|
|
211
215
|
export * from "./schemas/context-trim.js";
|
|
212
216
|
export * from "./schemas/devices.js";
|
|
213
217
|
export * from "./schemas/project-delivery.js";
|
|
218
|
+
export * from "./schemas/device-artifacts.js";
|
|
219
|
+
export * from "./schemas/run-targets.js";
|
|
214
220
|
export * from "./schemas/engines.js";
|
|
215
221
|
export * from "./schemas/environment.js";
|
|
216
222
|
export * from "./schemas/exit.js";
|
|
@@ -264,6 +270,7 @@ export * from "./schemas/offload.js";
|
|
|
264
270
|
export * from "./schemas/webext.js";
|
|
265
271
|
export * from "./schemas/phone.js";
|
|
266
272
|
export * from "./schemas/workflows.js";
|
|
273
|
+
export * from "./schemas/workspace/workspace-graph.js";
|
|
267
274
|
export * from "./schemas/workspace/workspace-repos.js";
|
|
268
275
|
export * from "./schemas/workspace/workspace-search.js";
|
|
269
276
|
export * from "./schemas/workspace/workspace-setup.js";
|
|
@@ -276,7 +283,6 @@ export * from "./text/title.js";
|
|
|
276
283
|
export * from "./state/versions.js";
|
|
277
284
|
export * from "./text/model-answer.js";
|
|
278
285
|
export * from "./text/shell-regions.js";
|
|
279
|
-
export * from "./text/whisper.js";
|
|
280
286
|
export * from "./text/workflow-faults.js";
|
|
281
287
|
export * from "./text/emoji.js";
|
|
282
288
|
export * from "./time/zone.js";
|
|
@@ -315,6 +321,7 @@ export const sandboxContract = {
|
|
|
315
321
|
pages: pagesContract,
|
|
316
322
|
panels: panelsContract,
|
|
317
323
|
ports: portsContract,
|
|
324
|
+
runs: runsContract,
|
|
318
325
|
public: publicContract,
|
|
319
326
|
providers: providersContract,
|
|
320
327
|
push: pushContract,
|
|
@@ -121,7 +121,7 @@ export const OPENCODE: AgentCapabilities = {
|
|
|
121
121
|
steering: false,
|
|
122
122
|
permissions: "plan",
|
|
123
123
|
questions: false,
|
|
124
|
-
// Added at runtime under the conversation's own names, each
|
|
124
|
+
// Added at runtime under the conversation's own names, each session's own rules showing it only its own (opencode-mcp.ts).
|
|
125
125
|
mcp: "http",
|
|
126
126
|
execution: ["shell"],
|
|
127
127
|
effort: false,
|
|
@@ -131,7 +131,7 @@ export const OPENCODE: AgentCapabilities = {
|
|
|
131
131
|
terminals: false,
|
|
132
132
|
recovery: false,
|
|
133
133
|
warm: false,
|
|
134
|
-
//
|
|
134
|
+
// The session's own instruction entry, set per message; adds to OpenCode's own prompt, with no seam to replace it.
|
|
135
135
|
instructions: "append",
|
|
136
136
|
skillDiscovery: "prompt",
|
|
137
137
|
// OpenCode raises its permission asks for the commands its config names; the daemon answers from the rulebook, a
|
|
@@ -9,7 +9,9 @@ import { rawRouteUrl } from "../raw/raw-routes.js";
|
|
|
9
9
|
// The path of one of the four listener routes for a provider, built from RAW_ROUTES so the gateway and the daemon
|
|
10
10
|
// cannot spell it two ways.
|
|
11
11
|
export const listenerRouteUrl = (route: "state" | "dispatch" | "failure" | "status", provider: string): string =>
|
|
12
|
-
route === "state"
|
|
12
|
+
route === "state"
|
|
13
|
+
? rawRouteUrl("GET /listeners/{provider}/state", { provider })
|
|
14
|
+
: rawRouteUrl(`POST /listeners/{provider}/${route}`, { provider });
|
|
13
15
|
|
|
14
16
|
// The reconcile feed GET /listeners/<provider>/state answers: the enabled automations listening to this provider and
|
|
15
17
|
// the connectors the asking extension contributes, each with its whole config, secrets included, since the gateway
|
|
@@ -86,10 +88,9 @@ export const ListenerPairingSchema = z.object({
|
|
|
86
88
|
export type ListenerPairing = z.infer<typeof ListenerPairingSchema>;
|
|
87
89
|
|
|
88
90
|
// Push-based status: a gateway POSTs its connection/voice snapshot to /listeners/<provider>/status, since the daemon
|
|
89
|
-
// holds no provider connection to probe itself. Extends ActivityStatus with
|
|
90
|
-
//
|
|
91
|
+
// holds no provider connection to probe itself. Extends ActivityStatus with each unpaired capability's pairing ceremony
|
|
92
|
+
// by id.
|
|
91
93
|
export const ListenerStatusSchema = ActivityStatusSchema.extend({
|
|
92
|
-
whisperReady: z.boolean().optional(),
|
|
93
94
|
pairing: z.record(z.string(), ListenerPairingSchema).optional(),
|
|
94
95
|
});
|
|
95
96
|
export type ListenerStatus = z.infer<typeof ListenerStatusSchema>;
|
|
@@ -16,7 +16,11 @@ export const RAW_ROUTES = {
|
|
|
16
16
|
"GET /diff/raw": { lane: "bulk" },
|
|
17
17
|
// Arming the dictation model is a read any tier makes; dictating is writing a message, the collaborator's grant.
|
|
18
18
|
"GET /speech/status": { guest: true },
|
|
19
|
+
"POST /speech/prepare": { guest: true },
|
|
19
20
|
"POST /speech/transcribe": { floor: "collaborator", guest: true },
|
|
21
|
+
// The live phrase stream (schemas/speech.ts): a WebSocket, so the query ticket, floored at the collaborator in the
|
|
22
|
+
// handler like the transcribe it streams.
|
|
23
|
+
"GET /speech/stream": { auth: "door" },
|
|
20
24
|
// The bytes behind the workspace reads; each applies the caller's fence itself.
|
|
21
25
|
"GET /workspace/raw": { guest: true, lane: "bulk" },
|
|
22
26
|
// No door, unlike media: the page fetches a thumbnail itself, so the request carries the header.
|
|
@@ -124,6 +128,17 @@ export const RAW_ROUTES = {
|
|
|
124
128
|
"GET /fleet": { agent: true },
|
|
125
129
|
"POST /fleet/message": { agent: true },
|
|
126
130
|
"GET /fleet/{handle}": { agent: true },
|
|
131
|
+
// The `devices push` CLI: a program the agent built, carried to one of the owner's computers in chunks; held to the
|
|
132
|
+
// devices the calling conversation's turn mounts, and refused by the machine itself unless "Run programs" is on.
|
|
133
|
+
"POST /devices/{name}/artifacts": { agent: true, control: "never" },
|
|
134
|
+
// The rest of the `devices` command, held to the same rule (hosts/device-door.routes.ts): the device's app_start for a
|
|
135
|
+
// shell, and its loopback ports reached from the sandbox (hosts/device-tunnels.ts).
|
|
136
|
+
"POST /devices/{name}/apps": { agent: true, control: "never" },
|
|
137
|
+
"POST /devices/{name}/tunnels": { agent: true, control: "never" },
|
|
138
|
+
"DELETE /devices/{name}/tunnels/{port}": { agent: true, control: "never" },
|
|
139
|
+
"GET /devices/tunnels": { agent: true },
|
|
140
|
+
// Where a device dials back with a tunnel's one-time ticket; the ticket is the whole credential, checked by the route.
|
|
141
|
+
"GET /system/hosts/tunnel": { auth: "door", control: "never" },
|
|
127
142
|
// An extension gateway's realtime-listener control: /state hands back its connectors' stored credentials, so only the
|
|
128
143
|
// extension token of the extension declaring that `listener.provider` reaches them, checked in the handler too.
|
|
129
144
|
"GET /listeners/{provider}/state": { floor: "maintainer", panel: false, control: "never" },
|
package/src/schemas/agents.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { LandingDeliverySchema } from "./project-delivery.js";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { AgentHarnessSchema, AgentOriginSchema, AgentProviderSchema, ConversationQueueSchema, ForkedFromSchema } from "./agent.js";
|
|
5
5
|
import { LoopStateSchema } from "./loops.js";
|
|
6
|
-
import { LimitPolicySchema, RetryPolicySchema, TurnBreakPolicySchema, TurnBreakSchema } from "./turn-break.js";
|
|
6
|
+
import { LimitPolicySchema, RetryPolicySchema, RoomPolicySchema, TurnBreakPolicySchema, TurnBreakSchema } from "./turn-break.js";
|
|
7
7
|
import { KeepWarmSchema } from "./keep-warm.js";
|
|
8
8
|
import { AgentNeedSchema } from "./needs.js";
|
|
9
9
|
import { EMOJI_MAX_LENGTH, isSingleEmoji } from "../text/emoji.js";
|
|
@@ -362,6 +362,7 @@ export const AgentSummarySchema = z.object({
|
|
|
362
362
|
limitPolicy: LimitPolicySchema.optional(),
|
|
363
363
|
outagePolicy: RetryPolicySchema.optional(),
|
|
364
364
|
stopPolicy: RetryPolicySchema.optional(),
|
|
365
|
+
memoryPolicy: RoomPolicySchema.optional(),
|
|
365
366
|
// A collaborator's ask to land (collaborators can't merge themselves); cleared by whichever merge or discard
|
|
366
367
|
// answers it.
|
|
367
368
|
landRequested: z
|
|
@@ -907,9 +908,11 @@ export const AgentUnsentSchema = z.object({
|
|
|
907
908
|
// verbs is how the surfaces drifted apart in the first place.
|
|
908
909
|
export const AgentBreakPolicySchema = z.object({
|
|
909
910
|
id: z.string().min(1).describe("Which conversation."),
|
|
910
|
-
ending: TurnBreakSchema.describe(
|
|
911
|
+
ending: TurnBreakSchema.describe(
|
|
912
|
+
"Which wall this answers for: a spent usage limit, a provider outage, a turn that stopped short, or a message the sandbox held because it was short of memory.",
|
|
913
|
+
),
|
|
911
914
|
policy: TurnBreakPolicySchema.nullable().describe(
|
|
912
|
-
"What happens next for that ending. `wait` holds the turn for a press; `retry` re-runs it on a bounded ladder (outage, stop); `resend` sends it again at the published reset and `move` also tries another account with room (limit only). An answer the ending does not allow is refused. Null clears the override and goes back to following the sandbox-wide policy, so a conversation does not sit holding a frozen copy of a default it has quietly stopped following.",
|
|
915
|
+
"What happens next for that ending. `wait` holds the turn for a press; `retry` re-runs it on a bounded ladder (outage, stop); `resend` sends it again at the published reset (limit) or once memory frees up (memory), and `move` also tries another account with room (limit only). An answer the ending does not allow is refused. Null clears the override and goes back to following the sandbox-wide policy, so a conversation does not sit holding a frozen copy of a default it has quietly stopped following.",
|
|
913
916
|
),
|
|
914
917
|
});
|
|
915
918
|
export const AgentFileDiffQuerySchema = z.object({
|
|
@@ -215,6 +215,10 @@ export const DeviceScopesSchema = z.object({
|
|
|
215
215
|
// Its own switch under `shell`, default off while `shell` defaults on: a laptop has no disposable image to recreate
|
|
216
216
|
// from.
|
|
217
217
|
destructive: hostScope.default("off"),
|
|
218
|
+
// Its own switch, default off: receiving a program this sandbox built (`devices push`) and running it under the
|
|
219
|
+
// agent's supervision (app_start). Narrower than `shell` in what it reaches and wider in what it trusts: the program
|
|
220
|
+
// is the sandbox's, not one already on the machine.
|
|
221
|
+
programs: hostScope.default("off"),
|
|
218
222
|
// One directory per line. Empty ⇒ the machine's home directory, which is what the agent reports at connect.
|
|
219
223
|
roots: z.string().optional(),
|
|
220
224
|
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// A PROGRAM THIS SANDBOX BUILT, CARRIED TO A CONNECTED COMPUTER TO RUN THERE (`devices push`, then the device's app_start).
|
|
4
|
+
// The agent's CLI hands the daemon the bytes as the agent's own shell sees them; the daemon carries them over the device
|
|
5
|
+
// link in chunks through `stageArtifact`, and the machine files them under its own runs folder, one folder per content
|
|
6
|
+
// hash, so a second push of the same build is answered from what is already there and a running copy is never
|
|
7
|
+
// overwritten. Behind the device's "Run programs this sandbox sends" switch (`programs`), checked on the machine.
|
|
8
|
+
|
|
9
|
+
// One chunk's decoded size: small enough that a tunnel never sees a frame it would refuse, large enough that a 100 MB
|
|
10
|
+
// build is fifty calls.
|
|
11
|
+
export const ARTIFACT_CHUNK_BYTES = 2 * 1024 * 1024;
|
|
12
|
+
// The most one push carries, decoded. A build bigger than this is a disk image, not a program to try.
|
|
13
|
+
export const ARTIFACT_MAX_BYTES = 4 * 1024 * 1024 * 1024;
|
|
14
|
+
|
|
15
|
+
const Sha256Schema = z.string().regex(/^[0-9a-f]{64}$/, "a lowercase hex sha256");
|
|
16
|
+
// Names the folder a push lands in, so it is one plain path segment on every OS: no separators, no dots alone, nothing
|
|
17
|
+
// Windows reserves.
|
|
18
|
+
export const ArtifactNameSchema = z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/, "letters, digits, dot, dash and underscore, starting with a letter or digit, at most 64");
|
|
21
|
+
// The file's own name as it was in the sandbox, kept so a Windows program still ends in `.exe`.
|
|
22
|
+
export const ArtifactFileNameSchema = z
|
|
23
|
+
.string()
|
|
24
|
+
.regex(/^[A-Za-z0-9 ._()+-]{1,200}$/, "a plain file name")
|
|
25
|
+
.refine((name) => name !== "." && name !== ".." && !name.endsWith(".") && !name.endsWith(" "), "a plain file name");
|
|
26
|
+
// `file` is one file, filed as it is; `tar` a folder the CLI packed, unpacked on the machine.
|
|
27
|
+
export const ArtifactKindSchema = z.enum(["file", "tar"]);
|
|
28
|
+
export type ArtifactKind = z.infer<typeof ArtifactKindSchema>;
|
|
29
|
+
const UploadIdSchema = z.string().regex(/^[0-9a-f]{32}$/);
|
|
30
|
+
|
|
31
|
+
export const StageArtifactSchema = z.discriminatedUnion("op", [
|
|
32
|
+
// Whether this exact build is already staged here: the answer carries its path when it is, and nothing is sent.
|
|
33
|
+
z.object({ op: z.literal("have"), name: ArtifactNameSchema, sha256: Sha256Schema, kind: ArtifactKindSchema, fileName: ArtifactFileNameSchema }).strict(),
|
|
34
|
+
// One piece, at its offset; pieces arrive in order and a gap is refused.
|
|
35
|
+
z
|
|
36
|
+
.object({
|
|
37
|
+
op: z.literal("chunk"),
|
|
38
|
+
upload: UploadIdSchema,
|
|
39
|
+
offset: z.int().nonnegative(),
|
|
40
|
+
data: z.string().max(Math.ceil(ARTIFACT_CHUNK_BYTES / 3) * 4),
|
|
41
|
+
})
|
|
42
|
+
.strict(),
|
|
43
|
+
// The last word: the machine checks size and hash, then files it. A mismatch throws the upload away.
|
|
44
|
+
z
|
|
45
|
+
.object({
|
|
46
|
+
op: z.literal("commit"),
|
|
47
|
+
upload: UploadIdSchema,
|
|
48
|
+
name: ArtifactNameSchema,
|
|
49
|
+
kind: ArtifactKindSchema,
|
|
50
|
+
fileName: ArtifactFileNameSchema,
|
|
51
|
+
size: z.int().nonnegative().max(ARTIFACT_MAX_BYTES),
|
|
52
|
+
sha256: Sha256Schema,
|
|
53
|
+
})
|
|
54
|
+
.strict(),
|
|
55
|
+
// A push the daemon gave up on; what arrived is deleted.
|
|
56
|
+
z.object({ op: z.literal("abort"), upload: UploadIdSchema }).strict(),
|
|
57
|
+
]);
|
|
58
|
+
export type StageArtifact = z.infer<typeof StageArtifactSchema>;
|
|
59
|
+
|
|
60
|
+
export const StageArtifactResultSchema = z.object({
|
|
61
|
+
// Where the build is on the machine: the file itself, or the folder a `tar` was unpacked into. Absent for a `have`
|
|
62
|
+
// that found nothing, a chunk and an abort.
|
|
63
|
+
path: z.string().optional(),
|
|
64
|
+
// How many bytes of this upload the machine holds, answered to every chunk.
|
|
65
|
+
received: z.int().nonnegative().optional(),
|
|
66
|
+
});
|
|
67
|
+
export type StageArtifactResult = z.infer<typeof StageArtifactResultSchema>;
|
|
68
|
+
|
|
69
|
+
// What `POST /devices/{name}/artifacts` answers the CLI.
|
|
70
|
+
export const PushedArtifactSchema = z.object({
|
|
71
|
+
device: z.string(),
|
|
72
|
+
path: z.string(),
|
|
73
|
+
size: z.int().nonnegative(),
|
|
74
|
+
sha256: Sha256Schema,
|
|
75
|
+
// True when the machine already had this exact build and nothing was sent.
|
|
76
|
+
reused: z.boolean(),
|
|
77
|
+
});
|
|
78
|
+
export type PushedArtifact = z.infer<typeof PushedArtifactSchema>;
|
|
79
|
+
|
|
80
|
+
// A DEVICE'S LOOPBACK PORT, REACHED FROM THE SANDBOX (`devices reach`, hosts/device-tunnels.ts). The sandbox listens on
|
|
81
|
+
// its own 127.0.0.1:<localPort>; each connection asks the device to dial its own 127.0.0.1:<devicePort> and open a
|
|
82
|
+
// WebSocket back to /system/hosts/tunnel carrying the ticket below, and the two are pumped together.
|
|
83
|
+
export const DialLoopbackSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
ticket: z.string().regex(/^[0-9a-f]{48}$/),
|
|
86
|
+
port: z.int().min(1).max(65535),
|
|
87
|
+
})
|
|
88
|
+
.strict();
|
|
89
|
+
export type DialLoopback = z.infer<typeof DialLoopbackSchema>;
|
|
90
|
+
|
|
91
|
+
export const DeviceTunnelSchema = z.object({
|
|
92
|
+
device: z.string(),
|
|
93
|
+
devicePort: z.int().min(1).max(65535),
|
|
94
|
+
localPort: z.int().min(1).max(65535),
|
|
95
|
+
// What a program in the sandbox opens to reach the device's port.
|
|
96
|
+
url: z.string(),
|
|
97
|
+
openedAt: z.string(),
|
|
98
|
+
});
|
|
99
|
+
export type DeviceTunnel = z.infer<typeof DeviceTunnelSchema>;
|
package/src/schemas/hosts.ts
CHANGED
|
@@ -94,9 +94,11 @@ export type DeviceFacts = z.infer<typeof DeviceFactsSchema>;
|
|
|
94
94
|
// - `background-prepare`: the `prepare-background` op, the next update downloaded under the unattended rules of the
|
|
95
95
|
// agent's own timer (`ic sandbox prepare --auto`), which the update card sends when it opens; advertised only when
|
|
96
96
|
// the agent's `ic sandbox prepare` takes `--auto`.
|
|
97
|
+
// - `programs`: the `stageArtifact` procedure and the app_* tools, a program this sandbox built pushed to the machine and
|
|
98
|
+
// run under the agent's supervision (device-artifacts.ts); an agent without it is told to update rather than sent one.
|
|
97
99
|
// - `project-delivery`: the `deliverProject` procedure, landed work written into a folder attached to this computer's
|
|
98
100
|
// sandbox (project-delivery.ts); an agent without it is never sent one, and the land waits for Bring back.
|
|
99
|
-
export const DeviceFeatureSchema = z.enum(["reshape-later", "set-shape", "rollback-to", "loopback-catch", "background-prepare", "project-delivery"]);
|
|
101
|
+
export const DeviceFeatureSchema = z.enum(["reshape-later", "set-shape", "rollback-to", "loopback-catch", "background-prepare", "project-delivery", "programs"]);
|
|
100
102
|
export type DeviceFeature = z.infer<typeof DeviceFeatureSchema>;
|
|
101
103
|
export const DEVICE_FEATURE_RESHAPE_LATER: DeviceFeature = "reshape-later";
|
|
102
104
|
export const DEVICE_FEATURE_SET_SHAPE: DeviceFeature = "set-shape";
|
|
@@ -104,6 +106,7 @@ export const DEVICE_FEATURE_ROLLBACK_TO: DeviceFeature = "rollback-to";
|
|
|
104
106
|
export const DEVICE_FEATURE_LOOPBACK_CATCH: DeviceFeature = "loopback-catch";
|
|
105
107
|
export const DEVICE_FEATURE_BACKGROUND_PREPARE: DeviceFeature = "background-prepare";
|
|
106
108
|
export const DEVICE_FEATURE_PROJECT_DELIVERY: DeviceFeature = "project-delivery";
|
|
109
|
+
export const DEVICE_FEATURE_PROGRAMS: DeviceFeature = "programs";
|
|
107
110
|
// The features an agent advertised that this build knows; an unknown one is a newer agent's and means nothing here.
|
|
108
111
|
export const deviceFeatures = (facts: Pick<DeviceFacts, "features"> | undefined): DeviceFeature[] =>
|
|
109
112
|
(facts?.features ?? []).flatMap((feature) => {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ArtifactNameSchema } from "./device-artifacts.js";
|
|
3
|
+
|
|
4
|
+
// WHAT A REPOSITORY SAYS IT CAN BE RUN AS, ON ONE OF THE OWNER'S COMPUTERS (`<repo>/.intentic/run.json`). Beside the
|
|
5
|
+
// build scripts it names and tracked with them, like `.intentic/checks.json`. One file serves two doors that must do the
|
|
6
|
+
// same thing: an agent's `devices run <target>` in its own shell, and the editor's "Run on <device>" button, which runs
|
|
7
|
+
// that very command in a terminal the owner watches. Each run builds in the sandbox, carries the build to the device
|
|
8
|
+
// (`devices push`), opens the device ports the target says the sandbox needs (`devices reach`), and starts the program
|
|
9
|
+
// there (the device's app_start). Nothing here runs by itself: a target runs when someone runs it.
|
|
10
|
+
//
|
|
11
|
+
// {
|
|
12
|
+
// "targets": [{
|
|
13
|
+
// "name": "desktop",
|
|
14
|
+
// "device": "rog",
|
|
15
|
+
// "build": "bash _tools/scripts/desktop/build-desktop.sh 0.0.0 --windows-only",
|
|
16
|
+
// "artifact": {
|
|
17
|
+
// "intentic-desktop.exe": "_editor/desktop-app/src-tauri/target/x86_64-pc-windows-msvc/release/intentic-desktop.exe",
|
|
18
|
+
// "intentic-files.exe": "_editor/desktop-app/src-tauri/binaries/intentic-files-x86_64-pc-windows-msvc.exe"
|
|
19
|
+
// },
|
|
20
|
+
// "program": "intentic-desktop.exe"
|
|
21
|
+
// }]
|
|
22
|
+
// }
|
|
23
|
+
|
|
24
|
+
export const RUN_TARGETS_FILE = ".intentic/run.json";
|
|
25
|
+
|
|
26
|
+
const RepoPathSchema = z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.max(500)
|
|
30
|
+
.refine((path) => !path.startsWith("/") && !path.split(/[\\/]/).includes(".."), "a path inside the repository, relative to its root");
|
|
31
|
+
const BundleNameSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9 ._()+-]{0,199}(\/[A-Za-z0-9][A-Za-z0-9 ._()+-]{0,199})*$/, "a relative name, folders by /");
|
|
32
|
+
|
|
33
|
+
export const RunTargetSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
// What `devices run <name>` and the button call it.
|
|
36
|
+
name: ArtifactNameSchema,
|
|
37
|
+
description: z.string().max(300).optional(),
|
|
38
|
+
// The computer it runs on unless one is named at the run; the device's name as its tools are mounted.
|
|
39
|
+
device: z.string().min(1).max(100).optional(),
|
|
40
|
+
// A shell command run from the repository's root, in the sandbox, before every run; absent means the artifact
|
|
41
|
+
// is already there.
|
|
42
|
+
build: z.string().min(1).max(4000).optional(),
|
|
43
|
+
// What is carried: one file or one folder, or a set of files and folders gathered into one folder under names of
|
|
44
|
+
// their own (an .exe beside the sidecars, DLLs and resource folders it looks for next to itself).
|
|
45
|
+
artifact: z.union([RepoPathSchema, z.record(BundleNameSchema, RepoPathSchema)]),
|
|
46
|
+
// Which file to start when the artifact is a folder or a set; a lone file is started as itself.
|
|
47
|
+
program: BundleNameSchema.optional(),
|
|
48
|
+
args: z.array(z.string().max(4000)).max(100).default([]),
|
|
49
|
+
env: z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string().max(4000)).default({}),
|
|
50
|
+
// Run it inside Windows Sandbox, a throwaway VM that sees the build read-only and nothing else of the machine.
|
|
51
|
+
isolated: z.boolean().default(false),
|
|
52
|
+
// The device's own loopback ports the sandbox needs to reach while it runs (its local API, a dev server), each
|
|
53
|
+
// opened at the same port in the sandbox.
|
|
54
|
+
reach: z.array(z.int().min(1).max(65535)).max(16).default([]),
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
57
|
+
export type RunTarget = z.infer<typeof RunTargetSchema>;
|
|
58
|
+
|
|
59
|
+
export const RunTargetsFileSchema = z
|
|
60
|
+
.object({
|
|
61
|
+
targets: z
|
|
62
|
+
.array(RunTargetSchema)
|
|
63
|
+
.min(1)
|
|
64
|
+
.max(50)
|
|
65
|
+
.refine((targets) => new Set(targets.map((target) => target.name)).size === targets.length, "two targets share a name"),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
|
|
69
|
+
// One repository's file as the editor lists it: its targets, or what is wrong with the file.
|
|
70
|
+
export const RepoRunTargetsSchema = z.object({
|
|
71
|
+
repo: z.string(),
|
|
72
|
+
path: z.string(),
|
|
73
|
+
targets: z.array(RunTargetSchema),
|
|
74
|
+
error: z.string().optional(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const RunTargetsListSchema = z.object({
|
|
78
|
+
repos: z.array(RepoRunTargetsSchema),
|
|
79
|
+
// The owner's computers: whether each is connected, whether its agent is new enough to take a program, and whether
|
|
80
|
+
// its card's "Run programs this sandbox sends" switch is on. A run needs all three.
|
|
81
|
+
devices: z.array(z.object({ id: z.string(), online: z.boolean(), programs: z.boolean(), allowed: z.boolean() })),
|
|
82
|
+
});
|
|
83
|
+
export type RunTargetsList = z.infer<typeof RunTargetsListSchema>;
|
|
84
|
+
|
|
85
|
+
export const RunStartSchema = z.object({
|
|
86
|
+
repo: z.string().min(1),
|
|
87
|
+
target: ArtifactNameSchema,
|
|
88
|
+
device: z.string().min(1).max(100),
|
|
89
|
+
});
|
|
90
|
+
export type RunStart = z.infer<typeof RunStartSchema>;
|
|
91
|
+
|
|
92
|
+
// The terminal session the run is in, for the editor to open.
|
|
93
|
+
export const RunStartedSchema = z.object({ session: z.string() });
|
package/src/schemas/settings.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { CommandJudgeModeSchema, ProjectInstallModeSchema } from "../policy/safety-policy.js";
|
|
6
6
|
import { ModelRoleSchema } from "../models/model-roles.js";
|
|
7
7
|
import { AdmissionPolicySchema, AdmissionRuleSchema, ModelPinSchema } from "./agent.js";
|
|
8
|
-
import { LimitPolicySchema, RetryPolicySchema } from "./turn-break.js";
|
|
8
|
+
import { LimitPolicySchema, RetryPolicySchema, RoomPolicySchema } from "./turn-break.js";
|
|
9
9
|
import { ZoneSchema } from "../time/zone.js";
|
|
10
10
|
// Which prompt base the agent runs before this turn composes anything on top: Intentic's own (default), Claude Code's
|
|
11
11
|
// preset, or the owner's text. Declared out here since both the daemon and the browser branch on it.
|
|
@@ -452,6 +452,11 @@ export const SandboxSettingsSchema = z.object({
|
|
|
452
452
|
stopPolicy: RetryPolicySchema.default("wait").describe(
|
|
453
453
|
"What happens to a turn that stopped short with nothing to repair — a hung runtime, a crashed harness. `wait` holds it for a press. `retry` re-runs the held turn on a short ladder, standing down after three tries that got nowhere rather than looping forever.",
|
|
454
454
|
),
|
|
455
|
+
// The one ending that defaults to going by itself: nothing ran, so sending it once there is room spends nothing twice,
|
|
456
|
+
// and it is what the person meant when they pressed Send.
|
|
457
|
+
memoryPolicy: RoomPolicySchema.default("resend").describe(
|
|
458
|
+
"What happens to a message the sandbox held because it was short of memory: nothing ran, so nothing was spent. `resend` sends it by itself once memory has stayed free for a while, one held conversation at a time. `wait` holds it until somebody presses Send anyway. The sandbox-wide default; any one conversation can say otherwise.",
|
|
459
|
+
),
|
|
455
460
|
limitMoveCarryUnder: z
|
|
456
461
|
.number()
|
|
457
462
|
.int()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// Dictation on this sandbox: which local model hears a language, how far it is from ready, and the live wire the
|
|
4
|
+
// composer streams a spoken phrase over. The daemon's engine is src/speech; the composer's half is
|
|
5
|
+
// _editor/web/src/features/chat/composer/voice.
|
|
6
|
+
|
|
7
|
+
// The models a sandbox can hear with, by what they are for:
|
|
8
|
+
// - `parakeet` (NVIDIA Parakeet TDT 0.6B v3): 25 European languages, punctuated, ~30x faster than real time on two
|
|
9
|
+
// cores. Baked into the standard image, and the default for any language it covers.
|
|
10
|
+
// - `whisper` (OpenAI Whisper large-v3-turbo): the other ~75 languages, slower, fetched the first time one is asked for.
|
|
11
|
+
export const SpeechEngineSchema = z.enum(["parakeet", "whisper"]);
|
|
12
|
+
export type SpeechEngine = z.infer<typeof SpeechEngineSchema>;
|
|
13
|
+
|
|
14
|
+
// The 25 Parakeet hears, as ISO 639-1 primary subtags: it detects which on its own, so a browser in English and a
|
|
15
|
+
// speaker in Polish still land on it.
|
|
16
|
+
export const PARAKEET_LANGUAGES: ReadonlySet<string> = new Set([
|
|
17
|
+
"bg",
|
|
18
|
+
"cs",
|
|
19
|
+
"da",
|
|
20
|
+
"de",
|
|
21
|
+
"el",
|
|
22
|
+
"en",
|
|
23
|
+
"es",
|
|
24
|
+
"et",
|
|
25
|
+
"fi",
|
|
26
|
+
"fr",
|
|
27
|
+
"hr",
|
|
28
|
+
"hu",
|
|
29
|
+
"it",
|
|
30
|
+
"lt",
|
|
31
|
+
"lv",
|
|
32
|
+
"mt",
|
|
33
|
+
"nl",
|
|
34
|
+
"pl",
|
|
35
|
+
"pt",
|
|
36
|
+
"ro",
|
|
37
|
+
"ru",
|
|
38
|
+
"sk",
|
|
39
|
+
"sl",
|
|
40
|
+
"sv",
|
|
41
|
+
"uk",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// A locale's primary subtag, lowercased, or undefined for anything that is not one (`""`, `*`, `auto`).
|
|
45
|
+
export const speechLanguage = (locale: string | undefined): string | undefined => {
|
|
46
|
+
const primary = (locale ?? "").trim().toLowerCase().split(/[-_]/u)[0] ?? "";
|
|
47
|
+
return /^[a-z]{2,3}$/u.test(primary) && primary !== "auto" ? primary : undefined;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Which model hears a locale: Parakeet for its own languages and for anything unreadable, Whisper for the rest.
|
|
51
|
+
export const speechEngineFor = (locale: string | undefined): SpeechEngine => {
|
|
52
|
+
const language = speechLanguage(locale);
|
|
53
|
+
return language === undefined || PARAKEET_LANGUAGES.has(language) ? "parakeet" : "whisper";
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const SpeechStatusSchema = z.object({
|
|
57
|
+
provisioned: z.boolean().describe("This sandbox can run speech recognition at all."),
|
|
58
|
+
model: z
|
|
59
|
+
.enum(["absent", "downloading", "ready", "failed"])
|
|
60
|
+
.describe("Whether the model for the asked language is on disk: baked into the image, fetched, being fetched, or its fetch failed."),
|
|
61
|
+
engine: SpeechEngineSchema.optional().describe("Which model hears the asked language."),
|
|
62
|
+
loaded: z.boolean().optional().describe("The model is in memory, so the next phrase is heard without a load first."),
|
|
63
|
+
received: z.number().optional().describe("Bytes of the model fetched so far, while it downloads."),
|
|
64
|
+
total: z.number().optional().describe("The model's whole size in bytes, while it downloads."),
|
|
65
|
+
error: z.string().optional().describe("Why the last fetch failed, when it did."),
|
|
66
|
+
});
|
|
67
|
+
export type SpeechStatus = z.infer<typeof SpeechStatusSchema>;
|
|
68
|
+
|
|
69
|
+
// GET /speech/stream, a WebSocket: the composer cuts the microphone into phrases itself (its silence segmenter) and
|
|
70
|
+
// streams each one as it is spoken. A text frame `begin` opens phrase `id`; binary frames that follow are its audio,
|
|
71
|
+
// 16 kHz mono s16le; `end` closes it, keeping its first `keep` samples (the trailing pause trimmed off); `drop`
|
|
72
|
+
// abandons it. The daemon answers with `partial` text while the phrase is still being spoken, one `final` per ended
|
|
73
|
+
// phrase, in order, and `status` whenever the model's readiness moves. The language is the socket's `lang` query.
|
|
74
|
+
export const SpeechClientMessageSchema = z.discriminatedUnion("type", [
|
|
75
|
+
z.object({ type: z.literal("begin"), id: z.number() }),
|
|
76
|
+
z.object({ type: z.literal("end"), id: z.number(), keep: z.number() }),
|
|
77
|
+
z.object({ type: z.literal("drop"), id: z.number() }),
|
|
78
|
+
]);
|
|
79
|
+
export type SpeechClientMessage = z.infer<typeof SpeechClientMessageSchema>;
|
|
80
|
+
|
|
81
|
+
export const SpeechServerMessageSchema = z.discriminatedUnion("type", [
|
|
82
|
+
z.object({ type: z.literal("status"), status: SpeechStatusSchema }),
|
|
83
|
+
z.object({ type: z.literal("partial"), id: z.number(), text: z.string() }),
|
|
84
|
+
z.object({
|
|
85
|
+
type: z.literal("final"),
|
|
86
|
+
id: z.number(),
|
|
87
|
+
text: z.string().describe("Empty when the phrase held no words."),
|
|
88
|
+
decodeMs: z.number().describe("How long hearing it took once its audio was in."),
|
|
89
|
+
}),
|
|
90
|
+
z.object({ type: z.literal("error"), id: z.number().optional(), message: z.string() }),
|
|
91
|
+
]);
|
|
92
|
+
export type SpeechServerMessage = z.infer<typeof SpeechServerMessageSchema>;
|
|
93
|
+
|
|
94
|
+
// The longest phrase either door takes: a minute at 16 kHz. The composer's segmenter cuts a monologue at the same
|
|
95
|
+
// length, so nothing it sends is ever refused.
|
|
96
|
+
export const MAX_SPEECH_SAMPLES = 60 * 16_000;
|
package/src/schemas/terminal.ts
CHANGED
|
@@ -140,6 +140,10 @@ export const BrowsersListSchema = z.object({
|
|
|
140
140
|
});
|
|
141
141
|
export type BrowsersList = z.infer<typeof BrowsersListSchema>;
|
|
142
142
|
export const BrowserNameParamSchema = z.object({ name: z.string().describe("Which browser.") });
|
|
143
|
+
export const BrowserPageParamSchema = z.object({
|
|
144
|
+
name: z.string().describe("Which browser."),
|
|
145
|
+
pageId: z.string().describe("Which of its tabs: a page id from the roster."),
|
|
146
|
+
});
|
|
143
147
|
export const OpenBrowserInputSchema = z.object({
|
|
144
148
|
url: z.string().optional().describe("Where the new tab goes. Absent opens a blank tab, the way a browser's own new tab does."),
|
|
145
149
|
});
|
|
@@ -7,20 +7,23 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
// The
|
|
11
|
-
// `TurnEndingSchema.reason` spells them, so the ending a surface reads and the
|
|
12
|
-
//
|
|
13
|
-
|
|
10
|
+
// The walls a turn can stop at that take a standing answer. The first three end a turn that leaves finished work behind
|
|
11
|
+
// a live session, spelled exactly as `TurnEndingSchema.reason` spells them, so the ending a surface reads and the
|
|
12
|
+
// question it asks are one word. `memory` is the door: the sandbox too short of memory to start the turn at all, which
|
|
13
|
+
// holds the message (or the turn the sandbox kept) until a press or room comes back. A restart is handled apart
|
|
14
|
+
// (SandboxSettings.autoResumeOnRestart): nobody is watching a restart, so it has no in-chat question.
|
|
15
|
+
export const TurnBreakSchema = z.enum(["limit", "outage", "stopped", "memory"]);
|
|
14
16
|
export type TurnBreak = z.infer<typeof TurnBreakSchema>;
|
|
15
17
|
|
|
16
|
-
// Every ending that leaves a held turn behind:
|
|
17
|
-
// partway. That one has no standing answer and never re-runs by itself: the
|
|
18
|
-
// model again and another one (2026-09-30, the owner's call over an automatic
|
|
19
|
-
|
|
18
|
+
// Every ending that leaves a held turn behind: a spent allowance, an outage, a turn that stopped short, plus the
|
|
19
|
+
// provider's safety classifier stopping a turn partway. That one has no standing answer and never re-runs by itself: the
|
|
20
|
+
// person picks, each time, between the same model again and another one (2026-09-30, the owner's call over an automatic
|
|
21
|
+
// model switch). A low-memory hold is no ending: nothing ran, so it is never one of these.
|
|
22
|
+
export const HeldEndingSchema = z.enum(["limit", "outage", "stopped", "flagged"]);
|
|
20
23
|
export type HeldEnding = z.infer<typeof HeldEndingSchema>;
|
|
21
24
|
|
|
22
25
|
/** Whether this ending takes a standing answer (turn-break policy), which `flagged` never does. */
|
|
23
|
-
export const isTurnBreak = (ending: HeldEnding): ending is
|
|
26
|
+
export const isTurnBreak = (ending: HeldEnding): ending is Exclude<HeldEnding, "flagged"> => ending !== "flagged";
|
|
24
27
|
|
|
25
28
|
// `move` implies `resend`: an account with room is tried at once, and the reset appointment stands as its fallback.
|
|
26
29
|
// There is deliberately no "move, else hold" — a reader willing to spend a second account is willing to wait.
|
|
@@ -31,6 +34,11 @@ export type LimitPolicy = z.infer<typeof LimitPolicySchema>;
|
|
|
31
34
|
export const RetryPolicySchema = z.enum(["wait", "retry"]);
|
|
32
35
|
export type RetryPolicy = z.infer<typeof RetryPolicySchema>;
|
|
33
36
|
|
|
37
|
+
// The memory wall's two answers: hold for a press, or send once the sandbox has room again. Nothing ran, so sending it
|
|
38
|
+
// then spends nothing twice.
|
|
39
|
+
export const RoomPolicySchema = z.enum(["wait", "resend"]);
|
|
40
|
+
export type RoomPolicy = z.infer<typeof RoomPolicySchema>;
|
|
41
|
+
|
|
34
42
|
// One answer, whichever ending asked. Read against the ending: only `limit` can be `move`.
|
|
35
43
|
export const TurnBreakPolicySchema = z.enum(["wait", "resend", "move", "retry"]);
|
|
36
44
|
export type TurnBreakPolicy = z.infer<typeof TurnBreakPolicySchema>;
|
|
@@ -40,6 +48,7 @@ export const TURN_BREAK_POLICIES: Readonly<Record<TurnBreak, readonly TurnBreakP
|
|
|
40
48
|
limit: ["wait", "resend", "move"],
|
|
41
49
|
outage: ["wait", "retry"],
|
|
42
50
|
stopped: ["wait", "retry"],
|
|
51
|
+
memory: ["wait", "resend"],
|
|
43
52
|
};
|
|
44
53
|
|
|
45
54
|
/** Whether this ending may be answered this way; the one gate every writer goes through. */
|