open-multi-agent-kit 0.90.5 → 0.90.7

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 (123) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +9 -9
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/codexbar-cli.d.ts +39 -0
  7. package/dist/codexbar-cli.d.ts.map +1 -0
  8. package/dist/codexbar-cli.js +230 -0
  9. package/dist/codexbar-cli.js.map +1 -0
  10. package/dist/commands/doctor-provider.d.ts.map +1 -1
  11. package/dist/commands/doctor-provider.js +16 -3
  12. package/dist/commands/doctor-provider.js.map +1 -1
  13. package/dist/core/agent-session.d.ts.map +1 -1
  14. package/dist/core/agent-session.js +30 -8
  15. package/dist/core/agent-session.js.map +1 -1
  16. package/dist/core/codexbar-adapter.d.ts +44 -0
  17. package/dist/core/codexbar-adapter.d.ts.map +1 -0
  18. package/dist/core/codexbar-adapter.js +211 -0
  19. package/dist/core/codexbar-adapter.js.map +1 -0
  20. package/dist/core/command-safety.d.ts.map +1 -1
  21. package/dist/core/command-safety.js +7 -0
  22. package/dist/core/command-safety.js.map +1 -1
  23. package/dist/core/compaction/index.d.ts +2 -0
  24. package/dist/core/compaction/index.d.ts.map +1 -1
  25. package/dist/core/compaction/index.js +2 -0
  26. package/dist/core/compaction/index.js.map +1 -1
  27. package/dist/core/compaction/model-policy.d.ts +9 -0
  28. package/dist/core/compaction/model-policy.d.ts.map +1 -0
  29. package/dist/core/compaction/model-policy.js +37 -0
  30. package/dist/core/compaction/model-policy.js.map +1 -0
  31. package/dist/core/compaction/resume-policy.d.ts +6 -0
  32. package/dist/core/compaction/resume-policy.d.ts.map +1 -0
  33. package/dist/core/compaction/resume-policy.js +8 -0
  34. package/dist/core/compaction/resume-policy.js.map +1 -0
  35. package/dist/core/domain-loadouts.d.ts.map +1 -1
  36. package/dist/core/domain-loadouts.js +68 -1
  37. package/dist/core/domain-loadouts.js.map +1 -1
  38. package/dist/core/extensions/loader.d.ts.map +1 -1
  39. package/dist/core/extensions/loader.js +12 -0
  40. package/dist/core/extensions/loader.js.map +1 -1
  41. package/dist/core/extensions/runner.d.ts.map +1 -1
  42. package/dist/core/extensions/runner.js +3 -0
  43. package/dist/core/extensions/runner.js.map +1 -1
  44. package/dist/core/extensions/types.d.ts +15 -0
  45. package/dist/core/extensions/types.d.ts.map +1 -1
  46. package/dist/core/extensions/types.js.map +1 -1
  47. package/dist/core/grok-harness-dispatch.d.ts +22 -0
  48. package/dist/core/grok-harness-dispatch.d.ts.map +1 -0
  49. package/dist/core/grok-harness-dispatch.js +44 -0
  50. package/dist/core/grok-harness-dispatch.js.map +1 -0
  51. package/dist/core/grok-harness.d.ts +25 -0
  52. package/dist/core/grok-harness.d.ts.map +1 -0
  53. package/dist/core/grok-harness.js +61 -0
  54. package/dist/core/grok-harness.js.map +1 -0
  55. package/dist/core/grok-playbook.d.ts +7 -0
  56. package/dist/core/grok-playbook.d.ts.map +1 -0
  57. package/dist/core/grok-playbook.js +34 -0
  58. package/dist/core/grok-playbook.js.map +1 -0
  59. package/dist/core/reasoning-router-v4-normalize.d.ts +11 -0
  60. package/dist/core/reasoning-router-v4-normalize.d.ts.map +1 -0
  61. package/dist/core/reasoning-router-v4-normalize.js +224 -0
  62. package/dist/core/reasoning-router-v4-normalize.js.map +1 -0
  63. package/dist/core/reasoning-router-v4-weights.d.ts +13 -5
  64. package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
  65. package/dist/core/reasoning-router-v4-weights.js +8 -4
  66. package/dist/core/reasoning-router-v4-weights.js.map +1 -1
  67. package/dist/core/reasoning-router-v4.d.ts +3 -3
  68. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  69. package/dist/core/reasoning-router-v4.js +394 -58
  70. package/dist/core/reasoning-router-v4.js.map +1 -1
  71. package/dist/core/router-feedback-collector.d.ts +1 -1
  72. package/dist/core/router-feedback-collector.d.ts.map +1 -1
  73. package/dist/core/router-feedback-collector.js +9 -1
  74. package/dist/core/router-feedback-collector.js.map +1 -1
  75. package/dist/core/sdk.d.ts.map +1 -1
  76. package/dist/core/sdk.js +31 -12
  77. package/dist/core/sdk.js.map +1 -1
  78. package/dist/core/settings-manager.d.ts +3 -3
  79. package/dist/core/settings-manager.d.ts.map +1 -1
  80. package/dist/core/settings-manager.js.map +1 -1
  81. package/dist/main.d.ts.map +1 -1
  82. package/dist/main.js +4 -0
  83. package/dist/main.js.map +1 -1
  84. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/settings-selector.js +1 -0
  86. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  87. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  88. package/dist/modes/interactive/components/thinking-selector.js +1 -0
  89. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  90. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  91. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  92. package/dist/modes/interactive/theme/theme.js +2 -0
  93. package/dist/modes/interactive/theme/theme.js.map +1 -1
  94. package/dist/utils/tools-manager.d.ts +3 -2
  95. package/dist/utils/tools-manager.d.ts.map +1 -1
  96. package/dist/utils/tools-manager.js +42 -17
  97. package/dist/utils/tools-manager.js.map +1 -1
  98. package/docs/adaptorch-preview.md +84 -0
  99. package/docs/correctness-wall.md +117 -0
  100. package/docs/grok-harness.md +63 -0
  101. package/docs/loadout-domains/README.md +2 -1
  102. package/docs/loadout-domains/ai-agent-ops.md +8 -1
  103. package/docs/loadout-domains/frontend-ui.md +5 -1
  104. package/docs/loadout-domains/grok-harness.md +74 -0
  105. package/docs/providers.md +4 -0
  106. package/examples/extensions/correctness-wall/LOADOUT.md +87 -0
  107. package/examples/extensions/correctness-wall/README.md +111 -0
  108. package/examples/extensions/correctness-wall/adjudication-fixture.ts +151 -0
  109. package/examples/extensions/correctness-wall/index.ts +470 -0
  110. package/examples/extensions/correctness-wall/repair-state.ts +77 -0
  111. package/examples/extensions/correctness-wall/wall-cache.ts +81 -0
  112. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  113. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  114. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  115. package/examples/extensions/gondolin/package-lock.json +2 -2
  116. package/examples/extensions/gondolin/package.json +1 -1
  117. package/examples/extensions/preset.ts +2 -2
  118. package/examples/extensions/sandbox/package-lock.json +2 -2
  119. package/examples/extensions/sandbox/package.json +1 -1
  120. package/examples/extensions/with-deps/package-lock.json +2 -2
  121. package/examples/extensions/with-deps/package.json +1 -1
  122. package/npm-shrinkwrap.json +12 -12
  123. package/package.json +5 -5
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Correctness Wall — B2C patch safety gate for edit/write and explicit evaluation.
3
+ *
4
+ * Loads fast policy wall + optional OA adjudication from omk-adaptorch-wpl (relative import only).
5
+ */
6
+
7
+ import { readFile } from "node:fs/promises";
8
+ import { Type } from "omk-ai";
9
+ import type { ExtensionAPI, ExtensionContext } from "open-multi-agent-kit";
10
+ import type {
11
+ AdaptOrchClient,
12
+ UserVerdict,
13
+ VerdictCard,
14
+ VerificationReceipt,
15
+ } from "../../../../adaptorch-wpl/src/index.ts";
16
+ import {
17
+ buildRegeneratePacket,
18
+ capRepairHints,
19
+ evaluateCorrectnessWall,
20
+ parseRepairBudget,
21
+ shouldOfferRepair,
22
+ } from "../../../../adaptorch-wpl/src/index.ts";
23
+ import type { PolicyFlag } from "../../../../adaptorch-wpl/src/policy-wall.ts";
24
+ import {
25
+ applyEditsToNormalizedContent,
26
+ type Edit,
27
+ generateUnifiedPatch,
28
+ normalizeToLF,
29
+ stripBom,
30
+ } from "../../../../coding-agent/src/core/tools/edit-diff.ts";
31
+ import { resolveToCwd } from "../../../../coding-agent/src/core/tools/path-utils.ts";
32
+ import {
33
+ autoWireLiveAdaptOrch,
34
+ CORRECTNESS_WALL_EXTENSION_VERSION,
35
+ resolveAdjudicationFixturePath,
36
+ resolveOaClientForEvaluation,
37
+ wallVersionFromFixture,
38
+ } from "./adjudication-fixture.ts";
39
+ import { recordBlockedRepairAttempt, repairPacketKey, scopeKeyFromApprovedWriteScope } from "./repair-state.ts";
40
+ import { appendShadowTelemetry, type WallMode, writeVerdictCache } from "./wall-cache.ts";
41
+
42
+ const DEFAULT_KIND = "omk.patch";
43
+
44
+ const CorrectnessWallParams = Type.Object({
45
+ kind: Type.String({ description: "Verifier kind / packet kind for adjudication registry" }),
46
+ approvedWriteScope: Type.Optional(
47
+ Type.Array(Type.String(), {
48
+ description: "Glob paths allowed in the diff (overrides OMK_WALL_SCOPE for this call)",
49
+ }),
50
+ ),
51
+ previewOnly: Type.Optional(Type.Boolean({ description: "Preview-only fast wall (default true)", default: true })),
52
+ diffPath: Type.Optional(Type.String({ description: "Filesystem path to unified diff text" })),
53
+ runIds: Type.Optional(
54
+ Type.Array(Type.String(), { description: "AdaptOrch run ids for OA adjudication when previewOnly is false" }),
55
+ ),
56
+ packetId: Type.Optional(Type.String({ description: "Optional work packet id for receipt metadata" })),
57
+ adjudicationFixturePath: Type.Optional(
58
+ Type.String({
59
+ description:
60
+ "Path to OA adjudication fixture JSON (overrides OMK_WALL_OA_FIXTURE_PATH). Used when previewOnly is false and runIds are set.",
61
+ }),
62
+ ),
63
+ });
64
+
65
+ function parseWallMode(): WallMode {
66
+ const raw = (process.env.OMK_PATCH_SAFETY_WALL_MODE ?? "shadow").trim().toLowerCase();
67
+ if (raw === "soft" || raw === "hard") return raw;
68
+ return "shadow";
69
+ }
70
+
71
+ function parseApprovedScope(override?: string[]): string[] {
72
+ if (override !== undefined && override.length > 0) {
73
+ return override;
74
+ }
75
+ const env = process.env.OMK_WALL_SCOPE?.trim();
76
+ if (!env) return [];
77
+ return env
78
+ .split(",")
79
+ .map((s) => s.trim())
80
+ .filter((s) => s.length > 0);
81
+ }
82
+
83
+ /** Comma-separated AdaptOrch run ids from `OMK_WALL_RUN_IDS` (hook OA path when fixture env is set). */
84
+ function parseRunIdsFromEnv(): string[] {
85
+ const env = process.env.OMK_WALL_RUN_IDS?.trim();
86
+ if (!env) return [];
87
+ return env
88
+ .split(",")
89
+ .map((s) => s.trim())
90
+ .filter((s) => s.length > 0);
91
+ }
92
+
93
+ function wallOverrideActive(): boolean {
94
+ const v = process.env.OMK_WALL_OVERRIDE?.trim().toLowerCase();
95
+ return v === "1" || v === "true" || v === "yes";
96
+ }
97
+
98
+ function autoRegenerateEnabled(): boolean {
99
+ const v = process.env.OMK_WALL_AUTO_REGENERATE?.trim().toLowerCase();
100
+ return v === "1" || v === "true" || v === "yes";
101
+ }
102
+
103
+ function receiptSigningSecret(): string | undefined {
104
+ const v = process.env.OMK_WALL_RECEIPT_SIGNING_SECRET?.trim();
105
+ return v && v.length > 0 ? v : undefined;
106
+ }
107
+
108
+ function shortBlockReason(card: VerdictCard): string {
109
+ if (card.blocked_reasons.length > 0) return card.blocked_reasons[0];
110
+ if (card.next_actions.length > 0) return card.next_actions[0];
111
+ return `Correctness wall: ${card.verdict} (${card.risk} risk)`;
112
+ }
113
+
114
+ function shouldBlock(verdict: UserVerdict, mode: WallMode): boolean {
115
+ if (mode === "shadow") return false;
116
+ if (verdict === "PASS" || verdict === "ADVISORY") return false;
117
+ if (mode === "hard") return verdict === "BLOCKED" || verdict === "INCONCLUSIVE";
118
+ // soft
119
+ if (verdict !== "BLOCKED") return false;
120
+ return !wallOverrideActive();
121
+ }
122
+
123
+ function inconclusiveCard(kind: string, packetId?: string): VerdictCard {
124
+ return {
125
+ schemaVersion: 1,
126
+ verdict: "INCONCLUSIVE",
127
+ risk: "high",
128
+ limits: { requiresHumanReview: true, previewOnly: true },
129
+ passed_checks: [],
130
+ blocked_reasons: ["No diff text or diffPath was provided for evaluation."],
131
+ next_actions: ["Deep Check"],
132
+ packetId,
133
+ kind,
134
+ };
135
+ }
136
+
137
+ async function readDiffFromPath(diffPath: string): Promise<string> {
138
+ return readFile(diffPath, "utf-8");
139
+ }
140
+
141
+ type EditInput = {
142
+ path?: string;
143
+ edits?: Edit[];
144
+ oldText?: string;
145
+ newText?: string;
146
+ content?: string;
147
+ };
148
+
149
+ function normalizeEdits(input: EditInput): { path: string; edits: Edit[] } | { error: string } {
150
+ const path = typeof input.path === "string" ? input.path : "";
151
+ if (!path) return { error: "edit/write input missing path" };
152
+
153
+ let edits: Edit[] = [];
154
+ if (Array.isArray(input.edits)) {
155
+ edits = input.edits.filter((e): e is Edit => typeof e?.oldText === "string" && typeof e?.newText === "string");
156
+ } else if (typeof input.oldText === "string" && typeof input.newText === "string") {
157
+ edits = [{ oldText: input.oldText, newText: input.newText }];
158
+ }
159
+ return { path, edits };
160
+ }
161
+
162
+ async function previewDiffForEdit(input: EditInput, cwd: string): Promise<string | { error: string }> {
163
+ const normalized = normalizeEdits(input);
164
+ if ("error" in normalized) return normalized;
165
+ const { path, edits } = normalized;
166
+ if (edits.length === 0) return { error: "edit input has no edits" };
167
+
168
+ try {
169
+ const absolutePath = resolveToCwd(path, cwd);
170
+ const raw = await readFile(absolutePath, "utf-8");
171
+ const { text } = stripBom(raw);
172
+ const base = normalizeToLF(text);
173
+ const { newContent } = applyEditsToNormalizedContent(base, edits, path);
174
+ return generateUnifiedPatch(path, base, newContent);
175
+ } catch (err) {
176
+ return { error: err instanceof Error ? err.message : String(err) };
177
+ }
178
+ }
179
+
180
+ function previewDiffForWrite(input: EditInput): string | { error: string } {
181
+ const path = typeof input.path === "string" ? input.path : "";
182
+ const content = typeof input.content === "string" ? input.content : "";
183
+ if (!path) return { error: "write input missing path" };
184
+ const newContent = normalizeToLF(content);
185
+ return generateUnifiedPatch(path, "", newContent);
186
+ }
187
+
188
+ async function evaluateDiff(options: {
189
+ kind: string;
190
+ diffText?: string;
191
+ approvedWriteScope: string[];
192
+ previewOnly: boolean;
193
+ runIds?: string[];
194
+ packetId?: string;
195
+ adjudicationFixturePath?: string;
196
+ dispatchRecordId?: string;
197
+ }): Promise<{ verdictCard: VerdictCard; receipt: VerificationReceipt & { wall_version: string } }> {
198
+ const { client, fixture } = await resolveOaClientForEvaluation({
199
+ previewOnly: options.previewOnly,
200
+ runIds: options.runIds,
201
+ adjudicationFixturePath: options.adjudicationFixturePath,
202
+ });
203
+ const wall_version = wallVersionFromFixture(fixture);
204
+ const dispatchRecordId = options.dispatchRecordId ?? fixture?.dispatchRecordId ?? "correctness-wall-extension";
205
+ const { verdictCard, receipt } = await evaluateCorrectnessWall({
206
+ kind: options.kind,
207
+ diffText: options.diffText,
208
+ approvedWriteScope: options.approvedWriteScope,
209
+ previewOnly: options.previewOnly,
210
+ runIds: options.runIds,
211
+ packetId: options.packetId,
212
+ dispatchRecordId,
213
+ client: client as AdaptOrchClient | undefined,
214
+ receiptSigningSecret: receiptSigningSecret(),
215
+ });
216
+ return { verdictCard, receipt: { ...receipt, wall_version } };
217
+ }
218
+
219
+ function repairHintsForBlocked(card: VerdictCard, budget: number): string[] | undefined {
220
+ if (card.verdict !== "BLOCKED") return undefined;
221
+ const fromCard = card.repairHints;
222
+ if (fromCard !== undefined && fromCard.length > 0) return capRepairHints(fromCard, budget);
223
+ return undefined;
224
+ }
225
+
226
+ function formatEvaluateJson(
227
+ verdictCard: VerdictCard,
228
+ receipt: VerificationReceipt & { wall_version?: string },
229
+ options?: { attemptCount?: number; policyFlags?: PolicyFlag[]; previewOnly?: boolean; diffPaths?: string[] },
230
+ ): string {
231
+ const budget = parseRepairBudget();
232
+ const repairHints = repairHintsForBlocked(verdictCard, budget);
233
+ const body: {
234
+ verdictCard: VerdictCard;
235
+ receipt: unknown;
236
+ repairHints?: string[];
237
+ regeneratePacket?: ReturnType<typeof buildRegeneratePacket>;
238
+ } = {
239
+ verdictCard,
240
+ receipt,
241
+ };
242
+ if (repairHints !== undefined && repairHints.length > 0) {
243
+ body.repairHints = repairHints;
244
+ }
245
+ if (options?.policyFlags !== undefined) {
246
+ body.regeneratePacket = buildRegeneratePacket({
247
+ userVerdict: verdictCard.verdict,
248
+ policyFlags: options.policyFlags,
249
+ previewOnly: options.previewOnly ?? true,
250
+ diffPaths: options.diffPaths ?? [],
251
+ attemptCount: options.attemptCount ?? 0,
252
+ autoRegenerateEnabled: autoRegenerateEnabled(),
253
+ });
254
+ }
255
+ return JSON.stringify(body, null, 2);
256
+ }
257
+
258
+ async function persistToolCallVerdict(
259
+ ctx: ExtensionContext,
260
+ mode: WallMode,
261
+ verdictCard: VerdictCard,
262
+ meta: { tool: "edit" | "write"; previewOnly: boolean; usedOaFixture: boolean; wall_version: string },
263
+ ): Promise<void> {
264
+ const wouldBlock = shouldBlock(verdictCard.verdict, mode);
265
+ try {
266
+ await writeVerdictCache(ctx.cwd, { mode, verdict: verdictCard.verdict, wouldBlock, verdictCard });
267
+ } catch {
268
+ // Cache write must not block tool execution.
269
+ }
270
+ if (mode !== "shadow") return;
271
+ try {
272
+ await appendShadowTelemetry(ctx.cwd, {
273
+ event: "correctness_wall_shadow",
274
+ wall_version: meta.wall_version,
275
+ mode,
276
+ verdict: verdictCard.verdict,
277
+ wouldBlock,
278
+ kind: verdictCard.kind,
279
+ tool: meta.tool,
280
+ previewOnly: meta.previewOnly,
281
+ usedOaFixture: meta.usedOaFixture,
282
+ timestamp: new Date().toISOString(),
283
+ });
284
+ } catch {
285
+ // Telemetry must not block tool execution.
286
+ }
287
+ }
288
+
289
+ function shadowBlockedNotifySuffix(card: VerdictCard, mode: WallMode, budget: number): string {
290
+ if (mode !== "shadow" || card.verdict !== "BLOCKED") return "";
291
+ const hints = repairHintsForBlocked(card, budget);
292
+ if (hints === undefined || hints.length === 0) return "";
293
+ return ` — repair hints: ${hints.join("; ")}`;
294
+ }
295
+
296
+ function repairBudgetLimitSuffix(attempts: number, budget: number): string {
297
+ if (attempts < budget) return "";
298
+ return ` — repair budget exhausted (${attempts}/${budget} blocked attempts; capped regenerate)`;
299
+ }
300
+
301
+ export default function (omk: ExtensionAPI) {
302
+ // Auto-wire live adaptorch MCP transport when the host exposes `callMcpTool`.
303
+ // Using the live client is still gated by OMK_WALL_OA_TRANSPORT=mcp (explicit opt-in);
304
+ // when the capability is absent this is a no-op and fixture transport remains default.
305
+ autoWireLiveAdaptOrch(omk);
306
+
307
+ omk.registerTool({
308
+ name: "correctness_wall_evaluate",
309
+ label: "Correctness Wall",
310
+ description:
311
+ "Evaluate a patch against the B2C correctness wall (scope, policy flags, optional OA adjudication). Supply diffPath or use from edit/write gate context via preview.",
312
+ parameters: CorrectnessWallParams,
313
+
314
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
315
+ const kind = params.kind as string;
316
+ const previewOnly = params.previewOnly !== false;
317
+ const packetId = params.packetId as string | undefined;
318
+ const runIds = params.runIds as string[] | undefined;
319
+ const adjudicationFixturePath = params.adjudicationFixturePath as string | undefined;
320
+ const approvedWriteScope = parseApprovedScope(params.approvedWriteScope as string[] | undefined);
321
+
322
+ let diffText: string | undefined;
323
+ if (typeof params.diffPath === "string" && params.diffPath.length > 0) {
324
+ diffText = await readDiffFromPath(params.diffPath);
325
+ } else {
326
+ const card = inconclusiveCard(kind, packetId);
327
+ const text = formatEvaluateJson(card, {
328
+ schemaVersion: 1,
329
+ evaluatedAt: new Date().toISOString(),
330
+ kind,
331
+ packetId,
332
+ runIds: runIds ?? [],
333
+ previewOnly,
334
+ canApply: false,
335
+ shouldSubmit: false,
336
+ policyFlags: [],
337
+ diffPaths: [],
338
+ wall_version: CORRECTNESS_WALL_EXTENSION_VERSION,
339
+ });
340
+ return {
341
+ content: [{ type: "text", text }],
342
+ details: { verdict: card.verdict },
343
+ };
344
+ }
345
+
346
+ const { verdictCard, receipt } = await evaluateDiff({
347
+ kind,
348
+ diffText,
349
+ approvedWriteScope,
350
+ previewOnly,
351
+ runIds,
352
+ packetId,
353
+ adjudicationFixturePath,
354
+ });
355
+
356
+ return {
357
+ content: [
358
+ {
359
+ type: "text",
360
+ text: formatEvaluateJson(verdictCard, receipt, {
361
+ policyFlags: receipt.policyFlags as PolicyFlag[],
362
+ previewOnly,
363
+ diffPaths: receipt.diffPaths,
364
+ }),
365
+ },
366
+ ],
367
+ details: { verdict: verdictCard.verdict, wall_version: receipt.wall_version },
368
+ };
369
+ },
370
+ });
371
+
372
+ omk.on("tool_call", async (event, ctx: ExtensionContext) => {
373
+ if (event.toolName !== "edit" && event.toolName !== "write") {
374
+ return undefined;
375
+ }
376
+
377
+ const mode = parseWallMode();
378
+ const kind = `${DEFAULT_KIND}.${event.toolName}`;
379
+ const approvedWriteScope = parseApprovedScope();
380
+ const runIds = parseRunIdsFromEnv();
381
+ const adjudicationFixturePath = resolveAdjudicationFixturePath(undefined);
382
+ const hookOaEnabled = runIds.length > 0 && adjudicationFixturePath !== undefined;
383
+ const previewOnly = !hookOaEnabled;
384
+
385
+ const input = event.input as EditInput;
386
+ const preview =
387
+ event.toolName === "write" ? previewDiffForWrite(input) : await previewDiffForEdit(input, ctx.cwd);
388
+
389
+ if (typeof preview === "object" && "error" in preview) {
390
+ const card: VerdictCard = {
391
+ schemaVersion: 1,
392
+ verdict: "INCONCLUSIVE",
393
+ risk: "high",
394
+ limits: { requiresHumanReview: true, previewOnly: true },
395
+ passed_checks: [],
396
+ blocked_reasons: [`Could not build diff preview: ${preview.error}`],
397
+ next_actions: ["Deep Check"],
398
+ kind,
399
+ };
400
+ const verdict = card.verdict;
401
+ await persistToolCallVerdict(ctx, mode, card, {
402
+ tool: event.toolName,
403
+ previewOnly: true,
404
+ usedOaFixture: false,
405
+ wall_version: CORRECTNESS_WALL_EXTENSION_VERSION,
406
+ });
407
+ if (ctx.hasUI) {
408
+ ctx.ui.notify(`Correctness wall (${mode}): ${verdict} — ${shortBlockReason(card)}`, "warning");
409
+ }
410
+ if (shouldBlock(verdict, mode)) {
411
+ return { block: true, reason: shortBlockReason(card) };
412
+ }
413
+ return undefined;
414
+ }
415
+
416
+ const { verdictCard, receipt } = await evaluateDiff({
417
+ kind,
418
+ diffText: preview,
419
+ approvedWriteScope,
420
+ previewOnly,
421
+ runIds: hookOaEnabled ? runIds : undefined,
422
+ adjudicationFixturePath: hookOaEnabled ? adjudicationFixturePath : undefined,
423
+ });
424
+
425
+ await persistToolCallVerdict(ctx, mode, verdictCard, {
426
+ tool: event.toolName,
427
+ previewOnly,
428
+ usedOaFixture: hookOaEnabled,
429
+ wall_version: receipt.wall_version,
430
+ });
431
+
432
+ const repairBudget = parseRepairBudget();
433
+ let repairLimitSuffix = "";
434
+ if (verdictCard.verdict === "BLOCKED") {
435
+ const packetKey = repairPacketKey({
436
+ kind,
437
+ scopeKey: scopeKeyFromApprovedWriteScope(approvedWriteScope),
438
+ });
439
+ try {
440
+ const state = await recordBlockedRepairAttempt(ctx.cwd, packetKey, verdictCard.verdict);
441
+ if (!shouldOfferRepair(verdictCard.verdict, state.attempts, repairBudget)) {
442
+ repairLimitSuffix = repairBudgetLimitSuffix(state.attempts, repairBudget);
443
+ }
444
+ } catch {
445
+ // Repair state must not block tool execution.
446
+ }
447
+ }
448
+
449
+ const summary = `Correctness wall (${mode}): ${verdictCard.verdict}`;
450
+ const detail =
451
+ verdictCard.blocked_reasons[0] ??
452
+ verdictCard.passed_checks[0] ??
453
+ (previewOnly ? "preview-only evaluation" : "");
454
+
455
+ if (ctx.hasUI) {
456
+ const level =
457
+ verdictCard.verdict === "BLOCKED" ? "error" : verdictCard.verdict === "INCONCLUSIVE" ? "warning" : "info";
458
+ ctx.ui.notify(
459
+ `${summary}${detail ? ` — ${detail}` : ""}${shadowBlockedNotifySuffix(verdictCard, mode, repairBudget)}${repairLimitSuffix}`,
460
+ level,
461
+ );
462
+ }
463
+
464
+ if (shouldBlock(verdictCard.verdict, mode)) {
465
+ return { block: true, reason: shortBlockReason(verdictCard) };
466
+ }
467
+
468
+ return undefined;
469
+ });
470
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Per-packet repair attempt state under .omk/wall-cache/repair-budget.json.
3
+ */
4
+
5
+ import { createHash } from "node:crypto";
6
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import type { RepairBudgetState, UserVerdict } from "../../../../adaptorch-wpl/src/index.ts";
9
+
10
+ const REPAIR_STATE_REL = join(".omk", "wall-cache", "repair-budget.json");
11
+
12
+ type RepairBudgetFile = {
13
+ schemaVersion: 1;
14
+ packets: Record<string, RepairBudgetState>;
15
+ };
16
+
17
+ function emptyFile(): RepairBudgetFile {
18
+ return { schemaVersion: 1, packets: {} };
19
+ }
20
+
21
+ export function repairPacketKey(options: { packetId?: string; kind: string; scopeKey: string }): string {
22
+ if (options.packetId !== undefined && options.packetId.length > 0) {
23
+ return options.packetId;
24
+ }
25
+ const material = `${options.kind}\0${options.scopeKey}`;
26
+ return createHash("sha256").update(material, "utf8").digest("hex");
27
+ }
28
+
29
+ export function scopeKeyFromApprovedWriteScope(approvedWriteScope: string[]): string {
30
+ if (approvedWriteScope.length === 0) return "";
31
+ return [...approvedWriteScope].sort().join(",");
32
+ }
33
+
34
+ async function readRepairStateFile(cwd: string): Promise<RepairBudgetFile> {
35
+ const path = join(cwd, REPAIR_STATE_REL);
36
+ try {
37
+ const raw = await readFile(path, "utf-8");
38
+ const parsed = JSON.parse(raw) as Partial<RepairBudgetFile>;
39
+ if (parsed.schemaVersion !== 1 || typeof parsed.packets !== "object" || parsed.packets === null) {
40
+ return emptyFile();
41
+ }
42
+ return { schemaVersion: 1, packets: parsed.packets };
43
+ } catch (err) {
44
+ if (err && typeof err === "object" && "code" in err && (err as NodeJS.ErrnoException).code === "ENOENT") {
45
+ return emptyFile();
46
+ }
47
+ return emptyFile();
48
+ }
49
+ }
50
+
51
+ async function writeRepairStateFile(cwd: string, file: RepairBudgetFile): Promise<void> {
52
+ const dir = join(cwd, ".omk", "wall-cache");
53
+ await mkdir(dir, { recursive: true });
54
+ await writeFile(join(cwd, REPAIR_STATE_REL), `${JSON.stringify(file, null, 2)}\n`, "utf-8");
55
+ }
56
+
57
+ export async function readRepairBudgetState(cwd: string, packetKey: string): Promise<RepairBudgetState | undefined> {
58
+ const file = await readRepairStateFile(cwd);
59
+ return file.packets[packetKey];
60
+ }
61
+
62
+ export async function recordBlockedRepairAttempt(
63
+ cwd: string,
64
+ packetKey: string,
65
+ verdict: UserVerdict,
66
+ ): Promise<RepairBudgetState> {
67
+ const file = await readRepairStateFile(cwd);
68
+ const prev = file.packets[packetKey];
69
+ const next: RepairBudgetState = {
70
+ packetKey,
71
+ attempts: (prev?.attempts ?? 0) + 1,
72
+ lastVerdict: verdict,
73
+ };
74
+ file.packets[packetKey] = next;
75
+ await writeRepairStateFile(cwd, file);
76
+ return next;
77
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Persists latest correctness-wall verdict metadata under .omk/wall-cache (no diff, no secrets).
3
+ */
4
+
5
+ import { mkdir, writeFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import type { UserVerdict, VerdictCard } from "../../../../adaptorch-wpl/src/index.ts";
8
+
9
+ export type WallMode = "shadow" | "soft" | "hard";
10
+
11
+ export interface WallVerdictCacheEntry {
12
+ mode: WallMode;
13
+ verdict: UserVerdict;
14
+ wouldBlock: boolean;
15
+ verdictCardSummary: {
16
+ verdict: UserVerdict;
17
+ risk: VerdictCard["risk"];
18
+ kind: string;
19
+ blockedReasonCount: number;
20
+ passedCheckCount: number;
21
+ firstBlockedReason?: string;
22
+ firstNextAction?: string;
23
+ };
24
+ timestamp: string;
25
+ }
26
+
27
+ /** Shadow-mode telemetry line (append-only NDJSON; no diff or secrets). */
28
+ export interface WallShadowTelemetryEvent {
29
+ event: "correctness_wall_shadow";
30
+ wall_version: string;
31
+ mode: WallMode;
32
+ verdict: UserVerdict;
33
+ wouldBlock: boolean;
34
+ kind: string;
35
+ tool?: "edit" | "write" | "correctness_wall_evaluate";
36
+ previewOnly: boolean;
37
+ usedOaFixture: boolean;
38
+ timestamp: string;
39
+ }
40
+
41
+ const CACHE_REL = join(".omk", "wall-cache", "latest.json");
42
+ const SHADOW_TELEMETRY_REL = join(".omk", "wall-cache", "shadow-telemetry.ndjson");
43
+
44
+ function summarizeVerdictCard(card: VerdictCard): WallVerdictCacheEntry["verdictCardSummary"] {
45
+ return {
46
+ verdict: card.verdict,
47
+ risk: card.risk,
48
+ kind: card.kind,
49
+ blockedReasonCount: card.blocked_reasons.length,
50
+ passedCheckCount: card.passed_checks.length,
51
+ firstBlockedReason: card.blocked_reasons[0],
52
+ firstNextAction: card.next_actions[0],
53
+ };
54
+ }
55
+
56
+ export async function writeVerdictCache(
57
+ cwd: string,
58
+ entry: {
59
+ mode: WallMode;
60
+ verdict: UserVerdict;
61
+ wouldBlock: boolean;
62
+ verdictCard: VerdictCard;
63
+ },
64
+ ): Promise<void> {
65
+ const payload: WallVerdictCacheEntry = {
66
+ mode: entry.mode,
67
+ verdict: entry.verdict,
68
+ wouldBlock: entry.wouldBlock,
69
+ verdictCardSummary: summarizeVerdictCard(entry.verdictCard),
70
+ timestamp: new Date().toISOString(),
71
+ };
72
+ const dir = join(cwd, ".omk", "wall-cache");
73
+ await mkdir(dir, { recursive: true });
74
+ await writeFile(join(cwd, CACHE_REL), `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
75
+ }
76
+
77
+ export async function appendShadowTelemetry(cwd: string, event: WallShadowTelemetryEvent): Promise<void> {
78
+ const dir = join(cwd, ".omk", "wall-cache");
79
+ await mkdir(dir, { recursive: true });
80
+ await writeFile(join(cwd, SHADOW_TELEMETRY_REL), `${JSON.stringify(event)}\n`, { flag: "a" });
81
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omk-extension-custom-provider",
3
- "version": "0.90.5",
3
+ "version": "0.90.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omk-extension-custom-provider",
9
- "version": "0.90.5",
9
+ "version": "0.90.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "omk-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.90.5",
4
+ "version": "0.90.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "omk-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.90.5",
4
+ "version": "0.90.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omk-extension-gondolin",
3
- "version": "0.90.5",
3
+ "version": "0.90.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omk-extension-gondolin",
9
- "version": "0.90.5",
9
+ "version": "0.90.7",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "omk-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.90.5",
4
+ "version": "0.90.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",