@agentic-kit/run-log 0.5.0 → 0.7.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/README.md +0 -4
- package/esm/file-store.d.ts +2 -2
- package/esm/index.d.ts +6 -0
- package/esm/index.js +5 -0
- package/esm/mirror.d.ts +6 -6
- package/esm/mirror.js +5 -2
- package/esm/projectors/events.d.ts +31 -0
- package/esm/projectors/events.js +23 -0
- package/esm/projectors/parts.d.ts +13 -10
- package/esm/projectors/parts.js +88 -105
- package/esm/projectors/session.d.ts +5 -0
- package/esm/projectors/session.js +6 -0
- package/esm/projectors/spans.d.ts +10 -9
- package/esm/projectors/spans.js +115 -111
- package/esm/projectors/tool-state.d.ts +4 -3
- package/esm/projectors/tool-state.js +28 -33
- package/esm/projectors/usage.d.ts +3 -2
- package/esm/projectors/usage.js +12 -18
- package/esm/record.d.ts +9 -6
- package/esm/record.js +15 -13
- package/esm/store.d.ts +5 -5
- package/esm/transcripts/entry.d.ts +20 -0
- package/esm/transcripts/entry.js +23 -0
- package/esm/transcripts/event.d.ts +118 -0
- package/esm/transcripts/event.js +15 -0
- package/esm/transcripts/pi-entry.d.ts +1 -0
- package/esm/transcripts/pi-reader.d.ts +16 -0
- package/esm/transcripts/pi-reader.js +137 -0
- package/esm/transcripts/reader.d.ts +45 -0
- package/esm/transcripts/reader.js +66 -0
- package/esm/transcripts/registry.d.ts +16 -0
- package/esm/transcripts/registry.js +17 -0
- package/file-store.d.ts +2 -2
- package/index.d.ts +6 -0
- package/index.js +15 -1
- package/mirror.d.ts +6 -6
- package/mirror.js +5 -2
- package/package.json +2 -2
- package/projectors/events.d.ts +31 -0
- package/projectors/events.js +27 -0
- package/projectors/parts.d.ts +13 -10
- package/projectors/parts.js +88 -105
- package/projectors/session.d.ts +5 -0
- package/projectors/session.js +6 -0
- package/projectors/spans.d.ts +10 -9
- package/projectors/spans.js +115 -111
- package/projectors/tool-state.d.ts +4 -3
- package/projectors/tool-state.js +28 -33
- package/projectors/usage.d.ts +3 -2
- package/projectors/usage.js +12 -18
- package/record.d.ts +9 -6
- package/record.js +14 -12
- package/store.d.ts +5 -5
- package/transcripts/entry.d.ts +20 -0
- package/transcripts/entry.js +28 -0
- package/transcripts/event.d.ts +118 -0
- package/transcripts/event.js +16 -0
- package/transcripts/pi-entry.d.ts +1 -0
- package/transcripts/pi-reader.d.ts +16 -0
- package/transcripts/pi-reader.js +141 -0
- package/transcripts/reader.d.ts +45 -0
- package/transcripts/reader.js +71 -0
- package/transcripts/registry.d.ts +16 -0
- package/transcripts/registry.js +21 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step every projector shares: records → neutral events, in log order.
|
|
3
|
+
*
|
|
4
|
+
* Each record names its own transcript format, so this is also where a mixed
|
|
5
|
+
* log stops being a special case: the reader is chosen per record, and a run
|
|
6
|
+
* that changed harness projects as one conversation.
|
|
7
|
+
*/
|
|
8
|
+
import type { RunEventRecord } from '../record';
|
|
9
|
+
import type { TranscriptEvent } from '../transcripts/event';
|
|
10
|
+
import type { TranscriptReaderRegistry } from '../transcripts/reader';
|
|
11
|
+
export interface ProjectionOptions {
|
|
12
|
+
/** Readers to interpret the records with. Defaults to the process registry. */
|
|
13
|
+
readers?: TranscriptReaderRegistry;
|
|
14
|
+
}
|
|
15
|
+
/** What one record meant, tagged with its position in the run. */
|
|
16
|
+
export interface EventGroup {
|
|
17
|
+
seq: number;
|
|
18
|
+
events: TranscriptEvent[];
|
|
19
|
+
}
|
|
20
|
+
/** One event, tagged with the position of the record it came from. */
|
|
21
|
+
export interface SequencedEvent {
|
|
22
|
+
seq: number;
|
|
23
|
+
event: TranscriptEvent;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read every record with the reader its format names. Grouped by record because
|
|
27
|
+
* a span asks when the *previous entry* was written, which the flattened stream
|
|
28
|
+
* can no longer tell you.
|
|
29
|
+
*/
|
|
30
|
+
export declare function toEventGroups(records: readonly RunEventRecord[], options?: ProjectionOptions): EventGroup[];
|
|
31
|
+
export declare function toEvents(records: readonly RunEventRecord[], options?: ProjectionOptions): SequencedEvent[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The step every projector shares: records → neutral events, in log order.
|
|
4
|
+
*
|
|
5
|
+
* Each record names its own transcript format, so this is also where a mixed
|
|
6
|
+
* log stops being a special case: the reader is chosen per record, and a run
|
|
7
|
+
* that changed harness projects as one conversation.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.toEventGroups = toEventGroups;
|
|
11
|
+
exports.toEvents = toEvents;
|
|
12
|
+
const registry_1 = require("../transcripts/registry");
|
|
13
|
+
/**
|
|
14
|
+
* Read every record with the reader its format names. Grouped by record because
|
|
15
|
+
* a span asks when the *previous entry* was written, which the flattened stream
|
|
16
|
+
* can no longer tell you.
|
|
17
|
+
*/
|
|
18
|
+
function toEventGroups(records, options = {}) {
|
|
19
|
+
const readers = options.readers ?? registry_1.transcriptReaders;
|
|
20
|
+
return records.map((record) => ({
|
|
21
|
+
seq: record.seq,
|
|
22
|
+
events: readers.require(record.transcriptFormat).toEvents(record.entry)
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
function toEvents(records, options = {}) {
|
|
26
|
+
return toEventGroups(records, options).flatMap(({ seq, events }) => events.map((event) => ({ seq, event })));
|
|
27
|
+
}
|
package/projectors/parts.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Renderable projection: run log records → an ordered list of parts a UI draws.
|
|
3
3
|
*
|
|
4
|
-
* This is the projection that replaces per-host transcript encodings.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* This is the projection that replaces per-host transcript encodings. It reads
|
|
5
|
+
* neutral transcript events rather than any harness's entries, so a renderer
|
|
6
|
+
* draws a run without knowing which harness produced it. A tool call and its
|
|
7
|
+
* later result collapse into one part, so a renderer never has to correlate two
|
|
8
|
+
* messages itself, and an entry the format's reader does not understand becomes
|
|
9
|
+
* an `unknown` part rather than disappearing — a log written by a newer harness
|
|
10
|
+
* still renders, minus the detail this version understands.
|
|
9
11
|
*/
|
|
10
12
|
import type { RunEventRecord } from '../record';
|
|
11
|
-
import {
|
|
13
|
+
import type { TranscriptEntry } from '../transcripts/entry';
|
|
14
|
+
import { type ProjectionOptions } from './events';
|
|
12
15
|
export type ToolStatus = 'requested' | 'completed' | 'failed';
|
|
13
16
|
export interface PartBase {
|
|
14
17
|
/** Sequence of the record that introduced the part — a stable React key. */
|
|
@@ -59,17 +62,17 @@ export interface SummaryPart extends PartBase {
|
|
|
59
62
|
export interface UnknownPart extends PartBase {
|
|
60
63
|
kind: 'unknown';
|
|
61
64
|
entryType: string;
|
|
62
|
-
entry:
|
|
65
|
+
entry: TranscriptEntry;
|
|
63
66
|
}
|
|
64
67
|
export type ConversationPart = TextPart | ThinkingPart | ToolPart | BashPart | CustomPart | SummaryPart | UnknownPart;
|
|
65
68
|
export interface Conversation {
|
|
66
69
|
parts: ConversationPart[];
|
|
67
|
-
/** From the
|
|
70
|
+
/** From the transcript's opening entry, when the log carries one. */
|
|
68
71
|
sessionId?: string;
|
|
69
72
|
cwd?: string;
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
72
75
|
* Project records into a conversation. Pure and total: the same records always
|
|
73
|
-
* produce the same parts, whichever
|
|
76
|
+
* produce the same parts, whichever harness wrote them.
|
|
74
77
|
*/
|
|
75
|
-
export declare function projectParts(records: readonly RunEventRecord[]): Conversation;
|
|
78
|
+
export declare function projectParts(records: readonly RunEventRecord[], options?: ProjectionOptions): Conversation;
|
package/projectors/parts.js
CHANGED
|
@@ -2,133 +2,116 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Renderable projection: run log records → an ordered list of parts a UI draws.
|
|
4
4
|
*
|
|
5
|
-
* This is the projection that replaces per-host transcript encodings.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* This is the projection that replaces per-host transcript encodings. It reads
|
|
6
|
+
* neutral transcript events rather than any harness's entries, so a renderer
|
|
7
|
+
* draws a run without knowing which harness produced it. A tool call and its
|
|
8
|
+
* later result collapse into one part, so a renderer never has to correlate two
|
|
9
|
+
* messages itself, and an entry the format's reader does not understand becomes
|
|
10
|
+
* an `unknown` part rather than disappearing — a log written by a newer harness
|
|
11
|
+
* still renders, minus the detail this version understands.
|
|
10
12
|
*/
|
|
11
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
14
|
exports.projectParts = projectParts;
|
|
13
|
-
const
|
|
15
|
+
const events_1 = require("./events");
|
|
14
16
|
/**
|
|
15
17
|
* Project records into a conversation. Pure and total: the same records always
|
|
16
|
-
* produce the same parts, whichever
|
|
18
|
+
* produce the same parts, whichever harness wrote them.
|
|
17
19
|
*/
|
|
18
|
-
function projectParts(records) {
|
|
20
|
+
function projectParts(records, options = {}) {
|
|
19
21
|
const parts = [];
|
|
20
22
|
const toolsByCallId = new Map();
|
|
21
23
|
let sessionId;
|
|
22
24
|
let cwd;
|
|
23
|
-
for (const
|
|
24
|
-
const {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
for (const block of Array.isArray(message.content) ? message.content : []) {
|
|
51
|
-
if (block.type === 'text' && block.text.length > 0) {
|
|
52
|
-
parts.push({
|
|
53
|
-
kind: 'text',
|
|
54
|
-
role: 'assistant',
|
|
55
|
-
text: block.text,
|
|
56
|
-
...(message.model ? { model: message.model } : {}),
|
|
57
|
-
...(message.provider ? { provider: message.provider } : {}),
|
|
58
|
-
...base
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
else if (block.type === 'thinking') {
|
|
62
|
-
parts.push({ kind: 'thinking', text: block.thinking, ...base });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
for (const call of (0, pi_entry_1.toolCalls)(message)) {
|
|
25
|
+
for (const { seq, event } of (0, events_1.toEvents)(records, options)) {
|
|
26
|
+
const base = { seq, ...(event.entryId === undefined ? {} : { entryId: event.entryId }) };
|
|
27
|
+
switch (event.kind) {
|
|
28
|
+
case 'session-start':
|
|
29
|
+
if (event.sessionId !== undefined)
|
|
30
|
+
sessionId = event.sessionId;
|
|
31
|
+
if (event.cwd !== undefined)
|
|
32
|
+
cwd = event.cwd;
|
|
33
|
+
break;
|
|
34
|
+
// A model call is a trace and metering concern; its content arrives as the
|
|
35
|
+
// text and tool-call events that follow it.
|
|
36
|
+
case 'model-response':
|
|
37
|
+
break;
|
|
38
|
+
case 'text':
|
|
39
|
+
parts.push({
|
|
40
|
+
kind: 'text',
|
|
41
|
+
role: event.role,
|
|
42
|
+
text: event.text,
|
|
43
|
+
...(event.model ? { model: event.model } : {}),
|
|
44
|
+
...(event.provider ? { provider: event.provider } : {}),
|
|
45
|
+
...base
|
|
46
|
+
});
|
|
47
|
+
break;
|
|
48
|
+
case 'thinking':
|
|
49
|
+
parts.push({ kind: 'thinking', text: event.text, ...base });
|
|
50
|
+
break;
|
|
51
|
+
case 'tool-call': {
|
|
66
52
|
const part = {
|
|
67
53
|
kind: 'tool',
|
|
68
|
-
toolCallId:
|
|
69
|
-
name:
|
|
70
|
-
arguments:
|
|
54
|
+
toolCallId: event.toolCallId,
|
|
55
|
+
name: event.name,
|
|
56
|
+
arguments: event.arguments,
|
|
71
57
|
status: 'requested',
|
|
72
58
|
...base
|
|
73
59
|
};
|
|
74
|
-
toolsByCallId.set(
|
|
60
|
+
toolsByCallId.set(event.toolCallId, part);
|
|
75
61
|
parts.push(part);
|
|
62
|
+
break;
|
|
76
63
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
64
|
+
case 'tool-result': {
|
|
65
|
+
const existing = toolsByCallId.get(event.toolCallId);
|
|
66
|
+
const status = event.failed ? 'failed' : 'completed';
|
|
67
|
+
if (existing) {
|
|
68
|
+
existing.status = status;
|
|
69
|
+
existing.output = event.output;
|
|
70
|
+
existing.settledSeq = seq;
|
|
71
|
+
if (event.details !== undefined)
|
|
72
|
+
existing.details = event.details;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// A result whose call is not in this window (paged read, forked branch).
|
|
76
|
+
parts.push({
|
|
77
|
+
kind: 'tool',
|
|
78
|
+
toolCallId: event.toolCallId,
|
|
79
|
+
name: event.name,
|
|
80
|
+
arguments: {},
|
|
81
|
+
status,
|
|
82
|
+
output: event.output,
|
|
83
|
+
settledSeq: seq,
|
|
84
|
+
...base
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
// A result whose call is not in this window (paged read, forked branch).
|
|
89
|
+
case 'bash':
|
|
92
90
|
parts.push({
|
|
93
|
-
kind: '
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
status,
|
|
98
|
-
output,
|
|
99
|
-
settledSeq: seq,
|
|
91
|
+
kind: 'bash',
|
|
92
|
+
command: event.command,
|
|
93
|
+
output: event.output,
|
|
94
|
+
...(event.exitCode === undefined ? {} : { exitCode: event.exitCode }),
|
|
100
95
|
...base
|
|
101
96
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
kind: '
|
|
118
|
-
|
|
119
|
-
text: (0, pi_entry_1.contentText)(message.content),
|
|
120
|
-
display: message.display !== false,
|
|
121
|
-
...(message.details !== undefined ? { details: message.details } : {}),
|
|
122
|
-
...base
|
|
123
|
-
});
|
|
124
|
-
continue;
|
|
97
|
+
break;
|
|
98
|
+
case 'custom':
|
|
99
|
+
parts.push({
|
|
100
|
+
kind: 'custom',
|
|
101
|
+
customType: event.customType,
|
|
102
|
+
text: event.text,
|
|
103
|
+
display: event.display,
|
|
104
|
+
...(event.details === undefined ? {} : { details: event.details }),
|
|
105
|
+
...base
|
|
106
|
+
});
|
|
107
|
+
break;
|
|
108
|
+
case 'summary':
|
|
109
|
+
parts.push({ kind: 'summary', reason: event.reason, summary: event.summary, ...base });
|
|
110
|
+
break;
|
|
111
|
+
case 'unknown':
|
|
112
|
+
parts.push({ kind: 'unknown', entryType: event.entryType, entry: event.entry, ...base });
|
|
113
|
+
break;
|
|
125
114
|
}
|
|
126
|
-
parts.push({
|
|
127
|
-
kind: 'summary',
|
|
128
|
-
reason: message.role === 'branchSummary' ? 'branch' : 'compaction',
|
|
129
|
-
summary: message.summary ?? '',
|
|
130
|
-
...base
|
|
131
|
-
});
|
|
132
115
|
}
|
|
133
116
|
return {
|
|
134
117
|
parts,
|
package/projectors/session.d.ts
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Returns a string rather than writing a file: the projection is pure, and the
|
|
10
10
|
* node-side write lives in `@agentic-kit/run-log/file-store`.
|
|
11
|
+
*
|
|
12
|
+
* Unlike the other projectors this one is deliberately *not* neutral:
|
|
13
|
+
* re-emitting a transcript in its native encoding is per-format work, so a
|
|
14
|
+
* second harness ships its own session projection beside its reader rather than
|
|
15
|
+
* reusing this. The neutral half of "read a log" is `../transcripts/event`.
|
|
11
16
|
*/
|
|
12
17
|
import { type RunEventRecord } from '../record';
|
|
13
18
|
import { type PiSessionEntry } from '../transcripts/pi-entry';
|
package/projectors/session.js
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Returns a string rather than writing a file: the projection is pure, and the
|
|
11
11
|
* node-side write lives in `@agentic-kit/run-log/file-store`.
|
|
12
|
+
*
|
|
13
|
+
* Unlike the other projectors this one is deliberately *not* neutral:
|
|
14
|
+
* re-emitting a transcript in its native encoding is per-format work, so a
|
|
15
|
+
* second harness ships its own session projection beside its reader rather than
|
|
16
|
+
* reusing this. The neutral half of "read a log" is `../transcripts/event`.
|
|
12
17
|
*/
|
|
13
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
19
|
exports.projectSession = projectSession;
|
|
@@ -29,6 +34,7 @@ function projectSession(records, options = {}) {
|
|
|
29
34
|
throw new Error(`run log mixes pi session versions (${Array.from(versions).sort().join(', ')}); migrate the older entries before projecting a session`);
|
|
30
35
|
}
|
|
31
36
|
const transcriptVersion = records[0]?.transcriptVersion ?? format_1.SUPPORTED_PI_TRANSCRIPT_VERSION;
|
|
37
|
+
// Safe: every record was just asserted to carry the pi transcript format.
|
|
32
38
|
const entries = records.map((record) => record.entry);
|
|
33
39
|
const headerIndex = entries.findIndex(pi_entry_1.isPiSessionHeader);
|
|
34
40
|
if (headerIndex > 0) {
|
package/projectors/spans.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The log answers *what* happened and the parts projection answers *in what
|
|
5
5
|
* order*, but a trace view asks "how long did this take, and inside what" —
|
|
6
|
-
* questions
|
|
6
|
+
* questions a transcript only answers in pairs. Every entry is timestamped, so a
|
|
7
7
|
* tool call and its result, or an approval request and its answer, bracket an
|
|
8
8
|
* interval; nothing new has to be stored to know it.
|
|
9
9
|
*
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* still in flight must not look like one that finished.
|
|
17
17
|
*/
|
|
18
18
|
import type { RunEventRecord } from '../record';
|
|
19
|
-
import {
|
|
19
|
+
import type { TranscriptUsage } from '../transcripts/event';
|
|
20
|
+
import { type ProjectionOptions } from './events';
|
|
20
21
|
export type SpanKind = 'model' | 'tool' | 'approval';
|
|
21
22
|
export type SpanStatus = 'open' | 'ok' | 'error' | 'denied';
|
|
22
23
|
/**
|
|
@@ -44,10 +45,10 @@ export interface ModelSpan extends SpanBase {
|
|
|
44
45
|
kind: 'model';
|
|
45
46
|
model?: string;
|
|
46
47
|
provider?: string;
|
|
47
|
-
/**
|
|
48
|
+
/** The provider's response id — the join key to a metered inference row. */
|
|
48
49
|
responseId?: string;
|
|
49
50
|
stopReason?: string;
|
|
50
|
-
usage?:
|
|
51
|
+
usage?: TranscriptUsage;
|
|
51
52
|
errorMessage?: string;
|
|
52
53
|
}
|
|
53
54
|
export interface ToolSpan extends SpanBase {
|
|
@@ -76,9 +77,9 @@ export interface SpanProjection {
|
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
79
|
* Project records into spans. Pure, and like the other projectors it ignores what
|
|
79
|
-
* it does not recognise — a log written by a newer
|
|
80
|
-
* type that is malformed is another matter and throws: a gate
|
|
81
|
-
* tool call is a writer bug, and a trace that quietly omitted
|
|
82
|
-
* worse than one that fails.
|
|
80
|
+
* it does not recognise — a log written by a newer harness still traces. A
|
|
81
|
+
* *registered* type that is malformed is another matter and throws: a gate
|
|
82
|
+
* decision naming no tool call is a writer bug, and a trace that quietly omitted
|
|
83
|
+
* a refusal would be worse than one that fails.
|
|
83
84
|
*/
|
|
84
|
-
export declare function projectSpans(records: readonly RunEventRecord[]): SpanProjection;
|
|
85
|
+
export declare function projectSpans(records: readonly RunEventRecord[], options?: ProjectionOptions): SpanProjection;
|