takomi 2.1.2 → 2.1.4
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 +511 -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 +90 -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 +306 -215
- package/.pi/extensions/takomi-subagents/index.ts +76 -75
- package/.pi/extensions/takomi-subagents/live-updates.ts +136 -83
- package/.pi/extensions/takomi-subagents/native-render.ts +5 -142
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +228 -0
- 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/cli.js +14 -0
- 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
- package/src/update-check.js +140 -0
|
@@ -1,75 +1,76 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { Type } from "typebox";
|
|
3
|
-
import { renderTakomiSubagentCall, renderTakomiSubagentResult } from "./native-render";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const ChecklistItemSchema = Type.Object({
|
|
7
|
-
text: Type.String(),
|
|
8
|
-
done: Type.Optional(Type.Boolean()),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const ThinkingSchema = Type.Union([
|
|
12
|
-
Type.Literal("off"),
|
|
13
|
-
Type.Literal("minimal"),
|
|
14
|
-
Type.Literal("low"),
|
|
15
|
-
Type.Literal("medium"),
|
|
16
|
-
Type.Literal("high"),
|
|
17
|
-
Type.Literal("xhigh"),
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
const TaskSchema = Type.Object({
|
|
21
|
-
agent: Type.String(),
|
|
22
|
-
task: Type.String(),
|
|
23
|
-
workflow: Type.Optional(Type.String()),
|
|
24
|
-
skills: Type.Optional(Type.Array(Type.String())),
|
|
25
|
-
model: Type.Optional(Type.String()),
|
|
26
|
-
fallbackModels: Type.Optional(Type.Array(Type.String())),
|
|
27
|
-
thinking: Type.Optional(ThinkingSchema),
|
|
28
|
-
conversationId: Type.Optional(Type.String()),
|
|
29
|
-
cwd: Type.Optional(Type.String()),
|
|
30
|
-
checklist: Type.Optional(Type.Array(Type.Union([Type.String(), ChecklistItemSchema]))),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const SubagentParameters = Type.Object({
|
|
34
|
-
agent: Type.Optional(Type.String({ description: "Agent name for single execution" })),
|
|
35
|
-
task: Type.Optional(Type.String({ description: "Task for single execution" })),
|
|
36
|
-
workflow: Type.Optional(Type.String({ description: "Workflow or playbook overlay for this task" })),
|
|
37
|
-
skills: Type.Optional(Type.Array(Type.String(), { description: "Extra skills to apply during the task" })),
|
|
38
|
-
model: Type.Optional(Type.String({ description: "Optional per-run model override" })),
|
|
39
|
-
fallbackModels: Type.Optional(Type.Array(Type.String(), { description: "Optional ordered model fallback list" })),
|
|
40
|
-
thinking: Type.Optional(ThinkingSchema),
|
|
41
|
-
conversationId: Type.Optional(Type.String({ description: "Persistent conversation id to resume the same subagent session" })),
|
|
42
|
-
cwd: Type.Optional(Type.String({ description: "Working directory override" })),
|
|
43
|
-
checklist: Type.Optional(Type.Array(Type.Union([Type.String(), ChecklistItemSchema]), { description: "Optional checklist for the subagent" })),
|
|
44
|
-
tasks: Type.Optional(Type.Array(TaskSchema, { description: "Parallel subagent tasks" })),
|
|
45
|
-
confirmLaunch: Type.Optional(Type.Boolean({ description: "Required to launch immediately in manual Takomi launch mode" })),
|
|
46
|
-
previewOnly: Type.Optional(Type.Boolean({ description: "Return the delegation plan without launching" })),
|
|
47
|
-
chain: Type.Optional(Type.Array(TaskSchema, { description: "Sequential chain of subagent tasks" })),
|
|
48
|
-
agentScope: Type.Optional(Type.Union([Type.Literal("user"), Type.Literal("project"), Type.Literal("both")])),
|
|
49
|
-
confirmProjectAgents: Type.Optional(Type.Boolean({ description: "Prompt before running project-local agents. Default: true." })),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
function registerSubagentTool(pi: ExtensionAPI): void {
|
|
53
|
-
pi
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"Use
|
|
61
|
-
"Use
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { renderTakomiSubagentCall, renderTakomiSubagentResult } from "./native-render";
|
|
4
|
+
import { createTakomiPiSubagentsEngine } from "./pi-subagents-engine";
|
|
5
|
+
|
|
6
|
+
const ChecklistItemSchema = Type.Object({
|
|
7
|
+
text: Type.String(),
|
|
8
|
+
done: Type.Optional(Type.Boolean()),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const ThinkingSchema = Type.Union([
|
|
12
|
+
Type.Literal("off"),
|
|
13
|
+
Type.Literal("minimal"),
|
|
14
|
+
Type.Literal("low"),
|
|
15
|
+
Type.Literal("medium"),
|
|
16
|
+
Type.Literal("high"),
|
|
17
|
+
Type.Literal("xhigh"),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const TaskSchema = Type.Object({
|
|
21
|
+
agent: Type.String(),
|
|
22
|
+
task: Type.String(),
|
|
23
|
+
workflow: Type.Optional(Type.String()),
|
|
24
|
+
skills: Type.Optional(Type.Array(Type.String())),
|
|
25
|
+
model: Type.Optional(Type.String()),
|
|
26
|
+
fallbackModels: Type.Optional(Type.Array(Type.String())),
|
|
27
|
+
thinking: Type.Optional(ThinkingSchema),
|
|
28
|
+
conversationId: Type.Optional(Type.String()),
|
|
29
|
+
cwd: Type.Optional(Type.String()),
|
|
30
|
+
checklist: Type.Optional(Type.Array(Type.Union([Type.String(), ChecklistItemSchema]))),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const SubagentParameters = Type.Object({
|
|
34
|
+
agent: Type.Optional(Type.String({ description: "Agent name for single execution" })),
|
|
35
|
+
task: Type.Optional(Type.String({ description: "Task for single execution" })),
|
|
36
|
+
workflow: Type.Optional(Type.String({ description: "Workflow or playbook overlay for this task" })),
|
|
37
|
+
skills: Type.Optional(Type.Array(Type.String(), { description: "Extra skills to apply during the task" })),
|
|
38
|
+
model: Type.Optional(Type.String({ description: "Optional per-run model override" })),
|
|
39
|
+
fallbackModels: Type.Optional(Type.Array(Type.String(), { description: "Optional ordered model fallback list" })),
|
|
40
|
+
thinking: Type.Optional(ThinkingSchema),
|
|
41
|
+
conversationId: Type.Optional(Type.String({ description: "Persistent conversation id to resume the same subagent session" })),
|
|
42
|
+
cwd: Type.Optional(Type.String({ description: "Working directory override" })),
|
|
43
|
+
checklist: Type.Optional(Type.Array(Type.Union([Type.String(), ChecklistItemSchema]), { description: "Optional checklist for the subagent" })),
|
|
44
|
+
tasks: Type.Optional(Type.Array(TaskSchema, { description: "Parallel subagent tasks" })),
|
|
45
|
+
confirmLaunch: Type.Optional(Type.Boolean({ description: "Required to launch immediately in manual Takomi launch mode" })),
|
|
46
|
+
previewOnly: Type.Optional(Type.Boolean({ description: "Return the delegation plan without launching" })),
|
|
47
|
+
chain: Type.Optional(Type.Array(TaskSchema, { description: "Sequential chain of subagent tasks" })),
|
|
48
|
+
agentScope: Type.Optional(Type.Union([Type.Literal("user"), Type.Literal("project"), Type.Literal("both")])),
|
|
49
|
+
confirmProjectAgents: Type.Optional(Type.Boolean({ description: "Prompt before running project-local agents. Default: true." })),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function registerSubagentTool(pi: ExtensionAPI): void {
|
|
53
|
+
const engine = createTakomiPiSubagentsEngine(pi);
|
|
54
|
+
pi.registerTool({
|
|
55
|
+
name: "takomi_subagent",
|
|
56
|
+
label: "Takomi",
|
|
57
|
+
description: "Run subagents with Pi-style single, parallel, or chain modes plus Takomi lifecycle metadata.",
|
|
58
|
+
promptSnippet: "Delegate lifecycle-aware Takomi work to specialist subagents. Use single, tasks, or chain; reuse conversationId for review loops.",
|
|
59
|
+
promptGuidelines: [
|
|
60
|
+
"Use this tool during orchestration when a specialist should handle a task.",
|
|
61
|
+
"Use tasks for independent parallel work and chain for dependent handoffs with {previous}.",
|
|
62
|
+
"Use model, fallbackModels, and thinking only when deliberate; otherwise let the agent/profile defaults apply.",
|
|
63
|
+
"If review sends work back to the same agent, reuse the same conversationId for continuity.",
|
|
64
|
+
],
|
|
65
|
+
parameters: SubagentParameters,
|
|
66
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
67
|
+
return engine.execute(toolCallId, params, signal, onUpdate as any, ctx);
|
|
68
|
+
},
|
|
69
|
+
renderCall: renderTakomiSubagentCall,
|
|
70
|
+
renderResult: renderTakomiSubagentResult,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default function takomiSubagents(pi: ExtensionAPI) {
|
|
75
|
+
registerSubagentTool(pi);
|
|
76
|
+
}
|
|
@@ -1,83 +1,136 @@
|
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
results[index]
|
|
80
|
-
|
|
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 mergeRecentTools(
|
|
20
|
+
existing: Array<{ tool: string; args: string; endMs: number }> | undefined,
|
|
21
|
+
incoming: Array<{ tool: string; args: string; endMs: number }> | undefined,
|
|
22
|
+
): Array<{ tool: string; args: string; endMs: number }> | undefined {
|
|
23
|
+
if (!incoming?.length) return existing;
|
|
24
|
+
return incoming.slice(-8);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function appendLine(value: string, line: string): string {
|
|
28
|
+
const next = [value, line.trim()].filter(Boolean).join("\n");
|
|
29
|
+
return next.split(/\r?\n/).slice(-12).join("\n");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function compactOutputPreview(text: string): string[] {
|
|
33
|
+
const lines = text
|
|
34
|
+
.split(/\r?\n/)
|
|
35
|
+
.map((line) => line.replace(/\s+/g, " ").trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.filter((line) => !/^\*\*[^*]+\*\*\s+I\s/i.test(line))
|
|
38
|
+
.filter((line) => !/\bI need to make sure\b/i.test(line))
|
|
39
|
+
.filter((line) => !/\bI want the user to\b/i.test(line));
|
|
40
|
+
return lines.slice(-3);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createTakomiLiveUpdateBridge(
|
|
44
|
+
tasks: LiveTask[],
|
|
45
|
+
mode: "single" | "parallel" | "chain",
|
|
46
|
+
agentScope: string,
|
|
47
|
+
onUpdate: ToolUpdate | undefined,
|
|
48
|
+
) {
|
|
49
|
+
const results: TakomiDispatchResult[] = tasks.map((task, index) => ({
|
|
50
|
+
agent: task.agent,
|
|
51
|
+
task: task.task,
|
|
52
|
+
workflow: task.workflow,
|
|
53
|
+
model: task.model,
|
|
54
|
+
thinking: task.thinking,
|
|
55
|
+
conversationId: task.conversationId ?? `pending-${index + 1}`,
|
|
56
|
+
code: -1,
|
|
57
|
+
output: "Queued.",
|
|
58
|
+
stderr: "",
|
|
59
|
+
preflight: "",
|
|
60
|
+
startedAt: Date.now(),
|
|
61
|
+
lastActivityAt: Date.now(),
|
|
62
|
+
recentTools: [],
|
|
63
|
+
recentOutput: ["Queued."],
|
|
64
|
+
toolCount: 0,
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
const emit = () => {
|
|
68
|
+
const done = results.filter((result) => result.code === 0).length;
|
|
69
|
+
const running = results.filter((result) => result.code === -1).length;
|
|
70
|
+
onUpdate?.({
|
|
71
|
+
content: [{ type: "text", text: `Takomi ${mode}: ${done}/${results.length} done, ${running} running` }],
|
|
72
|
+
details: { results: [...results], mode, agentScope },
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
results,
|
|
78
|
+
event(index: number, event: TakomiSubagentRuntimeEvent): void {
|
|
79
|
+
const current = results[index];
|
|
80
|
+
if (!current) return;
|
|
81
|
+
current.lastActivityAt = Date.now();
|
|
82
|
+
if (event.type === "start") {
|
|
83
|
+
current.startedAt = current.startedAt ?? Date.now();
|
|
84
|
+
current.conversationId = event.state.conversationId ?? current.conversationId;
|
|
85
|
+
current.thinking = event.state.thinking ?? current.thinking;
|
|
86
|
+
current.sessionFile = event.state.sessionFile ?? current.sessionFile;
|
|
87
|
+
current.output = event.state.summary ?? "Starting.";
|
|
88
|
+
current.recentOutput = [current.output].filter(Boolean).slice(-8);
|
|
89
|
+
} else if (event.type === "update") {
|
|
90
|
+
current.model = event.patch.model ?? current.model;
|
|
91
|
+
current.thinking = event.patch.thinking ?? current.thinking;
|
|
92
|
+
current.sessionFile = event.patch.sessionFile ?? current.sessionFile;
|
|
93
|
+
current.currentTool = event.patch.currentTool;
|
|
94
|
+
current.currentToolArgs = event.patch.currentToolArgs;
|
|
95
|
+
current.currentToolStartedAt = event.patch.currentToolStartedAt;
|
|
96
|
+
current.recentTools = mergeRecentTools(current.recentTools, event.patch.recentTools);
|
|
97
|
+
current.toolCount = event.patch.toolCount ?? current.toolCount;
|
|
98
|
+
current.output = event.patch.outputText ?? event.patch.summary ?? current.output;
|
|
99
|
+
current.recentOutput = event.patch.recentOutput
|
|
100
|
+
?? (event.patch.outputText ? compactOutputPreview(event.patch.outputText) : current.recentOutput);
|
|
101
|
+
if (event.patch.logs?.length) current.output = appendLine(current.output, event.patch.logs.join("\n"));
|
|
102
|
+
} else if (event.type === "appendLog") {
|
|
103
|
+
current.output = appendLine(current.output, event.chunk);
|
|
104
|
+
current.recentOutput = [...(current.recentOutput ?? []), event.chunk.trim()].filter(Boolean).slice(-8);
|
|
105
|
+
} else if (event.type === "complete") {
|
|
106
|
+
current.code = 0;
|
|
107
|
+
current.endedAt = Date.now();
|
|
108
|
+
current.currentTool = undefined;
|
|
109
|
+
current.currentToolArgs = undefined;
|
|
110
|
+
current.currentToolStartedAt = undefined;
|
|
111
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
112
|
+
current.recentOutput = event.patch?.recentOutput ?? current.recentOutput;
|
|
113
|
+
current.recentTools = mergeRecentTools(current.recentTools, event.patch?.recentTools);
|
|
114
|
+
current.toolCount = event.patch?.toolCount ?? current.toolCount;
|
|
115
|
+
current.sessionFile = event.patch?.sessionFile ?? current.sessionFile;
|
|
116
|
+
} else if (event.type === "block") {
|
|
117
|
+
current.code = 1;
|
|
118
|
+
current.endedAt = Date.now();
|
|
119
|
+
current.currentTool = undefined;
|
|
120
|
+
current.currentToolArgs = undefined;
|
|
121
|
+
current.currentToolStartedAt = undefined;
|
|
122
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
123
|
+
current.recentOutput = event.patch?.recentOutput ?? current.recentOutput;
|
|
124
|
+
current.recentTools = mergeRecentTools(current.recentTools, event.patch?.recentTools);
|
|
125
|
+
current.toolCount = event.patch?.toolCount ?? current.toolCount;
|
|
126
|
+
current.sessionFile = event.patch?.sessionFile ?? current.sessionFile;
|
|
127
|
+
if (event.patch?.logs?.length) current.stderr = event.patch.logs.join("\n");
|
|
128
|
+
}
|
|
129
|
+
emit();
|
|
130
|
+
},
|
|
131
|
+
finish(index: number, result: TakomiDispatchResult): void {
|
|
132
|
+
results[index] = result;
|
|
133
|
+
emit();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import { renderSubagentResult, syncResultAnimation } from "pi-subagents/src/tui/render";
|
|
2
2
|
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
3
|
-
import type { Details
|
|
3
|
+
import type { Details } from "pi-subagents/src/shared/types";
|
|
4
4
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import {
|
|
6
|
-
import type { TakomiDispatchResult } from "./dispatch";
|
|
5
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
7
6
|
import type { TakomiSubagentToolParams } from "./tool-runner";
|
|
8
7
|
|
|
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
|
-
};
|
|
8
|
+
type ToolResult = AgentToolResult<Details>;
|
|
19
9
|
|
|
20
10
|
function taskList(params: TakomiSubagentToolParams): Array<{ agent: string; task: string }> {
|
|
21
11
|
if (params.chain?.length) return params.chain;
|
|
@@ -41,134 +31,7 @@ export function renderTakomiSubagentCall(params: TakomiSubagentToolParams, theme
|
|
|
41
31
|
);
|
|
42
32
|
}
|
|
43
33
|
|
|
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
34
|
export function renderTakomiSubagentResult(result: ToolResult, options: { expanded?: boolean; isPartial?: boolean }, theme: Theme, context: any) {
|
|
104
|
-
|
|
105
|
-
|
|
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);
|
|
35
|
+
syncResultAnimation(result, context);
|
|
36
|
+
return renderSubagentResult(result, { expanded: options.expanded ?? false }, theme);
|
|
173
37
|
}
|
|
174
|
-
|