takomi 2.1.42 → 2.1.44

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.
@@ -41,12 +41,12 @@ The extension ships with two default upstream profiles:
41
41
  - auth mode: OAuth
42
42
  - oauth provider: `openai-codex`
43
43
  - api: `openai-codex-responses`
44
- - default models: `gpt-5.1`, `gpt-5.4`, `gpt-5.4-mini`
44
+ - default models: `gpt-5.4-mini`, `gpt-5.4`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`
45
45
 
46
46
  2. `openai-compatible`
47
47
  - auth mode: API key fallback
48
48
  - api: `openai-responses`
49
- - default models: `gpt-4o`, `gpt-4.1`, `o4-mini`
49
+ - default models: `gpt-5.4-mini`, `gpt-5.4`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`
50
50
 
51
51
  Edit `~/.pi/agent/oauth-router/config.json` to add more upstreams, swap endpoints, change model catalogs, or tune retry behavior. By default client-side transport failures such as `Codex SSE response headers timed out after 10000ms` retry the same account 5 times with exponential backoff (`5s`, `10s`, `20s`, `40s`, then capped at `60s`) before router failover. While this is happening, Pi's footer shows the active retry/failover/error state so the UI no longer looks frozen. These failures are recorded but do not cool down an account unless `clientNetworkPenaltyMs` is set above `0`.
52
52
 
@@ -62,8 +62,7 @@ Edit `~/.pi/agent/oauth-router/config.json` to add more upstreams, swap endpoint
62
62
  - `/router-status`
63
63
  - `/router-usage`
64
64
  5. Select a model:
65
- - `oauth-router/gpt-5.4`
66
- - `oauth-router/gpt-4o`
65
+ - `oauth-router/gpt-5.6-sol`
67
66
 
68
67
  ## Account storage format
69
68
 
@@ -275,11 +275,7 @@ export function formatUsageRawReport(runtime: RouterRuntime, accountId?: string)
275
275
 
276
276
  function emitReport(ctx: ExtensionCommandContext, text: string) {
277
277
  const lines = text.split(/\r?\n/);
278
- const visibleLines = [
279
- "oauth-router report (UI-only; not sent to the agent)",
280
- ...lines,
281
- ];
282
- ctx.ui.setWidget("oauth-router-report", visibleLines, { placement: "belowEditor" });
278
+ ctx.ui.setWidget("oauth-router-report", lines, { placement: "belowEditor" });
283
279
  ctx.ui.notify(lines[0] || "oauth-router report updated", "info");
284
280
  }
285
281
 
@@ -11,42 +11,15 @@ export const STATE_PATH = join(DATA_ROOT, "state.json");
11
11
 
12
12
  const LEGACY_CODEX_CONTEXT_WINDOW = 272000;
13
13
  const SAFE_CODEX_CONTEXT_WINDOW = 240000;
14
- const CODEX_MODEL_IDS = new Set(["gpt-5.1", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5"]);
14
+ const CODEX_MODEL_IDS = new Set(["gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]);
15
15
 
16
16
  const DEFAULT_MODELS: RouterModelConfig[] = [
17
17
  {
18
- id: "gpt-4o",
19
- name: "GPT-4o",
20
- reasoning: false,
21
- input: ["text", "image"],
22
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
23
- contextWindow: 128000,
24
- maxTokens: 16384,
25
- },
26
- {
27
- id: "gpt-4.1",
28
- name: "GPT-4.1",
29
- reasoning: false,
30
- input: ["text", "image"],
31
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
32
- contextWindow: 128000,
33
- maxTokens: 16384,
34
- },
35
- {
36
- id: "o4-mini",
37
- name: "o4-mini",
38
- reasoning: true,
39
- input: ["text", "image"],
40
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
41
- contextWindow: 200000,
42
- maxTokens: 100000,
43
- },
44
- {
45
- id: "gpt-5.1",
46
- name: "GPT-5.1",
18
+ id: "gpt-5.4-mini",
19
+ name: "GPT-5.4 Mini",
47
20
  reasoning: true,
48
21
  input: ["text", "image"],
49
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
22
+ cost: { input: 0.75, output: 4.50, cacheRead: 0.075, cacheWrite: 0.9375 },
50
23
  contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
51
24
  maxTokens: 128000,
52
25
  },
@@ -55,28 +28,46 @@ const DEFAULT_MODELS: RouterModelConfig[] = [
55
28
  name: "GPT-5.4",
56
29
  reasoning: true,
57
30
  input: ["text", "image"],
58
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
31
+ cost: { input: 2.50, output: 15.00, cacheRead: 0.25, cacheWrite: 3.125 },
59
32
  contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
60
33
  maxTokens: 128000,
61
34
  },
62
- {
63
- id: "gpt-5.4-mini",
64
- name: "GPT-5.4 Mini",
65
- reasoning: true,
66
- input: ["text", "image"],
67
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
68
- contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
69
- maxTokens: 128000,
70
- },
71
- {
72
- id: "gpt-5.5",
35
+ {
36
+ id: "gpt-5.5",
73
37
  name: "GPT-5.5",
74
38
  reasoning: true,
75
39
  input: ["text", "image"],
76
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
40
+ cost: { input: 5.00, output: 30.00, cacheRead: 0.50, cacheWrite: 6.25 },
77
41
  contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
78
- maxTokens: 128000,
79
- },
42
+ maxTokens: 128000,
43
+ },
44
+ {
45
+ id: "gpt-5.6-luna",
46
+ name: "GPT-5.6 Luna",
47
+ reasoning: true,
48
+ input: ["text", "image"],
49
+ cost: { input: 1.00, output: 6.00, cacheRead: 0.10, cacheWrite: 1.25 },
50
+ contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
51
+ maxTokens: 128000,
52
+ },
53
+ {
54
+ id: "gpt-5.6-sol",
55
+ name: "GPT-5.6 Sol",
56
+ reasoning: true,
57
+ input: ["text", "image"],
58
+ cost: { input: 5.00, output: 30.00, cacheRead: 0.50, cacheWrite: 6.25 },
59
+ contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
60
+ maxTokens: 128000,
61
+ },
62
+ {
63
+ id: "gpt-5.6-terra",
64
+ name: "GPT-5.6 Terra",
65
+ reasoning: true,
66
+ input: ["text", "image"],
67
+ cost: { input: 2.50, output: 15.00, cacheRead: 0.25, cacheWrite: 3.125 },
68
+ contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
69
+ maxTokens: 128000,
70
+ },
80
71
  ];
81
72
 
82
73
  const DEFAULT_UPSTREAMS: RouterUpstreamConfig[] = [
@@ -88,7 +79,7 @@ const DEFAULT_UPSTREAMS: RouterUpstreamConfig[] = [
88
79
  api: "openai-responses",
89
80
  authMode: "api-key",
90
81
  enabled: true,
91
- modelIds: ["gpt-4o", "gpt-4.1", "o4-mini"],
82
+ modelIds: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"],
92
83
  },
93
84
  {
94
85
  id: "chatgpt-codex",
@@ -99,7 +90,7 @@ const DEFAULT_UPSTREAMS: RouterUpstreamConfig[] = [
99
90
  authMode: "oauth",
100
91
  oauthProviderId: "openai-codex",
101
92
  enabled: true,
102
- modelIds: ["gpt-5.1", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5"],
93
+ modelIds: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"],
103
94
  usageProbe: {
104
95
  enabled: true,
105
96
  timeoutMs: 8_000,
@@ -209,8 +200,12 @@ function mergeModelConfigs(candidateModels: RouterModelConfig[] | undefined): Ro
209
200
  return deepClone(DEFAULT_CONFIG.models);
210
201
  }
211
202
 
203
+ const defaultIds = new Set(DEFAULT_CONFIG.models.map((m) => m.id));
212
204
  const merged = new Map(DEFAULT_CONFIG.models.map((model) => [model.id, deepClone(model)]));
213
205
  for (const model of candidateModels) {
206
+ if (!defaultIds.has(model.id)) {
207
+ continue;
208
+ }
214
209
  const previous = merged.get(model.id) ?? ({} as RouterModelConfig);
215
210
  merged.set(model.id, { ...previous, ...deepClone(model), id: model.id });
216
211
  }
@@ -227,6 +222,7 @@ function mergeUpstreamConfigs(candidateUpstreams: RouterUpstreamConfig[] | undef
227
222
  return deepClone(DEFAULT_CONFIG.upstreams);
228
223
  }
229
224
 
225
+ const defaultModelIds = new Set(DEFAULT_CONFIG.models.map((m) => m.id));
230
226
  const merged = new Map(DEFAULT_CONFIG.upstreams.map((upstream) => [upstream.id, deepClone(upstream)]));
231
227
  for (const upstream of candidateUpstreams) {
232
228
  const previous = merged.get(upstream.id);
@@ -235,7 +231,8 @@ function mergeUpstreamConfigs(candidateUpstreams: RouterUpstreamConfig[] | undef
235
231
  continue;
236
232
  }
237
233
 
238
- const modelIds = Array.from(new Set([...(previous.modelIds ?? []), ...(upstream.modelIds ?? [])]));
234
+ const modelIds = Array.from(new Set([...(previous.modelIds ?? []), ...(upstream.modelIds ?? [])]))
235
+ .filter((id) => defaultModelIds.has(id));
239
236
  const usageProbe = {
240
237
  ...(previous.usageProbe ?? {}),
241
238
  ...(upstream.usageProbe ?? {}),
@@ -120,10 +120,7 @@ export default function (pi: ExtensionAPI) {
120
120
  description: "Show a detailed oauth-router report in the UI only",
121
121
  handler: async (_args, ctx) => {
122
122
  const report = formatStatusReport(runtime);
123
- ctx.ui.setWidget("oauth-router-report", [
124
- "oauth-router debug report (UI-only; not sent to the agent)",
125
- ...report.split(/\r?\n/),
126
- ], { placement: "belowEditor" });
123
+ ctx.ui.setWidget("oauth-router-report", report.split(/\r?\n/), { placement: "belowEditor" });
127
124
  ctx.ui.notify("oauth-router debug report updated", "info");
128
125
  },
129
126
  });
@@ -21,7 +21,7 @@ REQUIRED = [
21
21
  ROOT / "types.ts",
22
22
  ROOT / "README.md",
23
23
  ]
24
- EXPECTED_MODELS = ["oauth-router", "gpt-4o", "gpt-4.1", "o4-mini", "gpt-5.4"]
24
+ EXPECTED_MODELS = ["oauth-router", "gpt-5.4-mini", "gpt-5.4", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]
25
25
  PI_CANDIDATES = [
26
26
  os.environ.get("PI_BIN"),
27
27
  shutil.which("pi"),
@@ -77,12 +77,12 @@ def main() -> int:
77
77
  failed = True
78
78
  else:
79
79
  print("[ OK ] `pi --list-models` executed")
80
- for token in EXPECTED_MODELS:
81
- if token not in output:
80
+ for token in EXPECTED_MODELS:
81
+ if token not in output:
82
82
  print(f"[FAIL] model token not found in list output: {token}")
83
83
  failed = True
84
- else:
85
- print(f"[ OK ] model token present: {token}")
84
+ else:
85
+ print(f"[ OK ] model token present: {token}")
86
86
  except Exception as exc:
87
87
  print(f"[FAIL] unable to execute `pi --list-models`: {exc}")
88
88
  failed = True
@@ -18,6 +18,7 @@ import type { ContextManagerConfig } from "./types";
18
18
  export default function takomiContextManager(pi: ExtensionAPI) {
19
19
  const state = createState();
20
20
  let config: ContextManagerConfig = DEFAULT_CONFIG;
21
+ let duplicateExtensionWarnings: Array<{ toolName: string; paths: string[] }> = [];
21
22
 
22
23
  registerSkillTools(pi, state);
23
24
  registerPolicyTools(pi, state);
@@ -27,21 +28,30 @@ export default function takomiContextManager(pi: ExtensionAPI) {
27
28
 
28
29
  pi.on("session_start", async (_event, ctx) => {
29
30
  config = await loadConfig(ctx.cwd);
30
- state.policies = await discoverPolicies(ctx.cwd, config);
31
- state.skills = mergeSkills(await discoverSkillsFromFilesystem(ctx.cwd));
31
+ [state.policies, duplicateExtensionWarnings] = await Promise.all([
32
+ discoverPolicies(ctx.cwd, config),
33
+ detectDuplicateTakomiExtensions(ctx.cwd),
34
+ ]);
35
+
36
+ // Pi already discovers skills while building systemPromptOptions. Rewalking
37
+ // every global/project skill tree here made startup block on thousands of
38
+ // filesystem calls, then repeated the same work before the first request.
39
+ // Keep filesystem discovery lazy for direct skill-tool calls and as a
40
+ // compatibility fallback when Pi supplies no skill metadata.
41
+ state.skills = new Map();
32
42
  state.report.cwd = ctx.cwd;
33
- state.report.skillCount = state.skills.size;
43
+ state.report.skillCount = 0;
34
44
  restoreReportFromSession(state, ctx);
35
45
  });
36
46
 
37
47
  pi.on("before_agent_start", async (event, ctx) => {
38
- config = await loadConfig(ctx.cwd);
39
- state.policies = await discoverPolicies(ctx.cwd, config);
40
- const duplicateExtensionWarnings = await detectDuplicateTakomiExtensions(ctx.cwd);
41
48
  const optionSkills = collectSkillsFromOptions(event.systemPromptOptions);
42
49
  const xmlSkills = collectSkillsFromXml(event.systemPrompt);
43
- const filesystemSkills = await discoverSkillsFromFilesystem(ctx.cwd);
44
- state.skills = mergeSkills([...filesystemSkills, ...optionSkills, ...xmlSkills]);
50
+ const suppliedSkills = [...optionSkills, ...xmlSkills];
51
+ const filesystemSkills = suppliedSkills.length === 0
52
+ ? await discoverSkillsFromFilesystem(ctx.cwd)
53
+ : [];
54
+ state.skills = mergeSkills([...filesystemSkills, ...suppliedSkills]);
45
55
 
46
56
  const candidates = findCandidates(event.prompt, state.skills, config);
47
57
  const rewrite = rewritePrompt(event.systemPrompt, state.skills, candidates, config);
@@ -34,7 +34,6 @@ import {
34
34
  type VibeLifecycleStage,
35
35
  } from "../../../src/pi-takomi-core";
36
36
  import {
37
- renderRuntimeStatus,
38
37
  renderRuntimeWidget,
39
38
  renderTakomiHeader,
40
39
  TakomiFooterComponent,
@@ -689,10 +688,11 @@ function installTakomiFooter(ctx: ExtensionContext, stateRef: { current: TakomiS
689
688
  ctx.ui.setFooter((tui, theme, footerData) => new TakomiFooterComponent(tui, theme, footerData, ctx, () => stateRef.current));
690
689
  }
691
690
 
692
- // Mutable state ref so the footer closure always reads the latest state
693
- const footerStateRef: { current: TakomiState; installed: boolean } = { current: cloneState(DEFAULT_STATE), installed: false };
694
-
695
- async function refreshUi(ctx: ExtensionContext, state: TakomiState) {
691
+ async function refreshUi(
692
+ ctx: ExtensionContext,
693
+ state: TakomiState,
694
+ footerStateRef: { current: TakomiState; context?: ExtensionContext },
695
+ ) {
696
696
  if (!ctx.hasUI) return;
697
697
  ctx.ui.setTitle("Takomi");
698
698
  ctx.ui.setHeader((_tui, theme) => ({
@@ -702,17 +702,25 @@ async function refreshUi(ctx: ExtensionContext, state: TakomiState) {
702
702
  },
703
703
  }));
704
704
  footerStateRef.current = state;
705
- ctx.ui.setStatus("takomi-runtime", renderRuntimeStatus(ctx.ui.theme, state));
705
+
706
+ // The mode indicator belongs in the widget above the editor. Keeping a
707
+ // second setStatus copy makes Pi's default footer duplicate it whenever a
708
+ // custom footer is replaced or a session is rebound.
709
+ ctx.ui.setStatus("takomi-runtime", undefined);
706
710
  const widget = renderRuntimeWidget(ctx.ui.theme, state);
707
711
  ctx.ui.setWidget("takomi-runtime", widget.length > 0 ? widget : undefined);
708
- if (!footerStateRef.installed) {
712
+
713
+ // A replacement session receives a fresh UI context even when extension
714
+ // modules remain cached. Install once per context, not once per module.
715
+ if (footerStateRef.context !== ctx) {
709
716
  installTakomiFooter(ctx, footerStateRef);
710
- footerStateRef.installed = true;
717
+ footerStateRef.context = ctx;
711
718
  }
712
719
  }
713
720
 
714
721
  export default function takomiRuntime(pi: ExtensionAPI) {
715
722
  let state = cloneState(DEFAULT_STATE);
723
+ const footerStateRef: { current: TakomiState; context?: ExtensionContext } = { current: state };
716
724
  const subagentController = getTakomiSubagentController();
717
725
  const contextPanel = new TakomiContextPanel();
718
726
  let runtimeCtx: ExtensionContext | undefined;
@@ -798,7 +806,7 @@ export default function takomiRuntime(pi: ExtensionAPI) {
798
806
  mutator();
799
807
  persistState();
800
808
  syncContextPanelState();
801
- await refreshUi(ctx, state);
809
+ await refreshUi(ctx, state, footerStateRef);
802
810
  const resolvedMessage = typeof message === "function" ? message() : message;
803
811
  if (resolvedMessage) ctx.ui.notify(resolvedMessage, "info");
804
812
  }
@@ -910,7 +918,7 @@ export default function takomiRuntime(pi: ExtensionAPI) {
910
918
 
911
919
  persistState();
912
920
  syncContextPanelState();
913
- await refreshUi(ctx, state);
921
+ await refreshUi(ctx, state, footerStateRef);
914
922
  const label = state.modeSource === "idle" ? "idle" : `${state.modeSource}:${state.stage ?? state.role}`;
915
923
  return `Takomi mode set to ${label}${state.modeReason ? ` (${state.modeReason})` : ""}.`;
916
924
  }
@@ -1137,7 +1145,7 @@ ${stateJson}`
1137
1145
  state.modeReason = state.modeReason ?? "board task update";
1138
1146
  persistState();
1139
1147
  syncContextPanelState();
1140
- await refreshUi(ctx, state);
1148
+ await refreshUi(ctx, state, footerStateRef);
1141
1149
  const nextState = buildSessionState(
1142
1150
  sessionState.sessionId,
1143
1151
  sessionState.title,
@@ -1197,7 +1205,7 @@ ${stateJson}`
1197
1205
  state.modeReason = `expanded ${params.stage} stage`;
1198
1206
  persistState();
1199
1207
  syncContextPanelState();
1200
- await refreshUi(ctx, state);
1208
+ await refreshUi(ctx, state, footerStateRef);
1201
1209
 
1202
1210
  return {
1203
1211
  content: [{ type: "text", text: `Expanded ${params.stage} stage in session ${nextState.sessionId}.\n\nDocs: ${paths.root}\nState: ${paths.stateFile}\n\n${buildTaskRows(nextState.tasks)}` }],
@@ -1242,7 +1250,7 @@ ${stateJson}`
1242
1250
  state.modeReason = "orchestrator session";
1243
1251
  persistState();
1244
1252
  syncContextPanelState();
1245
- await refreshUi(ctx, state);
1253
+ await refreshUi(ctx, state, footerStateRef);
1246
1254
 
1247
1255
  return {
1248
1256
  content: [{ type: "text", text: `Created Takomi orchestrator session ${nextState.sessionId} in hybrid mode\n\nDocs: ${paths.root}\nState: ${paths.stateFile}\n\n${buildTaskRows(nextState.tasks) || "No tasks provided."}` }],
@@ -1356,7 +1364,7 @@ ${stateJson}`
1356
1364
  }
1357
1365
  persistState();
1358
1366
  syncContextPanelState();
1359
- await refreshUi(ctx, state);
1367
+ await refreshUi(ctx, state, footerStateRef);
1360
1368
 
1361
1369
  const routingPolicy = await resolveTakomiRoutingPolicy(runtimeCwd);
1362
1370
  const optionalFeatureContext = (() => {
@@ -1458,7 +1466,7 @@ ${stateJson}`
1458
1466
 
1459
1467
  syncContextPanelState();
1460
1468
  contextPanel.rebuildFromSession(ctx);
1461
- await refreshUi(ctx, state);
1469
+ await refreshUi(ctx, state, footerStateRef);
1462
1470
  contextPanel.show(ctx);
1463
1471
  flushPendingSubagentEvents();
1464
1472
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "takomi",
3
- "version": "2.1.42",
3
+ "version": "2.1.44",
4
4
  "description": "🎯 Stop wrestling with AI. Start building with purpose. The artisan's toolkit for agent workflows, Codex skills, and original Takomi capabilities like 21st.dev integration.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/pi-harness.js CHANGED
@@ -575,6 +575,45 @@ function printFirstRunGuidance(reason) {
575
575
  console.log(pc.dim(' takomi --help Show all commands\n'));
576
576
  }
577
577
 
578
+ export function getSourceCheckoutLaunchArgs(cwd) {
579
+ const packageJson = path.join(cwd, 'package.json');
580
+ const extensionsRoot = path.join(cwd, '.pi', 'extensions');
581
+ const required = [
582
+ path.join(extensionsRoot, 'oauth-router', 'index.ts'),
583
+ path.join(extensionsRoot, 'takomi-runtime', 'index.ts'),
584
+ path.join(extensionsRoot, 'takomi-subagents', 'index.ts'),
585
+ path.join(extensionsRoot, 'takomi-context-manager', 'index.ts'),
586
+ path.join(extensionsRoot, 'notify-sound', 'index.ts'),
587
+ ];
588
+ const promptsRoot = path.join(cwd, '.pi', 'prompts');
589
+ const theme = path.join(cwd, '.pi', 'themes', 'takomi-noir.json');
590
+
591
+ let isTakomiSourceCheckout = false;
592
+ try {
593
+ const pkg = fs.readJsonSync(packageJson);
594
+ isTakomiSourceCheckout = pkg?.name === 'takomi'
595
+ && required.every((entry) => fs.existsSync(entry))
596
+ && fs.existsSync(promptsRoot)
597
+ && fs.existsSync(theme);
598
+ } catch {}
599
+
600
+ if (!isTakomiSourceCheckout) return [];
601
+
602
+ // Normal Pi auto-discovery would load both the globally installed Takomi
603
+ // extensions and this checkout's project copies. Besides tool conflicts,
604
+ // both runtime instances register lifecycle/UI handlers, which causes slow
605
+ // startup and duplicated headers/widgets/footers. Match scripts/pi-dev.ps1:
606
+ // disable discovery and load exactly one local development set.
607
+ return [
608
+ '--no-extensions',
609
+ ...required.flatMap((entry) => ['--extension', entry]),
610
+ '--no-prompt-templates',
611
+ '--prompt-template', promptsRoot,
612
+ '--no-themes',
613
+ '--theme', theme,
614
+ ];
615
+ }
616
+
578
617
  export async function launchTakomiHarness(cwd = process.cwd()) {
579
618
  // Keep the common `takomi` path lean. A full environment inspection shells out
580
619
  // to `pi --version`, which costs multiple seconds on Windows before Pi even
@@ -606,7 +645,8 @@ export async function launchTakomiHarness(cwd = process.cwd()) {
606
645
  };
607
646
 
608
647
  return await new Promise((resolve) => {
609
- const resolved = resolveCommandForSpawn(pi.path || 'pi', []);
648
+ const launchArgs = getSourceCheckoutLaunchArgs(cwd);
649
+ const resolved = resolveCommandForSpawn(pi.path || 'pi', launchArgs);
610
650
  const child = spawn(resolved.command, resolved.args, {
611
651
  cwd,
612
652
  stdio: 'inherit',
@@ -16,8 +16,11 @@ const pc = {
16
16
  magenta: ansi(35, 39),
17
17
  };
18
18
 
19
- const PRICES = {
20
- 'gpt-5.5': [5.00, 0.50, 30.00],
19
+ const PRICES = {
20
+ 'gpt-5.6-luna': [1.00, 0.10, 6.00],
21
+ 'gpt-5.6-sol': [5.00, 0.50, 30.00],
22
+ 'gpt-5.6-terra': [2.50, 0.25, 15.00],
23
+ 'gpt-5.5': [5.00, 0.50, 30.00],
21
24
  'gpt-5.4': [2.50, 0.25, 15.00],
22
25
  'gpt-5.4-mini': [0.75, 0.075, 4.50],
23
26
  'gpt-5.4-nano': [0.20, 0.02, 1.25],