@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
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A harness's own session entry, as the log stores it.
|
|
3
|
+
*
|
|
4
|
+
* The log is deliberately not a re-encoding of a harness's transcript — an
|
|
5
|
+
* entry is kept byte-for-byte so the run stays resumable in the harness that
|
|
6
|
+
* wrote it. All the wrapper knows structurally is that an entry is an object
|
|
7
|
+
* with a `type`; what that type *means* is the reader's business, selected by
|
|
8
|
+
* the record's `transcriptFormat`.
|
|
9
|
+
*/
|
|
10
|
+
export interface TranscriptEntry {
|
|
11
|
+
/** The format's own entry discriminator, e.g. pi's `message`. */
|
|
12
|
+
type: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare const isEntryRecord: (value: unknown) => value is Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Narrow an untrusted value (a JSONB column, an HTTP body) to the structure
|
|
18
|
+
* every transcript shares. A format's own reader validates the rest.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertTranscriptEntry(value: unknown): TranscriptEntry;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A harness's own session entry, as the log stores it.
|
|
4
|
+
*
|
|
5
|
+
* The log is deliberately not a re-encoding of a harness's transcript — an
|
|
6
|
+
* entry is kept byte-for-byte so the run stays resumable in the harness that
|
|
7
|
+
* wrote it. All the wrapper knows structurally is that an entry is an object
|
|
8
|
+
* with a `type`; what that type *means* is the reader's business, selected by
|
|
9
|
+
* the record's `transcriptFormat`.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.isEntryRecord = void 0;
|
|
13
|
+
exports.assertTranscriptEntry = assertTranscriptEntry;
|
|
14
|
+
const isEntryRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
15
|
+
exports.isEntryRecord = isEntryRecord;
|
|
16
|
+
/**
|
|
17
|
+
* Narrow an untrusted value (a JSONB column, an HTTP body) to the structure
|
|
18
|
+
* every transcript shares. A format's own reader validates the rest.
|
|
19
|
+
*/
|
|
20
|
+
function assertTranscriptEntry(value) {
|
|
21
|
+
if (!(0, exports.isEntryRecord)(value)) {
|
|
22
|
+
throw new TypeError(`transcript entry must be an object, received ${typeof value}`);
|
|
23
|
+
}
|
|
24
|
+
if (typeof value.type !== 'string' || value.type.length === 0) {
|
|
25
|
+
throw new TypeError('transcript entry must carry a non-empty string `type`');
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The neutral vocabulary every projector reads.
|
|
3
|
+
*
|
|
4
|
+
* A projector used to know pi's message shapes, which made "render a run" and
|
|
5
|
+
* "read pi" the same capability. An event is what a transcript entry *means* —
|
|
6
|
+
* a model answered, a tool was called, a human was asked — so a format is
|
|
7
|
+
* understood in exactly one place (its `TranscriptReader`) and rendering,
|
|
8
|
+
* tracing and usage folding stop caring which harness ran.
|
|
9
|
+
*
|
|
10
|
+
* Kept close to what the projectors need rather than to any one harness: pi's
|
|
11
|
+
* `message`/`toolResult`/`custom` entries and DeepSeek Harness's
|
|
12
|
+
* `assistant/message`, `tool/call`, `tool/result` events both land here without
|
|
13
|
+
* either format's naming surviving the trip.
|
|
14
|
+
*/
|
|
15
|
+
import type { TranscriptEntry } from './entry';
|
|
16
|
+
/** Tokens and cost as a transcript reports them — all fields optional. */
|
|
17
|
+
export interface TranscriptUsage {
|
|
18
|
+
input?: number;
|
|
19
|
+
output?: number;
|
|
20
|
+
cacheRead?: number;
|
|
21
|
+
cacheWrite?: number;
|
|
22
|
+
totalTokens?: number;
|
|
23
|
+
cost?: {
|
|
24
|
+
total?: number;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
};
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface TranscriptEventBase {
|
|
30
|
+
/** The entry's id in its own transcript, when the format gives entries ids. */
|
|
31
|
+
entryId?: string;
|
|
32
|
+
/** The entry's ISO timestamp, when it carries one. */
|
|
33
|
+
at?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The transcript's opening entry: which session, and where it ran. */
|
|
36
|
+
export interface SessionStartEvent extends TranscriptEventBase {
|
|
37
|
+
kind: 'session-start';
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
cwd?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface TextEvent extends TranscriptEventBase {
|
|
42
|
+
kind: 'text';
|
|
43
|
+
role: 'user' | 'assistant';
|
|
44
|
+
text: string;
|
|
45
|
+
model?: string;
|
|
46
|
+
provider?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ThinkingEvent extends TranscriptEventBase {
|
|
49
|
+
kind: 'thinking';
|
|
50
|
+
text: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A model finished answering. Separate from the text it produced because a
|
|
54
|
+
* trace and a usage total ask about the *call* — which model, what it cost, why
|
|
55
|
+
* it stopped — while a renderer asks about the content.
|
|
56
|
+
*/
|
|
57
|
+
export interface ModelResponseEvent extends TranscriptEventBase {
|
|
58
|
+
kind: 'model-response';
|
|
59
|
+
model?: string;
|
|
60
|
+
provider?: string;
|
|
61
|
+
/** The provider's response id — the join key to a metered inference row. */
|
|
62
|
+
responseId?: string;
|
|
63
|
+
stopReason?: string;
|
|
64
|
+
usage?: TranscriptUsage;
|
|
65
|
+
errorMessage?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ToolCallEvent extends TranscriptEventBase {
|
|
68
|
+
kind: 'tool-call';
|
|
69
|
+
toolCallId: string;
|
|
70
|
+
name: string;
|
|
71
|
+
arguments: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
export interface ToolResultEvent extends TranscriptEventBase {
|
|
74
|
+
kind: 'tool-result';
|
|
75
|
+
toolCallId: string;
|
|
76
|
+
name: string;
|
|
77
|
+
output: string;
|
|
78
|
+
failed: boolean;
|
|
79
|
+
details?: unknown;
|
|
80
|
+
/** Nested model work the tool itself paid for. */
|
|
81
|
+
usage?: TranscriptUsage;
|
|
82
|
+
}
|
|
83
|
+
export interface BashEvent extends TranscriptEventBase {
|
|
84
|
+
kind: 'bash';
|
|
85
|
+
command: string;
|
|
86
|
+
output: string;
|
|
87
|
+
exitCode?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A platform entry riding in the transcript — an approval request, a gate
|
|
91
|
+
* decision — discriminated by `customType` and interpreted by the entry-type
|
|
92
|
+
* registry rather than by a format's reader.
|
|
93
|
+
*/
|
|
94
|
+
export interface CustomEvent extends TranscriptEventBase {
|
|
95
|
+
kind: 'custom';
|
|
96
|
+
customType: string;
|
|
97
|
+
text: string;
|
|
98
|
+
/** Whether the entry is part of the conversation a human is shown. */
|
|
99
|
+
display: boolean;
|
|
100
|
+
details?: unknown;
|
|
101
|
+
}
|
|
102
|
+
export interface SummaryEvent extends TranscriptEventBase {
|
|
103
|
+
kind: 'summary';
|
|
104
|
+
reason: 'compaction' | 'branch';
|
|
105
|
+
summary: string;
|
|
106
|
+
usage?: TranscriptUsage;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* An entry this reader does not understand. Carried rather than dropped: a log
|
|
110
|
+
* written by a newer harness still renders, minus the detail this version
|
|
111
|
+
* knows, and a projector never silently loses a row.
|
|
112
|
+
*/
|
|
113
|
+
export interface UnknownEvent extends TranscriptEventBase {
|
|
114
|
+
kind: 'unknown';
|
|
115
|
+
entryType: string;
|
|
116
|
+
entry: TranscriptEntry;
|
|
117
|
+
}
|
|
118
|
+
export type TranscriptEvent = SessionStartEvent | TextEvent | ThinkingEvent | ModelResponseEvent | ToolCallEvent | ToolResultEvent | BashEvent | CustomEvent | SummaryEvent | UnknownEvent;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The neutral vocabulary every projector reads.
|
|
4
|
+
*
|
|
5
|
+
* A projector used to know pi's message shapes, which made "render a run" and
|
|
6
|
+
* "read pi" the same capability. An event is what a transcript entry *means* —
|
|
7
|
+
* a model answered, a tool was called, a human was asked — so a format is
|
|
8
|
+
* understood in exactly one place (its `TranscriptReader`) and rendering,
|
|
9
|
+
* tracing and usage folding stop caring which harness ran.
|
|
10
|
+
*
|
|
11
|
+
* Kept close to what the projectors need rather than to any one harness: pi's
|
|
12
|
+
* `message`/`toolResult`/`custom` entries and DeepSeek Harness's
|
|
13
|
+
* `assistant/message`, `tool/call`, `tool/result` events both land here without
|
|
14
|
+
* either format's naming surviving the trip.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi's transcript reader: pi session entries → neutral events.
|
|
3
|
+
*
|
|
4
|
+
* This is the only place in the package that knows pi's message shapes. It stays
|
|
5
|
+
* here rather than in `@agentic-kit/pi` on purpose — a reader must be
|
|
6
|
+
* browser-safe, because renderers project logs client-side, while the adapter
|
|
7
|
+
* package pulls pi's node SDK. The types it reads are structural (see
|
|
8
|
+
* `./pi-entry`), so nothing here imports pi either.
|
|
9
|
+
*/
|
|
10
|
+
import type { TranscriptEntry } from './entry';
|
|
11
|
+
import type { TranscriptEvent } from './event';
|
|
12
|
+
import type { TranscriptReader } from './reader';
|
|
13
|
+
/** What a single pi entry means, in order. */
|
|
14
|
+
export declare function piEntryToEvents(entry: TranscriptEntry): TranscriptEvent[];
|
|
15
|
+
/** pi's session-file transcript (`@earendil-works/pi-coding-agent`). */
|
|
16
|
+
export declare const piTranscriptReader: TranscriptReader;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pi's transcript reader: pi session entries → neutral events.
|
|
4
|
+
*
|
|
5
|
+
* This is the only place in the package that knows pi's message shapes. It stays
|
|
6
|
+
* here rather than in `@agentic-kit/pi` on purpose — a reader must be
|
|
7
|
+
* browser-safe, because renderers project logs client-side, while the adapter
|
|
8
|
+
* package pulls pi's node SDK. The types it reads are structural (see
|
|
9
|
+
* `./pi-entry`), so nothing here imports pi either.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.piTranscriptReader = void 0;
|
|
13
|
+
exports.piEntryToEvents = piEntryToEvents;
|
|
14
|
+
const format_1 = require("./format");
|
|
15
|
+
const pi_entry_1 = require("./pi-entry");
|
|
16
|
+
/** What a single pi entry means, in order. */
|
|
17
|
+
function piEntryToEvents(entry) {
|
|
18
|
+
const pi = entry;
|
|
19
|
+
const base = {
|
|
20
|
+
...(typeof pi.id === 'string' ? { entryId: pi.id } : {}),
|
|
21
|
+
...(typeof pi.timestamp === 'string' ? { at: pi.timestamp } : {})
|
|
22
|
+
};
|
|
23
|
+
if ((0, pi_entry_1.isPiSessionHeader)(pi)) {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
kind: 'session-start',
|
|
27
|
+
...base,
|
|
28
|
+
...(typeof pi.id === 'string' ? { sessionId: pi.id } : {}),
|
|
29
|
+
...(typeof pi.cwd === 'string' ? { cwd: pi.cwd } : {})
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
if ((0, pi_entry_1.isPiCompactionEntry)(pi)) {
|
|
34
|
+
return [
|
|
35
|
+
{ kind: 'summary', reason: 'compaction', summary: pi.summary, ...(pi.usage ? { usage: pi.usage } : {}), ...base }
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
if ((0, pi_entry_1.isPiBranchSummaryEntry)(pi)) {
|
|
39
|
+
return [
|
|
40
|
+
{ kind: 'summary', reason: 'branch', summary: pi.summary, ...(pi.usage ? { usage: pi.usage } : {}), ...base }
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
if (!(0, pi_entry_1.isPiMessageEntry)(pi)) {
|
|
44
|
+
return [{ kind: 'unknown', entryType: pi.type, entry, ...base }];
|
|
45
|
+
}
|
|
46
|
+
const message = pi.message;
|
|
47
|
+
if (message.role === 'user') {
|
|
48
|
+
return [{ kind: 'text', role: 'user', text: (0, pi_entry_1.contentText)(message.content), ...base }];
|
|
49
|
+
}
|
|
50
|
+
if ((0, pi_entry_1.isAssistantMessage)(message)) {
|
|
51
|
+
const events = [
|
|
52
|
+
{
|
|
53
|
+
kind: 'model-response',
|
|
54
|
+
...(message.model ? { model: message.model } : {}),
|
|
55
|
+
...(message.provider ? { provider: message.provider } : {}),
|
|
56
|
+
...(typeof message.responseId === 'string' ? { responseId: message.responseId } : {}),
|
|
57
|
+
...(message.stopReason ? { stopReason: message.stopReason } : {}),
|
|
58
|
+
...(message.usage ? { usage: message.usage } : {}),
|
|
59
|
+
...(message.errorMessage ? { errorMessage: message.errorMessage } : {}),
|
|
60
|
+
...base
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
for (const block of Array.isArray(message.content) ? message.content : []) {
|
|
64
|
+
if (block.type === 'text' && block.text.length > 0) {
|
|
65
|
+
events.push({
|
|
66
|
+
kind: 'text',
|
|
67
|
+
role: 'assistant',
|
|
68
|
+
text: block.text,
|
|
69
|
+
...(message.model ? { model: message.model } : {}),
|
|
70
|
+
...(message.provider ? { provider: message.provider } : {}),
|
|
71
|
+
...base
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (block.type === 'thinking') {
|
|
75
|
+
events.push({ kind: 'thinking', text: block.thinking, ...base });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const call of (0, pi_entry_1.toolCalls)(message)) {
|
|
79
|
+
events.push({
|
|
80
|
+
kind: 'tool-call',
|
|
81
|
+
toolCallId: call.id,
|
|
82
|
+
name: call.name,
|
|
83
|
+
arguments: call.arguments ?? {},
|
|
84
|
+
...base
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return events;
|
|
88
|
+
}
|
|
89
|
+
if ((0, pi_entry_1.isToolResultMessage)(message)) {
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
kind: 'tool-result',
|
|
93
|
+
toolCallId: message.toolCallId,
|
|
94
|
+
name: message.toolName,
|
|
95
|
+
output: (0, pi_entry_1.contentText)(message.content),
|
|
96
|
+
failed: message.isError === true,
|
|
97
|
+
...(message.details === undefined ? {} : { details: message.details }),
|
|
98
|
+
...(message.usage ? { usage: message.usage } : {}),
|
|
99
|
+
...base
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
if (message.role === 'bashExecution') {
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
kind: 'bash',
|
|
107
|
+
command: message.command,
|
|
108
|
+
output: message.output,
|
|
109
|
+
...(typeof message.exitCode === 'number' ? { exitCode: message.exitCode } : {}),
|
|
110
|
+
...base
|
|
111
|
+
}
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
if (message.role === 'custom') {
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
kind: 'custom',
|
|
118
|
+
customType: message.customType,
|
|
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
|
+
];
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
{
|
|
128
|
+
kind: 'summary',
|
|
129
|
+
reason: message.role === 'branchSummary' ? 'branch' : 'compaction',
|
|
130
|
+
summary: message.summary ?? '',
|
|
131
|
+
...base
|
|
132
|
+
}
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
/** pi's session-file transcript (`@earendil-works/pi-coding-agent`). */
|
|
136
|
+
exports.piTranscriptReader = {
|
|
137
|
+
format: format_1.PI_TRANSCRIPT_FORMAT,
|
|
138
|
+
version: format_1.SUPPORTED_PI_TRANSCRIPT_VERSION,
|
|
139
|
+
assertEntry: pi_entry_1.assertPiSessionEntry,
|
|
140
|
+
toEvents: piEntryToEvents
|
|
141
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript reader registry: one place per format that knows how to read
|
|
3
|
+
* it, looked up by the format a record was written under.
|
|
4
|
+
*
|
|
5
|
+
* A reader is registered *by value*, like an entry type, so a second harness
|
|
6
|
+
* ships its reader with its adapter and neither this package nor a renderer is
|
|
7
|
+
* released to support it. A record names its format, so a run that changed
|
|
8
|
+
* harness mid-flight still projects as one ordered log.
|
|
9
|
+
*/
|
|
10
|
+
import { type TranscriptEntry } from './entry';
|
|
11
|
+
import type { TranscriptEvent } from './event';
|
|
12
|
+
import type { TranscriptFormat } from './format';
|
|
13
|
+
export interface TranscriptReader {
|
|
14
|
+
format: TranscriptFormat;
|
|
15
|
+
/** The format version this reader projects without migration. */
|
|
16
|
+
version: number;
|
|
17
|
+
/** Narrow an untrusted entry of this format. Throws on anything unreadable. */
|
|
18
|
+
assertEntry: (value: unknown) => TranscriptEntry;
|
|
19
|
+
/**
|
|
20
|
+
* What the entry means, in order. One entry may mean several things — a pi
|
|
21
|
+
* assistant message is a model response plus its text plus its tool calls.
|
|
22
|
+
*/
|
|
23
|
+
toEvents: (entry: TranscriptEntry) => TranscriptEvent[];
|
|
24
|
+
}
|
|
25
|
+
export declare class TranscriptReaderRegistry {
|
|
26
|
+
private readonly readers;
|
|
27
|
+
constructor(readers?: readonly TranscriptReader[]);
|
|
28
|
+
register(reader: TranscriptReader): this;
|
|
29
|
+
get(format: TranscriptFormat): TranscriptReader | undefined;
|
|
30
|
+
has(format: TranscriptFormat): boolean;
|
|
31
|
+
formats(): TranscriptFormat[];
|
|
32
|
+
/**
|
|
33
|
+
* The reader for a format, or a loud failure. A log whose format nothing can
|
|
34
|
+
* read must not render as an empty conversation — the missing piece is an
|
|
35
|
+
* unregistered adapter, and saying so is the only useful outcome.
|
|
36
|
+
*/
|
|
37
|
+
require(format: TranscriptFormat): TranscriptReader;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A reader for a format whose entries the platform stores but cannot interpret.
|
|
41
|
+
*
|
|
42
|
+
* Every entry becomes an `unknown` event, which is enough to keep a log
|
|
43
|
+
* append-only, ordered and inspectable while its adapter is still being written.
|
|
44
|
+
*/
|
|
45
|
+
export declare function passthroughReader(format: TranscriptFormat, version?: number): TranscriptReader;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The transcript reader registry: one place per format that knows how to read
|
|
4
|
+
* it, looked up by the format a record was written under.
|
|
5
|
+
*
|
|
6
|
+
* A reader is registered *by value*, like an entry type, so a second harness
|
|
7
|
+
* ships its reader with its adapter and neither this package nor a renderer is
|
|
8
|
+
* released to support it. A record names its format, so a run that changed
|
|
9
|
+
* harness mid-flight still projects as one ordered log.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TranscriptReaderRegistry = void 0;
|
|
13
|
+
exports.passthroughReader = passthroughReader;
|
|
14
|
+
const entry_1 = require("./entry");
|
|
15
|
+
class TranscriptReaderRegistry {
|
|
16
|
+
readers = new Map();
|
|
17
|
+
constructor(readers = []) {
|
|
18
|
+
for (const reader of readers)
|
|
19
|
+
this.register(reader);
|
|
20
|
+
}
|
|
21
|
+
register(reader) {
|
|
22
|
+
this.readers.set(reader.format, reader);
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
get(format) {
|
|
26
|
+
return this.readers.get(format);
|
|
27
|
+
}
|
|
28
|
+
has(format) {
|
|
29
|
+
return this.readers.has(format);
|
|
30
|
+
}
|
|
31
|
+
formats() {
|
|
32
|
+
return Array.from(this.readers.keys()).sort();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The reader for a format, or a loud failure. A log whose format nothing can
|
|
36
|
+
* read must not render as an empty conversation — the missing piece is an
|
|
37
|
+
* unregistered adapter, and saying so is the only useful outcome.
|
|
38
|
+
*/
|
|
39
|
+
require(format) {
|
|
40
|
+
const reader = this.readers.get(format);
|
|
41
|
+
if (!reader) {
|
|
42
|
+
const known = this.formats();
|
|
43
|
+
throw new Error(`no transcript reader registered for format "${format}"` +
|
|
44
|
+
(known.length === 0 ? '' : `; this registry reads ${known.join(', ')}`));
|
|
45
|
+
}
|
|
46
|
+
return reader;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.TranscriptReaderRegistry = TranscriptReaderRegistry;
|
|
50
|
+
/**
|
|
51
|
+
* A reader for a format whose entries the platform stores but cannot interpret.
|
|
52
|
+
*
|
|
53
|
+
* Every entry becomes an `unknown` event, which is enough to keep a log
|
|
54
|
+
* append-only, ordered and inspectable while its adapter is still being written.
|
|
55
|
+
*/
|
|
56
|
+
function passthroughReader(format, version = 1) {
|
|
57
|
+
return {
|
|
58
|
+
format,
|
|
59
|
+
version,
|
|
60
|
+
assertEntry: entry_1.assertTranscriptEntry,
|
|
61
|
+
toEvents: (entry) => [
|
|
62
|
+
{
|
|
63
|
+
kind: 'unknown',
|
|
64
|
+
entryType: entry.type,
|
|
65
|
+
entry,
|
|
66
|
+
...(typeof entry.id === 'string' ? { entryId: entry.id } : {}),
|
|
67
|
+
...(typeof entry.timestamp === 'string' ? { at: entry.timestamp } : {})
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry the projectors use when a caller does not pass their own.
|
|
3
|
+
*
|
|
4
|
+
* pi is registered because it is the harness the platform ships; a host adding
|
|
5
|
+
* a second one either registers into its own registry or into this one at
|
|
6
|
+
* startup. Kept apart from `./reader` so the registry type does not depend on
|
|
7
|
+
* any format.
|
|
8
|
+
*/
|
|
9
|
+
import { TranscriptReaderRegistry } from './reader';
|
|
10
|
+
/** Readers registered out of the box. */
|
|
11
|
+
export declare const defaultTranscriptReaders: () => TranscriptReaderRegistry;
|
|
12
|
+
/**
|
|
13
|
+
* The process-wide registry. Mutable on purpose: a host registers its adapter's
|
|
14
|
+
* reader once at startup and every projector call then reads that format.
|
|
15
|
+
*/
|
|
16
|
+
export declare const transcriptReaders: TranscriptReaderRegistry;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The registry the projectors use when a caller does not pass their own.
|
|
4
|
+
*
|
|
5
|
+
* pi is registered because it is the harness the platform ships; a host adding
|
|
6
|
+
* a second one either registers into its own registry or into this one at
|
|
7
|
+
* startup. Kept apart from `./reader` so the registry type does not depend on
|
|
8
|
+
* any format.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.transcriptReaders = exports.defaultTranscriptReaders = void 0;
|
|
12
|
+
const pi_reader_1 = require("./pi-reader");
|
|
13
|
+
const reader_1 = require("./reader");
|
|
14
|
+
/** Readers registered out of the box. */
|
|
15
|
+
const defaultTranscriptReaders = () => new reader_1.TranscriptReaderRegistry([pi_reader_1.piTranscriptReader]);
|
|
16
|
+
exports.defaultTranscriptReaders = defaultTranscriptReaders;
|
|
17
|
+
/**
|
|
18
|
+
* The process-wide registry. Mutable on purpose: a host registers its adapter's
|
|
19
|
+
* reader once at startup and every projector call then reads that format.
|
|
20
|
+
*/
|
|
21
|
+
exports.transcriptReaders = (0, exports.defaultTranscriptReaders)();
|