@coderifts/agent-guard 11.0.1 → 13.0.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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/cas-adapters/fs-default-wire.d.ts +41 -1
  3. package/dist/cjs/cas-adapters/fs-default-wire.d.ts.map +1 -1
  4. package/dist/cjs/cas-adapters/fs-default-wire.js +47 -9
  5. package/dist/cjs/cas-adapters/fs-default-wire.js.map +1 -1
  6. package/dist/cjs/cas-adapters/fs.d.ts +49 -0
  7. package/dist/cjs/cas-adapters/fs.d.ts.map +1 -1
  8. package/dist/cjs/cas-adapters/fs.js +167 -13
  9. package/dist/cjs/cas-adapters/fs.js.map +1 -1
  10. package/dist/cjs/cas-attestation.d.ts +16 -1
  11. package/dist/cjs/cas-attestation.d.ts.map +1 -1
  12. package/dist/cjs/cas-attestation.js +18 -0
  13. package/dist/cjs/cas-attestation.js.map +1 -1
  14. package/dist/cjs/conditional-write.d.ts +77 -5
  15. package/dist/cjs/conditional-write.d.ts.map +1 -1
  16. package/dist/cjs/conditional-write.js +25 -7
  17. package/dist/cjs/conditional-write.js.map +1 -1
  18. package/dist/cjs/execute-tool-call.d.ts.map +1 -1
  19. package/dist/cjs/execute-tool-call.js +1 -0
  20. package/dist/cjs/execute-tool-call.js.map +1 -1
  21. package/dist/cjs/execution-proof.d.ts +6 -2
  22. package/dist/cjs/execution-proof.d.ts.map +1 -1
  23. package/dist/cjs/execution-proof.js +8 -3
  24. package/dist/cjs/execution-proof.js.map +1 -1
  25. package/dist/cjs/freshness.d.ts +25 -0
  26. package/dist/cjs/freshness.d.ts.map +1 -1
  27. package/dist/cjs/freshness.js +38 -0
  28. package/dist/cjs/freshness.js.map +1 -1
  29. package/dist/cjs/guard.d.ts.map +1 -1
  30. package/dist/cjs/guard.js +13 -8
  31. package/dist/cjs/guard.js.map +1 -1
  32. package/dist/cjs/index.d.ts +6 -4
  33. package/dist/cjs/index.d.ts.map +1 -1
  34. package/dist/cjs/index.js +11 -3
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/tool-registry.d.ts.map +1 -1
  37. package/dist/cjs/tool-registry.js +15 -2
  38. package/dist/cjs/tool-registry.js.map +1 -1
  39. package/dist/esm/cas-adapters/fs-default-wire.d.ts +41 -1
  40. package/dist/esm/cas-adapters/fs-default-wire.d.ts.map +1 -1
  41. package/dist/esm/cas-adapters/fs-default-wire.js +46 -10
  42. package/dist/esm/cas-adapters/fs-default-wire.js.map +1 -1
  43. package/dist/esm/cas-adapters/fs.d.ts +49 -0
  44. package/dist/esm/cas-adapters/fs.d.ts.map +1 -1
  45. package/dist/esm/cas-adapters/fs.js +162 -12
  46. package/dist/esm/cas-adapters/fs.js.map +1 -1
  47. package/dist/esm/cas-attestation.d.ts +16 -1
  48. package/dist/esm/cas-attestation.d.ts.map +1 -1
  49. package/dist/esm/cas-attestation.js +18 -0
  50. package/dist/esm/cas-attestation.js.map +1 -1
  51. package/dist/esm/conditional-write.d.ts +77 -5
  52. package/dist/esm/conditional-write.d.ts.map +1 -1
  53. package/dist/esm/conditional-write.js +25 -7
  54. package/dist/esm/conditional-write.js.map +1 -1
  55. package/dist/esm/execute-tool-call.d.ts.map +1 -1
  56. package/dist/esm/execute-tool-call.js +1 -0
  57. package/dist/esm/execute-tool-call.js.map +1 -1
  58. package/dist/esm/execution-proof.d.ts +6 -2
  59. package/dist/esm/execution-proof.d.ts.map +1 -1
  60. package/dist/esm/execution-proof.js +8 -3
  61. package/dist/esm/execution-proof.js.map +1 -1
  62. package/dist/esm/freshness.d.ts +25 -0
  63. package/dist/esm/freshness.d.ts.map +1 -1
  64. package/dist/esm/freshness.js +37 -0
  65. package/dist/esm/freshness.js.map +1 -1
  66. package/dist/esm/guard.d.ts.map +1 -1
  67. package/dist/esm/guard.js +14 -9
  68. package/dist/esm/guard.js.map +1 -1
  69. package/dist/esm/index.d.ts +6 -4
  70. package/dist/esm/index.d.ts.map +1 -1
  71. package/dist/esm/index.js +3 -3
  72. package/dist/esm/index.js.map +1 -1
  73. package/dist/esm/tool-registry.d.ts.map +1 -1
  74. package/dist/esm/tool-registry.js +16 -3
  75. package/dist/esm/tool-registry.js.map +1 -1
  76. package/package.json +1 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CodeRifts
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,4 +1,6 @@
1
1
  import type { ExecuteIfUnchangedOutcome } from '../conditional-write.js';
2
+ import type { VersionToken } from '../conditional-write.js';
3
+ import type { FsObjectIdentity } from './fs.js';
2
4
  /** Unambiguous fs target: a non-empty `path` string on tool arguments (Write convention). */
3
5
  export declare function inferFsPathFromArgs(args: unknown): string | null;
4
6
  /**
@@ -6,10 +8,48 @@ export declare function inferFsPathFromArgs(args: unknown): string | null;
6
8
  * is not an Edit (no old_string/new_string). Fragments are not a whole-file CAS.
7
9
  */
8
10
  export declare function inferFullFileWriteContent(args: unknown): string | null;
11
+ /**
12
+ * Options carrying the AUTHORIZATION-TIME measurement into the write.
13
+ */
14
+ export type FsCasWireOptions = {
15
+ /**
16
+ * Token of the state the AUTHORIZATION examined, measured at T1 by the runner — before preflight,
17
+ * not at write time. This is the whole fix: see the note on wrapWriteWithFsCas.
18
+ */
19
+ expected_token?: VersionToken | null;
20
+ /** Identity (dev+ino) of that same T1 object. Catches an inode swap the token cannot see. */
21
+ expected_identity?: FsObjectIdentity | null;
22
+ };
23
+ /** The pair a runner measures at T1. Both halves describe the SAME observation. */
24
+ export type FsAuthorizationMeasurement = {
25
+ expected_token: VersionToken;
26
+ expected_identity: FsObjectIdentity | null;
27
+ };
9
28
  /**
10
29
  * Wire the FS adapter when path + full-file contents are both present.
11
30
  * Otherwise run the host write unchanged. If the host already returned an
12
31
  * ExecuteIfUnchangedOutcome, pass it through (do not wrap again).
32
+ *
33
+ * THE TOKEN MUST COME FROM T1. This function used to call createFsVersionToken HERE, inside
34
+ * executeFactory — after preflight (T1) and after the T2 recheck — and then condition the write on
35
+ * that self-fetched value. Measured on 12.0.0: with a writer landing between T2 and that read, the
36
+ * CAS adopted the interfering state as its legitimate starting point and still reported
37
+ * status:'committed' with enforced:true. It was a real check of a vacuous window — it asked whether
38
+ * the state had changed since a read a microsecond earlier, which is a question whose answer is
39
+ * always no and which says nothing about the state the authorization actually examined.
40
+ *
41
+ * Now the expected token is supplied by the caller from the T1 measurement. When it is ABSENT we do
42
+ * NOT fall back to self-fetching, because that is precisely the vacuous claim: the write runs
43
+ * unwrapped and the guarantee is NON_ATOMIC, which is the honest thing to report and which
44
+ * requireConditionalWrite will refuse. A weaker guarantee is reportable; a false one is not.
45
+ */
46
+ export declare function wrapWriteWithFsCas<T>(args: unknown, write: () => Promise<T> | T, opts?: FsCasWireOptions): Promise<T | ExecuteIfUnchangedOutcome<unknown>>;
47
+ /**
48
+ * The T1 measurement itself — call this in the RUNNER, before preflight, and pass the result to
49
+ * wrapWriteWithFsCas. Returns null when this call is not an inferable full-file FS write, or when
50
+ * the path is unsafe / unreadable (a token we could not measure is not a token we may claim).
13
51
  */
14
- export declare function wrapWriteWithFsCas<T>(args: unknown, write: () => Promise<T> | T): Promise<T | ExecuteIfUnchangedOutcome<unknown>>;
52
+ export declare function measureFsAuthorization(args: unknown): Promise<FsAuthorizationMeasurement | null>;
53
+ /** Convenience: the token half only. */
54
+ export declare function measureFsAuthorizationToken(args: unknown): Promise<VersionToken | null>;
15
55
  //# sourceMappingURL=fs-default-wire.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fs-default-wire.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGzE,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC1B,OAAO,CAAC,CAAC,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAmBjD"}
1
+ {"version":3,"file":"fs-default-wire.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGhD,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,cAAc,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC7C,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,0BAA0B,GAAG;IACvC,cAAc,EAAE,YAAY,CAAC;IAC7B,iBAAiB,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC3B,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,CAAC,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAgBjD;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAetG;AAED,wCAAwC;AACxC,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAG7F"}
@@ -15,6 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.inferFsPathFromArgs = inferFsPathFromArgs;
16
16
  exports.inferFullFileWriteContent = inferFullFileWriteContent;
17
17
  exports.wrapWriteWithFsCas = wrapWriteWithFsCas;
18
+ exports.measureFsAuthorization = measureFsAuthorization;
19
+ exports.measureFsAuthorizationToken = measureFsAuthorizationToken;
18
20
  const cas_attestation_js_1 = require("../cas-attestation.js");
19
21
  const fs_js_1 = require("./fs.js");
20
22
  /** Unambiguous fs target: a non-empty `path` string on tool arguments (Write convention). */
@@ -50,22 +52,30 @@ function inferFullFileWriteContent(args) {
50
52
  * Wire the FS adapter when path + full-file contents are both present.
51
53
  * Otherwise run the host write unchanged. If the host already returned an
52
54
  * ExecuteIfUnchangedOutcome, pass it through (do not wrap again).
55
+ *
56
+ * THE TOKEN MUST COME FROM T1. This function used to call createFsVersionToken HERE, inside
57
+ * executeFactory — after preflight (T1) and after the T2 recheck — and then condition the write on
58
+ * that self-fetched value. Measured on 12.0.0: with a writer landing between T2 and that read, the
59
+ * CAS adopted the interfering state as its legitimate starting point and still reported
60
+ * status:'committed' with enforced:true. It was a real check of a vacuous window — it asked whether
61
+ * the state had changed since a read a microsecond earlier, which is a question whose answer is
62
+ * always no and which says nothing about the state the authorization actually examined.
63
+ *
64
+ * Now the expected token is supplied by the caller from the T1 measurement. When it is ABSENT we do
65
+ * NOT fall back to self-fetching, because that is precisely the vacuous claim: the write runs
66
+ * unwrapped and the guarantee is NON_ATOMIC, which is the honest thing to report and which
67
+ * requireConditionalWrite will refuse. A weaker guarantee is reportable; a false one is not.
53
68
  */
54
- async function wrapWriteWithFsCas(args, write) {
69
+ async function wrapWriteWithFsCas(args, write, opts) {
55
70
  const filePath = inferFsPathFromArgs(args);
56
71
  const content = inferFullFileWriteContent(args);
57
- if (filePath && content != null) {
58
- let expected;
59
- try {
60
- expected = await (0, fs_js_1.createFsVersionToken)(filePath);
61
- }
62
- catch {
63
- return write();
64
- }
72
+ const expected = opts && typeof opts.expected_token === 'string' ? opts.expected_token : null;
73
+ if (filePath && content != null && expected !== null) {
65
74
  return (0, fs_js_1.writeFileIfUnchanged)({
66
75
  path: filePath,
67
76
  expected_token: expected,
68
77
  content,
78
+ ...(opts && 'expected_identity' in opts ? { expected_identity: opts.expected_identity ?? null } : {}),
69
79
  });
70
80
  }
71
81
  const raw = await write();
@@ -73,4 +83,32 @@ async function wrapWriteWithFsCas(args, write) {
73
83
  return raw;
74
84
  return raw;
75
85
  }
86
+ /**
87
+ * The T1 measurement itself — call this in the RUNNER, before preflight, and pass the result to
88
+ * wrapWriteWithFsCas. Returns null when this call is not an inferable full-file FS write, or when
89
+ * the path is unsafe / unreadable (a token we could not measure is not a token we may claim).
90
+ */
91
+ async function measureFsAuthorization(args) {
92
+ const filePath = inferFsPathFromArgs(args);
93
+ const content = inferFullFileWriteContent(args);
94
+ if (!filePath || content == null)
95
+ return null;
96
+ try {
97
+ const target = await (0, fs_js_1.assertSafeCasPath)(filePath);
98
+ // Identity FIRST, then the token: both must describe the same observation, and taking identity
99
+ // first means a swap between them makes the token the newer of the two, which the pre-rename
100
+ // re-check then catches rather than silently accepting.
101
+ const expected_identity = await (0, fs_js_1.fsObjectIdentity)(target);
102
+ const expected_token = await (0, fs_js_1.createFsVersionToken)(target);
103
+ return { expected_token, expected_identity };
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /** Convenience: the token half only. */
110
+ async function measureFsAuthorizationToken(args) {
111
+ const m = await measureFsAuthorization(args);
112
+ return m ? m.expected_token : null;
113
+ }
76
114
  //# sourceMappingURL=fs-default-wire.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fs-default-wire.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,CAAC;;AAOb,kDAQC;AAMD,8DAQC;AAOD,gDAsBC;AAxDD,8DAAoE;AAEpE,mCAAqE;AAErE,6FAA6F;AAC7F,SAAgB,mBAAmB,CAAC,IAAa;IAC/C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAI,IAA2B,CAAC,IAAI,CAAC;IAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAgB,yBAAyB,CAAC,IAAa;IACrD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,QAAQ,CAAC;IACtD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAClF,OAAO,CAAC,CAAC,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,kBAAkB,CACtC,IAAa,EACb,KAA2B;IAE3B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,QAAQ,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QAChC,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAA,4BAAoB,EAAC,QAAQ,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,OAAO,IAAA,4BAAoB,EAAC;YAC1B,IAAI,EAAE,QAAQ;YACd,cAAc,EAAE,QAAQ;YACxB,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,KAAK,EAAE,CAAC;IAC1B,IAAI,IAAA,gDAA2B,EAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACjD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"fs-default-wire.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,CAAC;;AASb,kDAQC;AAMD,8DAQC;AAuCD,gDAoBC;AAOD,wDAeC;AAGD,kEAGC;AApHD,8DAAoE;AAIpE,mCAA0G;AAE1G,6FAA6F;AAC7F,SAAgB,mBAAmB,CAAC,IAAa;IAC/C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAI,IAA2B,CAAC,IAAI,CAAC;IAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAgB,yBAAyB,CAAC,IAAa;IACrD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,QAAQ,CAAC;IACtD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAClF,OAAO,CAAC,CAAC,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAqBD;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,kBAAkB,CACtC,IAAa,EACb,KAA2B,EAC3B,IAAuB;IAEvB,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;IAE9F,IAAI,QAAQ,IAAI,OAAO,IAAI,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACrD,OAAO,IAAA,4BAAoB,EAAC;YAC1B,IAAI,EAAE,QAAQ;YACd,cAAc,EAAE,QAAQ;YACxB,OAAO;YACP,GAAG,CAAC,IAAI,IAAI,mBAAmB,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtG,CAAC,CAAC;IACL,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,KAAK,EAAE,CAAC;IAC1B,IAAI,IAAA,gDAA2B,EAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACjD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,sBAAsB,CAAC,IAAa;IACxD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,QAAQ,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,QAAQ,CAAC,CAAC;QACjD,+FAA+F;QAC/F,6FAA6F;QAC7F,wDAAwD;QACxD,MAAM,iBAAiB,GAAG,MAAM,IAAA,wBAAgB,EAAC,MAAM,CAAC,CAAC;QACzD,MAAM,cAAc,GAAG,MAAM,IAAA,4BAAoB,EAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,wCAAwC;AACjC,KAAK,UAAU,2BAA2B,CAAC,IAAa;IAC7D,MAAM,CAAC,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC"}
@@ -21,6 +21,46 @@ export declare const FS_VERSION_TOKEN_PREFIX = "fs:v1:";
21
21
  export declare const FS_ABSENT_TOKEN: VersionToken;
22
22
  /** Extract content-hash segment from an fs:v1 token (null if absent/malformed). */
23
23
  export declare function fsTokenContentHash(token: VersionToken | null | undefined): string | null;
24
+ /**
25
+ * Refuse a path this adapter must not treat as a CAS target.
26
+ *
27
+ * MEASURED ABSENT before this existed (2026-08-27): `..` was silently resolved and the write landed
28
+ * outside the intended directory, and a symlinked target was followed by stat/readFile while the
29
+ * rename replaced the LINK — so the check and the write referred to DIFFERENT objects. That object
30
+ * mismatch is the other half of the TOCTOU: a path interpreted twice can have a symlink swapped in
31
+ * between the two interpretations.
32
+ *
33
+ * Three refusals, each for a different escape:
34
+ * - a literal `..` segment in the caller's own path (traversal, judged BEFORE resolution),
35
+ * - a symlinked final component (lstat — the object we would check is not the one we would write).
36
+ *
37
+ * The parent directory is CANONICALISED rather than rejected. Requiring realpath(dir) === dir was
38
+ * written first and measured wrong within the hour: on macOS /var is itself a symlink, so every
39
+ * path under a normal temp directory was refused. A symlinked ancestor is not the danger — an
40
+ * ancestor that CHANGES between the check and the write is, and that is closed by the inode/device
41
+ * identity re-check in writeFileIfUnchanged, not by refusing ordinary filesystems. Resolving the
42
+ * parent once and building the target from it means both the check and the write name one object.
43
+ */
44
+ export declare class UnsafeCasPathError extends Error {
45
+ readonly reason: 'path_traversal' | 'symlink_target';
46
+ readonly casPath: string;
47
+ constructor(reason: 'path_traversal' | 'symlink_target', casPath: string);
48
+ }
49
+ export declare function assertSafeCasPath(filePath: string): Promise<string>;
50
+ /** Filesystem identity of the object a token was measured on. Evidence, not a comparison key. */
51
+ export type FsObjectIdentity = {
52
+ dev: number;
53
+ ino: number;
54
+ };
55
+ /**
56
+ * Identity of the object CURRENTLY at a path, read through a FILE DESCRIPTOR.
57
+ *
58
+ * Opening once and fstat-ing the handle is what makes this an identity rather than another path
59
+ * interpretation: the fd names the object, so a symlink swapped in afterwards cannot redirect it.
60
+ * null when the path does not exist.
61
+ */
62
+ export declare function fsObjectIdentity(filePath: string): Promise<FsObjectIdentity | null>;
63
+ export declare function identitiesEqual(a: FsObjectIdentity | null | undefined, b: FsObjectIdentity | null | undefined): boolean;
24
64
  /**
25
65
  * Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
26
66
  * Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
@@ -36,6 +76,13 @@ export type WriteFileIfUnchangedArgs = {
36
76
  path: string;
37
77
  /** Token measured before the host decided to write (from createFsVersionToken). */
38
78
  expected_token: VersionToken;
79
+ /**
80
+ * Identity (dev+ino) of the object the AUTHORIZATION examined, from fsObjectIdentity at T1.
81
+ * Optional, and when omitted no identity claim is made. Supplying it is strictly stronger than
82
+ * the token alone: an inode swapped in carrying identical bytes and mtime produces an IDENTICAL
83
+ * token, so content equality cannot see it. null means "absent at T1" (create-if-absent).
84
+ */
85
+ expected_identity?: FsObjectIdentity | null;
39
86
  /** Full new file contents (utf8 string or Buffer). */
40
87
  content: string | Buffer;
41
88
  };
@@ -59,6 +106,8 @@ export declare function writeFileIfUnchanged(args: WriteFileIfUnchangedArgs): Pr
59
106
  path: string;
60
107
  bytes: number;
61
108
  written_content_hash: string;
109
+ checked_identity: FsObjectIdentity | null;
110
+ committed_identity: FsObjectIdentity | null;
62
111
  }>>;
63
112
  /**
64
113
  * Opt-in PriorContentResolver that reads a file path from tool arguments.
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAM5D,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBlF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAOnF;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,cAAc,EAAE,YAAY,CAAC;IAC7B,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAsDnG;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACrE,GAAG,oBAAoB,CAoBvB;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAM5D,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,OAAO,EAAE,MAAM;CAMzE;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCzE;AAED,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAazF;AAED,wBAAgB,eAAe,CAC7B,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACtC,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAGT;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BlF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAOnF;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,cAAc,EAAE,YAAY,CAAC;IAC7B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,kBAAkB,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC7C,CAAC,CAAC,CAgGF;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACrE,GAAG,oBAAoB,CAoBvB;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -45,8 +45,11 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  };
46
46
  })();
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.tokensEqual = exports.FS_ABSENT_TOKEN = exports.FS_VERSION_TOKEN_PREFIX = void 0;
48
+ exports.tokensEqual = exports.UnsafeCasPathError = exports.FS_ABSENT_TOKEN = exports.FS_VERSION_TOKEN_PREFIX = void 0;
49
49
  exports.fsTokenContentHash = fsTokenContentHash;
50
+ exports.assertSafeCasPath = assertSafeCasPath;
51
+ exports.fsObjectIdentity = fsObjectIdentity;
52
+ exports.identitiesEqual = identitiesEqual;
50
53
  exports.createFsVersionToken = createFsVersionToken;
51
54
  exports.readVersionedFile = readVersionedFile;
52
55
  exports.writeFileIfUnchanged = writeFileIfUnchanged;
@@ -79,15 +82,118 @@ function fsTokenContentHash(token) {
79
82
  const hash = rest.slice(colon + 1);
80
83
  return /^[a-f0-9]{64}$/.test(hash) ? hash : null;
81
84
  }
85
+ /**
86
+ * Refuse a path this adapter must not treat as a CAS target.
87
+ *
88
+ * MEASURED ABSENT before this existed (2026-08-27): `..` was silently resolved and the write landed
89
+ * outside the intended directory, and a symlinked target was followed by stat/readFile while the
90
+ * rename replaced the LINK — so the check and the write referred to DIFFERENT objects. That object
91
+ * mismatch is the other half of the TOCTOU: a path interpreted twice can have a symlink swapped in
92
+ * between the two interpretations.
93
+ *
94
+ * Three refusals, each for a different escape:
95
+ * - a literal `..` segment in the caller's own path (traversal, judged BEFORE resolution),
96
+ * - a symlinked final component (lstat — the object we would check is not the one we would write).
97
+ *
98
+ * The parent directory is CANONICALISED rather than rejected. Requiring realpath(dir) === dir was
99
+ * written first and measured wrong within the hour: on macOS /var is itself a symlink, so every
100
+ * path under a normal temp directory was refused. A symlinked ancestor is not the danger — an
101
+ * ancestor that CHANGES between the check and the write is, and that is closed by the inode/device
102
+ * identity re-check in writeFileIfUnchanged, not by refusing ordinary filesystems. Resolving the
103
+ * parent once and building the target from it means both the check and the write name one object.
104
+ */
105
+ class UnsafeCasPathError extends Error {
106
+ reason;
107
+ casPath;
108
+ constructor(reason, casPath) {
109
+ super(`unsafe CAS path (${reason}): ${casPath}`);
110
+ this.name = 'UnsafeCasPathError';
111
+ this.reason = reason;
112
+ this.casPath = casPath;
113
+ }
114
+ }
115
+ exports.UnsafeCasPathError = UnsafeCasPathError;
116
+ async function assertSafeCasPath(filePath) {
117
+ // 1. Traversal is judged on what the CALLER wrote, not on the resolved form — resolution is
118
+ // exactly what makes `sub/../escaped` look innocent.
119
+ const segments = filePath.split(/[\\/]+/);
120
+ if (segments.includes('..'))
121
+ throw new UnsafeCasPathError('path_traversal', filePath);
122
+ const resolved = path.resolve(filePath);
123
+ // 2. Canonicalise the PARENT once, then rebuild the target from it, so every later use names the
124
+ // same directory object. realpath on the parent only — never on the target itself, because
125
+ // that would follow exactly the final-component symlink step 3 exists to refuse.
126
+ const dir = path.dirname(resolved);
127
+ let canonicalDir = dir;
128
+ try {
129
+ canonicalDir = await node_fs_1.promises.realpath(dir);
130
+ }
131
+ catch (err) {
132
+ const code = err && typeof err === 'object' && 'code' in err ? String(err.code) : '';
133
+ if (code !== 'ENOENT')
134
+ throw err;
135
+ // Parent does not exist yet — a create under mkdir -p. Keep the resolved form.
136
+ }
137
+ const target = path.join(canonicalDir, path.basename(resolved));
138
+ // 3. The final component must not be a symlink. lstat does NOT follow; stat would, which is the
139
+ // whole bug: stat/readFile measured the TARGET while rename replaced the LINK, so the check
140
+ // and the write referred to different objects. ENOENT is fine — create-if-absent is legitimate.
141
+ try {
142
+ const lst = await node_fs_1.promises.lstat(target);
143
+ if (lst.isSymbolicLink())
144
+ throw new UnsafeCasPathError('symlink_target', filePath);
145
+ }
146
+ catch (err) {
147
+ if (err instanceof UnsafeCasPathError)
148
+ throw err;
149
+ const code = err && typeof err === 'object' && 'code' in err ? String(err.code) : '';
150
+ if (code !== 'ENOENT')
151
+ throw err;
152
+ }
153
+ return target;
154
+ }
155
+ /**
156
+ * Identity of the object CURRENTLY at a path, read through a FILE DESCRIPTOR.
157
+ *
158
+ * Opening once and fstat-ing the handle is what makes this an identity rather than another path
159
+ * interpretation: the fd names the object, so a symlink swapped in afterwards cannot redirect it.
160
+ * null when the path does not exist.
161
+ */
162
+ async function fsObjectIdentity(filePath) {
163
+ let fh;
164
+ try {
165
+ fh = await node_fs_1.promises.open(filePath, 'r');
166
+ const st = await fh.stat();
167
+ return { dev: st.dev, ino: st.ino };
168
+ }
169
+ catch (err) {
170
+ const code = err && typeof err === 'object' && 'code' in err ? String(err.code) : '';
171
+ if (code === 'ENOENT')
172
+ return null;
173
+ throw err;
174
+ }
175
+ finally {
176
+ if (fh)
177
+ await fh.close().catch(() => { });
178
+ }
179
+ }
180
+ function identitiesEqual(a, b) {
181
+ if (!a || !b)
182
+ return false;
183
+ return a.dev === b.dev && a.ino === b.ino;
184
+ }
82
185
  /**
83
186
  * Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
84
187
  * Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
85
188
  * so hosts can CAS create vs create-raced.
86
189
  */
87
190
  async function createFsVersionToken(filePath) {
88
- let st;
191
+ // ONE open, then stat AND read from the same handle. Previously this was stat(path) followed by
192
+ // readFile(path) — two interpretations of the same string, so a swap between them produced a
193
+ // token describing neither object exactly. The fd names the object for both reads.
194
+ let fh;
89
195
  try {
90
- st = await node_fs_1.promises.stat(filePath);
196
+ fh = await node_fs_1.promises.open(filePath, 'r');
91
197
  }
92
198
  catch (err) {
93
199
  const code = err && typeof err === 'object' && 'code' in err
@@ -97,14 +203,20 @@ async function createFsVersionToken(filePath) {
97
203
  return exports.FS_ABSENT_TOKEN;
98
204
  throw err;
99
205
  }
100
- if (!st.isFile()) {
101
- // Directories / specials are not content CAS targets for this adapter.
102
- throw new Error(`createFsVersionToken: not a regular file: ${filePath}`);
206
+ try {
207
+ const st = await fh.stat();
208
+ if (!st.isFile()) {
209
+ // Directories / specials are not content CAS targets for this adapter.
210
+ throw new Error(`createFsVersionToken: not a regular file: ${filePath}`);
211
+ }
212
+ const buf = await fh.readFile();
213
+ const hash = sha256hex(buf);
214
+ const mtimeMs = Math.trunc(st.mtimeMs);
215
+ return `${exports.FS_VERSION_TOKEN_PREFIX}${mtimeMs}:${hash}`;
216
+ }
217
+ finally {
218
+ await fh.close().catch(() => { });
103
219
  }
104
- const buf = await node_fs_1.promises.readFile(filePath);
105
- const hash = sha256hex(buf);
106
- const mtimeMs = Math.trunc(st.mtimeMs);
107
- return `${exports.FS_VERSION_TOKEN_PREFIX}${mtimeMs}:${hash}`;
108
220
  }
109
221
  /**
110
222
  * Read file content + version token for VersionedContent (host reporting / preflight prior).
@@ -135,9 +247,29 @@ async function readVersionedFile(filePath) {
135
247
  * Wired through executeIfUnchanged — not a parallel path.
136
248
  */
137
249
  async function writeFileIfUnchanged(args) {
138
- const target = path.resolve(args.path);
250
+ // Refuse traversal / symlink / mounted-over-parent BEFORE any token is measured. A CAS over an
251
+ // object the caller did not name is not a weaker guarantee, it is a wrong one.
252
+ const target = await assertSafeCasPath(args.path);
139
253
  const body = typeof args.content === 'string' ? Buffer.from(args.content, 'utf8') : args.content;
140
254
  const writtenHash = sha256hex(body);
255
+ // Identity of the object the CHECK is about, captured through an fd (null when creating).
256
+ const checkedIdentity = await fsObjectIdentity(target);
257
+ // T1 identity claim: if the caller measured one at authorization time, the object must still be
258
+ // that object BEFORE we do anything. Captured-here identity cannot see a swap that predates this
259
+ // call, which is exactly the case the token also cannot see.
260
+ const hasT1Identity = Object.prototype.hasOwnProperty.call(args, 'expected_identity');
261
+ if (hasT1Identity) {
262
+ const t1 = args.expected_identity ?? null;
263
+ const held = t1 === null ? checkedIdentity === null : identitiesEqual(t1, checkedIdentity);
264
+ if (!held) {
265
+ return {
266
+ status: 'refused',
267
+ reason: 'stale_version_token',
268
+ expected_token: args.expected_token,
269
+ current_token: await createFsVersionToken(target).catch(() => null),
270
+ };
271
+ }
272
+ }
141
273
  return (0, conditional_write_js_1.executeIfUnchanged)({
142
274
  expected_token: args.expected_token,
143
275
  current_token: () => createFsVersionToken(target),
@@ -160,7 +292,22 @@ async function writeFileIfUnchanged(args) {
160
292
  const tmp = path.join(dir, `.coderifts-cas-${path.basename(target)}-${process.pid}-${(0, node_crypto_1.randomBytes)(6).toString('hex')}.tmp`);
161
293
  try {
162
294
  await node_fs_1.promises.writeFile(tmp, body);
163
- // Close pre-rename window: if target moved since the executeIfUnchanged check, do not clobber.
295
+ // Close pre-rename window on BOTH facts. Content equality alone cannot see an object swap:
296
+ // a symlink or a replaced inode with identical bytes passes a token check and is still a
297
+ // different object. Identity is checked first because it is the stronger statement.
298
+ const stillIdentity = await fsObjectIdentity(target);
299
+ const identityHeld = checkedIdentity === null
300
+ ? stillIdentity === null // create-if-absent: nothing may have appeared
301
+ : identitiesEqual(checkedIdentity, stillIdentity);
302
+ if (!identityHeld) {
303
+ try {
304
+ await node_fs_1.promises.unlink(tmp);
305
+ }
306
+ catch {
307
+ /* ignore */
308
+ }
309
+ throw new conditional_write_js_1.StaleVersionTokenAbort(await createFsVersionToken(target).catch(() => null));
310
+ }
164
311
  const still = await createFsVersionToken(target);
165
312
  if (!(0, conditional_write_js_1.tokensEqual)(args.expected_token, still)) {
166
313
  try {
@@ -186,7 +333,14 @@ async function writeFileIfUnchanged(args) {
186
333
  }
187
334
  throw err;
188
335
  }
189
- return { path: target, bytes: body.length, written_content_hash: writtenHash };
336
+ // Evidence: which OBJECT was checked, not merely which path string.
337
+ return {
338
+ path: target,
339
+ bytes: body.length,
340
+ written_content_hash: writtenHash,
341
+ checked_identity: checkedIdentity,
342
+ committed_identity: await fsObjectIdentity(target),
343
+ };
190
344
  },
191
345
  });
192
346
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import type { ExecutorKeyRegistry } from '@coderifts/sdk';
15
15
  import type { GuardExecutionProof, ExecutionResultHash } from './execution-proof.js';
16
- import type { ExecuteIfUnchangedOutcome, VersionToken } from './conditional-write.js';
16
+ import type { ExecuteIfUnchangedOutcome, IndeterminateReason, VersionToken } from './conditional-write.js';
17
17
  /** Machine-readable schema id — mirrors EXECUTION_PROOF_SPEC style. */
18
18
  export declare const CAS_ATTESTATION_SPEC: "cas-attestation.v1";
19
19
  /**
@@ -51,6 +51,16 @@ export type CasAttestationCas = {
51
51
  reason: 'stale_during_commit';
52
52
  expected_token: VersionToken;
53
53
  post_commit_token: VersionToken | null;
54
+ } | {
55
+ status: 'indeterminate';
56
+ /**
57
+ * 'unknown' is a THIRD value, never a default to false. Collapsing it to false would say the
58
+ * write did not run, which is a claim we do not have; collapsing it to true would say it did.
59
+ */
60
+ write_ran: 'unknown';
61
+ reason: IndeterminateReason;
62
+ expected_token: VersionToken;
63
+ observed_token: VersionToken | null;
54
64
  };
55
65
  /**
56
66
  * Frozen cas-attestation.v1 record.
@@ -79,6 +89,11 @@ export type CasAttestation = {
79
89
  stale_during_commit: boolean;
80
90
  /** outcome.status === 'refused'. */
81
91
  refused: boolean;
92
+ /**
93
+ * outcome.status === 'indeterminate'. Downstream MUST block on this: it is not a pass, and it
94
+ * is not a failure either. Reconciliation is required before anything may proceed.
95
+ */
96
+ indeterminate: boolean;
82
97
  };
83
98
  /**
84
99
  * Observation-side CAS evidence class (S2-F2a R3). Never a verdict/preimage field.
@@ -1 +1 @@
1
- {"version":3,"file":"cas-attestation.d.ts","sourceRoot":"","sources":["../../src/cas-attestation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cas-attestation.d.ts","sourceRoot":"","sources":["../../src/cas-attestation.ts"],"names":[],"mappings":"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"}
@@ -195,6 +195,13 @@ function isExecuteIfUnchangedOutcome(x) {
195
195
  const s = x;
196
196
  return s.reason === 'stale_during_commit' && typeof s.expected_token === 'string';
197
197
  }
198
+ if (o.status === 'indeterminate') {
199
+ const i = x;
200
+ return (i.reason === 'response_lost'
201
+ || i.reason === 'ambiguous_provider_reply'
202
+ || i.reason === 'observation_failed')
203
+ && typeof i.expected_token === 'string';
204
+ }
198
205
  return false;
199
206
  }
200
207
  function freezeExecutionResultHash(h) {
@@ -217,6 +224,15 @@ function projectCas(outcome) {
217
224
  current_token: outcome.current_token == null ? null : outcome.current_token,
218
225
  });
219
226
  }
227
+ if (outcome.status === 'indeterminate') {
228
+ return Object.freeze({
229
+ status: 'indeterminate',
230
+ write_ran: 'unknown',
231
+ reason: outcome.reason,
232
+ expected_token: outcome.expected_token,
233
+ observed_token: outcome.observed_token == null ? null : outcome.observed_token,
234
+ });
235
+ }
220
236
  // committed_stale_detected
221
237
  return Object.freeze({
222
238
  status: 'committed_stale_detected',
@@ -246,6 +262,7 @@ function buildCasAttestation(proof, outcome, opts = {}) {
246
262
  const write_ran = cas.write_ran === true;
247
263
  const stale_during_commit = cas.status === 'committed_stale_detected';
248
264
  const refused = cas.status === 'refused';
265
+ const indeterminate = cas.status === 'indeterminate';
249
266
  const cas_evidence = evaluateCasEvidence(outcome, opts);
250
267
  let authorized_and_committed = receipt_verified && cas.status === 'committed';
251
268
  // ENFORCING_STRICT: the existing derived name now requires executor_attested + kernel
@@ -270,6 +287,7 @@ function buildCasAttestation(proof, outcome, opts = {}) {
270
287
  write_ran,
271
288
  stale_during_commit,
272
289
  refused,
290
+ indeterminate,
273
291
  }),
274
292
  cas_evidence,
275
293
  limits: LIMITS,