indusagi-coding-agent 0.1.42 → 0.1.43

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 (220) hide show
  1. package/CHANGELOG.md +13 -2
  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/login-handler.d.ts.map +1 -1
  6. package/dist/command-line/login-handler.js +7 -0
  7. package/dist/command-line/login-handler.js.map +1 -1
  8. package/dist/dev/observe-deep.d.ts +2 -0
  9. package/dist/dev/observe-deep.d.ts.map +1 -0
  10. package/dist/dev/observe-deep.js +333 -0
  11. package/dist/dev/observe-deep.js.map +1 -0
  12. package/dist/dev/observe-smoke.d.ts +2 -0
  13. package/dist/dev/observe-smoke.d.ts.map +1 -0
  14. package/dist/dev/observe-smoke.js +264 -0
  15. package/dist/dev/observe-smoke.js.map +1 -0
  16. package/dist/interfaces/terminal-ui/components/oauth-selector.d.ts.map +1 -1
  17. package/dist/interfaces/terminal-ui/components/oauth-selector.js +1 -0
  18. package/dist/interfaces/terminal-ui/components/oauth-selector.js.map +1 -1
  19. package/dist/main.d.ts.map +1 -1
  20. package/dist/main.js +40 -18
  21. package/dist/main.js.map +1 -1
  22. package/dist/observe.d.ts +2 -0
  23. package/dist/observe.d.ts.map +1 -0
  24. package/dist/observe.js +2 -0
  25. package/dist/observe.js.map +1 -0
  26. package/dist/runtime/agent-session.d.ts +11 -0
  27. package/dist/runtime/agent-session.d.ts.map +1 -1
  28. package/dist/runtime/agent-session.js +391 -201
  29. package/dist/runtime/agent-session.js.map +1 -1
  30. package/dist/runtime/model-resolver.d.ts.map +1 -1
  31. package/dist/runtime/model-resolver.js +1 -0
  32. package/dist/runtime/model-resolver.js.map +1 -1
  33. package/dist/runtime/observe.d.ts +50 -0
  34. package/dist/runtime/observe.d.ts.map +1 -0
  35. package/dist/runtime/observe.js +233 -0
  36. package/dist/runtime/observe.js.map +1 -0
  37. package/dist/runtime/sdk.d.ts +2 -0
  38. package/dist/runtime/sdk.d.ts.map +1 -1
  39. package/dist/runtime/sdk.js +19 -6
  40. package/dist/runtime/sdk.js.map +1 -1
  41. package/dist/vendor/observe/adapters/agent-event-adapter.d.ts +28 -0
  42. package/dist/vendor/observe/adapters/agent-event-adapter.d.ts.map +1 -0
  43. package/dist/vendor/observe/adapters/agent-event-adapter.js +136 -0
  44. package/dist/vendor/observe/adapters/agent-event-adapter.js.map +1 -0
  45. package/dist/vendor/observe/adapters/ai-stream-adapter.d.ts +24 -0
  46. package/dist/vendor/observe/adapters/ai-stream-adapter.d.ts.map +1 -0
  47. package/dist/vendor/observe/adapters/ai-stream-adapter.js +118 -0
  48. package/dist/vendor/observe/adapters/ai-stream-adapter.js.map +1 -0
  49. package/dist/vendor/observe/artifacts/content-store.d.ts +12 -0
  50. package/dist/vendor/observe/artifacts/content-store.d.ts.map +1 -0
  51. package/dist/vendor/observe/artifacts/content-store.js +2 -0
  52. package/dist/vendor/observe/artifacts/content-store.js.map +1 -0
  53. package/dist/vendor/observe/artifacts/file-content-store.d.ts +16 -0
  54. package/dist/vendor/observe/artifacts/file-content-store.d.ts.map +1 -0
  55. package/dist/vendor/observe/artifacts/file-content-store.js +43 -0
  56. package/dist/vendor/observe/artifacts/file-content-store.js.map +1 -0
  57. package/dist/vendor/observe/batcher.d.ts +27 -0
  58. package/dist/vendor/observe/batcher.d.ts.map +1 -0
  59. package/dist/vendor/observe/batcher.js +66 -0
  60. package/dist/vendor/observe/batcher.js.map +1 -0
  61. package/dist/vendor/observe/client/artifacts-manager.d.ts +29 -0
  62. package/dist/vendor/observe/client/artifacts-manager.d.ts.map +1 -0
  63. package/dist/vendor/observe/client/artifacts-manager.js +120 -0
  64. package/dist/vendor/observe/client/artifacts-manager.js.map +1 -0
  65. package/dist/vendor/observe/client/datasets-manager.d.ts +16 -0
  66. package/dist/vendor/observe/client/datasets-manager.d.ts.map +1 -0
  67. package/dist/vendor/observe/client/datasets-manager.js +27 -0
  68. package/dist/vendor/observe/client/datasets-manager.js.map +1 -0
  69. package/dist/vendor/observe/client/experiments-manager.d.ts +61 -0
  70. package/dist/vendor/observe/client/experiments-manager.d.ts.map +1 -0
  71. package/dist/vendor/observe/client/experiments-manager.js +132 -0
  72. package/dist/vendor/observe/client/experiments-manager.js.map +1 -0
  73. package/dist/vendor/observe/client/index.d.ts +9 -0
  74. package/dist/vendor/observe/client/index.d.ts.map +1 -0
  75. package/dist/vendor/observe/client/index.js +9 -0
  76. package/dist/vendor/observe/client/index.js.map +1 -0
  77. package/dist/vendor/observe/client/observe-client.d.ts +30 -0
  78. package/dist/vendor/observe/client/observe-client.d.ts.map +1 -0
  79. package/dist/vendor/observe/client/observe-client.js +22 -0
  80. package/dist/vendor/observe/client/observe-client.js.map +1 -0
  81. package/dist/vendor/observe/client/prompts-manager.d.ts +17 -0
  82. package/dist/vendor/observe/client/prompts-manager.d.ts.map +1 -0
  83. package/dist/vendor/observe/client/prompts-manager.js +38 -0
  84. package/dist/vendor/observe/client/prompts-manager.js.map +1 -0
  85. package/dist/vendor/observe/client/scores-manager.d.ts +10 -0
  86. package/dist/vendor/observe/client/scores-manager.d.ts.map +1 -0
  87. package/dist/vendor/observe/client/scores-manager.js +15 -0
  88. package/dist/vendor/observe/client/scores-manager.js.map +1 -0
  89. package/dist/vendor/observe/client/sessions-manager.d.ts +15 -0
  90. package/dist/vendor/observe/client/sessions-manager.d.ts.map +1 -0
  91. package/dist/vendor/observe/client/sessions-manager.js +24 -0
  92. package/dist/vendor/observe/client/sessions-manager.js.map +1 -0
  93. package/dist/vendor/observe/client/traces-manager.d.ts +23 -0
  94. package/dist/vendor/observe/client/traces-manager.d.ts.map +1 -0
  95. package/dist/vendor/observe/client/traces-manager.js +39 -0
  96. package/dist/vendor/observe/client/traces-manager.js.map +1 -0
  97. package/dist/vendor/observe/console-exporter.d.ts +11 -0
  98. package/dist/vendor/observe/console-exporter.d.ts.map +1 -0
  99. package/dist/vendor/observe/console-exporter.js +18 -0
  100. package/dist/vendor/observe/console-exporter.js.map +1 -0
  101. package/dist/vendor/observe/context.d.ts +4 -0
  102. package/dist/vendor/observe/context.d.ts.map +1 -0
  103. package/dist/vendor/observe/context.js +9 -0
  104. package/dist/vendor/observe/context.js.map +1 -0
  105. package/dist/vendor/observe/entities/artifacts.d.ts +46 -0
  106. package/dist/vendor/observe/entities/artifacts.d.ts.map +1 -0
  107. package/dist/vendor/observe/entities/artifacts.js +2 -0
  108. package/dist/vendor/observe/entities/artifacts.js.map +1 -0
  109. package/dist/vendor/observe/entities/datasets.d.ts +37 -0
  110. package/dist/vendor/observe/entities/datasets.d.ts.map +1 -0
  111. package/dist/vendor/observe/entities/datasets.js +2 -0
  112. package/dist/vendor/observe/entities/datasets.js.map +1 -0
  113. package/dist/vendor/observe/entities/experiments.d.ts +66 -0
  114. package/dist/vendor/observe/entities/experiments.d.ts.map +1 -0
  115. package/dist/vendor/observe/entities/experiments.js +2 -0
  116. package/dist/vendor/observe/entities/experiments.js.map +1 -0
  117. package/dist/vendor/observe/entities/index.d.ts +9 -0
  118. package/dist/vendor/observe/entities/index.d.ts.map +1 -0
  119. package/dist/vendor/observe/entities/index.js +9 -0
  120. package/dist/vendor/observe/entities/index.js.map +1 -0
  121. package/dist/vendor/observe/entities/prompts.d.ts +25 -0
  122. package/dist/vendor/observe/entities/prompts.d.ts.map +1 -0
  123. package/dist/vendor/observe/entities/prompts.js +2 -0
  124. package/dist/vendor/observe/entities/prompts.js.map +1 -0
  125. package/dist/vendor/observe/entities/scores.d.ts +28 -0
  126. package/dist/vendor/observe/entities/scores.d.ts.map +1 -0
  127. package/dist/vendor/observe/entities/scores.js +2 -0
  128. package/dist/vendor/observe/entities/scores.js.map +1 -0
  129. package/dist/vendor/observe/entities/sessions.d.ts +36 -0
  130. package/dist/vendor/observe/entities/sessions.d.ts.map +1 -0
  131. package/dist/vendor/observe/entities/sessions.js +2 -0
  132. package/dist/vendor/observe/entities/sessions.js.map +1 -0
  133. package/dist/vendor/observe/entities/shared.d.ts +26 -0
  134. package/dist/vendor/observe/entities/shared.d.ts.map +1 -0
  135. package/dist/vendor/observe/entities/shared.js +2 -0
  136. package/dist/vendor/observe/entities/shared.js.map +1 -0
  137. package/dist/vendor/observe/entities/traces.d.ts +59 -0
  138. package/dist/vendor/observe/entities/traces.d.ts.map +1 -0
  139. package/dist/vendor/observe/entities/traces.js +2 -0
  140. package/dist/vendor/observe/entities/traces.js.map +1 -0
  141. package/dist/vendor/observe/exporter.d.ts +2 -0
  142. package/dist/vendor/observe/exporter.d.ts.map +1 -0
  143. package/dist/vendor/observe/exporter.js +2 -0
  144. package/dist/vendor/observe/exporter.js.map +1 -0
  145. package/dist/vendor/observe/file-exporter.d.ts +16 -0
  146. package/dist/vendor/observe/file-exporter.d.ts.map +1 -0
  147. package/dist/vendor/observe/file-exporter.js +39 -0
  148. package/dist/vendor/observe/file-exporter.js.map +1 -0
  149. package/dist/vendor/observe/global.d.ts +6 -0
  150. package/dist/vendor/observe/global.d.ts.map +1 -0
  151. package/dist/vendor/observe/global.js +17 -0
  152. package/dist/vendor/observe/global.js.map +1 -0
  153. package/dist/vendor/observe/index.d.ts +24 -0
  154. package/dist/vendor/observe/index.d.ts.map +1 -0
  155. package/dist/vendor/observe/index.js +24 -0
  156. package/dist/vendor/observe/index.js.map +1 -0
  157. package/dist/vendor/observe/noop.d.ts +17 -0
  158. package/dist/vendor/observe/noop.d.ts.map +1 -0
  159. package/dist/vendor/observe/noop.js +80 -0
  160. package/dist/vendor/observe/noop.js.map +1 -0
  161. package/dist/vendor/observe/privacy.d.ts +18 -0
  162. package/dist/vendor/observe/privacy.d.ts.map +1 -0
  163. package/dist/vendor/observe/privacy.js +160 -0
  164. package/dist/vendor/observe/privacy.js.map +1 -0
  165. package/dist/vendor/observe/query/simple-query-client.d.ts +11 -0
  166. package/dist/vendor/observe/query/simple-query-client.d.ts.map +1 -0
  167. package/dist/vendor/observe/query/simple-query-client.js +31 -0
  168. package/dist/vendor/observe/query/simple-query-client.js.map +1 -0
  169. package/dist/vendor/observe/replay/session-replay.d.ts +20 -0
  170. package/dist/vendor/observe/replay/session-replay.d.ts.map +1 -0
  171. package/dist/vendor/observe/replay/session-replay.js +180 -0
  172. package/dist/vendor/observe/replay/session-replay.js.map +1 -0
  173. package/dist/vendor/observe/runtime.d.ts +27 -0
  174. package/dist/vendor/observe/runtime.d.ts.map +1 -0
  175. package/dist/vendor/observe/runtime.js +177 -0
  176. package/dist/vendor/observe/runtime.js.map +1 -0
  177. package/dist/vendor/observe/server/dashboard-html.d.ts +6 -0
  178. package/dist/vendor/observe/server/dashboard-html.d.ts.map +1 -0
  179. package/dist/vendor/observe/server/dashboard-html.js +305 -0
  180. package/dist/vendor/observe/server/dashboard-html.js.map +1 -0
  181. package/dist/vendor/observe/server/http-observe-server.d.ts +62 -0
  182. package/dist/vendor/observe/server/http-observe-server.d.ts.map +1 -0
  183. package/dist/vendor/observe/server/http-observe-server.js +418 -0
  184. package/dist/vendor/observe/server/http-observe-server.js.map +1 -0
  185. package/dist/vendor/observe/server/in-memory-observe-server.d.ts +20 -0
  186. package/dist/vendor/observe/server/in-memory-observe-server.d.ts.map +1 -0
  187. package/dist/vendor/observe/server/in-memory-observe-server.js +76 -0
  188. package/dist/vendor/observe/server/in-memory-observe-server.js.map +1 -0
  189. package/dist/vendor/observe/span.d.ts +45 -0
  190. package/dist/vendor/observe/span.d.ts.map +1 -0
  191. package/dist/vendor/observe/span.js +110 -0
  192. package/dist/vendor/observe/span.js.map +1 -0
  193. package/dist/vendor/observe/store/contracts.d.ts +59 -0
  194. package/dist/vendor/observe/store/contracts.d.ts.map +1 -0
  195. package/dist/vendor/observe/store/contracts.js +2 -0
  196. package/dist/vendor/observe/store/contracts.js.map +1 -0
  197. package/dist/vendor/observe/store/file-control-plane-store.d.ts +33 -0
  198. package/dist/vendor/observe/store/file-control-plane-store.d.ts.map +1 -0
  199. package/dist/vendor/observe/store/file-control-plane-store.js +146 -0
  200. package/dist/vendor/observe/store/file-control-plane-store.js.map +1 -0
  201. package/dist/vendor/observe/store/in-memory-control-plane-store.d.ts +84 -0
  202. package/dist/vendor/observe/store/in-memory-control-plane-store.d.ts.map +1 -0
  203. package/dist/vendor/observe/store/in-memory-control-plane-store.js +447 -0
  204. package/dist/vendor/observe/store/in-memory-control-plane-store.js.map +1 -0
  205. package/dist/vendor/observe/store/index.d.ts +4 -0
  206. package/dist/vendor/observe/store/index.d.ts.map +1 -0
  207. package/dist/vendor/observe/store/index.js +4 -0
  208. package/dist/vendor/observe/store/index.js.map +1 -0
  209. package/dist/vendor/observe/types.d.ts +246 -0
  210. package/dist/vendor/observe/types.d.ts.map +1 -0
  211. package/dist/vendor/observe/types.js +152 -0
  212. package/dist/vendor/observe/types.js.map +1 -0
  213. package/dist/vendor/observe/worker/in-memory-observe-worker.d.ts +25 -0
  214. package/dist/vendor/observe/worker/in-memory-observe-worker.d.ts.map +1 -0
  215. package/dist/vendor/observe/worker/in-memory-observe-worker.js +82 -0
  216. package/dist/vendor/observe/worker/in-memory-observe-worker.js.map +1 -0
  217. package/guides/INDUSVX_OBSERVE_FULL_INTEGRATION_REPORT.md +543 -0
  218. package/guides/OBSERVE_DEEP_VALIDATION.md +252 -0
  219. package/guides/OBSERVE_LOCAL_TESTING.md +183 -0
  220. 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";
@@ -342,6 +344,7 @@ export class AgentSession {
342
344
  this._subagentStore = new SubagentStore({ cwd: this._cwd, agentDir: this._agentDir });
343
345
  this._extensionRunnerRef = config.extensionRunnerRef;
344
346
  this._hookRunnerRef = config.hookRunnerRef;
347
+ this._observe = config.observe;
345
348
  this._initialActiveToolNames = config.initialActiveToolNames;
346
349
  this._baseToolsOverride = config.baseToolsOverride;
347
350
  this._wireAgentEventHandlers();
@@ -354,6 +357,27 @@ export class AgentSession {
354
357
  get modelRegistry() {
355
358
  return this._modelRegistry;
356
359
  }
360
+ async initializeObserve() {
361
+ if (!this._observe)
362
+ return;
363
+ await this._observe.initializeSession(this._getObserveSessionSnapshot());
364
+ if (!this._observeAgentEventsUnsubscribe) {
365
+ const adapter = new AgentEventObserveAdapter({
366
+ sink: this._observe.sink,
367
+ includeMessageText: this._observe.captureAgentEventText,
368
+ });
369
+ this._observeAgentEventsUnsubscribe = adapter.attach(this.agent);
370
+ }
371
+ await this._observe.log({
372
+ sessionId: this.sessionId,
373
+ name: "session.initialize",
374
+ summary: "observe session initialized",
375
+ attributes: {
376
+ sessionFile: this.sessionFile,
377
+ activeToolCount: this.getActiveToolNames().length,
378
+ },
379
+ });
380
+ }
357
381
  // =========================================================================
358
382
  // Event Subscription
359
383
  // =========================================================================
@@ -431,6 +455,42 @@ export class AgentSession {
431
455
  this._retryPromise = undefined;
432
456
  }
433
457
  }
458
+ _getObserveSessionSnapshot() {
459
+ return {
460
+ id: this.sessionId,
461
+ cwd: this._cwd,
462
+ sessionFile: this.sessionFile,
463
+ model: this.model ? { provider: this.model.provider, id: this.model.id } : undefined,
464
+ activeToolNames: this.getActiveToolNames(),
465
+ };
466
+ }
467
+ async _refreshObserveSession() {
468
+ await this._observe?.initializeSession(this._getObserveSessionSnapshot());
469
+ }
470
+ async _endObserveSession(sessionId, status = "ended") {
471
+ await this._observe?.endSession(sessionId, status);
472
+ }
473
+ async _logObserve(options) {
474
+ await this._observe?.log({
475
+ sessionId: this.sessionId,
476
+ ...options,
477
+ });
478
+ }
479
+ async _runObserveOperation(name, kind, fn, options = {}) {
480
+ return runObserveOperation(this._observe, {
481
+ sessionId: this.sessionId,
482
+ name,
483
+ kind,
484
+ input: options.input,
485
+ attributes: {
486
+ sessionFile: this.sessionFile,
487
+ modelId: this.model?.id,
488
+ provider: this.model?.provider,
489
+ ...options.attributes,
490
+ },
491
+ output: options.output,
492
+ }, fn);
493
+ }
434
494
  /** Extract text content from a message */
435
495
  _getUserMessageText(message) {
436
496
  if (message.role !== "user")
@@ -532,6 +592,8 @@ export class AgentSession {
532
592
  */
533
593
  dispose() {
534
594
  this._disconnectFromAgent();
595
+ this._observeAgentEventsUnsubscribe?.();
596
+ this._observeAgentEventsUnsubscribe = undefined;
535
597
  this._eventListeners = [];
536
598
  }
537
599
  // =========================================================================
@@ -786,8 +848,21 @@ export class AgentSession {
786
848
  this.agent.setSystemPrompt(this._baseSystemPrompt);
787
849
  }
788
850
  }
851
+ await this._logObserve({
852
+ name: "session.prompt",
853
+ summary: "prompt submitted to agent",
854
+ attributes: {
855
+ textLength: expandedText.length,
856
+ imageCount: currentImages?.length ?? 0,
857
+ pendingNextTurnMessages: messages.length - 1,
858
+ },
859
+ data: {
860
+ imageCount: currentImages?.length ?? 0,
861
+ },
862
+ });
789
863
  await this.agent.prompt(messages);
790
864
  await this.waitForRetry();
865
+ await this._refreshObserveSession();
791
866
  }
792
867
  /**
793
868
  * Try to execute an extension command. Returns true if command was found and executed.
@@ -1044,38 +1119,55 @@ export class AgentSession {
1044
1119
  * @returns true if completed, false if cancelled by extension
1045
1120
  */
1046
1121
  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;
1122
+ return this._runObserveOperation("session.new", "session_switch", async () => {
1123
+ const previousSessionFile = this.sessionFile;
1124
+ const previousSessionId = this.sessionId;
1125
+ // Emit session_before_switch event with reason "new" (can be cancelled)
1126
+ if (this._extensionRunner?.hasHandlers("session_before_switch")) {
1127
+ const result = (await this._extensionRunner.emit({
1128
+ type: "session_before_switch",
1129
+ reason: "new",
1130
+ }));
1131
+ if (result?.cancel) {
1132
+ return false;
1133
+ }
1056
1134
  }
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,
1135
+ this._disconnectFromAgent();
1136
+ await this.abort();
1137
+ this.agent.reset();
1138
+ this.sessionManager.newSession(options);
1139
+ this._rebuildTodoStore();
1140
+ this.agent.sessionId = this.sessionManager.getSessionId();
1141
+ this._memoryThreadId = this.sessionManager.getSessionId();
1142
+ this._steeringMessages = [];
1143
+ this._followUpMessages = [];
1144
+ this._pendingNextTurnMessages = [];
1145
+ this._reconnectToAgent();
1146
+ await this._endObserveSession(previousSessionId);
1147
+ await this._refreshObserveSession();
1148
+ await this._logObserve({
1149
+ name: "session.new",
1150
+ summary: "created new session",
1151
+ attributes: {
1152
+ previousSessionId,
1153
+ previousSessionFile,
1154
+ },
1075
1155
  });
1076
- }
1077
- // Emit session event to custom tools
1078
- return true;
1156
+ // Emit session_switch event with reason "new" to extensions
1157
+ if (this._extensionRunner) {
1158
+ await this._extensionRunner.emit({
1159
+ type: "session_switch",
1160
+ reason: "new",
1161
+ previousSessionFile,
1162
+ });
1163
+ }
1164
+ return true;
1165
+ }, {
1166
+ attributes: {
1167
+ previousSessionFile: this.sessionFile,
1168
+ },
1169
+ output: (completed) => ({ completed }),
1170
+ });
1079
1171
  }
1080
1172
  // =========================================================================
1081
1173
  // Model Management
@@ -1905,31 +1997,57 @@ export class AgentSession {
1905
1997
  * @param options.operations Custom BashOperations for remote execution
1906
1998
  */
1907
1999
  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,
2000
+ return this._runObserveOperation("session.execute_bash", "command_execution", async () => {
2001
+ this._bashAbortController = new AbortController();
2002
+ // Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
2003
+ const prefix = this.settingsManager.getShellCommandPrefix();
2004
+ const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
2005
+ const hookEnv = this._hookRunner?.hasHandlers("shell.env")
2006
+ ? (await this._hookRunner.trigger("shell.env", { cwd: process.cwd() }, { env: {} })).env
2007
+ : undefined;
2008
+ try {
2009
+ const result = options?.operations
2010
+ ? await executeBashWithOperations(resolvedCommand, process.cwd(), options.operations, {
2011
+ onChunk,
2012
+ signal: this._bashAbortController.signal,
2013
+ env: hookEnv,
2014
+ })
2015
+ : await executeBashCommand(resolvedCommand, {
2016
+ onChunk,
2017
+ signal: this._bashAbortController.signal,
2018
+ env: hookEnv,
2019
+ });
2020
+ this.recordBashResult(command, result, options);
2021
+ await this._logObserve({
2022
+ name: "session.execute_bash",
2023
+ summary: "bash command completed",
2024
+ attributes: {
2025
+ commandLength: command.length,
2026
+ exitCode: result.exitCode,
2027
+ cancelled: result.cancelled,
2028
+ truncated: result.truncated,
2029
+ excludeFromContext: options?.excludeFromContext ?? false,
2030
+ },
1926
2031
  });
1927
- this.recordBashResult(command, result, options);
1928
- return result;
1929
- }
1930
- finally {
1931
- this._bashAbortController = undefined;
1932
- }
2032
+ return result;
2033
+ }
2034
+ finally {
2035
+ this._bashAbortController = undefined;
2036
+ }
2037
+ }, {
2038
+ input: {
2039
+ commandLength: command.length,
2040
+ excludeFromContext: options?.excludeFromContext ?? false,
2041
+ },
2042
+ attributes: {
2043
+ hasCustomOperations: !!options?.operations,
2044
+ },
2045
+ output: (result) => ({
2046
+ exitCode: result.exitCode,
2047
+ cancelled: result.cancelled,
2048
+ truncated: result.truncated,
2049
+ }),
2050
+ });
1933
2051
  }
1934
2052
  /**
1935
2053
  * Record a bash execution result in session history.
@@ -1998,56 +2116,72 @@ export class AgentSession {
1998
2116
  * @returns true if switch completed, false if cancelled by extension
1999
2117
  */
2000
2118
  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;
2119
+ return this._runObserveOperation("session.switch", "session_switch", async () => {
2120
+ const previousSessionFile = this.sessionManager.getSessionFile();
2121
+ const previousSessionId = this.sessionId;
2122
+ // Emit session_before_switch event (can be cancelled)
2123
+ if (this._extensionRunner?.hasHandlers("session_before_switch")) {
2124
+ const result = (await this._extensionRunner.emit({
2125
+ type: "session_before_switch",
2126
+ reason: "resume",
2127
+ targetSessionFile: sessionPath,
2128
+ }));
2129
+ if (result?.cancel) {
2130
+ return false;
2131
+ }
2011
2132
  }
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");
2133
+ this._disconnectFromAgent();
2134
+ await this.abort();
2135
+ this._steeringMessages = [];
2136
+ this._followUpMessages = [];
2137
+ this._pendingNextTurnMessages = [];
2138
+ // Set new session
2139
+ this.sessionManager.setSessionFile(sessionPath);
2140
+ this._rebuildTodoStore();
2141
+ this.agent.sessionId = this.sessionManager.getSessionId();
2142
+ this._memoryThreadId = this.sessionManager.getSessionId();
2143
+ // Reload messages
2144
+ const sessionContext = this.sessionManager.buildSessionContext();
2145
+ // Emit session_switch event to extensions
2146
+ if (this._extensionRunner) {
2147
+ await this._extensionRunner.emit({
2148
+ type: "session_switch",
2149
+ reason: "resume",
2150
+ previousSessionFile,
2151
+ });
2043
2152
  }
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;
2153
+ this.agent.replaceMessages(sessionContext.messages);
2154
+ // Restore model if saved
2155
+ if (sessionContext.model) {
2156
+ const previousModel = this.model;
2157
+ const availableModels = await this._modelRegistry.getAvailable();
2158
+ const match = availableModels.find((m) => m.provider === sessionContext.model.provider && m.id === sessionContext.model.modelId);
2159
+ if (match) {
2160
+ this.agent.setModel(match);
2161
+ await this._emitModelSelect(match, previousModel, "restore");
2162
+ }
2163
+ }
2164
+ // Restore thinking level if saved (setThinkingLevel clamps to model capabilities)
2165
+ if (sessionContext.thinkingLevel) {
2166
+ this.setThinkingLevel(sessionContext.thinkingLevel);
2167
+ }
2168
+ this._reconnectToAgent();
2169
+ await this._endObserveSession(previousSessionId);
2170
+ await this._refreshObserveSession();
2171
+ await this._logObserve({
2172
+ name: "session.switch",
2173
+ summary: "switched session",
2174
+ attributes: {
2175
+ previousSessionId,
2176
+ previousSessionFile,
2177
+ targetSessionFile: sessionPath,
2178
+ },
2179
+ });
2180
+ return true;
2181
+ }, {
2182
+ input: { targetSessionFile: sessionPath },
2183
+ output: (completed) => ({ completed }),
2184
+ });
2051
2185
  }
2052
2186
  /**
2053
2187
  * Create a fork from a specific entry.
@@ -2059,49 +2193,66 @@ export class AgentSession {
2059
2193
  * - cancelled: True if an extension cancelled the fork
2060
2194
  */
2061
2195
  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 };
2196
+ return this._runObserveOperation("session.fork", "session_fork", async () => {
2197
+ const previousSessionFile = this.sessionFile;
2198
+ const previousSessionId = this.sessionId;
2199
+ const selectedEntry = this.sessionManager.getEntry(entryId);
2200
+ if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
2201
+ throw new Error("Invalid entry ID for forking");
2077
2202
  }
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,
2203
+ const selectedText = this._extractUserMessageText(selectedEntry.message.content);
2204
+ let skipConversationRestore = false;
2205
+ // Emit session_before_fork event (can be cancelled)
2206
+ if (this._extensionRunner?.hasHandlers("session_before_fork")) {
2207
+ const result = (await this._extensionRunner.emit({
2208
+ type: "session_before_fork",
2209
+ entryId,
2210
+ }));
2211
+ if (result?.cancel) {
2212
+ return { selectedText, cancelled: true };
2213
+ }
2214
+ skipConversationRestore = result?.skipConversationRestore ?? false;
2215
+ }
2216
+ // Clear pending messages (bound to old session state)
2217
+ this._pendingNextTurnMessages = [];
2218
+ if (!selectedEntry.parentId) {
2219
+ this.sessionManager.newSession();
2220
+ }
2221
+ else {
2222
+ this.sessionManager.createBranchedSession(selectedEntry.parentId);
2223
+ }
2224
+ this.agent.sessionId = this.sessionManager.getSessionId();
2225
+ this._memoryThreadId = this.sessionManager.getSessionId();
2226
+ this._rebuildTodoStore();
2227
+ // Reload messages from entries (works for both file and in-memory mode)
2228
+ const sessionContext = this.sessionManager.buildSessionContext();
2229
+ // Emit session_fork event to extensions (after fork completes)
2230
+ if (this._extensionRunner) {
2231
+ await this._extensionRunner.emit({
2232
+ type: "session_fork",
2233
+ previousSessionFile,
2234
+ });
2235
+ }
2236
+ if (!skipConversationRestore) {
2237
+ this.agent.replaceMessages(sessionContext.messages);
2238
+ }
2239
+ await this._endObserveSession(previousSessionId);
2240
+ await this._refreshObserveSession();
2241
+ await this._logObserve({
2242
+ name: "session.fork",
2243
+ summary: "forked session branch",
2244
+ attributes: {
2245
+ entryId,
2246
+ previousSessionId,
2247
+ previousSessionFile,
2248
+ skipConversationRestore,
2249
+ },
2098
2250
  });
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 };
2251
+ return { selectedText, cancelled: false };
2252
+ }, {
2253
+ input: { entryId },
2254
+ output: (result) => ({ cancelled: result.cancelled, selectedTextLength: result.selectedText.length }),
2255
+ });
2105
2256
  }
2106
2257
  // =========================================================================
2107
2258
  // Tree Navigation
@@ -2451,13 +2602,22 @@ export class AgentSession {
2451
2602
  }
2452
2603
  // Rebuild active tools to include MCP tools
2453
2604
  const currentActive = this.getActiveToolNames();
2454
- const mcpToolNames = tools.map(t => t.name);
2605
+ const mcpToolNames = tools.map((t) => t.name);
2455
2606
  this.setActiveToolsByName([...currentActive, ...mcpToolNames]);
2456
2607
  // Rebuild runtime to include MCP tools in agent
2457
2608
  this._rebuildRuntimeState({
2458
2609
  activeToolNames: [...currentActive, ...mcpToolNames],
2459
2610
  includeAllExtensionTools: true,
2460
2611
  });
2612
+ void this._refreshObserveSession();
2613
+ void this._logObserve({
2614
+ name: "mcp.register_tools",
2615
+ summary: "registered MCP tools",
2616
+ attributes: {
2617
+ toolCount: tools.length,
2618
+ toolNames: tools.map((tool) => tool.name),
2619
+ },
2620
+ });
2461
2621
  }
2462
2622
  /**
2463
2623
  * Get MCP server status.
@@ -2499,16 +2659,22 @@ export class AgentSession {
2499
2659
  * @returns Promise resolving to the number of tools loaded
2500
2660
  */
2501
2661
  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;
2662
+ return this._runObserveOperation("mcp.reconnect", "mcp_call", async () => {
2663
+ // First disconnect existing connections
2664
+ await this.disconnectMCP();
2665
+ // Load new tools
2666
+ const { createMCPTools } = await import("./tooling/index.js");
2667
+ const result = await createMCPTools(configPath);
2668
+ // Register tools
2669
+ if (result.tools.length > 0) {
2670
+ this.registerMCPTools(result.tools, result.pool);
2671
+ }
2672
+ await this._refreshObserveSession();
2673
+ return result.tools.length;
2674
+ }, {
2675
+ input: { configPath },
2676
+ output: (toolCount) => ({ toolCount }),
2677
+ });
2512
2678
  }
2513
2679
  // =========================================================================
2514
2680
  // Memory System
@@ -2529,73 +2695,97 @@ export class AgentSession {
2529
2695
  }
2530
2696
  // Rebuild active tools to include memory tools
2531
2697
  const currentActive = this.getActiveToolNames();
2532
- const memoryToolNames = tools.map(t => t.name);
2698
+ const memoryToolNames = tools.map((t) => t.name);
2533
2699
  this.setActiveToolsByName([...currentActive, ...memoryToolNames]);
2534
2700
  // Rebuild runtime to include memory tools in agent
2535
2701
  this._rebuildRuntimeState({
2536
2702
  activeToolNames: [...currentActive, ...memoryToolNames],
2537
2703
  includeAllExtensionTools: true,
2538
2704
  });
2705
+ void this._refreshObserveSession();
2706
+ void this._logObserve({
2707
+ name: "memory.register_tools",
2708
+ summary: "registered memory tools",
2709
+ attributes: {
2710
+ toolCount: tools.length,
2711
+ toolNames: tools.map((tool) => tool.name),
2712
+ },
2713
+ });
2539
2714
  }
2540
2715
  /**
2541
2716
  * Enable memory for the current and future sessions.
2542
2717
  * Optionally persists an OpenAI API key before initializing memory.
2543
2718
  */
2544
2719
  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",
2720
+ await this._runObserveOperation("memory.enable", "custom", async () => {
2721
+ const persistSettings = options?.persistSettings ?? true;
2722
+ if (this._memory) {
2723
+ await this.disableMemory({ persistSettings: false });
2724
+ }
2725
+ if (options?.apiKey) {
2726
+ this._modelRegistry.authStorage.set("openai", "default", {
2727
+ type: "api_key",
2728
+ key: options.apiKey,
2729
+ accountName: "Default",
2730
+ });
2731
+ }
2732
+ const openaiKey = options?.apiKey ?? (await this._modelRegistry.getApiKeyForProvider("openai"));
2733
+ if (!openaiKey) {
2734
+ throw new Error("OpenAI API key not found. Run /memory setup <OPENAI_API_KEY> first.");
2735
+ }
2736
+ const memory = await createMemory({
2737
+ apiKey: openaiKey,
2738
+ lastMessages: this.settingsManager.getMemoryLastMessages(),
2739
+ workingMemory: this.settingsManager.getMemoryWorkingMemoryEnabled()
2740
+ ? { enabled: true, scope: "resource", template: DEFAULT_WORKING_MEMORY_TEMPLATE }
2741
+ : undefined,
2742
+ semanticRecall: this.settingsManager.getMemorySemanticRecallEnabled()
2743
+ ? { topK: 5, messageRange: 2 }
2744
+ : undefined,
2745
+ observationalMemory: this.settingsManager.getMemoryObservationalMemoryEnabled()
2746
+ ? { enabled: true, scope: "thread" }
2747
+ : undefined,
2554
2748
  });
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,
2749
+ const memoryTools = createMemoryTools(memory, {
2750
+ threadId: this.sessionManager.getSessionId(),
2751
+ resourceId: this._memoryResourceId,
2752
+ });
2753
+ this.registerMemoryTools(memoryTools, memory);
2754
+ if (persistSettings) {
2755
+ this.settingsManager.setMemorySettings({ enabled: true, autoConnect: true });
2756
+ }
2757
+ await this._refreshObserveSession();
2758
+ }, {
2759
+ attributes: {
2760
+ persistSettings: options?.persistSettings ?? true,
2761
+ hasApiKeyOverride: !!options?.apiKey,
2762
+ },
2576
2763
  });
2577
- this.registerMemoryTools(memoryTools, memory);
2578
- if (persistSettings) {
2579
- this.settingsManager.setMemorySettings({ enabled: true, autoConnect: true });
2580
- }
2581
2764
  }
2582
2765
  /**
2583
2766
  * Disable memory for the current and future sessions.
2584
2767
  */
2585
2768
  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,
2769
+ await this._runObserveOperation("memory.disable", "custom", async () => {
2770
+ const persistSettings = options?.persistSettings ?? true;
2771
+ const memoryToolNames = new Set(this._memoryTools.map((tool) => tool.name));
2772
+ const activeWithoutMemory = this.getActiveToolNames().filter((name) => !memoryToolNames.has(name));
2773
+ this._memory = undefined;
2774
+ this._memoryTools = [];
2775
+ this._memoryThreadId = "";
2776
+ this._rebuildRuntimeState({
2777
+ activeToolNames: activeWithoutMemory,
2778
+ includeAllExtensionTools: true,
2779
+ });
2780
+ if (persistSettings) {
2781
+ this.settingsManager.setMemorySettings({ enabled: false, autoConnect: false });
2782
+ }
2783
+ await this._refreshObserveSession();
2784
+ }, {
2785
+ attributes: {
2786
+ persistSettings: options?.persistSettings ?? true,
2787
+ },
2595
2788
  });
2596
- if (persistSettings) {
2597
- this.settingsManager.setMemorySettings({ enabled: false, autoConnect: false });
2598
- }
2599
2789
  }
2600
2790
  /**
2601
2791
  * Get the memory instance.