@m8t-stack/cli 0.2.37 → 0.2.38

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
@@ -1298,7 +1298,7 @@ var init_enable_hosted_brain = __esm({
1298
1298
  import { Builtins, Cli } from "clipanion";
1299
1299
 
1300
1300
  // src/lib/package-version.ts
1301
- var CLI_VERSION = "0.2.37";
1301
+ var CLI_VERSION = "0.2.38";
1302
1302
 
1303
1303
  // src/lib/render-error.ts
1304
1304
  init_errors();
@@ -16963,7 +16963,7 @@ async function listModelQuota(region) {
16963
16963
  function usageModelName(usageName) {
16964
16964
  return usageName.split(".").slice(2).join(".");
16965
16965
  }
16966
- var REASONING_USAGE_RE = /gpt-5|o[1-9]/i;
16966
+ var REASONING_USAGE_RE = /gpt-5|o[1-9]|grok-4/i;
16967
16967
  function isReasoningModel(modelDeployment) {
16968
16968
  return REASONING_USAGE_RE.test(modelDeployment);
16969
16969
  }
@@ -25476,7 +25476,7 @@ async function ensureDeployment(args) {
25476
25476
  "--model-version",
25477
25477
  args.modelVersion,
25478
25478
  "--model-format",
25479
- "OpenAI",
25479
+ args.format ?? "OpenAI",
25480
25480
  "--sku-name",
25481
25481
  "GlobalStandard",
25482
25482
  "--sku-capacity",
@@ -27466,6 +27466,7 @@ async function deploySimpleStacey(args) {
27466
27466
  repoRoot: args.repoRoot,
27467
27467
  persona: SIMPLE_STACEY_PERSONA,
27468
27468
  agentName: SIMPLE_STACEY_AGENT,
27469
+ model: args.model,
27469
27470
  fieldOverrides: args.fieldOverrides
27470
27471
  });
27471
27472
  } catch (e) {
@@ -27534,6 +27535,7 @@ async function deploySimpleStaceyWithRetry(args) {
27534
27535
  credential: args.credential,
27535
27536
  endpoint: args.endpoint,
27536
27537
  repoRoot: args.repoRoot,
27538
+ model: args.model,
27537
27539
  fieldOverrides: args.fieldOverrides
27538
27540
  });
27539
27541
  } catch (error) {
@@ -27930,6 +27932,257 @@ function stopOnboardingUi(home = os17.homedir()) {
27930
27932
  return uiStopped || relayStopped;
27931
27933
  }
27932
27934
 
27935
+ // src/lib/model-catalog.ts
27936
+ async function listAgentModels(region) {
27937
+ let out;
27938
+ try {
27939
+ out = await runAz(["cognitiveservices", "model", "list", "-l", region, "--output", "json"]);
27940
+ } catch (e) {
27941
+ const msg = e instanceof Error ? e.message : String(e);
27942
+ const denied = /AuthorizationFailed|Forbidden|\b403\b|does not have authorization/i.test(msg);
27943
+ return { ok: false, error: denied ? "denied" : "unavailable" };
27944
+ }
27945
+ let raw;
27946
+ try {
27947
+ raw = JSON.parse(out);
27948
+ } catch {
27949
+ return { ok: false, error: "malformed" };
27950
+ }
27951
+ if (!Array.isArray(raw) || raw.some((e) => typeof e !== "object" || e === null)) {
27952
+ return { ok: false, error: "malformed" };
27953
+ }
27954
+ try {
27955
+ const rows = raw.map((e) => ({
27956
+ format: e.model?.format ?? "",
27957
+ name: e.model?.name ?? "",
27958
+ version: e.model?.version ?? "",
27959
+ skus: [...new Set((e.model?.skus ?? []).map((s) => s.name ?? "").filter(Boolean))],
27960
+ agentsV2: e.model?.capabilities?.agentsV2 === "true"
27961
+ }));
27962
+ return { ok: true, rows };
27963
+ } catch {
27964
+ return { ok: false, error: "malformed" };
27965
+ }
27966
+ }
27967
+
27968
+ // src/lib/model-cascade.ts
27969
+ var WHITELIST = [
27970
+ { model: "gpt-5.6-luna", format: "OpenAI", version: "2026-07-09", capacity: 100 },
27971
+ { model: "gpt-5.6-sol", format: "OpenAI", version: "2026-07-09", capacity: 100 },
27972
+ { model: "gpt-5.6-terra", format: "OpenAI", version: "2026-07-09", capacity: 100 },
27973
+ { model: "gpt-5.4", format: "OpenAI", version: "2026-03-05", capacity: 100 },
27974
+ { model: "grok-4.3", format: "xAI", version: "1", capacity: 100 },
27975
+ { model: "gpt-4.1-mini", format: "OpenAI", version: "2025-04-14", capacity: 50 }
27976
+ ];
27977
+ var GLOBAL_STANDARD = "GlobalStandard";
27978
+ function planCascade(whitelist, catalog, quota) {
27979
+ if (!catalog.ok) {
27980
+ return {
27981
+ candidates: [],
27982
+ skipped: whitelist.map((r) => ({ model: r.model, outcome: "catalog-unverified", detail: catalog.error }))
27983
+ };
27984
+ }
27985
+ const rowsByName = /* @__PURE__ */ new Map();
27986
+ for (const row of catalog.rows) {
27987
+ const list = rowsByName.get(row.name);
27988
+ if (list) list.push(row);
27989
+ else rowsByName.set(row.name, [row]);
27990
+ }
27991
+ const candidates = [];
27992
+ const skipped = [];
27993
+ for (const rung of whitelist) {
27994
+ const rows = rowsByName.get(rung.model);
27995
+ if (!rows || rows.length === 0) {
27996
+ skipped.push({ model: rung.model, outcome: "not-in-catalog" });
27997
+ continue;
27998
+ }
27999
+ const agentRows = rows.filter((r) => r.agentsV2);
28000
+ if (agentRows.length === 0) {
28001
+ skipped.push({ model: rung.model, outcome: "not-agent-eligible" });
28002
+ continue;
28003
+ }
28004
+ if (!agentRows.some((r) => r.skus.includes(GLOBAL_STANDARD))) {
28005
+ skipped.push({ model: rung.model, outcome: "no-global-standard" });
28006
+ continue;
28007
+ }
28008
+ if (modelQuotaVerdict(quota, rung.model).verdict === "no_quota") {
28009
+ skipped.push({ model: rung.model, outcome: "no-quota" });
28010
+ continue;
28011
+ }
28012
+ candidates.push(rung);
28013
+ }
28014
+ return { candidates, skipped };
28015
+ }
28016
+ var QUOTA_RE = /insufficient\s*quota|not enough quota|quota limit|quota\b[^.]{0,40}exceed|exceed\w*\b[^.]{0,40}quota|capacity[^.]{0,40}exceed|exceed\w*\b[^.]{0,40}capacity/i;
28017
+ var REGION_RE = /not available in|not supported in|NotAvailableInRegion|InvalidResourceLocation/i;
28018
+ var CONFLICT_RE = /\bconflict\b|\b409\b/i;
28019
+ function classifyDeployError(message) {
28020
+ if (QUOTA_RE.test(message)) return "deploy-rejected-quota";
28021
+ if (REGION_RE.test(message)) return "deploy-rejected-region";
28022
+ return "deploy-unverified";
28023
+ }
28024
+ async function walkCascade(whitelist, plan, deploy, opts) {
28025
+ const outcomes = /* @__PURE__ */ new Map();
28026
+ for (const s of plan.skipped) outcomes.set(s.model, s);
28027
+ let chosen = null;
28028
+ let aborted = false;
28029
+ for (const rung of plan.candidates) {
28030
+ if (chosen) break;
28031
+ if (opts.now() >= opts.deadlineAt) {
28032
+ aborted = true;
28033
+ break;
28034
+ }
28035
+ let row;
28036
+ try {
28037
+ await deploy(rung);
28038
+ row = { model: rung.model, outcome: "deployed" };
28039
+ chosen = rung;
28040
+ } catch (e) {
28041
+ const msg = e instanceof Error ? e.message : String(e);
28042
+ if (CONFLICT_RE.test(msg)) {
28043
+ try {
28044
+ await deploy(rung);
28045
+ row = { model: rung.model, outcome: "deployed" };
28046
+ chosen = rung;
28047
+ } catch (e2) {
28048
+ const msg2 = e2 instanceof Error ? e2.message : String(e2);
28049
+ row = {
28050
+ model: rung.model,
28051
+ outcome: CONFLICT_RE.test(msg2) ? "deploy-unverified" : classifyDeployError(msg2),
28052
+ detail: msg2
28053
+ };
28054
+ }
28055
+ } else {
28056
+ row = { model: rung.model, outcome: classifyDeployError(msg), detail: msg };
28057
+ }
28058
+ }
28059
+ outcomes.set(rung.model, row);
28060
+ opts.onRung?.(row.model, row.outcome);
28061
+ }
28062
+ const trace = whitelist.map(
28063
+ (r) => outcomes.get(r.model) ?? { model: r.model, outcome: aborted ? "aborted-deadline" : "not-reached" }
28064
+ );
28065
+ return { chosen, trace };
28066
+ }
28067
+ var CERTAIN_UNAVAILABLE = /* @__PURE__ */ new Set([
28068
+ "not-in-catalog",
28069
+ "not-agent-eligible",
28070
+ "no-global-standard",
28071
+ "deploy-rejected-region"
28072
+ ]);
28073
+ var QUOTA_BLOCKED = /* @__PURE__ */ new Set(["no-quota", "deploy-rejected-quota"]);
28074
+ function decideNote(whitelist, result) {
28075
+ const chosenIdx = result.chosen ? whitelist.findIndex((r) => r.model === result.chosen?.model) : whitelist.length;
28076
+ const better = result.trace.slice(0, Math.max(chosenIdx, 0));
28077
+ const runningModel = result.chosen?.model ?? null;
28078
+ if (chosenIdx === 0) {
28079
+ return { status: "top", runningModel, pitchModel: null, unavailableAbovePitch: [] };
28080
+ }
28081
+ const pitch = better.find((t) => QUOTA_BLOCKED.has(t.outcome));
28082
+ if (pitch) {
28083
+ const pitchIdx = better.findIndex((t) => t.model === pitch.model);
28084
+ return {
28085
+ status: "lesser-quota",
28086
+ runningModel,
28087
+ pitchModel: pitch.model,
28088
+ unavailableAbovePitch: better.slice(0, pitchIdx).filter((t) => CERTAIN_UNAVAILABLE.has(t.outcome)).map((t) => t.model)
28089
+ };
28090
+ }
28091
+ const allCertain = better.length > 0 && better.every((t) => CERTAIN_UNAVAILABLE.has(t.outcome));
28092
+ return {
28093
+ status: allCertain ? "lesser-unavailable" : "lesser-unverified",
28094
+ runningModel,
28095
+ pitchModel: null,
28096
+ unavailableAbovePitch: []
28097
+ };
28098
+ }
28099
+ var listNames = (names) => names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
28100
+ function renderChosenModelNote(d) {
28101
+ const subject = d.runningModel ? `You are running on ${d.runningModel}.` : "You are running on this install's default model.";
28102
+ switch (d.status) {
28103
+ case "top":
28104
+ return `${subject} That is the best available model for this install.`;
28105
+ case "lesser-quota": {
28106
+ const aside = d.unavailableAbovePitch.length > 0 ? ` ${listNames(d.unavailableAbovePitch)} ${d.unavailableAbovePitch.length > 1 ? "are" : "is"} not offered for this install.` : "";
28107
+ return `${subject} A stronger model, ${d.pitchModel ?? ""}, is offered here, but this subscription has no quota for it.${aside} You can offer to help request that quota.`;
28108
+ }
28109
+ case "lesser-unavailable":
28110
+ return `${subject} No stronger model is offered for this install right now.`;
28111
+ case "lesser-unverified":
28112
+ return `${subject} It was not possible to check which stronger models this subscription can run, so do not make claims about what is or is not available.`;
28113
+ }
28114
+ }
28115
+
28116
+ // src/lib/intake-model.ts
28117
+ var DEFAULT_DEADLINE_MS = 18e4;
28118
+ var SCOPE_RE = /\/resourceGroups\/([^/]+)\/providers\/Microsoft\.CognitiveServices\/accounts\/([^/]+)/i;
28119
+ function parseAccountScope(scope) {
28120
+ const m = SCOPE_RE.exec(scope);
28121
+ return m ? { resourceGroup: m[1], account: m[2] } : null;
28122
+ }
28123
+ function narrate(model, outcome, region) {
28124
+ if (outcome === "deployed") return `${model} - ready`;
28125
+ return `${model} - not available for your subscription in ${region}`;
28126
+ }
28127
+ async function accountRegion(account, resourceGroup, fallback) {
28128
+ try {
28129
+ const out = await runAz([
28130
+ "cognitiveservices",
28131
+ "account",
28132
+ "show",
28133
+ "--name",
28134
+ account,
28135
+ "--resource-group",
28136
+ resourceGroup,
28137
+ "--query",
28138
+ "location",
28139
+ "-o",
28140
+ "tsv"
28141
+ ]);
28142
+ return out.trim() || fallback;
28143
+ } catch {
28144
+ return fallback;
28145
+ }
28146
+ }
28147
+ async function resolveIntakeModel(args) {
28148
+ const floorTrace = (outcome, detail) => WHITELIST.map((r) => ({ model: r.model, outcome, ...detail === void 0 ? {} : { detail } }));
28149
+ const degradeToFloor = (detail) => {
28150
+ const trace = floorTrace("catalog-unverified", detail);
28151
+ return { model: void 0, note: renderChosenModelNote(decideNote(WHITELIST, { chosen: null, trace })), trace };
28152
+ };
28153
+ const parsed = parseAccountScope(args.accountScope);
28154
+ if (!parsed) return degradeToFloor(`unparseable account scope: ${args.accountScope}`);
28155
+ try {
28156
+ const region = await accountRegion(parsed.account, parsed.resourceGroup, args.fallbackRegion);
28157
+ args.onNarrate?.("choosing the best model your subscription can run...");
28158
+ const [catalog, quota] = await Promise.all([listAgentModels(region), listModelQuota(region)]);
28159
+ const plan = planCascade(WHITELIST, catalog, quota);
28160
+ const deploy = async (rung) => {
28161
+ await ensureDeployment({
28162
+ account: parsed.account,
28163
+ resourceGroup: parsed.resourceGroup,
28164
+ model: rung.model,
28165
+ modelVersion: rung.version,
28166
+ capacity: rung.capacity,
28167
+ format: rung.format
28168
+ });
28169
+ };
28170
+ const start = Date.now();
28171
+ const result = await walkCascade(WHITELIST, plan, deploy, {
28172
+ now: () => Date.now(),
28173
+ deadlineAt: start + (args.deadlineMs ?? DEFAULT_DEADLINE_MS),
28174
+ onRung: (model, outcome) => args.onNarrate?.(narrate(model, outcome, region))
28175
+ });
28176
+ return {
28177
+ model: result.chosen?.model,
28178
+ note: renderChosenModelNote(decideNote(WHITELIST, result)),
28179
+ trace: result.trace
28180
+ };
28181
+ } catch (e) {
28182
+ return degradeToFloor(e instanceof Error ? e.message : String(e));
28183
+ }
28184
+ }
28185
+
27933
28186
  // src/commands/bootstrap/ui.ts
27934
28187
  function renderDeploySuccess(version, envPath) {
27935
28188
  return `${colors.success("\u2713")} Simple Stacey is live (stacey-intake v${version}).
@@ -28031,13 +28284,24 @@ var BootstrapUiCommand = class extends M8tCommand {
28031
28284
  await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
28032
28285
  const identity = await getSignedInUserIdentity();
28033
28286
  out("deploying Simple Stacey (stacey-intake) in the background...");
28034
- const deployOutcome = deploySimpleStaceyWithRetry({
28035
- credential: credential2,
28036
- endpoint,
28037
- repoRoot,
28038
- fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) },
28039
- onWait: out
28040
- }).then(
28287
+ const deployOutcome = (async () => {
28288
+ const choice = await resolveIntakeModel({
28289
+ accountScope,
28290
+ fallbackRegion: state.location,
28291
+ onNarrate: out
28292
+ });
28293
+ return deploySimpleStaceyWithRetry({
28294
+ credential: credential2,
28295
+ endpoint,
28296
+ repoRoot,
28297
+ model: choice.model,
28298
+ fieldOverrides: {
28299
+ founder_identity_note: composeFounderIdentityNote(identity),
28300
+ chosen_model_note: choice.note
28301
+ },
28302
+ onWait: out
28303
+ });
28304
+ })().then(
28041
28305
  (version) => ({ ok: true, version }),
28042
28306
  (error) => ({ ok: false, error: error instanceof Error ? error : new Error(String(error)) })
28043
28307
  );