codex-overleaf-link 2.1.4 → 2.2.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.
Files changed (69) hide show
  1. package/README.md +27 -23
  2. package/extension/bootstrap/background.js +14 -1
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/runtime-manifest.json +11 -0
  5. package/extension/src/background.js +507 -8
  6. package/extension/src/backgroundUpdateCoordinator.js +284 -19
  7. package/extension/src/content/activeTurnControl.js +179 -0
  8. package/extension/src/content/composerPanel.js +19 -6
  9. package/extension/src/content/contentRuntime.js +335 -317
  10. package/extension/src/content/diagnosticsPanel.js +132 -1
  11. package/extension/src/content/markdownDomRenderer.js +451 -0
  12. package/extension/src/content/markdownText.js +86 -7
  13. package/extension/src/content/mathText.js +49 -4
  14. package/extension/src/content/modelPicker.js +14 -5
  15. package/extension/src/content/nativeChannel.js +7 -1
  16. package/extension/src/content/otWarmMirror.js +1 -1
  17. package/extension/src/content/panelRenderer.js +112 -12
  18. package/extension/src/content/pendingInputView.js +106 -0
  19. package/extension/src/content/projectProviderSelection.js +68 -0
  20. package/extension/src/content/projectSessionCleanup.js +104 -0
  21. package/extension/src/content/providerSettingsCoordinator.js +170 -22
  22. package/extension/src/content/providerSettingsDialog.js +2 -2
  23. package/extension/src/content/recentProjects.js +24 -13
  24. package/extension/src/content/runController.js +2 -2
  25. package/extension/src/content/runGuidanceController.js +70 -0
  26. package/extension/src/content/runGuidanceView.js +48 -0
  27. package/extension/src/content/runQueueScheduler.js +260 -0
  28. package/extension/src/content/runTimelineView.js +33 -80
  29. package/extension/src/content/sessionManager.js +2 -2
  30. package/extension/src/content/sessionPersistence.js +62 -0
  31. package/extension/src/content/settingsPanel.js +1 -1
  32. package/extension/src/content/updateNotice.js +80 -1
  33. package/extension/src/page/writebackRouter.js +3 -9
  34. package/extension/src/shared/compatibility.js +2 -1
  35. package/extension/src/shared/i18n.js +54 -0
  36. package/extension/src/shared/providerProfiles.js +3 -2
  37. package/extension/src/shared/runInputQueue.js +253 -0
  38. package/extension/src/shared/sessionState.js +45 -22
  39. package/extension/src/shared/storageDb.js +39 -44
  40. package/extension/src/shared/storageMigration.js +123 -3
  41. package/extension/src/shared/storageRunActions.js +34 -1
  42. package/extension/src/shared/updateConsent.js +15 -4
  43. package/extension/styles/panel.css +370 -23
  44. package/extension/vendor/katex/katex.min.css +1 -1
  45. package/extension/vendor/markdown-it/LICENSE +22 -0
  46. package/extension/vendor/markdown-it/SOURCE.json +7 -0
  47. package/extension/vendor/markdown-it/VERSION +1 -0
  48. package/extension/vendor/markdown-it/markdown-it.min.js +3 -0
  49. package/native-host/src/anthropicBridgeRequest.js +3 -3
  50. package/native-host/src/anthropicBridgeResponse.js +58 -17
  51. package/native-host/src/anthropicMessagesBridge.js +156 -23
  52. package/native-host/src/chatBridgeRequest.js +1 -1
  53. package/native-host/src/chatBridgeResponse.js +12 -8
  54. package/native-host/src/chatCompletionsBridge.js +22 -40
  55. package/native-host/src/codexPromptAssembly.js +3 -1
  56. package/native-host/src/codexProviderLaunch.js +6 -3
  57. package/native-host/src/codexSessionRunner.js +66 -66
  58. package/native-host/src/codexSessionTiming.js +45 -0
  59. package/native-host/src/diffEngine.js +10 -5
  60. package/native-host/src/index.js +58 -3
  61. package/native-host/src/managedInstall.js +45 -7
  62. package/native-host/src/providerProfile.js +2 -2
  63. package/native-host/src/providerStreamLifecycle.js +100 -0
  64. package/native-host/src/responsesPassthroughBridge.js +636 -0
  65. package/native-host/src/taskRunnerRuntime.js +81 -2
  66. package/native-host/src/updateArchive.js +4 -3
  67. package/native-host/src/updateManager.js +306 -19
  68. package/native-host/src/updateTrust.js +1 -1
  69. package/package.json +3 -1
@@ -528,15 +528,9 @@
528
528
  }
529
529
  const CANCELLED_RACE_SENTINEL = Symbol('writebackRouter.cancelled');
530
530
  async function raceOpAgainstCancellation(opPromise) {
531
- const racer = createCancellationRacer();
532
- try {
533
- return await Promise.race([
534
- opPromise,
535
- racer.promise.then(() => CANCELLED_RACE_SENTINEL)
536
- ]);
537
- } finally {
538
- racer.dispose();
539
- }
531
+ // Once an editor operation starts, preserve its actual result and undo
532
+ // metadata. The loop's pre-operation cancellation check stops the tail.
533
+ return await opPromise;
540
534
  }
541
535
 
542
536
  for (const rawOperation of operations) {
@@ -12,7 +12,7 @@
12
12
  const MIN_NATIVE_VERSION = '1.0.0';
13
13
  const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
14
14
  const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
15
- const BUILD_TARGET_VERSION = '2.1.4';
15
+ const BUILD_TARGET_VERSION = '2.2.0';
16
16
  const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
17
17
  const REQUIRED_CAPABILITIES = Object.freeze([
18
18
  'bridgePing',
@@ -21,6 +21,7 @@
21
21
  'mirrorStatus',
22
22
  'codexRun',
23
23
  'codexCancel',
24
+ 'codexSteer',
24
25
  'codexModels',
25
26
  'historyClearPlugin',
26
27
  'localSkills',
@@ -292,6 +292,7 @@
292
292
  providerSettingsHelp: 'Configure experimental third-party model APIs. Compatibility varies by provider and gateway.',
293
293
  providerSettingsConfigure: 'Configure',
294
294
  providerSettingsBuiltin: 'Built-in Codex is active',
295
+ providerSettingsLoading: 'Checking current project provider…',
295
296
  storageUsageLoading: 'Calculating usage\u2026',
296
297
  storageClearAll: 'Clear all history\u2026',
297
298
  storageClearAllHelp: 'Removes every stored session, run and audit record for all projects. Project settings and rules are kept.',
@@ -344,6 +345,24 @@
344
345
  warmMirrorStaleBlockedTitle: 'Blocked: local workspace is stale',
345
346
  send: 'Send',
346
347
  cancelRun: 'Cancel Current Task',
348
+ queueNextInput: 'Queue follow-up',
349
+ queuedInputQueued: 'Queued',
350
+ queuedInputPaused: 'Queue paused',
351
+ queuedInputGuiding: 'Sending guidance',
352
+ queuedInputExecuting: 'Running',
353
+ queuedInputGuide: 'Guide',
354
+ queuedInputGuideTitle: 'Send this message into the active Codex turn now.',
355
+ queuedInputGuideUnavailable: 'The active turn is not ready for guidance yet.',
356
+ queuedInputGuideFailed: 'Guidance could not be delivered. The message remains queued.',
357
+ queuedInputGuided: 'Follow-up guidance was delivered to the active Codex turn.',
358
+ queuedInputAdded: 'Follow-up queued. It will run after this task settles.',
359
+ queuedInputResume: 'Resume',
360
+ queuedInputRemove: 'Remove',
361
+ queuedInputAttachmentsUnsupported: 'Queued follow-ups currently support text and @context. Remove binary attachments before queuing.',
362
+ queuedInputError_empty_input: 'Enter a follow-up first.',
363
+ queuedInputError_input_too_long: 'This follow-up is too long to queue.',
364
+ queuedInputError_queue_full: 'The follow-up queue is full.',
365
+ queuedInputError_queue_too_large: 'The follow-up queue reached its storage limit.',
347
366
  contextTitle: '@ Context',
348
367
  refreshFileList: 'Refresh file list',
349
368
  contextStatus: 'Type @ to add files, or click +: files, attachments (PDF/images), @compile-log.',
@@ -469,6 +488,14 @@
469
488
  recentProjects_sessions_toggle: "Show / manage this project's sessions",
470
489
  recentProjects_sessions_action: 'View runs',
471
490
  recentProjects_sessions_action_open: 'Hide runs',
491
+ recentProjects_clearProject: 'Delete all sessions for this project',
492
+ recentProjects_clearProject_title: 'Delete all Codex sessions for {project}?',
493
+ recentProjects_clearProject_message: 'This permanently deletes all {count} plugin sessions, run history, and matching plugin-local Codex thread history for {project}. It will not modify Overleaf files or global Codex history.',
494
+ recentProjects_clearProject_confirm: 'Delete all',
495
+ recentProjects_clearProject_running: 'This project may still have a running Codex task. Cancel it before deleting all sessions.',
496
+ recentProjects_clearProject_done: 'Deleted {count} session(s) from {project}.',
497
+ recentProjects_clearProject_partial: 'Deleted {removed} of {count} sessions. The remaining records are still visible; retry to remove them.',
498
+ recentProjects_clearProject_historyPartial: 'Sessions were deleted, but {count} plugin-local Codex thread history item(s) could not be cleared.',
472
499
  recentProjects_cleanup: 'Remove this dead entry',
473
500
  recentProjects_cleanup_title: 'Remove dead project entry?',
474
501
  recentProjects_cleanup_message: 'This entry has no usable project link, so it cannot be opened or expanded. Removing it deletes all of its session records. This cannot be undone.',
@@ -778,6 +805,7 @@
778
805
  providerSettingsHelp: '配置实验性第三方模型 API;兼容性取决于具体模型服务与网关。',
779
806
  providerSettingsConfigure: '配置',
780
807
  providerSettingsBuiltin: '当前使用内置 Codex',
808
+ providerSettingsLoading: '正在读取当前项目的模型服务…',
781
809
  storageUsageLoading: '正在计算占用…',
782
810
  storageClearAll: '清空全部历史…',
783
811
  storageClearAllHelp: '删除所有项目已存储的全部会话、运行与审计记录。项目设置与规则会保留。',
@@ -830,6 +858,24 @@
830
858
  warmMirrorStaleBlockedTitle: '已阻止:本地 workspace 过期',
831
859
  send: '发送',
832
860
  cancelRun: '中断当前任务',
861
+ queueNextInput: '加入后续队列',
862
+ queuedInputQueued: '已排队',
863
+ queuedInputPaused: '队列已暂停',
864
+ queuedInputGuiding: '正在发送引导',
865
+ queuedInputExecuting: '正在运行',
866
+ queuedInputGuide: '立即引导',
867
+ queuedInputGuideTitle: '立即把这条消息发送到当前 Codex turn。',
868
+ queuedInputGuideUnavailable: '当前 turn 尚未进入可引导状态。',
869
+ queuedInputGuideFailed: '引导消息未送达,消息已保留在队列中。',
870
+ queuedInputGuided: '后续引导已发送到当前 Codex turn。',
871
+ queuedInputAdded: '后续任务已排队,将在当前任务完整结束后执行。',
872
+ queuedInputResume: '恢复队列',
873
+ queuedInputRemove: '删除',
874
+ queuedInputAttachmentsUnsupported: '当前排队消息支持文本和 @context;请先移除二进制附件。',
875
+ queuedInputError_empty_input: '请先输入后续任务。',
876
+ queuedInputError_input_too_long: '这条后续任务太长,无法排队。',
877
+ queuedInputError_queue_full: '后续任务队列已满。',
878
+ queuedInputError_queue_too_large: '后续任务队列已达到存储上限。',
833
879
  contextTitle: '@ 上下文',
834
880
  refreshFileList: '刷新文件列表',
835
881
  contextStatus: '输入 @ 添加文件,或点 +:文件、附件(PDF/图片)、@compile-log。',
@@ -954,6 +1000,14 @@
954
1000
  recentProjects_sessions_toggle: '查看/管理该项目的会话',
955
1001
  recentProjects_sessions_action: '查看会话',
956
1002
  recentProjects_sessions_action_open: '收起会话',
1003
+ recentProjects_clearProject: '删除该项目的全部会话',
1004
+ recentProjects_clearProject_title: '删除 {project} 的全部 Codex 会话?',
1005
+ recentProjects_clearProject_message: '这会永久删除 {project} 的全部 {count} 个插件会话、运行历史及对应的插件本地 Codex 线程历史。不会修改 Overleaf 文件或全局 Codex 历史。',
1006
+ recentProjects_clearProject_confirm: '全部删除',
1007
+ recentProjects_clearProject_running: '该项目可能仍有 Codex 任务在运行。请先取消任务,再删除全部会话。',
1008
+ recentProjects_clearProject_done: '已删除 {project} 的 {count} 个会话。',
1009
+ recentProjects_clearProject_partial: '已删除 {count} 个会话中的 {removed} 个。剩余记录仍会显示,可重试删除。',
1010
+ recentProjects_clearProject_historyPartial: '会话已删除,但有 {count} 条插件本地 Codex 线程历史未能清除。',
957
1011
  recentProjects_cleanup: '清除这条失效记录',
958
1012
  recentProjects_cleanup_title: '清除失效的项目记录?',
959
1013
  recentProjects_cleanup_message: '这条记录的项目链接已失效,无法打开或展开。清除后,它名下的全部会话记录将被删除,且无法恢复。',
@@ -159,7 +159,7 @@
159
159
  anthropicThinkingMode: 'budget',
160
160
  anthropicPromptCaching: false,
161
161
  impersonateClaudeCode: false,
162
- maxOutputTokens: 32768,
162
+ maxOutputTokens: 65536,
163
163
  hasSecret: false,
164
164
  secretUpdatedAt: 0,
165
165
  endpointDisclosureHost: '',
@@ -280,7 +280,8 @@
280
280
 
281
281
  function normalizeMaxOutputTokens(value) {
282
282
  const number = normalizeInteger(value);
283
- return !number || number === 8192 ? 32768 : number;
283
+ if (!number || number === 8192 || number === 32768) return 65536;
284
+ return Math.min(65536, Math.max(256, number));
284
285
  }
285
286
 
286
287
  return {
@@ -0,0 +1,253 @@
1
+ (function initCodexOverleafRunInputQueue(root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory();
4
+ } else {
5
+ root.CodexOverleafRunInputQueue = factory();
6
+ }
7
+ })(typeof window !== 'undefined' ? window : globalThis, function runInputQueueFactory() {
8
+ 'use strict';
9
+
10
+ const MAX_ITEMS = 20;
11
+ const MAX_TEXT_CHARS = 12000;
12
+ const MAX_QUEUE_BYTES = 128 * 1024;
13
+ const ACTIVE_STATUSES = new Set(['claimed', 'executing', 'steering']);
14
+ const VALID_STATUSES = new Set(['queued', 'paused', ...ACTIVE_STATUSES]);
15
+
16
+ function normalizeQueue(items, options = {}) {
17
+ const seen = new Set();
18
+ const normalized = [];
19
+ for (const raw of Array.isArray(items) ? items : []) {
20
+ const text = String(raw?.text || '').trim().slice(0, MAX_TEXT_CHARS);
21
+ const id = String(raw?.id || '').trim();
22
+ if (!id || !text || seen.has(id)) {
23
+ continue;
24
+ }
25
+ seen.add(id);
26
+ let status = VALID_STATUSES.has(raw.status) ? raw.status : 'queued';
27
+ if (options.recoverActive === true && ACTIVE_STATUSES.has(status)) {
28
+ status = 'paused';
29
+ }
30
+ normalized.push({
31
+ id,
32
+ clientUserMessageId: String(raw.clientUserMessageId || id),
33
+ text,
34
+ status,
35
+ createdAt: normalizeTimestamp(raw.createdAt),
36
+ updatedAt: normalizeTimestamp(raw.updatedAt),
37
+ sourceRunId: String(raw.sourceRunId || ''),
38
+ linkedRunId: status === 'executing' ? String(raw.linkedRunId || '') : '',
39
+ claimToken: status === 'claimed' || status === 'executing' ? String(raw.claimToken || '') : '',
40
+ pauseReason: status === 'paused' ? String(raw.pauseReason || '') : '',
41
+ payload: normalizePayload(raw.payload)
42
+ });
43
+ if (normalized.length >= MAX_ITEMS) {
44
+ break;
45
+ }
46
+ }
47
+ return trimToBudget(normalized);
48
+ }
49
+
50
+ function enqueue(items, input = {}, deps = {}) {
51
+ const queue = normalizeQueue(items);
52
+ const text = String(input.text || '').trim();
53
+ if (!text) {
54
+ return failure(queue, 'empty_input');
55
+ }
56
+ if (text.length > MAX_TEXT_CHARS) {
57
+ return failure(queue, 'input_too_long');
58
+ }
59
+ if (queue.length >= MAX_ITEMS) {
60
+ return failure(queue, 'queue_full');
61
+ }
62
+ const idFactory = deps.randomUUID || (() => 'pending_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8));
63
+ const nowFactory = deps.now || (() => new Date().toISOString());
64
+ const id = String(input.id || idFactory());
65
+ const timestamp = String(nowFactory());
66
+ const item = {
67
+ id,
68
+ clientUserMessageId: String(input.clientUserMessageId || id),
69
+ text,
70
+ status: input.status === 'paused' ? 'paused' : 'queued',
71
+ createdAt: timestamp,
72
+ updatedAt: timestamp,
73
+ sourceRunId: String(input.sourceRunId || ''),
74
+ linkedRunId: '',
75
+ claimToken: '',
76
+ pauseReason: input.status === 'paused' ? String(input.pauseReason || '') : '',
77
+ payload: normalizePayload(input.payload)
78
+ };
79
+ const next = [...queue, item];
80
+ if (estimateBytes(next) > MAX_QUEUE_BYTES) {
81
+ return failure(queue, 'queue_too_large');
82
+ }
83
+ return { ok: true, queue: next, item };
84
+ }
85
+
86
+ function remove(items, id) {
87
+ return normalizeQueue(items).filter(item => item.id !== id);
88
+ }
89
+
90
+ function markSteering(items, id) {
91
+ return update(items, id, item => ({
92
+ ...item,
93
+ status: 'steering',
94
+ updatedAt: new Date().toISOString(),
95
+ pauseReason: ''
96
+ }));
97
+ }
98
+
99
+ function returnToQueue(items, id, reason = '') {
100
+ return update(items, id, item => ({
101
+ ...item,
102
+ status: 'queued',
103
+ updatedAt: new Date().toISOString(),
104
+ linkedRunId: '',
105
+ claimToken: '',
106
+ pauseReason: String(reason || '')
107
+ }));
108
+ }
109
+
110
+ function claimNext(items, deps = {}) {
111
+ const queue = normalizeQueue(items);
112
+ const index = queue.findIndex(item => item.status === 'queued');
113
+ if (index < 0) {
114
+ return { ok: false, queue, item: null };
115
+ }
116
+ const tokenFactory = deps.randomUUID || (() => 'claim_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8));
117
+ const item = {
118
+ ...queue[index],
119
+ status: 'claimed',
120
+ claimToken: String(tokenFactory()),
121
+ updatedAt: new Date().toISOString(),
122
+ pauseReason: ''
123
+ };
124
+ queue[index] = item;
125
+ return { ok: true, queue, item };
126
+ }
127
+
128
+ function markExecuting(items, id, linkedRunId) {
129
+ return update(items, id, item => ({
130
+ ...item,
131
+ status: 'executing',
132
+ linkedRunId: String(linkedRunId || ''),
133
+ updatedAt: new Date().toISOString()
134
+ }));
135
+ }
136
+
137
+ function pauseAll(items, reason = '') {
138
+ return normalizeQueue(items).map(item => ({
139
+ ...item,
140
+ status: 'paused',
141
+ linkedRunId: '',
142
+ claimToken: '',
143
+ pauseReason: String(reason || ''),
144
+ updatedAt: new Date().toISOString()
145
+ }));
146
+ }
147
+
148
+ function resumeAll(items) {
149
+ return normalizeQueue(items).map(item => ({
150
+ ...item,
151
+ status: 'queued',
152
+ linkedRunId: '',
153
+ claimToken: '',
154
+ pauseReason: '',
155
+ updatedAt: new Date().toISOString()
156
+ }));
157
+ }
158
+
159
+ function update(items, id, updater) {
160
+ return normalizeQueue(items).map(item => item.id === id ? updater(item) : item);
161
+ }
162
+
163
+ function normalizePayload(payload = {}) {
164
+ return {
165
+ mode: ['ask', 'confirm', 'auto'].includes(payload.mode) ? payload.mode : 'ask',
166
+ providerId: String(payload.providerId || 'builtin').slice(0, 160),
167
+ providerRevision: String(payload.providerRevision || '').slice(0, 160),
168
+ model: String(payload.model || '').slice(0, 160),
169
+ reasoningEffort: String(payload.reasoningEffort || '').slice(0, 32),
170
+ speedTier: payload.speedTier === 'fast' ? 'fast' : 'standard',
171
+ autoRecompile: payload.autoRecompile !== false,
172
+ requireReviewing: payload.requireReviewing !== false,
173
+ focusFiles: (Array.isArray(payload.focusFiles) ? payload.focusFiles : [])
174
+ .map(path => String(path || '').replace(/\\/g, '/').replace(/^\/+/, '').trim())
175
+ .filter(Boolean)
176
+ .slice(0, 100)
177
+ };
178
+ }
179
+
180
+ function normalizeTimestamp(value) {
181
+ return typeof value === 'string' && value ? value : new Date().toISOString();
182
+ }
183
+
184
+ function trimToBudget(items) {
185
+ const queue = items.slice(-MAX_ITEMS);
186
+ while (queue.length && estimateBytes(queue) > MAX_QUEUE_BYTES) {
187
+ queue.shift();
188
+ }
189
+ return queue;
190
+ }
191
+
192
+ function estimateBytes(value) {
193
+ const text = JSON.stringify(value);
194
+ return typeof TextEncoder !== 'undefined'
195
+ ? new TextEncoder().encode(text).length
196
+ : text.length * 2;
197
+ }
198
+
199
+ function compactForStorage(items, helpers = {}) {
200
+ const normalizeField = helpers.normalizeField || (value => String(value || ''));
201
+ const normalizeDisplay = helpers.normalizeDisplay || normalizeField;
202
+ const normalizePaths = helpers.normalizePaths || (value => normalizePayload({ focusFiles: value }).focusFiles);
203
+ return (Array.isArray(items) ? items : [])
204
+ .slice(-MAX_ITEMS)
205
+ .map(item => {
206
+ const payload = item?.payload && typeof item.payload === 'object' ? item.payload : {};
207
+ return {
208
+ id: normalizeField(item?.id, 160),
209
+ clientUserMessageId: normalizeField(item?.clientUserMessageId, 160),
210
+ text: normalizeDisplay(item?.text, MAX_TEXT_CHARS),
211
+ status: VALID_STATUSES.has(item?.status) ? item.status : 'queued',
212
+ createdAt: typeof item?.createdAt === 'string' ? item.createdAt : '',
213
+ updatedAt: typeof item?.updatedAt === 'string' ? item.updatedAt : '',
214
+ sourceRunId: normalizeField(item?.sourceRunId, 160),
215
+ linkedRunId: normalizeField(item?.linkedRunId, 160),
216
+ claimToken: normalizeField(item?.claimToken, 160),
217
+ pauseReason: normalizeField(item?.pauseReason, 160),
218
+ payload: {
219
+ mode: typeof payload.mode === 'string' ? payload.mode : 'ask',
220
+ providerId: normalizeField(payload.providerId, 160) || 'builtin',
221
+ providerRevision: normalizeField(payload.providerRevision, 160),
222
+ model: normalizeField(payload.model, 160),
223
+ reasoningEffort: normalizeField(payload.reasoningEffort, 32),
224
+ speedTier: payload.speedTier === 'fast' ? 'fast' : 'standard',
225
+ autoRecompile: payload.autoRecompile !== false,
226
+ requireReviewing: payload.requireReviewing !== false,
227
+ focusFiles: normalizePaths(payload.focusFiles)
228
+ }
229
+ };
230
+ })
231
+ .filter(item => item.id && item.text);
232
+ }
233
+
234
+ function failure(queue, code) {
235
+ return { ok: false, queue, item: null, error: { code } };
236
+ }
237
+
238
+ return {
239
+ MAX_ITEMS,
240
+ MAX_QUEUE_BYTES,
241
+ MAX_TEXT_CHARS,
242
+ claimNext,
243
+ compactForStorage,
244
+ enqueue,
245
+ markExecuting,
246
+ markSteering,
247
+ normalizeQueue,
248
+ pauseAll,
249
+ remove,
250
+ resumeAll,
251
+ returnToQueue
252
+ };
253
+ });
@@ -127,6 +127,9 @@
127
127
  if (!VALID_SPEED_TIERS.has(state.speedTier)) {
128
128
  state.speedTier = DEFAULT_PANEL_STATE.speedTier;
129
129
  }
130
+ state.providerId = typeof state.providerId === 'string' && state.providerId.trim()
131
+ ? state.providerId.trim()
132
+ : 'builtin';
130
133
  if (!VALID_LOCALES.has(state.locale)) {
131
134
  state.locale = DEFAULT_PANEL_STATE.locale;
132
135
  }
@@ -178,7 +181,7 @@
178
181
  ...legacySession,
179
182
  task: state.task,
180
183
  mode: state.mode,
181
- providerId: 'builtin',
184
+ providerId: state.providerId,
182
185
  model: state.model,
183
186
  reasoningEffort: state.reasoningEffort,
184
187
  speedTier: state.speedTier,
@@ -212,7 +215,7 @@
212
215
  mode: VALID_MODES.has(session.mode) ? session.mode : fallbackState.mode,
213
216
  providerId: typeof session.providerId === 'string' && session.providerId.trim()
214
217
  ? session.providerId.trim()
215
- : 'builtin',
218
+ : (fallbackState.providerId || 'builtin'),
216
219
  model: typeof session.model === 'string' && session.model ? session.model : fallbackState.model,
217
220
  reasoningEffort: VALID_REASONING.has(session.reasoningEffort)
218
221
  ? session.reasoningEffort
@@ -222,7 +225,8 @@
222
225
  : fallbackState.speedTier,
223
226
  requireReviewing: session.requireReviewing !== false,
224
227
  focusFiles: normalizeFocusFiles(session.focusFiles),
225
- codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : ''
228
+ codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
229
+ pendingInputs: normalizePendingInputs(session.pendingInputs, options.restoreRunningRuns === true)
226
230
  };
227
231
  }
228
232
 
@@ -291,22 +295,12 @@
291
295
  history: Array.isArray(active.history) ? active.history.slice(-10) : [],
292
296
  focusFiles: normalizeFocusFiles(active.focusFiles),
293
297
  codexThreadId: active.codexThreadId || '',
294
- providerId: active.providerId || 'builtin'
298
+ providerId: state.providerId || 'builtin'
295
299
  };
296
300
  state.runs = Array.isArray(active.runs) ? active.runs : [];
297
301
  state.task = typeof active.task === 'string' ? active.task : '';
298
302
  state.focusFiles = normalizeFocusFiles(active.focusFiles);
299
303
  state.mode = VALID_MODES.has(active.mode) ? active.mode : DEFAULT_PANEL_STATE.mode;
300
- state.providerId = typeof active.providerId === 'string' && active.providerId
301
- ? active.providerId
302
- : 'builtin';
303
- state.model = typeof active.model === 'string' && active.model ? active.model : DEFAULT_PANEL_STATE.model;
304
- state.reasoningEffort = VALID_REASONING.has(active.reasoningEffort)
305
- ? active.reasoningEffort
306
- : DEFAULT_PANEL_STATE.reasoningEffort;
307
- state.speedTier = VALID_SPEED_TIERS.has(active.speedTier)
308
- ? active.speedTier
309
- : DEFAULT_PANEL_STATE.speedTier;
310
304
  state.requireReviewing = active.requireReviewing !== false;
311
305
 
312
306
  return state;
@@ -341,7 +335,8 @@
341
335
  : DEFAULT_PANEL_STATE.speedTier,
342
336
  requireReviewing: overrides.requireReviewing !== false,
343
337
  focusFiles: normalizeFocusFiles(overrides.focusFiles),
344
- codexThreadId: typeof overrides.codexThreadId === 'string' ? overrides.codexThreadId : ''
338
+ codexThreadId: typeof overrides.codexThreadId === 'string' ? overrides.codexThreadId : '',
339
+ pendingInputs: normalizePendingInputs(overrides.pendingInputs)
345
340
  };
346
341
  }
347
342
 
@@ -623,7 +618,12 @@
623
618
  undoBaseFiles: normalizeRunFiles(run.undoBaseFiles),
624
619
  undoTrackedChanges: normalizeRunTrackedChanges(run.undoTrackedChanges),
625
620
  undoExpectedFiles: normalizeRunFiles(run.undoExpectedFiles),
626
- undoStatus: sanitizeAssistantVisibleText(run.undoStatus)
621
+ undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
622
+ nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
623
+ codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
624
+ nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
625
+ queueItemId: sanitizeAssistantVisibleText(run.queueItemId),
626
+ interruptedDraft: run.interruptedDraft ? sanitizeAssistantVisibleValue(run.interruptedDraft) : undefined
627
627
  };
628
628
 
629
629
  applyTrackedChangeStatus(normalized, run.trackedChangeStatus);
@@ -720,6 +720,7 @@
720
720
  timestamp: typeof event.timestamp === 'string' ? event.timestamp : '',
721
721
  kind: typeof event.kind === 'string' ? event.kind : 'activity',
722
722
  technicalDetail: sanitizeAssistantVisibleValue(event.technicalDetail),
723
+ guidanceId: sanitizeAssistantVisibleText(event.guidanceId),
723
724
  streamKey: sanitizeAssistantVisibleText(event.streamKey),
724
725
  streamRole: sanitizeAssistantVisibleText(event.streamRole)
725
726
  };
@@ -965,11 +966,14 @@
965
966
  codexOverleafSkills: compactCodexOverleafSkillsForStorage(source.codexOverleafSkills),
966
967
  composerAttachments: normalizeComposerAttachmentsForPersistence(source.composerAttachments),
967
968
  mode: VALID_MODES.has(active?.mode) ? active.mode : normalizeMode(source.mode),
968
- model: normalizeTextField(active?.model || source.model || DEFAULT_PANEL_STATE.model, 80),
969
- reasoningEffort: VALID_REASONING.has(active?.reasoningEffort)
970
- ? active.reasoningEffort
969
+ providerId: typeof source.providerId === 'string' && source.providerId.trim()
970
+ ? source.providerId.trim()
971
+ : 'builtin',
972
+ model: normalizeTextField(source.model || DEFAULT_PANEL_STATE.model, 80),
973
+ reasoningEffort: VALID_REASONING.has(source.reasoningEffort)
974
+ ? source.reasoningEffort
971
975
  : normalizeReasoning(source.reasoningEffort),
972
- speedTier: normalizeSpeedTier(active?.speedTier || source.speedTier),
976
+ speedTier: normalizeSpeedTier(source.speedTier),
973
977
  locale: normalizeLocale(source.locale),
974
978
  requireReviewing: active ? active.requireReviewing !== false : source.requireReviewing !== false,
975
979
  autoOpen: source.autoOpen !== false,
@@ -1058,7 +1062,9 @@
1058
1062
  reasoningEffort: normalizeReasoning(session.reasoningEffort || fallbackState.reasoningEffort),
1059
1063
  speedTier: normalizeSpeedTier(session.speedTier || fallbackState.speedTier),
1060
1064
  requireReviewing: session.requireReviewing !== false,
1061
- focusFiles: normalizeFocusFiles(session.focusFiles)
1065
+ focusFiles: normalizeFocusFiles(session.focusFiles),
1066
+ codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
1067
+ pendingInputs: normalizePendingInputs(session.pendingInputs)
1062
1068
  };
1063
1069
  // Welcome-panel + write-guard: preserve the four
1064
1070
  // Recent-projects fields through compaction so they round-trip when state
@@ -1139,8 +1145,15 @@
1139
1145
  undoBaseFiles: undoPayload.undoBaseFiles,
1140
1146
  undoTrackedChanges: undoPayload.undoTrackedChanges,
1141
1147
  undoExpectedFiles: undoPayload.undoExpectedFiles,
1142
- undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status')
1148
+ undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status'),
1149
+ nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
1150
+ codexTurnId: normalizeTextField(run.codexTurnId, 160),
1151
+ nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
1152
+ queueItemId: normalizeTextField(run.queueItemId, 160)
1143
1153
  };
1154
+ if (run.interruptedDraft) {
1155
+ compact.interruptedDraft = sanitizeAssistantVisibleValue(run.interruptedDraft);
1156
+ }
1144
1157
  if (VALID_TRACKED_CHANGE_STATUS.has(run.trackedChangeStatus)) {
1145
1158
  compact.trackedChangeStatus = run.trackedChangeStatus;
1146
1159
  }
@@ -1157,6 +1170,7 @@
1157
1170
  status: normalizeEventStatus(event.status),
1158
1171
  timestamp: typeof event.timestamp === 'string' ? event.timestamp : '',
1159
1172
  kind: typeof event.kind === 'string' ? event.kind : 'activity',
1173
+ guidanceId: normalizeTextField(event.guidanceId, 160),
1160
1174
  streamKey: typeof event.streamKey === 'string' ? event.streamKey : '',
1161
1175
  streamRole: typeof event.streamRole === 'string' ? event.streamRole : ''
1162
1176
  };
@@ -1370,6 +1384,15 @@
1370
1384
  }));
1371
1385
  }
1372
1386
 
1387
+ function normalizePendingInputs(value, recoverActive = false) {
1388
+ const Queue = (typeof globalThis !== 'undefined' ? globalThis.CodexOverleafRunInputQueue : null)
1389
+ || (typeof module === 'object' && module.exports ? require('./runInputQueue') : null);
1390
+ if (Queue?.normalizeQueue) {
1391
+ return Queue.normalizeQueue(value, { recoverActive });
1392
+ }
1393
+ return [];
1394
+ }
1395
+
1373
1396
  function sanitizeAssistantVisibleValue(value, depth = 0) {
1374
1397
  if (typeof value === 'string') {
1375
1398
  return sanitizeAssistantVisibleText(value);