oh-my-knowledge 0.51.2 → 0.52.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.
Files changed (49) hide show
  1. package/README.md +27 -6
  2. package/README.zh.md +27 -6
  3. package/dist/assets/agent-skills/omk/SKILL.md +8 -8
  4. package/dist/observability/codex-conversation-index.d.ts +88 -0
  5. package/dist/observability/codex-conversation-index.js +566 -0
  6. package/dist/observability/codex-protocol.d.ts +12 -0
  7. package/dist/observability/codex-protocol.js +78 -0
  8. package/dist/observability/codex-tool-status.d.ts +16 -0
  9. package/dist/observability/codex-tool-status.js +114 -0
  10. package/dist/observability/codex-trace-adapter.js +342 -119
  11. package/dist/observability/conversation-catalog.d.ts +38 -0
  12. package/dist/observability/conversation-catalog.js +573 -0
  13. package/dist/observability/conversation-index-process.d.ts +1 -0
  14. package/dist/observability/conversation-index-process.js +31 -0
  15. package/dist/observability/conversation-view-model.d.ts +5 -0
  16. package/dist/observability/conversation-view-model.js +100 -0
  17. package/dist/observability/experience.d.ts +3 -1
  18. package/dist/observability/experience.js +248 -7
  19. package/dist/observability/inbox.js +40 -2
  20. package/dist/observability/knowledge-debugger.d.ts +4 -0
  21. package/dist/observability/knowledge-debugger.js +364 -0
  22. package/dist/observability/polling-subscription-hub.d.ts +27 -0
  23. package/dist/observability/polling-subscription-hub.js +149 -0
  24. package/dist/observability/source-record-archive.d.ts +13 -0
  25. package/dist/observability/source-record-archive.js +324 -0
  26. package/dist/observability/task-window.d.ts +8 -0
  27. package/dist/observability/task-window.js +36 -0
  28. package/dist/observability/trace-ir.d.ts +66 -1
  29. package/dist/observability/trace-source.d.ts +1 -0
  30. package/dist/observability/trace-source.js +12 -5
  31. package/dist/observability/turn-index.d.ts +3 -0
  32. package/dist/observability/turn-index.js +251 -0
  33. package/dist/renderer/conversation-renderer.d.ts +9 -0
  34. package/dist/renderer/conversation-renderer.js +410 -0
  35. package/dist/renderer/icons.js +1 -0
  36. package/dist/renderer/inline-markdown.d.ts +6 -0
  37. package/dist/renderer/inline-markdown.js +158 -0
  38. package/dist/renderer/knowledge-debugger-renderer.d.ts +9 -0
  39. package/dist/renderer/knowledge-debugger-renderer.js +2280 -0
  40. package/dist/renderer/observation-inbox-renderer.js +9 -0
  41. package/dist/renderer/skill-list-renderer.js +2 -1
  42. package/dist/renderer/trajectory-live.d.ts +42 -0
  43. package/dist/renderer/trajectory-live.js +258 -0
  44. package/dist/renderer/trajectory-routing.d.ts +60 -0
  45. package/dist/renderer/trajectory-routing.js +351 -0
  46. package/dist/server/report-server.d.ts +4 -1
  47. package/dist/server/report-server.js +274 -6
  48. package/dist/types/observability.d.ts +225 -1
  49. package/package.json +8 -5
@@ -0,0 +1,251 @@
1
+ import { createHash } from 'node:crypto';
2
+ const TERMINAL_LIFECYCLE_LABELS = new Set([
3
+ 'turn_completed',
4
+ 'turn_aborted',
5
+ 'turn_interrupted',
6
+ ]);
7
+ /** Reconstruct every observable task without consulting Skill attribution. */
8
+ export function reconstructExperienceTurns(timeline) {
9
+ const events = stableTimeline(timeline);
10
+ const candidates = [
11
+ ...turnIdCandidates(events),
12
+ ...lifecycleCandidates(events),
13
+ ...userMessageCandidates(events),
14
+ ].sort((left, right) => left.start - right.start || left.end - right.end);
15
+ return candidates.map(turnSummary);
16
+ }
17
+ function turnIdCandidates(events) {
18
+ const groups = new Map();
19
+ events.forEach((event, position) => {
20
+ if (!event.turnId)
21
+ return;
22
+ const key = `${traceKey(event)}\u0000${event.turnId}`;
23
+ groups.set(key, [...(groups.get(key) ?? []), { event, position }]);
24
+ });
25
+ return Array.from(groups.values()).map((group) => {
26
+ const first = group[0];
27
+ const last = group.at(-1);
28
+ if (!first || !last || !first.event.turnId)
29
+ return undefined;
30
+ const start = expandTurnStart(events, first.position, traceKey(first.event));
31
+ const end = expandTurnEnd(events, last.position, traceKey(last.event), first.event.turnId);
32
+ return candidate('turn_id', events, start, end, traceKey(first.event), first.event.turnId);
33
+ }).filter((item) => Boolean(item));
34
+ }
35
+ function lifecycleCandidates(events) {
36
+ const byTrace = indexedEventsByTrace(events);
37
+ const candidates = [];
38
+ for (const traceEvents of byTrace.values()) {
39
+ if (traceEvents.some(({ event }) => event.turnId))
40
+ continue;
41
+ for (let index = 0; index < traceEvents.length; index += 1) {
42
+ const opening = traceEvents[index];
43
+ if (!opening || opening.event.label !== 'turn_started')
44
+ continue;
45
+ const nextOpeningIndex = traceEvents.findIndex((item, candidateIndex) => (candidateIndex > index && item.event.label === 'turn_started'));
46
+ const searchEnd = nextOpeningIndex >= 0 ? nextOpeningIndex : traceEvents.length;
47
+ const terminal = traceEvents.slice(index + 1, searchEnd).find((item) => (TERMINAL_LIFECYCLE_LABELS.has(item.event.label ?? '')));
48
+ const last = terminal ?? traceEvents[Math.max(index, searchEnd - 1)] ?? opening;
49
+ const start = expandTurnStart(events, opening.position, traceKey(opening.event));
50
+ candidates.push(candidate('turn_lifecycle', events, start, last.position, traceKey(opening.event), opening.event.turnId));
51
+ }
52
+ }
53
+ return candidates;
54
+ }
55
+ function userMessageCandidates(events) {
56
+ const byTrace = indexedEventsByTrace(events);
57
+ const candidates = [];
58
+ for (const traceEvents of byTrace.values()) {
59
+ if (traceEvents.some(({ event }) => hasNativeTurnBoundary(event)))
60
+ continue;
61
+ const users = traceEvents.filter((item) => isHumanUserMessage(item.event));
62
+ users.forEach((user, userIndex) => {
63
+ const nextUser = users[userIndex + 1];
64
+ const terminal = traceEvents.find((item) => (item.position > user.position
65
+ && (!nextUser || item.position < nextUser.position)
66
+ && TERMINAL_LIFECYCLE_LABELS.has(item.event.label ?? '')));
67
+ const end = terminal?.position
68
+ ?? (nextUser
69
+ ? previousGlobalPosition(events, nextUser.position, traceKey(user.event))
70
+ : traceEvents.at(-1)?.position ?? user.position);
71
+ candidates.push(candidate('user_message', events, expandUserTaskStart(events, user.position, traceKey(user.event)), end, traceKey(user.event)));
72
+ });
73
+ }
74
+ return candidates;
75
+ }
76
+ function candidate(basis, events, start, end, eventTraceKey, sourceTurnId) {
77
+ const safeStart = Math.max(0, Math.min(start, events.length - 1));
78
+ const safeEnd = Math.max(safeStart, Math.min(end, events.length - 1));
79
+ return {
80
+ basis,
81
+ sourceTurnId,
82
+ start: safeStart,
83
+ end: safeEnd,
84
+ events: events.slice(safeStart, safeEnd + 1)
85
+ .filter((event) => traceKey(event) === eventTraceKey),
86
+ };
87
+ }
88
+ function turnSummary(value) {
89
+ const events = value.events;
90
+ const first = events[0];
91
+ return {
92
+ turnId: value.sourceTurnId ?? syntheticTurnId(value),
93
+ sourceTurnId: value.sourceTurnId,
94
+ boundaryBasis: value.basis,
95
+ traceId: first?.traceId,
96
+ sourceTrace: first?.sourceTrace ?? '',
97
+ startTimestamp: events.find((event) => event.timestamp)?.timestamp,
98
+ endTimestamp: [...events].reverse().find((event) => event.timestamp)?.timestamp,
99
+ status: turnStatus(events),
100
+ title: turnTitle(events),
101
+ eventIds: events.map((event) => event.id),
102
+ userMessageCount: events.filter(isHumanUserMessage).length,
103
+ assistantMessageCount: events.filter((event) => event.kind === 'assistant_message').length,
104
+ toolCallCount: events.filter((event) => event.kind === 'tool_use').length,
105
+ toolFailureCount: events.filter((event) => (event.kind === 'tool_result'
106
+ && (event.toolStatus === 'failure' || event.isError))).length,
107
+ };
108
+ }
109
+ function syntheticTurnId(value) {
110
+ const first = value.events[0];
111
+ const last = value.events.at(-1);
112
+ const digest = createHash('sha256')
113
+ .update([
114
+ value.basis,
115
+ first ? traceKey(first) : '',
116
+ first?.id ?? String(value.start),
117
+ last?.id ?? String(value.end),
118
+ ].join('\u0000'))
119
+ .digest('hex')
120
+ .slice(0, 20);
121
+ return `turn:${digest}`;
122
+ }
123
+ function turnStatus(events) {
124
+ const labels = new Set(events.map((event) => event.label));
125
+ if (labels.has('turn_completed'))
126
+ return 'completed';
127
+ if (labels.has('turn_aborted'))
128
+ return 'aborted';
129
+ if (labels.has('turn_interrupted'))
130
+ return 'interrupted';
131
+ if (labels.has('turn_started'))
132
+ return 'open';
133
+ return 'unknown';
134
+ }
135
+ function turnTitle(events) {
136
+ const userText = events.find(isHumanUserMessage);
137
+ const assistantText = events.find((event) => event.kind === 'assistant_message');
138
+ const fallback = events.find((event) => event.kind !== 'runtime_context' && event.kind !== 'lifecycle');
139
+ return eventDisplayText(userText)
140
+ ?? eventDisplayText(assistantText)
141
+ ?? eventDisplayText(fallback)
142
+ ?? '未命名任务';
143
+ }
144
+ function eventDisplayText(event) {
145
+ const value = event?.snippet?.trim()
146
+ || event?.fullText?.trim()
147
+ || event?.label?.trim();
148
+ if (!value)
149
+ return undefined;
150
+ return value.replace(/\s+/g, ' ').slice(0, 160);
151
+ }
152
+ function expandTurnStart(events, firstPosition, eventTraceKey) {
153
+ let start = firstPosition;
154
+ let foundUser = false;
155
+ for (let position = firstPosition - 1; position >= 0; position -= 1) {
156
+ const event = events[position];
157
+ if (!event || traceKey(event) !== eventTraceKey)
158
+ continue;
159
+ if (TERMINAL_LIFECYCLE_LABELS.has(event.label ?? '') || event.label === 'turn_started')
160
+ break;
161
+ if (event.turnId && events[firstPosition]?.turnId && event.turnId !== events[firstPosition]?.turnId)
162
+ break;
163
+ if (isHumanUserMessage(event)) {
164
+ if (foundUser)
165
+ break;
166
+ foundUser = true;
167
+ start = position;
168
+ continue;
169
+ }
170
+ if (foundUser && event.kind !== 'runtime_context' && event.kind !== 'skill_context')
171
+ break;
172
+ start = position;
173
+ }
174
+ return start;
175
+ }
176
+ function expandUserTaskStart(events, userPosition, eventTraceKey) {
177
+ let start = userPosition;
178
+ for (let position = userPosition - 1; position >= 0; position -= 1) {
179
+ const event = events[position];
180
+ if (!event || traceKey(event) !== eventTraceKey)
181
+ continue;
182
+ if (TERMINAL_LIFECYCLE_LABELS.has(event.label ?? '')
183
+ || event.label === 'turn_started'
184
+ || isHumanUserMessage(event))
185
+ break;
186
+ if (event.kind !== 'runtime_context' && event.kind !== 'skill_context')
187
+ break;
188
+ start = position;
189
+ }
190
+ return start;
191
+ }
192
+ function expandTurnEnd(events, lastPosition, eventTraceKey, turnId) {
193
+ if (TERMINAL_LIFECYCLE_LABELS.has(events[lastPosition]?.label ?? ''))
194
+ return lastPosition;
195
+ let end = lastPosition;
196
+ for (let position = lastPosition + 1; position < events.length; position += 1) {
197
+ const event = events[position];
198
+ if (!event || traceKey(event) !== eventTraceKey)
199
+ continue;
200
+ if (event.turnId && event.turnId !== turnId)
201
+ break;
202
+ if (event.label === 'turn_started' || isHumanUserMessage(event))
203
+ break;
204
+ end = position;
205
+ if (TERMINAL_LIFECYCLE_LABELS.has(event.label ?? ''))
206
+ break;
207
+ }
208
+ return end;
209
+ }
210
+ function previousGlobalPosition(events, nextPosition, eventTraceKey) {
211
+ for (let position = nextPosition - 1; position >= 0; position -= 1) {
212
+ const event = events[position];
213
+ if (event && traceKey(event) === eventTraceKey)
214
+ return position;
215
+ }
216
+ return Math.max(0, nextPosition - 1);
217
+ }
218
+ function indexedEventsByTrace(events) {
219
+ const groups = new Map();
220
+ events.forEach((event, position) => {
221
+ const key = traceKey(event);
222
+ groups.set(key, [...(groups.get(key) ?? []), { event, position }]);
223
+ });
224
+ return groups;
225
+ }
226
+ function stableTimeline(events) {
227
+ return events.map((event, position) => ({ event, position }))
228
+ .sort((left, right) => {
229
+ const leftTimestamp = left.event.timestamp;
230
+ const rightTimestamp = right.event.timestamp;
231
+ if (leftTimestamp && rightTimestamp && leftTimestamp !== rightTimestamp) {
232
+ return leftTimestamp.localeCompare(rightTimestamp);
233
+ }
234
+ if (traceKey(left.event) === traceKey(right.event)) {
235
+ return left.event.order - right.event.order;
236
+ }
237
+ return left.position - right.position;
238
+ })
239
+ .map(({ event }) => event);
240
+ }
241
+ function traceKey(event) {
242
+ return event.traceId ?? event.sourceTrace;
243
+ }
244
+ function isHumanUserMessage(event) {
245
+ return event.kind === 'user_message' && event.role !== 'tool';
246
+ }
247
+ function hasNativeTurnBoundary(event) {
248
+ return Boolean(event.turnId)
249
+ || event.label === 'turn_started'
250
+ || TERMINAL_LIFECYCLE_LABELS.has(event.label ?? '');
251
+ }
@@ -0,0 +1,9 @@
1
+ import type { ConversationIndexViewModel, ConversationListItem, Lang } from '../types/index.js';
2
+ export interface ConversationActivitySnapshot {
3
+ schemaVersion: 1;
4
+ revision: string;
5
+ runningCount: number;
6
+ }
7
+ export declare function buildConversationActivitySnapshot(model: ConversationIndexViewModel): ConversationActivitySnapshot;
8
+ export declare function renderConversationIndexPage(model: ConversationIndexViewModel, lang?: Lang): string;
9
+ export declare function renderConversationDetailPage(conversation: ConversationListItem, lang?: Lang): string;