@kb-labs/agent-entry 2.118.2 → 2.119.0-canary.0c078654e

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 (62) hide show
  1. package/dist/cli/commands/diff.js.map +1 -1
  2. package/dist/cli/commands/history.js.map +1 -1
  3. package/dist/cli/commands/quality-report.js.map +1 -1
  4. package/dist/cli/commands/rollback.js.map +1 -1
  5. package/dist/cli/commands/run.js.map +1 -1
  6. package/dist/cli/commands/trace-context.js.map +1 -1
  7. package/dist/cli/commands/trace-diagnose.js.map +1 -1
  8. package/dist/cli/commands/trace-event-normalizer.js.map +1 -1
  9. package/dist/cli/commands/trace-filter.js.map +1 -1
  10. package/dist/cli/commands/trace-iteration.js.map +1 -1
  11. package/dist/cli/commands/trace-stats.js.map +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/manifest.js.map +1 -1
  14. package/dist/rest/handlers/approve-handler.js.map +1 -1
  15. package/dist/rest/handlers/approve-session-plan-handler.js.map +1 -1
  16. package/dist/rest/handlers/correct-handler.js +29 -112
  17. package/dist/rest/handlers/correct-handler.js.map +1 -1
  18. package/dist/rest/handlers/create-session-handler.js.map +1 -1
  19. package/dist/rest/handlers/execute-session-plan-handler.js +56 -118
  20. package/dist/rest/handlers/execute-session-plan-handler.js.map +1 -1
  21. package/dist/rest/handlers/generate-spec-handler.js +50 -116
  22. package/dist/rest/handlers/generate-spec-handler.js.map +1 -1
  23. package/dist/rest/handlers/get-file-diff-handler.js.map +1 -1
  24. package/dist/rest/handlers/get-session-handler.js.map +1 -1
  25. package/dist/rest/handlers/get-session-plan-handler.js.map +1 -1
  26. package/dist/rest/handlers/get-session-turns-handler.js.map +1 -1
  27. package/dist/rest/handlers/get-spec-handler.js.map +1 -1
  28. package/dist/rest/handlers/list-file-changes-handler.js.map +1 -1
  29. package/dist/rest/handlers/list-sessions-handler.js.map +1 -1
  30. package/dist/rest/handlers/rollback-handler.js.map +1 -1
  31. package/dist/rest/handlers/run-handler.js +56 -118
  32. package/dist/rest/handlers/run-handler.js.map +1 -1
  33. package/dist/rest/handlers/sessions-handler.js.map +1 -1
  34. package/dist/rest/handlers/status-handler.js +29 -112
  35. package/dist/rest/handlers/status-handler.js.map +1 -1
  36. package/dist/rest/handlers/stop-handler.js +29 -112
  37. package/dist/rest/handlers/stop-handler.js.map +1 -1
  38. package/dist/ws/session-stream-handler.js +70 -180
  39. package/dist/ws/session-stream-handler.js.map +1 -1
  40. package/package.json +14 -14
  41. package/dist/manifest.json +0 -685
  42. package/dist/widgets/117.js +0 -25
  43. package/dist/widgets/165.js +0 -1
  44. package/dist/widgets/326.js +0 -1
  45. package/dist/widgets/370.js +0 -1
  46. package/dist/widgets/405.js +0 -1
  47. package/dist/widgets/46.js +0 -445
  48. package/dist/widgets/50.js +0 -1
  49. package/dist/widgets/546.js +0 -1
  50. package/dist/widgets/62.js +0 -96
  51. package/dist/widgets/648.js +0 -1
  52. package/dist/widgets/669.js +0 -1
  53. package/dist/widgets/707.js +0 -1
  54. package/dist/widgets/775.js +0 -1
  55. package/dist/widgets/78.js +0 -1
  56. package/dist/widgets/875.js +0 -25
  57. package/dist/widgets/945.js +0 -1
  58. package/dist/widgets/968.js +0 -9
  59. package/dist/widgets/__federation_expose_AgentsPage.js +0 -1
  60. package/dist/widgets/mf-manifest.json +0 -229
  61. package/dist/widgets/mf-stats.json +0 -256
  62. package/dist/widgets/remoteEntry.js +0 -7
@@ -1,16 +1,13 @@
1
- import { defineHandler, rethrowForRest, useCache, usePlatform } from '@kb-labs/sdk';
1
+ import { defineHandler, rethrowForRest, useCache } from '@kb-labs/sdk';
2
2
 
3
3
  // src/rest/handlers/status-handler.ts
4
4
  var CACHE_PREFIX = "agent:run:";
5
- var EVENT_TOPIC_PREFIX = "agent:events:";
6
5
  var CACHE_TTL = 36e5;
7
6
  var RunManagerImpl = class {
8
- /** Live agents and listeners (not cacheable) */
7
+ /** Live agents (not cacheable) */
9
8
  activeRuns = /* @__PURE__ */ new Map();
10
- /** Session-level listeners: sessionId → Set<callback> — receive events from ALL runs in session */
9
+ /** Session-level listeners: sessionId → Set<callback> — receive updates from ALL runs in session */
11
10
  sessionListeners = /* @__PURE__ */ new Map();
12
- /** sessionId per runId — set when run is registered */
13
- runSessionMap = /* @__PURE__ */ new Map();
14
11
  /**
15
12
  * Generate unique run ID
16
13
  */
@@ -20,7 +17,7 @@ var RunManagerImpl = class {
20
17
  /**
21
18
  * Register a new run
22
19
  */
23
- async register(runId, task, agent, sessionManager, sessionId) {
20
+ async register(runId, task, agent, sessionManager) {
24
21
  const now = (/* @__PURE__ */ new Date()).toISOString();
25
22
  const run = {
26
23
  runId,
@@ -28,15 +25,9 @@ var RunManagerImpl = class {
28
25
  status: "pending",
29
26
  agent,
30
27
  sessionManager,
31
- startedAt: now,
32
- listeners: /* @__PURE__ */ new Set(),
33
- lastSeq: 0,
34
- eventBuffer: []
28
+ startedAt: now
35
29
  };
36
30
  this.activeRuns.set(runId, run);
37
- if (sessionId) {
38
- this.runSessionMap.set(runId, sessionId);
39
- }
40
31
  await this.saveToCache(run);
41
32
  return run;
42
33
  }
@@ -99,101 +90,42 @@ var RunManagerImpl = class {
99
90
  };
100
91
  await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);
101
92
  }
102
- /** Track subscriptions for cleanup */
103
- subscriptions = /* @__PURE__ */ new Map();
104
93
  /**
105
- * Add event listener to run (uses eventBus for cross-process)
94
+ * Broadcast turn deltas to all session-level listeners. Callers MUST await
95
+ * persistence (sessionManager.addEvent) before calling this, so listeners
96
+ * never observe a delta whose underlying turn state isn't on disk yet —
97
+ * that ordering is what makes gap-recovery / reconnect resume correct.
106
98
  */
107
- addListener(runId, callback) {
108
- const platform = usePlatform();
109
- if (!platform?.eventBus) {
110
- const run2 = this.activeRuns.get(runId);
111
- if (run2) {
112
- run2.listeners.add(callback);
113
- return true;
114
- }
115
- return false;
116
- }
117
- const topic = `${EVENT_TOPIC_PREFIX}${runId}`;
118
- const unsubscribe = platform.eventBus.subscribe(topic, async (event) => {
119
- try {
120
- callback(event);
121
- } catch {
122
- }
123
- });
124
- if (!this.subscriptions.has(runId)) {
125
- this.subscriptions.set(runId, /* @__PURE__ */ new Map());
126
- }
127
- this.subscriptions.get(runId).set(callback, unsubscribe);
128
- const run = this.activeRuns.get(runId);
129
- if (run) {
130
- run.listeners.add(callback);
99
+ broadcastSessionDeltas(sessionId, deltas) {
100
+ if (deltas.length === 0) {
101
+ return;
131
102
  }
132
- return true;
103
+ this.notifySessionListeners(sessionId, { kind: "deltas", deltas });
133
104
  }
134
105
  /**
135
- * Remove event listener from run
106
+ * Broadcast a run's terminal outcome to session-level listeners. Called
107
+ * directly from the run's own completion handler (after `agent.execute()`
108
+ * resolves) rather than derived reactively from an `agent:end` event —
109
+ * the completion handler already has definitive success/summary/duration,
110
+ * and this guarantees the message fires exactly once, deterministically.
136
111
  */
137
- removeListener(runId, callback) {
138
- const runSubs = this.subscriptions.get(runId);
139
- if (runSubs) {
140
- const unsubscribe = runSubs.get(callback);
141
- if (unsubscribe) {
142
- unsubscribe();
143
- runSubs.delete(callback);
144
- }
145
- if (runSubs.size === 0) {
146
- this.subscriptions.delete(runId);
147
- }
148
- }
149
- const run = this.activeRuns.get(runId);
150
- if (run) {
151
- run.listeners.delete(callback);
152
- }
112
+ broadcastRunCompleted(sessionId, payload) {
113
+ this.notifySessionListeners(sessionId, { kind: "run:completed", ...payload });
153
114
  }
154
- /**
155
- * Broadcast event to all listeners of a run (uses eventBus for cross-process)
156
- * Assigns monotonic sequence number for reliable ordering
157
- * @returns The event with seq assigned (for persistence)
158
- */
159
- broadcast(runId, event) {
160
- const platform = usePlatform();
161
- const run = this.activeRuns.get(runId);
162
- let seqEvent = event;
163
- if (run) {
164
- run.lastSeq++;
165
- seqEvent = { ...event, seq: run.lastSeq, runId };
166
- run.eventBuffer.push(seqEvent);
167
- }
168
- if (platform?.eventBus) {
169
- const topic = `${EVENT_TOPIC_PREFIX}${runId}`;
170
- platform.eventBus.publish(topic, seqEvent).catch(() => {
171
- });
172
- }
173
- if (run) {
174
- for (const listener of run.listeners) {
175
- try {
176
- listener(seqEvent);
177
- } catch {
178
- }
179
- }
115
+ notifySessionListeners(sessionId, payload) {
116
+ const listeners = this.sessionListeners.get(sessionId);
117
+ if (!listeners) {
118
+ return;
180
119
  }
181
- const sessionId = this.runSessionMap.get(runId);
182
- if (sessionId) {
183
- const sListeners = this.sessionListeners.get(sessionId);
184
- if (sListeners) {
185
- for (const listener of sListeners) {
186
- try {
187
- listener(seqEvent);
188
- } catch {
189
- }
190
- }
120
+ for (const listener of listeners) {
121
+ try {
122
+ listener(payload);
123
+ } catch {
191
124
  }
192
125
  }
193
- return seqEvent;
194
126
  }
195
127
  /**
196
- * Add a session-level listener — receives events from ALL runs in this session.
128
+ * Add a session-level listener — receives updates from ALL runs in this session.
197
129
  */
198
130
  addSessionListener(sessionId, callback) {
199
131
  if (!this.sessionListeners.has(sessionId)) {
@@ -213,21 +145,6 @@ var RunManagerImpl = class {
213
145
  }
214
146
  }
215
147
  }
216
- /**
217
- * Get replay buffer for a run (all events emitted since start).
218
- * Used by WS handler to send missed events on late connection.
219
- * Optionally filter by afterSeq to only get events the client hasn't seen.
220
- */
221
- getEventBuffer(runId, afterSeq) {
222
- const run = this.activeRuns.get(runId);
223
- if (!run) {
224
- return [];
225
- }
226
- if (afterSeq != null) {
227
- return run.eventBuffer.filter((e) => e.seq != null && e.seq > afterSeq);
228
- }
229
- return [...run.eventBuffer];
230
- }
231
148
  /**
232
149
  * List all active runs
233
150
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/rest/run-manager.ts","../../../src/rest/handlers/status-handler.ts"],"names":["run"],"mappings":"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","file":"status-handler.js","sourcesContent":["/**\n * Run Manager - tracks active agent runs for REST/WS API\n *\n * Uses platform cache for persistence and in-memory Map for active agents.\n * Cache stores serializable run state, Map stores live agent references.\n * Uses platform eventBus for cross-process event broadcasting.\n */\n\nimport type { IAgentRunner } from '@kb-labs/agent-sdk';\nimport type { SessionManager } from '@kb-labs/agent-core';\nimport type { AgentEvent, AgentEventCallback } from '@kb-labs/agent-contracts';\nimport { useCache, usePlatform } from '@kb-labs/sdk';\n\nconst CACHE_PREFIX = 'agent:run:';\nconst EVENT_TOPIC_PREFIX = 'agent:events:';\nconst CACHE_TTL = 3600000; // 1 hour\n\n/**\n * Run status\n */\nexport type RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'stopped';\n\n/**\n * Serializable run state (stored in cache)\n */\nexport interface RunState {\n runId: string;\n task: string;\n status: RunStatus;\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n summary?: string;\n error?: string;\n}\n\n/**\n * Active run with live agent (in-memory only)\n */\nexport interface ActiveRun extends RunState {\n agent?: IAgentRunner;\n sessionManager: SessionManager;\n listeners: Set<AgentEventCallback>;\n /** Monotonic sequence counter for event ordering */\n lastSeq: number;\n /** Replay buffer: all events emitted since run start (for late WS connections) */\n eventBuffer: AgentEvent[];\n}\n\n/**\n * Run Manager implementation\n */\nclass RunManagerImpl {\n /** Live agents and listeners (not cacheable) */\n private activeRuns: Map<string, ActiveRun> = new Map();\n\n /** Session-level listeners: sessionId → Set<callback> — receive events from ALL runs in session */\n private sessionListeners: Map<string, Set<AgentEventCallback>> = new Map();\n\n /** sessionId per runId — set when run is registered */\n private runSessionMap: Map<string, string> = new Map();\n\n /**\n * Generate unique run ID\n */\n generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n }\n\n /**\n * Register a new run\n */\n async register(\n runId: string,\n task: string,\n agent: IAgentRunner | undefined,\n sessionManager: SessionManager,\n sessionId?: string\n ): Promise<ActiveRun> {\n const now = new Date().toISOString();\n\n const run: ActiveRun = {\n runId,\n task,\n status: 'pending',\n agent,\n sessionManager,\n startedAt: now,\n listeners: new Set(),\n lastSeq: 0,\n eventBuffer: [],\n };\n\n // Store in memory (for live agent)\n this.activeRuns.set(runId, run);\n\n // Track sessionId → runId mapping for session-level listeners\n if (sessionId) {\n this.runSessionMap.set(runId, sessionId);\n }\n\n // Store serializable state in cache\n await this.saveToCache(run);\n\n return run;\n }\n\n /**\n * Get run by ID (from memory first, then cache for state)\n */\n get(runId: string): ActiveRun | undefined {\n return this.activeRuns.get(runId);\n }\n\n /**\n * Check if run exists (in memory or cache)\n */\n async exists(runId: string): Promise<boolean> {\n // Check memory first\n if (this.activeRuns.has(runId)) {\n return true;\n }\n // Fallback to cache\n const state = await this.getState(runId);\n return state !== null;\n }\n\n /**\n * Get run state from cache (for completed runs or cross-process access)\n */\n async getState(runId: string): Promise<RunState | null> {\n const cache = useCache();\n if (!cache) {return null;}\n\n return cache.get<RunState>(`${CACHE_PREFIX}${runId}`);\n }\n\n /**\n * Update run status\n */\n async updateStatus(runId: string, status: RunStatus, extra?: Partial<RunState>): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (run) {\n run.status = status;\n if (extra) {\n Object.assign(run, extra);\n }\n await this.saveToCache(run);\n }\n }\n\n /**\n * Save run state to cache\n */\n private async saveToCache(run: ActiveRun): Promise<void> {\n const cache = useCache();\n if (!cache) {return;}\n\n const state: RunState = {\n runId: run.runId,\n task: run.task,\n status: run.status,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n\n await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);\n }\n\n /** Track subscriptions for cleanup */\n private subscriptions: Map<string, Map<AgentEventCallback, () => void>> = new Map();\n\n /**\n * Add event listener to run (uses eventBus for cross-process)\n */\n addListener(runId: string, callback: AgentEventCallback): boolean {\n const platform = usePlatform();\n if (!platform?.eventBus) {\n // Fallback to in-memory only\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.add(callback);\n return true;\n }\n return false;\n }\n\n // Subscribe to eventBus topic for this run\n const topic = `${EVENT_TOPIC_PREFIX}${runId}`;\n const unsubscribe = platform.eventBus.subscribe<AgentEvent>(topic, async (event) => {\n try {\n callback(event);\n } catch {\n // Ignore callback errors\n }\n });\n\n // Track subscription for cleanup\n if (!this.subscriptions.has(runId)) {\n this.subscriptions.set(runId, new Map());\n }\n this.subscriptions.get(runId)!.set(callback, unsubscribe);\n\n // Also add to in-memory if run exists locally\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.add(callback);\n }\n\n return true;\n }\n\n /**\n * Remove event listener from run\n */\n removeListener(runId: string, callback: AgentEventCallback): void {\n // Unsubscribe from eventBus\n const runSubs = this.subscriptions.get(runId);\n if (runSubs) {\n const unsubscribe = runSubs.get(callback);\n if (unsubscribe) {\n unsubscribe();\n runSubs.delete(callback);\n }\n if (runSubs.size === 0) {\n this.subscriptions.delete(runId);\n }\n }\n\n // Also remove from in-memory\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.delete(callback);\n }\n }\n\n /**\n * Broadcast event to all listeners of a run (uses eventBus for cross-process)\n * Assigns monotonic sequence number for reliable ordering\n * @returns The event with seq assigned (for persistence)\n */\n broadcast(runId: string, event: AgentEvent): AgentEvent {\n const platform = usePlatform();\n const run = this.activeRuns.get(runId);\n\n // Assign sequence number for ordering\n let seqEvent = event;\n if (run) {\n run.lastSeq++;\n seqEvent = { ...event, seq: run.lastSeq, runId };\n\n // Store in replay buffer (for late WS connections)\n run.eventBuffer.push(seqEvent);\n }\n\n // Publish to eventBus for cross-process delivery\n if (platform?.eventBus) {\n const topic = `${EVENT_TOPIC_PREFIX}${runId}`;\n platform.eventBus.publish(topic, seqEvent).catch(() => {\n // Ignore publish errors\n });\n }\n\n // Also notify local in-memory listeners (for same-process)\n if (run) {\n for (const listener of run.listeners) {\n try {\n listener(seqEvent);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n // Notify session-level listeners (persistent connections)\n const sessionId = this.runSessionMap.get(runId);\n if (sessionId) {\n const sListeners = this.sessionListeners.get(sessionId);\n if (sListeners) {\n for (const listener of sListeners) {\n try {\n listener(seqEvent);\n } catch {\n // Ignore listener errors\n }\n }\n }\n }\n\n return seqEvent;\n }\n\n /**\n * Add a session-level listener — receives events from ALL runs in this session.\n */\n addSessionListener(sessionId: string, callback: AgentEventCallback): void {\n if (!this.sessionListeners.has(sessionId)) {\n this.sessionListeners.set(sessionId, new Set());\n }\n this.sessionListeners.get(sessionId)!.add(callback);\n }\n\n /**\n * Remove a session-level listener.\n */\n removeSessionListener(sessionId: string, callback: AgentEventCallback): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.sessionListeners.delete(sessionId);\n }\n }\n }\n\n /**\n * Get replay buffer for a run (all events emitted since start).\n * Used by WS handler to send missed events on late connection.\n * Optionally filter by afterSeq to only get events the client hasn't seen.\n */\n getEventBuffer(runId: string, afterSeq?: number): AgentEvent[] {\n const run = this.activeRuns.get(runId);\n if (!run) {return [];}\n\n if (afterSeq != null) {\n return run.eventBuffer.filter((e) => e.seq != null && e.seq > afterSeq);\n }\n return [...run.eventBuffer];\n }\n\n /**\n * List all active runs\n */\n listActive(): Array<{ runId: string; task: string; status: RunStatus; startedAt: string }> {\n return Array.from(this.activeRuns.values()).map(r => ({\n runId: r.runId,\n task: r.task,\n status: r.status,\n startedAt: r.startedAt,\n }));\n }\n\n /**\n * Request graceful stop of a running agent (and its child agents via propagated AbortSignal).\n * Agent finishes its current tool call then exits at the next iteration boundary.\n */\n requestStop(runId: string): boolean {\n const run = this.activeRuns.get(runId);\n if (!run || run.status !== 'running' || !run.agent) {\n return false;\n }\n run.agent.requestStop();\n return true;\n }\n\n /**\n * Clean up completed runs from memory (cache handles its own TTL)\n */\n cleanup(): void {\n for (const [runId, run] of this.activeRuns) {\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n // Remove from memory but keep in cache\n this.activeRuns.delete(runId);\n }\n }\n }\n}\n\n/**\n * Singleton instance — stored on globalThis so all bundled modules share one instance.\n * When tsup compiles multiple entry points, each gets its own module scope,\n * so a plain `export const RunManager = new RunManagerImpl()` creates separate instances.\n * Using globalThis ensures run-handler.js and session-stream-handler.js share one RunManager.\n */\nconst GLOBAL_KEY = '__kb_agent_run_manager__';\nif (!(globalThis as Record<string, unknown>)[GLOBAL_KEY]) {\n (globalThis as Record<string, unknown>)[GLOBAL_KEY] = new RunManagerImpl();\n}\nexport const RunManager = (globalThis as Record<string, unknown>)[GLOBAL_KEY] as RunManagerImpl;\n","/**\n * GET /run/:runId handler\n *\n * Returns current status of a run\n */\n\nimport { defineHandler, rethrowForRest, type RestInput, type PluginContextV3 } from '@kb-labs/sdk';\nimport type { RunStatusResponse } from '@kb-labs/agent-contracts';\nimport { RunManager } from '../run-manager.js';\n\nexport default defineHandler({\n async execute(\n ctx: PluginContextV3,\n input: RestInput<void>\n ): Promise<RunStatusResponse> {\n try {\n const { runId } = input.params as { runId: string };\n\n // First try to get active run from memory\n const run = RunManager.get(runId);\n\n if (!run) {\n // If not in memory, check cache for completed runs\n const state = await RunManager.getState(runId);\n\n if (!state) {\n // Return 404-like response\n return {\n runId,\n status: 'failed',\n task: '',\n startedAt: '',\n error: `Run ${runId} not found`,\n };\n }\n\n // Return cached state (no active orchestrator)\n return {\n runId: state.runId,\n status: state.status,\n task: state.task,\n startedAt: state.startedAt,\n completedAt: state.completedAt,\n durationMs: state.durationMs,\n summary: state.summary,\n error: state.error,\n };\n }\n\n return {\n runId: run.runId,\n status: run.status,\n task: run.task,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n } catch (err) {\n rethrowForRest(err);\n }\n },\n});\n"]}
1
+ {"version":3,"sources":["../../../../home/runner/work/kb-labs/kb-labs/plugins/agents/entry/src/rest/run-manager.ts","../../../../home/runner/work/kb-labs/kb-labs/plugins/agents/entry/src/rest/handlers/status-handler.ts"],"names":[],"mappings":"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","file":"status-handler.js","sourcesContent":["/**\n * Run Manager - tracks active agent runs for REST/WS API\n *\n * Uses platform cache for persistence and in-memory Map for active agents.\n * Cache stores serializable run state, Map stores live agent references.\n */\n\nimport type { IAgentRunner } from '@kb-labs/agent-sdk';\nimport type { SessionManager } from '@kb-labs/agent-core';\nimport type { Turn, TurnDelta } from '@kb-labs/agent-contracts';\nimport { useCache } from '@kb-labs/sdk';\n\nconst CACHE_PREFIX = 'agent:run:';\nconst CACHE_TTL = 3600000; // 1 hour\n\n/**\n * Run status\n */\nexport type RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'stopped';\n\n/**\n * Serializable run state (stored in cache)\n */\nexport interface RunState {\n runId: string;\n task: string;\n status: RunStatus;\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n summary?: string;\n error?: string;\n}\n\n/**\n * Active run with live agent (in-memory only)\n */\nexport interface ActiveRun extends RunState {\n agent?: IAgentRunner;\n sessionManager: SessionManager;\n}\n\n/**\n * Payload delivered to session-level listeners — the sole live-update\n * channel for a session's WS connection(s). `deltas` carries incremental\n * turn changes (see TurnDelta); `run:completed` is a terminal signal\n * carrying the finished run's outcome and, when available, the final\n * state of the assistant turn it produced.\n */\nexport type SessionBroadcastPayload =\n | { kind: 'deltas'; deltas: TurnDelta[] }\n | {\n kind: 'run:completed';\n runId: string;\n success: boolean;\n summary: string;\n durationMs: number;\n seq: number;\n turn?: Turn;\n };\n\ntype SessionListenerCallback = (payload: SessionBroadcastPayload) => void;\n\n/**\n * Run Manager implementation\n */\nclass RunManagerImpl {\n /** Live agents (not cacheable) */\n private activeRuns: Map<string, ActiveRun> = new Map();\n\n /** Session-level listeners: sessionId → Set<callback> — receive updates from ALL runs in session */\n private sessionListeners: Map<string, Set<SessionListenerCallback>> = new Map();\n\n /**\n * Generate unique run ID\n */\n generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n }\n\n /**\n * Register a new run\n */\n async register(\n runId: string,\n task: string,\n agent: IAgentRunner | undefined,\n sessionManager: SessionManager,\n ): Promise<ActiveRun> {\n const now = new Date().toISOString();\n\n const run: ActiveRun = {\n runId,\n task,\n status: 'pending',\n agent,\n sessionManager,\n startedAt: now,\n };\n\n // Store in memory (for live agent)\n this.activeRuns.set(runId, run);\n\n // Store serializable state in cache\n await this.saveToCache(run);\n\n return run;\n }\n\n /**\n * Get run by ID (from memory first, then cache for state)\n */\n get(runId: string): ActiveRun | undefined {\n return this.activeRuns.get(runId);\n }\n\n /**\n * Check if run exists (in memory or cache)\n */\n async exists(runId: string): Promise<boolean> {\n // Check memory first\n if (this.activeRuns.has(runId)) {\n return true;\n }\n // Fallback to cache\n const state = await this.getState(runId);\n return state !== null;\n }\n\n /**\n * Get run state from cache (for completed runs or cross-process access)\n */\n async getState(runId: string): Promise<RunState | null> {\n const cache = useCache();\n if (!cache) {return null;}\n\n return cache.get<RunState>(`${CACHE_PREFIX}${runId}`);\n }\n\n /**\n * Update run status\n */\n async updateStatus(runId: string, status: RunStatus, extra?: Partial<RunState>): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (run) {\n run.status = status;\n if (extra) {\n Object.assign(run, extra);\n }\n await this.saveToCache(run);\n }\n }\n\n /**\n * Save run state to cache\n */\n private async saveToCache(run: ActiveRun): Promise<void> {\n const cache = useCache();\n if (!cache) {return;}\n\n const state: RunState = {\n runId: run.runId,\n task: run.task,\n status: run.status,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n\n await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);\n }\n\n /**\n * Broadcast turn deltas to all session-level listeners. Callers MUST await\n * persistence (sessionManager.addEvent) before calling this, so listeners\n * never observe a delta whose underlying turn state isn't on disk yet —\n * that ordering is what makes gap-recovery / reconnect resume correct.\n */\n broadcastSessionDeltas(sessionId: string, deltas: TurnDelta[]): void {\n if (deltas.length === 0) { return; }\n this.notifySessionListeners(sessionId, { kind: 'deltas', deltas });\n }\n\n /**\n * Broadcast a run's terminal outcome to session-level listeners. Called\n * directly from the run's own completion handler (after `agent.execute()`\n * resolves) rather than derived reactively from an `agent:end` event —\n * the completion handler already has definitive success/summary/duration,\n * and this guarantees the message fires exactly once, deterministically.\n */\n broadcastRunCompleted(sessionId: string, payload: Omit<Extract<SessionBroadcastPayload, { kind: 'run:completed' }>, 'kind'>): void {\n this.notifySessionListeners(sessionId, { kind: 'run:completed', ...payload });\n }\n\n private notifySessionListeners(sessionId: string, payload: SessionBroadcastPayload): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (!listeners) { return; }\n for (const listener of listeners) {\n try {\n listener(payload);\n } catch {\n // Ignore listener errors — one bad WS connection shouldn't affect others\n }\n }\n }\n\n /**\n * Add a session-level listener — receives updates from ALL runs in this session.\n */\n addSessionListener(sessionId: string, callback: SessionListenerCallback): void {\n if (!this.sessionListeners.has(sessionId)) {\n this.sessionListeners.set(sessionId, new Set());\n }\n this.sessionListeners.get(sessionId)!.add(callback);\n }\n\n /**\n * Remove a session-level listener.\n */\n removeSessionListener(sessionId: string, callback: SessionListenerCallback): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.sessionListeners.delete(sessionId);\n }\n }\n }\n\n /**\n * List all active runs\n */\n listActive(): Array<{ runId: string; task: string; status: RunStatus; startedAt: string }> {\n return Array.from(this.activeRuns.values()).map(r => ({\n runId: r.runId,\n task: r.task,\n status: r.status,\n startedAt: r.startedAt,\n }));\n }\n\n /**\n * Request graceful stop of a running agent (and its child agents via propagated AbortSignal).\n * Agent finishes its current tool call then exits at the next iteration boundary.\n */\n requestStop(runId: string): boolean {\n const run = this.activeRuns.get(runId);\n if (!run || run.status !== 'running' || !run.agent) {\n return false;\n }\n run.agent.requestStop();\n return true;\n }\n\n /**\n * Clean up completed runs from memory (cache handles its own TTL)\n */\n cleanup(): void {\n for (const [runId, run] of this.activeRuns) {\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n // Remove from memory but keep in cache\n this.activeRuns.delete(runId);\n }\n }\n }\n}\n\n/**\n * Singleton instance — stored on globalThis so all bundled modules share one instance.\n * When tsup compiles multiple entry points, each gets its own module scope,\n * so a plain `export const RunManager = new RunManagerImpl()` creates separate instances.\n * Using globalThis ensures run-handler.js and session-stream-handler.js share one RunManager.\n */\nconst GLOBAL_KEY = '__kb_agent_run_manager__';\nif (!(globalThis as Record<string, unknown>)[GLOBAL_KEY]) {\n (globalThis as Record<string, unknown>)[GLOBAL_KEY] = new RunManagerImpl();\n}\nexport const RunManager = (globalThis as Record<string, unknown>)[GLOBAL_KEY] as RunManagerImpl;\n","/**\n * GET /run/:runId handler\n *\n * Returns current status of a run\n */\n\nimport { defineHandler, rethrowForRest, type RestInput, type PluginContextV3 } from '@kb-labs/sdk';\nimport type { RunStatusResponse } from '@kb-labs/agent-contracts';\nimport { RunManager } from '../run-manager.js';\n\nexport default defineHandler({\n async execute(\n ctx: PluginContextV3,\n input: RestInput<void>\n ): Promise<RunStatusResponse> {\n try {\n const { runId } = input.params as { runId: string };\n\n // First try to get active run from memory\n const run = RunManager.get(runId);\n\n if (!run) {\n // If not in memory, check cache for completed runs\n const state = await RunManager.getState(runId);\n\n if (!state) {\n // Return 404-like response\n return {\n runId,\n status: 'failed',\n task: '',\n startedAt: '',\n error: `Run ${runId} not found`,\n };\n }\n\n // Return cached state (no active orchestrator)\n return {\n runId: state.runId,\n status: state.status,\n task: state.task,\n startedAt: state.startedAt,\n completedAt: state.completedAt,\n durationMs: state.durationMs,\n summary: state.summary,\n error: state.error,\n };\n }\n\n return {\n runId: run.runId,\n status: run.status,\n task: run.task,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n } catch (err) {\n rethrowForRest(err);\n }\n },\n});\n"]}
@@ -1,17 +1,14 @@
1
- import { defineHandler, useAnalytics, rethrowForRest, useCache, usePlatform } from '@kb-labs/sdk';
1
+ import { defineHandler, useAnalytics, rethrowForRest, useCache } from '@kb-labs/sdk';
2
2
  import { AGENT_ANALYTICS_EVENTS } from '@kb-labs/agent-contracts';
3
3
 
4
4
  // src/rest/handlers/stop-handler.ts
5
5
  var CACHE_PREFIX = "agent:run:";
6
- var EVENT_TOPIC_PREFIX = "agent:events:";
7
6
  var CACHE_TTL = 36e5;
8
7
  var RunManagerImpl = class {
9
- /** Live agents and listeners (not cacheable) */
8
+ /** Live agents (not cacheable) */
10
9
  activeRuns = /* @__PURE__ */ new Map();
11
- /** Session-level listeners: sessionId → Set<callback> — receive events from ALL runs in session */
10
+ /** Session-level listeners: sessionId → Set<callback> — receive updates from ALL runs in session */
12
11
  sessionListeners = /* @__PURE__ */ new Map();
13
- /** sessionId per runId — set when run is registered */
14
- runSessionMap = /* @__PURE__ */ new Map();
15
12
  /**
16
13
  * Generate unique run ID
17
14
  */
@@ -21,7 +18,7 @@ var RunManagerImpl = class {
21
18
  /**
22
19
  * Register a new run
23
20
  */
24
- async register(runId, task, agent, sessionManager, sessionId) {
21
+ async register(runId, task, agent, sessionManager) {
25
22
  const now = (/* @__PURE__ */ new Date()).toISOString();
26
23
  const run = {
27
24
  runId,
@@ -29,15 +26,9 @@ var RunManagerImpl = class {
29
26
  status: "pending",
30
27
  agent,
31
28
  sessionManager,
32
- startedAt: now,
33
- listeners: /* @__PURE__ */ new Set(),
34
- lastSeq: 0,
35
- eventBuffer: []
29
+ startedAt: now
36
30
  };
37
31
  this.activeRuns.set(runId, run);
38
- if (sessionId) {
39
- this.runSessionMap.set(runId, sessionId);
40
- }
41
32
  await this.saveToCache(run);
42
33
  return run;
43
34
  }
@@ -100,101 +91,42 @@ var RunManagerImpl = class {
100
91
  };
101
92
  await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);
102
93
  }
103
- /** Track subscriptions for cleanup */
104
- subscriptions = /* @__PURE__ */ new Map();
105
94
  /**
106
- * Add event listener to run (uses eventBus for cross-process)
95
+ * Broadcast turn deltas to all session-level listeners. Callers MUST await
96
+ * persistence (sessionManager.addEvent) before calling this, so listeners
97
+ * never observe a delta whose underlying turn state isn't on disk yet —
98
+ * that ordering is what makes gap-recovery / reconnect resume correct.
107
99
  */
108
- addListener(runId, callback) {
109
- const platform = usePlatform();
110
- if (!platform?.eventBus) {
111
- const run2 = this.activeRuns.get(runId);
112
- if (run2) {
113
- run2.listeners.add(callback);
114
- return true;
115
- }
116
- return false;
117
- }
118
- const topic = `${EVENT_TOPIC_PREFIX}${runId}`;
119
- const unsubscribe = platform.eventBus.subscribe(topic, async (event) => {
120
- try {
121
- callback(event);
122
- } catch {
123
- }
124
- });
125
- if (!this.subscriptions.has(runId)) {
126
- this.subscriptions.set(runId, /* @__PURE__ */ new Map());
127
- }
128
- this.subscriptions.get(runId).set(callback, unsubscribe);
129
- const run = this.activeRuns.get(runId);
130
- if (run) {
131
- run.listeners.add(callback);
100
+ broadcastSessionDeltas(sessionId, deltas) {
101
+ if (deltas.length === 0) {
102
+ return;
132
103
  }
133
- return true;
104
+ this.notifySessionListeners(sessionId, { kind: "deltas", deltas });
134
105
  }
135
106
  /**
136
- * Remove event listener from run
107
+ * Broadcast a run's terminal outcome to session-level listeners. Called
108
+ * directly from the run's own completion handler (after `agent.execute()`
109
+ * resolves) rather than derived reactively from an `agent:end` event —
110
+ * the completion handler already has definitive success/summary/duration,
111
+ * and this guarantees the message fires exactly once, deterministically.
137
112
  */
138
- removeListener(runId, callback) {
139
- const runSubs = this.subscriptions.get(runId);
140
- if (runSubs) {
141
- const unsubscribe = runSubs.get(callback);
142
- if (unsubscribe) {
143
- unsubscribe();
144
- runSubs.delete(callback);
145
- }
146
- if (runSubs.size === 0) {
147
- this.subscriptions.delete(runId);
148
- }
149
- }
150
- const run = this.activeRuns.get(runId);
151
- if (run) {
152
- run.listeners.delete(callback);
153
- }
113
+ broadcastRunCompleted(sessionId, payload) {
114
+ this.notifySessionListeners(sessionId, { kind: "run:completed", ...payload });
154
115
  }
155
- /**
156
- * Broadcast event to all listeners of a run (uses eventBus for cross-process)
157
- * Assigns monotonic sequence number for reliable ordering
158
- * @returns The event with seq assigned (for persistence)
159
- */
160
- broadcast(runId, event) {
161
- const platform = usePlatform();
162
- const run = this.activeRuns.get(runId);
163
- let seqEvent = event;
164
- if (run) {
165
- run.lastSeq++;
166
- seqEvent = { ...event, seq: run.lastSeq, runId };
167
- run.eventBuffer.push(seqEvent);
168
- }
169
- if (platform?.eventBus) {
170
- const topic = `${EVENT_TOPIC_PREFIX}${runId}`;
171
- platform.eventBus.publish(topic, seqEvent).catch(() => {
172
- });
173
- }
174
- if (run) {
175
- for (const listener of run.listeners) {
176
- try {
177
- listener(seqEvent);
178
- } catch {
179
- }
180
- }
116
+ notifySessionListeners(sessionId, payload) {
117
+ const listeners = this.sessionListeners.get(sessionId);
118
+ if (!listeners) {
119
+ return;
181
120
  }
182
- const sessionId = this.runSessionMap.get(runId);
183
- if (sessionId) {
184
- const sListeners = this.sessionListeners.get(sessionId);
185
- if (sListeners) {
186
- for (const listener of sListeners) {
187
- try {
188
- listener(seqEvent);
189
- } catch {
190
- }
191
- }
121
+ for (const listener of listeners) {
122
+ try {
123
+ listener(payload);
124
+ } catch {
192
125
  }
193
126
  }
194
- return seqEvent;
195
127
  }
196
128
  /**
197
- * Add a session-level listener — receives events from ALL runs in this session.
129
+ * Add a session-level listener — receives updates from ALL runs in this session.
198
130
  */
199
131
  addSessionListener(sessionId, callback) {
200
132
  if (!this.sessionListeners.has(sessionId)) {
@@ -214,21 +146,6 @@ var RunManagerImpl = class {
214
146
  }
215
147
  }
216
148
  }
217
- /**
218
- * Get replay buffer for a run (all events emitted since start).
219
- * Used by WS handler to send missed events on late connection.
220
- * Optionally filter by afterSeq to only get events the client hasn't seen.
221
- */
222
- getEventBuffer(runId, afterSeq) {
223
- const run = this.activeRuns.get(runId);
224
- if (!run) {
225
- return [];
226
- }
227
- if (afterSeq != null) {
228
- return run.eventBuffer.filter((e) => e.seq != null && e.seq > afterSeq);
229
- }
230
- return [...run.eventBuffer];
231
- }
232
149
  /**
233
150
  * List all active runs
234
151
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/rest/run-manager.ts","../../../src/rest/handlers/stop-handler.ts"],"names":["run"],"mappings":"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","file":"stop-handler.js","sourcesContent":["/**\n * Run Manager - tracks active agent runs for REST/WS API\n *\n * Uses platform cache for persistence and in-memory Map for active agents.\n * Cache stores serializable run state, Map stores live agent references.\n * Uses platform eventBus for cross-process event broadcasting.\n */\n\nimport type { IAgentRunner } from '@kb-labs/agent-sdk';\nimport type { SessionManager } from '@kb-labs/agent-core';\nimport type { AgentEvent, AgentEventCallback } from '@kb-labs/agent-contracts';\nimport { useCache, usePlatform } from '@kb-labs/sdk';\n\nconst CACHE_PREFIX = 'agent:run:';\nconst EVENT_TOPIC_PREFIX = 'agent:events:';\nconst CACHE_TTL = 3600000; // 1 hour\n\n/**\n * Run status\n */\nexport type RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'stopped';\n\n/**\n * Serializable run state (stored in cache)\n */\nexport interface RunState {\n runId: string;\n task: string;\n status: RunStatus;\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n summary?: string;\n error?: string;\n}\n\n/**\n * Active run with live agent (in-memory only)\n */\nexport interface ActiveRun extends RunState {\n agent?: IAgentRunner;\n sessionManager: SessionManager;\n listeners: Set<AgentEventCallback>;\n /** Monotonic sequence counter for event ordering */\n lastSeq: number;\n /** Replay buffer: all events emitted since run start (for late WS connections) */\n eventBuffer: AgentEvent[];\n}\n\n/**\n * Run Manager implementation\n */\nclass RunManagerImpl {\n /** Live agents and listeners (not cacheable) */\n private activeRuns: Map<string, ActiveRun> = new Map();\n\n /** Session-level listeners: sessionId → Set<callback> — receive events from ALL runs in session */\n private sessionListeners: Map<string, Set<AgentEventCallback>> = new Map();\n\n /** sessionId per runId — set when run is registered */\n private runSessionMap: Map<string, string> = new Map();\n\n /**\n * Generate unique run ID\n */\n generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n }\n\n /**\n * Register a new run\n */\n async register(\n runId: string,\n task: string,\n agent: IAgentRunner | undefined,\n sessionManager: SessionManager,\n sessionId?: string\n ): Promise<ActiveRun> {\n const now = new Date().toISOString();\n\n const run: ActiveRun = {\n runId,\n task,\n status: 'pending',\n agent,\n sessionManager,\n startedAt: now,\n listeners: new Set(),\n lastSeq: 0,\n eventBuffer: [],\n };\n\n // Store in memory (for live agent)\n this.activeRuns.set(runId, run);\n\n // Track sessionId → runId mapping for session-level listeners\n if (sessionId) {\n this.runSessionMap.set(runId, sessionId);\n }\n\n // Store serializable state in cache\n await this.saveToCache(run);\n\n return run;\n }\n\n /**\n * Get run by ID (from memory first, then cache for state)\n */\n get(runId: string): ActiveRun | undefined {\n return this.activeRuns.get(runId);\n }\n\n /**\n * Check if run exists (in memory or cache)\n */\n async exists(runId: string): Promise<boolean> {\n // Check memory first\n if (this.activeRuns.has(runId)) {\n return true;\n }\n // Fallback to cache\n const state = await this.getState(runId);\n return state !== null;\n }\n\n /**\n * Get run state from cache (for completed runs or cross-process access)\n */\n async getState(runId: string): Promise<RunState | null> {\n const cache = useCache();\n if (!cache) {return null;}\n\n return cache.get<RunState>(`${CACHE_PREFIX}${runId}`);\n }\n\n /**\n * Update run status\n */\n async updateStatus(runId: string, status: RunStatus, extra?: Partial<RunState>): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (run) {\n run.status = status;\n if (extra) {\n Object.assign(run, extra);\n }\n await this.saveToCache(run);\n }\n }\n\n /**\n * Save run state to cache\n */\n private async saveToCache(run: ActiveRun): Promise<void> {\n const cache = useCache();\n if (!cache) {return;}\n\n const state: RunState = {\n runId: run.runId,\n task: run.task,\n status: run.status,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n\n await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);\n }\n\n /** Track subscriptions for cleanup */\n private subscriptions: Map<string, Map<AgentEventCallback, () => void>> = new Map();\n\n /**\n * Add event listener to run (uses eventBus for cross-process)\n */\n addListener(runId: string, callback: AgentEventCallback): boolean {\n const platform = usePlatform();\n if (!platform?.eventBus) {\n // Fallback to in-memory only\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.add(callback);\n return true;\n }\n return false;\n }\n\n // Subscribe to eventBus topic for this run\n const topic = `${EVENT_TOPIC_PREFIX}${runId}`;\n const unsubscribe = platform.eventBus.subscribe<AgentEvent>(topic, async (event) => {\n try {\n callback(event);\n } catch {\n // Ignore callback errors\n }\n });\n\n // Track subscription for cleanup\n if (!this.subscriptions.has(runId)) {\n this.subscriptions.set(runId, new Map());\n }\n this.subscriptions.get(runId)!.set(callback, unsubscribe);\n\n // Also add to in-memory if run exists locally\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.add(callback);\n }\n\n return true;\n }\n\n /**\n * Remove event listener from run\n */\n removeListener(runId: string, callback: AgentEventCallback): void {\n // Unsubscribe from eventBus\n const runSubs = this.subscriptions.get(runId);\n if (runSubs) {\n const unsubscribe = runSubs.get(callback);\n if (unsubscribe) {\n unsubscribe();\n runSubs.delete(callback);\n }\n if (runSubs.size === 0) {\n this.subscriptions.delete(runId);\n }\n }\n\n // Also remove from in-memory\n const run = this.activeRuns.get(runId);\n if (run) {\n run.listeners.delete(callback);\n }\n }\n\n /**\n * Broadcast event to all listeners of a run (uses eventBus for cross-process)\n * Assigns monotonic sequence number for reliable ordering\n * @returns The event with seq assigned (for persistence)\n */\n broadcast(runId: string, event: AgentEvent): AgentEvent {\n const platform = usePlatform();\n const run = this.activeRuns.get(runId);\n\n // Assign sequence number for ordering\n let seqEvent = event;\n if (run) {\n run.lastSeq++;\n seqEvent = { ...event, seq: run.lastSeq, runId };\n\n // Store in replay buffer (for late WS connections)\n run.eventBuffer.push(seqEvent);\n }\n\n // Publish to eventBus for cross-process delivery\n if (platform?.eventBus) {\n const topic = `${EVENT_TOPIC_PREFIX}${runId}`;\n platform.eventBus.publish(topic, seqEvent).catch(() => {\n // Ignore publish errors\n });\n }\n\n // Also notify local in-memory listeners (for same-process)\n if (run) {\n for (const listener of run.listeners) {\n try {\n listener(seqEvent);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n // Notify session-level listeners (persistent connections)\n const sessionId = this.runSessionMap.get(runId);\n if (sessionId) {\n const sListeners = this.sessionListeners.get(sessionId);\n if (sListeners) {\n for (const listener of sListeners) {\n try {\n listener(seqEvent);\n } catch {\n // Ignore listener errors\n }\n }\n }\n }\n\n return seqEvent;\n }\n\n /**\n * Add a session-level listener — receives events from ALL runs in this session.\n */\n addSessionListener(sessionId: string, callback: AgentEventCallback): void {\n if (!this.sessionListeners.has(sessionId)) {\n this.sessionListeners.set(sessionId, new Set());\n }\n this.sessionListeners.get(sessionId)!.add(callback);\n }\n\n /**\n * Remove a session-level listener.\n */\n removeSessionListener(sessionId: string, callback: AgentEventCallback): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.sessionListeners.delete(sessionId);\n }\n }\n }\n\n /**\n * Get replay buffer for a run (all events emitted since start).\n * Used by WS handler to send missed events on late connection.\n * Optionally filter by afterSeq to only get events the client hasn't seen.\n */\n getEventBuffer(runId: string, afterSeq?: number): AgentEvent[] {\n const run = this.activeRuns.get(runId);\n if (!run) {return [];}\n\n if (afterSeq != null) {\n return run.eventBuffer.filter((e) => e.seq != null && e.seq > afterSeq);\n }\n return [...run.eventBuffer];\n }\n\n /**\n * List all active runs\n */\n listActive(): Array<{ runId: string; task: string; status: RunStatus; startedAt: string }> {\n return Array.from(this.activeRuns.values()).map(r => ({\n runId: r.runId,\n task: r.task,\n status: r.status,\n startedAt: r.startedAt,\n }));\n }\n\n /**\n * Request graceful stop of a running agent (and its child agents via propagated AbortSignal).\n * Agent finishes its current tool call then exits at the next iteration boundary.\n */\n requestStop(runId: string): boolean {\n const run = this.activeRuns.get(runId);\n if (!run || run.status !== 'running' || !run.agent) {\n return false;\n }\n run.agent.requestStop();\n return true;\n }\n\n /**\n * Clean up completed runs from memory (cache handles its own TTL)\n */\n cleanup(): void {\n for (const [runId, run] of this.activeRuns) {\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n // Remove from memory but keep in cache\n this.activeRuns.delete(runId);\n }\n }\n }\n}\n\n/**\n * Singleton instance — stored on globalThis so all bundled modules share one instance.\n * When tsup compiles multiple entry points, each gets its own module scope,\n * so a plain `export const RunManager = new RunManagerImpl()` creates separate instances.\n * Using globalThis ensures run-handler.js and session-stream-handler.js share one RunManager.\n */\nconst GLOBAL_KEY = '__kb_agent_run_manager__';\nif (!(globalThis as Record<string, unknown>)[GLOBAL_KEY]) {\n (globalThis as Record<string, unknown>)[GLOBAL_KEY] = new RunManagerImpl();\n}\nexport const RunManager = (globalThis as Record<string, unknown>)[GLOBAL_KEY] as RunManagerImpl;\n","/**\n * POST /run/:runId/stop handler\n *\n * Stops a running agent execution\n */\n\nimport { defineHandler, rethrowForRest, useAnalytics, type RestInput, type PluginContextV3 } from '@kb-labs/sdk';\nimport type { StopRequest, StopResponse } from '@kb-labs/agent-contracts';\nimport { AGENT_ANALYTICS_EVENTS } from '@kb-labs/agent-contracts';\nimport { RunManager } from '../run-manager.js';\n\nexport default defineHandler({\n async execute(\n ctx: PluginContextV3,\n input: RestInput<StopRequest>\n ): Promise<StopResponse> {\n try {\n const { runId } = input.params as { runId: string };\n const body = input.body as StopRequest | undefined;\n const reason = body?.reason;\n\n ctx.platform.logger.info(`[stop-handler] Stop requested for run ${runId}${reason ? `: ${reason}` : ''}`);\n\n const run = RunManager.get(runId);\n\n if (!run) {\n return {\n stopped: false,\n runId,\n finalStatus: 'not_found',\n };\n }\n\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n return {\n stopped: false,\n runId,\n finalStatus: 'already_completed',\n };\n }\n\n // Signal the agent to stop after its current tool call finishes\n RunManager.requestStop(runId);\n\n // Update status optimistically — agent:end event will also update it async\n await RunManager.updateStatus(runId, 'stopped', {\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - new Date(run.startedAt).getTime(),\n summary: reason ? `Stopped by user: ${reason}` : 'Stopped by user',\n });\n\n // Track stop\n const analytics = useAnalytics();\n await analytics?.track(AGENT_ANALYTICS_EVENTS.RUN_STOPPED, {\n runId,\n durationMs: Date.now() - new Date(run.startedAt).getTime(),\n reason: reason?.slice(0, 200),\n });\n\n ctx.platform.logger.info(`[stop-handler] Run ${runId} stopped`);\n\n return {\n stopped: true,\n runId,\n finalStatus: 'stopped',\n };\n } catch (err) {\n rethrowForRest(err);\n }\n },\n});\n"]}
1
+ {"version":3,"sources":["../../../../home/runner/work/kb-labs/kb-labs/plugins/agents/entry/src/rest/run-manager.ts","../../../../home/runner/work/kb-labs/kb-labs/plugins/agents/entry/src/rest/handlers/stop-handler.ts"],"names":[],"mappings":"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","file":"stop-handler.js","sourcesContent":["/**\n * Run Manager - tracks active agent runs for REST/WS API\n *\n * Uses platform cache for persistence and in-memory Map for active agents.\n * Cache stores serializable run state, Map stores live agent references.\n */\n\nimport type { IAgentRunner } from '@kb-labs/agent-sdk';\nimport type { SessionManager } from '@kb-labs/agent-core';\nimport type { Turn, TurnDelta } from '@kb-labs/agent-contracts';\nimport { useCache } from '@kb-labs/sdk';\n\nconst CACHE_PREFIX = 'agent:run:';\nconst CACHE_TTL = 3600000; // 1 hour\n\n/**\n * Run status\n */\nexport type RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'stopped';\n\n/**\n * Serializable run state (stored in cache)\n */\nexport interface RunState {\n runId: string;\n task: string;\n status: RunStatus;\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n summary?: string;\n error?: string;\n}\n\n/**\n * Active run with live agent (in-memory only)\n */\nexport interface ActiveRun extends RunState {\n agent?: IAgentRunner;\n sessionManager: SessionManager;\n}\n\n/**\n * Payload delivered to session-level listeners — the sole live-update\n * channel for a session's WS connection(s). `deltas` carries incremental\n * turn changes (see TurnDelta); `run:completed` is a terminal signal\n * carrying the finished run's outcome and, when available, the final\n * state of the assistant turn it produced.\n */\nexport type SessionBroadcastPayload =\n | { kind: 'deltas'; deltas: TurnDelta[] }\n | {\n kind: 'run:completed';\n runId: string;\n success: boolean;\n summary: string;\n durationMs: number;\n seq: number;\n turn?: Turn;\n };\n\ntype SessionListenerCallback = (payload: SessionBroadcastPayload) => void;\n\n/**\n * Run Manager implementation\n */\nclass RunManagerImpl {\n /** Live agents (not cacheable) */\n private activeRuns: Map<string, ActiveRun> = new Map();\n\n /** Session-level listeners: sessionId → Set<callback> — receive updates from ALL runs in session */\n private sessionListeners: Map<string, Set<SessionListenerCallback>> = new Map();\n\n /**\n * Generate unique run ID\n */\n generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n }\n\n /**\n * Register a new run\n */\n async register(\n runId: string,\n task: string,\n agent: IAgentRunner | undefined,\n sessionManager: SessionManager,\n ): Promise<ActiveRun> {\n const now = new Date().toISOString();\n\n const run: ActiveRun = {\n runId,\n task,\n status: 'pending',\n agent,\n sessionManager,\n startedAt: now,\n };\n\n // Store in memory (for live agent)\n this.activeRuns.set(runId, run);\n\n // Store serializable state in cache\n await this.saveToCache(run);\n\n return run;\n }\n\n /**\n * Get run by ID (from memory first, then cache for state)\n */\n get(runId: string): ActiveRun | undefined {\n return this.activeRuns.get(runId);\n }\n\n /**\n * Check if run exists (in memory or cache)\n */\n async exists(runId: string): Promise<boolean> {\n // Check memory first\n if (this.activeRuns.has(runId)) {\n return true;\n }\n // Fallback to cache\n const state = await this.getState(runId);\n return state !== null;\n }\n\n /**\n * Get run state from cache (for completed runs or cross-process access)\n */\n async getState(runId: string): Promise<RunState | null> {\n const cache = useCache();\n if (!cache) {return null;}\n\n return cache.get<RunState>(`${CACHE_PREFIX}${runId}`);\n }\n\n /**\n * Update run status\n */\n async updateStatus(runId: string, status: RunStatus, extra?: Partial<RunState>): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (run) {\n run.status = status;\n if (extra) {\n Object.assign(run, extra);\n }\n await this.saveToCache(run);\n }\n }\n\n /**\n * Save run state to cache\n */\n private async saveToCache(run: ActiveRun): Promise<void> {\n const cache = useCache();\n if (!cache) {return;}\n\n const state: RunState = {\n runId: run.runId,\n task: run.task,\n status: run.status,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n durationMs: run.durationMs,\n summary: run.summary,\n error: run.error,\n };\n\n await cache.set(`${CACHE_PREFIX}${run.runId}`, state, CACHE_TTL);\n }\n\n /**\n * Broadcast turn deltas to all session-level listeners. Callers MUST await\n * persistence (sessionManager.addEvent) before calling this, so listeners\n * never observe a delta whose underlying turn state isn't on disk yet —\n * that ordering is what makes gap-recovery / reconnect resume correct.\n */\n broadcastSessionDeltas(sessionId: string, deltas: TurnDelta[]): void {\n if (deltas.length === 0) { return; }\n this.notifySessionListeners(sessionId, { kind: 'deltas', deltas });\n }\n\n /**\n * Broadcast a run's terminal outcome to session-level listeners. Called\n * directly from the run's own completion handler (after `agent.execute()`\n * resolves) rather than derived reactively from an `agent:end` event —\n * the completion handler already has definitive success/summary/duration,\n * and this guarantees the message fires exactly once, deterministically.\n */\n broadcastRunCompleted(sessionId: string, payload: Omit<Extract<SessionBroadcastPayload, { kind: 'run:completed' }>, 'kind'>): void {\n this.notifySessionListeners(sessionId, { kind: 'run:completed', ...payload });\n }\n\n private notifySessionListeners(sessionId: string, payload: SessionBroadcastPayload): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (!listeners) { return; }\n for (const listener of listeners) {\n try {\n listener(payload);\n } catch {\n // Ignore listener errors — one bad WS connection shouldn't affect others\n }\n }\n }\n\n /**\n * Add a session-level listener — receives updates from ALL runs in this session.\n */\n addSessionListener(sessionId: string, callback: SessionListenerCallback): void {\n if (!this.sessionListeners.has(sessionId)) {\n this.sessionListeners.set(sessionId, new Set());\n }\n this.sessionListeners.get(sessionId)!.add(callback);\n }\n\n /**\n * Remove a session-level listener.\n */\n removeSessionListener(sessionId: string, callback: SessionListenerCallback): void {\n const listeners = this.sessionListeners.get(sessionId);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.sessionListeners.delete(sessionId);\n }\n }\n }\n\n /**\n * List all active runs\n */\n listActive(): Array<{ runId: string; task: string; status: RunStatus; startedAt: string }> {\n return Array.from(this.activeRuns.values()).map(r => ({\n runId: r.runId,\n task: r.task,\n status: r.status,\n startedAt: r.startedAt,\n }));\n }\n\n /**\n * Request graceful stop of a running agent (and its child agents via propagated AbortSignal).\n * Agent finishes its current tool call then exits at the next iteration boundary.\n */\n requestStop(runId: string): boolean {\n const run = this.activeRuns.get(runId);\n if (!run || run.status !== 'running' || !run.agent) {\n return false;\n }\n run.agent.requestStop();\n return true;\n }\n\n /**\n * Clean up completed runs from memory (cache handles its own TTL)\n */\n cleanup(): void {\n for (const [runId, run] of this.activeRuns) {\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n // Remove from memory but keep in cache\n this.activeRuns.delete(runId);\n }\n }\n }\n}\n\n/**\n * Singleton instance — stored on globalThis so all bundled modules share one instance.\n * When tsup compiles multiple entry points, each gets its own module scope,\n * so a plain `export const RunManager = new RunManagerImpl()` creates separate instances.\n * Using globalThis ensures run-handler.js and session-stream-handler.js share one RunManager.\n */\nconst GLOBAL_KEY = '__kb_agent_run_manager__';\nif (!(globalThis as Record<string, unknown>)[GLOBAL_KEY]) {\n (globalThis as Record<string, unknown>)[GLOBAL_KEY] = new RunManagerImpl();\n}\nexport const RunManager = (globalThis as Record<string, unknown>)[GLOBAL_KEY] as RunManagerImpl;\n","/**\n * POST /run/:runId/stop handler\n *\n * Stops a running agent execution\n */\n\nimport { defineHandler, rethrowForRest, useAnalytics, type RestInput, type PluginContextV3 } from '@kb-labs/sdk';\nimport type { StopRequest, StopResponse } from '@kb-labs/agent-contracts';\nimport { AGENT_ANALYTICS_EVENTS } from '@kb-labs/agent-contracts';\nimport { RunManager } from '../run-manager.js';\n\nexport default defineHandler({\n async execute(\n ctx: PluginContextV3,\n input: RestInput<StopRequest>\n ): Promise<StopResponse> {\n try {\n const { runId } = input.params as { runId: string };\n const body = input.body as StopRequest | undefined;\n const reason = body?.reason;\n\n ctx.platform.logger.info(`[stop-handler] Stop requested for run ${runId}${reason ? `: ${reason}` : ''}`);\n\n const run = RunManager.get(runId);\n\n if (!run) {\n return {\n stopped: false,\n runId,\n finalStatus: 'not_found',\n };\n }\n\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'stopped') {\n return {\n stopped: false,\n runId,\n finalStatus: 'already_completed',\n };\n }\n\n // Signal the agent to stop after its current tool call finishes\n RunManager.requestStop(runId);\n\n // Update status optimistically — agent:end event will also update it async\n await RunManager.updateStatus(runId, 'stopped', {\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - new Date(run.startedAt).getTime(),\n summary: reason ? `Stopped by user: ${reason}` : 'Stopped by user',\n });\n\n // Track stop\n const analytics = useAnalytics();\n await analytics?.track(AGENT_ANALYTICS_EVENTS.RUN_STOPPED, {\n runId,\n durationMs: Date.now() - new Date(run.startedAt).getTime(),\n reason: reason?.slice(0, 200),\n });\n\n ctx.platform.logger.info(`[stop-handler] Run ${runId} stopped`);\n\n return {\n stopped: true,\n runId,\n finalStatus: 'stopped',\n };\n } catch (err) {\n rethrowForRest(err);\n }\n },\n});\n"]}