codex-overleaf-link 2.2.1 → 2.3.1

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 (79) hide show
  1. package/README.md +25 -23
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/runtime-manifest.json +5 -71
  4. package/extension/src/background.js +67 -63
  5. package/extension/src/backgroundUpdateCoordinator.js +128 -66
  6. package/extension/src/content/applyResultFormatters.js +2 -3
  7. package/extension/src/content/composerPanel.js +0 -2
  8. package/extension/src/content/contentRuntime.js +757 -1558
  9. package/extension/src/content/contextTray.js +4 -6
  10. package/extension/src/content/diagnosticsController.js +1 -2
  11. package/extension/src/content/diffReviewPanel.js +14 -13
  12. package/extension/src/content/generated/content.bundle.js +41605 -0
  13. package/extension/src/content/generated/content.bundle.meta.json +103 -0
  14. package/extension/src/content/legacyGlobalRegistry.js +129 -0
  15. package/extension/src/content/markdownText.js +17 -17
  16. package/extension/src/content/mathText.js +155 -9
  17. package/extension/src/content/modelPicker.js +1 -1
  18. package/extension/src/content/modelPickerSupport.js +10 -5
  19. package/extension/src/content/moduleRegistryKernel.js +125 -0
  20. package/extension/src/content/nativeCompatibilityController.js +313 -0
  21. package/extension/src/content/otWarmMirror.js +8 -6
  22. package/extension/src/content/pageBridgeClient.js +224 -0
  23. package/extension/src/content/panelMaintenance.js +8 -3
  24. package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
  25. package/extension/src/content/projectProviderSelection.js +4 -12
  26. package/extension/src/content/projectSessionCleanup.js +3 -3
  27. package/extension/src/content/providerSettingsCoordinator.js +59 -26
  28. package/extension/src/content/providerSettingsDialog.js +30 -18
  29. package/extension/src/content/recentProjects.js +15 -23
  30. package/extension/src/content/runGuidanceController.js +1 -1
  31. package/extension/src/content/runQueueScheduler.js +129 -29
  32. package/extension/src/content/runSettlementPersistence.js +51 -0
  33. package/extension/src/content/runTimelineView.js +17 -8
  34. package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
  35. package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
  36. package/extension/src/content/scopedPersistencePanelState.js +111 -0
  37. package/extension/src/content/sessionManager.js +15 -12
  38. package/extension/src/content/sessionPersistence.js +67 -8
  39. package/extension/src/content/updateNotice.js +10 -4
  40. package/extension/src/content/writebackOrchestrator.js +89 -68
  41. package/extension/src/contentScript.js +5 -1
  42. package/extension/src/page/trackedChangesLifecycle.js +16 -5
  43. package/extension/src/page/writebackRouter.js +6 -2
  44. package/extension/src/pageBridge.js +47 -98
  45. package/extension/src/shared/agentTranscript.js +18 -8
  46. package/extension/src/shared/compatibility.js +1 -1
  47. package/extension/src/shared/governanceRules.js +1 -1
  48. package/extension/src/shared/i18n.js +2 -4
  49. package/extension/src/shared/managedUpdateProjection.js +337 -0
  50. package/extension/src/shared/nativeRequestIdentity.js +60 -0
  51. package/extension/src/shared/otText.js +0 -19
  52. package/extension/src/shared/pageRpcContract.js +136 -0
  53. package/extension/src/shared/runExecutionSnapshot.js +187 -0
  54. package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
  55. package/extension/src/shared/runInputQueue.js +81 -34
  56. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
  57. package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
  58. package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
  59. package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
  60. package/extension/src/shared/sessionState.js +110 -59
  61. package/extension/src/shared/settlementFacts.js +351 -0
  62. package/extension/src/shared/storageDb.js +25 -10
  63. package/extension/src/shared/storageMigration.js +275 -61
  64. package/extension/src/shared/storageSessionClaims.js +60 -0
  65. package/extension/src/shared/updateConsent.js +4 -0
  66. package/extension/src/shared/updateRevocationIntent.js +109 -0
  67. package/extension/src/shared/writebackEvidenceProjection.js +150 -0
  68. package/extension/src/shared/writebackOperationEvidence.js +153 -0
  69. package/extension/src/shared/writebackSettlement.js +727 -0
  70. package/extension/styles/panel.css +18 -8
  71. package/native-host/src/codexPromptAssembly.js +1 -1
  72. package/native-host/src/codexRuntimeIdentity.js +1 -1
  73. package/native-host/src/codexSessionRunner.js +7 -13
  74. package/native-host/src/index.js +3 -69
  75. package/native-host/src/managedInstall.js +50 -8
  76. package/native-host/src/nativeTransportEnvelope.js +148 -0
  77. package/native-host/src/taskRunnerRuntime.js +14 -84
  78. package/native-host/src/updateManager.js +36 -1
  79. package/package.json +13 -5
@@ -2686,13 +2686,23 @@
2686
2686
  }
2687
2687
 
2688
2688
  #codex-overleaf-panel .run-math--fallback {
2689
- border-radius: 4px;
2690
- background: var(--tl-surface-2);
2691
- color: var(--tl-review);
2692
- padding: 1px 4px;
2689
+ border-bottom: 1px dotted var(--tl-fg-4);
2690
+ border-radius: 0;
2691
+ background: transparent;
2692
+ color: inherit;
2693
+ padding: 0 2px;
2693
2694
  white-space: pre-wrap;
2694
2695
  overflow-wrap: anywhere;
2695
- font: 0.96em/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
2696
+ font: inherit;
2697
+ }
2698
+
2699
+ #codex-overleaf-panel .run-math--display.run-math--fallback {
2700
+ border: 1px solid var(--tl-border);
2701
+ border-left: 3px solid var(--tl-fg-4);
2702
+ border-radius: 6px;
2703
+ background: var(--tl-surface-2);
2704
+ padding: 8px 10px;
2705
+ text-align: left;
2696
2706
  }
2697
2707
 
2698
2708
  #codex-overleaf-panel .run-stream[data-stream-role="reasoning"] .run-stream-text {
@@ -3173,12 +3183,12 @@
3173
3183
  white-space: nowrap;
3174
3184
  }
3175
3185
 
3176
- /* Mode switch (Ask/Suggest/Auto) — a real segmented control: a bordered track
3177
- holding three choices. Inactive choices are muted; hover is a light fill
3186
+ /* Mode switch (Ask/Auto) — a real segmented control: a bordered track
3187
+ holding two choices. Inactive choices are muted; hover is a light fill
3178
3188
  (distinct from the active accent-wash fill) so hover never reads as active. */
3179
3189
  #codex-overleaf-panel .codex-mode-switch {
3180
3190
  display: grid;
3181
- grid-template-columns: repeat(3, minmax(0, 1fr));
3191
+ grid-template-columns: repeat(2, minmax(0, 1fr));
3182
3192
  gap: 2px;
3183
3193
  min-width: 0;
3184
3194
  padding: 2px;
@@ -64,7 +64,7 @@ function buildCodexTurnPrompt(options = {}) {
64
64
  'Mode for this turn:',
65
65
  `- ${context.mode}`,
66
66
  '- ask: inspect and explain only; do not edit files.',
67
- '- confirm/auto: edit the local workspace directly when the request calls for changes. The browser bridge handles review, confirmation, deletion approval, and syncing back to Overleaf.',
67
+ '- auto: edit the local workspace directly when the request calls for changes. The browser bridge handles authorization, deletion approval, and syncing back to Overleaf.',
68
68
  '',
69
69
  'Write expectation for this turn:',
70
70
  formatWriteExpectation({
@@ -7,7 +7,7 @@ const { spawnSync } = require('node:child_process');
7
7
 
8
8
  const RUNTIME_ENV_KEY = 'CODEX_OVERLEAF_CODEX_RUNTIME_JSON';
9
9
  const MAX_CODEX_CANDIDATES = 8;
10
- const VERSION_TIMEOUT_MS = 700;
10
+ const VERSION_TIMEOUT_MS = 3000;
11
11
 
12
12
  function buildCodexRuntimeIdentity(options = {}) {
13
13
  const env = options.env || process.env;
@@ -10,7 +10,10 @@ const { computeTextPatches } = require('./textPatch');
10
10
  const { buildCodexHomeEnv } = require('./codexHome');
11
11
  const { buildCodexSpeedArgs } = require('./codexArgs');
12
12
  const { truncateText } = require('./debugLog');
13
- const { enforceNativeOkResponseBudget } = require('./nativeResponseBudget');
13
+ const {
14
+ estimateBase64DecodedBytes,
15
+ reduceTaskResult
16
+ } = require('./nativeTransportEnvelope');
14
17
  const { buildCodexTurnPrompt: buildCodexPromptParts } = require('./codexPromptAssembly');
15
18
  const { evaluateSkillCommand } = require('./commandApproval');
16
19
  const {
@@ -186,7 +189,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
186
189
  throwIfAborted(signal);
187
190
 
188
191
  if (skillInstallTurn) {
189
- return enforceNativeOkResponseBudget({
192
+ return reduceTaskResult({
190
193
  status: 'completed',
191
194
  projectId: mirror.projectKey,
192
195
  workspacePath: mirror.workspacePath,
@@ -245,7 +248,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
245
248
  ignoredChangedCount: rawSyncChanges.length,
246
249
  ignoredUnsupportedCount: unsupportedChanges.length
247
250
  }, rawSyncChanges.length || unsupportedChanges.length ? 'warning' : 'completed');
248
- return enforceNativeOkResponseBudget({
251
+ return reduceTaskResult({
249
252
  status: 'completed',
250
253
  projectId: mirror.projectKey,
251
254
  workspacePath: mirror.workspacePath,
@@ -266,7 +269,7 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
266
269
  }
267
270
  return change;
268
271
  });
269
- const response = enforceNativeOkResponseBudget({
272
+ const response = reduceTaskResult({
270
273
  status: 'completed',
271
274
  projectId: mirror.projectKey,
272
275
  workspacePath: mirror.workspacePath,
@@ -370,15 +373,6 @@ function normalizeTurnAttachments(value) {
370
373
  return result;
371
374
  }
372
375
 
373
- function estimateBase64DecodedBytes(value) {
374
- const clean = String(value || '').replace(/\s+/g, '');
375
- if (!clean) {
376
- return 0;
377
- }
378
- const padding = clean.endsWith('==') ? 2 : clean.endsWith('=') ? 1 : 0;
379
- return Math.max(0, Math.floor(clean.length * 3 / 4) - padding);
380
- }
381
-
382
376
  function sanitizeAttachmentFileName(value) {
383
377
  const basename = String(value || '')
384
378
  .replace(/\0/g, '')
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const { decodeFrames, encodeMessage } = require('./nativeMessaging');
4
+ const { decodeInputFrames, encodeOutputFrame } = require('./nativeTransportEnvelope');
5
5
  const { logDebug } = require('./debugLog');
6
6
  const { handleRequest } = require('./taskRunner');
7
7
  const { abortAllActiveOperations, getActiveNativeWorkState } = require('./taskRunnerRuntime');
@@ -21,7 +21,7 @@ process.stdin.on('data', chunk => {
21
21
 
22
22
  let decoded;
23
23
  try {
24
- decoded = decodeFrames(buffered);
24
+ decoded = decodeInputFrames(buffered);
25
25
  } catch (error) {
26
26
  writeResponse({
27
27
  ok: false,
@@ -111,24 +111,7 @@ function writeResponse(response) {
111
111
  logDebug('stdout.write_skipped', { ok: response?.ok, code: response?.error?.code });
112
112
  return false;
113
113
  }
114
- let frame;
115
- try {
116
- frame = encodeMessage(response);
117
- } catch (error) {
118
- const fallback = buildOversizeResponseFallback(response, error);
119
- try {
120
- frame = encodeMessage(fallback);
121
- } catch (fallbackError) {
122
- frame = encodeMessage({
123
- id: response?.id,
124
- ok: false,
125
- error: {
126
- code: 'native_response_too_large',
127
- message: truncateForNativeFrame(fallbackError.message || error.message || 'Native response exceeded the browser frame limit.', 800)
128
- }
129
- });
130
- }
131
- }
114
+ const frame = encodeOutputFrame(response);
132
115
  logDebug('stdout.write', { bytes: frame.length, ok: response?.ok, code: response?.error?.code });
133
116
  try {
134
117
  process.stdout.write(frame, error => {
@@ -168,55 +151,6 @@ function handleNativeDisconnect(source, error) {
168
151
  exitTimer.unref?.();
169
152
  }
170
153
 
171
- function buildOversizeResponseFallback(response, error) {
172
- if (response?.event) {
173
- const event = response.event || {};
174
- return {
175
- id: response.id,
176
- ok: true,
177
- event: {
178
- type: event.type || 'native.event.truncated',
179
- title: truncateForNativeFrame(event.title || 'Native event was truncated', 500),
180
- status: event.status || 'warning',
181
- detail: {
182
- code: 'native_event_truncated',
183
- reason: truncateForNativeFrame(error?.message || 'Native event exceeded the browser frame limit.', 800),
184
- originalType: truncateForNativeFrame(event.type || '', 160),
185
- originalTitle: truncateForNativeFrame(event.title || '', 500),
186
- originalDetailBytes: stringByteLength(safeJsonStringify(event.detail))
187
- },
188
- timestamp: event.timestamp || new Date().toISOString()
189
- }
190
- };
191
- }
192
-
193
- return {
194
- id: response?.id,
195
- ok: false,
196
- error: {
197
- code: 'native_response_too_large',
198
- message: truncateForNativeFrame(error?.message || 'Native response exceeded the browser frame limit.', 800),
199
- originalOk: response?.ok === true
200
- }
201
- };
202
- }
203
-
204
- function truncateForNativeFrame(value, limit) {
205
- const text = String(value || '');
206
- if (text.length <= limit) {
207
- return text;
208
- }
209
- return `${text.slice(0, Math.max(0, limit - 24))}... [truncated]`;
210
- }
211
-
212
- function safeJsonStringify(value) {
213
- try {
214
- return JSON.stringify(value);
215
- } catch (_error) {
216
- return '';
217
- }
218
- }
219
-
220
154
  function summarizeRequest(message) {
221
155
  const params = message?.params || {};
222
156
  return {
@@ -54,14 +54,19 @@ function installManagedDistribution(options = {}) {
54
54
  const unique = process.pid + '-' + Date.now() + '-' + Math.random().toString(16).slice(2);
55
55
  const extensionStage = path.join(parent, '.extension.staging-' + unique);
56
56
  const extensionRollback = path.join(parent, '.extension.rollback-' + unique);
57
- buildManagedExtensionTree({
58
- packageRoot,
59
- targetRoot: extensionStage,
60
- version,
61
- releaseRef,
62
- releaseChannel,
63
- allowedFiles: options.allowedFiles
64
- });
57
+ try {
58
+ buildManagedExtensionTree({
59
+ packageRoot,
60
+ targetRoot: extensionStage,
61
+ version,
62
+ releaseRef,
63
+ releaseChannel,
64
+ allowedFiles: options.allowedFiles
65
+ });
66
+ } catch (error) {
67
+ safeRemove(extensionStage);
68
+ throw error;
69
+ }
65
70
 
66
71
  const previousExtensionMarker = readManagedMarker(extensionRoot, EXTENSION_MARKER);
67
72
  if (fs.existsSync(extensionRoot) && !isManagedMarker(previousExtensionMarker, 'extension')) {
@@ -145,6 +150,7 @@ function buildManagedExtensionTree(options = {}) {
145
150
  copyPackageTree(packageRoot, 'extension/vendor', targetRoot, 'runtime/vendor', { allowedFiles });
146
151
  copyPackageFile(packageRoot, 'extension/runtime-manifest.json', targetRoot, 'runtime/runtime-manifest.json', allowedFiles);
147
152
  rewriteManagedRuntimeAssetPaths(targetRoot);
153
+ assertManagedRuntimeManifestFiles(targetRoot);
148
154
  fs.writeFileSync(path.join(targetRoot, EXTENSION_MARKER), JSON.stringify({
149
155
  managedBy: MANAGED_BY,
150
156
  kind: 'extension',
@@ -413,6 +419,41 @@ function resolveForPlatform(value, platformPath) {
413
419
  return platformPath.isAbsolute(value) ? value : platformPath.resolve(value);
414
420
  }
415
421
 
422
+ function assertManagedRuntimeManifestFiles(targetRoot) {
423
+ const runtimeRoot = path.join(targetRoot, 'runtime');
424
+ const runtimeManifest = readJson(path.join(runtimeRoot, 'runtime-manifest.json'));
425
+ const runtimeFiles = [
426
+ ...(Array.isArray(runtimeManifest.js) ? runtimeManifest.js : []),
427
+ ...(Array.isArray(runtimeManifest.css) ? runtimeManifest.css : [])
428
+ ];
429
+ const missing = [];
430
+
431
+ for (const relativePath of runtimeFiles) {
432
+ if (
433
+ typeof relativePath !== 'string'
434
+ || relativePath.length === 0
435
+ || path.isAbsolute(relativePath)
436
+ || relativePath.split(/[\\/]+/).includes('..')
437
+ ) {
438
+ throw new Error('Managed runtime manifest contains an unsafe path: ' + String(relativePath));
439
+ }
440
+ const filePath = path.resolve(runtimeRoot, relativePath);
441
+ const relativeToRuntime = path.relative(runtimeRoot, filePath);
442
+ if (
443
+ relativeToRuntime.startsWith('..')
444
+ || path.isAbsolute(relativeToRuntime)
445
+ || !fs.existsSync(filePath)
446
+ || !fs.statSync(filePath).isFile()
447
+ ) {
448
+ missing.push(relativePath);
449
+ }
450
+ }
451
+
452
+ if (missing.length > 0) {
453
+ throw new Error('Managed runtime is incomplete; missing file(s): ' + missing.join(', '));
454
+ }
455
+ }
456
+
416
457
  function safeRemove(target) {
417
458
  fs.rmSync(target, { recursive: true, force: true });
418
459
  }
@@ -425,6 +466,7 @@ module.exports = {
425
466
  EXTENSION_MARKER,
426
467
  MANAGED_BY,
427
468
  NATIVE_MARKER,
469
+ assertManagedRuntimeManifestFiles,
428
470
  assertSafeManagedRoot,
429
471
  buildManagedExtensionTree,
430
472
  installManagedDistribution,
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ MAX_NATIVE_OUTPUT_MESSAGE_BYTES,
5
+ decodeFrames,
6
+ encodeMessage
7
+ } = require('./nativeMessaging');
8
+ const nativeQuotas = require('./nativeQuotas');
9
+ const {
10
+ enforceNativeOkResponseBudget,
11
+ truncateUtf8Text
12
+ } = require('./nativeResponseBudget');
13
+
14
+ function decodeInputFrames(buffer) {
15
+ return decodeFrames(buffer);
16
+ }
17
+
18
+ function encodeOutputFrame(response) {
19
+ try {
20
+ return encodeMessage(response);
21
+ } catch (error) {
22
+ const fallback = buildOversizeResponseFallback(response, error);
23
+ try {
24
+ return encodeMessage(fallback);
25
+ } catch (fallbackError) {
26
+ return encodeMessage(buildFinalOversizeError(response, fallbackError, error));
27
+ }
28
+ }
29
+ }
30
+
31
+ function getRequestQuotaViolation(request) {
32
+ if (typeof request?.id === "string" && request.id.length > 160) {
33
+ return {
34
+ field: "id",
35
+ limit: 160,
36
+ actual: request.id.length,
37
+ reason: "request id is too large"
38
+ };
39
+ }
40
+ return nativeQuotas.validateNativeRequestQuotas(request);
41
+ }
42
+
43
+ function reduceTaskResult(result) {
44
+ return enforceNativeOkResponseBudget(result);
45
+ }
46
+
47
+ function buildOversizeResponseFallback(response, error) {
48
+ if (response?.event) {
49
+ const event = response.event || {};
50
+ return {
51
+ id: boundedResponseId(response.id),
52
+ ok: true,
53
+ event: {
54
+ type: event.type || 'native.event.truncated',
55
+ title: truncateForNativeFrame(event.title || 'Native event was truncated', 500),
56
+ status: event.status || 'warning',
57
+ detail: {
58
+ code: 'native_event_truncated',
59
+ reason: truncateForNativeFrame(
60
+ error?.message || 'Native event exceeded the browser frame limit.',
61
+ 800
62
+ ),
63
+ originalType: truncateForNativeFrame(event.type || '', 160),
64
+ originalTitle: truncateForNativeFrame(event.title || '', 500),
65
+ originalDetailBytes: measureJsonBytes(event.detail)
66
+ },
67
+ timestamp: event.timestamp || new Date().toISOString()
68
+ }
69
+ };
70
+ }
71
+
72
+ return {
73
+ id: boundedResponseId(response?.id),
74
+ ok: false,
75
+ error: {
76
+ code: 'native_response_too_large',
77
+ message: truncateForNativeFrame(
78
+ error?.message || 'Native response exceeded the browser frame limit.',
79
+ 800
80
+ ),
81
+ originalOk: response?.ok === true
82
+ }
83
+ };
84
+ }
85
+
86
+ function buildFinalOversizeError(response, fallbackError, originalError) {
87
+ return {
88
+ id: boundedResponseId(response?.id),
89
+ ok: false,
90
+ error: {
91
+ code: 'native_response_too_large',
92
+ message: truncateForNativeFrame(
93
+ fallbackError?.message
94
+ || originalError?.message
95
+ || 'Native response exceeded the browser frame limit.',
96
+ 800
97
+ )
98
+ }
99
+ };
100
+ }
101
+
102
+ function boundedResponseId(value) {
103
+ if (typeof value === 'number' && Number.isFinite(value)) {
104
+ return value;
105
+ }
106
+ return truncateForNativeFrame(value, 160);
107
+ }
108
+
109
+ function measureJsonBytes(value) {
110
+ try {
111
+ return Buffer.byteLength(JSON.stringify(value), 'utf8');
112
+ } catch (_error) {
113
+ return 0;
114
+ }
115
+ }
116
+
117
+ function truncateForNativeFrame(value, maxChars) {
118
+ const text = String(value || '');
119
+ if (text.length <= maxChars) {
120
+ return text;
121
+ }
122
+ return `${text.slice(0, Math.max(0, maxChars - 24))}... [truncated]`;
123
+ }
124
+
125
+ module.exports = {
126
+ MAX_NATIVE_OUTPUT_MESSAGE_BYTES,
127
+ NATIVE_REQUEST_QUOTAS: nativeQuotas.NATIVE_REQUEST_QUOTAS,
128
+ buildOversizeResponseFallback,
129
+ decodeInputFrames,
130
+ encodeOutputFrame,
131
+ estimateBase64DecodedBytes: nativeQuotas.estimateBase64DecodedBytes,
132
+ firstQuotaViolation: nativeQuotas.firstQuotaViolation,
133
+ getRequestQuotaViolation,
134
+ measureJsonBytes,
135
+ measureOperationPayloads: nativeQuotas.measureOperationPayloads,
136
+ quotaViolation: nativeQuotas.quotaViolation,
137
+ reduceTaskResult,
138
+ truncateForNativeFrame,
139
+ truncateUtf8Text,
140
+ validateAttachmentQuota: nativeQuotas.validateAttachmentQuota,
141
+ validateCompileLogQuota: nativeQuotas.validateCompileLogQuota,
142
+ validateFilePayloadQuota: nativeQuotas.validateFilePayloadQuota,
143
+ validateOperationListQuota: nativeQuotas.validateOperationListQuota,
144
+ validateOperationPayloadQuota: nativeQuotas.validateOperationPayloadQuota,
145
+ validatePatchFileTextQuota: nativeQuotas.validatePatchFileTextQuota,
146
+ validateProjectSnapshotQuota: nativeQuotas.validateProjectSnapshotQuota,
147
+ validateSkillContentQuota: nativeQuotas.validateSkillContentQuota
148
+ };
@@ -3,7 +3,6 @@
3
3
  const { buildCodexRuntimeEvent } = require('./codexRuntimeIdentity');
4
4
 
5
5
  const { spawn } = require('node:child_process');
6
- const crypto = require('node:crypto');
7
6
  const { buildOperationSummary, splitDeletePlan } = require('../../extension/src/shared/summary');
8
7
  const {
9
8
  MIN_COMPATIBLE_EXTENSION_VERSION,
@@ -26,10 +25,10 @@ const { getNativeRuntimePlatform, summarizeNativeEnvironment } = require('./nati
26
25
  const {
27
26
  NATIVE_REQUEST_QUOTAS,
28
27
  firstQuotaViolation,
29
- validateNativeRequestQuotas,
28
+ getRequestQuotaViolation,
30
29
  validateOperationListQuota,
31
30
  validateOperationPayloadQuota
32
- } = require('./nativeQuotas');
31
+ } = require('./nativeTransportEnvelope');
33
32
  const { version: PACKAGE_VERSION } = require('../../package.json');
34
33
 
35
34
  const activeProjectLocks = new Map();
@@ -40,8 +39,6 @@ const activeRunEntries = new Map();
40
39
  // Overleaf tab was reloaded — the requestId lived in content-side JS state
41
40
  // and is gone, but the native-host-side controller is still running).
42
41
  const activeRunByProject = new Map();
43
- const pendingPlans = new Map();
44
- const PENDING_PLAN_TTL_MS = 30 * 60 * 1000;
45
42
  const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result']);
46
43
 
47
44
  async function handleRequest(request, env = process.env, emit = () => {}) {
@@ -49,7 +46,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
49
46
  return errorResponse(undefined, 'invalid_request', 'Request must be an object');
50
47
  }
51
48
 
52
- const quotaError = validateNativeRequestQuotas(request);
49
+ const quotaError = getRequestQuotaViolation(request);
53
50
  if (quotaError) {
54
51
  return quotaErrorResponse(request.id, quotaError);
55
52
  }
@@ -136,7 +133,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
136
133
  }
137
134
 
138
135
  if (request.method === 'task.confirm') {
139
- return handleTaskConfirm(request);
136
+ return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Reload the extension and choose Ask or Auto.');
140
137
  }
141
138
 
142
139
  return errorResponse(request.id, 'method_not_found', `Unknown method: ${request.method}`);
@@ -716,8 +713,11 @@ async function handleTaskRun(request, env, emit) {
716
713
  const params = request.params || {};
717
714
  const mode = params.mode;
718
715
 
719
- if (!['ask', 'confirm', 'auto'].includes(mode)) {
720
- return errorResponse(request.id, 'invalid_mode', 'Mode must be "ask", "confirm", or "auto"');
716
+ if (mode === 'confirm') {
717
+ return errorResponse(request.id, 'suggest_mode_removed', 'Suggest mode has been removed. Choose Ask or Auto.');
718
+ }
719
+ if (!['ask', 'auto'].includes(mode)) {
720
+ return errorResponse(request.id, 'invalid_mode', 'Mode must be "ask" or "auto"');
721
721
  }
722
722
 
723
723
  if (mode === 'auto' && !params.checkpoint?.ok && !isVerifiedReviewing(params.reviewing)) {
@@ -805,24 +805,6 @@ function isVerifiedReviewing(reviewing) {
805
805
  return reviewing?.ok === true && reviewing.status !== 'manual-override';
806
806
  }
807
807
 
808
- async function handleTaskConfirm(request) {
809
- const planId = request.params?.planId;
810
- purgeExpiredPendingPlans();
811
- if (!planId || !pendingPlans.has(planId)) {
812
- return errorResponse(request.id, 'plan_not_found', 'No pending task plan matched the supplied planId');
813
- }
814
-
815
- const plan = pendingPlans.get(planId);
816
- pendingPlans.delete(planId);
817
-
818
- return okResponse(request.id, {
819
- status: 'confirmed',
820
- notes: plan.notes || '',
821
- userReport: plan.userReport,
822
- operations: plan.operations
823
- });
824
- }
825
-
826
808
  function buildDefaultTaskResult(mode, operations, params = {}) {
827
809
  if (mode === 'ask') {
828
810
  return {
@@ -836,16 +818,6 @@ function buildDefaultTaskResult(mode, operations, params = {}) {
836
818
 
837
819
  const summary = buildOperationSummary(operations);
838
820
 
839
- if (mode === 'confirm') {
840
- return {
841
- status: 'requires_task_confirmation',
842
- summary,
843
- notes: '',
844
- userReport: buildDefaultUserReport(mode, operations, params),
845
- operations
846
- };
847
- }
848
-
849
821
  const split = splitDeletePlan(operations);
850
822
  if (split.needsConfirmation.length > 0) {
851
823
  return {
@@ -889,7 +861,7 @@ function normalizeAgentResult(mode, result, params = {}) {
889
861
  };
890
862
 
891
863
  if (!normalized.status) {
892
- normalized.status = mode === 'confirm' ? 'requires_task_confirmation' : 'completed';
864
+ normalized.status = 'completed';
893
865
  }
894
866
 
895
867
  return normalized;
@@ -911,40 +883,7 @@ function prepareResultForResponse(mode, result) {
911
883
  }
912
884
  }
913
885
 
914
- const confirmOperations = mode === 'confirm' ? collectResultOperations(result) : [];
915
- if (confirmOperations.length > 0) {
916
- result = {
917
- ...result,
918
- status: 'requires_task_confirmation',
919
- summary: buildOperationSummary(confirmOperations),
920
- operations: confirmOperations
921
- };
922
- }
923
-
924
- if (mode !== 'confirm' || result.status !== 'requires_task_confirmation') {
925
- return result;
926
- }
927
-
928
- purgeExpiredPendingPlans();
929
- const planId = `plan_${crypto.randomUUID()}`;
930
- pendingPlans.set(planId, {
931
- createdAt: Date.now(),
932
- expiresAt: Date.now() + PENDING_PLAN_TTL_MS,
933
- notes: result.notes || '',
934
- userReport: result.userReport,
935
- operations: Array.isArray(result.operations) ? result.operations : []
936
- });
937
-
938
- const {
939
- operations: _operations,
940
- pendingOperations: _pendingOperations,
941
- deletePlan: _deletePlan,
942
- ...redacted
943
- } = result;
944
- return {
945
- ...redacted,
946
- planId
947
- };
886
+ return result;
948
887
  }
949
888
 
950
889
  function validateTaskResultOperationQuotas(result = {}) {
@@ -965,7 +904,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
965
904
 
966
905
  return {
967
906
  conclusion: hasOperations
968
- ? reportText(locale, 'Codex prepared proposed changes and is waiting for confirmation or writeback.', 'Codex 已准备好建议修改,等待确认或写入。')
907
+ ? reportText(locale, 'Codex prepared changes for writeback.', 'Codex 已准备好写回修改。')
969
908
  : reportText(locale, 'This task completed without writing Overleaf files.', '这轮任务已完成,没有写入 Overleaf 文件。'),
970
909
  checked,
971
910
  findings: [],
@@ -975,7 +914,7 @@ function buildDefaultUserReport(mode, operations = [], params = {}) {
975
914
  ? ''
976
915
  : (mode === 'ask' ? reportText(locale, 'This run was Ask mode.', '这轮是只问不改。') : ''),
977
916
  nextStep: hasOperations
978
- ? reportText(locale, 'Review and confirm the proposed changes before writing them to Overleaf.', '请确认修改方案后写入 Overleaf。')
917
+ ? reportText(locale, 'Review the written changes in Overleaf.', '请在 Overleaf 中检查写入的修改。')
979
918
  : reportText(locale, 'Continue the conversation, or add more @context and run another check.', '可以继续追问,或加入更多 @context 后再检查。')
980
919
  };
981
920
  }
@@ -1193,14 +1132,6 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
1193
1132
  });
1194
1133
  }
1195
1134
 
1196
- function purgeExpiredPendingPlans(now = Date.now()) {
1197
- for (const [planId, plan] of pendingPlans.entries()) {
1198
- if (Number.isFinite(plan?.expiresAt) && plan.expiresAt <= now) {
1199
- pendingPlans.delete(planId);
1200
- }
1201
- }
1202
- }
1203
-
1204
1135
  function parsePositiveInteger(value, fallback) {
1205
1136
  const parsed = Number.parseInt(value, 10);
1206
1137
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -1295,6 +1226,5 @@ module.exports = {
1295
1226
  buildDefaultTaskResult,
1296
1227
  getActiveNativeWorkState,
1297
1228
  handleRequest,
1298
- parseAgentEventLines,
1299
- purgeExpiredPendingPlans
1229
+ parseAgentEventLines
1300
1230
  };