squadrant 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -225,7 +225,12 @@ var init_config = __esm({
225
225
 
226
226
  // packages/shared/dist/provider-preset.js
227
227
  function isProviderPresetId(v2) {
228
- return v2 === "a" || v2 === "b" || v2 === "c" || v2 === "d";
228
+ return v2 === "a" || v2 === "b" || v2 === "c";
229
+ }
230
+ function normalizePresetId(v2) {
231
+ if (v2 === "d")
232
+ return "c";
233
+ return isProviderPresetId(v2) ? v2 : void 0;
229
234
  }
230
235
  function presetCrewRouting(id) {
231
236
  const base = (tier) => {
@@ -248,17 +253,10 @@ function presetCrewRouting(id) {
248
253
  { ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
249
254
  ];
250
255
  case "c":
251
- return [
252
- { ...base("extreme"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
253
- { ...base("hard"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
254
- codex("mobile"),
255
- { ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
256
- ];
257
- case "d":
258
256
  return ["extreme", "hard", "mobile", "daily"].map(codex);
259
257
  }
260
258
  }
261
- function providerPresetDefaults(id, router) {
259
+ function providerPresetDefaults(id) {
262
260
  switch (id) {
263
261
  case "a":
264
262
  return {
@@ -287,28 +285,6 @@ function providerPresetDefaults(id, router) {
287
285
  };
288
286
  }
289
287
  case "c": {
290
- if (!router) {
291
- throw new Error("preset C (claude harness + router backend) requires a router config \u2014 set defaults.router first");
292
- }
293
- const routed = { agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL };
294
- return {
295
- roles: {
296
- command: { ...routed },
297
- captain: { ...routed },
298
- crew: { ...routed },
299
- exploration: { ...routed },
300
- side: { ...routed }
301
- },
302
- // Auto mode's classifier is hardcoded to Sonnet 5 and fails closed on a
303
- // router upstream — every claude session must use a manual mode so the
304
- // U7 gate owns the prompt.
305
- permissions: { command: "default", captain: "default", crew: "default" },
306
- crewRouting: { rules: presetCrewRouting("c") },
307
- router,
308
- gate: { mode: "on" }
309
- };
310
- }
311
- case "d": {
312
288
  const codex = { agent: "codex" };
313
289
  return {
314
290
  roles: {
@@ -319,7 +295,7 @@ function providerPresetDefaults(id, router) {
319
295
  side: { ...codex }
320
296
  },
321
297
  permissions: {},
322
- crewRouting: { rules: presetCrewRouting("d") }
298
+ crewRouting: { rules: presetCrewRouting("c") }
323
299
  };
324
300
  }
325
301
  }
@@ -329,13 +305,11 @@ function detectProviderPresetId(config) {
329
305
  if (crew?.agent === "opencode")
330
306
  return "b";
331
307
  if (crew?.agent === "codex")
332
- return "d";
333
- if (crew?.backend && crew.backend !== "native")
334
308
  return "c";
335
309
  return "a";
336
310
  }
337
311
  function applyProviderPreset(config, id, opts) {
338
- const preset = providerPresetDefaults(id, opts.router);
312
+ const preset = providerPresetDefaults(id);
339
313
  const next = structuredClone(config);
340
314
  const changes = [];
341
315
  if (opts.overwrite) {
@@ -361,21 +335,9 @@ function applyProviderPreset(config, id, opts) {
361
335
  next.defaults.permissions = { ...preset.permissions };
362
336
  changes.push("defaults.permissions");
363
337
  }
364
- if (preset.router) {
365
- if (opts.overwrite || next.defaults.router === void 0) {
366
- next.defaults.router = structuredClone(preset.router);
367
- changes.push("defaults.router");
368
- }
369
- }
370
- if (preset.gate) {
371
- if (opts.overwrite || next.defaults.gate === void 0) {
372
- next.defaults.gate = { ...preset.gate };
373
- changes.push("defaults.gate");
374
- }
375
- }
376
338
  return { config: next, changes };
377
339
  }
378
- var PROVIDER_PRESETS, ROUTER_PRESET_MODEL, DEFAULT_ROUTER_KIND, DEFAULT_ROUTER_BASE_URL, OPENCODE_MODEL;
340
+ var PROVIDER_PRESETS, OPENCODE_MODEL;
379
341
  var init_provider_preset = __esm({
380
342
  "packages/shared/dist/provider-preset.js"() {
381
343
  init_config();
@@ -394,20 +356,11 @@ var init_provider_preset = __esm({
394
356
  },
395
357
  {
396
358
  id: "c",
397
- label: "Claude harness + router backend (advanced)",
398
- summary: "Claude harness against a router upstream. The U7 gate replaces auto mode.",
399
- requiresAnthropic: false
400
- },
401
- {
402
- id: "d",
403
359
  label: "Codex (ChatGPT Pro)",
404
360
  summary: "All roles run on the codex harness.",
405
361
  requiresAnthropic: false
406
362
  }
407
363
  ];
408
- ROUTER_PRESET_MODEL = "deepseek-v4.1-flash";
409
- DEFAULT_ROUTER_KIND = "opencode-go";
410
- DEFAULT_ROUTER_BASE_URL = "https://opencode.ai/zen/go";
411
364
  OPENCODE_MODEL = "opencode-go/deepseek-v4.1-flash";
412
365
  }
413
366
  });
@@ -1661,15 +1614,12 @@ __export(dist_exports, {
1661
1614
  DEFAULT_CONFIG_PATH: () => DEFAULT_CONFIG_PATH,
1662
1615
  DEFAULT_CREW_ROUTING_RULES: () => DEFAULT_CREW_ROUTING_RULES,
1663
1616
  DEFAULT_NOTIFY: () => DEFAULT_NOTIFY,
1664
- DEFAULT_ROUTER_BASE_URL: () => DEFAULT_ROUTER_BASE_URL,
1665
- DEFAULT_ROUTER_KIND: () => DEFAULT_ROUTER_KIND,
1666
1617
  GATE_ENGINES: () => GATE_ENGINES,
1667
1618
  GATE_MODES: () => GATE_MODES,
1668
1619
  GATE_POLICIES: () => GATE_POLICIES,
1669
1620
  MANAGED_TARGETS: () => MANAGED_TARGETS,
1670
1621
  PROVIDER_PRESETS: () => PROVIDER_PRESETS,
1671
1622
  ROUTER_KINDS: () => ROUTER_KINDS,
1672
- ROUTER_PRESET_MODEL: () => ROUTER_PRESET_MODEL,
1673
1623
  SAFE_KINDS: () => SAFE_KINDS,
1674
1624
  SOCKET_CONTROL_MODE_PATH: () => SOCKET_CONTROL_MODE_PATH,
1675
1625
  SPOKE_SUBDIRS: () => SPOKE_SUBDIRS,
@@ -1726,6 +1676,7 @@ __export(dist_exports, {
1726
1676
  mirrorDir: () => mirrorDir,
1727
1677
  mirrorFlat: () => mirrorFlat,
1728
1678
  needsCheck: () => needsCheck,
1679
+ normalizePresetId: () => normalizePresetId,
1729
1680
  notifyIfUpdateAvailable: () => notifyIfUpdateAvailable,
1730
1681
  parseSection: () => parseSection,
1731
1682
  parseThinkingLevel: () => parseThinkingLevel,
@@ -5448,8 +5399,10 @@ async function resolveAndPersistOpencodeCaptain(opts) {
5448
5399
  sleep: opts.sleep,
5449
5400
  fetchImpl: opts.fetchImpl
5450
5401
  });
5451
- if (!sessionId)
5402
+ if (!sessionId) {
5403
+ opts.log?.(`opencode captain session resolution timed out for ${opts.project} (${opts.directory}) \u2014 captain.json has no session id yet; the daemon's own re-resolve fills it in once a session appears`);
5452
5404
  return null;
5405
+ }
5453
5406
  writeCaptainAddress(opts.stateRoot, opts.project, {
5454
5407
  agent: "opencode",
5455
5408
  port: opts.port,
@@ -5469,6 +5422,33 @@ var init_opencode_session = __esm({
5469
5422
  function discoverCaptainSurface(surfaces, captainTitle) {
5470
5423
  return surfaces.find((s) => s.title === captainTitle) ?? null;
5471
5424
  }
5425
+ async function reresolveOpencodeAddress(rec) {
5426
+ const live = discoverLiveOpencodeServer({ directory: rec.directory, sessionId: rec.sessionId });
5427
+ const port = live?.port ?? rec.port;
5428
+ if (port === void 0)
5429
+ return null;
5430
+ const newest = newestSessionInDirectory(await listSessions(port), rec.directory, Date.parse(rec.launchedAt));
5431
+ const sessionId = newest ?? live?.sessionId ?? rec.sessionId;
5432
+ if (!sessionId || port === rec.port && sessionId === rec.sessionId)
5433
+ return null;
5434
+ return { port, sessionId };
5435
+ }
5436
+ async function healStaleOpencodeAddress(stateRoot, project, log, logPrefix) {
5437
+ const rec = readCaptainAddress(stateRoot, project);
5438
+ if (!rec)
5439
+ return false;
5440
+ const resolved = await reresolveOpencodeAddress(rec);
5441
+ if (!resolved)
5442
+ return false;
5443
+ const current = readCaptainAddress(stateRoot, project);
5444
+ if (!current || current.launchedAt !== rec.launchedAt) {
5445
+ log(`captain-channel ${project}: skipped stale-address heal \u2014 a newer launch record won the race`);
5446
+ return false;
5447
+ }
5448
+ writeCaptainAddress(stateRoot, project, { ...current, port: resolved.port, sessionId: resolved.sessionId });
5449
+ log(`captain-channel ${project}: ${logPrefix} \u2192 port ${resolved.port}`);
5450
+ return true;
5451
+ }
5472
5452
  async function reapOrphanedCrews(store, project, isSurfaceAlive) {
5473
5453
  let reaped = 0;
5474
5454
  for (const r of store.list(project)) {
@@ -5693,27 +5673,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
5693
5673
  log(`captain-channel ${project}: threw, falling back to pane \u2014 ${e.message}`);
5694
5674
  }
5695
5675
  if (agent === "opencode" && mode !== "off") {
5696
- const rec = readCaptainAddress(stateRoot, project);
5697
- let healed = false;
5698
- if (rec?.port) {
5699
- const fresh = newestSessionInDirectory(await listSessions(rec.port), rec.directory, Date.parse(rec.launchedAt));
5700
- if (fresh && fresh !== rec.sessionId) {
5701
- writeCaptainAddress(stateRoot, project, { ...rec, sessionId: fresh });
5702
- healed = true;
5703
- }
5704
- }
5705
- if (!healed && rec) {
5706
- const live = discoverLiveOpencodeServer({ directory: rec.directory, sessionId: rec.sessionId });
5707
- if (live && (live.port !== rec.port || live.sessionId && live.sessionId !== rec.sessionId)) {
5708
- writeCaptainAddress(stateRoot, project, {
5709
- ...rec,
5710
- port: live.port,
5711
- sessionId: live.sessionId ?? rec.sessionId
5712
- });
5713
- log(`captain-channel ${project}: re-resolved opencode address \u2192 port ${live.port}`);
5714
- healed = true;
5715
- }
5716
- }
5676
+ const healed = await healStaleOpencodeAddress(stateRoot, project, log, "re-resolved opencode address");
5717
5677
  if (healed) {
5718
5678
  try {
5719
5679
  const retry = await deliverToCaptain(project, text, { channel: picked, mode, log });
@@ -6571,11 +6531,15 @@ function parseCommand(text) {
6571
6531
  }
6572
6532
  return entry.build(args);
6573
6533
  }
6574
- var WRITABLE_CONFIG_KEYS, EFFORT_MODES, REGISTRY;
6534
+ var WRITABLE_CONFIG_KEYS, EFFORT_MODES, GATE_MODES2, CONFIG_VALUE_VALIDATORS, REGISTRY;
6575
6535
  var init_commands = __esm({
6576
6536
  "packages/core/dist/telegram/commands.js"() {
6577
- WRITABLE_CONFIG_KEYS = ["defaults.effort"];
6537
+ WRITABLE_CONFIG_KEYS = ["defaults.effort", "defaults.gate.mode"];
6578
6538
  EFFORT_MODES = /* @__PURE__ */ new Set(["max", "balance", "low"]);
6539
+ GATE_MODES2 = /* @__PURE__ */ new Set(["on", "off", "auto"]);
6540
+ CONFIG_VALUE_VALIDATORS = {
6541
+ "defaults.gate.mode": { valid: GATE_MODES2, usage: "usage: /config set defaults.gate.mode <on|off|auto>" }
6542
+ };
6579
6543
  REGISTRY = {
6580
6544
  status: { usage: "/status", build: () => ok("status", ["status"]) },
6581
6545
  projects: { usage: "/projects", build: () => ok("projects", ["projects", "list"]) },
@@ -6622,6 +6586,10 @@ var init_commands = __esm({
6622
6586
  message: `\u26D4 '${key}' is not writable over Telegram. Allowed: ${WRITABLE_CONFIG_KEYS.join(", ")}`
6623
6587
  };
6624
6588
  }
6589
+ const validator = CONFIG_VALUE_VALIDATORS[key];
6590
+ if (validator && !validator.valid.has(value)) {
6591
+ return usage("config", validator.usage);
6592
+ }
6625
6593
  return ok("config", ["config", "set", key, value]);
6626
6594
  }
6627
6595
  return usage("config", "usage: /config get <key> | /config set <key> <value>");
@@ -9119,7 +9087,7 @@ var init_permission_gate = __esm({
9119
9087
  });
9120
9088
 
9121
9089
  // packages/core/dist/auto-gate.js
9122
- import { createAutoGate, projectGateConfig } from "@squadrant-ai/auto-gate";
9090
+ import { createAutoGate, projectGateConfig, resolveApiKey } from "@squadrant-ai/auto-gate";
9123
9091
  function gateForProjection(config) {
9124
9092
  const gate = config.defaults.gate;
9125
9093
  if (!gate)
@@ -9133,6 +9101,9 @@ function gateForProjection(config) {
9133
9101
  out.deny = gate.deny;
9134
9102
  return out;
9135
9103
  }
9104
+ function hasAutoGateCredential(env = process.env, home) {
9105
+ return Boolean(resolveApiKey({ env, ...home !== void 0 ? { home } : {} }));
9106
+ }
9136
9107
  function createSquadrantAutoGate(deps) {
9137
9108
  const env = deps.env ?? process.env;
9138
9109
  const taskId = env.SQUADRANT_CREW_TASK_ID;
@@ -10107,6 +10078,7 @@ __export(dist_exports2, {
10107
10078
  freshTrace: () => freshTrace,
10108
10079
  gateCacheKey: () => gateCacheKey,
10109
10080
  getDaemonPid: () => getDaemonPid,
10081
+ hasAutoGateCredential: () => hasAutoGateCredential,
10110
10082
  hasModalOptionList: () => hasModalOptionList,
10111
10083
  healCmdFor: () => healCmdFor,
10112
10084
  inboundBody: () => inboundBody,
@@ -16454,27 +16426,6 @@ function promptLine(question) {
16454
16426
  });
16455
16427
  });
16456
16428
  }
16457
- async function resolveRouter(config, opts, isTTY) {
16458
- if (config.defaults?.router) return config.defaults.router;
16459
- let kind = opts.routerKind;
16460
- let baseUrl = opts.routerBaseUrl;
16461
- let apiKeyEnv = opts.routerApiKeyEnv;
16462
- if (isTTY && (!kind || !baseUrl)) {
16463
- const k = await promptLine(chalk4.cyan(` Router kind [${DEFAULT_ROUTER_KIND}]: `));
16464
- kind = kind ?? (k || DEFAULT_ROUTER_KIND);
16465
- const b = await promptLine(chalk4.cyan(` Router base URL [${DEFAULT_ROUTER_BASE_URL}]: `));
16466
- baseUrl = baseUrl ?? (b || DEFAULT_ROUTER_BASE_URL);
16467
- const e = await promptLine(chalk4.cyan(" Env var holding the API key (optional): "));
16468
- apiKeyEnv = apiKeyEnv ?? (e || void 0);
16469
- }
16470
- const resolvedKind = kind && isRouterKind(kind) ? kind : DEFAULT_ROUTER_KIND;
16471
- const resolvedBase = baseUrl || DEFAULT_ROUTER_BASE_URL;
16472
- if (!isTTY && !opts.routerBaseUrl) {
16473
- console.log(chalk4.dim(` - no router flags given; defaulting to ${resolvedKind} (${resolvedBase})`));
16474
- console.log(chalk4.dim(" set a credential: squadrant config set defaults.router.apiKeyEnv <VAR>"));
16475
- }
16476
- return { kind: resolvedKind, baseUrl: resolvedBase, ...apiKeyEnv ? { apiKeyEnv } : {} };
16477
- }
16478
16429
  async function askProvider(current, auth) {
16479
16430
  console.log(chalk4.bold("\n Choose your provider:"));
16480
16431
  for (const p of PROVIDER_PRESETS) {
@@ -16483,10 +16434,10 @@ async function askProvider(current, auth) {
16483
16434
  console.log(chalk4.dim(` ${p.summary}`));
16484
16435
  }
16485
16436
  if (!auth.authenticated) {
16486
- console.log(chalk4.yellow("\n \u26A0 No Anthropic credential detected \u2014 preset a will not work; consider b or c."));
16437
+ console.log(chalk4.yellow("\n \u26A0 No Anthropic credential detected \u2014 preset a will not work; consider b."));
16487
16438
  }
16488
16439
  const answer = (await promptLine(chalk4.cyan(`
16489
- Provider [a/b/c/d] (default ${current}): `))).toLowerCase();
16440
+ Provider [a/b/c] (default ${current}): `))).toLowerCase();
16490
16441
  if (!answer) return current;
16491
16442
  if (isProviderPresetId(answer)) return answer;
16492
16443
  console.log(chalk4.yellow(` \u26A0 Unknown choice '${answer}' \u2014 keeping '${current}'.`));
@@ -16495,9 +16446,9 @@ async function askProvider(current, auth) {
16495
16446
  function warnIfNoAnthropic(preset, auth) {
16496
16447
  if (preset !== "a" || auth.authenticated) return;
16497
16448
  console.log(chalk4.yellow(" \u26A0 No Anthropic credential detected (`claude auth status`)."));
16498
- console.log(chalk4.dim(" Preset A (Claude Pro/Max or an API key) may not work \u2014 consider preset b or c."));
16449
+ console.log(chalk4.dim(" Preset A (Claude Pro/Max or an API key) may not work \u2014 consider preset b."));
16499
16450
  }
16500
- var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c|d (non-interactive)").option("--router-kind <kind>", "router kind for --preset c (opencode-go|openrouter|ccr|litellm|custom)").option("--router-base-url <url>", "router base URL for --preset c").option("--router-api-key-env <env>", "env var holding the router credential for --preset c").action(async (opts) => {
16451
+ var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c (non-interactive; d accepted as a deprecated alias for c)").action(async (opts) => {
16501
16452
  const hubPath = resolveHome(opts.hub);
16502
16453
  const pkgRoot = findPackageRoot();
16503
16454
  const configDir = path23.join(os13.homedir(), ".config", "squadrant");
@@ -16505,9 +16456,9 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
16505
16456
  console.log(chalk4.bold("\nSquadrant Init\n"));
16506
16457
  let explicitPreset;
16507
16458
  if (opts.preset !== void 0) {
16508
- const candidate = opts.preset.toLowerCase();
16509
- if (!isProviderPresetId(candidate)) {
16510
- console.error(chalk4.red(` \u2718 Unknown --preset '${opts.preset}'. Valid values: a, b, c, d`));
16459
+ const candidate = normalizePresetId(opts.preset.toLowerCase());
16460
+ if (!candidate) {
16461
+ console.error(chalk4.red(` \u2718 Unknown --preset '${opts.preset}'. Valid values: a, b, c`));
16511
16462
  process.exitCode = 1;
16512
16463
  return;
16513
16464
  }
@@ -16517,7 +16468,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
16517
16468
  console.log(" Run these steps to get started:\n");
16518
16469
  console.log(chalk4.bold(" 1/5 Hub vault + provider"));
16519
16470
  console.log(chalk4.cyan(` squadrant init --hub ${opts.hub}`));
16520
- console.log(chalk4.dim(" Choose a provider: squadrant init --preset a|b|c|d"));
16471
+ console.log(chalk4.dim(" Choose a provider: squadrant init --preset a|b|c"));
16521
16472
  console.log(chalk4.bold("\n 2/5 Agent + projection setup"));
16522
16473
  console.log(" (handled automatically by: " + chalk4.cyan("squadrant init") + ")");
16523
16474
  console.log(chalk4.bold("\n 3/5 Plugins \u2014 open Claude Code and run:"));
@@ -16563,9 +16514,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
16563
16514
  let appliedChanges = [];
16564
16515
  if (chosenPreset) {
16565
16516
  const label = PROVIDER_PRESETS.find((p) => p.id === chosenPreset)?.label ?? chosenPreset;
16566
- const router = chosenPreset === "c" ? await resolveRouter(config, opts, isTTY) : void 0;
16567
16517
  const overwrite = !configExists || explicitPreset !== void 0;
16568
- const result = applyProviderPreset(config, chosenPreset, { router, overwrite });
16518
+ const result = applyProviderPreset(config, chosenPreset, { overwrite });
16569
16519
  appliedChanges = result.changes;
16570
16520
  config = result.config;
16571
16521
  console.log(chalk4.bold(`
@@ -20175,7 +20125,7 @@ var launchCommand = new Command14("launch").description(
20175
20125
  sessionId: priorRecord?.sessionId
20176
20126
  });
20177
20127
  if (!live) return;
20178
- const sessionId = live.sessionId ?? priorRecord?.sessionId;
20128
+ const sessionId = live.sessionId ?? (live.port === priorRecord?.port ? priorRecord?.sessionId : void 0);
20179
20129
  if (priorRecord && priorRecord.port === live.port && priorRecord.sessionId === sessionId) return;
20180
20130
  writeCaptainAddress(stateRoot, projectName, {
20181
20131
  agent: "opencode",
@@ -20189,13 +20139,20 @@ var launchCommand = new Command14("launch").description(
20189
20139
  console.log(chalk15.green(` \u2714 Workspace '${name}' created`));
20190
20140
  if (!projectName) return;
20191
20141
  if (isOpencodeCaptain && captainPort) {
20142
+ writeCaptainAddress(stateRoot, projectName, {
20143
+ agent: "opencode",
20144
+ port: captainPort,
20145
+ directory: realpathOrSelf(cwd),
20146
+ launchedAt
20147
+ });
20192
20148
  void resolveAndPersistOpencodeCaptain({
20193
20149
  stateRoot,
20194
20150
  project: projectName,
20195
20151
  port: captainPort,
20196
20152
  directory: realpathOrSelf(cwd),
20197
20153
  launchedAt,
20198
- sessionId: opencodeResumeSessionId
20154
+ sessionId: opencodeResumeSessionId,
20155
+ log: (m) => console.error(chalk15.yellow(` \u26A0 ${m}`))
20199
20156
  }).catch(() => {
20200
20157
  });
20201
20158
  } else if (role === "captain") {
@@ -22585,6 +22542,7 @@ init_dist2();
22585
22542
  init_dist();
22586
22543
  init_dist3();
22587
22544
  import { Command as Command32 } from "commander";
22545
+ import chalk33 from "chalk";
22588
22546
  var SOCK4 = DAEMON_SOCK_PATH;
22589
22547
  async function runAutoGateClaudeHook(o) {
22590
22548
  const make = o.deps.createAutoGate ?? createSquadrantAutoGate;
@@ -22669,10 +22627,60 @@ async function runGatePermissionRequest(deps) {
22669
22627
  return evaluation;
22670
22628
  }
22671
22629
  var DEFAULT_EVENT = "permission-request";
22630
+ function runGateModeGet(configPath = DEFAULT_CONFIG_PATH, env = process.env) {
22631
+ const config = loadConfig(configPath);
22632
+ const mode = resolveGateMode(env, config.defaults.gate);
22633
+ const source = env.SQUADRANT_GATE !== void 0 && isGateMode(env.SQUADRANT_GATE) ? "env" : config.defaults.gate?.mode && isGateMode(config.defaults.gate.mode) ? "config" : "default";
22634
+ return { mode, source };
22635
+ }
22636
+ function runGateModeSet(value, configPath = DEFAULT_CONFIG_PATH) {
22637
+ if (!isGateMode(value)) {
22638
+ throw new Error(`Invalid gate mode '${value}'. Valid values: ${GATE_MODES.join(" | ")}`);
22639
+ }
22640
+ const config = loadConfig(configPath);
22641
+ const old = config.defaults.gate?.mode && isGateMode(config.defaults.gate.mode) ? config.defaults.gate.mode : "auto";
22642
+ config.defaults.gate = { ...config.defaults.gate, mode: value };
22643
+ saveConfig(config, configPath);
22644
+ return { old, next: value };
22645
+ }
22646
+ function runGateStatus(configPath = DEFAULT_CONFIG_PATH, env = process.env) {
22647
+ const config = loadConfig(configPath);
22648
+ const { mode, source } = runGateModeGet(configPath, env);
22649
+ const engine = resolveGateEngine(env, config.defaults.gate);
22650
+ const model = resolveClassifierModel(env, config);
22651
+ const credentialPresent = engine === "auto-gate" ? hasAutoGateCredential(env) : Boolean(config.defaults.router?.apiKey ?? (config.defaults.router?.apiKeyEnv ? env[config.defaults.router.apiKeyEnv] : void 0));
22652
+ return { mode, source, engine, model, credentialPresent };
22653
+ }
22672
22654
  function gateCommand() {
22673
22655
  const gate = new Command32("gate").description(
22674
- "(internal) hook-based permission gate for router-backed crew/side sessions (#782)"
22656
+ "Permission gate (#782/#854): get/set defaults.gate.mode; 'claude <event>' is an internal hook handler"
22675
22657
  );
22658
+ gate.command("mode [value]").description("Get or set the permission gate mode (on | off | auto)").action((value) => {
22659
+ if (value === void 0) {
22660
+ const result2 = runGateModeGet();
22661
+ console.log(chalk33.bold("Gate mode:"), chalk33.cyan(result2.mode), chalk33.dim(`(${result2.source})`));
22662
+ return;
22663
+ }
22664
+ let result;
22665
+ try {
22666
+ result = runGateModeSet(value);
22667
+ } catch (e) {
22668
+ console.error(chalk33.red(e.message));
22669
+ process.exit(1);
22670
+ }
22671
+ console.log(chalk33.green(`\u2714 gate mode: ${result.old} \u2192 ${result.next}`));
22672
+ console.log(chalk33.dim("No daemon bounce needed \u2014 defaults.gate.mode is read live."));
22673
+ if (isGateMode(process.env.SQUADRANT_GATE ?? "") && process.env.SQUADRANT_GATE !== result.next) {
22674
+ console.log(chalk33.yellow(`Note: SQUADRANT_GATE=${process.env.SQUADRANT_GATE} in this session still overrides the config value.`));
22675
+ }
22676
+ });
22677
+ gate.command("status").description("Show the effective gate mode, engine, classifier model, and credential presence").action(() => {
22678
+ const result = runGateStatus();
22679
+ console.log(chalk33.bold("Gate mode:"), chalk33.cyan(result.mode), chalk33.dim(`(${result.source})`));
22680
+ console.log(chalk33.bold("Engine:"), result.engine);
22681
+ console.log(chalk33.bold("Classifier model:"), result.model ?? chalk33.dim("(unset)"));
22682
+ console.log(chalk33.bold("Credential:"), result.credentialPresent ? chalk33.green("present") : chalk33.yellow("absent"));
22683
+ });
22676
22684
  gate.command("claude <event>", { hidden: true }).description("internal: classify a claude PermissionRequest with the configured router model").action(async (event) => {
22677
22685
  let stdin = "";
22678
22686
  try {
@@ -22826,7 +22834,7 @@ init_dist();
22826
22834
  init_dist2();
22827
22835
  import path34 from "path";
22828
22836
  import { Command as Command34 } from "commander";
22829
- import chalk33 from "chalk";
22837
+ import chalk34 from "chalk";
22830
22838
  function detectCurrentProject(config, cwd = process.cwd()) {
22831
22839
  for (const [name, proj] of Object.entries(config.projects)) {
22832
22840
  const projPath = resolveHome(proj.path);
@@ -22866,21 +22874,21 @@ function visibleItems(items, includeDone) {
22866
22874
  function stateColor(state) {
22867
22875
  switch (state) {
22868
22876
  case "done":
22869
- return chalk33.green;
22877
+ return chalk34.green;
22870
22878
  case "cancelled":
22871
- return chalk33.dim;
22879
+ return chalk34.dim;
22872
22880
  case "blocked":
22873
- return chalk33.red;
22881
+ return chalk34.red;
22874
22882
  case "paused":
22875
- return chalk33.yellow;
22883
+ return chalk34.yellow;
22876
22884
  default:
22877
- return chalk33.cyan;
22885
+ return chalk34.cyan;
22878
22886
  }
22879
22887
  }
22880
22888
  function printItem(item, indent) {
22881
22889
  const color = stateColor(item.state);
22882
- const line = " ".repeat(indent) + `${chalk33.dim(item.id)} ${item.title} ${color(`[${item.state}]`)}` + (indent === 0 ? chalk33.dim(` (${item.project})`) : "");
22883
- console.log(TERMINAL_WORK_STATES.has(item.state) ? chalk33.dim(line) : line);
22890
+ const line = " ".repeat(indent) + `${chalk34.dim(item.id)} ${item.title} ${color(`[${item.state}]`)}` + (indent === 0 ? chalk34.dim(` (${item.project})`) : "");
22891
+ console.log(TERMINAL_WORK_STATES.has(item.state) ? chalk34.dim(line) : line);
22884
22892
  }
22885
22893
  function printTree(items) {
22886
22894
  const { roots, childrenOf } = groupByParent(items);
@@ -22899,15 +22907,15 @@ var startCmd = new Command34("start").description("Start a new work item").argum
22899
22907
  purgeExpiredWorkItems(store);
22900
22908
  const project = opts.project ?? detectCurrentProject(config);
22901
22909
  if (!project) {
22902
- console.error(chalk33.red("No --project given and cwd is not inside a registered project."));
22910
+ console.error(chalk34.red("No --project given and cwd is not inside a registered project."));
22903
22911
  process.exit(1);
22904
22912
  }
22905
22913
  if (opts.parent && !findWorkItemById(store, opts.parent)) {
22906
- console.error(chalk33.red(`Parent work item '${opts.parent}' not found.`));
22914
+ console.error(chalk34.red(`Parent work item '${opts.parent}' not found.`));
22907
22915
  process.exit(1);
22908
22916
  }
22909
22917
  const item = createWorkItem(store, { project, title, parent: opts.parent ?? null, tags: opts.tag });
22910
- console.log(chalk33.green(`\u2713 ${item.id}`) + ` ${item.title}` + chalk33.dim(` (${item.project})`));
22918
+ console.log(chalk34.green(`\u2713 ${item.id}`) + ` ${item.title}` + chalk34.dim(` (${item.project})`));
22911
22919
  });
22912
22920
  var listCmd2 = new Command34("list").description("List work items").option("--project <name>", "scope to one project").option("--all", "list across every project").option("--tree", "render parent/child nesting").option("--include-done", "include done/cancelled items").action((opts) => {
22913
22921
  const config = loadConfig();
@@ -22921,14 +22929,14 @@ var listCmd2 = new Command34("list").description("List work items").option("--pr
22921
22929
  } else {
22922
22930
  const project = detectCurrentProject(config);
22923
22931
  if (!project) {
22924
- console.error(chalk33.red("cwd is not inside a registered project \u2014 pass --project or --all."));
22932
+ console.error(chalk34.red("cwd is not inside a registered project \u2014 pass --project or --all."));
22925
22933
  process.exit(1);
22926
22934
  }
22927
22935
  items = store.list(project);
22928
22936
  }
22929
22937
  items = visibleItems(items, opts.includeDone ?? false);
22930
22938
  if (items.length === 0) {
22931
- console.log(chalk33.dim("\nNo work items.\n"));
22939
+ console.log(chalk34.dim("\nNo work items.\n"));
22932
22940
  return;
22933
22941
  }
22934
22942
  console.log();
@@ -22942,15 +22950,15 @@ function closeCommand(name, state, flag, key, desc) {
22942
22950
  purgeExpiredWorkItems(store);
22943
22951
  const item = closeWorkItem(store, id, state, { note: opts[key] });
22944
22952
  if (!item) {
22945
- console.error(chalk33.red(`Work item '${id}' not found.`));
22953
+ console.error(chalk34.red(`Work item '${id}' not found.`));
22946
22954
  process.exit(1);
22947
22955
  }
22948
- console.log(chalk33.green(`\u2713 ${item.id}`) + ` ${item.title} ${chalk33.dim(`[${item.state}]`)}`);
22956
+ console.log(chalk34.green(`\u2713 ${item.id}`) + ` ${item.title} ${chalk34.dim(`[${item.state}]`)}`);
22949
22957
  if (state === "done") {
22950
22958
  const openChildren = findOpenChildren(store, item.id);
22951
22959
  if (openChildren.length > 0) {
22952
22960
  const names = openChildren.map((c) => `${c.id} [${c.state}]`).join(", ");
22953
- console.log(chalk33.yellow(`\u26A0 still has ${openChildren.length} unfinished child item(s): ${names}`));
22961
+ console.log(chalk34.yellow(`\u26A0 still has ${openChildren.length} unfinished child item(s): ${names}`));
22954
22962
  }
22955
22963
  }
22956
22964
  });
@@ -23433,7 +23441,7 @@ handoffCommand.command("facts <project>").description(
23433
23441
  init_dist();
23434
23442
  init_dist3();
23435
23443
  import { Command as Command36 } from "commander";
23436
- import chalk34 from "chalk";
23444
+ import chalk35 from "chalk";
23437
23445
  var KNOWN_AGENTS = ["claude", "opencode", "codex", "gemini"];
23438
23446
  function normalized(p) {
23439
23447
  return p.replace(/\/+$/, "");
@@ -23486,7 +23494,7 @@ async function collectSessions(drivers) {
23486
23494
  }
23487
23495
  var sessionsCommand = new Command36("sessions").description("List live agent sessions squadrant can introspect (read-only; claude + opencode)").option("--json", "emit machine-readable JSON").option("--agent <name>", `filter to one agent (${KNOWN_AGENTS.join("|")})`).option("--project <name>", "filter to one registered project").option("--live-only", "drop sessions whose process is dead (status: stale)").action(async (opts) => {
23488
23496
  if (opts.agent && !KNOWN_AGENTS.includes(opts.agent)) {
23489
- console.error(chalk34.red(`
23497
+ console.error(chalk35.red(`
23490
23498
  \u2718 Unknown agent '${opts.agent}'. Known agents: ${KNOWN_AGENTS.join(", ")}
23491
23499
  `));
23492
23500
  process.exit(1);
@@ -23494,7 +23502,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
23494
23502
  const config = loadConfig();
23495
23503
  if (opts.project && !(opts.project in config.projects)) {
23496
23504
  const known = Object.keys(config.projects).sort().join(", ") || "(none registered)";
23497
- console.error(chalk34.red(`
23505
+ console.error(chalk35.red(`
23498
23506
  \u2718 Unknown project '${opts.project}'. Known projects: ${known}
23499
23507
  `));
23500
23508
  process.exit(1);
@@ -23510,7 +23518,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
23510
23518
  };
23511
23519
  const { rows, unsupported } = await collectSessions(drivers);
23512
23520
  if (opts.agent && unsupported.includes(opts.agent)) {
23513
- console.error(chalk34.red(`
23521
+ console.error(chalk35.red(`
23514
23522
  \u2718 Agent '${opts.agent}' does not support session introspection.
23515
23523
  `));
23516
23524
  process.exit(1);
@@ -23526,7 +23534,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
23526
23534
  } else {
23527
23535
  console.log(formatSessionTable(filtered));
23528
23536
  if (!opts.agent && unsupported.length > 0) {
23529
- console.error(chalk34.dim(`(unsupported for session introspection: ${unsupported.join(", ")})`));
23537
+ console.error(chalk35.dim(`(unsupported for session introspection: ${unsupported.join(", ")})`));
23530
23538
  }
23531
23539
  }
23532
23540
  });
@@ -23538,7 +23546,7 @@ init_dist3();
23538
23546
  import os20 from "os";
23539
23547
  import path38 from "path";
23540
23548
  import { Command as Command37 } from "commander";
23541
- import chalk35 from "chalk";
23549
+ import chalk36 from "chalk";
23542
23550
 
23543
23551
  // packages/cli/src/lib/whoami.ts
23544
23552
  function projectForCwd(cwd, projects) {
@@ -23697,7 +23705,7 @@ var whoamiCommand = new Command37("whoami").description("Show the agent session
23697
23705
  } else {
23698
23706
  console.log(formatWhoami(result));
23699
23707
  if (!result.ok) {
23700
- console.error(chalk35.yellow("\n Could not identify a squadrant agent session for this invocation."));
23708
+ console.error(chalk36.yellow("\n Could not identify a squadrant agent session for this invocation."));
23701
23709
  }
23702
23710
  }
23703
23711
  if (!result.ok) process.exitCode = 1;