@oar-sh/oar 0.9.21 → 0.9.23

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 (82) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/README.md +9 -9
  3. package/bin/oar.js +28 -0
  4. package/package.json +13 -14
  5. package/server/claude-cloud-worker/claude-cloud-session-worker.mjs +9 -1
  6. package/server/claude-worker/claude-cli-pinned-env.mjs +4 -1
  7. package/server/claude-worker/claude-sdk-adapter.mjs +149 -4
  8. package/server/claude-worker/claude-session-process.mjs +9 -4
  9. package/server/claude-worker/claude-session-worker-main.mjs +10 -1
  10. package/server/copilot-worker/copilot-sdk-session-process.mjs +1 -0
  11. package/server/copilot-worker/copilot-sdk-session-worker.mjs +10 -1
  12. package/server/cursor-worker/cursor-session-worker.mjs +9 -1
  13. package/server/db-schema.mjs +19 -0
  14. package/server/grok-worker/grok-background-tasks.mjs +1 -0
  15. package/server/grok-worker/grok-session-worker.mjs +10 -1
  16. package/server/grok-worker/grok-turn-runner.mjs +1 -0
  17. package/server/migrations/0037-relay-asks-queue-id.mjs +66 -0
  18. package/server/migrations/0038-turn-seq.mjs +92 -0
  19. package/server/public/app/activity-replay-state.mjs +160 -42
  20. package/server/public/app/api-client.js +7 -0
  21. package/server/public/app/ask-user-view.js +4 -3
  22. package/server/public/app/bootstrap.js +62 -4
  23. package/server/public/app/chat-actions-menu-state.mjs +2 -0
  24. package/server/public/app/context-usage-view.mjs +68 -0
  25. package/server/public/app/conversation-view.js +306 -49
  26. package/server/public/app/header-priority-actions.mjs +5 -1
  27. package/server/public/app/held-details.mjs +202 -0
  28. package/server/public/app/inbox-ui.js +70 -79
  29. package/server/public/app/inbox-view.mjs +35 -19
  30. package/server/public/app/journal-view.js +17 -0
  31. package/server/public/app/live-activity-cap.mjs +103 -0
  32. package/server/public/app/questions-panel-ui.js +634 -0
  33. package/server/public/app/questions-stub-view.mjs +40 -0
  34. package/server/public/app/questions-view.mjs +89 -0
  35. package/server/public/app/relay-board-view.js +12 -2
  36. package/server/public/app/relay-thoughts.mjs +15 -1
  37. package/server/public/app/session-history-ui.js +1 -0
  38. package/server/public/app/settings-modal.js +6 -0
  39. package/server/public/app/socket-handlers.js +42 -11
  40. package/server/public/app/stream-view-preference.mjs +60 -0
  41. package/server/public/app/subagent-fold.mjs +6 -3
  42. package/server/public/app/subagent-timeline-view.mjs +12 -5
  43. package/server/public/app/subagent-viewer.mjs +8 -6
  44. package/server/public/app/thoughts-preference.mjs +79 -0
  45. package/server/public/app/turn-stream-view.mjs +124 -0
  46. package/server/public/index.html +391 -214
  47. package/server/repositories/message-repository.mjs +10 -0
  48. package/server/repositories/question-repository.mjs +51 -7
  49. package/server/repositories/relay-turn-rows.mjs +56 -0
  50. package/server/routes/ask-user-routes.mjs +34 -5
  51. package/server/routes/messages-routes.mjs +46 -34
  52. package/server/routes/relay-ask-routes.mjs +15 -5
  53. package/server/routes/sessions-routes.mjs +77 -6
  54. package/server/server-runtime.mjs +199 -171
  55. package/server/services/cloudflared-tunnel-service.mjs +7 -0
  56. package/server/services/earlier-database-service.mjs +1 -1
  57. package/server/services/file-serving.mjs +13 -0
  58. package/server/services/idle-timers.mjs +184 -0
  59. package/server/services/native-addon-build-service.mjs +224 -0
  60. package/server/services/oar-cli-helpers.mjs +7 -4
  61. package/server/services/queue-wake-hook.mjs +145 -0
  62. package/server/services/queue-waker.mjs +127 -0
  63. package/server/services/relay-ask-service.mjs +64 -5
  64. package/server/services/relay-console-log.mjs +2 -3
  65. package/server/services/relay-event-service.mjs +4 -1
  66. package/server/services/remote-relay-dispatcher.mjs +23 -4
  67. package/server/services/remote-relay-loopback.mjs +3 -3
  68. package/server/services/sdk-session-sync-service.mjs +1 -1
  69. package/server/services/session-history-refresh-service.mjs +4 -0
  70. package/server/services/session-worker-supervisor-service.mjs +36 -0
  71. package/server/services/session-worker-websocket-service.mjs +143 -15
  72. package/server/services/ssh-tunnel-manager-service.mjs +7 -0
  73. package/server/services/update-install-service.mjs +32 -1
  74. package/shared/ask-user-bridge.mjs +9 -6
  75. package/shared/relay-asks.mjs +10 -0
  76. package/shared/relay-events.mjs +1 -0
  77. package/shared/worker-runtime/heartbeat.mjs +26 -0
  78. package/shared/worker-runtime/worker-keepalive.mjs +108 -0
  79. package/shared/worker-runtime/worker-websocket-link.mjs +21 -10
  80. package/server/services/package-data-rescue-preinstall.mjs +0 -22
  81. package/server/services/package-data-rescue.mjs +0 -139
  82. package/server/tty-console-bootstrap.mjs +0 -75
@@ -0,0 +1,124 @@
1
+ // The stream view of one agent turn, without a DOM: its thoughts, tool calls
2
+ // and reply as one list in the order they happened. The order is the turn's
3
+ // shared number on every thought and tool line (turnSeq, migration 0038).
4
+ // A turn with a row that has none (recorded before, or by an older relay)
5
+ // returns null and keeps the grouped layout. Main thread only: a sub-agent's
6
+ // rows stay in its own row and viewer.
7
+
8
+ import {
9
+ isOmittedActivityMarker,
10
+ normalizeRelayActivityEntry,
11
+ normalizeTurnSeq,
12
+ } from './activity-replay-state.mjs';
13
+
14
+ function escHtml(value) {
15
+ return String(value ?? '').replace(/[&<>"']/g, (ch) => ({
16
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
17
+ }[ch]));
18
+ }
19
+
20
+ /** A '● ' line is a progress note: it stays where the grouped layout puts it. */
21
+ export function isProgressActivityText(text) {
22
+ return String(text || '').startsWith('● ');
23
+ }
24
+
25
+ function firstLine(text, max = 110) {
26
+ const line = String(text || '').split('\n').map((part) => part.trim()).find(Boolean) || '';
27
+ return line.length > max ? `${line.slice(0, max - 1)}…` : line;
28
+ }
29
+
30
+ /**
31
+ * The turn's main-thread entries in order, the reply last:
32
+ * `[{ kind: 'thought'|'tool'|'omitted'|'reply', turnSeq, … }]`, or null when
33
+ * the turn cannot be ordered (a main-thread thought or tool line without a
34
+ * turnSeq) or has no thought or tool line at all. `activities` are the
35
+ * bubble's visible ones (the promoted compaction boundary already taken out);
36
+ * progress lines ('● ') are left out here. The "… earlier tool lines
37
+ * omitted" row the relay's cap adds goes where the first line it stands for
38
+ * was (`metadata.fromTurnSeq`), else right before the first tool line kept
39
+ * after it.
40
+ */
41
+ export function turnStreamEntries({ thoughts = [], activities = [], text = '' } = {}) {
42
+ const entries = [];
43
+ let missing = false;
44
+ for (const thought of Array.isArray(thoughts) ? thoughts : []) {
45
+ if (!thought || thought.subagentRunId || !String(thought.text || '').trim()) continue;
46
+ const turnSeq = normalizeTurnSeq(thought.turnSeq);
47
+ if (turnSeq === null) { missing = true; break; }
48
+ entries.push({
49
+ kind: 'thought',
50
+ turnSeq,
51
+ order: turnSeq,
52
+ reasoningId: String(thought.reasoningId || ''),
53
+ text: String(thought.text),
54
+ });
55
+ }
56
+ if (missing) return null;
57
+
58
+ let pendingMarkers = [];
59
+ for (const item of Array.isArray(activities) ? activities : []) {
60
+ const entry = normalizeRelayActivityEntry(item);
61
+ if (!entry || entry.subagentRunId) continue;
62
+ if (isOmittedActivityMarker(entry)) {
63
+ const marker = { kind: 'omitted', turnSeq: null, text: entry.text, count: Number(entry.metadata.count) || 0 };
64
+ // Where the first line it stands for was; else before the next line kept.
65
+ const from = normalizeTurnSeq(entry.metadata.fromTurnSeq);
66
+ if (from !== null) entries.push({ ...marker, order: from });
67
+ else pendingMarkers.push(marker);
68
+ continue;
69
+ }
70
+ if (isProgressActivityText(entry.text)) continue;
71
+ if (entry.turnSeq === undefined) return null;
72
+ for (const [index, marker] of pendingMarkers.entries()) {
73
+ entries.push({ ...marker, order: entry.turnSeq - 0.5 + index * 0.001 });
74
+ }
75
+ pendingMarkers = [];
76
+ entries.push({
77
+ kind: 'tool',
78
+ turnSeq: entry.turnSeq,
79
+ order: entry.turnSeq,
80
+ text: entry.text,
81
+ ...(entry.metadata ? { metadata: entry.metadata } : {}),
82
+ });
83
+ }
84
+ if (!entries.length) return null;
85
+ const lastOrder = Math.max(...entries.map((entry) => entry.order));
86
+ for (const [index, marker] of pendingMarkers.entries()) {
87
+ entries.push({ ...marker, order: lastOrder + 0.5 + index * 0.001 });
88
+ }
89
+ entries.sort((a, b) => a.order - b.order);
90
+ const ordered = entries.map(({ order, ...entry }) => entry);
91
+ if (String(text || '').trim()) ordered.push({ kind: 'reply', turnSeq: null, text: String(text) });
92
+ return ordered;
93
+ }
94
+
95
+ /**
96
+ * The list's markup. `renderMarkdown(text)` renders a thought and the reply;
97
+ * `decorate(text)` is the tool line's display text (its emoji);
98
+ * `thoughtsOpen` opens the thought folds (closed on a finished turn).
99
+ */
100
+ export function renderTurnStreamMarkup(entries, {
101
+ thoughtsOpen = false,
102
+ renderMarkdown = (text) => `<p>${escHtml(text)}</p>`,
103
+ decorate = (text) => text,
104
+ } = {}) {
105
+ const rows = (Array.isArray(entries) ? entries : []).map((entry) => {
106
+ const seqAttr = entry.turnSeq != null ? ` data-turn-seq="${escHtml(entry.turnSeq)}"` : '';
107
+ if (entry.kind === 'thought') {
108
+ return `<details class="msg-stream-thought"${seqAttr}${entry.reasoningId ? ` data-reasoning-id="${escHtml(entry.reasoningId)}"` : ''}${thoughtsOpen ? ' open' : ''}>`
109
+ + `<summary>💭 ${escHtml(firstLine(entry.text) || 'Thought')}</summary>`
110
+ + `<div class="msg-stream-thought-body">${renderMarkdown(entry.text)}</div></details>`;
111
+ }
112
+ if (entry.kind === 'tool') {
113
+ return `<div class="msg-activity-item msg-stream-tool"${seqAttr}>${escHtml(decorate(entry.text))}</div>`;
114
+ }
115
+ if (entry.kind === 'omitted') {
116
+ return `<div class="msg-stream-omitted">${escHtml(entry.text)}</div>`;
117
+ }
118
+ if (entry.kind === 'reply') {
119
+ return `<div class="msg-stream-reply">${renderMarkdown(entry.text)}</div>`;
120
+ }
121
+ return '';
122
+ });
123
+ return `<div class="msg-turn-stream">${rows.join('')}</div>`;
124
+ }