@gajae-code/coding-agent 0.15.5 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/CHANGELOG.md +85 -1
  2. package/dist/types/async/job-manager.d.ts +83 -0
  3. package/dist/types/cli/args.d.ts +2 -0
  4. package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
  5. package/dist/types/cli/model-presets-cli.d.ts +9 -0
  6. package/dist/types/cli/root-flags.d.ts +14 -0
  7. package/dist/types/cli/update-cli.d.ts +15 -0
  8. package/dist/types/cli.d.ts +7 -0
  9. package/dist/types/commands/auth-gateway.d.ts +3 -0
  10. package/dist/types/commands/gc.d.ts +13 -0
  11. package/dist/types/commands/launch.d.ts +10 -0
  12. package/dist/types/commands/model-presets.d.ts +21 -0
  13. package/dist/types/config/autorouting-tier-map.d.ts +120 -0
  14. package/dist/types/config/file-lock.d.ts +54 -2
  15. package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
  16. package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
  17. package/dist/types/config/keybindings.d.ts +3 -2
  18. package/dist/types/config/model-preset-registry.d.ts +368 -0
  19. package/dist/types/config/model-profile-activation.d.ts +39 -0
  20. package/dist/types/config/model-profiles.d.ts +5 -3
  21. package/dist/types/config/model-registry.d.ts +17 -5
  22. package/dist/types/config/settings-schema.d.ts +19 -1
  23. package/dist/types/config/settings.d.ts +2 -0
  24. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
  25. package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
  26. package/dist/types/crash/record-loader.d.ts +15 -0
  27. package/dist/types/extensibility/extensions/types.d.ts +2 -5
  28. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
  29. package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
  30. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  31. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  32. package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
  33. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
  34. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  35. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/main.d.ts +3 -1
  38. package/dist/types/master-mode/context.d.ts +21 -0
  39. package/dist/types/master-mode/first-request.d.ts +34 -0
  40. package/dist/types/master-mode/snapshot.d.ts +32 -0
  41. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  42. package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
  43. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  44. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  45. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  46. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  47. package/dist/types/modes/jobs-observer.d.ts +36 -0
  48. package/dist/types/modes/print-mode.d.ts +17 -0
  49. package/dist/types/modes/ui-language.d.ts +7 -1
  50. package/dist/types/sdk/broker/broker.d.ts +80 -1
  51. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  52. package/dist/types/sdk/broker/index.d.ts +3 -0
  53. package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
  54. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  55. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  56. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  57. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  58. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  59. package/dist/types/sdk/broker/state-version.d.ts +4 -1
  60. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  61. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  62. package/dist/types/sdk/bus/index.d.ts +6 -0
  63. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
  64. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
  65. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  66. package/dist/types/sdk/cli/index.d.ts +1 -0
  67. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  68. package/dist/types/sdk/cli/rows.d.ts +4 -3
  69. package/dist/types/sdk/cli/session-cli.d.ts +18 -2
  70. package/dist/types/sdk/client/client.d.ts +3 -2
  71. package/dist/types/sdk/client/discovery.d.ts +1 -0
  72. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  73. package/dist/types/sdk/host/control/index.d.ts +1 -1
  74. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  75. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  76. package/dist/types/sdk/host/host.d.ts +10 -0
  77. package/dist/types/sdk/host/model-pin.d.ts +7 -2
  78. package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
  79. package/dist/types/sdk/host/session-runtime.d.ts +67 -1
  80. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  81. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  82. package/dist/types/sdk/lifecycle-session.d.ts +2 -0
  83. package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
  84. package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
  85. package/dist/types/sdk/router/session-router.d.ts +11 -0
  86. package/dist/types/sdk/session.d.ts +16 -0
  87. package/dist/types/sdk/startup-capability.d.ts +2 -1
  88. package/dist/types/session/account-inventory.d.ts +1 -0
  89. package/dist/types/session/agent-session.d.ts +24 -7
  90. package/dist/types/session/credential-store-errors.d.ts +1 -0
  91. package/dist/types/session/fold-coordinator.d.ts +201 -0
  92. package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
  93. package/dist/types/session/sdk-run-capability.d.ts +2 -0
  94. package/dist/types/session/session-manager.d.ts +65 -0
  95. package/dist/types/session/yield-queue.d.ts +5 -4
  96. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  97. package/dist/types/telemetry/control.d.ts +11 -0
  98. package/dist/types/telemetry/events.d.ts +20 -0
  99. package/dist/types/telemetry/index.d.ts +3 -0
  100. package/dist/types/telemetry/transport.d.ts +22 -0
  101. package/dist/types/tools/bash-interactive.d.ts +23 -0
  102. package/dist/types/tools/bash.d.ts +4 -0
  103. package/dist/types/tools/gh.d.ts +8 -0
  104. package/dist/types/tools/index.d.ts +10 -5
  105. package/dist/types/web/search/providers/base.d.ts +9 -4
  106. package/dist/types/web/search/providers/xai.d.ts +2 -1
  107. package/dist/types/web/search/types.d.ts +14 -0
  108. package/package.json +10 -7
  109. package/scripts/generate-hotkeys-docs.ts +4 -0
  110. package/scripts/generate-sdk-operation-inventory.ts +9 -3
  111. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  112. package/src/async/job-manager.ts +671 -39
  113. package/src/cli/accounts-cli.ts +9 -5
  114. package/src/cli/args.ts +17 -0
  115. package/src/cli/auth-broker-cli.ts +40 -12
  116. package/src/cli/auth-gateway-cli.ts +273 -97
  117. package/src/cli/config-cli.ts +2 -2
  118. package/src/cli/fast-help.ts +4 -0
  119. package/src/cli/model-presets-cli.ts +100 -0
  120. package/src/cli/root-flags.ts +5 -0
  121. package/src/cli/update-cli.ts +117 -16
  122. package/src/cli.ts +6 -2
  123. package/src/commands/auth-gateway.ts +12 -8
  124. package/src/commands/gc.ts +19 -0
  125. package/src/commands/launch.ts +86 -18
  126. package/src/commands/model-presets.ts +24 -0
  127. package/src/commands/sdk.ts +113 -21
  128. package/src/config/autorouting-tier-map.ts +44 -0
  129. package/src/config/file-lock-gc.ts +9 -4
  130. package/src/config/file-lock.ts +890 -99
  131. package/src/config/internal/model-preset-registry-test-state.ts +28 -0
  132. package/src/config/internal/model-preset-registry-test-support.ts +11 -0
  133. package/src/config/keybindings.ts +6 -2
  134. package/src/config/model-preset-registry.ts +2795 -0
  135. package/src/config/model-profile-activation.ts +115 -28
  136. package/src/config/model-profile-contract.ts +4 -1
  137. package/src/config/model-profiles.ts +56 -14
  138. package/src/config/model-registry.ts +1044 -122
  139. package/src/config/model-resolver.ts +5 -0
  140. package/src/config/settings-schema.ts +19 -1
  141. package/src/config/settings.ts +11 -1
  142. package/src/coordinator-mcp/model-preset.ts +32 -17
  143. package/src/coordinator-mcp/projection-scan.ts +592 -0
  144. package/src/coordinator-mcp/question-state.ts +9 -11
  145. package/src/coordinator-mcp/server.ts +111 -42
  146. package/src/coordinator-mcp/session-reaper.ts +9 -3
  147. package/src/crash/index-store.ts +2 -0
  148. package/src/crash/record-loader.ts +10 -0
  149. package/src/extensibility/extensions/loader.ts +0 -1
  150. package/src/extensibility/extensions/types.ts +2 -3
  151. package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
  152. package/src/gjc-runtime/empty-delete-gc.ts +301 -0
  153. package/src/gjc-runtime/gc-render.ts +29 -0
  154. package/src/gjc-runtime/gc-runtime.ts +174 -11
  155. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  156. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  157. package/src/gjc-runtime/session-state-lock.ts +1321 -147
  158. package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
  159. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  160. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
  161. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  162. package/src/hooks/skill-state.ts +6 -0
  163. package/src/index.ts +1 -0
  164. package/src/internal-urls/docs-index.generated.ts +14 -13
  165. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  166. package/src/lsp/config.ts +4 -3
  167. package/src/lsp/path-trust.ts +12 -6
  168. package/src/main.ts +66 -4
  169. package/src/master-mode/context.ts +50 -0
  170. package/src/master-mode/first-request.ts +65 -0
  171. package/src/master-mode/snapshot.ts +205 -0
  172. package/src/modes/acp/acp-agent.ts +616 -161
  173. package/src/modes/components/footer.ts +4 -2
  174. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  175. package/src/modes/components/jobs-overlay-model.ts +99 -16
  176. package/src/modes/components/model-selector.ts +158 -18
  177. package/src/modes/components/oauth-selector.ts +6 -1
  178. package/src/modes/components/settings-selector.ts +1 -0
  179. package/src/modes/components/status-line/model-name.ts +53 -0
  180. package/src/modes/components/status-line/priority-row.ts +107 -0
  181. package/src/modes/components/status-line/segments.ts +29 -10
  182. package/src/modes/components/tool-status-header.ts +151 -15
  183. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  184. package/src/modes/controllers/input-controller.ts +51 -6
  185. package/src/modes/controllers/selector-controller.ts +191 -96
  186. package/src/modes/interactive-mode.ts +14 -1
  187. package/src/modes/jobs-observer.ts +149 -16
  188. package/src/modes/print-mode.ts +64 -0
  189. package/src/modes/runtime-init.ts +1 -1
  190. package/src/modes/ui-language.ts +49 -2
  191. package/src/modes/utils/injected-user-submission.ts +15 -1
  192. package/src/prompts/system/master-mode.md +28 -0
  193. package/src/prompts/tools/bash.md +2 -2
  194. package/src/prompts/tools/fold-receipt.md +3 -0
  195. package/src/prompts/tools/job.md +1 -1
  196. package/src/sdk/acp/adapter.ts +17 -2
  197. package/src/sdk/broker/broker.ts +1671 -34
  198. package/src/sdk/broker/endpoint-authority.ts +86 -0
  199. package/src/sdk/broker/ensure.ts +5 -0
  200. package/src/sdk/broker/index.ts +3 -0
  201. package/src/sdk/broker/lifecycle.ts +217 -46
  202. package/src/sdk/broker/master-capability.ts +232 -0
  203. package/src/sdk/broker/session-index.ts +121 -17
  204. package/src/sdk/broker/session-scope.ts +272 -0
  205. package/src/sdk/broker/spawn-authority.ts +1031 -0
  206. package/src/sdk/broker/spawn-substrate.ts +548 -0
  207. package/src/sdk/broker/state-version.ts +26 -14
  208. package/src/sdk/broker/transport.ts +1 -0
  209. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  210. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  211. package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
  212. package/src/sdk/bus/index.ts +51 -4
  213. package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
  214. package/src/sdk/bus/prompt-reconciliation.ts +28 -4
  215. package/src/sdk/bus/slack-conversation.ts +5 -1
  216. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  217. package/src/sdk/cli/index.ts +1 -0
  218. package/src/sdk/cli/master-cli.ts +259 -0
  219. package/src/sdk/cli/rows.ts +11 -8
  220. package/src/sdk/cli/session-cli.ts +276 -6
  221. package/src/sdk/client/client.ts +14 -8
  222. package/src/sdk/client/discovery.ts +3 -1
  223. package/src/sdk/host/control/dispatch.ts +101 -37
  224. package/src/sdk/host/control/index.ts +2 -0
  225. package/src/sdk/host/control/operations.ts +2 -0
  226. package/src/sdk/host/control/runtime-gate.ts +203 -11
  227. package/src/sdk/host/host.ts +27 -2
  228. package/src/sdk/host/model-pin.ts +185 -25
  229. package/src/sdk/host/sdk-run-capability.ts +1 -0
  230. package/src/sdk/host/session-runtime.test.ts +581 -43
  231. package/src/sdk/host/session-runtime.ts +655 -101
  232. package/src/sdk/lifecycle/broker-client.ts +28 -0
  233. package/src/sdk/lifecycle/service.ts +279 -20
  234. package/src/sdk/lifecycle-session.ts +10 -2
  235. package/src/sdk/prompt-deadline-manager.ts +16 -1
  236. package/src/sdk/protocol/adapter-validation.ts +27 -0
  237. package/src/sdk/protocol/operation-inventory.generated.json +53 -2
  238. package/src/sdk/protocol/operation-registry.ts +17 -4
  239. package/src/sdk/router/session-router.ts +294 -154
  240. package/src/sdk/session.ts +440 -167
  241. package/src/sdk/startup-capability.ts +1 -0
  242. package/src/session/account-inventory.ts +27 -11
  243. package/src/session/agent-session.ts +1292 -180
  244. package/src/session/blob-store.ts +15 -5
  245. package/src/session/credential-store-errors.ts +2 -0
  246. package/src/session/fold-coordinator.ts +433 -0
  247. package/src/session/internal/managed-session-storage.ts +153 -78
  248. package/src/session/sdk-run-capability-internal.ts +15 -0
  249. package/src/session/sdk-run-capability.ts +2 -0
  250. package/src/session/session-manager.ts +342 -28
  251. package/src/session/yield-queue.ts +24 -10
  252. package/src/setup/frictionless-onboarding.ts +2 -0
  253. package/src/setup/provider-presets.json +1 -1
  254. package/src/slash-commands/helpers/credential-switch.ts +6 -1
  255. package/src/task/executor.ts +19 -3
  256. package/src/telemetry/control.ts +26 -0
  257. package/src/telemetry/events.ts +1268 -0
  258. package/src/telemetry/index.ts +3 -0
  259. package/src/telemetry/transport.ts +87 -0
  260. package/src/tools/bash-interactive.ts +182 -85
  261. package/src/tools/bash.ts +1028 -109
  262. package/src/tools/gh.ts +104 -48
  263. package/src/tools/github-cache.ts +2 -2
  264. package/src/tools/index.ts +11 -5
  265. package/src/tools/tool-catalog.generated.ts +1 -1
  266. package/src/web/search/provider.ts +120 -11
  267. package/src/web/search/providers/anthropic.ts +89 -17
  268. package/src/web/search/providers/base.ts +9 -4
  269. package/src/web/search/providers/gemini.ts +87 -15
  270. package/src/web/search/providers/openai-compatible.ts +23 -8
  271. package/src/web/search/providers/xai.ts +98 -12
  272. package/src/web/search/types.ts +16 -0
@@ -290,8 +290,8 @@ export interface ManagedBoundedAppendExpectation {
290
290
 
291
291
  function managedFileIdentityFromNative(identity: RecoveryFsIdentity): ManagedFileIdentity {
292
292
  return {
293
- dev: BigInt(identity.dev),
294
- ino: BigInt(identity.ino),
293
+ dev: canonicalFileId(BigInt(identity.dev)),
294
+ ino: canonicalFileId(BigInt(identity.ino)),
295
295
  nlink: BigInt(identity.nlink),
296
296
  size: Number(identity.size),
297
297
  mtimeNs: BigInt(identity.mtimeNs),
@@ -346,6 +346,12 @@ type LegacyReplacementCleanupIdentity = {
346
346
  };
347
347
 
348
348
  const U64_MAX = 18_446_744_073_709_551_615n;
349
+ const I64_ABS_MIN = 9_223_372_036_854_775_808n;
350
+
351
+ function canonicalFileId(value: bigint): bigint {
352
+ if (value < -I64_ABS_MIN || value > U64_MAX) throw new Error("file_identity_out_of_range");
353
+ return BigInt.asUintN(64, value);
354
+ }
349
355
 
350
356
  function parseCanonicalU64(value: unknown): bigint | undefined {
351
357
  if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > 20) return undefined;
@@ -353,10 +359,19 @@ function parseCanonicalU64(value: unknown): bigint | undefined {
353
359
  return parsed <= U64_MAX ? parsed : undefined;
354
360
  }
355
361
 
356
- function parseLegacyHexU64(value: string): bigint | undefined {
357
- if (!/^[0-9a-f]+$/.test(value) || value.length > 16) return undefined;
358
- const parsed = BigInt(`0x${value}`);
359
- return parsed <= U64_MAX ? parsed : undefined;
362
+ function parseFileId(value: unknown): bigint | undefined {
363
+ const canonical = parseCanonicalU64(value);
364
+ if (canonical !== undefined) return canonical;
365
+ if (typeof value !== "string" || !/^-[1-9][0-9]*$/.test(value) || value.length > 20) return undefined;
366
+ const parsed = BigInt(value);
367
+ return parsed >= -I64_ABS_MIN ? canonicalFileId(parsed) : undefined;
368
+ }
369
+
370
+ function parseFileIdHex(value: string): bigint | undefined {
371
+ if (/^(0|[1-9a-f][0-9a-f]*)$/.test(value) && value.length <= 16) return BigInt(`0x${value}`);
372
+ if (!/^-[1-9a-f][0-9a-f]*$/.test(value) || value.length > 17) return undefined;
373
+ const magnitude = BigInt(`0x${value.slice(1)}`);
374
+ return magnitude <= I64_ABS_MIN ? canonicalFileId(-magnitude) : undefined;
360
375
  }
361
376
 
362
377
  function asRecord(value: unknown): Record<string, unknown> | undefined {
@@ -365,19 +380,21 @@ function asRecord(value: unknown): Record<string, unknown> | undefined {
365
380
  : undefined;
366
381
  }
367
382
 
368
- function legacyReplacementCleanupReceiptBinding(name: string): { dev: bigint; ino: bigint } | undefined {
369
- const match = /^\.gjc-replace-cleanup-([0-9a-f]+)-([0-9a-f]+)\.json$/.exec(name);
383
+ function legacyReplacementCleanupReceiptBinding(
384
+ name: string,
385
+ ): { dev: bigint; ino: bigint; encodedDev: string; encodedIno: string } | undefined {
386
+ const match = /^\.gjc-replace-cleanup-(-?(?:0|[1-9a-f][0-9a-f]*))-(-?(?:0|[1-9a-f][0-9a-f]*))\.json$/.exec(name);
370
387
  if (!match?.[1] || !match[2]) return undefined;
371
- const dev = parseLegacyHexU64(match[1]);
372
- const ino = parseLegacyHexU64(match[2]);
373
- return dev !== undefined && ino !== undefined ? { dev, ino } : undefined;
388
+ const dev = parseFileIdHex(match[1]);
389
+ const ino = parseFileIdHex(match[2]);
390
+ return dev !== undefined && ino !== undefined ? { dev, ino, encodedDev: match[1], encodedIno: match[2] } : undefined;
374
391
  }
375
392
 
376
393
  function parseLegacyReplacementCleanupIdentity(value: unknown): LegacyReplacementCleanupIdentity | undefined {
377
394
  const identity = asRecord(value);
378
395
  if (!identity) return undefined;
379
- const dev = parseCanonicalU64(identity.dev);
380
- const ino = parseCanonicalU64(identity.ino);
396
+ const dev = parseFileId(identity.dev);
397
+ const ino = parseFileId(identity.ino);
381
398
  const nlink = parseCanonicalU64(identity.nlink);
382
399
  const size = parseCanonicalU64(identity.size);
383
400
  const mtimeNs = parseCanonicalU64(identity.mtimeNs);
@@ -417,28 +434,50 @@ function replacementCleanupReceiptBinding(
417
434
  name: string,
418
435
  ): { predecessor: { dev: bigint; ino: bigint }; receipt: { dev: bigint; ino: bigint } } | undefined {
419
436
  const match =
420
- /^\.gjc-replace-cleanup-(0|[1-9a-f][0-9a-f]*)-(0|[1-9a-f][0-9a-f]*)-receipt-(0|[1-9a-f][0-9a-f]*)-(0|[1-9a-f][0-9a-f]*)\.json$/.exec(
437
+ /^\.gjc-replace-cleanup-(-?(?:0|[1-9a-f][0-9a-f]*))-(-?(?:0|[1-9a-f][0-9a-f]*))-receipt-(-?(?:0|[1-9a-f][0-9a-f]*))-(-?(?:0|[1-9a-f][0-9a-f]*))\.json$/.exec(
421
438
  name,
422
439
  );
423
440
  if (!match?.[1] || !match[2] || !match[3] || !match[4]) return undefined;
424
- const predecessor = { dev: BigInt(`0x${match[1]}`), ino: BigInt(`0x${match[2]}`) };
425
- const receipt = { dev: BigInt(`0x${match[3]}`), ino: BigInt(`0x${match[4]}`) };
426
- return predecessor.dev <= U64_MAX && predecessor.ino <= U64_MAX && receipt.dev <= U64_MAX && receipt.ino <= U64_MAX
427
- ? { predecessor, receipt }
441
+ const predecessor = { dev: parseFileIdHex(match[1]), ino: parseFileIdHex(match[2]) };
442
+ const receipt = { dev: parseFileIdHex(match[3]), ino: parseFileIdHex(match[4]) };
443
+ return predecessor.dev !== undefined &&
444
+ predecessor.ino !== undefined &&
445
+ receipt.dev !== undefined &&
446
+ receipt.ino !== undefined
447
+ ? { predecessor: { dev: predecessor.dev, ino: predecessor.ino }, receipt: { dev: receipt.dev, ino: receipt.ino } }
428
448
  : undefined;
429
449
  }
430
450
 
431
451
  function parseReplacementIdentity(value: unknown): ManagedFileSnapshot["identity"] | undefined {
432
- const parsed = parseLegacyReplacementCleanupIdentity(value);
433
- if (!parsed || parsed.size > BigInt(Number.MAX_SAFE_INTEGER)) return undefined;
452
+ const identity = asRecord(value);
453
+ if (!identity) return undefined;
454
+ const dev = parseFileId(identity.dev);
455
+ const ino = parseFileId(identity.ino);
456
+ const nlink = parseCanonicalU64(identity.nlink);
457
+ const size = parseCanonicalU64(identity.size);
458
+ const mtimeNs = parseCanonicalU64(identity.mtimeNs);
459
+ const ctimeNs = parseCanonicalU64(identity.ctimeNs);
460
+ const sha256 = identity.sha256;
461
+ if (
462
+ dev === undefined ||
463
+ ino === undefined ||
464
+ nlink === undefined ||
465
+ size === undefined ||
466
+ size > BigInt(Number.MAX_SAFE_INTEGER) ||
467
+ mtimeNs === undefined ||
468
+ ctimeNs === undefined ||
469
+ typeof sha256 !== "string" ||
470
+ !/^[0-9a-f]{64}$/.test(sha256)
471
+ )
472
+ return undefined;
434
473
  return {
435
- dev: parsed.dev,
436
- ino: parsed.ino,
437
- nlink: parsed.nlink,
438
- size: Number(parsed.size),
439
- mtimeNs: parsed.mtimeNs,
440
- ctimeNs: parsed.ctimeNs,
441
- sha256: parsed.sha256,
474
+ dev,
475
+ ino,
476
+ nlink,
477
+ size: Number(size),
478
+ mtimeNs,
479
+ ctimeNs,
480
+ sha256,
442
481
  };
443
482
  }
444
483
 
@@ -463,8 +502,8 @@ function parseReplacementCleanupReceipt(bytes: Uint8Array): ReplacementCleanupRe
463
502
 
464
503
  function serializeReplacementIdentity(identity: ManagedFileSnapshot["identity"]): SerializedReplacementIdentity {
465
504
  return {
466
- dev: identity.dev.toString(),
467
- ino: identity.ino.toString(),
505
+ dev: canonicalFileId(identity.dev).toString(),
506
+ ino: canonicalFileId(identity.ino).toString(),
468
507
  nlink: identity.nlink.toString(),
469
508
  size: String(identity.size),
470
509
  mtimeNs: identity.mtimeNs.toString(),
@@ -480,7 +519,7 @@ function replacementReceiptPath(
480
519
  ): string {
481
520
  return path.join(
482
521
  baseDir,
483
- `.gjc-replace-cleanup-${predecessor.dev.toString(16)}-${predecessor.ino.toString(16)}-receipt-${receipt.dev.toString(16)}-${receipt.ino.toString(16)}.json`,
522
+ `.gjc-replace-cleanup-${canonicalFileId(predecessor.dev).toString(16)}-${canonicalFileId(predecessor.ino).toString(16)}-receipt-${canonicalFileId(receipt.dev).toString(16)}-${canonicalFileId(receipt.ino).toString(16)}.json`,
484
523
  );
485
524
  }
486
525
 
@@ -488,7 +527,7 @@ function replacementReceiptRetirementName(
488
527
  receipt: { dev: bigint; ino: bigint },
489
528
  predecessor: { dev: bigint; ino: bigint },
490
529
  ): string {
491
- return `.gjc-receipt-remove-${receipt.dev.toString(16)}-${receipt.ino.toString(16)}-${predecessor.dev.toString(16)}-${predecessor.ino.toString(16)}`;
530
+ return `.gjc-receipt-remove-${canonicalFileId(receipt.dev).toString(16)}-${canonicalFileId(receipt.ino).toString(16)}-${canonicalFileId(predecessor.dev).toString(16)}-${canonicalFileId(predecessor.ino).toString(16)}`;
492
531
  }
493
532
 
494
533
  function replacementReceiptPlaceholderRetirementName(
@@ -496,7 +535,7 @@ function replacementReceiptPlaceholderRetirementName(
496
535
  predecessor: { dev: bigint; ino: bigint },
497
536
  receipt: { dev: bigint; ino: bigint },
498
537
  ): string {
499
- return `.gjc-receipt-placeholder-remove-${placeholder.dev.toString(16)}-${placeholder.ino.toString(16)}-${predecessor.dev.toString(16)}-${predecessor.ino.toString(16)}-${receipt.dev.toString(16)}-${receipt.ino.toString(16)}`;
538
+ return `.gjc-receipt-placeholder-remove-${canonicalFileId(placeholder.dev).toString(16)}-${canonicalFileId(placeholder.ino).toString(16)}-${canonicalFileId(predecessor.dev).toString(16)}-${canonicalFileId(predecessor.ino).toString(16)}-${canonicalFileId(receipt.dev).toString(16)}-${canonicalFileId(receipt.ino).toString(16)}`;
500
539
  }
501
540
 
502
541
  /**
@@ -805,7 +844,7 @@ export function managedDirectoryRoot(configuredRoot: string): ManagedDirectoryRo
805
844
  const canonicalPath = fs.realpathSync.native(configuredRoot);
806
845
  const stat = fs.lstatSync(canonicalPath, { bigint: true });
807
846
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error(`Unsafe managed root: ${configuredRoot}`);
808
- return Object.freeze({ canonicalPath, dev: stat.dev, ino: stat.ino });
847
+ return Object.freeze({ canonicalPath, dev: canonicalFileId(stat.dev), ino: canonicalFileId(stat.ino) });
809
848
  }
810
849
 
811
850
  export function retainManagedDirectoryAuthority(
@@ -819,7 +858,7 @@ export function retainManagedDirectoryAuthority(
819
858
  if (process.platform !== "linux") return undefined;
820
859
  const named = fs.lstatSync(resolved, { bigint: true });
821
860
  if (!named.isDirectory() || named.isSymbolicLink()) throw new Error("Managed directory authority is unavailable");
822
- if (expected && (named.dev !== expected.dev || named.ino !== expected.ino))
861
+ if (expected && (canonicalFileId(named.dev) !== expected.dev || canonicalFileId(named.ino) !== expected.ino))
823
862
  throw new Error("Managed directory identity changed before retention");
824
863
  const rootAuthority = nativeSessionStorage().openRecoveryFsRoot(root.canonicalPath);
825
864
  try {
@@ -832,7 +871,11 @@ export function retainManagedDirectoryAuthority(
832
871
  )
833
872
  throw new Error("Managed root authority changed");
834
873
  const relative = path.relative(root.canonicalPath, resolved).split(path.sep).join("/");
835
- return rootAuthority.retainManagedDirectory(relative, named.dev.toString(), named.ino.toString());
874
+ return rootAuthority.retainManagedDirectory(
875
+ relative,
876
+ canonicalFileId(named.dev).toString(),
877
+ canonicalFileId(named.ino).toString(),
878
+ );
836
879
  } finally {
837
880
  rootAuthority.close();
838
881
  }
@@ -852,7 +895,12 @@ function ensureDirectoryComponent(pathname: string): boolean {
852
895
 
853
896
  export function assertManagedDirectoryRoot(root: ManagedDirectoryRoot): void {
854
897
  const named = fs.lstatSync(root.canonicalPath, { bigint: true });
855
- if (!named.isDirectory() || named.isSymbolicLink() || named.dev !== root.dev || named.ino !== root.ino) {
898
+ if (
899
+ !named.isDirectory() ||
900
+ named.isSymbolicLink() ||
901
+ canonicalFileId(named.dev) !== root.dev ||
902
+ canonicalFileId(named.ino) !== root.ino
903
+ ) {
856
904
  throw new Error(`Managed root authority changed: ${root.canonicalPath}`);
857
905
  }
858
906
  }
@@ -932,7 +980,12 @@ function verifyExistingManagedPathSecurity(
932
980
  expected: fs.BigIntStats,
933
981
  ): void {
934
982
  const verified = validateNativeSecurityResult(
935
- nativeSessionStorage().verifyOwnerOnlyPathSecurityExpected(pathname, kind, expected.dev, expected.ino),
983
+ nativeSessionStorage().verifyOwnerOnlyPathSecurityExpected(
984
+ pathname,
985
+ kind,
986
+ canonicalFileId(expected.dev),
987
+ canonicalFileId(expected.ino),
988
+ ),
936
989
  "verify",
937
990
  kind,
938
991
  );
@@ -945,7 +998,12 @@ function secureExistingManagedDirectory(pathname: string, kind: "directory" | "f
945
998
  const safeKind = kind === "directory" ? named.isDirectory() : named.isFile();
946
999
  if (!safeKind || named.isSymbolicLink()) throw new Error(`Unsafe managed ${kind}: ${pathname}`);
947
1000
  const verified = validateNativeSecurityResult(
948
- nativeSessionStorage().verifyOwnerOnlyPathSecurityExpected(pathname, kind, named.dev, named.ino),
1001
+ nativeSessionStorage().verifyOwnerOnlyPathSecurityExpected(
1002
+ pathname,
1003
+ kind,
1004
+ canonicalFileId(named.dev),
1005
+ canonicalFileId(named.ino),
1006
+ ),
949
1007
  "verify",
950
1008
  kind,
951
1009
  );
@@ -953,7 +1011,12 @@ function secureExistingManagedDirectory(pathname: string, kind: "directory" | "f
953
1011
  if (verified.ok) return;
954
1012
  if (verified.code !== "acl_verify_failed") throw securityError(pathname, verified);
955
1013
  const repaired = validateNativeSecurityResult(
956
- nativeSessionStorage().repairOwnerOnlyPathSecurityExpected(pathname, kind, named.dev, named.ino),
1014
+ nativeSessionStorage().repairOwnerOnlyPathSecurityExpected(
1015
+ pathname,
1016
+ kind,
1017
+ canonicalFileId(named.dev),
1018
+ canonicalFileId(named.ino),
1019
+ ),
957
1020
  "verify",
958
1021
  kind,
959
1022
  );
@@ -1015,8 +1078,8 @@ export class ManagedSessionDescendantStore {
1015
1078
  throw new Error(rootIdentity.code ?? "Managed subtree identity unavailable");
1016
1079
  this.#subtreeRoot = Object.freeze({
1017
1080
  canonicalPath: this.#baseDir,
1018
- dev: BigInt(rootIdentity.identity.dev),
1019
- ino: BigInt(rootIdentity.identity.ino),
1081
+ dev: canonicalFileId(BigInt(rootIdentity.identity.dev)),
1082
+ ino: canonicalFileId(BigInt(rootIdentity.identity.ino)),
1020
1083
  });
1021
1084
  } else {
1022
1085
  const captured = retained.authority.snapshotManagedTree(relative);
@@ -1024,8 +1087,8 @@ export class ManagedSessionDescendantStore {
1024
1087
  throw new Error(captured.code ?? "Managed subtree identity unavailable");
1025
1088
  this.#subtreeRoot = Object.freeze({
1026
1089
  canonicalPath: this.#baseDir,
1027
- dev: BigInt(captured.snapshot.rootDev),
1028
- ino: BigInt(captured.snapshot.rootIno),
1090
+ dev: canonicalFileId(BigInt(captured.snapshot.rootDev)),
1091
+ ino: canonicalFileId(BigInt(captured.snapshot.rootIno)),
1029
1092
  });
1030
1093
  }
1031
1094
  this.#authority = retained.authority;
@@ -1036,7 +1099,11 @@ export class ManagedSessionDescendantStore {
1036
1099
  assertManagedDirectoryRoot(root);
1037
1100
  ensureManagedDirectory(this.#baseDir, root, this.#policy);
1038
1101
  const subtreeStat = fs.lstatSync(this.#baseDir, { bigint: true });
1039
- this.#subtreeRoot = Object.freeze({ canonicalPath: this.#baseDir, dev: subtreeStat.dev, ino: subtreeStat.ino });
1102
+ this.#subtreeRoot = Object.freeze({
1103
+ canonicalPath: this.#baseDir,
1104
+ dev: canonicalFileId(subtreeStat.dev),
1105
+ ino: canonicalFileId(subtreeStat.ino),
1106
+ });
1040
1107
  if (process.platform === "linux") {
1041
1108
  const before = fs.lstatSync(this.#baseDir, { bigint: true });
1042
1109
  const authority = nativeSessionStorage().openRecoveryFsRoot(this.#baseDir);
@@ -1044,8 +1111,8 @@ export class ManagedSessionDescendantStore {
1044
1111
  if (
1045
1112
  !retained.ok ||
1046
1113
  !retained.identity ||
1047
- retained.identity.dev !== before.dev.toString() ||
1048
- retained.identity.ino !== before.ino.toString()
1114
+ retained.identity.dev !== canonicalFileId(before.dev).toString() ||
1115
+ retained.identity.ino !== canonicalFileId(before.ino).toString()
1049
1116
  ) {
1050
1117
  authority.close();
1051
1118
  throw new Error("Managed descendant root identity changed");
@@ -1150,7 +1217,11 @@ export class ManagedSessionDescendantStore {
1150
1217
  #assertBound(): void {
1151
1218
  if (!this.#authority) {
1152
1219
  const named = fs.statSync(this.#baseDir, { bigint: true });
1153
- if (!named.isDirectory() || named.dev !== this.#subtreeRoot.dev || named.ino !== this.#subtreeRoot.ino)
1220
+ if (
1221
+ !named.isDirectory() ||
1222
+ canonicalFileId(named.dev) !== this.#subtreeRoot.dev ||
1223
+ canonicalFileId(named.ino) !== this.#subtreeRoot.ino
1224
+ )
1154
1225
  throw new Error("Managed descendant root binding changed");
1155
1226
  return;
1156
1227
  }
@@ -1170,10 +1241,10 @@ export class ManagedSessionDescendantStore {
1170
1241
  !retained.identity ||
1171
1242
  !named.isDirectory() ||
1172
1243
  named.isSymbolicLink() ||
1173
- retained.identity.dev !== named.dev.toString() ||
1244
+ retained.identity.dev !== canonicalFileId(named.dev).toString() ||
1174
1245
  retained.identity.dev !== this.#subtreeRoot.dev.toString() ||
1175
1246
  retained.identity.ino !== this.#subtreeRoot.ino.toString() ||
1176
- retained.identity.ino !== named.ino.toString()
1247
+ retained.identity.ino !== canonicalFileId(named.ino).toString()
1177
1248
  ) {
1178
1249
  throw new Error("Managed descendant root binding changed");
1179
1250
  }
@@ -1283,7 +1354,7 @@ export class ManagedSessionDescendantStore {
1283
1354
  const parsed = parseLegacyReplacementCleanupReceipt(receipt.bytes);
1284
1355
  const expectedPredecessor = path.join(
1285
1356
  this.#baseDir,
1286
- `.gjc-exact-replace-destination-${binding.dev.toString(16)}-${binding.ino.toString(16)}`,
1357
+ `.gjc-exact-replace-destination-${binding.encodedDev}-${binding.encodedIno}`,
1287
1358
  );
1288
1359
  if (
1289
1360
  !parsed ||
@@ -1488,13 +1559,17 @@ export class ManagedSessionDescendantStore {
1488
1559
  this.#assertBound();
1489
1560
  return Object.freeze({
1490
1561
  canonicalPath: this.#resolve(relativePath),
1491
- dev: BigInt(ensured.identity.dev),
1492
- ino: BigInt(ensured.identity.ino),
1562
+ dev: canonicalFileId(BigInt(ensured.identity.dev)),
1563
+ ino: canonicalFileId(BigInt(ensured.identity.ino)),
1493
1564
  });
1494
1565
  }
1495
1566
  ensureManagedDirectory(this.#resolve(relativePath), this.#root, this.#policy);
1496
1567
  const named = fs.lstatSync(this.#resolve(relativePath), { bigint: true });
1497
- return Object.freeze({ canonicalPath: this.#resolve(relativePath), dev: named.dev, ino: named.ino });
1568
+ return Object.freeze({
1569
+ canonicalPath: this.#resolve(relativePath),
1570
+ dev: canonicalFileId(named.dev),
1571
+ ino: canonicalFileId(named.ino),
1572
+ });
1498
1573
  }
1499
1574
 
1500
1575
  async publishNoReplace(relativePath: string, bytes: Uint8Array): Promise<void> {
@@ -1878,7 +1953,7 @@ export class ManagedSessionDescendantStore {
1878
1953
  for (const component of components.slice(0, -1)) {
1879
1954
  current = path.join(current, component);
1880
1955
  const stat = fs.lstatSync(current, { bigint: true });
1881
- if (!stat.isDirectory() || stat.isSymbolicLink() || stat.dev !== this.#subtreeRoot.dev)
1956
+ if (!stat.isDirectory() || stat.isSymbolicLink() || canonicalFileId(stat.dev) !== this.#subtreeRoot.dev)
1882
1957
  throw new Error("Managed descendant path escapes retained store");
1883
1958
  }
1884
1959
  }
@@ -1999,8 +2074,8 @@ export class ManagedSessionDescendantStore {
1999
2074
  if (
2000
2075
  !rootBefore.isDirectory() ||
2001
2076
  rootBefore.isSymbolicLink() ||
2002
- rootBefore.dev !== this.#subtreeRoot.dev ||
2003
- rootBefore.ino !== this.#subtreeRoot.ino
2077
+ canonicalFileId(rootBefore.dev) !== this.#subtreeRoot.dev ||
2078
+ canonicalFileId(rootBefore.ino) !== this.#subtreeRoot.ino
2004
2079
  )
2005
2080
  throw new Error("Managed descendant root binding changed");
2006
2081
  let fd: number | undefined;
@@ -2031,8 +2106,8 @@ export class ManagedSessionDescendantStore {
2031
2106
  throw new Error("Managed descendant root binding changed");
2032
2107
  this.#assertBound();
2033
2108
  return managedAppendReceiptFromIdentity({
2034
- dev: opened.dev,
2035
- ino: opened.ino,
2109
+ dev: canonicalFileId(opened.dev),
2110
+ ino: canonicalFileId(opened.ino),
2036
2111
  nlink: opened.nlink,
2037
2112
  size: Number(opened.size),
2038
2113
  mtimeNs: opened.mtimeNs,
@@ -2076,8 +2151,8 @@ export class ManagedSessionDescendantStore {
2076
2151
  if (
2077
2152
  !rootBefore.isDirectory() ||
2078
2153
  rootBefore.isSymbolicLink() ||
2079
- rootBefore.dev !== this.#subtreeRoot.dev ||
2080
- rootBefore.ino !== this.#subtreeRoot.ino
2154
+ canonicalFileId(rootBefore.dev) !== this.#subtreeRoot.dev ||
2155
+ canonicalFileId(rootBefore.ino) !== this.#subtreeRoot.ino
2081
2156
  )
2082
2157
  throw new Error("Managed descendant root binding changed");
2083
2158
  const resolved = this.#resolve(relativePath);
@@ -2099,8 +2174,8 @@ export class ManagedSessionDescendantStore {
2099
2174
  if (retained?.identity) {
2100
2175
  const expected = retained.identity;
2101
2176
  if (
2102
- before.dev.toString() !== expected.dev ||
2103
- before.ino.toString() !== expected.ino ||
2177
+ canonicalFileId(before.dev).toString() !== expected.dev ||
2178
+ canonicalFileId(before.ino).toString() !== expected.ino ||
2104
2179
  before.nlink.toString() !== expected.nlink ||
2105
2180
  before.size.toString() !== expected.size ||
2106
2181
  before.mtimeNs.toString() !== expected.mtimeNs ||
@@ -2110,8 +2185,8 @@ export class ManagedSessionDescendantStore {
2110
2185
  }
2111
2186
  if (
2112
2187
  expectedDescriptor &&
2113
- (before.dev !== expectedDescriptor.dev ||
2114
- before.ino !== expectedDescriptor.ino ||
2188
+ (canonicalFileId(before.dev) !== expectedDescriptor.dev ||
2189
+ canonicalFileId(before.ino) !== expectedDescriptor.ino ||
2115
2190
  before.nlink !== (expectedDescriptor.nlink ?? before.nlink) ||
2116
2191
  Number(before.size) !== expectedDescriptor.size ||
2117
2192
  before.mtimeNs !== expectedDescriptor.mtimeNs ||
@@ -2154,8 +2229,8 @@ export class ManagedSessionDescendantStore {
2154
2229
  return {
2155
2230
  bytes,
2156
2231
  stat: {
2157
- dev: after.dev,
2158
- ino: after.ino,
2232
+ dev: canonicalFileId(after.dev),
2233
+ ino: canonicalFileId(after.ino),
2159
2234
  nlink: after.nlink,
2160
2235
  size: Number(after.size),
2161
2236
  mtimeNs: after.mtimeNs,
@@ -2194,8 +2269,8 @@ export class ManagedSessionDescendantStore {
2194
2269
  return {
2195
2270
  bytes: Buffer.from(read.data),
2196
2271
  identity: {
2197
- dev: BigInt(read.identity.dev),
2198
- ino: BigInt(read.identity.ino),
2272
+ dev: canonicalFileId(BigInt(read.identity.dev)),
2273
+ ino: canonicalFileId(BigInt(read.identity.ino)),
2199
2274
  nlink: BigInt(read.identity.nlink),
2200
2275
  size: Number(read.identity.size),
2201
2276
  mtimeNs: BigInt(read.identity.mtimeNs),
@@ -2217,8 +2292,8 @@ export class ManagedSessionDescendantStore {
2217
2292
  size: BigInt(expected.identity.size),
2218
2293
  mtimeNs: expected.identity.mtimeNs,
2219
2294
  sha256: expected.identity.sha256,
2220
- parentDev: parent.dev,
2221
- parentIno: parent.ino,
2295
+ parentDev: canonicalFileId(parent.dev),
2296
+ parentIno: canonicalFileId(parent.ino),
2222
2297
  quarantineName: `.gjc-remove-${process.pid}-${randomUUID()}`,
2223
2298
  });
2224
2299
  if (
@@ -2647,8 +2722,8 @@ function bootId(): string | undefined {
2647
2722
 
2648
2723
  function identity(stat: fs.BigIntStats, sha256 = ""): ManagedFileSnapshot["identity"] {
2649
2724
  return {
2650
- dev: stat.dev,
2651
- ino: stat.ino,
2725
+ dev: canonicalFileId(stat.dev),
2726
+ ino: canonicalFileId(stat.ino),
2652
2727
  nlink: stat.nlink,
2653
2728
  size: Number(stat.size),
2654
2729
  mtimeNs: stat.mtimeNs,
@@ -3163,8 +3238,8 @@ function replaceManagedFileGeneratedSync(
3163
3238
  fsyncDirectory(parent);
3164
3239
  receiptCleanup = {
3165
3240
  path: receiptPath,
3166
- parentDev: parentIdentity.dev,
3167
- parentIno: parentIdentity.ino,
3241
+ parentDev: canonicalFileId(parentIdentity.dev),
3242
+ parentIno: canonicalFileId(parentIdentity.ino),
3168
3243
  identity: publishedReceiptIdentity,
3169
3244
  predecessor: expectedDestination,
3170
3245
  };
@@ -3184,8 +3259,8 @@ function replaceManagedFileGeneratedSync(
3184
3259
  dev: successor.dev,
3185
3260
  ino: successor.ino,
3186
3261
  nlink: successor.nlink,
3187
- parentDev: parentIdentity.dev,
3188
- parentIno: parentIdentity.ino,
3262
+ parentDev: canonicalFileId(parentIdentity.dev),
3263
+ parentIno: canonicalFileId(parentIdentity.ino),
3189
3264
  size: BigInt(successor.size),
3190
3265
  mtimeNs: successor.mtimeNs,
3191
3266
  sha256: successor.sha256,
@@ -3194,8 +3269,8 @@ function replaceManagedFileGeneratedSync(
3194
3269
  dev: expectedDestination.dev,
3195
3270
  ino: expectedDestination.ino,
3196
3271
  nlink: expectedDestination.nlink,
3197
- parentDev: parentIdentity.dev,
3198
- parentIno: parentIdentity.ino,
3272
+ parentDev: canonicalFileId(parentIdentity.dev),
3273
+ parentIno: canonicalFileId(parentIdentity.ino),
3199
3274
  size: BigInt(expectedDestination.size),
3200
3275
  mtimeNs: expectedDestination.mtimeNs,
3201
3276
  sha256: expectedDestination.sha256,
@@ -0,0 +1,15 @@
1
+ const sdkRunTokenBrand: unique symbol = Symbol("sdkRunToken");
2
+
3
+ export interface SdkRunCapability {
4
+ readonly [sdkRunTokenBrand]: string;
5
+ }
6
+
7
+ export function createSdkRunCapability(token: string): SdkRunCapability {
8
+ return { [sdkRunTokenBrand]: token };
9
+ }
10
+
11
+ export function readSdkRunCapability(value: unknown): string | undefined {
12
+ if (!value || typeof value !== "object") return undefined;
13
+ const token = (value as Partial<SdkRunCapability>)[sdkRunTokenBrand];
14
+ return typeof token === "string" && token.length > 0 ? token : undefined;
15
+ }
@@ -0,0 +1,2 @@
1
+ export type { SdkRunCapability } from "./sdk-run-capability-internal";
2
+ export { readSdkRunCapability } from "./sdk-run-capability-internal";