@oar-sh/oar 0.9.20 → 0.9.22
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.
- package/CHANGELOG.md +325 -0
- package/README.md +13 -13
- package/bin/oar.js +2 -2
- package/package.json +1 -1
- package/server/claude-cloud-worker/claude-cloud-session-process.mjs +231 -1
- package/server/claude-cloud-worker/claude-cloud-session-worker.mjs +31 -2
- package/server/claude-cloud-worker/claude-cloud-worker-steering.mjs +34 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +149 -4
- package/server/claude-worker/claude-session-process.mjs +16 -4
- package/server/claude-worker/claude-session-worker-main.mjs +18 -2
- package/server/copilot-worker/copilot-sdk-session-process.mjs +75 -1
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +18 -2
- package/server/cursor-worker/cursor-session-worker.mjs +21 -2
- package/server/cursor-worker/cursor-worker-steering.mjs +20 -0
- package/server/db-schema.mjs +19 -0
- package/server/grok-worker/grok-background-tasks.mjs +10 -0
- package/server/grok-worker/grok-session-worker.mjs +22 -2
- package/server/grok-worker/grok-turn-runner.mjs +4 -0
- package/server/grok-worker/grok-worker-steering.mjs +23 -0
- package/server/migrations/0037-relay-asks-queue-id.mjs +66 -0
- package/server/migrations/0038-turn-seq.mjs +92 -0
- package/server/public/app/about-ui.js +1 -1
- package/server/public/app/about-view.mjs +19 -0
- package/server/public/app/action-confirmations.js +1 -1
- package/server/public/app/activity-replay-state.mjs +160 -42
- package/server/public/app/api-client.js +11 -2
- package/server/public/app/ask-user-view.js +24 -9
- package/server/public/app/attachments-view.js +1 -1
- package/server/public/app/back-guard.mjs +31 -5
- package/server/public/app/bootstrap.js +618 -72
- package/server/public/app/chat-actions-menu-state.mjs +141 -4
- package/server/public/app/chat-header-meta.mjs +27 -0
- package/server/public/app/claude-billing-toggle-ui.js +2 -1
- package/server/public/app/claude-cloud-conversation-ui.js +2 -2
- package/server/public/app/composer-settings-label.mjs +90 -0
- package/server/public/app/composer-settings-ui.js +442 -0
- package/server/public/app/context-menu.mjs +36 -5
- package/server/public/app/context-usage-view.mjs +68 -0
- package/server/public/app/conversation-deep-link.mjs +15 -11
- package/server/public/app/conversation-group-drag.mjs +12 -2
- package/server/public/app/conversation-group-order.mjs +9 -2
- package/server/public/app/conversation-group-state.mjs +47 -10
- package/server/public/app/conversation-origin-ui.js +17 -1
- package/server/public/app/conversation-view.js +309 -49
- package/server/public/app/cwd-menu-placement.mjs +18 -6
- package/server/public/app/cwd-picker.js +36 -6
- package/server/public/app/emoji-view.js +1 -1
- package/server/public/app/font-scaling.js +26 -0
- package/server/public/app/header-priority-actions.mjs +238 -0
- package/server/public/app/held-details.mjs +202 -0
- package/server/public/app/hidden-messages-view.mjs +4 -0
- package/server/public/app/inbox-ui.js +88 -81
- package/server/public/app/inbox-view.mjs +35 -19
- package/server/public/app/journal-view.js +328 -58
- package/server/public/app/live-activity-cap.mjs +103 -0
- package/server/public/app/phone-landscape-ui.js +225 -0
- package/server/public/app/phone-landscape.mjs +99 -0
- package/server/public/app/phone-typing-state.mjs +104 -0
- package/server/public/app/pinned-messages-view.mjs +5 -0
- package/server/public/app/push-settings.js +2 -2
- package/server/public/app/pwa-install.js +24 -3
- package/server/public/app/questions-panel-ui.js +634 -0
- package/server/public/app/questions-stub-view.mjs +40 -0
- package/server/public/app/questions-view.mjs +89 -0
- package/server/public/app/relay-board-view.js +12 -2
- package/server/public/app/relay-event-subscriptions-ui.js +71 -22
- package/server/public/app/relay-event-view.mjs +38 -0
- package/server/public/app/relay-restart-ui.js +2 -2
- package/server/public/app/relay-schedule-view.mjs +32 -6
- package/server/public/app/relay-schedules-ui.js +69 -20
- package/server/public/app/relay-thoughts.mjs +15 -1
- package/server/public/app/remote-relay-origin-view.mjs +41 -23
- package/server/public/app/session-history-ui.js +251 -0
- package/server/public/app/session-history.mjs +599 -0
- package/server/public/app/settings-modal.js +67 -6
- package/server/public/app/settings-tabs.js +12 -0
- package/server/public/app/share-state.js +1 -1
- package/server/public/app/shared-conversations-settings.js +2 -2
- package/server/public/app/socket-handlers.js +47 -13
- package/server/public/app/status-view.mjs +18 -2
- package/server/public/app/store.js +87 -16
- package/server/public/app/stream-view-preference.mjs +60 -0
- package/server/public/app/subagent-fold.mjs +6 -3
- package/server/public/app/subagent-timeline-view.mjs +12 -5
- package/server/public/app/subagent-viewer.mjs +35 -8
- package/server/public/app/tab-strip-fade.mjs +101 -0
- package/server/public/app/tap-actions.js +66 -0
- package/server/public/app/thoughts-preference.mjs +79 -0
- package/server/public/app/turn-stream-view.mjs +124 -0
- package/server/public/app/usage-limit-ui.js +63 -2
- package/server/public/app/viewer-history.mjs +13 -2
- package/server/public/index.html +2603 -1767
- package/server/relay-tools.md +1 -0
- package/server/repositories/message-repository.mjs +33 -0
- package/server/repositories/question-repository.mjs +51 -7
- package/server/repositories/relay-turn-rows.mjs +56 -0
- package/server/repositories/session-repository.mjs +19 -0
- package/server/routes/ask-user-routes.mjs +34 -5
- package/server/routes/claude-cloud-routes.mjs +19 -0
- package/server/routes/messages-routes.mjs +92 -15
- package/server/routes/relay-ask-routes.mjs +15 -5
- package/server/routes/sessions-routes.mjs +117 -12
- package/server/server-runtime.mjs +367 -170
- package/server/services/about-service.mjs +22 -53
- package/server/services/claude-billing-swap-service.mjs +7 -20
- package/server/services/claude-cloud-session-service.mjs +47 -5
- package/server/services/cloudflared-tunnel-service.mjs +7 -0
- package/server/services/idle-timers.mjs +184 -0
- package/server/services/queue-wake-hook.mjs +145 -0
- package/server/services/queue-waker.mjs +127 -0
- package/server/services/relay-ask-service.mjs +64 -5
- package/server/services/relay-event-service.mjs +4 -1
- package/server/services/remote-relay-dispatcher.mjs +84 -18
- package/server/services/session-activity-tracker.mjs +1 -1
- package/server/services/session-history-refresh-service.mjs +4 -0
- package/server/services/session-launch-mutex-service.mjs +23 -0
- package/server/services/session-worker-registry-service.mjs +36 -0
- package/server/services/session-worker-supervisor-service.mjs +36 -0
- package/server/services/session-worker-websocket-service.mjs +183 -31
- package/server/services/ssh-tunnel-manager-service.mjs +7 -0
- package/server/services/worker-recycle-service.mjs +772 -0
- package/server/services/worker-replacement.mjs +197 -0
- package/shared/ask-user-bridge.mjs +9 -6
- package/shared/build-id.mjs +128 -0
- package/shared/relay-asks.mjs +10 -0
- package/shared/relay-events.mjs +1 -0
- package/shared/worker-runtime/heartbeat.mjs +31 -0
- package/shared/worker-runtime/simple-steering-state.mjs +38 -0
- package/shared/worker-runtime/worker-keepalive.mjs +108 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +44 -10
- package/shared/worker-runtime/working-directory.mjs +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { buildIdsDiffer } from "../build-id.mjs";
|
|
2
|
+
|
|
1
3
|
function normalizeText(value) {
|
|
2
4
|
const text = String(value || "").trim();
|
|
3
5
|
return text || null;
|
|
@@ -61,15 +63,23 @@ export function createWorkerWebSocketLink({
|
|
|
61
63
|
getDeliveryHeld = null,
|
|
62
64
|
// Called with the hello's reason once per connection, on the relay's first
|
|
63
65
|
// server.hello: from then on the relay (possibly a restarted one that knows
|
|
64
|
-
// nothing of this worker) has the socket. Later hellos only ack
|
|
65
|
-
// worker.hello
|
|
66
|
+
// nothing of this worker) has the socket. Later hellos only ack a later
|
|
67
|
+
// worker.hello, and are not reported.
|
|
66
68
|
onServerHello = () => {},
|
|
69
|
+
// Called with "server-hello" on every server.hello and with "deliver" on
|
|
70
|
+
// every queue.deliver: the keepalive keeps its busy cadence for a while
|
|
71
|
+
// after either (worker-keepalive.mjs).
|
|
72
|
+
onRelayFrame = () => {},
|
|
67
73
|
getSessionId = () => null,
|
|
68
74
|
getPid = () => null,
|
|
69
75
|
// The kind of worker this is ("claude", "claude-api", ...), stated in
|
|
70
76
|
// every hello and ready: the relay hands a Claude chat's row only to a
|
|
71
77
|
// worker of the row's billing.
|
|
72
78
|
workerKind = null,
|
|
79
|
+
// The build id of the code this worker started on (shared/build-id.mjs),
|
|
80
|
+
// stated in every hello and ready next to the kind: the relay recycles a
|
|
81
|
+
// worker whose build differs from its own once the worker is idle.
|
|
82
|
+
buildId = null,
|
|
73
83
|
minBackoffMs = 1000,
|
|
74
84
|
maxBackoffMs = 8_000,
|
|
75
85
|
jitterMs = 250,
|
|
@@ -82,8 +92,12 @@ export function createWorkerWebSocketLink({
|
|
|
82
92
|
// Failed attempts after the first few are logged at most once per this
|
|
83
93
|
// interval, with a count of the ones in between.
|
|
84
94
|
failureLogIntervalMs = 60_000,
|
|
95
|
+
// The link's own readiness refresh; 0 leaves it to the caller, which then
|
|
96
|
+
// calls refreshReadiness itself (worker-keepalive.mjs does, on its beat).
|
|
85
97
|
readyRefreshMs = 10_000,
|
|
86
|
-
|
|
98
|
+
// Three missed idle beats (30 s each) without any word from the relay: the
|
|
99
|
+
// connection is given up and made again.
|
|
100
|
+
staleConnectionMs = 90_000,
|
|
87
101
|
WebSocketImpl = globalThis.WebSocket,
|
|
88
102
|
setTimeoutImpl = setTimeout,
|
|
89
103
|
clearTimeoutImpl = clearTimeout,
|
|
@@ -121,6 +135,12 @@ export function createWorkerWebSocketLink({
|
|
|
121
135
|
let lastPongAt = null;
|
|
122
136
|
let lastQueueChangedAt = null;
|
|
123
137
|
let lastDeliveryAt = null;
|
|
138
|
+
// The build id the relay named in its latest server.hello (null when it
|
|
139
|
+
// named none), and whether this connection has already logged that it
|
|
140
|
+
// differs from ours: the relay acks every worker.hello with another
|
|
141
|
+
// server.hello, and the log needs the news only once per connection.
|
|
142
|
+
let relayBuildId = null;
|
|
143
|
+
let buildMismatchLogged = false;
|
|
124
144
|
|
|
125
145
|
function getNowMs() {
|
|
126
146
|
const value = Number(now());
|
|
@@ -175,6 +195,7 @@ export function createWorkerWebSocketLink({
|
|
|
175
195
|
sessionId: normalizeText(getSessionId()),
|
|
176
196
|
pid: normalizePositiveInt(getPid()),
|
|
177
197
|
...(normalizeText(workerKind) ? { kind: normalizeText(workerKind) } : {}),
|
|
198
|
+
...(normalizeText(buildId) ? { buildId: normalizeText(buildId) } : {}),
|
|
178
199
|
...(held ? { ready: false } : {}),
|
|
179
200
|
});
|
|
180
201
|
if (sent) lastHelloSentAt = getNowMs();
|
|
@@ -203,6 +224,7 @@ export function createWorkerWebSocketLink({
|
|
|
203
224
|
sessionId: normalizeText(getSessionId()),
|
|
204
225
|
pid: normalizePositiveInt(getPid()),
|
|
205
226
|
...(normalizeText(workerKind) ? { kind: normalizeText(workerKind) } : {}),
|
|
227
|
+
...(normalizeText(buildId) ? { buildId: normalizeText(buildId) } : {}),
|
|
206
228
|
});
|
|
207
229
|
if (sent) lastReadySentAt = getNowMs();
|
|
208
230
|
return sent;
|
|
@@ -295,19 +317,17 @@ export function createWorkerWebSocketLink({
|
|
|
295
317
|
notifyUnready(reason);
|
|
296
318
|
return true;
|
|
297
319
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
notifyHello(reason);
|
|
320
|
+
// notifyReady gates itself: mid-delivery it only goes through when the
|
|
321
|
+
// worker is steering-ready. No hello: the socket's identity, kind and
|
|
322
|
+
// build were bound by the hello at open, and a ready carries them again;
|
|
323
|
+
// a hello would only draw a second server.hello and a second ready.
|
|
305
324
|
void notifyReady(reason);
|
|
306
325
|
return true;
|
|
307
326
|
}
|
|
308
327
|
|
|
309
328
|
function startReadyRefreshTimer() {
|
|
310
329
|
clearReadyRefreshTimer();
|
|
330
|
+
if (Number(readyRefreshMs) === 0) return;
|
|
311
331
|
const intervalMs = Math.max(1000, Number(readyRefreshMs) || 10_000);
|
|
312
332
|
readyRefreshTimer = setIntervalImpl(() => {
|
|
313
333
|
refreshReadiness("readiness-refresh");
|
|
@@ -393,6 +413,7 @@ export function createWorkerWebSocketLink({
|
|
|
393
413
|
clearReadyRefreshTimer();
|
|
394
414
|
// The next connection may reach a different (restarted) relay.
|
|
395
415
|
serverCapabilities = new Set();
|
|
416
|
+
buildMismatchLogged = false;
|
|
396
417
|
}
|
|
397
418
|
if (stopped || !reconnect) return true;
|
|
398
419
|
// A newer socket is already under way and owns the reconnect.
|
|
@@ -434,12 +455,21 @@ export function createWorkerWebSocketLink({
|
|
|
434
455
|
return;
|
|
435
456
|
}
|
|
436
457
|
if (payload?.type === "queue.deliver") {
|
|
458
|
+
try { onRelayFrame("deliver"); } catch {}
|
|
437
459
|
void deliverPending(payload.pending || null, String(payload.reason || "queue-deliver"))
|
|
438
460
|
.finally(() => notifyReady("delivery-complete"));
|
|
439
461
|
return;
|
|
440
462
|
}
|
|
441
463
|
if (payload?.type === "server.hello") {
|
|
464
|
+
try { onRelayFrame("server-hello"); } catch {}
|
|
442
465
|
serverCapabilities = new Set(Array.isArray(payload.capabilities) ? payload.capabilities.map(String) : []);
|
|
466
|
+
relayBuildId = normalizeText(payload.buildId);
|
|
467
|
+
// Only a line in the log: the relay decides when to replace this
|
|
468
|
+
// worker, and the worker takes no action of its own on it.
|
|
469
|
+
if (!buildMismatchLogged && buildIdsDiffer(relayBuildId, normalizeText(buildId))) {
|
|
470
|
+
buildMismatchLogged = true;
|
|
471
|
+
dbg(`relay build ${relayBuildId}, mine ${normalizeText(buildId)}: the relay will recycle this worker when idle`);
|
|
472
|
+
}
|
|
443
473
|
if (!serverHelloSeen) {
|
|
444
474
|
serverHelloSeen = true;
|
|
445
475
|
// The handshake is complete: only now does the backoff start over.
|
|
@@ -536,12 +566,16 @@ export function createWorkerWebSocketLink({
|
|
|
536
566
|
lastPongAt,
|
|
537
567
|
lastQueueChangedAt,
|
|
538
568
|
lastDeliveryAt,
|
|
569
|
+
relayBuildId,
|
|
539
570
|
};
|
|
540
571
|
}
|
|
541
572
|
|
|
542
573
|
return {
|
|
543
574
|
notifyReady,
|
|
544
575
|
notifyUnready,
|
|
576
|
+
refreshReadiness,
|
|
577
|
+
// A delivery in flight or a hold: the keepalive beats at its busy cadence.
|
|
578
|
+
isBusy: () => deliveriesInFlight.size > 0 || deliveryHeld(),
|
|
545
579
|
serverSupports: (capability) => serverCapabilities.has(String(capability || "")),
|
|
546
580
|
start,
|
|
547
581
|
stop,
|
|
@@ -16,7 +16,7 @@ import fs from 'node:fs';
|
|
|
16
16
|
export const WORKING_DIRECTORY_MISSING_CODE = 'working-directory-missing';
|
|
17
17
|
|
|
18
18
|
export const WORKING_DIRECTORY_GUIDANCE =
|
|
19
|
-
'Restore the folder, or choose another working directory for this conversation (
|
|
19
|
+
'Restore the folder, or choose another working directory for this conversation (︙ → Change CWD), '
|
|
20
20
|
+ 'then send the message again.';
|
|
21
21
|
|
|
22
22
|
// What a failed launch says, in the words of the SDKs: Node's spawn error, the
|