@oar-sh/oar 0.9.2 → 0.9.4

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 (138) hide show
  1. package/.github/extensions/web-relay/extension.mjs +6 -1
  2. package/.github/extensions/web-relay/skills/tool-activity.mjs +7 -0
  3. package/CHANGELOG.md +336 -0
  4. package/README.md +455 -423
  5. package/bin/oar.js +25 -10
  6. package/package.json +11 -5
  7. package/server/README.md +44 -4
  8. package/server/claude-worker/claude-remote-relay-tool.mjs +99 -0
  9. package/server/claude-worker/claude-sdk-adapter.mjs +26 -4
  10. package/server/claude-worker/claude-session-process.mjs +1073 -69
  11. package/server/claude-worker/claude-session-worker.mjs +45 -4
  12. package/server/claude-worker/claude-turn-publisher.mjs +81 -20
  13. package/server/claude-worker/claude-worker-link-wiring.mjs +9 -0
  14. package/server/claude-worker/sdk-message-normalizer.mjs +7 -0
  15. package/server/copilot-worker/copilot-ask-user-tool.mjs +89 -0
  16. package/server/copilot-worker/copilot-continuation-signals.mjs +55 -0
  17. package/server/copilot-worker/copilot-question-bridge.mjs +34 -12
  18. package/server/copilot-worker/copilot-remote-relay-tool.mjs +59 -0
  19. package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +113 -14
  20. package/server/copilot-worker/copilot-sdk-session-process.mjs +2349 -498
  21. package/server/copilot-worker/copilot-sdk-session-worker.mjs +82 -20
  22. package/server/cursor-worker/cursor-remote-relay-tool.mjs +65 -0
  23. package/server/cursor-worker/cursor-turn-runner.mjs +45 -2
  24. package/server/cursor-worker/sdk-message-normalizer.mjs +25 -0
  25. package/server/db-schema.mjs +28 -0
  26. package/server/grok-worker/acp-client.mjs +92 -6
  27. package/server/grok-worker/grok-turn-runner.mjs +59 -8
  28. package/server/grok-worker/sdk-message-normalizer.mjs +13 -0
  29. package/server/mcp/oar-mcp-server.mjs +473 -0
  30. package/server/migrations/0004-steer-settle-markers.mjs +119 -0
  31. package/server/migrations/0005-remote-relays.mjs +90 -0
  32. package/server/public/app/action-confirmations.js +141 -13
  33. package/server/public/app/api-client.js +125 -2
  34. package/server/public/app/ask-user-view.js +102 -13
  35. package/server/public/app/attachments-view.js +37 -2
  36. package/server/public/app/background-task-timeout-options.mjs +9 -0
  37. package/server/public/app/background-tasks-view.mjs +33 -18
  38. package/server/public/app/bootstrap.js +55 -14
  39. package/server/public/app/composer-control-state.mjs +52 -34
  40. package/server/public/app/conversation-deep-link.mjs +42 -0
  41. package/server/public/app/conversation-draft-sync.mjs +187 -0
  42. package/server/public/app/conversation-list-filter.mjs +86 -0
  43. package/server/public/app/conversation-provider-indicator.mjs +8 -0
  44. package/server/public/app/conversation-view.js +1016 -200
  45. package/server/public/app/emoji-view.js +2 -0
  46. package/server/public/app/font-scaling.js +4 -37
  47. package/server/public/app/host-suspend-ui.js +272 -0
  48. package/server/public/app/journal-view.js +149 -6
  49. package/server/public/app/mention-autocomplete.mjs +298 -0
  50. package/server/public/app/model-selector-options.mjs +1 -1
  51. package/server/public/app/push-settings.js +1 -0
  52. package/server/public/app/pwa-install.js +8 -3
  53. package/server/public/app/question-multi-select.mjs +52 -0
  54. package/server/public/app/relay-prompt-sanitizer.mjs +61 -3
  55. package/server/public/app/remote-relay-origin-view.mjs +76 -0
  56. package/server/public/app/remote-relay-shared.mjs +85 -0
  57. package/server/public/app/remote-relays-settings.js +618 -0
  58. package/server/public/app/remote-relays-store.mjs +126 -0
  59. package/server/public/app/settings-modal.js +12 -8
  60. package/server/public/app/settings-tabs.js +1 -1
  61. package/server/public/app/socket-handlers.js +37 -1
  62. package/server/public/app/steered-turn-merge.mjs +100 -0
  63. package/server/public/app/store.js +126 -15
  64. package/server/public/app/sync-outbox.mjs +16 -8
  65. package/server/public/app/thinking-follow.js +260 -0
  66. package/server/public/app/tool-activity-emoji.mjs +40 -0
  67. package/server/public/app/transcript-viewport-anchor.mjs +210 -0
  68. package/server/public/index.html +463 -17
  69. package/server/public/sw.js +7 -0
  70. package/server/relay-tools.md +7 -1
  71. package/server/repositories/message-repository.mjs +68 -18
  72. package/server/repositories/question-repository.mjs +18 -0
  73. package/server/repositories/remote-relay-repository.mjs +75 -0
  74. package/server/repositories/session-repository.mjs +13 -32
  75. package/server/routes/ask-user-routes.mjs +42 -2
  76. package/server/routes/messages-routes.mjs +572 -56
  77. package/server/routes/remote-relay-routes.mjs +133 -0
  78. package/server/routes/sessions-routes.mjs +378 -84
  79. package/server/server-runtime.mjs +303 -25
  80. package/server/services/github-ci-activity-service.mjs +199 -0
  81. package/server/services/host-suspend-command.mjs +38 -0
  82. package/server/services/host-suspend-service.mjs +254 -0
  83. package/server/services/live-turn-picker.mjs +47 -0
  84. package/server/services/push-dispatch-service.mjs +37 -4
  85. package/server/services/relay-prompt-sanitizer.mjs +17 -2
  86. package/server/services/relay-question-context.mjs +46 -0
  87. package/server/services/remote-relay-caller-context.mjs +341 -0
  88. package/server/services/remote-relay-client.mjs +332 -0
  89. package/server/services/remote-relay-dispatcher.mjs +1500 -0
  90. package/server/services/remote-relay-inbound.mjs +187 -0
  91. package/server/services/remote-relay-pairing.mjs +189 -0
  92. package/server/services/remote-relay-registry-service.mjs +581 -0
  93. package/server/services/sdk-session-import-service.mjs +49 -7
  94. package/server/services/session-worker-launch-service.mjs +131 -12
  95. package/server/services/session-worker-registry-service.mjs +75 -0
  96. package/server/services/session-worker-supervisor-service.mjs +7 -1
  97. package/server/services/session-worker-websocket-service.mjs +23 -1
  98. package/server/services/steer-resend.mjs +45 -0
  99. package/shared/background-task-timeout.mjs +12 -4
  100. package/shared/control-poller.mjs +4 -1
  101. package/shared/question-multi-select.mjs +16 -0
  102. package/shared/relay-stop-failure.mjs +15 -0
  103. package/shared/remote-relay-contract.mjs +539 -0
  104. package/shared/remote-relay-mentions.mjs +129 -0
  105. package/shared/remote-relay-tool-core.mjs +314 -0
  106. package/shared/slash-command-guard.mjs +76 -0
  107. package/shared/steer-note.mjs +66 -0
  108. package/shared/steer-settle-failure.mjs +89 -0
  109. package/shared/steer-settle-markers.mjs +18 -0
  110. package/shared/worker-crash-guard.mjs +40 -25
  111. package/shared/worker-runtime/api-client.mjs +112 -22
  112. package/shared/worker-runtime/heartbeat.mjs +55 -1
  113. package/shared/worker-runtime/runner-link-wiring.mjs +44 -0
  114. package/shared/worker-runtime/worker-websocket-link.mjs +119 -8
  115. package/server/claude-worker/claude-session-test-harness.mjs +0 -250
  116. package/server/claude-worker/fixtures/workflow-run-record.json +0 -1
  117. package/server/copilot-worker/copilot-sdk-test-harness.mjs +0 -382
  118. package/server/copilot-worker/fixtures/abort-turn.json +0 -588
  119. package/server/copilot-worker/fixtures/ask-user-turn.json +0 -1154
  120. package/server/copilot-worker/fixtures/background-timer-continuation.json +0 -445
  121. package/server/copilot-worker/fixtures/background-timer-turn.json +0 -497
  122. package/server/copilot-worker/fixtures/happy-turn.json +0 -566
  123. package/server/copilot-worker/fixtures/quota-turn.json +0 -647
  124. package/server/copilot-worker/fixtures/reasoning-turn.json +0 -625
  125. package/server/copilot-worker/fixtures/resume-turn.json +0 -700
  126. package/server/copilot-worker/fixtures/subagent-turn.json +0 -1378
  127. package/server/copilot-worker/fixtures/tool-permission-turn.json +0 -1293
  128. package/server/routes/messages-routes-test-harness.mjs +0 -111
  129. package/server/services/fixtures/claude-auth-stub.cmd +0 -1
  130. package/server/services/fixtures/claude-auth-stub.mjs +0 -76
  131. package/server/services/fixtures/claude-auth-stub.sh +0 -4
  132. package/server/services/fixtures/cli-install-stub.cmd +0 -1
  133. package/server/services/fixtures/cli-install-stub.mjs +0 -98
  134. package/server/services/fixtures/cli-install-stub.sh +0 -4
  135. package/server/services/fixtures/grok-stub.cmd +0 -1
  136. package/server/services/fixtures/grok-stub.mjs +0 -89
  137. package/server/services/fixtures/grok-stub.sh +0 -4
  138. package/shared/fixtures/copilot-catalog-raw-2026-09-07.json +0 -2363
@@ -0,0 +1,133 @@
1
+ 'use strict';
2
+
3
+ // Remote relays API (plan §5.1, §5.4, §5.6): this relay's identity, the list
4
+ // of paired relays, adding/pairing, self settings and the single endpoint all
5
+ // agent tool calls converge on. Everything is behind `auth` — including the
6
+ // inbound pairing call, which another relay makes with a token that works here.
7
+ //
8
+ // No response carries a stored token: the registry's public views drop it, and
9
+ // the pairing service's answers never contain one.
10
+
11
+ function sendResult(res, result, fallbackStatus = 400) {
12
+ const { status, ...payload } = result || {};
13
+ const code = Number.isInteger(status) ? status : (payload.ok === false ? fallbackStatus : 200);
14
+ return res.status(code).json(payload);
15
+ }
16
+
17
+ function internalError(res, error, what) {
18
+ return res.status(500).json({ error: `${what} failed: ${String(error?.message || error)}` });
19
+ }
20
+
21
+ export function registerRemoteRelayRoutes(app, deps) {
22
+ const {
23
+ auth,
24
+ registry,
25
+ pairing,
26
+ // WP2's dispatcher: `{ dispatch({ conversationId, action, args, req }) →
27
+ // Promise<{ status, body }>, inflight(conversationId) → number }`.
28
+ dispatcher = null,
29
+ } = deps;
30
+
31
+ // Static paths first: Express would otherwise hand them to /:id.
32
+
33
+ // GET /api/relay/identity — who this relay is, for other relays' probes.
34
+ app.get('/api/relay/identity', auth, (_req, res) => {
35
+ res.json(registry.selfIdentity());
36
+ });
37
+
38
+ app.get('/api/remote-relays', auth, (_req, res) => {
39
+ res.json({
40
+ relays: registry.listPublic(),
41
+ self: { ...registry.selfIdentity(), ...registry.getSelfSettings() },
42
+ });
43
+ });
44
+
45
+ // Workers ask at start whether to register the remote_relay tool at all.
46
+ app.get('/api/remote-relays/summary', auth, (_req, res) => {
47
+ const relays = registry.listPublic();
48
+ res.json({
49
+ count: relays.length,
50
+ online: relays.filter((relay) => relay.lastStatus === 'online').length,
51
+ });
52
+ });
53
+
54
+ // The Grok inactivity hold asks whether a tool call is still running.
55
+ app.get('/api/remote-relays/inflight', auth, (req, res) => {
56
+ const conversationId = String(req.query?.conversationId || '').trim();
57
+ let inflight = 0;
58
+ try {
59
+ inflight = Number(dispatcher?.inflight?.(conversationId)) || 0;
60
+ } catch {}
61
+ res.json({ inflight });
62
+ });
63
+
64
+ // POST /api/remote-relays — add a relay from a pasted link (and pair back).
65
+ app.post('/api/remote-relays', auth, async (req, res) => {
66
+ try {
67
+ sendResult(res, await pairing.addFromLink(req.body || {}));
68
+ } catch (error) {
69
+ internalError(res, error, 'Adding the remote relay');
70
+ }
71
+ });
72
+
73
+ // POST /api/remote-relays/pair — another relay introduces itself.
74
+ app.post('/api/remote-relays/pair', auth, async (req, res) => {
75
+ try {
76
+ sendResult(res, await pairing.acceptPairing(req.body || {}));
77
+ } catch (error) {
78
+ internalError(res, error, 'Pairing');
79
+ }
80
+ });
81
+
82
+ // POST /api/remote-relays/tool — every provider adapter's remote_relay call.
83
+ app.post('/api/remote-relays/tool', auth, async (req, res) => {
84
+ if (!dispatcher) return res.status(503).json({ error: 'Remote relay dispatcher unavailable' });
85
+ // A worker that gives up on the call (turn stopped, worker gone) closes
86
+ // the request: withdraw its approval card and stop waiting for the reply.
87
+ const controller = new AbortController();
88
+ res.on('close', () => {
89
+ if (!res.writableFinished) controller.abort();
90
+ });
91
+ try {
92
+ const result = await dispatcher.dispatch({
93
+ conversationId: req.body?.conversationId,
94
+ action: req.body?.action,
95
+ args: req.body?.args || {},
96
+ req,
97
+ signal: controller.signal,
98
+ });
99
+ if (controller.signal.aborted) return;
100
+ res.status(Number.isInteger(result?.status) ? result.status : 200).json(result?.body ?? {});
101
+ } catch (error) {
102
+ internalError(res, error, 'The remote relay call');
103
+ }
104
+ });
105
+
106
+ app.get('/api/settings/remote-relays', auth, (_req, res) => {
107
+ res.json(registry.getSelfSettings());
108
+ });
109
+
110
+ app.post('/api/settings/remote-relays', auth, (req, res) => {
111
+ sendResult(res, registry.setSelfSettings(req.body || {}));
112
+ });
113
+
114
+ // PATCH /api/remote-relays/:id — { permission, url, token, tokenMode }.
115
+ app.patch('/api/remote-relays/:id', auth, (req, res) => {
116
+ sendResult(res, registry.update(req.params?.id, req.body || {}));
117
+ });
118
+
119
+ // DELETE /api/remote-relays/:id — local only; the other side keeps its entry.
120
+ app.delete('/api/remote-relays/:id', auth, (req, res) => {
121
+ sendResult(res, registry.remove(req.params?.id), 404);
122
+ });
123
+
124
+ app.post('/api/remote-relays/:id/check', auth, async (req, res) => {
125
+ try {
126
+ const relay = await registry.check(req.params?.id);
127
+ if (!relay) return res.status(404).json({ error: 'Unknown remote relay' });
128
+ res.json({ ok: true, relay });
129
+ } catch (error) {
130
+ internalError(res, error, 'The check');
131
+ }
132
+ });
133
+ }