@intentic/sandbox-contract 1.312.0 → 1.314.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -3
- package/dist/chores/index.d.ts +0 -1
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +0 -1
- package/dist/chores/index.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +57 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +436 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +11 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +123 -96
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +1 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/device.contract.d.ts +20 -4
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +1 -1
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +11 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.d.ts +2 -0
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.d.ts +2 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +138 -0
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +1 -0
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +59 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +12 -8
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +2 -2
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +141 -19
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +72 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +85 -14
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.d.ts.map +1 -1
- package/dist/documents/index.js +1 -0
- package/dist/documents/index.js.map +1 -1
- package/dist/documents/read.d.ts +24 -0
- package/dist/documents/read.d.ts.map +1 -0
- package/dist/documents/read.js +81 -0
- package/dist/documents/read.js.map +1 -0
- package/dist/events/agent-events.d.ts +235 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +3 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/child-run.d.ts +9 -0
- package/dist/events/child-run.d.ts.map +1 -0
- package/dist/events/child-run.js +41 -0
- package/dist/events/child-run.js.map +1 -0
- package/dist/events/errands.d.ts +5 -0
- package/dist/events/errands.d.ts.map +1 -0
- package/dist/events/errands.js +19 -0
- package/dist/events/errands.js.map +1 -0
- package/dist/events/requests.d.ts +152 -0
- package/dist/events/requests.d.ts.map +1 -1
- package/dist/events/requests.js +26 -1
- package/dist/events/requests.js.map +1 -1
- package/dist/events/system-events.d.ts +8 -12
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +387 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +3 -0
- package/dist/events/transcript.js.map +1 -1
- package/dist/front/browser-wire.d.ts +6 -0
- package/dist/front/browser-wire.d.ts.map +1 -0
- package/dist/front/browser-wire.js +4 -0
- package/dist/front/browser-wire.js.map +1 -0
- package/dist/front/front-wire.d.ts +6 -0
- package/dist/front/front-wire.d.ts.map +1 -0
- package/dist/front/front-wire.js +5 -0
- package/dist/front/front-wire.js.map +1 -0
- package/dist/front/generated/browser-wire.d.ts +19 -0
- package/dist/front/generated/browser-wire.d.ts.map +1 -0
- package/dist/front/generated/browser-wire.js +2 -0
- package/dist/front/generated/browser-wire.js.map +1 -0
- package/dist/front/generated/wire.d.ts +36 -30
- package/dist/front/generated/wire.d.ts.map +1 -1
- package/dist/ids/conversation-ids.d.ts +1 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +2 -2
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/ids/session-names.d.ts +3 -0
- package/dist/ids/session-names.d.ts.map +1 -1
- package/dist/ids/session-names.js +3 -0
- package/dist/ids/session-names.js.map +1 -1
- package/dist/index.d.ts +1038 -168
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/models/agent-catalog.d.ts.map +1 -1
- package/dist/models/agent-catalog.js +4 -6
- package/dist/models/agent-catalog.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts +2 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +7 -1
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/plan-pools.d.ts +22 -1
- package/dist/models/plan-pools.d.ts.map +1 -1
- package/dist/models/plan-pools.js +86 -0
- package/dist/models/plan-pools.js.map +1 -1
- package/dist/policy/request-status.d.ts +1 -1
- package/dist/policy/request-status.d.ts.map +1 -1
- package/dist/policy/request-status.js +4 -11
- package/dist/policy/request-status.js.map +1 -1
- package/dist/policy/reserved-servers.d.ts.map +1 -1
- package/dist/policy/reserved-servers.js +2 -1
- package/dist/policy/reserved-servers.js.map +1 -1
- package/dist/policy/turned-away.d.ts.map +1 -1
- package/dist/policy/turned-away.js +1 -0
- package/dist/policy/turned-away.js.map +1 -1
- package/dist/protocol/edge-verdict.d.ts +2 -1
- package/dist/protocol/edge-verdict.d.ts.map +1 -1
- package/dist/protocol/edge-verdict.js +1 -1
- package/dist/protocol/edge-verdict.js.map +1 -1
- package/dist/protocol/ingress-contract.d.ts +1 -1
- package/dist/protocol/ingress-contract.d.ts.map +1 -1
- package/dist/protocol/ingress-contract.js +1 -1
- package/dist/protocol/ingress-contract.js.map +1 -1
- package/dist/protocol/raw-routes.d.ts +18 -40
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +5 -16
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/route-meta.d.ts +20 -0
- package/dist/protocol/route-meta.d.ts.map +1 -1
- package/dist/protocol/route-meta.js +16 -0
- package/dist/protocol/route-meta.js.map +1 -1
- package/dist/protocol/tunnel-bulk.d.ts +2 -0
- package/dist/protocol/tunnel-bulk.d.ts.map +1 -0
- package/dist/protocol/tunnel-bulk.js +3 -0
- package/dist/protocol/tunnel-bulk.js.map +1 -0
- package/dist/protocol/x402.d.ts +17 -0
- package/dist/protocol/x402.d.ts.map +1 -0
- package/dist/protocol/x402.js +37 -0
- package/dist/protocol/x402.js.map +1 -0
- package/dist/schemas/agent.d.ts +25 -0
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +14 -2
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +17 -18
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +17 -4
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +4 -6
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/devices.d.ts +134 -7
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +43 -18
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +22 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/extension-updates.js +4 -0
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/git/git.d.ts +3 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/hosts.d.ts +35 -5
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +21 -7
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +2 -12
- package/dist/schemas/keep-warm.d.ts.map +1 -1
- package/dist/schemas/keep-warm.js +8 -10
- package/dist/schemas/keep-warm.js.map +1 -1
- package/dist/schemas/metrics.d.ts +24 -0
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +22 -3
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/personas.d.ts +3 -0
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +9 -0
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +140 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +47 -1
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +57 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +6 -1
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +28 -1
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +35 -1
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +184 -4
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +31 -23
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/speaker.d.ts +36 -0
- package/dist/schemas/speaker.d.ts.map +1 -0
- package/dist/schemas/speaker.js +62 -0
- package/dist/schemas/speaker.js.map +1 -0
- package/dist/schemas/state-plan.d.ts +46 -0
- package/dist/schemas/state-plan.d.ts.map +1 -0
- package/dist/schemas/state-plan.js +31 -0
- package/dist/schemas/state-plan.js.map +1 -0
- package/dist/schemas/system.d.ts +48 -8
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +10 -14
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workspace/mainline.d.ts +206 -64
- package/dist/schemas/workspace/mainline.d.ts.map +1 -1
- package/dist/schemas/workspace/mainline.js +73 -25
- package/dist/schemas/workspace/mainline.js.map +1 -1
- package/dist/state/contract-lock.d.ts +1 -0
- package/dist/state/contract-lock.d.ts.map +1 -1
- package/dist/state/contract-lock.js +47 -1
- package/dist/state/contract-lock.js.map +1 -1
- package/dist/state/definition.d.ts +24 -16
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/fix-stance.d.ts +1 -1
- package/dist/state/fix-stance.d.ts.map +1 -1
- package/dist/state/fix-stance.js +2 -1
- package/dist/state/fix-stance.js.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +2 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/landed-commit.d.ts +1 -0
- package/dist/state/landed-commit.d.ts.map +1 -1
- package/dist/state/landed-commit.js +2 -0
- package/dist/state/landed-commit.js.map +1 -1
- package/dist/state/red-streak.d.ts +7 -0
- package/dist/state/red-streak.d.ts.map +1 -0
- package/dist/state/red-streak.js +11 -0
- package/dist/state/red-streak.js.map +1 -0
- package/dist/state/workspace-state.d.ts +1 -0
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +6 -2
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/shell-regions.d.ts +1 -0
- package/dist/text/shell-regions.d.ts.map +1 -1
- package/dist/text/shell-regions.js +1 -1
- package/dist/text/shell-regions.js.map +1 -1
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +14 -7
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +28 -29
- package/src/chores/index.ts +0 -1
- package/src/contracts/agent.contract.ts +14 -0
- package/src/contracts/capabilities.contract.ts +2 -2
- package/src/contracts/device.contract.ts +5 -3
- package/src/contracts/secrets.contract.ts +2 -0
- package/src/contracts/settings.contract.ts +2 -1
- package/src/documents/conversions.ts +3 -3
- package/src/documents/index.ts +3 -1
- package/src/documents/read.ts +114 -0
- package/src/events/agent-events.ts +5 -0
- package/src/events/child-run.test.ts +81 -0
- package/src/events/child-run.ts +73 -0
- package/src/events/errands.test.ts +22 -0
- package/src/events/errands.ts +27 -0
- package/src/events/requests.ts +40 -1
- package/src/events/transcript.ts +8 -0
- package/src/front/browser-wire.ts +20 -0
- package/src/front/front-wire.ts +15 -0
- package/src/front/generated/browser-wire.json +138 -0
- package/src/front/generated/browser-wire.ts +16 -0
- package/src/front/generated/front-wire.json +793 -0
- package/src/front/generated/tunnel.json +38 -0
- package/src/front/generated/wire.ts +21 -24
- package/src/front/wire-manifests.test.ts +61 -0
- package/src/ids/conversation-ids.ts +6 -5
- package/src/ids/session-names.ts +7 -0
- package/src/index.ts +8 -1
- package/src/models/agent-catalog.test.ts +6 -4
- package/src/models/agent-catalog.ts +4 -6
- package/src/models/agent-runtimes.ts +22 -7
- package/src/models/capability-ledger.test.ts +2 -0
- package/src/models/plan-pools.test.ts +121 -2
- package/src/models/plan-pools.ts +152 -2
- package/src/policy/request-status.ts +4 -12
- package/src/policy/reserved-servers.test.ts +6 -0
- package/src/policy/reserved-servers.ts +13 -10
- package/src/policy/turned-away.ts +3 -0
- package/src/protocol/edge-verdict.ts +13 -11
- package/src/protocol/ingress-contract.fixture.json +13 -0
- package/src/protocol/ingress-contract.ts +9 -5
- package/src/protocol/raw-routes.ts +10 -21
- package/src/protocol/route-meta.ts +36 -1
- package/src/protocol/tunnel-bulk.test.ts +37 -0
- package/src/protocol/tunnel-bulk.ts +7 -0
- package/src/protocol/x402.test.ts +29 -0
- package/src/protocol/x402.ts +61 -0
- package/src/schemas/agent.ts +28 -3
- package/src/schemas/agents.ts +33 -7
- package/src/schemas/automations.ts +4 -2
- package/src/schemas/devices.ts +107 -39
- package/src/schemas/extension-updates.ts +6 -0
- package/src/schemas/hosts.ts +68 -19
- package/src/schemas/ic-shape-args.test.ts +64 -0
- package/src/schemas/keep-warm.test.ts +6 -13
- package/src/schemas/keep-warm.ts +15 -20
- package/src/schemas/metrics.ts +32 -3
- package/src/schemas/personas.ts +15 -0
- package/src/schemas/providers/plan-limits.ts +71 -1
- package/src/schemas/providers/provider-oauth.ts +10 -1
- package/src/schemas/settings-history.ts +50 -3
- package/src/schemas/settings.test.ts +54 -1
- package/src/schemas/settings.ts +49 -31
- package/src/schemas/speaker.ts +89 -0
- package/src/schemas/state-plan.test.ts +69 -0
- package/src/schemas/state-plan.ts +47 -0
- package/src/schemas/system.ts +20 -16
- package/src/schemas/workspace/mainline.test.ts +28 -0
- package/src/schemas/workspace/mainline.ts +111 -44
- package/src/state/contract-lock.test.ts +65 -4
- package/src/state/contract-lock.ts +70 -2
- package/src/state/fix-stance.test.ts +8 -0
- package/src/state/fix-stance.ts +3 -3
- package/src/state/history-state.ts +5 -1
- package/src/state/landed-commit.test.ts +12 -1
- package/src/state/landed-commit.ts +5 -0
- package/src/state/red-streak.test.ts +28 -0
- package/src/state/red-streak.ts +28 -0
- package/src/state/workspace-state.test.ts +7 -1
- package/src/state/workspace-state.ts +10 -2
- package/src/text/shell-regions.test.ts +38 -0
- package/src/text/shell-regions.ts +3 -3
- package/src/text/transcript-fold.test.ts +13 -0
- package/src/text/transcript-fold.ts +17 -8
- package/dist/chores/fix-deps.d.ts +0 -10
- package/dist/chores/fix-deps.d.ts.map +0 -1
- package/dist/chores/fix-deps.js +0 -19
- package/dist/chores/fix-deps.js.map +0 -1
- package/dist/front/terminal-frames.d.ts +0 -17
- package/dist/front/terminal-frames.d.ts.map +0 -1
- package/dist/front/terminal-frames.js +0 -79
- package/dist/front/terminal-frames.js.map +0 -1
- package/dist/protocol/tunnel-lanes.d.ts +0 -10
- package/dist/protocol/tunnel-lanes.d.ts.map +0 -1
- package/dist/protocol/tunnel-lanes.js +0 -16
- package/dist/protocol/tunnel-lanes.js.map +0 -1
- package/dist/protocol/upgrade-relay.d.ts +0 -4
- package/dist/protocol/upgrade-relay.d.ts.map +0 -1
- package/dist/protocol/upgrade-relay.js +0 -25
- package/dist/protocol/upgrade-relay.js.map +0 -1
- package/src/chores/fix-deps.ts +0 -23
- package/src/front/terminal-frames.fixture.json +0 -55
- package/src/front/terminal-frames.test.ts +0 -63
- package/src/front/terminal-frames.ts +0 -102
- package/src/protocol/tunnel-lanes.json +0 -136
- package/src/protocol/tunnel-lanes.test.ts +0 -30
- package/src/protocol/tunnel-lanes.ts +0 -32
- package/src/protocol/upgrade-relay.ts +0 -28
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { convertDocument } from "../documents/conversions.js";
|
|
2
|
+
import { SETTINGS_HISTORY } from "./settings-history.js";
|
|
3
|
+
import { RepoChecksFileSchema, RuleSchema, SandboxSettingsSchema, SandboxSettingsWriteSchema } from "./settings.js";
|
|
2
4
|
|
|
3
5
|
// A rule that saves cleanly and silently does nothing is the failure this schema exists to refuse: an action, and a
|
|
4
6
|
// built-in's name, are checked against the moment they are written at.
|
|
@@ -57,3 +59,54 @@ describe(`where a command lives`, () => {
|
|
|
57
59
|
expect(file([{ when: `land`, run: `pnpm verify`, paths: [`src/**`] }])).toBe(false);
|
|
58
60
|
});
|
|
59
61
|
});
|
|
62
|
+
|
|
63
|
+
// Nothing runs when a turn ends any more. A file written before still reads, its conversion drops the inert rule, and no
|
|
64
|
+
// save may stand a new one there.
|
|
65
|
+
describe(`the retired turn.ending moment`, () => {
|
|
66
|
+
const look = { id: `verify-ui-edits`, label: `Look at what changed`, moment: `turn.ending`, action: { kind: `builtin`, name: `verify-ui-edits` } };
|
|
67
|
+
const land = { id: `auto-land`, label: `land`, moment: `agent.finished`, action: { kind: `verdict`, verdict: `allow` } };
|
|
68
|
+
|
|
69
|
+
test(`still reads, so an older file parses`, () => {
|
|
70
|
+
expect(SandboxSettingsSchema.safeParse({ rules: [look, land] }).success).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test(`is refused on a save`, () => {
|
|
74
|
+
const saved = SandboxSettingsWriteSchema.safeParse(SandboxSettingsSchema.parse({ rules: [look, land] }));
|
|
75
|
+
expect(saved.error?.issues.map(({ message, path }) => ({ message, path }))).toEqual([
|
|
76
|
+
{ message: `turn.ending is retired: nothing runs when a turn ends any more, so a rule cannot stand there`, path: [`rules`] },
|
|
77
|
+
]);
|
|
78
|
+
expect(SandboxSettingsWriteSchema.safeParse(SandboxSettingsSchema.parse({ rules: [land] })).success).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test(`is dropped from a stored file by its conversion, which leaves every other rule and settles`, () => {
|
|
82
|
+
const converted = convertDocument(SETTINGS_HISTORY, `object`, { rules: [look, land] }, true);
|
|
83
|
+
expect(converted.value).toEqual({ rules: [land] });
|
|
84
|
+
expect(converted.changes.map(({ conversion }) => conversion)).toEqual([
|
|
85
|
+
`drops rules at the retired turn.ending moment (and the verify-ui-edits built-in), which run nothing`,
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// keep-warm's four top-level settings became one object on 2026-09-26; a stored file keeps what its owner picked.
|
|
91
|
+
describe(`the folded keep-warm settings`, () => {
|
|
92
|
+
test(`fold into one object, keeping each value and the key's place`, () => {
|
|
93
|
+
const stored = { timezone: `Europe/Warsaw`, keepWarm: true, keepWarmHours: 6, keepWarmMinTokens: 50_000, keepWarmReserve: 20, iqSearch: true };
|
|
94
|
+
const converted = convertDocument(SETTINGS_HISTORY, `object`, stored, true);
|
|
95
|
+
expect(converted.value).toEqual({ timezone: `Europe/Warsaw`, keepWarm: { auto: true, hours: 6, minTokens: 50_000, reserve: 20 }, iqSearch: true });
|
|
96
|
+
expect(JSON.stringify(converted.value)).toBe(
|
|
97
|
+
`{"timezone":"Europe/Warsaw","keepWarm":{"auto":true,"hours":6,"minTokens":50000,"reserve":20},"iqSearch":true}`,
|
|
98
|
+
);
|
|
99
|
+
expect(SandboxSettingsSchema.parse(converted.value).keepWarm).toEqual({ auto: true, hours: 6, minTokens: 50_000, reserve: 20 });
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test(`fold a file that set only some, leaving the rest to the defaults`, () => {
|
|
103
|
+
const converted = convertDocument(SETTINGS_HISTORY, `object`, { keepWarmReserve: 30 }, true);
|
|
104
|
+
expect(converted.value).toEqual({ keepWarm: { reserve: 30 } });
|
|
105
|
+
expect(SandboxSettingsSchema.parse(converted.value).keepWarm).toEqual({ auto: false, hours: 4, minTokens: 100_000, reserve: 30 });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test(`leave a file already in the new shape alone`, () => {
|
|
109
|
+
const current = { keepWarm: { auto: true, hours: 2 } };
|
|
110
|
+
expect(convertDocument(SETTINGS_HISTORY, `object`, current, true).changes).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
});
|
package/src/schemas/settings.ts
CHANGED
|
@@ -98,6 +98,9 @@ export const RuleSchema = z
|
|
|
98
98
|
path: ["action"],
|
|
99
99
|
});
|
|
100
100
|
export type Rule = z.infer<typeof RuleSchema>;
|
|
101
|
+
// A rule at a retired moment or with a retired built-in: read, so an older file parses, but never written again.
|
|
102
|
+
export const isRetiredMomentRule = (rule: Pick<Rule, "moment" | "action">): boolean =>
|
|
103
|
+
rule.moment === "turn.ending" || (rule.action.kind === "builtin" && rule.action.name === "verify-ui-edits");
|
|
101
104
|
// Kept out of the settings object on purpose: a firing is not an edit, and writing config on every push would make
|
|
102
105
|
// every run a settings save.
|
|
103
106
|
export const RuleFiringsSchema = z.record(z.string(), z.number());
|
|
@@ -189,6 +192,40 @@ export type SkillSwitch = z.infer<typeof SkillSwitchSchema>;
|
|
|
189
192
|
// picks rather than failing whole. A setting removed or reshaped here gets its conversion in settings-history.ts, which
|
|
190
193
|
// also names every retired setting, none of whose names may come back.
|
|
191
194
|
|
|
195
|
+
// keep-warm's knobs, one object since each only means anything beside the others (cache-keepwarm.ts).
|
|
196
|
+
export const KeepWarmSettingsSchema = z.object({
|
|
197
|
+
auto: z
|
|
198
|
+
.boolean()
|
|
199
|
+
.default(false)
|
|
200
|
+
.describe(
|
|
201
|
+
"Keep a Claude conversation's prompt cache warm after each turn a person asked for. Each refresh re-reads the cached context at the cache price and adds nothing to the conversation.",
|
|
202
|
+
),
|
|
203
|
+
hours: z
|
|
204
|
+
.number()
|
|
205
|
+
.min(1)
|
|
206
|
+
.max(8)
|
|
207
|
+
.default(4)
|
|
208
|
+
.describe(
|
|
209
|
+
"How long an idle conversation is kept warm after its last turn, in hours, and what a press on one offers first. Shortened where refreshing would cost more than the cold resume it saves, and at midnight where the agent runs, when the date in its prompt changes.",
|
|
210
|
+
),
|
|
211
|
+
minTokens: z
|
|
212
|
+
.number()
|
|
213
|
+
.int()
|
|
214
|
+
.min(0)
|
|
215
|
+
.default(100_000)
|
|
216
|
+
.describe("Only conversations at least this large, in tokens, are kept warm by `auto`: a small one is cheap to re-read anyway."),
|
|
217
|
+
reserve: z
|
|
218
|
+
.number()
|
|
219
|
+
.int()
|
|
220
|
+
.min(0)
|
|
221
|
+
.max(90)
|
|
222
|
+
.default(15)
|
|
223
|
+
.describe(
|
|
224
|
+
"How much of an account's usage limit, in percent, keeping conversations warm must leave untouched for real work. Refreshing stops once any limit that account's model spends is fuller than that.",
|
|
225
|
+
),
|
|
226
|
+
});
|
|
227
|
+
export type KeepWarmSettings = z.infer<typeof KeepWarmSettingsSchema>;
|
|
228
|
+
|
|
192
229
|
export const SandboxSettingsSchema = z.object({
|
|
193
230
|
// The zone every WALL-CLOCK RULE in this sandbox is meant in — an automation's cron, and anything else that says
|
|
194
231
|
// "at 09:00" instead of naming an instant. It is not a display preference: nothing formats through it, and an
|
|
@@ -392,36 +429,11 @@ export const SandboxSettingsSchema = z.object({
|
|
|
392
429
|
.describe(
|
|
393
430
|
"When a spent usage limit moves a turn to another account, carry the provider session (the model keeps everything, and re-reads all of it once on the other account) while the conversation's context is under this many tokens; at or above it, start a fresh session with the sandbox's measured brief instead. Zero always starts fresh.",
|
|
394
431
|
),
|
|
395
|
-
// Off by default: every refresh spends the account's own allowance on a conversation nobody is using yet.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
.
|
|
399
|
-
|
|
400
|
-
"Keep a Claude conversation's prompt cache warm after each turn a person asked for, so coming back to it hours later costs a cache read instead of re-sending everything. Each refresh re-reads the cached context at the cache price and adds nothing to the conversation. Any one conversation can be kept warm or let cool by hand either way.",
|
|
401
|
-
),
|
|
402
|
-
keepWarmHours: z
|
|
403
|
-
.number()
|
|
404
|
-
.min(1)
|
|
405
|
-
.max(8)
|
|
406
|
-
.default(4)
|
|
407
|
-
.describe(
|
|
408
|
-
"How long an idle conversation is kept warm after its last turn, in hours. Shortened where refreshing would cost more than the cold resume it saves, and at midnight where the agent runs, when the date in its prompt changes.",
|
|
409
|
-
),
|
|
410
|
-
keepWarmMinTokens: z
|
|
411
|
-
.number()
|
|
412
|
-
.int()
|
|
413
|
-
.min(0)
|
|
414
|
-
.default(100_000)
|
|
415
|
-
.describe("Only conversations at least this large, in tokens, are kept warm by themselves: a small one is cheap to re-read anyway."),
|
|
416
|
-
keepWarmReserve: z
|
|
417
|
-
.number()
|
|
418
|
-
.int()
|
|
419
|
-
.min(0)
|
|
420
|
-
.max(90)
|
|
421
|
-
.default(15)
|
|
422
|
-
.describe(
|
|
423
|
-
"How much of an account's usage limit, in percent, keeping conversations warm must leave untouched for real work. Refreshing stops once any limit that account's model spends is fuller than that.",
|
|
424
|
-
),
|
|
432
|
+
// Off by default: every refresh spends the account's own allowance on a conversation nobody is using yet. Before
|
|
433
|
+
// 2026-09-26 these were four top-level settings (settings-history.ts folds them).
|
|
434
|
+
keepWarm: KeepWarmSettingsSchema.prefault({}).describe(
|
|
435
|
+
"Keeping an idle conversation's prompt cache warm, so coming back to it hours later costs a cache read instead of re-sending everything. Any one conversation can be kept warm or let cool by hand whatever `auto` says.",
|
|
436
|
+
),
|
|
425
437
|
// Worth it since the container is recreated on every update or environment approval — otherwise approving a
|
|
426
438
|
// Dockerfile change costs the run that asked for it.
|
|
427
439
|
// What happens to breakage found after the work that caused it has left the turn. A land that turns the main tree's own
|
|
@@ -436,7 +448,7 @@ export const SandboxSettingsSchema = z.object({
|
|
|
436
448
|
"Whether breakage found after the work left its turn is repaired without asking. A land that turns the main tree's check red is repaired once the work queued behind it has been checked too: by the conversation that landed it while it still has the work in mind, otherwise by a fresh conversation handed the failures and the suspects' changes. Main's CI staying red on the same failure gets a fix agent once pushes go quiet, and a failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.",
|
|
437
449
|
),
|
|
438
450
|
// Where heavy work runs: a runner (the same image, on one of the owner's machines) instead of this sandbox. Keyed by
|
|
439
|
-
// the heavy-command rule an agent's command matched (
|
|
451
|
+
// the heavy-command rule an agent's command matched (system/resources/heavy-commands.ts), and one entry for the
|
|
440
452
|
// check after landing, each naming a runner id. A runner that is offline, outdated or full hands the work back here,
|
|
441
453
|
// which the command's output says. Never pushed to a runner itself (portability/definition.ts), which must not pass
|
|
442
454
|
// work on again.
|
|
@@ -517,6 +529,12 @@ export const SandboxSettingsSchema = z.object({
|
|
|
517
529
|
.describe("How many levels deep the delegation may go, since a subagent can start subagents of its own."),
|
|
518
530
|
});
|
|
519
531
|
export type SandboxSettings = z.infer<typeof SandboxSettingsSchema>;
|
|
532
|
+
// What a save takes: the settings as read, less what is only read. `turn.ending` (and its `verify-ui-edits` built-in) is
|
|
533
|
+
// retired; a file written before still parses (and its conversion drops such rules), but no save may stand a new one.
|
|
534
|
+
export const SandboxSettingsWriteSchema = SandboxSettingsSchema.refine((settings) => !settings.rules.some(isRetiredMomentRule), {
|
|
535
|
+
message: "turn.ending is retired: nothing runs when a turn ends any more, so a rule cannot stand there",
|
|
536
|
+
path: ["rules"],
|
|
537
|
+
});
|
|
520
538
|
|
|
521
539
|
// A browser telling the sandbox which clock IT is on. An offer, not an instruction: see `adoptTimezone`.
|
|
522
540
|
export const TimezoneOfferSchema = z.object({
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { MessageVoice } from "./agent.js";
|
|
3
|
+
import type { SessionOwner } from "./agents.js";
|
|
4
|
+
|
|
5
|
+
// WHO IS SPEAKING, as the sandbox verified it: one typed value every door builds once, from which the older one-line
|
|
6
|
+
// facts are derived (the message's `voice`, the turn's `actor` string, the conversation's `owner`), so no reader parses a
|
|
7
|
+
// prefix to learn whether a person is at the keyboard. Shared with transcript rows that want to say who spoke. Named apart
|
|
8
|
+
// from `Speaker` (agents.ts), which is only which side of a transcript a search snippet came from.
|
|
9
|
+
|
|
10
|
+
export const TurnSpeakerSchema = z.discriminatedUnion("kind", [
|
|
11
|
+
z.object({
|
|
12
|
+
kind: z.literal("person"),
|
|
13
|
+
email: z.string().describe("The signed-in member, as the sandbox verified them."),
|
|
14
|
+
name: z.string().optional().describe("Their display name, where the sign-in carries one."),
|
|
15
|
+
}),
|
|
16
|
+
z.object({
|
|
17
|
+
kind: z.literal("program"),
|
|
18
|
+
token: z.string().describe("The label of the control token a person minted and handed to this program."),
|
|
19
|
+
}),
|
|
20
|
+
z.object({
|
|
21
|
+
kind: z.literal("agent"),
|
|
22
|
+
conversationId: z.string().describe("The conversation whose agent is speaking: a child reporting back, a peer's message."),
|
|
23
|
+
}),
|
|
24
|
+
z.object({
|
|
25
|
+
kind: z.literal("sandbox"),
|
|
26
|
+
source: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say."),
|
|
30
|
+
}),
|
|
31
|
+
]);
|
|
32
|
+
export type TurnSpeaker = z.infer<typeof TurnSpeakerSchema>;
|
|
33
|
+
|
|
34
|
+
// Prefixed so a token labelled like an email, or a conversation id, can never read as a person.
|
|
35
|
+
const TOKEN_PREFIX = "token:";
|
|
36
|
+
const AGENT_PREFIX = "agent:";
|
|
37
|
+
|
|
38
|
+
/** The one-line attribution a turn and a queued message carry: a person's email, else the prefixed program or agent. */
|
|
39
|
+
export const speakerActor = (speaker: TurnSpeaker): string | undefined => {
|
|
40
|
+
switch (speaker.kind) {
|
|
41
|
+
case "person":
|
|
42
|
+
return speaker.email;
|
|
43
|
+
case "program":
|
|
44
|
+
return `${TOKEN_PREFIX}${speaker.token}`;
|
|
45
|
+
case "agent":
|
|
46
|
+
return `${AGENT_PREFIX}${speaker.conversationId}`;
|
|
47
|
+
case "sandbox":
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Whose words a message is: a program writes through a person's door, so it speaks with a person's voice. */
|
|
53
|
+
export const speakerVoice = (speaker: TurnSpeaker): MessageVoice =>
|
|
54
|
+
speaker.kind === "program" ? "person" : speaker.kind === "agent" ? "agent" : speaker.kind === "sandbox" ? "sandbox" : "person";
|
|
55
|
+
|
|
56
|
+
/** The member who owns what this speaker opens; a program, an agent and the sandbox own nothing, so theirs stay claimable. */
|
|
57
|
+
export const speakerOwner = (speaker: TurnSpeaker): Pick<SessionOwner, "email" | "name"> | undefined => {
|
|
58
|
+
if (speaker.kind !== "person") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const owner: Pick<SessionOwner, "email" | "name"> = { email: speaker.email };
|
|
62
|
+
if (speaker.name !== undefined) {
|
|
63
|
+
owner.name = speaker.name;
|
|
64
|
+
}
|
|
65
|
+
return owner;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** A person, verified, at the keyboard: never a program holding a token a person minted. */
|
|
69
|
+
export const spokenByPerson = (speaker: TurnSpeaker | undefined): boolean => speaker?.kind === "person";
|
|
70
|
+
|
|
71
|
+
// WHAT A COMPOSED PROMPT IS FOR, when the sandbox or the app wrote it rather than whoever is named as speaking: a land's
|
|
72
|
+
// breakage sent back to it, a fresh fix-up on a red main line and its later nudges, a note to a conversation still
|
|
73
|
+
// working on what failed, the brief a person's press hands an agent (what a push left, main's red CI), a land conflict to
|
|
74
|
+
// resolve. Carried on the turn and the row it opens, so a reader shows it as the sandbox's words, not the owner's, and
|
|
75
|
+
// never has to recognise the prompt by its opening; the four openings older rows are recognised by stay what they are
|
|
76
|
+
// (events/errands.ts, errandOfPrompt).
|
|
77
|
+
export const TurnErrandSchema = z.enum([
|
|
78
|
+
"land-conflict",
|
|
79
|
+
"verify-nudge",
|
|
80
|
+
"land-breakage",
|
|
81
|
+
"land-fix",
|
|
82
|
+
"land-fix-nudge",
|
|
83
|
+
"land-held",
|
|
84
|
+
"push-fix",
|
|
85
|
+
"push-fix-nudge",
|
|
86
|
+
"ci-fix",
|
|
87
|
+
"ci-fix-nudge",
|
|
88
|
+
]);
|
|
89
|
+
export type TurnErrand = z.infer<typeof TurnErrandSchema>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { STATE_DIR } from "@intentic/constants";
|
|
4
|
+
import { packageRoot } from "@intentic/constants/node";
|
|
5
|
+
import { STATE_PLAN_FORMAT, type StatePlan, StatePlanSchema, type StateStatus, StateStatusSchema } from "./state-plan.js";
|
|
6
|
+
|
|
7
|
+
// The documents `ic` reads by field name, spelled once here: each golden file under golden/ is one of these examples
|
|
8
|
+
// as JSON, and the Rust tests (_sandbox/ic/src/sandbox/preflight.rs, health.rs) parse the same files. A change here
|
|
9
|
+
// that the committed file does not carry fails, naming the file; `INTENTIC_WRITE_GOLDEN=1` rewrites them, and the Rust
|
|
10
|
+
// tests then say whether ic still reads them.
|
|
11
|
+
|
|
12
|
+
const GOLDEN = join(packageRoot(import.meta.url), "golden");
|
|
13
|
+
|
|
14
|
+
const clear: StatePlan = {
|
|
15
|
+
plan: STATE_PLAN_FORMAT,
|
|
16
|
+
version: "1.400.0",
|
|
17
|
+
engine: 84,
|
|
18
|
+
digest: "c8cff51cab20a2b5",
|
|
19
|
+
ok: true,
|
|
20
|
+
downgrade: false,
|
|
21
|
+
failures: [],
|
|
22
|
+
steps: [
|
|
23
|
+
{ document: "conversations-db-schema", change: "upgrades the conversations database schema" },
|
|
24
|
+
{ document: `${STATE_DIR}/config/personas.json`, change: `moves it from ${STATE_DIR}/identities.json` },
|
|
25
|
+
],
|
|
26
|
+
converts: [
|
|
27
|
+
{ document: `${STATE_DIR}/config/settings.json`, change: "converts personaRouting from its old values", detail: '"suggest" became true' },
|
|
28
|
+
],
|
|
29
|
+
files: [`${STATE_DIR}/config/personas.json`],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const refused: StatePlan = {
|
|
33
|
+
plan: STATE_PLAN_FORMAT,
|
|
34
|
+
version: "1.400.0",
|
|
35
|
+
engine: 84,
|
|
36
|
+
digest: "c8cff51cab20a2b5",
|
|
37
|
+
ok: false,
|
|
38
|
+
downgrade: true,
|
|
39
|
+
failures: [{ document: `${STATE_DIR}/config/automations.json`, detail: 'conversion "converts numbered kinds" failed: no such kind' }],
|
|
40
|
+
steps: [],
|
|
41
|
+
converts: [],
|
|
42
|
+
files: [],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// The whole `/health` answer is the daemon's; ic reads only `state` out of it, so the golden carries just enough around it.
|
|
46
|
+
const health: { readonly ok: true; readonly state: StateStatus } = { ok: true, state: { journal: "open", engine: 84 } };
|
|
47
|
+
|
|
48
|
+
const goldens = [
|
|
49
|
+
{ file: "state-plan-clear.json", value: StatePlanSchema.parse(clear) },
|
|
50
|
+
{ file: "state-plan-refused.json", value: StatePlanSchema.parse(refused) },
|
|
51
|
+
{ file: "health-state.json", value: { ...health, state: StateStatusSchema.parse(health.state) } },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
test.each(goldens)("golden/$file is the contract's example, as the host reads it", ({ file, value }) => {
|
|
55
|
+
const path = join(GOLDEN, file);
|
|
56
|
+
const expected = `${JSON.stringify(value, undefined, 2)}\n`;
|
|
57
|
+
if (process.env["INTENTIC_WRITE_GOLDEN"] === "1") {
|
|
58
|
+
writeFileSync(path, expected);
|
|
59
|
+
}
|
|
60
|
+
expect(readFileSync(path, "utf8")).toBe(expected);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("a marker plan an older ic wrote, with only what it kept, still reads", () => {
|
|
64
|
+
expect(StatePlanSchema.partial().extend({ ok: StatePlanSchema.shape.ok }).parse({ ok: true, downgrade: false, steps: [{ document: "a.json", change: "renames a to b" }] })).toEqual({
|
|
65
|
+
ok: true,
|
|
66
|
+
downgrade: false,
|
|
67
|
+
steps: [{ document: "a.json", change: "renames a to b" }],
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// The daemon's state engine as the host reads it, in two documents a Rust binary (`ic`) parses by these field names:
|
|
4
|
+
// the update pre-flight's one line (the target image's state-plan.ts, read by _sandbox/ic/src/sandbox/preflight.rs and
|
|
5
|
+
// embedded verbatim in the staged-update marker), and the `state` of `/health` (read by _sandbox/ic/src/health.rs). The
|
|
6
|
+
// golden files beside the contract (golden/) are this schema's examples, and the Rust tests assert against them.
|
|
7
|
+
|
|
8
|
+
export const PlanStepSchema = z.object({
|
|
9
|
+
document: z.string().describe("The stored file, workspace-relative, or `<volume>:<path>` for one on another volume; a structural step's id."),
|
|
10
|
+
change: z.string().describe("What is done to it, in one line."),
|
|
11
|
+
detail: z.string().optional().describe("What was particular about this one: a conflict's losing value, a retired entry, a mapped value."),
|
|
12
|
+
});
|
|
13
|
+
export type PlanStep = z.infer<typeof PlanStepSchema>;
|
|
14
|
+
|
|
15
|
+
export const PlanFailureSchema = z.object({
|
|
16
|
+
document: z.string().describe("The stored file whose conversion would fail, or the structural step that would."),
|
|
17
|
+
detail: z.string().describe("Why, in the conversion's own words."),
|
|
18
|
+
});
|
|
19
|
+
export type PlanFailure = z.infer<typeof PlanFailureSchema>;
|
|
20
|
+
|
|
21
|
+
// The one format `ic` reads; any other is "no plan", never a guess at what its fields mean.
|
|
22
|
+
export const STATE_PLAN_FORMAT = 1;
|
|
23
|
+
|
|
24
|
+
export const StatePlanSchema = z.object({
|
|
25
|
+
plan: z.literal(STATE_PLAN_FORMAT).describe("The format of this line. A reader refuses any other rather than guessing at its fields."),
|
|
26
|
+
version: z.string().describe("The release of the image that planned it; 0.0.0 for a development build."),
|
|
27
|
+
engine: z.number().describe("The conversion count builds before the digest compared. Reported, never decided by."),
|
|
28
|
+
digest: z.string().describe("What identifies the planning build's conversion set: its episodes resume only under the same one."),
|
|
29
|
+
ok: z.boolean().describe("False when a conversion would fail on this sandbox's files, which refuses the update before anything is touched."),
|
|
30
|
+
downgrade: z.boolean().describe("A newer release than the planning build ran here: it opens what it cannot read read-only."),
|
|
31
|
+
failures: z.array(PlanFailureSchema).describe("Each conversion or step that would fail, and why."),
|
|
32
|
+
steps: z.array(PlanStepSchema).describe("What the first boot changes on disk: documents moved, structural steps run."),
|
|
33
|
+
converts: z
|
|
34
|
+
.array(PlanStepSchema)
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("What the build's conversions change as its stores read these files, written by each store's next save. Absent from a build before it."),
|
|
37
|
+
files: z.array(z.string()).describe("Every file the first boot writes, workspace-relative where it can be."),
|
|
38
|
+
});
|
|
39
|
+
export type StatePlan = z.infer<typeof StatePlanSchema>;
|
|
40
|
+
|
|
41
|
+
// The `state` of `/health`: whether an update's state changes are still uncommitted. The host rolls an update back when
|
|
42
|
+
// the journal stays open, so this rides the same probe as the boot's progress.
|
|
43
|
+
export const StateStatusSchema = z.object({
|
|
44
|
+
journal: z.enum(["open", "none"]).describe("Open from the start of a boot that changed stored files until that boot has converged."),
|
|
45
|
+
engine: z.number().describe("The running build's conversion count."),
|
|
46
|
+
});
|
|
47
|
+
export type StateStatus = z.infer<typeof StateStatusSchema>;
|
package/src/schemas/system.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { StatePlanSchema } from "./state-plan.js";
|
|
2
3
|
// version is this build's baked-in version; latest/updateAvailable come from comparing it to the published stable
|
|
3
4
|
// release.
|
|
4
5
|
// Whether a runtime can serve a turn, probed off the turn path. "unknown" reads as available-but-unverified, never as
|
|
@@ -29,19 +30,10 @@ export const StagedUpdateSchema = z.object({
|
|
|
29
30
|
at: z.number().describe("When the download finished, in milliseconds, which answers whether this is still the update being offered."),
|
|
30
31
|
// The staged build's state pre-flight (the daemon's state-plan.ts, run by `ic sandbox prepare` over read-only mounts
|
|
31
32
|
// of this sandbox's volumes): what its first boot converts, before anyone accepts the update.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
steps: z
|
|
37
|
-
.array(z.object({ document: z.string(), change: z.string() }))
|
|
38
|
-
.optional()
|
|
39
|
-
.describe("Each stored file the first boot converts, and how."),
|
|
40
|
-
failures: z
|
|
41
|
-
.array(z.object({ document: z.string(), detail: z.string() }))
|
|
42
|
-
.optional()
|
|
43
|
-
.describe("Each conversion that would fail, and why."),
|
|
44
|
-
})
|
|
33
|
+
// Embedded verbatim by the ic that staged it (StatePlanSchema); a marker an older ic wrote held only `ok` with
|
|
34
|
+
// `downgrade` and `steps`, or `failures`, so everything but `ok` stays optional here.
|
|
35
|
+
plan: StatePlanSchema.partial()
|
|
36
|
+
.extend({ ok: StatePlanSchema.shape.ok })
|
|
45
37
|
.optional()
|
|
46
38
|
.describe("What the downloaded build's first boot would convert in this sandbox's stored files. Absent when no plan could be had, which says nothing either way."),
|
|
47
39
|
});
|
|
@@ -92,8 +84,9 @@ export const InfoSchema = z.object({
|
|
|
92
84
|
),
|
|
93
85
|
});
|
|
94
86
|
export type Info = z.infer<typeof InfoSchema>;
|
|
95
|
-
// What the daemon could not read in its own `.intentic/` state files
|
|
96
|
-
//
|
|
87
|
+
// What the daemon could not read in its own `.intentic/` state files, and in the files on its volume that
|
|
88
|
+
// REPORTED_VOLUME_FILES names. Reported per file, only for files listed in REPORTED_MANIFEST_PATHS or
|
|
89
|
+
// REPORTED_VOLUME_FILES (workspace-state.ts). `kind`:
|
|
97
90
|
// unreadable: whole file ignored, everything in it at default.
|
|
98
91
|
// unknownKey: only that key ignored; `suggestion` may name what it should be.
|
|
99
92
|
// invalidEntry: one list entry skipped, rest of the file stands.
|
|
@@ -103,6 +96,13 @@ export const ManifestProblemSchema = z.object({
|
|
|
103
96
|
.describe(
|
|
104
97
|
"What to do about it. Unreadable means the whole file is being ignored and everything in it is at its default. An unknown key means only that key is ignored. An invalid entry means one item of a list was skipped and the rest is fine.",
|
|
105
98
|
),
|
|
99
|
+
// Why, as a word a reader matches on; `detail` is prose for a person and never parsed. Absent from an older daemon.
|
|
100
|
+
reason: z
|
|
101
|
+
.enum(["io", "not-json", "conversion-failed", "rejected"])
|
|
102
|
+
.optional()
|
|
103
|
+
.describe(
|
|
104
|
+
"Why it could not be read: the file could not be opened (io), it is not JSON, a conversion to this version's shape failed, or its contents are not what this version expects (rejected). A rejected file after a newer version ran is usually that version's, not a broken one.",
|
|
105
|
+
),
|
|
106
106
|
detail: z.string().describe("What exactly was wrong, as one sentence and nothing else. Never the remedy: that is `fix`."),
|
|
107
107
|
suggestion: z.string().optional().describe("The name it was probably meant to be, when one is close enough to guess honestly."),
|
|
108
108
|
// Set only when the fix isn't "open the file and correct it", e.g. a version skew where editing would be wrong.
|
|
@@ -115,7 +115,11 @@ export type ManifestProblem = z.infer<typeof ManifestProblemSchema>;
|
|
|
115
115
|
// Workspace-relative path and everything currently wrong with that file; a clean file is absent from the list, not
|
|
116
116
|
// present and empty.
|
|
117
117
|
export const ManifestProblemReportSchema = z.object({
|
|
118
|
-
path: z
|
|
118
|
+
path: z
|
|
119
|
+
.string()
|
|
120
|
+
.describe(
|
|
121
|
+
"The file, as a workspace path, or as its absolute path on the daemon's own history volume for one kept there (the conversation registry). The file is the unit somebody fixes, which is why problems are grouped by it.",
|
|
122
|
+
),
|
|
119
123
|
problems: z
|
|
120
124
|
.array(ManifestProblemSchema)
|
|
121
125
|
.describe("Everything currently wrong with it. A file with nothing wrong is absent rather than present and empty."),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { pushFixConversationId } from "../../ids/conversation-ids.js";
|
|
2
|
+
import { pushFixBase, pushRedOf, type Red, RedSchema } from "./mainline.js";
|
|
3
|
+
|
|
4
|
+
// What a push left is a Red like any other (source `push`, scope the project), and the hand-over's id is named by when
|
|
5
|
+
// that red began, so the editor and the daemon find the same attempt whichever of them asks.
|
|
6
|
+
|
|
7
|
+
const red = (over: Partial<Red>): Red => RedSchema.parse({ source: `push`, scope: `app`, since: 100, ...over });
|
|
8
|
+
|
|
9
|
+
describe(`a project's push red`, () => {
|
|
10
|
+
test(`is the push red of that project, never a land check's red of the same folder or another project's`, () => {
|
|
11
|
+
const land = red({ source: `land` });
|
|
12
|
+
const other = red({ scope: `web` });
|
|
13
|
+
const mine = red({});
|
|
14
|
+
expect(pushRedOf([land, other, mine], `app`)).toBe(mine);
|
|
15
|
+
expect(pushRedOf([land, other], `app`)).toBeUndefined();
|
|
16
|
+
expect(pushRedOf(undefined, `app`)).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test(`names its hand-over by when it began, with the workspace root spelled as the workspace`, () => {
|
|
20
|
+
expect(pushFixBase(red({}))).toBe(pushFixConversationId(`app`, `red:100`));
|
|
21
|
+
expect(pushFixBase(red({ scope: `` }))).toBe(pushFixConversationId(`workspace`, `red:100`));
|
|
22
|
+
expect(pushFixBase(undefined)).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test(`that began later is a fresh attempt`, () => {
|
|
26
|
+
expect(pushFixBase(red({ since: 200 }))).toBe(pushFixConversationId(`app`, `red:200`));
|
|
27
|
+
});
|
|
28
|
+
});
|