@intentic/sandbox-contract 1.316.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/dist/contracts/agent.contract.d.ts +561 -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/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/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 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +160 -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/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/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 +38 -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 +1826 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol/raw-routes.d.ts +0 -5
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +0 -1
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/schemas/agents.d.ts +58 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +9 -0
- 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/devices.d.ts +68 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +7 -0
- 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/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.d.ts +2 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +4 -0
- 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/state/definition.d.ts +16 -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 +3 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +4 -0
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +1 -0
- 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/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/events/agent-events.ts +4 -0
- package/src/events/agent-words.ts +2 -1
- 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/index.ts +6 -0
- package/src/protocol/raw-routes.ts +1 -2
- package/src/schemas/agents.ts +14 -1
- package/src/schemas/capabilities.ts +28 -0
- package/src/schemas/devices.ts +17 -0
- package/src/schemas/hosts.ts +4 -1
- package/src/schemas/needs.ts +292 -0
- package/src/schemas/secrets.ts +23 -0
- package/src/schemas/settings.ts +8 -0
- package/src/schemas/state-plan.ts +8 -1
- package/src/schemas/system.ts +15 -0
- package/src/schemas/updates.ts +56 -0
- package/src/state/history-state.ts +5 -0
- package/src/state/workspace-state.ts +4 -0
- package/src/text/transcript-fold.ts +4 -0
|
@@ -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/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
|
package/src/schemas/settings.ts
CHANGED
|
@@ -457,6 +457,14 @@ export const SandboxSettingsSchema = z.object({
|
|
|
457
457
|
.describe(
|
|
458
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
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.",
|
|
467
|
+
),
|
|
460
468
|
autoResumeOnRestart: z
|
|
461
469
|
.boolean()
|
|
462
470
|
.default(false)
|
|
@@ -40,8 +40,15 @@ export type StatePlan = z.infer<typeof StatePlanSchema>;
|
|
|
40
40
|
|
|
41
41
|
// The `state` of `/health`: whether an update's state changes are still uncommitted. The host rolls an update back when
|
|
42
42
|
// the journal stays open, so this rides the same probe as the boot's progress.
|
|
43
|
+
// `failed`: a conversion or structural step threw partway through this boot; the daemon put back every file the episode
|
|
44
|
+
// had touched and keeps running on read-time conversions, and a host rolls the update back as it would an open journal
|
|
45
|
+
// that never commits (this version could not bring the files to its shape, the one before it can read them as they are).
|
|
43
46
|
export const StateStatusSchema = z.object({
|
|
44
|
-
journal: z
|
|
47
|
+
journal: z
|
|
48
|
+
.enum(["open", "none", "failed"])
|
|
49
|
+
.describe(
|
|
50
|
+
"Open from the start of a boot that changed stored files until that boot has converged. Failed when a conversion threw partway: the files were put back as they were, and a host takes that as the update not having taken.",
|
|
51
|
+
),
|
|
45
52
|
engine: z.number().describe("The running build's conversion count."),
|
|
46
53
|
});
|
|
47
54
|
export type StateStatus = z.infer<typeof StateStatusSchema>;
|
package/src/schemas/system.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { StatePlanSchema } from "./state-plan.js";
|
|
3
|
+
import { UpdateOutcomeSchema, WithdrawnReleaseSchema } from "./updates.js";
|
|
3
4
|
// version is this build's baked-in version; latest/updateAvailable come from comparing it to the published stable
|
|
4
5
|
// release.
|
|
5
6
|
// Whether a runtime can serve a turn, probed off the turn path. "unknown" reads as available-but-unverified, never as
|
|
@@ -82,6 +83,20 @@ export const InfoSchema = z.object({
|
|
|
82
83
|
staged: StagedUpdateSchema.optional().describe(
|
|
83
84
|
"An update already downloaded and built on the machine running this container, waiting only for the restart that applies it. That restart is seconds, where an unprepared update is minutes, which is a different decision entirely. Absent when nothing is waiting.",
|
|
84
85
|
),
|
|
86
|
+
// What the host last did about this sandbox's version (update-outcome.json on /history, written by ic); absent
|
|
87
|
+
// when no host has said, which is every hosted sandbox and every host older than the file.
|
|
88
|
+
lastUpdate: UpdateOutcomeSchema.optional().describe(
|
|
89
|
+
"What the machine running this sandbox last did about its version: an update that took, one it gave up on and why, and until when the previous version stays ready. Absent when that machine has never said.",
|
|
90
|
+
),
|
|
91
|
+
// The running version was taken back after it shipped; absent for every version still standing.
|
|
92
|
+
withdrawn: WithdrawnReleaseSchema.optional().describe(
|
|
93
|
+
"Set when the version this sandbox runs was withdrawn after it shipped, which is the moment to go back to the one before it. Absent for every version still standing.",
|
|
94
|
+
),
|
|
95
|
+
// The owner's "not this one" (POST /system/update/skip); updateAvailable reads false while `latest` is it.
|
|
96
|
+
skippedVersion: z
|
|
97
|
+
.string()
|
|
98
|
+
.optional()
|
|
99
|
+
.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."),
|
|
85
100
|
});
|
|
86
101
|
export type Info = z.infer<typeof InfoSchema>;
|
|
87
102
|
// What the daemon could not read in its own `.intentic/` state files, and in the files on its volume that
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// WHAT THE HOST LAST DID ABOUT THIS SANDBOX'S VERSION, in the host's own words. Written by `ic` (the swap, its health
|
|
4
|
+
// check and the probation that follows it: _sandbox/ic/src/sandbox/outcome.rs) onto /history as `update-outcome.json`,
|
|
5
|
+
// the same volume whichever container runs, so the version that ends up running reads what happened to the other one.
|
|
6
|
+
// Advisory: the swap and its undoing are the host's; this is only how the sandbox and its owner hear about them.
|
|
7
|
+
export const UpdateOutcomeResultSchema = z.enum(["updated", "kept", "restored", "rolled-back"]);
|
|
8
|
+
export type UpdateOutcomeResult = z.infer<typeof UpdateOutcomeResultSchema>;
|
|
9
|
+
|
|
10
|
+
export const UpdateOutcomeSchema = z.object({
|
|
11
|
+
result: UpdateOutcomeResultSchema.describe(
|
|
12
|
+
"What happened. Updated: the new version passed its first health check and runs, with the previous one kept ready until keepUntil. Kept: that probation ended and the new version stays. Restored: the new version never came up, so the previous container was put back at once. Rolled back: the new version came up and then failed its probation (it kept crashing, never became ready, or lost its tunnel), so the host went back to the previous one by itself.",
|
|
13
|
+
),
|
|
14
|
+
// What the person or program asked for. A string rather than an enum: a newer ic's verb must not make an older
|
|
15
|
+
// daemon drop the whole outcome.
|
|
16
|
+
verb: z.string().optional().describe("What was asked for: update, rollback, rebuild, dev, reshape, or the probation watch acting on its own."),
|
|
17
|
+
at: z.number().describe("When it happened, in milliseconds."),
|
|
18
|
+
from: z.string().optional().describe("The version (or, when it would not say, the image) that ran before."),
|
|
19
|
+
to: z.string().optional().describe("The version (or image) that was moved onto, or that was tried and given up on."),
|
|
20
|
+
// Plain words for the owner: why a version was given up on. Absent for an update that simply worked.
|
|
21
|
+
reason: z.string().optional().describe("Why the host gave up on the new version, in plain words. Absent when nothing went wrong."),
|
|
22
|
+
// A path on the machine that runs the sandbox, which only means something to a person on that machine.
|
|
23
|
+
log: z.string().optional().describe("Where the host kept the full log of the swap, as a path on the machine that runs the sandbox."),
|
|
24
|
+
keepUntil: z
|
|
25
|
+
.number()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(
|
|
28
|
+
"Until when the previous version stays parked and ready, in milliseconds. While it does, going back takes seconds and nothing is downloaded or rebuilt; after it, going back uses the pinned image.",
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
export type UpdateOutcome = z.infer<typeof UpdateOutcomeSchema>;
|
|
32
|
+
|
|
33
|
+
// A published release that was taken back after it shipped (the release pipeline's rollback-stable.sh marks it on its
|
|
34
|
+
// GitHub release). A sandbox running one is told, and offered the way back.
|
|
35
|
+
export const WithdrawnReleaseSchema = z.object({
|
|
36
|
+
version: z.string().describe("The withdrawn version, which is the one this sandbox is running."),
|
|
37
|
+
reason: z.string().optional().describe("Why it was withdrawn, as the people who withdrew it put it."),
|
|
38
|
+
});
|
|
39
|
+
export type WithdrawnRelease = z.infer<typeof WithdrawnReleaseSchema>;
|
|
40
|
+
|
|
41
|
+
// The owner's "not this one": no update is offered while the newest release is the skipped one. `null` clears it.
|
|
42
|
+
export const SkipUpdateInputSchema = z.object({
|
|
43
|
+
version: z
|
|
44
|
+
.string()
|
|
45
|
+
.min(1)
|
|
46
|
+
.nullable()
|
|
47
|
+
.describe("The release to stop offering, or null to offer the newest release again. A newer release than the skipped one is always offered."),
|
|
48
|
+
});
|
|
49
|
+
export type SkipUpdateInput = z.infer<typeof SkipUpdateInputSchema>;
|
|
50
|
+
|
|
51
|
+
// One image a sandbox can go back to, as `ic sandbox versions` knows it: the pinned local image and what it says it is.
|
|
52
|
+
export const RollbackTargetSchema = z.object({
|
|
53
|
+
image: z.string().describe("The local image a rollback would run, pinned under a tag no other flow writes."),
|
|
54
|
+
version: z.string().optional().describe("What that image says it is. Absent when it would not say."),
|
|
55
|
+
});
|
|
56
|
+
export type RollbackTarget = z.infer<typeof RollbackTargetSchema>;
|
|
@@ -78,6 +78,11 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
78
78
|
{ path: "engines/", portability: "derived" },
|
|
79
79
|
// An update the host downloaded and built for this sandbox (`ic sandbox prepare`); about this machine only.
|
|
80
80
|
{ path: "update-staged.json", portability: "derived" },
|
|
81
|
+
// What the host last did about this sandbox's version (ic writes it), the owner's skipped release, and why the
|
|
82
|
+
// last boot could not start (the daemon writes it, the host's probation reads it): all rebuilt by whoever next says.
|
|
83
|
+
{ path: "update-outcome.json", portability: "derived" },
|
|
84
|
+
{ path: "update-skipped.json", portability: "derived" },
|
|
85
|
+
{ path: "boot-failure.json", portability: "derived" },
|
|
81
86
|
|
|
82
87
|
/* ---- credentials ---- */
|
|
83
88
|
|
|
@@ -132,6 +132,10 @@ const STATE_FILES = [
|
|
|
132
132
|
// needing, not what one machine did. The drift snapshot self-expires on a move since its `bornAt` won't match the
|
|
133
133
|
// new container.
|
|
134
134
|
{ path: ".intentic/records/runtime-installs.json", invalidates: ["environment"], portability: "carry" },
|
|
135
|
+
// What agents are waiting on people for (docs/architecture/needs.md): every open need and the recent closed ones.
|
|
136
|
+
// Holds no credential (a secret need names the secret, its value goes to the secret store), so it may `carry`: a
|
|
137
|
+
// question a moved sandbox still has to ask. `needs` is the chat cards', the board's and the inbox's shared read.
|
|
138
|
+
{ path: ".intentic/records/needs.json", invalidates: ["needs"], portability: "carry" },
|
|
135
139
|
// Where each agent engine's version comes from: blessed list, upstream newest, a pin, or the image
|
|
136
140
|
// (schemas/engines.ts). The versions themselves are machine state on the daemon's volume (architecture-specific
|
|
137
141
|
// binaries); the policy here travels. `versioned`: a human decision worth reviewing. Own key, not `environment`'s:
|
|
@@ -16,6 +16,7 @@ import { isLandConflict } from "../events/land-conflict.js";
|
|
|
16
16
|
import { turnedAwayCode } from "../policy/turned-away.js";
|
|
17
17
|
import { mentionedPathTokens } from "./mentions.js";
|
|
18
18
|
import { unspokenPromptRow } from "../events/agent-words.js";
|
|
19
|
+
import { needRowText } from "../events/need-wake.js";
|
|
19
20
|
|
|
20
21
|
// Folds a turn's frames into rows once, live and for the settled record alike, so a reopened chat matches what was on
|
|
21
22
|
// screen. `tag` selects the stream read: undefined is the main turn, a tool-call id is the subagent it spawned; other
|
|
@@ -408,6 +409,9 @@ export class TranscriptFold {
|
|
|
408
409
|
return this.park(event.requestId, { paymentOffer: { requestId: event.requestId, offer: event.offer, status: "pending" } });
|
|
409
410
|
case "credential_offer":
|
|
410
411
|
return this.park(event.requestId, { credentialOffer: { requestId: event.requestId, offer: event.offer, status: "pending" } });
|
|
412
|
+
case "need":
|
|
413
|
+
// A row of its own, not a park: the turn goes on, and the card reads the need's live state by its id.
|
|
414
|
+
return this.pushRow({ role: "notice", text: needRowText(event.need), need: event.need });
|
|
411
415
|
case "resolved":
|
|
412
416
|
// Releases the card; the answering window already froze it locally, so this is a no-op there.
|
|
413
417
|
return this.patchParked(event.requestId, (row) => Object.assign(row, settledRequests(row, event.reply)));
|