remote-codex 0.11.39 → 0.11.40
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/apps/supervisor-api/dist/index.js +33 -4
- package/apps/supervisor-web/dist/assets/{index-CUhMNGXI.js → index-B-qj7e8G.js} +4 -4
- package/apps/supervisor-web/dist/index.html +1 -1
- package/package.json +1 -1
- package/packages/claude/src/runtimeAdapter.test.ts +100 -1
- package/packages/claude/src/runtimeAdapter.ts +58 -7
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
11
11
|
<link rel="manifest" href="/site.webmanifest" />
|
|
12
12
|
<title>Remote Codex</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-B-qj7e8G.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-Dfg_6BLf.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-CuR8GHb0.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVQUpZ8C.js">
|
package/package.json
CHANGED
|
@@ -22,6 +22,11 @@ function wait(ms = 0) {
|
|
|
22
22
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function uuidV7At(timestamp = Date.now()) {
|
|
26
|
+
const timestampHex = Math.trunc(timestamp).toString(16).padStart(12, '0');
|
|
27
|
+
return `${timestampHex.slice(0, 8)}-${timestampHex.slice(8)}-7000-8000-000000000001`;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
class FakeQuery implements Query {
|
|
26
31
|
interrupted = false;
|
|
27
32
|
closed = false;
|
|
@@ -541,6 +546,7 @@ describe('ClaudeRuntimeAdapter', () => {
|
|
|
541
546
|
|
|
542
547
|
it('reconciles active multimodal transcript turns back to the live runtime turn id', async () => {
|
|
543
548
|
let queryMessages: SDKMessage[] = [systemInit()];
|
|
549
|
+
const currentMessageUuid = uuidV7At();
|
|
544
550
|
const adapter = new ClaudeRuntimeAdapter({
|
|
545
551
|
home: '/tmp/claude-home',
|
|
546
552
|
command: 'claude',
|
|
@@ -556,7 +562,7 @@ describe('ClaudeRuntimeAdapter', () => {
|
|
|
556
562
|
getSessionMessages: (async () => [
|
|
557
563
|
{
|
|
558
564
|
type: 'user',
|
|
559
|
-
uuid:
|
|
565
|
+
uuid: currentMessageUuid,
|
|
560
566
|
session_id: 'claude-session-1',
|
|
561
567
|
message: {
|
|
562
568
|
role: 'user',
|
|
@@ -609,6 +615,99 @@ describe('ClaudeRuntimeAdapter', () => {
|
|
|
609
615
|
expect(reloadedSession.turns[0]?.providerTurnId).toBe(started.providerTurnId);
|
|
610
616
|
});
|
|
611
617
|
|
|
618
|
+
it('does not reconcile an old matching user-only turn as the active turn', async () => {
|
|
619
|
+
const historicalMessageUuid = uuidV7At(Date.now() - 10 * 60_000);
|
|
620
|
+
const adapter = new ClaudeRuntimeAdapter({
|
|
621
|
+
home: '/tmp/claude-home',
|
|
622
|
+
command: 'claude',
|
|
623
|
+
query: (() => new FakeQuery([systemInit()], { holdOpen: true })) as any,
|
|
624
|
+
listSessions: (async () => [] satisfies SDKSessionInfo[]) as any,
|
|
625
|
+
getSessionInfo: (async () => ({
|
|
626
|
+
sessionId: 'claude-session-1',
|
|
627
|
+
summary: 'Existing session',
|
|
628
|
+
cwd: '/tmp/workspace',
|
|
629
|
+
})) as any,
|
|
630
|
+
getSessionMessages: (async () => [{
|
|
631
|
+
type: 'user',
|
|
632
|
+
uuid: historicalMessageUuid,
|
|
633
|
+
session_id: 'claude-session-1',
|
|
634
|
+
message: { role: 'user', content: 'Repeat this prompt' },
|
|
635
|
+
parent_tool_use_id: null,
|
|
636
|
+
}] satisfies SessionMessage[]) as any,
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const started = await adapter.startTurn({
|
|
640
|
+
providerSessionId: 'claude-session-1',
|
|
641
|
+
prompt: 'Repeat this prompt',
|
|
642
|
+
model: 'sonnet',
|
|
643
|
+
workspacePath: '/tmp/workspace',
|
|
644
|
+
});
|
|
645
|
+
const session = await adapter.readSession('claude-session-1');
|
|
646
|
+
|
|
647
|
+
expect(session.turns).toHaveLength(1);
|
|
648
|
+
expect(session.turns[0]).toMatchObject({
|
|
649
|
+
providerTurnId: `claude-turn-${historicalMessageUuid}`,
|
|
650
|
+
status: 'completed',
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
await adapter.interruptTurn({
|
|
654
|
+
providerSessionId: 'claude-session-1',
|
|
655
|
+
providerTurnId: started.providerTurnId,
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('reconciles only the current prompt when an older user-only turn exists', async () => {
|
|
660
|
+
const historicalMessageUuid = uuidV7At(Date.now() - 10 * 60_000);
|
|
661
|
+
const currentMessageUuid = uuidV7At();
|
|
662
|
+
const adapter = new ClaudeRuntimeAdapter({
|
|
663
|
+
home: '/tmp/claude-home',
|
|
664
|
+
command: 'claude',
|
|
665
|
+
query: (() => new FakeQuery([systemInit()], { holdOpen: true })) as any,
|
|
666
|
+
listSessions: (async () => [] satisfies SDKSessionInfo[]) as any,
|
|
667
|
+
getSessionInfo: (async () => ({
|
|
668
|
+
sessionId: 'claude-session-1',
|
|
669
|
+
summary: 'Existing session',
|
|
670
|
+
cwd: '/tmp/workspace',
|
|
671
|
+
})) as any,
|
|
672
|
+
getSessionMessages: (async () => [
|
|
673
|
+
{
|
|
674
|
+
type: 'user',
|
|
675
|
+
uuid: historicalMessageUuid,
|
|
676
|
+
session_id: 'claude-session-1',
|
|
677
|
+
message: { role: 'user', content: 'An older prompt' },
|
|
678
|
+
parent_tool_use_id: null,
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
type: 'user',
|
|
682
|
+
uuid: currentMessageUuid,
|
|
683
|
+
session_id: 'claude-session-1',
|
|
684
|
+
message: { role: 'user', content: 'The current prompt' },
|
|
685
|
+
parent_tool_use_id: null,
|
|
686
|
+
},
|
|
687
|
+
] satisfies SessionMessage[]) as any,
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const started = await adapter.startTurn({
|
|
691
|
+
providerSessionId: 'claude-session-1',
|
|
692
|
+
prompt: 'The current prompt',
|
|
693
|
+
model: 'sonnet',
|
|
694
|
+
workspacePath: '/tmp/workspace',
|
|
695
|
+
});
|
|
696
|
+
const session = await adapter.readSession('claude-session-1');
|
|
697
|
+
|
|
698
|
+
expect(session.turns.map((turn) => turn.providerTurnId)).toEqual([
|
|
699
|
+
`claude-turn-${historicalMessageUuid}`,
|
|
700
|
+
started.providerTurnId,
|
|
701
|
+
]);
|
|
702
|
+
expect(session.turns[0]?.items[0]).toMatchObject({ text: 'An older prompt' });
|
|
703
|
+
expect(session.turns[1]).toMatchObject({ status: 'inProgress' });
|
|
704
|
+
|
|
705
|
+
await adapter.interruptTurn({
|
|
706
|
+
providerSessionId: 'claude-session-1',
|
|
707
|
+
providerTurnId: started.providerTurnId,
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
|
|
612
711
|
it('keeps image blocks visible when reading Claude session history', async () => {
|
|
613
712
|
const workspace = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-history-image-'));
|
|
614
713
|
const adapter = new ClaudeRuntimeAdapter({
|
|
@@ -247,6 +247,33 @@ interface ActiveClaudeTurn {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
const promptPhotoTokenPattern = /\[PHOTO\s+([^\]]+)\]/g;
|
|
250
|
+
const activeTranscriptMatchWindowMs = 120_000;
|
|
251
|
+
|
|
252
|
+
function normalizePromptForTurnReconciliation(value: string) {
|
|
253
|
+
return value
|
|
254
|
+
// Claude history can rewrite or omit an image marker. The surrounding
|
|
255
|
+
// text and UUIDv7 timestamp remain stable reconciliation identifiers.
|
|
256
|
+
.replace(/\[PHOTO\s+[^\]]+\]/g, ' ')
|
|
257
|
+
.replace(/\s+/g, ' ')
|
|
258
|
+
.trim();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function userPromptForTurn(turn: AgentTurn) {
|
|
262
|
+
return turn.items.find((item) => item.kind === 'userMessage')?.text ?? null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function isRecentActiveTranscriptTurn(turn: AgentTurn, activeStartedAt: string) {
|
|
266
|
+
if (!turn.startedAt) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
const transcriptStartedAt = Date.parse(turn.startedAt);
|
|
270
|
+
const activeStartedAtMs = Date.parse(activeStartedAt);
|
|
271
|
+
return (
|
|
272
|
+
!Number.isFinite(transcriptStartedAt)
|
|
273
|
+
|| !Number.isFinite(activeStartedAtMs)
|
|
274
|
+
|| Math.abs(transcriptStartedAt - activeStartedAtMs) <= activeTranscriptMatchWindowMs
|
|
275
|
+
);
|
|
276
|
+
}
|
|
250
277
|
|
|
251
278
|
function mimeTypeForImagePath(filePath: string) {
|
|
252
279
|
const extension = path.extname(filePath).toLowerCase();
|
|
@@ -1766,12 +1793,17 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1766
1793
|
const activeItems = [...activeTurn.itemOrder]
|
|
1767
1794
|
.map((itemId) => activeTurn.items.get(itemId))
|
|
1768
1795
|
.filter((item): item is AgentHistoryItem => Boolean(item));
|
|
1769
|
-
const
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1796
|
+
const normalizedPrompt = normalizePromptForTurnReconciliation(prompt);
|
|
1797
|
+
const transcriptTurnIndex = turns.findLastIndex((turn) => {
|
|
1798
|
+
const transcriptPrompt = userPromptForTurn(turn);
|
|
1799
|
+
return (
|
|
1800
|
+
turn.status === 'completed'
|
|
1801
|
+
&& transcriptPrompt !== null
|
|
1802
|
+
&& normalizePromptForTurnReconciliation(transcriptPrompt) === normalizedPrompt
|
|
1803
|
+
&& !turn.items.some((item) => item.kind === 'agentMessage')
|
|
1804
|
+
&& isRecentActiveTranscriptTurn(turn, activeTurn.startedAt)
|
|
1805
|
+
);
|
|
1806
|
+
});
|
|
1775
1807
|
|
|
1776
1808
|
if (transcriptTurnIndex < 0) {
|
|
1777
1809
|
return turns;
|
|
@@ -1784,6 +1816,14 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1784
1816
|
aliases = new Map();
|
|
1785
1817
|
this.historicalTurnIdAliases.set(providerSessionId, aliases);
|
|
1786
1818
|
}
|
|
1819
|
+
for (const [historicalTurnId, displayTurnId] of aliases) {
|
|
1820
|
+
if (
|
|
1821
|
+
displayTurnId === activeTurn.providerTurnId
|
|
1822
|
+
&& historicalTurnId !== transcriptTurn.providerTurnId
|
|
1823
|
+
) {
|
|
1824
|
+
aliases.delete(historicalTurnId);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1787
1827
|
aliases.set(transcriptTurn.providerTurnId, activeTurn.providerTurnId);
|
|
1788
1828
|
}
|
|
1789
1829
|
|
|
@@ -1812,9 +1852,20 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1812
1852
|
if (!aliases || aliases.size === 0) {
|
|
1813
1853
|
return turns;
|
|
1814
1854
|
}
|
|
1855
|
+
const rawTurnIds = new Set(turns.map((turn) => turn.providerTurnId));
|
|
1856
|
+
const usedTurnIds = new Set<string>();
|
|
1815
1857
|
return turns.map((turn) => {
|
|
1816
1858
|
const providerTurnId = aliases.get(turn.providerTurnId);
|
|
1817
|
-
|
|
1859
|
+
if (
|
|
1860
|
+
!providerTurnId
|
|
1861
|
+
|| usedTurnIds.has(providerTurnId)
|
|
1862
|
+
|| (providerTurnId !== turn.providerTurnId && rawTurnIds.has(providerTurnId))
|
|
1863
|
+
) {
|
|
1864
|
+
usedTurnIds.add(turn.providerTurnId);
|
|
1865
|
+
return turn;
|
|
1866
|
+
}
|
|
1867
|
+
usedTurnIds.add(providerTurnId);
|
|
1868
|
+
return { ...turn, providerTurnId };
|
|
1818
1869
|
});
|
|
1819
1870
|
}
|
|
1820
1871
|
|