@deftai/directive-core 0.96.0 → 0.97.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 (73) hide show
  1. package/dist/cache/archive.js +10 -4
  2. package/dist/check/gate-lists.js +8 -0
  3. package/dist/consumer-check-contract/evaluate.d.ts +124 -0
  4. package/dist/consumer-check-contract/evaluate.js +699 -0
  5. package/dist/consumer-check-contract/index.d.ts +5 -0
  6. package/dist/consumer-check-contract/index.js +5 -0
  7. package/dist/delivery-attempt/disk-begin.d.ts +51 -0
  8. package/dist/delivery-attempt/disk-begin.js +68 -0
  9. package/dist/delivery-attempt/evaluate.d.ts +26 -0
  10. package/dist/delivery-attempt/evaluate.js +443 -0
  11. package/dist/delivery-attempt/fingerprint.d.ts +32 -0
  12. package/dist/delivery-attempt/fingerprint.js +100 -0
  13. package/dist/delivery-attempt/handoff.d.ts +25 -0
  14. package/dist/delivery-attempt/handoff.js +102 -0
  15. package/dist/delivery-attempt/index.d.ts +17 -0
  16. package/dist/delivery-attempt/index.js +17 -0
  17. package/dist/delivery-attempt/ledger.d.ts +169 -0
  18. package/dist/delivery-attempt/ledger.js +758 -0
  19. package/dist/delivery-attempt/material-delta.d.ts +38 -0
  20. package/dist/delivery-attempt/material-delta.js +126 -0
  21. package/dist/delivery-attempt/types.d.ts +210 -0
  22. package/dist/delivery-attempt/types.js +77 -0
  23. package/dist/doctor/index.d.ts +1 -0
  24. package/dist/doctor/index.js +1 -0
  25. package/dist/doctor/main.js +12 -0
  26. package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
  27. package/dist/doctor/openclaw-soft-rebind.js +164 -0
  28. package/dist/hooks/dispatcher.d.ts +2 -1
  29. package/dist/hooks/dispatcher.js +63 -9
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +4 -0
  32. package/dist/init-deposit/gitignore.js +7 -0
  33. package/dist/init-deposit/init-deposit.js +5 -0
  34. package/dist/init-deposit/refresh.js +3 -0
  35. package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
  36. package/dist/pr-merge-readiness/ci-gate.js +191 -24
  37. package/dist/pr-merge-readiness/compute.js +10 -1
  38. package/dist/pr-merge-readiness/index.d.ts +2 -1
  39. package/dist/pr-merge-readiness/index.js +2 -1
  40. package/dist/pr-merge-readiness/output.js +14 -0
  41. package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
  42. package/dist/pr-merge-readiness/platform-status.js +49 -0
  43. package/dist/pr-watch/constants.d.ts +10 -0
  44. package/dist/pr-watch/constants.js +12 -1
  45. package/dist/pr-watch/main.js +16 -1
  46. package/dist/pr-watch/probe.js +13 -1
  47. package/dist/pr-watch/types.d.ts +3 -2
  48. package/dist/pr-watch/watch.js +14 -7
  49. package/dist/scope-provenance/digest.d.ts +67 -0
  50. package/dist/scope-provenance/digest.js +188 -0
  51. package/dist/scope-provenance/evaluate.d.ts +82 -0
  52. package/dist/scope-provenance/evaluate.js +528 -0
  53. package/dist/scope-provenance/index.d.ts +6 -0
  54. package/dist/scope-provenance/index.js +6 -0
  55. package/dist/session/compact-ritual.d.ts +96 -0
  56. package/dist/session/compact-ritual.js +237 -0
  57. package/dist/session/compact-ritual.spec.d.ts +2 -0
  58. package/dist/session/compact-ritual.spec.js +21 -0
  59. package/dist/session/index.d.ts +2 -0
  60. package/dist/session/index.js +2 -0
  61. package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
  62. package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
  63. package/dist/test-boundary/evaluate.d.ts +54 -0
  64. package/dist/test-boundary/evaluate.js +368 -0
  65. package/dist/test-boundary/index.d.ts +6 -0
  66. package/dist/test-boundary/index.js +6 -0
  67. package/dist/test-boundary/policy.d.ts +52 -0
  68. package/dist/test-boundary/policy.js +182 -0
  69. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  70. package/dist/triage/bootstrap/gitignore.js +15 -1
  71. package/dist/vbrief-activate/activate.js +22 -6
  72. package/dist/xbrief/styles.js +33 -17
  73. package/package.json +7 -3
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Failure fingerprinting for delivery-attempt circuit breaker (#3143).
3
+ *
4
+ * Free-form model judgment alone is not sufficient. Prefer structured
5
+ * stage/code fields; normalize volatile identifiers out of messages.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ /** Strip volatile tokens that must not affect fingerprint stability. */
9
+ const VOLATILE_PATTERNS = [
10
+ // ISO timestamps first
11
+ /\b\d{4}-\d{2}-\d{2}T[\d:.]+Z?\b/gi,
12
+ // UUIDs before long digit runs (UUID tail is 12 hex digits)
13
+ /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,
14
+ // Hex digests (git SHAs etc.)
15
+ /\b[0-9a-f]{40}\b/gi,
16
+ /\b[0-9a-f]{64}\b/gi,
17
+ // Epoch-ish numbers (after UUID so 12-digit UUID tails are not partially eaten)
18
+ /\b\d{10,13}\b/g,
19
+ // Absolute / home paths
20
+ /(?:[A-Za-z]:)?(?:\\|\/)(?:Users|home|tmp|var|private)[^\s'"]+/gi,
21
+ /(?:[A-Za-z]:\\|[\\/])[^\s'"]+/g,
22
+ // Secret-like assignments
23
+ /\b(?:token|password|secret|api[_-]?key|authorization)\s*[:=]\s*\S+/gi,
24
+ /Bearer\s+\S+/gi,
25
+ // Run / attempt ids
26
+ /\b(?:run|attempt|job)[-_]?id[=:]\s*\S+/gi,
27
+ // Line numbers that churn
28
+ /:\d+(?::\d+)?/g,
29
+ ];
30
+ /**
31
+ * Normalize a free-form failure message for fingerprinting.
32
+ * Removes volatile identifiers, timestamps, paths, and secret-like values.
33
+ */
34
+ export function normalizeFailureMessage(message) {
35
+ if (message === null || message === undefined)
36
+ return "";
37
+ let out = message.normalize("NFKC");
38
+ for (const re of VOLATILE_PATTERNS) {
39
+ out = out.replace(re, "<redacted>");
40
+ }
41
+ return out
42
+ .toLowerCase()
43
+ .replace(/\s+/g, " ")
44
+ .replace(/[^\w\s.<>=/_-]/g, "")
45
+ .trim()
46
+ .slice(0, 512);
47
+ }
48
+ /**
49
+ * Derive a stable redacted fingerprint from structured failure fields.
50
+ * SHA-256 hex truncated to 32 chars for compact ledger storage.
51
+ */
52
+ export function computeFailureFingerprint(input) {
53
+ const stage = (input.stage ?? "unknown").trim().toLowerCase() || "unknown";
54
+ const code = (input.code ?? "").trim().toLowerCase();
55
+ const resource = (input.resourceClass ?? "").trim().toLowerCase();
56
+ const msg = normalizeFailureMessage(input.message);
57
+ const material = [stage, code, resource, msg].join("|");
58
+ return createHash("sha256").update(material, "utf8").digest("hex").slice(0, 32);
59
+ }
60
+ /**
61
+ * Build a FailureInfo from structured fields.
62
+ * Defaults retryability to `unknown` when not provided.
63
+ */
64
+ export function buildFailureInfo(input) {
65
+ const stage = (input.stage ?? "unknown").trim() || "unknown";
66
+ const code = input.code === null || input.code === undefined || String(input.code).trim() === ""
67
+ ? null
68
+ : String(input.code).trim();
69
+ const resourceClass = input.resourceClass === null ||
70
+ input.resourceClass === undefined ||
71
+ String(input.resourceClass).trim() === ""
72
+ ? null
73
+ : String(input.resourceClass).trim();
74
+ return {
75
+ stage,
76
+ code,
77
+ fingerprint: computeFailureFingerprint({
78
+ stage,
79
+ code,
80
+ message: input.message,
81
+ resourceClass,
82
+ }),
83
+ retryability: input.retryability ?? "unknown",
84
+ resourceClass,
85
+ };
86
+ }
87
+ /** Infer retryability from structured code when adapter does not set it. */
88
+ export function inferRetryability(code) {
89
+ if (code === null || code === undefined || code.trim() === "")
90
+ return "unknown";
91
+ const c = code.trim().toUpperCase();
92
+ if (/TIMEOUT|THROTTLE|RATE_LIMIT|ECONNRESET|ECONNREFUSED|ETIMEDOUT|503|429|UNAVAILABLE|TEMPORARY/.test(c)) {
93
+ return "transient";
94
+ }
95
+ if (/SCHEMA|CONFIG|PERMISSION|AUTHZ|FORBIDDEN|INVALID|VALIDATION|NOT_FOUND|404|401|403|PRECONDITION|INVARIANT/.test(c)) {
96
+ return "deterministic";
97
+ }
98
+ return "unknown";
99
+ }
100
+ //# sourceMappingURL=fingerprint.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Terminal handoff contract for blocked delivery attempts (#3143).
3
+ *
4
+ * Persisted before the worker exits so a successor cannot restart an
5
+ * exhausted loop. Excludes raw secret-bearing logs.
6
+ */
7
+ import type { DeliveryUnitLedger, PreDispatchDecision, ResumeCondition, TerminalHandoff } from "./types.js";
8
+ export declare function nextSafeActionFor(decision: PreDispatchDecision): string;
9
+ export declare function defaultResumeFor(decision: PreDispatchDecision): ResumeCondition;
10
+ export declare function buildTerminalHandoff(options: {
11
+ readonly ledger: DeliveryUnitLedger;
12
+ readonly decision: PreDispatchDecision;
13
+ readonly now?: string;
14
+ readonly overridePermitted?: boolean;
15
+ }): TerminalHandoff;
16
+ /**
17
+ * Human-readable halt report (operator-visible). No secret fields.
18
+ */
19
+ export declare function formatHandoffReport(handoff: TerminalHandoff): string;
20
+ /**
21
+ * Strip any accidental secret-like keys from a handoff-shaped object before persist.
22
+ * Defense in depth — ledger fields are already structured.
23
+ */
24
+ export declare function redactHandoffForPersist(handoff: TerminalHandoff): TerminalHandoff;
25
+ //# sourceMappingURL=handoff.d.ts.map
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Terminal handoff contract for blocked delivery attempts (#3143).
3
+ *
4
+ * Persisted before the worker exits so a successor cannot restart an
5
+ * exhausted loop. Excludes raw secret-bearing logs.
6
+ */
7
+ import { utcIso } from "./types.js";
8
+ export function nextSafeActionFor(decision) {
9
+ switch (decision) {
10
+ case "DENY_DUPLICATE_ACTIVE":
11
+ return "Wait for the active attempt to finish or cancel it; do not start a duplicate dispatch.";
12
+ case "BLOCK_NON_RETRYABLE":
13
+ return "Fix the deterministic configuration/schema/permission failure, record a relevant material delta, then resume.";
14
+ case "BLOCK_NO_MATERIAL_PROGRESS":
15
+ return "Change code/config/evidence that addresses the failing invariant, or record an audited operator override.";
16
+ case "BLOCK_REPEATED_UNKNOWN":
17
+ return "Investigate the unknown failure class with fresh evidence; do not auto-retry until a material delta or override is recorded.";
18
+ case "BLOCK_ATTEMPT_BUDGET":
19
+ return "Phase attempt budget exhausted. Rescope, abandon, or record a bounded operator override with rationale.";
20
+ case "BLOCK_ELAPSED_BUDGET":
21
+ return "Elapsed-time budget exhausted. Suspend-and-wake monitoring preferred over polling; override only with audit.";
22
+ case "BLOCK_TOOL_OR_TOKEN_BUDGET":
23
+ return "Tool-call or token budget exhausted. Halt automatic dispatch; operator decides override or abandon.";
24
+ default:
25
+ return "Halt automatic re-dispatch; review ledger and decide next human action.";
26
+ }
27
+ }
28
+ export function defaultResumeFor(decision) {
29
+ if (decision === "DENY_DUPLICATE_ACTIVE") {
30
+ return {
31
+ kind: "monitor-wake",
32
+ description: "active attempt reaches a terminal status",
33
+ satisfied: false,
34
+ };
35
+ }
36
+ if (decision === "BLOCK_ELAPSED_BUDGET" || decision === "BLOCK_TOOL_OR_TOKEN_BUDGET") {
37
+ return {
38
+ kind: "operator-override",
39
+ description: "audited operator override or phase budget reset",
40
+ satisfied: false,
41
+ };
42
+ }
43
+ return {
44
+ kind: "material-delta",
45
+ description: nextSafeActionFor(decision),
46
+ satisfied: false,
47
+ };
48
+ }
49
+ export function buildTerminalHandoff(options) {
50
+ const { ledger, decision } = options;
51
+ const fingerprint = ledger.lastFailure?.fingerprint ?? null;
52
+ const sameFailureCount = fingerprint !== null ? (ledger.sameFailureCounts[fingerprint] ?? 0) : 0;
53
+ const resume = ledger.resumeCondition ?? defaultResumeFor(decision);
54
+ return {
55
+ schemaVersion: 1,
56
+ scopeId: ledger.scopeId,
57
+ targetId: ledger.targetId,
58
+ workflowId: ledger.workflowId,
59
+ lastSourceRevision: ledger.lastSourceRevision,
60
+ failure: ledger.lastFailure,
61
+ totalAttempts: ledger.attempts.length,
62
+ failedAttemptCount: ledger.failedAttemptCount,
63
+ sameFailureCount,
64
+ elapsedSeconds: ledger.totalElapsedSeconds,
65
+ toolCallCount: ledger.totalToolCallCount,
66
+ hostTokenCount: ledger.totalHostTokenCount,
67
+ lastMaterialDelta: ledger.lastMaterialDelta,
68
+ denyReason: decision,
69
+ nextSafeAction: nextSafeActionFor(decision),
70
+ resumeCondition: resume,
71
+ overridePermitted: options.overridePermitted !== false,
72
+ recordedAt: utcIso(options.now),
73
+ };
74
+ }
75
+ /**
76
+ * Human-readable halt report (operator-visible). No secret fields.
77
+ */
78
+ export function formatHandoffReport(handoff) {
79
+ const lines = [
80
+ "BLOCKED: delivery-attempt circuit breaker (#3143)",
81
+ `scope=${handoff.scopeId} target=${handoff.targetId} workflow=${handoff.workflowId}`,
82
+ `revision=${handoff.lastSourceRevision ?? "n/a"}`,
83
+ `decision=${handoff.denyReason}`,
84
+ `attempts=${handoff.totalAttempts} failed=${handoff.failedAttemptCount} sameFailure=${handoff.sameFailureCount}`,
85
+ `elapsedSeconds=${handoff.elapsedSeconds} toolCalls=${handoff.toolCallCount} hostTokens=${handoff.hostTokenCount ?? "n/a"}`,
86
+ ];
87
+ if (handoff.failure) {
88
+ lines.push(`failure.stage=${handoff.failure.stage} code=${handoff.failure.code ?? "n/a"} retryability=${handoff.failure.retryability}`, `failure.fingerprint=${handoff.failure.fingerprint}`);
89
+ }
90
+ lines.push(`materialDelta=${handoff.lastMaterialDelta.map((d) => d.kind).join(",") || "none"}`, `nextSafeAction=${handoff.nextSafeAction}`, `resume=${handoff.resumeCondition.kind}: ${handoff.resumeCondition.description}`, `overridePermitted=${handoff.overridePermitted}`);
91
+ return lines.join("\n");
92
+ }
93
+ /**
94
+ * Strip any accidental secret-like keys from a handoff-shaped object before persist.
95
+ * Defense in depth — ledger fields are already structured.
96
+ */
97
+ export function redactHandoffForPersist(handoff) {
98
+ // TerminalHandoff has no free-form log fields; return as-is for type stability.
99
+ // Callers must not attach raw logs to this object.
100
+ return handoff;
101
+ }
102
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Delivery-attempt material-progress circuit breaker (#3143).
3
+ *
4
+ * Deterministic pre-dispatch gate + durable unit ledger for autonomous
5
+ * delivery and operational-acceptance loops. Implements the mechanical
6
+ * enforcement surface for the delivery/acceptance subset of dual-stop (#2442).
7
+ *
8
+ * @see content/docs/delivery-attempt.md
9
+ */
10
+ export { type BeginAttemptOnDiskInput, beginAttemptOnDisk, type CompleteAttemptOnDiskInput, completeAttemptOnDisk, } from "./disk-begin.js";
11
+ export { evaluateAndPrepareBlock, evaluatePreDispatch, } from "./evaluate.js";
12
+ export { buildFailureInfo, computeFailureFingerprint, type FingerprintInput, inferRetryability, normalizeFailureMessage, } from "./fingerprint.js";
13
+ export { buildTerminalHandoff, defaultResumeFor, formatHandoffReport, nextSafeActionFor, redactHandoffForPersist, } from "./handoff.js";
14
+ export { activeAttempts, beginAttempt, clearBlockIfResumed, completeAttempt, deliveryAttemptsDir, emptyUnitLedger, hasActiveAttempt, isUnitLockReclaimable, type LoadUnitLedgerResult, listUnitLedgers, loadOrCreateUnitLedger, loadUnitLedger, loadUnitLedgerResult, MemoryLedgerStore, markBlocked, newAttemptId, parseUnitLedger, recordOperatorOverride, saveUnitLedger, UNIT_LOCK_STALE_MS, unitLedgerFilename, unitLedgerPath, withUnitLock, } from "./ledger.js";
15
+ export { evaluateMaterialProgress, isRevisionChangeMaterial, type MaterialProgressResult, } from "./material-delta.js";
16
+ export { ATTEMPT_STATUSES, ATTEMPT_TRIGGERS, type AttemptStatus, type AttemptTrigger, DEFAULT_DELIVERY_BUDGET_POLICY, DELIVERY_ATTEMPT_DIR, DELIVERY_ATTEMPT_SCHEMA_VERSION, type DeliveryAttemptRecord, type DeliveryBudgetPolicy, type DeliveryUnitLedger, deliveryUnitKey, type FailureInfo, isAllowDecision, isBlockDecision, isDenyDecision, MATERIAL_DELTA_KINDS, type MaterialDeltaClaim, type MaterialDeltaKind, mergePolicy, type OperatorOverride, PRE_DISPATCH_DECISIONS, type PreDispatchDecision, type PreDispatchDecisionEvent, type PreDispatchInput, type PreDispatchResult, RETRYABILITY, type ResumeCondition, type Retryability, type TerminalHandoff, utcIso, } from "./types.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Delivery-attempt material-progress circuit breaker (#3143).
3
+ *
4
+ * Deterministic pre-dispatch gate + durable unit ledger for autonomous
5
+ * delivery and operational-acceptance loops. Implements the mechanical
6
+ * enforcement surface for the delivery/acceptance subset of dual-stop (#2442).
7
+ *
8
+ * @see content/docs/delivery-attempt.md
9
+ */
10
+ export { beginAttemptOnDisk, completeAttemptOnDisk, } from "./disk-begin.js";
11
+ export { evaluateAndPrepareBlock, evaluatePreDispatch, } from "./evaluate.js";
12
+ export { buildFailureInfo, computeFailureFingerprint, inferRetryability, normalizeFailureMessage, } from "./fingerprint.js";
13
+ export { buildTerminalHandoff, defaultResumeFor, formatHandoffReport, nextSafeActionFor, redactHandoffForPersist, } from "./handoff.js";
14
+ export { activeAttempts, beginAttempt, clearBlockIfResumed, completeAttempt, deliveryAttemptsDir, emptyUnitLedger, hasActiveAttempt, isUnitLockReclaimable, listUnitLedgers, loadOrCreateUnitLedger, loadUnitLedger, loadUnitLedgerResult, MemoryLedgerStore, markBlocked, newAttemptId, parseUnitLedger, recordOperatorOverride, saveUnitLedger, UNIT_LOCK_STALE_MS, unitLedgerFilename, unitLedgerPath, withUnitLock, } from "./ledger.js";
15
+ export { evaluateMaterialProgress, isRevisionChangeMaterial, } from "./material-delta.js";
16
+ export { ATTEMPT_STATUSES, ATTEMPT_TRIGGERS, DEFAULT_DELIVERY_BUDGET_POLICY, DELIVERY_ATTEMPT_DIR, DELIVERY_ATTEMPT_SCHEMA_VERSION, deliveryUnitKey, isAllowDecision, isBlockDecision, isDenyDecision, MATERIAL_DELTA_KINDS, mergePolicy, PRE_DISPATCH_DECISIONS, RETRYABILITY, utcIso, } from "./types.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Durable delivery-attempt unit ledger (#3143).
3
+ *
4
+ * Persists under `.deft/delivery-attempts/` so counters survive worker
5
+ * replacement, session restart, context compaction, and new revisions.
6
+ */
7
+ import type { AttemptTrigger, DeliveryAttemptRecord, DeliveryUnitLedger, FailureInfo, MaterialDeltaClaim, PreDispatchDecision, ResumeCondition } from "./types.js";
8
+ export declare function deliveryAttemptsDir(projectRoot: string): string;
9
+ /**
10
+ * Stable collision-resistant filename for a unit key.
11
+ * Full SHA-256 hex (64 chars) — do not truncate base64 of the raw key (#3143 P1).
12
+ */
13
+ export declare function unitLedgerFilename(scopeId: string, targetId: string, workflowId: string): string;
14
+ export declare function unitLedgerPath(projectRoot: string, scopeId: string, targetId: string, workflowId: string): string;
15
+ export declare function newAttemptId(prefix?: string): string;
16
+ export declare function emptyUnitLedger(input: {
17
+ readonly scopeId: string;
18
+ readonly targetId: string;
19
+ readonly workflowId: string;
20
+ readonly phaseId?: string;
21
+ readonly now?: string;
22
+ }): DeliveryUnitLedger;
23
+ /**
24
+ * Parse a unit ledger from JSON. Returns null when required fields are invalid.
25
+ */
26
+ export declare function parseUnitLedger(raw: unknown): DeliveryUnitLedger | null;
27
+ export type LoadUnitLedgerResult = {
28
+ readonly status: "missing";
29
+ } | {
30
+ readonly status: "ok";
31
+ readonly ledger: DeliveryUnitLedger;
32
+ } | {
33
+ readonly status: "corrupt";
34
+ readonly detail: string;
35
+ };
36
+ /**
37
+ * Load unit ledger distinguishing missing vs corrupt.
38
+ * Corrupt persisted state MUST NOT be treated as a new empty unit (fail-closed).
39
+ */
40
+ export declare function loadUnitLedgerResult(projectRoot: string, scopeId: string, targetId: string, workflowId: string): LoadUnitLedgerResult;
41
+ /**
42
+ * Load unit ledger from disk, or null if missing.
43
+ * Throws when the file exists but is corrupt (fail-closed circuit breaker).
44
+ */
45
+ export declare function loadUnitLedger(projectRoot: string, scopeId: string, targetId: string, workflowId: string): DeliveryUnitLedger | null;
46
+ /** Persist unit ledger atomically under project root. */
47
+ export declare function saveUnitLedger(projectRoot: string, ledger: DeliveryUnitLedger): void;
48
+ /**
49
+ * Exclusive unit lock for begin/complete on disk (#3143 concurrent-snapshot P1).
50
+ * Uses O_EXCL create of a lock file under the delivery-attempts dir.
51
+ */
52
+ /** @deprecated Kept for API stability; live PIDs are never time-reclaimed. */
53
+ export declare const UNIT_LOCK_STALE_MS: number;
54
+ interface UnitLockRecord {
55
+ readonly pid: number;
56
+ readonly token: string;
57
+ readonly startedAt: string;
58
+ }
59
+ /**
60
+ * Whether an existing lock may be reclaimed.
61
+ *
62
+ * **Live owner PID is never reclaimed** — including long critical sections and
63
+ * event-loop stalls. Only dead owners and corrupt/unreadable records are
64
+ * reclaimable. PID-reuse residual (dead owner, OS recycled the number onto an
65
+ * unrelated live process) requires manual `.lock` deletion.
66
+ */
67
+ export declare function isUnitLockReclaimable(rec: UnitLockRecord | null): boolean;
68
+ /**
69
+ * Acquire exclusive unit lock.
70
+ *
71
+ * Create is atomic: `containedWrite(..., mode: "create")` (O_EXCL) writes the
72
+ * owner record in the exclusive create (no empty-file window).
73
+ *
74
+ * Recovery when EEXIST (dead owner or corrupt record only):
75
+ * 1. Take an exclusive **reclaim ticket** (`*.lock.reclaim`) with create mode.
76
+ * 2. Under that ticket, re-read the lock; only unlink if still reclaimable
77
+ * (owner still dead / corrupt). Live PIDs are never unlinked.
78
+ * 3. Create the replacement lock with create mode, then drop the ticket.
79
+ *
80
+ * The ticket serializes reclaimers so a delayed contender cannot unlink a
81
+ * live replacement lock.
82
+ */
83
+ export declare function withUnitLock<T>(projectRoot: string, scopeId: string, targetId: string, workflowId: string, fn: () => T, options?: {
84
+ readonly nowMs?: number;
85
+ readonly staleMs?: number;
86
+ }): T;
87
+ /**
88
+ * Load or create empty unit ledger.
89
+ * Creates only when the file is missing — never on corrupt state.
90
+ */
91
+ export declare function loadOrCreateUnitLedger(projectRoot: string, input: {
92
+ readonly scopeId: string;
93
+ readonly targetId: string;
94
+ readonly workflowId: string;
95
+ readonly phaseId?: string;
96
+ readonly now?: string;
97
+ }): DeliveryUnitLedger;
98
+ export declare function listUnitLedgers(projectRoot: string): DeliveryUnitLedger[];
99
+ export declare function activeAttempts(ledger: DeliveryUnitLedger): readonly DeliveryAttemptRecord[];
100
+ export declare function hasActiveAttempt(ledger: DeliveryUnitLedger): boolean;
101
+ /**
102
+ * Open a new queued/running attempt. Caller must have passed evaluatePreDispatch.
103
+ * Does not re-check the gate (orchestrators own the order: evaluate → begin).
104
+ */
105
+ export declare function beginAttempt(ledger: DeliveryUnitLedger, input: {
106
+ readonly attemptId?: string;
107
+ readonly sourceRevision: string;
108
+ readonly trigger: AttemptTrigger;
109
+ readonly status?: "queued" | "running";
110
+ readonly workerId?: string | null;
111
+ readonly externalRunId?: string | null;
112
+ readonly materialDelta?: readonly MaterialDeltaClaim[];
113
+ readonly now?: string;
114
+ /**
115
+ * When true, decrement override.remainingAttempts (ALLOW_OVERRIDE path).
116
+ * Also set when trigger is `"override"`. Ordinary automatic/retry begins
117
+ * that do not need override MUST leave this false so quota is preserved.
118
+ */
119
+ readonly consumeOverride?: boolean;
120
+ }): {
121
+ ledger: DeliveryUnitLedger;
122
+ attempt: DeliveryAttemptRecord;
123
+ };
124
+ /**
125
+ * Record terminal outcome for an attempt. Increments budgets / sameFailureCounts
126
+ * on failure. Idempotent when the same externalRunId is already terminal.
127
+ */
128
+ export declare function completeAttempt(ledger: DeliveryUnitLedger, input: {
129
+ readonly attemptId?: string;
130
+ readonly externalRunId?: string | null;
131
+ readonly status: "succeeded" | "failed" | "cancelled" | "blocked";
132
+ readonly failure?: FailureInfo | null;
133
+ readonly materialDelta?: readonly MaterialDeltaClaim[];
134
+ readonly elapsedSeconds?: number;
135
+ readonly toolCallCount?: number;
136
+ readonly hostTokenCount?: number | null;
137
+ readonly now?: string;
138
+ }): DeliveryUnitLedger;
139
+ /** Mark unit blocked with resume condition (persisted before worker exit). */
140
+ export declare function markBlocked(ledger: DeliveryUnitLedger, decision: PreDispatchDecision, resume: ResumeCondition, now?: string): DeliveryUnitLedger;
141
+ /** Clear block when resume condition is satisfied (caller sets satisfied=true). */
142
+ export declare function clearBlockIfResumed(ledger: DeliveryUnitLedger, now?: string): DeliveryUnitLedger;
143
+ /**
144
+ * Record an audited operator override. Preserves full attempt history.
145
+ * remainingAttempts starts at allowedAttempts.
146
+ */
147
+ export declare function recordOperatorOverride(ledger: DeliveryUnitLedger, input: {
148
+ readonly actor: string;
149
+ readonly rationale: string;
150
+ readonly allowedAttempts?: number;
151
+ readonly expiresAt?: string | null;
152
+ readonly now?: string;
153
+ }): DeliveryUnitLedger;
154
+ /** In-memory ledger store for tests and pure evaluation paths. */
155
+ export declare class MemoryLedgerStore {
156
+ private readonly map;
157
+ get(scopeId: string, targetId: string, workflowId: string): DeliveryUnitLedger | null;
158
+ set(ledger: DeliveryUnitLedger): void;
159
+ getOrCreate(input: {
160
+ readonly scopeId: string;
161
+ readonly targetId: string;
162
+ readonly workflowId: string;
163
+ readonly phaseId?: string;
164
+ readonly now?: string;
165
+ }): DeliveryUnitLedger;
166
+ clear(): void;
167
+ }
168
+ export {};
169
+ //# sourceMappingURL=ledger.d.ts.map