takomi 2.1.2 → 2.1.3
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/.pi/README.md +124 -124
- package/.pi/agents/architect.md +15 -15
- package/.pi/agents/coder.md +14 -14
- package/.pi/agents/designer.md +17 -17
- package/.pi/agents/orchestrator.md +22 -22
- package/.pi/agents/reviewer.md +16 -16
- package/.pi/extensions/oauth-router/README.md +125 -125
- package/.pi/extensions/oauth-router/commands.ts +380 -380
- package/.pi/extensions/oauth-router/config.ts +200 -200
- package/.pi/extensions/oauth-router/index.ts +41 -41
- package/.pi/extensions/oauth-router/oauth-flow.ts +154 -154
- package/.pi/extensions/oauth-router/oauth-store.ts +121 -121
- package/.pi/extensions/oauth-router/package.json +14 -14
- package/.pi/extensions/oauth-router/policies.ts +27 -27
- package/.pi/extensions/oauth-router/provider.ts +492 -492
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -98
- package/.pi/extensions/oauth-router/state.ts +174 -174
- package/.pi/extensions/oauth-router/types.ts +153 -153
- package/.pi/extensions/takomi-runtime/command-text.ts +130 -130
- package/.pi/extensions/takomi-runtime/commands.ts +179 -179
- package/.pi/extensions/takomi-runtime/context-panel.ts +282 -282
- package/.pi/extensions/takomi-runtime/index.ts +1288 -1288
- package/.pi/extensions/takomi-runtime/profile.ts +114 -114
- package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -105
- package/.pi/extensions/takomi-runtime/shared.ts +492 -492
- package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -364
- package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -501
- package/.pi/extensions/takomi-runtime/subagent-types.ts +83 -83
- package/.pi/extensions/takomi-runtime/ui.ts +133 -133
- package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -18
- package/.pi/extensions/takomi-subagents/agents.ts +113 -113
- package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -95
- package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -26
- package/.pi/extensions/takomi-subagents/dispatch.ts +215 -215
- package/.pi/extensions/takomi-subagents/index.ts +75 -75
- package/.pi/extensions/takomi-subagents/live-updates.ts +83 -83
- package/.pi/extensions/takomi-subagents/native-render.ts +174 -174
- package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -209
- package/.pi/themes/takomi-noir.json +81 -81
- package/package.json +59 -59
- package/src/doctor.js +87 -84
- package/src/pi-harness.js +355 -351
- package/src/pi-installer.js +193 -171
- package/src/pi-takomi-core/index.ts +4 -4
- package/src/pi-takomi-core/orchestration.ts +402 -402
- package/src/pi-takomi-core/routing.ts +93 -93
- package/src/pi-takomi-core/types.ts +173 -173
- package/src/pi-takomi-core/workflows.ts +299 -299
- package/src/skills-installer.js +101 -101
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
2
|
-
import type { TakomiSubagentRuntimeEvent } from "../takomi-runtime/subagent-types";
|
|
3
|
-
import type { TakomiDispatchResult } from "./dispatch";
|
|
4
|
-
|
|
5
|
-
type ToolUpdate = (partial: {
|
|
6
|
-
content: Array<{ type: "text"; text: string }>;
|
|
7
|
-
details: Record<string, unknown>;
|
|
8
|
-
}) => void;
|
|
9
|
-
|
|
10
|
-
type LiveTask = {
|
|
11
|
-
agent: string;
|
|
12
|
-
task: string;
|
|
13
|
-
workflow?: string;
|
|
14
|
-
model?: string;
|
|
15
|
-
thinking?: TakomiThinkingLevel;
|
|
16
|
-
conversationId?: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function appendLine(value: string, line: string): string {
|
|
20
|
-
const next = [value, line.trim()].filter(Boolean).join("\n");
|
|
21
|
-
return next.split(/\r?\n/).slice(-12).join("\n");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function createTakomiLiveUpdateBridge(
|
|
25
|
-
tasks: LiveTask[],
|
|
26
|
-
mode: "single" | "parallel" | "chain",
|
|
27
|
-
agentScope: string,
|
|
28
|
-
onUpdate: ToolUpdate | undefined,
|
|
29
|
-
) {
|
|
30
|
-
const results: TakomiDispatchResult[] = tasks.map((task, index) => ({
|
|
31
|
-
agent: task.agent,
|
|
32
|
-
task: task.task,
|
|
33
|
-
workflow: task.workflow,
|
|
34
|
-
model: task.model,
|
|
35
|
-
thinking: task.thinking,
|
|
36
|
-
conversationId: task.conversationId ?? `pending-${index + 1}`,
|
|
37
|
-
code: -1,
|
|
38
|
-
output: "Queued.",
|
|
39
|
-
stderr: "",
|
|
40
|
-
preflight: "",
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
const emit = () => {
|
|
44
|
-
const done = results.filter((result) => result.code === 0).length;
|
|
45
|
-
const running = results.filter((result) => result.code === -1).length;
|
|
46
|
-
onUpdate?.({
|
|
47
|
-
content: [{ type: "text", text: `Takomi ${mode}: ${done}/${results.length} done, ${running} running` }],
|
|
48
|
-
details: { results: [...results], mode, agentScope },
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
results,
|
|
54
|
-
event(index: number, event: TakomiSubagentRuntimeEvent): void {
|
|
55
|
-
const current = results[index];
|
|
56
|
-
if (!current) return;
|
|
57
|
-
if (event.type === "start") {
|
|
58
|
-
current.conversationId = event.state.conversationId ?? current.conversationId;
|
|
59
|
-
current.thinking = event.state.thinking ?? current.thinking;
|
|
60
|
-
current.output = event.state.summary ?? "Starting.";
|
|
61
|
-
} else if (event.type === "update") {
|
|
62
|
-
current.model = event.patch.model ?? current.model;
|
|
63
|
-
current.thinking = event.patch.thinking ?? current.thinking;
|
|
64
|
-
current.output = event.patch.outputText ?? event.patch.summary ?? current.output;
|
|
65
|
-
if (event.patch.logs?.length) current.output = appendLine(current.output, event.patch.logs.join("\n"));
|
|
66
|
-
} else if (event.type === "appendLog") {
|
|
67
|
-
current.output = appendLine(current.output, event.chunk);
|
|
68
|
-
} else if (event.type === "complete") {
|
|
69
|
-
current.code = 0;
|
|
70
|
-
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
71
|
-
} else if (event.type === "block") {
|
|
72
|
-
current.code = 1;
|
|
73
|
-
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
74
|
-
if (event.patch?.logs?.length) current.stderr = event.patch.logs.join("\n");
|
|
75
|
-
}
|
|
76
|
-
emit();
|
|
77
|
-
},
|
|
78
|
-
finish(index: number, result: TakomiDispatchResult): void {
|
|
79
|
-
results[index] = result;
|
|
80
|
-
emit();
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
}
|
|
1
|
+
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
2
|
+
import type { TakomiSubagentRuntimeEvent } from "../takomi-runtime/subagent-types";
|
|
3
|
+
import type { TakomiDispatchResult } from "./dispatch";
|
|
4
|
+
|
|
5
|
+
type ToolUpdate = (partial: {
|
|
6
|
+
content: Array<{ type: "text"; text: string }>;
|
|
7
|
+
details: Record<string, unknown>;
|
|
8
|
+
}) => void;
|
|
9
|
+
|
|
10
|
+
type LiveTask = {
|
|
11
|
+
agent: string;
|
|
12
|
+
task: string;
|
|
13
|
+
workflow?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
thinking?: TakomiThinkingLevel;
|
|
16
|
+
conversationId?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function appendLine(value: string, line: string): string {
|
|
20
|
+
const next = [value, line.trim()].filter(Boolean).join("\n");
|
|
21
|
+
return next.split(/\r?\n/).slice(-12).join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createTakomiLiveUpdateBridge(
|
|
25
|
+
tasks: LiveTask[],
|
|
26
|
+
mode: "single" | "parallel" | "chain",
|
|
27
|
+
agentScope: string,
|
|
28
|
+
onUpdate: ToolUpdate | undefined,
|
|
29
|
+
) {
|
|
30
|
+
const results: TakomiDispatchResult[] = tasks.map((task, index) => ({
|
|
31
|
+
agent: task.agent,
|
|
32
|
+
task: task.task,
|
|
33
|
+
workflow: task.workflow,
|
|
34
|
+
model: task.model,
|
|
35
|
+
thinking: task.thinking,
|
|
36
|
+
conversationId: task.conversationId ?? `pending-${index + 1}`,
|
|
37
|
+
code: -1,
|
|
38
|
+
output: "Queued.",
|
|
39
|
+
stderr: "",
|
|
40
|
+
preflight: "",
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const emit = () => {
|
|
44
|
+
const done = results.filter((result) => result.code === 0).length;
|
|
45
|
+
const running = results.filter((result) => result.code === -1).length;
|
|
46
|
+
onUpdate?.({
|
|
47
|
+
content: [{ type: "text", text: `Takomi ${mode}: ${done}/${results.length} done, ${running} running` }],
|
|
48
|
+
details: { results: [...results], mode, agentScope },
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
results,
|
|
54
|
+
event(index: number, event: TakomiSubagentRuntimeEvent): void {
|
|
55
|
+
const current = results[index];
|
|
56
|
+
if (!current) return;
|
|
57
|
+
if (event.type === "start") {
|
|
58
|
+
current.conversationId = event.state.conversationId ?? current.conversationId;
|
|
59
|
+
current.thinking = event.state.thinking ?? current.thinking;
|
|
60
|
+
current.output = event.state.summary ?? "Starting.";
|
|
61
|
+
} else if (event.type === "update") {
|
|
62
|
+
current.model = event.patch.model ?? current.model;
|
|
63
|
+
current.thinking = event.patch.thinking ?? current.thinking;
|
|
64
|
+
current.output = event.patch.outputText ?? event.patch.summary ?? current.output;
|
|
65
|
+
if (event.patch.logs?.length) current.output = appendLine(current.output, event.patch.logs.join("\n"));
|
|
66
|
+
} else if (event.type === "appendLog") {
|
|
67
|
+
current.output = appendLine(current.output, event.chunk);
|
|
68
|
+
} else if (event.type === "complete") {
|
|
69
|
+
current.code = 0;
|
|
70
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
71
|
+
} else if (event.type === "block") {
|
|
72
|
+
current.code = 1;
|
|
73
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
74
|
+
if (event.patch?.logs?.length) current.stderr = event.patch.logs.join("\n");
|
|
75
|
+
}
|
|
76
|
+
emit();
|
|
77
|
+
},
|
|
78
|
+
finish(index: number, result: TakomiDispatchResult): void {
|
|
79
|
+
results[index] = result;
|
|
80
|
+
emit();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import { renderSubagentResult, syncResultAnimation } from "pi-subagents/src/tui/render";
|
|
2
|
-
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
3
|
-
import type { Details, SingleResult, AgentProgress } from "pi-subagents/src/shared/types";
|
|
4
|
-
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
|
|
6
|
-
import type { TakomiDispatchResult } from "./dispatch";
|
|
7
|
-
import type { TakomiSubagentToolParams } from "./tool-runner";
|
|
8
|
-
|
|
9
|
-
type ToolResult = {
|
|
10
|
-
content?: Array<{ type: string; text?: string }>;
|
|
11
|
-
details?: {
|
|
12
|
-
results?: TakomiDispatchResult[];
|
|
13
|
-
mode?: "single" | "parallel" | "chain";
|
|
14
|
-
agentScope?: string;
|
|
15
|
-
plan?: unknown;
|
|
16
|
-
};
|
|
17
|
-
isError?: boolean;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function taskList(params: TakomiSubagentToolParams): Array<{ agent: string; task: string }> {
|
|
21
|
-
if (params.chain?.length) return params.chain;
|
|
22
|
-
if (params.tasks?.length) return params.tasks;
|
|
23
|
-
if (params.agent || params.task) return [{ agent: params.agent ?? "...", task: params.task ?? "..." }];
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function renderTakomiSubagentCall(params: TakomiSubagentToolParams, theme: Theme) {
|
|
28
|
-
const tasks = taskList(params);
|
|
29
|
-
const mode = params.chain?.length ? "chain" : params.tasks?.length ? "parallel" : "single";
|
|
30
|
-
if (tasks.length === 1) {
|
|
31
|
-
return new Text(
|
|
32
|
-
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${theme.fg("accent", tasks[0]?.agent || "?")}`,
|
|
33
|
-
0,
|
|
34
|
-
0,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return new Text(
|
|
38
|
-
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${mode} (${tasks.length})`,
|
|
39
|
-
0,
|
|
40
|
-
0,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function parseTakomiOutput(outputText: string) {
|
|
45
|
-
const rawLines = outputText.split(/\r?\n/);
|
|
46
|
-
const textLines: string[] = [];
|
|
47
|
-
const recentTools: Array<{ tool: string; args: string; endMs: number }> = [];
|
|
48
|
-
let currentTool: string | undefined;
|
|
49
|
-
let currentToolArgs: string | undefined;
|
|
50
|
-
|
|
51
|
-
for (const line of rawLines) {
|
|
52
|
-
if (!line.trim()) continue;
|
|
53
|
-
|
|
54
|
-
// 1. Check for tool lifecycle markers
|
|
55
|
-
if (line.startsWith("Tool start: ")) {
|
|
56
|
-
currentTool = line.replace("Tool start: ", "").trim();
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (line.startsWith("Tool complete: ") || line.startsWith("Tool failed: ")) {
|
|
60
|
-
const toolName = line.replace(/Tool (complete|failed): /, "").trim();
|
|
61
|
-
recentTools.push({ tool: toolName, args: "", endMs: Date.now() });
|
|
62
|
-
if (currentTool === toolName) {
|
|
63
|
-
currentTool = undefined;
|
|
64
|
-
currentToolArgs = undefined;
|
|
65
|
-
}
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 2. Handle JSON blobs (tool calls)
|
|
70
|
-
const jsonStartIdx = line.indexOf('{"');
|
|
71
|
-
if (jsonStartIdx !== -1) {
|
|
72
|
-
const beforeJson = line.substring(0, jsonStartIdx).trim();
|
|
73
|
-
if (beforeJson) textLines.push(beforeJson);
|
|
74
|
-
|
|
75
|
-
const jsonPart = line.substring(jsonStartIdx);
|
|
76
|
-
try {
|
|
77
|
-
const parsed = JSON.parse(jsonPart);
|
|
78
|
-
const toolName = parsed.tool || (parsed.command ? parsed.command.split(" ")[0] : undefined);
|
|
79
|
-
if (toolName) {
|
|
80
|
-
const args = parsed.args ? JSON.stringify(parsed.args) : (parsed.command || "");
|
|
81
|
-
currentTool = toolName;
|
|
82
|
-
currentToolArgs = args;
|
|
83
|
-
}
|
|
84
|
-
} catch (e) {
|
|
85
|
-
if (jsonPart.trim().length > 1) {
|
|
86
|
-
textLines.push(jsonPart);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 3. Skip lone brackets
|
|
93
|
-
const trimmed = line.trim();
|
|
94
|
-
if (trimmed === "{" || trimmed === "}") continue;
|
|
95
|
-
|
|
96
|
-
// 4. Everything else is text (thoughts)
|
|
97
|
-
textLines.push(line);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return { textLines, recentTools, currentTool, currentToolArgs };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function renderTakomiSubagentResult(result: ToolResult, options: { expanded?: boolean; isPartial?: boolean }, theme: Theme, context: any) {
|
|
104
|
-
const details = result.details;
|
|
105
|
-
const results = details?.results ?? [];
|
|
106
|
-
|
|
107
|
-
if (results.length === 0) {
|
|
108
|
-
const text = result.content?.find((item) => item.type === "text")?.text ?? "(no output)";
|
|
109
|
-
return new Text(text, 0, 0);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const mappedResults: SingleResult[] = results.map((r, i) => {
|
|
113
|
-
const isRunning = r.code === -1;
|
|
114
|
-
const outputText = r.output || r.stderr || "";
|
|
115
|
-
const { textLines, recentTools, currentTool, currentToolArgs } = parseTakomiOutput(outputText);
|
|
116
|
-
|
|
117
|
-
const recentOutput = textLines.slice(-5);
|
|
118
|
-
|
|
119
|
-
const progress: AgentProgress | undefined = isRunning ? {
|
|
120
|
-
index: i,
|
|
121
|
-
agent: r.agent,
|
|
122
|
-
status: "running",
|
|
123
|
-
task: r.task || "",
|
|
124
|
-
lastActivityAt: Date.now(),
|
|
125
|
-
currentTool,
|
|
126
|
-
currentToolArgs,
|
|
127
|
-
currentToolStartedAt: currentTool ? Date.now() : undefined,
|
|
128
|
-
recentTools,
|
|
129
|
-
recentOutput,
|
|
130
|
-
toolCount: recentTools.length + (currentTool ? 1 : 0),
|
|
131
|
-
tokens: 0,
|
|
132
|
-
durationMs: 0,
|
|
133
|
-
} : {
|
|
134
|
-
index: i,
|
|
135
|
-
agent: r.agent,
|
|
136
|
-
status: "completed", // Takomi results in mappedResults are usually final if code !== -1
|
|
137
|
-
task: r.task || "",
|
|
138
|
-
lastActivityAt: Date.now(),
|
|
139
|
-
recentTools,
|
|
140
|
-
recentOutput: [],
|
|
141
|
-
toolCount: recentTools.length,
|
|
142
|
-
tokens: 0,
|
|
143
|
-
durationMs: 0,
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
agent: r.agent,
|
|
148
|
-
task: r.task || "",
|
|
149
|
-
exitCode: isRunning ? 0 : r.code,
|
|
150
|
-
model: r.model,
|
|
151
|
-
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
152
|
-
progress,
|
|
153
|
-
truncation: isRunning ? undefined : {
|
|
154
|
-
text: textLines.join("\n").replace(/\n/g, " \n"),
|
|
155
|
-
truncated: false
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
const mappedDetails: Details = {
|
|
161
|
-
mode: details?.mode ?? "single",
|
|
162
|
-
results: mappedResults,
|
|
163
|
-
chainAgents: details?.mode === "chain" ? results.map(r => r.agent) : undefined,
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const agentToolResult: AgentToolResult<Details> = {
|
|
167
|
-
content: result.content as any || [],
|
|
168
|
-
details: mappedDetails,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
syncResultAnimation(agentToolResult, context);
|
|
172
|
-
return renderSubagentResult(agentToolResult, { expanded: options.expanded ?? false }, theme);
|
|
173
|
-
}
|
|
174
|
-
|
|
1
|
+
import { renderSubagentResult, syncResultAnimation } from "pi-subagents/src/tui/render";
|
|
2
|
+
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
3
|
+
import type { Details, SingleResult, AgentProgress } from "pi-subagents/src/shared/types";
|
|
4
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
|
|
6
|
+
import type { TakomiDispatchResult } from "./dispatch";
|
|
7
|
+
import type { TakomiSubagentToolParams } from "./tool-runner";
|
|
8
|
+
|
|
9
|
+
type ToolResult = {
|
|
10
|
+
content?: Array<{ type: string; text?: string }>;
|
|
11
|
+
details?: {
|
|
12
|
+
results?: TakomiDispatchResult[];
|
|
13
|
+
mode?: "single" | "parallel" | "chain";
|
|
14
|
+
agentScope?: string;
|
|
15
|
+
plan?: unknown;
|
|
16
|
+
};
|
|
17
|
+
isError?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function taskList(params: TakomiSubagentToolParams): Array<{ agent: string; task: string }> {
|
|
21
|
+
if (params.chain?.length) return params.chain;
|
|
22
|
+
if (params.tasks?.length) return params.tasks;
|
|
23
|
+
if (params.agent || params.task) return [{ agent: params.agent ?? "...", task: params.task ?? "..." }];
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function renderTakomiSubagentCall(params: TakomiSubagentToolParams, theme: Theme) {
|
|
28
|
+
const tasks = taskList(params);
|
|
29
|
+
const mode = params.chain?.length ? "chain" : params.tasks?.length ? "parallel" : "single";
|
|
30
|
+
if (tasks.length === 1) {
|
|
31
|
+
return new Text(
|
|
32
|
+
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${theme.fg("accent", tasks[0]?.agent || "?")}`,
|
|
33
|
+
0,
|
|
34
|
+
0,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return new Text(
|
|
38
|
+
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${mode} (${tasks.length})`,
|
|
39
|
+
0,
|
|
40
|
+
0,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseTakomiOutput(outputText: string) {
|
|
45
|
+
const rawLines = outputText.split(/\r?\n/);
|
|
46
|
+
const textLines: string[] = [];
|
|
47
|
+
const recentTools: Array<{ tool: string; args: string; endMs: number }> = [];
|
|
48
|
+
let currentTool: string | undefined;
|
|
49
|
+
let currentToolArgs: string | undefined;
|
|
50
|
+
|
|
51
|
+
for (const line of rawLines) {
|
|
52
|
+
if (!line.trim()) continue;
|
|
53
|
+
|
|
54
|
+
// 1. Check for tool lifecycle markers
|
|
55
|
+
if (line.startsWith("Tool start: ")) {
|
|
56
|
+
currentTool = line.replace("Tool start: ", "").trim();
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.startsWith("Tool complete: ") || line.startsWith("Tool failed: ")) {
|
|
60
|
+
const toolName = line.replace(/Tool (complete|failed): /, "").trim();
|
|
61
|
+
recentTools.push({ tool: toolName, args: "", endMs: Date.now() });
|
|
62
|
+
if (currentTool === toolName) {
|
|
63
|
+
currentTool = undefined;
|
|
64
|
+
currentToolArgs = undefined;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Handle JSON blobs (tool calls)
|
|
70
|
+
const jsonStartIdx = line.indexOf('{"');
|
|
71
|
+
if (jsonStartIdx !== -1) {
|
|
72
|
+
const beforeJson = line.substring(0, jsonStartIdx).trim();
|
|
73
|
+
if (beforeJson) textLines.push(beforeJson);
|
|
74
|
+
|
|
75
|
+
const jsonPart = line.substring(jsonStartIdx);
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(jsonPart);
|
|
78
|
+
const toolName = parsed.tool || (parsed.command ? parsed.command.split(" ")[0] : undefined);
|
|
79
|
+
if (toolName) {
|
|
80
|
+
const args = parsed.args ? JSON.stringify(parsed.args) : (parsed.command || "");
|
|
81
|
+
currentTool = toolName;
|
|
82
|
+
currentToolArgs = args;
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (jsonPart.trim().length > 1) {
|
|
86
|
+
textLines.push(jsonPart);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. Skip lone brackets
|
|
93
|
+
const trimmed = line.trim();
|
|
94
|
+
if (trimmed === "{" || trimmed === "}") continue;
|
|
95
|
+
|
|
96
|
+
// 4. Everything else is text (thoughts)
|
|
97
|
+
textLines.push(line);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { textLines, recentTools, currentTool, currentToolArgs };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function renderTakomiSubagentResult(result: ToolResult, options: { expanded?: boolean; isPartial?: boolean }, theme: Theme, context: any) {
|
|
104
|
+
const details = result.details;
|
|
105
|
+
const results = details?.results ?? [];
|
|
106
|
+
|
|
107
|
+
if (results.length === 0) {
|
|
108
|
+
const text = result.content?.find((item) => item.type === "text")?.text ?? "(no output)";
|
|
109
|
+
return new Text(text, 0, 0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const mappedResults: SingleResult[] = results.map((r, i) => {
|
|
113
|
+
const isRunning = r.code === -1;
|
|
114
|
+
const outputText = r.output || r.stderr || "";
|
|
115
|
+
const { textLines, recentTools, currentTool, currentToolArgs } = parseTakomiOutput(outputText);
|
|
116
|
+
|
|
117
|
+
const recentOutput = textLines.slice(-5);
|
|
118
|
+
|
|
119
|
+
const progress: AgentProgress | undefined = isRunning ? {
|
|
120
|
+
index: i,
|
|
121
|
+
agent: r.agent,
|
|
122
|
+
status: "running",
|
|
123
|
+
task: r.task || "",
|
|
124
|
+
lastActivityAt: Date.now(),
|
|
125
|
+
currentTool,
|
|
126
|
+
currentToolArgs,
|
|
127
|
+
currentToolStartedAt: currentTool ? Date.now() : undefined,
|
|
128
|
+
recentTools,
|
|
129
|
+
recentOutput,
|
|
130
|
+
toolCount: recentTools.length + (currentTool ? 1 : 0),
|
|
131
|
+
tokens: 0,
|
|
132
|
+
durationMs: 0,
|
|
133
|
+
} : {
|
|
134
|
+
index: i,
|
|
135
|
+
agent: r.agent,
|
|
136
|
+
status: "completed", // Takomi results in mappedResults are usually final if code !== -1
|
|
137
|
+
task: r.task || "",
|
|
138
|
+
lastActivityAt: Date.now(),
|
|
139
|
+
recentTools,
|
|
140
|
+
recentOutput: [],
|
|
141
|
+
toolCount: recentTools.length,
|
|
142
|
+
tokens: 0,
|
|
143
|
+
durationMs: 0,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
agent: r.agent,
|
|
148
|
+
task: r.task || "",
|
|
149
|
+
exitCode: isRunning ? 0 : r.code,
|
|
150
|
+
model: r.model,
|
|
151
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
152
|
+
progress,
|
|
153
|
+
truncation: isRunning ? undefined : {
|
|
154
|
+
text: textLines.join("\n").replace(/\n/g, " \n"),
|
|
155
|
+
truncated: false
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const mappedDetails: Details = {
|
|
161
|
+
mode: details?.mode ?? "single",
|
|
162
|
+
results: mappedResults,
|
|
163
|
+
chainAgents: details?.mode === "chain" ? results.map(r => r.agent) : undefined,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const agentToolResult: AgentToolResult<Details> = {
|
|
167
|
+
content: result.content as any || [],
|
|
168
|
+
details: mappedDetails,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
syncResultAnimation(agentToolResult, context);
|
|
172
|
+
return renderSubagentResult(agentToolResult, { expanded: options.expanded ?? false }, theme);
|
|
173
|
+
}
|
|
174
|
+
|