@integrity-labs/agt-cli 0.28.948 → 0.28.949

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 (26) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-LRVXTEFJ.js → chunk-A3T3WOC2.js} +2 -2
  3. package/dist/{chunk-YZ5HGHPG.js → chunk-JANAAFED.js} +36 -1
  4. package/dist/{chunk-YZ5HGHPG.js.map → chunk-JANAAFED.js.map} +1 -1
  5. package/dist/{chunk-YGYDTSMZ.js → chunk-MTRJTGJL.js} +4 -4
  6. package/dist/{claude-code-updater-4E5T2X3Z.js → claude-code-updater-NAHJ6O6C.js} +37 -3
  7. package/dist/claude-code-updater-NAHJ6O6C.js.map +1 -0
  8. package/dist/{claude-pair-runtime-HCFLP3RT.js → claude-pair-runtime-7JTAECL4.js} +2 -2
  9. package/dist/lib/manager-worker.js +32 -19
  10. package/dist/lib/manager-worker.js.map +1 -1
  11. package/dist/mcp/direct-chat-channel.js +35 -0
  12. package/dist/mcp/index.js +35 -0
  13. package/dist/mcp/origami.js +35 -0
  14. package/dist/mcp/slack-channel.js +35 -0
  15. package/dist/mcp/telegram-channel.js +35 -0
  16. package/dist/{persistent-session-WN4GLJUN.js → persistent-session-VX26HP7Z.js} +3 -3
  17. package/dist/{responsiveness-probe-DJMZ4WAU.js → responsiveness-probe-5LUHQCKN.js} +3 -3
  18. package/dist/{session-auth-dead-T5SUK2HT.js → session-auth-dead-2WOMHFPP.js} +2 -2
  19. package/package.json +1 -1
  20. package/dist/claude-code-updater-4E5T2X3Z.js.map +0 -1
  21. /package/dist/{chunk-LRVXTEFJ.js.map → chunk-A3T3WOC2.js.map} +0 -0
  22. /package/dist/{chunk-YGYDTSMZ.js.map → chunk-MTRJTGJL.js.map} +0 -0
  23. /package/dist/{claude-pair-runtime-HCFLP3RT.js.map → claude-pair-runtime-7JTAECL4.js.map} +0 -0
  24. /package/dist/{persistent-session-WN4GLJUN.js.map → persistent-session-VX26HP7Z.js.map} +0 -0
  25. /package/dist/{responsiveness-probe-DJMZ4WAU.js.map → responsiveness-probe-5LUHQCKN.js.map} +0 -0
  26. /package/dist/{session-auth-dead-T5SUK2HT.js.map → session-auth-dead-2WOMHFPP.js.map} +0 -0
@@ -38072,7 +38072,42 @@ var FLAG_REGISTRY = [
38072
38072
  // Deliberately NOT public. The browser has no decision to make here; the
38073
38073
  // consumers are the host payload that builds `input.integrations` and the
38074
38074
  // grant route that refuses to install the row while the flag is off.
38075
+ },
38076
+ {
38077
+ key: "ip-allowlist-enforce-admin-debug",
38078
+ description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. Separate from the mcp flag on purpose - see that entry.",
38079
+ flagType: "boolean",
38080
+ // Shadow. The measurement comes first: nobody yet knows the real set of
38081
+ // staff addresses, and arming against a guess locks you out of the very
38082
+ // tool you would use to diagnose the lockout.
38083
+ defaultValue: false,
38084
+ // Arming it changes who can reach production diagnostics. Worth one
38085
+ // confirmation.
38086
+ sensitive: true
38087
+ // Not public: the browser makes no decision from this. The only consumer is
38088
+ // packages/api/src/middleware/ip-allowlist.ts.
38075
38089
  }
38090
+ // THERE IS DELIBERATELY NO `ip-allowlist-enforce-mcp` FLAG.
38091
+ //
38092
+ // The per-agent MCP surface (POST /agents/:codeName/mcp) is allowlist
38093
+ // SHADOW-ONLY, enforced structurally rather than by a default: its entry in
38094
+ // ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
38095
+ // flag is consulted and no flag flip can arm it.
38096
+ //
38097
+ // Why it is not simply a second flag defaulting false: most of that surface's
38098
+ // callers have no allowlistable address at all. Per ADR-0032 only hosts in the
38099
+ // agt-hosts account egress through stable NAT EIPs; a host with
38100
+ // provision_source `self` or `customer-ec2`, or any BYO account, egresses from
38101
+ // an address the control plane cannot know. Arming against that population
38102
+ // black-holes a customer's whole fleet, and per
38103
+ // docs/runbooks/composio-allowlist-rollout.md this class of change has no
38104
+ // fleet-level undo. A flag would leave that one toggle away during an
38105
+ // incident, from someone who had just armed the admin-debug surface and
38106
+ // reasonably assumed the pair moved together. Requiring a code change puts
38107
+ // review in front of an irreversible action, which is the right price.
38108
+ //
38109
+ // If the shadow data ever shows the population IS allowlistable, adding the
38110
+ // flag here and a key in ENFORCE_FLAG is a small, deliberate diff.
38076
38111
  ];
38077
38112
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
38078
38113
 
package/dist/mcp/index.js CHANGED
@@ -30388,7 +30388,42 @@ var FLAG_REGISTRY = [
30388
30388
  // Deliberately NOT public. The browser has no decision to make here; the
30389
30389
  // consumers are the host payload that builds `input.integrations` and the
30390
30390
  // grant route that refuses to install the row while the flag is off.
30391
+ },
30392
+ {
30393
+ key: "ip-allowlist-enforce-admin-debug",
30394
+ description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. Separate from the mcp flag on purpose - see that entry.",
30395
+ flagType: "boolean",
30396
+ // Shadow. The measurement comes first: nobody yet knows the real set of
30397
+ // staff addresses, and arming against a guess locks you out of the very
30398
+ // tool you would use to diagnose the lockout.
30399
+ defaultValue: false,
30400
+ // Arming it changes who can reach production diagnostics. Worth one
30401
+ // confirmation.
30402
+ sensitive: true
30403
+ // Not public: the browser makes no decision from this. The only consumer is
30404
+ // packages/api/src/middleware/ip-allowlist.ts.
30391
30405
  }
30406
+ // THERE IS DELIBERATELY NO `ip-allowlist-enforce-mcp` FLAG.
30407
+ //
30408
+ // The per-agent MCP surface (POST /agents/:codeName/mcp) is allowlist
30409
+ // SHADOW-ONLY, enforced structurally rather than by a default: its entry in
30410
+ // ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
30411
+ // flag is consulted and no flag flip can arm it.
30412
+ //
30413
+ // Why it is not simply a second flag defaulting false: most of that surface's
30414
+ // callers have no allowlistable address at all. Per ADR-0032 only hosts in the
30415
+ // agt-hosts account egress through stable NAT EIPs; a host with
30416
+ // provision_source `self` or `customer-ec2`, or any BYO account, egresses from
30417
+ // an address the control plane cannot know. Arming against that population
30418
+ // black-holes a customer's whole fleet, and per
30419
+ // docs/runbooks/composio-allowlist-rollout.md this class of change has no
30420
+ // fleet-level undo. A flag would leave that one toggle away during an
30421
+ // incident, from someone who had just armed the admin-debug surface and
30422
+ // reasonably assumed the pair moved together. Requiring a code change puts
30423
+ // review in front of an irreversible action, which is the right price.
30424
+ //
30425
+ // If the shadow data ever shows the population IS allowlistable, adding the
30426
+ // flag here and a key in ENFORCE_FLAG is a small, deliberate diff.
30392
30427
  ];
30393
30428
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
30394
30429
 
@@ -44315,7 +44315,42 @@ var FLAG_REGISTRY = [
44315
44315
  // Deliberately NOT public. The browser has no decision to make here; the
44316
44316
  // consumers are the host payload that builds `input.integrations` and the
44317
44317
  // grant route that refuses to install the row while the flag is off.
44318
+ },
44319
+ {
44320
+ key: "ip-allowlist-enforce-admin-debug",
44321
+ description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. Separate from the mcp flag on purpose - see that entry.",
44322
+ flagType: "boolean",
44323
+ // Shadow. The measurement comes first: nobody yet knows the real set of
44324
+ // staff addresses, and arming against a guess locks you out of the very
44325
+ // tool you would use to diagnose the lockout.
44326
+ defaultValue: false,
44327
+ // Arming it changes who can reach production diagnostics. Worth one
44328
+ // confirmation.
44329
+ sensitive: true
44330
+ // Not public: the browser makes no decision from this. The only consumer is
44331
+ // packages/api/src/middleware/ip-allowlist.ts.
44318
44332
  }
44333
+ // THERE IS DELIBERATELY NO `ip-allowlist-enforce-mcp` FLAG.
44334
+ //
44335
+ // The per-agent MCP surface (POST /agents/:codeName/mcp) is allowlist
44336
+ // SHADOW-ONLY, enforced structurally rather than by a default: its entry in
44337
+ // ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
44338
+ // flag is consulted and no flag flip can arm it.
44339
+ //
44340
+ // Why it is not simply a second flag defaulting false: most of that surface's
44341
+ // callers have no allowlistable address at all. Per ADR-0032 only hosts in the
44342
+ // agt-hosts account egress through stable NAT EIPs; a host with
44343
+ // provision_source `self` or `customer-ec2`, or any BYO account, egresses from
44344
+ // an address the control plane cannot know. Arming against that population
44345
+ // black-holes a customer's whole fleet, and per
44346
+ // docs/runbooks/composio-allowlist-rollout.md this class of change has no
44347
+ // fleet-level undo. A flag would leave that one toggle away during an
44348
+ // incident, from someone who had just armed the admin-debug surface and
44349
+ // reasonably assumed the pair moved together. Requiring a code change puts
44350
+ // review in front of an irreversible action, which is the right price.
44351
+ //
44352
+ // If the shadow data ever shows the population IS allowlistable, adding the
44353
+ // flag here and a key in ENFORCE_FLAG is a small, deliberate diff.
44319
44354
  ];
44320
44355
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
44321
44356
 
@@ -38972,7 +38972,42 @@ var FLAG_REGISTRY = [
38972
38972
  // Deliberately NOT public. The browser has no decision to make here; the
38973
38973
  // consumers are the host payload that builds `input.integrations` and the
38974
38974
  // grant route that refuses to install the row while the flag is off.
38975
+ },
38976
+ {
38977
+ key: "ip-allowlist-enforce-admin-debug",
38978
+ description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. Separate from the mcp flag on purpose - see that entry.",
38979
+ flagType: "boolean",
38980
+ // Shadow. The measurement comes first: nobody yet knows the real set of
38981
+ // staff addresses, and arming against a guess locks you out of the very
38982
+ // tool you would use to diagnose the lockout.
38983
+ defaultValue: false,
38984
+ // Arming it changes who can reach production diagnostics. Worth one
38985
+ // confirmation.
38986
+ sensitive: true
38987
+ // Not public: the browser makes no decision from this. The only consumer is
38988
+ // packages/api/src/middleware/ip-allowlist.ts.
38975
38989
  }
38990
+ // THERE IS DELIBERATELY NO `ip-allowlist-enforce-mcp` FLAG.
38991
+ //
38992
+ // The per-agent MCP surface (POST /agents/:codeName/mcp) is allowlist
38993
+ // SHADOW-ONLY, enforced structurally rather than by a default: its entry in
38994
+ // ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
38995
+ // flag is consulted and no flag flip can arm it.
38996
+ //
38997
+ // Why it is not simply a second flag defaulting false: most of that surface's
38998
+ // callers have no allowlistable address at all. Per ADR-0032 only hosts in the
38999
+ // agt-hosts account egress through stable NAT EIPs; a host with
39000
+ // provision_source `self` or `customer-ec2`, or any BYO account, egresses from
39001
+ // an address the control plane cannot know. Arming against that population
39002
+ // black-holes a customer's whole fleet, and per
39003
+ // docs/runbooks/composio-allowlist-rollout.md this class of change has no
39004
+ // fleet-level undo. A flag would leave that one toggle away during an
39005
+ // incident, from someone who had just armed the admin-debug surface and
39006
+ // reasonably assumed the pair moved together. Requiring a code change puts
39007
+ // review in front of an irreversible action, which is the right price.
39008
+ //
39009
+ // If the shadow data ever shows the population IS allowlistable, adding the
39010
+ // flag here and a key in ENFORCE_FLAG is a small, deliberate diff.
38976
39011
  ];
38977
39012
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
38978
39013
 
@@ -38984,7 +38984,42 @@ var FLAG_REGISTRY = [
38984
38984
  // Deliberately NOT public. The browser has no decision to make here; the
38985
38985
  // consumers are the host payload that builds `input.integrations` and the
38986
38986
  // grant route that refuses to install the row while the flag is off.
38987
+ },
38988
+ {
38989
+ key: "ip-allowlist-enforce-admin-debug",
38990
+ description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. Separate from the mcp flag on purpose - see that entry.",
38991
+ flagType: "boolean",
38992
+ // Shadow. The measurement comes first: nobody yet knows the real set of
38993
+ // staff addresses, and arming against a guess locks you out of the very
38994
+ // tool you would use to diagnose the lockout.
38995
+ defaultValue: false,
38996
+ // Arming it changes who can reach production diagnostics. Worth one
38997
+ // confirmation.
38998
+ sensitive: true
38999
+ // Not public: the browser makes no decision from this. The only consumer is
39000
+ // packages/api/src/middleware/ip-allowlist.ts.
38987
39001
  }
39002
+ // THERE IS DELIBERATELY NO `ip-allowlist-enforce-mcp` FLAG.
39003
+ //
39004
+ // The per-agent MCP surface (POST /agents/:codeName/mcp) is allowlist
39005
+ // SHADOW-ONLY, enforced structurally rather than by a default: its entry in
39006
+ // ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
39007
+ // flag is consulted and no flag flip can arm it.
39008
+ //
39009
+ // Why it is not simply a second flag defaulting false: most of that surface's
39010
+ // callers have no allowlistable address at all. Per ADR-0032 only hosts in the
39011
+ // agt-hosts account egress through stable NAT EIPs; a host with
39012
+ // provision_source `self` or `customer-ec2`, or any BYO account, egresses from
39013
+ // an address the control plane cannot know. Arming against that population
39014
+ // black-holes a customer's whole fleet, and per
39015
+ // docs/runbooks/composio-allowlist-rollout.md this class of change has no
39016
+ // fleet-level undo. A flag would leave that one toggle away during an
39017
+ // incident, from someone who had just armed the admin-debug surface and
39018
+ // reasonably assumed the pair moved together. Requiring a code change puts
39019
+ // review in front of an irreversible action, which is the right price.
39020
+ //
39021
+ // If the shadow data ever shows the population IS allowlistable, adding the
39022
+ // flag here and a key in ENFORCE_FLAG is a small, deliberate diff.
38988
39023
  ];
38989
39024
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
38990
39025
 
@@ -62,8 +62,8 @@ import {
62
62
  writeDirectChatSessionState,
63
63
  writeEgressAllowlist,
64
64
  writePersistentClaudeWrapper
65
- } from "./chunk-LRVXTEFJ.js";
66
- import "./chunk-YZ5HGHPG.js";
65
+ } from "./chunk-A3T3WOC2.js";
66
+ import "./chunk-JANAAFED.js";
67
67
  import "./chunk-DHWNVVX4.js";
68
68
  import "./chunk-XWVM4KPK.js";
69
69
  export {
@@ -131,4 +131,4 @@ export {
131
131
  writeEgressAllowlist,
132
132
  writePersistentClaudeWrapper
133
133
  };
134
- //# sourceMappingURL=persistent-session-WN4GLJUN.js.map
134
+ //# sourceMappingURL=persistent-session-VX26HP7Z.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-LRVXTEFJ.js";
4
- import "./chunk-YZ5HGHPG.js";
3
+ } from "./chunk-A3T3WOC2.js";
4
+ import "./chunk-JANAAFED.js";
5
5
  import "./chunk-DHWNVVX4.js";
6
6
  import "./chunk-XWVM4KPK.js";
7
7
 
@@ -800,4 +800,4 @@ export {
800
800
  readAndResetSlackReplyBindingClassifications,
801
801
  readAndResetSlackReplyTargetClassifications
802
802
  };
803
- //# sourceMappingURL=responsiveness-probe-DJMZ4WAU.js.map
803
+ //# sourceMappingURL=responsiveness-probe-5LUHQCKN.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-YZ5HGHPG.js";
3
+ } from "./chunk-JANAAFED.js";
4
4
 
5
5
  // src/lib/session-auth-dead.ts
6
6
  import { closeSync, openSync, readSync, readdirSync, statSync } from "fs";
@@ -203,4 +203,4 @@ export {
203
203
  decideSessionAuthState,
204
204
  probeSessionAuth
205
205
  };
206
- //# sourceMappingURL=session-auth-dead-T5SUK2HT.js.map
206
+ //# sourceMappingURL=session-auth-dead-2WOMHFPP.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.948",
3
+ "version": "0.28.949",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/claude-code-updater.ts"],"sourcesContent":["// ENG-5672: idle-gated, server-pinned Claude Code auto-updater.\n// ENG-5798: multi-install drift handling — detect coexisting claude binaries,\n// short-circuit on native autoupdater hosts, verify against absolute\n// install path, hard-error after N consecutive verify mismatches.\n//\n// Hosts install @anthropic-ai/claude-code once at bootstrap (host-bootstrap.ts)\n// and never refresh it, so a long-lived host runs a stale Claude Code forever.\n// This module brings a host to its *desired* version, which the server pins via\n// /host/refresh (per-host override, else fleet default, else 'latest').\n//\n// Safety properties (this runs npm install -g on live prod hosts):\n// • Idle-gated — we never install while a `claude --name agt-*` agent process\n// is running on the host (a running process holds the old binary anyway;\n// replacing it under a live session is pointless and risky). Busy → defer\n// to a later cycle.\n// • No-op when already on target — a concrete target equal to the installed\n// version short-circuits; a dist-tag ('latest') is resolved to a concrete\n// version via `npm view` and compared, so we don't reinstall every cycle.\n// • Host-global throttle — at most one check per interval (default 6h), so the\n// manager's ~10-30s poll doesn't shell out to npm/ps constantly.\n// • Log-and-continue — every failure is logged and swallowed; an error here\n// must never break the manager poll cycle.\n// • Multi-install awareness (ENG-5798) — on hosts where Anthropic's native\n// autoupdater owns /usr/bin/claude, this updater short-circuits cleanly\n// instead of fighting it. On hosts where multiple binaries coexist (e.g. a\n// brew cask alongside the npm global), we log every path+version once so\n// drift is visible in manager.log without spamming every cycle.\n//\n// The *installed* version is reported separately as hosts.framework_version\n// (the claude-code framework adapter's getVersion() == `claude --version`), so\n// this module does not report it itself.\n\nimport { execFile, execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { promisify } from 'node:util';\nimport { parsePsRows, CLAUDE_AGENT_PROCESS_PATTERN } from './orphan-channel-mcp-reaper.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst NPM_PKG = '@anthropic-ai/claude-code';\n\n/** Default host-global check interval. Overridable via env for tuning. */\nconst DEFAULT_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6h\n\n/**\n * ENG-5798: After this many consecutive verify mismatches against the same\n * target version, emit a distinct one-shot error code that the\n * synthetic-probe / CloudWatch alarm pipeline can latch onto, instead of\n * silently re-failing forever. Overridable via env for testing.\n */\nconst DEFAULT_VERIFY_MISMATCH_THRESHOLD = 5;\n\nfunction intervalMs(): number {\n const raw = Number.parseInt(process.env['AGT_CLAUDE_UPDATE_INTERVAL_MS'] ?? '', 10);\n return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_INTERVAL_MS;\n}\n\nfunction verifyMismatchThreshold(): number {\n const raw = Number.parseInt(process.env['AGT_CLAUDE_UPDATE_VERIFY_THRESHOLD'] ?? '', 10);\n return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_VERIFY_MISMATCH_THRESHOLD;\n}\n\n/** Extract a version string (leading semver) from `claude --version` / npm output. */\nexport function parseVersion(output: string): string | null {\n const m = output.match(/\\d+\\.\\d+\\.\\d+[A-Za-z0-9.+-]*/);\n return m ? m[0] : null;\n}\n\n/** A target spec is \"concrete\" when it already names an exact version. */\nexport function isConcreteVersion(spec: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+[A-Za-z0-9.+-]*$/.test(spec.trim());\n}\n\n/**\n * Well-known absolute paths where a `claude` binary may live on managed hosts.\n * Probed alongside the PATH-resolved binary so we can see all install slots,\n * even ones not on the manager systemd unit's PATH.\n */\nexport const WELL_KNOWN_CLAUDE_PATHS: readonly string[] = [\n '/usr/bin/claude', // Anthropic native autoupdater (Linux deb/rpm)\n '/usr/local/bin/claude', // npm global on macOS Intel + Linux without nvm\n '/opt/homebrew/bin/claude', // Homebrew on macOS Apple Silicon\n '/home/linuxbrew/.linuxbrew/bin/claude', // Linuxbrew (used on agt-aws-* hosts)\n];\n\nexport interface ClaudeCodeUpdaterDeps {\n /** Server-pinned target: a concrete version or an npm dist-tag like 'latest'. */\n desiredVersion: string;\n /** Currently-installed version, if the caller already knows it (e.g. the\n * manager's cachedFrameworkVersion). When omitted we run `claude --version`. */\n currentVersion?: string | null;\n log: (msg: string) => void;\n /** Override \"now\" for tests. */\n now?: () => number;\n /** Run a command; injected in tests. Defaults to execFile. */\n runCommand?: (cmd: string, args: string[]) => Promise<{ stdout: string; stderr: string }>;\n /** Raw `ps -eo pid,ppid,args` snapshot; injected in tests. */\n psSnapshot?: () => string;\n /** Sync existence probe for binary detection; injected in tests. */\n fileExists?: (path: string) => boolean;\n /** Read ~/.claude.json contents (raw); returns null when absent. Injected in tests. */\n readClaudeJson?: (path: string) => string | null;\n /** Resolve the user's home directory; injected in tests. */\n homeDir?: () => string;\n}\n\nexport type UpdaterOutcome =\n | { action: 'throttled' }\n | { action: 'up_to_date'; version: string }\n | { action: 'deferred_busy'; target: string }\n | { action: 'skipped_native_install'; target: string }\n | { action: 'updated'; from: string | null; to: string }\n | { action: 'failed'; reason: string };\n\n// ── Host-global state (module-level; one updater per host process) ──────────\nlet lastCheckAt = 0;\n/** Per-process one-shot guard for the multi-install warn line. */\nlet multiInstallWarned = false;\n/**\n * Sticky counter for consecutive `post-install verify mismatch` against the\n * same target version. Reset on success, on target change, or after the\n * threshold error has been emitted (so we emit it exactly once per stuck run).\n */\nlet verifyMismatchState: { target: string; count: number; alarmed: boolean } | null = null;\n\n/** Test helper — reset module state. Not for production use. */\nexport function _resetClaudeCodeUpdaterState(): void {\n lastCheckAt = 0;\n multiInstallWarned = false;\n verifyMismatchState = null;\n}\n\nfunction defaultPsSnapshot(): string {\n return execFileSync('ps', ['-eo', 'pid,ppid,args'], { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 });\n}\n\nfunction defaultReadClaudeJson(path: string): string | null {\n try {\n return readFileSync(path, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/** True when at least one `claude --name agt-*` agent process is live. */\nexport function hostHasLiveAgent(psOutput: string): boolean {\n return parsePsRows(psOutput).some((r) => CLAUDE_AGENT_PROCESS_PATTERN.test(r.args));\n}\n\n/**\n * ENG-5798: returns true when ~/.claude.json reports `installMethod: \"native\"`,\n * meaning Anthropic's native autoupdater owns the on-disk binary. In that case\n * the brew/npm update path is actively harmful — it installs a second binary\n * the host's PATH never resolves to, and verify reads from yet a third.\n */\nexport function readsAsNativeInstall(raw: string | null): boolean {\n if (!raw) return false;\n try {\n const parsed = JSON.parse(raw) as { installMethod?: unknown };\n return parsed.installMethod === 'native';\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the absolute filesystem path that `npm install -g <pkg>` will write\n * the `claude` binary to. Returns null when `npm prefix -g` fails or is empty,\n * in which case the caller should fall back to PATH-based verify.\n *\n * We deliberately use `npm prefix -g` rather than `which claude` because the\n * latter resolves through PATH, which on agt-aws-* hosts can land on\n * /usr/bin/claude (the native autoupdater slot) — exactly the misalignment\n * ENG-5798 is fixing.\n */\nasync function resolveInstallTargetPath(\n run: (cmd: string, args: string[]) => Promise<{ stdout: string; stderr: string }>,\n): Promise<string | null> {\n try {\n const { stdout } = await run('npm', ['prefix', '-g']);\n const prefix = stdout.trim();\n if (!prefix) return null;\n return `${prefix}/bin/claude`;\n } catch {\n return null;\n }\n}\n\n/**\n * Enumerate every `claude` binary visible to the host: PATH-resolved + the\n * well-known absolute slots + `~/.claude/local/claude`. Returns one entry per\n * unique resolved path with the version each reports (or null if it failed to\n * execute). Used purely for the one-shot drift warn line.\n */\nexport async function probeClaudeInstalls(\n run: (cmd: string, args: string[]) => Promise<{ stdout: string; stderr: string }>,\n fileExists: (path: string) => boolean,\n home: string,\n): Promise<Array<{ path: string; version: string | null }>> {\n const candidates = new Set<string>(WELL_KNOWN_CLAUDE_PATHS);\n candidates.add(`${home}/.claude/local/claude`);\n // PATH-resolved. `which` is portable enough across the Linux + macOS hosts\n // we target; errors here just mean \"no claude on PATH\", which is fine.\n try {\n const { stdout } = await run('which', ['claude']);\n const p = stdout.trim().split('\\n')[0]?.trim();\n if (p) candidates.add(p);\n } catch {\n /* no-op */\n }\n\n const results: Array<{ path: string; version: string | null }> = [];\n for (const p of candidates) {\n if (!fileExists(p)) continue;\n let version: string | null = null;\n try {\n const { stdout } = await run(p, ['--version']);\n version = parseVersion(stdout);\n } catch {\n /* binary exists but failed to execute — record path with null version */\n }\n results.push({ path: p, version });\n }\n return results;\n}\n\n/**\n * Bring Claude Code to the desired version if the host is idle and not already\n * on target. Safe to call every manager poll — internally throttled. Returns an\n * outcome describing what happened (for logging/observability/tests).\n */\nexport async function maybeUpdateClaudeCode(deps: ClaudeCodeUpdaterDeps): Promise<UpdaterOutcome> {\n const now = deps.now ? deps.now() : Date.now();\n const run = deps.runCommand ?? ((cmd, args) => execFileAsync(cmd, args, { maxBuffer: 16 * 1024 * 1024 }));\n const ps = deps.psSnapshot ?? defaultPsSnapshot;\n const fileExists = deps.fileExists ?? existsSync;\n const readClaudeJson = deps.readClaudeJson ?? defaultReadClaudeJson;\n const home = (deps.homeDir ?? homedir)();\n const { log } = deps;\n\n // 1. Host-global throttle.\n if (now - lastCheckAt < intervalMs()) {\n return { action: 'throttled' };\n }\n lastCheckAt = now;\n\n try {\n const spec = (deps.desiredVersion || 'latest').trim();\n\n // 2. ENG-5798 multi-install drift detection. Emit one warn line per\n // process listing every claude binary + version we can see. This is\n // independent of the install decision below — even on hosts where we\n // proceed normally, surfacing drift is the difference between a 3-day\n // silent loop and a one-line lead.\n if (!multiInstallWarned) {\n const installs = await probeClaudeInstalls(run, fileExists, home);\n if (installs.length > 1) {\n const summary = installs\n .map((i) => `${i.path}=${i.version ?? 'unknown'}`)\n .join(', ');\n log(`[claude-code-updater] multiple claude installs detected: ${summary}`);\n }\n multiInstallWarned = true;\n }\n\n // 3. ENG-5798 native-install short-circuit. When Anthropic's native\n // autoupdater owns the host binary, the brew/npm update path is\n // actively harmful (it installs a second binary the manager's PATH\n // never resolves to, then verifies against yet a third). Defer.\n if (readsAsNativeInstall(readClaudeJson(`${home}/.claude.json`))) {\n // One-shot info line per process — same throttle as the warn above, but\n // we re-use the throttle window to keep this from logging every poll.\n log('[claude-code-updater] native install detected, deferring to Anthropic autoupdater');\n // Reset any sticky verify-mismatch state — it can't apply once we stop\n // attempting installs.\n verifyMismatchState = null;\n return { action: 'skipped_native_install', target: spec };\n }\n\n // 4. Resolve the target to a concrete version (read-only). A concrete spec\n // is its own target; a dist-tag is resolved via `npm view`.\n let target: string;\n if (isConcreteVersion(spec)) {\n target = spec;\n } else {\n const { stdout } = await run('npm', ['view', `${NPM_PKG}@${spec}`, 'version']);\n const resolved = parseVersion(stdout);\n if (!resolved) {\n log(`[claude-code-updater] could not resolve ${NPM_PKG}@${spec} to a version; skipping`);\n return { action: 'failed', reason: 'unresolved_target' };\n }\n target = resolved;\n }\n\n // 5. Determine the installed version (prefer the caller's cached value).\n let current = deps.currentVersion ?? null;\n if (!current) {\n try {\n const { stdout } = await run('claude', ['--version']);\n current = parseVersion(stdout);\n } catch {\n current = null; // claude missing/broken — treat as \"needs install\".\n }\n }\n\n // 6. Already on target → nothing to do.\n if (current && current === target) {\n // Successful no-op — clear sticky verify mismatch state for this target.\n if (verifyMismatchState && verifyMismatchState.target === target) {\n verifyMismatchState = null;\n }\n return { action: 'up_to_date', version: target };\n }\n\n // 7. Idle gate — never replace the binary under a live agent session.\n if (hostHasLiveAgent(ps())) {\n log(`[claude-code-updater] host busy (live agent) — deferring update to ${target} to a later cycle`);\n return { action: 'deferred_busy', target };\n }\n\n // 8. Install the target, then verify against the *absolute* path of the\n // binary npm just wrote — not via PATH. PATH resolution is the original\n // ENG-5798 bug: on hosts with multiple claude installs, PATH lands on a\n // different binary than the one npm overwrites, so verify never agrees.\n const installTargetPath = await resolveInstallTargetPath(run);\n log(`[claude-code-updater] updating Claude Code ${current ?? '(none)'} → ${target}`);\n await run('npm', ['install', '-g', `${NPM_PKG}@${target}`]);\n\n // Prefer the absolute install target; fall back to PATH only when\n // `npm prefix -g` failed (in which case verify is best-effort).\n const verifyTarget = installTargetPath ?? 'claude';\n let installed: string | null = null;\n try {\n const { stdout } = await run(verifyTarget, ['--version']);\n installed = parseVersion(stdout);\n } catch {\n installed = null;\n }\n\n if (installed !== target) {\n log(\n `[claude-code-updater] post-install verify mismatch: expected ${target}, got ${installed ?? 'unknown'} from ${verifyTarget}`,\n );\n\n // ENG-5798: hard-error after N consecutive mismatches against the same\n // target. Reset the counter when the target changes — a new pin is a\n // new event, not a continuation of the old stuck run.\n if (!verifyMismatchState || verifyMismatchState.target !== target) {\n verifyMismatchState = { target, count: 1, alarmed: false };\n } else {\n verifyMismatchState.count += 1;\n }\n\n const threshold = verifyMismatchThreshold();\n if (verifyMismatchState.count >= threshold && !verifyMismatchState.alarmed) {\n verifyMismatchState.alarmed = true;\n log(\n `[claude-code-updater] ERROR verify_mismatch_stuck: ${verifyMismatchState.count} consecutive verify failures for target ${target} (verify path: ${verifyTarget}) — install is not taking effect, investigate host install slots`,\n );\n return { action: 'failed', reason: 'verify_mismatch_threshold' };\n }\n return { action: 'failed', reason: 'verify_mismatch' };\n }\n\n // Successful install — clear sticky state.\n verifyMismatchState = null;\n log(`[claude-code-updater] updated Claude Code ${current ?? '(none)'} → ${installed}`);\n return { action: 'updated', from: current, to: installed };\n } catch (err) {\n // Log-and-continue: an updater failure must never break the manager tick.\n log(`[claude-code-updater] update attempt failed: ${err instanceof Error ? err.message : String(err)}`);\n return { action: 'failed', reason: 'exception' };\n }\n}\n"],"mappings":"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","names":[]}