@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.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/apiContracts.cjs +54 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +49 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/config-CFSs3iku.mjs +72 -0
- package/dist/config-CFSs3iku.mjs.map +1 -0
- package/dist/config-CdX-Bv8q.cjs +173 -0
- package/dist/config-CdX-Bv8q.cjs.map +1 -0
- package/dist/config.cjs +19 -0
- package/dist/config.d.mts +2 -0
- package/dist/config.mjs +2 -0
- package/dist/delegation-BiOy3_7g.mjs +7 -0
- package/dist/delegation-BiOy3_7g.mjs.map +1 -0
- package/dist/delegation-CdMqOZB4.mjs +626 -0
- package/dist/delegation-CdMqOZB4.mjs.map +1 -0
- package/dist/delegation-DsnKjMbJ.cjs +661 -0
- package/dist/delegation-DsnKjMbJ.cjs.map +1 -0
- package/dist/delegation-LDpHP2CC.cjs +12 -0
- package/dist/delegation-LDpHP2CC.cjs.map +1 -0
- package/dist/delegationManager.cjs +10 -0
- package/dist/delegationManager.d.mts +2 -0
- package/dist/delegationManager.mjs +3 -0
- package/dist/extensions/mcp-ui.mjs +23 -0
- package/dist/extensions/mcp-ui.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +1144 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +1115 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/native.mjs +1175 -0
- package/dist/extensions/native.mjs.map +1 -0
- package/dist/extensions/server.cjs +569 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +563 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +1018 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index-B-3sCF2S.d.mts +43 -0
- package/dist/index-B-3sCF2S.d.mts.map +1 -0
- package/dist/index-B1JPJdpZ.d.mts +51 -0
- package/dist/index-B1JPJdpZ.d.mts.map +1 -0
- package/dist/index-CUWCvh-E.d.mts +72 -0
- package/dist/index-CUWCvh-E.d.mts.map +1 -0
- package/dist/index-DlmXKyhQ.d.mts +34 -0
- package/dist/index-DlmXKyhQ.d.mts.map +1 -0
- package/dist/index-Dvb8X_qB.d.mts +67 -0
- package/dist/index-Dvb8X_qB.d.mts.map +1 -0
- package/dist/index-XY9VPmfm.d.mts +26 -0
- package/dist/index-XY9VPmfm.d.mts.map +1 -0
- package/dist/index-teIf67qQ.d.mts +196 -0
- package/dist/index-teIf67qQ.d.mts.map +1 -0
- package/dist/index.cjs +93 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.mjs +12 -0
- package/dist/invariants-DhjEjBPU.cjs +79 -0
- package/dist/invariants-DhjEjBPU.cjs.map +1 -0
- package/dist/invariants-q_UamF7D.mjs +62 -0
- package/dist/invariants-q_UamF7D.mjs.map +1 -0
- package/dist/logSinkTelemetry-B23qGiP-.cjs +52 -0
- package/dist/logSinkTelemetry-B23qGiP-.cjs.map +1 -0
- package/dist/logSinkTelemetry-BMBsC54p.mjs +41 -0
- package/dist/logSinkTelemetry-BMBsC54p.mjs.map +1 -0
- package/dist/logSinkTelemetry.cjs +6 -0
- package/dist/logSinkTelemetry.d.mts +23 -0
- package/dist/logSinkTelemetry.d.mts.map +1 -0
- package/dist/logSinkTelemetry.mjs +3 -0
- package/dist/paths-CSya2l9_.cjs +308 -0
- package/dist/paths-CSya2l9_.cjs.map +1 -0
- package/dist/paths-Cah1vFGv.mjs +207 -0
- package/dist/paths-Cah1vFGv.mjs.map +1 -0
- package/dist/processLiveness-Ccd9wenT.mjs +20 -0
- package/dist/processLiveness-Ccd9wenT.mjs.map +1 -0
- package/dist/processLiveness-umpNgsPR.cjs +25 -0
- package/dist/processLiveness-umpNgsPR.cjs.map +1 -0
- package/dist/reconcile-BcD-hTbd.mjs +63 -0
- package/dist/reconcile-BcD-hTbd.mjs.map +1 -0
- package/dist/reconcile-cRL0NjDr.cjs +80 -0
- package/dist/reconcile-cRL0NjDr.cjs.map +1 -0
- package/dist/reducer-BEfJFM5L.mjs +345 -0
- package/dist/reducer-BEfJFM5L.mjs.map +1 -0
- package/dist/reducer-BV0YIZ2o.cjs +380 -0
- package/dist/reducer-BV0YIZ2o.cjs.map +1 -0
- package/dist/storeInvariants.cjs +5 -0
- package/dist/storeInvariants.d.mts +23 -0
- package/dist/storeInvariants.d.mts.map +1 -0
- package/dist/storeInvariants.mjs +2 -0
- package/dist/storePaths.cjs +14 -0
- package/dist/storePaths.d.mts +2 -0
- package/dist/storePaths.mjs +2 -0
- package/dist/storeProcessLiveness.cjs +3 -0
- package/dist/storeProcessLiveness.d.mts +11 -0
- package/dist/storeProcessLiveness.d.mts.map +1 -0
- package/dist/storeProcessLiveness.mjs +2 -0
- package/dist/storeReconcile.cjs +5 -0
- package/dist/storeReconcile.d.mts +2 -0
- package/dist/storeReconcile.mjs +2 -0
- package/dist/storeReducer.cjs +8 -0
- package/dist/storeReducer.d.mts +2 -0
- package/dist/storeReducer.mjs +2 -0
- package/dist/storeTaskGraph.cjs +7 -0
- package/dist/storeTaskGraph.d.mts +2 -0
- package/dist/storeTaskGraph.mjs +2 -0
- package/dist/storeTaskStore.cjs +3 -0
- package/dist/storeTaskStore.d.mts +2 -0
- package/dist/storeTaskStore.mjs +2 -0
- package/dist/storeTypes.cjs +6 -0
- package/dist/storeTypes.d.mts +2 -0
- package/dist/storeTypes.mjs +2 -0
- package/dist/task-CXF9VULl.cjs +49 -0
- package/dist/task-CXF9VULl.cjs.map +1 -0
- package/dist/task-DJpTteeJ.d.mts +187 -0
- package/dist/task-DJpTteeJ.d.mts.map +1 -0
- package/dist/task-DO0TwZ0z.mjs +132 -0
- package/dist/task-DO0TwZ0z.mjs.map +1 -0
- package/dist/task-Dhta7JyA.d.mts +126 -0
- package/dist/task-Dhta7JyA.d.mts.map +1 -0
- package/dist/task-UvW877Ez.cjs +227 -0
- package/dist/task-UvW877Ez.cjs.map +1 -0
- package/dist/task-giCbpKzV.mjs +26 -0
- package/dist/task-giCbpKzV.mjs.map +1 -0
- package/dist/taskGraph-BOQvT0-M.mjs +63 -0
- package/dist/taskGraph-BOQvT0-M.mjs.map +1 -0
- package/dist/taskGraph-BskRslrr.cjs +92 -0
- package/dist/taskGraph-BskRslrr.cjs.map +1 -0
- package/dist/taskGraph-D0zFHF2p.d.mts +25 -0
- package/dist/taskGraph-D0zFHF2p.d.mts.map +1 -0
- package/dist/taskResult-D2G7b4w-.cjs +224 -0
- package/dist/taskResult-D2G7b4w-.cjs.map +1 -0
- package/dist/taskResult-edBAAKSH.mjs +165 -0
- package/dist/taskResult-edBAAKSH.mjs.map +1 -0
- package/dist/taskStore-DjU60rMm.cjs +215 -0
- package/dist/taskStore-DjU60rMm.cjs.map +1 -0
- package/dist/taskStore-pAJ971Ml.mjs +208 -0
- package/dist/taskStore-pAJ971Ml.mjs.map +1 -0
- package/dist/telemetry-Ca0I3Tqo.cjs +30 -0
- package/dist/telemetry-Ca0I3Tqo.cjs.map +1 -0
- package/dist/telemetry-DyS44zAD.mjs +25 -0
- package/dist/telemetry-DyS44zAD.mjs.map +1 -0
- package/dist/telemetry-FMGRovb4.d.mts +35 -0
- package/dist/telemetry-FMGRovb4.d.mts.map +1 -0
- package/dist/toolPromptGuidelines.cjs +27 -0
- package/dist/toolPromptGuidelines.cjs.map +1 -0
- package/dist/toolPromptGuidelines.d.mts +13 -0
- package/dist/toolPromptGuidelines.d.mts.map +1 -0
- package/dist/toolPromptGuidelines.mjs +26 -0
- package/dist/toolPromptGuidelines.mjs.map +1 -0
- package/dist/toolResponseEnvelope.cjs +12 -0
- package/dist/toolResponseEnvelope.d.mts +2 -0
- package/dist/toolResponseEnvelope.mjs +2 -0
- package/dist/toolSchema.cjs +18 -0
- package/dist/toolSchema.d.mts +2 -0
- package/dist/toolSchema.mjs +2 -0
- package/package.json +223 -0
- 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
|