@agimon-ai/po-task 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/apiContracts.cjs +54 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +49 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/config-CFSs3iku.mjs +72 -0
  10. package/dist/config-CFSs3iku.mjs.map +1 -0
  11. package/dist/config-CdX-Bv8q.cjs +173 -0
  12. package/dist/config-CdX-Bv8q.cjs.map +1 -0
  13. package/dist/config.cjs +19 -0
  14. package/dist/config.d.mts +2 -0
  15. package/dist/config.mjs +2 -0
  16. package/dist/delegation-BiOy3_7g.mjs +7 -0
  17. package/dist/delegation-BiOy3_7g.mjs.map +1 -0
  18. package/dist/delegation-CdMqOZB4.mjs +626 -0
  19. package/dist/delegation-CdMqOZB4.mjs.map +1 -0
  20. package/dist/delegation-DsnKjMbJ.cjs +661 -0
  21. package/dist/delegation-DsnKjMbJ.cjs.map +1 -0
  22. package/dist/delegation-LDpHP2CC.cjs +12 -0
  23. package/dist/delegation-LDpHP2CC.cjs.map +1 -0
  24. package/dist/delegationManager.cjs +10 -0
  25. package/dist/delegationManager.d.mts +2 -0
  26. package/dist/delegationManager.mjs +3 -0
  27. package/dist/extensions/mcp-ui.mjs +23 -0
  28. package/dist/extensions/mcp-ui.mjs.map +1 -0
  29. package/dist/extensions/mcp.cjs +1144 -0
  30. package/dist/extensions/mcp.cjs.map +1 -0
  31. package/dist/extensions/mcp.d.mts +5 -0
  32. package/dist/extensions/mcp.d.mts.map +1 -0
  33. package/dist/extensions/mcp.mjs +1115 -0
  34. package/dist/extensions/mcp.mjs.map +1 -0
  35. package/dist/extensions/native.mjs +1175 -0
  36. package/dist/extensions/native.mjs.map +1 -0
  37. package/dist/extensions/server.cjs +569 -0
  38. package/dist/extensions/server.cjs.map +1 -0
  39. package/dist/extensions/server.d.mts +5 -0
  40. package/dist/extensions/server.d.mts.map +1 -0
  41. package/dist/extensions/server.mjs +563 -0
  42. package/dist/extensions/server.mjs.map +1 -0
  43. package/dist/extensions/web.mjs +1018 -0
  44. package/dist/extensions/web.mjs.map +1 -0
  45. package/dist/index-B-3sCF2S.d.mts +43 -0
  46. package/dist/index-B-3sCF2S.d.mts.map +1 -0
  47. package/dist/index-B1JPJdpZ.d.mts +51 -0
  48. package/dist/index-B1JPJdpZ.d.mts.map +1 -0
  49. package/dist/index-CUWCvh-E.d.mts +72 -0
  50. package/dist/index-CUWCvh-E.d.mts.map +1 -0
  51. package/dist/index-DlmXKyhQ.d.mts +34 -0
  52. package/dist/index-DlmXKyhQ.d.mts.map +1 -0
  53. package/dist/index-Dvb8X_qB.d.mts +67 -0
  54. package/dist/index-Dvb8X_qB.d.mts.map +1 -0
  55. package/dist/index-XY9VPmfm.d.mts +26 -0
  56. package/dist/index-XY9VPmfm.d.mts.map +1 -0
  57. package/dist/index-teIf67qQ.d.mts +196 -0
  58. package/dist/index-teIf67qQ.d.mts.map +1 -0
  59. package/dist/index.cjs +93 -0
  60. package/dist/index.d.mts +11 -0
  61. package/dist/index.mjs +12 -0
  62. package/dist/invariants-DhjEjBPU.cjs +79 -0
  63. package/dist/invariants-DhjEjBPU.cjs.map +1 -0
  64. package/dist/invariants-q_UamF7D.mjs +62 -0
  65. package/dist/invariants-q_UamF7D.mjs.map +1 -0
  66. package/dist/logSinkTelemetry-B23qGiP-.cjs +52 -0
  67. package/dist/logSinkTelemetry-B23qGiP-.cjs.map +1 -0
  68. package/dist/logSinkTelemetry-BMBsC54p.mjs +41 -0
  69. package/dist/logSinkTelemetry-BMBsC54p.mjs.map +1 -0
  70. package/dist/logSinkTelemetry.cjs +6 -0
  71. package/dist/logSinkTelemetry.d.mts +23 -0
  72. package/dist/logSinkTelemetry.d.mts.map +1 -0
  73. package/dist/logSinkTelemetry.mjs +3 -0
  74. package/dist/paths-CSya2l9_.cjs +308 -0
  75. package/dist/paths-CSya2l9_.cjs.map +1 -0
  76. package/dist/paths-Cah1vFGv.mjs +207 -0
  77. package/dist/paths-Cah1vFGv.mjs.map +1 -0
  78. package/dist/processLiveness-Ccd9wenT.mjs +20 -0
  79. package/dist/processLiveness-Ccd9wenT.mjs.map +1 -0
  80. package/dist/processLiveness-umpNgsPR.cjs +25 -0
  81. package/dist/processLiveness-umpNgsPR.cjs.map +1 -0
  82. package/dist/reconcile-BcD-hTbd.mjs +63 -0
  83. package/dist/reconcile-BcD-hTbd.mjs.map +1 -0
  84. package/dist/reconcile-cRL0NjDr.cjs +80 -0
  85. package/dist/reconcile-cRL0NjDr.cjs.map +1 -0
  86. package/dist/reducer-BEfJFM5L.mjs +345 -0
  87. package/dist/reducer-BEfJFM5L.mjs.map +1 -0
  88. package/dist/reducer-BV0YIZ2o.cjs +380 -0
  89. package/dist/reducer-BV0YIZ2o.cjs.map +1 -0
  90. package/dist/storeInvariants.cjs +5 -0
  91. package/dist/storeInvariants.d.mts +23 -0
  92. package/dist/storeInvariants.d.mts.map +1 -0
  93. package/dist/storeInvariants.mjs +2 -0
  94. package/dist/storePaths.cjs +14 -0
  95. package/dist/storePaths.d.mts +2 -0
  96. package/dist/storePaths.mjs +2 -0
  97. package/dist/storeProcessLiveness.cjs +3 -0
  98. package/dist/storeProcessLiveness.d.mts +11 -0
  99. package/dist/storeProcessLiveness.d.mts.map +1 -0
  100. package/dist/storeProcessLiveness.mjs +2 -0
  101. package/dist/storeReconcile.cjs +5 -0
  102. package/dist/storeReconcile.d.mts +2 -0
  103. package/dist/storeReconcile.mjs +2 -0
  104. package/dist/storeReducer.cjs +8 -0
  105. package/dist/storeReducer.d.mts +2 -0
  106. package/dist/storeReducer.mjs +2 -0
  107. package/dist/storeTaskGraph.cjs +7 -0
  108. package/dist/storeTaskGraph.d.mts +2 -0
  109. package/dist/storeTaskGraph.mjs +2 -0
  110. package/dist/storeTaskStore.cjs +3 -0
  111. package/dist/storeTaskStore.d.mts +2 -0
  112. package/dist/storeTaskStore.mjs +2 -0
  113. package/dist/storeTypes.cjs +6 -0
  114. package/dist/storeTypes.d.mts +2 -0
  115. package/dist/storeTypes.mjs +2 -0
  116. package/dist/task-CXF9VULl.cjs +49 -0
  117. package/dist/task-CXF9VULl.cjs.map +1 -0
  118. package/dist/task-DJpTteeJ.d.mts +187 -0
  119. package/dist/task-DJpTteeJ.d.mts.map +1 -0
  120. package/dist/task-DO0TwZ0z.mjs +132 -0
  121. package/dist/task-DO0TwZ0z.mjs.map +1 -0
  122. package/dist/task-Dhta7JyA.d.mts +126 -0
  123. package/dist/task-Dhta7JyA.d.mts.map +1 -0
  124. package/dist/task-UvW877Ez.cjs +227 -0
  125. package/dist/task-UvW877Ez.cjs.map +1 -0
  126. package/dist/task-giCbpKzV.mjs +26 -0
  127. package/dist/task-giCbpKzV.mjs.map +1 -0
  128. package/dist/taskGraph-BOQvT0-M.mjs +63 -0
  129. package/dist/taskGraph-BOQvT0-M.mjs.map +1 -0
  130. package/dist/taskGraph-BskRslrr.cjs +92 -0
  131. package/dist/taskGraph-BskRslrr.cjs.map +1 -0
  132. package/dist/taskGraph-D0zFHF2p.d.mts +25 -0
  133. package/dist/taskGraph-D0zFHF2p.d.mts.map +1 -0
  134. package/dist/taskResult-D2G7b4w-.cjs +224 -0
  135. package/dist/taskResult-D2G7b4w-.cjs.map +1 -0
  136. package/dist/taskResult-edBAAKSH.mjs +165 -0
  137. package/dist/taskResult-edBAAKSH.mjs.map +1 -0
  138. package/dist/taskStore-DjU60rMm.cjs +215 -0
  139. package/dist/taskStore-DjU60rMm.cjs.map +1 -0
  140. package/dist/taskStore-pAJ971Ml.mjs +208 -0
  141. package/dist/taskStore-pAJ971Ml.mjs.map +1 -0
  142. package/dist/telemetry-Ca0I3Tqo.cjs +30 -0
  143. package/dist/telemetry-Ca0I3Tqo.cjs.map +1 -0
  144. package/dist/telemetry-DyS44zAD.mjs +25 -0
  145. package/dist/telemetry-DyS44zAD.mjs.map +1 -0
  146. package/dist/telemetry-FMGRovb4.d.mts +35 -0
  147. package/dist/telemetry-FMGRovb4.d.mts.map +1 -0
  148. package/dist/toolPromptGuidelines.cjs +27 -0
  149. package/dist/toolPromptGuidelines.cjs.map +1 -0
  150. package/dist/toolPromptGuidelines.d.mts +13 -0
  151. package/dist/toolPromptGuidelines.d.mts.map +1 -0
  152. package/dist/toolPromptGuidelines.mjs +26 -0
  153. package/dist/toolPromptGuidelines.mjs.map +1 -0
  154. package/dist/toolResponseEnvelope.cjs +12 -0
  155. package/dist/toolResponseEnvelope.d.mts +2 -0
  156. package/dist/toolResponseEnvelope.mjs +2 -0
  157. package/dist/toolSchema.cjs +18 -0
  158. package/dist/toolSchema.d.mts +2 -0
  159. package/dist/toolSchema.mjs +2 -0
  160. package/package.json +223 -0
  161. package/src/types/webTasks.ts +24 -0
@@ -0,0 +1,1018 @@
1
+ import { defineActivityGroup, defineChannelFile, defineFill, defineSessionStore, defineToolRenderer, defineWebPlugin } from "@agimon-ai/po-core/web";
2
+ import { Button, ChevronDownIcon, ChevronRightIcon, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Dot, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, EditIcon, Input, KebabIcon, MessageIcon, MessageItem, MessageItemBody, MessageItemHeader, MessageItemStatus, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, TaskIcon, Textarea, TrashIcon, toolTone } from "@agimon-ai/po-web-components";
3
+ import { useStore } from "@tanstack/react-store";
4
+ import { useState } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/types/webTasks.ts
7
+ /** Browser-safe task view published by the cockpit hub channel. */
8
+ const TASKS_CHANNEL_TYPE = "task_graph";
9
+ //#endregion
10
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/_lib/tasksModel.ts
11
+ function isRecord$1(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ function parseTask(value) {
15
+ if (!isRecord$1(value) || typeof value.id !== "number" || typeof value.subject !== "string") return null;
16
+ if (![
17
+ "pending",
18
+ "in_progress",
19
+ "completed",
20
+ "failed"
21
+ ].includes(String(value.status))) return null;
22
+ const task = {
23
+ id: value.id,
24
+ subject: value.subject,
25
+ status: value.status,
26
+ blockedBy: Array.isArray(value.blockedBy) ? value.blockedBy.filter((id) => typeof id === "number") : []
27
+ };
28
+ if (typeof value.description === "string") task.description = value.description;
29
+ if (typeof value.activeForm === "string") task.activeForm = value.activeForm;
30
+ if (typeof value.owner === "string") task.owner = value.owner;
31
+ if (typeof value.updatedAt === "string") task.updatedAt = value.updatedAt;
32
+ if (isRecord$1(value.delegation)) {
33
+ task.delegation = {};
34
+ if (typeof value.delegation.agent === "string") task.delegation.agent = value.delegation.agent;
35
+ if (typeof value.delegation.state === "string") task.delegation.state = value.delegation.state;
36
+ }
37
+ return task;
38
+ }
39
+ /** Narrows a hub frame to a task graph payload; null when it is not one. */
40
+ function parseTasksPayload(input) {
41
+ if (!isRecord$1(input) || !Array.isArray(input.tasks) || typeof input.rev !== "number") return null;
42
+ return {
43
+ tasks: input.tasks.map(parseTask).filter((task) => task !== null),
44
+ rev: input.rev
45
+ };
46
+ }
47
+ function taskEditDraft(task) {
48
+ return {
49
+ subject: task.subject,
50
+ description: task.description ?? "",
51
+ status: task.status
52
+ };
53
+ }
54
+ function changedFields(task, draft) {
55
+ const changes = [];
56
+ const subject = draft.subject.trim();
57
+ const description = draft.description.trim();
58
+ if (subject && subject !== task.subject) changes.push(`set subject to "${subject}"`);
59
+ if (description !== (task.description ?? "").trim()) changes.push(description ? `set description to "${description}"` : "clear the description");
60
+ if (draft.status !== task.status) changes.push(`set status to ${draft.status}`);
61
+ return changes;
62
+ }
63
+ /**
64
+ * The agent stays the only writer of task state, so an edit leaves the cockpit
65
+ * as one prompt naming exactly the fields the human changed. Nothing changed
66
+ * means nothing to send.
67
+ */
68
+ function taskEditInstruction(task, draft) {
69
+ const changes = changedFields(task, draft);
70
+ if (changes.length === 0) return null;
71
+ return `Update task #${task.id}: ${changes.join("; ")}. Change nothing else and keep the dependency graph valid.`;
72
+ }
73
+ function requestTaskEdit(send, sessionId, task, draft) {
74
+ const message = taskEditInstruction(task, draft);
75
+ if (message === null) return false;
76
+ send(sessionId, {
77
+ type: "prompt",
78
+ message
79
+ });
80
+ return true;
81
+ }
82
+ /** Steering for the subagent that owns a delegated task. */
83
+ function taskMessageInstruction(taskId, agent, message) {
84
+ return `Steer the ${agent} run working on task #${taskId} with this message, then update the task if its plan changed: ${message.trim()}`;
85
+ }
86
+ function requestTaskMessage(send, sessionId, taskId, agent, message) {
87
+ send(sessionId, {
88
+ type: "prompt",
89
+ message: taskMessageInstruction(taskId, agent, message)
90
+ });
91
+ }
92
+ function requestTaskRemoval(send, sessionId, taskId) {
93
+ send(sessionId, {
94
+ type: "prompt",
95
+ message: `Remove task #${taskId}. Also remove or repair every dependency that references it so the remaining task graph is valid.`
96
+ });
97
+ }
98
+ //#endregion
99
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/_lib/tasksStore.ts
100
+ const tasks = defineSessionStore({
101
+ tasks: [],
102
+ rev: 0
103
+ });
104
+ const tasksChannel = tasks.channel({
105
+ channel: TASKS_CHANNEL_TYPE,
106
+ parse: parseTasksPayload,
107
+ reduce: (_current, payload) => payload
108
+ });
109
+ //#endregion
110
+ //#region src/extensions/workspaces/sessions/(frontend)/activity-group/tasks.web.ts
111
+ /**
112
+ * The live task board in the session Activity dock.
113
+ *
114
+ * Active means a delegated run is still out, not that the list has entries. A
115
+ * pending task waits for this agent's next turn and reports to nobody, so
116
+ * counting it as running would leave the dock badge, the top bar and the
117
+ * timeline's background-work notice claiming work that nothing will finish.
118
+ */
119
+ var tasks_web_default = defineActivityGroup({
120
+ keys: "t l",
121
+ icon: TaskIcon,
122
+ statusKey: "@agimon-ai/po-task",
123
+ marksBackgroundWork: false,
124
+ activeSource: {
125
+ subscribe(listener) {
126
+ const subscription = tasks.store.subscribe(listener);
127
+ return () => subscription.unsubscribe();
128
+ },
129
+ isActive(sessionId) {
130
+ return tasks.select(tasks.store.state, sessionId).tasks.some((task) => task.delegation?.state === "requested" || task.delegation?.state === "running");
131
+ }
132
+ },
133
+ order: 65
134
+ });
135
+ //#endregion
136
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/task-graph.web.ts
137
+ var task_graph_web_default = defineChannelFile(tasksChannel);
138
+ //#endregion
139
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_lib/TaskDetailDialog.tsx
140
+ const STATUS_CHOICES = [
141
+ "pending",
142
+ "in_progress",
143
+ "completed",
144
+ "failed"
145
+ ];
146
+ const DESCRIPTION_ROWS = 5;
147
+ const MESSAGE_ROWS = 4;
148
+ function FieldLabel({ children }) {
149
+ return /* @__PURE__ */ jsx("span", {
150
+ className: "text-base font-medium text-doom-text",
151
+ children
152
+ });
153
+ }
154
+ function Field({ label, children }) {
155
+ return /* @__PURE__ */ jsxs("div", {
156
+ className: "flex flex-col gap-1.5",
157
+ children: [/* @__PURE__ */ jsx(FieldLabel, { children: label }), children]
158
+ });
159
+ }
160
+ function ReadOnlyValue({ value, testId }) {
161
+ return /* @__PURE__ */ jsx("span", {
162
+ "data-testid": testId,
163
+ className: "whitespace-pre-wrap break-words text-base leading-relaxed text-doom-dim",
164
+ children: value
165
+ });
166
+ }
167
+ function TaskFacts({ task }) {
168
+ return /* @__PURE__ */ jsxs("div", {
169
+ className: "flex flex-wrap gap-5",
170
+ children: [
171
+ /* @__PURE__ */ jsx(Field, {
172
+ label: "Status",
173
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
174
+ testId: "task-detail-status",
175
+ value: task.status.replace("_", " ")
176
+ })
177
+ }),
178
+ task.activeForm ? /* @__PURE__ */ jsx(Field, {
179
+ label: "Active form",
180
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
181
+ testId: "task-detail-active-form",
182
+ value: task.activeForm
183
+ })
184
+ }) : null,
185
+ task.owner ? /* @__PURE__ */ jsx(Field, {
186
+ label: "Owner",
187
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
188
+ testId: "task-detail-owner",
189
+ value: task.owner
190
+ })
191
+ }) : null,
192
+ task.delegation?.agent ? /* @__PURE__ */ jsx(Field, {
193
+ label: "Delegated to",
194
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
195
+ testId: "task-detail-agent",
196
+ value: task.delegation.state ? `${task.delegation.agent} · ${task.delegation.state}` : task.delegation.agent
197
+ })
198
+ }) : null,
199
+ task.blockedBy.length > 0 ? /* @__PURE__ */ jsx(Field, {
200
+ label: "Blocked by",
201
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
202
+ testId: "task-detail-blocked-by",
203
+ value: task.blockedBy.map((id) => `#${id}`).join(", ")
204
+ })
205
+ }) : null,
206
+ task.updatedAt ? /* @__PURE__ */ jsx(Field, {
207
+ label: "Updated",
208
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
209
+ testId: "task-detail-updated",
210
+ value: task.updatedAt
211
+ })
212
+ }) : null
213
+ ]
214
+ });
215
+ }
216
+ /**
217
+ * One task, read first and changed second. Every change leaves as a prompt the
218
+ * session can see, so the agent remains the only writer of task state.
219
+ */
220
+ function TaskDetailDialog({ task, sessionId, mode: initialMode, send, onClose }) {
221
+ const [mode, setMode] = useState(initialMode);
222
+ const [draft, setDraft] = useState(() => taskEditDraft(task));
223
+ const [message, setMessage] = useState("");
224
+ const agent = task.delegation?.agent;
225
+ const submitEdit = () => {
226
+ if (requestTaskEdit(send, sessionId, task, draft)) onClose();
227
+ };
228
+ const submitMessage = () => {
229
+ if (!message.trim() || !agent) return;
230
+ requestTaskMessage(send, sessionId, task.id, agent, message);
231
+ onClose();
232
+ };
233
+ return /* @__PURE__ */ jsx(Dialog, {
234
+ open: true,
235
+ onOpenChange: (open) => {
236
+ if (!open) onClose();
237
+ },
238
+ children: /* @__PURE__ */ jsxs(DialogContent, {
239
+ width: "lg",
240
+ "data-testid": "task-detail-dialog",
241
+ "data-mode": mode,
242
+ "aria-describedby": void 0,
243
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsxs("div", {
244
+ className: "flex min-w-0 flex-col items-start gap-1 sm:flex-row sm:items-center sm:gap-2.5",
245
+ children: [/* @__PURE__ */ jsxs("span", {
246
+ className: "text-sm text-doom-dim",
247
+ children: ["task #", task.id]
248
+ }), /* @__PURE__ */ jsx(DialogTitle, {
249
+ "data-testid": "task-detail-title",
250
+ className: "max-w-full break-words",
251
+ children: task.subject
252
+ })]
253
+ }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
254
+ mode === "view" ? /* @__PURE__ */ jsxs(Fragment, { children: [
255
+ /* @__PURE__ */ jsx(Field, {
256
+ label: "Description",
257
+ children: /* @__PURE__ */ jsx(ReadOnlyValue, {
258
+ testId: "task-detail-description",
259
+ value: task.description ?? "no description on this task"
260
+ })
261
+ }),
262
+ /* @__PURE__ */ jsx(TaskFacts, { task }),
263
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
264
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
265
+ agent ? /* @__PURE__ */ jsx(Button, {
266
+ variant: "outline",
267
+ size: "xs",
268
+ "data-testid": "task-detail-open-message",
269
+ onClick: () => setMode("message"),
270
+ children: "message agent"
271
+ }) : null,
272
+ /* @__PURE__ */ jsx(Button, {
273
+ variant: "primary",
274
+ size: "xs",
275
+ "data-testid": "task-detail-open-edit",
276
+ onClick: () => setMode("edit"),
277
+ children: "edit"
278
+ })
279
+ ] })
280
+ ] }) : null,
281
+ mode === "edit" ? /* @__PURE__ */ jsxs(Fragment, { children: [
282
+ /* @__PURE__ */ jsx(Field, {
283
+ label: "Subject",
284
+ children: /* @__PURE__ */ jsx(Input, {
285
+ "data-testid": "task-detail-subject-input",
286
+ autoFocus: true,
287
+ value: draft.subject,
288
+ onChange: (event) => setDraft({
289
+ ...draft,
290
+ subject: event.target.value
291
+ })
292
+ })
293
+ }),
294
+ /* @__PURE__ */ jsx(Field, {
295
+ label: "Description",
296
+ children: /* @__PURE__ */ jsx(Textarea, {
297
+ "data-testid": "task-detail-description-input",
298
+ rows: DESCRIPTION_ROWS,
299
+ value: draft.description,
300
+ placeholder: "what this task covers",
301
+ onChange: (event) => setDraft({
302
+ ...draft,
303
+ description: event.target.value
304
+ })
305
+ })
306
+ }),
307
+ /* @__PURE__ */ jsx(Field, {
308
+ label: "Status",
309
+ children: /* @__PURE__ */ jsxs(Select, {
310
+ value: draft.status,
311
+ onValueChange: (value) => setDraft({
312
+ ...draft,
313
+ status: value
314
+ }),
315
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
316
+ "data-testid": "task-detail-status-input",
317
+ className: "h-7 w-full min-w-0 sm:min-w-[200px]",
318
+ children: /* @__PURE__ */ jsx(SelectValue, {})
319
+ }), /* @__PURE__ */ jsx(SelectContent, { children: STATUS_CHOICES.map((status) => /* @__PURE__ */ jsx(SelectItem, {
320
+ value: status,
321
+ children: status.replace("_", " ")
322
+ }, status)) })]
323
+ })
324
+ }),
325
+ /* @__PURE__ */ jsxs(DialogFooter, {
326
+ className: "flex-wrap sm:flex-nowrap",
327
+ children: [
328
+ /* @__PURE__ */ jsx("span", {
329
+ className: "w-full text-sm text-doom-dim sm:w-auto",
330
+ children: "saved as one prompt naming only the fields you changed"
331
+ }),
332
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
333
+ /* @__PURE__ */ jsx(Button, {
334
+ variant: "outline",
335
+ size: "xs",
336
+ "data-testid": "task-detail-cancel",
337
+ onClick: onClose,
338
+ children: "cancel"
339
+ }),
340
+ /* @__PURE__ */ jsx(Button, {
341
+ variant: "primary",
342
+ size: "xs",
343
+ "data-testid": "task-detail-save",
344
+ onClick: submitEdit,
345
+ children: "save"
346
+ })
347
+ ]
348
+ })
349
+ ] }) : null,
350
+ mode === "message" && agent ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Field, {
351
+ label: `Message to ${agent}`,
352
+ children: /* @__PURE__ */ jsx(Textarea, {
353
+ "data-testid": "task-detail-message-input",
354
+ autoFocus: true,
355
+ rows: MESSAGE_ROWS,
356
+ value: message,
357
+ placeholder: "guidance for the run working on this task",
358
+ onChange: (event) => setMessage(event.target.value)
359
+ })
360
+ }), /* @__PURE__ */ jsxs(DialogFooter, {
361
+ className: "flex-wrap sm:flex-nowrap",
362
+ children: [
363
+ /* @__PURE__ */ jsx("span", {
364
+ className: "w-full text-sm text-doom-dim sm:w-auto",
365
+ children: "steers the run, it does not edit the task"
366
+ }),
367
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
368
+ /* @__PURE__ */ jsx(Button, {
369
+ variant: "outline",
370
+ size: "xs",
371
+ "data-testid": "task-detail-cancel",
372
+ onClick: onClose,
373
+ children: "cancel"
374
+ }),
375
+ /* @__PURE__ */ jsx(Button, {
376
+ variant: "primary",
377
+ size: "xs",
378
+ "data-testid": "task-detail-send",
379
+ onClick: submitMessage,
380
+ children: "send"
381
+ })
382
+ ]
383
+ })] }) : null
384
+ ] })]
385
+ })
386
+ });
387
+ }
388
+ //#endregion
389
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_lib/TasksActivitySection.tsx
390
+ const STATUS_TONE$1 = {
391
+ pending: "muted",
392
+ in_progress: "yellow",
393
+ completed: "green",
394
+ failed: "red",
395
+ deleted: "muted"
396
+ };
397
+ function TaskRow({ task, sessionId, sendSessionFrame, openDialog }) {
398
+ const detail = task.status === "in_progress" ? task.activeForm : task.description;
399
+ const agent = task.delegation?.agent;
400
+ return /* @__PURE__ */ jsxs("div", {
401
+ "data-testid": `activity-task-${task.id}`,
402
+ "data-task-status": task.status,
403
+ className: "flex flex-col gap-1 rounded-md px-1 py-1",
404
+ children: [/* @__PURE__ */ jsxs("div", {
405
+ className: "grid min-w-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-1.5",
406
+ children: [
407
+ /* @__PURE__ */ jsx(Dot, {
408
+ tone: STATUS_TONE$1[task.status],
409
+ pulse: task.status === "in_progress"
410
+ }),
411
+ /* @__PURE__ */ jsxs("button", {
412
+ type: "button",
413
+ "data-testid": `activity-task-title-${task.id}`,
414
+ title: task.subject,
415
+ onClick: () => openDialog("view"),
416
+ className: `min-h-6 min-w-0 truncate text-left text-base font-semibold hover:underline ${task.status === "failed" ? "text-doom-red" : task.status === "completed" ? "text-doom-dim" : "text-doom-hi"}`,
417
+ children: [
418
+ /* @__PURE__ */ jsxs("span", {
419
+ className: "text-doom-dim",
420
+ children: ["#", task.id]
421
+ }),
422
+ " ",
423
+ task.subject
424
+ ]
425
+ }),
426
+ /* @__PURE__ */ jsx("span", {
427
+ className: "shrink-0 text-sm text-doom-dim",
428
+ children: task.status.replace("_", " ")
429
+ }),
430
+ task.status !== "completed" && task.status !== "deleted" ? /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
431
+ asChild: true,
432
+ children: /* @__PURE__ */ jsx(Button, {
433
+ variant: "ghost",
434
+ size: "icon",
435
+ "data-testid": `activity-task-menu-${task.id}`,
436
+ "aria-label": `task #${task.id} actions`,
437
+ title: `task #${task.id} actions`,
438
+ className: "self-center shrink-0 text-doom-faint hover:bg-doom-deep hover:text-doom-hi data-[state=open]:bg-doom-deep data-[state=open]:text-doom-hi",
439
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
440
+ })
441
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
442
+ "data-testid": `activity-task-menu-list-${task.id}`,
443
+ children: [
444
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
445
+ "data-testid": `activity-task-edit-${task.id}`,
446
+ onSelect: () => openDialog("edit"),
447
+ children: [/* @__PURE__ */ jsx(EditIcon, { className: "h-3 w-3" }), "Edit"]
448
+ }),
449
+ agent ? /* @__PURE__ */ jsxs(DropdownMenuItem, {
450
+ "data-testid": `activity-task-message-${task.id}`,
451
+ onSelect: () => openDialog("message"),
452
+ children: [/* @__PURE__ */ jsx(MessageIcon, { className: "h-3 w-3" }), "Message agent"]
453
+ }) : null,
454
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
455
+ variant: "destructive",
456
+ "data-testid": `activity-task-remove-${task.id}`,
457
+ onSelect: () => requestTaskRemoval(sendSessionFrame, sessionId, task.id),
458
+ children: [/* @__PURE__ */ jsx(TrashIcon, { className: "h-3 w-3" }), "Remove"]
459
+ })
460
+ ]
461
+ })] }) : null
462
+ ]
463
+ }), detail || agent || task.blockedBy.length > 0 ? /* @__PURE__ */ jsxs("span", {
464
+ className: "truncate pl-3 text-sm text-doom-dim",
465
+ children: [
466
+ agent ? `[${agent}] ` : "",
467
+ detail ?? "",
468
+ task.blockedBy.length > 0 ? ` · blocked by ${task.blockedBy.map((id) => `#${id}`).join(", ")}` : ""
469
+ ]
470
+ }) : null]
471
+ });
472
+ }
473
+ /**
474
+ * Session task graph in the activity dock. Empty graphs have no cockpit presence.
475
+ *
476
+ * This fills the `tasks` activity group, so the dock has already drawn the
477
+ * heading, the key chip and the section frame. Rendering rows only keeps the
478
+ * group titled once.
479
+ */
480
+ function TasksActivitySection({ sessionId, sendSessionFrame }) {
481
+ const sessionTasks = useStore(tasks.store, (state) => tasks.select(state, sessionId).tasks);
482
+ const [historyOpen, setHistoryOpen] = useState(false);
483
+ const [dialog, setDialog] = useState();
484
+ if (sessionId === null || sessionTasks.length === 0) return null;
485
+ const active = sessionTasks.filter((task) => task.status === "pending" || task.status === "in_progress");
486
+ const history = sessionTasks.filter((task) => task.status === "completed" || task.status === "failed");
487
+ const dialogTask = dialog ? sessionTasks.find((task) => task.id === dialog.taskId) : void 0;
488
+ return /* @__PURE__ */ jsxs("div", {
489
+ className: "flex flex-col gap-2",
490
+ children: [
491
+ active.length > 0 ? /* @__PURE__ */ jsx("div", {
492
+ className: "flex flex-col gap-0.5",
493
+ children: active.map((task) => /* @__PURE__ */ jsx(TaskRow, {
494
+ task,
495
+ sessionId,
496
+ sendSessionFrame,
497
+ openDialog: (mode) => setDialog({
498
+ taskId: task.id,
499
+ mode
500
+ })
501
+ }, task.id))
502
+ }) : null,
503
+ history.length > 0 ? /* @__PURE__ */ jsxs("div", {
504
+ className: "flex flex-col gap-0.5",
505
+ children: [/* @__PURE__ */ jsxs(Button, {
506
+ variant: "ghost",
507
+ size: "xs",
508
+ "aria-expanded": historyOpen,
509
+ onClick: () => setHistoryOpen((open) => !open),
510
+ className: "justify-start gap-1.5 rounded-md px-1 py-0.5 hover:bg-doom-panel",
511
+ children: [
512
+ historyOpen ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 text-doom-faint" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-2.5 w-2.5 text-doom-faint" }),
513
+ /* @__PURE__ */ jsx("span", {
514
+ className: "text-base font-medium text-doom-dim",
515
+ children: "session"
516
+ }),
517
+ /* @__PURE__ */ jsx("span", {
518
+ className: "text-2xs text-doom-faint",
519
+ children: history.length
520
+ })
521
+ ]
522
+ }), historyOpen ? /* @__PURE__ */ jsx("div", {
523
+ className: "flex flex-col gap-0.5 pl-2",
524
+ children: history.map((task) => /* @__PURE__ */ jsx(TaskRow, {
525
+ task,
526
+ sessionId,
527
+ sendSessionFrame,
528
+ openDialog: (mode) => setDialog({
529
+ taskId: task.id,
530
+ mode
531
+ })
532
+ }, task.id))
533
+ }) : null]
534
+ }) : null,
535
+ dialog && dialogTask ? /* @__PURE__ */ jsx(TaskDetailDialog, {
536
+ task: dialogTask,
537
+ sessionId,
538
+ mode: dialog.mode,
539
+ send: sendSessionFrame,
540
+ onClose: () => setDialog(void 0)
541
+ }, `${dialogTask.id}-${dialog.mode}`) : null
542
+ ]
543
+ });
544
+ }
545
+ //#endregion
546
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Tasks.activity.tasks.web.tsx
547
+ var Tasks_activity_tasks_web_default = defineFill({ component: TasksActivitySection });
548
+ //#endregion
549
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/taskToolFormat.ts
550
+ const STATUS_GLYPH = {
551
+ pending: "○",
552
+ in_progress: "◐",
553
+ completed: "●",
554
+ failed: "✗",
555
+ deleted: "⊘"
556
+ };
557
+ const STATUS_LABEL = {
558
+ pending: "pending",
559
+ in_progress: "in progress",
560
+ completed: "completed",
561
+ failed: "failed",
562
+ deleted: "deleted"
563
+ };
564
+ const ACTION_GLYPH = {
565
+ upsert: "✎",
566
+ delete: "×",
567
+ get: "›",
568
+ list: "☰",
569
+ clear: "∅",
570
+ assign: "⇒",
571
+ cancel: "⊗"
572
+ };
573
+ /**
574
+ * `upsert` is two operations behind one name, so a homogeneous batch narrows to
575
+ * the glyph the operator already reads: `+` means new work appeared, `→` means
576
+ * work moved. A mixed batch keeps the neutral write glyph.
577
+ */
578
+ const UPSERT_CREATE_GLYPH = "+";
579
+ const UPSERT_UPDATE_GLYPH = "→";
580
+ /** Task rows kept in a collapsed result, before the card is expanded. */
581
+ const TRANSCRIPT_TASK_ROWS = 8;
582
+ const TASK_STATUSES = [
583
+ "pending",
584
+ "in_progress",
585
+ "completed",
586
+ "failed",
587
+ "deleted"
588
+ ];
589
+ const TASK_ACTIONS = [
590
+ "upsert",
591
+ "list",
592
+ "get",
593
+ "delete",
594
+ "clear",
595
+ "assign",
596
+ "cancel"
597
+ ];
598
+ const INLINE_WHITESPACE = /\s+/gu;
599
+ function isRecord(value) {
600
+ return typeof value === "object" && value !== null && !Array.isArray(value);
601
+ }
602
+ function isStatus(value) {
603
+ return typeof value === "string" && TASK_STATUSES.includes(value);
604
+ }
605
+ function isAction(value) {
606
+ return typeof value === "string" && TASK_ACTIONS.includes(value);
607
+ }
608
+ function taskView(value) {
609
+ if (!isRecord(value) || typeof value.id !== "number" || typeof value.subject !== "string") return null;
610
+ if (!isStatus(value.status)) return null;
611
+ const task = {
612
+ id: value.id,
613
+ subject: value.subject,
614
+ status: value.status
615
+ };
616
+ if (typeof value.activeForm === "string") task.activeForm = value.activeForm;
617
+ if (Array.isArray(value.blockedBy)) task.blockedBy = value.blockedBy.filter((id) => typeof id === "number");
618
+ if (isRecord(value.delegation)) {
619
+ task.delegation = {};
620
+ if (typeof value.delegation.agent === "string") task.delegation.agent = value.delegation.agent;
621
+ if (typeof value.delegation.state === "string") task.delegation.state = value.delegation.state;
622
+ }
623
+ return task;
624
+ }
625
+ function idList(value) {
626
+ return Array.isArray(value) ? value.filter((id) => typeof id === "number") : [];
627
+ }
628
+ /** Narrows the wire details; null when they are not a task result at all. */
629
+ function taskDetailsView(details) {
630
+ if (!isRecord(details) || !isAction(details.action) || !Array.isArray(details.tasks)) return null;
631
+ const view = {
632
+ action: details.action,
633
+ params: isRecord(details.params) ? details.params : {},
634
+ tasks: details.tasks.map(taskView).filter((task) => task !== null)
635
+ };
636
+ if (typeof details.error === "string") view.error = details.error;
637
+ if (isRecord(details.upsert)) view.upsert = {
638
+ applied: idList(details.upsert.applied),
639
+ failed: typeof details.upsert.failed === "number" ? details.upsert.failed : 0
640
+ };
641
+ if (isRecord(details.assignment)) view.assignment = {
642
+ assigned: idList(details.assignment.assigned),
643
+ failed: typeof details.assignment.failed === "number" ? details.assignment.failed : 0
644
+ };
645
+ return view;
646
+ }
647
+ /** `#id` for the wire's id, which the TUI would resolve to a subject through the store. */
648
+ function idLabel(value) {
649
+ return typeof value === "number" || typeof value === "string" ? `#${value}` : "#?";
650
+ }
651
+ /** Keep dynamic content on one line. */
652
+ function inlineText(text) {
653
+ return text.replace(INLINE_WHITESPACE, " ").trim();
654
+ }
655
+ function upsertGlyph(items) {
656
+ if (items.length === 0) return ACTION_GLYPH.upsert;
657
+ if (items.every((item) => item.id === void 0)) return UPSERT_CREATE_GLYPH;
658
+ if (items.every((item) => item.id !== void 0)) return UPSERT_UPDATE_GLYPH;
659
+ return ACTION_GLYPH.upsert;
660
+ }
661
+ /**
662
+ * The TUI resolves an id to its subject through the task store; the card has
663
+ * no store and shows `#id` until the result names the task.
664
+ */
665
+ function taskCallView(args) {
666
+ const action = typeof args.action === "string" ? args.action : "task";
667
+ if (!isAction(action)) return {
668
+ glyph: "?",
669
+ action,
670
+ subjectTone: "dim"
671
+ };
672
+ const items = action === "upsert" && Array.isArray(args.tasks) ? args.tasks.filter(isRecord) : [];
673
+ const view = {
674
+ glyph: action === "upsert" ? upsertGlyph(items) : ACTION_GLYPH[action],
675
+ action,
676
+ subjectTone: "dim"
677
+ };
678
+ if (action === "upsert") {
679
+ if (items.length === 1) {
680
+ const only = items[0] ?? {};
681
+ if (only.id === void 0) {
682
+ if (typeof only.subject === "string") view.subject = inlineText(only.subject);
683
+ } else {
684
+ view.subject = typeof only.subject === "string" ? inlineText(only.subject) : idLabel(only.id);
685
+ view.subjectTone = "accent";
686
+ }
687
+ } else if (items.length > 1) view.subject = `${items.length} tasks`;
688
+ } else if (action === "assign") {
689
+ const assignments = Array.isArray(args.assignments) ? args.assignments.filter(isRecord) : [];
690
+ if (assignments.length === 1) {
691
+ const only = assignments[0] ?? {};
692
+ view.subject = idLabel(only.id);
693
+ view.subjectTone = "accent";
694
+ if (typeof only.agent === "string") view.detail = `→ ${only.agent}`;
695
+ } else if (assignments.length > 1) view.subject = `${assignments.length} tasks`;
696
+ } else if ((action === "get" || action === "delete" || action === "cancel") && args.id !== void 0) {
697
+ view.subject = idLabel(args.id);
698
+ view.subjectTone = "accent";
699
+ } else if (action === "list" && isStatus(args.status)) view.detail = STATUS_LABEL[args.status];
700
+ return view;
701
+ }
702
+ function taskRow(task) {
703
+ const row = {
704
+ id: task.id,
705
+ glyph: STATUS_GLYPH[task.status],
706
+ subject: inlineText(task.subject),
707
+ status: task.status,
708
+ closed: task.status === "completed" || task.status === "deleted",
709
+ blockedBy: task.blockedBy ?? []
710
+ };
711
+ if (task.delegation?.agent) row.agent = inlineText(task.delegation.agent);
712
+ if (task.status === "in_progress" && task.activeForm) row.activeForm = inlineText(task.activeForm);
713
+ return row;
714
+ }
715
+ function nonEmptyLines(text) {
716
+ return text.split("\n").filter((line) => line.trim().length > 0);
717
+ }
718
+ /**
719
+ * The rows `list` reported to the model: `details.tasks` is the whole
720
+ * document, so the call's filters apply again here or the card would disagree
721
+ * with the text the model was given.
722
+ */
723
+ function listedTasks(details) {
724
+ let view = details.tasks;
725
+ if (details.params.includeDeleted !== true) view = view.filter((task) => task.status !== "deleted");
726
+ if (isStatus(details.params.status)) view = view.filter((task) => task.status === details.params.status);
727
+ return view;
728
+ }
729
+ function byIds(details, ids) {
730
+ return ids.flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);
731
+ }
732
+ function batch(tasks, failed, verb, expanded) {
733
+ if (tasks.length === 1 && failed === 0) return {
734
+ rows: [taskRow(tasks[0])],
735
+ status: null
736
+ };
737
+ const shown = expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);
738
+ let summary = `${tasks.length} ${verb}`;
739
+ if (failed > 0) summary += ` · ${failed} failed`;
740
+ if (tasks.length > shown.length) summary += ` · ${tasks.length - shown.length} more`;
741
+ return {
742
+ rows: shown.map(taskRow),
743
+ status: {
744
+ glyph: failed > 0 ? "!" : "✓",
745
+ tone: failed > 0 ? "warning" : "ok",
746
+ text: summary
747
+ }
748
+ };
749
+ }
750
+ function taskResultView(input) {
751
+ const details = taskDetailsView(input.details);
752
+ if (input.isPartial) return {
753
+ rows: [],
754
+ errorLines: [],
755
+ status: {
756
+ glyph: "◐",
757
+ tone: "running",
758
+ text: nonEmptyLines(input.output).at(-1) ?? "working"
759
+ }
760
+ };
761
+ if (input.isError || details?.error !== void 0) {
762
+ const all = nonEmptyLines(details?.error ?? input.output);
763
+ if (all.length <= 1) return {
764
+ rows: [],
765
+ errorLines: [],
766
+ status: {
767
+ glyph: "✗",
768
+ tone: "error",
769
+ text: all[0] ?? ""
770
+ }
771
+ };
772
+ return {
773
+ rows: [],
774
+ errorLines: input.expanded ? all : all.slice(0, TRANSCRIPT_TASK_ROWS),
775
+ status: {
776
+ glyph: "✗",
777
+ tone: "error",
778
+ text: ""
779
+ }
780
+ };
781
+ }
782
+ if (details === null) return {
783
+ rows: [],
784
+ errorLines: [],
785
+ status: {
786
+ glyph: "✓",
787
+ tone: "ok",
788
+ text: ""
789
+ }
790
+ };
791
+ if (details.action === "list") {
792
+ const tasks = listedTasks(details);
793
+ const shown = input.expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);
794
+ let summary = tasks.length === 0 ? "no tasks" : `${tasks.length} task${tasks.length === 1 ? "" : "s"}`;
795
+ if (tasks.length > shown.length) summary += ` · ${tasks.length - shown.length} more`;
796
+ return {
797
+ rows: shown.map(taskRow),
798
+ errorLines: [],
799
+ status: {
800
+ glyph: "✓",
801
+ tone: "ok",
802
+ text: summary
803
+ }
804
+ };
805
+ }
806
+ if (details.action === "clear") return {
807
+ rows: [],
808
+ errorLines: [],
809
+ status: {
810
+ glyph: "✓",
811
+ tone: "ok",
812
+ text: "cleared"
813
+ }
814
+ };
815
+ if (details.action === "assign" && details.assignment) return {
816
+ errorLines: [],
817
+ ...batch(byIds(details, details.assignment.assigned), details.assignment.failed, "assigned", input.expanded)
818
+ };
819
+ if (details.action === "upsert") return {
820
+ errorLines: [],
821
+ ...batch(byIds(details, details.upsert?.applied ?? []), details.upsert?.failed ?? 0, "applied", input.expanded)
822
+ };
823
+ const id = details.params.id;
824
+ const task = typeof id === "number" ? details.tasks.find((candidate) => candidate.id === id) : void 0;
825
+ if (!task) return {
826
+ rows: [],
827
+ errorLines: [],
828
+ status: {
829
+ glyph: "✓",
830
+ tone: "ok",
831
+ text: ""
832
+ }
833
+ };
834
+ return {
835
+ rows: [taskRow(task)],
836
+ errorLines: [],
837
+ status: null
838
+ };
839
+ }
840
+ //#endregion
841
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/TaskToolMessage.tsx
842
+ const STATUS_TONE = {
843
+ pending: "text-doom-dim",
844
+ in_progress: "text-doom-yellow",
845
+ completed: "text-doom-green",
846
+ failed: "text-doom-red",
847
+ deleted: "text-doom-dim"
848
+ };
849
+ const RESULT_TONE = {
850
+ running: "running",
851
+ ok: "ok",
852
+ error: "error",
853
+ warning: "running"
854
+ };
855
+ /** One task row: status glyph, id, subject and chips on the left, the status label flushed right. */
856
+ function TaskRowLine({ row }) {
857
+ return /* @__PURE__ */ jsxs("li", {
858
+ "data-testid": "tool-result-task-row",
859
+ "data-task-id": row.id,
860
+ className: "flex items-baseline gap-2",
861
+ children: [
862
+ /* @__PURE__ */ jsx("span", {
863
+ className: `shrink-0 ${STATUS_TONE[row.status]}`,
864
+ children: row.glyph
865
+ }),
866
+ /* @__PURE__ */ jsxs("span", {
867
+ className: "shrink-0 text-doom-dim",
868
+ children: ["#", row.id]
869
+ }),
870
+ /* @__PURE__ */ jsxs("span", {
871
+ className: `min-w-0 flex-1 truncate text-base font-semibold ${row.closed ? "text-doom-dim line-through" : row.status === "in_progress" ? "text-doom-blue" : "text-doom-hi"}`,
872
+ children: [
873
+ row.subject,
874
+ row.agent ? /* @__PURE__ */ jsxs("span", {
875
+ className: "text-doom-dim",
876
+ children: [
877
+ " [",
878
+ row.agent,
879
+ "]"
880
+ ]
881
+ }) : null,
882
+ row.activeForm ? /* @__PURE__ */ jsxs("span", {
883
+ className: "text-doom-dim",
884
+ children: [
885
+ " (",
886
+ row.activeForm,
887
+ ")"
888
+ ]
889
+ }) : null,
890
+ row.blockedBy.length > 0 ? /* @__PURE__ */ jsxs("span", {
891
+ className: "text-doom-dim",
892
+ children: [" ⛓ ", row.blockedBy.map((id) => `#${id}`).join(",")]
893
+ }) : null
894
+ ]
895
+ }),
896
+ /* @__PURE__ */ jsx("span", {
897
+ className: `shrink-0 ${STATUS_TONE[row.status]}`,
898
+ children: STATUS_LABEL[row.status]
899
+ })
900
+ ]
901
+ });
902
+ }
903
+ /**
904
+ * The task tool's timeline item: `glyph action subject` with the assignee or
905
+ * list filter in the header, as renderTaskCall shows it; the rows the action
906
+ * touched, then the batch or list summary, in the body, as renderTaskResult
907
+ * lays it out.
908
+ */
909
+ function TaskToolMessage({ args, result, output, running, isError }) {
910
+ const call = taskCallView(args);
911
+ const collapsed = taskResultView({
912
+ details: result?.details,
913
+ output,
914
+ expanded: false,
915
+ isPartial: running,
916
+ isError
917
+ });
918
+ const full = taskResultView({
919
+ details: result?.details,
920
+ output,
921
+ expanded: true,
922
+ isPartial: running,
923
+ isError
924
+ });
925
+ const hidden = full.rows.length - collapsed.rows.length + (full.errorLines.length - collapsed.errorLines.length);
926
+ return /* @__PURE__ */ jsx(MessageItem, {
927
+ tone: toolTone({
928
+ running,
929
+ isError
930
+ }),
931
+ expandable: hidden > 0,
932
+ children: ({ expanded }) => {
933
+ const view = expanded ? full : collapsed;
934
+ const empty = view.rows.length === 0 && view.errorLines.length === 0 && view.status === null;
935
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MessageItemHeader, {
936
+ title: "task",
937
+ children: /* @__PURE__ */ jsxs("span", {
938
+ "data-testid": "tool-call-task",
939
+ className: "flex min-w-0 flex-1 items-center gap-1.5",
940
+ children: [
941
+ /* @__PURE__ */ jsxs("span", {
942
+ className: "shrink-0 text-base font-semibold text-doom-text",
943
+ children: [
944
+ call.glyph,
945
+ " ",
946
+ call.action
947
+ ]
948
+ }),
949
+ call.subject ? /* @__PURE__ */ jsx("span", {
950
+ className: `min-w-0 truncate text-base font-semibold ${call.subjectTone === "accent" ? "text-doom-blue" : "text-doom-hi"}`,
951
+ children: call.subject
952
+ }) : null,
953
+ call.detail ? /* @__PURE__ */ jsx("span", {
954
+ className: "shrink-0 text-doom-dim",
955
+ children: call.detail
956
+ }) : null
957
+ ]
958
+ })
959
+ }), empty ? null : /* @__PURE__ */ jsxs(MessageItemBody, {
960
+ "data-testid": "tool-result-task",
961
+ className: "flex flex-col gap-1",
962
+ children: [
963
+ view.rows.length > 0 ? /* @__PURE__ */ jsx("ul", {
964
+ className: "flex flex-col gap-0.5",
965
+ children: view.rows.map((row) => /* @__PURE__ */ jsx(TaskRowLine, { row }, row.id))
966
+ }) : null,
967
+ view.errorLines.length > 0 ? /* @__PURE__ */ jsx("pre", {
968
+ className: "whitespace-pre-wrap break-words font-mono text-doom-dim",
969
+ children: view.errorLines.join("\n")
970
+ }) : null,
971
+ !expanded && hidden > 0 ? /* @__PURE__ */ jsxs(MessageItemStatus, {
972
+ expands: true,
973
+ children: [hidden, " more"]
974
+ }) : null,
975
+ view.status ? /* @__PURE__ */ jsx(MessageItemStatus, {
976
+ "data-testid": "tool-result-task-status",
977
+ tone: RESULT_TONE[view.status.tone],
978
+ glyph: view.status.glyph,
979
+ children: view.status.text
980
+ }) : null
981
+ ]
982
+ })] });
983
+ }
984
+ });
985
+ }
986
+ //#endregion
987
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/task.web.tsx
988
+ var task_web_default = defineToolRenderer({ message: TaskToolMessage });
989
+ //#endregion
990
+ //#region generated/web.ts
991
+ const via = (identity, value) => value === void 0 ? void 0 : {
992
+ ...identity,
993
+ ...value
994
+ };
995
+ const webPlugin = defineWebPlugin({
996
+ id: "task",
997
+ session: {
998
+ get activityGroups() {
999
+ return [via({ name: "tasks" }, tasks_web_default)];
1000
+ },
1001
+ get channels() {
1002
+ return [via({ channel: "task_graph" }, task_graph_web_default)];
1003
+ },
1004
+ get fills() {
1005
+ return [via({
1006
+ slot: "activity.tasks",
1007
+ id: "tasks"
1008
+ }, Tasks_activity_tasks_web_default)];
1009
+ },
1010
+ get toolRenderers() {
1011
+ return [via({ tools: ["task"] }, task_web_default)];
1012
+ }
1013
+ }
1014
+ });
1015
+ //#endregion
1016
+ export { webPlugin };
1017
+
1018
+ //# sourceMappingURL=web.mjs.map