@intentic/sandbox-contract 1.329.0 → 1.331.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/accounts.contract.d.ts +4 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +1 -0
- package/dist/contracts/agent.contract.d.ts.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/device.contract.d.ts +6 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +2 -1
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +3 -0
- package/dist/contracts/runner.contract.d.ts.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 +10 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +45 -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/translator.contract.d.ts +4 -0
- package/dist/contracts/translator.contract.d.ts.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/agent-events.d.ts +1 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/system-events.d.ts +10 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/index.d.ts +179 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/models/model-roles.d.ts +8 -0
- package/dist/models/model-roles.d.ts.map +1 -1
- package/dist/models/model-roles.js +8 -0
- package/dist/models/model-roles.js.map +1 -1
- package/dist/models/plan-pools.d.ts +2 -0
- package/dist/models/plan-pools.d.ts.map +1 -1
- package/dist/models/plan-pools.js +23 -7
- package/dist/models/plan-pools.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 +25 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +7 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/schemas/agent.d.ts +3 -0
- package/dist/schemas/agent.d.ts.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/device-artifacts.d.ts +13 -0
- package/dist/schemas/device-artifacts.d.ts.map +1 -1
- package/dist/schemas/device-artifacts.js +13 -0
- package/dist/schemas/device-artifacts.js.map +1 -1
- package/dist/schemas/environment.d.ts +30 -0
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +13 -0
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +7 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +5 -1
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +4 -0
- package/dist/schemas/providers/provider-oauth.d.ts.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-history.d.ts +6 -1
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +4 -1
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +10 -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 +8 -4
- 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 +6 -1
- 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 +7 -1
- package/src/models/agent-runtimes.ts +2 -2
- package/src/models/model-roles.ts +8 -0
- package/src/models/plan-pools.test.ts +27 -0
- package/src/models/plan-pools.ts +38 -10
- package/src/protocol/doors/listener-protocol.ts +5 -4
- package/src/protocol/raw/raw-routes.ts +12 -0
- package/src/schemas/agents.ts +6 -3
- package/src/schemas/device-artifacts.ts +21 -0
- package/src/schemas/environment.ts +25 -0
- package/src/schemas/providers/plan-limits.ts +9 -1
- package/src/schemas/run-targets.ts +93 -0
- package/src/schemas/settings-history.ts +15 -1
- package/src/schemas/settings.test.ts +25 -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
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TranscriptRow, TranscriptTool } from "../events/transcript.js";
|
|
2
|
+
export declare const PAGE_TEXT_CAP = 8000;
|
|
3
|
+
export declare const fitPageRow: <Row extends TranscriptRow>(row: Row) => Row;
|
|
4
|
+
export declare const fitNestedTool: (tool: TranscriptTool) => TranscriptTool;
|
|
5
|
+
//# sourceMappingURL=transcript-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-page.d.ts","sourceRoot":"","sources":["../../src/text/transcript-page.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAS7E,eAAO,MAAM,aAAa,OAAQ,CAAC;AAkBnC,eAAO,MAAM,UAAU,GAAI,GAAG,SAAS,aAAa,OAAO,GAAG,KAAG,GACgB,CAAC;AAGlF,eAAO,MAAM,aAAa,SAAU,cAAc,KAAG,cAInD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const PAGE_TEXT_CAP = 8_000;
|
|
2
|
+
const fitContent = (entry) => entry.type === "text" && entry.text.length > PAGE_TEXT_CAP ? { ...entry, text: entry.text.slice(0, PAGE_TEXT_CAP) } : entry;
|
|
3
|
+
const fitPageTool = (tool) => {
|
|
4
|
+
const { children, content, ...carried } = tool;
|
|
5
|
+
return {
|
|
6
|
+
...carried,
|
|
7
|
+
...(content !== undefined ? { content: content.map(fitContent) } : {}),
|
|
8
|
+
...(children !== undefined && children.length > 0 ? { nested: children.length } : {}),
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export const fitPageRow = (row) => row.tools === undefined ? row : { ...row, tools: row.tools.map(fitPageTool) };
|
|
12
|
+
export const fitNestedTool = (tool) => ({
|
|
13
|
+
...tool,
|
|
14
|
+
...(tool.content !== undefined ? { content: tool.content.map(fitContent) } : {}),
|
|
15
|
+
...(tool.children !== undefined ? { children: tool.children.map(fitNestedTool) } : {}),
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=transcript-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-page.js","sourceRoot":"","sources":["../../src/text/transcript-page.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AAEnC,MAAM,UAAU,GAAG,CAAC,KAAsB,EAAmB,EAAE,CAC3D,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAKhI,MAAM,WAAW,GAAG,CAAC,IAAoB,EAAkB,EAAE;IACzD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/C,OAAO;QACH,GAAG,OAAO;QACV,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxF,CAAC;AACN,CAAC,CAAC;AAGF,MAAM,CAAC,MAAM,UAAU,GAAG,CAA4B,GAAQ,EAAO,EAAE,CACnE,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;AAGlF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAoB,EAAkB,EAAE,CAAC,CAAC;IACpE,GAAG,IAAI;IACP,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAChF,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CACzF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/sandbox-contract",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.331.0",
|
|
4
4
|
"description": "oRPC wire contract for the intentic sandbox daemon, shared by the daemon and its browser client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -306,10 +306,10 @@
|
|
|
306
306
|
}
|
|
307
307
|
},
|
|
308
308
|
"dependencies": {
|
|
309
|
-
"@intentic/base": "1.
|
|
310
|
-
"@intentic/constants": "1.
|
|
311
|
-
"@intentic/extension-manifest": "1.
|
|
312
|
-
"@intentic/registry": "1.
|
|
309
|
+
"@intentic/base": "1.331.0",
|
|
310
|
+
"@intentic/constants": "1.331.0",
|
|
311
|
+
"@intentic/extension-manifest": "1.331.0",
|
|
312
|
+
"@intentic/registry": "1.331.0",
|
|
313
313
|
"@orpc/contract": "1.15.4",
|
|
314
314
|
"tslib": "2.8.1",
|
|
315
315
|
"zod": "4.6.5"
|
package/src/chores/probes.ts
CHANGED
|
@@ -86,7 +86,6 @@ const STANDALONE_DIRS =
|
|
|
86
86
|
`sed -n -e 's|/\\*\\*[[:space:]]*$||' -e 's|^[[:space:]]*-[[:space:]]*!\\([^[:space:]#]*\\).*$|\\1|p'`;
|
|
87
87
|
|
|
88
88
|
// The line that opens one standalone package's report, as JSON so a guard can still read the stream with `jq -s`.
|
|
89
|
-
const STANDALONE_KEY = `standalone`;
|
|
90
89
|
const STANDALONE_LINE = /^\{"standalone":"([^"]*)"\}$/;
|
|
91
90
|
|
|
92
91
|
// Runs `measure` against each standalone package in a scratch copy, never in place: `--lockfile-only` would write
|
|
@@ -97,7 +96,7 @@ const STANDALONE_LINE = /^\{"standalone":"([^"]*)"\}$/;
|
|
|
97
96
|
const standaloneCommand = (measure: string): string =>
|
|
98
97
|
`${STANDALONE_DIRS} | while IFS= read -r dir; do ` +
|
|
99
98
|
`[ -f "$dir/package.json" ] || continue; ` +
|
|
100
|
-
`printf '{"
|
|
99
|
+
`printf '{"standalone":"%s"}\\n' "$dir"; ` +
|
|
101
100
|
`scratch=$(mktemp -d) || continue; ` +
|
|
102
101
|
`for file in package.json pnpm-lock.yaml package-lock.json .npmrc; do [ -f "$dir/$file" ] && cp "$dir/$file" "$scratch/"; done; ` +
|
|
103
102
|
`( cd "$scratch" && { [ -f pnpm-lock.yaml ] || { [ -f package-lock.json ] && pnpm import >/dev/null 2>&1; } || ` +
|
|
@@ -7,7 +7,7 @@ import { DeviceFactsSchema } from "../schemas/hosts.js";
|
|
|
7
7
|
import { LoopbackCatchEventSchema, LoopbackCatchSchema } from "../schemas/loopback-catch.js";
|
|
8
8
|
import { OkSchema } from "../schemas/shared.js";
|
|
9
9
|
import { ProjectDeliveryResultSchema, ProjectDeliverySchema } from "../schemas/project-delivery.js";
|
|
10
|
-
import { StageArtifactResultSchema, StageArtifactSchema } from "../schemas/device-artifacts.js";
|
|
10
|
+
import { DialLoopbackSchema, StageArtifactResultSchema, StageArtifactSchema } from "../schemas/device-artifacts.js";
|
|
11
11
|
|
|
12
12
|
// What a connected device can be asked, over the socket it opened; the machine is the oRPC server, the daemon the
|
|
13
13
|
// client. No `.route()`: the procedure path is the address, not HTTP. Every input is strict (devices.ts says why). `mcp` stays opaque (`z.unknown()`) so a machine
|
|
@@ -41,4 +41,9 @@ export const deviceContract = {
|
|
|
41
41
|
// Only the daemon calls it, for the agent's `devices push`, and only on an agent advertising `programs`; the machine
|
|
42
42
|
// refuses every op unless "Run programs this sandbox sends" is on.
|
|
43
43
|
stageArtifact: oc.input(StageArtifactSchema).output(StageArtifactResultSchema),
|
|
44
|
+
// A connection to the sandbox side of a `devices reach` tunnel: the machine dials its own 127.0.0.1:<port> and, once
|
|
45
|
+
// that answered, opens a WebSocket back to /system/hosts/tunnel with the ticket. Answers once the local port took
|
|
46
|
+
// the connection, or throws (nothing listening there, the switch is off). Advertised with `programs`; the machine
|
|
47
|
+
// allows it when "Run commands" or "Run programs this sandbox sends" is on, either of which already reaches it.
|
|
48
|
+
dialLoopback: oc.input(DialLoopbackSchema).output(z.object({ ok: z.literal(true) })),
|
|
44
49
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { procedure } from "../protocol/route-meta.js";
|
|
2
|
+
import { RUN_TARGETS_FILE, RunStartedSchema, RunStartSchema, RunTargetsListSchema } from "../schemas/run-targets.js";
|
|
3
|
+
|
|
4
|
+
// A repository's programs run on the owner's own computers (schemas/run-targets.ts): what each repository declares, and
|
|
5
|
+
// the button that runs one. The run itself is `devices run` in a terminal, the same command an agent runs, so what the
|
|
6
|
+
// owner watches is what an agent gets.
|
|
7
|
+
export const runsContract = {
|
|
8
|
+
targets: procedure
|
|
9
|
+
.route({
|
|
10
|
+
method: "GET",
|
|
11
|
+
path: "/runs/targets",
|
|
12
|
+
summary: "What each repository can run on your computers",
|
|
13
|
+
description: `Every repository that declares run targets at \`${RUN_TARGETS_FILE}\`, and which of your computers could run one now.`,
|
|
14
|
+
})
|
|
15
|
+
.output(RunTargetsListSchema),
|
|
16
|
+
start: procedure
|
|
17
|
+
.route({
|
|
18
|
+
method: "POST",
|
|
19
|
+
path: "/runs/start",
|
|
20
|
+
summary: "Run a target on one of your computers",
|
|
21
|
+
description:
|
|
22
|
+
"Builds it in the sandbox, carries the build to the computer and starts it there, in a terminal you can watch. The computer still decides: its \"Run programs this sandbox sends\" switch has to be on.",
|
|
23
|
+
})
|
|
24
|
+
.input(RunStartSchema)
|
|
25
|
+
.output(RunStartedSchema),
|
|
26
|
+
};
|
|
@@ -17,6 +17,7 @@ import { DaemonSessionSchema, InfoSchema, ManifestProblemsSchema, ManifestRepair
|
|
|
17
17
|
import { AutoUpdateInputSchema, AutoUpdateSchema, SkipUpdateInputSchema } from "../schemas/updates.js";
|
|
18
18
|
import {
|
|
19
19
|
BrowserNameParamSchema,
|
|
20
|
+
BrowserPageParamSchema,
|
|
20
21
|
BrowsersListSchema,
|
|
21
22
|
OpenBrowserInputSchema,
|
|
22
23
|
OpenBrowserResultSchema,
|
|
@@ -253,6 +254,18 @@ export const systemContract = {
|
|
|
253
254
|
})
|
|
254
255
|
.input(BrowserNameParamSchema)
|
|
255
256
|
.output(OkSchema),
|
|
257
|
+
// One tab, closed without watching its window: the Browsers strip shows every window's tabs, and only the window in
|
|
258
|
+
// front has a picture socket to say a close on.
|
|
259
|
+
closeBrowserPage: systemRoute
|
|
260
|
+
.route({
|
|
261
|
+
method: "DELETE",
|
|
262
|
+
path: "/system/browsers/{name}/pages/{pageId}",
|
|
263
|
+
summary: "Close one tab of a browser",
|
|
264
|
+
description:
|
|
265
|
+
"Closes one tab of a browser window, as its own close button does. A tab that is already gone answers the same, since gone is what was asked for.",
|
|
266
|
+
})
|
|
267
|
+
.input(BrowserPageParamSchema)
|
|
268
|
+
.output(OkSchema),
|
|
256
269
|
// The picture and the hands come over /system/desktop-view; this is whether there is anything on it, and which windows
|
|
257
270
|
// a tab could show alone. Pushed through the `desktop` runtime domain as windows open, close and take the keyboard,
|
|
258
271
|
// and as their titles change while a view is open; never polled by the client.
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
ReposListSchema,
|
|
14
14
|
RunTestsSchema,
|
|
15
15
|
TemplatesListSchema,
|
|
16
|
-
WorkspaceGraphSchema,
|
|
17
16
|
WorkspaceSyncSchema,
|
|
18
17
|
} from "../schemas/workspace/workspace-repos.js";
|
|
18
|
+
import { PackageModulesParamSchema, PackageModulesSchema, WorkspaceGraphSchema } from "../schemas/workspace/workspace-graph.js";
|
|
19
19
|
import { WorkspaceSearchQuerySchema, WorkspaceSearchResultSchema } from "../schemas/workspace/workspace-search.js";
|
|
20
20
|
import { WorkspaceInstallResultSchema, WorkspaceInstallSchema, WorkspaceSetupSchema } from "../schemas/workspace/workspace-setup.js";
|
|
21
21
|
import {
|
|
@@ -341,6 +341,17 @@ export const workspaceContract = {
|
|
|
341
341
|
})
|
|
342
342
|
.input(RepoAppsParamSchema)
|
|
343
343
|
.output(WorkspaceGraphSchema),
|
|
344
|
+
// One package's modules grouped into units, and the value imports between them, placed in its declared layers.
|
|
345
|
+
packageModules: procedure
|
|
346
|
+
.route({
|
|
347
|
+
method: "GET",
|
|
348
|
+
path: "/workspace/repos/{repo}/layers",
|
|
349
|
+
summary: "How one package's modules import each other",
|
|
350
|
+
description:
|
|
351
|
+
"One workspace package's source directories as units, the value imports between them, and the layers its layers.json declares, which is what a view of its insides draws.",
|
|
352
|
+
})
|
|
353
|
+
.input(PackageModulesParamSchema)
|
|
354
|
+
.output(PackageModulesSchema),
|
|
344
355
|
// Every repo's package dirs in one call, since a review spans repos and per-repo would fan out per open.
|
|
345
356
|
modules: procedure
|
|
346
357
|
.route({
|
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";
|
|
@@ -212,6 +216,7 @@ export * from "./schemas/context-trim.js";
|
|
|
212
216
|
export * from "./schemas/devices.js";
|
|
213
217
|
export * from "./schemas/project-delivery.js";
|
|
214
218
|
export * from "./schemas/device-artifacts.js";
|
|
219
|
+
export * from "./schemas/run-targets.js";
|
|
215
220
|
export * from "./schemas/engines.js";
|
|
216
221
|
export * from "./schemas/environment.js";
|
|
217
222
|
export * from "./schemas/exit.js";
|
|
@@ -265,6 +270,7 @@ export * from "./schemas/offload.js";
|
|
|
265
270
|
export * from "./schemas/webext.js";
|
|
266
271
|
export * from "./schemas/phone.js";
|
|
267
272
|
export * from "./schemas/workflows.js";
|
|
273
|
+
export * from "./schemas/workspace/workspace-graph.js";
|
|
268
274
|
export * from "./schemas/workspace/workspace-repos.js";
|
|
269
275
|
export * from "./schemas/workspace/workspace-search.js";
|
|
270
276
|
export * from "./schemas/workspace/workspace-setup.js";
|
|
@@ -277,7 +283,6 @@ export * from "./text/title.js";
|
|
|
277
283
|
export * from "./state/versions.js";
|
|
278
284
|
export * from "./text/model-answer.js";
|
|
279
285
|
export * from "./text/shell-regions.js";
|
|
280
|
-
export * from "./text/whisper.js";
|
|
281
286
|
export * from "./text/workflow-faults.js";
|
|
282
287
|
export * from "./text/emoji.js";
|
|
283
288
|
export * from "./time/zone.js";
|
|
@@ -316,6 +321,7 @@ export const sandboxContract = {
|
|
|
316
321
|
pages: pagesContract,
|
|
317
322
|
panels: panelsContract,
|
|
318
323
|
ports: portsContract,
|
|
324
|
+
runs: runsContract,
|
|
319
325
|
public: publicContract,
|
|
320
326
|
providers: providersContract,
|
|
321
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
|
|
@@ -92,6 +92,14 @@ export const MODEL_ROLES = [
|
|
|
92
92
|
trigger: "pressed",
|
|
93
93
|
icon: "wrench",
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
id: "refactor-run",
|
|
97
|
+
label: "Refactors",
|
|
98
|
+
blurb: "The agent started from a hotspot or key module on a repository's Health tab.",
|
|
99
|
+
kind: "run",
|
|
100
|
+
trigger: "pressed",
|
|
101
|
+
icon: "hammer",
|
|
102
|
+
},
|
|
95
103
|
{
|
|
96
104
|
id: "documentation-run",
|
|
97
105
|
label: "Documentation runs",
|
|
@@ -132,6 +132,22 @@ test("spent is the contract's one line: 99.5% still has room, 100% is spent unti
|
|
|
132
132
|
expect(headroomState(CLAUDE, { id: "claude-opus-4-6" })).toEqual({ kind: "spent" });
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
// Anthropic reports whole percents: an account read at 100 kept serving turns for hours, so that reading is the line,
|
|
136
|
+
// not proof. Only the provider's refusal makes such a pool spent.
|
|
137
|
+
test("a pool read at 100 in whole percents is at the line with no room, until a refusal proves it empty", () => {
|
|
138
|
+
const week = window({ kind: "seven_day", utilization: 100, resetsAt: 900, rounded: true });
|
|
139
|
+
const atLine = usage(window({ kind: "five_hour", utilization: 75, rounded: true }), week);
|
|
140
|
+
expect(headroomState(atLine)).toEqual({ kind: "ready", room: 0 });
|
|
141
|
+
expect(headroomState(atLine, { id: "claude-opus-5-5" })).toEqual({ kind: "ready", room: 0 });
|
|
142
|
+
// A precise 100 is still spent.
|
|
143
|
+
expect(headroomState(usage(window({ kind: "seven_day", utilization: 100, resetsAt: 900 })))).toEqual({ kind: "spent", reopensAt: 900 });
|
|
144
|
+
// The provider refused it: spent until the pool reopens, and a reading still at the line does not answer that.
|
|
145
|
+
const facts = { account: "a", usage: { ...atLine, measuredAt: 2_000 } };
|
|
146
|
+
const refused = refusal({ kind: "limit", account: "a" });
|
|
147
|
+
expect(refusalVerdict(refused, [facts])).toBe("standing");
|
|
148
|
+
expect(serviceStates([facts], refused).get("a")).toEqual({ kind: "spent", reopensAt: 900 });
|
|
149
|
+
});
|
|
150
|
+
|
|
135
151
|
test("with no model named, an account is spent only when no model can run on it", () => {
|
|
136
152
|
// A full Opus slice leaves every other model the room of the fullest pool still open.
|
|
137
153
|
expect(headroomState(CLAUDE)).toEqual({ kind: "ready", room: 70 });
|
|
@@ -243,3 +259,14 @@ test("an unnamed turn takes the most room, then an unmeasured account, then a sp
|
|
|
243
259
|
expect(roomiestAccount([entry("seatless", blocked), entry("unread", { kind: "unknown" })])).toBeUndefined();
|
|
244
260
|
expect(roomiestAccount([entry("low", { kind: "ready", room: 5 }), entry("high", { kind: "ready", room: 60 })])?.id).toBe("high");
|
|
245
261
|
});
|
|
262
|
+
|
|
263
|
+
test("an account at the line is tried after an unmeasured one and before a spent one, and never a move's destination", () => {
|
|
264
|
+
const entry = (id: string, state: AccountState) => ({ id, state });
|
|
265
|
+
const atLine: AccountState = { kind: "ready", room: 0 };
|
|
266
|
+
expect(preferredAccount([entry("line", atLine), entry("low", { kind: "ready", room: 2 })])?.id).toBe("low");
|
|
267
|
+
expect(preferredAccount([entry("line", atLine), entry("unread", { kind: "unknown" })])?.id).toBe("unread");
|
|
268
|
+
expect(preferredAccount([entry("spent", { kind: "spent" }), entry("line", atLine)])?.id).toBe("line");
|
|
269
|
+
// A move lands only on proven room: two accounts at the line would otherwise hand a refused turn back and forth.
|
|
270
|
+
expect(roomiestAccount([entry("line", atLine), entry("spent", { kind: "spent" })])).toBeUndefined();
|
|
271
|
+
expect(roomiestAccount([entry("line", atLine), entry("low", { kind: "ready", room: 2 })])?.id).toBe("low");
|
|
272
|
+
});
|
package/src/models/plan-pools.ts
CHANGED
|
@@ -8,6 +8,18 @@ import type { AccountState, AccountUsage, ProviderRefusal, UsageWindow, WindowGa
|
|
|
8
8
|
// anything short of it still has room.
|
|
9
9
|
export const SPENT_UTILIZATION = 100;
|
|
10
10
|
|
|
11
|
+
// Whether a reading proves its pool empty. A pool read at the line by a provider that rounds to whole percents
|
|
12
|
+
// (`rounded`) does not: Anthropic reports 99.5% as 100 and keeps serving turns on it, so treating that as spent strands
|
|
13
|
+
// the last of an allowance (and turns a plain send into one scheduled for the reset). Such a pool is at the line, with
|
|
14
|
+
// no room to rank by; only the provider's refusal (pinnedFull) proves it empty.
|
|
15
|
+
export const provenFull = (window: UsageWindow): boolean => window.utilization >= SPENT_UTILIZATION && window.rounded !== true;
|
|
16
|
+
|
|
17
|
+
// A pool a standing limit refusal shows full: at the line, and proven so whatever the provider's rounding.
|
|
18
|
+
export const pinnedFull = (window: UsageWindow): UsageWindow => {
|
|
19
|
+
const { rounded: _rounded, ...pool } = window;
|
|
20
|
+
return { ...pool, utilization: Math.max(window.utilization, SPENT_UTILIZATION) };
|
|
21
|
+
};
|
|
22
|
+
|
|
11
23
|
export interface ModelRef {
|
|
12
24
|
// Wire model id.
|
|
13
25
|
readonly id: string;
|
|
@@ -210,15 +222,15 @@ export const headroomState = (usage: AccountUsage | undefined, model?: ModelRef)
|
|
|
210
222
|
if (windows.length === 0) {
|
|
211
223
|
return { kind: "unknown" };
|
|
212
224
|
}
|
|
213
|
-
const full = windows.filter(
|
|
214
|
-
const open = windows.filter((window) => window
|
|
225
|
+
const full = windows.filter(provenFull);
|
|
226
|
+
const open = windows.filter((window) => !provenFull(window));
|
|
215
227
|
if (model !== undefined || full.some((window) => window.gates === "all") || open.length === 0) {
|
|
216
228
|
const holding = model !== undefined ? full : full.some((window) => window.gates === "all") ? full.filter((window) => window.gates === "all") : full;
|
|
217
229
|
if (holding.length > 0) {
|
|
218
230
|
return spent(reopening(holding, model === undefined && !holding.some((window) => window.gates === "all")));
|
|
219
231
|
}
|
|
220
232
|
}
|
|
221
|
-
return { kind: "ready", room: SPENT_UTILIZATION - Math.max(...open.map((window) => window.utilization)) };
|
|
233
|
+
return { kind: "ready", room: Math.max(0, SPENT_UTILIZATION - Math.max(...open.map((window) => window.utilization))) };
|
|
222
234
|
};
|
|
223
235
|
|
|
224
236
|
// A standing limit refusal reads its pool as full, since a polled reading freezes the moment a pool actually empties: the
|
|
@@ -231,7 +243,7 @@ const refusedPool = (usage: AccountUsage | undefined, refusal: ProviderRefusal,
|
|
|
231
243
|
const sameModel = refusal.model === undefined || model === undefined || refusal.model === model.id;
|
|
232
244
|
return sameModel ? spent(refusal.resetsAt) : headroomState(usage, model);
|
|
233
245
|
}
|
|
234
|
-
const pinned = usage.windows.map((window) => (window === binding ?
|
|
246
|
+
const pinned = usage.windows.map((window) => (window === binding ? pinnedFull(window) : window));
|
|
235
247
|
const state = headroomState({ ...usage, windows: pinned }, model);
|
|
236
248
|
// The provider's own "try again at" is when it reopens, over whatever the polled pool last said.
|
|
237
249
|
return state.kind === "spent" && refusal.resetsAt !== undefined ? spent(refusal.resetsAt) : state;
|
|
@@ -287,19 +299,35 @@ export const serviceStates = (
|
|
|
287
299
|
);
|
|
288
300
|
};
|
|
289
301
|
|
|
290
|
-
// Worst last: proven room, no reading either way,
|
|
291
|
-
|
|
302
|
+
// Worst last: proven room, no reading either way, a pool at the line (it may hold a sliver, or nothing), known spent,
|
|
303
|
+
// and nothing a turn can run on.
|
|
304
|
+
const preference = (state: AccountState): number => {
|
|
305
|
+
switch (state.kind) {
|
|
306
|
+
case "ready":
|
|
307
|
+
return state.room > 0 ? 0 : 2;
|
|
308
|
+
case "unknown":
|
|
309
|
+
return 1;
|
|
310
|
+
case "spent":
|
|
311
|
+
return 3;
|
|
312
|
+
case "blocked":
|
|
313
|
+
return 4;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
292
316
|
|
|
293
|
-
/** The account an unnamed turn runs on: ready by most room, then unmeasured, then spent, blocked only when that is all there is. Ties keep the caller's order. */
|
|
317
|
+
/** The account an unnamed turn runs on: ready by most room, then unmeasured, then at the line, then spent, blocked only when that is all there is. Ties keep the caller's order. */
|
|
294
318
|
export const preferredAccount = <T extends { readonly state: AccountState }>(entries: readonly T[]): T | undefined =>
|
|
295
319
|
entries.reduce<T | undefined>((best, next) => {
|
|
296
320
|
if (best === undefined) {
|
|
297
321
|
return next;
|
|
298
322
|
}
|
|
299
|
-
const tier =
|
|
323
|
+
const tier = preference(next.state) - preference(best.state);
|
|
300
324
|
return tier < 0 || (tier === 0 && next.state.kind === "ready" && best.state.kind === "ready" && next.state.room > best.state.room) ? next : best;
|
|
301
325
|
}, undefined);
|
|
302
326
|
|
|
303
|
-
/**
|
|
327
|
+
/**
|
|
328
|
+
* The roomiest account with proven room, or none: what a move off a refused account may land on. Ties keep the caller's
|
|
329
|
+
* order. An account at the line is left out: a move there could be refused in turn, and with one refusal remembered per
|
|
330
|
+
* provider, two such accounts would hand the turn back and forth.
|
|
331
|
+
*/
|
|
304
332
|
export const roomiestAccount = <T extends { readonly state: AccountState }>(entries: readonly T[]): T | undefined =>
|
|
305
|
-
preferredAccount(entries.filter((entry) => entry.state.kind === "ready"));
|
|
333
|
+
preferredAccount(entries.filter((entry) => entry.state.kind === "ready" && entry.state.room > 0));
|
|
@@ -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.
|
|
@@ -127,6 +131,14 @@ export const RAW_ROUTES = {
|
|
|
127
131
|
// The `devices push` CLI: a program the agent built, carried to one of the owner's computers in chunks; held to the
|
|
128
132
|
// devices the calling conversation's turn mounts, and refused by the machine itself unless "Run programs" is on.
|
|
129
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" },
|
|
130
142
|
// An extension gateway's realtime-listener control: /state hands back its connectors' stored credentials, so only the
|
|
131
143
|
// extension token of the extension declaring that `listener.provider` reaches them, checked in the handler too.
|
|
132
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({
|
|
@@ -76,3 +76,24 @@ export const PushedArtifactSchema = z.object({
|
|
|
76
76
|
reused: z.boolean(),
|
|
77
77
|
});
|
|
78
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>;
|
|
@@ -45,12 +45,37 @@ export type RuntimeInstall = z.infer<typeof RuntimeInstallSchema>;
|
|
|
45
45
|
// (`environment propose`) clears its tool's entries.
|
|
46
46
|
export const SettledDraftSchema = z.object({ tool: z.string(), hash: z.string(), at: z.number() });
|
|
47
47
|
export type SettledDraft = z.infer<typeof SettledDraftSchema>;
|
|
48
|
+
// One overlay the daemon composed, by the hash a rebuild is stamped with, and the custom-section blocks it carried (each
|
|
49
|
+
// by name and the hash of its trimmed steps). Kept so "is this tool in the image" is answered against the overlay the
|
|
50
|
+
// running container was actually built from, which is rarely the newest one composed: every approval, capability change
|
|
51
|
+
// and daemon update composes again, and a rebuild builds whatever was newest when it started.
|
|
52
|
+
export const EnvironmentCompositionSchema = z.object({
|
|
53
|
+
hash: z.string(),
|
|
54
|
+
at: z.number(),
|
|
55
|
+
blocks: z.array(z.object({ name: z.string(), hash: z.string() })),
|
|
56
|
+
});
|
|
57
|
+
export type EnvironmentComposition = z.infer<typeof EnvironmentCompositionSchema>;
|
|
58
|
+
// A draft the daemon wrote on its own (a recurring runtime install, a cache-rule revision of an approved block), by the
|
|
59
|
+
// name its file carries and the hash of its steps. A draft that later vanishes with no answer on record was thrown away
|
|
60
|
+
// by hand (the Changes panel's discard, a git clean): that is read as a no, rather than writing it back on the next
|
|
61
|
+
// sweep. `install` is the ledger entry an auto-draft came from, which a no declines.
|
|
62
|
+
export const EnvironmentOfferSchema = z.object({
|
|
63
|
+
tool: z.string(),
|
|
64
|
+
hash: z.string(),
|
|
65
|
+
at: z.number(),
|
|
66
|
+
install: z.string().optional(),
|
|
67
|
+
});
|
|
68
|
+
export type EnvironmentOffer = z.infer<typeof EnvironmentOfferSchema>;
|
|
48
69
|
export const RuntimeInstallsFileSchema = z.object({
|
|
49
70
|
installs: z.array(RuntimeInstallSchema),
|
|
50
71
|
// The last drift snapshot, persisted so a daemon restart does not blank the card until the next sweep.
|
|
51
72
|
drift: EnvironmentDriftSchema.optional(),
|
|
52
73
|
// Drafts already answered, newest last and capped; absent on a ledger written before removal existed.
|
|
53
74
|
settled: z.array(SettledDraftSchema).optional(),
|
|
75
|
+
// Overlays composed, oldest first and capped; absent on a ledger written before they were kept.
|
|
76
|
+
compositions: z.array(EnvironmentCompositionSchema).optional(),
|
|
77
|
+
// Drafts the daemon wrote that nobody has answered yet; absent on a ledger written before they were kept.
|
|
78
|
+
offered: z.array(EnvironmentOfferSchema).optional(),
|
|
54
79
|
});
|
|
55
80
|
export type RuntimeInstallsFile = z.infer<typeof RuntimeInstallsFileSchema>;
|
|
56
81
|
// Ledger entry as the Environment card shows it: recurrence joined with whether it's present in the live container
|
|
@@ -23,6 +23,12 @@ export const UsageWindowSchema = z.object({
|
|
|
23
23
|
utilization: z.number(), // 0-100
|
|
24
24
|
resetsAt: z.number().optional(),
|
|
25
25
|
gates: WindowGatesSchema,
|
|
26
|
+
rounded: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe(
|
|
30
|
+
"The provider rounds this figure to a whole percent (Anthropic does), so a reading of 100 may be 99.5 and the pool can still serve. Such a pool reads as at the line, not spent: only the provider's refusal proves it empty.",
|
|
31
|
+
),
|
|
26
32
|
});
|
|
27
33
|
export type UsageWindow = z.infer<typeof UsageWindowSchema>;
|
|
28
34
|
// A failed re-read, whatever the cause: a 429 park, a 403 asking for verification, a 5xx, a timeout. Recorded as the fact
|
|
@@ -59,7 +65,9 @@ export const AccountStateSchema = z.discriminatedUnion("kind", [
|
|
|
59
65
|
kind: z.literal("ready"),
|
|
60
66
|
room: z
|
|
61
67
|
.number()
|
|
62
|
-
.describe(
|
|
68
|
+
.describe(
|
|
69
|
+
"How much of the fullest pool that gates the turn is left, in percent (0 up to 100). Pickers take the most room. 0 is a rounded pool read at 100: at the line, tried after an unmeasured account, never a limit move's destination.",
|
|
70
|
+
),
|
|
63
71
|
}),
|
|
64
72
|
z.object({
|
|
65
73
|
kind: z.literal("spent"),
|