sneakoscope 8.0.2 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +47 -23
  2. package/config/skills-hash-ledger.v1.json +1 -1
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/fast-inline.js +55 -13
  6. package/dist/bin/sks-dispatch.js +1 -1
  7. package/dist/cli/command-manifest-lite.js +4 -0
  8. package/dist/cli/command-registry.js +4 -0
  9. package/dist/cli/install-helpers-codex-lb-chain.js +187 -0
  10. package/dist/cli/install-helpers-codex-lb-config.js +74 -8
  11. package/dist/cli/install-helpers.js +89 -44
  12. package/dist/commands/codex-lb.js +117 -19
  13. package/dist/commands/doctor.js +697 -37
  14. package/dist/config/skills-manifest.json +3 -2
  15. package/dist/core/agents/agent-runner-codex-exec.js +5 -3
  16. package/dist/core/codex/codex-config-guard.js +238 -52
  17. package/dist/core/codex-adapter.js +5 -3
  18. package/dist/core/codex-app/menubar/constants.js +10 -1
  19. package/dist/core/codex-app/menubar/global-install.js +288 -0
  20. package/dist/core/codex-app/menubar/installer.js +79 -14
  21. package/dist/core/codex-app/menubar/launch-agent.js +267 -45
  22. package/dist/core/codex-app/menubar/paths.js +3 -0
  23. package/dist/core/codex-app/menubar/rollback.js +55 -7
  24. package/dist/core/codex-app/menubar/status.js +75 -4
  25. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
  26. package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
  27. package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
  28. package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
  29. package/dist/core/codex-exec-output-schema.js +4 -1
  30. package/dist/core/codex-lb/codex-lb-env.js +63 -72
  31. package/dist/core/codex-lb/codex-lb-setup.js +15 -2
  32. package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
  33. package/dist/core/codex-lb/desktop-controller.js +153 -24
  34. package/dist/core/codex-lb/routing-truth.js +327 -0
  35. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  36. package/dist/core/commands/basic-cli.js +7 -1
  37. package/dist/core/commands/qa-loop-command.js +3 -1
  38. package/dist/core/commands/telegram-command.js +241 -0
  39. package/dist/core/config-adopt/config-adopt-command.js +35 -0
  40. package/dist/core/config-adopt/config-adopt.js +346 -0
  41. package/dist/core/config-adopt/index.js +3 -0
  42. package/dist/core/doctor/command-alias-cleanup.js +4 -1
  43. package/dist/core/doctor/current-project-guidance-nested.js +37 -6
  44. package/dist/core/doctor/current-project-guidance.js +11 -6
  45. package/dist/core/doctor/doctor-readiness-matrix.js +14 -0
  46. package/dist/core/doctor/retired-managed-residue-private.js +1 -1
  47. package/dist/core/doctor/sks-menubar-doctor.js +4 -0
  48. package/dist/core/feature-fixtures.js +8 -0
  49. package/dist/core/hooks-runtime/hook-context.js +9 -0
  50. package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
  51. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -1
  52. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
  53. package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
  54. package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
  55. package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
  56. package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
  57. package/dist/core/hooks-runtime.js +148 -4
  58. package/dist/core/imagegen/imagegen-capability.js +46 -0
  59. package/dist/core/imagegen/require-imagegen.js +2 -2
  60. package/dist/core/init/skills.js +17 -3
  61. package/dist/core/install/installed-package-smoke.js +8 -2
  62. package/dist/core/naruto/hardware-capacity-probe.js +9 -3
  63. package/dist/core/naruto/naruto-concurrency-governor.js +68 -24
  64. package/dist/core/perf/import-graph-budget.js +0 -1
  65. package/dist/core/product-design-app-server.js +13 -1
  66. package/dist/core/release/package-size-budget.js +2 -2
  67. package/dist/core/release/release-pack-receipt.js +1 -2
  68. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  69. package/dist/core/routes.js +1 -1
  70. package/dist/core/security/private-credential-file.js +209 -0
  71. package/dist/core/subagents/agent-catalog.js +17 -32
  72. package/dist/core/subagents/naruto-help-contract.js +5 -5
  73. package/dist/core/subagents/official-subagent-config.js +16 -6
  74. package/dist/core/subagents/official-subagent-preparation.js +34 -15
  75. package/dist/core/subagents/official-subagent-prompt.js +4 -4
  76. package/dist/core/subagents/official-subagent-runner.js +27 -4
  77. package/dist/core/subagents/thread-budget.js +19 -12
  78. package/dist/core/subagents/wave-lifecycle.js +18 -6
  79. package/dist/core/telegram/access.js +54 -0
  80. package/dist/core/telegram/client.js +144 -0
  81. package/dist/core/telegram/confirmation.js +37 -0
  82. package/dist/core/telegram/doctor.js +151 -0
  83. package/dist/core/telegram/index.js +9 -0
  84. package/dist/core/telegram/keychain.js +265 -0
  85. package/dist/core/telegram/liveness.js +109 -0
  86. package/dist/core/telegram/poller.js +90 -0
  87. package/dist/core/telegram/redaction.js +15 -0
  88. package/dist/core/telegram/types.js +1 -0
  89. package/dist/core/update/update-diagnostics.js +52 -0
  90. package/dist/core/update/update-migration-state.js +5 -1
  91. package/dist/core/update/update-operation.js +2 -1
  92. package/dist/core/update/update-stage-diagnostics.js +38 -0
  93. package/dist/core/update/update-status.js +2 -9
  94. package/dist/core/update-check.js +147 -31
  95. package/dist/core/version.js +1 -1
  96. package/dist/core/zellij/zellij-launcher.js +23 -4
  97. package/dist/native/sks-menubar/Sources/AppDelegate.swift +13 -0
  98. package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
  99. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +3 -2
  100. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
  101. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
  102. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
  103. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +100 -59
  104. package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
  105. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +320 -0
  106. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +303 -0
  107. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +238 -0
  108. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
  109. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  110. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +474 -0
  111. package/dist/native/sks-menubar/Sources/main.swift +15 -1
  112. package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +4 -3
  113. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +129 -0
  114. package/dist/native/sks-menubar/Tests/SingletonInstanceGuardTests.swift +183 -0
  115. package/dist/native/sks-menubar/Tests/TelegramRuntimeSmokeTests.swift +267 -0
  116. package/dist/native/sks-menubar/Tests/TelegramRuntimeTests.swift +222 -0
  117. package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -1
  118. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -1
  119. package/dist/scripts/current-command-surface-check.js +2 -4
  120. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  121. package/dist/scripts/packlist-performance-check.js +1 -2
  122. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "8.0.2",
3
+ "package_version": "8.0.3",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -326,8 +326,9 @@
326
326
  {
327
327
  "canonical_name": "sks-naruto",
328
328
  "type": "core",
329
- "content_sha256": "f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37",
329
+ "content_sha256": "34222c72e4e1ead3584907f4d905444b7de820283ee7348018f110d5e6b04461",
330
330
  "hash_history": [
331
+ "f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37",
331
332
  "49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"
332
333
  ],
333
334
  "deprecated_aliases": []
@@ -5,6 +5,7 @@ import { agentWorkerEnv, validateAgentWorkerResult } from './agent-worker-pipeli
5
5
  import { fastModeEnv, resolveFastModePolicy } from './fast-mode-policy.js';
6
6
  import { buildCodexExecArgs } from '../codex/codex-cli-syntax-builder.js';
7
7
  import { codexLbRecoveryBlockedProcessResult, withCodexLbCliLaunchRecovery } from '../codex-control/codex-lb-launch-recovery.js';
8
+ import { prepareCodexAppServerRuntimeEnv } from '../codex-control/codex-app-server-runtime-env.js';
8
9
  export function buildCodexExecAgentArgs(agent, prompt, opts = {}) {
9
10
  const resultFile = opts.resultFile || defaultCodexResultFile(agent, opts);
10
11
  const sandbox = opts.workspaceWrite ? 'workspace-write' : 'read-only';
@@ -74,15 +75,16 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
74
75
  const allowedCommandsFile = path.join(opts.agentRoot || opts.cwd || process.cwd(), 'agent-allowed-commands.json');
75
76
  const workerEnv = agentWorkerEnv(agent, allowedCommandsFile);
76
77
  const effectiveEnv = { ...process.env, ...(opts.env || {}) };
77
- const proxyEnv = managedProxyEnvForChild(effectiveEnv);
78
+ const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: effectiveEnv });
79
+ const proxyEnv = managedProxyEnvForChild(runtimeEnv);
78
80
  const execute = opts.runProcessImpl || runProcess;
79
81
  const guarded = await withCodexLbCliLaunchRecovery({
80
82
  root: opts.cwd || process.cwd(),
81
- env: effectiveEnv,
83
+ env: runtimeEnv,
82
84
  cliArgs: command.args.slice(0, -1),
83
85
  ...(typeof opts.recoveryFetch === 'function' ? { fetchImpl: opts.recoveryFetch } : {}),
84
86
  ...(opts.recoveryTimeoutMs === undefined ? {} : { timeoutMs: opts.recoveryTimeoutMs })
85
- }, () => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...(opts.env || {}), ...proxyEnv, ...fastModeEnv(fastPolicy), ...workerEnv }, timeoutMs: opts.timeoutMs || 30 * 60 * 1000, maxOutputBytes: 256 * 1024, stdoutFile, stderrFile }));
87
+ }, () => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...runtimeEnv, ...proxyEnv, ...fastModeEnv(fastPolicy), ...workerEnv }, timeoutMs: opts.timeoutMs || 30 * 60 * 1000, maxOutputBytes: 256 * 1024, stdoutFile, stderrFile }));
86
88
  const result = guarded.launched
87
89
  ? guarded.value
88
90
  : codexLbRecoveryBlockedProcessResult(guarded.toolOutputRecovery);
@@ -5,6 +5,7 @@ import { appendJsonl, ensureDir, nowIso, readText, sha256, writeTextAtomic } fro
5
5
  import { diffCodexAppUiSnapshots, snapshotCodexAppUiState } from '../codex-app/codex-app-ui-state-snapshot.js';
6
6
  import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from './codex-config-toml.js';
7
7
  import { escapeRegExp } from '../text/regex.js';
8
+ export const SKS_MANAGED_CODEX_CONFIG_MARKER = '# SKS-MANAGED-CODEX-CONFIG';
8
9
  const FAST_FEATURE_KEYS = [
9
10
  'fast_mode',
10
11
  'hooks',
@@ -23,18 +24,6 @@ export async function writeCodexConfigGuarded(input) {
23
24
  const root = path.resolve(input.root || process.cwd());
24
25
  const cause = input.cause || 'codex-config';
25
26
  const before = input.before === undefined ? String(await readText(configPath, '')) : String(input.before || '');
26
- if (isUnmanagedProjectCodexConfig(root, configPath, before) && input.ownershipVerified !== true) {
27
- const result = { ok: false, status: 'blocked_unmanaged_project_config', config_path: configPath, backup_path: null, changed: false };
28
- await recordCodexConfigGuard(root, input.reportPath, {
29
- cause,
30
- config_path: configPath,
31
- ok: false,
32
- status: result.status,
33
- blocker: 'user_owned_file_without_sks_marker',
34
- changed: false
35
- });
36
- return result;
37
- }
38
27
  const beforeSmoke = codexConfigParseSmoke(before);
39
28
  const beforeValidation = validateCodexConfigRoundTrip(before);
40
29
  if (before.trim() && (!beforeSmoke.ok || beforeValidation.parse_error)) {
@@ -62,6 +51,21 @@ export async function writeCodexConfigGuarded(input) {
62
51
  next = preserved.text;
63
52
  if (input.removeTopLevelModeLocks === true)
64
53
  next = removeLegacyTopLevelCodexModeLocks(next);
54
+ const expectedBefore = normalizeText(before);
55
+ const unmanagedProjectConfig = isUnmanagedProjectCodexConfig(root, configPath, before)
56
+ && input.ownershipVerified !== true;
57
+ if (unmanagedProjectConfig && next !== expectedBefore) {
58
+ const result = { ok: false, status: 'blocked_unmanaged_project_config', config_path: configPath, backup_path: null, changed: false };
59
+ await recordCodexConfigGuard(root, input.reportPath, {
60
+ cause,
61
+ config_path: configPath,
62
+ ok: false,
63
+ status: result.status,
64
+ blocker: 'user_owned_file_without_sks_marker',
65
+ changed: false
66
+ });
67
+ return result;
68
+ }
65
69
  const forbiddenTopLevel = topLevelModeLocks(next);
66
70
  const nextSmoke = codexConfigParseSmoke(next);
67
71
  const nextValidation = validateCodexConfigRoundTrip(next);
@@ -80,17 +84,19 @@ export async function writeCodexConfigGuarded(input) {
80
84
  });
81
85
  return result;
82
86
  }
83
- const expectedBefore = normalizeText(before);
84
87
  if (next === expectedBefore) {
85
- if (input.verifyUnchangedBeforeWrite === true) {
88
+ const verifyWithoutWrite = input.verifyUnchangedBeforeWrite === true || unmanagedProjectConfig;
89
+ if (verifyWithoutWrite) {
86
90
  const observed = await readConfigCommitSnapshot(configPath);
87
- const expectedExists = input.expectedBeforeExists ?? true;
91
+ const expectedExists = unmanagedProjectConfig ? true : (input.expectedBeforeExists ?? true);
88
92
  if (!configCommitSnapshotMatches(observed, expectedExists, before, input.expectedBeforeMode)) {
89
- return recordConcurrentConfigChange({
93
+ return recordConfigCommitFailure({
90
94
  root,
91
95
  reportPath: input.reportPath,
92
96
  cause,
93
97
  configPath,
98
+ status: 'concurrent_change_detected',
99
+ changed: false,
94
100
  before,
95
101
  expectedExists,
96
102
  ...(input.expectedBeforeMode === undefined
@@ -106,11 +112,11 @@ export async function writeCodexConfigGuarded(input) {
106
112
  else {
107
113
  await writeTextAtomic(configPath, next, { mode: 0o600 });
108
114
  }
109
- const expectedAfterExists = input.verifyUnchangedBeforeWrite === true
110
- ? (input.expectedBeforeExists ?? true)
115
+ const expectedAfterExists = verifyWithoutWrite
116
+ ? (unmanagedProjectConfig ? true : (input.expectedBeforeExists ?? true))
111
117
  : true;
112
118
  const expectedAfterMode = expectedAfterExists
113
- ? input.verifyUnchangedBeforeWrite === true
119
+ ? verifyWithoutWrite
114
120
  ? (input.expectedBeforeMode === undefined ? null : Number(input.expectedBeforeMode) & 0o777)
115
121
  : 0o600
116
122
  : null;
@@ -125,7 +131,7 @@ export async function writeCodexConfigGuarded(input) {
125
131
  expected_after: {
126
132
  exists: expectedAfterExists,
127
133
  text: expectedAfterExists
128
- ? (input.verifyUnchangedBeforeWrite === true ? before : next)
134
+ ? (verifyWithoutWrite ? before : next)
129
135
  : '',
130
136
  mode: expectedAfterMode
131
137
  }
@@ -157,11 +163,13 @@ export async function writeCodexConfigGuarded(input) {
157
163
  backupTag: input.backupTag || cause
158
164
  });
159
165
  if (!commit.ok) {
160
- return recordConcurrentConfigChange({
166
+ return recordConfigCommitFailure({
161
167
  root,
162
168
  reportPath: input.reportPath,
163
169
  cause,
164
170
  configPath,
171
+ status: commit.failure,
172
+ changed: commit.changed,
165
173
  before,
166
174
  expectedExists,
167
175
  ...(input.expectedBeforeMode === undefined
@@ -226,13 +234,13 @@ function configCommitSnapshotMatches(observed, expectedExists, expectedText, exp
226
234
  || expectedMode === undefined
227
235
  || observed.mode === (Number(expectedMode) & 0o777));
228
236
  }
229
- async function recordConcurrentConfigChange(input) {
237
+ async function recordConfigCommitFailure(input) {
230
238
  const result = {
231
239
  ok: false,
232
- status: 'concurrent_change_detected',
240
+ status: input.status,
233
241
  config_path: input.configPath,
234
242
  backup_path: input.backupPath,
235
- changed: false,
243
+ changed: input.changed,
236
244
  repaired_keys: input.repairedKeys,
237
245
  forbidden_top_level: input.forbiddenTopLevel
238
246
  };
@@ -251,7 +259,7 @@ async function recordConcurrentConfigChange(input) {
251
259
  observed_sha256: input.observed.ok ? sha256(input.observed.text) : null,
252
260
  observed_status: input.observed.status,
253
261
  backup_path: input.backupPath,
254
- changed: false
262
+ changed: input.changed
255
263
  });
256
264
  return result;
257
265
  }
@@ -264,48 +272,168 @@ async function commitCodexConfigIfUnchanged(input) {
264
272
  await writeTextAtomic(candidatePath, input.next, { mode: 0o600 });
265
273
  const observed = await readConfigCommitSnapshot(input.configPath);
266
274
  if (!configCommitSnapshotMatches(observed, input.expectedExists, input.before, input.expectedBeforeMode)) {
267
- return { ok: false, backupPath, observed };
275
+ return {
276
+ ok: false,
277
+ failure: 'concurrent_change_detected',
278
+ changed: false,
279
+ backupPath,
280
+ observed
281
+ };
268
282
  }
283
+ const claimedBeforeMode = input.expectedBeforeMode === undefined
284
+ ? (observed.ok && observed.exists ? observed.mode : undefined)
285
+ : Number(input.expectedBeforeMode) & 0o777;
269
286
  if (input.expectedExists) {
270
287
  backupPath = `${input.configPath}.sks-${safeBackupTag(input.backupTag)}-${token}.bak`;
271
288
  try {
272
289
  await fsp.rename(input.configPath, backupPath);
273
290
  }
274
291
  catch {
275
- return { ok: false, backupPath: null, observed: await readConfigCommitSnapshot(input.configPath) };
292
+ return {
293
+ ok: false,
294
+ failure: 'concurrent_change_detected',
295
+ changed: false,
296
+ backupPath: null,
297
+ observed: await readConfigCommitSnapshot(input.configPath)
298
+ };
276
299
  }
277
300
  const claimed = await readConfigCommitSnapshot(backupPath);
278
- if (!configCommitSnapshotMatches(claimed, true, input.before, input.expectedBeforeMode)) {
279
- await restoreClaimedConfigIfAbsent(backupPath, input.configPath, claimed);
280
- return { ok: false, backupPath, observed: claimed };
281
- }
282
- try {
283
- await fsp.chmod(backupPath, 0o600);
284
- const now = new Date();
285
- await fsp.utimes(backupPath, now, now);
286
- }
287
- catch {
288
- await restoreClaimedConfigIfAbsent(backupPath, input.configPath, claimed);
289
- return { ok: false, backupPath, observed: await readConfigCommitSnapshot(input.configPath) };
301
+ if (!configCommitSnapshotMatches(claimed, true, input.before, claimedBeforeMode)) {
302
+ const rollback = await rollbackCodexConfigCommit({
303
+ candidatePath,
304
+ configPath: input.configPath,
305
+ backupPath,
306
+ before: input.before,
307
+ expectedExists: input.expectedExists,
308
+ expectedBeforeMode: claimedBeforeMode
309
+ });
310
+ return {
311
+ ok: false,
312
+ failure: rollback.restored ? 'concurrent_change_detected' : 'postwrite_verification_failed',
313
+ changed: !rollback.restored,
314
+ backupPath: rollback.backupPath,
315
+ observed: rollback.observed
316
+ };
290
317
  }
291
318
  const afterClaim = await readConfigCommitSnapshot(input.configPath);
292
319
  if (!afterClaim.ok || afterClaim.exists) {
293
- if (!afterClaim.exists)
294
- await restoreClaimedConfigIfAbsent(backupPath, input.configPath, claimed);
295
- return { ok: false, backupPath, observed: await readConfigCommitSnapshot(input.configPath) };
320
+ const rollback = await rollbackCodexConfigCommit({
321
+ candidatePath,
322
+ configPath: input.configPath,
323
+ backupPath,
324
+ before: input.before,
325
+ expectedExists: input.expectedExists,
326
+ expectedBeforeMode: claimedBeforeMode
327
+ });
328
+ return {
329
+ ok: false,
330
+ failure: 'concurrent_change_detected',
331
+ changed: !rollback.restored,
332
+ backupPath: rollback.backupPath,
333
+ observed: rollback.observed
334
+ };
296
335
  }
297
336
  }
298
337
  try {
299
338
  await fsp.link(candidatePath, input.configPath);
300
339
  }
301
340
  catch {
302
- if (backupPath) {
303
- const claimed = await readConfigCommitSnapshot(backupPath);
304
- await restoreClaimedConfigIfAbsent(backupPath, input.configPath, claimed);
341
+ const rollback = await rollbackCodexConfigCommit({
342
+ candidatePath,
343
+ configPath: input.configPath,
344
+ backupPath,
345
+ before: input.before,
346
+ expectedExists: input.expectedExists,
347
+ expectedBeforeMode: claimedBeforeMode
348
+ });
349
+ return {
350
+ ok: false,
351
+ failure: 'concurrent_change_detected',
352
+ changed: !rollback.restored,
353
+ backupPath: rollback.backupPath,
354
+ observed: rollback.observed
355
+ };
356
+ }
357
+ try {
358
+ await fsp.chmod(input.configPath, 0o600);
359
+ }
360
+ catch {
361
+ const rollback = await rollbackCodexConfigCommit({
362
+ candidatePath,
363
+ configPath: input.configPath,
364
+ backupPath,
365
+ before: input.before,
366
+ expectedExists: input.expectedExists,
367
+ expectedBeforeMode: claimedBeforeMode
368
+ });
369
+ return {
370
+ ok: false,
371
+ failure: 'postwrite_verification_failed',
372
+ changed: !rollback.restored,
373
+ backupPath: rollback.backupPath,
374
+ observed: rollback.observed
375
+ };
376
+ }
377
+ const committed = await readConfigCommitSnapshot(input.configPath);
378
+ if (!configCommitSnapshotMatches(committed, true, input.next, 0o600)) {
379
+ const rollback = await rollbackCodexConfigCommit({
380
+ candidatePath,
381
+ configPath: input.configPath,
382
+ backupPath,
383
+ before: input.before,
384
+ expectedExists: input.expectedExists,
385
+ expectedBeforeMode: claimedBeforeMode
386
+ });
387
+ return {
388
+ ok: false,
389
+ failure: 'postwrite_verification_failed',
390
+ changed: !rollback.restored,
391
+ backupPath: rollback.backupPath,
392
+ observed: rollback.observed
393
+ };
394
+ }
395
+ if (backupPath) {
396
+ try {
397
+ await fsp.chmod(backupPath, 0o600);
398
+ const now = new Date();
399
+ await fsp.utimes(backupPath, now, now);
400
+ }
401
+ catch {
402
+ const rollback = await rollbackCodexConfigCommit({
403
+ candidatePath,
404
+ configPath: input.configPath,
405
+ backupPath,
406
+ before: input.before,
407
+ expectedExists: input.expectedExists,
408
+ expectedBeforeMode: claimedBeforeMode
409
+ });
410
+ return {
411
+ ok: false,
412
+ failure: 'postwrite_verification_failed',
413
+ changed: !rollback.restored,
414
+ backupPath: rollback.backupPath,
415
+ observed: rollback.observed
416
+ };
417
+ }
418
+ const hardenedBackup = await readConfigCommitSnapshot(backupPath);
419
+ if (!configCommitSnapshotMatches(hardenedBackup, true, input.before, 0o600)) {
420
+ const rollback = await rollbackCodexConfigCommit({
421
+ candidatePath,
422
+ configPath: input.configPath,
423
+ backupPath,
424
+ before: input.before,
425
+ expectedExists: input.expectedExists,
426
+ expectedBeforeMode: claimedBeforeMode
427
+ });
428
+ return {
429
+ ok: false,
430
+ failure: 'postwrite_verification_failed',
431
+ changed: !rollback.restored,
432
+ backupPath: rollback.backupPath,
433
+ observed: rollback.observed
434
+ };
305
435
  }
306
- return { ok: false, backupPath, observed: await readConfigCommitSnapshot(input.configPath) };
307
436
  }
308
- await fsp.chmod(input.configPath, 0o600).catch(() => undefined);
309
437
  await cleanupCodexConfigBackups(input.configPath, { keepPerTag: 3, maxAgeMs: 30 * 24 * 60 * 60 * 1000 }).catch(() => undefined);
310
438
  return { ok: true, backupPath };
311
439
  }
@@ -313,10 +441,65 @@ async function commitCodexConfigIfUnchanged(input) {
313
441
  await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
314
442
  }
315
443
  }
316
- async function restoreClaimedConfigIfAbsent(backupPath, configPath, claimed) {
317
- if (!claimed.ok || !claimed.exists || claimed.status !== 'regular')
318
- return;
319
- await fsp.link(backupPath, configPath).catch(() => undefined);
444
+ async function rollbackCodexConfigCommit(input) {
445
+ let config = await readConfigCommitSnapshot(input.configPath);
446
+ if (config.exists) {
447
+ try {
448
+ const [candidateStat, configStat] = await Promise.all([
449
+ fsp.lstat(input.candidatePath),
450
+ fsp.lstat(input.configPath)
451
+ ]);
452
+ if (candidateStat.dev !== configStat.dev || candidateStat.ino !== configStat.ino) {
453
+ return { restored: false, backupPath: input.backupPath, observed: config };
454
+ }
455
+ }
456
+ catch {
457
+ return { restored: false, backupPath: input.backupPath, observed: config };
458
+ }
459
+ try {
460
+ await fsp.unlink(input.configPath);
461
+ }
462
+ catch {
463
+ return {
464
+ restored: false,
465
+ backupPath: input.backupPath,
466
+ observed: await readConfigCommitSnapshot(input.configPath)
467
+ };
468
+ }
469
+ config = await readConfigCommitSnapshot(input.configPath);
470
+ }
471
+ let remainingBackupPath = input.backupPath;
472
+ const restoreMode = input.expectedBeforeMode === undefined
473
+ ? undefined
474
+ : Number(input.expectedBeforeMode) & 0o777;
475
+ if (input.expectedExists) {
476
+ if (!input.backupPath || !config.ok || config.exists) {
477
+ return { restored: false, backupPath: remainingBackupPath, observed: config };
478
+ }
479
+ const backup = await readConfigCommitSnapshot(input.backupPath);
480
+ if (!backup.ok || !backup.exists || sha256(backup.text) !== sha256(input.before)) {
481
+ return { restored: false, backupPath: remainingBackupPath, observed: backup };
482
+ }
483
+ try {
484
+ await fsp.rename(input.backupPath, input.configPath);
485
+ remainingBackupPath = null;
486
+ if (restoreMode !== undefined)
487
+ await fsp.chmod(input.configPath, restoreMode);
488
+ }
489
+ catch {
490
+ return {
491
+ restored: false,
492
+ backupPath: remainingBackupPath,
493
+ observed: await readConfigCommitSnapshot(input.configPath)
494
+ };
495
+ }
496
+ }
497
+ const observed = await readConfigCommitSnapshot(input.configPath);
498
+ return {
499
+ restored: configCommitSnapshotMatches(observed, input.expectedExists, input.before, restoreMode),
500
+ backupPath: remainingBackupPath,
501
+ observed
502
+ };
320
503
  }
321
504
  function safeBackupTag(value) {
322
505
  return String(value || 'codex-config').replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 96) || 'codex-config';
@@ -384,12 +567,15 @@ export function isProjectCodexConfig(root, configPath) {
384
567
  }
385
568
  export function hasSksManagedCodexConfigMarker(text) {
386
569
  const source = String(text || '');
387
- return /^\s*#\s*SKS-MANAGED-CODEX-CONFIG\b/im.test(source)
570
+ return hasExplicitSksManagedCodexConfigMarker(source)
388
571
  || /(?:SKS managed|Sneakoscope|sneakoscope|sks_|agents\.native_agent|agents\.implementation_worker|multi_agent)/i.test(source)
389
572
  || /^\s*model_provider\s*=\s*["']codex-lb["']\s*(?:#.*)?$/mi.test(source)
390
573
  || /^\s*default_profile\s*=\s*["']sks-fast-high["']\s*(?:#.*)?$/mi.test(source)
391
574
  || /^\s*\[(?:user\.fast_mode|model_providers\.(?:"codex-lb"|codex-lb)|profiles\.(?:"sks-fast-high"|sks-fast-high))\]\s*(?:#.*)?$/mi.test(source);
392
575
  }
576
+ export function hasExplicitSksManagedCodexConfigMarker(text) {
577
+ return /^\s*#\s*SKS-MANAGED-CODEX-CONFIG\b/im.test(String(text || ''));
578
+ }
393
579
  export function isUnmanagedProjectCodexConfig(root, configPath, text) {
394
580
  return isProjectCodexConfig(root, configPath)
395
581
  && String(text || '').trim().length > 0
@@ -4,6 +4,7 @@ import { preserveCodexModelArgs } from './codex-model-guard.js';
4
4
  import { managedProxyEnvForChild } from './codex/managed-proxy-env.js';
5
5
  import { resolveCodexRuntime } from './codex-runtime/resolve-codex-runtime.js';
6
6
  import { codexLbRecoveryBlockedProcessResult, withCodexLbCliLaunchRecovery } from './codex-control/codex-lb-launch-recovery.js';
7
+ import { prepareCodexAppServerRuntimeEnv } from './codex-control/codex-app-server-runtime-env.js';
7
8
  export async function findCodexBinary() {
8
9
  const resolved = await resolveCodexRuntime({
9
10
  explicitPath: process.env.DCODEX_CODEX_BIN || null,
@@ -40,13 +41,14 @@ export function buildCodexExecArgs({ root, prompt, outputFile, json = true, prof
40
41
  args.push(prompt);
41
42
  return args;
42
43
  }
43
- export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFile, json = true, profile = null, extraArgs = [], onStdout, onStderr, logDir = null, stdoutFile = null, stderrFile = null, maxBufferBytes = 256 * 1024, timeoutMs = null, env = process.env, codexBin = null, findCodexBinaryImpl = findCodexBinary, runProcessImpl = runProcess, recoveryFetch = undefined, recoveryTimeoutMs = undefined }) {
44
+ export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFile, json = true, profile = null, extraArgs = [], onStdout, onStderr, logDir = null, stdoutFile = null, stderrFile = null, maxBufferBytes = 256 * 1024, timeoutMs = null, env = process.env, codexBin = null, findCodexBinaryImpl = findCodexBinary, runProcessImpl = runProcess, prepareCodexRuntimeEnvImpl = prepareCodexAppServerRuntimeEnv, recoveryFetch = undefined, recoveryTimeoutMs = undefined }) {
44
45
  const args = buildCodexExecArgs({ root, prompt, outputFile, json, profile, extraArgs });
45
46
  const effectiveTimeoutMs = Number(timeoutMs || process.env.SKS_CODEX_TIMEOUT_MS || process.env.DCODEX_CODEX_TIMEOUT_MS || 30 * 60 * 1000);
46
47
  const recoveryArgs = args.slice(1, -1);
48
+ const runtimeEnv = await prepareCodexRuntimeEnvImpl({ env });
47
49
  const guarded = await withCodexLbCliLaunchRecovery({
48
50
  root: recoveryRoot,
49
- env,
51
+ env: runtimeEnv,
50
52
  cliArgs: recoveryArgs,
51
53
  ...(typeof recoveryFetch === 'function' ? { fetchImpl: recoveryFetch } : {}),
52
54
  ...(recoveryTimeoutMs === undefined ? {} : { timeoutMs: recoveryTimeoutMs })
@@ -65,7 +67,7 @@ export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFi
65
67
  }
66
68
  return runProcessImpl(bin, args, {
67
69
  cwd: root,
68
- env: { ...env, ...managedProxyEnvForChild(env) },
70
+ env: { ...runtimeEnv, ...managedProxyEnvForChild(runtimeEnv) },
69
71
  onStdout,
70
72
  onStderr,
71
73
  timeoutMs: effectiveTimeoutMs,
@@ -21,7 +21,9 @@ export const NATIVE_SOURCE_FILES = [
21
21
  'UpdatesViewController.swift',
22
22
  'MCPServersViewController.swift',
23
23
  'ProvidersViewController.swift',
24
+ 'ProvidersConnectTest.swift',
24
25
  'ProvidersRoutingTruth.swift',
26
+ 'ProvidersFastMode.swift',
25
27
  'ProvidersOpenRouter.swift',
26
28
  'ProvidersRoleModels.swift',
27
29
  'ProvidersMultiProvider.swift',
@@ -33,9 +35,16 @@ export const NATIVE_SOURCE_FILES = [
33
35
  'ProcessExecutionState.swift',
34
36
  'ProcessIdentityGuard.swift',
35
37
  'SecureProcessEnvelope.swift',
38
+ 'TelegramPrivateFileSupport.swift',
39
+ 'TelegramPrivateFileStore.swift',
40
+ 'TelegramSupport.swift',
41
+ 'TelegramRuntimeSupport.swift',
42
+ 'TelegramTransport.swift',
43
+ 'TelegramProcessGateway.swift',
36
44
  'NotificationCoordinator.swift',
37
45
  'AlertFactory.swift',
38
- 'AppIdentity.swift'
46
+ 'AppIdentity.swift',
47
+ 'SingletonInstanceGuard.swift'
39
48
  ];
40
49
  export const NATIVE_RESOURCE_FILES = [
41
50
  'AppIcon.icns',