@mnemonik/shared 7.69.0 → 7.72.1
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.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/jitDeliveryAck.d.ts +27 -3
- package/dist/jitDeliveryAck.d.ts.map +1 -1
- package/dist/jitDeliveryAck.js +74 -4
- package/dist/jitDeliveryAck.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +7 -0
- package/src/jitDeliveryAck.ts +131 -4
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { AST_LANGUAGE_IDS, QUERY_CHAIN, VENDORED_ARTIFACT_DIR, astArtifactReport
|
|
|
11
11
|
export { MAX_AST_PARSE_BYTES, chunkWithAst, type AstChunk, type AstChunkResult, } from './ast/astChunker.js';
|
|
12
12
|
export { SECRET_PATTERNS, SECRET_REDACTION_PLACEHOLDER, scrubSecrets, redactHighEntropyTokens, } from './secretPatterns.js';
|
|
13
13
|
export { FETCH_TIMEOUT_MS, TELEMETRY_TIMEOUT_MS, POST_TOOL_TIMEOUT_MS, MCP_PRECHECK_TIMEOUT_MS, withHookTimeout, } from './hookTimeouts.js';
|
|
14
|
-
export { persistJitDeliveryRef, readPendingJitDeliveryRefs, acknowledgeJitDeliveryRefs, } from './jitDeliveryAck.js';
|
|
14
|
+
export { persistJitDeliveryRef, readPendingJitDeliveryRefs, reportJitDeliveryRefsAttached, acknowledgeJitDeliveryRefs, pruneExpiredJitDeliveryRefs, applyJitDeliveryReceiptResponse, normalizeJitHookVersion, type JitReceiptClientEvent, type JitReceiptClientEventName, type JitReceiptEventContext, } from './jitDeliveryAck.js';
|
|
15
15
|
export * from './settingsIo.js';
|
|
16
16
|
export * from './claudeProxySettings.js';
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,YAAY,EACZ,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,YAAY,EACZ,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { AST_LANGUAGE_IDS, QUERY_CHAIN, VENDORED_ARTIFACT_DIR, astArtifactReport
|
|
|
11
11
|
export { MAX_AST_PARSE_BYTES, chunkWithAst, } from './ast/astChunker.js';
|
|
12
12
|
export { SECRET_PATTERNS, SECRET_REDACTION_PLACEHOLDER, scrubSecrets, redactHighEntropyTokens, } from './secretPatterns.js';
|
|
13
13
|
export { FETCH_TIMEOUT_MS, TELEMETRY_TIMEOUT_MS, POST_TOOL_TIMEOUT_MS, MCP_PRECHECK_TIMEOUT_MS, withHookTimeout, } from './hookTimeouts.js';
|
|
14
|
-
export { persistJitDeliveryRef, readPendingJitDeliveryRefs, acknowledgeJitDeliveryRefs, } from './jitDeliveryAck.js';
|
|
14
|
+
export { persistJitDeliveryRef, readPendingJitDeliveryRefs, reportJitDeliveryRefsAttached, acknowledgeJitDeliveryRefs, pruneExpiredJitDeliveryRefs, applyJitDeliveryReceiptResponse, normalizeJitHookVersion, } from './jitDeliveryAck.js';
|
|
15
15
|
export * from './settingsIo.js';
|
|
16
16
|
export * from './claudeProxySettings.js';
|
|
17
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GAKnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,YAAY,GAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,YAAY,EACZ,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GAKnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,YAAY,GAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,YAAY,EACZ,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,GAIxB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC"}
|
package/dist/jitDeliveryAck.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
|
+
export declare function normalizeJitHookVersion(hookVersion: unknown): string;
|
|
2
|
+
export type JitReceiptClientEventName = 'marker_persisted' | 'marker_read' | 'attached_to_request' | 'marker_confirmed' | 'pruned_expired';
|
|
3
|
+
export interface JitReceiptClientEvent {
|
|
4
|
+
mnemonik_marker: 'jit_receipt_client';
|
|
5
|
+
event: JitReceiptClientEventName;
|
|
6
|
+
deliveryRefHash: string;
|
|
7
|
+
sessionFingerprint: string;
|
|
8
|
+
hookVersion: string;
|
|
9
|
+
reason?: 'client_retention' | 'server_unknown_or_expired';
|
|
10
|
+
}
|
|
11
|
+
export interface JitReceiptEventContext {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
hookVersion: string;
|
|
14
|
+
/** Test seam; production writes one JSON event per line to hook stderr. */
|
|
15
|
+
emit?: (event: JitReceiptClientEvent) => void;
|
|
16
|
+
}
|
|
1
17
|
/** Persist only after the final host-visible payload contains this JIT result. */
|
|
2
|
-
export declare function persistJitDeliveryRef(directory: string, deliveryRef: string): boolean;
|
|
3
|
-
export declare function readPendingJitDeliveryRefs(directory: string): string[];
|
|
18
|
+
export declare function persistJitDeliveryRef(directory: string, deliveryRef: string, context?: JitReceiptEventContext): boolean;
|
|
19
|
+
export declare function readPendingJitDeliveryRefs(directory: string, context?: JitReceiptEventContext): string[];
|
|
20
|
+
export declare function reportJitDeliveryRefsAttached(deliveryRefs: string[], context?: JitReceiptEventContext): void;
|
|
4
21
|
/** Remove only references the server says were valid or safe duplicates. */
|
|
5
|
-
export declare function acknowledgeJitDeliveryRefs(directory: string, deliveryRefs: string[]): void;
|
|
22
|
+
export declare function acknowledgeJitDeliveryRefs(directory: string, deliveryRefs: string[], context?: JitReceiptEventContext): void;
|
|
23
|
+
/** Remove references the server can no longer accept instead of retrying forever. */
|
|
24
|
+
export declare function pruneExpiredJitDeliveryRefs(directory: string, deliveryRefs: string[], context?: JitReceiptEventContext): void;
|
|
25
|
+
/** Apply only receipt outcomes correlated to the exact refs on this request. */
|
|
26
|
+
export declare function applyJitDeliveryReceiptResponse(directory: string, attachedDeliveryRefs: string[], response: {
|
|
27
|
+
confirmedDeliveryRefs?: string[];
|
|
28
|
+
discardedDeliveryRefs?: string[];
|
|
29
|
+
}, context?: JitReceiptEventContext): void;
|
|
6
30
|
//# sourceMappingURL=jitDeliveryAck.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jitDeliveryAck.d.ts","sourceRoot":"","sources":["../src/jitDeliveryAck.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jitDeliveryAck.d.ts","sourceRoot":"","sources":["../src/jitDeliveryAck.ts"],"names":[],"mappings":"AAoBA,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,CAOpE;AAED,MAAM,MAAM,yBAAyB,GACjC,kBAAkB,GAClB,aAAa,GACb,qBAAqB,GACrB,kBAAkB,GAClB,gBAAgB,CAAC;AAErB,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,oBAAoB,CAAC;IACtC,KAAK,EAAE,yBAAyB,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,GAAG,2BAA2B,CAAC;CAC3D;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC/C;AAyCD,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CA8BT;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,EAAE,CAyCV;AAED,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,IAAI,CAMN;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,IAAI,CAUN;AAED,qFAAqF;AACrF,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,IAAI,CAUN;AAED,gFAAgF;AAChF,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,oBAAoB,EAAE,MAAM,EAAE,EAC9B,QAAQ,EAAE;IAAE,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EAChF,OAAO,CAAC,EAAE,sBAAsB,GAC/B,IAAI,CAYN"}
|
package/dist/jitDeliveryAck.js
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { chmodSync, closeSync, constants, existsSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync, } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
4
|
+
import { stderr } from 'node:process';
|
|
3
5
|
const DELIVERY_REF_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
6
|
+
const HOOK_VERSION_RE = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
|
4
7
|
const MAX_PENDING_REFS = 8;
|
|
5
8
|
const DELIVERY_REF_RETENTION_MS = 240_000;
|
|
9
|
+
export function normalizeJitHookVersion(hookVersion) {
|
|
10
|
+
if (hookVersion === undefined || hookVersion === null)
|
|
11
|
+
return 'unknown';
|
|
12
|
+
return typeof hookVersion === 'string' &&
|
|
13
|
+
hookVersion.length <= 64 &&
|
|
14
|
+
HOOK_VERSION_RE.test(hookVersion)
|
|
15
|
+
? hookVersion
|
|
16
|
+
: 'unrecognized';
|
|
17
|
+
}
|
|
18
|
+
function hash(value) {
|
|
19
|
+
return createHash('sha256').update(value).digest('hex');
|
|
20
|
+
}
|
|
21
|
+
function emitReceiptEvent(context, event, deliveryRef, reason) {
|
|
22
|
+
if (!context)
|
|
23
|
+
return;
|
|
24
|
+
const record = {
|
|
25
|
+
mnemonik_marker: 'jit_receipt_client',
|
|
26
|
+
event,
|
|
27
|
+
deliveryRefHash: hash(deliveryRef),
|
|
28
|
+
sessionFingerprint: hash(context.sessionId).slice(0, 16),
|
|
29
|
+
hookVersion: normalizeJitHookVersion(context.hookVersion),
|
|
30
|
+
...(reason ? { reason } : {}),
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
if (context.emit)
|
|
34
|
+
context.emit(record);
|
|
35
|
+
else
|
|
36
|
+
stderr.write(`${JSON.stringify(record)}\n`);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Receipt instrumentation must never interfere with the host turn.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
6
42
|
function ensurePrivateDirectory(path) {
|
|
7
43
|
if (!path)
|
|
8
44
|
return false;
|
|
@@ -18,19 +54,23 @@ function ensurePrivateDirectory(path) {
|
|
|
18
54
|
}
|
|
19
55
|
}
|
|
20
56
|
/** Persist only after the final host-visible payload contains this JIT result. */
|
|
21
|
-
export function persistJitDeliveryRef(directory, deliveryRef) {
|
|
57
|
+
export function persistJitDeliveryRef(directory, deliveryRef, context) {
|
|
22
58
|
if (!DELIVERY_REF_RE.test(deliveryRef) || !ensurePrivateDirectory(directory))
|
|
23
59
|
return false;
|
|
24
60
|
let fd = null;
|
|
25
61
|
try {
|
|
26
62
|
fd = openSync(join(directory, deliveryRef), constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | (constants.O_NOFOLLOW ?? 0), 0o600);
|
|
63
|
+
emitReceiptEvent(context, 'marker_persisted', deliveryRef);
|
|
27
64
|
return true;
|
|
28
65
|
}
|
|
29
66
|
catch {
|
|
30
67
|
// An existing marker is already the desired idempotent state.
|
|
31
68
|
try {
|
|
32
69
|
const stat = lstatSync(join(directory, deliveryRef));
|
|
33
|
-
|
|
70
|
+
const persisted = stat.isFile() && !stat.isSymbolicLink() && (stat.mode & 0o077) === 0;
|
|
71
|
+
if (persisted)
|
|
72
|
+
emitReceiptEvent(context, 'marker_persisted', deliveryRef);
|
|
73
|
+
return persisted;
|
|
34
74
|
}
|
|
35
75
|
catch {
|
|
36
76
|
return false;
|
|
@@ -47,7 +87,7 @@ export function persistJitDeliveryRef(directory, deliveryRef) {
|
|
|
47
87
|
}
|
|
48
88
|
}
|
|
49
89
|
}
|
|
50
|
-
export function readPendingJitDeliveryRefs(directory) {
|
|
90
|
+
export function readPendingJitDeliveryRefs(directory, context) {
|
|
51
91
|
if (!directory)
|
|
52
92
|
return [];
|
|
53
93
|
try {
|
|
@@ -73,8 +113,10 @@ export function readPendingJitDeliveryRefs(directory) {
|
|
|
73
113
|
catch {
|
|
74
114
|
// Keep expired markers out of the send window even if cleanup races or fails.
|
|
75
115
|
}
|
|
116
|
+
emitReceiptEvent(context, 'pruned_expired', deliveryRef, 'client_retention');
|
|
76
117
|
continue;
|
|
77
118
|
}
|
|
119
|
+
emitReceiptEvent(context, 'marker_read', deliveryRef);
|
|
78
120
|
live.push({ deliveryRef, modifiedAt: marker.mtimeMs });
|
|
79
121
|
}
|
|
80
122
|
catch {
|
|
@@ -90,11 +132,19 @@ export function readPendingJitDeliveryRefs(directory) {
|
|
|
90
132
|
return [];
|
|
91
133
|
}
|
|
92
134
|
}
|
|
135
|
+
export function reportJitDeliveryRefsAttached(deliveryRefs, context) {
|
|
136
|
+
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
137
|
+
if (DELIVERY_REF_RE.test(deliveryRef)) {
|
|
138
|
+
emitReceiptEvent(context, 'attached_to_request', deliveryRef);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
93
142
|
/** Remove only references the server says were valid or safe duplicates. */
|
|
94
|
-
export function acknowledgeJitDeliveryRefs(directory, deliveryRefs) {
|
|
143
|
+
export function acknowledgeJitDeliveryRefs(directory, deliveryRefs, context) {
|
|
95
144
|
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
96
145
|
if (!DELIVERY_REF_RE.test(deliveryRef))
|
|
97
146
|
continue;
|
|
147
|
+
emitReceiptEvent(context, 'marker_confirmed', deliveryRef);
|
|
98
148
|
try {
|
|
99
149
|
unlinkSync(join(directory, deliveryRef));
|
|
100
150
|
}
|
|
@@ -103,4 +153,24 @@ export function acknowledgeJitDeliveryRefs(directory, deliveryRefs) {
|
|
|
103
153
|
}
|
|
104
154
|
}
|
|
105
155
|
}
|
|
156
|
+
/** Remove references the server can no longer accept instead of retrying forever. */
|
|
157
|
+
export function pruneExpiredJitDeliveryRefs(directory, deliveryRefs, context) {
|
|
158
|
+
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
159
|
+
if (!DELIVERY_REF_RE.test(deliveryRef))
|
|
160
|
+
continue;
|
|
161
|
+
try {
|
|
162
|
+
unlinkSync(join(directory, deliveryRef));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Missing/locked marker: the ordinary client-retention sweep remains a backstop.
|
|
166
|
+
}
|
|
167
|
+
emitReceiptEvent(context, 'pruned_expired', deliveryRef, 'server_unknown_or_expired');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** Apply only receipt outcomes correlated to the exact refs on this request. */
|
|
171
|
+
export function applyJitDeliveryReceiptResponse(directory, attachedDeliveryRefs, response, context) {
|
|
172
|
+
const attached = new Set(attachedDeliveryRefs);
|
|
173
|
+
acknowledgeJitDeliveryRefs(directory, (response.confirmedDeliveryRefs ?? []).filter((deliveryRef) => attached.has(deliveryRef)), context);
|
|
174
|
+
pruneExpiredJitDeliveryRefs(directory, (response.discardedDeliveryRefs ?? []).filter((deliveryRef) => attached.has(deliveryRef)), context);
|
|
175
|
+
}
|
|
106
176
|
//# sourceMappingURL=jitDeliveryAck.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jitDeliveryAck.js","sourceRoot":"","sources":["../src/jitDeliveryAck.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"jitDeliveryAck.js","sourceRoot":"","sources":["../src/jitDeliveryAck.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -61,7 +61,14 @@ export {
|
|
|
61
61
|
export {
|
|
62
62
|
persistJitDeliveryRef,
|
|
63
63
|
readPendingJitDeliveryRefs,
|
|
64
|
+
reportJitDeliveryRefsAttached,
|
|
64
65
|
acknowledgeJitDeliveryRefs,
|
|
66
|
+
pruneExpiredJitDeliveryRefs,
|
|
67
|
+
applyJitDeliveryReceiptResponse,
|
|
68
|
+
normalizeJitHookVersion,
|
|
69
|
+
type JitReceiptClientEvent,
|
|
70
|
+
type JitReceiptClientEventName,
|
|
71
|
+
type JitReceiptEventContext,
|
|
65
72
|
} from './jitDeliveryAck.js';
|
|
66
73
|
export * from './settingsIo.js';
|
|
67
74
|
export * from './claudeProxySettings.js';
|
package/src/jitDeliveryAck.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import {
|
|
2
3
|
chmodSync,
|
|
3
4
|
closeSync,
|
|
@@ -10,11 +11,72 @@ import {
|
|
|
10
11
|
unlinkSync,
|
|
11
12
|
} from 'node:fs';
|
|
12
13
|
import { join } from 'node:path';
|
|
14
|
+
import { stderr } from 'node:process';
|
|
13
15
|
|
|
14
16
|
const DELIVERY_REF_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
17
|
+
const HOOK_VERSION_RE = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
|
15
18
|
const MAX_PENDING_REFS = 8;
|
|
16
19
|
const DELIVERY_REF_RETENTION_MS = 240_000;
|
|
17
20
|
|
|
21
|
+
export function normalizeJitHookVersion(hookVersion: unknown): string {
|
|
22
|
+
if (hookVersion === undefined || hookVersion === null) return 'unknown';
|
|
23
|
+
return typeof hookVersion === 'string' &&
|
|
24
|
+
hookVersion.length <= 64 &&
|
|
25
|
+
HOOK_VERSION_RE.test(hookVersion)
|
|
26
|
+
? hookVersion
|
|
27
|
+
: 'unrecognized';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type JitReceiptClientEventName =
|
|
31
|
+
| 'marker_persisted'
|
|
32
|
+
| 'marker_read'
|
|
33
|
+
| 'attached_to_request'
|
|
34
|
+
| 'marker_confirmed'
|
|
35
|
+
| 'pruned_expired';
|
|
36
|
+
|
|
37
|
+
export interface JitReceiptClientEvent {
|
|
38
|
+
mnemonik_marker: 'jit_receipt_client';
|
|
39
|
+
event: JitReceiptClientEventName;
|
|
40
|
+
deliveryRefHash: string;
|
|
41
|
+
sessionFingerprint: string;
|
|
42
|
+
hookVersion: string;
|
|
43
|
+
reason?: 'client_retention' | 'server_unknown_or_expired';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface JitReceiptEventContext {
|
|
47
|
+
sessionId: string;
|
|
48
|
+
hookVersion: string;
|
|
49
|
+
/** Test seam; production writes one JSON event per line to hook stderr. */
|
|
50
|
+
emit?: (event: JitReceiptClientEvent) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function hash(value: string): string {
|
|
54
|
+
return createHash('sha256').update(value).digest('hex');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function emitReceiptEvent(
|
|
58
|
+
context: JitReceiptEventContext | undefined,
|
|
59
|
+
event: JitReceiptClientEventName,
|
|
60
|
+
deliveryRef: string,
|
|
61
|
+
reason?: JitReceiptClientEvent['reason']
|
|
62
|
+
): void {
|
|
63
|
+
if (!context) return;
|
|
64
|
+
const record: JitReceiptClientEvent = {
|
|
65
|
+
mnemonik_marker: 'jit_receipt_client',
|
|
66
|
+
event,
|
|
67
|
+
deliveryRefHash: hash(deliveryRef),
|
|
68
|
+
sessionFingerprint: hash(context.sessionId).slice(0, 16),
|
|
69
|
+
hookVersion: normalizeJitHookVersion(context.hookVersion),
|
|
70
|
+
...(reason ? { reason } : {}),
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
if (context.emit) context.emit(record);
|
|
74
|
+
else stderr.write(`${JSON.stringify(record)}\n`);
|
|
75
|
+
} catch {
|
|
76
|
+
// Receipt instrumentation must never interfere with the host turn.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
18
80
|
function ensurePrivateDirectory(path: string): boolean {
|
|
19
81
|
if (!path) return false;
|
|
20
82
|
try {
|
|
@@ -28,7 +90,11 @@ function ensurePrivateDirectory(path: string): boolean {
|
|
|
28
90
|
}
|
|
29
91
|
|
|
30
92
|
/** Persist only after the final host-visible payload contains this JIT result. */
|
|
31
|
-
export function persistJitDeliveryRef(
|
|
93
|
+
export function persistJitDeliveryRef(
|
|
94
|
+
directory: string,
|
|
95
|
+
deliveryRef: string,
|
|
96
|
+
context?: JitReceiptEventContext
|
|
97
|
+
): boolean {
|
|
32
98
|
if (!DELIVERY_REF_RE.test(deliveryRef) || !ensurePrivateDirectory(directory)) return false;
|
|
33
99
|
let fd: number | null = null;
|
|
34
100
|
try {
|
|
@@ -37,12 +103,15 @@ export function persistJitDeliveryRef(directory: string, deliveryRef: string): b
|
|
|
37
103
|
constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | (constants.O_NOFOLLOW ?? 0),
|
|
38
104
|
0o600
|
|
39
105
|
);
|
|
106
|
+
emitReceiptEvent(context, 'marker_persisted', deliveryRef);
|
|
40
107
|
return true;
|
|
41
108
|
} catch {
|
|
42
109
|
// An existing marker is already the desired idempotent state.
|
|
43
110
|
try {
|
|
44
111
|
const stat = lstatSync(join(directory, deliveryRef));
|
|
45
|
-
|
|
112
|
+
const persisted = stat.isFile() && !stat.isSymbolicLink() && (stat.mode & 0o077) === 0;
|
|
113
|
+
if (persisted) emitReceiptEvent(context, 'marker_persisted', deliveryRef);
|
|
114
|
+
return persisted;
|
|
46
115
|
} catch {
|
|
47
116
|
return false;
|
|
48
117
|
}
|
|
@@ -57,7 +126,10 @@ export function persistJitDeliveryRef(directory: string, deliveryRef: string): b
|
|
|
57
126
|
}
|
|
58
127
|
}
|
|
59
128
|
|
|
60
|
-
export function readPendingJitDeliveryRefs(
|
|
129
|
+
export function readPendingJitDeliveryRefs(
|
|
130
|
+
directory: string,
|
|
131
|
+
context?: JitReceiptEventContext
|
|
132
|
+
): string[] {
|
|
61
133
|
if (!directory) return [];
|
|
62
134
|
try {
|
|
63
135
|
if (!existsSync(directory)) return [];
|
|
@@ -78,8 +150,10 @@ export function readPendingJitDeliveryRefs(directory: string): string[] {
|
|
|
78
150
|
} catch {
|
|
79
151
|
// Keep expired markers out of the send window even if cleanup races or fails.
|
|
80
152
|
}
|
|
153
|
+
emitReceiptEvent(context, 'pruned_expired', deliveryRef, 'client_retention');
|
|
81
154
|
continue;
|
|
82
155
|
}
|
|
156
|
+
emitReceiptEvent(context, 'marker_read', deliveryRef);
|
|
83
157
|
live.push({ deliveryRef, modifiedAt: marker.mtimeMs });
|
|
84
158
|
} catch {
|
|
85
159
|
// A marker removed concurrently is already in the desired state.
|
|
@@ -98,10 +172,26 @@ export function readPendingJitDeliveryRefs(directory: string): string[] {
|
|
|
98
172
|
}
|
|
99
173
|
}
|
|
100
174
|
|
|
175
|
+
export function reportJitDeliveryRefsAttached(
|
|
176
|
+
deliveryRefs: string[],
|
|
177
|
+
context?: JitReceiptEventContext
|
|
178
|
+
): void {
|
|
179
|
+
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
180
|
+
if (DELIVERY_REF_RE.test(deliveryRef)) {
|
|
181
|
+
emitReceiptEvent(context, 'attached_to_request', deliveryRef);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
101
186
|
/** Remove only references the server says were valid or safe duplicates. */
|
|
102
|
-
export function acknowledgeJitDeliveryRefs(
|
|
187
|
+
export function acknowledgeJitDeliveryRefs(
|
|
188
|
+
directory: string,
|
|
189
|
+
deliveryRefs: string[],
|
|
190
|
+
context?: JitReceiptEventContext
|
|
191
|
+
): void {
|
|
103
192
|
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
104
193
|
if (!DELIVERY_REF_RE.test(deliveryRef)) continue;
|
|
194
|
+
emitReceiptEvent(context, 'marker_confirmed', deliveryRef);
|
|
105
195
|
try {
|
|
106
196
|
unlinkSync(join(directory, deliveryRef));
|
|
107
197
|
} catch {
|
|
@@ -109,3 +199,40 @@ export function acknowledgeJitDeliveryRefs(directory: string, deliveryRefs: stri
|
|
|
109
199
|
}
|
|
110
200
|
}
|
|
111
201
|
}
|
|
202
|
+
|
|
203
|
+
/** Remove references the server can no longer accept instead of retrying forever. */
|
|
204
|
+
export function pruneExpiredJitDeliveryRefs(
|
|
205
|
+
directory: string,
|
|
206
|
+
deliveryRefs: string[],
|
|
207
|
+
context?: JitReceiptEventContext
|
|
208
|
+
): void {
|
|
209
|
+
for (const deliveryRef of new Set(deliveryRefs)) {
|
|
210
|
+
if (!DELIVERY_REF_RE.test(deliveryRef)) continue;
|
|
211
|
+
try {
|
|
212
|
+
unlinkSync(join(directory, deliveryRef));
|
|
213
|
+
} catch {
|
|
214
|
+
// Missing/locked marker: the ordinary client-retention sweep remains a backstop.
|
|
215
|
+
}
|
|
216
|
+
emitReceiptEvent(context, 'pruned_expired', deliveryRef, 'server_unknown_or_expired');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Apply only receipt outcomes correlated to the exact refs on this request. */
|
|
221
|
+
export function applyJitDeliveryReceiptResponse(
|
|
222
|
+
directory: string,
|
|
223
|
+
attachedDeliveryRefs: string[],
|
|
224
|
+
response: { confirmedDeliveryRefs?: string[]; discardedDeliveryRefs?: string[] },
|
|
225
|
+
context?: JitReceiptEventContext
|
|
226
|
+
): void {
|
|
227
|
+
const attached = new Set(attachedDeliveryRefs);
|
|
228
|
+
acknowledgeJitDeliveryRefs(
|
|
229
|
+
directory,
|
|
230
|
+
(response.confirmedDeliveryRefs ?? []).filter((deliveryRef) => attached.has(deliveryRef)),
|
|
231
|
+
context
|
|
232
|
+
);
|
|
233
|
+
pruneExpiredJitDeliveryRefs(
|
|
234
|
+
directory,
|
|
235
|
+
(response.discardedDeliveryRefs ?? []).filter((deliveryRef) => attached.has(deliveryRef)),
|
|
236
|
+
context
|
|
237
|
+
);
|
|
238
|
+
}
|