@paradow/ledge 0.28.0 → 0.29.0-snapshot.41297455946.1
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 +8 -0
- package/dist/cli/{action-NQRSZEC5.js → action-MCR4BCSE.js} +5 -5
- package/dist/cli/ask-L3PSQZ2K.js +17 -0
- package/dist/cli/{attach-KXOSFUHO.js → attach-R7FT7LFH.js} +9 -14
- package/dist/cli/brain-BJTBN2ZK.js +20 -0
- package/dist/cli/{bridge-XCTXVWKH.js → bridge-YBHXFEFX.js} +2 -2
- package/dist/cli/{chunk-VKRFHDRK.js → chunk-2X56ZRK7.js} +1122 -1164
- package/dist/cli/{chunk-3NQNWGDX.js → chunk-6EW2FD2U.js} +3 -5
- package/dist/cli/chunk-DJMFCYF4.js +60 -0
- package/dist/cli/chunk-DLKJOK3B.js +6 -0
- package/dist/cli/{chunk-VMGACWNP.js → chunk-FWLT3SAN.js} +1 -1
- package/dist/cli/{chunk-6IYPRUQG.js → chunk-G3ST5WKR.js} +1 -1
- package/dist/cli/{chunk-ZI47U5CQ.js → chunk-GY35ZR5I.js} +102 -72
- package/dist/cli/{chunk-NLL2EQJ2.js → chunk-KBSXP63H.js} +22 -7
- package/dist/cli/chunk-LOXLGCGN.js +57 -0
- package/dist/cli/{chunk-LG3XFIPV.js → chunk-O4MUYFPH.js} +6 -7
- package/dist/cli/{chunk-JLUHY5AB.js → chunk-OR2NHT5O.js} +4 -4
- package/dist/cli/chunk-S6BZV7AY.js +57 -0
- package/dist/cli/{chunk-CR3LGTZQ.js → chunk-TCQ5PYIF.js} +3 -3
- package/dist/cli/{chunk-RYZ6FHYT.js → chunk-UMQGRTA4.js} +17 -39
- package/dist/cli/{chunk-3CF4FQWG.js → chunk-VQR5ME75.js} +4 -3
- package/dist/cli/{chunk-HOOHS3MX.js → chunk-W724IVDC.js} +1 -1
- package/dist/cli/{chunk-K7UEKSB2.js → chunk-WYRQZA4G.js} +2 -2
- package/dist/cli/chunk-XTDE2URL.js +365 -0
- package/dist/cli/daemon-ICC6LBCO.js +16562 -0
- package/dist/cli/{decide-OSSTI3FE.js → decide-JSY46QZC.js} +14 -16
- package/dist/cli/{handover-2G7OEWIG.js → handover-5RUSRVAD.js} +5 -5
- package/dist/cli/{hook-XY4E5H7O.js → hook-QQP3ENGA.js} +87 -81
- package/dist/cli/index.js +40 -28
- package/dist/cli/launch-RTBGUJSC.js +28 -0
- package/dist/cli/{send-RUHGKLMA.js → send-BIIH6SQW.js} +5 -5
- package/dist/cli/{setup-K42IVMHS.js → setup-OLVRJLJE.js} +4 -4
- package/dist/cli/{status-4Z2VJFID.js → status-Z3I33KZJ.js} +3 -3
- package/dist/cli/{tui-4IRYQXYU.js → tui-AIXCETOB.js} +6036 -3629
- package/dist/cli/{uninstall-4IMMR55T.js → uninstall-X65CWHIJ.js} +4 -4
- package/dist/cli/{version-L2Y7MRH6.js → version-QMHFVP3N.js} +1 -1
- package/dist/cli/who-C2VMEM35.js +12 -0
- package/package.json +21 -6
- package/dist/cli/chunk-CIQAFCX3.js +0 -118
- package/dist/cli/chunk-JHFKBKSO.js +0 -159
- package/dist/cli/chunk-OYDTU6AP.js +0 -9482
- package/dist/cli/chunk-QSRB3F5Y.js +0 -198
- package/dist/cli/chunk-UME2F7VM.js +0 -6
- package/dist/cli/daemon-ZUHJKLSL.js +0 -2322
- package/dist/cli/launch-7WOARLZM.js +0 -35
- package/dist/cli/task-2GESYTOL.js +0 -19
- package/dist/cli/team-MTSKNMAE.js +0 -205
- package/dist/cli/who-PSKHASMK.js +0 -12
|
@@ -1,42 +1,208 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paths
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-W724IVDC.js";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// src/auth.ts
|
|
6
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
7
|
+
import { chmod, link, mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
8
|
+
import { readFileSync } from "fs";
|
|
9
|
+
import { dirname } from "path";
|
|
10
|
+
var TOKEN_RE = /^[0-9a-f]{64}$/;
|
|
11
|
+
var DAEMON_PROTOCOL_PREFIX = "ledge.";
|
|
12
|
+
function isDaemonToken(value) {
|
|
13
|
+
return TOKEN_RE.test(value);
|
|
14
|
+
}
|
|
15
|
+
function tokenEquals(presented, expected) {
|
|
16
|
+
const a = createHash("sha256").update(presented.slice(0, 256), "utf8").digest();
|
|
17
|
+
const b = createHash("sha256").update(expected, "utf8").digest();
|
|
18
|
+
return timingSafeEqual(a, b);
|
|
19
|
+
}
|
|
20
|
+
function hostAllowed(hostHeader) {
|
|
21
|
+
const raw = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader;
|
|
22
|
+
if (!raw || raw.length > 255) return false;
|
|
23
|
+
const host = raw.trim().toLowerCase();
|
|
24
|
+
if (!host || host.includes(" ") || host.endsWith(".")) return false;
|
|
25
|
+
let name;
|
|
26
|
+
if (host.startsWith("[")) {
|
|
27
|
+
const end = host.indexOf("]");
|
|
28
|
+
if (end < 0) return false;
|
|
29
|
+
name = host.slice(1, end);
|
|
30
|
+
const rest = host.slice(end + 1);
|
|
31
|
+
if (rest !== "" && !/^:\d{1,5}$/.test(rest)) return false;
|
|
32
|
+
} else {
|
|
33
|
+
const colon = host.lastIndexOf(":");
|
|
34
|
+
if (colon >= 0) {
|
|
35
|
+
if (!/^\d{1,5}$/.test(host.slice(colon + 1))) return false;
|
|
36
|
+
name = host.slice(0, colon);
|
|
37
|
+
} else name = host;
|
|
38
|
+
}
|
|
39
|
+
return name === "localhost" || name === "127.0.0.1" || name === "::1";
|
|
40
|
+
}
|
|
41
|
+
function headerValue(value, max) {
|
|
42
|
+
const raw = Array.isArray(value) ? value.join(",") : value;
|
|
43
|
+
return !raw || raw.length > max ? "" : raw;
|
|
44
|
+
}
|
|
45
|
+
function bearerToken(authorization) {
|
|
46
|
+
if (!authorization || authorization.length > 512) return "";
|
|
47
|
+
return /^Bearer[ \t]+(\S+)$/i.exec(authorization)?.[1] ?? "";
|
|
48
|
+
}
|
|
49
|
+
function protocolToken(header) {
|
|
50
|
+
const raw = headerValue(header, 512);
|
|
51
|
+
if (!raw) return "";
|
|
52
|
+
for (const part of raw.split(",").slice(0, 8)) {
|
|
53
|
+
const protocol = part.trim();
|
|
54
|
+
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX)) return protocol.slice(DAEMON_PROTOCOL_PREFIX.length);
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
function requestAuthorized(headers, expected) {
|
|
59
|
+
const bearerOk = tokenEquals(bearerToken(typeof headers.authorization === "string" ? headers.authorization : void 0), expected);
|
|
60
|
+
const presented = protocolToken(headers["sec-websocket-protocol"]);
|
|
61
|
+
return bearerOk || presented !== "" && tokenEquals(presented, expected);
|
|
62
|
+
}
|
|
63
|
+
function selectDaemonProtocol(protocols, expected) {
|
|
64
|
+
for (const protocol of protocols) {
|
|
65
|
+
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX) && tokenEquals(protocol.slice(DAEMON_PROTOCOL_PREFIX.length), expected)) return protocol;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
async function readTokenFile(file) {
|
|
70
|
+
try {
|
|
71
|
+
const raw = (await readFile(file, "utf8")).trim();
|
|
72
|
+
return isDaemonToken(raw) ? raw : void 0;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
if (e.code === "ENOENT") return void 0;
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function lockTokenFile(file) {
|
|
79
|
+
await chmod(file, 384);
|
|
80
|
+
}
|
|
81
|
+
async function ensureDaemonToken() {
|
|
82
|
+
const file = paths().token;
|
|
83
|
+
await mkdir(dirname(file), { recursive: true });
|
|
84
|
+
const current = await readTokenFile(file);
|
|
85
|
+
if (current) {
|
|
86
|
+
await lockTokenFile(file);
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
const token = randomBytes(32).toString("hex");
|
|
90
|
+
const tmp = `${file}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
91
|
+
await writeFile(tmp, `${token}
|
|
92
|
+
`, { mode: 384, flag: "wx" });
|
|
93
|
+
await chmod(tmp, 384);
|
|
94
|
+
try {
|
|
95
|
+
await link(tmp, file);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
await unlink(tmp).catch(() => {
|
|
98
|
+
});
|
|
99
|
+
if (e.code !== "EEXIST") throw e;
|
|
100
|
+
const raced = await readTokenFile(file);
|
|
101
|
+
if (raced) {
|
|
102
|
+
await lockTokenFile(file);
|
|
103
|
+
return raced;
|
|
104
|
+
}
|
|
105
|
+
await writeFile(file, `${token}
|
|
106
|
+
`, { mode: 384 });
|
|
107
|
+
}
|
|
108
|
+
await unlink(tmp).catch(() => {
|
|
109
|
+
});
|
|
110
|
+
await lockTokenFile(file);
|
|
111
|
+
const stored = await readTokenFile(file);
|
|
112
|
+
if (!stored) throw new Error("daemon token was not stored");
|
|
113
|
+
return stored;
|
|
114
|
+
}
|
|
115
|
+
var SESSION_KEY_VAR = "LEDGE_SESSION_KEY";
|
|
116
|
+
function sessionKey(id2, token = readDaemonToken()) {
|
|
117
|
+
return token ? createHmac("sha256", token).update(`ledge-session:${id2}`).digest("hex") : void 0;
|
|
118
|
+
}
|
|
119
|
+
function readDaemonToken() {
|
|
120
|
+
try {
|
|
121
|
+
const raw = readFileSync(paths().token, "utf8").trim();
|
|
122
|
+
return isDaemonToken(raw) ? raw : void 0;
|
|
123
|
+
} catch {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ../protocol/src/asks.ts
|
|
6
129
|
import { z } from "zod";
|
|
7
|
-
var
|
|
130
|
+
var ASK_QUESTION_MAX = 2e3;
|
|
131
|
+
var ASK_OPTIONS_MAX = 4e3;
|
|
132
|
+
var ASK_ANSWER_MAX = 4e3;
|
|
133
|
+
var AskIdSchema = z.string().regex(/^a[0-9a-f]{6}$/);
|
|
134
|
+
var AskSchema = z.object({
|
|
135
|
+
id: AskIdSchema,
|
|
136
|
+
from: z.string().min(1),
|
|
137
|
+
fromName: z.string(),
|
|
138
|
+
question: z.string().min(1).max(ASK_QUESTION_MAX),
|
|
139
|
+
options: z.string().max(ASK_OPTIONS_MAX),
|
|
140
|
+
at: z.string().datetime(),
|
|
141
|
+
state: z.enum(["open", "answered", "dropped"]),
|
|
142
|
+
answer: z.object({ text: z.string().min(1).max(ASK_ANSWER_MAX), at: z.string().datetime() }).optional(),
|
|
143
|
+
closedAt: z.string().datetime().optional()
|
|
144
|
+
});
|
|
145
|
+
var AskRefusalSchema = z.enum(["unknown", "closed", "dropped", "not_session", "invalid"]);
|
|
146
|
+
var AskCreateSchema = z.object({
|
|
147
|
+
type: z.literal("ask.create"),
|
|
148
|
+
rid: z.string().min(1).max(100),
|
|
149
|
+
by: z.string().min(1),
|
|
150
|
+
question: z.string().trim().min(1).max(ASK_QUESTION_MAX),
|
|
151
|
+
options: AskSchema.shape.options
|
|
152
|
+
});
|
|
153
|
+
var AskAnswerSchema = z.object({
|
|
154
|
+
type: z.literal("ask.answer"),
|
|
155
|
+
rid: z.string().min(1).max(100),
|
|
156
|
+
id: AskIdSchema,
|
|
157
|
+
text: z.string().trim().min(1).max(ASK_ANSWER_MAX),
|
|
158
|
+
machineId: z.string().optional()
|
|
159
|
+
});
|
|
160
|
+
var AsksMessageSchema = z.object({ type: z.literal("asks"), asks: z.array(AskSchema) });
|
|
161
|
+
var AskUpdatedSchema = z.object({ type: z.literal("ask.updated"), ask: AskSchema });
|
|
162
|
+
var AskResultSchema = z.object({
|
|
163
|
+
type: z.literal("ask.result"),
|
|
164
|
+
rid: z.string(),
|
|
165
|
+
ask: AskSchema.optional(),
|
|
166
|
+
code: AskRefusalSchema.optional(),
|
|
167
|
+
reason: z.string().optional()
|
|
168
|
+
});
|
|
169
|
+
var askShown = (a) => a.state === "open";
|
|
170
|
+
|
|
171
|
+
// ../protocol/src/handover.ts
|
|
172
|
+
import { z as z2 } from "zod";
|
|
173
|
+
var ContextSchema = z2.object({ used: z2.number().int().min(0), window: z2.number().int().positive(), at: z2.string().datetime() });
|
|
8
174
|
var CONTEXT_NEAR = 0.8;
|
|
9
175
|
var CONTEXT_NEAR_MIN = 0.5;
|
|
10
176
|
var CONTEXT_NEAR_MAX = 0.95;
|
|
11
|
-
var ContextPrefsSchema =
|
|
177
|
+
var ContextPrefsSchema = z2.object({ near: z2.number().min(CONTEXT_NEAR_MIN).max(CONTEXT_NEAR_MAX) });
|
|
12
178
|
var contextShare = (c) => c ? Math.min(1, c.used / c.window) : void 0;
|
|
13
179
|
var contextNear = (c, near = CONTEXT_NEAR) => (contextShare(c) ?? 0) >= near;
|
|
14
|
-
var HandoverStateSchema =
|
|
15
|
-
var HandoverSchema =
|
|
180
|
+
var HandoverStateSchema = z2.enum(["briefing", "launching", "done", "failed"]);
|
|
181
|
+
var HandoverSchema = z2.object({
|
|
16
182
|
state: HandoverStateSchema,
|
|
17
|
-
at:
|
|
183
|
+
at: z2.string().datetime(),
|
|
18
184
|
/** The relief, once launched. */
|
|
19
|
-
to:
|
|
20
|
-
toName:
|
|
21
|
-
reason:
|
|
185
|
+
to: z2.string().optional(),
|
|
186
|
+
toName: z2.string().optional(),
|
|
187
|
+
reason: z2.string().optional(),
|
|
22
188
|
/** Started by Ledge itself (a seat working without asking), not by the user. */
|
|
23
|
-
auto:
|
|
189
|
+
auto: z2.literal(true).optional()
|
|
24
190
|
});
|
|
25
|
-
var RelievesSchema =
|
|
191
|
+
var RelievesSchema = z2.object({ id: z2.string(), name: z2.string(), at: z2.string().datetime() });
|
|
26
192
|
var BRIEF_MAX = 2e4;
|
|
27
|
-
var ContextSetSchema =
|
|
28
|
-
var ContextPrefsMessageSchema =
|
|
29
|
-
var SessionHandoverSchema =
|
|
30
|
-
var HandoverBriefSchema =
|
|
31
|
-
var HandoverShowSchema =
|
|
32
|
-
var SessionHandoverResultSchema =
|
|
33
|
-
var HandoverResultSchema =
|
|
193
|
+
var ContextSetSchema = z2.object({ type: z2.literal("context.set"), prefs: ContextPrefsSchema });
|
|
194
|
+
var ContextPrefsMessageSchema = z2.object({ type: z2.literal("context.prefs"), prefs: ContextPrefsSchema });
|
|
195
|
+
var SessionHandoverSchema = z2.object({ type: z2.literal("session.handover"), rid: z2.string().min(1).max(100), id: z2.string().min(1) });
|
|
196
|
+
var HandoverBriefSchema = z2.object({ type: z2.literal("handover.brief"), rid: z2.string().min(1).max(100), by: z2.string().min(1), text: z2.string().min(1).max(BRIEF_MAX) });
|
|
197
|
+
var HandoverShowSchema = z2.object({ type: z2.literal("handover.show"), rid: z2.string().min(1).max(100), by: z2.string().min(1) });
|
|
198
|
+
var SessionHandoverResultSchema = z2.object({ type: z2.literal("session.handover.result"), rid: z2.string(), ok: z2.boolean(), reason: z2.string().optional() });
|
|
199
|
+
var HandoverResultSchema = z2.object({ type: z2.literal("handover.result"), rid: z2.string(), text: z2.string().optional(), reason: z2.string().optional() });
|
|
34
200
|
|
|
35
201
|
// ../protocol/src/session.ts
|
|
36
|
-
import { z as
|
|
37
|
-
var ToolSchema =
|
|
38
|
-
var OmpApprovalSchema =
|
|
39
|
-
var AutonomySchema =
|
|
202
|
+
import { z as z3 } from "zod";
|
|
203
|
+
var ToolSchema = z3.enum(["claude", "codex", "cursor", "omp"]);
|
|
204
|
+
var OmpApprovalSchema = z3.enum(["always-ask", "write", "yolo"]);
|
|
205
|
+
var AutonomySchema = z3.enum(["ask", "edits", "auto"]);
|
|
40
206
|
function autonomyArgs(tool, autonomy) {
|
|
41
207
|
if (!autonomy || autonomy === "ask") return [];
|
|
42
208
|
switch (tool) {
|
|
@@ -53,97 +219,95 @@ function autonomyArgs(tool, autonomy) {
|
|
|
53
219
|
function ompApprovalFor(autonomy) {
|
|
54
220
|
return autonomy === "auto" ? "yolo" : autonomy ? "always-ask" : void 0;
|
|
55
221
|
}
|
|
56
|
-
var BackendSchema =
|
|
57
|
-
var SessionStateSchema =
|
|
58
|
-
var AskQuestionSchema =
|
|
59
|
-
id:
|
|
60
|
-
question:
|
|
61
|
-
header:
|
|
62
|
-
multi:
|
|
63
|
-
options:
|
|
64
|
-
});
|
|
65
|
-
var PendingSchema =
|
|
66
|
-
kind:
|
|
67
|
-
text:
|
|
222
|
+
var BackendSchema = z3.enum(["tmux", "omp-rpc"]);
|
|
223
|
+
var SessionStateSchema = z3.enum(["starting", "working", "waiting_input", "waiting_permission", "idle", "exited", "error"]);
|
|
224
|
+
var AskQuestionSchema = z3.object({
|
|
225
|
+
id: z3.string(),
|
|
226
|
+
question: z3.string(),
|
|
227
|
+
header: z3.string().optional(),
|
|
228
|
+
multi: z3.boolean().optional(),
|
|
229
|
+
options: z3.array(z3.object({ label: z3.string(), description: z3.string().optional() }))
|
|
230
|
+
});
|
|
231
|
+
var PendingSchema = z3.object({
|
|
232
|
+
kind: z3.enum(["question", "permission", "plan", "diff"]),
|
|
233
|
+
text: z3.string(),
|
|
68
234
|
/** What to review in full before deciding, when `text` is only its summary: a plan's markdown. */
|
|
69
|
-
body:
|
|
70
|
-
tool:
|
|
71
|
-
options:
|
|
235
|
+
body: z3.string().optional(),
|
|
236
|
+
tool: z3.string().optional(),
|
|
237
|
+
options: z3.array(z3.string()).optional(),
|
|
72
238
|
/** Each option's description, by index ('' when it has none); left out when no option has one. */
|
|
73
|
-
optionDetails:
|
|
239
|
+
optionDetails: z3.array(z3.string()).optional(),
|
|
74
240
|
/** `rpc`: the harness's own protocol asked (OMP): answered on its request id, never by typing. */
|
|
75
|
-
source:
|
|
241
|
+
source: z3.enum(["hook", "pane", "rpc"]),
|
|
76
242
|
/** OMP: every question of one `ask` call, answered together with `session.answer`; `requestId` is the dialog it answers. */
|
|
77
|
-
questions:
|
|
78
|
-
requestId:
|
|
243
|
+
questions: z3.array(AskQuestionSchema).optional(),
|
|
244
|
+
requestId: z3.string().optional(),
|
|
79
245
|
/** Set when the request comes from a helper (subagent): it is routed through its parent. */
|
|
80
|
-
helperId:
|
|
246
|
+
helperId: z3.string().optional(),
|
|
81
247
|
/** For a helper-routed request: the state the parent returns to once it is answered (main thread may stop meanwhile). */
|
|
82
248
|
parentState: SessionStateSchema.optional()
|
|
83
249
|
});
|
|
84
|
-
var ReplySchema =
|
|
85
|
-
text:
|
|
86
|
-
at:
|
|
250
|
+
var ReplySchema = z3.object({
|
|
251
|
+
text: z3.string(),
|
|
252
|
+
at: z3.string().datetime(),
|
|
87
253
|
/** The reply is a plan the agent proposes (Codex Plan mode): nothing in it is done yet. */
|
|
88
|
-
plan:
|
|
254
|
+
plan: z3.boolean().optional()
|
|
89
255
|
});
|
|
90
|
-
var HelperSchema =
|
|
91
|
-
id:
|
|
92
|
-
kind:
|
|
93
|
-
task:
|
|
256
|
+
var HelperSchema = z3.object({
|
|
257
|
+
id: z3.string(),
|
|
258
|
+
kind: z3.string(),
|
|
259
|
+
task: z3.string(),
|
|
94
260
|
state: SessionStateSchema,
|
|
95
261
|
/** When this helper finished; retained across daemon restarts until its five-minute expiry. */
|
|
96
|
-
finishedAt:
|
|
262
|
+
finishedAt: z3.string().datetime().optional()
|
|
97
263
|
});
|
|
98
|
-
var TopicSourceSchema =
|
|
99
|
-
var
|
|
264
|
+
var TopicSourceSchema = z3.enum(["user", "pane", "prompt"]);
|
|
265
|
+
var SessionRigSchema = z3.object({ machine: z3.string(), rigId: z3.string(), rigName: z3.string(), seat: z3.string(), pod: z3.string(), member: z3.string() });
|
|
266
|
+
var SessionSchema = z3.object({
|
|
100
267
|
/** Origin is annotated by the overlay, never used as an unqualified session id on the wire. */
|
|
101
|
-
machineId:
|
|
102
|
-
machineName:
|
|
103
|
-
repoId:
|
|
268
|
+
machineId: z3.string().optional(),
|
|
269
|
+
machineName: z3.string().optional(),
|
|
270
|
+
repoId: z3.string().optional(),
|
|
104
271
|
/** Annotated by the fleet when its machine is unreachable and the last snapshot is kept (keepStale, spec 2026-10-14 §6). */
|
|
105
|
-
offline:
|
|
106
|
-
|
|
107
|
-
|
|
272
|
+
offline: z3.boolean().optional(),
|
|
273
|
+
/** The permanent Ledge brain session (Lot 7). */
|
|
274
|
+
brain: z3.literal(true).optional(),
|
|
275
|
+
id: z3.string().min(1),
|
|
276
|
+
name: z3.string().min(1),
|
|
108
277
|
tool: ToolSchema,
|
|
109
|
-
cwd:
|
|
110
|
-
project:
|
|
111
|
-
title:
|
|
278
|
+
cwd: z3.string(),
|
|
279
|
+
project: z3.string(),
|
|
280
|
+
title: z3.string(),
|
|
112
281
|
/** One line saying what the session is about (spec 2026-10-11 §3). */
|
|
113
|
-
topic:
|
|
282
|
+
topic: z3.string().optional(),
|
|
114
283
|
topicSource: TopicSourceSchema.optional(),
|
|
115
284
|
/** The repository (its main worktree's folder name and absolute path), the checked-out branch, and whether `cwd` is a linked worktree (§4). */
|
|
116
|
-
repo:
|
|
117
|
-
repoPath:
|
|
118
|
-
branch:
|
|
119
|
-
worktree:
|
|
285
|
+
repo: z3.string().optional(),
|
|
286
|
+
repoPath: z3.string().optional(),
|
|
287
|
+
branch: z3.string().optional(),
|
|
288
|
+
worktree: z3.boolean().optional(),
|
|
120
289
|
/** The folder the agent works in now, as its last hook reported it: `cwd` stays where it started, this follows a worktree it entered. */
|
|
121
|
-
workdir:
|
|
122
|
-
tmuxSession:
|
|
290
|
+
workdir: z3.string().optional(),
|
|
291
|
+
tmuxSession: z3.string().nullable(),
|
|
123
292
|
/** Left out for a tmux session. `omp-rpc`: no pane, nothing to attach to; the daemon holds the process (see `agentSessionFile`). */
|
|
124
293
|
backend: BackendSchema.optional(),
|
|
125
294
|
state: SessionStateSchema,
|
|
126
295
|
pending: PendingSchema.optional(),
|
|
127
|
-
helpers:
|
|
296
|
+
helpers: z3.array(HelperSchema).default([]),
|
|
128
297
|
/** OMP: the instructions waiting in its own queue (typed while it works), as it reports them; left out when none. */
|
|
129
|
-
queue:
|
|
298
|
+
queue: z3.array(z3.object({ text: z3.string(), mode: z3.enum(["steer", "follow_up"]) })).optional(),
|
|
130
299
|
lastReply: ReplySchema.optional(),
|
|
131
300
|
/** The last message this session sent to another agent: its tool_use_id, the address, the peer's Ledge session id, a one-line summary. */
|
|
132
|
-
lastMessage:
|
|
133
|
-
startedAt:
|
|
134
|
-
lastActivityAt:
|
|
135
|
-
lastHookAt:
|
|
301
|
+
lastMessage: z3.object({ id: z3.string(), to: z3.string(), peer: z3.string().optional(), summary: z3.string(), at: z3.string().datetime() }).optional(),
|
|
302
|
+
startedAt: z3.string().datetime(),
|
|
303
|
+
lastActivityAt: z3.string().datetime(),
|
|
304
|
+
lastHookAt: z3.string().datetime().optional(),
|
|
136
305
|
/** The model and effort Ledge launched the agent with (#58); left out, the agent's own default. */
|
|
137
|
-
model:
|
|
138
|
-
effort:
|
|
306
|
+
model: z3.string().optional(),
|
|
307
|
+
effort: z3.string().optional(),
|
|
139
308
|
/** OMP: the approval mode it was launched with, kept by a resume; left out, `always-ask`. */
|
|
140
309
|
approval: OmpApprovalSchema.optional(),
|
|
141
|
-
/**
|
|
142
|
-
role: z2.string().optional(),
|
|
143
|
-
roleLabel: z2.string().optional(),
|
|
144
|
-
/** The live team it sits in (`team-…`); left out, the project's common table. */
|
|
145
|
-
team: z2.string().optional(),
|
|
146
|
-
/** Its role's autonomy, as launched (kept by a resume); left out, `ask`. */
|
|
310
|
+
/** Its autonomy, as launched (kept by a resume); left out, `ask`. */
|
|
147
311
|
autonomy: AutonomySchema.optional(),
|
|
148
312
|
/** Its context as its agent last reported it, after a turn (context handover spec §3.1); left out, not measured. */
|
|
149
313
|
context: ContextSchema.optional(),
|
|
@@ -152,21 +316,23 @@ var SessionSchema = z2.object({
|
|
|
152
316
|
/** The session it relieves, when it is a relief. */
|
|
153
317
|
relieves: RelievesSchema.optional(),
|
|
154
318
|
/** A model change asked while the agent worked (`session.model`), applied at its next free moment; a field left out is the agent's default. */
|
|
155
|
-
modelNext:
|
|
319
|
+
modelNext: z3.object({ model: z3.string().optional(), effort: z3.string().optional() }).optional(),
|
|
156
320
|
/** The agent's own session id when Ledge chose it at launch (Claude Code `--session-id`). */
|
|
157
|
-
agentSessionId:
|
|
321
|
+
agentSessionId: z3.string().optional(),
|
|
158
322
|
/** OMP: the exact journal file of the conversation, what a resume names (never "the latest of the folder"). */
|
|
159
|
-
agentSessionFile:
|
|
323
|
+
agentSessionFile: z3.string().optional(),
|
|
160
324
|
/** The agent's transcript: the latest one a hook named (it follows /clear and --resume), else the one the launch implies. */
|
|
161
|
-
transcriptPath:
|
|
162
|
-
exitCode:
|
|
163
|
-
exitedAt:
|
|
164
|
-
lastError:
|
|
165
|
-
source:
|
|
325
|
+
transcriptPath: z3.string().optional(),
|
|
326
|
+
exitCode: z3.number().int().nullable().optional(),
|
|
327
|
+
exitedAt: z3.string().datetime().optional(),
|
|
328
|
+
lastError: z3.string().optional(),
|
|
329
|
+
source: z3.enum(["launcher", "hook_only"]),
|
|
166
330
|
/** When the session was put in the archive (session management): set, it leaves the live list and waits to be restored, resumed or deleted. */
|
|
167
|
-
archivedAt:
|
|
331
|
+
archivedAt: z3.string().datetime().optional(),
|
|
168
332
|
/** When the session was last taken out of the archive: the poller's retirement clock restarts from it, not from `exitedAt`. */
|
|
169
|
-
restoredAt:
|
|
333
|
+
restoredAt: z3.string().datetime().optional(),
|
|
334
|
+
/** The OpenRig seat this session runs in (paired from the hook's `openrig.sessionName`). */
|
|
335
|
+
rig: SessionRigSchema.optional()
|
|
170
336
|
});
|
|
171
337
|
function backendOf(s) {
|
|
172
338
|
return s.backend ?? "tmux";
|
|
@@ -175,346 +341,21 @@ function isArchived(s) {
|
|
|
175
341
|
return s.archivedAt !== void 0;
|
|
176
342
|
}
|
|
177
343
|
|
|
178
|
-
// ../protocol/src/teams.ts
|
|
179
|
-
import { z as z7 } from "zod";
|
|
180
|
-
|
|
181
|
-
// ../protocol/src/models.ts
|
|
182
|
-
import { z as z3 } from "zod";
|
|
183
|
-
var ModelIdSchema = z3.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@[\]=,+-]*$/, "not a model id");
|
|
184
|
-
var EffortSchema = z3.string().min(1).max(20).regex(/^[a-z]+$/, "not an effort");
|
|
185
|
-
var ModelChoiceSchema = z3.object({
|
|
186
|
-
id: ModelIdSchema,
|
|
187
|
-
label: z3.string(),
|
|
188
|
-
description: z3.string().optional(),
|
|
189
|
-
efforts: z3.array(EffortSchema).optional(),
|
|
190
|
-
defaultEffort: EffortSchema.optional(),
|
|
191
|
-
isDefault: z3.boolean().optional()
|
|
192
|
-
});
|
|
193
|
-
var ModelsListSchema = z3.object({
|
|
194
|
-
type: z3.literal("models.list"),
|
|
195
|
-
requestId: z3.string().min(1).max(100),
|
|
196
|
-
tool: ToolSchema,
|
|
197
|
-
machineId: z3.string().optional()
|
|
198
|
-
});
|
|
199
|
-
var ModelsListingSchema = z3.object({
|
|
200
|
-
type: z3.literal("models.listing"),
|
|
201
|
-
requestId: z3.string(),
|
|
202
|
-
tool: ToolSchema,
|
|
203
|
-
models: z3.array(ModelChoiceSchema),
|
|
204
|
-
error: z3.string().optional()
|
|
205
|
-
});
|
|
206
|
-
var ModelRefusalSchema = z3.enum(["not_launched", "ended", "no_conversation", "not_running", "effort_in_model", "restart_failed", "refused"]);
|
|
207
|
-
var SessionModelAcceptedSchema = z3.object({ type: z3.literal("session.model.accepted"), id: z3.string(), when: z3.enum(["now", "turn", "none"]) });
|
|
208
|
-
|
|
209
|
-
// ../protocol/src/projects.ts
|
|
210
|
-
import { z as z6 } from "zod";
|
|
211
|
-
|
|
212
|
-
// ../protocol/src/machines.ts
|
|
213
|
-
import { z as z5 } from "zod";
|
|
214
|
-
|
|
215
|
-
// ../protocol/src/ssh.ts
|
|
216
|
-
import { z as z4 } from "zod";
|
|
217
|
-
var SshAliasSchema = z4.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/);
|
|
218
|
-
|
|
219
|
-
// ../protocol/src/machines.ts
|
|
220
|
-
var MachineIdSchema = z5.string().regex(/^[a-zA-Z0-9_-]{1,64}$/);
|
|
221
|
-
var MachineSchema = z5.object({
|
|
222
|
-
id: MachineIdSchema,
|
|
223
|
-
label: z5.string().trim().min(1).max(80),
|
|
224
|
-
ssh: SshAliasSchema.optional()
|
|
225
|
-
});
|
|
226
|
-
var MachinesSchema = z5.array(MachineSchema).min(1).max(20).superRefine((list, ctx) => {
|
|
227
|
-
if (list[0]?.id !== "local" || list[0].ssh !== void 0) ctx.addIssue({ code: "custom", message: "the first machine is local, without ssh", path: [0] });
|
|
228
|
-
const ids = /* @__PURE__ */ new Set(), aliases = /* @__PURE__ */ new Set();
|
|
229
|
-
list.forEach((m, i) => {
|
|
230
|
-
if (i > 0 && !m.ssh) ctx.addIssue({ code: "custom", message: "a remote machine needs an ssh alias", path: [i, "ssh"] });
|
|
231
|
-
if (ids.has(m.id)) ctx.addIssue({ code: "custom", message: `duplicate id ${m.id}`, path: [i, "id"] });
|
|
232
|
-
ids.add(m.id);
|
|
233
|
-
if (m.ssh) {
|
|
234
|
-
if (aliases.has(m.ssh)) ctx.addIssue({ code: "custom", message: `duplicate ssh alias ${m.ssh}`, path: [i, "ssh"] });
|
|
235
|
-
aliases.add(m.ssh);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// ../protocol/src/projects.ts
|
|
241
|
-
var PROJECT_COLORS = { blue: "#3b82f6", green: "#10b981", amber: "#f59e0b", violet: "#8b5cf6", teal: "#14b8a6", pink: "#ec4899", red: "#ef4444", slate: "#64748b" };
|
|
242
|
-
var ProjectColorSchema = z6.enum(["blue", "green", "amber", "violet", "teal", "pink", "red", "slate"]);
|
|
243
|
-
var ProjectIdSchema = z6.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/);
|
|
244
|
-
function normalizePath(raw) {
|
|
245
|
-
const p = raw.trim().replace(/\/+/g, "/");
|
|
246
|
-
return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
247
|
-
}
|
|
248
|
-
var PathSchema = z6.string().max(4096).refine((p) => p.startsWith("/") && p === normalizePath(p), "an absolute, normalized path");
|
|
249
|
-
var RepoSchema = z6.object({ machine: MachineIdSchema, path: PathSchema });
|
|
250
|
-
var ProjectSchema = z6.object({
|
|
251
|
-
id: ProjectIdSchema,
|
|
252
|
-
name: z6.string().trim().min(1).max(80),
|
|
253
|
-
color: ProjectColorSchema,
|
|
254
|
-
description: z6.string().max(500).default(""),
|
|
255
|
-
repos: z6.array(RepoSchema).max(50)
|
|
256
|
-
});
|
|
257
|
-
var RuleSchema = z6.object({ machine: MachineIdSchema, path: PathSchema, project: ProjectIdSchema });
|
|
258
|
-
function projectsProblems(p) {
|
|
259
|
-
const out = [];
|
|
260
|
-
const ids = /* @__PURE__ */ new Set(), names = /* @__PURE__ */ new Set(), repos = /* @__PURE__ */ new Set(), rules = /* @__PURE__ */ new Set();
|
|
261
|
-
p.projects.forEach((project, i) => {
|
|
262
|
-
if (ids.has(project.id)) out.push({ code: "duplicate_id", path: ["projects", i, "id"] });
|
|
263
|
-
ids.add(project.id);
|
|
264
|
-
const name = project.name.trim().toLocaleLowerCase();
|
|
265
|
-
if (names.has(name)) out.push({ code: "duplicate_name", path: ["projects", i, "name"] });
|
|
266
|
-
names.add(name);
|
|
267
|
-
project.repos.forEach((repo, j) => {
|
|
268
|
-
const key = `${repo.machine}
|
|
269
|
-
${repo.path}`;
|
|
270
|
-
if (repos.has(key)) out.push({ code: "duplicate_repo", path: ["projects", i, "repos", j] });
|
|
271
|
-
repos.add(key);
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
p.rules.forEach((rule, i) => {
|
|
275
|
-
const key = `${rule.machine}
|
|
276
|
-
${rule.path}`;
|
|
277
|
-
if (rules.has(key)) out.push({ code: "duplicate_rule", path: ["rules", i] });
|
|
278
|
-
rules.add(key);
|
|
279
|
-
if (!ids.has(rule.project)) out.push({ code: "unknown_project", path: ["rules", i, "project"] });
|
|
280
|
-
});
|
|
281
|
-
return out;
|
|
282
|
-
}
|
|
283
|
-
var ProjectsSchema = z6.object({ projects: z6.array(ProjectSchema).max(100), rules: z6.array(RuleSchema).max(1e3) }).superRefine((p, ctx) => {
|
|
284
|
-
for (const problem of projectsProblems(p)) ctx.addIssue({ code: "custom", message: problem.code, path: problem.path });
|
|
285
|
-
});
|
|
286
|
-
var DEFAULT_PROJECTS = { projects: [], rules: [] };
|
|
287
|
-
var under = (cwd, path2) => path2 === "/" || cwd === path2 || cwd.startsWith(`${path2}/`);
|
|
288
|
-
function projectOf(session, state) {
|
|
289
|
-
const machine = session.machineId ?? "local";
|
|
290
|
-
const cwd = normalizePath(session.cwd);
|
|
291
|
-
const known = new Set(state.projects.map((p) => p.id));
|
|
292
|
-
const candidates = [
|
|
293
|
-
...state.projects.flatMap((p) => p.repos.filter((r) => r.machine === machine).map((r) => ({ project: p.id, path: r.path, rule: false }))),
|
|
294
|
-
...state.rules.filter((r) => r.machine === machine && known.has(r.project)).map((r) => ({ project: r.project, path: r.path, rule: true }))
|
|
295
|
-
].filter((c) => under(cwd, c.path));
|
|
296
|
-
candidates.sort((x, y) => y.path.length - x.path.length || Number(y.rule) - Number(x.rule));
|
|
297
|
-
return candidates[0]?.project ?? null;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// ../protocol/src/teams.ts
|
|
301
|
-
var RoleIdSchema = z7.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/);
|
|
302
|
-
var ROLE_LABEL_MAX = 40;
|
|
303
|
-
var ROLE_PROMPT_MAX = 8e3;
|
|
304
|
-
var DefinitionHashSchema = z7.string().regex(/^[0-9a-f]{16}$/);
|
|
305
|
-
var RoleSchema = z7.object({
|
|
306
|
-
id: RoleIdSchema,
|
|
307
|
-
label: z7.string().min(1).max(ROLE_LABEL_MAX),
|
|
308
|
-
prompt: z7.string().max(ROLE_PROMPT_MAX),
|
|
309
|
-
tool: ToolSchema.optional(),
|
|
310
|
-
model: ModelIdSchema.optional(),
|
|
311
|
-
effort: EffortSchema.optional(),
|
|
312
|
-
autonomy: AutonomySchema.optional(),
|
|
313
|
-
source: z7.enum(["repo", "home"]),
|
|
314
|
-
path: z7.string(),
|
|
315
|
-
shadows: z7.string().optional(),
|
|
316
|
-
/** 16 hex chars of the file's text: what a write or delete must name as its base (absent from older daemons). */
|
|
317
|
-
hash: DefinitionHashSchema.optional()
|
|
318
|
-
});
|
|
319
|
-
var DefinitionProblemSchema = z7.object({ path: z7.string(), reason: z7.string() });
|
|
320
|
-
var TEAM_SEATS_MAX = 8;
|
|
321
|
-
var SeatSchema = z7.object({
|
|
322
|
-
role: RoleIdSchema,
|
|
323
|
-
tool: ToolSchema.optional(),
|
|
324
|
-
model: ModelIdSchema.optional(),
|
|
325
|
-
effort: EffortSchema.optional(),
|
|
326
|
-
name: z7.string().min(1).max(40).optional()
|
|
327
|
-
});
|
|
328
|
-
var uniqueRoles = (seats) => new Set(seats.map((s) => s.role)).size === seats.length;
|
|
329
|
-
var SeatsSchema = z7.array(SeatSchema).min(1).max(TEAM_SEATS_MAX).refine(uniqueRoles, "a role twice in one team");
|
|
330
|
-
var TeamNameSchema = z7.string().trim().min(1).max(60);
|
|
331
|
-
var TemplateIdSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/);
|
|
332
|
-
var TeamTemplateSchema = z7.object({ id: z7.string().min(1).max(64), name: TeamNameSchema, seats: SeatsSchema, source: z7.enum(["repo", "home"]), path: z7.string(), hash: DefinitionHashSchema.optional() });
|
|
333
|
-
var TeamIdSchema = z7.string().regex(/^team-[0-9a-f]{4,8}$/);
|
|
334
|
-
var TeamStateSchema = z7.enum(["active", "resting", "dissolved"]);
|
|
335
|
-
var TeamSchema = z7.object({
|
|
336
|
-
id: TeamIdSchema,
|
|
337
|
-
name: TeamNameSchema,
|
|
338
|
-
template: z7.string().optional(),
|
|
339
|
-
project: z7.string().nullable(),
|
|
340
|
-
machine: z7.string(),
|
|
341
|
-
cwd: z7.string(),
|
|
342
|
-
state: TeamStateSchema,
|
|
343
|
-
/** A live seat: `tool`, `model` and `effort` are what it was composed with over the role's defaults; `leaving` marks a seat removed
|
|
344
|
-
* from the composition that finishes its turn and then is closed. */
|
|
345
|
-
seats: z7.array(z7.object({
|
|
346
|
-
role: RoleIdSchema,
|
|
347
|
-
sessionId: z7.string().nullable(),
|
|
348
|
-
tool: ToolSchema.optional(),
|
|
349
|
-
model: ModelIdSchema.optional(),
|
|
350
|
-
effort: EffortSchema.optional(),
|
|
351
|
-
leaving: z7.literal(true).optional()
|
|
352
|
-
})).max(TEAM_SEATS_MAX * 2),
|
|
353
|
-
createdAt: z7.string(),
|
|
354
|
-
updatedAt: z7.string(),
|
|
355
|
-
restingAt: z7.string().optional(),
|
|
356
|
-
dissolvedAt: z7.string().optional()
|
|
357
|
-
});
|
|
358
|
-
var RolesListSchema = z7.object({ type: z7.literal("roles.list"), cwd: z7.string().min(1).optional(), requestId: z7.string().max(100).optional(), machineId: z7.string().optional() });
|
|
359
|
-
var RolesListingSchema = z7.object({
|
|
360
|
-
type: z7.literal("roles.listing"),
|
|
361
|
-
requestId: z7.string().optional(),
|
|
362
|
-
roles: z7.array(RoleSchema),
|
|
363
|
-
templates: z7.array(TeamTemplateSchema).default([]),
|
|
364
|
-
problems: z7.array(DefinitionProblemSchema),
|
|
365
|
-
/** Set by the fleet when the project's machine could not answer (#99): 'offline', or 'outdated' for a daemon without roles.list. */
|
|
366
|
-
error: z7.string().optional()
|
|
367
|
-
});
|
|
368
|
-
var TeamLaunchSchema = z7.object({
|
|
369
|
-
type: z7.literal("team.launch"),
|
|
370
|
-
rid: z7.string().min(1).max(100),
|
|
371
|
-
cwd: z7.string().min(1).optional(),
|
|
372
|
-
template: z7.string().min(1).max(64).optional(),
|
|
373
|
-
seats: SeatsSchema.optional(),
|
|
374
|
-
name: TeamNameSchema.optional(),
|
|
375
|
-
qid: z7.string().min(1).optional(),
|
|
376
|
-
machineId: z7.string().optional(),
|
|
377
|
-
/** The project's name, for a client that holds no path (the phone): the daemon resolves its folder. */
|
|
378
|
-
project: ProjectIdSchema.optional()
|
|
379
|
-
});
|
|
380
|
-
var TeamJoinSchema = z7.object({ type: z7.literal("team.join"), team: TeamIdSchema, session: z7.string().min(1), role: RoleIdSchema });
|
|
381
|
-
var TeamLeaveSchema = z7.object({ type: z7.literal("team.leave"), team: TeamIdSchema, session: z7.string().min(1) });
|
|
382
|
-
var TeamResumeSchema = z7.object({ type: z7.literal("team.resume"), team: TeamIdSchema });
|
|
383
|
-
var TeamDissolveSchema = z7.object({ type: z7.literal("team.dissolve"), team: TeamIdSchema });
|
|
384
|
-
var TeamSaveSchema = z7.object({ type: z7.literal("team.save"), rid: z7.string().min(1).max(100), name: TeamNameSchema, seats: SeatsSchema, qid: z7.string().min(1).optional() });
|
|
385
|
-
var DefinitionScopeSchema = z7.enum(["home", "repo"]);
|
|
386
|
-
var RoleDraftSchema = RoleSchema.pick({ id: true, label: true, prompt: true, tool: true, model: true, effort: true, autonomy: true });
|
|
387
|
-
var DefinitionCodeSchema = z7.enum(["changed", "exists", "missing", "unknown_project", "invalid"]);
|
|
388
|
-
var scopeFields = { rid: z7.string().min(1).max(100), scope: DefinitionScopeSchema, project: ProjectIdSchema.optional() };
|
|
389
|
-
var scopeIssue = (m) => m.scope === void 0 || m.scope === "repo" === (m.project !== void 0) ? null : "a repo scope names a project, a home scope none";
|
|
390
|
-
var DefinitionBaseSchema = DefinitionHashSchema;
|
|
391
|
-
var DefinitionsListSchema = z7.object({ type: z7.literal("definitions.list"), requestId: z7.string().max(100).optional() });
|
|
392
|
-
var RoleWriteSchema = z7.object({ type: z7.literal("role.write"), ...scopeFields, role: RoleDraftSchema, base: DefinitionBaseSchema.nullable() });
|
|
393
|
-
var RoleDeleteSchema = z7.object({ type: z7.literal("role.delete"), ...scopeFields, id: RoleIdSchema, base: DefinitionBaseSchema });
|
|
394
|
-
var TemplateWriteSchema = z7.object({
|
|
395
|
-
type: z7.literal("template.write"),
|
|
396
|
-
...scopeFields,
|
|
397
|
-
id: TemplateIdSchema.optional(),
|
|
398
|
-
name: TeamNameSchema,
|
|
399
|
-
seats: SeatsSchema,
|
|
400
|
-
base: DefinitionBaseSchema.nullable()
|
|
401
|
-
});
|
|
402
|
-
var TemplateDeleteSchema = z7.object({ type: z7.literal("template.delete"), ...scopeFields, id: TemplateIdSchema, base: DefinitionBaseSchema });
|
|
403
|
-
var TeamComposeSchema = z7.object({ type: z7.literal("team.compose"), rid: z7.string().min(1).max(100), team: TeamIdSchema, seats: SeatsSchema });
|
|
404
|
-
var TeamSeatSchema = z7.object({ type: z7.literal("team.seat"), rid: z7.string().min(1).max(100), team: TeamIdSchema, role: RoleIdSchema, action: z7.enum(["start", "restart"]) });
|
|
405
|
-
var DefinitionsListingSchema = z7.object({
|
|
406
|
-
type: z7.literal("definitions.listing"),
|
|
407
|
-
requestId: z7.string().optional(),
|
|
408
|
-
scopes: z7.array(z7.object({
|
|
409
|
-
scope: DefinitionScopeSchema,
|
|
410
|
-
project: z7.string().nullable(),
|
|
411
|
-
roles: z7.array(RoleSchema),
|
|
412
|
-
templates: z7.array(TeamTemplateSchema),
|
|
413
|
-
problems: z7.array(DefinitionProblemSchema)
|
|
414
|
-
}))
|
|
415
|
-
});
|
|
416
|
-
var DefinitionResultSchema = z7.object({ type: z7.literal("definition.result"), rid: z7.string(), id: z7.string().optional(), hash: DefinitionHashSchema.optional(), code: DefinitionCodeSchema.optional(), reason: z7.string().optional() });
|
|
417
|
-
var SeatModelOutcomeSchema = z7.object({ role: z7.string(), session: z7.string(), when: z7.enum(["now", "turn"]).optional(), code: ModelRefusalSchema.optional() });
|
|
418
|
-
var TeamChangedSchema = z7.object({ type: z7.literal("team.changed"), rid: z7.string(), team: TeamSchema.optional(), reason: z7.string().optional(), models: z7.array(SeatModelOutcomeSchema).max(16).optional() });
|
|
419
|
-
var TeamsMessageSchema = z7.object({ type: z7.literal("teams"), teams: z7.array(TeamSchema) });
|
|
420
|
-
var TeamUpdatedSchema = z7.object({ type: z7.literal("team.updated"), team: TeamSchema });
|
|
421
|
-
var TeamLaunchedSchema = z7.object({ type: z7.literal("team.launched"), rid: z7.string(), team: TeamSchema });
|
|
422
|
-
var TeamFailedSchema = z7.object({ type: z7.literal("team.failed"), rid: z7.string(), reason: z7.string(), team: TeamSchema.optional() });
|
|
423
|
-
var TeamSavedSchema = z7.object({ type: z7.literal("team.saved"), rid: z7.string(), path: z7.string().optional(), reason: z7.string().optional() });
|
|
424
|
-
var isTeamLive = (t) => t.state !== "dissolved";
|
|
425
|
-
|
|
426
|
-
// ../protocol/src/tasks.ts
|
|
427
|
-
import { z as z8 } from "zod";
|
|
428
|
-
var TASK_TITLE_MAX = 200;
|
|
429
|
-
var TASK_TEXT_MAX = 2e4;
|
|
430
|
-
var LEDGE_GIVER = "ledge";
|
|
431
|
-
var TASK_OPS = ["give", "take", "pass", "done", "refuse", "cancel", "drop"];
|
|
432
|
-
var TRANSMISSION_OPS = ["give", "pass", "done", "refuse", "cancel"];
|
|
433
|
-
var TaskLogEntrySchema = z8.object({
|
|
434
|
-
at: z8.string(),
|
|
435
|
-
by: z8.string(),
|
|
436
|
-
what: z8.string(),
|
|
437
|
-
op: z8.enum(TASK_OPS).optional(),
|
|
438
|
-
from: z8.string().optional(),
|
|
439
|
-
to: z8.string().optional(),
|
|
440
|
-
fromName: z8.string().optional(),
|
|
441
|
-
toName: z8.string().optional(),
|
|
442
|
-
note: z8.string().optional()
|
|
443
|
-
});
|
|
444
|
-
var TaskSchema = z8.object({
|
|
445
|
-
id: z8.string(),
|
|
446
|
-
title: z8.string(),
|
|
447
|
-
text: z8.string().optional(),
|
|
448
|
-
giver: z8.string(),
|
|
449
|
-
giverName: z8.string(),
|
|
450
|
-
owner: z8.string(),
|
|
451
|
-
ownerName: z8.string(),
|
|
452
|
-
state: z8.enum(["open", "taken", "done", "refused", "dropped"]),
|
|
453
|
-
result: z8.string().optional(),
|
|
454
|
-
/** The live team its owner sat in when it was given (or passed), so a team's tasks can be listed. */
|
|
455
|
-
team: z8.string().optional(),
|
|
456
|
-
/** A task Ledge gave and the user put away from « À toi » once closed. */
|
|
457
|
-
seen: z8.boolean().optional(),
|
|
458
|
-
at: z8.string(),
|
|
459
|
-
updatedAt: z8.string(),
|
|
460
|
-
log: z8.array(TaskLogEntrySchema)
|
|
461
|
-
});
|
|
462
|
-
var handOver = (op) => TRANSMISSION_OPS.includes(op ?? "");
|
|
463
|
-
function transmissions(t) {
|
|
464
|
-
return t.log.flatMap((e, index) => handOver(e.op) && e.from !== void 0 && e.to !== void 0 ? [{ index, at: e.at, op: e.op, from: e.from, to: e.to, fromName: e.fromName ?? "", toName: e.toName ?? "", ...e.note !== void 0 ? { note: e.note } : {} }] : []);
|
|
465
|
-
}
|
|
466
|
-
var givenByLedge = (t) => t.giver === LEDGE_GIVER;
|
|
467
|
-
var taskClosed = (t) => t.state === "done" || t.state === "refused" || t.state === "dropped";
|
|
468
|
-
var TaskCmdSchema = z8.object({
|
|
469
|
-
type: z8.literal("task.cmd"),
|
|
470
|
-
rid: z8.string().min(1),
|
|
471
|
-
by: z8.string().min(1),
|
|
472
|
-
op: z8.enum(["give", "take", "done", "refuse", "pass", "show", "list", "seen", "cancel"]),
|
|
473
|
-
task: z8.string().min(1).max(40).optional(),
|
|
474
|
-
to: z8.string().min(1).optional(),
|
|
475
|
-
title: z8.string().min(1).max(TASK_TITLE_MAX).optional(),
|
|
476
|
-
text: z8.string().max(TASK_TEXT_MAX).optional(),
|
|
477
|
-
all: z8.boolean().optional(),
|
|
478
|
-
team: z8.string().min(1).max(40).optional(),
|
|
479
|
-
qid: z8.string().min(1).optional(),
|
|
480
|
-
row: z8.number().int().min(0).max(7).optional()
|
|
481
|
-
});
|
|
482
|
-
var TaskUpdatedSchema = z8.object({ type: z8.literal("task.updated"), task: TaskSchema });
|
|
483
|
-
var TaskResultSchema = z8.object({
|
|
484
|
-
type: z8.literal("task.result"),
|
|
485
|
-
rid: z8.string(),
|
|
486
|
-
task: TaskSchema.optional(),
|
|
487
|
-
tasks: z8.array(TaskSchema).optional(),
|
|
488
|
-
reason: z8.string().optional()
|
|
489
|
-
});
|
|
490
|
-
|
|
491
344
|
// ../protocol/src/actions.ts
|
|
492
|
-
import { z as
|
|
345
|
+
import { z as z7 } from "zod";
|
|
493
346
|
|
|
494
347
|
// ../protocol/src/proposal.ts
|
|
495
|
-
import { z as
|
|
496
|
-
var ProposalBlockSchema =
|
|
497
|
-
var RelayBlockSchema =
|
|
498
|
-
var ProjectBlockSchema =
|
|
499
|
-
name:
|
|
500
|
-
description:
|
|
501
|
-
repos:
|
|
502
|
-
addTo:
|
|
503
|
-
});
|
|
504
|
-
var CloseBlockSchema =
|
|
505
|
-
var
|
|
506
|
-
cwd: z9.string().trim().min(1),
|
|
507
|
-
template: z9.string().trim().min(1).max(64).optional(),
|
|
508
|
-
name: z9.string().trim().min(1).max(60).optional(),
|
|
509
|
-
seats: z9.array(z9.object({ role: z9.string().trim().min(1).max(40), tool: z9.enum(["claude", "codex", "cursor", "omp"]).optional(), model: z9.string().max(200).optional(), effort: z9.string().max(20).optional(), name: z9.string().max(40).optional() })).min(1).max(8).optional()
|
|
510
|
-
}).refine((b) => !!b.template || !!b.seats?.length, "a template or seats");
|
|
511
|
-
var TaskBlockSchema = z9.object({
|
|
512
|
-
to: z9.string().trim().min(1).max(80),
|
|
513
|
-
team: z9.string().trim().max(40).optional(),
|
|
514
|
-
title: z9.string().trim().min(1).max(200),
|
|
515
|
-
text: z9.string().max(2e4).optional()
|
|
516
|
-
});
|
|
517
|
-
var BLOCK = /```ledge-(session|relay|project|close|team|task|action)[ \t]*\n?([\s\S]*?)(?:```|$)/g;
|
|
348
|
+
import { z as z4 } from "zod";
|
|
349
|
+
var ProposalBlockSchema = z4.object({ cwd: z4.string().trim().min(1), prompt: z4.string().optional() });
|
|
350
|
+
var RelayBlockSchema = z4.object({ session: z4.string().trim().min(1), text: z4.string().trim().min(1) });
|
|
351
|
+
var ProjectBlockSchema = z4.object({
|
|
352
|
+
name: z4.string().trim().min(1).max(80),
|
|
353
|
+
description: z4.string().max(500).optional(),
|
|
354
|
+
repos: z4.array(z4.object({ path: z4.string().min(1).max(4096), why: z4.string().trim().min(1).max(200) })).min(1).max(50),
|
|
355
|
+
addTo: z4.string().trim().max(80).optional()
|
|
356
|
+
});
|
|
357
|
+
var CloseBlockSchema = z4.object({ session: z4.string().trim().min(1) });
|
|
358
|
+
var BLOCK = /```ledge-(session|relay|project|close|action)[ \t]*\n?([\s\S]*?)(?:```|$)/g;
|
|
518
359
|
function extractProposal(answer) {
|
|
519
360
|
const found = [];
|
|
520
361
|
const text2 = answer.replace(BLOCK, (_m, kind, body) => {
|
|
@@ -540,22 +381,6 @@ function parseCloseBlock(raw) {
|
|
|
540
381
|
return null;
|
|
541
382
|
}
|
|
542
383
|
}
|
|
543
|
-
function parseTeamBlock(raw) {
|
|
544
|
-
try {
|
|
545
|
-
const r = TeamBlockSchema.safeParse(JSON.parse(raw));
|
|
546
|
-
return r.success ? r.data : null;
|
|
547
|
-
} catch {
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
function parseTaskBlock(raw) {
|
|
552
|
-
try {
|
|
553
|
-
const r = TaskBlockSchema.safeParse(JSON.parse(raw));
|
|
554
|
-
return r.success ? r.data : null;
|
|
555
|
-
} catch {
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
384
|
function parseProjectBlock(raw) {
|
|
560
385
|
try {
|
|
561
386
|
const r = ProjectBlockSchema.safeParse(JSON.parse(raw));
|
|
@@ -565,9 +390,37 @@ function parseProjectBlock(raw) {
|
|
|
565
390
|
}
|
|
566
391
|
}
|
|
567
392
|
|
|
393
|
+
// ../protocol/src/models.ts
|
|
394
|
+
import { z as z5 } from "zod";
|
|
395
|
+
var ModelIdSchema = z5.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@[\]=,+-]*$/, "not a model id");
|
|
396
|
+
var EffortSchema = z5.string().min(1).max(20).regex(/^[a-z]+$/, "not an effort");
|
|
397
|
+
var ModelChoiceSchema = z5.object({
|
|
398
|
+
id: ModelIdSchema,
|
|
399
|
+
label: z5.string(),
|
|
400
|
+
description: z5.string().optional(),
|
|
401
|
+
efforts: z5.array(EffortSchema).optional(),
|
|
402
|
+
defaultEffort: EffortSchema.optional(),
|
|
403
|
+
isDefault: z5.boolean().optional()
|
|
404
|
+
});
|
|
405
|
+
var ModelsListSchema = z5.object({
|
|
406
|
+
type: z5.literal("models.list"),
|
|
407
|
+
requestId: z5.string().min(1).max(100),
|
|
408
|
+
tool: ToolSchema,
|
|
409
|
+
machineId: z5.string().optional()
|
|
410
|
+
});
|
|
411
|
+
var ModelsListingSchema = z5.object({
|
|
412
|
+
type: z5.literal("models.listing"),
|
|
413
|
+
requestId: z5.string(),
|
|
414
|
+
tool: ToolSchema,
|
|
415
|
+
models: z5.array(ModelChoiceSchema),
|
|
416
|
+
error: z5.string().optional()
|
|
417
|
+
});
|
|
418
|
+
var ModelRefusalSchema = z5.enum(["not_launched", "ended", "no_conversation", "not_running", "effort_in_model", "restart_failed", "refused"]);
|
|
419
|
+
var SessionModelAcceptedSchema = z5.object({ type: z5.literal("session.model.accepted"), id: z5.string(), when: z5.enum(["now", "turn", "none"]) });
|
|
420
|
+
|
|
568
421
|
// ../protocol/src/decisions.ts
|
|
569
|
-
import { z as
|
|
570
|
-
var DecisionSchema =
|
|
422
|
+
import { z as z6 } from "zod";
|
|
423
|
+
var DecisionSchema = z6.enum(["accept", "deny", "always"]);
|
|
571
424
|
var NOTE_MAX = 4e3;
|
|
572
425
|
var NOTE_NOT_TYPED = "note_not_typed";
|
|
573
426
|
var DECISION_OFFERS = {
|
|
@@ -597,33 +450,22 @@ function pendingKey(p) {
|
|
|
597
450
|
}
|
|
598
451
|
|
|
599
452
|
// ../protocol/src/actions.ts
|
|
600
|
-
var ActionRiskSchema =
|
|
453
|
+
var ActionRiskSchema = z7.enum(["read", "reversible", "destructive"]);
|
|
601
454
|
var ACTION_IDS = [
|
|
602
455
|
"session.launch",
|
|
603
456
|
"session.relay",
|
|
604
457
|
"session.close",
|
|
605
458
|
"project.propose",
|
|
606
|
-
"team.launch",
|
|
607
|
-
"task.give",
|
|
608
459
|
"session.model",
|
|
609
460
|
"session.interrupt",
|
|
610
461
|
"session.archive",
|
|
611
462
|
"session.restore",
|
|
612
463
|
"session.resume",
|
|
613
|
-
"session.decide"
|
|
614
|
-
"team.join",
|
|
615
|
-
"team.leave",
|
|
616
|
-
"team.resume",
|
|
617
|
-
"team.dissolve",
|
|
618
|
-
"team.save",
|
|
619
|
-
"task.cancel",
|
|
620
|
-
"task.pass"
|
|
464
|
+
"session.decide"
|
|
621
465
|
];
|
|
622
|
-
var ActionIdSchema =
|
|
466
|
+
var ActionIdSchema = z7.enum(ACTION_IDS);
|
|
623
467
|
var def = (d) => d;
|
|
624
|
-
var SessionRef =
|
|
625
|
-
var TeamRef = z11.string().trim().min(1).max(60);
|
|
626
|
-
var TaskRef = z11.string().trim().min(1).max(40);
|
|
468
|
+
var SessionRef = z7.string().trim().min(1).max(80);
|
|
627
469
|
var LaunchArgs = ProposalBlockSchema.extend({ model: ModelIdSchema.optional(), effort: EffortSchema.optional() });
|
|
628
470
|
var ACTIONS = [
|
|
629
471
|
def({
|
|
@@ -671,44 +513,20 @@ var ACTIONS = [
|
|
|
671
513
|
when: "seulement si l'utilisateur demande explicitement de cr\xE9er ou de compl\xE9ter un projet : lis projects.md, ne cite que des chemins absolus lus dans sessions.md, folders.md ou projects.md, jamais un d\xE9p\xF4t qui appartient d\xE9j\xE0 \xE0 un projet ; ta r\xE9ponse dit le nom, chaque d\xE9p\xF4t et pourquoi",
|
|
672
514
|
example: { name: "Facturation", description: "Le service et son front", repos: [{ path: "/home/alice/projets/billing-api", why: "le service" }] }
|
|
673
515
|
}),
|
|
674
|
-
def({
|
|
675
|
-
id: "team.launch",
|
|
676
|
-
args: TeamBlockSchema,
|
|
677
|
-
card: "team",
|
|
678
|
-
phone: "launch",
|
|
679
|
-
risk: "reversible",
|
|
680
|
-
max: 1,
|
|
681
|
-
cli: "ledge team up <mod\xE8le> --cwd <dossier>",
|
|
682
|
-
summary: "monter une \xE9quipe dans un dossier : un mod\xE8le de teams.md (template), ou des si\xE8ges (r\xF4le de roles.md, agent, et si demand\xE9 model, effort)",
|
|
683
|
-
when: "quand l'utilisateur demande explicitement de monter, lancer ou composer une \xE9quipe, avec la m\xEAme r\xE8gle de dossier s\xFBr qu'une session ; 8 si\xE8ges au plus, un r\xF4le une seule fois, seulement des r\xF4les de roles.md disponibles dans ce dossier (s'il en manque un, dis lequel et le fichier \xE0 \xE9crire, ~/.ledge/roles/<id>.md). Ne choisis jamais l'autonomie : c'est celle du r\xF4le",
|
|
684
|
-
example: { cwd: "/home/alice/projets/api", name: "api", seats: [{ role: "dev-back", tool: "claude" }, { role: "rev-back", tool: "codex" }] }
|
|
685
|
-
}),
|
|
686
|
-
def({
|
|
687
|
-
id: "task.give",
|
|
688
|
-
args: TaskBlockSchema,
|
|
689
|
-
card: "task",
|
|
690
|
-
phone: null,
|
|
691
|
-
risk: "reversible",
|
|
692
|
-
max: 8,
|
|
693
|
-
cli: 'ledge task give <session|@r\xF4le> "<titre>" "<d\xE9tail>"',
|
|
694
|
-
summary: "confier une t\xE2che \xE0 une session, ou \xE0 un @r\xF4le d'une \xE9quipe (team : son id dans teams.md ; sans team, un si\xE8ge de l'\xE9quipe propos\xE9e dans la m\xEAme r\xE9ponse)",
|
|
695
|
-
when: "quand l'utilisateur demande explicitement de confier une t\xE2che, ou juste apr\xE8s team.launch quand il a dit ce que l'\xE9quipe doit faire. Une fois l'\xE9quipe lanc\xE9e, ne propose rien pour ce que les si\xE8ges se confient entre eux",
|
|
696
|
-
example: { to: "@dev-back", title: "Pagination de /invoices", text: "Ajoute la pagination par curseur et ses tests" }
|
|
697
|
-
}),
|
|
698
516
|
def({
|
|
699
517
|
id: "session.model",
|
|
700
518
|
card: "generic",
|
|
701
519
|
phone: null,
|
|
702
520
|
risk: "reversible",
|
|
703
521
|
max: 8,
|
|
704
|
-
args:
|
|
522
|
+
args: z7.object({ session: SessionRef, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
705
523
|
summary: "changer le mod\xE8le et l'effort d'une session en cours (null : le d\xE9faut de l'agent) ; elle red\xE9marre sur la m\xEAme conversation, \xE0 son prochain moment libre",
|
|
706
524
|
when: "quand l'utilisateur demande de passer une session sur un autre mod\xE8le ou un autre effort",
|
|
707
525
|
example: { session: "Nova", model: "claude-opus-5-5", effort: "high" }
|
|
708
526
|
}),
|
|
709
527
|
def({
|
|
710
528
|
id: "session.interrupt",
|
|
711
|
-
args:
|
|
529
|
+
args: z7.object({ session: SessionRef }),
|
|
712
530
|
card: "generic",
|
|
713
531
|
phone: null,
|
|
714
532
|
risk: "reversible",
|
|
@@ -719,7 +537,7 @@ var ACTIONS = [
|
|
|
719
537
|
}),
|
|
720
538
|
def({
|
|
721
539
|
id: "session.archive",
|
|
722
|
-
args:
|
|
540
|
+
args: z7.object({ session: SessionRef }),
|
|
723
541
|
card: "generic",
|
|
724
542
|
phone: null,
|
|
725
543
|
risk: "reversible",
|
|
@@ -730,7 +548,7 @@ var ACTIONS = [
|
|
|
730
548
|
}),
|
|
731
549
|
def({
|
|
732
550
|
id: "session.restore",
|
|
733
|
-
args:
|
|
551
|
+
args: z7.object({ session: SessionRef }),
|
|
734
552
|
card: "generic",
|
|
735
553
|
phone: null,
|
|
736
554
|
risk: "reversible",
|
|
@@ -741,7 +559,7 @@ var ACTIONS = [
|
|
|
741
559
|
}),
|
|
742
560
|
def({
|
|
743
561
|
id: "session.resume",
|
|
744
|
-
args:
|
|
562
|
+
args: z7.object({ session: SessionRef }),
|
|
745
563
|
card: "generic",
|
|
746
564
|
phone: "act",
|
|
747
565
|
risk: "reversible",
|
|
@@ -755,102 +573,22 @@ var ACTIONS = [
|
|
|
755
573
|
card: "generic",
|
|
756
574
|
phone: "act",
|
|
757
575
|
max: 1,
|
|
758
|
-
args:
|
|
576
|
+
args: z7.object({ session: SessionRef, decision: z7.enum(["accept", "deny"]), note: z7.string().trim().min(1).max(NOTE_MAX).optional() }),
|
|
759
577
|
risk: (a) => a.decision === "accept" ? "destructive" : "reversible",
|
|
760
578
|
summary: "accepter ou refuser la demande en attente d'une session (permission, diff ou plan), note possible avec un refus ; jamais \xAB toujours \xBB",
|
|
761
579
|
when: "seulement quand l'utilisateur demande explicitement d'accepter ou de refuser la demande d'une session pr\xE9cise, et que sessions.md montre une demande en attente pour elle ; cite la demande telle quelle dans ta r\xE9ponse",
|
|
762
580
|
example: { session: "Nova", decision: "deny", note: "Pas sur main : cr\xE9e une branche" }
|
|
763
|
-
}),
|
|
764
|
-
def({
|
|
765
|
-
id: "team.join",
|
|
766
|
-
args: z11.object({ team: TeamRef, session: SessionRef, role: RoleIdSchema }),
|
|
767
|
-
card: "generic",
|
|
768
|
-
phone: null,
|
|
769
|
-
risk: "reversible",
|
|
770
|
-
max: 8,
|
|
771
|
-
summary: "faire entrer une session dans une \xE9quipe en cours, \xE0 un r\xF4le de roles.md",
|
|
772
|
-
when: "quand l'utilisateur demande d'ajouter une session existante \xE0 une \xE9quipe",
|
|
773
|
-
example: { team: "team-3fa2", session: "Nova", role: "dev-back" }
|
|
774
|
-
}),
|
|
775
|
-
def({
|
|
776
|
-
id: "team.leave",
|
|
777
|
-
args: z11.object({ team: TeamRef, session: SessionRef }),
|
|
778
|
-
card: "generic",
|
|
779
|
-
phone: null,
|
|
780
|
-
risk: "reversible",
|
|
781
|
-
max: 8,
|
|
782
|
-
summary: "faire sortir une session d'une \xE9quipe (la session continue)",
|
|
783
|
-
when: "quand l'utilisateur demande de retirer une session d'une \xE9quipe",
|
|
784
|
-
example: { team: "team-3fa2", session: "Nova" }
|
|
785
|
-
}),
|
|
786
|
-
def({
|
|
787
|
-
id: "team.resume",
|
|
788
|
-
args: z11.object({ team: TeamRef }),
|
|
789
|
-
card: "generic",
|
|
790
|
-
phone: "act",
|
|
791
|
-
risk: "reversible",
|
|
792
|
-
max: 1,
|
|
793
|
-
summary: "reprendre une \xE9quipe au repos : chaque si\xE8ge reprend sa conversation",
|
|
794
|
-
when: "quand l'utilisateur demande de reprendre ou relancer une \xE9quipe au repos",
|
|
795
|
-
example: { team: "team-3fa2" }
|
|
796
|
-
}),
|
|
797
|
-
def({
|
|
798
|
-
id: "team.dissolve",
|
|
799
|
-
args: z11.object({ team: TeamRef }),
|
|
800
|
-
card: "generic",
|
|
801
|
-
phone: "act",
|
|
802
|
-
risk: "destructive",
|
|
803
|
-
max: 1,
|
|
804
|
-
cli: "ledge team dissolve <\xE9quipe>",
|
|
805
|
-
summary: "dissoudre une \xE9quipe : elle dispara\xEEt, ses sessions restent ; irr\xE9versible",
|
|
806
|
-
when: "seulement quand l'utilisateur demande explicitement de dissoudre une \xE9quipe pr\xE9cise",
|
|
807
|
-
example: { team: "team-3fa2" }
|
|
808
|
-
}),
|
|
809
|
-
def({
|
|
810
|
-
id: "team.save",
|
|
811
|
-
args: z11.object({ team: TeamRef, name: TeamNameSchema }),
|
|
812
|
-
card: "generic",
|
|
813
|
-
phone: null,
|
|
814
|
-
risk: "reversible",
|
|
815
|
-
max: 1,
|
|
816
|
-
summary: "enregistrer une \xE9quipe en cours comme mod\xE8le (~/.ledge/teams/<nom>.yaml)",
|
|
817
|
-
when: "quand l'utilisateur demande d'enregistrer ou de garder la composition d'une \xE9quipe",
|
|
818
|
-
example: { team: "team-3fa2", name: "api" }
|
|
819
|
-
}),
|
|
820
|
-
def({
|
|
821
|
-
id: "task.cancel",
|
|
822
|
-
args: z11.object({ task: TaskRef, reason: z11.string().trim().min(1).max(500).optional() }),
|
|
823
|
-
card: "generic",
|
|
824
|
-
phone: "act",
|
|
825
|
-
risk: "destructive",
|
|
826
|
-
max: 8,
|
|
827
|
-
summary: "annuler une t\xE2che ouverte ou prise ; la session qui la tient est pr\xE9venue ; irr\xE9versible",
|
|
828
|
-
when: "seulement quand l'utilisateur demande explicitement d'annuler une t\xE2che de tasks.md",
|
|
829
|
-
example: { task: "t91b0d4", reason: "Plus n\xE9cessaire" }
|
|
830
|
-
}),
|
|
831
|
-
def({
|
|
832
|
-
id: "task.pass",
|
|
833
|
-
args: z11.object({ task: TaskRef, to: SessionRef, note: z11.string().trim().min(1).max(TASK_TEXT_MAX).optional() }),
|
|
834
|
-
card: "generic",
|
|
835
|
-
phone: null,
|
|
836
|
-
risk: "reversible",
|
|
837
|
-
max: 8,
|
|
838
|
-
summary: "passer une t\xE2che de tasks.md \xE0 une autre session (elle redevient ouverte)",
|
|
839
|
-
when: "quand l'utilisateur demande de r\xE9assigner ou de passer une t\xE2che \xE0 une autre session",
|
|
840
|
-
example: { task: "t91b0d4", to: "Bix" }
|
|
841
581
|
})
|
|
842
582
|
];
|
|
843
583
|
var byId = new Map(ACTIONS.map((a) => [a.id, a]));
|
|
844
|
-
var actionById = (
|
|
584
|
+
var actionById = (id2) => byId.get(id2);
|
|
845
585
|
var riskOf = (a, args) => typeof a.risk === "function" ? a.risk(args) : a.risk;
|
|
846
|
-
var ActionBlockSchema =
|
|
586
|
+
var ActionBlockSchema = z7.object({ action: z7.string().trim().min(1).max(40), args: z7.record(z7.unknown()).default({}) });
|
|
847
587
|
var LEGACY_FENCE_ACTION = {
|
|
848
588
|
session: "session.launch",
|
|
849
589
|
relay: "session.relay",
|
|
850
590
|
close: "session.close",
|
|
851
|
-
project: "project.propose"
|
|
852
|
-
team: "team.launch",
|
|
853
|
-
task: "task.give"
|
|
591
|
+
project: "project.propose"
|
|
854
592
|
};
|
|
855
593
|
function parseActionBlock(kind, raw) {
|
|
856
594
|
let json;
|
|
@@ -859,21 +597,21 @@ function parseActionBlock(kind, raw) {
|
|
|
859
597
|
} catch {
|
|
860
598
|
return { error: "invalid JSON" };
|
|
861
599
|
}
|
|
862
|
-
let
|
|
600
|
+
let id2;
|
|
863
601
|
let args;
|
|
864
602
|
if (kind === "action") {
|
|
865
603
|
const b = ActionBlockSchema.safeParse(json);
|
|
866
604
|
if (!b.success) return { error: "not an {action, args} object" };
|
|
867
|
-
|
|
605
|
+
id2 = b.data.action;
|
|
868
606
|
args = b.data.args;
|
|
869
607
|
} else {
|
|
870
|
-
|
|
608
|
+
id2 = LEGACY_FENCE_ACTION[kind];
|
|
871
609
|
args = json;
|
|
872
610
|
}
|
|
873
|
-
const action = actionById(
|
|
874
|
-
if (!action) return { error: `unknown action ${
|
|
611
|
+
const action = actionById(id2);
|
|
612
|
+
if (!action) return { error: `unknown action ${id2}` };
|
|
875
613
|
const parsed = action.args.safeParse(args);
|
|
876
|
-
if (!parsed.success) return { error: `invalid args for ${
|
|
614
|
+
if (!parsed.success) return { error: `invalid args for ${id2}: ${parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"} ${i.message}`).join("; ")}` };
|
|
877
615
|
return { action, args: parsed.data };
|
|
878
616
|
}
|
|
879
617
|
var NEVER_PROPOSED = [
|
|
@@ -885,123 +623,89 @@ var NEVER_PROPOSED = [
|
|
|
885
623
|
"r\xE9pondre \xAB toujours \xBB \xE0 une demande, ou r\xE9pondre aux questions \xE0 choix d'OMP"
|
|
886
624
|
];
|
|
887
625
|
|
|
888
|
-
// ../protocol/src/
|
|
889
|
-
import { z as
|
|
626
|
+
// ../protocol/src/openrig.ts
|
|
627
|
+
import { z as z10 } from "zod";
|
|
890
628
|
|
|
891
629
|
// ../protocol/src/chat.ts
|
|
892
|
-
import { z as
|
|
630
|
+
import { z as z9 } from "zod";
|
|
893
631
|
|
|
894
632
|
// ../protocol/src/attachments.ts
|
|
895
|
-
import { z as
|
|
633
|
+
import { z as z8 } from "zod";
|
|
896
634
|
var ATTACHMENT_ID = /^[0-9a-f]{64}$/;
|
|
897
|
-
var AttachmentIdSchema =
|
|
635
|
+
var AttachmentIdSchema = z8.string().regex(ATTACHMENT_ID);
|
|
898
636
|
var IMAGE_MIMES = ["image/webp", "image/jpeg", "image/png", "image/gif"];
|
|
899
|
-
var ImageMimeSchema =
|
|
637
|
+
var ImageMimeSchema = z8.enum(IMAGE_MIMES);
|
|
900
638
|
var ATTACHMENT_MAX = 4e5;
|
|
901
639
|
var ATTACHMENT_LOCAL_MAX = 2e6;
|
|
902
640
|
var PREVIEW_MAX = 4e4;
|
|
903
641
|
var ATTACHMENTS_MAX = 5;
|
|
904
642
|
var b64Max = (bytes) => Math.ceil(bytes / 3) * 4;
|
|
905
|
-
var AttachmentRefSchema =
|
|
643
|
+
var AttachmentRefSchema = z8.object({
|
|
906
644
|
id: AttachmentIdSchema,
|
|
907
645
|
mime: ImageMimeSchema,
|
|
908
|
-
width:
|
|
909
|
-
height:
|
|
910
|
-
bytes:
|
|
911
|
-
preview:
|
|
646
|
+
width: z8.number().int().positive(),
|
|
647
|
+
height: z8.number().int().positive(),
|
|
648
|
+
bytes: z8.number().int().positive(),
|
|
649
|
+
preview: z8.boolean()
|
|
912
650
|
});
|
|
913
|
-
var ImageUploadSchema =
|
|
651
|
+
var ImageUploadSchema = z8.object({
|
|
914
652
|
mime: ImageMimeSchema,
|
|
915
|
-
data:
|
|
916
|
-
preview:
|
|
917
|
-
});
|
|
918
|
-
var AttachmentErrorReasonSchema =
|
|
919
|
-
var requestId =
|
|
920
|
-
var AttachmentPutSchema =
|
|
921
|
-
var AttachmentGetSchema =
|
|
922
|
-
var AttachmentStoredSchema =
|
|
923
|
-
var AttachmentDataSchema =
|
|
924
|
-
var AttachmentErrorSchema =
|
|
653
|
+
data: z8.string().min(1).max(b64Max(ATTACHMENT_LOCAL_MAX)),
|
|
654
|
+
preview: z8.object({ mime: ImageMimeSchema, data: z8.string().min(1).max(b64Max(PREVIEW_MAX)) }).optional()
|
|
655
|
+
});
|
|
656
|
+
var AttachmentErrorReasonSchema = z8.enum(["too_large", "not_image", "unreadable", "not_found", "offline"]);
|
|
657
|
+
var requestId = z8.string().min(1).max(100);
|
|
658
|
+
var AttachmentPutSchema = z8.object({ type: z8.literal("attachment.put"), requestId, id: z8.string().optional(), image: ImageUploadSchema });
|
|
659
|
+
var AttachmentGetSchema = z8.object({ type: z8.literal("attachment.get"), requestId, attachment: AttachmentIdSchema, preview: z8.boolean().optional(), id: z8.string().optional(), machine: z8.string().optional() });
|
|
660
|
+
var AttachmentStoredSchema = z8.object({ type: z8.literal("attachment.stored"), requestId, ref: AttachmentRefSchema });
|
|
661
|
+
var AttachmentDataSchema = z8.object({ type: z8.literal("attachment.data"), requestId, attachment: AttachmentIdSchema, mime: ImageMimeSchema, data: z8.string() });
|
|
662
|
+
var AttachmentErrorSchema = z8.object({ type: z8.literal("attachment.error"), requestId, reason: AttachmentErrorReasonSchema });
|
|
925
663
|
|
|
926
664
|
// ../protocol/src/chat.ts
|
|
927
|
-
var ChatStepSchema =
|
|
928
|
-
kind:
|
|
929
|
-
id:
|
|
930
|
-
name:
|
|
931
|
-
path:
|
|
932
|
-
});
|
|
933
|
-
var ChatProposalSchema =
|
|
934
|
-
cwd:
|
|
935
|
-
project:
|
|
936
|
-
prompt:
|
|
665
|
+
var ChatStepSchema = z9.object({
|
|
666
|
+
kind: z9.enum(["sessions", "pane", "reply", "git", "repo"]),
|
|
667
|
+
id: z9.string().optional(),
|
|
668
|
+
name: z9.string().optional(),
|
|
669
|
+
path: z9.string().optional()
|
|
670
|
+
});
|
|
671
|
+
var ChatProposalSchema = z9.object({
|
|
672
|
+
cwd: z9.string().min(1),
|
|
673
|
+
project: z9.string(),
|
|
674
|
+
prompt: z9.string().optional(),
|
|
937
675
|
tool: ToolSchema,
|
|
938
676
|
/** What Ledge proposed on the card's picker (spec 2026-10-26 action registry §4), the agent's own default when left out. */
|
|
939
677
|
model: ModelIdSchema.optional(),
|
|
940
678
|
effort: EffortSchema.optional(),
|
|
941
|
-
launchedId:
|
|
942
|
-
error:
|
|
679
|
+
launchedId: z9.string().optional(),
|
|
680
|
+
error: z9.string().optional()
|
|
943
681
|
});
|
|
944
682
|
var MAX_PROPOSALS = 8;
|
|
945
|
-
var ChatProposalsSchema =
|
|
946
|
-
var ChatRelaySchema =
|
|
947
|
-
id:
|
|
948
|
-
name:
|
|
683
|
+
var ChatProposalsSchema = z9.array(ChatProposalSchema).min(1).max(MAX_PROPOSALS);
|
|
684
|
+
var ChatRelaySchema = z9.object({
|
|
685
|
+
id: z9.string().min(1),
|
|
686
|
+
name: z9.string(),
|
|
949
687
|
tool: ToolSchema,
|
|
950
|
-
text:
|
|
951
|
-
status:
|
|
952
|
-
sentAt:
|
|
953
|
-
error:
|
|
954
|
-
});
|
|
955
|
-
var ChatCloseSchema =
|
|
956
|
-
id:
|
|
957
|
-
name:
|
|
688
|
+
text: z9.string().min(1),
|
|
689
|
+
status: z9.enum(["queued", "sent"]).optional(),
|
|
690
|
+
sentAt: z9.string().datetime().optional(),
|
|
691
|
+
error: z9.string().optional()
|
|
692
|
+
});
|
|
693
|
+
var ChatCloseSchema = z9.object({
|
|
694
|
+
id: z9.string().min(1),
|
|
695
|
+
name: z9.string(),
|
|
958
696
|
tool: ToolSchema,
|
|
959
|
-
closedAt:
|
|
960
|
-
how:
|
|
961
|
-
error:
|
|
962
|
-
});
|
|
963
|
-
var ChatClosesSchema =
|
|
964
|
-
var ChatProjectProposalSchema =
|
|
965
|
-
name:
|
|
966
|
-
description:
|
|
967
|
-
repos:
|
|
968
|
-
addTo:
|
|
969
|
-
});
|
|
970
|
-
var
|
|
971
|
-
role: z13.string().min(1),
|
|
972
|
-
label: z13.string(),
|
|
973
|
-
tool: ToolSchema,
|
|
974
|
-
model: z13.string().optional(),
|
|
975
|
-
effort: z13.string().optional(),
|
|
976
|
-
autonomy: AutonomySchema.optional(),
|
|
977
|
-
name: z13.string().optional()
|
|
978
|
-
});
|
|
979
|
-
var ChatTeamProposalSchema = z13.object({
|
|
980
|
-
cwd: z13.string().min(1),
|
|
981
|
-
project: z13.string(),
|
|
982
|
-
projectName: z13.string().optional(),
|
|
983
|
-
name: z13.string().min(1),
|
|
984
|
-
template: z13.string().optional(),
|
|
985
|
-
seats: z13.array(ChatTeamSeatSchema).min(1).max(8),
|
|
986
|
-
teamId: z13.string().optional(),
|
|
987
|
-
launchedAt: z13.string().datetime().optional(),
|
|
988
|
-
error: z13.string().optional(),
|
|
989
|
-
savedPath: z13.string().optional()
|
|
990
|
-
});
|
|
991
|
-
var ChatTaskProposalSchema = z13.object({
|
|
992
|
-
to: z13.string().min(1),
|
|
993
|
-
toName: z13.string().optional(),
|
|
994
|
-
team: z13.string().optional(),
|
|
995
|
-
title: z13.string().min(1).max(200),
|
|
996
|
-
text: z13.string().max(2e4).optional(),
|
|
997
|
-
taskId: z13.string().optional(),
|
|
998
|
-
ownerName: z13.string().optional(),
|
|
999
|
-
state: z13.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
|
|
1000
|
-
givenAt: z13.string().datetime().optional(),
|
|
1001
|
-
error: z13.string().optional()
|
|
1002
|
-
});
|
|
1003
|
-
var ChatTasksSchema = z13.array(ChatTaskProposalSchema).min(1).max(MAX_PROPOSALS);
|
|
1004
|
-
var ActionVarsSchema = z13.record(z13.string().max(40), z13.string().max(300));
|
|
697
|
+
closedAt: z9.string().datetime().optional(),
|
|
698
|
+
how: z9.enum(["exit", "killed"]).optional(),
|
|
699
|
+
error: z9.string().optional()
|
|
700
|
+
});
|
|
701
|
+
var ChatClosesSchema = z9.array(ChatCloseSchema).min(1).max(MAX_PROPOSALS);
|
|
702
|
+
var ChatProjectProposalSchema = z9.object({
|
|
703
|
+
name: z9.string().min(1).max(80),
|
|
704
|
+
description: z9.string().max(500).optional(),
|
|
705
|
+
repos: z9.array(z9.object({ path: z9.string().min(1).max(4096), why: z9.string().min(1).max(200) })).min(1).max(50),
|
|
706
|
+
addTo: z9.string().min(1).max(80).optional()
|
|
707
|
+
});
|
|
708
|
+
var ActionVarsSchema = z9.record(z9.string().max(40), z9.string().max(300));
|
|
1005
709
|
var DETAIL_MAX = 4e3;
|
|
1006
710
|
var ACTION_ERROR_CODES = [
|
|
1007
711
|
"unknown",
|
|
@@ -1016,98 +720,381 @@ var ACTION_ERROR_CODES = [
|
|
|
1016
720
|
"no_request",
|
|
1017
721
|
"in_session",
|
|
1018
722
|
"no_note",
|
|
1019
|
-
"member",
|
|
1020
|
-
"not_member",
|
|
1021
|
-
"unknown_role",
|
|
1022
|
-
"not_resting",
|
|
1023
|
-
"dissolved",
|
|
1024
|
-
"no_seat",
|
|
1025
|
-
"closed",
|
|
1026
|
-
"holder",
|
|
1027
723
|
"changed",
|
|
1028
724
|
"cut",
|
|
1029
725
|
"failed"
|
|
1030
726
|
];
|
|
1031
|
-
var ChatActionStateSchema =
|
|
1032
|
-
var ChatActionSchema =
|
|
1033
|
-
action:
|
|
1034
|
-
args:
|
|
1035
|
-
summary:
|
|
727
|
+
var ChatActionStateSchema = z9.enum(["running", "done", "denied", "failed", "refused"]);
|
|
728
|
+
var ChatActionSchema = z9.object({
|
|
729
|
+
action: z9.string().min(1).max(40),
|
|
730
|
+
args: z9.record(z9.unknown()),
|
|
731
|
+
summary: z9.string().min(1).max(300),
|
|
1036
732
|
vars: ActionVarsSchema.optional(),
|
|
1037
|
-
detail:
|
|
1038
|
-
detailCut:
|
|
733
|
+
detail: z9.string().max(DETAIL_MAX).optional(),
|
|
734
|
+
detailCut: z9.literal(true).optional(),
|
|
1039
735
|
risk: ActionRiskSchema,
|
|
1040
|
-
session:
|
|
1041
|
-
team: z13.string().optional(),
|
|
736
|
+
session: z9.string().optional(),
|
|
1042
737
|
state: ChatActionStateSchema.optional(),
|
|
1043
|
-
doneAt:
|
|
1044
|
-
error:
|
|
1045
|
-
code:
|
|
1046
|
-
});
|
|
1047
|
-
var ChatActionsSchema =
|
|
1048
|
-
var
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
738
|
+
doneAt: z9.string().datetime().optional(),
|
|
739
|
+
error: z9.string().optional(),
|
|
740
|
+
code: z9.string().max(40).optional()
|
|
741
|
+
});
|
|
742
|
+
var ChatActionsSchema = z9.array(ChatActionSchema).min(1).max(MAX_PROPOSALS);
|
|
743
|
+
var ChatRigLaunchSchema = z9.object({
|
|
744
|
+
spec: z9.string().min(1).max(200).optional(),
|
|
745
|
+
goal: z9.string().min(1).max(8e3),
|
|
746
|
+
project: z9.string().min(1).optional(),
|
|
747
|
+
off: z9.string().min(1).max(40).optional(),
|
|
748
|
+
machine: z9.string().min(1).optional()
|
|
749
|
+
});
|
|
750
|
+
var ChatTurnSchema = z9.object({
|
|
751
|
+
machineId: z9.string().optional(),
|
|
752
|
+
machineName: z9.string().optional(),
|
|
753
|
+
choices: z9.array(z9.object({ id: z9.string(), label: z9.string() })).optional(),
|
|
754
|
+
qid: z9.string().min(1),
|
|
755
|
+
question: z9.string(),
|
|
756
|
+
askedAt: z9.string().datetime(),
|
|
757
|
+
steps: z9.array(ChatStepSchema),
|
|
758
|
+
answer: z9.string().optional(),
|
|
759
|
+
answeredAt: z9.string().datetime().optional(),
|
|
760
|
+
error: z9.string().optional(),
|
|
1059
761
|
proposal: ChatProposalSchema.optional(),
|
|
1060
762
|
proposals: ChatProposalsSchema.optional(),
|
|
1061
763
|
relay: ChatRelaySchema.optional(),
|
|
1062
764
|
closes: ChatClosesSchema.optional(),
|
|
1063
765
|
projectProposal: ChatProjectProposalSchema.optional(),
|
|
1064
|
-
team: ChatTeamProposalSchema.optional(),
|
|
1065
|
-
tasks: ChatTasksSchema.optional(),
|
|
1066
766
|
actions: ChatActionsSchema.optional(),
|
|
1067
|
-
|
|
1068
|
-
|
|
767
|
+
rigLaunch: ChatRigLaunchSchema.optional(),
|
|
768
|
+
ignoredAt: z9.string().datetime().optional(),
|
|
769
|
+
attachments: z9.array(AttachmentRefSchema).optional()
|
|
1069
770
|
});
|
|
1070
771
|
var proposalRows = (t) => t?.proposals ?? (t?.proposal ? [t.proposal] : []);
|
|
1071
772
|
|
|
773
|
+
// ../protocol/src/openrig.ts
|
|
774
|
+
var MandateCategorySchema = z10.enum(["git", "tests", "context", "code", "design", "architecture", "critical"]);
|
|
775
|
+
var ALWAYS_USER = ["design", "architecture", "critical"];
|
|
776
|
+
var MANDATE_CATEGORY_LABEL = {
|
|
777
|
+
git: { fr: "Git et branches", en: "Git and branches" },
|
|
778
|
+
tests: { fr: "Tests et relectures", en: "Tests and reviews" },
|
|
779
|
+
context: { fr: "Contexte et rel\xE8ves", en: "Context and handovers" },
|
|
780
|
+
code: { fr: "Petits choix de code", en: "Small code choices" },
|
|
781
|
+
design: { fr: "Design", en: "Design" },
|
|
782
|
+
architecture: { fr: "Architecture", en: "Architecture" },
|
|
783
|
+
critical: { fr: "Critique", en: "Critical" }
|
|
784
|
+
};
|
|
785
|
+
var OpenRigStateSchema = z10.enum(["absent", "stopped", "starting", "running", "unsupported", "error", "unreachable"]);
|
|
786
|
+
var OpenRigStatusSchema = z10.object({
|
|
787
|
+
machine: z10.string(),
|
|
788
|
+
state: OpenRigStateSchema,
|
|
789
|
+
version: z10.string().optional(),
|
|
790
|
+
human: z10.enum(["registered", "missing", "failed"]).optional(),
|
|
791
|
+
detail: z10.string().optional(),
|
|
792
|
+
install: z10.string().optional()
|
|
793
|
+
});
|
|
794
|
+
var SeatStateSchema = z10.enum(["starting", "trust", "attention", "ready", "working", "idle", "stopped", "crashed", "unreachable"]);
|
|
795
|
+
var RigSeatSchema = z10.object({
|
|
796
|
+
ref: z10.string(),
|
|
797
|
+
// OpenRig session name: "dev-build@starter"
|
|
798
|
+
address: z10.string(),
|
|
799
|
+
// "@build"
|
|
800
|
+
name: z10.string(),
|
|
801
|
+
runtime: z10.string(),
|
|
802
|
+
model: z10.string().optional(),
|
|
803
|
+
state: SeatStateSchema,
|
|
804
|
+
contextPct: z10.number().min(0).max(100).optional(),
|
|
805
|
+
sessionId: z10.string().optional()
|
|
806
|
+
});
|
|
807
|
+
var RigRepoSchema = z10.object({
|
|
808
|
+
path: z10.string(),
|
|
809
|
+
name: z10.string(),
|
|
810
|
+
branch: z10.string().optional(),
|
|
811
|
+
remote: z10.string().optional(),
|
|
812
|
+
modified: z10.number().int().min(0),
|
|
813
|
+
by: z10.string().optional(),
|
|
814
|
+
how: z10.enum(["cloned", "opened", "created"]).optional()
|
|
815
|
+
});
|
|
816
|
+
var RigLifecycleSchema = z10.enum(["launching", "running", "partial", "stopped", "unreachable"]);
|
|
817
|
+
var RIG_NAME = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
818
|
+
var RigLaunchInfoSchema = z10.object({
|
|
819
|
+
planId: z10.string(),
|
|
820
|
+
goal: z10.string(),
|
|
821
|
+
lead: z10.string(),
|
|
822
|
+
startedAt: z10.string().datetime(),
|
|
823
|
+
goalSentAt: z10.string().datetime().optional(),
|
|
824
|
+
lateNotice: z10.boolean().optional()
|
|
825
|
+
});
|
|
826
|
+
var RigSchema = z10.object({
|
|
827
|
+
id: z10.string(),
|
|
828
|
+
name: z10.string(),
|
|
829
|
+
machine: z10.string(),
|
|
830
|
+
spec: z10.string().optional(),
|
|
831
|
+
cwd: z10.string(),
|
|
832
|
+
projectId: z10.string().nullable(),
|
|
833
|
+
offProject: z10.boolean(),
|
|
834
|
+
branch: z10.string().optional(),
|
|
835
|
+
lifecycle: RigLifecycleSchema,
|
|
836
|
+
kernel: z10.boolean(),
|
|
837
|
+
seats: z10.array(RigSeatSchema),
|
|
838
|
+
repos: z10.array(RigRepoSchema),
|
|
839
|
+
launch: RigLaunchInfoSchema.optional()
|
|
840
|
+
});
|
|
841
|
+
var EscalationQuestionSchema = z10.object({
|
|
842
|
+
id: z10.string(),
|
|
843
|
+
question: z10.string(),
|
|
844
|
+
options: z10.array(z10.object({ id: z10.string(), label: z10.string(), recommended: z10.boolean().optional() })).min(2).max(4)
|
|
845
|
+
});
|
|
846
|
+
var EscalationStateSchema = z10.enum(["pending", "grace", "answered", "closed"]);
|
|
847
|
+
var EscalationSchema = z10.object({
|
|
848
|
+
id: z10.string(),
|
|
849
|
+
// OpenRig qitemId, or "ask-<id>" for `ledge ask`
|
|
850
|
+
source: z10.enum(["rig", "operator", "ask"]),
|
|
851
|
+
machine: z10.string(),
|
|
852
|
+
rigId: z10.string().optional(),
|
|
853
|
+
from: z10.string(),
|
|
854
|
+
// seat ref, or the Ledge session name for `ask`
|
|
855
|
+
intent: z10.enum(["decision", "update"]),
|
|
856
|
+
summary: z10.string(),
|
|
857
|
+
detail: z10.string().optional(),
|
|
858
|
+
evidenceRef: z10.string().optional(),
|
|
859
|
+
questions: z10.array(EscalationQuestionSchema).max(4),
|
|
860
|
+
replyTo: z10.string().optional(),
|
|
861
|
+
createdAt: z10.string().datetime(),
|
|
862
|
+
state: EscalationStateSchema,
|
|
863
|
+
graceUntil: z10.string().datetime().optional(),
|
|
864
|
+
correcting: z10.string().optional(),
|
|
865
|
+
// id of the decision being corrected (Revenir dessus)
|
|
866
|
+
category: MandateCategorySchema.optional(),
|
|
867
|
+
advice: z10.string().optional(),
|
|
868
|
+
answer: z10.object({
|
|
869
|
+
by: z10.enum(["ledge", "user"]),
|
|
870
|
+
answers: z10.record(z10.string()),
|
|
871
|
+
at: z10.string().datetime(),
|
|
872
|
+
rule: z10.string().optional(),
|
|
873
|
+
delivery: z10.enum(["queued", "sent"]).optional()
|
|
874
|
+
}).optional()
|
|
875
|
+
});
|
|
876
|
+
var ExchangeSchema = z10.object({
|
|
877
|
+
id: z10.string(),
|
|
878
|
+
machine: z10.string(),
|
|
879
|
+
rigId: z10.string(),
|
|
880
|
+
from: z10.string(),
|
|
881
|
+
to: z10.string(),
|
|
882
|
+
kind: z10.enum(["message", "task", "handoff", "ledge"]),
|
|
883
|
+
text: z10.string(),
|
|
884
|
+
at: z10.string().datetime()
|
|
885
|
+
});
|
|
886
|
+
var EXCHANGES_MAX = 200;
|
|
887
|
+
var EXCHANGE_GROUP_MS = 6e5;
|
|
888
|
+
var THREAD_PAGE = 50;
|
|
889
|
+
var LedgeDecisionSchema = z10.object({
|
|
890
|
+
id: z10.string(),
|
|
891
|
+
escalationId: z10.string(),
|
|
892
|
+
by: z10.enum(["ledge", "user"]),
|
|
893
|
+
category: MandateCategorySchema.optional(),
|
|
894
|
+
// absent: a user answer on an uncategorised escalation (never guessed)
|
|
895
|
+
summary: z10.string(),
|
|
896
|
+
answers: z10.record(z10.string()),
|
|
897
|
+
rule: z10.string().optional(),
|
|
898
|
+
from: z10.string().optional(),
|
|
899
|
+
rigId: z10.string().optional(),
|
|
900
|
+
// the seat and rig the decision was for
|
|
901
|
+
why: z10.string().optional(),
|
|
902
|
+
at: z10.string().datetime(),
|
|
903
|
+
revisitedBy: z10.string().optional()
|
|
904
|
+
});
|
|
905
|
+
var LedgeRuleSchema = z10.object({
|
|
906
|
+
id: z10.string(),
|
|
907
|
+
text: z10.string(),
|
|
908
|
+
category: MandateCategorySchema,
|
|
909
|
+
since: z10.string(),
|
|
910
|
+
from: z10.string(),
|
|
911
|
+
appliedCount: z10.number().int().min(0),
|
|
912
|
+
status: z10.enum(["active", "proposed"])
|
|
913
|
+
});
|
|
914
|
+
var LedgeThreadItemSchema = z10.discriminatedUnion("kind", [
|
|
915
|
+
z10.object({
|
|
916
|
+
kind: z10.literal("user"),
|
|
917
|
+
id: z10.string(),
|
|
918
|
+
at: z10.string().datetime(),
|
|
919
|
+
text: z10.string(),
|
|
920
|
+
to: z10.string().optional(),
|
|
921
|
+
// to = "@build"
|
|
922
|
+
qid: z10.string().optional()
|
|
923
|
+
}),
|
|
924
|
+
z10.object({
|
|
925
|
+
kind: z10.literal("ledge"),
|
|
926
|
+
id: z10.string(),
|
|
927
|
+
at: z10.string().datetime(),
|
|
928
|
+
text: z10.string(),
|
|
929
|
+
qid: z10.string().optional(),
|
|
930
|
+
pending: z10.literal(true).optional()
|
|
931
|
+
}),
|
|
932
|
+
z10.object({ kind: z10.literal("escalation"), id: z10.string(), at: z10.string().datetime(), escalationId: z10.string() }),
|
|
933
|
+
z10.object({ kind: z10.literal("decided"), id: z10.string(), at: z10.string().datetime(), decisionId: z10.string() }),
|
|
934
|
+
z10.object({
|
|
935
|
+
kind: z10.literal("exchanges"),
|
|
936
|
+
id: z10.string(),
|
|
937
|
+
at: z10.string().datetime(),
|
|
938
|
+
machine: z10.string(),
|
|
939
|
+
rigId: z10.string(),
|
|
940
|
+
from: z10.string().datetime(),
|
|
941
|
+
to: z10.string().datetime(),
|
|
942
|
+
exchangeIds: z10.array(z10.string())
|
|
943
|
+
}),
|
|
944
|
+
z10.object({ kind: z10.literal("card"), id: z10.string(), at: z10.string().datetime(), action: ChatActionSchema })
|
|
945
|
+
// existing action card
|
|
946
|
+
]);
|
|
947
|
+
var BrainStatusSchema = z10.object({
|
|
948
|
+
state: z10.enum(["off", "starting", "on", "down"]),
|
|
949
|
+
tool: ToolSchema,
|
|
950
|
+
contextPct: z10.number().optional(),
|
|
951
|
+
sessionId: z10.string().optional(),
|
|
952
|
+
enabled: z10.boolean().nullable().optional(),
|
|
953
|
+
// null: the user was never asked
|
|
954
|
+
restarts: z10.number().int().min(0).optional(),
|
|
955
|
+
reason: z10.string().optional()
|
|
956
|
+
});
|
|
957
|
+
var RigLaunchWhereSchema = z10.discriminatedUnion("kind", [
|
|
958
|
+
z10.object({ kind: z10.literal("project"), projectId: z10.string(), repo: z10.string(), branch: z10.string(), copy: z10.boolean() }),
|
|
959
|
+
z10.object({ kind: z10.literal("off-project"), name: z10.string() })
|
|
960
|
+
]);
|
|
961
|
+
var RigPlanSchema = z10.object({
|
|
962
|
+
id: z10.string(),
|
|
963
|
+
machine: z10.string(),
|
|
964
|
+
spec: z10.string(),
|
|
965
|
+
goal: z10.string(),
|
|
966
|
+
where: RigLaunchWhereSchema,
|
|
967
|
+
cwd: z10.string(),
|
|
968
|
+
seats: z10.array(z10.object({ address: z10.string(), runtime: z10.string(), model: z10.string().optional(), autonomy: z10.string() })),
|
|
969
|
+
writes: z10.array(z10.string()),
|
|
970
|
+
command: z10.string(),
|
|
971
|
+
required: z10.boolean()
|
|
972
|
+
});
|
|
973
|
+
var RigVerbSchema = z10.enum(["up", "down", "restore", "restart", "stop", "handover"]);
|
|
974
|
+
var RIG_RESULT_CODES = ["rig_managed", "already_answered", "openrig_down", "unsupported", "mandate", "unreachable", "failed", "invalid"];
|
|
975
|
+
var RigResultCodeSchema = z10.enum(RIG_RESULT_CODES);
|
|
976
|
+
var BRAIN_OPS = ["say", "decide", "ask", "rig", "send", "propose", "remember", "who", "rigs", "escalations", "rules", "handover"];
|
|
977
|
+
var BRAIN_RESULT_CODES = ["not_brain", "identity", "mandate", "grace", "invalid", "refused", "already_answered", "failed", "unreachable", "openrig_down"];
|
|
978
|
+
var BrainResultCodeSchema = z10.enum(BRAIN_RESULT_CODES);
|
|
979
|
+
var BRAIN_STOPPED_TEXT = "Ledge est arr\xEAt\xE9 \xB7 D\xE9marrer";
|
|
980
|
+
var OpenRigStatusMessageSchema = z10.object({ type: z10.literal("openrig.status"), status: OpenRigStatusSchema });
|
|
981
|
+
var RigSnapshotSchema = z10.object({ type: z10.literal("rig.snapshot"), machine: z10.string(), rigs: z10.array(RigSchema) });
|
|
982
|
+
var RigUpdatedSchema = z10.object({ type: z10.literal("rig.updated"), rig: RigSchema });
|
|
983
|
+
var RigRemovedSchema = z10.object({ type: z10.literal("rig.removed"), machine: z10.string(), rigId: z10.string() });
|
|
984
|
+
var RigExchangeSchema = z10.object({ type: z10.literal("rig.exchange"), exchange: ExchangeSchema });
|
|
985
|
+
var RigExchangesSchema = z10.object({ type: z10.literal("rig.exchanges"), machine: z10.string(), rigId: z10.string(), items: z10.array(ExchangeSchema) });
|
|
986
|
+
var EscalationUpdatedSchema = z10.object({ type: z10.literal("escalation.updated"), escalation: EscalationSchema });
|
|
987
|
+
var EscalationRemovedSchema = z10.object({ type: z10.literal("escalation.removed"), id: z10.string() });
|
|
988
|
+
var LedgeThreadItemMessageSchema = z10.object({ type: z10.literal("ledge.thread.item"), item: LedgeThreadItemSchema });
|
|
989
|
+
var LedgeThreadHistorySchema = z10.object({ type: z10.literal("ledge.thread.history"), items: z10.array(LedgeThreadItemSchema), before: z10.string().optional() });
|
|
990
|
+
var DecisionsMessageSchema = z10.object({ type: z10.literal("decisions"), items: z10.array(LedgeDecisionSchema) });
|
|
991
|
+
var RulesMessageSchema = z10.object({ type: z10.literal("rules"), items: z10.array(LedgeRuleSchema) });
|
|
992
|
+
var MandateMessageSchema = z10.object({ type: z10.literal("mandate"), text: z10.string(), path: z10.string() });
|
|
993
|
+
var BrainStatusMessageSchema = z10.object({ type: z10.literal("brain.status"), status: BrainStatusSchema });
|
|
994
|
+
var RigPlanMessageSchema = z10.object({ type: z10.literal("rig.plan"), rid: z10.string(), plan: RigPlanSchema });
|
|
995
|
+
var BrainResultSchema = z10.object({ type: z10.literal("brain.result"), rid: z10.string(), ok: z10.boolean(), line: z10.string(), code: BrainResultCodeSchema.optional() });
|
|
996
|
+
var BrainDelegateClaimSchema = z10.object({ type: z10.literal("brain.delegate.claim"), rid: z10.string().min(1).max(100), id: z10.string().min(1).max(200) });
|
|
997
|
+
var BrainDelegateDecideSchema = z10.object({ type: z10.literal("brain.delegate.decide"), rid: z10.string().min(1).max(100), id: z10.string().min(1).max(200), token: z10.string().min(1).max(100), answers: z10.record(z10.string().max(8e3)), category: MandateCategorySchema });
|
|
998
|
+
var BrainDelegateQuerySchema = z10.object({ type: z10.literal("brain.delegate.query"), rid: z10.string().min(1).max(100), operation: z10.string().min(1).max(100), token: z10.string().min(1).max(100) });
|
|
999
|
+
var BrainDelegateReleaseSchema = z10.object({ type: z10.literal("brain.delegate.release"), rid: z10.string().min(1).max(100), id: z10.string().min(1).max(200), token: z10.string().min(1).max(100), advice: z10.string().max(8e3).optional(), category: MandateCategorySchema.optional() });
|
|
1000
|
+
var BrainDelegateResultSchema = z10.object({ type: z10.literal("brain.delegate.result"), rid: z10.string().min(1).max(100), ok: z10.boolean(), token: z10.string().optional(), graceUntil: z10.string().datetime().optional(), status: z10.enum(["applied"]).optional(), code: z10.string().optional(), reason: z10.string().optional() });
|
|
1001
|
+
var RigResultSchema = z10.object({ type: z10.literal("rig.result"), rid: z10.string(), ok: z10.boolean(), confirmed: z10.literal(true).optional(), code: RigResultCodeSchema.optional(), reason: z10.string().optional() });
|
|
1002
|
+
var RigActSchema = z10.object({ type: z10.literal("rig.act"), rid: z10.string(), machine: z10.string(), rigId: z10.string().optional(), seatRef: z10.string().optional(), verb: RigVerbSchema });
|
|
1003
|
+
var RigPrepareSchema = z10.object({ type: z10.literal("rig.prepare"), rid: z10.string(), machine: z10.string(), spec: z10.string(), goal: z10.string().max(8e3), where: RigLaunchWhereSchema });
|
|
1004
|
+
var RigLaunchSchema = z10.object({ type: z10.literal("rig.launch"), rid: z10.string(), planId: z10.string() });
|
|
1005
|
+
var RigAdoptSchema = z10.object({ type: z10.literal("rig.adopt"), rid: z10.string(), machine: z10.string(), rigId: z10.string(), projectId: z10.string() });
|
|
1006
|
+
var RigSendSchema = z10.object({ type: z10.literal("rig.send"), rid: z10.string(), machine: z10.string(), seatRef: z10.string(), text: z10.string() });
|
|
1007
|
+
var RigSpecSeatSchema = z10.object({
|
|
1008
|
+
address: z10.string(),
|
|
1009
|
+
member: z10.string(),
|
|
1010
|
+
pod: z10.string(),
|
|
1011
|
+
runtime: z10.string(),
|
|
1012
|
+
model: z10.string().optional(),
|
|
1013
|
+
effort: z10.string().optional(),
|
|
1014
|
+
profile: z10.string().optional()
|
|
1015
|
+
});
|
|
1016
|
+
var RigSpecInfoSchema = z10.object({
|
|
1017
|
+
id: z10.string(),
|
|
1018
|
+
name: z10.string(),
|
|
1019
|
+
summary: z10.string().optional(),
|
|
1020
|
+
source: z10.enum(["builtin", "library", "registry"]),
|
|
1021
|
+
path: z10.string().optional(),
|
|
1022
|
+
seats: z10.array(RigSpecSeatSchema)
|
|
1023
|
+
});
|
|
1024
|
+
var RigSpecsRequestSchema = z10.object({ type: z10.literal("rig.specs.request"), requestId: z10.string(), machineId: z10.string().optional() });
|
|
1025
|
+
var RigSpecsSchema = z10.object({
|
|
1026
|
+
type: z10.literal("rig.specs"),
|
|
1027
|
+
requestId: z10.string(),
|
|
1028
|
+
machine: z10.string(),
|
|
1029
|
+
specs: z10.array(RigSpecInfoSchema),
|
|
1030
|
+
error: z10.enum(["openrig_down", "unsupported", "failed"]).optional()
|
|
1031
|
+
});
|
|
1032
|
+
var RigExchangesRequestSchema = z10.object({ type: z10.literal("rig.exchanges.request"), machine: z10.string(), rigId: z10.string() });
|
|
1033
|
+
var EscalationAnswerSchema = z10.object({ type: z10.literal("escalation.answer"), rid: z10.string(), id: z10.string(), answers: z10.record(z10.string()), by: z10.literal("user") });
|
|
1034
|
+
var LedgeSaySchema = z10.object({ type: z10.literal("ledge.say"), rid: z10.string(), text: z10.string(), about: z10.string().optional() });
|
|
1035
|
+
var LedgeThreadRequestSchema = z10.object({ type: z10.literal("ledge.thread.request"), before: z10.string().optional() });
|
|
1036
|
+
var MandateUpdateSchema = z10.object({ type: z10.literal("mandate.update"), rid: z10.string(), text: z10.string() });
|
|
1037
|
+
var RuleSetSchema = z10.object({ type: z10.literal("rule.set"), rid: z10.string(), id: z10.string(), status: z10.enum(["active", "rejected"]) });
|
|
1038
|
+
var DecisionRevisitSchema = z10.object({ type: z10.literal("decision.revisit"), rid: z10.string(), id: z10.string(), text: z10.string().optional() });
|
|
1039
|
+
var CategoryEscalateSchema = z10.object({ type: z10.literal("category.escalate"), rid: z10.string(), category: MandateCategorySchema });
|
|
1040
|
+
var BrainSetSchema = z10.object({
|
|
1041
|
+
type: z10.literal("brain.set"),
|
|
1042
|
+
rid: z10.string(),
|
|
1043
|
+
enabled: z10.boolean(),
|
|
1044
|
+
tool: ToolSchema.optional(),
|
|
1045
|
+
first: z10.literal(true).optional()
|
|
1046
|
+
});
|
|
1047
|
+
var BrainCmdSchema = z10.object({
|
|
1048
|
+
type: z10.literal("brain.cmd"),
|
|
1049
|
+
rid: z10.string(),
|
|
1050
|
+
by: z10.string(),
|
|
1051
|
+
key: z10.string(),
|
|
1052
|
+
op: z10.enum(BRAIN_OPS),
|
|
1053
|
+
args: z10.array(z10.string()).max(40)
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
// ../protocol/src/messages.ts
|
|
1057
|
+
import { z as z21 } from "zod";
|
|
1058
|
+
|
|
1072
1059
|
// ../protocol/src/repos.ts
|
|
1073
|
-
import { z as
|
|
1074
|
-
var ReposInspectSchema =
|
|
1075
|
-
type:
|
|
1076
|
-
requestId:
|
|
1077
|
-
paths:
|
|
1078
|
-
machineId:
|
|
1079
|
-
});
|
|
1080
|
-
var RepoInfoSchema =
|
|
1081
|
-
var ReposInfoSchema =
|
|
1060
|
+
import { z as z11 } from "zod";
|
|
1061
|
+
var ReposInspectSchema = z11.object({
|
|
1062
|
+
type: z11.literal("repos.inspect"),
|
|
1063
|
+
requestId: z11.string().min(1).max(100),
|
|
1064
|
+
paths: z11.array(z11.string().min(1)).min(1).max(50),
|
|
1065
|
+
machineId: z11.string().optional()
|
|
1066
|
+
});
|
|
1067
|
+
var RepoInfoSchema = z11.object({ path: z11.string(), exists: z11.boolean(), branch: z11.string().nullable() });
|
|
1068
|
+
var ReposInfoSchema = z11.object({ type: z11.literal("repos.info"), requestId: z11.string(), repos: z11.array(RepoInfoSchema) });
|
|
1082
1069
|
|
|
1083
1070
|
// ../protocol/src/scan.ts
|
|
1084
|
-
import { z as
|
|
1085
|
-
var ReposScanSchema =
|
|
1086
|
-
var FoundRepoSchema =
|
|
1087
|
-
var ReposFoundSchema =
|
|
1071
|
+
import { z as z12 } from "zod";
|
|
1072
|
+
var ReposScanSchema = z12.object({ type: z12.literal("repos.scan"), requestId: z12.string().min(1).max(100), machineId: z12.string().optional() });
|
|
1073
|
+
var FoundRepoSchema = z12.object({ path: z12.string(), branch: z12.string().nullable(), remote: z12.string().optional(), manifest: z12.string().max(200).optional() });
|
|
1074
|
+
var ReposFoundSchema = z12.object({ type: z12.literal("repos.found"), requestId: z12.string(), repos: z12.array(FoundRepoSchema).max(500), truncated: z12.boolean(), error: z12.string().optional() });
|
|
1088
1075
|
|
|
1089
1076
|
// ../protocol/src/documents.ts
|
|
1090
|
-
import { z as
|
|
1077
|
+
import { z as z13 } from "zod";
|
|
1091
1078
|
var DOCUMENT_MAX_BYTES = 1048576;
|
|
1092
|
-
var path =
|
|
1093
|
-
var requestId2 =
|
|
1094
|
-
var DocumentReadSchema =
|
|
1095
|
-
type:
|
|
1079
|
+
var path = z13.string().min(1).max(4096);
|
|
1080
|
+
var requestId2 = z13.string().min(1);
|
|
1081
|
+
var DocumentReadSchema = z13.object({
|
|
1082
|
+
type: z13.literal("document.read"),
|
|
1096
1083
|
requestId: requestId2,
|
|
1097
1084
|
path,
|
|
1098
|
-
machineId:
|
|
1099
|
-
sessionId:
|
|
1085
|
+
machineId: z13.string().optional(),
|
|
1086
|
+
sessionId: z13.string().min(1).optional(),
|
|
1100
1087
|
basePath: path.optional()
|
|
1101
1088
|
});
|
|
1102
|
-
var DocumentContentSchema =
|
|
1103
|
-
type:
|
|
1089
|
+
var DocumentContentSchema = z13.object({
|
|
1090
|
+
type: z13.literal("document.content"),
|
|
1104
1091
|
requestId: requestId2,
|
|
1105
1092
|
path,
|
|
1106
|
-
text:
|
|
1107
|
-
bytes:
|
|
1108
|
-
modifiedAt:
|
|
1093
|
+
text: z13.string(),
|
|
1094
|
+
bytes: z13.number().int().nonnegative(),
|
|
1095
|
+
modifiedAt: z13.string().datetime()
|
|
1109
1096
|
});
|
|
1110
|
-
var DocumentErrorCodeSchema =
|
|
1097
|
+
var DocumentErrorCodeSchema = z13.enum([
|
|
1111
1098
|
"not_found",
|
|
1112
1099
|
"not_file",
|
|
1113
1100
|
"unsupported_path",
|
|
@@ -1119,62 +1106,155 @@ var DocumentErrorCodeSchema = z16.enum([
|
|
|
1119
1106
|
"session_missing",
|
|
1120
1107
|
"read_failed"
|
|
1121
1108
|
]);
|
|
1122
|
-
var DocumentErrorSchema =
|
|
1123
|
-
type:
|
|
1109
|
+
var DocumentErrorSchema = z13.object({
|
|
1110
|
+
type: z13.literal("document.error"),
|
|
1124
1111
|
requestId: requestId2,
|
|
1125
1112
|
path,
|
|
1126
1113
|
code: DocumentErrorCodeSchema
|
|
1127
1114
|
});
|
|
1128
1115
|
|
|
1129
1116
|
// ../protocol/src/routing.ts
|
|
1130
|
-
import { z as
|
|
1131
|
-
var MachineInventorySchema =
|
|
1132
|
-
id:
|
|
1133
|
-
label:
|
|
1134
|
-
online:
|
|
1135
|
-
local:
|
|
1136
|
-
sessions:
|
|
1137
|
-
folders:
|
|
1138
|
-
});
|
|
1139
|
-
var RoutingContextSchema =
|
|
1140
|
-
machines:
|
|
1141
|
-
history:
|
|
1142
|
-
});
|
|
1143
|
-
var RouteResultSchema =
|
|
1117
|
+
import { z as z14 } from "zod";
|
|
1118
|
+
var MachineInventorySchema = z14.object({
|
|
1119
|
+
id: z14.string().min(1).max(80),
|
|
1120
|
+
label: z14.string().min(1).max(120),
|
|
1121
|
+
online: z14.boolean(),
|
|
1122
|
+
local: z14.boolean(),
|
|
1123
|
+
sessions: z14.array(z14.object({ id: z14.string(), name: z14.string(), project: z14.string(), cwd: z14.string(), topic: z14.string().optional(), state: z14.string(), readOnly: z14.boolean() })).max(500),
|
|
1124
|
+
folders: z14.array(z14.object({ project: z14.string(), cwd: z14.string() })).max(100)
|
|
1125
|
+
});
|
|
1126
|
+
var RoutingContextSchema = z14.object({
|
|
1127
|
+
machines: z14.array(MachineInventorySchema).min(1).max(20),
|
|
1128
|
+
history: z14.array(z14.object({ question: z14.string().max(2e4), answer: z14.string().max(2e4).optional(), machineId: z14.string().optional() })).max(50)
|
|
1129
|
+
});
|
|
1130
|
+
var RouteResultSchema = z14.object({
|
|
1144
1131
|
/** Null means an answer or a clarification, never an implicit local destination. */
|
|
1145
|
-
machineId:
|
|
1146
|
-
text:
|
|
1147
|
-
choices:
|
|
1132
|
+
machineId: z14.string().nullable(),
|
|
1133
|
+
text: z14.string().min(1).max(2e4),
|
|
1134
|
+
choices: z14.array(z14.string()).max(20).optional()
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// ../protocol/src/ssh.ts
|
|
1138
|
+
import { z as z15 } from "zod";
|
|
1139
|
+
var SshAliasSchema = z15.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/);
|
|
1140
|
+
|
|
1141
|
+
// ../protocol/src/machines.ts
|
|
1142
|
+
import { z as z16 } from "zod";
|
|
1143
|
+
var MachineIdSchema = z16.string().regex(/^[a-zA-Z0-9_-]{1,64}$/);
|
|
1144
|
+
var MachineSchema = z16.object({
|
|
1145
|
+
id: MachineIdSchema,
|
|
1146
|
+
label: z16.string().trim().min(1).max(80),
|
|
1147
|
+
ssh: SshAliasSchema.optional()
|
|
1148
|
+
});
|
|
1149
|
+
var MachinesSchema = z16.array(MachineSchema).min(1).max(20).superRefine((list, ctx) => {
|
|
1150
|
+
if (list[0]?.id !== "local" || list[0].ssh !== void 0) ctx.addIssue({ code: "custom", message: "the first machine is local, without ssh", path: [0] });
|
|
1151
|
+
const ids = /* @__PURE__ */ new Set(), aliases = /* @__PURE__ */ new Set();
|
|
1152
|
+
list.forEach((m, i) => {
|
|
1153
|
+
if (i > 0 && !m.ssh) ctx.addIssue({ code: "custom", message: "a remote machine needs an ssh alias", path: [i, "ssh"] });
|
|
1154
|
+
if (ids.has(m.id)) ctx.addIssue({ code: "custom", message: `duplicate id ${m.id}`, path: [i, "id"] });
|
|
1155
|
+
ids.add(m.id);
|
|
1156
|
+
if (m.ssh) {
|
|
1157
|
+
if (aliases.has(m.ssh)) ctx.addIssue({ code: "custom", message: `duplicate ssh alias ${m.ssh}`, path: [i, "ssh"] });
|
|
1158
|
+
aliases.add(m.ssh);
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1148
1161
|
});
|
|
1149
1162
|
|
|
1150
1163
|
// ../protocol/src/island.ts
|
|
1151
|
-
import { z as
|
|
1152
|
-
var IslandScaleSchema =
|
|
1153
|
-
var IslandPrefsSchema =
|
|
1164
|
+
import { z as z17 } from "zod";
|
|
1165
|
+
var IslandScaleSchema = z17.union([z17.literal("auto"), z17.literal(1), z17.literal(1.25), z17.literal(1.5), z17.literal(2)]);
|
|
1166
|
+
var IslandPrefsSchema = z17.object({
|
|
1154
1167
|
/** Shown at the top of the screen; hidden, Desktop still notifies. */
|
|
1155
|
-
visible:
|
|
1168
|
+
visible: z17.boolean(),
|
|
1156
1169
|
/** `primary` (whichever it is after a reconfiguration), or a monitor's id as the shell lists it (`list_monitors`: the OS
|
|
1157
1170
|
* name, `#index` without one). `second`, written by older fronts, is the first monitor that is not the primary. */
|
|
1158
|
-
screen:
|
|
1159
|
-
position:
|
|
1171
|
+
screen: z17.string().trim().min(1).max(200),
|
|
1172
|
+
position: z17.enum(["center", "left", "right"]),
|
|
1160
1173
|
/** A strip under a maximised window. */
|
|
1161
|
-
discreet:
|
|
1174
|
+
discreet: z17.boolean(),
|
|
1162
1175
|
islandScale: IslandScaleSchema,
|
|
1163
1176
|
panelScale: IslandScaleSchema,
|
|
1164
1177
|
/** What the island shows besides asks and questions, which it always shows. */
|
|
1165
|
-
shows:
|
|
1178
|
+
shows: z17.object({
|
|
1166
1179
|
/** Allow, deny or answer in a line from the island; off, its cards only say « Open in Ledge ». */
|
|
1167
|
-
quick:
|
|
1180
|
+
quick: z17.boolean(),
|
|
1168
1181
|
/** The 6 s bubble when an agent finishes. */
|
|
1169
|
-
finished:
|
|
1182
|
+
finished: z17.boolean(),
|
|
1170
1183
|
/** An agent's sentence in a bubble. */
|
|
1171
|
-
say:
|
|
1184
|
+
say: z17.boolean(),
|
|
1172
1185
|
/** The sub-agents next to their main agent. */
|
|
1173
|
-
helpers:
|
|
1186
|
+
helpers: z17.boolean()
|
|
1174
1187
|
}),
|
|
1175
1188
|
/** The global shortcut that opens the island, as shown (`Ctrl Alt L`). */
|
|
1176
|
-
shortcut:
|
|
1189
|
+
shortcut: z17.string().trim().min(1).max(40)
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
// ../protocol/src/projects.ts
|
|
1193
|
+
import { z as z18 } from "zod";
|
|
1194
|
+
var PROJECT_COLORS = { blue: "#3b82f6", green: "#10b981", amber: "#f59e0b", violet: "#8b5cf6", teal: "#14b8a6", pink: "#ec4899", red: "#ef4444", slate: "#64748b" };
|
|
1195
|
+
var ProjectColorSchema = z18.enum(["blue", "green", "amber", "violet", "teal", "pink", "red", "slate"]);
|
|
1196
|
+
var ProjectIdSchema = z18.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/);
|
|
1197
|
+
function normalizePath(raw) {
|
|
1198
|
+
const p = raw.trim().replace(/\/+/g, "/");
|
|
1199
|
+
return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
1200
|
+
}
|
|
1201
|
+
var PathSchema = z18.string().max(4096).refine((p) => p.startsWith("/") && p === normalizePath(p), "an absolute, normalized path");
|
|
1202
|
+
var RepoSchema = z18.object({ machine: MachineIdSchema, path: PathSchema });
|
|
1203
|
+
var ProjectSchema = z18.object({
|
|
1204
|
+
id: ProjectIdSchema,
|
|
1205
|
+
name: z18.string().trim().min(1).max(80),
|
|
1206
|
+
color: ProjectColorSchema,
|
|
1207
|
+
description: z18.string().max(500).default(""),
|
|
1208
|
+
repos: z18.array(RepoSchema).max(50),
|
|
1209
|
+
/** Ids of the OpenRig rigs filed in this project (a rig lives in one project at most). */
|
|
1210
|
+
rigs: z18.array(z18.string().min(1).max(200)).max(200).default([])
|
|
1211
|
+
});
|
|
1212
|
+
var RuleSchema = z18.object({ machine: MachineIdSchema, path: PathSchema, project: ProjectIdSchema });
|
|
1213
|
+
function projectsProblems(p) {
|
|
1214
|
+
const out = [];
|
|
1215
|
+
const ids = /* @__PURE__ */ new Set(), names = /* @__PURE__ */ new Set(), repos = /* @__PURE__ */ new Set(), rigIds = /* @__PURE__ */ new Set(), rules = /* @__PURE__ */ new Set();
|
|
1216
|
+
p.projects.forEach((project, i) => {
|
|
1217
|
+
if (ids.has(project.id)) out.push({ code: "duplicate_id", path: ["projects", i, "id"] });
|
|
1218
|
+
ids.add(project.id);
|
|
1219
|
+
const name = project.name.trim().toLocaleLowerCase();
|
|
1220
|
+
if (names.has(name)) out.push({ code: "duplicate_name", path: ["projects", i, "name"] });
|
|
1221
|
+
names.add(name);
|
|
1222
|
+
project.repos.forEach((repo, j) => {
|
|
1223
|
+
const key2 = `${repo.machine}
|
|
1224
|
+
${repo.path}`;
|
|
1225
|
+
if (repos.has(key2)) out.push({ code: "duplicate_repo", path: ["projects", i, "repos", j] });
|
|
1226
|
+
repos.add(key2);
|
|
1227
|
+
});
|
|
1228
|
+
project.rigs.forEach((rig, j) => {
|
|
1229
|
+
if (rigIds.has(rig)) out.push({ code: "duplicate_rig", path: ["projects", i, "rigs", j] });
|
|
1230
|
+
rigIds.add(rig);
|
|
1231
|
+
});
|
|
1232
|
+
});
|
|
1233
|
+
p.rules.forEach((rule, i) => {
|
|
1234
|
+
const key2 = `${rule.machine}
|
|
1235
|
+
${rule.path}`;
|
|
1236
|
+
if (rules.has(key2)) out.push({ code: "duplicate_rule", path: ["rules", i] });
|
|
1237
|
+
rules.add(key2);
|
|
1238
|
+
if (!ids.has(rule.project)) out.push({ code: "unknown_project", path: ["rules", i, "project"] });
|
|
1239
|
+
});
|
|
1240
|
+
return out;
|
|
1241
|
+
}
|
|
1242
|
+
var ProjectsSchema = z18.object({ projects: z18.array(ProjectSchema).max(100), rules: z18.array(RuleSchema).max(1e3) }).superRefine((p, ctx) => {
|
|
1243
|
+
for (const problem of projectsProblems(p)) ctx.addIssue({ code: "custom", message: problem.code, path: problem.path });
|
|
1177
1244
|
});
|
|
1245
|
+
var DEFAULT_PROJECTS = { projects: [], rules: [] };
|
|
1246
|
+
var under = (cwd, path2) => path2 === "/" || cwd === path2 || cwd.startsWith(`${path2}/`);
|
|
1247
|
+
function projectOf(session, state) {
|
|
1248
|
+
const machine = session.machineId ?? "local";
|
|
1249
|
+
const cwd = normalizePath(session.cwd);
|
|
1250
|
+
const known2 = new Set(state.projects.map((p) => p.id));
|
|
1251
|
+
const candidates = [
|
|
1252
|
+
...state.projects.flatMap((p) => p.repos.filter((r) => r.machine === machine).map((r) => ({ project: p.id, path: r.path, rule: false }))),
|
|
1253
|
+
...state.rules.filter((r) => r.machine === machine && known2.has(r.project)).map((r) => ({ project: r.project, path: r.path, rule: true }))
|
|
1254
|
+
].filter((c) => under(cwd, c.path));
|
|
1255
|
+
candidates.sort((x, y) => y.path.length - x.path.length || Number(y.rule) - Number(x.rule));
|
|
1256
|
+
return candidates[0]?.project ?? null;
|
|
1257
|
+
}
|
|
1178
1258
|
|
|
1179
1259
|
// ../protocol/src/thread.ts
|
|
1180
1260
|
import { z as z19 } from "zod";
|
|
@@ -1203,13 +1283,6 @@ var ThreadItemSchema = z19.discriminatedUnion("kind", [
|
|
|
1203
1283
|
kind: z19.literal("notice"),
|
|
1204
1284
|
...base,
|
|
1205
1285
|
notice: z19.enum(["idle", "exited", "unavailable", "expired", "delivery", "other"]),
|
|
1206
|
-
/** An `other` notice from Ledge: `task`, the task a `<ledge-task>` is about; `team`, the team a `<ledge-notice team>` seated the
|
|
1207
|
-
* session in (roles spec §3.3). An older front, which knows neither, shows it as any other notice. */
|
|
1208
|
-
task: z19.string().optional(),
|
|
1209
|
-
team: z19.string().optional(),
|
|
1210
|
-
/** A task this session handed over (`outgoing`), and how (issue #92). */
|
|
1211
|
-
taskOp: z19.enum(TRANSMISSION_OPS).optional(),
|
|
1212
|
-
outgoing: z19.literal(true).optional(),
|
|
1213
1286
|
name: z19.string().optional(),
|
|
1214
1287
|
peer: z19.string().optional(),
|
|
1215
1288
|
time: z19.string().optional(),
|
|
@@ -1272,7 +1345,7 @@ var MobileStateSchema = z20.object({
|
|
|
1272
1345
|
|
|
1273
1346
|
// ../protocol/src/messages.ts
|
|
1274
1347
|
var PROTOCOL_PORT = 47831;
|
|
1275
|
-
var RefusalCodeSchema = z21.enum(["stale", "read_only", "no_prompt", "not_taken", "busy", "unknown_session", "nothing_to_decide", "not_offered", "free_text", "note_not_typed", "unavailable", "invalid", "attachment_missing", "attachments_not_accepted"]);
|
|
1348
|
+
var RefusalCodeSchema = z21.enum(["stale", "read_only", "no_prompt", "not_taken", "busy", "unknown_session", "nothing_to_decide", "not_offered", "free_text", "note_not_typed", "unavailable", "invalid", "attachment_missing", "attachments_not_accepted", "rig_managed"]);
|
|
1276
1349
|
var SessionEventSchema = z21.object({
|
|
1277
1350
|
name: z21.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PermissionRequest", "Notification", "Stop", "SessionEnd", "PaneChanged", "PaneGone", "PanePrompt", "PaneIdle", "InjectFailed", "Decided", "PostToolUse", "PostToolUseFailure", "StopFailure", "SubagentStart", "SubagentStop", "PaneTitle", "Renamed", "NativeRequest", "AgentMessage", "QueueUpdate"]),
|
|
1278
1351
|
at: z21.string().datetime(),
|
|
@@ -1341,8 +1414,15 @@ var DaemonInfoSchema = z21.object({
|
|
|
1341
1414
|
/** Its home folder, so a client shows the folders under it as `~` (absent from older daemons). */
|
|
1342
1415
|
home: z21.string().optional(),
|
|
1343
1416
|
mobile: z21.boolean().optional(),
|
|
1344
|
-
/** Knows
|
|
1345
|
-
|
|
1417
|
+
/** Knows `ask.create` / `ask.answer`: a session's decision for the user (`ledge ask`). */
|
|
1418
|
+
asks: z21.boolean().optional(),
|
|
1419
|
+
/** Reads OpenRig (rigs, escalations, exchanges) through its local bridge. */
|
|
1420
|
+
openrig: z21.boolean().optional(),
|
|
1421
|
+
/** Takes color-preview subscriptions. */
|
|
1422
|
+
colorPreview: z21.boolean().optional(),
|
|
1423
|
+
/** Relays rigs of remote machines. */
|
|
1424
|
+
openrigFleet: z21.boolean().optional(),
|
|
1425
|
+
brainDelegate: z21.boolean().optional()
|
|
1346
1426
|
});
|
|
1347
1427
|
var UpdateFailureSchema = z21.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
|
|
1348
1428
|
var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
@@ -1372,6 +1452,8 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1372
1452
|
}),
|
|
1373
1453
|
z21.object({ type: z21.literal("session.removed"), id: z21.string() }),
|
|
1374
1454
|
z21.object({ type: z21.literal("session.preview"), id: z21.string(), lines: z21.array(z21.string()) }),
|
|
1455
|
+
/** The session's terminal in color (SGR codes kept), sent only to the client that subscribed to it. */
|
|
1456
|
+
z21.object({ type: z21.literal("session.preview.color"), id: z21.string(), lines: z21.array(z21.string()) }),
|
|
1375
1457
|
/** A session's thread, on request (spec 2026-10-13 §5); then each created or updated item, broadcast. */
|
|
1376
1458
|
z21.object({ type: z21.literal("session.thread"), id: z21.string(), items: z21.array(ThreadItemSchema) }),
|
|
1377
1459
|
z21.object({ type: z21.literal("session.thread.item"), id: z21.string(), item: ThreadItemSchema }),
|
|
@@ -1397,7 +1479,7 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1397
1479
|
/** The chat (spec 2026-10-11 §5): what Ledge reads, its words as they come, the final text, or why it stopped; the day's turns after each snapshot. */
|
|
1398
1480
|
z21.object({ type: z21.literal("chat.step"), qid: z21.string(), step: ChatStepSchema }),
|
|
1399
1481
|
z21.object({ type: z21.literal("chat.delta"), qid: z21.string(), text: z21.string() }),
|
|
1400
|
-
z21.object({ type: z21.literal("chat.done"), qid: z21.string(), text: z21.string(), at: z21.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(),
|
|
1482
|
+
z21.object({ type: z21.literal("chat.done"), qid: z21.string(), text: z21.string(), at: z21.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(), actions: ChatActionsSchema.optional() }),
|
|
1401
1483
|
/** What a `chat.act` applied: the rows done, and the one that failed with why (the rows after it stay proposed). */
|
|
1402
1484
|
z21.object({ type: z21.literal("chat.acted"), rid: z21.string(), qid: z21.string(), done: z21.array(z21.number().int().min(0)), failed: z21.array(z21.object({
|
|
1403
1485
|
row: z21.number().int().min(0),
|
|
@@ -1407,7 +1489,7 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1407
1489
|
})) }),
|
|
1408
1490
|
/** What an `action.run` came to: `row` the action as the daemon resolved it (its summary, its risk); `reason` why it was not
|
|
1409
1491
|
* done, with `code` (`refused`: not possible now; `agent`: a session may not run it; `confirm_required`: destructive, ask
|
|
1410
|
-
* again; `failed`: the handler turned it down; a free string, as an `error`'s); `path`
|
|
1492
|
+
* again; `failed`: the handler turned it down; a free string, as an `error`'s); `path` a file the action wrote. */
|
|
1411
1493
|
z21.object({ type: z21.literal("action.result"), rid: z21.string(), action: z21.string().min(1).max(40), row: ChatActionSchema.optional(), reason: z21.string().optional(), code: z21.string().optional(), path: z21.string().optional() }),
|
|
1412
1494
|
z21.object({ type: z21.literal("chat.error"), qid: z21.string(), message: z21.string() }),
|
|
1413
1495
|
z21.object({ type: z21.literal("chat.history"), turns: z21.array(ChatTurnSchema) }),
|
|
@@ -1427,20 +1509,31 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1427
1509
|
z21.object({ type: z21.literal("message.ack"), rid: z21.string(), outcome: z21.enum(["accepted", "queued", "native", "refused"]), reason: z21.string().optional() }),
|
|
1428
1510
|
/** The answer to a `message.read`: the body with its sender, target and time, or the refusal's `reason`. */
|
|
1429
1511
|
z21.object({ type: z21.literal("message.body"), id: z21.string(), from: z21.string().optional(), to: z21.string().optional(), at: z21.string().optional(), text: z21.string().optional(), reason: z21.string().optional() }),
|
|
1430
|
-
TaskResultSchema,
|
|
1431
1512
|
SessionHandoverResultSchema,
|
|
1432
1513
|
ContextPrefsMessageSchema,
|
|
1433
1514
|
HandoverResultSchema,
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1515
|
+
AsksMessageSchema,
|
|
1516
|
+
AskUpdatedSchema,
|
|
1517
|
+
AskResultSchema,
|
|
1518
|
+
OpenRigStatusMessageSchema,
|
|
1519
|
+
RigSnapshotSchema,
|
|
1520
|
+
RigUpdatedSchema,
|
|
1521
|
+
RigRemovedSchema,
|
|
1522
|
+
RigExchangeSchema,
|
|
1523
|
+
RigExchangesSchema,
|
|
1524
|
+
RigSpecsSchema,
|
|
1525
|
+
EscalationUpdatedSchema,
|
|
1526
|
+
EscalationRemovedSchema,
|
|
1527
|
+
LedgeThreadItemMessageSchema,
|
|
1528
|
+
LedgeThreadHistorySchema,
|
|
1529
|
+
DecisionsMessageSchema,
|
|
1530
|
+
RulesMessageSchema,
|
|
1531
|
+
MandateMessageSchema,
|
|
1532
|
+
BrainStatusMessageSchema,
|
|
1533
|
+
RigPlanMessageSchema,
|
|
1534
|
+
RigResultSchema,
|
|
1535
|
+
BrainResultSchema,
|
|
1536
|
+
BrainDelegateResultSchema,
|
|
1444
1537
|
z21.object({
|
|
1445
1538
|
type: z21.literal("error"),
|
|
1446
1539
|
message: z21.string(),
|
|
@@ -1483,10 +1576,6 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1483
1576
|
effort: EffortSchema.optional(),
|
|
1484
1577
|
/** OMP only: its approval mode, `always-ask` when left out. */
|
|
1485
1578
|
approval: OmpApprovalSchema.optional(),
|
|
1486
|
-
/** A role (spec 2026-10-25 roles §3.4): its prompt reaches the agent at every start, its defaults fill what is left out. */
|
|
1487
|
-
role: RoleIdSchema.optional(),
|
|
1488
|
-
/** A live team the new session joins at once, in `role`. */
|
|
1489
|
-
team: TeamIdSchema.optional(),
|
|
1490
1579
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1491
1580
|
machineId: z21.string().optional()
|
|
1492
1581
|
}),
|
|
@@ -1574,10 +1663,20 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1574
1663
|
z21.object({ type: z21.literal("session.delete"), id: z21.string() }),
|
|
1575
1664
|
/** The user names the session's topic from the overlay (500 characters at most, the hook's prompt cap); an empty topic hands back to the automatic sources. */
|
|
1576
1665
|
z21.object({ type: z21.literal("session.rename"), id: z21.string(), topic: z21.string().max(500) }),
|
|
1666
|
+
/** The one session this client follows in color; `id: null` follows none. */
|
|
1667
|
+
z21.object({ type: z21.literal("session.preview.subscribe"), id: z21.string().nullable(), color: z21.literal(true) }),
|
|
1577
1668
|
z21.object({ type: z21.literal("session.thread"), id: z21.string() }),
|
|
1578
1669
|
z21.object({ type: z21.literal("session.register"), session: SessionSchema }),
|
|
1579
1670
|
/** `agent` and `cwd` describe the session when the daemon does not know it yet (a hook from an agent started without ledge). */
|
|
1580
|
-
z21.object({
|
|
1671
|
+
z21.object({
|
|
1672
|
+
type: z21.literal("session.event"),
|
|
1673
|
+
id: z21.string(),
|
|
1674
|
+
event: SessionEventSchema,
|
|
1675
|
+
agent: ToolSchema.optional(),
|
|
1676
|
+
cwd: z21.string().optional(),
|
|
1677
|
+
/** The OpenRig seat a hook ran in: its session name (`dev-build@starter`) and node id, what pairs the session with its seat. */
|
|
1678
|
+
openrig: z21.object({ sessionName: z21.string().min(1).max(200), nodeId: z21.string().max(200).optional() }).optional()
|
|
1679
|
+
}),
|
|
1581
1680
|
z21.object({ type: z21.literal("session.unregister"), id: z21.string(), exitCode: z21.number().int().nullable() }),
|
|
1582
1681
|
z21.object({ type: z21.literal("snapshot.request") }),
|
|
1583
1682
|
z21.object({ type: z21.literal("chat.ask"), qid: z21.string().min(1), text: z21.string(), images: z21.array(ImageUploadSchema).max(ATTACHMENTS_MAX).optional(), tool: ToolSchema.optional() }),
|
|
@@ -1607,29 +1706,33 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1607
1706
|
z21.object({ type: z21.literal("session.message"), rid: z21.string().min(1), from: z21.string().min(1), to: z21.string().min(1), text: z21.string().min(1).max(MESSAGE_MAX), summary: z21.string().max(SUMMARY_MAX).optional(), intercept: z21.boolean().optional() }),
|
|
1608
1707
|
/** `ledge read <id>`: a message's whole body, for its receiver or its sender (`by`, a session id). */
|
|
1609
1708
|
z21.object({ type: z21.literal("message.read"), id: z21.string().min(1).max(40), by: z21.string().min(1) }),
|
|
1610
|
-
|
|
1709
|
+
AskCreateSchema,
|
|
1710
|
+
AskAnswerSchema,
|
|
1611
1711
|
SessionHandoverSchema,
|
|
1612
1712
|
ContextSetSchema,
|
|
1613
1713
|
HandoverBriefSchema,
|
|
1614
1714
|
HandoverShowSchema,
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1715
|
+
RigActSchema,
|
|
1716
|
+
RigPrepareSchema,
|
|
1717
|
+
RigLaunchSchema,
|
|
1718
|
+
RigAdoptSchema,
|
|
1719
|
+
RigSendSchema,
|
|
1720
|
+
RigExchangesRequestSchema,
|
|
1721
|
+
RigSpecsRequestSchema,
|
|
1722
|
+
EscalationAnswerSchema,
|
|
1723
|
+
LedgeSaySchema,
|
|
1724
|
+
LedgeThreadRequestSchema,
|
|
1725
|
+
MandateUpdateSchema,
|
|
1726
|
+
RuleSetSchema,
|
|
1727
|
+
DecisionRevisitSchema,
|
|
1728
|
+
CategoryEscalateSchema,
|
|
1729
|
+
BrainSetSchema,
|
|
1730
|
+
BrainCmdSchema,
|
|
1731
|
+
BrainDelegateClaimSchema,
|
|
1732
|
+
BrainDelegateDecideSchema,
|
|
1733
|
+
BrainDelegateReleaseSchema,
|
|
1734
|
+
BrainDelegateQuerySchema
|
|
1735
|
+
]);
|
|
1633
1736
|
function parseClientMessage(raw) {
|
|
1634
1737
|
return ClientMessageSchema.parse(raw);
|
|
1635
1738
|
}
|
|
@@ -1802,9 +1905,61 @@ var MeResponseSchema = z23.object({
|
|
|
1802
1905
|
// ../protocol/src/phone.ts
|
|
1803
1906
|
import { z as z26 } from "zod";
|
|
1804
1907
|
|
|
1805
|
-
// ../protocol/src/
|
|
1908
|
+
// ../protocol/src/phoneOpenrig.ts
|
|
1806
1909
|
import { z as z24 } from "zod";
|
|
1807
|
-
var
|
|
1910
|
+
var id = z24.string().min(1).max(120);
|
|
1911
|
+
var key = z24.string().min(1).max(64);
|
|
1912
|
+
var at = z24.string().datetime();
|
|
1913
|
+
var op = z24.string().uuid();
|
|
1914
|
+
var messageMax = 2e4;
|
|
1915
|
+
var toolTextMax = 2048;
|
|
1916
|
+
var PhoneRigSeatSchema = z24.object({ ref: id, address: id, name: z24.string().max(80), runtime: z24.string().max(80), model: z24.string().max(120).optional(), state: SeatStateSchema, contextPct: z24.number().min(0).max(100).optional(), sessionId: id.optional() }).strict();
|
|
1917
|
+
var PhoneRigSchema = z24.object({ key, name: z24.string().max(80), spec: z24.string().max(80).optional(), project: z24.string().max(80).nullable(), offProject: z24.boolean(), branch: z24.string().max(200).optional(), lifecycle: RigLifecycleSchema, kernel: z24.boolean(), seats: z24.array(PhoneRigSeatSchema).max(16), repos: z24.number().int().nonnegative(), launch: z24.object({ planId: id, goal: z24.string().max(600), lead: id, startedAt: at, goalSentAt: at.optional(), lateNotice: z24.literal(true).optional() }).strict().optional() }).strict();
|
|
1918
|
+
var PhoneExchangeSchema = z24.object({ id, rigKey: key, from: id, to: id, kind: z24.enum(["message", "task", "handoff", "ledge"]), text: z24.string().max(toolTextMax), at, cut: z24.literal(true).optional() }).strict();
|
|
1919
|
+
var PhoneQuestionSchema = z24.object({ id, question: z24.string().max(toolTextMax), options: z24.array(z24.object({ id, label: z24.string().max(200), recommended: z24.boolean().optional() }).strict()).min(2).max(4) }).strict();
|
|
1920
|
+
var PhoneEscalationSchema = z24.object({ id, source: z24.enum(["rig", "operator", "ask"]), rigKey: key.optional(), rigName: z24.string().max(60).optional(), from: id, intent: z24.enum(["decision", "update"]), summary: z24.string().max(toolTextMax), detail: z24.string().max(toolTextMax).optional(), questions: z24.array(PhoneQuestionSchema).max(4), createdAt: at, state: z24.enum(["pending", "grace", "answered", "closed"]), graceUntil: at.optional(), correcting: id.optional(), category: MandateCategorySchema.optional(), advice: z24.string().max(toolTextMax).optional(), rules: z24.array(z24.object({ text: z24.string().max(200), since: z24.string().max(10) }).strict()).max(5).optional(), answer: z24.object({ by: z24.enum(["ledge", "user"]), answers: z24.record(z24.string().max(120), z24.string().max(messageMax)), at, rule: id.optional(), delivery: z24.enum(["queued", "sent"]).optional() }).strict().optional() }).strict();
|
|
1921
|
+
var PhoneLedgeDecisionSchema = z24.object({ id, escalationId: id, by: z24.enum(["ledge", "user"]), category: MandateCategorySchema.optional(), summary: z24.string().max(toolTextMax), answers: z24.record(z24.string().max(120), z24.string().max(messageMax)), rule: id.optional(), from: id.optional(), rigName: z24.string().max(60).optional(), why: z24.string().max(toolTextMax).optional(), at, revisitedBy: id.optional() }).strict();
|
|
1922
|
+
var PhoneRigPlanSchema = z24.object({ id, spec: z24.string().max(80), goal: z24.string().max(600), where: z24.discriminatedUnion("kind", [z24.object({ kind: z24.literal("project"), project: z24.string().max(80), branch: z24.string().max(200), copy: z24.boolean() }).strict(), z24.object({ kind: z24.literal("off-project"), name: z24.string().max(80) }).strict()]), seats: z24.array(z24.object({ address: id, runtime: z24.string().max(80), model: z24.string().max(120).optional(), autonomy: z24.string().max(80) }).strict()).max(16), writes: z24.array(z24.string().max(200)).max(40), required: z24.boolean(), launchedRigKey: key.optional(), launchedAt: at.optional() }).strict();
|
|
1923
|
+
var PhoneThreadItemSchema = z24.discriminatedUnion("kind", [
|
|
1924
|
+
z24.object({ kind: z24.literal("user"), id, at, text: z24.string().max(toolTextMax), to: id.optional(), qid: id.optional() }).strict(),
|
|
1925
|
+
z24.object({ kind: z24.literal("ledge"), id, at, text: z24.string().max(toolTextMax), qid: id.optional(), pending: z24.literal(true).optional() }).strict(),
|
|
1926
|
+
z24.object({ kind: z24.literal("escalation"), id, at, escalation: PhoneEscalationSchema }).strict(),
|
|
1927
|
+
z24.object({ kind: z24.literal("decided"), id, at, decision: PhoneLedgeDecisionSchema }).strict(),
|
|
1928
|
+
z24.object({ kind: z24.literal("exchanges"), id, at, rigKey: key, rigName: z24.string().max(60), from: at, to: at, count: z24.number().int().min(1), items: z24.array(PhoneExchangeSchema).max(20) }).strict(),
|
|
1929
|
+
z24.object({ kind: z24.literal("card"), id, at, action: z24.lazy(() => PhoneChatActionSchema) }).strict(),
|
|
1930
|
+
z24.object({ kind: z24.literal("plan"), id, at, plan: PhoneRigPlanSchema }).strict()
|
|
1931
|
+
]);
|
|
1932
|
+
var PhoneRigActSchema = z24.object({ type: z24.literal("rig.act"), op, key, verb: z24.enum(["down", "restart"]), seatRef: id.optional(), assertion: z24.lazy(() => AssertionSchema).optional() }).strict();
|
|
1933
|
+
var PhoneRigLaunchSchema = z24.object({ type: z24.literal("rig.launch"), op, planId: id, assertion: z24.lazy(() => AssertionSchema).optional() }).strict();
|
|
1934
|
+
var PhoneEscalationAnswerSchema = z24.object({ type: z24.literal("escalation.answer"), op, id, answers: z24.record(z24.string().max(64), z24.string().max(messageMax)).refine((value) => Object.keys(value).length <= 4) }).strict();
|
|
1935
|
+
var rigSubject = (m) => m.type === "rig.launch" ? `launch@${m.planId}` : `${m.verb}@${m.key}${m.seatRef ? `/${m.seatRef}` : ""}`;
|
|
1936
|
+
var phoneOpenRigClientMessages = [
|
|
1937
|
+
z24.object({ type: z24.literal("ledge.say"), op, text: z24.string().trim().min(1).max(messageMax), about: id.optional() }).strict(),
|
|
1938
|
+
z24.object({ type: z24.literal("ledge.thread.request"), before: id.optional() }).strict(),
|
|
1939
|
+
PhoneEscalationAnswerSchema,
|
|
1940
|
+
z24.object({ type: z24.literal("answer.receipt.request"), answer: PhoneEscalationAnswerSchema }).strict(),
|
|
1941
|
+
z24.object({ type: z24.literal("decision.revisit"), op, id, text: z24.string().max(messageMax).optional() }).strict(),
|
|
1942
|
+
z24.object({ type: z24.literal("rig.exchanges.request"), key }).strict(),
|
|
1943
|
+
z24.object({ type: z24.literal("rig.prepare"), op, goal: z24.string().trim().min(1).max(600), spec: z24.string().max(64).optional(), project: z24.string().max(80).nullable() }).strict(),
|
|
1944
|
+
PhoneRigLaunchSchema,
|
|
1945
|
+
PhoneRigActSchema
|
|
1946
|
+
];
|
|
1947
|
+
var phoneOpenRigServerMessages = [
|
|
1948
|
+
z24.object({ type: z24.literal("answer.receipt"), op, status: z24.enum(["received", "applied", "unknown"]) }).strict(),
|
|
1949
|
+
z24.object({ type: z24.literal("rig.updated"), rig: PhoneRigSchema }).strict(),
|
|
1950
|
+
z24.object({ type: z24.literal("rig.removed"), key }).strict(),
|
|
1951
|
+
z24.object({ type: z24.literal("rig.exchanges"), key, items: z24.array(PhoneExchangeSchema).max(EXCHANGES_MAX), cut: z24.literal(true).optional() }).strict(),
|
|
1952
|
+
z24.object({ type: z24.literal("escalation.updated"), escalation: PhoneEscalationSchema }).strict(),
|
|
1953
|
+
z24.object({ type: z24.literal("escalation.removed"), id }).strict(),
|
|
1954
|
+
z24.object({ type: z24.literal("ledge.thread.item"), item: PhoneThreadItemSchema }).strict(),
|
|
1955
|
+
z24.object({ type: z24.literal("ledge.thread.history"), items: z24.array(PhoneThreadItemSchema).max(THREAD_PAGE), before: id.optional() }).strict(),
|
|
1956
|
+
z24.object({ type: z24.literal("decisions"), items: z24.array(PhoneLedgeDecisionSchema).max(50) }).strict(),
|
|
1957
|
+
z24.object({ type: z24.literal("rig.plan"), plan: PhoneRigPlanSchema }).strict()
|
|
1958
|
+
];
|
|
1959
|
+
|
|
1960
|
+
// ../protocol/src/push.ts
|
|
1961
|
+
import { z as z25 } from "zod";
|
|
1962
|
+
var B64U = z25.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1808
1963
|
var PUSH_HOSTS = ["*.push.apple.com", "fcm.googleapis.com", "updates.push.services.mozilla.com", "*.notify.windows.com"];
|
|
1809
1964
|
function pushEndpointAllowed(url) {
|
|
1810
1965
|
let u;
|
|
@@ -1821,76 +1976,34 @@ function pushEndpointAllowed(url) {
|
|
|
1821
1976
|
return host.length > suffix.length && host.endsWith(suffix);
|
|
1822
1977
|
});
|
|
1823
1978
|
}
|
|
1824
|
-
var PushSendSchema =
|
|
1825
|
-
endpoint:
|
|
1979
|
+
var PushSendSchema = z25.object({
|
|
1980
|
+
endpoint: z25.string().url().max(1024),
|
|
1826
1981
|
body: B64U.max(5462),
|
|
1827
|
-
ttl:
|
|
1828
|
-
urgency:
|
|
1829
|
-
topic:
|
|
1830
|
-
});
|
|
1831
|
-
var PushSendResultSchema =
|
|
1832
|
-
result:
|
|
1833
|
-
status:
|
|
1834
|
-
});
|
|
1835
|
-
var PushSubscriptionSchema =
|
|
1836
|
-
endpoint:
|
|
1837
|
-
keys:
|
|
1838
|
-
});
|
|
1839
|
-
var PushPayloadSchema =
|
|
1840
|
-
v:
|
|
1841
|
-
kind:
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1982
|
+
ttl: z25.number().int().min(0).max(86400),
|
|
1983
|
+
urgency: z25.enum(["normal", "high"]),
|
|
1984
|
+
topic: z25.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1985
|
+
});
|
|
1986
|
+
var PushSendResultSchema = z25.object({
|
|
1987
|
+
result: z25.enum(["sent", "gone", "failed"]),
|
|
1988
|
+
status: z25.number().int().optional()
|
|
1989
|
+
});
|
|
1990
|
+
var PushSubscriptionSchema = z25.object({
|
|
1991
|
+
endpoint: z25.string().url().max(1024).refine(pushEndpointAllowed),
|
|
1992
|
+
keys: z25.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
|
|
1993
|
+
});
|
|
1994
|
+
var PushPayloadSchema = z25.object({
|
|
1995
|
+
v: z25.literal(1),
|
|
1996
|
+
kind: z25.enum(["request", "question", "finished", "chat", "escalation", "decided"]),
|
|
1997
|
+
escalationId: z25.string().min(1).max(120).optional(),
|
|
1998
|
+
count: z25.number().int().min(1).max(999).optional(),
|
|
1999
|
+
actions: z25.array(z25.object({ qId: z25.string().min(1).max(64), optId: z25.string().min(1).max(64), label: z25.string().max(40) }).strict()).max(2).optional(),
|
|
2000
|
+
title: z25.string().max(80),
|
|
2001
|
+
body: z25.string().max(120),
|
|
2002
|
+
url: z25.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
|
|
2003
|
+
badge: z25.number().int().min(0).max(999),
|
|
2004
|
+
tag: z25.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1847
2005
|
});
|
|
1848
2006
|
|
|
1849
|
-
// ../protocol/src/phoneTeams.ts
|
|
1850
|
-
import { z as z25 } from "zod";
|
|
1851
|
-
var scope = DefinitionScopeSchema;
|
|
1852
|
-
var PhoneSeatsSchema = z25.array(SeatSchema.strict()).min(1).max(TEAM_SEATS_MAX).refine(uniqueRoles, "a role twice in one team");
|
|
1853
|
-
var PhoneRoleDraftSchema = RoleDraftSchema.strict();
|
|
1854
|
-
var projectName = z25.string().max(80).nullable();
|
|
1855
|
-
var PhoneRoleSchema = z25.object({
|
|
1856
|
-
id: RoleIdSchema,
|
|
1857
|
-
label: z25.string().min(1).max(ROLE_LABEL_MAX),
|
|
1858
|
-
prompt: z25.string().max(ROLE_PROMPT_MAX),
|
|
1859
|
-
cut: z25.literal(true).optional(),
|
|
1860
|
-
tool: ToolSchema.optional(),
|
|
1861
|
-
model: ModelIdSchema.optional(),
|
|
1862
|
-
effort: EffortSchema.optional(),
|
|
1863
|
-
autonomy: AutonomySchema.optional(),
|
|
1864
|
-
scope,
|
|
1865
|
-
project: projectName,
|
|
1866
|
-
hash: DefinitionHashSchema,
|
|
1867
|
-
shadows: z25.literal(true).optional(),
|
|
1868
|
-
shadowed: z25.literal(true).optional(),
|
|
1869
|
-
shadowedIn: z25.array(z25.string().max(80)).min(1).max(8).optional()
|
|
1870
|
-
}).strict();
|
|
1871
|
-
var PhoneTemplateSchema = z25.object({ id: TemplateIdSchema, name: TeamNameSchema, seats: PhoneSeatsSchema, scope, project: projectName, hash: DefinitionHashSchema }).strict();
|
|
1872
|
-
var PhoneProblemSchema = z25.object({ file: z25.string().max(80), scope, project: projectName, reason: z25.string().max(200) }).strict();
|
|
1873
|
-
var PhoneCatalogSchema = z25.object({
|
|
1874
|
-
/** The names of the projects with a local repository: the ones a « Dépôt » scope or a « Lever » can name. */
|
|
1875
|
-
projects: z25.array(z25.string().max(80)).max(50),
|
|
1876
|
-
roles: z25.array(PhoneRoleSchema).max(200),
|
|
1877
|
-
templates: z25.array(PhoneTemplateSchema).max(100),
|
|
1878
|
-
problems: z25.array(PhoneProblemSchema).max(50)
|
|
1879
|
-
}).strict();
|
|
1880
|
-
var TEAMS_OPERATION_TYPES = ["role.write", "role.delete", "template.write", "template.delete", "team.launch", "team.compose", "team.seat", "team.dissolve"];
|
|
1881
|
-
var isTeamsOperation = (m) => TEAMS_OPERATION_TYPES.includes(m.type);
|
|
1882
|
-
function canonicalJson(v) {
|
|
1883
|
-
const sort = (x) => {
|
|
1884
|
-
if (Array.isArray(x)) return x.map(sort);
|
|
1885
|
-
if (x !== null && typeof x === "object") {
|
|
1886
|
-
const o = x;
|
|
1887
|
-
return Object.fromEntries(Object.keys(o).sort().filter((k) => o[k] !== void 0).map((k) => [k, sort(o[k])]));
|
|
1888
|
-
}
|
|
1889
|
-
return x;
|
|
1890
|
-
};
|
|
1891
|
-
return JSON.stringify(sort(v));
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
2007
|
// ../protocol/src/phone.ts
|
|
1895
2008
|
var PHONE_PROTOCOL = 1;
|
|
1896
2009
|
var PAD_TIERS = [1024, 4096, 16384, 65536, 262144, 655360];
|
|
@@ -1928,7 +2041,7 @@ var RiskSchema = z26.enum(["low", "high"]);
|
|
|
1928
2041
|
var RiskReasonSchema = z26.enum(["question", "read_only_command", "edit_inside_folder", "pushes", "destroys", "deploys", "publishes", "pipes_to_shell", "network", "writes_outside_folder", "sensitive_path", "unsafe_flag", "shell_syntax", "script", "plan", "unknown"]);
|
|
1929
2042
|
var OperationIdSchema = z26.string().uuid();
|
|
1930
2043
|
var OperationStatusSchema = z26.enum(["received", "applied", "rejected", "failed"]);
|
|
1931
|
-
var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "
|
|
2044
|
+
var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "scheduled", ...ModelRefusalSchema.options, ...RIG_RESULT_CODES]);
|
|
1932
2045
|
var CapabilitiesSchema = z26.object({
|
|
1933
2046
|
/** A question can be answered in free text. */
|
|
1934
2047
|
freeTextAnswer: z26.boolean(),
|
|
@@ -1961,19 +2074,21 @@ var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
|
1961
2074
|
effort: true,
|
|
1962
2075
|
modelNext: true,
|
|
1963
2076
|
canSwitch: true,
|
|
1964
|
-
/** Its role and its live team (ledge home spec §2.4): the agents screen groups a project's sessions by table. */
|
|
1965
|
-
role: true,
|
|
1966
|
-
roleLabel: true,
|
|
1967
|
-
team: true,
|
|
1968
2077
|
/** Its context and its relief (context handover spec §3.2): the phone shows the gauge; the relief is started from the PC. */
|
|
1969
2078
|
context: true,
|
|
1970
|
-
relieves: true
|
|
2079
|
+
relieves: true,
|
|
2080
|
+
/** When it ended: « Terminée · à 18:42 » in the list. */
|
|
2081
|
+
exitedAt: true
|
|
1971
2082
|
}).extend({
|
|
1972
2083
|
pending: PhonePendingSchema.optional(),
|
|
1973
2084
|
unread: z26.boolean(),
|
|
1974
2085
|
handover: z26.object({ state: HandoverStateSchema, toName: z26.string().max(80).optional() }).strict().optional(),
|
|
1975
2086
|
/** Absent: the agent has no checklist. */
|
|
1976
|
-
checklist: PhoneChecklistSummarySchema.optional()
|
|
2087
|
+
checklist: PhoneChecklistSummarySchema.optional(),
|
|
2088
|
+
/** Started without Ledge (`hook_only`): the phone can neither close nor resume it. */
|
|
2089
|
+
readOnly: z26.literal(true).optional(),
|
|
2090
|
+
rig: z26.object({ rigName: z26.string().max(80), seat: z26.string().max(120) }).strict().optional(),
|
|
2091
|
+
brain: z26.literal(true).optional()
|
|
1977
2092
|
});
|
|
1978
2093
|
var B64U2 = z26.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1979
2094
|
var CredentialSchema = z26.object({
|
|
@@ -1990,8 +2105,7 @@ var AssertionSchema = z26.object({
|
|
|
1990
2105
|
authenticatorData: B64U2.max(4096),
|
|
1991
2106
|
signature: B64U2.max(1024)
|
|
1992
2107
|
});
|
|
1993
|
-
var
|
|
1994
|
-
var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "define", "team", "pc"]);
|
|
2108
|
+
var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "pc", "rig"]);
|
|
1995
2109
|
var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
|
|
1996
2110
|
var PhoneChatProposalSchema = z26.object({
|
|
1997
2111
|
project: z26.string().max(80),
|
|
@@ -2009,33 +2123,15 @@ var PhoneChatRelaySchema = z26.object({
|
|
|
2009
2123
|
sentAt: z26.string().datetime().optional(),
|
|
2010
2124
|
failed: z26.literal(true).optional()
|
|
2011
2125
|
}).strict();
|
|
2126
|
+
var CloseHowSchema = z26.enum(["exit", "killed"]);
|
|
2012
2127
|
var PhoneChatCloseSchema = z26.object({
|
|
2013
2128
|
id: z26.string().min(1),
|
|
2014
2129
|
name: z26.string(),
|
|
2015
2130
|
tool: ToolSchema,
|
|
2016
|
-
how:
|
|
2131
|
+
how: CloseHowSchema.optional(),
|
|
2017
2132
|
closedAt: z26.string().datetime().optional(),
|
|
2018
2133
|
failed: z26.literal(true).optional()
|
|
2019
2134
|
}).strict();
|
|
2020
|
-
var PhoneChatTeamSchema = z26.object({
|
|
2021
|
-
project: z26.string().max(80),
|
|
2022
|
-
name: z26.string().max(60),
|
|
2023
|
-
template: z26.string().max(64).optional(),
|
|
2024
|
-
seats: z26.array(z26.object({ role: z26.string().max(40), label: z26.string().max(40), tool: ToolSchema, model: z26.string().max(200).optional(), effort: z26.string().max(20).optional(), autonomy: AutonomySchema.optional() }).strict()).min(1).max(8),
|
|
2025
|
-
teamId: z26.string().max(40).optional(),
|
|
2026
|
-
launchedAt: z26.string().datetime().optional(),
|
|
2027
|
-
failed: z26.literal(true).optional()
|
|
2028
|
-
}).strict();
|
|
2029
|
-
var PhoneChatTaskSchema = z26.object({
|
|
2030
|
-
to: z26.string().max(80),
|
|
2031
|
-
toName: z26.string().max(80).optional(),
|
|
2032
|
-
title: z26.string().max(200),
|
|
2033
|
-
text: z26.string().max(TOOL_TEXT_MAX).optional(),
|
|
2034
|
-
taskId: z26.string().max(40).optional(),
|
|
2035
|
-
ownerName: z26.string().max(80).optional(),
|
|
2036
|
-
state: z26.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
|
|
2037
|
-
failed: z26.literal(true).optional()
|
|
2038
|
-
}).strict();
|
|
2039
2135
|
var PhoneChatActionSchema = z26.object({
|
|
2040
2136
|
action: z26.string().min(1).max(40),
|
|
2041
2137
|
summary: z26.string().max(300),
|
|
@@ -2044,46 +2140,10 @@ var PhoneChatActionSchema = z26.object({
|
|
|
2044
2140
|
detailCut: z26.literal(true).optional(),
|
|
2045
2141
|
risk: ActionRiskSchema,
|
|
2046
2142
|
session: z26.string().max(100).optional(),
|
|
2047
|
-
team: z26.string().max(40).optional(),
|
|
2048
2143
|
state: ChatActionStateSchema.optional(),
|
|
2049
2144
|
doneAt: z26.string().datetime().optional(),
|
|
2050
2145
|
code: z26.string().max(40).optional()
|
|
2051
2146
|
}).strict();
|
|
2052
|
-
var PhoneTeamSchema = z26.object({
|
|
2053
|
-
id: z26.string().max(40),
|
|
2054
|
-
name: z26.string().max(60),
|
|
2055
|
-
project: z26.string().max(80).nullable(),
|
|
2056
|
-
state: z26.enum(["active", "resting"]),
|
|
2057
|
-
template: z26.string().max(64).optional(),
|
|
2058
|
-
/** Absent from a daemon older than the phone: the phone then says no start time. */
|
|
2059
|
-
createdAt: z26.string().optional(),
|
|
2060
|
-
/** `tool`, `model` and `effort` are what the seat was composed with; `leaving` is a seat that finishes its turn and is then closed. */
|
|
2061
|
-
seats: z26.array(z26.object({
|
|
2062
|
-
role: z26.string().max(40),
|
|
2063
|
-
sessionId: z26.string().max(100).nullable(),
|
|
2064
|
-
tool: ToolSchema.optional(),
|
|
2065
|
-
model: ModelIdSchema.optional(),
|
|
2066
|
-
effort: EffortSchema.optional(),
|
|
2067
|
-
leaving: z26.literal(true).optional()
|
|
2068
|
-
}).strict()).max(16),
|
|
2069
|
-
done: z26.number().int().min(0),
|
|
2070
|
-
given: z26.number().int().min(0)
|
|
2071
|
-
}).strict();
|
|
2072
|
-
var PhoneTaskSchema = z26.object({
|
|
2073
|
-
id: z26.string().max(40),
|
|
2074
|
-
title: z26.string().max(200),
|
|
2075
|
-
owner: z26.string().max(100),
|
|
2076
|
-
ownerName: z26.string().max(80),
|
|
2077
|
-
ownerRole: z26.string().max(40).optional(),
|
|
2078
|
-
team: z26.string().max(40).optional(),
|
|
2079
|
-
teamName: z26.string().max(60).optional(),
|
|
2080
|
-
state: z26.enum(["open", "taken", "done", "refused", "dropped"]),
|
|
2081
|
-
result: z26.string().max(TOOL_TEXT_MAX).optional(),
|
|
2082
|
-
cut: z26.literal(true).optional(),
|
|
2083
|
-
at: z26.string(),
|
|
2084
|
-
updatedAt: z26.string(),
|
|
2085
|
-
seen: z26.literal(true).optional()
|
|
2086
|
-
}).strict();
|
|
2087
2147
|
var ChatErrorCodeSchema = z26.enum(["busy", "timeout", "cancelled", "failed"]);
|
|
2088
2148
|
var PhoneChatTurnSchema = z26.object({
|
|
2089
2149
|
qid: OperationIdSchema,
|
|
@@ -2097,8 +2157,6 @@ var PhoneChatTurnSchema = z26.object({
|
|
|
2097
2157
|
relay: PhoneChatRelaySchema.optional(),
|
|
2098
2158
|
closes: z26.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2099
2159
|
cut: z26.literal(true).optional(),
|
|
2100
|
-
team: PhoneChatTeamSchema.optional(),
|
|
2101
|
-
tasks: z26.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2102
2160
|
actions: z26.array(PhoneChatActionSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2103
2161
|
ignoredAt: z26.string().datetime().optional(),
|
|
2104
2162
|
attachments: z26.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional()
|
|
@@ -2107,30 +2165,32 @@ var CHAT_HISTORY_BUDGET = 393216;
|
|
|
2107
2165
|
var sessionId = z26.string().min(1).max(100);
|
|
2108
2166
|
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z26.string().min(1).max(64) });
|
|
2109
2167
|
var CardRowsSchema = z26.array(z26.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
|
|
2110
|
-
var
|
|
2168
|
+
var signedOp = { op: OperationIdSchema, assertion: AssertionSchema.optional() };
|
|
2111
2169
|
var PcActionSchema = z26.enum(["update", "restart"]);
|
|
2112
2170
|
var pcSubject = (action, version) => action === "update" ? `update@${version ?? ""}` : "restart";
|
|
2113
|
-
var teamsScope = { scope: DefinitionScopeSchema, project: z26.string().min(1).max(80).optional() };
|
|
2114
2171
|
var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
2115
2172
|
z26.object({ type: z26.literal("snapshot.request") }),
|
|
2173
|
+
...phoneOpenRigClientMessages,
|
|
2116
2174
|
z26.object({ type: z26.literal("session.thread"), id: sessionId }),
|
|
2117
2175
|
/** The session's request with its whole body (up to `PENDING_BODY_MAX`). */
|
|
2118
2176
|
z26.object({ type: z26.literal("session.pending"), id: sessionId }),
|
|
2119
2177
|
/** The session whose terminal the phone shows; null when it shows none. */
|
|
2120
2178
|
z26.object({ type: z26.literal("session.watch"), id: sessionId.nullable() }),
|
|
2121
2179
|
z26.object({ type: z26.literal("session.read"), id: sessionId, at: z26.string().datetime() }),
|
|
2122
|
-
/**
|
|
2180
|
+
/**
|
|
2181
|
+
* Asks for a challenge. One object for every purpose: the gateway refuses a `decide` without `id` and `expect`. A `close`
|
|
2182
|
+
* or `act` names a turn of the chat (`qid`) or a session (`id`, for `session.close` and `session.resume`).
|
|
2183
|
+
*/
|
|
2123
2184
|
z26.object({
|
|
2124
2185
|
type: z26.literal("confirm.prepare"),
|
|
2125
2186
|
purpose: ConfirmPurposeSchema,
|
|
2126
2187
|
id: sessionId.optional(),
|
|
2127
2188
|
expect: PhoneExpectSchema.optional(),
|
|
2128
2189
|
qid: OperationIdSchema.optional(),
|
|
2129
|
-
/** sha256 of the `define` or `team` operation the challenge is for (`teamsDigest`). */
|
|
2130
|
-
digest: TeamsDigestSchema.optional(),
|
|
2131
2190
|
/** A `pc` challenge: what it is for, and for an update the version it covers. */
|
|
2132
2191
|
action: PcActionSchema.optional(),
|
|
2133
|
-
version: z26.string().regex(SEMVER).optional()
|
|
2192
|
+
version: z26.string().regex(SEMVER).optional(),
|
|
2193
|
+
rig: z26.discriminatedUnion("type", [PhoneRigLaunchSchema, PhoneRigActSchema]).optional()
|
|
2134
2194
|
}),
|
|
2135
2195
|
z26.object({ type: z26.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
|
|
2136
2196
|
z26.object({
|
|
@@ -2165,6 +2225,10 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2165
2225
|
expect: PhoneExpectSchema
|
|
2166
2226
|
}),
|
|
2167
2227
|
z26.object({ type: z26.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
|
|
2228
|
+
/** Ends a live session as `/exit` would, killed as a last resort; answered with its `how`. Passkey, purpose `close`, over the session. */
|
|
2229
|
+
z26.object({ type: z26.literal("session.close"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
2230
|
+
/** Starts an ended session again on its conversation. Passkey, purpose `act`, over the session. */
|
|
2231
|
+
z26.object({ type: z26.literal("session.resume"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
2168
2232
|
/** Changes a live session's model and effort, `null` for the agent's own default; answered by `operation.result` and, when refused, `session.model.failed`. */
|
|
2169
2233
|
z26.object({ type: z26.literal("session.model"), op: OperationIdSchema, id: sessionId, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
2170
2234
|
/** The models an agent offers on the PC. */
|
|
@@ -2185,10 +2249,6 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2185
2249
|
z26.object({ type: z26.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
2186
2250
|
z26.object({ type: z26.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2187
2251
|
z26.object({ type: z26.literal("chat.close"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2188
|
-
/** The turn's team card (roles spec §3.8): launched like a session card, with the device check (purpose `launch`, the turn). */
|
|
2189
|
-
z26.object({ type: z26.literal("chat.team"), op: OperationIdSchema, qid: OperationIdSchema, assertion: AssertionSchema.optional() }),
|
|
2190
|
-
/** A row of the turn's task card: given by Ledge. */
|
|
2191
|
-
z26.object({ type: z26.literal("chat.task"), op: OperationIdSchema, qid: OperationIdSchema, row: z26.number().int().min(0).max(MAX_PROPOSALS - 1) }),
|
|
2192
2252
|
/** Rows of the turn's generic card (action registry spec §5): `confirmed` as the local `chat.act`; a row whose action asks
|
|
2193
2253
|
* for a device check (`phone`) needs one assertion, purpose `act`, over the turn. */
|
|
2194
2254
|
z26.object({
|
|
@@ -2203,33 +2263,13 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2203
2263
|
}),
|
|
2204
2264
|
/** « Ignorer » the turn's cards, or `undo` (« Revoir »), as the local `chat.ignore`. */
|
|
2205
2265
|
z26.object({ type: z26.literal("chat.ignore"), op: OperationIdSchema, qid: OperationIdSchema, undo: z26.literal(true).optional() }),
|
|
2206
|
-
/** « Reprendre » a resting team. */
|
|
2207
|
-
z26.object({ type: z26.literal("team.resume"), op: OperationIdSchema, team: z26.string().min(1).max(40) }),
|
|
2208
|
-
/** Puts a closed task Ledge gave away from « À traiter ». */
|
|
2209
|
-
z26.object({ type: z26.literal("task.seen"), op: OperationIdSchema, task: z26.string().min(1).max(40) }),
|
|
2210
2266
|
/** Whether the phone's app is in front: a device that is visible gets no push. */
|
|
2211
2267
|
z26.object({ type: z26.literal("presence"), visible: z26.boolean() }),
|
|
2212
|
-
/** The roles, templates and problems of every scope the phone may name; then one role whole, when the catalog cut its prompt. */
|
|
2213
|
-
z26.object({ type: z26.literal("teams.catalog") }),
|
|
2214
|
-
z26.object({ type: z26.literal("teams.role"), ...teamsScope, id: RoleIdSchema }).strict(),
|
|
2215
|
-
/** `base` is the hash the phone was shown, null to create. */
|
|
2216
|
-
z26.object({ type: z26.literal("role.write"), ...teamsOp, ...teamsScope, role: PhoneRoleDraftSchema, base: DefinitionHashSchema.nullable() }).strict(),
|
|
2217
|
-
z26.object({ type: z26.literal("role.delete"), ...teamsOp, ...teamsScope, id: RoleIdSchema, base: DefinitionHashSchema }).strict(),
|
|
2218
|
-
/** No `id`: create, the id comes from the name. */
|
|
2219
|
-
z26.object({ type: z26.literal("template.write"), ...teamsOp, ...teamsScope, id: TemplateIdSchema.optional(), name: TeamNameSchema, seats: PhoneSeatsSchema, base: DefinitionHashSchema.nullable() }).strict(),
|
|
2220
|
-
z26.object({ type: z26.literal("template.delete"), ...teamsOp, ...teamsScope, id: TemplateIdSchema, base: DefinitionHashSchema }).strict(),
|
|
2221
|
-
z26.object({ type: z26.literal("team.launch"), ...teamsOp, project: z26.string().min(1).max(80), name: TeamNameSchema, seats: PhoneSeatsSchema, template: z26.string().min(1).max(64).optional() }).strict(),
|
|
2222
|
-
z26.object({ type: z26.literal("team.compose"), ...teamsOp, team: TeamIdSchema, seats: PhoneSeatsSchema }).strict(),
|
|
2223
|
-
z26.object({ type: z26.literal("team.seat"), ...teamsOp, team: TeamIdSchema, role: RoleIdSchema, action: z26.enum(["start", "restart"]) }).strict(),
|
|
2224
|
-
z26.object({ type: z26.literal("team.dissolve"), ...teamsOp, team: TeamIdSchema }).strict(),
|
|
2225
2268
|
/** Installs `version` on the PC, then restarts its daemon; the phone asks for its own build's version. Passkey, purpose `pc`. */
|
|
2226
|
-
z26.object({ type: z26.literal("daemon.update"), ...
|
|
2269
|
+
z26.object({ type: z26.literal("daemon.update"), ...signedOp, version: z26.string().regex(SEMVER) }).strict(),
|
|
2227
2270
|
/** Restarts the PC's daemon without installing anything. Passkey, purpose `pc`. */
|
|
2228
|
-
z26.object({ type: z26.literal("daemon.restart"), ...
|
|
2229
|
-
])
|
|
2230
|
-
const issue = "scope" in m ? scopeIssue(m) : null;
|
|
2231
|
-
if (issue) ctx.addIssue({ code: z26.ZodIssueCode.custom, message: issue, path: ["project"] });
|
|
2232
|
-
});
|
|
2271
|
+
z26.object({ type: z26.literal("daemon.restart"), ...signedOp }).strict()
|
|
2272
|
+
]);
|
|
2233
2273
|
var PhoneModelChoiceSchema = z26.object({
|
|
2234
2274
|
id: ModelChoiceSchema.shape.id.max(120),
|
|
2235
2275
|
label: z26.string().max(80),
|
|
@@ -2238,30 +2278,31 @@ var PhoneModelChoiceSchema = z26.object({
|
|
|
2238
2278
|
defaultEffort: EffortSchema.optional(),
|
|
2239
2279
|
isDefault: z26.boolean().optional()
|
|
2240
2280
|
}).strict();
|
|
2241
|
-
var PhoneSeatModelSchema = z26.object({ role: z26.string().max(40), session: sessionId, status: z26.enum(["applied", "scheduled", "refused"]), reason: ModelRefusalSchema.optional() });
|
|
2242
2281
|
var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
2243
2282
|
z26.object({ type: z26.literal("snapshot"), protocol: z26.literal(PHONE_PROTOCOL), sessions: z26.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z26.object({
|
|
2244
2283
|
version: z26.string(),
|
|
2245
2284
|
name: z26.string().max(40),
|
|
2246
2285
|
boot: z26.string().uuid().optional(),
|
|
2247
|
-
teams: z26.literal(true).optional(),
|
|
2248
2286
|
/** The daemon takes `session.model`. */
|
|
2249
2287
|
models: z26.literal(true).optional(),
|
|
2250
2288
|
/** The daemon sends `session.checklist` and a session's `checklist` summary. */
|
|
2251
2289
|
checklist: z26.literal(true).optional(),
|
|
2252
2290
|
/** `update`: the daemon can install a version and restart (npm, systemd); `restart`: it can restart (systemd). */
|
|
2253
2291
|
update: z26.literal(true).optional(),
|
|
2254
|
-
restart: z26.literal(true).optional()
|
|
2255
|
-
|
|
2292
|
+
restart: z26.literal(true).optional(),
|
|
2293
|
+
/** The daemon takes `session.close` and `session.resume`. */
|
|
2294
|
+
sessionClose: z26.literal(true).optional(),
|
|
2295
|
+
openrig: z26.literal(true).optional(),
|
|
2296
|
+
answerReceipts: z26.literal(true).optional()
|
|
2297
|
+
}), openrigStatus: z26.object({ state: z26.enum(["absent", "stopped", "starting", "running", "unsupported", "error", "unreachable"]), version: z26.string().max(80).optional() }).strict().optional(), rigs: z26.array(PhoneRigSchema).max(40).optional(), escalations: z26.array(PhoneEscalationSchema).max(40).optional(), decisionsToday: z26.array(PhoneLedgeDecisionSchema).max(50).optional(), projects: z26.array(z26.object({ id: z26.string().min(1).max(80), name: z26.string().min(1).max(80), hasLocalRepo: z26.boolean() }).strict()).max(100).optional(), device: z26.object({ confirm: z26.boolean(), push: z26.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z26.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
|
|
2298
|
+
...phoneOpenRigServerMessages,
|
|
2256
2299
|
z26.object({
|
|
2257
2300
|
type: z26.literal("confirm.challenge"),
|
|
2258
2301
|
purpose: ConfirmPurposeSchema,
|
|
2259
2302
|
challenge: B64U2.max(64),
|
|
2260
2303
|
rpId: z26.string().max(253),
|
|
2261
2304
|
credentialId: B64U2.max(1366).optional(),
|
|
2262
|
-
expiresInMs: z26.number().int().positive()
|
|
2263
|
-
/** Echoed from the `confirm.prepare` of a `define` or `team` challenge, so the phone matches each challenge to its own call. Absent from an older gateway. */
|
|
2264
|
-
digest: TeamsDigestSchema.optional()
|
|
2305
|
+
expiresInMs: z26.number().int().positive()
|
|
2265
2306
|
}),
|
|
2266
2307
|
z26.object({ type: z26.literal("session.updated"), session: PhoneSessionSchema }),
|
|
2267
2308
|
z26.object({ type: z26.literal("session.removed"), id: z26.string() }),
|
|
@@ -2273,7 +2314,14 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2273
2314
|
/** `cut`: the ids of the items shortened; `more`: older items were left out. */
|
|
2274
2315
|
z26.object({ type: z26.literal("session.thread"), id: z26.string(), items: z26.array(ThreadItemSchema), cut: z26.array(z26.string()), more: z26.boolean() }),
|
|
2275
2316
|
z26.object({ type: z26.literal("session.thread.item"), id: z26.string(), item: ThreadItemSchema, cut: z26.literal(true).optional() }),
|
|
2276
|
-
z26.object({
|
|
2317
|
+
z26.object({
|
|
2318
|
+
type: z26.literal("operation.result"),
|
|
2319
|
+
op: OperationIdSchema,
|
|
2320
|
+
status: OperationStatusSchema,
|
|
2321
|
+
reason: OperationReasonSchema.optional(),
|
|
2322
|
+
/** How an applied `session.close` ended the agent: `killed` when it had to be forced. */
|
|
2323
|
+
how: CloseHowSchema.optional()
|
|
2324
|
+
}),
|
|
2277
2325
|
/** The models an agent offers; `error`: it could not be asked, `models` is then what Ledge knows without it. */
|
|
2278
2326
|
z26.object({ type: z26.literal("models.listing"), tool: ToolSchema, models: z26.array(PhoneModelChoiceSchema).max(40), error: z26.literal(true).optional() }),
|
|
2279
2327
|
/** A model change that was accepted and then failed (the agent could not restart). */
|
|
@@ -2284,30 +2332,26 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2284
2332
|
z26.object({ type: z26.literal("chat.delta"), qid: OperationIdSchema, text: z26.string().max(THREAD_TEXT_MAX) }),
|
|
2285
2333
|
z26.object({ type: z26.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
|
|
2286
2334
|
z26.object({ type: z26.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
|
|
2287
|
-
z26.object({ type: z26.literal("teams.catalog"), catalog: PhoneCatalogSchema }),
|
|
2288
2335
|
/** The daemon updating or restarting itself, whoever asked: npm runs, then the service restarts (the link drops right after). */
|
|
2289
2336
|
z26.object({ type: z26.literal("daemon.progress"), version: z26.string().max(40), step: z26.enum(["installing", "restarting"]) }),
|
|
2290
2337
|
/** Nothing was changed. Never npm's output nor a command: both hold the PC's paths. */
|
|
2291
2338
|
z26.object({ type: z26.literal("daemon.failed"), version: z26.string().max(40), reason: UpdateFailureSchema }),
|
|
2292
|
-
z26.object({ type: z26.literal("teams.role"), role: PhoneRoleSchema }),
|
|
2293
|
-
/** The live teams, after each snapshot and on every change. */
|
|
2294
|
-
z26.object({ type: z26.literal("teams"), teams: z26.array(PhoneTeamSchema).max(50) }),
|
|
2295
|
-
/** The tasks Ledge gave, open or closed lately, after each snapshot; then each change. */
|
|
2296
|
-
z26.object({ type: z26.literal("tasks"), tasks: z26.array(PhoneTaskSchema).max(200) }),
|
|
2297
|
-
z26.object({ type: z26.literal("task.updated"), task: PhoneTaskSchema }),
|
|
2298
2339
|
AttachmentStoredSchema,
|
|
2299
2340
|
AttachmentDataSchema,
|
|
2300
2341
|
AttachmentErrorSchema,
|
|
2301
2342
|
/** Never carries free text. */
|
|
2302
2343
|
z26.object({ type: z26.literal("error"), code: z26.enum(["invalid", "unknown_session", "too_large"]), request: z26.string().max(40).optional() })
|
|
2303
2344
|
]);
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2345
|
+
function canonicalJson(v) {
|
|
2346
|
+
const sort = (x) => {
|
|
2347
|
+
if (Array.isArray(x)) return x.map(sort);
|
|
2348
|
+
if (x !== null && typeof x === "object") {
|
|
2349
|
+
const o = x;
|
|
2350
|
+
return Object.fromEntries(Object.keys(o).sort().filter((k) => o[k] !== void 0).map((k) => [k, sort(o[k])]));
|
|
2351
|
+
}
|
|
2352
|
+
return x;
|
|
2353
|
+
};
|
|
2354
|
+
return JSON.stringify(sort(v));
|
|
2311
2355
|
}
|
|
2312
2356
|
|
|
2313
2357
|
// ../protocol/src/todos.ts
|
|
@@ -2364,130 +2408,56 @@ function latestTodos(items, helperId) {
|
|
|
2364
2408
|
}
|
|
2365
2409
|
var doneCount = (todos) => todos.filter((x) => x.status === "completed").length;
|
|
2366
2410
|
|
|
2367
|
-
// src/
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
}
|
|
2382
|
-
function hostAllowed(hostHeader) {
|
|
2383
|
-
const raw = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader;
|
|
2384
|
-
if (!raw || raw.length > 255) return false;
|
|
2385
|
-
const host = raw.trim().toLowerCase();
|
|
2386
|
-
if (!host || host.includes(" ") || host.endsWith(".")) return false;
|
|
2387
|
-
let name;
|
|
2388
|
-
if (host.startsWith("[")) {
|
|
2389
|
-
const end = host.indexOf("]");
|
|
2390
|
-
if (end < 0) return false;
|
|
2391
|
-
name = host.slice(1, end);
|
|
2392
|
-
const rest = host.slice(end + 1);
|
|
2393
|
-
if (rest !== "" && !/^:\d{1,5}$/.test(rest)) return false;
|
|
2394
|
-
} else {
|
|
2395
|
-
const colon = host.lastIndexOf(":");
|
|
2396
|
-
if (colon >= 0) {
|
|
2397
|
-
if (!/^\d{1,5}$/.test(host.slice(colon + 1))) return false;
|
|
2398
|
-
name = host.slice(0, colon);
|
|
2399
|
-
} else name = host;
|
|
2400
|
-
}
|
|
2401
|
-
return name === "localhost" || name === "127.0.0.1" || name === "::1";
|
|
2402
|
-
}
|
|
2403
|
-
function headerValue(value, max) {
|
|
2404
|
-
const raw = Array.isArray(value) ? value.join(",") : value;
|
|
2405
|
-
return !raw || raw.length > max ? "" : raw;
|
|
2406
|
-
}
|
|
2407
|
-
function bearerToken(authorization) {
|
|
2408
|
-
if (!authorization || authorization.length > 512) return "";
|
|
2409
|
-
return /^Bearer[ \t]+(\S+)$/i.exec(authorization)?.[1] ?? "";
|
|
2410
|
-
}
|
|
2411
|
-
function protocolToken(header) {
|
|
2412
|
-
const raw = headerValue(header, 512);
|
|
2413
|
-
if (!raw) return "";
|
|
2414
|
-
for (const part of raw.split(",").slice(0, 8)) {
|
|
2415
|
-
const protocol = part.trim();
|
|
2416
|
-
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX)) return protocol.slice(DAEMON_PROTOCOL_PREFIX.length);
|
|
2417
|
-
}
|
|
2418
|
-
return "";
|
|
2419
|
-
}
|
|
2420
|
-
function requestAuthorized(headers, expected) {
|
|
2421
|
-
const bearerOk = tokenEquals(bearerToken(typeof headers.authorization === "string" ? headers.authorization : void 0), expected);
|
|
2422
|
-
const presented = protocolToken(headers["sec-websocket-protocol"]);
|
|
2423
|
-
return bearerOk || presented !== "" && tokenEquals(presented, expected);
|
|
2424
|
-
}
|
|
2425
|
-
function selectDaemonProtocol(protocols, expected) {
|
|
2426
|
-
for (const protocol of protocols) {
|
|
2427
|
-
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX) && tokenEquals(protocol.slice(DAEMON_PROTOCOL_PREFIX.length), expected)) return protocol;
|
|
2428
|
-
}
|
|
2429
|
-
return false;
|
|
2430
|
-
}
|
|
2431
|
-
async function readTokenFile(file) {
|
|
2432
|
-
try {
|
|
2433
|
-
const raw = (await readFile(file, "utf8")).trim();
|
|
2434
|
-
return isDaemonToken(raw) ? raw : void 0;
|
|
2435
|
-
} catch (e) {
|
|
2436
|
-
if (e.code === "ENOENT") return void 0;
|
|
2437
|
-
throw e;
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
async function lockTokenFile(file) {
|
|
2441
|
-
await chmod(file, 384);
|
|
2442
|
-
}
|
|
2443
|
-
async function ensureDaemonToken() {
|
|
2444
|
-
const file = paths().token;
|
|
2445
|
-
await mkdir(dirname(file), { recursive: true });
|
|
2446
|
-
const current = await readTokenFile(file);
|
|
2447
|
-
if (current) {
|
|
2448
|
-
await lockTokenFile(file);
|
|
2449
|
-
return current;
|
|
2450
|
-
}
|
|
2451
|
-
const token = randomBytes(32).toString("hex");
|
|
2452
|
-
const tmp = `${file}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
2453
|
-
await writeFile(tmp, `${token}
|
|
2454
|
-
`, { mode: 384, flag: "wx" });
|
|
2455
|
-
await chmod(tmp, 384);
|
|
2456
|
-
try {
|
|
2457
|
-
await link(tmp, file);
|
|
2458
|
-
} catch (e) {
|
|
2459
|
-
await unlink(tmp).catch(() => {
|
|
2460
|
-
});
|
|
2461
|
-
if (e.code !== "EEXIST") throw e;
|
|
2462
|
-
const raced = await readTokenFile(file);
|
|
2463
|
-
if (raced) {
|
|
2464
|
-
await lockTokenFile(file);
|
|
2465
|
-
return raced;
|
|
2411
|
+
// ../protocol/src/mandate.ts
|
|
2412
|
+
var SELF_TITLE = "ledge tranche seul";
|
|
2413
|
+
var USER_TITLE = "te revient toujours";
|
|
2414
|
+
var isAlways = (id2) => ALWAYS_USER.includes(id2);
|
|
2415
|
+
function mandateEntries(text2) {
|
|
2416
|
+
const out = { self: [], user: [], always: [], readable: false };
|
|
2417
|
+
let list = null;
|
|
2418
|
+
for (const raw of text2.split("\n")) {
|
|
2419
|
+
const line = raw.trim();
|
|
2420
|
+
if (line.startsWith("#")) {
|
|
2421
|
+
const title = line.replace(/^#+\s*/, "").toLowerCase();
|
|
2422
|
+
list = title.startsWith(SELF_TITLE) ? "self" : title.startsWith(USER_TITLE) ? "user" : null;
|
|
2423
|
+
if (list) out.readable = true;
|
|
2424
|
+
continue;
|
|
2466
2425
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2426
|
+
const item = /^[-*]\s+(.+)$/.exec(line)?.[1];
|
|
2427
|
+
if (!list || !item) continue;
|
|
2428
|
+
const m = /^(\S+)\s+:\s+(.+)$/.exec(item);
|
|
2429
|
+
const entry = { id: m?.[1], said: (m?.[2] ?? item).trim() };
|
|
2430
|
+
if (list === "self" && entry.id && isAlways(entry.id)) out.always.push(entry);
|
|
2431
|
+
else out[list].push(entry);
|
|
2469
2432
|
}
|
|
2470
|
-
|
|
2471
|
-
});
|
|
2472
|
-
await lockTokenFile(file);
|
|
2473
|
-
const stored = await readTokenFile(file);
|
|
2474
|
-
if (!stored) throw new Error("daemon token was not stored");
|
|
2475
|
-
return stored;
|
|
2476
|
-
}
|
|
2477
|
-
var SESSION_KEY_VAR = "LEDGE_SESSION_KEY";
|
|
2478
|
-
function sessionKey(id, token = readDaemonToken()) {
|
|
2479
|
-
return token ? createHmac("sha256", token).update(`ledge-session:${id}`).digest("hex") : void 0;
|
|
2433
|
+
return out;
|
|
2480
2434
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
}
|
|
2435
|
+
var known = (id2) => {
|
|
2436
|
+
const r = MandateCategorySchema.safeParse(id2);
|
|
2437
|
+
return r.success ? r.data : null;
|
|
2438
|
+
};
|
|
2439
|
+
function parseMandate(text2) {
|
|
2440
|
+
const e = mandateEntries(text2);
|
|
2441
|
+
if (!e.readable) return { self: [], user: [], readable: false };
|
|
2442
|
+
const uniq = (xs) => [...new Set(xs.filter((x) => x !== null))];
|
|
2443
|
+
const user = uniq([...e.user, ...e.always].map((x) => known(x.id)));
|
|
2444
|
+
return { self: uniq(e.self.map((x) => known(x.id))).filter((c) => !user.includes(c)), user, readable: true };
|
|
2488
2445
|
}
|
|
2489
2446
|
|
|
2490
2447
|
export {
|
|
2448
|
+
tokenEquals,
|
|
2449
|
+
hostAllowed,
|
|
2450
|
+
requestAuthorized,
|
|
2451
|
+
selectDaemonProtocol,
|
|
2452
|
+
ensureDaemonToken,
|
|
2453
|
+
SESSION_KEY_VAR,
|
|
2454
|
+
sessionKey,
|
|
2455
|
+
readDaemonToken,
|
|
2456
|
+
ASK_QUESTION_MAX,
|
|
2457
|
+
ASK_OPTIONS_MAX,
|
|
2458
|
+
ASK_ANSWER_MAX,
|
|
2459
|
+
AskSchema,
|
|
2460
|
+
askShown,
|
|
2491
2461
|
ATTACHMENT_ID,
|
|
2492
2462
|
ATTACHMENT_MAX,
|
|
2493
2463
|
ATTACHMENT_LOCAL_MAX,
|
|
@@ -2498,6 +2468,7 @@ export {
|
|
|
2498
2468
|
CONTEXT_NEAR_MIN,
|
|
2499
2469
|
CONTEXT_NEAR_MAX,
|
|
2500
2470
|
ContextPrefsSchema,
|
|
2471
|
+
contextShare,
|
|
2501
2472
|
contextNear,
|
|
2502
2473
|
BRIEF_MAX,
|
|
2503
2474
|
ToolSchema,
|
|
@@ -2512,34 +2483,12 @@ export {
|
|
|
2512
2483
|
extractProposal,
|
|
2513
2484
|
parseRelayBlock,
|
|
2514
2485
|
parseCloseBlock,
|
|
2515
|
-
parseTeamBlock,
|
|
2516
|
-
parseTaskBlock,
|
|
2517
2486
|
parseProjectBlock,
|
|
2518
|
-
SshAliasSchema,
|
|
2519
|
-
MachinesSchema,
|
|
2520
|
-
PROJECT_COLORS,
|
|
2521
|
-
ProjectsSchema,
|
|
2522
|
-
DEFAULT_PROJECTS,
|
|
2523
|
-
projectOf,
|
|
2524
|
-
RoleIdSchema,
|
|
2525
|
-
ROLE_LABEL_MAX,
|
|
2526
|
-
ROLE_PROMPT_MAX,
|
|
2527
|
-
RoleSchema,
|
|
2528
|
-
SeatsSchema,
|
|
2529
|
-
TeamNameSchema,
|
|
2530
|
-
TemplateIdSchema,
|
|
2531
|
-
TeamSchema,
|
|
2532
|
-
isTeamLive,
|
|
2533
2487
|
NOTE_MAX,
|
|
2534
2488
|
NOTE_NOT_TYPED,
|
|
2535
2489
|
offers,
|
|
2536
2490
|
pendingKey,
|
|
2537
|
-
|
|
2538
|
-
TASK_TEXT_MAX,
|
|
2539
|
-
LEDGE_GIVER,
|
|
2540
|
-
transmissions,
|
|
2541
|
-
givenByLedge,
|
|
2542
|
-
taskClosed,
|
|
2491
|
+
ActionIdSchema,
|
|
2543
2492
|
ACTIONS,
|
|
2544
2493
|
actionById,
|
|
2545
2494
|
riskOf,
|
|
@@ -2552,10 +2501,29 @@ export {
|
|
|
2552
2501
|
ChatTurnSchema,
|
|
2553
2502
|
proposalRows,
|
|
2554
2503
|
DOCUMENT_MAX_BYTES,
|
|
2504
|
+
SshAliasSchema,
|
|
2505
|
+
MachinesSchema,
|
|
2555
2506
|
IslandPrefsSchema,
|
|
2507
|
+
PROJECT_COLORS,
|
|
2508
|
+
ProjectsSchema,
|
|
2509
|
+
DEFAULT_PROJECTS,
|
|
2510
|
+
projectOf,
|
|
2556
2511
|
RouteResultSchema,
|
|
2557
2512
|
DEFAULT_RELAY_URL,
|
|
2558
2513
|
MobileConfigSchema,
|
|
2514
|
+
MandateCategorySchema,
|
|
2515
|
+
ALWAYS_USER,
|
|
2516
|
+
MANDATE_CATEGORY_LABEL,
|
|
2517
|
+
RIG_NAME,
|
|
2518
|
+
EscalationSchema,
|
|
2519
|
+
EXCHANGES_MAX,
|
|
2520
|
+
EXCHANGE_GROUP_MS,
|
|
2521
|
+
THREAD_PAGE,
|
|
2522
|
+
LedgeDecisionSchema,
|
|
2523
|
+
LedgeRuleSchema,
|
|
2524
|
+
LedgeThreadItemSchema,
|
|
2525
|
+
BRAIN_OPS,
|
|
2526
|
+
BRAIN_STOPPED_TEXT,
|
|
2559
2527
|
PROTOCOL_PORT,
|
|
2560
2528
|
RefusalCodeSchema,
|
|
2561
2529
|
RecentSchema,
|
|
@@ -2575,15 +2543,12 @@ export {
|
|
|
2575
2543
|
RELAY_CLOSE_SIGNED_OUT,
|
|
2576
2544
|
DeviceStartResponseSchema,
|
|
2577
2545
|
DevicePollResponseSchema,
|
|
2546
|
+
rigSubject,
|
|
2578
2547
|
pushEndpointAllowed,
|
|
2579
2548
|
PushSendResultSchema,
|
|
2580
2549
|
PushPayloadSchema,
|
|
2581
|
-
PhoneRoleSchema,
|
|
2582
|
-
PhoneTemplateSchema,
|
|
2583
|
-
TEAMS_OPERATION_TYPES,
|
|
2584
|
-
isTeamsOperation,
|
|
2585
|
-
canonicalJson,
|
|
2586
2550
|
PHONE_PROTOCOL,
|
|
2551
|
+
PLAIN_MAX,
|
|
2587
2552
|
padPlain,
|
|
2588
2553
|
THREAD_ITEMS_MAX,
|
|
2589
2554
|
THREAD_BUDGET,
|
|
@@ -2599,17 +2564,10 @@ export {
|
|
|
2599
2564
|
pcSubject,
|
|
2600
2565
|
PhoneClientMessageSchema,
|
|
2601
2566
|
PhoneModelChoiceSchema,
|
|
2602
|
-
|
|
2603
|
-
teamsDigest,
|
|
2567
|
+
canonicalJson,
|
|
2604
2568
|
TODO_TOOLS,
|
|
2605
2569
|
latestTodos,
|
|
2606
2570
|
doneCount,
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
requestAuthorized,
|
|
2610
|
-
selectDaemonProtocol,
|
|
2611
|
-
ensureDaemonToken,
|
|
2612
|
-
SESSION_KEY_VAR,
|
|
2613
|
-
sessionKey,
|
|
2614
|
-
readDaemonToken
|
|
2571
|
+
mandateEntries,
|
|
2572
|
+
parseMandate
|
|
2615
2573
|
};
|