@integrity-labs/agt-cli 0.28.892 → 0.28.894

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-FZ6KIIYX.js → chunk-5464FJVN.js} +32 -8
  3. package/dist/{chunk-FZ6KIIYX.js.map → chunk-5464FJVN.js.map} +1 -1
  4. package/dist/{chunk-WMJBXN6N.js → chunk-RAEBKUW2.js} +2 -2
  5. package/dist/{chunk-SUVQ2KSP.js → chunk-WRFHISNG.js} +4 -4
  6. package/dist/{claude-pair-runtime-RTD7ZCVS.js → claude-pair-runtime-3MK5MUPN.js} +2 -2
  7. package/dist/lib/manager-worker.js +14 -14
  8. package/dist/mcp/augmented-admin.js +22 -6
  9. package/dist/mcp/direct-chat-channel.js +31 -7
  10. package/dist/mcp/index.js +116 -52
  11. package/dist/mcp/origami.js +31 -7
  12. package/dist/mcp/slack-channel.js +31 -7
  13. package/dist/mcp/telegram-channel.js +31 -7
  14. package/dist/{persistent-session-ARKZDYMX.js → persistent-session-5HSYQBF7.js} +3 -3
  15. package/dist/{responsiveness-probe-X2WXPIUI.js → responsiveness-probe-ZSB3DLLI.js} +3 -3
  16. package/dist/{session-auth-dead-BLEV4AUJ.js → session-auth-dead-J7SQAYRR.js} +2 -2
  17. package/package.json +1 -1
  18. /package/dist/{chunk-WMJBXN6N.js.map → chunk-RAEBKUW2.js.map} +0 -0
  19. /package/dist/{chunk-SUVQ2KSP.js.map → chunk-WRFHISNG.js.map} +0 -0
  20. /package/dist/{claude-pair-runtime-RTD7ZCVS.js.map → claude-pair-runtime-3MK5MUPN.js.map} +0 -0
  21. /package/dist/{persistent-session-ARKZDYMX.js.map → persistent-session-5HSYQBF7.js.map} +0 -0
  22. /package/dist/{responsiveness-probe-X2WXPIUI.js.map → responsiveness-probe-ZSB3DLLI.js.map} +0 -0
  23. /package/dist/{session-auth-dead-BLEV4AUJ.js.map → session-auth-dead-J7SQAYRR.js.map} +0 -0
@@ -19,7 +19,7 @@ import {
19
19
  rotateDailySession,
20
20
  sessionFileExists,
21
21
  todayLocalIso
22
- } from "./chunk-FZ6KIIYX.js";
22
+ } from "./chunk-5464FJVN.js";
23
23
  import {
24
24
  reapOrphanChannelMcps
25
25
  } from "./chunk-XWVM4KPK.js";
@@ -5730,4 +5730,4 @@ export {
5730
5730
  stopAllSessionsAndWait,
5731
5731
  getProjectDir
5732
5732
  };
5733
- //# sourceMappingURL=chunk-WMJBXN6N.js.map
5733
+ //# sourceMappingURL=chunk-RAEBKUW2.js.map
@@ -16,7 +16,7 @@ import {
16
16
  parseEnvIntegrations,
17
17
  shellQuote,
18
18
  summarizeUnanswerablePane
19
- } from "./chunk-WMJBXN6N.js";
19
+ } from "./chunk-RAEBKUW2.js";
20
20
  import {
21
21
  BIND_FAILURE_QUARANTINE_THRESHOLD,
22
22
  INTEGRATIONS_SECTION_END,
@@ -76,7 +76,7 @@ import {
76
76
  sessionTranscriptDir,
77
77
  worseConnectivityOutcome,
78
78
  wrapScheduledTaskPrompt
79
- } from "./chunk-FZ6KIIYX.js";
79
+ } from "./chunk-5464FJVN.js";
80
80
  import {
81
81
  parsePsRows
82
82
  } from "./chunk-XWVM4KPK.js";
@@ -6841,7 +6841,7 @@ function exchangeFailureKind(err) {
6841
6841
  }
6842
6842
 
6843
6843
  // src/lib/api-client.ts
6844
- var agtCliVersion = true ? "0.28.892" : "dev";
6844
+ var agtCliVersion = true ? "0.28.894" : "dev";
6845
6845
  var lastConfigHash = null;
6846
6846
  function setConfigHash(hash) {
6847
6847
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -11451,4 +11451,4 @@ export {
11451
11451
  managerInstallSystemUnitCommand,
11452
11452
  managerUninstallSystemUnitCommand
11453
11453
  };
11454
- //# sourceMappingURL=chunk-SUVQ2KSP.js.map
11454
+ //# sourceMappingURL=chunk-WRFHISNG.js.map
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-ARKZDYMX.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-5HSYQBF7.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-RTD7ZCVS.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-3MK5MUPN.js.map
@@ -63,7 +63,7 @@ import {
63
63
  safeWriteJsonAtomic,
64
64
  setConfigHash,
65
65
  tripClass
66
- } from "../chunk-SUVQ2KSP.js";
66
+ } from "../chunk-WRFHISNG.js";
67
67
  import {
68
68
  getProjectDir as getProjectDir2,
69
69
  getReadyTasks,
@@ -136,7 +136,7 @@ import {
136
136
  takeZombieDetection,
137
137
  toOpencodeModel,
138
138
  writeEgressAllowlist
139
- } from "../chunk-WMJBXN6N.js";
139
+ } from "../chunk-RAEBKUW2.js";
140
140
  import {
141
141
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
142
142
  AnchorSessionClient,
@@ -231,7 +231,7 @@ import {
231
231
  subagentActivityAgeSeconds,
232
232
  sumTranscriptUsageInWindow,
233
233
  transcriptActivityAgeSeconds
234
- } from "../chunk-FZ6KIIYX.js";
234
+ } from "../chunk-5464FJVN.js";
235
235
  import {
236
236
  reapOrphanChannelMcps
237
237
  } from "../chunk-XWVM4KPK.js";
@@ -13003,7 +13003,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
13003
13003
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
13004
13004
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
13005
13005
  async function channelInboundActivityAgeSecondsFor(codeName) {
13006
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-X2WXPIUI.js");
13006
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-ZSB3DLLI.js");
13007
13007
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
13008
13008
  if (newest === null) return null;
13009
13009
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -13671,7 +13671,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13671
13671
  var lastVersionCheckAt = 0;
13672
13672
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13673
13673
  var lastResponsivenessProbeAt = 0;
13674
- var agtCliVersion = true ? "0.28.892" : "dev";
13674
+ var agtCliVersion = true ? "0.28.894" : "dev";
13675
13675
  function resolveBrewPath(execFileSync2) {
13676
13676
  try {
13677
13677
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15261,7 +15261,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
15261
15261
  if (codeNames.length === 0) return;
15262
15262
  void (async () => {
15263
15263
  try {
15264
- const { collectDiagnostics } = await import("../persistent-session-ARKZDYMX.js");
15264
+ const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
15265
15265
  await api.post("/host/heartbeat", {
15266
15266
  host_id: hostId,
15267
15267
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -15403,7 +15403,7 @@ async function pollCycleInner() {
15403
15403
  }
15404
15404
  try {
15405
15405
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
15406
- const { collectDiagnostics } = await import("../persistent-session-ARKZDYMX.js");
15406
+ const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
15407
15407
  const diagCodeNames = [...agentState.persistentSessionAgents];
15408
15408
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
15409
15409
  let tailscaleHostname;
@@ -15572,7 +15572,7 @@ async function pollCycleInner() {
15572
15572
  collectPanelessActivityProbes,
15573
15573
  getResponsivenessIntervalMs,
15574
15574
  occupancyQualificationClassifications
15575
- } = await import("../responsiveness-probe-X2WXPIUI.js");
15575
+ } = await import("../responsiveness-probe-ZSB3DLLI.js");
15576
15576
  const probeIntervalMs = getResponsivenessIntervalMs();
15577
15577
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
15578
15578
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -15689,7 +15689,7 @@ async function pollCycleInner() {
15689
15689
  collectResponsivenessProbes,
15690
15690
  livePendingInboundOldestAgeSeconds,
15691
15691
  parkPendingInbound
15692
- } = await import("../responsiveness-probe-X2WXPIUI.js");
15692
+ } = await import("../responsiveness-probe-ZSB3DLLI.js");
15693
15693
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
15694
15694
  const wedgeNow = /* @__PURE__ */ new Date();
15695
15695
  const liveAgents = agentState.persistentSessionAgents;
@@ -15845,7 +15845,7 @@ async function pollCycleInner() {
15845
15845
  }
15846
15846
  try {
15847
15847
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
15848
- const { probeSessionAuth } = await import("../session-auth-dead-BLEV4AUJ.js");
15848
+ const { probeSessionAuth } = await import("../session-auth-dead-J7SQAYRR.js");
15849
15849
  const observations = [];
15850
15850
  const pendingCacheCommits = [];
15851
15851
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -20103,7 +20103,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20103
20103
  try {
20104
20104
  const freshId = rotateSessionForWedge(codeName);
20105
20105
  consecutiveWedgeCycles.delete(codeName);
20106
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-X2WXPIUI.js");
20106
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-ZSB3DLLI.js");
20107
20107
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
20108
20108
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
20109
20109
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -20146,7 +20146,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20146
20146
  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}`));
20147
20147
  void (async () => {
20148
20148
  try {
20149
- const { collectDiagnostics } = await import("../persistent-session-ARKZDYMX.js");
20149
+ const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
20150
20150
  await api.post("/host/heartbeat", {
20151
20151
  host_id: hostId,
20152
20152
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20197,7 +20197,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
20197
20197
  }
20198
20198
  try {
20199
20199
  const hostId = await getHostId();
20200
- const { collectDiagnostics } = await import("../persistent-session-ARKZDYMX.js");
20200
+ const { collectDiagnostics } = await import("../persistent-session-5HSYQBF7.js");
20201
20201
  await api.post("/host/heartbeat", {
20202
20202
  host_id: hostId,
20203
20203
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20839,7 +20839,7 @@ async function processClaudePairSessions(agents) {
20839
20839
  killPairSession,
20840
20840
  pairTmuxSession,
20841
20841
  finalizeClaudePairOnboarding
20842
- } = await import("../claude-pair-runtime-RTD7ZCVS.js");
20842
+ } = await import("../claude-pair-runtime-3MK5MUPN.js");
20843
20843
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
20844
20844
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
20845
20845
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -21478,14 +21478,30 @@ var resumeAgentSchema = strictObject({
21478
21478
  });
21479
21479
  var listAlertsSchema = strictObject({
21480
21480
  severity: external_exports.string().max(16).optional().describe("Filter by severity (critical | warning | info)."),
21481
- // ENG-8566: without this, an old alert of a specific kind is unreachable —
21482
- // the listing is newest-first and capped at 100, and the open board exceeds
21483
- // that. Twelve slack_manifest_drift warnings sat unfindable for 48 days.
21481
+ // ENG-8566: without this, an alert of a specific kind could be unreachable —
21482
+ // the listing is capped at 100 and the open board exceeds that. Twelve
21483
+ // slack_manifest_drift warnings sat unfindable for 48 days.
21484
+ //
21485
+ // ENG-10379 corrected the ordering claim this comment used to carry ("the
21486
+ // listing is newest-first"). It is severity-first, then OLDEST-first within a
21487
+ // severity, so the rows a truncated page drops are the NEWEST of the lowest
21488
+ // severity present. `cursor` below is now the general escape hatch; `kind`
21489
+ // remains the narrow one for when you already know what you are hunting.
21484
21490
  kind: external_exports.string().max(64).optional().describe(
21485
- "Filter by alert kind, exact match (e.g. slack_manifest_drift, host_down, integration_down). Use this to reach older alerts of one kind: the list is newest-first and capped at 100, so without a kind filter an old alert below that cutoff cannot be retrieved at all."
21491
+ "Filter by alert kind, exact match (e.g. slack_manifest_drift, host_down, integration_down). ENG-10379: ordering is severity first (critical, then warning, then info) and OLDEST-first within each severity, so the rows a truncated page drops are the NEWEST of the lowest severity present - not the oldest. Page with `cursor` to reach them; `kind` narrows the set when you already know what you are looking for."
21486
21492
  ),
21487
21493
  open: external_exports.boolean().optional().describe("When true, only currently-open (unclosed) alerts."),
21488
- limit: limitSchema
21494
+ limit: limitSchema,
21495
+ // ENG-10379: the API has accepted a keyset `cursor` since ENG-9295 and returns
21496
+ // `next_cursor`, but this schema never exposed one - and these schemas are
21497
+ // STRICT, so a caller passing `cursor` was REJECTED rather than ignored. The
21498
+ // paging existed and could not be driven from here, which is what left rows
21499
+ // past the 100-row cap unreachable: on a board over the cap those are the
21500
+ // NEWEST alerts of the lowest severity present, and one of them was a 25-hour
21501
+ // channel outage nobody could see.
21502
+ cursor: external_exports.string().min(1).max(512).optional().describe(
21503
+ "Keyset cursor from a previous response's `next_cursor`. Pass it to fetch the NEXT page, and repeat until `next_cursor` is null to walk the whole board. Opaque: it encodes (severity_rank, opened_at, id) as one string, and a malformed cursor is refused rather than silently serving page one again."
21504
+ )
21489
21505
  });
21490
21506
  var requestAccessSchema = strictObject({
21491
21507
  target_org_id: external_exports.string().min(1).max(64).describe("UUID of the customer organization to open scoped read access to."),
@@ -21693,7 +21709,7 @@ server.registerTool(
21693
21709
  );
21694
21710
  server.registerTool(
21695
21711
  "debug_list_alerts",
21696
- { description: "List recent platform alerts across authorized orgs (host-down, agent-stale, probe-timeout, auth-failed), including NULL-team host alerts. Returns a projection per alert (kind, severity, message, source, open/closed state), plus { count, total, truncated } \u2014 `total` is the matches before the 100-row cap, so `truncated: true` means you are NOT seeing everything. Filter with { kind, severity, open, limit }. **ENG-10030 \u2014 two fields that change how you read `message`.** `dedupe_key` is the alert's identity (`<kind>:<subject id>`), so it tells you which ROW an alert is about, not just which agent. `updated_at` tells you whether `message` is CURRENT: `open_alert` overwrites `message` on every re-raise but never touches `opened_at`, so a message is a snapshot unless the monitor re-raised that `(kind, dedupe_key)` since the alert opened \u2014 and it reads like live output either way. ONE DIRECTION IS PROOF, THE OTHER IS A HINT: `updated_at === opened_at` is CONCLUSIVE (never written since insert, so never re-raised, so `message` is frozen at `opened_at` \u2014 check this before proposing a fix for what a message describes, because the code may have moved since); `updated_at > opened_at` is SUGGESTIVE ONLY, because the `alerts_set_updated_at` trigger fires on EVERY write \u2014 an acknowledge, a snooze, a close, or a backfill migration. Compare against `acknowledged_at` / `snoozed_until` / `closed_at`: if `updated_at` matches one of them, that write was the human action, not a re-raise. Matching NONE of them is evidence of a re-raise but not proof, because a migration that touches the row leaves no timestamp of its own. Reading it as a biconditional is expensive: on one measured board 63 of 98 open alerts were acknowledged, so ~64% would falsely report current text. Two reads an hour apart remain the only unambiguous positive test. **Use `kind` to find older alerts**: results are newest-first and hard-capped at 100, so on a busy board an alert below that cutoff is unreachable without it (ENG-8566: 12 slack_manifest_drift warnings sat unfindable for 48 days for exactly this reason).", inputSchema: listAlertsSchema },
21712
+ { description: "List recent platform alerts across authorized orgs (host-down, agent-stale, probe-timeout, auth-failed), including NULL-team host alerts. Returns a projection per alert (kind, severity, message, source, open/closed state), plus { count, total, truncated } \u2014 `total` is the matches before the 100-row cap, so `truncated: true` means you are NOT seeing everything. Filter with { kind, severity, open, limit }. **ENG-10030 \u2014 two fields that change how you read `message`.** `dedupe_key` is the alert's identity (`<kind>:<subject id>`), so it tells you which ROW an alert is about, not just which agent. `updated_at` tells you whether `message` is CURRENT: `open_alert` overwrites `message` on every re-raise but never touches `opened_at`, so a message is a snapshot unless the monitor re-raised that `(kind, dedupe_key)` since the alert opened \u2014 and it reads like live output either way. ONE DIRECTION IS PROOF, THE OTHER IS A HINT: `updated_at === opened_at` is CONCLUSIVE (never written since insert, so never re-raised, so `message` is frozen at `opened_at` \u2014 check this before proposing a fix for what a message describes, because the code may have moved since); `updated_at > opened_at` is SUGGESTIVE ONLY, because the `alerts_set_updated_at` trigger fires on EVERY write \u2014 an acknowledge, a snooze, a close, or a backfill migration. Compare against `acknowledged_at` / `snoozed_until` / `closed_at`: if `updated_at` matches one of them, that write was the human action, not a re-raise. Matching NONE of them is evidence of a re-raise but not proof, because a migration that touches the row leaves no timestamp of its own. Reading it as a biconditional is expensive: on one measured board 63 of 98 open alerts were acknowledged, so ~64% would falsely report current text. Two reads an hour apart remain the only unambiguous positive test. **ORDERING AND PAGING - ENG-10379, and the previous text here was wrong.** Results are ordered by SEVERITY first (critical, then warning, then info) and OLDEST-FIRST within each severity. They are not ordered newest first. So on a board past the 100-row cap, the rows dropped are the NEWEST alerts of the lowest severity present - which is how a sweep that reads one page missed a live 25-hour channel outage. `truncated: true` still means exactly what it always did (more matches exist than this page carries); to REACH them, pass `cursor` set to this response's `next_cursor` and repeat until `next_cursor` comes back null, which walks the whole board in order. `kind` still narrows the set when you already know what you are looking for (ENG-8566: 12 slack_manifest_drift warnings sat unfindable for 48 days).", inputSchema: listAlertsSchema },
21697
21713
  async (args) => {
21698
21714
  try {
21699
21715
  const result = await client.listAlerts(args);
@@ -37526,14 +37526,38 @@ var FLAG_REGISTRY = [
37526
37526
  },
37527
37527
  {
37528
37528
  key: "app-top-nav-shell",
37529
- description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). Ships dark.",
37529
+ description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). NO LONGER DARK: ENG-9831 cutover step 1 flipped the default to true, so an org with no override now gets the top-nav shell. The key survives only until cutover step 2 deletes it.",
37530
37530
  flagType: "boolean",
37531
- // Declared safe value is `false` = the current left sidebar. Fail-closed to the
37532
- // shell that ships today if the flag DB is unreachable.
37533
- defaultValue: false,
37534
- // Read CLIENT-SIDE by the dashboard layout + sidebar ("use client") via
37535
- // usePublicBooleanFlag, so the key must be in the browser-exposed public map.
37536
- // Additive layout gate not sensitive.
37531
+ // ENG-9831 (cutover step 1): flipped `false` -> `true`. The comment this
37532
+ // replaces called `false` the fail-closed direction — "the shell that ships
37533
+ // today". That was true while the sidebar was the shell that shipped; the
37534
+ // top nav is now the one being cut over to, so `false` points at the layout
37535
+ // being retired. The old rationale is recorded rather than deleted because
37536
+ // it was correct under a premise that has expired.
37537
+ //
37538
+ // NOTE the same split as ninjafy-brand below: this value is consulted by the
37539
+ // API evaluator when building the `GET /flags` map for an org with no
37540
+ // override. It is NOT the fallback the webapp uses on an outage — that is
37541
+ // the argument passed at the call site. Four call sites carry `true` to
37542
+ // match: (dashboard)/layout.tsx and the three legacy redirect hubs
37543
+ // (settings, team, organization/settings). Flipping the registry alone
37544
+ // leaves the cutover half-applied.
37545
+ defaultValue: true,
37546
+ // ENG-9831: NO LONGER read client-side. It was — via usePublicBooleanFlag in
37547
+ // sidebar.tsx (twice) and top-nav.tsx — and that is precisely what this
37548
+ // slice removed: a client hook starts at its default and corrects after an
37549
+ // async GET /flags, so the console painted the sidebar and then replaced the
37550
+ // WHOLE dashboard chrome after hydration. The ENG-8858/ENG-9227 flash at
37551
+ // layout scale. It is now resolved once server-side in (dashboard)/
37552
+ // layout.tsx and passed to both shells as a required prop.
37553
+ //
37554
+ // `public` MUST STAY ANYWAY — the same trap documented on ninjafy-brand.
37555
+ // getPublicBooleanFlagServer resolves against the very same public-filtered
37556
+ // `GET /flags` the browser uses (routes/flags.ts filters to
37557
+ // listPublicFlagKeys()). Drop `public` and the key is simply absent from the
37558
+ // map, the server helper falls through to its default forever, and every org
37559
+ // silently gets whatever that default happens to be with no error anywhere.
37560
+ // `public` means "readable through the public map", not "read by a browser".
37537
37561
  public: true
37538
37562
  },
37539
37563
  {
package/dist/mcp/index.js CHANGED
@@ -29821,14 +29821,38 @@ var FLAG_REGISTRY = [
29821
29821
  },
29822
29822
  {
29823
29823
  key: "app-top-nav-shell",
29824
- description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). Ships dark.",
29824
+ description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). NO LONGER DARK: ENG-9831 cutover step 1 flipped the default to true, so an org with no override now gets the top-nav shell. The key survives only until cutover step 2 deletes it.",
29825
29825
  flagType: "boolean",
29826
- // Declared safe value is `false` = the current left sidebar. Fail-closed to the
29827
- // shell that ships today if the flag DB is unreachable.
29828
- defaultValue: false,
29829
- // Read CLIENT-SIDE by the dashboard layout + sidebar ("use client") via
29830
- // usePublicBooleanFlag, so the key must be in the browser-exposed public map.
29831
- // Additive layout gate not sensitive.
29826
+ // ENG-9831 (cutover step 1): flipped `false` -> `true`. The comment this
29827
+ // replaces called `false` the fail-closed direction — "the shell that ships
29828
+ // today". That was true while the sidebar was the shell that shipped; the
29829
+ // top nav is now the one being cut over to, so `false` points at the layout
29830
+ // being retired. The old rationale is recorded rather than deleted because
29831
+ // it was correct under a premise that has expired.
29832
+ //
29833
+ // NOTE the same split as ninjafy-brand below: this value is consulted by the
29834
+ // API evaluator when building the `GET /flags` map for an org with no
29835
+ // override. It is NOT the fallback the webapp uses on an outage — that is
29836
+ // the argument passed at the call site. Four call sites carry `true` to
29837
+ // match: (dashboard)/layout.tsx and the three legacy redirect hubs
29838
+ // (settings, team, organization/settings). Flipping the registry alone
29839
+ // leaves the cutover half-applied.
29840
+ defaultValue: true,
29841
+ // ENG-9831: NO LONGER read client-side. It was — via usePublicBooleanFlag in
29842
+ // sidebar.tsx (twice) and top-nav.tsx — and that is precisely what this
29843
+ // slice removed: a client hook starts at its default and corrects after an
29844
+ // async GET /flags, so the console painted the sidebar and then replaced the
29845
+ // WHOLE dashboard chrome after hydration. The ENG-8858/ENG-9227 flash at
29846
+ // layout scale. It is now resolved once server-side in (dashboard)/
29847
+ // layout.tsx and passed to both shells as a required prop.
29848
+ //
29849
+ // `public` MUST STAY ANYWAY — the same trap documented on ninjafy-brand.
29850
+ // getPublicBooleanFlagServer resolves against the very same public-filtered
29851
+ // `GET /flags` the browser uses (routes/flags.ts filters to
29852
+ // listPublicFlagKeys()). Drop `public` and the key is simply absent from the
29853
+ // map, the server helper falls through to its default forever, and every org
29854
+ // silently gets whatever that default happens to be with no error anywhere.
29855
+ // `public` means "readable through the public map", not "read by a browser".
29832
29856
  public: true
29833
29857
  },
29834
29858
  {
@@ -44984,6 +45008,73 @@ function truncate(text, max) {
44984
45008
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
44985
45009
  }
44986
45010
 
45011
+ // src/integration-improve-render.ts
45012
+ function fenceFor(...values) {
45013
+ let longest = 0;
45014
+ for (const value of values) {
45015
+ for (const run of value.match(/`+/g) ?? []) longest = Math.max(longest, run.length);
45016
+ }
45017
+ return "`".repeat(Math.max(3, longest + 1));
45018
+ }
45019
+ function renderImproveResponse(data) {
45020
+ const typedChanges = data.proposed.changed_fields ?? [];
45021
+ const overridesChanged = !!data.proposed.appended_to_overrides;
45022
+ const nothingChanged = typedChanges.length === 0 && !overridesChanged;
45023
+ const lines = [];
45024
+ lines.push(data.applied ? "\u2705 Applied integration context update" : "\u{1F4CB} Proposed integration context update (preview)");
45025
+ lines.push("");
45026
+ lines.push(`Rationale: ${data.proposed.rationale}`);
45027
+ if (typedChanges.length > 0) {
45028
+ lines.push("");
45029
+ lines.push("## Typed field changes");
45030
+ for (const field of typedChanges) {
45031
+ const before = JSON.stringify(data.current.values[field] ?? null);
45032
+ const after = JSON.stringify(data.proposed.values[field] ?? null);
45033
+ lines.push(`- \`${field}\`: ${before} \u2192 ${after}`);
45034
+ }
45035
+ }
45036
+ if (overridesChanged) {
45037
+ lines.push("");
45038
+ lines.push("## Overrides change");
45039
+ const note = data.proposed.overrides_append;
45040
+ if (typeof note === "string" && note.length > 0) {
45041
+ lines.push("Appending this note. The existing overrides text is kept exactly as it is:");
45042
+ const fence = fenceFor(note);
45043
+ lines.push(fence);
45044
+ lines.push(note);
45045
+ lines.push(fence);
45046
+ } else {
45047
+ const fence = fenceFor(data.current.overrides ?? "", data.proposed.overrides ?? "");
45048
+ lines.push("Before:");
45049
+ lines.push(fence);
45050
+ lines.push(data.current.overrides || "(empty)");
45051
+ lines.push(fence);
45052
+ lines.push("After:");
45053
+ lines.push(fence);
45054
+ lines.push(data.proposed.overrides || "(empty)");
45055
+ lines.push(fence);
45056
+ }
45057
+ }
45058
+ if (nothingChanged) {
45059
+ lines.push("");
45060
+ lines.push("## No change proposed");
45061
+ lines.push(
45062
+ data.applied ? "Nothing was changed: no typed field changes and nothing added to the overrides." : "Nothing to apply: no typed field changes and nothing to add to the overrides. The rationale above says why."
45063
+ );
45064
+ }
45065
+ if (!data.applied) {
45066
+ if (!nothingChanged) {
45067
+ lines.push("");
45068
+ lines.push("---");
45069
+ lines.push("Show this diff to the user. If they confirm, call `integration_improve` again with `auto_apply: true` and the same arguments to apply.");
45070
+ }
45071
+ } else if (!nothingChanged) {
45072
+ lines.push("");
45073
+ lines.push("Update applied. The manager will re-render the integration's SKILL.md files on the next refresh cycle.");
45074
+ }
45075
+ return lines.join("\n");
45076
+ }
45077
+
44987
45078
  // src/kanban-list-render.ts
44988
45079
  var KANBAN_LIST_DISPLAY_ORDER = [
44989
45080
  "in_progress",
@@ -48834,7 +48925,7 @@ server.tool(
48834
48925
  // Description leads with the user phrasings the agent should match against,
48835
48926
  // then explains the safety rule. Order matters — Claude\'s tool selection
48836
48927
  // weights the first line of a description heavily.
48837
- `Update, edit, or add a rule to an installed integration (e.g. the Coding integration, the Knowledge Base integration, the Slack integration). USE THIS TOOL when the user says any of: "update the X integration", "add a rule to integration Y", "tell the X integration not to do Z", "change the X integration so it does Y", "the Coding integration should always/never...", "remember this for the X integration". This is the ONLY correct way to modify an integration's behavior. NEVER edit SKILL.md files under .claude/skills/integration-* directly \u2014 those files are derived from the platform database and your edits will be silently overwritten on the next manager refresh. The platform calls an LLM to translate the user's request into a structured update of the integration's typed context fields and/or freeform overrides text, then returns a diff for you to show the user. Default flow is preview-then-apply: call once to get the proposed diff, show it to the user, then call again with auto_apply=true if they confirm. Only works for integrations that are actually installed on this agent.`,
48928
+ `Update, edit, or add a rule to an installed integration (e.g. the Coding integration, the Knowledge Base integration, the Slack integration). USE THIS TOOL when the user says any of: "update the X integration", "add a rule to integration Y", "tell the X integration not to do Z", "change the X integration so it does Y", "the Coding integration should always/never...", "remember this for the X integration". This is the ONLY correct way to modify an integration's behavior. NEVER edit SKILL.md files under .claude/skills/integration-* directly \u2014 those files are derived from the platform database and your edits will be silently overwritten on the next manager refresh. The platform calls an LLM to translate the user's request into a structured update of the integration's typed context fields and/or freeform overrides text, then returns a diff for you to show the user. Default flow is preview-then-apply: call once to get the proposed diff, show it to the user, then call again with auto_apply=true if they confirm. Only works for integrations that are actually installed on this agent. Stored overrides are never rewritten: a note is only ever appended after them, and existing overrides text cannot be removed or reworded through this tool. If an auto_apply=true call fails as INDETERMINATE, do NOT retry it: a retry generates the note again and can append it twice. Tell the user the outcome is unknown instead.`,
48838
48929
  {
48839
48930
  integration_slug: external_exports.string().optional().describe('Integration slug to improve (e.g. "claude-code-github"). Either this or integration_id is required.'),
48840
48931
  integration_id: external_exports.string().optional().describe("Integration UUID. Either this or integration_slug is required."),
@@ -48856,50 +48947,23 @@ server.tool(
48856
48947
  content: [{ type: "text", text: "Error: either integration_slug or integration_id is required." }]
48857
48948
  };
48858
48949
  }
48859
- const data = await apiPost("/host/improve-integration", {
48860
- agent_id: AGT_AGENT_ID,
48861
- integration_slug: integrationSlug,
48862
- integration_id: integrationId,
48863
- feedback: params.feedback,
48864
- transcript_snippet: params.transcript_snippet,
48865
- auto_apply: params.auto_apply ?? false
48866
- });
48867
- const lines = [];
48868
- lines.push(data.applied ? "\u2705 Applied integration context update" : "\u{1F4CB} Proposed integration context update (preview)");
48869
- lines.push("");
48870
- lines.push(`Rationale: ${data.proposed.rationale}`);
48871
- if (data.proposed.changed_fields.length > 0) {
48872
- lines.push("");
48873
- lines.push("## Typed field changes");
48874
- for (const field of data.proposed.changed_fields) {
48875
- const before = JSON.stringify(data.current.values[field] ?? null);
48876
- const after = JSON.stringify(data.proposed.values[field] ?? null);
48877
- lines.push(`- \`${field}\`: ${before} \u2192 ${after}`);
48878
- }
48879
- }
48880
- if (data.proposed.appended_to_overrides) {
48881
- lines.push("");
48882
- lines.push("## Overrides change");
48883
- const before = data.current.overrides || "(empty)";
48884
- const after = data.proposed.overrides || "(empty)";
48885
- lines.push("Before:");
48886
- lines.push("```");
48887
- lines.push(before);
48888
- lines.push("```");
48889
- lines.push("After:");
48890
- lines.push("```");
48891
- lines.push(after);
48892
- lines.push("```");
48893
- }
48894
- if (!data.applied) {
48895
- lines.push("");
48896
- lines.push("---");
48897
- lines.push("Show this diff to the user. If they confirm, call `integration_improve` again with `auto_apply: true` and the same arguments to apply.");
48898
- } else {
48899
- lines.push("");
48900
- lines.push("Update applied. The manager will re-render the integration's SKILL.md files on the next refresh cycle.");
48901
- }
48902
- return { content: [{ type: "text", text: lines.join("\n") }] };
48950
+ const autoApply = params.auto_apply ?? false;
48951
+ const data = await apiPost(
48952
+ "/host/improve-integration",
48953
+ {
48954
+ agent_id: AGT_AGENT_ID,
48955
+ integration_slug: integrationSlug,
48956
+ integration_id: integrationId,
48957
+ feedback: params.feedback,
48958
+ transcript_snippet: params.transcript_snippet,
48959
+ auto_apply: autoApply
48960
+ },
48961
+ false,
48962
+ FORWARD_TOOL_CALL_TIMEOUT_MS,
48963
+ "integration_improve",
48964
+ !autoApply
48965
+ );
48966
+ return { content: [{ type: "text", text: renderImproveResponse(data) }] };
48903
48967
  }
48904
48968
  );
48905
48969
  server.tool(
@@ -43749,14 +43749,38 @@ var FLAG_REGISTRY = [
43749
43749
  },
43750
43750
  {
43751
43751
  key: "app-top-nav-shell",
43752
- description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). Ships dark.",
43752
+ description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). NO LONGER DARK: ENG-9831 cutover step 1 flipped the default to true, so an org with no override now gets the top-nav shell. The key survives only until cutover step 2 deletes it.",
43753
43753
  flagType: "boolean",
43754
- // Declared safe value is `false` = the current left sidebar. Fail-closed to the
43755
- // shell that ships today if the flag DB is unreachable.
43756
- defaultValue: false,
43757
- // Read CLIENT-SIDE by the dashboard layout + sidebar ("use client") via
43758
- // usePublicBooleanFlag, so the key must be in the browser-exposed public map.
43759
- // Additive layout gate not sensitive.
43754
+ // ENG-9831 (cutover step 1): flipped `false` -> `true`. The comment this
43755
+ // replaces called `false` the fail-closed direction — "the shell that ships
43756
+ // today". That was true while the sidebar was the shell that shipped; the
43757
+ // top nav is now the one being cut over to, so `false` points at the layout
43758
+ // being retired. The old rationale is recorded rather than deleted because
43759
+ // it was correct under a premise that has expired.
43760
+ //
43761
+ // NOTE the same split as ninjafy-brand below: this value is consulted by the
43762
+ // API evaluator when building the `GET /flags` map for an org with no
43763
+ // override. It is NOT the fallback the webapp uses on an outage — that is
43764
+ // the argument passed at the call site. Four call sites carry `true` to
43765
+ // match: (dashboard)/layout.tsx and the three legacy redirect hubs
43766
+ // (settings, team, organization/settings). Flipping the registry alone
43767
+ // leaves the cutover half-applied.
43768
+ defaultValue: true,
43769
+ // ENG-9831: NO LONGER read client-side. It was — via usePublicBooleanFlag in
43770
+ // sidebar.tsx (twice) and top-nav.tsx — and that is precisely what this
43771
+ // slice removed: a client hook starts at its default and corrects after an
43772
+ // async GET /flags, so the console painted the sidebar and then replaced the
43773
+ // WHOLE dashboard chrome after hydration. The ENG-8858/ENG-9227 flash at
43774
+ // layout scale. It is now resolved once server-side in (dashboard)/
43775
+ // layout.tsx and passed to both shells as a required prop.
43776
+ //
43777
+ // `public` MUST STAY ANYWAY — the same trap documented on ninjafy-brand.
43778
+ // getPublicBooleanFlagServer resolves against the very same public-filtered
43779
+ // `GET /flags` the browser uses (routes/flags.ts filters to
43780
+ // listPublicFlagKeys()). Drop `public` and the key is simply absent from the
43781
+ // map, the server helper falls through to its default forever, and every org
43782
+ // silently gets whatever that default happens to be with no error anywhere.
43783
+ // `public` means "readable through the public map", not "read by a browser".
43760
43784
  public: true
43761
43785
  },
43762
43786
  {
@@ -38386,14 +38386,38 @@ var FLAG_REGISTRY = [
38386
38386
  },
38387
38387
  {
38388
38388
  key: "app-top-nav-shell",
38389
- description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). Ships dark.",
38389
+ description: "Global top-navigation shell redesign (ENG-8478 epic / ENG-8486). When ON, the dashboard chrome replaces the left sidebar with a top navigation bar (team switcher \xB7 destinations \xB7 command palette + account) so the left side is free for an agent-scoped rail; when OFF, the existing left sidebar is unchanged. Additive layout gate \u2014 no data-model change. Composes independently with agent-unified-screen (the two flags are reconciled in ENG-8488). NO LONGER DARK: ENG-9831 cutover step 1 flipped the default to true, so an org with no override now gets the top-nav shell. The key survives only until cutover step 2 deletes it.",
38390
38390
  flagType: "boolean",
38391
- // Declared safe value is `false` = the current left sidebar. Fail-closed to the
38392
- // shell that ships today if the flag DB is unreachable.
38393
- defaultValue: false,
38394
- // Read CLIENT-SIDE by the dashboard layout + sidebar ("use client") via
38395
- // usePublicBooleanFlag, so the key must be in the browser-exposed public map.
38396
- // Additive layout gate not sensitive.
38391
+ // ENG-9831 (cutover step 1): flipped `false` -> `true`. The comment this
38392
+ // replaces called `false` the fail-closed direction — "the shell that ships
38393
+ // today". That was true while the sidebar was the shell that shipped; the
38394
+ // top nav is now the one being cut over to, so `false` points at the layout
38395
+ // being retired. The old rationale is recorded rather than deleted because
38396
+ // it was correct under a premise that has expired.
38397
+ //
38398
+ // NOTE the same split as ninjafy-brand below: this value is consulted by the
38399
+ // API evaluator when building the `GET /flags` map for an org with no
38400
+ // override. It is NOT the fallback the webapp uses on an outage — that is
38401
+ // the argument passed at the call site. Four call sites carry `true` to
38402
+ // match: (dashboard)/layout.tsx and the three legacy redirect hubs
38403
+ // (settings, team, organization/settings). Flipping the registry alone
38404
+ // leaves the cutover half-applied.
38405
+ defaultValue: true,
38406
+ // ENG-9831: NO LONGER read client-side. It was — via usePublicBooleanFlag in
38407
+ // sidebar.tsx (twice) and top-nav.tsx — and that is precisely what this
38408
+ // slice removed: a client hook starts at its default and corrects after an
38409
+ // async GET /flags, so the console painted the sidebar and then replaced the
38410
+ // WHOLE dashboard chrome after hydration. The ENG-8858/ENG-9227 flash at
38411
+ // layout scale. It is now resolved once server-side in (dashboard)/
38412
+ // layout.tsx and passed to both shells as a required prop.
38413
+ //
38414
+ // `public` MUST STAY ANYWAY — the same trap documented on ninjafy-brand.
38415
+ // getPublicBooleanFlagServer resolves against the very same public-filtered
38416
+ // `GET /flags` the browser uses (routes/flags.ts filters to
38417
+ // listPublicFlagKeys()). Drop `public` and the key is simply absent from the
38418
+ // map, the server helper falls through to its default forever, and every org
38419
+ // silently gets whatever that default happens to be with no error anywhere.
38420
+ // `public` means "readable through the public map", not "read by a browser".
38397
38421
  public: true
38398
38422
  },
38399
38423
  {