@marcoscale98/pi-extensible-workflows 5.17.0-fork.1 → 5.19.1-fork.1
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/CHANGELOG.md +68 -0
- package/dist/src/agent-execution.js +81 -39
- package/dist/src/background-widget.d.ts +5 -0
- package/dist/src/background-widget.js +1 -1
- package/dist/src/decoders.js +26 -0
- package/dist/src/execution.d.ts +4 -2
- package/dist/src/execution.js +43 -2
- package/dist/src/host-delivery.js +1 -0
- package/dist/src/host-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +5 -0
- package/dist/src/host-view.js +29 -5
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +190 -37
- package/dist/src/index.js +495 -93
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/registry.js +1 -1
- package/dist/src/roles.js +4 -3
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +13 -0
- package/dist/src/settings.js +27 -3
- package/dist/src/trajectory.d.ts +12 -2
- package/dist/src/trajectory.js +30 -8
- package/dist/src/types.d.ts +30 -2
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +19 -0
- package/dist/src/utils.js +30 -0
- package/dist/src/validation.d.ts +6 -1
- package/dist/src/validation.js +21 -1
- package/dist/starter/index.js +0 -2
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +0 -2
- package/dist/subagents/index.js +267 -159
- package/dist/subagents/index.js.map +2 -2
- package/dist/subagents/src/contracts.d.ts +45 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +13 -6
- package/dist/subagents/src/manager.js +3 -3
- package/dist/subagents/src/view.d.ts +1 -0
- package/dist/subagents/src/view.js +13 -52
- package/dist/trajectory/assets/index.html +27 -5
- package/dist/trajectory/index.js +76 -28
- package/dist/trajectory/index.js.map +2 -2
- package/dist/trajectory/src/assets/index.html +27 -5
- package/dist/trajectory/src/index.js +8 -18
- package/dist/trajectory/src/server.js +4 -2
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +6 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +77 -41
- package/src/background-widget.ts +1 -1
- package/src/decoders.ts +20 -0
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +23 -5
- package/src/host.ts +173 -35
- package/src/paths.ts +3 -0
- package/src/registry.ts +1 -1
- package/src/roles.ts +4 -3
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +22 -4
- package/src/trajectory.ts +30 -11
- package/src/types.ts +11 -4
- package/src/utils.ts +28 -0
- package/src/validation.ts +19 -1
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +2 -2
- package/subagents/src/contracts.ts +18 -7
- package/subagents/src/index.ts +16 -6
- package/subagents/src/manager.ts +3 -3
- package/subagents/src/view.ts +13 -50
- package/trajectory/src/assets/index.html +27 -5
- package/trajectory/src/index.ts +6 -9
- package/trajectory/src/server.ts +2 -2
|
@@ -24,6 +24,7 @@ export declare const SUBAGENTS_RUN_PARAMETERS: Type.TObject<{
|
|
|
24
24
|
}>;
|
|
25
25
|
export declare const SUBAGENTS_INSPECT_PARAMETERS: Type.TObject<{
|
|
26
26
|
id: Type.TOptional<Type.TString>;
|
|
27
|
+
scope: Type.TOptional<Type.TUnion<[Type.TLiteral<"session">, Type.TLiteral<"all">]>>;
|
|
27
28
|
}>;
|
|
28
29
|
export declare const SUBAGENTS_ID_PARAMETERS: Type.TObject<{
|
|
29
30
|
id: Type.TString;
|
|
@@ -38,36 +39,51 @@ export declare const SUBAGENTS_STOP_PARAMETERS: Type.TObject<{
|
|
|
38
39
|
export declare const SUBAGENTS_RETRY_PARAMETERS: Type.TObject<{
|
|
39
40
|
id: Type.TString;
|
|
40
41
|
}>;
|
|
41
|
-
export declare const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
timeoutMs: Type.TOptional<Type.TUnion<[Type.TInteger, Type.TNull]>>;
|
|
56
|
-
}>;
|
|
57
|
-
readonly subagents_inspect: Type.TObject<{
|
|
58
|
-
id: Type.TOptional<Type.TString>;
|
|
59
|
-
}>;
|
|
60
|
-
readonly subagents_steer: Type.TObject<{
|
|
61
|
-
id: Type.TString;
|
|
42
|
+
export declare const SUBAGENT_STATUS_OUTPUT: Type.TObject<{
|
|
43
|
+
id: Type.TString;
|
|
44
|
+
state: Type.TString;
|
|
45
|
+
sessionId: Type.TOptional<Type.TString>;
|
|
46
|
+
startedAt: Type.TOptional<Type.TNumber>;
|
|
47
|
+
finishedAt: Type.TOptional<Type.TNumber>;
|
|
48
|
+
attempts: Type.TOptional<Type.TNumber>;
|
|
49
|
+
attemptDetails: Type.TOptional<Type.TArray<Type.TUnknown>>;
|
|
50
|
+
worktree: Type.TOptional<Type.TObject<{
|
|
51
|
+
path: Type.TString;
|
|
52
|
+
branch: Type.TString;
|
|
53
|
+
}>>;
|
|
54
|
+
error: Type.TOptional<Type.TObject<{
|
|
55
|
+
code: Type.TString;
|
|
62
56
|
message: Type.TString;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
}>>;
|
|
58
|
+
progress: Type.TOptional<Type.TUnknown>;
|
|
59
|
+
value: Type.TOptional<Type.TUnknown>;
|
|
60
|
+
}>;
|
|
61
|
+
export declare const SUBAGENTS_INSPECT_OUTPUT: Type.TUnion<[Type.TObject<{
|
|
62
|
+
id: Type.TString;
|
|
63
|
+
state: Type.TString;
|
|
64
|
+
sessionId: Type.TOptional<Type.TString>;
|
|
65
|
+
startedAt: Type.TOptional<Type.TNumber>;
|
|
66
|
+
finishedAt: Type.TOptional<Type.TNumber>;
|
|
67
|
+
attempts: Type.TOptional<Type.TNumber>;
|
|
68
|
+
attemptDetails: Type.TOptional<Type.TArray<Type.TUnknown>>;
|
|
69
|
+
worktree: Type.TOptional<Type.TObject<{
|
|
70
|
+
path: Type.TString;
|
|
71
|
+
branch: Type.TString;
|
|
72
|
+
}>>;
|
|
73
|
+
error: Type.TOptional<Type.TObject<{
|
|
74
|
+
code: Type.TString;
|
|
75
|
+
message: Type.TString;
|
|
76
|
+
}>>;
|
|
77
|
+
progress: Type.TOptional<Type.TUnknown>;
|
|
78
|
+
value: Type.TOptional<Type.TUnknown>;
|
|
79
|
+
}>, Type.TArray<Type.TObject<{
|
|
80
|
+
id: Type.TString;
|
|
81
|
+
state: Type.TString;
|
|
82
|
+
}>>]>;
|
|
83
|
+
export declare const SUBAGENTS_STEER_OUTPUT: Type.TObject<{
|
|
84
|
+
id: Type.TString;
|
|
85
|
+
accepted: Type.TBoolean;
|
|
86
|
+
}>;
|
|
71
87
|
export type SubagentRunRequest = Static<typeof SUBAGENTS_RUN_PARAMETERS>;
|
|
72
88
|
export declare function normalizeSubagentRunRequest(value: unknown): SubagentRunRequest;
|
|
73
89
|
export type SubagentInspectRequest = Static<typeof SUBAGENTS_INSPECT_PARAMETERS>;
|
|
@@ -33,6 +33,7 @@ export const SUBAGENTS_RUN_PARAMETERS = Type.Object({
|
|
|
33
33
|
}, { additionalProperties: false });
|
|
34
34
|
export const SUBAGENTS_INSPECT_PARAMETERS = Type.Object({
|
|
35
35
|
id: Type.Optional(Type.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
36
|
+
scope: Type.Optional(Type.Union([Type.Literal("session"), Type.Literal("all")], { description: "List scope when id is omitted: session (default) lists runs from the current Pi session; all lists every stored run" })),
|
|
36
37
|
}, { additionalProperties: false });
|
|
37
38
|
export const SUBAGENTS_ID_PARAMETERS = Type.Object({
|
|
38
39
|
id: Type.String({ description: "Subagent ID" }),
|
|
@@ -47,13 +48,23 @@ export const SUBAGENTS_STOP_PARAMETERS = Type.Object({
|
|
|
47
48
|
export const SUBAGENTS_RETRY_PARAMETERS = Type.Object({
|
|
48
49
|
id: Type.String({ description: "Failed or stopped subagent ID to retry" }),
|
|
49
50
|
}, { additionalProperties: false });
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
// Structured results codemode scripts receive instead of the JSON text; nested records stay loose so they cannot drift from persistence.
|
|
52
|
+
export const SUBAGENT_STATUS_OUTPUT = Type.Object({
|
|
53
|
+
id: Type.String(),
|
|
54
|
+
state: Type.String(),
|
|
55
|
+
sessionId: Type.Optional(Type.String()),
|
|
56
|
+
startedAt: Type.Optional(Type.Number()),
|
|
57
|
+
finishedAt: Type.Optional(Type.Number()),
|
|
58
|
+
attempts: Type.Optional(Type.Number()),
|
|
59
|
+
attemptDetails: Type.Optional(Type.Array(Type.Unknown())),
|
|
60
|
+
worktree: Type.Optional(Type.Object({ path: Type.String(), branch: Type.String() })),
|
|
61
|
+
error: Type.Optional(Type.Object({ code: Type.String(), message: Type.String() })),
|
|
62
|
+
progress: Type.Optional(Type.Unknown()),
|
|
63
|
+
value: Type.Optional(Type.Unknown()),
|
|
64
|
+
});
|
|
65
|
+
// One run when id is given, otherwise the ordered run summaries; the summaries stay short in the codemode description.
|
|
66
|
+
export const SUBAGENTS_INSPECT_OUTPUT = Type.Union([SUBAGENT_STATUS_OUTPUT, Type.Array(Type.Object({ id: Type.String(), state: Type.String() }))]);
|
|
67
|
+
export const SUBAGENTS_STEER_OUTPUT = Type.Object({ id: Type.String(), accepted: Type.Boolean() });
|
|
57
68
|
export function normalizeSubagentRunRequest(value) {
|
|
58
69
|
if (!Value.Check(SUBAGENTS_RUN_PARAMETERS, value))
|
|
59
70
|
throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
|
|
@@ -5,7 +5,7 @@ export { attemptValue, statusValue, subagentErrorValue } from "./decode.js";
|
|
|
5
5
|
export { createSubagentManager, createUnavailableSubagentManager } from "./manager.js";
|
|
6
6
|
export { createRunStoreWorktreeAdapter, defaultWorktreeHome } from "./worktree.js";
|
|
7
7
|
export type { SubagentWorktreeAdapter, SubagentWorktreeContext, SubagentWorktreeHandle, SubagentWorktreeRunStore } from "./worktree.js";
|
|
8
|
-
type SubagentsExtensionAPI = Pick<ExtensionAPI, "registerTool"> & Partial<Pick<ExtensionAPI, "getActiveTools" | "on" | "sendMessage" | "registerCommand" | "appendEntry" | "registerEntryRenderer">>;
|
|
8
|
+
type SubagentsExtensionAPI = Pick<ExtensionAPI, "registerTool"> & Partial<Pick<ExtensionAPI, "getActiveTools" | "getAllTools" | "on" | "sendMessage" | "registerCommand" | "appendEntry" | "registerEntryRenderer">>;
|
|
9
9
|
export declare function createSubagentTools(manager: SubagentManager): readonly ToolDefinition[];
|
|
10
10
|
export declare function createSubagentsExtension(options?: SubagentsExtensionOptions, activeTools?: () => readonly string[], notify?: (notification: SubagentNotification) => void | Promise<void>, onStatus?: (status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>) => void, onResourceWarning?: (message: string) => void): SubagentsExtension;
|
|
11
11
|
export declare function registerSubagentsExtension(pi: SubagentsExtensionAPI, options?: SubagentsExtensionOptions): SubagentsExtension;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { defineTool, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
|
-
import { WorkflowError, loadingRegistry } from "../../src/index.js";
|
|
4
|
+
import { WorkflowError, loadCodemodeToolsSetting, loadingRegistry, reachableTools, workflowToolExposure } from "../../src/index.js";
|
|
5
5
|
import { clearSubagentManager, setSubagentManager } from "../../src/subagent-manager-handle.js";
|
|
6
6
|
import { createSubagentManager } from "./manager.js";
|
|
7
7
|
import { registerSubagentNavigator } from "./navigator.js";
|
|
8
8
|
import { createSubagentBackgroundWidget, renderSubagentCall, renderSubagentControlCall, renderSubagentControlResult, renderSubagentInspectCall, renderSubagentInspectResult, renderSubagentResult } from "./view.js";
|
|
9
|
-
import { normalizeSubagentRunRequest, SUBAGENTS_ID_PARAMETERS, SUBAGENTS_INSPECT_PARAMETERS, SUBAGENTS_RETRY_PARAMETERS, SUBAGENTS_RUN_PARAMETERS, SUBAGENTS_STEER_PARAMETERS, SUBAGENTS_STOP_PARAMETERS, } from "./contracts.js";
|
|
9
|
+
import { normalizeSubagentRunRequest, SUBAGENT_STATUS_OUTPUT, SUBAGENTS_ID_PARAMETERS, SUBAGENTS_INSPECT_OUTPUT, SUBAGENTS_INSPECT_PARAMETERS, SUBAGENTS_RETRY_PARAMETERS, SUBAGENTS_RUN_PARAMETERS, SUBAGENTS_STEER_OUTPUT, SUBAGENTS_STEER_PARAMETERS, SUBAGENTS_STOP_PARAMETERS, } from "./contracts.js";
|
|
10
10
|
export * from "./contracts.js";
|
|
11
11
|
export { attemptValue, statusValue, subagentErrorValue } from "./decode.js";
|
|
12
12
|
export { createSubagentManager, createUnavailableSubagentManager } from "./manager.js";
|
|
@@ -41,7 +41,7 @@ function serialize(value) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
function toolResult(value) {
|
|
44
|
-
return { content: [{ type: "text", text: serialize(value) }], details: value };
|
|
44
|
+
return { content: [{ type: "text", text: serialize(value) }], details: value, structuredContent: value };
|
|
45
45
|
}
|
|
46
46
|
function managerContext(toolCallId, signal, onUpdate, context) {
|
|
47
47
|
return {
|
|
@@ -63,6 +63,7 @@ export function createSubagentTools(manager) {
|
|
|
63
63
|
"Do not poll a running ID; use subagents_inspect({ id }) when you need its current status or terminal result.",
|
|
64
64
|
],
|
|
65
65
|
parameters: SUBAGENTS_RUN_PARAMETERS,
|
|
66
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
66
67
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
67
68
|
return toolResult(await manager.run(validateSubagentRunRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
|
|
68
69
|
},
|
|
@@ -72,8 +73,9 @@ export function createSubagentTools(manager) {
|
|
|
72
73
|
defineTool({
|
|
73
74
|
name: "subagents_inspect",
|
|
74
75
|
label: "Subagents Inspect",
|
|
75
|
-
description: "Inspect durable subagent runs. Omit id for ordered run summaries, or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
76
|
+
description: "Inspect durable subagent runs. Omit id for ordered run summaries of the current session (scope all for every session), or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
|
|
76
77
|
parameters: SUBAGENTS_INSPECT_PARAMETERS,
|
|
78
|
+
outputSchema: SUBAGENTS_INSPECT_OUTPUT,
|
|
77
79
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
78
80
|
return toolResult(await manager.inspect(validateSubagentInspectRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
|
|
79
81
|
},
|
|
@@ -85,6 +87,7 @@ export function createSubagentTools(manager) {
|
|
|
85
87
|
label: "Subagents Steer",
|
|
86
88
|
description: "Send a message to a running subagent. The message is queued safely if its steering handler is not ready; settled runs cannot be steered.",
|
|
87
89
|
parameters: SUBAGENTS_STEER_PARAMETERS,
|
|
90
|
+
outputSchema: SUBAGENTS_STEER_OUTPUT,
|
|
88
91
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
89
92
|
return toolResult(await manager.steer(validateSubagentSteerRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
|
|
90
93
|
},
|
|
@@ -96,6 +99,7 @@ export function createSubagentTools(manager) {
|
|
|
96
99
|
label: "Subagents Stop",
|
|
97
100
|
description: "Stop one running subagent, abort its active session, persist state \"stopped\", and clean its worktree without affecting sibling runs.",
|
|
98
101
|
parameters: SUBAGENTS_STOP_PARAMETERS,
|
|
102
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
99
103
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
100
104
|
return toolResult(await manager.stop(validateSubagentIdRequest(params, "subagents_stop"), managerContext(toolCallId, signal, onUpdate, context)));
|
|
101
105
|
},
|
|
@@ -107,6 +111,7 @@ export function createSubagentTools(manager) {
|
|
|
107
111
|
label: "Subagents Retry",
|
|
108
112
|
description: "Start a fresh run from a failed or stopped subagent's persisted request. The new run gets a new ID and preserves its original background or foreground mode.",
|
|
109
113
|
parameters: SUBAGENTS_RETRY_PARAMETERS,
|
|
114
|
+
outputSchema: SUBAGENT_STATUS_OUTPUT,
|
|
110
115
|
async execute(toolCallId, params, signal, onUpdate, context) {
|
|
111
116
|
return toolResult(await manager.retry(validateSubagentIdRequest(params, "subagents_retry"), managerContext(toolCallId, signal, onUpdate, context)));
|
|
112
117
|
},
|
|
@@ -145,7 +150,8 @@ export function createSubagentsExtension(options = {}, activeTools, notify, onSt
|
|
|
145
150
|
}
|
|
146
151
|
export function registerSubagentsExtension(pi, options = {}) {
|
|
147
152
|
const getActiveTools = pi.getActiveTools;
|
|
148
|
-
const
|
|
153
|
+
const getAllTools = pi.getAllTools;
|
|
154
|
+
const activeTools = getActiveTools === undefined ? undefined : () => reachableTools({ getActiveTools: () => getActiveTools.call(pi), ...(getAllTools === undefined ? {} : { getAllTools: () => getAllTools.call(pi) }) });
|
|
149
155
|
const sendMessage = pi.sendMessage;
|
|
150
156
|
const notify = sendMessage === undefined ? undefined : (notification) => {
|
|
151
157
|
sendMessage.call(pi, { customType: "subagents", content: notificationContent(notification), display: true, details: notification }, { deliverAs: "steer", triggerTurn: true });
|
|
@@ -157,8 +163,9 @@ export function registerSubagentsExtension(pi, options = {}) {
|
|
|
157
163
|
const widget = createSubagentBackgroundWidget({ ...(appendEntry === undefined ? {} : { appendEntry: (customType, data) => { appendEntry.call(pi, customType, data); } }), ...(pi.registerEntryRenderer === undefined ? {} : { registerEntryRenderer: pi.registerEntryRenderer.bind(pi) }) });
|
|
158
164
|
const extension = createSubagentsExtension(options, activeTools, notify, (status, request) => { widget.update(status, request); const registry = loadingRegistry(); if (typeof registry.observeSubagentStatus === "function")
|
|
159
165
|
registry.observeSubagentStatus(status, request); }, onResourceWarning);
|
|
166
|
+
const codemodeTools = loadCodemodeToolsSetting(options.managerDependencies?.agentDir);
|
|
160
167
|
for (const tool of extension.tools)
|
|
161
|
-
pi.registerTool(tool);
|
|
168
|
+
pi.registerTool({ ...tool, ...workflowToolExposure(tool.name, codemodeTools) });
|
|
162
169
|
if (pi.registerCommand !== undefined)
|
|
163
170
|
registerSubagentNavigator(pi.registerCommand.bind(pi), extension.manager, storageDirectory(options), options.clipboard);
|
|
164
171
|
if (pi.on !== undefined) {
|
|
@@ -1156,9 +1156,9 @@ class PersistentSubagentManager {
|
|
|
1156
1156
|
}
|
|
1157
1157
|
return inspection;
|
|
1158
1158
|
}
|
|
1159
|
-
return this.inspectList();
|
|
1159
|
+
return this.inspectList(request.scope === "all" ? undefined : context.extensionContext.sessionManager.getSessionId());
|
|
1160
1160
|
}
|
|
1161
|
-
async inspectList() {
|
|
1161
|
+
async inspectList(sessionId) {
|
|
1162
1162
|
const root = storageDirectory(this.dependencies);
|
|
1163
1163
|
await secureDirectory(root);
|
|
1164
1164
|
const entries = await readdir(root, { withFileTypes: true });
|
|
@@ -1172,7 +1172,7 @@ class PersistentSubagentManager {
|
|
|
1172
1172
|
try {
|
|
1173
1173
|
const active = this.activeRuns.get(entry.name);
|
|
1174
1174
|
const status = publicStatus(active ? persistedStatus(active) : this.terminalSummaries.get(entry.name) ?? await loadPersistedStatus(root, entry.name, false));
|
|
1175
|
-
if (status.startedAt === undefined)
|
|
1175
|
+
if (status.startedAt === undefined || (sessionId !== undefined && status.sessionId !== sessionId))
|
|
1176
1176
|
continue;
|
|
1177
1177
|
statuses.push({ status, startedAt: status.startedAt });
|
|
1178
1178
|
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
|
-
import { formatCost, formatStalledDuration, sanitizeDisplayText, WORKFLOW_AGENT_STALL_THRESHOLD_MS } from "../../src/index.js";
|
|
3
|
-
import {
|
|
2
|
+
import { formatCost, formatStalledDuration, formatWorkflowRuntime, sanitizeDisplayText, WORKFLOW_AGENT_STALL_THRESHOLD_MS } from "../../src/index.js";
|
|
3
|
+
import { agentActivityLabel, stalledDuration, textBlock } from "../../src/host-view.js";
|
|
4
|
+
import { drawFrame, formatElapsed, formatTokens, mark, QUIET_MS, REPAINT_MS } from "../../src/background-widget.js";
|
|
4
5
|
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
5
6
|
const TERMINAL_STATES = new Set(["completed", "failed", "stopped"]);
|
|
6
7
|
const MAX_INSPECT_VALUE_CHARS = 4000;
|
|
7
8
|
const MAX_INSPECT_TOOL_CALLS = 32;
|
|
8
9
|
const MAX_BACKGROUND_WIDGET_ROWS = 10;
|
|
9
|
-
function textBlock(text) {
|
|
10
|
-
return {
|
|
11
|
-
render(width) { return text.split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…")); },
|
|
12
|
-
invalidate() { },
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
10
|
function roleName(value) {
|
|
16
11
|
if (typeof value === "string")
|
|
17
12
|
return value.trim() || undefined;
|
|
@@ -60,20 +55,6 @@ function statusValue(value) {
|
|
|
60
55
|
return undefined;
|
|
61
56
|
return value;
|
|
62
57
|
}
|
|
63
|
-
function runtime(startedAt, finishedAt, now) {
|
|
64
|
-
if (startedAt === undefined)
|
|
65
|
-
return "";
|
|
66
|
-
const seconds = Math.max(0, Math.floor(((finishedAt ?? now) - startedAt) / 1000));
|
|
67
|
-
if (seconds < 60)
|
|
68
|
-
return `${String(seconds)}s`;
|
|
69
|
-
const minutes = Math.floor(seconds / 60);
|
|
70
|
-
const remainingSeconds = seconds % 60;
|
|
71
|
-
if (minutes < 60)
|
|
72
|
-
return `${String(minutes)}m${remainingSeconds ? ` ${String(remainingSeconds)}s` : ""}`;
|
|
73
|
-
const hours = Math.floor(minutes / 60);
|
|
74
|
-
const remainingMinutes = minutes % 60;
|
|
75
|
-
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
76
|
-
}
|
|
77
58
|
function stateGlyph(state, spinner) {
|
|
78
59
|
if (state === "running")
|
|
79
60
|
return spinner;
|
|
@@ -84,26 +65,6 @@ function stateColor(state) {
|
|
|
84
65
|
return "accent";
|
|
85
66
|
return state === "completed" ? "success" : "error";
|
|
86
67
|
}
|
|
87
|
-
function activity(status) {
|
|
88
|
-
if (status.state !== "running")
|
|
89
|
-
return undefined;
|
|
90
|
-
const current = status.progress?.activity;
|
|
91
|
-
if (current?.kind === "reasoning" || current?.kind === "text") {
|
|
92
|
-
const name = current.kind === "reasoning" ? "reasoning" : "responding";
|
|
93
|
-
const text = sanitizeDisplayText(current.text);
|
|
94
|
-
return text && text !== name ? `${name} · ${text}` : name;
|
|
95
|
-
}
|
|
96
|
-
if (current?.kind === "tool")
|
|
97
|
-
return sanitizeDisplayText(current.text);
|
|
98
|
-
return sanitizeDisplayText([...(status.progress?.toolCalls ?? [])].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
99
|
-
}
|
|
100
|
-
function stalledDuration(status, now) {
|
|
101
|
-
const lastEventAt = status.progress?.lastEventAt;
|
|
102
|
-
if (status.state !== "running" || lastEventAt === undefined || !Number.isFinite(lastEventAt))
|
|
103
|
-
return undefined;
|
|
104
|
-
const duration = now - lastEventAt;
|
|
105
|
-
return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : undefined;
|
|
106
|
-
}
|
|
107
68
|
/** Tokens and cost as the workflow header shows them. */
|
|
108
69
|
function usageStats(status) {
|
|
109
70
|
const value = status.progress?.accounting;
|
|
@@ -132,16 +93,16 @@ function boundedValue(value) {
|
|
|
132
93
|
}
|
|
133
94
|
function formatSubagentProgress(status, args, theme, spinner, now, expanded) {
|
|
134
95
|
const color = stateColor(status.state);
|
|
135
|
-
const elapsed =
|
|
96
|
+
const elapsed = status.startedAt === undefined ? "" : formatWorkflowRuntime((status.finishedAt ?? now) - status.startedAt);
|
|
136
97
|
const metadata = requestMetadata(args, args.id === undefined);
|
|
137
98
|
const stats = usageStats(status);
|
|
138
99
|
const lines = [
|
|
139
|
-
`${theme.fg(color, stateGlyph(status.state,
|
|
100
|
+
`${theme.fg(color, stateGlyph(status.state, "●"))} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg(color, `[${status.state}]`)}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}${stats ? ` ${stats}` : ""}${elapsed ? ` runtime=${elapsed}` : ""}`,
|
|
140
101
|
];
|
|
141
|
-
const current =
|
|
142
|
-
const stalled = stalledDuration(status, now);
|
|
143
|
-
if (current)
|
|
144
|
-
lines.push(` ${theme.fg("accent", spinner)} ${theme.fg("dim", current)}${stalled === undefined ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
102
|
+
const current = status.state === "running" ? agentActivityLabel(status.progress ?? {}) : undefined;
|
|
103
|
+
const stalled = stalledDuration({ ...status.progress, state: status.state }, now);
|
|
104
|
+
if (current || status.state === "running")
|
|
105
|
+
lines.push(` ${theme.fg("accent", spinner)}${current ? ` ${theme.fg("dim", current)}` : ""}${stalled === undefined ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
145
106
|
else if (stalled !== undefined)
|
|
146
107
|
lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
|
|
147
108
|
if (status.error)
|
|
@@ -222,7 +183,7 @@ export function subagentProgressBlock(status, args, theme, freezeAt) {
|
|
|
222
183
|
const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
|
|
223
184
|
if (!TERMINAL_STATES.has(current.state))
|
|
224
185
|
frozenAt = now;
|
|
225
|
-
const frame = SPINNER[Math.floor(now /
|
|
186
|
+
const frame = SPINNER[Math.floor(now / REPAINT_MS) % SPINNER.length] ?? "◇";
|
|
226
187
|
return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…"));
|
|
227
188
|
},
|
|
228
189
|
invalidate() { },
|
|
@@ -238,7 +199,7 @@ export function renderSubagentResult(result, options, theme, context) {
|
|
|
238
199
|
state.subagentStatus = state.subagentStatus?.id === incoming.id ? { ...state.subagentStatus, ...incoming } : incoming;
|
|
239
200
|
const status = state.subagentStatus;
|
|
240
201
|
if (status?.state === "running" && options.isPartial && !state.subagentSpinner) {
|
|
241
|
-
state.subagentSpinner = setInterval(() => { context.invalidate(); },
|
|
202
|
+
state.subagentSpinner = setInterval(() => { context.invalidate(); }, REPAINT_MS);
|
|
242
203
|
state.subagentSpinner.unref();
|
|
243
204
|
}
|
|
244
205
|
else if ((!options.isPartial || status?.state !== "running") && state.subagentSpinner) {
|
|
@@ -378,7 +339,7 @@ export function createSubagentBackgroundWidget(host = {}) {
|
|
|
378
339
|
};
|
|
379
340
|
}, { placement: "belowEditor" });
|
|
380
341
|
showing = true;
|
|
381
|
-
timer = setInterval(() => { requestRender?.(); },
|
|
342
|
+
timer = setInterval(() => { requestRender?.(); }, REPAINT_MS);
|
|
382
343
|
timer.unref();
|
|
383
344
|
}
|
|
384
345
|
catch {
|
|
@@ -419,7 +380,7 @@ export function createSubagentBackgroundWidget(host = {}) {
|
|
|
419
380
|
}
|
|
420
381
|
export function renderSubagentInspectCall(args, theme) {
|
|
421
382
|
const title = theme.fg("toolTitle", theme.bold("subagents_inspect"));
|
|
422
|
-
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", "all")}`);
|
|
383
|
+
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", args.scope === "all" ? "all" : "session")}`);
|
|
423
384
|
}
|
|
424
385
|
export function renderSubagentInspectResult(result, options, theme, args) {
|
|
425
386
|
const status = statusValue(result.details);
|
|
@@ -157,6 +157,14 @@
|
|
|
157
157
|
.log-row { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); gap: 10px; padding: 5px 8px; border-top: 1px solid var(--line); font: 11px ui-monospace, Menlo, Consolas, monospace; }
|
|
158
158
|
.log-time { color: var(--mute); white-space: nowrap; }
|
|
159
159
|
.log-message { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
160
|
+
.calls-list { max-height: 480px; overflow: auto; }
|
|
161
|
+
.call-row { display: grid; grid-template-columns: 16px 7.5rem minmax(80px, 160px) minmax(0, 1fr) 64px; gap: 10px; align-items: center; padding: 5px 8px; border-top: 1px solid var(--line); font: 11px ui-monospace, Menlo, Consolas, monospace; cursor: pointer; list-style: none; }
|
|
162
|
+
.call-row::-webkit-details-marker { display: none; }
|
|
163
|
+
.call-row:hover { background: var(--bg-hover); }
|
|
164
|
+
.call-row b, .call-preview { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
165
|
+
.call-preview { color: var(--dim); }
|
|
166
|
+
.call-row .num { color: var(--dim); text-align: right; }
|
|
167
|
+
.call-body { padding: 4px 8px 12px 34px; }
|
|
160
168
|
.row { display: grid; grid-template-columns: 16px 1fr; gap: 8px; align-items: center; padding: 3px 12px; cursor: pointer; }
|
|
161
169
|
.row.child { padding-left: 28px; }
|
|
162
170
|
.row:hover { background: var(--bg-hover); }
|
|
@@ -341,7 +349,7 @@
|
|
|
341
349
|
<script src="./morphdom.min.js"></script>
|
|
342
350
|
<script src="./prism.min.js"></script>
|
|
343
351
|
<script>
|
|
344
|
-
const defaultRunLayout = () => ({ swimHeight: 220, ganttCollapsed: false, agentsCollapsed: false, logsCollapsed: false });
|
|
352
|
+
const defaultRunLayout = () => ({ swimHeight: 220, ganttCollapsed: false, agentsCollapsed: false, callsCollapsed: false, logsCollapsed: false });
|
|
345
353
|
const maxSwimHeight = () => Math.max(72, window.innerHeight * 0.7);
|
|
346
354
|
function loadRunLayout() {
|
|
347
355
|
const fallback = defaultRunLayout();
|
|
@@ -354,6 +362,7 @@
|
|
|
354
362
|
swimHeight: Number.isFinite(value.swimHeight) && value.swimHeight >= 72 ? value.swimHeight : fallback.swimHeight,
|
|
355
363
|
ganttCollapsed: typeof value.ganttCollapsed === "boolean" ? value.ganttCollapsed : fallback.ganttCollapsed,
|
|
356
364
|
agentsCollapsed: typeof value.agentsCollapsed === "boolean" ? value.agentsCollapsed : fallback.agentsCollapsed,
|
|
365
|
+
callsCollapsed: typeof value.callsCollapsed === "boolean" ? value.callsCollapsed : fallback.callsCollapsed,
|
|
357
366
|
logsCollapsed: typeof value.logsCollapsed === "boolean" ? value.logsCollapsed : fallback.logsCollapsed,
|
|
358
367
|
};
|
|
359
368
|
} catch {
|
|
@@ -372,7 +381,7 @@
|
|
|
372
381
|
}
|
|
373
382
|
}
|
|
374
383
|
function saveSidebarCollapsed() { try { localStorage.setItem("traj-sidebar-collapsed", JSON.stringify([...state.sidebarCollapsed])); } catch { /* Preferences are optional. */ } }
|
|
375
|
-
const state = { publishers: [], currentPub: null, currentRun: null, currentTarget: null, currentAgent: null, pendingTarget: null, agentRange: null, agentRangeAgent: null, selectedEvent: null, timelineHighlight: null, eventsSig: null, toolPane: "summary", sysPane: "prompt", inspMode: "event", inspSig: null, socket: null, transcripts: {}, transcriptPending: {}, transcriptSources: {}, transcriptRefresh: {}, transcriptRevision: {}, transcriptStatus: {}, transcriptCacheOrder: [], actionTargets: new Map(), shareUrls: {}, runLayout: loadRunLayout(), sidebarCollapsed: loadSidebarCollapsed(), workflowArgumentsOpen: new Set() };
|
|
384
|
+
const state = { publishers: [], currentPub: null, currentRun: null, currentTarget: null, currentAgent: null, pendingTarget: null, agentRange: null, agentRangeAgent: null, selectedEvent: null, timelineHighlight: null, eventsSig: null, toolPane: "summary", sysPane: "prompt", inspMode: "event", inspSig: null, socket: null, transcripts: {}, transcriptPending: {}, transcriptSources: {}, transcriptRefresh: {}, transcriptRevision: {}, transcriptStatus: {}, transcriptCacheOrder: [], actionTargets: new Map(), shareUrls: {}, runLayout: loadRunLayout(), sidebarCollapsed: loadSidebarCollapsed(), workflowArgumentsOpen: new Set(), callsOpen: new Set() };
|
|
376
385
|
let hasAcceptedState = false;
|
|
377
386
|
let socketGeneration = 0;
|
|
378
387
|
let socketReconnect;
|
|
@@ -676,6 +685,8 @@
|
|
|
676
685
|
const style = running ? `left:${Math.max(0, left / span * 100)}%;width:0%` : barStyle(Math.max(0, left), agent.durationMs || 0, 0, span);
|
|
677
686
|
lanes.push({ key: `agent:${agent.id}`, html: `<div class="lane${agent.parentId ? " child" : ""}" data-agent="${esc(agent.id)}">${glyph(agent.state)}<div class="lab">${esc(agent.label || agent.name)}</div><div class="track"><i class="bar ${stateClass(agent.state)}" style="${style}"${running ? ` data-clock-bar-start="${agent.startedAt || start}" data-clock-bar-min="2"` : ""}></i>${toolBars}</div></div>` });
|
|
678
687
|
}
|
|
688
|
+
const calls = scriptCalls(record);
|
|
689
|
+
if (calls.length) lanes.push({ key: "calls", html: `<div class="lane crumb"><span></span><div class="lab">script</div><div class="track">${calls.map((call) => { const status = callState(record, call); const running = status === "running"; const title = `${callLabel(call)} · ${status}${running ? "" : ` · ${callDuration(call)}`}`; return `<i class="bar tool ${stateClass(status)}" style="${running ? `left:${Math.max(0, (call.startedAt - start) / span * 100)}%;width:0.5%` : barStyle(call.startedAt - start, (call.finishedAt ?? call.startedAt) - call.startedAt, 0.5, span)}"${running ? ` data-clock-bar-start="${call.startedAt}" data-clock-bar-min="0.5"` : ""} title="${esc(title)}"></i>`; }).join("")}</div></div>` });
|
|
679
690
|
return [{ key: "axis", html: `<div class="axis"><div></div><div class="ticks"><span>${fmtClock(start)}</span><span data-clock-axis="mid">${live ? "" : `+${fmtRuntime(span / 2)}`}</span><span data-clock-axis="end">${live ? "" : "end"}</span></div></div>` }, ...lanes];
|
|
680
691
|
}
|
|
681
692
|
// Only the lanes whose markup changed are re-parsed; the quantised span keeps the others byte-identical between frames.
|
|
@@ -726,8 +737,19 @@
|
|
|
726
737
|
return `<div class="agent-grid"><div class="agent-grid-head"><span>AGENT</span><span>MODEL</span><span>ROLE</span><span>TOOL TIMING</span><span>DURATION</span><span>TOKENS</span><span>COST</span></div>${body}</div>`;
|
|
727
738
|
}
|
|
728
739
|
function renderLogs(record) { const events = record.run.events || []; const logs = events.filter((event) => event.type === "log"); const omitted = events.find((event) => event.type === "trajectory:truncated"); if (!logs.length && !omitted) return ""; const collapsed = state.runLayout.logsCollapsed; return `<section class="logs"><button type="button" class="sec section-toggle" data-toggle="logs" aria-expanded="${!collapsed}">${collapsed ? "▸" : "▾"} LOGS</button>${collapsed ? "" : `<div class="logs-list">${logs.map((event) => `<div class="log-row"><span class="log-time">${esc(fmtClock(event.timestamp))}</span><span class="log-message">${esc(event.message)}</span></div>`).join("")}${omitted ? `<div class="log-row"><span class="log-time">—</span><span class="log-message">${esc(omitted.omitted)} events omitted from live state</span></div>` : ""}</div>`}</section>`; }
|
|
740
|
+
const scriptCalls = (record) => (record.run.scriptCalls || []).filter((call) => call?.type !== "trajectory:truncated");
|
|
741
|
+
// The journal proves completion even if a crash prevented the finish metadata write.
|
|
742
|
+
const callState = (record, call) => call.output?.status === "completed" || call.output?.status === "available" || call.output?.status === "truncated" && call.output.kind === "result" ? "completed" : call.error ? (call.error.code === "CANCELLED" ? "stopped" : "failed") : call.finishedAt !== undefined ? "completed" : record.run.state === "running" || record.run.state === "pausing" ? "running" : "interrupted";
|
|
743
|
+
const callDuration = (call) => { if (call.finishedAt === undefined) return "—"; const ms = Math.max(0, call.finishedAt - call.startedAt); return ms < 1000 ? `${ms}ms` : fmtRuntime(ms); };
|
|
744
|
+
const callLabel = (call) => call.kind === "shell" ? "shell" : `tools.${call.name}`;
|
|
745
|
+
const callPreview = (call) => call.kind === "tool" ? call.error?.code || "" : typeof call.input === "string" ? call.input : JSON.stringify(call.input);
|
|
746
|
+
function renderCalls(record) {
|
|
747
|
+
const calls = scriptCalls(record); const omitted = (record.run.scriptCalls || []).find((call) => call?.type === "trajectory:truncated"); if (!calls.length && !omitted) return ""; const collapsed = state.runLayout.callsCollapsed;
|
|
748
|
+
const rows = calls.map((call) => { const key = `${state.currentRun}\t${call.path}`; const status = callState(record, call); const duration = status === "running" ? `<span data-clock-kind="runtime" data-clock-start="${call.startedAt}" data-clock-end="live"></span>` : call.finishedAt === undefined ? "—" : esc(callDuration(call)); const input = call.kind !== "shell" ? "" : call.inputBytes !== undefined ? `<div class="hint">Input truncated (${esc(call.inputBytes)} bytes).</div><div class="code">${esc(call.input)}</div>` : `<div class="code">${highlightCode(json(call.input), "json")}</div>`; return `<details class="call"${state.callsOpen.has(key) ? " open" : ""} data-call="${esc(key)}"><summary class="call-row">${glyph(status)}<span class="log-time">${esc(fmtClock(call.startedAt))}</span><b>${esc(callLabel(call))}</b><span class="call-preview">${esc(callPreview(call))}</span><span class="num">${duration}</span></summary><div class="call-body">${call.phase ? `<div class="kv"><div class="k">Phase</div><div>${esc(call.phase)}</div></div>` : ""}${call.kind === "shell" ? `<div class="sec">INPUT</div>${input}<div class="sec">OUTPUT</div>${renderOutputPane(call)}` : `<div class="kv"><div class="k">Status</div><div>${esc(status)}</div>${call.error ? `<div class="k">Error</div><div>${esc(call.error.code)}</div>` : ""}</div>`}</div></details>`; }).join("");
|
|
749
|
+
return `<section class="logs"><button type="button" class="sec section-toggle" data-toggle="calls" aria-expanded="${!collapsed}">${collapsed ? "▸" : "▾"} CALLS</button>${collapsed ? "" : `<div class="calls-list">${rows}${omitted ? `<div class="log-row"><span class="log-time">—</span><span class="log-message">${esc(omitted.omitted)} calls omitted from live state</span></div>` : ""}</div>`}</section>`;
|
|
750
|
+
}
|
|
729
751
|
function renderAgentSection(record, timingsByAgent) { const collapsed = state.runLayout.agentsCollapsed; return `<button type="button" class="sec section-toggle" data-toggle="agents" aria-expanded="${!collapsed}">${collapsed ? "▸" : "▾"} AGENTS</button>${collapsed ? "" : renderAgentGrid(record, timingsByAgent)}`; }
|
|
730
|
-
function renderList(record, timingsByAgent) { const run = record.run; const completed = run.agents.filter((agent) => agent.state === "completed").length; return `<section class="workflow-card"><button type="button" class="workflow-head on" data-run="${esc(state.currentRun)}">${glyph(run.state)}<span class="workflow-name">${esc(run.workflowName)}</span><span class="workflow-meta">${completed}/${run.agents.length} agents · ${fmtCost(runCost(record))} · <span data-clock-kind="runtime" data-clock-start="${startOf(record).getTime()}" data-clock-end="${run.state === "running" ? "live" : endOf(record)}"></span></span></button>${renderAgentSection(record, timingsByAgent)}${renderLogs(record)}</section>`; }
|
|
752
|
+
function renderList(record, timingsByAgent) { const run = record.run; const completed = run.agents.filter((agent) => agent.state === "completed").length; return `<section class="workflow-card"><button type="button" class="workflow-head on" data-run="${esc(state.currentRun)}">${glyph(run.state)}<span class="workflow-name">${esc(run.workflowName)}</span><span class="workflow-meta">${completed}/${run.agents.length} agents · ${fmtCost(runCost(record))} · <span data-clock-kind="runtime" data-clock-start="${startOf(record).getTime()}" data-clock-end="${run.state === "running" ? "live" : endOf(record)}"></span></span></button>${renderAgentSection(record, timingsByAgent)}${renderCalls(record)}${renderLogs(record)}</section>`; }
|
|
731
753
|
function actionButton(label, action, disabled) { return `<button class="btn-alt" data-act="${action}"${disabled ? " disabled" : ""}>${label}</button>`; }
|
|
732
754
|
function subagentAgent(subagent) { return { id: subagent.id, name: subagentLabel(subagent), label: subagentLabel(subagent), role: subagent.role || "subagent", state: subagent.state, model: subagent.model, requestedModel: subagent.request.model, tools: subagent.tools || [], toolDefinitions: subagent.toolDefinitions || [], skills: subagent.request.skills || [], extensions: subagent.request.extensions || [], contextFiles: subagent.request.contextFiles || [], prompt: subagent.request.prompt, systemPrompt: subagent.attempt?.setup?.systemPrompt || subagent.request.prompt, attempts: subagent.attempts || 0, mode: subagent.mode, cwd: subagent.cwd, worktree: subagent.worktree, output: subagent.output || subagentOutputFallback(subagent), outcome: subagent.result !== undefined ? { kind: "result", value: subagent.result } : subagent.failure !== undefined ? { kind: "failure", value: subagent.failure } : subagent.attempt?.error !== undefined ? { kind: "failure", value: subagent.attempt.error } : undefined, finishedAt: subagent.finishedAt, attemptDetails: subagent.attempt ? [subagent.attempt] : [] }; }
|
|
733
755
|
function subagentOutputFallback(subagent) { if (subagent.result !== undefined) return { status: "available", value: subagent.result, bytes: 0 }; if (subagent.failure !== undefined) return { status: "failed", code: subagent.failure.code || "AGENT_FAILED", message: subagent.failure.message || "Subagent failed" }; if (subagent.state === "running") return { status: "pending" }; if (subagent.state === "completed") return { status: "unavailable" }; if (subagent.state === "stopped") return { status: "cancelled", code: subagent.error?.code || "CANCELLED", message: subagent.error?.message || "Subagent stopped" }; if (subagent.state === "failed") return { status: "failed", code: subagent.error?.code || "AGENT_FAILED", message: subagent.error?.message || "Subagent failed" }; return { status: "unavailable" }; }
|
|
@@ -862,8 +884,8 @@
|
|
|
862
884
|
});
|
|
863
885
|
}
|
|
864
886
|
$("script-close").addEventListener("click", closeScript); $("script-bg").addEventListener("click", closeScript); document.addEventListener("keydown", (event) => { if (event.key === "Escape") { closeScript(); setAgentRange(null); if (document.body.dataset.view === "run") renderRun(); else renderAgent(); } });
|
|
865
|
-
document.addEventListener("toggle", (event) => { const target = event.target; if (!(target instanceof HTMLDetailsElement)
|
|
866
|
-
document.querySelector(".app").addEventListener("click", (event) => { const target = event.target.closest("[data-sidebar-group], [data-sidebar-section], [data-copy-share], [data-act], [data-run], [data-subagent], [data-agent], [data-agent-details], [data-script], [data-pane], [data-toggle]"); if (!target) return; if (target.dataset.copyShare) { const input = target.previousElementSibling; navigator.clipboard?.writeText(target.dataset.copyShare).then(() => showToast("copied"), () => { input?.select(); showToast("press Ctrl+C to copy"); }); return; } if (target.dataset.sidebarGroup !== undefined || target.dataset.sidebarSection !== undefined) { const key = target.dataset.sidebarGroup ?? target.dataset.sidebarSection; if (state.sidebarCollapsed.has(key)) state.sidebarCollapsed.delete(key); else state.sidebarCollapsed.add(key); saveSidebarCollapsed(); renderSidebar(); return; } if (target.dataset.toggle) { if (document.body.dataset.view !== "run") return; const key = target.dataset.toggle === "gantt" ? "ganttCollapsed" : target.dataset.toggle === "agents" ? "agentsCollapsed" : target.dataset.toggle === "logs" ? "logsCollapsed" : ""; if (!key) return; state.runLayout[key] = !state.runLayout[key]; saveRunLayout(); renderRun(); return; } if (target.dataset.act) { if (target.dataset.act === "steer") { const input = $("subagent-steer"); const message = input?.value.trim(); if (!message) return; sendAction("steer", { payload: { message } }); if (input) input.value = ""; return; } const name = target.closest("[data-name]")?.dataset.name; sendAction(target.dataset.act, name ? { name } : {}); return; } if (target.dataset.agentDetails !== undefined) { state.inspMode = "agent"; state.inspSig = null; renderAgent(); return; } if (target.dataset.script) { openScript(); return; } if (target.dataset.pane) { const found = selected(); const agent = found ? found.target.kind === "subagent" ? subagentAgent(found.record) : found.record.run.agents.find((candidate) => candidate.id === state.currentAgent) : undefined; const entries = found && agent ? agentEntries(found.record, agent) : []; if (target.closest("#tool-tabs")) { state.toolPane = target.dataset.pane; state.inspMode = "event"; renderInspector(entries[state.selectedEvent || 0] ? entryDetails(entries[state.selectedEvent || 0], agent, entries) : undefined, agent, entries); } else { state.sysPane = target.dataset.pane; state.inspMode = "agent"; if (agent) renderSystemPane(agent); } return; } if (target.dataset.run) { selectRun(target.dataset.run); return; } if (target.dataset.subagent) { selectSubagent(target.dataset.subagent); return; } if (target.dataset.agent && document.body.dataset.view === "run") { state.currentAgent = target.dataset.agent; state.selectedEvent = null; state.inspMode = "event"; setView("agent"); } });
|
|
887
|
+
document.addEventListener("toggle", (event) => { const target = event.target; if (!(target instanceof HTMLDetailsElement)) return; const key = target.dataset.workflowArgs ?? target.dataset.call; const open = target.dataset.workflowArgs !== undefined ? state.workflowArgumentsOpen : target.dataset.call !== undefined ? state.callsOpen : undefined; if (!open) return; if (target.open) open.add(key); else open.delete(key); }, true);
|
|
888
|
+
document.querySelector(".app").addEventListener("click", (event) => { const target = event.target.closest("[data-sidebar-group], [data-sidebar-section], [data-copy-share], [data-act], [data-run], [data-subagent], [data-agent], [data-agent-details], [data-script], [data-pane], [data-toggle]"); if (!target) return; if (target.dataset.copyShare) { const input = target.previousElementSibling; navigator.clipboard?.writeText(target.dataset.copyShare).then(() => showToast("copied"), () => { input?.select(); showToast("press Ctrl+C to copy"); }); return; } if (target.dataset.sidebarGroup !== undefined || target.dataset.sidebarSection !== undefined) { const key = target.dataset.sidebarGroup ?? target.dataset.sidebarSection; if (state.sidebarCollapsed.has(key)) state.sidebarCollapsed.delete(key); else state.sidebarCollapsed.add(key); saveSidebarCollapsed(); renderSidebar(); return; } if (target.dataset.toggle) { if (document.body.dataset.view !== "run") return; const key = target.dataset.toggle === "gantt" ? "ganttCollapsed" : target.dataset.toggle === "agents" ? "agentsCollapsed" : target.dataset.toggle === "calls" ? "callsCollapsed" : target.dataset.toggle === "logs" ? "logsCollapsed" : ""; if (!key) return; state.runLayout[key] = !state.runLayout[key]; saveRunLayout(); renderRun(); return; } if (target.dataset.act) { if (target.dataset.act === "steer") { const input = $("subagent-steer"); const message = input?.value.trim(); if (!message) return; sendAction("steer", { payload: { message } }); if (input) input.value = ""; return; } const name = target.closest("[data-name]")?.dataset.name; sendAction(target.dataset.act, name ? { name } : {}); return; } if (target.dataset.agentDetails !== undefined) { state.inspMode = "agent"; state.inspSig = null; renderAgent(); return; } if (target.dataset.script) { openScript(); return; } if (target.dataset.pane) { const found = selected(); const agent = found ? found.target.kind === "subagent" ? subagentAgent(found.record) : found.record.run.agents.find((candidate) => candidate.id === state.currentAgent) : undefined; const entries = found && agent ? agentEntries(found.record, agent) : []; if (target.closest("#tool-tabs")) { state.toolPane = target.dataset.pane; state.inspMode = "event"; renderInspector(entries[state.selectedEvent || 0] ? entryDetails(entries[state.selectedEvent || 0], agent, entries) : undefined, agent, entries); } else { state.sysPane = target.dataset.pane; state.inspMode = "agent"; if (agent) renderSystemPane(agent); } return; } if (target.dataset.run) { selectRun(target.dataset.run); return; } if (target.dataset.subagent) { selectSubagent(target.dataset.subagent); return; } if (target.dataset.agent && document.body.dataset.view === "run") { state.currentAgent = target.dataset.agent; state.selectedEvent = null; state.inspMode = "event"; setView("agent"); } });
|
|
867
889
|
document.addEventListener("visibilitychange", () => { sendFocus(); if (!document.hidden) scheduleRender(); });
|
|
868
890
|
// Phones show one sheet at a time, so picking something moves to the sheet that shows its result.
|
|
869
891
|
const phone = matchMedia("(max-width: 900px)");
|