@intentic/sandbox-contract 1.317.0 → 1.319.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/dist/chores/chores.js +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/probes.js +1 -1
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +75 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/accounts.contract.js +10 -1
- package/dist/contracts/accounts.contract.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +62 -1
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +830 -93
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +12 -12
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/ci.contract.d.ts +22 -4
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +38 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +2 -0
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts +23 -0
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js +6 -1
- package/dist/contracts/endpoints.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +12 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +2 -0
- package/dist/contracts/needs.contract.d.ts.map +1 -1
- package/dist/contracts/providers.contract.d.ts +4 -0
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +143 -124
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts +37 -0
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +20 -1
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +4 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +31 -4
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +20 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +102 -464
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +2 -12
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/translator.contract.d.ts +7 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.js.map +1 -1
- package/dist/contracts/webext.contract.d.ts +38 -0
- package/dist/contracts/webext.contract.d.ts.map +1 -1
- package/dist/contracts/webext.contract.js +3 -0
- package/dist/contracts/webext.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +1 -133
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +1 -37
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +21 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +11 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-words.d.ts +1 -0
- package/dist/events/agent-words.d.ts.map +1 -1
- package/dist/events/agent-words.js +1 -1
- package/dist/events/agent-words.js.map +1 -1
- package/dist/events/land-breakage.d.ts.map +1 -1
- package/dist/events/land-breakage.js.map +1 -1
- package/dist/events/resume.d.ts +6 -1
- package/dist/events/resume.d.ts.map +1 -1
- package/dist/events/resume.js +17 -10
- package/dist/events/resume.js.map +1 -1
- package/dist/events/sandbox-notice.d.ts +183 -0
- package/dist/events/sandbox-notice.d.ts.map +1 -0
- package/dist/events/sandbox-notice.js +56 -0
- package/dist/events/sandbox-notice.js.map +1 -0
- package/dist/events/system-events.d.ts +50 -18
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +4 -2
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +24 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +3 -1
- package/dist/events/transcript.js.map +1 -1
- package/dist/front/generated/browser-wire.d.ts +13 -0
- package/dist/front/generated/browser-wire.d.ts.map +1 -1
- package/dist/front/generated/wire.d.ts +4 -0
- package/dist/front/generated/wire.d.ts.map +1 -1
- package/dist/ids/conversation-ids.d.ts +1 -3
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +1 -3
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/ids/project-dir.d.ts +6 -0
- package/dist/ids/project-dir.d.ts.map +1 -0
- package/dist/ids/project-dir.js +43 -0
- package/dist/ids/project-dir.js.map +1 -0
- package/dist/index.d.ts +11543 -11039
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +2 -2
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/model-roles.d.ts +1 -9
- package/dist/models/model-roles.d.ts.map +1 -1
- package/dist/models/model-roles.js +1 -9
- package/dist/models/model-roles.js.map +1 -1
- package/dist/policy/command-classes.d.ts.map +1 -1
- package/dist/policy/command-classes.js +52 -3
- package/dist/policy/command-classes.js.map +1 -1
- package/dist/policy/safety-policy.d.ts +7 -1
- package/dist/policy/safety-policy.d.ts.map +1 -1
- package/dist/policy/safety-policy.js +2 -0
- package/dist/policy/safety-policy.js.map +1 -1
- package/dist/policy/secret-hosts.d.ts +13 -0
- package/dist/policy/secret-hosts.d.ts.map +1 -0
- package/dist/policy/secret-hosts.js +41 -0
- package/dist/policy/secret-hosts.js.map +1 -0
- package/dist/policy/turned-away.d.ts.map +1 -1
- package/dist/policy/turned-away.js +1 -0
- package/dist/policy/turned-away.js.map +1 -1
- package/dist/protocol/raw-routes.d.ts +12 -0
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +5 -0
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/routes.d.ts +2 -6
- package/dist/protocol/routes.d.ts.map +1 -1
- package/dist/protocol/routes.js +2 -3
- package/dist/protocol/routes.js.map +1 -1
- package/dist/protocol/stream-of.d.ts +6 -0
- package/dist/protocol/stream-of.d.ts.map +1 -0
- package/dist/protocol/stream-of.js +4 -0
- package/dist/protocol/stream-of.js.map +1 -0
- package/dist/protocol/vitals.d.ts +8 -0
- package/dist/protocol/vitals.d.ts.map +1 -0
- package/dist/protocol/vitals.js +16 -0
- package/dist/protocol/vitals.js.map +1 -0
- package/dist/schemas/activity.js +1 -1
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agent.d.ts +25 -2
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +14 -3
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +67 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +36 -0
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +20 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +2 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +34 -4
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/ci.d.ts +69 -9
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +29 -8
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/context-trim.d.ts +5 -1
- package/dist/schemas/context-trim.d.ts.map +1 -1
- package/dist/schemas/context-trim.js +8 -4
- package/dist/schemas/context-trim.js.map +1 -1
- package/dist/schemas/devices.d.ts +23 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +16 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/environment.d.ts +48 -0
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +15 -0
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +24 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +13 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +11 -1
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +2 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +2 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +4 -0
- package/dist/schemas/keep-warm.d.ts.map +1 -1
- package/dist/schemas/keep-warm.js +15 -5
- package/dist/schemas/keep-warm.js.map +1 -1
- package/dist/schemas/loopback-catch.d.ts +33 -0
- package/dist/schemas/loopback-catch.d.ts.map +1 -0
- package/dist/schemas/loopback-catch.js +20 -0
- package/dist/schemas/loopback-catch.js.map +1 -0
- package/dist/schemas/maintenance.js +1 -1
- package/dist/schemas/maintenance.js.map +1 -1
- package/dist/schemas/needs.d.ts +3 -0
- package/dist/schemas/needs.d.ts.map +1 -1
- package/dist/schemas/needs.js +10 -3
- package/dist/schemas/needs.js.map +1 -1
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +6 -3
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +87 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +20 -0
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/secrets.d.ts +66 -0
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js +50 -0
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +2 -2
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +2 -0
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +42 -4
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +26 -8
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/terminal.d.ts +2 -3
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js +12 -4
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/webext.d.ts +3 -0
- package/dist/schemas/webext.d.ts.map +1 -1
- package/dist/schemas/webext.js +1 -0
- package/dist/schemas/webext.js.map +1 -1
- package/dist/schemas/workflows.js +3 -3
- package/dist/schemas/workflows.js.map +1 -1
- package/dist/schemas/workspace/workspace-tree.d.ts +2 -56
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +7 -15
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +12 -8
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +7 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +0 -3
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +0 -1
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +0 -5
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +0 -6
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/documents.d.ts +3 -0
- package/dist/text/documents.d.ts.map +1 -1
- package/dist/text/documents.js +14 -0
- package/dist/text/documents.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +4 -3
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +137 -31
- package/dist/text/transcript-fold.js.map +1 -1
- package/dist/webext/index.d.ts +1 -0
- package/dist/webext/index.d.ts.map +1 -1
- package/dist/webext/index.js +1 -0
- package/dist/webext/index.js.map +1 -1
- package/package.json +5 -5
- package/src/chores/chores.ts +2 -2
- package/src/chores/probes.ts +1 -1
- package/src/contracts/accounts.contract.ts +12 -0
- package/src/contracts/agents.contract.ts +13 -13
- package/src/contracts/device.contract.ts +5 -0
- package/src/contracts/endpoints.contract.ts +20 -4
- package/src/contracts/secrets.contract.ts +28 -0
- package/src/contracts/settings.contract.ts +29 -0
- package/src/contracts/system.contract.ts +5 -14
- package/src/contracts/translator.contract.ts +2 -1
- package/src/contracts/webext.contract.ts +7 -1
- package/src/contracts/workspace.contract.ts +0 -49
- package/src/events/agent-events.ts +18 -1
- package/src/events/agent-words.ts +4 -1
- package/src/events/land-breakage.ts +3 -4
- package/src/events/resume.test.ts +13 -2
- package/src/events/resume.ts +25 -12
- package/src/events/sandbox-notice.ts +92 -0
- package/src/events/system-events.ts +12 -5
- package/src/events/transcript.ts +7 -1
- package/src/events/verify-nudge.ts +3 -3
- package/src/front/generated/browser-wire.json +95 -0
- package/src/front/generated/browser-wire.ts +36 -0
- package/src/front/generated/front-wire.json +25 -0
- package/src/front/generated/wire.ts +2 -2
- package/src/front/wire-manifests.test.ts +7 -0
- package/src/ids/conversation-ids.test.ts +2 -26
- package/src/ids/conversation-ids.ts +6 -12
- package/src/ids/project-dir.fixture.json +65 -0
- package/src/ids/project-dir.test.ts +60 -0
- package/src/ids/project-dir.ts +70 -0
- package/src/index.ts +13 -4
- package/src/models/agent-catalog.test.ts +7 -1
- package/src/models/agent-runtimes.ts +7 -5
- package/src/models/model-roles.ts +1 -9
- package/src/policy/command-classes.test.ts +39 -0
- package/src/policy/command-classes.ts +70 -3
- package/src/policy/safety-policy.ts +10 -0
- package/src/policy/secret-hosts.test.ts +89 -0
- package/src/policy/secret-hosts.ts +73 -0
- package/src/policy/turned-away.ts +1 -0
- package/src/protocol/raw-routes.ts +8 -1
- package/src/protocol/routes.test.ts +6 -6
- package/src/protocol/routes.ts +2 -9
- package/src/protocol/stream-of.ts +13 -0
- package/src/protocol/vitals.test.ts +46 -0
- package/src/protocol/vitals.ts +38 -0
- package/src/schemas/activity.ts +1 -1
- package/src/schemas/agent.ts +20 -3
- package/src/schemas/agents.ts +58 -4
- package/src/schemas/automations.ts +2 -2
- package/src/schemas/capabilities.ts +44 -7
- package/src/schemas/ci.ts +56 -11
- package/src/schemas/context-trim.ts +11 -6
- package/src/schemas/devices.ts +38 -2
- package/src/schemas/environment.ts +39 -0
- package/src/schemas/git/git.ts +20 -2
- package/src/schemas/hosts.ts +4 -1
- package/src/schemas/keep-warm.ts +19 -6
- package/src/schemas/loopback-catch.ts +45 -0
- package/src/schemas/maintenance.ts +1 -1
- package/src/schemas/needs.ts +12 -3
- package/src/schemas/personas.ts +8 -3
- package/src/schemas/providers/provider-oauth.ts +35 -0
- package/src/schemas/secrets.ts +70 -0
- package/src/schemas/settings-history.ts +3 -0
- package/src/schemas/settings.test.ts +19 -1
- package/src/schemas/settings.ts +59 -16
- package/src/schemas/speaker.ts +5 -5
- package/src/schemas/terminal.ts +19 -6
- package/src/schemas/webext.ts +3 -0
- package/src/schemas/workflows.ts +5 -5
- package/src/schemas/workspace/workspace-tree.ts +12 -20
- package/src/state/fix-attempt-plan.test.ts +1 -1
- package/src/state/fix-stance.ts +1 -1
- package/src/state/history-state.ts +14 -0
- package/src/state/runtime-state.ts +0 -3
- package/src/state/workspace-state.ts +0 -6
- package/src/text/documents.test.ts +25 -1
- package/src/text/documents.ts +24 -0
- package/src/text/title.ts +1 -1
- package/src/text/transcript-fold.test.ts +218 -25
- package/src/text/transcript-fold.ts +166 -45
- package/src/webext/index.ts +1 -0
- package/dist/schemas/workspace/push-checks.d.ts +0 -222
- package/dist/schemas/workspace/push-checks.d.ts.map +0 -1
- package/dist/schemas/workspace/push-checks.js +0 -97
- package/dist/schemas/workspace/push-checks.js.map +0 -1
- package/dist/state/red-streak.d.ts +0 -7
- package/dist/state/red-streak.d.ts.map +0 -1
- package/dist/state/red-streak.js +0 -11
- package/dist/state/red-streak.js.map +0 -1
- package/src/schemas/workspace/push-checks.test.ts +0 -28
- package/src/schemas/workspace/push-checks.ts +0 -155
- package/src/state/red-streak.test.ts +0 -28
- package/src/state/red-streak.ts +0 -28
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { eventIterator, oc } from "@orpc/contract";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { SANDBOX_ROUTE_NAMES,
|
|
3
|
+
import { SANDBOX_ROUTE_NAMES, SANDBOX_ROUTES, sandboxRouteName, sandboxRouteFingerprints } from "../index.js";
|
|
4
4
|
import { procedure } from "./route-meta.js";
|
|
5
5
|
import { contractRoutes, routeNameForRequest, routeShapes, servedRoute, streamOf } from "./routes.js";
|
|
6
6
|
|
|
@@ -231,7 +231,7 @@ describe(`the real sandbox contract`, () => {
|
|
|
231
231
|
it(`fingerprints every route, streams included`, () => {
|
|
232
232
|
// Nothing may opt out: a route with no fingerprint is a route the browser can never tell has drifted, and the
|
|
233
233
|
// streaming ones (system.events, agent.attach, the exit dials) carry the frames a stale daemon breaks first.
|
|
234
|
-
expect(SANDBOX_ROUTE_NAMES.filter((name) => !(name in
|
|
234
|
+
expect(SANDBOX_ROUTE_NAMES.filter((name) => !(name in sandboxRouteFingerprints()))).toEqual([]);
|
|
235
235
|
});
|
|
236
236
|
|
|
237
237
|
it(`fingerprints the streaming routes eventIterator used to hide`, () => {
|
|
@@ -250,7 +250,7 @@ describe(`the real sandbox contract`, () => {
|
|
|
250
250
|
`system.runDeviceAgentFlow`,
|
|
251
251
|
`vpn.connect`,
|
|
252
252
|
]
|
|
253
|
-
.filter((name) => name in
|
|
253
|
+
.filter((name) => name in sandboxRouteFingerprints())
|
|
254
254
|
.toSorted(),
|
|
255
255
|
).toEqual([
|
|
256
256
|
`agent.attach`,
|
|
@@ -269,8 +269,8 @@ describe(`the real sandbox contract`, () => {
|
|
|
269
269
|
});
|
|
270
270
|
|
|
271
271
|
it(`fingerprints every route exactly once`, () => {
|
|
272
|
-
expect(Object.keys(
|
|
273
|
-
expect(Object.keys(
|
|
272
|
+
expect(Object.keys(sandboxRouteFingerprints()).every((name) => SANDBOX_ROUTE_NAMES.includes(name))).toBe(true);
|
|
273
|
+
expect(Object.keys(sandboxRouteFingerprints()).length).toBe(SANDBOX_ROUTE_NAMES.length);
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
it(`derives a route table with no duplicate names`, () => {
|
|
@@ -287,7 +287,7 @@ describe(`the real sandbox contract`, () => {
|
|
|
287
287
|
// differently has to read as drift rather than as a machine that runs nothing.
|
|
288
288
|
it(`fingerprints the fleet view the device buttons are gated on`, () => {
|
|
289
289
|
expect(sandboxRouteName(`GET`, `/system/devices`)).toBe(`system.devices`);
|
|
290
|
-
expect(
|
|
290
|
+
expect(sandboxRouteFingerprints()[`system.devices`]).toEqual(expect.any(String));
|
|
291
291
|
});
|
|
292
292
|
|
|
293
293
|
it(`resolves a known concrete request back to its contract name`, () => {
|
package/src/protocol/routes.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import { eventIterator } from "@orpc/contract";
|
|
2
1
|
import { z } from "zod";
|
|
3
2
|
import type { RouteMeta } from "./route-meta.js";
|
|
3
|
+
import { FRAME, streamOf } from "./stream-of.js";
|
|
4
4
|
|
|
5
5
|
// Named route surface of the daemon's contract (`<group>.<route>`), derived automatically so nothing here is
|
|
6
6
|
// hand-maintained. The daemon advertises which routes it implements (the /events hello frame); the browser diffs that
|
|
7
7
|
// against its own contract so an old daemon's gap is a named feature check, not a silent 404.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// standard-schema validator holding no reachable inner schema, so a stream's payload would be unfingerprintable and its
|
|
11
|
-
// drift invisible. The frame schema rides along under a symbol, which oRPC never reads and JSON never serializes.
|
|
12
|
-
// Typed as a plain `symbol`, not the inferred unique one: a unique symbol would ride into every contract's inferred
|
|
13
|
-
// type and break declaration emit on a local name nothing outside can refer to.
|
|
14
|
-
const FRAME: symbol = Symbol.for("intentic.contract.frame");
|
|
15
|
-
|
|
16
|
-
export const streamOf = <T extends z.ZodType>(frame: T) => Object.assign(eventIterator(frame), { [FRAME]: frame });
|
|
9
|
+
export { streamOf };
|
|
17
10
|
|
|
18
11
|
// The frame schema behind a streamed route's validator, or undefined for an ordinary request/response schema.
|
|
19
12
|
const frameOf = (schema: unknown): z.ZodType | undefined =>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { eventIterator } from "@orpc/contract";
|
|
2
|
+
import type * as z from "zod";
|
|
3
|
+
|
|
4
|
+
// Every streamed route declares its frames through this, never oRPC's `eventIterator` directly: that returns an opaque
|
|
5
|
+
// standard-schema validator holding no reachable inner schema, so a stream's payload would be unfingerprintable and its
|
|
6
|
+
// drift invisible. The frame schema rides along under a symbol, which oRPC never reads and JSON never serializes.
|
|
7
|
+
// Typed as a plain `symbol`, not the inferred unique one: a unique symbol would ride into every contract's inferred
|
|
8
|
+
// type and break declaration emit on a local name nothing outside can refer to.
|
|
9
|
+
// Its own module, apart from routes.ts, because the browser extension's contract streams too and routes.ts reaches
|
|
10
|
+
// zod's namespace at runtime, which the extension's bundle budget cannot carry.
|
|
11
|
+
export const FRAME: symbol = Symbol.for("intentic.contract.frame");
|
|
12
|
+
|
|
13
|
+
export const streamOf = <T extends z.ZodType>(frame: T) => Object.assign(eventIterator(frame), { [FRAME]: frame });
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { RAW_ROUTES, rawRoutePath } from "./raw-routes.js";
|
|
2
|
+
import { parseVitals, type SandboxVitals, VITALS_PATH } from "./vitals.js";
|
|
3
|
+
|
|
4
|
+
// The JSON browser-wire's own test pins (`vitals_are_the_camel_case_json_the_editor_parses_with_null_for_what_is_unknown`):
|
|
5
|
+
// what the front writes, the editor reads.
|
|
6
|
+
const RUST_GOLDEN_JSON = `{"node":"restarting","lagMs":null,"restarts":2,"uptimeS":3600,"pressure":{"cpu":12.34,"memory":0.0,"io":1.5}}`;
|
|
7
|
+
|
|
8
|
+
const up: SandboxVitals = { node: "up", lagMs: 12, restarts: 0, uptimeS: 60, pressure: { cpu: 0.29, memory: 0.49, io: 0.88 } };
|
|
9
|
+
|
|
10
|
+
it("reads the front's own answer as it writes it", () => {
|
|
11
|
+
expect(parseVitals(JSON.parse(RUST_GOLDEN_JSON))).toEqual({
|
|
12
|
+
node: "restarting",
|
|
13
|
+
lagMs: null,
|
|
14
|
+
restarts: 2,
|
|
15
|
+
uptimeS: 3600,
|
|
16
|
+
pressure: { cpu: 12.34, memory: 0, io: 1.5 },
|
|
17
|
+
});
|
|
18
|
+
expect(parseVitals(up)).toEqual(up);
|
|
19
|
+
expect(parseVitals({ ...up, node: "starting" })?.node).toBe("starting");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("reads a lag or pressure it cannot make out as unknown, and keeps the rest", () => {
|
|
23
|
+
const { lagMs: _lag, pressure: _pressure, ...bare } = up;
|
|
24
|
+
expect(parseVitals(bare)).toEqual({ ...bare, lagMs: null, pressure: null });
|
|
25
|
+
expect(parseVitals({ ...up, lagMs: -1, pressure: { cpu: 1 } })).toEqual({ ...up, lagMs: null, pressure: null });
|
|
26
|
+
// A later front's extra fields are not this reader's business.
|
|
27
|
+
expect(parseVitals({ ...up, since: "later" })).toEqual(up);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("is undefined for anything that is not the front's answer", () => {
|
|
31
|
+
// An older sandbox forwards the path to Node, which answers 404 as JSON, or a preview's HTML page.
|
|
32
|
+
expect(parseVitals({ error: "Not Found" })).toBeUndefined();
|
|
33
|
+
expect(parseVitals("<!doctype html><title>Not found</title>")).toBeUndefined();
|
|
34
|
+
expect(parseVitals(null)).toBeUndefined();
|
|
35
|
+
expect(parseVitals(42)).toBeUndefined();
|
|
36
|
+
expect(parseVitals([up])).toBeUndefined();
|
|
37
|
+
expect(parseVitals({ ...up, node: "crashed" })).toBeUndefined();
|
|
38
|
+
expect(parseVitals({ ...up, restarts: -1 })).toBeUndefined();
|
|
39
|
+
expect(parseVitals({ ...up, uptimeS: 1.5 })).toBeUndefined();
|
|
40
|
+
expect(parseVitals({ ...up, restarts: "2" })).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("is a route the front serves itself, with no credential, before the daemon boots", () => {
|
|
44
|
+
expect(rawRoutePath("GET /system/vitals")).toBe(VITALS_PATH);
|
|
45
|
+
expect(RAW_ROUTES["GET /system/vitals"]).toEqual({ auth: "door", beforeBoot: true, front: true });
|
|
46
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// The sandbox's proof of life, which intentic-front answers itself on the daemon's own address and never forwards to
|
|
2
|
+
// Node: whether Node is up, how long its event loop takes to answer the front's ping, how often the front restarted it,
|
|
3
|
+
// how long the container has run, and its pressure stall. The daemon's heartbeat on /events rides Node's event loop,
|
|
4
|
+
// so a busy sandbox and a dead one look alike from there; this route tells them apart. The Rust crate `browser-wire`
|
|
5
|
+
// is the definition (SandboxVitals), and wire-manifests.test.ts holds the path here to the manifest it writes.
|
|
6
|
+
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { NodeLink, Pressure, SandboxVitals } from "../front/generated/browser-wire.js";
|
|
9
|
+
|
|
10
|
+
export type { NodeLink, Pressure, SandboxVitals };
|
|
11
|
+
|
|
12
|
+
// A plain GET any origin may read, answered by the front whatever state Node is in. The route is registered as
|
|
13
|
+
// `front: true` in raw-routes.ts, so the daemon serves none.
|
|
14
|
+
export const VITALS_PATH = "/system/vitals";
|
|
15
|
+
|
|
16
|
+
// Every state, so a new one in the Rust enum fails this file's typecheck until it is read here too.
|
|
17
|
+
const NODE_LINKS = { starting: "starting", up: "up", restarting: "restarting" } as const satisfies { readonly [K in NodeLink]: K };
|
|
18
|
+
|
|
19
|
+
const PressureSchema = z.object({ cpu: z.number(), memory: z.number(), io: z.number() });
|
|
20
|
+
|
|
21
|
+
// Lenient where a figure is only a detail: a lag or a pressure it cannot read is unknown, and the rest still stands.
|
|
22
|
+
const VitalsSchema = z.object({
|
|
23
|
+
node: z.enum(NODE_LINKS),
|
|
24
|
+
lagMs: z.number().nonnegative().nullable().catch(null),
|
|
25
|
+
restarts: z.number().int().nonnegative(),
|
|
26
|
+
uptimeS: z.number().int().nonnegative(),
|
|
27
|
+
pressure: PressureSchema.nullable().catch(null),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// A response body as `Response.json()` hands it over: any JSON value, not yet known to be the front's answer.
|
|
31
|
+
type JsonBody = string | number | boolean | null | readonly JsonBody[] | { readonly [key: string]: JsonBody };
|
|
32
|
+
|
|
33
|
+
// The front's answer, or undefined for anything else: an older sandbox forwards the path to Node, which answers 404 or
|
|
34
|
+
// an HTML page, and an edge in between may answer for a box that is not there.
|
|
35
|
+
export const parseVitals = (body: JsonBody): SandboxVitals | undefined => {
|
|
36
|
+
const parsed = VitalsSchema.safeParse(body);
|
|
37
|
+
return parsed.success ? parsed.data : undefined;
|
|
38
|
+
};
|
package/src/schemas/activity.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const ActivityEventSchema = z.object({
|
|
|
26
26
|
type: z
|
|
27
27
|
.string()
|
|
28
28
|
.describe(
|
|
29
|
-
"Exactly what happened: a message received or sent, a reaction, a turn starting or ending, a rule doing something. A rule that ran and passed says nothing here, because a feed of
|
|
29
|
+
"Exactly what happened: a message received or sent, a reaction, a turn starting or ending, a rule doing something. A rule that ran and passed says nothing here, because a feed of passes is one the eye learns to skip.",
|
|
30
30
|
),
|
|
31
31
|
channelId: z.string().optional().describe("Which channel or thread it happened in."),
|
|
32
32
|
// Inbound author display name.
|
package/src/schemas/agent.ts
CHANGED
|
@@ -131,7 +131,7 @@ export type ForkedFrom = z.infer<typeof ForkedFromSchema>;
|
|
|
131
131
|
const AgentTurnFieldsSchema = z.object({
|
|
132
132
|
prompt: z.string().describe("What to say to the agent. May be empty if you are only attaching files."),
|
|
133
133
|
errand: TurnErrandSchema.optional().describe(
|
|
134
|
-
"What the words are for, when the app or the sandbox composed them rather than a person typing them: a land conflict to resolve, a
|
|
134
|
+
"What the words are for, when the app or the sandbox composed them rather than a person typing them: a land conflict to resolve, a failed CI run to fix. Shown as the sandbox's words, not yours. Leave it out for your own words.",
|
|
135
135
|
),
|
|
136
136
|
// Minted by the sender so a send whose answer was lost can be sent again without being delivered twice.
|
|
137
137
|
messageId: z
|
|
@@ -304,6 +304,16 @@ const AgentTurnFieldsSchema = z.object({
|
|
|
304
304
|
editorContext: EditorContextSchema.optional().describe(
|
|
305
305
|
'What the user has open in their editor, folded into the prompt so that pointing words like "this" resolve.',
|
|
306
306
|
),
|
|
307
|
+
// The composer's scheduled send: a person who knows the account is spent books the message for its reopen instead of
|
|
308
|
+
// sending it into a refusal. Held in the conversation's queue, never started early; the queue's `until` is this.
|
|
309
|
+
sendAt: z
|
|
310
|
+
.number()
|
|
311
|
+
.int()
|
|
312
|
+
.positive()
|
|
313
|
+
.optional()
|
|
314
|
+
.describe(
|
|
315
|
+
"Hold this message until then (epoch milliseconds) instead of starting a turn now, for an allowance you know is spent. It waits in the conversation's queue, where it can be sent early, reworded or removed, and goes by itself at that instant. Ignored when already past, or when a turn is running.",
|
|
316
|
+
),
|
|
307
317
|
});
|
|
308
318
|
export const AgentTurnSchema = AgentTurnFieldsSchema
|
|
309
319
|
// An attachment-only send (no text) is legal; an entirely empty turn is not.
|
|
@@ -473,7 +483,7 @@ export const QueuedMessageSchema = z.object({
|
|
|
473
483
|
});
|
|
474
484
|
export type QueuedMessage = z.infer<typeof QueuedMessageSchema>;
|
|
475
485
|
// Why a queue holds its messages rather than letting them go when the conversation is free.
|
|
476
|
-
export const QueuePauseSchema = z.enum(["stopped", "refused"]);
|
|
486
|
+
export const QueuePauseSchema = z.enum(["stopped", "refused", "scheduled"]);
|
|
477
487
|
export type QueuePause = z.infer<typeof QueuePauseSchema>;
|
|
478
488
|
// What waits for a conversation's next turn: messages that arrived while a turn that could not take them ran, and
|
|
479
489
|
// held ones. Conversation state, the same for every window and kept across a restart.
|
|
@@ -481,13 +491,20 @@ export const ConversationQueueSchema = z.object({
|
|
|
481
491
|
items: z.array(QueuedMessageSchema).describe("What waits, in the order it goes out."),
|
|
482
492
|
revision: z.number().int().nonnegative().describe("Moves with every change to the queue, so of two copies the higher is the newer."),
|
|
483
493
|
paused: QueuePauseSchema.optional().describe(
|
|
484
|
-
"Why nothing goes out by itself: somebody stopped the turn,
|
|
494
|
+
"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for later. Resuming lets them go, and so does sending another message unless they are scheduled.",
|
|
485
495
|
),
|
|
496
|
+
until: z
|
|
497
|
+
.number()
|
|
498
|
+
.optional()
|
|
499
|
+
.describe("When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`."),
|
|
486
500
|
});
|
|
487
501
|
export type ConversationQueue = z.infer<typeof ConversationQueueSchema>;
|
|
488
502
|
// What resuming a queue did: started a turn with what waited, when nothing else ran.
|
|
489
503
|
export const QueueResumedSchema = z.object({
|
|
490
504
|
run: z.string().optional().describe("The turn the waiting messages started. Absent when a turn was already running, and they go after it."),
|
|
505
|
+
queue: ConversationQueueSchema.optional().describe(
|
|
506
|
+
"The queue the release left: what still waits once the turn took what it could. Absent from sandboxes older than the field.",
|
|
507
|
+
),
|
|
491
508
|
});
|
|
492
509
|
export type QueueResumed = z.infer<typeof QueueResumedSchema>;
|
|
493
510
|
// Attaches to a conversation's newest run (live, or finished within retention); no cursor to resume from, the head
|
package/src/schemas/agents.ts
CHANGED
|
@@ -89,7 +89,7 @@ export const UnfinishedWorkSchema = z.object({
|
|
|
89
89
|
})
|
|
90
90
|
.optional()
|
|
91
91
|
.describe("The agent's own checklist where that turn left it. Absent for a conversation that kept no list."),
|
|
92
|
-
// Name of the `turn.ending` check still
|
|
92
|
+
// Name of the `turn.ending` check still failing when the turn ended. Retired with that check: nothing writes it now, and
|
|
93
93
|
// it stays so entries written before still read.
|
|
94
94
|
check: z
|
|
95
95
|
.string()
|
|
@@ -98,7 +98,7 @@ export const UnfinishedWorkSchema = z.object({
|
|
|
98
98
|
});
|
|
99
99
|
export type UnfinishedWork = z.infer<typeof UnfinishedWorkSchema>;
|
|
100
100
|
// What the last turn showed of its own work, read off its tool calls and never asked of the model: the record a card
|
|
101
|
-
// badges instead of sending the turn back to prove anything. CI checks what the owner pushes (
|
|
101
|
+
// badges instead of sending the turn back to prove anything. CI checks what the owner pushes (schemas/ci.ts).
|
|
102
102
|
export const TurnProofSchema = z.object({
|
|
103
103
|
at: z.number().describe("When the turn that left this ended, in milliseconds."),
|
|
104
104
|
verification: z
|
|
@@ -432,6 +432,32 @@ export const AgentSummarySchema = z.object({
|
|
|
432
432
|
"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.",
|
|
433
433
|
),
|
|
434
434
|
attention: AgentAttentionSchema.describe("Which kinds of waiting-for-you it is doing."),
|
|
435
|
+
// Beside `attention.permission`, which says only that one waits: the id an answer names and what it asks, so a card
|
|
436
|
+
// can offer the answer itself instead of a trip to the chat. The oldest one, while its turn runs.
|
|
437
|
+
permissionAsk: z
|
|
438
|
+
.object({
|
|
439
|
+
requestId: z.string().describe("Which request this is: the id an answer to it names."),
|
|
440
|
+
ask: z.string().describe("What it asks to do, on one line: the runtime's own sentence, else the tool's short name."),
|
|
441
|
+
})
|
|
442
|
+
.optional()
|
|
443
|
+
.describe(
|
|
444
|
+
"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.",
|
|
445
|
+
),
|
|
446
|
+
// Kept on the record until a land goes through, unlike `failure`, which the next turn clears: a land that broke
|
|
447
|
+
// leaves the work stuck on its branch however many turns run after it.
|
|
448
|
+
landFailure: z
|
|
449
|
+
.object({
|
|
450
|
+
reason: z.string().describe("Why it failed, in the words it failed with."),
|
|
451
|
+
code: z
|
|
452
|
+
.string()
|
|
453
|
+
.optional()
|
|
454
|
+
.describe("Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise."),
|
|
455
|
+
at: z.number().describe("When it failed, in milliseconds."),
|
|
456
|
+
})
|
|
457
|
+
.optional()
|
|
458
|
+
.describe(
|
|
459
|
+
"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.",
|
|
460
|
+
),
|
|
435
461
|
// Only the causes that still hold, re-read live (conversations/land/standing.ts), never the stored report's own list: a
|
|
436
462
|
// blocker the user has since cleared is not something to offer them an action about.
|
|
437
463
|
conflictCauses: z
|
|
@@ -452,8 +478,8 @@ export const AgentSummarySchema = z.object({
|
|
|
452
478
|
// Completed turns and lifetime tool calls, the card's msgs/tools counters.
|
|
453
479
|
turns: z.number().optional().describe("Turns it has finished."),
|
|
454
480
|
toolUses: z.number().optional().describe("Tools it has used, over its whole life."),
|
|
455
|
-
// `running` reads the live subagent
|
|
456
|
-
//
|
|
481
|
+
// `running` reads the live subagent roster, `total` counts on the agent's own entry; a child's spend is its own,
|
|
482
|
+
// never folded into the parent's cost.
|
|
457
483
|
subagents: z
|
|
458
484
|
.object({
|
|
459
485
|
running: z.number().describe("Subagents working right now."),
|
|
@@ -479,6 +505,25 @@ export const AgentSummarySchema = z.object({
|
|
|
479
505
|
.object({
|
|
480
506
|
landed: z.number().describe("Paths this conversation merged in."),
|
|
481
507
|
present: z.number().describe("How many of them are still there, either pending or committed."),
|
|
508
|
+
// Read off who acted on the tree between the land and the moment it was found missing; only a sole candidate
|
|
509
|
+
// is named, so an unattributed removal says nothing rather than guessing.
|
|
510
|
+
removedBy: z
|
|
511
|
+
.discriminatedUnion("kind", [
|
|
512
|
+
z.object({
|
|
513
|
+
kind: z.literal("agent"),
|
|
514
|
+
id: z.string().describe("The conversation that took them out."),
|
|
515
|
+
title: z.string().optional().describe("What that conversation is called."),
|
|
516
|
+
}),
|
|
517
|
+
z.object({
|
|
518
|
+
kind: z.literal("person"),
|
|
519
|
+
email: z.string().optional().describe("Who it was, as the sandbox verified them. Absent when the request carried no identity."),
|
|
520
|
+
name: z.string().optional().describe("What to call them, when their sign-in carried a name."),
|
|
521
|
+
}),
|
|
522
|
+
])
|
|
523
|
+
.optional()
|
|
524
|
+
.describe(
|
|
525
|
+
"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.",
|
|
526
|
+
),
|
|
482
527
|
})
|
|
483
528
|
.optional()
|
|
484
529
|
.describe(
|
|
@@ -593,6 +638,12 @@ export const awaitsWake = (agent: { readonly awaitingWake?: boolean | undefined
|
|
|
593
638
|
export type AgentWatch = NonNullable<AgentSummary["watches"]>[number];
|
|
594
639
|
// One background job as a card carries it.
|
|
595
640
|
export type AgentJob = NonNullable<AgentSummary["jobs"]>[number];
|
|
641
|
+
// Who took landed work out of the workspace, as the card names them.
|
|
642
|
+
export type LandedRemover = NonNullable<NonNullable<AgentSummary["landedPresence"]>["removedBy"]>;
|
|
643
|
+
// A land that broke, kept on the card until one goes through.
|
|
644
|
+
export type LandFailure = NonNullable<AgentSummary["landFailure"]>;
|
|
645
|
+
// The permission a running turn waits on, as a card can answer it.
|
|
646
|
+
export type WaitingPermission = NonNullable<AgentSummary["permissionAsk"]>;
|
|
596
647
|
// AgentsListSchema is declared later, after AutomationApprovalSchema, since the fleet list carries held wakes and zod
|
|
597
648
|
// needs that type declared first.
|
|
598
649
|
export const AgentIdSchema = z.object({ id: z.string().min(1).describe("Which conversation.") });
|
|
@@ -623,6 +674,9 @@ export const AgentTranscriptQuerySchema = AgentIdSchema.extend({
|
|
|
623
674
|
export const AgentToolChildrenQuerySchema = AgentIdSchema.extend({
|
|
624
675
|
toolId: z.string().min(1).describe("Which tool call, by the id its card carries."),
|
|
625
676
|
});
|
|
677
|
+
export const AgentSubagentQuerySchema = AgentIdSchema.extend({
|
|
678
|
+
subagentId: z.string().min(1).describe("Which subagent this conversation's runtime ran in-process, by the id of the call that started it."),
|
|
679
|
+
});
|
|
626
680
|
// Absent `ids` archives every archivable finished agent (the lane's "Clear"); unarchive always names its own ids.
|
|
627
681
|
export const AgentIdsSchema = z.object({ ids: z.array(z.string().min(1)).min(1).max(500).describe("Which conversations.") });
|
|
628
682
|
// What actually moved, not the roster afterward: two archives finishing at once would otherwise let a slower snapshot
|
|
@@ -29,7 +29,7 @@ import { ZoneSchema } from "../time/zone.js";
|
|
|
29
29
|
// turn.settled: fires once per isolated turn regardless of outcome, while the diff is still on screen
|
|
30
30
|
// agent.landed: fires only once work reaches the main tree, including a later manual Land
|
|
31
31
|
// deps.broken/deps.fixed: retired with the check that ran after every land (2026-09-27). Nothing emits them any more;
|
|
32
|
-
// they stay so an automation written for them still reads, and it never fires. Main's
|
|
32
|
+
// they stay so an automation written for them still reads, and it never fires. Main's failing CI is the `ci` listener's.
|
|
33
33
|
export const WorkspaceEventKindSchema = z.enum(["turn.settled", "agent.landed", "deps.broken", "deps.fixed"]);
|
|
34
34
|
export type WorkspaceEventKind = z.infer<typeof WorkspaceEventKindSchema>;
|
|
35
35
|
// Payload of a workspace-triggered wake, delivered as JSON in $AUTOMATION_PAYLOAD and appended to the prompt.
|
|
@@ -45,7 +45,7 @@ export const WorkspaceEventSchema = z.object({
|
|
|
45
45
|
outcome: z.enum(["landed", "conflict", "ready", "idle", "error"]),
|
|
46
46
|
repos: z.array(z.object({ repo: z.string(), from: z.string(), dir: z.string() })),
|
|
47
47
|
// Present only for `deps.*` events: which project broke, the command and exit code, a bounded log tail, and
|
|
48
|
-
// `attempt` counting consecutive
|
|
48
|
+
// `attempt` counting consecutive failures since the last pass.
|
|
49
49
|
deps: z
|
|
50
50
|
.object({
|
|
51
51
|
project: z.string(),
|
|
@@ -244,7 +244,8 @@ export const LOCAL_MODEL_WINDOW_MAX = 1_048_576;
|
|
|
244
244
|
// against.
|
|
245
245
|
export const LOCAL_MODEL_KV_BYTES_PER_TOKEN = 65_536;
|
|
246
246
|
// What a rung honestly serves. `instant` is the one the connect view prefetches — it downloads in under a minute and
|
|
247
|
-
// cannot drive a full agent turn, so
|
|
247
|
+
// cannot drive a full agent turn, so its catalog row is helper-only (Model.helperOnly): the chat picker does not offer
|
|
248
|
+
// it and the daemon refuses a turn on it. `work` is what that view recommends for real use.
|
|
248
249
|
export type LocalModelTier = "instant" | "work";
|
|
249
250
|
export interface LocalModelChoice {
|
|
250
251
|
// Hugging Face owner/repo/file.gguf, exactly as LocalModelConfig.model carries it.
|
|
@@ -254,21 +255,57 @@ export interface LocalModelChoice {
|
|
|
254
255
|
// estimate all read this, so a model cannot be priced two ways.
|
|
255
256
|
readonly weightsBytes: number;
|
|
256
257
|
readonly tier: LocalModelTier;
|
|
258
|
+
// The repository commit the file is fetched at, a sha and never a branch: `main` moves, and a re-upload under the
|
|
259
|
+
// same name would change what a card promised without the card changing.
|
|
260
|
+
readonly revision: string;
|
|
261
|
+
// The file's own sha256 at that commit (its LFS oid). A download that does not hash to it is deleted, not served.
|
|
262
|
+
readonly sha256: string;
|
|
257
263
|
}
|
|
258
|
-
// The curated list, smallest first. ponytail:
|
|
264
|
+
// The curated list, smallest first. ponytail: sizes, revisions and digests are the Hugging Face API's answer for each
|
|
265
|
+
// file (`/api/models/<repo>/revision/main` for the sha, `/tree/<sha>` for the size and LFS oid), read 2026-09-29 — the
|
|
259
266
|
// hand-written labels that predated them overstated gemma-4 12B by 2x and Qwen3.8 27B by a third, which is the class of
|
|
260
|
-
// drift a machine-readable size exists to end.
|
|
267
|
+
// drift a machine-readable size exists to end. Bump a row's three together: each belongs to its revision.
|
|
261
268
|
export const LOCAL_MODELS: readonly LocalModelChoice[] = [
|
|
262
|
-
{
|
|
269
|
+
{
|
|
270
|
+
id: "unsloth/Qwen3.5-2B-GGUF/Qwen3.5-2B-Q4_K_M.gguf",
|
|
271
|
+
label: "Qwen3.5 2B",
|
|
272
|
+
weightsBytes: 1_280_835_840,
|
|
273
|
+
tier: "instant",
|
|
274
|
+
revision: "f6d5376be1edb4d416d56da11e5397a961aca8ae",
|
|
275
|
+
sha256: "aaf42c8b7c3cab2bf3d69c355048d4a0ee9973d48f16c731c0520ee914699223",
|
|
276
|
+
},
|
|
263
277
|
{
|
|
264
278
|
id: "unsloth/Phi-4-mini-instruct-GGUF/Phi-4-mini-instruct-Q4_K_M.gguf",
|
|
265
279
|
label: "Phi-4-mini 3.8B",
|
|
266
280
|
weightsBytes: 2_491_874_272,
|
|
267
281
|
tier: "work",
|
|
282
|
+
revision: "78eb92a46fc37e6b524df991ed9aca9bc6aa7b80",
|
|
283
|
+
sha256: "88c00229914083cd112853aab84ed51b87bdf6b9ce42f532d8c85c7c63b1730a",
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: "unsloth/Qwen3.5-9B-GGUF/Qwen3.5-9B-Q4_K_M.gguf",
|
|
287
|
+
label: "Qwen3.5 9B",
|
|
288
|
+
weightsBytes: 5_680_522_464,
|
|
289
|
+
tier: "work",
|
|
290
|
+
revision: "3885219b6810b007914f3a7950a8d1b469d598a5",
|
|
291
|
+
sha256: "03b74727a860a56338e042c4420bb3f04b2fec5734175f4cb9fa853daf52b7e8",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "unsloth/gemma-4-12b-it-GGUF/gemma-4-12b-it-Q4_K_M.gguf",
|
|
295
|
+
label: "Gemma 4 12B",
|
|
296
|
+
weightsBytes: 7_121_861_440,
|
|
297
|
+
tier: "work",
|
|
298
|
+
revision: "fc034cfff751157913579611efad8462ac1be606",
|
|
299
|
+
sha256: "0a270ec9fe6b34f4a0d33992b6135117b484ebc4766ab76b51d4ae8c457e4c42",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: "unsloth/Qwen3.8-27B-GGUF/Qwen3.8-27B-UD-Q4_K_M.gguf",
|
|
303
|
+
label: "Qwen3.8 27B",
|
|
304
|
+
weightsBytes: 16_464_440_224,
|
|
305
|
+
tier: "work",
|
|
306
|
+
revision: "4ca720788d1e01f1bff70c033e0d0028fd02e502",
|
|
307
|
+
sha256: "322e194ff79741c7baa497c240f677f54b201b0efab44ca8e50f122b39123482",
|
|
268
308
|
},
|
|
269
|
-
{ id: "unsloth/Qwen3.5-9B-GGUF/Qwen3.5-9B-Q4_K_M.gguf", label: "Qwen3.5 9B", weightsBytes: 5_680_522_464, tier: "work" },
|
|
270
|
-
{ id: "unsloth/gemma-4-12b-it-GGUF/gemma-4-12b-it-Q4_K_M.gguf", label: "Gemma 4 12B", weightsBytes: 7_121_861_440, tier: "work" },
|
|
271
|
-
{ id: "unsloth/Qwen3.8-27B-GGUF/Qwen3.8-27B-UD-Q4_K_M.gguf", label: "Qwen3.8 27B", weightsBytes: 16_464_440_224, tier: "work" },
|
|
272
309
|
];
|
|
273
310
|
export const localModelChoice = (id: string): LocalModelChoice | undefined => LOCAL_MODELS.find((choice) => choice.id === id);
|
|
274
311
|
// Exactly one row carries the instant tier; the fit module and the prefetch route take it from here rather than
|
package/src/schemas/ci.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// ci: pipeline runs on the workspace repos' github/gitlab remotes
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { AgentRunPickSchema } from "./agent.js";
|
|
4
|
-
import { RedDecisionSchema } from "./workspace/push-checks.js";
|
|
5
4
|
// The daemon maps each workspace repo to its CI project via a connected github/gitlab capability, registers a webhook
|
|
6
5
|
// to fire `ci` listener automations instantly, and serves the Pipelines rail from a webhook-freshened, poll-backfilled
|
|
7
6
|
// cache. `host` names which provider API serves a repo; the listener provider is always `ci`, since a trigger narrows
|
|
@@ -112,31 +111,72 @@ export type CiRepo = z.infer<typeof CiRepoSchema>;
|
|
|
112
111
|
// Fallback poll interval when a repo's webhook couldn't register; lives here since both the poller and the automation
|
|
113
112
|
// editor need it to tell the owner what a `hookWarning` actually costs (minutes' delay, not the feature).
|
|
114
113
|
export const CI_POLL_INTERVAL_MS = 2 * 60_000;
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
// What was decided about a main-line branch while its CI fails, in the order it was decided (ci/main-fixer.ts).
|
|
115
|
+
export const MainFailureDecisionKindSchema = z.enum([
|
|
116
|
+
// Its one fix agent was put on it: by the sandbox at the first job that failed, or by a person's Fix press, which
|
|
117
|
+
// gives the agent its turns back.
|
|
118
|
+
"fix-up",
|
|
119
|
+
// Nobody was sent: repairs are switched off, or nothing could take it.
|
|
120
|
+
"reported",
|
|
121
|
+
// The fix agent had its turns, or stopped without a fix: it waits for a person.
|
|
122
|
+
"spent",
|
|
123
|
+
]);
|
|
124
|
+
export type MainFailureDecisionKind = z.infer<typeof MainFailureDecisionKindSchema>;
|
|
125
|
+
|
|
126
|
+
// Why the fix agent handed the failure back (a `spent` decision), as a closed set the editor words for itself, so what a
|
|
127
|
+
// reader sees stays a few words in every language rather than whatever the turn's error happened to say.
|
|
128
|
+
export const MainFailureHandBackSchema = z.enum([
|
|
129
|
+
// It had every turn the sandbox gives one failure, and main still fails.
|
|
130
|
+
"turns",
|
|
131
|
+
// Its turn ended without changing anything, so the failure is likely not in the code.
|
|
132
|
+
"no-change",
|
|
133
|
+
// A person stopped it.
|
|
134
|
+
"stopped",
|
|
135
|
+
// Its turn was cut off.
|
|
136
|
+
"interrupted",
|
|
137
|
+
// Its turn failed.
|
|
138
|
+
"turn-failed",
|
|
139
|
+
// Its conversation is gone or archived.
|
|
140
|
+
"gone",
|
|
141
|
+
// It could not be told about the newest failure.
|
|
142
|
+
"refused",
|
|
143
|
+
]);
|
|
144
|
+
export type MainFailureHandBack = z.infer<typeof MainFailureHandBackSchema>;
|
|
145
|
+
|
|
146
|
+
export const MainFailureDecisionSchema = z.object({
|
|
147
|
+
kind: MainFailureDecisionKindSchema.describe("What was decided."),
|
|
148
|
+
reason: MainFailureHandBackSchema.optional().describe("Why the fix agent handed it back, on a `spent` decision."),
|
|
149
|
+
conversationId: z.string().optional().describe("The conversation working on it, when one is."),
|
|
150
|
+
at: z.number().describe("When that was decided, in milliseconds."),
|
|
151
|
+
detail: z.string().optional().describe("One short sentence on why, in the sandbox's words."),
|
|
152
|
+
});
|
|
153
|
+
export type MainFailureDecision = z.infer<typeof MainFailureDecisionSchema>;
|
|
154
|
+
|
|
155
|
+
// A main-line branch while its CI fails, as the daemon's one fix agent on it sees it (ci/main-fixer.ts): from the first
|
|
156
|
+
// job that failed until a later run of every workflow that failed on it passes, whatever else failed along the way.
|
|
157
|
+
export const CiMainFailureSchema = z.object({
|
|
118
158
|
repo: z.string().describe("Which workspace repository."),
|
|
119
159
|
branch: z.string().describe("Which main-line branch."),
|
|
120
160
|
since: z.number().describe("When its first job failed, in milliseconds."),
|
|
121
161
|
runId: z.number().describe("The newest run that failed on it."),
|
|
122
|
-
jobs: z.array(z.string()).describe("The jobs failing on it now, by name: the newest
|
|
162
|
+
jobs: z.array(z.string()).describe("The jobs failing on it now, by name: the newest failed run's failures, and any that failed since."),
|
|
123
163
|
fixer: z
|
|
124
164
|
.string()
|
|
125
165
|
.optional()
|
|
126
|
-
.describe("The one conversation working on it, which every failure goes to until the branch
|
|
127
|
-
decision:
|
|
166
|
+
.describe("The one conversation working on it, which every failure goes to until the branch passes. Absent while nobody is on it."),
|
|
167
|
+
decision: MainFailureDecisionSchema.optional().describe(
|
|
128
168
|
"The latest thing decided about it: `fix-up` while the fix agent has it, `spent` once it waits for you (its turns are used up, or it stopped without a fix), `reported` when repairs are off.",
|
|
129
169
|
),
|
|
130
170
|
});
|
|
131
|
-
export type
|
|
171
|
+
export type CiMainFailure = z.infer<typeof CiMainFailureSchema>;
|
|
132
172
|
export const CiRunsResponseSchema = z.object({
|
|
133
173
|
repos: z.array(CiRepoSchema).describe("Which workspace repositories are wired to a forge, and how each one's notifications are set up."),
|
|
134
174
|
// Newest first, across all mapped repos.
|
|
135
175
|
runs: z.array(PipelineRunSchema).describe("Runs across all of them, newest first."),
|
|
136
|
-
|
|
137
|
-
.array(
|
|
176
|
+
failures: z
|
|
177
|
+
.array(CiMainFailureSchema)
|
|
138
178
|
.optional()
|
|
139
|
-
.describe("Every main-line branch
|
|
179
|
+
.describe("Every main-line branch failing right now, with the fix agent on it. Absent from a daemon that keeps none."),
|
|
140
180
|
});
|
|
141
181
|
export type CiRunsResponse = z.infer<typeof CiRunsResponseSchema>;
|
|
142
182
|
// Re-resolves repo → project + token on every call, so a stale card can't act on a mapping the workspace no longer has.
|
|
@@ -155,6 +195,11 @@ export const CiFixParamSchema = CiRunParamSchema.extend({
|
|
|
155
195
|
pick: AgentRunPickSchema.describe(
|
|
156
196
|
"Which model to open the conversation on, when somebody chose one. Leave it out for the sandbox's own choice, which is the ordinary path.",
|
|
157
197
|
),
|
|
198
|
+
// The browser's own chat pick, which the daemon never holds: without it an unpinned fix opened on a provider the
|
|
199
|
+
// owner never chose. Absent (an older app, or nobody pressed) keeps the sandbox's own fallback.
|
|
200
|
+
fallback: AgentRunPickSchema.describe(
|
|
201
|
+
"The model a new chat opens on for whoever pressed, for when nobody chose one and no model set for fixing pipelines can run. Used while this sandbox can serve its provider; left out, or not servable, the sandbox takes its default provider when connected, else the first one connected.",
|
|
202
|
+
),
|
|
158
203
|
// The verb the picker's bar was ended with, when an attempt already exists (state/fix-attempt-plan.ts). Left out,
|
|
159
204
|
// an ended attempt is continued and a fresh press opens attempt 1; one still in play answers CONFLICT.
|
|
160
205
|
mode: z
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// context-trim: what a window too small for a full turn made the sandbox leave out
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import type { SandboxNotice } from "../events/sandbox-notice.js";
|
|
3
4
|
|
|
4
5
|
// The only disclosure of a note that never rode; without it a turn runs thinner and nothing on screen says why.
|
|
5
6
|
export const ContextTrimSchema = z.object({
|
|
@@ -18,12 +19,16 @@ export type ContextTrim = z.infer<typeof ContextTrimSchema>;
|
|
|
18
19
|
// 16k, 32k; a server's own odd number spelled out in full.
|
|
19
20
|
const windowLabel = (tokens: number): string => (tokens % 1024 === 0 ? `${tokens / 1024}k` : tokens.toLocaleString("en-US"));
|
|
20
21
|
|
|
22
|
+
/** The trim's notice by code and facts, for a reader that words it in its own language. */
|
|
23
|
+
export const contextTrimNotice = ({ window, omitted, base }: ContextTrim): Extract<SandboxNotice, { code: "contextTrim" }> => ({
|
|
24
|
+
code: "contextTrim",
|
|
25
|
+
params: { window: windowLabel(window), ...(omitted.length === 0 ? {} : { omitted: omitted.join(`, `) }), base },
|
|
26
|
+
});
|
|
27
|
+
|
|
21
28
|
// The closing clause is load-bearing: without it the row reads as "my AGENTS.md may not have arrived".
|
|
22
|
-
export const contextTrimLine = (
|
|
29
|
+
export const contextTrimLine = (trim: ContextTrim): string => {
|
|
30
|
+
const { window, omitted, base } = contextTrimNotice(trim).params;
|
|
23
31
|
const swapped = base ? ` The agent's own base instructions were swapped for a short paragraph, too.` : ``;
|
|
24
|
-
const left = omitted
|
|
25
|
-
return
|
|
26
|
-
`Sent thin for this model's ${windowLabel(window)} window${left}.${swapped}` +
|
|
27
|
-
` Your workspace rules, this turn's persona and where its files live still rode.`
|
|
28
|
-
);
|
|
32
|
+
const left = omitted === undefined ? `` : ` — left out: ${omitted}`;
|
|
33
|
+
return `Sent thin for this model's ${window} window${left}.${swapped}` + ` Your workspace rules, this turn's persona and where its files live still rode.`;
|
|
29
34
|
};
|