@songsid/agend 2.2.0-alpha.1 → 2.2.0-alpha.3

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 (317) hide show
  1. package/README.md +6 -4
  2. package/README.zh-TW.md +6 -4
  3. package/dist/agent-cli.js +6 -1
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.d.ts +4 -1
  6. package/dist/agent-endpoint.js +82 -14
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-token-header.d.ts +13 -0
  9. package/dist/agent-token-header.js +16 -0
  10. package/dist/agent-token-header.js.map +1 -0
  11. package/dist/atomic-write.d.ts +21 -0
  12. package/dist/atomic-write.js +65 -0
  13. package/dist/atomic-write.js.map +1 -0
  14. package/dist/auth-api.js +2 -0
  15. package/dist/auth-api.js.map +1 -1
  16. package/dist/backend/antigravity.js +4 -3
  17. package/dist/backend/antigravity.js.map +1 -1
  18. package/dist/backend/codex.d.ts +7 -0
  19. package/dist/backend/codex.js +7 -0
  20. package/dist/backend/codex.js.map +1 -1
  21. package/dist/backend/grok.js +1 -1
  22. package/dist/backend/grok.js.map +1 -1
  23. package/dist/backend/kiro.d.ts +7 -0
  24. package/dist/backend/kiro.js +82 -33
  25. package/dist/backend/kiro.js.map +1 -1
  26. package/dist/backend/muse.d.ts +7 -0
  27. package/dist/backend/muse.js +8 -1
  28. package/dist/backend/muse.js.map +1 -1
  29. package/dist/backend/types.d.ts +12 -0
  30. package/dist/backend/types.js.map +1 -1
  31. package/dist/binary-probe.d.ts +80 -0
  32. package/dist/binary-probe.js +182 -0
  33. package/dist/binary-probe.js.map +1 -0
  34. package/dist/cache-analysis.d.ts +92 -0
  35. package/dist/cache-analysis.js +147 -0
  36. package/dist/cache-analysis.js.map +1 -0
  37. package/dist/cache-ledger.d.ts +108 -0
  38. package/dist/cache-ledger.js +230 -0
  39. package/dist/cache-ledger.js.map +1 -0
  40. package/dist/cache-prices.d.ts +32 -0
  41. package/dist/cache-prices.js +73 -0
  42. package/dist/cache-prices.js.map +1 -0
  43. package/dist/cache-scan.d.ts +39 -0
  44. package/dist/cache-scan.js +115 -0
  45. package/dist/cache-scan.js.map +1 -0
  46. package/dist/cache-service.d.ts +131 -0
  47. package/dist/cache-service.js +452 -0
  48. package/dist/cache-service.js.map +1 -0
  49. package/dist/channel/adapters/discord.d.ts +5 -1
  50. package/dist/channel/adapters/discord.js +56 -11
  51. package/dist/channel/adapters/discord.js.map +1 -1
  52. package/dist/channel/adapters/telegram.d.ts +38 -1
  53. package/dist/channel/adapters/telegram.js +82 -0
  54. package/dist/channel/adapters/telegram.js.map +1 -1
  55. package/dist/channel/mcp-tools.js +1 -1
  56. package/dist/channel/mcp-tools.js.map +1 -1
  57. package/dist/channel/tool-router.d.ts +8 -1
  58. package/dist/channel/tool-router.js +4 -2
  59. package/dist/channel/tool-router.js.map +1 -1
  60. package/dist/channel/types.d.ts +21 -0
  61. package/dist/cli-entry.d.ts +19 -0
  62. package/dist/cli-entry.js +36 -0
  63. package/dist/cli-entry.js.map +1 -0
  64. package/dist/cli.js +376 -54
  65. package/dist/cli.js.map +1 -1
  66. package/dist/connection-secrets.d.ts +5 -0
  67. package/dist/connection-secrets.js.map +1 -1
  68. package/dist/cpu-profile.js +50 -0
  69. package/dist/cpu-profile.js.map +1 -1
  70. package/dist/daemon.d.ts +18 -0
  71. package/dist/daemon.js +256 -63
  72. package/dist/daemon.js.map +1 -1
  73. package/dist/delivery-outbox.d.ts +1 -0
  74. package/dist/delivery-outbox.js +12 -0
  75. package/dist/delivery-outbox.js.map +1 -1
  76. package/dist/discord-permissions.d.ts +33 -0
  77. package/dist/discord-permissions.js +42 -0
  78. package/dist/discord-permissions.js.map +1 -0
  79. package/dist/env-file.d.ts +11 -0
  80. package/dist/env-file.js +27 -0
  81. package/dist/env-file.js.map +1 -0
  82. package/dist/event-log.d.ts +4 -1
  83. package/dist/event-log.js +6 -3
  84. package/dist/event-log.js.map +1 -1
  85. package/dist/fleet-context.d.ts +5 -0
  86. package/dist/fleet-context.js.map +1 -1
  87. package/dist/fleet-control-audit.d.ts +5 -0
  88. package/dist/fleet-control-audit.js +7 -0
  89. package/dist/fleet-control-audit.js.map +1 -1
  90. package/dist/fleet-level-config.d.ts +8 -1
  91. package/dist/fleet-level-config.js +53 -0
  92. package/dist/fleet-level-config.js.map +1 -1
  93. package/dist/fleet-manager.d.ts +122 -7
  94. package/dist/fleet-manager.js +871 -152
  95. package/dist/fleet-manager.js.map +1 -1
  96. package/dist/fleet-stop-budget.d.ts +22 -0
  97. package/dist/fleet-stop-budget.js +95 -0
  98. package/dist/fleet-stop-budget.js.map +1 -0
  99. package/dist/full-restart.js +2 -3
  100. package/dist/full-restart.js.map +1 -1
  101. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
  102. package/dist/install-lock.d.ts +49 -0
  103. package/dist/install-lock.js +200 -0
  104. package/dist/install-lock.js.map +1 -0
  105. package/dist/instance-config-impact.js +3 -2
  106. package/dist/instance-config-impact.js.map +1 -1
  107. package/dist/kiro-transcript-lane.d.ts +8 -4
  108. package/dist/kiro-transcript-lane.js +66 -44
  109. package/dist/kiro-transcript-lane.js.map +1 -1
  110. package/dist/locale.js +96 -0
  111. package/dist/locale.js.map +1 -1
  112. package/dist/next-steps.d.ts +5 -0
  113. package/dist/next-steps.js +26 -0
  114. package/dist/next-steps.js.map +1 -0
  115. package/dist/outbound-handlers.js +8 -0
  116. package/dist/outbound-handlers.js.map +1 -1
  117. package/dist/outbound-schemas.d.ts +4 -0
  118. package/dist/outbound-schemas.js +7 -2
  119. package/dist/outbound-schemas.js.map +1 -1
  120. package/dist/outbox-open-error.d.ts +7 -0
  121. package/dist/outbox-open-error.js +15 -0
  122. package/dist/outbox-open-error.js.map +1 -0
  123. package/dist/package-preimage.d.ts +26 -0
  124. package/dist/package-preimage.js +96 -0
  125. package/dist/package-preimage.js.map +1 -0
  126. package/dist/pane-context-cache.d.ts +25 -0
  127. package/dist/pane-context-cache.js +73 -0
  128. package/dist/pane-context-cache.js.map +1 -0
  129. package/dist/paths.d.ts +0 -7
  130. package/dist/paths.js +8 -2
  131. package/dist/paths.js.map +1 -1
  132. package/dist/provider-probe.d.ts +15 -0
  133. package/dist/provider-probe.js +20 -0
  134. package/dist/provider-probe.js.map +1 -1
  135. package/dist/public-link-progress.d.ts +73 -0
  136. package/dist/public-link-progress.js +190 -0
  137. package/dist/public-link-progress.js.map +1 -0
  138. package/dist/public-web-gateway.js +2 -2
  139. package/dist/public-web-gateway.js.map +1 -1
  140. package/dist/public-web-link.d.ts +6 -1
  141. package/dist/public-web-link.js +87 -15
  142. package/dist/public-web-link.js.map +1 -1
  143. package/dist/quickstart-api.d.ts +93 -20
  144. package/dist/quickstart-api.js +194 -62
  145. package/dist/quickstart-api.js.map +1 -1
  146. package/dist/quickstart.d.ts +15 -0
  147. package/dist/quickstart.js +110 -33
  148. package/dist/quickstart.js.map +1 -1
  149. package/dist/reply-buttons-controller.d.ts +98 -0
  150. package/dist/reply-buttons-controller.js +174 -0
  151. package/dist/reply-buttons-controller.js.map +1 -0
  152. package/dist/reply-buttons.d.ts +113 -0
  153. package/dist/reply-buttons.js +208 -0
  154. package/dist/reply-buttons.js.map +1 -0
  155. package/dist/reply-dedup.js +3 -1
  156. package/dist/reply-dedup.js.map +1 -1
  157. package/dist/restart-guard.d.ts +53 -0
  158. package/dist/restart-guard.js +114 -0
  159. package/dist/restart-guard.js.map +1 -0
  160. package/dist/scheduler/db.js +6 -2
  161. package/dist/scheduler/db.js.map +1 -1
  162. package/dist/service-activation.d.ts +62 -7
  163. package/dist/service-activation.js +237 -55
  164. package/dist/service-activation.js.map +1 -1
  165. package/dist/service-installer.d.ts +56 -1
  166. package/dist/service-installer.js +152 -12
  167. package/dist/service-installer.js.map +1 -1
  168. package/dist/service-plan.d.ts +65 -0
  169. package/dist/service-plan.js +108 -0
  170. package/dist/service-plan.js.map +1 -0
  171. package/dist/settings-api.js +92 -10
  172. package/dist/settings-api.js.map +1 -1
  173. package/dist/settings-authority.d.ts +11 -0
  174. package/dist/settings-authority.js +128 -0
  175. package/dist/settings-authority.js.map +1 -0
  176. package/dist/settings-change.d.ts +5 -0
  177. package/dist/settings-change.js +37 -8
  178. package/dist/settings-change.js.map +1 -1
  179. package/dist/settings-confirmation.d.ts +3 -0
  180. package/dist/settings-confirmation.js +7 -1
  181. package/dist/settings-confirmation.js.map +1 -1
  182. package/dist/settings-effect.js +19 -7
  183. package/dist/settings-effect.js.map +1 -1
  184. package/dist/settings-http-confirmation.js +1 -1
  185. package/dist/settings-http-confirmation.js.map +1 -1
  186. package/dist/settings-redaction.d.ts +18 -0
  187. package/dist/settings-redaction.js +58 -0
  188. package/dist/settings-redaction.js.map +1 -0
  189. package/dist/setup-form.d.ts +9 -1
  190. package/dist/setup-form.js +25 -1
  191. package/dist/setup-form.js.map +1 -1
  192. package/dist/setup-host.js +15 -6
  193. package/dist/setup-host.js.map +1 -1
  194. package/dist/setup-wizard.js +27 -19
  195. package/dist/setup-wizard.js.map +1 -1
  196. package/dist/sqlite-open-errors.d.ts +12 -0
  197. package/dist/sqlite-open-errors.js +13 -0
  198. package/dist/sqlite-open-errors.js.map +1 -0
  199. package/dist/systemd-runtime.d.ts +15 -0
  200. package/dist/systemd-runtime.js +41 -0
  201. package/dist/systemd-runtime.js.map +1 -0
  202. package/dist/tmux-control.d.ts +44 -0
  203. package/dist/tmux-control.js +170 -21
  204. package/dist/tmux-control.js.map +1 -1
  205. package/dist/token-env-name.d.ts +37 -0
  206. package/dist/token-env-name.js +93 -0
  207. package/dist/token-env-name.js.map +1 -0
  208. package/dist/tool-permissions.js +5 -3
  209. package/dist/tool-permissions.js.map +1 -1
  210. package/dist/topic-commands.d.ts +7 -16
  211. package/dist/topic-commands.js +56 -85
  212. package/dist/topic-commands.js.map +1 -1
  213. package/dist/transcript-monitor.js +3 -0
  214. package/dist/transcript-monitor.js.map +1 -1
  215. package/dist/transcript-sources.d.ts +68 -0
  216. package/dist/transcript-sources.js +336 -29
  217. package/dist/transcript-sources.js.map +1 -1
  218. package/dist/transcript-turns.d.ts +39 -0
  219. package/dist/transcript-turns.js +58 -0
  220. package/dist/transcript-turns.js.map +1 -0
  221. package/dist/tunnel/cloudflared-install.d.ts +18 -0
  222. package/dist/tunnel/cloudflared-install.js +15 -1
  223. package/dist/tunnel/cloudflared-install.js.map +1 -1
  224. package/dist/turn-reply-guard.d.ts +3 -0
  225. package/dist/turn-reply-guard.js.map +1 -1
  226. package/dist/ui/chat-commands.js +43 -0
  227. package/dist/ui/chat-render.js +34 -8
  228. package/dist/ui/chat-store.js +51 -2
  229. package/dist/ui/chat-strings.js +30 -2
  230. package/dist/ui/chat-thread.js +141 -47
  231. package/dist/ui/first-run.js +64 -0
  232. package/dist/ui/fleet-cache.js +168 -0
  233. package/dist/ui/html-attachment.js +26 -0
  234. package/dist/ui/panel-chat.js +117 -23
  235. package/dist/ui/panel-fleet.js +31 -17
  236. package/dist/ui/panel-settings.js +29 -15
  237. package/dist/ui/preview-panel.js +237 -0
  238. package/dist/ui/preview.js +7 -3
  239. package/dist/ui/settings-apply.js +3 -1
  240. package/dist/ui/settings-dialogs.js +95 -25
  241. package/dist/ui/settings-discord.js +130 -0
  242. package/dist/ui/settings-model.js +21 -0
  243. package/dist/ui/settings-strings.js +50 -8
  244. package/dist/ui/settings-token.js +54 -0
  245. package/dist/ui/settings-wizard.js +63 -29
  246. package/dist/ui/shared/app-route.js +1 -1
  247. package/dist/ui/shared/app-shell.js +25 -37
  248. package/dist/ui/shared/app-store.js +5 -3
  249. package/dist/ui/shared/app-stream.js +2 -1
  250. package/dist/ui/shared/app.css +181 -5
  251. package/dist/ui/shared/app.js +2 -1
  252. package/dist/ui/shared/instance-nav.js +310 -0
  253. package/dist/ui/shared/panel-view.js +12 -190
  254. package/dist/ui/shared/signin.js +13 -1
  255. package/dist/ui/shared/ui-icons.js +2 -0
  256. package/dist/ui/shared/view-roster-store.js +5 -0
  257. package/dist/ui/shared/view-strings.js +12 -0
  258. package/dist/ui/signin.html +2 -1
  259. package/dist/update-dispatch.d.ts +28 -0
  260. package/dist/update-dispatch.js +73 -0
  261. package/dist/update-dispatch.js.map +1 -0
  262. package/dist/update-install.d.ts +78 -8
  263. package/dist/update-install.js +158 -30
  264. package/dist/update-install.js.map +1 -1
  265. package/dist/update-launch.d.ts +22 -0
  266. package/dist/update-launch.js +106 -0
  267. package/dist/update-launch.js.map +1 -0
  268. package/dist/update-marker.js +3 -8
  269. package/dist/update-marker.js.map +1 -1
  270. package/dist/usage/format-rich.js +7 -1
  271. package/dist/usage/format-rich.js.map +1 -1
  272. package/dist/usage/i18n-keys.d.ts +1 -1
  273. package/dist/usage/i18n-keys.js +1 -1
  274. package/dist/usage/i18n-keys.js.map +1 -1
  275. package/dist/usage/providers.d.ts +4 -0
  276. package/dist/usage/providers.js +2 -1
  277. package/dist/usage/providers.js.map +1 -1
  278. package/dist/usage/usage-api.js +48 -2
  279. package/dist/usage/usage-api.js.map +1 -1
  280. package/dist/view-api.d.ts +40 -0
  281. package/dist/view-api.js +28 -10
  282. package/dist/view-api.js.map +1 -1
  283. package/dist/web-api.d.ts +22 -0
  284. package/dist/web-api.js +115 -16
  285. package/dist/web-api.js.map +1 -1
  286. package/dist/web-auth.d.ts +8 -0
  287. package/dist/web-auth.js +34 -8
  288. package/dist/web-auth.js.map +1 -1
  289. package/dist/web-chat-history.d.ts +21 -0
  290. package/dist/web-chat-history.js +44 -0
  291. package/dist/web-chat-history.js.map +1 -1
  292. package/dist/web-commands.d.ts +77 -0
  293. package/dist/web-commands.js +81 -0
  294. package/dist/web-commands.js.map +1 -0
  295. package/dist/web-host-guard.d.ts +18 -2
  296. package/dist/web-host-guard.js +30 -5
  297. package/dist/web-host-guard.js.map +1 -1
  298. package/dist/web-session.d.ts +7 -1
  299. package/dist/web-session.js +23 -2
  300. package/dist/web-session.js.map +1 -1
  301. package/dist/web-shell-routes.d.ts +1 -1
  302. package/dist/web-shell-routes.js +1 -1
  303. package/dist/web-shell-routes.js.map +1 -1
  304. package/launcher/agend +110 -0
  305. package/launcher/agend-agent +110 -0
  306. package/launcher/agend-agent.cjs +4 -0
  307. package/launcher/agend.cjs +4 -0
  308. package/launcher/install-admission.cjs +105 -0
  309. package/launcher/launch.cjs +64 -0
  310. package/launcher/postinstall.cjs +118 -0
  311. package/launcher/run-in-process.cjs +13 -0
  312. package/launcher/runtime-platform.cjs +86 -0
  313. package/launcher/runtime-select.cjs +217 -0
  314. package/package.json +12 -4
  315. package/scripts/preinstall-guard.cjs +40 -21
  316. package/templates/launchd.plist.ejs +2 -2
  317. package/templates/systemd.service.ejs +4 -4
@@ -0,0 +1,93 @@
1
+ /**
2
+ * #1519 P1 (docs/design/ux-onboarding-walkthrough.md §5.2): the env var that holds a connection's bot token is
3
+ * generated, never asked for — `AGEND_<PLATFORM>_<ID>_TOKEN`, upper-cased, the id reduced to [A-Z0-9_], a numeric
4
+ * suffix on collision. A name is never one another connection holds, a provider key, a reserved shell name, or a key
5
+ * already in the data dir's .env or this process's environment: a generated name only ever names a new value.
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { isReservedProviderEnvKey, providerRegistryEnvKeys } from "./provider-secret-registry.js";
10
+ import { parseEnvText } from "./env-file.js";
11
+ /** What a token env name must look like (fleet.yaml and the wizard). */
12
+ export const TOKEN_ENV_PATTERN = /^[A-Z][A-Z0-9_]{2,63}$/;
13
+ const sanitize = (s) => s.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
14
+ /** `AGEND_<PLATFORM>_TOKEN` for the platform's own id, else `AGEND_<PLATFORM>_<ID>_TOKEN` (a leading "<platform>-" dropped). */
15
+ export function tokenEnvBase(platform, channelId) {
16
+ const p = sanitize(platform) || "BOT";
17
+ const lowerId = channelId.toLowerCase(), lowerP = platform.toLowerCase();
18
+ const rest = lowerId === lowerP ? "" : sanitize(lowerId.startsWith(`${lowerP}-`) ? channelId.slice(platform.length + 1) : channelId);
19
+ const middle = rest ? `_${rest}` : "";
20
+ // AGEND_ + P + middle + _TOKEN must fit 64 characters (with room for a "_99" suffix).
21
+ const room = 64 - "AGEND_".length - p.length - "_TOKEN".length - 4;
22
+ return `AGEND_${p}${middle.slice(0, Math.max(0, room)).replace(/_+$/, "")}_TOKEN`;
23
+ }
24
+ /** The base name, or with `_2`, `_3`… before `_TOKEN` until it is free. */
25
+ export function generateTokenEnvName(platform, channelId, taken) {
26
+ const base = tokenEnvBase(platform, channelId);
27
+ const free = (name) => !taken.has(name) && !isReservedProviderEnvKey(name);
28
+ if (free(base))
29
+ return base;
30
+ const stem = base.slice(0, -"_TOKEN".length);
31
+ for (let n = 2; n < 10_000; n++) {
32
+ const candidate = `${stem}_${n}_TOKEN`;
33
+ if (free(candidate))
34
+ return candidate;
35
+ }
36
+ throw new Error("no free token env name");
37
+ }
38
+ /** The .env could not be read, so which names it holds is unknown — never taken as "none" (#1529 review). */
39
+ export class EnvFileUnreadableError extends Error {
40
+ constructor(cause) { super(`the data dir's .env cannot be read: ${cause?.message ?? String(cause)}`); }
41
+ }
42
+ /** The names already in the data dir's .env (names only). No file: none. A file that cannot be read throws. */
43
+ export function envFileKeys(dataDir) {
44
+ let text;
45
+ try {
46
+ text = readFileSync(join(dataDir, ".env"), "utf-8");
47
+ }
48
+ catch (err) {
49
+ if (err?.code === "ENOENT")
50
+ return new Set();
51
+ throw new EnvFileUnreadableError(err);
52
+ }
53
+ return new Set(parseEnvText(text).keys()); // the same reading as the fleet's (env-file.ts)
54
+ }
55
+ /**
56
+ * Why a GENERATED name (#1519 P1) can no longer be written, or null. A generated name only ever names a new value, so
57
+ * at the write it must still be free of everything the plan avoided — another connection, a provider or reserved name,
58
+ * a key in .env (read under the caller's lease), one in this process's environment. Throws when .env is unreadable.
59
+ */
60
+ export function generatedTokenEnvStale(name, opts) {
61
+ const taken = takenTokenEnvNames({ channelEnvs: opts.channelEnvs, envFile: envFileKeys(opts.dataDir), processEnv: opts.processEnv ?? Object.keys(process.env) });
62
+ if (taken.has(name) || isReservedProviderEnvKey(name))
63
+ return `${name} is no longer free (it was taken after this setup was planned) — plan it again`;
64
+ return null;
65
+ }
66
+ /** Every name a new connection's token must not take. */
67
+ export function takenTokenEnvNames(opts) {
68
+ const taken = new Set(providerRegistryEnvKeys());
69
+ for (const name of opts.channelEnvs)
70
+ if (name)
71
+ taken.add(name);
72
+ for (const name of opts.envFile ?? [])
73
+ taken.add(name);
74
+ for (const name of opts.processEnv ?? [])
75
+ taken.add(name);
76
+ return taken;
77
+ }
78
+ /**
79
+ * Why `name` cannot be the token env of a NEW connection, or null. Only the names that would make it another holder's
80
+ * value: another connection's, a provider key, a reserved shell name (a key merely present in .env is allowed when a
81
+ * caller names it explicitly — the pre-fleet setup form and the CLI do).
82
+ */
83
+ export function newTokenEnvConflict(name, channels) {
84
+ if (!TOKEN_ENV_PATTERN.test(name))
85
+ return "token_env must be an UPPER_SNAKE env var name";
86
+ if (isReservedProviderEnvKey(name) || providerRegistryEnvKeys().has(name))
87
+ return `${name} is reserved`;
88
+ const owner = channels.find(channel => channel.token_env === name);
89
+ if (owner)
90
+ return `${name} already holds the token of the "${owner.id}" connection — a new connection never replaces one; use Replace token on that connection`;
91
+ return null;
92
+ }
93
+ //# sourceMappingURL=token-env-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-env-name.js","sourceRoot":"","sources":["../src/token-env-name.ts"],"names":[],"mappings":"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"}
@@ -15,13 +15,15 @@
15
15
  * — not only the ones in cli mode.
16
16
  *
17
17
  * Which is why keeping a tool out of the schema is a way to spend fewer tokens
18
- * and get a clearer error, and is not a control. The control is this module,
18
+ * and get a clearer error, and is not an authorization check. The policy check is this module,
19
19
  * called at the points where those four paths converge: the fleet's IPC
20
20
  * dispatch and the agent endpoint. See
21
21
  * `docs/design/coordinator-tool-perms.zh-TW.md`.
22
22
  *
23
- * Stage 1 wires the callers and records what they would have decided. Nothing
24
- * here denies anything yet.
23
+ * This policy is enforced for the identity resolved by the IPC socket or token.
24
+ * Same-uid agents with a shell can read a sibling's agent.token or connect to
25
+ * its channel.sock and assume that identity. A tool profile is policy protection,
26
+ * not an isolation boundary against those agents; that requires OS isolation.
25
27
  */
26
28
  import { TOOLS } from "./channel/mcp-tools.js";
27
29
  /** Everything an agent that is doing the work needs, and nothing that runs the fleet. */
@@ -1 +1 @@
1
- {"version":3,"file":"tool-permissions.js","sourceRoot":"","sources":["../src/tool-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAI/C,yFAAyF;AACzF,MAAM,MAAM,GAAsB;IAChC,qBAAqB;IACrB,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,qBAAqB;IACvD,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,gDAAgD;IAChD,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB;IAC3G,4DAA4D;IAC5D,gBAAgB,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa;IAClE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB;IAC5G,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB;IACzE,kBAAkB;IAClB,MAAM,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB;IACpH,eAAe,EAAE,kBAAkB;IACnC,0EAA0E;IAC1E,uEAAuE;IACvE,6EAA6E;IAC7E,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB;IACvD,gFAAgF;IAChF,eAAe,EAAE,cAAc;CAChC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,aAAa,GAAsB;IACvC,iBAAiB,EAAE,iBAAiB,EAAE,kBAAkB;IACxD,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB;IACxF,iBAAiB,EAAE,qBAAqB;IACxC,aAAa,EAAE,aAAa,EAAE,aAAa;IAC3C,uBAAuB,EAAE,wBAAwB;IACjD,+EAA+E;IAC/E,iBAAiB;CAClB,CAAC;AAEF,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAqD;IAC7E,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,MAAM;IACd,WAAW,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;IAC1C,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,qBAAqB;QACvD,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa;QACjH,qFAAqF;QACrF,oBAAoB;QACpB,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB;QAC3G,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,eAAe;QACxE,MAAM,EAAE,gBAAgB,EAAE,eAAe;QACzC,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB;QACzE,0EAA0E;QAC1E,gEAAgE;QAChE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB;QACpD,eAAe,EAAE,kBAAkB;KACpC;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,OAAO,EAAE,cAAc;QAChC,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB;QACzF,gBAAgB,EAAE,eAAe,EAAE,MAAM,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB;QACtI,eAAe,EAAE,kBAAkB;QACnC,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,oBAAoB;QAC/H,gDAAgD;QAChD,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB;KAC1E;IACD,2EAA2E;IAC3E,8EAA8E;IAC9E,wEAAwE;IACxE,kEAAkE;IAClE,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,CAAC;CACzK,CAAC;AAEF,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CACrC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CACZ,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAoB;IAC3C,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,IAAY;IAC3D,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAkE,EAClE,IAAY,EACZ,eAA4B,QAAQ;IAEpC,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAC;IAClC,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,MAAM,EAAE,aAAa,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3E,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAyB;IAC7D,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACxG,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAqC;IAC9D,qBAAqB,EAAE,iBAAiB;IACxC,mBAAmB,EAAE,gBAAgB;IACrC,qBAAqB,EAAE,iBAAiB;IACxC,qBAAqB,EAAE,iBAAiB;IACxC,qBAAqB,EAAE,eAAe;IACtC,mBAAmB,EAAE,gBAAgB;IACrC,qBAAqB,EAAE,iBAAiB;IACxC,UAAU,EAAE,MAAM;IAClB,sBAAsB,EAAE,kBAAkB;IAC1C,qBAAqB,EAAE,iBAAiB;IACxC,iBAAiB,EAAE,aAAa;IAChC,uBAAuB,EAAE,mBAAmB;IAC5C,oBAAoB,EAAE,gBAAgB;IACtC,mBAAmB,EAAE,eAAe;IACpC,sBAAsB,EAAE,kBAAkB;CAC3C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAqC;IACpE,iBAAiB,EAAE,iBAAiB;IACpC,eAAe,EAAE,gBAAgB;IACjC,iBAAiB,EAAE,iBAAiB;IACpC,iBAAiB,EAAE,iBAAiB;IACpC,0EAA0E;IAC1E,yEAAyE;IACzE,eAAe,EAAE,eAAe;IAChC,iBAAiB,EAAE,eAAe;IAClC,eAAe,EAAE,gBAAgB;IACjC,iBAAiB,EAAE,iBAAiB;IACpC,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,WAAW;IAClB,MAAM,EAAE,kBAAkB;IAC1B,iBAAiB,EAAE,iBAAiB;IACpC,MAAM,EAAE,aAAa;IACrB,eAAe,EAAE,mBAAmB;IACpC,eAAe,EAAE,gBAAgB;IACjC,QAAQ,EAAE,eAAe;IACzB,iBAAiB,EAAE,kBAAkB;CACtC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAoB,EAAE,IAAY;IACnE,OAAO,GAAG,IAAI,yDAAyD,OAAO,mBAAmB,IAAI,6BAA6B;UAC9H,yEAAyE;UACzE,2EAA2E,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,gBAAgB,GAA6B,IAAI,GAAG,CAAc,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;AAI5G;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAoB,EACpB,MAAc,EACd,EAAc,EACd,OAAkG;IAElG,IAAI,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,IAAI,GAAG,GAAG,EAAE,WAAW,CAAC;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;IAC1D,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,SAAS,IAAI,MAAM,CAAC;QACnC,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,sBAAsB,MAAM,GAAG,CAAC,CAAC;IACnH,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,2EAA2E;IAC3E,qCAAqC;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7D,OAAO,sBAAsB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EACjD,QAAQ,CAAC,MAAM,KAAK,MAAM;YACxB,CAAC,CAAC,mCAAmC,QAAQ,CAAC,MAAM,GAAG;YACvD,CAAC,CAAC,sBAAsB,QAAQ,CAAC,MAAM,eAAe,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,EAAE,KAAK,QAAQ,IAAI,SAAS,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzD,OAAO,sBAAsB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,yBAAyB,SAAS,GAAG,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+FAA+F;AAC/F,SAAS,sBAAsB,CAAC,OAAoB,EAAE,IAAY,EAAE,MAAc,EAAE,SAAiB;IACnG,OAAO,GAAG,IAAI,wBAAwB,OAAO,6DAA6D;UACtG,yBAAyB,MAAM,yCAAyC,SAAS,IAAI;UACrF,sFAAsF;UACtF,yCAAyC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"tool-permissions.js","sourceRoot":"","sources":["../src/tool-permissions.ts"],"names":[],"mappings":"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"}
@@ -147,27 +147,18 @@ export declare function scrapePaneContext(instanceName: string, backend: string)
147
147
  context: number | null;
148
148
  tokenRatio: TokenContextRatio | null;
149
149
  };
150
- /** Forget a deleted instance's cached context so the map can't grow forever. */
150
+ /** Forget a deleted instance and revoke any outstanding refresh for its old pane. */
151
151
  export declare function forgetInstanceContext(instanceName: string): void;
152
152
  /**
153
- * Single source of truth for instance context % across /ctx, /status, and View.
154
- * Claude-code prefers statusline.json (authoritative, no TUI scrape); everyone
155
- * else scrapes the live pane with the same parsers /ctx uses.
156
- *
157
- * Non-blocking by default (stale-while-revalidate): a fresh cache entry is
158
- * returned as-is; a stale or missing one is returned immediately anyway while a
159
- * background refresh runs. This used to scrape synchronously with `execFileSync`
160
- * (2s timeout) on a cache miss, and the 12s TTL is LONGER than the dashboard's
161
- * 10s poll — so roughly every other tick did N blocking captures. With ten
162
- * non-claude-code instances and a slow tmux that froze the entire fleet event
163
- * loop for up to 20s per tick: no IPC, no message delivery, no watchdog ping.
164
- * Three open browser tabs ran three independent polls.
165
- *
166
- * Pass `bypassCache` for a synchronous, authoritative read — used by `/ctx`,
167
- * where a user is asking right now and 2s of blocking is the correct trade.
153
+ * Shared context percentage for /ctx, /status and View, using the same pane parsers.
154
+ * Claude Code prefers its authoritative statusline.json.
155
+ * Polled reads return cached values immediately and refresh through the captured
156
+ * owner after the monotonic 8s interval. A missing pane owner returns unknown.
157
+ * Explicit /ctx retains its synchronous authoritative read via bypassCache.
168
158
  */
169
159
  export declare function resolveInstanceContext(dataDir: string, instanceName: string, backend: string, opts?: {
170
160
  bypassCache?: boolean;
161
+ source?: import("./pane-context-cache.js").PaneContextSource | null;
171
162
  }): {
172
163
  context: number | null;
173
164
  tokenRatio: TokenContextRatio | null;
@@ -1,3 +1,4 @@
1
+ import { PaneContextCache } from "./pane-context-cache.js";
1
2
  import { measureSyncWork } from "./sync-work-attribution.js";
2
3
  import { LOOPBACK_HOST_NAMES } from "./web-host-guard.js";
3
4
  import { WEB_REMOTE_DOCS_URL } from "./upgrade-notices.js";
@@ -20,7 +21,9 @@ import { isGeneralInstance } from "./general-instance.js";
20
21
  import { instanceSupportsSteer } from "./steer-capability.js";
21
22
  import { SYSINFO_BACKEND_IDS } from "./backend/types.js";
22
23
  import { recordInternalRequest, withOrigin } from "./fleet-control-audit.js";
23
- import { UPDATE_COMMAND } from "./update-check.js";
24
+ import { resolveInstalledAgend } from "./update-dispatch.js";
25
+ import { resolveUpdateLaunch, watchUpdateLaunch } from "./update-launch.js";
26
+ import { selfCommand } from "./cli-entry.js";
24
27
  export { parseContextPercent, parseTokenContextRatio } from "./context-percent.js";
25
28
  /** Longest one setMyCommands call may take. */
26
29
  const TELEGRAM_COMMANDS_TIMEOUT_MS = 10_000;
@@ -382,60 +385,17 @@ export function scrapePaneContext(instanceName, backend) {
382
385
  return { context: null, tokenRatio: null };
383
386
  }
384
387
  }
385
- const paneContextCache = new Map();
386
- // Below the dashboard's 10s SSE tick on purpose: at the previous 12s the cache was
387
- // guaranteed to be stale on roughly every other tick, which is what made the
388
- // blocking scrape fire so often. Now a tick either hits the cache or triggers a
389
- // background refresh, never a synchronous capture.
390
- const PANE_CONTEXT_CACHE_MS = 8_000;
391
- /** Instances with a background scrape in flight, so polls don't pile up captures. */
392
- const paneScrapeInFlight = new Set();
393
- /** Async twin of scrapePaneContext — same parsers, no blocking. */
394
- async function scrapePaneContextAsync(instanceName, backend) {
395
- try {
396
- const socketName = getTmuxSocketName();
397
- const baseArgs = ["capture-pane", "-t", `${getTmuxSessionName()}:${instanceName}`, "-p", "-S", "-60"];
398
- const tmuxArgs = socketName ? ["-L", socketName, ...baseArgs] : baseArgs;
399
- const { promisify } = await import("node:util");
400
- const { execFile } = await import("node:child_process");
401
- const { stdout } = await promisify(execFile)("tmux", tmuxArgs, { encoding: "utf-8", timeout: 2000 });
402
- const pane = stdout.toString();
403
- const tokenRatio = backend === "grok" ? parseTokenContextRatio(pane) : null;
404
- return { context: tokenRatio?.percentage ?? parseContextPercent(pane), tokenRatio };
405
- }
406
- catch {
407
- return { context: null, tokenRatio: null };
408
- }
409
- }
410
- /** Refresh one instance's cached context in the background (deduped per instance). */
411
- function refreshPaneContext(instanceName, backend) {
412
- if (paneScrapeInFlight.has(instanceName))
413
- return;
414
- paneScrapeInFlight.add(instanceName);
415
- void scrapePaneContextAsync(instanceName, backend)
416
- .then(scraped => { paneContextCache.set(instanceName, { at: Date.now(), ...scraped }); })
417
- .finally(() => { paneScrapeInFlight.delete(instanceName); });
418
- }
419
- /** Forget a deleted instance's cached context so the map can't grow forever. */
388
+ const paneContextCache = new PaneContextCache();
389
+ /** Forget a deleted instance and revoke any outstanding refresh for its old pane. */
420
390
  export function forgetInstanceContext(instanceName) {
421
- paneContextCache.delete(instanceName);
391
+ paneContextCache.forget(instanceName);
422
392
  }
423
393
  /**
424
- * Single source of truth for instance context % across /ctx, /status, and View.
425
- * Claude-code prefers statusline.json (authoritative, no TUI scrape); everyone
426
- * else scrapes the live pane with the same parsers /ctx uses.
427
- *
428
- * Non-blocking by default (stale-while-revalidate): a fresh cache entry is
429
- * returned as-is; a stale or missing one is returned immediately anyway while a
430
- * background refresh runs. This used to scrape synchronously with `execFileSync`
431
- * (2s timeout) on a cache miss, and the 12s TTL is LONGER than the dashboard's
432
- * 10s poll — so roughly every other tick did N blocking captures. With ten
433
- * non-claude-code instances and a slow tmux that froze the entire fleet event
434
- * loop for up to 20s per tick: no IPC, no message delivery, no watchdog ping.
435
- * Three open browser tabs ran three independent polls.
436
- *
437
- * Pass `bypassCache` for a synchronous, authoritative read — used by `/ctx`,
438
- * where a user is asking right now and 2s of blocking is the correct trade.
394
+ * Shared context percentage for /ctx, /status and View, using the same pane parsers.
395
+ * Claude Code prefers its authoritative statusline.json.
396
+ * Polled reads return cached values immediately and refresh through the captured
397
+ * owner after the monotonic 8s interval. A missing pane owner returns unknown.
398
+ * Explicit /ctx retains its synchronous authoritative read via bypassCache.
439
399
  */
440
400
  export function resolveInstanceContext(dataDir, instanceName, backend, opts) {
441
401
  if (backend === "claude-code") {
@@ -445,17 +405,10 @@ export function resolveInstanceContext(dataDir, instanceName, backend, opts) {
445
405
  }
446
406
  if (opts?.bypassCache) {
447
407
  const scraped = scrapePaneContext(instanceName, backend);
448
- paneContextCache.set(instanceName, { at: Date.now(), ...scraped });
408
+ paneContextCache.record(dataDir, instanceName, backend, scraped, opts.source);
449
409
  return scraped;
450
410
  }
451
- const hit = paneContextCache.get(instanceName);
452
- if (hit && Date.now() - hit.at < PANE_CONTEXT_CACHE_MS) {
453
- return { context: hit.context, tokenRatio: hit.tokenRatio };
454
- }
455
- // Stale or absent: kick off the refresh and answer with what we have. A brand-new
456
- // instance reads as "no data" for one tick rather than blocking the fleet.
457
- refreshPaneContext(instanceName, backend);
458
- return hit ? { context: hit.context, tokenRatio: hit.tokenRatio } : { context: null, tokenRatio: null };
411
+ return paneContextCache.resolve(dataDir, instanceName, backend, opts?.source);
459
412
  }
460
413
  const PROFILE_RE = /^\/profile(?:@\w+)?(?:\s+([\s\S]*))?$/;
461
414
  const RESTART_RE = /^\/restart(?:@[A-Za-z0-9_]*)?(?:\s+(.*))?$/i;
@@ -956,7 +909,7 @@ export class TopicCommands {
956
909
  // classic channel manager for those so we don't mis-report defaults.backend.
957
910
  const backend = this.effectiveBackend(instanceName);
958
911
  // Fresh scrape for /ctx (user is asking right now) — bypass short cache.
959
- const { context, tokenRatio } = resolveInstanceContext(this.ctx.dataDir, instanceName, backend, { bypassCache: true });
912
+ const { context, tokenRatio } = resolveInstanceContext(this.ctx.dataDir, instanceName, backend, { bypassCache: true, source: this.ctx.getPaneContextSource?.(instanceName) });
960
913
  const contextLine = context == null ? null : formatContextUsageLine(context, tokenRatio);
961
914
  // Effective model (resolves per-instance → fleet default → classic channel →
962
915
  // the CLI's own default) via the shared resolver, so /ctx and /model agree.
@@ -1368,7 +1321,7 @@ export class TopicCommands {
1368
1321
  // Without this, kiro/grok/codex always showed "-" in the Ctx column.
1369
1322
  const { context } = backend === "-"
1370
1323
  ? { context: null }
1371
- : resolveInstanceContext(this.ctx.dataDir, name, backend);
1324
+ : resolveInstanceContext(this.ctx.dataDir, name, backend, { source: this.ctx.getPaneContextSource?.(name) });
1372
1325
  const contextStr = context == null ? "-" : `${Math.round(context)}%`;
1373
1326
  const costCents = this.ctx.costGuard?.getDailyCostCents(name) ?? 0;
1374
1327
  // Merged State (#1052): lifecycle + execution state in one column. The
@@ -1535,15 +1488,29 @@ export class TopicCommands {
1535
1488
  }
1536
1489
  const sent = await adapter.sendText(chatId, t("update.progress.preparing", 0), { threadId });
1537
1490
  this.ctx.beginUpdateProgress?.(adapter, chatId, threadId, sent.messageId);
1538
- // The CLI picks the channel from the installed version it replaces; see UPDATE_COMMAND.
1539
- const updateCmd = UPDATE_COMMAND;
1491
+ // The CLI picks the channel from the installed version it replaces; see UPDATE_COMMAND. #1450 C5: the INSTALLED
1492
+ // agend, verified by identity, by absolute path — or a refusal, never whatever `agend` is first on PATH.
1493
+ const installed = await resolveInstalledAgend();
1494
+ if (!installed.ok) {
1495
+ this.ctx.failUpdateProgress?.(`/update cannot verify the installed AgEnD (${installed.reason}). Run \`agend update\` from a shell.`);
1496
+ return;
1497
+ }
1540
1498
  const { spawn } = await import("node:child_process");
1541
1499
  const origin = `command /update by ${msg.adapterId}:${msg.userId}`;
1542
1500
  recordInternalRequest(this.ctx.dataDir, "update", origin);
1543
- const child = spawn("sh", ["-c", `sleep 2 && ${updateCmd}`], {
1501
+ const launch = await resolveUpdateLaunch(installed.agend);
1502
+ if (!launch.ok) {
1503
+ this.ctx.failUpdateProgress?.(`/update cannot prepare an independent updater (${launch.reason}). Run ` + "`agend update` from a host shell.");
1504
+ return;
1505
+ }
1506
+ if (!this.ctx.hasFleetAdmins(msg.adapterId) || !this.ctx.isFleetAdmin(msg.userId, msg.adapterId) || this.ctx.isFleetStopping()) {
1507
+ this.ctx.failUpdateProgress?.(t("not_authorized"));
1508
+ return;
1509
+ }
1510
+ const child = spawn(launch.command, launch.args, {
1544
1511
  detached: true, stdio: "ignore", env: withOrigin(origin),
1545
1512
  });
1546
- child.once("error", err => this.ctx.failUpdateProgress?.(err.message));
1513
+ watchUpdateLaunch(child, message => this.ctx.failUpdateProgress?.(message));
1547
1514
  child.unref();
1548
1515
  }
1549
1516
  /** #1302: `/visibility [full|summary|hidden]` in the General topic. */
@@ -1580,7 +1547,9 @@ export class TopicCommands {
1580
1547
  try {
1581
1548
  const { execFile } = await import("node:child_process");
1582
1549
  const { promisify } = await import("node:util");
1583
- const { stdout } = await promisify(execFile)("agend", ["backend", "doctor", backend], {
1550
+ // This CLI on this Node, not whichever `agend` is first on PATH (#1450 C5).
1551
+ const doctor = selfCommand(["backend", "doctor", backend]);
1552
+ const { stdout } = await promisify(execFile)(doctor.command, doctor.args, {
1584
1553
  timeout: 30_000,
1585
1554
  encoding: "utf-8",
1586
1555
  });
@@ -1651,26 +1620,22 @@ export class TopicCommands {
1651
1620
  if (!botToken)
1652
1621
  return;
1653
1622
  let configChanged = false;
1623
+ const channels = this.ctx.fleetConfig?.channels ?? (this.ctx.fleetConfig?.channel ? [this.ctx.fleetConfig.channel] : []);
1654
1624
  for (const [name, config] of Object.entries(this.ctx.fleetConfig.instances)) {
1655
1625
  if (config.topic_id != null)
1656
1626
  continue;
1657
- // General topic: determine platform type from channel_id → channels config
1627
+ // #1549 review: the agent's own connection decides — its channel_id, else the first. A topic is made, or a
1628
+ // General bound, only in that connection's chat: an owner that is not configured is left unbound, and nothing
1629
+ // is borrowed from another connection (a topic in the old group, the old bot's General).
1630
+ const ownerId = config.channel_id;
1631
+ const owner = ownerId != null ? channels.find(c => (c.id ?? c.type) === ownerId) : channels[0];
1632
+ if (!owner) {
1633
+ this.ctx.logger.warn({ name, channelId: ownerId }, "Agent's connection is not configured — leaving its topic unbound");
1634
+ continue;
1635
+ }
1658
1636
  if (config.general_topic) {
1659
- const channels = this.ctx.fleetConfig?.channels ?? (this.ctx.fleetConfig?.channel ? [this.ctx.fleetConfig.channel] : []);
1660
- let platformType;
1661
- if (config.channel_id) {
1662
- const matched = channels.find(c => (c.id ?? c.type) === config.channel_id);
1663
- platformType = matched?.type;
1664
- }
1665
- if (!platformType) {
1666
- if (name.includes("telegram"))
1667
- platformType = "telegram";
1668
- else if (name.includes("discord"))
1669
- platformType = "discord";
1670
- }
1671
- if (platformType === "discord") {
1672
- const ch = channels.find(c => c.type === "discord");
1673
- const gcid = ch?.options?.general_channel_id;
1637
+ if (owner.type === "discord") {
1638
+ const gcid = owner.options?.general_channel_id;
1674
1639
  // A Discord general needs a real channel id — NOT the TG-convention
1675
1640
  // "1", which makes the DC adapter throw fetching channel "1". Skip
1676
1641
  // (leave unbound) if there's no valid channel to bind to.
@@ -1684,7 +1649,13 @@ export class TopicCommands {
1684
1649
  config.topic_id = 1;
1685
1650
  }
1686
1651
  configChanged = true;
1687
- this.ctx.logger.info({ name, topicId: config.topic_id, platformType }, "Bound to General topic");
1652
+ this.ctx.logger.info({ name, topicId: config.topic_id, platformType: owner.type }, "Bound to General topic");
1653
+ continue;
1654
+ }
1655
+ // A new topic is made through the primary connection only (createForumTopic's adapter): an agent of another
1656
+ // connection is not given a topic in the primary's group.
1657
+ if (owner !== channels[0]) {
1658
+ this.ctx.logger.info({ name, channelId: ownerId }, "Agent belongs to another connection — not auto-creating a topic in the primary group");
1688
1659
  continue;
1689
1660
  }
1690
1661
  try {