@intentic/sandbox-contract 1.323.0 → 1.324.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 +7 -0
- package/dist/contracts/agent.contract.d.ts +48 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +84 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/approvals.contract.d.ts +31 -0
- package/dist/contracts/approvals.contract.d.ts.map +1 -1
- package/dist/contracts/approvals.contract.js +2 -2
- package/dist/contracts/approvals.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/runner.contract.d.ts +167 -133
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +8 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +74 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +54 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +11 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +39 -1
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +4 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-ui.d.ts +33 -0
- package/dist/events/agent-ui.d.ts.map +1 -0
- package/dist/events/agent-ui.js +24 -0
- package/dist/events/agent-ui.js.map +1 -0
- package/dist/events/system-events.d.ts +11 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +1 -0
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +48 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +2 -0
- package/dist/events/transcript.js.map +1 -1
- package/dist/index.d.ts +302 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/schemas/agents.d.ts +12 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +8 -1
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/approvals.d.ts +102 -0
- package/dist/schemas/approvals.d.ts.map +1 -1
- package/dist/schemas/approvals.js +45 -4
- package/dist/schemas/approvals.js.map +1 -1
- package/dist/schemas/automations.d.ts +4 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +2 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +10 -1
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/logs.d.ts +1 -0
- package/dist/schemas/logs.d.ts.map +1 -1
- package/dist/schemas/logs.js +4 -0
- package/dist/schemas/logs.js.map +1 -1
- package/dist/schemas/providers/usage.d.ts +1 -0
- package/dist/schemas/providers/usage.d.ts.map +1 -1
- package/dist/schemas/providers/usage.js +1 -0
- package/dist/schemas/providers/usage.js.map +1 -1
- package/dist/schemas/settings.d.ts +80 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +14 -3
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/system.d.ts +17 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +2 -1
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/updates.d.ts +30 -0
- package/dist/schemas/updates.d.ts.map +1 -1
- package/dist/schemas/updates.js +37 -0
- package/dist/schemas/updates.js.map +1 -1
- package/dist/state/definition.d.ts +10 -0
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +1 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +3 -0
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +1 -0
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +9 -0
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +5 -5
- package/src/contracts/approvals.contract.ts +2 -2
- package/src/contracts/system.contract.ts +14 -1
- package/src/events/agent-events.ts +6 -0
- package/src/events/agent-ui.ts +40 -0
- package/src/events/system-events.ts +2 -0
- package/src/events/transcript.ts +5 -0
- package/src/index.ts +1 -0
- package/src/schemas/agents.ts +16 -3
- package/src/schemas/approvals.ts +59 -4
- package/src/schemas/capabilities.ts +15 -2
- package/src/schemas/ci.ts +1 -1
- package/src/schemas/logs.ts +6 -0
- package/src/schemas/providers/usage.ts +3 -0
- package/src/schemas/settings-history.ts +3 -3
- package/src/schemas/settings.test.ts +5 -5
- package/src/schemas/settings.ts +47 -19
- package/src/schemas/system.ts +6 -1
- package/src/schemas/updates.ts +71 -0
- package/src/state/history-state.ts +3 -0
- package/src/state/runtime-state.ts +4 -0
- package/src/text/transcript-fold.test.ts +15 -0
- package/src/text/transcript-fold.ts +10 -0
package/src/schemas/settings.ts
CHANGED
|
@@ -20,13 +20,15 @@ export const SYSTEM_PROMPT_MAX = 20_000;
|
|
|
20
20
|
export const AUTO_MODEL_GUIDANCE_MAX = 2000;
|
|
21
21
|
// Rules: "at this moment, if this is true, do this". The owner's rules decide (land, hold, version); every command a
|
|
22
22
|
// moment runs is a repository's own check (`<repo>/.intentic/checks.json`), compiled into this same table by the daemon,
|
|
23
|
-
// so no command lives in settings. Nothing verifies
|
|
24
|
-
// the
|
|
23
|
+
// so no command lives in settings. Nothing verifies after a land or on the way to a push, and nothing here holds
|
|
24
|
+
// anything: what a check finds inside a turn is said back to the model, and CI checks what the owner pushes
|
|
25
|
+
// (schemas/ci.ts).
|
|
25
26
|
export const RuleMomentSchema = z.enum([
|
|
26
27
|
// A command here runs on the just-written file (`{file}` is its path); the cheapest moment to catch a defect.
|
|
27
28
|
"file.edited",
|
|
28
|
-
//
|
|
29
|
-
//
|
|
29
|
+
// An isolated turn about to stop: a repository's own `turn` check runs here once, on what the turn changed, and what
|
|
30
|
+
// it finds is said back to the model, which may fix it or say why not. It holds and refuses nothing. Only what a
|
|
31
|
+
// repository declares stands here; an owner's rule left here from before is inert, and no save stands a new one.
|
|
30
32
|
"turn.ending",
|
|
31
33
|
// An agent's turn is over and its delta is sitting on its branch. A rule here decides whether it lands.
|
|
32
34
|
"agent.finished",
|
|
@@ -55,8 +57,8 @@ export const RuleActionSchema = z.discriminatedUnion("kind", [
|
|
|
55
57
|
z.object({ kind: z.literal("builtin"), name: RuleBuiltinSchema }),
|
|
56
58
|
]);
|
|
57
59
|
export type RuleAction = z.infer<typeof RuleActionSchema>;
|
|
58
|
-
// `checks-failed` was a clean turn whose `turn.ending` check failed. Nothing produces it now
|
|
59
|
-
//
|
|
60
|
+
// `checks-failed` was a clean turn whose `turn.ending` check failed. Nothing produces it now: a `turn` check's findings
|
|
61
|
+
// are said back to the model and never decide whether its work lands. It stays so older rules naming it still read.
|
|
60
62
|
export const RuleOutcomeSchema = z.enum(["clean", "error", "conflict", "checks-failed"]);
|
|
61
63
|
export type RuleOutcome = z.infer<typeof RuleOutcomeSchema>;
|
|
62
64
|
// Every key absent means the rule always matches.
|
|
@@ -68,7 +70,8 @@ export const RuleConditionSchema = z.object({
|
|
|
68
70
|
// How the turn ended. Absent/empty ⇒ any.
|
|
69
71
|
outcome: z.array(RuleOutcomeSchema).optional(),
|
|
70
72
|
// The fraction of occasions a rule fires on, at a moment that draws one; absent ⇒ every occasion. No moment draws
|
|
71
|
-
//
|
|
73
|
+
// one any more (`turn.ending` did, before it held only the checks a repository declares), so a sampled rule fires
|
|
74
|
+
// every time.
|
|
72
75
|
sample: z.number().gt(0).lt(1).optional(),
|
|
73
76
|
});
|
|
74
77
|
export type RuleCondition = z.infer<typeof RuleConditionSchema>;
|
|
@@ -103,7 +106,8 @@ export const RuleSchema = z
|
|
|
103
106
|
path: ["action"],
|
|
104
107
|
});
|
|
105
108
|
export type Rule = z.infer<typeof RuleSchema>;
|
|
106
|
-
// A rule
|
|
109
|
+
// A rule the owner's settings no longer take: one at `turn.ending`, where only a repository's own checks stand, or one
|
|
110
|
+
// with the retired `verify-ui-edits` built-in. Read, so an older file parses, but never written again.
|
|
107
111
|
export const isRetiredMomentRule = (rule: Pick<Rule, "moment" | "action">): boolean =>
|
|
108
112
|
rule.moment === "turn.ending" || (rule.action.kind === "builtin" && rule.action.name === "verify-ui-edits");
|
|
109
113
|
// Kept out of the settings object on purpose: a firing is not an edit, and writing config on every push would make
|
|
@@ -314,6 +318,23 @@ export const SandboxSettingsSchema = z.object({
|
|
|
314
318
|
.describe(
|
|
315
319
|
"What share of conversations to keep on the long form, so the two can be compared. Whole conversations rather than individual turns, because the guidance sits in the prompt for the whole session.",
|
|
316
320
|
),
|
|
321
|
+
// Measured by the `clearing` experiment: the gateway in front of a native Claude turn replaces old tool results with
|
|
322
|
+
// a placeholder, a chunk at a time, so the cached prompt breaks once per chunk rather than on every call
|
|
323
|
+
// (privacy/gateway/tool-result-clearing.ts). Off by default until that experiment says it pays.
|
|
324
|
+
toolResultClearing: z
|
|
325
|
+
.boolean()
|
|
326
|
+
.default(false)
|
|
327
|
+
.describe(
|
|
328
|
+
"Replace old tool results from a Claude conversation once it grows long, a large chunk at a time, keeping the most recent ones whole. The agent sees a short placeholder where each one was and can run the tool again. Smaller prompts cost less and answer sooner, at the risk of the agent re-reading what it already saw.",
|
|
329
|
+
),
|
|
330
|
+
toolResultClearingHoldout: z
|
|
331
|
+
.number()
|
|
332
|
+
.min(0)
|
|
333
|
+
.max(1)
|
|
334
|
+
.default(0.5)
|
|
335
|
+
.describe(
|
|
336
|
+
"What share of conversations keep every tool result, so the two can be compared. Whole conversations rather than individual turns, because what was dropped stays dropped for the rest of the session.",
|
|
337
|
+
),
|
|
317
338
|
iqSearch: z
|
|
318
339
|
.boolean()
|
|
319
340
|
.default(false)
|
|
@@ -557,10 +578,11 @@ export const SandboxSettingsSchema = z.object({
|
|
|
557
578
|
.describe("How many levels deep the delegation may go, since a subagent can start subagents of its own."),
|
|
558
579
|
});
|
|
559
580
|
export type SandboxSettings = z.infer<typeof SandboxSettingsSchema>;
|
|
560
|
-
// What a save takes: the settings as read, less what is only read. `turn.ending`
|
|
561
|
-
//
|
|
581
|
+
// What a save takes: the settings as read, less what is only read. `turn.ending` holds only the checks a repository
|
|
582
|
+
// declares, which the daemon adds to a turn's rules and never saves, and the `verify-ui-edits` built-in is retired; a
|
|
583
|
+
// file written before still parses (and its conversion drops such rules), but no save may stand a new one.
|
|
562
584
|
export const SandboxSettingsWriteSchema = SandboxSettingsSchema.refine((settings) => !settings.rules.some(isRetiredMomentRule), {
|
|
563
|
-
message: "turn.ending
|
|
585
|
+
message: "turn.ending runs only the checks a repository declares in its own .intentic/checks.json, so a rule cannot stand there",
|
|
564
586
|
path: ["rules"],
|
|
565
587
|
});
|
|
566
588
|
|
|
@@ -632,9 +654,13 @@ export const SavingsArmSchema = z.object({ turns: z.number(), mean: z.number() }
|
|
|
632
654
|
// openingListings: directory listings a turn ran to orient itself (the project map).
|
|
633
655
|
// callsBeforeTarget: how far a turn walked before touching a file it went on to edit.
|
|
634
656
|
// failedCalls: tool calls that ended in error (the field notes, whose largest section is a failure taxonomy).
|
|
635
|
-
//
|
|
657
|
+
// roundTrips: model calls a turn took (the guidance form, whose batching and context-reuse paragraphs exist to save them).
|
|
658
|
+
// contextPerCall: prompt tokens per model call, cached or not (tool-result clearing, which exists to shrink it).
|
|
659
|
+
// Never cost: each mechanism moves one small part of a turn's work, inside the noise of the rest. Round trips are the
|
|
660
|
+
// nearest a reading comes to it, and still a count of calls, not of tokens or money; the prompt a call carries is the
|
|
661
|
+
// one exception, for the one mechanism that changes nothing else.
|
|
636
662
|
export const TurnMetricReadingSchema = z.object({
|
|
637
|
-
metric: z.enum(["searchCalls", "openingSearches", "openingListings", "callsBeforeTarget", "failedCalls"]),
|
|
663
|
+
metric: z.enum(["searchCalls", "openingSearches", "openingListings", "callsBeforeTarget", "failedCalls", "roundTrips", "contextPerCall"]),
|
|
638
664
|
on: SavingsArmSchema,
|
|
639
665
|
off: SavingsArmSchema,
|
|
640
666
|
// Additional control turns to reach a fixed target resolution, not today's delta (which inherits noise and always
|
|
@@ -698,6 +724,7 @@ export const SavingsReportSchema = z.object({
|
|
|
698
724
|
map: TurnExperimentSchema.optional(),
|
|
699
725
|
notes: TurnExperimentSchema.optional(),
|
|
700
726
|
guidance: TurnExperimentSchema.optional(),
|
|
727
|
+
clearing: TurnExperimentSchema.optional(),
|
|
701
728
|
});
|
|
702
729
|
export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
703
730
|
|
|
@@ -712,17 +739,18 @@ export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
|
712
739
|
// spelling for the daemon that reads it, the screen that names it and the demo that mimics it.
|
|
713
740
|
export const REPO_CHECKS_FILE = `${STATE_DIR}/checks.json`;
|
|
714
741
|
|
|
715
|
-
// Named for the occasion as a repository would say it: `edit` is `file.edited`
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
//
|
|
719
|
-
//
|
|
742
|
+
// Named for the occasion as a repository would say it: `edit` is `file.edited` and `turn` is `turn.ending`
|
|
743
|
+
// (rules/repo-checks.ts maps them). `edit` runs on one file (`{file}`) and its output rides back in the edit's own
|
|
744
|
+
// response, so a command declared there is paid per edit and has to take the file. `turn` runs once when an isolated
|
|
745
|
+
// turn is about to stop, on the whole tree, and what it prints when it fails is said back to the model once, which may
|
|
746
|
+
// fix it or say why not. `land` is retired: nothing runs after work lands any more (CI checks what the owner pushes), and
|
|
747
|
+
// a declaration naming it still reads but runs nothing. No check refuses a land, a commit or a push.
|
|
720
748
|
export const RepoCheckMomentSchema = z.enum(["edit", "turn", "land"]);
|
|
721
749
|
export type RepoCheckMoment = z.infer<typeof RepoCheckMomentSchema>;
|
|
722
750
|
|
|
723
751
|
export const RepoCheckSchema = z.object({
|
|
724
752
|
when: RepoCheckMomentSchema.describe(
|
|
725
|
-
"When to run it: `edit` on each file as it is written (`{file}` is its path)
|
|
753
|
+
"When to run it: `edit` on each file as it is written (`{file}` is its path); `turn` once when an isolated turn is about to stop, for at most three minutes, with what it prints on failure said back to the model once. Neither holds or refuses anything. `land` is retired and runs nothing, since CI checks what is pushed.",
|
|
726
754
|
),
|
|
727
755
|
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."),
|
|
728
756
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { StatePlanSchema } from "./state-plan.js";
|
|
3
|
-
import { UpdateOutcomeSchema, WithdrawnReleaseSchema } from "./updates.js";
|
|
3
|
+
import { AutoUpdateSchema, UpdateOutcomeSchema, WithdrawnReleaseSchema } from "./updates.js";
|
|
4
4
|
// version is this build's baked-in version; latest/updateAvailable come from comparing it to the published stable
|
|
5
5
|
// release.
|
|
6
6
|
// Whether a runtime can serve a turn, probed off the turn path. "unknown" reads as available-but-unverified, never as
|
|
@@ -122,6 +122,11 @@ export const InfoSchema = z.object({
|
|
|
122
122
|
.string()
|
|
123
123
|
.optional()
|
|
124
124
|
.describe("A release the owner chose to skip. While it is the newest, no update is offered; a newer one is. Absent when nothing is skipped."),
|
|
125
|
+
// Taking a downloaded update by itself at a quiet moment (system/updates/auto-update.ts); absent where the daemon
|
|
126
|
+
// cannot (a hosted machine, a checkout-built base, a daemon outside a container) and from a daemon older than it.
|
|
127
|
+
autoUpdate: AutoUpdateSchema.optional().describe(
|
|
128
|
+
"Whether and when this sandbox takes a downloaded update by itself, and what it is waiting on. Absent where it cannot: a hosted sandbox, one built from a checkout, or one older than automatic updates.",
|
|
129
|
+
),
|
|
125
130
|
});
|
|
126
131
|
export type Info = z.infer<typeof InfoSchema>;
|
|
127
132
|
// What the daemon could not read in its own `.intentic/` state files, and in the files on its volume that
|
package/src/schemas/updates.ts
CHANGED
|
@@ -54,3 +54,74 @@ export const RollbackTargetSchema = z.object({
|
|
|
54
54
|
version: z.string().optional().describe("What that image says it is. Absent when it would not say."),
|
|
55
55
|
});
|
|
56
56
|
export type RollbackTarget = z.infer<typeof RollbackTargetSchema>;
|
|
57
|
+
|
|
58
|
+
// UPDATING BY ITSELF. Everything an update costs nobody is done unasked: the machine downloads and builds the next
|
|
59
|
+
// release in the background (`ic sandbox prepare --auto`) and checks its state conversions against this sandbox's files.
|
|
60
|
+
// What is left is a restart of about half a minute, and THAT is what this decides the moment of: the daemon holds a staged
|
|
61
|
+
// update until nothing it can see would feel the restart (no agent mid-turn, nobody at the editor, terminals quiet,
|
|
62
|
+
// nothing due), says so to anyone still connected, and then hands the machine the same `update` the button sends, asking
|
|
63
|
+
// the next boot to pick up any turn the restart cut. Only a STAGED update is taken this way: one that still has to be
|
|
64
|
+
// downloaded is minutes of downtime, which stays a person's decision. `system/updates/auto-update.ts` in the daemon.
|
|
65
|
+
|
|
66
|
+
// One thing keeping an update waiting. `kind` is a string rather than an enum, as UpdateOutcome's `verb` is: a newer
|
|
67
|
+
// daemon's reason must not make an older page drop the whole answer. The ones this build names:
|
|
68
|
+
// - agents: an agent is mid-turn, or a subagent, a workflow or a land is still running (`names` says which).
|
|
69
|
+
// - people: somebody has the editor on screen and is using it (`names` says who).
|
|
70
|
+
// - terminal: a terminal printed something in the last few minutes, the trace of a person or a build still at work.
|
|
71
|
+
// - schedule: an automation or a scheduled message is due within minutes, and the restart would land on it (`until`).
|
|
72
|
+
// - paused: the owner asked for no automatic update until `until`.
|
|
73
|
+
// - machine: the machine that runs this sandbox is not connected, so nothing here can ask it to restart anything.
|
|
74
|
+
// - retry: the last try did not take; it is tried again at `until`.
|
|
75
|
+
// - consent: the release changes something developers build on (its breaking notes), so it waits for a person to take
|
|
76
|
+
// it, however quiet the sandbox is. Never lifts by itself.
|
|
77
|
+
export const AutoUpdateHoldSchema = z.object({
|
|
78
|
+
kind: z
|
|
79
|
+
.string()
|
|
80
|
+
.describe(
|
|
81
|
+
"What it waits on: agents (an agent is mid-turn, or a subagent, workflow or land is running), people (somebody is using the editor), terminal (a terminal was busy in the last few minutes), schedule (an automation or a scheduled message is due shortly), paused (the owner asked it to wait), machine (the machine that runs this sandbox is not connected), retry (the last try did not take), or consent (the release changes something developers build on, so a person takes it). A newer sandbox may name others.",
|
|
82
|
+
),
|
|
83
|
+
names: z.array(z.string()).optional().describe("Who or what, by name: the agents still working, the people at the editor. Absent when there is nobody to name."),
|
|
84
|
+
until: z
|
|
85
|
+
.number()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("When this lifts by itself, in milliseconds, for a hold with a moment of its own: a pause, a retry, an automation's next run."),
|
|
88
|
+
});
|
|
89
|
+
export type AutoUpdateHold = z.infer<typeof AutoUpdateHoldSchema>;
|
|
90
|
+
|
|
91
|
+
export const AutoUpdateSchema = z.object({
|
|
92
|
+
enabled: z.boolean().describe("Whether this sandbox takes downloaded updates by itself. On unless the owner turned it off."),
|
|
93
|
+
// A string for the same reason as a hold's kind.
|
|
94
|
+
phase: z
|
|
95
|
+
.string()
|
|
96
|
+
.describe(
|
|
97
|
+
"Where it stands: idle (nothing downloaded to take, or turned off), waiting (an update is downloaded and waits for the holds to clear), countdown (it restarts at startsAt unless somebody stops it), or updating (the machine is restarting this sandbox onto it now).",
|
|
98
|
+
),
|
|
99
|
+
version: z.string().optional().describe("The downloaded version it will take. Absent while idle, or when the download did not say."),
|
|
100
|
+
holds: z.array(AutoUpdateHoldSchema).describe("Everything keeping it waiting right now, the one most likely to last first. Empty unless waiting."),
|
|
101
|
+
startsAt: z.number().optional().describe("During a countdown, when the restart begins, in milliseconds."),
|
|
102
|
+
pausedUntil: z.number().optional().describe("The owner's pause: no automatic update before this moment, in milliseconds. Absent when not paused."),
|
|
103
|
+
failure: z.string().optional().describe("Why the last automatic try did not take, in the machine's own words, while that is still the news."),
|
|
104
|
+
// The update this daemon itself started, read back by the version it moved onto: what lets the card say the
|
|
105
|
+
// restart happened by itself, while nobody was looking.
|
|
106
|
+
lastApplied: z
|
|
107
|
+
.object({ at: z.number(), to: z.string().optional() })
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("The last update this sandbox took by itself: when it began and which version it moved onto."),
|
|
110
|
+
});
|
|
111
|
+
export type AutoUpdate = z.infer<typeof AutoUpdateSchema>;
|
|
112
|
+
|
|
113
|
+
// The owner's say over it. Every field is optional and only what is named changes: the switch, a pause (null lifts it),
|
|
114
|
+
// or "now", which skips the wait and the countdown for the update already downloaded.
|
|
115
|
+
export const AutoUpdateInputSchema = z.object({
|
|
116
|
+
enabled: z.boolean().optional().describe("Turn taking downloaded updates by itself on or off."),
|
|
117
|
+
pausedUntil: z
|
|
118
|
+
.number()
|
|
119
|
+
.nullable()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("Hold automatic updates until this moment, in milliseconds; null lifts a pause. A moment already past lifts it too."),
|
|
122
|
+
applyNow: z
|
|
123
|
+
.literal(true)
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Take the downloaded update now, without waiting for a quiet moment or counting down. Refused when nothing is downloaded."),
|
|
126
|
+
});
|
|
127
|
+
export type AutoUpdateInput = z.infer<typeof AutoUpdateInputSchema>;
|
|
@@ -88,6 +88,9 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
88
88
|
// last boot could not start (the daemon writes it, the host's probation reads it): all rebuilt by whoever next says.
|
|
89
89
|
{ path: "update-outcome.json", portability: "derived" },
|
|
90
90
|
{ path: "update-skipped.json", portability: "derived" },
|
|
91
|
+
// Whether this sandbox takes downloaded updates by itself, a pause, and the update it last started unasked
|
|
92
|
+
// (system/updates/update-policy.ts): about this machine's restarts, so a moved sandbox starts on the default.
|
|
93
|
+
{ path: "update-policy.json", portability: "derived" },
|
|
91
94
|
{ path: "boot-failure.json", portability: "derived" },
|
|
92
95
|
// A rebuild's ask that the next boot resume the turns it cuts (restart-resume.ts): about this machine's next boot only,
|
|
93
96
|
// read once by it, so a moved sandbox starts without one.
|
|
@@ -58,6 +58,10 @@ const RUNTIME_DOMAINS = [
|
|
|
58
58
|
|
|
59
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.
|
|
60
60
|
{ domain: "activity", invalidates: [["activity"], ["activity-status"]] },
|
|
61
|
+
|
|
62
|
+
// An automatic update started waiting, changed what it waits on, began counting down or handed itself to the
|
|
63
|
+
// machine: what every page needs to hear before the sandbox goes quiet under it, wherever the reader is.
|
|
64
|
+
{ domain: "update", invalidates: [["update"]] },
|
|
61
65
|
] as const satisfies readonly RuntimeDomainBinding[];
|
|
62
66
|
|
|
63
67
|
export const RUNTIME_DOMAIN_BINDINGS: readonly RuntimeDomainBinding[] = RUNTIME_DOMAINS;
|
|
@@ -408,6 +408,21 @@ describe("foldTurn", () => {
|
|
|
408
408
|
});
|
|
409
409
|
});
|
|
410
410
|
|
|
411
|
+
// What a runtime's extension said is a notice row a reopened chat still shows; its status line is live state only.
|
|
412
|
+
it("folds an extension's notice into a notice row and keeps its status entries out of the rows", () => {
|
|
413
|
+
const rows = foldOf("go", [
|
|
414
|
+
{ kind: "agent_status", key: "lint", text: "linting…", source: "lint-ext" },
|
|
415
|
+
{ kind: "agent_notice", level: "warning", text: "Command blocked by user", source: "guard-ext" },
|
|
416
|
+
{ kind: "agent_notice", level: "info", text: "Compacting soon" },
|
|
417
|
+
{ kind: "agent_notice", level: "error", text: " " },
|
|
418
|
+
]);
|
|
419
|
+
expect(rows.slice(1)).toEqual([
|
|
420
|
+
{ role: "notice", text: "Command blocked by user", agentNotice: { level: "warning", source: "guard-ext" } },
|
|
421
|
+
{ role: "notice", text: "Compacting soon", agentNotice: { level: "info" } },
|
|
422
|
+
]);
|
|
423
|
+
expect(rows.slice(1).map((row) => TranscriptRowSchema.parse(row))).toEqual(rows.slice(1));
|
|
424
|
+
});
|
|
425
|
+
|
|
411
426
|
// An agent's own install, let through the command gate, says where it writes before its output arrives.
|
|
412
427
|
it("says where an agent's own install writes, naming its projects", () => {
|
|
413
428
|
expect(foldOf("go", [{ kind: "install", reach: "own-copy", projects: ["video"] }]).at(-1)).toEqual({
|
|
@@ -459,6 +459,15 @@ export class TranscriptFold {
|
|
|
459
459
|
return this.pushRow(compactedRow());
|
|
460
460
|
case "install":
|
|
461
461
|
return this.pushRow(installRow(event));
|
|
462
|
+
case "agent_notice":
|
|
463
|
+
// A runtime's own extension or loop said it: a notice row, so a reopened conversation still says it.
|
|
464
|
+
return event.text.trim() === ""
|
|
465
|
+
? []
|
|
466
|
+
: this.pushRow({
|
|
467
|
+
role: "notice",
|
|
468
|
+
text: event.text,
|
|
469
|
+
agentNotice: { level: event.level, ...(event.source === undefined ? {} : { source: event.source }) },
|
|
470
|
+
});
|
|
462
471
|
case "error":
|
|
463
472
|
// Keeps a refusal (no prose from the provider) from reading as a session that ended mid-question.
|
|
464
473
|
// A refusal that ran nothing takes its message back out ahead of the notice standing in for it.
|
|
@@ -554,6 +563,7 @@ export class TranscriptFold {
|
|
|
554
563
|
case "account_usage":
|
|
555
564
|
case "context_usage":
|
|
556
565
|
case "mode":
|
|
566
|
+
case "agent_status":
|
|
557
567
|
case "done":
|
|
558
568
|
return [];
|
|
559
569
|
}
|