dextunnel 0.1.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.
- package/LICENSE +211 -0
- package/README.md +112 -0
- package/SECURITY.md +27 -0
- package/SUPPORT.md +43 -0
- package/package.json +44 -0
- package/public/client-shared.js +1831 -0
- package/public/favicon.svg +11 -0
- package/public/host.html +29 -0
- package/public/host.js +2079 -0
- package/public/index.html +28 -0
- package/public/index.js +98 -0
- package/public/live-bridge-lifecycle.js +258 -0
- package/public/live-bridge-retry-state.js +61 -0
- package/public/live-selection-intent.js +79 -0
- package/public/remote-operator-state.js +316 -0
- package/public/remote.html +167 -0
- package/public/remote.js +3967 -0
- package/public/styles.css +2793 -0
- package/public/surface-view-state.js +89 -0
- package/public/voice-dictation.js +45 -0
- package/src/bin/desktop-rehydration-smoke.mjs +111 -0
- package/src/bin/dextunnel.mjs +41 -0
- package/src/bin/doctor.mjs +48 -0
- package/src/bin/launch-attest.mjs +39 -0
- package/src/bin/launch-status.mjs +49 -0
- package/src/bin/mobile-link-proxy.mjs +221 -0
- package/src/bin/mobile-proof.mjs +164 -0
- package/src/bin/mobile-transport-smoke.mjs +200 -0
- package/src/bin/probe-codex-app-server-write.mjs +36 -0
- package/src/bin/probe-codex-app-server.mjs +30 -0
- package/src/lib/agent-room-context.mjs +54 -0
- package/src/lib/agent-room-runtime.mjs +355 -0
- package/src/lib/agent-room-service.mjs +335 -0
- package/src/lib/agent-room-state.mjs +406 -0
- package/src/lib/agent-room-store.mjs +71 -0
- package/src/lib/agent-room-text.mjs +48 -0
- package/src/lib/app-server-contract.mjs +66 -0
- package/src/lib/app-server-runtime.mjs +60 -0
- package/src/lib/attachment-service.mjs +119 -0
- package/src/lib/bridge-api-handler.mjs +719 -0
- package/src/lib/bridge-runtime-lifecycle.mjs +51 -0
- package/src/lib/bridge-status-builder.mjs +60 -0
- package/src/lib/codex-app-server-client.mjs +1511 -0
- package/src/lib/companion-state.mjs +453 -0
- package/src/lib/control-lease-service.mjs +180 -0
- package/src/lib/debug-harness-service.mjs +173 -0
- package/src/lib/desktop-integration.mjs +146 -0
- package/src/lib/desktop-rehydration-smoke.mjs +269 -0
- package/src/lib/dextunnel-cli.mjs +122 -0
- package/src/lib/discovery-docs.mjs +1321 -0
- package/src/lib/fake-codex-app-server-bridge.mjs +340 -0
- package/src/lib/install-preflight.mjs +373 -0
- package/src/lib/interaction-resolution-service.mjs +185 -0
- package/src/lib/interaction-state.mjs +360 -0
- package/src/lib/launch-release-bar.mjs +158 -0
- package/src/lib/live-control-state.mjs +107 -0
- package/src/lib/live-payload-builder.mjs +298 -0
- package/src/lib/live-selection-transition-state.mjs +49 -0
- package/src/lib/live-transcript-state.mjs +549 -0
- package/src/lib/mobile-network-profile.mjs +39 -0
- package/src/lib/mock-codex-adapter.mjs +62 -0
- package/src/lib/operator-diagnostics.mjs +82 -0
- package/src/lib/repo-changes-service.mjs +527 -0
- package/src/lib/runtime-config.mjs +106 -0
- package/src/lib/selection-state-service.mjs +214 -0
- package/src/lib/session-store.mjs +355 -0
- package/src/lib/shared-room-state.mjs +473 -0
- package/src/lib/shared-selection-state.mjs +40 -0
- package/src/lib/sse-hub.mjs +35 -0
- package/src/lib/static-surface-service.mjs +71 -0
- package/src/lib/surface-access.mjs +189 -0
- package/src/lib/surface-presence-service.mjs +118 -0
- package/src/lib/surface-request-guard.mjs +52 -0
- package/src/lib/thread-sync-state.mjs +536 -0
- package/src/lib/watcher-lifecycle.mjs +287 -0
- package/src/server.mjs +1446 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
export function createLiveTranscriptStateService({
|
|
2
|
+
liveState,
|
|
3
|
+
mapThreadItemToCompanionEntry,
|
|
4
|
+
nowIso = () => new Date().toISOString(),
|
|
5
|
+
getDefaultCwd = () => process.cwd(),
|
|
6
|
+
extractNotificationDelta = (params = {}) => (
|
|
7
|
+
params.delta ??
|
|
8
|
+
params.textDelta ??
|
|
9
|
+
params.outputDelta ??
|
|
10
|
+
params.output ??
|
|
11
|
+
params.chunk ??
|
|
12
|
+
""
|
|
13
|
+
),
|
|
14
|
+
visibleTranscriptLimit = 120
|
|
15
|
+
} = {}) {
|
|
16
|
+
function mergeThreadSummary(threadId, patch = {}) {
|
|
17
|
+
if (!threadId) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
liveState.threads = liveState.threads.map((thread) => (
|
|
22
|
+
thread.id === threadId
|
|
23
|
+
? {
|
|
24
|
+
...thread,
|
|
25
|
+
...patch,
|
|
26
|
+
id: thread.id
|
|
27
|
+
}
|
|
28
|
+
: thread
|
|
29
|
+
));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ensureLiveSelectedSnapshot(threadId, cwd = null) {
|
|
33
|
+
const current = liveState.selectedThreadSnapshot;
|
|
34
|
+
if (current?.thread?.id === threadId) {
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const summary = liveState.threads.find((thread) => thread.id === threadId) || null;
|
|
39
|
+
const nextSnapshot = {
|
|
40
|
+
thread: {
|
|
41
|
+
activeTurnId: null,
|
|
42
|
+
activeTurnStatus: null,
|
|
43
|
+
cwd: cwd || summary?.cwd || getDefaultCwd(),
|
|
44
|
+
id: threadId,
|
|
45
|
+
lastTurnId: null,
|
|
46
|
+
lastTurnStatus: null,
|
|
47
|
+
name: summary?.name || null,
|
|
48
|
+
path: null,
|
|
49
|
+
preview: summary?.preview || null,
|
|
50
|
+
source: summary?.source || null,
|
|
51
|
+
status: summary?.status || null,
|
|
52
|
+
updatedAt: summary?.updatedAt || nowIso()
|
|
53
|
+
},
|
|
54
|
+
transcript: [],
|
|
55
|
+
transcriptCount: 0
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
liveState.selectedThreadSnapshot = nextSnapshot;
|
|
59
|
+
return nextSnapshot;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function commitLiveSelectedSnapshot(snapshot) {
|
|
63
|
+
liveState.selectedThreadSnapshot = snapshot;
|
|
64
|
+
liveState.lastSyncAt = nowIso();
|
|
65
|
+
liveState.lastError = null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function clampTranscriptEntries(entries) {
|
|
69
|
+
return entries.slice(-visibleTranscriptLimit);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function upsertTranscriptEntry(snapshot, entry) {
|
|
73
|
+
const transcript = Array.isArray(snapshot?.transcript) ? [...snapshot.transcript] : [];
|
|
74
|
+
const transcriptCountBase = Number.isFinite(snapshot?.transcriptCount)
|
|
75
|
+
? snapshot.transcriptCount
|
|
76
|
+
: transcript.length;
|
|
77
|
+
const nextEntry = {
|
|
78
|
+
...entry
|
|
79
|
+
};
|
|
80
|
+
const index = nextEntry.itemId
|
|
81
|
+
? transcript.findIndex((existing) => existing.itemId && existing.itemId === nextEntry.itemId)
|
|
82
|
+
: -1;
|
|
83
|
+
|
|
84
|
+
if (index >= 0) {
|
|
85
|
+
transcript[index] = {
|
|
86
|
+
...transcript[index],
|
|
87
|
+
...nextEntry,
|
|
88
|
+
itemId: transcript[index].itemId || nextEntry.itemId || null,
|
|
89
|
+
text: nextEntry.text ?? transcript[index].text,
|
|
90
|
+
timestamp: nextEntry.timestamp || transcript[index].timestamp || null
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
...snapshot,
|
|
94
|
+
transcript: clampTranscriptEntries(transcript),
|
|
95
|
+
transcriptCount: Math.max(transcriptCountBase, transcript.length)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
transcript.push(nextEntry);
|
|
100
|
+
return {
|
|
101
|
+
...snapshot,
|
|
102
|
+
transcript: clampTranscriptEntries(transcript),
|
|
103
|
+
transcriptCount: Math.max(transcriptCountBase + 1, transcript.length)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function updateTranscriptEntryByItemId(snapshot, itemId, updater) {
|
|
108
|
+
if (!itemId) {
|
|
109
|
+
return snapshot;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const transcript = Array.isArray(snapshot?.transcript) ? [...snapshot.transcript] : [];
|
|
113
|
+
const index = transcript.findIndex((entry) => entry.itemId && entry.itemId === itemId);
|
|
114
|
+
if (index < 0) {
|
|
115
|
+
return snapshot;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const updated = updater(transcript[index]);
|
|
119
|
+
if (!updated) {
|
|
120
|
+
return snapshot;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
transcript[index] = updated;
|
|
124
|
+
return {
|
|
125
|
+
...snapshot,
|
|
126
|
+
transcript: clampTranscriptEntries(transcript),
|
|
127
|
+
transcriptCount: Number.isFinite(snapshot?.transcriptCount) ? snapshot.transcriptCount : transcript.length
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function applyTranscriptItemUpdate({ threadId, cwd, turnId, item, timestamp = nowIso() }) {
|
|
132
|
+
if (!threadId || !item) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
137
|
+
const nextSnapshot = upsertTranscriptEntry(
|
|
138
|
+
{
|
|
139
|
+
...snapshot,
|
|
140
|
+
thread: {
|
|
141
|
+
...snapshot.thread,
|
|
142
|
+
cwd: cwd || snapshot.thread?.cwd || null,
|
|
143
|
+
lastTurnId: turnId || snapshot.thread?.lastTurnId || null,
|
|
144
|
+
updatedAt: timestamp
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
mapThreadItemToCompanionEntry(item, {
|
|
148
|
+
id: turnId || snapshot.thread?.lastTurnId || null,
|
|
149
|
+
startedAt: timestamp,
|
|
150
|
+
updatedAt: timestamp
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
commitLiveSelectedSnapshot(nextSnapshot);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function appendToTranscriptItem({ threadId, cwd, turnId, itemId, defaults, appendText, timestamp = nowIso() }) {
|
|
159
|
+
if (!threadId || !itemId || !appendText) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
164
|
+
const existing = (snapshot.transcript || []).find((entry) => entry.itemId === itemId) || null;
|
|
165
|
+
const baseEntry = existing || {
|
|
166
|
+
itemId,
|
|
167
|
+
kind: defaults.kind,
|
|
168
|
+
phase: defaults.phase || null,
|
|
169
|
+
role: defaults.role,
|
|
170
|
+
text: "",
|
|
171
|
+
timestamp,
|
|
172
|
+
turnId: turnId || snapshot.thread?.lastTurnId || null
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const nextEntry = {
|
|
176
|
+
...baseEntry,
|
|
177
|
+
...defaults,
|
|
178
|
+
itemId,
|
|
179
|
+
text: `${baseEntry.text || ""}${appendText}`,
|
|
180
|
+
timestamp,
|
|
181
|
+
turnId: turnId || baseEntry.turnId || null
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const nextSnapshot = upsertTranscriptEntry(
|
|
185
|
+
{
|
|
186
|
+
...snapshot,
|
|
187
|
+
thread: {
|
|
188
|
+
...snapshot.thread,
|
|
189
|
+
cwd: cwd || snapshot.thread?.cwd || null,
|
|
190
|
+
lastTurnId: turnId || snapshot.thread?.lastTurnId || null,
|
|
191
|
+
updatedAt: timestamp
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
nextEntry
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
commitLiveSelectedSnapshot(nextSnapshot);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function appendCommandOutputDelta({ threadId, cwd, turnId, itemId, delta, timestamp = nowIso() }) {
|
|
202
|
+
if (!threadId || !itemId || !delta) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
207
|
+
const nextSnapshot = updateTranscriptEntryByItemId(snapshot, itemId, (entry) => {
|
|
208
|
+
const needsSeparator = entry.text && !entry.text.includes("\n");
|
|
209
|
+
return {
|
|
210
|
+
...entry,
|
|
211
|
+
text: `${entry.text || ""}${needsSeparator ? "\n" : ""}${delta}`,
|
|
212
|
+
timestamp,
|
|
213
|
+
turnId: turnId || entry.turnId || null
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (nextSnapshot === snapshot) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
commitLiveSelectedSnapshot({
|
|
222
|
+
...nextSnapshot,
|
|
223
|
+
thread: {
|
|
224
|
+
...nextSnapshot.thread,
|
|
225
|
+
updatedAt: timestamp
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function appendFileChangeOutputDelta({ threadId, cwd, turnId, itemId, delta, timestamp = nowIso() }) {
|
|
232
|
+
if (!threadId || !itemId || !delta) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
237
|
+
const nextSnapshot = updateTranscriptEntryByItemId(snapshot, itemId, (entry) => {
|
|
238
|
+
const separator = entry.text && !entry.text.endsWith("\n") ? "\n" : "";
|
|
239
|
+
return {
|
|
240
|
+
...entry,
|
|
241
|
+
text: `${entry.text || ""}${separator}${delta}`,
|
|
242
|
+
timestamp,
|
|
243
|
+
turnId: turnId || entry.turnId || null
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (nextSnapshot === snapshot) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
commitLiveSelectedSnapshot({
|
|
252
|
+
...nextSnapshot,
|
|
253
|
+
thread: {
|
|
254
|
+
...nextSnapshot.thread,
|
|
255
|
+
updatedAt: timestamp
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function applyTurnPlanUpdate({ threadId, cwd, turnId, explanation = null, plan = null, timestamp = nowIso() }) {
|
|
262
|
+
if (!threadId) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
267
|
+
const nextThread = {
|
|
268
|
+
...snapshot.thread,
|
|
269
|
+
cwd: cwd || snapshot.thread?.cwd || null,
|
|
270
|
+
lastTurnId: turnId || snapshot.thread?.lastTurnId || null,
|
|
271
|
+
livePlan: Array.isArray(plan) ? plan : snapshot.thread?.livePlan || null,
|
|
272
|
+
planExplanation: explanation ?? snapshot.thread?.planExplanation ?? null,
|
|
273
|
+
updatedAt: timestamp
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
commitLiveSelectedSnapshot({
|
|
277
|
+
...snapshot,
|
|
278
|
+
thread: nextThread
|
|
279
|
+
});
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function applyThreadTokenUsageUpdate({ threadId, cwd, tokenUsage, timestamp = nowIso() }) {
|
|
284
|
+
if (!threadId || !tokenUsage) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
289
|
+
commitLiveSelectedSnapshot({
|
|
290
|
+
...snapshot,
|
|
291
|
+
thread: {
|
|
292
|
+
...snapshot.thread,
|
|
293
|
+
cwd: cwd || snapshot.thread?.cwd || null,
|
|
294
|
+
tokenUsage,
|
|
295
|
+
updatedAt: timestamp
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function applyTurnLifecycleUpdate({ threadId, cwd, turn, timestamp = nowIso() }) {
|
|
302
|
+
if (!threadId) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
307
|
+
const status = turn?.status || null;
|
|
308
|
+
const nextThread = {
|
|
309
|
+
...snapshot.thread,
|
|
310
|
+
activeTurnId: status === "inProgress" ? turn?.id || snapshot.thread?.activeTurnId || null : null,
|
|
311
|
+
activeTurnStatus: status === "inProgress" ? status : null,
|
|
312
|
+
cwd: cwd || turn?.cwd || snapshot.thread?.cwd || null,
|
|
313
|
+
id: threadId,
|
|
314
|
+
lastTurnId: turn?.id || snapshot.thread?.lastTurnId || null,
|
|
315
|
+
lastTurnStatus: status || snapshot.thread?.lastTurnStatus || null,
|
|
316
|
+
status: status === "inProgress" ? "inProgress" : snapshot.thread?.status || null,
|
|
317
|
+
updatedAt: turn?.updatedAt || turn?.startedAt || timestamp
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
if (status && status !== "inProgress") {
|
|
321
|
+
nextThread.status = status;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
commitLiveSelectedSnapshot({
|
|
325
|
+
...snapshot,
|
|
326
|
+
thread: nextThread
|
|
327
|
+
});
|
|
328
|
+
mergeThreadSummary(threadId, {
|
|
329
|
+
cwd: nextThread.cwd,
|
|
330
|
+
status: nextThread.status,
|
|
331
|
+
updatedAt: nextThread.updatedAt
|
|
332
|
+
});
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function applyThreadNameUpdate({ threadId, cwd, name, timestamp = nowIso() }) {
|
|
337
|
+
if (!threadId || !name) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
342
|
+
commitLiveSelectedSnapshot({
|
|
343
|
+
...snapshot,
|
|
344
|
+
thread: {
|
|
345
|
+
...snapshot.thread,
|
|
346
|
+
name,
|
|
347
|
+
updatedAt: timestamp
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
mergeThreadSummary(threadId, {
|
|
351
|
+
name,
|
|
352
|
+
updatedAt: timestamp
|
|
353
|
+
});
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function applyThreadStatusUpdate({ threadId, cwd, status, timestamp = nowIso() }) {
|
|
358
|
+
if (!threadId) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const snapshot = ensureLiveSelectedSnapshot(threadId, cwd);
|
|
363
|
+
commitLiveSelectedSnapshot({
|
|
364
|
+
...snapshot,
|
|
365
|
+
thread: {
|
|
366
|
+
...snapshot.thread,
|
|
367
|
+
status: status || snapshot.thread?.status || null,
|
|
368
|
+
updatedAt: timestamp
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
mergeThreadSummary(threadId, {
|
|
372
|
+
status: status || null,
|
|
373
|
+
updatedAt: timestamp
|
|
374
|
+
});
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function applyWatcherNotification(message, { threadId, cwd }) {
|
|
379
|
+
const params = message.params || {};
|
|
380
|
+
const eventThreadId = params.threadId || threadId;
|
|
381
|
+
const eventTurnId = params.turnId || params.turn?.id || null;
|
|
382
|
+
const timestamp = nowIso();
|
|
383
|
+
|
|
384
|
+
if (eventThreadId !== liveState.selectedThreadId) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
switch (message.method) {
|
|
389
|
+
case "turn/started":
|
|
390
|
+
return applyTurnLifecycleUpdate({
|
|
391
|
+
cwd,
|
|
392
|
+
threadId: eventThreadId,
|
|
393
|
+
timestamp,
|
|
394
|
+
turn: params.turn || { id: eventTurnId, status: "inProgress" }
|
|
395
|
+
});
|
|
396
|
+
case "turn/completed":
|
|
397
|
+
return applyTurnLifecycleUpdate({
|
|
398
|
+
cwd,
|
|
399
|
+
threadId: eventThreadId,
|
|
400
|
+
timestamp,
|
|
401
|
+
turn: params.turn || { id: eventTurnId, status: "completed" }
|
|
402
|
+
});
|
|
403
|
+
case "item/started":
|
|
404
|
+
case "item/completed":
|
|
405
|
+
return applyTranscriptItemUpdate({
|
|
406
|
+
cwd,
|
|
407
|
+
item: params.item,
|
|
408
|
+
threadId: eventThreadId,
|
|
409
|
+
timestamp,
|
|
410
|
+
turnId: eventTurnId
|
|
411
|
+
});
|
|
412
|
+
case "item/agentMessage/delta":
|
|
413
|
+
return appendToTranscriptItem({
|
|
414
|
+
appendText: extractNotificationDelta(params),
|
|
415
|
+
cwd,
|
|
416
|
+
defaults: {
|
|
417
|
+
kind: "message",
|
|
418
|
+
phase: null,
|
|
419
|
+
role: "assistant"
|
|
420
|
+
},
|
|
421
|
+
itemId: params.itemId || null,
|
|
422
|
+
threadId: eventThreadId,
|
|
423
|
+
timestamp,
|
|
424
|
+
turnId: eventTurnId
|
|
425
|
+
});
|
|
426
|
+
case "item/plan/delta":
|
|
427
|
+
return appendToTranscriptItem({
|
|
428
|
+
appendText: extractNotificationDelta(params),
|
|
429
|
+
cwd,
|
|
430
|
+
defaults: {
|
|
431
|
+
kind: "plan",
|
|
432
|
+
phase: null,
|
|
433
|
+
role: "system"
|
|
434
|
+
},
|
|
435
|
+
itemId: params.itemId || null,
|
|
436
|
+
threadId: eventThreadId,
|
|
437
|
+
timestamp,
|
|
438
|
+
turnId: eventTurnId
|
|
439
|
+
});
|
|
440
|
+
case "item/reasoning/summaryTextDelta":
|
|
441
|
+
case "item/reasoning/textDelta":
|
|
442
|
+
return appendToTranscriptItem({
|
|
443
|
+
appendText: extractNotificationDelta(params),
|
|
444
|
+
cwd,
|
|
445
|
+
defaults: {
|
|
446
|
+
kind: "reasoning",
|
|
447
|
+
phase: null,
|
|
448
|
+
role: "system"
|
|
449
|
+
},
|
|
450
|
+
itemId: params.itemId || null,
|
|
451
|
+
threadId: eventThreadId,
|
|
452
|
+
timestamp,
|
|
453
|
+
turnId: eventTurnId
|
|
454
|
+
});
|
|
455
|
+
case "item/reasoning/summaryPartAdded":
|
|
456
|
+
return appendToTranscriptItem({
|
|
457
|
+
appendText: "\n\n",
|
|
458
|
+
cwd,
|
|
459
|
+
defaults: {
|
|
460
|
+
kind: "reasoning",
|
|
461
|
+
phase: null,
|
|
462
|
+
role: "system"
|
|
463
|
+
},
|
|
464
|
+
itemId: params.itemId || null,
|
|
465
|
+
threadId: eventThreadId,
|
|
466
|
+
timestamp,
|
|
467
|
+
turnId: eventTurnId
|
|
468
|
+
});
|
|
469
|
+
case "item/commandExecution/outputDelta":
|
|
470
|
+
return appendCommandOutputDelta({
|
|
471
|
+
cwd,
|
|
472
|
+
delta: extractNotificationDelta(params),
|
|
473
|
+
itemId: params.itemId || null,
|
|
474
|
+
threadId: eventThreadId,
|
|
475
|
+
timestamp,
|
|
476
|
+
turnId: eventTurnId
|
|
477
|
+
});
|
|
478
|
+
case "item/fileChange/outputDelta":
|
|
479
|
+
return appendFileChangeOutputDelta({
|
|
480
|
+
cwd,
|
|
481
|
+
delta: extractNotificationDelta(params),
|
|
482
|
+
itemId: params.itemId || null,
|
|
483
|
+
threadId: eventThreadId,
|
|
484
|
+
timestamp,
|
|
485
|
+
turnId: eventTurnId
|
|
486
|
+
});
|
|
487
|
+
case "turn/plan/updated":
|
|
488
|
+
return applyTurnPlanUpdate({
|
|
489
|
+
cwd,
|
|
490
|
+
explanation: params.explanation || null,
|
|
491
|
+
plan: params.plan || null,
|
|
492
|
+
threadId: eventThreadId,
|
|
493
|
+
timestamp,
|
|
494
|
+
turnId: eventTurnId
|
|
495
|
+
});
|
|
496
|
+
case "thread/tokenUsage/updated":
|
|
497
|
+
return applyThreadTokenUsageUpdate({
|
|
498
|
+
cwd,
|
|
499
|
+
threadId: eventThreadId,
|
|
500
|
+
timestamp,
|
|
501
|
+
tokenUsage: params.tokenUsage || params.usage || params.thread?.tokenUsage || null
|
|
502
|
+
});
|
|
503
|
+
case "thread/compacted":
|
|
504
|
+
return applyTranscriptItemUpdate({
|
|
505
|
+
cwd,
|
|
506
|
+
item: {
|
|
507
|
+
id: params.itemId || `thread-compacted-${eventTurnId || timestamp}`,
|
|
508
|
+
type: "contextCompaction"
|
|
509
|
+
},
|
|
510
|
+
threadId: eventThreadId,
|
|
511
|
+
timestamp,
|
|
512
|
+
turnId: eventTurnId
|
|
513
|
+
});
|
|
514
|
+
case "thread/name/updated":
|
|
515
|
+
return applyThreadNameUpdate({
|
|
516
|
+
cwd,
|
|
517
|
+
name: params.name || params.thread?.name || null,
|
|
518
|
+
threadId: eventThreadId,
|
|
519
|
+
timestamp
|
|
520
|
+
});
|
|
521
|
+
case "thread/status/changed":
|
|
522
|
+
return applyThreadStatusUpdate({
|
|
523
|
+
cwd,
|
|
524
|
+
status: params.status || params.thread?.status || null,
|
|
525
|
+
threadId: eventThreadId,
|
|
526
|
+
timestamp
|
|
527
|
+
});
|
|
528
|
+
default:
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
appendCommandOutputDelta,
|
|
535
|
+
appendFileChangeOutputDelta,
|
|
536
|
+
appendToTranscriptItem,
|
|
537
|
+
applyThreadNameUpdate,
|
|
538
|
+
applyThreadStatusUpdate,
|
|
539
|
+
applyThreadTokenUsageUpdate,
|
|
540
|
+
applyTranscriptItemUpdate,
|
|
541
|
+
applyTurnLifecycleUpdate,
|
|
542
|
+
applyTurnPlanUpdate,
|
|
543
|
+
applyWatcherNotification,
|
|
544
|
+
commitLiveSelectedSnapshot,
|
|
545
|
+
ensureLiveSelectedSnapshot,
|
|
546
|
+
mergeThreadSummary,
|
|
547
|
+
upsertTranscriptEntry
|
|
548
|
+
};
|
|
549
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const KIB = 1024;
|
|
2
|
+
|
|
3
|
+
export const MOBILE_NETWORK_PROFILES = {
|
|
4
|
+
"weak-mobile": {
|
|
5
|
+
downstreamBytesPerSecond: 48 * KIB,
|
|
6
|
+
dropSseAfterMs: null,
|
|
7
|
+
jitterMs: 40,
|
|
8
|
+
requestDelayMs: 180,
|
|
9
|
+
responseDelayMs: 180,
|
|
10
|
+
upstreamBytesPerSecond: 32 * KIB
|
|
11
|
+
},
|
|
12
|
+
"weak-mobile-reconnect": {
|
|
13
|
+
downstreamBytesPerSecond: 48 * KIB,
|
|
14
|
+
dropSseAfterMs: 3200,
|
|
15
|
+
jitterMs: 50,
|
|
16
|
+
requestDelayMs: 220,
|
|
17
|
+
responseDelayMs: 220,
|
|
18
|
+
upstreamBytesPerSecond: 32 * KIB
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function resolveMobileNetworkProfile(name = "") {
|
|
23
|
+
const key = String(name || "").trim().toLowerCase();
|
|
24
|
+
if (!key) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return MOBILE_NETWORK_PROFILES[key] ? { name: key, ...MOBILE_NETWORK_PROFILES[key] } : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function withNetworkJitter(baseMs, jitterMs = 0) {
|
|
31
|
+
const base = Math.max(0, Number(baseMs) || 0);
|
|
32
|
+
const jitter = Math.max(0, Number(jitterMs) || 0);
|
|
33
|
+
if (jitter === 0) {
|
|
34
|
+
return base;
|
|
35
|
+
}
|
|
36
|
+
const offset = Math.round((Math.random() * (jitter * 2)) - jitter);
|
|
37
|
+
return Math.max(0, base + offset);
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function createMockCodexAdapter(store) {
|
|
2
|
+
let heartbeat = null;
|
|
3
|
+
|
|
4
|
+
function scheduleFollowUp(command) {
|
|
5
|
+
if (command.type === "send_text") {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
store.applyCommand({
|
|
8
|
+
type: "simulate_assistant_turn",
|
|
9
|
+
source: "mock-adapter",
|
|
10
|
+
text: `Mock adapter reply: "${command.text.trim()}" was accepted. A real Codex adapter would now focus the app, submit the text, and stream the resulting turn back into the companion.`
|
|
11
|
+
});
|
|
12
|
+
}, 700);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (command.type === "approve") {
|
|
17
|
+
setTimeout(() => {
|
|
18
|
+
store.applyCommand({
|
|
19
|
+
type: "simulate_assistant_turn",
|
|
20
|
+
source: "mock-adapter",
|
|
21
|
+
text: "Approval path validated. The next native spike can wire this action to a real gated tool call or session step."
|
|
22
|
+
});
|
|
23
|
+
}, 500);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (command.type === "set_strategy") {
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
store.applyCommand({
|
|
30
|
+
type: "simulate_assistant_turn",
|
|
31
|
+
source: "mock-adapter",
|
|
32
|
+
text: "Capability ladder updated. This is where a real adapter would announce what semantic read and write paths are currently healthy."
|
|
33
|
+
});
|
|
34
|
+
}, 350);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function start() {
|
|
39
|
+
heartbeat = setInterval(() => {
|
|
40
|
+
const snapshot = store.getState();
|
|
41
|
+
const mode = snapshot.session.strategy.label;
|
|
42
|
+
store.publishTranscript(
|
|
43
|
+
"system",
|
|
44
|
+
"status",
|
|
45
|
+
`Heartbeat: host is still live in ${mode}. Transport is ${snapshot.session.transportLabel}.`
|
|
46
|
+
);
|
|
47
|
+
}, 45000);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function stop() {
|
|
51
|
+
if (heartbeat) {
|
|
52
|
+
clearInterval(heartbeat);
|
|
53
|
+
heartbeat = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
start,
|
|
59
|
+
stop,
|
|
60
|
+
scheduleFollowUp
|
|
61
|
+
};
|
|
62
|
+
}
|