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
@@ -1886,10 +1886,13 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
1886
1886
  user_message: '用户消息',
1887
1887
  synthetic_user_event: '构造事件',
1888
1888
  assistant_message: '助手回复',
1889
+ model_activity: '模型活动',
1890
+ agent_activity: '协作代理活动',
1889
1891
  tool_use: '工具调用',
1890
1892
  tool_result: '工具结果',
1891
1893
  skill_context: '能力说明',
1892
1894
  runtime_context: '运行注入',
1895
+ lifecycle: '运行状态',
1893
1896
  observation: '过程发现',
1894
1897
  };
1895
1898
  return labels[kind] ?? kind;
@@ -2393,6 +2396,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
2393
2396
  return { icon: 'S', label: 'Skill 注入上下文', tone: 'skill' };
2394
2397
  if (event.kind === 'runtime_context')
2395
2398
  return { icon: 'R', label: event.label === 'command envelope' ? '命令注入上下文' : '运行时注入上下文', tone: 'skill' };
2399
+ if (event.kind === 'lifecycle')
2400
+ return { icon: 'L', label: '运行状态', tone: 'runtime' };
2396
2401
  return { icon: 'O', label: '过程发现', tone: 'observation' };
2397
2402
  };
2398
2403
  const isSkillLaunchResult = (event) => event.kind === 'tool_result' && /^Launching skill:/i.test((event.fullText ?? event.snippet ?? '').trim());
@@ -2629,6 +2634,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
2629
2634
  if (event.kind === 'runtime_context') {
2630
2635
  badges.push({ label: '不计入用户交互', className: 'metric-skill-context', title: event.label === 'command envelope' ? '这是斜杠命令的系统包装内容,不是人工用户原话,不计入用户消息、追问、纠正、情绪反馈或用户硬性要求。' : '这是 SDK/工作台注入的运行时上下文,不是人工用户原话,不计入用户消息、追问、纠正、情绪反馈或用户硬性要求。' });
2631
2636
  }
2637
+ if (event.kind === 'lifecycle') {
2638
+ badges.push({ label: '运行状态', className: 'metric-neutral', title: '这是 runtime 记录的生命周期状态,不是模型上下文,也不计入用户交互。' });
2639
+ }
2632
2640
  if (event.kind === 'observation') {
2633
2641
  badges.push({ label: '过程发现来源', className: 'metric-explicit', title: '这条事件来自 observation 信号,会进入过程发现类指标。' });
2634
2642
  }
@@ -5157,6 +5165,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
5157
5165
  <span>入口 ${e(inboxEntrypointShort(session))}</span>
5158
5166
  <span>调用段 ${session.invocationIds.length}</span>
5159
5167
  <span>工具调用 ${indicators.toolCallCount}</span>
5168
+ <a href="/conversations/${encodeURIComponent(session.threadId)}${lang === DEFAULT_LANG ? '' : `?lang=${lang}`}" onclick="event.stopPropagation()">${lang === 'zh' ? '查看对话任务' : 'Conversation tasks'}</a>
5160
5169
  </div>
5161
5170
  ${navHtml}
5162
5171
  <template id="${flowTemplateId}">${inboxRenderSessionFlow(cardSkillName, session, siblings, flowSummaryText)}</template>
@@ -90,7 +90,8 @@ function renderPanel(a, lang) {
90
90
  <h1>${zh ? 'Skill 健康评测工作台' : 'Skill Health Dashboard'}</h1>
91
91
  <span class="phead-sub">${a.totalSkills} ${zh ? '个 skill' : 'skills'}</span>
92
92
  <span class="phead-when">${icon('clock', { size: 13 })} ${zh ? '更新于' : 'updated'} ${fmtDate(a.lastTs)}</span>
93
- <a href="/managed${zh ? '' : '?lang=en'}" style="margin-left:auto;font-size:13px;color:var(--accent);text-decoration:none;white-space:nowrap">${zh ? '受管决策史' : 'Managed history'} →</a>
93
+ <a href="/${zh ? '' : '?lang=en'}" style="margin-left:auto;font-size:13px;color:var(--accent);text-decoration:none;white-space:nowrap">${zh ? '对话总览' : 'Conversation overview'} →</a>
94
+ <a href="/managed${zh ? '' : '?lang=en'}" style="font-size:13px;color:var(--accent);text-decoration:none;white-space:nowrap">${zh ? '受管决策史' : 'Managed history'} →</a>
94
95
  </div>`;
95
96
  // 综合健康(细线总览首格):ring + 标签 + 说明入口。
96
97
  const ovMain = `<div class="ov-cell ov-main">
@@ -0,0 +1,42 @@
1
+ export interface TrajectoryLiveLabels {
2
+ connecting: string;
3
+ live: string;
4
+ syncing: string;
5
+ reconnecting: string;
6
+ failed: string;
7
+ following: string;
8
+ resume: string;
9
+ pending: string;
10
+ completed: string;
11
+ aborted: string;
12
+ interrupted: string;
13
+ unknown: string;
14
+ pauseTitle: string;
15
+ }
16
+ export interface TrajectoryLiveClientOptions {
17
+ shell: HTMLElement;
18
+ timeline: HTMLElement | null;
19
+ liveState: HTMLElement | null;
20
+ followButton: HTMLButtonElement | null;
21
+ followLabel: HTMLElement | null;
22
+ liveEndpoint: string;
23
+ labels: TrajectoryLiveLabels;
24
+ getMode: () => string;
25
+ setMode: (mode: string) => void;
26
+ isScrollTrackingSuppressed?: () => boolean;
27
+ refreshSnapshot?: (state: TrajectoryLiveViewState) => Promise<void>;
28
+ browserWindow: Window;
29
+ browserDocument: Document;
30
+ }
31
+ export interface TrajectoryLiveViewState {
32
+ mode: string;
33
+ scrollLeft: number;
34
+ followLatest: boolean;
35
+ animateToLatest?: boolean;
36
+ }
37
+ export interface TrajectoryLiveController {
38
+ pauseFollowing: () => void;
39
+ dispose: () => void;
40
+ }
41
+ export declare function createTrajectoryLiveController(options: TrajectoryLiveClientOptions): TrajectoryLiveController;
42
+ export declare function renderTrajectoryLiveClientSource(): string;
@@ -0,0 +1,258 @@
1
+ export function createTrajectoryLiveController(options) {
2
+ const { shell, timeline, liveState, followButton, followLabel, liveEndpoint, labels, browserWindow, browserDocument, } = options;
3
+ const stateKey = `omk:live-trajectory:${browserWindow.location.pathname}`;
4
+ const lifecycle = new AbortController();
5
+ let followLatest = Boolean(liveEndpoint);
6
+ let pendingRevision = '';
7
+ let terminalState;
8
+ let refreshTimer;
9
+ let scrollReleaseTimer;
10
+ let suppressScrollTracking = false;
11
+ let liveSource;
12
+ const setConnectionState = (state, label) => {
13
+ if (!liveState)
14
+ return;
15
+ liveState.dataset.state = state;
16
+ liveState.textContent = label;
17
+ };
18
+ const isNearLatest = () => {
19
+ if (!timeline)
20
+ return true;
21
+ const remaining = timeline.scrollWidth - timeline.clientWidth - timeline.scrollLeft;
22
+ return remaining <= 72;
23
+ };
24
+ const scrollToLatest = (smooth = false) => {
25
+ if (!timeline || options.getMode() !== 'semantic')
26
+ return;
27
+ suppressScrollTracking = true;
28
+ const left = Math.max(0, timeline.scrollWidth - timeline.clientWidth);
29
+ const reduceMotion = browserWindow.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
30
+ if (smooth && !reduceMotion && typeof timeline.scrollTo === 'function') {
31
+ timeline.scrollTo({ left, behavior: 'smooth' });
32
+ if (scrollReleaseTimer !== undefined)
33
+ browserWindow.clearTimeout(scrollReleaseTimer);
34
+ scrollReleaseTimer = browserWindow.setTimeout(() => {
35
+ timeline.scrollLeft = left;
36
+ suppressScrollTracking = false;
37
+ scrollReleaseTimer = undefined;
38
+ }, 700);
39
+ }
40
+ else {
41
+ timeline.scrollLeft = left;
42
+ browserWindow.requestAnimationFrame(() => { suppressScrollTracking = false; });
43
+ }
44
+ };
45
+ const updateFollowControl = () => {
46
+ if (!followButton || !followLabel)
47
+ return;
48
+ const state = terminalState ?? (!followLatest && pendingRevision
49
+ ? 'pending'
50
+ : (followLatest ? 'following' : 'paused'));
51
+ const terminalLabel = terminalState ? labels[terminalState] : undefined;
52
+ followButton.dataset.state = state;
53
+ followButton.dataset.following = String(followLatest);
54
+ followButton.setAttribute('aria-pressed', String(followLatest));
55
+ followLabel.textContent = terminalLabel ?? (state === 'pending'
56
+ ? labels.pending
57
+ : state === 'following'
58
+ ? labels.following
59
+ : labels.resume);
60
+ followButton.title = state === 'following' ? labels.pauseTitle : followLabel.textContent;
61
+ };
62
+ const setFollowing = (next, shouldScroll = next) => {
63
+ if (!next && refreshTimer !== undefined) {
64
+ browserWindow.clearTimeout(refreshTimer);
65
+ refreshTimer = undefined;
66
+ setConnectionState('live', labels.live);
67
+ }
68
+ followLatest = next;
69
+ updateFollowControl();
70
+ if (shouldScroll)
71
+ scrollToLatest(true);
72
+ };
73
+ const readViewState = () => {
74
+ try {
75
+ const raw = browserWindow.sessionStorage.getItem(stateKey);
76
+ browserWindow.sessionStorage.removeItem(stateKey);
77
+ return raw ? JSON.parse(raw) : undefined;
78
+ }
79
+ catch {
80
+ return undefined;
81
+ }
82
+ };
83
+ const currentViewState = (animateToLatest = false) => ({
84
+ mode: options.getMode(),
85
+ scrollLeft: timeline?.scrollLeft ?? 0,
86
+ followLatest,
87
+ ...(animateToLatest ? { animateToLatest: true } : {}),
88
+ });
89
+ const saveViewState = (state = currentViewState()) => {
90
+ try {
91
+ browserWindow.sessionStorage.setItem(stateKey, JSON.stringify(state));
92
+ }
93
+ catch {
94
+ // Storage can be unavailable in hardened browsers.
95
+ }
96
+ };
97
+ const restoreViewState = () => {
98
+ const state = readViewState();
99
+ if (state?.mode && ['semantic', 'normalized', 'source'].includes(state.mode))
100
+ options.setMode(state.mode);
101
+ if (typeof state?.followLatest === 'boolean')
102
+ followLatest = state.followLatest;
103
+ if (!liveEndpoint || options.getMode() !== 'semantic')
104
+ followLatest = false;
105
+ updateFollowControl();
106
+ if (timeline && Number.isFinite(state?.scrollLeft)) {
107
+ // Browsers can dispatch this assignment's scroll event after listeners are attached below.
108
+ // Keep restoration programmatic until the final frame settles so it cannot pause live follow.
109
+ suppressScrollTracking = true;
110
+ timeline.scrollLeft = Math.min(state?.scrollLeft ?? 0, Math.max(0, timeline.scrollWidth - timeline.clientWidth));
111
+ }
112
+ browserWindow.requestAnimationFrame(() => {
113
+ browserWindow.requestAnimationFrame(() => {
114
+ if (followLatest && liveEndpoint)
115
+ scrollToLatest(Boolean(state?.animateToLatest));
116
+ else if (timeline && Number.isFinite(state?.scrollLeft)) {
117
+ timeline.scrollLeft = state?.scrollLeft ?? 0;
118
+ browserWindow.requestAnimationFrame(() => { suppressScrollTracking = false; });
119
+ }
120
+ else {
121
+ suppressScrollTracking = false;
122
+ }
123
+ });
124
+ });
125
+ };
126
+ const reloadNow = async () => {
127
+ if (!pendingRevision)
128
+ return;
129
+ if (refreshTimer !== undefined)
130
+ browserWindow.clearTimeout(refreshTimer);
131
+ if (scrollReleaseTimer !== undefined)
132
+ browserWindow.clearTimeout(scrollReleaseTimer);
133
+ refreshTimer = undefined;
134
+ scrollReleaseTimer = undefined;
135
+ setConnectionState('syncing', labels.syncing);
136
+ // Live snapshots can arrive faster than native smooth scrolling settles.
137
+ // Restore them at the latest edge immediately so following never lags behind.
138
+ const viewState = currentViewState();
139
+ saveViewState(viewState);
140
+ if (options.refreshSnapshot) {
141
+ const revision = pendingRevision;
142
+ pendingRevision = '';
143
+ updateFollowControl();
144
+ try {
145
+ await options.refreshSnapshot(viewState);
146
+ }
147
+ catch {
148
+ pendingRevision = revision;
149
+ setConnectionState('reconnecting', labels.reconnecting);
150
+ updateFollowControl();
151
+ }
152
+ return;
153
+ }
154
+ browserWindow.location.reload();
155
+ };
156
+ const applyPendingUpdate = () => {
157
+ updateFollowControl();
158
+ if (!pendingRevision
159
+ || !followLatest
160
+ || options.getMode() !== 'semantic'
161
+ || browserDocument.hidden
162
+ || refreshTimer !== undefined)
163
+ return;
164
+ setConnectionState('syncing', labels.syncing);
165
+ refreshTimer = browserWindow.setTimeout(() => { void reloadNow(); }, 600);
166
+ };
167
+ const pauseFollowing = () => {
168
+ if (!liveEndpoint || !followLatest)
169
+ return;
170
+ setFollowing(false, false);
171
+ };
172
+ const dispose = () => {
173
+ lifecycle.abort();
174
+ liveSource?.close();
175
+ if (refreshTimer !== undefined)
176
+ browserWindow.clearTimeout(refreshTimer);
177
+ if (scrollReleaseTimer !== undefined)
178
+ browserWindow.clearTimeout(scrollReleaseTimer);
179
+ refreshTimer = undefined;
180
+ scrollReleaseTimer = undefined;
181
+ };
182
+ restoreViewState();
183
+ timeline?.addEventListener('scroll', () => {
184
+ if (suppressScrollTracking
185
+ || options.isScrollTrackingSuppressed?.()
186
+ || !followLatest
187
+ || options.getMode() !== 'semantic')
188
+ return;
189
+ if (!isNearLatest())
190
+ pauseFollowing();
191
+ }, { passive: true, signal: lifecycle.signal });
192
+ followButton?.addEventListener('click', () => {
193
+ if (pendingRevision && !followLatest) {
194
+ setFollowing(true, false);
195
+ void reloadNow();
196
+ return;
197
+ }
198
+ setFollowing(!followLatest);
199
+ }, { signal: lifecycle.signal });
200
+ browserDocument.addEventListener('visibilitychange', () => {
201
+ if (!browserDocument.hidden)
202
+ applyPendingUpdate();
203
+ }, { signal: lifecycle.signal });
204
+ browserWindow.addEventListener('pagehide', dispose, { once: true, signal: lifecycle.signal });
205
+ const EventSourceConstructor = browserWindow.EventSource;
206
+ if (liveEndpoint && EventSourceConstructor) {
207
+ setConnectionState('connecting', labels.connecting);
208
+ const source = new EventSourceConstructor(liveEndpoint);
209
+ liveSource = source;
210
+ source.addEventListener('open', () => setConnectionState('live', labels.live), { signal: lifecycle.signal });
211
+ source.addEventListener('trajectory', (event) => {
212
+ try {
213
+ const update = JSON.parse(event.data);
214
+ const explicitTerminal = update.status === 'completed'
215
+ || update.status === 'aborted'
216
+ || update.status === 'interrupted';
217
+ const streamTerminal = update.liveObservable === false || explicitTerminal;
218
+ terminalState = explicitTerminal
219
+ ? update.status
220
+ : update.liveObservable === false ? 'unknown' : undefined;
221
+ if (streamTerminal) {
222
+ source.close();
223
+ if (liveSource === source)
224
+ liveSource = undefined;
225
+ }
226
+ if (!update.revision || update.revision === shell.dataset.liveRevision) {
227
+ const quiet = update.status === 'unknown';
228
+ const connectionLabel = terminalState
229
+ ? labels[terminalState]
230
+ : quiet ? labels.reconnecting : labels.live;
231
+ setConnectionState(terminalState ?? (quiet ? 'reconnecting' : 'live'), connectionLabel);
232
+ updateFollowControl();
233
+ return;
234
+ }
235
+ pendingRevision = update.revision;
236
+ applyPendingUpdate();
237
+ }
238
+ catch {
239
+ // Ignore malformed live events and wait for the next revision.
240
+ }
241
+ }, { signal: lifecycle.signal });
242
+ source.addEventListener('trajectory-error', () => {
243
+ source.close();
244
+ if (liveSource === source)
245
+ liveSource = undefined;
246
+ setConnectionState('failed', labels.failed);
247
+ }, { signal: lifecycle.signal });
248
+ source.addEventListener('error', () => {
249
+ if (!pendingRevision)
250
+ setConnectionState('reconnecting', labels.reconnecting);
251
+ }, { signal: lifecycle.signal });
252
+ }
253
+ return { pauseFollowing, dispose };
254
+ }
255
+ export function renderTrajectoryLiveClientSource() {
256
+ // Studio 页面没有独立客户端打包链路;序列化已类型检查的控制器,避免维护第二份浏览器实现。
257
+ return createTrajectoryLiveController.toString();
258
+ }
@@ -0,0 +1,60 @@
1
+ export interface RoutingPoint {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface RoutingRect<Owner = unknown> {
6
+ left: number;
7
+ right: number;
8
+ top: number;
9
+ bottom: number;
10
+ owner?: Owner;
11
+ }
12
+ export interface HorizontalObstacleIndex<Owner = unknown> {
13
+ bucketSize: number;
14
+ buckets: Map<number, RoutingRect<Owner>[]>;
15
+ all: RoutingRect<Owner>[];
16
+ }
17
+ export interface FlowRouteMetrics {
18
+ obstacleQueries: number;
19
+ obstacleCandidates: number;
20
+ collisionChecks: number;
21
+ curveCandidates: number;
22
+ corridorCandidates: number;
23
+ }
24
+ export interface FlowRoutePlan {
25
+ routeKind: 'straight' | 'quadratic' | 'corridor';
26
+ d: string;
27
+ from: RoutingPoint;
28
+ to: RoutingPoint;
29
+ arrowFrom: RoutingPoint;
30
+ metrics: FlowRouteMetrics;
31
+ }
32
+ export interface FlowRouteRequest<Owner = unknown> {
33
+ fromRect: RoutingRect<Owner>;
34
+ toRect: RoutingRect<Owner>;
35
+ fromLane: string;
36
+ toLane: string;
37
+ obstacleIndex: HorizontalObstacleIndex<Owner>;
38
+ fromOwner?: Owner;
39
+ toOwner?: Owner;
40
+ }
41
+ export declare function planFlowMarkerProgresses(pathLength: number, markerCount: number): number[];
42
+ export declare function createHorizontalObstacleIndex<Owner>(rects: RoutingRect<Owner>[], bucketSize?: number): HorizontalObstacleIndex<Owner>;
43
+ export declare function queryHorizontalObstacleIndex<Owner>(index: HorizontalObstacleIndex<Owner>, minX: number, maxX: number, minY?: number, maxY?: number): RoutingRect<Owner>[];
44
+ export declare function routingPointInsideRect(point: RoutingPoint, rect: RoutingRect): boolean;
45
+ export declare function routingLineHitsRect(fromPoint: RoutingPoint, toPoint: RoutingPoint, rect: RoutingRect): boolean;
46
+ export declare function routingQuadraticHitsRect(fromPoint: RoutingPoint, control: RoutingPoint, toPoint: RoutingPoint, rect: RoutingRect): boolean;
47
+ export declare function routingMoveToward(fromPoint: RoutingPoint, toPoint: RoutingPoint, distance: number): RoutingPoint;
48
+ export declare function routingEdgePoint(rect: RoutingRect, targetX: number, targetY: number): RoutingPoint;
49
+ export declare function routingConnectionPoints(fromRect: RoutingRect, toRect: RoutingRect): {
50
+ from: RoutingPoint;
51
+ to: RoutingPoint;
52
+ };
53
+ export declare function routingRectPort(rect: RoutingRect, side: 'left' | 'right' | 'top' | 'bottom'): RoutingPoint;
54
+ export declare function routingFlowControlPoint(points: {
55
+ from: RoutingPoint;
56
+ to: RoutingPoint;
57
+ }, requestedBend?: number, side?: number): RoutingPoint;
58
+ export declare function routingRoundedPolylinePath(routePoints: RoutingPoint[], radius?: number): string;
59
+ export declare function planFlowRoute<Owner>(request: FlowRouteRequest<Owner>): FlowRoutePlan | undefined;
60
+ export declare function renderTrajectoryRoutingClientSource(): string;