@songsid/agend 2.1.5 → 2.1.6-beta.10

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 (184) hide show
  1. package/dist/agent-endpoint.d.ts +18 -0
  2. package/dist/agent-endpoint.js +53 -2
  3. package/dist/agent-endpoint.js.map +1 -1
  4. package/dist/apply-job.d.ts +114 -0
  5. package/dist/apply-job.js +214 -0
  6. package/dist/apply-job.js.map +1 -0
  7. package/dist/backend/claude-code.d.ts +25 -0
  8. package/dist/backend/claude-code.js +127 -0
  9. package/dist/backend/claude-code.js.map +1 -1
  10. package/dist/backend/codex.d.ts +57 -0
  11. package/dist/backend/codex.js +183 -0
  12. package/dist/backend/codex.js.map +1 -1
  13. package/dist/backend/credential-profile.d.ts +159 -0
  14. package/dist/backend/credential-profile.js +340 -0
  15. package/dist/backend/credential-profile.js.map +1 -0
  16. package/dist/backend/factory.js +4 -1
  17. package/dist/backend/factory.js.map +1 -1
  18. package/dist/backend/kiro-auth-store.d.ts +25 -0
  19. package/dist/backend/kiro-auth-store.js +61 -0
  20. package/dist/backend/kiro-auth-store.js.map +1 -0
  21. package/dist/backend/kiro.d.ts +8 -0
  22. package/dist/backend/kiro.js +31 -2
  23. package/dist/backend/kiro.js.map +1 -1
  24. package/dist/backend/muse.d.ts +90 -0
  25. package/dist/backend/muse.js +437 -0
  26. package/dist/backend/muse.js.map +1 -0
  27. package/dist/backend/types.d.ts +54 -0
  28. package/dist/backend/types.js.map +1 -1
  29. package/dist/channel/access-manager.d.ts +20 -0
  30. package/dist/channel/access-manager.js +25 -0
  31. package/dist/channel/access-manager.js.map +1 -1
  32. package/dist/channel/adapters/discord.d.ts +17 -0
  33. package/dist/channel/adapters/discord.js +72 -11
  34. package/dist/channel/adapters/discord.js.map +1 -1
  35. package/dist/channel/adapters/telegram.d.ts +6 -0
  36. package/dist/channel/adapters/telegram.js +36 -0
  37. package/dist/channel/adapters/telegram.js.map +1 -1
  38. package/dist/channel/markdown-chunk.d.ts +8 -3
  39. package/dist/channel/markdown-chunk.js +12 -3
  40. package/dist/channel/markdown-chunk.js.map +1 -1
  41. package/dist/channel/mcp-server.js +9 -12
  42. package/dist/channel/mcp-server.js.map +1 -1
  43. package/dist/channel/mcp-tools.d.ts +6 -2
  44. package/dist/channel/mcp-tools.js +6 -36
  45. package/dist/channel/mcp-tools.js.map +1 -1
  46. package/dist/channel/tool-router.js +32 -3
  47. package/dist/channel/tool-router.js.map +1 -1
  48. package/dist/channel/types.d.ts +18 -0
  49. package/dist/cli.js +113 -0
  50. package/dist/cli.js.map +1 -1
  51. package/dist/config-validator.js +45 -5
  52. package/dist/config-validator.js.map +1 -1
  53. package/dist/connection-secrets.d.ts +96 -0
  54. package/dist/connection-secrets.js +25 -0
  55. package/dist/connection-secrets.js.map +1 -0
  56. package/dist/daemon.d.ts +76 -0
  57. package/dist/daemon.js +489 -103
  58. package/dist/daemon.js.map +1 -1
  59. package/dist/event-log.d.ts +9 -0
  60. package/dist/event-log.js +21 -0
  61. package/dist/event-log.js.map +1 -1
  62. package/dist/fleet-level-config.d.ts +42 -0
  63. package/dist/fleet-level-config.js +78 -0
  64. package/dist/fleet-level-config.js.map +1 -0
  65. package/dist/fleet-lock.d.ts +21 -0
  66. package/dist/fleet-lock.js +42 -8
  67. package/dist/fleet-lock.js.map +1 -1
  68. package/dist/fleet-manager.d.ts +425 -2
  69. package/dist/fleet-manager.js +2055 -182
  70. package/dist/fleet-manager.js.map +1 -1
  71. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +145 -0
  72. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +11 -1
  73. package/dist/instance-config-impact.d.ts +55 -0
  74. package/dist/instance-config-impact.js +152 -0
  75. package/dist/instance-config-impact.js.map +1 -0
  76. package/dist/instance-lifecycle.js +8 -4
  77. package/dist/instance-lifecycle.js.map +1 -1
  78. package/dist/locale.js +24 -0
  79. package/dist/locale.js.map +1 -1
  80. package/dist/logger.js +42 -25
  81. package/dist/logger.js.map +1 -1
  82. package/dist/muse-usage-relay.d.ts +72 -0
  83. package/dist/muse-usage-relay.js +419 -0
  84. package/dist/muse-usage-relay.js.map +1 -0
  85. package/dist/outbound-handlers.d.ts +5 -1
  86. package/dist/outbound-handlers.js +199 -1
  87. package/dist/outbound-handlers.js.map +1 -1
  88. package/dist/outbound-schemas.d.ts +7 -5
  89. package/dist/outbound-schemas.js +3 -2
  90. package/dist/outbound-schemas.js.map +1 -1
  91. package/dist/provider-probe.d.ts +48 -0
  92. package/dist/provider-probe.js +121 -0
  93. package/dist/provider-probe.js.map +1 -0
  94. package/dist/provider-secret-registry.d.ts +98 -0
  95. package/dist/provider-secret-registry.js +334 -0
  96. package/dist/provider-secret-registry.js.map +1 -0
  97. package/dist/quickstart-api.d.ts +164 -0
  98. package/dist/quickstart-api.js +351 -0
  99. package/dist/quickstart-api.js.map +1 -0
  100. package/dist/quickstart.js +23 -50
  101. package/dist/quickstart.js.map +1 -1
  102. package/dist/scheduler/db.js +17 -5
  103. package/dist/scheduler/db.js.map +1 -1
  104. package/dist/scheduler/db.test.js +34 -1
  105. package/dist/scheduler/db.test.js.map +1 -1
  106. package/dist/scheduler/types.d.ts +4 -0
  107. package/dist/scheduler/types.js.map +1 -1
  108. package/dist/secret-store.d.ts +25 -0
  109. package/dist/secret-store.js +165 -0
  110. package/dist/secret-store.js.map +1 -0
  111. package/dist/self-restart-limit.d.ts +21 -0
  112. package/dist/self-restart-limit.js +129 -0
  113. package/dist/self-restart-limit.js.map +1 -0
  114. package/dist/settings-api.d.ts +116 -0
  115. package/dist/settings-api.js +530 -5
  116. package/dist/settings-api.js.map +1 -1
  117. package/dist/setup-auth.d.ts +113 -0
  118. package/dist/setup-auth.js +181 -0
  119. package/dist/setup-auth.js.map +1 -0
  120. package/dist/setup-form.d.ts +18 -0
  121. package/dist/setup-form.js +315 -0
  122. package/dist/setup-form.js.map +1 -0
  123. package/dist/setup-host.d.ts +162 -0
  124. package/dist/setup-host.js +496 -0
  125. package/dist/setup-host.js.map +1 -0
  126. package/dist/setup-marker.d.ts +8 -0
  127. package/dist/setup-marker.js +39 -0
  128. package/dist/setup-marker.js.map +1 -0
  129. package/dist/setup-tunnel-consent.d.ts +53 -0
  130. package/dist/setup-tunnel-consent.js +82 -0
  131. package/dist/setup-tunnel-consent.js.map +1 -0
  132. package/dist/setup-wizard.js +4 -0
  133. package/dist/setup-wizard.js.map +1 -1
  134. package/dist/steer-capability.js +4 -1
  135. package/dist/steer-capability.js.map +1 -1
  136. package/dist/tips.js +1 -1
  137. package/dist/tips.js.map +1 -1
  138. package/dist/tmux-manager.js +29 -4
  139. package/dist/tmux-manager.js.map +1 -1
  140. package/dist/tool-permissions-notice.d.ts +38 -0
  141. package/dist/tool-permissions-notice.js +92 -0
  142. package/dist/tool-permissions-notice.js.map +1 -0
  143. package/dist/tool-permissions.d.ts +120 -0
  144. package/dist/tool-permissions.js +277 -0
  145. package/dist/tool-permissions.js.map +1 -0
  146. package/dist/topic-commands.js +1 -0
  147. package/dist/topic-commands.js.map +1 -1
  148. package/dist/transcript-sources.d.ts +12 -1
  149. package/dist/transcript-sources.js +19 -3
  150. package/dist/transcript-sources.js.map +1 -1
  151. package/dist/tunnel/cloudflared.d.ts +71 -0
  152. package/dist/tunnel/cloudflared.js +447 -0
  153. package/dist/tunnel/cloudflared.js.map +1 -0
  154. package/dist/tunnel/lease.d.ts +75 -0
  155. package/dist/tunnel/lease.js +209 -0
  156. package/dist/tunnel/lease.js.map +1 -0
  157. package/dist/tunnel/manager.d.ts +56 -0
  158. package/dist/tunnel/manager.js +167 -0
  159. package/dist/tunnel/manager.js.map +1 -0
  160. package/dist/tunnel/types.d.ts +119 -0
  161. package/dist/tunnel/types.js +33 -0
  162. package/dist/tunnel/types.js.map +1 -0
  163. package/dist/types.d.ts +2 -0
  164. package/dist/ui/dashboard.html +4 -3
  165. package/dist/ui/settings.html +808 -156
  166. package/dist/ui/view.html +2 -2
  167. package/dist/usage/i18n-keys.d.ts +1 -1
  168. package/dist/usage/i18n-keys.js +1 -1
  169. package/dist/usage/i18n-keys.js.map +1 -1
  170. package/dist/usage/providers.d.ts +23 -14
  171. package/dist/usage/providers.js +220 -58
  172. package/dist/usage/providers.js.map +1 -1
  173. package/dist/usage/usage-api.d.ts +13 -0
  174. package/dist/usage/usage-api.js +69 -1
  175. package/dist/usage/usage-api.js.map +1 -1
  176. package/dist/web-api.js +8 -5
  177. package/dist/web-api.js.map +1 -1
  178. package/dist/web-auth.d.ts +75 -0
  179. package/dist/web-auth.js +208 -2
  180. package/dist/web-auth.js.map +1 -1
  181. package/dist/web-terminal.d.ts +30 -1
  182. package/dist/web-terminal.js +86 -3
  183. package/dist/web-terminal.js.map +1 -1
  184. package/package.json +2 -1
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: credential-profiles
3
+ description: Run agents on more than one subscription of the same backend. Use when someone asks for a worker on a different account, wants an agent moved to another subscription, or asks which subscription an agent is using. Currently kiro-cli only.
4
+ roles: [general]
5
+ ---
6
+
7
+ # Credential Profiles
8
+
9
+ One AgEnD fleet normally shares one login per backend. A **credential profile**
10
+ is a named, separate login for the same backend, so two agents can run on two
11
+ subscriptions at once.
12
+
13
+ `kiro-cli` and `codex` have profiles today. For any other backend, say so rather
14
+ than setting an option that will be ignored.
15
+
16
+ **The two behave differently when you switch, and it is not a setting.** Kiro
17
+ keeps its conversations inside the same database as its login, so a different
18
+ subscription is a different set of them: switching is a new conversation. Codex
19
+ keeps its login in one file beside conversation stores that carry no account, so
20
+ switching leaves the history where it is and both subscriptions see it. Say
21
+ which one you are talking about.
22
+
23
+ ## Is a profile already set up?
24
+
25
+ A profile exists once someone has logged into it on the host. AgEnD cannot log
26
+ in for them: the login is a browser device flow.
27
+
28
+ **A switch to a profile that has never been logged in is refused**, and the
29
+ error carries the exact command to run. This is deliberate: kiro-cli does not
30
+ start a signed-out session, it stops at "let's get you signed in!" and waits for
31
+ a keypress, so the agent would sit on a login screen instead of working. Relay
32
+ the command, do not try to work around the refusal.
33
+
34
+ Ask the operator to run this on the host once per subscription:
35
+
36
+ ```
37
+ XDG_DATA_HOME="${AGEND_HOME:-~/.agend}/credential-profiles/kiro-cli/<profile>" kiro-cli login
38
+ ```
39
+
40
+ For codex the variable and the path are different:
41
+
42
+ ```
43
+ CODEX_HOME="${AGEND_HOME:-~/.agend}/credential-profiles/codex/<profile>" codex login
44
+ ```
45
+
46
+ ## Create a worker on another subscription
47
+
48
+ ```
49
+ create_instance(
50
+ name: "research-b",
51
+ working_directory: "/home/you/projects/app",
52
+ backend: "kiro-cli",
53
+ backend_options: { "kiro-cli": { "credential_profile": "personal" } },
54
+ )
55
+ ```
56
+
57
+ The profile name is yours to choose — `work`, `personal`, `team-b`. Reuse the
58
+ same name for every agent that should share that subscription.
59
+
60
+ ## Move an existing agent to another subscription
61
+
62
+ ```
63
+ update_instance_config(
64
+ name: "research-a",
65
+ config: { backend_options: { "kiro-cli": { "credential_profile": "personal" } } },
66
+ )
67
+ ```
68
+
69
+ The credentials are read when the CLI starts, so **the instance is restarted
70
+ for you** and the reply says `restarted: true`.
71
+
72
+ **For kiro**, a switch is always a **new conversation**, and the reply says so
73
+ (`conversation_carried_over: false`). kiro keeps its conversations in the same
74
+ database as its login, so a different subscription has a different set of them
75
+ and there is nothing to resume — AgEnD does not even try. What it does instead
76
+ is hand the new session a summary of what the old one was doing
77
+ (`handover_chars` says how much), so the agent can pick the work up without the
78
+ transcript. Tell the user plainly: the agent restarted on the other
79
+ subscription, it knows what it was doing, it cannot quote what was said.
80
+
81
+ **For codex** the reply says `conversation_carried_over: true` and carries a
82
+ `conversation_note`. Relay it as what it is — a statement about where the
83
+ history lives, not a promise about what happens next. The accurate sentence is
84
+ "codex keeps its history outside the login, so both subscriptions share it".
85
+ **Do not promise the agent will pick the thread back up**: whether codex reopens
86
+ a conversation recorded under a different account has not been tested yet. If it
87
+ does not, the agent starts a fresh conversation and carries on — nothing breaks,
88
+ it just does not continue.
89
+
90
+ `backend_options` merges per backend, so setting a kiro option leaves a codex
91
+ option on the same instance alone.
92
+
93
+ ## Move an agent back to the default login
94
+
95
+ A merge cannot remove a key, so send `null` for it:
96
+
97
+ ```
98
+ update_instance_config(
99
+ name: "research-a",
100
+ config: { backend_options: { "kiro-cli": { "credential_profile": null } } },
101
+ )
102
+ ```
103
+
104
+ The agent goes back to the login the host uses with no profile set, and is
105
+ restarted for the same reason as any other switch.
106
+
107
+ ## Answer "which subscription is this agent on?"
108
+
109
+ Read it from the instance config (`get_fleet_config` or `describe_instance`):
110
+ `backend_options["kiro-cli"].credential_profile`. **No profile means the shared
111
+ login** — the one `kiro-cli login` uses on the host with no environment set.
112
+ Say "the default login", not "no subscription".
113
+
114
+ ## Answer "how much is left on each subscription?"
115
+
116
+ `get_usage` returns one row per subscription: `Kiro (work)` and
117
+ `Kiro (personal)` rather than a single `Kiro`. Read them out separately — they
118
+ are two quotas, and adding them together would give a number that is true of
119
+ neither. An agent with no profile appears under the plain `Kiro` row.
120
+
121
+ A profile that is configured but has never been logged in still gets a row,
122
+ reading **Signed out — run `kiro-cli` to log in**. That is the reminder to go
123
+ and log it in, so report it as such rather than as a missing subscription.
124
+
125
+ ## What to tell the user
126
+
127
+ - Two agents with the same profile name share one login and one quota.
128
+ - An agent with no profile uses the fleet's default login, exactly as before.
129
+ - Quotas only add up if the profiles are genuinely different billing accounts.
130
+ Two profiles logged into the same account do not double anything, and it is
131
+ worth saying so before someone sets this up expecting more headroom.
132
+ - Switching a profile does not carry the conversation across; the agent gets a
133
+ summary of the work, not the transcript. Say anything that must survive
134
+ verbatim in the channel before switching.
135
+
136
+ ## Do not
137
+
138
+ - Do not invent a profile that has never been logged in and report success —
139
+ the switch is refused, and the refusal tells you what to relay.
140
+ - Do not set `credential_profile` for a backend other than `kiro-cli` or
141
+ `codex`; it is ignored, and the config validator warns about it.
142
+ - Do not tell a codex user their conversation will definitely continue across a
143
+ switch. Say the history is shared; that part is verified.
144
+ - Do not use a profile name with slashes or spaces — it becomes a directory
145
+ name and is rejected.
@@ -10,7 +10,17 @@ roles: [worker]
10
10
 
11
11
  - Treat the assignment as the full scope unless the sender explicitly expands it.
12
12
  - Work without acknowledgment-only messages. Silence means the task is in progress.
13
- - Do not call `delegate_task`; delegation and fleet orchestration belong to General.
13
+ - Fleet orchestration is not yours: you do not have `create_instance`,
14
+ `delete_instance`, `restart_instance`, `deploy_template`, the team tools, or
15
+ the tools that create, change or delete schedules. Calling one is refused
16
+ with an explanation rather than silently ignored. If the work needs more
17
+ capacity or another agent restarted, say so with `report_result` and let the
18
+ coordinator decide.
19
+ - You can still *look*: `list_schedules`, `list_deployments`, `list_instances`
20
+ and the other read-only queries are yours, so check before asking.
21
+ - You *can* `delegate_task`, because it only hands work to an instance that
22
+ already exists. Prefer reporting back over delegating sideways unless the
23
+ assignment said otherwise — delegation belongs to whoever is coordinating.
14
24
 
15
25
  ## Request missing information
16
26
 
@@ -0,0 +1,55 @@
1
+ import type { InstanceConfig } from "./types.js";
2
+ /** ⚡ applied to the running agent / 🔄 restart the agent / 🔄🔄 restart AgEnD. */
3
+ export type ConfigImpact = "now" | "instance" | "fleet";
4
+ /** Cheapest first. A batch of edits costs as much as its worst member. */
5
+ export declare const IMPACT_ORDER: readonly ConfigImpact[];
6
+ /**
7
+ * Instance settings a live daemon can take without a restart.
8
+ *
9
+ * Adding a key here is only half the change: `Daemon.applyConfigUpdate` has to
10
+ * learn it too, or the setting is written to disk, reported as applied, and
11
+ * silently ignored by the running agent. `tests/hot-cold-parity.test.ts` fails
12
+ * when the two drift.
13
+ */
14
+ export declare const HOT_INSTANCE_CONFIG_KEYS: ReadonlySet<keyof InstanceConfig>;
15
+ /**
16
+ * The hot keys a ClassicBot channel edit can use.
17
+ *
18
+ * Narrower than the fleet-wide set because a classic channel's hot path carries
19
+ * exactly what `FleetManager.classicBehaviorUpdate()` builds; a key that is hot
20
+ * for a fleet instance but absent from that payload (auto_pause_after) would be
21
+ * dropped on the way. Derived, not re-listed: dropping a key from the hot set
22
+ * drops it here too.
23
+ */
24
+ export declare const CLASSIC_HOT_CONFIG_KEYS: ReadonlySet<string>;
25
+ /** Split a config into what a live daemon can take and what needs a restart. */
26
+ export declare function splitHotColdConfig(config: InstanceConfig): {
27
+ hot: Partial<InstanceConfig>;
28
+ cold: Partial<InstanceConfig>;
29
+ };
30
+ /** The complete hot snapshot sent over IPC; absent values are explicit nulls so
31
+ * the daemon removes them rather than keeping a stale override. */
32
+ export declare function hotConfigUpdate(config: InstanceConfig): Record<string, unknown>;
33
+ /**
34
+ * What applying `next` to a running instance costs.
35
+ *
36
+ * The one comparison `reconcileInstances` makes when it decides between an IPC
37
+ * hot update and a stop/start — shared so the apply job's plan says what the
38
+ * reconcile will actually do rather than holding a second opinion about it.
39
+ */
40
+ export declare function classifyInstanceChange(runtime: InstanceConfig, next: InstanceConfig): "restart" | "hot" | "none";
41
+ export declare function instanceFieldImpact(key: string): ConfigImpact;
42
+ export declare function classicFieldImpact(key: string): ConfigImpact;
43
+ /**
44
+ * Impact of one staged edit: hot only while every changed key is hot, since a
45
+ * single cold key in the batch forces the restart anyway.
46
+ */
47
+ export declare function batchImpact(keys: readonly string[], impactOf: (key: string) => ConfigImpact): ConfigImpact;
48
+ export interface SettingsImpactSchema {
49
+ /** field path → impact, e.g. `instance.tool_progress` → "now". */
50
+ impacts: Record<string, ConfigImpact>;
51
+ /** Escalation order, so the page can cost a batch without knowing the rule. */
52
+ order: readonly ConfigImpact[];
53
+ }
54
+ /** The payload behind `GET /api/settings/schema`. */
55
+ export declare function buildSettingsImpactSchema(): SettingsImpactSchema;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * One source of truth for "what does changing this setting cost?".
3
+ *
4
+ * `HOT_INSTANCE_CONFIG_KEYS` is the authority: a hot key is pushed into a live
5
+ * daemon over IPC (`Daemon.applyConfigUpdate`), anything else needs the CLI
6
+ * process restarted. That knowledge used to be spelled out in five places —
7
+ * the fleet manager, the settings API, twice in the settings page, and once per
8
+ * field as a hand-written badge — so the four copies could drift away from the
9
+ * one that decides what actually happens.
10
+ *
11
+ * Everything here is derived from the two sets below. The settings page reads
12
+ * it from `GET /api/settings/schema` instead of carrying its own copy.
13
+ */
14
+ import { isDeepStrictEqual } from "node:util";
15
+ /** Cheapest first. A batch of edits costs as much as its worst member. */
16
+ export const IMPACT_ORDER = ["now", "instance", "fleet"];
17
+ /**
18
+ * Instance settings a live daemon can take without a restart.
19
+ *
20
+ * Adding a key here is only half the change: `Daemon.applyConfigUpdate` has to
21
+ * learn it too, or the setting is written to disk, reported as applied, and
22
+ * silently ignored by the running agent. `tests/hot-cold-parity.test.ts` fails
23
+ * when the two drift.
24
+ */
25
+ export const HOT_INSTANCE_CONFIG_KEYS = new Set([
26
+ "tool_progress",
27
+ "reply_completion_guard",
28
+ "mcp_proxy_reply",
29
+ "auto_pause_after",
30
+ "warm_cap",
31
+ "display_name",
32
+ "description",
33
+ "tags",
34
+ "log_level",
35
+ ]);
36
+ /**
37
+ * The hot keys a ClassicBot channel edit can use.
38
+ *
39
+ * Narrower than the fleet-wide set because a classic channel's hot path carries
40
+ * exactly what `FleetManager.classicBehaviorUpdate()` builds; a key that is hot
41
+ * for a fleet instance but absent from that payload (auto_pause_after) would be
42
+ * dropped on the way. Derived, not re-listed: dropping a key from the hot set
43
+ * drops it here too.
44
+ */
45
+ export const CLASSIC_HOT_CONFIG_KEYS = new Set(["tool_progress", "reply_completion_guard"]
46
+ .filter(key => HOT_INSTANCE_CONFIG_KEYS.has(key)));
47
+ /**
48
+ * Instance keys that are not the instance's own business: they change how the
49
+ * fleet binds and routes the instance, so the fleet process has to restart.
50
+ */
51
+ const FLEET_SCOPED_INSTANCE_KEYS = new Set([
52
+ "topic_id",
53
+ "channel_id",
54
+ "general_topic",
55
+ ]);
56
+ /** Instance-scoped fields the settings page renders. */
57
+ const INSTANCE_FIELDS = [
58
+ "display_name", "description", "backend", "model", "working_directory",
59
+ "auto_pause_after", "warm_cap", "topic_id", "channel_id", "general_topic",
60
+ "tool_progress", "reply_completion_guard", "mcp_proxy_reply", "log_level",
61
+ "systemPrompt", "tags", "hang_detector", "agent_mode", "tool_set",
62
+ "lightweight", "model_failover", "effort", "pre_task_command",
63
+ ];
64
+ /** ClassicBot channel fields the settings page renders. */
65
+ const CLASSIC_FIELDS = [
66
+ "backend", "model", "auto_pause_after", "tool_progress",
67
+ "reply_completion_guard", "collab", "context_lines", "pre_task_command",
68
+ ];
69
+ /**
70
+ * Fleet-level fields, which no per-key rule can derive: they belong to the
71
+ * fleet process or the channel binding rather than to an instance's config.
72
+ */
73
+ const FLEET_FIELD_IMPACTS = {
74
+ "defaults.locale": "now",
75
+ "fleet.channels": "fleet",
76
+ "fleet.channel.access.mode": "fleet",
77
+ "fleet.channel.access.allowed_users": "fleet",
78
+ "fleet.spawn_concurrency": "fleet",
79
+ "fleet.spawn_stagger_ms": "fleet",
80
+ "classic.admin_users": "fleet",
81
+ "classic.allowed_guilds": "fleet",
82
+ "instance.delete": "fleet",
83
+ };
84
+ /** Split a config into what a live daemon can take and what needs a restart. */
85
+ export function splitHotColdConfig(config) {
86
+ const hot = {};
87
+ const cold = {};
88
+ for (const [key, value] of Object.entries(config)) {
89
+ (HOT_INSTANCE_CONFIG_KEYS.has(key) ? hot : cold)[key] = value;
90
+ }
91
+ return { hot, cold };
92
+ }
93
+ /** The complete hot snapshot sent over IPC; absent values are explicit nulls so
94
+ * the daemon removes them rather than keeping a stale override. */
95
+ export function hotConfigUpdate(config) {
96
+ const update = {};
97
+ for (const key of HOT_INSTANCE_CONFIG_KEYS)
98
+ update[key] = config[key] ?? null;
99
+ return update;
100
+ }
101
+ /**
102
+ * What applying `next` to a running instance costs.
103
+ *
104
+ * The one comparison `reconcileInstances` makes when it decides between an IPC
105
+ * hot update and a stop/start — shared so the apply job's plan says what the
106
+ * reconcile will actually do rather than holding a second opinion about it.
107
+ */
108
+ export function classifyInstanceChange(runtime, next) {
109
+ const before = splitHotColdConfig(runtime);
110
+ const after = splitHotColdConfig(next);
111
+ // Every field not explicitly classified hot is cold by default.
112
+ if (!isDeepStrictEqual(before.cold, after.cold))
113
+ return "restart";
114
+ if (!isDeepStrictEqual(before.hot, after.hot))
115
+ return "hot";
116
+ return "none";
117
+ }
118
+ export function instanceFieldImpact(key) {
119
+ if (FLEET_SCOPED_INSTANCE_KEYS.has(key))
120
+ return "fleet";
121
+ return HOT_INSTANCE_CONFIG_KEYS.has(key) ? "now" : "instance";
122
+ }
123
+ export function classicFieldImpact(key) {
124
+ return CLASSIC_HOT_CONFIG_KEYS.has(key) ? "now" : "instance";
125
+ }
126
+ /**
127
+ * Impact of one staged edit: hot only while every changed key is hot, since a
128
+ * single cold key in the batch forces the restart anyway.
129
+ */
130
+ export function batchImpact(keys, impactOf) {
131
+ let worst = 0;
132
+ for (const key of keys)
133
+ worst = Math.max(worst, IMPACT_ORDER.indexOf(impactOf(key)));
134
+ return IMPACT_ORDER[worst];
135
+ }
136
+ /** The payload behind `GET /api/settings/schema`. */
137
+ export function buildSettingsImpactSchema() {
138
+ const impacts = { ...FLEET_FIELD_IMPACTS };
139
+ for (const key of INSTANCE_FIELDS) {
140
+ impacts[`instance.${key}`] = instanceFieldImpact(key);
141
+ // Fleet defaults carry the same restart cost as the instance field they
142
+ // seed — except the fleet-scoped ones, which are not defaults at all.
143
+ if (!FLEET_SCOPED_INSTANCE_KEYS.has(key))
144
+ impacts[`defaults.${key}`] = instanceFieldImpact(key);
145
+ }
146
+ for (const key of CLASSIC_FIELDS) {
147
+ impacts[`classic.${key}`] = classicFieldImpact(key);
148
+ impacts[`classic_defaults.${key}`] = classicFieldImpact(key);
149
+ }
150
+ return { impacts, order: IMPACT_ORDER };
151
+ }
152
+ //# sourceMappingURL=instance-config-impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-config-impact.js","sourceRoot":"","sources":["../src/instance-config-impact.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@ export const BACKEND_INSTALLATION_INFO = {
27
27
  opencode: { binary: "opencode", install: "curl -fsSL https://opencode.ai/install | bash" },
28
28
  antigravity: { binary: "agy", install: "curl -fsSL https://antigravity.google/cli/install.sh | bash" },
29
29
  grok: { binary: "grok", install: "curl -fsSL https://x.ai/cli/install.sh | bash" },
30
+ muse: { binary: "muse", install: "curl -fsSL https://api.meta.ai/muse-launcher.sh | bash" },
30
31
  };
31
32
  /** Check one executable using the same PATH visible to the fleet process. */
32
33
  export function checkBinaryInstalled(binary) {
@@ -674,10 +675,13 @@ export class InstanceLifecycle {
674
675
  this.ctx.logger.warn({ name, backend: "codex" }, "Codex quota verification failed or timed out — pausing conservatively");
675
676
  }
676
677
  }
677
- // Claude's credit-balance message also remains in pane scrollback. Only
678
- // a fresh usage API row can prove it stale; provider fallback to a local
679
- // statusline, timeout, or missing credentials remains fail-closed.
680
- if (data.type === "quota" && data.action === "pause" && this.backendOf(name) === "claude-code") {
678
+ // Claude's credit-balance and hard usage-pause messages can remain in pane
679
+ // scrollback. Only a fresh usage API row can prove them stale; provider
680
+ // fallback to a local statusline, timeout, or missing credentials remains
681
+ // fail-closed. `verifyQuota` lets a notify-only pattern request the same
682
+ // second opinion without entering the destructive pause action below.
683
+ if (data.type === "quota" && this.backendOf(name) === "claude-code"
684
+ && (data.action === "pause" || data.verifyQuota)) {
681
685
  const verdict = await this.verifyClaudeQuota();
682
686
  if (verdict === "available") {
683
687
  this.ctx.logger.debug({ name, backend: "claude-code" }, "quota pattern ignored — live usage has capacity (stale pane history)");