@m8t-stack/cli 0.2.19 → 0.2.21

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 (3) hide show
  1. package/dist/cli.js +1006 -279
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1157,6 +1157,10 @@ async function enableHostedBrain(args) {
1157
1157
  memory: def.memory,
1158
1158
  env,
1159
1159
  metadata: {
1160
+ // Spread the current metadata first so unmanaged keys (a2a, a2aCard, …)
1161
+ // survive the relink version; the managed fields are re-asserted over the
1162
+ // spread, and createCoderVersion applies the `brain` arg last.
1163
+ ...current.metadata,
1160
1164
  source: "m8t-stack-POC",
1161
1165
  kind: "hosted",
1162
1166
  persona: current.metadata?.persona ?? "coding-agent",
@@ -1227,7 +1231,7 @@ var init_enable_hosted_brain = __esm({
1227
1231
  import { Builtins, Cli } from "clipanion";
1228
1232
 
1229
1233
  // src/lib/package-version.ts
1230
- var CLI_VERSION = "0.2.19";
1234
+ var CLI_VERSION = "0.2.21";
1231
1235
 
1232
1236
  // src/lib/render-error.ts
1233
1237
  init_errors();
@@ -3326,12 +3330,12 @@ function encodeURIPath(str4) {
3326
3330
  return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
3327
3331
  }
3328
3332
  var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3329
- var createPathTagFunction = (pathEncoder = encodeURIPath) => function path33(statics, ...params) {
3333
+ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(statics, ...params) {
3330
3334
  if (statics.length === 1)
3331
3335
  return statics[0];
3332
3336
  let postPath = false;
3333
3337
  const invalidSegments = [];
3334
- const path34 = statics.reduce((previousValue, currentValue, index) => {
3338
+ const path38 = statics.reduce((previousValue, currentValue, index) => {
3335
3339
  if (/[?#]/.test(currentValue)) {
3336
3340
  postPath = true;
3337
3341
  }
@@ -3348,7 +3352,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path33(sta
3348
3352
  }
3349
3353
  return previousValue + currentValue + (index === params.length ? "" : encoded);
3350
3354
  }, "");
3351
- const pathOnly = path34.split(/[?#]/, 1)[0];
3355
+ const pathOnly = path38.split(/[?#]/, 1)[0];
3352
3356
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
3353
3357
  let match;
3354
3358
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -3369,10 +3373,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path33(sta
3369
3373
  }, "");
3370
3374
  throw new OpenAIError(`Path parameters result in path with invalid segments:
3371
3375
  ${invalidSegments.map((e) => e.error).join("\n")}
3372
- ${path34}
3376
+ ${path38}
3373
3377
  ${underline}`);
3374
3378
  }
3375
- return path34;
3379
+ return path38;
3376
3380
  };
3377
3381
  var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
3378
3382
 
@@ -10889,9 +10893,9 @@ var OpenAI = class {
10889
10893
  this.apiKey = token;
10890
10894
  return true;
10891
10895
  }
10892
- buildURL(path33, query, defaultBaseURL) {
10896
+ buildURL(path37, query, defaultBaseURL) {
10893
10897
  const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
10894
- const url = isAbsoluteURL(path33) ? new URL(path33) : new URL(baseURL + (baseURL.endsWith("/") && path33.startsWith("/") ? path33.slice(1) : path33));
10898
+ const url = isAbsoluteURL(path37) ? new URL(path37) : new URL(baseURL + (baseURL.endsWith("/") && path37.startsWith("/") ? path37.slice(1) : path37));
10895
10899
  const defaultQuery = this.defaultQuery();
10896
10900
  const pathQuery = Object.fromEntries(url.searchParams);
10897
10901
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
@@ -10919,24 +10923,24 @@ var OpenAI = class {
10919
10923
  */
10920
10924
  async prepareRequest(request, { url, options }) {
10921
10925
  }
10922
- get(path33, opts) {
10923
- return this.methodRequest("get", path33, opts);
10926
+ get(path37, opts) {
10927
+ return this.methodRequest("get", path37, opts);
10924
10928
  }
10925
- post(path33, opts) {
10926
- return this.methodRequest("post", path33, opts);
10929
+ post(path37, opts) {
10930
+ return this.methodRequest("post", path37, opts);
10927
10931
  }
10928
- patch(path33, opts) {
10929
- return this.methodRequest("patch", path33, opts);
10932
+ patch(path37, opts) {
10933
+ return this.methodRequest("patch", path37, opts);
10930
10934
  }
10931
- put(path33, opts) {
10932
- return this.methodRequest("put", path33, opts);
10935
+ put(path37, opts) {
10936
+ return this.methodRequest("put", path37, opts);
10933
10937
  }
10934
- delete(path33, opts) {
10935
- return this.methodRequest("delete", path33, opts);
10938
+ delete(path37, opts) {
10939
+ return this.methodRequest("delete", path37, opts);
10936
10940
  }
10937
- methodRequest(method, path33, opts) {
10941
+ methodRequest(method, path37, opts) {
10938
10942
  return this.request(Promise.resolve(opts).then((opts2) => {
10939
- return { method, path: path33, ...opts2 };
10943
+ return { method, path: path37, ...opts2 };
10940
10944
  }));
10941
10945
  }
10942
10946
  request(options, remainingRetries = null) {
@@ -11058,8 +11062,8 @@ var OpenAI = class {
11058
11062
  }));
11059
11063
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
11060
11064
  }
11061
- getAPIList(path33, Page2, opts) {
11062
- return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path33, ...opts2 })) : { method: "get", path: path33, ...opts });
11065
+ getAPIList(path37, Page2, opts) {
11066
+ return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path37, ...opts2 })) : { method: "get", path: path37, ...opts });
11063
11067
  }
11064
11068
  requestAPIList(Page2, options) {
11065
11069
  const request = this.makeRequest(options, null, void 0);
@@ -11153,8 +11157,8 @@ var OpenAI = class {
11153
11157
  }
11154
11158
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
11155
11159
  const options = { ...inputOptions };
11156
- const { method, path: path33, query, defaultBaseURL } = options;
11157
- const url = this.buildURL(path33, query, defaultBaseURL);
11160
+ const { method, path: path37, query, defaultBaseURL } = options;
11161
+ const url = this.buildURL(path37, query, defaultBaseURL);
11158
11162
  if ("timeout" in options)
11159
11163
  validatePositiveInteger("timeout", options.timeout);
11160
11164
  options.timeout = options.timeout ?? this.timeout;
@@ -12156,10 +12160,10 @@ var BindListCommand = class extends M8tCommand {
12156
12160
  const qs = new URLSearchParams();
12157
12161
  if (this.channel) qs.set("channel", this.channel);
12158
12162
  if (this.status) qs.set("status", this.status);
12159
- const path33 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12163
+ const path37 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12160
12164
  const data = await apiCall(
12161
12165
  { gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
12162
- { method: "GET", path: path33 },
12166
+ { method: "GET", path: path37 },
12163
12167
  (msg) => this.context.stderr.write(msg + "\n")
12164
12168
  );
12165
12169
  if (mode === "json") {
@@ -13370,8 +13374,8 @@ ${r.stderr}`;
13370
13374
  message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
13371
13375
  });
13372
13376
  },
13373
- getFile: async (path33) => {
13374
- const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path33}`]);
13377
+ getFile: async (path37) => {
13378
+ const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path37}`]);
13375
13379
  return r.exitCode === 0 ? r.stdout : null;
13376
13380
  },
13377
13381
  isEmpty: async () => {
@@ -13615,11 +13619,11 @@ var BrainCheckAppCommand = class extends M8tCommand {
13615
13619
  // src/commands/brain/create.ts
13616
13620
  init_esm2();
13617
13621
  import { Command as Command18, Option as Option17 } from "clipanion";
13618
- import { spawnSync } from "child_process";
13622
+ import { execFileSync, spawnSync } from "child_process";
13619
13623
  import { readFileSync as readFileSync6 } from "fs";
13620
- import * as fs10 from "fs";
13624
+ import * as fs11 from "fs";
13621
13625
  import * as os4 from "os";
13622
- import * as path9 from "path";
13626
+ import * as path10 from "path";
13623
13627
  import * as readline2 from "readline/promises";
13624
13628
  import { DefaultAzureCredential as DefaultAzureCredential4 } from "@azure/identity";
13625
13629
  init_errors();
@@ -14135,11 +14139,66 @@ async function createBrainEnabledVersion(args) {
14135
14139
 
14136
14140
  // src/lib/brain-seed.ts
14137
14141
  init_errors();
14142
+ import * as fs10 from "fs";
14143
+ import * as path9 from "path";
14144
+
14145
+ // src/lib/seed-manifest.ts
14138
14146
  import * as fs9 from "fs";
14139
14147
  import * as path8 from "path";
14148
+ var SEED_MANIFEST_PATH = ".m8t/seed-manifest.json";
14149
+ var SEED_PATHS = ["skills", "references", "targets"];
14150
+ function isLineage(v) {
14151
+ if (!v || typeof v !== "object") return false;
14152
+ const l = v;
14153
+ return typeof l.name === "string" && typeof l.refreshedToCommit === "string" && typeof l.refreshedToVersion === "string" && Array.isArray(l.ownedPaths) && l.ownedPaths.every((p) => typeof p === "string");
14154
+ }
14155
+ function parseSeedManifest(text) {
14156
+ if (!text?.trim()) return null;
14157
+ let parsed;
14158
+ try {
14159
+ parsed = JSON.parse(text);
14160
+ } catch {
14161
+ return null;
14162
+ }
14163
+ if (!parsed || typeof parsed !== "object") return null;
14164
+ const m = parsed;
14165
+ if (m.schemaVersion !== 1 || !Array.isArray(m.seeds) || !m.seeds.every(isLineage)) return null;
14166
+ return { schemaVersion: 1, seeds: m.seeds };
14167
+ }
14168
+ function buildSeedManifest(seeds) {
14169
+ return { schemaVersion: 1, seeds };
14170
+ }
14171
+ function upsertLineage(manifest, lineage) {
14172
+ const prior = manifest?.seeds ?? [];
14173
+ const others = prior.filter((s) => s.name !== lineage.name);
14174
+ return { schemaVersion: 1, seeds: [...others, lineage].sort((a, b) => a.name.localeCompare(b.name)) };
14175
+ }
14176
+ function serializeSeedManifest(m) {
14177
+ return JSON.stringify(m, null, 2) + "\n";
14178
+ }
14179
+ function walkFiles(root, rel = "") {
14180
+ const abs = path8.join(root, rel);
14181
+ if (!fs9.existsSync(abs)) return [];
14182
+ const out = [];
14183
+ for (const entry of fs9.readdirSync(abs, { withFileTypes: true })) {
14184
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
14185
+ if (entry.isDirectory()) out.push(...walkFiles(root, childRel));
14186
+ else if (entry.isFile()) out.push(childRel);
14187
+ }
14188
+ return out;
14189
+ }
14190
+ function deriveOwnedPaths(seedDir) {
14191
+ const owned = /* @__PURE__ */ new Set([SEED_MANIFEST_PATH]);
14192
+ for (const sub of SEED_PATHS) {
14193
+ for (const rel of walkFiles(path8.join(seedDir, sub))) owned.add(`${sub}/${rel}`);
14194
+ }
14195
+ return [...owned].sort((a, b) => a.localeCompare(b));
14196
+ }
14197
+
14198
+ // src/lib/brain-seed.ts
14140
14199
  function resolveSeed(name) {
14141
- const dir = path8.join(resolveRepoRoot(), "brain-seeds", name);
14142
- if (!fs9.existsSync(dir)) {
14200
+ const dir = path9.join(resolveRepoRoot(), "brain-seeds", name);
14201
+ if (!fs10.existsSync(dir)) {
14143
14202
  throw new LocalCliError({
14144
14203
  code: "SEED_NOT_FOUND",
14145
14204
  message: `--seed "${name}": no brain-seeds/${name}/ directory found.`,
@@ -14149,11 +14208,22 @@ function resolveSeed(name) {
14149
14208
  return dir;
14150
14209
  }
14151
14210
  function materializeBrainTree(opts) {
14152
- fs9.cpSync(opts.templateSrc, opts.destDir, { recursive: true });
14211
+ fs10.cpSync(opts.templateSrc, opts.destDir, { recursive: true });
14153
14212
  if (opts.seedDir) {
14154
- fs9.cpSync(opts.seedDir, opts.destDir, { recursive: true });
14213
+ fs10.cpSync(opts.seedDir, opts.destDir, { recursive: true });
14155
14214
  }
14156
14215
  }
14216
+ function writeSeedMarker(args) {
14217
+ const manifest = buildSeedManifest([{
14218
+ name: args.seedName,
14219
+ refreshedToCommit: args.refreshedToCommit,
14220
+ refreshedToVersion: args.version,
14221
+ ownedPaths: deriveOwnedPaths(args.seedDir)
14222
+ }]);
14223
+ const dest = path9.join(args.destDir, SEED_MANIFEST_PATH);
14224
+ fs10.mkdirSync(path9.dirname(dest), { recursive: true });
14225
+ fs10.writeFileSync(dest, serializeSeedManifest(manifest));
14226
+ }
14157
14227
 
14158
14228
  // src/lib/github-app-repo.ts
14159
14229
  init_errors();
@@ -14260,18 +14330,32 @@ async function mintInstallationTokenFromPem(args) {
14260
14330
  }
14261
14331
  async function readRepoFileViaApp(args) {
14262
14332
  const doFetch = args.fetchImpl ?? fetch;
14263
- const res = await doFetch(`${GH_API}/repos/${args.repo}/contents/${args.path}`, { headers: ghHeaders(args.token), signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14333
+ const q = args.ref ? `?ref=${encodeURIComponent(args.ref)}` : "";
14334
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/contents/${args.path}${q}`, { headers: ghHeaders(args.token), signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14264
14335
  if (res.status === 404) return null;
14265
14336
  const text = await res.text();
14266
14337
  if (!res.ok) {
14267
14338
  throw new LocalCliError({
14268
14339
  code: "GH_APP_FILE_READ_FAILED",
14269
- message: `GET /repos/${args.repo}/contents/${args.path} HTTP ${res.status.toString()}: ${text.slice(0, 300)}`
14340
+ message: `GET /repos/${args.repo}/contents/${args.path}${q} HTTP ${res.status.toString()}: ${text.slice(0, 300)}`
14270
14341
  });
14271
14342
  }
14272
14343
  const data = JSON.parse(text);
14273
14344
  return Buffer.from(data.content ?? "", "base64").toString("utf8");
14274
14345
  }
14346
+ async function getInitialSeedCommitSha(args) {
14347
+ const doFetch = args.fetchImpl ?? fetch;
14348
+ const H = ghHeaders(args.token);
14349
+ const first = await doFetch(`${GH_API}/repos/${args.repo}/commits?per_page=1`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14350
+ if (!first.ok) return null;
14351
+ const link = first.headers.get("link");
14352
+ const lastUrl = /<([^>]+)>;\s*rel="last"/.exec(link ?? "")?.[1];
14353
+ const url = lastUrl ?? `${GH_API}/repos/${args.repo}/commits?per_page=1`;
14354
+ const res = await doFetch(url, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14355
+ if (!res.ok) return null;
14356
+ const commits = JSON.parse(await res.text());
14357
+ return commits.length > 0 ? commits[commits.length - 1].sha ?? null : null;
14358
+ }
14275
14359
  async function commitFilesViaApp(args) {
14276
14360
  const doFetch = args.fetchImpl ?? fetch;
14277
14361
  const H = ghHeaders(args.token);
@@ -14289,19 +14373,68 @@ async function commitFilesViaApp(args) {
14289
14373
  const ref = await api(`/git/ref/heads/${args.branch}`);
14290
14374
  const baseCommitSha = ref.object.sha;
14291
14375
  const baseCommit = await api(`/git/commits/${baseCommitSha}`);
14292
- const blobs = await Promise.all(args.files.map((f) => api(`/git/blobs`, { method: "POST", body: JSON.stringify({ content: f.content, encoding: "utf-8" }) })));
14293
- const tree = await api(`/git/trees`, {
14376
+ const upserts = args.files.filter((f) => !("delete" in f));
14377
+ const blobs = await Promise.all(upserts.map((f) => api(`/git/blobs`, { method: "POST", body: JSON.stringify({ content: f.content, encoding: "utf-8" }) })));
14378
+ const tree = args.files.map((f) => "delete" in f ? { path: f.path, mode: "100644", type: "blob", sha: null } : { path: f.path, mode: "100644", type: "blob", sha: blobs[upserts.indexOf(f)].sha });
14379
+ const newTree = await api(`/git/trees`, { method: "POST", body: JSON.stringify({ base_tree: baseCommit.tree.sha, tree }) });
14380
+ const commit = await api(`/git/commits`, {
14294
14381
  method: "POST",
14295
- body: JSON.stringify({
14296
- base_tree: baseCommit.tree.sha,
14297
- tree: args.files.map((f, i) => ({ path: f.path, mode: "100644", type: "blob", sha: blobs[i].sha }))
14298
- })
14382
+ body: JSON.stringify({ message: args.message, tree: newTree.sha, parents: [baseCommitSha] })
14383
+ });
14384
+ await api(`/git/refs/heads/${args.branch}`, { method: "PATCH", body: JSON.stringify({ sha: commit.sha }) });
14385
+ }
14386
+ async function createBranchViaApp(args) {
14387
+ const doFetch = args.fetchImpl ?? fetch;
14388
+ const H = ghHeaders(args.token);
14389
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/git/refs`, {
14390
+ method: "POST",
14391
+ headers: H,
14392
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14393
+ body: JSON.stringify({ ref: `refs/heads/${args.branch}`, sha: args.fromSha })
14394
+ });
14395
+ if (res.ok) return;
14396
+ if (res.status === 422) {
14397
+ const patch = await doFetch(`${GH_API}/repos/${args.repo}/git/refs/heads/${args.branch}`, {
14398
+ method: "PATCH",
14399
+ headers: H,
14400
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14401
+ body: JSON.stringify({ sha: args.fromSha, force: true })
14402
+ });
14403
+ if (patch.ok) return;
14404
+ throw new LocalCliError({
14405
+ code: "GH_APP_BRANCH_FAILED",
14406
+ message: `PATCH refs/heads/${args.branch} HTTP ${patch.status.toString()}: ${(await patch.text()).slice(0, 300)}`
14407
+ });
14408
+ }
14409
+ throw new LocalCliError({
14410
+ code: "GH_APP_BRANCH_FAILED",
14411
+ message: `POST /git/refs HTTP ${res.status.toString()}: ${(await res.text()).slice(0, 300)}`
14299
14412
  });
14413
+ }
14414
+ async function commitFilesFromBaseViaApp(args) {
14415
+ const doFetch = args.fetchImpl ?? fetch;
14416
+ const H = ghHeaders(args.token);
14417
+ const api = async (p, init) => {
14418
+ const res = await doFetch(`${GH_API}/repos/${args.repo}${p}`, init ? { ...init, headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) } : { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14419
+ const text = await res.text();
14420
+ if (!res.ok) {
14421
+ throw new LocalCliError({
14422
+ code: "GH_APP_COMMIT_FAILED",
14423
+ message: `${init?.method ?? "GET"} ${p} HTTP ${res.status.toString()}: ${text.slice(0, 300)}`
14424
+ });
14425
+ }
14426
+ return text ? JSON.parse(text) : {};
14427
+ };
14428
+ const baseCommit = await api(`/git/commits/${args.baseSha}`);
14429
+ const upserts = args.files.filter((f) => !("delete" in f));
14430
+ const blobs = await Promise.all(upserts.map((f) => api(`/git/blobs`, { method: "POST", body: JSON.stringify({ content: f.content, encoding: "utf-8" }) })));
14431
+ const tree = args.files.map((f) => "delete" in f ? { path: f.path, mode: "100644", type: "blob", sha: null } : { path: f.path, mode: "100644", type: "blob", sha: blobs[upserts.indexOf(f)].sha });
14432
+ const newTree = await api(`/git/trees`, { method: "POST", body: JSON.stringify({ base_tree: baseCommit.tree.sha, tree }) });
14300
14433
  const commit = await api(`/git/commits`, {
14301
14434
  method: "POST",
14302
- body: JSON.stringify({ message: args.message, tree: tree.sha, parents: [baseCommitSha] })
14435
+ body: JSON.stringify({ message: args.message, tree: newTree.sha, parents: [args.baseSha] })
14303
14436
  });
14304
- await api(`/git/refs/heads/${args.branch}`, { method: "PATCH", body: JSON.stringify({ sha: commit.sha }) });
14437
+ await createBranchViaApp({ token: args.token, repo: args.repo, branch: args.branch, fromSha: commit.sha, fetchImpl: args.fetchImpl });
14305
14438
  }
14306
14439
  async function probeRepoState(probe) {
14307
14440
  const status = await probe.repoStatus();
@@ -14325,13 +14458,66 @@ function appRepoProbe(args) {
14325
14458
  const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14326
14459
  return res.status;
14327
14460
  },
14328
- getFile: (path33) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path33, fetchImpl: args.fetchImpl }),
14461
+ getFile: (path37) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path37, fetchImpl: args.fetchImpl }),
14329
14462
  isEmpty: async () => {
14330
14463
  const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14331
14464
  return res.status === 404;
14332
14465
  }
14333
14466
  };
14334
14467
  }
14468
+ async function openPullRequestViaApp(args) {
14469
+ const doFetch = args.fetchImpl ?? fetch;
14470
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/pulls`, {
14471
+ method: "POST",
14472
+ headers: ghHeaders(args.token),
14473
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14474
+ body: JSON.stringify({ base: args.base, head: args.head, title: args.title, body: args.body })
14475
+ });
14476
+ const text = await res.text();
14477
+ if (!res.ok) throw new LocalCliError({ code: "GH_APP_PR_OPEN_FAILED", message: `POST /pulls HTTP ${res.status.toString()}: ${text.slice(0, 300)}` });
14478
+ const j = JSON.parse(text);
14479
+ return { number: j.number, htmlUrl: j.html_url };
14480
+ }
14481
+ async function findOpenPr(args) {
14482
+ const doFetch = args.fetchImpl ?? fetch;
14483
+ const owner = args.repo.split("/")[0];
14484
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/pulls?state=open&head=${owner}:${args.head}`, { headers: ghHeaders(args.token), signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14485
+ if (!res.ok) return null;
14486
+ const list = JSON.parse(await res.text());
14487
+ return list.length > 0 ? { number: list[0].number, htmlUrl: list[0].html_url } : null;
14488
+ }
14489
+ async function updatePullRequestViaApp(args) {
14490
+ const doFetch = args.fetchImpl ?? fetch;
14491
+ const patch = {};
14492
+ if (args.title !== void 0) patch.title = args.title;
14493
+ if (args.body !== void 0) patch.body = args.body;
14494
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/pulls/${args.number.toString()}`, {
14495
+ method: "PATCH",
14496
+ headers: ghHeaders(args.token),
14497
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14498
+ body: JSON.stringify(patch)
14499
+ });
14500
+ if (!res.ok) throw new LocalCliError({ code: "GH_APP_PR_UPDATE_FAILED", message: `PATCH /pulls/${args.number.toString()} HTTP ${res.status.toString()}: ${(await res.text()).slice(0, 300)}` });
14501
+ }
14502
+ async function closePullRequestViaApp(args) {
14503
+ const doFetch = args.fetchImpl ?? fetch;
14504
+ const H = ghHeaders(args.token);
14505
+ if (args.comment) {
14506
+ await doFetch(`${GH_API}/repos/${args.repo}/issues/${args.number.toString()}/comments`, {
14507
+ method: "POST",
14508
+ headers: H,
14509
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14510
+ body: JSON.stringify({ body: args.comment })
14511
+ });
14512
+ }
14513
+ const res = await doFetch(`${GH_API}/repos/${args.repo}/pulls/${args.number.toString()}`, {
14514
+ method: "PATCH",
14515
+ headers: H,
14516
+ signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS),
14517
+ body: JSON.stringify({ state: "closed" })
14518
+ });
14519
+ if (!res.ok) throw new LocalCliError({ code: "GH_APP_PR_CLOSE_FAILED", message: `PATCH /pulls/${args.number.toString()} HTTP ${res.status.toString()}: ${(await res.text()).slice(0, 300)}` });
14520
+ }
14335
14521
 
14336
14522
  // src/lib/brain-repo-collision.ts
14337
14523
  init_errors();
@@ -14513,15 +14699,15 @@ var BrainCreateCommand = class extends M8tCommand {
14513
14699
  agentEndpoint
14514
14700
  });
14515
14701
  const repoRoot = resolveRepoRoot();
14516
- const templateSrc = path9.join(repoRoot, "brain-template");
14517
- if (!fs10.existsSync(templateSrc)) {
14702
+ const templateSrc = path10.join(repoRoot, "brain-template");
14703
+ if (!fs11.existsSync(templateSrc)) {
14518
14704
  throw new LocalCliError({
14519
14705
  code: "TEMPLATE_MISSING",
14520
14706
  message: `brain-template/ missing at ${templateSrc}`,
14521
14707
  hint: "Ensure brain-template/ exists at the repo root (same level as apps/)."
14522
14708
  });
14523
14709
  }
14524
- const tmpDir = fs10.mkdtempSync(path9.join(os4.tmpdir(), `m8t-brain-create-${worker}-`));
14710
+ const tmpDir = fs11.mkdtempSync(path10.join(os4.tmpdir(), `m8t-brain-create-${worker}-`));
14525
14711
  let result;
14526
14712
  let resolvedInstallationId = null;
14527
14713
  const force = this.reuse === true ? "reuse" : this.newName === true ? "new" : void 0;
@@ -14558,6 +14744,10 @@ var BrainCreateCommand = class extends M8tCommand {
14558
14744
  try {
14559
14745
  if (needsPush) {
14560
14746
  materializeBrainTree({ templateSrc, destDir: tmpDir, seedDir });
14747
+ if (seedName && seedDir) {
14748
+ const headSha = execFileSync("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" }).trim();
14749
+ writeSeedMarker({ destDir: tmpDir, seedName, seedDir, refreshedToCommit: headSha, version: "(scaffold)" });
14750
+ }
14561
14751
  this.context.stdout.write(
14562
14752
  seedName ? `${colors.dim("\u2192")} copied brain-template/ + overlaid seed ${colors.field(seedName)} to ${tmpDir}
14563
14753
  ` : `${colors.dim("\u2192")} copied brain-template/ to ${tmpDir}
@@ -14649,7 +14839,7 @@ var BrainCreateCommand = class extends M8tCommand {
14649
14839
  project
14650
14840
  });
14651
14841
  } finally {
14652
- fs10.rmSync(tmpDir, { recursive: true, force: true });
14842
+ fs11.rmSync(tmpDir, { recursive: true, force: true });
14653
14843
  }
14654
14844
  if (mode === "json") {
14655
14845
  this.context.stdout.write(
@@ -14986,9 +15176,9 @@ init_errors();
14986
15176
  // src/lib/brain-unlink.ts
14987
15177
  init_esm2();
14988
15178
  init_errors();
14989
- import * as fs11 from "fs";
15179
+ import * as fs12 from "fs";
14990
15180
  import * as os5 from "os";
14991
- import * as path10 from "path";
15181
+ import * as path11 from "path";
14992
15182
  init_foundry_agent_get();
14993
15183
  var FOUNDRY_ARM_API2 = "2025-04-01-preview";
14994
15184
  var FOUNDRY_DATA_SCOPE2 = "https://ai.azure.com/.default";
@@ -15019,15 +15209,15 @@ async function unlinkBrain(args) {
15019
15209
  if (yaml?.personaPath) {
15020
15210
  let personaPath = yaml.personaPath;
15021
15211
  if (!personaPath.startsWith("/")) {
15022
- const marker = path10.join(args.home ?? os5.homedir(), ".m8t-stack", "repo-root");
15023
- if (!fs11.existsSync(marker)) {
15212
+ const marker = path11.join(args.home ?? os5.homedir(), ".m8t-stack", "repo-root");
15213
+ if (!fs12.existsSync(marker)) {
15024
15214
  throw new LocalCliError({
15025
15215
  code: "PERSONA_PATH_NOT_ABSOLUTE",
15026
15216
  message: `personaPath '${personaPath}' is relative and the repo-root marker is missing.`,
15027
15217
  hint: "Re-run 'm8t install' from a fresh m8t-stack checkout (writes ~/.m8t-stack/repo-root) \u2014 or edit the personaPath to absolute."
15028
15218
  });
15029
15219
  }
15030
- personaPath = path10.join(fs11.readFileSync(marker, "utf8").trim(), personaPath);
15220
+ personaPath = path11.join(fs12.readFileSync(marker, "utf8").trim(), personaPath);
15031
15221
  }
15032
15222
  strippedInstructions = renderPersonaBody(personaPath, yaml.fillableFieldValues ?? {}, args.agentName);
15033
15223
  } else {
@@ -15267,27 +15457,27 @@ import { DefaultAzureCredential as DefaultAzureCredential9 } from "@azure/identi
15267
15457
  init_errors();
15268
15458
 
15269
15459
  // src/lib/deploy-prompt-advisor.ts
15270
- import * as fs12 from "fs";
15271
- import * as path11 from "path";
15460
+ import * as fs13 from "fs";
15461
+ import * as path12 from "path";
15272
15462
  import { parse as parseYaml6 } from "yaml";
15273
15463
  init_foundry_agents();
15274
15464
  init_errors();
15275
15465
  function findPersonasRoot(hint) {
15276
- let dir = path11.resolve(hint);
15277
- const fsRoot = path11.parse(dir).root;
15466
+ let dir = path12.resolve(hint);
15467
+ const fsRoot = path12.parse(dir).root;
15278
15468
  for (; ; ) {
15279
- if (fs12.existsSync(path11.join(dir, "personas"))) return dir;
15280
- const parent = path11.dirname(dir);
15469
+ if (fs13.existsSync(path12.join(dir, "personas"))) return dir;
15470
+ const parent = path12.dirname(dir);
15281
15471
  if (parent === dir || dir === fsRoot) return null;
15282
15472
  dir = parent;
15283
15473
  }
15284
15474
  }
15285
15475
  async function deployPromptAdvisor(args) {
15286
15476
  const resolvedRoot = findPersonasRoot(args.repoRoot);
15287
- const personaPath = resolvedRoot ? path11.join(resolvedRoot, "personas", args.persona, "persona.md") : path11.join(args.repoRoot, "personas", args.persona, "persona.md");
15477
+ const personaPath = resolvedRoot ? path12.join(resolvedRoot, "personas", args.persona, "persona.md") : path12.join(args.repoRoot, "personas", args.persona, "persona.md");
15288
15478
  let raw;
15289
15479
  try {
15290
- raw = fs12.readFileSync(personaPath, "utf8");
15480
+ raw = fs13.readFileSync(personaPath, "utf8");
15291
15481
  } catch (e) {
15292
15482
  throw new LocalCliError({
15293
15483
  code: "ADVISOR_PERSONA_MISSING",
@@ -15449,7 +15639,7 @@ init_errors();
15449
15639
  // src/lib/agent-remove.ts
15450
15640
  init_errors();
15451
15641
  init_foundry_agent_get();
15452
- import * as fs14 from "fs";
15642
+ import * as fs15 from "fs";
15453
15643
  import { spawnSync as spawnSync2 } from "child_process";
15454
15644
 
15455
15645
  // src/lib/a2a-enable.ts
@@ -15563,12 +15753,12 @@ async function awaitAgentQueryable(args) {
15563
15753
  // src/lib/persona-a2a.ts
15564
15754
  init_errors();
15565
15755
  init_esm();
15566
- import * as fs13 from "fs";
15756
+ import * as fs14 from "fs";
15567
15757
  import { parse as parseYaml7 } from "yaml";
15568
15758
  function readPersonaA2aCard(personaPath) {
15569
15759
  let raw;
15570
15760
  try {
15571
- raw = fs13.readFileSync(personaPath, "utf8");
15761
+ raw = fs14.readFileSync(personaPath, "utf8");
15572
15762
  } catch (e) {
15573
15763
  throw new LocalCliError({ code: "PERSONA_READ_FAILED", message: `Could not read persona '${personaPath}': ${e.message}` });
15574
15764
  }
@@ -15893,7 +16083,7 @@ function defaultRemoveAgentDeps(args) {
15893
16083
  },
15894
16084
  removeLocalYaml(agentName) {
15895
16085
  const p = agentYamlPath(agentName, home);
15896
- if (fs14.existsSync(p)) fs14.rmSync(p);
16086
+ if (fs15.existsSync(p)) fs15.rmSync(p);
15897
16087
  }
15898
16088
  };
15899
16089
  }
@@ -16035,16 +16225,16 @@ ${colors.success("\u2713")} removed ${colors.field(name)}.
16035
16225
  };
16036
16226
 
16037
16227
  // src/commands/a2a/enable.ts
16038
- import * as path13 from "path";
16228
+ import * as path14 from "path";
16039
16229
  import { Command as Command25, Option as Option24 } from "clipanion";
16040
16230
  import { DefaultAzureCredential as DefaultAzureCredential11 } from "@azure/identity";
16041
16231
  init_errors();
16042
16232
 
16043
16233
  // src/lib/bridge-url.ts
16044
16234
  init_errors();
16045
- import * as fs15 from "fs";
16235
+ import * as fs16 from "fs";
16046
16236
  import * as os6 from "os";
16047
- import * as path12 from "path";
16237
+ import * as path13 from "path";
16048
16238
  import { parse as parseYaml8 } from "yaml";
16049
16239
  var A2A_PATH = "/api/a2a/mcp";
16050
16240
  function resolveBridgeUrl(opts) {
@@ -16066,10 +16256,10 @@ function clean(v) {
16066
16256
  return t.length ? t : void 0;
16067
16257
  }
16068
16258
  function readConfigGatewayUrl(home) {
16069
- const cfg = path12.join(home ?? os6.homedir(), ".m8t-stack", "config.yaml");
16070
- if (!fs15.existsSync(cfg)) return void 0;
16259
+ const cfg = path13.join(home ?? os6.homedir(), ".m8t-stack", "config.yaml");
16260
+ if (!fs16.existsSync(cfg)) return void 0;
16071
16261
  try {
16072
- const o = parseYaml8(fs15.readFileSync(cfg, "utf8"));
16262
+ const o = parseYaml8(fs16.readFileSync(cfg, "utf8"));
16073
16263
  return typeof o.gatewayUrl === "string" ? o.gatewayUrl : void 0;
16074
16264
  } catch {
16075
16265
  return void 0;
@@ -16138,11 +16328,11 @@ var A2aEnableCommand = class extends M8tCommand {
16138
16328
  resolvePersonaPath() {
16139
16329
  if (typeof this.persona === "string" && this.persona.length > 0) {
16140
16330
  if (this.persona.includes("/") || this.persona.endsWith(".md")) return this.persona;
16141
- return path13.join(resolveRepoRoot(), "personas", this.persona, "persona.md");
16331
+ return path14.join(resolveRepoRoot(), "personas", this.persona, "persona.md");
16142
16332
  }
16143
16333
  const yaml = readAgentYaml(this.worker);
16144
16334
  if (yaml?.personaPath) {
16145
- return yaml.personaPath.startsWith("/") ? yaml.personaPath : path13.join(resolveRepoRoot(), yaml.personaPath);
16335
+ return yaml.personaPath.startsWith("/") ? yaml.personaPath : path14.join(resolveRepoRoot(), yaml.personaPath);
16146
16336
  }
16147
16337
  throw new LocalCliError({ code: "A2A_NO_PERSONA", message: `Could not resolve a persona for '${this.worker}'. Pass --persona <name>.` });
16148
16338
  }
@@ -16198,12 +16388,12 @@ import { Command as Command27 } from "clipanion";
16198
16388
 
16199
16389
  // src/lib/architect-version.ts
16200
16390
  import { createHash as createHash2 } from "crypto";
16201
- import * as fs16 from "fs";
16391
+ import * as fs17 from "fs";
16202
16392
  import * as os7 from "os";
16203
- import * as path14 from "path";
16393
+ import * as path15 from "path";
16204
16394
  function readVersionFrontmatter(file) {
16205
- if (!fs16.existsSync(file)) return "";
16206
- const text = fs16.readFileSync(file, "utf8");
16395
+ if (!fs17.existsSync(file)) return "";
16396
+ const text = fs17.readFileSync(file, "utf8");
16207
16397
  if (!text.startsWith("---")) return "";
16208
16398
  const closeIdx = text.indexOf("\n---", 3);
16209
16399
  if (closeIdx < 0) return "";
@@ -16212,23 +16402,23 @@ function readVersionFrontmatter(file) {
16212
16402
  return m ? m[1].trim() : "";
16213
16403
  }
16214
16404
  function sha256File(filePath) {
16215
- return createHash2("sha256").update(fs16.readFileSync(filePath)).digest("hex");
16405
+ return createHash2("sha256").update(fs17.readFileSync(filePath)).digest("hex");
16216
16406
  }
16217
16407
  function checkArchitectDrift() {
16218
- const sourcePath = path14.join(
16408
+ const sourcePath = path15.join(
16219
16409
  resolveRepoRoot(),
16220
16410
  "personas",
16221
16411
  "m8t-architect",
16222
16412
  "persona.md"
16223
16413
  );
16224
- const sidecarPath = path14.join(
16414
+ const sidecarPath = path15.join(
16225
16415
  os7.homedir(),
16226
16416
  ".claude",
16227
16417
  "skills",
16228
16418
  "m8t-architect",
16229
16419
  ".m8t-skill.json"
16230
16420
  );
16231
- if (!fs16.existsSync(sourcePath)) {
16421
+ if (!fs17.existsSync(sourcePath)) {
16232
16422
  return {
16233
16423
  match: false,
16234
16424
  sourceVersion: "",
@@ -16238,9 +16428,9 @@ function checkArchitectDrift() {
16238
16428
  }
16239
16429
  const sourceVersion = readVersionFrontmatter(sourcePath);
16240
16430
  let sidecar = null;
16241
- if (fs16.existsSync(sidecarPath)) {
16431
+ if (fs17.existsSync(sidecarPath)) {
16242
16432
  try {
16243
- const raw = JSON.parse(fs16.readFileSync(sidecarPath, "utf8"));
16433
+ const raw = JSON.parse(fs17.readFileSync(sidecarPath, "utf8"));
16244
16434
  if (raw !== null && typeof raw === "object") {
16245
16435
  const r = raw;
16246
16436
  sidecar = {
@@ -16303,7 +16493,7 @@ var ArchitectCheckCommand = class extends M8tCommand {
16303
16493
 
16304
16494
  // src/commands/coder/deploy.ts
16305
16495
  init_esm2();
16306
- import * as path16 from "path";
16496
+ import * as path17 from "path";
16307
16497
  import { Command as Command28, Option as Option26 } from "clipanion";
16308
16498
  import { DefaultAzureCredential as DefaultAzureCredential13 } from "@azure/identity";
16309
16499
  init_errors();
@@ -16570,14 +16760,14 @@ async function deployHostedWorker(args) {
16570
16760
  }
16571
16761
 
16572
16762
  // src/lib/persona.ts
16573
- import * as fs17 from "fs";
16574
- import * as path15 from "path";
16763
+ import * as fs18 from "fs";
16764
+ import * as path16 from "path";
16575
16765
  import { parse as parseYaml9 } from "yaml";
16576
16766
  function findRepoRoot(startDir) {
16577
- let dir = path15.resolve(startDir);
16767
+ let dir = path16.resolve(startDir);
16578
16768
  for (; ; ) {
16579
- if (fs17.existsSync(path15.join(dir, "personas"))) return dir;
16580
- const parent = path15.dirname(dir);
16769
+ if (fs18.existsSync(path16.join(dir, "personas"))) return dir;
16770
+ const parent = path16.dirname(dir);
16581
16771
  if (parent === dir) return null;
16582
16772
  dir = parent;
16583
16773
  }
@@ -16585,10 +16775,10 @@ function findRepoRoot(startDir) {
16585
16775
  function resolvePersona(persona, cwd = process.cwd()) {
16586
16776
  const root = findRepoRoot(cwd);
16587
16777
  if (!root) return { persona, personaVersion: null };
16588
- const file = path15.join(root, "personas", persona, "persona.md");
16778
+ const file = path16.join(root, "personas", persona, "persona.md");
16589
16779
  let raw;
16590
16780
  try {
16591
- raw = fs17.readFileSync(file, "utf-8");
16781
+ raw = fs18.readFileSync(file, "utf-8");
16592
16782
  } catch {
16593
16783
  return { persona, personaVersion: null };
16594
16784
  }
@@ -16925,7 +17115,7 @@ var CoderDeployCommand = class extends M8tCommand {
16925
17115
  projectEndpoint: project.endpoint,
16926
17116
  projectArmId: `${project.accountScope}/projects/${project.projectName}`,
16927
17117
  agentName: this.name,
16928
- personaPath: path16.join(resolveRepoRoot(), "personas", personaName, "persona.md"),
17118
+ personaPath: path17.join(resolveRepoRoot(), "personas", personaName, "persona.md"),
16929
17119
  bridgeUrl,
16930
17120
  onProgress
16931
17121
  });
@@ -17051,7 +17241,7 @@ var CoderTeardownCommand = class extends M8tCommand {
17051
17241
 
17052
17242
  // src/commands/azure-exec/deploy.ts
17053
17243
  init_esm2();
17054
- import * as path17 from "path";
17244
+ import * as path18 from "path";
17055
17245
  import { Command as Command30, Option as Option28 } from "clipanion";
17056
17246
  import { DefaultAzureCredential as DefaultAzureCredential15 } from "@azure/identity";
17057
17247
  init_errors();
@@ -17321,7 +17511,7 @@ var AzureExecDeployCommand = class extends M8tCommand {
17321
17511
  kvUri
17322
17512
  });
17323
17513
  onProgress?.("a2a-enabling as a target\u2026");
17324
- const personaPath = path17.join(resolveRepoRoot(), "personas", "azure-executor", "persona.md");
17514
+ const personaPath = path18.join(resolveRepoRoot(), "personas", "azure-executor", "persona.md");
17325
17515
  const bridgeUrl = resolveBridgeUrl({
17326
17516
  flag: typeof this.gatewayUrl === "string" ? this.gatewayUrl : void 0,
17327
17517
  env: processEnv
@@ -17463,8 +17653,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17463
17653
  hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
17464
17654
  });
17465
17655
  }
17466
- const path33 = repo.replace(/^ghcr\.io\//, "");
17467
- const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path33}:pull`);
17656
+ const path37 = repo.replace(/^ghcr\.io\//, "");
17657
+ const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path37}:pull`);
17468
17658
  if (!tokenRes.ok) {
17469
17659
  throw new LocalCliError({
17470
17660
  code: "PLATFORM_GHCR_TOKEN_FAILED",
@@ -17480,7 +17670,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17480
17670
  hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
17481
17671
  });
17482
17672
  }
17483
- const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path33}/tags/list`, {
17673
+ const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path37}/tags/list`, {
17484
17674
  headers: { Authorization: `Bearer ${token}` }
17485
17675
  });
17486
17676
  if (!tagsRes.ok) {
@@ -17494,7 +17684,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17494
17684
  }
17495
17685
 
17496
17686
  // src/lib/release-channel.ts
17497
- import * as fs18 from "fs";
17687
+ import * as fs19 from "fs";
17498
17688
 
17499
17689
  // ../../packages/platform-release/dist/esm/manifest.js
17500
17690
  var SEVERITIES = ["critical", "recommended", "optional"];
@@ -17661,7 +17851,7 @@ function manifestSourceForVersionTag(channelUrl, version) {
17661
17851
  return { url: custom };
17662
17852
  }
17663
17853
  async function fetchManifest(source, deps = {}) {
17664
- const readFile8 = deps.readFile ?? ((p) => fs18.readFileSync(p, "utf8"));
17854
+ const readFile8 = deps.readFile ?? ((p) => fs19.readFileSync(p, "utf8"));
17665
17855
  const fetchImpl = deps.fetchImpl ?? fetch;
17666
17856
  const ghToken = deps.ghToken ?? getGhToken;
17667
17857
  let raw;
@@ -17707,10 +17897,10 @@ async function fetchManifest(source, deps = {}) {
17707
17897
  }
17708
17898
 
17709
17899
  // src/lib/release-content.ts
17710
- import * as fs19 from "fs";
17900
+ import * as fs20 from "fs";
17711
17901
  import * as os8 from "os";
17712
- import * as path18 from "path";
17713
- import { execFileSync } from "child_process";
17902
+ import * as path19 from "path";
17903
+ import { execFileSync as execFileSync2 } from "child_process";
17714
17904
  init_errors();
17715
17905
  var OWNER_REPO = "m8t-run/m8t";
17716
17906
  function parseTreeResponse(body) {
@@ -17748,15 +17938,15 @@ function verifyPersonaTrees(manifest, tree) {
17748
17938
  }
17749
17939
  async function resolveReleaseContent(manifest, opts = {}) {
17750
17940
  if (opts.contentDir) {
17751
- if (!fs19.existsSync(path18.join(opts.contentDir, "personas"))) {
17941
+ if (!fs20.existsSync(path19.join(opts.contentDir, "personas"))) {
17752
17942
  throw new LocalCliError({ code: "PLATFORM_CONTENT_DIR_INVALID", message: `--content-dir '${opts.contentDir}' has no personas/ \u2014 not a repo checkout.` });
17753
17943
  }
17754
17944
  return opts.contentDir;
17755
17945
  }
17756
17946
  const commit = manifest.platform.commit;
17757
- const cacheRoot = opts.cacheRoot ?? path18.join(os8.homedir(), ".m8t-stack", "cache");
17758
- const dest = path18.join(cacheRoot, `release-${commit}`);
17759
- if (fs19.existsSync(path18.join(dest, "personas"))) return dest;
17947
+ const cacheRoot = opts.cacheRoot ?? path19.join(os8.homedir(), ".m8t-stack", "cache");
17948
+ const dest = path19.join(cacheRoot, `release-${commit}`);
17949
+ if (fs20.existsSync(path19.join(dest, "personas"))) return dest;
17760
17950
  const fetchImpl = opts.fetchImpl ?? fetch;
17761
17951
  const ghToken = opts.ghToken ?? getGhToken;
17762
17952
  let token;
@@ -17770,19 +17960,19 @@ async function resolveReleaseContent(manifest, opts = {}) {
17770
17960
  if (!res.ok) {
17771
17961
  throw new LocalCliError({ code: "PLATFORM_TARBALL_FETCH_FAILED", message: `GET ${url} \u2192 HTTP ${res.status.toString()}.` });
17772
17962
  }
17773
- fs19.mkdirSync(cacheRoot, { recursive: true });
17774
- const tmp = fs19.mkdtempSync(path18.join(cacheRoot, `.dl-${commit.slice(0, 8)}-`));
17963
+ fs20.mkdirSync(cacheRoot, { recursive: true });
17964
+ const tmp = fs20.mkdtempSync(path19.join(cacheRoot, `.dl-${commit.slice(0, 8)}-`));
17775
17965
  try {
17776
- const tgz = path18.join(tmp, "src.tar.gz");
17777
- fs19.writeFileSync(tgz, Buffer.from(await res.arrayBuffer()));
17778
- const unpacked = path18.join(tmp, "unpacked");
17779
- fs19.mkdirSync(unpacked);
17966
+ const tgz = path19.join(tmp, "src.tar.gz");
17967
+ fs20.writeFileSync(tgz, Buffer.from(await res.arrayBuffer()));
17968
+ const unpacked = path19.join(tmp, "unpacked");
17969
+ fs20.mkdirSync(unpacked);
17780
17970
  extractTarGz(tgz, unpacked);
17781
17971
  try {
17782
- fs19.renameSync(unpacked, dest);
17972
+ fs20.renameSync(unpacked, dest);
17783
17973
  } catch (e) {
17784
17974
  const err = e;
17785
- const isBenignRace = (err.code === "EEXIST" || err.code === "ENOTEMPTY") && fs19.existsSync(path18.join(dest, "personas"));
17975
+ const isBenignRace = (err.code === "EEXIST" || err.code === "ENOTEMPTY") && fs20.existsSync(path19.join(dest, "personas"));
17786
17976
  if (!isBenignRace) {
17787
17977
  throw new LocalCliError({
17788
17978
  code: "PLATFORM_CACHE_PUBLISH_FAILED",
@@ -17791,12 +17981,12 @@ async function resolveReleaseContent(manifest, opts = {}) {
17791
17981
  }
17792
17982
  }
17793
17983
  } finally {
17794
- fs19.rmSync(tmp, { recursive: true, force: true });
17984
+ fs20.rmSync(tmp, { recursive: true, force: true });
17795
17985
  }
17796
17986
  return dest;
17797
17987
  }
17798
17988
  function extractTarGz(tgz, into) {
17799
- execFileSync("tar", ["-xzf", tgz, "-C", into, "--strip-components=1"]);
17989
+ execFileSync2("tar", ["-xzf", tgz, "-C", into, "--strip-components=1"]);
17800
17990
  }
17801
17991
 
17802
17992
  // src/lib/platform-stamp.ts
@@ -17918,14 +18108,14 @@ function sleep2(ms) {
17918
18108
  }
17919
18109
 
17920
18110
  // src/lib/platform-converge.ts
17921
- import * as fs21 from "fs";
17922
- import * as path20 from "path";
18111
+ import * as fs22 from "fs";
18112
+ import * as path21 from "path";
17923
18113
  import { parse as parseYaml10 } from "yaml";
17924
18114
 
17925
18115
  // src/lib/deploy.ts
17926
- import * as fs20 from "fs/promises";
18116
+ import * as fs21 from "fs/promises";
17927
18117
  import * as os9 from "os";
17928
- import * as path19 from "path";
18118
+ import * as path20 from "path";
17929
18119
  init_errors();
17930
18120
  var FOUNDRY_TRACING_MODES = ["project", "account", "skip"];
17931
18121
  function parseFoundryTracingMode(v) {
@@ -17973,9 +18163,9 @@ function buildBicepParams(p) {
17973
18163
  return params;
17974
18164
  }
17975
18165
  async function resolveRepoRoot2(home = os9.homedir()) {
17976
- const marker = path19.join(home, ".m8t-stack", "repo-root");
18166
+ const marker = path20.join(home, ".m8t-stack", "repo-root");
17977
18167
  try {
17978
- const root = (await fs20.readFile(marker, "utf8")).trim();
18168
+ const root = (await fs21.readFile(marker, "utf8")).trim();
17979
18169
  if (!root) throw new Error("empty");
17980
18170
  return root;
17981
18171
  } catch {
@@ -18052,7 +18242,7 @@ function resolveAcrPullIdentity(opts) {
18052
18242
  return opts.explicit ?? "";
18053
18243
  }
18054
18244
  async function runBicepDeployment(opts) {
18055
- const bicepPath = path19.join(opts.repoRoot, "deploy", "main.bicep");
18245
+ const bicepPath = path20.join(opts.repoRoot, "deploy", "main.bicep");
18056
18246
  const result = JSON.parse(
18057
18247
  await runAz([
18058
18248
  "deployment",
@@ -18123,7 +18313,16 @@ function sortObj(o) {
18123
18313
  }
18124
18314
 
18125
18315
  // src/lib/platform-converge.ts
18126
- var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas"];
18316
+ var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
18317
+ function seedSetRev(manifest) {
18318
+ const items = manifest.components.brainSeeds.items;
18319
+ const parts = [];
18320
+ for (const name of Object.keys(items).sort()) {
18321
+ const subs = items[name].subtrees;
18322
+ for (const sub of Object.keys(subs).sort()) parts.push(`${name}:${sub}:${subs[sub] ?? "-"}`);
18323
+ }
18324
+ return parts.join("|");
18325
+ }
18127
18326
  function infraTargetSha(manifest, tree) {
18128
18327
  return manifest.components.infra?.treeSha ?? tree.treeShaOf("deploy") ?? "";
18129
18328
  }
@@ -18159,6 +18358,16 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
18159
18358
  }
18160
18359
  continue;
18161
18360
  }
18361
+ if (component === "brainSeeds") {
18362
+ const to = seedSetRev(manifest);
18363
+ const from2 = stamp?.components.brainSeeds?.seedRev ?? null;
18364
+ const targeted = opts.only === "brainSeeds";
18365
+ if (adopt) actions.push({ component, reason: "adopt", from: from2, to });
18366
+ else if (targeted) actions.push({ component, reason: from2 === to ? "forced" : "changed", from: from2, to });
18367
+ else if (from2 !== to) actions.push({ component, reason: "changed", from: from2, to });
18368
+ else skipped.push({ component, reason: "up-to-date" });
18369
+ continue;
18370
+ }
18162
18371
  const img = manifest.components[component];
18163
18372
  const cur = stamp?.components[component];
18164
18373
  if (cur?.state === "external") {
@@ -18202,6 +18411,7 @@ function installedValueOf(stamp, component, personaName) {
18202
18411
  if (!personaName || !Object.hasOwn(stamp.components.personas, personaName)) return "-";
18203
18412
  return stamp.components.personas[personaName].treeSha;
18204
18413
  }
18414
+ if (component === "brainSeeds") return stamp.components.brainSeeds?.seedRev ?? "-";
18205
18415
  return stamp.components[component].tag;
18206
18416
  }
18207
18417
  function targetValueOf(manifest, tree, component, personaName) {
@@ -18210,6 +18420,7 @@ function targetValueOf(manifest, tree, component, personaName) {
18210
18420
  if (!personaName || !Object.hasOwn(manifest.components.personas.items, personaName)) return "-";
18211
18421
  return manifest.components.personas.items[personaName].treeSha;
18212
18422
  }
18423
+ if (component === "brainSeeds") return seedSetRev(manifest) || "-";
18213
18424
  return manifest.components[component].tag;
18214
18425
  }
18215
18426
  async function applyPlan(plan, deps, ctx) {
@@ -18230,6 +18441,18 @@ async function applyPlan(plan, deps, ctx) {
18230
18441
  continue;
18231
18442
  }
18232
18443
  stamp.components[a.component] = { ...r, state: "managed" };
18444
+ } else if (a.component === "brainSeeds") {
18445
+ if (!deps.applySeedRefresh) {
18446
+ ctx.onProgress?.("brainSeeds: seed refresh is not supported by this driver (CLI-only in v1) \u2014 skipped.");
18447
+ continue;
18448
+ }
18449
+ const r = await deps.applySeedRefresh(a, ctx);
18450
+ ctx.onProgress?.(`brainSeeds: delivered to ${r.delivered.toString()} repo(s), ${r.noop.toString()} already current.`);
18451
+ if (!r.converged) {
18452
+ ctx.onProgress?.("brainSeeds: not fully delivered (no Key Vault, or some repos failed) \u2014 stamp NOT advanced; will retry next converge.");
18453
+ continue;
18454
+ }
18455
+ stamp.components.brainSeeds = { seedRev: a.to };
18233
18456
  } else {
18234
18457
  if (!a.personaName) {
18235
18458
  throw new LocalCliError({ code: "PLATFORM_PERSONA_NO_NAME", message: "persona action missing personaName" });
@@ -18282,7 +18505,14 @@ function seedStamp(prior, manifest, plan) {
18282
18505
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
18283
18506
  lastResult: "partial",
18284
18507
  cli: "",
18285
- components: { gateway: blankImg(), codingAgent: blankImg(), azureExecutor: blankImg(), personas: {}, infra: { treeSha: "" } }
18508
+ components: {
18509
+ gateway: blankImg(),
18510
+ codingAgent: blankImg(),
18511
+ azureExecutor: blankImg(),
18512
+ personas: {},
18513
+ infra: { treeSha: "" },
18514
+ brainSeeds: { seedRev: "" }
18515
+ }
18286
18516
  };
18287
18517
  }
18288
18518
  async function applyGatewayImage(a, ctx, gatewayResourceId) {
@@ -18376,7 +18606,7 @@ async function applyAgentImage(a, ctx, opts) {
18376
18606
  function declaredFillableFields(personaPath) {
18377
18607
  let raw;
18378
18608
  try {
18379
- raw = fs21.readFileSync(personaPath, "utf8");
18609
+ raw = fs22.readFileSync(personaPath, "utf8");
18380
18610
  } catch {
18381
18611
  return [];
18382
18612
  }
@@ -18402,7 +18632,7 @@ function injectAndRender(personaPath, values, agentName) {
18402
18632
  function loaderText(loaderPath) {
18403
18633
  if (!loaderPath) return null;
18404
18634
  try {
18405
- return fs21.readFileSync(loaderPath, "utf8");
18635
+ return fs22.readFileSync(loaderPath, "utf8");
18406
18636
  } catch {
18407
18637
  return null;
18408
18638
  }
@@ -18419,7 +18649,7 @@ function brainRepoOf(current) {
18419
18649
  }
18420
18650
  async function applyPersona(a, ctx, opts) {
18421
18651
  const current = await getAgentVersion({ credential: ctx.credential, projectEndpoint: opts.project.endpoint, agentName: opts.agentName });
18422
- const personaPath = path20.join(opts.personaDir, "persona.md");
18652
+ const personaPath = path21.join(opts.personaDir, "persona.md");
18423
18653
  const yamlValues = readAgentYaml(opts.agentName)?.fillableFieldValues ?? null;
18424
18654
  const values = resolveFillableValues(current, yamlValues);
18425
18655
  const declared = declaredFillableFields(personaPath);
@@ -18618,7 +18848,7 @@ import { confirm as confirm5 } from "@inquirer/prompts";
18618
18848
  import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/identity";
18619
18849
 
18620
18850
  // src/lib/platform-converge-cli.ts
18621
- import * as path21 from "path";
18851
+ import * as path25 from "path";
18622
18852
 
18623
18853
  // src/lib/platform-infra-params.ts
18624
18854
  import { TableClient as TableClient2 } from "@azure/data-tables";
@@ -18673,6 +18903,356 @@ async function deriveSuffixVerified(opts) {
18673
18903
 
18674
18904
  // src/lib/platform-converge-cli.ts
18675
18905
  init_errors();
18906
+
18907
+ // src/lib/seed-refresh-driver.ts
18908
+ init_esm2();
18909
+ import * as path24 from "path";
18910
+
18911
+ // src/lib/seed-refresh.ts
18912
+ init_errors();
18913
+ import * as fs24 from "fs";
18914
+ import * as path23 from "path";
18915
+
18916
+ // src/lib/seed-merge.ts
18917
+ import * as fs23 from "fs";
18918
+ import * as os10 from "os";
18919
+ import * as path22 from "path";
18920
+ import { spawnSync as spawnSync3 } from "child_process";
18921
+ function isProbablyText(s) {
18922
+ return !s.includes("\0");
18923
+ }
18924
+ function mergeThreeWay(ours, base, theirs) {
18925
+ if (![ours, base, theirs].every(isProbablyText)) {
18926
+ return { ok: false, merged: "" };
18927
+ }
18928
+ const lf = (s) => s.replace(/\r\n/g, "\n");
18929
+ const dir = fs23.mkdtempSync(path22.join(os10.tmpdir(), "seed-merge-"));
18930
+ try {
18931
+ const o = path22.join(dir, "ours");
18932
+ const b = path22.join(dir, "base");
18933
+ const t = path22.join(dir, "theirs");
18934
+ fs23.writeFileSync(o, lf(ours));
18935
+ fs23.writeFileSync(b, lf(base));
18936
+ fs23.writeFileSync(t, lf(theirs));
18937
+ const res = spawnSync3(
18938
+ "git",
18939
+ ["merge-file", "-p", "--diff3", o, b, t],
18940
+ {
18941
+ encoding: "utf8",
18942
+ maxBuffer: 32 * 1024 * 1024
18943
+ }
18944
+ );
18945
+ if (res.status === 0) {
18946
+ return { ok: true, merged: res.stdout };
18947
+ }
18948
+ return { ok: false, merged: "" };
18949
+ } finally {
18950
+ fs23.rmSync(dir, { recursive: true, force: true });
18951
+ }
18952
+ }
18953
+ var skip = (path37, skipReason) => ({ path: path37, kind: "skip", skipReason });
18954
+ function classifyCandidate(c) {
18955
+ const { path: path37, base, ours, theirs } = c;
18956
+ if (theirs === base) return { path: path37, kind: "noop" };
18957
+ if (base === null) {
18958
+ if (theirs === null) return { path: path37, kind: "noop" };
18959
+ if (ours === null) return { path: path37, kind: "add", content: theirs };
18960
+ if (ours === theirs) return { path: path37, kind: "noop" };
18961
+ return skip(path37, "you already have a file at this path");
18962
+ }
18963
+ if (ours === null) {
18964
+ return theirs === null ? { path: path37, kind: "noop" } : skip(path37, "you removed this file; upstream changed it");
18965
+ }
18966
+ if (theirs === null) {
18967
+ return ours === base ? { path: path37, kind: "delete" } : skip(path37, "you edited this file; upstream removed it");
18968
+ }
18969
+ if (ours === base) return { path: path37, kind: "update", content: theirs };
18970
+ const m = mergeThreeWay(ours, base, theirs);
18971
+ return m.ok ? { path: path37, kind: "update", content: m.merged } : skip(path37, "your edits conflict with the update");
18972
+ }
18973
+
18974
+ // src/lib/seed-pr-body.ts
18975
+ function composeSeedPrTitle(s) {
18976
+ return `Update your ${s.advisorLabel}'s built-in skills & guides (${s.version})`;
18977
+ }
18978
+ var basename = (p) => p.split("/").filter(Boolean).slice(-2).join("/");
18979
+ function composeSeedPrBody(s) {
18980
+ const lines = [];
18981
+ lines.push(`This refreshes the skills and reference guides that came built into your ${s.advisorLabel}, bringing them up to date with platform release **${s.version}**.`);
18982
+ lines.push("");
18983
+ if (s.updated.length || s.added.length || s.deleted.length) {
18984
+ lines.push("**What changed**");
18985
+ if (s.updated.length) lines.push(`- \u270F\uFE0F Updated ${s.updated.length.toString()}: ${s.updated.map(basename).join(", ")}`);
18986
+ if (s.added.length) lines.push(`- \u2795 Added ${s.added.length.toString()}: ${s.added.map(basename).join(", ")}`);
18987
+ if (s.deleted.length) lines.push(`- \u{1F5D1}\uFE0F Removed ${s.deleted.length.toString()}: ${s.deleted.map(basename).join(", ")}`);
18988
+ lines.push("");
18989
+ }
18990
+ lines.push("**Your own content is untouched.** Only built-in files change here \u2014 nothing you or your advisor wrote (your memory, your inbox, skills your advisor learned on its own) is modified.");
18991
+ if (s.skipped.length) {
18992
+ lines.push("");
18993
+ lines.push(`**${s.skipped.length.toString()} file(s) were left as-is** because you've customized them and we won't overwrite your edits. To take an update anyway, open that file in the latest release and copy the changes over by hand:`);
18994
+ for (const sk of s.skipped) lines.push(`- \`${sk.path}\` \u2014 ${sk.skipReason}`);
18995
+ }
18996
+ lines.push("");
18997
+ lines.push("Your advisor's review workflow will check this PR automatically. Merge whenever you're ready.");
18998
+ lines.push("");
18999
+ lines.push(`\u2014 Opened by the m8t platform \xB7 release ${s.version}${s.notesUrl ? ` \xB7 [release notes](${s.notesUrl})` : ""}`);
19000
+ return lines.join("\n");
19001
+ }
19002
+
19003
+ // src/lib/seed-refresh.ts
19004
+ function readIf(dir, rel) {
19005
+ if (!dir) return null;
19006
+ const abs = path23.join(dir, rel);
19007
+ return fs24.existsSync(abs) ? fs24.readFileSync(abs, "utf8") : null;
19008
+ }
19009
+ async function assembleCandidates(args) {
19010
+ const paths = /* @__PURE__ */ new Set([...args.lineage?.ownedPaths ?? [], ...args.trees.ownedAtTheirs]);
19011
+ const out = [];
19012
+ for (const p of [...paths].sort((a, b) => a.localeCompare(b))) {
19013
+ out.push({ path: p, base: readIf(args.trees.baseDir, p), theirs: readIf(args.trees.theirsDir, p), ours: await args.source.readOurs(p) });
19014
+ }
19015
+ return out;
19016
+ }
19017
+ function assertNoDanglingIndexRefs(finalPaths, repoHas, indexBodies) {
19018
+ const refRe = /skills\/[^)\s]+\/SKILL\.md/g;
19019
+ for (const idx of indexBodies) {
19020
+ for (const m of idx.body.matchAll(refRe)) {
19021
+ const ref = m[0];
19022
+ if (!finalPaths.has(ref) && !repoHas(ref)) {
19023
+ throw new LocalCliError({
19024
+ code: "PLATFORM_SEED_INDEX_DANGLING",
19025
+ message: `Refusing to open a PR: ${idx.path} would reference a missing skill '${ref}'. The brain loader only opens indexed paths.`
19026
+ });
19027
+ }
19028
+ }
19029
+ }
19030
+ }
19031
+ async function refreshBrainRepo(args) {
19032
+ const cands = await assembleCandidates({ lineage: args.lineage, trees: args.trees, source: args.source, seedName: args.seedName });
19033
+ const outcomes = cands.map(classifyCandidate);
19034
+ const files = [];
19035
+ const updated = [];
19036
+ const added = [];
19037
+ const deleted = [];
19038
+ const skipped = [];
19039
+ const indexBodies = [];
19040
+ const finalPaths = /* @__PURE__ */ new Set();
19041
+ for (const o of outcomes) {
19042
+ if (o.path === SEED_MANIFEST_PATH) continue;
19043
+ if (o.kind === "update") {
19044
+ files.push({ path: o.path, content: o.content ?? "" });
19045
+ updated.push(o.path);
19046
+ finalPaths.add(o.path);
19047
+ } else if (o.kind === "add") {
19048
+ files.push({ path: o.path, content: o.content ?? "" });
19049
+ added.push(o.path);
19050
+ finalPaths.add(o.path);
19051
+ } else if (o.kind === "delete") {
19052
+ files.push({ path: o.path, delete: true });
19053
+ deleted.push(o.path);
19054
+ } else if (o.kind === "skip") {
19055
+ skipped.push({ path: o.path, skipReason: o.skipReason ?? "left as-is" });
19056
+ }
19057
+ if (o.path.endsWith("/_index.md") && (o.kind === "update" || o.kind === "add")) {
19058
+ indexBodies.push({ path: o.path, body: o.content ?? "" });
19059
+ }
19060
+ }
19061
+ const contentChanged = updated.length + added.length + deleted.length > 0;
19062
+ if (!contentChanged) {
19063
+ return { repo: args.repo, prUrl: null, action: "noop", updated, added, deleted, skipped };
19064
+ }
19065
+ const ownedNow = new Set(args.trees.ownedAtTheirs);
19066
+ assertNoDanglingIndexRefs(finalPaths, (p) => ownedNow.has(p), indexBodies);
19067
+ const newOwned = deriveOwnedPaths(args.trees.theirsDir);
19068
+ const nextManifest = upsertLineage(
19069
+ args.priorManifest ?? (args.lineage ? { schemaVersion: 1, seeds: [args.lineage] } : null),
19070
+ {
19071
+ name: args.seedName,
19072
+ refreshedToCommit: args.targetCommit ?? "",
19073
+ refreshedToVersion: args.version,
19074
+ ownedPaths: newOwned
19075
+ }
19076
+ );
19077
+ files.push({ path: SEED_MANIFEST_PATH, content: serializeSeedManifest(nextManifest) });
19078
+ for (const sb of args.supersedeBranches ?? []) {
19079
+ if (sb === args.branch) continue;
19080
+ const stale = await args.api.findOpenPr(sb);
19081
+ if (stale) await args.api.closePr(stale.number, `superseded by the newer update on \`${args.branch}\`.`);
19082
+ }
19083
+ const opened = await args.api.findOpenPr(args.branch);
19084
+ const headSha = await args.api.getHeadSha(args.base);
19085
+ await args.api.commitFromBase(files, args.branch, headSha, `chore(seed): refresh built-in skills & guides (${args.version})`);
19086
+ const summary = {
19087
+ advisorLabel: args.advisorLabel,
19088
+ version: args.version,
19089
+ notesUrl: args.notesUrl,
19090
+ repo: args.repo,
19091
+ updated,
19092
+ added,
19093
+ deleted,
19094
+ skipped
19095
+ };
19096
+ const title = composeSeedPrTitle(summary);
19097
+ const body = composeSeedPrBody(summary);
19098
+ if (opened) {
19099
+ await args.api.updatePr(opened.number, title, body);
19100
+ return { repo: args.repo, prUrl: opened.htmlUrl, action: "updated", updated, added, deleted, skipped };
19101
+ }
19102
+ const pr = await args.api.openPr(args.branch, args.base, title, body);
19103
+ return { repo: args.repo, prUrl: pr.htmlUrl, action: args.lineage ? "opened" : "bootstrapped", updated, added, deleted, skipped };
19104
+ }
19105
+
19106
+ // src/lib/seed-refresh-driver.ts
19107
+ var PERSONA_SEED_MAP = {
19108
+ "startup-advisor": "startup-advisor",
19109
+ "azure-advisor": "azure-advisor"
19110
+ };
19111
+ var ADVISOR_LABEL = {
19112
+ "startup-advisor": "Startup Advisor",
19113
+ "azure-advisor": "Azure Advisor"
19114
+ };
19115
+ function dedupeBrainRepos(agents) {
19116
+ const byRepo = /* @__PURE__ */ new Map();
19117
+ for (const a of agents) {
19118
+ const raw = a.metadata.brain;
19119
+ if (!raw) continue;
19120
+ let repo;
19121
+ let installationId;
19122
+ try {
19123
+ const parsed = JSON.parse(raw);
19124
+ repo = parsed.repo;
19125
+ installationId = parsed.installationId;
19126
+ } catch {
19127
+ repo = void 0;
19128
+ installationId = void 0;
19129
+ }
19130
+ if (!repo || !installationId) continue;
19131
+ const seedName = a.metadata.persona ? PERSONA_SEED_MAP[a.metadata.persona] ?? null : null;
19132
+ if (!byRepo.has(repo)) byRepo.set(repo, { repo, seedName, installationId });
19133
+ }
19134
+ return [...byRepo.values()];
19135
+ }
19136
+ function seedContentDir(contentRoot, manifest, seedName) {
19137
+ const items = manifest.components.brainSeeds.items;
19138
+ if (!Object.hasOwn(items, seedName)) return null;
19139
+ return path24.join(contentRoot, items[seedName].path);
19140
+ }
19141
+ function bindRepoApi(token, repo) {
19142
+ return {
19143
+ getDefaultBranch: () => Promise.resolve("main"),
19144
+ getHeadSha: async (branch) => {
19145
+ const res = await fetch(`https://api.github.com/repos/${repo}/git/ref/heads/${branch}`, {
19146
+ headers: { Authorization: `token ${token}`, Accept: "application/vnd.github+json", "User-Agent": "m8t" }
19147
+ });
19148
+ const j = await res.json();
19149
+ return j.object?.sha ?? "";
19150
+ },
19151
+ findOpenPr: (head) => findOpenPr({ token, repo, head }),
19152
+ commitFromBase: (files, branch, baseSha, message) => commitFilesFromBaseViaApp({ token, repo, branch, baseSha, files, message }),
19153
+ openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
19154
+ updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
19155
+ closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
19156
+ readFileAtRef: (path37, ref) => readRepoFileViaApp({ token, repo, path: path37, ref })
19157
+ };
19158
+ }
19159
+ async function runSeedRefresh(args) {
19160
+ const targetCommit = args.manifest.platform.commit;
19161
+ const theirsRoot = await resolveReleaseContent(args.manifest, {});
19162
+ const repos = dedupeBrainRepos(args.agents);
19163
+ const results = [];
19164
+ let delivered = 0;
19165
+ let noop2 = 0;
19166
+ let failed = 0;
19167
+ for (const ref of repos) {
19168
+ try {
19169
+ const minted = await mintInstallationToken({
19170
+ credential: args.credential,
19171
+ kvUri: args.kvUri,
19172
+ installationId: ref.installationId,
19173
+ repository: ref.repo,
19174
+ permissions: { contents: "write", pull_requests: "write" }
19175
+ });
19176
+ const token = minted.token;
19177
+ const probe = appRepoProbe({ token, repo: ref.repo });
19178
+ const state = await probeRepoState(probe);
19179
+ if (state !== "m8t-brain") {
19180
+ args.onWarn?.(`${ref.repo}: not a reachable m8t brain (${state}) \u2014 skipped.`);
19181
+ continue;
19182
+ }
19183
+ const markerText = await readRepoFileViaApp({ token, repo: ref.repo, path: SEED_MANIFEST_PATH });
19184
+ const marker = parseSeedManifest(markerText);
19185
+ const seedName = ref.seedName ?? marker?.seeds[0]?.name ?? null;
19186
+ if (!seedName) {
19187
+ args.onWarn?.(`${ref.repo}: cannot determine seed lineage \u2014 skipped.`);
19188
+ continue;
19189
+ }
19190
+ const theirsDir = seedContentDir(theirsRoot, args.manifest, seedName);
19191
+ if (!theirsDir) {
19192
+ args.onWarn?.(`${ref.repo}: seed '${seedName}' is not in this release \u2014 skipped.`);
19193
+ continue;
19194
+ }
19195
+ const advisorLabel = ADVISOR_LABEL[seedName] ?? seedName;
19196
+ let lineage = marker?.seeds.find((s) => s.name === seedName) ?? null;
19197
+ let baseDir = null;
19198
+ if (lineage) {
19199
+ try {
19200
+ const baseRoot = await resolveReleaseContent(
19201
+ { ...args.manifest, platform: { ...args.manifest.platform, commit: lineage.refreshedToCommit } },
19202
+ {}
19203
+ );
19204
+ baseDir = seedContentDir(baseRoot, args.manifest, seedName);
19205
+ } catch {
19206
+ baseDir = null;
19207
+ args.onWarn?.(`${ref.repo}: base rev ${lineage.refreshedToCommit} unfetchable \u2014 falling back to marker-only re-adopt.`);
19208
+ lineage = null;
19209
+ }
19210
+ } else {
19211
+ const initSha = await getInitialSeedCommitSha({ token, repo: ref.repo });
19212
+ if (initSha) {
19213
+ try {
19214
+ const baseRoot = await resolveReleaseContent(
19215
+ { ...args.manifest, platform: { ...args.manifest.platform, commit: initSha } },
19216
+ {}
19217
+ );
19218
+ baseDir = seedContentDir(baseRoot, args.manifest, seedName);
19219
+ lineage = baseDir ? { name: seedName, refreshedToCommit: initSha, refreshedToVersion: "(bootstrap)", ownedPaths: deriveOwnedPaths(baseDir) } : null;
19220
+ } catch {
19221
+ baseDir = null;
19222
+ }
19223
+ }
19224
+ }
19225
+ const trees = { baseDir, theirsDir, ownedAtTheirs: deriveOwnedPaths(theirsDir) };
19226
+ const source = { readOurs: (p) => readRepoFileViaApp({ token, repo: ref.repo, path: p }) };
19227
+ const res = await refreshBrainRepo({
19228
+ repo: ref.repo,
19229
+ branch: `m8t/seed-update-${args.version}`,
19230
+ base: "main",
19231
+ version: args.version,
19232
+ notesUrl: args.manifest.platform.notesUrl,
19233
+ advisorLabel,
19234
+ seedName,
19235
+ lineage,
19236
+ trees,
19237
+ source,
19238
+ api: bindRepoApi(token, ref.repo),
19239
+ targetCommit,
19240
+ priorManifest: marker
19241
+ });
19242
+ results.push(res);
19243
+ if (res.action === "noop") noop2++;
19244
+ else delivered++;
19245
+ args.onProgress?.(`${ref.repo}: ${res.action}${res.prUrl ? ` \u2192 ${res.prUrl}` : ""}`);
19246
+ } catch (e) {
19247
+ failed++;
19248
+ args.onWarn?.(`${ref.repo}: seed refresh failed \u2014 ${e.message}; skipped this repo.`);
19249
+ continue;
19250
+ }
19251
+ }
19252
+ return { delivered, noop: noop2, failed, results };
19253
+ }
19254
+
19255
+ // src/lib/platform-converge-cli.ts
18676
19256
  var HOSTED_AGENT_PERSONA_DIR = {
18677
19257
  codingAgent: "coding-agent",
18678
19258
  azureExecutor: "azure-executor"
@@ -18686,7 +19266,7 @@ async function buildConvergeDeps(args) {
18686
19266
  const persona = a.metadata.persona;
18687
19267
  if (persona) discovered[persona] = a.name;
18688
19268
  }
18689
- const loaderPath = path21.join(args.contentDir, "targets/foundry/brain-loader.md");
19269
+ const loaderPath = path25.join(args.contentDir, "targets/foundry/brain-loader.md");
18690
19270
  return {
18691
19271
  async applyPersona(a, ctx) {
18692
19272
  const personaName = a.personaName;
@@ -18702,7 +19282,7 @@ async function buildConvergeDeps(args) {
18702
19282
  return applyPersona(a, ctx, {
18703
19283
  agentName,
18704
19284
  project: args.project,
18705
- personaDir: path21.join(ctx.contentDir, personaItem.path),
19285
+ personaDir: path25.join(ctx.contentDir, personaItem.path),
18706
19286
  loaderPath
18707
19287
  });
18708
19288
  },
@@ -18804,6 +19384,31 @@ async function buildConvergeDeps(args) {
18804
19384
  },
18805
19385
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
18806
19386
  });
19387
+ },
19388
+ async applySeedRefresh(_a3, ctx) {
19389
+ let kvUri;
19390
+ try {
19391
+ kvUri = discoverKvUri(process.env);
19392
+ } catch {
19393
+ kvUri = void 0;
19394
+ }
19395
+ if (!kvUri) {
19396
+ warn("brainSeeds: no Key Vault URI (set AZURE_KEYVAULT_URI) \u2014 skipping seed refresh.");
19397
+ return { delivered: 0, noop: 0, converged: false };
19398
+ }
19399
+ const { delivered, noop: noop2, failed } = await runSeedRefresh({
19400
+ credential: args.credential,
19401
+ kvUri,
19402
+ manifest: ctx.manifest,
19403
+ agents: discoveredAgents,
19404
+ version: ctx.manifest.platform.version,
19405
+ onProgress: args.onProgress,
19406
+ onWarn: args.onWarn
19407
+ });
19408
+ if (failed > 0) {
19409
+ warn(`brainSeeds: ${failed.toString()} repo(s) failed to receive a seed update \u2014 re-run 'm8t platform update --only brainSeeds' to retry.`);
19410
+ }
19411
+ return { delivered, noop: noop2, converged: failed === 0 };
18807
19412
  }
18808
19413
  };
18809
19414
  }
@@ -18843,7 +19448,7 @@ var PlatformUpdateCommand = class extends M8tCommand {
18843
19448
  description: "Pin the platform version to converge to (defaults to the current release channel)."
18844
19449
  });
18845
19450
  only = Option30.String("--only", {
18846
- description: "Converge only one component: infra | gateway | codingAgent | azureExecutor | personas."
19451
+ description: "Converge only one component: infra | gateway | codingAgent | azureExecutor | personas | brainSeeds."
18847
19452
  });
18848
19453
  check = Option30.Boolean("--check", false);
18849
19454
  yes = Option30.Boolean("--yes", false);
@@ -19057,15 +19662,15 @@ async function patchApplyRequest(client, mutate) {
19057
19662
  }
19058
19663
 
19059
19664
  // src/lib/platform-self-update.ts
19060
- import * as fs22 from "fs";
19061
- import * as path22 from "path";
19665
+ import * as fs25 from "fs";
19666
+ import * as path26 from "path";
19062
19667
  function toVTag2(v) {
19063
19668
  return v.startsWith("v") ? v : `v${v}`;
19064
19669
  }
19065
19670
  function readBakedReleaseVersion(repoRoot) {
19066
19671
  if (!repoRoot) return null;
19067
19672
  try {
19068
- const raw = fs22.readFileSync(path22.join(repoRoot, "installer", "version.json"), "utf8");
19673
+ const raw = fs25.readFileSync(path26.join(repoRoot, "installer", "version.json"), "utf8");
19069
19674
  const v = JSON.parse(raw).version;
19070
19675
  return typeof v === "string" && v.length > 0 ? v : null;
19071
19676
  } catch {
@@ -20020,7 +20625,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
20020
20625
  init_errors();
20021
20626
 
20022
20627
  // src/lib/whatif.ts
20023
- import * as path23 from "path";
20628
+ import * as path27 from "path";
20024
20629
  function deriveResourceType(resourceId) {
20025
20630
  if (resourceId.startsWith("[")) return "";
20026
20631
  const afterProviders = resourceId.split("/providers/")[1];
@@ -20031,7 +20636,7 @@ function deriveResourceType(resourceId) {
20031
20636
  return parts.join("/");
20032
20637
  }
20033
20638
  async function runWhatIf(opts) {
20034
- const bicepPath = path23.join(opts.repoRoot, "deploy", "main.bicep");
20639
+ const bicepPath = path27.join(opts.repoRoot, "deploy", "main.bicep");
20035
20640
  const out = await runAz([
20036
20641
  "deployment",
20037
20642
  "group",
@@ -20096,9 +20701,9 @@ function flattenDelta(delta, prefix = "") {
20096
20701
  const out = [];
20097
20702
  for (const d of delta) {
20098
20703
  const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
20099
- const path33 = prefix ? `${prefix}${segment}` : d.path;
20100
- if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path33));
20101
- else out.push({ ...d, path: path33 });
20704
+ const path37 = prefix ? `${prefix}${segment}` : d.path;
20705
+ if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path37));
20706
+ else out.push({ ...d, path: path37 });
20102
20707
  }
20103
20708
  return out;
20104
20709
  }
@@ -20320,7 +20925,7 @@ var DeployCommand = class extends M8tCommand {
20320
20925
  };
20321
20926
 
20322
20927
  // src/commands/eval/skill.ts
20323
- import { spawnSync as spawnSync3 } from "child_process";
20928
+ import { spawnSync as spawnSync4 } from "child_process";
20324
20929
  import { Command as Command37, Option as Option34 } from "clipanion";
20325
20930
  init_errors();
20326
20931
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
@@ -20372,7 +20977,7 @@ var EvalSkillCommand = class extends M8tCommand {
20372
20977
  const args = ["skill", candidate2, "--skills-dir", skillsDir, "--json"];
20373
20978
  if (this.noJudge === true) args.push("--no-judge");
20374
20979
  if (typeof this.deployment === "string") args.push("--deployment", this.deployment);
20375
- const res = spawnSync3(bin, args, { encoding: "utf-8" });
20980
+ const res = spawnSync4(bin, args, { encoding: "utf-8" });
20376
20981
  if (res.error) {
20377
20982
  throw new LocalCliError({
20378
20983
  code: "BRAIN_EVAL_SPAWN",
@@ -20409,9 +21014,9 @@ var EvalSkillCommand = class extends M8tCommand {
20409
21014
  };
20410
21015
 
20411
21016
  // src/commands/eval/exam.ts
20412
- import { spawnSync as spawnSync4 } from "child_process";
20413
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync16, existsSync as existsSync13, readdirSync } from "fs";
20414
- import { join as join23 } from "path";
21017
+ import { spawnSync as spawnSync5 } from "child_process";
21018
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync17, existsSync as existsSync15, readdirSync as readdirSync2 } from "fs";
21019
+ import { join as join27 } from "path";
20415
21020
  import { Command as Command38, Option as Option35 } from "clipanion";
20416
21021
  init_errors();
20417
21022
  init_esm();
@@ -20593,11 +21198,11 @@ function resolveRefereeHome(refereeHomeOut, env) {
20593
21198
  return null;
20594
21199
  }
20595
21200
  function resolveTaskSetDir(base, worker, version) {
20596
- if (existsSync13(join23(base, version))) return version;
21201
+ if (existsSync15(join27(base, version))) return version;
20597
21202
  const prefixed = `${worker}-${version}`;
20598
- if (existsSync13(join23(base, prefixed))) return prefixed;
20599
- if (version === "latest" && existsSync13(base)) {
20600
- const dirs = readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
21203
+ if (existsSync15(join27(base, prefixed))) return prefixed;
21204
+ if (version === "latest" && existsSync15(base)) {
21205
+ const dirs = readdirSync2(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
20601
21206
  if (dirs.length > 0) return dirs[dirs.length - 1];
20602
21207
  }
20603
21208
  return version;
@@ -20611,11 +21216,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
20611
21216
  message: "cannot resolve the referee home to read the sealed-task manifest \u2014 set $REFEREE_HOME (mirroring the Python runner) so the feed can be redacted; refusing to emit a possibly-unredacted feed (Law-1)"
20612
21217
  });
20613
21218
  }
20614
- const versionDir = resolveTaskSetDir(join23(home, "tasksets", worker), worker, version);
20615
- const manifestPath = join23(home, "tasksets", worker, versionDir, "manifest.yaml");
21219
+ const versionDir = resolveTaskSetDir(join27(home, "tasksets", worker), worker, version);
21220
+ const manifestPath = join27(home, "tasksets", worker, versionDir, "manifest.yaml");
20616
21221
  let text;
20617
21222
  try {
20618
- text = readFileSync16(manifestPath, "utf-8");
21223
+ text = readFileSync17(manifestPath, "utf-8");
20619
21224
  } catch (e) {
20620
21225
  throw new LocalCliError({
20621
21226
  code: "EXAM_REDACTION_UNSAFE",
@@ -20722,7 +21327,7 @@ var EvalExamCommand = class extends M8tCommand {
20722
21327
  return 0;
20723
21328
  }
20724
21329
  const bin = process.env.BRAIN_EXAM_BIN ?? "brain-exam";
20725
- const res = spawnSync4(bin, ["run", "--plan", "-", "--json"], { encoding: "utf-8", input: renderJson(plan) });
21330
+ const res = spawnSync5(bin, ["run", "--plan", "-", "--json"], { encoding: "utf-8", input: renderJson(plan) });
20726
21331
  if (res.error) {
20727
21332
  throw new LocalCliError({
20728
21333
  code: "EXAM_SPAWN",
@@ -20735,10 +21340,10 @@ var EvalExamCommand = class extends M8tCommand {
20735
21340
  const verdict = parseExamVerdict(res.stdout);
20736
21341
  if (out !== void 0) {
20737
21342
  const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
20738
- mkdirSync4(out, { recursive: true });
20739
- writeFileSync4(join23(out, "verdict.json"), JSON.stringify(verdict, null, 2));
21343
+ mkdirSync5(out, { recursive: true });
21344
+ writeFileSync6(join27(out, "verdict.json"), JSON.stringify(verdict, null, 2));
20740
21345
  const feed = redactForFeed(verdict, sealedTaskIds);
20741
- writeFileSync4(join23(out, "feed.json"), JSON.stringify(feed, null, 2));
21346
+ writeFileSync6(join27(out, "feed.json"), JSON.stringify(feed, null, 2));
20742
21347
  }
20743
21348
  if (mode === "json") {
20744
21349
  this.context.stdout.write(renderJson(verdict) + "\n");
@@ -21001,9 +21606,9 @@ var StatusCommand = class extends M8tCommand {
21001
21606
  // src/commands/doctor.ts
21002
21607
  import { Command as Command42, Option as Option39 } from "clipanion";
21003
21608
  import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
21004
- import * as fs23 from "fs";
21005
- import * as os10 from "os";
21006
- import * as path24 from "path";
21609
+ import * as fs26 from "fs";
21610
+ import * as os11 from "os";
21611
+ import * as path28 from "path";
21007
21612
 
21008
21613
  // src/lib/doctor-checks.ts
21009
21614
  function checkAzSignedIn(az) {
@@ -21228,24 +21833,24 @@ async function resolveAccountLocation(accountId) {
21228
21833
  }
21229
21834
  }
21230
21835
  function probeRepoRoot() {
21231
- const marker = path24.join(os10.homedir(), ".m8t-stack", "repo-root");
21232
- if (!fs23.existsSync(marker)) {
21836
+ const marker = path28.join(os11.homedir(), ".m8t-stack", "repo-root");
21837
+ if (!fs26.existsSync(marker)) {
21233
21838
  return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
21234
21839
  }
21235
21840
  let repoPath;
21236
21841
  try {
21237
- repoPath = fs23.readFileSync(marker, "utf8").trim();
21842
+ repoPath = fs26.readFileSync(marker, "utf8").trim();
21238
21843
  } catch {
21239
21844
  return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
21240
21845
  }
21241
21846
  if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
21242
21847
  let isDir = false;
21243
21848
  try {
21244
- isDir = fs23.statSync(repoPath).isDirectory();
21849
+ isDir = fs26.statSync(repoPath).isDirectory();
21245
21850
  } catch {
21246
21851
  isDir = false;
21247
21852
  }
21248
- const isGitCheckout = isDir && fs23.existsSync(path24.join(repoPath, ".git"));
21853
+ const isGitCheckout = isDir && fs26.existsSync(path28.join(repoPath, ".git"));
21249
21854
  return { markerExists: true, path: repoPath, isDir, isGitCheckout };
21250
21855
  }
21251
21856
  var DoctorCommand = class extends M8tCommand {
@@ -21374,15 +21979,15 @@ var DoctorCommand = class extends M8tCommand {
21374
21979
  import { Command as Command43, Option as Option40 } from "clipanion";
21375
21980
 
21376
21981
  // src/lib/profiles.ts
21377
- import * as fs24 from "fs/promises";
21378
- import * as path25 from "path";
21982
+ import * as fs27 from "fs/promises";
21983
+ import * as path29 from "path";
21379
21984
  import { parse as parseYaml11, stringify as stringifyYaml4 } from "yaml";
21380
21985
  function profilesDir() {
21381
21986
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21382
- return path25.join(home, ".m8t-stack", "profiles");
21987
+ return path29.join(home, ".m8t-stack", "profiles");
21383
21988
  }
21384
21989
  function getProfilePath(name) {
21385
- return path25.join(profilesDir(), `${path25.basename(name)}.yaml`);
21990
+ return path29.join(profilesDir(), `${path29.basename(name)}.yaml`);
21386
21991
  }
21387
21992
  function slugifyTenant(domainOrId) {
21388
21993
  const base = domainOrId.split(".")[0].toLowerCase();
@@ -21391,7 +21996,7 @@ function slugifyTenant(domainOrId) {
21391
21996
  }
21392
21997
  async function listProfiles() {
21393
21998
  try {
21394
- const entries = await fs24.readdir(profilesDir());
21999
+ const entries = await fs27.readdir(profilesDir());
21395
22000
  return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
21396
22001
  } catch (e) {
21397
22002
  if (e.code === "ENOENT") return [];
@@ -21400,7 +22005,7 @@ async function listProfiles() {
21400
22005
  }
21401
22006
  async function readProfile(name) {
21402
22007
  try {
21403
- const raw = await fs24.readFile(getProfilePath(name), "utf8");
22008
+ const raw = await fs27.readFile(getProfilePath(name), "utf8");
21404
22009
  const parsed = parseYaml11(raw);
21405
22010
  if (!parsed || typeof parsed !== "object") return null;
21406
22011
  return parsed;
@@ -21411,14 +22016,14 @@ async function readProfile(name) {
21411
22016
  }
21412
22017
  async function exists(p) {
21413
22018
  try {
21414
- await fs24.stat(p);
22019
+ await fs27.stat(p);
21415
22020
  return true;
21416
22021
  } catch {
21417
22022
  return false;
21418
22023
  }
21419
22024
  }
21420
22025
  async function saveProfile(p) {
21421
- await fs24.mkdir(profilesDir(), { recursive: true });
22026
+ await fs27.mkdir(profilesDir(), { recursive: true });
21422
22027
  let name = p.name;
21423
22028
  let n = 2;
21424
22029
  while (await exists(getProfilePath(name))) {
@@ -21427,8 +22032,8 @@ async function saveProfile(p) {
21427
22032
  }
21428
22033
  const target = getProfilePath(name);
21429
22034
  const tmp = `${target}.tmp`;
21430
- await fs24.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
21431
- await fs24.rename(tmp, target);
22035
+ await fs27.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
22036
+ await fs27.rename(tmp, target);
21432
22037
  return name;
21433
22038
  }
21434
22039
 
@@ -21814,6 +22419,8 @@ function odataFilter(worker, f) {
21814
22419
  parts.unshift(`PartitionKey eq '${esc2(worker)}'`);
21815
22420
  if (f.source !== "all")
21816
22421
  parts.push(`source eq '${esc2(f.source)}'`);
22422
+ if (f.foundryConversationId)
22423
+ parts.push(`foundryConversationId eq '${esc2(f.foundryConversationId)}'`);
21817
22424
  return parts.join(" and ");
21818
22425
  }
21819
22426
  async function fetchLedgerRows(filters, client) {
@@ -21965,7 +22572,7 @@ init_errors();
21965
22572
  // ../../packages/brain/engine/dist/esm/types.js
21966
22573
  var ENGINE_VERSION = "0.1.0";
21967
22574
  var DEFAULT_SAFETY_LAG_SECONDS = 3600;
21968
- var SOURCES = ["webapp", "a2a", "mcp", "telegram"];
22575
+ var SOURCES = ["webapp", "a2a", "mcp", "telegram", "voice-relay"];
21969
22576
  var WORKER_ALIASES = { "azure-advisor": "azzy" };
21970
22577
  function canonicalWorker(pk) {
21971
22578
  const lower = pk.toLowerCase();
@@ -22029,9 +22636,11 @@ var TableCursor = class {
22029
22636
  throw e;
22030
22637
  }
22031
22638
  const watermarks = row.watermarks ? JSON.parse(row.watermarks) : {};
22639
+ const fetchFailures = row.fetchFailures ? JSON.parse(row.fetchFailures) : void 0;
22032
22640
  return {
22033
22641
  worker: canonical,
22034
22642
  watermarks,
22643
+ ...fetchFailures && Object.keys(fetchFailures).length > 0 ? { fetchFailures } : {},
22035
22644
  safetyLagSeconds: row.safetyLagSeconds ?? DEFAULT_SAFETY_LAG_SECONDS,
22036
22645
  engineVersion: row.engineVersion ?? ENGINE_VERSION,
22037
22646
  ...row.lastDreamAt ? { lastDreamAt: row.lastDreamAt } : {}
@@ -22054,9 +22663,12 @@ var TableCursor = class {
22054
22663
  safetyLagSeconds: cursor.safetyLagSeconds,
22055
22664
  engineVersion: cursor.engineVersion
22056
22665
  };
22666
+ if (cursor.fetchFailures && Object.keys(cursor.fetchFailures).length > 0) {
22667
+ entity.fetchFailures = JSON.stringify(cursor.fetchFailures);
22668
+ }
22057
22669
  if (cursor.lastDreamAt)
22058
22670
  entity.lastDreamAt = cursor.lastDreamAt;
22059
- await this.client.upsertEntity(entity);
22671
+ await this.client.upsertEntity(entity, "Replace");
22060
22672
  }
22061
22673
  async ensureTable() {
22062
22674
  try {
@@ -22092,9 +22704,22 @@ function computeWatermark(rows, now, safetyLagSeconds) {
22092
22704
  }
22093
22705
  async function commitCursorAdvance(plan, cursor) {
22094
22706
  const prior = await cursor.read(plan.worker);
22707
+ const watermarks = {};
22708
+ for (const [pk, watermark] of Object.entries(plan.watermarks)) {
22709
+ const priorWatermark = Object.hasOwn(prior.watermarks, pk) ? prior.watermarks[pk] : void 0;
22710
+ if (priorWatermark && watermark.ts <= priorWatermark.ts) {
22711
+ watermarks[pk] = {
22712
+ ts: watermark.ts,
22713
+ consumedRowKeys: [.../* @__PURE__ */ new Set([...priorWatermark.consumedRowKeys, ...watermark.consumedRowKeys])]
22714
+ };
22715
+ } else {
22716
+ watermarks[pk] = watermark;
22717
+ }
22718
+ }
22095
22719
  const next = {
22096
22720
  worker: plan.worker,
22097
- watermarks: plan.watermarks,
22721
+ watermarks,
22722
+ ...plan.fetchFailures && Object.keys(plan.fetchFailures).length > 0 ? { fetchFailures: plan.fetchFailures } : {},
22098
22723
  safetyLagSeconds: prior.safetyLagSeconds || DEFAULT_SAFETY_LAG_SECONDS,
22099
22724
  engineVersion: ENGINE_VERSION,
22100
22725
  lastDreamAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -22348,6 +22973,7 @@ function normalize(group, items, enrichment) {
22348
22973
 
22349
22974
  // ../../packages/brain/engine/dist/esm/pipeline.js
22350
22975
  var isKnownSource = (s) => SOURCES.includes(s);
22976
+ var MAX_CONV_FETCH_ATTEMPTS = 3;
22351
22977
  async function runPipeline(args) {
22352
22978
  const { worker, sources, now, since, reset } = args;
22353
22979
  const monotonicNow = args.monotonicNow ?? Date.now;
@@ -22374,6 +23000,7 @@ async function runPipeline(args) {
22374
23000
  let recovered = 0;
22375
23001
  const consumedRowsByPk = {};
22376
23002
  const failedTsByPk = {};
23003
+ const newFailures = {};
22377
23004
  const window2 = windowFor(rows, now, since);
22378
23005
  for (const g of groups) {
22379
23006
  const pk = g.worker;
@@ -22464,8 +23091,17 @@ async function runPipeline(args) {
22464
23091
  else
22465
23092
  recovered += 1;
22466
23093
  } else {
22467
- skips.addSkip(outcome, sampleId(g), fetchError?.message);
22468
- if (outcome === "conv-fetch-failed" || outcome === "auth-failed") {
23094
+ let lastError = fetchError?.message;
23095
+ if (outcome === "conv-fetch-failed") {
23096
+ const id = conversationId ?? "";
23097
+ const next = (cursor.fetchFailures?.[id] ?? 0) + 1;
23098
+ newFailures[id] = next;
23099
+ if (next >= MAX_CONV_FETCH_ATTEMPTS) {
23100
+ lastError = `terminal after ${String(next)} cycles \u2014 watermark released`;
23101
+ }
23102
+ }
23103
+ skips.addSkip(outcome, sampleId(g), lastError);
23104
+ if (outcome === "auth-failed" || outcome === "conv-fetch-failed" && conversationId && newFailures[conversationId] < MAX_CONV_FETCH_ATTEMPTS) {
22469
23105
  const cur = failedTsByPk[pk];
22470
23106
  if (!cur || g.startedAt < cur)
22471
23107
  failedTsByPk[pk] = g.startedAt;
@@ -22485,7 +23121,7 @@ async function runPipeline(args) {
22485
23121
  latencyMs: Math.round(monotonicNow() - t0)
22486
23122
  };
22487
23123
  assertInvariant(stats, groups.length);
22488
- const advance = buildAdvancePlan(target, cursor, physicalPks, consumedRowsByPk, failedTsByPk, now, safetyLag);
23124
+ const advance = buildAdvancePlan(target, cursor, physicalPks, consumedRowsByPk, failedTsByPk, newFailures, now, safetyLag);
22489
23125
  const out = {
22490
23126
  worker: target,
22491
23127
  window: window2,
@@ -22564,7 +23200,7 @@ function windowFor(rows, now, since) {
22564
23200
  from = r.eventTimestamp;
22565
23201
  return { from, to };
22566
23202
  }
22567
- function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedTsByPk, now, safetyLagSeconds) {
23203
+ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedTsByPk, fetchFailures, now, safetyLagSeconds) {
22568
23204
  const watermarks = {};
22569
23205
  const consumedKeysByPk = {};
22570
23206
  const priorTsByPk = {};
@@ -22588,6 +23224,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
22588
23224
  watermarks,
22589
23225
  consumedRowsByPk: consumedKeysByPk,
22590
23226
  failedTsByPk,
23227
+ ...Object.keys(fetchFailures).length > 0 ? { fetchFailures } : {},
22591
23228
  now: now.toISOString(),
22592
23229
  safetyLagSeconds,
22593
23230
  priorTsByPk
@@ -22597,7 +23234,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
22597
23234
  // ../../packages/brain/engine/dist/esm/dream/writer.js
22598
23235
  var API2 = "https://api.github.com";
22599
23236
  var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
22600
- var isAllowedWritePath = (path33, allowedPrefix) => allowedPrefix.test(path33) && !path33.split("/").includes("..");
23237
+ var isAllowedWritePath = (path37, allowedPrefix) => allowedPrefix.test(path37) && !path37.split("/").includes("..");
22601
23238
  var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
22602
23239
  function parseFrontmatter(content) {
22603
23240
  const m = /^---\n([\s\S]*?)\n---/.exec(content);
@@ -22640,8 +23277,8 @@ function makeGitDataWriter(args) {
22640
23277
  if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
22641
23278
  throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
22642
23279
  }
22643
- async function gh(token, method, path33, body) {
22644
- const res = await doFetch(`${API2}/repos/${repoPath}${path33}`, {
23280
+ async function gh(token, method, path37, body) {
23281
+ const res = await doFetch(`${API2}/repos/${repoPath}${path37}`, {
22645
23282
  method,
22646
23283
  headers: {
22647
23284
  Authorization: `Bearer ${token}`,
@@ -22654,8 +23291,8 @@ function makeGitDataWriter(args) {
22654
23291
  const text = await res.text();
22655
23292
  return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
22656
23293
  }
22657
- async function readFileWith(token, path33) {
22658
- const encodedPath = path33.split("/").map(encodeURIComponent).join("/");
23294
+ async function readFileWith(token, path37) {
23295
+ const encodedPath = path37.split("/").map(encodeURIComponent).join("/");
22659
23296
  const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
22660
23297
  if (r.status === 404)
22661
23298
  return null;
@@ -22671,9 +23308,9 @@ function makeGitDataWriter(args) {
22671
23308
  throw new Error(`fetchTip: HTTP ${String(r.status)}`);
22672
23309
  return r.json.object.sha;
22673
23310
  },
22674
- async readFile(path33) {
23311
+ async readFile(path37) {
22675
23312
  const token = await args.mintToken();
22676
- return readFileWith(token, path33);
23313
+ return readFileWith(token, path37);
22677
23314
  },
22678
23315
  async listMemory() {
22679
23316
  const token = await args.mintToken();
@@ -22682,9 +23319,9 @@ function makeGitDataWriter(args) {
22682
23319
  return [];
22683
23320
  const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
22684
23321
  const out = [];
22685
- for (const path33 of paths) {
22686
- const content = await readFileWith(token, path33);
22687
- out.push({ path: path33, frontmatter: content ? parseFrontmatter(content) : {} });
23322
+ for (const path37 of paths) {
23323
+ const content = await readFileWith(token, path37);
23324
+ out.push({ path: path37, frontmatter: content ? parseFrontmatter(content) : {} });
22688
23325
  }
22689
23326
  return out;
22690
23327
  },
@@ -23123,7 +23760,7 @@ async function loadMemoryContext(brain) {
23123
23760
  return {
23124
23761
  files,
23125
23762
  indexEntries,
23126
- originOf: (path33) => originByPath.get(path33) ?? "worker"
23763
+ originOf: (path37) => originByPath.get(path37) ?? "worker"
23127
23764
  };
23128
23765
  }
23129
23766
 
@@ -23144,14 +23781,14 @@ function checkEvidenceMembership(delta, harvested) {
23144
23781
  }
23145
23782
  var BatchAbort = class extends Error {
23146
23783
  path;
23147
- constructor(path33, message) {
23784
+ constructor(path37, message) {
23148
23785
  super(message);
23149
- this.path = path33;
23786
+ this.path = path37;
23150
23787
  this.name = "BatchAbort";
23151
23788
  }
23152
23789
  };
23153
- var isIndexFile = (path33) => /(^|\/)[^/]*_index\.md$/.test(path33);
23154
- var isMemoryIndex = (path33) => path33 === "memory/MEMORY.md";
23790
+ var isIndexFile = (path37) => /(^|\/)[^/]*_index\.md$/.test(path37);
23791
+ var isMemoryIndex = (path37) => path37 === "memory/MEMORY.md";
23155
23792
  function targetPath(delta) {
23156
23793
  switch (delta.verb) {
23157
23794
  case "new":
@@ -23168,29 +23805,29 @@ function targetPath(delta) {
23168
23805
  }
23169
23806
  async function checkOriginTier(delta, lookup) {
23170
23807
  const evidence = evidenceOf(delta) ?? [];
23171
- const path33 = targetPath(delta);
23172
- if (path33 === null)
23808
+ const path37 = targetPath(delta);
23809
+ if (path37 === null)
23173
23810
  return { ok: true };
23174
- if (isIndexFile(path33) && !isMemoryIndex(path33)) {
23175
- return { ok: false, flagged: { kind: "flagged", path: path33, tension: `refused: ${path33} is an index file (only memory/MEMORY.md is editable)`, evidence } };
23811
+ if (isIndexFile(path37) && !isMemoryIndex(path37)) {
23812
+ return { ok: false, flagged: { kind: "flagged", path: path37, tension: `refused: ${path37} is an index file (only memory/MEMORY.md is editable)`, evidence } };
23176
23813
  }
23177
- const target = await lookup(path33);
23178
- const isStructural = path33.startsWith("references/");
23814
+ const target = await lookup(path37);
23815
+ const isStructural = path37.startsWith("references/");
23179
23816
  const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
23180
23817
  const editable = origin === "worker" || origin === "dream";
23181
23818
  if (editable)
23182
23819
  return { ok: true };
23183
23820
  const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
23184
23821
  if (removesOperatorTarget) {
23185
- throw new BatchAbort(path33, `supersede/retract OLD target ${path33} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
23822
+ throw new BatchAbort(path37, `supersede/retract OLD target ${path37} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
23186
23823
  }
23187
- return { ok: false, flagged: { kind: "flagged", path: path33, tension: `operator-origin (${origin}); flag-only`, evidence } };
23824
+ return { ok: false, flagged: { kind: "flagged", path: path37, tension: `operator-origin (${origin}); flag-only`, evidence } };
23188
23825
  }
23189
23826
  var ALLOWED_MEMORY = /^memory\/.+\.md$/;
23190
23827
  var ALLOWED_INBOX = /^inbox\//;
23191
23828
  var ALLOWED_QUARANTINE = /^quarantine\//;
23192
23829
  var ALLOWED_FLAG = /^memory\//;
23193
- var hasTraversal = (path33) => path33.split("/").includes("..");
23830
+ var hasTraversal = (path37) => path37.split("/").includes("..");
23194
23831
  function checkPathAllowed(delta) {
23195
23832
  const evidence = evidenceOf(delta) ?? [];
23196
23833
  const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
@@ -23297,7 +23934,7 @@ ${inject}
23297
23934
  ---
23298
23935
  `);
23299
23936
  }
23300
- var indexLine = (path33, title, date, tags) => `- \`${path33}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
23937
+ var indexLine = (path37, title, date, tags) => `- \`${path37}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
23301
23938
  function splitIndex(index) {
23302
23939
  const all = index.split("\n");
23303
23940
  const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
@@ -23470,16 +24107,16 @@ function defaultDreamSeams(opts = {}) {
23470
24107
  const mem = await memory(deps.brain);
23471
24108
  const harvested = harvestedIds(input);
23472
24109
  const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
23473
- const lookup = (path33) => Promise.resolve(mem.files.find((f) => f.path === path33) ?? null);
24110
+ const lookup = (path37) => Promise.resolve(mem.files.find((f) => f.path === path37) ?? null);
23474
24111
  const digest = [...pendingRejected];
23475
24112
  let changes = [];
23476
24113
  let indexEdit;
23477
24114
  try {
23478
24115
  for (const raw of deltas) {
23479
24116
  const delta = forcePolicyQuarantine(raw);
23480
- const path33 = checkPathAllowed(delta);
23481
- if (!path33.ok) {
23482
- digest.push(path33.rejected);
24117
+ const path37 = checkPathAllowed(delta);
24118
+ if (!path37.ok) {
24119
+ digest.push(path37.rejected);
23483
24120
  continue;
23484
24121
  }
23485
24122
  const ev = checkEvidenceMembership(delta, harvested);
@@ -23580,6 +24217,7 @@ function narrowAdvance(src, kept) {
23580
24217
  watermarks,
23581
24218
  consumedRowsByPk,
23582
24219
  failedTsByPk: src.failedTsByPk,
24220
+ ...src.fetchFailures ? { fetchFailures: src.fetchFailures } : {},
23583
24221
  now,
23584
24222
  safetyLagSeconds,
23585
24223
  priorTsByPk
@@ -23597,7 +24235,13 @@ function filterAdvance(src, keptRowsByPk) {
23597
24235
  consumedRowsByPk[pk] = (src.consumedRowsByPk[pk] ?? []).filter((rk) => keep.has(rk));
23598
24236
  watermarks[pk] = { ts: wm.ts, consumedRowKeys: wm.consumedRowKeys.filter((rk) => keep.has(rk)) };
23599
24237
  }
23600
- return { worker: src.worker, watermarks, consumedRowsByPk, failedTsByPk: src.failedTsByPk };
24238
+ return {
24239
+ worker: src.worker,
24240
+ watermarks,
24241
+ consumedRowsByPk,
24242
+ failedTsByPk: src.failedTsByPk,
24243
+ ...src.fetchFailures ? { fetchFailures: src.fetchFailures } : {}
24244
+ };
23601
24245
  }
23602
24246
 
23603
24247
  // ../../packages/brain/engine/dist/esm/dream/index.js
@@ -23615,6 +24259,18 @@ async function runDream(input, deps, seams = defaultDreamSeams()) {
23615
24259
  });
23616
24260
  }
23617
24261
  input = capped;
24262
+ if (input.conversations.length === 0) {
24263
+ await commitCursorAdvance(input.advance, deps.cursor);
24264
+ deps.logger.info({
24265
+ at: "runDream",
24266
+ worker: input.worker,
24267
+ terminal: "no-corpus",
24268
+ skipped: input.stats.skipped,
24269
+ advanced: true,
24270
+ skipLedger: input.skipLedger.map((s) => `${s.reason}:${String(s.count)}`)
24271
+ });
24272
+ return { worker: input.worker, applied: [], digest, advanced: true };
24273
+ }
23618
24274
  const proposed = await seams.propose(input, deps);
23619
24275
  digest.push({ kind: "cost", model: proposed.cost.model, inputTokens: proposed.cost.inputTokens, outputTokens: proposed.cost.outputTokens, ms: proposed.cost.ms });
23620
24276
  if (!proposed.deltas) {
@@ -24699,9 +25355,9 @@ ${colors.error(" " + why)}
24699
25355
  }
24700
25356
 
24701
25357
  // src/commands/bootstrap/launch.ts
24702
- import * as fs27 from "fs";
24703
- import * as os13 from "os";
24704
- import * as path28 from "path";
25358
+ import * as fs30 from "fs";
25359
+ import * as os14 from "os";
25360
+ import * as path32 from "path";
24705
25361
  import { Command as Command49, Option as Option46 } from "clipanion";
24706
25362
  init_errors();
24707
25363
 
@@ -24847,9 +25503,9 @@ async function kickInstaller(s) {
24847
25503
 
24848
25504
  // src/lib/bootstrap-status.ts
24849
25505
  import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
24850
- import * as fs25 from "fs/promises";
24851
- import * as os11 from "os";
24852
- import * as path26 from "path";
25506
+ import * as fs28 from "fs/promises";
25507
+ import * as os12 from "os";
25508
+ import * as path30 from "path";
24853
25509
  init_errors();
24854
25510
  var STATUS_CONTAINER = "status";
24855
25511
  var STATUS_BLOB = "status.json";
@@ -24888,7 +25544,7 @@ async function readStatusBlob(opts) {
24888
25544
  cause: e
24889
25545
  });
24890
25546
  }
24891
- const tmpFile = path26.join(os11.tmpdir(), `m8t-status-${randomUUID2()}.json`);
25547
+ const tmpFile = path30.join(os12.tmpdir(), `m8t-status-${randomUUID2()}.json`);
24892
25548
  try {
24893
25549
  await runAz([
24894
25550
  "storage",
@@ -24907,7 +25563,7 @@ async function readStatusBlob(opts) {
24907
25563
  "--no-progress",
24908
25564
  "--only-show-errors"
24909
25565
  ]);
24910
- const raw = await fs25.readFile(tmpFile, "utf8");
25566
+ const raw = await fs28.readFile(tmpFile, "utf8");
24911
25567
  return JSON.parse(raw.trim());
24912
25568
  } catch (e) {
24913
25569
  throw new LocalCliError({
@@ -24917,34 +25573,34 @@ async function readStatusBlob(opts) {
24917
25573
  cause: e
24918
25574
  });
24919
25575
  } finally {
24920
- await fs25.rm(tmpFile, { force: true });
25576
+ await fs28.rm(tmpFile, { force: true });
24921
25577
  }
24922
25578
  }
24923
25579
 
24924
25580
  // src/lib/bootstrap-state.ts
24925
- import * as fs26 from "fs/promises";
24926
- import * as os12 from "os";
24927
- import * as path27 from "path";
25581
+ import * as fs29 from "fs/promises";
25582
+ import * as os13 from "os";
25583
+ import * as path31 from "path";
24928
25584
  function stateDir(home) {
24929
- return path27.join(home, ".m8t-stack");
25585
+ return path31.join(home, ".m8t-stack");
24930
25586
  }
24931
25587
  function statePath(home) {
24932
- return path27.join(stateDir(home), "bootstrap.json");
25588
+ return path31.join(stateDir(home), "bootstrap.json");
24933
25589
  }
24934
- async function readBootstrapState(home = os12.homedir()) {
25590
+ async function readBootstrapState(home = os13.homedir()) {
24935
25591
  try {
24936
- const raw = await fs26.readFile(statePath(home), "utf8");
25592
+ const raw = await fs29.readFile(statePath(home), "utf8");
24937
25593
  return JSON.parse(raw);
24938
25594
  } catch (e) {
24939
25595
  if (e.code === "ENOENT") return null;
24940
25596
  throw e;
24941
25597
  }
24942
25598
  }
24943
- async function writeBootstrapState(state, home = os12.homedir()) {
24944
- await fs26.mkdir(stateDir(home), { recursive: true });
25599
+ async function writeBootstrapState(state, home = os13.homedir()) {
25600
+ await fs29.mkdir(stateDir(home), { recursive: true });
24945
25601
  const tmp = `${statePath(home)}.tmp`;
24946
- await fs26.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
24947
- await fs26.rename(tmp, statePath(home));
25602
+ await fs29.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
25603
+ await fs29.rename(tmp, statePath(home));
24948
25604
  }
24949
25605
 
24950
25606
  // src/commands/bootstrap/launch.ts
@@ -24984,12 +25640,12 @@ var BootstrapLaunchCommand = class extends M8tCommand {
24984
25640
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
24985
25641
  const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
24986
25642
  `);
24987
- const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path28.join(os13.homedir(), ".m8t-stack", "github-app.json");
25643
+ const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path32.join(os14.homedir(), ".m8t-stack", "github-app.json");
24988
25644
  let githubApp;
24989
- if (fs27.existsSync(credsPath)) {
25645
+ if (fs30.existsSync(credsPath)) {
24990
25646
  let parsed;
24991
25647
  try {
24992
- parsed = JSON.parse(fs27.readFileSync(credsPath, "utf8"));
25648
+ parsed = JSON.parse(fs30.readFileSync(credsPath, "utf8"));
24993
25649
  } catch {
24994
25650
  throw new LocalCliError({
24995
25651
  code: "GITHUB_APP_CREDS_INVALID",
@@ -24999,7 +25655,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
24999
25655
  }
25000
25656
  let pem;
25001
25657
  try {
25002
- pem = fs27.readFileSync(parsed.pemPath, "utf8");
25658
+ pem = fs30.readFileSync(parsed.pemPath, "utf8");
25003
25659
  } catch {
25004
25660
  throw new LocalCliError({
25005
25661
  code: "GITHUB_APP_PEM_MISSING",
@@ -25324,17 +25980,17 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
25324
25980
  };
25325
25981
 
25326
25982
  // src/commands/bootstrap/finish.ts
25327
- import * as fs28 from "fs/promises";
25328
- import * as os15 from "os";
25329
- import * as path30 from "path";
25983
+ import * as fs31 from "fs/promises";
25984
+ import * as os16 from "os";
25985
+ import * as path34 from "path";
25330
25986
  import { Command as Command52, Option as Option49 } from "clipanion";
25331
25987
  init_errors();
25332
25988
 
25333
25989
  // src/lib/company-profile-seed.ts
25334
25990
  import { spawn as spawn6 } from "child_process";
25335
- import { closeSync, openSync, readFileSync as readFileSync19 } from "fs";
25336
- import * as os14 from "os";
25337
- import * as path29 from "path";
25991
+ import { closeSync, openSync, readFileSync as readFileSync20 } from "fs";
25992
+ import * as os15 from "os";
25993
+ import * as path33 from "path";
25338
25994
  init_errors();
25339
25995
 
25340
25996
  // src/lib/onboarding-profile.ts
@@ -25515,9 +26171,9 @@ function composeFounderIdentityNote(id) {
25515
26171
 
25516
26172
  // src/lib/company-profile-seed.ts
25517
26173
  function readGithubAppCreds(credsPath) {
25518
- const p = credsPath ?? path29.join(os14.homedir(), ".m8t-stack", "github-app.json");
26174
+ const p = credsPath ?? path33.join(os15.homedir(), ".m8t-stack", "github-app.json");
25519
26175
  try {
25520
- return JSON.parse(readFileSync19(p, "utf8"));
26176
+ return JSON.parse(readFileSync20(p, "utf8"));
25521
26177
  } catch {
25522
26178
  return null;
25523
26179
  }
@@ -25545,7 +26201,7 @@ async function resolveSeedContext(opts) {
25545
26201
  async function applyProfileToBrain(args) {
25546
26202
  const token = await mintInstallationTokenFromPem({
25547
26203
  appId: args.appCreds.appId,
25548
- privateKeyPem: readFileSync19(args.appCreds.pemPath, "utf8"),
26204
+ privateKeyPem: readFileSync20(args.appCreds.pemPath, "utf8"),
25549
26205
  installationId: args.appCreds.installationId,
25550
26206
  fetchImpl: args.fetchImpl
25551
26207
  });
@@ -25580,7 +26236,7 @@ async function applyProfileToBrain(args) {
25580
26236
  });
25581
26237
  }
25582
26238
  function spawnDetachedSeedWatch() {
25583
- const logPath = path29.join(os14.homedir(), ".m8t-stack", "seed-profile.log");
26239
+ const logPath = path33.join(os15.homedir(), ".m8t-stack", "seed-profile.log");
25584
26240
  const fd = openSync(logPath, "a");
25585
26241
  try {
25586
26242
  const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
@@ -25679,9 +26335,9 @@ var BootstrapFinishCommand = class extends M8tCommand {
25679
26335
  });
25680
26336
  }
25681
26337
  const repoRoot = (typeof this.repoRoot === "string" ? this.repoRoot : void 0) ?? process.cwd();
25682
- const markerDir = path30.join(os15.homedir(), ".m8t-stack");
25683
- await fs28.mkdir(markerDir, { recursive: true });
25684
- await fs28.writeFile(path30.join(markerDir, "repo-root"), `${repoRoot}
26338
+ const markerDir = path34.join(os16.homedir(), ".m8t-stack");
26339
+ await fs31.mkdir(markerDir, { recursive: true });
26340
+ await fs31.writeFile(path34.join(markerDir, "repo-root"), `${repoRoot}
25685
26341
  `, "utf8");
25686
26342
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? state.subscriptionId;
25687
26343
  const resourceGroup = (typeof this.resourceGroup === "string" ? this.resourceGroup : void 0) ?? state.resourceGroup;
@@ -25717,7 +26373,7 @@ var BootstrapFinishCommand = class extends M8tCommand {
25717
26373
  }
25718
26374
  let brainOrg = null;
25719
26375
  try {
25720
- const credsRaw = await fs28.readFile(path30.join(markerDir, "github-app.json"), "utf8");
26376
+ const credsRaw = await fs31.readFile(path34.join(markerDir, "github-app.json"), "utf8");
25721
26377
  const creds = JSON.parse(credsRaw);
25722
26378
  brainOrg = typeof creds.org === "string" ? creds.org : null;
25723
26379
  } catch {
@@ -25748,18 +26404,18 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
25748
26404
  };
25749
26405
 
25750
26406
  // src/commands/bootstrap/ui.ts
25751
- import * as fs30 from "fs";
25752
- import * as os17 from "os";
25753
- import * as path32 from "path";
26407
+ import * as fs33 from "fs";
26408
+ import * as os18 from "os";
26409
+ import * as path36 from "path";
25754
26410
  import { Command as Command53, Option as Option50 } from "clipanion";
25755
26411
  import { DefaultAzureCredential as DefaultAzureCredential20 } from "@azure/identity";
25756
26412
  init_errors();
25757
26413
 
25758
26414
  // src/lib/bootstrap-ui.ts
25759
- import * as fs29 from "fs";
26415
+ import * as fs32 from "fs";
25760
26416
  import * as net from "net";
25761
- import * as os16 from "os";
25762
- import * as path31 from "path";
26417
+ import * as os17 from "os";
26418
+ import * as path35 from "path";
25763
26419
  import { spawn as spawn7 } from "child_process";
25764
26420
  init_errors();
25765
26421
  init_rbac();
@@ -25826,9 +26482,9 @@ async function ensureFounderFoundryRole(args) {
25826
26482
  });
25827
26483
  }
25828
26484
  function writeWebEnvLocal(args) {
25829
- const envPath = path31.join(args.repoRoot, "apps", "web", ".env.local");
25830
- if (fs29.existsSync(envPath)) {
25831
- fs29.copyFileSync(envPath, envPath + ".bak");
26485
+ const envPath = path35.join(args.repoRoot, "apps", "web", ".env.local");
26486
+ if (fs32.existsSync(envPath)) {
26487
+ fs32.copyFileSync(envPath, envPath + ".bak");
25832
26488
  }
25833
26489
  const body = [
25834
26490
  "# Written by `m8t bootstrap ui` \u2014 onboarding chat-only run (Simple Stacey).",
@@ -25836,13 +26492,14 @@ function writeWebEnvLocal(args) {
25836
26492
  `AZURE_TENANT_ID=${args.tenantId}`,
25837
26493
  `AZURE_CLIENT_ID=${args.clientId}`,
25838
26494
  `FOUNDRY_PROJECT_ENDPOINT=${args.foundryEndpoint}`,
26495
+ "VOICE_RELAY_PUBLIC_URL=ws://localhost:8790",
25839
26496
  "STORAGE_ACCOUNT_NAME=",
25840
26497
  "M8T_DISABLE_WORKER=1",
25841
26498
  "M8T_ONBOARDING=1",
25842
26499
  "NEXT_PUBLIC_M8T_ONBOARDING=1",
25843
26500
  ""
25844
26501
  ].join("\n");
25845
- fs29.writeFileSync(envPath, body, "utf8");
26502
+ fs32.writeFileSync(envPath, body, "utf8");
25846
26503
  return envPath;
25847
26504
  }
25848
26505
  function assertNodeVersion(versionString = process.version) {
@@ -25883,18 +26540,23 @@ async function installWebDeps(repoRoot) {
25883
26540
  });
25884
26541
  await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
25885
26542
  }
25886
- function onboardingUiPaths(home = os16.homedir()) {
25887
- const dir = path31.join(home, ".m8t-stack");
26543
+ function onboardingUiPaths(home = os17.homedir()) {
26544
+ const dir = path35.join(home, ".m8t-stack");
25888
26545
  return {
25889
- logPath: path31.join(dir, "onboarding-ui.log"),
25890
- pidPath: path31.join(dir, "onboarding-ui.pid")
26546
+ logPath: path35.join(dir, "onboarding-ui.log"),
26547
+ pidPath: path35.join(dir, "onboarding-ui.pid")
25891
26548
  };
25892
26549
  }
25893
- async function serveOnboardingUiDetached(args) {
25894
- const { logPath, pidPath } = onboardingUiPaths();
25895
- const portNum = Number(args.port);
25896
- const isPortOpen = () => new Promise((resolve3) => {
25897
- const s = net.createConnection({ host: "127.0.0.1", port: portNum });
26550
+ function onboardingRelayPaths(home = os17.homedir()) {
26551
+ const dir = path35.join(home, ".m8t-stack");
26552
+ return {
26553
+ logPath: path35.join(dir, "onboarding-relay.log"),
26554
+ pidPath: path35.join(dir, "onboarding-relay.pid")
26555
+ };
26556
+ }
26557
+ function isLocalPortOpen(port) {
26558
+ return new Promise((resolve3) => {
26559
+ const s = net.createConnection({ host: "127.0.0.1", port });
25898
26560
  s.setTimeout(1e3);
25899
26561
  s.on("connect", () => {
25900
26562
  s.destroy();
@@ -25908,11 +26570,16 @@ async function serveOnboardingUiDetached(args) {
25908
26570
  resolve3(false);
25909
26571
  });
25910
26572
  });
26573
+ }
26574
+ async function serveOnboardingUiDetached(args) {
26575
+ const { logPath, pidPath } = onboardingUiPaths();
26576
+ const portNum = Number(args.port);
26577
+ const isPortOpen = () => isLocalPortOpen(portNum);
25911
26578
  if (await isPortOpen()) {
25912
26579
  return { alreadyRunning: true, logPath };
25913
26580
  }
25914
- fs29.mkdirSync(path31.dirname(logPath), { recursive: true });
25915
- const fd = fs29.openSync(logPath, "a");
26581
+ fs32.mkdirSync(path35.dirname(logPath), { recursive: true });
26582
+ const fd = fs32.openSync(logPath, "a");
25916
26583
  const child = spawn7("pnpm", ["--filter", "web", "dev"], {
25917
26584
  cwd: args.repoRoot,
25918
26585
  detached: true,
@@ -25920,7 +26587,7 @@ async function serveOnboardingUiDetached(args) {
25920
26587
  env: { ...process.env, PORT: args.port },
25921
26588
  shell: false
25922
26589
  });
25923
- fs29.closeSync(fd);
26590
+ fs32.closeSync(fd);
25924
26591
  if (child.pid == null) {
25925
26592
  throw new LocalCliError({
25926
26593
  code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
@@ -25929,7 +26596,7 @@ async function serveOnboardingUiDetached(args) {
25929
26596
  });
25930
26597
  }
25931
26598
  child.unref();
25932
- fs29.writeFileSync(pidPath, String(child.pid), "utf8");
26599
+ fs32.writeFileSync(pidPath, String(child.pid), "utf8");
25933
26600
  const deadline = Date.now() + 45e3;
25934
26601
  const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
25935
26602
  while (Date.now() < deadline) {
@@ -25938,6 +26605,46 @@ async function serveOnboardingUiDetached(args) {
25938
26605
  }
25939
26606
  return { alreadyRunning: false, logPath };
25940
26607
  }
26608
+ async function serveOnboardingRelayDetached(args) {
26609
+ const { logPath, pidPath } = onboardingRelayPaths();
26610
+ const portNum = 8790;
26611
+ if (await isLocalPortOpen(portNum)) {
26612
+ return { alreadyRunning: true, logPath };
26613
+ }
26614
+ fs32.mkdirSync(path35.dirname(logPath), { recursive: true });
26615
+ const fd = fs32.openSync(logPath, "a");
26616
+ const child = spawn7("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
26617
+ cwd: args.repoRoot,
26618
+ detached: true,
26619
+ stdio: ["ignore", fd, fd],
26620
+ env: {
26621
+ ...process.env,
26622
+ FOUNDRY_PROJECT_ENDPOINT: args.foundryEndpoint,
26623
+ AZURE_TENANT_ID: args.tenantId,
26624
+ AZURE_CLIENT_ID: args.clientId,
26625
+ RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
26626
+ RELAY_PORT: "8790"
26627
+ },
26628
+ shell: false
26629
+ });
26630
+ fs32.closeSync(fd);
26631
+ if (child.pid == null) {
26632
+ throw new LocalCliError({
26633
+ code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
26634
+ message: "Could not start the voice relay (pnpm did not spawn).",
26635
+ hint: "Check that pnpm + Node 20+ are installed; see the log at " + logPath + "."
26636
+ });
26637
+ }
26638
+ child.unref();
26639
+ fs32.writeFileSync(pidPath, String(child.pid), "utf8");
26640
+ const deadline = Date.now() + 45e3;
26641
+ const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
26642
+ while (Date.now() < deadline) {
26643
+ await sleep3(500);
26644
+ if (await isLocalPortOpen(portNum)) break;
26645
+ }
26646
+ return { alreadyRunning: false, logPath };
26647
+ }
25941
26648
  function autoOpenOnboardingUi(url, open = tryOpenUrl) {
25942
26649
  try {
25943
26650
  const result = open(url);
@@ -25948,11 +26655,10 @@ function autoOpenOnboardingUi(url, open = tryOpenUrl) {
25948
26655
  } catch {
25949
26656
  }
25950
26657
  }
25951
- function stopOnboardingUi(home = os16.homedir()) {
25952
- const { pidPath } = onboardingUiPaths(home);
26658
+ function stopPidFile(pidPath) {
25953
26659
  let pidStr;
25954
26660
  try {
25955
- pidStr = fs29.readFileSync(pidPath, "utf8").trim();
26661
+ pidStr = fs32.readFileSync(pidPath, "utf8").trim();
25956
26662
  } catch {
25957
26663
  return false;
25958
26664
  }
@@ -25965,11 +26671,16 @@ function stopOnboardingUi(home = os16.homedir()) {
25965
26671
  }
25966
26672
  }
25967
26673
  try {
25968
- fs29.unlinkSync(pidPath);
26674
+ fs32.unlinkSync(pidPath);
25969
26675
  } catch {
25970
26676
  }
25971
26677
  return true;
25972
26678
  }
26679
+ function stopOnboardingUi(home = os17.homedir()) {
26680
+ const uiStopped = stopPidFile(onboardingUiPaths(home).pidPath);
26681
+ const relayStopped = stopPidFile(onboardingRelayPaths(home).pidPath);
26682
+ return uiStopped || relayStopped;
26683
+ }
25973
26684
 
25974
26685
  // src/lib/simple-stacey.ts
25975
26686
  var SIMPLE_STACEY_PERSONA = "startup-advisor-intake";
@@ -26081,7 +26792,7 @@ var BootstrapUiCommand = class extends M8tCommand {
26081
26792
  this.context.stderr.write(` ${colors.dim(m)}
26082
26793
  `);
26083
26794
  };
26084
- if (fs30.existsSync(path32.join(os17.homedir(), ".m8t-stack", "config.yaml"))) {
26795
+ if (fs33.existsSync(path36.join(os18.homedir(), ".m8t-stack", "config.yaml"))) {
26085
26796
  out(colors.error("\u26A0 an existing ~/.m8t-stack/config.yaml will override the onboarding app registration in apps/web \u2014 if Microsoft sign-in fails, move it aside and retry."));
26086
26797
  }
26087
26798
  const credential2 = new DefaultAzureCredential20();
@@ -26131,10 +26842,26 @@ var BootstrapUiCommand = class extends M8tCommand {
26131
26842
  return 0;
26132
26843
  }
26133
26844
  if (this.foreground === true) {
26845
+ out("starting the voice relay on :8790 in the background\u2026");
26846
+ await serveOnboardingRelayDetached({
26847
+ repoRoot,
26848
+ foundryEndpoint: endpoint,
26849
+ tenantId: account.tenantId,
26850
+ clientId: state.appRegClientId
26851
+ });
26852
+ out("voice relay on :8790");
26134
26853
  out("starting the webapp on :3000 (Ctrl-C to stop)\u2026");
26135
26854
  await runInherit("pnpm", ["--filter", "web", "dev"], repoRoot, "BOOTSTRAP_UI_DEV_FAILED");
26136
26855
  return 0;
26137
26856
  }
26857
+ out("starting the voice relay on :8790 in the background\u2026");
26858
+ await serveOnboardingRelayDetached({
26859
+ repoRoot,
26860
+ foundryEndpoint: endpoint,
26861
+ tenantId: account.tenantId,
26862
+ clientId: state.appRegClientId
26863
+ });
26864
+ out("voice relay on :8790");
26138
26865
  out("starting the webapp on :3000 in the background\u2026");
26139
26866
  const { alreadyRunning, logPath } = await serveOnboardingUiDetached({ repoRoot, port: this.port });
26140
26867
  if (alreadyRunning) {