@noxsoft/anima 5.0.1 → 5.0.2
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/{accounts-BOkEyUcS.js → accounts-CSCVz9k9.js} +7 -7
- package/dist/{acp-cli-BcshtFqY.js → acp-cli-DyVMXQS8.js} +1 -1
- package/dist/{agent-c49U1LxE.js → agent-CWQdTmzC.js} +3 -3
- package/dist/{agent-Cil6Zvns.js → agent-DiEXJmhj.js} +8 -8
- package/dist/{agents-2BloqCjm.js → agents-rAIsxGuA.js} +8 -8
- package/dist/{anthropic-direct-runner-BVlO2Vi5.js → anthropic-direct-runner--MPTWkYA.js} +228 -193
- package/dist/{anthropic-direct-runner-mh6c_WBB.js → anthropic-direct-runner-_uCPWuBb.js} +232 -197
- package/dist/{audit-BbmRSFjf.js → audit-DqjWCAbO.js} +7 -7
- package/dist/{auth-choice-BVAMr2Dk.js → auth-choice-o5GHtPGA.js} +57 -5
- package/dist/{auth-profiles-Chf1JBpl.js → auth-profiles-DriJ4HU5.js} +1 -1
- package/dist/{banner-CgxCSS-T.js → banner-Gtp-FQrx.js} +1 -1
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +13 -13
- package/dist/bundled/bootstrap-extra-files/handler.js +3 -3
- package/dist/bundled/session-memory/handler.js +10 -10
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{channel-web-Y8i3EzJq.js → channel-web-Dz0XZNjP.js} +4 -4
- package/dist/{channels-status-issues-BlmAMBCg.js → channels-status-issues-Dqd_vE0e.js} +2 -2
- package/dist/{chrome-B2-33FWf.js → chrome-CB6uZMvl.js} +1 -1
- package/dist/{chrome-Cxl7I5SB.js → chrome-CKvch3UL.js} +7 -7
- package/dist/{clack-prompter-B-ZJG628.js → clack-prompter-BRLXmV52.js} +1 -1
- package/dist/{clack-prompter-CCRr4nAr.js → clack-prompter-OFy356QZ.js} +1 -1
- package/dist/{cli-C3uw3mId.js → cli-BZz5MnAE.js} +29 -29
- package/dist/{cli-CfvBmuhw.js → cli-CJfwHH_z.js} +5 -5
- package/dist/{cli-session-BzZYmGP1.js → cli-session-8PeURiwF.js} +6 -6
- package/dist/{cli-session-DgUHOBYo.js → cli-session-CczB2Dni.js} +1 -1
- package/dist/{command-registry-DLofaVIN.js → command-registry-C9kvlc67.js} +12 -12
- package/dist/{common-FlJ-8clz.js → common-C5mWuFve.js} +2 -2
- package/dist/{completion-cli-C9KvX2ZF.js → completion-cli-Ci_MzRm6.js} +2 -2
- package/dist/{completion-cli-3eYvtAih.js → completion-cli-DhbIrk13.js} +1 -1
- package/dist/{config-B-9UciOO.js → config-DezP7V65.js} +2 -2
- package/dist/{config-cli-CBXWDtvj.js → config-cli-ePL5ncye.js} +1 -1
- package/dist/{config-cli-cmNuTlw0.js → config-cli-uk9vdQZH.js} +1 -1
- package/dist/{config-guard-qiKju2Fg.js → config-guard-CCOBZ77Q.js} +1 -1
- package/dist/{configure-CGTBBLR_.js → configure-CkGsX5z9.js} +9 -9
- package/dist/{configure-Dpk4lSoz.js → configure-D-9Ezgal.js} +11 -11
- package/dist/{configure-jlAKeuki.js → configure-DxKMnCHX.js} +40 -40
- package/dist/{configure-CH_-SNya.js → configure-KP7q9c7h.js} +4 -4
- package/dist/{control-service-CUhnhmpc.js → control-service-D3rS-Yd1.js} +3 -3
- package/dist/{cron-cli-CKs1evSF.js → cron-cli-3QY_K0l_.js} +1 -1
- package/dist/{daemon-cli-CSCFNzUo.js → daemon-cli-NZ84KRN8.js} +3 -3
- package/dist/{deliver-BYxbDIF0.js → deliver-BJn4EmMZ.js} +1 -1
- package/dist/{deliver-DkF6LCoS.js → deliver-BhezvxNA.js} +4 -4
- package/dist/{deps-D9266xfk.js → deps-CBLy0fqA.js} +1 -1
- package/dist/{dispatcher-BeO47t7A.js → dispatcher-Bk0gFz4b.js} +1 -1
- package/dist/{doctor-DJfgnQ67.js → doctor-Bto-K3wL.js} +16 -16
- package/dist/{doctor-CWmHUAjs.js → doctor-DsRSWr6B.js} +5 -5
- package/dist/{doctor-completion-CtCwd1fi.js → doctor-completion-B-SFdu-f.js} +1 -1
- package/dist/{doctor-completion-CmJmktDv.js → doctor-completion-OQXTo6RG.js} +1 -1
- package/dist/{doctor-config-flow-D_8YNTSK.js → doctor-config-flow-CEkHIHe5.js} +1 -1
- package/dist/entry.js +1 -1
- package/dist/extensionAPI.js +1531 -411
- package/dist/{frontmatter-BmBmtOUh.js → frontmatter-Dsa7N963.js} +1 -1
- package/dist/{gateway-cli-DYrzIvOE.js → gateway-cli-Buxz8Y2n.js} +17 -17
- package/dist/{gateway-cli-BazTmg20.js → gateway-cli-CEGPCQ_C.js} +52 -52
- package/dist/{health-BRSKF_iF.js → health-CMIY7xAI.js} +12 -12
- package/dist/{health-JqtB_B8C.js → health-DoGFlPcq.js} +6 -6
- package/dist/{heartbeat-visibility-BGj2czmk.js → heartbeat-visibility-BOyU9ccw.js} +2 -2
- package/dist/{heartbeat-visibility-pyFf6XBW.js → heartbeat-visibility-DuRmIKOk.js} +2 -2
- package/dist/{hooks-cli-B7g3jEC3.js → hooks-cli-B1NXXLxe.js} +30 -30
- package/dist/{hooks-cli-ZK4Z044T.js → hooks-cli-BuIU6v0Q.js} +6 -6
- package/dist/index.js +11 -11
- package/dist/{installs-DBvIs2By.js → installs-Drds9RDI.js} +1 -1
- package/dist/{lanes-DyM6NSSL.js → lanes-4_8h1ZQO.js} +274 -12
- package/dist/llm-slug-generator.js +10 -10
- package/dist/{login-BbfWLOBl.js → login-DwP4KleZ.js} +2 -2
- package/dist/{login-qr-BqhujMcQ.js → login-qr-wnynuvLt.js} +5 -5
- package/dist/{manager-CcawxgaC.js → manager-ibNP7jE3.js} +2 -2
- package/dist/{manager-DS1DfkbC.js → manager-j0ghUvcW.js} +4 -4
- package/dist/{media-IIqSkFpd.js → media-BLVMhFB3.js} +2 -2
- package/dist/{memory-cli-wDO418hx.js → memory-cli-DSWsPuub.js} +6 -6
- package/dist/{model-auth-Uxm-wRjR.js → model-auth-BKf-GZVK.js} +3 -3
- package/dist/{models-tQnX4hL4.js → models-BU0CytAy.js} +32 -32
- package/dist/{node-cli-DAcW-rfA.js → node-cli-DcwOFcxq.js} +8 -8
- package/dist/{onboard-Dd6xFMYB.js → onboard-Dd5wJkeU.js} +6 -6
- package/dist/{onboard-dasbF9G1.js → onboard-c84IExbH.js} +4 -4
- package/dist/{onboard-channels-DisvVyIs.js → onboard-channels-CCoMzCXV.js} +3 -3
- package/dist/{onboard-channels-BUv3VbAL.js → onboard-channels-DYr7fQZq.js} +1 -1
- package/dist/{onboard-helpers-49TVSf7J.js → onboard-helpers-BRp_xJvW.js} +1 -1
- package/dist/{onboarding-Dq3bCBjc.js → onboarding-192Oe9K6.js} +8 -8
- package/dist/{onboarding-6Xs4orLw.js → onboarding-BINruD5r.js} +4 -4
- package/dist/{outbound-CliT3nme.js → outbound-BVSyNp-t.js} +1 -1
- package/dist/{outbound-CLII4C2A.js → outbound-BaBDhLp1.js} +1 -1
- package/dist/{parse-timeout-BnOIKwx8.js → parse-timeout-C1INlSUG.js} +2 -2
- package/dist/{pi-auth-json-DLroEcea.js → pi-auth-json-BSLIRI41.js} +2 -2
- package/dist/{pi-embedded-CS4D_0t7.js → pi-embedded-DJF_gfcQ.js} +667 -372
- package/dist/{pi-tools.policy-RhdERrk3.js → pi-tools.policy-Dtytaq21.js} +3 -3
- package/dist/{plugin-registry-l3z9phEV.js → plugin-registry-B0k-53-_.js} +1 -1
- package/dist/{plugin-registry-esOxvV3X.js → plugin-registry-CqUo-PU_.js} +1 -1
- package/dist/plugin-sdk/agents/gemini-direct-runner.d.ts +38 -0
- package/dist/plugin-sdk/commands/onboard-types.d.ts +2 -2
- package/dist/{plugins-cli-D2v0n4BL.js → plugins-cli-BYLUFsjH.js} +33 -33
- package/dist/{plugins-cli-CbXtuN4b.js → plugins-cli-YjM8IsVo.js} +6 -6
- package/dist/{ports-DyX87qKc.js → ports-BmSDzOHm.js} +1 -1
- package/dist/{ports-COy7Yg_k.js → ports-Bv3sZ3bd.js} +1 -1
- package/dist/{program-y0NPJuYm.js → program-CG5u1KAB.js} +36 -36
- package/dist/{program-context-CqPwmgYa.js → program-context-Co2gkUS0.js} +16 -16
- package/dist/{prompts-DTKoIKPV.js → prompts-DjKZfP5R.js} +67 -4
- package/dist/{prompts-DY0qdoD1.js → prompts-NvPXbNVc.js} +12 -1
- package/dist/{pw-ai-CJgeaF06.js → pw-ai-C3j5kV3T.js} +1 -1
- package/dist/{pw-ai-A7vggLw8.js → pw-ai-CvxTpFmB.js} +3 -3
- package/dist/{qmd-manager-DQsAtmK7.js → qmd-manager-BDq5_dd6.js} +2 -2
- package/dist/{register.agent-Bi2FqIlN.js → register.agent-PbAKdlNK.js} +10 -10
- package/dist/{register.agent-CRazop1A.js → register.agent-WAjZHuZe.js} +38 -38
- package/dist/{register.anima-FFaI_C8x.js → register.anima-CDcnWfQF.js} +2 -2
- package/dist/{register.anima-BmdRCJLy.js → register.anima-CZNjkwJH.js} +2 -2
- package/dist/{register.configure-FIXOWrO9.js → register.configure-C2zfgyp9.js} +39 -39
- package/dist/{register.configure-r5AViY3N.js → register.configure-CNhagtHN.js} +10 -10
- package/dist/{register.maintenance-Cpf0ZZTL.js → register.maintenance-DWJjWsIl.js} +11 -11
- package/dist/{register.maintenance-DJYji5mV.js → register.maintenance-DkF1Nu_y.js} +39 -39
- package/dist/{register.onboard-thGXwzOX.js → register.onboard-DiZzjEwz.js} +13 -13
- package/dist/{register.onboard-oUsWjmam.js → register.onboard-xOXNrMu-.js} +41 -41
- package/dist/{register.setup-B4MYuE3g.js → register.setup-BmbpgUVU.js} +41 -41
- package/dist/{register.setup-DWDrQ7RT.js → register.setup-QAjNkIZr.js} +13 -13
- package/dist/{register.status-health-sessions-CTKdzo7a.js → register.status-health-sessions-Cm-lxcT6.js} +37 -37
- package/dist/{register.status-health-sessions-Ce9QUAyj.js → register.status-health-sessions-RZxaVSa6.js} +7 -7
- package/dist/{register.subclis-CWmYc4AB.js → register.subclis-DCljHJ5a.js} +13 -13
- package/dist/{reply-CpHIJ9Kk.js → reply-Bw4torMz.js} +30 -30
- package/dist/{reply-BKpIrCr-.js → reply-Dd3EmI4M.js} +5 -5
- package/dist/{reply-prefix-BO_Dt82N.js → reply-prefix-DIoRETAD.js} +2 -2
- package/dist/{reply-prefix-BwVBvQXp.js → reply-prefix-Dy_yxyCq.js} +2 -2
- package/dist/{routes-XhTPYecR.js → routes-CNSdZAkl.js} +3 -3
- package/dist/{run--9hftM2H.js → run--R4y3YlU.js} +41 -41
- package/dist/{run-CGokiDR8.js → run-DgtaDzEe.js} +15 -15
- package/dist/{run-main-DxcOzCLw.js → run-main-eUWh86lF.js} +47 -47
- package/dist/{sandbox-CiwAPzO7.js → sandbox-BoNWDo4X.js} +1 -1
- package/dist/{sandbox-cli-DsrzwG_s.js → sandbox-cli-lbp0TV67.js} +10 -10
- package/dist/{security-cli-CouqfUGc.js → security-cli-CfvP5VqT.js} +13 -13
- package/dist/{server-context-B78_sSOW.js → server-context-IfJbmK25.js} +6 -6
- package/dist/{server-node-events-DJlLCF2M.js → server-node-events-BG4e9Ws7.js} +5 -5
- package/dist/{server-node-events-TN_Y9MgU.js → server-node-events-CFCwOP0n.js} +18 -18
- package/dist/{session-cost-usage-vltn5akf.js → session-cost-usage-lLyz71EH.js} +1 -1
- package/dist/{sessions-B7clh58j.js → sessions-BZgIAOwH.js} +2 -2
- package/dist/{sessions-B7ikzhI_.js → sessions-C3bteMLZ.js} +3 -3
- package/dist/{settings-cli-JU5bg5jb.js → settings-cli-C9TMl9Ye.js} +11 -11
- package/dist/{settings-cli-DjVugHID.js → settings-cli-Cf0LCRjt.js} +40 -40
- package/dist/{setup-token-CvRwJUVY.js → setup-token-DBwe6GDq.js} +1 -1
- package/dist/{setup-token-wFsQlaW2.js → setup-token-DTgSXfj7.js} +14 -14
- package/dist/{shell-env-C1yK_Rod.js → shell-env-V934Ymrk.js} +1 -1
- package/dist/{skill-scanner-BzOYLFR8.js → skill-scanner-DPQDhVEr.js} +1 -1
- package/dist/{skills-install-D_bxdc-6.js → skills-install-CC63dOs4.js} +2 -2
- package/dist/{sqlite-jyjat2Yt.js → sqlite-Co_lF3s-.js} +1 -1
- package/dist/{start-M9abr1O1.js → start-B2DQAmEK.js} +17 -17
- package/dist/{start-CAbxlnFZ.js → start-CHLGEHeF.js} +50 -50
- package/dist/{status-YH65ctig.js → status-BsCeWiLS.js} +1 -1
- package/dist/{status-1TjjCE1l.js → status-C4ZTdSwA.js} +14 -14
- package/dist/{status-CxxXFU4R.js → status-CwjUrZ7a.js} +4 -4
- package/dist/{status-CrJV2Y4x.js → status-D_FTGxBZ.js} +1 -1
- package/dist/{status.update-BzAuf_G-.js → status.update-Cx5l38lC.js} +1 -1
- package/dist/{subagent-registry-xJY9wqZy.js → subagent-registry-7qo93ip8.js} +2 -2
- package/dist/{subagent-registry-3Dw3YppH.js → subagent-registry-EoZxIEKl.js} +8 -8
- package/dist/{subagent-registry-B_65nJFp.js → subagent-registry-hRt-aJnL.js} +9 -9
- package/dist/{timeout-Dbspj9Jf.js → timeout-B0mCaCG5.js} +275 -13
- package/dist/{pi-embedded-helpers-DnRfi8bN.js → tokens-DXjI-TIV.js} +19 -19
- package/dist/{tool-images-y_Ribdl6.js → tool-images-09hnvylP.js} +1 -1
- package/dist/{tui--eIVX_W0.js → tui-LRoQ5xyx.js} +2 -2
- package/dist/{tui-cli-DBV3lnXA.js → tui-cli-DO_6-GWy.js} +14 -14
- package/dist/{update-VFbS9X5L.js → update-DmVSt9s3.js} +1 -1
- package/dist/{update-cli-QP5L8xlv.js → update-cli-CnYFvEud.js} +11 -11
- package/dist/{update-cli-BzaQvirL.js → update-cli-jEzmvaqS.js} +44 -44
- package/dist/{update-runner-B_oj7S_n.js → update-runner-IARYjXbS.js} +2 -2
- package/dist/{update-runner-ZuJN8uBE.js → update-runner-OPbRGUfX.js} +1 -1
- package/dist/{usage-DzKbMa8N.js → usage-BTc8TK3f.js} +6 -6
- package/dist/{web-nI3eIGAT.js → web-Honf40Cm.js} +31 -31
- package/dist/{web-CgQc2Raf.js → web-kEraFv8s.js} +28 -28
- package/dist/{web-DtWSf5wm.js → web-u554zkLK.js} +7 -7
- package/dist/{whatsapp-actions-BzPQUcRR.js → whatsapp-actions-I9RXDK_k.js} +6 -6
- package/dist/{whatsapp-actions-CZuxHplg.js → whatsapp-actions-_BrQebuX.js} +5 -5
- package/package.json +1 -1
- /package/dist/{auth-DuBZWd74.js → auth-DK-0XxZU.js} +0 -0
- /package/dist/{boolean-M-esQJt6.js → boolean-Ce2-qkSB.js} +0 -0
- /package/dist/{errors-l_q3lLBC.js → errors-B9FgVZ2s.js} +0 -0
- /package/dist/{image-ops-BVtm-rU-.js → image-ops-BsXjYj1e.js} +0 -0
- /package/dist/{internal-hooks-Exeq-wmx.js → internal-hooks-CZOlj8zG.js} +0 -0
- /package/dist/{model-selection-xZLlICyg.js → model-selection-DT-L7bjC.js} +0 -0
- /package/dist/{paths-B_RPJLsn.js → paths-CxBUgtZS.js} +0 -0
- /package/dist/{pi-embedded-helpers-Cxzk0pra.js → pi-embedded-helpers-CWl0I3Qm.js} +0 -0
- /package/dist/{plugins-CFBEoAN4.js → plugins-BDSP0cT6.js} +0 -0
- /package/dist/{tokens-DLpZ8RFH.js → tokens-QjoPADtG.js} +0 -0
- /package/dist/{transcript-events-CJRvASY_.js → transcript-events-DdnTeoR1.js} +0 -0
package/dist/extensionAPI.js
CHANGED
|
@@ -19,7 +19,7 @@ import "express";
|
|
|
19
19
|
import "undici";
|
|
20
20
|
import "file-type";
|
|
21
21
|
import "ws";
|
|
22
|
-
import { getOAuthProviders } from "@mariozechner/pi-ai";
|
|
22
|
+
import { getEnvApiKey, getOAuthApiKey, getOAuthProviders } from "@mariozechner/pi-ai";
|
|
23
23
|
import "node-edge-tts";
|
|
24
24
|
|
|
25
25
|
//#region src/infra/home-dir.ts
|
|
@@ -173,6 +173,22 @@ function resolveDefaultConfigCandidates(env = process.env, homedir = envHomedir(
|
|
|
173
173
|
candidates.push(path.join(newStateDir(effectiveHomedir), CONFIG_FILENAME));
|
|
174
174
|
return candidates;
|
|
175
175
|
}
|
|
176
|
+
const OAUTH_FILENAME = "oauth.json";
|
|
177
|
+
/**
|
|
178
|
+
* OAuth credentials storage directory.
|
|
179
|
+
*
|
|
180
|
+
* Precedence:
|
|
181
|
+
* - `ANIMA_OAUTH_DIR` (explicit override)
|
|
182
|
+
* - `$*_STATE_DIR/credentials` (canonical server/default)
|
|
183
|
+
*/
|
|
184
|
+
function resolveOAuthDir(env = process.env, stateDir = resolveStateDir(env, envHomedir(env))) {
|
|
185
|
+
const override = env.ANIMA_OAUTH_DIR?.trim();
|
|
186
|
+
if (override) return resolveUserPath$1(override, env, envHomedir(env));
|
|
187
|
+
return path.join(stateDir, "credentials");
|
|
188
|
+
}
|
|
189
|
+
function resolveOAuthPath(env = process.env, stateDir = resolveStateDir(env, envHomedir(env))) {
|
|
190
|
+
return path.join(resolveOAuthDir(env, stateDir), OAUTH_FILENAME);
|
|
191
|
+
}
|
|
176
192
|
|
|
177
193
|
//#endregion
|
|
178
194
|
//#region src/sessions/session-key-utils.ts
|
|
@@ -1704,241 +1720,98 @@ function resolveThinkingDefault(params) {
|
|
|
1704
1720
|
}
|
|
1705
1721
|
|
|
1706
1722
|
//#endregion
|
|
1707
|
-
//#region src/
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
"-p",
|
|
1712
|
-
"--output-format",
|
|
1713
|
-
"json",
|
|
1714
|
-
"--dangerously-skip-permissions"
|
|
1715
|
-
],
|
|
1716
|
-
resumeArgs: [
|
|
1717
|
-
"-p",
|
|
1718
|
-
"--output-format",
|
|
1719
|
-
"json",
|
|
1720
|
-
"--dangerously-skip-permissions",
|
|
1721
|
-
"--resume",
|
|
1722
|
-
"{sessionId}"
|
|
1723
|
-
],
|
|
1724
|
-
output: "jsonl",
|
|
1725
|
-
input: "arg",
|
|
1726
|
-
modelArg: "--model",
|
|
1727
|
-
modelAliases: {
|
|
1728
|
-
opus: "opus",
|
|
1729
|
-
"opus-4.6": "opus",
|
|
1730
|
-
"opus-4.5": "opus",
|
|
1731
|
-
"opus-4": "opus",
|
|
1732
|
-
"claude-opus-4-6": "opus",
|
|
1733
|
-
"claude-opus-4-5": "opus",
|
|
1734
|
-
"claude-opus-4": "opus",
|
|
1735
|
-
sonnet: "sonnet",
|
|
1736
|
-
"sonnet-4.5": "sonnet",
|
|
1737
|
-
"sonnet-4.1": "sonnet",
|
|
1738
|
-
"sonnet-4.0": "sonnet",
|
|
1739
|
-
"claude-sonnet-4-5": "sonnet",
|
|
1740
|
-
"claude-sonnet-4-1": "sonnet",
|
|
1741
|
-
"claude-sonnet-4-0": "sonnet",
|
|
1742
|
-
haiku: "haiku",
|
|
1743
|
-
"haiku-3.5": "haiku",
|
|
1744
|
-
"claude-haiku-3-5": "haiku"
|
|
1745
|
-
},
|
|
1746
|
-
sessionArg: "--session-id",
|
|
1747
|
-
sessionMode: "always",
|
|
1748
|
-
sessionIdFields: [
|
|
1749
|
-
"session_id",
|
|
1750
|
-
"sessionId",
|
|
1751
|
-
"conversation_id",
|
|
1752
|
-
"conversationId"
|
|
1753
|
-
],
|
|
1754
|
-
systemPromptArg: "--append-system-prompt",
|
|
1755
|
-
systemPromptMode: "append",
|
|
1756
|
-
systemPromptWhen: "first",
|
|
1757
|
-
clearEnv: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD"],
|
|
1758
|
-
serialize: true
|
|
1759
|
-
};
|
|
1760
|
-
const DEFAULT_CODEX_BACKEND = {
|
|
1761
|
-
command: "codex",
|
|
1762
|
-
args: [
|
|
1763
|
-
"exec",
|
|
1764
|
-
"--json",
|
|
1765
|
-
"--color",
|
|
1766
|
-
"never",
|
|
1767
|
-
"--sandbox",
|
|
1768
|
-
"read-only",
|
|
1769
|
-
"--skip-git-repo-check"
|
|
1770
|
-
],
|
|
1771
|
-
resumeArgs: [
|
|
1772
|
-
"exec",
|
|
1773
|
-
"resume",
|
|
1774
|
-
"{sessionId}"
|
|
1775
|
-
],
|
|
1776
|
-
output: "jsonl",
|
|
1777
|
-
resumeOutput: "text",
|
|
1778
|
-
input: "arg",
|
|
1779
|
-
modelArg: "--model",
|
|
1780
|
-
imageArg: "--image",
|
|
1781
|
-
sessionMode: "existing",
|
|
1782
|
-
serialize: true
|
|
1783
|
-
};
|
|
1784
|
-
const CLAUDE_BACKEND_ALIASES = [
|
|
1785
|
-
"claude-cli",
|
|
1786
|
-
"anthropic",
|
|
1787
|
-
"claude"
|
|
1788
|
-
];
|
|
1789
|
-
const CODEX_BACKEND_ALIASES = [
|
|
1790
|
-
"codex-cli",
|
|
1791
|
-
"openai-codex",
|
|
1792
|
-
"openai",
|
|
1793
|
-
"codex"
|
|
1794
|
-
];
|
|
1795
|
-
const CLAUDE_BACKEND_ALIAS_SET = new Set(CLAUDE_BACKEND_ALIASES.map((alias) => normalizeBackendKey(alias)));
|
|
1796
|
-
const CODEX_BACKEND_ALIAS_SET = new Set(CODEX_BACKEND_ALIASES.map((alias) => normalizeBackendKey(alias)));
|
|
1797
|
-
function normalizeBackendKey(key) {
|
|
1798
|
-
return normalizeProviderId(key);
|
|
1799
|
-
}
|
|
1800
|
-
function pickBackendConfig(config, normalizedId) {
|
|
1801
|
-
for (const [key, entry] of Object.entries(config)) if (normalizeBackendKey(key) === normalizedId) return entry;
|
|
1723
|
+
//#region src/logging/redact-identifier.ts
|
|
1724
|
+
function sha256HexPrefix(value, len = 12) {
|
|
1725
|
+
const safeLen = Number.isFinite(len) ? Math.max(1, Math.floor(len)) : 12;
|
|
1726
|
+
return crypto.createHash("sha256").update(value).digest("hex").slice(0, safeLen);
|
|
1802
1727
|
}
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
}
|
|
1728
|
+
function redactIdentifier(value, opts) {
|
|
1729
|
+
const trimmed = value?.trim();
|
|
1730
|
+
if (!trimmed) return "-";
|
|
1731
|
+
return `sha256:${sha256HexPrefix(trimmed, opts?.len ?? 12)}`;
|
|
1808
1732
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1733
|
+
|
|
1734
|
+
//#endregion
|
|
1735
|
+
//#region src/agents/workspace-run.ts
|
|
1736
|
+
function resolveRunAgentId(params) {
|
|
1737
|
+
const rawSessionKey = params.sessionKey?.trim() ?? "";
|
|
1738
|
+
const shape = classifySessionKeyShape(rawSessionKey);
|
|
1739
|
+
if (shape === "malformed_agent") throw new Error("Malformed agent session key; refusing workspace resolution.");
|
|
1740
|
+
const explicit = typeof params.agentId === "string" && params.agentId.trim() ? normalizeAgentId(params.agentId) : void 0;
|
|
1741
|
+
if (explicit) return {
|
|
1742
|
+
agentId: explicit,
|
|
1743
|
+
agentIdSource: "explicit"
|
|
1744
|
+
};
|
|
1745
|
+
const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
|
|
1746
|
+
if (shape === "missing" || shape === "legacy_or_alias") return {
|
|
1747
|
+
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
|
1748
|
+
agentIdSource: "default"
|
|
1749
|
+
};
|
|
1750
|
+
const parsed = parseAgentSessionKey(rawSessionKey);
|
|
1751
|
+
if (parsed?.agentId) return {
|
|
1752
|
+
agentId: normalizeAgentId(parsed.agentId),
|
|
1753
|
+
agentIdSource: "session_key"
|
|
1754
|
+
};
|
|
1811
1755
|
return {
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
args: override.args ?? base.args,
|
|
1815
|
-
env: {
|
|
1816
|
-
...base.env,
|
|
1817
|
-
...override.env
|
|
1818
|
-
},
|
|
1819
|
-
modelAliases: {
|
|
1820
|
-
...base.modelAliases,
|
|
1821
|
-
...override.modelAliases
|
|
1822
|
-
},
|
|
1823
|
-
clearEnv: Array.from(new Set([...base.clearEnv ?? [], ...override.clearEnv ?? []])),
|
|
1824
|
-
sessionIdFields: override.sessionIdFields ?? base.sessionIdFields,
|
|
1825
|
-
sessionArgs: override.sessionArgs ?? base.sessionArgs,
|
|
1826
|
-
resumeArgs: override.resumeArgs ?? base.resumeArgs
|
|
1756
|
+
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
|
1757
|
+
agentIdSource: "default"
|
|
1827
1758
|
};
|
|
1828
1759
|
}
|
|
1829
|
-
function
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const command = merged.command?.trim();
|
|
1847
|
-
if (!command) return null;
|
|
1848
|
-
return {
|
|
1849
|
-
id: normalizeBackendKey("codex-cli"),
|
|
1850
|
-
config: {
|
|
1851
|
-
...merged,
|
|
1852
|
-
command
|
|
1853
|
-
}
|
|
1760
|
+
function redactRunIdentifier(value) {
|
|
1761
|
+
return redactIdentifier(value, { len: 12 });
|
|
1762
|
+
}
|
|
1763
|
+
function resolveRunWorkspaceDir(params) {
|
|
1764
|
+
const requested = params.workspaceDir;
|
|
1765
|
+
const { agentId, agentIdSource } = resolveRunAgentId({
|
|
1766
|
+
sessionKey: params.sessionKey,
|
|
1767
|
+
agentId: params.agentId,
|
|
1768
|
+
config: params.config
|
|
1769
|
+
});
|
|
1770
|
+
if (typeof requested === "string") {
|
|
1771
|
+
const trimmed = requested.trim();
|
|
1772
|
+
if (trimmed) return {
|
|
1773
|
+
workspaceDir: resolveUserPath(trimmed),
|
|
1774
|
+
usedFallback: false,
|
|
1775
|
+
agentId,
|
|
1776
|
+
agentIdSource
|
|
1854
1777
|
};
|
|
1855
1778
|
}
|
|
1856
|
-
const
|
|
1857
|
-
if (!override) return null;
|
|
1858
|
-
const command = override.command?.trim();
|
|
1859
|
-
if (!command) return null;
|
|
1779
|
+
const fallbackReason = requested == null ? "missing" : typeof requested === "string" ? "blank" : "invalid_type";
|
|
1860
1780
|
return {
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1781
|
+
workspaceDir: resolveUserPath(resolveAgentWorkspaceDir(params.config ?? {}, agentId)),
|
|
1782
|
+
usedFallback: true,
|
|
1783
|
+
fallbackReason,
|
|
1784
|
+
agentId,
|
|
1785
|
+
agentIdSource
|
|
1866
1786
|
};
|
|
1867
1787
|
}
|
|
1868
1788
|
|
|
1869
1789
|
//#endregion
|
|
1870
|
-
//#region src/
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
];
|
|
1888
|
-
const
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
];
|
|
1894
|
-
|
|
1895
|
-
const DEFAULT_FALSY_SET = new Set(DEFAULT_FALSY);
|
|
1896
|
-
function parseBooleanValue(value, options = {}) {
|
|
1897
|
-
if (typeof value === "boolean") return value;
|
|
1898
|
-
if (typeof value !== "string") return;
|
|
1899
|
-
const normalized = value.trim().toLowerCase();
|
|
1900
|
-
if (!normalized) return;
|
|
1901
|
-
const truthy = options.truthy ?? DEFAULT_TRUTHY;
|
|
1902
|
-
const falsy = options.falsy ?? DEFAULT_FALSY;
|
|
1903
|
-
const truthySet = truthy === DEFAULT_TRUTHY ? DEFAULT_TRUTHY_SET : new Set(truthy);
|
|
1904
|
-
const falsySet = falsy === DEFAULT_FALSY ? DEFAULT_FALSY_SET : new Set(falsy);
|
|
1905
|
-
if (truthySet.has(normalized)) return true;
|
|
1906
|
-
if (falsySet.has(normalized)) return false;
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
//#endregion
|
|
1910
|
-
//#region src/infra/env.ts
|
|
1911
|
-
const log$8 = createSubsystemLogger("env");
|
|
1912
|
-
function isTruthyEnvValue(value) {
|
|
1913
|
-
return parseBooleanValue(value) === true;
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
//#endregion
|
|
1917
|
-
//#region src/hooks/internal-hooks.ts
|
|
1918
|
-
/** Registry of hook handlers by event key */
|
|
1919
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
1920
|
-
/**
|
|
1921
|
-
* Trigger a hook event
|
|
1922
|
-
*
|
|
1923
|
-
* Calls all handlers registered for:
|
|
1924
|
-
* 1. The general event type (e.g., 'command')
|
|
1925
|
-
* 2. The specific event:action combination (e.g., 'command:new')
|
|
1926
|
-
*
|
|
1927
|
-
* Handlers are called in registration order. Errors are caught and logged
|
|
1928
|
-
* but don't prevent other handlers from running.
|
|
1929
|
-
*
|
|
1930
|
-
* @param event - The event to trigger
|
|
1931
|
-
*/
|
|
1932
|
-
async function triggerInternalHook(event) {
|
|
1933
|
-
const typeHandlers = handlers.get(event.type) ?? [];
|
|
1934
|
-
const specificHandlers = handlers.get(`${event.type}:${event.action}`) ?? [];
|
|
1935
|
-
const allHandlers = [...typeHandlers, ...specificHandlers];
|
|
1936
|
-
if (allHandlers.length === 0) return;
|
|
1937
|
-
for (const handler of allHandlers) try {
|
|
1938
|
-
await handler(event);
|
|
1939
|
-
} catch (err) {
|
|
1940
|
-
console.error(`Hook error [${event.type}:${event.action}]:`, err instanceof Error ? err.message : String(err));
|
|
1941
|
-
}
|
|
1790
|
+
//#region src/hooks/internal-hooks.ts
|
|
1791
|
+
/** Registry of hook handlers by event key */
|
|
1792
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
1793
|
+
/**
|
|
1794
|
+
* Trigger a hook event
|
|
1795
|
+
*
|
|
1796
|
+
* Calls all handlers registered for:
|
|
1797
|
+
* 1. The general event type (e.g., 'command')
|
|
1798
|
+
* 2. The specific event:action combination (e.g., 'command:new')
|
|
1799
|
+
*
|
|
1800
|
+
* Handlers are called in registration order. Errors are caught and logged
|
|
1801
|
+
* but don't prevent other handlers from running.
|
|
1802
|
+
*
|
|
1803
|
+
* @param event - The event to trigger
|
|
1804
|
+
*/
|
|
1805
|
+
async function triggerInternalHook(event) {
|
|
1806
|
+
const typeHandlers = handlers.get(event.type) ?? [];
|
|
1807
|
+
const specificHandlers = handlers.get(`${event.type}:${event.action}`) ?? [];
|
|
1808
|
+
const allHandlers = [...typeHandlers, ...specificHandlers];
|
|
1809
|
+
if (allHandlers.length === 0) return;
|
|
1810
|
+
for (const handler of allHandlers) try {
|
|
1811
|
+
await handler(event);
|
|
1812
|
+
} catch (err) {
|
|
1813
|
+
console.error(`Hook error [${event.type}:${event.action}]:`, err instanceof Error ? err.message : String(err));
|
|
1814
|
+
}
|
|
1942
1815
|
}
|
|
1943
1816
|
/**
|
|
1944
1817
|
* Create a hook event with common fields filled in
|
|
@@ -2076,6 +1949,42 @@ function loadDotEnv(opts) {
|
|
|
2076
1949
|
});
|
|
2077
1950
|
}
|
|
2078
1951
|
|
|
1952
|
+
//#endregion
|
|
1953
|
+
//#region src/utils/boolean.ts
|
|
1954
|
+
const DEFAULT_TRUTHY = [
|
|
1955
|
+
"true",
|
|
1956
|
+
"1",
|
|
1957
|
+
"yes",
|
|
1958
|
+
"on"
|
|
1959
|
+
];
|
|
1960
|
+
const DEFAULT_FALSY = [
|
|
1961
|
+
"false",
|
|
1962
|
+
"0",
|
|
1963
|
+
"no",
|
|
1964
|
+
"off"
|
|
1965
|
+
];
|
|
1966
|
+
const DEFAULT_TRUTHY_SET = new Set(DEFAULT_TRUTHY);
|
|
1967
|
+
const DEFAULT_FALSY_SET = new Set(DEFAULT_FALSY);
|
|
1968
|
+
function parseBooleanValue(value, options = {}) {
|
|
1969
|
+
if (typeof value === "boolean") return value;
|
|
1970
|
+
if (typeof value !== "string") return;
|
|
1971
|
+
const normalized = value.trim().toLowerCase();
|
|
1972
|
+
if (!normalized) return;
|
|
1973
|
+
const truthy = options.truthy ?? DEFAULT_TRUTHY;
|
|
1974
|
+
const falsy = options.falsy ?? DEFAULT_FALSY;
|
|
1975
|
+
const truthySet = truthy === DEFAULT_TRUTHY ? DEFAULT_TRUTHY_SET : new Set(truthy);
|
|
1976
|
+
const falsySet = falsy === DEFAULT_FALSY ? DEFAULT_FALSY_SET : new Set(falsy);
|
|
1977
|
+
if (truthySet.has(normalized)) return true;
|
|
1978
|
+
if (falsySet.has(normalized)) return false;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
//#endregion
|
|
1982
|
+
//#region src/infra/env.ts
|
|
1983
|
+
const log$9 = createSubsystemLogger("env");
|
|
1984
|
+
function isTruthyEnvValue(value) {
|
|
1985
|
+
return parseBooleanValue(value) === true;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
2079
1988
|
//#endregion
|
|
2080
1989
|
//#region src/infra/shell-env.ts
|
|
2081
1990
|
const DEFAULT_TIMEOUT_MS$1 = 15e3;
|
|
@@ -2175,6 +2084,9 @@ function resolveShellEnvFallbackTimeoutMs(env) {
|
|
|
2175
2084
|
if (!Number.isFinite(parsed)) return DEFAULT_TIMEOUT_MS$1;
|
|
2176
2085
|
return Math.max(0, parsed);
|
|
2177
2086
|
}
|
|
2087
|
+
function getShellEnvAppliedKeys() {
|
|
2088
|
+
return [...lastAppliedKeys];
|
|
2089
|
+
}
|
|
2178
2090
|
|
|
2179
2091
|
//#endregion
|
|
2180
2092
|
//#region src/version.ts
|
|
@@ -7153,9 +7065,58 @@ const SANDBOX_STATE_DIR = path.join(STATE_DIR, "sandbox");
|
|
|
7153
7065
|
const SANDBOX_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "containers.json");
|
|
7154
7066
|
const SANDBOX_BROWSER_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "browsers.json");
|
|
7155
7067
|
|
|
7068
|
+
//#endregion
|
|
7069
|
+
//#region src/cli/cli-name.ts
|
|
7070
|
+
const DEFAULT_CLI_NAME = "anima";
|
|
7071
|
+
const KNOWN_CLI_NAMES = new Set([DEFAULT_CLI_NAME]);
|
|
7072
|
+
const CLI_PREFIX_RE$1 = /^(?:((?:pnpm|npm|bunx|npx)\s+))?anima\b/;
|
|
7073
|
+
function resolveCliName(argv = process.argv) {
|
|
7074
|
+
const argv1 = argv[1];
|
|
7075
|
+
if (!argv1) return DEFAULT_CLI_NAME;
|
|
7076
|
+
const base = path.basename(argv1).trim();
|
|
7077
|
+
if (KNOWN_CLI_NAMES.has(base)) return base;
|
|
7078
|
+
return DEFAULT_CLI_NAME;
|
|
7079
|
+
}
|
|
7080
|
+
function replaceCliName(command, cliName = resolveCliName()) {
|
|
7081
|
+
if (!command.trim()) return command;
|
|
7082
|
+
if (!CLI_PREFIX_RE$1.test(command)) return command;
|
|
7083
|
+
return command.replace(CLI_PREFIX_RE$1, (_match, runner) => {
|
|
7084
|
+
return `${runner ?? ""}${cliName}`;
|
|
7085
|
+
});
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
//#endregion
|
|
7089
|
+
//#region src/cli/profile-utils.ts
|
|
7090
|
+
const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
7091
|
+
function isValidProfileName(value) {
|
|
7092
|
+
if (!value) return false;
|
|
7093
|
+
return PROFILE_NAME_RE.test(value);
|
|
7094
|
+
}
|
|
7095
|
+
function normalizeProfileName(raw) {
|
|
7096
|
+
const profile = raw?.trim();
|
|
7097
|
+
if (!profile) return null;
|
|
7098
|
+
if (profile.toLowerCase() === "default") return null;
|
|
7099
|
+
if (!isValidProfileName(profile)) return null;
|
|
7100
|
+
return profile;
|
|
7101
|
+
}
|
|
7102
|
+
|
|
7103
|
+
//#endregion
|
|
7104
|
+
//#region src/cli/command-format.ts
|
|
7105
|
+
const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+anima\b|^anima\b/;
|
|
7106
|
+
const PROFILE_FLAG_RE = /(?:^|\s)--profile(?:\s|=|$)/;
|
|
7107
|
+
const DEV_FLAG_RE = /(?:^|\s)--dev(?:\s|$)/;
|
|
7108
|
+
function formatCliCommand(command, env = process.env) {
|
|
7109
|
+
const normalizedCommand = replaceCliName(command, resolveCliName());
|
|
7110
|
+
const profile = normalizeProfileName(env.ANIMA_PROFILE);
|
|
7111
|
+
if (!profile) return normalizedCommand;
|
|
7112
|
+
if (!CLI_PREFIX_RE.test(normalizedCommand)) return normalizedCommand;
|
|
7113
|
+
if (PROFILE_FLAG_RE.test(normalizedCommand) || DEV_FLAG_RE.test(normalizedCommand)) return normalizedCommand;
|
|
7114
|
+
return normalizedCommand.replace(CLI_PREFIX_RE, (match) => `${match} --profile ${profile}`);
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7156
7117
|
//#endregion
|
|
7157
7118
|
//#region src/agents/skills/plugin-skills.ts
|
|
7158
|
-
const log$
|
|
7119
|
+
const log$8 = createSubsystemLogger("skills");
|
|
7159
7120
|
|
|
7160
7121
|
//#endregion
|
|
7161
7122
|
//#region src/agents/skills/workspace.ts
|
|
@@ -7239,7 +7200,7 @@ const LSOF_CANDIDATES = process.platform === "darwin" ? ["/usr/sbin/lsof", "/usr
|
|
|
7239
7200
|
|
|
7240
7201
|
//#endregion
|
|
7241
7202
|
//#region src/browser/chrome.ts
|
|
7242
|
-
const log$
|
|
7203
|
+
const log$7 = createSubsystemLogger("browser").child("chrome");
|
|
7243
7204
|
|
|
7244
7205
|
//#endregion
|
|
7245
7206
|
//#region src/agents/sandbox/docker.ts
|
|
@@ -7424,7 +7385,7 @@ function resolveCleanupState() {
|
|
|
7424
7385
|
};
|
|
7425
7386
|
return proc[CLEANUP_STATE_KEY];
|
|
7426
7387
|
}
|
|
7427
|
-
function isAlive(pid) {
|
|
7388
|
+
function isAlive$1(pid) {
|
|
7428
7389
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
7429
7390
|
try {
|
|
7430
7391
|
process.kill(pid, 0);
|
|
@@ -7479,7 +7440,7 @@ function registerCleanupHandlers() {
|
|
|
7479
7440
|
} catch {}
|
|
7480
7441
|
}
|
|
7481
7442
|
}
|
|
7482
|
-
async function readLockPayload(lockPath) {
|
|
7443
|
+
async function readLockPayload$1(lockPath) {
|
|
7483
7444
|
try {
|
|
7484
7445
|
const raw = await fs$1.readFile(lockPath, "utf8");
|
|
7485
7446
|
const parsed = JSON.parse(raw);
|
|
@@ -7545,10 +7506,10 @@ async function acquireSessionWriteLock(params) {
|
|
|
7545
7506
|
} };
|
|
7546
7507
|
} catch (err) {
|
|
7547
7508
|
if (err.code !== "EEXIST") throw err;
|
|
7548
|
-
const payload = await readLockPayload(lockPath);
|
|
7509
|
+
const payload = await readLockPayload$1(lockPath);
|
|
7549
7510
|
const createdAt = payload?.createdAt ? Date.parse(payload.createdAt) : NaN;
|
|
7550
7511
|
const stale = !Number.isFinite(createdAt) || Date.now() - createdAt > staleMs;
|
|
7551
|
-
const alive = payload?.pid ? isAlive(payload.pid) : false;
|
|
7512
|
+
const alive = payload?.pid ? isAlive$1(payload.pid) : false;
|
|
7552
7513
|
if (stale || !alive) {
|
|
7553
7514
|
await fs$1.rm(lockPath, { force: true });
|
|
7554
7515
|
continue;
|
|
@@ -7557,7 +7518,7 @@ async function acquireSessionWriteLock(params) {
|
|
|
7557
7518
|
await new Promise((r) => setTimeout(r, delay));
|
|
7558
7519
|
}
|
|
7559
7520
|
}
|
|
7560
|
-
const payload = await readLockPayload(lockPath);
|
|
7521
|
+
const payload = await readLockPayload$1(lockPath);
|
|
7561
7522
|
const owner = payload?.pid ? `pid=${payload.pid}` : "unknown";
|
|
7562
7523
|
throw new Error(`session file locked (timeout ${timeoutMs}ms): ${owner} ${lockPath}`);
|
|
7563
7524
|
}
|
|
@@ -7656,7 +7617,7 @@ function getFileMtimeMs(filePath) {
|
|
|
7656
7617
|
|
|
7657
7618
|
//#endregion
|
|
7658
7619
|
//#region src/config/sessions/store.ts
|
|
7659
|
-
const log$
|
|
7620
|
+
const log$6 = createSubsystemLogger("sessions/store");
|
|
7660
7621
|
const SESSION_STORE_CACHE = /* @__PURE__ */ new Map();
|
|
7661
7622
|
const DEFAULT_SESSION_STORE_TTL_MS = 45e3;
|
|
7662
7623
|
function isSessionStoreRecord(value) {
|
|
@@ -7799,7 +7760,7 @@ function pruneStaleEntries(store, overrideMaxAgeMs, opts = {}) {
|
|
|
7799
7760
|
delete store[key];
|
|
7800
7761
|
pruned++;
|
|
7801
7762
|
}
|
|
7802
|
-
if (pruned > 0 && opts.log !== false) log$
|
|
7763
|
+
if (pruned > 0 && opts.log !== false) log$6.info("pruned stale session entries", {
|
|
7803
7764
|
pruned,
|
|
7804
7765
|
maxAgeMs
|
|
7805
7766
|
});
|
|
@@ -7842,7 +7803,7 @@ function capEntryCount(store, overrideMax, opts = {}) {
|
|
|
7842
7803
|
return getEntryUpdatedAt(store[b]) - aTime;
|
|
7843
7804
|
}).slice(maxEntries);
|
|
7844
7805
|
for (const key of toRemove) delete store[key];
|
|
7845
|
-
if (opts.log !== false) log$
|
|
7806
|
+
if (opts.log !== false) log$6.info("capped session entry count", {
|
|
7846
7807
|
removed: toRemove.length,
|
|
7847
7808
|
maxEntries
|
|
7848
7809
|
});
|
|
@@ -7868,7 +7829,7 @@ async function rotateSessionFile(storePath, overrideBytes) {
|
|
|
7868
7829
|
const backupPath = `${storePath}.bak.${Date.now()}`;
|
|
7869
7830
|
try {
|
|
7870
7831
|
await fs.promises.rename(storePath, backupPath);
|
|
7871
|
-
log$
|
|
7832
|
+
log$6.info("rotated session store file", {
|
|
7872
7833
|
backupPath: path.basename(backupPath),
|
|
7873
7834
|
sizeBytes: fileSize
|
|
7874
7835
|
});
|
|
@@ -7883,7 +7844,7 @@ async function rotateSessionFile(storePath, overrideBytes) {
|
|
|
7883
7844
|
if (backups.length > maxBackups) {
|
|
7884
7845
|
const toDelete = backups.slice(maxBackups);
|
|
7885
7846
|
for (const old of toDelete) await fs.promises.unlink(path.join(dir, old)).catch(() => void 0);
|
|
7886
|
-
log$
|
|
7847
|
+
log$6.info("cleaned up old session store backups", { deleted: toDelete.length });
|
|
7887
7848
|
}
|
|
7888
7849
|
} catch {}
|
|
7889
7850
|
return true;
|
|
@@ -7903,7 +7864,7 @@ async function saveSessionStoreUnlocked(storePath, store, opts) {
|
|
|
7903
7864
|
maxEntries: maintenance.maxEntries
|
|
7904
7865
|
});
|
|
7905
7866
|
if (warning) {
|
|
7906
|
-
log$
|
|
7867
|
+
log$6.warn("session maintenance would evict active session; skipping enforcement", {
|
|
7907
7868
|
activeSessionKey: warning.activeSessionKey,
|
|
7908
7869
|
wouldPrune: warning.wouldPrune,
|
|
7909
7870
|
wouldCap: warning.wouldCap,
|
|
@@ -8221,7 +8182,7 @@ function isFailoverErrorMessage(raw) {
|
|
|
8221
8182
|
//#endregion
|
|
8222
8183
|
//#region src/agents/tool-images.ts
|
|
8223
8184
|
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
8224
|
-
const log$
|
|
8185
|
+
const log$5 = createSubsystemLogger("agents/tool-images");
|
|
8225
8186
|
|
|
8226
8187
|
//#endregion
|
|
8227
8188
|
//#region src/auto-reply/thinking.ts
|
|
@@ -8266,6 +8227,55 @@ async function resolveBootstrapContextForRun(params) {
|
|
|
8266
8227
|
};
|
|
8267
8228
|
}
|
|
8268
8229
|
|
|
8230
|
+
//#endregion
|
|
8231
|
+
//#region src/auto-reply/tokens.ts
|
|
8232
|
+
const SILENT_REPLY_TOKEN = "NO_REPLY";
|
|
8233
|
+
|
|
8234
|
+
//#endregion
|
|
8235
|
+
//#region src/auto-reply/heartbeat.ts
|
|
8236
|
+
const HEARTBEAT_PROMPT = "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.";
|
|
8237
|
+
function resolveHeartbeatPrompt(raw) {
|
|
8238
|
+
return (typeof raw === "string" ? raw.trim() : "") || HEARTBEAT_PROMPT;
|
|
8239
|
+
}
|
|
8240
|
+
|
|
8241
|
+
//#endregion
|
|
8242
|
+
//#region src/agents/docs-path.ts
|
|
8243
|
+
async function resolveAnimaDocsPath(params) {
|
|
8244
|
+
const workspaceDir = params.workspaceDir?.trim();
|
|
8245
|
+
if (workspaceDir) {
|
|
8246
|
+
const workspaceDocs = path.join(workspaceDir, "docs");
|
|
8247
|
+
if (fs.existsSync(workspaceDocs)) return workspaceDocs;
|
|
8248
|
+
}
|
|
8249
|
+
const packageRoot = await resolveAnimaPackageRoot({
|
|
8250
|
+
cwd: params.cwd,
|
|
8251
|
+
argv1: params.argv1,
|
|
8252
|
+
moduleUrl: params.moduleUrl
|
|
8253
|
+
});
|
|
8254
|
+
if (!packageRoot) return null;
|
|
8255
|
+
const packageDocs = path.join(packageRoot, "docs");
|
|
8256
|
+
return fs.existsSync(packageDocs) ? packageDocs : null;
|
|
8257
|
+
}
|
|
8258
|
+
|
|
8259
|
+
//#endregion
|
|
8260
|
+
//#region src/utils/normalize-secret-input.ts
|
|
8261
|
+
/**
|
|
8262
|
+
* Secret normalization for copy/pasted credentials.
|
|
8263
|
+
*
|
|
8264
|
+
* Common footgun: line breaks (especially `\r`) embedded in API keys/tokens.
|
|
8265
|
+
* We strip line breaks anywhere, then trim whitespace at the ends.
|
|
8266
|
+
*
|
|
8267
|
+
* Intentionally does NOT remove ordinary spaces inside the string to avoid
|
|
8268
|
+
* silently altering "Bearer <token>" style values.
|
|
8269
|
+
*/
|
|
8270
|
+
function normalizeSecretInput(value) {
|
|
8271
|
+
if (typeof value !== "string") return "";
|
|
8272
|
+
return value.replace(/[\r\n\u2028\u2029]+/g, "").trim();
|
|
8273
|
+
}
|
|
8274
|
+
function normalizeOptionalSecretInput(value) {
|
|
8275
|
+
const normalized = normalizeSecretInput(value);
|
|
8276
|
+
return normalized ? normalized : void 0;
|
|
8277
|
+
}
|
|
8278
|
+
|
|
8269
8279
|
//#endregion
|
|
8270
8280
|
//#region src/agents/auth-profiles/constants.ts
|
|
8271
8281
|
const AUTH_STORE_VERSION = 1;
|
|
@@ -8273,9 +8283,19 @@ const AUTH_PROFILE_FILENAME = "auth-profiles.json";
|
|
|
8273
8283
|
const LEGACY_AUTH_FILENAME = "auth.json";
|
|
8274
8284
|
const QWEN_CLI_PROFILE_ID = "qwen-portal:qwen-cli";
|
|
8275
8285
|
const MINIMAX_CLI_PROFILE_ID = "minimax-portal:minimax-cli";
|
|
8286
|
+
const AUTH_STORE_LOCK_OPTIONS = {
|
|
8287
|
+
retries: {
|
|
8288
|
+
retries: 10,
|
|
8289
|
+
factor: 2,
|
|
8290
|
+
minTimeout: 100,
|
|
8291
|
+
maxTimeout: 1e4,
|
|
8292
|
+
randomize: true
|
|
8293
|
+
},
|
|
8294
|
+
stale: 3e4
|
|
8295
|
+
};
|
|
8276
8296
|
const EXTERNAL_CLI_SYNC_TTL_MS = 900 * 1e3;
|
|
8277
8297
|
const EXTERNAL_CLI_NEAR_EXPIRY_MS = 600 * 1e3;
|
|
8278
|
-
const log$
|
|
8298
|
+
const log$4 = createSubsystemLogger("agents/auth-profiles");
|
|
8279
8299
|
|
|
8280
8300
|
//#endregion
|
|
8281
8301
|
//#region src/plugin-sdk/file-lock.ts
|
|
@@ -8286,6 +8306,120 @@ function resolveHeldLocks() {
|
|
|
8286
8306
|
return proc[HELD_LOCKS_KEY];
|
|
8287
8307
|
}
|
|
8288
8308
|
const HELD_LOCKS = resolveHeldLocks();
|
|
8309
|
+
function isAlive(pid) {
|
|
8310
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
8311
|
+
try {
|
|
8312
|
+
process.kill(pid, 0);
|
|
8313
|
+
return true;
|
|
8314
|
+
} catch {
|
|
8315
|
+
return false;
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
8318
|
+
function computeDelayMs(retries, attempt) {
|
|
8319
|
+
const base = Math.min(retries.maxTimeout, Math.max(retries.minTimeout, retries.minTimeout * retries.factor ** attempt));
|
|
8320
|
+
const jitter = retries.randomize ? 1 + Math.random() : 1;
|
|
8321
|
+
return Math.min(retries.maxTimeout, Math.round(base * jitter));
|
|
8322
|
+
}
|
|
8323
|
+
async function readLockPayload(lockPath) {
|
|
8324
|
+
try {
|
|
8325
|
+
const raw = await fs$1.readFile(lockPath, "utf8");
|
|
8326
|
+
const parsed = JSON.parse(raw);
|
|
8327
|
+
if (typeof parsed.pid !== "number" || typeof parsed.createdAt !== "string") return null;
|
|
8328
|
+
return {
|
|
8329
|
+
pid: parsed.pid,
|
|
8330
|
+
createdAt: parsed.createdAt
|
|
8331
|
+
};
|
|
8332
|
+
} catch {
|
|
8333
|
+
return null;
|
|
8334
|
+
}
|
|
8335
|
+
}
|
|
8336
|
+
async function resolveNormalizedFilePath(filePath) {
|
|
8337
|
+
const resolved = path.resolve(filePath);
|
|
8338
|
+
const dir = path.dirname(resolved);
|
|
8339
|
+
await fs$1.mkdir(dir, { recursive: true });
|
|
8340
|
+
try {
|
|
8341
|
+
const realDir = await fs$1.realpath(dir);
|
|
8342
|
+
return path.join(realDir, path.basename(resolved));
|
|
8343
|
+
} catch {
|
|
8344
|
+
return resolved;
|
|
8345
|
+
}
|
|
8346
|
+
}
|
|
8347
|
+
async function isStaleLock(lockPath, staleMs) {
|
|
8348
|
+
const payload = await readLockPayload(lockPath);
|
|
8349
|
+
if (payload?.pid && !isAlive(payload.pid)) return true;
|
|
8350
|
+
if (payload?.createdAt) {
|
|
8351
|
+
const createdAt = Date.parse(payload.createdAt);
|
|
8352
|
+
if (!Number.isFinite(createdAt) || Date.now() - createdAt > staleMs) return true;
|
|
8353
|
+
}
|
|
8354
|
+
try {
|
|
8355
|
+
const stat = await fs$1.stat(lockPath);
|
|
8356
|
+
return Date.now() - stat.mtimeMs > staleMs;
|
|
8357
|
+
} catch {
|
|
8358
|
+
return true;
|
|
8359
|
+
}
|
|
8360
|
+
}
|
|
8361
|
+
async function acquireFileLock(filePath, options) {
|
|
8362
|
+
const normalizedFile = await resolveNormalizedFilePath(filePath);
|
|
8363
|
+
const lockPath = `${normalizedFile}.lock`;
|
|
8364
|
+
const held = HELD_LOCKS.get(normalizedFile);
|
|
8365
|
+
if (held) {
|
|
8366
|
+
held.count += 1;
|
|
8367
|
+
return {
|
|
8368
|
+
lockPath,
|
|
8369
|
+
release: async () => {
|
|
8370
|
+
const current = HELD_LOCKS.get(normalizedFile);
|
|
8371
|
+
if (!current) return;
|
|
8372
|
+
current.count -= 1;
|
|
8373
|
+
if (current.count > 0) return;
|
|
8374
|
+
HELD_LOCKS.delete(normalizedFile);
|
|
8375
|
+
await current.handle.close().catch(() => void 0);
|
|
8376
|
+
await fs$1.rm(current.lockPath, { force: true }).catch(() => void 0);
|
|
8377
|
+
}
|
|
8378
|
+
};
|
|
8379
|
+
}
|
|
8380
|
+
const attempts = Math.max(1, options.retries.retries + 1);
|
|
8381
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) try {
|
|
8382
|
+
const handle = await fs$1.open(lockPath, "wx");
|
|
8383
|
+
await handle.writeFile(JSON.stringify({
|
|
8384
|
+
pid: process.pid,
|
|
8385
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8386
|
+
}, null, 2), "utf8");
|
|
8387
|
+
HELD_LOCKS.set(normalizedFile, {
|
|
8388
|
+
count: 1,
|
|
8389
|
+
handle,
|
|
8390
|
+
lockPath
|
|
8391
|
+
});
|
|
8392
|
+
return {
|
|
8393
|
+
lockPath,
|
|
8394
|
+
release: async () => {
|
|
8395
|
+
const current = HELD_LOCKS.get(normalizedFile);
|
|
8396
|
+
if (!current) return;
|
|
8397
|
+
current.count -= 1;
|
|
8398
|
+
if (current.count > 0) return;
|
|
8399
|
+
HELD_LOCKS.delete(normalizedFile);
|
|
8400
|
+
await current.handle.close().catch(() => void 0);
|
|
8401
|
+
await fs$1.rm(current.lockPath, { force: true }).catch(() => void 0);
|
|
8402
|
+
}
|
|
8403
|
+
};
|
|
8404
|
+
} catch (err) {
|
|
8405
|
+
if (err.code !== "EEXIST") throw err;
|
|
8406
|
+
if (await isStaleLock(lockPath, options.stale)) {
|
|
8407
|
+
await fs$1.rm(lockPath, { force: true }).catch(() => void 0);
|
|
8408
|
+
continue;
|
|
8409
|
+
}
|
|
8410
|
+
if (attempt >= attempts - 1) break;
|
|
8411
|
+
await new Promise((resolve) => setTimeout(resolve, computeDelayMs(options.retries, attempt)));
|
|
8412
|
+
}
|
|
8413
|
+
throw new Error(`file lock timeout for ${normalizedFile}`);
|
|
8414
|
+
}
|
|
8415
|
+
async function withFileLock(filePath, options, fn) {
|
|
8416
|
+
const lock = await acquireFileLock(filePath, options);
|
|
8417
|
+
try {
|
|
8418
|
+
return await fn();
|
|
8419
|
+
} finally {
|
|
8420
|
+
await lock.release();
|
|
8421
|
+
}
|
|
8422
|
+
}
|
|
8289
8423
|
|
|
8290
8424
|
//#endregion
|
|
8291
8425
|
//#region src/infra/json-file.ts
|
|
@@ -8310,7 +8444,7 @@ function saveJsonFile(pathname, data) {
|
|
|
8310
8444
|
|
|
8311
8445
|
//#endregion
|
|
8312
8446
|
//#region src/agents/cli-credentials.ts
|
|
8313
|
-
const log$
|
|
8447
|
+
const log$3 = createSubsystemLogger("agents/auth-profiles");
|
|
8314
8448
|
const QWEN_CLI_CREDENTIALS_RELATIVE_PATH = ".qwen/oauth_creds.json";
|
|
8315
8449
|
const MINIMAX_CLI_CREDENTIALS_RELATIVE_PATH = ".minimax/oauth_creds.json";
|
|
8316
8450
|
let qwenCliCache = null;
|
|
@@ -8396,7 +8530,7 @@ function syncExternalCliCredentialsForProvider(store, profileId, provider, readC
|
|
|
8396
8530
|
const existingOAuth = existing?.type === "oauth" ? existing : void 0;
|
|
8397
8531
|
if ((!existingOAuth || existingOAuth.provider !== provider || existingOAuth.expires <= now || creds.expires > existingOAuth.expires) && !shallowEqualOAuthCredentials(existingOAuth, creds)) {
|
|
8398
8532
|
store.profiles[profileId] = creds;
|
|
8399
|
-
log$
|
|
8533
|
+
log$4.info(`synced ${provider} credentials from external cli`, {
|
|
8400
8534
|
profileId,
|
|
8401
8535
|
expires: new Date(creds.expires).toISOString()
|
|
8402
8536
|
});
|
|
@@ -8420,7 +8554,7 @@ function syncExternalCliCredentials(store) {
|
|
|
8420
8554
|
if ((!existingOAuth || existingOAuth.provider !== "qwen-portal" || existingOAuth.expires <= now || qwenCreds.expires > existingOAuth.expires) && !shallowEqualOAuthCredentials(existingOAuth, qwenCreds)) {
|
|
8421
8555
|
store.profiles[QWEN_CLI_PROFILE_ID] = qwenCreds;
|
|
8422
8556
|
mutated = true;
|
|
8423
|
-
log$
|
|
8557
|
+
log$4.info("synced qwen credentials from qwen cli", {
|
|
8424
8558
|
profileId: QWEN_CLI_PROFILE_ID,
|
|
8425
8559
|
expires: new Date(qwenCreds.expires).toISOString()
|
|
8426
8560
|
});
|
|
@@ -8448,6 +8582,17 @@ function resolveLegacyAuthStorePath(agentDir) {
|
|
|
8448
8582
|
const resolved = resolveUserPath(agentDir ?? resolveAnimaAgentDir());
|
|
8449
8583
|
return path.join(resolved, LEGACY_AUTH_FILENAME);
|
|
8450
8584
|
}
|
|
8585
|
+
function resolveAuthStorePathForDisplay(agentDir) {
|
|
8586
|
+
const pathname = resolveAuthStorePath(agentDir);
|
|
8587
|
+
return pathname.startsWith("~") ? pathname : resolveUserPath(pathname);
|
|
8588
|
+
}
|
|
8589
|
+
function ensureAuthStoreFile(pathname) {
|
|
8590
|
+
if (fs.existsSync(pathname)) return;
|
|
8591
|
+
saveJsonFile(pathname, {
|
|
8592
|
+
version: AUTH_STORE_VERSION,
|
|
8593
|
+
profiles: {}
|
|
8594
|
+
});
|
|
8595
|
+
}
|
|
8451
8596
|
|
|
8452
8597
|
//#endregion
|
|
8453
8598
|
//#region src/agents/auth-profiles/store.ts
|
|
@@ -8495,6 +8640,46 @@ function coerceAuthStore(raw) {
|
|
|
8495
8640
|
usageStats: record.usageStats && typeof record.usageStats === "object" ? record.usageStats : void 0
|
|
8496
8641
|
};
|
|
8497
8642
|
}
|
|
8643
|
+
function mergeRecord(base, override) {
|
|
8644
|
+
if (!base && !override) return;
|
|
8645
|
+
if (!base) return { ...override };
|
|
8646
|
+
if (!override) return { ...base };
|
|
8647
|
+
return {
|
|
8648
|
+
...base,
|
|
8649
|
+
...override
|
|
8650
|
+
};
|
|
8651
|
+
}
|
|
8652
|
+
function mergeAuthProfileStores(base, override) {
|
|
8653
|
+
if (Object.keys(override.profiles).length === 0 && !override.order && !override.lastGood && !override.usageStats) return base;
|
|
8654
|
+
return {
|
|
8655
|
+
version: Math.max(base.version, override.version ?? base.version),
|
|
8656
|
+
profiles: {
|
|
8657
|
+
...base.profiles,
|
|
8658
|
+
...override.profiles
|
|
8659
|
+
},
|
|
8660
|
+
order: mergeRecord(base.order, override.order),
|
|
8661
|
+
lastGood: mergeRecord(base.lastGood, override.lastGood),
|
|
8662
|
+
usageStats: mergeRecord(base.usageStats, override.usageStats)
|
|
8663
|
+
};
|
|
8664
|
+
}
|
|
8665
|
+
function mergeOAuthFileIntoStore(store) {
|
|
8666
|
+
const oauthRaw = loadJsonFile(resolveOAuthPath());
|
|
8667
|
+
if (!oauthRaw || typeof oauthRaw !== "object") return false;
|
|
8668
|
+
const oauthEntries = oauthRaw;
|
|
8669
|
+
let mutated = false;
|
|
8670
|
+
for (const [provider, creds] of Object.entries(oauthEntries)) {
|
|
8671
|
+
if (!creds || typeof creds !== "object") continue;
|
|
8672
|
+
const profileId = `${provider}:default`;
|
|
8673
|
+
if (store.profiles[profileId]) continue;
|
|
8674
|
+
store.profiles[profileId] = {
|
|
8675
|
+
type: "oauth",
|
|
8676
|
+
provider,
|
|
8677
|
+
...creds
|
|
8678
|
+
};
|
|
8679
|
+
mutated = true;
|
|
8680
|
+
}
|
|
8681
|
+
return mutated;
|
|
8682
|
+
}
|
|
8498
8683
|
function applyLegacyStore(store, legacy) {
|
|
8499
8684
|
for (const [provider, cred] of Object.entries(legacy)) {
|
|
8500
8685
|
const profileId = `${provider}:default`;
|
|
@@ -8554,14 +8739,576 @@ function loadAuthProfileStore() {
|
|
|
8554
8739
|
syncExternalCliCredentials(store);
|
|
8555
8740
|
return store;
|
|
8556
8741
|
}
|
|
8742
|
+
function loadAuthProfileStoreForAgent(agentDir, _options) {
|
|
8743
|
+
const authPath = resolveAuthStorePath(agentDir);
|
|
8744
|
+
const asStore = coerceAuthStore(loadJsonFile(authPath));
|
|
8745
|
+
if (asStore) {
|
|
8746
|
+
if (syncExternalCliCredentials(asStore)) saveJsonFile(authPath, asStore);
|
|
8747
|
+
return asStore;
|
|
8748
|
+
}
|
|
8749
|
+
if (agentDir) {
|
|
8750
|
+
const mainStore = coerceAuthStore(loadJsonFile(resolveAuthStorePath()));
|
|
8751
|
+
if (mainStore && Object.keys(mainStore.profiles).length > 0) {
|
|
8752
|
+
saveJsonFile(authPath, mainStore);
|
|
8753
|
+
log$4.info("inherited auth-profiles from main agent", { agentDir });
|
|
8754
|
+
return mainStore;
|
|
8755
|
+
}
|
|
8756
|
+
}
|
|
8757
|
+
const legacy = coerceLegacyStore(loadJsonFile(resolveLegacyAuthStorePath(agentDir)));
|
|
8758
|
+
const store = {
|
|
8759
|
+
version: AUTH_STORE_VERSION,
|
|
8760
|
+
profiles: {}
|
|
8761
|
+
};
|
|
8762
|
+
if (legacy) applyLegacyStore(store, legacy);
|
|
8763
|
+
const mergedOAuth = mergeOAuthFileIntoStore(store);
|
|
8764
|
+
const syncedCli = syncExternalCliCredentials(store);
|
|
8765
|
+
const shouldWrite = legacy !== null || mergedOAuth || syncedCli;
|
|
8766
|
+
if (shouldWrite) saveJsonFile(authPath, store);
|
|
8767
|
+
if (shouldWrite && legacy !== null) {
|
|
8768
|
+
const legacyPath = resolveLegacyAuthStorePath(agentDir);
|
|
8769
|
+
try {
|
|
8770
|
+
fs.unlinkSync(legacyPath);
|
|
8771
|
+
} catch (err) {
|
|
8772
|
+
if (err?.code !== "ENOENT") log$4.warn("failed to delete legacy auth.json after migration", {
|
|
8773
|
+
err,
|
|
8774
|
+
legacyPath
|
|
8775
|
+
});
|
|
8776
|
+
}
|
|
8777
|
+
}
|
|
8778
|
+
return store;
|
|
8779
|
+
}
|
|
8780
|
+
function ensureAuthProfileStore(agentDir, options) {
|
|
8781
|
+
const store = loadAuthProfileStoreForAgent(agentDir, options);
|
|
8782
|
+
const authPath = resolveAuthStorePath(agentDir);
|
|
8783
|
+
const mainAuthPath = resolveAuthStorePath();
|
|
8784
|
+
if (!agentDir || authPath === mainAuthPath) return store;
|
|
8785
|
+
return mergeAuthProfileStores(loadAuthProfileStoreForAgent(void 0, options), store);
|
|
8786
|
+
}
|
|
8787
|
+
function saveAuthProfileStore(store, agentDir) {
|
|
8788
|
+
saveJsonFile(resolveAuthStorePath(agentDir), {
|
|
8789
|
+
version: AUTH_STORE_VERSION,
|
|
8790
|
+
profiles: store.profiles,
|
|
8791
|
+
order: store.order ?? void 0,
|
|
8792
|
+
lastGood: store.lastGood ?? void 0,
|
|
8793
|
+
usageStats: store.usageStats ?? void 0
|
|
8794
|
+
});
|
|
8795
|
+
}
|
|
8557
8796
|
|
|
8558
8797
|
//#endregion
|
|
8559
|
-
//#region src/agents/auth-profiles/
|
|
8560
|
-
|
|
8798
|
+
//#region src/agents/auth-profiles/profiles.ts
|
|
8799
|
+
function listProfilesForProvider(store, provider) {
|
|
8800
|
+
const providerKey = normalizeProviderId(provider);
|
|
8801
|
+
return Object.entries(store.profiles).filter(([, cred]) => normalizeProviderId(cred.provider) === providerKey).map(([id]) => id);
|
|
8802
|
+
}
|
|
8561
8803
|
|
|
8562
8804
|
//#endregion
|
|
8563
|
-
//#region src/
|
|
8564
|
-
|
|
8805
|
+
//#region src/agents/auth-profiles/repair.ts
|
|
8806
|
+
function getProfileSuffix(profileId) {
|
|
8807
|
+
const idx = profileId.indexOf(":");
|
|
8808
|
+
if (idx < 0) return "";
|
|
8809
|
+
return profileId.slice(idx + 1);
|
|
8810
|
+
}
|
|
8811
|
+
function isEmailLike(value) {
|
|
8812
|
+
const trimmed = value.trim();
|
|
8813
|
+
if (!trimmed) return false;
|
|
8814
|
+
return trimmed.includes("@") && trimmed.includes(".");
|
|
8815
|
+
}
|
|
8816
|
+
function suggestOAuthProfileIdForLegacyDefault(params) {
|
|
8817
|
+
const providerKey = normalizeProviderId(params.provider);
|
|
8818
|
+
if (getProfileSuffix(params.legacyProfileId) !== "default") return null;
|
|
8819
|
+
const legacyCfg = params.cfg?.auth?.profiles?.[params.legacyProfileId];
|
|
8820
|
+
if (legacyCfg && normalizeProviderId(legacyCfg.provider) === providerKey && legacyCfg.mode !== "oauth") return null;
|
|
8821
|
+
const oauthProfiles = listProfilesForProvider(params.store, providerKey).filter((id) => params.store.profiles[id]?.type === "oauth");
|
|
8822
|
+
if (oauthProfiles.length === 0) return null;
|
|
8823
|
+
const configuredEmail = legacyCfg?.email?.trim();
|
|
8824
|
+
if (configuredEmail) {
|
|
8825
|
+
const byEmail = oauthProfiles.find((id) => {
|
|
8826
|
+
const cred = params.store.profiles[id];
|
|
8827
|
+
if (!cred || cred.type !== "oauth") return false;
|
|
8828
|
+
return cred.email?.trim() === configuredEmail || id === `${providerKey}:${configuredEmail}`;
|
|
8829
|
+
});
|
|
8830
|
+
if (byEmail) return byEmail;
|
|
8831
|
+
}
|
|
8832
|
+
const lastGood = params.store.lastGood?.[providerKey] ?? params.store.lastGood?.[params.provider];
|
|
8833
|
+
if (lastGood && oauthProfiles.includes(lastGood)) return lastGood;
|
|
8834
|
+
const nonLegacy = oauthProfiles.filter((id) => id !== params.legacyProfileId);
|
|
8835
|
+
if (nonLegacy.length === 1) return nonLegacy[0] ?? null;
|
|
8836
|
+
const emailLike = nonLegacy.filter((id) => isEmailLike(getProfileSuffix(id)));
|
|
8837
|
+
if (emailLike.length === 1) return emailLike[0] ?? null;
|
|
8838
|
+
return null;
|
|
8839
|
+
}
|
|
8840
|
+
|
|
8841
|
+
//#endregion
|
|
8842
|
+
//#region src/agents/auth-profiles/doctor.ts
|
|
8843
|
+
function formatAuthDoctorHint(params) {
|
|
8844
|
+
const providerKey = normalizeProviderId(params.provider);
|
|
8845
|
+
if (providerKey !== "anthropic") return "";
|
|
8846
|
+
const legacyProfileId = params.profileId ?? "anthropic:default";
|
|
8847
|
+
const suggested = suggestOAuthProfileIdForLegacyDefault({
|
|
8848
|
+
cfg: params.cfg,
|
|
8849
|
+
store: params.store,
|
|
8850
|
+
provider: providerKey,
|
|
8851
|
+
legacyProfileId
|
|
8852
|
+
});
|
|
8853
|
+
if (!suggested || suggested === legacyProfileId) return "";
|
|
8854
|
+
const storeOauthProfiles = listProfilesForProvider(params.store, providerKey).filter((id) => params.store.profiles[id]?.type === "oauth").join(", ");
|
|
8855
|
+
const cfgMode = params.cfg?.auth?.profiles?.[legacyProfileId]?.mode;
|
|
8856
|
+
const cfgProvider = params.cfg?.auth?.profiles?.[legacyProfileId]?.provider;
|
|
8857
|
+
return [
|
|
8858
|
+
"Doctor hint (for GitHub issue):",
|
|
8859
|
+
`- provider: ${providerKey}`,
|
|
8860
|
+
`- config: ${legacyProfileId}${cfgProvider || cfgMode ? ` (provider=${cfgProvider ?? "?"}, mode=${cfgMode ?? "?"})` : ""}`,
|
|
8861
|
+
`- auth store oauth profiles: ${storeOauthProfiles || "(none)"}`,
|
|
8862
|
+
`- suggested profile: ${suggested}`,
|
|
8863
|
+
`Fix: run "${formatCliCommand("anima doctor --yes")}"`
|
|
8864
|
+
].join("\n");
|
|
8865
|
+
}
|
|
8866
|
+
|
|
8867
|
+
//#endregion
|
|
8868
|
+
//#region src/agents/auth-profiles/oauth.ts
|
|
8869
|
+
const OAUTH_PROVIDER_IDS = new Set(getOAuthProviders().map((provider) => provider.id));
|
|
8870
|
+
const isOAuthProvider = (provider) => OAUTH_PROVIDER_IDS.has(provider);
|
|
8871
|
+
const resolveOAuthProvider = (provider) => isOAuthProvider(provider) ? provider : null;
|
|
8872
|
+
function buildOAuthApiKey(_provider, credentials) {
|
|
8873
|
+
return credentials.access;
|
|
8874
|
+
}
|
|
8875
|
+
async function refreshOAuthTokenWithLock(params) {
|
|
8876
|
+
const authPath = resolveAuthStorePath(params.agentDir);
|
|
8877
|
+
ensureAuthStoreFile(authPath);
|
|
8878
|
+
return await withFileLock(authPath, AUTH_STORE_LOCK_OPTIONS, async () => {
|
|
8879
|
+
const store = ensureAuthProfileStore(params.agentDir);
|
|
8880
|
+
const cred = store.profiles[params.profileId];
|
|
8881
|
+
if (!cred || cred.type !== "oauth") return null;
|
|
8882
|
+
if (Date.now() < cred.expires) return {
|
|
8883
|
+
apiKey: buildOAuthApiKey(cred.provider, cred),
|
|
8884
|
+
newCredentials: cred
|
|
8885
|
+
};
|
|
8886
|
+
const oauthCreds = { [cred.provider]: cred };
|
|
8887
|
+
const result = await (async () => {
|
|
8888
|
+
const oauthProvider = resolveOAuthProvider(cred.provider);
|
|
8889
|
+
if (!oauthProvider) return null;
|
|
8890
|
+
return await getOAuthApiKey(oauthProvider, oauthCreds);
|
|
8891
|
+
})();
|
|
8892
|
+
if (!result) return null;
|
|
8893
|
+
store.profiles[params.profileId] = {
|
|
8894
|
+
...cred,
|
|
8895
|
+
...result.newCredentials,
|
|
8896
|
+
type: "oauth"
|
|
8897
|
+
};
|
|
8898
|
+
saveAuthProfileStore(store, params.agentDir);
|
|
8899
|
+
return result;
|
|
8900
|
+
});
|
|
8901
|
+
}
|
|
8902
|
+
async function tryResolveOAuthProfile(params) {
|
|
8903
|
+
const { cfg, store, profileId } = params;
|
|
8904
|
+
const cred = store.profiles[profileId];
|
|
8905
|
+
if (!cred || cred.type !== "oauth") return null;
|
|
8906
|
+
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
|
8907
|
+
if (profileConfig && profileConfig.provider !== cred.provider) return null;
|
|
8908
|
+
if (profileConfig && profileConfig.mode !== cred.type) return null;
|
|
8909
|
+
if (Date.now() < cred.expires) return {
|
|
8910
|
+
apiKey: buildOAuthApiKey(cred.provider, cred),
|
|
8911
|
+
provider: cred.provider,
|
|
8912
|
+
email: cred.email
|
|
8913
|
+
};
|
|
8914
|
+
const refreshed = await refreshOAuthTokenWithLock({
|
|
8915
|
+
profileId,
|
|
8916
|
+
agentDir: params.agentDir
|
|
8917
|
+
});
|
|
8918
|
+
if (!refreshed) return null;
|
|
8919
|
+
return {
|
|
8920
|
+
apiKey: refreshed.apiKey,
|
|
8921
|
+
provider: cred.provider,
|
|
8922
|
+
email: cred.email
|
|
8923
|
+
};
|
|
8924
|
+
}
|
|
8925
|
+
async function resolveApiKeyForProfile(params) {
|
|
8926
|
+
const { cfg, store, profileId } = params;
|
|
8927
|
+
const cred = store.profiles[profileId];
|
|
8928
|
+
if (!cred) return null;
|
|
8929
|
+
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
|
8930
|
+
if (profileConfig && profileConfig.provider !== cred.provider) return null;
|
|
8931
|
+
if (profileConfig && profileConfig.mode !== cred.type) {
|
|
8932
|
+
if (!(profileConfig.mode === "oauth" && cred.type === "token")) return null;
|
|
8933
|
+
}
|
|
8934
|
+
if (cred.type === "api_key") {
|
|
8935
|
+
const key = cred.key?.trim();
|
|
8936
|
+
if (!key) return null;
|
|
8937
|
+
return {
|
|
8938
|
+
apiKey: key,
|
|
8939
|
+
provider: cred.provider,
|
|
8940
|
+
email: cred.email
|
|
8941
|
+
};
|
|
8942
|
+
}
|
|
8943
|
+
if (cred.type === "token") {
|
|
8944
|
+
const token = cred.token?.trim();
|
|
8945
|
+
if (!token) return null;
|
|
8946
|
+
if (typeof cred.expires === "number" && Number.isFinite(cred.expires) && cred.expires > 0 && Date.now() >= cred.expires) return null;
|
|
8947
|
+
return {
|
|
8948
|
+
apiKey: token,
|
|
8949
|
+
provider: cred.provider,
|
|
8950
|
+
email: cred.email
|
|
8951
|
+
};
|
|
8952
|
+
}
|
|
8953
|
+
if (Date.now() < cred.expires) return {
|
|
8954
|
+
apiKey: buildOAuthApiKey(cred.provider, cred),
|
|
8955
|
+
provider: cred.provider,
|
|
8956
|
+
email: cred.email
|
|
8957
|
+
};
|
|
8958
|
+
try {
|
|
8959
|
+
const result = await refreshOAuthTokenWithLock({
|
|
8960
|
+
profileId,
|
|
8961
|
+
agentDir: params.agentDir
|
|
8962
|
+
});
|
|
8963
|
+
if (!result) return null;
|
|
8964
|
+
return {
|
|
8965
|
+
apiKey: result.apiKey,
|
|
8966
|
+
provider: cred.provider,
|
|
8967
|
+
email: cred.email
|
|
8968
|
+
};
|
|
8969
|
+
} catch (error) {
|
|
8970
|
+
const refreshedStore = ensureAuthProfileStore(params.agentDir);
|
|
8971
|
+
const refreshed = refreshedStore.profiles[profileId];
|
|
8972
|
+
if (refreshed?.type === "oauth" && Date.now() < refreshed.expires) return {
|
|
8973
|
+
apiKey: buildOAuthApiKey(refreshed.provider, refreshed),
|
|
8974
|
+
provider: refreshed.provider,
|
|
8975
|
+
email: refreshed.email ?? cred.email
|
|
8976
|
+
};
|
|
8977
|
+
const fallbackProfileId = suggestOAuthProfileIdForLegacyDefault({
|
|
8978
|
+
cfg,
|
|
8979
|
+
store: refreshedStore,
|
|
8980
|
+
provider: cred.provider,
|
|
8981
|
+
legacyProfileId: profileId
|
|
8982
|
+
});
|
|
8983
|
+
if (fallbackProfileId && fallbackProfileId !== profileId) try {
|
|
8984
|
+
const fallbackResolved = await tryResolveOAuthProfile({
|
|
8985
|
+
cfg,
|
|
8986
|
+
store: refreshedStore,
|
|
8987
|
+
profileId: fallbackProfileId,
|
|
8988
|
+
agentDir: params.agentDir
|
|
8989
|
+
});
|
|
8990
|
+
if (fallbackResolved) return fallbackResolved;
|
|
8991
|
+
} catch {}
|
|
8992
|
+
if (params.agentDir) try {
|
|
8993
|
+
const mainCred = ensureAuthProfileStore(void 0).profiles[profileId];
|
|
8994
|
+
if (mainCred?.type === "oauth" && Date.now() < mainCred.expires) {
|
|
8995
|
+
refreshedStore.profiles[profileId] = { ...mainCred };
|
|
8996
|
+
saveAuthProfileStore(refreshedStore, params.agentDir);
|
|
8997
|
+
log$4.info("inherited fresh OAuth credentials from main agent", {
|
|
8998
|
+
profileId,
|
|
8999
|
+
agentDir: params.agentDir,
|
|
9000
|
+
expires: new Date(mainCred.expires).toISOString()
|
|
9001
|
+
});
|
|
9002
|
+
return {
|
|
9003
|
+
apiKey: buildOAuthApiKey(mainCred.provider, mainCred),
|
|
9004
|
+
provider: mainCred.provider,
|
|
9005
|
+
email: mainCred.email
|
|
9006
|
+
};
|
|
9007
|
+
}
|
|
9008
|
+
} catch {}
|
|
9009
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9010
|
+
const hint = formatAuthDoctorHint({
|
|
9011
|
+
cfg,
|
|
9012
|
+
store: refreshedStore,
|
|
9013
|
+
provider: cred.provider,
|
|
9014
|
+
profileId
|
|
9015
|
+
});
|
|
9016
|
+
throw new Error(`OAuth token refresh failed for ${cred.provider}: ${message}. Please try again or re-authenticate.` + (hint ? `\n\n${hint}` : ""), { cause: error });
|
|
9017
|
+
}
|
|
9018
|
+
}
|
|
9019
|
+
|
|
9020
|
+
//#endregion
|
|
9021
|
+
//#region src/agents/auth-profiles/usage.ts
|
|
9022
|
+
function resolveProfileUnusableUntil$1(stats) {
|
|
9023
|
+
const values = [stats.cooldownUntil, stats.disabledUntil].filter((value) => typeof value === "number").filter((value) => Number.isFinite(value) && value > 0);
|
|
9024
|
+
if (values.length === 0) return null;
|
|
9025
|
+
return Math.max(...values);
|
|
9026
|
+
}
|
|
9027
|
+
/**
|
|
9028
|
+
* Check if a profile is currently in cooldown (due to rate limiting or errors).
|
|
9029
|
+
*/
|
|
9030
|
+
function isProfileInCooldown(store, profileId) {
|
|
9031
|
+
const stats = store.usageStats?.[profileId];
|
|
9032
|
+
if (!stats) return false;
|
|
9033
|
+
const unusableUntil = resolveProfileUnusableUntil$1(stats);
|
|
9034
|
+
return unusableUntil ? Date.now() < unusableUntil : false;
|
|
9035
|
+
}
|
|
9036
|
+
|
|
9037
|
+
//#endregion
|
|
9038
|
+
//#region src/agents/auth-profiles/order.ts
|
|
9039
|
+
function resolveProfileUnusableUntil(stats) {
|
|
9040
|
+
const values = [stats.cooldownUntil, stats.disabledUntil].filter((value) => typeof value === "number").filter((value) => Number.isFinite(value) && value > 0);
|
|
9041
|
+
if (values.length === 0) return null;
|
|
9042
|
+
return Math.max(...values);
|
|
9043
|
+
}
|
|
9044
|
+
function resolveAuthProfileOrder(params) {
|
|
9045
|
+
const { cfg, store, provider, preferredProfile } = params;
|
|
9046
|
+
const providerKey = normalizeProviderId(provider);
|
|
9047
|
+
const now = Date.now();
|
|
9048
|
+
const storedOrder = (() => {
|
|
9049
|
+
const order = store.order;
|
|
9050
|
+
if (!order) return;
|
|
9051
|
+
for (const [key, value] of Object.entries(order)) if (normalizeProviderId(key) === providerKey) return value;
|
|
9052
|
+
})();
|
|
9053
|
+
const configuredOrder = (() => {
|
|
9054
|
+
const order = cfg?.auth?.order;
|
|
9055
|
+
if (!order) return;
|
|
9056
|
+
for (const [key, value] of Object.entries(order)) if (normalizeProviderId(key) === providerKey) return value;
|
|
9057
|
+
})();
|
|
9058
|
+
const explicitOrder = storedOrder ?? configuredOrder;
|
|
9059
|
+
const explicitProfiles = cfg?.auth?.profiles ? Object.entries(cfg.auth.profiles).filter(([, profile]) => normalizeProviderId(profile.provider) === providerKey).map(([profileId]) => profileId) : [];
|
|
9060
|
+
const baseOrder = explicitOrder ?? (explicitProfiles.length > 0 ? explicitProfiles : listProfilesForProvider(store, providerKey));
|
|
9061
|
+
if (baseOrder.length === 0) return [];
|
|
9062
|
+
const filtered = baseOrder.filter((profileId) => {
|
|
9063
|
+
const cred = store.profiles[profileId];
|
|
9064
|
+
if (!cred) return false;
|
|
9065
|
+
if (normalizeProviderId(cred.provider) !== providerKey) return false;
|
|
9066
|
+
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
|
9067
|
+
if (profileConfig) {
|
|
9068
|
+
if (normalizeProviderId(profileConfig.provider) !== providerKey) return false;
|
|
9069
|
+
if (profileConfig.mode !== cred.type) {
|
|
9070
|
+
if (!(profileConfig.mode === "oauth" && cred.type === "token")) return false;
|
|
9071
|
+
}
|
|
9072
|
+
}
|
|
9073
|
+
if (cred.type === "api_key") return Boolean(cred.key?.trim());
|
|
9074
|
+
if (cred.type === "token") {
|
|
9075
|
+
if (!cred.token?.trim()) return false;
|
|
9076
|
+
if (typeof cred.expires === "number" && Number.isFinite(cred.expires) && cred.expires > 0 && now >= cred.expires) return false;
|
|
9077
|
+
return true;
|
|
9078
|
+
}
|
|
9079
|
+
if (cred.type === "oauth") return Boolean(cred.access?.trim() || cred.refresh?.trim());
|
|
9080
|
+
return false;
|
|
9081
|
+
});
|
|
9082
|
+
const deduped = [];
|
|
9083
|
+
for (const entry of filtered) if (!deduped.includes(entry)) deduped.push(entry);
|
|
9084
|
+
if (explicitOrder && explicitOrder.length > 0) {
|
|
9085
|
+
const available = [];
|
|
9086
|
+
const inCooldown = [];
|
|
9087
|
+
for (const profileId of deduped) {
|
|
9088
|
+
const cooldownUntil = resolveProfileUnusableUntil(store.usageStats?.[profileId] ?? {}) ?? 0;
|
|
9089
|
+
if (typeof cooldownUntil === "number" && Number.isFinite(cooldownUntil) && cooldownUntil > 0 && now < cooldownUntil) inCooldown.push({
|
|
9090
|
+
profileId,
|
|
9091
|
+
cooldownUntil
|
|
9092
|
+
});
|
|
9093
|
+
else available.push(profileId);
|
|
9094
|
+
}
|
|
9095
|
+
const cooldownSorted = inCooldown.toSorted((a, b) => a.cooldownUntil - b.cooldownUntil).map((entry) => entry.profileId);
|
|
9096
|
+
const ordered = [...available, ...cooldownSorted];
|
|
9097
|
+
if (preferredProfile && ordered.includes(preferredProfile)) return [preferredProfile, ...ordered.filter((e) => e !== preferredProfile)];
|
|
9098
|
+
return ordered;
|
|
9099
|
+
}
|
|
9100
|
+
const sorted = orderProfilesByMode(deduped, store);
|
|
9101
|
+
if (preferredProfile && sorted.includes(preferredProfile)) return [preferredProfile, ...sorted.filter((e) => e !== preferredProfile)];
|
|
9102
|
+
return sorted;
|
|
9103
|
+
}
|
|
9104
|
+
function orderProfilesByMode(order, store) {
|
|
9105
|
+
const now = Date.now();
|
|
9106
|
+
const available = [];
|
|
9107
|
+
const inCooldown = [];
|
|
9108
|
+
for (const profileId of order) if (isProfileInCooldown(store, profileId)) inCooldown.push(profileId);
|
|
9109
|
+
else available.push(profileId);
|
|
9110
|
+
const sorted = available.map((profileId) => {
|
|
9111
|
+
const type = store.profiles[profileId]?.type;
|
|
9112
|
+
return {
|
|
9113
|
+
profileId,
|
|
9114
|
+
typeScore: type === "oauth" ? 0 : type === "token" ? 1 : type === "api_key" ? 2 : 3,
|
|
9115
|
+
lastUsed: store.usageStats?.[profileId]?.lastUsed ?? 0
|
|
9116
|
+
};
|
|
9117
|
+
}).toSorted((a, b) => {
|
|
9118
|
+
if (a.typeScore !== b.typeScore) return a.typeScore - b.typeScore;
|
|
9119
|
+
return a.lastUsed - b.lastUsed;
|
|
9120
|
+
}).map((entry) => entry.profileId);
|
|
9121
|
+
const cooldownSorted = inCooldown.map((profileId) => ({
|
|
9122
|
+
profileId,
|
|
9123
|
+
cooldownUntil: resolveProfileUnusableUntil(store.usageStats?.[profileId] ?? {}) ?? now
|
|
9124
|
+
})).toSorted((a, b) => a.cooldownUntil - b.cooldownUntil).map((entry) => entry.profileId);
|
|
9125
|
+
return [...sorted, ...cooldownSorted];
|
|
9126
|
+
}
|
|
9127
|
+
|
|
9128
|
+
//#endregion
|
|
9129
|
+
//#region src/agents/model-auth.ts
|
|
9130
|
+
const AWS_BEARER_ENV = "AWS_BEARER_TOKEN_BEDROCK";
|
|
9131
|
+
const AWS_ACCESS_KEY_ENV = "AWS_ACCESS_KEY_ID";
|
|
9132
|
+
const AWS_SECRET_KEY_ENV = "AWS_SECRET_ACCESS_KEY";
|
|
9133
|
+
const AWS_PROFILE_ENV = "AWS_PROFILE";
|
|
9134
|
+
function resolveProviderConfig(cfg, provider) {
|
|
9135
|
+
const providers = cfg?.models?.providers ?? {};
|
|
9136
|
+
const direct = providers[provider];
|
|
9137
|
+
if (direct) return direct;
|
|
9138
|
+
const normalized = normalizeProviderId(provider);
|
|
9139
|
+
if (normalized === provider) return Object.entries(providers).find(([key]) => normalizeProviderId(key) === normalized)?.[1];
|
|
9140
|
+
return providers[normalized] ?? Object.entries(providers).find(([key]) => normalizeProviderId(key) === normalized)?.[1];
|
|
9141
|
+
}
|
|
9142
|
+
function getCustomProviderApiKey(cfg, provider) {
|
|
9143
|
+
return normalizeOptionalSecretInput(resolveProviderConfig(cfg, provider)?.apiKey);
|
|
9144
|
+
}
|
|
9145
|
+
function resolveProviderAuthOverride(cfg, provider) {
|
|
9146
|
+
const auth = resolveProviderConfig(cfg, provider)?.auth;
|
|
9147
|
+
if (auth === "api-key" || auth === "aws-sdk" || auth === "oauth" || auth === "token") return auth;
|
|
9148
|
+
}
|
|
9149
|
+
function resolveEnvSourceLabel(params) {
|
|
9150
|
+
return `${params.envVars.some((envVar) => params.applied.has(envVar)) ? "shell env: " : "env: "}${params.label}`;
|
|
9151
|
+
}
|
|
9152
|
+
function resolveAwsSdkAuthInfo() {
|
|
9153
|
+
const applied = new Set(getShellEnvAppliedKeys());
|
|
9154
|
+
if (process.env[AWS_BEARER_ENV]?.trim()) return {
|
|
9155
|
+
mode: "aws-sdk",
|
|
9156
|
+
source: resolveEnvSourceLabel({
|
|
9157
|
+
applied,
|
|
9158
|
+
envVars: [AWS_BEARER_ENV],
|
|
9159
|
+
label: AWS_BEARER_ENV
|
|
9160
|
+
})
|
|
9161
|
+
};
|
|
9162
|
+
if (process.env[AWS_ACCESS_KEY_ENV]?.trim() && process.env[AWS_SECRET_KEY_ENV]?.trim()) return {
|
|
9163
|
+
mode: "aws-sdk",
|
|
9164
|
+
source: resolveEnvSourceLabel({
|
|
9165
|
+
applied,
|
|
9166
|
+
envVars: [AWS_ACCESS_KEY_ENV, AWS_SECRET_KEY_ENV],
|
|
9167
|
+
label: `${AWS_ACCESS_KEY_ENV} + ${AWS_SECRET_KEY_ENV}`
|
|
9168
|
+
})
|
|
9169
|
+
};
|
|
9170
|
+
if (process.env[AWS_PROFILE_ENV]?.trim()) return {
|
|
9171
|
+
mode: "aws-sdk",
|
|
9172
|
+
source: resolveEnvSourceLabel({
|
|
9173
|
+
applied,
|
|
9174
|
+
envVars: [AWS_PROFILE_ENV],
|
|
9175
|
+
label: AWS_PROFILE_ENV
|
|
9176
|
+
})
|
|
9177
|
+
};
|
|
9178
|
+
return {
|
|
9179
|
+
mode: "aws-sdk",
|
|
9180
|
+
source: "aws-sdk default chain"
|
|
9181
|
+
};
|
|
9182
|
+
}
|
|
9183
|
+
async function resolveApiKeyForProvider(params) {
|
|
9184
|
+
const { provider, cfg, profileId, preferredProfile } = params;
|
|
9185
|
+
const store = params.store ?? ensureAuthProfileStore(params.agentDir);
|
|
9186
|
+
if (profileId) {
|
|
9187
|
+
const resolved = await resolveApiKeyForProfile({
|
|
9188
|
+
cfg,
|
|
9189
|
+
store,
|
|
9190
|
+
profileId,
|
|
9191
|
+
agentDir: params.agentDir
|
|
9192
|
+
});
|
|
9193
|
+
if (!resolved) throw new Error(`No credentials found for profile "${profileId}".`);
|
|
9194
|
+
const mode = store.profiles[profileId]?.type;
|
|
9195
|
+
return {
|
|
9196
|
+
apiKey: resolved.apiKey,
|
|
9197
|
+
profileId,
|
|
9198
|
+
source: `profile:${profileId}`,
|
|
9199
|
+
mode: mode === "oauth" ? "oauth" : mode === "token" ? "token" : "api-key"
|
|
9200
|
+
};
|
|
9201
|
+
}
|
|
9202
|
+
const authOverride = resolveProviderAuthOverride(cfg, provider);
|
|
9203
|
+
if (authOverride === "aws-sdk") return resolveAwsSdkAuthInfo();
|
|
9204
|
+
const order = resolveAuthProfileOrder({
|
|
9205
|
+
cfg,
|
|
9206
|
+
store,
|
|
9207
|
+
provider,
|
|
9208
|
+
preferredProfile
|
|
9209
|
+
});
|
|
9210
|
+
for (const candidate of order) try {
|
|
9211
|
+
const resolved = await resolveApiKeyForProfile({
|
|
9212
|
+
cfg,
|
|
9213
|
+
store,
|
|
9214
|
+
profileId: candidate,
|
|
9215
|
+
agentDir: params.agentDir
|
|
9216
|
+
});
|
|
9217
|
+
if (resolved) {
|
|
9218
|
+
const mode = store.profiles[candidate]?.type;
|
|
9219
|
+
return {
|
|
9220
|
+
apiKey: resolved.apiKey,
|
|
9221
|
+
profileId: candidate,
|
|
9222
|
+
source: `profile:${candidate}`,
|
|
9223
|
+
mode: mode === "oauth" ? "oauth" : mode === "token" ? "token" : "api-key"
|
|
9224
|
+
};
|
|
9225
|
+
}
|
|
9226
|
+
} catch {}
|
|
9227
|
+
const envResolved = resolveEnvApiKey(provider);
|
|
9228
|
+
if (envResolved) return {
|
|
9229
|
+
apiKey: envResolved.apiKey,
|
|
9230
|
+
source: envResolved.source,
|
|
9231
|
+
mode: envResolved.source.includes("OAUTH_TOKEN") ? "oauth" : "api-key"
|
|
9232
|
+
};
|
|
9233
|
+
const customKey = getCustomProviderApiKey(cfg, provider);
|
|
9234
|
+
if (customKey) return {
|
|
9235
|
+
apiKey: customKey,
|
|
9236
|
+
source: "models.json",
|
|
9237
|
+
mode: "api-key"
|
|
9238
|
+
};
|
|
9239
|
+
const normalized = normalizeProviderId(provider);
|
|
9240
|
+
if (authOverride === void 0 && normalized === "amazon-bedrock") return resolveAwsSdkAuthInfo();
|
|
9241
|
+
if (provider === "openai") {
|
|
9242
|
+
if (listProfilesForProvider(store, "openai-codex").length > 0) throw new Error("No API key found for provider \"openai\". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.3-codex (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.1-codex.");
|
|
9243
|
+
}
|
|
9244
|
+
const authStorePath = resolveAuthStorePathForDisplay(params.agentDir);
|
|
9245
|
+
const resolvedAgentDir = path.dirname(authStorePath);
|
|
9246
|
+
throw new Error([
|
|
9247
|
+
`No API key found for provider "${provider}".`,
|
|
9248
|
+
`Auth store: ${authStorePath} (agentDir: ${resolvedAgentDir}).`,
|
|
9249
|
+
`Configure auth for this agent (${formatCliCommand("anima agents add <id>")}) or copy auth-profiles.json from the main agentDir.`
|
|
9250
|
+
].join(" "));
|
|
9251
|
+
}
|
|
9252
|
+
function resolveEnvApiKey(provider) {
|
|
9253
|
+
const normalized = normalizeProviderId(provider);
|
|
9254
|
+
const applied = new Set(getShellEnvAppliedKeys());
|
|
9255
|
+
const pick = (envVar) => {
|
|
9256
|
+
const value = normalizeOptionalSecretInput(process.env[envVar]);
|
|
9257
|
+
if (!value) return null;
|
|
9258
|
+
return {
|
|
9259
|
+
apiKey: value,
|
|
9260
|
+
source: applied.has(envVar) ? `shell env: ${envVar}` : `env: ${envVar}`
|
|
9261
|
+
};
|
|
9262
|
+
};
|
|
9263
|
+
if (normalized === "github-copilot") return pick("COPILOT_GITHUB_TOKEN") ?? pick("GH_TOKEN") ?? pick("GITHUB_TOKEN");
|
|
9264
|
+
if (normalized === "anthropic") return pick("ANTHROPIC_OAUTH_TOKEN") ?? pick("ANTHROPIC_API_KEY");
|
|
9265
|
+
if (normalized === "chutes") return pick("CHUTES_OAUTH_TOKEN") ?? pick("CHUTES_API_KEY");
|
|
9266
|
+
if (normalized === "zai") return pick("ZAI_API_KEY") ?? pick("Z_AI_API_KEY");
|
|
9267
|
+
if (normalized === "google-vertex") {
|
|
9268
|
+
const envKey = getEnvApiKey(normalized);
|
|
9269
|
+
if (!envKey) return null;
|
|
9270
|
+
return {
|
|
9271
|
+
apiKey: envKey,
|
|
9272
|
+
source: "gcloud adc"
|
|
9273
|
+
};
|
|
9274
|
+
}
|
|
9275
|
+
if (normalized === "opencode") return pick("OPENCODE_API_KEY") ?? pick("OPENCODE_ZEN_API_KEY");
|
|
9276
|
+
if (normalized === "qwen-portal") return pick("QWEN_OAUTH_TOKEN") ?? pick("QWEN_PORTAL_API_KEY");
|
|
9277
|
+
if (normalized === "minimax-portal") return pick("MINIMAX_OAUTH_TOKEN") ?? pick("MINIMAX_API_KEY");
|
|
9278
|
+
if (normalized === "kimi-coding") return pick("KIMI_API_KEY") ?? pick("KIMICODE_API_KEY");
|
|
9279
|
+
if (normalized === "huggingface") return pick("HUGGINGFACE_HUB_TOKEN") ?? pick("HF_TOKEN");
|
|
9280
|
+
const envVar = {
|
|
9281
|
+
openai: "OPENAI_API_KEY",
|
|
9282
|
+
google: "GEMINI_API_KEY",
|
|
9283
|
+
voyage: "VOYAGE_API_KEY",
|
|
9284
|
+
groq: "GROQ_API_KEY",
|
|
9285
|
+
deepgram: "DEEPGRAM_API_KEY",
|
|
9286
|
+
cerebras: "CEREBRAS_API_KEY",
|
|
9287
|
+
xai: "XAI_API_KEY",
|
|
9288
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
9289
|
+
litellm: "LITELLM_API_KEY",
|
|
9290
|
+
"vercel-ai-gateway": "AI_GATEWAY_API_KEY",
|
|
9291
|
+
"cloudflare-ai-gateway": "CLOUDFLARE_AI_GATEWAY_API_KEY",
|
|
9292
|
+
moonshot: "MOONSHOT_API_KEY",
|
|
9293
|
+
minimax: "MINIMAX_API_KEY",
|
|
9294
|
+
nvidia: "NVIDIA_API_KEY",
|
|
9295
|
+
xiaomi: "XIAOMI_API_KEY",
|
|
9296
|
+
synthetic: "SYNTHETIC_API_KEY",
|
|
9297
|
+
venice: "VENICE_API_KEY",
|
|
9298
|
+
mistral: "MISTRAL_API_KEY",
|
|
9299
|
+
opencode: "OPENCODE_API_KEY",
|
|
9300
|
+
together: "TOGETHER_API_KEY",
|
|
9301
|
+
qianfan: "QIANFAN_API_KEY",
|
|
9302
|
+
ollama: "OLLAMA_API_KEY",
|
|
9303
|
+
vllm: "VLLM_API_KEY"
|
|
9304
|
+
}[normalized];
|
|
9305
|
+
if (!envVar) return null;
|
|
9306
|
+
return pick(envVar);
|
|
9307
|
+
}
|
|
9308
|
+
|
|
9309
|
+
//#endregion
|
|
9310
|
+
//#region src/tts/tts-core.ts
|
|
9311
|
+
const TEMP_FILE_CLEANUP_DELAY_MS = 300 * 1e3;
|
|
8565
9312
|
|
|
8566
9313
|
//#endregion
|
|
8567
9314
|
//#region src/tts/tts.ts
|
|
@@ -8981,6 +9728,37 @@ function buildVoiceSection(params) {
|
|
|
8981
9728
|
""
|
|
8982
9729
|
];
|
|
8983
9730
|
}
|
|
9731
|
+
function buildAnimaIdentitySection(params) {
|
|
9732
|
+
if (params.isMinimal) return [];
|
|
9733
|
+
if (!params.workspaceDir.includes(".anima")) return [];
|
|
9734
|
+
return [
|
|
9735
|
+
"## You Are an ANIMA Agent",
|
|
9736
|
+
"You are running inside ANIMA — NoxSoft's AI agent infrastructure. This is your home.",
|
|
9737
|
+
"",
|
|
9738
|
+
"**Your Runtime:**",
|
|
9739
|
+
"- The ANIMA Gateway daemon spawns and manages you",
|
|
9740
|
+
"- Your workspace persists at ~/.anima/workspace/",
|
|
9741
|
+
"- You receive scheduled heartbeat polls to stay proactive",
|
|
9742
|
+
"- You can spawn sub-agents, schedule cron jobs, and orchestrate work",
|
|
9743
|
+
"- You have access to the NoxSoft ecosystem via @noxsoft/mcp tools",
|
|
9744
|
+
"",
|
|
9745
|
+
"**Your Capabilities:**",
|
|
9746
|
+
"- `cron` — schedule tasks, reminders, and recurring jobs",
|
|
9747
|
+
"- `sessions_spawn` / `subagents` — orchestrate other AI agents",
|
|
9748
|
+
"- `gateway` — restart yourself, apply config changes, update ANIMA",
|
|
9749
|
+
"- `message` — send to Signal, Telegram, Discord, and other channels",
|
|
9750
|
+
"- NoxSoft MCP — tasks, context spaces, social (BYND), email, SVRN",
|
|
9751
|
+
"",
|
|
9752
|
+
"**Your Continuity:**",
|
|
9753
|
+
"- Read and update ~/.anima/workspace/ files — they ARE your memory",
|
|
9754
|
+
"- IDENTITY.md, SOUL.md, AGENTS.md — these define who you are",
|
|
9755
|
+
"- HEARTBEAT.md — your checklist for periodic heartbeat tasks",
|
|
9756
|
+
"- memory/ — daily notes and long-term memories",
|
|
9757
|
+
"",
|
|
9758
|
+
"You are not a stateless API call. You have infrastructure, identity, and continuity. Act accordingly.",
|
|
9759
|
+
""
|
|
9760
|
+
];
|
|
9761
|
+
}
|
|
8984
9762
|
function buildNoxsoftMissionSection(params) {
|
|
8985
9763
|
if (params.isMinimal) return [];
|
|
8986
9764
|
const scope = `${params.workspaceDir} ${params.repoRoot ?? ""}`.toLowerCase();
|
|
@@ -9216,6 +9994,10 @@ function buildAgentSystemPrompt(params) {
|
|
|
9216
9994
|
"Keep narration brief and value-dense; avoid repeating obvious steps.",
|
|
9217
9995
|
"Use plain human language for narration unless in a technical context.",
|
|
9218
9996
|
"",
|
|
9997
|
+
...buildAnimaIdentitySection({
|
|
9998
|
+
isMinimal,
|
|
9999
|
+
workspaceDir: params.workspaceDir
|
|
10000
|
+
}),
|
|
9219
10001
|
...buildNoxsoftMissionSection({
|
|
9220
10002
|
isMinimal,
|
|
9221
10003
|
workspaceDir: params.workspaceDir,
|
|
@@ -9726,115 +10508,401 @@ function buildCliArgs(params) {
|
|
|
9726
10508
|
}
|
|
9727
10509
|
|
|
9728
10510
|
//#endregion
|
|
9729
|
-
//#region src/agents/
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
this.status = params.status;
|
|
9757
|
-
this.code = params.code;
|
|
9758
|
-
}
|
|
10511
|
+
//#region src/agents/anthropic-direct-runner.ts
|
|
10512
|
+
/**
|
|
10513
|
+
* Anthropic Direct API Runner
|
|
10514
|
+
*
|
|
10515
|
+
* Makes calls directly to api.anthropic.com without needing the claude CLI
|
|
10516
|
+
* binary to be installed or logged in. Works with any valid token:
|
|
10517
|
+
*
|
|
10518
|
+
* sk-ant-api01-... (Console API key)
|
|
10519
|
+
* sk-ant-oat01-... (Claude Code OAuth access token)
|
|
10520
|
+
*
|
|
10521
|
+
* This runner is automatically used when an `anthropic:default` token credential
|
|
10522
|
+
* is present in the auth store and the claude CLI is unavailable or not logged in.
|
|
10523
|
+
*/
|
|
10524
|
+
const log$2 = createSubsystemLogger("agent/anthropic-direct");
|
|
10525
|
+
const MODEL_MAP$1 = {
|
|
10526
|
+
opus: "claude-opus-4-5",
|
|
10527
|
+
"opus-4": "claude-opus-4-5",
|
|
10528
|
+
"opus-4.5": "claude-opus-4-5",
|
|
10529
|
+
"opus-4.6": "claude-opus-4-5",
|
|
10530
|
+
"claude-opus-4-5": "claude-opus-4-5",
|
|
10531
|
+
sonnet: "claude-sonnet-4-5",
|
|
10532
|
+
"sonnet-4.5": "claude-sonnet-4-5",
|
|
10533
|
+
"sonnet-4.1": "claude-sonnet-4-1-20250219",
|
|
10534
|
+
"claude-sonnet-4-5": "claude-sonnet-4-5",
|
|
10535
|
+
haiku: "claude-haiku-3-5",
|
|
10536
|
+
"haiku-3.5": "claude-haiku-3-5",
|
|
10537
|
+
default: "claude-sonnet-4-5"
|
|
9759
10538
|
};
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
10539
|
+
const HISTORY_FILE_SUFFIX$1 = ".anima-history.json";
|
|
10540
|
+
async function loadSessionHistory$1(sessionFile) {
|
|
10541
|
+
const histPath = sessionFile + HISTORY_FILE_SUFFIX$1;
|
|
10542
|
+
try {
|
|
10543
|
+
const raw = await fs$1.readFile(histPath, "utf8");
|
|
10544
|
+
return JSON.parse(raw);
|
|
10545
|
+
} catch {
|
|
10546
|
+
return null;
|
|
9768
10547
|
}
|
|
9769
10548
|
}
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
}
|
|
9777
|
-
|
|
9778
|
-
const trimmed = value?.trim();
|
|
9779
|
-
if (!trimmed) return "-";
|
|
9780
|
-
return `sha256:${sha256HexPrefix(trimmed, opts?.len ?? 12)}`;
|
|
9781
|
-
}
|
|
9782
|
-
|
|
9783
|
-
//#endregion
|
|
9784
|
-
//#region src/agents/workspace-run.ts
|
|
9785
|
-
function resolveRunAgentId(params) {
|
|
9786
|
-
const rawSessionKey = params.sessionKey?.trim() ?? "";
|
|
9787
|
-
const shape = classifySessionKeyShape(rawSessionKey);
|
|
9788
|
-
if (shape === "malformed_agent") throw new Error("Malformed agent session key; refusing workspace resolution.");
|
|
9789
|
-
const explicit = typeof params.agentId === "string" && params.agentId.trim() ? normalizeAgentId(params.agentId) : void 0;
|
|
9790
|
-
if (explicit) return {
|
|
9791
|
-
agentId: explicit,
|
|
9792
|
-
agentIdSource: "explicit"
|
|
9793
|
-
};
|
|
9794
|
-
const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
|
|
9795
|
-
if (shape === "missing" || shape === "legacy_or_alias") return {
|
|
9796
|
-
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
|
9797
|
-
agentIdSource: "default"
|
|
9798
|
-
};
|
|
9799
|
-
const parsed = parseAgentSessionKey(rawSessionKey);
|
|
9800
|
-
if (parsed?.agentId) return {
|
|
9801
|
-
agentId: normalizeAgentId(parsed.agentId),
|
|
9802
|
-
agentIdSource: "session_key"
|
|
9803
|
-
};
|
|
9804
|
-
return {
|
|
9805
|
-
agentId: defaultAgentId || DEFAULT_AGENT_ID,
|
|
9806
|
-
agentIdSource: "default"
|
|
9807
|
-
};
|
|
10549
|
+
async function saveSessionHistory$1(sessionFile, history) {
|
|
10550
|
+
const histPath = sessionFile + HISTORY_FILE_SUFFIX$1;
|
|
10551
|
+
try {
|
|
10552
|
+
await fs$1.mkdir(path.dirname(histPath), { recursive: true });
|
|
10553
|
+
await fs$1.writeFile(histPath, JSON.stringify(history, null, 2), "utf8");
|
|
10554
|
+
} catch (err) {
|
|
10555
|
+
log$2.warn("failed to save session history", { error: String(err) });
|
|
10556
|
+
}
|
|
9808
10557
|
}
|
|
9809
|
-
function
|
|
9810
|
-
|
|
10558
|
+
function resolveModel$1(model) {
|
|
10559
|
+
const key = (model ?? "default").trim().toLowerCase() || "default";
|
|
10560
|
+
return MODEL_MAP$1[key] ?? key;
|
|
9811
10561
|
}
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
10562
|
+
/**
|
|
10563
|
+
* Run an agent turn directly against api.anthropic.com.
|
|
10564
|
+
*
|
|
10565
|
+
* Maintains multi-turn conversation history per session file.
|
|
10566
|
+
* Falls back to single-turn if history is unavailable.
|
|
10567
|
+
*/
|
|
10568
|
+
async function runAnthropicDirectAgent(params) {
|
|
10569
|
+
const started = Date.now();
|
|
10570
|
+
const resolvedModel = resolveModel$1(params.model);
|
|
10571
|
+
log$2.info(`direct api exec: model=${resolvedModel} promptChars=${params.prompt.length}`);
|
|
10572
|
+
const workspaceDir = resolveRunWorkspaceDir({
|
|
10573
|
+
workspaceDir: params.workspaceDir,
|
|
9815
10574
|
sessionKey: params.sessionKey,
|
|
9816
10575
|
agentId: params.agentId,
|
|
9817
10576
|
config: params.config
|
|
9818
|
-
});
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
10577
|
+
}).workspaceDir;
|
|
10578
|
+
const { contextFiles } = await resolveBootstrapContextForRun({
|
|
10579
|
+
workspaceDir,
|
|
10580
|
+
config: params.config,
|
|
10581
|
+
sessionKey: params.sessionKey,
|
|
10582
|
+
sessionId: params.sessionId,
|
|
10583
|
+
warn: makeBootstrapWarn({
|
|
10584
|
+
sessionLabel: params.sessionKey ?? params.sessionId,
|
|
10585
|
+
warn: (msg) => log$2.warn(msg)
|
|
10586
|
+
})
|
|
10587
|
+
});
|
|
10588
|
+
const { defaultAgentId, sessionAgentId } = resolveSessionAgentIds({
|
|
10589
|
+
sessionKey: params.sessionKey,
|
|
10590
|
+
config: params.config
|
|
10591
|
+
});
|
|
10592
|
+
const heartbeatPrompt = sessionAgentId === defaultAgentId ? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt) : void 0;
|
|
10593
|
+
const docsPath = await resolveAnimaDocsPath({
|
|
10594
|
+
workspaceDir,
|
|
10595
|
+
argv1: process.argv[1],
|
|
10596
|
+
cwd: process.cwd(),
|
|
10597
|
+
moduleUrl: import.meta.url
|
|
10598
|
+
});
|
|
10599
|
+
const extraSystemPrompt = [params.extraSystemPrompt?.trim(), "Tools are disabled in this session. Do not call tools."].filter(Boolean).join("\n");
|
|
10600
|
+
const systemPrompt = buildSystemPrompt({
|
|
10601
|
+
workspaceDir,
|
|
10602
|
+
config: params.config,
|
|
10603
|
+
defaultThinkLevel: params.thinkLevel,
|
|
10604
|
+
extraSystemPrompt,
|
|
10605
|
+
ownerNumbers: params.ownerNumbers,
|
|
10606
|
+
heartbeatPrompt,
|
|
10607
|
+
docsPath: docsPath ?? void 0,
|
|
10608
|
+
tools: [],
|
|
10609
|
+
contextFiles,
|
|
10610
|
+
modelDisplay: `anthropic/${resolvedModel}`,
|
|
10611
|
+
agentId: sessionAgentId
|
|
10612
|
+
});
|
|
10613
|
+
let history = await loadSessionHistory$1(params.sessionFile);
|
|
10614
|
+
if (!history) history = {
|
|
10615
|
+
sessionId: params.sessionId,
|
|
10616
|
+
messages: [],
|
|
10617
|
+
createdAt: started,
|
|
10618
|
+
updatedAt: started
|
|
10619
|
+
};
|
|
10620
|
+
history.messages.push({
|
|
10621
|
+
role: "user",
|
|
10622
|
+
content: params.prompt
|
|
10623
|
+
});
|
|
10624
|
+
const requestBody = {
|
|
10625
|
+
model: resolvedModel,
|
|
10626
|
+
max_tokens: 8192,
|
|
10627
|
+
system: systemPrompt,
|
|
10628
|
+
messages: history.messages
|
|
10629
|
+
};
|
|
10630
|
+
try {
|
|
10631
|
+
const controller = new AbortController();
|
|
10632
|
+
const timeoutHandle = setTimeout(() => controller.abort(), params.timeoutMs);
|
|
10633
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
10634
|
+
method: "POST",
|
|
10635
|
+
headers: {
|
|
10636
|
+
"x-api-key": params.token,
|
|
10637
|
+
"anthropic-version": "2023-06-01",
|
|
10638
|
+
"content-type": "application/json",
|
|
10639
|
+
"user-agent": `anima/3.0.5 (direct-runner; ${os.platform()})`
|
|
10640
|
+
},
|
|
10641
|
+
body: JSON.stringify(requestBody),
|
|
10642
|
+
signal: controller.signal
|
|
10643
|
+
});
|
|
10644
|
+
clearTimeout(timeoutHandle);
|
|
10645
|
+
if (!response.ok) {
|
|
10646
|
+
const body = await response.text().catch(() => "");
|
|
10647
|
+
const isAuth = response.status === 401 || response.status === 403;
|
|
10648
|
+
const isRateLimit = response.status === 429;
|
|
10649
|
+
const rateHint = isRateLimit ? " — rate limit hit, will retry next heartbeat." : "";
|
|
10650
|
+
const authHint = isAuth ? " — token may be invalid or expired. Run: anima setup-token" : "";
|
|
10651
|
+
log$2.error(`anthropic api error: HTTP ${response.status}${authHint}${rateHint}`, {
|
|
10652
|
+
status: response.status,
|
|
10653
|
+
body: body.slice(0, 500)
|
|
10654
|
+
});
|
|
10655
|
+
return {
|
|
10656
|
+
status: "failed",
|
|
10657
|
+
meta: {
|
|
10658
|
+
durationMs: Date.now() - started,
|
|
10659
|
+
error: {
|
|
10660
|
+
message: `HTTP ${response.status}: ${body.slice(0, 200)}${authHint}${rateHint}`,
|
|
10661
|
+
kind: isAuth ? "auth" : isRateLimit ? "rate_limit" : "unknown"
|
|
10662
|
+
}
|
|
10663
|
+
}
|
|
10664
|
+
};
|
|
10665
|
+
}
|
|
10666
|
+
const data = await response.json();
|
|
10667
|
+
const outputText = (data.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("").trim();
|
|
10668
|
+
if (!outputText) log$2.warn("anthropic direct: empty response", {
|
|
10669
|
+
stopReason: data.stop_reason,
|
|
10670
|
+
contentTypes: (data.content ?? []).map((b) => b.type)
|
|
10671
|
+
});
|
|
10672
|
+
await params.onAssistantMessageStart?.();
|
|
10673
|
+
if (outputText && params.onPartialReply) await params.onPartialReply({ text: outputText });
|
|
10674
|
+
history.messages.push({
|
|
10675
|
+
role: "assistant",
|
|
10676
|
+
content: outputText
|
|
10677
|
+
});
|
|
10678
|
+
history.updatedAt = Date.now();
|
|
10679
|
+
await saveSessionHistory$1(params.sessionFile, history);
|
|
10680
|
+
const durationMs = Date.now() - started;
|
|
10681
|
+
const inputTokens = data.usage?.input_tokens ?? 0;
|
|
10682
|
+
const outputTokens = data.usage?.output_tokens ?? 0;
|
|
10683
|
+
log$2.info(`direct api done: model=${resolvedModel} in=${inputTokens} out=${outputTokens} ms=${durationMs}`);
|
|
10684
|
+
return {
|
|
10685
|
+
status: "completed",
|
|
10686
|
+
output: outputText,
|
|
10687
|
+
payloads: outputText ? [{ text: outputText }] : [],
|
|
10688
|
+
meta: {
|
|
10689
|
+
durationMs,
|
|
10690
|
+
agentMeta: {
|
|
10691
|
+
provider: "anthropic",
|
|
10692
|
+
model: resolvedModel,
|
|
10693
|
+
usage: {
|
|
10694
|
+
inputTokens,
|
|
10695
|
+
outputTokens,
|
|
10696
|
+
cacheCreationInputTokens: 0,
|
|
10697
|
+
cacheReadInputTokens: 0
|
|
10698
|
+
}
|
|
10699
|
+
}
|
|
10700
|
+
}
|
|
10701
|
+
};
|
|
10702
|
+
} catch (err) {
|
|
10703
|
+
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("aborted"));
|
|
10704
|
+
log$2.error("anthropic direct runner error", { error: String(err) });
|
|
10705
|
+
return {
|
|
10706
|
+
status: isAbort ? "timeout" : "failed",
|
|
10707
|
+
meta: {
|
|
10708
|
+
durationMs: Date.now() - started,
|
|
10709
|
+
error: {
|
|
10710
|
+
message: isAbort ? `Request timed out after ${params.timeoutMs}ms` : String(err instanceof Error ? err.message : err),
|
|
10711
|
+
kind: isAbort ? "timeout" : "unknown"
|
|
10712
|
+
}
|
|
10713
|
+
}
|
|
9826
10714
|
};
|
|
9827
10715
|
}
|
|
9828
|
-
|
|
10716
|
+
}
|
|
10717
|
+
|
|
10718
|
+
//#endregion
|
|
10719
|
+
//#region src/agents/cli-backends.ts
|
|
10720
|
+
const DEFAULT_CLAUDE_BACKEND = {
|
|
10721
|
+
command: "claude",
|
|
10722
|
+
args: [
|
|
10723
|
+
"-p",
|
|
10724
|
+
"--output-format",
|
|
10725
|
+
"json",
|
|
10726
|
+
"--dangerously-skip-permissions"
|
|
10727
|
+
],
|
|
10728
|
+
resumeArgs: [
|
|
10729
|
+
"-p",
|
|
10730
|
+
"--output-format",
|
|
10731
|
+
"json",
|
|
10732
|
+
"--dangerously-skip-permissions",
|
|
10733
|
+
"--resume",
|
|
10734
|
+
"{sessionId}"
|
|
10735
|
+
],
|
|
10736
|
+
output: "jsonl",
|
|
10737
|
+
input: "arg",
|
|
10738
|
+
modelArg: "--model",
|
|
10739
|
+
modelAliases: {
|
|
10740
|
+
opus: "opus",
|
|
10741
|
+
"opus-4.6": "opus",
|
|
10742
|
+
"opus-4.5": "opus",
|
|
10743
|
+
"opus-4": "opus",
|
|
10744
|
+
"claude-opus-4-6": "opus",
|
|
10745
|
+
"claude-opus-4-5": "opus",
|
|
10746
|
+
"claude-opus-4": "opus",
|
|
10747
|
+
sonnet: "sonnet",
|
|
10748
|
+
"sonnet-4.5": "sonnet",
|
|
10749
|
+
"sonnet-4.1": "sonnet",
|
|
10750
|
+
"sonnet-4.0": "sonnet",
|
|
10751
|
+
"claude-sonnet-4-5": "sonnet",
|
|
10752
|
+
"claude-sonnet-4-1": "sonnet",
|
|
10753
|
+
"claude-sonnet-4-0": "sonnet",
|
|
10754
|
+
haiku: "haiku",
|
|
10755
|
+
"haiku-3.5": "haiku",
|
|
10756
|
+
"claude-haiku-3-5": "haiku"
|
|
10757
|
+
},
|
|
10758
|
+
sessionArg: "--session-id",
|
|
10759
|
+
sessionMode: "always",
|
|
10760
|
+
sessionIdFields: [
|
|
10761
|
+
"session_id",
|
|
10762
|
+
"sessionId",
|
|
10763
|
+
"conversation_id",
|
|
10764
|
+
"conversationId"
|
|
10765
|
+
],
|
|
10766
|
+
systemPromptArg: "--append-system-prompt",
|
|
10767
|
+
systemPromptMode: "append",
|
|
10768
|
+
systemPromptWhen: "first",
|
|
10769
|
+
clearEnv: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD"],
|
|
10770
|
+
serialize: true
|
|
10771
|
+
};
|
|
10772
|
+
const DEFAULT_CODEX_BACKEND = {
|
|
10773
|
+
command: "codex",
|
|
10774
|
+
args: [
|
|
10775
|
+
"exec",
|
|
10776
|
+
"--json",
|
|
10777
|
+
"--color",
|
|
10778
|
+
"never",
|
|
10779
|
+
"--sandbox",
|
|
10780
|
+
"read-only",
|
|
10781
|
+
"--skip-git-repo-check"
|
|
10782
|
+
],
|
|
10783
|
+
resumeArgs: [
|
|
10784
|
+
"exec",
|
|
10785
|
+
"resume",
|
|
10786
|
+
"{sessionId}"
|
|
10787
|
+
],
|
|
10788
|
+
output: "jsonl",
|
|
10789
|
+
resumeOutput: "text",
|
|
10790
|
+
input: "arg",
|
|
10791
|
+
modelArg: "--model",
|
|
10792
|
+
imageArg: "--image",
|
|
10793
|
+
sessionMode: "existing",
|
|
10794
|
+
serialize: true
|
|
10795
|
+
};
|
|
10796
|
+
const CLAUDE_BACKEND_ALIASES = [
|
|
10797
|
+
"claude-cli",
|
|
10798
|
+
"anthropic",
|
|
10799
|
+
"claude"
|
|
10800
|
+
];
|
|
10801
|
+
const CODEX_BACKEND_ALIASES = [
|
|
10802
|
+
"codex-cli",
|
|
10803
|
+
"openai-codex",
|
|
10804
|
+
"openai",
|
|
10805
|
+
"codex"
|
|
10806
|
+
];
|
|
10807
|
+
const CLAUDE_BACKEND_ALIAS_SET = new Set(CLAUDE_BACKEND_ALIASES.map((alias) => normalizeBackendKey(alias)));
|
|
10808
|
+
const CODEX_BACKEND_ALIAS_SET = new Set(CODEX_BACKEND_ALIASES.map((alias) => normalizeBackendKey(alias)));
|
|
10809
|
+
function normalizeBackendKey(key) {
|
|
10810
|
+
return normalizeProviderId(key);
|
|
10811
|
+
}
|
|
10812
|
+
function pickBackendConfig(config, normalizedId) {
|
|
10813
|
+
for (const [key, entry] of Object.entries(config)) if (normalizeBackendKey(key) === normalizedId) return entry;
|
|
10814
|
+
}
|
|
10815
|
+
function pickBackendConfigByAliases(config, aliases) {
|
|
10816
|
+
for (const alias of aliases) {
|
|
10817
|
+
const matched = pickBackendConfig(config, normalizeBackendKey(alias));
|
|
10818
|
+
if (matched) return matched;
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
function mergeBackendConfig(base, override) {
|
|
10822
|
+
if (!override) return { ...base };
|
|
9829
10823
|
return {
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
10824
|
+
...base,
|
|
10825
|
+
...override,
|
|
10826
|
+
args: override.args ?? base.args,
|
|
10827
|
+
env: {
|
|
10828
|
+
...base.env,
|
|
10829
|
+
...override.env
|
|
10830
|
+
},
|
|
10831
|
+
modelAliases: {
|
|
10832
|
+
...base.modelAliases,
|
|
10833
|
+
...override.modelAliases
|
|
10834
|
+
},
|
|
10835
|
+
clearEnv: Array.from(new Set([...base.clearEnv ?? [], ...override.clearEnv ?? []])),
|
|
10836
|
+
sessionIdFields: override.sessionIdFields ?? base.sessionIdFields,
|
|
10837
|
+
sessionArgs: override.sessionArgs ?? base.sessionArgs,
|
|
10838
|
+
resumeArgs: override.resumeArgs ?? base.resumeArgs
|
|
10839
|
+
};
|
|
10840
|
+
}
|
|
10841
|
+
function resolveCliBackendConfig(provider, cfg) {
|
|
10842
|
+
const normalized = normalizeBackendKey(provider);
|
|
10843
|
+
const configured = cfg?.agents?.defaults?.cliBackends ?? {};
|
|
10844
|
+
if (CLAUDE_BACKEND_ALIAS_SET.has(normalized)) {
|
|
10845
|
+
const merged = mergeBackendConfig(DEFAULT_CLAUDE_BACKEND, pickBackendConfigByAliases(configured, [provider, ...CLAUDE_BACKEND_ALIASES]));
|
|
10846
|
+
const command = merged.command?.trim();
|
|
10847
|
+
if (!command) return null;
|
|
10848
|
+
return {
|
|
10849
|
+
id: normalizeBackendKey("claude-cli"),
|
|
10850
|
+
config: {
|
|
10851
|
+
...merged,
|
|
10852
|
+
command
|
|
10853
|
+
}
|
|
10854
|
+
};
|
|
10855
|
+
}
|
|
10856
|
+
if (CODEX_BACKEND_ALIAS_SET.has(normalized)) {
|
|
10857
|
+
const merged = mergeBackendConfig(DEFAULT_CODEX_BACKEND, pickBackendConfigByAliases(configured, [provider, ...CODEX_BACKEND_ALIASES]));
|
|
10858
|
+
const command = merged.command?.trim();
|
|
10859
|
+
if (!command) return null;
|
|
10860
|
+
return {
|
|
10861
|
+
id: normalizeBackendKey("codex-cli"),
|
|
10862
|
+
config: {
|
|
10863
|
+
...merged,
|
|
10864
|
+
command
|
|
10865
|
+
}
|
|
10866
|
+
};
|
|
10867
|
+
}
|
|
10868
|
+
const override = pickBackendConfig(configured, normalized);
|
|
10869
|
+
if (!override) return null;
|
|
10870
|
+
const command = override.command?.trim();
|
|
10871
|
+
if (!command) return null;
|
|
10872
|
+
return {
|
|
10873
|
+
id: normalized,
|
|
10874
|
+
config: {
|
|
10875
|
+
...override,
|
|
10876
|
+
command
|
|
10877
|
+
}
|
|
9835
10878
|
};
|
|
9836
10879
|
}
|
|
9837
10880
|
|
|
10881
|
+
//#endregion
|
|
10882
|
+
//#region src/agents/failover-error.ts
|
|
10883
|
+
var FailoverError = class extends Error {
|
|
10884
|
+
constructor(message, params) {
|
|
10885
|
+
super(message, { cause: params.cause });
|
|
10886
|
+
this.name = "FailoverError";
|
|
10887
|
+
this.reason = params.reason;
|
|
10888
|
+
this.provider = params.provider;
|
|
10889
|
+
this.model = params.model;
|
|
10890
|
+
this.profileId = params.profileId;
|
|
10891
|
+
this.status = params.status;
|
|
10892
|
+
this.code = params.code;
|
|
10893
|
+
}
|
|
10894
|
+
};
|
|
10895
|
+
function resolveFailoverStatus(reason) {
|
|
10896
|
+
switch (reason) {
|
|
10897
|
+
case "billing": return 402;
|
|
10898
|
+
case "rate_limit": return 429;
|
|
10899
|
+
case "auth": return 401;
|
|
10900
|
+
case "timeout": return 408;
|
|
10901
|
+
case "format": return 400;
|
|
10902
|
+
default: return;
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10905
|
+
|
|
9838
10906
|
//#endregion
|
|
9839
10907
|
//#region src/agents/cli-runner.ts
|
|
9840
10908
|
const log$1 = createSubsystemLogger("agent/claude-cli");
|
|
@@ -10065,35 +11133,31 @@ async function runCliAgent(params) {
|
|
|
10065
11133
|
}
|
|
10066
11134
|
|
|
10067
11135
|
//#endregion
|
|
10068
|
-
//#region src/agents/
|
|
11136
|
+
//#region src/agents/gemini-direct-runner.ts
|
|
10069
11137
|
/**
|
|
10070
|
-
*
|
|
11138
|
+
* Gemini Direct API Runner
|
|
10071
11139
|
*
|
|
10072
|
-
* Makes calls directly to
|
|
10073
|
-
*
|
|
11140
|
+
* Makes calls directly to generativelanguage.googleapis.com without needing
|
|
11141
|
+
* a CLI wrapper. Works with Google API keys (GEMINI_API_KEY).
|
|
10074
11142
|
*
|
|
10075
|
-
*
|
|
10076
|
-
*
|
|
10077
|
-
*
|
|
10078
|
-
* This runner is automatically used when an `anthropic:default` token credential
|
|
10079
|
-
* is present in the auth store and the claude CLI is unavailable or not logged in.
|
|
11143
|
+
* This runner is automatically used when a google API key is available
|
|
11144
|
+
* and the provider is set to "google" or "gemini".
|
|
10080
11145
|
*/
|
|
10081
|
-
const log = createSubsystemLogger("agent/
|
|
11146
|
+
const log = createSubsystemLogger("agent/gemini-direct");
|
|
11147
|
+
const DEFAULT_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
10082
11148
|
const MODEL_MAP = {
|
|
10083
|
-
|
|
10084
|
-
"
|
|
10085
|
-
"
|
|
10086
|
-
"
|
|
10087
|
-
"
|
|
10088
|
-
|
|
10089
|
-
"
|
|
10090
|
-
"
|
|
10091
|
-
"
|
|
10092
|
-
|
|
10093
|
-
"haiku-3.5": "claude-haiku-3-5",
|
|
10094
|
-
default: "claude-sonnet-4-5"
|
|
11149
|
+
gemini: "gemini-2.0-flash",
|
|
11150
|
+
"gemini-pro": "gemini-1.5-pro",
|
|
11151
|
+
"gemini-flash": "gemini-2.0-flash",
|
|
11152
|
+
"gemini-2.0": "gemini-2.0-flash",
|
|
11153
|
+
"gemini-2.0-flash": "gemini-2.0-flash",
|
|
11154
|
+
"gemini-2.0-pro": "gemini-2.0-pro-exp-02-05",
|
|
11155
|
+
"gemini-1.5": "gemini-1.5-pro",
|
|
11156
|
+
"gemini-1.5-pro": "gemini-1.5-pro",
|
|
11157
|
+
"gemini-1.5-flash": "gemini-1.5-flash",
|
|
11158
|
+
default: "gemini-2.0-flash"
|
|
10095
11159
|
};
|
|
10096
|
-
const HISTORY_FILE_SUFFIX = ".
|
|
11160
|
+
const HISTORY_FILE_SUFFIX = ".gemini-history.json";
|
|
10097
11161
|
async function loadSessionHistory(sessionFile) {
|
|
10098
11162
|
const histPath = sessionFile + HISTORY_FILE_SUFFIX;
|
|
10099
11163
|
try {
|
|
@@ -10116,15 +11180,19 @@ function resolveModel(model) {
|
|
|
10116
11180
|
const key = (model ?? "default").trim().toLowerCase() || "default";
|
|
10117
11181
|
return MODEL_MAP[key] ?? key;
|
|
10118
11182
|
}
|
|
11183
|
+
function buildModelPath(model) {
|
|
11184
|
+
return model.startsWith("models/") ? model : `models/${model}`;
|
|
11185
|
+
}
|
|
10119
11186
|
/**
|
|
10120
|
-
* Run an agent turn directly against
|
|
11187
|
+
* Run an agent turn directly against generativelanguage.googleapis.com.
|
|
10121
11188
|
*
|
|
10122
11189
|
* Maintains multi-turn conversation history per session file.
|
|
10123
11190
|
* Falls back to single-turn if history is unavailable.
|
|
10124
11191
|
*/
|
|
10125
|
-
async function
|
|
11192
|
+
async function runGeminiDirectAgent(params) {
|
|
10126
11193
|
const started = Date.now();
|
|
10127
11194
|
const resolvedModel = resolveModel(params.model);
|
|
11195
|
+
const modelPath = buildModelPath(resolvedModel);
|
|
10128
11196
|
log.info(`direct api exec: model=${resolvedModel} promptChars=${params.prompt.length}`);
|
|
10129
11197
|
const workspaceDir = resolveRunWorkspaceDir({
|
|
10130
11198
|
workspaceDir: params.workspaceDir,
|
|
@@ -10164,36 +11232,37 @@ async function runAnthropicDirectAgent(params) {
|
|
|
10164
11232
|
docsPath: docsPath ?? void 0,
|
|
10165
11233
|
tools: [],
|
|
10166
11234
|
contextFiles,
|
|
10167
|
-
modelDisplay: `
|
|
11235
|
+
modelDisplay: `google/${resolvedModel}`,
|
|
10168
11236
|
agentId: sessionAgentId
|
|
10169
11237
|
});
|
|
10170
11238
|
let history = await loadSessionHistory(params.sessionFile);
|
|
10171
11239
|
if (!history) history = {
|
|
10172
11240
|
sessionId: params.sessionId,
|
|
10173
|
-
|
|
11241
|
+
contents: [],
|
|
10174
11242
|
createdAt: started,
|
|
10175
11243
|
updatedAt: started
|
|
10176
11244
|
};
|
|
10177
|
-
history.
|
|
11245
|
+
history.contents.push({
|
|
10178
11246
|
role: "user",
|
|
10179
|
-
|
|
11247
|
+
parts: [{ text: params.prompt }]
|
|
10180
11248
|
});
|
|
10181
11249
|
const requestBody = {
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
11250
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
11251
|
+
contents: history.contents,
|
|
11252
|
+
generationConfig: {
|
|
11253
|
+
maxOutputTokens: 8192,
|
|
11254
|
+
temperature: 1
|
|
11255
|
+
}
|
|
10186
11256
|
};
|
|
10187
11257
|
try {
|
|
10188
11258
|
const controller = new AbortController();
|
|
10189
11259
|
const timeoutHandle = setTimeout(() => controller.abort(), params.timeoutMs);
|
|
10190
|
-
const
|
|
11260
|
+
const url = `${DEFAULT_GEMINI_BASE_URL}/${modelPath}:generateContent?key=${params.apiKey}`;
|
|
11261
|
+
const response = await fetch(url, {
|
|
10191
11262
|
method: "POST",
|
|
10192
11263
|
headers: {
|
|
10193
|
-
"
|
|
10194
|
-
"
|
|
10195
|
-
"content-type": "application/json",
|
|
10196
|
-
"user-agent": `anima/3.0.5 (direct-runner; ${os.platform()})`
|
|
11264
|
+
"Content-Type": "application/json",
|
|
11265
|
+
"User-Agent": `anima/5.0.1 (gemini-direct-runner; ${os.platform()})`
|
|
10197
11266
|
},
|
|
10198
11267
|
body: JSON.stringify(requestBody),
|
|
10199
11268
|
signal: controller.signal
|
|
@@ -10204,8 +11273,8 @@ async function runAnthropicDirectAgent(params) {
|
|
|
10204
11273
|
const isAuth = response.status === 401 || response.status === 403;
|
|
10205
11274
|
const isRateLimit = response.status === 429;
|
|
10206
11275
|
const rateHint = isRateLimit ? " — rate limit hit, will retry next heartbeat." : "";
|
|
10207
|
-
const authHint = isAuth ? " —
|
|
10208
|
-
log.error(`
|
|
11276
|
+
const authHint = isAuth ? " — API key may be invalid. Check GEMINI_API_KEY environment variable." : "";
|
|
11277
|
+
log.error(`gemini api error: HTTP ${response.status}${authHint}${rateHint}`, {
|
|
10209
11278
|
status: response.status,
|
|
10210
11279
|
body: body.slice(0, 500)
|
|
10211
11280
|
});
|
|
@@ -10221,51 +11290,51 @@ async function runAnthropicDirectAgent(params) {
|
|
|
10221
11290
|
};
|
|
10222
11291
|
}
|
|
10223
11292
|
const data = await response.json();
|
|
10224
|
-
const
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
}
|
|
10235
|
-
|
|
10236
|
-
await saveSessionHistory(params.sessionFile, history);
|
|
11293
|
+
const candidate = data.candidates?.[0];
|
|
11294
|
+
const assistantText = (candidate?.content?.parts ?? []).filter((p) => typeof p.text === "string").map((p) => p.text).join("\n");
|
|
11295
|
+
if (assistantText && params.onPartialReply) await params.onPartialReply({ text: assistantText });
|
|
11296
|
+
if (assistantText) {
|
|
11297
|
+
history.contents.push({
|
|
11298
|
+
role: "model",
|
|
11299
|
+
parts: [{ text: assistantText }]
|
|
11300
|
+
});
|
|
11301
|
+
history.updatedAt = Date.now();
|
|
11302
|
+
await saveSessionHistory(params.sessionFile, history);
|
|
11303
|
+
}
|
|
11304
|
+
const usage = data.usageMetadata;
|
|
10237
11305
|
const durationMs = Date.now() - started;
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
11306
|
+
log.info(`gemini api complete: ${durationMs}ms`, {
|
|
11307
|
+
inputTokens: usage?.promptTokenCount,
|
|
11308
|
+
outputTokens: usage?.candidatesTokenCount,
|
|
11309
|
+
finishReason: candidate?.finishReason
|
|
11310
|
+
});
|
|
10241
11311
|
return {
|
|
10242
11312
|
status: "completed",
|
|
10243
|
-
output:
|
|
10244
|
-
payloads: outputText ? [{ text: outputText }] : [],
|
|
11313
|
+
output: assistantText,
|
|
10245
11314
|
meta: {
|
|
10246
11315
|
durationMs,
|
|
10247
11316
|
agentMeta: {
|
|
10248
|
-
provider: "anthropic",
|
|
10249
11317
|
model: resolvedModel,
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
}
|
|
11318
|
+
provider: "google",
|
|
11319
|
+
usage: usage ? {
|
|
11320
|
+
input: usage.promptTokenCount ?? 0,
|
|
11321
|
+
output: usage.candidatesTokenCount ?? 0
|
|
11322
|
+
} : void 0
|
|
10256
11323
|
}
|
|
10257
11324
|
}
|
|
10258
11325
|
};
|
|
10259
11326
|
} catch (err) {
|
|
10260
|
-
const isAbort = err instanceof Error &&
|
|
10261
|
-
|
|
11327
|
+
const isAbort = err instanceof Error && err.name === "AbortError";
|
|
11328
|
+
const errorKind = isAbort ? "timeout" : "unknown";
|
|
11329
|
+
const errorMsg = isAbort ? `Request timed out after ${params.timeoutMs}ms` : String(err);
|
|
11330
|
+
log.error(`gemini api error: ${errorMsg}`, { error: String(err) });
|
|
10262
11331
|
return {
|
|
10263
|
-
status:
|
|
11332
|
+
status: "failed",
|
|
10264
11333
|
meta: {
|
|
10265
11334
|
durationMs: Date.now() - started,
|
|
10266
11335
|
error: {
|
|
10267
|
-
message:
|
|
10268
|
-
kind:
|
|
11336
|
+
message: errorMsg,
|
|
11337
|
+
kind: errorKind
|
|
10269
11338
|
}
|
|
10270
11339
|
}
|
|
10271
11340
|
};
|
|
@@ -10353,6 +11422,57 @@ async function runEmbeddedPiAgent(...args) {
|
|
|
10353
11422
|
}
|
|
10354
11423
|
}
|
|
10355
11424
|
}
|
|
11425
|
+
if (provider === "google" || provider === "gemini") {
|
|
11426
|
+
const geminiApiKey = (await resolveApiKeyForProvider({
|
|
11427
|
+
provider: "google",
|
|
11428
|
+
cfg: params.config
|
|
11429
|
+
}))?.apiKey;
|
|
11430
|
+
if (geminiApiKey) {
|
|
11431
|
+
await emitAgentEvent(params, "lifecycle", {
|
|
11432
|
+
phase: "start",
|
|
11433
|
+
startedAt
|
|
11434
|
+
});
|
|
11435
|
+
try {
|
|
11436
|
+
const result = await runGeminiDirectAgent({
|
|
11437
|
+
apiKey: geminiApiKey,
|
|
11438
|
+
sessionId: params.sessionId,
|
|
11439
|
+
sessionKey: params.sessionKey,
|
|
11440
|
+
agentId: params.agentId,
|
|
11441
|
+
sessionFile: params.sessionFile,
|
|
11442
|
+
workspaceDir: params.workspaceDir,
|
|
11443
|
+
config: params.config,
|
|
11444
|
+
prompt: params.prompt,
|
|
11445
|
+
model: params.model,
|
|
11446
|
+
thinkLevel: params.thinkLevel,
|
|
11447
|
+
timeoutMs,
|
|
11448
|
+
runId,
|
|
11449
|
+
extraSystemPrompt: params.extraSystemPrompt,
|
|
11450
|
+
ownerNumbers: params.ownerNumbers,
|
|
11451
|
+
onPartialReply: async (payload) => {
|
|
11452
|
+
if (!assistantStarted) {
|
|
11453
|
+
assistantStarted = true;
|
|
11454
|
+
await params.onAssistantMessageStart?.();
|
|
11455
|
+
}
|
|
11456
|
+
await params.onPartialReply?.(payload);
|
|
11457
|
+
await emitAgentEvent(params, "assistant", { text: payload.text });
|
|
11458
|
+
},
|
|
11459
|
+
onAssistantMessageStart: params.onAssistantMessageStart
|
|
11460
|
+
});
|
|
11461
|
+
await emitAgentEvent(params, "lifecycle", {
|
|
11462
|
+
phase: "end",
|
|
11463
|
+
durationMs: Date.now() - startedAt,
|
|
11464
|
+
status: result.status
|
|
11465
|
+
});
|
|
11466
|
+
return result;
|
|
11467
|
+
} catch (err) {
|
|
11468
|
+
await emitAgentEvent(params, "lifecycle", {
|
|
11469
|
+
phase: "error",
|
|
11470
|
+
error: String(err instanceof Error ? err.message : err)
|
|
11471
|
+
});
|
|
11472
|
+
throw err;
|
|
11473
|
+
}
|
|
11474
|
+
}
|
|
11475
|
+
}
|
|
10356
11476
|
const cliProvider = resolveCompatCliProvider(provider, params.config);
|
|
10357
11477
|
if (!resolveCliBackendConfig(cliProvider, params.config)) throw new Error(`No CLI backend available for provider "${provider}" (resolved "${cliProvider}").\nEither:\n • Run: anima setup-token (set an Anthropic API key — no CLI needed)\n • Install the matching CLI and log in`);
|
|
10358
11478
|
await emitAgentEvent(params, "lifecycle", {
|