indusagi-coding-agent 0.1.42 → 0.1.44

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 (288) hide show
  1. package/CHANGELOG.md +23 -3
  2. package/README.md +3 -0
  3. package/dist/command-line/args.js +1 -1
  4. package/dist/command-line/args.js.map +1 -1
  5. package/dist/command-line/list-models.js +1 -1
  6. package/dist/command-line/list-models.js.map +1 -1
  7. package/dist/command-line/login-handler.d.ts.map +1 -1
  8. package/dist/command-line/login-handler.js +39 -0
  9. package/dist/command-line/login-handler.js.map +1 -1
  10. package/dist/dev/observe-deep.d.ts +2 -0
  11. package/dist/dev/observe-deep.d.ts.map +1 -0
  12. package/dist/dev/observe-deep.js +333 -0
  13. package/dist/dev/observe-deep.js.map +1 -0
  14. package/dist/dev/observe-smoke.d.ts +2 -0
  15. package/dist/dev/observe-smoke.d.ts.map +1 -0
  16. package/dist/dev/observe-smoke.js +264 -0
  17. package/dist/dev/observe-smoke.js.map +1 -0
  18. package/dist/index.d.ts +4 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/interfaces/induscode-ui-delegate.d.ts +3 -0
  23. package/dist/interfaces/induscode-ui-delegate.d.ts.map +1 -0
  24. package/dist/interfaces/induscode-ui-delegate.js +148 -0
  25. package/dist/interfaces/induscode-ui-delegate.js.map +1 -0
  26. package/dist/interfaces/terminal-ui/components/footer.d.ts.map +1 -1
  27. package/dist/interfaces/terminal-ui/components/footer.js +9 -15
  28. package/dist/interfaces/terminal-ui/components/footer.js.map +1 -1
  29. package/dist/interfaces/terminal-ui/components/model-selector.d.ts.map +1 -1
  30. package/dist/interfaces/terminal-ui/components/model-selector.js +1 -1
  31. package/dist/interfaces/terminal-ui/components/model-selector.js.map +1 -1
  32. package/dist/interfaces/terminal-ui/components/oauth-selector.d.ts.map +1 -1
  33. package/dist/interfaces/terminal-ui/components/oauth-selector.js +1 -0
  34. package/dist/interfaces/terminal-ui/components/oauth-selector.js.map +1 -1
  35. package/dist/main.d.ts.map +1 -1
  36. package/dist/main.js +42 -18
  37. package/dist/main.js.map +1 -1
  38. package/dist/observe.d.ts +2 -0
  39. package/dist/observe.d.ts.map +1 -0
  40. package/dist/observe.js +2 -0
  41. package/dist/observe.js.map +1 -0
  42. package/dist/runtime/agent-session.d.ts +15 -0
  43. package/dist/runtime/agent-session.d.ts.map +1 -1
  44. package/dist/runtime/agent-session.js +444 -223
  45. package/dist/runtime/agent-session.js.map +1 -1
  46. package/dist/runtime/index.d.ts +5 -0
  47. package/dist/runtime/index.d.ts.map +1 -1
  48. package/dist/runtime/index.js +5 -0
  49. package/dist/runtime/index.js.map +1 -1
  50. package/dist/runtime/model-registry.d.ts +6 -0
  51. package/dist/runtime/model-registry.d.ts.map +1 -1
  52. package/dist/runtime/model-registry.js +71 -14
  53. package/dist/runtime/model-registry.js.map +1 -1
  54. package/dist/runtime/model-resolver.d.ts.map +1 -1
  55. package/dist/runtime/model-resolver.js +6 -3
  56. package/dist/runtime/model-resolver.js.map +1 -1
  57. package/dist/runtime/observe.d.ts +50 -0
  58. package/dist/runtime/observe.d.ts.map +1 -0
  59. package/dist/runtime/observe.js +233 -0
  60. package/dist/runtime/observe.js.map +1 -0
  61. package/dist/runtime/plugins/types.d.ts +3 -0
  62. package/dist/runtime/plugins/types.d.ts.map +1 -1
  63. package/dist/runtime/plugins/types.js.map +1 -1
  64. package/dist/runtime/providers/adapters/claude-cli-adapter.d.ts +19 -0
  65. package/dist/runtime/providers/adapters/claude-cli-adapter.d.ts.map +1 -0
  66. package/dist/runtime/providers/adapters/claude-cli-adapter.js +242 -0
  67. package/dist/runtime/providers/adapters/claude-cli-adapter.js.map +1 -0
  68. package/dist/runtime/providers/adapters/codex-cli-adapter.d.ts +18 -0
  69. package/dist/runtime/providers/adapters/codex-cli-adapter.d.ts.map +1 -0
  70. package/dist/runtime/providers/adapters/codex-cli-adapter.js +177 -0
  71. package/dist/runtime/providers/adapters/codex-cli-adapter.js.map +1 -0
  72. package/dist/runtime/providers/adapters/indusagi-cli-adapter.d.ts +21 -0
  73. package/dist/runtime/providers/adapters/indusagi-cli-adapter.d.ts.map +1 -0
  74. package/dist/runtime/providers/adapters/indusagi-cli-adapter.js +162 -0
  75. package/dist/runtime/providers/adapters/indusagi-cli-adapter.js.map +1 -0
  76. package/dist/runtime/providers/adapters/utils.d.ts +12 -0
  77. package/dist/runtime/providers/adapters/utils.d.ts.map +1 -0
  78. package/dist/runtime/providers/adapters/utils.js +122 -0
  79. package/dist/runtime/providers/adapters/utils.js.map +1 -0
  80. package/dist/runtime/providers/builtins.d.ts +3 -0
  81. package/dist/runtime/providers/builtins.d.ts.map +1 -0
  82. package/dist/runtime/providers/builtins.js +99 -0
  83. package/dist/runtime/providers/builtins.js.map +1 -0
  84. package/dist/runtime/providers/provider-adapter-registry.d.ts +11 -0
  85. package/dist/runtime/providers/provider-adapter-registry.d.ts.map +1 -0
  86. package/dist/runtime/providers/provider-adapter-registry.js +36 -0
  87. package/dist/runtime/providers/provider-adapter-registry.js.map +1 -0
  88. package/dist/runtime/providers/provider-adapter.d.ts +33 -0
  89. package/dist/runtime/providers/provider-adapter.d.ts.map +1 -0
  90. package/dist/runtime/providers/provider-adapter.js +2 -0
  91. package/dist/runtime/providers/provider-adapter.js.map +1 -0
  92. package/dist/runtime/providers/provider-service.d.ts +33 -0
  93. package/dist/runtime/providers/provider-service.d.ts.map +1 -0
  94. package/dist/runtime/providers/provider-service.js +157 -0
  95. package/dist/runtime/providers/provider-service.js.map +1 -0
  96. package/dist/runtime/providers/types.d.ts +29 -0
  97. package/dist/runtime/providers/types.d.ts.map +1 -0
  98. package/dist/runtime/providers/types.js +9 -0
  99. package/dist/runtime/providers/types.js.map +1 -0
  100. package/dist/runtime/sdk.d.ts +6 -0
  101. package/dist/runtime/sdk.d.ts.map +1 -1
  102. package/dist/runtime/sdk.js +55 -8
  103. package/dist/runtime/sdk.js.map +1 -1
  104. package/dist/telemetry/core/types.d.ts +1 -1
  105. package/dist/telemetry/core/types.js +1 -1
  106. package/dist/vendor/observe/adapters/agent-event-adapter.d.ts +28 -0
  107. package/dist/vendor/observe/adapters/agent-event-adapter.d.ts.map +1 -0
  108. package/dist/vendor/observe/adapters/agent-event-adapter.js +136 -0
  109. package/dist/vendor/observe/adapters/agent-event-adapter.js.map +1 -0
  110. package/dist/vendor/observe/adapters/ai-stream-adapter.d.ts +24 -0
  111. package/dist/vendor/observe/adapters/ai-stream-adapter.d.ts.map +1 -0
  112. package/dist/vendor/observe/adapters/ai-stream-adapter.js +118 -0
  113. package/dist/vendor/observe/adapters/ai-stream-adapter.js.map +1 -0
  114. package/dist/vendor/observe/artifacts/content-store.d.ts +12 -0
  115. package/dist/vendor/observe/artifacts/content-store.d.ts.map +1 -0
  116. package/dist/vendor/observe/artifacts/content-store.js +2 -0
  117. package/dist/vendor/observe/artifacts/content-store.js.map +1 -0
  118. package/dist/vendor/observe/artifacts/file-content-store.d.ts +16 -0
  119. package/dist/vendor/observe/artifacts/file-content-store.d.ts.map +1 -0
  120. package/dist/vendor/observe/artifacts/file-content-store.js +43 -0
  121. package/dist/vendor/observe/artifacts/file-content-store.js.map +1 -0
  122. package/dist/vendor/observe/batcher.d.ts +27 -0
  123. package/dist/vendor/observe/batcher.d.ts.map +1 -0
  124. package/dist/vendor/observe/batcher.js +66 -0
  125. package/dist/vendor/observe/batcher.js.map +1 -0
  126. package/dist/vendor/observe/client/artifacts-manager.d.ts +29 -0
  127. package/dist/vendor/observe/client/artifacts-manager.d.ts.map +1 -0
  128. package/dist/vendor/observe/client/artifacts-manager.js +120 -0
  129. package/dist/vendor/observe/client/artifacts-manager.js.map +1 -0
  130. package/dist/vendor/observe/client/datasets-manager.d.ts +16 -0
  131. package/dist/vendor/observe/client/datasets-manager.d.ts.map +1 -0
  132. package/dist/vendor/observe/client/datasets-manager.js +27 -0
  133. package/dist/vendor/observe/client/datasets-manager.js.map +1 -0
  134. package/dist/vendor/observe/client/experiments-manager.d.ts +61 -0
  135. package/dist/vendor/observe/client/experiments-manager.d.ts.map +1 -0
  136. package/dist/vendor/observe/client/experiments-manager.js +132 -0
  137. package/dist/vendor/observe/client/experiments-manager.js.map +1 -0
  138. package/dist/vendor/observe/client/index.d.ts +9 -0
  139. package/dist/vendor/observe/client/index.d.ts.map +1 -0
  140. package/dist/vendor/observe/client/index.js +9 -0
  141. package/dist/vendor/observe/client/index.js.map +1 -0
  142. package/dist/vendor/observe/client/observe-client.d.ts +30 -0
  143. package/dist/vendor/observe/client/observe-client.d.ts.map +1 -0
  144. package/dist/vendor/observe/client/observe-client.js +22 -0
  145. package/dist/vendor/observe/client/observe-client.js.map +1 -0
  146. package/dist/vendor/observe/client/prompts-manager.d.ts +17 -0
  147. package/dist/vendor/observe/client/prompts-manager.d.ts.map +1 -0
  148. package/dist/vendor/observe/client/prompts-manager.js +38 -0
  149. package/dist/vendor/observe/client/prompts-manager.js.map +1 -0
  150. package/dist/vendor/observe/client/scores-manager.d.ts +10 -0
  151. package/dist/vendor/observe/client/scores-manager.d.ts.map +1 -0
  152. package/dist/vendor/observe/client/scores-manager.js +15 -0
  153. package/dist/vendor/observe/client/scores-manager.js.map +1 -0
  154. package/dist/vendor/observe/client/sessions-manager.d.ts +15 -0
  155. package/dist/vendor/observe/client/sessions-manager.d.ts.map +1 -0
  156. package/dist/vendor/observe/client/sessions-manager.js +24 -0
  157. package/dist/vendor/observe/client/sessions-manager.js.map +1 -0
  158. package/dist/vendor/observe/client/traces-manager.d.ts +23 -0
  159. package/dist/vendor/observe/client/traces-manager.d.ts.map +1 -0
  160. package/dist/vendor/observe/client/traces-manager.js +39 -0
  161. package/dist/vendor/observe/client/traces-manager.js.map +1 -0
  162. package/dist/vendor/observe/console-exporter.d.ts +11 -0
  163. package/dist/vendor/observe/console-exporter.d.ts.map +1 -0
  164. package/dist/vendor/observe/console-exporter.js +18 -0
  165. package/dist/vendor/observe/console-exporter.js.map +1 -0
  166. package/dist/vendor/observe/context.d.ts +4 -0
  167. package/dist/vendor/observe/context.d.ts.map +1 -0
  168. package/dist/vendor/observe/context.js +9 -0
  169. package/dist/vendor/observe/context.js.map +1 -0
  170. package/dist/vendor/observe/entities/artifacts.d.ts +46 -0
  171. package/dist/vendor/observe/entities/artifacts.d.ts.map +1 -0
  172. package/dist/vendor/observe/entities/artifacts.js +2 -0
  173. package/dist/vendor/observe/entities/artifacts.js.map +1 -0
  174. package/dist/vendor/observe/entities/datasets.d.ts +37 -0
  175. package/dist/vendor/observe/entities/datasets.d.ts.map +1 -0
  176. package/dist/vendor/observe/entities/datasets.js +2 -0
  177. package/dist/vendor/observe/entities/datasets.js.map +1 -0
  178. package/dist/vendor/observe/entities/experiments.d.ts +66 -0
  179. package/dist/vendor/observe/entities/experiments.d.ts.map +1 -0
  180. package/dist/vendor/observe/entities/experiments.js +2 -0
  181. package/dist/vendor/observe/entities/experiments.js.map +1 -0
  182. package/dist/vendor/observe/entities/index.d.ts +9 -0
  183. package/dist/vendor/observe/entities/index.d.ts.map +1 -0
  184. package/dist/vendor/observe/entities/index.js +9 -0
  185. package/dist/vendor/observe/entities/index.js.map +1 -0
  186. package/dist/vendor/observe/entities/prompts.d.ts +25 -0
  187. package/dist/vendor/observe/entities/prompts.d.ts.map +1 -0
  188. package/dist/vendor/observe/entities/prompts.js +2 -0
  189. package/dist/vendor/observe/entities/prompts.js.map +1 -0
  190. package/dist/vendor/observe/entities/scores.d.ts +28 -0
  191. package/dist/vendor/observe/entities/scores.d.ts.map +1 -0
  192. package/dist/vendor/observe/entities/scores.js +2 -0
  193. package/dist/vendor/observe/entities/scores.js.map +1 -0
  194. package/dist/vendor/observe/entities/sessions.d.ts +36 -0
  195. package/dist/vendor/observe/entities/sessions.d.ts.map +1 -0
  196. package/dist/vendor/observe/entities/sessions.js +2 -0
  197. package/dist/vendor/observe/entities/sessions.js.map +1 -0
  198. package/dist/vendor/observe/entities/shared.d.ts +26 -0
  199. package/dist/vendor/observe/entities/shared.d.ts.map +1 -0
  200. package/dist/vendor/observe/entities/shared.js +2 -0
  201. package/dist/vendor/observe/entities/shared.js.map +1 -0
  202. package/dist/vendor/observe/entities/traces.d.ts +59 -0
  203. package/dist/vendor/observe/entities/traces.d.ts.map +1 -0
  204. package/dist/vendor/observe/entities/traces.js +2 -0
  205. package/dist/vendor/observe/entities/traces.js.map +1 -0
  206. package/dist/vendor/observe/exporter.d.ts +2 -0
  207. package/dist/vendor/observe/exporter.d.ts.map +1 -0
  208. package/dist/vendor/observe/exporter.js +2 -0
  209. package/dist/vendor/observe/exporter.js.map +1 -0
  210. package/dist/vendor/observe/file-exporter.d.ts +16 -0
  211. package/dist/vendor/observe/file-exporter.d.ts.map +1 -0
  212. package/dist/vendor/observe/file-exporter.js +39 -0
  213. package/dist/vendor/observe/file-exporter.js.map +1 -0
  214. package/dist/vendor/observe/global.d.ts +6 -0
  215. package/dist/vendor/observe/global.d.ts.map +1 -0
  216. package/dist/vendor/observe/global.js +17 -0
  217. package/dist/vendor/observe/global.js.map +1 -0
  218. package/dist/vendor/observe/index.d.ts +24 -0
  219. package/dist/vendor/observe/index.d.ts.map +1 -0
  220. package/dist/vendor/observe/index.js +24 -0
  221. package/dist/vendor/observe/index.js.map +1 -0
  222. package/dist/vendor/observe/noop.d.ts +17 -0
  223. package/dist/vendor/observe/noop.d.ts.map +1 -0
  224. package/dist/vendor/observe/noop.js +80 -0
  225. package/dist/vendor/observe/noop.js.map +1 -0
  226. package/dist/vendor/observe/privacy.d.ts +18 -0
  227. package/dist/vendor/observe/privacy.d.ts.map +1 -0
  228. package/dist/vendor/observe/privacy.js +160 -0
  229. package/dist/vendor/observe/privacy.js.map +1 -0
  230. package/dist/vendor/observe/query/simple-query-client.d.ts +11 -0
  231. package/dist/vendor/observe/query/simple-query-client.d.ts.map +1 -0
  232. package/dist/vendor/observe/query/simple-query-client.js +31 -0
  233. package/dist/vendor/observe/query/simple-query-client.js.map +1 -0
  234. package/dist/vendor/observe/replay/session-replay.d.ts +20 -0
  235. package/dist/vendor/observe/replay/session-replay.d.ts.map +1 -0
  236. package/dist/vendor/observe/replay/session-replay.js +180 -0
  237. package/dist/vendor/observe/replay/session-replay.js.map +1 -0
  238. package/dist/vendor/observe/runtime.d.ts +27 -0
  239. package/dist/vendor/observe/runtime.d.ts.map +1 -0
  240. package/dist/vendor/observe/runtime.js +177 -0
  241. package/dist/vendor/observe/runtime.js.map +1 -0
  242. package/dist/vendor/observe/server/dashboard-html.d.ts +6 -0
  243. package/dist/vendor/observe/server/dashboard-html.d.ts.map +1 -0
  244. package/dist/vendor/observe/server/dashboard-html.js +305 -0
  245. package/dist/vendor/observe/server/dashboard-html.js.map +1 -0
  246. package/dist/vendor/observe/server/http-observe-server.d.ts +62 -0
  247. package/dist/vendor/observe/server/http-observe-server.d.ts.map +1 -0
  248. package/dist/vendor/observe/server/http-observe-server.js +418 -0
  249. package/dist/vendor/observe/server/http-observe-server.js.map +1 -0
  250. package/dist/vendor/observe/server/in-memory-observe-server.d.ts +20 -0
  251. package/dist/vendor/observe/server/in-memory-observe-server.d.ts.map +1 -0
  252. package/dist/vendor/observe/server/in-memory-observe-server.js +76 -0
  253. package/dist/vendor/observe/server/in-memory-observe-server.js.map +1 -0
  254. package/dist/vendor/observe/span.d.ts +45 -0
  255. package/dist/vendor/observe/span.d.ts.map +1 -0
  256. package/dist/vendor/observe/span.js +110 -0
  257. package/dist/vendor/observe/span.js.map +1 -0
  258. package/dist/vendor/observe/store/contracts.d.ts +59 -0
  259. package/dist/vendor/observe/store/contracts.d.ts.map +1 -0
  260. package/dist/vendor/observe/store/contracts.js +2 -0
  261. package/dist/vendor/observe/store/contracts.js.map +1 -0
  262. package/dist/vendor/observe/store/file-control-plane-store.d.ts +33 -0
  263. package/dist/vendor/observe/store/file-control-plane-store.d.ts.map +1 -0
  264. package/dist/vendor/observe/store/file-control-plane-store.js +146 -0
  265. package/dist/vendor/observe/store/file-control-plane-store.js.map +1 -0
  266. package/dist/vendor/observe/store/in-memory-control-plane-store.d.ts +84 -0
  267. package/dist/vendor/observe/store/in-memory-control-plane-store.d.ts.map +1 -0
  268. package/dist/vendor/observe/store/in-memory-control-plane-store.js +447 -0
  269. package/dist/vendor/observe/store/in-memory-control-plane-store.js.map +1 -0
  270. package/dist/vendor/observe/store/index.d.ts +4 -0
  271. package/dist/vendor/observe/store/index.d.ts.map +1 -0
  272. package/dist/vendor/observe/store/index.js +4 -0
  273. package/dist/vendor/observe/store/index.js.map +1 -0
  274. package/dist/vendor/observe/types.d.ts +246 -0
  275. package/dist/vendor/observe/types.d.ts.map +1 -0
  276. package/dist/vendor/observe/types.js +152 -0
  277. package/dist/vendor/observe/types.js.map +1 -0
  278. package/dist/vendor/observe/worker/in-memory-observe-worker.d.ts +25 -0
  279. package/dist/vendor/observe/worker/in-memory-observe-worker.d.ts.map +1 -0
  280. package/dist/vendor/observe/worker/in-memory-observe-worker.js +82 -0
  281. package/dist/vendor/observe/worker/in-memory-observe-worker.js.map +1 -0
  282. package/guides/CLAUDE_CODEX_CLI_INTEGRATION_PLAN.md +321 -0
  283. package/guides/{PI_MONO_MIT_REMOVAL_GUIDE.md → INDUSAGI_MONO_MIT_REMOVAL_GUIDE.md} +29 -29
  284. package/guides/INDUSVX_OBSERVE_FULL_INTEGRATION_REPORT.md +543 -0
  285. package/guides/OBSERVE_DEEP_VALIDATION.md +252 -0
  286. package/guides/OBSERVE_LOCAL_TESTING.md +183 -0
  287. package/guides/OPTION3_PROVIDER_ADAPTER_RUNTIME_PLAN.md +370 -0
  288. package/package.json +11 -4
@@ -220,8 +220,10 @@
220
220
  import { readFileSync } from "node:fs";
221
221
  import { join } from "node:path";
222
222
  import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "indusagi/ai";
223
+ import { AgentEventObserveAdapter } from "../observe.js";
223
224
  import { getAgentDir, getDocsPath } from "../config.js";
224
225
  import { theme } from "../interfaces/terminal-ui/theme/theme.js";
226
+ import { runObserveOperation } from "./observe.js";
225
227
  import { stripFrontmatter } from "../helpers/frontmatter.js";
226
228
  import { sleep } from "../helpers/sleep.js";
227
229
  import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
@@ -333,6 +335,7 @@ export class AgentSession {
333
335
  this._customTools = config.customTools ?? [];
334
336
  this._cwd = config.cwd;
335
337
  this._modelRegistry = config.modelRegistry;
338
+ this._providerService = config.providerService;
336
339
  this._agentDir = config.agentDir ?? getAgentDir();
337
340
  this._todoStore = createSessionTodoStore({
338
341
  sessionManager: this.sessionManager,
@@ -342,6 +345,7 @@ export class AgentSession {
342
345
  this._subagentStore = new SubagentStore({ cwd: this._cwd, agentDir: this._agentDir });
343
346
  this._extensionRunnerRef = config.extensionRunnerRef;
344
347
  this._hookRunnerRef = config.hookRunnerRef;
348
+ this._observe = config.observe;
345
349
  this._initialActiveToolNames = config.initialActiveToolNames;
346
350
  this._baseToolsOverride = config.baseToolsOverride;
347
351
  this._wireAgentEventHandlers();
@@ -354,6 +358,27 @@ export class AgentSession {
354
358
  get modelRegistry() {
355
359
  return this._modelRegistry;
356
360
  }
361
+ async initializeObserve() {
362
+ if (!this._observe)
363
+ return;
364
+ await this._observe.initializeSession(this._getObserveSessionSnapshot());
365
+ if (!this._observeAgentEventsUnsubscribe) {
366
+ const adapter = new AgentEventObserveAdapter({
367
+ sink: this._observe.sink,
368
+ includeMessageText: this._observe.captureAgentEventText,
369
+ });
370
+ this._observeAgentEventsUnsubscribe = adapter.attach(this.agent);
371
+ }
372
+ await this._observe.log({
373
+ sessionId: this.sessionId,
374
+ name: "session.initialize",
375
+ summary: "observe session initialized",
376
+ attributes: {
377
+ sessionFile: this.sessionFile,
378
+ activeToolCount: this.getActiveToolNames().length,
379
+ },
380
+ });
381
+ }
357
382
  // =========================================================================
358
383
  // Event Subscription
359
384
  // =========================================================================
@@ -431,6 +456,42 @@ export class AgentSession {
431
456
  this._retryPromise = undefined;
432
457
  }
433
458
  }
459
+ _getObserveSessionSnapshot() {
460
+ return {
461
+ id: this.sessionId,
462
+ cwd: this._cwd,
463
+ sessionFile: this.sessionFile,
464
+ model: this.model ? { provider: this.model.provider, id: this.model.id } : undefined,
465
+ activeToolNames: this.getActiveToolNames(),
466
+ };
467
+ }
468
+ async _refreshObserveSession() {
469
+ await this._observe?.initializeSession(this._getObserveSessionSnapshot());
470
+ }
471
+ async _endObserveSession(sessionId, status = "ended") {
472
+ await this._observe?.endSession(sessionId, status);
473
+ }
474
+ async _logObserve(options) {
475
+ await this._observe?.log({
476
+ sessionId: this.sessionId,
477
+ ...options,
478
+ });
479
+ }
480
+ async _runObserveOperation(name, kind, fn, options = {}) {
481
+ return runObserveOperation(this._observe, {
482
+ sessionId: this.sessionId,
483
+ name,
484
+ kind,
485
+ input: options.input,
486
+ attributes: {
487
+ sessionFile: this.sessionFile,
488
+ modelId: this.model?.id,
489
+ provider: this.model?.provider,
490
+ ...options.attributes,
491
+ },
492
+ output: options.output,
493
+ }, fn);
494
+ }
434
495
  /** Extract text content from a message */
435
496
  _getUserMessageText(message) {
436
497
  if (message.role !== "user")
@@ -531,7 +592,12 @@ export class AgentSession {
531
592
  * Call this when completely done with the session.
532
593
  */
533
594
  dispose() {
595
+ if (this.model && this._providerService?.usesAdapter(this.model)) {
596
+ void this._providerService.stopCurrent(this.model).catch(() => { });
597
+ }
534
598
  this._disconnectFromAgent();
599
+ this._observeAgentEventsUnsubscribe?.();
600
+ this._observeAgentEventsUnsubscribe = undefined;
535
601
  this._eventListeners = [];
536
602
  }
537
603
  // =========================================================================
@@ -717,16 +783,18 @@ export class AgentSession {
717
783
  "Then use /model to select a model.");
718
784
  }
719
785
  // Validate API key
720
- const apiKey = await this._modelRegistry.getApiKey(this.model);
721
- if (!apiKey) {
722
- const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
723
- if (isOAuth) {
724
- throw new Error(`Authentication failed for "${this.model.provider}". ` +
725
- `Credentials may have expired or network is unavailable. ` +
726
- `Run '/login ${this.model.provider}' to re-authenticate.`);
786
+ if (this._modelRegistry.providerNeedsAuth(this.model.provider)) {
787
+ const apiKey = await this._modelRegistry.getApiKey(this.model);
788
+ if (!apiKey) {
789
+ const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
790
+ if (isOAuth) {
791
+ throw new Error(`Authentication failed for "${this.model.provider}". ` +
792
+ `Credentials may have expired or network is unavailable. ` +
793
+ `Run '/login ${this.model.provider}' to re-authenticate.`);
794
+ }
795
+ throw new Error(`No API key found for ${this.model.provider}.\n\n` +
796
+ `Use /login or set an API key environment variable. See ${join(getDocsPath(), "authentication.md")}`);
727
797
  }
728
- throw new Error(`No API key found for ${this.model.provider}.\n\n` +
729
- `Use /login or set an API key environment variable. See ${join(getDocsPath(), "authentication.md")}`);
730
798
  }
731
799
  // Check if we need to compact before sending (catches aborted responses)
732
800
  const lastAssistant = this._findLastAssistantMessage();
@@ -786,8 +854,21 @@ export class AgentSession {
786
854
  this.agent.setSystemPrompt(this._baseSystemPrompt);
787
855
  }
788
856
  }
857
+ await this._logObserve({
858
+ name: "session.prompt",
859
+ summary: "prompt submitted to agent",
860
+ attributes: {
861
+ textLength: expandedText.length,
862
+ imageCount: currentImages?.length ?? 0,
863
+ pendingNextTurnMessages: messages.length - 1,
864
+ },
865
+ data: {
866
+ imageCount: currentImages?.length ?? 0,
867
+ },
868
+ });
789
869
  await this.agent.prompt(messages);
790
870
  await this.waitForRetry();
871
+ await this._refreshObserveSession();
791
872
  }
792
873
  /**
793
874
  * Try to execute an extension command. Returns true if command was found and executed.
@@ -1033,6 +1114,9 @@ export class AgentSession {
1033
1114
  */
1034
1115
  async abort() {
1035
1116
  this.abortRetry();
1117
+ if (this.model && this._providerService?.usesAdapter(this.model)) {
1118
+ await this._providerService.interruptCurrent(this.model);
1119
+ }
1036
1120
  this.agent.abort();
1037
1121
  await this.agent.waitForIdle();
1038
1122
  }
@@ -1044,38 +1128,58 @@ export class AgentSession {
1044
1128
  * @returns true if completed, false if cancelled by extension
1045
1129
  */
1046
1130
  async newSession(options) {
1047
- const previousSessionFile = this.sessionFile;
1048
- // Emit session_before_switch event with reason "new" (can be cancelled)
1049
- if (this._extensionRunner?.hasHandlers("session_before_switch")) {
1050
- const result = (await this._extensionRunner.emit({
1051
- type: "session_before_switch",
1052
- reason: "new",
1053
- }));
1054
- if (result?.cancel) {
1055
- return false;
1131
+ return this._runObserveOperation("session.new", "session_switch", async () => {
1132
+ const previousSessionFile = this.sessionFile;
1133
+ const previousSessionId = this.sessionId;
1134
+ // Emit session_before_switch event with reason "new" (can be cancelled)
1135
+ if (this._extensionRunner?.hasHandlers("session_before_switch")) {
1136
+ const result = (await this._extensionRunner.emit({
1137
+ type: "session_before_switch",
1138
+ reason: "new",
1139
+ }));
1140
+ if (result?.cancel) {
1141
+ return false;
1142
+ }
1056
1143
  }
1057
- }
1058
- this._disconnectFromAgent();
1059
- await this.abort();
1060
- this.agent.reset();
1061
- this.sessionManager.newSession(options);
1062
- this._rebuildTodoStore();
1063
- this.agent.sessionId = this.sessionManager.getSessionId();
1064
- this._memoryThreadId = this.sessionManager.getSessionId();
1065
- this._steeringMessages = [];
1066
- this._followUpMessages = [];
1067
- this._pendingNextTurnMessages = [];
1068
- this._reconnectToAgent();
1069
- // Emit session_switch event with reason "new" to extensions
1070
- if (this._extensionRunner) {
1071
- await this._extensionRunner.emit({
1072
- type: "session_switch",
1073
- reason: "new",
1074
- previousSessionFile,
1144
+ this._disconnectFromAgent();
1145
+ await this.abort();
1146
+ if (this.model && this._providerService?.usesAdapter(this.model)) {
1147
+ await this._providerService.stopCurrent(this.model);
1148
+ }
1149
+ this.agent.reset();
1150
+ this.sessionManager.newSession(options);
1151
+ this._rebuildTodoStore();
1152
+ this.agent.sessionId = this.sessionManager.getSessionId();
1153
+ this._memoryThreadId = this.sessionManager.getSessionId();
1154
+ this._steeringMessages = [];
1155
+ this._followUpMessages = [];
1156
+ this._pendingNextTurnMessages = [];
1157
+ this._reconnectToAgent();
1158
+ await this._endObserveSession(previousSessionId);
1159
+ await this._refreshObserveSession();
1160
+ await this._logObserve({
1161
+ name: "session.new",
1162
+ summary: "created new session",
1163
+ attributes: {
1164
+ previousSessionId,
1165
+ previousSessionFile,
1166
+ },
1075
1167
  });
1076
- }
1077
- // Emit session event to custom tools
1078
- return true;
1168
+ // Emit session_switch event with reason "new" to extensions
1169
+ if (this._extensionRunner) {
1170
+ await this._extensionRunner.emit({
1171
+ type: "session_switch",
1172
+ reason: "new",
1173
+ previousSessionFile,
1174
+ });
1175
+ }
1176
+ return true;
1177
+ }, {
1178
+ attributes: {
1179
+ previousSessionFile: this.sessionFile,
1180
+ },
1181
+ output: (completed) => ({ completed }),
1182
+ });
1079
1183
  }
1080
1184
  // =========================================================================
1081
1185
  // Model Management
@@ -1098,11 +1202,16 @@ export class AgentSession {
1098
1202
  * @throws Error if no API key available for the model
1099
1203
  */
1100
1204
  async setModel(model) {
1101
- const apiKey = await this._modelRegistry.getApiKey(model);
1102
- if (!apiKey) {
1103
- throw new Error(`No API key for ${model.provider}/${model.id}`);
1205
+ if (this._modelRegistry.providerNeedsAuth(model.provider)) {
1206
+ const apiKey = await this._modelRegistry.getApiKey(model);
1207
+ if (!apiKey) {
1208
+ throw new Error(`No API key for ${model.provider}/${model.id}`);
1209
+ }
1104
1210
  }
1105
1211
  const previousModel = this.model;
1212
+ if (previousModel && !modelsAreEqual(previousModel, model) && this._providerService?.usesAdapter(previousModel)) {
1213
+ await this._providerService.stopCurrent(previousModel);
1214
+ }
1106
1215
  this.agent.setModel(model);
1107
1216
  this.sessionManager.appendModelChange(model.provider, model.id);
1108
1217
  this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
@@ -1132,10 +1241,14 @@ export class AgentSession {
1132
1241
  const len = this._scopedModels.length;
1133
1242
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1134
1243
  const next = this._scopedModels[nextIndex];
1135
- // Validate API key
1136
- const apiKey = await this._modelRegistry.getApiKey(next.model);
1137
- if (!apiKey) {
1138
- throw new Error(`No API key for ${next.model.provider}/${next.model.id}`);
1244
+ if (this._modelRegistry.providerNeedsAuth(next.model.provider)) {
1245
+ const apiKey = await this._modelRegistry.getApiKey(next.model);
1246
+ if (!apiKey) {
1247
+ throw new Error(`No API key for ${next.model.provider}/${next.model.id}`);
1248
+ }
1249
+ }
1250
+ if (currentModel && !modelsAreEqual(currentModel, next.model) && this._providerService?.usesAdapter(currentModel)) {
1251
+ await this._providerService.stopCurrent(currentModel);
1139
1252
  }
1140
1253
  // Apply model
1141
1254
  this.agent.setModel(next.model);
@@ -1157,9 +1270,14 @@ export class AgentSession {
1157
1270
  const len = availableModels.length;
1158
1271
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1159
1272
  const nextModel = availableModels[nextIndex];
1160
- const apiKey = await this._modelRegistry.getApiKey(nextModel);
1161
- if (!apiKey) {
1162
- throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
1273
+ if (this._modelRegistry.providerNeedsAuth(nextModel.provider)) {
1274
+ const apiKey = await this._modelRegistry.getApiKey(nextModel);
1275
+ if (!apiKey) {
1276
+ throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
1277
+ }
1278
+ }
1279
+ if (currentModel && !modelsAreEqual(currentModel, nextModel) && this._providerService?.usesAdapter(currentModel)) {
1280
+ await this._providerService.stopCurrent(currentModel);
1163
1281
  }
1164
1282
  this.agent.setModel(nextModel);
1165
1283
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
@@ -1631,9 +1749,11 @@ export class AgentSession {
1631
1749
  getAllTools: () => this.getAllTools(),
1632
1750
  setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
1633
1751
  setModel: async (model) => {
1634
- const key = await this.modelRegistry.getApiKey(model);
1635
- if (!key)
1636
- return false;
1752
+ if (this.modelRegistry.providerNeedsAuth(model.provider)) {
1753
+ const key = await this.modelRegistry.getApiKey(model);
1754
+ if (!key)
1755
+ return false;
1756
+ }
1637
1757
  await this.setModel(model);
1638
1758
  return true;
1639
1759
  },
@@ -1905,31 +2025,57 @@ export class AgentSession {
1905
2025
  * @param options.operations Custom BashOperations for remote execution
1906
2026
  */
1907
2027
  async executeBash(command, onChunk, options) {
1908
- this._bashAbortController = new AbortController();
1909
- // Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
1910
- const prefix = this.settingsManager.getShellCommandPrefix();
1911
- const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
1912
- const hookEnv = this._hookRunner?.hasHandlers("shell.env")
1913
- ? (await this._hookRunner.trigger("shell.env", { cwd: process.cwd() }, { env: {} })).env
1914
- : undefined;
1915
- try {
1916
- const result = options?.operations
1917
- ? await executeBashWithOperations(resolvedCommand, process.cwd(), options.operations, {
1918
- onChunk,
1919
- signal: this._bashAbortController.signal,
1920
- env: hookEnv,
1921
- })
1922
- : await executeBashCommand(resolvedCommand, {
1923
- onChunk,
1924
- signal: this._bashAbortController.signal,
1925
- env: hookEnv,
2028
+ return this._runObserveOperation("session.execute_bash", "command_execution", async () => {
2029
+ this._bashAbortController = new AbortController();
2030
+ // Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
2031
+ const prefix = this.settingsManager.getShellCommandPrefix();
2032
+ const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
2033
+ const hookEnv = this._hookRunner?.hasHandlers("shell.env")
2034
+ ? (await this._hookRunner.trigger("shell.env", { cwd: process.cwd() }, { env: {} })).env
2035
+ : undefined;
2036
+ try {
2037
+ const result = options?.operations
2038
+ ? await executeBashWithOperations(resolvedCommand, process.cwd(), options.operations, {
2039
+ onChunk,
2040
+ signal: this._bashAbortController.signal,
2041
+ env: hookEnv,
2042
+ })
2043
+ : await executeBashCommand(resolvedCommand, {
2044
+ onChunk,
2045
+ signal: this._bashAbortController.signal,
2046
+ env: hookEnv,
2047
+ });
2048
+ this.recordBashResult(command, result, options);
2049
+ await this._logObserve({
2050
+ name: "session.execute_bash",
2051
+ summary: "bash command completed",
2052
+ attributes: {
2053
+ commandLength: command.length,
2054
+ exitCode: result.exitCode,
2055
+ cancelled: result.cancelled,
2056
+ truncated: result.truncated,
2057
+ excludeFromContext: options?.excludeFromContext ?? false,
2058
+ },
1926
2059
  });
1927
- this.recordBashResult(command, result, options);
1928
- return result;
1929
- }
1930
- finally {
1931
- this._bashAbortController = undefined;
1932
- }
2060
+ return result;
2061
+ }
2062
+ finally {
2063
+ this._bashAbortController = undefined;
2064
+ }
2065
+ }, {
2066
+ input: {
2067
+ commandLength: command.length,
2068
+ excludeFromContext: options?.excludeFromContext ?? false,
2069
+ },
2070
+ attributes: {
2071
+ hasCustomOperations: !!options?.operations,
2072
+ },
2073
+ output: (result) => ({
2074
+ exitCode: result.exitCode,
2075
+ cancelled: result.cancelled,
2076
+ truncated: result.truncated,
2077
+ }),
2078
+ });
1933
2079
  }
1934
2080
  /**
1935
2081
  * Record a bash execution result in session history.
@@ -1998,56 +2144,75 @@ export class AgentSession {
1998
2144
  * @returns true if switch completed, false if cancelled by extension
1999
2145
  */
2000
2146
  async switchSession(sessionPath) {
2001
- const previousSessionFile = this.sessionManager.getSessionFile();
2002
- // Emit session_before_switch event (can be cancelled)
2003
- if (this._extensionRunner?.hasHandlers("session_before_switch")) {
2004
- const result = (await this._extensionRunner.emit({
2005
- type: "session_before_switch",
2006
- reason: "resume",
2007
- targetSessionFile: sessionPath,
2008
- }));
2009
- if (result?.cancel) {
2010
- return false;
2147
+ return this._runObserveOperation("session.switch", "session_switch", async () => {
2148
+ const previousSessionFile = this.sessionManager.getSessionFile();
2149
+ const previousSessionId = this.sessionId;
2150
+ // Emit session_before_switch event (can be cancelled)
2151
+ if (this._extensionRunner?.hasHandlers("session_before_switch")) {
2152
+ const result = (await this._extensionRunner.emit({
2153
+ type: "session_before_switch",
2154
+ reason: "resume",
2155
+ targetSessionFile: sessionPath,
2156
+ }));
2157
+ if (result?.cancel) {
2158
+ return false;
2159
+ }
2011
2160
  }
2012
- }
2013
- this._disconnectFromAgent();
2014
- await this.abort();
2015
- this._steeringMessages = [];
2016
- this._followUpMessages = [];
2017
- this._pendingNextTurnMessages = [];
2018
- // Set new session
2019
- this.sessionManager.setSessionFile(sessionPath);
2020
- this._rebuildTodoStore();
2021
- this.agent.sessionId = this.sessionManager.getSessionId();
2022
- this._memoryThreadId = this.sessionManager.getSessionId();
2023
- // Reload messages
2024
- const sessionContext = this.sessionManager.buildSessionContext();
2025
- // Emit session_switch event to extensions
2026
- if (this._extensionRunner) {
2027
- await this._extensionRunner.emit({
2028
- type: "session_switch",
2029
- reason: "resume",
2030
- previousSessionFile,
2031
- });
2032
- }
2033
- // Emit session event to custom tools
2034
- this.agent.replaceMessages(sessionContext.messages);
2035
- // Restore model if saved
2036
- if (sessionContext.model) {
2037
- const previousModel = this.model;
2038
- const availableModels = await this._modelRegistry.getAvailable();
2039
- const match = availableModels.find((m) => m.provider === sessionContext.model.provider && m.id === sessionContext.model.modelId);
2040
- if (match) {
2041
- this.agent.setModel(match);
2042
- await this._emitModelSelect(match, previousModel, "restore");
2161
+ this._disconnectFromAgent();
2162
+ await this.abort();
2163
+ if (this.model && this._providerService?.usesAdapter(this.model)) {
2164
+ await this._providerService.stopCurrent(this.model);
2043
2165
  }
2044
- }
2045
- // Restore thinking level if saved (setThinkingLevel clamps to model capabilities)
2046
- if (sessionContext.thinkingLevel) {
2047
- this.setThinkingLevel(sessionContext.thinkingLevel);
2048
- }
2049
- this._reconnectToAgent();
2050
- return true;
2166
+ this._steeringMessages = [];
2167
+ this._followUpMessages = [];
2168
+ this._pendingNextTurnMessages = [];
2169
+ // Set new session
2170
+ this.sessionManager.setSessionFile(sessionPath);
2171
+ this._rebuildTodoStore();
2172
+ this.agent.sessionId = this.sessionManager.getSessionId();
2173
+ this._memoryThreadId = this.sessionManager.getSessionId();
2174
+ // Reload messages
2175
+ const sessionContext = this.sessionManager.buildSessionContext();
2176
+ // Emit session_switch event to extensions
2177
+ if (this._extensionRunner) {
2178
+ await this._extensionRunner.emit({
2179
+ type: "session_switch",
2180
+ reason: "resume",
2181
+ previousSessionFile,
2182
+ });
2183
+ }
2184
+ this.agent.replaceMessages(sessionContext.messages);
2185
+ // Restore model if saved
2186
+ if (sessionContext.model) {
2187
+ const previousModel = this.model;
2188
+ const availableModels = await this._modelRegistry.getAvailable();
2189
+ const match = availableModels.find((m) => m.provider === sessionContext.model.provider && m.id === sessionContext.model.modelId);
2190
+ if (match) {
2191
+ this.agent.setModel(match);
2192
+ await this._emitModelSelect(match, previousModel, "restore");
2193
+ }
2194
+ }
2195
+ // Restore thinking level if saved (setThinkingLevel clamps to model capabilities)
2196
+ if (sessionContext.thinkingLevel) {
2197
+ this.setThinkingLevel(sessionContext.thinkingLevel);
2198
+ }
2199
+ this._reconnectToAgent();
2200
+ await this._endObserveSession(previousSessionId);
2201
+ await this._refreshObserveSession();
2202
+ await this._logObserve({
2203
+ name: "session.switch",
2204
+ summary: "switched session",
2205
+ attributes: {
2206
+ previousSessionId,
2207
+ previousSessionFile,
2208
+ targetSessionFile: sessionPath,
2209
+ },
2210
+ });
2211
+ return true;
2212
+ }, {
2213
+ input: { targetSessionFile: sessionPath },
2214
+ output: (completed) => ({ completed }),
2215
+ });
2051
2216
  }
2052
2217
  /**
2053
2218
  * Create a fork from a specific entry.
@@ -2059,49 +2224,66 @@ export class AgentSession {
2059
2224
  * - cancelled: True if an extension cancelled the fork
2060
2225
  */
2061
2226
  async fork(entryId) {
2062
- const previousSessionFile = this.sessionFile;
2063
- const selectedEntry = this.sessionManager.getEntry(entryId);
2064
- if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
2065
- throw new Error("Invalid entry ID for forking");
2066
- }
2067
- const selectedText = this._extractUserMessageText(selectedEntry.message.content);
2068
- let skipConversationRestore = false;
2069
- // Emit session_before_fork event (can be cancelled)
2070
- if (this._extensionRunner?.hasHandlers("session_before_fork")) {
2071
- const result = (await this._extensionRunner.emit({
2072
- type: "session_before_fork",
2073
- entryId,
2074
- }));
2075
- if (result?.cancel) {
2076
- return { selectedText, cancelled: true };
2227
+ return this._runObserveOperation("session.fork", "session_fork", async () => {
2228
+ const previousSessionFile = this.sessionFile;
2229
+ const previousSessionId = this.sessionId;
2230
+ const selectedEntry = this.sessionManager.getEntry(entryId);
2231
+ if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
2232
+ throw new Error("Invalid entry ID for forking");
2077
2233
  }
2078
- skipConversationRestore = result?.skipConversationRestore ?? false;
2079
- }
2080
- // Clear pending messages (bound to old session state)
2081
- this._pendingNextTurnMessages = [];
2082
- if (!selectedEntry.parentId) {
2083
- this.sessionManager.newSession();
2084
- }
2085
- else {
2086
- this.sessionManager.createBranchedSession(selectedEntry.parentId);
2087
- }
2088
- this.agent.sessionId = this.sessionManager.getSessionId();
2089
- this._memoryThreadId = this.sessionManager.getSessionId();
2090
- this._rebuildTodoStore();
2091
- // Reload messages from entries (works for both file and in-memory mode)
2092
- const sessionContext = this.sessionManager.buildSessionContext();
2093
- // Emit session_fork event to extensions (after fork completes)
2094
- if (this._extensionRunner) {
2095
- await this._extensionRunner.emit({
2096
- type: "session_fork",
2097
- previousSessionFile,
2234
+ const selectedText = this._extractUserMessageText(selectedEntry.message.content);
2235
+ let skipConversationRestore = false;
2236
+ // Emit session_before_fork event (can be cancelled)
2237
+ if (this._extensionRunner?.hasHandlers("session_before_fork")) {
2238
+ const result = (await this._extensionRunner.emit({
2239
+ type: "session_before_fork",
2240
+ entryId,
2241
+ }));
2242
+ if (result?.cancel) {
2243
+ return { selectedText, cancelled: true };
2244
+ }
2245
+ skipConversationRestore = result?.skipConversationRestore ?? false;
2246
+ }
2247
+ // Clear pending messages (bound to old session state)
2248
+ this._pendingNextTurnMessages = [];
2249
+ if (!selectedEntry.parentId) {
2250
+ this.sessionManager.newSession();
2251
+ }
2252
+ else {
2253
+ this.sessionManager.createBranchedSession(selectedEntry.parentId);
2254
+ }
2255
+ this.agent.sessionId = this.sessionManager.getSessionId();
2256
+ this._memoryThreadId = this.sessionManager.getSessionId();
2257
+ this._rebuildTodoStore();
2258
+ // Reload messages from entries (works for both file and in-memory mode)
2259
+ const sessionContext = this.sessionManager.buildSessionContext();
2260
+ // Emit session_fork event to extensions (after fork completes)
2261
+ if (this._extensionRunner) {
2262
+ await this._extensionRunner.emit({
2263
+ type: "session_fork",
2264
+ previousSessionFile,
2265
+ });
2266
+ }
2267
+ if (!skipConversationRestore) {
2268
+ this.agent.replaceMessages(sessionContext.messages);
2269
+ }
2270
+ await this._endObserveSession(previousSessionId);
2271
+ await this._refreshObserveSession();
2272
+ await this._logObserve({
2273
+ name: "session.fork",
2274
+ summary: "forked session branch",
2275
+ attributes: {
2276
+ entryId,
2277
+ previousSessionId,
2278
+ previousSessionFile,
2279
+ skipConversationRestore,
2280
+ },
2098
2281
  });
2099
- }
2100
- // Emit session event to custom tools (with reason "fork")
2101
- if (!skipConversationRestore) {
2102
- this.agent.replaceMessages(sessionContext.messages);
2103
- }
2104
- return { selectedText, cancelled: false };
2282
+ return { selectedText, cancelled: false };
2283
+ }, {
2284
+ input: { entryId },
2285
+ output: (result) => ({ cancelled: result.cancelled, selectedTextLength: result.selectedText.length }),
2286
+ });
2105
2287
  }
2106
2288
  // =========================================================================
2107
2289
  // Tree Navigation
@@ -2451,13 +2633,22 @@ export class AgentSession {
2451
2633
  }
2452
2634
  // Rebuild active tools to include MCP tools
2453
2635
  const currentActive = this.getActiveToolNames();
2454
- const mcpToolNames = tools.map(t => t.name);
2636
+ const mcpToolNames = tools.map((t) => t.name);
2455
2637
  this.setActiveToolsByName([...currentActive, ...mcpToolNames]);
2456
2638
  // Rebuild runtime to include MCP tools in agent
2457
2639
  this._rebuildRuntimeState({
2458
2640
  activeToolNames: [...currentActive, ...mcpToolNames],
2459
2641
  includeAllExtensionTools: true,
2460
2642
  });
2643
+ void this._refreshObserveSession();
2644
+ void this._logObserve({
2645
+ name: "mcp.register_tools",
2646
+ summary: "registered MCP tools",
2647
+ attributes: {
2648
+ toolCount: tools.length,
2649
+ toolNames: tools.map((tool) => tool.name),
2650
+ },
2651
+ });
2461
2652
  }
2462
2653
  /**
2463
2654
  * Get MCP server status.
@@ -2499,16 +2690,22 @@ export class AgentSession {
2499
2690
  * @returns Promise resolving to the number of tools loaded
2500
2691
  */
2501
2692
  async reconnectMCP(configPath) {
2502
- // First disconnect existing connections
2503
- await this.disconnectMCP();
2504
- // Load new tools
2505
- const { createMCPTools } = await import("./tooling/index.js");
2506
- const result = await createMCPTools(configPath);
2507
- // Register tools
2508
- if (result.tools.length > 0) {
2509
- this.registerMCPTools(result.tools, result.pool);
2510
- }
2511
- return result.tools.length;
2693
+ return this._runObserveOperation("mcp.reconnect", "mcp_call", async () => {
2694
+ // First disconnect existing connections
2695
+ await this.disconnectMCP();
2696
+ // Load new tools
2697
+ const { createMCPTools } = await import("./tooling/index.js");
2698
+ const result = await createMCPTools(configPath);
2699
+ // Register tools
2700
+ if (result.tools.length > 0) {
2701
+ this.registerMCPTools(result.tools, result.pool);
2702
+ }
2703
+ await this._refreshObserveSession();
2704
+ return result.tools.length;
2705
+ }, {
2706
+ input: { configPath },
2707
+ output: (toolCount) => ({ toolCount }),
2708
+ });
2512
2709
  }
2513
2710
  // =========================================================================
2514
2711
  // Memory System
@@ -2529,73 +2726,97 @@ export class AgentSession {
2529
2726
  }
2530
2727
  // Rebuild active tools to include memory tools
2531
2728
  const currentActive = this.getActiveToolNames();
2532
- const memoryToolNames = tools.map(t => t.name);
2729
+ const memoryToolNames = tools.map((t) => t.name);
2533
2730
  this.setActiveToolsByName([...currentActive, ...memoryToolNames]);
2534
2731
  // Rebuild runtime to include memory tools in agent
2535
2732
  this._rebuildRuntimeState({
2536
2733
  activeToolNames: [...currentActive, ...memoryToolNames],
2537
2734
  includeAllExtensionTools: true,
2538
2735
  });
2736
+ void this._refreshObserveSession();
2737
+ void this._logObserve({
2738
+ name: "memory.register_tools",
2739
+ summary: "registered memory tools",
2740
+ attributes: {
2741
+ toolCount: tools.length,
2742
+ toolNames: tools.map((tool) => tool.name),
2743
+ },
2744
+ });
2539
2745
  }
2540
2746
  /**
2541
2747
  * Enable memory for the current and future sessions.
2542
2748
  * Optionally persists an OpenAI API key before initializing memory.
2543
2749
  */
2544
2750
  async enableMemory(options) {
2545
- const persistSettings = options?.persistSettings ?? true;
2546
- if (this._memory) {
2547
- await this.disableMemory({ persistSettings: false });
2548
- }
2549
- if (options?.apiKey) {
2550
- this._modelRegistry.authStorage.set("openai", "default", {
2551
- type: "api_key",
2552
- key: options.apiKey,
2553
- accountName: "Default",
2751
+ await this._runObserveOperation("memory.enable", "custom", async () => {
2752
+ const persistSettings = options?.persistSettings ?? true;
2753
+ if (this._memory) {
2754
+ await this.disableMemory({ persistSettings: false });
2755
+ }
2756
+ if (options?.apiKey) {
2757
+ this._modelRegistry.authStorage.set("openai", "default", {
2758
+ type: "api_key",
2759
+ key: options.apiKey,
2760
+ accountName: "Default",
2761
+ });
2762
+ }
2763
+ const openaiKey = options?.apiKey ?? (await this._modelRegistry.getApiKeyForProvider("openai"));
2764
+ if (!openaiKey) {
2765
+ throw new Error("OpenAI API key not found. Run /memory setup <OPENAI_API_KEY> first.");
2766
+ }
2767
+ const memory = await createMemory({
2768
+ apiKey: openaiKey,
2769
+ lastMessages: this.settingsManager.getMemoryLastMessages(),
2770
+ workingMemory: this.settingsManager.getMemoryWorkingMemoryEnabled()
2771
+ ? { enabled: true, scope: "resource", template: DEFAULT_WORKING_MEMORY_TEMPLATE }
2772
+ : undefined,
2773
+ semanticRecall: this.settingsManager.getMemorySemanticRecallEnabled()
2774
+ ? { topK: 5, messageRange: 2 }
2775
+ : undefined,
2776
+ observationalMemory: this.settingsManager.getMemoryObservationalMemoryEnabled()
2777
+ ? { enabled: true, scope: "thread" }
2778
+ : undefined,
2554
2779
  });
2555
- }
2556
- const openaiKey = options?.apiKey ?? (await this._modelRegistry.getApiKeyForProvider("openai"));
2557
- if (!openaiKey) {
2558
- throw new Error("OpenAI API key not found. Run /memory setup <OPENAI_API_KEY> first.");
2559
- }
2560
- const memory = await createMemory({
2561
- apiKey: openaiKey,
2562
- lastMessages: this.settingsManager.getMemoryLastMessages(),
2563
- workingMemory: this.settingsManager.getMemoryWorkingMemoryEnabled()
2564
- ? { enabled: true, scope: "resource", template: DEFAULT_WORKING_MEMORY_TEMPLATE }
2565
- : undefined,
2566
- semanticRecall: this.settingsManager.getMemorySemanticRecallEnabled()
2567
- ? { topK: 5, messageRange: 2 }
2568
- : undefined,
2569
- observationalMemory: this.settingsManager.getMemoryObservationalMemoryEnabled()
2570
- ? { enabled: true, scope: "thread" }
2571
- : undefined,
2572
- });
2573
- const memoryTools = createMemoryTools(memory, {
2574
- threadId: this.sessionManager.getSessionId(),
2575
- resourceId: this._memoryResourceId,
2780
+ const memoryTools = createMemoryTools(memory, {
2781
+ threadId: this.sessionManager.getSessionId(),
2782
+ resourceId: this._memoryResourceId,
2783
+ });
2784
+ this.registerMemoryTools(memoryTools, memory);
2785
+ if (persistSettings) {
2786
+ this.settingsManager.setMemorySettings({ enabled: true, autoConnect: true });
2787
+ }
2788
+ await this._refreshObserveSession();
2789
+ }, {
2790
+ attributes: {
2791
+ persistSettings: options?.persistSettings ?? true,
2792
+ hasApiKeyOverride: !!options?.apiKey,
2793
+ },
2576
2794
  });
2577
- this.registerMemoryTools(memoryTools, memory);
2578
- if (persistSettings) {
2579
- this.settingsManager.setMemorySettings({ enabled: true, autoConnect: true });
2580
- }
2581
2795
  }
2582
2796
  /**
2583
2797
  * Disable memory for the current and future sessions.
2584
2798
  */
2585
2799
  async disableMemory(options) {
2586
- const persistSettings = options?.persistSettings ?? true;
2587
- const memoryToolNames = new Set(this._memoryTools.map((tool) => tool.name));
2588
- const activeWithoutMemory = this.getActiveToolNames().filter((name) => !memoryToolNames.has(name));
2589
- this._memory = undefined;
2590
- this._memoryTools = [];
2591
- this._memoryThreadId = "";
2592
- this._rebuildRuntimeState({
2593
- activeToolNames: activeWithoutMemory,
2594
- includeAllExtensionTools: true,
2800
+ await this._runObserveOperation("memory.disable", "custom", async () => {
2801
+ const persistSettings = options?.persistSettings ?? true;
2802
+ const memoryToolNames = new Set(this._memoryTools.map((tool) => tool.name));
2803
+ const activeWithoutMemory = this.getActiveToolNames().filter((name) => !memoryToolNames.has(name));
2804
+ this._memory = undefined;
2805
+ this._memoryTools = [];
2806
+ this._memoryThreadId = "";
2807
+ this._rebuildRuntimeState({
2808
+ activeToolNames: activeWithoutMemory,
2809
+ includeAllExtensionTools: true,
2810
+ });
2811
+ if (persistSettings) {
2812
+ this.settingsManager.setMemorySettings({ enabled: false, autoConnect: false });
2813
+ }
2814
+ await this._refreshObserveSession();
2815
+ }, {
2816
+ attributes: {
2817
+ persistSettings: options?.persistSettings ?? true,
2818
+ },
2595
2819
  });
2596
- if (persistSettings) {
2597
- this.settingsManager.setMemorySettings({ enabled: false, autoConnect: false });
2598
- }
2599
2820
  }
2600
2821
  /**
2601
2822
  * Get the memory instance.