omk-adaptorch-wpl 0.91.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 (90) hide show
  1. package/README.md +49 -0
  2. package/dist/adaptorch-client.d.ts +184 -0
  3. package/dist/adaptorch-client.d.ts.map +1 -0
  4. package/dist/adaptorch-client.js +109 -0
  5. package/dist/adaptorch-client.js.map +1 -0
  6. package/dist/adjudicator-registry.d.ts +93 -0
  7. package/dist/adjudicator-registry.d.ts.map +1 -0
  8. package/dist/adjudicator-registry.js +89 -0
  9. package/dist/adjudicator-registry.js.map +1 -0
  10. package/dist/adjudicator.d.ts +73 -0
  11. package/dist/adjudicator.d.ts.map +1 -0
  12. package/dist/adjudicator.js +356 -0
  13. package/dist/adjudicator.js.map +1 -0
  14. package/dist/b2c-mapper.d.ts +25 -0
  15. package/dist/b2c-mapper.d.ts.map +1 -0
  16. package/dist/b2c-mapper.js +186 -0
  17. package/dist/b2c-mapper.js.map +1 -0
  18. package/dist/b2c-verdict.d.ts +66 -0
  19. package/dist/b2c-verdict.d.ts.map +1 -0
  20. package/dist/b2c-verdict.js +5 -0
  21. package/dist/b2c-verdict.js.map +1 -0
  22. package/dist/deep-wall.d.ts +80 -0
  23. package/dist/deep-wall.d.ts.map +1 -0
  24. package/dist/deep-wall.js +97 -0
  25. package/dist/deep-wall.js.map +1 -0
  26. package/dist/evaluate-correctness-wall.d.ts +35 -0
  27. package/dist/evaluate-correctness-wall.d.ts.map +1 -0
  28. package/dist/evaluate-correctness-wall.js +128 -0
  29. package/dist/evaluate-correctness-wall.js.map +1 -0
  30. package/dist/in-memory-adaptorch.d.ts +12 -0
  31. package/dist/in-memory-adaptorch.d.ts.map +1 -0
  32. package/dist/in-memory-adaptorch.js +26 -0
  33. package/dist/in-memory-adaptorch.js.map +1 -0
  34. package/dist/index.d.ts +46 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +28 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/live-adaptorch-transport.d.ts +14 -0
  39. package/dist/live-adaptorch-transport.d.ts.map +1 -0
  40. package/dist/live-adaptorch-transport.js +19 -0
  41. package/dist/live-adaptorch-transport.js.map +1 -0
  42. package/dist/loop.d.ts +149 -0
  43. package/dist/loop.d.ts.map +1 -0
  44. package/dist/loop.js +219 -0
  45. package/dist/loop.js.map +1 -0
  46. package/dist/mcp-introspection-transport.d.ts +18 -0
  47. package/dist/mcp-introspection-transport.d.ts.map +1 -0
  48. package/dist/mcp-introspection-transport.js +29 -0
  49. package/dist/mcp-introspection-transport.js.map +1 -0
  50. package/dist/policy-wall.d.ts +34 -0
  51. package/dist/policy-wall.d.ts.map +1 -0
  52. package/dist/policy-wall.js +125 -0
  53. package/dist/policy-wall.js.map +1 -0
  54. package/dist/receipt-signature.d.ts +28 -0
  55. package/dist/receipt-signature.d.ts.map +1 -0
  56. package/dist/receipt-signature.js +30 -0
  57. package/dist/receipt-signature.js.map +1 -0
  58. package/dist/regenerate-packet.d.ts +25 -0
  59. package/dist/regenerate-packet.d.ts.map +1 -0
  60. package/dist/regenerate-packet.js +29 -0
  61. package/dist/regenerate-packet.js.map +1 -0
  62. package/dist/repair-budget.d.ts +24 -0
  63. package/dist/repair-budget.d.ts.map +1 -0
  64. package/dist/repair-budget.js +44 -0
  65. package/dist/repair-budget.js.map +1 -0
  66. package/dist/repair-loop.d.ts +18 -0
  67. package/dist/repair-loop.d.ts.map +1 -0
  68. package/dist/repair-loop.js +54 -0
  69. package/dist/repair-loop.js.map +1 -0
  70. package/dist/retry-backoff.d.ts +56 -0
  71. package/dist/retry-backoff.d.ts.map +1 -0
  72. package/dist/retry-backoff.js +75 -0
  73. package/dist/retry-backoff.js.map +1 -0
  74. package/dist/signed-receipt.d.ts +29 -0
  75. package/dist/signed-receipt.d.ts.map +1 -0
  76. package/dist/signed-receipt.js +25 -0
  77. package/dist/signed-receipt.js.map +1 -0
  78. package/dist/state-machine.d.ts +40 -0
  79. package/dist/state-machine.d.ts.map +1 -0
  80. package/dist/state-machine.js +88 -0
  81. package/dist/state-machine.js.map +1 -0
  82. package/dist/types.d.ts +149 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +16 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist/wall-meta.d.ts +3 -0
  87. package/dist/wall-meta.d.ts.map +1 -0
  88. package/dist/wall-meta.js +3 -0
  89. package/dist/wall-meta.js.map +1 -0
  90. package/package.json +46 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regenerate-packet.d.ts","sourceRoot":"","sources":["../src/regenerate-packet.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA2B,SAAQ,eAAe;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,OAAO,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,gBAAgB,CAmBzF","sourcesContent":["/**\n * Structured capped-regenerate packet (hints only — no LLM runner in Wave 3).\n */\n\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\nimport { capRepairHints, parseRepairBudget, shouldOfferRepair } from \"./repair-budget.ts\";\nimport type { RepairHintInput } from \"./repair-loop.ts\";\nimport { deriveRepairHints } from \"./repair-loop.ts\";\n\nexport interface RegeneratePacket {\n\tenabled: boolean;\n\tverdict: UserVerdict;\n\tattemptCount: number;\n\tbudget: number;\n\tbudgetRemaining: number;\n\thints: string[];\n\tmessage: string;\n}\n\nexport interface BuildRegeneratePacketInput extends RepairHintInput {\n\tattemptCount: number;\n\tbudgetOverride?: number;\n\tautoRegenerateEnabled: boolean;\n}\n\n/**\n * When auto-regenerate is enabled and verdict is BLOCKED with budget headroom,\n * returns capped hints for operator or downstream agent — does not apply patches.\n */\nexport function buildRegeneratePacket(input: BuildRegeneratePacketInput): RegeneratePacket {\n\tconst budget = parseRepairBudget(input.budgetOverride);\n\tconst rawHints = deriveRepairHints(input);\n\tconst hints = capRepairHints(rawHints, budget);\n\tconst offer = shouldOfferRepair(input.userVerdict, input.attemptCount, budget);\n\tconst enabled = input.autoRegenerateEnabled && offer && hints.length > 0;\n\tconst budgetRemaining = Math.max(0, budget - input.attemptCount);\n\n\treturn {\n\t\tenabled,\n\t\tverdict: input.userVerdict,\n\t\tattemptCount: input.attemptCount,\n\t\tbudget,\n\t\tbudgetRemaining,\n\t\thints: enabled ? hints : [],\n\t\tmessage: enabled\n\t\t\t? \"Capped regenerate packet ready (hints only; re-run wall after editing).\"\n\t\t\t: \"Auto-regenerate not offered (disabled, non-BLOCKED verdict, or repair budget exhausted).\",\n\t};\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Structured capped-regenerate packet (hints only — no LLM runner in Wave 3).
3
+ */
4
+ import { capRepairHints, parseRepairBudget, shouldOfferRepair } from "./repair-budget.js";
5
+ import { deriveRepairHints } from "./repair-loop.js";
6
+ /**
7
+ * When auto-regenerate is enabled and verdict is BLOCKED with budget headroom,
8
+ * returns capped hints for operator or downstream agent — does not apply patches.
9
+ */
10
+ export function buildRegeneratePacket(input) {
11
+ const budget = parseRepairBudget(input.budgetOverride);
12
+ const rawHints = deriveRepairHints(input);
13
+ const hints = capRepairHints(rawHints, budget);
14
+ const offer = shouldOfferRepair(input.userVerdict, input.attemptCount, budget);
15
+ const enabled = input.autoRegenerateEnabled && offer && hints.length > 0;
16
+ const budgetRemaining = Math.max(0, budget - input.attemptCount);
17
+ return {
18
+ enabled,
19
+ verdict: input.userVerdict,
20
+ attemptCount: input.attemptCount,
21
+ budget,
22
+ budgetRemaining,
23
+ hints: enabled ? hints : [],
24
+ message: enabled
25
+ ? "Capped regenerate packet ready (hints only; re-run wall after editing)."
26
+ : "Auto-regenerate not offered (disabled, non-BLOCKED verdict, or repair budget exhausted).",
27
+ };
28
+ }
29
+ //# sourceMappingURL=regenerate-packet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regenerate-packet.js","sourceRoot":"","sources":["../src/regenerate-packet.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAkBrD;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiC,EAAoB;IAC1F,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC/E,MAAM,OAAO,GAAG,KAAK,CAAC,qBAAqB,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACzE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IAEjE,OAAO;QACN,OAAO;QACP,OAAO,EAAE,KAAK,CAAC,WAAW;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,MAAM;QACN,eAAe;QACf,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QAC3B,OAAO,EAAE,OAAO;YACf,CAAC,CAAC,yEAAyE;YAC3E,CAAC,CAAC,0FAA0F;KAC7F,CAAC;AAAA,CACF","sourcesContent":["/**\n * Structured capped-regenerate packet (hints only — no LLM runner in Wave 3).\n */\n\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\nimport { capRepairHints, parseRepairBudget, shouldOfferRepair } from \"./repair-budget.ts\";\nimport type { RepairHintInput } from \"./repair-loop.ts\";\nimport { deriveRepairHints } from \"./repair-loop.ts\";\n\nexport interface RegeneratePacket {\n\tenabled: boolean;\n\tverdict: UserVerdict;\n\tattemptCount: number;\n\tbudget: number;\n\tbudgetRemaining: number;\n\thints: string[];\n\tmessage: string;\n}\n\nexport interface BuildRegeneratePacketInput extends RepairHintInput {\n\tattemptCount: number;\n\tbudgetOverride?: number;\n\tautoRegenerateEnabled: boolean;\n}\n\n/**\n * When auto-regenerate is enabled and verdict is BLOCKED with budget headroom,\n * returns capped hints for operator or downstream agent — does not apply patches.\n */\nexport function buildRegeneratePacket(input: BuildRegeneratePacketInput): RegeneratePacket {\n\tconst budget = parseRepairBudget(input.budgetOverride);\n\tconst rawHints = deriveRepairHints(input);\n\tconst hints = capRepairHints(rawHints, budget);\n\tconst offer = shouldOfferRepair(input.userVerdict, input.attemptCount, budget);\n\tconst enabled = input.autoRegenerateEnabled && offer && hints.length > 0;\n\tconst budgetRemaining = Math.max(0, budget - input.attemptCount);\n\n\treturn {\n\t\tenabled,\n\t\tverdict: input.userVerdict,\n\t\tattemptCount: input.attemptCount,\n\t\tbudget,\n\t\tbudgetRemaining,\n\t\thints: enabled ? hints : [],\n\t\tmessage: enabled\n\t\t\t? \"Capped regenerate packet ready (hints only; re-run wall after editing).\"\n\t\t\t: \"Auto-regenerate not offered (disabled, non-BLOCKED verdict, or repair budget exhausted).\",\n\t};\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Capped regenerate repair budget (attempt counting vs env/config budget).
3
+ */
4
+ import type { UserVerdict } from "./b2c-verdict.ts";
5
+ export interface RepairBudgetState {
6
+ packetKey: string;
7
+ attempts: number;
8
+ lastVerdict: UserVerdict;
9
+ }
10
+ /**
11
+ * Parse a non-negative repair budget cap. Uses `override` when provided; otherwise reads
12
+ * `OMK_WALL_REPAIR_BUDGET` from the environment (invalid/missing → default 1).
13
+ */
14
+ export declare function parseRepairBudget(override?: number): number;
15
+ /**
16
+ * Whether another capped regenerate/repair offer is allowed for this packet.
17
+ * Offers while `attemptCount` is strictly below `budget` (0-based attempts consumed).
18
+ */
19
+ export declare function shouldOfferRepair(verdict: UserVerdict, attemptCount: number, budget: number): boolean;
20
+ /**
21
+ * Cap a list of repair hint strings for display (capped regenerate algorithm).
22
+ */
23
+ export declare function capRepairHints(hints: string[], budget: number): string[];
24
+ //# sourceMappingURL=repair-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-budget.d.ts","sourceRoot":"","sources":["../src/repair-budget.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;CACzB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAU3D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAKrG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAIxE","sourcesContent":["/**\n * Capped regenerate repair budget (attempt counting vs env/config budget).\n */\n\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\n\nexport interface RepairBudgetState {\n\tpacketKey: string;\n\tattempts: number;\n\tlastVerdict: UserVerdict;\n}\n\nconst DEFAULT_REPAIR_BUDGET = 1;\n\n/**\n * Parse a non-negative repair budget cap. Uses `override` when provided; otherwise reads\n * `OMK_WALL_REPAIR_BUDGET` from the environment (invalid/missing → default 1).\n */\nexport function parseRepairBudget(override?: number): number {\n\tif (override !== undefined) {\n\t\tif (!Number.isFinite(override) || override < 0) return DEFAULT_REPAIR_BUDGET;\n\t\treturn Math.floor(override);\n\t}\n\tconst raw = process.env.OMK_WALL_REPAIR_BUDGET?.trim();\n\tif (!raw) return DEFAULT_REPAIR_BUDGET;\n\tconst n = Number.parseInt(raw, 10);\n\tif (!Number.isFinite(n) || n < 0) return DEFAULT_REPAIR_BUDGET;\n\treturn n;\n}\n\n/**\n * Whether another capped regenerate/repair offer is allowed for this packet.\n * Offers while `attemptCount` is strictly below `budget` (0-based attempts consumed).\n */\nexport function shouldOfferRepair(verdict: UserVerdict, attemptCount: number, budget: number): boolean {\n\tif (verdict !== \"BLOCKED\") return false;\n\tconst cap = parseRepairBudget(budget);\n\tif (cap === 0) return false;\n\treturn attemptCount < cap;\n}\n\n/**\n * Cap a list of repair hint strings for display (capped regenerate algorithm).\n */\nexport function capRepairHints(hints: string[], budget: number): string[] {\n\tconst cap = parseRepairBudget(budget);\n\tif (cap === 0 || hints.length === 0) return [];\n\treturn hints.slice(0, cap);\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Capped regenerate repair budget (attempt counting vs env/config budget).
3
+ */
4
+ const DEFAULT_REPAIR_BUDGET = 1;
5
+ /**
6
+ * Parse a non-negative repair budget cap. Uses `override` when provided; otherwise reads
7
+ * `OMK_WALL_REPAIR_BUDGET` from the environment (invalid/missing → default 1).
8
+ */
9
+ export function parseRepairBudget(override) {
10
+ if (override !== undefined) {
11
+ if (!Number.isFinite(override) || override < 0)
12
+ return DEFAULT_REPAIR_BUDGET;
13
+ return Math.floor(override);
14
+ }
15
+ const raw = process.env.OMK_WALL_REPAIR_BUDGET?.trim();
16
+ if (!raw)
17
+ return DEFAULT_REPAIR_BUDGET;
18
+ const n = Number.parseInt(raw, 10);
19
+ if (!Number.isFinite(n) || n < 0)
20
+ return DEFAULT_REPAIR_BUDGET;
21
+ return n;
22
+ }
23
+ /**
24
+ * Whether another capped regenerate/repair offer is allowed for this packet.
25
+ * Offers while `attemptCount` is strictly below `budget` (0-based attempts consumed).
26
+ */
27
+ export function shouldOfferRepair(verdict, attemptCount, budget) {
28
+ if (verdict !== "BLOCKED")
29
+ return false;
30
+ const cap = parseRepairBudget(budget);
31
+ if (cap === 0)
32
+ return false;
33
+ return attemptCount < cap;
34
+ }
35
+ /**
36
+ * Cap a list of repair hint strings for display (capped regenerate algorithm).
37
+ */
38
+ export function capRepairHints(hints, budget) {
39
+ const cap = parseRepairBudget(budget);
40
+ if (cap === 0 || hints.length === 0)
41
+ return [];
42
+ return hints.slice(0, cap);
43
+ }
44
+ //# sourceMappingURL=repair-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-budget.js","sourceRoot":"","sources":["../src/repair-budget.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAiB,EAAU;IAC5D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC;YAAE,OAAO,qBAAqB,CAAC;QAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,qBAAqB,CAAC;IACvC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,qBAAqB,CAAC;IAC/D,OAAO,CAAC,CAAC;AAAA,CACT;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAoB,EAAE,YAAoB,EAAE,MAAc,EAAW;IACtG,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,YAAY,GAAG,GAAG,CAAC;AAAA,CAC1B;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAe,EAAE,MAAc,EAAY;IACzE,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAAA,CAC3B","sourcesContent":["/**\n * Capped regenerate repair budget (attempt counting vs env/config budget).\n */\n\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\n\nexport interface RepairBudgetState {\n\tpacketKey: string;\n\tattempts: number;\n\tlastVerdict: UserVerdict;\n}\n\nconst DEFAULT_REPAIR_BUDGET = 1;\n\n/**\n * Parse a non-negative repair budget cap. Uses `override` when provided; otherwise reads\n * `OMK_WALL_REPAIR_BUDGET` from the environment (invalid/missing → default 1).\n */\nexport function parseRepairBudget(override?: number): number {\n\tif (override !== undefined) {\n\t\tif (!Number.isFinite(override) || override < 0) return DEFAULT_REPAIR_BUDGET;\n\t\treturn Math.floor(override);\n\t}\n\tconst raw = process.env.OMK_WALL_REPAIR_BUDGET?.trim();\n\tif (!raw) return DEFAULT_REPAIR_BUDGET;\n\tconst n = Number.parseInt(raw, 10);\n\tif (!Number.isFinite(n) || n < 0) return DEFAULT_REPAIR_BUDGET;\n\treturn n;\n}\n\n/**\n * Whether another capped regenerate/repair offer is allowed for this packet.\n * Offers while `attemptCount` is strictly below `budget` (0-based attempts consumed).\n */\nexport function shouldOfferRepair(verdict: UserVerdict, attemptCount: number, budget: number): boolean {\n\tif (verdict !== \"BLOCKED\") return false;\n\tconst cap = parseRepairBudget(budget);\n\tif (cap === 0) return false;\n\treturn attemptCount < cap;\n}\n\n/**\n * Cap a list of repair hint strings for display (capped regenerate algorithm).\n */\nexport function capRepairHints(hints: string[], budget: number): string[] {\n\tconst cap = parseRepairBudget(budget);\n\tif (cap === 0 || hints.length === 0) return [];\n\treturn hints.slice(0, cap);\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Derive user-facing repair hints from wall verdict inputs (batch-1, no runner execution).
3
+ */
4
+ import type { AdjudicationReasonCode } from "./adjudicator-registry.ts";
5
+ import type { UserVerdict } from "./b2c-verdict.ts";
6
+ import type { PolicyFlag } from "./policy-wall.ts";
7
+ export interface RepairHintInput {
8
+ userVerdict: UserVerdict;
9
+ policyFlags: PolicyFlag[];
10
+ adjudicationReasonCode?: AdjudicationReasonCode;
11
+ previewOnly: boolean;
12
+ diffPaths: string[];
13
+ }
14
+ /**
15
+ * Produce ordered, de-duplicated repair hints for the verdict card.
16
+ */
17
+ export declare function deriveRepairHints(input: RepairHintInput): string[];
18
+ //# sourceMappingURL=repair-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-loop.d.ts","sourceRoot":"","sources":["../src/repair-loop.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;CACpB;AAeD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE,CAqClE","sourcesContent":["/**\n * Derive user-facing repair hints from wall verdict inputs (batch-1, no runner execution).\n */\n\nimport type { AdjudicationReasonCode } from \"./adjudicator-registry.ts\";\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\nimport type { PolicyFlag } from \"./policy-wall.ts\";\nimport { POLICY_FLAG } from \"./policy-wall.ts\";\n\nexport interface RepairHintInput {\n\tuserVerdict: UserVerdict;\n\tpolicyFlags: PolicyFlag[];\n\tadjudicationReasonCode?: AdjudicationReasonCode;\n\tpreviewOnly: boolean;\n\tdiffPaths: string[];\n}\n\nconst REASON_REPAIR: Partial<Record<AdjudicationReasonCode, string>> = {\n\tSCOPE_VIOLATION: \"Narrow the change to approved write paths only, then re-run the wall.\",\n\tSCHEMA_DRIFT: \"Align outputs with the expected schema contract before resubmitting.\",\n\tCONTENT_CHECK_FAILED: \"Fix failing content checks on artifacts, then re-adjudicate.\",\n\tNO_EVIDENCE_ON_SUCCESS: \"Attach terminal run evidence before claiming success.\",\n\tEMPTY_ARTIFACT_CONTENT: \"Ensure artifacts contain substantive content, not placeholders.\",\n\tTRACE_CHECK_FAILED: \"Resolve trace verification failures on the reported run.\",\n\tTRACE_ERROR_SPAN: \"Clear error spans under a success status or mark the run failed.\",\n\tMIN_ACTIONS_UNMET: \"Record the minimum required actions for this kind.\",\n\tRUN_FETCH_FAILED: \"Retry fetching run evidence when the runner is available.\",\n\tEVIDENCE_EMPTY: \"Widen verification scope or wait for terminal run artifacts.\",\n};\n\n/**\n * Produce ordered, de-duplicated repair hints for the verdict card.\n */\nexport function deriveRepairHints(input: RepairHintInput): string[] {\n\tconst hints: string[] = [];\n\n\tif (input.policyFlags.includes(POLICY_FLAG.CANDIDATE_LEAK_SUSPECT)) {\n\t\thints.push(\"Remove or relocate paths outside the approved write scope.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.SECRET_SUSPECT)) {\n\t\thints.push(\"Remove secret-like material from the diff; never commit credentials or private keys.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.NON_NEGOTIABLE_BLOCKING)) {\n\t\thints.push(\"Resolve non-negotiable policy blocks before apply or submit.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.EVIDENCE_DAG_INCOMPLETE)) {\n\t\thints.push(\"Complete the evidence DAG with terminal run ids (non-preview evaluation).\");\n\t}\n\tif (input.previewOnly && input.policyFlags.includes(POLICY_FLAG.LOW_DISCRIMINATION)) {\n\t\thints.push(\"Run a non-preview Deep Check with runner evidence for higher discrimination.\");\n\t}\n\n\tif (input.adjudicationReasonCode !== undefined) {\n\t\tconst fromReason = REASON_REPAIR[input.adjudicationReasonCode];\n\t\tif (fromReason !== undefined) {\n\t\t\thints.push(fromReason);\n\t\t}\n\t}\n\n\tif (input.userVerdict === \"INCONCLUSIVE\" && input.diffPaths.length === 0) {\n\t\thints.push(\"Provide a non-empty diff or explicit changed paths.\");\n\t}\n\tif (input.userVerdict === \"BLOCKED\" && hints.length === 0) {\n\t\thints.push(\"Regenerate the change set after addressing blocked reasons.\");\n\t}\n\tif (input.userVerdict === \"ADVISORY\") {\n\t\thints.push(\"Review advisory signals before applying in production.\");\n\t}\n\n\treturn [...new Set(hints)];\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Derive user-facing repair hints from wall verdict inputs (batch-1, no runner execution).
3
+ */
4
+ import { POLICY_FLAG } from "./policy-wall.js";
5
+ const REASON_REPAIR = {
6
+ SCOPE_VIOLATION: "Narrow the change to approved write paths only, then re-run the wall.",
7
+ SCHEMA_DRIFT: "Align outputs with the expected schema contract before resubmitting.",
8
+ CONTENT_CHECK_FAILED: "Fix failing content checks on artifacts, then re-adjudicate.",
9
+ NO_EVIDENCE_ON_SUCCESS: "Attach terminal run evidence before claiming success.",
10
+ EMPTY_ARTIFACT_CONTENT: "Ensure artifacts contain substantive content, not placeholders.",
11
+ TRACE_CHECK_FAILED: "Resolve trace verification failures on the reported run.",
12
+ TRACE_ERROR_SPAN: "Clear error spans under a success status or mark the run failed.",
13
+ MIN_ACTIONS_UNMET: "Record the minimum required actions for this kind.",
14
+ RUN_FETCH_FAILED: "Retry fetching run evidence when the runner is available.",
15
+ EVIDENCE_EMPTY: "Widen verification scope or wait for terminal run artifacts.",
16
+ };
17
+ /**
18
+ * Produce ordered, de-duplicated repair hints for the verdict card.
19
+ */
20
+ export function deriveRepairHints(input) {
21
+ const hints = [];
22
+ if (input.policyFlags.includes(POLICY_FLAG.CANDIDATE_LEAK_SUSPECT)) {
23
+ hints.push("Remove or relocate paths outside the approved write scope.");
24
+ }
25
+ if (input.policyFlags.includes(POLICY_FLAG.SECRET_SUSPECT)) {
26
+ hints.push("Remove secret-like material from the diff; never commit credentials or private keys.");
27
+ }
28
+ if (input.policyFlags.includes(POLICY_FLAG.NON_NEGOTIABLE_BLOCKING)) {
29
+ hints.push("Resolve non-negotiable policy blocks before apply or submit.");
30
+ }
31
+ if (input.policyFlags.includes(POLICY_FLAG.EVIDENCE_DAG_INCOMPLETE)) {
32
+ hints.push("Complete the evidence DAG with terminal run ids (non-preview evaluation).");
33
+ }
34
+ if (input.previewOnly && input.policyFlags.includes(POLICY_FLAG.LOW_DISCRIMINATION)) {
35
+ hints.push("Run a non-preview Deep Check with runner evidence for higher discrimination.");
36
+ }
37
+ if (input.adjudicationReasonCode !== undefined) {
38
+ const fromReason = REASON_REPAIR[input.adjudicationReasonCode];
39
+ if (fromReason !== undefined) {
40
+ hints.push(fromReason);
41
+ }
42
+ }
43
+ if (input.userVerdict === "INCONCLUSIVE" && input.diffPaths.length === 0) {
44
+ hints.push("Provide a non-empty diff or explicit changed paths.");
45
+ }
46
+ if (input.userVerdict === "BLOCKED" && hints.length === 0) {
47
+ hints.push("Regenerate the change set after addressing blocked reasons.");
48
+ }
49
+ if (input.userVerdict === "ADVISORY") {
50
+ hints.push("Review advisory signals before applying in production.");
51
+ }
52
+ return [...new Set(hints)];
53
+ }
54
+ //# sourceMappingURL=repair-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-loop.js","sourceRoot":"","sources":["../src/repair-loop.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C,MAAM,aAAa,GAAoD;IACtE,eAAe,EAAE,uEAAuE;IACxF,YAAY,EAAE,sEAAsE;IACpF,oBAAoB,EAAE,8DAA8D;IACpF,sBAAsB,EAAE,uDAAuD;IAC/E,sBAAsB,EAAE,iEAAiE;IACzF,kBAAkB,EAAE,0DAA0D;IAC9E,gBAAgB,EAAE,kEAAkE;IACpF,iBAAiB,EAAE,oDAAoD;IACvE,gBAAgB,EAAE,2DAA2D;IAC7E,cAAc,EAAE,8DAA8D;CAC9E,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB,EAAY;IACnE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,uBAAuB,CAAC,EAAE,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,uBAAuB,CAAC,EAAE,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACrF,KAAK,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,KAAK,CAAC,sBAAsB,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC/D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAED,IAAI,KAAK,CAAC,WAAW,KAAK,cAAc,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,CAC3B","sourcesContent":["/**\n * Derive user-facing repair hints from wall verdict inputs (batch-1, no runner execution).\n */\n\nimport type { AdjudicationReasonCode } from \"./adjudicator-registry.ts\";\nimport type { UserVerdict } from \"./b2c-verdict.ts\";\nimport type { PolicyFlag } from \"./policy-wall.ts\";\nimport { POLICY_FLAG } from \"./policy-wall.ts\";\n\nexport interface RepairHintInput {\n\tuserVerdict: UserVerdict;\n\tpolicyFlags: PolicyFlag[];\n\tadjudicationReasonCode?: AdjudicationReasonCode;\n\tpreviewOnly: boolean;\n\tdiffPaths: string[];\n}\n\nconst REASON_REPAIR: Partial<Record<AdjudicationReasonCode, string>> = {\n\tSCOPE_VIOLATION: \"Narrow the change to approved write paths only, then re-run the wall.\",\n\tSCHEMA_DRIFT: \"Align outputs with the expected schema contract before resubmitting.\",\n\tCONTENT_CHECK_FAILED: \"Fix failing content checks on artifacts, then re-adjudicate.\",\n\tNO_EVIDENCE_ON_SUCCESS: \"Attach terminal run evidence before claiming success.\",\n\tEMPTY_ARTIFACT_CONTENT: \"Ensure artifacts contain substantive content, not placeholders.\",\n\tTRACE_CHECK_FAILED: \"Resolve trace verification failures on the reported run.\",\n\tTRACE_ERROR_SPAN: \"Clear error spans under a success status or mark the run failed.\",\n\tMIN_ACTIONS_UNMET: \"Record the minimum required actions for this kind.\",\n\tRUN_FETCH_FAILED: \"Retry fetching run evidence when the runner is available.\",\n\tEVIDENCE_EMPTY: \"Widen verification scope or wait for terminal run artifacts.\",\n};\n\n/**\n * Produce ordered, de-duplicated repair hints for the verdict card.\n */\nexport function deriveRepairHints(input: RepairHintInput): string[] {\n\tconst hints: string[] = [];\n\n\tif (input.policyFlags.includes(POLICY_FLAG.CANDIDATE_LEAK_SUSPECT)) {\n\t\thints.push(\"Remove or relocate paths outside the approved write scope.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.SECRET_SUSPECT)) {\n\t\thints.push(\"Remove secret-like material from the diff; never commit credentials or private keys.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.NON_NEGOTIABLE_BLOCKING)) {\n\t\thints.push(\"Resolve non-negotiable policy blocks before apply or submit.\");\n\t}\n\tif (input.policyFlags.includes(POLICY_FLAG.EVIDENCE_DAG_INCOMPLETE)) {\n\t\thints.push(\"Complete the evidence DAG with terminal run ids (non-preview evaluation).\");\n\t}\n\tif (input.previewOnly && input.policyFlags.includes(POLICY_FLAG.LOW_DISCRIMINATION)) {\n\t\thints.push(\"Run a non-preview Deep Check with runner evidence for higher discrimination.\");\n\t}\n\n\tif (input.adjudicationReasonCode !== undefined) {\n\t\tconst fromReason = REASON_REPAIR[input.adjudicationReasonCode];\n\t\tif (fromReason !== undefined) {\n\t\t\thints.push(fromReason);\n\t\t}\n\t}\n\n\tif (input.userVerdict === \"INCONCLUSIVE\" && input.diffPaths.length === 0) {\n\t\thints.push(\"Provide a non-empty diff or explicit changed paths.\");\n\t}\n\tif (input.userVerdict === \"BLOCKED\" && hints.length === 0) {\n\t\thints.push(\"Regenerate the change set after addressing blocked reasons.\");\n\t}\n\tif (input.userVerdict === \"ADVISORY\") {\n\t\thints.push(\"Review advisory signals before applying in production.\");\n\t}\n\n\treturn [...new Set(hints)];\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Deterministic jitter retry backoff (W1).
3
+ *
4
+ * Produces a per-packet, per-attempt delay that is:
5
+ * - deterministic given (packet_id, retry_count): same inputs ⇒ same ms across
6
+ * processes and machines (no Math.random, no wall-clock dependence),
7
+ * - exponentially growing with retry_count, with the exponential BASE capped at `max`,
8
+ * - spread by a per-packet jitter factor derived from an FNV-1a hash of the packet id,
9
+ * so two packets retrying on the same schedule don't thunder-herd.
10
+ *
11
+ * FNV-1a 32-bit is implemented in-file (not imported across packages) so this module
12
+ * stays zero-dependency and the determinism contract is fully localized here.
13
+ */
14
+ /** Configuration for {@link backoffDelayMs}. All fields optional; see field defaults. */
15
+ export interface BackoffConfig {
16
+ /**
17
+ * Base delay in ms for retry_count 0 (the seed of the exponential). Default 2000.
18
+ * If greater than {@link BackoffConfig.max}, it is clamped down to `max`.
19
+ */
20
+ initial?: number;
21
+ /** Upper bound in ms applied to the exponential base. Default 120000. */
22
+ max?: number;
23
+ }
24
+ /**
25
+ * FNV-1a 32-bit hash over the UTF-16 code units of `s`. Returns an unsigned 32-bit integer
26
+ * in [0, 2^32). Iterates per UTF-16 code unit (String.prototype.charCodeAt), matching the
27
+ * spec contract exactly.
28
+ */
29
+ export declare function fnv1a32(s: string): number;
30
+ /**
31
+ * Deterministic exponential backoff with per-packet jitter.
32
+ *
33
+ * Formula (authoritative — the test's hard-coded vectors are generated from this exact
34
+ * expression by an independent reference script):
35
+ * n = retryCount clamped to >= 0 (negative / NaN treated as 0)
36
+ * initial = cfg.initial ?? 2000; if initial > max → initial = max
37
+ * max = cfg.max ?? 120000
38
+ * base = min(max, initial * 2**n) // CAP APPLIES TO BASE, not to the result
39
+ * h = fnv1a32(packetId) // unsigned 32-bit
40
+ * jitter = 0.5 + h / 2**32 // ∈ [0.5, 1.5)
41
+ * return = round(base * jitter)
42
+ *
43
+ * Range note: the literal expression `0.5 + h / 2**32` yields a jitter factor in [0.5, 1.5)
44
+ * because h ∈ [0, 2**32). This is implemented exactly as specified; the returned delay may
45
+ * therefore exceed `max` by up to ~50%. The spec comment "// range [0.5, 1.0)" is
46
+ * inconsistent with the literal formula; the formula and its externally-generated vectors
47
+ * are authoritative per the W1 task. See the W1 evidence note for details.
48
+ */
49
+ export declare function backoffDelayMs(packetId: string, retryCount: number, cfg?: BackoffConfig): number;
50
+ /**
51
+ * Computes the ISO-8601 "not-before" timestamp by adding `delayMs` milliseconds to `nowIso`.
52
+ * If `nowIso` is not a parseable date, falls back to `Date.now()` so a corrupt stored
53
+ * timestamp can never freeze a packet permanently.
54
+ */
55
+ export declare function retryNotBefore(nowIso: string, delayMs: number): string;
56
+ //# sourceMappingURL=retry-backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-backoff.d.ts","sourceRoot":"","sources":["../src/retry-backoff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAMD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,MAAM,CAchG;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAItE","sourcesContent":["/**\n * Deterministic jitter retry backoff (W1).\n *\n * Produces a per-packet, per-attempt delay that is:\n * - deterministic given (packet_id, retry_count): same inputs ⇒ same ms across\n * processes and machines (no Math.random, no wall-clock dependence),\n * - exponentially growing with retry_count, with the exponential BASE capped at `max`,\n * - spread by a per-packet jitter factor derived from an FNV-1a hash of the packet id,\n * so two packets retrying on the same schedule don't thunder-herd.\n *\n * FNV-1a 32-bit is implemented in-file (not imported across packages) so this module\n * stays zero-dependency and the determinism contract is fully localized here.\n */\n\n/** Configuration for {@link backoffDelayMs}. All fields optional; see field defaults. */\nexport interface BackoffConfig {\n\t/**\n\t * Base delay in ms for retry_count 0 (the seed of the exponential). Default 2000.\n\t * If greater than {@link BackoffConfig.max}, it is clamped down to `max`.\n\t */\n\tinitial?: number;\n\t/** Upper bound in ms applied to the exponential base. Default 120000. */\n\tmax?: number;\n}\n\nconst FNV1A_OFFSET_BASIS = 0x811c9dc5;\nconst FNV1A_PRIME = 0x01000193;\nconst UINT32_MOD = 4294967296; // 2^32\n\n/**\n * FNV-1a 32-bit hash over the UTF-16 code units of `s`. Returns an unsigned 32-bit integer\n * in [0, 2^32). Iterates per UTF-16 code unit (String.prototype.charCodeAt), matching the\n * spec contract exactly.\n */\nexport function fnv1a32(s: string): number {\n\tlet hash = FNV1A_OFFSET_BASIS >>> 0;\n\tfor (let i = 0; i < s.length; i++) {\n\t\thash = Math.imul((hash ^ s.charCodeAt(i)) >>> 0, FNV1A_PRIME) >>> 0;\n\t}\n\treturn hash >>> 0;\n}\n\nconst DEFAULT_INITIAL_MS = 2000;\nconst DEFAULT_MAX_MS = 120000;\n\n/**\n * Deterministic exponential backoff with per-packet jitter.\n *\n * Formula (authoritative — the test's hard-coded vectors are generated from this exact\n * expression by an independent reference script):\n * n = retryCount clamped to >= 0 (negative / NaN treated as 0)\n * initial = cfg.initial ?? 2000; if initial > max → initial = max\n * max = cfg.max ?? 120000\n * base = min(max, initial * 2**n) // CAP APPLIES TO BASE, not to the result\n * h = fnv1a32(packetId) // unsigned 32-bit\n * jitter = 0.5 + h / 2**32 // ∈ [0.5, 1.5)\n * return = round(base * jitter)\n *\n * Range note: the literal expression `0.5 + h / 2**32` yields a jitter factor in [0.5, 1.5)\n * because h ∈ [0, 2**32). This is implemented exactly as specified; the returned delay may\n * therefore exceed `max` by up to ~50%. The spec comment \"// range [0.5, 1.0)\" is\n * inconsistent with the literal formula; the formula and its externally-generated vectors\n * are authoritative per the W1 task. See the W1 evidence note for details.\n */\nexport function backoffDelayMs(packetId: string, retryCount: number, cfg?: BackoffConfig): number {\n\tlet n = retryCount;\n\tif (n < 0 || Number.isNaN(n)) {\n\t\tn = 0;\n\t}\n\tlet initial = cfg?.initial ?? DEFAULT_INITIAL_MS;\n\tconst max = cfg?.max ?? DEFAULT_MAX_MS;\n\tif (initial > max) {\n\t\tinitial = max;\n\t}\n\tconst base = Math.min(max, initial * 2 ** n);\n\tconst h = fnv1a32(packetId);\n\tconst jitter = 0.5 + h / UINT32_MOD;\n\treturn Math.round(base * jitter);\n}\n\n/**\n * Computes the ISO-8601 \"not-before\" timestamp by adding `delayMs` milliseconds to `nowIso`.\n * If `nowIso` is not a parseable date, falls back to `Date.now()` so a corrupt stored\n * timestamp can never freeze a packet permanently.\n */\nexport function retryNotBefore(nowIso: string, delayMs: number): string {\n\tconst parsed = Date.parse(nowIso);\n\tconst t = Number.isNaN(parsed) ? Date.now() : parsed;\n\treturn new Date(t + delayMs).toISOString();\n}\n"]}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Deterministic jitter retry backoff (W1).
3
+ *
4
+ * Produces a per-packet, per-attempt delay that is:
5
+ * - deterministic given (packet_id, retry_count): same inputs ⇒ same ms across
6
+ * processes and machines (no Math.random, no wall-clock dependence),
7
+ * - exponentially growing with retry_count, with the exponential BASE capped at `max`,
8
+ * - spread by a per-packet jitter factor derived from an FNV-1a hash of the packet id,
9
+ * so two packets retrying on the same schedule don't thunder-herd.
10
+ *
11
+ * FNV-1a 32-bit is implemented in-file (not imported across packages) so this module
12
+ * stays zero-dependency and the determinism contract is fully localized here.
13
+ */
14
+ const FNV1A_OFFSET_BASIS = 0x811c9dc5;
15
+ const FNV1A_PRIME = 0x01000193;
16
+ const UINT32_MOD = 4294967296; // 2^32
17
+ /**
18
+ * FNV-1a 32-bit hash over the UTF-16 code units of `s`. Returns an unsigned 32-bit integer
19
+ * in [0, 2^32). Iterates per UTF-16 code unit (String.prototype.charCodeAt), matching the
20
+ * spec contract exactly.
21
+ */
22
+ export function fnv1a32(s) {
23
+ let hash = FNV1A_OFFSET_BASIS >>> 0;
24
+ for (let i = 0; i < s.length; i++) {
25
+ hash = Math.imul((hash ^ s.charCodeAt(i)) >>> 0, FNV1A_PRIME) >>> 0;
26
+ }
27
+ return hash >>> 0;
28
+ }
29
+ const DEFAULT_INITIAL_MS = 2000;
30
+ const DEFAULT_MAX_MS = 120000;
31
+ /**
32
+ * Deterministic exponential backoff with per-packet jitter.
33
+ *
34
+ * Formula (authoritative — the test's hard-coded vectors are generated from this exact
35
+ * expression by an independent reference script):
36
+ * n = retryCount clamped to >= 0 (negative / NaN treated as 0)
37
+ * initial = cfg.initial ?? 2000; if initial > max → initial = max
38
+ * max = cfg.max ?? 120000
39
+ * base = min(max, initial * 2**n) // CAP APPLIES TO BASE, not to the result
40
+ * h = fnv1a32(packetId) // unsigned 32-bit
41
+ * jitter = 0.5 + h / 2**32 // ∈ [0.5, 1.5)
42
+ * return = round(base * jitter)
43
+ *
44
+ * Range note: the literal expression `0.5 + h / 2**32` yields a jitter factor in [0.5, 1.5)
45
+ * because h ∈ [0, 2**32). This is implemented exactly as specified; the returned delay may
46
+ * therefore exceed `max` by up to ~50%. The spec comment "// range [0.5, 1.0)" is
47
+ * inconsistent with the literal formula; the formula and its externally-generated vectors
48
+ * are authoritative per the W1 task. See the W1 evidence note for details.
49
+ */
50
+ export function backoffDelayMs(packetId, retryCount, cfg) {
51
+ let n = retryCount;
52
+ if (n < 0 || Number.isNaN(n)) {
53
+ n = 0;
54
+ }
55
+ let initial = cfg?.initial ?? DEFAULT_INITIAL_MS;
56
+ const max = cfg?.max ?? DEFAULT_MAX_MS;
57
+ if (initial > max) {
58
+ initial = max;
59
+ }
60
+ const base = Math.min(max, initial * 2 ** n);
61
+ const h = fnv1a32(packetId);
62
+ const jitter = 0.5 + h / UINT32_MOD;
63
+ return Math.round(base * jitter);
64
+ }
65
+ /**
66
+ * Computes the ISO-8601 "not-before" timestamp by adding `delayMs` milliseconds to `nowIso`.
67
+ * If `nowIso` is not a parseable date, falls back to `Date.now()` so a corrupt stored
68
+ * timestamp can never freeze a packet permanently.
69
+ */
70
+ export function retryNotBefore(nowIso, delayMs) {
71
+ const parsed = Date.parse(nowIso);
72
+ const t = Number.isNaN(parsed) ? Date.now() : parsed;
73
+ return new Date(t + delayMs).toISOString();
74
+ }
75
+ //# sourceMappingURL=retry-backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-backoff.js","sourceRoot":"","sources":["../src/retry-backoff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,MAAM,kBAAkB,GAAG,UAAU,CAAC;AACtC,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,OAAO;AAEtC;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,CAAS,EAAU;IAC1C,IAAI,IAAI,GAAG,kBAAkB,KAAK,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AAAA,CAClB;AAED,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,UAAkB,EAAE,GAAmB,EAAU;IACjG,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,CAAC,GAAG,CAAC,CAAC;IACP,CAAC;IACD,IAAI,OAAO,GAAG,GAAG,EAAE,OAAO,IAAI,kBAAkB,CAAC;IACjD,MAAM,GAAG,GAAG,GAAG,EAAE,GAAG,IAAI,cAAc,CAAC;IACvC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;QACnB,OAAO,GAAG,GAAG,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,UAAU,CAAC;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;AAAA,CACjC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,OAAe,EAAU;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACrD,OAAO,IAAI,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;AAAA,CAC3C","sourcesContent":["/**\n * Deterministic jitter retry backoff (W1).\n *\n * Produces a per-packet, per-attempt delay that is:\n * - deterministic given (packet_id, retry_count): same inputs ⇒ same ms across\n * processes and machines (no Math.random, no wall-clock dependence),\n * - exponentially growing with retry_count, with the exponential BASE capped at `max`,\n * - spread by a per-packet jitter factor derived from an FNV-1a hash of the packet id,\n * so two packets retrying on the same schedule don't thunder-herd.\n *\n * FNV-1a 32-bit is implemented in-file (not imported across packages) so this module\n * stays zero-dependency and the determinism contract is fully localized here.\n */\n\n/** Configuration for {@link backoffDelayMs}. All fields optional; see field defaults. */\nexport interface BackoffConfig {\n\t/**\n\t * Base delay in ms for retry_count 0 (the seed of the exponential). Default 2000.\n\t * If greater than {@link BackoffConfig.max}, it is clamped down to `max`.\n\t */\n\tinitial?: number;\n\t/** Upper bound in ms applied to the exponential base. Default 120000. */\n\tmax?: number;\n}\n\nconst FNV1A_OFFSET_BASIS = 0x811c9dc5;\nconst FNV1A_PRIME = 0x01000193;\nconst UINT32_MOD = 4294967296; // 2^32\n\n/**\n * FNV-1a 32-bit hash over the UTF-16 code units of `s`. Returns an unsigned 32-bit integer\n * in [0, 2^32). Iterates per UTF-16 code unit (String.prototype.charCodeAt), matching the\n * spec contract exactly.\n */\nexport function fnv1a32(s: string): number {\n\tlet hash = FNV1A_OFFSET_BASIS >>> 0;\n\tfor (let i = 0; i < s.length; i++) {\n\t\thash = Math.imul((hash ^ s.charCodeAt(i)) >>> 0, FNV1A_PRIME) >>> 0;\n\t}\n\treturn hash >>> 0;\n}\n\nconst DEFAULT_INITIAL_MS = 2000;\nconst DEFAULT_MAX_MS = 120000;\n\n/**\n * Deterministic exponential backoff with per-packet jitter.\n *\n * Formula (authoritative — the test's hard-coded vectors are generated from this exact\n * expression by an independent reference script):\n * n = retryCount clamped to >= 0 (negative / NaN treated as 0)\n * initial = cfg.initial ?? 2000; if initial > max → initial = max\n * max = cfg.max ?? 120000\n * base = min(max, initial * 2**n) // CAP APPLIES TO BASE, not to the result\n * h = fnv1a32(packetId) // unsigned 32-bit\n * jitter = 0.5 + h / 2**32 // ∈ [0.5, 1.5)\n * return = round(base * jitter)\n *\n * Range note: the literal expression `0.5 + h / 2**32` yields a jitter factor in [0.5, 1.5)\n * because h ∈ [0, 2**32). This is implemented exactly as specified; the returned delay may\n * therefore exceed `max` by up to ~50%. The spec comment \"// range [0.5, 1.0)\" is\n * inconsistent with the literal formula; the formula and its externally-generated vectors\n * are authoritative per the W1 task. See the W1 evidence note for details.\n */\nexport function backoffDelayMs(packetId: string, retryCount: number, cfg?: BackoffConfig): number {\n\tlet n = retryCount;\n\tif (n < 0 || Number.isNaN(n)) {\n\t\tn = 0;\n\t}\n\tlet initial = cfg?.initial ?? DEFAULT_INITIAL_MS;\n\tconst max = cfg?.max ?? DEFAULT_MAX_MS;\n\tif (initial > max) {\n\t\tinitial = max;\n\t}\n\tconst base = Math.min(max, initial * 2 ** n);\n\tconst h = fnv1a32(packetId);\n\tconst jitter = 0.5 + h / UINT32_MOD;\n\treturn Math.round(base * jitter);\n}\n\n/**\n * Computes the ISO-8601 \"not-before\" timestamp by adding `delayMs` milliseconds to `nowIso`.\n * If `nowIso` is not a parseable date, falls back to `Date.now()` so a corrupt stored\n * timestamp can never freeze a packet permanently.\n */\nexport function retryNotBefore(nowIso: string, delayMs: number): string {\n\tconst parsed = Date.parse(nowIso);\n\tconst t = Number.isNaN(parsed) ? Date.now() : parsed;\n\treturn new Date(t + delayMs).toISOString();\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Verification digest for receipts — patch + evidence fingerprints without secret material.
3
+ */
4
+ export interface VerificationDigestInput {
5
+ /** Unified diff or patch text (hashed, not echoed). */
6
+ patchText?: string;
7
+ /** Opaque evidence blobs (e.g. run artifact excerpts); only digests are retained. */
8
+ evidenceChunks?: string[];
9
+ /** Correctness wall policy batch id (included in composite hash when set). */
10
+ wallVersion?: string;
11
+ }
12
+ export interface VerificationDigest {
13
+ algorithm: "sha256-hex";
14
+ patchHash: string | null;
15
+ evidenceHashes: string[];
16
+ compositeHash: string;
17
+ /** Wall receipt batch id when supplied at digest build time. */
18
+ wallVersion?: string;
19
+ }
20
+ /** Wall-scoped receipt metadata paired with digest fingerprints. */
21
+ export interface WallReceiptMeta {
22
+ wallVersion: string;
23
+ compositeHash: string;
24
+ }
25
+ /**
26
+ * Build a stable verification digest from patch and evidence. No secrets or raw payloads in output.
27
+ */
28
+ export declare function buildVerificationDigest(input: VerificationDigestInput): VerificationDigest;
29
+ //# sourceMappingURL=signed-receipt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signed-receipt.d.ts","sourceRoot":"","sources":["../src/signed-receipt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,uBAAuB;IACvC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,YAAY,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACtB;AAMD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAc1F","sourcesContent":["/**\n * Verification digest for receipts — patch + evidence fingerprints without secret material.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport interface VerificationDigestInput {\n\t/** Unified diff or patch text (hashed, not echoed). */\n\tpatchText?: string;\n\t/** Opaque evidence blobs (e.g. run artifact excerpts); only digests are retained. */\n\tevidenceChunks?: string[];\n\t/** Correctness wall policy batch id (included in composite hash when set). */\n\twallVersion?: string;\n}\n\nexport interface VerificationDigest {\n\talgorithm: \"sha256-hex\";\n\tpatchHash: string | null;\n\tevidenceHashes: string[];\n\tcompositeHash: string;\n\t/** Wall receipt batch id when supplied at digest build time. */\n\twallVersion?: string;\n}\n\n/** Wall-scoped receipt metadata paired with digest fingerprints. */\nexport interface WallReceiptMeta {\n\twallVersion: string;\n\tcompositeHash: string;\n}\n\nfunction sha256Hex(text: string): string {\n\treturn createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Build a stable verification digest from patch and evidence. No secrets or raw payloads in output.\n */\nexport function buildVerificationDigest(input: VerificationDigestInput): VerificationDigest {\n\tconst patchHash = input.patchText !== undefined && input.patchText.length > 0 ? sha256Hex(input.patchText) : null;\n\tconst evidenceHashes = (input.evidenceChunks ?? []).filter((c) => c.length > 0).map((c) => sha256Hex(c));\n\tconst wallVersion = input.wallVersion !== undefined && input.wallVersion.length > 0 ? input.wallVersion : undefined;\n\tconst compositeMaterial = [wallVersion ?? \"\", patchHash ?? \"\", ...evidenceHashes].join(\"|\");\n\tconst compositeHash = sha256Hex(compositeMaterial || \"empty\");\n\n\treturn {\n\t\talgorithm: \"sha256-hex\",\n\t\tpatchHash,\n\t\tevidenceHashes,\n\t\tcompositeHash,\n\t\t...(wallVersion !== undefined ? { wallVersion } : {}),\n\t};\n}\n"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Verification digest for receipts — patch + evidence fingerprints without secret material.
3
+ */
4
+ import { createHash } from "node:crypto";
5
+ function sha256Hex(text) {
6
+ return createHash("sha256").update(text, "utf8").digest("hex");
7
+ }
8
+ /**
9
+ * Build a stable verification digest from patch and evidence. No secrets or raw payloads in output.
10
+ */
11
+ export function buildVerificationDigest(input) {
12
+ const patchHash = input.patchText !== undefined && input.patchText.length > 0 ? sha256Hex(input.patchText) : null;
13
+ const evidenceHashes = (input.evidenceChunks ?? []).filter((c) => c.length > 0).map((c) => sha256Hex(c));
14
+ const wallVersion = input.wallVersion !== undefined && input.wallVersion.length > 0 ? input.wallVersion : undefined;
15
+ const compositeMaterial = [wallVersion ?? "", patchHash ?? "", ...evidenceHashes].join("|");
16
+ const compositeHash = sha256Hex(compositeMaterial || "empty");
17
+ return {
18
+ algorithm: "sha256-hex",
19
+ patchHash,
20
+ evidenceHashes,
21
+ compositeHash,
22
+ ...(wallVersion !== undefined ? { wallVersion } : {}),
23
+ };
24
+ }
25
+ //# sourceMappingURL=signed-receipt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signed-receipt.js","sourceRoot":"","sources":["../src/signed-receipt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0BzC,SAAS,SAAS,CAAC,IAAY,EAAU;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC/D;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA8B,EAAsB;IAC3F,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClH,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACzG,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IACpH,MAAM,iBAAiB,GAAG,CAAC,WAAW,IAAI,EAAE,EAAE,SAAS,IAAI,EAAE,EAAE,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5F,MAAM,aAAa,GAAG,SAAS,CAAC,iBAAiB,IAAI,OAAO,CAAC,CAAC;IAE9D,OAAO;QACN,SAAS,EAAE,YAAY;QACvB,SAAS;QACT,cAAc;QACd,aAAa;QACb,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrD,CAAC;AAAA,CACF","sourcesContent":["/**\n * Verification digest for receipts — patch + evidence fingerprints without secret material.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport interface VerificationDigestInput {\n\t/** Unified diff or patch text (hashed, not echoed). */\n\tpatchText?: string;\n\t/** Opaque evidence blobs (e.g. run artifact excerpts); only digests are retained. */\n\tevidenceChunks?: string[];\n\t/** Correctness wall policy batch id (included in composite hash when set). */\n\twallVersion?: string;\n}\n\nexport interface VerificationDigest {\n\talgorithm: \"sha256-hex\";\n\tpatchHash: string | null;\n\tevidenceHashes: string[];\n\tcompositeHash: string;\n\t/** Wall receipt batch id when supplied at digest build time. */\n\twallVersion?: string;\n}\n\n/** Wall-scoped receipt metadata paired with digest fingerprints. */\nexport interface WallReceiptMeta {\n\twallVersion: string;\n\tcompositeHash: string;\n}\n\nfunction sha256Hex(text: string): string {\n\treturn createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Build a stable verification digest from patch and evidence. No secrets or raw payloads in output.\n */\nexport function buildVerificationDigest(input: VerificationDigestInput): VerificationDigest {\n\tconst patchHash = input.patchText !== undefined && input.patchText.length > 0 ? sha256Hex(input.patchText) : null;\n\tconst evidenceHashes = (input.evidenceChunks ?? []).filter((c) => c.length > 0).map((c) => sha256Hex(c));\n\tconst wallVersion = input.wallVersion !== undefined && input.wallVersion.length > 0 ? input.wallVersion : undefined;\n\tconst compositeMaterial = [wallVersion ?? \"\", patchHash ?? \"\", ...evidenceHashes].join(\"|\");\n\tconst compositeHash = sha256Hex(compositeMaterial || \"empty\");\n\n\treturn {\n\t\talgorithm: \"sha256-hex\",\n\t\tpatchHash,\n\t\tevidenceHashes,\n\t\tcompositeHash,\n\t\t...(wallVersion !== undefined ? { wallVersion } : {}),\n\t};\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Design-fidelity note: this file implements the packet-level state machine defined in
3
+ * Part 1 (`final-part1-core-algorithm.md`), Section 2 (transition table), Section 4 (terminal
4
+ * state, since only CLOSED is terminal per §2's own text), and Section 2 state 3
5
+ * (AWAITING_APPROVAL's trigger condition). It is an implementation of that document, not a
6
+ * restatement of it.
7
+ */
8
+ import type { LoopConfig, TopologyClassification, WorkPacket, WorkPacketState } from "./types.ts";
9
+ /**
10
+ * Part 1 §2 — returns whether `from -> to` is a legal Work Packet state transition, per the
11
+ * transition table. Pure and side-effect free.
12
+ */
13
+ export declare function canTransition(from: WorkPacketState, to: WorkPacketState): boolean;
14
+ /**
15
+ * Part 1 §2 / §7 — applies a state transition to a Work Packet, returning a new packet object
16
+ * with `state` updated and a new `TransitionLogEntry` appended to `transition_log`. Throws if
17
+ * the transition is not legal per `canTransition`. Does not mutate `packet`.
18
+ *
19
+ * `now` defaults to the wall-clock ISO timestamp but is accepted as a parameter so callers
20
+ * (and tests) can supply a deterministic clock.
21
+ */
22
+ export declare function applyTransition(packet: WorkPacket, to: WorkPacketState, cause: string, now?: () => string): WorkPacket;
23
+ /**
24
+ * Part 1 §2 state 14 / §4 "Completion exhaustion" — CLOSED is the only terminal state.
25
+ * ESCALATED and RETRY_QUEUED are explicitly not terminal: both can still transition further
26
+ * (ESCALATED via an explicit external unblock signal; RETRY_QUEUED back into ROUTED).
27
+ */
28
+ export declare function isTerminalState(state: WorkPacketState): boolean;
29
+ /**
30
+ * Part 1 §2 state 3 — whether the next attempt for `packet` must be held in
31
+ * AWAITING_APPROVAL before its first `adaptorch_run` call, rather than going straight from
32
+ * ROUTED to DISPATCHED.
33
+ *
34
+ * True only if the proposed payload differs from the packet's last-human-approved baseline,
35
+ * or the proposed topology classification differs from the packet's currently stored topology
36
+ * decision — unless the loop instance is running in pre-approved-batch mode (§2 state 3's
37
+ * documented opt-in escape hatch), in which case this always returns false.
38
+ */
39
+ export declare function requiresHumanApproval(packet: WorkPacket, newPayload: unknown, newTopology: TopologyClassification | null, loopConfig: LoopConfig): boolean;
40
+ //# sourceMappingURL=state-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,sBAAsB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAwBlG;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,GAAG,OAAO,CAEjF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,MAAuC,GAChD,UAAU,CAYZ;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,OAAO,EACnB,WAAW,EAAE,sBAAsB,GAAG,IAAI,EAC1C,UAAU,EAAE,UAAU,GACpB,OAAO,CAkBT","sourcesContent":["/**\n * Design-fidelity note: this file implements the packet-level state machine defined in\n * Part 1 (`final-part1-core-algorithm.md`), Section 2 (transition table), Section 4 (terminal\n * state, since only CLOSED is terminal per §2's own text), and Section 2 state 3\n * (AWAITING_APPROVAL's trigger condition). It is an implementation of that document, not a\n * restatement of it.\n */\n\nimport type { LoopConfig, TopologyClassification, WorkPacket, WorkPacketState } from \"./types.ts\";\n\n/**\n * Part 1 §2 — the full transition table, as an adjacency map keyed by source state. This is\n * the single source of truth for which transitions are legal; every other state-machine\n * function in this file is built on top of `canTransition`.\n */\nconst TRANSITIONS: Record<WorkPacketState, WorkPacketState[]> = {\n\tDRAFTED: [\"ROUTED\"],\n\tROUTED: [\"AWAITING_APPROVAL\", \"DISPATCHED\"],\n\tAWAITING_APPROVAL: [\"DISPATCHED\", \"CLOSED\"],\n\tDISPATCHED: [\"ACTIVE\", \"RAW_TERMINAL\", \"CLOSED\"],\n\tACTIVE: [\"RAW_TERMINAL\", \"HALTING\", \"CLOSED\"],\n\tHALTING: [\"RAW_TERMINAL\", \"CLOSED\"],\n\tRAW_TERMINAL: [\"UNDER_REVIEW\", \"ADJUDICATION_FAILED\"],\n\tUNDER_REVIEW: [\"ADJUDICATION_FAILED\", \"CONFIRMED\", \"DECLINED\"],\n\tADJUDICATION_FAILED: [\"UNDER_REVIEW\", \"ESCALATED\"],\n\tCONFIRMED: [\"CLOSED\"],\n\tDECLINED: [\"ESCALATED\", \"RETRY_QUEUED\", \"CLOSED\"],\n\tRETRY_QUEUED: [\"ROUTED\"],\n\tESCALATED: [\"RETRY_QUEUED\", \"CLOSED\"],\n\tCLOSED: [],\n};\n\n/**\n * Part 1 §2 — returns whether `from -> to` is a legal Work Packet state transition, per the\n * transition table. Pure and side-effect free.\n */\nexport function canTransition(from: WorkPacketState, to: WorkPacketState): boolean {\n\treturn TRANSITIONS[from].includes(to);\n}\n\n/**\n * Part 1 §2 / §7 — applies a state transition to a Work Packet, returning a new packet object\n * with `state` updated and a new `TransitionLogEntry` appended to `transition_log`. Throws if\n * the transition is not legal per `canTransition`. Does not mutate `packet`.\n *\n * `now` defaults to the wall-clock ISO timestamp but is accepted as a parameter so callers\n * (and tests) can supply a deterministic clock.\n */\nexport function applyTransition(\n\tpacket: WorkPacket,\n\tto: WorkPacketState,\n\tcause: string,\n\tnow: () => string = () => new Date().toISOString(),\n): WorkPacket {\n\tif (!canTransition(packet.state, to)) {\n\t\tthrow new Error(\n\t\t\t`Illegal work packet transition: ${packet.state} -> ${to} for packet ${packet.packet_id} (cause: ${cause})`,\n\t\t);\n\t}\n\tconst at = now();\n\treturn {\n\t\t...packet,\n\t\tstate: to,\n\t\ttransition_log: [...packet.transition_log, { from: packet.state, to, at, cause }],\n\t};\n}\n\n/**\n * Part 1 §2 state 14 / §4 \"Completion exhaustion\" — CLOSED is the only terminal state.\n * ESCALATED and RETRY_QUEUED are explicitly not terminal: both can still transition further\n * (ESCALATED via an explicit external unblock signal; RETRY_QUEUED back into ROUTED).\n */\nexport function isTerminalState(state: WorkPacketState): boolean {\n\treturn state === \"CLOSED\";\n}\n\n/**\n * Part 1 §2 state 3 — whether the next attempt for `packet` must be held in\n * AWAITING_APPROVAL before its first `adaptorch_run` call, rather than going straight from\n * ROUTED to DISPATCHED.\n *\n * True only if the proposed payload differs from the packet's last-human-approved baseline,\n * or the proposed topology classification differs from the packet's currently stored topology\n * decision — unless the loop instance is running in pre-approved-batch mode (§2 state 3's\n * documented opt-in escape hatch), in which case this always returns false.\n */\nexport function requiresHumanApproval(\n\tpacket: WorkPacket,\n\tnewPayload: unknown,\n\tnewTopology: TopologyClassification | null,\n\tloopConfig: LoopConfig,\n): boolean {\n\tif (loopConfig.pre_approved_batch) {\n\t\treturn false;\n\t}\n\n\t// Pragmatic choice, not a claim of full structural equality semantics: JSON.stringify\n\t// comparison is order-sensitive for object keys and does not handle non-JSON-serializable\n\t// values (e.g. undefined, functions, symbols) specially. Adequate for the plain task\n\t// payloads this design assumes; a caller with payloads that need true structural equality\n\t// should compare upstream and pass an already-decided `newPayload` reference instead.\n\tconst payloadChanged = JSON.stringify(newPayload) !== JSON.stringify(packet.last_human_approved_payload);\n\n\tconst topologyChanged =\n\t\tnewTopology !== null &&\n\t\tpacket.topology_decision !== null &&\n\t\tnewTopology !== packet.topology_decision.classification;\n\n\treturn payloadChanged || topologyChanged;\n}\n"]}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Design-fidelity note: this file implements the packet-level state machine defined in
3
+ * Part 1 (`final-part1-core-algorithm.md`), Section 2 (transition table), Section 4 (terminal
4
+ * state, since only CLOSED is terminal per §2's own text), and Section 2 state 3
5
+ * (AWAITING_APPROVAL's trigger condition). It is an implementation of that document, not a
6
+ * restatement of it.
7
+ */
8
+ /**
9
+ * Part 1 §2 — the full transition table, as an adjacency map keyed by source state. This is
10
+ * the single source of truth for which transitions are legal; every other state-machine
11
+ * function in this file is built on top of `canTransition`.
12
+ */
13
+ const TRANSITIONS = {
14
+ DRAFTED: ["ROUTED"],
15
+ ROUTED: ["AWAITING_APPROVAL", "DISPATCHED"],
16
+ AWAITING_APPROVAL: ["DISPATCHED", "CLOSED"],
17
+ DISPATCHED: ["ACTIVE", "RAW_TERMINAL", "CLOSED"],
18
+ ACTIVE: ["RAW_TERMINAL", "HALTING", "CLOSED"],
19
+ HALTING: ["RAW_TERMINAL", "CLOSED"],
20
+ RAW_TERMINAL: ["UNDER_REVIEW", "ADJUDICATION_FAILED"],
21
+ UNDER_REVIEW: ["ADJUDICATION_FAILED", "CONFIRMED", "DECLINED"],
22
+ ADJUDICATION_FAILED: ["UNDER_REVIEW", "ESCALATED"],
23
+ CONFIRMED: ["CLOSED"],
24
+ DECLINED: ["ESCALATED", "RETRY_QUEUED", "CLOSED"],
25
+ RETRY_QUEUED: ["ROUTED"],
26
+ ESCALATED: ["RETRY_QUEUED", "CLOSED"],
27
+ CLOSED: [],
28
+ };
29
+ /**
30
+ * Part 1 §2 — returns whether `from -> to` is a legal Work Packet state transition, per the
31
+ * transition table. Pure and side-effect free.
32
+ */
33
+ export function canTransition(from, to) {
34
+ return TRANSITIONS[from].includes(to);
35
+ }
36
+ /**
37
+ * Part 1 §2 / §7 — applies a state transition to a Work Packet, returning a new packet object
38
+ * with `state` updated and a new `TransitionLogEntry` appended to `transition_log`. Throws if
39
+ * the transition is not legal per `canTransition`. Does not mutate `packet`.
40
+ *
41
+ * `now` defaults to the wall-clock ISO timestamp but is accepted as a parameter so callers
42
+ * (and tests) can supply a deterministic clock.
43
+ */
44
+ export function applyTransition(packet, to, cause, now = () => new Date().toISOString()) {
45
+ if (!canTransition(packet.state, to)) {
46
+ throw new Error(`Illegal work packet transition: ${packet.state} -> ${to} for packet ${packet.packet_id} (cause: ${cause})`);
47
+ }
48
+ const at = now();
49
+ return {
50
+ ...packet,
51
+ state: to,
52
+ transition_log: [...packet.transition_log, { from: packet.state, to, at, cause }],
53
+ };
54
+ }
55
+ /**
56
+ * Part 1 §2 state 14 / §4 "Completion exhaustion" — CLOSED is the only terminal state.
57
+ * ESCALATED and RETRY_QUEUED are explicitly not terminal: both can still transition further
58
+ * (ESCALATED via an explicit external unblock signal; RETRY_QUEUED back into ROUTED).
59
+ */
60
+ export function isTerminalState(state) {
61
+ return state === "CLOSED";
62
+ }
63
+ /**
64
+ * Part 1 §2 state 3 — whether the next attempt for `packet` must be held in
65
+ * AWAITING_APPROVAL before its first `adaptorch_run` call, rather than going straight from
66
+ * ROUTED to DISPATCHED.
67
+ *
68
+ * True only if the proposed payload differs from the packet's last-human-approved baseline,
69
+ * or the proposed topology classification differs from the packet's currently stored topology
70
+ * decision — unless the loop instance is running in pre-approved-batch mode (§2 state 3's
71
+ * documented opt-in escape hatch), in which case this always returns false.
72
+ */
73
+ export function requiresHumanApproval(packet, newPayload, newTopology, loopConfig) {
74
+ if (loopConfig.pre_approved_batch) {
75
+ return false;
76
+ }
77
+ // Pragmatic choice, not a claim of full structural equality semantics: JSON.stringify
78
+ // comparison is order-sensitive for object keys and does not handle non-JSON-serializable
79
+ // values (e.g. undefined, functions, symbols) specially. Adequate for the plain task
80
+ // payloads this design assumes; a caller with payloads that need true structural equality
81
+ // should compare upstream and pass an already-decided `newPayload` reference instead.
82
+ const payloadChanged = JSON.stringify(newPayload) !== JSON.stringify(packet.last_human_approved_payload);
83
+ const topologyChanged = newTopology !== null &&
84
+ packet.topology_decision !== null &&
85
+ newTopology !== packet.topology_decision.classification;
86
+ return payloadChanged || topologyChanged;
87
+ }
88
+ //# sourceMappingURL=state-machine.js.map