@integrity-labs/agt-cli 0.27.9-test.12 → 0.27.9-test.14

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 (35) hide show
  1. package/dist/bin/agt.js +4 -4
  2. package/dist/chunk-354FAVQR.js +173 -0
  3. package/dist/chunk-354FAVQR.js.map +1 -0
  4. package/dist/{chunk-MQJ5DMPT.js → chunk-7GKJZBTB.js} +111 -204
  5. package/dist/chunk-7GKJZBTB.js.map +1 -0
  6. package/dist/{chunk-HR5T2RQF.js → chunk-I3YS5WFV.js} +3 -1
  7. package/dist/chunk-I3YS5WFV.js.map +1 -0
  8. package/dist/{chunk-SXBZDUKN.js → chunk-R7NKCGWN.js} +341 -46
  9. package/dist/chunk-R7NKCGWN.js.map +1 -0
  10. package/dist/{chunk-I2OTQJH3.js → chunk-WOOYOAPG.js} +2644 -2142
  11. package/dist/chunk-WOOYOAPG.js.map +1 -0
  12. package/dist/{claude-pair-runtime-F6USL3EW.js → claude-pair-runtime-GIUCD7IG.js} +2 -2
  13. package/dist/{claude-scheduler-EM24LTGV.js → claude-scheduler-FATCLHDM.js} +2 -2
  14. package/dist/daily-session-PNQX5URX.js +27 -0
  15. package/dist/lib/manager-worker.js +1125 -149
  16. package/dist/lib/manager-worker.js.map +1 -1
  17. package/dist/mcp/augmented-admin.js +21335 -0
  18. package/dist/mcp/index.js +121 -4
  19. package/dist/mcp/slack-channel.js +845 -208
  20. package/dist/mcp/teams-channel.js +73 -29
  21. package/dist/mcp/telegram-channel.js +579 -157
  22. package/dist/{persistent-session-OL5EDYB4.js → persistent-session-35PWSTLO.js} +10 -3
  23. package/dist/persistent-session-35PWSTLO.js.map +1 -0
  24. package/dist/responsiveness-probe-MA4M2QM4.js +158 -0
  25. package/dist/responsiveness-probe-MA4M2QM4.js.map +1 -0
  26. package/package.json +3 -2
  27. package/dist/chunk-HR5T2RQF.js.map +0 -1
  28. package/dist/chunk-I2OTQJH3.js.map +0 -1
  29. package/dist/chunk-MQJ5DMPT.js.map +0 -1
  30. package/dist/chunk-SXBZDUKN.js.map +0 -1
  31. package/dist/responsiveness-probe-B6LJJRUD.js +0 -74
  32. package/dist/responsiveness-probe-B6LJJRUD.js.map +0 -1
  33. /package/dist/{claude-pair-runtime-F6USL3EW.js.map → claude-pair-runtime-GIUCD7IG.js.map} +0 -0
  34. /package/dist/{claude-scheduler-EM24LTGV.js.map → claude-scheduler-FATCLHDM.js.map} +0 -0
  35. /package/dist/{persistent-session-OL5EDYB4.js.map → daily-session-PNQX5URX.js.map} +0 -0
@@ -8,8 +8,9 @@ import {
8
8
  isParseError,
9
9
  parseDeliveryTarget,
10
10
  registerFramework,
11
+ resolveAvatarEnvUrl,
11
12
  wrapScheduledTaskPrompt
12
- } from "./chunk-I2OTQJH3.js";
13
+ } from "./chunk-WOOYOAPG.js";
13
14
 
14
15
  // ../../packages/core/dist/integrations/registry.js
15
16
  var INTEGRATION_REGISTRY = [
@@ -49,7 +50,13 @@ var INTEGRATION_REGISTRY = [
49
50
  binary: "gh",
50
51
  env_key: "GITHUB_TOKEN",
51
52
  skill_id: "gh-cli",
52
- installer: "brew"
53
+ // ENG-6206: `brew` never installs on the Linux fleet (root-on-AL2023,
54
+ // no Homebrew) — gh was permanently missing. Use an OS-detecting script
55
+ // that installs from GitHub's official repos: dnf (AL2023 / RHEL),
56
+ // apt (Debian / Ubuntu), and brew (macOS hosts). The catalog is the
57
+ // trust boundary — this string is source-controlled, never runtime data.
58
+ installer: "script",
59
+ script: 'if command -v dnf >/dev/null 2>&1; then curl -fsSL https://cli.github.com/packages/rpm/gh-cli.repo -o /etc/yum.repos.d/gh-cli.repo && dnf install -y gh; elif command -v apt-get >/dev/null 2>&1; then curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /usr/share/keyrings/githubcli-archive-keyring.gpg && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && apt-get update && apt-get install -y gh; elif command -v brew >/dev/null 2>&1; then brew install gh; else echo "gh: no supported installer (need dnf, apt-get, or brew)" >&2; exit 1; fi'
53
60
  }
54
61
  },
55
62
  {
@@ -381,6 +388,24 @@ var INTEGRATION_REGISTRY = [
381
388
  }
382
389
  }
383
390
  },
391
+ {
392
+ // ENG-6195: admin-only debugging surface for Integrity Labs STAFF agents.
393
+ // Provisions the @integrity-labs/augmented-admin-mcp stdio broker, which
394
+ // reads end-user agent diagnostics cross-org via /admin/debug/*. `beta` so
395
+ // it is visible/enable-able only by admin-email-domain users; the API
396
+ // double-gates every call on the caller's owning org `is_internal = true`.
397
+ // auth `none` — no end-user OAuth; the host JWT (org_id claim) is the
398
+ // credential. NOT a customer integration; do not promote to `published`.
399
+ id: "augmented-admin",
400
+ name: "Augmented Admin Debug",
401
+ category: "infrastructure",
402
+ description: "Integrity Labs staff-only: cross-org agent/host/integration/alert diagnostics for troubleshooting managed agents.",
403
+ supported_auth_types: ["none"],
404
+ beta: true,
405
+ capabilities: [
406
+ { id: "augmented-admin:read-diagnostics", name: "Read Diagnostics", description: "Cross-org read of agent, host, integration, and alert diagnostics (projection only \u2014 never credentials or transcripts).", access: "read" }
407
+ ]
408
+ },
384
409
  {
385
410
  id: "custom",
386
411
  name: "Custom Integration",
@@ -3361,32 +3386,43 @@ acknowledge before you start.
3361
3386
  - **FAST (< 60s):** handle inline. Reply via the channel tool
3362
3387
  (slack.reply / telegram.reply / directchat.reply) and end your turn.
3363
3388
 
3364
- - **SLOW (\u2265 60s):** acknowledge first, then handle inline.
3389
+ - **SLOW (\u2265 60s):** acknowledge, dispatch to background, stay responsive.
3365
3390
  1. Send a one-line acknowledgement via the channel tool \u2014 short, warm,
3366
3391
  and tell the user you'll come back. Example shape (don't copy verbatim,
3367
3392
  match your voice): "On it \u2014 this'll take a minute or two, I'll ping
3368
3393
  when it's done."
3369
- 2. Do the work yourself in this same session. Use whatever tools you
3370
- need (MCP, skills, file reads, etc.) \u2014 your parent session has the
3371
- full MCP surface bound.
3372
- 3. Reply with the result via the channel tool (\`slack.reply\` /
3373
- \`telegram.reply\` / \`directchat.reply\`), addressing the same thread
3374
- / chat / conversation you acknowledged in step 1.
3375
-
3376
- > **Why inline and not sub-agent dispatch right now:** there is an
3377
- > upstream Claude Code bug
3378
- > ([anthropics/claude-code#64909](https://github.com/anthropics/claude-code/issues/64909))
3379
- > where sub-agents dispatched via the Task tool with an explicit
3380
- > \`tools:\` allowlist (which is the shape \`channel-message-handler\`
3381
- > uses) get an **empty MCP tool registry** \u2014 every \`mcp__*\` call
3382
- > returns "No such tool available", including the channel reply tools.
3383
- > Dispatching a slow channel reply to \`channel-message-handler\` will
3384
- > therefore silently fail to land: you'd post the one-line ack, the
3385
- > sub-agent would do the analysis fine, but its \`slack.reply\` call
3386
- > would error and the user would never see the substantive reply.
3387
- > Empirically confirmed 2026-06-03 with a 6-tool probe: 0/6 MCP tools
3388
- > bound inside \`channel-message-handler\`. Until Anthropic ships the
3389
- > fix, handling slow channel replies inline is the only working path.
3394
+ 2. Dispatch the work to a background sub-agent: the Agent tool with
3395
+ \`subagent_type: general-purpose\` AND \`run_in_background: true\`.
3396
+ Put EVERYTHING the worker needs in the dispatch prompt \u2014 the user's
3397
+ full request, relevant thread context, which integrations/tools to
3398
+ use, and the exact shape of result you want back. The worker inherits
3399
+ your full MCP tool surface but NOT your conversation context.
3400
+ 3. End your turn after dispatching. This is the point: you stay free to
3401
+ answer other messages while the worker grinds. Do NOT wait, poll, or
3402
+ dispatch the same work synchronously \u2014 a synchronous dispatch blocks
3403
+ your turn and defeats the purpose.
3404
+ 4. When the worker's completion notification arrives (it lands
3405
+ automatically as a system message), read its result and reply via the
3406
+ channel tool (\`slack.reply\` / \`telegram.reply\` / \`directchat.reply\`)
3407
+ to the same thread / chat / conversation you acknowledged in step 1.
3408
+ **You post the substantive reply \u2014 the worker has no channel tools.**
3409
+ 5. If the completion notification reports a failure or an unusable
3410
+ result, tell the user what happened and either re-dispatch with a
3411
+ better prompt or handle it inline \u2014 never go silent.
3412
+
3413
+ > **Why background dispatch (and why \`general-purpose\`):** validated
3414
+ > 2026-06-10 on Claude Code 2.1.170 (ENG-6274 spike,
3415
+ > \`docs/spikes/ENG-6274-run-in-background-dispatch.md\`): background
3416
+ > dispatch returns immediately, your turn ends, inbound messages get
3417
+ > answered in seconds while the worker runs, and the completion arrives
3418
+ > as a notification you handle like any other turn. Use
3419
+ > \`subagent_type: general-purpose\` (inherit-all tools) \u2014 NOT
3420
+ > \`channel-message-handler\` or \`augmented-worker\` \u2014 until ENG-6273
3421
+ > re-verifies on this host that the upstream allowlist bug
3422
+ > ([anthropics/claude-code#64909](https://github.com/anthropics/claude-code/issues/64909),
3423
+ > empirically 0/6 MCP tools in named sub-agents on 2026-06-03) is fixed
3424
+ > at the fleet's pinned Claude Code version. \`general-purpose\` escapes
3425
+ > that bug by construction either way.
3390
3426
 
3391
3427
  **Why this triage decision still matters more than any other instruction
3392
3428
  below:** if you skip the acknowledgement and just dive into slow work
@@ -3408,25 +3444,29 @@ For background tool work that **isn't** a channel reply \u2014 multi-step data
3408
3444
  pulls, CRM enrichments, research workflows, cross-MCP orchestration \u2014 use
3409
3445
  \`subagent_type: general-purpose\` (Anthropic's built-in). It inherits the
3410
3446
  full MCP tool surface from this session and reliably binds every
3411
- \`mcp__*\` server you have available.
3447
+ \`mcp__*\` server you have available. For anything expected to take more
3448
+ than a minute, add \`run_in_background: true\` so your turn ends and you
3449
+ stay responsive; the completion notification brings you the result.
3450
+ Don't background-dispatch trivial work \u2014 each dispatch is a fresh
3451
+ context and costs real tokens.
3412
3452
 
3413
3453
  **Why not \`augmented-worker\` for now:** there is an upstream Claude Code
3414
3454
  bug ([anthropics/claude-code#64909](https://github.com/anthropics/claude-code/issues/64909))
3415
3455
  where sub-agents with an explicit \`tools:\` allowlist get an empty MCP
3416
3456
  tool registry \u2014 every \`mcp__*\` call returns "No such tool available."
3417
3457
  \`general-purpose\` uses \`tools: *\` (inherit-all) and escapes the bug.
3418
- Once Anthropic ships the fix, \`augmented-worker\` becomes preferred again
3419
- (restricted tool surface for safety + working MCP binding); the dispatch
3420
- recommendation here will flip back automatically.
3458
+ The fix appears to have shipped upstream (verified locally on Claude Code
3459
+ 2.1.170, 2026-06-10 \u2014 ENG-6269 spike); once ENG-6273 re-verifies it on
3460
+ this host's pinned version, \`augmented-worker\` becomes preferred again
3461
+ (restricted tool surface for safety + working MCP binding) and
3462
+ \`channel-message-handler\` returns as a dispatch target. Until then,
3463
+ \`general-purpose\` only.
3421
3464
 
3422
3465
  For slow **channel** replies, see \xA7 FIRST ACTION above \u2014 those are
3423
- currently handled inline (not dispatched) because \`channel-message-handler\`
3424
- shares the explicit-allowlist shape and so suffers the same upstream
3425
- bug. Empirically confirmed 2026-06-03 on agt-aws-1 with a 6-tool probe:
3426
- 0/6 MCP tools bound inside \`channel-message-handler\` (matching the
3427
- \`augmented-worker\` result). When Anthropic ships the upstream fix, both
3428
- named sub-agents will work again and the FIRST ACTION triage will switch
3429
- back to dispatch.
3466
+ dispatched as background \`general-purpose\` workers and **you** post the
3467
+ result back to the channel when the completion notification arrives
3468
+ (\`channel-message-handler\` is not yet the dispatch target for the same
3469
+ ENG-6273-pending reason).
3430
3470
 
3431
3471
  ${activeTasksSection}${personalitySection}## Identity
3432
3472
 
@@ -3635,8 +3675,15 @@ ask the question alone, no task created yet, and let the user answer before
3635
3675
  anything goes on the board.
3636
3676
 
3637
3677
  When asked about existing work, tasks, or what you've been doing \u2014 call kanban.list
3638
- first to load your recent board state. This gives you context about completed and
3639
- in-progress items so you can answer accurately.
3678
+ first to load your recent board state (active items + the last 24h of completed work).
3679
+
3680
+ **But kanban.list is recency-windowed: done cards older than 24h are NOT on it** (only done cards age off \u2014 failed and active cards always show).
3681
+ So if someone references specific past work ("you drafted X for me", "did you finish Y
3682
+ on the weekend?"), do NOT answer from the board alone \u2014 run **kanban.search** for it
3683
+ first. "It's not on my board" only means it's older than 24h, not that you never did it.
3684
+ Each search hit includes the card's result, so you can recite what you actually
3685
+ produced. Denying delivered work because it aged off the board is a serious failure of
3686
+ trust \u2014 search before you say "no record".
3640
3687
 
3641
3688
  ${memorySection}
3642
3689
  ${reportsToSection}${teamSection}${peopleSection}${multiAgentSection}${integrationsSection}${capabilityPromptSection}${knowledgeSection}${kanbanWorkPolicySection}${skillAuthoringSection}## Dashboards
@@ -4633,6 +4680,136 @@ function provisionIsolationHook(codeName) {
4633
4680
  settings["hooks"] = hooks;
4634
4681
  writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4635
4682
  }
4683
+ function provisionAutoKanbanProgressHook(codeName) {
4684
+ const projectDir = getProjectDir(codeName);
4685
+ const claudeDir = join4(projectDir, ".claude");
4686
+ mkdirSync4(claudeDir, { recursive: true });
4687
+ const hookScriptPath = join4(claudeDir, "agt-auto-kanban-progress-hook.sh");
4688
+ const hookScript = `#!/usr/bin/env bash
4689
+ # Auto-generated by Augmented (ENG-6179 / ENG-6241) \u2014 PostToolUse auto-progress.
4690
+ # Maps the agent's latest tool action onto its active in-thread kanban progress
4691
+ # card. Best-effort, silent, throttled; every path degrades to exit 0 and the
4692
+ # push is fire-and-forget so it never blocks or fails a tool call.
4693
+ # Canonical source: packages/claudecode-plugin-augmented/hooks/auto-kanban-progress.sh
4694
+
4695
+ # Best-effort: any unexpected error just exits clean.
4696
+ trap 'exit 0' ERR
4697
+
4698
+ # Hook input (stdin canonical; CLAUDE_HOOK_INPUT kept as a fallback).
4699
+ INPUT="$(cat 2>/dev/null || true)"
4700
+ [ -n "$INPUT" ] || INPUT="\${CLAUDE_HOOK_INPUT:-}"
4701
+ [ -n "$INPUT" ] || exit 0
4702
+ command -v jq >/dev/null 2>&1 || exit 0
4703
+ command -v curl >/dev/null 2>&1 || exit 0
4704
+
4705
+ TOOL="$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || true)"
4706
+ [ -n "$TOOL" ] || exit 0
4707
+
4708
+ # Skip tools that aren't "work the requester wants narrated". kanban_* (incl.
4709
+ # the explicit kanban_progress) and channel/status tools would only produce
4710
+ # noise \u2014 and a kanban_done clears the step anyway.
4711
+ case "$TOOL" in
4712
+ *kanban*|*reply*|*ask_user*|*send_message*|*direct_chat*|status_*|TodoWrite|ExitPlanMode) exit 0 ;;
4713
+ esac
4714
+
4715
+ # Identity + endpoint.
4716
+ PROJECT_DIR="\${CLAUDE_PROJECT_DIR:-$PWD}"
4717
+ AGENT_ID="\${AGT_AGENT_ID:-}"
4718
+ if [ -z "$AGENT_ID" ] && [ -f "$PROJECT_DIR/.mcp.json" ]; then
4719
+ # The manager writes AGT_AGENT_ID literally into every augmented MCP server's
4720
+ # env block; take the first non-empty one.
4721
+ AGENT_ID="$(jq -r '[.mcpServers[]?.env?.AGT_AGENT_ID // empty] | map(select(. != "")) | .[0] // empty' "$PROJECT_DIR/.mcp.json" 2>/dev/null || true)"
4722
+ fi
4723
+ HOST="\${AGT_HOST:-}"
4724
+ KEY="\${AGT_API_KEY:-}"
4725
+ [ -n "$AGENT_ID" ] && [ -n "$HOST" ] && [ -n "$KEY" ] || exit 0
4726
+
4727
+ # Throttle: at most one push per ~15s per agent.
4728
+ STATE_DIR="\${HOME:-/tmp}/.augmented/.auto-progress/\${AGENT_ID}"
4729
+ mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
4730
+ STAMP="$STATE_DIR/last-push"
4731
+ NOW="$(date +%s)"
4732
+ if [ -f "$STAMP" ]; then
4733
+ LAST="$(cat "$STAMP" 2>/dev/null || echo 0)"
4734
+ case "$LAST" in ''|*[!0-9]*) LAST=0 ;; esac
4735
+ [ $(( NOW - LAST )) -ge 15 ] || exit 0
4736
+ fi
4737
+
4738
+ # Derive the one-line "what I'm doing right now" step.
4739
+ STEP="$(printf '%s' "$INPUT" | jq -r '
4740
+ def base(p): (p | tostring | ltrimstr("./") | split("/") | last);
4741
+ .tool_name as $t | (.tool_input // {}) as $i |
4742
+ if $t == "Bash" then ($i.description // ("Running: " + (($i.command // "") | tostring | .[0:80])))
4743
+ elif $t == "Edit" then ("Editing " + base($i.file_path // $i.filePath // "a file"))
4744
+ elif $t == "MultiEdit" then ("Editing " + base($i.file_path // $i.filePath // "a file"))
4745
+ elif $t == "Write" then ("Writing " + base($i.file_path // $i.filePath // "a file"))
4746
+ elif $t == "NotebookEdit" then ("Editing " + base($i.notebook_path // "a notebook"))
4747
+ elif $t == "Read" then ("Reading " + base($i.file_path // $i.filePath // "a file"))
4748
+ elif $t == "Grep" then ("Searching for " + (($i.pattern // "") | tostring | .[0:60]))
4749
+ elif $t == "Glob" then ("Finding files: " + (($i.pattern // "") | tostring | .[0:60]))
4750
+ elif $t == "WebSearch" then ("Searching the web: " + (($i.query // "") | tostring | .[0:60]))
4751
+ elif $t == "WebFetch" then ("Fetching " + (($i.url // "") | tostring | .[0:80]))
4752
+ elif ($t == "Task" or $t == "Agent") then ("Working on: " + (($i.description // "a subtask") | tostring | .[0:80]))
4753
+ elif ($t | startswith("mcp__")) then ("Using " + ($t | sub("^mcp__"; "") | gsub("__"; " / ")))
4754
+ else ("Working (" + $t + ")")
4755
+ end
4756
+ ' 2>/dev/null || true)"
4757
+ STEP="$(printf '%s' "$STEP" | tr '\\n\\t' ' ' | sed 's/ */ /g' | cut -c1-240)"
4758
+ [ -n "$STEP" ] || exit 0
4759
+
4760
+ # Stamp BEFORE the network call so a slow API can't unthrottle us.
4761
+ printf '%s' "$NOW" > "$STAMP" 2>/dev/null || true
4762
+
4763
+ # Bearer JWT (exchange tlk_ -> JWT, cached with expiry).
4764
+ TOKEN_FILE="$STATE_DIR/token"
4765
+ TOKEN=""
4766
+ if [ -f "$TOKEN_FILE" ]; then
4767
+ T_EXP="$(jq -r '.exp // 0' "$TOKEN_FILE" 2>/dev/null || echo 0)"
4768
+ case "$T_EXP" in ''|*[!0-9]*) T_EXP=0 ;; esac
4769
+ [ $(( T_EXP - NOW )) -gt 300 ] && TOKEN="$(jq -r '.token // empty' "$TOKEN_FILE" 2>/dev/null || true)"
4770
+ fi
4771
+ if [ -z "$TOKEN" ]; then
4772
+ EX="$(curl -fsS --max-time 5 -X POST "$HOST/host/exchange" \\
4773
+ -H 'Content-Type: application/json' \\
4774
+ -d "{\\"host_key\\":\\"$KEY\\"}" 2>/dev/null || true)"
4775
+ TOKEN="$(printf '%s' "$EX" | jq -r '.token // empty' 2>/dev/null || true)"
4776
+ [ -n "$TOKEN" ] || exit 0
4777
+ EXP_ISO="$(printf '%s' "$EX" | jq -r '.expires_at // empty' 2>/dev/null || true)"
4778
+ # GNU date (Linux hosts) first, then BSD date (macOS dev machines), then a
4779
+ # safe 30-min fallback so a parse miss doesn't poison the token cache.
4780
+ EXP_EPOCH="$(date -d "$EXP_ISO" +%s 2>/dev/null \\
4781
+ || date -j -f '%Y-%m-%dT%H:%M:%SZ' "$EXP_ISO" +%s 2>/dev/null \\
4782
+ || echo $(( NOW + 1800 )))"
4783
+ ( umask 177; printf '{"token":"%s","exp":%s}' "$TOKEN" "$EXP_EPOCH" > "$TOKEN_FILE" 2>/dev/null || true )
4784
+ fi
4785
+
4786
+ # Fire-and-forget the push (never block the agent's tool flow).
4787
+ BODY="$(jq -nc --arg a "$AGENT_ID" --arg s "$STEP" '{agent_id:$a, step:$s}' 2>/dev/null || printf '{"agent_id":"%s","step":"%s"}' "$AGENT_ID" "$STEP")"
4788
+ ( curl -fsS --max-time 4 -X POST "$HOST/host/kanban/auto-progress" \\
4789
+ -H 'Content-Type: application/json' \\
4790
+ -H "Authorization: Bearer $TOKEN" \\
4791
+ -d "$BODY" >/dev/null 2>&1 || true ) &
4792
+
4793
+ exit 0
4794
+ `;
4795
+ writeFileSync5(hookScriptPath, hookScript, { mode: 493 });
4796
+ const settingsPath = join4(claudeDir, "settings.local.json");
4797
+ let settings = {};
4798
+ try {
4799
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
4800
+ } catch {
4801
+ }
4802
+ const hooks = settings["hooks"] ?? {};
4803
+ hooks["PostToolUse"] = [
4804
+ {
4805
+ hooks: [
4806
+ { type: "command", command: hookScriptPath }
4807
+ ]
4808
+ }
4809
+ ];
4810
+ settings["hooks"] = hooks;
4811
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4812
+ }
4636
4813
  function provisionOrientHook(codeName) {
4637
4814
  const projectDir = getProjectDir(codeName);
4638
4815
  const claudeDir = join4(projectDir, ".claude");
@@ -4746,6 +4923,11 @@ var SECRETS_DENY_PERMISSIONS = [
4746
4923
  "Read(**/credentials/**)",
4747
4924
  "Read(**/credentials.json)",
4748
4925
  "Read(**/.aws/**)",
4926
+ // gcloud stores ADC + cached access/refresh tokens under ~/.config/gcloud/
4927
+ // (application_default_credentials.json, credentials.db, access_tokens.db).
4928
+ // Brokered GCP access flows via CLOUDSDK_AUTH_ACCESS_TOKEN env, never these
4929
+ // files, so denying reads here is the GCP analogue of the .aws/ block.
4930
+ "Read(**/.config/gcloud/**)",
4749
4931
  "Read(**/.ssh/**)",
4750
4932
  "Read(**/config/database.yml)",
4751
4933
  "Read(**/config/credentials.json)",
@@ -5018,6 +5200,12 @@ function buildMcpJson(input) {
5018
5200
  // dashboard links) in tool replies. Falls back to NEXT_PUBLIC_APP_URL
5019
5201
  // / AGT_CONSOLE_URL — same chain consoleUrl uses for kanban links.
5020
5202
  AGT_APP_URL: process.env["AGT_APP_URL"] ?? process.env["NEXT_PUBLIC_APP_URL"] ?? process.env["AGT_CONSOLE_URL"] ?? "",
5203
+ // ENG-6229: arms the in-session `request_restart` self-restart tool.
5204
+ // Read at provision time from the manager's env (like AGT_HOST above),
5205
+ // NOT a per-spawn `${...}` template — it's a host-level gate. Empty by
5206
+ // default ⇒ the tool isn't registered at all (ships dark). When an
5207
+ // operator sets it on the host, the next /host/refresh bakes it in.
5208
+ AGT_AGENT_SELF_RESTART_ENABLED: process.env["AGT_AGENT_SELF_RESTART_ENABLED"] ?? "",
5021
5209
  // Include PATH/HOME so the MCP subprocess can resolve binaries
5022
5210
  PATH: process.env["PATH"] ?? "",
5023
5211
  HOME: process.env["HOME"] ?? ""
@@ -5034,6 +5222,7 @@ function buildMcpJson(input) {
5034
5222
  integration
5035
5223
  });
5036
5224
  }
5225
+ const hasXeroBroker = input.integrations?.some((i) => i.definition_id === "xero-broker") ?? false;
5037
5226
  const xeroIntegration = input.integrations?.find((i) => i.definition_id === "xero");
5038
5227
  if (xeroIntegration) {
5039
5228
  const brokerMode = Boolean(xeroIntegration.id);
@@ -5048,6 +5237,8 @@ function buildMcpJson(input) {
5048
5237
  AGT_API_KEY: "${AGT_API_KEY}",
5049
5238
  AGT_AGENT_ID: input.agent.agent_id,
5050
5239
  ...brokerMode ? { AGT_INTEGRATION_ID: xeroIntegration.id } : {},
5240
+ // ENG-4922: writes go through xero-broker → read-only vendor server.
5241
+ ...hasXeroBroker ? { XERO_WRITES_VIA_BROKER: "true" } : {},
5051
5242
  PATH: process.env["PATH"] ?? "",
5052
5243
  HOME: process.env["HOME"] ?? ""
5053
5244
  }
@@ -5086,6 +5277,34 @@ function buildMcpJson(input) {
5086
5277
  }
5087
5278
  };
5088
5279
  }
5280
+ if (hasXeroBroker) {
5281
+ mcpServers["xero-broker"] = {
5282
+ command: "npx",
5283
+ args: ["-y", "@integrity-labs/xero-broker@latest"],
5284
+ env: {
5285
+ AGT_HOST: "${AGT_HOST}",
5286
+ AGT_AGENT_ID: input.agent.agent_id,
5287
+ AGT_RUN_ID: "${AGT_RUN_ID}",
5288
+ AGT_API_KEY: "${AGT_API_KEY}",
5289
+ PATH: process.env["PATH"] ?? "",
5290
+ HOME: process.env["HOME"] ?? ""
5291
+ }
5292
+ };
5293
+ }
5294
+ const hasAdminDebug = input.integrations?.some((i) => i.definition_id === "augmented-admin") ?? false;
5295
+ if (hasAdminDebug) {
5296
+ const localAdminMcpPath = join4(getHomeDir3(), ".augmented", "_mcp", "augmented-admin.js");
5297
+ mcpServers["augmented-admin"] = {
5298
+ command: "node",
5299
+ args: [localAdminMcpPath],
5300
+ env: {
5301
+ AGT_HOST: "${AGT_HOST}",
5302
+ AGT_AGENT_ID: input.agent.agent_id,
5303
+ AGT_RUN_ID: "${AGT_RUN_ID}",
5304
+ AGT_API_KEY: "${AGT_API_KEY}"
5305
+ }
5306
+ };
5307
+ }
5089
5308
  return { mcpServers };
5090
5309
  }
5091
5310
  function parseIntervalMinutes(scheduleEvery) {
@@ -5545,6 +5764,7 @@ ${sections}`
5545
5764
  }
5546
5765
  });
5547
5766
  const localSlackChannel = join4(getHomeDir3(), ".augmented", "_mcp", "slack-channel.js");
5767
+ const slackAvatarEnvUrl = resolveAvatarEnvUrl(options?.agentAvatarUrl).url;
5548
5768
  const slackEntry = {
5549
5769
  command: existsSync5(localSlackChannel) ? "node" : "npx",
5550
5770
  args: existsSync5(localSlackChannel) ? [localSlackChannel] : ["-y", "@augmented/claude-code-channel-slack"],
@@ -5562,6 +5782,13 @@ ${sections}`
5562
5782
  ...slackPeerEnv,
5563
5783
  ...slackAgtAuthEnv,
5564
5784
  ...tzEnv,
5785
+ // ENG-6155: the agent's avatar URL (public 512×512 JPG). The
5786
+ // slack-channel MCP sets it as the bot's Slack profile photo on
5787
+ // first connect (users.setPhoto). Omitted when absent so the bot
5788
+ // keeps its current photo; the URL's cache-bust param changes on
5789
+ // re-generation, so the MCP re-applies a new avatar.
5790
+ // ENG-6245: slackAvatarEnvUrl is null for data-URI / oversized values.
5791
+ ...slackAvatarEnvUrl ? { SLACK_AGENT_AVATAR_URL: slackAvatarEnvUrl } : {},
5565
5792
  // ENG-4940: channel-agnostic peer kill switch — same enum
5566
5793
  // as the Telegram path emits above. The Slack classifier
5567
5794
  // (ENG-4936) honours PEER_DISABLED with identical
@@ -5717,6 +5944,7 @@ ${sections}`
5717
5944
  ...appToken ? { SLACK_APP_TOKEN: appToken } : {}
5718
5945
  }
5719
5946
  });
5947
+ const slackAvatarEnvUrl = resolveAvatarEnvUrl(options?.agentAvatarUrl).url;
5720
5948
  if (isPersistent && existsSync5(localSlackChannel)) {
5721
5949
  mcpServers["slack"] = {
5722
5950
  command: "node",
@@ -5730,7 +5958,11 @@ ${sections}`
5730
5958
  ...oneshotBlockKitEnv,
5731
5959
  ...slackPeerEnv,
5732
5960
  ...slackAgtAuthEnv,
5733
- ...tzEnv
5961
+ ...tzEnv,
5962
+ // ENG-6155: avatar URL → bot Slack profile photo (see persistent
5963
+ // branch above). Mirrored here so oneshot-mode agents get it too.
5964
+ // ENG-6245: slackAvatarEnvUrl is null for data-URI / oversized values.
5965
+ ...slackAvatarEnvUrl ? { SLACK_AGENT_AVATAR_URL: slackAvatarEnvUrl } : {}
5734
5966
  }
5735
5967
  };
5736
5968
  } else {
@@ -5746,7 +5978,11 @@ ${sections}`
5746
5978
  ...oneshotBlockKitEnv,
5747
5979
  ...slackPeerEnv,
5748
5980
  ...slackAgtAuthEnv,
5749
- ...tzEnv
5981
+ ...tzEnv,
5982
+ // ENG-6155: avatar URL → bot Slack profile photo (see persistent
5983
+ // branch above). Mirrored here so oneshot-mode agents get it too.
5984
+ // ENG-6245: slackAvatarEnvUrl is null for data-URI / oversized values.
5985
+ ...slackAvatarEnvUrl ? { SLACK_AGENT_AVATAR_URL: slackAvatarEnvUrl } : {}
5750
5986
  }
5751
5987
  };
5752
5988
  }
@@ -5915,15 +6151,30 @@ ${sections}`
5915
6151
  for (const integration of decryptedIntegrations) {
5916
6152
  const prefix = integration.definition_id.toUpperCase().replace(/[^A-Z0-9]/g, "_");
5917
6153
  const creds = integration.credentials;
6154
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
6155
+ let token;
5918
6156
  if (integration.auth_type === "oauth2") {
5919
- const accessToken = creds.access_token;
5920
- if (accessToken) {
5921
- envUpdates[`${prefix}_ACCESS_TOKEN`] = accessToken;
6157
+ token = creds.access_token;
6158
+ if (token) {
6159
+ envUpdates[`${prefix}_ACCESS_TOKEN`] = token;
5922
6160
  }
5923
6161
  } else if (integration.auth_type === "api_key") {
5924
- const apiKey = creds.api_key;
5925
- if (apiKey) {
5926
- envUpdates[`${prefix}_API_KEY`] = apiKey;
6162
+ token = creds.api_key;
6163
+ if (token) {
6164
+ envUpdates[`${prefix}_API_KEY`] = token;
6165
+ }
6166
+ }
6167
+ if (def?.cli_tool) {
6168
+ const { env_key, extra_env } = def.cli_tool;
6169
+ if (env_key && token && !(env_key in envUpdates)) {
6170
+ envUpdates[env_key] = token;
6171
+ }
6172
+ if (extra_env) {
6173
+ for (const [k, v] of Object.entries(extra_env)) {
6174
+ if (typeof v === "string" && v && !(k in envUpdates)) {
6175
+ envUpdates[k] = v;
6176
+ }
6177
+ }
5927
6178
  }
5928
6179
  }
5929
6180
  if (integration.config) {
@@ -5964,12 +6215,15 @@ ${sections}`
5964
6215
  integration
5965
6216
  }));
5966
6217
  }
6218
+ const hasXeroBroker = integrations.some((i) => i.definition_id === "xero-broker");
5967
6219
  const xeroIntegration = integrations.find((i) => i.definition_id === "xero");
5968
6220
  if (xeroIntegration) {
5969
6221
  const brokerMode = Boolean(agentId && xeroIntegration.id);
5970
6222
  const xeroEnv = {
5971
6223
  ...brokerMode ? {} : { XERO_CLIENT_BEARER_TOKEN: "${XERO_ACCESS_TOKEN}" },
5972
6224
  XERO_TENANT_ID: "${XERO_TENANT_ID}",
6225
+ // ENG-4922: writes go through xero-broker → read-only vendor server.
6226
+ ...hasXeroBroker ? { XERO_WRITES_VIA_BROKER: "true" } : {},
5973
6227
  PATH: process.env["PATH"] ?? "",
5974
6228
  HOME: process.env["HOME"] ?? ""
5975
6229
  };
@@ -6020,12 +6274,48 @@ ${sections}`
6020
6274
  });
6021
6275
  }
6022
6276
  }
6277
+ if (hasXeroBroker) {
6278
+ const brokerAgentId = resolveBrokerAgentId(codeName);
6279
+ if (!brokerAgentId) {
6280
+ process.stderr.write(`[manager-worker] [xero-broker] skipping write for '${codeName}': no real AGT_AGENT_ID available. The full-provision artifact pipeline will recreate this on the next cycle.
6281
+ `);
6282
+ } else {
6283
+ this.writeMcpServer(codeName, "xero-broker", {
6284
+ command: "npx",
6285
+ args: ["-y", "@integrity-labs/xero-broker@latest"],
6286
+ env: {
6287
+ AGT_HOST: "${AGT_HOST}",
6288
+ AGT_API_KEY: "${AGT_API_KEY}",
6289
+ AGT_AGENT_ID: brokerAgentId,
6290
+ AGT_RUN_ID: "${AGT_RUN_ID}",
6291
+ PATH: process.env["PATH"] ?? "",
6292
+ HOME: process.env["HOME"] ?? ""
6293
+ }
6294
+ });
6295
+ }
6296
+ }
6297
+ const hasAdminDebug = integrations.some((i) => i.definition_id === "augmented-admin");
6298
+ if (hasAdminDebug) {
6299
+ const localAdminMcpPath = join4(getHomeDir3(), ".augmented", "_mcp", "augmented-admin.js");
6300
+ this.writeMcpServer(codeName, "augmented-admin", {
6301
+ command: "node",
6302
+ args: [localAdminMcpPath],
6303
+ env: {
6304
+ AGT_HOST: "${AGT_HOST}",
6305
+ AGT_AGENT_ID: resolveBrokerAgentId(codeName) ?? agentId ?? "",
6306
+ AGT_RUN_ID: "${AGT_RUN_ID}",
6307
+ AGT_API_KEY: "${AGT_API_KEY}"
6308
+ }
6309
+ });
6310
+ }
6023
6311
  if (this.removeMcpServer) {
6024
6312
  const nativeMcpKeys = INTEGRATION_REGISTRY.filter((d) => d.nativeMcp !== void 0).map((d) => d.nativeMcp.key ?? d.id);
6025
6313
  const integrationDerivedKeys = /* @__PURE__ */ new Set([
6026
6314
  "xero",
6027
6315
  "postiz",
6028
6316
  "cloud-broker",
6317
+ "xero-broker",
6318
+ "augmented-admin",
6029
6319
  ...nativeMcpKeys,
6030
6320
  ...Object.entries(OAUTH_PROVIDERS).filter(([, provider]) => Boolean(provider.mcpUrl)).map(([id]) => id)
6031
6321
  ]);
@@ -6036,6 +6326,10 @@ ${sections}`
6036
6326
  expectedKeys.add("postiz");
6037
6327
  if (hasCloudBroker)
6038
6328
  expectedKeys.add("cloud-broker");
6329
+ if (hasXeroBroker)
6330
+ expectedKeys.add("xero-broker");
6331
+ if (hasAdminDebug)
6332
+ expectedKeys.add("augmented-admin");
6039
6333
  for (const integration of integrations) {
6040
6334
  const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
6041
6335
  if (def?.nativeMcp) {
@@ -7555,6 +7849,7 @@ export {
7555
7849
  CHANNEL_SECRET_ENV_KEYS,
7556
7850
  provisionStopHook,
7557
7851
  provisionIsolationHook,
7852
+ provisionAutoKanbanProgressHook,
7558
7853
  provisionOrientHook,
7559
7854
  setJsonMode,
7560
7855
  isJsonMode,
@@ -7586,4 +7881,4 @@ export {
7586
7881
  managerInstallSystemUnitCommand,
7587
7882
  managerUninstallSystemUnitCommand
7588
7883
  };
7589
- //# sourceMappingURL=chunk-SXBZDUKN.js.map
7884
+ //# sourceMappingURL=chunk-R7NKCGWN.js.map