codex-overleaf-link 2.5.0 → 2.5.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.
- package/README.md +38 -26
- package/README.zh-CN.md +39 -27
- package/extension/bootstrap/background.js +52 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/bootstrap/popup.html +1 -0
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/src/background.js +206 -53
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +74 -14
- package/extension/src/backgroundUpdateCoordinator.js +118 -21
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +8 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +93 -45
- package/extension/src/content/composerPanel.js +18 -0
- package/extension/src/content/contentRuntime.js +661 -676
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +30 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +6879 -2786
- package/extension/src/content/generated/content.bundle.meta.json +11 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +6 -0
- package/extension/src/content/localSkillsPanel.js +102 -12
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +7 -4
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +15 -2
- package/extension/src/content/panelMaintenance.js +40 -12
- package/extension/src/content/panelRenderer.js +6 -0
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +71 -13
- package/extension/src/content/recentProjects.js +11 -5
- package/extension/src/content/runActivitySummary.js +40 -6
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +37 -14
- package/extension/src/content/runFailureNotice.js +21 -12
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runQueueScheduler.js +148 -42
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +8 -125
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +146 -153
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/settingsPanel.js +7 -3
- package/extension/src/content/settingsWorkbench.js +5 -0
- package/extension/src/content/sharedSessionViewSync.js +45 -8
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +60 -8
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +82 -73
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +1 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +6 -4
- package/extension/src/page/snapshotRouter.js +84 -39
- package/extension/src/page/textFileCreator.js +105 -25
- package/extension/src/page/trackedChangeCapture.js +12 -2
- package/extension/src/page/trackedChangeOwnership.js +19 -1
- package/extension/src/page/trackedChangeReplay.js +41 -21
- package/extension/src/page/trackedChangesLifecycle.js +217 -199
- package/extension/src/page/treeOperations.js +7 -10
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +2 -0
- package/extension/src/page/writebackRouter.js +80 -79
- package/extension/src/pageBridge.js +19 -3
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +23 -0
- package/extension/src/shared/auditRecords.js +2 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +79 -57
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +309 -3
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +298 -2
- package/extension/src/shared/managedUpdateProjection.js +2 -2
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +5 -4
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +72 -27
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +37 -14
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +58 -26
- package/extension/src/shared/storageMigration.js +71 -111
- package/extension/src/shared/storageRunActions.js +209 -26
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +136 -13
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +38 -38
- package/extension/styles/panel.css +19 -5
- package/native-host/src/anthropicBridgeRequest.js +4 -2
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexPromptAssembly.js +31 -12
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +110 -183
- package/native-host/src/codexSessionTiming.js +18 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +110 -35
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/processStopBarrier.js +42 -7
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerProfile.js +4 -2
- package/native-host/src/providerReasoning.js +2 -7
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +72 -14
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +36 -8
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +79 -66
- package/native-host/src/subagentTelemetry.js +19 -10
- package/native-host/src/subagentWorkspacePath.js +5 -2
- package/native-host/src/taskRunnerRuntime.js +77 -54
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +297 -39
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const { buildCodexRuntimeEvent } = require('./codexRuntimeIdentity');
|
|
4
4
|
|
|
5
|
+
const { runOwnedNativeHelper } = require('./nativeHelperLifecycle');
|
|
6
|
+
const { stopCodexAppServer, throwIfAborted } = require('./codexSessionTiming');
|
|
5
7
|
const { spawn } = require('node:child_process');
|
|
8
|
+
const { normalizeSafeProjectPath } = require('../../extension/src/shared/projectFiles');
|
|
9
|
+
const { createHash } = require('node:crypto');
|
|
10
|
+
const NativeRequestIdentity = require('../../extension/src/shared/nativeRequestIdentity');
|
|
6
11
|
const { buildOperationSummary, splitDeletePlan } = require('../../extension/src/shared/summary');
|
|
7
12
|
const {
|
|
8
13
|
MIN_COMPATIBLE_EXTENSION_VERSION,
|
|
@@ -36,6 +41,7 @@ const {
|
|
|
36
41
|
const { version: PACKAGE_VERSION } = require('../../package.json');
|
|
37
42
|
|
|
38
43
|
const processStopBarrier = require('./processStopBarrier');
|
|
44
|
+
const runLifecycle = require('./nativeRunLifecycle');
|
|
39
45
|
const activeProjectLocks = new Map();
|
|
40
46
|
const activeRunControllers = new Map();
|
|
41
47
|
const activeRunEntries = new Map();
|
|
@@ -44,7 +50,7 @@ const activeRunEntries = new Map();
|
|
|
44
50
|
// Overleaf tab was reloaded — the requestId lived in content-side JS state
|
|
45
51
|
// and is gone, but the native-host-side controller is still running).
|
|
46
52
|
const activeRunByProject = new Map();
|
|
47
|
-
const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result', 'codex_process_stop_unconfirmed', 'selected_context_unresolved']);
|
|
53
|
+
const CODEX_RUN_PASSTHROUGH_ERROR_CODES = new Set(['thread_resume_failed', 'codex_no_usable_result', 'codex_process_stop_unconfirmed', 'selected_context_unresolved', 'codex_local_skill_unavailable']);
|
|
48
54
|
|
|
49
55
|
async function handleRequest(request, env = process.env, emit = () => {}) {
|
|
50
56
|
if (!request || typeof request !== 'object') {
|
|
@@ -66,7 +72,7 @@ async function handleRequest(request, env = process.env, emit = () => {}) {
|
|
|
66
72
|
platform: getNativeRuntimePlatform({ env }),
|
|
67
73
|
protocolVersion: 2,
|
|
68
74
|
supportedProtocol: { ...SUPPORTED_NATIVE_PROTOCOL },
|
|
69
|
-
capabilities: Object.fromEntries(REQUIRED_CAPABILITIES.map(capability => [capability, true])),
|
|
75
|
+
capabilities: { ...Object.fromEntries(REQUIRED_CAPABILITIES.map(capability => [capability, true])), literalProjectPaths: true },
|
|
70
76
|
minExtensionVersion: MIN_COMPATIBLE_EXTENSION_VERSION,
|
|
71
77
|
version: PACKAGE_VERSION,
|
|
72
78
|
environment: summarizeNativeEnvironment(env)
|
|
@@ -204,6 +210,8 @@ function quotaErrorResponse(id, violation) {
|
|
|
204
210
|
}
|
|
205
211
|
|
|
206
212
|
async function handleCodexRun(request, env, emit) {
|
|
213
|
+
const identity = NativeRequestIdentity.validate(request.id);
|
|
214
|
+
if (!identity.ok) return errorResponse(request.id, identity.error.code, identity.error.message, identity.error.details);
|
|
207
215
|
let params = request.params || {};
|
|
208
216
|
if (params.writingStyleBuild != null) {
|
|
209
217
|
if (typeof params.writingStyleBuild !== 'object' || Array.isArray(params.writingStyleBuild)) {
|
|
@@ -215,7 +223,7 @@ async function handleCodexRun(request, env, emit) {
|
|
|
215
223
|
useExistingMirror: false,
|
|
216
224
|
fileOverlays: [],
|
|
217
225
|
writingStyle: null,
|
|
218
|
-
writingStyleBuild: { ...params.writingStyleBuild, requestId: String(request.id
|
|
226
|
+
writingStyleBuild: { ...params.writingStyleBuild, requestId: String(request.id) }
|
|
219
227
|
};
|
|
220
228
|
}
|
|
221
229
|
if (isCodexMissing(env)) {
|
|
@@ -236,17 +244,16 @@ async function handleCodexRun(request, env, emit) {
|
|
|
236
244
|
return errorResponse(request.id, 'project_locked', `Project ${projectKey} is currently in use by codex.run`);
|
|
237
245
|
}
|
|
238
246
|
const abortController = new AbortController();
|
|
247
|
+
const lifetime = runLifecycle.register({ controller: abortController, projectKey, rootDir: env.CODEX_OVERLEAF_MIRROR_ROOT });
|
|
239
248
|
const activeEntry = {
|
|
240
|
-
id: request.id
|
|
249
|
+
id: request.id,
|
|
241
250
|
projectKey,
|
|
242
251
|
controller: abortController,
|
|
243
252
|
control: null
|
|
244
253
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
activeRunByProject.set(projectKey, activeEntry);
|
|
249
|
-
}
|
|
254
|
+
activeRunControllers.set(request.id, abortController);
|
|
255
|
+
activeRunEntries.set(request.id, activeEntry);
|
|
256
|
+
activeRunByProject.set(projectKey, activeEntry);
|
|
250
257
|
let providerResolution;
|
|
251
258
|
try {
|
|
252
259
|
try {
|
|
@@ -264,6 +271,7 @@ async function handleCodexRun(request, env, emit) {
|
|
|
264
271
|
return providerErrorResponse(request.id, error);
|
|
265
272
|
}
|
|
266
273
|
|
|
274
|
+
abortController.signal.throwIfAborted();
|
|
267
275
|
if (params.useExistingMirror) {
|
|
268
276
|
const { getMirrorStatus, applyFileOverlays } = require('./mirrorWorkspace');
|
|
269
277
|
const rootDir = env.CODEX_OVERLEAF_MIRROR_ROOT;
|
|
@@ -288,7 +296,9 @@ async function handleCodexRun(request, env, emit) {
|
|
|
288
296
|
);
|
|
289
297
|
}
|
|
290
298
|
|
|
291
|
-
|
|
299
|
+
// OT overlays prove that the current page agrees with the trusted OT
|
|
300
|
+
// baseline; they must not rewrite it or supply a replacement hash.
|
|
301
|
+
if (!isOtWarmMirrorReuseRequest(params) && Array.isArray(params.fileOverlays) && params.fileOverlays.length) {
|
|
292
302
|
await applyFileOverlays({ projectId: projectKey, overlays: params.fileOverlays, rootDir });
|
|
293
303
|
}
|
|
294
304
|
} else if (!params.writingStyleBuild && !isSnapshotlessSkillInstallerRun(params) && !hasRunnableProjectSnapshotEvidence(params)) {
|
|
@@ -306,6 +316,7 @@ async function handleCodexRun(request, env, emit) {
|
|
|
306
316
|
rootDir: env.CODEX_OVERLEAF_MIRROR_ROOT,
|
|
307
317
|
providerLaunch: providerResolution.providerLaunch,
|
|
308
318
|
onStopUnconfirmed: error => processStopBarrier.record(projectKey, env.CODEX_OVERLEAF_MIRROR_ROOT, error),
|
|
319
|
+
onProcessStarted: lifetime.trackProcess,
|
|
309
320
|
onControlReady: control => {
|
|
310
321
|
activeEntry.control = control;
|
|
311
322
|
},
|
|
@@ -346,16 +357,17 @@ async function handleCodexRun(request, env, emit) {
|
|
|
346
357
|
});
|
|
347
358
|
return errorResponse(request.id, 'codex_run_failed', truncateText(error.message, 12000));
|
|
348
359
|
} finally {
|
|
349
|
-
if (
|
|
360
|
+
if (activeRunControllers.get(request.id) === abortController) {
|
|
350
361
|
activeRunControllers.delete(request.id);
|
|
351
362
|
}
|
|
352
|
-
if (
|
|
363
|
+
if (activeRunEntries.get(request.id) === activeEntry) {
|
|
353
364
|
activeRunEntries.delete(request.id);
|
|
354
365
|
}
|
|
355
366
|
if (activeRunByProject.get(projectKey)?.controller === abortController) {
|
|
356
367
|
activeRunByProject.delete(projectKey);
|
|
357
368
|
}
|
|
358
369
|
releaseProjectLock(projectKey, lockToken);
|
|
370
|
+
lifetime.complete();
|
|
359
371
|
}
|
|
360
372
|
}
|
|
361
373
|
|
|
@@ -400,8 +412,8 @@ async function handleCodexSteer(request) {
|
|
|
400
412
|
}
|
|
401
413
|
|
|
402
414
|
function findActiveRunEntry(params = {}) {
|
|
403
|
-
const targetId =
|
|
404
|
-
if (
|
|
415
|
+
const targetId = params.requestId ?? params.id;
|
|
416
|
+
if (activeRunEntries.has(targetId)) {
|
|
405
417
|
return activeRunEntries.get(targetId);
|
|
406
418
|
}
|
|
407
419
|
const projectKey = String(params.projectKey || '');
|
|
@@ -420,11 +432,11 @@ function findActiveRunEntry(params = {}) {
|
|
|
420
432
|
// can't punch through a real live run.
|
|
421
433
|
function handleCodexCancel(request, env = process.env) {
|
|
422
434
|
const params = request.params || {};
|
|
423
|
-
const targetId = params.requestId
|
|
435
|
+
const targetId = params.requestId ?? params.id;
|
|
424
436
|
const projectKey = typeof params.projectKey === 'string' ? params.projectKey : '';
|
|
425
437
|
const force = params.force === true;
|
|
426
438
|
|
|
427
|
-
if (
|
|
439
|
+
if (activeRunControllers.has(targetId)) {
|
|
428
440
|
activeRunControllers.get(targetId).abort(createCancellationError());
|
|
429
441
|
return okResponse(request.id, {
|
|
430
442
|
cancelled: true,
|
|
@@ -439,7 +451,7 @@ function handleCodexCancel(request, env = process.env) {
|
|
|
439
451
|
return okResponse(request.id, {
|
|
440
452
|
cancelled: true,
|
|
441
453
|
projectKey,
|
|
442
|
-
requestId: entry.id
|
|
454
|
+
requestId: entry.id
|
|
443
455
|
});
|
|
444
456
|
}
|
|
445
457
|
if (processStopBarrier.isBlocked(projectKey, env.CODEX_OVERLEAF_MIRROR_ROOT)) {
|
|
@@ -473,8 +485,10 @@ function handleCodexHistoryClear(request, env) {
|
|
|
473
485
|
}
|
|
474
486
|
}
|
|
475
487
|
|
|
476
|
-
function handleSkillsList(request, env) {
|
|
488
|
+
async function handleSkillsList(request, env) {
|
|
477
489
|
try {
|
|
490
|
+
if (request.params?.scope === 'codex-local') return okResponse(request.id,
|
|
491
|
+
await require('./codexLocalSkills').listCodexLocalSkills({ env }));
|
|
478
492
|
const { CODEX_OVERLEAF_SKILL_SCOPE, listCodexOverleafSkills, listProjectSkills } = require('./localSkills');
|
|
479
493
|
if (request.params?.scope === CODEX_OVERLEAF_SKILL_SCOPE) {
|
|
480
494
|
// Install/restore official skills before listing so newly shipped ones
|
|
@@ -493,9 +507,9 @@ function handleSkillsList(request, env) {
|
|
|
493
507
|
return errorResponse(request.id, 'skills_list_failed', error.message);
|
|
494
508
|
}
|
|
495
509
|
}
|
|
496
|
-
|
|
497
510
|
function handleSkillsInstall(request, env) {
|
|
498
511
|
try {
|
|
512
|
+
if (request.params?.scope === 'codex-local') throw new Error('Local Codex skills are managed by Codex.');
|
|
499
513
|
const { CODEX_OVERLEAF_SKILL_SCOPE, installCodexOverleafSkill, installProjectSkill } = require('./localSkills');
|
|
500
514
|
if (request.params?.scope === CODEX_OVERLEAF_SKILL_SCOPE) {
|
|
501
515
|
return okResponse(request.id, installCodexOverleafSkill({
|
|
@@ -514,9 +528,9 @@ function handleSkillsInstall(request, env) {
|
|
|
514
528
|
return errorResponse(request.id, 'skills_install_failed', error.message);
|
|
515
529
|
}
|
|
516
530
|
}
|
|
517
|
-
|
|
518
531
|
function handleSkillsRemove(request, env) {
|
|
519
532
|
try {
|
|
533
|
+
if (request.params?.scope === 'codex-local') throw new Error('Local Codex skills are managed by Codex.');
|
|
520
534
|
const { CODEX_OVERLEAF_SKILL_SCOPE, removeCodexOverleafSkill, removeProjectSkill } = require('./localSkills');
|
|
521
535
|
if (request.params?.scope === CODEX_OVERLEAF_SKILL_SCOPE) {
|
|
522
536
|
return okResponse(request.id, removeCodexOverleafSkill({
|
|
@@ -544,13 +558,13 @@ function hasRunnableProjectSnapshotEvidence(params = {}) {
|
|
|
544
558
|
if (params.restrictToFocusFiles !== true) {
|
|
545
559
|
return false;
|
|
546
560
|
}
|
|
547
|
-
const normalizedFocusFiles = normalizeSnapshotEvidencePaths(params.focusFiles);
|
|
561
|
+
const normalizedFocusFiles = normalizeSnapshotEvidencePaths(params.focusFiles, params.focusFilesAreLiteral === true);
|
|
548
562
|
if (!normalizedFocusFiles.length || !Array.isArray(params.project?.files)) {
|
|
549
563
|
return false;
|
|
550
564
|
}
|
|
551
565
|
const evidenceFiles = new Map();
|
|
552
566
|
for (const file of params.project.files) {
|
|
553
|
-
const filePath =
|
|
567
|
+
const filePath = normalizeSafeProjectPath(file?.path);
|
|
554
568
|
if (!filePath || !isUsableSnapshotEvidenceContent(file?.content)) {
|
|
555
569
|
continue;
|
|
556
570
|
}
|
|
@@ -561,19 +575,14 @@ function hasRunnableProjectSnapshotEvidence(params = {}) {
|
|
|
561
575
|
|
|
562
576
|
function isSnapshotlessSkillInstallerRun(params = {}) {
|
|
563
577
|
return params.skipMirrorSync === true
|
|
564
|
-
&& String(params.skillInvocation?.id || '').trim() === 'skill-installer'
|
|
578
|
+
&& String(params.skillInvocation?.id || '').trim() === 'skill-installer'
|
|
579
|
+
&& (!params.skillInvocation?.scope || params.skillInvocation.scope === 'builtin');
|
|
565
580
|
}
|
|
566
581
|
|
|
567
582
|
function validateOtFocusedWarmMirrorReuse(params = {}, status = {}) {
|
|
568
583
|
if (!isOtWarmMirrorReuseRequest(params)) {
|
|
569
584
|
return { ok: false };
|
|
570
585
|
}
|
|
571
|
-
if (Array.isArray(params.fileOverlays) && params.fileOverlays.length) {
|
|
572
|
-
return {
|
|
573
|
-
ok: false,
|
|
574
|
-
message: 'OT warm mirror reuse does not accept file overlays'
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
586
|
if (status?.exists !== true) {
|
|
578
587
|
return {
|
|
579
588
|
ok: false,
|
|
@@ -586,7 +595,7 @@ function validateOtFocusedWarmMirrorReuse(params = {}, status = {}) {
|
|
|
586
595
|
message: 'OT warm mirror reuse requires restrictToFocusFiles=true'
|
|
587
596
|
};
|
|
588
597
|
}
|
|
589
|
-
const normalizedFocusFiles = normalizeSnapshotEvidencePaths(params.focusFiles);
|
|
598
|
+
const normalizedFocusFiles = normalizeSnapshotEvidencePaths(params.focusFiles, params.focusFilesAreLiteral === true);
|
|
590
599
|
if (!normalizedFocusFiles.length) {
|
|
591
600
|
return {
|
|
592
601
|
ok: false,
|
|
@@ -594,14 +603,14 @@ function validateOtFocusedWarmMirrorReuse(params = {}, status = {}) {
|
|
|
594
603
|
};
|
|
595
604
|
}
|
|
596
605
|
|
|
597
|
-
const freshFiles = new
|
|
606
|
+
const freshFiles = new Map();
|
|
598
607
|
for (const file of Array.isArray(status.otFreshFiles) ? status.otFreshFiles : []) {
|
|
599
608
|
if (file?.state !== 'fresh') {
|
|
600
609
|
continue;
|
|
601
610
|
}
|
|
602
|
-
const filePath =
|
|
611
|
+
const filePath = normalizeSafeProjectPath(file.path);
|
|
603
612
|
if (filePath) {
|
|
604
|
-
freshFiles.
|
|
613
|
+
freshFiles.set(filePath, file);
|
|
605
614
|
}
|
|
606
615
|
}
|
|
607
616
|
const missingFiles = normalizedFocusFiles.filter(filePath => !freshFiles.has(filePath));
|
|
@@ -612,6 +621,22 @@ function validateOtFocusedWarmMirrorReuse(params = {}, status = {}) {
|
|
|
612
621
|
};
|
|
613
622
|
}
|
|
614
623
|
|
|
624
|
+
if (Array.isArray(params.fileOverlays) && params.fileOverlays.length) {
|
|
625
|
+
const overlays = new Set();
|
|
626
|
+
for (const overlay of params.fileOverlays) {
|
|
627
|
+
const filePath = normalizeSafeProjectPath(overlay?.path);
|
|
628
|
+
if (!normalizedFocusFiles.includes(filePath) || overlays.has(filePath)
|
|
629
|
+
|| typeof overlay?.content !== 'string' || !/^[a-f0-9]{64}$/i.test(freshFiles.get(filePath)?.sha256 || '')
|
|
630
|
+
|| createHash('sha256').update(overlay.content).digest('hex') !== freshFiles.get(filePath).sha256.toLowerCase()) {
|
|
631
|
+
return { ok: false, message: 'OT warm mirror overlay does not match the current focused OT baseline' };
|
|
632
|
+
}
|
|
633
|
+
overlays.add(filePath);
|
|
634
|
+
}
|
|
635
|
+
if (normalizedFocusFiles.some(filePath => !overlays.has(filePath))) {
|
|
636
|
+
return { ok: false, message: 'OT warm mirror overlays do not cover all focused files' };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
615
640
|
return { ok: true };
|
|
616
641
|
}
|
|
617
642
|
|
|
@@ -627,11 +652,11 @@ function isUsableSnapshotEvidenceContent(content) {
|
|
|
627
652
|
return Boolean(text) && !/^(loading|loading\.{3}|loading…)$/i.test(text);
|
|
628
653
|
}
|
|
629
654
|
|
|
630
|
-
function normalizeSnapshotEvidencePaths(value) {
|
|
655
|
+
function normalizeSnapshotEvidencePaths(value, literal = false) {
|
|
631
656
|
const seen = new Set();
|
|
632
657
|
const paths = [];
|
|
633
658
|
for (const item of Array.isArray(value) ? value : []) {
|
|
634
|
-
const filePath = normalizeSnapshotEvidencePath(item);
|
|
659
|
+
const filePath = literal ? normalizeSafeProjectPath(item) : normalizeSnapshotEvidencePath(item);
|
|
635
660
|
if (!filePath || seen.has(filePath)) {
|
|
636
661
|
continue;
|
|
637
662
|
}
|
|
@@ -656,6 +681,7 @@ function createCancellationError() {
|
|
|
656
681
|
}
|
|
657
682
|
|
|
658
683
|
function isCancellationError(error = {}) {
|
|
684
|
+
if (['codex_turn_failed', 'codex_turn_status_invalid'].includes(error.code)) return false;
|
|
659
685
|
return error.code === 'codex_cancelled'
|
|
660
686
|
|| error.name === 'AbortError'
|
|
661
687
|
|| /cancelled by the user|was cancelled/i.test(error.message || '');
|
|
@@ -753,6 +779,7 @@ async function handleMirrorConfirmWriteback(request, env) {
|
|
|
753
779
|
const result = await confirmWritebackFiles({
|
|
754
780
|
projectId,
|
|
755
781
|
paths: params.paths,
|
|
782
|
+
expectedFiles: params.expectedFiles,
|
|
756
783
|
rootDir
|
|
757
784
|
});
|
|
758
785
|
return okResponse(request.id, result);
|
|
@@ -1127,10 +1154,12 @@ function parseAgentArgsJson(value) {
|
|
|
1127
1154
|
}
|
|
1128
1155
|
|
|
1129
1156
|
function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
1130
|
-
return new Promise((resolve, reject) => {
|
|
1157
|
+
return runOwnedNativeHelper(signal => new Promise((resolve, reject) => {
|
|
1158
|
+
throwIfAborted(signal);
|
|
1131
1159
|
const child = spawn(agentSpec.file, agentSpec.args || [], {
|
|
1132
1160
|
env: options.env || process.env,
|
|
1133
1161
|
shell: false,
|
|
1162
|
+
detached: process.platform !== 'win32',
|
|
1134
1163
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
1135
1164
|
});
|
|
1136
1165
|
const timeoutMs = parseOptionalPositiveInteger(options.timeoutMs);
|
|
@@ -1140,6 +1169,8 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1140
1169
|
let stderrRemainder = '';
|
|
1141
1170
|
let outputBytes = 0;
|
|
1142
1171
|
let settled = false;
|
|
1172
|
+
const onAbort = () => fail(signal.reason);
|
|
1173
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
1143
1174
|
|
|
1144
1175
|
const timeout = timeoutMs
|
|
1145
1176
|
? setTimeout(() => {
|
|
@@ -1164,10 +1195,8 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1164
1195
|
if (timeout) {
|
|
1165
1196
|
clearTimeout(timeout);
|
|
1166
1197
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
}
|
|
1170
|
-
reject(error);
|
|
1198
|
+
signal.removeEventListener('abort', onAbort);
|
|
1199
|
+
stopCodexAppServer(child, { processGroup: process.platform !== 'win32', reason: error }).then(() => reject(error), reject);
|
|
1171
1200
|
}
|
|
1172
1201
|
|
|
1173
1202
|
function succeed(result) {
|
|
@@ -1178,7 +1207,8 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1178
1207
|
if (timeout) {
|
|
1179
1208
|
clearTimeout(timeout);
|
|
1180
1209
|
}
|
|
1181
|
-
|
|
1210
|
+
signal.removeEventListener('abort', onAbort);
|
|
1211
|
+
stopCodexAppServer(child, { processGroup: process.platform !== 'win32' }).then(() => resolve(result), reject);
|
|
1182
1212
|
}
|
|
1183
1213
|
|
|
1184
1214
|
child.stdout.setEncoding('utf8');
|
|
@@ -1200,6 +1230,7 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1200
1230
|
emitTaskEvent(emit, event.type || 'agent.progress', event.title || event.type || 'Agent progress', event.detail || {}, event.status || 'running');
|
|
1201
1231
|
}
|
|
1202
1232
|
});
|
|
1233
|
+
child.stdin.on('error', fail);
|
|
1203
1234
|
child.on('error', fail);
|
|
1204
1235
|
child.on('close', code => {
|
|
1205
1236
|
if (settled) {
|
|
@@ -1226,7 +1257,7 @@ function runExternalAgent(agentSpec, params, emit = () => {}, options = {}) {
|
|
|
1226
1257
|
});
|
|
1227
1258
|
|
|
1228
1259
|
child.stdin.end(JSON.stringify(params));
|
|
1229
|
-
});
|
|
1260
|
+
}));
|
|
1230
1261
|
}
|
|
1231
1262
|
|
|
1232
1263
|
function parsePositiveInteger(value, fallback) {
|
|
@@ -1299,27 +1330,19 @@ function errorResponse(id, code, message, details = {}) {
|
|
|
1299
1330
|
};
|
|
1300
1331
|
}
|
|
1301
1332
|
|
|
1302
|
-
function getActiveNativeWorkState() {
|
|
1333
|
+
function getActiveNativeWorkState(env = process.env) {
|
|
1303
1334
|
return {
|
|
1304
1335
|
projectLocks: activeProjectLocks.size,
|
|
1336
|
+
processStopBarriers: processStopBarrier.getBlockedProjectCount(env.CODEX_OVERLEAF_MIRROR_ROOT, { env }),
|
|
1305
1337
|
runControllers: activeRunControllers.size,
|
|
1306
1338
|
activeTurnControls: Array.from(activeRunEntries.values()).filter(entry => entry.control).length
|
|
1307
1339
|
};
|
|
1308
1340
|
}
|
|
1309
1341
|
|
|
1310
|
-
function abortAllActiveOperations(reason = 'Native messaging transport disconnected.') {
|
|
1311
|
-
const error = new Error(String(reason || 'Native messaging transport disconnected.'));
|
|
1312
|
-
error.code = 'native_transport_closed';
|
|
1313
|
-
for (const controller of activeRunControllers.values()) {
|
|
1314
|
-
if (controller && !controller.signal?.aborted) {
|
|
1315
|
-
controller.abort(error);
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
1342
|
module.exports = {
|
|
1321
1343
|
NATIVE_REQUEST_QUOTAS,
|
|
1322
|
-
abortAllActiveOperations,
|
|
1344
|
+
abortAllActiveOperations: runLifecycle.abortAllActiveOperations,
|
|
1345
|
+
quarantineActiveOperations: runLifecycle.quarantineActiveOperations,
|
|
1323
1346
|
buildDefaultTaskResult,
|
|
1324
1347
|
getActiveNativeWorkState,
|
|
1325
1348
|
handleRequest,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Preserve TOML source text while finding complete logical statements. This is
|
|
4
|
+
// a lexical boundary scanner, not a TOML value/schema parser. Filters must never
|
|
5
|
+
// treat an apparent key or table header inside a multiline value as syntax.
|
|
6
|
+
function splitTomlStatements(content) {
|
|
7
|
+
const text = String(content || '').replace(/\r\n/g, '\n');
|
|
8
|
+
const statements = [];
|
|
9
|
+
const containers = [];
|
|
10
|
+
let start = 0;
|
|
11
|
+
let quote = '';
|
|
12
|
+
let multiline = false;
|
|
13
|
+
let comment = false;
|
|
14
|
+
|
|
15
|
+
for (let index = 0; index < text.length; index++) {
|
|
16
|
+
const char = text[index];
|
|
17
|
+
if (comment) {
|
|
18
|
+
if (char !== '\n') continue;
|
|
19
|
+
comment = false;
|
|
20
|
+
}
|
|
21
|
+
if (quote) {
|
|
22
|
+
if (quote === '"' && char === '\\') {
|
|
23
|
+
// Basic strings escape the next character, including the first quote
|
|
24
|
+
// of a potential delimiter. Literal strings have no escape syntax.
|
|
25
|
+
index++;
|
|
26
|
+
} else if (char === quote) {
|
|
27
|
+
if (!multiline) {
|
|
28
|
+
quote = '';
|
|
29
|
+
} else if (text.slice(index, index + 3) === quote.repeat(3)) {
|
|
30
|
+
// A legal closing delimiter can have one or two literal quotes just
|
|
31
|
+
// before it: consume all four/five instead of opening another string.
|
|
32
|
+
let count = 3;
|
|
33
|
+
while (count < 5 && text[index + count] === quote) count++;
|
|
34
|
+
index += count - 1;
|
|
35
|
+
quote = '';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (char === '#') {
|
|
41
|
+
comment = true;
|
|
42
|
+
} else if (char === '"' || char === "'") {
|
|
43
|
+
quote = char;
|
|
44
|
+
multiline = text.slice(index, index + 3) === char.repeat(3);
|
|
45
|
+
if (multiline) index += 2;
|
|
46
|
+
} else if (char === '[' || char === '{') {
|
|
47
|
+
containers.push(char);
|
|
48
|
+
} else if (char === ']' || char === '}') {
|
|
49
|
+
const opener = char === ']' ? '[' : '{';
|
|
50
|
+
if (containers.pop() !== opener) throw incompleteValue();
|
|
51
|
+
} else if (char === '\n' && containers.length === 0) {
|
|
52
|
+
statements.push(text.slice(start, index + 1));
|
|
53
|
+
start = index + 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Do not silently remove the remaining config if a selected value is broken.
|
|
58
|
+
// The caller prepares the full string before writing the destination file.
|
|
59
|
+
if (quote || containers.length) throw incompleteValue();
|
|
60
|
+
if (start < text.length) statements.push(text.slice(start));
|
|
61
|
+
return statements;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function incompleteValue() {
|
|
65
|
+
return new Error('Codex configuration contains an unterminated or mismatched TOML value.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Parse only a statement's key path, leaving its value and original formatting
|
|
69
|
+
// untouched. A quoted dot belongs to a key; an unquoted dot separates keys.
|
|
70
|
+
function readTomlStatementPath(statement) {
|
|
71
|
+
const text = String(statement || '');
|
|
72
|
+
let index = 0;
|
|
73
|
+
const whitespace = () => { while (text[index] === ' ' || text[index] === '\t') index++; };
|
|
74
|
+
whitespace();
|
|
75
|
+
if (!text[index] || text[index] === '#' || text[index] === '\n') return null;
|
|
76
|
+
const table = text[index] === '[';
|
|
77
|
+
let brackets = 0;
|
|
78
|
+
if (table) {
|
|
79
|
+
brackets = text[index + 1] === '[' ? 2 : 1;
|
|
80
|
+
index += brackets;
|
|
81
|
+
}
|
|
82
|
+
const keys = [];
|
|
83
|
+
while (true) {
|
|
84
|
+
whitespace();
|
|
85
|
+
let key = '';
|
|
86
|
+
const quote = text[index];
|
|
87
|
+
if (quote === '"' || quote === "'") {
|
|
88
|
+
index++;
|
|
89
|
+
while (index < text.length && text[index] !== quote) {
|
|
90
|
+
const char = text[index++];
|
|
91
|
+
if (char === '\n' || char === '\r') throw invalidKey();
|
|
92
|
+
if (quote !== '"' || char !== '\\') { key += char; continue; }
|
|
93
|
+
const escape = text[index++];
|
|
94
|
+
const simple = { b: '\b', t: '\t', n: '\n', f: '\f', r: '\r', '"': '"', '\\': '\\' };
|
|
95
|
+
if (Object.prototype.hasOwnProperty.call(simple, escape)) { key += simple[escape]; continue; }
|
|
96
|
+
const count = escape === 'u' ? 4 : escape === 'U' ? 8 : 0;
|
|
97
|
+
const hex = text.slice(index, index + count);
|
|
98
|
+
if (!count || hex.length !== count || !/^[0-9a-f]+$/i.test(hex)) throw invalidKey();
|
|
99
|
+
const point = Number.parseInt(hex, 16);
|
|
100
|
+
if (point > 0x10ffff || (point >= 0xd800 && point <= 0xdfff)) throw invalidKey();
|
|
101
|
+
key += String.fromCodePoint(point);
|
|
102
|
+
index += count;
|
|
103
|
+
}
|
|
104
|
+
if (text[index++] !== quote) throw invalidKey();
|
|
105
|
+
} else {
|
|
106
|
+
const match = text.slice(index).match(/^[A-Za-z0-9_-]+/);
|
|
107
|
+
if (!match) return null;
|
|
108
|
+
key = match[0]; index += key.length;
|
|
109
|
+
}
|
|
110
|
+
keys.push(key);
|
|
111
|
+
whitespace();
|
|
112
|
+
if (text[index] !== '.') break;
|
|
113
|
+
index++;
|
|
114
|
+
}
|
|
115
|
+
if (table) {
|
|
116
|
+
if (text.slice(index, index + brackets) !== ']'.repeat(brackets)) return null;
|
|
117
|
+
index += brackets; whitespace();
|
|
118
|
+
if (text[index] && text[index] !== '#' && text[index] !== '\n') return null;
|
|
119
|
+
} else if (text[index] !== '=') return null;
|
|
120
|
+
return { table, keys, ...(table ? {} : { valueStart: index + 1 }) };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function filterTomlRootKeys(content, excludedKeys) {
|
|
124
|
+
const excluded = new Set(excludedKeys);
|
|
125
|
+
const output = [];
|
|
126
|
+
let section = [];
|
|
127
|
+
for (const statement of splitTomlStatements(content)) {
|
|
128
|
+
const parsed = readTomlStatementPath(statement);
|
|
129
|
+
if (parsed?.table) section = parsed.keys;
|
|
130
|
+
const root = section.length ? section[0] : parsed?.keys[0];
|
|
131
|
+
if (!excluded.has(root)) output.push(statement);
|
|
132
|
+
}
|
|
133
|
+
return output.join('');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function invalidKey() {
|
|
137
|
+
return new Error('Codex configuration contains an invalid TOML key.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { splitTomlStatements, filterTomlRootKeys, readTomlStatementPath };
|