pullfrog 0.1.38 → 0.1.40
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/dist/agents/opencodeShared.d.ts +37 -0
- package/dist/cli.mjs +70 -27
- package/dist/index.js +69 -26
- package/dist/internal.js +27 -6
- package/dist/models.d.ts +5 -3
- package/dist/utils/apiKeys.d.ts +12 -0
- package/dist/utils/claudeSubscription.d.ts +3 -1
- package/package.json +1 -1
|
@@ -16,6 +16,43 @@ export type OpenCodeConfig = {
|
|
|
16
16
|
export declare function geminiHighThinkingOverrides(): Record<string, {
|
|
17
17
|
options: object;
|
|
18
18
|
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Build the `provider.openrouter.models[id].options` map that pins every Kimi
|
|
21
|
+
* K2 OpenRouter alias away from the Enforcer-less providers via OpenRouter's
|
|
22
|
+
* `provider: {ignore:[...]}` routing directive. Sourced from the model registry
|
|
23
|
+
* so a Kimi alias/route change in `action/models.ts` flows through automatically.
|
|
24
|
+
*
|
|
25
|
+
* Wiring: the per-model `options` object merges into the request options
|
|
26
|
+
* (opencode `session/llm/request.ts` `mergeOptions(base, model.options)`),
|
|
27
|
+
* gets wrapped under `providerOptions.openrouter` (`provider/transform.ts`
|
|
28
|
+
* `sdkKey("@openrouter/ai-sdk-provider") === "openrouter"`), and the OpenRouter
|
|
29
|
+
* AI-SDK provider spreads everything under `providerOptions.openrouter` straight
|
|
30
|
+
* into the request body (`@openrouter/ai-sdk-provider` `doStream`/`doGenerate`),
|
|
31
|
+
* so `provider` lands as the top-level routing object OpenRouter expects.
|
|
32
|
+
*/
|
|
33
|
+
export declare function kimiOpenRouterProviderOverrides(): Record<string, {
|
|
34
|
+
options: object;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Build the `provider.openrouter.models[id].options` map that pins the
|
|
38
|
+
* efficient-tier DeepSeek model to high reasoning effort on the OpenRouter
|
|
39
|
+
* route — the funded/OSS default path. The shape must nest under `options`
|
|
40
|
+
* (opencode builds `Model.options` only from the entry's `options` sub-object —
|
|
41
|
+
* `provider.ts` `mergeDeep(existingModel.options, model.options)`) and use a
|
|
42
|
+
* `reasoning` record (the openrouter provider forwards only `usage`/`reasoning`/
|
|
43
|
+
* `promptCacheKey`); a bare `reasoningEffort` sibling is silently dropped.
|
|
44
|
+
* Sourced from the registry so a resolve bump carries the override forward.
|
|
45
|
+
* `high` is the ceiling OpenRouter's unified `reasoning.effort` exposes
|
|
46
|
+
* (low/medium/high); DeepSeek's native `max` is only reachable via a direct
|
|
47
|
+
* DeepSeek key, not the OpenRouter route.
|
|
48
|
+
*/
|
|
49
|
+
export declare function deepseekHighEffortOverrides(): Record<string, {
|
|
50
|
+
options: {
|
|
51
|
+
reasoning: {
|
|
52
|
+
effort: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
}>;
|
|
19
56
|
/**
|
|
20
57
|
* Read-only `reviewfrog` subagent for lens-based review. Non-mutative +
|
|
21
58
|
* non-recursive — enforced by the system prompt in reviewer.ts.
|
package/dist/cli.mjs
CHANGED
|
@@ -101934,11 +101934,20 @@ var providers = {
|
|
|
101934
101934
|
displayName: "Moonshot AI",
|
|
101935
101935
|
envVars: ["MOONSHOT_API_KEY"],
|
|
101936
101936
|
models: {
|
|
101937
|
+
// Moonshot's premium 1M-context multimodal reasoning flagship — pricier
|
|
101938
|
+
// than K2.7-code, so it's the preferred BYOK pick but NOT the subsidized
|
|
101939
|
+
// efficient default (that stays K2 via AUTO_EFFICIENT).
|
|
101940
|
+
"kimi-k3": {
|
|
101941
|
+
displayName: "Kimi K3",
|
|
101942
|
+
resolve: "moonshotai/kimi-k3",
|
|
101943
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3",
|
|
101944
|
+
preferred: true,
|
|
101945
|
+
subagentModel: "kimi-k2"
|
|
101946
|
+
},
|
|
101937
101947
|
"kimi-k2": {
|
|
101938
101948
|
displayName: "Kimi K2",
|
|
101939
101949
|
resolve: "moonshotai/kimi-k2.7-code",
|
|
101940
|
-
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
101941
|
-
preferred: true
|
|
101950
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
101942
101951
|
}
|
|
101943
101952
|
}
|
|
101944
101953
|
}),
|
|
@@ -102032,10 +102041,11 @@ var providers = {
|
|
|
102032
102041
|
resolve: "opencode/kimi-k2.6",
|
|
102033
102042
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
102034
102043
|
},
|
|
102044
|
+
// Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
|
|
102035
102045
|
"minimax-m2.5": {
|
|
102036
102046
|
displayName: "MiniMax M2",
|
|
102037
102047
|
resolve: "opencode/minimax-m2.5",
|
|
102038
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
102048
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
102039
102049
|
},
|
|
102040
102050
|
"gpt-5-nano": {
|
|
102041
102051
|
displayName: "GPT Nano",
|
|
@@ -102223,10 +102233,21 @@ var providers = {
|
|
|
102223
102233
|
resolve: "openrouter/moonshotai/kimi-k2.7-code",
|
|
102224
102234
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
102225
102235
|
},
|
|
102236
|
+
"kimi-k3": {
|
|
102237
|
+
displayName: "Kimi K3",
|
|
102238
|
+
resolve: "openrouter/moonshotai/kimi-k3",
|
|
102239
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3"
|
|
102240
|
+
},
|
|
102241
|
+
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
102226
102242
|
"minimax-m2.5": {
|
|
102227
102243
|
displayName: "MiniMax M2",
|
|
102228
|
-
resolve: "openrouter/minimax/minimax-m2.
|
|
102229
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
102244
|
+
resolve: "openrouter/minimax/minimax-m2.7",
|
|
102245
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
102246
|
+
},
|
|
102247
|
+
"minimax-m3": {
|
|
102248
|
+
displayName: "MiniMax M3",
|
|
102249
|
+
resolve: "openrouter/minimax/minimax-m3",
|
|
102250
|
+
openRouterResolve: "openrouter/minimax/minimax-m3"
|
|
102230
102251
|
}
|
|
102231
102252
|
}
|
|
102232
102253
|
})
|
|
@@ -102275,7 +102296,7 @@ var modelAliases = Object.entries(providers).flatMap(
|
|
|
102275
102296
|
var AUTO_EFFICIENT = "auto/efficient";
|
|
102276
102297
|
var AUTO_INTELLIGENT = "auto/intelligent";
|
|
102277
102298
|
var AUTO_TIER_TARGET = {
|
|
102278
|
-
[AUTO_EFFICIENT]: "
|
|
102299
|
+
[AUTO_EFFICIENT]: "deepseek/deepseek-pro",
|
|
102279
102300
|
[AUTO_INTELLIGENT]: "anthropic/claude-opus"
|
|
102280
102301
|
};
|
|
102281
102302
|
function isAutoTier(slug2) {
|
|
@@ -102473,7 +102494,7 @@ async function preflightClaudeSubscription(params) {
|
|
|
102473
102494
|
} catch {
|
|
102474
102495
|
return { usable: true };
|
|
102475
102496
|
}
|
|
102476
|
-
if (res.status !== 401 && res.status !== 429) return { usable: true };
|
|
102497
|
+
if (res.status !== 401 && res.status !== 403 && res.status !== 429) return { usable: true };
|
|
102477
102498
|
const body = await res.text().catch(() => "");
|
|
102478
102499
|
return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
|
|
102479
102500
|
}
|
|
@@ -102976,7 +102997,7 @@ function extractExcerpt(text, matchIndex) {
|
|
|
102976
102997
|
}
|
|
102977
102998
|
return excerpt.trim();
|
|
102978
102999
|
}
|
|
102979
|
-
var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford/is;
|
|
103000
|
+
var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford|Key limit exceeded \(total limit\)/is;
|
|
102980
103001
|
function isRouterKeylimitExhaustedError(text) {
|
|
102981
103002
|
return ROUTER_KEYLIMIT_EXHAUSTED_PATTERN.test(text);
|
|
102982
103003
|
}
|
|
@@ -103001,7 +103022,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
103001
103022
|
// package.json
|
|
103002
103023
|
var package_default = {
|
|
103003
103024
|
name: "pullfrog",
|
|
103004
|
-
version: "0.1.
|
|
103025
|
+
version: "0.1.40",
|
|
103005
103026
|
type: "module",
|
|
103006
103027
|
bin: {
|
|
103007
103028
|
pullfrog: "dist/cli.mjs",
|
|
@@ -110227,6 +110248,19 @@ function geminiHighThinkingOverrides() {
|
|
|
110227
110248
|
])
|
|
110228
110249
|
);
|
|
110229
110250
|
}
|
|
110251
|
+
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
110252
|
+
function kimiOpenRouterProviderOverrides() {
|
|
110253
|
+
return Object.fromEntries(
|
|
110254
|
+
modelAliases.map((a) => a.openRouterResolve).filter((r) => r?.includes("kimi") ?? false).map((r) => [
|
|
110255
|
+
r.replace(/^openrouter\//, ""),
|
|
110256
|
+
{ options: { provider: { ignore: KIMI_ENFORCERLESS_PROVIDERS } } }
|
|
110257
|
+
])
|
|
110258
|
+
);
|
|
110259
|
+
}
|
|
110260
|
+
function deepseekHighEffortOverrides() {
|
|
110261
|
+
const orModel = modelAliases.find((a) => a.slug === "deepseek/deepseek-pro")?.openRouterResolve?.replace(/^openrouter\//, "");
|
|
110262
|
+
return orModel ? { [orModel]: { options: { reasoning: { effort: "high" } } } } : {};
|
|
110263
|
+
}
|
|
110230
110264
|
function buildReviewerAgentConfig(orchestratorModel) {
|
|
110231
110265
|
const overrides = deriveSubagentModels(orchestratorModel);
|
|
110232
110266
|
return {
|
|
@@ -110295,9 +110329,18 @@ function buildSecurityConfig(ctx, model) {
|
|
|
110295
110329
|
log.info(`\xBB subagent models: reviewfrog=${reviewerModel}`);
|
|
110296
110330
|
return cfg;
|
|
110297
110331
|
})(),
|
|
110298
|
-
// gemini-3 thinking pinned to high for review depth;
|
|
110299
|
-
//
|
|
110300
|
-
|
|
110332
|
+
// gemini-3 thinking pinned to high for review depth; deepseek (funded
|
|
110333
|
+
// efficient tier) pinned to high reasoning effort, and kimi pinned away
|
|
110334
|
+
// from Enforcer-less openrouter providers (siliconflow / together) that
|
|
110335
|
+
// drop optional tool-call params — both per-model on the openrouter route,
|
|
110336
|
+
// so other models are unaffected. gpt/anthropic effort set elsewhere (gpt:
|
|
110337
|
+
// upstream default, anthropic: --effort flag in claude.ts). see opencodeShared.ts.
|
|
110338
|
+
provider: {
|
|
110339
|
+
google: { models: geminiHighThinkingOverrides() },
|
|
110340
|
+
openrouter: {
|
|
110341
|
+
models: { ...deepseekHighEffortOverrides(), ...kimiOpenRouterProviderOverrides() }
|
|
110342
|
+
}
|
|
110343
|
+
}
|
|
110301
110344
|
};
|
|
110302
110345
|
if (model) {
|
|
110303
110346
|
config3.model = model;
|
|
@@ -110423,7 +110466,6 @@ function newTurn() {
|
|
|
110423
110466
|
finalText: "",
|
|
110424
110467
|
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
110425
110468
|
costUsd: 0,
|
|
110426
|
-
sessionError: null,
|
|
110427
110469
|
lastToolError: null
|
|
110428
110470
|
};
|
|
110429
110471
|
}
|
|
@@ -110454,7 +110496,6 @@ async function dispatchEvent(ctx, event) {
|
|
|
110454
110496
|
if (sessionID !== ctx.orchestratorSessionID) return;
|
|
110455
110497
|
const err = event.properties.error;
|
|
110456
110498
|
const message = err ? extractErrorMessage(err) : "(no error payload)";
|
|
110457
|
-
if (ctx.currentTurn) ctx.currentTurn.sessionError = message;
|
|
110458
110499
|
log.info(`\xBB ${ctx.label} session error: ${message}`);
|
|
110459
110500
|
return;
|
|
110460
110501
|
}
|
|
@@ -110647,14 +110688,6 @@ async function runPromptTurn(ctx, params) {
|
|
|
110647
110688
|
usage
|
|
110648
110689
|
};
|
|
110649
110690
|
}
|
|
110650
|
-
if (turn.sessionError) {
|
|
110651
|
-
return {
|
|
110652
|
-
success: false,
|
|
110653
|
-
output: finalText,
|
|
110654
|
-
error: `session error: ${turn.sessionError}`,
|
|
110655
|
-
usage
|
|
110656
|
-
};
|
|
110657
|
-
}
|
|
110658
110691
|
return { success: true, output: finalText, usage };
|
|
110659
110692
|
}
|
|
110660
110693
|
async function aggregateTurnUsage(ctx, turnStartMs) {
|
|
@@ -164672,10 +164705,13 @@ function validateAgentApiKey(params) {
|
|
|
164672
164705
|
}
|
|
164673
164706
|
function isApiKeyAuthError(text) {
|
|
164674
164707
|
if (!text) return false;
|
|
164675
|
-
return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isOAuthCredentialExpiredError(text);
|
|
164708
|
+
return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isClaudeSubscriptionDisabledError(text) || isOAuthCredentialExpiredError(text);
|
|
164676
164709
|
}
|
|
164677
164710
|
function isOAuthCredentialExpiredError(text) {
|
|
164678
|
-
return /authentication token has expired/i.test(text) || /Token refresh failed/i.test(text);
|
|
164711
|
+
return /authentication token has (expired|been invalidated)/i.test(text) || /OAuth access token has expired/i.test(text) || /Token refresh failed/i.test(text);
|
|
164712
|
+
}
|
|
164713
|
+
function isClaudeSubscriptionDisabledError(text) {
|
|
164714
|
+
return /disabled Claude subscription access/i.test(text);
|
|
164679
164715
|
}
|
|
164680
164716
|
function formatApiKeyErrorSummary(params) {
|
|
164681
164717
|
if (params.raw.includes(MISSING_KEY_MARKER)) {
|
|
@@ -164684,11 +164720,18 @@ function formatApiKeyErrorSummary(params) {
|
|
|
164684
164720
|
}
|
|
164685
164721
|
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
164686
164722
|
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
164723
|
+
if (isClaudeSubscriptionDisabledError(params.raw)) {
|
|
164724
|
+
return [
|
|
164725
|
+
`**Your organization has disabled Claude subscription access for Claude Code.** Ask your Claude organization's admin to re-enable it in the Claude Console, or set an \`ANTHROPIC_API_KEY\` for this repo instead, then re-trigger the run.`,
|
|
164726
|
+
"",
|
|
164727
|
+
`[Add repo secret \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
164728
|
+
].join("\n");
|
|
164729
|
+
}
|
|
164687
164730
|
if (isOAuthCredentialExpiredError(params.raw)) {
|
|
164688
164731
|
return [
|
|
164689
|
-
`**Your provider OAuth credential has expired.** Re-authenticate the provider connection (e.g. \`pullfrog auth codex\`), then re-trigger the run.`,
|
|
164732
|
+
`**Your provider OAuth credential has expired or been revoked.** Re-authenticate the provider connection (e.g. \`pullfrog auth claude\` / \`pullfrog auth codex\`), then re-trigger the run.`,
|
|
164690
164733
|
"",
|
|
164691
|
-
`[
|
|
164734
|
+
`[Claude subscription \u2192](https://docs.pullfrog.com/claude-auth) \xB7 [ChatGPT subscription \u2192](https://docs.pullfrog.com/codex-auth) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
164692
164735
|
].join("\n");
|
|
164693
164736
|
}
|
|
164694
164737
|
return [
|
|
@@ -168474,7 +168517,7 @@ async function runCli4(input) {
|
|
|
168474
168517
|
}
|
|
168475
168518
|
|
|
168476
168519
|
// cli.ts
|
|
168477
|
-
var VERSION10 = "0.1.
|
|
168520
|
+
var VERSION10 = "0.1.40";
|
|
168478
168521
|
var bin = basename2(process.argv[1] || "");
|
|
168479
168522
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
168480
168523
|
var rawArgs = process.argv.slice(2);
|
package/dist/index.js
CHANGED
|
@@ -100005,11 +100005,20 @@ var providers = {
|
|
|
100005
100005
|
displayName: "Moonshot AI",
|
|
100006
100006
|
envVars: ["MOONSHOT_API_KEY"],
|
|
100007
100007
|
models: {
|
|
100008
|
+
// Moonshot's premium 1M-context multimodal reasoning flagship — pricier
|
|
100009
|
+
// than K2.7-code, so it's the preferred BYOK pick but NOT the subsidized
|
|
100010
|
+
// efficient default (that stays K2 via AUTO_EFFICIENT).
|
|
100011
|
+
"kimi-k3": {
|
|
100012
|
+
displayName: "Kimi K3",
|
|
100013
|
+
resolve: "moonshotai/kimi-k3",
|
|
100014
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3",
|
|
100015
|
+
preferred: true,
|
|
100016
|
+
subagentModel: "kimi-k2"
|
|
100017
|
+
},
|
|
100008
100018
|
"kimi-k2": {
|
|
100009
100019
|
displayName: "Kimi K2",
|
|
100010
100020
|
resolve: "moonshotai/kimi-k2.7-code",
|
|
100011
|
-
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
100012
|
-
preferred: true
|
|
100021
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
100013
100022
|
}
|
|
100014
100023
|
}
|
|
100015
100024
|
}),
|
|
@@ -100103,10 +100112,11 @@ var providers = {
|
|
|
100103
100112
|
resolve: "opencode/kimi-k2.6",
|
|
100104
100113
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
100105
100114
|
},
|
|
100115
|
+
// Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
|
|
100106
100116
|
"minimax-m2.5": {
|
|
100107
100117
|
displayName: "MiniMax M2",
|
|
100108
100118
|
resolve: "opencode/minimax-m2.5",
|
|
100109
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
100119
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
100110
100120
|
},
|
|
100111
100121
|
"gpt-5-nano": {
|
|
100112
100122
|
displayName: "GPT Nano",
|
|
@@ -100294,10 +100304,21 @@ var providers = {
|
|
|
100294
100304
|
resolve: "openrouter/moonshotai/kimi-k2.7-code",
|
|
100295
100305
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
100296
100306
|
},
|
|
100307
|
+
"kimi-k3": {
|
|
100308
|
+
displayName: "Kimi K3",
|
|
100309
|
+
resolve: "openrouter/moonshotai/kimi-k3",
|
|
100310
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3"
|
|
100311
|
+
},
|
|
100312
|
+
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
100297
100313
|
"minimax-m2.5": {
|
|
100298
100314
|
displayName: "MiniMax M2",
|
|
100299
|
-
resolve: "openrouter/minimax/minimax-m2.
|
|
100300
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
100315
|
+
resolve: "openrouter/minimax/minimax-m2.7",
|
|
100316
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
100317
|
+
},
|
|
100318
|
+
"minimax-m3": {
|
|
100319
|
+
displayName: "MiniMax M3",
|
|
100320
|
+
resolve: "openrouter/minimax/minimax-m3",
|
|
100321
|
+
openRouterResolve: "openrouter/minimax/minimax-m3"
|
|
100301
100322
|
}
|
|
100302
100323
|
}
|
|
100303
100324
|
})
|
|
@@ -100346,7 +100367,7 @@ var modelAliases = Object.entries(providers).flatMap(
|
|
|
100346
100367
|
var AUTO_EFFICIENT = "auto/efficient";
|
|
100347
100368
|
var AUTO_INTELLIGENT = "auto/intelligent";
|
|
100348
100369
|
var AUTO_TIER_TARGET = {
|
|
100349
|
-
[AUTO_EFFICIENT]: "
|
|
100370
|
+
[AUTO_EFFICIENT]: "deepseek/deepseek-pro",
|
|
100350
100371
|
[AUTO_INTELLIGENT]: "anthropic/claude-opus"
|
|
100351
100372
|
};
|
|
100352
100373
|
function isAutoTier(slug2) {
|
|
@@ -100544,7 +100565,7 @@ async function preflightClaudeSubscription(params) {
|
|
|
100544
100565
|
} catch {
|
|
100545
100566
|
return { usable: true };
|
|
100546
100567
|
}
|
|
100547
|
-
if (res.status !== 401 && res.status !== 429) return { usable: true };
|
|
100568
|
+
if (res.status !== 401 && res.status !== 403 && res.status !== 429) return { usable: true };
|
|
100548
100569
|
const body = await res.text().catch(() => "");
|
|
100549
100570
|
return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
|
|
100550
100571
|
}
|
|
@@ -101047,7 +101068,7 @@ function extractExcerpt(text, matchIndex) {
|
|
|
101047
101068
|
}
|
|
101048
101069
|
return excerpt.trim();
|
|
101049
101070
|
}
|
|
101050
|
-
var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford/is;
|
|
101071
|
+
var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford|Key limit exceeded \(total limit\)/is;
|
|
101051
101072
|
function isRouterKeylimitExhaustedError(text) {
|
|
101052
101073
|
return ROUTER_KEYLIMIT_EXHAUSTED_PATTERN.test(text);
|
|
101053
101074
|
}
|
|
@@ -101072,7 +101093,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
101072
101093
|
// package.json
|
|
101073
101094
|
var package_default = {
|
|
101074
101095
|
name: "pullfrog",
|
|
101075
|
-
version: "0.1.
|
|
101096
|
+
version: "0.1.40",
|
|
101076
101097
|
type: "module",
|
|
101077
101098
|
bin: {
|
|
101078
101099
|
pullfrog: "dist/cli.mjs",
|
|
@@ -108340,6 +108361,19 @@ function geminiHighThinkingOverrides() {
|
|
|
108340
108361
|
])
|
|
108341
108362
|
);
|
|
108342
108363
|
}
|
|
108364
|
+
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
108365
|
+
function kimiOpenRouterProviderOverrides() {
|
|
108366
|
+
return Object.fromEntries(
|
|
108367
|
+
modelAliases.map((a) => a.openRouterResolve).filter((r) => r?.includes("kimi") ?? false).map((r) => [
|
|
108368
|
+
r.replace(/^openrouter\//, ""),
|
|
108369
|
+
{ options: { provider: { ignore: KIMI_ENFORCERLESS_PROVIDERS } } }
|
|
108370
|
+
])
|
|
108371
|
+
);
|
|
108372
|
+
}
|
|
108373
|
+
function deepseekHighEffortOverrides() {
|
|
108374
|
+
const orModel = modelAliases.find((a) => a.slug === "deepseek/deepseek-pro")?.openRouterResolve?.replace(/^openrouter\//, "");
|
|
108375
|
+
return orModel ? { [orModel]: { options: { reasoning: { effort: "high" } } } } : {};
|
|
108376
|
+
}
|
|
108343
108377
|
function buildReviewerAgentConfig(orchestratorModel) {
|
|
108344
108378
|
const overrides = deriveSubagentModels(orchestratorModel);
|
|
108345
108379
|
return {
|
|
@@ -108408,9 +108442,18 @@ function buildSecurityConfig(ctx, model) {
|
|
|
108408
108442
|
log.info(`\xBB subagent models: reviewfrog=${reviewerModel}`);
|
|
108409
108443
|
return cfg;
|
|
108410
108444
|
})(),
|
|
108411
|
-
// gemini-3 thinking pinned to high for review depth;
|
|
108412
|
-
//
|
|
108413
|
-
|
|
108445
|
+
// gemini-3 thinking pinned to high for review depth; deepseek (funded
|
|
108446
|
+
// efficient tier) pinned to high reasoning effort, and kimi pinned away
|
|
108447
|
+
// from Enforcer-less openrouter providers (siliconflow / together) that
|
|
108448
|
+
// drop optional tool-call params — both per-model on the openrouter route,
|
|
108449
|
+
// so other models are unaffected. gpt/anthropic effort set elsewhere (gpt:
|
|
108450
|
+
// upstream default, anthropic: --effort flag in claude.ts). see opencodeShared.ts.
|
|
108451
|
+
provider: {
|
|
108452
|
+
google: { models: geminiHighThinkingOverrides() },
|
|
108453
|
+
openrouter: {
|
|
108454
|
+
models: { ...deepseekHighEffortOverrides(), ...kimiOpenRouterProviderOverrides() }
|
|
108455
|
+
}
|
|
108456
|
+
}
|
|
108414
108457
|
};
|
|
108415
108458
|
if (model) {
|
|
108416
108459
|
config3.model = model;
|
|
@@ -108536,7 +108579,6 @@ function newTurn() {
|
|
|
108536
108579
|
finalText: "",
|
|
108537
108580
|
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
108538
108581
|
costUsd: 0,
|
|
108539
|
-
sessionError: null,
|
|
108540
108582
|
lastToolError: null
|
|
108541
108583
|
};
|
|
108542
108584
|
}
|
|
@@ -108567,7 +108609,6 @@ async function dispatchEvent(ctx, event) {
|
|
|
108567
108609
|
if (sessionID !== ctx.orchestratorSessionID) return;
|
|
108568
108610
|
const err = event.properties.error;
|
|
108569
108611
|
const message = err ? extractErrorMessage(err) : "(no error payload)";
|
|
108570
|
-
if (ctx.currentTurn) ctx.currentTurn.sessionError = message;
|
|
108571
108612
|
log.info(`\xBB ${ctx.label} session error: ${message}`);
|
|
108572
108613
|
return;
|
|
108573
108614
|
}
|
|
@@ -108760,14 +108801,6 @@ async function runPromptTurn(ctx, params) {
|
|
|
108760
108801
|
usage
|
|
108761
108802
|
};
|
|
108762
108803
|
}
|
|
108763
|
-
if (turn.sessionError) {
|
|
108764
|
-
return {
|
|
108765
|
-
success: false,
|
|
108766
|
-
output: finalText,
|
|
108767
|
-
error: `session error: ${turn.sessionError}`,
|
|
108768
|
-
usage
|
|
108769
|
-
};
|
|
108770
|
-
}
|
|
108771
108804
|
return { success: true, output: finalText, usage };
|
|
108772
108805
|
}
|
|
108773
108806
|
async function aggregateTurnUsage(ctx, turnStartMs) {
|
|
@@ -162785,10 +162818,13 @@ function validateAgentApiKey(params) {
|
|
|
162785
162818
|
}
|
|
162786
162819
|
function isApiKeyAuthError(text) {
|
|
162787
162820
|
if (!text) return false;
|
|
162788
|
-
return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isOAuthCredentialExpiredError(text);
|
|
162821
|
+
return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isClaudeSubscriptionDisabledError(text) || isOAuthCredentialExpiredError(text);
|
|
162789
162822
|
}
|
|
162790
162823
|
function isOAuthCredentialExpiredError(text) {
|
|
162791
|
-
return /authentication token has expired/i.test(text) || /Token refresh failed/i.test(text);
|
|
162824
|
+
return /authentication token has (expired|been invalidated)/i.test(text) || /OAuth access token has expired/i.test(text) || /Token refresh failed/i.test(text);
|
|
162825
|
+
}
|
|
162826
|
+
function isClaudeSubscriptionDisabledError(text) {
|
|
162827
|
+
return /disabled Claude subscription access/i.test(text);
|
|
162792
162828
|
}
|
|
162793
162829
|
function formatApiKeyErrorSummary(params) {
|
|
162794
162830
|
if (params.raw.includes(MISSING_KEY_MARKER)) {
|
|
@@ -162797,11 +162833,18 @@ function formatApiKeyErrorSummary(params) {
|
|
|
162797
162833
|
}
|
|
162798
162834
|
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
162799
162835
|
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
162836
|
+
if (isClaudeSubscriptionDisabledError(params.raw)) {
|
|
162837
|
+
return [
|
|
162838
|
+
`**Your organization has disabled Claude subscription access for Claude Code.** Ask your Claude organization's admin to re-enable it in the Claude Console, or set an \`ANTHROPIC_API_KEY\` for this repo instead, then re-trigger the run.`,
|
|
162839
|
+
"",
|
|
162840
|
+
`[Add repo secret \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
162841
|
+
].join("\n");
|
|
162842
|
+
}
|
|
162800
162843
|
if (isOAuthCredentialExpiredError(params.raw)) {
|
|
162801
162844
|
return [
|
|
162802
|
-
`**Your provider OAuth credential has expired.** Re-authenticate the provider connection (e.g. \`pullfrog auth codex\`), then re-trigger the run.`,
|
|
162845
|
+
`**Your provider OAuth credential has expired or been revoked.** Re-authenticate the provider connection (e.g. \`pullfrog auth claude\` / \`pullfrog auth codex\`), then re-trigger the run.`,
|
|
162803
162846
|
"",
|
|
162804
|
-
`[
|
|
162847
|
+
`[Claude subscription \u2192](https://docs.pullfrog.com/claude-auth) \xB7 [ChatGPT subscription \u2192](https://docs.pullfrog.com/codex-auth) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
162805
162848
|
].join("\n");
|
|
162806
162849
|
}
|
|
162807
162850
|
return [
|
package/dist/internal.js
CHANGED
|
@@ -196,11 +196,20 @@ var providers = {
|
|
|
196
196
|
displayName: "Moonshot AI",
|
|
197
197
|
envVars: ["MOONSHOT_API_KEY"],
|
|
198
198
|
models: {
|
|
199
|
+
// Moonshot's premium 1M-context multimodal reasoning flagship — pricier
|
|
200
|
+
// than K2.7-code, so it's the preferred BYOK pick but NOT the subsidized
|
|
201
|
+
// efficient default (that stays K2 via AUTO_EFFICIENT).
|
|
202
|
+
"kimi-k3": {
|
|
203
|
+
displayName: "Kimi K3",
|
|
204
|
+
resolve: "moonshotai/kimi-k3",
|
|
205
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3",
|
|
206
|
+
preferred: true,
|
|
207
|
+
subagentModel: "kimi-k2"
|
|
208
|
+
},
|
|
199
209
|
"kimi-k2": {
|
|
200
210
|
displayName: "Kimi K2",
|
|
201
211
|
resolve: "moonshotai/kimi-k2.7-code",
|
|
202
|
-
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
203
|
-
preferred: true
|
|
212
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
204
213
|
}
|
|
205
214
|
}
|
|
206
215
|
}),
|
|
@@ -294,10 +303,11 @@ var providers = {
|
|
|
294
303
|
resolve: "opencode/kimi-k2.6",
|
|
295
304
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
296
305
|
},
|
|
306
|
+
// Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
|
|
297
307
|
"minimax-m2.5": {
|
|
298
308
|
displayName: "MiniMax M2",
|
|
299
309
|
resolve: "opencode/minimax-m2.5",
|
|
300
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
310
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
301
311
|
},
|
|
302
312
|
"gpt-5-nano": {
|
|
303
313
|
displayName: "GPT Nano",
|
|
@@ -485,10 +495,21 @@ var providers = {
|
|
|
485
495
|
resolve: "openrouter/moonshotai/kimi-k2.7-code",
|
|
486
496
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
487
497
|
},
|
|
498
|
+
"kimi-k3": {
|
|
499
|
+
displayName: "Kimi K3",
|
|
500
|
+
resolve: "openrouter/moonshotai/kimi-k3",
|
|
501
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k3"
|
|
502
|
+
},
|
|
503
|
+
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
488
504
|
"minimax-m2.5": {
|
|
489
505
|
displayName: "MiniMax M2",
|
|
490
|
-
resolve: "openrouter/minimax/minimax-m2.
|
|
491
|
-
openRouterResolve: "openrouter/minimax/minimax-m2.
|
|
506
|
+
resolve: "openrouter/minimax/minimax-m2.7",
|
|
507
|
+
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
508
|
+
},
|
|
509
|
+
"minimax-m3": {
|
|
510
|
+
displayName: "MiniMax M3",
|
|
511
|
+
resolve: "openrouter/minimax/minimax-m3",
|
|
512
|
+
openRouterResolve: "openrouter/minimax/minimax-m3"
|
|
492
513
|
}
|
|
493
514
|
}
|
|
494
515
|
})
|
|
@@ -546,7 +567,7 @@ var modelAliases = Object.entries(providers).flatMap(
|
|
|
546
567
|
var AUTO_EFFICIENT = "auto/efficient";
|
|
547
568
|
var AUTO_INTELLIGENT = "auto/intelligent";
|
|
548
569
|
var AUTO_TIER_TARGET = {
|
|
549
|
-
[AUTO_EFFICIENT]: "
|
|
570
|
+
[AUTO_EFFICIENT]: "deepseek/deepseek-pro",
|
|
550
571
|
[AUTO_INTELLIGENT]: "anthropic/claude-opus"
|
|
551
572
|
};
|
|
552
573
|
function isAutoTier(slug) {
|
package/dist/models.d.ts
CHANGED
|
@@ -121,9 +121,11 @@ export declare const modelAliases: ModelAlias[];
|
|
|
121
121
|
* concrete alias by `resolveDisplayAlias` below, so every downstream consumer
|
|
122
122
|
* (CLI resolve, OpenRouter resolve, footer label) handles them transparently.
|
|
123
123
|
*
|
|
124
|
-
* `efficient` mirrors the OSS/default subsidy model (
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* `efficient` mirrors the OSS/default subsidy model (DeepSeek V4 Pro — beats
|
|
125
|
+
* Kimi K2.7 on agentic-review quality at ~4-5x lower cost, run at high
|
|
126
|
+
* reasoning effort via `deepseekHighEffortOverrides`); `intelligent` is the
|
|
127
|
+
* frontier pick (Claude Opus). a `null` model means the tier hasn't been
|
|
128
|
+
* pinned: callers default by card status via `defaultAutoTier`.
|
|
127
129
|
*/
|
|
128
130
|
export declare const AUTO_EFFICIENT = "auto/efficient";
|
|
129
131
|
export declare const AUTO_INTELLIGENT = "auto/intelligent";
|
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ export declare function validateAgentApiKey(params: {
|
|
|
37
37
|
* ****XXXX is invalid`. anchored to "Your api key: ... is invalid" so it
|
|
38
38
|
* can't collide with DeepSeek's already-handled `Insufficient balance`
|
|
39
39
|
* billing shape (which routes to formatProviderBillingExhausted).
|
|
40
|
+
* - org-disabled Claude subscription (#1072): `Your organization has
|
|
41
|
+
* disabled Claude subscription access for Claude Code`, an entitlement
|
|
42
|
+
* denial with its own remedy — see isClaudeSubscriptionDisabledError.
|
|
40
43
|
*/
|
|
41
44
|
export declare function isApiKeyAuthError(text: string): boolean;
|
|
42
45
|
/**
|
|
@@ -46,8 +49,17 @@ export declare function isApiKeyAuthError(text: string): boolean;
|
|
|
46
49
|
* distinct copy for these. Patterns are deliberately narrow:
|
|
47
50
|
* "authentication token has expired" (not bare "token has expired") so a
|
|
48
51
|
* GitHub installation-token expiry can't be misread as an LLM key problem.
|
|
52
|
+
* also covers server-side revocation ("authentication token has been
|
|
53
|
+
* invalidated", OpenAI `token_invalidated`, #1041) and the Claude CLI's
|
|
54
|
+
* "OAuth access token has expired" phrasing (#1072) — same remediation.
|
|
49
55
|
*/
|
|
50
56
|
export declare function isOAuthCredentialExpiredError(text: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Anthropic entitlement denial for Claude Pro/Max subscription auth (#1072):
|
|
59
|
+
* an org admin turned off Claude Code subscription access. no credential the
|
|
60
|
+
* user controls fixes it, so it gets its own copy instead of the re-auth CTA.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isClaudeSubscriptionDisabledError(text: string): boolean;
|
|
51
63
|
/**
|
|
52
64
|
* Friendly Markdown summary for both the missing-key and invalid-key cases.
|
|
53
65
|
* Used in the catch / result-failure paths in `main.ts` to overwrite the raw
|
|
@@ -16,7 +16,9 @@ export type SubscriptionPreflight = {
|
|
|
16
16
|
* per-model ("You've hit your Opus limit"), so a cheaper stand-in could pass
|
|
17
17
|
* preflight and still leave the run dead on arrival.
|
|
18
18
|
*
|
|
19
|
-
* fail-open by design: only 401 (revoked/expired token)
|
|
19
|
+
* fail-open by design: only 401 (revoked/expired token), 403 (Anthropic
|
|
20
|
+
* `permission_error` — the credential isn't entitled to serve this run, e.g.
|
|
21
|
+
* an org disabled Claude Code subscription access, #1072) and 429
|
|
20
22
|
* (session/weekly/per-model limit) mark the token unusable. network errors,
|
|
21
23
|
* 5xx, and request-shape drift (400) all keep today's subscription-first
|
|
22
24
|
* behavior, so the preflight can never fail a run that would have worked —
|