@intentic/sandbox-contract 1.315.0 → 1.317.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 +5 -5
- package/dist/contracts/agent.contract.d.ts +562 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +402 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +3 -3
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +16 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +10 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/ci.contract.d.ts +22 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +1 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +532 -0
- package/dist/contracts/needs.contract.d.ts.map +1 -0
- package/dist/contracts/needs.contract.js +80 -0
- package/dist/contracts/needs.contract.js.map +1 -0
- package/dist/contracts/runner.contract.d.ts +294 -122
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.d.ts +16 -0
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/secrets.contract.js +12 -2
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +79 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +2 -3
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +163 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +11 -0
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +15 -137
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +25 -15
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +309 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +2 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-words.d.ts.map +1 -1
- package/dist/events/agent-words.js +2 -1
- package/dist/events/agent-words.js.map +1 -1
- package/dist/events/land-breakage.d.ts +0 -2
- package/dist/events/land-breakage.d.ts.map +1 -1
- package/dist/events/land-breakage.js +0 -2
- package/dist/events/land-breakage.js.map +1 -1
- package/dist/events/need-wake.d.ts +17 -0
- package/dist/events/need-wake.d.ts.map +1 -0
- package/dist/events/need-wake.js +34 -0
- package/dist/events/need-wake.js.map +1 -0
- package/dist/events/system-events.d.ts +40 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +484 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +9 -0
- package/dist/events/transcript.js.map +1 -1
- package/dist/index.d.ts +1956 -284
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +1 -1
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/plan-pools.d.ts +2 -1
- package/dist/models/plan-pools.d.ts.map +1 -1
- package/dist/models/plan-pools.js +8 -4
- package/dist/models/plan-pools.js.map +1 -1
- package/dist/protocol/raw-routes.d.ts +9 -5
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +2 -1
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/request-id.d.ts +0 -1
- package/dist/protocol/request-id.d.ts.map +1 -1
- package/dist/protocol/request-id.js +0 -1
- package/dist/protocol/request-id.js.map +1 -1
- package/dist/schemas/agent.js +1 -1
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +58 -1
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +11 -2
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +19 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +19 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +16 -0
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/ci.d.ts +45 -0
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +18 -0
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/devices.d.ts +68 -1
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +10 -4
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/hosts.d.ts +2 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +2 -1
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/needs.d.ts +661 -0
- package/dist/schemas/needs.d.ts.map +1 -0
- package/dist/schemas/needs.js +196 -0
- package/dist/schemas/needs.js.map +1 -0
- package/dist/schemas/personas.js +2 -2
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +2 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +4 -0
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/secrets.d.ts +18 -0
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js +20 -0
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +1 -1
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +2 -1
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +2 -4
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +10 -13
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/state-plan.d.ts +1 -0
- package/dist/schemas/state-plan.d.ts.map +1 -1
- package/dist/schemas/state-plan.js +3 -1
- package/dist/schemas/state-plan.js.map +1 -1
- package/dist/schemas/system.d.ts +20 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +7 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/updates.d.ts +39 -0
- package/dist/schemas/updates.d.ts.map +1 -0
- package/dist/schemas/updates.js +31 -0
- package/dist/schemas/updates.js.map +1 -0
- package/dist/schemas/workspace/push-checks.d.ts +222 -0
- package/dist/schemas/workspace/push-checks.d.ts.map +1 -0
- package/dist/schemas/workspace/push-checks.js +97 -0
- package/dist/schemas/workspace/push-checks.js.map +1 -0
- package/dist/schemas/workspace/workspace-tree.d.ts +9 -0
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +12 -0
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +16 -4
- 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 +3 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +2 -2
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +1 -1
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +5 -11
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +2 -13
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +3 -0
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +5 -5
- package/src/contracts/agents.contract.ts +3 -3
- package/src/contracts/capabilities.contract.ts +14 -1
- package/src/contracts/needs.contract.ts +103 -0
- package/src/contracts/secrets.contract.ts +17 -3
- package/src/contracts/system.contract.ts +14 -0
- package/src/contracts/workspace.contract.ts +35 -21
- package/src/events/agent-events.ts +4 -0
- package/src/events/agent-words.ts +2 -1
- package/src/events/errands.test.ts +4 -4
- package/src/events/land-breakage.ts +5 -9
- package/src/events/need-wake.test.ts +55 -0
- package/src/events/need-wake.ts +71 -0
- package/src/events/transcript.ts +16 -0
- package/src/events/verify-nudge.ts +3 -3
- package/src/index.ts +8 -2
- package/src/models/agent-runtimes.ts +5 -4
- package/src/models/plan-pools.test.ts +15 -0
- package/src/models/plan-pools.ts +20 -4
- package/src/protocol/ingress-contract.fixture.json +2 -0
- package/src/protocol/raw-routes.ts +5 -3
- package/src/protocol/request-id.ts +0 -3
- package/src/schemas/agent.ts +1 -1
- package/src/schemas/agents.ts +19 -8
- package/src/schemas/automations.ts +2 -2
- package/src/schemas/capabilities.ts +28 -0
- package/src/schemas/ci.ts +25 -0
- package/src/schemas/devices.ts +25 -12
- package/src/schemas/hosts.ts +4 -1
- package/src/schemas/needs.ts +292 -0
- package/src/schemas/personas.ts +2 -2
- package/src/schemas/providers/plan-limits.ts +8 -0
- package/src/schemas/secrets.ts +23 -0
- package/src/schemas/settings-history.ts +3 -1
- package/src/schemas/settings.test.ts +4 -3
- package/src/schemas/settings.ts +25 -31
- package/src/schemas/speaker.ts +4 -4
- package/src/schemas/state-plan.ts +8 -1
- package/src/schemas/system.ts +15 -0
- package/src/schemas/updates.ts +56 -0
- package/src/schemas/workspace/{mainline.test.ts → push-checks.test.ts} +5 -5
- package/src/schemas/workspace/push-checks.ts +155 -0
- package/src/schemas/workspace/workspace-tree.ts +15 -0
- package/src/state/fix-stance.test.ts +2 -1
- package/src/state/history-state.ts +5 -0
- package/src/state/landed-commit.ts +1 -1
- package/src/state/red-streak.ts +4 -4
- package/src/state/runtime-state.ts +2 -3
- package/src/state/workspace-state.ts +5 -13
- package/src/text/transcript-fold.ts +4 -0
- package/dist/schemas/workspace/mainline.d.ts +0 -513
- package/dist/schemas/workspace/mainline.d.ts.map +0 -1
- package/dist/schemas/workspace/mainline.js +0 -182
- package/dist/schemas/workspace/mainline.js.map +0 -1
- package/src/schemas/workspace/mainline.ts +0 -268
package/src/schemas/devices.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// What one of the user's own machines is running.
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { parseHostConnection, type DeviceFacts, DeviceFactsSchema, MachineIdSchema, userDistrosOf, WslEnvironmentSchema } from "./hosts.js";
|
|
4
|
+
import { RollbackTargetSchema, UpdateOutcomeSchema } from "./updates.js";
|
|
4
5
|
import { DEV_VERSION } from "../state/versions.js";
|
|
5
6
|
// Desktop-sync report shape shared by the agent, daemon and browser, produced only by the agent's own `deviceReport`.
|
|
6
7
|
// The agent never reports `sandboxes`; the docker half is filled in by whoever reads the report, scoped to the reader's
|
|
@@ -76,6 +77,19 @@ export const DeviceSandboxSchema = z.object({
|
|
|
76
77
|
resources: SandboxResourcesSchema.optional(),
|
|
77
78
|
// The update `ic sandbox prepare` built and left waiting for this sandbox; absent when nothing is staged.
|
|
78
79
|
staged: z.object({ image: z.string(), version: z.string().optional(), channel: z.string().optional() }).optional(),
|
|
80
|
+
// Everything below is read off an `ic` new enough to know it; an older ic's listing simply lacks it.
|
|
81
|
+
// What the running image says it is.
|
|
82
|
+
version: z.string().optional(),
|
|
83
|
+
// An interrupted swap left the old container set aside with no replacement: the sandbox is down, and `start` (or
|
|
84
|
+
// any swap) puts it back. `running` is false while this is true.
|
|
85
|
+
parked: z.boolean().optional(),
|
|
86
|
+
// A swap has just happened and the previous version is still parked and ready: until this moment the host watches
|
|
87
|
+
// the new one and goes back by itself if it keeps crashing, never becomes ready, or loses its tunnel.
|
|
88
|
+
probationUntil: z.number().optional(),
|
|
89
|
+
// What the host last did about this sandbox's version (the same record the sandbox reads as `lastUpdate`).
|
|
90
|
+
lastUpdate: UpdateOutcomeSchema.optional(),
|
|
91
|
+
// Newest first: what `rollback` would return to, then the older versions kept on this machine for `rollback --to`.
|
|
92
|
+
rollbackTargets: z.array(RollbackTargetSchema).optional(),
|
|
79
93
|
});
|
|
80
94
|
export type DeviceSandbox = z.infer<typeof DeviceSandboxSchema>;
|
|
81
95
|
// One operation on one sandbox, streamed as lines ending in a `result` or `error` frame. `prepare` builds the pending
|
|
@@ -118,6 +132,9 @@ export const DeviceSandboxFlowSchema = z.strictObject({
|
|
|
118
132
|
slug: z.string().min(1),
|
|
119
133
|
// Approved overlay's sha256, required only by `rebuild`; only content matching it is ever built.
|
|
120
134
|
hash: z.string().optional(),
|
|
135
|
+
// `rollback` only: a version or pinned image from the sandbox's `rollbackTargets` to go back to instead of the
|
|
136
|
+
// previous one. Sent only to an agent that advertises `rollback-to`: an older one refuses the field.
|
|
137
|
+
to: z.string().min(1).optional(),
|
|
121
138
|
// `set-shape` only, both required by it: the whole shape, and when it takes effect.
|
|
122
139
|
// Strict too, unlike the same shape read off a report (where a newer ic's extra field must not fail an older
|
|
123
140
|
// reader): an order carrying a field this agent does not know is refused rather than carried out without it.
|
|
@@ -505,7 +522,7 @@ export const DeviceSchema = z.object({
|
|
|
505
522
|
// The host connection's key, when this machine is also a connected device. Absent otherwise.
|
|
506
523
|
hostId: z.string().optional(),
|
|
507
524
|
// The card that connection is of, beside the key rather than parsed out of it: the machine's name, a label. Absent
|
|
508
|
-
// with `hostId
|
|
525
|
+
// with `hostId`.
|
|
509
526
|
card: z.string().optional(),
|
|
510
527
|
// The computer this row is an environment of, from whichever door has said (MachineIdSchema): what `machinesOf`
|
|
511
528
|
// groups by. Absent when no door has, which leaves the row a computer of its own.
|
|
@@ -563,20 +580,15 @@ export const deviceDistro = (device: Device): string | undefined => {
|
|
|
563
580
|
const byEnvironment = (a: Device, b: Device): number =>
|
|
564
581
|
Number(isWslDevice(a)) - Number(isWslDevice(b)) || (deviceDistro(a) ?? a.label).localeCompare(deviceDistro(b) ?? b.label);
|
|
565
582
|
|
|
566
|
-
// The card a device's door hangs off: one card is one computer, so two doors naming the same card are one machine
|
|
567
|
-
// whatever either has said about itself — which is the whole of what an environment that has never connected says.
|
|
568
|
-
// The row's own label, else its key read by the one parser there is (a row from a daemon older than `card`).
|
|
569
|
-
export const deviceCard = (device: Pick<Device, "card" | "hostId">): string | undefined =>
|
|
570
|
-
device.card ?? (device.hostId === undefined ? undefined : parseHostConnection(device.hostId).card);
|
|
571
|
-
|
|
572
583
|
// The computer a device is on, by whichever door said: its row, its connect-time facts, its report, its sync enrollment.
|
|
573
584
|
export const deviceMachineId = (device: Device): string | undefined =>
|
|
574
585
|
device.machineId ?? device.facts?.machineId ?? device.report?.machineId ?? device.sync?.machineId;
|
|
575
586
|
|
|
576
|
-
// What makes two devices one computer: a shared card
|
|
577
|
-
//
|
|
587
|
+
// What makes two devices one computer: a shared card (the row's own `card`, never parsed back out of its key: one card
|
|
588
|
+
// is one computer, whatever either door has said about itself), or a shared machine id. Nothing else: a hostname is
|
|
589
|
+
// shared by a PC and its WSL distros and by two unrelated machines with the same name, so it joined things by accident.
|
|
578
590
|
const tokensOf = (device: Device): string[] => {
|
|
579
|
-
const card =
|
|
591
|
+
const { card } = device;
|
|
580
592
|
const machine = deviceMachineId(device);
|
|
581
593
|
return [...(card === undefined ? [] : [`card:${card.toLowerCase()}`]), ...(machine === undefined ? [] : [`machine:${machine}`])];
|
|
582
594
|
};
|
|
@@ -621,8 +633,9 @@ const machineOf = (environments: readonly Device[]): Machine => {
|
|
|
621
633
|
if (first === undefined) {
|
|
622
634
|
return { key: "", label: "", environments };
|
|
623
635
|
}
|
|
624
|
-
const named =
|
|
625
|
-
|
|
636
|
+
const named =
|
|
637
|
+
environments.map((device) => device.card).find((key) => key !== undefined) ?? (rest.length === 0 ? undefined : deviceMachineId(first));
|
|
638
|
+
const label = first.label === first.hostId ? (first.card ?? first.label) : first.label;
|
|
626
639
|
return { key: named ?? first.key, label, environments };
|
|
627
640
|
};
|
|
628
641
|
|
package/src/schemas/hosts.ts
CHANGED
|
@@ -68,10 +68,13 @@ export type DeviceFacts = z.infer<typeof DeviceFactsSchema>;
|
|
|
68
68
|
// - `set-shape`: the `set-shape`/`forget-shape` ops, and `start`/`restart` applying a saved shape, all through an `ic`
|
|
69
69
|
// whose `ic sandbox shape` takes the contract's own shape (`--set`); advertised only when the agent's `ic` does.
|
|
70
70
|
// Both features ride that verb, so an agent advertises both or neither.
|
|
71
|
-
|
|
71
|
+
// - `rollback-to`: the `rollback` op's `to`, going back to an older version kept on the machine rather than the
|
|
72
|
+
// previous one; advertised only when the agent's `ic sandbox rollback` takes `--to`.
|
|
73
|
+
export const DeviceFeatureSchema = z.enum(["reshape-later", "set-shape", "rollback-to"]);
|
|
72
74
|
export type DeviceFeature = z.infer<typeof DeviceFeatureSchema>;
|
|
73
75
|
export const DEVICE_FEATURE_RESHAPE_LATER: DeviceFeature = "reshape-later";
|
|
74
76
|
export const DEVICE_FEATURE_SET_SHAPE: DeviceFeature = "set-shape";
|
|
77
|
+
export const DEVICE_FEATURE_ROLLBACK_TO: DeviceFeature = "rollback-to";
|
|
75
78
|
// The features an agent advertised that this build knows; an unknown one is a newer agent's and means nothing here.
|
|
76
79
|
export const deviceFeatures = (facts: Pick<DeviceFacts, "features"> | undefined): DeviceFeature[] =>
|
|
77
80
|
(facts?.features ?? []).flatMap((feature) => {
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// A need: something an agent cannot finish its task without that only a person can provide (a connection, a secret's
|
|
4
|
+
// value, wider reach, a tool in the image). Unlike a parked card it outlives the turn that raised it: the sandbox keeps
|
|
5
|
+
// it until someone answers, draws it wherever it is waiting, and wakes the conversation when it is met
|
|
6
|
+
// (docs/architecture/needs.md). Every word on a need is the daemon's except `why`, the agent's own case.
|
|
7
|
+
|
|
8
|
+
export const NEED_KINDS = ["capability", "secret", "grant", "release", "environment"] as const;
|
|
9
|
+
export const NeedKindSchema = z.enum(NEED_KINDS).describe("What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image.");
|
|
10
|
+
export type NeedKind = z.infer<typeof NeedKindSchema>;
|
|
11
|
+
|
|
12
|
+
// `working` is a yes still being carried out: a connection whose form is open, an overlay approved but not yet built.
|
|
13
|
+
export const NeedStatusSchema = z
|
|
14
|
+
.enum(["open", "working", "met", "declined", "cancelled"])
|
|
15
|
+
.describe(
|
|
16
|
+
"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away).",
|
|
17
|
+
);
|
|
18
|
+
export type NeedStatus = z.infer<typeof NeedStatusSchema>;
|
|
19
|
+
|
|
20
|
+
export const OPEN_NEED_STATUSES: readonly NeedStatus[] = ["open", "working"];
|
|
21
|
+
export const isOpenNeed = (need: { readonly status: NeedStatus }): boolean => OPEN_NEED_STATUSES.includes(need.status);
|
|
22
|
+
|
|
23
|
+
// A setting value as a form holds it: every field of a catalog entry is a string on the way in.
|
|
24
|
+
const FieldValuesSchema = z.record(z.string(), z.string());
|
|
25
|
+
|
|
26
|
+
export const CapabilityNeedModeSchema = z
|
|
27
|
+
.enum(["connect", "reconnect", "change"])
|
|
28
|
+
.describe("Connect something new, give a connected one a credential that works again, or change a setting on a connected one.");
|
|
29
|
+
export type CapabilityNeedMode = z.infer<typeof CapabilityNeedModeSchema>;
|
|
30
|
+
|
|
31
|
+
export const CapabilityNeedSchema = z.object({
|
|
32
|
+
kind: z.literal("capability"),
|
|
33
|
+
entry: z.string().min(1).describe("The catalog entry, as the catalog names it."),
|
|
34
|
+
name: z.string().describe("What the catalog calls it, never the agent's spelling."),
|
|
35
|
+
mode: CapabilityNeedModeSchema,
|
|
36
|
+
instance: z.string().optional().describe("The connection a reconnect or a change is about."),
|
|
37
|
+
target: z.string().optional().describe("The site, host or address the connection is for, when the entry can hold several."),
|
|
38
|
+
prefill: FieldValuesSchema.optional().describe(
|
|
39
|
+
"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.",
|
|
40
|
+
),
|
|
41
|
+
changes: FieldValuesSchema.optional().describe("For a change: each setting and the value it would take. Never a credential."),
|
|
42
|
+
reason: z.string().optional().describe("The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting."),
|
|
43
|
+
reported: z
|
|
44
|
+
.boolean()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe(
|
|
47
|
+
"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.",
|
|
48
|
+
),
|
|
49
|
+
});
|
|
50
|
+
export type CapabilityNeed = z.infer<typeof CapabilityNeedSchema>;
|
|
51
|
+
|
|
52
|
+
// The alphabet a `{{secret:NAME}}` reference resolves; a pasted value is stored under exactly this name.
|
|
53
|
+
export const SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
54
|
+
|
|
55
|
+
export const SecretNeedSchema = z.object({
|
|
56
|
+
kind: z.literal("secret"),
|
|
57
|
+
name: z.string().regex(SECRET_NAME).describe("The name it is stored under, and what `{{secret:NAME}}` will resolve."),
|
|
58
|
+
where: z.string().max(200).optional().describe("How it will be used: the header, the command, the site it goes to."),
|
|
59
|
+
link: z
|
|
60
|
+
.string()
|
|
61
|
+
.url()
|
|
62
|
+
.refine((url) => url.startsWith("https://"), "only an https link")
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Where a person gets one, shown as a link on the card."),
|
|
65
|
+
hint: z.string().max(120).optional().describe("What a valid one looks like, so a wrong paste is caught by eye."),
|
|
66
|
+
replace: z.boolean().optional().describe("One is stored under this name and is being refused: the ask is for a new value in its place."),
|
|
67
|
+
});
|
|
68
|
+
export type SecretNeed = z.infer<typeof SecretNeedSchema>;
|
|
69
|
+
|
|
70
|
+
export const GrantSubjectSchema = z
|
|
71
|
+
.enum(["capability", "folder", "shelf", "site"])
|
|
72
|
+
.describe(
|
|
73
|
+
"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow.",
|
|
74
|
+
);
|
|
75
|
+
export type GrantSubject = z.infer<typeof GrantSubjectSchema>;
|
|
76
|
+
|
|
77
|
+
// A site as a person's browser grants it: the host alone, whatever scheme, path or pattern it was written with
|
|
78
|
+
// ("https://github.com/*" and "github.com" are one site). What a site grant need is keyed and met by.
|
|
79
|
+
export const grantSite = (origin: string): string =>
|
|
80
|
+
origin
|
|
81
|
+
.trim()
|
|
82
|
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "")
|
|
83
|
+
.replace(/[/?#].*$/, "")
|
|
84
|
+
.toLowerCase();
|
|
85
|
+
|
|
86
|
+
export const GRANT_SHELVES = ["files", "shell", "code", "web", "browser", "delegate", "sandbox"] as const;
|
|
87
|
+
export const GrantShelfSchema = z.enum(GRANT_SHELVES);
|
|
88
|
+
export type GrantShelf = z.infer<typeof GrantShelfSchema>;
|
|
89
|
+
|
|
90
|
+
export const GrantScopeSchema = z
|
|
91
|
+
.enum(["conversation", "persona"])
|
|
92
|
+
.describe("How far a yes goes: this conversation only, or the persona itself, for every conversation that wears it.");
|
|
93
|
+
export type GrantScope = z.infer<typeof GrantScopeSchema>;
|
|
94
|
+
|
|
95
|
+
export const GrantNeedSchema = z.object({
|
|
96
|
+
kind: z.literal("grant"),
|
|
97
|
+
subject: GrantSubjectSchema,
|
|
98
|
+
what: z.string().min(1).describe("The capability's id, the folder, or the shelf."),
|
|
99
|
+
label: z.string().describe("What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."),
|
|
100
|
+
persona: z.string().optional().describe("The persona that withholds it, when one does."),
|
|
101
|
+
scope: GrantScopeSchema.optional().describe("How far the yes went, once there was one."),
|
|
102
|
+
});
|
|
103
|
+
export type GrantNeed = z.infer<typeof GrantNeedSchema>;
|
|
104
|
+
|
|
105
|
+
export const ReleaseNeedSchema = z.object({
|
|
106
|
+
kind: z.literal("release"),
|
|
107
|
+
subject: z.string().min(1).describe("The gated account or connector."),
|
|
108
|
+
approvers: z.array(z.string()).describe("Who may release it. Anyone else's answer is refused and leaves it waiting."),
|
|
109
|
+
});
|
|
110
|
+
export type ReleaseNeed = z.infer<typeof ReleaseNeedSchema>;
|
|
111
|
+
|
|
112
|
+
export const EnvironmentNeedSchema = z.object({
|
|
113
|
+
kind: z.literal("environment"),
|
|
114
|
+
tool: z.string().min(1).describe("What the steps install, the name the proposal is filed under."),
|
|
115
|
+
steps: z.string().min(1).describe("The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."),
|
|
116
|
+
approvedHash: z.string().optional().describe("The overlay these steps were approved into; met once the running container was built from it."),
|
|
117
|
+
});
|
|
118
|
+
export type EnvironmentNeed = z.infer<typeof EnvironmentNeedSchema>;
|
|
119
|
+
|
|
120
|
+
export const NeedSubjectSchema = z
|
|
121
|
+
.discriminatedUnion("kind", [CapabilityNeedSchema, SecretNeedSchema, GrantNeedSchema, ReleaseNeedSchema, EnvironmentNeedSchema])
|
|
122
|
+
.describe("What exactly is asked for.");
|
|
123
|
+
export type NeedSubject = z.infer<typeof NeedSubjectSchema>;
|
|
124
|
+
|
|
125
|
+
// How the agent learned the outcome: the call it raised the need with, words into its live turn, a turn of its own, or
|
|
126
|
+
// queued behind what the conversation was doing.
|
|
127
|
+
export const NeedToldSchema = z.enum(["call", "turn", "queued"]);
|
|
128
|
+
export type NeedTold = z.infer<typeof NeedToldSchema>;
|
|
129
|
+
|
|
130
|
+
export const NeedSchema = z.object({
|
|
131
|
+
id: z.string().describe("The need's handle, the one `needs cancel` takes."),
|
|
132
|
+
conversationId: z.string().describe("The conversation that asked, and the one its answer wakes."),
|
|
133
|
+
subject: NeedSubjectSchema,
|
|
134
|
+
title: z.string().describe("The one line it leads with, in the daemon's words."),
|
|
135
|
+
why: z.string().max(280).optional().describe("The agent's case for it, and the only words on a need that are the agent's."),
|
|
136
|
+
status: NeedStatusSchema,
|
|
137
|
+
createdAt: z.number().describe("When it was raised, in milliseconds."),
|
|
138
|
+
updatedAt: z.number().describe("When it last moved, in milliseconds."),
|
|
139
|
+
answeredBy: z.string().optional().describe("Who answered it, as the sandbox verified them."),
|
|
140
|
+
outcome: z.string().optional().describe("How it ended, in the daemon's words, once it has."),
|
|
141
|
+
told: NeedToldSchema.optional().describe("How the agent heard the outcome, once it has."),
|
|
142
|
+
unattended: z.boolean().optional().describe("Raised by a turn nobody was watching, so the card waited for whoever came next."),
|
|
143
|
+
});
|
|
144
|
+
export type Need = z.infer<typeof NeedSchema>;
|
|
145
|
+
|
|
146
|
+
// What an agent may send: the kind and what it can know about it. Names, titles and every other field the card shows are
|
|
147
|
+
// the daemon's to fill in, from the catalog, the persona, the gate policy.
|
|
148
|
+
export const NeedAskSchema = z.discriminatedUnion("kind", [
|
|
149
|
+
z.object({
|
|
150
|
+
kind: z.literal("capability"),
|
|
151
|
+
entry: z.string().min(1).describe("The catalog entry or a connected instance's id."),
|
|
152
|
+
target: z.string().max(200).optional().describe("The site, host or address it is for."),
|
|
153
|
+
set: FieldValuesSchema.optional().describe("Settings to fill in, or to change on a connected one. Credentials are refused."),
|
|
154
|
+
reconnect: z
|
|
155
|
+
.boolean()
|
|
156
|
+
.optional()
|
|
157
|
+
.describe("It is connected, but its credential is being refused: ask for a new one rather than being told to use it."),
|
|
158
|
+
}),
|
|
159
|
+
SecretNeedSchema,
|
|
160
|
+
z.object({
|
|
161
|
+
kind: z.literal("grant"),
|
|
162
|
+
subject: GrantSubjectSchema,
|
|
163
|
+
what: z.string().min(1).max(400),
|
|
164
|
+
}),
|
|
165
|
+
z.object({ kind: z.literal("release"), subject: z.string().min(1) }),
|
|
166
|
+
z.object({ kind: z.literal("environment"), tool: z.string().min(1).max(64), steps: z.string().min(1).max(20_000) }),
|
|
167
|
+
]);
|
|
168
|
+
export type NeedAsk = z.infer<typeof NeedAskSchema>;
|
|
169
|
+
|
|
170
|
+
// The longest a raising call holds: under the agent shell's 110-second cutoff, so the answer returns on the call.
|
|
171
|
+
export const NEED_WAIT_MAX_S = 100;
|
|
172
|
+
export const NEED_WAIT_DEFAULT_S = 90;
|
|
173
|
+
|
|
174
|
+
export const NeedRaiseSchema = z.object({
|
|
175
|
+
ask: NeedAskSchema,
|
|
176
|
+
why: z.string().max(280).optional(),
|
|
177
|
+
wait: z
|
|
178
|
+
.number()
|
|
179
|
+
.int()
|
|
180
|
+
.min(0)
|
|
181
|
+
.max(NEED_WAIT_MAX_S)
|
|
182
|
+
.optional()
|
|
183
|
+
.describe("Seconds to hold the call for an answer. Absent is 90 for a watched turn and 0 for an unattended one."),
|
|
184
|
+
});
|
|
185
|
+
export type NeedRaise = z.infer<typeof NeedRaiseSchema>;
|
|
186
|
+
|
|
187
|
+
// How a raising call ended. `met`: usable now, exit 0. `open`: asked and still waiting, exit 3; the answer arrives in the
|
|
188
|
+
// conversation by itself. `refused`: nothing was raised or it was declined, exit 1.
|
|
189
|
+
export const NeedRaisedSchema = z.object({
|
|
190
|
+
state: z.enum(["met", "open", "refused"]),
|
|
191
|
+
message: z.string().describe("The sentence the CLI prints, written for the agent to act on."),
|
|
192
|
+
need: NeedSchema.optional(),
|
|
193
|
+
code: z.string().optional().describe("A refusal's type, for a script to branch on."),
|
|
194
|
+
});
|
|
195
|
+
export type NeedRaised = z.infer<typeof NeedRaisedSchema>;
|
|
196
|
+
|
|
197
|
+
// A person's answer, by what the card offered. A secret's value travels on its own route (needs.contract.ts), never here.
|
|
198
|
+
export const NeedAnswerSchema = z.discriminatedUnion("kind", [
|
|
199
|
+
z.object({ kind: z.literal("decline"), note: z.string().max(500).optional().describe("Why not, passed to the agent.") }),
|
|
200
|
+
// A connection's yes before its form is saved: moves the need to working, so the board keeps saying so.
|
|
201
|
+
z.object({ kind: z.literal("accept") }),
|
|
202
|
+
// A change to a connected capability's settings, applied as the card showed it.
|
|
203
|
+
z.object({ kind: z.literal("apply") }),
|
|
204
|
+
z.object({ kind: z.literal("grant"), scope: GrantScopeSchema }),
|
|
205
|
+
z.object({ kind: z.literal("release") }),
|
|
206
|
+
z.object({ kind: z.literal("approve") }),
|
|
207
|
+
]);
|
|
208
|
+
export type NeedAnswer = z.infer<typeof NeedAnswerSchema>;
|
|
209
|
+
|
|
210
|
+
export const NeedIdParamSchema = z.object({ id: z.string().min(1).describe("Which need.") });
|
|
211
|
+
export const NeedAnswerInputSchema = NeedIdParamSchema.extend({ answer: NeedAnswerSchema });
|
|
212
|
+
export const NeedSecretInputSchema = NeedIdParamSchema.extend({
|
|
213
|
+
value: z.string().min(1).max(64_000).describe("The secret's value. Stored, never echoed, never written into a transcript."),
|
|
214
|
+
});
|
|
215
|
+
export const NeedsQuerySchema = z.object({
|
|
216
|
+
conversationId: z.string().optional().describe("One conversation's needs. Absent is every conversation's."),
|
|
217
|
+
open: z.boolean().optional().describe("Only the ones still waiting."),
|
|
218
|
+
});
|
|
219
|
+
export const NeedsListSchema = z.object({ needs: z.array(NeedSchema).describe("Newest first.") });
|
|
220
|
+
|
|
221
|
+
// What `capabilities list` reads (GET /capabilities/connectable, a raw route): shared by the daemon's handler and the
|
|
222
|
+
// CLI's suite, so the two cannot drift apart again the way a renamed field once left the command printing nothing.
|
|
223
|
+
export const CapabilityConnectableSchema = z.object({
|
|
224
|
+
entries: z.array(
|
|
225
|
+
z.object({
|
|
226
|
+
entry: z.string().describe("The catalog entry's id, what `capabilities request` takes."),
|
|
227
|
+
name: z.string(),
|
|
228
|
+
description: z.string(),
|
|
229
|
+
connected: z.boolean().describe("Whether an instance of it is live, not merely added."),
|
|
230
|
+
}),
|
|
231
|
+
),
|
|
232
|
+
suggested: z
|
|
233
|
+
.array(
|
|
234
|
+
z.object({
|
|
235
|
+
entry: z.string(),
|
|
236
|
+
claim: z.string().describe("What the workspace seems to want, in words."),
|
|
237
|
+
evidence: z.string().describe("What was read to say so, verbatim: a file, a remote."),
|
|
238
|
+
}),
|
|
239
|
+
)
|
|
240
|
+
.default([]),
|
|
241
|
+
});
|
|
242
|
+
export type CapabilityConnectable = z.infer<typeof CapabilityConnectableSchema>;
|
|
243
|
+
|
|
244
|
+
// One open need as the conversation's card lists it: enough for the board to say what the agent waits on.
|
|
245
|
+
export const AgentNeedSchema = z.object({
|
|
246
|
+
id: z.string(),
|
|
247
|
+
kind: NeedKindSchema,
|
|
248
|
+
title: z.string(),
|
|
249
|
+
status: NeedStatusSchema,
|
|
250
|
+
});
|
|
251
|
+
export type AgentNeed = z.infer<typeof AgentNeedSchema>;
|
|
252
|
+
|
|
253
|
+
// What a person allowed one conversation beyond its persona and area, from a grant need's card
|
|
254
|
+
// (personas/conversation-grants.ts). Honoured from that conversation's next turn on, and revocable on the Grants page.
|
|
255
|
+
export const ConversationGrantSchema = z.object({
|
|
256
|
+
capabilities: z.array(z.string()).default([]).describe("Connected capabilities it may use although its persona leaves them out."),
|
|
257
|
+
folders: z.array(z.string()).default([]).describe("Workspace folders its file tools may touch beyond its fence."),
|
|
258
|
+
shelves: z.array(GrantShelfSchema).default([]).describe("Shelves of tools opened for it."),
|
|
259
|
+
updatedAt: z.number().describe("When it last changed, in milliseconds."),
|
|
260
|
+
by: z.string().optional().describe("Who last allowed something here."),
|
|
261
|
+
});
|
|
262
|
+
export type ConversationGrant = z.infer<typeof ConversationGrantSchema>;
|
|
263
|
+
|
|
264
|
+
// Every yes still standing, by conversation: what its grants widened past its persona or area, and which gated
|
|
265
|
+
// credentials a named person released to it. Reviewed and taken back from Needs you.
|
|
266
|
+
export const StandingGrantsSchema = z.object({
|
|
267
|
+
conversations: z.array(
|
|
268
|
+
z.object({
|
|
269
|
+
conversationId: z.string(),
|
|
270
|
+
capabilities: z.array(z.string()).describe("Connected capabilities allowed although its persona leaves them out."),
|
|
271
|
+
folders: z.array(z.string()).describe("Workspace folders its file tools may touch beyond its fence."),
|
|
272
|
+
shelves: z.array(GrantShelfSchema).describe("Shelves of tools opened for it."),
|
|
273
|
+
by: z.string().optional().describe("Who last allowed one of those."),
|
|
274
|
+
updatedAt: z.number().optional().describe("When one of those last changed, in milliseconds."),
|
|
275
|
+
releases: z
|
|
276
|
+
.array(z.object({ subject: z.string(), approvedBy: z.string(), at: z.number() }))
|
|
277
|
+
.describe("Gated credentials released to it, until the daemon restarts or somebody takes one back."),
|
|
278
|
+
}),
|
|
279
|
+
),
|
|
280
|
+
});
|
|
281
|
+
export type StandingGrants = z.infer<typeof StandingGrantsSchema>;
|
|
282
|
+
|
|
283
|
+
export const GrantRevokeSchema = z.object({
|
|
284
|
+
conversationId: z.string(),
|
|
285
|
+
kind: z.enum(["capability", "folder", "shelf", "release"]).describe("Which kind of yes: a grant past the persona or area, or a credential's release."),
|
|
286
|
+
what: z.string().describe("The capability id, folder, shelf or released credential it named."),
|
|
287
|
+
});
|
|
288
|
+
export type GrantRevoke = z.infer<typeof GrantRevokeSchema>;
|
|
289
|
+
|
|
290
|
+
// The file the needs store keeps: every open need and the recent closed ones, by id.
|
|
291
|
+
export const NeedsFileSchema = z.record(z.string(), NeedSchema);
|
|
292
|
+
export type NeedsFile = z.infer<typeof NeedsFileSchema>;
|
package/src/schemas/personas.ts
CHANGED
|
@@ -141,8 +141,8 @@ export const TURN_BRIEFING_NOTES: readonly TurnBriefingNote[] = [
|
|
|
141
141
|
{
|
|
142
142
|
id: "checks",
|
|
143
143
|
label: "Checks after landing",
|
|
144
|
-
when: "First message and after a compaction
|
|
145
|
-
cost: "The agent does not know
|
|
144
|
+
when: "First message and after a compaction: that nothing checks its work when it finishes or after it lands, which checks are its to run, and that CI checks what the owner pushes.",
|
|
145
|
+
cost: "The agent does not know what checks its work, so it may run the whole repository's suite on a shared machine, or chase a failure main already has.",
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
148
|
id: "dependencies",
|
|
@@ -123,6 +123,14 @@ export const ProviderRefusalSchema = z.object({
|
|
|
123
123
|
// Lets a `limit` refusal be read against the pool that model spends (`UsageWindow.gates`), not the account's
|
|
124
124
|
// fullest pool.
|
|
125
125
|
model: z.string().optional().describe("Which model the refused turn was on, where that is known."),
|
|
126
|
+
// The provider's own "try again at …", which a polled reading cannot contradict: a usage endpoint can keep showing
|
|
127
|
+
// room while the plan refuses every turn.
|
|
128
|
+
resetsAt: z
|
|
129
|
+
.number()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe(
|
|
132
|
+
"When the provider said to try again, in epoch seconds, for a spent allowance where it named one. Until then the refusal stands whatever a reading says; after it, it is over.",
|
|
133
|
+
),
|
|
126
134
|
});
|
|
127
135
|
export type ProviderRefusal = z.infer<typeof ProviderRefusalSchema>;
|
|
128
136
|
export const ProviderRefusalsSchema = z.object({
|
package/src/schemas/secrets.ts
CHANGED
|
@@ -19,6 +19,29 @@ export const SecretKeysSchema = z.object({
|
|
|
19
19
|
export const SecretKeyParamSchema = z.object({ key: secretKey.describe("Which secret, by name.") });
|
|
20
20
|
export const SecretRevealSchema = z.object({ value: z.string().describe("The value itself. The only place in this API one is ever returned.") });
|
|
21
21
|
|
|
22
|
+
// A secret nobody has to find or paste: a session key, a webhook signing secret, a database password the task sets up
|
|
23
|
+
// itself. The sandbox makes it and keeps it; the caller gets its reference and length, never the value.
|
|
24
|
+
export const SECRET_FORMATS = ["hex", "base64url", "alnum"] as const;
|
|
25
|
+
export const SecretGenerateSchema = z.object({
|
|
26
|
+
key: secretKey.describe("The name to store it under: a name nothing here holds yet, since a new value would break whatever uses the old one."),
|
|
27
|
+
bytes: z
|
|
28
|
+
.number()
|
|
29
|
+
.int()
|
|
30
|
+
.min(16)
|
|
31
|
+
.max(128)
|
|
32
|
+
.default(32)
|
|
33
|
+
.describe("How much randomness, in bytes. 32 unless whatever reads it demands a particular length."),
|
|
34
|
+
format: z
|
|
35
|
+
.enum(SECRET_FORMATS)
|
|
36
|
+
.default("hex")
|
|
37
|
+
.describe("How it is spelled: `hex` (0-9, a-f), `base64url` (letters, digits, - and _), or `alnum` (letters and digits only, for readers that refuse symbols)."),
|
|
38
|
+
});
|
|
39
|
+
export const SecretGeneratedSchema = z.object({
|
|
40
|
+
key: z.string().describe("The name it is stored under."),
|
|
41
|
+
length: z.number().int().describe("How many characters it is, which a reader's validation may care about."),
|
|
42
|
+
stored: z.enum(["env", "sandbox"]).describe("Where it was kept: desired-state/.env once DevOps is active, the sandbox's own store before that."),
|
|
43
|
+
});
|
|
44
|
+
|
|
22
45
|
// A wall against the agent's own judgment, not a compromised container: a shell here can read the policy same as the
|
|
23
46
|
// vault. Approvers are an exact list, never a role floor; the owner isn't on it unless added.
|
|
24
47
|
export const CredentialGateScopeSchema = z
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { dropAll, fold, isJsonObject, mapValue, retype } from "../documents/conversions.js";
|
|
2
|
+
import { at, drop, dropAll, fold, isJsonObject, mapValue, retype } from "../documents/conversions.js";
|
|
3
3
|
import type { KeepWarmSettings, RuleSchema } from "./settings.js";
|
|
4
4
|
|
|
5
5
|
// The conversions the settings shape has had, oldest first: what brings any earlier release's settings to
|
|
@@ -105,4 +105,6 @@ export const SETTINGS_HISTORY = [
|
|
|
105
105
|
"drops rules at the retired turn.ending moment (and the verify-ui-edits built-in), which run nothing",
|
|
106
106
|
),
|
|
107
107
|
keepWarmObject,
|
|
108
|
+
// Where the check after landing ran (2026-09-27): nothing checks work after it lands any more, CI does.
|
|
109
|
+
at("offload", drop("landCheck")),
|
|
108
110
|
] as const;
|
|
@@ -48,15 +48,16 @@ describe(`where a command lives`, () => {
|
|
|
48
48
|
expect(file([{ when: `push`, run: `pnpm verify:push` }])).toBe(false);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// `land` is retired like `turn`: it runs nothing, so a file written while it ran reads however it spelled one.
|
|
52
|
+
test(`a retired land check still reads, however many a file names and whatever paths it gives`, () => {
|
|
52
53
|
const file = (checks: unknown[]) => RepoChecksFileSchema.safeParse({ checks }).success;
|
|
53
54
|
expect(
|
|
54
55
|
file([
|
|
55
56
|
{ when: `land`, run: `pnpm verify` },
|
|
56
57
|
{ when: `land`, run: `pnpm test` },
|
|
57
58
|
]),
|
|
58
|
-
).toBe(
|
|
59
|
-
expect(file([{ when: `land`, run: `pnpm verify`, paths: [`src/**`] }])).toBe(
|
|
59
|
+
).toBe(true);
|
|
60
|
+
expect(file([{ when: `land`, run: `pnpm verify`, paths: [`src/**`] }])).toBe(true);
|
|
60
61
|
});
|
|
61
62
|
});
|
|
62
63
|
|
package/src/schemas/settings.ts
CHANGED
|
@@ -15,8 +15,8 @@ export type SystemPromptMode = z.infer<typeof SystemPromptModeSchema>;
|
|
|
15
15
|
export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"]) });
|
|
16
16
|
// Rules: "at this moment, if this is true, do this". The owner's rules decide (land, hold, version); every command a
|
|
17
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:
|
|
19
|
-
// holds anything (schemas/workspace/
|
|
18
|
+
// so no command lives in settings. Nothing verifies inside a turn or after a land: CI checks what the owner pushes, and
|
|
19
|
+
// nothing here holds anything (schemas/workspace/push-checks.ts).
|
|
20
20
|
export const RuleMomentSchema = z.enum([
|
|
21
21
|
// A command here runs on the just-written file (`{file}` is its path); the cheapest moment to catch a defect.
|
|
22
22
|
"file.edited",
|
|
@@ -436,30 +436,34 @@ export const SandboxSettingsSchema = z.object({
|
|
|
436
436
|
),
|
|
437
437
|
// Worth it since the container is recreated on every update or environment approval — otherwise approving a
|
|
438
438
|
// Dockerfile change costs the run that asked for it.
|
|
439
|
-
// What happens
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
// cold; main's CI staying red on one failure gets a fix agent once pushes go quiet, with a fleet failure re-run instead.
|
|
443
|
-
// Off, all of it is only reported.
|
|
439
|
+
// What happens when main's CI goes red (ci/main-fixer.ts): its first failed job starts one fix agent, which is sent
|
|
440
|
+
// every later failure on main until main is green, within a few turns, and a failure on the CI fleet itself is re-run
|
|
441
|
+
// once instead. Off, it is only reported.
|
|
444
442
|
autoRepair: z
|
|
445
443
|
.boolean()
|
|
446
444
|
.default(true)
|
|
447
445
|
.describe(
|
|
448
|
-
"Whether
|
|
446
|
+
"Whether main's CI going red is repaired without asking. The first job that fails on main starts one fix agent, without waiting for the rest of the run, and every later failure on main goes to that same agent until a run passes. It gets a few turns; when they are spent, or it finishes without changing anything (a failure that is not in the code), the red waits for you. A failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.",
|
|
449
447
|
),
|
|
450
448
|
// Where heavy work runs: a runner (the same image, on one of the owner's machines) instead of this sandbox. Keyed by
|
|
451
|
-
// the heavy-command rule an agent's command matched (system/resources/heavy-commands.ts),
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
// work on again.
|
|
449
|
+
// the heavy-command rule an agent's command matched (system/resources/heavy-commands.ts), each naming a runner id. A
|
|
450
|
+
// runner that is offline, outdated or full hands the work back here, which the command's output says. Never pushed
|
|
451
|
+
// to a runner itself (portability/definition.ts), which must not pass work on again.
|
|
455
452
|
offload: z
|
|
456
453
|
.object({
|
|
457
454
|
commands: z.record(z.string().min(1), z.string().min(1)).default({}),
|
|
458
|
-
landCheck: z.string().min(1).optional(),
|
|
459
455
|
})
|
|
460
456
|
.default({ commands: {} })
|
|
461
457
|
.describe(
|
|
462
|
-
"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…)
|
|
458
|
+
"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…). 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.",
|
|
459
|
+
),
|
|
460
|
+
// A person answering a need is exactly the moment they want the work to go on (docs/architecture/needs.md), unlike a
|
|
461
|
+
// restart nobody watched, so this one is on to begin with.
|
|
462
|
+
continueWhenNeedMet: z
|
|
463
|
+
.boolean()
|
|
464
|
+
.default(true)
|
|
465
|
+
.describe(
|
|
466
|
+
"Whether a conversation carries on by itself once something it asked a person for arrives: a connection made, a secret given, access allowed, a tool built into the image. Off leaves the answer on the conversation's card until someone sends a message.",
|
|
463
467
|
),
|
|
464
468
|
autoResumeOnRestart: z
|
|
465
469
|
.boolean()
|
|
@@ -665,9 +669,9 @@ export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
|
665
669
|
// spelling for the daemon that reads it, the screen that names it and the demo that mimics it.
|
|
666
670
|
export const REPO_CHECKS_FILE = `${STATE_DIR}/checks.json`;
|
|
667
671
|
|
|
668
|
-
// Named for the occasion as a repository would say it: `edit` is `file.edited
|
|
669
|
-
//
|
|
670
|
-
// and a declaration naming
|
|
672
|
+
// Named for the occasion as a repository would say it: `edit` is `file.edited` (rules/repo-checks.ts maps it). `turn`
|
|
673
|
+
// and `land` are retired: nothing runs when a turn ends or after work lands any more (CI checks what the owner pushes),
|
|
674
|
+
// and a declaration naming either still reads but runs nothing. No check refuses a land, a commit or a push.
|
|
671
675
|
// `edit` runs on one file (`{file}`) and its output rides back in the edit's own response, so a command declared there is
|
|
672
676
|
// paid per edit and has to take the file.
|
|
673
677
|
export const RepoCheckMomentSchema = z.enum(["edit", "turn", "land"]);
|
|
@@ -675,7 +679,7 @@ export type RepoCheckMoment = z.infer<typeof RepoCheckMomentSchema>;
|
|
|
675
679
|
|
|
676
680
|
export const RepoCheckSchema = z.object({
|
|
677
681
|
when: RepoCheckMomentSchema.describe(
|
|
678
|
-
"When to run it: `edit` on each file as it is written (`{file}` is its path)
|
|
682
|
+
"When to run it: `edit` on each file as it is written (`{file}` is its path). `turn` and `land` are retired and run nothing: checks no longer run while a conversation works or after its work lands, since CI checks what is pushed.",
|
|
679
683
|
),
|
|
680
684
|
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."),
|
|
681
685
|
label: z.string().min(1).max(80).optional().describe("What to call it on screen. Absent names it after the command."),
|
|
@@ -695,11 +699,7 @@ export const RepoChecksFileSchema = z.object({
|
|
|
695
699
|
checks: z
|
|
696
700
|
.array(RepoCheckSchema)
|
|
697
701
|
.max(10)
|
|
698
|
-
.default([])
|
|
699
|
-
// One land check per repository: it is THE verdict on the main tree the push hook replays, not one of several.
|
|
700
|
-
.refine((checks) => checks.filter((check) => check.when === "land").length <= 1, { message: "a repository declares at most one land check" })
|
|
701
|
-
// A land checks the whole tree an install settled, not a change, so there is nothing for a glob to narrow.
|
|
702
|
-
.refine((checks) => checks.every((check) => check.when !== "land" || check.paths === undefined), { message: "a land check takes no paths" }),
|
|
702
|
+
.default([]),
|
|
703
703
|
});
|
|
704
704
|
export type RepoChecksFile = z.infer<typeof RepoChecksFileSchema>;
|
|
705
705
|
|
|
@@ -711,7 +711,7 @@ export const RepoChecksSummarySchema = z.object({
|
|
|
711
711
|
fired: z
|
|
712
712
|
.array(z.number().nullable())
|
|
713
713
|
.describe(
|
|
714
|
-
"When each declared check last reported something, in the file's order, as epoch milliseconds; null for one that never has, or for
|
|
714
|
+
"When each declared check last reported something, in the file's order, as epoch milliseconds; null for one that never has, or for a retired one, which runs nothing.",
|
|
715
715
|
),
|
|
716
716
|
adopted: z
|
|
717
717
|
.boolean()
|
|
@@ -722,16 +722,10 @@ export const RepoChecksSummarySchema = z.object({
|
|
|
722
722
|
"Whether the declaration changed since it was adopted, which holds it until the owner looks again. True only for a repository that was adopted before.",
|
|
723
723
|
),
|
|
724
724
|
error: z.string().optional().describe("Why the file could not be read, when it exists but does not parse. The checks list is empty in that case."),
|
|
725
|
-
landDefault: z
|
|
726
|
-
.string()
|
|
727
|
-
.optional()
|
|
728
|
-
.describe(
|
|
729
|
-
"What runs on the main tree after a land when the file declares no `land` check: the package's own verify or test script. Absent when the repository has neither.",
|
|
730
|
-
),
|
|
731
725
|
});
|
|
732
726
|
export type RepoChecksSummary = z.infer<typeof RepoChecksSummarySchema>;
|
|
733
727
|
export const RepoChecksListSchema = z.object({
|
|
734
|
-
repos: z.array(RepoChecksSummarySchema).describe("Every repository that declares checks
|
|
728
|
+
repos: z.array(RepoChecksSummarySchema).describe("Every repository that declares checks, in id order."),
|
|
735
729
|
});
|
|
736
730
|
export type RepoChecksList = z.infer<typeof RepoChecksListSchema>;
|
|
737
731
|
export const RepoChecksAdoptSchema = z.object({
|