@integrity-labs/agt-cli 0.28.1111 → 0.28.1112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -18867,7 +18867,7 @@ var require_filters = __commonJS({
18867
18867
  return r.copySafeness(str, res);
18868
18868
  }
18869
18869
  _exports.indent = indent;
18870
- function join10(arr, del, attr) {
18870
+ function join11(arr, del, attr) {
18871
18871
  del = del || "";
18872
18872
  if (attr) {
18873
18873
  arr = lib.map(arr, function(v) {
@@ -18876,7 +18876,7 @@ var require_filters = __commonJS({
18876
18876
  }
18877
18877
  return arr.join(del);
18878
18878
  }
18879
- _exports.join = join10;
18879
+ _exports.join = join11;
18880
18880
  function last(arr) {
18881
18881
  return arr[arr.length - 1];
18882
18882
  }
@@ -30673,14 +30673,37 @@ var FLAG_REGISTRY = [
30673
30673
  },
30674
30674
  {
30675
30675
  key: "platform-maintenance-mode",
30676
- 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.)',
30677
- flagType: "boolean",
30678
- // Declared safe value is `false` (normal operation). Fail-safe direction: a
30679
- // flag-DB read error or absent flag map must never silence the fleet on its
30680
- // own — agents stay responsive unless an operator deliberately flips this on
30681
- // from the admin Feature Flags page. No envVar: net-new control, no
30682
- // pre-flags env gate to migrate.
30683
- defaultValue: false,
30676
+ description: 'Platform-wide maintenance mode (ENG-6506; widened to modes by ENG-11436). off = normal operation (ships dark; channels behave exactly as today). tools-offline = every agent KEEPS conversing but every Augmented tool that WRITES to the control plane refuses with a reason, so nothing is created during a window whose database restore would discard it; reads are untouched. fleet-offline = the previous boolean-true behaviour AND the write gate: admitted human inbound gets a "we are offline for maintenance" notice instead of being dispatched, for whole-platform downtime such as DB upgrades. The modes are CUMULATIVE \u2014 fleet-offline gates writes too, because silencing channel inbound does not stop a /loop or a scheduled task filing work. Read host-side per inbound and per tool call from the flags cache (no MCP restart). (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
30677
+ flagType: "enum",
30678
+ allowedValues: ["off", "tools-offline", "fleet-offline"],
30679
+ // Declared safe value is `off` (normal operation). Fail-safe direction: a
30680
+ // flag-DB read error or absent flag map must never silence the fleet OR take
30681
+ // its write tools away on its own — agents stay fully functional unless an
30682
+ // operator deliberately sets a mode from the admin Feature Flags page. No
30683
+ // envVar: net-new control, no pre-flags env gate to migrate.
30684
+ //
30685
+ // ENG-11436 widened this from a boolean rather than adding a second flag, so
30686
+ // there is ONE operator control whose modes are mutually exclusive by type
30687
+ // instead of two similar booleans with four combinations.
30688
+ //
30689
+ // The host resolver GRANDFATHERS a boolean-shaped cache value
30690
+ // (true -> fleet-offline, false -> off), and the DIRECTION it protects is
30691
+ // narrower than an earlier version of this comment claimed (Obi Wan
30692
+ // `e3f0d2c154b9` on #6903). It said "an older manager still writing a boolean
30693
+ // must not make a central flip a silent no-op". That situation cannot arise:
30694
+ // once this registry change deploys, normalizeFlagValue only ever yields one
30695
+ // of the three strings, feature-flags-host.ts writes the heartbeat map
30696
+ // verbatim, so no manager writes a boolean again. What grandfathering
30697
+ // actually covers is a NEW MCP reading a STALE PRE-DEPLOY cache that still
30698
+ // holds one.
30699
+ //
30700
+ // The direction that DOES happen is not handled and cannot be: an OLD MCP
30701
+ // reads the new string with resolveHostBooleanFlag, gets undefined, and keeps
30702
+ // dispatching. That is a real regression against ENG-6506 until the fleet
30703
+ // converges, and it is why the runbook makes convergence a deploy-order
30704
+ // PRECONDITION rather than offering a boolean fallback — there is none, since
30705
+ // an enum flag rejects `{value: true}` with a 400.
30706
+ defaultValue: "off",
30684
30707
  // Flipping this takes the ENTIRE fleet offline to end users; a mis-flip is a
30685
30708
  // platform-wide availability incident, so mutations require explicit
30686
30709
  // confirmation (the admin flip-reach modal).
@@ -35564,8 +35587,8 @@ function renderKnowledgeBody(content) {
35564
35587
  }
35565
35588
 
35566
35589
  // src/index.ts
35567
- import { homedir as homedir4 } from "os";
35568
- import { basename, isAbsolute as isAbsolute2, join as join9, resolve as pathResolve, sep as sep2 } from "path";
35590
+ import { homedir as homedir5 } from "os";
35591
+ import { basename, isAbsolute as isAbsolute2, join as join10, resolve as pathResolve, sep as sep2 } from "path";
35569
35592
 
35570
35593
  // src/host-file-path.ts
35571
35594
  var SKILL_BODY_PATH_MAX_BYTES = 512 * 1024;
@@ -46089,6 +46112,192 @@ var StdioServerTransport = class {
46089
46112
  }
46090
46113
  };
46091
46114
 
46115
+ // src/flags-cache-read.ts
46116
+ import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
46117
+ import { homedir as homedir2 } from "os";
46118
+ import { join as join6 } from "path";
46119
+ function defaultFlagsCachePath() {
46120
+ return join6(homedir2(), ".augmented", "flags-cache.json");
46121
+ }
46122
+ function envBoolean(raw) {
46123
+ if (raw === void 0) return void 0;
46124
+ const v = raw.trim().toLowerCase();
46125
+ if (v === "") return void 0;
46126
+ if (v === "1" || v === "true" || v === "yes" || v === "on") return true;
46127
+ if (v === "0" || v === "false" || v === "no" || v === "off") return false;
46128
+ return void 0;
46129
+ }
46130
+ var MAINTENANCE_FLAG_KEY = "platform-maintenance-mode";
46131
+ function parseMaintenanceMode(raw) {
46132
+ if (raw === void 0) return void 0;
46133
+ const v = raw.trim().toLowerCase();
46134
+ if (v === "") return void 0;
46135
+ if (v === "off" || v === "tools-offline" || v === "fleet-offline") return v;
46136
+ const b = envBoolean(raw);
46137
+ if (b === true) return "fleet-offline";
46138
+ if (b === false) return "off";
46139
+ return void 0;
46140
+ }
46141
+ function cachedMaintenanceMode(path) {
46142
+ try {
46143
+ if (!existsSync2(path)) return void 0;
46144
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
46145
+ if (!parsed || typeof parsed !== "object") return void 0;
46146
+ const flags = parsed.flags;
46147
+ if (!flags || typeof flags !== "object") return void 0;
46148
+ const value = flags[MAINTENANCE_FLAG_KEY];
46149
+ if (typeof value === "string") return parseMaintenanceMode(value);
46150
+ if (typeof value === "boolean") return value ? "fleet-offline" : "off";
46151
+ return void 0;
46152
+ } catch {
46153
+ return void 0;
46154
+ }
46155
+ }
46156
+ function resolveMaintenanceMode(opts) {
46157
+ const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
46158
+ if (cached2 !== void 0) return cached2;
46159
+ return "off";
46160
+ }
46161
+
46162
+ // src/maintenance-tools-offline.ts
46163
+ function isToolsOfflineActive(opts) {
46164
+ const mode = resolveMaintenanceMode({ cachePath: opts?.cachePath, env: opts?.env });
46165
+ return mode === "tools-offline" || mode === "fleet-offline";
46166
+ }
46167
+ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
46168
+ // Tasks.
46169
+ "kanban_add",
46170
+ "kanban_update",
46171
+ "kanban_move",
46172
+ "kanban_done",
46173
+ "kanban_reopen",
46174
+ "kanban_assign",
46175
+ "kanban_reassign",
46176
+ "kanban_recall",
46177
+ "kanban_respond",
46178
+ "kanban_progress",
46179
+ "kanban_log",
46180
+ "kanban_assign_project",
46181
+ "assign_kanban_to_human",
46182
+ // Canvases. `dashboards_request_refresh` is gated because it enqueues
46183
+ // server-side work, not because it renders anything itself.
46184
+ "dashboards_upsert",
46185
+ "dashboards_persist_widget",
46186
+ "dashboards_request_refresh",
46187
+ // ───────────────────────────────────────────────────────────────────────
46188
+ // FORWARDED writes (ENG-11436 / CodeRabbit on #6903). These reach the agent
46189
+ // through `registerForwardedApiTools()` -> `server.registerTool`, from
46190
+ // `packages/api/src/routes/mcp-tools.ts`, with names resolved at RUNTIME. They
46191
+ // are gated only because index.ts now wraps that method too; before that, this
46192
+ // whole block would have been a list the gate could not see.
46193
+ //
46194
+ // CodeRabbit named five (workflow_update, workflow_run_{start,phase,end},
46195
+ // memory_delete). Enumerating the surface to check them found 37 of the 50
46196
+ // forwarded tools unclassified, so the five were a subset — these are all of
46197
+ // the writes among them. The test now classifies that surface exhaustively, so
46198
+ // the next addition fails CI rather than slipping through.
46199
+ "composio_upload_file",
46200
+ "memory_delete",
46201
+ "memory_save",
46202
+ "onboarding_advance",
46203
+ "onboarding_provision_skills",
46204
+ "onboarding_provision_tasks",
46205
+ "onboarding_record_manager_contact",
46206
+ "request_batch_approval",
46207
+ "request_integration_access",
46208
+ "skill_contribute_fragment",
46209
+ "skill_create",
46210
+ "skill_delete",
46211
+ "skill_improve",
46212
+ "skill_propose_revision",
46213
+ "skill_publish_to_marketplace",
46214
+ "skill_update",
46215
+ "submit_performance_review",
46216
+ "workflow_run_end",
46217
+ "workflow_run_phase",
46218
+ "workflow_run_start",
46219
+ "workflow_update",
46220
+ // CHANNEL-SERVER write (Obi Wan `b3537c1e8495` on #6903). Dispatched by NAME in
46221
+ // slack-channel.ts / telegram-channel.ts / direct-chat-channel.ts, which are
46222
+ // separate processes that never call server.tool — so this one is enforced by
46223
+ // `refuseIfToolsOffline` at each dispatch point, not by the registration
46224
+ // wrapper. It inserts a `pending_interaction` row, the same durable
46225
+ // human-actionable record that gates request_buttons / request_form.
46226
+ "channel_request_input",
46227
+ // `slack.ask_user` is the SAME write under a dotted name, and it was missed
46228
+ // because the test's enumerator could not see dotted names at all (Obi Wan
46229
+ // `122ac55ff278` / `d7245eddd77f` on #6903). handleAskUser calls
46230
+ // runtime.createPendingInteraction -> POST /host/pending-interactions ->
46231
+ // inserts `pending_interaction`. Ungated, an agent on a /loop files the row,
46232
+ // the restore discards it, and the agent blocks forever on a resolution that
46233
+ // no longer exists — the exact failure the entry above is gated for.
46234
+ //
46235
+ // Verified it is the ONLY dotted channel tool that writes: createPendingInteraction
46236
+ // is CALLED once in the three channel servers (slack-channel.ts:8053);
46237
+ // slack-block-kit-runtime.ts only re-exports it.
46238
+ "slack.ask_user",
46239
+ // Workflows. FORWARDED, not local — it reaches the agent through
46240
+ // `server.registerTool` in registerForwardedApiTools, so it is gated only
46241
+ // because that method is now wrapped too (CodeRabbit on #6903).
46242
+ "workflow_create",
46243
+ // Knowledge.
46244
+ "knowledge_add",
46245
+ "knowledge_update",
46246
+ "knowledge_delete",
46247
+ // Projects and project tagging.
46248
+ "projects_create",
46249
+ "projects_update",
46250
+ "artefacts_assign_project",
46251
+ // Routines.
46252
+ "routine_propose",
46253
+ "routine_revise",
46254
+ "routine_retire",
46255
+ "submit_routine_output",
46256
+ // Standing rules.
46257
+ "rule_add",
46258
+ "rule_remove",
46259
+ // Scheduled work.
46260
+ "schedule_create",
46261
+ "schedule_update",
46262
+ "schedule_delete",
46263
+ // Requests that file a durable record a human is expected to act on. Filing
46264
+ // one mid-window is worse than refusing it: the record is discarded by the
46265
+ // restore, so the agent would have promised a human a follow-up that no
46266
+ // longer exists anywhere.
46267
+ "request_approval",
46268
+ "request_buttons",
46269
+ "request_channel_access",
46270
+ "request_form",
46271
+ "request_reconnect",
46272
+ "request_feature",
46273
+ "request_feature_withdraw",
46274
+ // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
46275
+ // server object, so the wrapper in index.ts covers them too.
46276
+ "cancel_approval",
46277
+ // Status / identity.
46278
+ "status_standup",
46279
+ "status_update",
46280
+ "update_identity",
46281
+ "update_timezone",
46282
+ // Fleet changes.
46283
+ "recruit_agent",
46284
+ "integration_improve"
46285
+ ]);
46286
+ function toolsOfflineRefusalText(toolName) {
46287
+ return `Ninjafy is in a control-plane maintenance window, so \`${toolName}\` is unavailable right now. Nothing was created, changed or saved. Do NOT retry this call, and do not try a different Ninjafy tool to work around it \u2014 every tool that writes to the control plane is refusing for the same reason, and the window is short. Tell the person plainly that you cannot save this at the moment and ask them to bring it back afterwards; carry on with anything that does not need saving.`;
46288
+ }
46289
+ function withMaintenanceGate(name, handler, opts) {
46290
+ if (!MAINTENANCE_GATED_TOOLS.has(name)) return handler;
46291
+ const isActive = opts?.isActive ?? (() => isToolsOfflineActive());
46292
+ const gated = async (...args) => {
46293
+ if (isActive()) {
46294
+ return { content: [{ type: "text", text: toolsOfflineRefusalText(name) }] };
46295
+ }
46296
+ return handler(...args);
46297
+ };
46298
+ return gated;
46299
+ }
46300
+
46092
46301
  // src/json-schema-to-zod-shape.ts
46093
46302
  function jsonSchemaToZodShape(schema) {
46094
46303
  if (!schema || schema["type"] !== "object") return {};
@@ -46838,12 +47047,12 @@ function selectIntegrationsToRefresh(input) {
46838
47047
  }
46839
47048
 
46840
47049
  // src/self-restart.ts
46841
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
46842
- import { homedir as homedir2 } from "os";
46843
- import { join as join6 } from "path";
47050
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
47051
+ import { homedir as homedir3 } from "os";
47052
+ import { join as join7 } from "path";
46844
47053
  import { randomUUID } from "crypto";
46845
47054
  function restartFlagsDir() {
46846
- return join6(homedir2(), ".augmented", "restart-flags");
47055
+ return join7(homedir3(), ".augmented", "restart-flags");
46847
47056
  }
46848
47057
  function isSelfRestartEnabled(env2) {
46849
47058
  const raw = (env2.AGT_AGENT_SELF_RESTART_ENABLED ?? "").trim().toLowerCase();
@@ -46854,8 +47063,8 @@ function buildAgentRestartFlag(codeName, reason, nowMs) {
46854
47063
  }
46855
47064
  function writeAgentRestartFlag(codeName, reason, nowMs) {
46856
47065
  const dir = restartFlagsDir();
46857
- if (!existsSync2(dir)) mkdirSync3(dir, { recursive: true });
46858
- const path = join6(dir, `${codeName}.flag`);
47066
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
47067
+ const path = join7(dir, `${codeName}.flag`);
46859
47068
  const flag = buildAgentRestartFlag(codeName, reason, nowMs);
46860
47069
  const tmpPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
46861
47070
  writeFileSync2(tmpPath, JSON.stringify(flag) + "\n", "utf8");
@@ -46864,18 +47073,18 @@ function writeAgentRestartFlag(codeName, reason, nowMs) {
46864
47073
  }
46865
47074
 
46866
47075
  // src/self-restart-confirm.ts
46867
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync5 } from "fs";
46868
- import { homedir as homedir3 } from "os";
46869
- import { join as join7 } from "path";
47076
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync6 } from "fs";
47077
+ import { homedir as homedir4 } from "os";
47078
+ import { join as join8 } from "path";
46870
47079
 
46871
47080
  // src/restart-confirm.ts
46872
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
47081
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
46873
47082
  import { dirname as dirname4 } from "path";
46874
47083
  import { randomUUID as randomUUID2 } from "crypto";
46875
47084
  var RESTART_CONFIRM_MAX_AGE_MS = 10 * 60 * 1e3;
46876
47085
  function writeRestartConfirmMarker(filePath, marker) {
46877
47086
  const dir = dirname4(filePath);
46878
- if (!existsSync3(dir)) mkdirSync4(dir, { recursive: true, mode: 448 });
47087
+ if (!existsSync4(dir)) mkdirSync4(dir, { recursive: true, mode: 448 });
46879
47088
  const tmpPath = `${filePath}.${process.pid}.${randomUUID2()}.tmp`;
46880
47089
  writeFileSync3(tmpPath, JSON.stringify(marker) + "\n", { encoding: "utf8", mode: 384 });
46881
47090
  renameSync3(tmpPath, filePath);
@@ -46884,7 +47093,7 @@ function writeRestartConfirmMarker(filePath, marker) {
46884
47093
  // src/self-restart-confirm.ts
46885
47094
  var SELF_RESTART_PENDING_FRESHNESS_MS = 5 * 60 * 1e3;
46886
47095
  function selfRestartAgentDir(codeName) {
46887
- return join7(homedir3(), ".augmented", codeName);
47096
+ return join8(homedir4(), ".augmented", codeName);
46888
47097
  }
46889
47098
  function selectFreshestPending(candidates, nowMs, maxAgeMs) {
46890
47099
  let best = null;
@@ -46897,7 +47106,7 @@ function selectFreshestPending(candidates, nowMs, maxAgeMs) {
46897
47106
  return best;
46898
47107
  }
46899
47108
  function readMarkerDir(dir, toCandidate) {
46900
- if (!existsSync4(dir)) return [];
47109
+ if (!existsSync5(dir)) return [];
46901
47110
  const out = [];
46902
47111
  let entries;
46903
47112
  try {
@@ -46908,7 +47117,7 @@ function readMarkerDir(dir, toCandidate) {
46908
47117
  for (const entry of entries) {
46909
47118
  if (!entry.endsWith(".json") || entry.endsWith(".tmp")) continue;
46910
47119
  try {
46911
- const parsed = JSON.parse(readFileSync5(join7(dir, entry), "utf8"));
47120
+ const parsed = JSON.parse(readFileSync6(join8(dir, entry), "utf8"));
46912
47121
  if (!parsed || typeof parsed !== "object") continue;
46913
47122
  const candidate = toCandidate(parsed);
46914
47123
  if (candidate) out.push(candidate);
@@ -46918,7 +47127,7 @@ function readMarkerDir(dir, toCandidate) {
46918
47127
  return out;
46919
47128
  }
46920
47129
  function readPendingCandidates(agentDir) {
46921
- const slack = readMarkerDir(join7(agentDir, "slack-pending-inbound"), (p2) => {
47130
+ const slack = readMarkerDir(join8(agentDir, "slack-pending-inbound"), (p2) => {
46922
47131
  const channel = p2["channel"];
46923
47132
  if (typeof channel !== "string" || channel.length === 0) return null;
46924
47133
  const threadTs = p2["thread_ts"];
@@ -46926,7 +47135,7 @@ function readPendingCandidates(agentDir) {
46926
47135
  if (typeof threadTs === "string" && threadTs.length > 0) reply["thread_ts"] = threadTs;
46927
47136
  return { source: "slack", receivedAtMs: Date.parse(String(p2["received_at"])), reply };
46928
47137
  });
46929
- const telegram = readMarkerDir(join7(agentDir, "telegram-pending-inbound"), (p2) => {
47138
+ const telegram = readMarkerDir(join8(agentDir, "telegram-pending-inbound"), (p2) => {
46930
47139
  const chatId = p2["chat_id"];
46931
47140
  if (chatId == null || typeof chatId !== "string" && typeof chatId !== "number") return null;
46932
47141
  const reply = { chat_id: chatId };
@@ -46947,17 +47156,17 @@ function captureSelfRestartConfirm(opts) {
46947
47156
  // human, so buildBackOnlineText degrades to the generic copy.
46948
47157
  reply: best.reply
46949
47158
  };
46950
- const file = best.source === "slack" ? join7(opts.agentDir, "slack-restart-confirm.json") : join7(opts.agentDir, "telegram-restart-confirm.json");
47159
+ const file = best.source === "slack" ? join8(opts.agentDir, "slack-restart-confirm.json") : join8(opts.agentDir, "telegram-restart-confirm.json");
46951
47160
  writeRestartConfirmMarker(file, marker);
46952
47161
  return best.source;
46953
47162
  }
46954
47163
 
46955
47164
  // ../core/dist/integrations/forwarded-tools-report.js
46956
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
46957
- import { join as join8 } from "path";
47165
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
47166
+ import { join as join9 } from "path";
46958
47167
  var FORWARDED_TOOLS_REPORT_FILENAME = "forwarded-tools.json";
46959
47168
  function forwardedToolsReportPath(agentDir) {
46960
- return join8(agentDir, FORWARDED_TOOLS_REPORT_FILENAME);
47169
+ return join9(agentDir, FORWARDED_TOOLS_REPORT_FILENAME);
46961
47170
  }
46962
47171
  function writeForwardedToolsReport(agentDir, report) {
46963
47172
  try {
@@ -47972,6 +48181,26 @@ var server = new McpServer({
47972
48181
  name: "augmented",
47973
48182
  version: "0.1.0"
47974
48183
  });
48184
+ var wrapRegistration = (original) => (...args) => {
48185
+ const name = args[0];
48186
+ const handlerIndex = args.length - 1;
48187
+ const handler = args[handlerIndex];
48188
+ if (typeof name === "string" && typeof handler === "function") {
48189
+ const rewritten = [...args];
48190
+ rewritten[handlerIndex] = withMaintenanceGate(
48191
+ name,
48192
+ handler
48193
+ );
48194
+ return original(...rewritten);
48195
+ }
48196
+ return original(...args);
48197
+ };
48198
+ var registerToolUnwrapped = server.tool.bind(server);
48199
+ server.tool = wrapRegistration(registerToolUnwrapped);
48200
+ var registerToolV2Unwrapped = server.registerTool.bind(server);
48201
+ server.registerTool = wrapRegistration(
48202
+ registerToolV2Unwrapped
48203
+ );
47975
48204
  if (AGT_AGENT_ID) {
47976
48205
  registerApprovalTools(server, { agentId: AGT_AGENT_ID, apiPost, apiGet });
47977
48206
  }
@@ -50906,9 +51135,9 @@ async function forwardToolCall(toolName, args) {
50906
51135
  // root to its canonical form so both sides of the comparison are real
50907
51136
  // paths. Falls back to the unresolved join if the dir is missing, which
50908
51137
  // then simply refuses (there is nothing to read anyway).
50909
- agentDir: canonicalAgentDir(join9(homedir4(), ".augmented", AGT_AGENT_CODE_NAME)),
51138
+ agentDir: canonicalAgentDir(join10(homedir5(), ".augmented", AGT_AGENT_CODE_NAME)),
50910
51139
  // agent-dir-allow: immediately realpath-resolved to the ADR-0049 agent_id-keyed dir before use as a containment root
50911
- path: { isAbsolute: isAbsolute2, resolve: pathResolve, join: join9, sep: sep2 },
51140
+ path: { isAbsolute: isAbsolute2, resolve: pathResolve, join: join10, sep: sep2 },
50912
51141
  // O_NOFOLLOW is the whole point of opening by flags: the path is already
50913
51142
  // symlink-resolved, so a symlink at the final component means it was
50914
51143
  // swapped after the check, and this turns that into an ELOOP.
@@ -51331,7 +51560,7 @@ async function saveArtefactSourceToFile(artefactId) {
51331
51560
  codeName: AGT_AGENT_CODE_NAME,
51332
51561
  turnInitiatorFile: process.env.AGT_TURN_INITIATOR_FILE,
51333
51562
  agentId: AGT_AGENT_ID,
51334
- homeDir: homedir4()
51563
+ homeDir: homedir5()
51335
51564
  });
51336
51565
  const readOnce = async () => {
51337
51566
  const before = await fetchArtefactRevision(artefactId);
@@ -51346,7 +51575,7 @@ async function saveArtefactSourceToFile(artefactId) {
51346
51575
  };
51347
51576
  let result = await readOnce();
51348
51577
  if (result.revisionState !== "stable") result = await readOnce();
51349
- const filePath = join9(dir, safeDownloadName(`${artefactId}.html`, `${artefactId}.html`));
51578
+ const filePath = join10(dir, safeDownloadName(`${artefactId}.html`, `${artefactId}.html`));
51350
51579
  await writeFile(filePath, result.html, "utf8");
51351
51580
  return {
51352
51581
  path: filePath,
@@ -51427,7 +51656,7 @@ async function exportArtefactToFile(dir, artefactId, format, item) {
51427
51656
  return { kind: "pending", jobId: outcome.jobId, retryAfterMs: outcome.retryAfterMs };
51428
51657
  }
51429
51658
  const fallback = `${artefactId}${item !== void 0 ? `-item-${item + 1}` : ""}.${format}`;
51430
- const filePath = join9(dir, safeDownloadName(outcome.body.filename, fallback));
51659
+ const filePath = join10(dir, safeDownloadName(outcome.body.filename, fallback));
51431
51660
  const buf = Buffer.from(outcome.body.base64, "base64");
51432
51661
  await writeFile(filePath, buf);
51433
51662
  return {
@@ -51454,7 +51683,7 @@ async function downloadArtefactText(params) {
51454
51683
  codeName: AGT_AGENT_CODE_NAME,
51455
51684
  turnInitiatorFile: process.env.AGT_TURN_INITIATOR_FILE,
51456
51685
  agentId: AGT_AGENT_ID,
51457
- homeDir: homedir4()
51686
+ homeDir: homedir5()
51458
51687
  });
51459
51688
  let items;
51460
51689
  let truncatedFrom = null;
@@ -44619,14 +44619,37 @@ var FLAG_REGISTRY = [
44619
44619
  },
44620
44620
  {
44621
44621
  key: "platform-maintenance-mode",
44622
- 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.)',
44623
- flagType: "boolean",
44624
- // Declared safe value is `false` (normal operation). Fail-safe direction: a
44625
- // flag-DB read error or absent flag map must never silence the fleet on its
44626
- // own — agents stay responsive unless an operator deliberately flips this on
44627
- // from the admin Feature Flags page. No envVar: net-new control, no
44628
- // pre-flags env gate to migrate.
44629
- defaultValue: false,
44622
+ description: 'Platform-wide maintenance mode (ENG-6506; widened to modes by ENG-11436). off = normal operation (ships dark; channels behave exactly as today). tools-offline = every agent KEEPS conversing but every Augmented tool that WRITES to the control plane refuses with a reason, so nothing is created during a window whose database restore would discard it; reads are untouched. fleet-offline = the previous boolean-true behaviour AND the write gate: admitted human inbound gets a "we are offline for maintenance" notice instead of being dispatched, for whole-platform downtime such as DB upgrades. The modes are CUMULATIVE \u2014 fleet-offline gates writes too, because silencing channel inbound does not stop a /loop or a scheduled task filing work. Read host-side per inbound and per tool call from the flags cache (no MCP restart). (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
44623
+ flagType: "enum",
44624
+ allowedValues: ["off", "tools-offline", "fleet-offline"],
44625
+ // Declared safe value is `off` (normal operation). Fail-safe direction: a
44626
+ // flag-DB read error or absent flag map must never silence the fleet OR take
44627
+ // its write tools away on its own — agents stay fully functional unless an
44628
+ // operator deliberately sets a mode from the admin Feature Flags page. No
44629
+ // envVar: net-new control, no pre-flags env gate to migrate.
44630
+ //
44631
+ // ENG-11436 widened this from a boolean rather than adding a second flag, so
44632
+ // there is ONE operator control whose modes are mutually exclusive by type
44633
+ // instead of two similar booleans with four combinations.
44634
+ //
44635
+ // The host resolver GRANDFATHERS a boolean-shaped cache value
44636
+ // (true -> fleet-offline, false -> off), and the DIRECTION it protects is
44637
+ // narrower than an earlier version of this comment claimed (Obi Wan
44638
+ // `e3f0d2c154b9` on #6903). It said "an older manager still writing a boolean
44639
+ // must not make a central flip a silent no-op". That situation cannot arise:
44640
+ // once this registry change deploys, normalizeFlagValue only ever yields one
44641
+ // of the three strings, feature-flags-host.ts writes the heartbeat map
44642
+ // verbatim, so no manager writes a boolean again. What grandfathering
44643
+ // actually covers is a NEW MCP reading a STALE PRE-DEPLOY cache that still
44644
+ // holds one.
44645
+ //
44646
+ // The direction that DOES happen is not handled and cannot be: an OLD MCP
44647
+ // reads the new string with resolveHostBooleanFlag, gets undefined, and keeps
44648
+ // dispatching. That is a real regression against ENG-6506 until the fleet
44649
+ // converges, and it is why the runbook makes convergence a deploy-order
44650
+ // PRECONDITION rather than offering a boolean fallback — there is none, since
44651
+ // an enum flag rejects `{value: true}` with a 400.
44652
+ defaultValue: "off",
44630
44653
  // Flipping this takes the ENTIRE fleet offline to end users; a mis-flip is a
44631
44654
  // platform-wide availability incident, so mutations require explicit
44632
44655
  // confirmation (the admin flip-reach modal).