codexuse-cli 3.6.2 → 3.6.3

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/dist/index.js CHANGED
@@ -10226,7 +10226,7 @@ async function ensureCliStorageReady() {
10226
10226
  }
10227
10227
 
10228
10228
  // src/app/main.ts
10229
- var VERSION = true ? "3.6.2" : "0.0.0";
10229
+ var VERSION = true ? "3.6.3" : "0.0.0";
10230
10230
  async function runCli() {
10231
10231
  const args = process.argv.slice(2);
10232
10232
  if (args.length === 0) {
@@ -19324,6 +19324,10 @@ const GitStatusResult = Struct({
19324
19324
  behindCount: NonNegativeInt,
19325
19325
  pr: NullOr(GitStatusPr)
19326
19326
  });
19327
+ const GitStatusUpdatedPayload = Struct({
19328
+ cwd: TrimmedNonEmptyStringSchema,
19329
+ status: GitStatusResult
19330
+ });
19327
19331
  const GitListBranchesResult = Struct({
19328
19332
  branches: Array$1(GitBranch),
19329
19333
  isRepo: Boolean$2,
@@ -19527,43 +19531,43 @@ const EDITORS = [
19527
19531
  {
19528
19532
  id: "cursor",
19529
19533
  label: "Cursor",
19530
- command: "cursor",
19534
+ commands: ["cursor"],
19531
19535
  supportsGoto: true
19532
19536
  },
19533
19537
  {
19534
19538
  id: "vscode",
19535
19539
  label: "VS Code",
19536
- command: "code",
19540
+ commands: ["code"],
19537
19541
  supportsGoto: true
19538
19542
  },
19539
19543
  {
19540
19544
  id: "vscode-insiders",
19541
19545
  label: "VS Code Insiders",
19542
- command: "code-insiders",
19546
+ commands: ["code-insiders"],
19543
19547
  supportsGoto: true
19544
19548
  },
19545
19549
  {
19546
19550
  id: "vscodium",
19547
19551
  label: "VSCodium",
19548
- command: "codium",
19552
+ commands: ["codium"],
19549
19553
  supportsGoto: true
19550
19554
  },
19551
19555
  {
19552
19556
  id: "zed",
19553
19557
  label: "Zed",
19554
- command: "zed",
19558
+ commands: ["zed", "zeditor"],
19555
19559
  supportsGoto: false
19556
19560
  },
19557
19561
  {
19558
19562
  id: "antigravity",
19559
19563
  label: "Antigravity",
19560
- command: "agy",
19564
+ commands: ["agy"],
19561
19565
  supportsGoto: false
19562
19566
  },
19563
19567
  {
19564
19568
  id: "file-manager",
19565
19569
  label: "File Manager",
19566
- command: null,
19570
+ commands: null,
19567
19571
  supportsGoto: false
19568
19572
  }
19569
19573
  ];
@@ -19641,6 +19645,8 @@ const WS_METHODS = {
19641
19645
  shellOpenInEditor: "shell.openInEditor",
19642
19646
  gitPull: "git.pull",
19643
19647
  gitStatus: "git.status",
19648
+ gitWatchStatus: "git.watchStatus",
19649
+ gitUnwatchStatus: "git.unwatchStatus",
19644
19650
  gitRunStackedAction: "git.runStackedAction",
19645
19651
  gitListBranches: "git.listBranches",
19646
19652
  gitCreateWorktree: "git.createWorktree",
@@ -19713,6 +19719,7 @@ const WS_METHODS = {
19713
19719
  };
19714
19720
  const WS_CHANNELS = {
19715
19721
  gitActionProgress: "git.actionProgress",
19722
+ gitStatusUpdated: "git.statusUpdated",
19716
19723
  terminalEvent: "terminal.event",
19717
19724
  serverWelcome: "server.welcome",
19718
19725
  serverConfigUpdated: "server.configUpdated",
@@ -19737,6 +19744,8 @@ const WebSocketRequestBody = Union([
19737
19744
  tagRequestBody(WS_METHODS.shellOpenInEditor, OpenInEditorInput),
19738
19745
  tagRequestBody(WS_METHODS.gitPull, GitPullInput),
19739
19746
  tagRequestBody(WS_METHODS.gitStatus, GitStatusInput),
19747
+ tagRequestBody(WS_METHODS.gitWatchStatus, GitStatusInput),
19748
+ tagRequestBody(WS_METHODS.gitUnwatchStatus, GitStatusInput),
19740
19749
  tagRequestBody(WS_METHODS.gitRunStackedAction, GitRunStackedActionInput),
19741
19750
  tagRequestBody(WS_METHODS.gitListBranches, GitListBranchesInput),
19742
19751
  tagRequestBody(WS_METHODS.gitCreateWorktree, GitCreateWorktreeInput),
@@ -19862,6 +19871,7 @@ const WsPushServerWelcome = makeWsPushSchema(WS_CHANNELS.serverWelcome, WsWelcom
19862
19871
  const WsPushServerConfigUpdated = makeWsPushSchema(WS_CHANNELS.serverConfigUpdated, ServerConfigUpdatedPayload);
19863
19872
  const WsPushServerProvidersUpdated = makeWsPushSchema(WS_CHANNELS.serverProvidersUpdated, ServerProviderUpdatedPayload);
19864
19873
  const WsPushGitActionProgress = makeWsPushSchema(WS_CHANNELS.gitActionProgress, GitActionProgressEvent);
19874
+ const WsPushGitStatusUpdated = makeWsPushSchema(WS_CHANNELS.gitStatusUpdated, GitStatusUpdatedPayload);
19865
19875
  const WsPushTerminalEvent = makeWsPushSchema(WS_CHANNELS.terminalEvent, TerminalEvent);
19866
19876
  const WsPushProfilesSwitched = makeWsPushSchema(WS_CHANNELS.profilesSwitched, Unknown);
19867
19877
  const WsPushRateLimitsUpdated = makeWsPushSchema(WS_CHANNELS.rateLimitsUpdated, Unknown);
@@ -19870,6 +19880,7 @@ const WsPushCliStatusUpdated = makeWsPushSchema(WS_CHANNELS.cliStatusUpdated, Un
19870
19880
  const WsPushOrchestrationDomainEvent = makeWsPushSchema(ORCHESTRATION_WS_CHANNELS.domainEvent, OrchestrationEvent);
19871
19881
  const WsPushChannelSchema = Literals([
19872
19882
  WS_CHANNELS.gitActionProgress,
19883
+ WS_CHANNELS.gitStatusUpdated,
19873
19884
  WS_CHANNELS.serverWelcome,
19874
19885
  WS_CHANNELS.serverConfigUpdated,
19875
19886
  WS_CHANNELS.serverProvidersUpdated,
@@ -19885,6 +19896,7 @@ const WsPush = Union([
19885
19896
  WsPushServerConfigUpdated,
19886
19897
  WsPushServerProvidersUpdated,
19887
19898
  WsPushGitActionProgress,
19899
+ WsPushGitStatusUpdated,
19888
19900
  WsPushTerminalEvent,
19889
19901
  WsPushProfilesSwitched,
19890
19902
  WsPushRateLimitsUpdated,
@@ -19917,6 +19929,10 @@ const LINE_COLUMN_SUFFIX_PATTERN = /:\d+(?::\d+)?$/;
19917
19929
  function shouldUseGotoFlag(editor, target) {
19918
19930
  return editor.supportsGoto && LINE_COLUMN_SUFFIX_PATTERN.test(target);
19919
19931
  }
19932
+ function resolveAvailableCommand(commands, options = {}) {
19933
+ for (const command of commands) if (isCommandAvailable(command, options)) return command;
19934
+ return null;
19935
+ }
19920
19936
  function fileManagerCommandForPlatform(platform) {
19921
19937
  switch (platform) {
19922
19938
  case "darwin": return "open";
@@ -19992,26 +20008,41 @@ function isCommandAvailable(command, options = {}) {
19992
20008
  }
19993
20009
  function resolveAvailableEditors(platform = process.platform, env = process.env) {
19994
20010
  const available = [];
19995
- for (const editor of EDITORS) if (isCommandAvailable(editor.command ?? fileManagerCommandForPlatform(platform), {
19996
- platform,
19997
- env
19998
- })) available.push(editor.id);
20011
+ for (const editor of EDITORS) {
20012
+ if (editor.commands === null) {
20013
+ if (isCommandAvailable(fileManagerCommandForPlatform(platform), {
20014
+ platform,
20015
+ env
20016
+ })) available.push(editor.id);
20017
+ continue;
20018
+ }
20019
+ if (resolveAvailableCommand(editor.commands, {
20020
+ platform,
20021
+ env
20022
+ }) !== null) available.push(editor.id);
20023
+ }
19999
20024
  return available;
20000
20025
  }
20001
20026
  /**
20002
20027
  * Open - Service tag for browser/editor launch operations.
20003
20028
  */
20004
20029
  var Open = class extends Service()("t3/open") {};
20005
- const resolveEditorLaunch = fnUntraced(function* (input, platform = process.platform) {
20030
+ const resolveEditorLaunch = fnUntraced(function* (input, platform = process.platform, env = process.env) {
20006
20031
  const editorDef = EDITORS.find((editor) => editor.id === input.editor);
20007
20032
  if (!editorDef) return yield* new OpenError({ message: `Unknown editor: ${input.editor}` });
20008
- if (editorDef.command) return shouldUseGotoFlag(editorDef, input.cwd) ? {
20009
- command: editorDef.command,
20010
- args: ["--goto", input.cwd]
20011
- } : {
20012
- command: editorDef.command,
20013
- args: [input.cwd]
20014
- };
20033
+ if (editorDef.commands) {
20034
+ const command = resolveAvailableCommand(editorDef.commands, {
20035
+ platform,
20036
+ env
20037
+ }) ?? editorDef.commands[0];
20038
+ return shouldUseGotoFlag(editorDef, input.cwd) ? {
20039
+ command,
20040
+ args: ["--goto", input.cwd]
20041
+ } : {
20042
+ command,
20043
+ args: [input.cwd]
20044
+ };
20045
+ }
20015
20046
  if (editorDef.id !== "file-manager") return yield* new OpenError({ message: `Unsupported editor: ${input.editor}` });
20016
20047
  return {
20017
20048
  command: fileManagerCommandForPlatform(platform),
@@ -24278,10 +24309,13 @@ function projectEvent(model, event) {
24278
24309
  createdAt: payload.createdAt,
24279
24310
  updatedAt: payload.updatedAt
24280
24311
  }, event.type, "message");
24281
- const cappedMessages = (thread.messages.find((entry) => entry.id === message.id) ? thread.messages.map((entry) => entry.id === message.id ? {
24312
+ const existingMessage = thread.messages.find((entry) => entry.id === message.id);
24313
+ const shouldRefreshImportedMessageCreatedAt = message.id.startsWith(`${payload.threadId}:`) && !message.streaming;
24314
+ const cappedMessages = (existingMessage ? thread.messages.map((entry) => entry.id === message.id ? {
24282
24315
  ...entry,
24283
24316
  text: message.streaming ? `${entry.text}${message.text}` : message.role === "user" && message.attachments !== void 0 ? message.text : message.text.length > 0 ? message.text : entry.text,
24284
24317
  streaming: message.streaming,
24318
+ createdAt: shouldRefreshImportedMessageCreatedAt ? message.createdAt : entry.createdAt,
24285
24319
  updatedAt: message.updatedAt,
24286
24320
  turnId: message.turnId,
24287
24321
  ...message.attachments !== void 0 ? { attachments: message.attachments } : {}
@@ -28515,6 +28549,7 @@ const makeOrchestrationProjectionPipeline = gen(function* () {
28515
28549
  const existingMessage = (yield* projectionThreadMessageRepository.listByThreadId({ threadId: event.payload.threadId })).find((row) => row.messageId === event.payload.messageId);
28516
28550
  const nextText = existingMessage && event.payload.streaming ? `${existingMessage.text}${event.payload.text}` : existingMessage && event.payload.text.length === 0 ? existingMessage.text : event.payload.text;
28517
28551
  const nextAttachments = event.payload.attachments !== void 0 ? yield* materializeAttachmentsForProjection({ attachments: event.payload.attachments }) : existingMessage?.attachments;
28552
+ const shouldRefreshImportedMessageCreatedAt = event.payload.messageId.startsWith(`${event.payload.threadId}:`) && !event.payload.streaming;
28518
28553
  yield* projectionThreadMessageRepository.upsert({
28519
28554
  messageId: event.payload.messageId,
28520
28555
  threadId: event.payload.threadId,
@@ -28523,7 +28558,7 @@ const makeOrchestrationProjectionPipeline = gen(function* () {
28523
28558
  text: nextText,
28524
28559
  ...nextAttachments !== void 0 ? { attachments: [...nextAttachments] } : {},
28525
28560
  isStreaming: event.payload.streaming,
28526
- createdAt: existingMessage?.createdAt ?? event.payload.createdAt,
28561
+ createdAt: existingMessage && !shouldRefreshImportedMessageCreatedAt ? existingMessage.createdAt : event.payload.createdAt,
28527
28562
  updatedAt: event.payload.updatedAt
28528
28563
  });
28529
28564
  return;
@@ -31692,6 +31727,8 @@ const EXTERNAL_SYNC_MARKER_VERSION = 1;
31692
31727
  const EXTERNAL_THREAD_PAGE_SIZE = 100;
31693
31728
  const EXTERNAL_SYNC_FOREGROUND_THREAD_LIMIT = 8;
31694
31729
  const EXTERNAL_SYNC_APP_SERVER_IDLE_TTL_MS = 45e3;
31730
+ const EXTERNAL_SYNC_WATCHER_SETTLE_MS = 350;
31731
+ const EXTERNAL_SYNC_WATCH_DIRECTORY_NAMES = ["sessions", "archived_sessions"];
31695
31732
  const DEFAULT_IMPORTED_MODEL = "gpt-5-codex";
31696
31733
  function toImportedModelSelection(model) {
31697
31734
  return {
@@ -31720,8 +31757,10 @@ const EXTERNAL_SYNC_APP_SERVER_CLIENT_INFO = {
31720
31757
  let sharedExternalSyncAppServer = null;
31721
31758
  let sharedExternalSyncAppServerHomePath = null;
31722
31759
  let sharedExternalSyncAppServerIdleTimer = null;
31723
- let externalCodexThreadSyncWatcher = null;
31760
+ let externalCodexThreadSyncWatchers = [];
31724
31761
  let externalCodexThreadSyncWatcherHomePath = null;
31762
+ let externalCodexThreadSyncWatcherSettleTimer = null;
31763
+ let externalCodexThreadSyncWatcherPendingTrigger = null;
31725
31764
  const externalSyncAppServerLeaseCount = /* @__PURE__ */ new Map();
31726
31765
  let externalCodexThreadSyncRefreshQueue = null;
31727
31766
  function asRecord$3(value) {
@@ -31732,6 +31771,11 @@ function clearSharedExternalSyncAppServerIdleTimer() {
31732
31771
  clearTimeout(sharedExternalSyncAppServerIdleTimer);
31733
31772
  sharedExternalSyncAppServerIdleTimer = null;
31734
31773
  }
31774
+ function clearExternalCodexThreadSyncWatcherSettleTimer() {
31775
+ if (!externalCodexThreadSyncWatcherSettleTimer) return;
31776
+ clearTimeout(externalCodexThreadSyncWatcherSettleTimer);
31777
+ externalCodexThreadSyncWatcherSettleTimer = null;
31778
+ }
31735
31779
  function incrementExternalSyncAppServerLease(appServer) {
31736
31780
  externalSyncAppServerLeaseCount.set(appServer, (externalSyncAppServerLeaseCount.get(appServer) ?? 0) + 1);
31737
31781
  }
@@ -31798,8 +31842,10 @@ async function withExternalSyncAppServer(homePath, task) {
31798
31842
  }
31799
31843
  }
31800
31844
  function closeExternalCodexThreadSyncWatcher() {
31801
- externalCodexThreadSyncWatcher?.close();
31802
- externalCodexThreadSyncWatcher = null;
31845
+ clearExternalCodexThreadSyncWatcherSettleTimer();
31846
+ externalCodexThreadSyncWatcherPendingTrigger = null;
31847
+ for (const watcher of externalCodexThreadSyncWatchers) watcher.close();
31848
+ externalCodexThreadSyncWatchers = [];
31803
31849
  externalCodexThreadSyncWatcherHomePath = null;
31804
31850
  }
31805
31851
  function setExternalCodexThreadSyncRefreshQueue(queue) {
@@ -31819,26 +31865,102 @@ async function requestExternalCodexThreadSyncRefresh(trigger) {
31819
31865
  }
31820
31866
  }
31821
31867
  function queueExternalCodexThreadSyncRefresh(trigger) {
31822
- requestExternalCodexThreadSyncRefresh(trigger);
31868
+ externalCodexThreadSyncWatcherPendingTrigger = trigger;
31869
+ if (externalCodexThreadSyncWatcherSettleTimer) return;
31870
+ externalCodexThreadSyncWatcherSettleTimer = setTimeout(() => {
31871
+ const nextTrigger = externalCodexThreadSyncWatcherPendingTrigger ?? "external-codex-thread-sync-watcher";
31872
+ externalCodexThreadSyncWatcherPendingTrigger = null;
31873
+ clearExternalCodexThreadSyncWatcherSettleTimer();
31874
+ requestExternalCodexThreadSyncRefresh(nextTrigger);
31875
+ }, EXTERNAL_SYNC_WATCHER_SETTLE_MS);
31876
+ externalCodexThreadSyncWatcherSettleTimer.unref();
31877
+ }
31878
+ function supportsRecursiveExternalCodexThreadSyncWatcher() {
31879
+ return process.platform === "darwin" || process.platform === "win32";
31880
+ }
31881
+ function collectExternalCodexThreadSyncDirectoryWatchPaths(rootPath) {
31882
+ const collectedPaths = [];
31883
+ const stack = [rootPath];
31884
+ const seenPaths = /* @__PURE__ */ new Set();
31885
+ while (stack.length > 0) {
31886
+ const currentPath = stack.pop();
31887
+ if (!currentPath) continue;
31888
+ const resolvedCurrentPath = path.resolve(currentPath);
31889
+ if (seenPaths.has(resolvedCurrentPath)) continue;
31890
+ seenPaths.add(resolvedCurrentPath);
31891
+ let stat;
31892
+ try {
31893
+ stat = fs.statSync(resolvedCurrentPath);
31894
+ } catch {
31895
+ continue;
31896
+ }
31897
+ if (!stat.isDirectory()) continue;
31898
+ collectedPaths.push(resolvedCurrentPath);
31899
+ let entries;
31900
+ try {
31901
+ entries = fs.readdirSync(resolvedCurrentPath, { withFileTypes: true });
31902
+ } catch {
31903
+ continue;
31904
+ }
31905
+ for (const entry of entries) {
31906
+ if (!entry.isDirectory()) continue;
31907
+ stack.push(path.join(resolvedCurrentPath, entry.name));
31908
+ }
31909
+ }
31910
+ return collectedPaths;
31911
+ }
31912
+ function collectExternalCodexThreadSyncWatchTargets(homePath) {
31913
+ const recursive = supportsRecursiveExternalCodexThreadSyncWatcher();
31914
+ const targets = [];
31915
+ for (const relativePath of EXTERNAL_SYNC_WATCH_DIRECTORY_NAMES) {
31916
+ const absolutePath = path.join(homePath, relativePath);
31917
+ if (!fs.existsSync(absolutePath)) continue;
31918
+ if (recursive) {
31919
+ targets.push({
31920
+ watchPath: absolutePath,
31921
+ label: relativePath,
31922
+ recursive: true
31923
+ });
31924
+ continue;
31925
+ }
31926
+ for (const watchPath of collectExternalCodexThreadSyncDirectoryWatchPaths(absolutePath)) targets.push({
31927
+ watchPath,
31928
+ label: path.relative(homePath, watchPath) || ".",
31929
+ recursive: false
31930
+ });
31931
+ }
31932
+ if (targets.length === 0) targets.push({
31933
+ watchPath: homePath,
31934
+ label: ".",
31935
+ recursive
31936
+ });
31937
+ return targets;
31823
31938
  }
31824
31939
  function startExternalCodexThreadSyncWatcher(homePath) {
31825
31940
  const resolvedHomePath = path.resolve(homePath);
31826
- if (externalCodexThreadSyncWatcher !== null && externalCodexThreadSyncWatcherHomePath === resolvedHomePath) return;
31941
+ if (externalCodexThreadSyncWatchers.length > 0 && externalCodexThreadSyncWatcherHomePath === resolvedHomePath) return;
31827
31942
  closeExternalCodexThreadSyncWatcher();
31828
31943
  try {
31829
- externalCodexThreadSyncWatcher = fs.watch(resolvedHomePath, { recursive: process.platform === "darwin" || process.platform === "win32" }, (_eventType, filename) => {
31830
- if (filename && typeof filename === "string" && !filename.trim()) return;
31831
- queueExternalCodexThreadSyncRefresh(filename ?? resolvedHomePath);
31832
- });
31833
- externalCodexThreadSyncWatcherHomePath = resolvedHomePath;
31834
- externalCodexThreadSyncWatcher.on("error", (error) => {
31835
- console.warn("[t3 external thread sync] watcher error", {
31836
- homePath: resolvedHomePath,
31837
- error
31944
+ const watchTargets = collectExternalCodexThreadSyncWatchTargets(resolvedHomePath);
31945
+ for (const target of watchTargets) {
31946
+ const watcher = fs.watch(target.watchPath, { recursive: target.recursive }, (_eventType, filename) => {
31947
+ if (filename && typeof filename === "string" && !filename.trim()) return;
31948
+ const suffix = typeof filename === "string" && filename.trim().length > 0 ? `:${filename}` : "";
31949
+ queueExternalCodexThreadSyncRefresh(`${target.label}${suffix}`);
31838
31950
  });
31839
- closeExternalCodexThreadSyncWatcher();
31840
- });
31951
+ watcher.on("error", (error) => {
31952
+ console.warn("[t3 external thread sync] watcher error", {
31953
+ homePath: resolvedHomePath,
31954
+ watchPath: target.watchPath,
31955
+ error
31956
+ });
31957
+ closeExternalCodexThreadSyncWatcher();
31958
+ });
31959
+ externalCodexThreadSyncWatchers.push(watcher);
31960
+ }
31961
+ externalCodexThreadSyncWatcherHomePath = resolvedHomePath;
31841
31962
  } catch (error) {
31963
+ closeExternalCodexThreadSyncWatcher();
31842
31964
  console.warn("[t3 external thread sync] failed to start watcher", {
31843
31965
  homePath: resolvedHomePath,
31844
31966
  error
@@ -31886,6 +32008,57 @@ function linePreview(text, fallback) {
31886
32008
  if (!normalized) return fallback;
31887
32009
  return normalized.length > 120 ? `${normalized.slice(0, 117)}...` : normalized;
31888
32010
  }
32011
+ function hasImportedVisibleUserMessageContent(payload) {
32012
+ return firstNonEmptyString(payload.message) !== null || Array.isArray(payload.text_elements) && payload.text_elements.length > 0 || Array.isArray(payload.images) && payload.images.length > 0 || Array.isArray(payload.local_images) && payload.local_images.length > 0;
32013
+ }
32014
+ async function readImportedVisibleMessageTimeline(thread) {
32015
+ const threadPath = resolveLegacyFilePath(firstNonEmptyString(thread.path) ?? "");
32016
+ if (!threadPath) return null;
32017
+ let contents;
32018
+ try {
32019
+ contents = await fs$1.readFile(threadPath, "utf8");
32020
+ } catch {
32021
+ return null;
32022
+ }
32023
+ const user = [];
32024
+ const assistant = [];
32025
+ for (const rawLine of contents.split(/\r?\n/)) {
32026
+ const line = rawLine.trim();
32027
+ if (!line) continue;
32028
+ let entry;
32029
+ try {
32030
+ entry = JSON.parse(line);
32031
+ } catch {
32032
+ continue;
32033
+ }
32034
+ const record = asRecord$3(entry);
32035
+ if (!record || asTrimmedString$1(record.type) !== "event_msg") continue;
32036
+ const payload = asRecord$3(record.payload);
32037
+ if (!payload) continue;
32038
+ const timestamp = firstNonEmptyString(record.timestamp, payload.timestamp, payload.createdAt, payload.created_at);
32039
+ if (!timestamp || !Number.isFinite(Date.parse(timestamp))) continue;
32040
+ switch (asTrimmedString$1(payload.type)) {
32041
+ case "user_message":
32042
+ if (hasImportedVisibleUserMessageContent(payload)) user.push(timestamp);
32043
+ break;
32044
+ case "agent_message":
32045
+ if (firstNonEmptyString(payload.message) !== null) assistant.push(timestamp);
32046
+ break;
32047
+ }
32048
+ }
32049
+ return user.length > 0 || assistant.length > 0 ? {
32050
+ user,
32051
+ assistant
32052
+ } : null;
32053
+ }
32054
+ function takeImportedVisibleMessageTimestamp(timeline, role) {
32055
+ if (!timeline) return null;
32056
+ const queue = timeline[role];
32057
+ const nextTimestamp = queue[0];
32058
+ if (!nextTimestamp) return null;
32059
+ queue.shift();
32060
+ return nextTimestamp;
32061
+ }
31889
32062
  function listThreadSummariesFromResponse(payload) {
31890
32063
  const root = asRecord$3(payload);
31891
32064
  const result = asRecord$3(root?.result) ?? root;
@@ -32216,6 +32389,7 @@ async function buildThreadImportArtifacts(input) {
32216
32389
  const activities = [];
32217
32390
  const proposedPlans = [];
32218
32391
  const turns = Array.isArray(thread.turns) ? thread.turns : [];
32392
+ const visibleMessageTimeline = await readImportedVisibleMessageTimeline(thread);
32219
32393
  let latestArtifactUpdatedAt = createdAt;
32220
32394
  let latestUpdatedAt = summaryUpdatedAt;
32221
32395
  for (const [turnIndex, turnEntry] of turns.entries()) {
@@ -32229,7 +32403,8 @@ async function buildThreadImportArtifacts(input) {
32229
32403
  if (!item) continue;
32230
32404
  const itemId = firstNonEmptyString(item.id) ?? `${turnIndex + 1}-${itemIndex + 1}`;
32231
32405
  const type = asTrimmedString$1(item.type);
32232
- const timestamp = nextIsoFromThreadCursor(thread, cursor, item.createdAt ?? item.updatedAt ?? turn.createdAt ?? turn.updatedAt, fallbackBaseMs);
32406
+ const visibleMessageRole = type === "userMessage" ? "user" : type === "agentMessage" ? "assistant" : null;
32407
+ const timestamp = nextIsoFromThreadCursor(thread, cursor, (visibleMessageRole !== null ? takeImportedVisibleMessageTimestamp(visibleMessageTimeline, visibleMessageRole) : null) ?? item.createdAt ?? item.updatedAt ?? item.startedAt ?? item.completedAt ?? turn.createdAt ?? turn.updatedAt ?? turn.startedAt ?? turn.completedAt, fallbackBaseMs);
32233
32408
  if (timestamp > latestArtifactUpdatedAt) latestArtifactUpdatedAt = timestamp;
32234
32409
  if (timestamp > latestUpdatedAt) latestUpdatedAt = timestamp;
32235
32410
  if (type === "userMessage") {
@@ -32294,7 +32469,7 @@ async function buildThreadImportArtifacts(input) {
32294
32469
  }
32295
32470
  const turnError = firstNonEmptyString(turn.error);
32296
32471
  if (turnError) {
32297
- const timestamp = nextIsoFromThreadCursor(thread, cursor, turn.updatedAt ?? turn.completedAt ?? thread.updatedAt, fallbackBaseMs);
32472
+ const timestamp = nextIsoFromThreadCursor(thread, cursor, turn.updatedAt ?? turn.completedAt ?? turn.startedAt ?? thread.updatedAt, fallbackBaseMs);
32298
32473
  if (timestamp > latestArtifactUpdatedAt) latestArtifactUpdatedAt = timestamp;
32299
32474
  if (timestamp > latestUpdatedAt) latestUpdatedAt = timestamp;
32300
32475
  const turnKey = turnId ? turnId : TurnId.makeUnsafe(`${thread.id}:turn:${turnIndex + 1}`);
@@ -32526,6 +32701,11 @@ function latestIsoTimestamp(left, right) {
32526
32701
  const rightMs = toEpochMs(right, 0);
32527
32702
  return new Date(Math.max(leftMs, rightMs)).toISOString();
32528
32703
  }
32704
+ function shouldRefreshImportedMessage(input) {
32705
+ const { existingMessage, nextMessage } = input;
32706
+ if (!existingMessage) return true;
32707
+ return existingMessage.createdAt !== nextMessage.createdAt || existingMessage.updatedAt !== nextMessage.updatedAt || existingMessage.text !== nextMessage.text || existingMessage.turnId !== nextMessage.turnId || existingMessage.streaming !== nextMessage.streaming || JSON.stringify(existingMessage.attachments ?? null) !== JSON.stringify(nextMessage.attachments ?? null);
32708
+ }
32529
32709
  function hasImportedThreadArtifacts(state) {
32530
32710
  return state.messageIds.size > 0 || state.activityIds.size > 0 || state.planIds.size > 0;
32531
32711
  }
@@ -32728,7 +32908,11 @@ function importExternalThreadArtifacts(input) {
32728
32908
  input.state.threadStateById.set(input.threadId, existingThreadState);
32729
32909
  let importedMessageCount = 0;
32730
32910
  for (const message of input.importArtifacts.messages) {
32731
- if (existingThreadState.messageIds.has(message.id) || shouldSkipImportedMessageForLocalContinuation({
32911
+ const existingMessage = existingThreadReadModel?.messages.find((entry) => entry.id === message.id);
32912
+ if (!shouldRefreshImportedMessage({
32913
+ existingMessage,
32914
+ nextMessage: message
32915
+ }) || shouldSkipImportedMessageForLocalContinuation({
32732
32916
  message,
32733
32917
  dedupState: localContinuationDedupState
32734
32918
  })) continue;
@@ -52640,6 +52824,10 @@ function sendDesktopParentMessage$1(payload) {
52640
52824
  process.send(payload);
52641
52825
  } catch {}
52642
52826
  }
52827
+ const GIT_STATUS_WATCH_REFRESH_INTERVAL_MS = 1e4;
52828
+ function fingerprintGitStatus(status) {
52829
+ return JSON.stringify(status);
52830
+ }
52643
52831
  function appendSessionError(session, chunk) {
52644
52832
  const normalized = compactErrorText(chunk);
52645
52833
  if (!normalized) return;
@@ -52742,6 +52930,8 @@ const createServer = fn(function* () {
52742
52930
  });
52743
52931
  yield* addFinalizer$1(() => promise(() => accountPool.dispose()));
52744
52932
  const clients = yield* make$11(/* @__PURE__ */ new Set());
52933
+ const watchedGitStatusesByCwd = /* @__PURE__ */ new Map();
52934
+ const watchedGitStatusCwdsByClient = /* @__PURE__ */ new WeakMap();
52745
52935
  const logger = createLogger("ws");
52746
52936
  const readiness = yield* makeServerReadiness;
52747
52937
  function logOutgoingPush(push, recipients) {
@@ -53236,6 +53426,96 @@ const createServer = fn(function* () {
53236
53426
  cause
53237
53427
  })));
53238
53428
  const runPromise$1 = runPromiseWith(yield* services());
53429
+ const publishGitStatusToClient = (client, cwd, status) => pushBus.publishClient(client, WS_CHANNELS.gitStatusUpdated, {
53430
+ cwd,
53431
+ status
53432
+ }).pipe(asVoid);
53433
+ const stopGitStatusWatcher = (cwd, watcher) => {
53434
+ clearInterval(watcher.intervalId);
53435
+ watchedGitStatusesByCwd.delete(cwd);
53436
+ };
53437
+ const scheduleWatchedGitStatusRefresh = (cwd) => {
53438
+ const watcher = watchedGitStatusesByCwd.get(cwd);
53439
+ if (!watcher) return Promise.resolve();
53440
+ if (watcher.refreshPromise) return watcher.refreshPromise;
53441
+ const refreshPromise = runPromise$1(gitManager.status({ cwd }).pipe(catch_(() => succeed(null)), flatMap((status) => {
53442
+ if (status === null) return void_$1;
53443
+ return sync(() => {
53444
+ const currentWatcher = watchedGitStatusesByCwd.get(cwd);
53445
+ if (!currentWatcher) return null;
53446
+ const nextFingerprint = fingerprintGitStatus(status);
53447
+ const hasChanged = currentWatcher.lastFingerprint !== nextFingerprint;
53448
+ currentWatcher.lastFingerprint = nextFingerprint;
53449
+ currentWatcher.lastStatus = status;
53450
+ if (!hasChanged || currentWatcher.subscribers.size === 0) return null;
53451
+ return [...currentWatcher.subscribers];
53452
+ }).pipe(flatMap((subscribers) => subscribers === null ? void_$1 : forEach(subscribers, (client) => publishGitStatusToClient(client, cwd, status), { discard: true })));
53453
+ })));
53454
+ let trackedRefreshPromise;
53455
+ trackedRefreshPromise = refreshPromise.finally(() => {
53456
+ const currentWatcher = watchedGitStatusesByCwd.get(cwd);
53457
+ if (currentWatcher?.refreshPromise === trackedRefreshPromise) currentWatcher.refreshPromise = null;
53458
+ });
53459
+ watcher.refreshPromise = trackedRefreshPromise;
53460
+ return trackedRefreshPromise;
53461
+ };
53462
+ const getOrCreateGitStatusWatcher = (cwd) => {
53463
+ const existing = watchedGitStatusesByCwd.get(cwd);
53464
+ if (existing) return existing;
53465
+ const watcher = {
53466
+ subscribers: /* @__PURE__ */ new Set(),
53467
+ intervalId: setInterval(() => {
53468
+ scheduleWatchedGitStatusRefresh(cwd);
53469
+ }, GIT_STATUS_WATCH_REFRESH_INTERVAL_MS),
53470
+ lastFingerprint: null,
53471
+ lastStatus: null,
53472
+ refreshPromise: null
53473
+ };
53474
+ watchedGitStatusesByCwd.set(cwd, watcher);
53475
+ return watcher;
53476
+ };
53477
+ const removeGitStatusWatcherSubscriber = (client, cwd) => {
53478
+ const watcher = watchedGitStatusesByCwd.get(cwd);
53479
+ if (!watcher) return;
53480
+ watcher.subscribers.delete(client);
53481
+ if (watcher.subscribers.size === 0) stopGitStatusWatcher(cwd, watcher);
53482
+ };
53483
+ const watchGitStatusForClient = fnUntraced(function* (client, cwd) {
53484
+ let watchedCwds = watchedGitStatusCwdsByClient.get(client);
53485
+ if (!watchedCwds) {
53486
+ watchedCwds = /* @__PURE__ */ new Set();
53487
+ watchedGitStatusCwdsByClient.set(client, watchedCwds);
53488
+ }
53489
+ if (watchedCwds.has(cwd)) {
53490
+ const watcher = watchedGitStatusesByCwd.get(cwd);
53491
+ if (watcher?.lastStatus) yield* publishGitStatusToClient(client, cwd, watcher.lastStatus);
53492
+ return;
53493
+ }
53494
+ watchedCwds.add(cwd);
53495
+ const watcher = getOrCreateGitStatusWatcher(cwd);
53496
+ watcher.subscribers.add(client);
53497
+ if (watcher.lastStatus) {
53498
+ yield* publishGitStatusToClient(client, cwd, watcher.lastStatus);
53499
+ return;
53500
+ }
53501
+ yield* promise(() => scheduleWatchedGitStatusRefresh(cwd));
53502
+ });
53503
+ const unwatchGitStatusForClient = (client, cwd) => {
53504
+ const watchedCwds = watchedGitStatusCwdsByClient.get(client);
53505
+ if (!watchedCwds?.delete(cwd)) return;
53506
+ if (watchedCwds.size === 0) watchedGitStatusCwdsByClient.delete(client);
53507
+ removeGitStatusWatcherSubscriber(client, cwd);
53508
+ };
53509
+ const unwatchAllGitStatusForClient = (client) => {
53510
+ const watchedCwds = watchedGitStatusCwdsByClient.get(client);
53511
+ if (!watchedCwds) return;
53512
+ watchedGitStatusCwdsByClient.delete(client);
53513
+ for (const cwd of watchedCwds) removeGitStatusWatcherSubscriber(client, cwd);
53514
+ };
53515
+ yield* addFinalizer$1(sync(() => {
53516
+ for (const watcher of watchedGitStatusesByCwd.values()) clearInterval(watcher.intervalId);
53517
+ watchedGitStatusesByCwd.clear();
53518
+ }));
53239
53519
  const telegramBridge = createTelegramBridge({
53240
53520
  getSnapshot: () => runPromise$1(projectionReadModelQuery.getSnapshot()),
53241
53521
  getThreadSnapshot: (threadId) => runPromise$1(gen(function* () {
@@ -53410,6 +53690,18 @@ const createServer = fn(function* () {
53410
53690
  const body = stripRequestTag(request.body);
53411
53691
  return yield* gitManager.status(body);
53412
53692
  }
53693
+ case WS_METHODS.gitWatchStatus: {
53694
+ const body = stripRequestTag(request.body);
53695
+ if (!ws) return yield* new RouteRequestError({ message: "Git status watching requires a WebSocket client." });
53696
+ yield* watchGitStatusForClient(ws, yield* normalizeProjectWorkspaceRoot(body.cwd));
53697
+ return;
53698
+ }
53699
+ case WS_METHODS.gitUnwatchStatus: {
53700
+ const body = stripRequestTag(request.body);
53701
+ if (!ws) return yield* new RouteRequestError({ message: "Git status watching requires a WebSocket client." });
53702
+ unwatchGitStatusForClient(ws, yield* normalizeProjectWorkspaceRoot(body.cwd));
53703
+ return;
53704
+ }
53413
53705
  case WS_METHODS.gitPull: {
53414
53706
  const body = stripRequestTag(request.body);
53415
53707
  return yield* git.pullCurrentBranch(body.cwd);
@@ -53918,12 +54210,14 @@ const createServer = fn(function* () {
53918
54210
  runPromise$1(handleMessage(ws, raw).pipe(ignoreCause({ log: true })));
53919
54211
  });
53920
54212
  ws.on("close", () => {
54213
+ unwatchAllGitStatusForClient(ws);
53921
54214
  runPromise$1(update(clients, (clients) => {
53922
54215
  clients.delete(ws);
53923
54216
  return clients;
53924
54217
  }));
53925
54218
  });
53926
54219
  ws.on("error", () => {
54220
+ unwatchAllGitStatusForClient(ws);
53927
54221
  runPromise$1(update(clients, (clients) => {
53928
54222
  clients.delete(ws);
53929
54223
  return clients;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexuse-cli",
3
- "version": "3.6.2",
3
+ "version": "3.6.3",
4
4
  "description": "CodexUse CLI for profiles, Accounts Pool, daemon mode, licenses, and sync.",
5
5
  "author": {
6
6
  "name": "Hoang",