@moda-ai/cli 1.55.54 → 1.55.55

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 (34) hide show
  1. package/dist/{agent-rmmtap23.js → agent-dc9zkdsm.js} +10 -10
  2. package/dist/{cli-3dj84cbk.js → cli-2jwxdhh5.js} +1 -1
  3. package/dist/{cli-1ehmy9rh.js → cli-31ybq5f2.js} +2 -2
  4. package/dist/{cli-rdh9jjr2.js → cli-94r5x9bg.js} +1 -1
  5. package/dist/{cli-c1k20j6e.js → cli-9a4b1wxz.js} +1 -1
  6. package/dist/{cli-4pzbgxcn.js → cli-9g8kp42c.js} +2 -2
  7. package/dist/{cli-mf37dxpc.js → cli-b918vs2m.js} +4 -4
  8. package/dist/{cli-4vc2cksg.js → cli-eqj302as.js} +5 -5
  9. package/dist/{cli-wry78bg8.js → cli-hawwf30b.js} +1 -1
  10. package/dist/{cli-c1vmgdjb.js → cli-jnwba1f3.js} +8 -8
  11. package/dist/{cli-dxxc7tzq.js → cli-m25c0rke.js} +3 -3
  12. package/dist/{cli-40fqk078.js → cli-pnarsb96.js} +1 -1
  13. package/dist/{cli-7ye5004e.js → cli-px4dmcy0.js} +68 -301
  14. package/dist/{cli-6hdqp65a.js → cli-rtkjjf0a.js} +1255 -66
  15. package/dist/{cli-gtke9wrw.js → cli-v2h21cgf.js} +1 -1
  16. package/dist/{cli-vswckv9g.js → cli-y5d33a1y.js} +1 -1
  17. package/dist/cli.js +20 -20
  18. package/dist/{doctor-b34em2fm.js → doctor-r16n0me8.js} +8 -8
  19. package/dist/{harness-github-actions-rayyxh1w.js → harness-github-actions-6bex338w.js} +8 -8
  20. package/dist/{harness-jcqwf5h6.js → harness-j0n400bv.js} +8 -8
  21. package/dist/{harness-report-rjcjbh93.js → harness-report-gz5d3y48.js} +1 -1
  22. package/dist/{index-cqnp8pfv.js → index-ecy3ntzc.js} +14 -14
  23. package/dist/{production-intelligence-zek0rkzj.js → production-intelligence-fqvhzq6s.js} +4 -4
  24. package/dist/{profile-commands-nbaec4y9.js → profile-commands-yvpe4d8y.js} +5 -5
  25. package/dist/{prompt-attribution-gyx0g661.js → prompt-attribution-x84cgdct.js} +8 -8
  26. package/dist/prompt-source-mcp.js +8 -8
  27. package/dist/{prompt-sources-qw2p4t5h.js → prompt-sources-1hycgx6r.js} +2 -2
  28. package/dist/{prompts-s4y90zs7.js → prompts-r0mv69yx.js} +6 -6
  29. package/dist/{provision-tch8rc3y.js → provision-aqhfbnez.js} +6 -6
  30. package/dist/{registry-tv8fktxc.js → registry-wv1zzyce.js} +4 -4
  31. package/dist/{skills-mwftqzyk.js → skills-etzcqhp8.js} +6 -6
  32. package/dist/{sync-qxf3gkhy.js → sync-p4dz7qmn.js} +10 -10
  33. package/package.json +1 -1
  34. package/skills/integration/index.json +2 -2
@@ -9,19 +9,1196 @@ import {
9
9
  import { createHash } from "node:crypto";
10
10
  import {
11
11
  existsSync,
12
- lstatSync,
12
+ lstatSync as lstatSync2,
13
13
  mkdirSync,
14
- readFileSync,
14
+ readFileSync as readFileSync3,
15
15
  writeFileSync
16
16
  } from "node:fs";
17
- import { basename, dirname, join, relative, resolve } from "node:path";
17
+ import { basename, dirname, join as join3, relative as relative3, resolve } from "node:path";
18
+
19
+ // src/harness-component-status.ts
20
+ import { closeSync, openSync, readdirSync as readdirSync2, readFileSync as readFileSync2, readSync } from "node:fs";
21
+ import { isAbsolute as isAbsolute2, join as join2, posix as posix2, relative as relative2 } from "node:path";
22
+
23
+ // src/harness-wrangler-bindings.ts
24
+ import { lstatSync, readdirSync, readFileSync } from "node:fs";
25
+ import { isAbsolute, join, posix, relative } from "node:path";
26
+ var WRANGLER_FILE = /^wrangler(\.[\w-]+)?\.jsonc?$/;
27
+ var ANY_WRANGLER_FILE = /^wrangler(?:\.[\w-]+)?\.(?:jsonc?|toml)$/;
28
+ var SKIPPED_DIRS = new Set(["node_modules", ".git", ".wrangler", "dist", "build", ".next", ".turbo", "vendor"]);
29
+ var MAX_DEPTH = 4;
30
+ var AGENT_CLASS = /Agent(?:V\d+)?$/;
31
+ var TEST_SEGMENT = /^(?:test|tests|__tests__|test-fixtures|fixtures?|__fixtures__|e2e|mocks?)$/i;
32
+ var TEST_TOKEN = /(?:^|[._-])(?:test|tests|spec|e2e|fixtures?|mocks?)(?:$|[._-])/i;
33
+ function isTestWorkerConfig(input) {
34
+ const segments = input.path.split("/");
35
+ const file = segments[segments.length - 1] ?? "";
36
+ const variant = file.replace(/^wrangler\.?/, "").replace(/\.(?:jsonc?|toml)$/, "");
37
+ if (segments.slice(0, -1).some((segment) => TEST_SEGMENT.test(segment)))
38
+ return true;
39
+ if (variant && TEST_TOKEN.test(variant))
40
+ return true;
41
+ if (typeof input.name === "string" && TEST_TOKEN.test(input.name))
42
+ return true;
43
+ return (input.mains ?? []).some((main) => typeof main === "string" && main.split("/").slice(0, -1).some((segment) => TEST_SEGMENT.test(segment)));
44
+ }
45
+ function stripJsonc(text) {
46
+ let out = "";
47
+ let inString = false;
48
+ for (let i = 0;i < text.length; i += 1) {
49
+ const ch = text[i];
50
+ if (inString) {
51
+ out += ch;
52
+ if (ch === "\\") {
53
+ out += text[i + 1] ?? "";
54
+ i += 1;
55
+ } else if (ch === '"') {
56
+ inString = false;
57
+ }
58
+ continue;
59
+ }
60
+ if (ch === '"') {
61
+ inString = true;
62
+ out += ch;
63
+ } else if (ch === "/" && text[i + 1] === "/") {
64
+ while (i < text.length && text[i] !== `
65
+ `)
66
+ i += 1;
67
+ out += `
68
+ `;
69
+ } else if (ch === "/" && text[i + 1] === "*") {
70
+ i += 2;
71
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) {
72
+ if (text[i] === `
73
+ `)
74
+ out += `
75
+ `;
76
+ i += 1;
77
+ }
78
+ i += 1;
79
+ } else {
80
+ out += ch;
81
+ }
82
+ }
83
+ return out.replace(/,(\s*[}\]])/g, "$1");
84
+ }
85
+ function findWranglerFiles(rootDir, pattern = WRANGLER_FILE) {
86
+ const found = [];
87
+ const walk = (dir, depth) => {
88
+ let entries;
89
+ try {
90
+ entries = readdirSync(dir);
91
+ } catch {
92
+ return;
93
+ }
94
+ for (const name of entries) {
95
+ const abs = join(dir, name);
96
+ let stat;
97
+ try {
98
+ stat = lstatSync(abs);
99
+ } catch {
100
+ continue;
101
+ }
102
+ if (stat.isDirectory()) {
103
+ if (depth < MAX_DEPTH && !SKIPPED_DIRS.has(name) && !name.startsWith("."))
104
+ walk(abs, depth + 1);
105
+ } else if (stat.isFile() && pattern.test(name)) {
106
+ found.push(abs);
107
+ }
108
+ }
109
+ };
110
+ walk(rootDir, 0);
111
+ return found.sort();
112
+ }
113
+ function lineOf(text, className) {
114
+ const index = text.search(new RegExp(`"class_name"\\s*:\\s*"${className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"`));
115
+ return index < 0 ? undefined : text.slice(0, index).split(`
116
+ `).length;
117
+ }
118
+ function workerDir(path) {
119
+ const slash = path.lastIndexOf("/");
120
+ return slash < 0 ? "" : path.slice(0, slash);
121
+ }
122
+ function detectBoundRuntimeClasses(rootDir) {
123
+ const byClass = new Map;
124
+ for (const abs of findWranglerFiles(rootDir)) {
125
+ let text;
126
+ let config;
127
+ try {
128
+ text = readFileSync(abs, "utf8");
129
+ config = JSON.parse(stripJsonc(text));
130
+ } catch {
131
+ continue;
132
+ }
133
+ if (!config || typeof config !== "object")
134
+ continue;
135
+ const path = relative(rootDir, abs).split("\\").join("/");
136
+ const root = config;
137
+ if (isTestWorkerConfig({ path, name: root.name, mains: [root.main] }))
138
+ continue;
139
+ const sections = [root, ...Object.values(root.env ?? {})];
140
+ for (const section of sections) {
141
+ if (!section || typeof section !== "object")
142
+ continue;
143
+ const s = section;
144
+ const durable = s.durable_objects?.bindings;
145
+ const entries = [
146
+ ...Array.isArray(durable) ? durable.map((b) => ["durable_object", b]) : [],
147
+ ...Array.isArray(s.workflows) ? s.workflows.map((w) => ["workflow", w]) : []
148
+ ];
149
+ for (const [kind, raw] of entries) {
150
+ if (!raw || typeof raw !== "object")
151
+ continue;
152
+ const entry = raw;
153
+ const className = typeof entry.class_name === "string" ? entry.class_name : "";
154
+ const scriptName = typeof entry.script_name === "string" && entry.script_name ? entry.script_name : undefined;
155
+ const key = `${className}|${scriptName ? "external" : `config:${path}`}`;
156
+ if (!className || byClass.has(key))
157
+ continue;
158
+ const binding = typeof (kind === "workflow" ? entry.binding : entry.name) === "string" ? String(kind === "workflow" ? entry.binding : entry.name) : className;
159
+ byClass.set(key, {
160
+ className,
161
+ kind,
162
+ binding,
163
+ ...scriptName ? { scriptName } : {},
164
+ path,
165
+ line: lineOf(text, className)
166
+ });
167
+ }
168
+ }
169
+ }
170
+ return [...byClass.values()];
171
+ }
172
+ var RUNTIME_BASES = {
173
+ DurableObject: "durable_object",
174
+ WorkflowEntrypoint: "workflow",
175
+ AgentWorkflow: "workflow",
176
+ Agent: "agent",
177
+ AIChatAgent: "agent",
178
+ Think: "agent",
179
+ McpAgent: "agent"
180
+ };
181
+ var SOURCE_FILE = /\.tsx?$/;
182
+ var SKIPPED_SOURCE_FILE = /\.(?:test|spec)\.tsx?$|\.d\.ts$/;
183
+ var TEST_DIR = /^(?:tests?|__tests__|__mocks__|e2e|(?:.*[-_.])?fixtures?|__fixtures__)$/i;
184
+ var MAX_SOURCE_DEPTH = 12;
185
+ var MAX_SOURCE_FILES = 20000;
186
+ var MAX_SOURCE_BYTES = 1e6;
187
+ var CLASS_DECLARATION = /^[ \t]*(export[ \t]+(?:default[ \t]+)?)?(abstract[ \t]+)?class[ \t]+([A-Za-z_$][\w$]*)(?:[ \t]*<[^\n{]*?>)?[ \t]+extends[ \t]+([A-Za-z_$][\w$]*)/gm;
188
+ function findSourceFiles(rootDir) {
189
+ const found = [];
190
+ const walk = (dir, depth) => {
191
+ let entries;
192
+ try {
193
+ entries = readdirSync(dir).sort();
194
+ } catch {
195
+ return;
196
+ }
197
+ for (const name of entries) {
198
+ if (found.length >= MAX_SOURCE_FILES)
199
+ return;
200
+ const abs = join(dir, name);
201
+ let stat;
202
+ try {
203
+ stat = lstatSync(abs);
204
+ } catch {
205
+ continue;
206
+ }
207
+ if (stat.isDirectory()) {
208
+ if (depth < MAX_SOURCE_DEPTH && !SKIPPED_DIRS.has(name) && !name.startsWith(".") && !TEST_DIR.test(name))
209
+ walk(abs, depth + 1);
210
+ } else if (stat.isFile() && SOURCE_FILE.test(name) && !SKIPPED_SOURCE_FILE.test(name) && stat.size <= MAX_SOURCE_BYTES) {
211
+ found.push(abs);
212
+ }
213
+ }
214
+ };
215
+ walk(rootDir, 0);
216
+ return found;
217
+ }
218
+ function bindingsOfSection(section) {
219
+ const durable = section.durable_objects?.bindings;
220
+ const raw = [...Array.isArray(durable) ? durable : [], ...Array.isArray(section.workflows) ? section.workflows : []];
221
+ return raw.flatMap((entry) => {
222
+ if (!entry || typeof entry !== "object")
223
+ return [];
224
+ const { class_name: className, script_name: scriptName } = entry;
225
+ if (typeof className !== "string" || !className)
226
+ return [];
227
+ return [{ className, ...typeof scriptName === "string" && scriptName ? { scriptName } : {} }];
228
+ });
229
+ }
230
+ function readWranglerScope(rootDir, abs) {
231
+ let text;
232
+ try {
233
+ text = readFileSync(abs, "utf8");
234
+ } catch {
235
+ return;
236
+ }
237
+ const names = [];
238
+ const mains = [];
239
+ const bindings = [];
240
+ let topName;
241
+ let topMain;
242
+ if (abs.endsWith(".toml")) {
243
+ let header = "";
244
+ let binding;
245
+ for (const rawLine of text.split(`
246
+ `)) {
247
+ const line = rawLine.replace(/\s+#.*$/, "").trim();
248
+ const table = /^\[\[?\s*([^\]]+?)\s*\]\]?$/.exec(line);
249
+ if (table) {
250
+ header = table[1];
251
+ binding = undefined;
252
+ if (/(?:^|\.)(?:durable_objects\.bindings|workflows)$/.test(header) && line.startsWith("[[")) {
253
+ binding = { className: "" };
254
+ bindings.push(binding);
255
+ }
256
+ continue;
257
+ }
258
+ const pair = /^([\w.-]+)\s*=\s*["']([^"']*)["']/.exec(line);
259
+ if (!pair)
260
+ continue;
261
+ const [, key, value] = pair;
262
+ const envSection = header === "" || /^env\.[\w-]+$/.test(header);
263
+ if (binding && key === "class_name")
264
+ binding.className = value;
265
+ else if (binding && key === "script_name" && value)
266
+ binding.scriptName = value;
267
+ else if (envSection && key === "name" && value) {
268
+ names.push(value);
269
+ if (header === "")
270
+ topName = value;
271
+ } else if (envSection && key === "main" && value) {
272
+ mains.push(value);
273
+ if (header === "")
274
+ topMain = value;
275
+ }
276
+ }
277
+ } else {
278
+ let config;
279
+ try {
280
+ config = JSON.parse(stripJsonc(text));
281
+ } catch {
282
+ return;
283
+ }
284
+ if (!config || typeof config !== "object" || Array.isArray(config))
285
+ return;
286
+ const root = config;
287
+ if (typeof root.name === "string")
288
+ topName = root.name;
289
+ if (typeof root.main === "string")
290
+ topMain = root.main;
291
+ for (const section of [root, ...Object.values(root.env ?? {})]) {
292
+ if (!section || typeof section !== "object")
293
+ continue;
294
+ const s = section;
295
+ if (typeof s.name === "string" && s.name)
296
+ names.push(s.name);
297
+ if (typeof s.main === "string" && s.main)
298
+ mains.push(s.main);
299
+ bindings.push(...bindingsOfSection(s));
300
+ }
301
+ }
302
+ const path = relative(rootDir, abs).split("\\").join("/");
303
+ if (isTestWorkerConfig({ path, name: topName, mains: [topMain] }))
304
+ return;
305
+ const dir = workerDir(path);
306
+ const dirs = [dir];
307
+ for (const main of mains) {
308
+ const mainDir = workerDir(posix.normalize(posix.join(dir || ".", main)).replace(/^\.\//, ""));
309
+ if (!mainDir.startsWith("..") && !inside(mainDir, dir) && !dirs.includes(mainDir))
310
+ dirs.push(mainDir);
311
+ }
312
+ return { dirs, names, bindings: bindings.filter((entry) => entry.className) };
313
+ }
314
+ function inside(path, dir) {
315
+ return dir === "" || dir === "." || path === dir || path.startsWith(`${dir}/`);
316
+ }
317
+ function boundClassResolver(rootDir) {
318
+ const scopes = findWranglerFiles(rootDir, ANY_WRANGLER_FILE).map((abs) => readWranglerScope(rootDir, abs)).filter((scope) => Boolean(scope));
319
+ const anywhere = new Set;
320
+ const external = new Map;
321
+ for (const scope of scopes) {
322
+ for (const binding of scope.bindings) {
323
+ anywhere.add(binding.className);
324
+ if (!binding.scriptName)
325
+ continue;
326
+ const servers = scopes.filter((other) => other.names.includes(binding.scriptName));
327
+ if (servers.length === 0)
328
+ external.set(binding.className, new Set(["*"]));
329
+ for (const server of servers) {
330
+ const set = external.get(binding.className) ?? new Set;
331
+ server.names.forEach((name) => set.add(name));
332
+ external.set(binding.className, set);
333
+ }
334
+ }
335
+ }
336
+ return (className, path) => {
337
+ if (!anywhere.has(className))
338
+ return false;
339
+ if (external.get(className)?.has("*"))
340
+ return true;
341
+ let nearest = -1;
342
+ let owners = [];
343
+ for (const scope of scopes) {
344
+ const depth = Math.max(-1, ...scope.dirs.filter((dir) => inside(path, dir)).map((dir) => dir ? dir.split("/").length : 0));
345
+ if (depth < 0 || depth < nearest)
346
+ continue;
347
+ if (depth > nearest)
348
+ owners = [];
349
+ nearest = depth;
350
+ owners.push(scope);
351
+ }
352
+ if (owners.length === 0)
353
+ return true;
354
+ const servedNames = external.get(className);
355
+ return owners.some((owner) => owner.bindings.some((binding) => binding.className === className && !binding.scriptName) || servedNames !== undefined && owner.names.some((name) => servedNames.has(name)));
356
+ };
357
+ }
358
+ function relativeImportStems(path, text) {
359
+ const stems = new Set;
360
+ for (const match of text.matchAll(/\bfrom\s*['"](\.{1,2}\/[^'"\n]*)['"]/g)) {
361
+ const joined = posix.normalize(posix.join(workerDir(path) || ".", match[1])).replace(/^\.\//, "");
362
+ const stem = joined.replace(/\.(?:[cm]?[jt]sx?)$/, "");
363
+ stems.add(stem);
364
+ stems.add(`${stem}/index`);
365
+ }
366
+ return stems;
367
+ }
368
+ function pickBaseDeclaration(candidates, from, importsOf) {
369
+ if (candidates.length < 2)
370
+ return candidates[0];
371
+ const sameFile = candidates.find((candidate) => candidate.path === from.path);
372
+ if (sameFile)
373
+ return sameFile;
374
+ const imports = importsOf(from.path);
375
+ const imported = candidates.find((candidate) => imports.has(candidate.path.replace(/\.(?:[cm]?[jt]sx?)$/, "")));
376
+ if (imported)
377
+ return imported;
378
+ const fromDirs = workerDir(from.path).split("/");
379
+ const shared = (candidate) => {
380
+ const dirs = workerDir(candidate.path).split("/");
381
+ let n = 0;
382
+ while (n < dirs.length && n < fromDirs.length && dirs[n] === fromDirs[n] && dirs[n] !== "")
383
+ n += 1;
384
+ return n;
385
+ };
386
+ let best = candidates[0];
387
+ for (const candidate of candidates)
388
+ if (shared(candidate) > shared(best))
389
+ best = candidate;
390
+ return best;
391
+ }
392
+ function detectDefinedUnboundRuntimeClasses(rootDir) {
393
+ const declarations = [];
394
+ const texts = new Map;
395
+ for (const abs of findSourceFiles(rootDir)) {
396
+ let text;
397
+ try {
398
+ text = readFileSync(abs, "utf8");
399
+ } catch {
400
+ continue;
401
+ }
402
+ if (!text.includes("extends"))
403
+ continue;
404
+ const path = relative(rootDir, abs).split("\\").join("/");
405
+ texts.set(path, text);
406
+ for (const match of text.matchAll(CLASS_DECLARATION)) {
407
+ declarations.push({
408
+ className: match[3],
409
+ base: match[4],
410
+ exported: Boolean(match[1]),
411
+ abstract: Boolean(match[2]),
412
+ path,
413
+ line: text.slice(0, match.index).split(`
414
+ `).length
415
+ });
416
+ }
417
+ }
418
+ const byName = new Map;
419
+ for (const declaration of declarations) {
420
+ const list = byName.get(declaration.className) ?? [];
421
+ list.push(declaration);
422
+ byName.set(declaration.className, list);
423
+ }
424
+ const importCache = new Map;
425
+ const importsOf = (path) => {
426
+ let stems = importCache.get(path);
427
+ if (!stems) {
428
+ stems = relativeImportStems(path, texts.get(path) ?? "");
429
+ importCache.set(path, stems);
430
+ }
431
+ return stems;
432
+ };
433
+ const kindOf = (start) => {
434
+ let current = start;
435
+ for (let depth = 0;current && depth < 8; depth += 1) {
436
+ const base = current.base;
437
+ const kind = Object.hasOwn(RUNTIME_BASES, base) ? RUNTIME_BASES[base] : undefined;
438
+ if (kind)
439
+ return kind;
440
+ current = pickBaseDeclaration(byName.get(base) ?? [], current, importsOf);
441
+ }
442
+ return;
443
+ };
444
+ const isBound = boundClassResolver(rootDir);
445
+ const seen = new Set;
446
+ const found = [];
447
+ for (const declaration of declarations) {
448
+ if (!declaration.exported || declaration.abstract || isBound(declaration.className, declaration.path))
449
+ continue;
450
+ const kind = kindOf(declaration);
451
+ const key = `${declaration.className}|${declaration.path}`;
452
+ if (!kind || seen.has(key))
453
+ continue;
454
+ seen.add(key);
455
+ found.push({ className: declaration.className, kind, path: declaration.path, line: declaration.line });
456
+ }
457
+ return found;
458
+ }
459
+ function definedReason(defined) {
460
+ const what = defined.kind === "workflow" ? "Workflow" : defined.kind === "agent" ? "Agent (Durable Object)" : "Durable Object";
461
+ return `${what} class defined but not bound in any wrangler config`;
462
+ }
463
+ function norm(value) {
464
+ return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]/g, "") : "";
465
+ }
466
+ function itemPaths(item) {
467
+ const paths = [];
468
+ for (const value of [item.root, item.sourcePath]) {
469
+ if (typeof value === "string" && value && value !== ".")
470
+ paths.push(value.replace(/^\.\//, ""));
471
+ }
472
+ for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
473
+ const path = entry && typeof entry === "object" ? entry.path : undefined;
474
+ if (typeof path === "string" && path)
475
+ paths.push(path.replace(/^\.\//, ""));
476
+ }
477
+ return paths;
478
+ }
479
+ function withinWorker(path, dir, otherDirs) {
480
+ const inside2 = (d) => d === "" || path === d || path.startsWith(`${d}/`);
481
+ return inside2(dir) && !otherDirs.some((other) => other.length > dir.length && inside2(other));
482
+ }
483
+ function namesClass(name, className) {
484
+ if (typeof name !== "string")
485
+ return false;
486
+ const target = norm(className);
487
+ const bare = name.replace(/\s*\([^)]*\)\s*$/, "");
488
+ return norm(bare) === target || bare.split(/[^A-Za-z0-9_]+/).some((word) => norm(word) === target);
489
+ }
490
+ function graphNamesClass(graph, bound, all) {
491
+ const named = (name) => namesClass(name, bound.className);
492
+ const sameName = bound.scriptName ? [bound] : all.filter((other) => other.className === bound.className && !other.scriptName);
493
+ const dir = workerDir(bound.path);
494
+ const otherDirs = sameName.map((other) => workerDir(other.path)).filter((other) => other !== dir);
495
+ const covers = (item) => {
496
+ if (!named(item.name))
497
+ return false;
498
+ if (sameName.length < 2)
499
+ return true;
500
+ return itemPaths(item).some((path) => withinWorker(path, dir, otherDirs));
501
+ };
502
+ return graph.runtimeAgents.some(covers) || graph.artifacts.some(covers);
503
+ }
504
+ function citedFile(rootDir, path) {
505
+ let rel = path.trim().replace(/\\/g, "/").replace(/:\d+(?::\d+)?$/, "");
506
+ if (isAbsolute(rel))
507
+ rel = relative(rootDir, rel).split("\\").join("/");
508
+ return posix.normalize(rel).replace(/^\.\//, "");
509
+ }
510
+ function graphNamesDeclaration(graph, defined, rootDir) {
511
+ const named = [...graph.runtimeAgents, ...graph.artifacts].filter((item) => namesClass(item.name, defined.className));
512
+ if (named.length === 0)
513
+ return false;
514
+ const target = citedFile(rootDir, defined.path);
515
+ const cited = named.map((item) => filePaths(item).map((path) => citedFile(rootDir, path)));
516
+ if (cited.every((paths) => paths.length === 0))
517
+ return true;
518
+ return cited.some((paths) => paths.includes(target));
519
+ }
520
+ function filePaths(item) {
521
+ const paths = [];
522
+ if (typeof item.sourcePath === "string" && item.sourcePath.trim())
523
+ paths.push(item.sourcePath);
524
+ for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
525
+ const path = entry && typeof entry === "object" ? entry.path : undefined;
526
+ if (typeof path === "string" && path.trim())
527
+ paths.push(path);
528
+ }
529
+ return paths;
530
+ }
531
+ function bindingReason(bound) {
532
+ const what = bound.kind === "workflow" ? "Workflow" : "Durable Object";
533
+ const where = bound.scriptName ? ` served by the external Worker "${bound.scriptName}"` : "";
534
+ return `${what} binding ${bound.binding} -> class ${bound.className}${where}`;
535
+ }
536
+ function uniqueId(base, taken) {
537
+ let id = base;
538
+ for (let n = 2;taken.has(id); n += 1)
539
+ id = `${base}-${n}`;
540
+ taken.add(id);
541
+ return id;
542
+ }
543
+ function mergeBoundRuntimeClasses(graph, rootDir) {
544
+ if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
545
+ return { added: 0 };
546
+ let bound;
547
+ let defined;
548
+ try {
549
+ bound = detectBoundRuntimeClasses(rootDir);
550
+ defined = detectDefinedUnboundRuntimeClasses(rootDir);
551
+ } catch {
552
+ return { added: 0 };
553
+ }
554
+ const taken = new Set([
555
+ ...graph.runtimeAgents.map((agent) => agent.id),
556
+ ...graph.artifacts.map((artifact) => artifact.id)
557
+ ]);
558
+ let added = 0;
559
+ for (const entry of bound) {
560
+ if (graphNamesClass(graph, entry, bound))
561
+ continue;
562
+ const evidence = [{ path: entry.path, ...entry.line ? { line: entry.line } : {}, reason: bindingReason(entry) }];
563
+ const slug = entry.className.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase();
564
+ if (entry.kind === "durable_object" && AGENT_CLASS.test(entry.className) && !entry.scriptName) {
565
+ const agent = {
566
+ id: uniqueId(`agent.bound.${slug}`, taken),
567
+ name: entry.className,
568
+ kind: "runtime_agent",
569
+ purpose: "Agent class bound as a Durable Object.",
570
+ root: ".",
571
+ entrypointIds: [],
572
+ channelAdapterIds: [],
573
+ artifactIds: [],
574
+ identityNamespaceIds: [],
575
+ deploymentIds: [],
576
+ frameworkIds: [],
577
+ modelProviderIds: [],
578
+ confidence: 0.6,
579
+ evidence
580
+ };
581
+ graph.runtimeAgents.push(agent);
582
+ } else {
583
+ const artifact = {
584
+ id: uniqueId(`entrypoint.bound.${slug}`, taken),
585
+ type: "entrypoint",
586
+ name: entry.className,
587
+ scope: "shared",
588
+ sourcePath: entry.path,
589
+ usedByAgentIds: [],
590
+ confidence: 0.6,
591
+ evidence
592
+ };
593
+ graph.artifacts.push(artifact);
594
+ }
595
+ added += 1;
596
+ }
597
+ for (const entry of defined) {
598
+ if (graphNamesDeclaration(graph, entry, rootDir))
599
+ continue;
600
+ const slug = entry.className.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase();
601
+ graph.artifacts.push({
602
+ id: uniqueId(`entrypoint.defined.${slug}`, taken),
603
+ type: "entrypoint",
604
+ name: entry.className,
605
+ scope: "shared",
606
+ sourcePath: entry.path,
607
+ usedByAgentIds: [],
608
+ confidence: 0.6,
609
+ evidence: [{ path: entry.path, line: entry.line, reason: definedReason(entry) }]
610
+ });
611
+ added += 1;
612
+ }
613
+ return { added };
614
+ }
615
+
616
+ // src/harness-component-status.ts
617
+ var HARNESS_COMPONENT_STATUSES = new Set(["in_prod", "reachable", "defined_only"]);
618
+ var STATUS_ALIASES = {
619
+ in_prod: "in_prod",
620
+ inprod: "in_prod",
621
+ prod: "in_prod",
622
+ production: "in_prod",
623
+ in_production: "in_prod",
624
+ deployed: "in_prod",
625
+ live: "in_prod",
626
+ reachable: "reachable",
627
+ defined_only: "defined_only",
628
+ dead: "defined_only",
629
+ dead_code: "defined_only",
630
+ unused: "defined_only",
631
+ unreferenced: "defined_only",
632
+ unreachable: "defined_only",
633
+ orphan: "defined_only",
634
+ orphaned: "defined_only"
635
+ };
636
+ function normalizeComponentStatus(value) {
637
+ if (typeof value !== "string")
638
+ return;
639
+ return STATUS_ALIASES[value.trim().toLowerCase().replace(/[\s-]+/g, "_")];
640
+ }
641
+ var SKIPPED_DIRS2 = new Set(["node_modules", "dist", "build", "vendor", "coverage"]);
642
+ var NON_DEPLOY_DIR = /^(?:test|tests|__tests__|fixtures?|__fixtures__|examples?|e2e)$/;
643
+ var WRANGLER_FILE2 = /^wrangler(?:\.([\w.-]+))?\.(?:json|jsonc|toml)$/;
644
+ var NON_DEPLOY_VARIANT = /(?:^|[.-])(?:preview|dev|local|test|example|sample)(?:$|[.-])/i;
645
+ var CODE_FILE = /\.(?:[cm]?[jt]sx?)$/;
646
+ var TEST_RUNNER_FILE = /(?:^|\/)__tests__\/|\.(?:test|spec)\.[cm]?[jt]sx?$/;
647
+ var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
648
+ var TS_FOR_JS = { ".js": [".ts", ".tsx"], ".jsx": [".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
649
+ var MAX_FILES = 200000;
650
+ var MAX_READ_BYTES = 1024 * 1024;
651
+ var STATIC_IMPORT = /\b(?:import|export)\s+(type\s+)?([^'";]*?)\bfrom\s*['"]([^'"\n]+)['"]/g;
652
+ var SIDE_EFFECT_IMPORT = /\bimport\s*['"]([^'"\n]+)['"]/g;
653
+ var DYNAMIC_IMPORT = /\b(?:import|require)\s*\(\s*['"]([^'"\n]+)['"]\s*\)/g;
654
+ var CLASS_DECL = /\bclass\s+([A-Za-z_$][\w$]*)/g;
655
+ var SCRIPT_FILE = /(?:^|[\s;&|(])(?:bun|node|tsx)\s+((?:-{1,2}[\w-]+(?:=\S+)?\s+)*)(?:run\s+)?([^\s'"&|;()]+)/g;
656
+ function listFiles(rootDir) {
657
+ const files = new Set;
658
+ const walk = (abs, rel) => {
659
+ let entries;
660
+ try {
661
+ entries = readdirSync2(abs, { withFileTypes: true });
662
+ } catch {
663
+ return;
664
+ }
665
+ for (const entry of entries) {
666
+ if (files.size >= MAX_FILES)
667
+ return;
668
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
669
+ if (entry.isDirectory()) {
670
+ if (!entry.name.startsWith(".") && !SKIPPED_DIRS2.has(entry.name))
671
+ walk(join2(abs, entry.name), childRel);
672
+ } else if (entry.isFile()) {
673
+ files.add(childRel);
674
+ }
675
+ }
676
+ };
677
+ walk(rootDir, "");
678
+ return files;
679
+ }
680
+ function readCapped(abs) {
681
+ const fd = openSync(abs, "r");
682
+ try {
683
+ const buffer = Buffer.alloc(MAX_READ_BYTES);
684
+ const read = readSync(fd, buffer, 0, MAX_READ_BYTES, 0);
685
+ return buffer.subarray(0, read).toString("utf8");
686
+ } finally {
687
+ closeSync(fd);
688
+ }
689
+ }
690
+ function readJson(rootDir, rel) {
691
+ try {
692
+ const parsed = JSON.parse(stripJsonc(readFileSync2(join2(rootDir, rel), "utf8")));
693
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
694
+ } catch {
695
+ return;
696
+ }
697
+ }
698
+ function dirOf(rel) {
699
+ const slash = rel.lastIndexOf("/");
700
+ return slash < 0 ? "" : rel.slice(0, slash);
701
+ }
702
+ function joinRel(dir, path) {
703
+ const joined = posix2.normalize(posix2.join(dir || ".", path));
704
+ return joined === "." ? "" : joined.replace(/^\.\//, "").replace(/\/$/, "");
705
+ }
706
+ function isRecord(value) {
707
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
708
+ }
709
+ var PREFERRED_CONDITIONS = ["import", "default", "require", "node"];
710
+ function exportTargets(value, out = []) {
711
+ if (typeof value === "string")
712
+ out.push(value);
713
+ else if (Array.isArray(value))
714
+ value.forEach((item) => exportTargets(item, out));
715
+ else if (isRecord(value)) {
716
+ const keys = Object.keys(value).filter((key) => key !== "types" && key !== "typings");
717
+ const preferred = keys.filter((key) => PREFERRED_CONDITIONS.includes(key));
718
+ for (const key of [...preferred, ...keys.filter((key2) => !preferred.includes(key2))])
719
+ exportTargets(value[key], out);
720
+ }
721
+ return out;
722
+ }
723
+ function isTypeOnlyClause(clause) {
724
+ const braces = /^\{([^{}]*)\}$/.exec(clause.trim());
725
+ if (!braces)
726
+ return false;
727
+ const members = braces[1].split(",").map((member) => member.trim()).filter(Boolean);
728
+ return members.length > 0 && members.every((member) => /^type\s+(?!as\s+[\w$]+$)\S/.test(member));
729
+ }
730
+ function stringLeaves(value, out = []) {
731
+ if (typeof value === "string")
732
+ out.push(value);
733
+ else if (Array.isArray(value))
734
+ value.forEach((item) => stringLeaves(item, out));
735
+ else if (isRecord(value)) {
736
+ for (const [key, item] of Object.entries(value)) {
737
+ if (key !== "types" && key !== "typings")
738
+ stringLeaves(item, out);
739
+ }
740
+ }
741
+ return out;
742
+ }
743
+ function boundClassesOf(section) {
744
+ const durable = isRecord(section.durable_objects) ? section.durable_objects.bindings : undefined;
745
+ const raw = [...Array.isArray(durable) ? durable : [], ...Array.isArray(section.workflows) ? section.workflows : []];
746
+ return raw.filter(isRecord).flatMap((entry) => typeof entry.class_name === "string" && entry.class_name ? [{ className: entry.class_name, ...typeof entry.script_name === "string" && entry.script_name ? { scriptName: entry.script_name } : {} }] : []);
747
+ }
748
+ var PRODUCTION_ENV = /^(?:production|prod)$/i;
749
+ function parseWranglerToml(text) {
750
+ const names = [];
751
+ const mains = [];
752
+ const otherMains = [];
753
+ const boundClasses = [];
754
+ let topName;
755
+ let header = "";
756
+ let binding;
757
+ for (const rawLine of text.split(`
758
+ `)) {
759
+ const line = rawLine.replace(/\s+#.*$/, "").trim();
760
+ const table = /^\[\[?\s*([^\]]+?)\s*\]\]?$/.exec(line);
761
+ if (table) {
762
+ header = table[1];
763
+ binding = undefined;
764
+ if (/(?:^|\.)(?:durable_objects\.bindings|workflows)$/.test(header) && line.startsWith("[[")) {
765
+ binding = { className: "" };
766
+ boundClasses.push(binding);
767
+ }
768
+ continue;
769
+ }
770
+ const pair = /^([\w.-]+)\s*=\s*["']([^"']*)["']/.exec(line);
771
+ if (!pair)
772
+ continue;
773
+ const [, key, value] = pair;
774
+ const envName = /^env\.([\w-]+)$/.exec(header)?.[1];
775
+ const envSection = header === "" || envName !== undefined;
776
+ if (binding && key === "class_name")
777
+ binding.className = value;
778
+ else if (binding && key === "script_name" && value)
779
+ binding.scriptName = value;
780
+ else if (envSection && key === "name") {
781
+ if (header === "")
782
+ topName = value;
783
+ names.push(value);
784
+ } else if (envSection && key === "main") {
785
+ (envName === undefined || PRODUCTION_ENV.test(envName) ? mains : otherMains).push(value);
786
+ }
787
+ }
788
+ return { name: topName, names, mains, otherMains, boundClasses: boundClasses.filter((entry) => entry.className) };
789
+ }
790
+ function readWranglerWorker(rootDir, rel) {
791
+ const dir = dirOf(rel);
792
+ const fileName = rel.slice(dir ? dir.length + 1 : 0);
793
+ const variant = WRANGLER_FILE2.exec(fileName)?.[1] ?? "";
794
+ let name;
795
+ const names = [];
796
+ const mains = [];
797
+ const otherMains = [];
798
+ const boundClasses = [];
799
+ if (rel.endsWith(".toml")) {
800
+ let text;
801
+ try {
802
+ text = readFileSync2(join2(rootDir, rel), "utf8");
803
+ } catch {
804
+ return;
805
+ }
806
+ const parsed = parseWranglerToml(text);
807
+ name = parsed.name;
808
+ names.push(...parsed.names);
809
+ mains.push(...parsed.mains);
810
+ otherMains.push(...parsed.otherMains);
811
+ boundClasses.push(...parsed.boundClasses);
812
+ } else {
813
+ const config = readJson(rootDir, rel);
814
+ if (!config)
815
+ return;
816
+ name = typeof config.name === "string" && config.name ? config.name : undefined;
817
+ const sections = [[undefined, config], ...Object.entries(isRecord(config.env) ? config.env : {})];
818
+ for (const [envName, section] of sections) {
819
+ if (!isRecord(section))
820
+ continue;
821
+ if (typeof section.name === "string" && section.name)
822
+ names.push(section.name);
823
+ if (typeof section.main === "string" && section.main) {
824
+ (envName === undefined || PRODUCTION_ENV.test(envName) ? mains : otherMains).push(section.main);
825
+ }
826
+ boundClasses.push(...boundClassesOf(section));
827
+ }
828
+ }
829
+ const nonDeployDir = rel.split("/").slice(0, -1).some((segment) => NON_DEPLOY_DIR.test(segment));
830
+ return {
831
+ path: rel,
832
+ deployed: Boolean(name) && !nonDeployDir && !NON_DEPLOY_VARIANT.test(variant) && !isTestWorkerConfig({ path: rel, name, mains }),
833
+ names,
834
+ mains: [...new Set(mains)].map((main) => joinRel(dir, main)),
835
+ otherMains: [...new Set(otherMains)].filter((main) => !mains.includes(main)).map((main) => joinRel(dir, main)),
836
+ boundClasses,
837
+ reach: new Set
838
+ };
839
+ }
840
+ function readWorkspacePackage(rootDir, rel) {
841
+ const pkg = readJson(rootDir, rel);
842
+ if (!pkg)
843
+ return;
844
+ const dir = dirOf(rel);
845
+ const entries = [];
846
+ const subpaths = new Map;
847
+ const patterns = [];
848
+ for (const field of ["main", "module"]) {
849
+ if (typeof pkg[field] === "string")
850
+ entries.push(pkg[field]);
851
+ }
852
+ if (typeof pkg.bin === "string")
853
+ entries.push(pkg.bin);
854
+ else if (isRecord(pkg.bin))
855
+ entries.push(...stringLeaves(pkg.bin));
856
+ const exportsField = pkg.exports;
857
+ if (isRecord(exportsField) && Object.keys(exportsField).some((key) => key.startsWith("."))) {
858
+ for (const [key, value] of Object.entries(exportsField)) {
859
+ const targets = exportTargets(value);
860
+ const star = key.indexOf("*");
861
+ if (star >= 0) {
862
+ if (key.indexOf("*", star + 1) < 0)
863
+ patterns.push({ prefix: key.slice(0, star), suffix: key.slice(star + 1), targets });
864
+ continue;
865
+ }
866
+ subpaths.set(key, targets);
867
+ entries.push(...targets);
868
+ }
869
+ patterns.sort((a, b) => b.prefix.length - a.prefix.length || b.suffix.length - a.suffix.length);
870
+ } else if (exportsField !== undefined) {
871
+ const targets = exportTargets(exportsField);
872
+ subpaths.set(".", targets);
873
+ entries.push(...targets);
874
+ }
875
+ const scriptFiles = [];
876
+ if (isRecord(pkg.scripts)) {
877
+ for (const command of Object.values(pkg.scripts)) {
878
+ if (typeof command !== "string")
879
+ continue;
880
+ for (const match of command.matchAll(SCRIPT_FILE))
881
+ scriptFiles.push(match[2]);
882
+ }
883
+ }
884
+ return {
885
+ dir,
886
+ ...typeof pkg.name === "string" && pkg.name ? { name: pkg.name } : {},
887
+ entries: entries.filter((entry) => !entry.includes("*")),
888
+ subpaths,
889
+ patterns,
890
+ scriptFiles
891
+ };
892
+ }
893
+ function readPathAliases(rootDir, rel, depth = 0) {
894
+ const config = readJson(rootDir, rel);
895
+ if (!config)
896
+ return [];
897
+ const options = isRecord(config.compilerOptions) ? config.compilerOptions : {};
898
+ if (isRecord(options.paths)) {
899
+ const base = joinRel(dirOf(rel), typeof options.baseUrl === "string" ? options.baseUrl : ".");
900
+ return Object.entries(options.paths).flatMap(([pattern, targets]) => {
901
+ if (!Array.isArray(targets))
902
+ return [];
903
+ const star = pattern.indexOf("*");
904
+ return [{
905
+ prefix: star < 0 ? pattern : pattern.slice(0, star),
906
+ suffix: star < 0 ? "" : pattern.slice(star + 1),
907
+ wildcard: star >= 0,
908
+ targets: targets.filter((target) => typeof target === "string").map((target) => joinRel(base, target))
909
+ }];
910
+ });
911
+ }
912
+ if (depth < 3 && typeof config.extends === "string" && config.extends.startsWith(".")) {
913
+ const parent = joinRel(dirOf(rel), config.extends.endsWith(".json") ? config.extends : `${config.extends}.json`);
914
+ return readPathAliases(rootDir, parent, depth + 1);
915
+ }
916
+ return [];
917
+ }
918
+ function analyzeRepository(rootDir) {
919
+ const files = listFiles(rootDir);
920
+ const workers = [];
921
+ const packages = [];
922
+ for (const rel of files) {
923
+ const fileName = rel.slice(rel.lastIndexOf("/") + 1);
924
+ if (WRANGLER_FILE2.test(fileName)) {
925
+ const worker = readWranglerWorker(rootDir, rel);
926
+ if (worker)
927
+ workers.push(worker);
928
+ } else if (fileName === "package.json") {
929
+ const pkg = readWorkspacePackage(rootDir, rel);
930
+ if (pkg)
931
+ packages.push(pkg);
932
+ }
933
+ }
934
+ workers.sort((a, b) => a.path.localeCompare(b.path));
935
+ const packagesByName = new Map(packages.filter((pkg) => pkg.name).map((pkg) => [pkg.name, pkg]));
936
+ const packageDirs = new Set(packages.map((pkg) => pkg.dir));
937
+ const resolveFile = (base) => {
938
+ if (!base || base.startsWith(".."))
939
+ return;
940
+ const ext = posix2.extname(base);
941
+ for (const tsExt of TS_FOR_JS[ext] ?? []) {
942
+ const candidate = base.slice(0, -ext.length) + tsExt;
943
+ if (files.has(candidate))
944
+ return candidate;
945
+ }
946
+ if (files.has(base))
947
+ return base;
948
+ for (const candidate of RESOLVE_EXTS)
949
+ if (files.has(base + candidate))
950
+ return base + candidate;
951
+ for (const candidate of RESOLVE_EXTS)
952
+ if (files.has(`${base}/index${candidate}`))
953
+ return `${base}/index${candidate}`;
954
+ return;
955
+ };
956
+ const resolveEntry = (dir, entry) => {
957
+ const direct = resolveFile(joinRel(dir, entry));
958
+ if (direct)
959
+ return direct;
960
+ const fromSource = entry.replace(/^\.?\/?(?:dist|build|lib|out)\//, "src/").replace(/\.d\.[cm]?ts$/, ".ts");
961
+ return fromSource !== entry ? resolveFile(joinRel(dir, fromSource)) : undefined;
962
+ };
963
+ const aliasCache = new Map;
964
+ const aliasesFor = (dir) => {
965
+ const cached = aliasCache.get(dir);
966
+ if (cached)
967
+ return cached;
968
+ const tsconfig = dir ? `${dir}/tsconfig.json` : "tsconfig.json";
969
+ const aliases = files.has(tsconfig) ? readPathAliases(rootDir, tsconfig) : dir ? aliasesFor(dirOf(dir)) : [];
970
+ aliasCache.set(dir, aliases);
971
+ return aliases;
972
+ };
973
+ const resolvePackage = (specifier) => {
974
+ const parts = specifier.split("/");
975
+ const nameLength = specifier.startsWith("@") ? 2 : 1;
976
+ const name = parts.slice(0, nameLength).join("/");
977
+ const subpath = parts.slice(nameLength).join("/");
978
+ const pkg = packagesByName.get(name);
979
+ if (pkg) {
980
+ const key = subpath ? `./${subpath}` : ".";
981
+ let targets = pkg.subpaths.get(key);
982
+ if (!targets && subpath) {
983
+ const pattern = pkg.patterns.find((entry) => key.startsWith(entry.prefix) && key.endsWith(entry.suffix) && key.length >= entry.prefix.length + entry.suffix.length);
984
+ const captured = pattern ? key.slice(pattern.prefix.length, key.length - pattern.suffix.length) : "";
985
+ targets = pattern ? pattern.targets.map((target) => target.split("*").join(captured)) : [];
986
+ }
987
+ for (const target of targets ?? pkg.entries) {
988
+ const resolved = resolveEntry(pkg.dir, target);
989
+ if (resolved)
990
+ return resolved;
991
+ }
992
+ if (subpath)
993
+ return resolveFile(joinRel(pkg.dir, subpath)) ?? resolveFile(joinRel(pkg.dir, `src/${subpath}`));
994
+ return resolveFile(joinRel(pkg.dir, "src/index")) ?? resolveFile(joinRel(pkg.dir, "index"));
995
+ }
996
+ const leaf = parts[nameLength - 1];
997
+ for (const parent of ["packages", "apps", "libs"]) {
998
+ const dir = `${parent}/${leaf}`;
999
+ if (packageDirs.has(dir))
1000
+ continue;
1001
+ const resolved = subpath ? resolveFile(`${dir}/src/${subpath}`) ?? resolveFile(`${dir}/${subpath}`) : resolveFile(`${dir}/src/index`) ?? resolveFile(`${dir}/index`);
1002
+ if (resolved)
1003
+ return resolved;
1004
+ }
1005
+ return;
1006
+ };
1007
+ const resolveSpecifier = (raw, from) => {
1008
+ const specifier = raw.split("?")[0];
1009
+ if (!specifier || specifier.startsWith("node:") || specifier.startsWith("/"))
1010
+ return;
1011
+ if (specifier.startsWith("."))
1012
+ return resolveFile(joinRel(dirOf(from), specifier));
1013
+ for (const alias of aliasesFor(dirOf(from))) {
1014
+ let captured;
1015
+ if (!alias.wildcard && specifier === alias.prefix)
1016
+ captured = "";
1017
+ else if (alias.wildcard && specifier.startsWith(alias.prefix) && specifier.endsWith(alias.suffix) && specifier.length >= alias.prefix.length + alias.suffix.length) {
1018
+ captured = specifier.slice(alias.prefix.length, specifier.length - alias.suffix.length);
1019
+ }
1020
+ if (captured === undefined)
1021
+ continue;
1022
+ for (const target of alias.targets) {
1023
+ const resolved = resolveFile(alias.wildcard ? target.replace("*", captured) : target);
1024
+ if (resolved)
1025
+ return resolved;
1026
+ }
1027
+ }
1028
+ return resolvePackage(specifier);
1029
+ };
1030
+ const classFiles = new Map;
1031
+ const edgeCache = new Map;
1032
+ const edgesOf = (file) => {
1033
+ const cached = edgeCache.get(file);
1034
+ if (cached)
1035
+ return cached;
1036
+ const edges = [];
1037
+ edgeCache.set(file, edges);
1038
+ if (!CODE_FILE.test(file))
1039
+ return edges;
1040
+ let text;
1041
+ try {
1042
+ text = readCapped(join2(rootDir, file));
1043
+ } catch {
1044
+ return edges;
1045
+ }
1046
+ text = text.replace(/^\s*\/\/.*$/gm, "");
1047
+ const specifiers = [];
1048
+ for (const match of text.matchAll(STATIC_IMPORT)) {
1049
+ if (!match[1] && !isTypeOnlyClause(match[2]))
1050
+ specifiers.push(match[3]);
1051
+ }
1052
+ for (const match of text.matchAll(SIDE_EFFECT_IMPORT))
1053
+ specifiers.push(match[1]);
1054
+ for (const match of text.matchAll(DYNAMIC_IMPORT))
1055
+ specifiers.push(match[1]);
1056
+ for (const specifier of new Set(specifiers)) {
1057
+ const resolved = resolveSpecifier(specifier, file);
1058
+ if (resolved && resolved !== file)
1059
+ edges.push(resolved);
1060
+ }
1061
+ for (const match of text.matchAll(CLASS_DECL)) {
1062
+ const list = classFiles.get(match[1]) ?? [];
1063
+ if (!list.includes(file))
1064
+ list.push(file);
1065
+ classFiles.set(match[1], list);
1066
+ }
1067
+ return edges;
1068
+ };
1069
+ const walk = (starts, seen = new Set) => {
1070
+ const queue = [...starts].filter((start) => Boolean(start) && !seen.has(start));
1071
+ for (const start of queue)
1072
+ seen.add(start);
1073
+ while (queue.length > 0) {
1074
+ const file = queue.pop();
1075
+ for (const next of edgesOf(file)) {
1076
+ if (!seen.has(next)) {
1077
+ seen.add(next);
1078
+ queue.push(next);
1079
+ }
1080
+ }
1081
+ }
1082
+ return seen;
1083
+ };
1084
+ const inProd = new Set;
1085
+ for (const worker of workers) {
1086
+ worker.reach = walk(worker.mains.map((main) => resolveEntry("", main)));
1087
+ if (!worker.deployed)
1088
+ continue;
1089
+ inProd.add(worker.path);
1090
+ for (const file of worker.reach)
1091
+ inProd.add(file);
1092
+ }
1093
+ const otherStarts = workers.filter((worker) => !worker.deployed).map((worker) => worker.path);
1094
+ for (const worker of workers) {
1095
+ if (!worker.deployed)
1096
+ otherStarts.push(...worker.reach);
1097
+ otherStarts.push(...worker.otherMains.map((main) => resolveEntry("", main)));
1098
+ }
1099
+ for (const pkg of packages) {
1100
+ otherStarts.push(...pkg.entries.map((entry) => resolveEntry(pkg.dir, entry)));
1101
+ otherStarts.push(...pkg.scriptFiles.map((script) => resolveFile(joinRel(pkg.dir, script))));
1102
+ }
1103
+ const all = walk(otherStarts, new Set(inProd));
1104
+ const reachable = new Set([...all].filter((file) => !inProd.has(file)));
1105
+ return { inProd, reachable, files, workers, classFiles, resolveFile };
1106
+ }
1107
+ function norm2(value) {
1108
+ return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]/g, "") : "";
1109
+ }
1110
+ function normalizeItemPath(rootDir, path) {
1111
+ let rel = path.trim().replace(/\\/g, "/").replace(/:\d+(?::\d+)?$/, "");
1112
+ if (isAbsolute2(rel))
1113
+ rel = relative2(rootDir, rel).split("\\").join("/");
1114
+ return joinRel("", rel);
1115
+ }
1116
+ function definingPath(item) {
1117
+ if (typeof item.sourcePath === "string" && item.sourcePath.trim())
1118
+ return item.sourcePath;
1119
+ for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
1120
+ const path = isRecord(entry) ? entry.path : undefined;
1121
+ if (typeof path === "string" && path.trim())
1122
+ return path;
1123
+ }
1124
+ return;
1125
+ }
1126
+ function boundClassStatus(analysis, worker, item) {
1127
+ const wanted = new Set([norm2(item.name), norm2(typeof item.id === "string" ? item.id.split(".").pop() : "")]);
1128
+ const bound = worker.boundClasses.find((entry) => wanted.has(norm2(entry.className)));
1129
+ if (!bound)
1130
+ return null;
1131
+ const servers = bound.scriptName ? analysis.workers.filter((other) => other.names.includes(bound.scriptName)) : [worker];
1132
+ const declared = analysis.classFiles.get(bound.className) ?? [];
1133
+ if (servers.some((server) => server.deployed && declared.some((file) => server.reach.has(file))))
1134
+ return "in_prod";
1135
+ if (declared.some((file) => analysis.inProd.has(file) || analysis.reachable.has(file)))
1136
+ return "reachable";
1137
+ return;
1138
+ }
1139
+ function itemStatus(analysis, rootDir, item) {
1140
+ const raw = definingPath(item);
1141
+ if (!raw)
1142
+ return;
1143
+ const path = normalizeItemPath(rootDir, raw);
1144
+ const worker = analysis.workers.find((entry) => entry.path === path);
1145
+ if (worker) {
1146
+ const classStatus = boundClassStatus(analysis, worker, item);
1147
+ if (classStatus !== null)
1148
+ return classStatus;
1149
+ return worker.deployed ? "in_prod" : "reachable";
1150
+ }
1151
+ const file = analysis.files.has(path) ? path : analysis.resolveFile(path);
1152
+ if (!file)
1153
+ return;
1154
+ if (analysis.inProd.has(file))
1155
+ return "in_prod";
1156
+ if (analysis.reachable.has(file))
1157
+ return "reachable";
1158
+ return CODE_FILE.test(file) && !TEST_RUNNER_FILE.test(file) ? "defined_only" : undefined;
1159
+ }
1160
+ function tagComponentStatus(graph, rootDir) {
1161
+ if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
1162
+ return { tagged: 0, counts: {} };
1163
+ try {
1164
+ const analysis = analyzeRepository(rootDir);
1165
+ const counts = { in_prod: 0, reachable: 0, defined_only: 0, unknown: 0 };
1166
+ let tagged = 0;
1167
+ const items = [
1168
+ ...Array.isArray(graph.runtimeAgents) ? graph.runtimeAgents : [],
1169
+ ...Array.isArray(graph.artifacts) ? graph.artifacts : []
1170
+ ];
1171
+ for (const item of items) {
1172
+ if (!isRecord(item))
1173
+ continue;
1174
+ if (item.status === undefined || !HARNESS_COMPONENT_STATUSES.has(item.status)) {
1175
+ const status = itemStatus(analysis, rootDir, item);
1176
+ if (status) {
1177
+ item.status = status;
1178
+ tagged += 1;
1179
+ } else {
1180
+ delete item.status;
1181
+ }
1182
+ }
1183
+ counts[item.status ?? "unknown"] += 1;
1184
+ }
1185
+ if (isRecord(graph.scan)) {
1186
+ graph.scan.componentStatusCounts = counts;
1187
+ }
1188
+ return { tagged, counts };
1189
+ } catch {
1190
+ return { tagged: 0, counts: {} };
1191
+ }
1192
+ }
1193
+
1194
+ // src/harness-report.ts
18
1195
  var HARNESS_REPORT_SCHEMA_VERSION = "harness_report.v0.1";
19
1196
  var HARNESS_REPORT_VALIDATION_SCHEMA_VERSION = "harness_report_validation.v0.1";
20
1197
  var HARNESS_REPORT_APPROVAL_SCHEMA_VERSION = "harness_report_approval.v0.1";
21
- var HARNESS_REPORT_PATH = join(".moda", "harness-report.json");
22
- var HARNESS_REPORT_MARKDOWN_PATH = join(".moda", "HARNESS_REPORT.md");
23
- var HARNESS_REPORT_VALIDATION_PATH = join(".moda", "harness-report-validation.json");
24
- var HARNESS_REPORT_APPROVAL_PATH = join(".moda", "harness-report-approval.json");
1198
+ var HARNESS_REPORT_PATH = join3(".moda", "harness-report.json");
1199
+ var HARNESS_REPORT_MARKDOWN_PATH = join3(".moda", "HARNESS_REPORT.md");
1200
+ var HARNESS_REPORT_VALIDATION_PATH = join3(".moda", "harness-report-validation.json");
1201
+ var HARNESS_REPORT_APPROVAL_PATH = join3(".moda", "harness-report-approval.json");
25
1202
  var HARNESS_ANALYZE_CONCERNS = ["topology", "tools", "prompts", "evals"];
26
1203
  var HARNESS_GRAPH_ARRAY_FIELDS = [
27
1204
  "agentFamilies",
@@ -300,7 +1477,7 @@ function validateHarnessReport(report, rootDir, options) {
300
1477
  check("graph_present", Boolean(report?.graph?.schemaVersion), "Report must include the sanitized harness graph.");
301
1478
  check("citations_present", Array.isArray(report?.citations) && report.citations.length > 0, "Report must include citations.");
302
1479
  check("claims_present", Array.isArray(report?.claims) && report.claims.length > 0, "Report must include claims.");
303
- const syncPlan = isRecord(report?.syncPlan) ? report.syncPlan : undefined;
1480
+ const syncPlan = isRecord2(report?.syncPlan) ? report.syncPlan : undefined;
304
1481
  check("sync_plan_present", Boolean(syncPlan), "Report must include syncPlan.");
305
1482
  if (syncPlan) {
306
1483
  check("sync_plan_command", typeof syncPlan.command === "string" && syncPlan.command.length > 0, "Report syncPlan must include command.");
@@ -339,7 +1516,7 @@ function validateHarnessReport(report, rootDir, options) {
339
1516
  const requireLineCitations = report?.analyst?.mode === "agent";
340
1517
  for (const [index, citation] of citations.entries()) {
341
1518
  const label = citationLabel(citation, index);
342
- if (!isRecord(citation)) {
1519
+ if (!isRecord2(citation)) {
343
1520
  errors.push(`Citation ${index + 1} must be an object.`);
344
1521
  continue;
345
1522
  }
@@ -366,7 +1543,7 @@ function validateHarnessReport(report, rootDir, options) {
366
1543
  const claimIds = new Set;
367
1544
  for (const [index, claim] of claims.entries()) {
368
1545
  const label = claimLabel(claim, index);
369
- if (!isRecord(claim)) {
1546
+ if (!isRecord2(claim)) {
370
1547
  errors.push(`Claim ${index + 1} must be an object.`);
371
1548
  continue;
372
1549
  }
@@ -437,7 +1614,7 @@ function validateHarnessReport(report, rootDir, options) {
437
1614
  if (unknowns.length > 0) {
438
1615
  warn("unknowns_present", `${unknowns.length} open harness question(s) require human review.`);
439
1616
  for (const [index, unknown] of unknowns.entries()) {
440
- if (!isRecord(unknown)) {
1617
+ if (!isRecord2(unknown)) {
441
1618
  errors.push(`Unknown ${index + 1} must be an object.`);
442
1619
  continue;
443
1620
  }
@@ -465,7 +1642,7 @@ function validateHarnessReport(report, rootDir, options) {
465
1642
  };
466
1643
  }
467
1644
  function refutedToolCandidateCount(graph) {
468
- const unknowns = Array.isArray(graph.unknowns) ? graph.unknowns.filter(isRecord) : [];
1645
+ const unknowns = Array.isArray(graph.unknowns) ? graph.unknowns.filter(isRecord2) : [];
469
1646
  let count = 0;
470
1647
  for (const unknown of unknowns) {
471
1648
  const label = `${String(unknown.id ?? "")} ${String(unknown.question ?? "")}`;
@@ -484,8 +1661,8 @@ function validateDeterministicFloor(report, floor, check, warn, concern) {
484
1661
  return;
485
1662
  const applyToolFloor = concern === undefined || concern === "tools";
486
1663
  const applyTopologyFloors = concern === undefined || concern === "topology";
487
- const graph = isRecord(report?.graph) ? report.graph : {};
488
- const toolArtifactCount = Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord(artifact) && artifact.type === "tool").length : 0;
1664
+ const graph = isRecord2(report?.graph) ? report.graph : {};
1665
+ const toolArtifactCount = Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord2(artifact) && artifact.type === "tool").length : 0;
489
1666
  const providerCount = safeArrayLength(graph.modelProviders);
490
1667
  const runtimeAgentCount = safeArrayLength(graph.runtimeAgents);
491
1668
  if (applyToolFloor && typeof floor.toolCandidates === "number" && floor.toolCandidates >= 5) {
@@ -534,9 +1711,9 @@ async function approveHarnessReport(options) {
534
1711
  approved: true,
535
1712
  approvedAt: new Date().toISOString(),
536
1713
  approvedBy,
537
- reportPath: normalizePath(relative(options.rootDir, options.reportPath)) || HARNESS_REPORT_PATH
1714
+ reportPath: normalizePath(relative3(options.rootDir, options.reportPath)) || HARNESS_REPORT_PATH
538
1715
  };
539
- writeJson(join(options.rootDir, HARNESS_REPORT_APPROVAL_PATH), approval);
1716
+ writeJson(join3(options.rootDir, HARNESS_REPORT_APPROVAL_PATH), approval);
540
1717
  return { report, validation, approval };
541
1718
  }
542
1719
  function loadHarnessReportForSync(rootDir, reportPath, options) {
@@ -546,37 +1723,37 @@ function loadHarnessReportForSync(rootDir, reportPath, options) {
546
1723
  throw new CliInputError("Harness report validation failed; cannot sync.", validation.errors.slice(0, 5).join(`
547
1724
  `));
548
1725
  }
549
- const approvalPath = join(rootDir, HARNESS_REPORT_APPROVAL_PATH);
550
- const approval = existsSync(approvalPath) ? JSON.parse(readFileSync(approvalPath, "utf8")) : null;
1726
+ const approvalPath = join3(rootDir, HARNESS_REPORT_APPROVAL_PATH);
1727
+ const approval = existsSync(approvalPath) ? JSON.parse(readFileSync3(approvalPath, "utf8")) : null;
551
1728
  const approved = approval?.schemaVersion === HARNESS_REPORT_APPROVAL_SCHEMA_VERSION && approval.reportId === report.reportId && approval.reportHash === hashHarnessReport(report) && approval.approved === true;
552
1729
  return { report, validation, approval, approved };
553
1730
  }
554
1731
  function readHarnessReport(reportPath) {
555
- return normalizeHarnessReport(JSON.parse(readFileSync(reportPath, "utf8")));
1732
+ return normalizeHarnessReport(JSON.parse(readFileSync3(reportPath, "utf8")));
556
1733
  }
557
1734
  function normalizeHarnessReport(value) {
558
1735
  const report = cloneJson(value);
559
- if (!isRecord(report.graph))
1736
+ if (!isRecord2(report.graph))
560
1737
  return report;
561
1738
  const graph = report.graph;
562
1739
  if (typeof graph.schemaVersion !== "string" && typeof graph.graphSchemaVersion === "string") {
563
1740
  graph.schemaVersion = graph.graphSchemaVersion;
564
1741
  }
565
- if (typeof graph.schemaVersion !== "string" && isRecord(report.source) && typeof report.source.graphSchemaVersion === "string") {
1742
+ if (typeof graph.schemaVersion !== "string" && isRecord2(report.source) && typeof report.source.graphSchemaVersion === "string") {
566
1743
  graph.schemaVersion = report.source.graphSchemaVersion;
567
1744
  }
568
1745
  if (typeof graph.harnessId !== "string" && typeof report.harnessId === "string") {
569
1746
  graph.harnessId = report.harnessId;
570
1747
  }
571
- if (!isRecord(graph.repo) && isRecord(report.repo)) {
1748
+ if (!isRecord2(graph.repo) && isRecord2(report.repo)) {
572
1749
  graph.repo = report.repo;
573
1750
  }
574
- if (!isRecord(graph.tenant) && isRecord(report.tenant)) {
1751
+ if (!isRecord2(graph.tenant) && isRecord2(report.tenant)) {
575
1752
  graph.tenant = report.tenant;
576
1753
  }
577
- if (!isRecord(graph.scan)) {
578
- const source = isRecord(report.source) ? report.source : {};
579
- const readPlan = isRecord(source.readPlan) ? source.readPlan : defaultHarnessReadPlan();
1754
+ if (!isRecord2(graph.scan)) {
1755
+ const source = isRecord2(report.source) ? report.source : {};
1756
+ const readPlan = isRecord2(source.readPlan) ? source.readPlan : defaultHarnessReadPlan();
580
1757
  graph.scan = {
581
1758
  createdAt: typeof report.createdAt === "string" ? report.createdAt : new Date().toISOString(),
582
1759
  scannerVersion: analystAdapter(report),
@@ -602,7 +1779,7 @@ function normalizeHarnessReport(value) {
602
1779
  return report;
603
1780
  }
604
1781
  function normalizeHarnessSyncPlan(report) {
605
- const syncPlan = isRecord(report.syncPlan) ? report.syncPlan : {};
1782
+ const syncPlan = isRecord2(report.syncPlan) ? report.syncPlan : {};
606
1783
  if (typeof syncPlan.command !== "string" || syncPlan.command.length === 0) {
607
1784
  syncPlan.command = `moda harness sync --from-report ${HARNESS_REPORT_PATH}`;
608
1785
  }
@@ -627,7 +1804,7 @@ function normalizeRelationshipEvidence(graph) {
627
1804
  return;
628
1805
  const evidenceById = graphEvidenceById(graph);
629
1806
  for (const relationship of relationships) {
630
- if (!isRecord(relationship))
1807
+ if (!isRecord2(relationship))
631
1808
  continue;
632
1809
  if (Array.isArray(relationship.evidence) && relationship.evidence.length > 0)
633
1810
  continue;
@@ -650,9 +1827,9 @@ function graphEvidenceById(graph) {
650
1827
  if (!Array.isArray(collection))
651
1828
  continue;
652
1829
  for (const item of collection) {
653
- if (!isRecord(item) || typeof item.id !== "string" || !Array.isArray(item.evidence))
1830
+ if (!isRecord2(item) || typeof item.id !== "string" || !Array.isArray(item.evidence))
654
1831
  continue;
655
- const evidence = item.evidence.filter((entry) => isRecord(entry) && typeof entry.path === "string" && typeof entry.reason === "string").map((entry) => normalizeEvidenceEntry(entry));
1832
+ const evidence = item.evidence.filter((entry) => isRecord2(entry) && typeof entry.path === "string" && typeof entry.reason === "string").map((entry) => normalizeEvidenceEntry(entry));
656
1833
  if (evidence.length > 0)
657
1834
  byId.set(item.id, evidence);
658
1835
  }
@@ -676,7 +1853,7 @@ function normalizeEvidenceEntry(item) {
676
1853
  return line ? { path: item.path, reason: item.reason, line } : { path: item.path, reason: item.reason };
677
1854
  }
678
1855
  function normalizeHarnessClaim(claim, graph) {
679
- if (!isRecord(claim))
1856
+ if (!isRecord2(claim))
680
1857
  return claim;
681
1858
  if (typeof claim.subjectId !== "string" && Array.isArray(claim.subjectIds) && typeof claim.subjectIds[0] === "string") {
682
1859
  claim.subjectId = claim.subjectIds[0];
@@ -724,17 +1901,18 @@ function validateGraphItemShapes(graph, errors) {
724
1901
  if (typeof graph.harnessId !== "string" || graph.harnessId.length === 0) {
725
1902
  errors.push("Graph must include harnessId.");
726
1903
  }
727
- if (!isRecord(graph.repo)) {
1904
+ if (!isRecord2(graph.repo)) {
728
1905
  errors.push("Graph must include repo metadata.");
729
1906
  }
730
- if (!isRecord(graph.scan)) {
1907
+ if (!isRecord2(graph.scan)) {
731
1908
  errors.push("Graph must include scan metadata.");
732
1909
  }
733
1910
  validateGraphCollection(graph, errors, "runtimeAgents", {
734
1911
  strings: ["id", "name", "kind", "root"],
735
1912
  arrays: ["entrypointIds", "channelAdapterIds", "artifactIds", "identityNamespaceIds", "deploymentIds", "frameworkIds", "modelProviderIds", "evidence"],
736
1913
  expected: { kind: "runtime_agent" },
737
- confidence: true
1914
+ confidence: true,
1915
+ status: true
738
1916
  });
739
1917
  validateGraphCollection(graph, errors, "agentFamilies", {
740
1918
  strings: ["id", "name"],
@@ -744,7 +1922,8 @@ function validateGraphItemShapes(graph, errors) {
744
1922
  validateGraphCollection(graph, errors, "artifacts", {
745
1923
  strings: ["id", "type", "name", "scope"],
746
1924
  arrays: ["usedByAgentIds", "evidence"],
747
- confidence: true
1925
+ confidence: true,
1926
+ status: true
748
1927
  });
749
1928
  validateGraphCollection(graph, errors, "developerAgentAdapters", {
750
1929
  strings: ["id", "name", "kind", "sourcePath"],
@@ -797,7 +1976,7 @@ function validateGraphCollection(graph, errors, field, shape) {
797
1976
  return;
798
1977
  for (const [index, item] of collection.entries()) {
799
1978
  const label = graphItemLabel(field, item, index);
800
- if (!isRecord(item)) {
1979
+ if (!isRecord2(item)) {
801
1980
  errors.push(`Graph ${field} item ${index + 1} must be an object.`);
802
1981
  continue;
803
1982
  }
@@ -821,9 +2000,12 @@ function validateGraphCollection(graph, errors, field, shape) {
821
2000
  } else if (shape.confidence && typeof item.confidence === "number" && (item.confidence < 0 || item.confidence > 1)) {
822
2001
  errors.push(`Graph ${label} confidence must be between 0 and 1.`);
823
2002
  }
2003
+ if (shape.status && item.status !== undefined && (typeof item.status !== "string" || !HARNESS_COMPONENT_STATUSES.has(item.status))) {
2004
+ errors.push(`Graph ${label} status must be one of ${[...HARNESS_COMPONENT_STATUSES].join(", ")}.`);
2005
+ }
824
2006
  if (Array.isArray(item.evidence)) {
825
2007
  for (const [evidenceIndex, evidence] of item.evidence.entries()) {
826
- if (!isRecord(evidence) || typeof evidence.path !== "string" || typeof evidence.reason !== "string") {
2008
+ if (!isRecord2(evidence) || typeof evidence.path !== "string" || typeof evidence.reason !== "string") {
827
2009
  errors.push(`Graph ${label} evidence ${evidenceIndex + 1} must include path and reason.`);
828
2010
  continue;
829
2011
  }
@@ -856,7 +2038,7 @@ function artifactHasRetrievableLocation(item) {
856
2038
  }
857
2039
  function validateCaptureQuality(graph, warn) {
858
2040
  const scan = graph.scan;
859
- if (!isRecord(scan))
2041
+ if (!isRecord2(scan))
860
2042
  return;
861
2043
  if (scan.truncated === true) {
862
2044
  const reasons = Array.isArray(scan.truncationReasons) && scan.truncationReasons.length > 0 ? scan.truncationReasons.map((reason) => String(reason)).join(", ") : "scan budget or upload cap reached";
@@ -871,12 +2053,12 @@ function isWholeFileArtifact(item) {
871
2053
  return false;
872
2054
  if (!Array.isArray(item.evidence))
873
2055
  return false;
874
- return item.evidence.some((entry) => isRecord(entry) && entry.path === item.sourcePath);
2056
+ return item.evidence.some((entry) => isRecord2(entry) && entry.path === item.sourcePath);
875
2057
  }
876
2058
  function hasEvidenceLine(evidence) {
877
2059
  if (!Array.isArray(evidence))
878
2060
  return false;
879
- return evidence.some((entry) => isRecord(entry) && typeof entry.path === "string" && typeof entry.reason === "string" && evidenceLine({
2061
+ return evidence.some((entry) => isRecord2(entry) && typeof entry.path === "string" && typeof entry.reason === "string" && evidenceLine({
880
2062
  path: entry.path,
881
2063
  reason: entry.reason,
882
2064
  line: typeof entry.line === "number" ? entry.line : undefined
@@ -905,8 +2087,15 @@ var HARNESS_ARTIFACT_TYPES = new Set([
905
2087
  "unknown"
906
2088
  ]);
907
2089
  function normalizeGraphItemFieldAliases(collection, item) {
908
- if (!isRecord(item))
2090
+ if (!isRecord2(item))
909
2091
  return;
2092
+ if ((collection === "runtimeAgents" || collection === "artifacts") && item.status !== undefined) {
2093
+ const status = normalizeComponentStatus(item.status);
2094
+ if (status)
2095
+ item.status = status;
2096
+ else
2097
+ delete item.status;
2098
+ }
910
2099
  if (collection === "developerAgentAdapters") {
911
2100
  item.kind = "developer_agent_adapter";
912
2101
  if (typeof item.sourcePath !== "string")
@@ -975,14 +2164,14 @@ function firstEvidencePath(item) {
975
2164
  if (!Array.isArray(item.evidence))
976
2165
  return;
977
2166
  for (const evidence of item.evidence) {
978
- if (isRecord(evidence) && typeof evidence.path === "string" && evidence.path.length > 0) {
2167
+ if (isRecord2(evidence) && typeof evidence.path === "string" && evidence.path.length > 0) {
979
2168
  return evidence.path;
980
2169
  }
981
2170
  }
982
2171
  return;
983
2172
  }
984
2173
  function stampRemoteSnapshotTruncation(report) {
985
- const scanRecord = isRecord(report.graph.scan) ? report.graph.scan : {};
2174
+ const scanRecord = isRecord2(report.graph.scan) ? report.graph.scan : {};
986
2175
  const truncationReasons = Array.isArray(scanRecord.truncationReasons) ? scanRecord.truncationReasons.map(String) : [];
987
2176
  const stamped = {
988
2177
  ...report,
@@ -1007,12 +2196,12 @@ function requiresGraphItemConfidence(collection) {
1007
2196
  return collection !== "relationships" && collection !== "unknowns";
1008
2197
  }
1009
2198
  function graphItemLabel(collection, item, index) {
1010
- if (isRecord(item) && typeof item.id === "string" && item.id.length > 0)
2199
+ if (isRecord2(item) && typeof item.id === "string" && item.id.length > 0)
1011
2200
  return `${collection}.${item.id}`;
1012
2201
  return `${collection} item ${index + 1}`;
1013
2202
  }
1014
2203
  function normalizeConfidenceField(value, fallback) {
1015
- if (!isRecord(value))
2204
+ if (!isRecord2(value))
1016
2205
  return;
1017
2206
  const confidence = confidenceNumberFromUnknown(value.confidence);
1018
2207
  if (confidence !== undefined)
@@ -1057,7 +2246,7 @@ function inferClaimKind(subjectId, graph) {
1057
2246
  ];
1058
2247
  for (const [field, kind] of collectionKinds) {
1059
2248
  const collection = graph[field];
1060
- if (Array.isArray(collection) && collection.some((item) => isRecord(item) && item.id === subjectId)) {
2249
+ if (Array.isArray(collection) && collection.some((item) => isRecord2(item) && item.id === subjectId)) {
1061
2250
  return kind;
1062
2251
  }
1063
2252
  }
@@ -1134,7 +2323,7 @@ function normalizeClaimKind(value, subjectId, graph) {
1134
2323
  return inferred;
1135
2324
  }
1136
2325
  function analystAdapter(report) {
1137
- const analyst = isRecord(report.analyst) ? report.analyst : {};
2326
+ const analyst = isRecord2(report.analyst) ? report.analyst : {};
1138
2327
  return typeof analyst.adapter === "string" ? analyst.adapter : "external-analyst";
1139
2328
  }
1140
2329
  function defaultHarnessReadPlan() {
@@ -1195,12 +2384,12 @@ function resolveHarnessReportPath(rootDir, value) {
1195
2384
  }
1196
2385
  function writeHarnessReportArtifacts(options) {
1197
2386
  const written = [];
1198
- writeJson(join(options.rootDir, HARNESS_REPORT_PATH), options.report);
2387
+ writeJson(join3(options.rootDir, HARNESS_REPORT_PATH), options.report);
1199
2388
  written.push(HARNESS_REPORT_PATH);
1200
- writeJson(join(options.rootDir, HARNESS_REPORT_VALIDATION_PATH), options.validation);
2389
+ writeJson(join3(options.rootDir, HARNESS_REPORT_VALIDATION_PATH), options.validation);
1201
2390
  written.push(HARNESS_REPORT_VALIDATION_PATH);
1202
2391
  if (options.markdown) {
1203
- const markdownPath = join(options.rootDir, HARNESS_REPORT_MARKDOWN_PATH);
2392
+ const markdownPath = join3(options.rootDir, HARNESS_REPORT_MARKDOWN_PATH);
1204
2393
  mkdirSync(dirname(markdownPath), { recursive: true });
1205
2394
  writeFileSync(markdownPath, renderHarnessReportMarkdown(options.report, options.validation));
1206
2395
  written.push(HARNESS_REPORT_MARKDOWN_PATH);
@@ -1208,7 +2397,7 @@ function writeHarnessReportArtifacts(options) {
1208
2397
  return written;
1209
2398
  }
1210
2399
  function renderHarnessReportMarkdown(report, validation) {
1211
- const summary = isRecord(report.summary) ? report.summary : {};
2400
+ const summary = isRecord2(report.summary) ? report.summary : {};
1212
2401
  const citations = Array.isArray(report.citations) ? report.citations : [];
1213
2402
  const claims = Array.isArray(report.claims) ? report.claims : [];
1214
2403
  const unknowns = Array.isArray(report.unknowns) ? report.unknowns : [];
@@ -1232,7 +2421,7 @@ function renderHarnessReportMarkdown(report, validation) {
1232
2421
  ""
1233
2422
  ];
1234
2423
  for (const [index, claim] of claims.slice(0, 80).entries()) {
1235
- const claimRecord = isRecord(claim) ? claim : {};
2424
+ const claimRecord = isRecord2(claim) ? claim : {};
1236
2425
  const citationIds = Array.isArray(claimRecord.citationIds) ? claimRecord.citationIds : [];
1237
2426
  const citedPaths = citationIds.map((id) => citations.find((citation) => citation.id === id)).filter((citation) => Boolean(citation)).map((citation) => `${citation.path ?? citation.id}${citation.line ? `:${citation.line}` : ""}`).join(", ");
1238
2427
  lines.push(`### ${typeof claimRecord.title === "string" ? claimRecord.title : claimLabel(claim, index)}`, "", `- Kind: ${typeof claimRecord.kind === "string" ? claimRecord.kind : "unknown"}`, `- Confidence: ${formatConfidence(claimRecord.confidence)}`, `- Evidence: ${citedPaths || "none"}`, `- Summary: ${typeof claimRecord.summary === "string" ? claimRecord.summary : "unknown"}`, "");
@@ -1243,7 +2432,7 @@ function renderHarnessReportMarkdown(report, validation) {
1243
2432
  if (unknowns.length > 0) {
1244
2433
  lines.push("## Open Questions", "");
1245
2434
  for (const unknown of unknowns) {
1246
- lines.push(`- ${isRecord(unknown) && typeof unknown.question === "string" ? unknown.question : "Unknown harness question"}`);
2435
+ lines.push(`- ${isRecord2(unknown) && typeof unknown.question === "string" ? unknown.question : "Unknown harness question"}`);
1247
2436
  }
1248
2437
  lines.push("");
1249
2438
  }
@@ -1253,7 +2442,7 @@ function renderHarnessReportMarkdown(report, validation) {
1253
2442
  `;
1254
2443
  }
1255
2444
  function claimLabel(claim, index) {
1256
- if (isRecord(claim)) {
2445
+ if (isRecord2(claim)) {
1257
2446
  if (typeof claim.id === "string" && claim.id.length > 0)
1258
2447
  return claim.id;
1259
2448
  if (typeof claim.title === "string" && claim.title.length > 0)
@@ -1262,7 +2451,7 @@ function claimLabel(claim, index) {
1262
2451
  return `claim #${index + 1}`;
1263
2452
  }
1264
2453
  function citationLabel(citation, index) {
1265
- if (isRecord(citation) && typeof citation.id === "string" && citation.id.length > 0)
2454
+ if (isRecord2(citation) && typeof citation.id === "string" && citation.id.length > 0)
1266
2455
  return citation.id;
1267
2456
  return `#${index + 1}`;
1268
2457
  }
@@ -1332,10 +2521,10 @@ function citationExcerpt(rootDir, path, line) {
1332
2521
  if (!isInside(rootDir, absPath) || !existsSync(absPath))
1333
2522
  return {};
1334
2523
  try {
1335
- const stat = lstatSync(absPath);
2524
+ const stat = lstatSync2(absPath);
1336
2525
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 131072)
1337
2526
  return {};
1338
- const lines = readFileSync(absPath, "utf8").split(/\r?\n/);
2527
+ const lines = readFileSync3(absPath, "utf8").split(/\r?\n/);
1339
2528
  const selected = line ? lines[line - 1] : lines.find((candidate) => candidate.trim().length > 0);
1340
2529
  if (!selected)
1341
2530
  return {};
@@ -1345,13 +2534,13 @@ function citationExcerpt(rootDir, path, line) {
1345
2534
  }
1346
2535
  }
1347
2536
  function validateSummaryMatchesGraph(summary, graph, check) {
1348
- if (!isRecord(graph))
2537
+ if (!isRecord2(graph))
1349
2538
  return;
1350
2539
  const fields = {
1351
2540
  runtimeAgentCount: safeArrayLength(graph.runtimeAgents),
1352
2541
  agentFamilyCount: safeArrayLength(graph.agentFamilies),
1353
2542
  artifactCount: safeArrayLength(graph.artifacts),
1354
- sharedArtifactCount: Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord(artifact) && artifact.scope === "shared").length : 0,
2543
+ sharedArtifactCount: Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord2(artifact) && artifact.scope === "shared").length : 0,
1355
2544
  channelAdapterCount: safeArrayLength(graph.channelAdapters),
1356
2545
  identityNamespaceCount: safeArrayLength(graph.identityNamespaces),
1357
2546
  environmentCount: safeArrayLength(graph.environments),
@@ -1394,7 +2583,7 @@ function validateClaimSubjectExists(graph, claim, errors, warnings) {
1394
2583
  const collection = graph[collectionName];
1395
2584
  if (!Array.isArray(collection))
1396
2585
  return;
1397
- if (collection.some((item) => isRecord(item) && item.id === claim.subjectId))
2586
+ if (collection.some((item) => isRecord2(item) && item.id === claim.subjectId))
1398
2587
  return;
1399
2588
  const message = `Claim ${claim.id} subject ${claim.subjectId} is not present in graph.${collectionName}.`;
1400
2589
  if (claim.kind === "unknown")
@@ -1403,7 +2592,7 @@ function validateClaimSubjectExists(graph, claim, errors, warnings) {
1403
2592
  errors.push(message);
1404
2593
  }
1405
2594
  function validateClaimQuality(claims, graph, warn) {
1406
- const claimRecords = claims.filter(isRecord);
2595
+ const claimRecords = claims.filter(isRecord2);
1407
2596
  if (claimRecords.length === 0)
1408
2597
  return;
1409
2598
  const graphClaimableItems = graph ? safeArrayLength(graph.runtimeAgents) + safeArrayLength(graph.agentFamilies) + safeArrayLength(graph.developerAgentAdapters) + safeArrayLength(graph.artifacts) + safeArrayLength(graph.channelAdapters) + safeArrayLength(graph.identityNamespaces) + safeArrayLength(graph.agentFrameworks) + safeArrayLength(graph.modelProviders) + safeArrayLength(graph.deployments) + safeArrayLength(graph.relationships) : 0;
@@ -1483,7 +2672,7 @@ function validateCitation(citation, rootDir, errors, warnings, options) {
1483
2672
  return;
1484
2673
  }
1485
2674
  try {
1486
- const stat = lstatSync(absPath);
2675
+ const stat = lstatSync2(absPath);
1487
2676
  if (stat.isSymbolicLink())
1488
2677
  errors.push(`Citation ${citation.id} points to a symlink.`);
1489
2678
  if (stat.isDirectory()) {
@@ -1492,7 +2681,7 @@ function validateCitation(citation, rootDir, errors, warnings, options) {
1492
2681
  }
1493
2682
  if (!stat.isFile())
1494
2683
  errors.push(`Citation ${citation.id} does not point to a file.`);
1495
- const lines = readFileSync(absPath, "utf8").split(/\r?\n/);
2684
+ const lines = readFileSync3(absPath, "utf8").split(/\r?\n/);
1496
2685
  if (citation.line !== undefined) {
1497
2686
  const lineCount = lines.length;
1498
2687
  if (citation.line < 1 || citation.line > lineCount) {
@@ -1543,7 +2732,7 @@ function isSecretishPath(path) {
1543
2732
  return name === ".env" || name.startsWith(".env.") || name.includes("secret") || name.includes("private-key") || name.endsWith(".pem");
1544
2733
  }
1545
2734
  function isInside(rootDir, absPath) {
1546
- const rel = relative(resolve(rootDir), resolve(absPath));
2735
+ const rel = relative3(resolve(rootDir), resolve(absPath));
1547
2736
  return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
1548
2737
  }
1549
2738
  function writeJson(path, value) {
@@ -1569,11 +2758,11 @@ function sortJson(value) {
1569
2758
  }
1570
2759
  return value;
1571
2760
  }
1572
- function isRecord(value) {
2761
+ function isRecord2(value) {
1573
2762
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1574
2763
  }
1575
2764
  function normalizePath(path) {
1576
2765
  return path.replace(/\\/g, "/");
1577
2766
  }
1578
2767
 
1579
- export { HARNESS_REPORT_SCHEMA_VERSION, HARNESS_REPORT_VALIDATION_SCHEMA_VERSION, HARNESS_REPORT_APPROVAL_SCHEMA_VERSION, HARNESS_REPORT_PATH, HARNESS_REPORT_MARKDOWN_PATH, HARNESS_REPORT_VALIDATION_PATH, HARNESS_REPORT_APPROVAL_PATH, HARNESS_ANALYZE_CONCERNS, MAX_HARNESS_REPORT_SUMMARY_LENGTH, createHarnessReport, validateHarnessReport, approveHarnessReport, loadHarnessReportForSync, readHarnessReport, normalizeHarnessReport, requiresArtifactBody, artifactHasBody, firstEvidencePath, stampRemoteSnapshotTruncation, resolveHarnessReportPath, writeHarnessReportArtifacts, renderHarnessReportMarkdown, hashHarnessReport, hashStableJson, graphForReport };
2768
+ export { mergeBoundRuntimeClasses, tagComponentStatus, HARNESS_REPORT_SCHEMA_VERSION, HARNESS_REPORT_VALIDATION_SCHEMA_VERSION, HARNESS_REPORT_APPROVAL_SCHEMA_VERSION, HARNESS_REPORT_PATH, HARNESS_REPORT_MARKDOWN_PATH, HARNESS_REPORT_VALIDATION_PATH, HARNESS_REPORT_APPROVAL_PATH, HARNESS_ANALYZE_CONCERNS, MAX_HARNESS_REPORT_SUMMARY_LENGTH, createHarnessReport, validateHarnessReport, approveHarnessReport, loadHarnessReportForSync, readHarnessReport, normalizeHarnessReport, requiresArtifactBody, artifactHasBody, firstEvidencePath, stampRemoteSnapshotTruncation, resolveHarnessReportPath, writeHarnessReportArtifacts, renderHarnessReportMarkdown, hashHarnessReport, hashStableJson, graphForReport };