@ccmsg/protocol 1.16.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccmsg/protocol",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Wire contract (schema + types + op attribute table) shared by the ccmsg daemon and web UI",
5
5
  "license": "MIT",
6
6
  "author": "kawaz",
@@ -38,8 +38,12 @@ export type TranscriptItemType = Static<typeof TranscriptItemType>;
38
38
  export const TRANSCRIPT_ITEM_TYPES = [
39
39
  "message:user:in",
40
40
  "message:user:out",
41
+ "message:parent:in",
42
+ "message:parent:out",
41
43
  "message:sub:in",
42
44
  "message:sub:out",
45
+ "message:team:in",
46
+ "message:team:out",
43
47
  "message:session:in",
44
48
  "message:session:out",
45
49
  "thinking",
@@ -156,12 +160,95 @@ const Text = Type.String();
156
160
 
157
161
  /** The subject is a session by default, or one agent below it when the dump
158
162
  * names an agent, and `in` / `out` are read from wherever the subject stands.
159
- * A dump of an agent therefore reads `message:user:in` as the brief its parent
160
- * gave it, on the same type names a dump of the session uses which is what
161
- * lets one preset be carried down a chain of agents. */
163
+ * What moves when the subject moves is who the counterpart is, not the names:
164
+ * the same preset reads a session's talk with a person and an agent's talk with
165
+ * whoever started it, which is what lets one be carried down a chain of agents.
166
+ *
167
+ * The counterpart is named by the kind of party it is — a person, the one above,
168
+ * the throwaway agents below, a teammate that stays, another session — because a
169
+ * dump is read to find out who was talking, and a subject's own position is the
170
+ * one thing it cannot ask about itself. Hence `parent` rather than `user` for
171
+ * the one above: an agent's parent is a session or another agent, and calling it
172
+ * `user` would have a reader take a machine for a person.
173
+ *
174
+ * `message:user` is a person and nobody else. Both directions occur under a
175
+ * session and under a teammate, which someone can type at directly; under a
176
+ * throwaway agent neither does. A combination a subject is not expected to show
177
+ * — `team` below an agent, say — is not refused: an unexpected line is still a
178
+ * line, and it is emitted under the name it fits. */
162
179
  const MessageUserIn = item(Type.Literal("message:user:in"), { text: Text });
163
180
  const MessageUserOut = item(Type.Literal("message:user:out"), { text: Text });
164
181
 
182
+ /** What the one above said, and what was said back to it.
183
+ *
184
+ * An agent's first line is the brief it was started with, and its last is the
185
+ * answer that brief is discharged by; in between it may hand its parent
186
+ * something mid-flight. The answer is plain prose the harness collects, with no
187
+ * call behind it, so `parent:out` is prose-or-call and not a call alone:
188
+ * addressed to the parent is what the two forms have in common, and requiring a
189
+ * `tool_use_id` would leave the one message an agent is certain to send
190
+ * unnameable. */
191
+ const MessageParentIn = item(Type.Literal("message:parent:in"), {
192
+ text: Text,
193
+ /** The parent as the harness named it where this arrived — `main`, a lead's
194
+ * name, the agent above. Left out when the record says only that it came from
195
+ * above, which is the case for the brief an agent opens with. */
196
+ from: Type.Optional(Type.String()),
197
+ msg_id: Type.Optional(Type.String()),
198
+ });
199
+
200
+ /** Sent to the parent through a call, which the parent's own transcript has the
201
+ * other half of. */
202
+ const MessageParentOutSent = item(Type.Literal("message:parent:out"), {
203
+ ...USE_FIELDS,
204
+ text: Text,
205
+ /** The parent as the subject addressed it, in the harness's spelling. */
206
+ to: Type.Optional(Type.String()),
207
+ summary: Type.Optional(Type.String()),
208
+ });
209
+
210
+ /** Answered to the parent as prose — the agent's reply, final or interim. */
211
+ const MessageParentOutSaid = item(Type.Literal("message:parent:out"), { text: Text });
212
+
213
+ /** A teammate is an agent that was given a name and goes on standing, so what
214
+ * passes between the subject and one is a correspondence rather than an errand:
215
+ * a reply comes back as its own message, addressed and arriving whenever it is
216
+ * written, and not as the answer to the call that sent it.
217
+ *
218
+ * That is the whole of what separates `team` from `sub`. A throwaway agent is
219
+ * started, answers once and is done, which is why `sub:in` is the result of the
220
+ * `sub:out` that started it. Here the two halves of a round trip are two
221
+ * messages, and only the start of a teammate has a result to pair with. */
222
+ const MessageTeamOut = item(Type.Literal("message:team:out"), {
223
+ ...USE_FIELDS,
224
+ text: Text,
225
+ /** The teammate addressed, by the name it stands under. */
226
+ to: Type.Optional(Type.String()),
227
+ summary: Type.Optional(Type.String()),
228
+ agent_id: Type.Optional(Type.String()),
229
+ /** Present on the call that started the teammate, absent on the ones that
230
+ * write to it afterwards. */
231
+ subagent_type: Type.Optional(Type.String()),
232
+ description: Type.Optional(Type.String()),
233
+ });
234
+
235
+ /** A teammate writing to the subject, arriving under its own name whenever it
236
+ * was written. */
237
+ const MessageTeamInSaid = item(Type.Literal("message:team:in"), {
238
+ text: Text,
239
+ from: Type.Optional(Type.String()),
240
+ msg_id: Type.Optional(Type.String()),
241
+ });
242
+
243
+ /** A teammate's run ending, which answers the call that started it. */
244
+ const MessageTeamInDone = item(Type.Literal("message:team:in"), {
245
+ ...RESULT_FIELDS,
246
+ text: Text,
247
+ agent_id: Type.Optional(Type.String()),
248
+ status: Type.Optional(Type.String()),
249
+ duration_ms: Type.Optional(Type.Integer({ minimum: 0 })),
250
+ });
251
+
165
252
  const MessageSubOut = item(Type.Literal("message:sub:out"), {
166
253
  ...USE_FIELDS,
167
254
  prompt: Text,
@@ -373,8 +460,14 @@ export const TranscriptItem = Type.Union(
373
460
  [
374
461
  MessageUserIn,
375
462
  MessageUserOut,
463
+ MessageParentIn,
464
+ MessageParentOutSent,
465
+ MessageParentOutSaid,
376
466
  MessageSubOut,
377
467
  MessageSubIn,
468
+ MessageTeamOut,
469
+ MessageTeamInSaid,
470
+ MessageTeamInDone,
378
471
  MessageSessionOut,
379
472
  MessageSessionIn,
380
473
  Thinking,
@@ -213,7 +213,9 @@ export const DUMP_PRESETS_READ_RESPONSE: Static<typeof DumpPresetsReadResponse>
213
213
  {
214
214
  name: "howto",
215
215
  description: "how the work was done: what was thought, run, read and written",
216
- opts: { types: ["thinking", "message:user", "message:sub", "tool:Bash", "@file"] },
216
+ opts: {
217
+ types: ["thinking", "message:user", "message:parent", "message:sub", "tool:Bash", "@file"],
218
+ },
217
219
  },
218
220
  ],
219
221
  };
@@ -298,6 +300,80 @@ export const TRANSCRIPT_ITEMS: Static<typeof TranscriptItem>[] = [
298
300
  at: FIXTURE_NOW - 3_100_000,
299
301
  attachment: { type: "queued_command", command: "/pre-clear" },
300
302
  },
303
+ {
304
+ id: "c8a2f371:0",
305
+ uuid: "c8a2f371",
306
+ source: { offset: 182_800, bytes: 520 },
307
+ type: "message:team:out",
308
+ at: FIXTURE_NOW - 3_000_000,
309
+ role: "use",
310
+ result_item: "d4c1a0b2:0",
311
+ tool_use_id: "toolu_01Tm5XYp",
312
+ text: "契約に message:parent と message:team を足して",
313
+ to: "contract-dump-items",
314
+ agent_id: "b83e0f114",
315
+ subagent_type: "opus5-worker-high",
316
+ },
317
+ {
318
+ id: "e5b70c93:0",
319
+ uuid: "e5b70c93",
320
+ source: { offset: 183_320, bytes: 300 },
321
+ type: "message:team:in",
322
+ at: FIXTURE_NOW - 2_900_000,
323
+ text: "fixtures まで通ったので ci を回す",
324
+ from: "contract-dump-items",
325
+ },
326
+ {
327
+ id: "d4c1a0b2:0",
328
+ uuid: "d4c1a0b2",
329
+ source: { offset: 183_620, bytes: 410 },
330
+ type: "message:team:in",
331
+ at: FIXTURE_NOW - 2_800_000,
332
+ role: "result",
333
+ parent_item: "c8a2f371:0",
334
+ parent_tool_use_id: "toolu_01Tm5XYp",
335
+ text: "4 型を足して 1.17.0 を切った",
336
+ agent_id: "b83e0f114",
337
+ status: "ok",
338
+ duration_ms: 240_000,
339
+ },
340
+ ];
341
+
342
+ /** The same vocabulary read with an agent as the subject.
343
+ *
344
+ * Nothing here is a new type: the brief an agent opens with and the answer it
345
+ * closes with are what `message:parent` names from wherever it is read, and the
346
+ * answer comes as prose with no call behind it. */
347
+ export const TRANSCRIPT_ITEMS_AGENT_SUBJECT: Static<typeof TranscriptItem>[] = [
348
+ {
349
+ id: "a9f30d15:0",
350
+ uuid: "a9f30d15",
351
+ source: { offset: 0, bytes: 1_240 },
352
+ type: "message:parent:in",
353
+ at: FIXTURE_NOW - 3_290_000,
354
+ turn: 1,
355
+ text: "docs/design/dump-kinds.md を書き直す",
356
+ },
357
+ {
358
+ id: "b0e41c26:0",
359
+ uuid: "b0e41c26",
360
+ source: { offset: 1_240, bytes: 380 },
361
+ type: "message:parent:out",
362
+ at: FIXTURE_NOW - 3_260_000,
363
+ role: "use",
364
+ tool_use_id: "toolu_01Qz8Vbn",
365
+ text: "型一覧は 4 群に分けた。preset の例まで直してよいか",
366
+ to: "main",
367
+ summary: "型一覧の分け方を確認",
368
+ },
369
+ {
370
+ id: "c1f52d37:0",
371
+ uuid: "c1f52d37",
372
+ source: { offset: 1_620, bytes: 690 },
373
+ type: "message:parent:out",
374
+ at: FIXTURE_NOW - 3_200_000,
375
+ text: "型一覧を 4 群に整理し、preset の例も揃えた",
376
+ },
301
377
  ];
302
378
 
303
379
  export const TRANSCRIPT_READ_REQUEST: Static<typeof TranscriptReadRequest> = {