takomi 2.1.32 → 2.1.33
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/config.ts +14 -5
- package/.pi/extensions/oauth-router/index.ts +130 -130
- package/.pi/extensions/oauth-router/provider.ts +29 -0
- package/.pi/extensions/oauth-router/state.ts +372 -372
- package/.pi/extensions/oauth-router/types.ts +1 -1
- package/.pi/extensions/takomi-runtime/model-routing-defaults.ts +296 -296
- package/assets/.agent/skills/photo-book-builder/SKILL.md +96 -0
- package/assets/.agent/skills/photo-book-builder/references/layout_templates.md +72 -0
- package/assets/.agent/skills/photo-book-builder/scripts/create_full_bleed_layouts.py +212 -0
- package/assets/.agent/skills/photo-book-builder/scripts/organize_photos.py +99 -0
- package/assets/.agent/skills/photo-book-builder/scripts/revert_organization.py +61 -0
- package/assets/.agent/skills/photo-book-builder/scripts/upscale_covers.py +47 -0
- package/assets/.agent/skills/youtube-pipeline/SKILL.md +73 -62
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Application.md +1 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Phase 1.md +86 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Phase 2.md +106 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Phase 3.md +112 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Phase 4.md +90 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Phased Outline.md +58 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Repurposing.md +1 -0
- package/assets/.agent/skills/youtube-pipeline/resources/knowledge/Research.md +438 -0
- package/assets/.agent/skills/youtube-pipeline/resources/prompts/Shorts Bridge Protocol.md +159 -0
- package/assets/.agent/skills/youtube-pipeline/resources/prompts/Title Thumbnail Picker Prompt.md +144 -0
- package/assets/.agent/skills/youtube-pipeline/resources/prompts/Transcription Extraction Prompt v2.md +190 -0
- package/assets/.agent/skills/youtube-pipeline/resources/prompts/Transcription Extraction Prompt.md +156 -0
- package/assets/.agent/skills/youtube-pipeline/resources/prompts/Video QA Prompt.md +133 -0
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-phase1-strategy.md +28 -18
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-phase2-packaging.md +2 -2
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-phase3-scripting.md +2 -2
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-phase3.5-shorts.md +2 -2
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-phase4-production.md +2 -2
- package/assets/.agent/skills/youtube-pipeline/resources/youtube-pipeline.md +15 -15
- package/assets/.agent/skills/youtube-pipeline/scripts/google-trends/package.json +17 -0
- package/assets/.agent/skills/youtube-pipeline/scripts/google-trends/pnpm-lock.yaml +31 -0
- package/assets/.agent/skills/youtube-pipeline/scripts/google-trends/search.js +168 -0
- package/package.json +1 -1
- package/src/cli.js +5 -0
- package/src/pi-optional-features.js +190 -190
- package/src/postinstall.js +27 -27
- package/src/skills-catalog.js +245 -245
- package/src/skills-installer.js +244 -244
- package/src/skills-selection-tui.js +200 -200
- package/src/store.js +418 -418
- package/src/takomi-stats.d.ts +3 -3
- package/src/takomi-stats.js +442 -35
|
@@ -9,6 +9,10 @@ export const CONFIG_PATH = join(DATA_ROOT, "config.json");
|
|
|
9
9
|
export const CREDENTIALS_PATH = join(DATA_ROOT, "credentials.json");
|
|
10
10
|
export const STATE_PATH = join(DATA_ROOT, "state.json");
|
|
11
11
|
|
|
12
|
+
const LEGACY_CODEX_CONTEXT_WINDOW = 272000;
|
|
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"]);
|
|
15
|
+
|
|
12
16
|
const DEFAULT_MODELS: RouterModelConfig[] = [
|
|
13
17
|
{
|
|
14
18
|
id: "gpt-4o",
|
|
@@ -43,7 +47,7 @@ const DEFAULT_MODELS: RouterModelConfig[] = [
|
|
|
43
47
|
reasoning: true,
|
|
44
48
|
input: ["text", "image"],
|
|
45
49
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
46
|
-
contextWindow:
|
|
50
|
+
contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
|
|
47
51
|
maxTokens: 128000,
|
|
48
52
|
},
|
|
49
53
|
{
|
|
@@ -52,7 +56,7 @@ const DEFAULT_MODELS: RouterModelConfig[] = [
|
|
|
52
56
|
reasoning: true,
|
|
53
57
|
input: ["text", "image"],
|
|
54
58
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
55
|
-
contextWindow:
|
|
59
|
+
contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
|
|
56
60
|
maxTokens: 128000,
|
|
57
61
|
},
|
|
58
62
|
{
|
|
@@ -61,7 +65,7 @@ const DEFAULT_MODELS: RouterModelConfig[] = [
|
|
|
61
65
|
reasoning: true,
|
|
62
66
|
input: ["text", "image"],
|
|
63
67
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
64
|
-
contextWindow:
|
|
68
|
+
contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
|
|
65
69
|
maxTokens: 128000,
|
|
66
70
|
},
|
|
67
71
|
{
|
|
@@ -70,7 +74,7 @@ const DEFAULT_MODELS: RouterModelConfig[] = [
|
|
|
70
74
|
reasoning: true,
|
|
71
75
|
input: ["text", "image"],
|
|
72
76
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
73
|
-
contextWindow:
|
|
77
|
+
contextWindow: SAFE_CODEX_CONTEXT_WINDOW,
|
|
74
78
|
maxTokens: 128000,
|
|
75
79
|
},
|
|
76
80
|
];
|
|
@@ -210,7 +214,12 @@ function mergeModelConfigs(candidateModels: RouterModelConfig[] | undefined): Ro
|
|
|
210
214
|
const previous = merged.get(model.id) ?? ({} as RouterModelConfig);
|
|
211
215
|
merged.set(model.id, { ...previous, ...deepClone(model), id: model.id });
|
|
212
216
|
}
|
|
213
|
-
return Array.from(merged.values())
|
|
217
|
+
return Array.from(merged.values()).map((model) => {
|
|
218
|
+
if (CODEX_MODEL_IDS.has(model.id) && model.contextWindow === LEGACY_CODEX_CONTEXT_WINDOW) {
|
|
219
|
+
return { ...model, contextWindow: SAFE_CODEX_CONTEXT_WINDOW };
|
|
220
|
+
}
|
|
221
|
+
return model;
|
|
222
|
+
});
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
function mergeUpstreamConfigs(candidateUpstreams: RouterUpstreamConfig[] | undefined): RouterUpstreamConfig[] {
|
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { registerRouterCommands, formatStatusReport } from "./commands.ts";
|
|
3
|
-
import { registerRouterProvider, RouterRuntime } from "./provider.ts";
|
|
4
|
-
import type { RouterUiEvent } from "./types.ts";
|
|
5
|
-
|
|
6
|
-
function getHealthSummary(runtime: RouterRuntime) {
|
|
7
|
-
const rows = runtime.getStatusRows();
|
|
8
|
-
const healthy = rows.filter((row) => {
|
|
9
|
-
if (!row.enabled) return false;
|
|
10
|
-
if (row.authHealth !== "ok") return false;
|
|
11
|
-
if (row.cooldownUntil && row.cooldownUntil > Date.now()) return false;
|
|
12
|
-
if (row.penaltyUntil && row.penaltyUntil > Date.now()) return false;
|
|
13
|
-
return true;
|
|
14
|
-
}).length;
|
|
15
|
-
|
|
16
|
-
return `oauth-router ${healthy}/${rows.length || 0} healthy | ${runtime.getPolicy()}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function formatDuration(ms?: number): string {
|
|
20
|
-
if (!ms || ms <= 0) return "now";
|
|
21
|
-
if (ms < 1_000) return `${Math.round(ms)}ms`;
|
|
22
|
-
return `${Math.round(ms / 1_000)}s`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function truncate(text: string | undefined, max = 110): string {
|
|
26
|
-
if (!text) return "";
|
|
27
|
-
const clean = text.replace(/\s+/g, " ").trim();
|
|
28
|
-
return clean.length > max ? `${clean.slice(0, Math.max(0, max - 1))}…` : clean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function formatRoute(event: RouterUiEvent): string {
|
|
32
|
-
const parts = [event.accountLabel || event.accountId, event.upstreamId].filter(Boolean);
|
|
33
|
-
return parts.length ? parts.join(" @ ") : "router";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function formatActivityStatus(event: RouterUiEvent): string {
|
|
37
|
-
const route = formatRoute(event);
|
|
38
|
-
const model = event.modelId ? `${event.modelId} ` : "";
|
|
39
|
-
const reason = truncate(event.message, 70);
|
|
40
|
-
|
|
41
|
-
switch (event.phase) {
|
|
42
|
-
case "attempt": {
|
|
43
|
-
const retry = event.retryAttempt && event.maxRetries
|
|
44
|
-
? ` retry ${event.retryAttempt}/${event.maxRetries}`
|
|
45
|
-
: "";
|
|
46
|
-
return `oauth-router contacting ${model}via ${route}${retry}`;
|
|
47
|
-
}
|
|
48
|
-
case "retry":
|
|
49
|
-
return `oauth-router retry ${event.retryAttempt}/${event.maxRetries} in ${formatDuration(event.delayMs)} | ${reason || event.failureKind || "network error"}`;
|
|
50
|
-
case "failover":
|
|
51
|
-
return `oauth-router failover from ${route} | ${reason || event.failureKind || "upstream error"}`;
|
|
52
|
-
case "success":
|
|
53
|
-
return `oauth-router ok: ${model}via ${route}`;
|
|
54
|
-
case "error":
|
|
55
|
-
return `oauth-router error | ${reason || event.failureKind || "request failed"}`;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function notifyActivity(ctx: ExtensionContext, event: RouterUiEvent) {
|
|
60
|
-
if (event.phase === "attempt" || event.phase === "success") return;
|
|
61
|
-
|
|
62
|
-
const message = formatActivityStatus(event);
|
|
63
|
-
const level = event.phase === "error" ? "error" : "info";
|
|
64
|
-
ctx.ui.notify(message, level);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function installRouterUiBridge(pi: ExtensionAPI, runtime: RouterRuntime, notifyOnLoad = false) {
|
|
68
|
-
let activeCtx: ExtensionContext | undefined;
|
|
69
|
-
|
|
70
|
-
const setBaseStatus = (ctx: ExtensionContext) => {
|
|
71
|
-
activeCtx = ctx;
|
|
72
|
-
if (ctx.hasUI) ctx.ui.setStatus("oauth-router", getHealthSummary(runtime));
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
runtime.setUiReporter((event) => {
|
|
76
|
-
const ctx = activeCtx;
|
|
77
|
-
if (!ctx?.hasUI) return;
|
|
78
|
-
try {
|
|
79
|
-
ctx.ui.setStatus("oauth-router", formatActivityStatus(event));
|
|
80
|
-
notifyActivity(ctx, event);
|
|
81
|
-
} catch {
|
|
82
|
-
// Best-effort UI only; never disturb provider streaming.
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
87
|
-
setBaseStatus(ctx);
|
|
88
|
-
if (notifyOnLoad) ctx.ui.notify("oauth-router loaded", "info");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
pi.on("agent_start", async (_event, ctx) => {
|
|
92
|
-
setBaseStatus(ctx);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
pi.on("turn_start", async (_event, ctx) => {
|
|
96
|
-
setBaseStatus(ctx);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
pi.on("turn_end", async (_event, ctx) => {
|
|
100
|
-
setBaseStatus(ctx);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
pi.on("agent_end", async (_event, ctx) => {
|
|
104
|
-
setBaseStatus(ctx);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
pi.on("session_shutdown", async () => {
|
|
108
|
-
activeCtx = undefined;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export default function (pi: ExtensionAPI) {
|
|
113
|
-
const runtime = new RouterRuntime();
|
|
114
|
-
|
|
115
|
-
registerRouterProvider(pi, runtime);
|
|
116
|
-
registerRouterCommands(pi, runtime);
|
|
117
|
-
installRouterUiBridge(pi, runtime, false);
|
|
118
|
-
|
|
119
|
-
pi.registerCommand("router-debug-report", {
|
|
120
|
-
description: "Emit a detailed oauth-router report",
|
|
121
|
-
handler: async () => {
|
|
122
|
-
pi.sendMessage({
|
|
123
|
-
customType: "oauth-router",
|
|
124
|
-
content: formatStatusReport(runtime),
|
|
125
|
-
display: true,
|
|
126
|
-
details: { source: "oauth-router", debug: true },
|
|
127
|
-
});
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
}
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { registerRouterCommands, formatStatusReport } from "./commands.ts";
|
|
3
|
+
import { registerRouterProvider, RouterRuntime } from "./provider.ts";
|
|
4
|
+
import type { RouterUiEvent } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
function getHealthSummary(runtime: RouterRuntime) {
|
|
7
|
+
const rows = runtime.getStatusRows();
|
|
8
|
+
const healthy = rows.filter((row) => {
|
|
9
|
+
if (!row.enabled) return false;
|
|
10
|
+
if (row.authHealth !== "ok") return false;
|
|
11
|
+
if (row.cooldownUntil && row.cooldownUntil > Date.now()) return false;
|
|
12
|
+
if (row.penaltyUntil && row.penaltyUntil > Date.now()) return false;
|
|
13
|
+
return true;
|
|
14
|
+
}).length;
|
|
15
|
+
|
|
16
|
+
return `oauth-router ${healthy}/${rows.length || 0} healthy | ${runtime.getPolicy()}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatDuration(ms?: number): string {
|
|
20
|
+
if (!ms || ms <= 0) return "now";
|
|
21
|
+
if (ms < 1_000) return `${Math.round(ms)}ms`;
|
|
22
|
+
return `${Math.round(ms / 1_000)}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function truncate(text: string | undefined, max = 110): string {
|
|
26
|
+
if (!text) return "";
|
|
27
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
28
|
+
return clean.length > max ? `${clean.slice(0, Math.max(0, max - 1))}…` : clean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatRoute(event: RouterUiEvent): string {
|
|
32
|
+
const parts = [event.accountLabel || event.accountId, event.upstreamId].filter(Boolean);
|
|
33
|
+
return parts.length ? parts.join(" @ ") : "router";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatActivityStatus(event: RouterUiEvent): string {
|
|
37
|
+
const route = formatRoute(event);
|
|
38
|
+
const model = event.modelId ? `${event.modelId} ` : "";
|
|
39
|
+
const reason = truncate(event.message, 70);
|
|
40
|
+
|
|
41
|
+
switch (event.phase) {
|
|
42
|
+
case "attempt": {
|
|
43
|
+
const retry = event.retryAttempt && event.maxRetries
|
|
44
|
+
? ` retry ${event.retryAttempt}/${event.maxRetries}`
|
|
45
|
+
: "";
|
|
46
|
+
return `oauth-router contacting ${model}via ${route}${retry}`;
|
|
47
|
+
}
|
|
48
|
+
case "retry":
|
|
49
|
+
return `oauth-router retry ${event.retryAttempt}/${event.maxRetries} in ${formatDuration(event.delayMs)} | ${reason || event.failureKind || "network error"}`;
|
|
50
|
+
case "failover":
|
|
51
|
+
return `oauth-router failover from ${route} | ${reason || event.failureKind || "upstream error"}`;
|
|
52
|
+
case "success":
|
|
53
|
+
return `oauth-router ok: ${model}via ${route}`;
|
|
54
|
+
case "error":
|
|
55
|
+
return `oauth-router error | ${reason || event.failureKind || "request failed"}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function notifyActivity(ctx: ExtensionContext, event: RouterUiEvent) {
|
|
60
|
+
if (event.phase === "attempt" || event.phase === "success") return;
|
|
61
|
+
|
|
62
|
+
const message = formatActivityStatus(event);
|
|
63
|
+
const level = event.phase === "error" ? "error" : "info";
|
|
64
|
+
ctx.ui.notify(message, level);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function installRouterUiBridge(pi: ExtensionAPI, runtime: RouterRuntime, notifyOnLoad = false) {
|
|
68
|
+
let activeCtx: ExtensionContext | undefined;
|
|
69
|
+
|
|
70
|
+
const setBaseStatus = (ctx: ExtensionContext) => {
|
|
71
|
+
activeCtx = ctx;
|
|
72
|
+
if (ctx.hasUI) ctx.ui.setStatus("oauth-router", getHealthSummary(runtime));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
runtime.setUiReporter((event) => {
|
|
76
|
+
const ctx = activeCtx;
|
|
77
|
+
if (!ctx?.hasUI) return;
|
|
78
|
+
try {
|
|
79
|
+
ctx.ui.setStatus("oauth-router", formatActivityStatus(event));
|
|
80
|
+
notifyActivity(ctx, event);
|
|
81
|
+
} catch {
|
|
82
|
+
// Best-effort UI only; never disturb provider streaming.
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
87
|
+
setBaseStatus(ctx);
|
|
88
|
+
if (notifyOnLoad) ctx.ui.notify("oauth-router loaded", "info");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
pi.on("agent_start", async (_event, ctx) => {
|
|
92
|
+
setBaseStatus(ctx);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
pi.on("turn_start", async (_event, ctx) => {
|
|
96
|
+
setBaseStatus(ctx);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
pi.on("turn_end", async (_event, ctx) => {
|
|
100
|
+
setBaseStatus(ctx);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
104
|
+
setBaseStatus(ctx);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
pi.on("session_shutdown", async () => {
|
|
108
|
+
activeCtx = undefined;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default function (pi: ExtensionAPI) {
|
|
113
|
+
const runtime = new RouterRuntime();
|
|
114
|
+
|
|
115
|
+
registerRouterProvider(pi, runtime);
|
|
116
|
+
registerRouterCommands(pi, runtime);
|
|
117
|
+
installRouterUiBridge(pi, runtime, false);
|
|
118
|
+
|
|
119
|
+
pi.registerCommand("router-debug-report", {
|
|
120
|
+
description: "Emit a detailed oauth-router report",
|
|
121
|
+
handler: async () => {
|
|
122
|
+
pi.sendMessage({
|
|
123
|
+
customType: "oauth-router",
|
|
124
|
+
content: formatStatusReport(runtime),
|
|
125
|
+
display: true,
|
|
126
|
+
details: { source: "oauth-router", debug: true },
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
@@ -124,6 +124,20 @@ function isClientNetworkFailure(lower: string): boolean {
|
|
|
124
124
|
].some((token) => lower.includes(token));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
function isContextOverflowFailure(lower: string): boolean {
|
|
128
|
+
return [
|
|
129
|
+
"context_length_exceeded",
|
|
130
|
+
"context length exceeded",
|
|
131
|
+
"context window exceeded",
|
|
132
|
+
"exceeds the context window",
|
|
133
|
+
"exceeds the model's maximum context length",
|
|
134
|
+
"exceeds model's maximum context length",
|
|
135
|
+
"maximum context length",
|
|
136
|
+
"too many tokens",
|
|
137
|
+
"token limit exceeded",
|
|
138
|
+
].some((token) => lower.includes(token));
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
function classifyFailure(
|
|
128
142
|
status: number | undefined,
|
|
129
143
|
headers: Record<string, string>,
|
|
@@ -137,6 +151,10 @@ function classifyFailure(
|
|
|
137
151
|
return { kind: "rate-limit", status: status ?? 429, retryAfterMs, message };
|
|
138
152
|
}
|
|
139
153
|
|
|
154
|
+
if (isContextOverflowFailure(lower)) {
|
|
155
|
+
return { kind: "context-overflow", status, message };
|
|
156
|
+
}
|
|
157
|
+
|
|
140
158
|
if (status === 401 || status === 403 || lower.includes("unauthorized") || lower.includes("forbidden")) {
|
|
141
159
|
return { kind: "auth", status: status ?? 401, message };
|
|
142
160
|
}
|
|
@@ -461,6 +479,11 @@ export class RouterRuntime {
|
|
|
461
479
|
this.state.recordClientNetworkFailure(accountId, failure.status, failure.message);
|
|
462
480
|
}
|
|
463
481
|
break;
|
|
482
|
+
case "context-overflow":
|
|
483
|
+
// Context-size failures are request-level, not account-health failures.
|
|
484
|
+
// Do not cool down or quarantine accounts; Pi needs the overflow error surfaced
|
|
485
|
+
// directly so its built-in compaction recovery can compact and retry.
|
|
486
|
+
break;
|
|
464
487
|
case "fatal":
|
|
465
488
|
this.state.markTransientFailure(accountId, this.config.transientPenaltyMs, failure.status ?? 500, failure.message);
|
|
466
489
|
break;
|
|
@@ -567,6 +590,12 @@ export class RouterRuntime {
|
|
|
567
590
|
}
|
|
568
591
|
|
|
569
592
|
const failure = classifyFailure(responseStatus, responseHeaders, message, this.config);
|
|
593
|
+
|
|
594
|
+
if (failure.kind === "context-overflow") {
|
|
595
|
+
outerStream.push(event);
|
|
596
|
+
return { completed: true, emittedMeaningfulOutput, failure };
|
|
597
|
+
}
|
|
598
|
+
|
|
570
599
|
this.markFailure(selection.account.id, failure);
|
|
571
600
|
|
|
572
601
|
if (!emittedMeaningfulOutput) {
|