@paradow/ledge 0.28.0 → 0.29.0-snapshot.41306023381.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-MXRYKL5N.js} +5 -5
- package/dist/cli/ask-HIKSAHRR.js +17 -0
- package/dist/cli/{attach-KXOSFUHO.js → attach-J65WTS3H.js} +9 -14
- package/dist/cli/brain-XOGS4OPT.js +20 -0
- package/dist/cli/{bridge-XCTXVWKH.js → bridge-YZL5NT7Q.js} +2 -2
- package/dist/cli/{chunk-3NQNWGDX.js → chunk-3U6TAEIO.js} +3 -5
- package/dist/cli/chunk-66ZLZ2OW.js +365 -0
- package/dist/cli/{chunk-JLUHY5AB.js → chunk-7XFJ5QCZ.js} +4 -4
- package/dist/cli/chunk-BP7NGWYD.js +57 -0
- package/dist/cli/{chunk-LG3XFIPV.js → chunk-DEOQT7IM.js} +24 -7
- package/dist/cli/chunk-DJMFCYF4.js +60 -0
- package/dist/cli/{chunk-6IYPRUQG.js → chunk-G3ST5WKR.js} +1 -1
- package/dist/cli/{chunk-VKRFHDRK.js → chunk-G6ESCKEX.js} +1124 -1164
- package/dist/cli/{chunk-3CF4FQWG.js → chunk-J67O4QQG.js} +4 -3
- package/dist/cli/{chunk-RYZ6FHYT.js → chunk-JFXRDHZR.js} +17 -39
- package/dist/cli/{chunk-NLL2EQJ2.js → chunk-KBSXP63H.js} +22 -7
- package/dist/cli/{chunk-CR3LGTZQ.js → chunk-LQCWBJYE.js} +3 -3
- package/dist/cli/{chunk-ZI47U5CQ.js → chunk-MJTMM6GC.js} +102 -72
- package/dist/cli/chunk-NEYGPVEZ.js +57 -0
- package/dist/cli/chunk-OFY6GEYS.js +6 -0
- 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-VMGACWNP.js → chunk-X6NV2IEM.js} +1 -1
- package/dist/cli/daemon-JPKL3IAU.js +16576 -0
- package/dist/cli/{decide-OSSTI3FE.js → decide-2LECM6BO.js} +14 -16
- package/dist/cli/{handover-2G7OEWIG.js → handover-ZFNRGTTV.js} +5 -5
- package/dist/cli/{hook-XY4E5H7O.js → hook-TZXMVYOG.js} +87 -81
- package/dist/cli/index.js +40 -28
- package/dist/cli/launch-HGECZQCL.js +28 -0
- package/dist/cli/{send-RUHGKLMA.js → send-4TPIJG7O.js} +5 -5
- package/dist/cli/{setup-K42IVMHS.js → setup-OLVRJLJE.js} +4 -4
- package/dist/cli/{status-4Z2VJFID.js → status-C6I23SKG.js} +3 -3
- package/dist/cli/{tui-4IRYQXYU.js → tui-RD5JZLSU.js} +6008 -3597
- package/dist/cli/{uninstall-4IMMR55T.js → uninstall-X65CWHIJ.js} +4 -4
- package/dist/cli/{version-L2Y7MRH6.js → version-SU3VF37X.js} +1 -1
- package/dist/cli/who-JXTN6IDB.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", [
|
|
@@ -1361,6 +1441,8 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1361
1441
|
* from daemons older than the auto-update. */
|
|
1362
1442
|
z21.object({ type: z21.literal("snapshot"), sessions: z21.array(SessionSchema), recents: z21.array(RecentSchema).optional(), launcher: z21.array(z21.string()).optional(), daemon: DaemonInfoSchema.optional(), sshAlias: SshAliasSchema.optional() }),
|
|
1363
1443
|
z21.object({ type: z21.literal("session.updated"), session: SessionSchema }),
|
|
1444
|
+
/** Ephemeral office bubble; never included in snapshots or phone relay. */
|
|
1445
|
+
z21.object({ type: z21.literal("session.thought"), id: z21.string(), text: z21.string().max(80), at: z21.string().datetime() }),
|
|
1364
1446
|
/** OMP: the instructions taken back from its queue by an interrupt that restores it; the asker puts them back in its draft. */
|
|
1365
1447
|
z21.object({
|
|
1366
1448
|
type: z21.literal("session.queue.restored"),
|
|
@@ -1372,6 +1454,8 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1372
1454
|
}),
|
|
1373
1455
|
z21.object({ type: z21.literal("session.removed"), id: z21.string() }),
|
|
1374
1456
|
z21.object({ type: z21.literal("session.preview"), id: z21.string(), lines: z21.array(z21.string()) }),
|
|
1457
|
+
/** The session's terminal in color (SGR codes kept), sent only to the client that subscribed to it. */
|
|
1458
|
+
z21.object({ type: z21.literal("session.preview.color"), id: z21.string(), lines: z21.array(z21.string()) }),
|
|
1375
1459
|
/** A session's thread, on request (spec 2026-10-13 §5); then each created or updated item, broadcast. */
|
|
1376
1460
|
z21.object({ type: z21.literal("session.thread"), id: z21.string(), items: z21.array(ThreadItemSchema) }),
|
|
1377
1461
|
z21.object({ type: z21.literal("session.thread.item"), id: z21.string(), item: ThreadItemSchema }),
|
|
@@ -1397,7 +1481,7 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1397
1481
|
/** 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
1482
|
z21.object({ type: z21.literal("chat.step"), qid: z21.string(), step: ChatStepSchema }),
|
|
1399
1483
|
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(),
|
|
1484
|
+
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
1485
|
/** What a `chat.act` applied: the rows done, and the one that failed with why (the rows after it stay proposed). */
|
|
1402
1486
|
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
1487
|
row: z21.number().int().min(0),
|
|
@@ -1407,7 +1491,7 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1407
1491
|
})) }),
|
|
1408
1492
|
/** What an `action.run` came to: `row` the action as the daemon resolved it (its summary, its risk); `reason` why it was not
|
|
1409
1493
|
* 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`
|
|
1494
|
+
* again; `failed`: the handler turned it down; a free string, as an `error`'s); `path` a file the action wrote. */
|
|
1411
1495
|
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
1496
|
z21.object({ type: z21.literal("chat.error"), qid: z21.string(), message: z21.string() }),
|
|
1413
1497
|
z21.object({ type: z21.literal("chat.history"), turns: z21.array(ChatTurnSchema) }),
|
|
@@ -1427,20 +1511,31 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1427
1511
|
z21.object({ type: z21.literal("message.ack"), rid: z21.string(), outcome: z21.enum(["accepted", "queued", "native", "refused"]), reason: z21.string().optional() }),
|
|
1428
1512
|
/** The answer to a `message.read`: the body with its sender, target and time, or the refusal's `reason`. */
|
|
1429
1513
|
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
1514
|
SessionHandoverResultSchema,
|
|
1432
1515
|
ContextPrefsMessageSchema,
|
|
1433
1516
|
HandoverResultSchema,
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1517
|
+
AsksMessageSchema,
|
|
1518
|
+
AskUpdatedSchema,
|
|
1519
|
+
AskResultSchema,
|
|
1520
|
+
OpenRigStatusMessageSchema,
|
|
1521
|
+
RigSnapshotSchema,
|
|
1522
|
+
RigUpdatedSchema,
|
|
1523
|
+
RigRemovedSchema,
|
|
1524
|
+
RigExchangeSchema,
|
|
1525
|
+
RigExchangesSchema,
|
|
1526
|
+
RigSpecsSchema,
|
|
1527
|
+
EscalationUpdatedSchema,
|
|
1528
|
+
EscalationRemovedSchema,
|
|
1529
|
+
LedgeThreadItemMessageSchema,
|
|
1530
|
+
LedgeThreadHistorySchema,
|
|
1531
|
+
DecisionsMessageSchema,
|
|
1532
|
+
RulesMessageSchema,
|
|
1533
|
+
MandateMessageSchema,
|
|
1534
|
+
BrainStatusMessageSchema,
|
|
1535
|
+
RigPlanMessageSchema,
|
|
1536
|
+
RigResultSchema,
|
|
1537
|
+
BrainResultSchema,
|
|
1538
|
+
BrainDelegateResultSchema,
|
|
1444
1539
|
z21.object({
|
|
1445
1540
|
type: z21.literal("error"),
|
|
1446
1541
|
message: z21.string(),
|
|
@@ -1483,10 +1578,6 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1483
1578
|
effort: EffortSchema.optional(),
|
|
1484
1579
|
/** OMP only: its approval mode, `always-ask` when left out. */
|
|
1485
1580
|
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
1581
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1491
1582
|
machineId: z21.string().optional()
|
|
1492
1583
|
}),
|
|
@@ -1574,10 +1665,20 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1574
1665
|
z21.object({ type: z21.literal("session.delete"), id: z21.string() }),
|
|
1575
1666
|
/** 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
1667
|
z21.object({ type: z21.literal("session.rename"), id: z21.string(), topic: z21.string().max(500) }),
|
|
1668
|
+
/** The one session this client follows in color; `id: null` follows none. */
|
|
1669
|
+
z21.object({ type: z21.literal("session.preview.subscribe"), id: z21.string().nullable(), color: z21.literal(true) }),
|
|
1577
1670
|
z21.object({ type: z21.literal("session.thread"), id: z21.string() }),
|
|
1578
1671
|
z21.object({ type: z21.literal("session.register"), session: SessionSchema }),
|
|
1579
1672
|
/** `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({
|
|
1673
|
+
z21.object({
|
|
1674
|
+
type: z21.literal("session.event"),
|
|
1675
|
+
id: z21.string(),
|
|
1676
|
+
event: SessionEventSchema,
|
|
1677
|
+
agent: ToolSchema.optional(),
|
|
1678
|
+
cwd: z21.string().optional(),
|
|
1679
|
+
/** The OpenRig seat a hook ran in: its session name (`dev-build@starter`) and node id, what pairs the session with its seat. */
|
|
1680
|
+
openrig: z21.object({ sessionName: z21.string().min(1).max(200), nodeId: z21.string().max(200).optional() }).optional()
|
|
1681
|
+
}),
|
|
1581
1682
|
z21.object({ type: z21.literal("session.unregister"), id: z21.string(), exitCode: z21.number().int().nullable() }),
|
|
1582
1683
|
z21.object({ type: z21.literal("snapshot.request") }),
|
|
1583
1684
|
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 +1708,33 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1607
1708
|
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
1709
|
/** `ledge read <id>`: a message's whole body, for its receiver or its sender (`by`, a session id). */
|
|
1609
1710
|
z21.object({ type: z21.literal("message.read"), id: z21.string().min(1).max(40), by: z21.string().min(1) }),
|
|
1610
|
-
|
|
1711
|
+
AskCreateSchema,
|
|
1712
|
+
AskAnswerSchema,
|
|
1611
1713
|
SessionHandoverSchema,
|
|
1612
1714
|
ContextSetSchema,
|
|
1613
1715
|
HandoverBriefSchema,
|
|
1614
1716
|
HandoverShowSchema,
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1717
|
+
RigActSchema,
|
|
1718
|
+
RigPrepareSchema,
|
|
1719
|
+
RigLaunchSchema,
|
|
1720
|
+
RigAdoptSchema,
|
|
1721
|
+
RigSendSchema,
|
|
1722
|
+
RigExchangesRequestSchema,
|
|
1723
|
+
RigSpecsRequestSchema,
|
|
1724
|
+
EscalationAnswerSchema,
|
|
1725
|
+
LedgeSaySchema,
|
|
1726
|
+
LedgeThreadRequestSchema,
|
|
1727
|
+
MandateUpdateSchema,
|
|
1728
|
+
RuleSetSchema,
|
|
1729
|
+
DecisionRevisitSchema,
|
|
1730
|
+
CategoryEscalateSchema,
|
|
1731
|
+
BrainSetSchema,
|
|
1732
|
+
BrainCmdSchema,
|
|
1733
|
+
BrainDelegateClaimSchema,
|
|
1734
|
+
BrainDelegateDecideSchema,
|
|
1735
|
+
BrainDelegateReleaseSchema,
|
|
1736
|
+
BrainDelegateQuerySchema
|
|
1737
|
+
]);
|
|
1633
1738
|
function parseClientMessage(raw) {
|
|
1634
1739
|
return ClientMessageSchema.parse(raw);
|
|
1635
1740
|
}
|
|
@@ -1802,9 +1907,61 @@ var MeResponseSchema = z23.object({
|
|
|
1802
1907
|
// ../protocol/src/phone.ts
|
|
1803
1908
|
import { z as z26 } from "zod";
|
|
1804
1909
|
|
|
1805
|
-
// ../protocol/src/
|
|
1910
|
+
// ../protocol/src/phoneOpenrig.ts
|
|
1806
1911
|
import { z as z24 } from "zod";
|
|
1807
|
-
var
|
|
1912
|
+
var id = z24.string().min(1).max(120);
|
|
1913
|
+
var key = z24.string().min(1).max(64);
|
|
1914
|
+
var at = z24.string().datetime();
|
|
1915
|
+
var op = z24.string().uuid();
|
|
1916
|
+
var messageMax = 2e4;
|
|
1917
|
+
var toolTextMax = 2048;
|
|
1918
|
+
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();
|
|
1919
|
+
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();
|
|
1920
|
+
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();
|
|
1921
|
+
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();
|
|
1922
|
+
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();
|
|
1923
|
+
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();
|
|
1924
|
+
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();
|
|
1925
|
+
var PhoneThreadItemSchema = z24.discriminatedUnion("kind", [
|
|
1926
|
+
z24.object({ kind: z24.literal("user"), id, at, text: z24.string().max(toolTextMax), to: id.optional(), qid: id.optional() }).strict(),
|
|
1927
|
+
z24.object({ kind: z24.literal("ledge"), id, at, text: z24.string().max(toolTextMax), qid: id.optional(), pending: z24.literal(true).optional() }).strict(),
|
|
1928
|
+
z24.object({ kind: z24.literal("escalation"), id, at, escalation: PhoneEscalationSchema }).strict(),
|
|
1929
|
+
z24.object({ kind: z24.literal("decided"), id, at, decision: PhoneLedgeDecisionSchema }).strict(),
|
|
1930
|
+
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(),
|
|
1931
|
+
z24.object({ kind: z24.literal("card"), id, at, action: z24.lazy(() => PhoneChatActionSchema) }).strict(),
|
|
1932
|
+
z24.object({ kind: z24.literal("plan"), id, at, plan: PhoneRigPlanSchema }).strict()
|
|
1933
|
+
]);
|
|
1934
|
+
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();
|
|
1935
|
+
var PhoneRigLaunchSchema = z24.object({ type: z24.literal("rig.launch"), op, planId: id, assertion: z24.lazy(() => AssertionSchema).optional() }).strict();
|
|
1936
|
+
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();
|
|
1937
|
+
var rigSubject = (m) => m.type === "rig.launch" ? `launch@${m.planId}` : `${m.verb}@${m.key}${m.seatRef ? `/${m.seatRef}` : ""}`;
|
|
1938
|
+
var phoneOpenRigClientMessages = [
|
|
1939
|
+
z24.object({ type: z24.literal("ledge.say"), op, text: z24.string().trim().min(1).max(messageMax), about: id.optional() }).strict(),
|
|
1940
|
+
z24.object({ type: z24.literal("ledge.thread.request"), before: id.optional() }).strict(),
|
|
1941
|
+
PhoneEscalationAnswerSchema,
|
|
1942
|
+
z24.object({ type: z24.literal("answer.receipt.request"), answer: PhoneEscalationAnswerSchema }).strict(),
|
|
1943
|
+
z24.object({ type: z24.literal("decision.revisit"), op, id, text: z24.string().max(messageMax).optional() }).strict(),
|
|
1944
|
+
z24.object({ type: z24.literal("rig.exchanges.request"), key }).strict(),
|
|
1945
|
+
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(),
|
|
1946
|
+
PhoneRigLaunchSchema,
|
|
1947
|
+
PhoneRigActSchema
|
|
1948
|
+
];
|
|
1949
|
+
var phoneOpenRigServerMessages = [
|
|
1950
|
+
z24.object({ type: z24.literal("answer.receipt"), op, status: z24.enum(["received", "applied", "unknown"]) }).strict(),
|
|
1951
|
+
z24.object({ type: z24.literal("rig.updated"), rig: PhoneRigSchema }).strict(),
|
|
1952
|
+
z24.object({ type: z24.literal("rig.removed"), key }).strict(),
|
|
1953
|
+
z24.object({ type: z24.literal("rig.exchanges"), key, items: z24.array(PhoneExchangeSchema).max(EXCHANGES_MAX), cut: z24.literal(true).optional() }).strict(),
|
|
1954
|
+
z24.object({ type: z24.literal("escalation.updated"), escalation: PhoneEscalationSchema }).strict(),
|
|
1955
|
+
z24.object({ type: z24.literal("escalation.removed"), id }).strict(),
|
|
1956
|
+
z24.object({ type: z24.literal("ledge.thread.item"), item: PhoneThreadItemSchema }).strict(),
|
|
1957
|
+
z24.object({ type: z24.literal("ledge.thread.history"), items: z24.array(PhoneThreadItemSchema).max(THREAD_PAGE), before: id.optional() }).strict(),
|
|
1958
|
+
z24.object({ type: z24.literal("decisions"), items: z24.array(PhoneLedgeDecisionSchema).max(50) }).strict(),
|
|
1959
|
+
z24.object({ type: z24.literal("rig.plan"), plan: PhoneRigPlanSchema }).strict()
|
|
1960
|
+
];
|
|
1961
|
+
|
|
1962
|
+
// ../protocol/src/push.ts
|
|
1963
|
+
import { z as z25 } from "zod";
|
|
1964
|
+
var B64U = z25.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1808
1965
|
var PUSH_HOSTS = ["*.push.apple.com", "fcm.googleapis.com", "updates.push.services.mozilla.com", "*.notify.windows.com"];
|
|
1809
1966
|
function pushEndpointAllowed(url) {
|
|
1810
1967
|
let u;
|
|
@@ -1821,76 +1978,34 @@ function pushEndpointAllowed(url) {
|
|
|
1821
1978
|
return host.length > suffix.length && host.endsWith(suffix);
|
|
1822
1979
|
});
|
|
1823
1980
|
}
|
|
1824
|
-
var PushSendSchema =
|
|
1825
|
-
endpoint:
|
|
1981
|
+
var PushSendSchema = z25.object({
|
|
1982
|
+
endpoint: z25.string().url().max(1024),
|
|
1826
1983
|
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
|
-
|
|
1984
|
+
ttl: z25.number().int().min(0).max(86400),
|
|
1985
|
+
urgency: z25.enum(["normal", "high"]),
|
|
1986
|
+
topic: z25.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1987
|
+
});
|
|
1988
|
+
var PushSendResultSchema = z25.object({
|
|
1989
|
+
result: z25.enum(["sent", "gone", "failed"]),
|
|
1990
|
+
status: z25.number().int().optional()
|
|
1991
|
+
});
|
|
1992
|
+
var PushSubscriptionSchema = z25.object({
|
|
1993
|
+
endpoint: z25.string().url().max(1024).refine(pushEndpointAllowed),
|
|
1994
|
+
keys: z25.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
|
|
1995
|
+
});
|
|
1996
|
+
var PushPayloadSchema = z25.object({
|
|
1997
|
+
v: z25.literal(1),
|
|
1998
|
+
kind: z25.enum(["request", "question", "finished", "chat", "escalation", "decided"]),
|
|
1999
|
+
escalationId: z25.string().min(1).max(120).optional(),
|
|
2000
|
+
count: z25.number().int().min(1).max(999).optional(),
|
|
2001
|
+
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(),
|
|
2002
|
+
title: z25.string().max(80),
|
|
2003
|
+
body: z25.string().max(120),
|
|
2004
|
+
url: z25.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
|
|
2005
|
+
badge: z25.number().int().min(0).max(999),
|
|
2006
|
+
tag: z25.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1847
2007
|
});
|
|
1848
2008
|
|
|
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
2009
|
// ../protocol/src/phone.ts
|
|
1895
2010
|
var PHONE_PROTOCOL = 1;
|
|
1896
2011
|
var PAD_TIERS = [1024, 4096, 16384, 65536, 262144, 655360];
|
|
@@ -1928,7 +2043,7 @@ var RiskSchema = z26.enum(["low", "high"]);
|
|
|
1928
2043
|
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
2044
|
var OperationIdSchema = z26.string().uuid();
|
|
1930
2045
|
var OperationStatusSchema = z26.enum(["received", "applied", "rejected", "failed"]);
|
|
1931
|
-
var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "
|
|
2046
|
+
var OperationReasonSchema = z26.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "scheduled", ...ModelRefusalSchema.options, ...RIG_RESULT_CODES]);
|
|
1932
2047
|
var CapabilitiesSchema = z26.object({
|
|
1933
2048
|
/** A question can be answered in free text. */
|
|
1934
2049
|
freeTextAnswer: z26.boolean(),
|
|
@@ -1961,19 +2076,21 @@ var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
|
1961
2076
|
effort: true,
|
|
1962
2077
|
modelNext: true,
|
|
1963
2078
|
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
2079
|
/** Its context and its relief (context handover spec §3.2): the phone shows the gauge; the relief is started from the PC. */
|
|
1969
2080
|
context: true,
|
|
1970
|
-
relieves: true
|
|
2081
|
+
relieves: true,
|
|
2082
|
+
/** When it ended: « Terminée · à 18:42 » in the list. */
|
|
2083
|
+
exitedAt: true
|
|
1971
2084
|
}).extend({
|
|
1972
2085
|
pending: PhonePendingSchema.optional(),
|
|
1973
2086
|
unread: z26.boolean(),
|
|
1974
2087
|
handover: z26.object({ state: HandoverStateSchema, toName: z26.string().max(80).optional() }).strict().optional(),
|
|
1975
2088
|
/** Absent: the agent has no checklist. */
|
|
1976
|
-
checklist: PhoneChecklistSummarySchema.optional()
|
|
2089
|
+
checklist: PhoneChecklistSummarySchema.optional(),
|
|
2090
|
+
/** Started without Ledge (`hook_only`): the phone can neither close nor resume it. */
|
|
2091
|
+
readOnly: z26.literal(true).optional(),
|
|
2092
|
+
rig: z26.object({ rigName: z26.string().max(80), seat: z26.string().max(120) }).strict().optional(),
|
|
2093
|
+
brain: z26.literal(true).optional()
|
|
1977
2094
|
});
|
|
1978
2095
|
var B64U2 = z26.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1979
2096
|
var CredentialSchema = z26.object({
|
|
@@ -1990,8 +2107,7 @@ var AssertionSchema = z26.object({
|
|
|
1990
2107
|
authenticatorData: B64U2.max(4096),
|
|
1991
2108
|
signature: B64U2.max(1024)
|
|
1992
2109
|
});
|
|
1993
|
-
var
|
|
1994
|
-
var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "define", "team", "pc"]);
|
|
2110
|
+
var ConfirmPurposeSchema = z26.enum(["register", "decide", "launch", "close", "act", "pc", "rig"]);
|
|
1995
2111
|
var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
|
|
1996
2112
|
var PhoneChatProposalSchema = z26.object({
|
|
1997
2113
|
project: z26.string().max(80),
|
|
@@ -2009,33 +2125,15 @@ var PhoneChatRelaySchema = z26.object({
|
|
|
2009
2125
|
sentAt: z26.string().datetime().optional(),
|
|
2010
2126
|
failed: z26.literal(true).optional()
|
|
2011
2127
|
}).strict();
|
|
2128
|
+
var CloseHowSchema = z26.enum(["exit", "killed"]);
|
|
2012
2129
|
var PhoneChatCloseSchema = z26.object({
|
|
2013
2130
|
id: z26.string().min(1),
|
|
2014
2131
|
name: z26.string(),
|
|
2015
2132
|
tool: ToolSchema,
|
|
2016
|
-
how:
|
|
2133
|
+
how: CloseHowSchema.optional(),
|
|
2017
2134
|
closedAt: z26.string().datetime().optional(),
|
|
2018
2135
|
failed: z26.literal(true).optional()
|
|
2019
2136
|
}).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
2137
|
var PhoneChatActionSchema = z26.object({
|
|
2040
2138
|
action: z26.string().min(1).max(40),
|
|
2041
2139
|
summary: z26.string().max(300),
|
|
@@ -2044,46 +2142,10 @@ var PhoneChatActionSchema = z26.object({
|
|
|
2044
2142
|
detailCut: z26.literal(true).optional(),
|
|
2045
2143
|
risk: ActionRiskSchema,
|
|
2046
2144
|
session: z26.string().max(100).optional(),
|
|
2047
|
-
team: z26.string().max(40).optional(),
|
|
2048
2145
|
state: ChatActionStateSchema.optional(),
|
|
2049
2146
|
doneAt: z26.string().datetime().optional(),
|
|
2050
2147
|
code: z26.string().max(40).optional()
|
|
2051
2148
|
}).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
2149
|
var ChatErrorCodeSchema = z26.enum(["busy", "timeout", "cancelled", "failed"]);
|
|
2088
2150
|
var PhoneChatTurnSchema = z26.object({
|
|
2089
2151
|
qid: OperationIdSchema,
|
|
@@ -2097,8 +2159,6 @@ var PhoneChatTurnSchema = z26.object({
|
|
|
2097
2159
|
relay: PhoneChatRelaySchema.optional(),
|
|
2098
2160
|
closes: z26.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2099
2161
|
cut: z26.literal(true).optional(),
|
|
2100
|
-
team: PhoneChatTeamSchema.optional(),
|
|
2101
|
-
tasks: z26.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2102
2162
|
actions: z26.array(PhoneChatActionSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2103
2163
|
ignoredAt: z26.string().datetime().optional(),
|
|
2104
2164
|
attachments: z26.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional()
|
|
@@ -2107,30 +2167,32 @@ var CHAT_HISTORY_BUDGET = 393216;
|
|
|
2107
2167
|
var sessionId = z26.string().min(1).max(100);
|
|
2108
2168
|
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z26.string().min(1).max(64) });
|
|
2109
2169
|
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
|
|
2170
|
+
var signedOp = { op: OperationIdSchema, assertion: AssertionSchema.optional() };
|
|
2111
2171
|
var PcActionSchema = z26.enum(["update", "restart"]);
|
|
2112
2172
|
var pcSubject = (action, version) => action === "update" ? `update@${version ?? ""}` : "restart";
|
|
2113
|
-
var teamsScope = { scope: DefinitionScopeSchema, project: z26.string().min(1).max(80).optional() };
|
|
2114
2173
|
var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
2115
2174
|
z26.object({ type: z26.literal("snapshot.request") }),
|
|
2175
|
+
...phoneOpenRigClientMessages,
|
|
2116
2176
|
z26.object({ type: z26.literal("session.thread"), id: sessionId }),
|
|
2117
2177
|
/** The session's request with its whole body (up to `PENDING_BODY_MAX`). */
|
|
2118
2178
|
z26.object({ type: z26.literal("session.pending"), id: sessionId }),
|
|
2119
2179
|
/** The session whose terminal the phone shows; null when it shows none. */
|
|
2120
2180
|
z26.object({ type: z26.literal("session.watch"), id: sessionId.nullable() }),
|
|
2121
2181
|
z26.object({ type: z26.literal("session.read"), id: sessionId, at: z26.string().datetime() }),
|
|
2122
|
-
/**
|
|
2182
|
+
/**
|
|
2183
|
+
* Asks for a challenge. One object for every purpose: the gateway refuses a `decide` without `id` and `expect`. A `close`
|
|
2184
|
+
* or `act` names a turn of the chat (`qid`) or a session (`id`, for `session.close` and `session.resume`).
|
|
2185
|
+
*/
|
|
2123
2186
|
z26.object({
|
|
2124
2187
|
type: z26.literal("confirm.prepare"),
|
|
2125
2188
|
purpose: ConfirmPurposeSchema,
|
|
2126
2189
|
id: sessionId.optional(),
|
|
2127
2190
|
expect: PhoneExpectSchema.optional(),
|
|
2128
2191
|
qid: OperationIdSchema.optional(),
|
|
2129
|
-
/** sha256 of the `define` or `team` operation the challenge is for (`teamsDigest`). */
|
|
2130
|
-
digest: TeamsDigestSchema.optional(),
|
|
2131
2192
|
/** A `pc` challenge: what it is for, and for an update the version it covers. */
|
|
2132
2193
|
action: PcActionSchema.optional(),
|
|
2133
|
-
version: z26.string().regex(SEMVER).optional()
|
|
2194
|
+
version: z26.string().regex(SEMVER).optional(),
|
|
2195
|
+
rig: z26.discriminatedUnion("type", [PhoneRigLaunchSchema, PhoneRigActSchema]).optional()
|
|
2134
2196
|
}),
|
|
2135
2197
|
z26.object({ type: z26.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
|
|
2136
2198
|
z26.object({
|
|
@@ -2165,6 +2227,10 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2165
2227
|
expect: PhoneExpectSchema
|
|
2166
2228
|
}),
|
|
2167
2229
|
z26.object({ type: z26.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
|
|
2230
|
+
/** Ends a live session as `/exit` would, killed as a last resort; answered with its `how`. Passkey, purpose `close`, over the session. */
|
|
2231
|
+
z26.object({ type: z26.literal("session.close"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
2232
|
+
/** Starts an ended session again on its conversation. Passkey, purpose `act`, over the session. */
|
|
2233
|
+
z26.object({ type: z26.literal("session.resume"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
2168
2234
|
/** 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
2235
|
z26.object({ type: z26.literal("session.model"), op: OperationIdSchema, id: sessionId, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
2170
2236
|
/** The models an agent offers on the PC. */
|
|
@@ -2185,10 +2251,6 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2185
2251
|
z26.object({ type: z26.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
2186
2252
|
z26.object({ type: z26.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2187
2253
|
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
2254
|
/** Rows of the turn's generic card (action registry spec §5): `confirmed` as the local `chat.act`; a row whose action asks
|
|
2193
2255
|
* for a device check (`phone`) needs one assertion, purpose `act`, over the turn. */
|
|
2194
2256
|
z26.object({
|
|
@@ -2203,33 +2265,13 @@ var PhoneClientMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2203
2265
|
}),
|
|
2204
2266
|
/** « Ignorer » the turn's cards, or `undo` (« Revoir »), as the local `chat.ignore`. */
|
|
2205
2267
|
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
2268
|
/** Whether the phone's app is in front: a device that is visible gets no push. */
|
|
2211
2269
|
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
2270
|
/** 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"), ...
|
|
2271
|
+
z26.object({ type: z26.literal("daemon.update"), ...signedOp, version: z26.string().regex(SEMVER) }).strict(),
|
|
2227
2272
|
/** 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
|
-
});
|
|
2273
|
+
z26.object({ type: z26.literal("daemon.restart"), ...signedOp }).strict()
|
|
2274
|
+
]);
|
|
2233
2275
|
var PhoneModelChoiceSchema = z26.object({
|
|
2234
2276
|
id: ModelChoiceSchema.shape.id.max(120),
|
|
2235
2277
|
label: z26.string().max(80),
|
|
@@ -2238,30 +2280,31 @@ var PhoneModelChoiceSchema = z26.object({
|
|
|
2238
2280
|
defaultEffort: EffortSchema.optional(),
|
|
2239
2281
|
isDefault: z26.boolean().optional()
|
|
2240
2282
|
}).strict();
|
|
2241
|
-
var PhoneSeatModelSchema = z26.object({ role: z26.string().max(40), session: sessionId, status: z26.enum(["applied", "scheduled", "refused"]), reason: ModelRefusalSchema.optional() });
|
|
2242
2283
|
var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
2243
2284
|
z26.object({ type: z26.literal("snapshot"), protocol: z26.literal(PHONE_PROTOCOL), sessions: z26.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z26.object({
|
|
2244
2285
|
version: z26.string(),
|
|
2245
2286
|
name: z26.string().max(40),
|
|
2246
2287
|
boot: z26.string().uuid().optional(),
|
|
2247
|
-
teams: z26.literal(true).optional(),
|
|
2248
2288
|
/** The daemon takes `session.model`. */
|
|
2249
2289
|
models: z26.literal(true).optional(),
|
|
2250
2290
|
/** The daemon sends `session.checklist` and a session's `checklist` summary. */
|
|
2251
2291
|
checklist: z26.literal(true).optional(),
|
|
2252
2292
|
/** `update`: the daemon can install a version and restart (npm, systemd); `restart`: it can restart (systemd). */
|
|
2253
2293
|
update: z26.literal(true).optional(),
|
|
2254
|
-
restart: z26.literal(true).optional()
|
|
2255
|
-
|
|
2294
|
+
restart: z26.literal(true).optional(),
|
|
2295
|
+
/** The daemon takes `session.close` and `session.resume`. */
|
|
2296
|
+
sessionClose: z26.literal(true).optional(),
|
|
2297
|
+
openrig: z26.literal(true).optional(),
|
|
2298
|
+
answerReceipts: z26.literal(true).optional()
|
|
2299
|
+
}), 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() }),
|
|
2300
|
+
...phoneOpenRigServerMessages,
|
|
2256
2301
|
z26.object({
|
|
2257
2302
|
type: z26.literal("confirm.challenge"),
|
|
2258
2303
|
purpose: ConfirmPurposeSchema,
|
|
2259
2304
|
challenge: B64U2.max(64),
|
|
2260
2305
|
rpId: z26.string().max(253),
|
|
2261
2306
|
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()
|
|
2307
|
+
expiresInMs: z26.number().int().positive()
|
|
2265
2308
|
}),
|
|
2266
2309
|
z26.object({ type: z26.literal("session.updated"), session: PhoneSessionSchema }),
|
|
2267
2310
|
z26.object({ type: z26.literal("session.removed"), id: z26.string() }),
|
|
@@ -2273,7 +2316,14 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2273
2316
|
/** `cut`: the ids of the items shortened; `more`: older items were left out. */
|
|
2274
2317
|
z26.object({ type: z26.literal("session.thread"), id: z26.string(), items: z26.array(ThreadItemSchema), cut: z26.array(z26.string()), more: z26.boolean() }),
|
|
2275
2318
|
z26.object({ type: z26.literal("session.thread.item"), id: z26.string(), item: ThreadItemSchema, cut: z26.literal(true).optional() }),
|
|
2276
|
-
z26.object({
|
|
2319
|
+
z26.object({
|
|
2320
|
+
type: z26.literal("operation.result"),
|
|
2321
|
+
op: OperationIdSchema,
|
|
2322
|
+
status: OperationStatusSchema,
|
|
2323
|
+
reason: OperationReasonSchema.optional(),
|
|
2324
|
+
/** How an applied `session.close` ended the agent: `killed` when it had to be forced. */
|
|
2325
|
+
how: CloseHowSchema.optional()
|
|
2326
|
+
}),
|
|
2277
2327
|
/** The models an agent offers; `error`: it could not be asked, `models` is then what Ledge knows without it. */
|
|
2278
2328
|
z26.object({ type: z26.literal("models.listing"), tool: ToolSchema, models: z26.array(PhoneModelChoiceSchema).max(40), error: z26.literal(true).optional() }),
|
|
2279
2329
|
/** A model change that was accepted and then failed (the agent could not restart). */
|
|
@@ -2284,30 +2334,26 @@ var PhoneServerMessageSchema = z26.discriminatedUnion("type", [
|
|
|
2284
2334
|
z26.object({ type: z26.literal("chat.delta"), qid: OperationIdSchema, text: z26.string().max(THREAD_TEXT_MAX) }),
|
|
2285
2335
|
z26.object({ type: z26.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
|
|
2286
2336
|
z26.object({ type: z26.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
|
|
2287
|
-
z26.object({ type: z26.literal("teams.catalog"), catalog: PhoneCatalogSchema }),
|
|
2288
2337
|
/** The daemon updating or restarting itself, whoever asked: npm runs, then the service restarts (the link drops right after). */
|
|
2289
2338
|
z26.object({ type: z26.literal("daemon.progress"), version: z26.string().max(40), step: z26.enum(["installing", "restarting"]) }),
|
|
2290
2339
|
/** Nothing was changed. Never npm's output nor a command: both hold the PC's paths. */
|
|
2291
2340
|
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
2341
|
AttachmentStoredSchema,
|
|
2299
2342
|
AttachmentDataSchema,
|
|
2300
2343
|
AttachmentErrorSchema,
|
|
2301
2344
|
/** Never carries free text. */
|
|
2302
2345
|
z26.object({ type: z26.literal("error"), code: z26.enum(["invalid", "unknown_session", "too_large"]), request: z26.string().max(40).optional() })
|
|
2303
2346
|
]);
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2347
|
+
function canonicalJson(v) {
|
|
2348
|
+
const sort = (x) => {
|
|
2349
|
+
if (Array.isArray(x)) return x.map(sort);
|
|
2350
|
+
if (x !== null && typeof x === "object") {
|
|
2351
|
+
const o = x;
|
|
2352
|
+
return Object.fromEntries(Object.keys(o).sort().filter((k) => o[k] !== void 0).map((k) => [k, sort(o[k])]));
|
|
2353
|
+
}
|
|
2354
|
+
return x;
|
|
2355
|
+
};
|
|
2356
|
+
return JSON.stringify(sort(v));
|
|
2311
2357
|
}
|
|
2312
2358
|
|
|
2313
2359
|
// ../protocol/src/todos.ts
|
|
@@ -2364,130 +2410,56 @@ function latestTodos(items, helperId) {
|
|
|
2364
2410
|
}
|
|
2365
2411
|
var doneCount = (todos) => todos.filter((x) => x.status === "completed").length;
|
|
2366
2412
|
|
|
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;
|
|
2413
|
+
// ../protocol/src/mandate.ts
|
|
2414
|
+
var SELF_TITLE = "ledge tranche seul";
|
|
2415
|
+
var USER_TITLE = "te revient toujours";
|
|
2416
|
+
var isAlways = (id2) => ALWAYS_USER.includes(id2);
|
|
2417
|
+
function mandateEntries(text2) {
|
|
2418
|
+
const out = { self: [], user: [], always: [], readable: false };
|
|
2419
|
+
let list = null;
|
|
2420
|
+
for (const raw of text2.split("\n")) {
|
|
2421
|
+
const line = raw.trim();
|
|
2422
|
+
if (line.startsWith("#")) {
|
|
2423
|
+
const title = line.replace(/^#+\s*/, "").toLowerCase();
|
|
2424
|
+
list = title.startsWith(SELF_TITLE) ? "self" : title.startsWith(USER_TITLE) ? "user" : null;
|
|
2425
|
+
if (list) out.readable = true;
|
|
2426
|
+
continue;
|
|
2466
2427
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2428
|
+
const item = /^[-*]\s+(.+)$/.exec(line)?.[1];
|
|
2429
|
+
if (!list || !item) continue;
|
|
2430
|
+
const m = /^(\S+)\s+:\s+(.+)$/.exec(item);
|
|
2431
|
+
const entry = { id: m?.[1], said: (m?.[2] ?? item).trim() };
|
|
2432
|
+
if (list === "self" && entry.id && isAlways(entry.id)) out.always.push(entry);
|
|
2433
|
+
else out[list].push(entry);
|
|
2469
2434
|
}
|
|
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;
|
|
2435
|
+
return out;
|
|
2480
2436
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
}
|
|
2437
|
+
var known = (id2) => {
|
|
2438
|
+
const r = MandateCategorySchema.safeParse(id2);
|
|
2439
|
+
return r.success ? r.data : null;
|
|
2440
|
+
};
|
|
2441
|
+
function parseMandate(text2) {
|
|
2442
|
+
const e = mandateEntries(text2);
|
|
2443
|
+
if (!e.readable) return { self: [], user: [], readable: false };
|
|
2444
|
+
const uniq = (xs) => [...new Set(xs.filter((x) => x !== null))];
|
|
2445
|
+
const user = uniq([...e.user, ...e.always].map((x) => known(x.id)));
|
|
2446
|
+
return { self: uniq(e.self.map((x) => known(x.id))).filter((c) => !user.includes(c)), user, readable: true };
|
|
2488
2447
|
}
|
|
2489
2448
|
|
|
2490
2449
|
export {
|
|
2450
|
+
tokenEquals,
|
|
2451
|
+
hostAllowed,
|
|
2452
|
+
requestAuthorized,
|
|
2453
|
+
selectDaemonProtocol,
|
|
2454
|
+
ensureDaemonToken,
|
|
2455
|
+
SESSION_KEY_VAR,
|
|
2456
|
+
sessionKey,
|
|
2457
|
+
readDaemonToken,
|
|
2458
|
+
ASK_QUESTION_MAX,
|
|
2459
|
+
ASK_OPTIONS_MAX,
|
|
2460
|
+
ASK_ANSWER_MAX,
|
|
2461
|
+
AskSchema,
|
|
2462
|
+
askShown,
|
|
2491
2463
|
ATTACHMENT_ID,
|
|
2492
2464
|
ATTACHMENT_MAX,
|
|
2493
2465
|
ATTACHMENT_LOCAL_MAX,
|
|
@@ -2498,6 +2470,7 @@ export {
|
|
|
2498
2470
|
CONTEXT_NEAR_MIN,
|
|
2499
2471
|
CONTEXT_NEAR_MAX,
|
|
2500
2472
|
ContextPrefsSchema,
|
|
2473
|
+
contextShare,
|
|
2501
2474
|
contextNear,
|
|
2502
2475
|
BRIEF_MAX,
|
|
2503
2476
|
ToolSchema,
|
|
@@ -2512,34 +2485,12 @@ export {
|
|
|
2512
2485
|
extractProposal,
|
|
2513
2486
|
parseRelayBlock,
|
|
2514
2487
|
parseCloseBlock,
|
|
2515
|
-
parseTeamBlock,
|
|
2516
|
-
parseTaskBlock,
|
|
2517
2488
|
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
2489
|
NOTE_MAX,
|
|
2534
2490
|
NOTE_NOT_TYPED,
|
|
2535
2491
|
offers,
|
|
2536
2492
|
pendingKey,
|
|
2537
|
-
|
|
2538
|
-
TASK_TEXT_MAX,
|
|
2539
|
-
LEDGE_GIVER,
|
|
2540
|
-
transmissions,
|
|
2541
|
-
givenByLedge,
|
|
2542
|
-
taskClosed,
|
|
2493
|
+
ActionIdSchema,
|
|
2543
2494
|
ACTIONS,
|
|
2544
2495
|
actionById,
|
|
2545
2496
|
riskOf,
|
|
@@ -2552,10 +2503,29 @@ export {
|
|
|
2552
2503
|
ChatTurnSchema,
|
|
2553
2504
|
proposalRows,
|
|
2554
2505
|
DOCUMENT_MAX_BYTES,
|
|
2506
|
+
SshAliasSchema,
|
|
2507
|
+
MachinesSchema,
|
|
2555
2508
|
IslandPrefsSchema,
|
|
2509
|
+
PROJECT_COLORS,
|
|
2510
|
+
ProjectsSchema,
|
|
2511
|
+
DEFAULT_PROJECTS,
|
|
2512
|
+
projectOf,
|
|
2556
2513
|
RouteResultSchema,
|
|
2557
2514
|
DEFAULT_RELAY_URL,
|
|
2558
2515
|
MobileConfigSchema,
|
|
2516
|
+
MandateCategorySchema,
|
|
2517
|
+
ALWAYS_USER,
|
|
2518
|
+
MANDATE_CATEGORY_LABEL,
|
|
2519
|
+
RIG_NAME,
|
|
2520
|
+
EscalationSchema,
|
|
2521
|
+
EXCHANGES_MAX,
|
|
2522
|
+
EXCHANGE_GROUP_MS,
|
|
2523
|
+
THREAD_PAGE,
|
|
2524
|
+
LedgeDecisionSchema,
|
|
2525
|
+
LedgeRuleSchema,
|
|
2526
|
+
LedgeThreadItemSchema,
|
|
2527
|
+
BRAIN_OPS,
|
|
2528
|
+
BRAIN_STOPPED_TEXT,
|
|
2559
2529
|
PROTOCOL_PORT,
|
|
2560
2530
|
RefusalCodeSchema,
|
|
2561
2531
|
RecentSchema,
|
|
@@ -2575,15 +2545,12 @@ export {
|
|
|
2575
2545
|
RELAY_CLOSE_SIGNED_OUT,
|
|
2576
2546
|
DeviceStartResponseSchema,
|
|
2577
2547
|
DevicePollResponseSchema,
|
|
2548
|
+
rigSubject,
|
|
2578
2549
|
pushEndpointAllowed,
|
|
2579
2550
|
PushSendResultSchema,
|
|
2580
2551
|
PushPayloadSchema,
|
|
2581
|
-
PhoneRoleSchema,
|
|
2582
|
-
PhoneTemplateSchema,
|
|
2583
|
-
TEAMS_OPERATION_TYPES,
|
|
2584
|
-
isTeamsOperation,
|
|
2585
|
-
canonicalJson,
|
|
2586
2552
|
PHONE_PROTOCOL,
|
|
2553
|
+
PLAIN_MAX,
|
|
2587
2554
|
padPlain,
|
|
2588
2555
|
THREAD_ITEMS_MAX,
|
|
2589
2556
|
THREAD_BUDGET,
|
|
@@ -2599,17 +2566,10 @@ export {
|
|
|
2599
2566
|
pcSubject,
|
|
2600
2567
|
PhoneClientMessageSchema,
|
|
2601
2568
|
PhoneModelChoiceSchema,
|
|
2602
|
-
|
|
2603
|
-
teamsDigest,
|
|
2569
|
+
canonicalJson,
|
|
2604
2570
|
TODO_TOOLS,
|
|
2605
2571
|
latestTodos,
|
|
2606
2572
|
doneCount,
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
requestAuthorized,
|
|
2610
|
-
selectDaemonProtocol,
|
|
2611
|
-
ensureDaemonToken,
|
|
2612
|
-
SESSION_KEY_VAR,
|
|
2613
|
-
sessionKey,
|
|
2614
|
-
readDaemonToken
|
|
2573
|
+
mandateEntries,
|
|
2574
|
+
parseMandate
|
|
2615
2575
|
};
|