@makerbi/remodex 3.1.0 → 3.3.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.
@@ -5,6 +5,7 @@
5
5
  // Depends on: crypto, ./desktop-ipc-shared
6
6
 
7
7
  const { randomUUID } = require("crypto");
8
+ const { applyRuntimeSettingsToConversation } = require("./codex-runtime-settings");
8
9
 
9
10
  const {
10
11
  cloneJSON,
@@ -190,11 +191,15 @@ function applyAppServerMessageToConversationState({
190
191
  return null;
191
192
  }
192
193
  const conversation = ensureConversationInMap(conversations, threadId, { hostId, now });
193
- conversation.title = readString(message.params?.threadName)
194
+ const title = readString(message.params?.threadName)
194
195
  || readString(message.params?.thread_name)
195
196
  || readString(message.params?.name)
196
197
  || readString(message.params?.title)
197
198
  || conversation.title;
199
+ if (title === conversation.title) {
200
+ return { threadId, changed: false };
201
+ }
202
+ conversation.title = title;
198
203
  conversation.updatedAt = now();
199
204
  return { threadId, changed: true };
200
205
  }
@@ -446,7 +451,8 @@ function buildConversationStateFromThread(thread, {
446
451
  const createdAtMs = timestampSecondsToMs(thread?.createdAt) || previous?.createdAt || now();
447
452
  const updatedAtMs = timestampSecondsToMs(thread?.updatedAt) || now();
448
453
  const cwd = readString(thread?.cwd) || previous?.cwd || "";
449
- const latestModel = readString(thread?.model) || readString(thread?.modelProvider) || previous?.latestModel || "";
454
+ const latestModel = readString(previous?.latestThreadSettings?.model)
455
+ || readString(thread?.model) || previous?.latestModel || "";
450
456
  const turns = mergeConversationTurnsFromThread(thread?.turns, {
451
457
  previousTurns: previous?.turns,
452
458
  threadId,
@@ -454,17 +460,28 @@ function buildConversationStateFromThread(thread, {
454
460
  now,
455
461
  });
456
462
 
457
- return {
463
+ const state = {
458
464
  id: threadId,
465
+ forkedFromId: previous?.forkedFromId || null,
459
466
  hostId,
460
467
  turns,
461
468
  requests: cloneJSON(previous?.requests || []),
462
469
  createdAt: createdAtMs,
463
470
  updatedAt: updatedAtMs,
471
+ recencyAt: updatedAtMs,
464
472
  title: readString(thread?.name) || previous?.title || null,
473
+ source: readString(thread?.source) || previous?.source || "vscode",
474
+ agentNickname: previous?.agentNickname || null,
475
+ threadSource: readString(thread?.threadSource) || previous?.threadSource || "user",
476
+ historyMode: previous?.historyMode || "legacy",
477
+ parentThreadId: previous?.parentThreadId || null,
478
+ mode: previous?.mode || "default",
479
+ threadStartKind: previous?.threadStartKind || "default",
480
+ modelProvider: readString(thread?.modelProvider) || previous?.modelProvider || "openai",
465
481
  latestModel,
466
- latestReasoningEffort: previous?.latestReasoningEffort || null,
467
- latestServiceTier: previous?.latestServiceTier || null,
482
+ latestReasoningEffort: previous?.latestReasoningEffort ?? null,
483
+ latestServiceTier: previous?.latestServiceTier ?? null,
484
+ latestThreadSettings: cloneJSON(previous?.latestThreadSettings || null),
468
485
  previousTurnModel: previous?.previousTurnModel || null,
469
486
  latestCollaborationMode: previous?.latestCollaborationMode || {
470
487
  mode: "default",
@@ -488,7 +505,10 @@ function buildConversationStateFromThread(thread, {
488
505
  workspaceBrowserRoot: previous?.workspaceBrowserRoot || null,
489
506
  projectlessOutputDirectory: previous?.projectlessOutputDirectory || null,
490
507
  currentPermissions: cloneJSON(previous?.currentPermissions || null),
508
+ sessionId: readString(thread?.sessionId) || previous?.sessionId || null,
491
509
  };
510
+ synchronizeDesktopConversationCompatibility(state);
511
+ return state;
492
512
  }
493
513
 
494
514
  function mergeConversationTurnsFromThread(threadTurns, {
@@ -552,7 +572,7 @@ function createEmptyConversationState(threadId, {
552
572
  cwd = "",
553
573
  } = {}) {
554
574
  const timestamp = now();
555
- return {
575
+ const state = {
556
576
  id: threadId,
557
577
  hostId,
558
578
  turns: [],
@@ -587,6 +607,187 @@ function createEmptyConversationState(threadId, {
587
607
  projectlessOutputDirectory: null,
588
608
  currentPermissions: null,
589
609
  };
610
+ synchronizeDesktopConversationCompatibility(state);
611
+ return state;
612
+ }
613
+
614
+ // Desktop 26.825 moved rendered history to a canonical entity graph. It still
615
+ // accepts legacy turns during normalization, but unified-timeline selectors now
616
+ // assume the graph and several nested collections exist. Keep the bridge's
617
+ // mutable legacy turns for app-server event handling, and mirror them into the
618
+ // current Desktop shape before every stream broadcast.
619
+ function synchronizeDesktopConversationCompatibility(state) {
620
+ if (!state || typeof state !== "object" || Array.isArray(state)) {
621
+ return state;
622
+ }
623
+
624
+ const turns = Array.isArray(state.turns) ? state.turns : [];
625
+ const entries = [];
626
+ const entitiesByKey = {};
627
+ for (const turn of turns) {
628
+ const turnId = readString(turn?.turnId) || readString(turn?.id);
629
+ if (!turnId) {
630
+ continue;
631
+ }
632
+ turn.params = normalizeTurnParamsCompatibility(turn.params, {
633
+ cwd: readString(turn.params?.cwd) || readString(state.cwd),
634
+ });
635
+ turn.items = Array.isArray(turn.items) ? turn.items.map(normalizeDesktopItemCompatibility) : [];
636
+ turn.hookRuns = Array.isArray(turn.hookRuns) ? turn.hookRuns : [];
637
+ const key = `turn:${turnId}`;
638
+ entries.push({ key, value: key });
639
+ const { id: _legacyId, ...canonicalTurn } = turn;
640
+ entitiesByKey[key] = {
641
+ ...canonicalTurn,
642
+ turnId,
643
+ };
644
+ }
645
+
646
+ const islandId = "tail:0";
647
+ state.turnHistory = {
648
+ kind: "canonical",
649
+ history: {
650
+ entitiesByKey,
651
+ generation: 0,
652
+ isComplete: true,
653
+ islands: [{
654
+ id: islandId,
655
+ entries,
656
+ olderBoundary: {
657
+ status: "exhausted",
658
+ boundaryId: `${islandId}:older`,
659
+ },
660
+ newerBoundary: {
661
+ status: "exhausted",
662
+ boundaryId: `${islandId}:newer`,
663
+ },
664
+ }],
665
+ },
666
+ };
667
+ state.turnsPagination = {
668
+ olderCursor: null,
669
+ oldestLoadedTurnId: readString(turns[0]?.turnId) || readString(turns[0]?.id) || null,
670
+ isLoadingOlder: false,
671
+ hasLoadedOldest: true,
672
+ };
673
+
674
+ const latestParams = [...turns]
675
+ .reverse()
676
+ .map((turn) => turn?.params)
677
+ .find((params) => params && typeof params === "object") || null;
678
+ const sandboxPolicy = normalizeSandboxPolicyCompatibility(
679
+ latestParams?.sandboxPolicy || state.currentPermissions?.sandboxPolicy
680
+ );
681
+ if (sandboxPolicy) {
682
+ const runtimeWorkspaceRoots = Array.isArray(latestParams?.runtimeWorkspaceRoots)
683
+ ? cloneJSON(latestParams.runtimeWorkspaceRoots)
684
+ : Array.isArray(state.currentPermissions?.runtimeWorkspaceRoots)
685
+ ? cloneJSON(state.currentPermissions.runtimeWorkspaceRoots)
686
+ : [];
687
+ state.currentPermissions = {
688
+ activePermissionProfile: cloneJSON(
689
+ latestParams?.activePermissionProfile
690
+ ?? state.currentPermissions?.activePermissionProfile
691
+ ?? permissionProfileFromId(latestParams?.permissions)
692
+ ),
693
+ approvalPolicy: latestParams?.approvalPolicy
694
+ ?? state.currentPermissions?.approvalPolicy
695
+ ?? "on-request",
696
+ approvalsReviewer: latestParams?.approvalsReviewer
697
+ ?? state.currentPermissions?.approvalsReviewer
698
+ ?? "user",
699
+ runtimeWorkspaceRoots,
700
+ sandboxPolicy,
701
+ };
702
+ }
703
+
704
+ state.recencyAt = Number.isFinite(state.recencyAt) ? state.recencyAt : state.updatedAt;
705
+ state.source ||= "vscode";
706
+ state.threadSource ||= "user";
707
+ state.historyMode ||= "legacy";
708
+ state.mode ||= "default";
709
+ state.threadStartKind ||= "default";
710
+ state.modelProvider ||= "openai";
711
+ state.latestThreadSettings = {
712
+ cwd: readString(latestParams?.cwd) || readString(state.cwd) || null,
713
+ approvalPolicy: latestParams?.approvalPolicy ?? state.currentPermissions?.approvalPolicy ?? null,
714
+ approvalsReviewer: latestParams?.approvalsReviewer
715
+ ?? state.currentPermissions?.approvalsReviewer
716
+ ?? null,
717
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
718
+ activePermissionProfile: cloneJSON(state.currentPermissions?.activePermissionProfile ?? null),
719
+ model: readString(state.latestThreadSettings?.model)
720
+ || readString(latestParams?.model)
721
+ || readString(state.latestModel)
722
+ || null,
723
+ modelProvider: readString(state.latestThreadSettings?.modelProvider)
724
+ || readString(state.modelProvider)
725
+ || "openai",
726
+ serviceTier: Object.prototype.hasOwnProperty.call(state.latestThreadSettings || {}, "serviceTier")
727
+ ? state.latestThreadSettings.serviceTier
728
+ : latestParams?.serviceTier ?? state.latestServiceTier ?? null,
729
+ effort: Object.prototype.hasOwnProperty.call(state.latestThreadSettings || {}, "effort")
730
+ ? state.latestThreadSettings.effort
731
+ : latestParams?.effort ?? state.latestReasoningEffort ?? null,
732
+ summary: latestParams?.summary ?? state.latestThreadSettings?.summary ?? "none",
733
+ collaborationMode: cloneJSON(
734
+ state.latestThreadSettings?.collaborationMode
735
+ || latestParams?.collaborationMode
736
+ || state.latestCollaborationMode
737
+ || null
738
+ ),
739
+ multiAgentMode: latestParams?.multiAgentMode
740
+ ?? state.latestThreadSettings?.multiAgentMode
741
+ ?? null,
742
+ personality: latestParams?.personality
743
+ ?? state.latestThreadSettings?.personality
744
+ ?? null,
745
+ };
746
+ return state;
747
+ }
748
+
749
+ function normalizeTurnParamsCompatibility(params, { cwd = "" } = {}) {
750
+ const normalized = params && typeof params === "object" && !Array.isArray(params)
751
+ ? params
752
+ : {};
753
+ normalized.input = normalizeDesktopInputEntries(normalized.input);
754
+ normalized.attachments = Array.isArray(normalized.attachments) ? normalized.attachments : [];
755
+ normalized.cwd = readString(normalized.cwd) || readString(cwd) || null;
756
+ normalized.summary ??= "none";
757
+ normalized.personality ??= null;
758
+ normalized.outputSchema ??= null;
759
+ normalized.collaborationMode ??= null;
760
+ const sandboxPolicy = normalizeSandboxPolicyCompatibility(normalized.sandboxPolicy);
761
+ if (sandboxPolicy) {
762
+ normalized.sandboxPolicy = sandboxPolicy;
763
+ }
764
+ return normalized;
765
+ }
766
+
767
+ function normalizeSandboxPolicyCompatibility(policy) {
768
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
769
+ return null;
770
+ }
771
+ const normalized = cloneJSON(policy);
772
+ if (normalizeToken(normalized.type) === "workspacewrite") {
773
+ normalized.type = "workspaceWrite";
774
+ normalized.writableRoots = Array.isArray(normalized.writableRoots)
775
+ ? normalized.writableRoots
776
+ : [];
777
+ normalized.excludeSlashTmp = Boolean(normalized.excludeSlashTmp);
778
+ normalized.excludeTmpdirEnvVar = Boolean(normalized.excludeTmpdirEnvVar);
779
+ normalized.networkAccess = Boolean(normalized.networkAccess);
780
+ }
781
+ if (normalizeToken(normalized.type) === "readonly") {
782
+ normalized.type = "readOnly";
783
+ normalized.networkAccess = Boolean(normalized.networkAccess);
784
+ }
785
+ return normalized;
786
+ }
787
+
788
+ function permissionProfileFromId(value) {
789
+ const id = readString(value);
790
+ return id ? { id, extends: null } : null;
590
791
  }
591
792
 
592
793
  function buildConversationTurn(turn, {
@@ -596,7 +797,7 @@ function buildConversationTurn(turn, {
596
797
  now = () => Date.now(),
597
798
  } = {}) {
598
799
  const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
599
- const params = cloneJSON(previousTurn?.params || {
800
+ const params = normalizeTurnParamsCompatibility(cloneJSON(previousTurn?.params || {
600
801
  threadId,
601
802
  input: [],
602
803
  cwd: cwd || null,
@@ -611,7 +812,7 @@ function buildConversationTurn(turn, {
611
812
  outputSchema: null,
612
813
  collaborationMode: null,
613
814
  attachments: [],
614
- });
815
+ }), { cwd });
615
816
  const builtTurn = {
616
817
  id: turnId,
617
818
  turnId,
@@ -678,6 +879,9 @@ function applyPendingTurnStartParams(
678
879
  ...turn.params,
679
880
  ...cloneJSON(pendingParams),
680
881
  };
882
+ turn.params = normalizeTurnParamsCompatibility(turn.params, {
883
+ cwd: readString(turn.params?.cwd) || readString(conversation?.cwd),
884
+ });
681
885
  normalizeTurnInitialPrompt(turn);
682
886
  }
683
887
 
@@ -695,35 +899,8 @@ function applyTurnRuntimeMetadata(conversation, turnParams) {
695
899
  if (!conversation || !turnParams) {
696
900
  return;
697
901
  }
698
- const model = readString(turnParams.model);
699
- const effort = readString(turnParams.effort);
700
- const serviceTier = readString(turnParams.serviceTier) || null;
701
- if (model) {
702
- conversation.previousTurnModel = conversation.latestModel || null;
703
- conversation.latestModel = model;
704
- }
705
- if (effort) {
706
- conversation.latestReasoningEffort = effort;
707
- }
708
- conversation.latestServiceTier = serviceTier;
709
- if (turnParams.collaborationMode && typeof turnParams.collaborationMode === "object") {
710
- conversation.latestCollaborationMode = cloneJSON(turnParams.collaborationMode);
711
- return;
712
- }
713
- if (!model && !effort) {
714
- return;
715
- }
716
- const settings = conversation.latestCollaborationMode?.settings;
717
- conversation.latestCollaborationMode = {
718
- mode: conversation.latestCollaborationMode?.mode || "default",
719
- settings: {
720
- ...(settings && typeof settings === "object" ? settings : {
721
- developer_instructions: null,
722
- }),
723
- model: model || settings?.model || "",
724
- reasoning_effort: effort || settings?.reasoning_effort || null,
725
- },
726
- };
902
+ if (readString(turnParams.model)) conversation.previousTurnModel = conversation.latestModel || null;
903
+ applyRuntimeSettingsToConversation(conversation, turnParams);
727
904
  }
728
905
 
729
906
  function turnHasUserMessageItem(turn) {
@@ -763,7 +940,22 @@ function extractUserText(entries) {
763
940
  }
764
941
 
765
942
  function sanitizeUserInputEntries(entries) {
766
- return sanitizeSharedUserInputEntries(entries).map(cloneJSON);
943
+ return normalizeDesktopInputEntries(sanitizeSharedUserInputEntries(entries)).map(cloneJSON);
944
+ }
945
+
946
+ // App-server defaults text_elements, but Desktop 26.903 reads its length
947
+ // directly in IPC snapshots. Preserve existing spans and their byte offsets.
948
+ function normalizeDesktopInputEntries(entries) {
949
+ if (!Array.isArray(entries)) {
950
+ return [];
951
+ }
952
+ return entries.map((entry) => {
953
+ if (!entry || typeof entry !== "object" || entry.type !== "text"
954
+ || Array.isArray(entry.text_elements)) {
955
+ return entry;
956
+ }
957
+ return { ...entry, text_elements: [] };
958
+ });
767
959
  }
768
960
 
769
961
  function sanitizeUserMessageItem(item) {
@@ -784,15 +976,22 @@ function sanitizeUserMessageItem(item) {
784
976
  };
785
977
  }
786
978
 
787
- // Codex CLI 0.144.1 can omit receiverThreads from persisted collab tool calls,
788
- // while the matching Desktop renderer reads that collection without a fallback.
789
- // Keep the richer snapshots unchanged and synthesize lightweight references from
790
- // receiverThreadIds for older/CLI-owned rollouts so opening them cannot crash.
979
+ // Normalize optional app-server fields required by Desktop's snapshot renderer.
791
980
  function normalizeDesktopItemCompatibility(item) {
792
- if (!item || typeof item !== "object" || normalizeToken(item.type) !== "collabagenttoolcall") {
981
+ if (!item || typeof item !== "object") {
982
+ return item;
983
+ }
984
+
985
+ if (item.type === "userMessage" || item.type === "steeringUserMessage") {
986
+ const inputKey = item.type === "userMessage" ? "content" : "input";
987
+ return { ...item, [inputKey]: normalizeDesktopInputEntries(item[inputKey]) };
988
+ }
989
+ if (normalizeToken(item.type) !== "collabagenttoolcall") {
793
990
  return item;
794
991
  }
795
992
 
993
+ // Codex CLI 0.144.1 can omit receiverThreads from persisted collab tool calls.
994
+ // Keep richer snapshots and synthesize references for older/CLI-owned rollouts.
796
995
  const receiverThreads = Array.isArray(item.receiverThreads)
797
996
  ? item.receiverThreads
798
997
  : [];
@@ -886,14 +1085,19 @@ function normalizeTurnInitialPrompt(turn) {
886
1085
  function userMessageContentFromTurnInput(entry) {
887
1086
  if (typeof entry === "string") {
888
1087
  const text = readString(entry);
889
- return text ? { type: "text", text } : null;
1088
+ return text ? { type: "text", text, text_elements: [] } : null;
890
1089
  }
891
1090
  if (!entry || typeof entry !== "object") {
892
1091
  return null;
893
1092
  }
894
1093
  const type = normalizeToken(entry.type);
895
1094
  if (type === "inputtext" || type === "text") {
896
- return { type: "text", text: readString(entry.text) };
1095
+ return {
1096
+ ...cloneJSON(entry),
1097
+ type: "text",
1098
+ text: typeof entry.text === "string" ? entry.text : "",
1099
+ text_elements: Array.isArray(entry.text_elements) ? cloneJSON(entry.text_elements) : [],
1100
+ };
897
1101
  }
898
1102
  return cloneJSON(entry);
899
1103
  }
@@ -1262,6 +1466,7 @@ module.exports = {
1262
1466
  readThreadIdFromParams,
1263
1467
  readTurnIdFromParams,
1264
1468
  readTurnIdFromTurn,
1469
+ synchronizeDesktopConversationCompatibility,
1265
1470
  timestampSecondsToMs,
1266
1471
  upsertItem,
1267
1472
  upsertTurn,
@@ -203,6 +203,7 @@ function projectConversationState(threadId, rawState, {
203
203
  || readString(rawState?.latest_model)
204
204
  || "",
205
205
  ...(runtimeSettings ? {
206
+ runtimeSettings: cloneJSON(runtimeSettings),
206
207
  reasoningEffort: readString(runtimeSettings.reasoningEffort) || null,
207
208
  serviceTier: readString(runtimeSettings.serviceTier) || null,
208
209
  runtimeSettingsRevision: Number(runtimeSettings.revision) || 0,