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.
- package/.pi/extensions/oauth-router/README.md +3 -4
- package/.pi/extensions/oauth-router/commands.ts +1 -5
- package/.pi/extensions/oauth-router/config.ts +46 -49
- package/.pi/extensions/oauth-router/index.ts +1 -4
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +5 -5
- package/.pi/extensions/takomi-context-manager/index.ts +18 -8
- package/.pi/extensions/takomi-runtime/index.ts +23 -15
- package/package.json +1 -1
- package/src/pi-harness.js +41 -1
- package/src/takomi-stats.js +5 -2
|
@@ -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.
|
|
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-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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-
|
|
19
|
-
name: "GPT-
|
|
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:
|
|
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:
|
|
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.
|
|
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:
|
|
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-
|
|
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.
|
|
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-
|
|
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
|
|
31
|
-
|
|
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 =
|
|
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
|
|
44
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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',
|
package/src/takomi-stats.js
CHANGED
|
@@ -16,8 +16,11 @@ const pc = {
|
|
|
16
16
|
magenta: ansi(35, 39),
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const PRICES = {
|
|
20
|
-
'gpt-5.
|
|
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],
|