@ainyc/canonry 4.97.0 → 4.99.0
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/assets/agent-workspace/skills/canonry/references/canonry-cli.md +7 -3
- package/assets/assets/{BacklinksPage-BI9BtHjp.js → BacklinksPage-CuWs7I1D.js} +1 -1
- package/assets/assets/{ChartPrimitives-BcjGunGr.js → ChartPrimitives-bm37tprp.js} +1 -1
- package/assets/assets/{ProjectPage-BdoberYt.js → ProjectPage-BksYe8FP.js} +1 -1
- package/assets/assets/{RunRow-BT2kpdQR.js → RunRow-DzhQl87g.js} +1 -1
- package/assets/assets/{RunsPage-BAgZuX7j.js → RunsPage-wOaewhbF.js} +1 -1
- package/assets/assets/{SettingsPage-BvKcpivM.js → SettingsPage-CJdhhkkE.js} +1 -1
- package/assets/assets/{TrafficPage-BdGOZv1e.js → TrafficPage-BJBeN5Ph.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-C_N2D9xy.js → TrafficSourceDetailPage-D3PmH0B1.js} +1 -1
- package/assets/assets/{arrow-left-C8Fbup8X.js → arrow-left-BmITMI6O.js} +1 -1
- package/assets/assets/{extract-error-message-C29K35Gm.js → extract-error-message-BGwnyAfP.js} +1 -1
- package/assets/assets/{index-BGfyt3LX.js → index-BSX6r6ju.js} +9 -9
- package/assets/assets/{trash-2-DWLIELE_.js → trash-2-Bd2mkP_w.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-NUGGUQH3.js → chunk-CEHD33NQ.js} +326 -29
- package/dist/{chunk-E2FDXNMW.js → chunk-CWFTMFEU.js} +5 -3
- package/dist/{chunk-TXNKKTFD.js → chunk-DJVUNURL.js} +51 -2
- package/dist/{chunk-SRJ5IGNK.js → chunk-VQN74WWA.js} +28 -2
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +16 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-DC2TD7RR.js → intelligence-service-QHBXW4KI.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-BSX6r6ju.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
|
package/assets/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-BSX6r6ju.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-C9EZkgbP.js">
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
loadConfig,
|
|
10
10
|
loadConfigRaw,
|
|
11
11
|
saveConfigPatch
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-VQN74WWA.js";
|
|
13
13
|
import {
|
|
14
14
|
CC_CACHE_DIR,
|
|
15
15
|
DUCKDB_SPEC,
|
|
@@ -111,7 +111,7 @@ import {
|
|
|
111
111
|
siteAuditPages,
|
|
112
112
|
siteAuditSnapshots,
|
|
113
113
|
usageCounters
|
|
114
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-DJVUNURL.js";
|
|
115
115
|
import {
|
|
116
116
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
117
117
|
AGENT_PROVIDER_IDS,
|
|
@@ -171,7 +171,7 @@ import {
|
|
|
171
171
|
validationError,
|
|
172
172
|
winnabilityClassLabel,
|
|
173
173
|
withRetry
|
|
174
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-CWFTMFEU.js";
|
|
175
175
|
|
|
176
176
|
// src/telemetry.ts
|
|
177
177
|
import crypto from "crypto";
|
|
@@ -6402,7 +6402,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
6402
6402
|
return result;
|
|
6403
6403
|
}
|
|
6404
6404
|
async function backfillInsightsCommand(project, opts) {
|
|
6405
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
6405
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-QHBXW4KI.js");
|
|
6406
6406
|
const config = loadConfig();
|
|
6407
6407
|
const db = createClient(config.database);
|
|
6408
6408
|
migrate(db);
|
|
@@ -7988,7 +7988,10 @@ import { Agent } from "@mariozechner/pi-agent-core";
|
|
|
7988
7988
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
7989
7989
|
|
|
7990
7990
|
// src/agent/providers.ts
|
|
7991
|
-
import {
|
|
7991
|
+
import {
|
|
7992
|
+
getEnvApiKey,
|
|
7993
|
+
getModel
|
|
7994
|
+
} from "@mariozechner/pi-ai";
|
|
7992
7995
|
var PROVIDER_MODELS = {
|
|
7993
7996
|
[AgentProviderIds.claude]: {
|
|
7994
7997
|
[LlmCapabilities.agent]: "claude-opus-4-7",
|
|
@@ -8014,6 +8017,14 @@ var PROVIDER_MODELS = {
|
|
|
8014
8017
|
[LlmCapabilities.agent]: "glm-5.1",
|
|
8015
8018
|
[LlmCapabilities.analyze]: "glm-5-turbo",
|
|
8016
8019
|
[LlmCapabilities.classify]: "glm-5-turbo"
|
|
8020
|
+
},
|
|
8021
|
+
[AgentProviderIds.deepinfra]: {
|
|
8022
|
+
// DeepInfra `org/Model` slugs. GLM-5.2 drives the agent loop; the much
|
|
8023
|
+
// cheaper DeepSeek-V4-Flash fills analyze + classify. Resolved into a
|
|
8024
|
+
// custom openai-completions model, not pi-ai's catalog.
|
|
8025
|
+
[LlmCapabilities.agent]: "zai-org/GLM-5.2",
|
|
8026
|
+
[LlmCapabilities.analyze]: "deepseek-ai/DeepSeek-V4-Flash",
|
|
8027
|
+
[LlmCapabilities.classify]: "deepseek-ai/DeepSeek-V4-Flash"
|
|
8017
8028
|
}
|
|
8018
8029
|
};
|
|
8019
8030
|
var AGENT_PROVIDERS = {
|
|
@@ -8040,6 +8051,62 @@ var AGENT_PROVIDERS = {
|
|
|
8040
8051
|
label: "Z.ai (GLM)",
|
|
8041
8052
|
defaultModel: PROVIDER_MODELS[AgentProviderIds.zai][LlmCapabilities.agent],
|
|
8042
8053
|
autoDetectPriority: 3
|
|
8054
|
+
},
|
|
8055
|
+
[AgentProviderIds.deepinfra]: {
|
|
8056
|
+
// `piAiProvider` is the canonical id here (not a pi-ai vendor) — it's the
|
|
8057
|
+
// `model.provider` string the agent loop hands to `getApiKey`, which
|
|
8058
|
+
// `resolveAgentId` maps straight back to the deepinfra config/env key.
|
|
8059
|
+
piAiProvider: AgentProviderIds.deepinfra,
|
|
8060
|
+
label: "DeepInfra (GLM / DeepSeek)",
|
|
8061
|
+
defaultModel: PROVIDER_MODELS[AgentProviderIds.deepinfra][LlmCapabilities.agent],
|
|
8062
|
+
autoDetectPriority: 4,
|
|
8063
|
+
openaiCompatible: {
|
|
8064
|
+
baseUrl: "https://api.deepinfra.com/v1/openai",
|
|
8065
|
+
baseUrlEnvVar: "DEEPINFRA_BASE_URL",
|
|
8066
|
+
apiKeyEnvVar: "DEEPINFRA_TOKEN",
|
|
8067
|
+
// DeepInfra serves open-weight models (GLM, DeepSeek) behind an
|
|
8068
|
+
// OpenAI-compatible vLLM endpoint. pi-ai's `detectCompat` has no rule
|
|
8069
|
+
// for api.deepinfra.com, so it would apply the strict OpenAI profile;
|
|
8070
|
+
// we set the same open-model profile pi-ai uses for deepseek.com /
|
|
8071
|
+
// cerebras / z.ai. `developer` role and `reasoning_effort` are
|
|
8072
|
+
// OpenAI-platform-isms these models reject, `store` is response
|
|
8073
|
+
// persistence DeepInfra doesn't implement, and DeepInfra documents
|
|
8074
|
+
// `max_tokens` (not `max_completion_tokens`) on its OpenAI route.
|
|
8075
|
+
compat: {
|
|
8076
|
+
supportsStore: false,
|
|
8077
|
+
supportsDeveloperRole: false,
|
|
8078
|
+
supportsReasoningEffort: false,
|
|
8079
|
+
maxTokensField: "max_tokens"
|
|
8080
|
+
},
|
|
8081
|
+
// Best-effort metadata. Costs are USD/1M tokens (DeepInfra published
|
|
8082
|
+
// rates: GLM-5.2 ~$0.95 in / $0.18 cached / $3.00 out; DeepSeek-V4-Flash
|
|
8083
|
+
// ~$0.10 in / $0.20 out). contextWindow is DeepInfra's 1M (fp4) serving
|
|
8084
|
+
// window for both models; it's descriptive (see OpenAiCompatibleModelMeta),
|
|
8085
|
+
// so it documents the real window rather than gating compaction.
|
|
8086
|
+
knownModels: {
|
|
8087
|
+
"zai-org/GLM-5.2": {
|
|
8088
|
+
contextWindow: 1048576,
|
|
8089
|
+
maxTokens: 98304,
|
|
8090
|
+
reasoning: true,
|
|
8091
|
+
cost: { input: 0.95, output: 3, cacheRead: 0.18, cacheWrite: 0 }
|
|
8092
|
+
},
|
|
8093
|
+
"deepseek-ai/DeepSeek-V4-Flash": {
|
|
8094
|
+
contextWindow: 1048576,
|
|
8095
|
+
maxTokens: 32768,
|
|
8096
|
+
reasoning: false,
|
|
8097
|
+
cost: { input: 0.1, output: 0.2, cacheRead: 0, cacheWrite: 0 }
|
|
8098
|
+
}
|
|
8099
|
+
},
|
|
8100
|
+
// Fallback for arbitrary `--model` slugs we don't ship as tiers. cost is
|
|
8101
|
+
// 0, so an unknown slug isn't cost-tracked by pi-ai's `calculateCost`;
|
|
8102
|
+
// contextWindow stays conservative since we can't know the slug's window.
|
|
8103
|
+
defaultModelMeta: {
|
|
8104
|
+
contextWindow: 131072,
|
|
8105
|
+
maxTokens: 32768,
|
|
8106
|
+
reasoning: false,
|
|
8107
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8043
8110
|
}
|
|
8044
8111
|
};
|
|
8045
8112
|
function agentProvidersByPriority() {
|
|
@@ -8061,6 +8128,9 @@ function resolveModelForProvider(provider, modelId) {
|
|
|
8061
8128
|
function resolveModelForCapability(provider, capability, modelIdOverride) {
|
|
8062
8129
|
const entry = AGENT_PROVIDERS[provider];
|
|
8063
8130
|
const id = modelIdOverride ?? PROVIDER_MODELS[provider][capability];
|
|
8131
|
+
if (entry.openaiCompatible) {
|
|
8132
|
+
return buildOpenAiCompatibleModel(entry, id);
|
|
8133
|
+
}
|
|
8064
8134
|
const model = getModel(entry.piAiProvider, id);
|
|
8065
8135
|
if (!model) {
|
|
8066
8136
|
throw new Error(
|
|
@@ -8069,6 +8139,27 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
|
|
|
8069
8139
|
}
|
|
8070
8140
|
return model;
|
|
8071
8141
|
}
|
|
8142
|
+
function buildOpenAiCompatibleModel(entry, id) {
|
|
8143
|
+
const host = entry.openaiCompatible;
|
|
8144
|
+
if (!host) {
|
|
8145
|
+
throw new Error(`buildOpenAiCompatibleModel called for non-custom provider '${entry.piAiProvider}'`);
|
|
8146
|
+
}
|
|
8147
|
+
const meta = host.knownModels[id] ?? host.defaultModelMeta;
|
|
8148
|
+
const baseUrl = (host.baseUrlEnvVar ? process.env[host.baseUrlEnvVar] : void 0) || host.baseUrl;
|
|
8149
|
+
return {
|
|
8150
|
+
id,
|
|
8151
|
+
name: id,
|
|
8152
|
+
api: "openai-completions",
|
|
8153
|
+
provider: entry.piAiProvider,
|
|
8154
|
+
baseUrl,
|
|
8155
|
+
reasoning: meta.reasoning,
|
|
8156
|
+
input: ["text"],
|
|
8157
|
+
cost: meta.cost,
|
|
8158
|
+
contextWindow: meta.contextWindow,
|
|
8159
|
+
maxTokens: meta.maxTokens,
|
|
8160
|
+
...host.compat ? { compat: host.compat } : {}
|
|
8161
|
+
};
|
|
8162
|
+
}
|
|
8072
8163
|
function validateAgentProviderRegistry() {
|
|
8073
8164
|
for (const provider of listAgentProviders()) {
|
|
8074
8165
|
for (const capability of LLM_CAPABILITIES) {
|
|
@@ -8092,10 +8183,14 @@ function resolveApiKeySource(providerOrPiAi, config) {
|
|
|
8092
8183
|
const entry = AGENT_PROVIDERS[id];
|
|
8093
8184
|
const fromConfig = config.providers?.[id]?.apiKey;
|
|
8094
8185
|
if (fromConfig) return { key: fromConfig, source: "config" };
|
|
8095
|
-
const fromEnv = getEnvApiKey(entry.piAiProvider);
|
|
8186
|
+
const fromEnv = entry.openaiCompatible ? process.env[entry.openaiCompatible.apiKeyEnvVar] : getEnvApiKey(entry.piAiProvider);
|
|
8096
8187
|
if (fromEnv) return { key: fromEnv, source: "env" };
|
|
8097
8188
|
return void 0;
|
|
8098
8189
|
}
|
|
8190
|
+
function agentProviderApiKeyEnvVar(id) {
|
|
8191
|
+
const entry = AGENT_PROVIDERS[id];
|
|
8192
|
+
return entry.openaiCompatible ? entry.openaiCompatible.apiKeyEnvVar : `${entry.piAiProvider.toUpperCase()}_API_KEY`;
|
|
8193
|
+
}
|
|
8099
8194
|
function resolveAgentId(providerOrPiAi) {
|
|
8100
8195
|
if (isAgentProviderId(providerOrPiAi)) return providerOrPiAi;
|
|
8101
8196
|
for (const id of AGENT_PROVIDER_IDS) {
|
|
@@ -8236,13 +8331,63 @@ function buildSkillDocTools() {
|
|
|
8236
8331
|
// src/agent/mcp-to-agent-tool.ts
|
|
8237
8332
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
8238
8333
|
var MAX_TOOL_RESULT_CHARS = 2e4;
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
return
|
|
8334
|
+
var TRUNCATION_NOTE = "... (truncated \u2014 result too large)";
|
|
8335
|
+
function serializeResult(value) {
|
|
8336
|
+
return JSON.stringify(value, null, 2);
|
|
8337
|
+
}
|
|
8338
|
+
function largestArrayKey(obj) {
|
|
8339
|
+
let best;
|
|
8340
|
+
let bestLen = -1;
|
|
8341
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
8342
|
+
if (!Array.isArray(v)) continue;
|
|
8343
|
+
const len = serializeResult(v).length;
|
|
8344
|
+
if (len > bestLen) {
|
|
8345
|
+
bestLen = len;
|
|
8346
|
+
best = k;
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
return best;
|
|
8350
|
+
}
|
|
8351
|
+
function trimRowsToFit(rows, render) {
|
|
8352
|
+
let kept = rows.slice();
|
|
8353
|
+
while (kept.length > 0 && render(kept).length > MAX_TOOL_RESULT_CHARS) {
|
|
8354
|
+
kept = kept.slice(0, -1);
|
|
8355
|
+
}
|
|
8356
|
+
return kept;
|
|
8357
|
+
}
|
|
8358
|
+
function truncateToolResult(details) {
|
|
8359
|
+
const full = serializeResult(details);
|
|
8360
|
+
if (full.length <= MAX_TOOL_RESULT_CHARS) return full;
|
|
8361
|
+
if (Array.isArray(details)) {
|
|
8362
|
+
const kept = trimRowsToFit(
|
|
8363
|
+
details,
|
|
8364
|
+
(rows) => serializeResult({ items: rows, __truncated: true, __omittedRows: details.length - rows.length })
|
|
8365
|
+
);
|
|
8366
|
+
return serializeResult({ items: kept, __truncated: true, __omittedRows: details.length - kept.length });
|
|
8367
|
+
}
|
|
8368
|
+
if (details && typeof details === "object") {
|
|
8369
|
+
const obj = details;
|
|
8370
|
+
const arrayKey = largestArrayKey(obj);
|
|
8371
|
+
if (arrayKey) {
|
|
8372
|
+
const rows = obj[arrayKey];
|
|
8373
|
+
const kept = trimRowsToFit(
|
|
8374
|
+
rows,
|
|
8375
|
+
(r) => serializeResult({ ...obj, [arrayKey]: r, __truncated: true, __omittedRows: rows.length - r.length })
|
|
8376
|
+
);
|
|
8377
|
+
const out = serializeResult({
|
|
8378
|
+
...obj,
|
|
8379
|
+
[arrayKey]: kept,
|
|
8380
|
+
__truncated: true,
|
|
8381
|
+
__omittedRows: rows.length - kept.length
|
|
8382
|
+
});
|
|
8383
|
+
if (out.length <= MAX_TOOL_RESULT_CHARS) return out;
|
|
8384
|
+
}
|
|
8385
|
+
}
|
|
8386
|
+
return full.slice(0, MAX_TOOL_RESULT_CHARS) + "\n" + TRUNCATION_NOTE;
|
|
8242
8387
|
}
|
|
8243
8388
|
function textResult2(details) {
|
|
8244
8389
|
return {
|
|
8245
|
-
content: [{ type: "text", text:
|
|
8390
|
+
content: [{ type: "text", text: truncateToolResult(details) }],
|
|
8246
8391
|
details
|
|
8247
8392
|
};
|
|
8248
8393
|
}
|
|
@@ -8309,17 +8454,35 @@ function loadAeroSystemPrompt(pkgDir) {
|
|
|
8309
8454
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
8310
8455
|
const skillBody = fs7.readFileSync(path8.join(skillDir, "SKILL.md"), "utf-8");
|
|
8311
8456
|
const soulPath = path8.join(skillDir, "soul.md");
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8457
|
+
const base = fs7.existsSync(soulPath) ? `${fs7.readFileSync(soulPath, "utf-8").trimEnd()}
|
|
8458
|
+
|
|
8459
|
+
---
|
|
8460
|
+
|
|
8461
|
+
${skillBody}` : skillBody;
|
|
8462
|
+
return appendSystemPromptExtras(base);
|
|
8463
|
+
}
|
|
8464
|
+
function appendSystemPromptExtras(base, env = process.env) {
|
|
8465
|
+
const inline = env.AERO_SYSTEM_PROMPT_APPEND?.trim();
|
|
8466
|
+
let fileBody = "";
|
|
8467
|
+
const filePath = env.AERO_SYSTEM_PROMPT_FILE?.trim();
|
|
8468
|
+
if (filePath) {
|
|
8469
|
+
try {
|
|
8470
|
+
fileBody = fs7.readFileSync(filePath, "utf-8").trim();
|
|
8471
|
+
} catch {
|
|
8472
|
+
fileBody = "";
|
|
8473
|
+
}
|
|
8474
|
+
}
|
|
8475
|
+
const extras = [inline, fileBody].filter((s) => !!s && s.length > 0);
|
|
8476
|
+
if (extras.length === 0) return base;
|
|
8477
|
+
return `${base.trimEnd()}
|
|
8315
8478
|
|
|
8316
8479
|
---
|
|
8317
8480
|
|
|
8318
|
-
${
|
|
8481
|
+
${extras.join("\n\n")}`;
|
|
8319
8482
|
}
|
|
8320
8483
|
function missingProviderMessage() {
|
|
8321
8484
|
const configHints = agentProvidersByPriority().join(", ");
|
|
8322
|
-
const envHints = agentProvidersByPriority().map(
|
|
8485
|
+
const envHints = agentProvidersByPriority().map(agentProviderApiKeyEnvVar).join(" / ");
|
|
8323
8486
|
return `No agent LLM provider configured. Add an API key for one of: ${configHints} in ~/.canonry/config.yaml, or export ${envHints}.`;
|
|
8324
8487
|
}
|
|
8325
8488
|
function detectAgentProvider(config) {
|
|
@@ -8366,6 +8529,93 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
8366
8529
|
return { provider, modelId };
|
|
8367
8530
|
}
|
|
8368
8531
|
|
|
8532
|
+
// src/agent/remote-mcp.ts
|
|
8533
|
+
import { Type as Type3 } from "@sinclair/typebox";
|
|
8534
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
8535
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
8536
|
+
var log18 = createLogger("RemoteMcp");
|
|
8537
|
+
var AERO_EXCLUDED_MCP_TOOLS2 = /* @__PURE__ */ new Set([]);
|
|
8538
|
+
var MAX_TOOL_RESULT_CHARS2 = 2e4;
|
|
8539
|
+
function truncate(text) {
|
|
8540
|
+
if (text.length <= MAX_TOOL_RESULT_CHARS2) return text;
|
|
8541
|
+
return text.slice(0, MAX_TOOL_RESULT_CHARS2) + "\n... (truncated, result too large)";
|
|
8542
|
+
}
|
|
8543
|
+
async function connectStreamableHttp(server) {
|
|
8544
|
+
const transport = new StreamableHTTPClientTransport(new URL(server.url), {
|
|
8545
|
+
requestInit: {
|
|
8546
|
+
headers: { Authorization: `Bearer ${server.token}` }
|
|
8547
|
+
}
|
|
8548
|
+
});
|
|
8549
|
+
const client = new Client(
|
|
8550
|
+
{ name: "canonry-aero", version: "1.0.0" },
|
|
8551
|
+
{ capabilities: {} }
|
|
8552
|
+
);
|
|
8553
|
+
await client.connect(transport);
|
|
8554
|
+
return client;
|
|
8555
|
+
}
|
|
8556
|
+
function isReadOnly(tool) {
|
|
8557
|
+
return tool.annotations?.readOnlyHint === true;
|
|
8558
|
+
}
|
|
8559
|
+
function adaptRemoteTool(client, tool) {
|
|
8560
|
+
const parameters = Type3.Unsafe(
|
|
8561
|
+
tool.inputSchema ?? { type: "object", properties: {} }
|
|
8562
|
+
);
|
|
8563
|
+
const execute = async (_toolCallId, params) => {
|
|
8564
|
+
const result = await client.callTool({ name: tool.name, arguments: params });
|
|
8565
|
+
return {
|
|
8566
|
+
content: [{ type: "text", text: truncate(JSON.stringify(result, null, 2)) }],
|
|
8567
|
+
details: result
|
|
8568
|
+
};
|
|
8569
|
+
};
|
|
8570
|
+
return {
|
|
8571
|
+
name: tool.name,
|
|
8572
|
+
label: tool.annotations?.title ?? tool.name,
|
|
8573
|
+
description: tool.description ?? "",
|
|
8574
|
+
parameters,
|
|
8575
|
+
execute
|
|
8576
|
+
};
|
|
8577
|
+
}
|
|
8578
|
+
async function loadExternalMcpTools(servers, opts = {}) {
|
|
8579
|
+
if (!servers || servers.length === 0) return [];
|
|
8580
|
+
const connect = opts.connect ?? connectStreamableHttp;
|
|
8581
|
+
const excluded = opts.excluded ?? AERO_EXCLUDED_MCP_TOOLS2;
|
|
8582
|
+
const tools = [];
|
|
8583
|
+
for (const server of servers) {
|
|
8584
|
+
const label = server.label ?? server.url;
|
|
8585
|
+
let client;
|
|
8586
|
+
try {
|
|
8587
|
+
client = await connect(server);
|
|
8588
|
+
} catch (err) {
|
|
8589
|
+
log18.error("external-mcp.connect-failed", {
|
|
8590
|
+
label,
|
|
8591
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8592
|
+
});
|
|
8593
|
+
continue;
|
|
8594
|
+
}
|
|
8595
|
+
let listed;
|
|
8596
|
+
try {
|
|
8597
|
+
listed = await client.listTools();
|
|
8598
|
+
} catch (err) {
|
|
8599
|
+
log18.error("external-mcp.list-failed", {
|
|
8600
|
+
label,
|
|
8601
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8602
|
+
});
|
|
8603
|
+
continue;
|
|
8604
|
+
}
|
|
8605
|
+
for (const tool of listed.tools) {
|
|
8606
|
+
if (excluded.has(tool.name)) continue;
|
|
8607
|
+
if (!isReadOnly(tool)) continue;
|
|
8608
|
+
tools.push(adaptRemoteTool(client, tool));
|
|
8609
|
+
}
|
|
8610
|
+
log18.info("external-mcp.loaded", {
|
|
8611
|
+
label,
|
|
8612
|
+
discovered: listed.tools.length,
|
|
8613
|
+
adopted: tools.length
|
|
8614
|
+
});
|
|
8615
|
+
}
|
|
8616
|
+
return tools;
|
|
8617
|
+
}
|
|
8618
|
+
|
|
8369
8619
|
// src/agent/memory-store.ts
|
|
8370
8620
|
import crypto18 from "crypto";
|
|
8371
8621
|
import { and as and13, desc as desc6, eq as eq17, like, sql as sql5 } from "drizzle-orm";
|
|
@@ -8589,7 +8839,7 @@ async function compactMessages(args) {
|
|
|
8589
8839
|
}
|
|
8590
8840
|
|
|
8591
8841
|
// src/agent/session-registry.ts
|
|
8592
|
-
var
|
|
8842
|
+
var log19 = createLogger("SessionRegistry");
|
|
8593
8843
|
var MAX_HYDRATE_NOTES = 20;
|
|
8594
8844
|
var MAX_HYDRATE_BYTES = 32 * 1024;
|
|
8595
8845
|
function escapeMemoryFragment(value) {
|
|
@@ -8608,10 +8858,53 @@ var SessionRegistry = class {
|
|
|
8608
8858
|
* awaits the same promise instead of kicking off a duplicate LLM call.
|
|
8609
8859
|
*/
|
|
8610
8860
|
compactions = /* @__PURE__ */ new Map();
|
|
8861
|
+
/**
|
|
8862
|
+
* Read-only tools loaded once from the injected remote MCP servers (OSS-A).
|
|
8863
|
+
* The server set is static config, so we connect + adapt a single time and
|
|
8864
|
+
* cache the promise; every session merges the same AgentTool list. Resolves
|
|
8865
|
+
* to `[]` when no servers are configured or all fail to connect (the load is
|
|
8866
|
+
* fail-soft and never throws).
|
|
8867
|
+
*/
|
|
8868
|
+
externalToolsPromise;
|
|
8611
8869
|
opts;
|
|
8612
8870
|
constructor(opts) {
|
|
8613
8871
|
this.opts = opts;
|
|
8614
8872
|
}
|
|
8873
|
+
/**
|
|
8874
|
+
* Lazily load + cache the injected remote MCP tools. The seam OSS-A exposes:
|
|
8875
|
+
* `loadExternalMcpTools` connects to each configured server over the frozen
|
|
8876
|
+
* bearer-gated Streamable HTTP transport, lists tools, and adapts the
|
|
8877
|
+
* read-only, non-excluded ones into AgentTools. Cached so we connect once per
|
|
8878
|
+
* registry lifetime regardless of how many projects or turns run.
|
|
8879
|
+
*/
|
|
8880
|
+
loadExternalTools() {
|
|
8881
|
+
if (!this.externalToolsPromise) {
|
|
8882
|
+
this.externalToolsPromise = loadExternalMcpTools(this.opts.config.externalMcpServers).catch(
|
|
8883
|
+
(err) => {
|
|
8884
|
+
log19.error("external-mcp.load-failed", {
|
|
8885
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8886
|
+
});
|
|
8887
|
+
return [];
|
|
8888
|
+
}
|
|
8889
|
+
);
|
|
8890
|
+
}
|
|
8891
|
+
return this.externalToolsPromise;
|
|
8892
|
+
}
|
|
8893
|
+
/**
|
|
8894
|
+
* Append the cached external MCP tools to the live agent's tool list, idempotently.
|
|
8895
|
+
* Called after scope/model alignment in `acquireForTurn` (which rebuilds
|
|
8896
|
+
* `state.tools` from the local registry), so the remote read-only tools ride
|
|
8897
|
+
* alongside the local ones for the upcoming turn. No-op when none are configured.
|
|
8898
|
+
*/
|
|
8899
|
+
async mergeExternalTools(agent) {
|
|
8900
|
+
const external = await this.loadExternalTools();
|
|
8901
|
+
if (external.length === 0) return;
|
|
8902
|
+
const current = agent.state.tools;
|
|
8903
|
+
const present = new Set(current.map((t) => t.name));
|
|
8904
|
+
const additions = external.filter((t) => !present.has(t.name));
|
|
8905
|
+
if (additions.length === 0) return;
|
|
8906
|
+
agent.state.tools = [...current, ...additions];
|
|
8907
|
+
}
|
|
8615
8908
|
/** Read-only access to the config snapshot the registry was built with. */
|
|
8616
8909
|
getConfig() {
|
|
8617
8910
|
return this.opts.config;
|
|
@@ -8770,6 +9063,7 @@ ${lines.join("\n")}
|
|
|
8770
9063
|
if (preferences?.provider || preferences?.modelId) {
|
|
8771
9064
|
this.alignModel(projectName, agent, preferences);
|
|
8772
9065
|
}
|
|
9066
|
+
await this.mergeExternalTools(agent);
|
|
8773
9067
|
await this.maybeCompact(projectName, agent);
|
|
8774
9068
|
return agent;
|
|
8775
9069
|
}
|
|
@@ -8816,13 +9110,13 @@ ${lines.join("\n")}
|
|
|
8816
9110
|
agent.state.messages = result.messages;
|
|
8817
9111
|
agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
|
|
8818
9112
|
this.save(projectName);
|
|
8819
|
-
|
|
9113
|
+
log19.info("compaction.completed", {
|
|
8820
9114
|
projectName,
|
|
8821
9115
|
removedCount: result.removedCount,
|
|
8822
9116
|
summaryBytes: Buffer.byteLength(result.summary, "utf8")
|
|
8823
9117
|
});
|
|
8824
9118
|
} catch (err) {
|
|
8825
|
-
|
|
9119
|
+
log19.error("compaction.failed", {
|
|
8826
9120
|
projectName,
|
|
8827
9121
|
error: err instanceof Error ? err.message : String(err)
|
|
8828
9122
|
});
|
|
@@ -8919,7 +9213,7 @@ ${lines.join("\n")}
|
|
|
8919
9213
|
await agent.prompt(msgs);
|
|
8920
9214
|
this.save(projectName);
|
|
8921
9215
|
} catch (err) {
|
|
8922
|
-
|
|
9216
|
+
log19.error("drain.failed", {
|
|
8923
9217
|
projectName,
|
|
8924
9218
|
error: err instanceof Error ? err.message : String(err)
|
|
8925
9219
|
});
|
|
@@ -9273,7 +9567,7 @@ function pickExplainProvider(config, providerOverride) {
|
|
|
9273
9567
|
for (const provider of agentProvidersByPriority()) {
|
|
9274
9568
|
if (resolveApiKeyFor(provider, config)) return provider;
|
|
9275
9569
|
}
|
|
9276
|
-
const hints = agentProvidersByPriority().map(
|
|
9570
|
+
const hints = agentProvidersByPriority().map(agentProviderApiKeyEnvVar).join(" / ");
|
|
9277
9571
|
throw providerError(
|
|
9278
9572
|
`No LLM provider configured. Add an API key in ~/.canonry/config.yaml or set one of: ${hints}.`
|
|
9279
9573
|
);
|
|
@@ -9517,7 +9811,7 @@ function formatAuditFactorScore(factor) {
|
|
|
9517
9811
|
}
|
|
9518
9812
|
|
|
9519
9813
|
// src/snapshot-service.ts
|
|
9520
|
-
var
|
|
9814
|
+
var log20 = createLogger("Snapshot");
|
|
9521
9815
|
var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
|
|
9522
9816
|
var SNAPSHOT_QUERY_COUNT = 6;
|
|
9523
9817
|
var ProviderExecutionGate2 = class {
|
|
@@ -9663,7 +9957,7 @@ var SnapshotService = class {
|
|
|
9663
9957
|
return mapAuditReport(report);
|
|
9664
9958
|
} catch (err) {
|
|
9665
9959
|
const message = err instanceof Error ? err.message : String(err);
|
|
9666
|
-
|
|
9960
|
+
log20.warn("audit.failed", { homepageUrl, error: message });
|
|
9667
9961
|
return {
|
|
9668
9962
|
url: homepageUrl,
|
|
9669
9963
|
finalUrl: homepageUrl,
|
|
@@ -9692,7 +9986,7 @@ var SnapshotService = class {
|
|
|
9692
9986
|
queries: parsedQueries
|
|
9693
9987
|
};
|
|
9694
9988
|
} catch (err) {
|
|
9695
|
-
|
|
9989
|
+
log20.warn("profile.generation-failed", {
|
|
9696
9990
|
domain: ctx.domain,
|
|
9697
9991
|
provider: ctx.analysisProvider.adapter.name,
|
|
9698
9992
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -9834,7 +10128,7 @@ var SnapshotService = class {
|
|
|
9834
10128
|
recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
|
|
9835
10129
|
};
|
|
9836
10130
|
} catch (err) {
|
|
9837
|
-
|
|
10131
|
+
log20.warn("response.analysis-failed", {
|
|
9838
10132
|
provider: ctx.analysisProvider.adapter.name,
|
|
9839
10133
|
error: err instanceof Error ? err.message : String(err)
|
|
9840
10134
|
});
|
|
@@ -10116,7 +10410,7 @@ function clipText(value, length) {
|
|
|
10116
10410
|
// src/server.ts
|
|
10117
10411
|
var _require3 = createRequire3(import.meta.url);
|
|
10118
10412
|
var { version: PKG_VERSION2 } = _require3("../package.json");
|
|
10119
|
-
var
|
|
10413
|
+
var log21 = createLogger("Server");
|
|
10120
10414
|
var DEFAULT_QUOTA = {
|
|
10121
10415
|
maxConcurrency: 2,
|
|
10122
10416
|
maxRequestsPerMinute: 10,
|
|
@@ -10249,7 +10543,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
10249
10543
|
}
|
|
10250
10544
|
if (migratedGoogle > 0) {
|
|
10251
10545
|
saveConfigPatch({ google: config.google });
|
|
10252
|
-
|
|
10546
|
+
log21.info("credentials.migrated", { type: "google", count: migratedGoogle });
|
|
10253
10547
|
}
|
|
10254
10548
|
let migratedGa4 = 0;
|
|
10255
10549
|
for (const row of rows.ga4) {
|
|
@@ -10267,7 +10561,7 @@ function applyLegacyCredentials(rows, config) {
|
|
|
10267
10561
|
}
|
|
10268
10562
|
if (migratedGa4 > 0) {
|
|
10269
10563
|
saveConfigPatch({ ga4: config.ga4 });
|
|
10270
|
-
|
|
10564
|
+
log21.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
|
|
10271
10565
|
}
|
|
10272
10566
|
}
|
|
10273
10567
|
function isLoopbackBindHost(host) {
|
|
@@ -10306,11 +10600,11 @@ async function createServer(opts) {
|
|
|
10306
10600
|
applyLegacyCredentials(legacyRows, opts.config);
|
|
10307
10601
|
dropLegacyCredentialColumns(opts.db);
|
|
10308
10602
|
} catch (err) {
|
|
10309
|
-
|
|
10603
|
+
log21.warn("credentials.migration.failed", {
|
|
10310
10604
|
error: err instanceof Error ? err.message : String(err)
|
|
10311
10605
|
});
|
|
10312
10606
|
}
|
|
10313
|
-
|
|
10607
|
+
log21.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
10314
10608
|
const p = providers[k];
|
|
10315
10609
|
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
10316
10610
|
}) });
|
|
@@ -10892,6 +11186,9 @@ async function createServer(opts) {
|
|
|
10892
11186
|
},
|
|
10893
11187
|
getGoogleAuthConfig: () => getGoogleAuthConfig(opts.config),
|
|
10894
11188
|
getPlacesConfig: () => getPlacesConfig(opts.config),
|
|
11189
|
+
// Resolved fresh each call so a key added at runtime (settings API) shows
|
|
11190
|
+
// up immediately in the `config.agent-providers` doctor check.
|
|
11191
|
+
getAgentProviderSummary: () => buildAgentProvidersResponse(opts.config).providers,
|
|
10895
11192
|
googleConnectionStore,
|
|
10896
11193
|
googleStateSecret,
|
|
10897
11194
|
publicUrl: opts.config.publicUrl,
|
|
@@ -1434,7 +1434,8 @@ var ProviderIds = {
|
|
|
1434
1434
|
perplexity: "perplexity",
|
|
1435
1435
|
local: "local",
|
|
1436
1436
|
cdpChatgpt: "cdp:chatgpt",
|
|
1437
|
-
zai: "zai"
|
|
1437
|
+
zai: "zai",
|
|
1438
|
+
deepinfra: "deepinfra"
|
|
1438
1439
|
};
|
|
1439
1440
|
var PROVIDER_IDS = Object.values(ProviderIds);
|
|
1440
1441
|
var SweepProviderIds = {
|
|
@@ -1450,7 +1451,8 @@ var AgentProviderIds = {
|
|
|
1450
1451
|
claude: ProviderIds.claude,
|
|
1451
1452
|
openai: ProviderIds.openai,
|
|
1452
1453
|
gemini: ProviderIds.gemini,
|
|
1453
|
-
zai: ProviderIds.zai
|
|
1454
|
+
zai: ProviderIds.zai,
|
|
1455
|
+
deepinfra: ProviderIds.deepinfra
|
|
1454
1456
|
};
|
|
1455
1457
|
var AGENT_PROVIDER_IDS = Object.values(AgentProviderIds);
|
|
1456
1458
|
function isAgentProviderId(value) {
|
|
@@ -3027,7 +3029,7 @@ function escapeRegExp(value) {
|
|
|
3027
3029
|
|
|
3028
3030
|
// ../contracts/src/agent.ts
|
|
3029
3031
|
import { z as z22 } from "zod";
|
|
3030
|
-
var agentProviderIdSchema = z22.enum(["claude", "openai", "gemini", "zai"]);
|
|
3032
|
+
var agentProviderIdSchema = z22.enum(["claude", "openai", "gemini", "zai", "deepinfra"]);
|
|
3031
3033
|
var agentProviderOptionDtoSchema = z22.object({
|
|
3032
3034
|
/** Stable identifier — what clients pass back as `provider` on the prompt endpoint. */
|
|
3033
3035
|
id: agentProviderIdSchema,
|