@openparachute/agent 0.2.3-rc.7 → 0.2.3-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openparachute/agent",
3
- "version": "0.2.3-rc.7",
3
+ "version": "0.2.3-rc.9",
4
4
  "description": "Vault-native agents for Claude Code — a #agent/definition note + an inbound message becomes a sandboxed claude turn; the reply is written back as a note. Messaging gateway on :1941.",
5
5
  "license": "AGPL-3.0",
6
6
  "type": "module",
package/src/daemon.ts CHANGED
@@ -66,6 +66,7 @@ import {
66
66
  DEFAULT_HUB_ORIGIN,
67
67
  } from "./def-vaults.ts";
68
68
  import { mintScopedToken, vaultScope } from "./mint-token.ts";
69
+ import { registerAllDefVaultTriggers } from "./def-vault-triggers.ts";
69
70
  import { GrantsClient } from "./grants.ts";
70
71
  import { resolveEffectiveEnv } from "./effective-env.ts";
71
72
  import { VaultJobStore, validateJob, vaultTransportFor, type Job } from "./jobs.ts";
@@ -2352,6 +2353,14 @@ export function createFetchHandler(
2352
2353
  // ---------------------------------------------------------------------
2353
2354
  if (url.pathname === "/api/agent-defs" && (req.method === "GET" || req.method === "POST")) {
2354
2355
  // GET is READ-scoped (a listing, no secrets); POST is admin (it mints/writes).
2356
+ //
2357
+ // NOTE (step-up, agent#80/#154): POST is intentionally NOT step-up-gated, even
2358
+ // though a `#agent/definition` note can carry `filesystem: "full"`. Authoring a
2359
+ // def already requires the scope-gated `vault:write` (the daemon writes the note
2360
+ // with a vault token), so a step-up challenge here would gate a capability the
2361
+ // caller already had to hold a write credential to reach — mirrors the carve-out
2362
+ // comment on the vault-native parse path in agent-defs.ts. The `filesystem:full`
2363
+ // SPAWN path (`POST /api/agents`) IS step-up-gated; this AUTHORING path is not.
2355
2364
  const scope = req.method === "GET" ? SCOPE_READ : SCOPE_ADMIN;
2356
2365
  const denied = await requireScope(req, url, scope);
2357
2366
  if (denied) return denied;
@@ -2505,6 +2514,12 @@ export function createFetchHandler(
2505
2514
  // GET is READ-scoped to mirror GET /api/agent-defs — the listing is non-sensitive
2506
2515
  // ({vault,url,tokenPresent}); `tokenPresent` is a boolean, NEVER the token value.
2507
2516
  // POST is admin (it mints a token + writes config).
2517
+ //
2518
+ // NOTE (step-up, agent#80/#154): POST is intentionally NOT step-up-gated. It mints
2519
+ // a VAULT-SCOPED token (`vault:<name>:write`) — a lower blast radius than the
2520
+ // Claude OAuth credential / terminal / full-fs spawn the step-up PIN guards (those
2521
+ // can exfiltrate every token or open a raw host shell). A def-vault token only
2522
+ // reaches the named vault's notes, so `agent:admin` alone is the right bar here.
2508
2523
  const scope = req.method === "GET" ? SCOPE_READ : SCOPE_ADMIN;
2509
2524
  const denied = await requireScope(req, url, scope);
2510
2525
  if (denied) return denied;
@@ -3531,6 +3546,29 @@ function main(): void {
3531
3546
  const bindings = await resolveDefVaults({ hubOrigin: getHubOrigin(), managerBearer });
3532
3547
  for (const b of bindings) agentDefs.addVault(b);
3533
3548
  if (bindings.length === 0) return; // nothing bound — vault-native path idle.
3549
+
3550
+ // AUTO-REGISTER the per-def-vault runtime triggers (agent#157) so "define an
3551
+ // agent → it runs" needs NO manual trigger setup + NO restart-to-pick-up:
3552
+ // - the def-watch create/edit triggers, BARE-keyed (`agent/definition`) so a
3553
+ // created/edited def auto-fires the rescan — upsert-by-name REPLACES any
3554
+ // stale `#agent/definition`-keyed `conn_agentdefs-*` row the hub provisioned;
3555
+ // - the inbound trigger (`agent/message/inbound` + has_metadata:[agent]) so a
3556
+ // new inbound note wakes the agent without a hand-registered trigger.
3557
+ // Mints the admin (triggers API) + agent:send (webhook bearer) tokens the same
3558
+ // way the hub's Connections engine does (attenuated to the operator bearer).
3559
+ // Best-effort: a mint refusal / unreachable vault is logged, never fatal — the
3560
+ // 60s loadAll poll below stays the correctness floor. Skipped with no operator
3561
+ // bearer (can't mint) — the vault-native path still runs own-vault.
3562
+ if (managerBearer) {
3563
+ await registerAllDefVaultTriggers(bindings, { hubOrigin: getHubOrigin(), managerBearer }).catch(
3564
+ (err) => {
3565
+ console.warn(
3566
+ `parachute-agent: def-vault trigger auto-registration failed (continuing): ${(err as Error).message}`,
3567
+ );
3568
+ },
3569
+ );
3570
+ }
3571
+
3534
3572
  const n = await agentDefs.loadAll();
3535
3573
  console.log(
3536
3574
  `parachute-agent: vault-native agent defs — ${n} instantiated from ${bindings.length} def-vault(s).`,
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Auto-register the per-def-vault runtime triggers the daemon needs so that
3
+ * "just define an agent in the vault and it works" — no manual trigger setup, no
4
+ * `parachute restart agent` to pick up a new/edited def (agent#157).
5
+ *
6
+ * Two wiring gaps this closes:
7
+ *
8
+ * 1. **Def-watch triggers were stale `#`-keyed.** A def-vault carried
9
+ * `conn_agentdefs-create-<vault>` / `conn_agentdefs-edit-<vault>` triggers
10
+ * keyed on the PRE-canonicalization tag `#agent/definition`. Since defs are
11
+ * now bare `agent/definition`, those triggers never fired → creating/editing
12
+ * a def did NOT auto-rescan; only the 60s `loadAll` poll converged it. We
13
+ * re-register them BARE-keyed on (re)start, upsert-by-name, so the stale
14
+ * `#`-keyed rows are REPLACED in place (the runtime triggers API is an upsert
15
+ * by `name`, and we reuse the hub's `conn_agentdefs-{create,edit}-<vault>`
16
+ * names so our POST overwrites the hub-provisioned ones).
17
+ *
18
+ * 2. **The inbound trigger was never auto-registered.** Defining a def + the
19
+ * daemon discovering it (the channel appears) is NOT enough — nothing wakes
20
+ * the agent until an `agent_inbound` trigger fires the inbound webhook. The
21
+ * hub provisioned it only via the operator's explicit "Connect" click (or the
22
+ * hub Connections builder); a fresh box needed it registered BY HAND. We now
23
+ * register ONE inbound trigger per def-vault on (re)start (one trigger routes
24
+ * ALL agents in the vault by `metadata.agent`).
25
+ *
26
+ * ## How this mirrors the hub's provisioning path
27
+ *
28
+ * The hub's Connections engine (`parachute-hub/src/admin-connections.ts`) already
29
+ * registers these triggers by:
30
+ * - minting a `vault:<v>:admin` token (the triggers API is ADMIN-scoped — a
31
+ * webhook trigger exfiltrates note data, so even listing is admin, not write),
32
+ * - minting an `agent:send` webhook bearer for `action.auth.bearer`,
33
+ * - POSTing `{ name, events, when, action }` to
34
+ * `<vaultUrl>/vault/<v>/api/triggers` (upsert by name),
35
+ * - naming the def-watch triggers `conn_agentdefs-{create,edit}-<vault>`.
36
+ *
37
+ * We do the SAME thing, daemon-side, on boot — reusing the daemon's own
38
+ * `mintScopedToken` (attenuated to the operator bearer) for BOTH mints. This is
39
+ * the credential the hub uses too; the daemon already holds the operator bearer
40
+ * (it mints the def-vault `vault:<v>:write` token the same way), so the admin mint
41
+ * succeeds exactly when the operator's authority covers it.
42
+ *
43
+ * ## Scope caveat (admin mint)
44
+ *
45
+ * The triggers API requires `vault:<v>:admin`. The def-vault token the daemon
46
+ * persists in `agent-vaults.json` is only `vault:<v>:write`, so we CANNOT register
47
+ * triggers with that token — we MINT a short-lived `vault:<v>:admin` token against
48
+ * the operator bearer instead. If the operator bearer's own authority doesn't cover
49
+ * `vault:<v>:admin`, the hub returns `invalid_scope` on the mint (the same bound the
50
+ * hub's own provisioning hits) — we log + skip, never crash boot. The 60s `loadAll`
51
+ * poll remains the correctness floor either way.
52
+ *
53
+ * ## Webhook URL
54
+ *
55
+ * The webhook points at `<hub-origin>/agent/api/vault/{inbound,agent-def}` — the
56
+ * hub origin + the agent module's `/agent` proxy mount + the action endpoint
57
+ * (mirrors the hub's `buildWebhook` and the `AGENT_*_VAULT_TRIGGER_TEMPLATE`
58
+ * placeholders). The hub reverse-proxies `/agent/*` to the loopback daemon, so
59
+ * this works co-located AND exposed; and the daemon validates the webhook bearer's
60
+ * `iss` against the hub origin anyway, so the hub origin is the right base.
61
+ *
62
+ * Best-effort throughout: every failure is caught + logged, never thrown — a
63
+ * def-vault with no admin authority (or an unreachable vault) must never block the
64
+ * daemon from serving, and the poll fallback covers reactivity.
65
+ */
66
+
67
+ import type { DefVaultBinding } from "./agent-defs.ts";
68
+ import { mintScopedToken, vaultScope, MintError } from "./mint-token.ts";
69
+ import { DEFAULT_DEF_VAULT_URL } from "./def-vaults.ts";
70
+ import { AGENT_VAULT_TRIGGER_TEMPLATE } from "./transports/vault.ts";
71
+
72
+ /** The bare def-discriminator tag the def-watch triggers filter on (post-canonicalization). */
73
+ export const DEFINITION_TAG = "agent/definition";
74
+ /**
75
+ * The inbound trigger's `when` predicate. SOURCED from the existing
76
+ * {@link AGENT_VAULT_TRIGGER_TEMPLATE} (`src/transports/vault.ts`) — the
77
+ * module-owned shape the hub already substitutes — so the daemon's
78
+ * auto-registration produces a SEMANTICALLY IDENTICAL trigger to the
79
+ * hub-Connections path (bare `agent/message/inbound` tag, `has_metadata:[agent]`,
80
+ * `missing_metadata:[channel_inbound_rendered_at]`). Reusing the one source of
81
+ * truth keeps the two registration paths from drifting on the field names.
82
+ *
83
+ * (The real loop guard is the vault engine's own `<triggerName>_rendered_at`
84
+ * marker, checked unconditionally regardless of `missing_metadata`. The
85
+ * `missing_metadata` clause is the module-owned belt — kept identical so both
86
+ * paths upsert cleanly over the same trigger.)
87
+ */
88
+ const INBOUND_WHEN = AGENT_VAULT_TRIGGER_TEMPLATE.when;
89
+ /** The bare inbound-message child tag the inbound trigger fires on (from the template). */
90
+ export const INBOUND_TAG = INBOUND_WHEN.tags[0];
91
+
92
+ /** The `agent:send` scope minted for every webhook `action.auth.bearer`. */
93
+ const WEBHOOK_SCOPE = "agent:send";
94
+ /** Short TTL for the throwaway `vault:<v>:admin` registration token. */
95
+ const ADMIN_MINT_TTL_SECONDS = 60;
96
+
97
+ /**
98
+ * The shape POSTed to `<vaultUrl>/vault/<v>/api/triggers`. The vault validates
99
+ * `events` ⊆ {created, updated} (NO `deleted` — so the def-watch is two triggers,
100
+ * create + edit, not one create/updated/deleted trigger).
101
+ */
102
+ export interface VaultTriggerInput {
103
+ name: string;
104
+ events: Array<"created" | "updated">;
105
+ when: {
106
+ tags: string[];
107
+ has_metadata?: string[];
108
+ missing_metadata?: string[];
109
+ };
110
+ action: {
111
+ webhook: string;
112
+ send: "json";
113
+ auth?: { bearer: string };
114
+ };
115
+ }
116
+
117
+ /**
118
+ * The stable def-watch trigger name for a (vault, kind). MUST match the hub's
119
+ * `conn_${defReloadId(vault, kind)}` (web/ui/src/lib/hub.ts → `agentdefs-<kind>-<vault>`,
120
+ * hub admin-connections → `conn_<id>`) so our upsert REPLACES any stale `#`-keyed
121
+ * trigger the hub provisioned, rather than orphaning it alongside a new one.
122
+ */
123
+ export function defWatchTriggerName(vault: string, kind: "create" | "edit"): string {
124
+ return `conn_agentdefs-${kind}-${vault}`;
125
+ }
126
+
127
+ /**
128
+ * The inbound trigger name for a vault. ONE per def-vault — it routes ALL agents
129
+ * by `metadata.agent`, so it isn't per-agent. Stable so the POST upserts in place.
130
+ */
131
+ export function inboundTriggerName(vault: string): string {
132
+ return `conn_agentinbound-${vault}`;
133
+ }
134
+
135
+ /** Build the webhook URL `<hub-origin>/agent/api/vault/<endpoint>`. */
136
+ function buildWebhook(hubOrigin: string, endpoint: string): string {
137
+ const origin = hubOrigin.replace(/\/+$/, "");
138
+ const ep = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
139
+ return `${origin}/agent${ep}`;
140
+ }
141
+
142
+ /**
143
+ * The three triggers a def-vault needs, bare-keyed, with the webhook bearer filled.
144
+ * Exported for tests (asserts the bare tag + the trigger shapes without the live mints).
145
+ */
146
+ export function buildDefVaultTriggers(
147
+ vault: string,
148
+ hubOrigin: string,
149
+ webhookBearer: string,
150
+ ): VaultTriggerInput[] {
151
+ const defWebhook = buildWebhook(hubOrigin, "/api/vault/agent-def");
152
+ const inboundWebhook = buildWebhook(hubOrigin, "/api/vault/inbound");
153
+ const auth = { bearer: webhookBearer };
154
+ return [
155
+ // Def-watch CREATE — a new bare `agent/definition` note instantiates its agent live.
156
+ {
157
+ name: defWatchTriggerName(vault, "create"),
158
+ events: ["created"],
159
+ when: { tags: [DEFINITION_TAG] },
160
+ action: { webhook: defWebhook, send: "json", auth },
161
+ },
162
+ // Def-watch EDIT — an edited bare `agent/definition` note re-instantiates its agent.
163
+ {
164
+ name: defWatchTriggerName(vault, "edit"),
165
+ events: ["updated"],
166
+ when: { tags: [DEFINITION_TAG] },
167
+ action: { webhook: defWebhook, send: "json", auth },
168
+ },
169
+ // INBOUND — a new bare `agent/message/inbound` note (routed by metadata.agent,
170
+ // not yet rendered) wakes the agent. One trigger routes every agent in the vault.
171
+ // `when` is the module-owned shape from AGENT_VAULT_TRIGGER_TEMPLATE (copied so
172
+ // the predicate matches the hub-Connections path exactly).
173
+ {
174
+ name: inboundTriggerName(vault),
175
+ events: ["created"],
176
+ when: {
177
+ tags: [...INBOUND_WHEN.tags],
178
+ has_metadata: [...INBOUND_WHEN.has_metadata],
179
+ missing_metadata: [...INBOUND_WHEN.missing_metadata],
180
+ },
181
+ action: { webhook: inboundWebhook, send: "json", auth },
182
+ },
183
+ ];
184
+ }
185
+
186
+ /** Dependencies for {@link registerDefVaultTriggers} (injected for tests). */
187
+ export interface RegisterTriggersDeps {
188
+ /** Hub public origin (the webhook base + the mint endpoint + the JWT `iss`). */
189
+ hubOrigin: string;
190
+ /** The operator bearer the per-resource mints attenuate against. */
191
+ managerBearer: string;
192
+ /** Inject fetch for tests. Defaults to global fetch. */
193
+ fetchFn?: typeof fetch;
194
+ }
195
+
196
+ /** The outcome of registering one def-vault's triggers (for logging/tests). */
197
+ export interface RegisterTriggersResult {
198
+ vault: string;
199
+ /** Trigger names that registered (HTTP 200). */
200
+ registered: string[];
201
+ /** `name: reason` for each failure (mint refusal, vault non-2xx, fetch error). */
202
+ failures: string[];
203
+ }
204
+
205
+ /**
206
+ * Register (idempotent upsert) the def-watch + inbound triggers for ONE def-vault.
207
+ * Mints a `vault:<v>:admin` token for the triggers API and an `agent:send` token
208
+ * for the webhook bearer, then POSTs each trigger. Best-effort: never throws — a
209
+ * mint refusal (insufficient operator authority) or an unreachable vault is logged
210
+ * and returned in `failures`. The poll fallback is the correctness floor.
211
+ */
212
+ export async function registerDefVaultTriggers(
213
+ binding: DefVaultBinding,
214
+ deps: RegisterTriggersDeps,
215
+ ): Promise<RegisterTriggersResult> {
216
+ const vault = binding.vault;
217
+ const vaultUrl = (binding.vaultUrl ?? DEFAULT_DEF_VAULT_URL).replace(/\/+$/, "");
218
+ const fetchFn = deps.fetchFn ?? fetch;
219
+ const result: RegisterTriggersResult = { vault, registered: [], failures: [] };
220
+
221
+ const mintDeps = {
222
+ hubOrigin: deps.hubOrigin,
223
+ managerBearer: deps.managerBearer,
224
+ ...(deps.fetchFn ? { fetchFn: deps.fetchFn } : {}),
225
+ };
226
+
227
+ // 1. Mint the ADMIN token for the triggers API FIRST (the def-vault write token
228
+ // can't register triggers — admin-scoped endpoint). This is the mint most
229
+ // likely to be refused (a `vault:<v>:write`-only operator can't mint admin), so
230
+ // minting it first short-circuits a restricted install before the second mint.
231
+ // On `invalid_scope` the hub refuses here — log + skip, never crash boot.
232
+ let adminToken: string;
233
+ try {
234
+ const minted = await mintScopedToken(
235
+ { scope: vaultScope(vault, "admin"), expiresIn: ADMIN_MINT_TTL_SECONDS },
236
+ mintDeps,
237
+ );
238
+ adminToken = minted.token;
239
+ } catch (err) {
240
+ const detail = err instanceof MintError ? err.message : (err as Error).message;
241
+ result.failures.push(`mint ${vaultScope(vault, "admin")}: ${detail}`);
242
+ return result; // No admin token → can't POST triggers.
243
+ }
244
+
245
+ // 2. Mint the webhook bearer (agent:send) — the trigger fires this at the daemon.
246
+ // Long-lived (the hub's default ~90d, matching the hub's own provisioning) since
247
+ // it lives in the trigger's persistent `action.auth.bearer`; re-minted on each
248
+ // restart's re-registration. A refusal here also skips (no bearer → no trigger).
249
+ let webhookBearer: string;
250
+ try {
251
+ const minted = await mintScopedToken({ scope: WEBHOOK_SCOPE }, mintDeps);
252
+ webhookBearer = minted.token;
253
+ } catch (err) {
254
+ const detail = err instanceof MintError ? err.message : (err as Error).message;
255
+ result.failures.push(`mint ${WEBHOOK_SCOPE}: ${detail}`);
256
+ return result; // No bearer → no trigger can be registered.
257
+ }
258
+
259
+ // 3. POST each trigger (upsert by name).
260
+ const triggers = buildDefVaultTriggers(vault, deps.hubOrigin, webhookBearer);
261
+ const url = `${vaultUrl}/vault/${vault}/api/triggers`;
262
+ for (const trigger of triggers) {
263
+ try {
264
+ const res = await fetchFn(url, {
265
+ method: "POST",
266
+ headers: {
267
+ "content-type": "application/json",
268
+ authorization: `Bearer ${adminToken}`,
269
+ },
270
+ body: JSON.stringify(trigger),
271
+ });
272
+ if (res.ok) {
273
+ result.registered.push(trigger.name);
274
+ } else {
275
+ const detail = await res.text().catch(() => "");
276
+ result.failures.push(`${trigger.name}: HTTP ${res.status} ${detail}`.trim());
277
+ }
278
+ } catch (err) {
279
+ result.failures.push(`${trigger.name}: ${(err as Error).message}`);
280
+ }
281
+ }
282
+ return result;
283
+ }
284
+
285
+ /**
286
+ * Register triggers for EVERY def-vault binding. Best-effort + sequential (a
287
+ * handful of vaults at most); a per-vault failure never blocks the others. Logs a
288
+ * one-line summary per vault. Returns the per-vault results (for tests).
289
+ */
290
+ export async function registerAllDefVaultTriggers(
291
+ bindings: DefVaultBinding[],
292
+ deps: RegisterTriggersDeps,
293
+ ): Promise<RegisterTriggersResult[]> {
294
+ const results: RegisterTriggersResult[] = [];
295
+ for (const binding of bindings) {
296
+ const r = await registerDefVaultTriggers(binding, deps).catch(
297
+ (err): RegisterTriggersResult => ({
298
+ vault: binding.vault,
299
+ registered: [],
300
+ failures: [`unexpected: ${(err as Error).message}`],
301
+ }),
302
+ );
303
+ results.push(r);
304
+ if (r.failures.length === 0) {
305
+ console.log(
306
+ `parachute-agent: def-vault "${r.vault}" — auto-registered ${r.registered.length} trigger(s) ` +
307
+ `(def-watch create/edit + inbound, bare-keyed).`,
308
+ );
309
+ } else {
310
+ console.warn(
311
+ `parachute-agent: def-vault "${r.vault}" — registered ${r.registered.length}, ` +
312
+ `${r.failures.length} failed (continuing; the 60s poll is the correctness floor): ${r.failures.join("; ")}`,
313
+ );
314
+ }
315
+ }
316
+ return results;
317
+ }
@@ -57,4 +57,4 @@ Error generating stack: `+a.message+`
57
57
  * @license MIT
58
58
  */var or=/^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i,cm=/^[\\/]{2}/;function mg(i,o){return o+i.replace(/\\/g,"/")}var Lh="popstate";function kh(i){return typeof i=="object"&&i!=null&&"pathname"in i&&"search"in i&&"hash"in i&&"state"in i&&"key"in i}function vg(i={}){function o(s,d){var v;let m=(v=d.state)==null?void 0:v.masked,{pathname:p,search:E,hash:S}=m||s.location;return ur("",{pathname:p,search:E,hash:S},d.state&&d.state.usr||null,d.state&&d.state.key||"default",m?{pathname:s.location.pathname,search:s.location.search,hash:s.location.hash}:void 0)}function f(s,d){return typeof d=="string"?d:Zn(d)}return gg(o,f,null,i)}function Me(i,o){if(i===!1||i===null||typeof i>"u")throw new Error(o)}function Gt(i,o){if(!i){typeof console<"u"&&console.warn(o);try{throw new Error(o)}catch{}}}function yg(){return Math.random().toString(36).substring(2,10)}function Yh(i,o){return{usr:i.state,key:i.key,idx:o,masked:i.mask?{pathname:i.pathname,search:i.search,hash:i.hash}:void 0}}function ur(i,o,f=null,s,d){return{pathname:typeof i=="string"?i:i.pathname,search:"",hash:"",...typeof o=="string"?Za(o):o,state:f,key:o&&o.key||s||yg(),mask:d}}function Zn({pathname:i="/",search:o="",hash:f=""}){return o&&o!=="?"&&(i+=o.charAt(0)==="?"?o:"?"+o),f&&f!=="#"&&(i+=f.charAt(0)==="#"?f:"#"+f),i}function Za(i){let o={};if(i){let f=i.indexOf("#");f>=0&&(o.hash=i.substring(f),i=i.substring(0,f));let s=i.indexOf("?");s>=0&&(o.search=i.substring(s),i=i.substring(0,s)),i&&(o.pathname=i)}return o}function gg(i,o,f,s={}){let{window:d=document.defaultView,v5Compat:m=!1}=s,p=d.history,E="POP",S=null,v=C();v==null&&(v=0,p.replaceState({...p.state,idx:v},""));function C(){return(p.state||{idx:null}).idx}function j(){E="POP";let X=C(),M=X==null?null:X-v;v=X,S&&S({action:E,location:L.location,delta:M})}function z(X,M){E="PUSH";let D=kh(X)?X:ur(L.location,X,M);v=C()+1;let G=Yh(D,v),Z=L.createHref(D.mask||D);try{p.pushState(G,"",Z)}catch($){if($ instanceof DOMException&&$.name==="DataCloneError")throw $;d.location.assign(Z)}m&&S&&S({action:E,location:L.location,delta:1})}function k(X,M){E="REPLACE";let D=kh(X)?X:ur(L.location,X,M);v=C();let G=Yh(D,v),Z=L.createHref(D.mask||D);p.replaceState(G,"",Z),m&&S&&S({action:E,location:L.location,delta:0})}function Y(X){return pg(d,X)}let L={get action(){return E},get location(){return i(d,p)},listen(X){if(S)throw new Error("A history only accepts one active listener");return d.addEventListener(Lh,j),S=X,()=>{d.removeEventListener(Lh,j),S=null}},createHref(X){return o(d,X)},createURL:Y,encodeLocation(X){let M=Y(X);return{pathname:M.pathname,search:M.search,hash:M.hash}},push:z,replace:k,go(X){return p.go(X)}};return L}function pg(i,o,f=!1){let s="http://localhost";i&&(s=i.location.origin!=="null"?i.location.origin:i.location.href),Me(s,"No window.location.(origin|href) available to create URL");let d=typeof o=="string"?o:Zn(o);return d=d.replace(/ $/,"%20"),!f&&cm.test(d)&&(d=s+d),new URL(d,s)}function sm(i,o,f="/"){return bg(i,o,f,!1)}function bg(i,o,f,s,d){let m=typeof o=="string"?Za(o):o,p=ol(m.pathname||"/",f);if(p==null)return null;let E=Sg(i),S=null,v=Dg(p);for(let C=0;S==null&&C<E.length;++C)S=_g(E[C],v,s);return S}function Sg(i){let o=rm(i);return xg(o),o}function rm(i,o=[],f=[],s="",d=!1){let m=(p,E,S=d,v)=>{let C={relativePath:v===void 0?p.path||"":v,caseSensitive:p.caseSensitive===!0,childrenIndex:E,route:p};if(C.relativePath.startsWith("/")){if(!C.relativePath.startsWith(s)&&S)return;Me(C.relativePath.startsWith(s),`Absolute route path "${C.relativePath}" nested under path "${s}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),C.relativePath=C.relativePath.slice(s.length)}let j=Ht([s,C.relativePath]),z=f.concat(C);p.children&&p.children.length>0&&(Me(p.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${j}".`),rm(p.children,o,z,j,S)),!(p.path==null&&!p.index)&&o.push({path:j,score:Rg(j,p.index),routesMeta:z.map((k,Y)=>{let[L,X]=dm(k.relativePath,k.caseSensitive,Y===z.length-1);return{...k,matcher:L,compiledParams:X}})})};return i.forEach((p,E)=>{var S;if(p.path===""||!((S=p.path)!=null&&S.includes("?")))m(p,E);else for(let v of om(p.path))m(p,E,!0,v)}),o}function om(i){let o=i.split("/");if(o.length===0)return[];let[f,...s]=o,d=f.endsWith("?"),m=f.replace(/\?$/,"");if(s.length===0)return d?[m,""]:[m];let p=om(s.join("/")),E=[];return E.push(...p.map(S=>S===""?m:[m,S].join("/"))),d&&E.push(...p),E.map(S=>i.startsWith("/")&&S===""?"/":S)}function xg(i){i.sort((o,f)=>o.score!==f.score?f.score-o.score:zg(o.routesMeta.map(s=>s.childrenIndex),f.routesMeta.map(s=>s.childrenIndex)))}var Eg=/^:[\w-]+$/,jg=3,Ng=2,Cg=1,Tg=10,Ag=-2,Gh=i=>i==="*";function Rg(i,o){let f=i.split("/"),s=f.length;return f.some(Gh)&&(s+=Ag),o&&(s+=Ng),f.filter(d=>!Gh(d)).reduce((d,m)=>d+(Eg.test(m)?jg:m===""?Cg:Tg),s)}function zg(i,o){return i.length===o.length&&i.slice(0,-1).every((s,d)=>s===o[d])?i[i.length-1]-o[o.length-1]:0}function _g(i,o,f=!1){let{routesMeta:s}=i,d={},m="/",p=[];for(let E=0;E<s.length;++E){let S=s[E],v=E===s.length-1,C=m==="/"?o:o.slice(m.length)||"/",j={path:S.relativePath,caseSensitive:S.caseSensitive,end:v},z=S.matcher&&S.compiledParams?fm(j,C,S.matcher,S.compiledParams):Si(j,C),k=S.route;if(!z&&v&&f&&!s[s.length-1].route.index&&(z=Si({path:S.relativePath,caseSensitive:S.caseSensitive,end:!1},C)),!z)return null;Object.assign(d,z.params),p.push({params:d,pathname:Ht([m,z.pathname]),pathnameBase:Ug(Ht([m,z.pathnameBase])),route:k}),z.pathnameBase!=="/"&&(m=Ht([m,z.pathnameBase]))}return p}function Si(i,o){typeof i=="string"&&(i={path:i,caseSensitive:!1,end:!0});let[f,s]=dm(i.path,i.caseSensitive,i.end);return fm(i,o,f,s)}function fm(i,o,f,s){let d=o.match(f);if(!d)return null;let m=d[0],p=m.replace(/(.)\/+$/,"$1"),E=d.slice(1);return{params:s.reduce((v,{paramName:C,isOptional:j},z)=>{if(C==="*"){let Y=E[z]||"";p=m.slice(0,m.length-Y.length).replace(/(.)\/+$/,"$1")}const k=E[z];return j&&!k?v[C]=void 0:v[C]=(k||"").replace(/%2F/g,"/"),v},{}),pathname:m,pathnameBase:p,pattern:i}}function dm(i,o=!1,f=!0){Gt(i==="*"||!i.endsWith("*")||i.endsWith("/*"),`Route path "${i}" will be treated as if it were "${i.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${i.replace(/\*$/,"/*")}".`);let s=[],d="^"+i.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(p,E,S,v,C)=>{if(s.push({paramName:E,isOptional:S!=null}),S){let j=C.charAt(v+p.length);return j&&j!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return i.endsWith("*")?(s.push({paramName:"*"}),d+=i==="*"||i==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):f?d+="\\/*$":i!==""&&i!=="/"&&(d+="(?:(?=\\/|$))"),[new RegExp(d,o?void 0:"i"),s]}function Dg(i){try{return i.split("/").map(o=>decodeURIComponent(o).replace(/\//g,"%2F")).join("/")}catch(o){return Gt(!1,`The URL path "${i}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${o}).`),i}}function ol(i,o){if(o==="/")return i;if(!i.toLowerCase().startsWith(o.toLowerCase()))return null;let f=o.endsWith("/")?o.length-1:o.length,s=i.charAt(f);return s&&s!=="/"?null:i.slice(f)||"/"}function Og(i,o="/"){let{pathname:f,search:s="",hash:d=""}=typeof i=="string"?Za(i):i,m;return f?(f=mm(f),f.startsWith("/")?m=Xh(f.substring(1),"/"):m=Xh(f,o)):m=o,{pathname:m,search:wg(s),hash:Hg(d)}}function Xh(i,o){let f=xi(o).split("/");return i.split("/").forEach(d=>{d===".."?f.length>1&&f.pop():d!=="."&&f.push(d)}),f.length>1?f.join("/"):"/"}function er(i,o,f,s){return`Cannot include a '${i}' character in a manually specified \`to.${o}\` field [${JSON.stringify(s)}]. Please separate it out to the \`to.${f}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`}function Mg(i){return i.filter((o,f)=>f===0||o.route.path&&o.route.path.length>0)}function hm(i){let o=Mg(i);return o.map((f,s)=>s===o.length-1?f.pathname:f.pathnameBase)}function fr(i,o,f,s=!1){let d;typeof i=="string"?d=Za(i):(d={...i},Me(!d.pathname||!d.pathname.includes("?"),er("?","pathname","search",d)),Me(!d.pathname||!d.pathname.includes("#"),er("#","pathname","hash",d)),Me(!d.search||!d.search.includes("#"),er("#","search","hash",d)));let m=i===""||d.pathname==="",p=m?"/":d.pathname,E;if(p==null)E=f;else{let j=o.length-1;if(!s&&p.startsWith("..")){let z=p.split("/");for(;z[0]==="..";)z.shift(),j-=1;d.pathname=z.join("/")}E=j>=0?o[j]:"/"}let S=Og(d,E),v=p&&p!=="/"&&p.endsWith("/"),C=(m||p===".")&&f.endsWith("/");return!S.pathname.endsWith("/")&&(v||C)&&(S.pathname+="/"),S}var mm=i=>i.replace(/[\\/]{2,}/g,"/"),Ht=i=>mm(i.join("/")),xi=i=>i.replace(/\/+$/,""),Ug=i=>xi(i).replace(/^\/*/,"/"),wg=i=>!i||i==="?"?"":i.startsWith("?")?i:"?"+i,Hg=i=>!i||i==="#"?"":i.startsWith("#")?i:"#"+i,Bg=class{constructor(i,o,f,s=!1){this.status=i,this.statusText=o||"",this.internal=s,f instanceof Error?(this.data=f.toString(),this.error=f):this.data=f}};function qg(i){return i!=null&&typeof i.status=="number"&&typeof i.statusText=="string"&&typeof i.internal=="boolean"&&"data"in i}function Lg(i){let o=i.map(f=>f.route.path).filter(Boolean);return Ht(o)||"/"}var vm=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function ym(i,o){let f=i;if(typeof f!="string"||!or.test(f))return{absoluteURL:void 0,isExternal:!1,to:f};let s=f,d=!1;if(vm)try{let m=new URL(window.location.href),p=cm.test(f)?new URL(mg(f,m.protocol)):new URL(f),E=ol(p.pathname,o);p.origin===m.origin&&E!=null?f=E+p.search+p.hash:d=!0}catch{Gt(!1,`<Link to="${f}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:s,isExternal:d,to:f}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var gm=["POST","PUT","PATCH","DELETE"];new Set(gm);var kg=["GET",...gm];new Set(kg);var Yg=["about:","blob:","chrome:","chrome-untrusted:","content:","data:","devtools:","file:","filesystem:","javascript:"];function Gg(i){try{return Yg.includes(new URL(i).protocol)}catch{return!1}}var Ka=y.createContext(null);Ka.displayName="DataRouter";var ji=y.createContext(null);ji.displayName="DataRouterState";var pm=y.createContext(!1);function Xg(){return y.useContext(pm)}var bm=y.createContext({isTransitioning:!1});bm.displayName="ViewTransition";var Qg=y.createContext(new Map);Qg.displayName="Fetchers";var Vg=y.createContext(null);Vg.displayName="Await";var Ot=y.createContext(null);Ot.displayName="Navigation";var Jn=y.createContext(null);Jn.displayName="Location";var Xt=y.createContext({outlet:null,matches:[],isDataRoute:!1});Xt.displayName="Route";var dr=y.createContext(null);dr.displayName="RouteError";var Sm="REACT_ROUTER_ERROR",Zg="REDIRECT",Kg="ROUTE_ERROR_RESPONSE";function Jg(i){if(i.startsWith(`${Sm}:${Zg}:{`))try{let o=JSON.parse(i.slice(28));if(typeof o=="object"&&o&&typeof o.status=="number"&&typeof o.statusText=="string"&&typeof o.location=="string"&&typeof o.reloadDocument=="boolean"&&typeof o.replace=="boolean")return o}catch{}}function $g(i){if(i.startsWith(`${Sm}:${Kg}:{`))try{let o=JSON.parse(i.slice(40));if(typeof o=="object"&&o&&typeof o.status=="number"&&typeof o.statusText=="string")return new Bg(o.status,o.statusText,o.data)}catch{}}function Wg(i,{relative:o}={}){Me($n(),"useHref() may be used only in the context of a <Router> component.");let{basename:f,navigator:s}=y.useContext(Ot),{hash:d,pathname:m,search:p}=Wn(i,{relative:o}),E=m;return f!=="/"&&(E=m==="/"?f:Ht([f,m])),s.createHref({pathname:E,search:p,hash:d})}function $n(){return y.useContext(Jn)!=null}function Bt(){return Me($n(),"useLocation() may be used only in the context of a <Router> component."),y.useContext(Jn).location}var xm="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Em(i){y.useContext(Ot).static||y.useLayoutEffect(i)}function jm(){let{isDataRoute:i}=y.useContext(Xt);return i?op():Fg()}function Fg(){Me($n(),"useNavigate() may be used only in the context of a <Router> component.");let i=y.useContext(Ka),{basename:o,navigator:f}=y.useContext(Ot),{matches:s}=y.useContext(Xt),{pathname:d}=Bt(),m=JSON.stringify(hm(s)),p=y.useRef(!1);return Em(()=>{p.current=!0}),y.useCallback((S,v={})=>{if(Gt(p.current,xm),!p.current)return;if(typeof S=="number"){f.go(S);return}let C=fr(S,JSON.parse(m),d,v.relative==="path");i==null&&o!=="/"&&(C.pathname=C.pathname==="/"?o:Ht([o,C.pathname])),(v.replace?f.replace:f.push)(C,v.state,v)},[o,f,m,d,i])}y.createContext(null);function Ig(){let{matches:i}=y.useContext(Xt),o=i[i.length-1];return(o==null?void 0:o.params)??{}}function Wn(i,{relative:o}={}){let{matches:f}=y.useContext(Xt),{pathname:s}=Bt(),d=JSON.stringify(hm(f));return y.useMemo(()=>fr(i,JSON.parse(d),s,o==="path"),[i,d,s,o])}function Pg(i,o){return Nm(i,o)}function Nm(i,o,f){var X;Me($n(),"useRoutes() may be used only in the context of a <Router> component.");let{navigator:s}=y.useContext(Ot),{matches:d}=y.useContext(Xt),m=d[d.length-1],p=m?m.params:{},E=m?m.pathname:"/",S=m?m.pathnameBase:"/",v=m&&m.route;{let M=v&&v.path||"";Tm(E,!v||M.endsWith("*")||M.endsWith("*?"),`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${E}" (under <Route path="${M}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
59
59
 
60
- Please change the parent <Route path="${M}"> to <Route path="${M==="/"?"*":`${M}/*`}">.`)}let C=Bt(),j;if(o){let M=typeof o=="string"?Za(o):o;Me(S==="/"||((X=M.pathname)==null?void 0:X.startsWith(S)),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${S}" but pathname "${M.pathname}" was given in the \`location\` prop.`),j=M}else j=C;let z=j.pathname||"/",k=z;if(S!=="/"){let M=S.replace(/^\//,"").split("/");k="/"+z.replace(/^\//,"").split("/").slice(M.length).join("/")}let Y=f&&f.state.matches.length?f.state.matches.map(M=>Object.assign(M,{route:f.manifest[M.route.id]||M.route})):sm(i,{pathname:k});Gt(v||Y!=null,`No routes matched location "${j.pathname}${j.search}${j.hash}" `),Gt(Y==null||Y[Y.length-1].route.element!==void 0||Y[Y.length-1].route.Component!==void 0||Y[Y.length-1].route.lazy!==void 0,`Matched leaf route at location "${j.pathname}${j.search}${j.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let L=np(Y&&Y.map(M=>Object.assign({},M,{params:Object.assign({},p,M.params),pathname:Ht([S,s.encodeLocation?s.encodeLocation(M.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathname]),pathnameBase:M.pathnameBase==="/"?S:Ht([S,s.encodeLocation?s.encodeLocation(M.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathnameBase])})),d,f);return o&&L?y.createElement(Jn.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...j},navigationType:"POP"}},L):L}function ep(){let i=rp(),o=qg(i)?`${i.status} ${i.statusText}`:i instanceof Error?i.message:JSON.stringify(i),f=i instanceof Error?i.stack:null,s="rgba(200,200,200, 0.5)",d={padding:"0.5rem",backgroundColor:s},m={padding:"2px 4px",backgroundColor:s},p=null;return console.error("Error handled by React Router default ErrorBoundary:",i),p=y.createElement(y.Fragment,null,y.createElement("p",null,"💿 Hey developer 👋"),y.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",y.createElement("code",{style:m},"ErrorBoundary")," or"," ",y.createElement("code",{style:m},"errorElement")," prop on your route.")),y.createElement(y.Fragment,null,y.createElement("h2",null,"Unexpected Application Error!"),y.createElement("h3",{style:{fontStyle:"italic"}},o),f?y.createElement("pre",{style:d},f):null,p)}var tp=y.createElement(ep,null),Cm=class extends y.Component{constructor(i){super(i),this.state={location:i.location,revalidation:i.revalidation,error:i.error}}static getDerivedStateFromError(i){return{error:i}}static getDerivedStateFromProps(i,o){return o.location!==i.location||o.revalidation!=="idle"&&i.revalidation==="idle"?{error:i.error,location:i.location,revalidation:i.revalidation}:{error:i.error!==void 0?i.error:o.error,location:o.location,revalidation:i.revalidation||o.revalidation}}componentDidCatch(i,o){this.props.onError?this.props.onError(i,o):console.error("React Router caught the following error during render",i)}render(){let i=this.state.error;if(this.context&&typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){const f=$g(i.digest);f&&(i=f)}let o=i!==void 0?y.createElement(Xt.Provider,{value:this.props.routeContext},y.createElement(dr.Provider,{value:i,children:this.props.component})):this.props.children;return this.context?y.createElement(lp,{error:i},o):o}};Cm.contextType=pm;var tr=new WeakMap;function lp({children:i,error:o}){let{basename:f}=y.useContext(Ot);if(typeof o=="object"&&o&&"digest"in o&&typeof o.digest=="string"){let s=Jg(o.digest);if(s){let d=tr.get(o);if(d)throw d;let m=ym(s.location,f),p=m.absoluteURL||m.to;if(Gg(p))throw new Error("Invalid redirect location");if(vm&&!tr.get(o))if(m.isExternal||s.reloadDocument)window.location.href=p;else{const E=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(m.to,{replace:s.replace}));throw tr.set(o,E),E}return y.createElement("meta",{httpEquiv:"refresh",content:`0;url=${p}`})}}return i}function ap({routeContext:i,match:o,children:f}){let s=y.useContext(Ka);return s&&s.static&&s.staticContext&&(o.route.errorElement||o.route.ErrorBoundary)&&(s.staticContext._deepestRenderedBoundaryId=o.route.id),y.createElement(Xt.Provider,{value:i},f)}function np(i,o=[],f){let s=f==null?void 0:f.state;if(i==null){if(!s)return null;if(s.errors)i=s.matches;else if(o.length===0&&!s.initialized&&s.matches.length>0)i=s.matches;else return null}let d=i,m=s==null?void 0:s.errors;if(m!=null){let C=d.findIndex(j=>j.route.id&&(m==null?void 0:m[j.route.id])!==void 0);Me(C>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(m).join(",")}`),d=d.slice(0,Math.min(d.length,C+1))}let p=!1,E=-1;if(f&&s){p=s.renderFallback;for(let C=0;C<d.length;C++){let j=d[C];if((j.route.HydrateFallback||j.route.hydrateFallbackElement)&&(E=C),j.route.id){let{loaderData:z,errors:k}=s,Y=j.route.loader&&!z.hasOwnProperty(j.route.id)&&(!k||k[j.route.id]===void 0);if(j.route.lazy||Y){f.isStatic&&(p=!0),E>=0?d=d.slice(0,E+1):d=[d[0]];break}}}}let S=f==null?void 0:f.onError,v=s&&S?(C,j)=>{var z,k;S(C,{location:s.location,params:((k=(z=s.matches)==null?void 0:z[0])==null?void 0:k.params)??{},pattern:Lg(s.matches),errorInfo:j})}:void 0;return d.reduceRight((C,j,z)=>{let k,Y=!1,L=null,X=null;s&&(k=m&&j.route.id?m[j.route.id]:void 0,L=j.route.errorElement||tp,p&&(E<0&&z===0?(Tm("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),Y=!0,X=null):E===z&&(Y=!0,X=j.route.hydrateFallbackElement||null)));let M=o.concat(d.slice(0,z+1)),D=()=>{let G;return k?G=L:Y?G=X:j.route.Component?G=y.createElement(j.route.Component,null):j.route.element?G=j.route.element:G=C,y.createElement(ap,{match:j,routeContext:{outlet:C,matches:M,isDataRoute:s!=null},children:G})};return s&&(j.route.ErrorBoundary||j.route.errorElement||z===0)?y.createElement(Cm,{location:s.location,revalidation:s.revalidation,component:L,error:k,children:D(),routeContext:{outlet:null,matches:M,isDataRoute:!0},onError:v}):D()},null)}function hr(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function up(i){let o=y.useContext(Ka);return Me(o,hr(i)),o}function ip(i){let o=y.useContext(ji);return Me(o,hr(i)),o}function cp(i){let o=y.useContext(Xt);return Me(o,hr(i)),o}function mr(i){let o=cp(i),f=o.matches[o.matches.length-1];return Me(f.route.id,`${i} can only be used on routes that contain a unique "id"`),f.route.id}function sp(){return mr("useRouteId")}function rp(){var s;let i=y.useContext(dr),o=ip("useRouteError"),f=mr("useRouteError");return i!==void 0?i:(s=o.errors)==null?void 0:s[f]}function op(){let{router:i}=up("useNavigate"),o=mr("useNavigate"),f=y.useRef(!1);return Em(()=>{f.current=!0}),y.useCallback(async(d,m={})=>{Gt(f.current,xm),f.current&&(typeof d=="number"?await i.navigate(d):await i.navigate(d,{fromRouteId:o,...m}))},[i,o])}var Qh={};function Tm(i,o,f){!o&&!Qh[i]&&(Qh[i]=!0,Gt(!1,f))}y.memo(fp);function fp({routes:i,manifest:o,future:f,state:s,isStatic:d,onError:m}){return Nm(i,void 0,{manifest:o,state:s,isStatic:d,onError:m})}function na(i){Me(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function dp({basename:i="/",children:o=null,location:f,navigationType:s="POP",navigator:d,static:m=!1,useTransitions:p}){Me(!$n(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let E=i.replace(/^\/*/,"/"),S=y.useMemo(()=>({basename:E,navigator:d,static:m,useTransitions:p,future:{}}),[E,d,m,p]);typeof f=="string"&&(f=Za(f));let{pathname:v="/",search:C="",hash:j="",state:z=null,key:k="default",mask:Y}=f,L=y.useMemo(()=>{let X=ol(v,E);return X==null?null:{location:{pathname:X,search:C,hash:j,state:z,key:k,mask:Y},navigationType:s}},[E,v,C,j,z,k,s,Y]);return Gt(L!=null,`<Router basename="${E}"> is not able to match the URL "${v}${C}${j}" because it does not start with the basename, so the <Router> won't render anything.`),L==null?null:y.createElement(Ot.Provider,{value:S},y.createElement(Jn.Provider,{children:o,value:L}))}function hp({children:i,location:o}){return Pg(ir(i),o)}function ir(i,o=[]){let f=[];return y.Children.forEach(i,(s,d)=>{if(!y.isValidElement(s))return;let m=[...o,d];if(s.type===y.Fragment){f.push.apply(f,ir(s.props.children,m));return}Me(s.type===na,`[${typeof s.type=="string"?s.type:s.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),Me(!s.props.index||!s.props.children,"An index route cannot have child routes.");let p={id:s.props.id||m.join("-"),caseSensitive:s.props.caseSensitive,element:s.props.element,Component:s.props.Component,index:s.props.index,path:s.props.path,middleware:s.props.middleware,loader:s.props.loader,action:s.props.action,hydrateFallbackElement:s.props.hydrateFallbackElement,HydrateFallback:s.props.HydrateFallback,errorElement:s.props.errorElement,ErrorBoundary:s.props.ErrorBoundary,hasErrorBoundary:s.props.hasErrorBoundary===!0||s.props.ErrorBoundary!=null||s.props.errorElement!=null,shouldRevalidate:s.props.shouldRevalidate,handle:s.props.handle,lazy:s.props.lazy};s.props.children&&(p.children=ir(s.props.children,m)),f.push(p)}),f}var gi="get",pi="application/x-www-form-urlencoded";function Ni(i){return typeof HTMLElement<"u"&&i instanceof HTMLElement}function mp(i){return Ni(i)&&i.tagName.toLowerCase()==="button"}function vp(i){return Ni(i)&&i.tagName.toLowerCase()==="form"}function yp(i){return Ni(i)&&i.tagName.toLowerCase()==="input"}function gp(i){return!!(i.metaKey||i.altKey||i.ctrlKey||i.shiftKey)}function pp(i,o){return i.button===0&&(!o||o==="_self")&&!gp(i)}var vi=null;function bp(){if(vi===null)try{new FormData(document.createElement("form"),0),vi=!1}catch{vi=!0}return vi}var Sp=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function lr(i){return i!=null&&!Sp.has(i)?(Gt(!1,`"${i}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${pi}"`),null):i}function xp(i,o){let f,s,d,m,p;if(vp(i)){let E=i.getAttribute("action");s=E?ol(E,o):null,f=i.getAttribute("method")||gi,d=lr(i.getAttribute("enctype"))||pi,m=new FormData(i)}else if(mp(i)||yp(i)&&(i.type==="submit"||i.type==="image")){let E=i.form;if(E==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let S=i.getAttribute("formaction")||E.getAttribute("action");if(s=S?ol(S,o):null,f=i.getAttribute("formmethod")||E.getAttribute("method")||gi,d=lr(i.getAttribute("formenctype"))||lr(E.getAttribute("enctype"))||pi,m=new FormData(E,i),!bp()){let{name:v,type:C,value:j}=i;if(C==="image"){let z=v?`${v}.`:"";m.append(`${z}x`,"0"),m.append(`${z}y`,"0")}else v&&m.append(v,j)}}else{if(Ni(i))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');f=gi,s=null,d=pi,p=i}return m&&d==="text/plain"&&(p=m,m=void 0),{action:s,method:f.toLowerCase(),encType:d,formData:m,body:p}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function vr(i,o){if(i===!1||i===null||typeof i>"u")throw new Error(o)}function Am(i,o,f,s){let d=typeof i=="string"?new URL(i,typeof window>"u"?"server://singlefetch/":window.location.origin):i;return f?d.pathname.endsWith("/")?d.pathname=`${d.pathname}_.${s}`:d.pathname=`${d.pathname}.${s}`:d.pathname==="/"?d.pathname=`_root.${s}`:o&&ol(d.pathname,o)==="/"?d.pathname=`${xi(o)}/_root.${s}`:d.pathname=`${xi(d.pathname)}.${s}`,d}async function Ep(i,o){if(i.id in o)return o[i.id];try{let f=await import(i.module);return o[i.id]=f,f}catch(f){return console.error(`Error loading route module \`${i.module}\`, reloading page...`),console.error(f),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function jp(i){return i==null?!1:i.href==null?i.rel==="preload"&&typeof i.imageSrcSet=="string"&&typeof i.imageSizes=="string":typeof i.rel=="string"&&typeof i.href=="string"}async function Np(i,o,f){let s=await Promise.all(i.map(async d=>{let m=o.routes[d.route.id];if(m){let p=await Ep(m,f);return p.links?p.links():[]}return[]}));return Rp(s.flat(1).filter(jp).filter(d=>d.rel==="stylesheet"||d.rel==="preload").map(d=>d.rel==="stylesheet"?{...d,rel:"prefetch",as:"style"}:{...d,rel:"prefetch"}))}function Vh(i,o,f,s,d,m){let p=(S,v)=>f[v]?S.route.id!==f[v].route.id:!0,E=(S,v)=>{var C;return f[v].pathname!==S.pathname||((C=f[v].route.path)==null?void 0:C.endsWith("*"))&&f[v].params["*"]!==S.params["*"]};return m==="assets"?o.filter((S,v)=>p(S,v)||E(S,v)):m==="data"?o.filter((S,v)=>{var j;let C=s.routes[S.route.id];if(!C||!C.hasLoader)return!1;if(p(S,v)||E(S,v))return!0;if(S.route.shouldRevalidate){let z=S.route.shouldRevalidate({currentUrl:new URL(d.pathname+d.search+d.hash,window.origin),currentParams:((j=f[0])==null?void 0:j.params)||{},nextUrl:new URL(i,window.origin),nextParams:S.params,defaultShouldRevalidate:!0});if(typeof z=="boolean")return z}return!0}):[]}function Cp(i,o,{includeHydrateFallback:f}={}){return Tp(i.map(s=>{let d=o.routes[s.route.id];if(!d)return[];let m=[d.module];return d.clientActionModule&&(m=m.concat(d.clientActionModule)),d.clientLoaderModule&&(m=m.concat(d.clientLoaderModule)),f&&d.hydrateFallbackModule&&(m=m.concat(d.hydrateFallbackModule)),d.imports&&(m=m.concat(d.imports)),m}).flat(1))}function Tp(i){return[...new Set(i)]}function Ap(i){let o={},f=Object.keys(i).sort();for(let s of f)o[s]=i[s];return o}function Rp(i,o){let f=new Set;return new Set(o),i.reduce((s,d)=>{let m=JSON.stringify(Ap(d));return f.has(m)||(f.add(m),s.push({key:m,link:d})),s},[])}function yr(){let i=y.useContext(Ka);return vr(i,"You must render this element inside a <DataRouterContext.Provider> element"),i}function zp(){let i=y.useContext(ji);return vr(i,"You must render this element inside a <DataRouterStateContext.Provider> element"),i}var gr=y.createContext(void 0);gr.displayName="FrameworkContext";function Ci(){let i=y.useContext(gr);return vr(i,"You must render this element inside a <HydratedRouter> element"),i}function _p(i,o){let f=y.useContext(gr),[s,d]=y.useState(!1),[m,p]=y.useState(!1),{onFocus:E,onBlur:S,onMouseEnter:v,onMouseLeave:C,onTouchStart:j}=o,z=y.useRef(null);y.useEffect(()=>{if(i==="render"&&p(!0),i==="viewport"){let L=M=>{M.forEach(D=>{p(D.isIntersecting)})},X=new IntersectionObserver(L,{threshold:.5});return z.current&&X.observe(z.current),()=>{X.disconnect()}}},[i]),y.useEffect(()=>{if(s){let L=setTimeout(()=>{p(!0)},100);return()=>{clearTimeout(L)}}},[s]);let k=()=>{d(!0)},Y=()=>{d(!1),p(!1)};return f?i!=="intent"?[m,z,{}]:[m,z,{onFocus:Qn(E,k),onBlur:Qn(S,Y),onMouseEnter:Qn(v,k),onMouseLeave:Qn(C,Y),onTouchStart:Qn(j,k)}]:[!1,z,{}]}function Qn(i,o){return f=>{i&&i(f),f.defaultPrevented||o(f)}}function Dp({page:i,...o}){let f=Xg(),{nonce:s}=Ci(),{router:d}=yr(),m=y.useMemo(()=>sm(d.routes,i,d.basename),[d.routes,i,d.basename]);return m?(o.nonce==null&&s&&(o={...o,nonce:s}),f?y.createElement(Mp,{page:i,matches:m,...o}):y.createElement(Up,{page:i,matches:m,...o})):null}function Op(i){let{manifest:o,routeModules:f}=Ci(),[s,d]=y.useState([]);return y.useEffect(()=>{let m=!1;return Np(i,o,f).then(p=>{m||d(p)}),()=>{m=!0}},[i,o,f]),s}function Mp({page:i,matches:o,...f}){let s=Bt(),{future:d}=Ci(),{basename:m}=yr(),p=y.useMemo(()=>{if(i===s.pathname+s.search+s.hash)return[];let E=Am(i,m,d.v8_trailingSlashAwareDataRequests,"rsc"),S=!1,v=[];for(let C of o)typeof C.route.shouldRevalidate=="function"?S=!0:v.push(C.route.id);return S&&v.length>0&&E.searchParams.set("_routes",v.join(",")),[E.pathname+E.search]},[m,d.v8_trailingSlashAwareDataRequests,i,s,o]);return y.createElement(y.Fragment,null,p.map(E=>y.createElement("link",{key:E,rel:"prefetch",as:"fetch",href:E,...f})))}function Up({page:i,matches:o,...f}){let s=Bt(),{future:d,manifest:m,routeModules:p}=Ci(),{basename:E}=yr(),{loaderData:S,matches:v}=zp(),C=y.useMemo(()=>Vh(i,o,v,m,s,"data"),[i,o,v,m,s]),j=y.useMemo(()=>Vh(i,o,v,m,s,"assets"),[i,o,v,m,s]),z=y.useMemo(()=>{if(i===s.pathname+s.search+s.hash)return[];let L=new Set,X=!1;if(o.forEach(D=>{var Z;let G=m.routes[D.route.id];!G||!G.hasLoader||(!C.some($=>$.route.id===D.route.id)&&D.route.id in S&&((Z=p[D.route.id])!=null&&Z.shouldRevalidate)||G.hasClientLoader?X=!0:L.add(D.route.id))}),L.size===0)return[];let M=Am(i,E,d.v8_trailingSlashAwareDataRequests,"data");return X&&L.size>0&&M.searchParams.set("_routes",o.filter(D=>L.has(D.route.id)).map(D=>D.route.id).join(",")),[M.pathname+M.search]},[E,d.v8_trailingSlashAwareDataRequests,S,s,m,C,o,i,p]),k=y.useMemo(()=>Cp(j,m),[j,m]),Y=Op(j);return y.createElement(y.Fragment,null,z.map(L=>y.createElement("link",{key:L,rel:"prefetch",as:"fetch",href:L,...f})),k.map(L=>y.createElement("link",{key:L,rel:"modulepreload",href:L,...f})),Y.map(({key:L,link:X})=>y.createElement("link",{key:L,nonce:f.nonce,...X,crossOrigin:X.crossOrigin??f.crossOrigin})))}function wp(...i){return o=>{i.forEach(f=>{typeof f=="function"?f(o):f!=null&&(f.current=o)})}}var Hp=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{Hp&&(window.__reactRouterVersion="7.18.0")}catch{}function Bp({basename:i,children:o,useTransitions:f,window:s}){let d=y.useRef();d.current==null&&(d.current=vg({window:s,v5Compat:!0}));let m=d.current,[p,E]=y.useState({action:m.action,location:m.location}),S=y.useCallback(v=>{f===!1?E(v):y.startTransition(()=>E(v))},[f]);return y.useLayoutEffect(()=>m.listen(S),[m,S]),y.createElement(dp,{basename:i,children:o,location:p.location,navigationType:p.action,navigator:m,useTransitions:f})}var fl=y.forwardRef(function({onClick:o,discover:f="render",prefetch:s="none",relative:d,reloadDocument:m,replace:p,mask:E,state:S,target:v,to:C,preventScrollReset:j,viewTransition:z,defaultShouldRevalidate:k,...Y},L){let{basename:X,navigator:M,useTransitions:D}=y.useContext(Ot),G=typeof C=="string"&&or.test(C),Z=ym(C,X);C=Z.to;let $=Wg(C,{relative:d}),ue=Bt(),V=null;if(E){let De=fr(E,[],ue.mask?ue.mask.pathname:"/",!0);X!=="/"&&(De.pathname=De.pathname==="/"?X:Ht([X,De.pathname])),V=M.createHref(De)}let[I,P,Re]=_p(s,Y),le=Yp(C,{replace:p,mask:E,state:S,target:v,preventScrollReset:j,relative:d,viewTransition:z,defaultShouldRevalidate:k,useTransitions:D});function xe(De){o&&o(De),De.defaultPrevented||le(De)}let Ie=!(Z.isExternal||m),Ze=y.createElement("a",{...Y,...Re,href:(Ie?V:void 0)||Z.absoluteURL||$,onClick:Ie?xe:o,ref:wp(L,P),target:v,"data-discover":!G&&f==="render"?"true":void 0});return I&&!G?y.createElement(y.Fragment,null,Ze,y.createElement(Dp,{page:$})):Ze});fl.displayName="Link";var qp=y.forwardRef(function({"aria-current":o="page",caseSensitive:f=!1,className:s="",end:d=!1,style:m,to:p,viewTransition:E,children:S,...v},C){let j=Wn(p,{relative:v.relative}),z=Bt(),k=y.useContext(ji),{navigator:Y,basename:L}=y.useContext(Ot),X=k!=null&&Zp(j)&&E===!0,M=Y.encodeLocation?Y.encodeLocation(j).pathname:j.pathname,D=z.pathname,G=k&&k.navigation&&k.navigation.location?k.navigation.location.pathname:null;f||(D=D.toLowerCase(),G=G?G.toLowerCase():null,M=M.toLowerCase()),G&&L&&(G=ol(G,L)||G);const Z=M!=="/"&&M.endsWith("/")?M.length-1:M.length;let $=D===M||!d&&D.startsWith(M)&&D.charAt(Z)==="/",ue=G!=null&&(G===M||!d&&G.startsWith(M)&&G.charAt(M.length)==="/"),V={isActive:$,isPending:ue,isTransitioning:X},I=$?o:void 0,P;typeof s=="function"?P=s(V):P=[s,$?"active":null,ue?"pending":null,X?"transitioning":null].filter(Boolean).join(" ");let Re=typeof m=="function"?m(V):m;return y.createElement(fl,{...v,"aria-current":I,className:P,ref:C,style:Re,to:p,viewTransition:E},typeof S=="function"?S(V):S)});qp.displayName="NavLink";var Lp=y.forwardRef(({discover:i="render",fetcherKey:o,navigate:f,reloadDocument:s,replace:d,state:m,method:p=gi,action:E,onSubmit:S,relative:v,preventScrollReset:C,viewTransition:j,defaultShouldRevalidate:z,...k},Y)=>{let{useTransitions:L}=y.useContext(Ot),X=Qp(),M=Vp(E,{relative:v}),D=p.toLowerCase()==="get"?"get":"post",G=typeof E=="string"&&or.test(E),Z=$=>{if(S&&S($),$.defaultPrevented)return;$.preventDefault();let ue=$.nativeEvent.submitter,V=(ue==null?void 0:ue.getAttribute("formmethod"))||p,I=()=>X(ue||$.currentTarget,{fetcherKey:o,method:V,navigate:f,replace:d,state:m,relative:v,preventScrollReset:C,viewTransition:j,defaultShouldRevalidate:z});L&&f!==!1?y.startTransition(()=>I()):I()};return y.createElement("form",{ref:Y,method:D,action:M,onSubmit:s?S:Z,...k,"data-discover":!G&&i==="render"?"true":void 0})});Lp.displayName="Form";function kp(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Rm(i){let o=y.useContext(Ka);return Me(o,kp(i)),o}function Yp(i,{target:o,replace:f,mask:s,state:d,preventScrollReset:m,relative:p,viewTransition:E,defaultShouldRevalidate:S,useTransitions:v}={}){let C=jm(),j=Bt(),z=Wn(i,{relative:p});return y.useCallback(k=>{if(pp(k,o)){k.preventDefault();let Y=f!==void 0?f:Zn(j)===Zn(z),L=()=>C(i,{replace:Y,mask:s,state:d,preventScrollReset:m,relative:p,viewTransition:E,defaultShouldRevalidate:S});v?y.startTransition(()=>L()):L()}},[j,C,z,f,s,d,o,i,m,p,E,S,v])}var Gp=0,Xp=()=>`__${String(++Gp)}__`;function Qp(){let{router:i}=Rm("useSubmit"),{basename:o}=y.useContext(Ot),f=sp(),s=i.fetch,d=i.navigate;return y.useCallback(async(m,p={})=>{let{action:E,method:S,encType:v,formData:C,body:j}=xp(m,o);if(p.navigate===!1){let z=p.fetcherKey||Xp();await s(z,f,p.action||E,{defaultShouldRevalidate:p.defaultShouldRevalidate,preventScrollReset:p.preventScrollReset,formData:C,body:j,formMethod:p.method||S,formEncType:p.encType||v,flushSync:p.flushSync})}else await d(p.action||E,{defaultShouldRevalidate:p.defaultShouldRevalidate,preventScrollReset:p.preventScrollReset,formData:C,body:j,formMethod:p.method||S,formEncType:p.encType||v,replace:p.replace,state:p.state,fromRouteId:f,flushSync:p.flushSync,viewTransition:p.viewTransition})},[s,d,o,f])}function Vp(i,{relative:o}={}){let{basename:f}=y.useContext(Ot),s=y.useContext(Xt);Me(s,"useFormAction must be used inside a RouteContext");let[d]=s.matches.slice(-1),m={...Wn(i||".",{relative:o})},p=Bt();if(i==null){m.search=p.search;let E=new URLSearchParams(m.search),S=E.getAll("index");if(S.some(C=>C==="")){E.delete("index"),S.filter(j=>j).forEach(j=>E.append("index",j));let C=E.toString();m.search=C?`?${C}`:""}}return(!i||i===".")&&d.route.index&&(m.search=m.search?m.search.replace(/^\?/,"?index&"):"?index"),f!=="/"&&(m.pathname=m.pathname==="/"?f:Ht([f,m.pathname])),Zn(m)}function Zp(i,{relative:o}={}){let f=y.useContext(bm);Me(f!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:s}=Rm("useViewTransitionState"),d=Wn(i,{relative:o});if(!f.isTransitioning)return!1;let m=ol(f.currentLocation.pathname,s)||f.currentLocation.pathname,p=ol(f.nextLocation.pathname,s)||f.nextLocation.pathname;return Si(d.pathname,p)!=null||Si(d.pathname,m)!=null}let rl=null,Vn=null;const Kp=3e4,Jp=600*1e3;function $p(){return`${typeof window<"u"?window.location.origin:""}/admin/agent-token`}async function Va(){const i=Date.now();return rl&&rl.expiresAt-i>Kp?rl.token:Vn||(Vn=Wp().finally(()=>{Vn=null}),Vn)}async function Wp(){let i;try{i=await fetch($p(),{method:"GET",headers:{accept:"application/json"},credentials:"include"})}catch{return rl=null,null}if(!i.ok)return rl=null,null;let o;try{o=await i.json()}catch{return rl=null,null}if(!o.token)return rl=null,null;const f=o.expires_at?new Date(o.expires_at).getTime():Date.now()+Jp;return rl={token:o.token,expiresAt:f},o.token}function Ti(){rl=null}async function Zh(){const i=await Va();if(!i)return null;let o;try{o=await fetch(Kh(),{method:"POST",headers:{accept:"application/json",authorization:`Bearer ${i}`},credentials:"include"})}catch{return null}if(o.status===401){Ti();const s=await Va();if(!s)return null;try{o=await fetch(Kh(),{method:"POST",headers:{accept:"application/json",authorization:`Bearer ${s}`},credentials:"include"})}catch{return null}}if(!o.ok)return null;let f;try{f=await o.json()}catch{return null}return f.ticket??null}function Kh(){const o="/agent/app/".match(/^(.*\/)app\/?$/);return`${o?`${o[1]}api`:"/agent/api"}/ui/sse-ticket`}const Jh="x-step-up-token";let Qa=null;const Fp=5e3;function bi(){return Qa?Qa.expiresAt-Date.now()<=Fp?(Qa=null,null):Qa.token:null}function Ip(i,o){Qa={token:i,expiresAt:o}}function Pp(){Qa=null}async function pr(i,o={}){const f=await Va(),s=new Headers(o.headers);s.set("accept","application/json"),f&&s.set("authorization",`Bearer ${f}`);const d=await fetch(`${Bl()}${i}`,{...o,headers:s});if(d.status!==401)return d;Ti();const m=await Va();if(!m)return d;const p=new Headers(o.headers);return p.set("accept","application/json"),p.set("authorization",`Bearer ${m}`),fetch(`${Bl()}${i}`,{...o,headers:p})}async function br(i){try{return(await i.json()).error??""}catch{return await i.text().catch(()=>"")}}async function e0(){const i=await pr("/step-up");if(!i.ok)throw new et(i.status,await br(i)||`step-up status failed: ${i.status}`);return await i.json()}async function $h(i){const o=await pr("/step-up",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({pin:i})});if(!o.ok)throw new et(o.status,await br(o)||`PIN exchange failed: ${o.status}`);const f=await o.json(),s=new Date(f.expires_at).getTime();return Ip(f.stepUpToken,s),f.stepUpToken}async function Wh(i,o){const f=await pr("/step-up/pin",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({newPin:i,...o?{currentPin:o}:{}})});if(!f.ok)throw new et(f.status,await br(f)||`set PIN failed: ${f.status}`)}let cr=null;function Fh(i){cr=i}async function t0(i){const o=bi();return o&&i==="token"?o:cr?cr(i):null}class et extends Error{constructor(o,f){super(f),this.status=o,this.name="HttpError"}}function Bl(){const o="/agent/app/".match(/^(.*\/)app\/?$/);return o?`${o[1]}api`:"/agent/api"}async function Ai(i,o={},f=!1){const s=await Va(),d=new Headers(o.headers);d.set("accept","application/json"),s&&d.set("authorization",`Bearer ${s}`);const m=bi();m&&d.set(Jh,m);const p=await fetch(i,{...o,headers:d});if(p.status===403&&!f){const C=await l0(p);if(C)return bi()&&Pp(),await t0(C)?Ai(i,o,!0):p}if(p.status!==401)return p;Ti();const E=await Va();if(!E)return p;const S=new Headers(o.headers);S.set("accept","application/json"),S.set("authorization",`Bearer ${E}`);const v=bi();return v&&S.set(Jh,v),fetch(i,{...o,headers:S})}async function l0(i){try{const o=await i.clone().json();return o.error!=="step_up_required"?null:o.reason==="setup"?"setup":o.reason==="token"?"token":null}catch{return null}}async function Sr(i){try{return(await i.json()).error??""}catch{return await i.text().catch(()=>"")}}async function Qt(i){const o=await Ai(`${Bl()}${i}`);if(!o.ok)throw new et(o.status,await Sr(o)||`${i} failed: ${o.status}`);return await o.json()}async function ql(i,o){return xr("POST",i,o)}async function a0(i,o){return xr("PATCH",i,o)}async function Ri(i){const o=await Ai(`${Bl()}${i}`,{method:"DELETE"});if(!o.ok)throw new et(o.status,await Sr(o)||`${i} failed: ${o.status}`);return await o.json()}async function xr(i,o,f){const s=await Ai(`${Bl()}${o}`,{method:i,headers:{"content-type":"application/json"},body:JSON.stringify(f)});if(!s.ok)throw new et(s.status,await Sr(s)||`${o} failed: ${s.status}`);return await s.json()}function n0(i,o){return xr("DELETE",i,o)}function u0(){return Qt("/agents")}function i0(){return Qt("/agent-defs")}function zm(){return Qt("/agent-vaults")}function c0(i){return ql("/agent-defs",i)}function s0(i){return Qt(`/agent-defs/${encodeURIComponent(i)}`)}function _m(i,o){return a0(`/agent-defs/${encodeURIComponent(i)}`,o)}function r0(i){return Ri(`/agent-defs/${encodeURIComponent(i)}`)}function o0(i){return ql("/agent-vaults",i)}function f0(i){return Ri(`/agent-vaults/${encodeURIComponent(i)}`)}const d0=new Set(["ANTHROPIC_API_KEY","CLAUDE_API_KEY","CLAUDE_CODE_OAUTH_TOKEN"]),h0=/^[A-Za-z_][A-Za-z0-9_]*$/;function m0(){return Qt("/credentials/env")}function v0(i){return ql("/credentials/env",i)}function y0(i){return n0("/credentials/env",i)}function g0(){return Qt("/credentials/claude")}function p0(i){var f;const o=(f=i.channel)==null?void 0:f.trim();return o&&o.length>0?ql(`/credentials/claude/${encodeURIComponent(o)}`,{token:i.token}):ql("/credentials/claude",{token:i.token})}function b0(i){return Ri(`/credentials/claude/${encodeURIComponent(i)}`)}function S0(i){return Qt(`/agents/${encodeURIComponent(i)}/env`)}function x0(){return Qt("/jobs")}function E0(i){return ql("/jobs",i)}function j0(i){return ql(`/jobs/${encodeURIComponent(i)}/run`,{})}function N0(i){return Ri(`/jobs/${encodeURIComponent(i)}`)}function C0(i,o){const f=Bl().replace(/\/api$/,"");return`claude mcp add --transport http --scope user agent-${i} ${o}${f}/mcp/${i}`}function T0(){return Qt("/channels")}function Ih(i){return Qt(`/channels/${encodeURIComponent(i)}/messages`)}function A0(i,o){return ql(`/channels/${encodeURIComponent(i)}/send`,{text:o})}function R0(i,o){let s=`${Bl().replace(/\/api$/,"")}/ui/events?channel=${encodeURIComponent(i)}`;return o&&(s+=`&ticket=${encodeURIComponent(o)}`),s}function z0(i,o){let s=`${Bl().replace(/\/api$/,"")}/api/channels/${encodeURIComponent(i)}/turn-events`;return o&&(s+=`?ticket=${encodeURIComponent(o)}`),s}class Fn extends Error{constructor(o,f){super(f),this.status=o,this.name="HubError"}}function _0(){return typeof window<"u"?window.location.origin:""}function D0(){if(typeof window>"u")return!1;const{hostname:i,port:o}=window.location;return(i==="127.0.0.1"||i==="localhost"||i==="[::1]")&&o==="1941"}const Er="definition.reload",jr="note.created",Nr="note.updated",O0="agent/definition";function sr(i,o){return`agentdefs-${o}-${i}`}function M0(i,o){return{id:sr(i,o),requestedBy:"agent",source:{module:"vault",vault:i,event:o==="create"?jr:Nr,filter:{tags:[O0]}},sink:{module:"agent",action:Er}}}async function zi(i,o={}){const f=new Headers(o.headers);return f.set("accept","application/json"),fetch(`${_0()}${i}`,{...o,credentials:"include",headers:f})}function Kn(i,o){return i===401?"Not signed in to the hub portal — sign in, then retry.":i===403?"Not authorized — reactive reload needs host-admin access.":i===404?"Reactive reload needs the hub-proxied URL — open the agent app via your hub origin, not the loopback daemon.":o||`hub request failed (${i})`}async function _i(i){try{const o=await i.json();return o.error_description??o.error??""}catch{return await i.text().catch(()=>"")}}async function Ph(){const i=await zi("/admin/connections");if(!i.ok)throw new Fn(i.status,Kn(i.status,await _i(i)));return(await i.json()).connections??[]}function U0(i,o){const f=m=>o.some(p=>p.source.module==="vault"&&p.source.vault===i&&p.source.event===m&&p.sink.module==="agent"&&p.sink.action===Er),s=f(jr),d=f(Nr);return{create:s,edit:d,active:s&&d}}async function em(i){const o=["create","edit"],f=[];let s=0,d="";for(const m of o){const p=await zi("/admin/connections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(M0(i,m))});if(!p.ok){const E=await _i(p);s===0&&(s=p.status,d=E),f.push(`${m}: ${Kn(p.status,E)}`)}}if(f.length===o.length)throw new Fn(s,Kn(s,d||"provisioning failed"));return{ok:f.length===0,failures:f}}async function tm(i,o,f){const s={};o!==void 0&&(s.token=o),f&&(s.returnTo=f);const d=await zi(`/admin/grants/${encodeURIComponent(i)}/approve`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!d.ok)throw new Fn(d.status,Kn(d.status,await _i(d)));return await d.json()}async function lm(i,o=[]){const f=new Set([sr(i,"create"),sr(i,"edit")]);for(const s of o)s.source.module==="vault"&&s.source.vault===i&&(s.source.event===jr||s.source.event===Nr)&&s.sink.module==="agent"&&s.sink.action===Er&&f.add(s.id);for(const s of f){const d=await zi(`/admin/connections/${encodeURIComponent(s)}`,{method:"DELETE"});if(!d.ok&&d.status!==404)throw new Fn(d.status,Kn(d.status,await _i(d)))}}const Cr=[{value:"",label:"Default (Claude Code's default)"},{value:"opus",label:"Opus — most capable"},{value:"sonnet",label:"Sonnet — balanced"},{value:"haiku",label:"Haiku — fastest"}];function w0(i){var o;return i?((o=Cr.find(f=>f.value===i))==null?void 0:o.label)??i:"Default"}function H0(i,o){const f=new Map;for(const s of i)f.set(s.name,{name:s.name,backend:s.backend,...s.channel?{channel:s.channel}:{},...s.vault?{vault:s.vault}:{},...s.status?{status:s.status}:{},live:!0});for(const s of o){const d=f.get(s.name);d?(d.def=s,d.mode=s.mode,!d.channel&&s.channel&&(d.channel=s.channel),!d.vault&&s.vault&&(d.vault=s.vault)):f.set(s.name,{name:s.name,backend:s.backend,channel:s.channel,vault:s.vault,status:s.status,mode:s.mode,live:!1,def:s})}return[...f.values()].sort((s,d)=>s.name.localeCompare(d.name))}function Dm(i){return i==="programmatic"?"pill backend-programmatic":i==="channel"?"pill backend-channel":"pill"}function Om(i){return i==="enabled"||i==="idle"?"pill status-enabled":i==="working"?"pill status-working":i==="pending"||i.startsWith("queued")?"pill status-queued":i==="error"?"pill status-error":"pill"}function am(){const[i,o]=y.useState({kind:"loading"}),[f,s]=y.useState(null),[d,m]=y.useState(!1),p=y.useCallback(async()=>{o({kind:"loading"});try{const[v,C,j]=await Promise.all([u0(),i0(),zm()]);o({kind:"ok",agents:v.agents,defs:C.defs,vaults:j.vaults})}catch(v){const C=v instanceof et?v.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load agents: ${v.message}`:`Failed to load agents: ${v.message}`;o({kind:"error",message:C})}},[]);y.useEffect(()=>{p()},[p]),y.useEffect(()=>{const v=new URLSearchParams(window.location.search);if(v.get("mcp_connected")!=="1")return;m(!0),v.delete("mcp_connected");const C=v.toString();window.history.replaceState(null,"",window.location.pathname+(C?`?${C}`:"")+window.location.hash)},[]);const E=y.useMemo(()=>i.kind==="ok"?H0(i.agents,i.defs):[],[i]),S=y.useMemo(()=>E.find(v=>v.name===f)??null,[E,f]);return c.jsxs("div",{children:[c.jsx("h1",{children:"Agents"}),c.jsx("p",{className:"lede",children:"Every agent across all backends, in one place — programmatic and channel. Read-only for now; the create flow and def-vault editor land in later phases."}),i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void p(),children:"Retry"})]}):null,d?c.jsxs("div",{className:"success-banner",role:"status",children:["✓ MCP server connected — it'll be available to the agent on its next run."," ",c.jsx("button",{type:"button",className:"link",onClick:()=>m(!1),children:"Dismiss"})]}):null,i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading agents…"}):null,i.kind==="ok"?c.jsxs(c.Fragment,{children:[S?c.jsx(B0,{agent:S,onClose:()=>s(null),onChanged:()=>void p(),onDeleted:()=>{s(null),p()}}):null,c.jsxs("section",{className:"card","aria-label":"Agents",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"All agents"}),c.jsxs("span",{className:"section-head-actions",children:[c.jsxs("span",{className:"count","data-testid":"agents-count",children:[E.length," ",E.length===1?"agent":"agents"]}),c.jsx(fl,{to:"/create",className:"button-link","data-testid":"new-agent-link",children:"New agent"})]})]}),E.length===0?c.jsxs("div",{className:"empty",children:["No agents yet. Define one in a vault as an"," ",c.jsx("code",{children:"#agent/definition"})," note, or spawn one from the create flow (coming in a later phase)."]}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Backend"}),c.jsx("th",{children:"Channel"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Vault"})]})}),c.jsx("tbody",{children:E.map(v=>c.jsxs("tr",{className:`agent-row${f===v.name?" selected":""}`,"data-testid":`agent-row-${v.name}`,onClick:()=>s(f===v.name?null:v.name),children:[c.jsx("td",{className:"cell-name",children:v.name}),c.jsx("td",{children:c.jsx("span",{className:Dm(v.backend),children:v.backend})}),c.jsx("td",{className:v.channel?"":"cell-dim",children:v.channel??"—"}),c.jsx("td",{children:v.status?c.jsx("span",{className:Om(v.status),children:v.status}):v.live?c.jsx("span",{className:"cell-dim",children:"live"}):c.jsx("span",{className:"cell-dim",children:"not running"})}),c.jsx("td",{className:v.vault?"":"cell-dim",children:v.vault??"—"})]},v.name))})]})]}),c.jsx(I0,{}),c.jsx(P0,{vaults:i.vaults,onChanged:()=>void p()})]}):null]})}function B0({agent:i,onClose:o,onChanged:f,onDeleted:s}){const d=i.def,m=d==null?void 0:d.noteId,[p,E]=y.useState("view");return y.useEffect(()=>{E("view")},[i.name]),p==="edit"&&m?c.jsx(L0,{noteId:m,name:i.name,onCancel:()=>E("view"),onSaved:()=>{E("view"),f()}}):c.jsxs("div",{className:"detail","data-testid":"agent-detail",children:[c.jsxs("div",{className:"detail-head",children:[c.jsx("h2",{children:i.name}),c.jsx("span",{className:Dm(i.backend),children:i.backend}),i.status?c.jsx("span",{className:Om(i.status),children:i.status}):null,c.jsx("button",{type:"button",className:"detail-close",onClick:o,children:"Close"})]}),c.jsxs("dl",{className:"detail-grid",children:[c.jsx("dt",{children:"Backend"}),c.jsx("dd",{children:i.backend}),c.jsx("dt",{children:"Mode"}),c.jsx("dd",{"data-testid":"detail-mode",children:i.mode?q0(i.mode):"—"}),c.jsx("dt",{children:"Channel"}),c.jsx("dd",{children:i.channel??"—"}),c.jsx("dt",{children:"Vault"}),c.jsx("dd",{children:i.vault??"—"}),c.jsx("dt",{children:"Running"}),c.jsx("dd",{children:i.live?"yes":"no (defined, not instantiated)"}),d?c.jsxs(c.Fragment,{children:[c.jsx("dt",{children:"Model"}),c.jsx("dd",{"data-testid":"detail-model",children:w0(d.model)}),c.jsx("dt",{children:"Def status"}),c.jsx("dd",{children:d.status})]}):null]}),d?c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"System prompt"}),d.systemPromptPreview?c.jsx("p",{className:"detail-prompt","data-testid":"detail-prompt",children:d.systemPromptPreview}):c.jsx("p",{className:"dim",children:"No system prompt set (Claude Code's default)."}),c.jsx("h3",{children:"Wants"}),d.wants.length>0?c.jsx("div",{className:"tag-list",children:d.wants.map(S=>c.jsx("span",{className:"tag",children:S},S))}):c.jsx("p",{className:"dim",children:"Own-vault only — no extra connections requested."}),d.pending.length>0?c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"Pending approval"}),c.jsx("div",{className:"tag-list",children:d.pending.map(S=>c.jsx("span",{className:"tag",children:S},S))})]}):null,m?c.jsx(X0,{noteId:m,def:d,onChanged:f}):null]}):c.jsxs("p",{className:"dim","data-testid":"detail-no-def",children:["This agent isn't backed by a vault-native ",c.jsx("code",{children:"#agent/definition"})," note, so there's no system prompt or wants to show."]}),i.backend==="channel"?c.jsx("p",{className:"detail-note","data-testid":"detail-channel-note",children:'Channel backend — the queue depth and the "connect your Claude Code session" affordance arrive in a later phase.'}):null,i.channel?c.jsx(Z0,{channel:i.channel}):null,i.channel?c.jsx(F0,{channel:i.channel,backend:i.backend}):null,i.channel?c.jsx(W0,{name:i.name}):null,m?p==="delete"?c.jsx(k0,{noteId:m,name:i.name,onCancel:()=>E("view"),onDeleted:s}):c.jsxs("div",{className:"detail-actions","data-testid":"detail-actions",children:[c.jsx("button",{type:"button",className:"secondary","data-testid":"edit-agent",onClick:()=>E("edit"),children:"Edit"}),c.jsx("button",{type:"button",className:"button-danger","data-testid":"delete-agent",onClick:()=>E("delete"),children:"Delete"})]}):null]})}function q0(i){return i==="single-threaded"?"Single-threaded":"Multi-threaded"}function L0({noteId:i,name:o,onCancel:f,onSaved:s}){const[d,m]=y.useState({kind:"loading"}),[p,E]=y.useState(""),[S,v]=y.useState("single-threaded"),[C,j]=y.useState(""),[z,k]=y.useState(""),[Y,L]=y.useState(!1),[X,M]=y.useState(null),D=y.useCallback(async()=>{m({kind:"loading"});try{const $=(await s0(i)).def;E($.systemPrompt),v($.mode),j($.model??""),k($.wants.join(", ")),m({kind:"ready",systemPrompt:$.systemPrompt,mode:$.mode,wants:$.wants.join(", ")})}catch(Z){const $=Z instanceof et?Z.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load the def: ${Z.message}`:`Failed to load the def: ${Z.message}`;m({kind:"error",message:$})}},[i]);y.useEffect(()=>{D()},[D]);async function G(Z){if(Z.preventDefault(),!Y){L(!0),M(null);try{await _m(i,{systemPrompt:p,metadata:{mode:S,model:C},wants:z.trim()}),s()}catch($){const ue=$ instanceof et&&$.status===401?"Not signed in to the hub — sign in to the portal, then reload.":$.message;M(ue)}finally{L(!1)}}}return c.jsxs("div",{className:"detail","data-testid":"edit-agent-form",children:[c.jsxs("div",{className:"detail-head",children:[c.jsxs("h2",{children:["Edit ",o]}),c.jsx("button",{type:"button",className:"detail-close",onClick:f,children:"Close"})]}),d.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading def…"}):null,d.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"edit-load-error",children:[d.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void D(),children:"Retry"})]}):null,d.kind==="ready"?c.jsxs("form",{className:"card",onSubmit:G,"aria-label":"Edit agent",children:[X?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"edit-error",children:X}):null,c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Mode"}),c.jsx(Ei,{name:"edit-mode",value:"single-threaded",checked:S==="single-threaded",onChange:()=>v("single-threaded"),label:"Single-threaded",help:"One continuous conversation — remembers everything on this channel.",testid:"edit-mode-single-threaded"}),c.jsx(Ei,{name:"edit-mode",value:"multi-threaded",checked:S==="multi-threaded",onChange:()=>v("multi-threaded"),label:"Multi-threaded",help:"Each run is its own thread — good for scheduled or stateless tasks.",testid:"edit-mode-multi-threaded"})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-model",children:"Model"}),c.jsx("select",{id:"edit-model",value:C,"data-testid":"edit-model",onChange:Z=>j(Z.target.value),children:Cr.map(Z=>c.jsx("option",{value:Z.value,children:Z.label},Z.value))}),c.jsx("p",{className:"field-hint",children:"Which model Parachute runs this agent on (programmatic backend). A channel-backend agent uses whatever model your own session runs."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-prompt",children:"System prompt"}),c.jsx("textarea",{id:"edit-prompt",rows:8,value:p,placeholder:"You are…",onChange:Z=>E(Z.target.value)}),c.jsx("p",{className:"field-hint",children:"The agent's persona + instructions (the note body). Leave blank for Claude Code's default."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-wants",children:"Wants (connections)"}),c.jsx("input",{id:"edit-wants",type:"text",value:z,placeholder:"vault:other, service:github",autoComplete:"off",onChange:Z=>k(Z.target.value)}),c.jsx("p",{className:"field-hint",children:"Comma-separated connection keys the agent requests beyond its own vault. Each needs approval before it's granted."})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:Y,children:Y?"Saving…":"Save changes"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]}):null]})}function k0({noteId:i,name:o,onCancel:f,onDeleted:s}){const[d,m]=y.useState(""),[p,E]=y.useState(!1),[S,v]=y.useState(null),C=d===o&&!p;async function j(){if(C){E(!0),v(null);try{await r0(i),s()}catch(z){const k=z instanceof et&&z.status===401?"Not signed in to the hub — sign in to the portal, then reload.":z.message;v(k),E(!1)}}}return c.jsxs("div",{className:"confirm-box","data-testid":"delete-confirm",children:[c.jsxs("p",{className:"confirm-prompt",children:["Delete ",c.jsx("strong",{children:o}),"? This removes the definition note and deregisters the agent. Type the agent name to confirm."]}),S?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"delete-error",children:S}):null,c.jsx("div",{className:"field",children:c.jsx("input",{type:"text",value:d,placeholder:o,autoComplete:"off","aria-label":"Type the agent name to confirm deletion","data-testid":"delete-confirm-input",onChange:z=>m(z.target.value)})}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":"delete-confirm-button",disabled:!C,onClick:()=>void j(),children:p?"Deleting…":"Delete agent"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]})}function Y0({status:i}){return i==="approved"?c.jsx("span",{className:"pill status-enabled","data-testid":"conn-status-approved",children:"Connected"}):i==="needs_consent"?c.jsx("span",{className:"pill status-error","data-testid":"conn-status-needs_consent",children:"Needs reconnect"}):i==="revoked"?c.jsx("span",{className:"pill status-error","data-testid":"conn-status-revoked",children:"Revoked"}):c.jsx("span",{className:"pill status-pending","data-testid":"conn-status-pending",children:"Pending"})}function G0(i){if(i.connections&&i.connections.length>0)return i.connections.filter(f=>f.kind==="mcp");const o=new Set(i.pending);return i.wants.filter(f=>f.startsWith("mcp:")&&/^mcp:https?:\/\//i.test(f)).map(f=>({key:f,kind:"mcp",target:f.slice(4),status:o.has(f)?"pending":"approved"}))}function X0({noteId:i,def:o,onChanged:f}){const[s,d]=y.useState(!1),[m,p]=y.useState(null),[E,S]=y.useState(null),[v,C]=y.useState(""),[j,z]=y.useState(null),k=G0(o),Y=D0();async function L(M){p(M),z(null);try{const D=window.location.pathname+window.location.search+window.location.hash,G=await tm(M,void 0,D);if(G.authorizeUrl){window.location.assign(G.authorizeUrl);return}f()}catch(D){z(nm(D))}finally{p(null)}}async function X(M){const D=v.trim();if(D.length!==0){p(M),z(null);try{await tm(M,D),S(null),C(""),f()}catch(G){z(nm(G))}finally{p(null)}}}return c.jsxs("section",{className:"detail-section","aria-label":"Connections","data-testid":"connections-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Connections / MCP servers"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-mcp-toggle",onClick:()=>{d(M=>!M),z(null)},children:s?"Cancel":"Add MCP server"})]}),c.jsx("p",{className:"muted",children:"Remote MCP servers this agent can reach. Add one, then connect it via OAuth (or paste a static bearer). Each connection is operator-approved before it's granted."}),Y?c.jsx("div",{className:"info-banner",role:"status","data-testid":"connections-daemon-direct",children:"Open this surface via your hub to connect MCP servers — the OAuth/approve step needs the hub origin (you're on the loopback daemon)."}):null,s?c.jsx(Q0,{noteId:i,def:o,onCancel:()=>d(!1),onAdded:()=>{d(!1),f()}}):null,j?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"connections-row-error",children:j}):null,k.length===0?c.jsx("div",{className:"empty","data-testid":"connections-empty",children:"No MCP servers connected to this agent yet."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"MCP server"}),c.jsx("th",{children:"Status"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:k.map(M=>{const D=M.status!=="approved",G=D&&!!M.grantId&&!Y;return c.jsxs("tr",{"data-testid":`connection-row-${M.target}`,children:[c.jsx("td",{className:"cell-name",children:c.jsx("code",{children:M.target})}),c.jsx("td",{children:c.jsx(Y0,{status:M.status})}),c.jsx("td",{children:D?E===M.grantId?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("input",{type:"password",value:v,placeholder:"bearer token",autoComplete:"off",spellCheck:!1,"aria-label":"Static bearer token","data-testid":`paste-token-input-${M.target}`,onChange:Z=>C(Z.target.value)}),c.jsx("button",{type:"button",disabled:m===M.grantId||v.trim().length===0,"data-testid":`paste-token-save-${M.target}`,onClick:()=>void X(M.grantId),children:m===M.grantId?"Saving…":"Save"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>{S(null),C("")},children:"Cancel"})]}):c.jsxs("span",{className:"schedule-row-actions",children:[c.jsx("button",{type:"button",disabled:!G||m===M.grantId,"data-testid":`connect-${M.target}`,title:Y?"Open the agent app via your hub origin to connect.":M.grantId?void 0:"No grant registered yet — reload after the daemon registers it.",onClick:()=>void L(M.grantId),children:m===M.grantId?"Connecting…":M.status==="needs_consent"?"Reconnect":"Connect"}),M.grantId&&!Y?c.jsx("button",{type:"button",className:"cancel-link","data-testid":`paste-token-${M.target}`,onClick:()=>{z(null),C(""),S(M.grantId)},children:"Paste token"}):null]}):c.jsx("span",{className:"cell-dim",children:"—"})})]},M.key)})})]})]})}function nm(i){return i instanceof Fn,i.message}function Q0({noteId:i,def:o,onCancel:f,onAdded:s}){const[d,m]=y.useState(""),[p,E]=y.useState("oauth"),[S,v]=y.useState(!1),[C,j]=y.useState(null),z=d.trim(),k=/^https?:\/\/.+/i.test(z)&&V0(z),Y=o.wants.includes(`mcp:${z}`),L=k&&!Y&&!S;async function X(M){if(M.preventDefault(),!!L){v(!0),j(null);try{const D=[...o.wants,`mcp:${z}`].join(", ");await _m(i,{wants:D}),s()}catch(D){const G=D instanceof et&&D.status===401?"Not signed in to the hub — sign in to the portal, then reload.":D.message;j(G)}finally{v(!1)}}}return c.jsxs("form",{className:"inline-form",onSubmit:X,"aria-label":"Add MCP server","data-testid":"add-mcp-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-mcp-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"mcp-url",children:"MCP server URL"}),c.jsx("input",{id:"mcp-url",type:"text",value:d,placeholder:"https://mcp.example.com/mcp",autoComplete:"off",spellCheck:!1,onChange:M=>m(M.target.value)}),z.length>0&&!k?c.jsx("p",{className:"field-error","data-testid":"mcp-url-invalid",children:"Must be a full http(s) URL."}):null,Y?c.jsx("p",{className:"field-error","data-testid":"mcp-url-duplicate",children:"This MCP server is already connected to the agent."}):null]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Authentication"}),c.jsx("p",{className:"muted",children:"You'll enter credentials in the next step — on the connection's row after it's added."}),c.jsx(Ei,{name:"mcp-auth",value:"oauth",checked:p==="oauth",onChange:()=>E("oauth"),label:"OAuth",help:"Sign in to the MCP server in your browser (recommended). Connect on the row after adding.",testid:"mcp-auth-oauth"}),c.jsx(Ei,{name:"mcp-auth",value:"token",checked:p==="token",onChange:()=>E("token"),label:"Paste a token",help:"For an MCP server with a static bearer token. Paste it on the row's “Paste token” after adding.",testid:"mcp-auth-token"})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!L,"data-testid":"add-mcp-submit",children:S?"Adding…":"Add MCP server"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]})}function V0(i){try{return new URL(i),!0}catch{return!1}}function Z0({channel:i}){const[o,f]=y.useState({kind:"loading"}),[s,d]=y.useState(!1),[m,p]=y.useState(null),[E,S]=y.useState(null),[v,C]=y.useState(null),[j,z]=y.useState(null),[k,Y]=y.useState(null),L=y.useCallback(async()=>{f({kind:"loading"});try{const G=(await x0()).jobs.filter(Z=>Z.channel===i);f({kind:"ok",jobs:G})}catch(D){f({kind:"error",message:at(D)})}},[i]);y.useEffect(()=>{L()},[L]);async function X(D){p(D),z(null),Y(null);try{const G=await j0(D);Y(`Ran ${D} (${G.status}).`),await L()}catch(G){z(`Run failed: ${at(G)}`)}finally{p(null)}}async function M(D){C(D),z(null);try{await N0(D),S(null),await L()}catch(G){z(at(G))}finally{C(null)}}return c.jsxs("section",{className:"schedules","aria-label":"Schedules","data-testid":"schedules-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Schedules"}),c.jsx("span",{className:"section-head-actions",children:c.jsx("button",{type:"button",className:"secondary","data-testid":"add-schedule-toggle",onClick:()=>d(D=>!D),children:s?"Cancel":"New schedule"})})]}),c.jsx("p",{className:"muted",children:"Send this agent a message on a cron schedule. The runner writes the message as an inbound note; the agent runs its turn as if you typed it."}),s?c.jsx(J0,{channel:i,onCancel:()=>d(!1),onCreated:()=>{d(!1),L()}}):null,k?c.jsx("p",{className:"schedule-status","data-testid":"schedule-row-status",children:k}):null,j?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"schedule-row-error",children:j}):null,o.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading schedules…"}):null,o.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"schedules-error",children:[o.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void L(),children:"Retry"})]}):null,o.kind==="ok"?o.jobs.length===0?c.jsx("div",{className:"empty","data-testid":"schedules-empty",children:"No schedules yet for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Id"}),c.jsx("th",{children:"Cron"}),c.jsx("th",{children:"Next run"}),c.jsx("th",{children:"Last status"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:o.jobs.map(D=>c.jsxs("tr",{"data-testid":`schedule-row-${D.id}`,children:[c.jsxs("td",{className:"cell-name",children:[c.jsx("code",{children:D.id}),D.enabled?null:c.jsx("span",{className:"cell-dim",children:" (disabled)"})]}),c.jsxs("td",{children:[c.jsx("code",{children:D.schedule.cron}),D.schedule.tz?c.jsxs("span",{className:"cell-dim",children:[" ",D.schedule.tz]}):null]}),c.jsx("td",{className:D.nextRunAt?"":"cell-dim",children:um(D.nextRunAt)}),c.jsxs("td",{children:[D.lastStatus?c.jsx("span",{className:D.lastStatus.startsWith("error")?"pill status-error":"pill status-enabled",children:D.lastStatus}):c.jsx("span",{className:"cell-dim",children:"—"}),D.lastRunAt?c.jsxs("span",{className:"cell-dim",children:[" ",um(D.lastRunAt)]}):null]}),c.jsx("td",{children:E===D.id?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`schedule-delete-confirm-${D.id}`,disabled:v===D.id,onClick:()=>void M(D.id),children:v===D.id?"Deleting…":"Confirm delete"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>S(null),children:"Cancel"})]}):c.jsxs("span",{className:"schedule-row-actions",children:[c.jsx("button",{type:"button",className:"secondary","data-testid":`schedule-run-${D.id}`,disabled:m===D.id,onClick:()=>void X(D.id),children:m===D.id?"Running…":"Run now"}),c.jsx("button",{type:"button",className:"button-danger","data-testid":`schedule-delete-${D.id}`,onClick:()=>{z(null),S(D.id)},children:"Delete"})]})})]},D.id))})]}):null]})}const K0=[{label:"daily 8am",cron:"0 8 * * *"},{label:"hourly",cron:"0 * * * *"},{label:"every 15m",cron:"*/15 * * * *"},{label:"weekdays 9am",cron:"0 9 * * 1-5"},{label:"weekly Mon 8am",cron:"0 8 * * 1"}];function J0({channel:i,onCancel:o,onCreated:f}){const[s,d]=y.useState(""),[m,p]=y.useState(""),[E,S]=y.useState(""),[v,C]=y.useState(""),[j,z]=y.useState(!1),[k,Y]=y.useState(null),L=/^[a-zA-Z0-9_-]+$/.test(s),X=L&&m.trim().length>0&&E.trim().length>0&&!j;async function M(D){if(D.preventDefault(),!!X){z(!0),Y(null);try{await E0({id:s,channel:i,message:m.trim(),schedule:{cron:E.trim(),...v.trim()?{tz:v.trim()}:{}},enabled:!0}),f()}catch(G){Y(at(G))}finally{z(!1)}}}return c.jsxs("form",{className:"inline-form",onSubmit:M,"aria-label":"New schedule","data-testid":"schedule-form",children:[k?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"schedule-form-error",children:k}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-id",children:"Job id (slug)"}),c.jsx("input",{id:"schedule-id",type:"text",value:s,placeholder:"morning-standup",autoComplete:"off",onChange:D=>d(D.target.value)}),s.length>0&&!L?c.jsx("p",{className:"field-error","data-testid":"schedule-id-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-message",children:"Message to send"}),c.jsx("textarea",{id:"schedule-message",rows:3,value:m,placeholder:"Run the morning weave…",onChange:D=>p(D.target.value)})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-cron",children:"Cron (min hour dom mon dow)"}),c.jsx("input",{id:"schedule-cron",type:"text",value:E,placeholder:"0 8 * * *",autoComplete:"off",onChange:D=>S(D.target.value)}),c.jsx("div",{className:"schedule-presets",children:K0.map(D=>c.jsx("button",{type:"button",className:"secondary",onClick:()=>S(D.cron),children:D.label},D.cron))})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-tz",children:"Timezone (IANA, optional)"}),c.jsx("input",{id:"schedule-tz",type:"text",value:v,placeholder:"America/Los_Angeles",autoComplete:"off",onChange:D=>C(D.target.value)}),c.jsx("p",{className:"field-hint",children:"Leave blank to use the daemon's local timezone."})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!X,children:j?"Scheduling…":"Create schedule"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:o,children:"Cancel"})]})]})}function um(i){if(!i)return"—";try{return new Date(i).toLocaleString()}catch{return i}}function Ei(i){return c.jsxs("label",{className:`radio-row${i.checked?" selected":""}`,"data-testid":i.testid,children:[c.jsx("input",{type:"radio",name:i.name,value:i.value,checked:i.checked,onChange:i.onChange}),c.jsxs("span",{className:"radio-body",children:[c.jsx("span",{className:"radio-label",children:i.label}),c.jsx("span",{className:"radio-help",children:i.help})]})]})}function $0(i){return i==="channel"?"pill backend-channel":i.startsWith("grant:")?"pill backend-programmatic":"pill"}function W0({name:i}){const[o,f]=y.useState({kind:"loading"}),s=y.useCallback(async()=>{f({kind:"loading"});try{const d=await S0(i);f({kind:"ok",env:d.env,...d.note?{note:d.note}:{}})}catch(d){f({kind:"error",message:at(d)})}},[i]);return y.useEffect(()=>{s()},[s]),c.jsxs("section",{className:"detail-section","aria-label":"Effective env","data-testid":"effective-env-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Effective env"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"effective-env-refresh",onClick:()=>void s(),children:"Refresh"})]}),c.jsxs("p",{className:"muted",children:["The env-var ",c.jsx("strong",{children:"names"})," this agent's sandboxed turn runs with, resolved across the operator default, this agent's override, and approved-grant service env."," ",c.jsx("strong",{children:"Names only"})," — values are never shown. Precedence: channel > default > grant."]}),o.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading effective env…"}):null,o.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"effective-env-error",children:[o.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void s(),children:"Retry"})]}):null,o.kind==="ok"?c.jsxs(c.Fragment,{children:[o.note?c.jsx("p",{className:"muted","data-testid":"effective-env-note",children:o.note}):null,o.env.length===0?c.jsx("div",{className:"empty","data-testid":"effective-env-empty",children:"No env vars resolved for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Source"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:o.env.map((d,m)=>c.jsxs("tr",{className:d.overridden?"cell-dim":"","data-testid":`effective-env-${d.name}-${d.source}`,children:[c.jsx("td",{className:"cell-name",children:d.name}),c.jsx("td",{children:c.jsx("span",{className:$0(d.source),children:d.source})}),c.jsx("td",{children:d.overridden?c.jsx("span",{className:"cell-dim","data-testid":`effective-env-overridden-${d.name}-${d.source}`,children:"overridden"}):null})]},`${d.name}-${d.source}-${m}`))})]})]}):null]})}function F0({channel:i,backend:o}){const[f,s]=y.useState({kind:"loading"}),[d,m]=y.useState(!1),[p,E]=y.useState(""),[S,v]=y.useState(""),[C,j]=y.useState(!1),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),Z=y.useCallback(async()=>{s({kind:"loading"});try{const le=await m0();s({kind:"ok",names:(le.channels[i]??[]).slice().sort()})}catch(le){s({kind:"error",message:at(le)})}},[i]);y.useEffect(()=>{Z()},[Z]);const $=p.trim(),ue=d0.has($),V=$.length===0||h0.test($),I=$.length>0&&V&&!ue&&S.length>0&&!C;async function P(le){if(le.preventDefault(),!!I){j(!0),k(null);try{await v0({channel:i,name:$,value:S}),E(""),v(""),m(!1),await Z()}catch(xe){k(at(xe))}finally{j(!1)}}}async function Re(le){M(le),G(null);try{await y0({channel:i,name:le}),L(null),await Z()}catch(xe){G(at(xe))}finally{M(null)}}return c.jsxs("section",{className:"detail-section","aria-label":"Secrets","data-testid":"secrets-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Secrets (env vars)"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-secret-toggle",onClick:()=>{m(le=>!le),k(null)},children:d?"Cancel":"Add secret"})]}),c.jsxs("p",{className:"muted",children:["Local env vars (e.g. ",c.jsx("code",{children:"GH_TOKEN"}),") injected into this agent's sandboxed turns — stored 0600 on this machine, ",c.jsx("strong",{children:"never in the vault"}),". Values are write-only: they're never shown again.",o==="channel"?c.jsxs(c.Fragment,{children:[" ","This agent uses the ",c.jsx("strong",{children:"channel"})," backend, so it runs in your own Claude Code session with your own environment — these vars apply only to programmatic turns."]}):null]}),d?c.jsxs("form",{className:"inline-form",onSubmit:P,"aria-label":"Add secret","aria-busy":C,"data-testid":"add-secret-form",children:[z?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-secret-error",children:z}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"secret-name",children:"Name"}),c.jsx("input",{id:"secret-name",type:"text",value:p,placeholder:"GH_TOKEN",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:le=>E(le.target.value)}),$.length>0&&!V?c.jsx("p",{className:"field-error","data-testid":"secret-name-invalid",children:"A valid env var name — letters, numbers, underscore; not starting with a digit."}):null,ue?c.jsxs("p",{className:"field-error","data-testid":"secret-name-denylisted",children:["Reserved — ",$," would hijack the agent's managed billing/auth."]}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"secret-value",children:"Value"}),c.jsx("input",{id:"secret-value",type:"password",value:S,placeholder:"ghp_…",autoComplete:"off",spellCheck:!1,onChange:le=>v(le.target.value)}),c.jsx("p",{className:"field-hint",children:"Stored 0600 on disk (access-controlled, not encrypted). Write-only — never re-displayed."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!I,children:C?"Saving…":"Save secret"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"secret-row-error",children:D}):null,f.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading secrets…"}):null,f.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"secrets-load-error",children:[f.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void Z(),children:"Retry"})]}):null,f.kind==="ok"?f.names.length===0?c.jsx("div",{className:"empty","data-testid":"secrets-empty",children:"No secrets set for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Value"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:f.names.map(le=>c.jsxs("tr",{"data-testid":`secret-${le}`,children:[c.jsx("td",{className:"cell-name",children:le}),c.jsx("td",{className:"cell-dim",children:"••••••••"}),c.jsx("td",{children:Y===le?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`secret-remove-confirm-${le}`,disabled:X===le,onClick:()=>void Re(le),children:X===le?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`secret-remove-${le}`,onClick:()=>{G(null),L(le)},children:"Remove"})})]},le))})]}):null]})}function I0(){const[i,o]=y.useState({kind:"loading"}),[f,s]=y.useState(!1),[d,m]=y.useState(""),[p,E]=y.useState(""),[S,v]=y.useState(!1),[C,j]=y.useState(null),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),Z=y.useCallback(async()=>{o({kind:"loading"});try{const P=await g0();o({kind:"ok",status:P})}catch(P){o({kind:"error",message:at(P)})}},[]);y.useEffect(()=>{Z()},[Z]);const $=p.trim(),ue=d.length>0&&!S;async function V(P){if(P.preventDefault(),!!ue){v(!0),j(null),k(null);try{await p0({token:d,...$.length>0?{channel:$}:{}}),m(""),E(""),s(!1),k($.length>0?`Saved a Claude token override for "${$}".`:"Saved the default Claude token."),await Z()}catch(Re){j(at(Re))}finally{v(!1)}}}async function I(P){M(P),G(null);try{await b0(P),L(null),await Z()}catch(Re){G(at(Re))}finally{M(null)}}return c.jsxs("section",{className:"card","aria-label":"Claude auth","data-testid":"claude-auth-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"Claude auth"}),c.jsxs("span",{className:"section-head-actions",children:[i.kind==="ok"?i.status.defaultSet?c.jsx("span",{className:"pill status-enabled","data-testid":"claude-default-configured",children:"configured"}):c.jsx("span",{className:"pill status-error","data-testid":"claude-default-missing",children:"not configured"}):null,c.jsx("button",{type:"button",className:"secondary","data-testid":"set-claude-token-toggle",onClick:()=>{s(P=>(P&&(m(""),E("")),!P)),j(null),k(null)},children:f?"Cancel":"Set token"})]})]}),c.jsxs("p",{className:"muted",children:["The token from ",c.jsx("code",{children:"claude setup-token"})," that the daemon runs each agent turn on. It's stored locally (",c.jsx("code",{children:"credentials.json"}),", 0600) and used to run turns on your ",c.jsx("strong",{children:"Claude subscription"})," — ",c.jsx("strong",{children:"not"})," API billing. It's write-only: the value is never shown again. Set a ",c.jsx("strong",{children:"default"})," token (used by every agent) or a per-",c.jsx("strong",{children:"channel"})," override."]}),z?c.jsx("div",{className:"info-banner",role:"status","data-testid":"claude-saved-notice",children:z}):null,f?c.jsxs("form",{className:"inline-form",onSubmit:V,"aria-label":"Set Claude token","aria-busy":S,"data-testid":"set-claude-token-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"set-claude-token-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"claude-token",children:"Token"}),c.jsx("input",{id:"claude-token",type:"password",value:d,placeholder:"paste the output of `claude setup-token`",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:P=>m(P.target.value)}),c.jsxs("p",{className:"field-hint",children:["Run ",c.jsx("code",{children:"claude setup-token"})," on a machine where you're signed in, then paste it here. Stored 0600 on disk (access-controlled, not encrypted). Write-only — never re-displayed."]})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"claude-channel",children:"Channel (optional)"}),c.jsx("input",{id:"claude-channel",type:"text",value:p,placeholder:"leave blank for the default (operator) token",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:P=>E(P.target.value)}),c.jsx("p",{className:"field-hint",children:"A channel name to override just that agent; blank sets the default token every agent falls back to."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!ue,children:S?"Saving…":"Save token"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"claude-row-error",children:D}):null,i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading Claude auth…"}):null,i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"claude-auth-load-error",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void Z(),children:"Retry"})]}):null,i.kind==="ok"?c.jsxs(c.Fragment,{children:[i.status.defaultSet?null:c.jsx("div",{className:"empty","data-testid":"claude-default-empty",children:"No default Claude token set — programmatic agents can't run turns until one is set (or a per-channel override covers them)."}),i.status.channels.length>0?c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Channel override"}),c.jsx("th",{children:"Token"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:i.status.channels.map(P=>c.jsxs("tr",{"data-testid":`claude-override-${P}`,children:[c.jsx("td",{className:"cell-name",children:P}),c.jsx("td",{className:"cell-dim",children:"••••••••"}),c.jsx("td",{children:Y===P?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`claude-override-remove-confirm-${P}`,disabled:X===P,onClick:()=>void I(P),children:X===P?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`claude-override-remove-${P}`,onClick:()=>{G(null),L(P)},children:"Remove"})})]},P))})]}):null]}):null]})}function P0({vaults:i,onChanged:o}){const[f,s]=y.useState(!1),[d,m]=y.useState(""),[p,E]=y.useState(""),[S,v]=y.useState(!1),[C,j]=y.useState(null),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),[Z,$]=y.useState([]),[ue,V]=y.useState(!1),[I,P]=y.useState(null),[Re,le]=y.useState(null),xe=y.useCallback(async()=>{try{const Q=await Ph();$(Q),V(!0)}catch{V(!1)}},[]);y.useEffect(()=>{xe()},[xe]);const Ie=/^[a-zA-Z0-9_-]+$/.test(d),Ze=Ie&&!S;async function De(Q){if(Q.preventDefault(),!Ze)return;v(!0),j(null),k(null);const ne=d;try{await o0({vault:ne,...p.trim().length>0?{url:p.trim()}:{}}),m(""),E(""),s(!1),o();try{const{ok:oe}=await em(ne);k(oe?`Added "${ne}" — reactive reload is on (def changes apply instantly).`:`Added "${ne}" — reactive reload partially wired; defs still converge within 60s.`)}catch(oe){k(`Added "${ne}". Reactive reload couldn't be enabled (${at(oe)}) — def changes converge within 60s; you can enable it below.`)}await xe()}catch(oe){j(at(oe))}finally{v(!1)}}async function U(Q){M(Q),G(null);try{await f0(Q);try{const ne=await Ph().catch(()=>Z);await lm(Q,ne)}catch{}L(null),o(),await xe()}catch(ne){G(at(ne))}finally{M(null)}}async function K(Q,ne){P(Q),le(null);try{ne?await lm(Q,Z):await em(Q),await xe()}catch(oe){le(at(oe))}finally{P(null)}}return c.jsxs("section",{className:"card","aria-label":"Def-vaults",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"Def-vaults"}),c.jsxs("span",{className:"section-head-actions",children:[c.jsx("span",{className:"count",children:i.length}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-def-vault-toggle",onClick:()=>s(Q=>!Q),children:f?"Cancel":"Add def-vault"})]})]}),c.jsxs("p",{className:"muted",children:["Vaults this module reads ",c.jsx("code",{children:"#agent/definition"})," notes from. Removing one deregisters every agent defined in it. ",c.jsx("strong",{children:"Reactive reload"})," wires a vault trigger so def changes apply instantly instead of waiting up to 60s."]}),z?c.jsx("div",{className:"info-banner",role:"status","data-testid":"add-def-vault-notice",children:z}):null,Re?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"reactive-reload-error",children:Re}):null,f?c.jsxs("form",{className:"inline-form",onSubmit:De,"aria-label":"Add def-vault","data-testid":"add-def-vault-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-def-vault-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"new-vault-name",children:"Vault name"}),c.jsx("input",{id:"new-vault-name",type:"text",value:d,placeholder:"research",autoComplete:"off",onChange:Q=>m(Q.target.value)}),d.length>0&&!Ie?c.jsx("p",{className:"field-error","data-testid":"new-vault-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"new-vault-url",children:"Vault URL (optional)"}),c.jsx("input",{id:"new-vault-url",type:"text",value:p,placeholder:"http://127.0.0.1:1940",autoComplete:"off",onChange:Q=>E(Q.target.value)}),c.jsx("p",{className:"field-hint",children:"The vault REST origin. Defaults to the loopback vault."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!Ze,children:S?"Adding…":"Add def-vault"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"remove-def-vault-error",children:D}):null,i.length===0?c.jsx("div",{className:"empty","data-testid":"def-vaults-empty",children:"No def-vaults configured yet."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Vault"}),c.jsx("th",{children:"URL"}),c.jsx("th",{children:"Token"}),c.jsx("th",{children:"Reactive reload"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:i.map(Q=>{const ne=U0(Q.vault,Z);return c.jsxs("tr",{"data-testid":`def-vault-${Q.vault}`,children:[c.jsx("td",{className:"cell-name",children:Q.vault}),c.jsx("td",{className:"cell-dim",children:Q.url}),c.jsx("td",{children:Q.tokenPresent?c.jsx("span",{className:"pill status-enabled",children:"present"}):c.jsx("span",{className:"pill status-error",children:"missing"})}),c.jsx("td",{"data-testid":`reactive-reload-cell-${Q.vault}`,children:ue?c.jsxs("span",{className:"confirm-inline",children:[ne.active?c.jsx("span",{className:"pill status-enabled","data-testid":`reactive-on-${Q.vault}`,children:"on"}):c.jsx("span",{className:"pill","data-testid":`reactive-off-${Q.vault}`,children:"off"}),c.jsx("button",{type:"button",className:"cancel-link","data-testid":`reactive-toggle-${Q.vault}`,disabled:I===Q.vault,onClick:()=>void K(Q.vault,ne.active),children:I===Q.vault?"Working…":ne.active?"Disable":"Enable"})]}):c.jsx("span",{className:"cell-dim",title:"Open the agent app via your hub origin to manage reactive reload.",children:"—"})}),c.jsx("td",{children:Y===Q.vault?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`remove-def-vault-confirm-${Q.vault}`,disabled:X===Q.vault,onClick:()=>void U(Q.vault),children:X===Q.vault?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`remove-def-vault-${Q.vault}`,onClick:()=>{G(null),L(Q.vault)},children:"Remove"})})]},Q.vault)})})]})]})}function at(i){return i instanceof et&&i.status===401?"Not signed in to the hub — sign in to the portal, then reload.":i.message}function eb(i){return{key:i.id,kind:i.direction==="outbound"?"them":"you",text:i.text}}function im(){const i=Ig(),o=jm(),[f,s]=y.useState(null),[d,m]=y.useState(null),[p,E]=y.useState(i.channel??null),[S,v]=y.useState([]),[C,j]=y.useState(null),[z,k]=y.useState({text:"",kind:""}),[Y,L]=y.useState(""),[X,M]=y.useState(!1),D=y.useRef(new Set),G=y.useRef(null),Z=y.useRef(null),$=y.useRef(!1),ue=y.useRef(0),V=y.useRef(null),I=y.useCallback(g=>{var O;return((O=f==null?void 0:f.find(H=>H.name===g))==null?void 0:O.transport)??""},[f]),P=y.useCallback(g=>I(g)==="vault",[I]),Re=y.useCallback(g=>{v(O=>[...O,{key:crypto.randomUUID(),kind:"sys",text:g}])},[]),le=y.useCallback(g=>{!g.id||D.current.has(g.id)||(D.current.add(g.id),v(O=>[...O,eb(g)]))},[]);y.useEffect(()=>{const g=V.current;g&&(g.scrollTop=g.scrollHeight)},[S,C]);const xe=y.useCallback(async g=>{var re,ge;const O=ue.current;(re=G.current)==null||re.close(),G.current=null,(ge=Z.current)==null||ge.close(),Z.current=null;const H=I(g),J=()=>{$.current=!1,k({text:`live - ${g}`,kind:"live"})},ae=()=>{var Ue,he;if(!$.current){$.current=!0,k({text:"re-authenticating...",kind:""}),(Ue=G.current)==null||Ue.close(),G.current=null,(he=Z.current)==null||he.close(),Z.current=null,Ti(),xe(g);return}k({text:"reconnecting...",kind:""})};if(H==="http-ui"){const Ue=await Zh();if(O!==ue.current)return;const he=new EventSource(R0(g,Ue));he.onopen=J,he.addEventListener("reply",ft=>{try{const tt=JSON.parse(ft.data);tt.id?le({id:tt.id,text:tt.text??"",direction:"outbound",sender:"session",ts:""}):v(Ja=>[...Ja,{key:crypto.randomUUID(),kind:"them",text:tt.text??""}])}catch{}}),he.addEventListener("edit",ft=>{try{const tt=JSON.parse(ft.data);Re(`(edited) ${tt.text??""}`)}catch{}}),he.addEventListener("permission",ft=>{try{const tt=JSON.parse(ft.data);Re(`permission: ${tt.tool_name??""}${tt.description?` - ${tt.description}`:""} (respond in the session terminal)`)}catch{}}),he.onerror=ae,G.current=he}if(P(g)){const Ue=await Zh();if(O!==ue.current)return;const he=new EventSource(z0(g,Ue));he.onopen=J,he.addEventListener("turn",ft=>{try{Ze(JSON.parse(ft.data),g)}catch{}}),he.onerror=ae,Z.current=he}},[le,Re,P,I]),Ie=y.useCallback(async g=>{try{(await Ih(g)).messages.forEach(le)}catch{}},[le]),Ze=y.useCallback((g,O)=>{switch(g.kind){case"init":j({text:"",tools:[]});return;case"text":j(H=>({text:((H==null?void 0:H.text)??"")+g.text,tools:(H==null?void 0:H.tools)??[],...H!=null&&H.error?{error:H.error}:{}}));return;case"tool":j(H=>{const J=(H==null?void 0:H.tools)??[];return{text:(H==null?void 0:H.text)??"",tools:J.includes(g.tool)?J:[...J,g.tool]}});return;case"done":j(null),Ie(O);return;case"error":j(H=>({text:(H==null?void 0:H.text)??"",tools:(H==null?void 0:H.tools)??[],error:g.error}));return}},[Ie]);y.useEffect(()=>{let g=!1;return(async()=>{try{const O=await T0();if(g)return;s(O.channels),E(H=>{var J;return H&&O.channels.some(ae=>ae.name===H)?H:((J=O.channels[0])==null?void 0:J.name)??null})}catch(O){g||m(ar(O))}})(),()=>{g=!0}},[]),y.useEffect(()=>{const g=p;if(!g){k({text:"no channel",kind:""});return}let O=!1;return ue.current+=1,D.current=new Set,$.current=!1,v([]),j(null),k({text:"loading history...",kind:""}),(async()=>{try{const H=await Ih(g);if(O)return;H.messages.forEach(le),k({text:`live - ${g}`,kind:"live"})}catch(H){if(O)return;k({text:`history error: ${ar(H)}`,kind:"err"})}O||await xe(g)})(),()=>{var H,J;O=!0,ue.current+=1,(H=G.current)==null||H.close(),G.current=null,(J=Z.current)==null||J.close(),Z.current=null}},[p]);function De(g){E(g),o(`/chat/${encodeURIComponent(g)}`,{replace:!0})}async function U(){const g=Y.trim(),O=p;if(!(!g||!O||X)){M(!0),v(H=>[...H,{key:crypto.randomUUID(),kind:"you",text:g}]),L("");try{const H=await A0(O,g);H.id&&D.current.add(H.id)}catch(H){Re(`send failed: ${ar(H)}`)}finally{M(!1)}}}function K(g){g.key==="Enter"&&!g.shiftKey&&(g.preventDefault(),U())}const Q=((f==null?void 0:f.length)??0)>0,ne=!!p&&Y.trim().length>0&&!X,oe=y.useMemo(()=>z.kind==="live"?"chat-status status-live":z.kind==="err"?"chat-status status-err":"chat-status",[z.kind]);return c.jsxs("div",{className:"chat","data-testid":"chat-view",children:[c.jsxs("div",{className:"chat-head",children:[c.jsx("h1",{children:"Chat"}),f&&Q?c.jsxs("label",{className:"chat-picker",children:[c.jsx("span",{className:"chat-picker-label",children:"Channel"}),c.jsx("select",{"data-testid":"chat-channel-select",value:p??"",onChange:g=>De(g.target.value),children:f.map(g=>c.jsxs("option",{value:g.name,children:[g.name," (",g.transport,")"]},g.name))})]}):null,z.text?c.jsx("span",{className:oe,"data-testid":"chat-status",children:z.text}):null]}),d?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"chat-channels-error",children:d}):null,f&&!Q&&!d?c.jsx("div",{className:"empty","data-testid":"chat-no-channels",children:"No channels yet. Create a channel-backed agent in the create flow, then chat with it here."}):null,Q?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"transcript",ref:V,"data-testid":"chat-transcript",children:[S.map(g=>c.jsx("div",{className:`msg ${g.kind}`,"data-testid":`chat-msg-${g.kind}`,children:g.text},g.key)),C?c.jsx(tb,{turn:C}):null]}),c.jsxs("form",{className:"composer","data-testid":"chat-composer",onSubmit:g=>{g.preventDefault(),U()},children:[c.jsx("textarea",{className:"chat-input","data-testid":"chat-input",rows:1,value:Y,placeholder:"Type a message... (Enter to send, Shift+Enter for newline)",autoComplete:"off",onChange:g=>L(g.target.value),onKeyDown:K}),c.jsx("button",{type:"submit","data-testid":"chat-send",disabled:!ne,children:X?"Sending...":"Send"})]})]}):null]})}function tb({turn:i}){return c.jsxs("div",{className:`msg them live${i.error?" errored":""}`,"data-testid":"chat-live-turn",children:[i.text?c.jsx("div",{className:"live-text",children:i.text}):null,i.tools.length>0?c.jsx("div",{className:"live-tools",children:i.tools.map(o=>c.jsx("span",{className:"tool-chip","data-testid":`chat-tool-${o}`,children:o},o))}):null,c.jsx("div",{className:"live-working","data-testid":"chat-live-status",children:i.error?`turn failed: ${i.error}`:"working..."})]})}function ar(i){return i instanceof et&&i.status===401?"Not signed in to the hub - sign in to the portal, then reload.":i.message}const lb=/^[a-zA-Z0-9_-]+$/;function ab({vaults:i}){var V;const o=i.length>0,[f,s]=y.useState(""),[d,m]=y.useState(((V=i[0])==null?void 0:V.vault)??""),[p,E]=y.useState("single-threaded"),[S,v]=y.useState("programmatic"),[C,j]=y.useState(""),[z,k]=y.useState(""),[Y,L]=y.useState(""),[X,M]=y.useState(!1),[D,G]=y.useState({kind:"idle"}),Z=lb.test(f),$=o&&Z&&d.length>0&&D.kind!=="submitting";async function ue(I){if(I.preventDefault(),!!$){G({kind:"submitting"});try{const P=await c0({vault:d,name:f,backend:S,systemPrompt:z,metadata:{mode:p,...C?{model:C}:{}},...Y.trim().length>0?{wants:Y.trim()}:{}});G({kind:"ok",result:P,mode:p})}catch(P){const Re=P instanceof et&&P.status===401?"Not signed in to the hub — sign in to the portal, then reload.":P.message;G({kind:"error",message:Re})}}}return D.kind==="ok"?c.jsx(ub,{result:D.result,mode:D.mode}):c.jsxs("div",{children:[c.jsx("h1",{children:"New agent"}),c.jsxs("p",{className:"lede",children:["A ",c.jsx("code",{children:"#agent/definition"})," note IS the agent — writing it instantiates the agent and its channel routing in one step. The def-vault editor, schedules, and editing land in a later phase."]}),o?null:c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"no-def-vaults",children:["No def-vault is configured, and one is required to create an agent. The add / remove def-vault editor arrives in a later phase; until then, configure a def-vault out-of-band. Submitting anyway will return the daemon's"," ",c.jsx("code",{children:"no def-vaults configured"})," error."]}),D.kind==="error"?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"create-error",children:D.message}):null,c.jsxs("form",{className:"card",onSubmit:ue,"aria-label":"Create agent",children:[c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-name",children:"Name"}),c.jsx("input",{id:"agent-name",type:"text",value:f,placeholder:"my-agent",autoComplete:"off",onChange:I=>s(I.target.value)}),c.jsx("p",{className:"field-hint",children:"A slug (letters, numbers, dash, underscore). This is also the agent's channel."}),f.length>0&&!Z?c.jsx("p",{className:"field-error","data-testid":"name-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-vault",children:"Def-vault"}),c.jsx("select",{id:"agent-vault",value:d,disabled:!o,onChange:I=>m(I.target.value),children:o?i.map(I=>c.jsxs("option",{value:I.vault,children:[I.vault,I.tokenPresent?"":" (token missing)"]},I.vault)):c.jsx("option",{value:"",children:"No def-vaults configured"})}),c.jsx("p",{className:"field-hint",children:"The vault this agent's definition note is written to."})]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Mode"}),c.jsx(yi,{name:"mode",value:"single-threaded",checked:p==="single-threaded",onChange:()=>E("single-threaded"),label:"Single-threaded",help:"One continuous conversation — remembers everything on this channel.",testid:"mode-single-threaded"}),c.jsx(yi,{name:"mode",value:"multi-threaded",checked:p==="multi-threaded",onChange:()=>E("multi-threaded"),label:"Multi-threaded",help:"Each run is its own thread; today every run starts fresh (per-conversation continuation is coming) — good for scheduled or stateless tasks.",testid:"mode-multi-threaded"})]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Backend"}),c.jsx(yi,{name:"backend",value:"programmatic",checked:S==="programmatic",onChange:()=>v("programmatic"),label:"Programmatic",help:"Parachute runs it headless — always on, sandboxed.",testid:"backend-programmatic"}),c.jsx(yi,{name:"backend",value:"channel",checked:S==="channel",onChange:()=>v("channel"),label:"Channel",help:"You run a Claude Code session on your own machine and connect it — your env, unsandboxed.",testid:"backend-channel"})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-model",children:"Model"}),c.jsx("select",{id:"agent-model",value:C,"data-testid":"agent-model",onChange:I=>j(I.target.value),children:Cr.map(I=>c.jsx("option",{value:I.value,children:I.label},I.value))}),c.jsx("p",{className:"field-hint",children:"Which model Parachute runs this agent on (programmatic backend). A channel-backend agent uses whatever model your own session runs."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-prompt",children:"System prompt"}),c.jsx("textarea",{id:"agent-prompt",rows:6,value:z,placeholder:"You are…",onChange:I=>k(I.target.value)}),c.jsx("p",{className:"field-hint",children:"The agent's persona + instructions (the note body). Leave blank for Claude Code's default."})]}),c.jsxs("details",{className:"advanced",open:X,onToggle:I=>M(I.target.open),children:[c.jsx("summary",{children:"Advanced"}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-wants",children:"Wants (connections)"}),c.jsx("input",{id:"agent-wants",type:"text",value:Y,placeholder:"vault:other, service:github",autoComplete:"off",onChange:I=>L(I.target.value)}),c.jsx("p",{className:"field-hint",children:"Comma-separated connection keys the agent requests beyond its own vault. Each needs approval before it's granted."})]})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!$,children:D.kind==="submitting"?"Creating…":"Create agent"}),c.jsx(fl,{to:"/",className:"cancel-link",children:"Cancel"})]})]})]})}function nb(){const[i,o]=y.useState({kind:"loading"}),f=y.useCallback(async()=>{o({kind:"loading"});try{const s=await zm();o({kind:"ok",vaults:s.vaults})}catch(s){const d=s instanceof et?s.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load def-vaults: ${s.message}`:`Failed to load def-vaults: ${s.message}`;o({kind:"error",message:d})}},[]);return y.useEffect(()=>{f()},[f]),i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading…"}):i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void f(),children:"Retry"})]}):c.jsx(ab,{vaults:i.vaults},i.vaults.map(s=>s.vault).join(","))}function yi(i){return c.jsxs("label",{className:`radio-row${i.checked?" selected":""}`,"data-testid":i.testid,children:[c.jsx("input",{type:"radio",name:i.name,value:i.value,checked:i.checked,onChange:i.onChange}),c.jsxs("span",{className:"radio-body",children:[c.jsx("span",{className:"radio-label",children:i.label}),c.jsx("span",{className:"radio-help",children:i.help})]})]})}function ub({result:i,mode:o}){const f=i.def;return c.jsxs("div",{"data-testid":"create-success",children:[c.jsx("h1",{children:"Agent created"}),c.jsxs("div",{className:"success-banner",role:"status",children:[c.jsx("strong",{children:f.name})," · ",o," · ",f.backend]}),c.jsxs("p",{className:"lede",children:["The definition was written to ",c.jsx("code",{children:f.vault})," and instantiated. It now appears in the ",c.jsx(fl,{to:"/",children:"Agents list"}),"."]}),f.backend==="channel"?c.jsx(ib,{name:f.name}):null,c.jsx("p",{children:c.jsx(fl,{to:"/",children:"← Back to agents"})})]})}function ib({name:i}){const[o,f]=y.useState(!1),s=typeof window<"u"?window.location.origin:"",d=y.useMemo(()=>C0(i,s),[i,s]);async function m(){var p;try{await((p=navigator.clipboard)==null?void 0:p.writeText(d)),f(!0),setTimeout(()=>f(!1),2e3)}catch{}}return c.jsxs("section",{className:"card","aria-label":"Connect a session","data-testid":"connect-session",children:[c.jsx("h2",{children:"Connect your Claude Code session"}),c.jsx("p",{className:"muted",children:"Run this on your own machine to make a Claude Code session a responder for this channel:"}),c.jsxs("div",{className:"snippet-row",children:[c.jsx("code",{className:"snippet","data-testid":"connect-command",children:d}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>void m(),children:o?"Copied":"Copy"})]}),c.jsx("p",{className:"field-hint",children:"Your Claude Code session pulls messages from this channel; until you connect, inbound messages queue durably in the vault."})]})}const Mm=y.createContext(null);function cb(){const i=y.useContext(Mm);if(!i)throw new Error("useStepUp must be used inside <StepUpProvider>");return i}function sb({children:i}){const[o,f]=y.useState(null);y.useEffect(()=>(Fh(m=>new Promise(p=>{f({mode:m==="setup"?"setup":"prompt",resolve:p})})),()=>Fh(null)),[]);const s=y.useCallback(m=>{f(p=>{var E;return(E=p==null?void 0:p.resolve)==null||E.call(p,m),null})},[]),d=y.useMemo(()=>({openSettings:()=>f({mode:"settings",resolve:null})}),[]);return c.jsxs(Mm.Provider,{value:d,children:[i,o&&c.jsx(rb,{state:o,onClose:s})]})}function rb({state:i,onClose:o}){const{mode:f}=i,[s,d]=y.useState(""),[m,p]=y.useState(""),[E,S]=y.useState(""),[v,C]=y.useState(""),[j,z]=y.useState(!1),[k,Y]=y.useState(null),[L,X]=y.useState(f==="settings"?null:f!=="setup"),M=y.useRef(null);y.useEffect(()=>{var V;(V=M.current)==null||V.focus()},[]),y.useEffect(()=>{if(f!=="settings")return;let V=!0;return e0().then(I=>V&&X(I.configured)).catch(()=>V&&X(!1)),()=>{V=!1}},[f]);const D=y.useCallback(V=>{V.key==="Escape"&&o(null)},[o]);function G(V){return V instanceof et?V.status===401?"Incorrect PIN.":V.status===429?"Too many attempts — wait a minute and try again.":V.status===400?"PIN must be 4–12 digits.":V.message||"Something went wrong.":V instanceof Error?V.message:"Something went wrong."}async function Z(){Y(null),z(!0);try{const V=await $h(s);o(V)}catch(V){Y(G(V)),z(!1)}}async function $(){if(Y(null),m!==E){Y("The PINs don't match.");return}z(!0);try{await Wh(m);const V=await $h(m);o(V)}catch(V){Y(G(V)),z(!1)}}async function ue(){if(Y(null),m!==E){Y("The PINs don't match.");return}z(!0);try{await Wh(m,L?v:void 0),o(null)}catch(V){Y(G(V)),z(!1)}}return c.jsx("div",{className:"step-up-backdrop",role:"presentation",onClick:()=>o(null),children:c.jsxs("div",{className:"step-up-modal",role:"dialog","aria-modal":"true","aria-label":"Step-up PIN",onClick:V=>V.stopPropagation(),onKeyDown:D,children:[f==="prompt"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:"Confirm with your PIN"}),c.jsx("p",{className:"step-up-sub",children:"This is a high-privilege action. Enter your step-up PIN to continue."}),c.jsx(Xa,{label:"PIN",value:s,onChange:d,inputRef:M})]}),f==="setup"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:"Set a step-up PIN"}),c.jsx("p",{className:"step-up-sub",children:"High-privilege actions (set credentials, open a terminal, spawn a full-filesystem agent) require a PIN. Set one now to continue."}),c.jsx(Xa,{label:"New PIN (4–12 digits)",value:m,onChange:p,inputRef:M}),c.jsx(Xa,{label:"Confirm PIN",value:E,onChange:S})]}),f==="settings"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:L?"Change step-up PIN":"Set step-up PIN"}),c.jsx("p",{className:"step-up-sub",children:"The step-up PIN gates high-privilege admin actions (set credentials, open a terminal, spawn a full-filesystem agent) as a second factor on top of your login."}),L&&c.jsx(Xa,{label:"Current PIN",value:v,onChange:C,inputRef:M}),c.jsx(Xa,{label:"New PIN (4–12 digits)",value:m,onChange:p,inputRef:L?void 0:M}),c.jsx(Xa,{label:"Confirm PIN",value:E,onChange:S})]}),k&&c.jsx("p",{className:"step-up-error",role:"alert",children:k}),c.jsxs("div",{className:"step-up-actions",children:[c.jsx("button",{type:"button",className:"secondary",onClick:()=>o(null),disabled:j,children:"Cancel"}),c.jsx("button",{type:"button",onClick:f==="prompt"?Z:f==="setup"?$:ue,disabled:j||f==="settings"&&L===null,children:j?"Working…":f==="prompt"?"Confirm":"Save PIN"})]})]})})}function Xa({label:i,value:o,onChange:f,inputRef:s}){return c.jsxs("label",{className:"step-up-field",children:[c.jsx("span",{children:i}),c.jsx("input",{ref:s,type:"password",inputMode:"numeric",autoComplete:"off",value:o,onChange:d=>f(d.target.value.replace(/[^0-9]/g,"").slice(0,12))})]})}const ob="Parachute Agent";function fb(i){return i==="/create"||i.startsWith("/create/")?"new agent":i==="/chat"||i.startsWith("/chat/")?"chat":"agents"}function db(){return c.jsx(sb,{children:c.jsx(hb,{})})}function hb(){const{pathname:i}=Bt(),o=fb(i),{openSettings:f}=cb();return c.jsxs("div",{className:"page",children:[c.jsxs("nav",{className:"nav",children:[c.jsxs(fl,{to:"/",className:"brand",children:[c.jsx("span",{className:"brand-wordmark",children:ob}),c.jsx("span",{className:"sub",children:o})]}),c.jsx(nr,{to:"/",label:"Agents",exact:!0}),c.jsx(nr,{to:"/create",label:"New agent"}),c.jsx(nr,{to:"/chat",label:"Chat"}),c.jsx("button",{type:"button",className:"nav-link nav-link-button",onClick:f,title:"Set or change the step-up PIN that gates high-privilege actions",children:"Step-up PIN"})]}),c.jsxs(hp,{children:[c.jsx(na,{path:"/",element:c.jsx(am,{})}),c.jsx(na,{path:"/agents",element:c.jsx(am,{})}),c.jsx(na,{path:"/create",element:c.jsx(nb,{})}),c.jsx(na,{path:"/chat",element:c.jsx(im,{})}),c.jsx(na,{path:"/chat/:channel",element:c.jsx(im,{})}),c.jsx(na,{path:"*",element:c.jsxs("div",{className:"empty",children:["404 — back to ",c.jsx(fl,{to:"/",children:"Agents"}),"."]})})]})]})}function nr({to:i,label:o,exact:f}){const{pathname:s}=Bt(),d=f?s===i||s==="/agents":s===i||s.startsWith(`${i}/`);return c.jsx(fl,{to:i,className:d?"nav-link nav-link-active":"nav-link","aria-current":d?"page":void 0,children:o})}function mb(i){return i==="/agent/app"||i.startsWith("/agent/app/")?"/agent/app":i==="/app"||i.startsWith("/app/")?"/app":""}const Um=document.getElementById("root");if(!Um)throw new Error("#root not found");hg.createRoot(Um).render(c.jsx(y.StrictMode,{children:c.jsx(Bp,{basename:mb(window.location.pathname),children:c.jsx(db,{})})}));
60
+ Please change the parent <Route path="${M}"> to <Route path="${M==="/"?"*":`${M}/*`}">.`)}let C=Bt(),j;if(o){let M=typeof o=="string"?Za(o):o;Me(S==="/"||((X=M.pathname)==null?void 0:X.startsWith(S)),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${S}" but pathname "${M.pathname}" was given in the \`location\` prop.`),j=M}else j=C;let z=j.pathname||"/",k=z;if(S!=="/"){let M=S.replace(/^\//,"").split("/");k="/"+z.replace(/^\//,"").split("/").slice(M.length).join("/")}let Y=f&&f.state.matches.length?f.state.matches.map(M=>Object.assign(M,{route:f.manifest[M.route.id]||M.route})):sm(i,{pathname:k});Gt(v||Y!=null,`No routes matched location "${j.pathname}${j.search}${j.hash}" `),Gt(Y==null||Y[Y.length-1].route.element!==void 0||Y[Y.length-1].route.Component!==void 0||Y[Y.length-1].route.lazy!==void 0,`Matched leaf route at location "${j.pathname}${j.search}${j.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let L=np(Y&&Y.map(M=>Object.assign({},M,{params:Object.assign({},p,M.params),pathname:Ht([S,s.encodeLocation?s.encodeLocation(M.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathname]),pathnameBase:M.pathnameBase==="/"?S:Ht([S,s.encodeLocation?s.encodeLocation(M.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathnameBase])})),d,f);return o&&L?y.createElement(Jn.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...j},navigationType:"POP"}},L):L}function ep(){let i=rp(),o=qg(i)?`${i.status} ${i.statusText}`:i instanceof Error?i.message:JSON.stringify(i),f=i instanceof Error?i.stack:null,s="rgba(200,200,200, 0.5)",d={padding:"0.5rem",backgroundColor:s},m={padding:"2px 4px",backgroundColor:s},p=null;return console.error("Error handled by React Router default ErrorBoundary:",i),p=y.createElement(y.Fragment,null,y.createElement("p",null,"💿 Hey developer 👋"),y.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",y.createElement("code",{style:m},"ErrorBoundary")," or"," ",y.createElement("code",{style:m},"errorElement")," prop on your route.")),y.createElement(y.Fragment,null,y.createElement("h2",null,"Unexpected Application Error!"),y.createElement("h3",{style:{fontStyle:"italic"}},o),f?y.createElement("pre",{style:d},f):null,p)}var tp=y.createElement(ep,null),Cm=class extends y.Component{constructor(i){super(i),this.state={location:i.location,revalidation:i.revalidation,error:i.error}}static getDerivedStateFromError(i){return{error:i}}static getDerivedStateFromProps(i,o){return o.location!==i.location||o.revalidation!=="idle"&&i.revalidation==="idle"?{error:i.error,location:i.location,revalidation:i.revalidation}:{error:i.error!==void 0?i.error:o.error,location:o.location,revalidation:i.revalidation||o.revalidation}}componentDidCatch(i,o){this.props.onError?this.props.onError(i,o):console.error("React Router caught the following error during render",i)}render(){let i=this.state.error;if(this.context&&typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){const f=$g(i.digest);f&&(i=f)}let o=i!==void 0?y.createElement(Xt.Provider,{value:this.props.routeContext},y.createElement(dr.Provider,{value:i,children:this.props.component})):this.props.children;return this.context?y.createElement(lp,{error:i},o):o}};Cm.contextType=pm;var tr=new WeakMap;function lp({children:i,error:o}){let{basename:f}=y.useContext(Ot);if(typeof o=="object"&&o&&"digest"in o&&typeof o.digest=="string"){let s=Jg(o.digest);if(s){let d=tr.get(o);if(d)throw d;let m=ym(s.location,f),p=m.absoluteURL||m.to;if(Gg(p))throw new Error("Invalid redirect location");if(vm&&!tr.get(o))if(m.isExternal||s.reloadDocument)window.location.href=p;else{const E=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(m.to,{replace:s.replace}));throw tr.set(o,E),E}return y.createElement("meta",{httpEquiv:"refresh",content:`0;url=${p}`})}}return i}function ap({routeContext:i,match:o,children:f}){let s=y.useContext(Ka);return s&&s.static&&s.staticContext&&(o.route.errorElement||o.route.ErrorBoundary)&&(s.staticContext._deepestRenderedBoundaryId=o.route.id),y.createElement(Xt.Provider,{value:i},f)}function np(i,o=[],f){let s=f==null?void 0:f.state;if(i==null){if(!s)return null;if(s.errors)i=s.matches;else if(o.length===0&&!s.initialized&&s.matches.length>0)i=s.matches;else return null}let d=i,m=s==null?void 0:s.errors;if(m!=null){let C=d.findIndex(j=>j.route.id&&(m==null?void 0:m[j.route.id])!==void 0);Me(C>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(m).join(",")}`),d=d.slice(0,Math.min(d.length,C+1))}let p=!1,E=-1;if(f&&s){p=s.renderFallback;for(let C=0;C<d.length;C++){let j=d[C];if((j.route.HydrateFallback||j.route.hydrateFallbackElement)&&(E=C),j.route.id){let{loaderData:z,errors:k}=s,Y=j.route.loader&&!z.hasOwnProperty(j.route.id)&&(!k||k[j.route.id]===void 0);if(j.route.lazy||Y){f.isStatic&&(p=!0),E>=0?d=d.slice(0,E+1):d=[d[0]];break}}}}let S=f==null?void 0:f.onError,v=s&&S?(C,j)=>{var z,k;S(C,{location:s.location,params:((k=(z=s.matches)==null?void 0:z[0])==null?void 0:k.params)??{},pattern:Lg(s.matches),errorInfo:j})}:void 0;return d.reduceRight((C,j,z)=>{let k,Y=!1,L=null,X=null;s&&(k=m&&j.route.id?m[j.route.id]:void 0,L=j.route.errorElement||tp,p&&(E<0&&z===0?(Tm("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),Y=!0,X=null):E===z&&(Y=!0,X=j.route.hydrateFallbackElement||null)));let M=o.concat(d.slice(0,z+1)),D=()=>{let G;return k?G=L:Y?G=X:j.route.Component?G=y.createElement(j.route.Component,null):j.route.element?G=j.route.element:G=C,y.createElement(ap,{match:j,routeContext:{outlet:C,matches:M,isDataRoute:s!=null},children:G})};return s&&(j.route.ErrorBoundary||j.route.errorElement||z===0)?y.createElement(Cm,{location:s.location,revalidation:s.revalidation,component:L,error:k,children:D(),routeContext:{outlet:null,matches:M,isDataRoute:!0},onError:v}):D()},null)}function hr(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function up(i){let o=y.useContext(Ka);return Me(o,hr(i)),o}function ip(i){let o=y.useContext(ji);return Me(o,hr(i)),o}function cp(i){let o=y.useContext(Xt);return Me(o,hr(i)),o}function mr(i){let o=cp(i),f=o.matches[o.matches.length-1];return Me(f.route.id,`${i} can only be used on routes that contain a unique "id"`),f.route.id}function sp(){return mr("useRouteId")}function rp(){var s;let i=y.useContext(dr),o=ip("useRouteError"),f=mr("useRouteError");return i!==void 0?i:(s=o.errors)==null?void 0:s[f]}function op(){let{router:i}=up("useNavigate"),o=mr("useNavigate"),f=y.useRef(!1);return Em(()=>{f.current=!0}),y.useCallback(async(d,m={})=>{Gt(f.current,xm),f.current&&(typeof d=="number"?await i.navigate(d):await i.navigate(d,{fromRouteId:o,...m}))},[i,o])}var Qh={};function Tm(i,o,f){!o&&!Qh[i]&&(Qh[i]=!0,Gt(!1,f))}y.memo(fp);function fp({routes:i,manifest:o,future:f,state:s,isStatic:d,onError:m}){return Nm(i,void 0,{manifest:o,state:s,isStatic:d,onError:m})}function na(i){Me(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function dp({basename:i="/",children:o=null,location:f,navigationType:s="POP",navigator:d,static:m=!1,useTransitions:p}){Me(!$n(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let E=i.replace(/^\/*/,"/"),S=y.useMemo(()=>({basename:E,navigator:d,static:m,useTransitions:p,future:{}}),[E,d,m,p]);typeof f=="string"&&(f=Za(f));let{pathname:v="/",search:C="",hash:j="",state:z=null,key:k="default",mask:Y}=f,L=y.useMemo(()=>{let X=ol(v,E);return X==null?null:{location:{pathname:X,search:C,hash:j,state:z,key:k,mask:Y},navigationType:s}},[E,v,C,j,z,k,s,Y]);return Gt(L!=null,`<Router basename="${E}"> is not able to match the URL "${v}${C}${j}" because it does not start with the basename, so the <Router> won't render anything.`),L==null?null:y.createElement(Ot.Provider,{value:S},y.createElement(Jn.Provider,{children:o,value:L}))}function hp({children:i,location:o}){return Pg(ir(i),o)}function ir(i,o=[]){let f=[];return y.Children.forEach(i,(s,d)=>{if(!y.isValidElement(s))return;let m=[...o,d];if(s.type===y.Fragment){f.push.apply(f,ir(s.props.children,m));return}Me(s.type===na,`[${typeof s.type=="string"?s.type:s.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),Me(!s.props.index||!s.props.children,"An index route cannot have child routes.");let p={id:s.props.id||m.join("-"),caseSensitive:s.props.caseSensitive,element:s.props.element,Component:s.props.Component,index:s.props.index,path:s.props.path,middleware:s.props.middleware,loader:s.props.loader,action:s.props.action,hydrateFallbackElement:s.props.hydrateFallbackElement,HydrateFallback:s.props.HydrateFallback,errorElement:s.props.errorElement,ErrorBoundary:s.props.ErrorBoundary,hasErrorBoundary:s.props.hasErrorBoundary===!0||s.props.ErrorBoundary!=null||s.props.errorElement!=null,shouldRevalidate:s.props.shouldRevalidate,handle:s.props.handle,lazy:s.props.lazy};s.props.children&&(p.children=ir(s.props.children,m)),f.push(p)}),f}var gi="get",pi="application/x-www-form-urlencoded";function Ni(i){return typeof HTMLElement<"u"&&i instanceof HTMLElement}function mp(i){return Ni(i)&&i.tagName.toLowerCase()==="button"}function vp(i){return Ni(i)&&i.tagName.toLowerCase()==="form"}function yp(i){return Ni(i)&&i.tagName.toLowerCase()==="input"}function gp(i){return!!(i.metaKey||i.altKey||i.ctrlKey||i.shiftKey)}function pp(i,o){return i.button===0&&(!o||o==="_self")&&!gp(i)}var vi=null;function bp(){if(vi===null)try{new FormData(document.createElement("form"),0),vi=!1}catch{vi=!0}return vi}var Sp=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function lr(i){return i!=null&&!Sp.has(i)?(Gt(!1,`"${i}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${pi}"`),null):i}function xp(i,o){let f,s,d,m,p;if(vp(i)){let E=i.getAttribute("action");s=E?ol(E,o):null,f=i.getAttribute("method")||gi,d=lr(i.getAttribute("enctype"))||pi,m=new FormData(i)}else if(mp(i)||yp(i)&&(i.type==="submit"||i.type==="image")){let E=i.form;if(E==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let S=i.getAttribute("formaction")||E.getAttribute("action");if(s=S?ol(S,o):null,f=i.getAttribute("formmethod")||E.getAttribute("method")||gi,d=lr(i.getAttribute("formenctype"))||lr(E.getAttribute("enctype"))||pi,m=new FormData(E,i),!bp()){let{name:v,type:C,value:j}=i;if(C==="image"){let z=v?`${v}.`:"";m.append(`${z}x`,"0"),m.append(`${z}y`,"0")}else v&&m.append(v,j)}}else{if(Ni(i))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');f=gi,s=null,d=pi,p=i}return m&&d==="text/plain"&&(p=m,m=void 0),{action:s,method:f.toLowerCase(),encType:d,formData:m,body:p}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function vr(i,o){if(i===!1||i===null||typeof i>"u")throw new Error(o)}function Am(i,o,f,s){let d=typeof i=="string"?new URL(i,typeof window>"u"?"server://singlefetch/":window.location.origin):i;return f?d.pathname.endsWith("/")?d.pathname=`${d.pathname}_.${s}`:d.pathname=`${d.pathname}.${s}`:d.pathname==="/"?d.pathname=`_root.${s}`:o&&ol(d.pathname,o)==="/"?d.pathname=`${xi(o)}/_root.${s}`:d.pathname=`${xi(d.pathname)}.${s}`,d}async function Ep(i,o){if(i.id in o)return o[i.id];try{let f=await import(i.module);return o[i.id]=f,f}catch(f){return console.error(`Error loading route module \`${i.module}\`, reloading page...`),console.error(f),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function jp(i){return i==null?!1:i.href==null?i.rel==="preload"&&typeof i.imageSrcSet=="string"&&typeof i.imageSizes=="string":typeof i.rel=="string"&&typeof i.href=="string"}async function Np(i,o,f){let s=await Promise.all(i.map(async d=>{let m=o.routes[d.route.id];if(m){let p=await Ep(m,f);return p.links?p.links():[]}return[]}));return Rp(s.flat(1).filter(jp).filter(d=>d.rel==="stylesheet"||d.rel==="preload").map(d=>d.rel==="stylesheet"?{...d,rel:"prefetch",as:"style"}:{...d,rel:"prefetch"}))}function Vh(i,o,f,s,d,m){let p=(S,v)=>f[v]?S.route.id!==f[v].route.id:!0,E=(S,v)=>{var C;return f[v].pathname!==S.pathname||((C=f[v].route.path)==null?void 0:C.endsWith("*"))&&f[v].params["*"]!==S.params["*"]};return m==="assets"?o.filter((S,v)=>p(S,v)||E(S,v)):m==="data"?o.filter((S,v)=>{var j;let C=s.routes[S.route.id];if(!C||!C.hasLoader)return!1;if(p(S,v)||E(S,v))return!0;if(S.route.shouldRevalidate){let z=S.route.shouldRevalidate({currentUrl:new URL(d.pathname+d.search+d.hash,window.origin),currentParams:((j=f[0])==null?void 0:j.params)||{},nextUrl:new URL(i,window.origin),nextParams:S.params,defaultShouldRevalidate:!0});if(typeof z=="boolean")return z}return!0}):[]}function Cp(i,o,{includeHydrateFallback:f}={}){return Tp(i.map(s=>{let d=o.routes[s.route.id];if(!d)return[];let m=[d.module];return d.clientActionModule&&(m=m.concat(d.clientActionModule)),d.clientLoaderModule&&(m=m.concat(d.clientLoaderModule)),f&&d.hydrateFallbackModule&&(m=m.concat(d.hydrateFallbackModule)),d.imports&&(m=m.concat(d.imports)),m}).flat(1))}function Tp(i){return[...new Set(i)]}function Ap(i){let o={},f=Object.keys(i).sort();for(let s of f)o[s]=i[s];return o}function Rp(i,o){let f=new Set;return new Set(o),i.reduce((s,d)=>{let m=JSON.stringify(Ap(d));return f.has(m)||(f.add(m),s.push({key:m,link:d})),s},[])}function yr(){let i=y.useContext(Ka);return vr(i,"You must render this element inside a <DataRouterContext.Provider> element"),i}function zp(){let i=y.useContext(ji);return vr(i,"You must render this element inside a <DataRouterStateContext.Provider> element"),i}var gr=y.createContext(void 0);gr.displayName="FrameworkContext";function Ci(){let i=y.useContext(gr);return vr(i,"You must render this element inside a <HydratedRouter> element"),i}function _p(i,o){let f=y.useContext(gr),[s,d]=y.useState(!1),[m,p]=y.useState(!1),{onFocus:E,onBlur:S,onMouseEnter:v,onMouseLeave:C,onTouchStart:j}=o,z=y.useRef(null);y.useEffect(()=>{if(i==="render"&&p(!0),i==="viewport"){let L=M=>{M.forEach(D=>{p(D.isIntersecting)})},X=new IntersectionObserver(L,{threshold:.5});return z.current&&X.observe(z.current),()=>{X.disconnect()}}},[i]),y.useEffect(()=>{if(s){let L=setTimeout(()=>{p(!0)},100);return()=>{clearTimeout(L)}}},[s]);let k=()=>{d(!0)},Y=()=>{d(!1),p(!1)};return f?i!=="intent"?[m,z,{}]:[m,z,{onFocus:Qn(E,k),onBlur:Qn(S,Y),onMouseEnter:Qn(v,k),onMouseLeave:Qn(C,Y),onTouchStart:Qn(j,k)}]:[!1,z,{}]}function Qn(i,o){return f=>{i&&i(f),f.defaultPrevented||o(f)}}function Dp({page:i,...o}){let f=Xg(),{nonce:s}=Ci(),{router:d}=yr(),m=y.useMemo(()=>sm(d.routes,i,d.basename),[d.routes,i,d.basename]);return m?(o.nonce==null&&s&&(o={...o,nonce:s}),f?y.createElement(Mp,{page:i,matches:m,...o}):y.createElement(Up,{page:i,matches:m,...o})):null}function Op(i){let{manifest:o,routeModules:f}=Ci(),[s,d]=y.useState([]);return y.useEffect(()=>{let m=!1;return Np(i,o,f).then(p=>{m||d(p)}),()=>{m=!0}},[i,o,f]),s}function Mp({page:i,matches:o,...f}){let s=Bt(),{future:d}=Ci(),{basename:m}=yr(),p=y.useMemo(()=>{if(i===s.pathname+s.search+s.hash)return[];let E=Am(i,m,d.v8_trailingSlashAwareDataRequests,"rsc"),S=!1,v=[];for(let C of o)typeof C.route.shouldRevalidate=="function"?S=!0:v.push(C.route.id);return S&&v.length>0&&E.searchParams.set("_routes",v.join(",")),[E.pathname+E.search]},[m,d.v8_trailingSlashAwareDataRequests,i,s,o]);return y.createElement(y.Fragment,null,p.map(E=>y.createElement("link",{key:E,rel:"prefetch",as:"fetch",href:E,...f})))}function Up({page:i,matches:o,...f}){let s=Bt(),{future:d,manifest:m,routeModules:p}=Ci(),{basename:E}=yr(),{loaderData:S,matches:v}=zp(),C=y.useMemo(()=>Vh(i,o,v,m,s,"data"),[i,o,v,m,s]),j=y.useMemo(()=>Vh(i,o,v,m,s,"assets"),[i,o,v,m,s]),z=y.useMemo(()=>{if(i===s.pathname+s.search+s.hash)return[];let L=new Set,X=!1;if(o.forEach(D=>{var Z;let G=m.routes[D.route.id];!G||!G.hasLoader||(!C.some($=>$.route.id===D.route.id)&&D.route.id in S&&((Z=p[D.route.id])!=null&&Z.shouldRevalidate)||G.hasClientLoader?X=!0:L.add(D.route.id))}),L.size===0)return[];let M=Am(i,E,d.v8_trailingSlashAwareDataRequests,"data");return X&&L.size>0&&M.searchParams.set("_routes",o.filter(D=>L.has(D.route.id)).map(D=>D.route.id).join(",")),[M.pathname+M.search]},[E,d.v8_trailingSlashAwareDataRequests,S,s,m,C,o,i,p]),k=y.useMemo(()=>Cp(j,m),[j,m]),Y=Op(j);return y.createElement(y.Fragment,null,z.map(L=>y.createElement("link",{key:L,rel:"prefetch",as:"fetch",href:L,...f})),k.map(L=>y.createElement("link",{key:L,rel:"modulepreload",href:L,...f})),Y.map(({key:L,link:X})=>y.createElement("link",{key:L,nonce:f.nonce,...X,crossOrigin:X.crossOrigin??f.crossOrigin})))}function wp(...i){return o=>{i.forEach(f=>{typeof f=="function"?f(o):f!=null&&(f.current=o)})}}var Hp=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{Hp&&(window.__reactRouterVersion="7.18.0")}catch{}function Bp({basename:i,children:o,useTransitions:f,window:s}){let d=y.useRef();d.current==null&&(d.current=vg({window:s,v5Compat:!0}));let m=d.current,[p,E]=y.useState({action:m.action,location:m.location}),S=y.useCallback(v=>{f===!1?E(v):y.startTransition(()=>E(v))},[f]);return y.useLayoutEffect(()=>m.listen(S),[m,S]),y.createElement(dp,{basename:i,children:o,location:p.location,navigationType:p.action,navigator:m,useTransitions:f})}var fl=y.forwardRef(function({onClick:o,discover:f="render",prefetch:s="none",relative:d,reloadDocument:m,replace:p,mask:E,state:S,target:v,to:C,preventScrollReset:j,viewTransition:z,defaultShouldRevalidate:k,...Y},L){let{basename:X,navigator:M,useTransitions:D}=y.useContext(Ot),G=typeof C=="string"&&or.test(C),Z=ym(C,X);C=Z.to;let $=Wg(C,{relative:d}),ue=Bt(),V=null;if(E){let De=fr(E,[],ue.mask?ue.mask.pathname:"/",!0);X!=="/"&&(De.pathname=De.pathname==="/"?X:Ht([X,De.pathname])),V=M.createHref(De)}let[I,P,Re]=_p(s,Y),le=Yp(C,{replace:p,mask:E,state:S,target:v,preventScrollReset:j,relative:d,viewTransition:z,defaultShouldRevalidate:k,useTransitions:D});function xe(De){o&&o(De),De.defaultPrevented||le(De)}let Ie=!(Z.isExternal||m),Ze=y.createElement("a",{...Y,...Re,href:(Ie?V:void 0)||Z.absoluteURL||$,onClick:Ie?xe:o,ref:wp(L,P),target:v,"data-discover":!G&&f==="render"?"true":void 0});return I&&!G?y.createElement(y.Fragment,null,Ze,y.createElement(Dp,{page:$})):Ze});fl.displayName="Link";var qp=y.forwardRef(function({"aria-current":o="page",caseSensitive:f=!1,className:s="",end:d=!1,style:m,to:p,viewTransition:E,children:S,...v},C){let j=Wn(p,{relative:v.relative}),z=Bt(),k=y.useContext(ji),{navigator:Y,basename:L}=y.useContext(Ot),X=k!=null&&Zp(j)&&E===!0,M=Y.encodeLocation?Y.encodeLocation(j).pathname:j.pathname,D=z.pathname,G=k&&k.navigation&&k.navigation.location?k.navigation.location.pathname:null;f||(D=D.toLowerCase(),G=G?G.toLowerCase():null,M=M.toLowerCase()),G&&L&&(G=ol(G,L)||G);const Z=M!=="/"&&M.endsWith("/")?M.length-1:M.length;let $=D===M||!d&&D.startsWith(M)&&D.charAt(Z)==="/",ue=G!=null&&(G===M||!d&&G.startsWith(M)&&G.charAt(M.length)==="/"),V={isActive:$,isPending:ue,isTransitioning:X},I=$?o:void 0,P;typeof s=="function"?P=s(V):P=[s,$?"active":null,ue?"pending":null,X?"transitioning":null].filter(Boolean).join(" ");let Re=typeof m=="function"?m(V):m;return y.createElement(fl,{...v,"aria-current":I,className:P,ref:C,style:Re,to:p,viewTransition:E},typeof S=="function"?S(V):S)});qp.displayName="NavLink";var Lp=y.forwardRef(({discover:i="render",fetcherKey:o,navigate:f,reloadDocument:s,replace:d,state:m,method:p=gi,action:E,onSubmit:S,relative:v,preventScrollReset:C,viewTransition:j,defaultShouldRevalidate:z,...k},Y)=>{let{useTransitions:L}=y.useContext(Ot),X=Qp(),M=Vp(E,{relative:v}),D=p.toLowerCase()==="get"?"get":"post",G=typeof E=="string"&&or.test(E),Z=$=>{if(S&&S($),$.defaultPrevented)return;$.preventDefault();let ue=$.nativeEvent.submitter,V=(ue==null?void 0:ue.getAttribute("formmethod"))||p,I=()=>X(ue||$.currentTarget,{fetcherKey:o,method:V,navigate:f,replace:d,state:m,relative:v,preventScrollReset:C,viewTransition:j,defaultShouldRevalidate:z});L&&f!==!1?y.startTransition(()=>I()):I()};return y.createElement("form",{ref:Y,method:D,action:M,onSubmit:s?S:Z,...k,"data-discover":!G&&i==="render"?"true":void 0})});Lp.displayName="Form";function kp(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Rm(i){let o=y.useContext(Ka);return Me(o,kp(i)),o}function Yp(i,{target:o,replace:f,mask:s,state:d,preventScrollReset:m,relative:p,viewTransition:E,defaultShouldRevalidate:S,useTransitions:v}={}){let C=jm(),j=Bt(),z=Wn(i,{relative:p});return y.useCallback(k=>{if(pp(k,o)){k.preventDefault();let Y=f!==void 0?f:Zn(j)===Zn(z),L=()=>C(i,{replace:Y,mask:s,state:d,preventScrollReset:m,relative:p,viewTransition:E,defaultShouldRevalidate:S});v?y.startTransition(()=>L()):L()}},[j,C,z,f,s,d,o,i,m,p,E,S,v])}var Gp=0,Xp=()=>`__${String(++Gp)}__`;function Qp(){let{router:i}=Rm("useSubmit"),{basename:o}=y.useContext(Ot),f=sp(),s=i.fetch,d=i.navigate;return y.useCallback(async(m,p={})=>{let{action:E,method:S,encType:v,formData:C,body:j}=xp(m,o);if(p.navigate===!1){let z=p.fetcherKey||Xp();await s(z,f,p.action||E,{defaultShouldRevalidate:p.defaultShouldRevalidate,preventScrollReset:p.preventScrollReset,formData:C,body:j,formMethod:p.method||S,formEncType:p.encType||v,flushSync:p.flushSync})}else await d(p.action||E,{defaultShouldRevalidate:p.defaultShouldRevalidate,preventScrollReset:p.preventScrollReset,formData:C,body:j,formMethod:p.method||S,formEncType:p.encType||v,replace:p.replace,state:p.state,fromRouteId:f,flushSync:p.flushSync,viewTransition:p.viewTransition})},[s,d,o,f])}function Vp(i,{relative:o}={}){let{basename:f}=y.useContext(Ot),s=y.useContext(Xt);Me(s,"useFormAction must be used inside a RouteContext");let[d]=s.matches.slice(-1),m={...Wn(i||".",{relative:o})},p=Bt();if(i==null){m.search=p.search;let E=new URLSearchParams(m.search),S=E.getAll("index");if(S.some(C=>C==="")){E.delete("index"),S.filter(j=>j).forEach(j=>E.append("index",j));let C=E.toString();m.search=C?`?${C}`:""}}return(!i||i===".")&&d.route.index&&(m.search=m.search?m.search.replace(/^\?/,"?index&"):"?index"),f!=="/"&&(m.pathname=m.pathname==="/"?f:Ht([f,m.pathname])),Zn(m)}function Zp(i,{relative:o}={}){let f=y.useContext(bm);Me(f!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:s}=Rm("useViewTransitionState"),d=Wn(i,{relative:o});if(!f.isTransitioning)return!1;let m=ol(f.currentLocation.pathname,s)||f.currentLocation.pathname,p=ol(f.nextLocation.pathname,s)||f.nextLocation.pathname;return Si(d.pathname,p)!=null||Si(d.pathname,m)!=null}let rl=null,Vn=null;const Kp=3e4,Jp=600*1e3;function $p(){return`${typeof window<"u"?window.location.origin:""}/admin/agent-token`}async function Va(){const i=Date.now();return rl&&rl.expiresAt-i>Kp?rl.token:Vn||(Vn=Wp().finally(()=>{Vn=null}),Vn)}async function Wp(){let i;try{i=await fetch($p(),{method:"GET",headers:{accept:"application/json"},credentials:"include"})}catch{return rl=null,null}if(!i.ok)return rl=null,null;let o;try{o=await i.json()}catch{return rl=null,null}if(!o.token)return rl=null,null;const f=o.expires_at?new Date(o.expires_at).getTime():Date.now()+Jp;return rl={token:o.token,expiresAt:f},o.token}function Ti(){rl=null}async function Zh(){const i=await Va();if(!i)return null;let o;try{o=await fetch(Kh(),{method:"POST",headers:{accept:"application/json",authorization:`Bearer ${i}`},credentials:"include"})}catch{return null}if(o.status===401){Ti();const s=await Va();if(!s)return null;try{o=await fetch(Kh(),{method:"POST",headers:{accept:"application/json",authorization:`Bearer ${s}`},credentials:"include"})}catch{return null}}if(!o.ok)return null;let f;try{f=await o.json()}catch{return null}return f.ticket??null}function Kh(){const o="/agent/app/".match(/^(.*\/)app\/?$/);return`${o?`${o[1]}api`:"/agent/api"}/ui/sse-ticket`}const Jh="x-step-up-token";let Qa=null;const Fp=5e3;function bi(){return Qa?Qa.expiresAt-Date.now()<=Fp?(Qa=null,null):Qa.token:null}function Ip(i,o){Qa={token:i,expiresAt:o}}function Pp(){Qa=null}async function pr(i,o={}){const f=await Va(),s=new Headers(o.headers);s.set("accept","application/json"),f&&s.set("authorization",`Bearer ${f}`);const d=await fetch(`${Bl()}${i}`,{...o,headers:s});if(d.status!==401)return d;Ti();const m=await Va();if(!m)return d;const p=new Headers(o.headers);return p.set("accept","application/json"),p.set("authorization",`Bearer ${m}`),fetch(`${Bl()}${i}`,{...o,headers:p})}async function br(i){try{return(await i.json()).error??""}catch{return await i.text().catch(()=>"")}}async function e0(){const i=await pr("/step-up");if(!i.ok)throw new et(i.status,await br(i)||`step-up status failed: ${i.status}`);return await i.json()}async function $h(i){const o=await pr("/step-up",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({pin:i})});if(!o.ok)throw new et(o.status,await br(o)||`PIN exchange failed: ${o.status}`);const f=await o.json(),s=new Date(f.expires_at).getTime();return Ip(f.stepUpToken,s),f.stepUpToken}async function Wh(i,o){const f=await pr("/step-up/pin",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({newPin:i,...o?{currentPin:o}:{}})});if(!f.ok)throw new et(f.status,await br(f)||`set PIN failed: ${f.status}`)}let cr=null;function Fh(i){cr=i}async function t0(i){const o=bi();return o&&i==="token"?o:cr?cr(i):null}class et extends Error{constructor(o,f){super(f),this.status=o,this.name="HttpError"}}function Bl(){const o="/agent/app/".match(/^(.*\/)app\/?$/);return o?`${o[1]}api`:"/agent/api"}async function Ai(i,o={},f=!1){const s=await Va(),d=new Headers(o.headers);d.set("accept","application/json"),s&&d.set("authorization",`Bearer ${s}`);const m=bi();m&&d.set(Jh,m);const p=await fetch(i,{...o,headers:d});if(p.status===403&&!f){const C=await l0(p);if(C)return bi()&&Pp(),await t0(C)?Ai(i,o,!0):p}if(p.status!==401)return p;Ti();const E=await Va();if(!E)return p;const S=new Headers(o.headers);S.set("accept","application/json"),S.set("authorization",`Bearer ${E}`);const v=bi();return v&&S.set(Jh,v),fetch(i,{...o,headers:S})}async function l0(i){try{const o=await i.clone().json();return o.error!=="step_up_required"?null:o.reason==="setup"?"setup":o.reason==="token"?"token":null}catch{return null}}async function Sr(i){try{return(await i.json()).error??""}catch{return await i.text().catch(()=>"")}}async function Qt(i){const o=await Ai(`${Bl()}${i}`);if(!o.ok)throw new et(o.status,await Sr(o)||`${i} failed: ${o.status}`);return await o.json()}async function ql(i,o){return xr("POST",i,o)}async function a0(i,o){return xr("PATCH",i,o)}async function Ri(i){const o=await Ai(`${Bl()}${i}`,{method:"DELETE"});if(!o.ok)throw new et(o.status,await Sr(o)||`${i} failed: ${o.status}`);return await o.json()}async function xr(i,o,f){const s=await Ai(`${Bl()}${o}`,{method:i,headers:{"content-type":"application/json"},body:JSON.stringify(f)});if(!s.ok)throw new et(s.status,await Sr(s)||`${o} failed: ${s.status}`);return await s.json()}function n0(i,o){return xr("DELETE",i,o)}function u0(i){return i==="attached"||i==="channel"?"channel":"programmatic"}async function i0(){return{agents:(await Qt("/agents")).agents.map(o=>({...o,backend:u0(o.backend)}))}}function c0(){return Qt("/agent-defs")}function zm(){return Qt("/agent-vaults")}function s0(i){return ql("/agent-defs",i)}function r0(i){return Qt(`/agent-defs/${encodeURIComponent(i)}`)}function _m(i,o){return a0(`/agent-defs/${encodeURIComponent(i)}`,o)}function o0(i){return Ri(`/agent-defs/${encodeURIComponent(i)}`)}function f0(i){return ql("/agent-vaults",i)}function d0(i){return Ri(`/agent-vaults/${encodeURIComponent(i)}`)}const h0=new Set(["ANTHROPIC_API_KEY","CLAUDE_API_KEY","CLAUDE_CODE_OAUTH_TOKEN"]),m0=/^[A-Za-z_][A-Za-z0-9_]*$/;function v0(){return Qt("/credentials/env")}function y0(i){return ql("/credentials/env",i)}function g0(i){return n0("/credentials/env",i)}function p0(){return Qt("/credentials/claude")}function b0(i){var f;const o=(f=i.channel)==null?void 0:f.trim();return o&&o.length>0?ql(`/credentials/claude/${encodeURIComponent(o)}`,{token:i.token}):ql("/credentials/claude",{token:i.token})}function S0(i){return Ri(`/credentials/claude/${encodeURIComponent(i)}`)}function x0(i){return Qt(`/agents/${encodeURIComponent(i)}/env`)}function E0(){return Qt("/jobs")}function j0(i){return ql("/jobs",i)}function N0(i){return ql(`/jobs/${encodeURIComponent(i)}/run`,{})}function C0(i){return Ri(`/jobs/${encodeURIComponent(i)}`)}function T0(i,o){const f=Bl().replace(/\/api$/,"");return`claude mcp add --transport http --scope user agent-${i} ${o}${f}/mcp/${i}`}function A0(){return Qt("/channels")}function Ih(i){return Qt(`/channels/${encodeURIComponent(i)}/messages`)}function R0(i,o){return ql(`/channels/${encodeURIComponent(i)}/send`,{text:o})}function z0(i,o){let s=`${Bl().replace(/\/api$/,"")}/ui/events?channel=${encodeURIComponent(i)}`;return o&&(s+=`&ticket=${encodeURIComponent(o)}`),s}function _0(i,o){let s=`${Bl().replace(/\/api$/,"")}/api/channels/${encodeURIComponent(i)}/turn-events`;return o&&(s+=`?ticket=${encodeURIComponent(o)}`),s}class Fn extends Error{constructor(o,f){super(f),this.status=o,this.name="HubError"}}function D0(){return typeof window<"u"?window.location.origin:""}function O0(){if(typeof window>"u")return!1;const{hostname:i,port:o}=window.location;return(i==="127.0.0.1"||i==="localhost"||i==="[::1]")&&o==="1941"}const Er="definition.reload",jr="note.created",Nr="note.updated",M0="agent/definition";function sr(i,o){return`agentdefs-${o}-${i}`}function U0(i,o){return{id:sr(i,o),requestedBy:"agent",source:{module:"vault",vault:i,event:o==="create"?jr:Nr,filter:{tags:[M0]}},sink:{module:"agent",action:Er}}}async function zi(i,o={}){const f=new Headers(o.headers);return f.set("accept","application/json"),fetch(`${D0()}${i}`,{...o,credentials:"include",headers:f})}function Kn(i,o){return i===401?"Not signed in to the hub portal — sign in, then retry.":i===403?"Not authorized — reactive reload needs host-admin access.":i===404?"Reactive reload needs the hub-proxied URL — open the agent app via your hub origin, not the loopback daemon.":o||`hub request failed (${i})`}async function _i(i){try{const o=await i.json();return o.error_description??o.error??""}catch{return await i.text().catch(()=>"")}}async function Ph(){const i=await zi("/admin/connections");if(!i.ok)throw new Fn(i.status,Kn(i.status,await _i(i)));return(await i.json()).connections??[]}function w0(i,o){const f=m=>o.some(p=>p.source.module==="vault"&&p.source.vault===i&&p.source.event===m&&p.sink.module==="agent"&&p.sink.action===Er),s=f(jr),d=f(Nr);return{create:s,edit:d,active:s&&d}}async function em(i){const o=["create","edit"],f=[];let s=0,d="";for(const m of o){const p=await zi("/admin/connections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(U0(i,m))});if(!p.ok){const E=await _i(p);s===0&&(s=p.status,d=E),f.push(`${m}: ${Kn(p.status,E)}`)}}if(f.length===o.length)throw new Fn(s,Kn(s,d||"provisioning failed"));return{ok:f.length===0,failures:f}}async function tm(i,o,f){const s={};o!==void 0&&(s.token=o),f&&(s.returnTo=f);const d=await zi(`/admin/grants/${encodeURIComponent(i)}/approve`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!d.ok)throw new Fn(d.status,Kn(d.status,await _i(d)));return await d.json()}async function lm(i,o=[]){const f=new Set([sr(i,"create"),sr(i,"edit")]);for(const s of o)s.source.module==="vault"&&s.source.vault===i&&(s.source.event===jr||s.source.event===Nr)&&s.sink.module==="agent"&&s.sink.action===Er&&f.add(s.id);for(const s of f){const d=await zi(`/admin/connections/${encodeURIComponent(s)}`,{method:"DELETE"});if(!d.ok&&d.status!==404)throw new Fn(d.status,Kn(d.status,await _i(d)))}}const Cr=[{value:"",label:"Default (Claude Code's default)"},{value:"opus",label:"Opus — most capable"},{value:"sonnet",label:"Sonnet — balanced"},{value:"haiku",label:"Haiku — fastest"}];function H0(i){var o;return i?((o=Cr.find(f=>f.value===i))==null?void 0:o.label)??i:"Default"}function B0(i,o){const f=new Map;for(const s of i)f.set(s.name,{name:s.name,backend:s.backend,...s.channel?{channel:s.channel}:{},...s.vault?{vault:s.vault}:{},...s.status?{status:s.status}:{},live:!0});for(const s of o){const d=f.get(s.name);d?(d.def=s,d.mode=s.mode,!d.channel&&s.channel&&(d.channel=s.channel),!d.vault&&s.vault&&(d.vault=s.vault)):f.set(s.name,{name:s.name,backend:s.backend,channel:s.channel,vault:s.vault,status:s.status,mode:s.mode,live:!1,def:s})}return[...f.values()].sort((s,d)=>s.name.localeCompare(d.name))}function Dm(i){return i==="programmatic"?"pill backend-programmatic":i==="channel"?"pill backend-channel":"pill"}function Om(i){return i==="enabled"||i==="idle"?"pill status-enabled":i==="working"?"pill status-working":i==="pending"||i.startsWith("queued")?"pill status-queued":i==="error"?"pill status-error":"pill"}function am(){const[i,o]=y.useState({kind:"loading"}),[f,s]=y.useState(null),[d,m]=y.useState(!1),p=y.useCallback(async()=>{o({kind:"loading"});try{const[v,C,j]=await Promise.all([i0(),c0(),zm()]);o({kind:"ok",agents:v.agents,defs:C.defs,vaults:j.vaults})}catch(v){const C=v instanceof et?v.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load agents: ${v.message}`:`Failed to load agents: ${v.message}`;o({kind:"error",message:C})}},[]);y.useEffect(()=>{p()},[p]),y.useEffect(()=>{const v=new URLSearchParams(window.location.search);if(v.get("mcp_connected")!=="1")return;m(!0),v.delete("mcp_connected");const C=v.toString();window.history.replaceState(null,"",window.location.pathname+(C?`?${C}`:"")+window.location.hash)},[]);const E=y.useMemo(()=>i.kind==="ok"?B0(i.agents,i.defs):[],[i]),S=y.useMemo(()=>E.find(v=>v.name===f)??null,[E,f]);return c.jsxs("div",{children:[c.jsx("h1",{children:"Agents"}),c.jsx("p",{className:"lede",children:"Every agent across all backends, in one place — programmatic and channel. Read-only for now; the create flow and def-vault editor land in later phases."}),i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void p(),children:"Retry"})]}):null,d?c.jsxs("div",{className:"success-banner",role:"status",children:["✓ MCP server connected — it'll be available to the agent on its next run."," ",c.jsx("button",{type:"button",className:"link",onClick:()=>m(!1),children:"Dismiss"})]}):null,i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading agents…"}):null,i.kind==="ok"?c.jsxs(c.Fragment,{children:[S?c.jsx(q0,{agent:S,onClose:()=>s(null),onChanged:()=>void p(),onDeleted:()=>{s(null),p()}}):null,c.jsxs("section",{className:"card","aria-label":"Agents",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"All agents"}),c.jsxs("span",{className:"section-head-actions",children:[c.jsxs("span",{className:"count","data-testid":"agents-count",children:[E.length," ",E.length===1?"agent":"agents"]}),c.jsx(fl,{to:"/create",className:"button-link","data-testid":"new-agent-link",children:"New agent"})]})]}),E.length===0?c.jsxs("div",{className:"empty",children:["No agents yet. Define one in a vault as an"," ",c.jsx("code",{children:"#agent/definition"})," note, or spawn one from the create flow (coming in a later phase)."]}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Backend"}),c.jsx("th",{children:"Channel"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Vault"})]})}),c.jsx("tbody",{children:E.map(v=>c.jsxs("tr",{className:`agent-row${f===v.name?" selected":""}`,"data-testid":`agent-row-${v.name}`,onClick:()=>s(f===v.name?null:v.name),children:[c.jsx("td",{className:"cell-name",children:v.name}),c.jsx("td",{children:c.jsx("span",{className:Dm(v.backend),children:v.backend})}),c.jsx("td",{className:v.channel?"":"cell-dim",children:v.channel??"—"}),c.jsx("td",{children:v.status?c.jsx("span",{className:Om(v.status),children:v.status}):v.live?c.jsx("span",{className:"cell-dim",children:"live"}):c.jsx("span",{className:"cell-dim",children:"not running"})}),c.jsx("td",{className:v.vault?"":"cell-dim",children:v.vault??"—"})]},v.name))})]})]}),c.jsx(P0,{}),c.jsx(eb,{vaults:i.vaults,onChanged:()=>void p()})]}):null]})}function q0({agent:i,onClose:o,onChanged:f,onDeleted:s}){const d=i.def,m=d==null?void 0:d.noteId,[p,E]=y.useState("view");return y.useEffect(()=>{E("view")},[i.name]),p==="edit"&&m?c.jsx(k0,{noteId:m,name:i.name,onCancel:()=>E("view"),onSaved:()=>{E("view"),f()}}):c.jsxs("div",{className:"detail","data-testid":"agent-detail",children:[c.jsxs("div",{className:"detail-head",children:[c.jsx("h2",{children:i.name}),c.jsx("span",{className:Dm(i.backend),children:i.backend}),i.status?c.jsx("span",{className:Om(i.status),children:i.status}):null,c.jsx("button",{type:"button",className:"detail-close",onClick:o,children:"Close"})]}),c.jsxs("dl",{className:"detail-grid",children:[c.jsx("dt",{children:"Backend"}),c.jsx("dd",{children:i.backend}),c.jsx("dt",{children:"Mode"}),c.jsx("dd",{"data-testid":"detail-mode",children:i.mode?L0(i.mode):"—"}),c.jsx("dt",{children:"Channel"}),c.jsx("dd",{children:i.channel??"—"}),c.jsx("dt",{children:"Vault"}),c.jsx("dd",{children:i.vault??"—"}),c.jsx("dt",{children:"Running"}),c.jsx("dd",{children:i.live?"yes":"no (defined, not instantiated)"}),d?c.jsxs(c.Fragment,{children:[c.jsx("dt",{children:"Model"}),c.jsx("dd",{"data-testid":"detail-model",children:H0(d.model)}),c.jsx("dt",{children:"Def status"}),c.jsx("dd",{children:d.status})]}):null]}),d?c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"System prompt"}),d.systemPromptPreview?c.jsx("p",{className:"detail-prompt","data-testid":"detail-prompt",children:d.systemPromptPreview}):c.jsx("p",{className:"dim",children:"No system prompt set (Claude Code's default)."}),c.jsx("h3",{children:"Wants"}),d.wants.length>0?c.jsx("div",{className:"tag-list",children:d.wants.map(S=>c.jsx("span",{className:"tag",children:S},S))}):c.jsx("p",{className:"dim",children:"Own-vault only — no extra connections requested."}),d.pending.length>0?c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"Pending approval"}),c.jsx("div",{className:"tag-list",children:d.pending.map(S=>c.jsx("span",{className:"tag",children:S},S))})]}):null,m?c.jsx(Q0,{noteId:m,def:d,onChanged:f}):null]}):c.jsxs("p",{className:"dim","data-testid":"detail-no-def",children:["This agent isn't backed by a vault-native ",c.jsx("code",{children:"#agent/definition"})," note, so there's no system prompt or wants to show."]}),i.backend==="channel"?c.jsx("p",{className:"detail-note","data-testid":"detail-channel-note",children:'Channel backend — the queue depth and the "connect your Claude Code session" affordance arrive in a later phase.'}):null,i.channel?c.jsx(K0,{channel:i.channel}):null,i.channel?c.jsx(I0,{channel:i.channel,backend:i.backend}):null,i.channel?c.jsx(F0,{name:i.name}):null,m?p==="delete"?c.jsx(Y0,{noteId:m,name:i.name,onCancel:()=>E("view"),onDeleted:s}):c.jsxs("div",{className:"detail-actions","data-testid":"detail-actions",children:[c.jsx("button",{type:"button",className:"secondary","data-testid":"edit-agent",onClick:()=>E("edit"),children:"Edit"}),c.jsx("button",{type:"button",className:"button-danger","data-testid":"delete-agent",onClick:()=>E("delete"),children:"Delete"})]}):null]})}function L0(i){return i==="single-threaded"?"Single-threaded":"Multi-threaded"}function k0({noteId:i,name:o,onCancel:f,onSaved:s}){const[d,m]=y.useState({kind:"loading"}),[p,E]=y.useState(""),[S,v]=y.useState("single-threaded"),[C,j]=y.useState(""),[z,k]=y.useState(""),[Y,L]=y.useState(!1),[X,M]=y.useState(null),D=y.useCallback(async()=>{m({kind:"loading"});try{const $=(await r0(i)).def;E($.systemPrompt),v($.mode),j($.model??""),k($.wants.join(", ")),m({kind:"ready",systemPrompt:$.systemPrompt,mode:$.mode,wants:$.wants.join(", ")})}catch(Z){const $=Z instanceof et?Z.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load the def: ${Z.message}`:`Failed to load the def: ${Z.message}`;m({kind:"error",message:$})}},[i]);y.useEffect(()=>{D()},[D]);async function G(Z){if(Z.preventDefault(),!Y){L(!0),M(null);try{await _m(i,{systemPrompt:p,metadata:{mode:S,model:C},wants:z.trim()}),s()}catch($){const ue=$ instanceof et&&$.status===401?"Not signed in to the hub — sign in to the portal, then reload.":$.message;M(ue)}finally{L(!1)}}}return c.jsxs("div",{className:"detail","data-testid":"edit-agent-form",children:[c.jsxs("div",{className:"detail-head",children:[c.jsxs("h2",{children:["Edit ",o]}),c.jsx("button",{type:"button",className:"detail-close",onClick:f,children:"Close"})]}),d.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading def…"}):null,d.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"edit-load-error",children:[d.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void D(),children:"Retry"})]}):null,d.kind==="ready"?c.jsxs("form",{className:"card",onSubmit:G,"aria-label":"Edit agent",children:[X?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"edit-error",children:X}):null,c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Mode"}),c.jsx(Ei,{name:"edit-mode",value:"single-threaded",checked:S==="single-threaded",onChange:()=>v("single-threaded"),label:"Single-threaded",help:"One continuous conversation — remembers everything on this channel.",testid:"edit-mode-single-threaded"}),c.jsx(Ei,{name:"edit-mode",value:"multi-threaded",checked:S==="multi-threaded",onChange:()=>v("multi-threaded"),label:"Multi-threaded",help:"Each run is its own thread — good for scheduled or stateless tasks.",testid:"edit-mode-multi-threaded"})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-model",children:"Model"}),c.jsx("select",{id:"edit-model",value:C,"data-testid":"edit-model",onChange:Z=>j(Z.target.value),children:Cr.map(Z=>c.jsx("option",{value:Z.value,children:Z.label},Z.value))}),c.jsx("p",{className:"field-hint",children:"Which model Parachute runs this agent on (programmatic backend). A channel-backend agent uses whatever model your own session runs."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-prompt",children:"System prompt"}),c.jsx("textarea",{id:"edit-prompt",rows:8,value:p,placeholder:"You are…",onChange:Z=>E(Z.target.value)}),c.jsx("p",{className:"field-hint",children:"The agent's persona + instructions (the note body). Leave blank for Claude Code's default."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"edit-wants",children:"Wants (connections)"}),c.jsx("input",{id:"edit-wants",type:"text",value:z,placeholder:"vault:other, service:github",autoComplete:"off",onChange:Z=>k(Z.target.value)}),c.jsx("p",{className:"field-hint",children:"Comma-separated connection keys the agent requests beyond its own vault. Each needs approval before it's granted."})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:Y,children:Y?"Saving…":"Save changes"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]}):null]})}function Y0({noteId:i,name:o,onCancel:f,onDeleted:s}){const[d,m]=y.useState(""),[p,E]=y.useState(!1),[S,v]=y.useState(null),C=d===o&&!p;async function j(){if(C){E(!0),v(null);try{await o0(i),s()}catch(z){const k=z instanceof et&&z.status===401?"Not signed in to the hub — sign in to the portal, then reload.":z.message;v(k),E(!1)}}}return c.jsxs("div",{className:"confirm-box","data-testid":"delete-confirm",children:[c.jsxs("p",{className:"confirm-prompt",children:["Delete ",c.jsx("strong",{children:o}),"? This removes the definition note and deregisters the agent. Type the agent name to confirm."]}),S?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"delete-error",children:S}):null,c.jsx("div",{className:"field",children:c.jsx("input",{type:"text",value:d,placeholder:o,autoComplete:"off","aria-label":"Type the agent name to confirm deletion","data-testid":"delete-confirm-input",onChange:z=>m(z.target.value)})}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":"delete-confirm-button",disabled:!C,onClick:()=>void j(),children:p?"Deleting…":"Delete agent"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]})}function G0({status:i}){return i==="approved"?c.jsx("span",{className:"pill status-enabled","data-testid":"conn-status-approved",children:"Connected"}):i==="needs_consent"?c.jsx("span",{className:"pill status-error","data-testid":"conn-status-needs_consent",children:"Needs reconnect"}):i==="revoked"?c.jsx("span",{className:"pill status-error","data-testid":"conn-status-revoked",children:"Revoked"}):c.jsx("span",{className:"pill status-pending","data-testid":"conn-status-pending",children:"Pending"})}function X0(i){if(i.connections&&i.connections.length>0)return i.connections.filter(f=>f.kind==="mcp");const o=new Set(i.pending);return i.wants.filter(f=>f.startsWith("mcp:")&&/^mcp:https?:\/\//i.test(f)).map(f=>({key:f,kind:"mcp",target:f.slice(4),status:o.has(f)?"pending":"approved"}))}function Q0({noteId:i,def:o,onChanged:f}){const[s,d]=y.useState(!1),[m,p]=y.useState(null),[E,S]=y.useState(null),[v,C]=y.useState(""),[j,z]=y.useState(null),k=X0(o),Y=O0();async function L(M){p(M),z(null);try{const D=window.location.pathname+window.location.search+window.location.hash,G=await tm(M,void 0,D);if(G.authorizeUrl){window.location.assign(G.authorizeUrl);return}f()}catch(D){z(nm(D))}finally{p(null)}}async function X(M){const D=v.trim();if(D.length!==0){p(M),z(null);try{await tm(M,D),S(null),C(""),f()}catch(G){z(nm(G))}finally{p(null)}}}return c.jsxs("section",{className:"detail-section","aria-label":"Connections","data-testid":"connections-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Connections / MCP servers"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-mcp-toggle",onClick:()=>{d(M=>!M),z(null)},children:s?"Cancel":"Add MCP server"})]}),c.jsx("p",{className:"muted",children:"Remote MCP servers this agent can reach. Add one, then connect it via OAuth (or paste a static bearer). Each connection is operator-approved before it's granted."}),Y?c.jsx("div",{className:"info-banner",role:"status","data-testid":"connections-daemon-direct",children:"Open this surface via your hub to connect MCP servers — the OAuth/approve step needs the hub origin (you're on the loopback daemon)."}):null,s?c.jsx(V0,{noteId:i,def:o,onCancel:()=>d(!1),onAdded:()=>{d(!1),f()}}):null,j?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"connections-row-error",children:j}):null,k.length===0?c.jsx("div",{className:"empty","data-testid":"connections-empty",children:"No MCP servers connected to this agent yet."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"MCP server"}),c.jsx("th",{children:"Status"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:k.map(M=>{const D=M.status!=="approved",G=D&&!!M.grantId&&!Y;return c.jsxs("tr",{"data-testid":`connection-row-${M.target}`,children:[c.jsx("td",{className:"cell-name",children:c.jsx("code",{children:M.target})}),c.jsx("td",{children:c.jsx(G0,{status:M.status})}),c.jsx("td",{children:D?E===M.grantId?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("input",{type:"password",value:v,placeholder:"bearer token",autoComplete:"off",spellCheck:!1,"aria-label":"Static bearer token","data-testid":`paste-token-input-${M.target}`,onChange:Z=>C(Z.target.value)}),c.jsx("button",{type:"button",disabled:m===M.grantId||v.trim().length===0,"data-testid":`paste-token-save-${M.target}`,onClick:()=>void X(M.grantId),children:m===M.grantId?"Saving…":"Save"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>{S(null),C("")},children:"Cancel"})]}):c.jsxs("span",{className:"schedule-row-actions",children:[c.jsx("button",{type:"button",disabled:!G||m===M.grantId,"data-testid":`connect-${M.target}`,title:Y?"Open the agent app via your hub origin to connect.":M.grantId?void 0:"No grant registered yet — reload after the daemon registers it.",onClick:()=>void L(M.grantId),children:m===M.grantId?"Connecting…":M.status==="needs_consent"?"Reconnect":"Connect"}),M.grantId&&!Y?c.jsx("button",{type:"button",className:"cancel-link","data-testid":`paste-token-${M.target}`,onClick:()=>{z(null),C(""),S(M.grantId)},children:"Paste token"}):null]}):c.jsx("span",{className:"cell-dim",children:"—"})})]},M.key)})})]})]})}function nm(i){return i instanceof Fn,i.message}function V0({noteId:i,def:o,onCancel:f,onAdded:s}){const[d,m]=y.useState(""),[p,E]=y.useState("oauth"),[S,v]=y.useState(!1),[C,j]=y.useState(null),z=d.trim(),k=/^https?:\/\/.+/i.test(z)&&Z0(z),Y=o.wants.includes(`mcp:${z}`),L=k&&!Y&&!S;async function X(M){if(M.preventDefault(),!!L){v(!0),j(null);try{const D=[...o.wants,`mcp:${z}`].join(", ");await _m(i,{wants:D}),s()}catch(D){const G=D instanceof et&&D.status===401?"Not signed in to the hub — sign in to the portal, then reload.":D.message;j(G)}finally{v(!1)}}}return c.jsxs("form",{className:"inline-form",onSubmit:X,"aria-label":"Add MCP server","data-testid":"add-mcp-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-mcp-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"mcp-url",children:"MCP server URL"}),c.jsx("input",{id:"mcp-url",type:"text",value:d,placeholder:"https://mcp.example.com/mcp",autoComplete:"off",spellCheck:!1,onChange:M=>m(M.target.value)}),z.length>0&&!k?c.jsx("p",{className:"field-error","data-testid":"mcp-url-invalid",children:"Must be a full http(s) URL."}):null,Y?c.jsx("p",{className:"field-error","data-testid":"mcp-url-duplicate",children:"This MCP server is already connected to the agent."}):null]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Authentication"}),c.jsx("p",{className:"muted",children:"You'll enter credentials in the next step — on the connection's row after it's added."}),c.jsx(Ei,{name:"mcp-auth",value:"oauth",checked:p==="oauth",onChange:()=>E("oauth"),label:"OAuth",help:"Sign in to the MCP server in your browser (recommended). Connect on the row after adding.",testid:"mcp-auth-oauth"}),c.jsx(Ei,{name:"mcp-auth",value:"token",checked:p==="token",onChange:()=>E("token"),label:"Paste a token",help:"For an MCP server with a static bearer token. Paste it on the row's “Paste token” after adding.",testid:"mcp-auth-token"})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!L,"data-testid":"add-mcp-submit",children:S?"Adding…":"Add MCP server"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:f,children:"Cancel"})]})]})}function Z0(i){try{return new URL(i),!0}catch{return!1}}function K0({channel:i}){const[o,f]=y.useState({kind:"loading"}),[s,d]=y.useState(!1),[m,p]=y.useState(null),[E,S]=y.useState(null),[v,C]=y.useState(null),[j,z]=y.useState(null),[k,Y]=y.useState(null),L=y.useCallback(async()=>{f({kind:"loading"});try{const G=(await E0()).jobs.filter(Z=>Z.channel===i);f({kind:"ok",jobs:G})}catch(D){f({kind:"error",message:at(D)})}},[i]);y.useEffect(()=>{L()},[L]);async function X(D){p(D),z(null),Y(null);try{const G=await N0(D);Y(`Ran ${D} (${G.status}).`),await L()}catch(G){z(`Run failed: ${at(G)}`)}finally{p(null)}}async function M(D){C(D),z(null);try{await C0(D),S(null),await L()}catch(G){z(at(G))}finally{C(null)}}return c.jsxs("section",{className:"schedules","aria-label":"Schedules","data-testid":"schedules-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Schedules"}),c.jsx("span",{className:"section-head-actions",children:c.jsx("button",{type:"button",className:"secondary","data-testid":"add-schedule-toggle",onClick:()=>d(D=>!D),children:s?"Cancel":"New schedule"})})]}),c.jsx("p",{className:"muted",children:"Send this agent a message on a cron schedule. The runner writes the message as an inbound note; the agent runs its turn as if you typed it."}),s?c.jsx($0,{channel:i,onCancel:()=>d(!1),onCreated:()=>{d(!1),L()}}):null,k?c.jsx("p",{className:"schedule-status","data-testid":"schedule-row-status",children:k}):null,j?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"schedule-row-error",children:j}):null,o.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading schedules…"}):null,o.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"schedules-error",children:[o.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void L(),children:"Retry"})]}):null,o.kind==="ok"?o.jobs.length===0?c.jsx("div",{className:"empty","data-testid":"schedules-empty",children:"No schedules yet for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Id"}),c.jsx("th",{children:"Cron"}),c.jsx("th",{children:"Next run"}),c.jsx("th",{children:"Last status"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:o.jobs.map(D=>c.jsxs("tr",{"data-testid":`schedule-row-${D.id}`,children:[c.jsxs("td",{className:"cell-name",children:[c.jsx("code",{children:D.id}),D.enabled?null:c.jsx("span",{className:"cell-dim",children:" (disabled)"})]}),c.jsxs("td",{children:[c.jsx("code",{children:D.schedule.cron}),D.schedule.tz?c.jsxs("span",{className:"cell-dim",children:[" ",D.schedule.tz]}):null]}),c.jsx("td",{className:D.nextRunAt?"":"cell-dim",children:um(D.nextRunAt)}),c.jsxs("td",{children:[D.lastStatus?c.jsx("span",{className:D.lastStatus.startsWith("error")?"pill status-error":"pill status-enabled",children:D.lastStatus}):c.jsx("span",{className:"cell-dim",children:"—"}),D.lastRunAt?c.jsxs("span",{className:"cell-dim",children:[" ",um(D.lastRunAt)]}):null]}),c.jsx("td",{children:E===D.id?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`schedule-delete-confirm-${D.id}`,disabled:v===D.id,onClick:()=>void M(D.id),children:v===D.id?"Deleting…":"Confirm delete"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>S(null),children:"Cancel"})]}):c.jsxs("span",{className:"schedule-row-actions",children:[c.jsx("button",{type:"button",className:"secondary","data-testid":`schedule-run-${D.id}`,disabled:m===D.id,onClick:()=>void X(D.id),children:m===D.id?"Running…":"Run now"}),c.jsx("button",{type:"button",className:"button-danger","data-testid":`schedule-delete-${D.id}`,onClick:()=>{z(null),S(D.id)},children:"Delete"})]})})]},D.id))})]}):null]})}const J0=[{label:"daily 8am",cron:"0 8 * * *"},{label:"hourly",cron:"0 * * * *"},{label:"every 15m",cron:"*/15 * * * *"},{label:"weekdays 9am",cron:"0 9 * * 1-5"},{label:"weekly Mon 8am",cron:"0 8 * * 1"}];function $0({channel:i,onCancel:o,onCreated:f}){const[s,d]=y.useState(""),[m,p]=y.useState(""),[E,S]=y.useState(""),[v,C]=y.useState(""),[j,z]=y.useState(!1),[k,Y]=y.useState(null),L=/^[a-zA-Z0-9_-]+$/.test(s),X=L&&m.trim().length>0&&E.trim().length>0&&!j;async function M(D){if(D.preventDefault(),!!X){z(!0),Y(null);try{await j0({id:s,channel:i,message:m.trim(),schedule:{cron:E.trim(),...v.trim()?{tz:v.trim()}:{}},enabled:!0}),f()}catch(G){Y(at(G))}finally{z(!1)}}}return c.jsxs("form",{className:"inline-form",onSubmit:M,"aria-label":"New schedule","data-testid":"schedule-form",children:[k?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"schedule-form-error",children:k}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-id",children:"Job id (slug)"}),c.jsx("input",{id:"schedule-id",type:"text",value:s,placeholder:"morning-standup",autoComplete:"off",onChange:D=>d(D.target.value)}),s.length>0&&!L?c.jsx("p",{className:"field-error","data-testid":"schedule-id-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-message",children:"Message to send"}),c.jsx("textarea",{id:"schedule-message",rows:3,value:m,placeholder:"Run the morning weave…",onChange:D=>p(D.target.value)})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-cron",children:"Cron (min hour dom mon dow)"}),c.jsx("input",{id:"schedule-cron",type:"text",value:E,placeholder:"0 8 * * *",autoComplete:"off",onChange:D=>S(D.target.value)}),c.jsx("div",{className:"schedule-presets",children:J0.map(D=>c.jsx("button",{type:"button",className:"secondary",onClick:()=>S(D.cron),children:D.label},D.cron))})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"schedule-tz",children:"Timezone (IANA, optional)"}),c.jsx("input",{id:"schedule-tz",type:"text",value:v,placeholder:"America/Los_Angeles",autoComplete:"off",onChange:D=>C(D.target.value)}),c.jsx("p",{className:"field-hint",children:"Leave blank to use the daemon's local timezone."})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!X,children:j?"Scheduling…":"Create schedule"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:o,children:"Cancel"})]})]})}function um(i){if(!i)return"—";try{return new Date(i).toLocaleString()}catch{return i}}function Ei(i){return c.jsxs("label",{className:`radio-row${i.checked?" selected":""}`,"data-testid":i.testid,children:[c.jsx("input",{type:"radio",name:i.name,value:i.value,checked:i.checked,onChange:i.onChange}),c.jsxs("span",{className:"radio-body",children:[c.jsx("span",{className:"radio-label",children:i.label}),c.jsx("span",{className:"radio-help",children:i.help})]})]})}function W0(i){return i==="channel"?"pill backend-channel":i.startsWith("grant:")?"pill backend-programmatic":"pill"}function F0({name:i}){const[o,f]=y.useState({kind:"loading"}),s=y.useCallback(async()=>{f({kind:"loading"});try{const d=await x0(i);f({kind:"ok",env:d.env,...d.note?{note:d.note}:{}})}catch(d){f({kind:"error",message:at(d)})}},[i]);return y.useEffect(()=>{s()},[s]),c.jsxs("section",{className:"detail-section","aria-label":"Effective env","data-testid":"effective-env-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Effective env"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"effective-env-refresh",onClick:()=>void s(),children:"Refresh"})]}),c.jsxs("p",{className:"muted",children:["The env-var ",c.jsx("strong",{children:"names"})," this agent's sandboxed turn runs with, resolved across the operator default, this agent's override, and approved-grant service env."," ",c.jsx("strong",{children:"Names only"})," — values are never shown. Precedence: channel > default > grant."]}),o.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading effective env…"}):null,o.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"effective-env-error",children:[o.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void s(),children:"Retry"})]}):null,o.kind==="ok"?c.jsxs(c.Fragment,{children:[o.note?c.jsx("p",{className:"muted","data-testid":"effective-env-note",children:o.note}):null,o.env.length===0?c.jsx("div",{className:"empty","data-testid":"effective-env-empty",children:"No env vars resolved for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Source"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:o.env.map((d,m)=>c.jsxs("tr",{className:d.overridden?"cell-dim":"","data-testid":`effective-env-${d.name}-${d.source}`,children:[c.jsx("td",{className:"cell-name",children:d.name}),c.jsx("td",{children:c.jsx("span",{className:W0(d.source),children:d.source})}),c.jsx("td",{children:d.overridden?c.jsx("span",{className:"cell-dim","data-testid":`effective-env-overridden-${d.name}-${d.source}`,children:"overridden"}):null})]},`${d.name}-${d.source}-${m}`))})]})]}):null]})}function I0({channel:i,backend:o}){const[f,s]=y.useState({kind:"loading"}),[d,m]=y.useState(!1),[p,E]=y.useState(""),[S,v]=y.useState(""),[C,j]=y.useState(!1),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),Z=y.useCallback(async()=>{s({kind:"loading"});try{const le=await v0();s({kind:"ok",names:(le.channels[i]??[]).slice().sort()})}catch(le){s({kind:"error",message:at(le)})}},[i]);y.useEffect(()=>{Z()},[Z]);const $=p.trim(),ue=h0.has($),V=$.length===0||m0.test($),I=$.length>0&&V&&!ue&&S.length>0&&!C;async function P(le){if(le.preventDefault(),!!I){j(!0),k(null);try{await y0({channel:i,name:$,value:S}),E(""),v(""),m(!1),await Z()}catch(xe){k(at(xe))}finally{j(!1)}}}async function Re(le){M(le),G(null);try{await g0({channel:i,name:le}),L(null),await Z()}catch(xe){G(at(xe))}finally{M(null)}}return c.jsxs("section",{className:"detail-section","aria-label":"Secrets","data-testid":"secrets-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h3",{children:"Secrets (env vars)"}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-secret-toggle",onClick:()=>{m(le=>!le),k(null)},children:d?"Cancel":"Add secret"})]}),c.jsxs("p",{className:"muted",children:["Local env vars (e.g. ",c.jsx("code",{children:"GH_TOKEN"}),") injected into this agent's sandboxed turns — stored 0600 on this machine, ",c.jsx("strong",{children:"never in the vault"}),". Values are write-only: they're never shown again.",o==="channel"?c.jsxs(c.Fragment,{children:[" ","This agent uses the ",c.jsx("strong",{children:"channel"})," backend, so it runs in your own Claude Code session with your own environment — these vars apply only to programmatic turns."]}):null]}),d?c.jsxs("form",{className:"inline-form",onSubmit:P,"aria-label":"Add secret","aria-busy":C,"data-testid":"add-secret-form",children:[z?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-secret-error",children:z}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"secret-name",children:"Name"}),c.jsx("input",{id:"secret-name",type:"text",value:p,placeholder:"GH_TOKEN",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:le=>E(le.target.value)}),$.length>0&&!V?c.jsx("p",{className:"field-error","data-testid":"secret-name-invalid",children:"A valid env var name — letters, numbers, underscore; not starting with a digit."}):null,ue?c.jsxs("p",{className:"field-error","data-testid":"secret-name-denylisted",children:["Reserved — ",$," would hijack the agent's managed billing/auth."]}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"secret-value",children:"Value"}),c.jsx("input",{id:"secret-value",type:"password",value:S,placeholder:"ghp_…",autoComplete:"off",spellCheck:!1,onChange:le=>v(le.target.value)}),c.jsx("p",{className:"field-hint",children:"Stored 0600 on disk (access-controlled, not encrypted). Write-only — never re-displayed."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!I,children:C?"Saving…":"Save secret"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"secret-row-error",children:D}):null,f.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading secrets…"}):null,f.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"secrets-load-error",children:[f.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void Z(),children:"Retry"})]}):null,f.kind==="ok"?f.names.length===0?c.jsx("div",{className:"empty","data-testid":"secrets-empty",children:"No secrets set for this agent."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Value"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:f.names.map(le=>c.jsxs("tr",{"data-testid":`secret-${le}`,children:[c.jsx("td",{className:"cell-name",children:le}),c.jsx("td",{className:"cell-dim",children:"••••••••"}),c.jsx("td",{children:Y===le?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`secret-remove-confirm-${le}`,disabled:X===le,onClick:()=>void Re(le),children:X===le?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`secret-remove-${le}`,onClick:()=>{G(null),L(le)},children:"Remove"})})]},le))})]}):null]})}function P0(){const[i,o]=y.useState({kind:"loading"}),[f,s]=y.useState(!1),[d,m]=y.useState(""),[p,E]=y.useState(""),[S,v]=y.useState(!1),[C,j]=y.useState(null),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),Z=y.useCallback(async()=>{o({kind:"loading"});try{const P=await p0();o({kind:"ok",status:P})}catch(P){o({kind:"error",message:at(P)})}},[]);y.useEffect(()=>{Z()},[Z]);const $=p.trim(),ue=d.length>0&&!S;async function V(P){if(P.preventDefault(),!!ue){v(!0),j(null),k(null);try{await b0({token:d,...$.length>0?{channel:$}:{}}),m(""),E(""),s(!1),k($.length>0?`Saved a Claude token override for "${$}".`:"Saved the default Claude token."),await Z()}catch(Re){j(at(Re))}finally{v(!1)}}}async function I(P){M(P),G(null);try{await S0(P),L(null),await Z()}catch(Re){G(at(Re))}finally{M(null)}}return c.jsxs("section",{className:"card","aria-label":"Claude auth","data-testid":"claude-auth-section",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"Claude auth"}),c.jsxs("span",{className:"section-head-actions",children:[i.kind==="ok"?i.status.defaultSet?c.jsx("span",{className:"pill status-enabled","data-testid":"claude-default-configured",children:"configured"}):c.jsx("span",{className:"pill status-error","data-testid":"claude-default-missing",children:"not configured"}):null,c.jsx("button",{type:"button",className:"secondary","data-testid":"set-claude-token-toggle",onClick:()=>{s(P=>(P&&(m(""),E("")),!P)),j(null),k(null)},children:f?"Cancel":"Set token"})]})]}),c.jsxs("p",{className:"muted",children:["The token from ",c.jsx("code",{children:"claude setup-token"})," that the daemon runs each agent turn on. It's stored locally (",c.jsx("code",{children:"credentials.json"}),", 0600) and used to run turns on your ",c.jsx("strong",{children:"Claude subscription"})," — ",c.jsx("strong",{children:"not"})," API billing. It's write-only: the value is never shown again. Set a ",c.jsx("strong",{children:"default"})," token (used by every agent) or a per-",c.jsx("strong",{children:"channel"})," override."]}),z?c.jsx("div",{className:"info-banner",role:"status","data-testid":"claude-saved-notice",children:z}):null,f?c.jsxs("form",{className:"inline-form",onSubmit:V,"aria-label":"Set Claude token","aria-busy":S,"data-testid":"set-claude-token-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"set-claude-token-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"claude-token",children:"Token"}),c.jsx("input",{id:"claude-token",type:"password",value:d,placeholder:"paste the output of `claude setup-token`",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:P=>m(P.target.value)}),c.jsxs("p",{className:"field-hint",children:["Run ",c.jsx("code",{children:"claude setup-token"})," on a machine where you're signed in, then paste it here. Stored 0600 on disk (access-controlled, not encrypted). Write-only — never re-displayed."]})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"claude-channel",children:"Channel (optional)"}),c.jsx("input",{id:"claude-channel",type:"text",value:p,placeholder:"leave blank for the default (operator) token",autoComplete:"off",autoCapitalize:"off",spellCheck:!1,onChange:P=>E(P.target.value)}),c.jsx("p",{className:"field-hint",children:"A channel name to override just that agent; blank sets the default token every agent falls back to."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!ue,children:S?"Saving…":"Save token"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"claude-row-error",children:D}):null,i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading Claude auth…"}):null,i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"claude-auth-load-error",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void Z(),children:"Retry"})]}):null,i.kind==="ok"?c.jsxs(c.Fragment,{children:[i.status.defaultSet?null:c.jsx("div",{className:"empty","data-testid":"claude-default-empty",children:"No default Claude token set — programmatic agents can't run turns until one is set (or a per-channel override covers them)."}),i.status.channels.length>0?c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Channel override"}),c.jsx("th",{children:"Token"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:i.status.channels.map(P=>c.jsxs("tr",{"data-testid":`claude-override-${P}`,children:[c.jsx("td",{className:"cell-name",children:P}),c.jsx("td",{className:"cell-dim",children:"••••••••"}),c.jsx("td",{children:Y===P?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`claude-override-remove-confirm-${P}`,disabled:X===P,onClick:()=>void I(P),children:X===P?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`claude-override-remove-${P}`,onClick:()=>{G(null),L(P)},children:"Remove"})})]},P))})]}):null]}):null]})}function eb({vaults:i,onChanged:o}){const[f,s]=y.useState(!1),[d,m]=y.useState(""),[p,E]=y.useState(""),[S,v]=y.useState(!1),[C,j]=y.useState(null),[z,k]=y.useState(null),[Y,L]=y.useState(null),[X,M]=y.useState(null),[D,G]=y.useState(null),[Z,$]=y.useState([]),[ue,V]=y.useState(!1),[I,P]=y.useState(null),[Re,le]=y.useState(null),xe=y.useCallback(async()=>{try{const Q=await Ph();$(Q),V(!0)}catch{V(!1)}},[]);y.useEffect(()=>{xe()},[xe]);const Ie=/^[a-zA-Z0-9_-]+$/.test(d),Ze=Ie&&!S;async function De(Q){if(Q.preventDefault(),!Ze)return;v(!0),j(null),k(null);const ne=d;try{await f0({vault:ne,...p.trim().length>0?{url:p.trim()}:{}}),m(""),E(""),s(!1),o();try{const{ok:oe}=await em(ne);k(oe?`Added "${ne}" — reactive reload is on (def changes apply instantly).`:`Added "${ne}" — reactive reload partially wired; defs still converge within 60s.`)}catch(oe){k(`Added "${ne}". Reactive reload couldn't be enabled (${at(oe)}) — def changes converge within 60s; you can enable it below.`)}await xe()}catch(oe){j(at(oe))}finally{v(!1)}}async function U(Q){M(Q),G(null);try{await d0(Q);try{const ne=await Ph().catch(()=>Z);await lm(Q,ne)}catch{}L(null),o(),await xe()}catch(ne){G(at(ne))}finally{M(null)}}async function K(Q,ne){P(Q),le(null);try{ne?await lm(Q,Z):await em(Q),await xe()}catch(oe){le(at(oe))}finally{P(null)}}return c.jsxs("section",{className:"card","aria-label":"Def-vaults",children:[c.jsxs("div",{className:"section-head",children:[c.jsx("h2",{children:"Def-vaults"}),c.jsxs("span",{className:"section-head-actions",children:[c.jsx("span",{className:"count",children:i.length}),c.jsx("button",{type:"button",className:"secondary","data-testid":"add-def-vault-toggle",onClick:()=>s(Q=>!Q),children:f?"Cancel":"Add def-vault"})]})]}),c.jsxs("p",{className:"muted",children:["Vaults this module reads ",c.jsx("code",{children:"#agent/definition"})," notes from. Removing one deregisters every agent defined in it. ",c.jsx("strong",{children:"Reactive reload"})," wires a vault trigger so def changes apply instantly instead of waiting up to 60s."]}),z?c.jsx("div",{className:"info-banner",role:"status","data-testid":"add-def-vault-notice",children:z}):null,Re?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"reactive-reload-error",children:Re}):null,f?c.jsxs("form",{className:"inline-form",onSubmit:De,"aria-label":"Add def-vault","data-testid":"add-def-vault-form",children:[C?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"add-def-vault-error",children:C}):null,c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"new-vault-name",children:"Vault name"}),c.jsx("input",{id:"new-vault-name",type:"text",value:d,placeholder:"research",autoComplete:"off",onChange:Q=>m(Q.target.value)}),d.length>0&&!Ie?c.jsx("p",{className:"field-error","data-testid":"new-vault-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"new-vault-url",children:"Vault URL (optional)"}),c.jsx("input",{id:"new-vault-url",type:"text",value:p,placeholder:"http://127.0.0.1:1940",autoComplete:"off",onChange:Q=>E(Q.target.value)}),c.jsx("p",{className:"field-hint",children:"The vault REST origin. Defaults to the loopback vault."})]}),c.jsx("div",{className:"form-actions",children:c.jsx("button",{type:"submit",disabled:!Ze,children:S?"Adding…":"Add def-vault"})})]}):null,D?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"remove-def-vault-error",children:D}):null,i.length===0?c.jsx("div",{className:"empty","data-testid":"def-vaults-empty",children:"No def-vaults configured yet."}):c.jsxs("table",{children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Vault"}),c.jsx("th",{children:"URL"}),c.jsx("th",{children:"Token"}),c.jsx("th",{children:"Reactive reload"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:i.map(Q=>{const ne=w0(Q.vault,Z);return c.jsxs("tr",{"data-testid":`def-vault-${Q.vault}`,children:[c.jsx("td",{className:"cell-name",children:Q.vault}),c.jsx("td",{className:"cell-dim",children:Q.url}),c.jsx("td",{children:Q.tokenPresent?c.jsx("span",{className:"pill status-enabled",children:"present"}):c.jsx("span",{className:"pill status-error",children:"missing"})}),c.jsx("td",{"data-testid":`reactive-reload-cell-${Q.vault}`,children:ue?c.jsxs("span",{className:"confirm-inline",children:[ne.active?c.jsx("span",{className:"pill status-enabled","data-testid":`reactive-on-${Q.vault}`,children:"on"}):c.jsx("span",{className:"pill","data-testid":`reactive-off-${Q.vault}`,children:"off"}),c.jsx("button",{type:"button",className:"cancel-link","data-testid":`reactive-toggle-${Q.vault}`,disabled:I===Q.vault,onClick:()=>void K(Q.vault,ne.active),children:I===Q.vault?"Working…":ne.active?"Disable":"Enable"})]}):c.jsx("span",{className:"cell-dim",title:"Open the agent app via your hub origin to manage reactive reload.",children:"—"})}),c.jsx("td",{children:Y===Q.vault?c.jsxs("span",{className:"confirm-inline",children:[c.jsx("button",{type:"button",className:"button-danger","data-testid":`remove-def-vault-confirm-${Q.vault}`,disabled:X===Q.vault,onClick:()=>void U(Q.vault),children:X===Q.vault?"Removing…":"Confirm remove"}),c.jsx("button",{type:"button",className:"cancel-link",onClick:()=>L(null),children:"Cancel"})]}):c.jsx("button",{type:"button",className:"button-danger","data-testid":`remove-def-vault-${Q.vault}`,onClick:()=>{G(null),L(Q.vault)},children:"Remove"})})]},Q.vault)})})]})]})}function at(i){return i instanceof et&&i.status===401?"Not signed in to the hub — sign in to the portal, then reload.":i.message}function tb(i){return{key:i.id,kind:i.direction==="outbound"?"them":"you",text:i.text}}function im(){const i=Ig(),o=jm(),[f,s]=y.useState(null),[d,m]=y.useState(null),[p,E]=y.useState(i.channel??null),[S,v]=y.useState([]),[C,j]=y.useState(null),[z,k]=y.useState({text:"",kind:""}),[Y,L]=y.useState(""),[X,M]=y.useState(!1),D=y.useRef(new Set),G=y.useRef(null),Z=y.useRef(null),$=y.useRef(!1),ue=y.useRef(0),V=y.useRef(null),I=y.useCallback(g=>{var O;return((O=f==null?void 0:f.find(H=>H.name===g))==null?void 0:O.transport)??""},[f]),P=y.useCallback(g=>I(g)==="vault",[I]),Re=y.useCallback(g=>{v(O=>[...O,{key:crypto.randomUUID(),kind:"sys",text:g}])},[]),le=y.useCallback(g=>{!g.id||D.current.has(g.id)||(D.current.add(g.id),v(O=>[...O,tb(g)]))},[]);y.useEffect(()=>{const g=V.current;g&&(g.scrollTop=g.scrollHeight)},[S,C]);const xe=y.useCallback(async g=>{var re,ge;const O=ue.current;(re=G.current)==null||re.close(),G.current=null,(ge=Z.current)==null||ge.close(),Z.current=null;const H=I(g),J=()=>{$.current=!1,k({text:`live - ${g}`,kind:"live"})},ae=()=>{var Ue,he;if(!$.current){$.current=!0,k({text:"re-authenticating...",kind:""}),(Ue=G.current)==null||Ue.close(),G.current=null,(he=Z.current)==null||he.close(),Z.current=null,Ti(),xe(g);return}k({text:"reconnecting...",kind:""})};if(H==="http-ui"){const Ue=await Zh();if(O!==ue.current)return;const he=new EventSource(z0(g,Ue));he.onopen=J,he.addEventListener("reply",ft=>{try{const tt=JSON.parse(ft.data);tt.id?le({id:tt.id,text:tt.text??"",direction:"outbound",sender:"session",ts:""}):v(Ja=>[...Ja,{key:crypto.randomUUID(),kind:"them",text:tt.text??""}])}catch{}}),he.addEventListener("edit",ft=>{try{const tt=JSON.parse(ft.data);Re(`(edited) ${tt.text??""}`)}catch{}}),he.addEventListener("permission",ft=>{try{const tt=JSON.parse(ft.data);Re(`permission: ${tt.tool_name??""}${tt.description?` - ${tt.description}`:""} (respond in the session terminal)`)}catch{}}),he.onerror=ae,G.current=he}if(P(g)){const Ue=await Zh();if(O!==ue.current)return;const he=new EventSource(_0(g,Ue));he.onopen=J,he.addEventListener("turn",ft=>{try{Ze(JSON.parse(ft.data),g)}catch{}}),he.onerror=ae,Z.current=he}},[le,Re,P,I]),Ie=y.useCallback(async g=>{try{(await Ih(g)).messages.forEach(le)}catch{}},[le]),Ze=y.useCallback((g,O)=>{switch(g.kind){case"init":j({text:"",tools:[]});return;case"text":j(H=>({text:((H==null?void 0:H.text)??"")+g.text,tools:(H==null?void 0:H.tools)??[],...H!=null&&H.error?{error:H.error}:{}}));return;case"tool":j(H=>{const J=(H==null?void 0:H.tools)??[];return{text:(H==null?void 0:H.text)??"",tools:J.includes(g.tool)?J:[...J,g.tool]}});return;case"done":j(null),Ie(O);return;case"error":j(H=>({text:(H==null?void 0:H.text)??"",tools:(H==null?void 0:H.tools)??[],error:g.error}));return}},[Ie]);y.useEffect(()=>{let g=!1;return(async()=>{try{const O=await A0();if(g)return;s(O.channels),E(H=>{var J;return H&&O.channels.some(ae=>ae.name===H)?H:((J=O.channels[0])==null?void 0:J.name)??null})}catch(O){g||m(ar(O))}})(),()=>{g=!0}},[]),y.useEffect(()=>{const g=p;if(!g){k({text:"no channel",kind:""});return}let O=!1;return ue.current+=1,D.current=new Set,$.current=!1,v([]),j(null),k({text:"loading history...",kind:""}),(async()=>{try{const H=await Ih(g);if(O)return;H.messages.forEach(le),k({text:`live - ${g}`,kind:"live"})}catch(H){if(O)return;k({text:`history error: ${ar(H)}`,kind:"err"})}O||await xe(g)})(),()=>{var H,J;O=!0,ue.current+=1,(H=G.current)==null||H.close(),G.current=null,(J=Z.current)==null||J.close(),Z.current=null}},[p]);function De(g){E(g),o(`/chat/${encodeURIComponent(g)}`,{replace:!0})}async function U(){const g=Y.trim(),O=p;if(!(!g||!O||X)){M(!0),v(H=>[...H,{key:crypto.randomUUID(),kind:"you",text:g}]),L("");try{const H=await R0(O,g);H.id&&D.current.add(H.id)}catch(H){Re(`send failed: ${ar(H)}`)}finally{M(!1)}}}function K(g){g.key==="Enter"&&!g.shiftKey&&(g.preventDefault(),U())}const Q=((f==null?void 0:f.length)??0)>0,ne=!!p&&Y.trim().length>0&&!X,oe=y.useMemo(()=>z.kind==="live"?"chat-status status-live":z.kind==="err"?"chat-status status-err":"chat-status",[z.kind]);return c.jsxs("div",{className:"chat","data-testid":"chat-view",children:[c.jsxs("div",{className:"chat-head",children:[c.jsx("h1",{children:"Chat"}),f&&Q?c.jsxs("label",{className:"chat-picker",children:[c.jsx("span",{className:"chat-picker-label",children:"Channel"}),c.jsx("select",{"data-testid":"chat-channel-select",value:p??"",onChange:g=>De(g.target.value),children:f.map(g=>c.jsxs("option",{value:g.name,children:[g.name," (",g.transport,")"]},g.name))})]}):null,z.text?c.jsx("span",{className:oe,"data-testid":"chat-status",children:z.text}):null]}),d?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"chat-channels-error",children:d}):null,f&&!Q&&!d?c.jsx("div",{className:"empty","data-testid":"chat-no-channels",children:"No channels yet. Create a channel-backed agent in the create flow, then chat with it here."}):null,Q?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"transcript",ref:V,"data-testid":"chat-transcript",children:[S.map(g=>c.jsx("div",{className:`msg ${g.kind}`,"data-testid":`chat-msg-${g.kind}`,children:g.text},g.key)),C?c.jsx(lb,{turn:C}):null]}),c.jsxs("form",{className:"composer","data-testid":"chat-composer",onSubmit:g=>{g.preventDefault(),U()},children:[c.jsx("textarea",{className:"chat-input","data-testid":"chat-input",rows:1,value:Y,placeholder:"Type a message... (Enter to send, Shift+Enter for newline)",autoComplete:"off",onChange:g=>L(g.target.value),onKeyDown:K}),c.jsx("button",{type:"submit","data-testid":"chat-send",disabled:!ne,children:X?"Sending...":"Send"})]})]}):null]})}function lb({turn:i}){return c.jsxs("div",{className:`msg them live${i.error?" errored":""}`,"data-testid":"chat-live-turn",children:[i.text?c.jsx("div",{className:"live-text",children:i.text}):null,i.tools.length>0?c.jsx("div",{className:"live-tools",children:i.tools.map(o=>c.jsx("span",{className:"tool-chip","data-testid":`chat-tool-${o}`,children:o},o))}):null,c.jsx("div",{className:"live-working","data-testid":"chat-live-status",children:i.error?`turn failed: ${i.error}`:"working..."})]})}function ar(i){return i instanceof et&&i.status===401?"Not signed in to the hub - sign in to the portal, then reload.":i.message}const ab=/^[a-zA-Z0-9_-]+$/;function nb({vaults:i}){var V;const o=i.length>0,[f,s]=y.useState(""),[d,m]=y.useState(((V=i[0])==null?void 0:V.vault)??""),[p,E]=y.useState("single-threaded"),[S,v]=y.useState("programmatic"),[C,j]=y.useState(""),[z,k]=y.useState(""),[Y,L]=y.useState(""),[X,M]=y.useState(!1),[D,G]=y.useState({kind:"idle"}),Z=ab.test(f),$=o&&Z&&d.length>0&&D.kind!=="submitting";async function ue(I){if(I.preventDefault(),!!$){G({kind:"submitting"});try{const P=await s0({vault:d,name:f,backend:S,systemPrompt:z,metadata:{mode:p,...C?{model:C}:{}},...Y.trim().length>0?{wants:Y.trim()}:{}});G({kind:"ok",result:P,mode:p})}catch(P){const Re=P instanceof et&&P.status===401?"Not signed in to the hub — sign in to the portal, then reload.":P.message;G({kind:"error",message:Re})}}}return D.kind==="ok"?c.jsx(ib,{result:D.result,mode:D.mode}):c.jsxs("div",{children:[c.jsx("h1",{children:"New agent"}),c.jsxs("p",{className:"lede",children:["A ",c.jsx("code",{children:"#agent/definition"})," note IS the agent — writing it instantiates the agent and its channel routing in one step. The def-vault editor, schedules, and editing land in a later phase."]}),o?null:c.jsxs("div",{className:"error-banner",role:"alert","data-testid":"no-def-vaults",children:["No def-vault is configured, and one is required to create an agent. The add / remove def-vault editor arrives in a later phase; until then, configure a def-vault out-of-band. Submitting anyway will return the daemon's"," ",c.jsx("code",{children:"no def-vaults configured"})," error."]}),D.kind==="error"?c.jsx("div",{className:"error-banner",role:"alert","data-testid":"create-error",children:D.message}):null,c.jsxs("form",{className:"card",onSubmit:ue,"aria-label":"Create agent",children:[c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-name",children:"Name"}),c.jsx("input",{id:"agent-name",type:"text",value:f,placeholder:"my-agent",autoComplete:"off",onChange:I=>s(I.target.value)}),c.jsx("p",{className:"field-hint",children:"A slug (letters, numbers, dash, underscore). This is also the agent's channel."}),f.length>0&&!Z?c.jsx("p",{className:"field-error","data-testid":"name-invalid",children:"Must be a slug — letters, numbers, dash, underscore only."}):null]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-vault",children:"Def-vault"}),c.jsx("select",{id:"agent-vault",value:d,disabled:!o,onChange:I=>m(I.target.value),children:o?i.map(I=>c.jsxs("option",{value:I.vault,children:[I.vault,I.tokenPresent?"":" (token missing)"]},I.vault)):c.jsx("option",{value:"",children:"No def-vaults configured"})}),c.jsx("p",{className:"field-hint",children:"The vault this agent's definition note is written to."})]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Mode"}),c.jsx(yi,{name:"mode",value:"single-threaded",checked:p==="single-threaded",onChange:()=>E("single-threaded"),label:"Single-threaded",help:"One continuous conversation — remembers everything on this channel.",testid:"mode-single-threaded"}),c.jsx(yi,{name:"mode",value:"multi-threaded",checked:p==="multi-threaded",onChange:()=>E("multi-threaded"),label:"Multi-threaded",help:"Each run is its own thread; today every run starts fresh (per-conversation continuation is coming) — good for scheduled or stateless tasks.",testid:"mode-multi-threaded"})]}),c.jsxs("fieldset",{className:"field",children:[c.jsx("legend",{children:"Backend"}),c.jsx(yi,{name:"backend",value:"programmatic",checked:S==="programmatic",onChange:()=>v("programmatic"),label:"Programmatic",help:"Parachute runs it headless — always on, sandboxed.",testid:"backend-programmatic"}),c.jsx(yi,{name:"backend",value:"channel",checked:S==="channel",onChange:()=>v("channel"),label:"Channel",help:"You run a Claude Code session on your own machine and connect it — your env, unsandboxed.",testid:"backend-channel"})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-model",children:"Model"}),c.jsx("select",{id:"agent-model",value:C,"data-testid":"agent-model",onChange:I=>j(I.target.value),children:Cr.map(I=>c.jsx("option",{value:I.value,children:I.label},I.value))}),c.jsx("p",{className:"field-hint",children:"Which model Parachute runs this agent on (programmatic backend). A channel-backend agent uses whatever model your own session runs."})]}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-prompt",children:"System prompt"}),c.jsx("textarea",{id:"agent-prompt",rows:6,value:z,placeholder:"You are…",onChange:I=>k(I.target.value)}),c.jsx("p",{className:"field-hint",children:"The agent's persona + instructions (the note body). Leave blank for Claude Code's default."})]}),c.jsxs("details",{className:"advanced",open:X,onToggle:I=>M(I.target.open),children:[c.jsx("summary",{children:"Advanced"}),c.jsxs("div",{className:"field",children:[c.jsx("label",{htmlFor:"agent-wants",children:"Wants (connections)"}),c.jsx("input",{id:"agent-wants",type:"text",value:Y,placeholder:"vault:other, service:github",autoComplete:"off",onChange:I=>L(I.target.value)}),c.jsx("p",{className:"field-hint",children:"Comma-separated connection keys the agent requests beyond its own vault. Each needs approval before it's granted."})]})]}),c.jsxs("div",{className:"form-actions",children:[c.jsx("button",{type:"submit",disabled:!$,children:D.kind==="submitting"?"Creating…":"Create agent"}),c.jsx(fl,{to:"/",className:"cancel-link",children:"Cancel"})]})]})]})}function ub(){const[i,o]=y.useState({kind:"loading"}),f=y.useCallback(async()=>{o({kind:"loading"});try{const s=await zm();o({kind:"ok",vaults:s.vaults})}catch(s){const d=s instanceof et?s.status===401?"Not signed in to the hub — sign in to the portal, then reload.":`Failed to load def-vaults: ${s.message}`:`Failed to load def-vaults: ${s.message}`;o({kind:"error",message:d})}},[]);return y.useEffect(()=>{f()},[f]),i.kind==="loading"?c.jsx("div",{className:"loading",children:"Loading…"}):i.kind==="error"?c.jsxs("div",{className:"error-banner",role:"alert",children:[i.message," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>void f(),children:"Retry"})]}):c.jsx(nb,{vaults:i.vaults},i.vaults.map(s=>s.vault).join(","))}function yi(i){return c.jsxs("label",{className:`radio-row${i.checked?" selected":""}`,"data-testid":i.testid,children:[c.jsx("input",{type:"radio",name:i.name,value:i.value,checked:i.checked,onChange:i.onChange}),c.jsxs("span",{className:"radio-body",children:[c.jsx("span",{className:"radio-label",children:i.label}),c.jsx("span",{className:"radio-help",children:i.help})]})]})}function ib({result:i,mode:o}){const f=i.def;return c.jsxs("div",{"data-testid":"create-success",children:[c.jsx("h1",{children:"Agent created"}),c.jsxs("div",{className:"success-banner",role:"status",children:[c.jsx("strong",{children:f.name})," · ",o," · ",f.backend]}),c.jsxs("p",{className:"lede",children:["The definition was written to ",c.jsx("code",{children:f.vault})," and instantiated. It now appears in the ",c.jsx(fl,{to:"/",children:"Agents list"}),"."]}),f.backend==="channel"?c.jsx(cb,{name:f.name}):null,c.jsx("p",{children:c.jsx(fl,{to:"/",children:"← Back to agents"})})]})}function cb({name:i}){const[o,f]=y.useState(!1),s=typeof window<"u"?window.location.origin:"",d=y.useMemo(()=>T0(i,s),[i,s]);async function m(){var p;try{await((p=navigator.clipboard)==null?void 0:p.writeText(d)),f(!0),setTimeout(()=>f(!1),2e3)}catch{}}return c.jsxs("section",{className:"card","aria-label":"Connect a session","data-testid":"connect-session",children:[c.jsx("h2",{children:"Connect your Claude Code session"}),c.jsx("p",{className:"muted",children:"Run this on your own machine to make a Claude Code session a responder for this channel:"}),c.jsxs("div",{className:"snippet-row",children:[c.jsx("code",{className:"snippet","data-testid":"connect-command",children:d}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>void m(),children:o?"Copied":"Copy"})]}),c.jsx("p",{className:"field-hint",children:"Your Claude Code session pulls messages from this channel; until you connect, inbound messages queue durably in the vault."})]})}const Mm=y.createContext(null);function sb(){const i=y.useContext(Mm);if(!i)throw new Error("useStepUp must be used inside <StepUpProvider>");return i}function rb({children:i}){const[o,f]=y.useState(null);y.useEffect(()=>(Fh(m=>new Promise(p=>{f({mode:m==="setup"?"setup":"prompt",resolve:p})})),()=>Fh(null)),[]);const s=y.useCallback(m=>{f(p=>{var E;return(E=p==null?void 0:p.resolve)==null||E.call(p,m),null})},[]),d=y.useMemo(()=>({openSettings:()=>f({mode:"settings",resolve:null})}),[]);return c.jsxs(Mm.Provider,{value:d,children:[i,o&&c.jsx(ob,{state:o,onClose:s})]})}function ob({state:i,onClose:o}){const{mode:f}=i,[s,d]=y.useState(""),[m,p]=y.useState(""),[E,S]=y.useState(""),[v,C]=y.useState(""),[j,z]=y.useState(!1),[k,Y]=y.useState(null),[L,X]=y.useState(f==="settings"?null:f!=="setup"),M=y.useRef(null);y.useEffect(()=>{var V;(V=M.current)==null||V.focus()},[]),y.useEffect(()=>{if(f!=="settings")return;let V=!0;return e0().then(I=>V&&X(I.configured)).catch(()=>V&&X(!1)),()=>{V=!1}},[f]);const D=y.useCallback(V=>{V.key==="Escape"&&o(null)},[o]);function G(V){return V instanceof et?V.status===401?"Incorrect PIN.":V.status===429?"Too many attempts — wait a minute and try again.":V.status===400?"PIN must be 4–12 digits.":V.message||"Something went wrong.":V instanceof Error?V.message:"Something went wrong."}async function Z(){Y(null),z(!0);try{const V=await $h(s);o(V)}catch(V){Y(G(V)),z(!1)}}async function $(){if(Y(null),m!==E){Y("The PINs don't match.");return}z(!0);try{await Wh(m);const V=await $h(m);o(V)}catch(V){Y(G(V)),z(!1)}}async function ue(){if(Y(null),m!==E){Y("The PINs don't match.");return}z(!0);try{await Wh(m,L?v:void 0),o(null)}catch(V){Y(G(V)),z(!1)}}return c.jsx("div",{className:"step-up-backdrop",role:"presentation",onClick:()=>o(null),children:c.jsxs("div",{className:"step-up-modal",role:"dialog","aria-modal":"true","aria-label":"Step-up PIN",onClick:V=>V.stopPropagation(),onKeyDown:D,children:[f==="prompt"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:"Confirm with your PIN"}),c.jsx("p",{className:"step-up-sub",children:"This is a high-privilege action. Enter your step-up PIN to continue."}),c.jsx(Xa,{label:"PIN",value:s,onChange:d,inputRef:M})]}),f==="setup"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:"Set a step-up PIN"}),c.jsx("p",{className:"step-up-sub",children:"High-privilege actions (set credentials, open a terminal, spawn a full-filesystem agent) require a PIN. Set one now to continue."}),c.jsx(Xa,{label:"New PIN (4–12 digits)",value:m,onChange:p,inputRef:M}),c.jsx(Xa,{label:"Confirm PIN",value:E,onChange:S})]}),f==="settings"&&c.jsxs(c.Fragment,{children:[c.jsx("h2",{children:L?"Change step-up PIN":"Set step-up PIN"}),c.jsx("p",{className:"step-up-sub",children:"The step-up PIN gates high-privilege admin actions (set credentials, open a terminal, spawn a full-filesystem agent) as a second factor on top of your login."}),L&&c.jsx(Xa,{label:"Current PIN",value:v,onChange:C,inputRef:M}),c.jsx(Xa,{label:"New PIN (4–12 digits)",value:m,onChange:p,inputRef:L?void 0:M}),c.jsx(Xa,{label:"Confirm PIN",value:E,onChange:S})]}),k&&c.jsx("p",{className:"step-up-error",role:"alert",children:k}),c.jsxs("div",{className:"step-up-actions",children:[c.jsx("button",{type:"button",className:"secondary",onClick:()=>o(null),disabled:j,children:"Cancel"}),c.jsx("button",{type:"button",onClick:f==="prompt"?Z:f==="setup"?$:ue,disabled:j||f==="settings"&&L===null,children:j?"Working…":f==="prompt"?"Confirm":"Save PIN"})]})]})})}function Xa({label:i,value:o,onChange:f,inputRef:s}){return c.jsxs("label",{className:"step-up-field",children:[c.jsx("span",{children:i}),c.jsx("input",{ref:s,type:"password",inputMode:"numeric",autoComplete:"off",value:o,onChange:d=>f(d.target.value.replace(/[^0-9]/g,"").slice(0,12))})]})}const fb="Parachute Agent";function db(i){return i==="/create"||i.startsWith("/create/")?"new agent":i==="/chat"||i.startsWith("/chat/")?"chat":"agents"}function hb(){return c.jsx(rb,{children:c.jsx(mb,{})})}function mb(){const{pathname:i}=Bt(),o=db(i),{openSettings:f}=sb();return c.jsxs("div",{className:"page",children:[c.jsxs("nav",{className:"nav",children:[c.jsxs(fl,{to:"/",className:"brand",children:[c.jsx("span",{className:"brand-wordmark",children:fb}),c.jsx("span",{className:"sub",children:o})]}),c.jsx(nr,{to:"/",label:"Agents",exact:!0}),c.jsx(nr,{to:"/create",label:"New agent"}),c.jsx(nr,{to:"/chat",label:"Chat"}),c.jsx("button",{type:"button",className:"nav-link nav-link-button",onClick:f,title:"Set or change the step-up PIN that gates high-privilege actions",children:"Step-up PIN"})]}),c.jsxs(hp,{children:[c.jsx(na,{path:"/",element:c.jsx(am,{})}),c.jsx(na,{path:"/agents",element:c.jsx(am,{})}),c.jsx(na,{path:"/create",element:c.jsx(ub,{})}),c.jsx(na,{path:"/chat",element:c.jsx(im,{})}),c.jsx(na,{path:"/chat/:channel",element:c.jsx(im,{})}),c.jsx(na,{path:"*",element:c.jsxs("div",{className:"empty",children:["404 — back to ",c.jsx(fl,{to:"/",children:"Agents"}),"."]})})]})]})}function nr({to:i,label:o,exact:f}){const{pathname:s}=Bt(),d=f?s===i||s==="/agents":s===i||s.startsWith(`${i}/`);return c.jsx(fl,{to:i,className:d?"nav-link nav-link-active":"nav-link","aria-current":d?"page":void 0,children:o})}function vb(i){return i==="/agent/app"||i.startsWith("/agent/app/")?"/agent/app":i==="/app"||i.startsWith("/app/")?"/app":""}const Um=document.getElementById("root");if(!Um)throw new Error("#root not found");hg.createRoot(Um).render(c.jsx(y.StrictMode,{children:c.jsx(Bp,{basename:vb(window.location.pathname),children:c.jsx(hb,{})})}));
@@ -6,7 +6,7 @@
6
6
  <meta name="referrer" content="no-referrer" />
7
7
  <title>Parachute Agent</title>
8
8
  <meta name="description" content="Manage Parachute agents — every backend, one surface." />
9
- <script type="module" crossorigin src="/agent/app/assets/index-CAQMmePW.js"></script>
9
+ <script type="module" crossorigin src="/agent/app/assets/index-Di5MmFZR.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/agent/app/assets/index-Dhr5Kl_d.css">
11
11
  </head>
12
12
  <body>