@ccmsg/protocol 1.19.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,8 +13,8 @@ import { Sid, Timestamp } from "../identifiers.ts";
13
13
  * down here. That split is what lets a harness change its file, or a second
14
14
  * harness be read at all, without the contract moving.
15
15
  *
16
- * A type name is `:`-separated and read left to right, so a prefix names
17
- * everything below it: `tool` is every tool, `message:user` is both directions
16
+ * A type name is `.`-separated and read left to right, so a prefix names
17
+ * everything below it: `tool` is every tool, `message.user` is both directions
18
18
  * of what a person and a session said. Three families stay open, because their
19
19
  * last segment is a name someone else coins — a tool, an attachment kind, a
20
20
  * hook event — and closing them would turn every newcomer into `unknown`. */
@@ -22,49 +22,56 @@ import { Sid, Timestamp } from "../identifiers.ts";
22
22
  /** A type name as written on the wire and in a selection.
23
23
  *
24
24
  * Segments after the first carry the spelling of whatever named them, which is
25
- * why they are not held to snake_case: `tool:Bash` and `hook:PreToolUse` are
25
+ * why they are not held to snake_case: `tool.Bash` and `hook.PreToolUse` are
26
26
  * the harness's words, and rewriting them would leave the reader unable to
27
- * match what it sees against what it ran.
27
+ * match what it sees against what it ran. That segment carries no `.` of its
28
+ * own — a harness name holding one is written with `_` by whoever coins the
29
+ * type — so a reader may split any type name on `.` and get the hierarchy.
30
+ *
31
+ * `tool.unknown` is the reserved name for a call whose tool the reader could
32
+ * not name. It is a coined segment like any other, so a harness tool actually
33
+ * called `unknown` lands on it; what is lost is that distinction and nothing
34
+ * else, where dropping the item would lose the call.
28
35
  *
29
36
  * Under `message`, the second segment is **a relation read from the subject**:
30
37
  * `parent` is whoever started this agent, `sub` a throwaway agent it started,
31
38
  * `team` a named counterpart that goes on standing, `session` another session
32
39
  * over ccmsg. The one exception is `user` — a person is not a relation to
33
40
  * anyone, but the user, standing alone. A harness's own name for a party is
34
- * never a type: `message:main` would read as the main session's traffic being
41
+ * never a type: `message.main` would read as the main session's traffic being
35
42
  * overheard wherever it happens, when what is meant is the party this subject
36
43
  * answers to — which is what `parent` says. The literal names (`main`, a
37
44
  * lead's, a teammate's) are kept in `harness_name` on the item. */
38
45
  export const TranscriptItemType = Type.String({
39
- pattern: "^[a-z]+(?::[A-Za-z0-9_.-]+)*$",
46
+ pattern: "^[a-z][a-z0-9_]*(?:\\.[A-Za-z0-9_-]+)*$",
40
47
  $id: "TranscriptItemType",
41
48
  });
42
49
  export type TranscriptItemType = Static<typeof TranscriptItemType>;
43
50
 
44
51
  /** The type names that are fully spelled out here. The three open families
45
- * (`tool:<Name>`, `system:attachment:<kind>`, `hook:<Event>`) are not in the
52
+ * (`tool.<Name>`, `system.attachment.<kind>`, `hook.<Event>`) are not in the
46
53
  * list: their last segment is coined elsewhere, and a name absent from this
47
54
  * list is a newcomer rather than an error. */
48
55
  export const TRANSCRIPT_ITEM_TYPES = [
49
- "message:user:in",
50
- "message:user:out",
51
- "message:parent:in",
52
- "message:parent:out",
53
- "message:sub:in",
54
- "message:sub:out",
55
- "message:team:in",
56
- "message:team:out",
57
- "message:session:in",
58
- "message:session:out",
56
+ "message.user.in",
57
+ "message.user.out",
58
+ "message.parent.in",
59
+ "message.parent.out",
60
+ "message.sub.in",
61
+ "message.sub.out",
62
+ "message.team.in",
63
+ "message.team.out",
64
+ "message.session.in",
65
+ "message.session.out",
59
66
  "thinking",
60
- "notice:slash",
61
- "notice:interrupt",
62
- "system:compact",
63
- "system:api-error",
64
- "system:task",
65
- "system:caveat",
66
- "system:resume",
67
- "system:unknown",
67
+ "notice.slash",
68
+ "notice.interrupt",
69
+ "system.compact",
70
+ "system.api.error",
71
+ "system.task",
72
+ "system.caveat",
73
+ "system.resume",
74
+ "system.unknown",
68
75
  ] as const;
69
76
  export type KnownTranscriptItemType = (typeof TRANSCRIPT_ITEM_TYPES)[number];
70
77
 
@@ -74,7 +81,7 @@ export type KnownTranscriptItemType = (typeof TRANSCRIPT_ITEM_TYPES)[number];
74
81
  * Elements apply left to right, so an exclusion reaches whatever a prefix or an
75
82
  * expansion before it brought in. */
76
83
  export const TranscriptItemSelector = Type.String({
77
- pattern: "^-?(?:@[A-Za-z0-9][A-Za-z0-9_-]*|[a-z]+(?::[A-Za-z0-9_.-]+)*)$",
84
+ pattern: "^-?(?:@[A-Za-z0-9][A-Za-z0-9_-]*|[a-z][a-z0-9_]*(?:\\.[A-Za-z0-9_-]+)*)$",
78
85
  $id: "TranscriptItemSelector",
79
86
  });
80
87
  export type TranscriptItemSelector = Static<typeof TranscriptItemSelector>;
@@ -84,7 +91,7 @@ export type TranscriptItemSelector = Static<typeof TranscriptItemSelector>;
84
91
  * started to answer once — and `team` a teammate's, an agent that was named and
85
92
  * goes on standing.
86
93
  *
87
- * It is what the relations are read from. `message:parent:in` under a `sub` is
94
+ * It is what the relations are read from. `message.parent.in` under a `sub` is
88
95
  * an errand's brief and under a `team` is what its lead wrote, and an item that
89
96
  * does not say which of the two it stood in can only be placed by whoever
90
97
  * remembers the request that fetched it. Carrying it on the item is what lets
@@ -105,7 +112,7 @@ export type TranscriptSubject = Static<typeof TranscriptSubject>;
105
112
  *
106
113
  * An item is what a reader made of a record, and a reader is fallible: the one
107
114
  * question it cannot answer is what the record actually said. These two numbers
108
- * are that answer's address — `transcript_read` bounded to end at `offset +
115
+ * are that answer's address — `transcript.read` bounded to end at `offset +
109
116
  * bytes` and to carry `bytes` returns the record itself — which is what lets a
110
117
  * client show items and still let a person open the line behind one. Several
111
118
  * items read out of a single record share the address, so what comes back is
@@ -208,24 +215,24 @@ const Text = Type.String();
208
215
  * the one above: an agent's parent is a session or another agent, and calling it
209
216
  * `user` would have a reader take a machine for a person.
210
217
  *
211
- * `message:user` is a person and nobody else. Both directions occur under a
218
+ * `message.user` is a person and nobody else. Both directions occur under a
212
219
  * session and under a teammate, which someone can type at directly; under a
213
220
  * throwaway agent neither does. A combination a subject is not expected to show
214
221
  * — `team` below an agent, say — is not refused: an unexpected line is still a
215
222
  * line, and it is emitted under the name it fits. */
216
- const MessageUserIn = item(Type.Literal("message:user:in"), { text: Text });
217
- const MessageUserOut = item(Type.Literal("message:user:out"), { text: Text });
223
+ const MessageUserIn = item(Type.Literal("message.user.in"), { text: Text });
224
+ const MessageUserOut = item(Type.Literal("message.user.out"), { text: Text });
218
225
 
219
226
  /** What the one above said, and what was said back to it.
220
227
  *
221
228
  * An agent's first line is the brief it was started with, and its last is the
222
229
  * answer that brief is discharged by; in between it may hand its parent
223
230
  * something mid-flight. The answer is plain prose the harness collects, with no
224
- * call behind it, so `parent:out` is prose-or-call and not a call alone:
231
+ * call behind it, so `parent.out` is prose-or-call and not a call alone:
225
232
  * addressed to the parent is what the two forms have in common, and requiring a
226
233
  * `tool_use_id` would leave the one message an agent is certain to send
227
234
  * unnameable. */
228
- const MessageParentIn = item(Type.Literal("message:parent:in"), {
235
+ const MessageParentIn = item(Type.Literal("message.parent.in"), {
229
236
  text: Text,
230
237
  /** The harness's own name for the parent — `main`, a lead's name, the agent
231
238
  * above. Left out when the record says only that it came from above, which is
@@ -236,7 +243,7 @@ const MessageParentIn = item(Type.Literal("message:parent:in"), {
236
243
 
237
244
  /** Sent to the parent through a call, which the parent's own transcript has the
238
245
  * other half of. */
239
- const MessageParentOutSent = item(Type.Literal("message:parent:out"), {
246
+ const MessageParentOutSent = item(Type.Literal("message.parent.out"), {
240
247
  ...USE_FIELDS,
241
248
  text: Text,
242
249
  /** The harness's own name for the parent, as the subject addressed it. */
@@ -245,7 +252,7 @@ const MessageParentOutSent = item(Type.Literal("message:parent:out"), {
245
252
  });
246
253
 
247
254
  /** Answered to the parent as prose — the agent's reply, final or interim. */
248
- const MessageParentOutSaid = item(Type.Literal("message:parent:out"), { text: Text });
255
+ const MessageParentOutSaid = item(Type.Literal("message.parent.out"), { text: Text });
249
256
 
250
257
  /** A teammate is an agent that was given a name and goes on standing, so what
251
258
  * passes between the subject and one is a correspondence rather than an errand:
@@ -253,10 +260,10 @@ const MessageParentOutSaid = item(Type.Literal("message:parent:out"), { text: Te
253
260
  * written, and not as the answer to the call that sent it.
254
261
  *
255
262
  * That is the whole of what separates `team` from `sub`. A throwaway agent is
256
- * started, answers once and is done, which is why `sub:in` is the result of the
257
- * `sub:out` that started it. Here the two halves of a round trip are two
263
+ * started, answers once and is done, which is why `sub.in` is the result of the
264
+ * `sub.out` that started it. Here the two halves of a round trip are two
258
265
  * messages, and only the start of a teammate has a result to pair with. */
259
- const MessageTeamOut = item(Type.Literal("message:team:out"), {
266
+ const MessageTeamOut = item(Type.Literal("message.team.out"), {
260
267
  ...USE_FIELDS,
261
268
  text: Text,
262
269
  /** The teammate addressed, by the name it stands under. */
@@ -271,7 +278,7 @@ const MessageTeamOut = item(Type.Literal("message:team:out"), {
271
278
 
272
279
  /** A teammate writing to the subject, arriving under its own name whenever it
273
280
  * was written. */
274
- const MessageTeamInSaid = item(Type.Literal("message:team:in"), {
281
+ const MessageTeamInSaid = item(Type.Literal("message.team.in"), {
275
282
  text: Text,
276
283
  /** The name the teammate stands under. */
277
284
  harness_name: Type.Optional(Type.String()),
@@ -279,7 +286,7 @@ const MessageTeamInSaid = item(Type.Literal("message:team:in"), {
279
286
  });
280
287
 
281
288
  /** A teammate's run ending, which answers the call that started it. */
282
- const MessageTeamInDone = item(Type.Literal("message:team:in"), {
289
+ const MessageTeamInDone = item(Type.Literal("message.team.in"), {
283
290
  ...RESULT_FIELDS,
284
291
  text: Text,
285
292
  harness_name: Type.Optional(Type.String()),
@@ -288,7 +295,7 @@ const MessageTeamInDone = item(Type.Literal("message:team:in"), {
288
295
  duration_ms: Type.Optional(Type.Integer({ minimum: 0 })),
289
296
  });
290
297
 
291
- const MessageSubOut = item(Type.Literal("message:sub:out"), {
298
+ const MessageSubOut = item(Type.Literal("message.sub.out"), {
292
299
  ...USE_FIELDS,
293
300
  prompt: Text,
294
301
  agent_id: Type.Optional(Type.String()),
@@ -298,7 +305,7 @@ const MessageSubOut = item(Type.Literal("message:sub:out"), {
298
305
  description: Type.Optional(Type.String()),
299
306
  });
300
307
 
301
- const MessageSubIn = item(Type.Literal("message:sub:in"), {
308
+ const MessageSubIn = item(Type.Literal("message.sub.in"), {
302
309
  ...RESULT_FIELDS,
303
310
  text: Text,
304
311
  agent_id: Type.Optional(Type.String()),
@@ -306,7 +313,7 @@ const MessageSubIn = item(Type.Literal("message:sub:in"), {
306
313
  duration_ms: Type.Optional(Type.Integer({ minimum: 0 })),
307
314
  });
308
315
 
309
- const MessageSessionOut = item(Type.Literal("message:session:out"), {
316
+ const MessageSessionOut = item(Type.Literal("message.session.out"), {
310
317
  text: Text,
311
318
  /** The addressee as the subject wrote it: a sid, or a name that was resolved
312
319
  * to one. Kept unresolved when that is all the transcript says. */
@@ -315,7 +322,7 @@ const MessageSessionOut = item(Type.Literal("message:session:out"), {
315
322
  reply_to: Type.Optional(Type.String()),
316
323
  });
317
324
 
318
- const MessageSessionIn = item(Type.Literal("message:session:in"), {
325
+ const MessageSessionIn = item(Type.Literal("message.session.in"), {
319
326
  text: Text,
320
327
  from: Type.Optional(Type.String()),
321
328
  msg_id: Type.Optional(Type.String()),
@@ -325,52 +332,52 @@ const Thinking = item(Type.Literal("thinking"), { text: Text });
325
332
 
326
333
  // --- notice: a person operated the harness ---
327
334
 
328
- /** Kept apart from `system:*` because these explain a break in the
335
+ /** Kept apart from `system.*` because these explain a break in the
329
336
  * conversation: someone typed a command or stopped a turn. A reader skimming
330
337
  * for why the thread jumps needs them, and can skip what the harness injected
331
338
  * for its own reasons. */
332
- const NoticeSlash = item(Type.Literal("notice:slash"), {
339
+ const NoticeSlash = item(Type.Literal("notice.slash"), {
333
340
  command: Type.String(),
334
341
  args: Type.Optional(Type.String()),
335
342
  stdout: Type.Optional(Type.String()),
336
343
  });
337
- const NoticeInterrupt = item(Type.Literal("notice:interrupt"), {
344
+ const NoticeInterrupt = item(Type.Literal("notice.interrupt"), {
338
345
  text: Type.Optional(Text),
339
346
  });
340
347
 
341
348
  // --- system: the harness talking in someone else's voice ---
342
349
 
343
- const SystemCompact = item(Type.Literal("system:compact"), { text: Text });
344
- const SystemApiError = item(Type.Literal("system:api-error"), { text: Text });
345
- const SystemTask = item(Type.Literal("system:task"), {
350
+ const SystemCompact = item(Type.Literal("system.compact"), { text: Text });
351
+ const SystemApiError = item(Type.Literal("system.api.error"), { text: Text });
352
+ const SystemTask = item(Type.Literal("system.task"), {
346
353
  text: Text,
347
354
  /** The background task or monitor the event came from. */
348
355
  task_id: Type.Optional(Type.String()),
349
356
  event: Type.Optional(Type.String()),
350
357
  });
351
- const SystemCaveat = item(Type.Literal("system:caveat"), { text: Text });
352
- const SystemResume = item(Type.Literal("system:resume"), { text: Text });
358
+ const SystemCaveat = item(Type.Literal("system.caveat"), { text: Text });
359
+ const SystemResume = item(Type.Literal("system.resume"), { text: Text });
353
360
 
354
- /** `system:attachment:<kind>` — the kind is the harness's own word for what it
361
+ /** `system.attachment.<kind>` — the kind is the harness's own word for what it
355
362
  * attached, taken through unchanged so an attachment nobody has seen before
356
363
  * still arrives under its own name instead of collapsing into `unknown`. */
357
- const SystemAttachment = item(Type.String({ pattern: "^system:attachment:[A-Za-z0-9_.-]+$" }), {
364
+ const SystemAttachment = item(Type.String({ pattern: "^system\\.attachment\\.[A-Za-z0-9_-]+$" }), {
358
365
  attachment: Type.Record(Type.String(), Type.Unknown()),
359
366
  });
360
367
 
361
368
  /** What the reader could not place. It is still an item: a line that vanishes
362
369
  * silently is the one failure a dump cannot be read around. */
363
- const SystemUnknown = item(Type.Literal("system:unknown"), {
370
+ const SystemUnknown = item(Type.Literal("system.unknown"), {
364
371
  record: Type.Record(Type.String(), Type.Unknown()),
365
372
  });
366
373
 
367
374
  // --- hook: code the operator installed ---
368
375
 
369
- /** `hook:<Event>` — the event alone, never the matcher. The name a hook runs
376
+ /** `hook.<Event>` — the event alone, never the matcher. The name a hook runs
370
377
  * under is `PreToolUse:Bash`, whose `:` would read as a level of the hierarchy
371
- * and make `hook:PreToolUse` select nothing; the full name is a field instead,
378
+ * and make `hook.PreToolUse` select nothing; the full name is a field instead,
372
379
  * and prefix selection keeps meaning what it says. */
373
- const Hook = item(Type.String({ pattern: "^hook:[A-Za-z0-9_.-]+$" }), {
380
+ const Hook = item(Type.String({ pattern: "^hook\\.[A-Za-z0-9_-]+$" }), {
374
381
  /** The hook's full name, matcher included. */
375
382
  hook_name: Type.String(),
376
383
  outcome: Type.Union([
@@ -387,19 +394,19 @@ const Hook = item(Type.String({ pattern: "^hook:[A-Za-z0-9_.-]+$" }), {
387
394
  tool_use_id: Type.Optional(Type.String()),
388
395
  });
389
396
 
390
- // --- tool: `tool:<Name>`, one item for the call and one for the result ---
397
+ // --- tool: `tool.<Name>`, one item for the call and one for the result ---
391
398
 
392
- const ToolType = Type.String({ pattern: "^tool:[A-Za-z0-9_.-]+$" });
399
+ const ToolType = Type.String({ pattern: "^tool\\.[A-Za-z0-9_-]+$" });
393
400
 
394
401
  function toolUse<N extends string, F extends Record<string, TSchema>>(name: N, fields: F) {
395
- return item(Type.Literal(`tool:${name}` as const), {
402
+ return item(Type.Literal(`tool.${name}` as const), {
396
403
  ...USE_FIELDS,
397
404
  ...fields,
398
405
  });
399
406
  }
400
407
 
401
408
  function toolResult<N extends string, F extends Record<string, TSchema>>(name: N, fields: F) {
402
- return item(Type.Literal(`tool:${name}` as const), {
409
+ return item(Type.Literal(`tool.${name}` as const), {
403
410
  ...RESULT_FIELDS,
404
411
  ...fields,
405
412
  });
@@ -453,7 +460,7 @@ const TOOL_ITEMS = [
453
460
  toolResult("WebFetch", { text: OptText }),
454
461
  toolUse("WebSearch", { query: Type.String() }),
455
462
  toolResult("WebSearch", { results: OptCount }),
456
- /** The same exchange `message:sub:*` carries, seen from the calling side:
463
+ /** The same exchange `message.sub.*` carries, seen from the calling side:
457
464
  * this pair states that an agent was started and how it ended, and what it
458
465
  * answered stays with the message. */
459
466
  toolUse("Agent", {
@@ -493,7 +500,7 @@ const TOOL_ITEMS = [
493
500
  * Items are finer than lines: one assistant record becomes the thinking, the
494
501
  * text and each tool call it held. A reader that does not recognise a record
495
502
  * still emits one — as a tool it has no fields for, an attachment under its own
496
- * kind, or `system:unknown` — so nothing in the file goes missing without
503
+ * kind, or `system.unknown` — so nothing in the file goes missing without
497
504
  * saying so. */
498
505
  export const TranscriptItem = Type.Union(
499
506
  [
@@ -628,5 +635,5 @@ export const DumpPresetsReadResult = Type.Object({
628
635
  });
629
636
  export type DumpPresetsReadResult = Static<typeof DumpPresetsReadResult>;
630
637
 
631
- export const DumpPresetsReadRequest = request("dump_presets_read", DumpPresetsReadArgs);
632
- export const DumpPresetsReadResponse = response("dump_presets_read", DumpPresetsReadResult);
638
+ export const DumpPresetsReadRequest = request("dump.presets.read", DumpPresetsReadArgs);
639
+ export const DumpPresetsReadResponse = response("dump.presets.read", DumpPresetsReadResult);
@@ -70,8 +70,8 @@ export const DirListResult = Type.Object({
70
70
  });
71
71
  export type DirListResult = Static<typeof DirListResult>;
72
72
 
73
- export const DirListRequest = request("dir_list", DirListArgs);
74
- export const DirListResponse = response("dir_list", DirListResult);
73
+ export const DirListRequest = request("dir.list", DirListArgs);
74
+ export const DirListResponse = response("dir.list", DirListResult);
75
75
 
76
76
  export const FileReadArgs = Type.Object({
77
77
  sid: Sid,
@@ -97,14 +97,14 @@ export const FileReadResult = Type.Object({
97
97
  });
98
98
  export type FileReadResult = Static<typeof FileReadResult>;
99
99
 
100
- export const FileReadRequest = request("file_read", FileReadArgs);
101
- export const FileReadResponse = response("file_read", FileReadResult);
100
+ export const FileReadRequest = request("file.read", FileReadArgs);
101
+ export const FileReadResponse = response("file.read", FileReadResult);
102
102
 
103
103
  /** Writes a new file into the session's inbox directory.
104
104
  *
105
105
  * The one place a client may put a file without the user having opened it
106
106
  * first, which is why it takes no `kind`: the destination is fixed and only the
107
- * name within it is the caller's. `file_create` is the general form. */
107
+ * name within it is the caller's. `file.create` is the general form. */
108
108
  export const FileWriteArgs = Type.Object({
109
109
  sid: Sid,
110
110
  /** Relative to the session's working directory. */
@@ -119,12 +119,12 @@ export const FileWriteResult = Type.Object({
119
119
  });
120
120
  export type FileWriteResult = Static<typeof FileWriteResult>;
121
121
 
122
- export const FileWriteRequest = request("file_write", FileWriteArgs);
123
- export const FileWriteResponse = response("file_write", FileWriteResult);
122
+ export const FileWriteRequest = request("file.write", FileWriteArgs);
123
+ export const FileWriteResponse = response("file.write", FileWriteResult);
124
124
 
125
125
  /** Creates a file that does not exist yet.
126
126
  *
127
- * The symmetric partner of `file_edit`: create versus overwrite. It never
127
+ * The symmetric partner of `file.edit`: create versus overwrite. It never
128
128
  * replaces an existing path, and it does not make parent directories. There is
129
129
  * no `external` kind — that allowlist names single files, so it holds no
130
130
  * directory to create in. */
@@ -143,8 +143,8 @@ export const FileCreateResult = Type.Object({
143
143
  });
144
144
  export type FileCreateResult = Static<typeof FileCreateResult>;
145
145
 
146
- export const FileCreateRequest = request("file_create", FileCreateArgs);
147
- export const FileCreateResponse = response("file_create", FileCreateResult);
146
+ export const FileCreateRequest = request("file.create", FileCreateArgs);
147
+ export const FileCreateResponse = response("file.create", FileCreateResult);
148
148
 
149
149
  /** Overwrites an existing text file in place.
150
150
  *
@@ -175,8 +175,8 @@ export const FileEditResult = Type.Object({
175
175
  });
176
176
  export type FileEditResult = Static<typeof FileEditResult>;
177
177
 
178
- export const FileEditRequest = request("file_edit", FileEditArgs);
179
- export const FileEditResponse = response("file_edit", FileEditResult);
178
+ export const FileEditRequest = request("file.edit", FileEditArgs);
179
+ export const FileEditResponse = response("file.edit", FileEditResult);
180
180
 
181
181
  /** Deletes one regular file. Never a directory, never a symlink, never
182
182
  * recursive: this only unlinks files a person could see as a leaf. `external`
@@ -195,8 +195,8 @@ export const FileDeleteResult = Type.Object({
195
195
  });
196
196
  export type FileDeleteResult = Static<typeof FileDeleteResult>;
197
197
 
198
- export const FileDeleteRequest = request("file_delete", FileDeleteArgs);
199
- export const FileDeleteResponse = response("file_delete", FileDeleteResult);
198
+ export const FileDeleteRequest = request("file.delete", FileDeleteArgs);
199
+ export const FileDeleteResponse = response("file.delete", FileDeleteResult);
200
200
 
201
201
  /** Searches for files by name under one browsable root.
202
202
  *
@@ -241,20 +241,20 @@ export const FileFindResult = Type.Object({
241
241
  });
242
242
  export type FileFindResult = Static<typeof FileFindResult>;
243
243
 
244
- export const FileFindRequest = request("file_find", FileFindArgs);
245
- export const FileFindResponse = response("file_find", FileFindResult);
244
+ export const FileFindRequest = request("file.find", FileFindArgs);
245
+ export const FileFindResponse = response("file.find", FileFindResult);
246
246
 
247
247
  /** Asks which of a batch of paths the instance is willing to serve as files.
248
248
  *
249
249
  * A client that has found path-shaped text in a message uses this to decide
250
250
  * which of them to turn into links. Each path is tried against the three
251
251
  * surfaces in turn and the first that admits it and finds a regular file wins. */
252
- export const FileStatBatchArgs = Type.Object({
252
+ export const FileStatArgs = Type.Object({
253
253
  sid: Sid,
254
254
  /** Absolute paths, resolved by the caller. */
255
255
  paths: Type.Array(Type.String()),
256
256
  });
257
- export type FileStatBatchArgs = Static<typeof FileStatBatchArgs>;
257
+ export type FileStatArgs = Static<typeof FileStatArgs>;
258
258
 
259
259
  export const FileStatEntry = Type.Object(
260
260
  {
@@ -266,7 +266,7 @@ export const FileStatEntry = Type.Object(
266
266
  );
267
267
  export type FileStatEntry = Static<typeof FileStatEntry>;
268
268
 
269
- export const FileStatBatchResult = Type.Object({
269
+ export const FileStatResult = Type.Object({
270
270
  /** One slot per requested path, in the same order.
271
271
  *
272
272
  * Design rationale: an unresolved path is `null` rather than being left out,
@@ -278,10 +278,10 @@ export const FileStatBatchResult = Type.Object({
278
278
  * path the caller may not read exists. */
279
279
  results: Type.Array(Type.Union([FileStatEntry, Type.Null()])),
280
280
  });
281
- export type FileStatBatchResult = Static<typeof FileStatBatchResult>;
281
+ export type FileStatResult = Static<typeof FileStatResult>;
282
282
 
283
- export const FileStatBatchRequest = request("file_stat_batch", FileStatBatchArgs);
284
- export const FileStatBatchResponse = response("file_stat_batch", FileStatBatchResult);
283
+ export const FileStatRequest = request("file.stat", FileStatArgs);
284
+ export const FileStatResponse = response("file.stat", FileStatResult);
285
285
 
286
286
  /** Reads the directory tree the launcher may start a session in.
287
287
  *
@@ -319,5 +319,5 @@ export const DirTreeResult = Type.Object({
319
319
  });
320
320
  export type DirTreeResult = Static<typeof DirTreeResult>;
321
321
 
322
- export const DirTreeRequest = request("dir_tree", DirTreeArgs);
323
- export const DirTreeResponse = response("dir_tree", DirTreeResult);
322
+ export const DirTreeRequest = request("dir.tree", DirTreeArgs);
323
+ export const DirTreeResponse = response("dir.tree", DirTreeResult);
@@ -0,0 +1,21 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { InstanceInfo } from "../common/hello.ts";
3
+ import { topicFrame } from "../envelope.ts";
4
+
5
+ /** The `instances` topic: the mesh as one instance sees it, itself included,
6
+ * as `hello` answers it.
7
+ *
8
+ * Carried as a topic so that a link going down is something a subscriber
9
+ * learns where it is already listening, rather than by greeting again to find
10
+ * out.
11
+ *
12
+ * Whole-value per instance: `reachable` is one instance's reading of every
13
+ * link it has, taken together, and two instances may legitimately disagree
14
+ * about the same link — so a frame states one sender's whole view and leaves
15
+ * every other sender's alone. It is apart from `peers` for the same reason it
16
+ * is whole: a mesh view is one value, while a session row is a row, and an
17
+ * entry here may also stand before its instance has an id to be matched by. */
18
+ export const InstancesFrame = topicFrame(
19
+ "instances",
20
+ Type.Object({ instances: Type.Array(InstanceInfo) }),
21
+ );
package/src/control/kv.ts CHANGED
@@ -45,8 +45,8 @@ export const KvReadResult = Type.Object({
45
45
  });
46
46
  export type KvReadResult = Static<typeof KvReadResult>;
47
47
 
48
- export const KvReadRequest = request("kv_read", KvReadArgs);
49
- export const KvReadResponse = response("kv_read", KvReadResult);
48
+ export const KvReadRequest = request("kv.read", KvReadArgs);
49
+ export const KvReadResponse = response("kv.read", KvReadResult);
50
50
 
51
51
  /** Writes one value, replacing whatever the key held. */
52
52
  export const KvWriteArgs = Type.Object({
@@ -68,8 +68,8 @@ export const KvWriteResult = Type.Object({
68
68
  });
69
69
  export type KvWriteResult = Static<typeof KvWriteResult>;
70
70
 
71
- export const KvWriteRequest = request("kv_write", KvWriteArgs);
72
- export const KvWriteResponse = response("kv_write", KvWriteResult);
71
+ export const KvWriteRequest = request("kv.write", KvWriteArgs);
72
+ export const KvWriteResponse = response("kv.write", KvWriteResult);
73
73
 
74
74
  /** Removes one key. A key that was not there is no error: the caller wanted the
75
75
  * namespace to be without it, and it is. */
@@ -79,8 +79,8 @@ export type KvDeleteArgs = Static<typeof KvDeleteArgs>;
79
79
  export const KvDeleteResult = Type.Object({});
80
80
  export type KvDeleteResult = Static<typeof KvDeleteResult>;
81
81
 
82
- export const KvDeleteRequest = request("kv_delete", KvDeleteArgs);
83
- export const KvDeleteResponse = response("kv_delete", KvDeleteResult);
82
+ export const KvDeleteRequest = request("kv.delete", KvDeleteArgs);
83
+ export const KvDeleteResponse = response("kv.delete", KvDeleteResult);
84
84
 
85
85
  /** One entry, in a snapshot or in the change that produced it. */
86
86
  export const KvEntry = Type.Object(
@@ -53,9 +53,9 @@ export const LauncherConfigReadResult = Type.Object({
53
53
  });
54
54
  export type LauncherConfigReadResult = Static<typeof LauncherConfigReadResult>;
55
55
 
56
- export const LauncherConfigReadRequest = request("launcher_config_read", LauncherConfigReadArgs);
56
+ export const LauncherConfigReadRequest = request("launcher.config.read", LauncherConfigReadArgs);
57
57
  export const LauncherConfigReadResponse = response(
58
- "launcher_config_read",
58
+ "launcher.config.read",
59
59
  LauncherConfigReadResult,
60
60
  );
61
61
 
@@ -96,5 +96,5 @@ export const LauncherRunResult = Type.Object({
96
96
  });
97
97
  export type LauncherRunResult = Static<typeof LauncherRunResult>;
98
98
 
99
- export const LauncherRunRequest = request("launcher_run", LauncherRunArgs);
100
- export const LauncherRunResponse = response("launcher_run", LauncherRunResult);
99
+ export const LauncherRunRequest = request("launcher.run", LauncherRunArgs);
100
+ export const LauncherRunResponse = response("launcher.run", LauncherRunResult);
@@ -159,8 +159,8 @@ export const LlmUsageReadResult = Type.Object({
159
159
  });
160
160
  export type LlmUsageReadResult = Static<typeof LlmUsageReadResult>;
161
161
 
162
- export const LlmUsageReadRequest = request("llm_usage_read", LlmUsageReadArgs);
163
- export const LlmUsageReadResponse = response("llm_usage_read", LlmUsageReadResult);
162
+ export const LlmUsageReadRequest = request("llm.usage.read", LlmUsageReadArgs);
163
+ export const LlmUsageReadResponse = response("llm.usage.read", LlmUsageReadResult);
164
164
 
165
165
  // ---------------------------------------------------------------------------
166
166
  // spend
@@ -214,8 +214,8 @@ export const LlmStatsReadResult = Type.Object({
214
214
  });
215
215
  export type LlmStatsReadResult = Static<typeof LlmStatsReadResult>;
216
216
 
217
- export const LlmStatsReadRequest = request("llm_stats_read", LlmStatsReadArgs);
218
- export const LlmStatsReadResponse = response("llm_stats_read", LlmStatsReadResult);
217
+ export const LlmStatsReadRequest = request("llm.stats.read", LlmStatsReadArgs);
218
+ export const LlmStatsReadResponse = response("llm.stats.read", LlmStatsReadResult);
219
219
 
220
220
  // ---------------------------------------------------------------------------
221
221
  // live requests
@@ -328,12 +328,12 @@ export function llmCacheWindowEndAt(info: {
328
328
  return info.received_at + LLM_PROMPT_CACHE_TTL_MS;
329
329
  }
330
330
 
331
- /** The `llm_requests` topic: the newest request per conversation series, always
331
+ /** The `llm.requests` topic: the newest request per conversation series, always
332
332
  * the whole unexpired set rather than the one that just arrived. A client that
333
333
  * starts listening mid-window still needs the countdown that began before it
334
334
  * was there, and one shape serves both that and the live update. An empty set
335
335
  * is a legitimate "no session has a warm cache". */
336
- export const LlmRequestsFrame = topicFrame("llm_requests", Type.Array(LlmRequestInfo));
336
+ export const LlmRequestsFrame = topicFrame("llm.requests", Type.Array(LlmRequestInfo));
337
337
 
338
338
  // ---------------------------------------------------------------------------
339
339
  // upstream health
@@ -503,5 +503,5 @@ export const LlmStatusReport = Type.Object(
503
503
  );
504
504
  export type LlmStatusReport = Static<typeof LlmStatusReport>;
505
505
 
506
- /** The `llm_status` topic. Whole-value: each frame replaces the last. */
507
- export const LlmStatusFrame = topicFrame("llm_status", LlmStatusReport);
506
+ /** The `llm.status` topic. Whole-value: each frame replaces the last. */
507
+ export const LlmStatusFrame = topicFrame("llm.status", LlmStatusReport);