@agentic-kit/run-log 0.5.0 → 0.6.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
package/README.md
CHANGED
|
@@ -106,10 +106,6 @@ Unreadable input fails loudly. A corrupt record, a mixed-version log, or a sessi
|
|
|
106
106
|
- [`@agentic-kit/metering`](../metering) — the metered gateway and usage reporting
|
|
107
107
|
- [`agentic-server`](../agentic-server) — the metered inference gateway
|
|
108
108
|
|
|
109
|
-
## License
|
|
110
|
-
|
|
111
|
-
SEE LICENSE IN LICENSE
|
|
112
|
-
|
|
113
109
|
---
|
|
114
110
|
|
|
115
111
|
## Education and Tutorials
|
package/esm/file-store.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { type SessionProjectionOptions } from './projectors/session';
|
|
14
14
|
import { type RunEventRecord } from './record';
|
|
15
15
|
import { type AppendOptions, type RunLogCursor, type RunLogPage, type RunLogStore } from './store';
|
|
16
|
-
import type {
|
|
16
|
+
import type { TranscriptEntry } from './transcripts/entry';
|
|
17
17
|
export interface FileRunLogStoreOptions {
|
|
18
18
|
/** Absolute path of the log file. Parent directories are created. */
|
|
19
19
|
path: string;
|
|
@@ -22,7 +22,7 @@ export declare class FileRunLogStore implements RunLogStore {
|
|
|
22
22
|
private readonly path;
|
|
23
23
|
constructor(options: FileRunLogStoreOptions);
|
|
24
24
|
private load;
|
|
25
|
-
append(runId: string, entries: readonly
|
|
25
|
+
append(runId: string, entries: readonly TranscriptEntry[], options?: AppendOptions): Promise<RunEventRecord[]>;
|
|
26
26
|
read(runId: string, cursor?: RunLogCursor, limit?: number): Promise<RunLogPage>;
|
|
27
27
|
}
|
|
28
28
|
/**
|
package/esm/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
export { constructiveEntryTypes, type EntryTypeDefinition, EntryTypeRegistry, type EntryVisibility } from './entry-types';
|
|
10
10
|
export { follow, type FollowOptions, readAll } from './follow';
|
|
11
11
|
export { type SessionEntrySource, SessionMirror, type SessionMirrorOptions } from './mirror';
|
|
12
|
+
export { type EventGroup, type ProjectionOptions, type SequencedEvent, toEventGroups, toEvents } from './projectors/events';
|
|
12
13
|
export { type BashPart, type Conversation, type ConversationPart, type CustomPart, projectParts, type SummaryPart, type TextPart, type ThinkingPart, type ToolPart, type ToolStatus, type UnknownPart } from './projectors/parts';
|
|
13
14
|
export { parseSessionJsonl, projectSession, type SessionProjection, type SessionProjectionOptions } from './projectors/session';
|
|
14
15
|
export { type ApprovalSpan, type ModelSpan, projectSpans, type Span, type SpanBase, type SpanKind, type SpanProjection, type SpanStatus, type SpanTiming, type ToolSpan } from './projectors/spans';
|
|
@@ -16,5 +17,10 @@ export { APPROVAL_REQUEST_TYPE, APPROVAL_RESOLUTION_TYPE, type ApprovalRequestIn
|
|
|
16
17
|
export { modelKey, type ModelUsage, projectUsage, type RunUsage, type UsageTotals } from './projectors/usage';
|
|
17
18
|
export { assertOrdered, assertRunEventRecord, idempotencyKey, RUN_LOG_WRAPPER_VERSION, type RunEventRecord, wrapEntry, type WrapEntryOptions } from './record';
|
|
18
19
|
export { type AppendOptions, cursorAfter, MemoryRunLogStore, type RunLogAppendStore, type RunLogCursor, type RunLogPage, type RunLogReadStore, type RunLogStore, START } from './store';
|
|
20
|
+
export { assertTranscriptEntry, type TranscriptEntry } from './transcripts/entry';
|
|
21
|
+
export { type BashEvent, type CustomEvent, type ModelResponseEvent, type SessionStartEvent, type SummaryEvent, type TextEvent, type ThinkingEvent, type ToolCallEvent, type ToolResultEvent, type TranscriptEvent, type TranscriptEventBase, type TranscriptUsage, type UnknownEvent } from './transcripts/event';
|
|
19
22
|
export { assertTranscriptFormat, PI_TRANSCRIPT_FORMAT, SUPPORTED_PI_TRANSCRIPT_VERSION, type TranscriptFormat } from './transcripts/format';
|
|
20
23
|
export { assertPiSessionEntry, contentText, isAssistantMessage, isPiBranchSummaryEntry, isPiCompactionEntry, isPiMessageEntry, isPiSessionHeader, isToolResultMessage, type PiAssistantMessage, type PiBashExecutionMessage, type PiBranchSummaryEntry, type PiCompactionEntry, type PiContent, type PiCustomMessage, type PiEntryBase, type PiImageContent, type PiMessage, type PiMessageEntry, type PiOtherEntry, type PiSessionEntry, type PiSessionHeader, type PiSummaryMessage, type PiTextContent, type PiThinkingContent, type PiToolCallContent, type PiToolResultMessage, type PiUsage, type PiUsageCost, type PiUserMessage, toolCalls } from './transcripts/pi-entry';
|
|
24
|
+
export { piEntryToEvents, piTranscriptReader } from './transcripts/pi-reader';
|
|
25
|
+
export { passthroughReader, type TranscriptReader, TranscriptReaderRegistry } from './transcripts/reader';
|
|
26
|
+
export { defaultTranscriptReaders, transcriptReaders } from './transcripts/registry';
|
package/esm/index.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
export { constructiveEntryTypes, EntryTypeRegistry } from './entry-types';
|
|
10
10
|
export { follow, readAll } from './follow';
|
|
11
11
|
export { SessionMirror } from './mirror';
|
|
12
|
+
export { toEventGroups, toEvents } from './projectors/events';
|
|
12
13
|
export { projectParts } from './projectors/parts';
|
|
13
14
|
export { parseSessionJsonl, projectSession } from './projectors/session';
|
|
14
15
|
export { projectSpans } from './projectors/spans';
|
|
@@ -16,5 +17,9 @@ export { APPROVAL_REQUEST_TYPE, APPROVAL_RESOLUTION_TYPE, approvalRequestMessage
|
|
|
16
17
|
export { modelKey, projectUsage } from './projectors/usage';
|
|
17
18
|
export { assertOrdered, assertRunEventRecord, idempotencyKey, RUN_LOG_WRAPPER_VERSION, wrapEntry } from './record';
|
|
18
19
|
export { cursorAfter, MemoryRunLogStore, START } from './store';
|
|
20
|
+
export { assertTranscriptEntry } from './transcripts/entry';
|
|
19
21
|
export { assertTranscriptFormat, PI_TRANSCRIPT_FORMAT, SUPPORTED_PI_TRANSCRIPT_VERSION } from './transcripts/format';
|
|
20
22
|
export { assertPiSessionEntry, contentText, isAssistantMessage, isPiBranchSummaryEntry, isPiCompactionEntry, isPiMessageEntry, isPiSessionHeader, isToolResultMessage, toolCalls } from './transcripts/pi-entry';
|
|
23
|
+
export { piEntryToEvents, piTranscriptReader } from './transcripts/pi-reader';
|
|
24
|
+
export { passthroughReader, TranscriptReaderRegistry } from './transcripts/reader';
|
|
25
|
+
export { defaultTranscriptReaders, transcriptReaders } from './transcripts/registry';
|
package/esm/mirror.d.ts
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { RunEventRecord } from './record';
|
|
21
21
|
import type { RunLogAppendStore } from './store';
|
|
22
|
-
import type
|
|
23
|
-
import { type
|
|
22
|
+
import { type TranscriptEntry } from './transcripts/entry';
|
|
23
|
+
import { type TranscriptFormat } from './transcripts/format';
|
|
24
24
|
/** The slice of a harness session manager the mirror needs. */
|
|
25
25
|
export interface SessionEntrySource {
|
|
26
26
|
getHeader(): unknown;
|
|
@@ -30,11 +30,11 @@ export interface SessionMirrorOptions {
|
|
|
30
30
|
runId: string;
|
|
31
31
|
store: RunLogAppendStore;
|
|
32
32
|
/**
|
|
33
|
-
* Narrow each entry before it is appended. Defaults to the
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* Narrow each entry before it is appended. Defaults to the reader registered
|
|
34
|
+
* for `transcriptFormat`, so an adapter for another transcript gets its own
|
|
35
|
+
* validation by registering its reader.
|
|
36
36
|
*/
|
|
37
|
-
assertEntry?: (entry: unknown) =>
|
|
37
|
+
assertEntry?: (entry: unknown) => TranscriptEntry;
|
|
38
38
|
/** Transcript format the entries are encoded in, e.g. `pi`. */
|
|
39
39
|
transcriptFormat?: TranscriptFormat;
|
|
40
40
|
/** Transcript format version the entries are produced under. */
|
package/esm/mirror.js
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* and the format the entries are validated as, so this is testable without a
|
|
18
18
|
* running agent.
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
20
|
+
import { PI_TRANSCRIPT_FORMAT } from './transcripts/format';
|
|
21
|
+
import { transcriptReaders } from './transcripts/registry';
|
|
21
22
|
export class SessionMirror {
|
|
22
23
|
runId;
|
|
23
24
|
store;
|
|
@@ -32,7 +33,9 @@ export class SessionMirror {
|
|
|
32
33
|
constructor(options) {
|
|
33
34
|
this.runId = options.runId;
|
|
34
35
|
this.store = options.store;
|
|
35
|
-
this.assertEntry =
|
|
36
|
+
this.assertEntry =
|
|
37
|
+
options.assertEntry ??
|
|
38
|
+
transcriptReaders.require(options.transcriptFormat ?? PI_TRANSCRIPT_FORMAT).assertEntry;
|
|
36
39
|
this.transcriptFormat = options.transcriptFormat;
|
|
37
40
|
this.transcriptVersion = options.transcriptVersion;
|
|
38
41
|
}
|
|
@@ -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,23 @@
|
|
|
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 { transcriptReaders } from '../transcripts/registry';
|
|
9
|
+
/**
|
|
10
|
+
* Read every record with the reader its format names. Grouped by record because
|
|
11
|
+
* a span asks when the *previous entry* was written, which the flattened stream
|
|
12
|
+
* can no longer tell you.
|
|
13
|
+
*/
|
|
14
|
+
export function toEventGroups(records, options = {}) {
|
|
15
|
+
const readers = options.readers ?? transcriptReaders;
|
|
16
|
+
return records.map((record) => ({
|
|
17
|
+
seq: record.seq,
|
|
18
|
+
events: readers.require(record.transcriptFormat).toEvents(record.entry)
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
export function toEvents(records, options = {}) {
|
|
22
|
+
return toEventGroups(records, options).flatMap(({ seq, events }) => events.map((event) => ({ seq, event })));
|
|
23
|
+
}
|
|
@@ -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/esm/projectors/parts.js
CHANGED
|
@@ -1,131 +1,114 @@
|
|
|
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
|
-
import {
|
|
12
|
+
import { toEvents } from './events';
|
|
11
13
|
/**
|
|
12
14
|
* Project records into a conversation. Pure and total: the same records always
|
|
13
|
-
* produce the same parts, whichever
|
|
15
|
+
* produce the same parts, whichever harness wrote them.
|
|
14
16
|
*/
|
|
15
|
-
export function projectParts(records) {
|
|
17
|
+
export function projectParts(records, options = {}) {
|
|
16
18
|
const parts = [];
|
|
17
19
|
const toolsByCallId = new Map();
|
|
18
20
|
let sessionId;
|
|
19
21
|
let cwd;
|
|
20
|
-
for (const
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
for (const block of Array.isArray(message.content) ? message.content : []) {
|
|
48
|
-
if (block.type === 'text' && block.text.length > 0) {
|
|
49
|
-
parts.push({
|
|
50
|
-
kind: 'text',
|
|
51
|
-
role: 'assistant',
|
|
52
|
-
text: block.text,
|
|
53
|
-
...(message.model ? { model: message.model } : {}),
|
|
54
|
-
...(message.provider ? { provider: message.provider } : {}),
|
|
55
|
-
...base
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
else if (block.type === 'thinking') {
|
|
59
|
-
parts.push({ kind: 'thinking', text: block.thinking, ...base });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
for (const call of toolCalls(message)) {
|
|
22
|
+
for (const { seq, event } of toEvents(records, options)) {
|
|
23
|
+
const base = { seq, ...(event.entryId === undefined ? {} : { entryId: event.entryId }) };
|
|
24
|
+
switch (event.kind) {
|
|
25
|
+
case 'session-start':
|
|
26
|
+
if (event.sessionId !== undefined)
|
|
27
|
+
sessionId = event.sessionId;
|
|
28
|
+
if (event.cwd !== undefined)
|
|
29
|
+
cwd = event.cwd;
|
|
30
|
+
break;
|
|
31
|
+
// A model call is a trace and metering concern; its content arrives as the
|
|
32
|
+
// text and tool-call events that follow it.
|
|
33
|
+
case 'model-response':
|
|
34
|
+
break;
|
|
35
|
+
case 'text':
|
|
36
|
+
parts.push({
|
|
37
|
+
kind: 'text',
|
|
38
|
+
role: event.role,
|
|
39
|
+
text: event.text,
|
|
40
|
+
...(event.model ? { model: event.model } : {}),
|
|
41
|
+
...(event.provider ? { provider: event.provider } : {}),
|
|
42
|
+
...base
|
|
43
|
+
});
|
|
44
|
+
break;
|
|
45
|
+
case 'thinking':
|
|
46
|
+
parts.push({ kind: 'thinking', text: event.text, ...base });
|
|
47
|
+
break;
|
|
48
|
+
case 'tool-call': {
|
|
63
49
|
const part = {
|
|
64
50
|
kind: 'tool',
|
|
65
|
-
toolCallId:
|
|
66
|
-
name:
|
|
67
|
-
arguments:
|
|
51
|
+
toolCallId: event.toolCallId,
|
|
52
|
+
name: event.name,
|
|
53
|
+
arguments: event.arguments,
|
|
68
54
|
status: 'requested',
|
|
69
55
|
...base
|
|
70
56
|
};
|
|
71
|
-
toolsByCallId.set(
|
|
57
|
+
toolsByCallId.set(event.toolCallId, part);
|
|
72
58
|
parts.push(part);
|
|
59
|
+
break;
|
|
73
60
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
61
|
+
case 'tool-result': {
|
|
62
|
+
const existing = toolsByCallId.get(event.toolCallId);
|
|
63
|
+
const status = event.failed ? 'failed' : 'completed';
|
|
64
|
+
if (existing) {
|
|
65
|
+
existing.status = status;
|
|
66
|
+
existing.output = event.output;
|
|
67
|
+
existing.settledSeq = seq;
|
|
68
|
+
if (event.details !== undefined)
|
|
69
|
+
existing.details = event.details;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// A result whose call is not in this window (paged read, forked branch).
|
|
73
|
+
parts.push({
|
|
74
|
+
kind: 'tool',
|
|
75
|
+
toolCallId: event.toolCallId,
|
|
76
|
+
name: event.name,
|
|
77
|
+
arguments: {},
|
|
78
|
+
status,
|
|
79
|
+
output: event.output,
|
|
80
|
+
settledSeq: seq,
|
|
81
|
+
...base
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
86
85
|
}
|
|
87
|
-
|
|
88
|
-
// A result whose call is not in this window (paged read, forked branch).
|
|
86
|
+
case 'bash':
|
|
89
87
|
parts.push({
|
|
90
|
-
kind: '
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
status,
|
|
95
|
-
output,
|
|
96
|
-
settledSeq: seq,
|
|
88
|
+
kind: 'bash',
|
|
89
|
+
command: event.command,
|
|
90
|
+
output: event.output,
|
|
91
|
+
...(event.exitCode === undefined ? {} : { exitCode: event.exitCode }),
|
|
97
92
|
...base
|
|
98
93
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
kind: '
|
|
115
|
-
|
|
116
|
-
text: contentText(message.content),
|
|
117
|
-
display: message.display !== false,
|
|
118
|
-
...(message.details !== undefined ? { details: message.details } : {}),
|
|
119
|
-
...base
|
|
120
|
-
});
|
|
121
|
-
continue;
|
|
94
|
+
break;
|
|
95
|
+
case 'custom':
|
|
96
|
+
parts.push({
|
|
97
|
+
kind: 'custom',
|
|
98
|
+
customType: event.customType,
|
|
99
|
+
text: event.text,
|
|
100
|
+
display: event.display,
|
|
101
|
+
...(event.details === undefined ? {} : { details: event.details }),
|
|
102
|
+
...base
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case 'summary':
|
|
106
|
+
parts.push({ kind: 'summary', reason: event.reason, summary: event.summary, ...base });
|
|
107
|
+
break;
|
|
108
|
+
case 'unknown':
|
|
109
|
+
parts.push({ kind: 'unknown', entryType: event.entryType, entry: event.entry, ...base });
|
|
110
|
+
break;
|
|
122
111
|
}
|
|
123
|
-
parts.push({
|
|
124
|
-
kind: 'summary',
|
|
125
|
-
reason: message.role === 'branchSummary' ? 'branch' : 'compaction',
|
|
126
|
-
summary: message.summary ?? '',
|
|
127
|
-
...base
|
|
128
|
-
});
|
|
129
112
|
}
|
|
130
113
|
return {
|
|
131
114
|
parts,
|
|
@@ -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';
|
|
@@ -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 { assertOrdered } from '../record';
|
|
13
18
|
import { assertTranscriptFormat, PI_TRANSCRIPT_FORMAT, SUPPORTED_PI_TRANSCRIPT_VERSION } from '../transcripts/format';
|
|
@@ -25,6 +30,7 @@ export function projectSession(records, options = {}) {
|
|
|
25
30
|
throw new Error(`run log mixes pi session versions (${Array.from(versions).sort().join(', ')}); migrate the older entries before projecting a session`);
|
|
26
31
|
}
|
|
27
32
|
const transcriptVersion = records[0]?.transcriptVersion ?? SUPPORTED_PI_TRANSCRIPT_VERSION;
|
|
33
|
+
// Safe: every record was just asserted to carry the pi transcript format.
|
|
28
34
|
const entries = records.map((record) => record.entry);
|
|
29
35
|
const headerIndex = entries.findIndex(isPiSessionHeader);
|
|
30
36
|
if (headerIndex > 0) {
|
|
@@ -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;
|