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.
Files changed (50) hide show
  1. package/.pi/README.md +9 -8
  2. package/.pi/agents/architect.md +2 -2
  3. package/.pi/agents/designer.md +2 -2
  4. package/.pi/agents/worker.md +32 -0
  5. package/.pi/extensions/oauth-router/commands.ts +66 -35
  6. package/.pi/extensions/oauth-router/index.ts +51 -7
  7. package/.pi/extensions/oauth-router/report-ui.ts +205 -0
  8. package/.pi/extensions/takomi-context-manager/diagnostics-tools.ts +41 -3
  9. package/.pi/extensions/takomi-context-manager/diagnostics.ts +26 -0
  10. package/.pi/extensions/takomi-context-manager/index.ts +3 -2
  11. package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +50 -117
  12. package/.pi/extensions/takomi-context-manager/policy-tools.ts +93 -42
  13. package/.pi/extensions/takomi-context-manager/skill-categories.d.ts +11 -0
  14. package/.pi/extensions/takomi-context-manager/skill-categories.js +212 -0
  15. package/.pi/extensions/takomi-context-manager/skill-registry.ts +179 -8
  16. package/.pi/extensions/takomi-context-manager/skill-tools.ts +208 -103
  17. package/.pi/extensions/takomi-context-manager/tool-renderers.ts +112 -0
  18. package/.pi/extensions/takomi-context-manager/types.ts +6 -0
  19. package/.pi/extensions/takomi-runtime/command-text.ts +5 -4
  20. package/.pi/extensions/takomi-runtime/commands.ts +58 -25
  21. package/.pi/extensions/takomi-runtime/gate-provenance.ts +24 -0
  22. package/.pi/extensions/takomi-runtime/index.ts +385 -124
  23. package/.pi/extensions/takomi-runtime/model-routing-defaults.ts +262 -326
  24. package/.pi/extensions/takomi-runtime/profile.ts +9 -8
  25. package/.pi/extensions/takomi-runtime/routing-policy.ts +97 -66
  26. package/.pi/extensions/takomi-runtime/shared.ts +7 -30
  27. package/.pi/extensions/takomi-runtime/takomi-stats.js +46 -26
  28. package/.pi/extensions/takomi-runtime/tool-renderers.ts +234 -0
  29. package/.pi/extensions/takomi-runtime/workflow-catalog.ts +54 -0
  30. package/.pi/extensions/takomi-subagents/agents.ts +9 -0
  31. package/.pi/extensions/takomi-subagents/async-lifecycle.ts +401 -0
  32. package/.pi/extensions/takomi-subagents/detached-results.ts +940 -0
  33. package/.pi/extensions/takomi-subagents/index.ts +50 -1
  34. package/.pi/extensions/takomi-subagents/native-render.ts +250 -188
  35. package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +62 -47
  36. package/.pi/extensions/takomi-subagents/pi-subagents-internal.ts +11 -5
  37. package/.pi/extensions/takomi-subagents/result-heartbeat.ts +55 -0
  38. package/.pi/extensions/takomi-subagents/subagent-ux.ts +162 -0
  39. package/.pi/extensions/takomi-subagents/tool-runner.ts +198 -29
  40. package/.pi/settings.json +39 -36
  41. package/.pi/takomi/model-routing.md +288 -3
  42. package/.pi/takomi-profile.json +54 -50
  43. package/assets/.agent/skills/shared-resend-portfolio/SKILL.md +124 -0
  44. package/package.json +4 -3
  45. package/src/pi-takomi-core/orchestration.ts +39 -21
  46. package/src/pi-takomi-core/routing.ts +8 -8
  47. package/src/pi-takomi-core/types.ts +35 -5
  48. package/src/pi-takomi-core/workflows.ts +86 -45
  49. package/src/skills-catalog.js +2 -202
  50. 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 { applyTakomiRoutingDefaults, loadTakomiModelRoutingSnapshot } from "../takomi-runtime/model-routing-defaults";
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
- function createRunFingerprint(rootCwd: string, mode: "single" | "parallel" | "chain" | undefined, tasks: TakomiSubagentToolTask[]): string {
104
- return JSON.stringify({ rootCwd, mode, tasks: tasks.map(compactTaskForFingerprint) });
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(pi: ExtensionAPI, fingerprint: string, reason: string, message: string): void {
116
- hardStopStore(pi).set(fingerprint, { at: Date.now(), reason, message });
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
- ...nativeResult,
392
+ ...resolvedResult,
271
393
  details: {
272
- ...(nativeResult?.details ?? {}),
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 validateTaskCwds(rootCwd, rawTasks);
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
- }, routingSnapshot));
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
- if (recentHardStop) {
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
- const projectAgentsTrusted = hostTrustsProjectAgents();
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" && !params.confirmLaunch) {
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
- onUpdate as any,
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
- "subagents": {
7
- "agentOverrides": {
8
- "general": {
9
- "model": "oauth-router/gpt-5.4",
10
- "thinking": "high",
11
- "fallbackModels": [
12
- "oauth-router/gpt-5.5:low"
13
- ]
14
- },
15
- "orchestrator": {
16
- "model": "oauth-router/gpt-5.5",
17
- "thinking": "high"
18
- },
19
- "architect": {
20
- "model": "oauth-router/gpt-5.5",
21
- "thinking": "high"
22
- },
23
- "designer": {
24
- "model": "oauth-router/gpt-5.4",
25
- "thinking": "high",
26
- "fallbackModels": [
27
- "oauth-router/gpt-5.5:low"
28
- ]
29
- },
30
- "coder": {
31
- "model": "oauth-router/gpt-5.4",
32
- "thinking": "high",
33
- "fallbackModels": [
34
- "oauth-router/gpt-5.5:low"
35
- ]
36
- },
37
- "reviewer": {
38
- "model": "oauth-router/gpt-5.5",
39
- "thinking": "high"
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
  }