agent-world 0.12.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +105 -17
  2. package/dist/cli/commands.d.ts +7 -1
  3. package/dist/cli/commands.js +27 -10
  4. package/dist/cli/hitl.d.ts +9 -2
  5. package/dist/cli/hitl.js +61 -20
  6. package/dist/cli/index.js +250 -96
  7. package/dist/cli/system-events.d.ts +27 -0
  8. package/dist/cli/system-events.js +63 -0
  9. package/dist/core/activity-tracker.d.ts +38 -2
  10. package/dist/core/activity-tracker.d.ts.map +1 -1
  11. package/dist/core/activity-tracker.js +62 -9
  12. package/dist/core/activity-tracker.js.map +1 -1
  13. package/dist/core/anthropic-direct.d.ts +2 -0
  14. package/dist/core/anthropic-direct.d.ts.map +1 -1
  15. package/dist/core/anthropic-direct.js +43 -1
  16. package/dist/core/anthropic-direct.js.map +1 -1
  17. package/dist/core/chat-constants.d.ts +12 -0
  18. package/dist/core/chat-constants.d.ts.map +1 -1
  19. package/dist/core/chat-constants.js +5 -0
  20. package/dist/core/chat-constants.js.map +1 -1
  21. package/dist/core/create-agent-tool.d.ts +28 -25
  22. package/dist/core/create-agent-tool.d.ts.map +1 -1
  23. package/dist/core/create-agent-tool.js +264 -141
  24. package/dist/core/create-agent-tool.js.map +1 -1
  25. package/dist/core/events/index.d.ts +5 -2
  26. package/dist/core/events/index.d.ts.map +1 -1
  27. package/dist/core/events/index.js +5 -2
  28. package/dist/core/events/index.js.map +1 -1
  29. package/dist/core/events/memory-manager.d.ts +26 -1
  30. package/dist/core/events/memory-manager.d.ts.map +1 -1
  31. package/dist/core/events/memory-manager.js +877 -72
  32. package/dist/core/events/memory-manager.js.map +1 -1
  33. package/dist/core/events/orchestrator.d.ts +8 -0
  34. package/dist/core/events/orchestrator.d.ts.map +1 -1
  35. package/dist/core/events/orchestrator.js +214 -38
  36. package/dist/core/events/orchestrator.js.map +1 -1
  37. package/dist/core/events/persistence.d.ts +21 -14
  38. package/dist/core/events/persistence.d.ts.map +1 -1
  39. package/dist/core/events/persistence.js +100 -61
  40. package/dist/core/events/persistence.js.map +1 -1
  41. package/dist/core/events/publishers.d.ts +13 -16
  42. package/dist/core/events/publishers.d.ts.map +1 -1
  43. package/dist/core/events/publishers.js +54 -55
  44. package/dist/core/events/publishers.js.map +1 -1
  45. package/dist/core/events/subscribers.d.ts +17 -14
  46. package/dist/core/events/subscribers.d.ts.map +1 -1
  47. package/dist/core/events/subscribers.js +68 -147
  48. package/dist/core/events/subscribers.js.map +1 -1
  49. package/dist/core/events/title-scheduler.d.ts +27 -0
  50. package/dist/core/events/title-scheduler.d.ts.map +1 -0
  51. package/dist/core/events/title-scheduler.js +135 -0
  52. package/dist/core/events/title-scheduler.js.map +1 -0
  53. package/dist/core/events/tool-bridge-logging.d.ts +4 -1
  54. package/dist/core/events/tool-bridge-logging.d.ts.map +1 -1
  55. package/dist/core/events/tool-bridge-logging.js +112 -13
  56. package/dist/core/events/tool-bridge-logging.js.map +1 -1
  57. package/dist/core/events-metadata.d.ts.map +1 -1
  58. package/dist/core/events-metadata.js +8 -4
  59. package/dist/core/events-metadata.js.map +1 -1
  60. package/dist/core/export.d.ts +1 -1
  61. package/dist/core/export.d.ts.map +1 -1
  62. package/dist/core/export.js +2 -15
  63. package/dist/core/export.js.map +1 -1
  64. package/dist/core/feature-path-logging.d.ts +50 -0
  65. package/dist/core/feature-path-logging.d.ts.map +1 -0
  66. package/dist/core/feature-path-logging.js +130 -0
  67. package/dist/core/feature-path-logging.js.map +1 -0
  68. package/dist/core/file-tools.d.ts +57 -1
  69. package/dist/core/file-tools.d.ts.map +1 -1
  70. package/dist/core/file-tools.js +329 -29
  71. package/dist/core/file-tools.js.map +1 -1
  72. package/dist/core/google-direct.d.ts +6 -1
  73. package/dist/core/google-direct.d.ts.map +1 -1
  74. package/dist/core/google-direct.js +76 -7
  75. package/dist/core/google-direct.js.map +1 -1
  76. package/dist/core/heartbeat.d.ts +34 -0
  77. package/dist/core/heartbeat.d.ts.map +1 -0
  78. package/dist/core/heartbeat.js +153 -0
  79. package/dist/core/heartbeat.js.map +1 -0
  80. package/dist/core/hitl-tool.d.ts +73 -0
  81. package/dist/core/hitl-tool.d.ts.map +1 -0
  82. package/dist/core/hitl-tool.js +284 -0
  83. package/dist/core/hitl-tool.js.map +1 -0
  84. package/dist/core/hitl.d.ts +85 -8
  85. package/dist/core/hitl.d.ts.map +1 -1
  86. package/dist/core/hitl.js +375 -61
  87. package/dist/core/hitl.js.map +1 -1
  88. package/dist/core/index.d.ts +12 -7
  89. package/dist/core/index.d.ts.map +1 -1
  90. package/dist/core/index.js +11 -6
  91. package/dist/core/index.js.map +1 -1
  92. package/dist/core/llm-manager.d.ts +17 -0
  93. package/dist/core/llm-manager.d.ts.map +1 -1
  94. package/dist/core/llm-manager.js +335 -43
  95. package/dist/core/llm-manager.js.map +1 -1
  96. package/dist/core/load-skill-tool.d.ts +36 -3
  97. package/dist/core/load-skill-tool.d.ts.map +1 -1
  98. package/dist/core/load-skill-tool.js +807 -93
  99. package/dist/core/load-skill-tool.js.map +1 -1
  100. package/dist/core/logger.d.ts +14 -0
  101. package/dist/core/logger.d.ts.map +1 -1
  102. package/dist/core/logger.js +15 -0
  103. package/dist/core/logger.js.map +1 -1
  104. package/dist/core/managers.d.ts +41 -52
  105. package/dist/core/managers.d.ts.map +1 -1
  106. package/dist/core/managers.js +422 -533
  107. package/dist/core/managers.js.map +1 -1
  108. package/dist/core/mcp-server-registry.d.ts +19 -2
  109. package/dist/core/mcp-server-registry.d.ts.map +1 -1
  110. package/dist/core/mcp-server-registry.js +168 -12
  111. package/dist/core/mcp-server-registry.js.map +1 -1
  112. package/dist/core/message-cutoff.d.ts +29 -0
  113. package/dist/core/message-cutoff.d.ts.map +1 -0
  114. package/dist/core/message-cutoff.js +63 -0
  115. package/dist/core/message-cutoff.js.map +1 -0
  116. package/dist/core/message-edit-manager.d.ts +54 -0
  117. package/dist/core/message-edit-manager.d.ts.map +1 -0
  118. package/dist/core/message-edit-manager.js +602 -0
  119. package/dist/core/message-edit-manager.js.map +1 -0
  120. package/dist/core/message-prep.d.ts +2 -0
  121. package/dist/core/message-prep.d.ts.map +1 -1
  122. package/dist/core/message-prep.js +39 -12
  123. package/dist/core/message-prep.js.map +1 -1
  124. package/dist/core/message-processing-control.d.ts +1 -0
  125. package/dist/core/message-processing-control.d.ts.map +1 -1
  126. package/dist/core/message-processing-control.js +23 -6
  127. package/dist/core/message-processing-control.js.map +1 -1
  128. package/dist/core/openai-direct.d.ts +9 -3
  129. package/dist/core/openai-direct.d.ts.map +1 -1
  130. package/dist/core/openai-direct.js +267 -33
  131. package/dist/core/openai-direct.js.map +1 -1
  132. package/dist/core/optional-tracers/opik-runtime.d.ts +32 -0
  133. package/dist/core/optional-tracers/opik-runtime.d.ts.map +1 -0
  134. package/dist/core/optional-tracers/opik-runtime.js +141 -0
  135. package/dist/core/optional-tracers/opik-runtime.js.map +1 -0
  136. package/dist/core/queue-manager.d.ts +84 -0
  137. package/dist/core/queue-manager.d.ts.map +1 -0
  138. package/dist/core/queue-manager.js +814 -0
  139. package/dist/core/queue-manager.js.map +1 -0
  140. package/dist/core/reasoning-controls.d.ts +30 -0
  141. package/dist/core/reasoning-controls.d.ts.map +1 -0
  142. package/dist/core/reasoning-controls.js +118 -0
  143. package/dist/core/reasoning-controls.js.map +1 -0
  144. package/dist/core/reliability-config.d.ts +82 -0
  145. package/dist/core/reliability-config.d.ts.map +1 -0
  146. package/dist/core/reliability-config.js +106 -0
  147. package/dist/core/reliability-config.js.map +1 -0
  148. package/dist/core/reliability-runtime.d.ts +53 -0
  149. package/dist/core/reliability-runtime.d.ts.map +1 -0
  150. package/dist/core/reliability-runtime.js +92 -0
  151. package/dist/core/reliability-runtime.js.map +1 -0
  152. package/dist/core/security/guardrails.d.ts +21 -0
  153. package/dist/core/security/guardrails.d.ts.map +1 -0
  154. package/dist/core/security/guardrails.js +111 -0
  155. package/dist/core/security/guardrails.js.map +1 -0
  156. package/dist/core/send-message-tool.d.ts +79 -0
  157. package/dist/core/send-message-tool.d.ts.map +1 -0
  158. package/dist/core/send-message-tool.js +222 -0
  159. package/dist/core/send-message-tool.js.map +1 -0
  160. package/dist/core/shell-cmd-tool.d.ts +82 -1
  161. package/dist/core/shell-cmd-tool.d.ts.map +1 -1
  162. package/dist/core/shell-cmd-tool.js +854 -42
  163. package/dist/core/shell-cmd-tool.js.map +1 -1
  164. package/dist/core/skill-registry.d.ts +2 -0
  165. package/dist/core/skill-registry.d.ts.map +1 -1
  166. package/dist/core/skill-registry.js +52 -2
  167. package/dist/core/skill-registry.js.map +1 -1
  168. package/dist/core/storage/eventStorage/fileEventStorage.d.ts +5 -0
  169. package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -1
  170. package/dist/core/storage/eventStorage/fileEventStorage.js +61 -0
  171. package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -1
  172. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +5 -0
  173. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -1
  174. package/dist/core/storage/eventStorage/memoryEventStorage.js +34 -0
  175. package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -1
  176. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +1 -0
  177. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -1
  178. package/dist/core/storage/eventStorage/sqliteEventStorage.js +19 -2
  179. package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -1
  180. package/dist/core/storage/eventStorage/types.d.ts +6 -0
  181. package/dist/core/storage/eventStorage/types.d.ts.map +1 -1
  182. package/dist/core/storage/eventStorage/types.js +1 -0
  183. package/dist/core/storage/eventStorage/types.js.map +1 -1
  184. package/dist/core/storage/eventStorage/validation.d.ts.map +1 -1
  185. package/dist/core/storage/eventStorage/validation.js +2 -1
  186. package/dist/core/storage/eventStorage/validation.js.map +1 -1
  187. package/dist/core/storage/github-world-import.d.ts +84 -0
  188. package/dist/core/storage/github-world-import.d.ts.map +1 -0
  189. package/dist/core/storage/github-world-import.js +365 -0
  190. package/dist/core/storage/github-world-import.js.map +1 -0
  191. package/dist/core/storage/memory-storage.d.ts +19 -8
  192. package/dist/core/storage/memory-storage.d.ts.map +1 -1
  193. package/dist/core/storage/memory-storage.js +147 -49
  194. package/dist/core/storage/memory-storage.js.map +1 -1
  195. package/dist/core/storage/queue-storage.d.ts +1 -0
  196. package/dist/core/storage/queue-storage.d.ts.map +1 -1
  197. package/dist/core/storage/queue-storage.js +3 -2
  198. package/dist/core/storage/queue-storage.js.map +1 -1
  199. package/dist/core/storage/sqlite-storage.d.ts +14 -9
  200. package/dist/core/storage/sqlite-storage.d.ts.map +1 -1
  201. package/dist/core/storage/sqlite-storage.js +131 -154
  202. package/dist/core/storage/sqlite-storage.js.map +1 -1
  203. package/dist/core/storage/storage-factory.d.ts +3 -0
  204. package/dist/core/storage/storage-factory.d.ts.map +1 -1
  205. package/dist/core/storage/storage-factory.js +175 -89
  206. package/dist/core/storage/storage-factory.js.map +1 -1
  207. package/dist/core/storage/world-storage.d.ts +1 -1
  208. package/dist/core/storage/world-storage.d.ts.map +1 -1
  209. package/dist/core/storage/world-storage.js +5 -1
  210. package/dist/core/storage/world-storage.js.map +1 -1
  211. package/dist/core/storage-init.d.ts +11 -0
  212. package/dist/core/storage-init.d.ts.map +1 -0
  213. package/dist/core/storage-init.js +122 -0
  214. package/dist/core/storage-init.js.map +1 -0
  215. package/dist/core/subscription.d.ts +8 -1
  216. package/dist/core/subscription.d.ts.map +1 -1
  217. package/dist/core/subscription.js +130 -23
  218. package/dist/core/subscription.js.map +1 -1
  219. package/dist/core/tool-approval.d.ts +45 -0
  220. package/dist/core/tool-approval.d.ts.map +1 -0
  221. package/dist/core/tool-approval.js +223 -0
  222. package/dist/core/tool-approval.js.map +1 -0
  223. package/dist/core/tool-execution-envelope.d.ts +87 -0
  224. package/dist/core/tool-execution-envelope.d.ts.map +1 -0
  225. package/dist/core/tool-execution-envelope.js +168 -0
  226. package/dist/core/tool-execution-envelope.js.map +1 -0
  227. package/dist/core/tool-utils.d.ts +9 -2
  228. package/dist/core/tool-utils.d.ts.map +1 -1
  229. package/dist/core/tool-utils.js +122 -28
  230. package/dist/core/tool-utils.js.map +1 -1
  231. package/dist/core/types.d.ts +69 -36
  232. package/dist/core/types.d.ts.map +1 -1
  233. package/dist/core/types.js +3 -2
  234. package/dist/core/types.js.map +1 -1
  235. package/dist/core/utils.d.ts +16 -0
  236. package/dist/core/utils.d.ts.map +1 -1
  237. package/dist/core/utils.js +99 -24
  238. package/dist/core/utils.js.map +1 -1
  239. package/dist/core/web-fetch-tool.d.ts +72 -0
  240. package/dist/core/web-fetch-tool.d.ts.map +1 -0
  241. package/dist/core/web-fetch-tool.js +491 -0
  242. package/dist/core/web-fetch-tool.js.map +1 -0
  243. package/dist/core/world-registry.d.ts +84 -0
  244. package/dist/core/world-registry.d.ts.map +1 -0
  245. package/dist/core/world-registry.js +247 -0
  246. package/dist/core/world-registry.js.map +1 -0
  247. package/dist/public/assets/index-Be-1xtV-.js +104 -0
  248. package/dist/public/assets/index-tsDdiXDU.css +1 -0
  249. package/dist/public/index.html +2 -2
  250. package/dist/public/mcp-sandbox-proxy.html +148 -0
  251. package/dist/server/api.js +288 -58
  252. package/dist/server/error-response.d.ts +27 -0
  253. package/dist/server/error-response.js +77 -0
  254. package/dist/server/index.d.ts +2 -1
  255. package/dist/server/index.js +6 -2
  256. package/dist/server/sse-handler.d.ts +13 -2
  257. package/dist/server/sse-handler.js +194 -26
  258. package/migrations/0015_add_message_queue.sql +36 -0
  259. package/migrations/0016_add_world_heartbeat.sql +13 -0
  260. package/migrations/0017_add_title_provenance.sql +7 -0
  261. package/package.json +31 -10
  262. package/dist/public/assets/index-BO20H4xt.js +0 -96
  263. package/dist/public/assets/index-ETY7W5_S.css +0 -1
package/dist/cli/index.js CHANGED
@@ -13,9 +13,15 @@
13
13
  * - Work with loaded world without importing (uses external storage path)
14
14
  *
15
15
  * Changes:
16
+ * - 2026-03-06: Routed runtime chat selection through explicit CLI state; removed live listener/send fallback to `world.currentChatId`.
17
+ * - 2026-02-27: Added active-chat event scoping in CLI listeners to prevent cross-chat message/system/SSE/tool leakage.
18
+ * - 2026-02-25: Added fallback shorthand attempt `@awesome-agent-world/<input>` when a provided local path does not exist.
19
+ * - 2026-02-25: Added GitHub shorthand world source import support (`@awesome-agent-world/<world-name>`) with safe remote staging and automatic import mode.
20
+ * - 2026-02-20: Enforced options-only HITL handling in interactive and pipeline modes.
16
21
  * - 2026-02-14: Added interactive + pipeline HITL option response handling for generic approval requests.
17
22
  * - 2026-02-11: Fixed tool-stream timeout: extendTimeout() resets idle timeout on streaming data
18
23
  * - 2026-02-11: Pipeline mode now listens for tool-stream SSE events to extend timeout
24
+ * - 2026-02-21: Extended timeout-refresh detection to include shell assistant-stream SSE events (`start`/`chunk`/`end` with `toolName='shell_cmd'`) in addition to legacy `tool-stream`.
19
25
  * - 2026-01-09: Added --streaming flag for explicit streaming control (overrides TTY auto-detection)
20
26
  * - 2025-02-06: Prevented duplicate MESSAGE output when streaming already rendered agent responses
21
27
  * - 2025-11-01: Added multi-world selection in loadWorldFromFile
@@ -76,14 +82,17 @@ import path from 'path';
76
82
  import { fileURLToPath } from 'url';
77
83
  import { program } from 'commander';
78
84
  import readline from 'readline';
79
- import { listWorlds, subscribeWorld, submitWorldOptionResponse, createCategoryLogger, LLMProvider, enableStreaming, disableStreaming } from '../core/index.js';
85
+ import { listWorlds, deleteWorld, subscribeWorld, submitWorldHitlResponse, GitHubWorldImportError, stageGitHubWorldFromShorthand, createCategoryLogger, LLMProvider, enableStreaming, disableStreaming } from '../core/index.js';
80
86
  import { EventType } from '../core/types.js';
81
87
  import { getDefaultRootPath } from '../core/storage/storage-factory.js';
88
+ // Opik integration: optional tracer attach for CLI world subscriptions.
89
+ import { attachOptionalOpikTracer } from '../core/optional-tracers/opik-runtime.js';
82
90
  import { processCLIInput, displayChatMessages } from './commands.js';
83
91
  import { createStreamingState, handleWorldEventWithStreaming, handleToolEvents, } from './stream.js';
84
92
  import { configureLLMProvider } from '../core/llm-config.js';
85
93
  import { createStatusLineManager, log as statusLog, } from './display.js';
86
- import { parseHitlOptionRequest, resolveHitlOptionSelectionInput, } from './hitl.js';
94
+ import { getSystemEventDisplayText } from './system-events.js';
95
+ import { markHitlRequestHandled, parseHitlPromptFromToolEvent, resolveHitlOptionSelectionInput, } from './hitl.js';
87
96
  // Create CLI category logger after logger auto-initialization
88
97
  const logger = createCategoryLogger('cli');
89
98
  function createGlobalState() {
@@ -394,11 +403,87 @@ function printCLIResult(result) {
394
403
  * @param rl - Readline interface for interactive mode (undefined for pipeline mode)
395
404
  * @returns Map of event types to listener functions for cleanup
396
405
  */
397
- function attachCLIListeners(world, streaming, globalState, activityMonitor, statusLine, rl) {
406
+ function attachCLIListeners(world, streaming, globalState, activityMonitor, statusLine, getSelectedChatId, rl) {
398
407
  const listeners = new Map();
399
408
  let hitlPromptChain = Promise.resolve();
409
+ const handledHitlRequestIds = new Set();
410
+ const isChatEventInScope = (eventChatId, includeUnscopedWhenScoped = false) => {
411
+ const activeChatId = getSelectedChatId();
412
+ if (!activeChatId) {
413
+ return true;
414
+ }
415
+ if (eventChatId === undefined || eventChatId === null) {
416
+ return includeUnscopedWhenScoped;
417
+ }
418
+ const normalizedEventChatId = String(eventChatId).trim();
419
+ if (!normalizedEventChatId) {
420
+ return includeUnscopedWhenScoped;
421
+ }
422
+ return normalizedEventChatId === activeChatId;
423
+ };
424
+ const shouldHandleWorldEvent = (eventData) => {
425
+ const eventType = String(eventData?.type || '').trim().toLowerCase();
426
+ if (eventType === 'response-start' || eventType === 'response-end' || eventType === 'idle' || eventType === 'info') {
427
+ return isChatEventInScope(eventData?.chatId, true);
428
+ }
429
+ if (eventType.startsWith('tool-')) {
430
+ return isChatEventInScope(eventData?.chatId, false);
431
+ }
432
+ return isChatEventInScope(eventData?.chatId, true);
433
+ };
400
434
  // World activity events
401
435
  const worldListener = (eventData) => {
436
+ if (!shouldHandleWorldEvent(eventData)) {
437
+ return;
438
+ }
439
+ const hitlRequest = parseHitlPromptFromToolEvent(eventData);
440
+ if (hitlRequest) {
441
+ if (!markHitlRequestHandled(handledHitlRequestIds, hitlRequest.requestId)) {
442
+ return;
443
+ }
444
+ hitlPromptChain = hitlPromptChain
445
+ .then(async () => {
446
+ if (streaming && globalState && rl && statusLine) {
447
+ globalState.hitlPromptActive = true;
448
+ try {
449
+ const result = submitWorldHitlResponse({
450
+ worldId: world.id,
451
+ requestId: hitlRequest.requestId,
452
+ optionId: await promptHitlOptionSelection(hitlRequest, statusLine, rl),
453
+ chatId: hitlRequest.chatId,
454
+ });
455
+ if (!result.accepted) {
456
+ statusLine.pause();
457
+ console.log(boldRed(`Failed to submit approval response: ${result.reason || 'unknown error'}`));
458
+ statusLine.resume();
459
+ return;
460
+ }
461
+ statusLine.pause();
462
+ console.log(green('Submitted HITL option response.'));
463
+ statusLine.resume();
464
+ return;
465
+ }
466
+ finally {
467
+ globalState.hitlPromptActive = false;
468
+ }
469
+ }
470
+ const result = submitWorldHitlResponse({
471
+ worldId: world.id,
472
+ requestId: hitlRequest.requestId,
473
+ optionId: hitlRequest.defaultOptionId,
474
+ chatId: hitlRequest.chatId,
475
+ });
476
+ if (!result.accepted) {
477
+ console.error(boldRed(`Failed to auto-respond HITL request: ${result.reason || 'unknown error'}`));
478
+ return;
479
+ }
480
+ console.log(`${gray('● system:')} Auto-selected HITL option "${hitlRequest.defaultOptionId}"`);
481
+ })
482
+ .catch((error) => {
483
+ console.error(boldRed(`Error handling HITL request: ${error instanceof Error ? error.message : String(error)}`));
484
+ });
485
+ return;
486
+ }
402
487
  activityMonitor.handle(eventData);
403
488
  // Only render activity progress in interactive mode
404
489
  if (streaming && globalState && rl && statusLine) {
@@ -411,6 +496,9 @@ function attachCLIListeners(world, streaming, globalState, activityMonitor, stat
411
496
  listeners.set(EventType.WORLD, worldListener);
412
497
  // Message events
413
498
  const messageListener = (eventData) => {
499
+ if (!isChatEventInScope(eventData?.chatId, false)) {
500
+ return;
501
+ }
414
502
  if (eventData.content &&
415
503
  typeof eventData.content === 'string' &&
416
504
  eventData.content.includes('Success message sent'))
@@ -434,8 +522,14 @@ function attachCLIListeners(world, streaming, globalState, activityMonitor, stat
434
522
  // SSE events (interactive mode only - pipeline mode uses non-streaming LLM calls)
435
523
  if (streaming && globalState && rl && statusLine) {
436
524
  const sseListener = (eventData) => {
437
- // Extend timeout when tool-stream data arrives (keeps long-running tools alive)
438
- if (eventData.type === 'tool-stream') {
525
+ if (!isChatEventInScope(eventData?.chatId, false)) {
526
+ return;
527
+ }
528
+ // Extend timeout when long-running shell stream activity arrives.
529
+ const isLegacyToolStream = eventData.type === 'tool-stream';
530
+ const isShellAssistantStream = eventData.toolName === 'shell_cmd' &&
531
+ (eventData.type === 'start' || eventData.type === 'chunk' || eventData.type === 'end');
532
+ if (isLegacyToolStream || isShellAssistantStream) {
439
533
  activityMonitor.extendTimeout();
440
534
  }
441
535
  handleWorldEvent(EventType.SSE, eventData, streaming, globalState, activityMonitor, statusLine, rl)
@@ -445,9 +539,15 @@ function attachCLIListeners(world, streaming, globalState, activityMonitor, stat
445
539
  listeners.set(EventType.SSE, sseListener);
446
540
  }
447
541
  else {
448
- // Pipeline mode: listen for tool-stream events to extend timeout on long-running commands
542
+ // Pipeline mode: listen for shell stream events to extend timeout on long-running commands.
449
543
  const sseTimeoutListener = (eventData) => {
450
- if (eventData.type === 'tool-stream') {
544
+ if (!isChatEventInScope(eventData?.chatId, false)) {
545
+ return;
546
+ }
547
+ const isLegacyToolStream = eventData.type === 'tool-stream';
548
+ const isShellAssistantStream = eventData.toolName === 'shell_cmd' &&
549
+ (eventData.type === 'start' || eventData.type === 'chunk' || eventData.type === 'end');
550
+ if (isLegacyToolStream || isShellAssistantStream) {
451
551
  activityMonitor.extendTimeout();
452
552
  }
453
553
  };
@@ -456,49 +556,7 @@ function attachCLIListeners(world, streaming, globalState, activityMonitor, stat
456
556
  }
457
557
  // System events
458
558
  const systemListener = (eventData) => {
459
- const hitlRequest = parseHitlOptionRequest(eventData);
460
- if (hitlRequest) {
461
- hitlPromptChain = hitlPromptChain
462
- .then(async () => {
463
- if (streaming && globalState && rl && statusLine) {
464
- globalState.hitlPromptActive = true;
465
- try {
466
- const selectedOptionId = await promptHitlOptionSelection(hitlRequest, statusLine, rl);
467
- const result = submitWorldOptionResponse({
468
- worldId: world.id,
469
- requestId: hitlRequest.requestId,
470
- optionId: selectedOptionId
471
- });
472
- if (!result.accepted) {
473
- statusLine.pause();
474
- console.log(boldRed(`Failed to submit approval response: ${result.reason || 'unknown error'}`));
475
- statusLine.resume();
476
- return;
477
- }
478
- statusLine.pause();
479
- console.log(green(`Approved option: ${selectedOptionId}`));
480
- statusLine.resume();
481
- return;
482
- }
483
- finally {
484
- globalState.hitlPromptActive = false;
485
- }
486
- }
487
- // Pipeline/non-interactive mode: auto-respond with default option to avoid blocking.
488
- const result = submitWorldOptionResponse({
489
- worldId: world.id,
490
- requestId: hitlRequest.requestId,
491
- optionId: hitlRequest.defaultOptionId
492
- });
493
- if (!result.accepted) {
494
- console.error(boldRed(`Failed to auto-respond HITL request: ${result.reason || 'unknown error'}`));
495
- return;
496
- }
497
- console.log(`${gray('● system:')} Auto-selected HITL option "${hitlRequest.defaultOptionId}"`);
498
- })
499
- .catch((error) => {
500
- console.error(boldRed(`Error handling HITL request: ${error instanceof Error ? error.message : String(error)}`));
501
- });
559
+ if (!isChatEventInScope(eventData?.chatId, false)) {
502
560
  return;
503
561
  }
504
562
  if (eventData.content &&
@@ -509,8 +567,11 @@ function attachCLIListeners(world, streaming, globalState, activityMonitor, stat
509
567
  handleWorldEvent(EventType.SYSTEM, eventData, streaming, globalState, activityMonitor, statusLine, rl)
510
568
  .catch(err => console.error('Error handling system event:', err));
511
569
  }
512
- else if (eventData.message || eventData.content) {
513
- // Pipeline mode: system messages are handled by message listener
570
+ else {
571
+ const systemText = getSystemEventDisplayText(eventData);
572
+ if (systemText) {
573
+ console.log(`${boldRed('● system:')} ${systemText}`);
574
+ }
514
575
  }
515
576
  };
516
577
  world.eventEmitter.on(EventType.SYSTEM, systemListener);
@@ -535,6 +596,7 @@ async function runPipelineMode(options, messageFromArgs) {
535
596
  let worldSubscription = null;
536
597
  let cliListeners = null;
537
598
  const activityMonitor = new WorldActivityMonitor();
599
+ const opikEnabledOverride = parseOptionalBoolean(options.opikEnabled);
538
600
  try {
539
601
  if (options.world) {
540
602
  // Subscribe to world lifecycle but do not request forwarding callbacks
@@ -544,9 +606,11 @@ async function runPipelineMode(options, messageFromArgs) {
544
606
  process.exit(1);
545
607
  }
546
608
  world = worldSubscription.world;
609
+ await attachOptionalOpikTracer(world, { source: 'cli', enabledOverride: opikEnabledOverride });
547
610
  // Attach direct listeners to the world.eventEmitter for pipeline handling
548
611
  // Note: Pipeline mode uses non-streaming LLM calls, so SSE events are not needed
549
- cliListeners = attachCLIListeners(world, null, null, activityMonitor, null);
612
+ const selectedChatId = String(world?.currentChatId || '').trim() || null;
613
+ cliListeners = attachCLIListeners(world, null, null, activityMonitor, null, () => selectedChatId);
550
614
  }
551
615
  // Execute command from --command option
552
616
  if (options.command) {
@@ -555,7 +619,7 @@ async function runPipelineMode(options, messageFromArgs) {
555
619
  process.exit(1);
556
620
  }
557
621
  const snapshot = activityMonitor.captureSnapshot();
558
- const result = await processCLIInput(options.command, world, 'human');
622
+ const result = await processCLIInput(options.command, world, 'human', String(world?.currentChatId || '').trim() || null);
559
623
  printCLIResult(result);
560
624
  if (!options.command.startsWith('/') && world) {
561
625
  try {
@@ -588,7 +652,7 @@ async function runPipelineMode(options, messageFromArgs) {
588
652
  process.exit(1);
589
653
  }
590
654
  const snapshot = activityMonitor.captureSnapshot();
591
- const result = await processCLIInput(messageFromArgs, world, 'human');
655
+ const result = await processCLIInput(messageFromArgs, world, 'human', String(world?.currentChatId || '').trim() || null);
592
656
  printCLIResult(result);
593
657
  try {
594
658
  // Event-driven completion: wait for world idle state
@@ -619,7 +683,7 @@ async function runPipelineMode(options, messageFromArgs) {
619
683
  process.exit(1);
620
684
  }
621
685
  const snapshot = activityMonitor.captureSnapshot();
622
- const result = await processCLIInput(input.trim(), world, 'HUMAN');
686
+ const result = await processCLIInput(input.trim(), world, 'HUMAN', String(world?.currentChatId || '').trim() || null);
623
687
  printCLIResult(result);
624
688
  try {
625
689
  // Event-driven completion: wait for world idle state
@@ -679,20 +743,26 @@ function cleanupWorldSubscription(worldState) {
679
743
  * @param rl - Readline interface for interactive input
680
744
  * @returns WorldState with subscription and world instance
681
745
  */
682
- async function handleSubscribe(rootPath, worldName, streaming, globalState, activityMonitor, statusLine, rl) {
746
+ async function handleSubscribe(rootPath, worldName, streaming, globalState, activityMonitor, statusLine, opikEnabledOverride, rl) {
683
747
  // Subscribe to world lifecycle but do not request forwarding callbacks
684
748
  const subscription = await subscribeWorld(worldName, { isOpen: true });
685
749
  if (!subscription)
686
750
  throw new Error('Failed to load world');
687
751
  const world = subscription.world;
752
+ await attachOptionalOpikTracer(world, { source: 'cli', enabledOverride: opikEnabledOverride });
688
753
  // Store world in globalState for access in interactive event handlers.
689
754
  if (globalState) {
690
755
  globalState.world = world;
691
756
  }
692
757
  // Attach direct listeners to the world.eventEmitter for CLI handling
693
758
  // Interactive mode needs all event types including SSE for streaming responses
694
- attachCLIListeners(world, streaming, globalState, activityMonitor, statusLine, rl);
695
- return { subscription, world };
759
+ const worldState = {
760
+ subscription,
761
+ world,
762
+ selectedChatId: String(world.currentChatId || '').trim() || null,
763
+ };
764
+ attachCLIListeners(world, streaming, globalState, activityMonitor, statusLine, () => worldState.selectedChatId, rl);
765
+ return worldState;
696
766
  }
697
767
  // Handle world events with streaming support
698
768
  async function handleWorldEvent(eventType, eventData, streaming, globalState, activityMonitor, statusLine, rl) {
@@ -810,8 +880,18 @@ async function handleWorldEvent(eventType, eventData, streaming, globalState, ac
810
880
  }
811
881
  return;
812
882
  }
813
- if ((eventType === 'system' || eventType === 'world') && (eventData.message || eventData.content)) {
814
- // existing logic
883
+ if (eventType === 'system') {
884
+ const systemText = getSystemEventDisplayText(eventData);
885
+ if (systemText) {
886
+ statusLog(statusLine, `${boldRed('● system:')} ${systemText}`);
887
+ }
888
+ return;
889
+ }
890
+ if (eventType === 'world' && (eventData.message || eventData.content)) {
891
+ const worldText = getSystemEventDisplayText(eventData);
892
+ if (worldText) {
893
+ statusLog(statusLine, `${gray('[World]')} ${worldText}`);
894
+ }
815
895
  }
816
896
  }
817
897
  // World discovery and selection
@@ -876,9 +956,13 @@ async function selectWorld(rootPath, rl) {
876
956
  // Load world from external file folder
877
957
  async function loadWorldFromFile(currentRootPath, rl) {
878
958
  const fs = await import('fs');
959
+ void currentRootPath;
960
+ let stagedGitHubWorld = null;
961
+ let resolvedSourcePath = '';
962
+ let importIsRequired = false;
879
963
  // Get world folder path
880
- const folderPath = await new Promise((resolve) => {
881
- rl.question(`\n${boldMagenta('Enter path to world folder:')} `, (answer) => {
964
+ const sourceInput = await new Promise((resolve) => {
965
+ rl.question(`\n${boldMagenta('Enter path to world folder (or @awesome-agent-world/<world-name>):')} `, (answer) => {
882
966
  const trimmed = answer.trim();
883
967
  if (trimmed === '') {
884
968
  resolve(null);
@@ -888,28 +972,57 @@ async function loadWorldFromFile(currentRootPath, rl) {
888
972
  }
889
973
  });
890
974
  });
891
- if (!folderPath) {
975
+ if (!sourceInput) {
892
976
  console.log(boldRed('Load cancelled.'));
893
977
  return null;
894
978
  }
895
- // Validate folder exists
896
- if (!fs.existsSync(folderPath)) {
897
- console.log(boldRed(`Folder does not exist: ${folderPath}`));
898
- return null;
899
- }
900
979
  try {
980
+ if (sourceInput.startsWith('@')) {
981
+ stagedGitHubWorld = await stageGitHubWorldFromShorthand(sourceInput);
982
+ resolvedSourcePath = stagedGitHubWorld.stagingRootPath;
983
+ importIsRequired = true;
984
+ console.log(green(`Fetched world source from GitHub: ${sourceInput}`));
985
+ if (stagedGitHubWorld.source.commitSha) {
986
+ console.log(` ${yellow('Source commit:')} ${stagedGitHubWorld.source.commitSha}`);
987
+ }
988
+ }
989
+ else {
990
+ resolvedSourcePath = sourceInput;
991
+ if (!fs.existsSync(resolvedSourcePath)) {
992
+ const fallbackShorthand = `@awesome-agent-world/${sourceInput}`;
993
+ try {
994
+ stagedGitHubWorld = await stageGitHubWorldFromShorthand(fallbackShorthand);
995
+ resolvedSourcePath = stagedGitHubWorld.stagingRootPath;
996
+ importIsRequired = true;
997
+ console.log(yellow(`Folder does not exist locally: ${sourceInput}`));
998
+ console.log(green(`Trying GitHub shorthand: ${fallbackShorthand}`));
999
+ if (stagedGitHubWorld.source.commitSha) {
1000
+ console.log(` ${yellow('Source commit:')} ${stagedGitHubWorld.source.commitSha}`);
1001
+ }
1002
+ }
1003
+ catch (fallbackError) {
1004
+ if (!(fallbackError instanceof GitHubWorldImportError)) {
1005
+ throw fallbackError;
1006
+ }
1007
+ }
1008
+ }
1009
+ }
1010
+ if (!fs.existsSync(resolvedSourcePath)) {
1011
+ console.log(boldRed(`Folder does not exist: ${resolvedSourcePath}`));
1012
+ return null;
1013
+ }
901
1014
  // Import necessary functions
902
1015
  const { createStorage } = await import('../core/storage/storage-factory.js');
903
- const { checkTargetExists, deleteExistingData } = await import('./commands.js');
1016
+ const { createStorageFromEnv } = await import('../core/storage/storage-factory.js');
904
1017
  // Create storage instance for source folder
905
1018
  const sourceStorage = await createStorage({
906
1019
  type: 'file',
907
- rootPath: folderPath
1020
+ rootPath: resolvedSourcePath
908
1021
  });
909
1022
  // List worlds in the source folder
910
1023
  const worldsInFolder = await sourceStorage.listWorlds();
911
1024
  if (worldsInFolder.length === 0) {
912
- console.log(boldRed(`No worlds found in: ${folderPath}`));
1025
+ console.log(boldRed(`No worlds found in: ${resolvedSourcePath}`));
913
1026
  return null;
914
1027
  }
915
1028
  // Select world if multiple
@@ -957,20 +1070,25 @@ async function loadWorldFromFile(currentRootPath, rl) {
957
1070
  const chats = await sourceStorage.listChats(worldData.id);
958
1071
  console.log(` ${yellow('Chats:')} ${chats.length}`);
959
1072
  // Ask if user wants to import
960
- const shouldImport = await new Promise((resolve) => {
961
- rl.question(`\n${boldMagenta('Import this world to current storage?')} ${boldMagenta('(yes/no):')} `, (answer) => {
962
- const trimmed = answer.trim().toLowerCase();
963
- resolve(trimmed === 'yes' || trimmed === 'y');
1073
+ const shouldImport = importIsRequired
1074
+ ? true
1075
+ : await new Promise((resolve) => {
1076
+ rl.question(`\n${boldMagenta('Import this world to current storage?')} ${boldMagenta('(yes/no):')} `, (answer) => {
1077
+ const trimmed = answer.trim().toLowerCase();
1078
+ resolve(trimmed === 'yes' || trimmed === 'y');
1079
+ });
964
1080
  });
965
- });
966
1081
  if (!shouldImport) {
967
1082
  console.log(yellow('World loaded from external storage (not imported).'));
968
1083
  // Return world name with external path to use that storage
969
- return { worldName: worldData.name, externalPath: folderPath };
1084
+ return { worldName: worldData.id, externalPath: resolvedSourcePath };
970
1085
  }
971
- // Check if world already exists in current storage
972
- const checkResult = await checkTargetExists(currentRootPath, 'file', worldData.id);
973
- if (checkResult.exists) {
1086
+ // Check if world already exists in active runtime storage
1087
+ const existingWorlds = await listWorlds();
1088
+ const idConflict = existingWorlds.find((world) => String(world?.id || '') === String(worldData?.id || ''));
1089
+ const nameConflict = existingWorlds.find((world) => String(world?.name || '').trim().toLowerCase() === String(worldData?.name || '').trim().toLowerCase());
1090
+ const existingWorld = idConflict || nameConflict;
1091
+ if (existingWorld) {
974
1092
  // Confirm overwrite
975
1093
  const shouldOverwrite = await new Promise((resolve) => {
976
1094
  rl.question(`\n${boldYellow(`World '${worldData.name}' already exists. Overwrite?`)} ${boldMagenta('(yes/no):')} `, (answer) => {
@@ -981,17 +1099,18 @@ async function loadWorldFromFile(currentRootPath, rl) {
981
1099
  if (!shouldOverwrite) {
982
1100
  console.log(yellow('Import cancelled. Loading from external storage instead.'));
983
1101
  // Return world name with external path to use that storage
984
- return { worldName: worldData.name, externalPath: folderPath };
1102
+ return { worldName: worldData.id, externalPath: resolvedSourcePath };
985
1103
  }
986
1104
  // Delete existing world
987
- await deleteExistingData(currentRootPath, 'file', worldData.id);
988
- console.log(green(`Deleted existing world '${worldData.name}'`));
1105
+ const deleted = await deleteWorld(String(existingWorld.id));
1106
+ if (!deleted) {
1107
+ console.log(boldRed(`Failed to delete existing world '${existingWorld.id}' before import.`));
1108
+ return null;
1109
+ }
1110
+ console.log(green(`Deleted existing world '${existingWorld.name || existingWorld.id}'`));
989
1111
  }
990
- // Create storage instance for target
991
- const targetStorage = await createStorage({
992
- type: 'file',
993
- rootPath: currentRootPath
994
- });
1112
+ // Create storage instance for active runtime target
1113
+ const targetStorage = await createStorageFromEnv();
995
1114
  // Save world
996
1115
  await targetStorage.saveWorld(worldData);
997
1116
  // Save all agents
@@ -1031,12 +1150,29 @@ async function loadWorldFromFile(currentRootPath, rl) {
1031
1150
  console.log(` ${yellow('Agents:')} ${agents.length}`);
1032
1151
  console.log(` ${yellow('Chats:')} ${chats.length}`);
1033
1152
  console.log(` ${yellow('Events:')} ${eventCount}`);
1034
- return { worldName: worldData.name };
1153
+ return { worldName: worldData.id };
1035
1154
  }
1036
1155
  catch (error) {
1156
+ if (error instanceof GitHubWorldImportError) {
1157
+ console.log(boldRed(`Error loading world: ${error.message}`));
1158
+ const details = error.details || {};
1159
+ const owner = String(details.owner || '');
1160
+ const repo = String(details.repo || '');
1161
+ const branch = String(details.branch || '');
1162
+ const worldPath = String(details.worldPath || '');
1163
+ if (owner && repo && branch && worldPath) {
1164
+ console.log(` ${yellow('Resolved source:')} ${owner}/${repo}@${branch}:${worldPath}`);
1165
+ }
1166
+ return null;
1167
+ }
1037
1168
  console.log(boldRed(`Error loading world: ${error instanceof Error ? error.message : String(error)}`));
1038
1169
  return null;
1039
1170
  }
1171
+ finally {
1172
+ if (stagedGitHubWorld) {
1173
+ await stagedGitHubWorld.cleanup();
1174
+ }
1175
+ }
1040
1176
  }
1041
1177
  // Chat discovery and selection
1042
1178
  async function selectChat(world, chats, currentChatId, rl) {
@@ -1141,6 +1277,7 @@ async function confirmOverwrite(message, rl) {
1141
1277
  // Interactive mode: console-based interface
1142
1278
  async function runInteractiveMode(options) {
1143
1279
  const rootPath = options.root || DEFAULT_ROOT_PATH;
1280
+ const opikEnabledOverride = parseOptionalBoolean(options.opikEnabled);
1144
1281
  const globalState = createGlobalState();
1145
1282
  const streaming = createStreamingState();
1146
1283
  const activityMonitor = new WorldActivityMonitor();
@@ -1162,7 +1299,7 @@ async function runInteractiveMode(options) {
1162
1299
  try {
1163
1300
  activityMonitor.reset();
1164
1301
  statusLine.reset();
1165
- worldState = await handleSubscribe(rootPath, options.world, streaming, globalState, activityMonitor, statusLine, rl);
1302
+ worldState = await handleSubscribe(rootPath, options.world, streaming, globalState, activityMonitor, statusLine, opikEnabledOverride, rl);
1166
1303
  currentWorldName = options.world;
1167
1304
  console.log(success(`Connected to world: ${currentWorldName}`));
1168
1305
  if (worldState?.world) {
@@ -1188,7 +1325,7 @@ async function runInteractiveMode(options) {
1188
1325
  try {
1189
1326
  activityMonitor.reset();
1190
1327
  statusLine.reset();
1191
- worldState = await handleSubscribe(effectiveRootPath, selectedWorld.worldName, streaming, globalState, activityMonitor, statusLine, rl);
1328
+ worldState = await handleSubscribe(effectiveRootPath, selectedWorld.worldName, streaming, globalState, activityMonitor, statusLine, opikEnabledOverride, rl);
1192
1329
  currentWorldName = selectedWorld.worldName;
1193
1330
  console.log(success(`Connected to world: ${currentWorldName}`));
1194
1331
  if (selectedWorld.externalPath) {
@@ -1216,7 +1353,7 @@ async function runInteractiveMode(options) {
1216
1353
  console.log('');
1217
1354
  // Display current chat messages after Quick Start tips
1218
1355
  if (worldState?.world) {
1219
- await displayChatMessages(worldState.world.id, worldState.world.currentChatId);
1356
+ await displayChatMessages(worldState.world.id, worldState.selectedChatId);
1220
1357
  }
1221
1358
  console.log(); // Empty line before prompt
1222
1359
  rl.prompt();
@@ -1250,7 +1387,7 @@ async function runInteractiveMode(options) {
1250
1387
  snapshot = activityMonitor.captureSnapshot();
1251
1388
  }
1252
1389
  try {
1253
- const result = await processCLIInput(trimmedInput, worldState?.world || null, 'HUMAN');
1390
+ const result = await processCLIInput(trimmedInput, worldState?.world || null, 'HUMAN', worldState?.selectedChatId || null);
1254
1391
  // Handle exit commands from result (redundant, but keep for safety)
1255
1392
  if (result.data?.exit) {
1256
1393
  if (isExiting)
@@ -1288,7 +1425,7 @@ async function runInteractiveMode(options) {
1288
1425
  logger.debug(`Subscribing to world: ${selectedWorld.worldName}...`);
1289
1426
  activityMonitor.reset();
1290
1427
  statusLine.reset();
1291
- worldState = await handleSubscribe(effectiveRootPath, selectedWorld.worldName, streaming, globalState, activityMonitor, statusLine, rl);
1428
+ worldState = await handleSubscribe(effectiveRootPath, selectedWorld.worldName, streaming, globalState, activityMonitor, statusLine, opikEnabledOverride, rl);
1292
1429
  currentWorldName = selectedWorld.worldName;
1293
1430
  console.log(success(`Connected to world: ${currentWorldName}`));
1294
1431
  if (selectedWorld.externalPath) {
@@ -1297,7 +1434,7 @@ async function runInteractiveMode(options) {
1297
1434
  if (worldState?.world) {
1298
1435
  console.log(`${gray('Agents:')} ${yellow(String(worldState.world.agents?.size || 0))} ${gray('| Turn Limit:')} ${yellow(String(worldState.world.turnLimit || 'N/A'))}`);
1299
1436
  // Display current chat messages
1300
- await displayChatMessages(worldState.world.id, worldState.world.currentChatId);
1437
+ await displayChatMessages(worldState.world.id, worldState.selectedChatId);
1301
1438
  }
1302
1439
  }
1303
1440
  catch (err) {
@@ -1335,6 +1472,7 @@ async function runInteractiveMode(options) {
1335
1472
  // console.log(boldBlue('Refreshing world state...'));
1336
1473
  const refreshedWorld = await worldState.subscription.refresh(rootPath);
1337
1474
  worldState.world = refreshedWorld;
1475
+ worldState.selectedChatId = selectedChatId;
1338
1476
  console.log(success('World state refreshed'));
1339
1477
  }
1340
1478
  }
@@ -1447,6 +1585,8 @@ async function runInteractiveMode(options) {
1447
1585
  // Use the subscription's refresh method to properly destroy old world and create new
1448
1586
  const refreshedWorld = await worldState.subscription.refresh(rootPath);
1449
1587
  worldState.world = refreshedWorld;
1588
+ const refreshedChatId = String(result.data?.currentChatId || refreshedWorld?.currentChatId || '').trim();
1589
+ worldState.selectedChatId = refreshedChatId || worldState.selectedChatId;
1450
1590
  console.log(success('World state refreshed'));
1451
1591
  }
1452
1592
  catch (err) {
@@ -1537,6 +1677,19 @@ function determineStreamingMode(streamingFlag, isTTY) {
1537
1677
  // Auto-detect: enable streaming for interactive (TTY), disable for pipeline
1538
1678
  return isTTY;
1539
1679
  }
1680
+ function parseOptionalBoolean(value) {
1681
+ if (!value) {
1682
+ return undefined;
1683
+ }
1684
+ const normalized = value.trim().toLowerCase();
1685
+ if (['true', '1', 'yes', 'on'].includes(normalized)) {
1686
+ return true;
1687
+ }
1688
+ if (['false', '0', 'no', 'off'].includes(normalized)) {
1689
+ return false;
1690
+ }
1691
+ return undefined;
1692
+ }
1540
1693
  // Main CLI entry point
1541
1694
  async function main() {
1542
1695
  // Configure LLM providers from environment variables at startup
@@ -1553,6 +1706,7 @@ async function main() {
1553
1706
  .option('-c, --command <cmd>', 'Command to execute in pipeline mode')
1554
1707
  .option('-l, --logLevel <level>', 'Set log level (trace, debug, info, warn, error)', 'error')
1555
1708
  .option('--streaming', 'Enable streaming responses (default: auto-detected from TTY)')
1709
+ .option('--opik-enabled <true|false>', 'Override OPIK_ENABLED for this CLI run')
1556
1710
  .option('-s, --server', 'Launch the server before running CLI')
1557
1711
  .allowUnknownOption()
1558
1712
  .allowExcessArguments()
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CLI System Event Formatting Helpers
3
+ *
4
+ * Purpose:
5
+ * - Convert runtime `system` event payloads into readable CLI output text.
6
+ *
7
+ * Key Features:
8
+ * - Supports plain-string and structured-object system payloads.
9
+ * - Preserves important status families such as title updates and queue failures.
10
+ * - Keeps formatting pure so both interactive and pipeline paths share the same logic.
11
+ *
12
+ * Notes on Implementation:
13
+ * - Leaf helper with no terminal side effects.
14
+ * - Unknown payloads return `null` so callers can skip empty/noise events.
15
+ *
16
+ * Summary of Recent Changes:
17
+ * - 2026-03-12: Created for cross-client system-status parity.
18
+ */
19
+ export interface CliSystemEventPayload {
20
+ message?: unknown;
21
+ content?: unknown;
22
+ eventType?: unknown;
23
+ type?: unknown;
24
+ title?: unknown;
25
+ [key: string]: unknown;
26
+ }
27
+ export declare function getSystemEventDisplayText(eventData: CliSystemEventPayload | null | undefined): string | null;