@antelopejs/dms-ai 0.1.2 → 0.1.3

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 (144) hide show
  1. package/README.md +49 -0
  2. package/dist/channels/bridge.d.ts +22 -0
  3. package/dist/channels/bridge.js +87 -0
  4. package/dist/channels/bridge.js.map +1 -0
  5. package/dist/channels/channel-service.d.ts +5 -0
  6. package/dist/channels/channel-service.js +68 -0
  7. package/dist/channels/channel-service.js.map +1 -0
  8. package/dist/channels/message-budget.d.ts +3 -0
  9. package/dist/channels/message-budget.js +29 -0
  10. package/dist/channels/message-budget.js.map +1 -0
  11. package/dist/channels/registry.d.ts +7 -0
  12. package/dist/channels/registry.js +47 -0
  13. package/dist/channels/registry.js.map +1 -0
  14. package/dist/channels/sse-stream.d.ts +19 -0
  15. package/dist/channels/sse-stream.js +59 -0
  16. package/dist/channels/sse-stream.js.map +1 -0
  17. package/dist/chatbox/passthrough.d.ts +3 -0
  18. package/dist/chatbox/passthrough.js +53 -0
  19. package/dist/chatbox/passthrough.js.map +1 -0
  20. package/dist/constants/channels.d.ts +30 -0
  21. package/dist/constants/channels.js +38 -0
  22. package/dist/constants/channels.js.map +1 -0
  23. package/dist/constants/chatbox.d.ts +10 -0
  24. package/dist/constants/chatbox.js +35 -0
  25. package/dist/constants/chatbox.js.map +1 -0
  26. package/dist/constants/sidecar.d.ts +4 -0
  27. package/dist/constants/sidecar.js +3 -1
  28. package/dist/constants/sidecar.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +5 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/lifecycle/sidecar-socket.d.ts +2 -0
  33. package/dist/lifecycle/sidecar-socket.js +37 -0
  34. package/dist/lifecycle/sidecar-socket.js.map +1 -0
  35. package/dist/lifecycle/sidecar-unavailable-error.d.ts +3 -0
  36. package/dist/lifecycle/sidecar-unavailable-error.js +10 -0
  37. package/dist/lifecycle/sidecar-unavailable-error.js.map +1 -0
  38. package/dist/routes/channels.d.ts +8 -0
  39. package/dist/routes/channels.js +52 -0
  40. package/dist/routes/channels.js.map +1 -0
  41. package/dist/routes/chatbox.d.ts +7 -0
  42. package/dist/routes/chatbox.js +47 -0
  43. package/dist/routes/chatbox.js.map +1 -0
  44. package/dist/routes/index.d.ts +2 -0
  45. package/dist/routes/index.js +2 -0
  46. package/dist/routes/index.js.map +1 -1
  47. package/dist/routes/sidecar-info.js +0 -2
  48. package/dist/routes/sidecar-info.js.map +1 -1
  49. package/frontend-vue/app/components/SettingsView.vue +8 -0
  50. package/frontend-vue/app/plugins/ai.client.ts +137 -58
  51. package/frontend-vue/app/runtime/channel-client.ts +265 -0
  52. package/frontend-vue/app/runtime/chat-transport.ts +82 -0
  53. package/frontend-vue/app/runtime/constants.ts +30 -14
  54. package/frontend-vue/app/runtime/current-page.ts +9 -5
  55. package/frontend-vue/app/runtime/event-stream.ts +75 -0
  56. package/frontend-vue/app/runtime/overlay.ts +23 -6
  57. package/frontend-vue/app/runtime/sidecar-status.ts +10 -25
  58. package/frontend-vue/app/runtime/theme-bridge.ts +16 -4
  59. package/frontend-vue/package.json +3 -0
  60. package/frontend-vue/tests/backoff.test.ts +15 -15
  61. package/frontend-vue/tests/channel-client.test.ts +247 -0
  62. package/frontend-vue/tests/chat-transport.test.ts +67 -0
  63. package/frontend-vue/tests/sidecar-status.test.ts +39 -0
  64. package/package.json +5 -3
  65. package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +1 -0
  66. package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +52 -0
  67. package/sidecar/chatbox/dist/index.html +2 -2
  68. package/sidecar/dist/agent/disposals.d.ts +5 -0
  69. package/sidecar/dist/agent/disposals.js +17 -0
  70. package/sidecar/dist/agent/disposals.js.map +1 -0
  71. package/sidecar/dist/agent/effective-mode.d.ts +2 -0
  72. package/sidecar/dist/agent/effective-mode.js +20 -0
  73. package/sidecar/dist/agent/effective-mode.js.map +1 -0
  74. package/sidecar/dist/agent/provider.d.ts +2 -2
  75. package/sidecar/dist/agent/runner-events.d.ts +9 -1
  76. package/sidecar/dist/agent/runner.d.ts +2 -2
  77. package/sidecar/dist/agent/runner.js +11 -5
  78. package/sidecar/dist/agent/runner.js.map +1 -1
  79. package/sidecar/dist/agent/session.d.ts +3 -3
  80. package/sidecar/dist/agent/session.js +54 -34
  81. package/sidecar/dist/agent/session.js.map +1 -1
  82. package/sidecar/dist/agent/switching-runner.js +16 -14
  83. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  84. package/sidecar/dist/agent/turn-end-reasons.d.ts +3 -0
  85. package/sidecar/dist/agent/turn-end-reasons.js +24 -0
  86. package/sidecar/dist/agent/turn-end-reasons.js.map +1 -0
  87. package/sidecar/dist/constants/agent.d.ts +6 -1
  88. package/sidecar/dist/constants/agent.js +13 -1
  89. package/sidecar/dist/constants/agent.js.map +1 -1
  90. package/sidecar/dist/constants/claude.d.ts +27 -1
  91. package/sidecar/dist/constants/claude.js +49 -0
  92. package/sidecar/dist/constants/claude.js.map +1 -1
  93. package/sidecar/dist/constants/codex.d.ts +6 -0
  94. package/sidecar/dist/constants/codex.js +13 -0
  95. package/sidecar/dist/constants/codex.js.map +1 -1
  96. package/sidecar/dist/constants/settings.js.map +1 -1
  97. package/sidecar/dist/constants/timing.d.ts +1 -0
  98. package/sidecar/dist/constants/timing.js +1 -0
  99. package/sidecar/dist/constants/timing.js.map +1 -1
  100. package/sidecar/dist/protocol/events.d.ts +36 -0
  101. package/sidecar/dist/protocol/events.js +13 -0
  102. package/sidecar/dist/protocol/events.js.map +1 -1
  103. package/sidecar/dist/providers/claude/activity.d.ts +5 -0
  104. package/sidecar/dist/providers/claude/activity.js +63 -0
  105. package/sidecar/dist/providers/claude/activity.js.map +1 -0
  106. package/sidecar/dist/providers/claude/adapter.js +21 -13
  107. package/sidecar/dist/providers/claude/adapter.js.map +1 -1
  108. package/sidecar/dist/providers/claude/config.d.ts +2 -1
  109. package/sidecar/dist/providers/claude/config.js +4 -0
  110. package/sidecar/dist/providers/claude/config.js.map +1 -1
  111. package/sidecar/dist/providers/claude/provider.js +18 -25
  112. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  113. package/sidecar/dist/providers/claude/result.d.ts +3 -0
  114. package/sidecar/dist/providers/claude/result.js +61 -0
  115. package/sidecar/dist/providers/claude/result.js.map +1 -0
  116. package/sidecar/dist/providers/claude/safe-mode.d.ts +4 -0
  117. package/sidecar/dist/providers/claude/safe-mode.js +24 -0
  118. package/sidecar/dist/providers/claude/safe-mode.js.map +1 -0
  119. package/sidecar/dist/providers/codex/adapter.js +8 -2
  120. package/sidecar/dist/providers/codex/adapter.js.map +1 -1
  121. package/sidecar/dist/providers/codex/client.d.ts +1 -0
  122. package/sidecar/dist/providers/codex/client.js +1 -0
  123. package/sidecar/dist/providers/codex/client.js.map +1 -1
  124. package/sidecar/dist/providers/codex/config.js +2 -1
  125. package/sidecar/dist/providers/codex/config.js.map +1 -1
  126. package/sidecar/dist/providers/codex/process.js +113 -33
  127. package/sidecar/dist/providers/codex/process.js.map +1 -1
  128. package/sidecar/dist/providers/codex/provider.js +6 -4
  129. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  130. package/sidecar/dist/server/routing.js +39 -21
  131. package/sidecar/dist/server/routing.js.map +1 -1
  132. package/sidecar/dist/server/turn-progress.d.ts +13 -0
  133. package/sidecar/dist/server/turn-progress.js +72 -0
  134. package/sidecar/dist/server/turn-progress.js.map +1 -0
  135. package/sidecar/dist/server/ws.js +4 -2
  136. package/sidecar/dist/server/ws.js.map +1 -1
  137. package/sidecar/dist/state/types.d.ts +2 -1
  138. package/sidecar/dist/state/types.js +1 -0
  139. package/sidecar/dist/state/types.js.map +1 -1
  140. package/sidecar/package.json +1 -1
  141. package/frontend-vue/app/runtime/ws-client.ts +0 -196
  142. package/frontend-vue/tests/sidecar-auth.test.ts +0 -87
  143. package/sidecar/chatbox/dist/assets/index-CEZ1NLp9.js +0 -52
  144. package/sidecar/chatbox/dist/assets/index-D_51Vqqk.css +0 -1
@@ -24,3 +24,7 @@ export declare const MODULE_ROOTS_TIMEOUT_MS = 3000;
24
24
  export declare const SIDECAR_DIST_EXTENSION = ".js";
25
25
  export declare const SIDECAR_LOOPBACK_HOST = "127.0.0.1";
26
26
  export declare const SIDECAR_AUTH_HEADER = "x-dms-ai-token";
27
+ export declare const SIDECAR_UNAVAILABLE_STATUS = 503;
28
+ export declare const SIDECAR_UNAVAILABLE_BODY: {
29
+ error: string;
30
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SIDECAR_AUTH_HEADER = exports.SIDECAR_LOOPBACK_HOST = exports.SIDECAR_DIST_EXTENSION = exports.MODULE_ROOTS_TIMEOUT_MS = exports.SIDECAR_HOST_ORIGIN_FLAG = exports.SIDECAR_BACKEND_URL_FLAG = exports.SIDECAR_BUILDER_FLAG = exports.SIDECAR_SKILL_DIRS_FLAG = exports.SIDECAR_MODULE_ROOTS_FLAG = exports.SIDECAR_BUILD_ID_FLAG = exports.SIDECAR_LOCK_POLL_INTERVAL_MS = exports.SIDECAR_HEALTH_TIMEOUT_MS = exports.SIDECAR_HEALTH_PATH = exports.SIDECAR_LOG_FILE_NAME = exports.SIDECAR_LOCK_FILE_NAME = exports.SIDECAR_CACHE_DIR_SEGMENTS = exports.SIDECAR_SUCCESS_EXIT_CODE = exports.SIDECAR_DIST_REL = exports.SIDECAR_ENV_DISABLE_KEY = exports.SIDECAR_DISABLED_FLAG = exports.SIDECAR_LOG_PREFIX = exports.SIDECAR_RESPAWN_DELAY_MS = exports.SIDECAR_RESPAWN_WINDOW_MS = exports.SIDECAR_RESPAWN_MAX = exports.SIDECAR_SPAWN_TIMEOUT_MS = exports.SIDECAR_BIN_NAME = void 0;
3
+ exports.SIDECAR_UNAVAILABLE_BODY = exports.SIDECAR_UNAVAILABLE_STATUS = exports.SIDECAR_AUTH_HEADER = exports.SIDECAR_LOOPBACK_HOST = exports.SIDECAR_DIST_EXTENSION = exports.MODULE_ROOTS_TIMEOUT_MS = exports.SIDECAR_HOST_ORIGIN_FLAG = exports.SIDECAR_BACKEND_URL_FLAG = exports.SIDECAR_BUILDER_FLAG = exports.SIDECAR_SKILL_DIRS_FLAG = exports.SIDECAR_MODULE_ROOTS_FLAG = exports.SIDECAR_BUILD_ID_FLAG = exports.SIDECAR_LOCK_POLL_INTERVAL_MS = exports.SIDECAR_HEALTH_TIMEOUT_MS = exports.SIDECAR_HEALTH_PATH = exports.SIDECAR_LOG_FILE_NAME = exports.SIDECAR_LOCK_FILE_NAME = exports.SIDECAR_CACHE_DIR_SEGMENTS = exports.SIDECAR_SUCCESS_EXIT_CODE = exports.SIDECAR_DIST_REL = exports.SIDECAR_ENV_DISABLE_KEY = exports.SIDECAR_DISABLED_FLAG = exports.SIDECAR_LOG_PREFIX = exports.SIDECAR_RESPAWN_DELAY_MS = exports.SIDECAR_RESPAWN_WINDOW_MS = exports.SIDECAR_RESPAWN_MAX = exports.SIDECAR_SPAWN_TIMEOUT_MS = exports.SIDECAR_BIN_NAME = void 0;
4
4
  exports.SIDECAR_BIN_NAME = "dms-ai-sidecar";
5
5
  exports.SIDECAR_SPAWN_TIMEOUT_MS = 10_000;
6
6
  exports.SIDECAR_RESPAWN_MAX = 3;
@@ -31,4 +31,6 @@ exports.MODULE_ROOTS_TIMEOUT_MS = 3_000;
31
31
  exports.SIDECAR_DIST_EXTENSION = ".js";
32
32
  exports.SIDECAR_LOOPBACK_HOST = "127.0.0.1";
33
33
  exports.SIDECAR_AUTH_HEADER = "x-dms-ai-token";
34
+ exports.SIDECAR_UNAVAILABLE_STATUS = 503;
35
+ exports.SIDECAR_UNAVAILABLE_BODY = { error: "sidecar_unavailable" };
34
36
  //# sourceMappingURL=sidecar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../../src/constants/sidecar.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAG,gBAAgB,CAAC;AACpC,QAAA,wBAAwB,GAAG,MAAM,CAAC;AAClC,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,yBAAyB,GAAG,MAAM,CAAC;AACnC,QAAA,wBAAwB,GAAG,GAAG,CAAC;AAC/B,QAAA,kBAAkB,GAAG,UAAU,CAAC;AAChC,QAAA,qBAAqB,GAAG,GAAG,CAAC;AAC5B,QAAA,uBAAuB,GAAG,QAAQ,CAAC;AACnC,QAAA,gBAAgB,GAAG,6BAA6B,CAAC;AACjD,QAAA,yBAAyB,GAAG,CAAC,CAAC;AAC9B,QAAA,0BAA0B,GAAG;IACxC,cAAc;IACd,QAAQ;IACR,QAAQ;CACA,CAAC;AACE,QAAA,sBAAsB,GAAG,cAAc,CAAC;AACxC,QAAA,qBAAqB,GAAG,aAAa,CAAC;AACtC,QAAA,mBAAmB,GAAG,SAAS,CAAC;AAChC,QAAA,yBAAyB,GAAG,IAAI,CAAC;AACjC,QAAA,6BAA6B,GAAG,GAAG,CAAC;AACpC,QAAA,qBAAqB,GAAG,YAAY,CAAC;AACrC,QAAA,yBAAyB,GAAG,gBAAgB,CAAC;AAC7C,QAAA,uBAAuB,GAAG,cAAc,CAAC;AACzC,QAAA,oBAAoB,GAAG,mBAAmB,CAAC;AAC3C,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAC3C,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAC3C,QAAA,uBAAuB,GAAG,KAAK,CAAC;AAChC,QAAA,sBAAsB,GAAG,KAAK,CAAC;AAC/B,QAAA,qBAAqB,GAAG,WAAW,CAAC;AACpC,QAAA,mBAAmB,GAAG,gBAAgB,CAAC"}
1
+ {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../../src/constants/sidecar.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAG,gBAAgB,CAAC;AACpC,QAAA,wBAAwB,GAAG,MAAM,CAAC;AAClC,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,yBAAyB,GAAG,MAAM,CAAC;AACnC,QAAA,wBAAwB,GAAG,GAAG,CAAC;AAC/B,QAAA,kBAAkB,GAAG,UAAU,CAAC;AAChC,QAAA,qBAAqB,GAAG,GAAG,CAAC;AAC5B,QAAA,uBAAuB,GAAG,QAAQ,CAAC;AACnC,QAAA,gBAAgB,GAAG,6BAA6B,CAAC;AACjD,QAAA,yBAAyB,GAAG,CAAC,CAAC;AAC9B,QAAA,0BAA0B,GAAG;IACxC,cAAc;IACd,QAAQ;IACR,QAAQ;CACA,CAAC;AACE,QAAA,sBAAsB,GAAG,cAAc,CAAC;AACxC,QAAA,qBAAqB,GAAG,aAAa,CAAC;AACtC,QAAA,mBAAmB,GAAG,SAAS,CAAC;AAChC,QAAA,yBAAyB,GAAG,IAAI,CAAC;AACjC,QAAA,6BAA6B,GAAG,GAAG,CAAC;AACpC,QAAA,qBAAqB,GAAG,YAAY,CAAC;AACrC,QAAA,yBAAyB,GAAG,gBAAgB,CAAC;AAC7C,QAAA,uBAAuB,GAAG,cAAc,CAAC;AACzC,QAAA,oBAAoB,GAAG,mBAAmB,CAAC;AAC3C,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAC3C,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAC3C,QAAA,uBAAuB,GAAG,KAAK,CAAC;AAChC,QAAA,sBAAsB,GAAG,KAAK,CAAC;AAC/B,QAAA,qBAAqB,GAAG,WAAW,CAAC;AACpC,QAAA,mBAAmB,GAAG,gBAAgB,CAAC;AAGvC,QAAA,0BAA0B,GAAG,GAAG,CAAC;AACjC,QAAA,wBAAwB,GAAG,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ import "./pages";
2
2
  import "./routes";
3
3
  export declare function construct(config: unknown): void;
4
4
  export declare function start(): Promise<void>;
5
+ export declare function destroy(): void;
package/dist/index.js CHANGED
@@ -5,10 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.construct = construct;
7
7
  exports.start = start;
8
+ exports.destroy = destroy;
8
9
  const node_path_1 = __importDefault(require("node:path"));
9
10
  const logging_1 = require("@antelopejs/interface-core/logging");
10
11
  const page_1 = require("@antelopejs/interface-dms/page");
11
12
  const presence_1 = require("./builder/presence");
13
+ const registry_1 = require("./channels/registry");
12
14
  const config_1 = require("./config");
13
15
  const frontend_module_1 = require("./constants/frontend-module");
14
16
  const sidecar_1 = require("./constants/sidecar");
@@ -44,4 +46,7 @@ async function start() {
44
46
  logging_1.Logging.Error(`${sidecar_1.SIDECAR_LOG_PREFIX} spawn failed:`, err);
45
47
  });
46
48
  }
49
+ function destroy() {
50
+ (0, registry_1.closeAllBridges)();
51
+ }
47
52
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAkBA,8BAEC;AAED,sBAyBC;AA/CD,0DAA6B;AAC7B,gEAA6D;AAC7D,yDAAmE;AACnE,iDAAwD;AACxD,qCAA6D;AAC7D,iEAIqC;AACrC,iDAAyD;AACzD,2DAA8D;AAC9D,6DAAgE;AAChE,6DAAyD;AACzD,qDAAuD;AACvD,mBAAiB;AACjB,oBAAkB;AAElB,SAAgB,SAAS,CAAC,MAAe;IACvC,IAAA,kBAAS,EAAC,IAAA,oBAAW,EAAC,MAAM,CAAC,CAAC,CAAC;AACjC,CAAC;AAEM,KAAK,UAAU,KAAK;IACzB,IAAA,4BAAe,GAAE,CAAC;IAClB,MAAM,IAAA,wBAAiB,EAAC;QACtB,IAAI,EAAE,sCAAoB;QAC1B,UAAU,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qCAAmB,CAAC;QACrD,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE;QACvC,QAAQ,EAAE,0CAAwB;KACnC,CAAC,CAAC;IAIH,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjE,IAAA,iCAAkB,GAAE;QACpB,IAAA,mCAAmB,GAAE;QACrB,IAAA,6BAAkB,GAAE;KACrB,CAAC,CAAC;IACH,KAAK,IAAA,4BAAY,EAAC;QAChB,eAAe,EAAE,OAAO,CAAC,GAAG,EAAE;QAC9B,GAAG,IAAA,kBAAS,GAAE;QACd,WAAW;QACX,SAAS;QACT,cAAc;KACf,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,iBAAO,CAAC,KAAK,CAAC,GAAG,4BAAkB,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAmBA,8BAEC;AAED,sBAyBC;AAOD,0BAEC;AAzDD,0DAA6B;AAC7B,gEAA6D;AAC7D,yDAAmE;AACnE,iDAAwD;AACxD,kDAAsD;AACtD,qCAA6D;AAC7D,iEAIqC;AACrC,iDAAyD;AACzD,2DAA8D;AAC9D,6DAAgE;AAChE,6DAAyD;AACzD,qDAAuD;AACvD,mBAAiB;AACjB,oBAAkB;AAElB,SAAgB,SAAS,CAAC,MAAe;IACvC,IAAA,kBAAS,EAAC,IAAA,oBAAW,EAAC,MAAM,CAAC,CAAC,CAAC;AACjC,CAAC;AAEM,KAAK,UAAU,KAAK;IACzB,IAAA,4BAAe,GAAE,CAAC;IAClB,MAAM,IAAA,wBAAiB,EAAC;QACtB,IAAI,EAAE,sCAAoB;QAC1B,UAAU,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qCAAmB,CAAC;QACrD,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE;QACvC,QAAQ,EAAE,0CAAwB;KACnC,CAAC,CAAC;IAIH,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjE,IAAA,iCAAkB,GAAE;QACpB,IAAA,mCAAmB,GAAE;QACrB,IAAA,6BAAkB,GAAE;KACrB,CAAC,CAAC;IACH,KAAK,IAAA,4BAAY,EAAC;QAChB,eAAe,EAAE,OAAO,CAAC,GAAG,EAAE;QAC9B,GAAG,IAAA,kBAAS,GAAE;QACd,WAAW;QACX,SAAS;QACT,cAAc;KACf,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,iBAAO,CAAC,KAAK,CAAC,GAAG,4BAAkB,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC;AAOD,SAAgB,OAAO;IACrB,IAAA,0BAAe,GAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import WebSocket from "ws";
2
+ export declare function connectSidecarSocket(path: string): Promise<WebSocket>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.connectSidecarSocket = connectSidecarSocket;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const channels_1 = require("../constants/channels");
9
+ const sidecar_1 = require("../constants/sidecar");
10
+ const spawn_sidecar_1 = require("./spawn-sidecar");
11
+ const sidecar_unavailable_error_1 = require("./sidecar-unavailable-error");
12
+ const HANDSHAKE_TIMEOUT_MS = 5_000;
13
+ function openSocket(url, token) {
14
+ return new Promise((resolve, reject) => {
15
+ const socket = new ws_1.default(url, {
16
+ headers: { Authorization: `Bearer ${token}` },
17
+ maxPayload: channels_1.CHANNEL_MESSAGE_MAX_BYTES,
18
+ handshakeTimeout: HANDSHAKE_TIMEOUT_MS,
19
+ });
20
+ const onError = () => reject(new sidecar_unavailable_error_1.SidecarUnavailableError());
21
+ socket.once("error", onError);
22
+ socket.once("open", () => {
23
+ socket.off("error", onError);
24
+ socket.pause();
25
+ resolve(socket);
26
+ });
27
+ });
28
+ }
29
+ async function connectSidecarSocket(path) {
30
+ await (0, spawn_sidecar_1.ensureSidecarRunning)();
31
+ const port = (0, spawn_sidecar_1.getSidecarPort)();
32
+ const token = (0, spawn_sidecar_1.getSidecarClientToken)();
33
+ if (port === null || !token)
34
+ throw new sidecar_unavailable_error_1.SidecarUnavailableError();
35
+ return openSocket(`ws://${sidecar_1.SIDECAR_LOOPBACK_HOST}:${port}${path}`, token);
36
+ }
37
+ //# sourceMappingURL=sidecar-socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-socket.js","sourceRoot":"","sources":["../../src/lifecycle/sidecar-socket.ts"],"names":[],"mappings":";;;;;AAoCA,oDAMC;AA1CD,4CAA2B;AAC3B,oDAAkE;AAClE,kDAA6D;AAC7D,mDAIyB;AACzB,2EAAsE;AAEtE,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAEnC,SAAS,UAAU,CAAC,GAAW,EAAE,KAAa;IAC5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,YAAS,CAAC,GAAG,EAAE;YAChC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;YAC7C,UAAU,EAAE,oCAAyB;YACrC,gBAAgB,EAAE,oBAAoB;SACvC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,IAAI,mDAAuB,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACvB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AASM,KAAK,UAAU,oBAAoB,CAAC,IAAY;IACrD,MAAM,IAAA,oCAAoB,GAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAA,8BAAc,GAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAA,qCAAqB,GAAE,CAAC;IACtC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,mDAAuB,EAAE,CAAC;IACjE,OAAO,UAAU,CAAC,QAAQ,+BAAqB,IAAI,IAAI,GAAG,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare class SidecarUnavailableError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SidecarUnavailableError = void 0;
4
+ class SidecarUnavailableError extends Error {
5
+ constructor() {
6
+ super("dms-ai sidecar is not reachable");
7
+ }
8
+ }
9
+ exports.SidecarUnavailableError = SidecarUnavailableError;
10
+ //# sourceMappingURL=sidecar-unavailable-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-unavailable-error.js","sourceRoot":"","sources":["../../src/lifecycle/sidecar-unavailable-error.ts"],"names":[],"mappings":";;;AACA,MAAa,uBAAwB,SAAQ,KAAK;IAChD;QACE,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAC3C,CAAC;CACF;AAJD,0DAIC"}
@@ -0,0 +1,8 @@
1
+ import { HTTPResult, type RequestContext } from "@antelopejs/interface-api";
2
+ import type { User } from "@antelopejs/interface-dms/auth/db";
3
+ declare const AIChannelsController_base: import("@antelopejs/interface-api").ControllerClass<object>;
4
+ export declare class AIChannelsController extends AIChannelsController_base {
5
+ events(ctx: RequestContext, channels: string, user: User): Promise<HTTPResult | undefined>;
6
+ messages(connectionId: string, _budget: void, body: Buffer, user: User): Promise<HTTPResult>;
7
+ }
8
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.AIChannelsController = void 0;
16
+ const interface_api_1 = require("@antelopejs/interface-api");
17
+ const auth_1 = require("@antelopejs/interface-dms/auth");
18
+ const channel_service_1 = require("../channels/channel-service");
19
+ const message_budget_1 = require("../channels/message-budget");
20
+ const channels_1 = require("../constants/channels");
21
+ let AIChannelsController = class AIChannelsController extends (0, interface_api_1.Controller)(channels_1.CHANNELS_ROUTE_PREFIX) {
22
+ events(ctx, channels, user) {
23
+ return (0, channel_service_1.openChannel)(ctx, channels, user._id);
24
+ }
25
+ messages(connectionId, _budget, body, user) {
26
+ return (0, channel_service_1.postChannelMessage)(connectionId, user._id, body);
27
+ }
28
+ };
29
+ exports.AIChannelsController = AIChannelsController;
30
+ __decorate([
31
+ (0, interface_api_1.Get)(channels_1.CHANNEL_EVENTS_ROUTE),
32
+ __param(0, (0, interface_api_1.Context)()),
33
+ __param(1, (0, interface_api_1.Parameter)(channels_1.CHANNELS_PARAM, "param")),
34
+ __param(2, (0, auth_1.AuthRawUser)()),
35
+ __metadata("design:type", Function),
36
+ __metadata("design:paramtypes", [Object, String, Function]),
37
+ __metadata("design:returntype", Promise)
38
+ ], AIChannelsController.prototype, "events", null);
39
+ __decorate([
40
+ (0, interface_api_1.Post)(channels_1.CHANNEL_MESSAGES_ROUTE),
41
+ __param(0, (0, interface_api_1.Parameter)(channels_1.CONNECTION_ID_PARAM, "param")),
42
+ __param(1, (0, message_budget_1.MessageBudget)()),
43
+ __param(2, (0, interface_api_1.RawBody)(channels_1.CHANNEL_MESSAGE_MAX_BYTES)),
44
+ __param(3, (0, auth_1.AuthRawUser)()),
45
+ __metadata("design:type", Function),
46
+ __metadata("design:paramtypes", [String, void 0, Buffer, Function]),
47
+ __metadata("design:returntype", Promise)
48
+ ], AIChannelsController.prototype, "messages", null);
49
+ exports.AIChannelsController = AIChannelsController = __decorate([
50
+ (0, auth_1.AuthOwnerOnly)()
51
+ ], AIChannelsController);
52
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/routes/channels.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6DASmC;AACnC,yDAA4E;AAE5E,iEAA8E;AAC9E,+DAA2D;AAC3D,oDAO+B;AASxB,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,IAAA,0BAAU,EAAC,gCAAqB,CAAC;IAEzE,MAAM,CACO,GAAmB,EACM,QAAgB,EACrC,IAAU;QAEzB,OAAO,IAAA,6BAAW,EAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IAGD,QAAQ,CACmC,YAAoB,EAC5C,OAAa,EACM,IAAY,EACjC,IAAU;QAEzB,OAAO,IAAA,oCAAkB,EAAC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1D,CAAC;CACF,CAAA;AAnBY,oDAAoB;AAE/B;IADC,IAAA,mBAAG,EAAC,+BAAoB,CAAC;IAEvB,WAAA,IAAA,uBAAO,GAAE,CAAA;IACT,WAAA,IAAA,yBAAS,EAAC,yBAAc,EAAE,OAAO,CAAC,CAAA;IAClC,WAAA,IAAA,kBAAW,GAAE,CAAA;;;;kDAGf;AAGD;IADC,IAAA,oBAAI,EAAC,iCAAsB,CAAC;IAE1B,WAAA,IAAA,yBAAS,EAAC,8BAAmB,EAAE,OAAO,CAAC,CAAA;IACvC,WAAA,IAAA,8BAAa,GAAE,CAAA;IACf,WAAA,IAAA,uBAAO,EAAC,oCAAyB,CAAC,CAAA;IAClC,WAAA,IAAA,kBAAW,GAAE,CAAA;;qDAD4B,MAAM;;oDAIjD;+BAlBU,oBAAoB;IADhC,IAAA,oBAAa,GAAE;GACH,oBAAoB,CAmBhC"}
@@ -0,0 +1,7 @@
1
+ import { type HTTPResult, type RequestContext } from "@antelopejs/interface-api";
2
+ declare const AIChatboxController_base: import("@antelopejs/interface-api").ControllerClass<object>;
3
+ export declare class AIChatboxController extends AIChatboxController_base {
4
+ document(ctx: RequestContext): Promise<HTTPResult | undefined>;
5
+ asset(ctx: RequestContext, path: string): Promise<HTTPResult | undefined>;
6
+ }
7
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.AIChatboxController = void 0;
16
+ const interface_api_1 = require("@antelopejs/interface-api");
17
+ const auth_1 = require("@antelopejs/interface-dms/auth");
18
+ const passthrough_1 = require("../chatbox/passthrough");
19
+ const chatbox_1 = require("../constants/chatbox");
20
+ let AIChatboxController = class AIChatboxController extends (0, interface_api_1.Controller)(chatbox_1.CHATBOX_ROUTE_PREFIX) {
21
+ document(ctx) {
22
+ return (0, passthrough_1.relayChatboxFile)(ctx, chatbox_1.CHATBOX_INDEX_PATH);
23
+ }
24
+ asset(ctx, path) {
25
+ return (0, passthrough_1.relayChatboxFile)(ctx, `/${path}`);
26
+ }
27
+ };
28
+ exports.AIChatboxController = AIChatboxController;
29
+ __decorate([
30
+ (0, interface_api_1.Get)(chatbox_1.CHATBOX_INDEX_ROUTE),
31
+ __param(0, (0, interface_api_1.Context)()),
32
+ __metadata("design:type", Function),
33
+ __metadata("design:paramtypes", [Object]),
34
+ __metadata("design:returntype", Promise)
35
+ ], AIChatboxController.prototype, "document", null);
36
+ __decorate([
37
+ (0, interface_api_1.Get)(chatbox_1.CHATBOX_ASSET_ROUTE),
38
+ __param(0, (0, interface_api_1.Context)()),
39
+ __param(1, (0, interface_api_1.Parameter)(chatbox_1.CHATBOX_PATH_PARAM, "param")),
40
+ __metadata("design:type", Function),
41
+ __metadata("design:paramtypes", [Object, String]),
42
+ __metadata("design:returntype", Promise)
43
+ ], AIChatboxController.prototype, "asset", null);
44
+ exports.AIChatboxController = AIChatboxController = __decorate([
45
+ (0, auth_1.AuthOwnerOnly)()
46
+ ], AIChatboxController);
47
+ //# sourceMappingURL=chatbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatbox.js","sourceRoot":"","sources":["../../src/routes/chatbox.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6DAOmC;AACnC,yDAA+D;AAC/D,wDAA0D;AAC1D,kDAM8B;AAOvB,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,IAAA,0BAAU,EAAC,8BAAoB,CAAC;IAEvE,QAAQ,CAAY,GAAmB;QACrC,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,4BAAkB,CAAC,CAAC;IACnD,CAAC;IAGD,KAAK,CACQ,GAAmB,EACU,IAAY;QAEpD,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AAbY,kDAAmB;AAE9B;IADC,IAAA,mBAAG,EAAC,6BAAmB,CAAC;IACf,WAAA,IAAA,uBAAO,GAAE,CAAA;;;;mDAElB;AAGD;IADC,IAAA,mBAAG,EAAC,6BAAmB,CAAC;IAEtB,WAAA,IAAA,uBAAO,GAAE,CAAA;IACT,WAAA,IAAA,yBAAS,EAAC,4BAAkB,EAAE,OAAO,CAAC,CAAA;;;;gDAGxC;8BAZU,mBAAmB;IAD/B,IAAA,oBAAa,GAAE;GACH,mBAAmB,CAa/B"}
@@ -1,4 +1,6 @@
1
1
  export * from "./builder";
2
+ export * from "./channels";
3
+ export * from "./chatbox";
2
4
  export * from "./health";
3
5
  export * from "./logs";
4
6
  export * from "./metrics";
@@ -15,6 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./builder"), exports);
18
+ __exportStar(require("./channels"), exports);
19
+ __exportStar(require("./chatbox"), exports);
18
20
  __exportStar(require("./health"), exports);
19
21
  __exportStar(require("./logs"), exports);
20
22
  __exportStar(require("./metrics"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,2CAAyB;AACzB,yCAAuB;AACvB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B;AAC3B,iDAA+B;AAC/B,2CAAyB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,6CAA2B;AAC3B,4CAA0B;AAC1B,2CAAyB;AACzB,yCAAuB;AACvB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B;AAC3B,iDAA+B;AAC/B,2CAAyB"}
@@ -18,8 +18,6 @@ let AISidecarInfoController = class AISidecarInfoController extends (0, interfac
18
18
  async sidecarInfo() {
19
19
  await (0, spawn_sidecar_1.ensureSidecarRunning)();
20
20
  return interface_api_1.HTTPResult.withHeaders({
21
- port: (0, spawn_sidecar_1.getSidecarPort)(),
22
- clientToken: (0, spawn_sidecar_1.getSidecarClientToken)(),
23
21
  isRunning: (0, spawn_sidecar_1.isSidecarRunning)(),
24
22
  hasGivenUp: (0, spawn_sidecar_1.hasSidecarGivenUp)(),
25
23
  disabled: (0, spawn_sidecar_1.isSidecarDisabled)(),
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-info.js","sourceRoot":"","sources":["../../src/routes/sidecar-info.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6DAAwE;AACxE,yDAA+D;AAC/D,gDAAmD;AACnD,8DAOoC;AAG7B,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,IAAA,0BAAU,EAAC,qBAAY,CAAC;IAE7D,AAAN,KAAK,CAAC,WAAW;QAGf,MAAM,IAAA,oCAAoB,GAAE,CAAC;QAC7B,OAAO,0BAAU,CAAC,WAAW,CAC3B;YACE,IAAI,EAAE,IAAA,8BAAc,GAAE;YACtB,WAAW,EAAE,IAAA,qCAAqB,GAAE;YACpC,SAAS,EAAE,IAAA,gCAAgB,GAAE;YAC7B,UAAU,EAAE,IAAA,iCAAiB,GAAE;YAC/B,QAAQ,EAAE,IAAA,iCAAiB,GAAE;SAC9B,EACD,EAAE,eAAe,EAAE,UAAU,EAAE,CAChC,CAAC;IACJ,CAAC;CACF,CAAA;AAjBY,0DAAuB;AAE5B;IADL,IAAA,mBAAG,EAAC,eAAe,CAAC;;;;0DAepB;kCAhBU,uBAAuB;IADnC,IAAA,oBAAa,GAAE;GACH,uBAAuB,CAiBnC"}
1
+ {"version":3,"file":"sidecar-info.js","sourceRoot":"","sources":["../../src/routes/sidecar-info.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6DAAwE;AACxE,yDAA+D;AAC/D,gDAAmD;AACnD,8DAKoC;AAO7B,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,IAAA,0BAAU,EAAC,qBAAY,CAAC;IAE7D,AAAN,KAAK,CAAC,WAAW;QAGf,MAAM,IAAA,oCAAoB,GAAE,CAAC;QAC7B,OAAO,0BAAU,CAAC,WAAW,CAC3B;YACE,SAAS,EAAE,IAAA,gCAAgB,GAAE;YAC7B,UAAU,EAAE,IAAA,iCAAiB,GAAE;YAC/B,QAAQ,EAAE,IAAA,iCAAiB,GAAE;SAC9B,EACD,EAAE,eAAe,EAAE,UAAU,EAAE,CAChC,CAAC;IACJ,CAAC;CACF,CAAA;AAfY,0DAAuB;AAE5B;IADL,IAAA,mBAAG,EAAC,eAAe,CAAC;;;;0DAapB;kCAdU,uBAAuB;IADnC,IAAA,oBAAa,GAAE;GACH,uBAAuB,CAenC"}
@@ -47,6 +47,11 @@ const MODE_OPTIONS: Array<{ value: Mode; label: string; hint: string }> = [
47
47
  { value: 'auto', label: 'Auto', hint: 'Auto-approve every tool action' },
48
48
  ]
49
49
 
50
+ // Safe mode refuses raw edits whatever the mode, and Auto stops short of
51
+ // approving everything there, so the mode hint says so while it is active.
52
+ const SAFE_MODE_MODE_NOTE =
53
+ 'Safe mode: raw edits and shell commands stay blocked, and Auto still asks before other actions.'
54
+
50
55
  const PROVIDER_OPTIONS: Array<{
51
56
  value: ProviderName
52
57
  label: string
@@ -241,6 +246,9 @@ const SELECT_CLASS =
241
246
  <p class="mt-1.5 text-xs text-dimmed">
242
247
  {{ MODE_OPTIONS.find((o) => o.value === settings.mode)?.hint }}
243
248
  </p>
249
+ <p v-if="activeMode === 'safe'" class="mt-1 text-xs text-dimmed">
250
+ {{ SAFE_MODE_MODE_NOTE }}
251
+ </p>
244
252
  </div>
245
253
  <div>
246
254
  <label class="mb-1.5 block text-xs font-medium text-toned">
@@ -4,85 +4,131 @@ import {
4
4
  useDmsRouter,
5
5
  } from '#dms/frontend-module'
6
6
  import {
7
- SIDECAR_HOST_NAME,
7
+ type ChannelClient,
8
+ createChannelClient,
9
+ } from '../runtime/channel-client'
10
+ import {
11
+ createChatTransport,
12
+ exposeChatTransport,
13
+ } from '../runtime/chat-transport'
14
+ import {
15
+ CHANNEL_IDLE_STOP_MS,
16
+ CHANNEL_STATUS_RECONNECTING,
17
+ CHAT_CHANNEL,
18
+ CHATBOX_PATH,
19
+ HELLO_MESSAGE_TYPE,
20
+ HOST_CHANNEL,
21
+ HOST_ROLE,
22
+ JSON_CONTENT_TYPE,
8
23
  SIDECAR_INFO_PATH,
9
- SIDECAR_PROBE_HOST,
10
24
  SIDECAR_STATUS_CONNECTED,
11
25
  SIDECAR_STATUS_REVIVING,
12
26
  SIDECAR_STATUS_UNAVAILABLE,
13
- WS_STATUS_CONNECTED,
14
- WS_STATUS_CONNECTING,
27
+ VISIBILITY_CHANGE_EVENT,
15
28
  } from '../runtime/constants'
16
- import { installCurrentPageTracker } from '../runtime/current-page'
29
+ import {
30
+ buildCurrentPageUpdate,
31
+ installCurrentPageTracker,
32
+ } from '../runtime/current-page'
17
33
  import { registerLauncherAction } from '../runtime/header-action'
18
- import { createHostCommandDispatcher } from '../runtime/host-commands'
34
+ import {
35
+ createHostCommandDispatcher,
36
+ type HostCommandDispatcher,
37
+ } from '../runtime/host-commands'
19
38
  import { installNavigationCompleteEmitter } from '../runtime/navigation-complete'
20
- import { injectOverlay } from '../runtime/overlay'
39
+ import { injectOverlay, type OverlayHandle } from '../runtime/overlay'
21
40
  import { runWhenLoggedIn } from '../runtime/session-gate'
22
41
  import { installToggleShortcut } from '../runtime/shortcuts'
23
42
  import {
24
43
  createSidecarStatusController,
25
44
  type SidecarStatus,
45
+ type SidecarStatusController,
26
46
  } from '../runtime/sidecar-status'
27
- import { createHostWsClient } from '../runtime/ws-client'
28
47
 
29
- function buildIframeUrl(port: number, token: string): string {
48
+ type AuthFetch = ReturnType<typeof useAuthFetch>['$authFetch']
49
+
50
+ interface ChannelDeps {
51
+ $authFetch: AuthFetch
52
+ controller: SidecarStatusController
53
+ dispatchHostCommand: HostCommandDispatcher
54
+ }
55
+
56
+ function buildIframeUrl(): string {
30
57
  // Seed the chatbox with the host's current light/dark mode so its very first
31
58
  // paint matches; the theme bridge keeps it in sync from there on.
32
59
  const mode = document.documentElement.classList.contains('dark')
33
60
  ? 'dark'
34
61
  : 'light'
35
- return `${SIDECAR_PROBE_HOST}:${port}/?theme=${mode}#token=${encodeURIComponent(token)}`
62
+ return `${CHATBOX_PATH}?theme=${mode}`
36
63
  }
37
64
 
38
- async function startAssistant({ vueApp }: DmsAppContext): Promise<void> {
39
- const { $authFetch } = useAuthFetch()
40
- const controller = createSidecarStatusController(() =>
41
- $authFetch(SIDECAR_INFO_PATH),
42
- )
43
- // A null/disabled first probe means the assistant shouldn't appear at all —
44
- // preserve the previous "no icon, no overlay" behavior in that case.
45
- if (!(await controller.init())) return
46
- const overlay = injectOverlay()
47
- if (overlay === null) return
48
-
49
- installToggleShortcut({ onToggle: () => overlay.toggleOpen() })
50
- // Registered unconditionally (even while reviving) so the launcher is present
51
- // through an outage; opening it surfaces the panel's own status placeholder.
52
- registerLauncherAction(() => overlay.toggleFromLauncher())
53
-
54
- const router = useDmsRouter()
55
- // Host-side dev hot reload. A page the agent just wrote is re-registered
56
- // asynchronously, so every overlay-driven navigation waits for the committed
57
- // site layout to serve the route before pushing it.
58
- const devReload = useDmsDevReload()
59
- // Serialized so a burst of navigate commands cannot land out of order.
60
- const dispatchHostCommand = createHostCommandDispatcher({ router, devReload })
61
- const hostWs = createHostWsClient({
62
- sidecarHost: SIDECAR_HOST_NAME,
63
- getPort: () => controller.getPort(),
64
- getClientToken: () => controller.getClientToken(),
65
- onMessage: dispatchHostCommand,
65
+ function createAssistantChannel(deps: ChannelDeps): ChannelClient {
66
+ const chat = createChatTransport({
67
+ send: (msg) => channel.send(CHAT_CHANNEL, msg),
68
+ reconnect: () => channel.reconnectNow(),
69
+ getStatus: () => channel.getStatus(),
70
+ })
71
+ const frameRoutes: Record<string, (raw: string) => void> = {
72
+ [HOST_CHANNEL]: deps.dispatchHostCommand,
73
+ [CHAT_CHANNEL]: chat.deliverFrame,
74
+ }
75
+ const channel = createChannelClient({
76
+ channels: [HOST_CHANNEL, CHAT_CHANNEL],
77
+ post: (path, body) =>
78
+ deps.$authFetch(path, {
79
+ method: 'POST',
80
+ body: JSON.stringify(body),
81
+ headers: { 'content-type': JSON_CONTENT_TYPE },
82
+ }),
83
+ onReady: () => {
84
+ channel.send(HOST_CHANNEL, { type: HELLO_MESSAGE_TYPE, role: HOST_ROLE })
85
+ channel.send(HOST_CHANNEL, buildCurrentPageUpdate(window.location.href))
86
+ chat.announceReady()
87
+ },
88
+ onFrame: (name, raw) => frameRoutes[name]?.(raw),
66
89
  onStatusChange: (status) => {
67
- // A socket drop means the sidecar likely idle-exited/crashed: ask the
68
- // controller to re-probe (relearning a possibly-new port) rather than let
69
- // the socket keep retrying the dead one.
70
- if (status !== WS_STATUS_CONNECTED && status !== WS_STATUS_CONNECTING) {
71
- controller.reportWsDown()
72
- }
90
+ chat.announceStatus(status)
91
+ // The sidecar went away, or dms-ai reloaded: re-probe, which revives
92
+ // an idle-exited sidecar, rather than only waiting on the retries.
93
+ if (status === CHANNEL_STATUS_RECONNECTING) deps.controller.reportChannelDown()
73
94
  },
74
95
  })
96
+ exposeChatTransport(chat.transport)
97
+ return channel
98
+ }
75
99
 
100
+ function followPanel(overlay: OverlayHandle, channel: ChannelClient): () => void {
101
+ let stopTimer: ReturnType<typeof setTimeout> | null = null
102
+ const clearStopTimer = (): void => {
103
+ if (stopTimer !== null) clearTimeout(stopTimer)
104
+ stopTimer = null
105
+ }
106
+ const follow = (): void => {
107
+ clearStopTimer()
108
+ const isNeeded = overlay.isOpen() && document.visibilityState === 'visible'
109
+ if (isNeeded) channel.start()
110
+ else stopTimer = setTimeout(() => channel.stop(), CHANNEL_IDLE_STOP_MS)
111
+ }
112
+ const stopOpenChange = overlay.onOpenChange(follow)
113
+ document.addEventListener(VISIBILITY_CHANGE_EVENT, follow)
114
+ follow()
115
+ return () => {
116
+ clearStopTimer()
117
+ stopOpenChange()
118
+ document.removeEventListener(VISIBILITY_CHANGE_EVENT, follow)
119
+ }
120
+ }
121
+
122
+ function statusApplier(
123
+ overlay: OverlayHandle,
124
+ channel: ChannelClient,
125
+ ): (status: SidecarStatus) => void {
76
126
  let firstApply = true
77
- const applyStatus = (status: SidecarStatus): void => {
127
+ return (status) => {
78
128
  if (status === SIDECAR_STATUS_CONNECTED) {
79
- const port = controller.getPort()
80
- const token = controller.getClientToken()
81
- if (port !== null && token) overlay.repoint(buildIframeUrl(port, token))
82
- // On the first apply the socket was just created and is connecting to the
83
- // right port; only later connects (after a drop or port change) need a
84
- // forced, backoff-skipping reconnect.
85
- if (!firstApply) hostWs.reconnectNow()
129
+ overlay.repoint(buildIframeUrl())
130
+ // Only a connect after a drop needs to skip the channel's backoff.
131
+ if (!firstApply) channel.reconnectNow()
86
132
  } else if (status === SIDECAR_STATUS_REVIVING) {
87
133
  overlay.showPlaceholder('reviving')
88
134
  } else if (status === SIDECAR_STATUS_UNAVAILABLE) {
@@ -92,22 +138,55 @@ async function startAssistant({ vueApp }: DmsAppContext): Promise<void> {
92
138
  }
93
139
  firstApply = false
94
140
  }
95
- controller.subscribe(applyStatus)
141
+ }
96
142
 
143
+ function installHostState(channel: ChannelClient): () => void {
97
144
  const sendWhenConnected = (msg: unknown): void => {
98
- if (!hostWs.isConnected()) return
99
- hostWs.send(msg)
145
+ if (!channel.isConnected()) return
146
+ channel.send(HOST_CHANNEL, msg)
100
147
  }
101
148
  const stopTracking = installCurrentPageTracker({ send: sendWhenConnected })
102
149
  const stopNavigation = installNavigationCompleteEmitter({
103
150
  send: sendWhenConnected,
104
151
  })
105
- const stop = (): void => {
152
+ return () => {
106
153
  stopTracking()
107
154
  stopNavigation()
108
155
  }
109
- vueApp.onUnmount(stop)
110
- import.meta.hot?.dispose(stop)
156
+ }
157
+
158
+ async function startAssistant({ vueApp }: DmsAppContext): Promise<void> {
159
+ const { $authFetch } = useAuthFetch()
160
+ const controller = createSidecarStatusController(() =>
161
+ $authFetch(SIDECAR_INFO_PATH),
162
+ )
163
+ // A null/disabled first probe means the assistant shouldn't appear at all —
164
+ // preserve the previous "no icon, no overlay" behavior in that case.
165
+ if (!(await controller.init())) return
166
+ const overlay = injectOverlay()
167
+ if (overlay === null) return
168
+ installToggleShortcut({ onToggle: () => overlay.toggleOpen() })
169
+ // Registered unconditionally (even while reviving) so the launcher is present
170
+ // through an outage; opening it surfaces the panel's own status placeholder.
171
+ registerLauncherAction(() => overlay.toggleFromLauncher())
172
+ // Host-side dev hot reload: a page the agent just wrote is re-registered
173
+ // asynchronously, so each navigation waits for the layout to serve it, one at
174
+ // a time so a burst of commands cannot land out of order.
175
+ const dispatchHostCommand = createHostCommandDispatcher({
176
+ router: useDmsRouter(),
177
+ devReload: useDmsDevReload(),
178
+ })
179
+ const channel = createAssistantChannel({ $authFetch, controller, dispatchHostCommand })
180
+ controller.subscribe(statusApplier(overlay, channel))
181
+ const stopFollowing = followPanel(overlay, channel)
182
+ const stopHostState = installHostState(channel)
183
+ const teardown = (): void => {
184
+ stopFollowing()
185
+ stopHostState()
186
+ channel.stop()
187
+ }
188
+ vueApp.onUnmount(teardown)
189
+ import.meta.hot?.dispose(teardown)
111
190
  }
112
191
 
113
192
  export default defineDmsPlugin((context) => {