@m8t-stack/cli 0.2.72 → 0.2.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1370,7 +1370,7 @@ var init_enable_hosted_brain = __esm({
1370
1370
  import { Builtins, Cli } from "clipanion";
1371
1371
 
1372
1372
  // src/lib/package-version.ts
1373
- var CLI_VERSION = "0.2.72";
1373
+ var CLI_VERSION = "0.2.73";
1374
1374
 
1375
1375
  // src/lib/render-error.ts
1376
1376
  init_errors();
@@ -15663,10 +15663,16 @@ import * as fs9 from "fs";
15663
15663
  import * as path9 from "path";
15664
15664
  var SEED_MANIFEST_PATH = ".m8t/seed-manifest.json";
15665
15665
  var SEED_PATHS = ["skills", "references", "targets"];
15666
+ function isUpstream(v) {
15667
+ if (v === void 0) return true;
15668
+ if (!v || typeof v !== "object") return false;
15669
+ const u = v;
15670
+ return typeof u.repo === "string" && u.repo.length > 0 && typeof u.ref === "string" && u.ref.length > 0;
15671
+ }
15666
15672
  function isLineage(v) {
15667
15673
  if (!v || typeof v !== "object") return false;
15668
15674
  const l = v;
15669
- return typeof l.name === "string" && typeof l.refreshedToCommit === "string" && typeof l.refreshedToVersion === "string" && Array.isArray(l.ownedPaths) && l.ownedPaths.every((p) => typeof p === "string");
15675
+ return typeof l.name === "string" && typeof l.refreshedToCommit === "string" && typeof l.refreshedToVersion === "string" && Array.isArray(l.ownedPaths) && l.ownedPaths.every((p) => typeof p === "string") && isUpstream(l.upstream);
15670
15676
  }
15671
15677
  function parseSeedManifest(text) {
15672
15678
  if (!text?.trim()) return null;
@@ -15684,6 +15690,11 @@ function parseSeedManifest(text) {
15684
15690
  function buildSeedManifest(seeds) {
15685
15691
  return { schemaVersion: 1, seeds };
15686
15692
  }
15693
+ function carryUpstream(prior, lineage) {
15694
+ if (lineage.upstream) return lineage;
15695
+ const before = prior?.seeds.find((s) => s.name === lineage.name)?.upstream;
15696
+ return before ? { ...lineage, upstream: before } : lineage;
15697
+ }
15687
15698
  function upsertLineage(manifest, lineage) {
15688
15699
  const prior = manifest?.seeds ?? [];
15689
15700
  const others = prior.filter((s) => s.name !== lineage.name);
@@ -15960,6 +15971,15 @@ async function syncAgentContent(args) {
15960
15971
  await hydrateAgentsInto(args.root, pin.agents, args.deps);
15961
15972
  return Object.values(pin.agents).map((a) => ({ persona: a.persona, repo: a.repo, ref: a.ref }));
15962
15973
  }
15974
+ function upstreamForSeed(root, seedName) {
15975
+ try {
15976
+ const pin = parseReleasePin(fs12.readFileSync(path12.join(root, PIN_REL_PATH), "utf8"), PIN_REL_PATH);
15977
+ const agent = Object.values(pin.agents).find((a) => a.persona === seedName);
15978
+ return agent ? { repo: agent.repo, ref: agent.ref } : void 0;
15979
+ } catch {
15980
+ return void 0;
15981
+ }
15982
+ }
15963
15983
  function agentContentHint(root, persona) {
15964
15984
  try {
15965
15985
  const pin = parseReleasePin(fs12.readFileSync(path12.join(root, PIN_REL_PATH), "utf8"), PIN_REL_PATH);
@@ -16034,7 +16054,8 @@ function writeSeedMarker(args) {
16034
16054
  name: args.seedName,
16035
16055
  refreshedToCommit: args.refreshedToCommit,
16036
16056
  refreshedToVersion: args.version,
16037
- ownedPaths: deriveOwnedPaths(args.seedDir)
16057
+ ownedPaths: deriveOwnedPaths(args.seedDir),
16058
+ ...args.upstream ? { upstream: args.upstream } : {}
16038
16059
  }]);
16039
16060
  const dest = path13.join(args.destDir, SEED_MANIFEST_PATH);
16040
16061
  fs13.mkdirSync(path13.dirname(dest), { recursive: true });
@@ -16658,7 +16679,15 @@ var BrainCreateCommand = class extends M8tCommand {
16658
16679
  materializeBrainTree({ templateSrc, destDir: tmpDir, seedDir });
16659
16680
  if (seedName && seedDir) {
16660
16681
  const headSha = resolveScaffoldCommit(repoRoot, env);
16661
- writeSeedMarker({ destDir: tmpDir, seedName, seedDir, refreshedToCommit: headSha, version: "(scaffold)" });
16682
+ const upstream = upstreamForSeed(repoRoot, seedName);
16683
+ writeSeedMarker({
16684
+ destDir: tmpDir,
16685
+ seedName,
16686
+ seedDir,
16687
+ refreshedToCommit: headSha,
16688
+ version: "(scaffold)",
16689
+ ...upstream ? { upstream } : {}
16690
+ });
16662
16691
  }
16663
16692
  this.context.stdout.write(
16664
16693
  seedName ? `${colors.dim("\u2192")} copied brain-template/ + overlaid seed ${colors.field(seedName)} to ${tmpDir}
@@ -21534,14 +21563,15 @@ async function refreshBrainRepo(args) {
21534
21563
  const ownedNow = new Set(args.trees.ownedAtTheirs);
21535
21564
  assertNoDanglingIndexRefs(finalPaths, (p) => ownedNow.has(p), indexBodies);
21536
21565
  const newOwned = deriveOwnedPaths(args.trees.theirsDir);
21566
+ const priorManifest = args.priorManifest ?? (args.lineage ? { schemaVersion: 1, seeds: [args.lineage] } : null);
21537
21567
  const nextManifest = upsertLineage(
21538
- args.priorManifest ?? (args.lineage ? { schemaVersion: 1, seeds: [args.lineage] } : null),
21539
- {
21568
+ priorManifest,
21569
+ carryUpstream(priorManifest, {
21540
21570
  name: args.seedName,
21541
21571
  refreshedToCommit: args.targetCommit ?? "",
21542
21572
  refreshedToVersion: args.version,
21543
21573
  ownedPaths: newOwned
21544
- }
21574
+ })
21545
21575
  );
21546
21576
  files.push({ path: SEED_MANIFEST_PATH, content: serializeSeedManifest(nextManifest) });
21547
21577
  for (const sb of args.supersedeBranches ?? []) {
@@ -29925,7 +29955,7 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
29925
29955
  // src/commands/bootstrap/launch.ts
29926
29956
  var DEFAULT_RG = "rg-m8t-stack";
29927
29957
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
29928
- var DEFAULT_INSTALLER_TAG = "v0.1.58";
29958
+ var DEFAULT_INSTALLER_TAG = "v0.1.59";
29929
29959
  var ACI_NAME = "m8t-installer";
29930
29960
  var MI_NAME = "m8t-installer-mi";
29931
29961
  var BootstrapLaunchCommand = class extends M8tCommand {
@@ -30892,19 +30922,22 @@ async function resolveSeedContext(opts) {
30892
30922
  if (!appCreds) return null;
30893
30923
  let endpoint = opts.endpointOverride;
30894
30924
  let subscriptionId = opts.subscriptionId;
30895
- if (!endpoint) {
30925
+ let recorded = opts.brainsOverride;
30926
+ if (!endpoint || !opts.brainOverride && recorded === void 0) {
30896
30927
  const state = await readBootstrapState();
30897
30928
  if (!state) {
30898
30929
  throw new LocalCliError({ code: "BOOTSTRAP_NO_STATE", message: "No bootstrap state found.", hint: "Run 'm8t bootstrap launch' first, or pass --endpoint." });
30899
30930
  }
30900
30931
  subscriptionId ??= state.subscriptionId;
30901
30932
  const doc = await readStatusBlob({ saName: state.statusSaName, resourceGroup: state.resourceGroup, subscriptionId: state.subscriptionId });
30902
- endpoint = doc.result?.foundryEndpoint;
30933
+ endpoint ??= doc.result?.foundryEndpoint;
30934
+ recorded ??= doc.result?.brains;
30903
30935
  if (!endpoint) {
30904
30936
  throw new LocalCliError({ code: "SEED_NO_ENDPOINT", message: "Could not resolve the Foundry endpoint from the install status.", hint: "Pass --endpoint <foundryEndpoint>." });
30905
30937
  }
30906
30938
  }
30907
- const brainRepos = opts.brainOverride ? [opts.brainOverride] : [`${appCreds.org}/stacey-brain`, `${appCreds.org}/ezra-brain`];
30939
+ const brainRepos = opts.brainOverride ? [opts.brainOverride] : recorded ?? [];
30940
+ if (brainRepos.length === 0) return null;
30908
30941
  return { endpoint, brainRepos, appCreds, subscriptionId };
30909
30942
  }
30910
30943
  var MEMORY_INDEX_PATH = "memory/MEMORY.md";
@@ -31051,7 +31084,12 @@ function spawnDetachedSeedWatch() {
31051
31084
  }
31052
31085
  }
31053
31086
  async function reactiveSeedOnInstallComplete(args) {
31054
- const ctx = await resolveSeedContext({ endpointOverride: args.endpoint, appCredsOverride: args.appCredsOverride, subscriptionId: args.subscriptionId });
31087
+ const ctx = await resolveSeedContext({
31088
+ endpointOverride: args.endpoint,
31089
+ appCredsOverride: args.appCredsOverride,
31090
+ subscriptionId: args.subscriptionId,
31091
+ brainsOverride: args.brains
31092
+ });
31055
31093
  if (!ctx) return;
31056
31094
  const token = await (args.getFoundryTokenImpl ?? getFoundryToken)();
31057
31095
  const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token, fetchImpl: args.fetchImpl });
@@ -31081,8 +31119,7 @@ async function reactiveSeedOnInstallComplete(args) {
31081
31119
 
31082
31120
  // src/lib/install-summary.ts
31083
31121
  var DEFAULT_WORKERS = [
31084
- { name: "stacey", label: "Stacey - Startup Advisor", brain: "stacey-brain", executor: "stacey-coder" },
31085
- { name: "ezra", label: "Ezra - Azure Expert", brain: "ezra-brain", executor: "ezra-executor" }
31122
+ { name: "ezra", label: "Ezra - Azure Expert", brain: "ezra-brain" }
31086
31123
  ];
31087
31124
  function renderInstallSummary(args) {
31088
31125
  const open3 = args.webappUrl ? `${args.webappUrl} (or run: m8t open)` : "run: m8t open";
@@ -31094,11 +31131,11 @@ function renderInstallSummary(args) {
31094
31131
  if (args.brainOrg) {
31095
31132
  lines.push(" Your team:");
31096
31133
  for (const w of DEFAULT_WORKERS) {
31097
- lines.push(` \u2022 ${w.label} \xB7 brain: ${args.brainOrg}/${w.brain} \xB7 executor: ${w.executor}`);
31134
+ lines.push(` \u2022 ${w.label} \xB7 brain: ${args.brainOrg}/${w.brain}`);
31098
31135
  }
31099
31136
  lines.push(
31100
31137
  "",
31101
- " Reach them: @stacey / @ezra (or /stacey) in your coding agent",
31138
+ " Reach them: @ezra (or /ezra) in your coding agent",
31102
31139
  " \xB7 the webapp \xB7 or connect Telegram in the webapp (Channels)"
31103
31140
  );
31104
31141
  } else {
@@ -32192,7 +32229,7 @@ async function runCompanionInstallCommand(stdout, install) {
32192
32229
  return 1;
32193
32230
  }
32194
32231
  stdout.write(
32195
- `Desktop companions installed: Stacey and Ezra are ready.
32232
+ `Desktop companions installed - they are at the edge of your screen.
32196
32233
  Version: ${state.version}
32197
32234
  `
32198
32235
  );
@@ -32352,7 +32389,7 @@ async function finalizeInstall(args, deps = defaultDeps2) {
32352
32389
  args.stdout(renderInstallSummary({ webappUrl, brainOrg }) + "\n");
32353
32390
  args.stdout(
32354
32391
  `${colors.field("Optional local tooling for your coding agent:")}
32355
- \u2022 Talk to your deployed workers from agent sessions (e.g. /stacey) \u2014 install the m8t plugin: guides/install/m8t-plugin.md.
32392
+ \u2022 Talk to your deployed workers from agent sessions (e.g. /ezra) \u2014 install the m8t plugin: guides/install/m8t-plugin.md.
32356
32393
  \u2022 Azure-capable MCP servers (microsoft/azure-skills, Microsoft Learn) \u2014 see the optional steps in guides/install.md.
32357
32394
  \u2022 Deploy workers conversationally with the opt-in persona skills \u2014 see guides/workers.md.
32358
32395
  \u2022 Make sure your m8t CLI is current: npm i -g @m8t-stack/cli@latest
@@ -32363,6 +32400,9 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
32363
32400
  try {
32364
32401
  await deps.reactiveSeed({
32365
32402
  endpoint: args.doc.result?.foundryEndpoint,
32403
+ // What the install RECORDED creating. Seeding a fixed roster instead used to
32404
+ // fail every default install against a brain that was never created.
32405
+ brains: args.doc.result?.brains,
32366
32406
  subscriptionId: args.subscriptionId,
32367
32407
  stdout: args.stdout
32368
32408
  });
@@ -32384,14 +32424,14 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
32384
32424
  const alreadyHasCompanion = await deps.companionStatus().then((s) => s.state !== "not-installed").catch(() => true);
32385
32425
  const pinned = alreadyHasCompanion ? void 0 : args.doc.result?.platformVersion;
32386
32426
  args.stdout(
32387
- (pinned === void 0 ? "Putting Stacey and Ezra on your desktop \u2014 fetching the latest released build\n" : `Putting Stacey and Ezra on your desktop \u2014 fetching the build ${pinned} pins
32427
+ (pinned === void 0 ? "Putting your mates on your desktop \u2014 fetching the latest released build\n" : `Putting your mates on your desktop \u2014 fetching the build ${pinned} pins
32388
32428
  `) + ` ${colors.hint("(about 120 MB; on a slow link this takes a few minutes)")}
32389
32429
  `
32390
32430
  );
32391
32431
  try {
32392
32432
  const companion = await deps.convergeCompanion(pinned);
32393
32433
  if (companion.state === "installed") {
32394
- args.stdout("Desktop companions installed: Stacey and Ezra are ready.\n\n");
32434
+ args.stdout("Desktop companions installed - they are at the edge of your screen.\n\n");
32395
32435
  } else if (companion.state === "not-released") {
32396
32436
  args.stdout(
32397
32437
  `${pinned === void 0 ? "The latest platform release carries no desktop companions." : `The release this platform is pinned to (${pinned}) carries no desktop companions.`}
@@ -34230,11 +34270,11 @@ function parseEvent(value) {
34230
34270
  };
34231
34271
  }
34232
34272
  if (value.type === "roster") {
34233
- if (!hasExactKeys(value, ["type", "origin", "mates"]) || !isGatewayOrigin(value.origin) || !Array.isArray(value.mates) || value.mates.length !== 2) {
34273
+ if (!hasExactKeys(value, ["type", "origin", "mates"]) || !isGatewayOrigin(value.origin) || !Array.isArray(value.mates) || value.mates.length === 0) {
34234
34274
  return eventError();
34235
34275
  }
34236
34276
  const mates = value.mates.map(parseMate);
34237
- if (new Set(mates.map(({ personaKey }) => personaKey)).size !== 2 || new Set(mates.map(({ agentName }) => agentName)).size !== 2) {
34277
+ if (new Set(mates.map(({ personaKey }) => personaKey)).size !== mates.length || new Set(mates.map(({ agentName }) => agentName)).size !== mates.length) {
34238
34278
  return eventError();
34239
34279
  }
34240
34280
  return { type: "roster", origin: value.origin, mates };
@@ -34425,7 +34465,8 @@ function resolveAgentName(agents, personaKey) {
34425
34465
  const matches = agents.filter(
34426
34466
  (agent) => agent.personaKey === personaKey && isAgentName(agent.name)
34427
34467
  );
34428
- if (matches.length !== 1) {
34468
+ if (matches.length === 0) return null;
34469
+ if (matches.length > 1) {
34429
34470
  throw new PredispatchFailure("mate-unavailable");
34430
34471
  }
34431
34472
  const match = matches[0];
@@ -34434,6 +34475,11 @@ function resolveAgentName(agents, personaKey) {
34434
34475
  }
34435
34476
  return match.name;
34436
34477
  }
34478
+ function requireAgentName(agents, personaKey) {
34479
+ const name = resolveAgentName(agents, personaKey);
34480
+ if (name === null) throw new PredispatchFailure("mate-unavailable");
34481
+ return name;
34482
+ }
34437
34483
  function decodeConversationId(data) {
34438
34484
  if (!isRecord3(data) || typeof data.id !== "string" || !OPAQUE_ID.test(data.id)) {
34439
34485
  throw new PredispatchFailure("not-connected");
@@ -34598,22 +34644,15 @@ async function rosterCompanions(sink, deps = {}) {
34598
34644
  const activeSince = new Date(
34599
34645
  installed === null || installed > now ? now : installed
34600
34646
  ).toISOString();
34601
- const mates = [
34602
- {
34603
- personaKey: "startup-advisor",
34604
- agentName: resolveAgentName(agents, "startup-advisor"),
34605
- displayName: "Stacey",
34606
- portraitKey: "stacey",
34607
- activeSince
34608
- },
34609
- {
34610
- personaKey: "ezra",
34611
- agentName: resolveAgentName(agents, "ezra"),
34612
- displayName: "Ezra",
34613
- portraitKey: "ezra",
34614
- activeSince
34615
- }
34647
+ const PRESENTABLE = [
34648
+ { personaKey: "startup-advisor", displayName: "Stacey", portraitKey: "stacey" },
34649
+ { personaKey: "ezra", displayName: "Ezra", portraitKey: "ezra" }
34616
34650
  ];
34651
+ const mates = PRESENTABLE.flatMap((mate) => {
34652
+ const agentName = resolveAgentName(agents, mate.personaKey);
34653
+ return agentName === null ? [] : [{ ...mate, agentName, activeSince }];
34654
+ });
34655
+ if (mates.length === 0) throw new PredispatchFailure("mate-unavailable");
34617
34656
  const event = parseEvent({ type: "roster", origin, mates });
34618
34657
  sink(event);
34619
34658
  return event;
@@ -34745,7 +34784,7 @@ async function dispatchConversation(request, sink, deps, consume, readDesktopMar
34745
34784
  token,
34746
34785
  controller.signal
34747
34786
  );
34748
- const agentName = resolveAgentName(agents, request.personaKey);
34787
+ const agentName = requireAgentName(agents, request.personaKey);
34749
34788
  const conversationId = await (deps.resolveConversationId ?? resolveConversationId)(
34750
34789
  fetchImpl,
34751
34790
  origin,
@@ -34856,7 +34895,7 @@ async function historyCompanionMessages(request, sink, deps = {}) {
34856
34895
  token,
34857
34896
  controller.signal
34858
34897
  );
34859
- const agentName = resolveAgentName(agents, request.personaKey);
34898
+ const agentName = requireAgentName(agents, request.personaKey);
34860
34899
  const conversationId = await (deps.resolveConversationId ?? resolveConversationId)(
34861
34900
  fetchImpl,
34862
34901
  origin,