@agimon-ai/po-team 0.0.0-alpha.0 → 0.0.0-alpha.2

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.
@@ -0,0 +1,752 @@
1
+ import { createApiClient, defineActivityGroup, defineApiRoutes, defineChannelFile, defineFill, defineNativePlugin, defineSessionStore, nativeApiTransport, sessionApiAddress } from "@agimon-ai/po-core/native";
2
+ import { Badge, Button, Dot, Input, ListRow, SegmentedControl, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, Textarea, UsersIcon } from "@agimon-ai/po-native-components";
3
+ import { createElement, useCallback, useEffect, useState, useSyncExternalStore } from "react";
4
+ import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
5
+ import { View } from "react-native-unistyles/components/native/View";
6
+ import { Text } from "react-native-unistyles/components/native/Text";
7
+ import { StyleSheet } from "react-native-unistyles";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/catalog.native.ts
10
+ const routes = defineApiRoutes({
11
+ catalog: {
12
+ method: "GET",
13
+ path: "/catalog"
14
+ },
15
+ run: {
16
+ method: "POST",
17
+ path: "/run"
18
+ },
19
+ steer: {
20
+ method: "POST",
21
+ path: "/steer"
22
+ },
23
+ stop: {
24
+ method: "POST",
25
+ path: "/stop"
26
+ }
27
+ });
28
+ const api = createApiClient(routes, {
29
+ scopes: ["session"],
30
+ basePath: "team",
31
+ transport: nativeApiTransport,
32
+ sessionAddress: sessionApiAddress
33
+ });
34
+ function isRecord$2(value) {
35
+ return typeof value === "object" && value !== null && !Array.isArray(value);
36
+ }
37
+ function isStrings(value) {
38
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
39
+ }
40
+ function isAgent(value) {
41
+ return isRecord$2(value) && typeof value.name === "string" && typeof value.description === "string" && typeof value.filePath === "string" && [
42
+ "project",
43
+ "user",
44
+ "plugin"
45
+ ].includes(String(value.source)) && (value.packageName === void 0 || typeof value.packageName === "string") && (value.model === void 0 || typeof value.model === "string") && isStrings(value.fallbackModels) && isStrings(value.tools) && isStrings(value.skills) && isStrings(value.extensions) && (value.defaultContext === "fresh" || value.defaultContext === "fork");
46
+ }
47
+ /** Read the selected session's catalog through the host's authenticated HTTP transport. */
48
+ async function fetchCatalog(sessionId, signal) {
49
+ const response = await api.session(sessionId).catalog({ signal });
50
+ const body = response.data;
51
+ if (!response.ok) throw new Error(isRecord$2(body) && typeof body.error === "string" ? body.error : `Agent catalog could not be loaded (${response.status}).`);
52
+ if (!isRecord$2(body) || typeof body.cwd !== "string" || !Array.isArray(body.agents) || !body.agents.every(isAgent) || !isStrings(body.models)) throw new Error("The server returned an invalid agent catalog.");
53
+ return {
54
+ cwd: body.cwd,
55
+ agents: body.agents,
56
+ models: body.models
57
+ };
58
+ }
59
+ /** Launch on the selected session host, without submitting a parent prompt. */
60
+ async function launchAgent(sessionId, request) {
61
+ const response = await api.session(sessionId).run({ body: request });
62
+ const body = response.data;
63
+ if (!response.ok) throw new Error(isRecord$2(body) && typeof body.error === "string" ? body.error : `Agent could not be launched (${response.status}).`);
64
+ if (!isRecord$2(body) || typeof body.runId !== "string" || body.runId === "") throw new Error("The server returned an invalid run ID.");
65
+ return body.runId;
66
+ }
67
+ function isSteerState(value) {
68
+ return value === "delivered" || value === "failed" || value === "pending";
69
+ }
70
+ /** Steer one run on the selected session host, without submitting a parent prompt. */
71
+ async function steerRun(sessionId, runId, message) {
72
+ const response = await api.session(sessionId).steer({ body: {
73
+ runId,
74
+ message
75
+ } });
76
+ const body = response.data;
77
+ if (!response.ok) throw new Error(isRecord$2(body) && typeof body.error === "string" ? body.error : `Guidance could not be sent (${response.status}).`);
78
+ if (!isRecord$2(body) || !isSteerState(body.state) || typeof body.message !== "string") throw new Error("The server returned an invalid steer result.");
79
+ return {
80
+ state: body.state,
81
+ message: body.message
82
+ };
83
+ }
84
+ /** Ask the selected session host to stop one run; the run's own status reports when it has. */
85
+ async function stopRun(sessionId, runId) {
86
+ const response = await api.session(sessionId).stop({ body: { runId } });
87
+ const body = response.data;
88
+ if (!response.ok) throw new Error(isRecord$2(body) && typeof body.error === "string" ? body.error : `The run could not be stopped (${response.status}).`);
89
+ }
90
+ //#endregion
91
+ //#region src/types/webSubagents.ts
92
+ const SUBAGENT_RUNS_TYPE = "subagent_runs";
93
+ const SUBAGENT_CATALOG_TYPE = "subagent_catalog";
94
+ //#endregion
95
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/catalogStore.native.ts
96
+ const catalog = defineSessionStore({
97
+ cwd: "",
98
+ agents: [],
99
+ models: [],
100
+ warning: void 0,
101
+ loading: false,
102
+ requestId: 0,
103
+ open: false,
104
+ filter: "",
105
+ task: "",
106
+ selected: void 0,
107
+ inspected: void 0,
108
+ launch: void 0
109
+ });
110
+ function isRecord$1(value) {
111
+ return typeof value === "object" && value !== null && !Array.isArray(value);
112
+ }
113
+ function openCatalog(sessionId, task = "") {
114
+ catalog.update(sessionId, (current) => ({
115
+ ...current,
116
+ open: true,
117
+ task
118
+ }));
119
+ }
120
+ function closeCatalog(sessionId) {
121
+ catalog.update(sessionId, (current) => ({
122
+ ...current,
123
+ open: false,
124
+ task: "",
125
+ launch: void 0
126
+ }));
127
+ }
128
+ /** Each drawer opening owns one request and cancels it when the drawer leaves. */
129
+ function loadCatalog(sessionId) {
130
+ const controller = new AbortController();
131
+ const requestId = catalog.select(catalog.store.state, sessionId).requestId + 1;
132
+ catalog.update(sessionId, (current) => ({
133
+ ...current,
134
+ requestId,
135
+ loading: true,
136
+ warning: void 0
137
+ }));
138
+ const isCurrent = () => !controller.signal.aborted && catalog.store.state[sessionId]?.requestId === requestId;
139
+ fetchCatalog(sessionId, controller.signal).then((payload) => {
140
+ if (isCurrent()) subagentCatalogChannel.apply(sessionId, payload);
141
+ }).catch((error) => {
142
+ if (isCurrent()) catalog.update(sessionId, (current) => ({
143
+ ...current,
144
+ warning: error instanceof Error ? error.message : String(error)
145
+ }));
146
+ }).finally(() => {
147
+ if (isCurrent()) catalog.update(sessionId, (current) => ({
148
+ ...current,
149
+ loading: false
150
+ }));
151
+ });
152
+ return () => {
153
+ if (isCurrent()) catalog.update(sessionId, (current) => ({
154
+ ...current,
155
+ loading: false
156
+ }));
157
+ controller.abort();
158
+ };
159
+ }
160
+ /** The plugin's catalog channel: 'subagent_catalog' payloads into the store; the drawer's own state survives a refresh. */
161
+ const subagentCatalogChannel = catalog.channel({
162
+ channel: SUBAGENT_CATALOG_TYPE,
163
+ parse(input) {
164
+ if (!isRecord$1(input) || typeof input.cwd !== "string" || !Array.isArray(input.agents)) return null;
165
+ const models = Array.isArray(input.models) ? input.models.filter((m) => typeof m === "string") : [];
166
+ return {
167
+ cwd: input.cwd,
168
+ agents: input.agents.filter(isRecord$1),
169
+ models,
170
+ ...typeof input.warning === "string" ? { warning: input.warning } : {}
171
+ };
172
+ },
173
+ reduce(current, payload) {
174
+ const names = new Set(payload.agents.map((agent) => agent.name));
175
+ return {
176
+ ...current,
177
+ cwd: payload.cwd,
178
+ agents: payload.agents,
179
+ models: payload.models,
180
+ warning: payload.warning,
181
+ selected: current.selected !== void 0 && names.has(current.selected) ? current.selected : void 0,
182
+ inspected: current.inspected !== void 0 && names.has(current.inspected) ? current.inspected : void 0,
183
+ launch: current.launch !== void 0 && names.has(current.launch.agent) ? current.launch : void 0
184
+ };
185
+ }
186
+ });
187
+ //#endregion
188
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/format.ts
189
+ /**
190
+ * Local copies of the host cockpit's pure formatters
191
+ * (po-web src/web/lib/sessionSummary.ts): this plugin ships zero host
192
+ * imports so the cockpit's bundler can compile it from any install.
193
+ */
194
+ const MINUTE_MS = 6e4;
195
+ const HOUR_MS = 60 * MINUTE_MS;
196
+ function formatRunDuration(elapsedMs) {
197
+ if (elapsedMs < MINUTE_MS) return "<1m";
198
+ if (elapsedMs < HOUR_MS) return `${Math.floor(elapsedMs / MINUTE_MS)}m`;
199
+ const hours = Math.floor(elapsedMs / HOUR_MS);
200
+ const minutes = Math.floor(elapsedMs % HOUR_MS / MINUTE_MS);
201
+ return `${hours}h ${String(minutes).padStart(2, "0")}m`;
202
+ }
203
+ /**
204
+ * What to call a run on screen.
205
+ *
206
+ * Prefers the generated identity, because a fan-out of one agent otherwise
207
+ * renders as N rows reading the same word. Falls back to the agent name for a
208
+ * run projected before identities existed. Duplicated rather than imported
209
+ * from `services/agentIdentity`, which reaches for `node:fs` and so cannot be
210
+ * pulled into the browser bundle; that module owns minting, this owns display.
211
+ */
212
+ function agentRunLabel(run) {
213
+ const base = run.identity ?? run.agent;
214
+ return run.inline ? `${base} (inline)` : base;
215
+ }
216
+ //#endregion
217
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/launchCommand.ts
218
+ const META_TOOL_LIMIT = 3;
219
+ /** Rows whose name, source, package, description or a tool contains the filter, case-insensitively. */
220
+ function filterCatalog(agents, filter) {
221
+ const needle = filter.trim().toLowerCase();
222
+ if (needle === "") return [...agents];
223
+ return agents.filter((agent) => [
224
+ agent.name,
225
+ agent.source,
226
+ agent.packageName ?? "",
227
+ agent.description,
228
+ ...agent.tools
229
+ ].some((field) => field.toLowerCase().includes(needle)));
230
+ }
231
+ /** What the picker offers: the agent's own model and fallbacks first, then the team's, without repeats. */
232
+ function modelChoices(agent, models) {
233
+ const seen = /* @__PURE__ */ new Set();
234
+ const choices = [];
235
+ for (const model of [
236
+ agent.model ?? "",
237
+ ...agent.fallbackModels,
238
+ ...models
239
+ ]) {
240
+ if (model === "" || seen.has(model)) continue;
241
+ seen.add(model);
242
+ choices.push(model);
243
+ }
244
+ return choices;
245
+ }
246
+ /** The row's one-liner: what tools it may use, how many skills, and how it starts. */
247
+ function agentMeta(agent) {
248
+ return `${agent.tools.length === 0 ? "all tools" : `tools ${agent.tools.slice(0, META_TOOL_LIMIT).join(", ")}${agent.tools.length > META_TOOL_LIMIT ? ` +${agent.tools.length - META_TOOL_LIMIT}` : ""}`}${agent.skills.length > 0 ? ` · ${agent.skills.length} skill${agent.skills.length === 1 ? "" : "s"}` : ""} · ${agent.defaultContext === "fork" ? "forks the session" : "fresh context"}`;
249
+ }
250
+ //#endregion
251
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/subagentsStore.native.ts
252
+ const TERMINAL_STATES = /* @__PURE__ */ new Set([
253
+ "done",
254
+ "failed",
255
+ "stopped"
256
+ ]);
257
+ function isTerminalRun(run) {
258
+ return TERMINAL_STATES.has(run.state);
259
+ }
260
+ const subagents = defineSessionStore({
261
+ runs: [],
262
+ dismissed: [],
263
+ stopRequested: [],
264
+ openRunId: void 0,
265
+ pendingLaunch: void 0,
266
+ autoOpenRunId: void 0
267
+ });
268
+ function isRecord(value) {
269
+ return typeof value === "object" && value !== null && !Array.isArray(value);
270
+ }
271
+ function without(list, runId) {
272
+ return list.filter((id) => id !== runId);
273
+ }
274
+ /** The full Subagents tab's runs minus the ones the reader dismissed. */
275
+ function visibleRuns(session) {
276
+ return session.dismissed.length === 0 ? session.runs : session.runs.filter((run) => !session.dismissed.includes(run.runId));
277
+ }
278
+ /** Only runs that can still be doing work belong in the activity group. */
279
+ function activityRuns(session) {
280
+ return visibleRuns(session).filter((run) => run.state === "queued" || run.state === "running");
281
+ }
282
+ /**
283
+ * Asks the session host to stop a run over the Team API, never as a parent
284
+ * prompt: the web host does not know Team's slash commands and refuses
285
+ * prompts mid-turn. Marked at once so the control reads "stopping"; rolled
286
+ * back and rethrown if the host refuses. Once accepted, the request stands
287
+ * until the run reports a final state.
288
+ */
289
+ async function requestRunStop(sessionId, runId) {
290
+ subagents.update(sessionId, (current) => ({
291
+ ...current,
292
+ stopRequested: [...without(current.stopRequested, runId), runId]
293
+ }));
294
+ try {
295
+ await stopRun(sessionId, runId);
296
+ } catch (error) {
297
+ subagents.update(sessionId, (current) => ({
298
+ ...current,
299
+ stopRequested: without(current.stopRequested, runId)
300
+ }));
301
+ throw error;
302
+ }
303
+ }
304
+ /** Hides a finished run from this page; nothing is deleted. */
305
+ function dismissRun(sessionId, runId) {
306
+ subagents.update(sessionId, (current) => ({
307
+ ...current,
308
+ dismissed: [...without(current.dismissed, runId), runId],
309
+ openRunId: current.openRunId === runId ? void 0 : current.openRunId
310
+ }));
311
+ }
312
+ /** Remember the fleet before the server launch so its first update can open the new run. */
313
+ function requestLaunch(sessionId, agent) {
314
+ subagents.update(sessionId, (current) => ({
315
+ ...current,
316
+ pendingLaunch: {
317
+ agent,
318
+ knownRunIds: current.runs.map((run) => run.runId)
319
+ }
320
+ }));
321
+ }
322
+ function clearPendingLaunch(sessionId) {
323
+ subagents.update(sessionId, (current) => ({
324
+ ...current,
325
+ pendingLaunch: void 0
326
+ }));
327
+ }
328
+ /** The plugin's session data channel: 'subagent_runs' payloads into the store. */
329
+ const subagentRunsChannel = subagents.channel({
330
+ channel: SUBAGENT_RUNS_TYPE,
331
+ parse(input) {
332
+ if (!isRecord(input) || !Array.isArray(input.runs)) return null;
333
+ return { runs: input.runs.filter(isRecord) };
334
+ },
335
+ reduce(current, { runs }) {
336
+ const present = new Map(runs.map((run) => [run.runId, run]));
337
+ const stopRequested = current.stopRequested.filter((runId) => {
338
+ const run = present.get(runId);
339
+ return run !== void 0 && !isTerminalRun(run);
340
+ });
341
+ const dismissed = current.dismissed.filter((runId) => present.has(runId));
342
+ const arrived = current.pendingLaunch === void 0 ? void 0 : runs.find((run) => run.agent === current.pendingLaunch?.agent && !current.pendingLaunch.knownRunIds.includes(run.runId));
343
+ return {
344
+ ...current,
345
+ runs,
346
+ stopRequested,
347
+ dismissed,
348
+ pendingLaunch: arrived === void 0 ? current.pendingLaunch : void 0,
349
+ autoOpenRunId: arrived === void 0 ? current.autoOpenRunId : arrived.runId
350
+ };
351
+ }
352
+ });
353
+ //#endregion
354
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/SubagentsPanel.native.tsx
355
+ function useSubagents(sessionId) {
356
+ const subscribe = useCallback((listener) => {
357
+ const sub = subagents.store.subscribe(listener);
358
+ return () => sub.unsubscribe();
359
+ }, []);
360
+ return useSyncExternalStore(subscribe, () => subagents.select(subagents.store.state, sessionId));
361
+ }
362
+ function subagentsTab() {
363
+ return {
364
+ id: "subagents",
365
+ label: "agents",
366
+ panel: SubagentsPanel
367
+ };
368
+ }
369
+ function agentThreadTab(run) {
370
+ const runId = run.runId;
371
+ return {
372
+ id: `subagents-run-${runId}`,
373
+ label: agentRunLabel(run),
374
+ panel: (props) => /* @__PURE__ */ jsx(AgentThreadPanel, {
375
+ ...props,
376
+ runId
377
+ })
378
+ };
379
+ }
380
+ function AgentRunRow({ run, onPress }) {
381
+ const detail = run.error ?? run.summary?.split("\n").find((line) => line.trim() !== "") ?? run.currentTool ?? run.tail.at(-1) ?? run.state;
382
+ return /* @__PURE__ */ jsx(ListRow, {
383
+ leading: /* @__PURE__ */ jsx(Dot, { tone: run.state === "failed" ? "red" : run.state === "running" ? "yellow" : run.state === "done" ? "green" : "muted" }),
384
+ title: /* @__PURE__ */ jsx(Button, {
385
+ variant: "ghost",
386
+ onPress,
387
+ testID: `activity-run-${run.runId}`,
388
+ children: agentRunLabel(run)
389
+ }),
390
+ description: detail,
391
+ trailing: /* @__PURE__ */ jsx(Badge, { children: run.state })
392
+ });
393
+ }
394
+ function SubagentsPanel(props) {
395
+ const state = useSubagents(props.sessionId);
396
+ return /* @__PURE__ */ jsxs(ScrollView, {
397
+ contentContainerStyle: styles.body,
398
+ children: [
399
+ /* @__PURE__ */ jsx(Button, {
400
+ testID: "activity-agents-launch",
401
+ onPress: () => {
402
+ if (props.sessionId !== null) openCatalog(props.sessionId);
403
+ },
404
+ children: "Launch an agent"
405
+ }),
406
+ visibleRuns(state).map((run) => /* @__PURE__ */ jsx(AgentRunRow, {
407
+ run,
408
+ onPress: () => props.openTransientTab(agentThreadTab(run))
409
+ }, run.runId)),
410
+ /* @__PURE__ */ jsx(AgentCatalog, { ...props })
411
+ ]
412
+ });
413
+ }
414
+ function AgentCatalog({ sessionId }) {
415
+ const subscribe = useCallback((listener) => {
416
+ const sub = catalog.store.subscribe(listener);
417
+ return () => sub.unsubscribe();
418
+ }, []);
419
+ const state = useSyncExternalStore(subscribe, () => catalog.select(catalog.store.state, sessionId));
420
+ const [filter, setFilter] = useState("");
421
+ const [agent, setAgent] = useState();
422
+ useEffect(() => {
423
+ if (sessionId !== null && state.open) return loadCatalog(sessionId);
424
+ }, [sessionId, state.open]);
425
+ if (sessionId === null || !state.open) return null;
426
+ return /* @__PURE__ */ jsx(Sheet, {
427
+ open: true,
428
+ onOpenChange: (open) => {
429
+ if (!open) {
430
+ closeCatalog(sessionId);
431
+ setAgent(void 0);
432
+ }
433
+ },
434
+ children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: agent ? `Launch ${agent.name}` : "Launch an agent" }) }), /* @__PURE__ */ jsx(SheetBody, { children: agent ? /* @__PURE__ */ jsx(AgentLaunch, {
435
+ sessionId,
436
+ agent,
437
+ models: state.models,
438
+ initialTask: state.task,
439
+ onClose: () => {
440
+ closeCatalog(sessionId);
441
+ setAgent(void 0);
442
+ }
443
+ }, agent.name) : /* @__PURE__ */ jsxs(View, {
444
+ style: styles.body,
445
+ children: [
446
+ /* @__PURE__ */ jsx(Input, {
447
+ accessibilityLabel: "Filter agents",
448
+ placeholder: "Find an agent",
449
+ value: filter,
450
+ onChangeText: setFilter
451
+ }),
452
+ state.loading ? /* @__PURE__ */ jsx(Text, {
453
+ style: styles.meta,
454
+ children: "Loading agents…"
455
+ }) : null,
456
+ state.warning ? /* @__PURE__ */ jsx(Text, {
457
+ style: styles.error,
458
+ children: state.warning
459
+ }) : null,
460
+ filterCatalog(state.agents, filter).map((item) => /* @__PURE__ */ jsx(ListRow, {
461
+ title: /* @__PURE__ */ jsx(Button, {
462
+ variant: "ghost",
463
+ onPress: () => setAgent(item),
464
+ children: item.name
465
+ }),
466
+ description: `${item.description}\n${agentMeta(item)}`
467
+ }, item.name))
468
+ ]
469
+ }) })] })
470
+ });
471
+ }
472
+ function AgentLaunch({ sessionId, agent, models, initialTask, onClose }) {
473
+ const [task, setTask] = useState(initialTask);
474
+ const [fork, setFork] = useState(agent.defaultContext === "fork");
475
+ const [model, setModel] = useState("");
476
+ const [busy, setBusy] = useState(false);
477
+ const [error, setError] = useState();
478
+ const launch = async () => {
479
+ if (busy) return;
480
+ setBusy(true);
481
+ setError(void 0);
482
+ requestLaunch(sessionId, agent.name);
483
+ try {
484
+ await launchAgent(sessionId, {
485
+ agent: agent.name,
486
+ task,
487
+ fork,
488
+ ...model === "" ? {} : { model }
489
+ });
490
+ onClose();
491
+ } catch (cause) {
492
+ clearPendingLaunch(sessionId);
493
+ setError(cause instanceof Error ? cause.message : String(cause));
494
+ } finally {
495
+ setBusy(false);
496
+ }
497
+ };
498
+ return /* @__PURE__ */ jsxs(View, {
499
+ style: styles.body,
500
+ children: [
501
+ /* @__PURE__ */ jsx(Text, {
502
+ style: styles.meta,
503
+ children: agent.description
504
+ }),
505
+ /* @__PURE__ */ jsx(Textarea, {
506
+ accessibilityLabel: "Task",
507
+ value: task,
508
+ onChangeText: setTask,
509
+ placeholder: "What should the agent do? Empty runs its own prompt"
510
+ }),
511
+ /* @__PURE__ */ jsx(SegmentedControl, {
512
+ value: fork ? "fork" : "fresh",
513
+ onValueChange: (value) => setFork(value === "fork"),
514
+ options: [{
515
+ value: "fresh",
516
+ label: "Fresh"
517
+ }, {
518
+ value: "fork",
519
+ label: "Fork this session"
520
+ }]
521
+ }),
522
+ /* @__PURE__ */ jsx(Text, {
523
+ style: styles.meta,
524
+ children: "Model"
525
+ }),
526
+ /* @__PURE__ */ jsxs(Button, {
527
+ variant: model === "" ? "primary" : "outline",
528
+ onPress: () => setModel(""),
529
+ children: ["Agent default", agent.model ? ` · ${agent.model}` : ""]
530
+ }),
531
+ modelChoices(agent, models).map((choice) => /* @__PURE__ */ jsx(Button, {
532
+ variant: "outline",
533
+ onPress: () => setModel(choice),
534
+ accessibilityState: { selected: model === choice },
535
+ children: choice
536
+ }, choice)),
537
+ error ? /* @__PURE__ */ jsx(Text, {
538
+ style: styles.error,
539
+ children: error
540
+ }) : null,
541
+ /* @__PURE__ */ jsx(Button, {
542
+ testID: "launch-agent-submit",
543
+ disabled: busy,
544
+ onPress: () => void launch(),
545
+ children: busy ? "Launching…" : "Launch"
546
+ })
547
+ ]
548
+ });
549
+ }
550
+ function AgentThreadPanel({ sessionId, runId, renderThread }) {
551
+ const state = useSubagents(sessionId);
552
+ const run = state.runs.find((item) => item.runId === runId);
553
+ const [guidance, setGuidance] = useState("");
554
+ const [busy, setBusy] = useState(false);
555
+ const [notice, setNotice] = useState();
556
+ const [confirmClear, setConfirmClear] = useState(false);
557
+ const act = async (action) => {
558
+ setBusy(true);
559
+ setNotice(void 0);
560
+ try {
561
+ await action();
562
+ } catch (cause) {
563
+ setNotice(cause instanceof Error ? cause.message : String(cause));
564
+ } finally {
565
+ setBusy(false);
566
+ }
567
+ };
568
+ return /* @__PURE__ */ jsxs(View, {
569
+ style: styles.screen,
570
+ children: [
571
+ run ? /* @__PURE__ */ jsxs(View, {
572
+ style: styles.body,
573
+ children: [
574
+ /* @__PURE__ */ jsxs(Text, {
575
+ style: styles.title,
576
+ children: [
577
+ agentRunLabel(run),
578
+ " · ",
579
+ run.state
580
+ ]
581
+ }),
582
+ /* @__PURE__ */ jsxs(Text, {
583
+ style: styles.meta,
584
+ children: [
585
+ run.model,
586
+ "\\n",
587
+ run.task,
588
+ "\\n",
589
+ formatRunDuration(Math.max(0, (run.endedAt ?? run.lastUpdate) - run.startedAt))
590
+ ]
591
+ }),
592
+ sessionId !== null ? isTerminalRun(run) ? /* @__PURE__ */ jsx(Button, {
593
+ variant: "outline",
594
+ onPress: () => setConfirmClear(true),
595
+ children: "Clear run"
596
+ }) : /* @__PURE__ */ jsx(Button, {
597
+ variant: "danger-outline",
598
+ disabled: busy || state.stopRequested.includes(runId),
599
+ onPress: () => void act(() => requestRunStop(sessionId, runId)),
600
+ children: state.stopRequested.includes(runId) ? "Stopping…" : "Stop"
601
+ }) : null
602
+ ]
603
+ }) : /* @__PURE__ */ jsx(Text, {
604
+ style: styles.meta,
605
+ children: "This run is no longer listed; its transcript stays readable."
606
+ }),
607
+ /* @__PURE__ */ jsx(View, {
608
+ style: styles.transcript,
609
+ children: renderThread(runId)
610
+ }),
611
+ run && sessionId !== null && !isTerminalRun(run) ? /* @__PURE__ */ jsxs(View, {
612
+ style: styles.body,
613
+ children: [/* @__PURE__ */ jsx(Textarea, {
614
+ accessibilityLabel: "Steering guidance",
615
+ value: guidance,
616
+ onChangeText: setGuidance,
617
+ placeholder: "Guide this agent…"
618
+ }), /* @__PURE__ */ jsx(Button, {
619
+ disabled: busy || guidance.trim() === "",
620
+ onPress: () => void act(async () => {
621
+ const result = await steerRun(sessionId, runId, guidance.trim());
622
+ setNotice(result.message);
623
+ if (result.state !== "failed") setGuidance("");
624
+ }),
625
+ children: "Send guidance"
626
+ })]
627
+ }) : null,
628
+ notice ? /* @__PURE__ */ jsx(Text, {
629
+ style: styles.meta,
630
+ children: notice
631
+ }) : null,
632
+ /* @__PURE__ */ jsx(Sheet, {
633
+ open: confirmClear,
634
+ onOpenChange: setConfirmClear,
635
+ children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "Clear this run?" }) }), /* @__PURE__ */ jsxs(SheetBody, { children: [/* @__PURE__ */ jsx(Text, {
636
+ style: styles.meta,
637
+ children: "This hides the finished run. Its transcript stays on disk."
638
+ }), /* @__PURE__ */ jsx(Button, {
639
+ variant: "danger-outline",
640
+ onPress: () => {
641
+ if (sessionId !== null) dismissRun(sessionId, runId);
642
+ setConfirmClear(false);
643
+ },
644
+ children: "Clear"
645
+ })] })] })
646
+ })
647
+ ]
648
+ });
649
+ }
650
+ const styles = StyleSheet.create((theme) => ({
651
+ screen: {
652
+ flex: 1,
653
+ backgroundColor: theme.colors.bg,
654
+ uni__dependencies: [0]
655
+ },
656
+ transcript: { flex: 1 },
657
+ body: {
658
+ padding: theme.spacing(3),
659
+ gap: theme.spacing(2),
660
+ uni__dependencies: [0]
661
+ },
662
+ title: {
663
+ color: theme.colors.hi,
664
+ fontSize: theme.fontSize.base,
665
+ fontWeight: "600",
666
+ uni__dependencies: [0]
667
+ },
668
+ meta: {
669
+ color: theme.colors.dim,
670
+ fontSize: theme.fontSize.sm,
671
+ uni__dependencies: [0]
672
+ },
673
+ error: {
674
+ color: theme.colors.red,
675
+ fontSize: theme.fontSize.sm,
676
+ uni__dependencies: [0]
677
+ }
678
+ }));
679
+ //#endregion
680
+ //#region src/extensions/workspaces/sessions/(frontend)/activity-group/agents.native.ts
681
+ var agents_native_default = defineActivityGroup({
682
+ keys: "a r",
683
+ icon: () => createElement(UsersIcon),
684
+ statusKey: "doom-team-agents",
685
+ order: 10,
686
+ transientTab: subagentsTab,
687
+ activeSource: {
688
+ subscribe(listener) {
689
+ const subscription = subagents.store.subscribe(listener);
690
+ return () => subscription.unsubscribe();
691
+ },
692
+ isActive(sessionId) {
693
+ return activityRuns(subagents.select(subagents.store.state, sessionId)).length > 0;
694
+ }
695
+ }
696
+ });
697
+ //#endregion
698
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/subagent-catalog.native.ts
699
+ var subagent_catalog_native_default = defineChannelFile(subagentCatalogChannel);
700
+ //#endregion
701
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/subagent-runs.native.ts
702
+ var subagent_runs_native_default = defineChannelFile(subagentRunsChannel);
703
+ //#endregion
704
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/AgentsActivitySection.native.tsx
705
+ function AgentsActivitySection({ sessionId, openTransientTab }) {
706
+ const runs = activityRuns(useSubagents(sessionId));
707
+ return /* @__PURE__ */ jsxs(View, { children: [runs.map((run) => /* @__PURE__ */ jsx(AgentRunRow, {
708
+ run,
709
+ onPress: () => openTransientTab(agentThreadTab(run))
710
+ }, run.runId)), runs.length === 0 ? /* @__PURE__ */ jsx(Button, {
711
+ variant: "ghost",
712
+ testID: "activity-agents-launch",
713
+ disabled: sessionId === null,
714
+ onPress: () => {
715
+ if (sessionId !== null) {
716
+ openCatalog(sessionId);
717
+ openTransientTab(subagentsTab());
718
+ }
719
+ },
720
+ children: "Launch an agent"
721
+ }) : null] });
722
+ }
723
+ //#endregion
724
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Agents.activity.agents.native.tsx
725
+ var Agents_activity_agents_native_default = defineFill({ component: AgentsActivitySection });
726
+ //#endregion
727
+ //#region generated/native.ts
728
+ const via = (identity, value) => value === void 0 ? void 0 : {
729
+ ...identity,
730
+ ...value
731
+ };
732
+ const nativePlugin = defineNativePlugin({
733
+ id: "subagents",
734
+ session: {
735
+ get activityGroups() {
736
+ return [via({ name: "agents" }, agents_native_default)];
737
+ },
738
+ get channels() {
739
+ return [via({ channel: "subagent_catalog" }, subagent_catalog_native_default), via({ channel: "subagent_runs" }, subagent_runs_native_default)];
740
+ },
741
+ get fills() {
742
+ return [via({
743
+ slot: "activity.agents",
744
+ id: "agents"
745
+ }, Agents_activity_agents_native_default)];
746
+ }
747
+ }
748
+ });
749
+ //#endregion
750
+ export { nativePlugin };
751
+
752
+ //# sourceMappingURL=native.mjs.map