@ccmsg/protocol 1.16.0 → 1.18.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 +1 -1
- package/src/control/dump.ts +109 -4
- package/src/fixtures/control.ts +77 -1
package/package.json
CHANGED
package/src/control/dump.ts
CHANGED
|
@@ -24,7 +24,17 @@ import { Sid, Timestamp } from "../identifiers.ts";
|
|
|
24
24
|
* Segments after the first carry the spelling of whatever named them, which is
|
|
25
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.
|
|
28
|
+
*
|
|
29
|
+
* Under `message`, the second segment is **a relation read from the subject**:
|
|
30
|
+
* `parent` is whoever started this agent, `sub` a throwaway agent it started,
|
|
31
|
+
* `team` a named counterpart that goes on standing, `session` another session
|
|
32
|
+
* over ccmsg. The one exception is `user` — a person is not a relation to
|
|
33
|
+
* 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
|
|
35
|
+
* overheard wherever it happens, when what is meant is the party this subject
|
|
36
|
+
* answers to — which is what `parent` says. The literal names (`main`, a
|
|
37
|
+
* lead's, a teammate's) are kept in `harness_name` on the item. */
|
|
28
38
|
export const TranscriptItemType = Type.String({
|
|
29
39
|
pattern: "^[a-z]+(?::[A-Za-z0-9_.-]+)*$",
|
|
30
40
|
$id: "TranscriptItemType",
|
|
@@ -38,8 +48,12 @@ export type TranscriptItemType = Static<typeof TranscriptItemType>;
|
|
|
38
48
|
export const TRANSCRIPT_ITEM_TYPES = [
|
|
39
49
|
"message:user:in",
|
|
40
50
|
"message:user:out",
|
|
51
|
+
"message:parent:in",
|
|
52
|
+
"message:parent:out",
|
|
41
53
|
"message:sub:in",
|
|
42
54
|
"message:sub:out",
|
|
55
|
+
"message:team:in",
|
|
56
|
+
"message:team:out",
|
|
43
57
|
"message:session:in",
|
|
44
58
|
"message:session:out",
|
|
45
59
|
"thinking",
|
|
@@ -156,12 +170,97 @@ const Text = Type.String();
|
|
|
156
170
|
|
|
157
171
|
/** The subject is a session by default, or one agent below it when the dump
|
|
158
172
|
* names an agent, and `in` / `out` are read from wherever the subject stands.
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* lets one
|
|
173
|
+
* What moves when the subject moves is who the counterpart is, not the names:
|
|
174
|
+
* the same preset reads a session's talk with a person and an agent's talk with
|
|
175
|
+
* whoever started it, which is what lets one be carried down a chain of agents.
|
|
176
|
+
*
|
|
177
|
+
* The counterpart is named by the kind of party it is — a person, the one above,
|
|
178
|
+
* the throwaway agents below, a teammate that stays, another session — because a
|
|
179
|
+
* dump is read to find out who was talking, and a subject's own position is the
|
|
180
|
+
* one thing it cannot ask about itself. Hence `parent` rather than `user` for
|
|
181
|
+
* the one above: an agent's parent is a session or another agent, and calling it
|
|
182
|
+
* `user` would have a reader take a machine for a person.
|
|
183
|
+
*
|
|
184
|
+
* `message:user` is a person and nobody else. Both directions occur under a
|
|
185
|
+
* session and under a teammate, which someone can type at directly; under a
|
|
186
|
+
* throwaway agent neither does. A combination a subject is not expected to show
|
|
187
|
+
* — `team` below an agent, say — is not refused: an unexpected line is still a
|
|
188
|
+
* line, and it is emitted under the name it fits. */
|
|
162
189
|
const MessageUserIn = item(Type.Literal("message:user:in"), { text: Text });
|
|
163
190
|
const MessageUserOut = item(Type.Literal("message:user:out"), { text: Text });
|
|
164
191
|
|
|
192
|
+
/** What the one above said, and what was said back to it.
|
|
193
|
+
*
|
|
194
|
+
* An agent's first line is the brief it was started with, and its last is the
|
|
195
|
+
* answer that brief is discharged by; in between it may hand its parent
|
|
196
|
+
* something mid-flight. The answer is plain prose the harness collects, with no
|
|
197
|
+
* call behind it, so `parent:out` is prose-or-call and not a call alone:
|
|
198
|
+
* addressed to the parent is what the two forms have in common, and requiring a
|
|
199
|
+
* `tool_use_id` would leave the one message an agent is certain to send
|
|
200
|
+
* unnameable. */
|
|
201
|
+
const MessageParentIn = item(Type.Literal("message:parent:in"), {
|
|
202
|
+
text: Text,
|
|
203
|
+
/** The harness's own name for the parent — `main`, a lead's name, the agent
|
|
204
|
+
* above. Left out when the record says only that it came from above, which is
|
|
205
|
+
* the case for the brief an agent opens with. */
|
|
206
|
+
harness_name: Type.Optional(Type.String()),
|
|
207
|
+
msg_id: Type.Optional(Type.String()),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
/** Sent to the parent through a call, which the parent's own transcript has the
|
|
211
|
+
* other half of. */
|
|
212
|
+
const MessageParentOutSent = item(Type.Literal("message:parent:out"), {
|
|
213
|
+
...USE_FIELDS,
|
|
214
|
+
text: Text,
|
|
215
|
+
/** The harness's own name for the parent, as the subject addressed it. */
|
|
216
|
+
harness_name: Type.Optional(Type.String()),
|
|
217
|
+
summary: Type.Optional(Type.String()),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
/** Answered to the parent as prose — the agent's reply, final or interim. */
|
|
221
|
+
const MessageParentOutSaid = item(Type.Literal("message:parent:out"), { text: Text });
|
|
222
|
+
|
|
223
|
+
/** A teammate is an agent that was given a name and goes on standing, so what
|
|
224
|
+
* passes between the subject and one is a correspondence rather than an errand:
|
|
225
|
+
* a reply comes back as its own message, addressed and arriving whenever it is
|
|
226
|
+
* written, and not as the answer to the call that sent it.
|
|
227
|
+
*
|
|
228
|
+
* That is the whole of what separates `team` from `sub`. A throwaway agent is
|
|
229
|
+
* started, answers once and is done, which is why `sub:in` is the result of the
|
|
230
|
+
* `sub:out` that started it. Here the two halves of a round trip are two
|
|
231
|
+
* messages, and only the start of a teammate has a result to pair with. */
|
|
232
|
+
const MessageTeamOut = item(Type.Literal("message:team:out"), {
|
|
233
|
+
...USE_FIELDS,
|
|
234
|
+
text: Text,
|
|
235
|
+
/** The teammate addressed, by the name it stands under. */
|
|
236
|
+
harness_name: Type.Optional(Type.String()),
|
|
237
|
+
summary: Type.Optional(Type.String()),
|
|
238
|
+
agent_id: Type.Optional(Type.String()),
|
|
239
|
+
/** Present on the call that started the teammate, absent on the ones that
|
|
240
|
+
* write to it afterwards. */
|
|
241
|
+
subagent_type: Type.Optional(Type.String()),
|
|
242
|
+
description: Type.Optional(Type.String()),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
/** A teammate writing to the subject, arriving under its own name whenever it
|
|
246
|
+
* was written. */
|
|
247
|
+
const MessageTeamInSaid = item(Type.Literal("message:team:in"), {
|
|
248
|
+
text: Text,
|
|
249
|
+
/** The name the teammate stands under. */
|
|
250
|
+
harness_name: Type.Optional(Type.String()),
|
|
251
|
+
msg_id: Type.Optional(Type.String()),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
/** A teammate's run ending, which answers the call that started it. */
|
|
255
|
+
const MessageTeamInDone = item(Type.Literal("message:team:in"), {
|
|
256
|
+
...RESULT_FIELDS,
|
|
257
|
+
text: Text,
|
|
258
|
+
harness_name: Type.Optional(Type.String()),
|
|
259
|
+
agent_id: Type.Optional(Type.String()),
|
|
260
|
+
status: Type.Optional(Type.String()),
|
|
261
|
+
duration_ms: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
262
|
+
});
|
|
263
|
+
|
|
165
264
|
const MessageSubOut = item(Type.Literal("message:sub:out"), {
|
|
166
265
|
...USE_FIELDS,
|
|
167
266
|
prompt: Text,
|
|
@@ -373,8 +472,14 @@ export const TranscriptItem = Type.Union(
|
|
|
373
472
|
[
|
|
374
473
|
MessageUserIn,
|
|
375
474
|
MessageUserOut,
|
|
475
|
+
MessageParentIn,
|
|
476
|
+
MessageParentOutSent,
|
|
477
|
+
MessageParentOutSaid,
|
|
376
478
|
MessageSubOut,
|
|
377
479
|
MessageSubIn,
|
|
480
|
+
MessageTeamOut,
|
|
481
|
+
MessageTeamInSaid,
|
|
482
|
+
MessageTeamInDone,
|
|
378
483
|
MessageSessionOut,
|
|
379
484
|
MessageSessionIn,
|
|
380
485
|
Thinking,
|
package/src/fixtures/control.ts
CHANGED
|
@@ -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: {
|
|
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
|
+
harness_name: "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
|
+
harness_name: "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
|
+
harness_name: "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> = {
|