@intentic/sandbox-contract 1.310.0 → 1.312.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 +43 -159
- package/dist/chores/chores.js +6 -6
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/fix-deps.d.ts.map +1 -1
- package/dist/chores/fix-deps.js +3 -2
- package/dist/chores/fix-deps.js.map +1 -1
- package/dist/chores/prompt.d.ts.map +1 -1
- package/dist/chores/prompt.js +3 -2
- package/dist/chores/prompt.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +15 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +15 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +1228 -32
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +42 -3
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +2 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +2 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +2 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/offload.contract.d.ts +107 -0
- package/dist/contracts/offload.contract.d.ts.map +1 -0
- package/dist/contracts/offload.contract.js +53 -0
- package/dist/contracts/offload.contract.js.map +1 -0
- package/dist/contracts/ports.contract.d.ts +5 -0
- package/dist/contracts/ports.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +111 -0
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +3 -1
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +16 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +154 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +16 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +146 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +40 -3
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/documents/conversions.d.ts +81 -0
- package/dist/documents/conversions.d.ts.map +1 -0
- package/dist/documents/conversions.js +213 -0
- package/dist/documents/conversions.js.map +1 -0
- package/dist/documents/index.d.ts +3 -0
- package/dist/documents/index.d.ts.map +1 -0
- package/dist/documents/index.js +3 -0
- package/dist/documents/index.js.map +1 -0
- package/dist/documents/passthrough.d.ts +13 -0
- package/dist/documents/passthrough.d.ts.map +1 -0
- package/dist/documents/passthrough.js +101 -0
- package/dist/documents/passthrough.js.map +1 -0
- package/dist/events/agent-events.d.ts +20 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +6 -1
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/land-breakage.d.ts +3 -0
- package/dist/events/land-breakage.d.ts.map +1 -1
- package/dist/events/land-breakage.js +3 -0
- package/dist/events/land-breakage.js.map +1 -1
- package/dist/events/land-conflict.d.ts +3 -0
- package/dist/events/land-conflict.d.ts.map +1 -0
- package/dist/events/land-conflict.js +4 -0
- package/dist/events/land-conflict.js.map +1 -0
- package/dist/events/system-events.d.ts +114 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +3 -1
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +5 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +9 -1
- package/dist/events/transcript.js.map +1 -1
- package/dist/events/verify-nudge.d.ts.map +1 -1
- package/dist/events/verify-nudge.js.map +1 -1
- package/dist/front/generated/wire.d.ts +137 -0
- package/dist/front/generated/wire.d.ts.map +1 -0
- package/dist/front/generated/wire.js +2 -0
- package/dist/front/generated/wire.js.map +1 -0
- package/dist/front/terminal-frames.d.ts +17 -0
- package/dist/front/terminal-frames.d.ts.map +1 -0
- package/dist/front/terminal-frames.js +79 -0
- package/dist/front/terminal-frames.js.map +1 -0
- package/dist/ids/conversation-ids.d.ts +2 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +2 -0
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/index.d.ts +1773 -96
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/policy/command-run.d.ts +1 -1
- package/dist/policy/command-run.d.ts.map +1 -1
- package/dist/policy/command-run.js +7 -4
- package/dist/policy/command-run.js.map +1 -1
- package/dist/protocol/container-requirements.d.ts +0 -1
- package/dist/protocol/container-requirements.d.ts.map +1 -1
- package/dist/protocol/container-requirements.js +1 -2
- package/dist/protocol/container-requirements.js.map +1 -1
- package/dist/protocol/ingress-contract.d.ts +0 -11
- package/dist/protocol/ingress-contract.d.ts.map +1 -1
- package/dist/protocol/raw-routes.d.ts +36 -4
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +18 -13
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/route-meta.d.ts +2 -0
- package/dist/protocol/route-meta.d.ts.map +1 -1
- package/dist/protocol/route-meta.js.map +1 -1
- package/dist/protocol/runner-protocol.d.ts +34 -0
- package/dist/protocol/runner-protocol.d.ts.map +1 -1
- package/dist/protocol/runner-protocol.js +33 -0
- package/dist/protocol/runner-protocol.js.map +1 -1
- package/dist/protocol/tunnel-lanes.d.ts +10 -0
- package/dist/protocol/tunnel-lanes.d.ts.map +1 -0
- package/dist/protocol/tunnel-lanes.js +16 -0
- package/dist/protocol/tunnel-lanes.js.map +1 -0
- package/dist/schemas/activity.d.ts.map +1 -1
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agents.d.ts +145 -3
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +43 -9
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +43 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +3 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +4 -0
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/devices.d.ts +34 -6
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +8 -6
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +4 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +16 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +1 -0
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +8 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +3 -0
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +68 -0
- package/dist/schemas/keep-warm.d.ts.map +1 -0
- package/dist/schemas/keep-warm.js +74 -0
- package/dist/schemas/keep-warm.js.map +1 -0
- package/dist/schemas/metrics.d.ts +7 -0
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +1 -0
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/offload.d.ts +62 -0
- package/dist/schemas/offload.d.ts.map +1 -0
- package/dist/schemas/offload.js +23 -0
- package/dist/schemas/offload.js.map +1 -0
- package/dist/schemas/personas.js +3 -3
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/ports.d.ts +10 -0
- package/dist/schemas/ports.d.ts.map +1 -1
- package/dist/schemas/ports.js +8 -0
- package/dist/schemas/ports.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +29 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +7 -0
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +15 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +4 -0
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/providers/usage.d.ts +6 -0
- package/dist/schemas/providers/usage.d.ts.map +1 -1
- package/dist/schemas/providers/usage.js +4 -0
- package/dist/schemas/providers/usage.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +28 -0
- package/dist/schemas/settings-history.d.ts.map +1 -0
- package/dist/schemas/settings-history.js +39 -0
- package/dist/schemas/settings-history.js.map +1 -0
- package/dist/schemas/settings.d.ts +8 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +33 -3
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/system.d.ts +24 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +15 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workspace/mainline.d.ts +371 -0
- package/dist/schemas/workspace/mainline.d.ts.map +1 -0
- package/dist/schemas/workspace/mainline.js +134 -0
- package/dist/schemas/workspace/mainline.js.map +1 -0
- package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +34 -0
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +36 -4
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/definition.js.map +1 -1
- package/dist/state/fix-stance.d.ts.map +1 -1
- package/dist/state/fix-stance.js +1 -0
- 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 +5 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +6 -0
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +2 -0
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +22 -1
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +27 -1
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +3 -0
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +30 -8
- package/dist/text/transcript-fold.js.map +1 -1
- package/dist/time/zone.d.ts +1 -0
- package/dist/time/zone.d.ts.map +1 -1
- package/dist/time/zone.js +5 -0
- package/dist/time/zone.js.map +1 -1
- package/package.json +34 -22
- package/src/chores/chores.ts +6 -6
- package/src/chores/fix-deps.ts +3 -2
- package/src/chores/prompt.ts +3 -2
- package/src/contracts/agents.contract.ts +50 -3
- package/src/contracts/offload.contract.ts +60 -0
- package/src/contracts/runner.contract.ts +7 -1
- package/src/contracts/workspace.contract.ts +54 -2
- package/src/documents/conversions.ts +402 -0
- package/src/documents/index.ts +5 -0
- package/src/documents/passthrough.ts +139 -0
- package/src/events/agent-events.ts +9 -1
- package/src/events/land-breakage.ts +12 -0
- package/src/events/land-conflict.ts +12 -0
- package/src/events/system-events.ts +9 -3
- package/src/events/transcript.ts +14 -3
- package/src/events/verify-nudge.ts +4 -3
- package/src/front/generated/wire.ts +104 -0
- package/src/front/terminal-frames.fixture.json +55 -0
- package/src/front/terminal-frames.test.ts +63 -0
- package/src/front/terminal-frames.ts +102 -0
- package/src/ids/conversation-ids.ts +7 -0
- package/src/ids/hostnames.fixture.json +20 -0
- package/src/ids/hostnames.test.ts +20 -0
- package/src/index.ts +8 -1
- package/src/policy/command-run.test.ts +35 -0
- package/src/policy/command-run.ts +10 -5
- package/src/protocol/container-requirements.test.ts +2 -3
- package/src/protocol/container-requirements.ts +1 -7
- package/src/protocol/ingress-contract.fixture.json +77 -0
- package/src/protocol/ingress-contract.test.ts +18 -26
- package/src/protocol/ingress-contract.ts +3 -22
- package/src/protocol/raw-routes.ts +21 -14
- package/src/protocol/route-meta.ts +4 -0
- package/src/protocol/runner-protocol.ts +53 -0
- package/src/protocol/tunnel-lanes.json +136 -0
- package/src/protocol/tunnel-lanes.test.ts +30 -0
- package/src/protocol/tunnel-lanes.ts +32 -0
- package/src/schemas/activity.ts +3 -1
- package/src/schemas/agents.ts +66 -10
- package/src/schemas/capabilities.ts +6 -0
- package/src/schemas/devices.ts +16 -9
- package/src/schemas/git/git.ts +5 -0
- package/src/schemas/hosts.ts +8 -0
- package/src/schemas/keep-warm.test.ts +68 -0
- package/src/schemas/keep-warm.ts +124 -0
- package/src/schemas/metrics.ts +2 -0
- package/src/schemas/offload.ts +44 -0
- package/src/schemas/personas.ts +3 -3
- package/src/schemas/ports.ts +10 -0
- package/src/schemas/providers/plan-limits.ts +13 -0
- package/src/schemas/providers/provider-oauth.ts +6 -0
- package/src/schemas/providers/usage.ts +7 -0
- package/src/schemas/settings-history.ts +61 -0
- package/src/schemas/settings.ts +70 -19
- package/src/schemas/system.ts +17 -0
- package/src/schemas/workspace/mainline.ts +201 -0
- package/src/schemas/workspace/workspace-tree.ts +47 -0
- package/src/state/definition.ts +3 -2
- package/src/state/fix-stance.test.ts +14 -0
- package/src/state/fix-stance.ts +2 -0
- package/src/state/history-state.ts +15 -0
- package/src/state/runtime-state.ts +8 -0
- package/src/state/workspace-state.ts +35 -1
- package/src/text/transcript-fold.test.ts +53 -1
- package/src/text/transcript-fold.ts +53 -10
- package/src/time/zone.ts +7 -0
- package/src/webext/index.ts +1 -1
- package/dist/protocol/ingress-protocol.d.ts +0 -30
- package/dist/protocol/ingress-protocol.d.ts.map +0 -1
- package/dist/protocol/ingress-protocol.js +0 -285
- package/dist/protocol/ingress-protocol.js.map +0 -1
- package/dist/protocol/terminal-protocol.d.ts +0 -18
- package/dist/protocol/terminal-protocol.d.ts.map +0 -1
- package/dist/protocol/terminal-protocol.js +0 -2
- package/dist/protocol/terminal-protocol.js.map +0 -1
- package/dist/protocol/tunnel-heartbeat.d.ts +0 -19
- package/dist/protocol/tunnel-heartbeat.d.ts.map +0 -1
- package/dist/protocol/tunnel-heartbeat.js +0 -41
- package/dist/protocol/tunnel-heartbeat.js.map +0 -1
- package/src/protocol/ingress-protocol.test.ts +0 -535
- package/src/protocol/ingress-protocol.ts +0 -448
- package/src/protocol/terminal-protocol.ts +0 -13
- package/src/protocol/tunnel-heartbeat.test.ts +0 -75
- package/src/protocol/tunnel-heartbeat.ts +0 -67
package/src/schemas/settings.ts
CHANGED
|
@@ -13,13 +13,15 @@ export const SystemPromptModeSchema = z.enum(["intentic", "claude", "custom"]);
|
|
|
13
13
|
export type SystemPromptMode = z.infer<typeof SystemPromptModeSchema>;
|
|
14
14
|
// Excludes "custom": there is nothing to fetch, it's whatever the owner already typed into the settings field.
|
|
15
15
|
export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"]) });
|
|
16
|
-
// Rules: "at this moment, if this is true, do this". The owner's rules decide (land, hold, version)
|
|
17
|
-
//
|
|
18
|
-
//
|
|
16
|
+
// Rules: "at this moment, if this is true, do this". The owner's rules decide (land, hold, version); every command a
|
|
17
|
+
// moment runs is a repository's own check (`<repo>/.intentic/checks.json`), compiled into this same table by the daemon,
|
|
18
|
+
// so no command lives in settings. Nothing verifies inside a turn: the whole-tree check runs after work lands and never
|
|
19
|
+
// holds anything (schemas/workspace/mainline.ts).
|
|
19
20
|
export const RuleMomentSchema = z.enum([
|
|
20
21
|
// A command here runs on the just-written file (`{file}` is its path); the cheapest moment to catch a defect.
|
|
21
22
|
"file.edited",
|
|
22
|
-
//
|
|
23
|
+
// Retired: the assistant stopping. Nothing runs here any more and nothing sends a turn back to work; the value stays
|
|
24
|
+
// so settings written before it went still read, and a rule standing here is inert.
|
|
23
25
|
"turn.ending",
|
|
24
26
|
// An agent's turn is over and its delta is sitting on its branch. A rule here decides whether it lands.
|
|
25
27
|
"agent.finished",
|
|
@@ -30,7 +32,8 @@ export type RuleMoment = z.infer<typeof RuleMomentSchema>;
|
|
|
30
32
|
// command: a repository's check; its exit code is the verdict.
|
|
31
33
|
// verdict: allows or holds what is about to happen.
|
|
32
34
|
// builtin: a daemon behaviour that reads a record only the daemon keeps.
|
|
33
|
-
// verify-ui-edits:
|
|
35
|
+
// verify-ui-edits: retired with `turn.ending`, read only so older settings parse; whether a turn looked at the surfaces it
|
|
36
|
+
// changed is recorded on its card instead (AgentSummary.proof), never asked of the model.
|
|
34
37
|
// version-landed: commits what a land brought, in the main tree, under the subject drafted for it, and the tree's own
|
|
35
38
|
// remainder before the next isolated turn starts; the only way a person who never commits keeps every agent seeing
|
|
36
39
|
// the latest tree, since worktrees are cut from HEAD.
|
|
@@ -47,8 +50,8 @@ export const RuleActionSchema = z.discriminatedUnion("kind", [
|
|
|
47
50
|
z.object({ kind: z.literal("builtin"), name: RuleBuiltinSchema }),
|
|
48
51
|
]);
|
|
49
52
|
export type RuleAction = z.infer<typeof RuleActionSchema>;
|
|
50
|
-
// `checks-failed`
|
|
51
|
-
//
|
|
53
|
+
// `checks-failed` was a clean turn whose `turn.ending` check went red. Nothing produces it now that no check runs inside a
|
|
54
|
+
// turn, and a check never holds work; it stays so older rules naming it still read.
|
|
52
55
|
export const RuleOutcomeSchema = z.enum(["clean", "error", "conflict", "checks-failed"]);
|
|
53
56
|
export type RuleOutcome = z.infer<typeof RuleOutcomeSchema>;
|
|
54
57
|
// Every key absent means the rule always matches.
|
|
@@ -57,9 +60,10 @@ export const RuleConditionSchema = z.object({
|
|
|
57
60
|
repo: z.string().min(1).optional(),
|
|
58
61
|
// Globs the change has to touch for the rule to fire. Absent/empty ⇒ any.
|
|
59
62
|
paths: z.array(z.string().min(1)).max(20).optional(),
|
|
60
|
-
// How the turn ended. Absent/empty ⇒ any
|
|
63
|
+
// How the turn ended. Absent/empty ⇒ any.
|
|
61
64
|
outcome: z.array(RuleOutcomeSchema).optional(),
|
|
62
|
-
// The fraction of occasions a rule fires on, at
|
|
65
|
+
// The fraction of occasions a rule fires on, at a moment that draws one; absent ⇒ every occasion. No moment draws
|
|
66
|
+
// now that `turn.ending` is retired, so a sampled rule fires every time.
|
|
63
67
|
sample: z.number().gt(0).lt(1).optional(),
|
|
64
68
|
});
|
|
65
69
|
export type RuleCondition = z.infer<typeof RuleConditionSchema>;
|
|
@@ -182,7 +186,8 @@ export type SkillSwitch = z.infer<typeof SkillSwitchSchema>;
|
|
|
182
186
|
// Opt-in settings the /settings routes edit and streamAgent reads, defaulting off so each can be A/B tested (`skills`
|
|
183
187
|
// defaults on for the baked tools worth having, since a skill file is the only thing that tells the agent a baked
|
|
184
188
|
// binary exists). Every default lives in the schema, so an older settings file still parses and keeps the owner's other
|
|
185
|
-
// picks rather than failing whole.
|
|
189
|
+
// picks rather than failing whole. A setting removed or reshaped here gets its conversion in settings-history.ts, which
|
|
190
|
+
// also names every retired setting, none of whose names may come back.
|
|
186
191
|
|
|
187
192
|
export const SandboxSettingsSchema = z.object({
|
|
188
193
|
// The zone every WALL-CLOCK RULE in this sandbox is meant in — an automation's cron, and anything else that says
|
|
@@ -387,16 +392,62 @@ export const SandboxSettingsSchema = z.object({
|
|
|
387
392
|
.describe(
|
|
388
393
|
"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.",
|
|
389
394
|
),
|
|
395
|
+
// Off by default: every refresh spends the account's own allowance on a conversation nobody is using yet.
|
|
396
|
+
keepWarm: z
|
|
397
|
+
.boolean()
|
|
398
|
+
.default(false)
|
|
399
|
+
.describe(
|
|
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
|
+
),
|
|
390
425
|
// Worth it since the container is recreated on every update or environment approval — otherwise approving a
|
|
391
426
|
// Dockerfile change costs the run that asked for it.
|
|
392
|
-
// What happens to breakage found after the work that caused it has left the turn
|
|
393
|
-
// check red
|
|
394
|
-
//
|
|
427
|
+
// What happens to breakage found after the work that caused it has left the turn. A land that turns the main tree's own
|
|
428
|
+
// check red waits for the lands queued behind it to be checked too, then goes back to the conversation that landed it
|
|
429
|
+
// while that one still has it in mind, or to a fresh conversation when nobody can be named or the one named has gone
|
|
430
|
+
// cold; main's CI staying red on one failure gets a fix agent once pushes go quiet, with a fleet failure re-run instead.
|
|
431
|
+
// Off, all of it is only reported.
|
|
395
432
|
autoRepair: z
|
|
396
433
|
.boolean()
|
|
397
434
|
.default(true)
|
|
398
435
|
.describe(
|
|
399
|
-
"Whether breakage found after the work left its turn is repaired without asking
|
|
436
|
+
"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
|
+
),
|
|
438
|
+
// 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 (platform/resources/heavy-commands.ts), and one entry for the
|
|
440
|
+
// check after landing, each naming a runner id. A runner that is offline, outdated or full hands the work back here,
|
|
441
|
+
// which the command's output says. Never pushed to a runner itself (portability/definition.ts), which must not pass
|
|
442
|
+
// work on again.
|
|
443
|
+
offload: z
|
|
444
|
+
.object({
|
|
445
|
+
commands: z.record(z.string().min(1), z.string().min(1)).default({}),
|
|
446
|
+
landCheck: z.string().min(1).optional(),
|
|
447
|
+
})
|
|
448
|
+
.default({ commands: {} })
|
|
449
|
+
.describe(
|
|
450
|
+
"Which heavy work runs on a runner on one of your machines instead of this sandbox: agents' commands by the kind the heavy-command rules sort them into (tests, typechecks, verify…), and the check after landing. The code travels as it stands, uncommitted work included; the output streams back, and any file the command changed comes back with it. A machine that is offline, outdated or busy hands the work back to this sandbox, and the output says so.",
|
|
400
451
|
),
|
|
401
452
|
autoResumeOnRestart: z
|
|
402
453
|
.boolean()
|
|
@@ -424,7 +475,7 @@ export const SandboxSettingsSchema = z.object({
|
|
|
424
475
|
message: "a command belongs in the repository's own .intentic/checks.json, not in settings",
|
|
425
476
|
})
|
|
426
477
|
.describe(
|
|
427
|
-
"Standing decisions about the sandbox's own work: land or hold finished work, save a version of what landed
|
|
478
|
+
"Standing decisions about the sandbox's own work: land or hold finished work, save a version of what landed. Empty is the default and is exactly the behaviour of a fresh sandbox, because each of those defaults is what no rule matched means at its own moment. A command to run is a repository's own check, declared in its .intentic/checks.json.",
|
|
428
479
|
),
|
|
429
480
|
automationFailureLimit: z
|
|
430
481
|
.number()
|
|
@@ -596,9 +647,9 @@ export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
|
596
647
|
// spelling for the daemon that reads it, the screen that names it and the demo that mimics it.
|
|
597
648
|
export const REPO_CHECKS_FILE = `${STATE_DIR}/checks.json`;
|
|
598
649
|
|
|
599
|
-
// Named for the occasion as a repository would say it: `edit` is `file.edited`, `
|
|
600
|
-
//
|
|
601
|
-
//
|
|
650
|
+
// Named for the occasion as a repository would say it: `edit` is `file.edited`, and `land` is the daemon's run over the
|
|
651
|
+
// main tree after every land (rules/repo-checks.ts maps them). `turn` is retired: nothing runs when a turn ends any more,
|
|
652
|
+
// and a declaration naming it still reads but runs nothing. No check refuses a land, a commit or a push.
|
|
602
653
|
// `edit` runs on one file (`{file}`) and its output rides back in the edit's own response, so a command declared there is
|
|
603
654
|
// paid per edit and has to take the file.
|
|
604
655
|
export const RepoCheckMomentSchema = z.enum(["edit", "turn", "land"]);
|
|
@@ -606,7 +657,7 @@ export type RepoCheckMoment = z.infer<typeof RepoCheckMomentSchema>;
|
|
|
606
657
|
|
|
607
658
|
export const RepoCheckSchema = z.object({
|
|
608
659
|
when: RepoCheckMomentSchema.describe(
|
|
609
|
-
"When to run it: `edit` on each file as it is written (`{file}` is its path), `
|
|
660
|
+
"When to run it: `edit` on each file as it is written (`{file}` is its path), `land` on the main tree after finished work lands (absent, the package's own verify or test script runs there). `turn` is retired and runs nothing: checks no longer run while a conversation works.",
|
|
610
661
|
),
|
|
611
662
|
run: z.string().min(1).max(500).describe("The command, run in this repository's own directory, so it reads as it would in a terminal there."),
|
|
612
663
|
label: z.string().min(1).max(80).optional().describe("What to call it on screen. Absent names it after the command."),
|
package/src/schemas/system.ts
CHANGED
|
@@ -27,6 +27,23 @@ export const StagedUpdateSchema = z.object({
|
|
|
27
27
|
),
|
|
28
28
|
// Epoch ms the download finished; used to check this is still the update being offered.
|
|
29
29
|
at: z.number().describe("When the download finished, in milliseconds, which answers whether this is still the update being offered."),
|
|
30
|
+
// The staged build's state pre-flight (the daemon's state-plan.ts, run by `ic sandbox prepare` over read-only mounts
|
|
31
|
+
// of this sandbox's volumes): what its first boot converts, before anyone accepts the update.
|
|
32
|
+
plan: z
|
|
33
|
+
.object({
|
|
34
|
+
ok: z.boolean().describe("False when a conversion would fail on this sandbox's files, which refuses the update before anything is touched."),
|
|
35
|
+
downgrade: z.boolean().optional().describe("The staged build is older than the one that converted these files; it opens them read-only."),
|
|
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
|
+
})
|
|
45
|
+
.optional()
|
|
46
|
+
.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."),
|
|
30
47
|
});
|
|
31
48
|
export type StagedUpdate = z.infer<typeof StagedUpdateSchema>;
|
|
32
49
|
export const InfoSchema = z.object({
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// main-line checks: the whole-tree check the daemon runs after work lands, and what became of what it found
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { pushFixConversationId } from "../../ids/conversation-ids.js";
|
|
4
|
+
import { AgentRunPickSchema } from "../agent.js";
|
|
5
|
+
|
|
6
|
+
// Nothing is verified inside a conversation any more: a turn ends when the model says it is done, its work lands, and
|
|
7
|
+
// the main tree's own check runs afterwards, off everyone's clock, one project at a time. This is that check as the
|
|
8
|
+
// editor reads it: what is being measured, what is waiting, what the last run said, and who is working on a red one.
|
|
9
|
+
|
|
10
|
+
export const MainlineLandSchema = z.object({
|
|
11
|
+
conversationId: z.string().describe("The conversation whose work landed."),
|
|
12
|
+
title: z.string().optional().describe("Its title when it landed."),
|
|
13
|
+
at: z.number().describe("When the land asked for the check, in milliseconds."),
|
|
14
|
+
});
|
|
15
|
+
export type MainlineLand = z.infer<typeof MainlineLandSchema>;
|
|
16
|
+
|
|
17
|
+
// What happened to a red verdict's failures, decided once the lands behind it had their own check.
|
|
18
|
+
export const MainlineRoutingKindSchema = z.enum([
|
|
19
|
+
// More work landed while this ran; the check that measures it decides, so nobody is sent after a failure it may fix.
|
|
20
|
+
"waiting",
|
|
21
|
+
// A conversation still working touches what failed; nobody else is started until it stops.
|
|
22
|
+
"held",
|
|
23
|
+
// Sent back to the conversation that landed it, which still had the work in mind.
|
|
24
|
+
"original",
|
|
25
|
+
// A fresh conversation was started on it with the failures, the suspects' changes and where to read their sessions.
|
|
26
|
+
"fix-up",
|
|
27
|
+
// Nobody was sent: repairs are switched off, or no conversation could take it. An automation may.
|
|
28
|
+
"reported",
|
|
29
|
+
// Gone at the next check, before anybody was sent.
|
|
30
|
+
"resolved",
|
|
31
|
+
// The sends and fresh attempts a red streak allows are used up; it waits for a person.
|
|
32
|
+
"spent",
|
|
33
|
+
]);
|
|
34
|
+
export type MainlineRoutingKind = z.infer<typeof MainlineRoutingKindSchema>;
|
|
35
|
+
|
|
36
|
+
export const MainlineRoutingSchema = z.object({
|
|
37
|
+
kind: MainlineRoutingKindSchema.describe("What became of the failures."),
|
|
38
|
+
conversationId: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("The conversation working on them (the original or a fresh one), or the one still working that the repair waits for."),
|
|
42
|
+
at: z.number().describe("When that was decided, in milliseconds."),
|
|
43
|
+
detail: z.string().optional().describe("One sentence on why, in the sandbox's words."),
|
|
44
|
+
});
|
|
45
|
+
export type MainlineRouting = z.infer<typeof MainlineRoutingSchema>;
|
|
46
|
+
|
|
47
|
+
// The first failures a run names; the whole list stays in its terminal.
|
|
48
|
+
export const MAINLINE_FAILURES_KEPT = 30;
|
|
49
|
+
|
|
50
|
+
export const MainlineRunSchema = z.object({
|
|
51
|
+
project: z.string().describe("Which project, by folder relative to the workspace. Empty is the workspace root."),
|
|
52
|
+
command: z.string().describe("What ran."),
|
|
53
|
+
status: z.enum(["green", "red"]).describe("How it ended."),
|
|
54
|
+
startedAt: z.number().describe("When it started, in milliseconds."),
|
|
55
|
+
at: z.number().describe("When it ended, in milliseconds."),
|
|
56
|
+
lands: z.array(MainlineLandSchema).describe("The lands it answered for, oldest first. Empty for a run no land asked for."),
|
|
57
|
+
failures: z.array(z.string()).describe(`The first failures it named, at most ${MAINLINE_FAILURES_KEPT}.`),
|
|
58
|
+
failureCount: z.number().describe("How many failures it named in all. Zero on a red run that wrote no list."),
|
|
59
|
+
attempt: z.number().describe("How many red runs in a row this is for the project; zero when green."),
|
|
60
|
+
suspects: z
|
|
61
|
+
.array(z.string())
|
|
62
|
+
.optional()
|
|
63
|
+
.describe("The conversations whose lands these failures were laid at, when any could be named."),
|
|
64
|
+
routing: MainlineRoutingSchema.optional().describe("What became of a red run's failures. Absent on green, and until it is decided."),
|
|
65
|
+
});
|
|
66
|
+
export type MainlineRun = z.infer<typeof MainlineRunSchema>;
|
|
67
|
+
|
|
68
|
+
export const MainlineProjectSchema = z.object({
|
|
69
|
+
project: z.string().describe("Which project, by folder relative to the workspace. Empty is the workspace root."),
|
|
70
|
+
running: z
|
|
71
|
+
.object({
|
|
72
|
+
command: z.string().describe("What is running."),
|
|
73
|
+
startedAt: z.number().describe("When it started, in milliseconds."),
|
|
74
|
+
lands: z.array(MainlineLandSchema).describe("The lands it answers for."),
|
|
75
|
+
on: z.string().optional().describe("The runner it was sent to, on one of your machines. Absent when it runs in this sandbox."),
|
|
76
|
+
})
|
|
77
|
+
.optional()
|
|
78
|
+
.describe("The check running on it now, if any."),
|
|
79
|
+
queued: z.array(MainlineLandSchema).describe("Lands waiting for the next check, which will measure them together."),
|
|
80
|
+
last: MainlineRunSchema.optional().describe("Its most recent settled check."),
|
|
81
|
+
redSince: z.number().optional().describe("When its checks went red and stayed so, in milliseconds. Absent while green."),
|
|
82
|
+
});
|
|
83
|
+
export type MainlineProject = z.infer<typeof MainlineProjectSchema>;
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// WHAT A PUSH LEFT BEHIND. The pre-push hook never refuses (verify-push.mjs, `--advisory`): it measures, reports, and the
|
|
87
|
+
// push goes. What it found used to vanish with the terminal it printed to. It now leaves a report in the repository's
|
|
88
|
+
// git dir, the daemon files it here once the push has actually reached the remote, and each finding waits until a later
|
|
89
|
+
// measurement no longer prints it or somebody dismisses it. Nobody is sent after one: acting on it is the owner's call.
|
|
90
|
+
|
|
91
|
+
// What measured a finding: one of the repository's checks (by id), the linter, the assertion ratchet, the
|
|
92
|
+
// manifest/lockfile lockstep, or rustfmt. Only a check's or the linter's finding can be measured again; the other three
|
|
93
|
+
// are about the pushed commits themselves, so they end only when dismissed.
|
|
94
|
+
export const PushFindingKindSchema = z.enum(["check", "lint", "ratchet", "lockstep", "rustfmt"]);
|
|
95
|
+
export type PushFindingKind = z.infer<typeof PushFindingKindSchema>;
|
|
96
|
+
|
|
97
|
+
export const PushFindingStateSchema = z.enum([
|
|
98
|
+
// Still printed at the last measurement, and nobody has dismissed it.
|
|
99
|
+
"open",
|
|
100
|
+
// A later push, recheck or land check measured it and it was gone.
|
|
101
|
+
"resolved",
|
|
102
|
+
// Somebody decided it will not be fixed.
|
|
103
|
+
"dismissed",
|
|
104
|
+
]);
|
|
105
|
+
export type PushFindingState = z.infer<typeof PushFindingStateSchema>;
|
|
106
|
+
|
|
107
|
+
export const PushFindingSchema = z.object({
|
|
108
|
+
id: z.string().describe("Stable across pushes: the same problem found again is the same id."),
|
|
109
|
+
kind: PushFindingKindSchema.describe("What measured it."),
|
|
110
|
+
check: z.string().optional().describe("The check's own id, for a check's finding."),
|
|
111
|
+
gate: z
|
|
112
|
+
.enum(["code", "tidy"])
|
|
113
|
+
.optional()
|
|
114
|
+
.describe("For a check's finding: `code` means the tree fails the check whoever caused it; `tidy` means the push added this line."),
|
|
115
|
+
text: z.string().describe("The finding as the check printed it."),
|
|
116
|
+
command: z.string().optional().describe("The command that shows it again."),
|
|
117
|
+
commit: z
|
|
118
|
+
.object({ sha: z.string(), subject: z.string() })
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("The newest pushed commit that touched the path the finding names, when it names one."),
|
|
121
|
+
state: PushFindingStateSchema,
|
|
122
|
+
settledAt: z.number().optional().describe("When it was resolved or dismissed, in milliseconds."),
|
|
123
|
+
});
|
|
124
|
+
export type PushFinding = z.infer<typeof PushFindingSchema>;
|
|
125
|
+
|
|
126
|
+
export const MainlinePushSchema = z.object({
|
|
127
|
+
project: z.string().describe("Which project, by folder relative to the workspace. Empty is the workspace root."),
|
|
128
|
+
id: z.string().describe("The report's own id."),
|
|
129
|
+
at: z.number().describe("When the push check ran, in milliseconds."),
|
|
130
|
+
remote: z.string().optional().describe("The remote it was pushed to."),
|
|
131
|
+
branch: z.string().optional().describe("The branch it was pushed to."),
|
|
132
|
+
base: z.string().optional().describe("The commit the pushed range starts from; absent when the remote had nothing to compare with."),
|
|
133
|
+
head: z.string().describe("The commit that was pushed."),
|
|
134
|
+
commits: z.number().describe("How many commits the push carried."),
|
|
135
|
+
findings: z.array(PushFindingSchema).describe("What it found that this push brought in, open or not. Empty for a clean push."),
|
|
136
|
+
measuredAt: z.number().optional().describe("When its findings were last measured again, in milliseconds."),
|
|
137
|
+
});
|
|
138
|
+
export type MainlinePush = z.infer<typeof MainlinePushSchema>;
|
|
139
|
+
|
|
140
|
+
export const MainlineStatusSchema = z.object({
|
|
141
|
+
projects: z.array(MainlineProjectSchema).describe("Every project a land has been checked in, or is waiting to be."),
|
|
142
|
+
recent: z.array(MainlineRunSchema).describe("The latest settled checks across every project, newest first."),
|
|
143
|
+
pushes: z
|
|
144
|
+
.array(MainlinePushSchema)
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("The latest push checks across every project, newest first, with what each left behind. Absent from a daemon that records none."),
|
|
147
|
+
});
|
|
148
|
+
export type MainlineStatus = z.infer<typeof MainlineStatusSchema>;
|
|
149
|
+
|
|
150
|
+
export const MainlinePushDismissSchema = z.object({
|
|
151
|
+
project: z.string().describe("Which project, by folder relative to the workspace."),
|
|
152
|
+
ids: z.array(z.string()).optional().describe("Which findings. Leave it out for every open one in the project."),
|
|
153
|
+
restore: z.boolean().optional().describe("Undo: open the named dismissed findings again."),
|
|
154
|
+
});
|
|
155
|
+
export type MainlinePushDismiss = z.infer<typeof MainlinePushDismissSchema>;
|
|
156
|
+
|
|
157
|
+
export const MainlinePushDismissResultSchema = z.object({
|
|
158
|
+
changed: z.number().describe("How many findings changed state."),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
export const MainlinePushRecheckSchema = z.object({
|
|
162
|
+
project: z.string().describe("Which project, by folder relative to the workspace."),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export const MainlinePushRecheckResultSchema = z.object({
|
|
166
|
+
measured: z.boolean().describe("Whether the project could be measured at all; false leaves every finding as it was."),
|
|
167
|
+
resolved: z.number().describe("How many findings the measurement no longer saw."),
|
|
168
|
+
open: z.number().describe("How many are still open."),
|
|
169
|
+
});
|
|
170
|
+
export type MainlinePushRecheckResult = z.infer<typeof MainlinePushRecheckResultSchema>;
|
|
171
|
+
|
|
172
|
+
export const MainlinePushFixSchema = z.object({
|
|
173
|
+
project: z.string().describe("Which project's open push findings to hand over, by folder relative to the workspace."),
|
|
174
|
+
pick: AgentRunPickSchema.describe(
|
|
175
|
+
"Which model to open the conversation on, when somebody chose one. Leave it out for the sandbox's own choice.",
|
|
176
|
+
),
|
|
177
|
+
mode: z
|
|
178
|
+
.enum(["continue", "start-over"])
|
|
179
|
+
.optional()
|
|
180
|
+
.describe(
|
|
181
|
+
"What to do about an attempt already made at these findings: `continue` carries on in it, `start-over` files it away and opens the next attempt. Leave it out for the plain press.",
|
|
182
|
+
),
|
|
183
|
+
});
|
|
184
|
+
export type MainlinePushFix = z.infer<typeof MainlinePushFixSchema>;
|
|
185
|
+
|
|
186
|
+
export const MainlinePushFixResultSchema = z.object({
|
|
187
|
+
conversationId: z.string().describe("The conversation holding the findings. Open it to watch."),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// The oldest push in `project` that still has an open finding: what a hand-over is keyed by, so pressing again while
|
|
191
|
+
// any of its findings stand continues the same attempt, and a backlog that starts after everything was handled starts
|
|
192
|
+
// a fresh one.
|
|
193
|
+
export const oldestOpenPush = (pushes: readonly MainlinePush[], project: string): MainlinePush | undefined =>
|
|
194
|
+
pushes.findLast((push) => push.project === project && push.findings.some((finding) => finding.state === "open"));
|
|
195
|
+
|
|
196
|
+
// The conversation id a hand-over of `project`'s open push findings wears (attempt 1), derived so the editor and the
|
|
197
|
+
// daemon agree on whether an agent is already on them; undefined when nothing is open.
|
|
198
|
+
export const pushFindingsFixBase = (pushes: readonly MainlinePush[], project: string): string | undefined => {
|
|
199
|
+
const oldest = oldestOpenPush(pushes, project);
|
|
200
|
+
return oldest === undefined ? undefined : pushFixConversationId(project === "" ? "workspace" : project, `left:${oldest.head}`);
|
|
201
|
+
};
|
|
@@ -72,8 +72,33 @@ export const WorkspaceTreeSchema = z.object({
|
|
|
72
72
|
.describe(
|
|
73
73
|
"Folders whose whole contents are empty folders, and nothing else. Complete for the workspace, however much of the tree above was listed, and ordered like the tree, so a parent comes before the branch below it.",
|
|
74
74
|
),
|
|
75
|
+
generation: z
|
|
76
|
+
.number()
|
|
77
|
+
.int()
|
|
78
|
+
.nonnegative()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe(
|
|
81
|
+
"Which state of the shared tree this is; the changes that follow count from it. Absent for a conversation's own checkout, which is listed when asked and followed by no changes.",
|
|
82
|
+
),
|
|
75
83
|
});
|
|
76
84
|
export type WorkspaceTree = z.infer<typeof WorkspaceTreeSchema>;
|
|
85
|
+
// What moved in the shared tree between two generations: every folder whose own entries changed, parents first.
|
|
86
|
+
export const WorkspaceTreeDeltaSchema = z.object({
|
|
87
|
+
from: z.number().int().nonnegative().describe("The generation this applies on top of. A reader holding any other fetches the tree afresh instead."),
|
|
88
|
+
generation: z.number().int().nonnegative().describe("The generation it leaves the tree at."),
|
|
89
|
+
dirs: z
|
|
90
|
+
.array(
|
|
91
|
+
z.object({
|
|
92
|
+
path: z.string().describe("The folder, as a workspace path; empty for the workspace root."),
|
|
93
|
+
entries: z
|
|
94
|
+
.array(WorkspaceTreeEntrySchema)
|
|
95
|
+
.describe("Its entries now, without their contents: a folder whose own entries also changed comes as an item of its own."),
|
|
96
|
+
}),
|
|
97
|
+
)
|
|
98
|
+
.describe("Each folder that changed, parents before the folders inside them."),
|
|
99
|
+
barren: z.array(z.string()).optional().describe("The barren folders now, present only when that list moved."),
|
|
100
|
+
});
|
|
101
|
+
export type WorkspaceTreeDelta = z.infer<typeof WorkspaceTreeDeltaSchema>;
|
|
77
102
|
// Lazy-loads one directory's children past the tree walk's budget; depth reads several levels into one flat `entries`
|
|
78
103
|
// list instead of one request per directory.
|
|
79
104
|
export const WorkspaceChildrenQuerySchema = WorkspaceScopeSchema.extend({
|
|
@@ -253,6 +278,28 @@ export const WorkspaceExtractSchema = z.object({
|
|
|
253
278
|
),
|
|
254
279
|
});
|
|
255
280
|
export type WorkspaceExtract = z.infer<typeof WorkspaceExtractSchema>;
|
|
281
|
+
// A delete moves the entry into the daemon's trash rather than erasing it, so the gesture can be taken back.
|
|
282
|
+
export const WorkspaceDeletedSchema = z.object({
|
|
283
|
+
ok: z.literal(true).describe("Always true: the entry is gone from where it was, or was never there."),
|
|
284
|
+
trashed: z
|
|
285
|
+
.string()
|
|
286
|
+
.optional()
|
|
287
|
+
.describe(
|
|
288
|
+
"The trash id that brings it back through the restore call, for a day. Absent when there was nothing at that path to delete.",
|
|
289
|
+
),
|
|
290
|
+
});
|
|
291
|
+
export type WorkspaceDeleted = z.infer<typeof WorkspaceDeletedSchema>;
|
|
292
|
+
export const WorkspaceRestoreSchema = z.object({
|
|
293
|
+
trashed: z.string().min(1).describe("The trash id a delete answered with."),
|
|
294
|
+
});
|
|
295
|
+
export const WorkspaceRestoredSchema = z.object({
|
|
296
|
+
path: z
|
|
297
|
+
.string()
|
|
298
|
+
.describe(
|
|
299
|
+
"Where it came back, as a workspace path: where it was deleted from, or beside that under a `(restored)` name when something new has taken the name since.",
|
|
300
|
+
),
|
|
301
|
+
});
|
|
302
|
+
export type WorkspaceRestored = z.infer<typeof WorkspaceRestoredSchema>;
|
|
256
303
|
// Deterministic, no-LLM classification into coarse buckets, read-only, applied via /workspace/move. reason is the
|
|
257
304
|
// winning signal: magic:<mime>, ext:<ext>, repository:<marker>, text-content, or unknown.
|
|
258
305
|
export const WorkspaceBucketSchema = z.enum(["repositories", "documents", "media", "archives", "other"]);
|
package/src/state/definition.ts
CHANGED
|
@@ -48,9 +48,10 @@ const bareField = (field: z.ZodType): z.ZodType => {
|
|
|
48
48
|
return inner;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// Typed as the prefault it is, so the static input type says what the runtime does: an absent table is accepted.
|
|
52
|
+
const definitionSettings = (): z.ZodPrefault<z.ZodType<Partial<z.infer<typeof SandboxSettingsSchema>>>> => {
|
|
52
53
|
const shape = Object.fromEntries(Object.entries(SandboxSettingsSchema.shape).map(([key, field]) => [key, bareField(field).optional()]));
|
|
53
|
-
return z.strictObject(shape).prefault({}) as unknown as z.ZodType<Partial<z.infer<typeof SandboxSettingsSchema
|
|
54
|
+
return z.strictObject(shape).prefault({}) as unknown as z.ZodPrefault<z.ZodType<Partial<z.infer<typeof SandboxSettingsSchema>>>>;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export const SandboxDefinitionSchema = z.strictObject({
|
|
@@ -45,6 +45,20 @@ test("held work reads as a fix to review, whether the fleet called it ready or l
|
|
|
45
45
|
expect(fixStance(agent({ status: `idle`, diff: { files: 4, insertions: 120, deletions: 8 } })).kind).toBe(`ready`);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
test.each([
|
|
49
|
+
["holding work", { files: 4, insertions: 120, deletions: 8 }],
|
|
50
|
+
["holding nothing yet", undefined],
|
|
51
|
+
] as const)("a turn that ended waiting on its own watch, %s, is still working", (_case, diff) => {
|
|
52
|
+
const watches = [{ id: `watch-a1b2`, note: `CI on the pushed branch`, intervalSeconds: 60, deadlineAt: 9_000 }];
|
|
53
|
+
expect(fixStance(agent({ status: `idle`, watches, ...(diff === undefined ? {} : { diff }) }))).toStrictEqual({
|
|
54
|
+
kind: `working`,
|
|
55
|
+
ongoing: true,
|
|
56
|
+
retry: false,
|
|
57
|
+
label: `Agent working`,
|
|
58
|
+
hint: `An agent is already working on this failure. Open the conversation to watch it.`,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
48
62
|
// Diff present doesn't change a failed status to ready; it only earns a mention in the hint.
|
|
49
63
|
test("an agent that failed after writing files still reads as failed", () => {
|
|
50
64
|
const stance = fixStance(agent({ status: `error`, diff: { files: 2, insertions: 34, deletions: 6 } }));
|
package/src/state/fix-stance.ts
CHANGED
|
@@ -130,6 +130,8 @@ const RULES: readonly ((agent: AgentSummary) => FixStance | undefined)[] = [
|
|
|
130
130
|
? needsYou("Needs you", "The fix agent has stopped and is waiting on you.")
|
|
131
131
|
: undefined,
|
|
132
132
|
(agent) => (IN_FLIGHT.has(agent.status) ? WORKING : undefined),
|
|
133
|
+
// An armed watch wakes it again by itself, so neither what it holds nor holding nothing is its last word yet.
|
|
134
|
+
(agent) => ((agent.watches?.length ?? 0) > 0 ? WORKING : undefined),
|
|
133
135
|
(agent) => (holdingWork(agent) ? READY : undefined),
|
|
134
136
|
(agent) => (agent.status === "landed" ? LANDED : undefined),
|
|
135
137
|
];
|
|
@@ -22,6 +22,9 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
22
22
|
// conversation's own runtime session store (what `.intentic/records/sessions/claude/` holds for every other).
|
|
23
23
|
// Carried whole, like the conversation it is.
|
|
24
24
|
{ path: "conversations/", portability: "carry" },
|
|
25
|
+
// The long tool outputs those transcripts name rather than hold, stored once by content for every record naming
|
|
26
|
+
// them; a transcript without them keeps only each output's start.
|
|
27
|
+
{ path: "blobs/", portability: "carry" },
|
|
25
28
|
// Index of published-page conversations; the pages travel with /work anyway, this makes one withdrawable.
|
|
26
29
|
{ path: "shares.json", portability: "carry" },
|
|
27
30
|
{ path: "activity.jsonl", portability: "carry" },
|
|
@@ -67,6 +70,8 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
67
70
|
{ path: ".isolation-probe", portability: "derived" },
|
|
68
71
|
// Finished bundles, excluded so an export doesn't pack every prior export in; kept off /work, unwatched.
|
|
69
72
|
{ path: "exports/", portability: "derived" },
|
|
73
|
+
// Each transcript as it was before this format converted it, zstd-compressed: a local way back, never read here.
|
|
74
|
+
{ path: "backups/", portability: "derived" },
|
|
70
75
|
// Bundles being taken in, mid-review; excluded as someone else's. Deleted on apply/abandon, swept after a crash.
|
|
71
76
|
{ path: "arrivals/", portability: "derived" },
|
|
72
77
|
|
|
@@ -120,4 +125,14 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
120
125
|
// The owner's yes to each workspace extension's declared powers, decided again by the target's owner.
|
|
121
126
|
{ path: "extension-approvals.json", portability: "identity", note: "Approve the extensions written in this workspace again from Extensions." },
|
|
122
127
|
{ path: "local-cert/", portability: "identity" },
|
|
128
|
+
|
|
129
|
+
/* ---- the state engine's own bookkeeping (the daemon's store/evolution/state-journal.ts) ---- */
|
|
130
|
+
|
|
131
|
+
// Pre-images of the history files an update's conversions changed; `secret` since identity files are among them.
|
|
132
|
+
{ path: "converting/", portability: "secret", note: "An update's undo record stays with the sandbox it happened on." },
|
|
133
|
+
// Which conversion episodes are open or recently committed, and earlier addresses awaiting removal.
|
|
134
|
+
{ path: "state-journal.json", portability: "derived" },
|
|
135
|
+
// Every conversation the import of the pre-1.308 layout settled (the daemon's agents/registry/pre-1308-import.ts),
|
|
136
|
+
// so one deleted after its import is not brought back from the old files still on disk. About this volume only.
|
|
137
|
+
{ path: "pre-1308-import.json", portability: "derived" },
|
|
123
138
|
];
|
|
@@ -27,6 +27,10 @@ const RUNTIME_DOMAINS = [
|
|
|
27
27
|
// The agent's Chromiums and open pages, daemon-held, minted from its own browser tool-call hooks.
|
|
28
28
|
{ domain: "browsers", invalidates: [["browsers"]] },
|
|
29
29
|
|
|
30
|
+
// A connection's status as its last probe found it: pushed when a probe behind a served answer finds another one, and
|
|
31
|
+
// when a browser sign-in is saved or cleared, which is what moves an account's card off "log in".
|
|
32
|
+
{ domain: "capabilities", invalidates: [["capabilities"]] },
|
|
33
|
+
|
|
30
34
|
// Daemon-held; changes continuously (tokens, tool uses), so it's rate-limited rather than pushed per mutation.
|
|
31
35
|
{ domain: "subagents", invalidates: [["subagents"]] },
|
|
32
36
|
|
|
@@ -54,6 +58,10 @@ const RUNTIME_DOMAINS = [
|
|
|
54
58
|
|
|
55
59
|
// The audit log grew or a gateway's status moved or aged out; the log lives outside /work, so no file push carries it.
|
|
56
60
|
{ domain: "activity", invalidates: [["activity"], ["activity-status"]] },
|
|
61
|
+
|
|
62
|
+
// The main tree's own check after a land: queued, started, settled, or its red handed to somebody. Held in the
|
|
63
|
+
// daemon's memory as much as in a file, so a push is the only way a watcher hears it move.
|
|
64
|
+
{ domain: "mainline", invalidates: [["mainline"]] },
|
|
57
65
|
] as const satisfies readonly RuntimeDomainBinding[];
|
|
58
66
|
|
|
59
67
|
export const RUNTIME_DOMAIN_BINDINGS: readonly RuntimeDomainBinding[] = RUNTIME_DOMAINS;
|
|
@@ -267,6 +267,14 @@ const STATE_FILES = [
|
|
|
267
267
|
// Per-extension update/advisory/health findings, written by the periodic check and by update/revert. Pushed live
|
|
268
268
|
// so an auto-disabling advisory doesn't wait for a reload.
|
|
269
269
|
{ path: ".intentic/records/extension-updates.json", invalidates: ["extensions"], portability: "carry" },
|
|
270
|
+
// Every conversion an update ran over this workspace's files, newest last and capped (the daemon's
|
|
271
|
+
// state-convergence.ts). `carry`: what happened to the owner's files is their history, wherever it goes next.
|
|
272
|
+
{
|
|
273
|
+
path: ".intentic/records/conversions.json",
|
|
274
|
+
invalidates: [],
|
|
275
|
+
why: "Written once per update that converted something, at boot before any browser is connected; the update card reads the plan from the new image instead.",
|
|
276
|
+
portability: "carry",
|
|
277
|
+
},
|
|
270
278
|
// Owner's per-extension update posture (notify/agent/auto, advisory opt-out). `carry`: a decision about the
|
|
271
279
|
// extension, not the machine.
|
|
272
280
|
{ path: ".intentic/config/extension-update-policy.json", invalidates: ["extensions"], portability: "carry", versioned: true },
|
|
@@ -348,6 +356,14 @@ const STATE_FILES = [
|
|
|
348
356
|
why: "Scratch that agents and tools leave behind (build logs, demo checkouts); nothing reads it after the turn that wrote it. The state janitor empties it at boot.",
|
|
349
357
|
portability: "derived",
|
|
350
358
|
},
|
|
359
|
+
// What the file view deleted, one folder per delete, kept so Undo can put it back. A day is longer than any undo
|
|
360
|
+
// stack lives in a browser tab; the hourly janitor sweep drops what's older.
|
|
361
|
+
{
|
|
362
|
+
path: ".intentic/local/trash/",
|
|
363
|
+
invalidates: [],
|
|
364
|
+
why: "Entries deleted from the file view, held for a day so Undo can restore them; nothing renders it, and the janitor expires it.",
|
|
365
|
+
portability: "derived",
|
|
366
|
+
},
|
|
351
367
|
// Account for the pnpm store even though pnpm creates it, not the daemon.
|
|
352
368
|
{
|
|
353
369
|
path: ".intentic/local/.pnpm-store/",
|
|
@@ -366,7 +382,13 @@ const STATE_FILES = [
|
|
|
366
382
|
{
|
|
367
383
|
path: ".intentic/records/verify.json",
|
|
368
384
|
invalidates: [],
|
|
369
|
-
why: "The
|
|
385
|
+
why: "The main-line check's memory: each project's last verdict and red streak, and the latest runs with what became of each red one. Read through GET /workspace/mainline, which the daemon pushes itself (the `mainline` runtime domain), so no file watch is needed.",
|
|
386
|
+
portability: "carry",
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
path: ".intentic/records/push-checks.json",
|
|
390
|
+
invalidates: [],
|
|
391
|
+
why: "What each push check found and let through, per project, and what became of every finding: still open, measured gone, or dismissed. Filed from the report the pre-push hook leaves in the repository's git dir; read through GET /workspace/mainline, which the daemon pushes itself (the `mainline` runtime domain).",
|
|
370
392
|
portability: "carry",
|
|
371
393
|
},
|
|
372
394
|
{
|
|
@@ -375,6 +397,16 @@ const STATE_FILES = [
|
|
|
375
397
|
why: "A running check's wrapper artifacts (log + exit status), read once by the daemon when the panel finishes.",
|
|
376
398
|
portability: "derived",
|
|
377
399
|
},
|
|
400
|
+
// What an update converted before it booted all the way (the sandbox daemon's state-journal.ts): a copy of every
|
|
401
|
+
// file the new version changed, kept until it commits and for a grace window after. `secret` whatever the source
|
|
402
|
+
// was, since a vault's copy is a vault; locked below for the same reason.
|
|
403
|
+
{
|
|
404
|
+
path: ".intentic/secrets/converting/",
|
|
405
|
+
invalidates: [],
|
|
406
|
+
why: "Pre-images of the files an update's conversions changed; only the boot step reads them, to put a rolled-back version's files back.",
|
|
407
|
+
portability: "secret",
|
|
408
|
+
note: "An export does not carry an update's undo record; the target converts its own files.",
|
|
409
|
+
},
|
|
378
410
|
{
|
|
379
411
|
path: ".intentic/secrets/ci.json",
|
|
380
412
|
invalidates: [],
|
|
@@ -588,6 +620,8 @@ export const LOCKED_STATE_ENTRIES: ReadonlySet<string> = new Set([
|
|
|
588
620
|
"secrets/ci.json",
|
|
589
621
|
"secrets/doors.json",
|
|
590
622
|
"secrets/auth",
|
|
623
|
+
// An update's pre-images: copies of whatever it converted, vaults included.
|
|
624
|
+
"secrets/converting",
|
|
591
625
|
"records/sessions",
|
|
592
626
|
"local/browser",
|
|
593
627
|
// The provider CLI's own home, undeclared in the table above and so without a group; stays at the state dir root.
|