takomi 2.1.44 → 2.5.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/.pi/README.md +9 -8
- package/.pi/agents/architect.md +2 -2
- package/.pi/agents/designer.md +2 -2
- package/.pi/agents/worker.md +32 -0
- package/.pi/extensions/oauth-router/commands.ts +66 -35
- package/.pi/extensions/oauth-router/index.ts +51 -7
- package/.pi/extensions/oauth-router/report-ui.ts +205 -0
- 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 +3 -2
- package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +50 -117
- 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/command-text.ts +5 -4
- package/.pi/extensions/takomi-runtime/commands.ts +58 -25
- package/.pi/extensions/takomi-runtime/gate-provenance.ts +24 -0
- package/.pi/extensions/takomi-runtime/index.ts +385 -124
- package/.pi/extensions/takomi-runtime/model-routing-defaults.ts +262 -326
- package/.pi/extensions/takomi-runtime/profile.ts +9 -8
- package/.pi/extensions/takomi-runtime/routing-policy.ts +97 -66
- package/.pi/extensions/takomi-runtime/shared.ts +7 -30
- 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 +9 -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 +50 -1
- package/.pi/extensions/takomi-subagents/native-render.ts +250 -188
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +62 -47
- 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 +198 -29
- package/.pi/settings.json +39 -36
- package/.pi/takomi/model-routing.md +288 -3
- package/.pi/takomi-profile.json +54 -50
- package/assets/.agent/skills/shared-resend-portfolio/SKILL.md +124 -0
- package/package.json +4 -3
- package/src/pi-takomi-core/orchestration.ts +39 -21
- package/src/pi-takomi-core/routing.ts +8 -8
- package/src/pi-takomi-core/types.ts +35 -5
- package/src/pi-takomi-core/workflows.ts +86 -45
- package/src/skills-catalog.js +2 -202
- package/src/skills-installer.js +4 -1
|
@@ -3,13 +3,17 @@ import path from "node:path";
|
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import type { TakomiLaunchMode, TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
5
5
|
import { loadTakomiProfile } from "../takomi-runtime/profile";
|
|
6
|
-
import {
|
|
6
|
+
import { hasUserGateAutoProvenance } from "../takomi-runtime/gate-provenance";
|
|
7
|
+
import { applyTakomiRoutingDefaults, isTakomiModelApproved, loadTakomiModelRoutingSnapshot } from "../takomi-runtime/model-routing-defaults";
|
|
7
8
|
import { resolveAgentName } from "./agent-aliases";
|
|
8
9
|
import { discoverTakomiAgents, type TakomiAgentConfig, type TakomiAgentScope } from "./agents";
|
|
9
10
|
import { createTakomiDelegationPlan, renderTakomiDelegationPlan } from "./delegation-plan";
|
|
11
|
+
import { rememberDetachedLaunch, resolveDetachedStatusResult } from "./detached-results";
|
|
10
12
|
import { createTakomiPiSubagentsEngine } from "./pi-subagents-engine";
|
|
13
|
+
import { createTakomiUxTasks, withTakomiUxDetails } from "./subagent-ux";
|
|
11
14
|
|
|
12
15
|
type ChecklistItem = string | { text: string; done?: boolean };
|
|
16
|
+
export type TakomiAcceptanceInput = false | "auto" | "none" | "attested" | "checked" | "verified" | "reviewed" | Record<string, unknown>;
|
|
13
17
|
|
|
14
18
|
export type TakomiSubagentToolTask = {
|
|
15
19
|
agent: string;
|
|
@@ -22,6 +26,8 @@ export type TakomiSubagentToolTask = {
|
|
|
22
26
|
conversationId?: string;
|
|
23
27
|
cwd?: string;
|
|
24
28
|
checklist?: ChecklistItem[];
|
|
29
|
+
requiredCapabilities?: string[];
|
|
30
|
+
acceptance?: TakomiAcceptanceInput;
|
|
25
31
|
};
|
|
26
32
|
|
|
27
33
|
export type TakomiSubagentToolParams = Partial<TakomiSubagentToolTask> & {
|
|
@@ -50,10 +56,18 @@ const MAX_PARALLEL_TASKS = 8;
|
|
|
50
56
|
const HARD_STOP_TTL_MS = 10 * 60 * 1000;
|
|
51
57
|
const ENGINES = new WeakMap<ExtensionAPI, ReturnType<typeof createTakomiPiSubagentsEngine>>();
|
|
52
58
|
|
|
59
|
+
type UserTurnMarker = {
|
|
60
|
+
sessionId?: string;
|
|
61
|
+
count: number;
|
|
62
|
+
lastEntryId?: string;
|
|
63
|
+
entryIds: string[];
|
|
64
|
+
};
|
|
65
|
+
|
|
53
66
|
type HardStopRecord = {
|
|
54
67
|
at: number;
|
|
55
68
|
reason: string;
|
|
56
69
|
message: string;
|
|
70
|
+
userTurnMarker?: UserTurnMarker;
|
|
57
71
|
};
|
|
58
72
|
|
|
59
73
|
const RECENT_HARD_STOPS = new WeakMap<ExtensionAPI, Map<string, HardStopRecord>>();
|
|
@@ -66,6 +80,11 @@ function getEngine(pi: ExtensionAPI): ReturnType<typeof createTakomiPiSubagentsE
|
|
|
66
80
|
return engine;
|
|
67
81
|
}
|
|
68
82
|
|
|
83
|
+
export function invalidateTakomiPiSubagentsEngine(pi: ExtensionAPI): void {
|
|
84
|
+
ENGINES.get(pi)?.dispose();
|
|
85
|
+
ENGINES.delete(pi);
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
function textResult<TDetails extends Record<string, unknown>>(text: string, details: TDetails, isError?: boolean) {
|
|
70
89
|
return { content: [{ type: "text" as const, text }], details, isError };
|
|
71
90
|
}
|
|
@@ -74,10 +93,27 @@ function hasProjectAgents(tasks: Array<{ agent: string }>, agents: Map<string, T
|
|
|
74
93
|
return tasks.some((task) => agents.get(task.agent)?.source === "project");
|
|
75
94
|
}
|
|
76
95
|
|
|
96
|
+
function taskRequiresWrite(task: TakomiSubagentToolTask): boolean {
|
|
97
|
+
if (task.requiredCapabilities?.some((capability) => /^(write|edit|write-docs|write-code)$/i.test(capability))) return true;
|
|
98
|
+
return /\b(?:create|write|author|edit|modify|update|implement|fix)\b[\s\S]{0,100}\b(?:file|files|markdown|document|documents|artifact|artifacts|code|configuration)\b/i.test(task.task);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function capabilityMismatch(task: TakomiSubagentToolTask, agent: TakomiAgentConfig | undefined): string | undefined {
|
|
102
|
+
if (!agent) return undefined;
|
|
103
|
+
if (taskRequiresWrite(task) && !agent.tools?.some((tool) => tool === "write" || tool === "edit")) {
|
|
104
|
+
return `Task assigned to '${task.agent}' requires file writing, but that persona is inspection-only. Choose architect or designer for their authored Markdown, coder for code, or worker for other writable artifacts.`;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
77
109
|
function hostTrustsProjectAgents(): boolean {
|
|
78
110
|
return /^(1|true|yes)$/i.test(process.env.TAKOMI_TRUST_PROJECT_AGENTS || "");
|
|
79
111
|
}
|
|
80
112
|
|
|
113
|
+
function isProjectAgentApprovalHardStop(record: HardStopRecord | undefined): boolean {
|
|
114
|
+
return record?.reason === "project-agent-approval-required" || record?.reason === "project-agent-denied";
|
|
115
|
+
}
|
|
116
|
+
|
|
81
117
|
function hardStopStore(pi: ExtensionAPI): Map<string, HardStopRecord> {
|
|
82
118
|
const existing = RECENT_HARD_STOPS.get(pi);
|
|
83
119
|
if (existing) return existing;
|
|
@@ -86,22 +122,79 @@ function hardStopStore(pi: ExtensionAPI): Map<string, HardStopRecord> {
|
|
|
86
122
|
return next;
|
|
87
123
|
}
|
|
88
124
|
|
|
125
|
+
type TakomiRunMode = "single" | "parallel" | "chain";
|
|
126
|
+
|
|
127
|
+
function compactChecklistForFingerprint(checklist: ChecklistItem[] | undefined): Array<{ text: string; done: boolean }> | undefined {
|
|
128
|
+
if (!checklist?.length) return undefined;
|
|
129
|
+
return checklist.map((item) => typeof item === "string" ? { text: item, done: false } : { text: item.text, done: item.done ?? false });
|
|
130
|
+
}
|
|
131
|
+
|
|
89
132
|
function compactTaskForFingerprint(task: TakomiSubagentToolTask): Record<string, unknown> {
|
|
90
133
|
return {
|
|
91
134
|
agent: task.agent,
|
|
92
135
|
task: task.task,
|
|
93
|
-
workflow: task.workflow,
|
|
94
|
-
skills: task.skills,
|
|
95
|
-
model: task.model,
|
|
96
|
-
fallbackModels: task.fallbackModels,
|
|
136
|
+
workflow: task.workflow || undefined,
|
|
137
|
+
skills: task.skills?.length ? task.skills : undefined,
|
|
138
|
+
model: task.model || undefined,
|
|
139
|
+
fallbackModels: task.fallbackModels?.length ? task.fallbackModels : undefined,
|
|
97
140
|
thinking: task.thinking,
|
|
98
|
-
conversationId: task.conversationId,
|
|
141
|
+
conversationId: task.conversationId || undefined,
|
|
99
142
|
cwd: task.cwd,
|
|
143
|
+
checklist: compactChecklistForFingerprint(task.checklist),
|
|
144
|
+
requiredCapabilities: task.requiredCapabilities?.length ? task.requiredCapabilities : undefined,
|
|
145
|
+
acceptance: task.acceptance,
|
|
100
146
|
};
|
|
101
147
|
}
|
|
102
148
|
|
|
103
|
-
|
|
104
|
-
|
|
149
|
+
const IMPLICIT_CONTEXT = "implicit" as const;
|
|
150
|
+
type CanonicalContext = typeof IMPLICIT_CONTEXT | "fresh" | "fork";
|
|
151
|
+
|
|
152
|
+
function canonicalContextForFingerprint(context: TakomiSubagentToolParams["context"]): CanonicalContext {
|
|
153
|
+
// Omission delegates context selection to native pi-subagents, whose built-in
|
|
154
|
+
// and settings defaults are authoritative. Never approximate that resolution
|
|
155
|
+
// with Takomi-only discovery: implicit input is its own approval semantic.
|
|
156
|
+
return context ?? IMPLICIT_CONTEXT;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function canonicalContextsForFingerprint(
|
|
160
|
+
params: TakomiSubagentToolParams,
|
|
161
|
+
tasks: TakomiSubagentToolTask[],
|
|
162
|
+
): CanonicalContext[] {
|
|
163
|
+
const context = canonicalContextForFingerprint(params.context);
|
|
164
|
+
// `context` is a global override. When omitted, each native task resolves its
|
|
165
|
+
// own default, but all retain the distinct implicit-input sentinel here.
|
|
166
|
+
return tasks.map(() => context);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function effectiveConcurrencyForFingerprint(mode: TakomiRunMode, concurrency: number | undefined): number | undefined {
|
|
170
|
+
if (mode !== "parallel") return undefined;
|
|
171
|
+
return typeof concurrency === "number" && Number.isInteger(concurrency) && concurrency >= 1 ? concurrency : 4;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function createRunFingerprint(
|
|
175
|
+
rootCwd: string,
|
|
176
|
+
mode: TakomiRunMode,
|
|
177
|
+
tasks: TakomiSubagentToolTask[],
|
|
178
|
+
params: TakomiSubagentToolParams,
|
|
179
|
+
agentScope: TakomiAgentScope,
|
|
180
|
+
): string {
|
|
181
|
+
const context = canonicalContextForFingerprint(params.context);
|
|
182
|
+
return JSON.stringify({
|
|
183
|
+
rootCwd,
|
|
184
|
+
mode,
|
|
185
|
+
tasks: tasks.map(compactTaskForFingerprint),
|
|
186
|
+
launch: {
|
|
187
|
+
context: {
|
|
188
|
+
global: context,
|
|
189
|
+
perTask: canonicalContextsForFingerprint(params, tasks),
|
|
190
|
+
},
|
|
191
|
+
async: params.async === true,
|
|
192
|
+
concurrency: effectiveConcurrencyForFingerprint(mode, params.concurrency),
|
|
193
|
+
worktree: mode === "parallel" && params.worktree === true,
|
|
194
|
+
clarify: params.clarify === true,
|
|
195
|
+
agentScope,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
105
198
|
}
|
|
106
199
|
|
|
107
200
|
function hardStopResult(message: string, details: Record<string, unknown>) {
|
|
@@ -112,8 +205,34 @@ function hardStopResult(message: string, details: Record<string, unknown>) {
|
|
|
112
205
|
].join("\n"), { ...details, takomiHardStop: true }, true);
|
|
113
206
|
}
|
|
114
207
|
|
|
115
|
-
function rememberHardStop(
|
|
116
|
-
|
|
208
|
+
function rememberHardStop(
|
|
209
|
+
pi: ExtensionAPI,
|
|
210
|
+
fingerprint: string,
|
|
211
|
+
reason: string,
|
|
212
|
+
message: string,
|
|
213
|
+
userTurnMarker?: UserTurnMarker,
|
|
214
|
+
): void {
|
|
215
|
+
hardStopStore(pi).set(fingerprint, { at: Date.now(), reason, message, userTurnMarker });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function readUserTurnMarker(ctx: ExtensionContext): UserTurnMarker {
|
|
219
|
+
const userEntries = ctx.sessionManager.getEntries().filter((entry) => entry.type === "message" && entry.message.role === "user");
|
|
220
|
+
const entryIds = userEntries.map((entry) => entry.id);
|
|
221
|
+
return {
|
|
222
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
223
|
+
count: userEntries.length,
|
|
224
|
+
lastEntryId: entryIds.at(-1),
|
|
225
|
+
entryIds,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function hasStrictlyNewerUserTurn(record: HardStopRecord, current: UserTurnMarker): boolean {
|
|
230
|
+
const recorded = record.userTurnMarker;
|
|
231
|
+
if (!recorded || recorded.sessionId !== current.sessionId || current.count <= recorded.count) return false;
|
|
232
|
+
if (!recorded.lastEntryId) return true;
|
|
233
|
+
|
|
234
|
+
const recordedTurnIndex = current.entryIds.indexOf(recorded.lastEntryId);
|
|
235
|
+
return recordedTurnIndex >= 0 && recordedTurnIndex < current.entryIds.length - 1;
|
|
117
236
|
}
|
|
118
237
|
|
|
119
238
|
function consumeExpiredHardStop(pi: ExtensionAPI, fingerprint: string): HardStopRecord | undefined {
|
|
@@ -146,12 +265,6 @@ async function resolveRelativeCwd(root: string, value: string | undefined, label
|
|
|
146
265
|
return realCandidate;
|
|
147
266
|
}
|
|
148
267
|
|
|
149
|
-
async function validateTaskCwds(root: string, tasks: TakomiSubagentToolTask[]): Promise<void> {
|
|
150
|
-
for (const [index, task] of tasks.entries()) {
|
|
151
|
-
await resolveRelativeCwd(root, task.cwd, `tasks[${index}].cwd`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
268
|
function getTextContent(result: any): string {
|
|
156
269
|
return (result?.content ?? [])
|
|
157
270
|
.map((item: any) => item?.type === "text" && typeof item.text === "string" ? item.text : "")
|
|
@@ -234,6 +347,8 @@ function resolveTasks(params: TakomiSubagentToolParams): TakomiSubagentToolTask[
|
|
|
234
347
|
conversationId: params.conversationId,
|
|
235
348
|
cwd: undefined,
|
|
236
349
|
checklist: params.checklist,
|
|
350
|
+
requiredCapabilities: params.requiredCapabilities,
|
|
351
|
+
acceptance: params.acceptance,
|
|
237
352
|
}];
|
|
238
353
|
}
|
|
239
354
|
return [];
|
|
@@ -255,6 +370,10 @@ export async function executeTakomiSubagentTool(
|
|
|
255
370
|
}
|
|
256
371
|
const profile = await loadTakomiProfile(rootCwd);
|
|
257
372
|
const runtimeLaunchMode = readRuntimeLaunchMode(ctx);
|
|
373
|
+
const userTurnMarker = readUserTurnMarker(ctx);
|
|
374
|
+
// Auto launch mode may come from a model, profile, default, or restored
|
|
375
|
+
// runtime state. None of those are project-agent authorization.
|
|
376
|
+
const userGateAutoAuthorized = hasUserGateAutoProvenance(ctx.sessionManager.getEntries());
|
|
258
377
|
const agentScope = params.agentScope ?? "both";
|
|
259
378
|
|
|
260
379
|
if (params.action) {
|
|
@@ -266,10 +385,13 @@ export async function executeTakomiSubagentTool(
|
|
|
266
385
|
onUpdate as any,
|
|
267
386
|
ctx,
|
|
268
387
|
);
|
|
388
|
+
const resolvedResult = params.action === "status"
|
|
389
|
+
? await resolveDetachedStatusResult(pi, params, nativeResult)
|
|
390
|
+
: nativeResult;
|
|
269
391
|
return {
|
|
270
|
-
...
|
|
392
|
+
...resolvedResult,
|
|
271
393
|
details: {
|
|
272
|
-
...(
|
|
394
|
+
...(resolvedResult?.details ?? {}),
|
|
273
395
|
takomi: {
|
|
274
396
|
action: params.action,
|
|
275
397
|
agentScope,
|
|
@@ -289,16 +411,39 @@ export async function executeTakomiSubagentTool(
|
|
|
289
411
|
let tasks: TakomiSubagentToolTask[];
|
|
290
412
|
try {
|
|
291
413
|
const rawTasks = resolveTasks(params);
|
|
292
|
-
await
|
|
293
|
-
tasks = rawTasks.map((task) => applyTakomiRoutingDefaults({
|
|
414
|
+
tasks = await Promise.all(rawTasks.map(async (task, index) => applyTakomiRoutingDefaults({
|
|
294
415
|
...task,
|
|
295
416
|
agent: resolveAgentName(task.agent, byName),
|
|
296
|
-
|
|
417
|
+
cwd: await resolveRelativeCwd(rootCwd, task.cwd, `tasks[${index}].cwd`),
|
|
418
|
+
}, routingSnapshot)));
|
|
297
419
|
} catch (error) {
|
|
298
420
|
const message = error instanceof Error ? error.message : String(error);
|
|
299
421
|
return textResult(message, { results: [], availableAgents: agents.map((agent) => agent.name), agentScope }, true);
|
|
300
422
|
}
|
|
301
423
|
|
|
424
|
+
for (const task of tasks) {
|
|
425
|
+
if (!byName.has(task.agent)) {
|
|
426
|
+
return textResult(
|
|
427
|
+
`Unknown or hidden Takomi persona '${task.agent}'. Available personas: ${agents.map((agent) => agent.name).join(", ") || "none"}.`,
|
|
428
|
+
{ results: [], agentScope, task, reason: "unknown-persona" },
|
|
429
|
+
true,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
const mismatch = capabilityMismatch(task, byName.get(task.agent));
|
|
433
|
+
if (mismatch) return textResult(`Blocked by Takomi capability validation.\n\n${mismatch}`, { results: [], agentScope, task, reason: "capability-mismatch" }, true);
|
|
434
|
+
if (task.model && routingSnapshot.approvedModels.length && !isTakomiModelApproved(task.model, routingSnapshot.approvedModels)) {
|
|
435
|
+
return textResult(
|
|
436
|
+
`Blocked by Takomi routing policy. Exact model '${task.model}' is not approved. No provider-equivalent substitution was attempted.`,
|
|
437
|
+
{ results: [], agentScope, task, reason: "model-not-approved", approvedModels: routingSnapshot.approvedModels },
|
|
438
|
+
true,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
const invalidFallback = task.fallbackModels?.find((model) => routingSnapshot.approvedModels.length && !isTakomiModelApproved(model, routingSnapshot.approvedModels));
|
|
442
|
+
if (invalidFallback) {
|
|
443
|
+
return textResult(`Blocked by Takomi routing policy. Explicit fallback '${invalidFallback}' is not approved.`, { results: [], agentScope, task, reason: "fallback-not-approved" }, true);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
302
447
|
if (!mode) {
|
|
303
448
|
return textResult(
|
|
304
449
|
`Provide exactly one mode: agent/task, tasks, or chain.\nAvailable agents: ${agents.map((agent) => `${agent.name} (${agent.source})`).join(", ") || "none"}`,
|
|
@@ -310,17 +455,23 @@ export async function executeTakomiSubagentTool(
|
|
|
310
455
|
return textResult(`Too many parallel tasks (${tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`, { results: [], agentScope }, true);
|
|
311
456
|
}
|
|
312
457
|
|
|
313
|
-
const fingerprint = createRunFingerprint(rootCwd, mode, tasks);
|
|
458
|
+
const fingerprint = createRunFingerprint(rootCwd, mode, tasks, params, agentScope);
|
|
459
|
+
const projectAgentsAuthorized = userGateAutoAuthorized || hostTrustsProjectAgents();
|
|
314
460
|
const recentHardStop = consumeExpiredHardStop(pi, fingerprint);
|
|
315
|
-
|
|
461
|
+
const authorizationOverridesHardStop = projectAgentsAuthorized && isProjectAgentApprovalHardStop(recentHardStop);
|
|
462
|
+
const consumesReviewGate = recentHardStop?.reason === "review-gate"
|
|
463
|
+
&& params.confirmLaunch === true
|
|
464
|
+
&& params.previewOnly !== true
|
|
465
|
+
&& hasStrictlyNewerUserTurn(recentHardStop, userTurnMarker);
|
|
466
|
+
if (recentHardStop && !authorizationOverridesHardStop && !consumesReviewGate) {
|
|
316
467
|
return hardStopResult(
|
|
317
468
|
`Subagent launch blocked: the same request was already stopped (${recentHardStop.reason}).\n${recentHardStop.message}`,
|
|
318
469
|
{ results: [], availableAgents: agents.map((agent) => agent.name), agentScope, mode, blockedAt: recentHardStop.at, reason: recentHardStop.reason },
|
|
319
470
|
);
|
|
320
471
|
}
|
|
472
|
+
if (authorizationOverridesHardStop || consumesReviewGate) hardStopStore(pi).delete(fingerprint);
|
|
321
473
|
|
|
322
|
-
|
|
323
|
-
if (!projectAgentsTrusted && hasProjectAgents(tasks, byName)) {
|
|
474
|
+
if (!projectAgentsAuthorized && hasProjectAgents(tasks, byName)) {
|
|
324
475
|
const names = tasks.map((task) => byName.get(task.agent)).filter((agent): agent is TakomiAgentConfig => agent?.source === "project").map((agent) => agent.name);
|
|
325
476
|
const uniqueNames = [...new Set(names)].join(", ");
|
|
326
477
|
if (!ctx.hasUI) {
|
|
@@ -356,9 +507,9 @@ export async function executeTakomiSubagentTool(
|
|
|
356
507
|
if (params.previewOnly) {
|
|
357
508
|
return textResult(renderTakomiDelegationPlan(plan), { plan, availableAgents: agents.map((agent) => agent.name), agentScope, mode });
|
|
358
509
|
}
|
|
359
|
-
if (plan.launchMode === "manual" && !
|
|
510
|
+
if (plan.launchMode === "manual" && !consumesReviewGate) {
|
|
360
511
|
const message = `${renderTakomiDelegationPlan(plan)}\n\nReview gate is awaiting explicit user approval.`;
|
|
361
|
-
rememberHardStop(pi, fingerprint, "review-gate", "Review gate displayed a delegation plan and paused before launch.");
|
|
512
|
+
rememberHardStop(pi, fingerprint, "review-gate", "Review gate displayed a delegation plan and paused before launch.", userTurnMarker);
|
|
362
513
|
return hardStopResult(message, { plan, availableAgents: agents.map((agent) => agent.name), agentScope, mode });
|
|
363
514
|
}
|
|
364
515
|
try {
|
|
@@ -367,15 +518,33 @@ export async function executeTakomiSubagentTool(
|
|
|
367
518
|
: mode === "parallel"
|
|
368
519
|
? { ...params, cwd: rootCwd, tasks, agentScope }
|
|
369
520
|
: { ...params, cwd: rootCwd, chain: tasks, agentScope };
|
|
521
|
+
const uxTasks = createTakomiUxTasks(tasks);
|
|
522
|
+
const nativeOnUpdate = onUpdate
|
|
523
|
+
? (partial: any) => onUpdate({
|
|
524
|
+
...partial,
|
|
525
|
+
details: withTakomiUxDetails(partial?.details, uxTasks),
|
|
526
|
+
})
|
|
527
|
+
: undefined;
|
|
370
528
|
|
|
371
529
|
const nativeResult: any = await engine.execute(
|
|
372
530
|
"takomi-tool",
|
|
373
531
|
nativeParams,
|
|
374
532
|
signal,
|
|
375
|
-
|
|
533
|
+
nativeOnUpdate as any,
|
|
376
534
|
ctx,
|
|
377
535
|
);
|
|
378
536
|
|
|
537
|
+
if (params.async === true) {
|
|
538
|
+
await rememberDetachedLaunch(
|
|
539
|
+
pi,
|
|
540
|
+
nativeResult,
|
|
541
|
+
uxTasks,
|
|
542
|
+
ctx,
|
|
543
|
+
rootCwd,
|
|
544
|
+
mode === "single" ? tasks[0]?.cwd ?? rootCwd : rootCwd,
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
379
548
|
const takomi = {
|
|
380
549
|
plan,
|
|
381
550
|
agentScope,
|
|
@@ -390,7 +559,7 @@ export async function executeTakomiSubagentTool(
|
|
|
390
559
|
return {
|
|
391
560
|
...nativeResult,
|
|
392
561
|
details: {
|
|
393
|
-
...(nativeResult?.details
|
|
562
|
+
...withTakomiUxDetails(nativeResult?.details, uxTasks),
|
|
394
563
|
takomi,
|
|
395
564
|
},
|
|
396
565
|
};
|
package/.pi/settings.json
CHANGED
|
@@ -1,42 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"theme": "takomi-noir",
|
|
3
3
|
"takomi": {
|
|
4
|
-
"modelRoutingPolicyFile": ".pi/takomi/model-routing.md"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
4
|
+
"modelRoutingPolicyFile": ".pi/takomi/model-routing.md",
|
|
5
|
+
"routing": {
|
|
6
|
+
"defaultProvider": "openai-codex",
|
|
7
|
+
"approvedModels": [
|
|
8
|
+
"openai-codex/gpt-5.6-luna",
|
|
9
|
+
"openai-codex/gpt-5.6-sol",
|
|
10
|
+
"openai-codex/gpt-5.6-terra"
|
|
11
|
+
],
|
|
12
|
+
"roleDefaults": {
|
|
13
|
+
"orchestrator": {
|
|
14
|
+
"model": "openai-codex/gpt-5.6-sol",
|
|
15
|
+
"thinking": "high"
|
|
16
|
+
},
|
|
17
|
+
"architect": {
|
|
18
|
+
"model": "openai-codex/gpt-5.6-sol",
|
|
19
|
+
"thinking": "high"
|
|
20
|
+
},
|
|
21
|
+
"designer": {
|
|
22
|
+
"model": "openai-codex/gpt-5.6-sol",
|
|
23
|
+
"thinking": "medium"
|
|
24
|
+
},
|
|
25
|
+
"coder": {
|
|
26
|
+
"model": "openai-codex/gpt-5.6-terra",
|
|
27
|
+
"thinking": "high",
|
|
28
|
+
"fallbackModels": [
|
|
29
|
+
"openai-codex/gpt-5.6-sol:low"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"worker": {
|
|
33
|
+
"model": "openai-codex/gpt-5.6-luna",
|
|
34
|
+
"thinking": "high",
|
|
35
|
+
"fallbackModels": [
|
|
36
|
+
"openai-codex/gpt-5.6-sol:low"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"reviewer": {
|
|
40
|
+
"model": "openai-codex/gpt-5.6-sol",
|
|
41
|
+
"thinking": "high"
|
|
42
|
+
}
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
}
|