takomi 2.1.43 → 2.1.45
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 +4 -3
- package/.pi/extensions/oauth-router/README.md +3 -3
- package/.pi/extensions/oauth-router/commands.ts +66 -39
- package/.pi/extensions/oauth-router/config.ts +34 -34
- package/.pi/extensions/oauth-router/index.ts +51 -10
- package/.pi/extensions/oauth-router/report-ui.ts +205 -0
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +5 -5
- package/.pi/extensions/takomi-context-manager/diagnostics-tools.ts +41 -3
- package/.pi/extensions/takomi-context-manager/diagnostics.ts +26 -0
- package/.pi/extensions/takomi-context-manager/index.ts +20 -9
- package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +36 -15
- package/.pi/extensions/takomi-context-manager/policy-tools.ts +93 -42
- package/.pi/extensions/takomi-context-manager/skill-categories.d.ts +11 -0
- package/.pi/extensions/takomi-context-manager/skill-categories.js +212 -0
- package/.pi/extensions/takomi-context-manager/skill-registry.ts +179 -8
- package/.pi/extensions/takomi-context-manager/skill-tools.ts +208 -103
- package/.pi/extensions/takomi-context-manager/tool-renderers.ts +112 -0
- package/.pi/extensions/takomi-context-manager/types.ts +6 -0
- package/.pi/extensions/takomi-runtime/commands.ts +45 -12
- package/.pi/extensions/takomi-runtime/gate-provenance.ts +24 -0
- package/.pi/extensions/takomi-runtime/index.ts +133 -56
- package/.pi/extensions/takomi-runtime/routing-policy.ts +14 -2
- package/.pi/extensions/takomi-runtime/takomi-stats.js +46 -26
- package/.pi/extensions/takomi-runtime/tool-renderers.ts +234 -0
- package/.pi/extensions/takomi-runtime/workflow-catalog.ts +54 -0
- package/.pi/extensions/takomi-subagents/agents.ts +4 -0
- package/.pi/extensions/takomi-subagents/async-lifecycle.ts +401 -0
- package/.pi/extensions/takomi-subagents/detached-results.ts +940 -0
- package/.pi/extensions/takomi-subagents/index.ts +46 -1
- package/.pi/extensions/takomi-subagents/native-render.ts +250 -188
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +55 -46
- package/.pi/extensions/takomi-subagents/pi-subagents-internal.ts +11 -5
- package/.pi/extensions/takomi-subagents/result-heartbeat.ts +55 -0
- package/.pi/extensions/takomi-subagents/subagent-ux.ts +162 -0
- package/.pi/extensions/takomi-subagents/tool-runner.ts +158 -28
- package/.pi/takomi/model-routing.md +282 -3
- package/assets/.agent/skills/shared-resend-portfolio/SKILL.md +124 -0
- package/package.json +4 -3
- package/src/pi-harness.js +41 -1
- package/src/pi-takomi-core/types.ts +10 -0
- package/src/pi-takomi-core/workflows.ts +86 -45
- package/src/skills-catalog.js +2 -202
- package/src/skills-installer.js +4 -1
- package/src/takomi-stats.js +5 -5
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { getMarkdownTheme, keyHint } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Container, Markdown, Text } from "@earendil-works/pi-tui";
|
|
3
|
+
|
|
4
|
+
type Theme = {
|
|
5
|
+
fg(color: string, text: string): string;
|
|
6
|
+
bold(text: string): string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ToolResult = {
|
|
10
|
+
content?: string | Array<{ type?: string; text?: string }>;
|
|
11
|
+
details?: unknown;
|
|
12
|
+
isError?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ResultOptions = { expanded: boolean; isError?: boolean };
|
|
16
|
+
type CardStatus = "success" | "warning" | "error" | "pending";
|
|
17
|
+
|
|
18
|
+
type ModeDetails = {
|
|
19
|
+
mode?: string;
|
|
20
|
+
source?: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
role?: string;
|
|
23
|
+
stage?: string;
|
|
24
|
+
workflow?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type RoutingDetails = {
|
|
28
|
+
result?: { detectedDefaults?: string[] };
|
|
29
|
+
preview?: { scope?: string };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type WorkflowDetails = { id?: string; title?: string; purpose?: string };
|
|
33
|
+
|
|
34
|
+
type BoardError = {
|
|
35
|
+
code: string;
|
|
36
|
+
message: string;
|
|
37
|
+
severity: "warning" | "error";
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type BoardDetails = {
|
|
41
|
+
sessionId?: string;
|
|
42
|
+
taskId?: string;
|
|
43
|
+
task?: { id?: string; status?: string };
|
|
44
|
+
tasks?: unknown[];
|
|
45
|
+
lifecycle?: unknown;
|
|
46
|
+
error?: BoardError;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type BoardArgs = {
|
|
50
|
+
action?: "init_session" | "expand_stage" | "show_workflows" | "show_session" | "update_task";
|
|
51
|
+
sessionId?: string;
|
|
52
|
+
taskId?: string;
|
|
53
|
+
stage?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Tool output is model-facing data. Sanitize only at the TUI boundary so
|
|
57
|
+
// ANSI, OSC, and C0 controls cannot alter terminal presentation.
|
|
58
|
+
function sanitizePresentation(value: string): string {
|
|
59
|
+
return value
|
|
60
|
+
.replace(/\x1B\][\s\S]*?(?:\x07|\x1B\\)/g, "") // OSC (BEL or ST terminated)
|
|
61
|
+
.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "") // CSI
|
|
62
|
+
.replace(/[\x00-\x08\x0B-\x1F\x7F-\x9F]/g, "");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resultText(result: ToolResult): string {
|
|
66
|
+
const text = typeof result.content === "string"
|
|
67
|
+
? result.content
|
|
68
|
+
: result.content?.filter((part) => part.type === "text").map((part) => part.text ?? "").join("\n") ?? "";
|
|
69
|
+
return sanitizePresentation(text);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function bounded(value: string | undefined, limit = 36): string {
|
|
73
|
+
const text = sanitizePresentation(value ?? "").replace(/\s+/g, " ").trim();
|
|
74
|
+
return text.length > limit ? `${text.slice(0, Math.max(1, limit - 1))}…` : text;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function status(result: ToolResult, options: ResultOptions, details?: { error?: BoardError }): CardStatus {
|
|
78
|
+
if (details?.error?.severity) return details.error.severity;
|
|
79
|
+
return result.isError || options.isError ? "error" : "success";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderToolCall(name: string, target: string | undefined, theme: Theme): Text {
|
|
83
|
+
return new Text(
|
|
84
|
+
`${theme.fg("toolTitle", theme.bold(`${name} `))}${target ? theme.fg("accent", bounded(target)) : ""}`,
|
|
85
|
+
0,
|
|
86
|
+
0,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderCompactCard(options: {
|
|
91
|
+
status: CardStatus;
|
|
92
|
+
title: string;
|
|
93
|
+
summary: string;
|
|
94
|
+
metadata: string;
|
|
95
|
+
}, theme: Theme): Text {
|
|
96
|
+
const presentation = {
|
|
97
|
+
success: ["✓", "success"],
|
|
98
|
+
warning: ["⚠", "warning"],
|
|
99
|
+
error: ["✗", "error"],
|
|
100
|
+
pending: ["…", "muted"],
|
|
101
|
+
} as const;
|
|
102
|
+
const [icon, color] = presentation[options.status];
|
|
103
|
+
return new Text([
|
|
104
|
+
`${theme.fg(color, icon)} ${theme.fg("accent", theme.bold(bounded(options.title)))} ${theme.fg("muted", bounded(options.summary, 72))}`,
|
|
105
|
+
theme.fg("dim", `${bounded(options.metadata, 64)} · ${keyHint("app.tools.expand", "view details")}`),
|
|
106
|
+
].join("\n"), 0, 0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function renderExpandedCard(options: {
|
|
110
|
+
status: CardStatus;
|
|
111
|
+
title: string;
|
|
112
|
+
summary: string;
|
|
113
|
+
metadata: string[];
|
|
114
|
+
markdown: string;
|
|
115
|
+
}, theme: Theme): Container {
|
|
116
|
+
const presentation = {
|
|
117
|
+
success: ["✓", "success"],
|
|
118
|
+
warning: ["⚠", "warning"],
|
|
119
|
+
error: ["✗", "error"],
|
|
120
|
+
pending: ["…", "muted"],
|
|
121
|
+
} as const;
|
|
122
|
+
const [icon, color] = presentation[options.status];
|
|
123
|
+
const container = new Container();
|
|
124
|
+
container.addChild(new Text(
|
|
125
|
+
`${theme.fg(color, icon)} ${theme.fg("accent", theme.bold(sanitizePresentation(options.title)))} ${theme.fg("muted", sanitizePresentation(options.summary))}`,
|
|
126
|
+
0,
|
|
127
|
+
0,
|
|
128
|
+
));
|
|
129
|
+
for (const detail of options.metadata) container.addChild(new Text(theme.fg("dim", sanitizePresentation(detail)), 0, 0));
|
|
130
|
+
container.addChild(new Text(theme.fg("dim", keyHint("app.tools.expand", "collapse")), 0, 0));
|
|
131
|
+
container.addChild(new Markdown(sanitizePresentation(options.markdown), 0, 1, getMarkdownTheme()));
|
|
132
|
+
return container;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function renderTakomiModeCall(args: { mode?: string } | undefined, theme: Theme): Text {
|
|
136
|
+
return renderToolCall("takomi_mode", args?.mode ?? "mode", theme);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function renderTakomiModeResult(result: ToolResult, options: ResultOptions, theme: Theme): Container | Text {
|
|
140
|
+
const details = result.details as ModeDetails | undefined;
|
|
141
|
+
const primary = details?.stage ?? details?.role ?? details?.mode ?? "mode";
|
|
142
|
+
const source = details?.source ?? "model";
|
|
143
|
+
const metadata = [source, details?.workflow ? `wf:${details.workflow}` : "footer updated"].join(" · ");
|
|
144
|
+
if (!options.expanded) {
|
|
145
|
+
return renderCompactCard({ status: status(result, options), title: "Takomi mode", summary: `${source}:${primary}`, metadata }, theme);
|
|
146
|
+
}
|
|
147
|
+
return renderExpandedCard({
|
|
148
|
+
status: status(result, options),
|
|
149
|
+
title: "Takomi mode",
|
|
150
|
+
summary: `${source}:${primary}`,
|
|
151
|
+
metadata: [metadata],
|
|
152
|
+
markdown: resultText(result),
|
|
153
|
+
}, theme);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function renderTakomiRoutingCall(args: { scope?: string } | undefined, theme: Theme): Text {
|
|
157
|
+
return renderToolCall("takomi_apply_routing_policy", args?.scope ?? "global", theme);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function renderTakomiRoutingResult(result: ToolResult, options: ResultOptions, theme: Theme): Container | Text {
|
|
161
|
+
const details = result.details as RoutingDetails | undefined;
|
|
162
|
+
const scope = details?.preview?.scope ?? "global";
|
|
163
|
+
const detectedCount = details?.result?.detectedDefaults?.length ?? 0;
|
|
164
|
+
const summary = status(result, options) === "error" ? "policy was not saved" : `${scope} policy saved`;
|
|
165
|
+
const metadata = `${detectedCount} routing defaults detected`;
|
|
166
|
+
if (!options.expanded) return renderCompactCard({ status: status(result, options), title: "Takomi routing", summary, metadata }, theme);
|
|
167
|
+
return renderExpandedCard({
|
|
168
|
+
status: status(result, options),
|
|
169
|
+
title: "Takomi routing",
|
|
170
|
+
summary,
|
|
171
|
+
metadata: [`Scope: ${scope}`, metadata],
|
|
172
|
+
markdown: resultText(result),
|
|
173
|
+
}, theme);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function renderTakomiWorkflowCall(args: { workflow?: string } | undefined, theme: Theme): Text {
|
|
177
|
+
return renderToolCall("takomi_workflow", args?.workflow ?? "library", theme);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function renderTakomiWorkflowResult(result: ToolResult, options: ResultOptions, theme: Theme): Container | Text {
|
|
181
|
+
const details = result.details as WorkflowDetails | undefined;
|
|
182
|
+
const selected = details?.id;
|
|
183
|
+
const title = selected ? "Takomi workflow" : "Workflow library";
|
|
184
|
+
const summary = selected ? (details?.title ?? selected) : "embedded lifecycle playbooks";
|
|
185
|
+
const metadata = selected ? bounded(selected) : "genesis · design · build";
|
|
186
|
+
if (!options.expanded) return renderCompactCard({ status: status(result, options), title, summary, metadata }, theme);
|
|
187
|
+
return renderExpandedCard({ status: status(result, options), title, summary, metadata: [metadata], markdown: resultText(result) }, theme);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function boardPresentation(args: BoardArgs, details: BoardDetails | undefined): { title: string; summary: string; metadata: string } {
|
|
191
|
+
switch (args.action) {
|
|
192
|
+
case "show_workflows":
|
|
193
|
+
return { title: "Workflow library", summary: "available lifecycle playbooks", metadata: "genesis · design · build" };
|
|
194
|
+
case "show_session":
|
|
195
|
+
return { title: "Takomi session", summary: "session loaded", metadata: `session:${bounded(args.sessionId ?? details?.sessionId, 24)}` };
|
|
196
|
+
case "update_task":
|
|
197
|
+
return {
|
|
198
|
+
title: `Task ${bounded(args.taskId ?? details?.taskId ?? details?.task?.id, 24) || "update"}`,
|
|
199
|
+
summary: details?.task?.status ? `status: ${details.task.status}` : "task updated",
|
|
200
|
+
metadata: `session:${bounded(args.sessionId ?? details?.sessionId, 24)}`,
|
|
201
|
+
};
|
|
202
|
+
case "expand_stage":
|
|
203
|
+
return {
|
|
204
|
+
title: `${args.stage ?? "Lifecycle"} stage`,
|
|
205
|
+
summary: "stage expanded",
|
|
206
|
+
metadata: `session:${bounded(args.sessionId ?? details?.sessionId, 24)}`,
|
|
207
|
+
};
|
|
208
|
+
default:
|
|
209
|
+
return {
|
|
210
|
+
title: "Takomi board",
|
|
211
|
+
summary: "orchestrator session created",
|
|
212
|
+
metadata: `session:${bounded(details?.sessionId ?? args.sessionId, 24)}`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function renderTakomiBoardCall(args: BoardArgs | undefined, theme: Theme): Text {
|
|
218
|
+
return renderToolCall("takomi_board", args?.action ?? "init_session", theme);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function renderTakomiBoardResult(result: ToolResult, options: ResultOptions, theme: Theme, args?: BoardArgs): Container | Text {
|
|
222
|
+
const details = result.details as BoardDetails | undefined;
|
|
223
|
+
const presentation = boardPresentation(args ?? {}, details);
|
|
224
|
+
const cardStatus = status(result, options, details);
|
|
225
|
+
const summary = cardStatus === "success" ? presentation.summary : details?.error?.message ?? "action needs attention";
|
|
226
|
+
if (!options.expanded) return renderCompactCard({ status: cardStatus, title: presentation.title, summary, metadata: presentation.metadata }, theme);
|
|
227
|
+
return renderExpandedCard({
|
|
228
|
+
status: cardStatus,
|
|
229
|
+
title: presentation.title,
|
|
230
|
+
summary,
|
|
231
|
+
metadata: [presentation.metadata],
|
|
232
|
+
markdown: resultText(result),
|
|
233
|
+
}, theme);
|
|
234
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getWorkflowDefinition,
|
|
3
|
+
listWorkflowCatalog,
|
|
4
|
+
type TakomiWorkflowId,
|
|
5
|
+
type WorkflowCatalogEntry,
|
|
6
|
+
type WorkflowDefinition,
|
|
7
|
+
} from "../../../src/pi-takomi-core";
|
|
8
|
+
|
|
9
|
+
type WorkflowCatalogPayload = {
|
|
10
|
+
content: Array<{ type: "text"; text: string }>;
|
|
11
|
+
details: { workflows: readonly WorkflowCatalogEntry[] };
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type WorkflowPlaybookPayload = {
|
|
15
|
+
content: Array<{ type: "text"; text: string }>;
|
|
16
|
+
details: WorkflowDefinition & { workflows: readonly WorkflowCatalogEntry[] };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function formatCatalogEntry(workflow: WorkflowCatalogEntry): string {
|
|
20
|
+
return `${workflow.id} (${workflow.stage}) — ${workflow.name}: ${workflow.description} [${workflow.availability}]`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Discovery/load adapter for the direct workflow API. */
|
|
24
|
+
export function discoverWorkflowPlaybooks(workflowId?: TakomiWorkflowId): WorkflowPlaybookPayload | WorkflowCatalogPayload {
|
|
25
|
+
if (workflowId) {
|
|
26
|
+
const workflow = getWorkflowDefinition(workflowId);
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: `${workflow.title}\n\n${workflow.playbook}` }],
|
|
29
|
+
details: { ...workflow, workflows: listWorkflowCatalog() },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const workflows = listWorkflowCatalog();
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: workflows.map(formatCatalogEntry).join("\n") }],
|
|
36
|
+
details: { workflows },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Lifecycle-board adapter; intentionally catalog-oriented and playbook-free. */
|
|
41
|
+
export function showWorkflowCatalogForBoard(): WorkflowCatalogPayload {
|
|
42
|
+
const workflows = listWorkflowCatalog();
|
|
43
|
+
return {
|
|
44
|
+
content: [{
|
|
45
|
+
type: "text",
|
|
46
|
+
text: [
|
|
47
|
+
"Takomi lifecycle workflow catalog",
|
|
48
|
+
"",
|
|
49
|
+
...workflows.map((workflow) => `- ${formatCatalogEntry(workflow)}`),
|
|
50
|
+
].join("\n"),
|
|
51
|
+
}],
|
|
52
|
+
details: { workflows },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -12,6 +12,7 @@ export type TakomiAgentConfig = {
|
|
|
12
12
|
model?: string;
|
|
13
13
|
fallbackModels?: string[];
|
|
14
14
|
thinking?: TakomiThinkingLevel;
|
|
15
|
+
defaultContext?: "fresh" | "fork";
|
|
15
16
|
systemPrompt: string;
|
|
16
17
|
filePath: string;
|
|
17
18
|
source: "user" | "project";
|
|
@@ -59,6 +60,9 @@ function loadAgentsFromDirectory(agentsDir: string, source: "user" | "project"):
|
|
|
59
60
|
model: frontmatter.model,
|
|
60
61
|
fallbackModels: splitList(frontmatter.fallbackModels ?? frontmatter.fallback_models),
|
|
61
62
|
thinking: normalizeThinking(frontmatter.thinking),
|
|
63
|
+
defaultContext: frontmatter.defaultContext === "fork" || frontmatter.defaultContext === "fresh"
|
|
64
|
+
? frontmatter.defaultContext
|
|
65
|
+
: undefined,
|
|
62
66
|
systemPrompt: body,
|
|
63
67
|
filePath,
|
|
64
68
|
source,
|