@psnext/slingcli 2.5.20260623-1 → 2.5.20260629-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/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +5 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +2 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +51 -47
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +9 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +9 -6
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare.js +0 -18
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +15 -25
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +30 -26
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +189 -204
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +24 -21
- package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -5
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +11 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +19 -4
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +81 -7
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
- package/node_modules/@earendil-works/pi-ai/dist/images-models.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/legacy-api-aliases.js +49 -0
- package/node_modules/@earendil-works/pi-ai/dist/models.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +25 -8
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +0 -34
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +21 -9
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +85 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js +13 -13
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +1 -20
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +19 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js +72 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +19 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +11 -11
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +15 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +310 -39
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +37 -18
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +349 -77
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +7 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +6 -6
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +6 -6
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +95 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/headers.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +86 -0
- package/node_modules/@earendil-works/pi-ai/package.json +1 -33
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +13 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +15 -30
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +6 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +5 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +2 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +9 -5
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/timings.js +24 -14
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +12 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +20 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +9 -9
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +12 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/session-selector.js +14 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +37 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/rpc-entry.js +10 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-convert.js +21 -15
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-process.js +83 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/mime.js +41 -0
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
- package/node_modules/@earendil-works/pi-coding-agent/package.json +8 -5
- package/node_modules/@earendil-works/pi-orchestrator/dist/cli.js +136 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/config.js +55 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/handler.js +105 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/index.js +11 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/client.js +56 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/protocol.js +12 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/server.js +145 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/radius.js +365 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/rpc-process.js +168 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/serve.js +70 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/storage.js +58 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/supervisor.js +290 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/types.js +2 -0
- package/node_modules/@earendil-works/pi-orchestrator/package.json +45 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +3 -0
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/diagnostics_channel.d.ts +17 -20
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
- package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
- package/node_modules/brace-expansion/dist/esm/index.js +24 -14
- package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
- package/node_modules/brace-expansion/package.json +2 -2
- package/node_modules/google-auth-library/build/src/auth/googleauth.js +1 -1
- package/node_modules/google-auth-library/package.json +4 -4
- package/package.json +7 -6
- package/slingshot/index.js +302 -304
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +0 -44
|
@@ -7,6 +7,7 @@ export const XAI_MODELS = {
|
|
|
7
7
|
api: "openai-completions",
|
|
8
8
|
provider: "xai",
|
|
9
9
|
baseUrl: "https://api.x.ai/v1",
|
|
10
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
10
11
|
reasoning: false,
|
|
11
12
|
input: ["text"],
|
|
12
13
|
cost: {
|
|
@@ -24,6 +25,7 @@ export const XAI_MODELS = {
|
|
|
24
25
|
api: "openai-completions",
|
|
25
26
|
provider: "xai",
|
|
26
27
|
baseUrl: "https://api.x.ai/v1",
|
|
28
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
27
29
|
reasoning: false,
|
|
28
30
|
input: ["text"],
|
|
29
31
|
cost: {
|
|
@@ -41,6 +43,7 @@ export const XAI_MODELS = {
|
|
|
41
43
|
api: "openai-completions",
|
|
42
44
|
provider: "xai",
|
|
43
45
|
baseUrl: "https://api.x.ai/v1",
|
|
46
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
44
47
|
reasoning: false,
|
|
45
48
|
input: ["text", "image"],
|
|
46
49
|
cost: {
|
|
@@ -58,6 +61,7 @@ export const XAI_MODELS = {
|
|
|
58
61
|
api: "openai-completions",
|
|
59
62
|
provider: "xai",
|
|
60
63
|
baseUrl: "https://api.x.ai/v1",
|
|
64
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
61
65
|
reasoning: true,
|
|
62
66
|
input: ["text", "image"],
|
|
63
67
|
cost: {
|
|
@@ -75,6 +79,7 @@ export const XAI_MODELS = {
|
|
|
75
79
|
api: "openai-completions",
|
|
76
80
|
provider: "xai",
|
|
77
81
|
baseUrl: "https://api.x.ai/v1",
|
|
82
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
78
83
|
reasoning: true,
|
|
79
84
|
input: ["text", "image"],
|
|
80
85
|
cost: {
|
|
@@ -92,6 +97,7 @@ export const XAI_MODELS = {
|
|
|
92
97
|
api: "openai-completions",
|
|
93
98
|
provider: "xai",
|
|
94
99
|
baseUrl: "https://api.x.ai/v1",
|
|
100
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
95
101
|
reasoning: true,
|
|
96
102
|
input: ["text", "image"],
|
|
97
103
|
cost: {
|
|
@@ -109,6 +115,7 @@ export const XAI_MODELS = {
|
|
|
109
115
|
api: "openai-completions",
|
|
110
116
|
provider: "xai",
|
|
111
117
|
baseUrl: "https://api.x.ai/v1",
|
|
118
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false },
|
|
112
119
|
reasoning: false,
|
|
113
120
|
input: ["text"],
|
|
114
121
|
cost: {
|
|
@@ -7,7 +7,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
7
7
|
api: "openai-completions",
|
|
8
8
|
provider: "zai-coding-cn",
|
|
9
9
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
10
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai" },
|
|
10
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai" },
|
|
11
11
|
reasoning: true,
|
|
12
12
|
input: ["text"],
|
|
13
13
|
cost: {
|
|
@@ -25,7 +25,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
25
25
|
api: "openai-completions",
|
|
26
26
|
provider: "zai-coding-cn",
|
|
27
27
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
28
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
28
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
29
29
|
reasoning: true,
|
|
30
30
|
input: ["text"],
|
|
31
31
|
cost: {
|
|
@@ -43,7 +43,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
43
43
|
api: "openai-completions",
|
|
44
44
|
provider: "zai-coding-cn",
|
|
45
45
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
46
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
46
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
47
47
|
reasoning: true,
|
|
48
48
|
input: ["text"],
|
|
49
49
|
cost: {
|
|
@@ -61,7 +61,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
61
61
|
api: "openai-completions",
|
|
62
62
|
provider: "zai-coding-cn",
|
|
63
63
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
64
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
64
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
65
65
|
reasoning: true,
|
|
66
66
|
input: ["text"],
|
|
67
67
|
cost: {
|
|
@@ -79,7 +79,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
79
79
|
api: "openai-completions",
|
|
80
80
|
provider: "zai-coding-cn",
|
|
81
81
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
82
|
-
compat: { "
|
|
82
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": true, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
83
83
|
reasoning: true,
|
|
84
84
|
thinkingLevelMap: { "minimal": null, "low": "high", "medium": "high", "high": "high", "xhigh": "max" },
|
|
85
85
|
input: ["text"],
|
|
@@ -98,7 +98,7 @@ export const ZAI_CODING_CN_MODELS = {
|
|
|
98
98
|
api: "openai-completions",
|
|
99
99
|
provider: "zai-coding-cn",
|
|
100
100
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
101
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
101
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
102
102
|
reasoning: true,
|
|
103
103
|
input: ["text", "image"],
|
|
104
104
|
cost: {
|
|
@@ -7,7 +7,7 @@ export const ZAI_MODELS = {
|
|
|
7
7
|
api: "openai-completions",
|
|
8
8
|
provider: "zai",
|
|
9
9
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
10
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai" },
|
|
10
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai" },
|
|
11
11
|
reasoning: true,
|
|
12
12
|
input: ["text"],
|
|
13
13
|
cost: {
|
|
@@ -25,7 +25,7 @@ export const ZAI_MODELS = {
|
|
|
25
25
|
api: "openai-completions",
|
|
26
26
|
provider: "zai",
|
|
27
27
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
28
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
28
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
29
29
|
reasoning: true,
|
|
30
30
|
input: ["text"],
|
|
31
31
|
cost: {
|
|
@@ -43,7 +43,7 @@ export const ZAI_MODELS = {
|
|
|
43
43
|
api: "openai-completions",
|
|
44
44
|
provider: "zai",
|
|
45
45
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
46
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
46
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
47
47
|
reasoning: true,
|
|
48
48
|
input: ["text"],
|
|
49
49
|
cost: {
|
|
@@ -61,7 +61,7 @@ export const ZAI_MODELS = {
|
|
|
61
61
|
api: "openai-completions",
|
|
62
62
|
provider: "zai",
|
|
63
63
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
64
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
64
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
65
65
|
reasoning: true,
|
|
66
66
|
input: ["text"],
|
|
67
67
|
cost: {
|
|
@@ -79,7 +79,7 @@ export const ZAI_MODELS = {
|
|
|
79
79
|
api: "openai-completions",
|
|
80
80
|
provider: "zai",
|
|
81
81
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
82
|
-
compat: { "
|
|
82
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": true, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
83
83
|
reasoning: true,
|
|
84
84
|
thinkingLevelMap: { "minimal": null, "low": "high", "medium": "high", "high": "high", "xhigh": "max" },
|
|
85
85
|
input: ["text"],
|
|
@@ -98,7 +98,7 @@ export const ZAI_MODELS = {
|
|
|
98
98
|
api: "openai-completions",
|
|
99
99
|
provider: "zai",
|
|
100
100
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
101
|
-
compat: { "supportsDeveloperRole": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
101
|
+
compat: { "supportsStore": false, "supportsDeveloperRole": false, "supportsReasoningEffort": false, "thinkingFormat": "zai", "zaiToolStream": true },
|
|
102
102
|
reasoning: true,
|
|
103
103
|
input: ["text", "image"],
|
|
104
104
|
cost: {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const CHARS_PER_TOKEN = 4;
|
|
2
|
+
const ESTIMATED_IMAGE_CHARS = 4800;
|
|
3
|
+
export function calculateContextTokens(usage) {
|
|
4
|
+
return usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
5
|
+
}
|
|
6
|
+
function safeJsonStringify(value) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(value) ?? "undefined";
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return "[unserializable]";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function estimateTextAndImageContentChars(content) {
|
|
15
|
+
if (typeof content === "string")
|
|
16
|
+
return content.length;
|
|
17
|
+
let chars = 0;
|
|
18
|
+
for (const block of content)
|
|
19
|
+
chars += block.type === "text" ? block.text.length : ESTIMATED_IMAGE_CHARS;
|
|
20
|
+
return chars;
|
|
21
|
+
}
|
|
22
|
+
export function estimateTextTokens(text) {
|
|
23
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
24
|
+
}
|
|
25
|
+
export function estimateTextAndImageContentTokens(content) {
|
|
26
|
+
return Math.ceil(estimateTextAndImageContentChars(content) / CHARS_PER_TOKEN);
|
|
27
|
+
}
|
|
28
|
+
export function estimateMessageTokens(message) {
|
|
29
|
+
let chars = 0;
|
|
30
|
+
if (message.role === "user")
|
|
31
|
+
return estimateTextAndImageContentTokens(message.content);
|
|
32
|
+
if (message.role === "toolResult")
|
|
33
|
+
return estimateTextAndImageContentTokens(message.content);
|
|
34
|
+
for (const block of message.content) {
|
|
35
|
+
if (block.type === "text") {
|
|
36
|
+
chars += block.text.length;
|
|
37
|
+
}
|
|
38
|
+
else if (block.type === "thinking") {
|
|
39
|
+
chars += block.thinking.length;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
chars += block.name.length + safeJsonStringify(block.arguments).length;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
46
|
+
}
|
|
47
|
+
function getLastAssistantUsageInfo(messages) {
|
|
48
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
49
|
+
const message = messages[i];
|
|
50
|
+
if (message.role !== "assistant")
|
|
51
|
+
continue;
|
|
52
|
+
const assistant = message;
|
|
53
|
+
if (assistant.stopReason === "aborted" || assistant.stopReason === "error")
|
|
54
|
+
continue;
|
|
55
|
+
if (calculateContextTokens(assistant.usage) > 0)
|
|
56
|
+
return { usage: assistant.usage, index: i };
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function estimateMessages(messages) {
|
|
61
|
+
const usageInfo = getLastAssistantUsageInfo(messages);
|
|
62
|
+
if (usageInfo) {
|
|
63
|
+
const usageTokens = calculateContextTokens(usageInfo.usage);
|
|
64
|
+
let trailingTokens = 0;
|
|
65
|
+
for (let i = usageInfo.index + 1; i < messages.length; i++) {
|
|
66
|
+
trailingTokens += estimateMessageTokens(messages[i]);
|
|
67
|
+
}
|
|
68
|
+
return { tokens: usageTokens + trailingTokens, usageTokens, trailingTokens, lastUsageIndex: usageInfo.index };
|
|
69
|
+
}
|
|
70
|
+
let tokens = 0;
|
|
71
|
+
for (const message of messages)
|
|
72
|
+
tokens += estimateMessageTokens(message);
|
|
73
|
+
return { tokens, usageTokens: 0, trailingTokens: tokens, lastUsageIndex: null };
|
|
74
|
+
}
|
|
75
|
+
function isMessageArray(value) {
|
|
76
|
+
return Array.isArray(value);
|
|
77
|
+
}
|
|
78
|
+
export function estimateContextTokens(context) {
|
|
79
|
+
if (isMessageArray(context))
|
|
80
|
+
return estimateMessages(context);
|
|
81
|
+
const estimate = estimateMessages(context.messages);
|
|
82
|
+
if (estimate.lastUsageIndex !== null)
|
|
83
|
+
return estimate;
|
|
84
|
+
let prefixTokens = context.systemPrompt ? estimateTextTokens(context.systemPrompt) : 0;
|
|
85
|
+
if (context.tools && context.tools.length > 0) {
|
|
86
|
+
prefixTokens += estimateTextTokens(safeJsonStringify(context.tools));
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
tokens: estimate.tokens + prefixTokens,
|
|
90
|
+
usageTokens: estimate.usageTokens,
|
|
91
|
+
trailingTokens: estimate.trailingTokens + prefixTokens,
|
|
92
|
+
lastUsageIndex: estimate.lastUsageIndex,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=estimate.js.map
|
|
@@ -5,4 +5,14 @@ export function headersToRecord(headers) {
|
|
|
5
5
|
}
|
|
6
6
|
return result;
|
|
7
7
|
}
|
|
8
|
+
export function providerHeadersToRecord(headers) {
|
|
9
|
+
if (!headers)
|
|
10
|
+
return undefined;
|
|
11
|
+
const result = {};
|
|
12
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
13
|
+
if (value !== null)
|
|
14
|
+
result[key] = value;
|
|
15
|
+
}
|
|
16
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
17
|
+
}
|
|
8
18
|
//# sourceMappingURL=headers.js.map
|
|
@@ -323,9 +323,9 @@ function getAccountId(accessToken) {
|
|
|
323
323
|
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
324
324
|
}
|
|
325
325
|
function credentialsFromToken(token) {
|
|
326
|
-
const accountId = getAccountId(token.access)
|
|
326
|
+
const accountId = getAccountId(token.access);
|
|
327
327
|
if (!accountId) {
|
|
328
|
-
|
|
328
|
+
throw new Error("Failed to extract accountId from token");
|
|
329
329
|
}
|
|
330
330
|
return {
|
|
331
331
|
access: token.access,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
function buildProviderErrorPattern(patterns) {
|
|
2
|
+
return new RegExp(patterns.join("|"), "i");
|
|
3
|
+
}
|
|
4
|
+
const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
|
|
5
|
+
// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's
|
|
6
|
+
// Zen API. These are subscription/account limits, not transient throttles.
|
|
7
|
+
"GoUsageLimitError",
|
|
8
|
+
"FreeUsageLimitError",
|
|
9
|
+
// OpenCode Go subscription-limit text asks users to enable available-balance
|
|
10
|
+
// usage after rolling/weekly/monthly limits are reached.
|
|
11
|
+
"Monthly usage limit reached",
|
|
12
|
+
"available balance",
|
|
13
|
+
// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's
|
|
14
|
+
// quota/billing error code; the other strings cover common gateway wording.
|
|
15
|
+
"insufficient_quota",
|
|
16
|
+
"out of budget",
|
|
17
|
+
"quota exceeded",
|
|
18
|
+
"billing",
|
|
19
|
+
]);
|
|
20
|
+
const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
21
|
+
// Generic provider load, HTTP status, and server-side transient failures.
|
|
22
|
+
"overloaded",
|
|
23
|
+
"rate.?limit",
|
|
24
|
+
"too many requests",
|
|
25
|
+
"429",
|
|
26
|
+
"500",
|
|
27
|
+
"502",
|
|
28
|
+
"503",
|
|
29
|
+
"504",
|
|
30
|
+
"service.?unavailable",
|
|
31
|
+
"server.?error",
|
|
32
|
+
"internal.?error",
|
|
33
|
+
// Wrapper/provider text for transient upstream failures, including OpenRouter
|
|
34
|
+
// "Provider returned error" responses (#2264).
|
|
35
|
+
"provider.?returned.?error",
|
|
36
|
+
// Network, proxy, and fetch transport failures. This includes OpenAI Codex
|
|
37
|
+
// raw-fetch failures such as "upstream connect", "connection refused", and
|
|
38
|
+
// "reset before headers" (#733), plus OpenRouter connection drops (#3317).
|
|
39
|
+
"network.?error",
|
|
40
|
+
"connection.?error",
|
|
41
|
+
"connection.?refused",
|
|
42
|
+
"connection.?lost",
|
|
43
|
+
"other side closed",
|
|
44
|
+
"fetch failed",
|
|
45
|
+
"upstream.?connect",
|
|
46
|
+
"reset before headers",
|
|
47
|
+
"socket hang up",
|
|
48
|
+
"timed? out",
|
|
49
|
+
"timeout",
|
|
50
|
+
"terminated",
|
|
51
|
+
// WebSocket transports can report close/error text instead of HTTP/fetch text.
|
|
52
|
+
"websocket.?closed",
|
|
53
|
+
"websocket.?error",
|
|
54
|
+
// Premature stream endings from SDKs and transports. Anthropic can throw
|
|
55
|
+
// "stream ended without ..." and "Anthropic stream ended before message_stop"
|
|
56
|
+
// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).
|
|
57
|
+
"ended without",
|
|
58
|
+
"stream ended before message_stop",
|
|
59
|
+
"http2 request did not get a response",
|
|
60
|
+
// Provider-requested retry delay cap failures should flow through the outer
|
|
61
|
+
// retry policy so callers can surface/abort the backoff (#1123).
|
|
62
|
+
"retry delay",
|
|
63
|
+
// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock
|
|
64
|
+
// stream exceptions (#6019).
|
|
65
|
+
"you can retry your request",
|
|
66
|
+
"try your request again",
|
|
67
|
+
"please retry your request",
|
|
68
|
+
]);
|
|
69
|
+
/**
|
|
70
|
+
* Classifies whether a failed assistant message looks like a transient provider
|
|
71
|
+
* or transport error, so callers can decide if the last assistant turn should be
|
|
72
|
+
* restarted.
|
|
73
|
+
*
|
|
74
|
+
* This does not implement retry policy. Callers should first handle context
|
|
75
|
+
* overflow separately, then apply their own retry budget, backoff, and reporting
|
|
76
|
+
* before restarting the assistant turn.
|
|
77
|
+
*/
|
|
78
|
+
export function isRetryableAssistantError(message) {
|
|
79
|
+
if (message.stopReason !== "error" || !message.errorMessage)
|
|
80
|
+
return false;
|
|
81
|
+
const errorMessage = message.errorMessage;
|
|
82
|
+
if (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage))
|
|
83
|
+
return false;
|
|
84
|
+
return RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@earendil-works/pi-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.2",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,38 +27,6 @@
|
|
|
27
27
|
"types": "./dist/api/*.d.ts",
|
|
28
28
|
"import": "./dist/api/*.js"
|
|
29
29
|
},
|
|
30
|
-
"./anthropic": {
|
|
31
|
-
"types": "./dist/api/anthropic-messages.d.ts",
|
|
32
|
-
"import": "./dist/api/anthropic-messages.js"
|
|
33
|
-
},
|
|
34
|
-
"./azure-openai-responses": {
|
|
35
|
-
"types": "./dist/api/azure-openai-responses.d.ts",
|
|
36
|
-
"import": "./dist/api/azure-openai-responses.js"
|
|
37
|
-
},
|
|
38
|
-
"./google": {
|
|
39
|
-
"types": "./dist/api/google-generative-ai.d.ts",
|
|
40
|
-
"import": "./dist/api/google-generative-ai.js"
|
|
41
|
-
},
|
|
42
|
-
"./google-vertex": {
|
|
43
|
-
"types": "./dist/api/google-vertex.d.ts",
|
|
44
|
-
"import": "./dist/api/google-vertex.js"
|
|
45
|
-
},
|
|
46
|
-
"./mistral": {
|
|
47
|
-
"types": "./dist/api/mistral-conversations.d.ts",
|
|
48
|
-
"import": "./dist/api/mistral-conversations.js"
|
|
49
|
-
},
|
|
50
|
-
"./openai-codex-responses": {
|
|
51
|
-
"types": "./dist/api/openai-codex-responses.d.ts",
|
|
52
|
-
"import": "./dist/api/openai-codex-responses.js"
|
|
53
|
-
},
|
|
54
|
-
"./openai-completions": {
|
|
55
|
-
"types": "./dist/api/openai-completions.d.ts",
|
|
56
|
-
"import": "./dist/api/openai-completions.js"
|
|
57
|
-
},
|
|
58
|
-
"./openai-responses": {
|
|
59
|
-
"types": "./dist/api/openai-responses.d.ts",
|
|
60
|
-
"import": "./dist/api/openai-responses.js"
|
|
61
|
-
},
|
|
62
30
|
"./oauth": {
|
|
63
31
|
"types": "./dist/oauth.d.ts",
|
|
64
32
|
"import": "./dist/oauth.js"
|
|
@@ -5,7 +5,7 @@ import { access, readFile, stat } from "node:fs/promises";
|
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { resolve } from "path";
|
|
7
7
|
import { resolveReadPath } from "../core/tools/path-utils.js";
|
|
8
|
-
import {
|
|
8
|
+
import { processImage } from "../utils/image-process.js";
|
|
9
9
|
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime.js";
|
|
10
10
|
/** Process @file arguments into text content and image attachments */
|
|
11
11
|
export async function processFileArguments(fileArgs, options) {
|
|
@@ -33,32 +33,20 @@ export async function processFileArguments(fileArgs, options) {
|
|
|
33
33
|
if (mimeType) {
|
|
34
34
|
// Handle image file
|
|
35
35
|
const content = await readFile(absolutePath);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!resized) {
|
|
41
|
-
text += `<file name="${absolutePath}">[Image omitted: could not be resized below the inline image size limit.]</file>\n`;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
dimensionNote = formatDimensionNote(resized);
|
|
45
|
-
attachment = {
|
|
46
|
-
type: "image",
|
|
47
|
-
mimeType: resized.mimeType,
|
|
48
|
-
data: resized.data,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
attachment = {
|
|
53
|
-
type: "image",
|
|
54
|
-
mimeType,
|
|
55
|
-
data: content.toString("base64"),
|
|
56
|
-
};
|
|
36
|
+
const processed = await processImage(content, mimeType, { autoResizeImages });
|
|
37
|
+
if (!processed.ok) {
|
|
38
|
+
text += `<file name="${absolutePath}">${processed.message}</file>\n`;
|
|
39
|
+
continue;
|
|
57
40
|
}
|
|
41
|
+
const attachment = {
|
|
42
|
+
type: "image",
|
|
43
|
+
mimeType: processed.mimeType,
|
|
44
|
+
data: processed.data,
|
|
45
|
+
};
|
|
58
46
|
images.push(attachment);
|
|
59
|
-
// Add text reference to image with optional
|
|
60
|
-
if (
|
|
61
|
-
text += `<file name="${absolutePath}">${
|
|
47
|
+
// Add text reference to image with optional processing hints
|
|
48
|
+
if (processed.hints.length > 0) {
|
|
49
|
+
text += `<file name="${absolutePath}">${processed.hints.join("\n")}</file>\n`;
|
|
62
50
|
}
|
|
63
51
|
else {
|
|
64
52
|
text += `<file name="${absolutePath}"></file>\n`;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname } from "node:path";
|
|
17
|
-
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
17
|
+
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
18
18
|
import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
|
|
19
19
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
20
20
|
import { resolvePath } from "../utils/paths.js";
|
|
@@ -769,18 +769,11 @@ export class AgentSession {
|
|
|
769
769
|
}
|
|
770
770
|
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
771
771
|
}
|
|
772
|
-
// Check if we need to compact before sending (catches aborted responses)
|
|
772
|
+
// Check if we need to compact before sending (catches aborted responses).
|
|
773
|
+
// The user's new prompt is sent below, so do not call agent.continue() here.
|
|
773
774
|
const lastAssistant = this._findLastAssistantMessage();
|
|
774
|
-
if (lastAssistant
|
|
775
|
-
|
|
776
|
-
await this.agent.continue();
|
|
777
|
-
while (await this._handlePostAgentRun()) {
|
|
778
|
-
await this.agent.continue();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
finally {
|
|
782
|
-
this._flushPendingBashMessages();
|
|
783
|
-
}
|
|
775
|
+
if (lastAssistant) {
|
|
776
|
+
await this._checkCompaction(lastAssistant, false);
|
|
784
777
|
}
|
|
785
778
|
// Build messages array (custom message if any, then user message)
|
|
786
779
|
messages = [];
|
|
@@ -1459,10 +1452,12 @@ export class AgentSession {
|
|
|
1459
1452
|
return await this._runAutoCompaction("overflow", willRetry);
|
|
1460
1453
|
}
|
|
1461
1454
|
// Case 2: Threshold - context is getting large
|
|
1462
|
-
// For error messages
|
|
1463
|
-
// This ensures sessions that hit persistent API errors (e.g. 529)
|
|
1455
|
+
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
1456
|
+
// This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
|
|
1457
|
+
// responses can still compact and do not reset context accounting.
|
|
1464
1458
|
let contextTokens;
|
|
1465
|
-
|
|
1459
|
+
const directContextTokens = assistantMessage.usage ? calculateContextTokens(assistantMessage.usage) : 0;
|
|
1460
|
+
if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
|
|
1466
1461
|
const messages = this.agent.state.messages;
|
|
1467
1462
|
const estimate = estimateContextTokens(messages);
|
|
1468
1463
|
if (estimate.lastUsageIndex === null)
|
|
@@ -1479,7 +1474,7 @@ export class AgentSession {
|
|
|
1479
1474
|
contextTokens = estimate.tokens;
|
|
1480
1475
|
}
|
|
1481
1476
|
else {
|
|
1482
|
-
contextTokens =
|
|
1477
|
+
contextTokens = directContextTokens;
|
|
1483
1478
|
}
|
|
1484
1479
|
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
1485
1480
|
return await this._runAutoCompaction("threshold", false);
|
|
@@ -1968,25 +1963,15 @@ export class AgentSession {
|
|
|
1968
1963
|
// =========================================================================
|
|
1969
1964
|
// Auto-Retry
|
|
1970
1965
|
// =========================================================================
|
|
1971
|
-
_isNonRetryableProviderLimitError(errorMessage) {
|
|
1972
|
-
return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorMessage);
|
|
1973
|
-
}
|
|
1974
1966
|
/**
|
|
1975
1967
|
* Check if an error is retryable (overloaded, rate limit, server errors).
|
|
1976
1968
|
* Context overflow errors are NOT retryable (handled by compaction instead).
|
|
1977
1969
|
*/
|
|
1978
1970
|
_isRetryableError(message) {
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
// Context overflow is handled by compaction, not retry
|
|
1982
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
1983
|
-
if (isContextOverflow(message, contextWindow))
|
|
1984
|
-
return false;
|
|
1985
|
-
const err = message.errorMessage;
|
|
1986
|
-
if (this._isNonRetryableProviderLimitError(err))
|
|
1971
|
+
// Context overflow is handled by compaction, not retry.
|
|
1972
|
+
if (isContextOverflow(message, this.model?.contextWindow ?? 0))
|
|
1987
1973
|
return false;
|
|
1988
|
-
|
|
1989
|
-
return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
|
|
1974
|
+
return isRetryableAssistantError(message);
|
|
1990
1975
|
}
|
|
1991
1976
|
/**
|
|
1992
1977
|
* Prepare a retryable error for continuation with exponential backoff.
|
|
@@ -2424,8 +2409,8 @@ export class AgentSession {
|
|
|
2424
2409
|
const contextTokens = calculateContextTokens(assistant.usage);
|
|
2425
2410
|
if (contextTokens > 0) {
|
|
2426
2411
|
hasPostCompactionUsage = true;
|
|
2412
|
+
break;
|
|
2427
2413
|
}
|
|
2428
|
-
break;
|
|
2429
2414
|
}
|
|
2430
2415
|
}
|
|
2431
2416
|
}
|
|
@@ -80,19 +80,22 @@ export function calculateContextTokens(usage) {
|
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* Get usage from an assistant message if available.
|
|
83
|
-
* Skips aborted and
|
|
83
|
+
* Skips aborted, error, and all-zero usage messages as they don't have valid usage data.
|
|
84
84
|
*/
|
|
85
85
|
function getAssistantUsage(msg) {
|
|
86
86
|
if (msg.role === "assistant" && "usage" in msg) {
|
|
87
87
|
const assistantMsg = msg;
|
|
88
|
-
if (assistantMsg.stopReason !== "aborted" &&
|
|
88
|
+
if (assistantMsg.stopReason !== "aborted" &&
|
|
89
|
+
assistantMsg.stopReason !== "error" &&
|
|
90
|
+
assistantMsg.usage &&
|
|
91
|
+
calculateContextTokens(assistantMsg.usage) > 0) {
|
|
89
92
|
return assistantMsg.usage;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
return undefined;
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
95
|
-
* Find the last
|
|
98
|
+
* Find the last valid assistant message usage from session entries.
|
|
96
99
|
*/
|
|
97
100
|
export function getLastAssistantUsage(entries) {
|
|
98
101
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
@@ -25,6 +25,7 @@ import { resolvePath } from "../../utils/paths.js";
|
|
|
25
25
|
import { createEventBus } from "../event-bus.js";
|
|
26
26
|
import { execCommand } from "../exec.js";
|
|
27
27
|
import { createSyntheticSourceInfo } from "../source-info.js";
|
|
28
|
+
import { time } from "../timings.js";
|
|
28
29
|
/** Modules available to extensions via virtualModules (for compiled Bun binary) */
|
|
29
30
|
const VIRTUAL_MODULES = {
|
|
30
31
|
typebox: _bundledTypebox,
|
|
@@ -153,7 +154,7 @@ export function createExtensionRuntime() {
|
|
|
153
154
|
invalidate: (message) => {
|
|
154
155
|
state.staleMessage ??=
|
|
155
156
|
message ??
|
|
156
|
-
"This extension ctx is stale after session replacement or reload. Do not use a captured
|
|
157
|
+
"This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().";
|
|
157
158
|
},
|
|
158
159
|
// Pre-bind: queue registrations so bindCore() can flush them once the
|
|
159
160
|
// model registry is available. bindCore() replaces both with direct calls.
|
|
@@ -340,12 +341,14 @@ async function loadExtension(extensionPath, cwd, eventBus, runtime, cacheToken)
|
|
|
340
341
|
const resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });
|
|
341
342
|
try {
|
|
342
343
|
const factory = await loadExtensionModule(resolvedPath, cacheToken);
|
|
344
|
+
time(`${extensionPath} module import`, "extensions");
|
|
343
345
|
if (!factory) {
|
|
344
346
|
return { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` };
|
|
345
347
|
}
|
|
346
348
|
const extension = createExtension(extensionPath, resolvedPath);
|
|
347
349
|
const api = createExtensionAPI(extension, runtime, cwd, eventBus);
|
|
348
350
|
await factory(api);
|
|
351
|
+
time(`${extensionPath} factory`, "extensions");
|
|
349
352
|
return { extension, error: null };
|
|
350
353
|
}
|
|
351
354
|
catch (err) {
|
|
@@ -361,6 +364,7 @@ export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime,
|
|
|
361
364
|
const resolvedCwd = resolvePath(cwd);
|
|
362
365
|
const api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);
|
|
363
366
|
await factory(api);
|
|
367
|
+
time(`${extensionPath} factory`, "extensions");
|
|
364
368
|
return extension;
|
|
365
369
|
}
|
|
366
370
|
/**
|
|
@@ -402,13 +402,11 @@ export class ModelRegistry {
|
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
404
|
else if (!isBuiltIn) {
|
|
405
|
-
// Non-built-in providers with custom models require endpoint
|
|
405
|
+
// Non-built-in providers with custom models require an endpoint.
|
|
406
|
+
// Auth can come from auth.json, --api-key, or provider request config.
|
|
406
407
|
if (!providerConfig.baseUrl) {
|
|
407
408
|
throw new Error(`Provider ${providerName}: "baseUrl" is required when defining custom models.`);
|
|
408
409
|
}
|
|
409
|
-
if (!providerConfig.apiKey) {
|
|
410
|
-
throw new Error(`Provider ${providerName}: "apiKey" is required when defining custom models.`);
|
|
411
|
-
}
|
|
412
410
|
}
|
|
413
411
|
// Built-in providers with custom models: baseUrl/apiKey/api are optional,
|
|
414
412
|
// inherited from built-in models. Auth comes from env vars / auth storage.
|