@coderifts/agent-guard 6.1.0 → 6.3.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 (66) hide show
  1. package/README.md +68 -2
  2. package/dist/cjs/cas-adapters/api.d.ts +94 -0
  3. package/dist/cjs/cas-adapters/api.d.ts.map +1 -0
  4. package/dist/cjs/cas-adapters/api.js +120 -0
  5. package/dist/cjs/cas-adapters/api.js.map +1 -0
  6. package/dist/cjs/cas-adapters/db.d.ts +93 -0
  7. package/dist/cjs/cas-adapters/db.d.ts.map +1 -0
  8. package/dist/cjs/cas-adapters/db.js +139 -0
  9. package/dist/cjs/cas-adapters/db.js.map +1 -0
  10. package/dist/cjs/cas-adapters/fs.d.ts +79 -0
  11. package/dist/cjs/cas-adapters/fs.d.ts.map +1 -0
  12. package/dist/cjs/cas-adapters/fs.js +224 -0
  13. package/dist/cjs/cas-adapters/fs.js.map +1 -0
  14. package/dist/cjs/cas-adapters/registry.d.ts +88 -0
  15. package/dist/cjs/cas-adapters/registry.d.ts.map +1 -0
  16. package/dist/cjs/cas-adapters/registry.js +120 -0
  17. package/dist/cjs/cas-adapters/registry.js.map +1 -0
  18. package/dist/cjs/cas-attestation.d.ts +94 -0
  19. package/dist/cjs/cas-attestation.d.ts.map +1 -0
  20. package/dist/cjs/cas-attestation.js +133 -0
  21. package/dist/cjs/cas-attestation.js.map +1 -0
  22. package/dist/cjs/conditional-write.d.ts +66 -0
  23. package/dist/cjs/conditional-write.d.ts.map +1 -1
  24. package/dist/cjs/conditional-write.js +72 -1
  25. package/dist/cjs/conditional-write.js.map +1 -1
  26. package/dist/cjs/index.d.ts +11 -2
  27. package/dist/cjs/index.d.ts.map +1 -1
  28. package/dist/cjs/index.js +44 -3
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/with-coderifts.d.ts +8 -0
  31. package/dist/cjs/with-coderifts.d.ts.map +1 -1
  32. package/dist/cjs/with-coderifts.js +3 -0
  33. package/dist/cjs/with-coderifts.js.map +1 -1
  34. package/dist/esm/cas-adapters/api.d.ts +94 -0
  35. package/dist/esm/cas-adapters/api.d.ts.map +1 -0
  36. package/dist/esm/cas-adapters/api.js +116 -0
  37. package/dist/esm/cas-adapters/api.js.map +1 -0
  38. package/dist/esm/cas-adapters/db.d.ts +93 -0
  39. package/dist/esm/cas-adapters/db.d.ts.map +1 -0
  40. package/dist/esm/cas-adapters/db.js +135 -0
  41. package/dist/esm/cas-adapters/db.js.map +1 -0
  42. package/dist/esm/cas-adapters/fs.d.ts +79 -0
  43. package/dist/esm/cas-adapters/fs.d.ts.map +1 -0
  44. package/dist/esm/cas-adapters/fs.js +185 -0
  45. package/dist/esm/cas-adapters/fs.js.map +1 -0
  46. package/dist/esm/cas-adapters/registry.d.ts +88 -0
  47. package/dist/esm/cas-adapters/registry.d.ts.map +1 -0
  48. package/dist/esm/cas-adapters/registry.js +116 -0
  49. package/dist/esm/cas-adapters/registry.js.map +1 -0
  50. package/dist/esm/cas-attestation.d.ts +94 -0
  51. package/dist/esm/cas-attestation.d.ts.map +1 -0
  52. package/dist/esm/cas-attestation.js +127 -0
  53. package/dist/esm/cas-attestation.js.map +1 -0
  54. package/dist/esm/conditional-write.d.ts +66 -0
  55. package/dist/esm/conditional-write.d.ts.map +1 -1
  56. package/dist/esm/conditional-write.js +69 -0
  57. package/dist/esm/conditional-write.js.map +1 -1
  58. package/dist/esm/index.d.ts +11 -2
  59. package/dist/esm/index.d.ts.map +1 -1
  60. package/dist/esm/index.js +14 -2
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/with-coderifts.d.ts +8 -0
  63. package/dist/esm/with-coderifts.d.ts.map +1 -1
  64. package/dist/esm/with-coderifts.js +3 -0
  65. package/dist/esm/with-coderifts.js.map +1 -1
  66. package/package.json +1 -1
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Filesystem CAS adapter — mtime + content-hash VersionToken and atomic-rename write.
3
+ *
4
+ * Uses the host contract helper executeIfUnchanged (conditional-write.ts). Does not invent
5
+ * a parallel outcome shape. Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * fs:v1:<mtime_ms>:<sha256_hex_of_file_bytes>
9
+ * Missing file → createFsVersionToken still works for "absent" via a dedicated absent token
10
+ * only if we choose; for create we require the file to exist (ENOENT throws) so hosts
11
+ * measure an existing prior. For write of new files, expected_token may be ABSENT_TOKEN.
12
+ */
13
+ 'use strict';
14
+ import { createHash, randomBytes } from 'node:crypto';
15
+ import { promises as fsp } from 'node:fs';
16
+ import * as path from 'node:path';
17
+ import { executeIfUnchanged, StaleVersionTokenAbort, tokensEqual, } from '../conditional-write.js';
18
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
19
+ export const FS_VERSION_TOKEN_PREFIX = 'fs:v1:';
20
+ /**
21
+ * Token used when the path does not exist yet (create-if-absent CAS).
22
+ * Hosts that measured absence should pass this as expected_token for first write.
23
+ */
24
+ export const FS_ABSENT_TOKEN = 'fs:v1:absent';
25
+ function sha256hex(buf) {
26
+ return createHash('sha256').update(buf).digest('hex');
27
+ }
28
+ /** Extract content-hash segment from an fs:v1 token (null if absent/malformed). */
29
+ export function fsTokenContentHash(token) {
30
+ if (typeof token !== 'string' || !token.startsWith(FS_VERSION_TOKEN_PREFIX))
31
+ return null;
32
+ if (token === FS_ABSENT_TOKEN)
33
+ return null;
34
+ const rest = token.slice(FS_VERSION_TOKEN_PREFIX.length);
35
+ const colon = rest.indexOf(':');
36
+ if (colon < 0)
37
+ return null;
38
+ const hash = rest.slice(colon + 1);
39
+ return /^[a-f0-9]{64}$/.test(hash) ? hash : null;
40
+ }
41
+ /**
42
+ * Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
43
+ * Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
44
+ * so hosts can CAS create vs create-raced.
45
+ */
46
+ export async function createFsVersionToken(filePath) {
47
+ let st;
48
+ try {
49
+ st = await fsp.stat(filePath);
50
+ }
51
+ catch (err) {
52
+ const code = err && typeof err === 'object' && 'code' in err
53
+ ? String(err.code)
54
+ : '';
55
+ if (code === 'ENOENT')
56
+ return FS_ABSENT_TOKEN;
57
+ throw err;
58
+ }
59
+ if (!st.isFile()) {
60
+ // Directories / specials are not content CAS targets for this adapter.
61
+ throw new Error(`createFsVersionToken: not a regular file: ${filePath}`);
62
+ }
63
+ const buf = await fsp.readFile(filePath);
64
+ const hash = sha256hex(buf);
65
+ const mtimeMs = Math.trunc(st.mtimeMs);
66
+ return `${FS_VERSION_TOKEN_PREFIX}${mtimeMs}:${hash}`;
67
+ }
68
+ /**
69
+ * Read file content + version token for VersionedContent (host reporting / preflight prior).
70
+ * ENOENT → content '' and FS_ABSENT_TOKEN.
71
+ */
72
+ export async function readVersionedFile(filePath) {
73
+ const version_token = await createFsVersionToken(filePath);
74
+ if (version_token === FS_ABSENT_TOKEN) {
75
+ return { content: '', version_token };
76
+ }
77
+ const content = await fsp.readFile(filePath, 'utf8');
78
+ return { content, version_token };
79
+ }
80
+ /**
81
+ * Conditional write: re-stat+re-hash; if token matches expected → temp file + atomic rename.
82
+ *
83
+ * MEASURED windows (pre-fix, 2fcd74e-era):
84
+ * 1. executeIfUnchanged: current_token() then await write() — no re-check in between.
85
+ * 2. write(): writeFile(tmp) then rename(tmp,target) with NO re-stat of target before rename
86
+ * (fs.ts former lines 106–108) — concurrent mutation of target is clobbered by rename.
87
+ *
88
+ * Closures:
89
+ * - Pre-rename: re-createFsVersionToken(target); on mismatch abort rename, unlink tmp,
90
+ * throw StaleVersionTokenAbort → refused/stale_version_token (write did not land).
91
+ * - Post-rename: detect_stale_during_commit — content-hash of path must match sha256(body);
92
+ * mismatch → committed_stale_detected (write landed; someone overwrote after rename).
93
+ *
94
+ * Wired through executeIfUnchanged — not a parallel path.
95
+ */
96
+ export async function writeFileIfUnchanged(args) {
97
+ const target = path.resolve(args.path);
98
+ const body = typeof args.content === 'string' ? Buffer.from(args.content, 'utf8') : args.content;
99
+ const writtenHash = sha256hex(body);
100
+ return executeIfUnchanged({
101
+ expected_token: args.expected_token,
102
+ current_token: () => createFsVersionToken(target),
103
+ detect_stale_during_commit: true,
104
+ // Post-commit: token content-hash must equal sha256 of what we wrote (mtime may vary).
105
+ expected_after_commit: async (result) => {
106
+ // Synthesize a comparable token: re-read path; equality is content-hash based via tokensEqual
107
+ // only if full token matches — mtime always changes on write, so we return the LIVE post
108
+ // token only when its hash matches writtenHash; else return a sentinel that cannot match.
109
+ const post = await createFsVersionToken(target);
110
+ const postHash = fsTokenContentHash(post);
111
+ if (postHash === result.written_content_hash)
112
+ return post;
113
+ // Force mismatch → committed_stale_detected (detection, not rollback).
114
+ return `${FS_VERSION_TOKEN_PREFIX}0:stale_during_commit_mismatch`;
115
+ },
116
+ write: async () => {
117
+ const dir = path.dirname(target);
118
+ await fsp.mkdir(dir, { recursive: true });
119
+ const tmp = path.join(dir, `.coderifts-cas-${path.basename(target)}-${process.pid}-${randomBytes(6).toString('hex')}.tmp`);
120
+ try {
121
+ await fsp.writeFile(tmp, body);
122
+ // Close pre-rename window: if target moved since the executeIfUnchanged check, do not clobber.
123
+ const still = await createFsVersionToken(target);
124
+ if (!tokensEqual(args.expected_token, still)) {
125
+ try {
126
+ await fsp.unlink(tmp);
127
+ }
128
+ catch {
129
+ /* ignore */
130
+ }
131
+ throw new StaleVersionTokenAbort(still);
132
+ }
133
+ // Atomic replace on same filesystem (POSIX rename; Windows overwrite where supported).
134
+ await fsp.rename(tmp, target);
135
+ }
136
+ catch (err) {
137
+ if (err instanceof StaleVersionTokenAbort)
138
+ throw err;
139
+ // Best-effort cleanup of temp — never leave partial target from failed rename.
140
+ try {
141
+ await fsp.unlink(tmp);
142
+ }
143
+ catch {
144
+ /* ignore */
145
+ }
146
+ throw err;
147
+ }
148
+ return { path: target, bytes: body.length, written_content_hash: writtenHash };
149
+ },
150
+ });
151
+ }
152
+ /**
153
+ * Opt-in PriorContentResolver that reads a file path from tool arguments.
154
+ * Returns file utf8 content for freshness measurement (string only — tokens travel separately
155
+ * via versioned_content / conditioned_on_token on the call context).
156
+ *
157
+ * Plug-in: pass as GuardConfig.resolvePriorContent / withCodeRifts({ resolvePriorContent }).
158
+ */
159
+ export function createFsPriorContentResolver(options) {
160
+ const pathForArtifact = options?.pathForArtifact;
161
+ return async (req) => {
162
+ let filePath = typeof req.path === 'string' && req.path.length > 0 ? req.path : undefined;
163
+ if (!filePath && pathForArtifact) {
164
+ const mapped = pathForArtifact(req.artifactId);
165
+ if (typeof mapped === 'string' && mapped.length > 0)
166
+ filePath = mapped;
167
+ }
168
+ if (!filePath)
169
+ return null;
170
+ try {
171
+ return await fsp.readFile(filePath, 'utf8');
172
+ }
173
+ catch (err) {
174
+ const code = err && typeof err === 'object' && 'code' in err
175
+ ? String(err.code)
176
+ : '';
177
+ if (code === 'ENOENT')
178
+ return null;
179
+ throw err;
180
+ }
181
+ };
182
+ }
183
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
184
+ export { tokensEqual };
185
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Registry compareAndSwap CAS adapter — opaque registry/version token discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * registry:v1:<host_registry_token>
9
+ * Missing / empty token → REGISTRY_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter never talks to a registry client. The host callback performs
12
+ * compareAndSwap(expected, newValue) (or equivalent) and reports {swapped|conflict}.
13
+ * The adapter only normalizes tokens and maps the report onto ExecuteIfUnchangedOutcome.
14
+ *
15
+ * No registry SDKs — all I/O is host-injected.
16
+ */
17
+ import { tokensEqual, type ExecuteIfUnchangedOutcome, type VersionToken } from '../conditional-write.js';
18
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
19
+ export declare const REGISTRY_VERSION_TOKEN_PREFIX = "registry:v1:";
20
+ /**
21
+ * Token used when the registry key is absent / unversioned.
22
+ * Hosts that measured absence should pass this as expected_token for first put.
23
+ */
24
+ export declare const REGISTRY_ABSENT_TOKEN: VersionToken;
25
+ /**
26
+ * Build a VersionToken from a host registry revision / generation / etag-like value.
27
+ * null / undefined / empty → REGISTRY_ABSENT_TOKEN.
28
+ */
29
+ export declare function createRegistryVersionToken(token: string | number | bigint | null | undefined): VersionToken;
30
+ /** Raw value embedded in a registry:v1: token (for host CAS APIs). Absent → null. */
31
+ export declare function registryTokenRaw(token: VersionToken | null | undefined): string | null;
32
+ /**
33
+ * Host report after compareAndSwap (or equivalent).
34
+ */
35
+ export type RegistryHostCasReport<T = unknown> = {
36
+ swapped: true;
37
+ /** New registry token after successful swap; null/omit when unknown. */
38
+ new_token?: string | number | bigint | null;
39
+ result?: T;
40
+ } | {
41
+ swapped: false;
42
+ /** Current token if observed on conflict; omit → null (no invented token). */
43
+ current_token?: string | number | bigint | null;
44
+ } | {
45
+ status: 'committed';
46
+ new_token?: string | number | bigint | null;
47
+ result?: T;
48
+ } | {
49
+ status: 'conflict';
50
+ current_token?: string | number | bigint | null;
51
+ };
52
+ export type WriteRegistryIfUnchangedArgs<T = unknown> = {
53
+ /** Token measured before the host decided to write (from createRegistryVersionToken). */
54
+ expected_token: VersionToken;
55
+ /** Re-read the registry key's current token. Pre-write and post-write when detect is on. */
56
+ current_token: () => string | number | bigint | null | Promise<string | number | bigint | null>;
57
+ /**
58
+ * Host compareAndSwap. Receives the raw expected token for the CAS call.
59
+ * Adapter does not invoke any registry client.
60
+ */
61
+ compareAndSwap: (ctx: {
62
+ expected: string | null;
63
+ expected_token: VersionToken;
64
+ }) => RegistryHostCasReport<T> | Promise<RegistryHostCasReport<T>>;
65
+ /**
66
+ * Opt-in post-commit detection (default false).
67
+ * When host omits new_token on success, detection is a no-op.
68
+ */
69
+ detect_stale_during_commit?: boolean;
70
+ };
71
+ export type RegistryWriteResult<T = unknown> = {
72
+ new_token: string | null;
73
+ result: T | undefined;
74
+ };
75
+ /**
76
+ * Conditional registry write: re-read token; if matches → host compareAndSwap.
77
+ *
78
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
79
+ * swapped:true / status:committed → committed
80
+ * swapped:false / status:conflict → refused / stale_version_token
81
+ * current_token from host when supplied, else null
82
+ *
83
+ * Wired through executeIfUnchanged — not a parallel path.
84
+ */
85
+ export declare function writeRegistryIfUnchanged<T = unknown>(args: WriteRegistryIfUnchangedArgs<T>): Promise<ExecuteIfUnchangedOutcome<RegistryWriteResult<T>>>;
86
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
87
+ export { tokensEqual };
88
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,6BAA6B,iBAAiB,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAAmC,CAAC;AAExE;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACjD,YAAY,CAMd;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAOtF;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,GAAG,OAAO,IACzC;IACE,OAAO,EAAE,IAAI,CAAC;IACd,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACjD,GACD;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ,GACD;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACjD,CAAC;AAEN,MAAM,MAAM,4BAA4B,CAAC,CAAC,GAAG,OAAO,IAAI;IACtD,yFAAyF;IACzF,cAAc,EAAE,YAAY,CAAC;IAC7B,4FAA4F;IAC5F,aAAa,EAAE,MACX,MAAM,GACN,MAAM,GACN,MAAM,GACN,IAAI,GACJ,OAAO,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C;;;OAGG;IACH,cAAc,EAAE,CAAC,GAAG,EAAE;QACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,cAAc,EAAE,YAAY,CAAC;KAC9B,KAAK,qBAAqB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE;;;OAGG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC7C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;CACvB,CAAC;AA0BF;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAAC,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACpC,OAAO,CAAC,yBAAyB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAsC5D;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Registry compareAndSwap CAS adapter — opaque registry/version token discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * registry:v1:<host_registry_token>
9
+ * Missing / empty token → REGISTRY_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter never talks to a registry client. The host callback performs
12
+ * compareAndSwap(expected, newValue) (or equivalent) and reports {swapped|conflict}.
13
+ * The adapter only normalizes tokens and maps the report onto ExecuteIfUnchangedOutcome.
14
+ *
15
+ * No registry SDKs — all I/O is host-injected.
16
+ */
17
+ 'use strict';
18
+ import { executeIfUnchanged, StaleVersionTokenAbort, tokensEqual, } from '../conditional-write.js';
19
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
20
+ export const REGISTRY_VERSION_TOKEN_PREFIX = 'registry:v1:';
21
+ /**
22
+ * Token used when the registry key is absent / unversioned.
23
+ * Hosts that measured absence should pass this as expected_token for first put.
24
+ */
25
+ export const REGISTRY_ABSENT_TOKEN = 'registry:v1:absent';
26
+ /**
27
+ * Build a VersionToken from a host registry revision / generation / etag-like value.
28
+ * null / undefined / empty → REGISTRY_ABSENT_TOKEN.
29
+ */
30
+ export function createRegistryVersionToken(token) {
31
+ if (token == null)
32
+ return REGISTRY_ABSENT_TOKEN;
33
+ if (typeof token === 'number' && !Number.isFinite(token))
34
+ return REGISTRY_ABSENT_TOKEN;
35
+ const s = String(token).trim();
36
+ if (s.length === 0)
37
+ return REGISTRY_ABSENT_TOKEN;
38
+ return `${REGISTRY_VERSION_TOKEN_PREFIX}${s}`;
39
+ }
40
+ /** Raw value embedded in a registry:v1: token (for host CAS APIs). Absent → null. */
41
+ export function registryTokenRaw(token) {
42
+ if (typeof token !== 'string' || !token.startsWith(REGISTRY_VERSION_TOKEN_PREFIX)) {
43
+ return null;
44
+ }
45
+ if (token === REGISTRY_ABSENT_TOKEN)
46
+ return null;
47
+ const raw = token.slice(REGISTRY_VERSION_TOKEN_PREFIX.length);
48
+ return raw.length > 0 ? raw : null;
49
+ }
50
+ function normalizeRegistryReport(report) {
51
+ if (!report || typeof report !== 'object') {
52
+ throw new Error('writeRegistryIfUnchanged: host compareAndSwap must return RegistryHostCasReport');
53
+ }
54
+ if ('swapped' in report) {
55
+ if (report.swapped === true) {
56
+ return { kind: 'committed', new_token: report.new_token, result: report.result };
57
+ }
58
+ return { kind: 'conflict', current_token: report.current_token };
59
+ }
60
+ if (report.status === 'committed') {
61
+ return { kind: 'committed', new_token: report.new_token, result: report.result };
62
+ }
63
+ if (report.status === 'conflict') {
64
+ return { kind: 'conflict', current_token: report.current_token };
65
+ }
66
+ throw new Error('writeRegistryIfUnchanged: unknown host report shape');
67
+ }
68
+ /**
69
+ * Conditional registry write: re-read token; if matches → host compareAndSwap.
70
+ *
71
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
72
+ * swapped:true / status:committed → committed
73
+ * swapped:false / status:conflict → refused / stale_version_token
74
+ * current_token from host when supplied, else null
75
+ *
76
+ * Wired through executeIfUnchanged — not a parallel path.
77
+ */
78
+ export async function writeRegistryIfUnchanged(args) {
79
+ const detect = args.detect_stale_during_commit === true;
80
+ return executeIfUnchanged({
81
+ expected_token: args.expected_token,
82
+ current_token: async () => createRegistryVersionToken(await args.current_token()),
83
+ detect_stale_during_commit: detect,
84
+ expected_after_commit: detect
85
+ ? async (written) => {
86
+ if (typeof written.new_token === 'string' && written.new_token.trim().length > 0) {
87
+ return createRegistryVersionToken(written.new_token);
88
+ }
89
+ return createRegistryVersionToken(await args.current_token());
90
+ }
91
+ : undefined,
92
+ write: async () => {
93
+ const report = await args.compareAndSwap({
94
+ expected: registryTokenRaw(args.expected_token),
95
+ expected_token: args.expected_token,
96
+ });
97
+ const norm = normalizeRegistryReport(report);
98
+ if (norm.kind === 'conflict') {
99
+ const cur = norm.current_token !== undefined
100
+ ? createRegistryVersionToken(norm.current_token)
101
+ : null;
102
+ throw new StaleVersionTokenAbort(cur);
103
+ }
104
+ const new_token = norm.new_token === undefined || norm.new_token === null
105
+ ? null
106
+ : String(norm.new_token);
107
+ return {
108
+ new_token,
109
+ result: norm.result,
110
+ };
111
+ },
112
+ });
113
+ }
114
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
115
+ export { tokensEqual };
116
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/cas-adapters/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * CAS attestation binder (ID781 option A follow-on) — separate record linking a frozen
3
+ * GuardExecutionProof (v1) with an ExecuteIfUnchangedOutcome.
4
+ *
5
+ * Does NOT modify execution-proof.ts, the proof shape, or conditional-write outcome types.
6
+ * Does NOT claim that the host write is unique, that version tokens equal change_fp, or that
7
+ * committed_stale_detected is "safe". Those non-claims are always set on `limits`.
8
+ *
9
+ * The proof's own limit `conditional_write_is_host_asserted_not_cas_verified` names this gap;
10
+ * this module is the separate attestation surface that pairs proof + CAS outcome without
11
+ * widening the proof spec.
12
+ */
13
+ import type { GuardExecutionProof, ExecutionResultHash } from './execution-proof.js';
14
+ import type { ExecuteIfUnchangedOutcome, VersionToken } from './conditional-write.js';
15
+ /** Machine-readable schema id — mirrors EXECUTION_PROOF_SPEC style. */
16
+ export declare const CAS_ATTESTATION_SPEC: "cas-attestation.v1";
17
+ /**
18
+ * Explicit non-claims. Always set (same discipline as GuardExecutionProof.limits).
19
+ * Readers that treat missing fields as "ok" are wrong.
20
+ */
21
+ export type CasAttestationLimits = {
22
+ /** Host may have performed other writes outside executeIfUnchanged. */
23
+ does_not_claim_only_write_on_host: true;
24
+ /** version_token / post_commit_token are opaque CAS resource versions — not change_fp identity. */
25
+ does_not_claim_version_token_proves_change_fp_match: true;
26
+ /** committed_stale_detected means write ran and post-check disagreed — not "safe" or rolled back. */
27
+ does_not_claim_committed_stale_is_safe: true;
28
+ /** execution_result_hash hashes the factory return, not CAS file bytes / token equality. */
29
+ does_not_claim_execution_result_hash_equals_cas_bytes: true;
30
+ /** Same host-bypass residual as the proof family — package cannot force host CAS use. */
31
+ does_not_claim_host_cannot_bypass: true;
32
+ /** Attestation is linkage of two inputs, not a re-decision of governance ALLOW/BLOCK. */
33
+ does_not_claim_governance_redecision: true;
34
+ };
35
+ /** CAS branch projection — only fields present on the measured outcome branch. */
36
+ export type CasAttestationCas = {
37
+ status: 'committed';
38
+ write_ran: true;
39
+ version_token: VersionToken;
40
+ } | {
41
+ status: 'refused';
42
+ write_ran: false;
43
+ reason: 'stale_version_token';
44
+ expected_token: VersionToken;
45
+ current_token: VersionToken | null;
46
+ } | {
47
+ status: 'committed_stale_detected';
48
+ write_ran: true;
49
+ reason: 'stale_during_commit';
50
+ expected_token: VersionToken;
51
+ post_commit_token: VersionToken | null;
52
+ };
53
+ /**
54
+ * Frozen cas-attestation.v1 record.
55
+ * References are lifted from the proof; cas is projected from the outcome; derived flags
56
+ * are only what is honestly computable from those two inputs.
57
+ */
58
+ export type CasAttestation = {
59
+ attestation_spec: typeof CAS_ATTESTATION_SPEC;
60
+ references: {
61
+ decision_id: string | null;
62
+ change_fp: string | null;
63
+ operation: string | null;
64
+ execution_result_hash: ExecutionResultHash;
65
+ receipt_verified: boolean;
66
+ };
67
+ cas: CasAttestationCas;
68
+ derived: {
69
+ /**
70
+ * receipt.verified === true AND outcome.status === 'committed'
71
+ * (clean commit only — committed_stale_detected is NOT this flag).
72
+ */
73
+ authorized_and_committed: boolean;
74
+ /** Write mutation ran: committed or committed_stale_detected. */
75
+ write_ran: boolean;
76
+ /** outcome.status === 'committed_stale_detected'. */
77
+ stale_during_commit: boolean;
78
+ /** outcome.status === 'refused'. */
79
+ refused: boolean;
80
+ };
81
+ limits: CasAttestationLimits;
82
+ };
83
+ /** Type guard: object carries the frozen v1 proof_spec. */
84
+ export declare function isGuardExecutionProof(x: unknown): x is GuardExecutionProof;
85
+ /** Type guard: object is one of the three ExecuteIfUnchangedOutcome branches. */
86
+ export declare function isExecuteIfUnchangedOutcome(x: unknown): x is ExecuteIfUnchangedOutcome<unknown>;
87
+ /**
88
+ * Bind a frozen GuardExecutionProof with an ExecuteIfUnchangedOutcome into a cas-attestation.v1.
89
+ *
90
+ * Validation-first: rejects non-v1 proofs and non-outcome shapes (throws TypeError —
91
+ * builders fail closed; the human render layer soft-fails instead).
92
+ */
93
+ export declare function buildCasAttestation(proof: GuardExecutionProof, outcome: ExecuteIfUnchangedOutcome<unknown>): CasAttestation;
94
+ //# sourceMappingURL=cas-attestation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cas-attestation.d.ts","sourceRoot":"","sources":["../../src/cas-attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAErF,OAAO,KAAK,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtF,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,EAAG,oBAA6B,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,uEAAuE;IACvE,iCAAiC,EAAE,IAAI,CAAC;IACxC,mGAAmG;IACnG,mDAAmD,EAAE,IAAI,CAAC;IAC1D,qGAAqG;IACrG,sCAAsC,EAAE,IAAI,CAAC;IAC7C,4FAA4F;IAC5F,qDAAqD,EAAE,IAAI,CAAC;IAC5D,yFAAyF;IACzF,iCAAiC,EAAE,IAAI,CAAC;IACxC,yFAAyF;IACzF,oCAAoC,EAAE,IAAI,CAAC;CAC5C,CAAC;AAWF,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GACzB;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,aAAa,EAAE,YAAY,CAAC;CAC7B,GACD;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,cAAc,EAAE,YAAY,CAAC;IAC7B,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;CACpC,GACD;IACE,MAAM,EAAE,0BAA0B,CAAC;IACnC,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,cAAc,EAAE,YAAY,CAAC;IAC7B,iBAAiB,EAAE,YAAY,GAAG,IAAI,CAAC;CACxC,CAAC;AAEN;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,gBAAgB,EAAE,OAAO,oBAAoB,CAAC;IAC9C,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,qBAAqB,EAAE,mBAAmB,CAAC;QAC3C,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;IACF,GAAG,EAAE,iBAAiB,CAAC;IACvB,OAAO,EAAE;QACP;;;WAGG;QACH,wBAAwB,EAAE,OAAO,CAAC;QAClC,iEAAiE;QACjE,SAAS,EAAE,OAAO,CAAC;QACnB,qDAAqD;QACrD,mBAAmB,EAAE,OAAO,CAAC;QAC7B,oCAAoC;QACpC,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,MAAM,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAQ1E;AAED,iFAAiF;AACjF,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,yBAAyB,CAAC,OAAO,CAAC,CAgB/F;AAiCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,yBAAyB,CAAC,OAAO,CAAC,GAC1C,cAAc,CAyChB"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * CAS attestation binder (ID781 option A follow-on) — separate record linking a frozen
3
+ * GuardExecutionProof (v1) with an ExecuteIfUnchangedOutcome.
4
+ *
5
+ * Does NOT modify execution-proof.ts, the proof shape, or conditional-write outcome types.
6
+ * Does NOT claim that the host write is unique, that version tokens equal change_fp, or that
7
+ * committed_stale_detected is "safe". Those non-claims are always set on `limits`.
8
+ *
9
+ * The proof's own limit `conditional_write_is_host_asserted_not_cas_verified` names this gap;
10
+ * this module is the separate attestation surface that pairs proof + CAS outcome without
11
+ * widening the proof spec.
12
+ */
13
+ import { EXECUTION_PROOF_SPEC } from './execution-proof.js';
14
+ /** Machine-readable schema id — mirrors EXECUTION_PROOF_SPEC style. */
15
+ export const CAS_ATTESTATION_SPEC = 'cas-attestation.v1';
16
+ const LIMITS = Object.freeze({
17
+ does_not_claim_only_write_on_host: true,
18
+ does_not_claim_version_token_proves_change_fp_match: true,
19
+ does_not_claim_committed_stale_is_safe: true,
20
+ does_not_claim_execution_result_hash_equals_cas_bytes: true,
21
+ does_not_claim_host_cannot_bypass: true,
22
+ does_not_claim_governance_redecision: true,
23
+ });
24
+ /** Type guard: object carries the frozen v1 proof_spec. */
25
+ export function isGuardExecutionProof(x) {
26
+ if (!x || typeof x !== 'object')
27
+ return false;
28
+ const p = x;
29
+ return p.proof_spec === EXECUTION_PROOF_SPEC
30
+ && p.receipt != null
31
+ && typeof p.receipt === 'object'
32
+ && p.execution_result_hash != null
33
+ && typeof p.execution_result_hash === 'object';
34
+ }
35
+ /** Type guard: object is one of the three ExecuteIfUnchangedOutcome branches. */
36
+ export function isExecuteIfUnchangedOutcome(x) {
37
+ if (!x || typeof x !== 'object')
38
+ return false;
39
+ const o = x;
40
+ if (o.status === 'committed') {
41
+ const c = x;
42
+ return typeof c.version_token === 'string';
43
+ }
44
+ if (o.status === 'refused') {
45
+ const r = x;
46
+ return r.reason === 'stale_version_token' && typeof r.expected_token === 'string';
47
+ }
48
+ if (o.status === 'committed_stale_detected') {
49
+ const s = x;
50
+ return s.reason === 'stale_during_commit' && typeof s.expected_token === 'string';
51
+ }
52
+ return false;
53
+ }
54
+ function freezeExecutionResultHash(h) {
55
+ return Object.freeze({ ...h });
56
+ }
57
+ function projectCas(outcome) {
58
+ if (outcome.status === 'committed') {
59
+ return Object.freeze({
60
+ status: 'committed',
61
+ write_ran: true,
62
+ version_token: outcome.version_token,
63
+ });
64
+ }
65
+ if (outcome.status === 'refused') {
66
+ return Object.freeze({
67
+ status: 'refused',
68
+ write_ran: false,
69
+ reason: 'stale_version_token',
70
+ expected_token: outcome.expected_token,
71
+ current_token: outcome.current_token == null ? null : outcome.current_token,
72
+ });
73
+ }
74
+ // committed_stale_detected
75
+ return Object.freeze({
76
+ status: 'committed_stale_detected',
77
+ write_ran: true,
78
+ reason: 'stale_during_commit',
79
+ expected_token: outcome.expected_token,
80
+ post_commit_token: outcome.post_commit_token == null ? null : outcome.post_commit_token,
81
+ });
82
+ }
83
+ /**
84
+ * Bind a frozen GuardExecutionProof with an ExecuteIfUnchangedOutcome into a cas-attestation.v1.
85
+ *
86
+ * Validation-first: rejects non-v1 proofs and non-outcome shapes (throws TypeError —
87
+ * builders fail closed; the human render layer soft-fails instead).
88
+ */
89
+ export function buildCasAttestation(proof, outcome) {
90
+ if (!isGuardExecutionProof(proof)) {
91
+ throw new TypeError('@coderifts/agent-guard: buildCasAttestation requires a valid guard-execution-proof.v1 object '
92
+ + '(proof_spec mismatch or missing required fields)');
93
+ }
94
+ if (!isExecuteIfUnchangedOutcome(outcome)) {
95
+ throw new TypeError('@coderifts/agent-guard: buildCasAttestation requires a valid ExecuteIfUnchangedOutcome '
96
+ + '(status committed | refused | committed_stale_detected with branch fields)');
97
+ }
98
+ const receipt_verified = proof.receipt.verified === true;
99
+ const cas = projectCas(outcome);
100
+ const write_ran = cas.write_ran === true;
101
+ const stale_during_commit = cas.status === 'committed_stale_detected';
102
+ const refused = cas.status === 'refused';
103
+ const authorized_and_committed = receipt_verified && cas.status === 'committed';
104
+ const attestation = {
105
+ attestation_spec: CAS_ATTESTATION_SPEC,
106
+ references: Object.freeze({
107
+ decision_id: proof.decision_id,
108
+ change_fp: proof.binds_to != null ? proof.binds_to.change_fp : null,
109
+ operation: proof.binds_to != null ? proof.binds_to.operation : null,
110
+ execution_result_hash: freezeExecutionResultHash(proof.execution_result_hash),
111
+ receipt_verified,
112
+ }),
113
+ cas,
114
+ derived: Object.freeze({
115
+ authorized_and_committed,
116
+ write_ran,
117
+ stale_during_commit,
118
+ refused,
119
+ }),
120
+ limits: LIMITS,
121
+ };
122
+ return freezeAttestation(attestation);
123
+ }
124
+ function freezeAttestation(a) {
125
+ return Object.freeze(a);
126
+ }
127
+ //# sourceMappingURL=cas-attestation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cas-attestation.js","sourceRoot":"","sources":["../../src/cas-attestation.ts"],"names":[],"mappings":"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"}