@gajae-code/coding-agent 0.11.6 → 0.11.7

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 (131) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/commands/daemon.d.ts +2 -2
  5. package/dist/types/commands/sdk.d.ts +20 -2
  6. package/dist/types/config/keybindings.d.ts +1 -1
  7. package/dist/types/config/settings-schema.d.ts +56 -6
  8. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  9. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +5 -5
  10. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +20 -0
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +19 -5
  12. package/dist/types/main.d.ts +24 -1
  13. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  14. package/dist/types/modes/components/model-selector.d.ts +2 -0
  15. package/dist/types/modes/components/tool-status-header.d.ts +1 -0
  16. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  17. package/dist/types/modes/interactive-mode.d.ts +2 -0
  18. package/dist/types/modes/types.d.ts +1 -0
  19. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  20. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
  21. package/dist/types/sdk/bus/index.d.ts +1 -1
  22. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  23. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  24. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -3
  25. package/dist/types/sdk/bus/telegram-daemon.d.ts +61 -3
  26. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  27. package/dist/types/sdk/client/discovery.d.ts +2 -0
  28. package/dist/types/sdk/client/liveness.d.ts +7 -0
  29. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  30. package/dist/types/sdk/transport/index.d.ts +12 -0
  31. package/dist/types/sdk/transport/relay.d.ts +43 -0
  32. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  33. package/dist/types/sdk/transport/socket.d.ts +7 -0
  34. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  35. package/dist/types/session/agent-session.d.ts +28 -5
  36. package/dist/types/session/btw-contract.d.ts +16 -0
  37. package/dist/types/session/internal/managed-session-scope.d.ts +21 -7
  38. package/dist/types/session/internal/managed-session-storage.d.ts +10 -8
  39. package/dist/types/session/session-manager.d.ts +25 -4
  40. package/dist/types/setup/credential-import.d.ts +5 -1
  41. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  42. package/dist/types/ssh/utils.d.ts +1 -0
  43. package/dist/types/task/discovery.d.ts +3 -1
  44. package/dist/types/tools/fetch.d.ts +5 -8
  45. package/dist/types/tools/image-gen.d.ts +16 -0
  46. package/dist/types/tools/skill-discovery.d.ts +7 -0
  47. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  48. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  49. package/dist/types/web/insane/bridge.d.ts +1 -0
  50. package/dist/types/web/insane/url-guard.d.ts +17 -0
  51. package/package.json +8 -8
  52. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  53. package/src/capability/index.ts +50 -42
  54. package/src/capability/types.ts +3 -0
  55. package/src/cli/setup-cli.ts +10 -4
  56. package/src/cli.ts +34 -9
  57. package/src/commands/daemon.ts +22 -5
  58. package/src/commands/sdk.ts +42 -9
  59. package/src/config/keybindings.ts +1 -1
  60. package/src/config/model-profiles.ts +18 -0
  61. package/src/config/settings-schema.ts +57 -6
  62. package/src/defaults/gjc/skills/ultragoal/SKILL.md +42 -1
  63. package/src/discovery/ssh.ts +12 -1
  64. package/src/eval/py/kernel.ts +1 -21
  65. package/src/eval/py/runner-artifact.ts +84 -0
  66. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  67. package/src/gjc-runtime/ultragoal-guard.ts +88 -10
  68. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +103 -0
  69. package/src/gjc-runtime/ultragoal-runtime.ts +275 -12
  70. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  71. package/src/gjc-runtime/workflow-manifest.ts +23 -0
  72. package/src/internal-urls/docs-index.generated.ts +8 -7
  73. package/src/internal-urls/local-protocol.ts +32 -6
  74. package/src/main.ts +86 -27
  75. package/src/modes/action-registry.ts +1 -1
  76. package/src/modes/components/btw-panel.ts +82 -16
  77. package/src/modes/components/model-selector.ts +31 -3
  78. package/src/modes/components/tool-status-header.ts +6 -1
  79. package/src/modes/controllers/btw-controller.ts +119 -36
  80. package/src/modes/controllers/input-controller.ts +31 -0
  81. package/src/modes/controllers/selector-controller.ts +106 -12
  82. package/src/modes/interactive-mode.ts +16 -7
  83. package/src/modes/types.ts +1 -0
  84. package/src/prompts/system/btw-user.md +3 -8
  85. package/src/prompts/tools/skill-discovery.md +1 -0
  86. package/src/runtime-mcp/client.ts +41 -55
  87. package/src/runtime-mcp/content-limits.ts +71 -0
  88. package/src/runtime-mcp/json-rpc.ts +23 -9
  89. package/src/runtime-mcp/transports/http.ts +50 -16
  90. package/src/sdk/bus/chat-daemon-control.ts +4 -3
  91. package/src/sdk/bus/index.ts +4 -5
  92. package/src/sdk/bus/lifecycle-commands.ts +16 -7
  93. package/src/sdk/bus/notification-service.ts +41 -18
  94. package/src/sdk/bus/telegram-daemon-contract.ts +8 -3
  95. package/src/sdk/bus/telegram-daemon-control.ts +2 -2
  96. package/src/sdk/bus/telegram-daemon.ts +1663 -375
  97. package/src/sdk/bus/topic-registry.ts +451 -12
  98. package/src/sdk/client/discovery.ts +19 -3
  99. package/src/sdk/client/liveness.ts +49 -0
  100. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  101. package/src/sdk/session.ts +29 -5
  102. package/src/sdk/transport/auth-preface.ts +82 -0
  103. package/src/sdk/transport/index.ts +18 -0
  104. package/src/sdk/transport/relay.ts +252 -0
  105. package/src/sdk/transport/serve-cli.ts +105 -0
  106. package/src/sdk/transport/socket.ts +197 -0
  107. package/src/sdk/transport/stdio.ts +17 -0
  108. package/src/session/agent-session.ts +240 -149
  109. package/src/session/blob-store.ts +19 -6
  110. package/src/session/btw-contract.ts +59 -0
  111. package/src/session/internal/managed-session-scope.ts +313 -57
  112. package/src/session/internal/managed-session-storage.ts +146 -35
  113. package/src/session/session-manager.ts +319 -85
  114. package/src/setup/credential-import.ts +28 -3
  115. package/src/setup/provider-onboarding.ts +30 -1
  116. package/src/setup/provider-presets.json +2 -1
  117. package/src/slash-commands/builtin-registry.ts +1 -1
  118. package/src/ssh/utils.ts +14 -0
  119. package/src/task/discovery.ts +8 -2
  120. package/src/tools/ask.ts +1 -1
  121. package/src/tools/fetch.ts +13 -131
  122. package/src/tools/image-gen.ts +301 -20
  123. package/src/tools/read.ts +2 -0
  124. package/src/tools/skill-discovery.ts +39 -3
  125. package/src/tools/sqlite-reader.ts +63 -5
  126. package/src/utils/pasted-image-path.ts +15 -6
  127. package/src/utils/shell-snapshot.ts +173 -63
  128. package/src/web/insane/bridge.ts +5 -1
  129. package/src/web/insane/url-guard.ts +138 -27
  130. package/src/web/scrapers/types.ts +19 -49
  131. package/src/web/scrapers/utils.ts +12 -26
@@ -12,9 +12,11 @@ import {
12
12
  openRecoveryFsRoot,
13
13
  type RecoveryFsRoot,
14
14
  renameNoReplacePath,
15
+ repairOwnerOnlyPathSecurityExpected,
15
16
  snapshotDirectoryTree,
16
17
  verifyOwnerOnlyFdSecurity,
17
18
  verifyOwnerOnlyPathSecurity,
19
+ verifyOwnerOnlyPathSecurityExpected,
18
20
  } from "@gajae-code/natives";
19
21
 
20
22
  export const MANAGED_ARTIFACT_MAX_DEPTH = 32;
@@ -27,6 +29,8 @@ const LOCK_WAIT_MS = 5_000;
27
29
 
28
30
  const LOCK_STALE_RECHECK_MS = 100;
29
31
 
32
+ export type ManagedSessionSecurityPolicy = "default" | "windows-existing-verify-first";
33
+
30
34
  export type ManagedStorageFailure =
31
35
  | "migration_busy"
32
36
  | "binding_conflict"
@@ -216,14 +220,16 @@ export function retainManagedDirectoryAuthority(
216
220
  }
217
221
  }
218
222
 
219
- function ensureDirectoryComponent(pathname: string): void {
223
+ function ensureDirectoryComponent(pathname: string): boolean {
224
+ let created = false;
220
225
  try {
221
226
  fs.mkdirSync(pathname, { mode: 0o700 });
227
+ created = true;
222
228
  } catch (error) {
223
229
  if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
224
230
  }
225
231
  assertSafeDirectory(pathname);
226
- secure(pathname, "directory");
232
+ return created;
227
233
  }
228
234
 
229
235
  export function assertManagedDirectoryRoot(root: ManagedDirectoryRoot): void {
@@ -263,10 +269,66 @@ function secure(pathname: string, kind: "directory" | "file"): void {
263
269
  if (!verified.ok) throw securityError(pathname, verified);
264
270
  }
265
271
 
272
+ function windowsExistingVerifyFirst(policy: ManagedSessionSecurityPolicy): boolean {
273
+ return process.platform === "win32" && policy === "windows-existing-verify-first";
274
+ }
275
+
276
+ function assertManagedPathIdentity(pathname: string, kind: "directory" | "file", expected: fs.BigIntStats): void {
277
+ const current = fs.lstatSync(pathname, { bigint: true });
278
+ if (current.isSymbolicLink()) throw new Error("reparse_point");
279
+ const expectedKind = kind === "directory" ? current.isDirectory() : current.isFile();
280
+ if (!expectedKind) throw new Error(kind === "directory" ? "not_directory" : "not_file");
281
+ if (current.dev !== expected.dev || current.ino !== expected.ino) throw new Error("identity_mismatch");
282
+ }
283
+
284
+ function verifyExistingManagedPathSecurity(
285
+ pathname: string,
286
+ kind: "directory" | "file",
287
+ expected: fs.BigIntStats,
288
+ ): void {
289
+ const verified = validateNativeSecurityResult(
290
+ verifyOwnerOnlyPathSecurityExpected(pathname, kind, expected.dev, expected.ino),
291
+ "verify",
292
+ kind,
293
+ );
294
+ assertManagedPathIdentity(pathname, kind, expected);
295
+ if (!verified.ok) throw securityError(pathname, verified);
296
+ }
297
+
298
+ function secureExistingManagedDirectory(pathname: string, kind: "directory" | "file"): void {
299
+ const named = fs.lstatSync(pathname, { bigint: true });
300
+ const safeKind = kind === "directory" ? named.isDirectory() : named.isFile();
301
+ if (!safeKind || named.isSymbolicLink()) throw new Error(`Unsafe managed ${kind}: ${pathname}`);
302
+ const verified = validateNativeSecurityResult(
303
+ verifyOwnerOnlyPathSecurityExpected(pathname, kind, named.dev, named.ino),
304
+ "verify",
305
+ kind,
306
+ );
307
+ assertManagedPathIdentity(pathname, kind, named);
308
+ if (verified.ok) return;
309
+ if (verified.code !== "acl_verify_failed") throw securityError(pathname, verified);
310
+ const repaired = validateNativeSecurityResult(
311
+ repairOwnerOnlyPathSecurityExpected(pathname, kind, named.dev, named.ino),
312
+ "verify",
313
+ kind,
314
+ );
315
+ if (!repaired.ok) throw securityError(pathname, repaired);
316
+ assertManagedPathIdentity(pathname, kind, named);
317
+ }
318
+
319
+ function secureManagedDirectory(pathname: string, created: boolean, policy: ManagedSessionSecurityPolicy): void {
320
+ if (!created && windowsExistingVerifyFirst(policy)) {
321
+ secureExistingManagedDirectory(pathname, "directory");
322
+ return;
323
+ }
324
+ secure(pathname, "directory");
325
+ }
326
+
266
327
  /** Internal retained-root capability for one managed descendant subtree. */
267
328
  export class ManagedSessionDescendantStore {
268
329
  readonly #root: ManagedDirectoryRoot;
269
330
  readonly #baseDir: string;
331
+ readonly #policy: ManagedSessionSecurityPolicy;
270
332
  readonly #authority: RecoveryFsRoot | undefined;
271
333
  readonly #authorityBaseDir: string;
272
334
  readonly #subtreeRoot: ManagedDirectoryRoot;
@@ -275,11 +337,13 @@ export class ManagedSessionDescendantStore {
275
337
  root: ManagedDirectoryRoot,
276
338
  baseDir: string,
277
339
  retained?: { authority: RecoveryFsRoot; authorityBaseDir: string },
340
+ policy?: ManagedSessionSecurityPolicy,
278
341
  ) {
279
342
  assertManagedDirectoryRoot(root);
280
343
  managedRelativePath(root, baseDir);
281
344
  this.#root = root;
282
345
  this.#baseDir = path.resolve(baseDir);
346
+ this.#policy = policy ?? "default";
283
347
  this.#authorityBaseDir = retained?.authorityBaseDir ?? this.#baseDir;
284
348
  if (retained) {
285
349
  const relative = path.relative(retained.authorityBaseDir, this.#baseDir).split(path.sep).join("/");
@@ -300,7 +364,7 @@ export class ManagedSessionDescendantStore {
300
364
  this.#authority = retained.authority;
301
365
  return;
302
366
  }
303
- ensureManagedDirectory(this.#baseDir, root);
367
+ ensureManagedDirectory(this.#baseDir, root, this.#policy);
304
368
  const subtreeStat = fs.lstatSync(this.#baseDir, { bigint: true });
305
369
  this.#subtreeRoot = Object.freeze({ canonicalPath: this.#baseDir, dev: subtreeStat.dev, ino: subtreeStat.ino });
306
370
  if (process.platform === "linux") {
@@ -332,19 +396,28 @@ export class ManagedSessionDescendantStore {
332
396
  return this.#subtreeRoot;
333
397
  }
334
398
 
399
+ get securityPolicy(): ManagedSessionSecurityPolicy {
400
+ return this.#policy;
401
+ }
402
+
335
403
  deriveSubtree(relativePath: string): ManagedSessionDescendantStore {
336
404
  const child = this.ensureDirectory(relativePath);
337
405
  const resolved = this.#resolve(relativePath);
338
- if (!this.#authority) return new ManagedSessionDescendantStore(this.#root, resolved);
406
+ if (!this.#authority) return new ManagedSessionDescendantStore(this.#root, resolved, undefined, this.#policy);
339
407
  const retainedChild = this.#authority.retainManagedDirectory(
340
408
  this.#relative(resolved),
341
409
  child.dev.toString(),
342
410
  child.ino.toString(),
343
411
  );
344
- return new ManagedSessionDescendantStore(this.#root, resolved, {
345
- authority: retainedChild,
346
- authorityBaseDir: resolved,
347
- });
412
+ return new ManagedSessionDescendantStore(
413
+ this.#root,
414
+ resolved,
415
+ {
416
+ authority: retainedChild,
417
+ authorityBaseDir: resolved,
418
+ },
419
+ this.#policy,
420
+ );
348
421
  }
349
422
 
350
423
  retainAuthority(): RecoveryFsRoot | undefined {
@@ -371,12 +444,19 @@ export class ManagedSessionDescendantStore {
371
444
  return;
372
445
  }
373
446
  } else {
374
- const verified = validateNativeSecurityResult(
375
- verifyOwnerOnlyPathSecurity(this.#baseDir, "directory"),
376
- "verify",
377
- "directory",
378
- );
379
- if (!verified.ok) throw securityError(this.#baseDir, verified);
447
+ const named = fs.lstatSync(this.#baseDir, { bigint: true });
448
+ if (!named.isDirectory() || named.isSymbolicLink())
449
+ throw new Error(`Unsafe managed directory: ${this.#baseDir}`);
450
+ if (windowsExistingVerifyFirst(this.#policy))
451
+ verifyExistingManagedPathSecurity(this.#baseDir, "directory", named);
452
+ else {
453
+ const verified = validateNativeSecurityResult(
454
+ verifyOwnerOnlyPathSecurity(this.#baseDir, "directory"),
455
+ "verify",
456
+ "directory",
457
+ );
458
+ if (!verified.ok) throw securityError(this.#baseDir, verified);
459
+ }
380
460
  }
381
461
  this.#assertBound();
382
462
  }
@@ -421,7 +501,7 @@ export class ManagedSessionDescendantStore {
421
501
  ino: BigInt(ensured.identity.ino),
422
502
  });
423
503
  }
424
- ensureManagedDirectory(this.#resolve(relativePath), this.#root);
504
+ ensureManagedDirectory(this.#resolve(relativePath), this.#root, this.#policy);
425
505
  const named = fs.lstatSync(this.#resolve(relativePath), { bigint: true });
426
506
  return Object.freeze({ canonicalPath: this.#resolve(relativePath), dev: named.dev, ino: named.ino });
427
507
  }
@@ -434,13 +514,13 @@ export class ManagedSessionDescendantStore {
434
514
  this.#assertBound();
435
515
  return;
436
516
  }
437
- await publishManagedFileNoReplace(resolved, bytes, undefined, this.#root);
517
+ await publishManagedFileNoReplace(resolved, bytes, undefined, this.#root, this.#policy);
438
518
  }
439
519
 
440
520
  publishNoReplaceSync(relativePath: string, bytes: Uint8Array): void {
441
521
  const resolved = this.#resolve(relativePath);
442
522
  if (!this.#authority) {
443
- publishManagedFileNoReplaceSync(resolved, bytes, this.#root);
523
+ publishManagedFileNoReplaceSync(resolved, bytes, this.#root, this.#policy);
444
524
  this.#assertBound();
445
525
  return;
446
526
  }
@@ -453,7 +533,7 @@ export class ManagedSessionDescendantStore {
453
533
  this.#assertBound();
454
534
  const resolved = this.#resolve(relativePath);
455
535
  if (!this.#authority) {
456
- await replaceManagedFile(resolved, bytes, this.#subtreeRoot);
536
+ await replaceManagedFile(resolved, bytes, this.#subtreeRoot, this.#policy);
457
537
  this.#assertBound();
458
538
  return;
459
539
  }
@@ -466,7 +546,7 @@ export class ManagedSessionDescendantStore {
466
546
  this.#assertBound();
467
547
  const resolved = this.#resolve(relativePath);
468
548
  if (!this.#authority) {
469
- replaceManagedFileSync(resolved, bytes, this.#subtreeRoot);
549
+ replaceManagedFileSync(resolved, bytes, this.#subtreeRoot, this.#policy);
470
550
  this.#assertBound();
471
551
  return;
472
552
  }
@@ -1060,25 +1140,43 @@ function sameFileIdentity(left: fs.BigIntStats, right: fs.BigIntStats): boolean
1060
1140
  }
1061
1141
 
1062
1142
  /** Create a managed directory and fail closed unless its owner-only mode/ACL verifies. */
1063
- export function ensureManagedDirectory(pathname: string, root?: ManagedDirectoryRoot): void {
1143
+ export function ensureManagedDirectory(
1144
+ pathname: string,
1145
+ root?: ManagedDirectoryRoot,
1146
+ policy: ManagedSessionSecurityPolicy = "default",
1147
+ ): void {
1064
1148
  if (!root) {
1149
+ let existed = false;
1150
+ try {
1151
+ const named = fs.lstatSync(pathname);
1152
+ existed = true;
1153
+ if (!named.isDirectory() || named.isSymbolicLink()) throw new Error(`Unsafe managed directory: ${pathname}`);
1154
+ } catch (error) {
1155
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
1156
+ }
1065
1157
  fs.mkdirSync(pathname, { recursive: true, mode: 0o700 });
1066
1158
  assertSafeDirectory(pathname);
1067
- secure(pathname, "directory");
1159
+ if (existed && windowsExistingVerifyFirst(policy)) secureExistingManagedDirectory(pathname, "directory");
1160
+ else secure(pathname, "directory");
1068
1161
  return;
1069
1162
  }
1070
1163
  ensureManagedRoot(root);
1164
+ const components = managedRelativePath(root, pathname);
1165
+ if (components.length === 0) {
1166
+ if (windowsExistingVerifyFirst(policy)) secureExistingManagedDirectory(root.canonicalPath, "directory");
1167
+ return;
1168
+ }
1071
1169
  let current = root.canonicalPath;
1072
- for (const component of managedRelativePath(root, pathname)) {
1170
+ if (windowsExistingVerifyFirst(policy)) secureExistingManagedDirectory(current, "directory");
1171
+ else secure(current, "directory");
1172
+ for (const component of components) {
1073
1173
  // Re-inspect the captured root or already-secured descendant before every
1074
1174
  // descent; a replaced component cannot grant authority to the next one.
1075
1175
  if (current === root.canonicalPath) assertManagedDirectoryRoot(root);
1076
- else {
1077
- assertSafeDirectory(current);
1078
- secure(current, "directory");
1079
- }
1176
+ else assertSafeDirectory(current);
1080
1177
  current = path.join(current, component);
1081
- ensureDirectoryComponent(current);
1178
+ const created = ensureDirectoryComponent(current);
1179
+ secureManagedDirectory(current, created, policy);
1082
1180
  }
1083
1181
  }
1084
1182
 
@@ -1123,9 +1221,10 @@ export async function publishManagedFileNoReplace(
1123
1221
  bytes: Uint8Array,
1124
1222
  assertOwned?: () => void,
1125
1223
  root?: ManagedDirectoryRoot,
1224
+ policy: ManagedSessionSecurityPolicy = "default",
1126
1225
  ): Promise<void> {
1127
1226
  const parent = path.dirname(destination);
1128
- ensureManagedDirectory(parent, root);
1227
+ ensureManagedDirectory(parent, root, policy);
1129
1228
  const staging = path.join(parent, `.${path.basename(destination)}.${randomUUID()}.staging`);
1130
1229
  let fd: number | undefined;
1131
1230
  let stagingIdentity: { dev: bigint; ino: bigint } | undefined;
@@ -1208,9 +1307,10 @@ export function publishManagedFileNoReplaceSync(
1208
1307
  destination: string,
1209
1308
  bytes: Uint8Array,
1210
1309
  root?: ManagedDirectoryRoot,
1310
+ policy: ManagedSessionSecurityPolicy = "default",
1211
1311
  ): void {
1212
1312
  const parent = path.dirname(destination);
1213
- ensureManagedDirectory(parent, root);
1313
+ ensureManagedDirectory(parent, root, policy);
1214
1314
  const staging = path.join(parent, `.${path.basename(destination)}.${randomUUID()}.staging`);
1215
1315
  let fd: number | undefined;
1216
1316
  let stagingIdentity: { dev: bigint; ino: bigint } | undefined;
@@ -1291,9 +1391,14 @@ export function publishManagedFileNoReplaceSync(
1291
1391
  }
1292
1392
 
1293
1393
  /** Replace one managed regular file while retaining the secured staging fd through publication. */
1294
- export function replaceManagedFileSync(destination: string, bytes: Uint8Array, root: ManagedDirectoryRoot): void {
1394
+ export function replaceManagedFileSync(
1395
+ destination: string,
1396
+ bytes: Uint8Array,
1397
+ root: ManagedDirectoryRoot,
1398
+ policy: ManagedSessionSecurityPolicy = "default",
1399
+ ): void {
1295
1400
  const parent = path.dirname(destination);
1296
- ensureManagedDirectory(parent, root);
1401
+ ensureManagedDirectory(parent, root, policy);
1297
1402
  const staging = path.join(parent, `.${path.basename(destination)}.${randomUUID()}.replacement`);
1298
1403
  let fd: number | undefined;
1299
1404
  let stagedIdentity: { dev: bigint; ino: bigint } | undefined;
@@ -1338,8 +1443,9 @@ export async function replaceManagedFile(
1338
1443
  destination: string,
1339
1444
  bytes: Uint8Array,
1340
1445
  root: ManagedDirectoryRoot,
1446
+ policy: ManagedSessionSecurityPolicy = "default",
1341
1447
  ): Promise<void> {
1342
- replaceManagedFileSync(destination, bytes, root);
1448
+ replaceManagedFileSync(destination, bytes, root, policy);
1343
1449
  }
1344
1450
 
1345
1451
  /** Copy the exact bytes captured from one no-follow source descriptor. */
@@ -1348,11 +1454,12 @@ export async function copyManagedFileNoReplace(
1348
1454
  destination: string,
1349
1455
  snapshot = captureManagedFileNoFollow(source),
1350
1456
  root?: ManagedDirectoryRoot,
1457
+ policy: ManagedSessionSecurityPolicy = "default",
1351
1458
  ): Promise<void> {
1352
1459
  const named = captureManagedFileNoFollow(source);
1353
1460
  if (!sameIdentity(snapshot.identity, named.identity) || !snapshot.bytes.equals(named.bytes))
1354
1461
  throw new Error("source_changed");
1355
- await publishManagedFileNoReplace(destination, snapshot.bytes, undefined, root);
1462
+ await publishManagedFileNoReplace(destination, snapshot.bytes, undefined, root, policy);
1356
1463
  const destinationSnapshot = captureManagedFileNoFollow(destination);
1357
1464
  if (!destinationSnapshot.bytes.equals(snapshot.bytes)) throw new Error("durability_failed");
1358
1465
  }
@@ -1362,8 +1469,9 @@ export async function acquireManagedLock(
1362
1469
  locksDirectory: string,
1363
1470
  name: string,
1364
1471
  root?: ManagedDirectoryRoot,
1472
+ policy: ManagedSessionSecurityPolicy = "default",
1365
1473
  ): Promise<ManagedStorageLock> {
1366
- ensureManagedDirectory(locksDirectory, root);
1474
+ ensureManagedDirectory(locksDirectory, root, policy);
1367
1475
  const lockPath = path.join(locksDirectory, `${name}.lock`);
1368
1476
  const deadline = Date.now() + LOCK_WAIT_MS;
1369
1477
  let staleObservedAt: number | undefined;
@@ -1513,7 +1621,10 @@ export function fsyncManagedArtifactTree(root: string): NativeDirectoryTreeSnaps
1513
1621
  const stat = fs.lstatSync(pathname);
1514
1622
  if (stat.isSymbolicLink()) throw new Error("unsafe_artifacts");
1515
1623
  if (stat.isFile()) {
1516
- const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
1624
+ const fd = fs.openSync(
1625
+ pathname,
1626
+ (process.platform === "win32" ? fs.constants.O_WRONLY : fs.constants.O_RDONLY) | fs.constants.O_NOFOLLOW,
1627
+ );
1517
1628
  try {
1518
1629
  fs.fsyncSync(fd);
1519
1630
  } finally {