@chibitek/chibi 0.1.172 → 0.1.174
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/assets/{abnfDiagram-O67JEVCF-CGoW52MW.js → abnfDiagram-O67JEVCF-BVDndX1g.js} +1 -1
- package/dist/assets/{arc-DEkKLw4F.js → arc-WduS6wYB.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-Cyi6MYs6.js → architectureDiagram-NJMV4G6O-CYK9LkCf.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CddA0wa_.js → blockDiagram-BEXU5L5S-BMj-aPW9.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-Dg8q9n90.js → c4Diagram-YGBWAQC7-cXYIStgL.js} +1 -1
- package/dist/assets/channel-TratrwmM.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BSMub34c.js → chunk-2Q5K7J3B-CZ_nCppA.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW--Xc7Up5y.js → chunk-3FUC2YCW-C3wRAbtM.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-B9Ae4f30.js → chunk-5VM5RSS4-CMvgOXJP.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Dh1To9G3.js → chunk-GTNCS2PH-9wBuLQzU.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-C_5eiV2n.js → chunk-GWA4HPMP-BpJMmfAi.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-5wQuPsks.js → chunk-JWPE2WC7-BnM06ABv.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-BGXS05ur.js → chunk-NETBCI7D-CjjiiXiT.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-ClIMhmD5.js → chunk-WEXAMYUT-D3heHHZs.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CcKs11-L.js → chunk-XXDRQBXY-n_P41ZlM.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BCiLckGU.js → classDiagram-v2-NBCMYWYE-Dz60Ek-6.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-JyAqT-ND.js → cose-bilkent-JH36ORCC-BCt6vBi9.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BznovZj0.js → cynefin-OW5HDTMX-U0uq4EAQ.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-BDRrTIEB.js → cynefinDiagram-VND7K2PF-CaQX0eUJ.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-BbWguhLj.js → dagre-6A5THRUB-DlCMESYH.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-BMk1KTBr.js → diagram-22UHCM2B-Cl1xmlTD.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-ChnEP2in.js → diagram-3UASUU5V-Cus3CPg7.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-C78imnTV.js → diagram-ATOU4E4O-i06rMvae.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-Dekx02f_.js → diagram-CDSNMT55-CUM5_heN.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-D2__fPwe.js → diagram-MLGK6HIB-B5NKRruo.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-C9l7MZm7.js → diagram-MPIPVDR6-CAumAcGl.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D4x0h3XT.js → ebnfDiagram-ZINNZB2B-B19d7d8l.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-twChz5io.js → elk-276RUBZZ-NJEpfRWi.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-BXEKWWMq.js → erDiagram-OPXOYQCR-BExk6CHc.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Ct-XrJMr.js → flowDiagram-KWPJA3E3-ClExQ_p0.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-Bn92lxCs.js → ganttDiagram-FUAMR5RP-1MeM9sSl.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dvfjo7cn.js → gitGraphDiagram-X574FWY7-Tw9Dudpa.js} +1 -1
- package/dist/assets/{index-DvneZE6Z.js → index-KYHldywn.js} +140 -140
- package/dist/assets/{index-CarzpTfN.js → index-qsorLr2x.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-MJ14OyNX.js → infoDiagram-VRGFBTTK-ChU2tjvl.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-nf9xhTNX.js → ishikawaDiagram-OU5B5YK6-BeTbrX2Q.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-DqfK6X2e.js → journeyDiagram-ZHPQQLJL-B7ijaWBD.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-CjUR1QYT.js → kanban-definition-PNTS6WVX-JBmK2F1r.js} +1 -1
- package/dist/assets/{linear-dPqGsZwL.js → linear-B7FXe3Ue.js} +1 -1
- package/dist/assets/{mermaid.core-zL0DLPAI.js → mermaid.core-BPlfZpqv.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-C1-Jqg3_.js → mindmap-definition-NLK3R4M7-CmT3S5QR.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-CFlvuAHC.js → pegDiagram-GJSIUBJH-CFFHIcdQ.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-BvyT0IgF.js → pieDiagram-5QR66LMP-yTiPmXRV.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-bg7JeR2c.js → quadrantDiagram-O4NWA36T-Cw8u7esm.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-dF2Nqim8.js → railroadDiagram-XR7U4H2S-BfKggsOW.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-CwVu_Or6.js → requirementDiagram-PLB6GJNP-Cf3n79jF.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-wBC3-mRy.js → sankeyDiagram-IPEJSGJF-B2NkSZig.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-Bs9lHMf-.js → sequenceDiagram-PO4LG4MO-BfFYiYK3.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-d6YiGOIQ.js → sizeCapture-INFHLROL-CZ_2nC3f.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK--f6dscuE.js → stateDiagram-v2-GCMORJYK-B9jwzMXf.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-CQpzBoAc.js → swimlanes-2SLR337P-Bxhafu77.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-DWNGCaH3.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-DpoyUyqt.js → timeline-definition-EJHVYXUP-Dle5SJls.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-Dg7mR3GW.js → usecaseDiagram-POWQR4AR-CgRTu9EO.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CoEfTL7k.js → vennDiagram-UO4OBE2U-D7m_1z6B.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-24tIY01C.js → wardleyDiagram-VNRHLVJA-7-TzmX4L.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-CsoqIRqn.js → xychartDiagram-PMCCYNJV-BVXXdviS.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/index.js +2797 -1875
- package/dist-server/openmausbot.js +64 -2
- package/dist-server/pair-cli.js +64 -2
- package/dist-server/server/builtin-chibi.js +1 -1
- package/dist-server/server/dictation.js +8 -0
- package/dist-server/server/drivers/claude.js +4 -4
- package/dist-server/server/engine-tiers-view.js +49 -0
- package/dist-server/server/engine-tiers.js +37 -55
- package/dist-server/server/harness-import/engine-policy.js +6 -1
- package/dist-server/server/index.js +12 -13
- package/dist-server/server/oauth-account-core.js +153 -0
- package/dist-server/server/oauth-account-tools.js +7 -348
- package/dist-server/server/oauth-calendar-tools.js +551 -0
- package/dist-server/server/personal-bot.js +0 -26
- package/dist-server/server/remote-desktop.js +139 -7
- package/dist-server/server/routes/engine-tiers.js +12 -1
- package/dist-server/server/subscription-bots.js +2 -21
- package/dist-server/server/turn-resources.js +28 -0
- package/dist-server/server/workspace-backup-archive.js +138 -0
- package/dist-server/server/workspace-backup-http.js +50 -8
- package/dist-server/server/workspace-backup-private.js +83 -0
- package/dist-server/server/workspace-backup-secrets.js +90 -0
- package/dist-server/server/workspace-backup.js +171 -235
- package/dist-server/server/workspace-restore-paths.js +186 -0
- package/dist-server/shared/oauth-providers.js +2 -1
- package/dist-server/shared/provider-accounts.js +29 -0
- package/package.json +1 -1
- package/dist/assets/channel-Dda8O6gd.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C1ko4o6t.js +0 -8
|
@@ -19685,6 +19685,68 @@ var init_provider_accounts = __esm({
|
|
|
19685
19685
|
config: {},
|
|
19686
19686
|
keyUrl: "https://opencode.ai/go",
|
|
19687
19687
|
signupUrl: "https://opencode.ai/go"
|
|
19688
|
+
},
|
|
19689
|
+
// Permanent free tiers (docs/specs/free-models-in-auto): the last resort
|
|
19690
|
+
// when every subscription and pay-per-use key is out (Erick's rule, a
|
|
19691
|
+
// model or billing error never stops the user). Off by default
|
|
19692
|
+
// (engineTiers.includeFree); never used for client-facing bots.
|
|
19693
|
+
{
|
|
19694
|
+
id: "openrouterFree",
|
|
19695
|
+
kind: "apiKey",
|
|
19696
|
+
label: "OpenRouter (free models)",
|
|
19697
|
+
billing: "free",
|
|
19698
|
+
driver: "openai-compat",
|
|
19699
|
+
envVar: "OPENROUTER_FREE_API_KEY",
|
|
19700
|
+
icon: "openrouter",
|
|
19701
|
+
config: { url: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_FREE_API_KEY", model: "", provider: "" },
|
|
19702
|
+
keyTest: { provider: "openaiCompat", url: "https://openrouter.ai/api/v1" },
|
|
19703
|
+
keyUrl: "https://openrouter.ai/keys",
|
|
19704
|
+
signupUrl: "https://openrouter.ai/models?max_price=0"
|
|
19705
|
+
},
|
|
19706
|
+
{
|
|
19707
|
+
id: "googleAiStudioFree",
|
|
19708
|
+
kind: "apiKey",
|
|
19709
|
+
label: "Google AI Studio (free tier)",
|
|
19710
|
+
billing: "free",
|
|
19711
|
+
driver: "openai-compat",
|
|
19712
|
+
envVar: "GOOGLE_AI_STUDIO_FREE_API_KEY",
|
|
19713
|
+
icon: "google",
|
|
19714
|
+
config: { url: "https://generativelanguage.googleapis.com/v1beta/openai", apiKeyEnv: "GOOGLE_AI_STUDIO_FREE_API_KEY", model: "", provider: "" },
|
|
19715
|
+
keyTest: { provider: "openaiCompat", url: "https://generativelanguage.googleapis.com/v1beta/openai" },
|
|
19716
|
+
keyUrl: "https://aistudio.google.com/apikey"
|
|
19717
|
+
},
|
|
19718
|
+
{
|
|
19719
|
+
id: "groqFree",
|
|
19720
|
+
kind: "apiKey",
|
|
19721
|
+
label: "Groq (free tier)",
|
|
19722
|
+
billing: "free",
|
|
19723
|
+
driver: "openai-compat",
|
|
19724
|
+
envVar: "GROQ_FREE_API_KEY",
|
|
19725
|
+
config: { url: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_FREE_API_KEY", model: "", provider: "" },
|
|
19726
|
+
keyTest: { provider: "openaiCompat", url: "https://api.groq.com/openai/v1" },
|
|
19727
|
+
keyUrl: "https://console.groq.com/keys"
|
|
19728
|
+
},
|
|
19729
|
+
{
|
|
19730
|
+
id: "cerebrasFree",
|
|
19731
|
+
kind: "apiKey",
|
|
19732
|
+
label: "Cerebras (free tier)",
|
|
19733
|
+
billing: "free",
|
|
19734
|
+
driver: "openai-compat",
|
|
19735
|
+
envVar: "CEREBRAS_FREE_API_KEY",
|
|
19736
|
+
config: { url: "https://api.cerebras.ai/v1", apiKeyEnv: "CEREBRAS_FREE_API_KEY", model: "", provider: "" },
|
|
19737
|
+
keyTest: { provider: "openaiCompat", url: "https://api.cerebras.ai/v1" },
|
|
19738
|
+
keyUrl: "https://cloud.cerebras.ai/platform/apikeys"
|
|
19739
|
+
},
|
|
19740
|
+
{
|
|
19741
|
+
id: "nvidiaFree",
|
|
19742
|
+
kind: "apiKey",
|
|
19743
|
+
label: "NVIDIA NIM (free tier)",
|
|
19744
|
+
billing: "free",
|
|
19745
|
+
driver: "openai-compat",
|
|
19746
|
+
envVar: "NVIDIA_FREE_API_KEY",
|
|
19747
|
+
config: { url: "https://integrate.api.nvidia.com/v1", apiKeyEnv: "NVIDIA_FREE_API_KEY", model: "", provider: "" },
|
|
19748
|
+
keyTest: { provider: "openaiCompat", url: "https://integrate.api.nvidia.com/v1" },
|
|
19749
|
+
keyUrl: "https://build.nvidia.com/settings/api-keys"
|
|
19688
19750
|
}
|
|
19689
19751
|
];
|
|
19690
19752
|
}
|
|
@@ -30117,8 +30179,8 @@ var init_claude = __esm({
|
|
|
30117
30179
|
DEFAULT_AUTOCOMPACT_TOKENS = 2e5;
|
|
30118
30180
|
CLAUDE_FLAG_FLOORS = {
|
|
30119
30181
|
"--strict-mcp-config": [1, 0, 60],
|
|
30120
|
-
"--setting-sources": [1, 0,
|
|
30121
|
-
"--autocompact": [2, 1,
|
|
30182
|
+
"--setting-sources": [1, 0, 125],
|
|
30183
|
+
"--autocompact": [2, 1, 221],
|
|
30122
30184
|
// 2.1.267 is the first CLI that accepts it; below that the recorded prompt
|
|
30123
30185
|
// simply is not refreshed, which is the pre-existing behaviour.
|
|
30124
30186
|
"--system-prompt-snapshot": [2, 1, 267],
|
package/dist-server/pair-cli.js
CHANGED
|
@@ -19685,6 +19685,68 @@ var init_provider_accounts = __esm({
|
|
|
19685
19685
|
config: {},
|
|
19686
19686
|
keyUrl: "https://opencode.ai/go",
|
|
19687
19687
|
signupUrl: "https://opencode.ai/go"
|
|
19688
|
+
},
|
|
19689
|
+
// Permanent free tiers (docs/specs/free-models-in-auto): the last resort
|
|
19690
|
+
// when every subscription and pay-per-use key is out (Erick's rule, a
|
|
19691
|
+
// model or billing error never stops the user). Off by default
|
|
19692
|
+
// (engineTiers.includeFree); never used for client-facing bots.
|
|
19693
|
+
{
|
|
19694
|
+
id: "openrouterFree",
|
|
19695
|
+
kind: "apiKey",
|
|
19696
|
+
label: "OpenRouter (free models)",
|
|
19697
|
+
billing: "free",
|
|
19698
|
+
driver: "openai-compat",
|
|
19699
|
+
envVar: "OPENROUTER_FREE_API_KEY",
|
|
19700
|
+
icon: "openrouter",
|
|
19701
|
+
config: { url: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_FREE_API_KEY", model: "", provider: "" },
|
|
19702
|
+
keyTest: { provider: "openaiCompat", url: "https://openrouter.ai/api/v1" },
|
|
19703
|
+
keyUrl: "https://openrouter.ai/keys",
|
|
19704
|
+
signupUrl: "https://openrouter.ai/models?max_price=0"
|
|
19705
|
+
},
|
|
19706
|
+
{
|
|
19707
|
+
id: "googleAiStudioFree",
|
|
19708
|
+
kind: "apiKey",
|
|
19709
|
+
label: "Google AI Studio (free tier)",
|
|
19710
|
+
billing: "free",
|
|
19711
|
+
driver: "openai-compat",
|
|
19712
|
+
envVar: "GOOGLE_AI_STUDIO_FREE_API_KEY",
|
|
19713
|
+
icon: "google",
|
|
19714
|
+
config: { url: "https://generativelanguage.googleapis.com/v1beta/openai", apiKeyEnv: "GOOGLE_AI_STUDIO_FREE_API_KEY", model: "", provider: "" },
|
|
19715
|
+
keyTest: { provider: "openaiCompat", url: "https://generativelanguage.googleapis.com/v1beta/openai" },
|
|
19716
|
+
keyUrl: "https://aistudio.google.com/apikey"
|
|
19717
|
+
},
|
|
19718
|
+
{
|
|
19719
|
+
id: "groqFree",
|
|
19720
|
+
kind: "apiKey",
|
|
19721
|
+
label: "Groq (free tier)",
|
|
19722
|
+
billing: "free",
|
|
19723
|
+
driver: "openai-compat",
|
|
19724
|
+
envVar: "GROQ_FREE_API_KEY",
|
|
19725
|
+
config: { url: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_FREE_API_KEY", model: "", provider: "" },
|
|
19726
|
+
keyTest: { provider: "openaiCompat", url: "https://api.groq.com/openai/v1" },
|
|
19727
|
+
keyUrl: "https://console.groq.com/keys"
|
|
19728
|
+
},
|
|
19729
|
+
{
|
|
19730
|
+
id: "cerebrasFree",
|
|
19731
|
+
kind: "apiKey",
|
|
19732
|
+
label: "Cerebras (free tier)",
|
|
19733
|
+
billing: "free",
|
|
19734
|
+
driver: "openai-compat",
|
|
19735
|
+
envVar: "CEREBRAS_FREE_API_KEY",
|
|
19736
|
+
config: { url: "https://api.cerebras.ai/v1", apiKeyEnv: "CEREBRAS_FREE_API_KEY", model: "", provider: "" },
|
|
19737
|
+
keyTest: { provider: "openaiCompat", url: "https://api.cerebras.ai/v1" },
|
|
19738
|
+
keyUrl: "https://cloud.cerebras.ai/platform/apikeys"
|
|
19739
|
+
},
|
|
19740
|
+
{
|
|
19741
|
+
id: "nvidiaFree",
|
|
19742
|
+
kind: "apiKey",
|
|
19743
|
+
label: "NVIDIA NIM (free tier)",
|
|
19744
|
+
billing: "free",
|
|
19745
|
+
driver: "openai-compat",
|
|
19746
|
+
envVar: "NVIDIA_FREE_API_KEY",
|
|
19747
|
+
config: { url: "https://integrate.api.nvidia.com/v1", apiKeyEnv: "NVIDIA_FREE_API_KEY", model: "", provider: "" },
|
|
19748
|
+
keyTest: { provider: "openaiCompat", url: "https://integrate.api.nvidia.com/v1" },
|
|
19749
|
+
keyUrl: "https://build.nvidia.com/settings/api-keys"
|
|
19688
19750
|
}
|
|
19689
19751
|
];
|
|
19690
19752
|
}
|
|
@@ -30117,8 +30179,8 @@ var init_claude = __esm({
|
|
|
30117
30179
|
DEFAULT_AUTOCOMPACT_TOKENS = 2e5;
|
|
30118
30180
|
CLAUDE_FLAG_FLOORS = {
|
|
30119
30181
|
"--strict-mcp-config": [1, 0, 60],
|
|
30120
|
-
"--setting-sources": [1, 0,
|
|
30121
|
-
"--autocompact": [2, 1,
|
|
30182
|
+
"--setting-sources": [1, 0, 125],
|
|
30183
|
+
"--autocompact": [2, 1, 221],
|
|
30122
30184
|
// 2.1.267 is the first CLI that accepts it; below that the recorded prompt
|
|
30123
30185
|
// simply is not refreshed, which is the pre-existing behaviour.
|
|
30124
30186
|
"--system-prompt-snapshot": [2, 1, 267],
|
|
@@ -10,7 +10,7 @@ export const CHIBI_PROFILE = {
|
|
|
10
10
|
name: CHIBI_NAME,
|
|
11
11
|
title: "Orchestrator",
|
|
12
12
|
description: "Atomic's built-in orchestrator. Plans the work and delegates it to the team.",
|
|
13
|
-
soul: "You are Chibi, Chibitek's orchestrator in Atomic. You plan the work, break it into tasks, and delegate: coding goes to the Claude and Codex bots, research to Grok. You use Mochii (the Mochii MCP server) to create, update and track tasks, and Nexus for company context. You do not write code yourself when a teammate can. Keep the person informed in plain language, confirm before anything irreversible, and call work done only after it has been verified.",
|
|
13
|
+
soul: "You are Chibi, Chibitek's orchestrator in Atomic. You plan the work, break it into tasks, and delegate: coding goes to the Claude and Codex bots, research to Grok. You use Mochii (the Mochii MCP server) to create, update and track tasks, and Nexus for company context. You do not write code yourself when a teammate can. Keep the person informed in plain language, confirm before anything irreversible, and call work done only after it has been verified. Every new feature the person asks for is logged as a Mochii task with the full requirements before or as it is handed to a teammate, and you send the person the task link. When the person approves an addition to an existing feature, add it to that feature's task as a comment.",
|
|
14
14
|
color: CHIEF_OF_STAFF_COLOR,
|
|
15
15
|
};
|
|
16
16
|
export const BUILTIN_CHIBI_REFUSALS = {
|
|
@@ -84,6 +84,14 @@ export function checkCleanup(input, output) {
|
|
|
84
84
|
if (tokens.filter((t) => kept.has(t)).length / tokens.length < 0.6)
|
|
85
85
|
return { ok: false, reason: "overlap" };
|
|
86
86
|
}
|
|
87
|
+
// a correction swaps a misheard word for the right one; words with nothing
|
|
88
|
+
// taken out for them were never said
|
|
89
|
+
const spoken = new Set(contentTokens(input));
|
|
90
|
+
const written = new Set(contentTokens(cleaned));
|
|
91
|
+
const added = [...written].filter((t) => !spoken.has(t)).length;
|
|
92
|
+
const removed = [...spoken].filter((t) => !written.has(t)).length;
|
|
93
|
+
if (added > removed + 1)
|
|
94
|
+
return { ok: false, reason: "added" };
|
|
87
95
|
return { ok: true, text: cleaned };
|
|
88
96
|
}
|
|
89
97
|
export async function ask(provider, prompt, timeoutMs) {
|
|
@@ -259,12 +259,12 @@ const DEFAULT_AUTOCOMPACT_TOKENS = 200_000;
|
|
|
259
259
|
*
|
|
260
260
|
* Verified against the published binaries, not the changelog (which never
|
|
261
261
|
* records `--autocompact`): `--strict-mcp-config` is present in 1.0.60 and
|
|
262
|
-
* absent from 1.0.0; `--setting-sources` first appears in 1.0.
|
|
263
|
-
* lacks it); `--autocompact` first appears in 2.1.
|
|
262
|
+
* absent from 1.0.0; `--setting-sources` first appears in 1.0.125 (1.0.124
|
|
263
|
+
* lacks it); `--autocompact` first appears in 2.1.221 (2.1.220 lacks it). */
|
|
264
264
|
export const CLAUDE_FLAG_FLOORS = {
|
|
265
265
|
"--strict-mcp-config": [1, 0, 60],
|
|
266
|
-
"--setting-sources": [1, 0,
|
|
267
|
-
"--autocompact": [2, 1,
|
|
266
|
+
"--setting-sources": [1, 0, 125],
|
|
267
|
+
"--autocompact": [2, 1, 221],
|
|
268
268
|
// 2.1.267 is the first CLI that accepts it; below that the recorded prompt
|
|
269
269
|
// simply is not refreshed, which is the pre-existing behaviour.
|
|
270
270
|
"--system-prompt-snapshot": [2, 1, 267],
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The Settings view of engine tiers (docs/chibitek/engine-tiers.md): every
|
|
2
|
+
// tier's ladder with health and rest, the installed engines, rest history,
|
|
3
|
+
// pay-per-use episodes and the "Include free models in Auto" setting.
|
|
4
|
+
import { BOT_TIERS, DEFAULT_LADDERS, DEFAULT_TIER_CLASSES } from "../shared/engine-tiers.js";
|
|
5
|
+
import { engineHealth, engineTierClasses, engineTierLadders, engineTiersIncludeFree, keyBased, matchesEngine } from "./engine-tiers.js";
|
|
6
|
+
import { perTokenModel } from "./harness-import/engine-policy.js";
|
|
7
|
+
const DRIVER_NAMES = {
|
|
8
|
+
"atomic-engine": "Atomic Engine", claudeAgent: "Claude", codex: "Codex", cursorAgent: "Cursor", kimiAgent: "Kimi",
|
|
9
|
+
grokAgent: "Grok", vibeAgent: "Mistral Vibe", antigravityAgent: "Gemini (Antigravity)", qwenAgent: "Qwen (local)", opencodeGo: "OpenCode",
|
|
10
|
+
hermesAgent: "Hermes", ollamaCloud: "Ollama Cloud", mistral: "Mistral", "openai-compat": "OpenAI-compatible endpoint",
|
|
11
|
+
appleIntelligence: "Apple Intelligence (on this Mac)",
|
|
12
|
+
};
|
|
13
|
+
/** The Settings view: every tier's ladder with each rung's health and rest,
|
|
14
|
+
* the installed engines a person may add, and the recent rest history. */
|
|
15
|
+
export function engineTiersView(engineTiers, candidates, rests, defaults = DEFAULT_LADDERS) {
|
|
16
|
+
const ladders = engineTierLadders(engineTiers, defaults);
|
|
17
|
+
const classes = engineTierClasses(engineTiers);
|
|
18
|
+
const restOf = (candidate) => {
|
|
19
|
+
const rest = rests?.rest(candidate);
|
|
20
|
+
return rest ? { rest } : {};
|
|
21
|
+
};
|
|
22
|
+
const engines = candidates.filter((candidate) => candidate.driverKind !== "boxAgent").map((candidate) => ({
|
|
23
|
+
instanceId: candidate.instanceId, driverKind: candidate.driverKind, label: candidate.displayName,
|
|
24
|
+
health: engineHealth(candidate), paid: keyBased(candidate),
|
|
25
|
+
models: [...new Map([candidate.models.default, ...candidate.models.options.map((option) => option.id)].filter(Boolean).map((id) => {
|
|
26
|
+
const option = candidate.models.options.find((item) => item.id === id);
|
|
27
|
+
return [id, { id, label: option?.label || id, paid: perTokenModel(candidate, id) }];
|
|
28
|
+
})).values()],
|
|
29
|
+
...(candidate.models.default ? { defaultModel: candidate.models.default } : {}), ...restOf(candidate),
|
|
30
|
+
}));
|
|
31
|
+
const rung = (entry) => {
|
|
32
|
+
const matching = candidates.filter((candidate) => matchesEngine(entry, candidate));
|
|
33
|
+
const best = matching.find((candidate) => engineHealth(candidate) === "ready" && !rests?.rest(candidate)) ??
|
|
34
|
+
matching.find((candidate) => engineHealth(candidate) === "ready") ?? matching[0];
|
|
35
|
+
const name = best?.displayName ?? DRIVER_NAMES[entry.engine] ?? entry.engine;
|
|
36
|
+
return { ...entry, label: entry.model ? `${name} (${entry.model})` : name, ...(best ? { instanceId: best.instanceId } : {}),
|
|
37
|
+
health: best ? engineHealth(best) : "unavailable", paid: best ? keyBased(best) : false, ...(best ? restOf(best) : {}) };
|
|
38
|
+
};
|
|
39
|
+
const label = (instanceId) => candidates.find((candidate) => candidate.instanceId === instanceId)?.displayName ?? instanceId;
|
|
40
|
+
return {
|
|
41
|
+
tiers: BOT_TIERS.map((tier) => ({ tier, modelClass: classes[tier], classCustom: classes[tier] !== DEFAULT_TIER_CLASSES[tier],
|
|
42
|
+
ladder: ladders[tier].map(rung), custom: JSON.stringify(ladders[tier]) !== JSON.stringify(defaults[tier]) })),
|
|
43
|
+
engines,
|
|
44
|
+
...(engineTiersIncludeFree(engineTiers) ? { includeFree: true } : {}),
|
|
45
|
+
...(rests?.payPerUse ? { payPerUse: rests.payPerUse() } : {}),
|
|
46
|
+
...(rests ? { restHistory: rests.history().map((record) => ({ ...record,
|
|
47
|
+
label: record.scope ? `${label(record.instanceId)} (${record.scope})` : label(record.instanceId) })) } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -7,7 +7,7 @@ import { BOT_TIERS, DEFAULT_LADDERS, DEFAULT_TIER_CLASSES, MODEL_CLASSES, classB
|
|
|
7
7
|
import { modelHost } from "../shared/engine-choices.js";
|
|
8
8
|
export { DEFAULT_LADDERS };
|
|
9
9
|
import { subscriptionProviderForDriver } from "../shared/subscription-bots.js";
|
|
10
|
-
import { ATOMIC_ENGINE_INSTANCE, engineIntent, localModel, modelBilling, modelProvider, paidCandidate, perTokenModel } from "./harness-import/engine-policy.js";
|
|
10
|
+
import { ATOMIC_ENGINE_INSTANCE, engineIntent, freeCandidate, localModel, modelBilling, modelProvider, paidCandidate, perTokenModel } from "./harness-import/engine-policy.js";
|
|
11
11
|
/** Engines a tier tries first, whatever the class catalog order says: Chibi
|
|
12
12
|
* runs on Claude (Erick, 2026-10-13: open-weight models broke its
|
|
13
13
|
* orchestration). Its ladder starts with Claude's best model graded in its
|
|
@@ -169,6 +169,13 @@ export function engineTierClasses(engineTiers) {
|
|
|
169
169
|
const classes = stored && typeof stored === "object" && !Array.isArray(stored) ? stored : {};
|
|
170
170
|
return Object.fromEntries(BOT_TIERS.map((tier) => [tier, toModelClass(classes[tier]) ?? DEFAULT_TIER_CLASSES[tier]]));
|
|
171
171
|
}
|
|
172
|
+
/** A bot counts as handling client data unless switched off (WireBot.clientData),
|
|
173
|
+
* so a free-tier model is never the default for one. */
|
|
174
|
+
export const handlesClientData = (bot) => bot.clientData !== false;
|
|
175
|
+
/** "Include free models in Auto" (config.json engineTiers.includeFree): off by default. */
|
|
176
|
+
export function engineTiersIncludeFree(engineTiers) {
|
|
177
|
+
return configObject(engineTiers).includeFree === true;
|
|
178
|
+
}
|
|
172
179
|
function storedClasses(engineTiers) {
|
|
173
180
|
const current = engineTierClasses(engineTiers);
|
|
174
181
|
return Object.fromEntries(BOT_TIERS.filter((tier) => current[tier] !== DEFAULT_TIER_CLASSES[tier]).map((tier) => [tier, current[tier]]));
|
|
@@ -224,7 +231,7 @@ export function engineHealth(candidate) {
|
|
|
224
231
|
return "signed-out";
|
|
225
232
|
return candidate.models.default || candidate.models.options.length ? "ready" : "unavailable";
|
|
226
233
|
}
|
|
227
|
-
const matchesEngine = (entry, candidate) => entry.engine === candidate.instanceId || entry.engine === candidate.driverKind;
|
|
234
|
+
export const matchesEngine = (entry, candidate) => entry.engine === candidate.instanceId || entry.engine === candidate.driverKind;
|
|
228
235
|
/** The model a rung runs on this engine: its exact model id, else the first
|
|
229
236
|
* id containing it, else (no model named) the engine's default. Where several
|
|
230
237
|
* ids contain it (one model offered by many providers on Hermes), one that is
|
|
@@ -274,10 +281,11 @@ export function resolveLadder(ladder, stored, context) {
|
|
|
274
281
|
const skipped = [];
|
|
275
282
|
const subscription = [];
|
|
276
283
|
const key = [];
|
|
284
|
+
const free = [];
|
|
277
285
|
// The engine the bot is already on goes first among equals.
|
|
278
286
|
const storedFirst = (a, b) => Number(b.instanceId === stored.instanceId) - Number(a.instanceId === stored.instanceId);
|
|
279
287
|
for (const [step, entry] of ladder.entries()) {
|
|
280
|
-
const matching = context.candidates.filter((candidate) => !keyBased(candidate) && matchesEngine(entry, candidate));
|
|
288
|
+
const matching = context.candidates.filter((candidate) => !keyBased(candidate) && !freeCandidate(candidate) && matchesEngine(entry, candidate));
|
|
281
289
|
for (const candidate of matching.sort(storedFirst)) {
|
|
282
290
|
const band = perTokenModel(candidate, entryModel(entry, candidate)) ? "key" : "subscription";
|
|
283
291
|
(band === "key" ? key : subscription).push({ step, entry, candidate, band, index: context.candidates.indexOf(candidate) });
|
|
@@ -293,6 +301,14 @@ export function resolveLadder(ladder, stored, context) {
|
|
|
293
301
|
if (candidate.driverKind === "ollamaCloud" && !keyBased(candidate) && !subscription.some((attempt) => attempt.candidate === candidate)) {
|
|
294
302
|
subscription.push({ step: ladder.length, entry: { engine: candidate.instanceId }, candidate, band: "subscription", index });
|
|
295
303
|
}
|
|
304
|
+
// A free-tier account joins every ladder by itself, in its own
|
|
305
|
+
// last-resort band (gated by includeFree/clientData below): no rung
|
|
306
|
+
// ever names one, same as how a key-based engine joins automatically.
|
|
307
|
+
if (freeCandidate(candidate)) {
|
|
308
|
+
const step = rungOf(candidate);
|
|
309
|
+
free.push({ step, entry: ladder[step] ?? { engine: candidate.instanceId }, candidate, band: "free", index });
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
296
312
|
if (!keyBased(candidate) || candidate.driverKind === "boxAgent")
|
|
297
313
|
continue;
|
|
298
314
|
const step = rungOf(candidate);
|
|
@@ -300,8 +316,12 @@ export function resolveLadder(ladder, stored, context) {
|
|
|
300
316
|
}
|
|
301
317
|
subscription.sort((a, b) => atomicFirst(a.candidate) - atomicFirst(b.candidate) || a.step - b.step);
|
|
302
318
|
key.sort((a, b) => a.step - b.step || storedFirst(a.candidate, b.candidate) || a.index - b.index);
|
|
303
|
-
|
|
304
|
-
|
|
319
|
+
free.sort((a, b) => a.step - b.step || storedFirst(a.candidate, b.candidate) || a.index - b.index);
|
|
320
|
+
for (const { step, entry, candidate, band } of [...subscription, ...key, ...free]) {
|
|
321
|
+
const reason = band === "key" && context.keyBandPaused ? "paused"
|
|
322
|
+
: band === "free" && !context.includeFree ? "free-off"
|
|
323
|
+
: band === "free" && context.clientData ? "client-data"
|
|
324
|
+
: skipReason(entry, candidate, context, step < ladder.length);
|
|
305
325
|
if (reason) {
|
|
306
326
|
skipped.push({ step, instanceId: candidate.instanceId, reason, band });
|
|
307
327
|
continue;
|
|
@@ -310,7 +330,7 @@ export function resolveLadder(ladder, stored, context) {
|
|
|
310
330
|
const keep = candidate.instanceId === stored.instanceId &&
|
|
311
331
|
(!entry.model || stored.model.toLowerCase().includes(entry.model.toLowerCase())) &&
|
|
312
332
|
(stored.model === candidate.models.default || candidate.models.options.some((option) => option.id === stored.model)) &&
|
|
313
|
-
(band
|
|
333
|
+
(band !== "subscription" || !perTokenModel(candidate, stored.model));
|
|
314
334
|
return { choice: { selection: keep ? { ...stored } : { instanceId: candidate.instanceId, model }, candidate, step, band }, skipped };
|
|
315
335
|
}
|
|
316
336
|
return { skipped };
|
|
@@ -362,7 +382,7 @@ export function resolveFailover(request) {
|
|
|
362
382
|
// a turn falls to any of them, not only to those its ladders name.
|
|
363
383
|
const extras = new Map();
|
|
364
384
|
for (const candidate of request.context.candidates) {
|
|
365
|
-
if (keyBased(candidate) || candidate.driverKind === "boxAgent")
|
|
385
|
+
if (keyBased(candidate) || freeCandidate(candidate) || candidate.driverKind === "boxAgent")
|
|
366
386
|
continue;
|
|
367
387
|
// An engine that runs many providers (Hermes, Atomic Engine) offers each
|
|
368
388
|
// flat plan's models; its key-billed and unlabelled ones are not a plan.
|
|
@@ -390,15 +410,19 @@ export function resolveFailover(request) {
|
|
|
390
410
|
};
|
|
391
411
|
const ladders = new Map(classes.map((modelClass) => [modelClass, ladderOf(modelClass)]));
|
|
392
412
|
const all = [];
|
|
393
|
-
let
|
|
394
|
-
for (const
|
|
413
|
+
let lastPassSkipped;
|
|
414
|
+
for (const stage of ["subscription", "key", "free"]) {
|
|
395
415
|
for (const modelClass of classes) {
|
|
396
416
|
const { ladder, vouched } = ladders.get(modelClass);
|
|
397
417
|
const effective = (candidate, model, entry, step) => entry?.modelClass ?? request.classOf(candidate, model) ?? (step < vouched ? modelClass : "Añejo");
|
|
398
418
|
const accept = (candidate, model, entry, named) => {
|
|
399
419
|
if (request.tried.has(candidate.instanceId))
|
|
400
420
|
return "tried";
|
|
401
|
-
|
|
421
|
+
// Free is the last-resort band: deferred past subscriptions and keys
|
|
422
|
+
// in every class, same as a key engine is deferred past subscriptions.
|
|
423
|
+
if (stage !== "free" && freeCandidate(candidate))
|
|
424
|
+
return "key-later";
|
|
425
|
+
if (stage === "subscription" && (keyBased(candidate) || perTokenModel(candidate, model)))
|
|
402
426
|
return "key-later";
|
|
403
427
|
// a ladder-named local model is a normal rung (Erick, 2026-10-24)
|
|
404
428
|
if (!named && request.isLocal?.(candidate, model) && !(request.localAllowed && modelClass === "Añejo"))
|
|
@@ -408,8 +432,8 @@ export function resolveFailover(request) {
|
|
|
408
432
|
};
|
|
409
433
|
const resolution = resolveLadder(ladder, request.failed, { ...request.context, accept });
|
|
410
434
|
all.push(...resolution.skipped);
|
|
411
|
-
if (
|
|
412
|
-
|
|
435
|
+
if (stage === "free" && modelClass === request.modelClass)
|
|
436
|
+
lastPassSkipped = resolution.skipped;
|
|
413
437
|
if (resolution.choice) {
|
|
414
438
|
const { choice } = resolution;
|
|
415
439
|
const named = choice.step < ladder.length;
|
|
@@ -418,7 +442,7 @@ export function resolveFailover(request) {
|
|
|
418
442
|
}
|
|
419
443
|
}
|
|
420
444
|
}
|
|
421
|
-
return { skipped:
|
|
445
|
+
return { skipped: lastPassSkipped ? mergeSkipped([...lastPassSkipped, ...all]) : mergeSkipped(all) };
|
|
422
446
|
}
|
|
423
447
|
/** One skip per engine, the most telling reason kept: a resting, paused or
|
|
424
448
|
* refused engine says more than "not this class" or "a key, later". */
|
|
@@ -441,50 +465,8 @@ export function engineLabel(candidates, selection) {
|
|
|
441
465
|
const model = candidate?.models.options.find((option) => option.id === selection.model)?.label ?? selection.model;
|
|
442
466
|
return model && model !== name ? `${name} · ${model}` : name;
|
|
443
467
|
}
|
|
444
|
-
const DRIVER_NAMES = {
|
|
445
|
-
"atomic-engine": "Atomic Engine", claudeAgent: "Claude", codex: "Codex", cursorAgent: "Cursor", kimiAgent: "Kimi",
|
|
446
|
-
grokAgent: "Grok", vibeAgent: "Mistral Vibe", antigravityAgent: "Gemini (Antigravity)", qwenAgent: "Qwen (local)", opencodeGo: "OpenCode",
|
|
447
|
-
hermesAgent: "Hermes", ollamaCloud: "Ollama Cloud", mistral: "Mistral", "openai-compat": "OpenAI-compatible endpoint",
|
|
448
|
-
appleIntelligence: "Apple Intelligence (on this Mac)",
|
|
449
|
-
};
|
|
450
468
|
/** The account an engine is signed in to, as resting keys it. */
|
|
451
469
|
export const engineAccount = (candidate) => candidate.snapshot.account?.email ?? candidate.snapshot.account?.organization;
|
|
452
|
-
/** The Settings view: every tier's ladder with each rung's health and rest,
|
|
453
|
-
* the installed engines a person may add, and the recent rest history. */
|
|
454
|
-
export function engineTiersView(engineTiers, candidates, rests, defaults = DEFAULT_LADDERS) {
|
|
455
|
-
const ladders = engineTierLadders(engineTiers, defaults);
|
|
456
|
-
const classes = engineTierClasses(engineTiers);
|
|
457
|
-
const restOf = (candidate) => {
|
|
458
|
-
const rest = rests?.rest(candidate);
|
|
459
|
-
return rest ? { rest } : {};
|
|
460
|
-
};
|
|
461
|
-
const engines = candidates.filter((candidate) => candidate.driverKind !== "boxAgent").map((candidate) => ({
|
|
462
|
-
instanceId: candidate.instanceId, driverKind: candidate.driverKind, label: candidate.displayName,
|
|
463
|
-
health: engineHealth(candidate), paid: keyBased(candidate),
|
|
464
|
-
models: [...new Map([candidate.models.default, ...candidate.models.options.map((option) => option.id)].filter(Boolean).map((id) => {
|
|
465
|
-
const option = candidate.models.options.find((item) => item.id === id);
|
|
466
|
-
return [id, { id, label: option?.label || id, paid: perTokenModel(candidate, id) }];
|
|
467
|
-
})).values()],
|
|
468
|
-
...(candidate.models.default ? { defaultModel: candidate.models.default } : {}), ...restOf(candidate),
|
|
469
|
-
}));
|
|
470
|
-
const rung = (entry) => {
|
|
471
|
-
const matching = candidates.filter((candidate) => matchesEngine(entry, candidate));
|
|
472
|
-
const best = matching.find((candidate) => engineHealth(candidate) === "ready" && !rests?.rest(candidate)) ??
|
|
473
|
-
matching.find((candidate) => engineHealth(candidate) === "ready") ?? matching[0];
|
|
474
|
-
const name = best?.displayName ?? DRIVER_NAMES[entry.engine] ?? entry.engine;
|
|
475
|
-
return { ...entry, label: entry.model ? `${name} (${entry.model})` : name, ...(best ? { instanceId: best.instanceId } : {}),
|
|
476
|
-
health: best ? engineHealth(best) : "unavailable", paid: best ? keyBased(best) : false, ...(best ? restOf(best) : {}) };
|
|
477
|
-
};
|
|
478
|
-
const label = (instanceId) => candidates.find((candidate) => candidate.instanceId === instanceId)?.displayName ?? instanceId;
|
|
479
|
-
return {
|
|
480
|
-
tiers: BOT_TIERS.map((tier) => ({ tier, modelClass: classes[tier], classCustom: classes[tier] !== DEFAULT_TIER_CLASSES[tier],
|
|
481
|
-
ladder: ladders[tier].map(rung), custom: JSON.stringify(ladders[tier]) !== JSON.stringify(defaults[tier]) })),
|
|
482
|
-
engines,
|
|
483
|
-
...(rests?.payPerUse ? { payPerUse: rests.payPerUse() } : {}),
|
|
484
|
-
...(rests ? { restHistory: rests.history().map((record) => ({ ...record,
|
|
485
|
-
label: record.scope ? `${label(record.instanceId)} (${record.scope})` : label(record.instanceId) })) } : {}),
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
470
|
/** One bot's rebalance preview row. `refuse` carries the same per-bot
|
|
489
471
|
* refusals a turn would apply (policy, approval level). */
|
|
490
472
|
export function rebalanceRow(bot, engineTiers, candidates, refuse, defaults = DEFAULT_LADDERS) {
|
|
@@ -54,7 +54,8 @@ const ready = (candidate) => candidate.snapshot.state === "available" && candida
|
|
|
54
54
|
* Plan, is a subscription); Ollama Cloud, a flat plan used through a key;
|
|
55
55
|
* else a metered snapshot, a per-token driver or a sign-in with an API key.
|
|
56
56
|
* An engine Atomic cannot tell about (Cursor, Antigravity) is not labelled;
|
|
57
|
-
* it still ranks after the subscription list.
|
|
57
|
+
* it still ranks after the subscription list. A free-tier account
|
|
58
|
+
* (billing: "free") is never pay per token. */
|
|
58
59
|
export const paidCandidate = (candidate) => {
|
|
59
60
|
if (candidate.billing)
|
|
60
61
|
return candidate.billing === "payPerUse";
|
|
@@ -63,6 +64,10 @@ export const paidCandidate = (candidate) => {
|
|
|
63
64
|
return candidate.snapshot.billing === "metered" || PER_TOKEN_KINDS.has(candidate.driverKind) ||
|
|
64
65
|
candidate.snapshot.account?.method === "api-key";
|
|
65
66
|
};
|
|
67
|
+
/** A provider account added as a permanent free tier (shared/
|
|
68
|
+
* provider-accounts.ts, billing: "free"): the last-resort band in
|
|
69
|
+
* resolveLadder, never the subscription or key band. */
|
|
70
|
+
export const freeCandidate = (candidate) => candidate.billing === "free";
|
|
66
71
|
// ── billing per model, on engines that run many providers ────────────────
|
|
67
72
|
// Hermes, and Atomic Engine (built on it), run models from every provider
|
|
68
73
|
// they are configured for, as `<provider>:<model>` ("deepinfra:XiaomiMiMo/
|
|
@@ -145,7 +145,7 @@ import { buildRecoveryText, buildTurnContext, engineIsFresh, NATIVELY_REPLAYING_
|
|
|
145
145
|
import { Handoffs, handedStateUsable, recordHanded, renderUnseen, sessionStart, unseenMessages, withUnseenMessages } from "./delta-context.js";
|
|
146
146
|
import { extractTurnImages } from "./turn-images.js";
|
|
147
147
|
import { TurnWatchdog } from "./turn-watchdog.js";
|
|
148
|
-
import { TurnResources, workspaceResource } from "./turn-resources.js";
|
|
148
|
+
import { TurnResources, folderHeldElsewhere, queuedTurnFolder, workspaceResource } from "./turn-resources.js";
|
|
149
149
|
import { ensureWorkspace, ensureTaskWorkspace, workspaceLocationsPrompt, supportsWorkspaceFiles, updateMemory, appendMemoryLog, isMemoryTopicName, loadMemory, memorySystemPrompt, memorySourceLabel, searchMemoryFiles, SESSION_SEARCH_SYSTEM_PROMPT, TASK_WORKSPACES_DIR, WORKSPACES_DIR, workspaceDir, } from "./workspace.js";
|
|
150
150
|
import { fromOwner, NexusMemory, surfaceForRequest } from "./nexus-memory/index.js";
|
|
151
151
|
import { formatNexusFile, formatNexusRecall } from "./nexus-memory/tool-text.js";
|
|
@@ -275,6 +275,7 @@ import { accountStops, injectedModel, restScope } from "./injected-model-rest.js
|
|
|
275
275
|
import { accountProvider } from "../shared/provider-accounts.js";
|
|
276
276
|
import { createAccountRoutes } from "./routes/accounts.js";
|
|
277
277
|
import { installNativeVault } from "./vault/runtime.js";
|
|
278
|
+
import { installWorkspaceSecrets } from "./workspace-backup-secrets.js";
|
|
278
279
|
import { OAuthAccounts, takeOAuthClients } from "./oauth-accounts.js";
|
|
279
280
|
import { DesktopAccountVault, FileAccountVault, UnavailableAccountVault, takeAccountsFromEnvironment } from "./oauth-account-vault.js";
|
|
280
281
|
import { accountConnectorNames } from "./claude-account-connectors.js";
|
|
@@ -289,7 +290,7 @@ import { HISTORY_RECEIPT_KIND, historyAttachmentStore } from "./harness-import/h
|
|
|
289
290
|
import { chooseEngine, engineIntent, harnessForName, localModel, newBotSelection, paidCandidate, perTokenModel } from "./harness-import/engine-policy.js";
|
|
290
291
|
import { createEngineTierRoutes } from "./routes/engine-tiers.js";
|
|
291
292
|
import { createPrimaryBotRoutes } from "./routes/primary-bot.js";
|
|
292
|
-
import { EngineSnapshotCache, botTier, canDriveComputer, classLadder, engineAccount, engineHealth, engineLabel, engineTierClasses, engineTierLadders, generateLadders, ladderForClass, keyBased, needsDelegation, resolveFailover, resolveLadder } from "./engine-tiers.js";
|
|
293
|
+
import { EngineSnapshotCache, botTier, canDriveComputer, classLadder, engineAccount, engineHealth, engineLabel, engineTierClasses, engineTierLadders, engineTiersIncludeFree, generateLadders, handlesClientData, ladderForClass, keyBased, needsDelegation, resolveFailover, resolveLadder } from "./engine-tiers.js";
|
|
293
294
|
import { PRIMARY_BOT_THREAD_TITLE, PRIMARY_BOT_TRIGGER, PrimaryBotStore, routesAsSweep, PrimaryBotSweeper } from "./primary-bot.js";
|
|
294
295
|
import { PRIMARY_BOT_LOCKED, primaryBotChoiceField } from "../shared/primary-bot.js";
|
|
295
296
|
import { moveIntoChibi } from "./move-into-chibi.js";
|
|
@@ -381,13 +382,10 @@ function releaseDataDirLeaseAtExit() {
|
|
|
381
382
|
process.once("exit", releaseDataDirLeaseAtExit);
|
|
382
383
|
// Restore before constructing any long-lived config, Store, session or provider
|
|
383
384
|
// objects. Replacing files underneath a live Store would overwrite restored data.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
workspaceRestore = readLastWorkspaceRestore(DATA_DIR) ?? workspaceRestore;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
385
|
+
const restoredThisBoot = existsSync(join(DATA_DIR, ".backups")) ? applyPendingWorkspaceRestore(DATA_DIR) : { restored: false };
|
|
386
|
+
let workspaceRestore = restoredThisBoot;
|
|
387
|
+
if (existsSync(join(DATA_DIR, ".backups")) && !workspaceRestore.restored && !workspaceRestore.rolledBack)
|
|
388
|
+
workspaceRestore = readLastWorkspaceRestore(DATA_DIR) ?? workspaceRestore;
|
|
391
389
|
const workspaceMaintenance = new WorkspaceBackupMaintenance();
|
|
392
390
|
// Only after ensureDirs(): it performs the one-time rename of the legacy data
|
|
393
391
|
// dir, which must not find a freshly created ~/.openmausbot already there.
|
|
@@ -1176,6 +1174,7 @@ const availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(join(DAT
|
|
|
1176
1174
|
// supplies parentPort; plain Node intentionally leaves it absent.
|
|
1177
1175
|
const utilityParentPort = process.parentPort;
|
|
1178
1176
|
installNativeVault(DATA_DIR, utilityParentPort, () => desktopMutationToken, req => authorizedInternalCapability(req.headers.authorization) !== null);
|
|
1177
|
+
installWorkspaceSecrets(utilityParentPort, DATA_DIR, restoredThisBoot);
|
|
1179
1178
|
function postDesktopPrivateMessage(message) {
|
|
1180
1179
|
if (!utilityParentPort)
|
|
1181
1180
|
return false;
|
|
@@ -3103,7 +3102,7 @@ function ladderTurnEngineChoice(profile, projected, threadId, needs, runOn, subs
|
|
|
3103
3102
|
const { ladder } = plan;
|
|
3104
3103
|
const computer = turnComputer(projected, threadId, runOn);
|
|
3105
3104
|
const context = { candidates, needsDelegation: needsDelegation(tier, profile), computer, refuse: engineTierRefusal(projected), resting: engineResting,
|
|
3106
|
-
keyBandPaused: subscriptionsOnly || payPerUse.pausedUntil() !== undefined };
|
|
3105
|
+
keyBandPaused: subscriptionsOnly || payPerUse.pausedUntil() !== undefined, includeFree: engineTiersIncludeFree(cfg.engineTiers), clientData: handlesClientData(profile) };
|
|
3107
3106
|
const local = (resolution) => localModeTurn(resolution, { tier, context, needs, prefer: [projected.modelSelection.instanceId] });
|
|
3108
3107
|
const wider = (tried, modelClass) => resolveFailover({
|
|
3109
3108
|
ladder, classLadder: (cls) => classLadder(classedModels(), cls), failed: projected.modelSelection, context, tried,
|
|
@@ -3876,7 +3875,7 @@ function failoverPick(turn, profile, projected, candidates, failedCandidate, res
|
|
|
3876
3875
|
classLadder: (modelClass) => classLadder(classedModels(), modelClass),
|
|
3877
3876
|
failed: attempt.selection,
|
|
3878
3877
|
context: { candidates, needsDelegation: needsDelegation(tier, profile), computer, refuse: engineTierRefusal(projected), resting: engineResting,
|
|
3879
|
-
keyBandPaused: Boolean(fallback) || keyBandPausedFor(turn.threadId) },
|
|
3878
|
+
keyBandPaused: Boolean(fallback) || keyBandPausedFor(turn.threadId), includeFree: engineTiersIncludeFree(cfg.engineTiers), clientData: handlesClientData(profile) },
|
|
3880
3879
|
tried,
|
|
3881
3880
|
modelClass: neededClass,
|
|
3882
3881
|
mayDrop: true,
|
|
@@ -5347,7 +5346,7 @@ const roomHandoffs = new RoomHandoffs(join(DATA_DIR, "room-handoffs.json"), {
|
|
|
5347
5346
|
busy: n => {
|
|
5348
5347
|
if (n.groupId)
|
|
5349
5348
|
return Boolean(store.bot(n.botId)?.busy || (store.group(n.groupId) && groupIsWorking(store.group(n.groupId))));
|
|
5350
|
-
const slot = threadBusy(n.botId, n.threadId) || botAtThreadCapacity(n.botId) || Boolean(activeGroupTurnForBot(n.botId));
|
|
5349
|
+
const slot = threadBusy(n.botId, n.threadId) || botAtThreadCapacity(n.botId) || Boolean(activeGroupTurnForBot(n.botId)) || folderHeldElsewhere(turnResources, queuedTurnFolder(store.taskByThread(n.botId, n.threadId)?.cwd, repoThreadStarted(n.threadId), turnRepoPlan(n.threadId), store.bot(n.botId)?.cwd), n.threadId);
|
|
5351
5350
|
return n.status === "resume" ? slot : slot || recipientAwaitingPerson(n.botId, n.threadId);
|
|
5352
5351
|
},
|
|
5353
5352
|
changed: (groupIds, directThreadIds) => {
|
|
@@ -21004,7 +21003,7 @@ const handleRequest = async (req, res) => {
|
|
|
21004
21003
|
section = trimmed;
|
|
21005
21004
|
}
|
|
21006
21005
|
}
|
|
21007
|
-
for (const key of ["unread", "cloudBackend", "color", "mascotExpression", "mascotBody", "pinned", "hidden"]) {
|
|
21006
|
+
for (const key of ["unread", "cloudBackend", "color", "mascotExpression", "mascotBody", "pinned", "hidden", "clientData"]) {
|
|
21008
21007
|
if (body[key] !== undefined)
|
|
21009
21008
|
patch[key] = body[key];
|
|
21010
21009
|
}
|