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.
Files changed (43) hide show
  1. package/README.md +9 -4
  2. package/dist/client-runtime.d.ts +3 -0
  3. package/dist/client-runtime.d.ts.map +1 -1
  4. package/dist/client-runtime.js +46 -14
  5. package/dist/client-runtime.js.map +1 -1
  6. package/dist/client.github.js +1 -1
  7. package/dist/client.js +1 -1
  8. package/dist/client.js.map +1 -1
  9. package/dist/codex/domain.d.ts +4 -0
  10. package/dist/codex/domain.d.ts.map +1 -1
  11. package/dist/codex/domain.js +74 -1
  12. package/dist/codex/domain.js.map +1 -1
  13. package/dist/codex/method-policy.d.ts +2 -2
  14. package/dist/codex/permissions.d.ts +4 -0
  15. package/dist/codex/permissions.d.ts.map +1 -0
  16. package/dist/codex/permissions.js +21 -0
  17. package/dist/codex/permissions.js.map +1 -0
  18. package/dist/codex/virtual-harness.d.ts +14 -4
  19. package/dist/codex/virtual-harness.d.ts.map +1 -1
  20. package/dist/codex/virtual-harness.js +180 -73
  21. package/dist/codex/virtual-harness.js.map +1 -1
  22. package/dist/harness-version.d.ts +7 -0
  23. package/dist/harness-version.d.ts.map +1 -1
  24. package/dist/harness-version.js +16 -0
  25. package/dist/harness-version.js.map +1 -1
  26. package/dist/index.js +648 -319
  27. package/dist/index.js.map +4 -4
  28. package/dist/remote-api-proxy.d.ts.map +1 -1
  29. package/dist/remote-api-proxy.js +34 -1
  30. package/dist/remote-api-proxy.js.map +1 -1
  31. package/dist/server-api.d.ts.map +1 -1
  32. package/dist/server-api.js +4 -5
  33. package/dist/server-api.js.map +1 -1
  34. package/dist/service.d.ts +1 -0
  35. package/dist/service.d.ts.map +1 -1
  36. package/dist/service.js +8 -4
  37. package/dist/service.js.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.d.ts.map +1 -1
  40. package/dist/version.js +1 -1
  41. package/dist/version.js.map +1 -1
  42. package/dsh-plugin.json +1 -1
  43. 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) ?? CODEX_DEFAULT_PERMISSION;
251
+ return this.selectedPermissions.get(sessionId);
249
252
  }
250
253
  setPermissionSelection(sessionId, preset) {
251
- this.selectedPermissions.set(sessionId, preset);
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) ?? requested;
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
- if (preset !== undefined)
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: { op: 'replace', start: placement.replaceSeq, end: placement.replaceSeq },
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: alphaEvent,
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.pushEvent(follow, 'assistant/chunk', {
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.pushEvent(follow, 'assistant/chunk', {
770
- turn: follow.turn,
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.pushEvent(follow, 'assistant/chunk', {
780
- turn: follow.turn,
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.pushEvent(follow, 'assistant/chunk', {
790
- turn: follow.turn,
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.pushEvent(follow, 'assistant/chunk', {
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) ?? permissionPreset ?? CODEX_DEFAULT_PERMISSION;
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.selectedPermissions.set(projected.id, permissionPreset);
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 (permissionPreset === undefined && serverPreset !== undefined) {
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(currentValue) {
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,