oh-my-knowledge 0.53.0 → 0.54.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.
@@ -0,0 +1,48 @@
1
+ import type { ExperienceTurnStatus } from '../types/index.js';
2
+ import { type TraceSession } from '../observability/trace-ir.js';
3
+ export interface DshSessionHeaderLike {
4
+ readonly version: number;
5
+ readonly id: string;
6
+ readonly createdAt: number;
7
+ readonly cwd?: string;
8
+ readonly parentSession?: string;
9
+ readonly seedLength?: number;
10
+ readonly origin?: string;
11
+ readonly delegationDepth?: number;
12
+ readonly agentPreset?: string;
13
+ }
14
+ export interface DshSessionEventLike {
15
+ readonly type: string;
16
+ readonly seq: number;
17
+ readonly time: number;
18
+ readonly data: unknown;
19
+ readonly ignorable?: true;
20
+ readonly sourceEventSeqs?: readonly number[];
21
+ readonly surfaceOp?: unknown;
22
+ }
23
+ export interface DshTraceIntegrity {
24
+ complete: boolean;
25
+ status: ExperienceTurnStatus;
26
+ unknownEventCount: number;
27
+ ignoredChunkCount: number;
28
+ unmatchedToolCallCount: number;
29
+ unmatchedToolResultCount: number;
30
+ openTurnCount: number;
31
+ openStepCount: number;
32
+ }
33
+ export interface DshTraceAdapterOptions {
34
+ rootRunId?: string;
35
+ role?: TraceSession['role'];
36
+ groupPath?: string;
37
+ }
38
+ export interface DshTraceAdapterResult {
39
+ session: TraceSession;
40
+ integrity: DshTraceIntegrity;
41
+ }
42
+ export declare class DshTraceUnsupportedEventError extends Error {
43
+ readonly eventType: string;
44
+ readonly seq: number;
45
+ constructor(eventType: string, seq: number);
46
+ }
47
+ /** Project one validated, backend-neutral DSH logical log into OMK Trace IR. */
48
+ export declare function adaptDshSession(header: DshSessionHeaderLike, inputEvents: readonly DshSessionEventLike[], options?: DshTraceAdapterOptions): DshTraceAdapterResult;
@@ -0,0 +1,590 @@
1
+ import { normalizeToolIdentity } from '../shared/tool-identity.js';
2
+ import { correlateTraceToolEvents, createTraceId, traceTimestampBounds, } from '../observability/trace-ir.js';
3
+ export class DshTraceUnsupportedEventError extends Error {
4
+ eventType;
5
+ seq;
6
+ constructor(eventType, seq) {
7
+ super(`DSH session 包含 OMK 尚不认识的 required event:${eventType}(seq=${seq})。`);
8
+ this.eventType = eventType;
9
+ this.seq = seq;
10
+ this.name = 'DshTraceUnsupportedEventError';
11
+ }
12
+ }
13
+ const AUXILIARY_EVENT_TYPES = new Set([
14
+ 'agent-preset/selected',
15
+ 'agent/inbox/spliced',
16
+ 'approval/asked',
17
+ 'approval/decided',
18
+ 'approval/policy',
19
+ 'command/done',
20
+ 'command/run',
21
+ 'feedback/record',
22
+ 'goal/change',
23
+ 'hook/invoked',
24
+ 'hook/result',
25
+ 'llm/retry',
26
+ 'llm/retry-started',
27
+ 'permission/preset',
28
+ 'plan/mode',
29
+ 'sandbox/mode',
30
+ 'schedule/change',
31
+ 'session/title',
32
+ 'session/title-llm-request',
33
+ 'subagent/descriptor',
34
+ 'team/member',
35
+ 'team/message/delivered',
36
+ 'team/message/queued',
37
+ 'team/task',
38
+ 'tool-workflow/agent-end',
39
+ 'tool-workflow/agent-start',
40
+ 'tool-workflow/run-end',
41
+ 'tool-workflow/run-start',
42
+ 'tool/code-dispatch',
43
+ 'tool/code-dispatch-start',
44
+ 'web/deepseek-search-llm-request',
45
+ ]);
46
+ const PROJECTED_EVENT_TYPES = new Set([
47
+ 'assistant/chunk',
48
+ 'assistant/message',
49
+ 'request/context',
50
+ 'request/header',
51
+ 'session/end-seed',
52
+ 'step/end',
53
+ 'step/start',
54
+ 'todo/write',
55
+ 'tool/call',
56
+ 'tool/result',
57
+ 'turn/end',
58
+ 'turn/start',
59
+ 'user/message',
60
+ ]);
61
+ function supportsEventType(eventType) {
62
+ return PROJECTED_EVENT_TYPES.has(eventType)
63
+ || AUXILIARY_EVENT_TYPES.has(eventType)
64
+ || eventType.startsWith('compaction/');
65
+ }
66
+ function isRecord(value) {
67
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
68
+ }
69
+ function stringValue(value) {
70
+ return typeof value === 'string' && value.trim() ? value : undefined;
71
+ }
72
+ function nonNegativeInteger(value) {
73
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0
74
+ ? value
75
+ : undefined;
76
+ }
77
+ function timestamp(value) {
78
+ const milliseconds = nonNegativeInteger(value);
79
+ if (milliseconds === undefined)
80
+ return undefined;
81
+ return new Date(milliseconds).toISOString();
82
+ }
83
+ function textFromContent(value) {
84
+ if (typeof value === 'string')
85
+ return value;
86
+ if (!Array.isArray(value))
87
+ return '';
88
+ return value.flatMap((block) => (isRecord(block) && block.type === 'text' && typeof block.text === 'string'
89
+ ? [block.text]
90
+ : [])).join('');
91
+ }
92
+ function reasoningFromContent(value) {
93
+ if (!Array.isArray(value))
94
+ return [];
95
+ return value.flatMap((block) => (isRecord(block) && block.type === 'reasoning' && typeof block.text === 'string'
96
+ ? [block.text]
97
+ : []));
98
+ }
99
+ function jsonText(value) {
100
+ try {
101
+ return JSON.stringify(value);
102
+ }
103
+ catch {
104
+ return String(value ?? '');
105
+ }
106
+ }
107
+ function objectInput(value) {
108
+ if (isRecord(value))
109
+ return value;
110
+ if (typeof value === 'string') {
111
+ try {
112
+ const parsed = JSON.parse(value);
113
+ if (isRecord(parsed))
114
+ return parsed;
115
+ }
116
+ catch {
117
+ // Preserve the exact provider output below.
118
+ }
119
+ return { rawArguments: value };
120
+ }
121
+ return { rawArguments: value ?? null };
122
+ }
123
+ function turnId(runId, data) {
124
+ const turn = nonNegativeInteger(data.turn);
125
+ return turn === undefined ? undefined : `dsh:${runId}:turn:${turn}`;
126
+ }
127
+ function eventBase(runId, event, sourceIndex, suffix) {
128
+ const data = isRecord(event.data) ? event.data : {};
129
+ return {
130
+ eventId: `${runId}:${event.seq}:${suffix}`,
131
+ sourceIndex,
132
+ sourceType: event.type,
133
+ timestamp: timestamp(event.time),
134
+ turnId: turnId(runId, data),
135
+ };
136
+ }
137
+ function messageOrigin(source) {
138
+ if (!isRecord(source))
139
+ return 'synthetic';
140
+ const sourceKind = stringValue(source.kind);
141
+ if (sourceKind === 'user')
142
+ return 'human';
143
+ if (sourceKind === 'skill-invocation' || sourceKind === 'skill-catalog')
144
+ return 'skill-context';
145
+ if (sourceKind?.includes('skill'))
146
+ return 'skill-context';
147
+ if (sourceKind === 'plugin' || sourceKind === 'agent-instructions')
148
+ return 'runtime';
149
+ return 'synthetic';
150
+ }
151
+ function turnEndProjection(reason) {
152
+ const record = isRecord(reason) ? reason : {};
153
+ const reasonKind = stringValue(record.kind) ?? 'unknown';
154
+ if (reasonKind === 'completed')
155
+ return { phase: 'turn_completed', status: 'completed', reason: reasonKind };
156
+ if (reasonKind === 'aborted')
157
+ return { phase: 'turn_aborted', status: 'aborted', reason: jsonText(record) };
158
+ if (reasonKind === 'interrupted')
159
+ return { phase: 'turn_interrupted', status: 'interrupted', reason: reasonKind };
160
+ if (reasonKind === 'error' || reasonKind === 'blocked' || reasonKind === 'max-tokens') {
161
+ return { phase: 'turn_failed', status: 'failed', reason: jsonText(record) };
162
+ }
163
+ return {
164
+ phase: 'turn_ended_unknown',
165
+ status: 'unknown',
166
+ reason: `unknown terminal reason: ${jsonText(record)}`,
167
+ };
168
+ }
169
+ function requestFacts(data) {
170
+ if (isRecord(data.header)) {
171
+ const config = isRecord(data.header.config) ? data.header.config : {};
172
+ return {
173
+ model: stringValue(config.model),
174
+ provider: stringValue(config.provider),
175
+ reasoningEffort: stringValue(config.reasoningEffort),
176
+ };
177
+ }
178
+ return {
179
+ model: stringValue(data.model),
180
+ provider: stringValue(data.provider),
181
+ contextWindowId: nonNegativeInteger(data.contextWindow)?.toString(),
182
+ };
183
+ }
184
+ function compactionEvents(runId, event, sourceIndex) {
185
+ const data = isRecord(event.data) ? event.data : {};
186
+ if (event.type === 'compaction/summary') {
187
+ return [{
188
+ ...eventBase(runId, event, sourceIndex, 'compaction-summary'),
189
+ eventKind: 'context_compaction',
190
+ summary: textFromContent(data.summary),
191
+ replacementItemCount: Array.isArray(data.shadowedSeqs) ? data.shadowedSeqs.length : undefined,
192
+ }];
193
+ }
194
+ return [{
195
+ ...eventBase(runId, event, sourceIndex, 'compaction'),
196
+ eventKind: 'runtime_context',
197
+ runtimeKind: 'execution_context',
198
+ summary: `${event.type}: ${jsonText(data)}`,
199
+ }];
200
+ }
201
+ function auxiliaryEvent(runId, event, sourceIndex) {
202
+ const data = isRecord(event.data) ? event.data : {};
203
+ const agentActivity = event.type.startsWith('subagent/')
204
+ || event.type.startsWith('team/')
205
+ || event.type.startsWith('tool-workflow/');
206
+ if (agentActivity) {
207
+ return {
208
+ ...eventBase(runId, event, sourceIndex, 'agent-activity'),
209
+ eventKind: 'agent_activity',
210
+ activityKind: event.type.includes('message') ? 'communication' : 'status',
211
+ activity: event.type,
212
+ agentId: stringValue(data.agentId) ?? stringValue(data.memberId),
213
+ author: stringValue(data.author),
214
+ recipient: stringValue(data.recipient),
215
+ text: jsonText(data),
216
+ };
217
+ }
218
+ return {
219
+ ...eventBase(runId, event, sourceIndex, 'runtime-context'),
220
+ eventKind: 'runtime_context',
221
+ runtimeKind: event.type.startsWith('goal/') || event.type === 'todo/write' ? 'goal' : 'execution_context',
222
+ goal: event.type.startsWith('goal/') || event.type === 'todo/write' ? jsonText(data) : undefined,
223
+ summary: `${event.type}: ${jsonText(data)}`,
224
+ };
225
+ }
226
+ function toolResultData(data) {
227
+ const message = isRecord(data.message) ? data.message : {};
228
+ const blocks = Array.isArray(message.content) ? message.content.filter(isRecord) : [];
229
+ const result = blocks.find((block) => block.type === 'tool-result');
230
+ const content = isRecord(result) ? result.content : undefined;
231
+ const output = textFromContent(content) || jsonText(content ?? result ?? data);
232
+ return {
233
+ callId: isRecord(result) ? stringValue(result.toolCallId) : undefined,
234
+ output,
235
+ failed: data.error !== undefined || (isRecord(result) && result.isError === true),
236
+ };
237
+ }
238
+ function validateEnvelope(event, expectedSeq) {
239
+ if (!Number.isSafeInteger(event.seq) || event.seq !== expectedSeq) {
240
+ throw new Error(`DSH session event seq 不连续:期望 ${expectedSeq},实际 ${String(event.seq)}。`);
241
+ }
242
+ if (!Number.isSafeInteger(event.time) || event.time < 0) {
243
+ throw new Error(`DSH session event time 非法:seq=${event.seq}。`);
244
+ }
245
+ if (!event.type.trim())
246
+ throw new Error(`DSH session event type 为空:seq=${event.seq}。`);
247
+ }
248
+ /** Project one validated, backend-neutral DSH logical log into OMK Trace IR. */
249
+ export function adaptDshSession(header, inputEvents, options = {}) {
250
+ const runId = String(header.id);
251
+ if (!runId.trim())
252
+ throw new Error('DSH session header 缺少 id。');
253
+ const seedLength = header.seedLength === undefined ? 0 : nonNegativeInteger(header.seedLength);
254
+ if (seedLength === undefined || seedLength > inputEvents.length) {
255
+ throw new Error(`DSH session header seedLength 非法:${String(header.seedLength)}。`);
256
+ }
257
+ inputEvents.forEach(validateEnvelope);
258
+ const activeEvents = inputEvents.slice(seedLength);
259
+ const isSubagent = header.origin === 'subagent';
260
+ const sourcePath = `dsh:${runId}`;
261
+ const traceId = createTraceId({ sourceKind: 'dsh', runId, sourcePath });
262
+ const rootRunId = options.rootRunId ?? (isSubagent ? header.parentSession : undefined) ?? runId;
263
+ const role = options.role ?? (isSubagent ? 'subagent' : 'main');
264
+ const events = [{
265
+ eventKind: 'lifecycle',
266
+ eventId: `${runId}:header:session-started`,
267
+ sourceIndex: 0,
268
+ sourceType: 'session/header',
269
+ timestamp: timestamp(header.createdAt),
270
+ phase: 'session_started',
271
+ }, {
272
+ eventKind: 'runtime_context',
273
+ eventId: `${runId}:header:context`,
274
+ sourceIndex: 0,
275
+ sourceType: 'session/header',
276
+ timestamp: timestamp(header.createdAt),
277
+ runtimeKind: 'session_context',
278
+ runtimeName: 'DeepSeek Harness',
279
+ cwd: header.cwd,
280
+ parentRunId: isSubagent ? header.parentSession : undefined,
281
+ delegationDepth: header.delegationDepth,
282
+ sourceOrigin: header.origin,
283
+ historyMode: header.seedLength === undefined ? undefined : `seed:${header.seedLength}`,
284
+ summary: jsonText({
285
+ agentPreset: header.agentPreset,
286
+ parentSession: header.parentSession,
287
+ origin: header.origin,
288
+ delegationDepth: header.delegationDepth,
289
+ seedLength: header.seedLength,
290
+ }),
291
+ }];
292
+ const pendingTools = new Set();
293
+ const openTurns = new Set();
294
+ const openSteps = new Set();
295
+ const assembledSteps = new Set();
296
+ let unknownEventCount = 0;
297
+ let ignoredChunkCount = 0;
298
+ let unmatchedToolResultCount = 0;
299
+ const terminal = { status: 'unknown' };
300
+ let observedModel;
301
+ let observedProvider;
302
+ for (const event of activeEvents) {
303
+ if (event.type !== 'assistant/message')
304
+ continue;
305
+ const data = isRecord(event.data) ? event.data : {};
306
+ const turn = nonNegativeInteger(data.turn);
307
+ const step = nonNegativeInteger(data.step);
308
+ if (turn !== undefined && step !== undefined)
309
+ assembledSteps.add(`${turn}:${step}`);
310
+ }
311
+ inputEvents.slice(0, seedLength).forEach((event) => {
312
+ if (event.ignorable !== true && !supportsEventType(event.type)) {
313
+ throw new DshTraceUnsupportedEventError(event.type, event.seq);
314
+ }
315
+ });
316
+ activeEvents.forEach((event, activeIndex) => {
317
+ const index = seedLength + activeIndex;
318
+ const sourceIndex = index + 1;
319
+ const data = isRecord(event.data) ? event.data : {};
320
+ const base = (suffix) => eventBase(runId, event, sourceIndex, suffix);
321
+ const sourceEventId = (value) => {
322
+ const id = stringValue(value);
323
+ return id ? { sourceEventId: id } : {};
324
+ };
325
+ if (event.type === 'turn/start') {
326
+ const turn = nonNegativeInteger(data.turn);
327
+ if (turn === undefined)
328
+ throw new Error(`DSH turn/start 缺少 turn:seq=${event.seq}。`);
329
+ openTurns.add(turn);
330
+ terminal.status = 'open';
331
+ events.push({ ...base('turn-start'), eventKind: 'lifecycle', phase: 'turn_started' });
332
+ return;
333
+ }
334
+ if (event.type === 'turn/end') {
335
+ const turn = nonNegativeInteger(data.turn);
336
+ if (turn === undefined)
337
+ throw new Error(`DSH turn/end 缺少 turn:seq=${event.seq}。`);
338
+ openTurns.delete(turn);
339
+ const projection = turnEndProjection(data.reason);
340
+ terminal.status = projection.status;
341
+ events.push({
342
+ ...base('turn-end'),
343
+ eventKind: 'lifecycle',
344
+ phase: projection.phase,
345
+ reason: projection.reason,
346
+ });
347
+ return;
348
+ }
349
+ if (event.type === 'step/start' || event.type === 'step/end') {
350
+ const turn = nonNegativeInteger(data.turn);
351
+ const step = nonNegativeInteger(data.step);
352
+ if (turn === undefined || step === undefined) {
353
+ throw new Error(`DSH ${event.type} 缺少 turn/step:seq=${event.seq}。`);
354
+ }
355
+ const key = `${turn}:${step}`;
356
+ if (event.type === 'step/start')
357
+ openSteps.add(key);
358
+ else
359
+ openSteps.delete(key);
360
+ events.push({
361
+ ...base(event.type === 'step/start' ? 'step-start' : 'step-end'),
362
+ eventKind: 'lifecycle',
363
+ phase: event.type === 'step/start' ? 'step_started' : 'step_completed',
364
+ reason: `step=${step}`,
365
+ });
366
+ return;
367
+ }
368
+ if (event.type === 'user/message') {
369
+ const text = textFromContent(data.content);
370
+ const source = isRecord(data.source) ? data.source : undefined;
371
+ events.push({
372
+ ...base('user-message'),
373
+ ...sourceEventId(data.id),
374
+ eventKind: 'message',
375
+ role: 'user',
376
+ origin: messageOrigin(source),
377
+ text,
378
+ attributionSkill: source?.kind === 'skill-invocation' ? stringValue(source.name) : undefined,
379
+ });
380
+ return;
381
+ }
382
+ if (event.type === 'assistant/message') {
383
+ const message = isRecord(data.message) ? data.message : {};
384
+ const source = isRecord(message.source) ? message.source : {};
385
+ const model = stringValue(source.model);
386
+ const provider = stringValue(source.provider);
387
+ observedModel = model ?? observedModel;
388
+ observedProvider = provider ?? observedProvider;
389
+ const text = textFromContent(message.content);
390
+ if (text) {
391
+ events.push({
392
+ ...base('assistant-message'),
393
+ ...sourceEventId(message.id),
394
+ eventKind: 'message',
395
+ role: 'assistant',
396
+ origin: 'synthetic',
397
+ text,
398
+ model,
399
+ });
400
+ }
401
+ reasoningFromContent(message.content).forEach((reasoning, reasoningIndex) => {
402
+ events.push({
403
+ ...base(`reasoning-${reasoningIndex}`),
404
+ ...sourceEventId(message.id),
405
+ eventKind: 'model_activity',
406
+ activityKind: 'reasoning',
407
+ contentVisibility: 'plaintext',
408
+ contentSource: 'content',
409
+ text: reasoning,
410
+ model,
411
+ });
412
+ });
413
+ if (isRecord(data.usage)) {
414
+ const inputTokens = nonNegativeInteger(data.usage.inputTokens);
415
+ const outputTokens = nonNegativeInteger(data.usage.outputTokens);
416
+ if (inputTokens === undefined || outputTokens === undefined) {
417
+ throw new Error(`DSH assistant/message usage 非法:seq=${event.seq}。`);
418
+ }
419
+ events.push({
420
+ ...base('usage'),
421
+ eventKind: 'usage',
422
+ model,
423
+ inputTokens,
424
+ outputTokens,
425
+ cacheReadTokens: nonNegativeInteger(data.usage.cacheReadTokens),
426
+ cacheCreationTokens: nonNegativeInteger(data.usage.cacheWriteTokens),
427
+ reasoningTokens: nonNegativeInteger(data.usage.reasoningTokens),
428
+ });
429
+ }
430
+ return;
431
+ }
432
+ if (event.type === 'assistant/chunk') {
433
+ const turn = nonNegativeInteger(data.turn);
434
+ const step = nonNegativeInteger(data.step);
435
+ const chunk = isRecord(data.chunk) ? data.chunk : {};
436
+ if (turn !== undefined && step !== undefined && assembledSteps.has(`${turn}:${step}`)) {
437
+ ignoredChunkCount += 1;
438
+ return;
439
+ }
440
+ if (chunk.type === 'reasoning-delta' && typeof chunk.text === 'string' && chunk.text) {
441
+ events.push({
442
+ ...base('reasoning-delta'),
443
+ eventKind: 'model_activity',
444
+ activityKind: 'reasoning',
445
+ contentVisibility: 'plaintext',
446
+ contentSource: 'text',
447
+ text: chunk.text,
448
+ model: observedModel,
449
+ });
450
+ }
451
+ else if (chunk.type === 'text-delta' && typeof chunk.text === 'string' && chunk.text) {
452
+ events.push({
453
+ ...base('text-delta'),
454
+ eventKind: 'message',
455
+ role: 'assistant',
456
+ origin: 'synthetic',
457
+ text: chunk.text,
458
+ model: observedModel,
459
+ });
460
+ }
461
+ else {
462
+ ignoredChunkCount += 1;
463
+ }
464
+ return;
465
+ }
466
+ if (event.type === 'tool/call') {
467
+ const callId = stringValue(data.callId);
468
+ const name = stringValue(data.name);
469
+ if (!callId || !name)
470
+ throw new Error(`DSH tool/call 缺少 callId/name:seq=${event.seq}。`);
471
+ pendingTools.add(callId);
472
+ events.push({
473
+ ...base('tool-call'),
474
+ ...sourceEventId(callId),
475
+ eventKind: 'tool_call',
476
+ callId,
477
+ callInstanceId: `${runId}:${callId}`,
478
+ tool: normalizeToolIdentity({ sourceName: name }),
479
+ input: objectInput(data.arguments),
480
+ model: observedModel,
481
+ });
482
+ return;
483
+ }
484
+ if (event.type === 'tool/result') {
485
+ const result = toolResultData(data);
486
+ if (!result.callId)
487
+ throw new Error(`DSH tool/result 缺少 toolCallId:seq=${event.seq}。`);
488
+ if (!pendingTools.delete(result.callId))
489
+ unmatchedToolResultCount += 1;
490
+ events.push({
491
+ ...base('tool-result'),
492
+ ...sourceEventId(result.callId),
493
+ eventKind: 'tool_result',
494
+ callId: result.callId,
495
+ callInstanceId: `${runId}:${result.callId}`,
496
+ output: result.output,
497
+ status: result.failed ? 'failure' : 'success',
498
+ statusSource: 'runtime',
499
+ });
500
+ return;
501
+ }
502
+ if (event.type === 'request/header' || event.type === 'request/context') {
503
+ const facts = requestFacts(data);
504
+ observedModel = facts.model ?? observedModel;
505
+ observedProvider = facts.provider ?? observedProvider;
506
+ events.push({
507
+ ...base('request-context'),
508
+ eventKind: 'runtime_context',
509
+ runtimeKind: 'execution_context',
510
+ runtimeName: 'DeepSeek Harness',
511
+ model: facts.model,
512
+ modelProvider: facts.provider,
513
+ reasoningEffort: facts.reasoningEffort,
514
+ contextWindowId: facts.contextWindowId,
515
+ summary: `${event.type}: ${jsonText(data)}`,
516
+ });
517
+ return;
518
+ }
519
+ if (event.type === 'session/end-seed') {
520
+ events.push({
521
+ ...base('seed-boundary'),
522
+ eventKind: 'runtime_context',
523
+ runtimeKind: 'session_context',
524
+ historyMode: `seed-boundary:${event.seq}`,
525
+ summary: 'DSH session seed boundary',
526
+ });
527
+ return;
528
+ }
529
+ if (event.type.startsWith('compaction/')) {
530
+ events.push(...compactionEvents(runId, event, sourceIndex));
531
+ return;
532
+ }
533
+ if (event.type === 'todo/write' || AUXILIARY_EVENT_TYPES.has(event.type)) {
534
+ events.push(auxiliaryEvent(runId, event, sourceIndex));
535
+ return;
536
+ }
537
+ if (event.ignorable === true) {
538
+ unknownEventCount += 1;
539
+ events.push({
540
+ ...base('unknown'),
541
+ eventKind: 'unknown',
542
+ raw: event,
543
+ });
544
+ return;
545
+ }
546
+ throw new DshTraceUnsupportedEventError(event.type, event.seq);
547
+ });
548
+ const correlated = correlateTraceToolEvents(events);
549
+ const bounds = traceTimestampBounds(correlated.map((event) => event.timestamp));
550
+ const unmatchedToolCallCount = pendingTools.size;
551
+ const openTurnCount = openTurns.size;
552
+ const openStepCount = openSteps.size;
553
+ const complete = unmatchedToolCallCount === 0
554
+ && unmatchedToolResultCount === 0
555
+ && openTurnCount === 0
556
+ && openStepCount === 0
557
+ && terminal.status !== 'open'
558
+ && terminal.status !== 'unknown';
559
+ return {
560
+ session: {
561
+ runId,
562
+ rootRunId,
563
+ ...(isSubagent && header.parentSession ? { parentRunId: header.parentSession } : {}),
564
+ traceId,
565
+ groupPath: options.groupPath ?? `dsh:${rootRunId}`,
566
+ role,
567
+ label: role === 'subagent' ? `DSH subagent ${runId}` : `DSH session ${runId}`,
568
+ sourcePath,
569
+ sourceKind: 'dsh',
570
+ events: correlated,
571
+ cwd: header.cwd,
572
+ entrypoint: 'dsh',
573
+ sourceMetadata: {
574
+ provider: observedProvider,
575
+ model: observedModel,
576
+ },
577
+ ...bounds,
578
+ },
579
+ integrity: {
580
+ complete,
581
+ status: terminal.status,
582
+ unknownEventCount,
583
+ ignoredChunkCount,
584
+ unmatchedToolCallCount,
585
+ unmatchedToolResultCount,
586
+ openTurnCount,
587
+ openStepCount,
588
+ },
589
+ };
590
+ }
@@ -542,7 +542,7 @@ function trajectoryRevisionFromStat(sourceStat, status) {
542
542
  return `${sourceStat.size}:${sourceStat.mtimeMs}:${status}`;
543
543
  }
544
544
  function isTerminalTaskStatus(status) {
545
- return status === 'completed' || status === 'aborted' || status === 'interrupted';
545
+ return status === 'completed' || status === 'failed' || status === 'aborted' || status === 'interrupted';
546
546
  }
547
547
  function secondsToMs(value) {
548
548
  const seconds = numberValue(value);
@@ -1012,6 +1012,7 @@ function isExperienceTurnSummaryArray(value) {
1012
1012
  && isOptionalTimestamp(turn.startTimestamp)
1013
1013
  && isOptionalTimestamp(turn.endTimestamp)
1014
1014
  && (turn.status === 'completed'
1015
+ || turn.status === 'failed'
1015
1016
  || turn.status === 'aborted'
1016
1017
  || turn.status === 'interrupted'
1017
1018
  || turn.status === 'open'
@@ -2621,6 +2622,9 @@ function timelineEventsFromTraceEvent(session, event, eventIndex) {
2621
2622
  memoryMode: event.memoryMode,
2622
2623
  historyMode: event.historyMode,
2623
2624
  contextWindowId: event.contextWindowId,
2625
+ parentRunId: event.parentRunId,
2626
+ delegationDepth: event.delegationDepth,
2627
+ sourceOrigin: event.sourceOrigin,
2624
2628
  availableTools: event.availableTools,
2625
2629
  instructions: event.instructions,
2626
2630
  goal: event.goal,
@@ -1,4 +1,5 @@
1
- import type { BuildObservationInboxReportOptions, ObservationEvidence, ObservationInboxItem, ObservationInboxReport, ObservationMessageRef, ObservationMessageWindow, ObservationSessionTimeRange, ObservationSeverityReasonCode, ObservationSignalSubtype, ObservationSignalType, ObservationSkillRollup, ObservationSourceKind } from '../types/index.js';
1
+ import type { BuildObservationInboxReportOptions, ObservationEvidence, ObservationInboxItem, ObservationInboxReport, ObservationMessageRef, ObservationMessageWindow, ObservationSessionTimeRange, ObservationSeverityReasonCode, ObservationSignalSubtype, ObservationSignalType, ObservationSkillRollup, ObservationSourceKind, TraceIngestionSummary } from '../types/index.js';
2
+ import { type TraceSession } from './trace-adapter.js';
2
3
  import { type PersistedObservationExperienceReport } from './experience.js';
3
4
  export type { BuildObservationInboxReportOptions, ObservationEvidence, ObservationInboxItem, ObservationInboxReport, ObservationMessageRef, ObservationMessageWindow, ObservationSessionTimeRange, ObservationSeverityReasonCode, ObservationSignalSubtype, ObservationSignalType, ObservationSkillRollup, ObservationSourceKind, };
4
5
  export type PersistedObservationInboxReport = Omit<ObservationInboxReport, 'experience'> & {
@@ -19,6 +20,8 @@ export declare function severityReasonCodeFor(item: Pick<ObservationInboxItem, '
19
20
  * `buildObserveDiagnosticsFromReport(report)` 写入 `report.diagnostics`。
20
21
  */
21
22
  export declare function buildObservationInboxReport(tracePath: string, options?: BuildObservationInboxReportOptions): ObservationInboxReport;
23
+ /** Build the existing inbox artifact from an in-memory source-neutral corpus. */
24
+ export declare function buildObservationInboxReportFromTraceSessions(tracePath: string, sessions: TraceSession[], ingestion: TraceIngestionSummary, options?: BuildObservationInboxReportOptions): ObservationInboxReport;
22
25
  export declare function aggregateInboxItems(items: ObservationInboxItem[]): ObservationInboxItem[];
23
26
  export declare function saveObservationInboxReport(report: ObservationInboxReport, outDir?: string): string;
24
27
  export declare function compactObservationInboxReport(report: ObservationInboxReport): PersistedObservationInboxReport;