@m8t-stack/cli 0.2.152 → 0.2.154

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/cli.js CHANGED
@@ -1464,7 +1464,7 @@ function installFoundryDnsShim() {
1464
1464
  }
1465
1465
 
1466
1466
  // src/lib/package-version.ts
1467
- var CLI_VERSION = "0.2.152";
1467
+ var CLI_VERSION = "0.2.154";
1468
1468
 
1469
1469
  // src/lib/render-error.ts
1470
1470
  init_errors();
@@ -16106,17 +16106,35 @@ function hydrateAgentContent(content, destRoot, persona) {
16106
16106
  fs11.rmSync(stage, { recursive: true, force: true });
16107
16107
  }
16108
16108
  }
16109
- function swapInto(from, dest) {
16110
- if (!fs11.existsSync(from)) {
16111
- fs11.rmSync(dest, { recursive: true, force: true });
16109
+ function moveAside(source, backup, ops) {
16110
+ try {
16111
+ ops.renameSync(source, backup);
16112
+ } catch (e) {
16113
+ const err = e;
16114
+ if (err.code !== "EXDEV") throw e;
16115
+ ops.cpSync(source, backup, { recursive: true });
16116
+ ops.rmSync(source, { recursive: true, force: true });
16117
+ }
16118
+ }
16119
+ function swapInto(from, dest, ops = fs11) {
16120
+ if (!ops.existsSync(from)) {
16121
+ ops.rmSync(dest, { recursive: true, force: true });
16112
16122
  return;
16113
16123
  }
16114
- fs11.mkdirSync(path11.dirname(dest), { recursive: true });
16124
+ ops.mkdirSync(path11.dirname(dest), { recursive: true });
16115
16125
  const old = `${dest}.old-${process.pid.toString()}`;
16116
- fs11.rmSync(old, { recursive: true, force: true });
16117
- if (fs11.existsSync(dest)) fs11.renameSync(dest, old);
16118
- fs11.renameSync(from, dest);
16119
- fs11.rmSync(old, { recursive: true, force: true });
16126
+ ops.rmSync(old, { recursive: true, force: true });
16127
+ const hadDest = ops.existsSync(dest);
16128
+ if (hadDest) moveAside(dest, old, ops);
16129
+ try {
16130
+ ops.renameSync(from, dest);
16131
+ } catch (e) {
16132
+ if (hadDest && !ops.existsSync(dest) && ops.existsSync(old)) {
16133
+ ops.renameSync(old, dest);
16134
+ }
16135
+ throw e;
16136
+ }
16137
+ ops.rmSync(old, { recursive: true, force: true });
16120
16138
  }
16121
16139
  async function resolveToken(deps) {
16122
16140
  const env = deps.env ?? process.env;
@@ -17951,6 +17969,42 @@ function personaFoundryTools(persona, frontmatter2) {
17951
17969
  });
17952
17970
  return rawTools;
17953
17971
  }
17972
+ function equalJson(a, b) {
17973
+ if (Object.is(a, b)) return true;
17974
+ if (typeof a !== typeof b || a === null || b === null) return false;
17975
+ if (Array.isArray(a) || Array.isArray(b)) {
17976
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((entry, i) => equalJson(entry, b[i]));
17977
+ }
17978
+ if (typeof a === "object" && typeof b === "object") {
17979
+ const ar = a;
17980
+ const br = b;
17981
+ const keys = Object.keys(ar);
17982
+ return keys.length === Object.keys(br).length && keys.every((key2) => Object.prototype.hasOwnProperty.call(br, key2) && equalJson(ar[key2], br[key2]));
17983
+ }
17984
+ return false;
17985
+ }
17986
+ function normalizeFoundryEchoes(value) {
17987
+ if (Array.isArray(value)) return value.map(normalizeFoundryEchoes);
17988
+ if (!value || typeof value !== "object") return value;
17989
+ const source = value;
17990
+ const normalized = Object.fromEntries(
17991
+ Object.entries(source).map(([key2, entry]) => [key2, normalizeFoundryEchoes(entry)])
17992
+ );
17993
+ if (source.type === "function" && source.strict === null) delete normalized.strict;
17994
+ if (source.type === "mcp") {
17995
+ const allowed = source.allowed_tools;
17996
+ if (allowed && typeof allowed === "object" && !Array.isArray(allowed)) {
17997
+ const names = allowed.tool_names;
17998
+ if (Array.isArray(names) && Object.keys(allowed).length === 1) {
17999
+ normalized.allowed_tools = names.map(normalizeFoundryEchoes);
18000
+ }
18001
+ }
18002
+ }
18003
+ return normalized;
18004
+ }
18005
+ function equivalentToolDefinition(a, b) {
18006
+ return equalJson(normalizeFoundryEchoes(a), normalizeFoundryEchoes(b));
18007
+ }
17954
18008
 
17955
18009
  // src/lib/deploy-prompt-advisor.ts
17956
18010
  init_esm();
@@ -19546,11 +19600,11 @@ function fromBuild(raw, fallbackRef) {
19546
19600
  };
19547
19601
  }
19548
19602
  var DEV_TAG = "v0.0.0-dev";
19549
- var PINNED_INSTALLER = fromBuild('{"ref":"ghcr.io/m8t-labs/m8t-installer:v0.1.116","registry":"ghcr.io/m8t-labs","image":"m8t-installer","tag":"v0.1.116"}', "ghcr.io/m8t-labs/m8t-installer");
19603
+ var PINNED_INSTALLER = fromBuild('{"ref":"ghcr.io/m8t-labs/m8t-installer:v0.1.124","registry":"ghcr.io/m8t-labs","image":"m8t-installer","tag":"v0.1.124"}', "ghcr.io/m8t-labs/m8t-installer");
19550
19604
  var PINNED_CODING_AGENT = fromBuild('{"ref":"ghcr.io/m8t-labs/m8t-coding-agent:v0.1.5","registry":"ghcr.io/m8t-labs","image":"m8t-coding-agent","tag":"v0.1.5"}', "ghcr.io/m8t-labs/m8t-coding-agent");
19551
19605
  var PINNED_AZURE_EXECUTOR = fromBuild('{"ref":"ghcr.io/m8t-labs/m8t-azure-executor:v0.1.5","registry":"ghcr.io/m8t-labs","image":"m8t-azure-executor","tag":"v0.1.5"}', "ghcr.io/m8t-labs/m8t-azure-executor");
19552
19606
  var PINNED_PLATFORM_VERSION = (() => {
19553
- const raw = "0.8.7";
19607
+ const raw = "0.8.9";
19554
19608
  return /^\d+\.\d+\.\d+$/.test(raw) ? raw : "";
19555
19609
  })();
19556
19610
 
@@ -21865,6 +21919,16 @@ function adoptedAcrTopologyError(live, adoption) {
21865
21919
  }
21866
21920
  return null;
21867
21921
  }
21922
+ async function assertLiveAdoptedGatewayTopology(args) {
21923
+ const live = await readLiveGatewayDeployment(args.gatewayResourceId);
21924
+ const topologyError = adoptedAcrTopologyError(live, args.adoption);
21925
+ if (topologyError) {
21926
+ throw new LocalCliError({
21927
+ code: "PLATFORM_GATEWAY_ADOPTION_DRIFT",
21928
+ message: `Adopted gateway held before infra converge: ${topologyError}.`
21929
+ });
21930
+ }
21931
+ }
21868
21932
  async function readAcrDigest(subscriptionId, acrName, image) {
21869
21933
  try {
21870
21934
  const value = (await runAz(["acr", "repository", "show", "--subscription", subscriptionId, "-n", acrName, "--image", image, "--query", "digest", "-o", "tsv"])).trim();
@@ -22551,22 +22615,41 @@ async function applyAgentImage(a, ctx, opts) {
22551
22615
  const digest = a.component === "codingAgent" ? ctx.manifest.components.codingAgent.digest : ctx.manifest.components.azureExecutor.digest;
22552
22616
  return { tag: a.to, digest };
22553
22617
  }
22554
- function declaredFillableFields(personaPath) {
22618
+ function readPersonaFrontmatter(personaPath) {
22555
22619
  let raw;
22556
22620
  try {
22557
22621
  raw = fs26.readFileSync(personaPath, "utf8");
22558
22622
  } catch {
22559
- return [];
22623
+ return null;
22560
22624
  }
22561
22625
  const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
22562
- if (!fmMatch) return [];
22626
+ if (!fmMatch) return null;
22563
22627
  try {
22564
- const fm = parseYaml11(fmMatch[1]);
22565
- return fm["fillable-fields"] ?? [];
22628
+ return parseYaml11(fmMatch[1]) ?? {};
22566
22629
  } catch {
22567
- return [];
22630
+ return null;
22568
22631
  }
22569
22632
  }
22633
+ function isRuntimePersonaTool(tool) {
22634
+ if (tool.type !== "mcp") return false;
22635
+ const label = tool.server_label;
22636
+ return label === "brain" || label === "a2a";
22637
+ }
22638
+ function convergePersonaTools(currentTools, declaredTools) {
22639
+ const personaTools = (declaredTools ?? []).map((tool) => tool).filter((tool) => !isRuntimePersonaTool(tool));
22640
+ const runtimeTools = (currentTools ?? []).filter(isRuntimePersonaTool);
22641
+ return [...personaTools, ...runtimeTools];
22642
+ }
22643
+ function personaPostCreateMismatches(candidate2, actual, expectedVersion) {
22644
+ const mismatches = [];
22645
+ if (actual.version !== expectedVersion) mismatches.push("version");
22646
+ const { tools: expectedTools, ...expectedDefinition } = candidate2.definition;
22647
+ const { tools: actualTools, ...actualDefinition } = actual.definition;
22648
+ if (!equivalentToolDefinition(actualDefinition, expectedDefinition)) mismatches.push("definition");
22649
+ if (!equivalentToolDefinition(actualTools ?? [], expectedTools ?? [])) mismatches.push("tools");
22650
+ if (!equivalentToolDefinition(actual.metadata ?? {}, candidate2.metadata)) mismatches.push("metadata");
22651
+ return mismatches;
22652
+ }
22570
22653
  function defaultsOf(declared) {
22571
22654
  const out = {};
22572
22655
  for (const f of declared) {
@@ -22600,7 +22683,12 @@ async function applyPersona(a, ctx, opts) {
22600
22683
  const personaPath = path27.join(opts.personaDir, "persona.md");
22601
22684
  const yamlValues = readAgentYaml(opts.agentName)?.fillableFieldValues ?? null;
22602
22685
  const values = resolveFillableValues(current, yamlValues);
22603
- const declared = declaredFillableFields(personaPath);
22686
+ const frontmatter2 = readPersonaFrontmatter(personaPath);
22687
+ const declared = frontmatter2?.["fillable-fields"] ?? [];
22688
+ const tools = convergePersonaTools(
22689
+ current.definition.tools,
22690
+ personaFoundryTools(a.personaName ?? opts.agentName, frontmatter2 ?? {})
22691
+ );
22604
22692
  if (declared.length > 0 && values === null) {
22605
22693
  const defaultBody = injectAndRender(personaPath, defaultsOf(declared), opts.agentName);
22606
22694
  const candidateInstructions = composeInstructions({
@@ -22639,7 +22727,9 @@ async function applyPersona(a, ctx, opts) {
22639
22727
  productVisibility: productVisibilityFor(opts.agentName),
22640
22728
  ...values ? { fillableFieldValues: JSON.stringify(values) } : {}
22641
22729
  };
22642
- const candidate2 = { definition: { ...current.definition, instructions }, metadata };
22730
+ if (frontmatter2?.version === void 0) delete metadata.personaVersion;
22731
+ else metadata.personaVersion = String(frontmatter2.version);
22732
+ const candidate2 = { definition: { ...current.definition, instructions, tools }, metadata };
22643
22733
  if (isNoop(candidate2, current)) return "noop";
22644
22734
  const foundryVersion = await createAgentVersion({
22645
22735
  credential: ctx.credential,
@@ -22648,6 +22738,19 @@ async function applyPersona(a, ctx, opts) {
22648
22738
  definition: candidate2.definition,
22649
22739
  metadata
22650
22740
  });
22741
+ const applied = await getAgentVersion({
22742
+ credential: ctx.credential,
22743
+ projectEndpoint: opts.project.endpoint,
22744
+ agentName: opts.agentName
22745
+ });
22746
+ const mismatches = personaPostCreateMismatches(candidate2, applied, foundryVersion);
22747
+ if (mismatches.length > 0) {
22748
+ throw new LocalCliError({
22749
+ code: "PLATFORM_PERSONA_VERIFY_FAILED",
22750
+ message: `Foundry did not persist the converged persona '${a.personaName ?? opts.agentName}' exactly (mismatch: ${mismatches.join(", ")}); refusing to stamp tree ${a.to}.`,
22751
+ hint: "Re-run the converge after Foundry reports the created version as latest; persistent mismatch requires platform investigation."
22752
+ });
22753
+ }
22651
22754
  return { treeSha: a.to, foundryVersion };
22652
22755
  }
22653
22756
 
@@ -23259,7 +23362,7 @@ function bindRepoApi(token, repo) {
23259
23362
  }
23260
23363
  async function runSeedRefresh(args) {
23261
23364
  const targetCommit = args.manifest.platform.commit;
23262
- const theirsRoot = await resolveReleaseContentWithAgents(args.manifest, {});
23365
+ const theirsRoot = await resolveReleaseContentWithAgents(args.manifest, { contentDir: args.contentDir });
23263
23366
  const repos = dedupeBrainRepos(args.agents);
23264
23367
  const results = [];
23265
23368
  let delivered = 0;
@@ -23456,6 +23559,19 @@ async function buildConvergeDeps(args) {
23456
23559
  async applyInfra(a, ctx) {
23457
23560
  const target = infraTargetSha(ctx.manifest, args.tree);
23458
23561
  if (a.reason !== "forced") return applyInfraDeploy(a, ctx, target);
23562
+ const stampOutcome = await readStampOutcome({
23563
+ credential: args.credential,
23564
+ subscriptionId: args.subscriptionId,
23565
+ resourceGroup: args.resourceGroup
23566
+ }).catch(() => ({ source: "unreadable" }));
23567
+ if (stampOutcome.source === "unreadable") {
23568
+ throw new LocalCliError({
23569
+ code: "PLATFORM_GATEWAY_ADOPTION_STAMP_UNREADABLE",
23570
+ message: "Forced infra held before deployment because the platform stamp could not be read, so gateway adoption authority is unknown.",
23571
+ hint: "Restore access to the platform stamp, then let the next converge retry."
23572
+ });
23573
+ }
23574
+ const adoption = stampOutcome.source === "explicit" ? stampOutcome.stamp.components.gateway.adoption : void 0;
23459
23575
  const infraTable = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
23460
23576
  const stamped = (await readInfraParams(infraTable))?.bicep;
23461
23577
  if (!stamped) {
@@ -23501,6 +23617,12 @@ async function buildConvergeDeps(args) {
23501
23617
  ...stamped.location ? { location: stamped.location } : {},
23502
23618
  ...voiceInternalSecret ? { voiceInternalSecret } : {}
23503
23619
  };
23620
+ if (adoption) {
23621
+ await assertLiveAdoptedGatewayTopology({
23622
+ gatewayResourceId: args.gatewayResourceId,
23623
+ adoption
23624
+ });
23625
+ }
23504
23626
  return applyInfraDeploy(a, ctx, target, opts);
23505
23627
  },
23506
23628
  async healthGate(_ctx, applied) {
@@ -23587,6 +23709,7 @@ async function buildConvergeDeps(args) {
23587
23709
  const { delivered, noop: noop2, failed } = await runSeedRefresh({
23588
23710
  credential: args.credential,
23589
23711
  kvUri,
23712
+ contentDir: ctx.contentDir,
23590
23713
  manifest: ctx.manifest,
23591
23714
  agents: discoveredAgents,
23592
23715
  version: ctx.manifest.platform.version,