agent-valley 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6439,6 +6439,72 @@ var init_zod = __esm(() => {
6439
6439
  init_external();
6440
6440
  });
6441
6441
 
6442
+ // ../../packages/core/src/oma/skill-matrix-schema.ts
6443
+ var hash, name, bounded, path, skillCompatibilityConfigSchema, skillCompatibilityPolicySchema, omaConfigSchema, matrixSkillSchema, matrixReportSchema;
6444
+ var init_skill_matrix_schema = __esm(() => {
6445
+ init_zod();
6446
+ hash = string2().regex(/^[a-f0-9]{64}$/);
6447
+ name = string2().regex(/^[a-z0-9][a-z0-9-]{0,127}$/);
6448
+ bounded = string2().trim().min(1).max(2000);
6449
+ path = string2().min(1).max(4096).refine((value) => !value.includes("\x00"));
6450
+ skillCompatibilityConfigSchema = strictObject({
6451
+ report_path: path,
6452
+ max_age_hours: number2().finite().positive().max(8760).default(168),
6453
+ mode: _enum(["warn", "require"])
6454
+ });
6455
+ skillCompatibilityPolicySchema = strictObject({
6456
+ reportPath: path,
6457
+ maxAgeHours: number2().finite().positive().max(8760),
6458
+ mode: _enum(["warn", "require"])
6459
+ });
6460
+ omaConfigSchema = strictObject({
6461
+ mode: _enum(["off", "strict"]).default("off"),
6462
+ skill_compatibility: skillCompatibilityConfigSchema.optional()
6463
+ });
6464
+ matrixSkillSchema = object({
6465
+ name,
6466
+ hash,
6467
+ caseId: bounded,
6468
+ requiredFiles: array(path).min(1).max(500),
6469
+ missingFiles: array(path).max(500),
6470
+ excludedReferences: array(object({ path, reason: bounded })).max(500)
6471
+ });
6472
+ matrixReportSchema = object({
6473
+ schemaVersion: literal(1),
6474
+ kind: literal("skill-compatibility-matrix"),
6475
+ mode: _enum(["plan", "live"]),
6476
+ status: _enum(["planned", "completed", "cancelled", "interrupted"]),
6477
+ createdAt: exports_iso.datetime(),
6478
+ omaVersion: bounded,
6479
+ host: object({ platform: bounded, arch: bounded, node: bounded }),
6480
+ suiteHash: hash,
6481
+ sourceKind: _enum(["synthetic", "installed"]),
6482
+ delivery: _enum(["native", "injected"]),
6483
+ auditScope: _enum(["fixture-contract", "read-reference"]),
6484
+ protocolVersion: literal("oma-skill-matrix-v2"),
6485
+ bundle: object({ hash, skills: array(matrixSkillSchema).min(1).max(100) }).optional(),
6486
+ cases: array(object({ id: bounded, skill: name })).max(100),
6487
+ models: object({ claude: bounded.nullable(), codex: bounded.nullable() }),
6488
+ cells: array(object({
6489
+ caseId: bounded,
6490
+ skill: name,
6491
+ vendor: _enum(["claude", "codex"]),
6492
+ status: _enum(["pass", "fail", "unverifiable", "error"]),
6493
+ contentHash: hash,
6494
+ checks: array(object({
6495
+ id: bounded,
6496
+ status: _enum(["pass", "fail", "unverifiable"]),
6497
+ detail: bounded,
6498
+ proof: _enum(["read", "canary"]).optional()
6499
+ })).max(1000),
6500
+ nativeActivation: _enum(["observed", "unobserved"]),
6501
+ cliVersion: string2().regex(/^[\w .()+/-]{1,160}$/).nullable(),
6502
+ model: bounded.nullable(),
6503
+ durationMs: number2().finite().nonnegative()
6504
+ })).max(200)
6505
+ });
6506
+ });
6507
+
6442
6508
  // ../../packages/core/src/config/task-schema.ts
6443
6509
  var reportPath, taskSchema, resolvedTaskSchema;
6444
6510
  var init_task_schema = __esm(() => {
@@ -6896,6 +6962,7 @@ var init_execution = __esm(() => {
6896
6962
  var actorType, text, amount, routingCandidateSchema, chiefOperatingPolicySchema, operatingRunSchema, routeEvidenceSchema, reviewDecisionSchema, chiefOperationsSchema;
6897
6963
  var init_operations = __esm(() => {
6898
6964
  init_zod();
6965
+ init_skill_matrix_schema();
6899
6966
  actorType = string2().regex(/^[a-z0-9][a-z0-9-]{0,127}$/);
6900
6967
  text = string2().trim().min(1).max(2000);
6901
6968
  amount = number2().finite().nonnegative();
@@ -6914,6 +6981,7 @@ var init_operations = __esm(() => {
6914
6981
  reviewVendor: _enum(["prefer", "require", "off"]),
6915
6982
  readyActors: array(actorType).max(20).optional(),
6916
6983
  memory: boolean2(),
6984
+ skillCompatibility: skillCompatibilityPolicySchema.optional(),
6917
6985
  metricTargets: array(strictObject({
6918
6986
  name: text,
6919
6987
  unit: text.optional(),
@@ -7232,7 +7300,7 @@ function chiefExecutionPolicy(config) {
7232
7300
  autoResume: execution.auto_resume
7233
7301
  } : {});
7234
7302
  }
7235
- function chiefOperatingPolicy(config, readyActors, automatic = true) {
7303
+ function chiefOperatingPolicy(config, readyActors, automatic = true, skillCompatibility) {
7236
7304
  return chiefOperatingPolicySchema.parse({
7237
7305
  routing: config.routing ? {
7238
7306
  candidates: config.routing.candidates.map((item) => ({
@@ -7247,7 +7315,8 @@ function chiefOperatingPolicy(config, readyActors, automatic = true) {
7247
7315
  reviewVendor: config.review_vendor ?? "prefer",
7248
7316
  memory: config.memory ?? true,
7249
7317
  metricTargets: config.metric_targets,
7250
- readyActors
7318
+ readyActors,
7319
+ skillCompatibility
7251
7320
  });
7252
7321
  }
7253
7322
  function chiefCapturePolicy(config) {
@@ -14575,7 +14644,7 @@ function mergeConfigs(global2, project, env) {
14575
14644
  tunnel: buildTunnelConfig(project),
14576
14645
  verify: buildVerifyConfig(project),
14577
14646
  task: project?.task?.kind === "analysis" ? { kind: "analysis", reportPath: project.task.report_path } : { kind: "code" },
14578
- oma: { mode: project?.oma?.mode ?? "off" },
14647
+ oma: project?.oma ?? { mode: "off" },
14579
14648
  chief: global2?.chief || project?.chief ? mergeChiefConfig(global2?.chief, project?.chief) : undefined
14580
14649
  };
14581
14650
  }
@@ -14613,6 +14682,7 @@ Symphony cannot start until all config errors are resolved.`);
14613
14682
  var globalConfigSchema, projectConfigSchema, githubConfigSchema, mergedConfigSchema;
14614
14683
  var init_yaml_loader = __esm(() => {
14615
14684
  init_zod();
14685
+ init_skill_matrix_schema();
14616
14686
  init_actor_schema();
14617
14687
  init_budget_schema();
14618
14688
  init_chief_schema();
@@ -14706,7 +14776,7 @@ var init_yaml_loader = __esm(() => {
14706
14776
  tunnel: tunnelProjectSchema,
14707
14777
  verify: verifyProjectSchema,
14708
14778
  task: taskSchema.optional(),
14709
- oma: object({ mode: _enum(["off", "strict"]).default("off") }).optional(),
14779
+ oma: omaConfigSchema.optional(),
14710
14780
  chief: chiefConfigSchema.optional()
14711
14781
  }).strict().overwrite(normalizeActorDefaults);
14712
14782
  githubConfigSchema = object({
@@ -14775,7 +14845,7 @@ var init_yaml_loader = __esm(() => {
14775
14845
  tunnel: tunnelMergedSchema,
14776
14846
  verify: verifyMergedSchema,
14777
14847
  task: resolvedTaskSchema.optional(),
14778
- oma: object({ mode: _enum(["off", "strict"]) }).optional(),
14848
+ oma: omaConfigSchema.optional(),
14779
14849
  chief: chiefConfigSchema.optional()
14780
14850
  }).superRefine((cfg, ctx) => {
14781
14851
  if (cfg.trackerKind === "linear") {
@@ -20724,7 +20794,7 @@ var init_readdirp = __esm(() => {
20724
20794
 
20725
20795
  // ../../node_modules/.bun/chokidar@5.0.0/node_modules/chokidar/handler.js
20726
20796
  import { watch as fs_watch, unwatchFile, watchFile } from "node:fs";
20727
- import { realpath as fsrealpath, lstat as lstat18, open as open11, stat as stat4 } from "node:fs/promises";
20797
+ import { realpath as fsrealpath, lstat as lstat18, open as open12, stat as stat4 } from "node:fs/promises";
20728
20798
  import { type as osType } from "node:os";
20729
20799
  import * as sp from "node:path";
20730
20800
  function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
@@ -21066,7 +21136,7 @@ var STR_DATA = "data", STR_END = "end", STR_CLOSE = "close", EMPTY_FN = () => {}
21066
21136
  cont.watcherUnusable = true;
21067
21137
  if (isWindows && error.code === "EPERM") {
21068
21138
  try {
21069
- const fd = await open11(path, "r");
21139
+ const fd = await open12(path, "r");
21070
21140
  await fd.close();
21071
21141
  broadcastErr(error);
21072
21142
  } catch (err) {}
@@ -22699,7 +22769,7 @@ var init_agent_provisioning = __esm(() => {
22699
22769
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "node:fs";
22700
22770
  import { chmod as chmod4, lstat as lstat19, readdir as readdir12, readFile as readFile16, realpath as realpath25, unlink as unlink8, writeFile as writeFile14 } from "node:fs/promises";
22701
22771
  import { homedir as homedir8, tmpdir as tmpdir5 } from "node:os";
22702
- import { delimiter as delimiter4, isAbsolute as isAbsolute15, join as join47, relative as relative14, resolve as resolve27 } from "node:path";
22772
+ import { delimiter as delimiter4, isAbsolute as isAbsolute15, join as join47, relative as relative14, resolve as resolve28 } from "node:path";
22703
22773
  function defaults2() {
22704
22774
  const home = homedir8();
22705
22775
  return {
@@ -22773,7 +22843,7 @@ async function restoreConfig(agents, snapshots) {
22773
22843
  async function workspacePath(input) {
22774
22844
  if (!input.trim() || !isAbsolute15(input))
22775
22845
  return null;
22776
- const root = resolve27(input);
22846
+ const root = resolve28(input);
22777
22847
  if (!await directory2(root))
22778
22848
  return null;
22779
22849
  for (const path of [join47(root, ".agents"), join47(root, ".agents", "skills")]) {
@@ -24399,7 +24469,7 @@ var init_workspace_step = __esm(() => {
24399
24469
 
24400
24470
  // src/setup/noninteractive-config.ts
24401
24471
  import { lstat as lstat20, realpath as realpath26 } from "node:fs/promises";
24402
- import { isAbsolute as isAbsolute17, join as join48, parse as parsePath, resolve as resolve28 } from "node:path";
24472
+ import { isAbsolute as isAbsolute17, join as join48, parse as parsePath, resolve as resolve29 } from "node:path";
24403
24473
  function validateNoninteractiveOptions(options) {
24404
24474
  if (!options.yes || options.edit || options.mode !== undefined && options.mode !== "order")
24405
24475
  throw new Error("Use av setup --yes only for local order setup; --edit and --mode tracker require interactive setup.");
@@ -24480,13 +24550,13 @@ async function prepareNoninteractiveConfig(options, deps) {
24480
24550
  validateNoninteractiveOptions(options);
24481
24551
  let projectRoot;
24482
24552
  try {
24483
- projectRoot = await realpath26(resolve28(deps.projectRoot));
24553
+ projectRoot = await realpath26(resolve29(deps.projectRoot));
24484
24554
  if (!(await lstat20(projectRoot)).isDirectory())
24485
24555
  throw new Error("not a directory");
24486
24556
  } catch {
24487
24557
  throw new Error(`AV configuration directory ${deps.projectRoot} does not exist. Run av setup from an existing directory.`);
24488
24558
  }
24489
- const globalPath = resolve28(deps.globalConfigPath);
24559
+ const globalPath = resolve29(deps.globalConfigPath);
24490
24560
  const projectPath = join48(projectRoot, "av.yaml");
24491
24561
  const project = await configFile(projectPath, projectConfigSchema);
24492
24562
  const global2 = await configFile(globalPath, globalConfigSchema);
@@ -24504,7 +24574,7 @@ async function prepareNoninteractiveConfig(options, deps) {
24504
24574
  }
24505
24575
  const savedWorkspace = project.raw.workspace?.root;
24506
24576
  const selectedWorkspace = options.workspace ?? savedWorkspace ?? projectRoot;
24507
- const workspacePath = isAbsolute17(selectedWorkspace) ? selectedWorkspace : resolve28(projectRoot, selectedWorkspace);
24577
+ const workspacePath = isAbsolute17(selectedWorkspace) ? selectedWorkspace : resolve29(projectRoot, selectedWorkspace);
24508
24578
  const workspaceError = await validateOrderWorkspace(workspacePath);
24509
24579
  if (workspaceError)
24510
24580
  throw new Error(workspaceError);
@@ -24539,7 +24609,7 @@ var init_noninteractive_config = __esm(() => {
24539
24609
  });
24540
24610
 
24541
24611
  // src/setup/noninteractive.ts
24542
- import { join as join49, parse as parsePath2, resolve as resolve29 } from "node:path";
24612
+ import { join as join49, parse as parsePath2, resolve as resolve30 } from "node:path";
24543
24613
  function rerunCommand(result, options) {
24544
24614
  const quote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
24545
24615
  return `cd ${quote(result.projectRoot)} && av setup --yes --actor ${result.chief.actorType} --model ${quote(result.chief.model ?? "")} --workspace ${quote(result.workspace ?? result.projectRoot)} --oma ${options.oma ?? "prepare"} --json`;
@@ -24550,11 +24620,11 @@ async function setupNoninteractive(options, overrides = {}) {
24550
24620
  version: 1,
24551
24621
  status: "failed",
24552
24622
  exitCode: 1,
24553
- projectRoot: resolve29(deps.projectRoot),
24623
+ projectRoot: resolve30(deps.projectRoot),
24554
24624
  workspace: null,
24555
24625
  config: {
24556
- project: join49(resolve29(deps.projectRoot), "av.yaml"),
24557
- global: resolve29(deps.globalConfigPath),
24626
+ project: join49(resolve30(deps.projectRoot), "av.yaml"),
24627
+ global: resolve30(deps.globalConfigPath),
24558
24628
  saved: false
24559
24629
  },
24560
24630
  chief: {
@@ -25127,18 +25197,18 @@ async function stepMetrics(ctx) {
25127
25197
  draft.file = file;
25128
25198
  }
25129
25199
  for (const field of ["value_path", "timestamp_path", "unit_path"]) {
25130
- const value = await text12(`JSON ${field}`, old?.[field] ?? field.replace("_path", ""), path3);
25200
+ const value = await text12(`JSON ${field}`, old?.[field] ?? field.replace("_path", ""), path4);
25131
25201
  if (navigated2(value))
25132
25202
  return value;
25133
25203
  draft[field] = value;
25134
25204
  }
25135
- const start = await text12("JSON measurement window start path (optional; blank omits window)", old?.window_start_path ?? "", (value) => value ? path3(value) : undefined);
25205
+ const start = await text12("JSON measurement window start path (optional; blank omits window)", old?.window_start_path ?? "", (value) => value ? path4(value) : undefined);
25136
25206
  if (navigated2(start))
25137
25207
  return start;
25138
25208
  delete draft.window_start_path;
25139
25209
  delete draft.window_end_path;
25140
25210
  if (start) {
25141
- const end = await text12("JSON measurement window end path", old?.window_end_path ?? "window.end", path3);
25211
+ const end = await text12("JSON measurement window end path", old?.window_end_path ?? "window.end", path4);
25142
25212
  if (navigated2(end))
25143
25213
  return end;
25144
25214
  draft.window_start_path = start;
@@ -25184,7 +25254,7 @@ async function stepMetrics(ctx) {
25184
25254
  ctx.chiefChanged = true;
25185
25255
  log2.info("Metric settings are saved after confirmation. Measurements are collected at mission runtime; setup does not contact the source. Set --duration or chief.execution.max_duration_sec long enough for work and the full measurement/observation windows. The default mission limit is 86400 seconds including waits; Stripe's default measurement window is also 86400 seconds.");
25186
25256
  }
25187
- var envName3 = (value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value), navigated2 = (value) => typeof value !== "string", required2 = (limit) => (value) => value && value.length <= limit ? undefined : `Enter 1 to ${limit} characters.`, environment = (optional = false) => (value) => optional && !value || envName3(value) ? undefined : "Enter an environment variable name, never a token or URL.", number4 = (minimum, maximum) => (value) => value && Number.isInteger(Number(value)) && Number(value) >= minimum && Number(value) <= maximum ? undefined : `Enter a whole number from ${minimum} to ${maximum}.`, path3 = (value) => {
25257
+ var envName3 = (value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value), navigated2 = (value) => typeof value !== "string", required2 = (limit) => (value) => value && value.length <= limit ? undefined : `Enter 1 to ${limit} characters.`, environment = (optional = false) => (value) => optional && !value || envName3(value) ? undefined : "Enter an environment variable name, never a token or URL.", number4 = (minimum, maximum) => (value) => value && Number.isInteger(Number(value)) && Number(value) >= minimum && Number(value) <= maximum ? undefined : `Enter a whole number from ${minimum} to ${maximum}.`, path4 = (value) => {
25188
25258
  const result = metricSourceSchema.shape.value_path.safeParse(value);
25189
25259
  return result.success ? undefined : "Use dot-separated JSON keys or array indexes, without prototype keys.";
25190
25260
  };
@@ -27762,7 +27832,7 @@ init_project_config_path();
27762
27832
  init_yaml_loader();
27763
27833
  import { spawn as spawn24, spawnSync as spawnSync5 } from "node:child_process";
27764
27834
  import { existsSync as existsSync10, unlinkSync as unlinkSync2 } from "node:fs";
27765
- import { resolve as resolve30 } from "node:path";
27835
+ import { resolve as resolve31 } from "node:path";
27766
27836
 
27767
27837
  // ../../packages/core/src/version.ts
27768
27838
  import { readFileSync as readFileSync2 } from "node:fs";
@@ -27872,7 +27942,7 @@ function humanReadableArgName(arg) {
27872
27942
  // ../../node_modules/.bun/commander@15.0.0/node_modules/commander/lib/command.js
27873
27943
  import { EventEmitter } from "node:events";
27874
27944
  import childProcess from "node:child_process";
27875
- import path from "node:path";
27945
+ import path2 from "node:path";
27876
27946
  import fs from "node:fs";
27877
27947
  import process2 from "node:process";
27878
27948
  import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
@@ -29016,10 +29086,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
29016
29086
  args = args.slice();
29017
29087
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
29018
29088
  function findFile(baseDir, baseName) {
29019
- const localBin = path.resolve(baseDir, baseName);
29089
+ const localBin = path2.resolve(baseDir, baseName);
29020
29090
  if (fs.existsSync(localBin))
29021
29091
  return localBin;
29022
- if (sourceExt.includes(path.extname(baseName)))
29092
+ if (sourceExt.includes(path2.extname(baseName)))
29023
29093
  return;
29024
29094
  const foundExt = sourceExt.find((ext) => fs.existsSync(`${localBin}${ext}`));
29025
29095
  if (foundExt)
@@ -29037,19 +29107,19 @@ Expecting one of '${allowedValues.join("', '")}'`);
29037
29107
  } catch {
29038
29108
  resolvedScriptPath = this._scriptPath;
29039
29109
  }
29040
- executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
29110
+ executableDir = path2.resolve(path2.dirname(resolvedScriptPath), executableDir);
29041
29111
  }
29042
29112
  if (executableDir) {
29043
29113
  let localFile = findFile(executableDir, executableFile);
29044
29114
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
29045
- const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
29115
+ const legacyName = path2.basename(this._scriptPath, path2.extname(this._scriptPath));
29046
29116
  if (legacyName !== this._name) {
29047
29117
  localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
29048
29118
  }
29049
29119
  }
29050
29120
  executableFile = localFile || executableFile;
29051
29121
  }
29052
- const launchWithNode = sourceExt.includes(path.extname(executableFile));
29122
+ const launchWithNode = sourceExt.includes(path2.extname(executableFile));
29053
29123
  let proc;
29054
29124
  if (process2.platform !== "win32") {
29055
29125
  if (launchWithNode) {
@@ -29640,13 +29710,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
29640
29710
  cmd.helpGroup(this._defaultCommandGroup);
29641
29711
  }
29642
29712
  nameFromFilename(filename) {
29643
- this._name = path.basename(filename, path.extname(filename));
29713
+ this._name = path2.basename(filename, path2.extname(filename));
29644
29714
  return this;
29645
29715
  }
29646
- executableDir(path2) {
29647
- if (path2 === undefined)
29716
+ executableDir(path) {
29717
+ if (path === undefined)
29648
29718
  return this._executableDir;
29649
- this._executableDir = path2;
29719
+ this._executableDir = path;
29650
29720
  return this;
29651
29721
  }
29652
29722
  helpInformation(contextOptions) {
@@ -48938,7 +49008,7 @@ var sha256 = string2().regex(/^[a-f0-9]{64}$/);
48938
49008
  function safeVerificationPath(path) {
48939
49009
  return path.length > 0 && path.length <= 1024 && !isAbsolute2(path) && !/^[a-z]:/i.test(path) && !/[\\\0\r\n]/.test(path) && path.split("/").every((part) => part !== "" && part !== "." && part !== "..") && !/^(?:\.git(?:\/|$)|\.agent-valley(?:\/|$)|\.agents\/(?:state|results)(?:\/|$))/.test(path);
48940
49010
  }
48941
- var path2 = string2().refine(safeVerificationPath, "Use a literal relative product path inside the worktree.");
49011
+ var path3 = string2().refine(safeVerificationPath, "Use a literal relative product path inside the worktree.");
48942
49012
  var testPath = (name, nodeOnly = false) => safeVerificationPath(name) && !name.startsWith("-") && !/[*?{}]/.test(name) && !name.startsWith("node_modules/") && (nodeOnly ? /(?:\.test|\.spec)\.(?:js|mjs|cjs)$/ : /(?:\.test|\.spec)\.(?:[cm]?[jt]sx?)$/).test(name);
48943
49013
  function verificationCommandTestPaths(program, args) {
48944
49014
  if (program === "bun" && args[0] === "test")
@@ -48975,14 +49045,14 @@ function safeVerificationCommand(program, args) {
48975
49045
  var goalVerificationCheckSchema = discriminatedUnion("kind", [
48976
49046
  strictObject({
48977
49047
  kind: literal("file"),
48978
- path: path2,
49048
+ path: path3,
48979
49049
  minBytes: number2().int().min(1).max(4194304).default(1),
48980
49050
  contains: array(text4).min(1).max(10).optional(),
48981
49051
  sha256: sha256.optional()
48982
49052
  }),
48983
49053
  strictObject({
48984
49054
  kind: literal("json"),
48985
- path: path2,
49055
+ path: path3,
48986
49056
  pointer: string2().max(1024).refine((value) => value === "" || /^(?:\/(?:[^~]|~[01])*)+$/.test(value), {
48987
49057
  message: "Use an RFC 6901 JSON pointer such as /engines/node."
48988
49058
  }),
@@ -49444,16 +49514,29 @@ function routeEvidence(mission) {
49444
49514
  function price2(candidate) {
49445
49515
  return candidate.inputPerMillionUsd !== undefined && candidate.outputPerMillionUsd !== undefined ? candidate.inputPerMillionUsd + candidate.outputPerMillionUsd : Number.POSITIVE_INFINITY;
49446
49516
  }
49447
- function selectWorkActor(mission, actor, taskId) {
49517
+ function workActorCandidates(mission, actor, preserveExplicit = false) {
49448
49518
  const routing = mission.operatingPolicy?.routing;
49449
- if (!routing || actor.id === mission.chiefId || !mission.availableAgents && actor.model)
49519
+ if (!routing || actor.id === mission.chiefId || !mission.availableAgents && (actor.model || preserveExplicit))
49520
+ return [{ actorType: actor.agentType, model: actor.model }];
49521
+ const ready = mission.operatingPolicy?.readyActors ?? mission.availableAgents ?? [];
49522
+ return routing.candidates.filter((candidate) => ready.includes(candidate.actorType));
49523
+ }
49524
+ function selectWorkActor(mission, actor, taskId, compatibleRoutes) {
49525
+ const routing = mission.operatingPolicy?.routing;
49526
+ if (!routing || actor.id === mission.chiefId || !mission.availableAgents && (actor.model || compatibleRoutes)) {
49527
+ if (compatibleRoutes && !compatibleRoutes.has(routeKey({ actorType: actor.agentType, model: actor.model })))
49528
+ throw new Error("Skill compatibility required: the configured Actor/model has no current passing evidence. Refresh oma.skill_compatibility.report_path in av.yaml; explicit Actors are not rerouted.");
49450
49529
  return { actor, reason: "Preserved the configured Actor and explicit model." };
49530
+ }
49451
49531
  const ready = mission.operatingPolicy?.readyActors ?? mission.availableAgents ?? [];
49452
49532
  const candidates = routing.candidates.filter((candidate) => ready.includes(candidate.actorType));
49453
49533
  if (!candidates.length)
49454
49534
  throw new Error("No configured work routing candidate is ready. Install/login a candidate CLI or update chief.routing.candidates in av.yaml.");
49535
+ const compatible = compatibleRoutes ? candidates.filter((candidate) => compatibleRoutes.has(routeKey(candidate))) : candidates;
49536
+ if (!compatible.length)
49537
+ throw new Error("Skill compatibility required: no ready work candidate has current passing installed evidence. Refresh oma.skill_compatibility.report_path in av.yaml with explicit models before retrying.");
49455
49538
  const evidence = routeEvidence(mission);
49456
- const eligible = candidates.filter((candidate) => {
49539
+ const eligible = compatible.filter((candidate) => {
49457
49540
  const observed = evidence.find((item) => routeKey(item) === routeKey(candidate));
49458
49541
  return !observed || observed.samples < routing.minSamples || observed.successes / observed.samples >= routing.minSuccessRate;
49459
49542
  });
@@ -49515,6 +49598,10 @@ function operationsEvidence(mission) {
49515
49598
  pricingBasis: "Estimates from configured input/output prices and actual adapter-reported tokens; excludes unreported cache/reasoning/subagent usage and subscription billing.",
49516
49599
  routeQuality: routeEvidence(mission).slice(0, 20),
49517
49600
  reviewPolicy: mission.operatingPolicy?.reviewVendor ?? "legacy independent Actor",
49601
+ skillCompatibility: mission.operatingPolicy?.skillCompatibility ? {
49602
+ mode: mission.operatingPolicy.skillCompatibility.mode,
49603
+ recentChecks: mission.history.filter((entry) => entry.stage === "skill-compatibility").slice(-10)
49604
+ } : undefined,
49518
49605
  recentRuns: runs.slice(-20),
49519
49606
  crossVendorReviews: mission.operations?.reviewDecisions.slice(-20) ?? [],
49520
49607
  organizationContext: mission.organizationContext,
@@ -49530,6 +49617,10 @@ function operationsReportLines(mission) {
49530
49617
  `기록된 token·설정 단가 기반 부분 비용 추정 USD ${evidence.knownEstimatedCostUsd.toFixed(6)} / 비용 미확인 ${evidence.unknownCostRuns}회 / 전체 추정 ${evidence.totalEstimatedCostUsd === null ? "미확인" : `USD ${evidence.totalEstimatedCostUsd.toFixed(6)}`}`,
49531
49618
  `최근 독립 리뷰 배정: 다른 vendor ${evidence.crossVendorReviews.filter((review) => review.crossVendor).length}회 / 동일 vendor fallback ${evidence.crossVendorReviews.filter((review) => !review.crossVendor).length}회 / 실제 리뷰 통과 ${evidence.crossVendorReviews.filter((review) => review.outcome === "passed").length}회`,
49532
49619
  `vendor 리뷰 정책: ${evidence.reviewPolicy}`,
49620
+ ...evidence.skillCompatibility ? [
49621
+ `OMA 스킬 읽기·참조 정책: ${evidence.skillCompatibility.mode}; AV 실행 환경 또는 작업 품질 인증이 아닙니다.`,
49622
+ ...evidence.skillCompatibility.recentChecks.map((entry) => `${entry.at}: ${entry.message}`)
49623
+ ] : [],
49533
49624
  ...mission.organizationContext ? [
49534
49625
  `계획·리뷰에 제공한 조직 근거: 승인 memory ${mission.organizationContext.memories.length}개 / 과거 outcome ${mission.organizationContext.outcomes.length}개 / 기록된 metric sample ${mission.organizationContext.metrics.length}개`
49535
49626
  ] : [],
@@ -49557,15 +49648,15 @@ var chiefReportSchema = strictObject({
49557
49648
  checks: entries,
49558
49649
  remaining: entries
49559
49650
  });
49560
- function bounded(value, limit = 500) {
49651
+ function bounded2(value, limit = 500) {
49561
49652
  const content = value.trim();
49562
49653
  return content.length <= limit ? content : `${content.slice(0, limit - 8)}… [생략]`;
49563
49654
  }
49564
49655
  function compact(values, limit = 20) {
49565
49656
  if (values.length <= limit)
49566
- return values.map((value) => bounded(value)).filter(Boolean);
49657
+ return values.map((value) => bounded2(value)).filter(Boolean);
49567
49658
  return [
49568
- ...values.slice(0, limit - 1).map((value) => bounded(value)).filter(Boolean),
49659
+ ...values.slice(0, limit - 1).map((value) => bounded2(value)).filter(Boolean),
49569
49660
  `${values.length - limit + 1}개 항목 생략`
49570
49661
  ];
49571
49662
  }
@@ -49580,7 +49671,7 @@ function criteria(mission) {
49580
49671
  function incompleteReasons(mission) {
49581
49672
  const reasons = [];
49582
49673
  if (mission.error)
49583
- reasons.push(`${["waiting", "paused"].includes(mission.status) ? "대기·중단 이유" : "실패 원인"}: ${bounded(mission.error, 450)}`);
49674
+ reasons.push(`${["waiting", "paused"].includes(mission.status) ? "대기·중단 이유" : "실패 원인"}: ${bounded2(mission.error, 450)}`);
49584
49675
  if (mission.status !== "completed")
49585
49676
  reasons.push(`미션 상태: ${mission.status}. 완료 판정이 없습니다.`);
49586
49677
  if (!mission.verification)
@@ -49633,33 +49724,33 @@ function incompleteReasons(mission) {
49633
49724
  }
49634
49725
  function decisionEvidence(mission) {
49635
49726
  const actions = { repair: "수정", reassign: "담당 교체", replan: "재계획", wait: "대기", stop: "중단" };
49636
- return (mission.supervision?.decisions ?? []).slice(-20).map((decision) => bounded(`${decision.round}차 ${actions[decision.action]}${decision.taskId ? ` / 작업 ${decision.taskId}` : ""}${decision.personaId ? ` / 담당 ${decision.personaId}` : ""}: ${decision.reason}`));
49727
+ return (mission.supervision?.decisions ?? []).slice(-20).map((decision) => bounded2(`${decision.round}차 ${actions[decision.action]}${decision.taskId ? ` / 작업 ${decision.taskId}` : ""}${decision.personaId ? ` / 담당 ${decision.personaId}` : ""}: ${decision.reason}`));
49637
49728
  }
49638
49729
  function reviewEvidence(review) {
49639
49730
  return review ? {
49640
49731
  passed: review.passed,
49641
- summary: bounded(review.summary),
49732
+ summary: bounded2(review.summary),
49642
49733
  findings: compact(review.findings),
49643
49734
  criteria: review.criteria?.slice(0, 20).map((item) => ({
49644
- criterion: bounded(item.criterion),
49735
+ criterion: bounded2(item.criterion),
49645
49736
  passed: item.passed,
49646
- evidence: bounded(item.evidence)
49737
+ evidence: bounded2(item.evidence)
49647
49738
  }))
49648
49739
  } : undefined;
49649
49740
  }
49650
49741
  function verificationEvidence(verification) {
49651
49742
  return verification ? {
49652
49743
  ok: verification.ok,
49653
- output: verification.output ? bounded(verification.output, 3000) : undefined,
49654
- fingerprint: bounded(verification.fingerprint, 128)
49744
+ output: verification.output ? bounded2(verification.output, 3000) : undefined,
49745
+ fingerprint: bounded2(verification.fingerprint, 128)
49655
49746
  } : undefined;
49656
49747
  }
49657
49748
  function advisoryEvidence(kind, advisory) {
49658
49749
  return advisory ? {
49659
49750
  kind: `upfront ${kind} advice; historical snapshot; not current approval`,
49660
- planKey: bounded(advisory.planKey, 128),
49751
+ planKey: bounded2(advisory.planKey, 128),
49661
49752
  reviewerId: advisory.reviewerId,
49662
- fingerprint: bounded(advisory.fingerprint, 128),
49753
+ fingerprint: bounded2(advisory.fingerprint, 128),
49663
49754
  review: reviewEvidence(advisory.review)
49664
49755
  } : undefined;
49665
49756
  }
@@ -49669,24 +49760,24 @@ function historicalEvidence(mission) {
49669
49760
  const verification = decision.evidence?.verification;
49670
49761
  const review = decision.evidence?.finalReview;
49671
49762
  if (verification)
49672
- lines.push(bounded(`${decision.round}차 판단 당시 검증: ${verification.ok ? "통과" : "실패"} / 파일 상태 ${bounded(verification.fingerprint, 128)}${verification.output ? ` / ${bounded(verification.output, 250)}` : ""}`));
49763
+ lines.push(bounded2(`${decision.round}차 판단 당시 검증: ${verification.ok ? "통과" : "실패"} / 파일 상태 ${bounded2(verification.fingerprint, 128)}${verification.output ? ` / ${bounded2(verification.output, 250)}` : ""}`));
49673
49764
  if (review) {
49674
- lines.push(bounded(`${decision.round}차 판단 당시 최종 리뷰: ${review.passed ? "통과" : "실패"} / ${bounded(review.summary, 350)}`));
49675
- lines.push(...compact(review.findings, 3).map((finding) => bounded(`${decision.round}차 판단 당시 지적: ${finding}`)));
49765
+ lines.push(bounded2(`${decision.round}차 판단 당시 최종 리뷰: ${review.passed ? "통과" : "실패"} / ${bounded2(review.summary, 350)}`));
49766
+ lines.push(...compact(review.findings, 3).map((finding) => bounded2(`${decision.round}차 판단 당시 지적: ${finding}`)));
49676
49767
  }
49677
49768
  return lines;
49678
49769
  });
49679
49770
  }
49680
49771
  function checkEvidence(mission) {
49681
49772
  return compact([
49682
- `운영자 검증: ${mission.verification ? mission.verification.ok ? "통과" : "실패" : "미실행"} / 명령: ${bounded(mission.supervision?.operatorVerifyCommand ?? mission.verifyCommand, 350)}`,
49773
+ `운영자 검증: ${mission.verification ? mission.verification.ok ? "통과" : "실패" : "미실행"} / 명령: ${bounded2(mission.supervision?.operatorVerifyCommand ?? mission.verifyCommand, 350)}`,
49683
49774
  ...mission.verification?.output ? [`검증 출력: ${mission.verification.output}`] : [],
49684
49775
  `Chief Director 최종 리뷰: ${mission.finalReview ? mission.finalReview.passed && mission.finalReview.findings.length === 0 ? "통과" : "실패" : "미실행"}${mission.finalReview ? ` / ${mission.finalReview.summary}` : ""}`,
49685
49776
  ...mission.tasks.map((task) => `${task.id}: 상태 ${task.status}, 시도 ${task.attempts}회, 독립 리뷰 ${task.review ? task.review.passed && task.review.findings.length === 0 ? "통과" : "실패" : "미실행"} / 리뷰어 ${task.reviewerId}`)
49686
49777
  ]);
49687
49778
  }
49688
49779
  function criterionEvidence(mission) {
49689
- return (mission.finalReview?.criteria ?? []).map((item) => bounded(`${bounded(item.criterion, 200)}: ${item.passed ? "통과" : "실패"} / 근거: ${bounded(item.evidence, 250)}`));
49780
+ return (mission.finalReview?.criteria ?? []).map((item) => bounded2(`${bounded2(item.criterion, 200)}: ${item.passed ? "통과" : "실패"} / 근거: ${bounded2(item.evidence, 250)}`));
49690
49781
  }
49691
49782
  function reportPrompt(mission) {
49692
49783
  const goal = mission.supervision?.operatorGoal ?? mission.goal;
@@ -49714,8 +49805,8 @@ function reportPrompt(mission) {
49714
49805
  '{"summary":"result","eli5":"plain-language result and user impact","goalAssessment":"criteria and actual verdict","assumptions":[],"decisions":[],"deliverables":[],"checks":[],"remaining":[]}',
49715
49806
  `Report evidence (JSON data):
49716
49807
  ${JSON.stringify({
49717
- operatorGoal: bounded(goal, 4000),
49718
- interpretation: mission.goalBrief ? bounded(mission.goalBrief.interpretation, 2000) : undefined,
49808
+ operatorGoal: bounded2(goal, 4000),
49809
+ interpretation: mission.goalBrief ? bounded2(mission.goalBrief.interpretation, 2000) : undefined,
49719
49810
  assumptions: compact(mission.goalBrief?.assumptions ?? []),
49720
49811
  successCriteria: compact(criteria(mission)),
49721
49812
  status: mission.status,
@@ -49724,9 +49815,9 @@ ${JSON.stringify({
49724
49815
  containerObservation: mission.containerObservation,
49725
49816
  verdict: incompleteReasons(mission).length === 0 ? "completed" : "incomplete",
49726
49817
  incompleteReasons: incompleteReasons(mission),
49727
- error: mission.error ? bounded(mission.error, 2000) : undefined,
49728
- workspace: bounded(mission.workspace.path, 2000),
49729
- branch: bounded(mission.workspace.branch),
49818
+ error: mission.error ? bounded2(mission.error, 2000) : undefined,
49819
+ workspace: bounded2(mission.workspace.path, 2000),
49820
+ branch: bounded2(mission.workspace.branch),
49730
49821
  chiefId: mission.chiefId,
49731
49822
  technicalLeadId: mission.technicalLeadId,
49732
49823
  technicalAdvice: advisoryEvidence("technical", mission.technicalReview),
@@ -49736,10 +49827,10 @@ ${JSON.stringify({
49736
49827
  marketingAdvice: advisoryEvidence("marketing", mission.marketingReview),
49737
49828
  actors: mission.personas.slice(0, 20).map((persona) => ({
49738
49829
  id: persona.id,
49739
- name: bounded(persona.name),
49740
- role: bounded(persona.role),
49741
- cli: bounded(persona.agentType),
49742
- model: persona.model ? bounded(persona.model) : "native default (actual model not recorded)",
49830
+ name: bounded2(persona.name),
49831
+ role: bounded2(persona.role),
49832
+ cli: bounded2(persona.agentType),
49833
+ model: persona.model ? bounded2(persona.model) : "native default (actual model not recorded)",
49743
49834
  selectedSkills: persona.skills.slice(0, 30)
49744
49835
  })),
49745
49836
  decisions: decisions.map((decision) => ({
@@ -49747,8 +49838,8 @@ ${JSON.stringify({
49747
49838
  action: decision.action,
49748
49839
  taskId: decision.taskId,
49749
49840
  actorId: decision.personaId,
49750
- reason: bounded(decision.reason),
49751
- instructions: decision.instructions ? bounded(decision.instructions) : undefined,
49841
+ reason: bounded2(decision.reason),
49842
+ instructions: decision.instructions ? bounded2(decision.instructions) : undefined,
49752
49843
  historicalEvidence: decision.evidence && snapshotDecisions.has(decision) ? {
49753
49844
  kind: "evidence at the recovery decision; not current approval",
49754
49845
  finalReview: reviewEvidence(decision.evidence.finalReview),
@@ -49757,25 +49848,25 @@ ${JSON.stringify({
49757
49848
  })),
49758
49849
  tasks: mission.tasks.slice(0, 12).map((state) => ({
49759
49850
  id: state.id,
49760
- title: bounded(mission.plan?.tasks.find((task) => task.id === state.id)?.title ?? state.id),
49851
+ title: bounded2(mission.plan?.tasks.find((task) => task.id === state.id)?.title ?? state.id),
49761
49852
  actorId: mission.plan?.tasks.find((task) => task.id === state.id)?.personaId,
49762
49853
  reviewerId: state.reviewerId,
49763
49854
  status: state.status,
49764
49855
  attempts: state.attempts,
49765
- actorClaim: state.output ? bounded(state.output, 3000) : undefined,
49856
+ actorClaim: state.output ? bounded2(state.output, 3000) : undefined,
49766
49857
  review: reviewEvidence(state.review)
49767
49858
  })),
49768
- verifyCommand: bounded(mission.supervision?.operatorVerifyCommand ?? mission.verifyCommand, 1200),
49859
+ verifyCommand: bounded2(mission.supervision?.operatorVerifyCommand ?? mission.verifyCommand, 1200),
49769
49860
  verification: verificationEvidence(mission.verification),
49770
49861
  goalVerification: mission.goalVerification,
49771
49862
  execution: mission.execution,
49772
49863
  executionPolicy: mission.executionPolicy,
49773
49864
  finalReview: reviewEvidence(mission.finalReview),
49774
49865
  history: mission.history.slice(-30).map((event) => ({
49775
- at: bounded(event.at),
49776
- stage: bounded(event.stage),
49866
+ at: bounded2(event.at),
49867
+ stage: bounded2(event.stage),
49777
49868
  taskId: event.taskId,
49778
- message: bounded(event.message)
49869
+ message: bounded2(event.message)
49779
49870
  }))
49780
49871
  })}`
49781
49872
  ].join(`
@@ -49797,10 +49888,10 @@ function parseReport(source) {
49797
49888
  function fallbackReport(mission) {
49798
49889
  const reasons = incompleteReasons(mission);
49799
49890
  const complete = reasons.length === 0;
49800
- const goal = bounded(mission.supervision?.operatorGoal ?? mission.goal, 350);
49891
+ const goal = bounded2(mission.supervision?.operatorGoal ?? mission.goal, 350);
49801
49892
  return {
49802
49893
  summary: complete ? "작업, 독립 리뷰, 운영자 검증과 Chief Director 최종 리뷰를 통과했습니다." : "목표는 미완료입니다. 확인된 미해결 조건과 보관된 실행 기록을 보고합니다.",
49803
- eli5: complete ? `“${goal}”을 위한 결과물을 만들고, 다른 담당자의 점검과 지정한 확인 절차를 마쳤습니다. 결과물은 아래 작업 경로에 보관되어 있어 직접 확인할 수 있습니다.` : `“${goal}”을 위해 작업했지만, 아직 모든 확인 절차를 통과한 결과는 아닙니다. ${bounded(reasons[0] ?? "완료 증거가 없습니다.", 350)} 이미 만든 작업은 보관되어 있습니다. Chief Director가 기록한 판단과 다음 관측·재개 시각, 아직 충족되지 않은 조건을 아래에서 보고합니다.`,
49894
+ eli5: complete ? `“${goal}”을 위한 결과물을 만들고, 다른 담당자의 점검과 지정한 확인 절차를 마쳤습니다. 결과물은 아래 작업 경로에 보관되어 있어 직접 확인할 수 있습니다.` : `“${goal}”을 위해 작업했지만, 아직 모든 확인 절차를 통과한 결과는 아닙니다. ${bounded2(reasons[0] ?? "완료 증거가 없습니다.", 350)} 이미 만든 작업은 보관되어 있습니다. Chief Director가 기록한 판단과 다음 관측·재개 시각, 아직 충족되지 않은 조건을 아래에서 보고합니다.`,
49804
49895
  goalAssessment: complete ? "기록된 성공 기준에 대해 작업별 독립 리뷰와 운영자 검증이 통과했고, Chief Director가 최종 승인했습니다." : "성공 기준 전체의 충족을 확인하지 못했습니다. 일부 작업이나 검증이 통과했더라도 목표 달성으로 표시하지 않습니다.",
49805
49896
  assumptions: compact(mission.goalBrief?.assumptions ?? []),
49806
49897
  decisions: decisionEvidence(mission),
@@ -49818,7 +49909,7 @@ function fallbackReport(mission) {
49818
49909
  };
49819
49910
  }
49820
49911
  function markdown(value, limit = 1200) {
49821
- return bounded(value, limit).replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/g, " ").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/[\\`*_{}[\]()#+.!|~-]/g, "\\$&");
49912
+ return bounded2(value, limit).replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/g, " ").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/[\\`*_{}[\]()#+.!|~-]/g, "\\$&");
49822
49913
  }
49823
49914
  function list(values) {
49824
49915
  return values.length ? compact(values).map((value) => `- ${markdown(value)}`).join(`
@@ -50333,12 +50424,12 @@ async function assertMissionWorkspace(mission) {
50333
50424
  }
50334
50425
 
50335
50426
  // ../../packages/core/src/chief/parallel-workspace.ts
50336
- var hash = (value) => createHash10("sha256").update(value).digest("hex");
50427
+ var hash2 = (value) => createHash10("sha256").update(value).digest("hex");
50337
50428
  var same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
50338
50429
  function identity3(missionId, taskId, attempt) {
50339
50430
  if (!taskId || taskId.length > 200 || !Number.isSafeInteger(attempt) || attempt < 1)
50340
50431
  throw new Error("Use a valid task ID and positive attempt number for each parallel Actor.");
50341
- return `${hash(missionId).slice(0, 16)}-${hash(taskId).slice(0, 16)}-${attempt}`;
50432
+ return `${hash2(missionId).slice(0, 16)}-${hash2(taskId).slice(0, 16)}-${attempt}`;
50342
50433
  }
50343
50434
  async function paths(source, missionId, taskId, attempt) {
50344
50435
  const root = await parallelRoot(source);
@@ -50482,7 +50573,7 @@ async function collectTaskDelivery(mission, workspace) {
50482
50573
  version: 1,
50483
50574
  workspace,
50484
50575
  patchPath: location.patch,
50485
- patchSha256: hash(patch),
50576
+ patchSha256: hash2(patch),
50486
50577
  deliveryTree: final.tree,
50487
50578
  changes,
50488
50579
  status: "prepared"
@@ -50498,7 +50589,7 @@ async function integrateTaskDelivery(mission, input) {
50498
50589
  if (delivery.workspace.missionId !== mission.id || !same(delivery.workspace, input.workspace) || delivery.patchSha256 !== input.patchSha256 || delivery.patchPath !== location.patch)
50499
50590
  throw new Error("Parallel delivery does not match its persisted task identity and patch. Inspect the saved artifacts before recovery.");
50500
50591
  const patch = await readFile4(location.patch);
50501
- if (hash(patch) !== delivery.patchSha256)
50592
+ if (hash2(patch) !== delivery.patchSha256)
50502
50593
  throw new Error("Parallel delivery patch changed. Restore its saved SHA256 before integration.");
50503
50594
  const current = await captureParallelBaseline(mission.workspace.path);
50504
50595
  const images = await parallelTreeImages(mission.workspace.path, current.tree);
@@ -66141,7 +66232,11 @@ ${details}` : ""}`);
66141
66232
  timeoutSec,
66142
66233
  maxRepairs,
66143
66234
  maxRounds,
66144
- operatingPolicy: chiefOperatingPolicy(chiefConfig, readyActors, !profile),
66235
+ operatingPolicy: chiefOperatingPolicy(chiefConfig, readyActors, !profile, project?.oma?.skill_compatibility ? {
66236
+ reportPath: resolve11(root, project.oma.skill_compatibility.report_path),
66237
+ maxAgeHours: project.oma.skill_compatibility.max_age_hours,
66238
+ mode: project.oma.skill_compatibility.mode
66239
+ } : undefined),
66145
66240
  capturePolicy: chiefCapturePolicy(chiefConfig),
66146
66241
  executionPolicy,
66147
66242
  metricSourcePolicy: chiefConfig.metric_sources,
@@ -66676,7 +66771,7 @@ class ContinuousOperationStore {
66676
66771
  }
66677
66772
 
66678
66773
  // src/chief-continuous.ts
66679
- import { execFile as execFile4 } from "node:child_process";
66774
+ import { execFile as execFile5 } from "node:child_process";
66680
66775
  import { randomUUID as randomUUID15 } from "node:crypto";
66681
66776
  import { mkdir as mkdir17, realpath as realpath19, rename as rename7, unlink as unlink7, writeFile as writeFile13 } from "node:fs/promises";
66682
66777
  import { join as join35, resolve as resolve19 } from "node:path";
@@ -68307,6 +68402,214 @@ function validateOmaEvidence(request, io = defaultIO2) {
68307
68402
  return { ok: true, runId: String(run.runId) };
68308
68403
  }
68309
68404
 
68405
+ // ../../packages/core/src/oma/skill-matrix-adapter.ts
68406
+ import { posix } from "node:path";
68407
+
68408
+ // ../../packages/core/src/oma/skill-matrix-io.ts
68409
+ init_skill_matrix_schema();
68410
+ import { execFile as execFile4 } from "node:child_process";
68411
+ import { constants as constants5 } from "node:fs";
68412
+ import { open as open7 } from "node:fs/promises";
68413
+ var MAX_BYTES = 4 * 1024 * 1024;
68414
+ async function command(binary, args, signal) {
68415
+ return new Promise((resolve, reject) => {
68416
+ execFile4(binary, args, {
68417
+ encoding: "utf8",
68418
+ timeout: 1e4,
68419
+ maxBuffer: MAX_BYTES,
68420
+ signal,
68421
+ ...binary === "oma" ? { env: { ...process.env, OMA_SKIP_VERSION_CHECK: "1" } } : {}
68422
+ }, (error, stdout) => {
68423
+ if (error)
68424
+ reject(new Error(`${binary} local diagnostic command failed. Check its installation/version and retry.`));
68425
+ else
68426
+ resolve(stdout);
68427
+ });
68428
+ });
68429
+ }
68430
+ async function readReport(path) {
68431
+ const file = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
68432
+ try {
68433
+ const info = await file.stat();
68434
+ if (!info.isFile() || info.size > MAX_BYTES)
68435
+ throw new Error("Matrix report must be a regular file at most 4 MiB.");
68436
+ const bytes = Buffer.alloc(info.size + 1);
68437
+ let length = 0;
68438
+ while (length < bytes.length) {
68439
+ const result = await file.read(bytes, length, bytes.length - length, length);
68440
+ if (!result.bytesRead)
68441
+ break;
68442
+ length += result.bytesRead;
68443
+ }
68444
+ if (length !== info.size)
68445
+ throw new Error("Matrix report changed during the read. Retry after its writer finishes.");
68446
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length)));
68447
+ } finally {
68448
+ await file.close();
68449
+ }
68450
+ }
68451
+ var defaultSkillMatrixDeps = {
68452
+ now: Date.now,
68453
+ platform: process.platform,
68454
+ arch: process.arch,
68455
+ readReport,
68456
+ plan: async (workspace, skills, signal) => JSON.parse(await command("oma", [
68457
+ "skills",
68458
+ "matrix",
68459
+ "--project-root",
68460
+ workspace,
68461
+ "--skills",
68462
+ skills.join(","),
68463
+ "--delivery",
68464
+ "injected",
68465
+ "--vendors",
68466
+ "claude,codex",
68467
+ "--json"
68468
+ ], signal)),
68469
+ cliVersion: async (vendor, signal) => {
68470
+ const version = (await command(vendor, ["--version"], signal)).trim();
68471
+ return /^[\w .()+/-]{1,160}$/.test(version) ? version : null;
68472
+ }
68473
+ };
68474
+ function parseSkillMatrix(value) {
68475
+ const parsed = matrixReportSchema.safeParse(value);
68476
+ if (!parsed.success)
68477
+ throw new Error("Matrix report uses an unsupported or malformed evidence contract. Regenerate it with the current OMA CLI.");
68478
+ const report = parsed.data;
68479
+ const unique = (values) => new Set(values).size === values.length;
68480
+ if (!unique(report.cases.map((entry) => entry.id)) || !unique(report.cells.map((entry) => `${entry.vendor}\x00${entry.caseId}`)) || report.bundle && (!unique(report.bundle.skills.map((entry) => entry.name)) || !unique(report.bundle.skills.map((entry) => entry.caseId))) || report.cells.some((cell) => !unique(cell.checks.map((check) => check.id))))
68481
+ throw new Error("Matrix report repeats a skill, case or vendor cell. Regenerate an unambiguous report.");
68482
+ return report;
68483
+ }
68484
+
68485
+ // ../../packages/core/src/oma/skill-matrix-adapter.ts
68486
+ var scope = "installed injected read-reference audit; not AV runtime or task-quality certification";
68487
+ var safeNames = /^[a-z0-9][a-z0-9-]{0,127}$/;
68488
+ var remediation = "Regenerate the installed injected matrix with explicit models, then set oma.skill_compatibility.report_path in av.yaml.";
68489
+ var matrixRouteKey = (route) => `${route.actorType}\x00${route.model ?? ""}`;
68490
+ function sourceProblem(report) {
68491
+ if (report.sourceKind !== "installed" || report.delivery !== "injected" || report.auditScope !== "read-reference" || !report.bundle)
68492
+ return "Evidence is not an installed, injected, read-reference audit. Synthetic probes cannot authorize installed skills.";
68493
+ return;
68494
+ }
68495
+ function sameCoverage(a, b) {
68496
+ const sorted = (items) => JSON.stringify([...items].sort());
68497
+ return a.hash === b.hash && a.caseId === b.caseId && sorted(a.requiredFiles) === sorted(b.requiredFiles) && sorted(a.missingFiles) === sorted(b.missingFiles) && JSON.stringify(a.excludedReferences) === JSON.stringify(b.excludedReferences);
68498
+ }
68499
+ function assessRoute(report, plan, skills, route, version) {
68500
+ const result = (status, reason) => ({
68501
+ ...route,
68502
+ status,
68503
+ reason
68504
+ });
68505
+ if (route.actorType !== "claude" && route.actorType !== "codex")
68506
+ return result("unknown", "This matrix protocol measures only Claude and Codex.");
68507
+ if (!route.model)
68508
+ return result("unknown", "The candidate uses an unpinned native model default; it cannot match a measured model.");
68509
+ if (!version)
68510
+ return result("unknown", "The current native CLI version could not be verified.");
68511
+ for (const name of skills) {
68512
+ const current = plan.bundle?.skills.find((entry) => entry.name === name);
68513
+ const saved = report.bundle?.skills.find((entry) => entry.name === name);
68514
+ if (!current || !saved)
68515
+ return result("unknown", `Skill ${name} has no complete installed audit coverage.`);
68516
+ if (!sameCoverage(current, saved))
68517
+ return result("unknown", `Skill ${name} content or reference coverage changed after the audit.`);
68518
+ if (current.missingFiles.length || current.excludedReferences.length || saved.missingFiles.length || saved.excludedReferences.length)
68519
+ return result("unknown", `Skill ${name} has missing or excluded references; complete its audit coverage first.`);
68520
+ if (!report.cases.some((entry) => entry.id === saved.caseId && entry.skill === name))
68521
+ return result("unknown", `Skill ${name} required case is absent from the measured selection.`);
68522
+ const cell = report.cells.find((entry) => entry.caseId === saved.caseId && entry.vendor === route.actorType);
68523
+ if (!cell || cell.skill !== name || cell.contentHash !== current.hash)
68524
+ return result("unknown", `Skill ${name} lacks a matching measured vendor cell.`);
68525
+ if (cell.cliVersion !== version)
68526
+ return result("unknown", `Skill ${name} was measured with a different or unknown native CLI version.`);
68527
+ if (cell.model !== route.model)
68528
+ return result("unknown", `Skill ${name} was measured with a different or unknown reported model.`);
68529
+ if (cell.status !== "pass" || !cell.checks.length || cell.checks.some((check) => check.status !== "pass"))
68530
+ return result(cell.status === "fail" ? "fail" : "unknown", `Skill ${name} audit is ${cell.status}; passing read-reference evidence is required.`);
68531
+ const entry = `${name}/SKILL.md`;
68532
+ if (!current.requiredFiles.includes(entry))
68533
+ return result("unknown", `Skill ${name} audit omits its installed SKILL.md.`);
68534
+ const required = [
68535
+ "process",
68536
+ "output",
68537
+ "content",
68538
+ "integrity",
68539
+ "reference-coverage",
68540
+ ...current.requiredFiles.filter((file) => file !== entry).map((file) => `reference:${posix.relative(name, file)}`)
68541
+ ];
68542
+ if (required.some((id) => !cell.checks.some((check) => check.id === id && check.status === "pass" && (id === "content" || id.startsWith("reference:") ? check.proof === "read" : true))))
68543
+ return result("unknown", `Skill ${name} audit lacks a mandatory successful read-reference check.`);
68544
+ }
68545
+ return result("pass", "All selected skills have current passing installed injected read-reference evidence.");
68546
+ }
68547
+ async function inspectSkillCompatibility(policy, workspace, selectedSkills, routes, deps = defaultSkillMatrixDeps, signal) {
68548
+ const unknown = (reason) => ({
68549
+ scope,
68550
+ routes: routes.map((route) => ({ ...route, status: "unknown", reason }))
68551
+ });
68552
+ if (selectedSkills?.length === 0)
68553
+ return {
68554
+ scope,
68555
+ routes: routes.map((route) => ({
68556
+ ...route,
68557
+ status: "pass",
68558
+ reason: "No skills selected; no compatibility claim is required."
68559
+ }))
68560
+ };
68561
+ try {
68562
+ if (signal?.aborted)
68563
+ throw new Error("Interrupted");
68564
+ const report = parseSkillMatrix(await deps.readReport(policy.reportPath));
68565
+ const source = sourceProblem(report);
68566
+ if (source)
68567
+ return unknown(source);
68568
+ if (report.mode !== "live" || report.status !== "completed")
68569
+ return unknown("The report is not a completed live measurement; a plan is unmeasured.");
68570
+ const age = deps.now() - Date.parse(report.createdAt);
68571
+ if (age < -60000 || age > policy.maxAgeHours * 3600000)
68572
+ return unknown("The cached matrix is stale or has a future timestamp.");
68573
+ if (report.host.platform !== deps.platform || report.host.arch !== deps.arch)
68574
+ return unknown("The matrix host platform or architecture differs from this runtime.");
68575
+ const skills = [...new Set(selectedSkills ?? report.bundle?.skills.map((entry) => entry.name) ?? [])];
68576
+ if (!skills.length || skills.length > 100 || skills.some((name) => !safeNames.test(name)))
68577
+ return unknown("Select a bounded list of valid installed skill names.");
68578
+ const plan = parseSkillMatrix(await deps.plan(workspace, skills, signal));
68579
+ if (sourceProblem(plan) || plan.mode !== "plan" || plan.status !== "planned")
68580
+ return unknown("OMA did not return a supported installed injected plan.");
68581
+ if (plan.omaVersion !== report.omaVersion)
68582
+ return unknown("The OMA version changed after measurement; refresh the cached matrix.");
68583
+ if (plan.host.platform !== deps.platform || plan.host.arch !== deps.arch)
68584
+ return unknown("The current OMA plan host does not match this runtime.");
68585
+ const versions = new Map;
68586
+ for (const route of routes) {
68587
+ if (versions.has(route.actorType) || route.actorType !== "claude" && route.actorType !== "codex")
68588
+ continue;
68589
+ try {
68590
+ versions.set(route.actorType, await deps.cliVersion(route.actorType, signal));
68591
+ } catch {
68592
+ versions.set(route.actorType, null);
68593
+ }
68594
+ }
68595
+ if (signal?.aborted)
68596
+ throw new Error("Interrupted");
68597
+ return {
68598
+ scope,
68599
+ routes: routes.map((route) => assessRoute(report, plan, skills, route, versions.get(route.actorType) ?? null))
68600
+ };
68601
+ } catch {
68602
+ if (signal?.aborted)
68603
+ throw new Error("Skill compatibility inspection interrupted before Actor launch.");
68604
+ return unknown(`The cached report or local OMA plan could not be validated. ${remediation}`);
68605
+ }
68606
+ }
68607
+ function assertCompatibleRoute(route, inspection) {
68608
+ const result = inspection.routes.find((entry) => matrixRouteKey(entry) === matrixRouteKey(route));
68609
+ if (result?.status !== "pass")
68610
+ throw new Error(`Skill compatibility required for ${route.actorType}/${route.model ?? "native default"}: ${result?.reason ?? "no matching evidence"} ${remediation}`);
68611
+ }
68612
+
68310
68613
  // ../../packages/core/src/orchestrator/agent-runner.ts
68311
68614
  init_logger();
68312
68615
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -68798,8 +69101,8 @@ async function withMissionDeadline(mission, parent, run) {
68798
69101
 
68799
69102
  // ../../packages/core/src/chief/skills.ts
68800
69103
  init_dist();
68801
- import { constants as constants5 } from "node:fs";
68802
- import { lstat as lstat11, mkdir as mkdir13, open as open7, readdir as readdir6, realpath as realpath14, stat, writeFile as writeFile10 } from "node:fs/promises";
69104
+ import { constants as constants6 } from "node:fs";
69105
+ import { lstat as lstat11, mkdir as mkdir13, open as open8, readdir as readdir6, realpath as realpath14, stat, writeFile as writeFile10 } from "node:fs/promises";
68803
69106
  import { isAbsolute as isAbsolute8, join as join26, relative as relative8, sep as sep10 } from "node:path";
68804
69107
  var MAX_SKILL_BYTES = 256000;
68805
69108
  var MAX_METADATA_BYTES = 16384;
@@ -68880,7 +69183,7 @@ async function prepareMissionSkills(sourceRepositoryPath, workspacePath) {
68880
69183
  const parent = await realpath14(targetDirectory);
68881
69184
  if (parent !== destinationRoot && !inside(destinationRoot, parent))
68882
69185
  throw new Error("A target skill resource escapes the mission worktree. Fix its symlink before creating this order.");
68883
- const file = await open7(from, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
69186
+ const file = await open8(from, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
68884
69187
  try {
68885
69188
  const current = await file.stat();
68886
69189
  if (!current.isFile())
@@ -68941,7 +69244,7 @@ async function skillPath(root, name) {
68941
69244
  return path;
68942
69245
  }
68943
69246
  async function readSkill(path, metadataOnly) {
68944
- const file = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
69247
+ const file = await open8(path, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
68945
69248
  try {
68946
69249
  const info = await file.stat();
68947
69250
  if (!info.isFile())
@@ -69169,11 +69472,13 @@ class ChiefRuntime {
69169
69472
  store;
69170
69473
  signal;
69171
69474
  onStage;
69475
+ skillMatrixDeps;
69172
69476
  runners = new Set;
69173
- constructor(store, signal, onStage = () => {}) {
69477
+ constructor(store, signal, onStage = () => {}, skillMatrixDeps) {
69174
69478
  this.store = store;
69175
69479
  this.signal = signal;
69176
69480
  this.onStage = onStage;
69481
+ this.skillMatrixDeps = skillMatrixDeps;
69177
69482
  }
69178
69483
  ports() {
69179
69484
  return {
@@ -69249,8 +69554,22 @@ class ChiefRuntime {
69249
69554
  };
69250
69555
  if (signal?.aborted)
69251
69556
  throw new Error("Order interrupted. Resume it with av order --resume followed by its mission ID.");
69252
- const route = stage === "work" ? selectWorkActor(mission, persona, context?.taskId) : undefined;
69557
+ const compatibilityPolicy = mission.operatingPolicy?.skillCompatibility;
69558
+ const compatibility = stage === "work" && compatibilityPolicy ? await inspectSkillCompatibility(compatibilityPolicy, workspace.path, persona.skills, workActorCandidates(mission, persona, compatibilityPolicy.mode === "require"), this.skillMatrixDeps, signal) : undefined;
69559
+ const compatibleRoutes = compatibilityPolicy?.mode === "require" && compatibility ? new Set(compatibility.routes.filter((entry) => entry.status === "pass").map(matrixRouteKey)) : undefined;
69560
+ if (compatibility && compatibilityPolicy)
69561
+ mission.history.push({
69562
+ at: new Date().toISOString(),
69563
+ stage: "skill-compatibility",
69564
+ message: `${compatibilityPolicy.mode}: ${compatibility.routes.map((entry) => `${entry.actorType}/${entry.model ?? "native default"} ${entry.status}: ${entry.reason}`).join("; ")} Scope: ${compatibility.scope}.`,
69565
+ ...context?.taskId ? { taskId: context.taskId } : {}
69566
+ });
69567
+ const route = stage === "work" ? selectWorkActor(mission, persona, context?.taskId, compatibleRoutes) : undefined;
69253
69568
  persona = route?.actor ?? persona;
69569
+ if (compatibility && compatibilityPolicy) {
69570
+ if (compatibilityPolicy.mode === "require")
69571
+ assertCompatibleRoute({ actorType: persona.agentType, model: persona.model }, compatibility);
69572
+ }
69254
69573
  this.onStage(stage, persona);
69255
69574
  const attempt = {
69256
69575
  id: randomUUID11(),
@@ -69852,19 +70171,19 @@ init_report_delivery_contract();
69852
70171
  init_zod();
69853
70172
  init_report_delivery_contract();
69854
70173
  import { createHash as createHash16, randomUUID as randomUUID12 } from "node:crypto";
69855
- import { constants as constants6 } from "node:fs";
69856
- import { mkdir as mkdir14, open as open8, readdir as readdir7, readFile as readFile11, realpath as realpath15, rename as rename6, unlink as unlink5, writeFile as writeFile11 } from "node:fs/promises";
70174
+ import { constants as constants7 } from "node:fs";
70175
+ import { mkdir as mkdir14, open as open9, readdir as readdir7, readFile as readFile11, realpath as realpath15, rename as rename6, unlink as unlink5, writeFile as writeFile11 } from "node:fs/promises";
69857
70176
  import { isAbsolute as isAbsolute9, join as join28, relative as relative9, resolve as resolve16 } from "node:path";
69858
- var hash2 = string2().regex(/^[a-f0-9]{64}$/);
70177
+ var hash3 = string2().regex(/^[a-f0-9]{64}$/);
69859
70178
  var outboxRecordSchema = strictObject({
69860
70179
  version: literal(1),
69861
- id: hash2,
70180
+ id: hash3,
69862
70181
  missionId: string2().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/),
69863
70182
  missionStatus: _enum(["completed", "failed"]),
69864
70183
  destinationId: string2().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/),
69865
70184
  channel: string2().regex(/^[a-z][a-z0-9-]{0,63}$/),
69866
- reportHash: hash2,
69867
- destinationHash: hash2.optional(),
70185
+ reportHash: hash3,
70186
+ destinationHash: hash3.optional(),
69868
70187
  goal: string2().max(32000),
69869
70188
  markdown: string2().max(256000),
69870
70189
  reportPath: string2().max(4096),
@@ -69899,7 +70218,7 @@ class ReportOutbox {
69899
70218
  this.directory = join28(stateDir, "report-outbox");
69900
70219
  }
69901
70220
  path(id) {
69902
- if (!hash2.safeParse(id).success)
70221
+ if (!hash3.safeParse(id).success)
69903
70222
  throw new Error("Invalid report receipt ID. Use av reports to find a receipt.");
69904
70223
  return join28(this.directory, `${id}.json`);
69905
70224
  }
@@ -69962,7 +70281,7 @@ async function verifiedAttachment(attachment, artifactRoot, readBytes) {
69962
70281
  throw failure;
69963
70282
  if (resolve16(attachment.path) !== attachment.path)
69964
70283
  throw failure;
69965
- const file = await open8(attachment.path, constants6.O_RDONLY | constants6.O_NOFOLLOW);
70284
+ const file = await open9(attachment.path, constants7.O_RDONLY | constants7.O_NOFOLLOW);
69966
70285
  try {
69967
70286
  const stat = await file.stat();
69968
70287
  if (!stat.isFile() || stat.size !== attachment.sizeBytes)
@@ -70497,8 +70816,8 @@ Return one valid JSON object under the original charter and pinned evidence.` :
70497
70816
 
70498
70817
  // ../../packages/core/src/chief/metric-source-adapters.ts
70499
70818
  init_metric_source_policy();
70500
- import { constants as constants7 } from "node:fs";
70501
- import { lstat as lstat12, open as open9, realpath as realpath16 } from "node:fs/promises";
70819
+ import { constants as constants8 } from "node:fs";
70820
+ import { lstat as lstat12, open as open10, realpath as realpath16 } from "node:fs/promises";
70502
70821
  import { isAbsolute as isAbsolute10, relative as relative10, resolve as resolve17, sep as sep11 } from "node:path";
70503
70822
  var MAX_METRIC_RESPONSE_BYTES = 2000000;
70504
70823
  function metricSourceEnvironment(request, name) {
@@ -70596,7 +70915,7 @@ var jsonFileMetricAdapter = {
70596
70915
  if ((await lstat12(current)).isSymbolicLink())
70597
70916
  throw new MetricSourceError("Metric file and parent directories must not be symlinks. Choose a regular JSON file inside the source repository.");
70598
70917
  }
70599
- const file = await open9(path, constants7.O_RDONLY | constants7.O_NOFOLLOW);
70918
+ const file = await open10(path, constants8.O_RDONLY | constants8.O_NOFOLLOW);
70600
70919
  try {
70601
70920
  const stat = await file.stat();
70602
70921
  if (!stat.isFile() || stat.size > MAX_METRIC_RESPONSE_BYTES || await realpath16(path) !== path)
@@ -70997,8 +71316,8 @@ function createMissionMetricPorts(repository, mission, dependencies = {}) {
70997
71316
 
70998
71317
  // src/chief-tool-config.ts
70999
71318
  import { randomUUID as randomUUID14 } from "node:crypto";
71000
- import { constants as constants8 } from "node:fs";
71001
- import { link as link2, lstat as lstat13, mkdir as mkdir16, open as open10, realpath as realpath17, unlink as unlink6 } from "node:fs/promises";
71319
+ import { constants as constants9 } from "node:fs";
71320
+ import { link as link2, lstat as lstat13, mkdir as mkdir16, open as open11, realpath as realpath17, unlink as unlink6 } from "node:fs/promises";
71002
71321
  import { join as join32 } from "node:path";
71003
71322
  init_dist2();
71004
71323
  init_client_toml();
@@ -71032,7 +71351,7 @@ async function assertLocalPath(root, local) {
71032
71351
  return true;
71033
71352
  }
71034
71353
  async function readPrivateFile(path) {
71035
- const file = await open10(path, constants8.O_RDONLY | constants8.O_NOFOLLOW);
71354
+ const file = await open11(path, constants9.O_RDONLY | constants9.O_NOFOLLOW);
71036
71355
  try {
71037
71356
  const stats = await file.stat();
71038
71357
  if (!stats.isFile() || stats.size > maximumBytes)
@@ -71095,7 +71414,7 @@ async function privateExclude(original, target, paths) {
71095
71414
  const patterns = paths.flatMap((local) => [`/${local}`, `/${local}.av-mcp-*.tmp`]).filter((pattern) => !lines.has(pattern));
71096
71415
  if (!patterns.length)
71097
71416
  return;
71098
- const file = await open10(path, constants8.O_WRONLY | constants8.O_APPEND | constants8.O_CREAT | constants8.O_NOFOLLOW, 384);
71417
+ const file = await open11(path, constants9.O_WRONLY | constants9.O_APPEND | constants9.O_CREAT | constants9.O_NOFOLLOW, 384);
71099
71418
  try {
71100
71419
  await file.writeFile(`${content && !content.endsWith(`
71101
71420
  `) ? `
@@ -71140,7 +71459,7 @@ async function prepareToolConfig(originalRepository, targetWorkspace) {
71140
71459
  continue;
71141
71460
  const path = join32(target, config.path);
71142
71461
  const temporary = `${path}.av-mcp-${randomUUID14()}.tmp`;
71143
- const file = await open10(temporary, "wx", 384);
71462
+ const file = await open11(temporary, "wx", 384);
71144
71463
  try {
71145
71464
  await file.writeFile(config.content);
71146
71465
  } finally {
@@ -71169,7 +71488,7 @@ async function prepareChiefToolConfig(originalRepository, targetWorkspace) {
71169
71488
 
71170
71489
  // src/tool-discovery.ts
71171
71490
  init_client_toml();
71172
- import { constants as constants9 } from "node:fs";
71491
+ import { constants as constants10 } from "node:fs";
71173
71492
  import { access, lstat as lstat14, readFile as readFile12, realpath as realpath18 } from "node:fs/promises";
71174
71493
  import { homedir as homedir6 } from "node:os";
71175
71494
  import { delimiter as delimiter2, isAbsolute as isAbsolute11, join as join33, resolve as resolve18 } from "node:path";
@@ -71191,7 +71510,7 @@ async function executable(name, env) {
71191
71510
  for (const directory of (env.PATH ?? "").split(delimiter2).filter(isAbsolute11)) {
71192
71511
  try {
71193
71512
  const path = join33(directory, name);
71194
- await access(path, constants9.X_OK);
71513
+ await access(path, constants10.X_OK);
71195
71514
  if ((await lstat14(await realpath18(path))).isFile())
71196
71515
  return true;
71197
71516
  } catch {}
@@ -71446,7 +71765,7 @@ async function decideContinuousGoal(operation, decisionId, root, dependencies =
71446
71765
  }
71447
71766
 
71448
71767
  // src/chief-continuous.ts
71449
- var execute = promisify2(execFile4);
71768
+ var execute = promisify2(execFile5);
71450
71769
  var orderKeys = [
71451
71770
  "workspace",
71452
71771
  "verify",
@@ -73135,7 +73454,7 @@ function selectTaskReviewer(mission, task, state) {
73135
73454
  }
73136
73455
 
73137
73456
  // ../../packages/core/src/chief/coordinator.ts
73138
- var bounded2 = (value) => value.slice(-32000);
73457
+ var bounded3 = (value) => value.slice(-32000);
73139
73458
  async function coordinate(mission, ports) {
73140
73459
  Object.assign(mission, validateMission(mission));
73141
73460
  const operatorGoal = mission.goal;
@@ -73199,7 +73518,7 @@ async function coordinate(mission, ports) {
73199
73518
  if (task.effectScope === "external" && state.effectState === "unknown")
73200
73519
  throw new MissionPause(`External effect ${task.id} has no established destination outcome and will not be repeated.`, "unknown-effect");
73201
73520
  if (task.effectScope !== "external" || state.effectState !== "completed") {
73202
- state.output = bounded2(await run(persona(task.personaId), workPrompt(mission, task, state, persona(task.personaId)), "work", task.id));
73521
+ state.output = bounded3(await run(persona(task.personaId), workPrompt(mission, task, state, persona(task.personaId)), "work", task.id));
73203
73522
  if (task.effectScope === "external")
73204
73523
  state.effectState = "completed";
73205
73524
  }
@@ -73373,7 +73692,7 @@ async function coordinate(mission, ports) {
73373
73692
  mission.fingerprint = await ports.fingerprint(mission);
73374
73693
  mission.verification = {
73375
73694
  ok: result.ok,
73376
- output: result.output ? bounded2(result.output) : undefined,
73695
+ output: result.output ? bounded3(result.output) : undefined,
73377
73696
  fingerprint: mission.fingerprint
73378
73697
  };
73379
73698
  if (mission.verificationMode === "chief" && !mission.goalVerification?.ok) {
@@ -73472,7 +73791,7 @@ async function coordinate(mission, ports) {
73472
73791
  return mission;
73473
73792
  }
73474
73793
  mission.status = "failed";
73475
- mission.error = bounded2(error instanceof Error ? error.message : String(error));
73794
+ mission.error = bounded3(error instanceof Error ? error.message : String(error));
73476
73795
  if (error instanceof ReadOnlyViolation || error instanceof ContractViolation || error instanceof CheckpointError)
73477
73796
  executionState(mission).failureKind = "integrity";
73478
73797
  if (error instanceof ReadOnlyViolation)
@@ -73489,7 +73808,7 @@ async function coordinate(mission, ports) {
73489
73808
  if (reportError instanceof ReadOnlyViolation) {
73490
73809
  executionState(mission).failureKind = "integrity";
73491
73810
  invalidate();
73492
- mission.error = bounded2(`${mission.error}
73811
+ mission.error = bounded3(`${mission.error}
73493
73812
  ${reportError.message}`);
73494
73813
  mission.report = fallbackReport(mission);
73495
73814
  }
@@ -74623,6 +74942,37 @@ function checkOma(project, deps) {
74623
74942
  return results;
74624
74943
  }
74625
74944
 
74945
+ // src/doctor-skill-matrix-checks.ts
74946
+ import { resolve as resolve26 } from "node:path";
74947
+ async function checkSkillMatrix(project, global2, deps) {
74948
+ const config = project?.oma?.skill_compatibility;
74949
+ if (!config)
74950
+ return [];
74951
+ const projectActor = project?.actor ?? project?.agent;
74952
+ const globalActor = global2?.actor ?? global2?.agent;
74953
+ const actorType = projectActor?.type ?? globalActor?.type ?? "claude";
74954
+ const model = projectActor?.model ?? (globalActor?.type === actorType ? globalActor.model : undefined);
74955
+ const inspection = await inspectSkillCompatibility({
74956
+ reportPath: resolve26(deps.cwd, config.report_path),
74957
+ maxAgeHours: config.max_age_hours,
74958
+ mode: config.mode
74959
+ }, resolve26(deps.cwd, project?.workspace?.root ?? "."), undefined, [{ actorType, model }], deps.skillMatrix);
74960
+ const route = inspection.routes[0];
74961
+ const status = route?.status ?? "unknown";
74962
+ return [
74963
+ {
74964
+ id: "oma.skill-compatibility",
74965
+ name: "OMA skill read-reference compatibility",
74966
+ status,
74967
+ message: `${config.mode}: ${actorType}/${model ?? "native default"}: ${route?.reason ?? "Evidence unavailable."} Scope: ${inspection.scope}. Work dispatch rechecks its own selected skills and candidates.`,
74968
+ ...status !== "pass" ? {
74969
+ fix: "Generate a completed installed injected matrix with explicit models and set oma.skill_compatibility.report_path in av.yaml. av doctor never runs a live matrix."
74970
+ } : {},
74971
+ critical: config.mode === "require"
74972
+ }
74973
+ ];
74974
+ }
74975
+
74626
74976
  // src/doctor-config-checks.ts
74627
74977
  function checkTunnel(project, deps) {
74628
74978
  const provider = project?.tunnel?.provider ?? "ngrok";
@@ -74877,10 +75227,11 @@ async function runDoctorChecks(deps, options = {}) {
74877
75227
  results.push(checkTunnel(project, deps));
74878
75228
  results.push(checkWebhookSecret(project));
74879
75229
  results.push(...checkOma(project, deps));
75230
+ results.push(...await checkSkillMatrix(project, global2, deps));
74880
75231
  return results;
74881
75232
  }
74882
75233
  function computeExitCode(results) {
74883
- return results.some((r) => r.critical && r.status === "fail") ? 1 : 0;
75234
+ return results.some((r) => r.critical && (r.status === "fail" || r.status === "unknown")) ? 1 : 0;
74884
75235
  }
74885
75236
  function summarize(results) {
74886
75237
  return { passed: results.filter((r) => r.status === "pass").length, total: results.length };
@@ -74955,7 +75306,7 @@ async function registerLinearWebhook(root, tunnelUrl) {
74955
75306
  init_yaml_loader();
74956
75307
  import { existsSync as existsSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "node:fs";
74957
75308
  import { createServer as createServer3 } from "node:net";
74958
- import { resolve as resolve26 } from "node:path";
75309
+ import { resolve as resolve27 } from "node:path";
74959
75310
  import { setTimeout as delay } from "node:timers/promises";
74960
75311
 
74961
75312
  // src/webhook-proxy.ts
@@ -75070,12 +75421,12 @@ function resolveServerPort(root, runningPort) {
75070
75421
  return String(port);
75071
75422
  }
75072
75423
  function resolveRuntimePaths(root, cliDir) {
75073
- const dashboardCwd = resolve26(root, "apps/dashboard");
75074
- if (!existsSync5(resolve26(dashboardCwd, "package.json"))) {
75424
+ const dashboardCwd = resolve27(root, "apps/dashboard");
75425
+ if (!existsSync5(resolve27(dashboardCwd, "package.json"))) {
75075
75426
  throw new Error(`Dashboard source is missing at ${dashboardCwd}. Clone https://github.com/first-fluke/agent-valley, run bun install, then run bun av setup and bun av dev from that checkout. The standalone CLI does not include the dashboard.`);
75076
75427
  }
75077
- const bundled = resolve26(cliDir, "supervisor.js");
75078
- const supervisorScript = existsSync5(bundled) ? bundled : resolve26(cliDir, "supervisor.ts");
75428
+ const bundled = resolve27(cliDir, "supervisor.js");
75429
+ const supervisorScript = existsSync5(bundled) ? bundled : resolve27(cliDir, "supervisor.ts");
75079
75430
  if (!existsSync5(supervisorScript)) {
75080
75431
  throw new Error(`Supervisor is missing at ${supervisorScript}. Run bun av up from an intact Agent Valley checkout.`);
75081
75432
  }
@@ -75354,8 +75705,8 @@ function spawnTunnel(config, input) {
75354
75705
 
75355
75706
  // src/index.ts
75356
75707
  var ROOT = process.cwd();
75357
- var PID_FILE = resolve30(ROOT, ".av.pid");
75358
- var LOG_FILE = resolve30(ROOT, ".av.log");
75708
+ var PID_FILE = resolve31(ROOT, ".av.pid");
75709
+ var LOG_FILE = resolve31(ROOT, ".av.log");
75359
75710
  function ensureConfig() {
75360
75711
  if (!existsSync10(resolveProjectConfigPath(ROOT))) {
75361
75712
  console.log(import_picocolors15.default.red("No av.yaml found. Run `av setup` first."));