ds-harness-remote 0.4.9 → 0.4.12
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/README.md +9 -4
- package/dist/client-runtime.d.ts +3 -0
- package/dist/client-runtime.d.ts.map +1 -1
- package/dist/client-runtime.js +46 -14
- package/dist/client-runtime.js.map +1 -1
- package/dist/client.github.js +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codex/domain.d.ts +4 -0
- package/dist/codex/domain.d.ts.map +1 -1
- package/dist/codex/domain.js +74 -1
- package/dist/codex/domain.js.map +1 -1
- package/dist/codex/method-policy.d.ts +2 -2
- package/dist/codex/permissions.d.ts +4 -0
- package/dist/codex/permissions.d.ts.map +1 -0
- package/dist/codex/permissions.js +21 -0
- package/dist/codex/permissions.js.map +1 -0
- package/dist/codex/virtual-harness.d.ts +14 -4
- package/dist/codex/virtual-harness.d.ts.map +1 -1
- package/dist/codex/virtual-harness.js +180 -73
- package/dist/codex/virtual-harness.js.map +1 -1
- package/dist/harness-version.d.ts +7 -0
- package/dist/harness-version.d.ts.map +1 -1
- package/dist/harness-version.js +16 -0
- package/dist/harness-version.js.map +1 -1
- package/dist/index.js +648 -319
- package/dist/index.js.map +4 -4
- package/dist/remote-api-proxy.d.ts.map +1 -1
- package/dist/remote-api-proxy.js +34 -1
- package/dist/remote-api-proxy.js.map +1 -1
- package/dist/server-api.d.ts.map +1 -1
- package/dist/server-api.js +4 -5
- package/dist/server-api.js.map +1 -1
- package/dist/service.d.ts +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +8 -4
- package/dist/service.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dsh-plugin.json +1 -1
- package/package.json +21 -21
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CodexRemoteClient, deriveCodexCwdWorkspaces, projectCodexThread, } from '@dsh-remote/client-core';
|
|
2
|
+
import { codexPermissionPresetFromResponse } from './permissions.js';
|
|
2
3
|
const CODEX_SESSION_PREFIX = 'codex:';
|
|
3
4
|
const CODEX_WORKSPACE_PREFIX = 'codex-workspace:';
|
|
4
5
|
const CODEX_PROVIDER = 'codex';
|
|
@@ -31,6 +32,7 @@ export async function discoverCodexVirtualWorkspaces(client, signal) {
|
|
|
31
32
|
export class CodexVirtualHarness {
|
|
32
33
|
client;
|
|
33
34
|
host;
|
|
35
|
+
sessionGeneration;
|
|
34
36
|
api;
|
|
35
37
|
catalog;
|
|
36
38
|
workspaceStreams = new Set();
|
|
@@ -52,13 +54,14 @@ export class CodexVirtualHarness {
|
|
|
52
54
|
commandSeq = 0;
|
|
53
55
|
selectedWorkspaceId;
|
|
54
56
|
closed = false;
|
|
55
|
-
constructor(client, host) {
|
|
57
|
+
constructor(client, host, sessionGeneration = 'legacy') {
|
|
56
58
|
this.client = client;
|
|
57
59
|
this.host = host;
|
|
60
|
+
this.sessionGeneration = sessionGeneration;
|
|
58
61
|
this.api = this.createApiProxy();
|
|
59
62
|
}
|
|
60
|
-
static remote(core, host) {
|
|
61
|
-
return new CodexVirtualHarness(new CodexRemoteClient(core), host);
|
|
63
|
+
static remote(core, host, sessionGeneration = 'legacy') {
|
|
64
|
+
return new CodexVirtualHarness(new CodexRemoteClient(core), host, sessionGeneration);
|
|
62
65
|
}
|
|
63
66
|
async workspaces(signal) {
|
|
64
67
|
return (await this.refreshCatalog(signal)).workspaces;
|
|
@@ -245,10 +248,13 @@ export class CodexVirtualHarness {
|
|
|
245
248
|
return this.selectedModels.get(sessionId) ?? directory?.default ?? modelSelection();
|
|
246
249
|
}
|
|
247
250
|
permissionSelection(sessionId) {
|
|
248
|
-
return this.selectedPermissions.get(sessionId)
|
|
251
|
+
return this.selectedPermissions.get(sessionId);
|
|
249
252
|
}
|
|
250
253
|
setPermissionSelection(sessionId, preset) {
|
|
251
|
-
|
|
254
|
+
if (preset === undefined)
|
|
255
|
+
this.selectedPermissions.delete(sessionId);
|
|
256
|
+
else
|
|
257
|
+
this.selectedPermissions.set(sessionId, preset);
|
|
252
258
|
this.publishProjection(sessionId, 'permissions', codexPermissionsProjection(preset), this.nextProjectionSeq());
|
|
253
259
|
}
|
|
254
260
|
commandList(args) {
|
|
@@ -275,19 +281,25 @@ export class CodexVirtualHarness {
|
|
|
275
281
|
result: { kind: 'error', text: 'CodeX Remote permission commands do not accept attachments.' },
|
|
276
282
|
};
|
|
277
283
|
const requested = (match[1] ?? '').trim();
|
|
278
|
-
if (requested === '')
|
|
284
|
+
if (requested === '') {
|
|
285
|
+
await this.readHistoryPage(threadId, { maxMessages: 1 }, signal);
|
|
279
286
|
return {
|
|
280
287
|
commandId,
|
|
281
|
-
result: { kind: 'success', text: `current preset ${this.permissionSelection(sessionId)}` },
|
|
288
|
+
result: { kind: 'success', text: `current preset ${this.permissionSelection(sessionId) ?? 'Host settings (not reported)'}` },
|
|
282
289
|
};
|
|
290
|
+
}
|
|
283
291
|
if (!isCodexPermissionPreset(requested))
|
|
284
292
|
return {
|
|
285
293
|
commandId,
|
|
286
294
|
result: { kind: 'error', text: `unknown preset "${requested}" (available: workspace-write, danger-full-access)` },
|
|
287
295
|
};
|
|
288
296
|
const result = await this.client.request('thread/resume', { threadId, permissionPreset: requested }, signal);
|
|
289
|
-
const effective = codexPermissionPresetFromResponse(result)
|
|
297
|
+
const effective = codexPermissionPresetFromResponse(result);
|
|
298
|
+
if (effective === undefined)
|
|
299
|
+
throw new Error('The Host did not confirm the CodeX permission preset.');
|
|
290
300
|
this.setPermissionSelection(sessionId, effective);
|
|
301
|
+
if (effective !== requested)
|
|
302
|
+
throw new Error('The Host did not apply the requested CodeX permission preset.');
|
|
291
303
|
return { commandId, result: { kind: 'success', text: `preset ${effective}` } };
|
|
292
304
|
}
|
|
293
305
|
sessionTitle(sessionId) {
|
|
@@ -446,6 +458,7 @@ export class CodexVirtualHarness {
|
|
|
446
458
|
streamedBlocks: new Map(),
|
|
447
459
|
nextBlockIndex: 0,
|
|
448
460
|
streamActive: false,
|
|
461
|
+
assistantStreamRevision: 0,
|
|
449
462
|
liveItems: new Map(),
|
|
450
463
|
liveToolOutput: new Map(),
|
|
451
464
|
liveToolResultSeq: new Map(),
|
|
@@ -470,6 +483,7 @@ export class CodexVirtualHarness {
|
|
|
470
483
|
imageLimits: codexImageLimitsProjection(),
|
|
471
484
|
},
|
|
472
485
|
},
|
|
486
|
+
...(this.sessionGeneration === 'v3' ? { assistantStream: { revision: 0 } } : {}),
|
|
473
487
|
});
|
|
474
488
|
try {
|
|
475
489
|
const stream = await this.client.subscribe(threadId, frame => this.acceptCodexFrame(follow, frame), signal, () => this.closeFollowAfterRemoteStreamClosed(follow));
|
|
@@ -499,12 +513,12 @@ export class CodexVirtualHarness {
|
|
|
499
513
|
}
|
|
500
514
|
if (frame.method === 'thread/settings/updated') {
|
|
501
515
|
const preset = codexPermissionPresetFromResponse(params);
|
|
502
|
-
|
|
503
|
-
this.setPermissionSelection(follow.sessionId, preset);
|
|
516
|
+
this.setPermissionSelection(follow.sessionId, preset);
|
|
504
517
|
return;
|
|
505
518
|
}
|
|
506
519
|
if (frame.method === 'turn/started') {
|
|
507
520
|
const turn = record(params.turn);
|
|
521
|
+
this.endAssistantAttempt(follow, { kind: 'abandoned' });
|
|
508
522
|
this.resetLiveTurn(follow);
|
|
509
523
|
follow.turn += 1;
|
|
510
524
|
follow.activeTurnId = string(turn.id);
|
|
@@ -641,6 +655,7 @@ export class CodexVirtualHarness {
|
|
|
641
655
|
this.closeAllStreamBlocks(follow);
|
|
642
656
|
if (follow.streamActive)
|
|
643
657
|
this.finishStream(follow);
|
|
658
|
+
this.endAssistantAttempt(follow, { kind: 'abandoned' });
|
|
644
659
|
this.pushEvent(follow, 'step/end', { turn: follow.turn, step: 1 });
|
|
645
660
|
follow.stepOpen = false;
|
|
646
661
|
}
|
|
@@ -686,13 +701,24 @@ export class CodexVirtualHarness {
|
|
|
686
701
|
if (type === 'agentMessage' && follow.streamActive && follow.streamedBlocks.size === 0)
|
|
687
702
|
this.finishStream(follow);
|
|
688
703
|
const supplementalImages = type === 'agentMessage' ? follow.pendingToolImages : [];
|
|
704
|
+
let assistantMessageSeq;
|
|
689
705
|
for (const event of itemEvents(item, follow.turn, 1, follow.requestId, this.modelSelection(follow.sessionId), follow.sessionId, supplementalImages)) {
|
|
690
706
|
if (event.type === 'tool/call' && follow.startedItems.has(itemId))
|
|
691
707
|
continue;
|
|
692
708
|
if (event.type === 'tool/result')
|
|
693
709
|
this.pushToolResult(follow, itemId, event);
|
|
694
|
-
else
|
|
695
|
-
this.pushEvent(follow, event.type, event.data, event.view);
|
|
710
|
+
else {
|
|
711
|
+
const seq = this.pushEvent(follow, event.type, event.data, event.view);
|
|
712
|
+
if (event.type === 'assistant/message')
|
|
713
|
+
assistantMessageSeq = seq;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (type === 'agentMessage' && assistantMessageSeq !== undefined) {
|
|
717
|
+
this.endAssistantAttempt(follow, {
|
|
718
|
+
kind: 'committed',
|
|
719
|
+
eventType: 'assistant/message',
|
|
720
|
+
seq: assistantMessageSeq,
|
|
721
|
+
});
|
|
696
722
|
}
|
|
697
723
|
if (type === 'agentMessage' && supplementalImages.length > 0)
|
|
698
724
|
follow.pendingToolImages = [];
|
|
@@ -710,24 +736,25 @@ export class CodexVirtualHarness {
|
|
|
710
736
|
type,
|
|
711
737
|
seq,
|
|
712
738
|
time: Date.now(),
|
|
713
|
-
data,
|
|
739
|
+
data: adaptEventData(type, type === 'assistant/message' && this.sessionGeneration === 'v3' && follow.assistantAttempt !== undefined
|
|
740
|
+
? { ...record(data), stream: follow.assistantAttempt.stream }
|
|
741
|
+
: data, this.sessionGeneration),
|
|
714
742
|
...(isSurfaceEvent(type)
|
|
715
743
|
? placement === undefined
|
|
716
744
|
? { surfaceOp: 'append' }
|
|
717
745
|
: {
|
|
718
|
-
surfaceOp:
|
|
746
|
+
surfaceOp: this.sessionGeneration === 'v3'
|
|
747
|
+
? { op: 'replace', startSeq: placement.replaceSeq, endSeq: placement.replaceSeq }
|
|
748
|
+
: { op: 'replace', start: placement.replaceSeq, end: placement.replaceSeq },
|
|
719
749
|
sourceEventSeqs: [placement.replaceSeq],
|
|
720
750
|
}
|
|
721
751
|
: {}),
|
|
722
752
|
};
|
|
723
753
|
this.cacheImageBlocks(follow.sessionId, event.data);
|
|
724
754
|
if (!follow.rcOnly) {
|
|
725
|
-
const alphaEvent = typeof event.surfaceOp === 'object'
|
|
726
|
-
? { ...event, surfaceOp: 'replace' }
|
|
727
|
-
: event;
|
|
728
755
|
follow.queue.push({
|
|
729
756
|
type: 'event',
|
|
730
|
-
event
|
|
757
|
+
event,
|
|
731
758
|
...(view === undefined ? {} : { view }),
|
|
732
759
|
});
|
|
733
760
|
}
|
|
@@ -756,52 +783,100 @@ export class CodexVirtualHarness {
|
|
|
756
783
|
const block = { itemId, index: follow.nextBlockIndex++, kind, text: '' };
|
|
757
784
|
follow.streamedBlocks.set(key, block);
|
|
758
785
|
follow.streamActive = true;
|
|
759
|
-
this.
|
|
760
|
-
turn: follow.turn,
|
|
761
|
-
step: 1,
|
|
762
|
-
chunk: { type: 'block-start', index: block.index, blockType: kind },
|
|
763
|
-
});
|
|
786
|
+
this.pushAssistantChunk(follow, { type: 'block-start', index: block.index, blockType: kind });
|
|
764
787
|
return block;
|
|
765
788
|
}
|
|
766
789
|
appendStreamDelta(follow, key, itemId, kind, delta) {
|
|
767
790
|
const block = this.ensureStreamBlock(follow, key, itemId, kind);
|
|
768
791
|
block.text = appendBoundedText(block.text, delta);
|
|
769
|
-
this.
|
|
770
|
-
|
|
771
|
-
step: 1,
|
|
772
|
-
chunk: { type: kind === 'reasoning' ? 'reasoning-delta' : 'text-delta', index: block.index, text: delta },
|
|
792
|
+
this.pushAssistantChunk(follow, {
|
|
793
|
+
type: kind === 'reasoning' ? 'reasoning-delta' : 'text-delta', index: block.index, text: delta,
|
|
773
794
|
});
|
|
774
795
|
}
|
|
775
796
|
closeItemStreamBlocks(follow, itemId, fallback) {
|
|
776
797
|
const matches = [...follow.streamedBlocks].filter(([, block]) => block.itemId === itemId);
|
|
777
798
|
for (const [key, block] of matches) {
|
|
778
799
|
const text = block.text || fallback || '';
|
|
779
|
-
this.
|
|
780
|
-
|
|
781
|
-
step: 1,
|
|
782
|
-
chunk: { type: 'block-end', index: block.index, block: { type: block.kind, text } },
|
|
800
|
+
this.pushAssistantChunk(follow, {
|
|
801
|
+
type: 'block-end', index: block.index, block: { type: block.kind, text },
|
|
783
802
|
});
|
|
784
803
|
follow.streamedBlocks.delete(key);
|
|
785
804
|
}
|
|
786
805
|
}
|
|
787
806
|
closeAllStreamBlocks(follow) {
|
|
788
807
|
for (const block of follow.streamedBlocks.values()) {
|
|
789
|
-
this.
|
|
790
|
-
|
|
791
|
-
step: 1,
|
|
792
|
-
chunk: { type: 'block-end', index: block.index, block: { type: block.kind, text: block.text } },
|
|
808
|
+
this.pushAssistantChunk(follow, {
|
|
809
|
+
type: 'block-end', index: block.index, block: { type: block.kind, text: block.text },
|
|
793
810
|
});
|
|
794
811
|
}
|
|
795
812
|
follow.streamedBlocks.clear();
|
|
796
813
|
}
|
|
797
814
|
finishStream(follow) {
|
|
798
|
-
this.
|
|
799
|
-
turn: follow.turn,
|
|
800
|
-
step: 1,
|
|
801
|
-
chunk: { type: 'finish', reason: { kind: 'stop' } },
|
|
802
|
-
});
|
|
815
|
+
this.pushAssistantChunk(follow, { type: 'finish', reason: { kind: 'stop' } });
|
|
803
816
|
follow.streamActive = false;
|
|
804
817
|
}
|
|
818
|
+
pushAssistantChunk(follow, chunk) {
|
|
819
|
+
if (this.sessionGeneration !== 'v3' || follow.rcOnly === true) {
|
|
820
|
+
this.pushEvent(follow, 'assistant/chunk', { turn: follow.turn, step: 1, chunk });
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
let attempt = follow.assistantAttempt;
|
|
824
|
+
if (attempt === undefined) {
|
|
825
|
+
attempt = {
|
|
826
|
+
attemptId: `codex-attempt:${follow.activeTurnId ?? follow.turn}:${follow.nextSeq}`,
|
|
827
|
+
startedAfterSeq: follow.nextSeq - 1,
|
|
828
|
+
nextIndex: 0,
|
|
829
|
+
stream: [],
|
|
830
|
+
};
|
|
831
|
+
follow.assistantAttempt = attempt;
|
|
832
|
+
follow.assistantStreamRevision += 1;
|
|
833
|
+
follow.queue.push({
|
|
834
|
+
type: 'assistant-stream',
|
|
835
|
+
frame: {
|
|
836
|
+
type: 'start',
|
|
837
|
+
attemptId: attempt.attemptId,
|
|
838
|
+
revision: follow.assistantStreamRevision,
|
|
839
|
+
startedAfterSeq: attempt.startedAfterSeq,
|
|
840
|
+
turn: follow.turn,
|
|
841
|
+
step: 1,
|
|
842
|
+
},
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
const time = Date.now();
|
|
846
|
+
const index = attempt.nextIndex++;
|
|
847
|
+
attempt.stream.push({ time, chunk });
|
|
848
|
+
follow.assistantStreamRevision += 1;
|
|
849
|
+
follow.queue.push({
|
|
850
|
+
type: 'assistant-stream',
|
|
851
|
+
frame: {
|
|
852
|
+
type: 'chunk',
|
|
853
|
+
attemptId: attempt.attemptId,
|
|
854
|
+
revision: follow.assistantStreamRevision,
|
|
855
|
+
index,
|
|
856
|
+
time,
|
|
857
|
+
chunk,
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
endAssistantAttempt(follow, outcome) {
|
|
862
|
+
const attempt = follow.assistantAttempt;
|
|
863
|
+
if (attempt === undefined)
|
|
864
|
+
return;
|
|
865
|
+
follow.assistantAttempt = undefined;
|
|
866
|
+
follow.assistantStreamRevision += 1;
|
|
867
|
+
if (this.sessionGeneration === 'v3' && follow.rcOnly !== true) {
|
|
868
|
+
follow.queue.push({
|
|
869
|
+
type: 'assistant-stream',
|
|
870
|
+
frame: {
|
|
871
|
+
type: 'end',
|
|
872
|
+
attemptId: attempt.attemptId,
|
|
873
|
+
revision: follow.assistantStreamRevision,
|
|
874
|
+
index: attempt.nextIndex,
|
|
875
|
+
outcome,
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
805
880
|
ensureToolStarted(follow, item) {
|
|
806
881
|
const itemId = string(item.id);
|
|
807
882
|
if (itemId === undefined)
|
|
@@ -835,6 +910,7 @@ export class CodexVirtualHarness {
|
|
|
835
910
|
follow.streamedBlocks.clear();
|
|
836
911
|
follow.nextBlockIndex = 0;
|
|
837
912
|
follow.streamActive = false;
|
|
913
|
+
follow.assistantAttempt = undefined;
|
|
838
914
|
follow.liveItems.clear();
|
|
839
915
|
follow.liveToolOutput.clear();
|
|
840
916
|
follow.liveToolResultSeq.clear();
|
|
@@ -1006,17 +1082,15 @@ export class CodexVirtualHarness {
|
|
|
1006
1082
|
}
|
|
1007
1083
|
async forkSession(request, signal) {
|
|
1008
1084
|
const sessionId = requiredString(request.sessionId, 'sessionId');
|
|
1009
|
-
let permissionPreset = this.selectedPermissions.get(sessionId);
|
|
1010
1085
|
const result = record(await this.client.request('thread/fork', {
|
|
1011
1086
|
threadId: nativeThreadId(sessionId),
|
|
1012
|
-
...(permissionPreset === undefined ? {} : { permissionPreset }),
|
|
1013
1087
|
}, signal));
|
|
1014
|
-
permissionPreset = codexPermissionPresetFromResponse(result)
|
|
1088
|
+
const permissionPreset = codexPermissionPresetFromResponse(result);
|
|
1015
1089
|
const projected = projectCodexThread(record(result.thread));
|
|
1016
1090
|
if (projected === undefined)
|
|
1017
1091
|
return failure('internal', 'CodeX returned an invalid forked Thread.');
|
|
1018
1092
|
this.selectedModels.set(projected.id, this.modelSelection(sessionId, await this.models(signal)));
|
|
1019
|
-
this.
|
|
1093
|
+
this.setPermissionSelection(projected.id, permissionPreset);
|
|
1020
1094
|
await this.refreshAndPublishWorkspaces();
|
|
1021
1095
|
const seq = this.nextProjectionSeq();
|
|
1022
1096
|
this.publishProjection(projected.id, 'title', displayTitle(projected), seq);
|
|
@@ -1030,7 +1104,6 @@ export class CodexVirtualHarness {
|
|
|
1030
1104
|
return failure('attachment-error', 'CodeX virtual Sessions accept text and pasted PNG, JPEG, WebP, or GIF images only.');
|
|
1031
1105
|
const threadId = nativeThreadId(sessionId);
|
|
1032
1106
|
const selection = this.modelSelection(sessionId, await this.models(signal));
|
|
1033
|
-
let permissionPreset = this.selectedPermissions.get(sessionId);
|
|
1034
1107
|
const isFreshBlankThread = this.blankThreads.has(threadId) || this.pendingThreads.has(threadId);
|
|
1035
1108
|
await this.ensureRcFollow(sessionId);
|
|
1036
1109
|
if (!isFreshBlankThread) {
|
|
@@ -1039,8 +1112,7 @@ export class CodexVirtualHarness {
|
|
|
1039
1112
|
model: selection.model,
|
|
1040
1113
|
}, signal);
|
|
1041
1114
|
const serverPreset = codexPermissionPresetFromResponse(resumeResult);
|
|
1042
|
-
if (
|
|
1043
|
-
permissionPreset = serverPreset;
|
|
1115
|
+
if (serverPreset !== undefined) {
|
|
1044
1116
|
this.setPermissionSelection(sessionId, serverPreset);
|
|
1045
1117
|
}
|
|
1046
1118
|
}
|
|
@@ -1057,7 +1129,6 @@ export class CodexVirtualHarness {
|
|
|
1057
1129
|
threadId,
|
|
1058
1130
|
input,
|
|
1059
1131
|
...codexModelParams(selection),
|
|
1060
|
-
...(permissionPreset === undefined ? {} : { permissionPreset }),
|
|
1061
1132
|
}, signal);
|
|
1062
1133
|
}
|
|
1063
1134
|
this.blankThreads.delete(threadId);
|
|
@@ -1144,15 +1215,20 @@ export class CodexVirtualHarness {
|
|
|
1144
1215
|
const thread = record(result.thread);
|
|
1145
1216
|
if (string(thread.id) !== threadId)
|
|
1146
1217
|
throw new Error('CodeX returned an invalid Thread history.');
|
|
1147
|
-
value = paginateCodexNativeHistory(projectCodexNativeHistory(thread, `codex:${threadId}
|
|
1218
|
+
value = paginateCodexNativeHistory(projectCodexNativeHistory(thread, `codex:${threadId}`, this.sessionGeneration), {
|
|
1148
1219
|
beforeSeq: page.beforeSeq,
|
|
1149
1220
|
throughSeq: page.throughSeq,
|
|
1150
1221
|
maxMessages: page.maxMessages,
|
|
1151
1222
|
});
|
|
1152
1223
|
}
|
|
1224
|
+
value = adaptCodexHistoryPage(value, this.sessionGeneration);
|
|
1153
1225
|
if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
|
|
1154
1226
|
throw new Error('CodeX Remote returned an invalid paginated History.');
|
|
1155
1227
|
}
|
|
1228
|
+
if (Object.hasOwn(value, 'permissionPreset')) {
|
|
1229
|
+
const preset = value.permissionPreset;
|
|
1230
|
+
this.setPermissionSelection(`codex:${threadId}`, preset === 'workspace-write' || preset === 'danger-full-access' ? preset : undefined);
|
|
1231
|
+
}
|
|
1156
1232
|
this.cacheHistoryImages(value);
|
|
1157
1233
|
return value;
|
|
1158
1234
|
}
|
|
@@ -1222,6 +1298,7 @@ export class CodexVirtualHarness {
|
|
|
1222
1298
|
streamedBlocks: new Map(),
|
|
1223
1299
|
nextBlockIndex: 0,
|
|
1224
1300
|
streamActive: false,
|
|
1301
|
+
assistantStreamRevision: 0,
|
|
1225
1302
|
liveItems: new Map(),
|
|
1226
1303
|
liveToolOutput: new Map(),
|
|
1227
1304
|
liveToolResultSeq: new Map(),
|
|
@@ -1531,7 +1608,7 @@ async function loadModelDirectory(client, signal) {
|
|
|
1531
1608
|
models,
|
|
1532
1609
|
};
|
|
1533
1610
|
}
|
|
1534
|
-
export function projectCodexNativeHistory(thread, sessionId) {
|
|
1611
|
+
export function projectCodexNativeHistory(thread, sessionId, sessionGeneration = 'legacy') {
|
|
1535
1612
|
const entries = [];
|
|
1536
1613
|
let seq = 0;
|
|
1537
1614
|
let turnNumber = 0;
|
|
@@ -1544,7 +1621,7 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1544
1621
|
type,
|
|
1545
1622
|
seq: eventSeq,
|
|
1546
1623
|
time,
|
|
1547
|
-
data,
|
|
1624
|
+
data: adaptEventData(type, data, sessionGeneration),
|
|
1548
1625
|
...(sourceEventSeqs === undefined ? {} : { sourceEventSeqs }),
|
|
1549
1626
|
...(isSurfaceEvent(type) ? { surfaceOp: 'append' } : {}),
|
|
1550
1627
|
},
|
|
@@ -1588,10 +1665,11 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1588
1665
|
const projected = projectCodexThread(thread);
|
|
1589
1666
|
return {
|
|
1590
1667
|
header: {
|
|
1591
|
-
version: 1,
|
|
1668
|
+
version: sessionGeneration === 'v3' ? 3 : 1,
|
|
1592
1669
|
id: sessionId,
|
|
1593
1670
|
createdAt: projected?.createdAt ?? Date.now(),
|
|
1594
1671
|
...(projected?.cwd === undefined ? {} : { cwd: projected.cwd }),
|
|
1672
|
+
...(sessionGeneration === 'v3' ? { isSeeded: false } : {}),
|
|
1595
1673
|
},
|
|
1596
1674
|
entries,
|
|
1597
1675
|
lastSeq: seq - 1,
|
|
@@ -1625,6 +1703,54 @@ export function paginateCodexNativeHistory(history, request) {
|
|
|
1625
1703
|
...(history.activeTurnId === undefined ? {} : { activeTurnId: history.activeTurnId }),
|
|
1626
1704
|
};
|
|
1627
1705
|
}
|
|
1706
|
+
function adaptEventData(type, data, sessionGeneration) {
|
|
1707
|
+
if (sessionGeneration !== 'v3' || type !== 'assistant/message')
|
|
1708
|
+
return data;
|
|
1709
|
+
const value = record(data);
|
|
1710
|
+
return Array.isArray(value.stream) ? value : { ...value, stream: [] };
|
|
1711
|
+
}
|
|
1712
|
+
function adaptCodexHistoryPage(value, sessionGeneration) {
|
|
1713
|
+
if (sessionGeneration !== 'v3')
|
|
1714
|
+
return value;
|
|
1715
|
+
const page = record(value);
|
|
1716
|
+
const sourceHeader = record(page.header);
|
|
1717
|
+
const { seedLength: _seedLength, ...header } = sourceHeader;
|
|
1718
|
+
const records = Array.isArray(page.records)
|
|
1719
|
+
? page.records.map(raw => {
|
|
1720
|
+
const entry = record(raw);
|
|
1721
|
+
const sourceEvent = record(entry.event);
|
|
1722
|
+
const sourceSurfaceOp = record(sourceEvent.surfaceOp);
|
|
1723
|
+
const surfaceOp = sourceEvent.surfaceOp === 'append'
|
|
1724
|
+
? 'append'
|
|
1725
|
+
: sourceSurfaceOp.op === 'replace'
|
|
1726
|
+
&& integer(sourceSurfaceOp.startSeq) !== undefined
|
|
1727
|
+
&& integer(sourceSurfaceOp.endSeq) !== undefined
|
|
1728
|
+
? sourceSurfaceOp
|
|
1729
|
+
: sourceSurfaceOp.op === 'replace'
|
|
1730
|
+
&& integer(sourceSurfaceOp.start) !== undefined
|
|
1731
|
+
&& integer(sourceSurfaceOp.end) !== undefined
|
|
1732
|
+
? {
|
|
1733
|
+
op: 'replace',
|
|
1734
|
+
startSeq: sourceSurfaceOp.start,
|
|
1735
|
+
endSeq: sourceSurfaceOp.end,
|
|
1736
|
+
}
|
|
1737
|
+
: sourceEvent.surfaceOp;
|
|
1738
|
+
return {
|
|
1739
|
+
...entry,
|
|
1740
|
+
event: {
|
|
1741
|
+
...sourceEvent,
|
|
1742
|
+
data: adaptEventData(string(sourceEvent.type) ?? '', sourceEvent.data, sessionGeneration),
|
|
1743
|
+
...(surfaceOp === undefined ? {} : { surfaceOp }),
|
|
1744
|
+
},
|
|
1745
|
+
};
|
|
1746
|
+
})
|
|
1747
|
+
: page.records;
|
|
1748
|
+
return {
|
|
1749
|
+
...page,
|
|
1750
|
+
header: { ...header, version: 3, isSeeded: sourceHeader.isSeeded === true },
|
|
1751
|
+
records,
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1628
1754
|
function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX, supplementalImages = []) {
|
|
1629
1755
|
const type = string(item.type);
|
|
1630
1756
|
const id = string(item.id) ?? `${turn}:${step}:${hashString(JSON.stringify(item))}`;
|
|
@@ -2256,7 +2382,7 @@ function isCanonicalBase64(value) {
|
|
|
2256
2382
|
function modelSelectionProjection(selection) {
|
|
2257
2383
|
return { lastUsed: selection, next: selection };
|
|
2258
2384
|
}
|
|
2259
|
-
function codexPermissionsProjection(
|
|
2385
|
+
function codexPermissionsProjection(preset) {
|
|
2260
2386
|
return {
|
|
2261
2387
|
options: [
|
|
2262
2388
|
{
|
|
@@ -2270,7 +2396,7 @@ function codexPermissionsProjection(currentValue) {
|
|
|
2270
2396
|
description: 'Full Host file access without approval prompts for subsequent turns in this virtual Session.',
|
|
2271
2397
|
},
|
|
2272
2398
|
],
|
|
2273
|
-
currentValue,
|
|
2399
|
+
currentValue: preset ?? 'Host settings (not reported)',
|
|
2274
2400
|
};
|
|
2275
2401
|
}
|
|
2276
2402
|
function codexImageLimitsProjection() {
|
|
@@ -2286,25 +2412,6 @@ function codexImageLimitsProjection() {
|
|
|
2286
2412
|
function isCodexPermissionPreset(value) {
|
|
2287
2413
|
return value === 'workspace-write' || value === 'danger-full-access';
|
|
2288
2414
|
}
|
|
2289
|
-
function codexPermissionPresetFromResponse(value) {
|
|
2290
|
-
const root = record(value);
|
|
2291
|
-
return codexPermissionPresetFromPolicyRecord(root)
|
|
2292
|
-
?? codexPermissionPresetFromPolicyRecord(record(root.threadSettings));
|
|
2293
|
-
}
|
|
2294
|
-
function codexPermissionPresetFromPolicyRecord(value) {
|
|
2295
|
-
const sandbox = value.sandbox ?? value.sandboxPolicy;
|
|
2296
|
-
if (value.approvalPolicy === 'never' && isDangerFullAccessSandbox(sandbox))
|
|
2297
|
-
return 'danger-full-access';
|
|
2298
|
-
if (value.approvalPolicy === 'on-request' && isWorkspaceWriteSandbox(sandbox))
|
|
2299
|
-
return 'workspace-write';
|
|
2300
|
-
return undefined;
|
|
2301
|
-
}
|
|
2302
|
-
function isDangerFullAccessSandbox(value) {
|
|
2303
|
-
return value === 'danger-full-access' || record(value).type === 'dangerFullAccess';
|
|
2304
|
-
}
|
|
2305
|
-
function isWorkspaceWriteSandbox(value) {
|
|
2306
|
-
return value === 'workspace-write' || record(value).type === 'workspaceWrite';
|
|
2307
|
-
}
|
|
2308
2415
|
function modelCatalog(directory) {
|
|
2309
2416
|
return {
|
|
2310
2417
|
default: directory.default,
|