@oar-sh/oar 0.9.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 (293) hide show
  1. package/.github/extensions/web-relay/README.md +190 -0
  2. package/.github/extensions/web-relay/extension.mjs +972 -0
  3. package/.github/extensions/web-relay/model-api/model-switching.mjs +338 -0
  4. package/.github/extensions/web-relay/polling/heartbeat.mjs +55 -0
  5. package/.github/extensions/web-relay/polling/polling-loop.mjs +1105 -0
  6. package/.github/extensions/web-relay/runtime/api-client.mjs +34 -0
  7. package/.github/extensions/web-relay/runtime/banner-state.mjs +126 -0
  8. package/.github/extensions/web-relay/runtime/config-loader.mjs +104 -0
  9. package/.github/extensions/web-relay/runtime/debug-log.mjs +27 -0
  10. package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +170 -0
  11. package/.github/extensions/web-relay/runtime/session-binding.mjs +78 -0
  12. package/.github/extensions/web-relay/runtime/session-io.mjs +349 -0
  13. package/.github/extensions/web-relay/runtime/session-join-retry.mjs +44 -0
  14. package/.github/extensions/web-relay/runtime/session-registry.mjs +29 -0
  15. package/.github/extensions/web-relay/runtime/session-runtime-manager.mjs +156 -0
  16. package/.github/extensions/web-relay/runtime/session-sync-bridge.mjs +74 -0
  17. package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +325 -0
  18. package/.github/extensions/web-relay/runtime/workspace-root.mjs +55 -0
  19. package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +386 -0
  20. package/.github/extensions/web-relay/skills/prompt-context.mjs +160 -0
  21. package/.github/extensions/web-relay/skills/question-bridge.mjs +141 -0
  22. package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +463 -0
  23. package/.github/extensions/web-relay/skills/reasoning-stream.mjs +285 -0
  24. package/.github/extensions/web-relay/skills/subagent-lifecycle.mjs +345 -0
  25. package/.github/extensions/web-relay/skills/tool-activity.mjs +243 -0
  26. package/.github/extensions/web-relay/utils/process-utils.mjs +25 -0
  27. package/.github/extensions/web-relay/utils/token-generation.mjs +58 -0
  28. package/CHANGELOG.md +25 -0
  29. package/LICENSE +21 -0
  30. package/README.md +717 -0
  31. package/bin/bat/oar.bat +46 -0
  32. package/bin/oar.js +665 -0
  33. package/package.json +68 -0
  34. package/server/README.md +1379 -0
  35. package/server/claude-worker/claude-attachments.mjs +134 -0
  36. package/server/claude-worker/claude-preview-tool.mjs +57 -0
  37. package/server/claude-worker/claude-sdk-adapter.mjs +420 -0
  38. package/server/claude-worker/claude-session-process.mjs +2391 -0
  39. package/server/claude-worker/claude-session-test-harness.mjs +236 -0
  40. package/server/claude-worker/claude-session-worker.mjs +186 -0
  41. package/server/claude-worker/claude-transcript-relocator.mjs +189 -0
  42. package/server/claude-worker/claude-turn-publisher.mjs +291 -0
  43. package/server/claude-worker/fixtures/workflow-run-record.json +1 -0
  44. package/server/claude-worker/sdk-message-normalizer.mjs +629 -0
  45. package/server/claude-worker/workflow-progress-digest.mjs +269 -0
  46. package/server/copilot-sdk-runtime.mjs +126 -0
  47. package/server/copilot-worker/copilot-attachments.mjs +97 -0
  48. package/server/copilot-worker/copilot-byok-provider.mjs +121 -0
  49. package/server/copilot-worker/copilot-continuation-signals.mjs +349 -0
  50. package/server/copilot-worker/copilot-plan-board.mjs +110 -0
  51. package/server/copilot-worker/copilot-prompt-context.mjs +86 -0
  52. package/server/copilot-worker/copilot-question-bridge.mjs +216 -0
  53. package/server/copilot-worker/copilot-sdk-adapter.mjs +494 -0
  54. package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +800 -0
  55. package/server/copilot-worker/copilot-sdk-session-process.mjs +1729 -0
  56. package/server/copilot-worker/copilot-sdk-session-worker.mjs +167 -0
  57. package/server/copilot-worker/copilot-sdk-test-harness.mjs +329 -0
  58. package/server/copilot-worker/fixtures/abort-turn.json +588 -0
  59. package/server/copilot-worker/fixtures/ask-user-turn.json +1154 -0
  60. package/server/copilot-worker/fixtures/background-timer-continuation.json +445 -0
  61. package/server/copilot-worker/fixtures/background-timer-turn.json +497 -0
  62. package/server/copilot-worker/fixtures/happy-turn.json +566 -0
  63. package/server/copilot-worker/fixtures/quota-turn.json +647 -0
  64. package/server/copilot-worker/fixtures/reasoning-turn.json +625 -0
  65. package/server/copilot-worker/fixtures/resume-turn.json +700 -0
  66. package/server/copilot-worker/fixtures/subagent-turn.json +1378 -0
  67. package/server/copilot-worker/fixtures/tool-permission-turn.json +1293 -0
  68. package/server/cursor-worker/cursor-ask-user-tool.mjs +83 -0
  69. package/server/cursor-worker/cursor-attachments.mjs +88 -0
  70. package/server/cursor-worker/cursor-context-usage.mjs +85 -0
  71. package/server/cursor-worker/cursor-preview-tool.mjs +44 -0
  72. package/server/cursor-worker/cursor-sdk-adapter.mjs +554 -0
  73. package/server/cursor-worker/cursor-session-worker.mjs +145 -0
  74. package/server/cursor-worker/cursor-subagent-roster.mjs +81 -0
  75. package/server/cursor-worker/cursor-turn-runner.mjs +717 -0
  76. package/server/cursor-worker/sdk-message-normalizer.mjs +485 -0
  77. package/server/db-schema.mjs +932 -0
  78. package/server/features.mjs +106 -0
  79. package/server/grok-worker/acp-client.mjs +434 -0
  80. package/server/grok-worker/acp-host-services.mjs +277 -0
  81. package/server/grok-worker/grok-context-usage.mjs +77 -0
  82. package/server/grok-worker/grok-sdk-adapter.mjs +334 -0
  83. package/server/grok-worker/grok-session-worker.mjs +145 -0
  84. package/server/grok-worker/grok-turn-runner.mjs +523 -0
  85. package/server/grok-worker/sdk-message-normalizer.mjs +311 -0
  86. package/server/migrations/0001-add-structured-answer.mjs +80 -0
  87. package/server/migrations/0002-recent-workspace-roots-path-key.mjs +121 -0
  88. package/server/migrations/0003-push-subscriptions.mjs +87 -0
  89. package/server/public/app/action-confirmations.js +267 -0
  90. package/server/public/app/activity-replay-state.mjs +149 -0
  91. package/server/public/app/api-client.js +1082 -0
  92. package/server/public/app/ask-user-view.js +545 -0
  93. package/server/public/app/attachments-view.js +2112 -0
  94. package/server/public/app/auto-compact-window-options.mjs +59 -0
  95. package/server/public/app/background-tasks-view.mjs +494 -0
  96. package/server/public/app/bootstrap.js +4347 -0
  97. package/server/public/app/claude-auth-ui.js +394 -0
  98. package/server/public/app/claude-thinking-options.mjs +38 -0
  99. package/server/public/app/cli-install-ui.js +455 -0
  100. package/server/public/app/code-copy.mjs +85 -0
  101. package/server/public/app/composer-attachment-cache.mjs +151 -0
  102. package/server/public/app/composer-control-state.mjs +107 -0
  103. package/server/public/app/composer-paste.mjs +132 -0
  104. package/server/public/app/context-tier-options.mjs +68 -0
  105. package/server/public/app/context-usage-view.mjs +338 -0
  106. package/server/public/app/conversation-draft-timestamp-utils.mjs +11 -0
  107. package/server/public/app/conversation-preferences.mjs +91 -0
  108. package/server/public/app/conversation-provider-indicator.mjs +88 -0
  109. package/server/public/app/conversation-view.js +2898 -0
  110. package/server/public/app/cwd-menu-placement.mjs +85 -0
  111. package/server/public/app/cwd-picker.js +605 -0
  112. package/server/public/app/emoji-view.js +101 -0
  113. package/server/public/app/external-link-policy.mjs +80 -0
  114. package/server/public/app/file-preview-navigation.mjs +51 -0
  115. package/server/public/app/font-scaling.js +262 -0
  116. package/server/public/app/git-changes-view.js +339 -0
  117. package/server/public/app/git-diff-model.mjs +109 -0
  118. package/server/public/app/grok-auth-ui.js +387 -0
  119. package/server/public/app/image-downscale.mjs +174 -0
  120. package/server/public/app/in-flight-snapshot.mjs +40 -0
  121. package/server/public/app/infinite-loader.js +164 -0
  122. package/server/public/app/journal-view.js +1141 -0
  123. package/server/public/app/known-cwd-options.mjs +41 -0
  124. package/server/public/app/launch-cwd-path.mjs +23 -0
  125. package/server/public/app/live-message-dedupe.mjs +57 -0
  126. package/server/public/app/message-search-view.js +242 -0
  127. package/server/public/app/model-selector-options.mjs +31 -0
  128. package/server/public/app/model-vision-support.mjs +56 -0
  129. package/server/public/app/new-conversation-model-choice.mjs +67 -0
  130. package/server/public/app/plan-usage-view.mjs +307 -0
  131. package/server/public/app/preview-cards.mjs +273 -0
  132. package/server/public/app/preview-command.mjs +103 -0
  133. package/server/public/app/push-settings.js +370 -0
  134. package/server/public/app/pwa-install.js +456 -0
  135. package/server/public/app/question-schema-view.mjs +71 -0
  136. package/server/public/app/reasoning-effort-labels.mjs +42 -0
  137. package/server/public/app/relay-board-view.js +117 -0
  138. package/server/public/app/relay-dot-state.mjs +69 -0
  139. package/server/public/app/relay-error-ctas.mjs +52 -0
  140. package/server/public/app/relay-prompt-sanitizer.mjs +114 -0
  141. package/server/public/app/relay-thoughts.mjs +98 -0
  142. package/server/public/app/relay-watchdog-policy.mjs +29 -0
  143. package/server/public/app/repo-browser-preferences.mjs +79 -0
  144. package/server/public/app/repo-browser-tree-state.mjs +97 -0
  145. package/server/public/app/router.js +657 -0
  146. package/server/public/app/selection-guard.mjs +93 -0
  147. package/server/public/app/settings-modal.js +1484 -0
  148. package/server/public/app/settings-tabs.js +129 -0
  149. package/server/public/app/slash-autocomplete.mjs +188 -0
  150. package/server/public/app/slash-commands.mjs +183 -0
  151. package/server/public/app/socket-handlers.js +703 -0
  152. package/server/public/app/status-store.mjs +242 -0
  153. package/server/public/app/status-view.mjs +315 -0
  154. package/server/public/app/store.js +1454 -0
  155. package/server/public/app/stream-state.mjs +73 -0
  156. package/server/public/app/streaming-dom-patch.mjs +23 -0
  157. package/server/public/app/sync-outbox.mjs +208 -0
  158. package/server/public/app/tap-actions.js +62 -0
  159. package/server/public/app/thread-order.mjs +54 -0
  160. package/server/public/app/tmux-inspector-view.js +422 -0
  161. package/server/public/app/transcript-separators.mjs +282 -0
  162. package/server/public/app-icon-192.png +0 -0
  163. package/server/public/app-icon-512.png +0 -0
  164. package/server/public/app-icon.svg +4 -0
  165. package/server/public/favicon.ico +0 -0
  166. package/server/public/index.html +5743 -0
  167. package/server/public/manifest.webmanifest +32 -0
  168. package/server/public/sw.js +307 -0
  169. package/server/relay-exit-codes.mjs +4 -0
  170. package/server/relay-self-restart.mjs +170 -0
  171. package/server/relay-tools.md +27 -0
  172. package/server/relay.mjs +1026 -0
  173. package/server/repositories/image-conversation-repository.mjs +317 -0
  174. package/server/repositories/message-repository.mjs +434 -0
  175. package/server/repositories/question-repository.mjs +161 -0
  176. package/server/repositories/session-repository.mjs +320 -0
  177. package/server/repositories/share-visibility-statements.mjs +24 -0
  178. package/server/routes/ask-user-routes.mjs +292 -0
  179. package/server/routes/cache-routes.mjs +44 -0
  180. package/server/routes/git-routes.mjs +92 -0
  181. package/server/routes/messages-routes-test-harness.mjs +111 -0
  182. package/server/routes/messages-routes.mjs +7107 -0
  183. package/server/routes/preview-routes.mjs +108 -0
  184. package/server/routes/push-routes.mjs +59 -0
  185. package/server/routes/relay-board-routes.mjs +268 -0
  186. package/server/routes/sessions-routes.mjs +6150 -0
  187. package/server/server-runtime.mjs +7463 -0
  188. package/server/server.js +24 -0
  189. package/server/services/ask-user-routing-service.mjs +117 -0
  190. package/server/services/cache-rebuild-service.mjs +236 -0
  191. package/server/services/claude-auth-service.mjs +578 -0
  192. package/server/services/claude-context-usage.mjs +240 -0
  193. package/server/services/claude-session-root-service.mjs +228 -0
  194. package/server/services/cli-install-service.mjs +1152 -0
  195. package/server/services/cli-process-runner.mjs +181 -0
  196. package/server/services/cloudflared-tunnel-service.mjs +358 -0
  197. package/server/services/context-snapshot-service.mjs +499 -0
  198. package/server/services/context-usage-view.mjs +160 -0
  199. package/server/services/conversation-preferences-service.mjs +208 -0
  200. package/server/services/cursor-dashboard-usage.mjs +217 -0
  201. package/server/services/cursor-session-root-service.mjs +62 -0
  202. package/server/services/delete-archive-service.mjs +154 -0
  203. package/server/services/drives-path-helpers.mjs +66 -0
  204. package/server/services/fixtures/claude-auth-stub.sh +62 -0
  205. package/server/services/fixtures/cli-install-stub.sh +75 -0
  206. package/server/services/fixtures/grok-stub.sh +86 -0
  207. package/server/services/generated-image-cleanup-service.mjs +114 -0
  208. package/server/services/git-changes-service.mjs +195 -0
  209. package/server/services/github-billing-usage.mjs +133 -0
  210. package/server/services/grok-auth-service.mjs +593 -0
  211. package/server/services/grok-billing-usage.mjs +120 -0
  212. package/server/services/image-context-service.mjs +106 -0
  213. package/server/services/image-operation-service.mjs +135 -0
  214. package/server/services/keyed-mutex-service.mjs +82 -0
  215. package/server/services/mime-sniffer.mjs +118 -0
  216. package/server/services/oar-cli-helpers.mjs +109 -0
  217. package/server/services/oar-state-migration-service.mjs +243 -0
  218. package/server/services/plan-usage-claude.mjs +387 -0
  219. package/server/services/plan-usage-contract.mjs +271 -0
  220. package/server/services/plan-usage-copilot.mjs +421 -0
  221. package/server/services/plan-usage-cursor.mjs +566 -0
  222. package/server/services/plan-usage-grok.mjs +436 -0
  223. package/server/services/plan-usage-service.mjs +487 -0
  224. package/server/services/preview-health-probe.mjs +82 -0
  225. package/server/services/preview-proxy-server.mjs +498 -0
  226. package/server/services/preview-registry-service.mjs +457 -0
  227. package/server/services/preview-static-handler.mjs +237 -0
  228. package/server/services/provider-model-selection.mjs +91 -0
  229. package/server/services/provider-reasoning-effort.mjs +94 -0
  230. package/server/services/push-active-device-service.mjs +57 -0
  231. package/server/services/push-dispatch-service.mjs +459 -0
  232. package/server/services/rate-limit-service.mjs +63 -0
  233. package/server/services/relay-bridge-owner-service.mjs +84 -0
  234. package/server/services/relay-cli-launcher-service.mjs +209 -0
  235. package/server/services/relay-prompt-sanitizer.mjs +137 -0
  236. package/server/services/relay-queue-gate-service.mjs +38 -0
  237. package/server/services/relay-restart-orchestrator-service.mjs +935 -0
  238. package/server/services/relay-singleton-guard.mjs +124 -0
  239. package/server/services/safe-served-content.mjs +57 -0
  240. package/server/services/sdk-session-import-service.mjs +271 -0
  241. package/server/services/sdk-session-sync-service.mjs +207 -0
  242. package/server/services/session-history-refresh-service.mjs +497 -0
  243. package/server/services/session-transcript-service.mjs +419 -0
  244. package/server/services/session-worker-launch-service.mjs +786 -0
  245. package/server/services/session-worker-process-service.mjs +319 -0
  246. package/server/services/session-worker-registry-service.mjs +194 -0
  247. package/server/services/session-worker-stop-service.mjs +141 -0
  248. package/server/services/session-worker-supervisor-service.mjs +898 -0
  249. package/server/services/session-worker-websocket-service.mjs +483 -0
  250. package/server/services/ssh-tunnel-manager-service.mjs +423 -0
  251. package/server/services/status-event-service.mjs +158 -0
  252. package/server/services/tmux-inspector-access-policy.mjs +77 -0
  253. package/server/services/tmux-inspector-socket-service.mjs +191 -0
  254. package/server/services/tmux-inspector-stream-service.mjs +552 -0
  255. package/server/services/tunnel-worker-path-guard.mjs +107 -0
  256. package/server/services/upload-sweep.mjs +60 -0
  257. package/server/services/usage-snapshot-helpers.mjs +209 -0
  258. package/server/services/windows-autostart-service.mjs +115 -0
  259. package/server/services/workspace-root-defaults-service.mjs +34 -0
  260. package/server/services/workspace-root-path-policy.mjs +196 -0
  261. package/server/services/workspace-root-relaunch-service.mjs +132 -0
  262. package/server/services/workspace-symlink-guard.mjs +54 -0
  263. package/server/tools/capture-proxy.mjs +173 -0
  264. package/server/tty-console-bootstrap.mjs +75 -0
  265. package/server/uploads/README.md +36 -0
  266. package/server/uploads/image_input.md +540 -0
  267. package/server/windows-terminal-launcher.mjs +29 -0
  268. package/server/workspace-root.mjs +87 -0
  269. package/shared/ask-user-bridge.mjs +131 -0
  270. package/shared/auto-compact-window.mjs +116 -0
  271. package/shared/background-task-timeout.mjs +52 -0
  272. package/shared/claude-thinking.mjs +101 -0
  273. package/shared/context-window-fallbacks.mjs +148 -0
  274. package/shared/control-poller.mjs +115 -0
  275. package/shared/empty-turn-completion.mjs +20 -0
  276. package/shared/model-catalog-freshness.mjs +11 -0
  277. package/shared/model-descriptors.mjs +118 -0
  278. package/shared/model-id.mjs +147 -0
  279. package/shared/model-refresh.mjs +24 -0
  280. package/shared/openai-reasoning.mjs +13 -0
  281. package/shared/plan-lines.mjs +13 -0
  282. package/shared/preview-instructions.mjs +77 -0
  283. package/shared/preview-tool-core.mjs +191 -0
  284. package/shared/provider-routing.mjs +29 -0
  285. package/shared/question-schema.mjs +315 -0
  286. package/shared/question-timeout.mjs +18 -0
  287. package/shared/stream-emit-gating.mjs +28 -0
  288. package/shared/subagent-run-id.mjs +17 -0
  289. package/shared/thought-cap.mjs +13 -0
  290. package/shared/tool-activity.mjs +98 -0
  291. package/shared/turn-ceiling.mjs +53 -0
  292. package/shared/worker-bootstrap.mjs +44 -0
  293. package/shared/worker-crash-guard.mjs +60 -0
@@ -0,0 +1,91 @@
1
+ 'use strict';
2
+
3
+ // Managed providers (OpenAI, Claude, Cursor, Grok) own their own model
4
+ // catalogs: the Copilot catalog does not know these ids, so an explicit
5
+ // selection must be resolved against the provider list alone. Resolving it
6
+ // through the shared catalog first is what let a Cursor request fall back to
7
+ // the global current model (e.g. "claude-opus-5") whenever the requested id
8
+ // was missing from that catalog.
9
+
10
+ export const AUTO_MODEL_SENTINEL = 'auto';
11
+
12
+ export function normalizeProviderModelId(value) {
13
+ return String(value || '').trim();
14
+ }
15
+
16
+ export function isAutoModelId(value) {
17
+ return normalizeProviderModelId(value).toLowerCase() === AUTO_MODEL_SENTINEL;
18
+ }
19
+
20
+ // Availability lists are assembled from settings and discovery, which preserve
21
+ // the provider's casing. Matching is case-insensitive, but the canonical entry
22
+ // is what gets bound and persisted so every later comparison lines up.
23
+ // Callers hold their catalogs as arrays or as Sets built for the older
24
+ // case-sensitive checks, so both are accepted.
25
+ function toModelList(availableModels) {
26
+ if (Array.isArray(availableModels)) return availableModels;
27
+ if (availableModels && typeof availableModels[Symbol.iterator] === 'function') return Array.from(availableModels);
28
+ return [];
29
+ }
30
+
31
+ export function buildCanonicalModelIndex(availableModels = []) {
32
+ const index = new Map();
33
+ for (const value of toModelList(availableModels)) {
34
+ const canonical = normalizeProviderModelId(value);
35
+ if (!canonical) continue;
36
+ const key = canonical.toLowerCase();
37
+ if (!index.has(key)) index.set(key, canonical);
38
+ }
39
+ return index;
40
+ }
41
+
42
+ export function canonicalProviderModelId(requestedModel, availableModels = []) {
43
+ const requested = normalizeProviderModelId(requestedModel);
44
+ if (!requested) return '';
45
+ return buildCanonicalModelIndex(availableModels).get(requested.toLowerCase()) || '';
46
+ }
47
+
48
+ export function isProviderModelAvailable(requestedModel, availableModels = []) {
49
+ return canonicalProviderModelId(requestedModel, availableModels) !== '';
50
+ }
51
+
52
+ // Resolution outcomes:
53
+ // matched — the request named an available model (canonical casing returned)
54
+ // default — no model was requested (blank or "auto"), so the provider default applies
55
+ // ok:false — the request named a model this provider does not offer; callers reject
56
+ export function resolveProviderModelSelection({
57
+ requestedModel = '',
58
+ availableModels = [],
59
+ configuredModel = '',
60
+ } = {}) {
61
+ const requested = normalizeProviderModelId(requestedModel);
62
+ const configured = normalizeProviderModelId(configuredModel);
63
+ const index = buildCanonicalModelIndex([configured, ...toModelList(availableModels)]);
64
+
65
+ if (!requested || isAutoModelId(requested)) {
66
+ return {
67
+ ok: true,
68
+ model: configured,
69
+ requestedModel: requested,
70
+ source: 'default',
71
+ };
72
+ }
73
+
74
+ const canonical = index.get(requested.toLowerCase());
75
+ if (canonical) {
76
+ return {
77
+ ok: true,
78
+ model: canonical,
79
+ requestedModel: requested,
80
+ source: 'matched',
81
+ };
82
+ }
83
+
84
+ return {
85
+ ok: false,
86
+ model: '',
87
+ requestedModel: requested,
88
+ source: 'unavailable',
89
+ availableModels: Array.from(index.values()),
90
+ };
91
+ }
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ import { openAIReasoningEffortsForModel } from '../../shared/openai-reasoning.mjs';
4
+
5
+ // 'ultracode' is not an Agent SDK EffortLevel: it is a relay-side sentinel for
6
+ // the SDK's session-scoped `ultracode` settings flag (xhigh effort plus
7
+ // standing workflow orchestration). It rides the effort ladder end to end —
8
+ // selector option, queue column, message field — and only the Claude worker
9
+ // translates it into `effort: 'xhigh'` + `settings: { ultracode: true }`.
10
+ // It must never be sent as an `effort`/`effortLevel` value: the CLI's schema
11
+ // silently discards unknown levels.
12
+ export const CLAUDE_ULTRACODE_EFFORT = 'ultracode';
13
+ export const DEFAULT_CLAUDE_REASONING_EFFORTS = Object.freeze(['none', 'low', 'medium', 'high', 'xhigh', 'max', CLAUDE_ULTRACODE_EFFORT]);
14
+ export const DEFAULT_GROK_REASONING_EFFORTS = Object.freeze(['none', 'low', 'medium', 'high']);
15
+
16
+ // Ultracode requires an xhigh-capable model (the SDK's stated gate), so the
17
+ // tier is derived from a model's discovered efforts rather than discovery —
18
+ // `supportedModels()` never reports it. Idempotent: augmenting an
19
+ // already-augmented ladder is a no-op.
20
+ export function withClaudeUltracodeTier(efforts) {
21
+ const levels = (Array.isArray(efforts) ? efforts : []).map(normalizeEffort).filter(Boolean);
22
+ if (!levels.includes('xhigh') || levels.includes(CLAUDE_ULTRACODE_EFFORT)) return levels;
23
+ return [...levels, CLAUDE_ULTRACODE_EFFORT];
24
+ }
25
+
26
+ function normalizeEffort(value) {
27
+ return String(value || '').trim().toLowerCase();
28
+ }
29
+
30
+ function effortsForModel(effortsByModel, model) {
31
+ const key = normalizeEffort(model);
32
+ if (!key || !effortsByModel || typeof effortsByModel !== 'object') return null;
33
+ const efforts = effortsByModel[key];
34
+ return Array.isArray(efforts) ? efforts.map(normalizeEffort).filter(Boolean) : null;
35
+ }
36
+
37
+ // Returns the tiers a provider/model pair accepts, or null when the tiers are
38
+ // unknown. null is not "no reasoning": Cursor models that predate effort
39
+ // discovery must pass the request through so the worker can validate it against
40
+ // the live model params instead of the relay silently disabling thinking.
41
+ export function supportedReasoningEffortsForProviderModel({
42
+ providerType = '',
43
+ model = '',
44
+ cursorSettings = null,
45
+ claudeSettings = null,
46
+ grokSettings = null,
47
+ reasoningByModel = null,
48
+ } = {}) {
49
+ const provider = normalizeEffort(providerType);
50
+ if (provider === 'cursor') return effortsForModel(cursorSettings?.effortsByModel, model);
51
+ if (provider === 'claude') {
52
+ return effortsForModel(claudeSettings?.effortsByModel, model) || [...DEFAULT_CLAUDE_REASONING_EFFORTS];
53
+ }
54
+ if (provider === 'grok') {
55
+ return effortsForModel(grokSettings?.effortsByModel, model) || [...DEFAULT_GROK_REASONING_EFFORTS];
56
+ }
57
+ if (provider === 'openai' || provider === 'openai-image') {
58
+ const efforts = openAIReasoningEffortsForModel(model);
59
+ return Array.isArray(efforts) && efforts.length ? efforts.map(normalizeEffort).filter(Boolean) : null;
60
+ }
61
+ return effortsForModel(reasoningByModel, model);
62
+ }
63
+
64
+ // Mirrors the per-turn rules on the message path so a New Chat selection and
65
+ // the first send agree: a supported tier is kept, an unsupported one clamps to
66
+ // the provider's off-switch, an unstated one takes the provider default, and an
67
+ // unknown tier list passes through.
68
+ //
69
+ // `strict` reproduces the one provider that refuses rather than clamps: OpenAI
70
+ // answers an unsupported effort with a 400, so bootstrap must not store a value
71
+ // the first send would reject.
72
+ export function resolveProviderReasoningEffort({
73
+ requestedEffort = '',
74
+ supportedEfforts = null,
75
+ strict = false,
76
+ } = {}) {
77
+ const requested = normalizeEffort(requestedEffort);
78
+ const supported = Array.isArray(supportedEfforts)
79
+ ? supportedEfforts.map(normalizeEffort).filter(Boolean)
80
+ : null;
81
+ if (!supported) return { ok: true, effort: requested, supported: [] };
82
+ const providerDefault = supported.includes('none') ? 'none' : (supported[0] || '');
83
+ if (!requested) return { ok: true, effort: providerDefault, supported };
84
+ if (supported.includes(requested)) return { ok: true, effort: requested, supported };
85
+ if (strict) {
86
+ return {
87
+ ok: false,
88
+ effort: '',
89
+ supported,
90
+ error: `Reasoning effort "${requested}" is not supported`,
91
+ };
92
+ }
93
+ return { ok: true, effort: providerDefault, supported };
94
+ }
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Active-device tracking for push suppression.
5
+ *
6
+ * "Active" means: a connected socket whose client explicitly reported itself
7
+ * visible via a `device_visibility` heartbeat within the staleness window. A
8
+ * connected socket alone is not enough — the client keeps its transport open
9
+ * for a grace period after backgrounding, so a pocketed phone can hold a live
10
+ * socket for tens of seconds.
11
+ */
12
+
13
+ export const DEVICE_VISIBILITY_STALE_MS = 90_000;
14
+
15
+ /**
16
+ * @param {object} options
17
+ * @param {() => Iterable<import('socket.io').Socket>} options.getSockets connected sockets of the main namespace
18
+ * @param {number} [options.staleMs]
19
+ * @param {() => number} [options.now]
20
+ */
21
+ export function createActiveDeviceTracker({ getSockets, staleMs = DEVICE_VISIBILITY_STALE_MS, now = () => Date.now() }) {
22
+ /**
23
+ * Bind visibility tracking to a freshly connected socket. Call from the
24
+ * io `connection` handler.
25
+ */
26
+ function registerSocket(socket) {
27
+ if (!socket) return;
28
+ if (socket.recovered === true && socket.data) {
29
+ // connectionStateRecovery restores socket.data from the recovered
30
+ // session, so a phone that slept with deviceVisible=true would come back
31
+ // still claiming to be visible and suppress push for every device.
32
+ // Reset and wait for the client's connect-time heartbeat (one round
33
+ // trip). The staleness window below is the independent second guard.
34
+ socket.data.deviceVisible = false;
35
+ }
36
+ socket.on('device_visibility', (payload = {}) => {
37
+ const deviceId = String(payload?.deviceId || '').trim();
38
+ if (deviceId) socket.data.deviceId = deviceId;
39
+ socket.data.deviceVisible = payload?.visible === true;
40
+ socket.data.deviceVisibleAt = now();
41
+ });
42
+ }
43
+
44
+ function hasActiveDevice() {
45
+ for (const socket of getSockets()) {
46
+ if (!socket || socket.connected === false) continue;
47
+ if (socket.data?.deviceVisible !== true) continue;
48
+ const visibleAt = Number(socket.data?.deviceVisibleAt || 0);
49
+ if (!Number.isFinite(visibleAt) || visibleAt <= 0) continue;
50
+ if ((now() - visibleAt) > staleMs) continue;
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+
56
+ return { registerSocket, hasActiveDevice };
57
+ }