@integrity-labs/agt-cli 0.28.1030 → 0.28.1032

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.
@@ -266,7 +266,7 @@ async function spawnPairSession(session) {
266
266
  return { ok: true };
267
267
  } catch {
268
268
  }
269
- const { resolveClaudeBinary } = await import("./persistent-session-BCEKKB2Y.js");
269
+ const { resolveClaudeBinary } = await import("./persistent-session-TOIMQIAU.js");
270
270
  const claudeBin = resolveClaudeBinary();
271
271
  const pairEnv = {
272
272
  ...process.env,
@@ -734,4 +734,4 @@ export {
734
734
  startClaudePair,
735
735
  submitClaudePairCode
736
736
  };
737
- //# sourceMappingURL=claude-pair-runtime-3LB2CNFQ.js.map
737
+ //# sourceMappingURL=claude-pair-runtime-KQDXWMWB.js.map
@@ -66,7 +66,7 @@ import {
66
66
  safeWriteJsonAtomic,
67
67
  setConfigHash,
68
68
  tripClass
69
- } from "../chunk-TBCJ5NNP.js";
69
+ } from "../chunk-OGITTSBM.js";
70
70
  import {
71
71
  getProjectDir as getProjectDir2,
72
72
  getReadyTasks,
@@ -149,7 +149,7 @@ import {
149
149
  takeZombieDetection,
150
150
  toOpencodeModel,
151
151
  writeEgressAllowlist
152
- } from "../chunk-4KNUH4DQ.js";
152
+ } from "../chunk-GLOCISGD.js";
153
153
  import {
154
154
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
155
155
  AnchorSessionClient,
@@ -254,7 +254,7 @@ import {
254
254
  subagentActivityAgeSeconds,
255
255
  sumTranscriptUsageInWindow,
256
256
  transcriptActivityAgeSeconds
257
- } from "../chunk-VUC5XNUP.js";
257
+ } from "../chunk-NCIGLRCL.js";
258
258
  import {
259
259
  classifyUnanswerablePane,
260
260
  paneRemedy
@@ -13927,7 +13927,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
13927
13927
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
13928
13928
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
13929
13929
  async function channelInboundActivityAgeSecondsFor(codeName) {
13930
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-6S6ZELJG.js");
13930
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-VJOXOK66.js");
13931
13931
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
13932
13932
  if (newest === null) return null;
13933
13933
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -14673,7 +14673,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
14673
14673
  var lastVersionCheckAt = 0;
14674
14674
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
14675
14675
  var lastResponsivenessProbeAt = 0;
14676
- var agtCliVersion = true ? "0.28.1030" : "dev";
14676
+ var agtCliVersion = true ? "0.28.1032" : "dev";
14677
14677
  function resolveBrewPath(execFileSync3) {
14678
14678
  try {
14679
14679
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -16308,7 +16308,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
16308
16308
  if (codeNames.length === 0) return;
16309
16309
  void (async () => {
16310
16310
  try {
16311
- const { collectDiagnostics } = await import("../persistent-session-BCEKKB2Y.js");
16311
+ const { collectDiagnostics } = await import("../persistent-session-TOIMQIAU.js");
16312
16312
  await api.post("/host/heartbeat", {
16313
16313
  host_id: hostId,
16314
16314
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -16450,7 +16450,7 @@ async function pollCycleInner() {
16450
16450
  }
16451
16451
  try {
16452
16452
  const { detectHostSecurity } = await import("../host-security-KQYYZZVW.js");
16453
- const { collectDiagnostics } = await import("../persistent-session-BCEKKB2Y.js");
16453
+ const { collectDiagnostics } = await import("../persistent-session-TOIMQIAU.js");
16454
16454
  const diagCodeNames = [...agentState.persistentSessionAgents];
16455
16455
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor) : void 0;
16456
16456
  let tailscaleHostname;
@@ -16626,7 +16626,7 @@ async function pollCycleInner() {
16626
16626
  collectPanelessActivityProbes,
16627
16627
  getResponsivenessIntervalMs,
16628
16628
  occupancyQualificationClassifications
16629
- } = await import("../responsiveness-probe-6S6ZELJG.js");
16629
+ } = await import("../responsiveness-probe-VJOXOK66.js");
16630
16630
  const probeIntervalMs = getResponsivenessIntervalMs();
16631
16631
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
16632
16632
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -16743,7 +16743,7 @@ async function pollCycleInner() {
16743
16743
  collectResponsivenessProbes,
16744
16744
  livePendingInboundOldestAgeSeconds,
16745
16745
  parkPendingInbound
16746
- } = await import("../responsiveness-probe-6S6ZELJG.js");
16746
+ } = await import("../responsiveness-probe-VJOXOK66.js");
16747
16747
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-M6AFZWNU.js");
16748
16748
  const wedgeNow = /* @__PURE__ */ new Date();
16749
16749
  const liveAgents = agentState.persistentSessionAgents;
@@ -16899,7 +16899,7 @@ async function pollCycleInner() {
16899
16899
  }
16900
16900
  try {
16901
16901
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-VR5QGP3J.js");
16902
- const { probeSessionAuth } = await import("../session-auth-dead-M43W63XW.js");
16902
+ const { probeSessionAuth } = await import("../session-auth-dead-T5EZNDRO.js");
16903
16903
  const observations = [];
16904
16904
  const pendingCacheCommits = [];
16905
16905
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -21442,7 +21442,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
21442
21442
  try {
21443
21443
  const freshId = rotateSessionForWedge(codeName);
21444
21444
  consecutiveWedgeCycles.delete(codeName);
21445
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-6S6ZELJG.js");
21445
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-VJOXOK66.js");
21446
21446
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
21447
21447
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
21448
21448
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -21485,7 +21485,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
21485
21485
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
21486
21486
  void (async () => {
21487
21487
  try {
21488
- const { collectDiagnostics } = await import("../persistent-session-BCEKKB2Y.js");
21488
+ const { collectDiagnostics } = await import("../persistent-session-TOIMQIAU.js");
21489
21489
  await api.post("/host/heartbeat", {
21490
21490
  host_id: hostId,
21491
21491
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -21536,7 +21536,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
21536
21536
  }
21537
21537
  try {
21538
21538
  const hostId = await getHostId();
21539
- const { collectDiagnostics } = await import("../persistent-session-BCEKKB2Y.js");
21539
+ const { collectDiagnostics } = await import("../persistent-session-TOIMQIAU.js");
21540
21540
  await api.post("/host/heartbeat", {
21541
21541
  host_id: hostId,
21542
21542
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -22208,7 +22208,7 @@ async function processClaudePairSessions(agents) {
22208
22208
  // cannot take it for itself — by the time it runs, the write it is looking
22209
22209
  // for may already have happened.
22210
22210
  snapshotPairConfig
22211
- } = await import("../claude-pair-runtime-3LB2CNFQ.js");
22211
+ } = await import("../claude-pair-runtime-KQDXWMWB.js");
22212
22212
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
22213
22213
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
22214
22214
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -36419,7 +36419,10 @@ var FLAG_REGISTRY = [
36419
36419
  // Disabling this RESTORES the shared host-wide key to every container, and
36420
36420
  // with it the cross-agent credential read ENG-8992 closes. A credential-scope
36421
36421
  // reduction should not be one click away without confirmation — same posture
36422
- // as `github-broker-credentials`, the other credential-delivery flag.
36422
+ // as the credential-delivery flags `github-broker-credentials` and
36423
+ // `xero-broker-credentials`. (This said "the OTHER credential-delivery flag"
36424
+ // while there was one; ENG-10939 added the second, so the singular had to go
36425
+ // rather than silently become a claim that github is the only sibling.)
36423
36426
  sensitive: true,
36424
36427
  // The reader is host-side (`hostFlagStore().getBoolean(...)` in the manager),
36425
36428
  // so a host on an older agt-cli has no consumer code: it keeps forwarding the
@@ -37566,6 +37569,12 @@ var FLAG_REGISTRY = [
37566
37569
  // are the precedent: pinned ABOVE every published release ON PURPOSE,
37567
37570
  // labelled as a placeholder, and gated on a post-publish bisect.
37568
37571
  //
37572
+ // ENG-10939: `xero-broker-credentials` is the worked example of the other
37573
+ // half of that convention - the bisect that DISCHARGES a placeholder. Its
37574
+ // `since` is a real published version read off `npm pack` tarballs, with the
37575
+ // markers it was bisected on written down, so the two entries together show
37576
+ // both states this field is allowed to be in.
37577
+ //
37569
37578
  // (This cited `claudecode-agt-bin-credential-helpers` until obi-wan
37570
37579
  // `8f9d3c43076c` pointed out no such key exists anywhere in the repo - the
37571
37580
  // only occurrence in the tree was this citation. The precedent was real and
@@ -37969,6 +37978,22 @@ var FLAG_REGISTRY = [
37969
37978
  public: true,
37970
37979
  defaultValue: false
37971
37980
  },
37981
+ {
37982
+ key: "ninja-lists",
37983
+ description: 'Ninja Lists (ADR-0120): small tables agents and people fill and review together. Gates the "Lists" nav item, the /lists pages AND the /ninja-sheets API, per org. Unlike its siblings `projects-menu` / `ninjafy-notes-menu`, this is the whole feature\'s gate, not only its menu entry: the surface is unlaunched, so an org with the flag off gets 404 from the API rather than a hidden link to a working one.',
37984
+ flagType: "boolean",
37985
+ // PUBLIC (ADR-0022 §2): the nav shells read it via usePublicBooleanFlag and
37986
+ // the pages via getPublicBooleanFlagServer, so it is serialized to the
37987
+ // client via GET /flags. The API evaluates it through getFlagClient() and
37988
+ // fails CLOSED on a read error. No env override (UI rollout, not an
37989
+ // operator gate). Ships dark (default false).
37990
+ public: true,
37991
+ defaultValue: false,
37992
+ // Until ADR-0120's access control exists, any member of an enabled org
37993
+ // reads every list in it, so an org admin must not be able to switch this
37994
+ // on for their own org. Only a platform admin can (PR #6522 review).
37995
+ platformAdminOnly: true
37996
+ },
37972
37997
  {
37973
37998
  key: "platform-maintenance-mode",
37974
37999
  description: 'Platform-wide maintenance mode (ENG-6506): when on, every agent across every team/org auto-replies to admitted human inbound with a "we are offline for maintenance" notice instead of dispatching to the agent \u2014 for whole-platform downtime such as DB upgrades. Read host-side per inbound from the flags cache (no MCP restart). Boolean gate; ships dark \u2014 when off, channels behave exactly as today. (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
@@ -38332,6 +38357,56 @@ var FLAG_REGISTRY = [
38332
38357
  // flips); shipping a guessed number is not.
38333
38358
  since: "0.29.0"
38334
38359
  },
38360
+ {
38361
+ key: "xero-broker-credentials",
38362
+ description: 'Deliver the Xero OAuth access token to the vendor Xero MCP server through the broker credential endpoint at USE time, instead of materializing it onto the agent host at spawn (ENG-10939, step 1 of the Xero lockdown ENG-10927). Mechanically the sibling of `github-broker-credentials`; the REASON and the cost of a bad flip are both different. ON: POST /host/agent-integrations strips `access_token`, `refresh_token` and `token_expires_at` from the xero row and marks it credential_delivery=broker, so XERO_ACCESS_TOKEN stops being written to .env.integrations. Nothing replaces it host-side and nothing needs to: the vendor MCP server has fetched a fresh token per call from POST /host/agent-integrations/:id/credential since ENG-4920, and `brokerMode` is true for every install carrying an id - so XERO_CLIENT_BEARER_TOKEN: ${XERO_ACCESS_TOKEN} is already not emitted and no dangling reference is left behind. `granted_scopes` and `read_only` are deliberately KEPT: they are consent metadata the host reports scope drift from, not credentials. OFF (default): byte-for-byte today\'s behaviour. WHY THIS IS NOT THE GITHUB FLAG. For github, an under-`since` host on a flipped flag LOSES `git`/`gh` - an outage. For xero the equivalent host LEAKS: before ENG-10939 the ENG-8344 broker guard was a loop-level `continue` that also skipped the config-derived env vars, so a broker-marked xero row dropped XERO_TENANT_ID, and per ENG-4898 the vendor MCP server then falls back to `tenants[0]` - a CROSS-TENANT DATA LEAK with no error anywhere. The dangerous fleet is therefore an INTERVAL, not "old hosts", and BOTH edges are bisected from published tarballs rather than named by issue: below agt-cli 0.28.508 a host carries no `credentialDelivery` handling at all, so it ignores the marker and is SAFE; from 0.28.508 up to (not including) 0.28.1015 it honours the marker with the WIDE skip and LEAKS; at 0.28.1015 and above the skip is narrowed and the behaviour is correct. `since` is a lower bound, so it classifies BOTH ends of that interval as `stale` and cannot tell a safe pre-0.28.508 host from a leaking-middle one. THE FLIP GATE IS THEREFORE NOT "the modal shows reach": it is zero xero-bearing hosts below 0.28.1015 per hosts.agt_version, counting NULL and stale as UNCONVERGED (the heartbeat writes that column conditionally on presence, so a host that stopped reporting keeps its last value - the async-export-rollout pattern). AND A SECOND GATE THE MODAL CANNOT SEE AT ALL: the rewritten Xero skills (ENG-10961) must be live in the catalog AND have reached hosts BEFORE this is flipped. Reversed, a host has no token while its own skills still tell it to read one - a silent read outage with no error at the write site. The catalog reaches prod through `pnpm db:catalog:seed` on deploy, not through the code deploy itself, so "merged" is not "live". EXPECTED ON FLIP DAY, so it is not a surprise: the payload shrinks, so computeIntegrationsHash moves once and XERO_ACCESS_TOKEN reads present->absent in the env diff. That does NOT respawn the session. `liveProxyTokenVars` collects AGT_REMOTE_MCP_TOKEN_VAR from .mcp.json (= XERO_ACCESS_TOKEN in broker mode) and the manager filters it out of `respawnVars` BY NAME, kind-blind - so a removal is exempt on exactly the same line a rotation is (ENG-8264). The ENG-5318 child-kill path is likewise unreachable, because broker mode never emits the ${XERO_ACCESS_TOKEN} reference the stale-mcp-reaper keys on. Both restart paths that touch this var are already quiet. Evaluated API-side only, per agent, in POST /host/agent-integrations. The host does not read this flag - it keys off the resulting credential_delivery marker - so there is deliberately NO host-side envVar that could put the token back on its own. Roll back from the flag.',
38363
+ flagType: "boolean",
38364
+ // Declared safe value is `false` = today's env delivery. Fail-safe here is
38365
+ // not symmetrical with github's: OFF leaves a credential on the host (bad,
38366
+ // but the status quo this epic is closing), while a wrongly-ON flip on an
38367
+ // under-`since` host LEAKS ANOTHER TENANT'S DATA. `false` is still the right
38368
+ // default, because the ON path is the one with a reach precondition — but
38369
+ // the reason is "do not arm this until the fleet is measured", not "a flag
38370
+ // blip must not break the agent".
38371
+ defaultValue: false,
38372
+ // Flipping ON changes how every Xero agent on the scope authenticates, and
38373
+ // on an unconverged host it is a cross-tenant leak rather than an outage. A
38374
+ // deliberate, confirmed action (ADR-0022 §4).
38375
+ sensitive: true,
38376
+ // A REAL bisected threshold, not the placeholder `github-broker-credentials`
38377
+ // and `per-agent-host-credentials` had to ship with: the host-side code this
38378
+ // flag depends on (ENG-10939 item 0, narrowing the ENG-8344 broker guard so
38379
+ // it skips the CREDENTIAL and not the whole row's config) landed in PR #6443
38380
+ // and has already published.
38381
+ //
38382
+ // Read off the published tarballs rather than inferred from the merge
38383
+ // timestamp. #6443 merged at 2026-09-24T01:54:41Z; 0.28.1015 published at
38384
+ // 01:59:23Z. `npm pack` of 0.28.1013/1014/1015/1016 + grep of the extracted
38385
+ // bundle agrees in both directions on four independent markers:
38386
+ // 0.28.1014 — `writeTokenFile` present (4 files), `isRespawnExemptEnvVar`
38387
+ // and `RESPAWN_EXEMPT_CONFIG_TAILS` absent, and the only
38388
+ // broker-guard spelling is `credentialDelivery === "broker"`
38389
+ // (the wide skip).
38390
+ // 0.28.1015 — `writeTokenFile` gone, both new exports present, and
38391
+ // `credentialDelivery !== "broker"` (the narrowed block form)
38392
+ // appears for the first time.
38393
+ // The `!==` form is the one that matters: it is the guard that keeps
38394
+ // XERO_TENANT_ID on a broker-marked row, i.e. the thing whose absence is the
38395
+ // ENG-4898 leak.
38396
+ //
38397
+ // The interval's OTHER edge was bisected the same way, so the description
38398
+ // above can name a version instead of an issue: `credentialDelivery` appears
38399
+ // in no file of 0.28.507 and in five files of 0.28.508, so ENG-8344's marker
38400
+ // handling first shipped in 0.28.508. A host below that is genuinely safe
38401
+ // rather than merely old — it has no code to honour the marker with.
38402
+ //
38403
+ // Same caveat as every other `since` in this registry: it is ADVISORY. It is
38404
+ // read only by `computeFlagReach` behind GET /flags/admin/reach, so it
38405
+ // changes what an operator is TOLD and does not stop a flip on an
38406
+ // under-`since` host. What holds the line is `defaultValue: false`, plus
38407
+ // `sensitive: true`, plus the two gates spelled out in the description.
38408
+ since: "0.28.1015"
38409
+ },
38335
38410
  {
38336
38411
  key: "help-kb-auto-grant",
38337
38412
  description: "Auto-grant the `augmented-help-kb` integration at ORG scope when an organization is provisioned (ENG-8542), so every agent in that org gets product-doc search without a per-agent operator action. One organization_integrations row reaches every agent in the org: POST /host/agent-integrations merges org -> team -> agent and buildMcpJson's predicate reads that merged set with no scope filter. ON: the two org-provisioning call sites write the row (status=active, created_by=the org owner). OFF (default): they write nothing - byte-for-byte today's behaviour. STAGED ROLLOUT IS THE POINT. ENG-8332 argues against widening distribution of the help KB until two things close, because distributing a corpus with wrong articles \"converts one agent's wrong answer into everyone's\": (1) a live 200 from GET /host/kb/search as the anon role has still never been observed - ENG-7260 was closed on remediation evidence, not on a live call, and the deploy seed log is SERVICE-ROLE evidence that says nothing about the anon grant; (2) the `site-structure` article is unverified against the current IA. Turning this on for ONE org and watching that org's help-kb connectivity-probe verdicts (ENG-8332 added a real mcp_stdio probe that calls search_knowledge_base) is the cheap positive control for (1) - an ENG-7260-class 500 then reports as `down` per agent instead of as silence. Widen only after it comes back healthy. REVOKING IS DELETING THE ROW, not flipping this off: the flag gates GRANTING, so an already-granted org keeps its row when the flag goes off. Evaluated API-side only, per org, inside grantHelpKbToOrg; no host-side envVar and deliberately no sst.config.ts entry (a literal default there would pin it - ENG-8303).",
package/dist/mcp/index.js CHANGED
@@ -28716,7 +28716,10 @@ var FLAG_REGISTRY = [
28716
28716
  // Disabling this RESTORES the shared host-wide key to every container, and
28717
28717
  // with it the cross-agent credential read ENG-8992 closes. A credential-scope
28718
28718
  // reduction should not be one click away without confirmation — same posture
28719
- // as `github-broker-credentials`, the other credential-delivery flag.
28719
+ // as the credential-delivery flags `github-broker-credentials` and
28720
+ // `xero-broker-credentials`. (This said "the OTHER credential-delivery flag"
28721
+ // while there was one; ENG-10939 added the second, so the singular had to go
28722
+ // rather than silently become a claim that github is the only sibling.)
28720
28723
  sensitive: true,
28721
28724
  // The reader is host-side (`hostFlagStore().getBoolean(...)` in the manager),
28722
28725
  // so a host on an older agt-cli has no consumer code: it keeps forwarding the
@@ -29863,6 +29866,12 @@ var FLAG_REGISTRY = [
29863
29866
  // are the precedent: pinned ABOVE every published release ON PURPOSE,
29864
29867
  // labelled as a placeholder, and gated on a post-publish bisect.
29865
29868
  //
29869
+ // ENG-10939: `xero-broker-credentials` is the worked example of the other
29870
+ // half of that convention - the bisect that DISCHARGES a placeholder. Its
29871
+ // `since` is a real published version read off `npm pack` tarballs, with the
29872
+ // markers it was bisected on written down, so the two entries together show
29873
+ // both states this field is allowed to be in.
29874
+ //
29866
29875
  // (This cited `claudecode-agt-bin-credential-helpers` until obi-wan
29867
29876
  // `8f9d3c43076c` pointed out no such key exists anywhere in the repo - the
29868
29877
  // only occurrence in the tree was this citation. The precedent was real and
@@ -30266,6 +30275,22 @@ var FLAG_REGISTRY = [
30266
30275
  public: true,
30267
30276
  defaultValue: false
30268
30277
  },
30278
+ {
30279
+ key: "ninja-lists",
30280
+ description: 'Ninja Lists (ADR-0120): small tables agents and people fill and review together. Gates the "Lists" nav item, the /lists pages AND the /ninja-sheets API, per org. Unlike its siblings `projects-menu` / `ninjafy-notes-menu`, this is the whole feature\'s gate, not only its menu entry: the surface is unlaunched, so an org with the flag off gets 404 from the API rather than a hidden link to a working one.',
30281
+ flagType: "boolean",
30282
+ // PUBLIC (ADR-0022 §2): the nav shells read it via usePublicBooleanFlag and
30283
+ // the pages via getPublicBooleanFlagServer, so it is serialized to the
30284
+ // client via GET /flags. The API evaluates it through getFlagClient() and
30285
+ // fails CLOSED on a read error. No env override (UI rollout, not an
30286
+ // operator gate). Ships dark (default false).
30287
+ public: true,
30288
+ defaultValue: false,
30289
+ // Until ADR-0120's access control exists, any member of an enabled org
30290
+ // reads every list in it, so an org admin must not be able to switch this
30291
+ // on for their own org. Only a platform admin can (PR #6522 review).
30292
+ platformAdminOnly: true
30293
+ },
30269
30294
  {
30270
30295
  key: "platform-maintenance-mode",
30271
30296
  description: 'Platform-wide maintenance mode (ENG-6506): when on, every agent across every team/org auto-replies to admitted human inbound with a "we are offline for maintenance" notice instead of dispatching to the agent \u2014 for whole-platform downtime such as DB upgrades. Read host-side per inbound from the flags cache (no MCP restart). Boolean gate; ships dark \u2014 when off, channels behave exactly as today. (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
@@ -30629,6 +30654,56 @@ var FLAG_REGISTRY = [
30629
30654
  // flips); shipping a guessed number is not.
30630
30655
  since: "0.29.0"
30631
30656
  },
30657
+ {
30658
+ key: "xero-broker-credentials",
30659
+ description: 'Deliver the Xero OAuth access token to the vendor Xero MCP server through the broker credential endpoint at USE time, instead of materializing it onto the agent host at spawn (ENG-10939, step 1 of the Xero lockdown ENG-10927). Mechanically the sibling of `github-broker-credentials`; the REASON and the cost of a bad flip are both different. ON: POST /host/agent-integrations strips `access_token`, `refresh_token` and `token_expires_at` from the xero row and marks it credential_delivery=broker, so XERO_ACCESS_TOKEN stops being written to .env.integrations. Nothing replaces it host-side and nothing needs to: the vendor MCP server has fetched a fresh token per call from POST /host/agent-integrations/:id/credential since ENG-4920, and `brokerMode` is true for every install carrying an id - so XERO_CLIENT_BEARER_TOKEN: ${XERO_ACCESS_TOKEN} is already not emitted and no dangling reference is left behind. `granted_scopes` and `read_only` are deliberately KEPT: they are consent metadata the host reports scope drift from, not credentials. OFF (default): byte-for-byte today\'s behaviour. WHY THIS IS NOT THE GITHUB FLAG. For github, an under-`since` host on a flipped flag LOSES `git`/`gh` - an outage. For xero the equivalent host LEAKS: before ENG-10939 the ENG-8344 broker guard was a loop-level `continue` that also skipped the config-derived env vars, so a broker-marked xero row dropped XERO_TENANT_ID, and per ENG-4898 the vendor MCP server then falls back to `tenants[0]` - a CROSS-TENANT DATA LEAK with no error anywhere. The dangerous fleet is therefore an INTERVAL, not "old hosts", and BOTH edges are bisected from published tarballs rather than named by issue: below agt-cli 0.28.508 a host carries no `credentialDelivery` handling at all, so it ignores the marker and is SAFE; from 0.28.508 up to (not including) 0.28.1015 it honours the marker with the WIDE skip and LEAKS; at 0.28.1015 and above the skip is narrowed and the behaviour is correct. `since` is a lower bound, so it classifies BOTH ends of that interval as `stale` and cannot tell a safe pre-0.28.508 host from a leaking-middle one. THE FLIP GATE IS THEREFORE NOT "the modal shows reach": it is zero xero-bearing hosts below 0.28.1015 per hosts.agt_version, counting NULL and stale as UNCONVERGED (the heartbeat writes that column conditionally on presence, so a host that stopped reporting keeps its last value - the async-export-rollout pattern). AND A SECOND GATE THE MODAL CANNOT SEE AT ALL: the rewritten Xero skills (ENG-10961) must be live in the catalog AND have reached hosts BEFORE this is flipped. Reversed, a host has no token while its own skills still tell it to read one - a silent read outage with no error at the write site. The catalog reaches prod through `pnpm db:catalog:seed` on deploy, not through the code deploy itself, so "merged" is not "live". EXPECTED ON FLIP DAY, so it is not a surprise: the payload shrinks, so computeIntegrationsHash moves once and XERO_ACCESS_TOKEN reads present->absent in the env diff. That does NOT respawn the session. `liveProxyTokenVars` collects AGT_REMOTE_MCP_TOKEN_VAR from .mcp.json (= XERO_ACCESS_TOKEN in broker mode) and the manager filters it out of `respawnVars` BY NAME, kind-blind - so a removal is exempt on exactly the same line a rotation is (ENG-8264). The ENG-5318 child-kill path is likewise unreachable, because broker mode never emits the ${XERO_ACCESS_TOKEN} reference the stale-mcp-reaper keys on. Both restart paths that touch this var are already quiet. Evaluated API-side only, per agent, in POST /host/agent-integrations. The host does not read this flag - it keys off the resulting credential_delivery marker - so there is deliberately NO host-side envVar that could put the token back on its own. Roll back from the flag.',
30660
+ flagType: "boolean",
30661
+ // Declared safe value is `false` = today's env delivery. Fail-safe here is
30662
+ // not symmetrical with github's: OFF leaves a credential on the host (bad,
30663
+ // but the status quo this epic is closing), while a wrongly-ON flip on an
30664
+ // under-`since` host LEAKS ANOTHER TENANT'S DATA. `false` is still the right
30665
+ // default, because the ON path is the one with a reach precondition — but
30666
+ // the reason is "do not arm this until the fleet is measured", not "a flag
30667
+ // blip must not break the agent".
30668
+ defaultValue: false,
30669
+ // Flipping ON changes how every Xero agent on the scope authenticates, and
30670
+ // on an unconverged host it is a cross-tenant leak rather than an outage. A
30671
+ // deliberate, confirmed action (ADR-0022 §4).
30672
+ sensitive: true,
30673
+ // A REAL bisected threshold, not the placeholder `github-broker-credentials`
30674
+ // and `per-agent-host-credentials` had to ship with: the host-side code this
30675
+ // flag depends on (ENG-10939 item 0, narrowing the ENG-8344 broker guard so
30676
+ // it skips the CREDENTIAL and not the whole row's config) landed in PR #6443
30677
+ // and has already published.
30678
+ //
30679
+ // Read off the published tarballs rather than inferred from the merge
30680
+ // timestamp. #6443 merged at 2026-09-24T01:54:41Z; 0.28.1015 published at
30681
+ // 01:59:23Z. `npm pack` of 0.28.1013/1014/1015/1016 + grep of the extracted
30682
+ // bundle agrees in both directions on four independent markers:
30683
+ // 0.28.1014 — `writeTokenFile` present (4 files), `isRespawnExemptEnvVar`
30684
+ // and `RESPAWN_EXEMPT_CONFIG_TAILS` absent, and the only
30685
+ // broker-guard spelling is `credentialDelivery === "broker"`
30686
+ // (the wide skip).
30687
+ // 0.28.1015 — `writeTokenFile` gone, both new exports present, and
30688
+ // `credentialDelivery !== "broker"` (the narrowed block form)
30689
+ // appears for the first time.
30690
+ // The `!==` form is the one that matters: it is the guard that keeps
30691
+ // XERO_TENANT_ID on a broker-marked row, i.e. the thing whose absence is the
30692
+ // ENG-4898 leak.
30693
+ //
30694
+ // The interval's OTHER edge was bisected the same way, so the description
30695
+ // above can name a version instead of an issue: `credentialDelivery` appears
30696
+ // in no file of 0.28.507 and in five files of 0.28.508, so ENG-8344's marker
30697
+ // handling first shipped in 0.28.508. A host below that is genuinely safe
30698
+ // rather than merely old — it has no code to honour the marker with.
30699
+ //
30700
+ // Same caveat as every other `since` in this registry: it is ADVISORY. It is
30701
+ // read only by `computeFlagReach` behind GET /flags/admin/reach, so it
30702
+ // changes what an operator is TOLD and does not stop a flip on an
30703
+ // under-`since` host. What holds the line is `defaultValue: false`, plus
30704
+ // `sensitive: true`, plus the two gates spelled out in the description.
30705
+ since: "0.28.1015"
30706
+ },
30632
30707
  {
30633
30708
  key: "help-kb-auto-grant",
30634
30709
  description: "Auto-grant the `augmented-help-kb` integration at ORG scope when an organization is provisioned (ENG-8542), so every agent in that org gets product-doc search without a per-agent operator action. One organization_integrations row reaches every agent in the org: POST /host/agent-integrations merges org -> team -> agent and buildMcpJson's predicate reads that merged set with no scope filter. ON: the two org-provisioning call sites write the row (status=active, created_by=the org owner). OFF (default): they write nothing - byte-for-byte today's behaviour. STAGED ROLLOUT IS THE POINT. ENG-8332 argues against widening distribution of the help KB until two things close, because distributing a corpus with wrong articles \"converts one agent's wrong answer into everyone's\": (1) a live 200 from GET /host/kb/search as the anon role has still never been observed - ENG-7260 was closed on remediation evidence, not on a live call, and the deploy seed log is SERVICE-ROLE evidence that says nothing about the anon grant; (2) the `site-structure` article is unverified against the current IA. Turning this on for ONE org and watching that org's help-kb connectivity-probe verdicts (ENG-8332 added a real mcp_stdio probe that calls search_knowledge_base) is the cheap positive control for (1) - an ENG-7260-class 500 then reports as `down` per agent instead of as silence. Widen only after it comes back healthy. REVOKING IS DELETING THE ROW, not flipping this off: the flag gates GRANTING, so an already-granted org keeps its row when the flag goes off. Evaluated API-side only, per org, inside grantHelpKbToOrg; no host-side envVar and deliberately no sst.config.ts entry (a literal default there would pin it - ENG-8303).",
@@ -42664,7 +42664,10 @@ var FLAG_REGISTRY = [
42664
42664
  // Disabling this RESTORES the shared host-wide key to every container, and
42665
42665
  // with it the cross-agent credential read ENG-8992 closes. A credential-scope
42666
42666
  // reduction should not be one click away without confirmation — same posture
42667
- // as `github-broker-credentials`, the other credential-delivery flag.
42667
+ // as the credential-delivery flags `github-broker-credentials` and
42668
+ // `xero-broker-credentials`. (This said "the OTHER credential-delivery flag"
42669
+ // while there was one; ENG-10939 added the second, so the singular had to go
42670
+ // rather than silently become a claim that github is the only sibling.)
42668
42671
  sensitive: true,
42669
42672
  // The reader is host-side (`hostFlagStore().getBoolean(...)` in the manager),
42670
42673
  // so a host on an older agt-cli has no consumer code: it keeps forwarding the
@@ -43811,6 +43814,12 @@ var FLAG_REGISTRY = [
43811
43814
  // are the precedent: pinned ABOVE every published release ON PURPOSE,
43812
43815
  // labelled as a placeholder, and gated on a post-publish bisect.
43813
43816
  //
43817
+ // ENG-10939: `xero-broker-credentials` is the worked example of the other
43818
+ // half of that convention - the bisect that DISCHARGES a placeholder. Its
43819
+ // `since` is a real published version read off `npm pack` tarballs, with the
43820
+ // markers it was bisected on written down, so the two entries together show
43821
+ // both states this field is allowed to be in.
43822
+ //
43814
43823
  // (This cited `claudecode-agt-bin-credential-helpers` until obi-wan
43815
43824
  // `8f9d3c43076c` pointed out no such key exists anywhere in the repo - the
43816
43825
  // only occurrence in the tree was this citation. The precedent was real and
@@ -44214,6 +44223,22 @@ var FLAG_REGISTRY = [
44214
44223
  public: true,
44215
44224
  defaultValue: false
44216
44225
  },
44226
+ {
44227
+ key: "ninja-lists",
44228
+ description: 'Ninja Lists (ADR-0120): small tables agents and people fill and review together. Gates the "Lists" nav item, the /lists pages AND the /ninja-sheets API, per org. Unlike its siblings `projects-menu` / `ninjafy-notes-menu`, this is the whole feature\'s gate, not only its menu entry: the surface is unlaunched, so an org with the flag off gets 404 from the API rather than a hidden link to a working one.',
44229
+ flagType: "boolean",
44230
+ // PUBLIC (ADR-0022 §2): the nav shells read it via usePublicBooleanFlag and
44231
+ // the pages via getPublicBooleanFlagServer, so it is serialized to the
44232
+ // client via GET /flags. The API evaluates it through getFlagClient() and
44233
+ // fails CLOSED on a read error. No env override (UI rollout, not an
44234
+ // operator gate). Ships dark (default false).
44235
+ public: true,
44236
+ defaultValue: false,
44237
+ // Until ADR-0120's access control exists, any member of an enabled org
44238
+ // reads every list in it, so an org admin must not be able to switch this
44239
+ // on for their own org. Only a platform admin can (PR #6522 review).
44240
+ platformAdminOnly: true
44241
+ },
44217
44242
  {
44218
44243
  key: "platform-maintenance-mode",
44219
44244
  description: 'Platform-wide maintenance mode (ENG-6506): when on, every agent across every team/org auto-replies to admitted human inbound with a "we are offline for maintenance" notice instead of dispatching to the agent \u2014 for whole-platform downtime such as DB upgrades. Read host-side per inbound from the flags cache (no MCP restart). Boolean gate; ships dark \u2014 when off, channels behave exactly as today. (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
@@ -44577,6 +44602,56 @@ var FLAG_REGISTRY = [
44577
44602
  // flips); shipping a guessed number is not.
44578
44603
  since: "0.29.0"
44579
44604
  },
44605
+ {
44606
+ key: "xero-broker-credentials",
44607
+ description: 'Deliver the Xero OAuth access token to the vendor Xero MCP server through the broker credential endpoint at USE time, instead of materializing it onto the agent host at spawn (ENG-10939, step 1 of the Xero lockdown ENG-10927). Mechanically the sibling of `github-broker-credentials`; the REASON and the cost of a bad flip are both different. ON: POST /host/agent-integrations strips `access_token`, `refresh_token` and `token_expires_at` from the xero row and marks it credential_delivery=broker, so XERO_ACCESS_TOKEN stops being written to .env.integrations. Nothing replaces it host-side and nothing needs to: the vendor MCP server has fetched a fresh token per call from POST /host/agent-integrations/:id/credential since ENG-4920, and `brokerMode` is true for every install carrying an id - so XERO_CLIENT_BEARER_TOKEN: ${XERO_ACCESS_TOKEN} is already not emitted and no dangling reference is left behind. `granted_scopes` and `read_only` are deliberately KEPT: they are consent metadata the host reports scope drift from, not credentials. OFF (default): byte-for-byte today\'s behaviour. WHY THIS IS NOT THE GITHUB FLAG. For github, an under-`since` host on a flipped flag LOSES `git`/`gh` - an outage. For xero the equivalent host LEAKS: before ENG-10939 the ENG-8344 broker guard was a loop-level `continue` that also skipped the config-derived env vars, so a broker-marked xero row dropped XERO_TENANT_ID, and per ENG-4898 the vendor MCP server then falls back to `tenants[0]` - a CROSS-TENANT DATA LEAK with no error anywhere. The dangerous fleet is therefore an INTERVAL, not "old hosts", and BOTH edges are bisected from published tarballs rather than named by issue: below agt-cli 0.28.508 a host carries no `credentialDelivery` handling at all, so it ignores the marker and is SAFE; from 0.28.508 up to (not including) 0.28.1015 it honours the marker with the WIDE skip and LEAKS; at 0.28.1015 and above the skip is narrowed and the behaviour is correct. `since` is a lower bound, so it classifies BOTH ends of that interval as `stale` and cannot tell a safe pre-0.28.508 host from a leaking-middle one. THE FLIP GATE IS THEREFORE NOT "the modal shows reach": it is zero xero-bearing hosts below 0.28.1015 per hosts.agt_version, counting NULL and stale as UNCONVERGED (the heartbeat writes that column conditionally on presence, so a host that stopped reporting keeps its last value - the async-export-rollout pattern). AND A SECOND GATE THE MODAL CANNOT SEE AT ALL: the rewritten Xero skills (ENG-10961) must be live in the catalog AND have reached hosts BEFORE this is flipped. Reversed, a host has no token while its own skills still tell it to read one - a silent read outage with no error at the write site. The catalog reaches prod through `pnpm db:catalog:seed` on deploy, not through the code deploy itself, so "merged" is not "live". EXPECTED ON FLIP DAY, so it is not a surprise: the payload shrinks, so computeIntegrationsHash moves once and XERO_ACCESS_TOKEN reads present->absent in the env diff. That does NOT respawn the session. `liveProxyTokenVars` collects AGT_REMOTE_MCP_TOKEN_VAR from .mcp.json (= XERO_ACCESS_TOKEN in broker mode) and the manager filters it out of `respawnVars` BY NAME, kind-blind - so a removal is exempt on exactly the same line a rotation is (ENG-8264). The ENG-5318 child-kill path is likewise unreachable, because broker mode never emits the ${XERO_ACCESS_TOKEN} reference the stale-mcp-reaper keys on. Both restart paths that touch this var are already quiet. Evaluated API-side only, per agent, in POST /host/agent-integrations. The host does not read this flag - it keys off the resulting credential_delivery marker - so there is deliberately NO host-side envVar that could put the token back on its own. Roll back from the flag.',
44608
+ flagType: "boolean",
44609
+ // Declared safe value is `false` = today's env delivery. Fail-safe here is
44610
+ // not symmetrical with github's: OFF leaves a credential on the host (bad,
44611
+ // but the status quo this epic is closing), while a wrongly-ON flip on an
44612
+ // under-`since` host LEAKS ANOTHER TENANT'S DATA. `false` is still the right
44613
+ // default, because the ON path is the one with a reach precondition — but
44614
+ // the reason is "do not arm this until the fleet is measured", not "a flag
44615
+ // blip must not break the agent".
44616
+ defaultValue: false,
44617
+ // Flipping ON changes how every Xero agent on the scope authenticates, and
44618
+ // on an unconverged host it is a cross-tenant leak rather than an outage. A
44619
+ // deliberate, confirmed action (ADR-0022 §4).
44620
+ sensitive: true,
44621
+ // A REAL bisected threshold, not the placeholder `github-broker-credentials`
44622
+ // and `per-agent-host-credentials` had to ship with: the host-side code this
44623
+ // flag depends on (ENG-10939 item 0, narrowing the ENG-8344 broker guard so
44624
+ // it skips the CREDENTIAL and not the whole row's config) landed in PR #6443
44625
+ // and has already published.
44626
+ //
44627
+ // Read off the published tarballs rather than inferred from the merge
44628
+ // timestamp. #6443 merged at 2026-09-24T01:54:41Z; 0.28.1015 published at
44629
+ // 01:59:23Z. `npm pack` of 0.28.1013/1014/1015/1016 + grep of the extracted
44630
+ // bundle agrees in both directions on four independent markers:
44631
+ // 0.28.1014 — `writeTokenFile` present (4 files), `isRespawnExemptEnvVar`
44632
+ // and `RESPAWN_EXEMPT_CONFIG_TAILS` absent, and the only
44633
+ // broker-guard spelling is `credentialDelivery === "broker"`
44634
+ // (the wide skip).
44635
+ // 0.28.1015 — `writeTokenFile` gone, both new exports present, and
44636
+ // `credentialDelivery !== "broker"` (the narrowed block form)
44637
+ // appears for the first time.
44638
+ // The `!==` form is the one that matters: it is the guard that keeps
44639
+ // XERO_TENANT_ID on a broker-marked row, i.e. the thing whose absence is the
44640
+ // ENG-4898 leak.
44641
+ //
44642
+ // The interval's OTHER edge was bisected the same way, so the description
44643
+ // above can name a version instead of an issue: `credentialDelivery` appears
44644
+ // in no file of 0.28.507 and in five files of 0.28.508, so ENG-8344's marker
44645
+ // handling first shipped in 0.28.508. A host below that is genuinely safe
44646
+ // rather than merely old — it has no code to honour the marker with.
44647
+ //
44648
+ // Same caveat as every other `since` in this registry: it is ADVISORY. It is
44649
+ // read only by `computeFlagReach` behind GET /flags/admin/reach, so it
44650
+ // changes what an operator is TOLD and does not stop a flip on an
44651
+ // under-`since` host. What holds the line is `defaultValue: false`, plus
44652
+ // `sensitive: true`, plus the two gates spelled out in the description.
44653
+ since: "0.28.1015"
44654
+ },
44580
44655
  {
44581
44656
  key: "help-kb-auto-grant",
44582
44657
  description: "Auto-grant the `augmented-help-kb` integration at ORG scope when an organization is provisioned (ENG-8542), so every agent in that org gets product-doc search without a per-agent operator action. One organization_integrations row reaches every agent in the org: POST /host/agent-integrations merges org -> team -> agent and buildMcpJson's predicate reads that merged set with no scope filter. ON: the two org-provisioning call sites write the row (status=active, created_by=the org owner). OFF (default): they write nothing - byte-for-byte today's behaviour. STAGED ROLLOUT IS THE POINT. ENG-8332 argues against widening distribution of the help KB until two things close, because distributing a corpus with wrong articles \"converts one agent's wrong answer into everyone's\": (1) a live 200 from GET /host/kb/search as the anon role has still never been observed - ENG-7260 was closed on remediation evidence, not on a live call, and the deploy seed log is SERVICE-ROLE evidence that says nothing about the anon grant; (2) the `site-structure` article is unverified against the current IA. Turning this on for ONE org and watching that org's help-kb connectivity-probe verdicts (ENG-8332 added a real mcp_stdio probe that calls search_knowledge_base) is the cheap positive control for (1) - an ENG-7260-class 500 then reports as `down` per agent instead of as silence. Widen only after it comes back healthy. REVOKING IS DELETING THE ROW, not flipping this off: the flag gates GRANTING, so an already-granted org keeps its row when the flag goes off. Evaluated API-side only, per org, inside grantHelpKbToOrg; no host-side envVar and deliberately no sst.config.ts entry (a literal default there would pin it - ENG-8303).",
@@ -37303,7 +37303,10 @@ var FLAG_REGISTRY = [
37303
37303
  // Disabling this RESTORES the shared host-wide key to every container, and
37304
37304
  // with it the cross-agent credential read ENG-8992 closes. A credential-scope
37305
37305
  // reduction should not be one click away without confirmation — same posture
37306
- // as `github-broker-credentials`, the other credential-delivery flag.
37306
+ // as the credential-delivery flags `github-broker-credentials` and
37307
+ // `xero-broker-credentials`. (This said "the OTHER credential-delivery flag"
37308
+ // while there was one; ENG-10939 added the second, so the singular had to go
37309
+ // rather than silently become a claim that github is the only sibling.)
37307
37310
  sensitive: true,
37308
37311
  // The reader is host-side (`hostFlagStore().getBoolean(...)` in the manager),
37309
37312
  // so a host on an older agt-cli has no consumer code: it keeps forwarding the
@@ -38450,6 +38453,12 @@ var FLAG_REGISTRY = [
38450
38453
  // are the precedent: pinned ABOVE every published release ON PURPOSE,
38451
38454
  // labelled as a placeholder, and gated on a post-publish bisect.
38452
38455
  //
38456
+ // ENG-10939: `xero-broker-credentials` is the worked example of the other
38457
+ // half of that convention - the bisect that DISCHARGES a placeholder. Its
38458
+ // `since` is a real published version read off `npm pack` tarballs, with the
38459
+ // markers it was bisected on written down, so the two entries together show
38460
+ // both states this field is allowed to be in.
38461
+ //
38453
38462
  // (This cited `claudecode-agt-bin-credential-helpers` until obi-wan
38454
38463
  // `8f9d3c43076c` pointed out no such key exists anywhere in the repo - the
38455
38464
  // only occurrence in the tree was this citation. The precedent was real and
@@ -38853,6 +38862,22 @@ var FLAG_REGISTRY = [
38853
38862
  public: true,
38854
38863
  defaultValue: false
38855
38864
  },
38865
+ {
38866
+ key: "ninja-lists",
38867
+ description: 'Ninja Lists (ADR-0120): small tables agents and people fill and review together. Gates the "Lists" nav item, the /lists pages AND the /ninja-sheets API, per org. Unlike its siblings `projects-menu` / `ninjafy-notes-menu`, this is the whole feature\'s gate, not only its menu entry: the surface is unlaunched, so an org with the flag off gets 404 from the API rather than a hidden link to a working one.',
38868
+ flagType: "boolean",
38869
+ // PUBLIC (ADR-0022 §2): the nav shells read it via usePublicBooleanFlag and
38870
+ // the pages via getPublicBooleanFlagServer, so it is serialized to the
38871
+ // client via GET /flags. The API evaluates it through getFlagClient() and
38872
+ // fails CLOSED on a read error. No env override (UI rollout, not an
38873
+ // operator gate). Ships dark (default false).
38874
+ public: true,
38875
+ defaultValue: false,
38876
+ // Until ADR-0120's access control exists, any member of an enabled org
38877
+ // reads every list in it, so an org admin must not be able to switch this
38878
+ // on for their own org. Only a platform admin can (PR #6522 review).
38879
+ platformAdminOnly: true
38880
+ },
38856
38881
  {
38857
38882
  key: "platform-maintenance-mode",
38858
38883
  description: 'Platform-wide maintenance mode (ENG-6506): when on, every agent across every team/org auto-replies to admitted human inbound with a "we are offline for maintenance" notice instead of dispatching to the agent \u2014 for whole-platform downtime such as DB upgrades. Read host-side per inbound from the flags cache (no MCP restart). Boolean gate; ships dark \u2014 when off, channels behave exactly as today. (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
@@ -39216,6 +39241,56 @@ var FLAG_REGISTRY = [
39216
39241
  // flips); shipping a guessed number is not.
39217
39242
  since: "0.29.0"
39218
39243
  },
39244
+ {
39245
+ key: "xero-broker-credentials",
39246
+ description: 'Deliver the Xero OAuth access token to the vendor Xero MCP server through the broker credential endpoint at USE time, instead of materializing it onto the agent host at spawn (ENG-10939, step 1 of the Xero lockdown ENG-10927). Mechanically the sibling of `github-broker-credentials`; the REASON and the cost of a bad flip are both different. ON: POST /host/agent-integrations strips `access_token`, `refresh_token` and `token_expires_at` from the xero row and marks it credential_delivery=broker, so XERO_ACCESS_TOKEN stops being written to .env.integrations. Nothing replaces it host-side and nothing needs to: the vendor MCP server has fetched a fresh token per call from POST /host/agent-integrations/:id/credential since ENG-4920, and `brokerMode` is true for every install carrying an id - so XERO_CLIENT_BEARER_TOKEN: ${XERO_ACCESS_TOKEN} is already not emitted and no dangling reference is left behind. `granted_scopes` and `read_only` are deliberately KEPT: they are consent metadata the host reports scope drift from, not credentials. OFF (default): byte-for-byte today\'s behaviour. WHY THIS IS NOT THE GITHUB FLAG. For github, an under-`since` host on a flipped flag LOSES `git`/`gh` - an outage. For xero the equivalent host LEAKS: before ENG-10939 the ENG-8344 broker guard was a loop-level `continue` that also skipped the config-derived env vars, so a broker-marked xero row dropped XERO_TENANT_ID, and per ENG-4898 the vendor MCP server then falls back to `tenants[0]` - a CROSS-TENANT DATA LEAK with no error anywhere. The dangerous fleet is therefore an INTERVAL, not "old hosts", and BOTH edges are bisected from published tarballs rather than named by issue: below agt-cli 0.28.508 a host carries no `credentialDelivery` handling at all, so it ignores the marker and is SAFE; from 0.28.508 up to (not including) 0.28.1015 it honours the marker with the WIDE skip and LEAKS; at 0.28.1015 and above the skip is narrowed and the behaviour is correct. `since` is a lower bound, so it classifies BOTH ends of that interval as `stale` and cannot tell a safe pre-0.28.508 host from a leaking-middle one. THE FLIP GATE IS THEREFORE NOT "the modal shows reach": it is zero xero-bearing hosts below 0.28.1015 per hosts.agt_version, counting NULL and stale as UNCONVERGED (the heartbeat writes that column conditionally on presence, so a host that stopped reporting keeps its last value - the async-export-rollout pattern). AND A SECOND GATE THE MODAL CANNOT SEE AT ALL: the rewritten Xero skills (ENG-10961) must be live in the catalog AND have reached hosts BEFORE this is flipped. Reversed, a host has no token while its own skills still tell it to read one - a silent read outage with no error at the write site. The catalog reaches prod through `pnpm db:catalog:seed` on deploy, not through the code deploy itself, so "merged" is not "live". EXPECTED ON FLIP DAY, so it is not a surprise: the payload shrinks, so computeIntegrationsHash moves once and XERO_ACCESS_TOKEN reads present->absent in the env diff. That does NOT respawn the session. `liveProxyTokenVars` collects AGT_REMOTE_MCP_TOKEN_VAR from .mcp.json (= XERO_ACCESS_TOKEN in broker mode) and the manager filters it out of `respawnVars` BY NAME, kind-blind - so a removal is exempt on exactly the same line a rotation is (ENG-8264). The ENG-5318 child-kill path is likewise unreachable, because broker mode never emits the ${XERO_ACCESS_TOKEN} reference the stale-mcp-reaper keys on. Both restart paths that touch this var are already quiet. Evaluated API-side only, per agent, in POST /host/agent-integrations. The host does not read this flag - it keys off the resulting credential_delivery marker - so there is deliberately NO host-side envVar that could put the token back on its own. Roll back from the flag.',
39247
+ flagType: "boolean",
39248
+ // Declared safe value is `false` = today's env delivery. Fail-safe here is
39249
+ // not symmetrical with github's: OFF leaves a credential on the host (bad,
39250
+ // but the status quo this epic is closing), while a wrongly-ON flip on an
39251
+ // under-`since` host LEAKS ANOTHER TENANT'S DATA. `false` is still the right
39252
+ // default, because the ON path is the one with a reach precondition — but
39253
+ // the reason is "do not arm this until the fleet is measured", not "a flag
39254
+ // blip must not break the agent".
39255
+ defaultValue: false,
39256
+ // Flipping ON changes how every Xero agent on the scope authenticates, and
39257
+ // on an unconverged host it is a cross-tenant leak rather than an outage. A
39258
+ // deliberate, confirmed action (ADR-0022 §4).
39259
+ sensitive: true,
39260
+ // A REAL bisected threshold, not the placeholder `github-broker-credentials`
39261
+ // and `per-agent-host-credentials` had to ship with: the host-side code this
39262
+ // flag depends on (ENG-10939 item 0, narrowing the ENG-8344 broker guard so
39263
+ // it skips the CREDENTIAL and not the whole row's config) landed in PR #6443
39264
+ // and has already published.
39265
+ //
39266
+ // Read off the published tarballs rather than inferred from the merge
39267
+ // timestamp. #6443 merged at 2026-09-24T01:54:41Z; 0.28.1015 published at
39268
+ // 01:59:23Z. `npm pack` of 0.28.1013/1014/1015/1016 + grep of the extracted
39269
+ // bundle agrees in both directions on four independent markers:
39270
+ // 0.28.1014 — `writeTokenFile` present (4 files), `isRespawnExemptEnvVar`
39271
+ // and `RESPAWN_EXEMPT_CONFIG_TAILS` absent, and the only
39272
+ // broker-guard spelling is `credentialDelivery === "broker"`
39273
+ // (the wide skip).
39274
+ // 0.28.1015 — `writeTokenFile` gone, both new exports present, and
39275
+ // `credentialDelivery !== "broker"` (the narrowed block form)
39276
+ // appears for the first time.
39277
+ // The `!==` form is the one that matters: it is the guard that keeps
39278
+ // XERO_TENANT_ID on a broker-marked row, i.e. the thing whose absence is the
39279
+ // ENG-4898 leak.
39280
+ //
39281
+ // The interval's OTHER edge was bisected the same way, so the description
39282
+ // above can name a version instead of an issue: `credentialDelivery` appears
39283
+ // in no file of 0.28.507 and in five files of 0.28.508, so ENG-8344's marker
39284
+ // handling first shipped in 0.28.508. A host below that is genuinely safe
39285
+ // rather than merely old — it has no code to honour the marker with.
39286
+ //
39287
+ // Same caveat as every other `since` in this registry: it is ADVISORY. It is
39288
+ // read only by `computeFlagReach` behind GET /flags/admin/reach, so it
39289
+ // changes what an operator is TOLD and does not stop a flip on an
39290
+ // under-`since` host. What holds the line is `defaultValue: false`, plus
39291
+ // `sensitive: true`, plus the two gates spelled out in the description.
39292
+ since: "0.28.1015"
39293
+ },
39219
39294
  {
39220
39295
  key: "help-kb-auto-grant",
39221
39296
  description: "Auto-grant the `augmented-help-kb` integration at ORG scope when an organization is provisioned (ENG-8542), so every agent in that org gets product-doc search without a per-agent operator action. One organization_integrations row reaches every agent in the org: POST /host/agent-integrations merges org -> team -> agent and buildMcpJson's predicate reads that merged set with no scope filter. ON: the two org-provisioning call sites write the row (status=active, created_by=the org owner). OFF (default): they write nothing - byte-for-byte today's behaviour. STAGED ROLLOUT IS THE POINT. ENG-8332 argues against widening distribution of the help KB until two things close, because distributing a corpus with wrong articles \"converts one agent's wrong answer into everyone's\": (1) a live 200 from GET /host/kb/search as the anon role has still never been observed - ENG-7260 was closed on remediation evidence, not on a live call, and the deploy seed log is SERVICE-ROLE evidence that says nothing about the anon grant; (2) the `site-structure` article is unverified against the current IA. Turning this on for ONE org and watching that org's help-kb connectivity-probe verdicts (ENG-8332 added a real mcp_stdio probe that calls search_knowledge_base) is the cheap positive control for (1) - an ENG-7260-class 500 then reports as `down` per agent instead of as silence. Widen only after it comes back healthy. REVOKING IS DELETING THE ROW, not flipping this off: the flag gates GRANTING, so an already-granted org keeps its row when the flag goes off. Evaluated API-side only, per org, inside grantHelpKbToOrg; no host-side envVar and deliberately no sst.config.ts entry (a literal default there would pin it - ENG-8303).",