zam-core 0.11.0 → 0.12.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/dist/cli/app.js +80 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +1169 -468
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/copilot-extension/host.bundle.js +1 -1
- package/dist/copilot-extension/manifest.json +1 -1
- package/dist/copilot-extension/mcp-client.bundle.mjs +1 -1
- package/dist/index.d.ts +22 -2
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -1
- package/dist/ui/graph-panel.html +1 -1
- package/dist/ui/recall-panel.html +1 -1
- package/dist/ui/settings-panel.html +1 -1
- package/dist/ui/studio-panel.html +2 -2
- package/dist/vscode-extension/ZAM_Companion_0.12.0.vsix +0 -0
- package/dist/vscode-extension/extension.cjs +34 -34
- package/dist/vscode-extension/manifest.json +2 -2
- package/package.json +1 -1
- package/dist/vscode-extension/ZAM_Companion_0.11.0.vsix +0 -0
package/dist/cli/commands/mcp.js
CHANGED
|
@@ -6178,9 +6178,18 @@ function getMachineCompanionConfig(path = defaultConfigPath()) {
|
|
|
6178
6178
|
if (typeof raw.selectedEvaluatorId === "string") {
|
|
6179
6179
|
result.selectedEvaluatorId = raw.selectedEvaluatorId;
|
|
6180
6180
|
}
|
|
6181
|
+
if (typeof raw.selectedVscodeEvaluatorId === "string") {
|
|
6182
|
+
result.selectedVscodeEvaluatorId = raw.selectedVscodeEvaluatorId;
|
|
6183
|
+
}
|
|
6184
|
+
if (typeof raw.selectedAntigravityEvaluatorId === "string") {
|
|
6185
|
+
result.selectedAntigravityEvaluatorId = raw.selectedAntigravityEvaluatorId;
|
|
6186
|
+
}
|
|
6181
6187
|
if (typeof raw.selectedVscodeModelId === "string") {
|
|
6182
6188
|
result.selectedVscodeModelId = raw.selectedVscodeModelId;
|
|
6183
6189
|
}
|
|
6190
|
+
if (typeof raw.selectedAntigravityModelId === "string") {
|
|
6191
|
+
result.selectedAntigravityModelId = raw.selectedAntigravityModelId;
|
|
6192
|
+
}
|
|
6184
6193
|
if (raw.collapsed && typeof raw.collapsed === "object" && !Array.isArray(raw.collapsed)) {
|
|
6185
6194
|
const collapsed = {};
|
|
6186
6195
|
for (const [surface, value] of Object.entries(raw.collapsed)) {
|
|
@@ -6211,6 +6220,34 @@ function updateMachineCompanionConfig(update, path = defaultConfigPath()) {
|
|
|
6211
6220
|
delete companion.selectedEvaluatorId;
|
|
6212
6221
|
}
|
|
6213
6222
|
}
|
|
6223
|
+
if ("selectedVscodeEvaluatorId" in update) {
|
|
6224
|
+
if (update.selectedVscodeEvaluatorId) {
|
|
6225
|
+
companion.selectedVscodeEvaluatorId = update.selectedVscodeEvaluatorId;
|
|
6226
|
+
} else {
|
|
6227
|
+
delete companion.selectedVscodeEvaluatorId;
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
if ("selectedAntigravityEvaluatorId" in update) {
|
|
6231
|
+
if (update.selectedAntigravityEvaluatorId) {
|
|
6232
|
+
companion.selectedAntigravityEvaluatorId = update.selectedAntigravityEvaluatorId;
|
|
6233
|
+
} else {
|
|
6234
|
+
delete companion.selectedAntigravityEvaluatorId;
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
if ("selectedVscodeModelId" in update) {
|
|
6238
|
+
if (update.selectedVscodeModelId) {
|
|
6239
|
+
companion.selectedVscodeModelId = update.selectedVscodeModelId;
|
|
6240
|
+
} else {
|
|
6241
|
+
delete companion.selectedVscodeModelId;
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6244
|
+
if ("selectedAntigravityModelId" in update) {
|
|
6245
|
+
if (update.selectedAntigravityModelId) {
|
|
6246
|
+
companion.selectedAntigravityModelId = update.selectedAntigravityModelId;
|
|
6247
|
+
} else {
|
|
6248
|
+
delete companion.selectedAntigravityModelId;
|
|
6249
|
+
}
|
|
6250
|
+
}
|
|
6214
6251
|
if (update.collapsed) {
|
|
6215
6252
|
companion.collapsed = {
|
|
6216
6253
|
...companion.collapsed ?? {},
|
|
@@ -6244,6 +6281,30 @@ function setCompanionSelectedEvaluatorId(evaluatorId, path = defaultConfigPath()
|
|
|
6244
6281
|
}
|
|
6245
6282
|
saveMachineCompanionConfig(companion, path);
|
|
6246
6283
|
}
|
|
6284
|
+
function getCompanionSelectedVscodeEvaluatorId(path = defaultConfigPath()) {
|
|
6285
|
+
return getMachineCompanionConfig(path).selectedVscodeEvaluatorId;
|
|
6286
|
+
}
|
|
6287
|
+
function setCompanionSelectedVscodeEvaluatorId(evaluatorId, path = defaultConfigPath()) {
|
|
6288
|
+
const companion = getMachineCompanionConfig(path);
|
|
6289
|
+
if (evaluatorId) {
|
|
6290
|
+
companion.selectedVscodeEvaluatorId = evaluatorId;
|
|
6291
|
+
} else {
|
|
6292
|
+
delete companion.selectedVscodeEvaluatorId;
|
|
6293
|
+
}
|
|
6294
|
+
saveMachineCompanionConfig(companion, path);
|
|
6295
|
+
}
|
|
6296
|
+
function getCompanionSelectedAntigravityEvaluatorId(path = defaultConfigPath()) {
|
|
6297
|
+
return getMachineCompanionConfig(path).selectedAntigravityEvaluatorId;
|
|
6298
|
+
}
|
|
6299
|
+
function setCompanionSelectedAntigravityEvaluatorId(evaluatorId, path = defaultConfigPath()) {
|
|
6300
|
+
const companion = getMachineCompanionConfig(path);
|
|
6301
|
+
if (evaluatorId) {
|
|
6302
|
+
companion.selectedAntigravityEvaluatorId = evaluatorId;
|
|
6303
|
+
} else {
|
|
6304
|
+
delete companion.selectedAntigravityEvaluatorId;
|
|
6305
|
+
}
|
|
6306
|
+
saveMachineCompanionConfig(companion, path);
|
|
6307
|
+
}
|
|
6247
6308
|
function getCompanionSelectedVscodeModelId(path = defaultConfigPath()) {
|
|
6248
6309
|
return getMachineCompanionConfig(path).selectedVscodeModelId;
|
|
6249
6310
|
}
|
|
@@ -6256,6 +6317,18 @@ function setCompanionSelectedVscodeModelId(modelId, path = defaultConfigPath())
|
|
|
6256
6317
|
}
|
|
6257
6318
|
saveMachineCompanionConfig(companion, path);
|
|
6258
6319
|
}
|
|
6320
|
+
function getCompanionSelectedAntigravityModelId(path = defaultConfigPath()) {
|
|
6321
|
+
return getMachineCompanionConfig(path).selectedAntigravityModelId;
|
|
6322
|
+
}
|
|
6323
|
+
function setCompanionSelectedAntigravityModelId(modelId, path = defaultConfigPath()) {
|
|
6324
|
+
const companion = getMachineCompanionConfig(path);
|
|
6325
|
+
if (modelId) {
|
|
6326
|
+
companion.selectedAntigravityModelId = modelId;
|
|
6327
|
+
} else {
|
|
6328
|
+
delete companion.selectedAntigravityModelId;
|
|
6329
|
+
}
|
|
6330
|
+
saveMachineCompanionConfig(companion, path);
|
|
6331
|
+
}
|
|
6259
6332
|
function getCompanionCollapsed(path = defaultConfigPath()) {
|
|
6260
6333
|
return getMachineCompanionConfig(path).collapsed ?? {};
|
|
6261
6334
|
}
|
|
@@ -6984,8 +7057,11 @@ __export(kernel_exports, {
|
|
|
6984
7057
|
getCardById: () => getCardById,
|
|
6985
7058
|
getCardDeletionImpact: () => getCardDeletionImpact,
|
|
6986
7059
|
getCompanionCollapsed: () => getCompanionCollapsed,
|
|
7060
|
+
getCompanionSelectedAntigravityEvaluatorId: () => getCompanionSelectedAntigravityEvaluatorId,
|
|
7061
|
+
getCompanionSelectedAntigravityModelId: () => getCompanionSelectedAntigravityModelId,
|
|
6987
7062
|
getCompanionSelectedEvaluatorId: () => getCompanionSelectedEvaluatorId,
|
|
6988
7063
|
getCompanionSelectedUserId: () => getCompanionSelectedUserId,
|
|
7064
|
+
getCompanionSelectedVscodeEvaluatorId: () => getCompanionSelectedVscodeEvaluatorId,
|
|
6989
7065
|
getCompanionSelectedVscodeModelId: () => getCompanionSelectedVscodeModelId,
|
|
6990
7066
|
getConfiguredWorkspaces: () => getConfiguredWorkspaces,
|
|
6991
7067
|
getDatabaseTargetInfo: () => getDatabaseTargetInfo,
|
|
@@ -7098,8 +7174,11 @@ __export(kernel_exports, {
|
|
|
7098
7174
|
setActiveWorkspaceId: () => setActiveWorkspaceId,
|
|
7099
7175
|
setAgentConnectAutoDone: () => setAgentConnectAutoDone,
|
|
7100
7176
|
setCompanionCollapsed: () => setCompanionCollapsed,
|
|
7177
|
+
setCompanionSelectedAntigravityEvaluatorId: () => setCompanionSelectedAntigravityEvaluatorId,
|
|
7178
|
+
setCompanionSelectedAntigravityModelId: () => setCompanionSelectedAntigravityModelId,
|
|
7101
7179
|
setCompanionSelectedEvaluatorId: () => setCompanionSelectedEvaluatorId,
|
|
7102
7180
|
setCompanionSelectedUserId: () => setCompanionSelectedUserId,
|
|
7181
|
+
setCompanionSelectedVscodeEvaluatorId: () => setCompanionSelectedVscodeEvaluatorId,
|
|
7103
7182
|
setCompanionSelectedVscodeModelId: () => setCompanionSelectedVscodeModelId,
|
|
7104
7183
|
setInstallChannel: () => setInstallChannel,
|
|
7105
7184
|
setInstallMode: () => setInstallMode,
|
|
@@ -7180,330 +7259,119 @@ var init_kernel = __esm({
|
|
|
7180
7259
|
}
|
|
7181
7260
|
});
|
|
7182
7261
|
|
|
7183
|
-
// src/cli/
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7262
|
+
// src/cli/llm/client.ts
|
|
7263
|
+
var client_exports = {};
|
|
7264
|
+
__export(client_exports, {
|
|
7265
|
+
DEFAULT_LLM_API_KEY: () => DEFAULT_LLM_API_KEY,
|
|
7266
|
+
DEFAULT_LLM_MAX_TOKENS: () => DEFAULT_LLM_MAX_TOKENS,
|
|
7267
|
+
DEFAULT_LLM_MODEL: () => DEFAULT_LLM_MODEL,
|
|
7268
|
+
DEFAULT_LLM_URL: () => DEFAULT_LLM_URL,
|
|
7269
|
+
LlmResponseTruncatedError: () => LlmResponseTruncatedError,
|
|
7270
|
+
RECALL_DISCUSSION_MAX_OUTPUT_TOKENS: () => RECALL_DISCUSSION_MAX_OUTPUT_TOKENS,
|
|
7271
|
+
RECALL_EVALUATION_MAX_OUTPUT_TOKENS: () => RECALL_EVALUATION_MAX_OUTPUT_TOKENS,
|
|
7272
|
+
RECALL_QUESTION_MAX_OUTPUT_TOKENS: () => RECALL_QUESTION_MAX_OUTPUT_TOKENS,
|
|
7273
|
+
checkVisionReadiness: () => checkVisionReadiness,
|
|
7274
|
+
clearRecallEndpointCache: () => clearRecallEndpointCache,
|
|
7275
|
+
discussReviewViaLLM: () => discussReviewViaLLM,
|
|
7276
|
+
ensureHighQualityQuestion: () => ensureHighQualityQuestion,
|
|
7277
|
+
ensureLlmReadyHeadless: () => ensureLlmReadyHeadless,
|
|
7278
|
+
ensureLocalLlmRunning: () => ensureLocalLlmRunning,
|
|
7279
|
+
evaluateAnswerViaLLM: () => evaluateAnswerViaLLM,
|
|
7280
|
+
extractTextFromScanViaLLM: () => extractTextFromScanViaLLM,
|
|
7281
|
+
fetchWithInteractiveTimeout: () => fetchWithInteractiveTimeout,
|
|
7282
|
+
generateFoundationsProposalsViaLLM: () => generateFoundationsProposalsViaLLM,
|
|
7283
|
+
generateQuestionViaLLM: () => generateQuestionViaLLM,
|
|
7284
|
+
generateSplitProposalsViaLLM: () => generateSplitProposalsViaLLM,
|
|
7285
|
+
generateTitleViaLLM: () => generateTitleViaLLM,
|
|
7286
|
+
getAvailableModels: () => getAvailableModels,
|
|
7287
|
+
getCloudModelRecommendation: () => getCloudModelRecommendation,
|
|
7288
|
+
getLlmConfig: () => getLlmConfig,
|
|
7289
|
+
getProviderForRole: () => getProviderForRole,
|
|
7290
|
+
getProviderRoleStatus: () => getProviderRoleStatus,
|
|
7291
|
+
getVisionConfig: () => getVisionConfig,
|
|
7292
|
+
importCurriculumViaLLM: () => importCurriculumViaLLM,
|
|
7293
|
+
inferApiFlavor: () => inferApiFlavor,
|
|
7294
|
+
isLlmOnline: () => isLlmOnline,
|
|
7295
|
+
repairUmlautsViaLLM: () => repairUmlautsViaLLM,
|
|
7296
|
+
resolveCapability: () => resolveCapability,
|
|
7297
|
+
resolveUsableRecallEndpoint: () => resolveUsableRecallEndpoint,
|
|
7298
|
+
sampleViaLocalLLM: () => sampleViaLocalLLM,
|
|
7299
|
+
translateQuestionViaLLM: () => translateQuestionViaLLM
|
|
7300
|
+
});
|
|
7301
|
+
import { spawn } from "child_process";
|
|
7302
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
7303
|
+
function clearRecallEndpointCache() {
|
|
7304
|
+
cachedRecallEndpoint = null;
|
|
7211
7305
|
}
|
|
7212
|
-
function
|
|
7213
|
-
return
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7306
|
+
function recallEndpointSignature(cfg) {
|
|
7307
|
+
return [
|
|
7308
|
+
cfg.enabled ? "on" : "off",
|
|
7309
|
+
cfg.providerName ?? "",
|
|
7310
|
+
cfg.url,
|
|
7311
|
+
cfg.model,
|
|
7312
|
+
cfg.apiFlavor,
|
|
7313
|
+
cfg.fallback?.url ?? "",
|
|
7314
|
+
cfg.fallback?.model ?? ""
|
|
7315
|
+
].join("|");
|
|
7221
7316
|
}
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7317
|
+
async function getLlmConfig(db) {
|
|
7318
|
+
return {
|
|
7319
|
+
enabled: await getSetting(db, "llm.enabled") === "true",
|
|
7320
|
+
url: await getSetting(db, "llm.url") || DEFAULT_LLM_URL,
|
|
7321
|
+
model: await getSetting(db, "llm.model") || DEFAULT_LLM_MODEL,
|
|
7322
|
+
apiKey: await getSetting(db, "llm.api_key") || DEFAULT_LLM_API_KEY,
|
|
7323
|
+
locale: await getSetting(db, "system.locale") || "en"
|
|
7324
|
+
};
|
|
7325
|
+
}
|
|
7326
|
+
function getCloudModelRecommendation(url) {
|
|
7327
|
+
const lowercase = url.toLowerCase();
|
|
7328
|
+
if (lowercase.includes("openrouter.ai")) {
|
|
7329
|
+
return { model: "openrouter/free", flavor: "chat-completions" };
|
|
7228
7330
|
}
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
const route = routes.find((candidate) => candidate.id === selectedId);
|
|
7232
|
-
if (!route) {
|
|
7233
|
-
throw new EvaluatorUnavailableError(selectedId, "not configured");
|
|
7331
|
+
if (lowercase.includes("openai.com") || lowercase.includes("api.openai")) {
|
|
7332
|
+
return { model: "gpt-5-mini", flavor: "chat-completions" };
|
|
7234
7333
|
}
|
|
7235
|
-
if (
|
|
7236
|
-
|
|
7237
|
-
selectedId,
|
|
7238
|
-
route.reason ?? "not routable on this surface"
|
|
7239
|
-
);
|
|
7334
|
+
if (lowercase.includes("googleapis.com") || lowercase.includes("google")) {
|
|
7335
|
+
return { model: "gemini-3.5-flash", flavor: "chat-completions" };
|
|
7240
7336
|
}
|
|
7241
|
-
|
|
7242
|
-
}
|
|
7243
|
-
|
|
7244
|
-
// src/vscode-extension/companion-selection.ts
|
|
7245
|
-
function resolveSelection(candidates) {
|
|
7246
|
-
if (candidates.invocation !== void 0) {
|
|
7247
|
-
return {
|
|
7248
|
-
value: candidates.invocation,
|
|
7249
|
-
source: "invocation",
|
|
7250
|
-
sessionScoped: true
|
|
7251
|
-
};
|
|
7337
|
+
if (lowercase.includes("deepseek.com")) {
|
|
7338
|
+
return { model: "deepseek-v4-flash", flavor: "chat-completions" };
|
|
7252
7339
|
}
|
|
7253
|
-
if (
|
|
7254
|
-
return {
|
|
7340
|
+
if (lowercase.includes("mimo")) {
|
|
7341
|
+
return { model: "mimo-v2.5", flavor: "chat-completions" };
|
|
7255
7342
|
}
|
|
7256
|
-
if (
|
|
7343
|
+
if (lowercase.includes("anthropic.com")) {
|
|
7257
7344
|
return {
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
sessionScoped: false
|
|
7345
|
+
model: "claude-haiku-4-5-20251001",
|
|
7346
|
+
flavor: "anthropic-messages"
|
|
7261
7347
|
};
|
|
7262
7348
|
}
|
|
7263
|
-
return
|
|
7264
|
-
value: candidates.fallback,
|
|
7265
|
-
source: "default",
|
|
7266
|
-
sessionScoped: false
|
|
7267
|
-
};
|
|
7268
|
-
}
|
|
7269
|
-
function isPersistableSelection(result) {
|
|
7270
|
-
return result.source === "manual";
|
|
7271
|
-
}
|
|
7272
|
-
|
|
7273
|
-
// src/vscode-extension/companion-context.ts
|
|
7274
|
-
var COMPANION_SURFACES = [
|
|
7275
|
-
"recall",
|
|
7276
|
-
"graph",
|
|
7277
|
-
"settings",
|
|
7278
|
-
"studio"
|
|
7279
|
-
];
|
|
7280
|
-
function isCompanionSurface(value) {
|
|
7281
|
-
return typeof value === "string" && COMPANION_SURFACES.includes(value);
|
|
7282
|
-
}
|
|
7283
|
-
function isRecord2(value) {
|
|
7284
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7349
|
+
return null;
|
|
7285
7350
|
}
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
};
|
|
7292
|
-
function normalizeNativeHostIdentity(clientInfo, harnessOverride) {
|
|
7293
|
-
if (harnessOverride) {
|
|
7294
|
-
const known2 = KNOWN_NATIVE_HOSTS[harnessOverride];
|
|
7295
|
-
return {
|
|
7296
|
-
rawName: clientInfo?.name ?? harnessOverride,
|
|
7297
|
-
version: clientInfo?.version,
|
|
7298
|
-
normalizedId: known2?.normalizedId,
|
|
7299
|
-
label: known2?.label ?? harnessOverride
|
|
7300
|
-
};
|
|
7351
|
+
function inferApiFlavor(url) {
|
|
7352
|
+
try {
|
|
7353
|
+
return new URL(url).hostname.toLowerCase().endsWith("anthropic.com") ? "anthropic-messages" : "chat-completions";
|
|
7354
|
+
} catch {
|
|
7355
|
+
return "chat-completions";
|
|
7301
7356
|
}
|
|
7302
|
-
if (!clientInfo) return void 0;
|
|
7303
|
-
const known = KNOWN_NATIVE_HOSTS[clientInfo.name];
|
|
7304
|
-
return {
|
|
7305
|
-
rawName: clientInfo.name,
|
|
7306
|
-
version: clientInfo.version,
|
|
7307
|
-
normalizedId: known?.normalizedId,
|
|
7308
|
-
label: known?.label ?? clientInfo.name
|
|
7309
|
-
};
|
|
7310
|
-
}
|
|
7311
|
-
function resolveCollapsedForSurface(state, surface) {
|
|
7312
|
-
return state?.[surface] ?? false;
|
|
7313
7357
|
}
|
|
7314
|
-
function
|
|
7315
|
-
|
|
7316
|
-
|
|
7358
|
+
async function readJsonSetting(db, key) {
|
|
7359
|
+
const raw = await getSetting(db, key);
|
|
7360
|
+
if (!raw) return null;
|
|
7361
|
+
try {
|
|
7362
|
+
return JSON.parse(raw);
|
|
7363
|
+
} catch {
|
|
7364
|
+
return null;
|
|
7317
7365
|
}
|
|
7318
|
-
const rawClientInfo = value.clientInfo;
|
|
7319
|
-
const clientInfo = isRecord2(rawClientInfo) && typeof rawClientInfo.name === "string" ? {
|
|
7320
|
-
name: rawClientInfo.name,
|
|
7321
|
-
version: typeof rawClientInfo.version === "string" ? rawClientInfo.version : void 0
|
|
7322
|
-
} : void 0;
|
|
7323
|
-
const harnessOverride = typeof value.harnessOverride === "string" ? value.harnessOverride : void 0;
|
|
7324
|
-
return { surface: value.surface, clientInfo, harnessOverride };
|
|
7325
7366
|
}
|
|
7326
|
-
function
|
|
7327
|
-
if (
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
let evaluatorId;
|
|
7332
|
-
if (value.evaluatorId !== void 0) {
|
|
7333
|
-
if (!isEvaluatorId(value.evaluatorId)) {
|
|
7334
|
-
throw new Error(
|
|
7335
|
-
`Unknown evaluator id in companion context write request: ${String(value.evaluatorId)}`
|
|
7336
|
-
);
|
|
7337
|
-
}
|
|
7338
|
-
evaluatorId = value.evaluatorId;
|
|
7339
|
-
}
|
|
7340
|
-
const collapsed = typeof value.collapsed === "boolean" ? value.collapsed : void 0;
|
|
7341
|
-
if (userId === void 0 && evaluatorId === void 0 && collapsed === void 0) {
|
|
7342
|
-
throw new Error(
|
|
7343
|
-
"Companion context write request must set userId, evaluatorId, and/or collapsed"
|
|
7344
|
-
);
|
|
7367
|
+
function resolveProviderApiKey(rec) {
|
|
7368
|
+
if (rec.apiKey) return rec.apiKey;
|
|
7369
|
+
if (rec.apiKeyRef) {
|
|
7370
|
+
const key = getProviderApiKey(rec.apiKeyRef);
|
|
7371
|
+
if (key) return key;
|
|
7345
7372
|
}
|
|
7346
|
-
return
|
|
7373
|
+
return DEFAULT_LLM_API_KEY;
|
|
7347
7374
|
}
|
|
7348
|
-
function buildCompanionContext(input) {
|
|
7349
|
-
const userSelection = resolveSelection(input.userSelection);
|
|
7350
|
-
const evaluatorSelection = resolveSelection(input.evaluatorSelection);
|
|
7351
|
-
const routes = buildEvaluatorRoutes(
|
|
7352
|
-
input.evaluatorRouteInputs,
|
|
7353
|
-
evaluatorSelection.value
|
|
7354
|
-
);
|
|
7355
|
-
let activeEvaluatorId;
|
|
7356
|
-
let activeEvaluatorError;
|
|
7357
|
-
try {
|
|
7358
|
-
activateSelectedEvaluator(routes, evaluatorSelection.value);
|
|
7359
|
-
activeEvaluatorId = evaluatorSelection.value;
|
|
7360
|
-
} catch (error) {
|
|
7361
|
-
if (error instanceof EvaluatorUnavailableError) {
|
|
7362
|
-
activeEvaluatorError = error;
|
|
7363
|
-
} else {
|
|
7364
|
-
throw error;
|
|
7365
|
-
}
|
|
7366
|
-
}
|
|
7367
|
-
const evaluators = activeEvaluatorId ? routes.map(
|
|
7368
|
-
(route) => route.id === activeEvaluatorId ? { ...route, active: true } : route
|
|
7369
|
-
) : routes;
|
|
7370
|
-
return {
|
|
7371
|
-
read: {
|
|
7372
|
-
surface: input.surface,
|
|
7373
|
-
nativeHost: input.nativeHost,
|
|
7374
|
-
user: {
|
|
7375
|
-
currentId: userSelection.value,
|
|
7376
|
-
persistedId: input.userSelection.persisted,
|
|
7377
|
-
source: userSelection.source
|
|
7378
|
-
},
|
|
7379
|
-
profiles: input.profiles ?? [],
|
|
7380
|
-
harnesses: input.harnesses,
|
|
7381
|
-
evaluators,
|
|
7382
|
-
selectedEvaluatorId: evaluatorSelection.value,
|
|
7383
|
-
activeEvaluatorId,
|
|
7384
|
-
collapsed: resolveCollapsedForSurface(input.collapsed, input.surface)
|
|
7385
|
-
},
|
|
7386
|
-
userSelection,
|
|
7387
|
-
evaluatorSelection,
|
|
7388
|
-
activeEvaluatorError
|
|
7389
|
-
};
|
|
7390
|
-
}
|
|
7391
|
-
|
|
7392
|
-
// src/cli/bridge-handlers.ts
|
|
7393
|
-
init_kernel();
|
|
7394
|
-
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
7395
|
-
import { join as join13 } from "path";
|
|
7396
|
-
|
|
7397
|
-
// src/cli/knowledge-contexts.ts
|
|
7398
|
-
init_kernel();
|
|
7399
|
-
async function resolveOperationKnowledgeContexts(db, requestedNames) {
|
|
7400
|
-
const explicitNames = [
|
|
7401
|
-
...new Set(requestedNames.map((name) => name.trim()).filter(Boolean))
|
|
7402
|
-
];
|
|
7403
|
-
const activeDefault = getActiveWorkspaceContext();
|
|
7404
|
-
const selectedNames = explicitNames.length > 0 ? explicitNames : activeDefault ? [activeDefault] : [];
|
|
7405
|
-
const contexts = [];
|
|
7406
|
-
for (const name of selectedNames) {
|
|
7407
|
-
const context = await getKnowledgeContextByName(db, name);
|
|
7408
|
-
if (!context) {
|
|
7409
|
-
const subject = explicitNames.length > 0 ? "Knowledge" : "Active knowledge";
|
|
7410
|
-
throw new Error(`${subject} context not found: ${name}`);
|
|
7411
|
-
}
|
|
7412
|
-
contexts.push(context);
|
|
7413
|
-
}
|
|
7414
|
-
return contexts;
|
|
7415
|
-
}
|
|
7416
|
-
|
|
7417
|
-
// src/cli/llm/client.ts
|
|
7418
|
-
init_kernel();
|
|
7419
|
-
import { spawn } from "child_process";
|
|
7420
|
-
import { existsSync as existsSync11, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
7421
|
-
var DEFAULT_LLM_URL = "http://localhost:8000/v1";
|
|
7422
|
-
var DEFAULT_LLM_MAX_TOKENS = 1e4;
|
|
7423
|
-
var LOCAL_CURRICULUM_IMPORT_HARD_TIMEOUT_MS = 6e5;
|
|
7424
|
-
var CLOUD_CURRICULUM_IMPORT_HARD_TIMEOUT_MS = 18e4;
|
|
7425
|
-
var RECALL_QUESTION_MAX_OUTPUT_TOKENS = 400;
|
|
7426
|
-
var RECALL_EVALUATION_MAX_OUTPUT_TOKENS = 1200;
|
|
7427
|
-
var RECALL_DISCUSSION_MAX_OUTPUT_TOKENS = 1200;
|
|
7428
|
-
var RECALL_ENDPOINT_CACHE_MS = 6e4;
|
|
7429
|
-
var cachedRecallEndpoint = null;
|
|
7430
|
-
function recallEndpointSignature(cfg) {
|
|
7431
|
-
return [
|
|
7432
|
-
cfg.enabled ? "on" : "off",
|
|
7433
|
-
cfg.providerName ?? "",
|
|
7434
|
-
cfg.url,
|
|
7435
|
-
cfg.model,
|
|
7436
|
-
cfg.apiFlavor,
|
|
7437
|
-
cfg.fallback?.url ?? "",
|
|
7438
|
-
cfg.fallback?.model ?? ""
|
|
7439
|
-
].join("|");
|
|
7440
|
-
}
|
|
7441
|
-
var DEFAULT_LLM_MODEL = "qwen3.5:4b";
|
|
7442
|
-
var DEFAULT_LLM_API_KEY = "sk-none";
|
|
7443
|
-
async function getLlmConfig(db) {
|
|
7444
|
-
return {
|
|
7445
|
-
enabled: await getSetting(db, "llm.enabled") === "true",
|
|
7446
|
-
url: await getSetting(db, "llm.url") || DEFAULT_LLM_URL,
|
|
7447
|
-
model: await getSetting(db, "llm.model") || DEFAULT_LLM_MODEL,
|
|
7448
|
-
apiKey: await getSetting(db, "llm.api_key") || DEFAULT_LLM_API_KEY,
|
|
7449
|
-
locale: await getSetting(db, "system.locale") || "en"
|
|
7450
|
-
};
|
|
7451
|
-
}
|
|
7452
|
-
function getCloudModelRecommendation(url) {
|
|
7453
|
-
const lowercase = url.toLowerCase();
|
|
7454
|
-
if (lowercase.includes("openrouter.ai")) {
|
|
7455
|
-
return { model: "openrouter/free", flavor: "chat-completions" };
|
|
7456
|
-
}
|
|
7457
|
-
if (lowercase.includes("openai.com") || lowercase.includes("api.openai")) {
|
|
7458
|
-
return { model: "gpt-5-mini", flavor: "chat-completions" };
|
|
7459
|
-
}
|
|
7460
|
-
if (lowercase.includes("googleapis.com") || lowercase.includes("google")) {
|
|
7461
|
-
return { model: "gemini-3.5-flash", flavor: "chat-completions" };
|
|
7462
|
-
}
|
|
7463
|
-
if (lowercase.includes("deepseek.com")) {
|
|
7464
|
-
return { model: "deepseek-v4-flash", flavor: "chat-completions" };
|
|
7465
|
-
}
|
|
7466
|
-
if (lowercase.includes("mimo")) {
|
|
7467
|
-
return { model: "mimo-v2.5", flavor: "chat-completions" };
|
|
7468
|
-
}
|
|
7469
|
-
if (lowercase.includes("anthropic.com")) {
|
|
7470
|
-
return {
|
|
7471
|
-
model: "claude-haiku-4-5-20251001",
|
|
7472
|
-
flavor: "anthropic-messages"
|
|
7473
|
-
};
|
|
7474
|
-
}
|
|
7475
|
-
return null;
|
|
7476
|
-
}
|
|
7477
|
-
function inferApiFlavor(url) {
|
|
7478
|
-
try {
|
|
7479
|
-
return new URL(url).hostname.toLowerCase().endsWith("anthropic.com") ? "anthropic-messages" : "chat-completions";
|
|
7480
|
-
} catch {
|
|
7481
|
-
return "chat-completions";
|
|
7482
|
-
}
|
|
7483
|
-
}
|
|
7484
|
-
async function readJsonSetting(db, key) {
|
|
7485
|
-
const raw = await getSetting(db, key);
|
|
7486
|
-
if (!raw) return null;
|
|
7487
|
-
try {
|
|
7488
|
-
return JSON.parse(raw);
|
|
7489
|
-
} catch {
|
|
7490
|
-
return null;
|
|
7491
|
-
}
|
|
7492
|
-
}
|
|
7493
|
-
function resolveProviderApiKey(rec) {
|
|
7494
|
-
if (rec.apiKey) return rec.apiKey;
|
|
7495
|
-
if (rec.apiKeyRef) {
|
|
7496
|
-
const key = getProviderApiKey(rec.apiKeyRef);
|
|
7497
|
-
if (key) return key;
|
|
7498
|
-
}
|
|
7499
|
-
return DEFAULT_LLM_API_KEY;
|
|
7500
|
-
}
|
|
7501
|
-
var ROLE_TO_CAPABILITY2 = {
|
|
7502
|
-
recall: "text",
|
|
7503
|
-
text: "text",
|
|
7504
|
-
vision: "image",
|
|
7505
|
-
embedding: "embedding"
|
|
7506
|
-
};
|
|
7507
7375
|
function materializeModelEntry(entry, base, enabled, maxFrames) {
|
|
7508
7376
|
const url = entry.url || base.url;
|
|
7509
7377
|
const cfg = {
|
|
@@ -7677,39 +7545,6 @@ async function getVisionConfig(db) {
|
|
|
7677
7545
|
maxFrames: p.maxFrames
|
|
7678
7546
|
};
|
|
7679
7547
|
}
|
|
7680
|
-
var LANGUAGE_NAMES = {
|
|
7681
|
-
en: "English",
|
|
7682
|
-
de: "German",
|
|
7683
|
-
es: "Spanish",
|
|
7684
|
-
fr: "French",
|
|
7685
|
-
pt: "Portuguese",
|
|
7686
|
-
zh: "Chinese",
|
|
7687
|
-
ja: "Japanese"
|
|
7688
|
-
};
|
|
7689
|
-
var LOCALIZED_RATING_PREFIX = {
|
|
7690
|
-
en: "Suggested rating",
|
|
7691
|
-
de: "Empfohlene Bewertung",
|
|
7692
|
-
es: "Calificaci\xF3n sugerida",
|
|
7693
|
-
fr: "Note sugg\xE9r\xE9e",
|
|
7694
|
-
pt: "Avalia\xE7\xE3o sugerida",
|
|
7695
|
-
zh: "\u5EFA\u8BAE\u8BC4\u5206",
|
|
7696
|
-
ja: "\u63A8\u5968\u8A55\u4FA1"
|
|
7697
|
-
};
|
|
7698
|
-
var BLOOM_VERBS2 = {
|
|
7699
|
-
1: "Remember",
|
|
7700
|
-
2: "Understand",
|
|
7701
|
-
3: "Apply",
|
|
7702
|
-
4: "Analyze",
|
|
7703
|
-
5: "Synthesize"
|
|
7704
|
-
};
|
|
7705
|
-
var LlmResponseTruncatedError = class extends Error {
|
|
7706
|
-
constructor(label) {
|
|
7707
|
-
super(
|
|
7708
|
-
`${label}: the model's response was cut off before producing usable output (finish_reason=length). The prompt likely left no room in the model's context window for a reply.`
|
|
7709
|
-
);
|
|
7710
|
-
this.name = "LlmResponseTruncatedError";
|
|
7711
|
-
}
|
|
7712
|
-
};
|
|
7713
7548
|
async function readChatContent(res, label) {
|
|
7714
7549
|
if (!res.ok) {
|
|
7715
7550
|
const errorText = await res.text().catch(() => "");
|
|
@@ -7899,8 +7734,6 @@ ${input.sourceLinkContent}` : ""}`;
|
|
|
7899
7734
|
providerName: endpoint.providerName
|
|
7900
7735
|
};
|
|
7901
7736
|
}
|
|
7902
|
-
var MAX_IMPORT_TEXT_CHARS = 2e5;
|
|
7903
|
-
var VALID_GENERATED_MODES = /* @__PURE__ */ new Set(["shadowing", "copilot", "autonomy"]);
|
|
7904
7737
|
function parseGeneratedCardArray(responseText, label, limits) {
|
|
7905
7738
|
const startIdx = responseText.indexOf("[");
|
|
7906
7739
|
const endIdx = responseText.lastIndexOf("]");
|
|
@@ -7962,7 +7795,6 @@ function parseGeneratedCardArray(responseText, label, limits) {
|
|
|
7962
7795
|
};
|
|
7963
7796
|
});
|
|
7964
7797
|
}
|
|
7965
|
-
var CURRICULUM_CHUNK_TEXT_CHARS = 3e3;
|
|
7966
7798
|
function splitCurriculumText(text, maxChars) {
|
|
7967
7799
|
if (text.length <= maxChars) return [text];
|
|
7968
7800
|
const chunks = [];
|
|
@@ -8438,6 +8270,33 @@ async function resolveUsableRecallEndpoint(db) {
|
|
|
8438
8270
|
};
|
|
8439
8271
|
return selected.endpoint;
|
|
8440
8272
|
}
|
|
8273
|
+
async function sampleViaLocalLLM(db, messages) {
|
|
8274
|
+
const cfg = await getProviderForRole(db, "recall");
|
|
8275
|
+
const endpoint = await resolveUsableRecallEndpoint(db);
|
|
8276
|
+
const res = await fetchWithInteractiveTimeout(
|
|
8277
|
+
`${endpoint.url}/chat/completions`,
|
|
8278
|
+
{
|
|
8279
|
+
method: "POST",
|
|
8280
|
+
headers: {
|
|
8281
|
+
"Content-Type": "application/json",
|
|
8282
|
+
Authorization: `Bearer ${endpoint.apiKey}`
|
|
8283
|
+
},
|
|
8284
|
+
body: JSON.stringify({
|
|
8285
|
+
model: endpoint.model,
|
|
8286
|
+
messages,
|
|
8287
|
+
temperature: 0.3,
|
|
8288
|
+
max_tokens: RECALL_DISCUSSION_MAX_OUTPUT_TOKENS
|
|
8289
|
+
}),
|
|
8290
|
+
locale: cfg.locale
|
|
8291
|
+
}
|
|
8292
|
+
);
|
|
8293
|
+
const text = await readChatContent(res, "LLM sampling");
|
|
8294
|
+
return {
|
|
8295
|
+
text,
|
|
8296
|
+
model: endpoint.model,
|
|
8297
|
+
providerName: endpoint.providerName
|
|
8298
|
+
};
|
|
8299
|
+
}
|
|
8441
8300
|
async function resolveUsableTextEndpoint(db) {
|
|
8442
8301
|
const cfg = await getProviderForRole(db, "text");
|
|
8443
8302
|
if (!cfg.enabled) {
|
|
@@ -8493,7 +8352,7 @@ async function prepareRecallChain(db, opts) {
|
|
|
8493
8352
|
} else {
|
|
8494
8353
|
spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
|
|
8495
8354
|
while (Date.now() < deadline) {
|
|
8496
|
-
await new Promise((
|
|
8355
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
8497
8356
|
if (await isLlmOnline(endpoint.url)) {
|
|
8498
8357
|
online = true;
|
|
8499
8358
|
break;
|
|
@@ -8786,7 +8645,7 @@ async function startLocalRunner(url, model, locale, hint) {
|
|
|
8786
8645
|
let attempts = 0;
|
|
8787
8646
|
const dotsPerLine = 30;
|
|
8788
8647
|
while (true) {
|
|
8789
|
-
await new Promise((
|
|
8648
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
8790
8649
|
if (await isLlmOnline(url)) {
|
|
8791
8650
|
if (attempts > 0) process.stdout.write("\n");
|
|
8792
8651
|
return true;
|
|
@@ -8810,123 +8669,833 @@ async function startLocalRunner(url, model, locale, hint) {
|
|
|
8810
8669
|
}
|
|
8811
8670
|
}
|
|
8812
8671
|
}
|
|
8672
|
+
async function ensureLocalLlmRunning(db) {
|
|
8673
|
+
const result = await prepareRecallChain(db, {
|
|
8674
|
+
timeoutMs: 25e3,
|
|
8675
|
+
interactive: true
|
|
8676
|
+
});
|
|
8677
|
+
if (result.usable) {
|
|
8678
|
+
const location = result.local ? "local" : "cloud";
|
|
8679
|
+
console.log(
|
|
8680
|
+
`\x1B[32m\u2713 Recall LLM ready (${location}: ${result.model}).\x1B[0m`
|
|
8681
|
+
);
|
|
8682
|
+
return { usable: true };
|
|
8683
|
+
}
|
|
8684
|
+
if (result.reason === "unsupported-provider") {
|
|
8685
|
+
console.warn(
|
|
8686
|
+
`\x1B[31m\u2717 Recall provider is not supported for active recall.\x1B[0m`
|
|
8687
|
+
);
|
|
8688
|
+
} else if (result.reason === "model-not-found") {
|
|
8689
|
+
console.warn(
|
|
8690
|
+
`\x1B[31m\u2717 Configured model "${result.model}" is not available on the server.\x1B[0m`
|
|
8691
|
+
);
|
|
8692
|
+
console.warn(` Available models: ${result.availableModels.join(", ")}`);
|
|
8693
|
+
} else if (result.reason === "offline") {
|
|
8694
|
+
console.warn(
|
|
8695
|
+
`\x1B[33m\u26A0 No recall LLM endpoint is reachable. Continuing without AI coaching.\x1B[0m
|
|
8696
|
+
`
|
|
8697
|
+
);
|
|
8698
|
+
}
|
|
8699
|
+
return { usable: false, reason: result.reason };
|
|
8700
|
+
}
|
|
8813
8701
|
async function ensureLlmReadyHeadless(db, opts = {}) {
|
|
8814
8702
|
return prepareRecallChain(db, {
|
|
8815
8703
|
timeoutMs: opts.timeoutMs ?? 25e3,
|
|
8816
8704
|
interactive: false
|
|
8817
8705
|
});
|
|
8818
8706
|
}
|
|
8819
|
-
async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
8820
|
-
const {
|
|
8821
|
-
timeoutMs = 2e4,
|
|
8822
|
-
hardTimeoutMs = 12e4,
|
|
8823
|
-
locale = "en",
|
|
8824
|
-
...fetchOptions
|
|
8825
|
-
} = options;
|
|
8826
|
-
const controller = new AbortController();
|
|
8827
|
-
const fetchPromise = fetch(url, {
|
|
8828
|
-
...fetchOptions,
|
|
8829
|
-
signal: controller.signal
|
|
8830
|
-
});
|
|
8831
|
-
if (!process.stdout.isTTY || process.env.ZAM_BRIDGE === "true") {
|
|
8832
|
-
let timeoutId;
|
|
8833
|
-
const hardTimeout = new Promise((_resolve, reject) => {
|
|
8834
|
-
timeoutId = setTimeout(() => {
|
|
8835
|
-
reject(new Error(`LLM request timed out after ${hardTimeoutMs}ms`));
|
|
8836
|
-
controller.abort();
|
|
8837
|
-
}, hardTimeoutMs);
|
|
8838
|
-
});
|
|
8839
|
-
try {
|
|
8840
|
-
return await Promise.race([fetchPromise, hardTimeout]);
|
|
8841
|
-
} finally {
|
|
8842
|
-
clearTimeout(timeoutId);
|
|
8843
|
-
}
|
|
8707
|
+
async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
8708
|
+
const {
|
|
8709
|
+
timeoutMs = 2e4,
|
|
8710
|
+
hardTimeoutMs = 12e4,
|
|
8711
|
+
locale = "en",
|
|
8712
|
+
...fetchOptions
|
|
8713
|
+
} = options;
|
|
8714
|
+
const controller = new AbortController();
|
|
8715
|
+
const fetchPromise = fetch(url, {
|
|
8716
|
+
...fetchOptions,
|
|
8717
|
+
signal: controller.signal
|
|
8718
|
+
});
|
|
8719
|
+
if (!process.stdout.isTTY || process.env.ZAM_BRIDGE === "true") {
|
|
8720
|
+
let timeoutId;
|
|
8721
|
+
const hardTimeout = new Promise((_resolve, reject) => {
|
|
8722
|
+
timeoutId = setTimeout(() => {
|
|
8723
|
+
reject(new Error(`LLM request timed out after ${hardTimeoutMs}ms`));
|
|
8724
|
+
controller.abort();
|
|
8725
|
+
}, hardTimeoutMs);
|
|
8726
|
+
});
|
|
8727
|
+
try {
|
|
8728
|
+
return await Promise.race([fetchPromise, hardTimeout]);
|
|
8729
|
+
} finally {
|
|
8730
|
+
clearTimeout(timeoutId);
|
|
8731
|
+
}
|
|
8732
|
+
}
|
|
8733
|
+
let attempts = 0;
|
|
8734
|
+
const dotsPerLine = 30;
|
|
8735
|
+
while (true) {
|
|
8736
|
+
let timeoutId;
|
|
8737
|
+
const timeoutPromise = new Promise((resolve8) => {
|
|
8738
|
+
timeoutId = setTimeout(() => resolve8("timeout"), timeoutMs);
|
|
8739
|
+
});
|
|
8740
|
+
const dotsInterval = setInterval(() => {
|
|
8741
|
+
process.stdout.write(".");
|
|
8742
|
+
attempts++;
|
|
8743
|
+
if (attempts % dotsPerLine === 0) process.stdout.write("\n");
|
|
8744
|
+
}, 500);
|
|
8745
|
+
try {
|
|
8746
|
+
const result = await Promise.race([fetchPromise, timeoutPromise]);
|
|
8747
|
+
clearInterval(dotsInterval);
|
|
8748
|
+
clearTimeout(timeoutId);
|
|
8749
|
+
if (result !== "timeout") {
|
|
8750
|
+
if (attempts > 0) process.stdout.write("\n");
|
|
8751
|
+
return result;
|
|
8752
|
+
}
|
|
8753
|
+
console.log(`
|
|
8754
|
+
\x1B[33m${t(locale, "local_ai_working")}\x1B[0m`);
|
|
8755
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
8756
|
+
const keepWaiting = await confirm({
|
|
8757
|
+
message: t(locale, "keep_waiting"),
|
|
8758
|
+
default: true
|
|
8759
|
+
}).catch(() => false);
|
|
8760
|
+
if (!keepWaiting) {
|
|
8761
|
+
controller.abort();
|
|
8762
|
+
console.log(`\x1B[33m${t(locale, "proceeding_offline")}\x1B[0m
|
|
8763
|
+
`);
|
|
8764
|
+
throw new Error("User cancelled waiting for slow LLM response");
|
|
8765
|
+
}
|
|
8766
|
+
attempts = 0;
|
|
8767
|
+
} catch (err) {
|
|
8768
|
+
clearInterval(dotsInterval);
|
|
8769
|
+
clearTimeout(timeoutId);
|
|
8770
|
+
if (attempts > 0) process.stdout.write("\n");
|
|
8771
|
+
throw err;
|
|
8772
|
+
}
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
async function ensureHighQualityQuestion(db, token) {
|
|
8776
|
+
const { enabled } = await getLlmConfig(db);
|
|
8777
|
+
const variationEnabled = await getSetting(db, "llm.dynamic_questions") !== "false";
|
|
8778
|
+
if (enabled && variationEnabled) {
|
|
8779
|
+
try {
|
|
8780
|
+
let sourceLinkContent = null;
|
|
8781
|
+
if (token.sourceLink) {
|
|
8782
|
+
const resolved = await resolveReviewContext(token.sourceLink).catch(
|
|
8783
|
+
() => null
|
|
8784
|
+
);
|
|
8785
|
+
if (resolved) {
|
|
8786
|
+
sourceLinkContent = resolved.content;
|
|
8787
|
+
}
|
|
8788
|
+
}
|
|
8789
|
+
const generated = await generateQuestionViaLLM(db, {
|
|
8790
|
+
slug: token.slug,
|
|
8791
|
+
concept: token.concept,
|
|
8792
|
+
domain: token.domain,
|
|
8793
|
+
bloomLevel: token.bloomLevel,
|
|
8794
|
+
sourceLinkContent,
|
|
8795
|
+
existingQuestion: token.question
|
|
8796
|
+
});
|
|
8797
|
+
if (generated.text.trim().length > 0) {
|
|
8798
|
+
return {
|
|
8799
|
+
question: generated.text,
|
|
8800
|
+
source: "llm",
|
|
8801
|
+
model: generated.model
|
|
8802
|
+
};
|
|
8803
|
+
}
|
|
8804
|
+
} catch {
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8807
|
+
if (token.question && token.question.trim().length > 0) {
|
|
8808
|
+
return {
|
|
8809
|
+
question: token.question.trim(),
|
|
8810
|
+
source: "original"
|
|
8811
|
+
};
|
|
8812
|
+
}
|
|
8813
|
+
return null;
|
|
8814
|
+
}
|
|
8815
|
+
async function generateTitleViaLLM(db, input, opts = {}) {
|
|
8816
|
+
const cfg = await getProviderForRole(db, "text");
|
|
8817
|
+
let locale = cfg.locale;
|
|
8818
|
+
const contextName = opts.knowledgeContext || getActiveWorkspaceContext();
|
|
8819
|
+
if (contextName) {
|
|
8820
|
+
const context = await getKnowledgeContextByName(db, contextName);
|
|
8821
|
+
if (context?.language) {
|
|
8822
|
+
locale = normalizeLocale(context.language);
|
|
8823
|
+
}
|
|
8824
|
+
}
|
|
8825
|
+
const endpoint = await resolveUsableTextEndpoint(db);
|
|
8826
|
+
const systemPrompt = `You are an expert at naming knowledge items for a personal knowledge graph.
|
|
8827
|
+
Your task: given a knowledge token's concept (the full reference answer), question, domain, and optional context, produce a short, descriptive, human-friendly TITLE.
|
|
8828
|
+
|
|
8829
|
+
Strict rules from the project ADR:
|
|
8830
|
+
- Concise name for the concept (\u2264 80 chars ideal).
|
|
8831
|
+
- Thoughtful, memorable name \u2014 NOT a definition or the first sentence of the concept.
|
|
8832
|
+
- NEVER echo the domain name (e.g. do not say "Workflow Engine Node Drain" if domain is "workflow-engine"; just "Node Drain Protection").
|
|
8833
|
+
- Prefer a name over spoiling the full concept.
|
|
8834
|
+
- Support Unicode (umlauts, etc.).
|
|
8835
|
+
- Output the title in the same language as the concept/content (e.g., German if the concept is German, English if the concept is English).
|
|
8836
|
+
- Output ONLY the raw title text. No preamble, quotes, explanations, or markdown.
|
|
8837
|
+
|
|
8838
|
+
Example good titles:
|
|
8839
|
+
- "RAG Retrieval Quality Evaluation" (for RAG failure modes focused on retrieval)
|
|
8840
|
+
- "Pythagorean Theorem Converse Proof"
|
|
8841
|
+
- "macOS Applications Directory Layout"
|
|
8842
|
+
- "Chronotype Sleep Preference"`;
|
|
8843
|
+
const userPrompt = `Domain: ${input.domain}
|
|
8844
|
+
Slug: ${input.slug}
|
|
8845
|
+
Question: ${input.question || "(none)"}
|
|
8846
|
+
Concept: ${input.concept}
|
|
8847
|
+
Context: ${input.context || "(none)"}
|
|
8848
|
+
|
|
8849
|
+
Title:`;
|
|
8850
|
+
const url = `${endpoint.url}/chat/completions`;
|
|
8851
|
+
const apiKey = endpoint.apiKey;
|
|
8852
|
+
const model = endpoint.model;
|
|
8853
|
+
try {
|
|
8854
|
+
const res = await fetchWithInteractiveTimeout(url, {
|
|
8855
|
+
method: "POST",
|
|
8856
|
+
headers: {
|
|
8857
|
+
"Content-Type": "application/json",
|
|
8858
|
+
Authorization: `Bearer ${apiKey}`
|
|
8859
|
+
},
|
|
8860
|
+
body: JSON.stringify({
|
|
8861
|
+
model,
|
|
8862
|
+
messages: [
|
|
8863
|
+
{ role: "system", content: systemPrompt },
|
|
8864
|
+
{ role: "user", content: userPrompt }
|
|
8865
|
+
],
|
|
8866
|
+
temperature: 0.2,
|
|
8867
|
+
max_tokens: 100
|
|
8868
|
+
}),
|
|
8869
|
+
locale,
|
|
8870
|
+
timeoutMs: opts.timeoutMs,
|
|
8871
|
+
hardTimeoutMs: opts.timeoutMs
|
|
8872
|
+
});
|
|
8873
|
+
const text = await readChatContent(res, "title generation");
|
|
8874
|
+
return {
|
|
8875
|
+
text: text.trim(),
|
|
8876
|
+
model,
|
|
8877
|
+
providerName: endpoint.providerName
|
|
8878
|
+
};
|
|
8879
|
+
} catch (_e) {
|
|
8880
|
+
return {
|
|
8881
|
+
text: input.concept.split(/[.;]/)[0].trim().substring(0, 80),
|
|
8882
|
+
model: "fallback",
|
|
8883
|
+
providerName: "heuristic"
|
|
8884
|
+
};
|
|
8885
|
+
}
|
|
8886
|
+
}
|
|
8887
|
+
async function repairUmlautsViaLLM(db, input, opts = {}) {
|
|
8888
|
+
const cfg = await getProviderForRole(db, "text");
|
|
8889
|
+
const endpoint = await resolveUsableTextEndpoint(db);
|
|
8890
|
+
const systemPrompt = `You are an expert editor specializing in German language orthography.
|
|
8891
|
+
Your task: given a text that may contain legacy ASCII-folded German umlauts (like 'ue' instead of '\xFC', 'ae' instead of '\xE4', 'oe' instead of '\xF6', 'Ue' instead of '\xDC', etc.), repair them back to proper German umlauts (\xE4, \xF6, \xFC, \xC4, \xD6, \xDC, \xDF) where appropriate.
|
|
8892
|
+
|
|
8893
|
+
Strict rules:
|
|
8894
|
+
- ONLY change character sequences that are clearly meant to be German umlauts (e.g. change "Ueber" to "\xDCber", "fuer" to "f\xFCr", "moeglich" to "m\xF6glich").
|
|
8895
|
+
- DO NOT change words that are correct in their current form (e.g. do NOT change "nahe" to "n\xE4he", do NOT change English words or technical terms, do NOT change names like "Ueda").
|
|
8896
|
+
- Output ONLY the repaired text. No preamble, no explanation, no markdown formatting.`;
|
|
8897
|
+
const userPrompt = `Text to repair:
|
|
8898
|
+
"""
|
|
8899
|
+
${input.text}
|
|
8900
|
+
"""
|
|
8901
|
+
|
|
8902
|
+
Repaired text:`;
|
|
8903
|
+
const url = `${endpoint.url}/chat/completions`;
|
|
8904
|
+
const apiKey = endpoint.apiKey;
|
|
8905
|
+
const model = endpoint.model;
|
|
8906
|
+
try {
|
|
8907
|
+
const res = await fetchWithInteractiveTimeout(url, {
|
|
8908
|
+
method: "POST",
|
|
8909
|
+
headers: {
|
|
8910
|
+
"Content-Type": "application/json",
|
|
8911
|
+
Authorization: `Bearer ${apiKey}`
|
|
8912
|
+
},
|
|
8913
|
+
body: JSON.stringify({
|
|
8914
|
+
model,
|
|
8915
|
+
messages: [
|
|
8916
|
+
{ role: "system", content: systemPrompt },
|
|
8917
|
+
{ role: "user", content: userPrompt }
|
|
8918
|
+
],
|
|
8919
|
+
temperature: 0.1
|
|
8920
|
+
}),
|
|
8921
|
+
locale: cfg.locale,
|
|
8922
|
+
timeoutMs: opts.timeoutMs,
|
|
8923
|
+
hardTimeoutMs: opts.timeoutMs
|
|
8924
|
+
});
|
|
8925
|
+
const text = await readChatContent(res, "umlaut repair");
|
|
8926
|
+
return text.trim();
|
|
8927
|
+
} catch (_e) {
|
|
8928
|
+
return input.text;
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
var DEFAULT_LLM_URL, DEFAULT_LLM_MAX_TOKENS, LOCAL_CURRICULUM_IMPORT_HARD_TIMEOUT_MS, CLOUD_CURRICULUM_IMPORT_HARD_TIMEOUT_MS, RECALL_QUESTION_MAX_OUTPUT_TOKENS, RECALL_EVALUATION_MAX_OUTPUT_TOKENS, RECALL_DISCUSSION_MAX_OUTPUT_TOKENS, RECALL_ENDPOINT_CACHE_MS, cachedRecallEndpoint, DEFAULT_LLM_MODEL, DEFAULT_LLM_API_KEY, ROLE_TO_CAPABILITY2, LANGUAGE_NAMES, LOCALIZED_RATING_PREFIX, BLOOM_VERBS2, LlmResponseTruncatedError, MAX_IMPORT_TEXT_CHARS, VALID_GENERATED_MODES, CURRICULUM_CHUNK_TEXT_CHARS;
|
|
8932
|
+
var init_client = __esm({
|
|
8933
|
+
"src/cli/llm/client.ts"() {
|
|
8934
|
+
"use strict";
|
|
8935
|
+
init_kernel();
|
|
8936
|
+
DEFAULT_LLM_URL = "http://localhost:8000/v1";
|
|
8937
|
+
DEFAULT_LLM_MAX_TOKENS = 1e4;
|
|
8938
|
+
LOCAL_CURRICULUM_IMPORT_HARD_TIMEOUT_MS = 6e5;
|
|
8939
|
+
CLOUD_CURRICULUM_IMPORT_HARD_TIMEOUT_MS = 18e4;
|
|
8940
|
+
RECALL_QUESTION_MAX_OUTPUT_TOKENS = 400;
|
|
8941
|
+
RECALL_EVALUATION_MAX_OUTPUT_TOKENS = 1200;
|
|
8942
|
+
RECALL_DISCUSSION_MAX_OUTPUT_TOKENS = 1200;
|
|
8943
|
+
RECALL_ENDPOINT_CACHE_MS = 6e4;
|
|
8944
|
+
cachedRecallEndpoint = null;
|
|
8945
|
+
DEFAULT_LLM_MODEL = "qwen3.5:4b";
|
|
8946
|
+
DEFAULT_LLM_API_KEY = "sk-none";
|
|
8947
|
+
ROLE_TO_CAPABILITY2 = {
|
|
8948
|
+
recall: "text",
|
|
8949
|
+
text: "text",
|
|
8950
|
+
vision: "image",
|
|
8951
|
+
embedding: "embedding"
|
|
8952
|
+
};
|
|
8953
|
+
LANGUAGE_NAMES = {
|
|
8954
|
+
en: "English",
|
|
8955
|
+
de: "German",
|
|
8956
|
+
es: "Spanish",
|
|
8957
|
+
fr: "French",
|
|
8958
|
+
pt: "Portuguese",
|
|
8959
|
+
zh: "Chinese",
|
|
8960
|
+
ja: "Japanese"
|
|
8961
|
+
};
|
|
8962
|
+
LOCALIZED_RATING_PREFIX = {
|
|
8963
|
+
en: "Suggested rating",
|
|
8964
|
+
de: "Empfohlene Bewertung",
|
|
8965
|
+
es: "Calificaci\xF3n sugerida",
|
|
8966
|
+
fr: "Note sugg\xE9r\xE9e",
|
|
8967
|
+
pt: "Avalia\xE7\xE3o sugerida",
|
|
8968
|
+
zh: "\u5EFA\u8BAE\u8BC4\u5206",
|
|
8969
|
+
ja: "\u63A8\u5968\u8A55\u4FA1"
|
|
8970
|
+
};
|
|
8971
|
+
BLOOM_VERBS2 = {
|
|
8972
|
+
1: "Remember",
|
|
8973
|
+
2: "Understand",
|
|
8974
|
+
3: "Apply",
|
|
8975
|
+
4: "Analyze",
|
|
8976
|
+
5: "Synthesize"
|
|
8977
|
+
};
|
|
8978
|
+
LlmResponseTruncatedError = class extends Error {
|
|
8979
|
+
constructor(label) {
|
|
8980
|
+
super(
|
|
8981
|
+
`${label}: the model's response was cut off before producing usable output (finish_reason=length). The prompt likely left no room in the model's context window for a reply.`
|
|
8982
|
+
);
|
|
8983
|
+
this.name = "LlmResponseTruncatedError";
|
|
8984
|
+
}
|
|
8985
|
+
};
|
|
8986
|
+
MAX_IMPORT_TEXT_CHARS = 2e5;
|
|
8987
|
+
VALID_GENERATED_MODES = /* @__PURE__ */ new Set(["shadowing", "copilot", "autonomy"]);
|
|
8988
|
+
CURRICULUM_CHUNK_TEXT_CHARS = 3e3;
|
|
8989
|
+
}
|
|
8990
|
+
});
|
|
8991
|
+
|
|
8992
|
+
// src/cli/okf/bundle.ts
|
|
8993
|
+
var bundle_exports = {};
|
|
8994
|
+
__export(bundle_exports, {
|
|
8995
|
+
OKF_VERSION: () => OKF_VERSION,
|
|
8996
|
+
RESERVED_FILES: () => RESERVED_FILES,
|
|
8997
|
+
appendLog: () => appendLog,
|
|
8998
|
+
buildCatalog: () => buildCatalog,
|
|
8999
|
+
isReservedFile: () => isReservedFile,
|
|
9000
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
9001
|
+
renderIndex: () => renderIndex,
|
|
9002
|
+
toCatalogEntry: () => toCatalogEntry,
|
|
9003
|
+
validateArticle: () => validateArticle
|
|
9004
|
+
});
|
|
9005
|
+
function isReservedFile(file) {
|
|
9006
|
+
return RESERVED_FILES.includes(file);
|
|
9007
|
+
}
|
|
9008
|
+
function unquote(raw) {
|
|
9009
|
+
const v = raw.trim();
|
|
9010
|
+
if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
|
|
9011
|
+
return v.slice(1, -1);
|
|
9012
|
+
}
|
|
9013
|
+
return v;
|
|
9014
|
+
}
|
|
9015
|
+
function parseFrontmatter(markdown) {
|
|
9016
|
+
const lines = markdown.split("\n");
|
|
9017
|
+
if (lines[0]?.trim() !== "---") {
|
|
9018
|
+
throw new Error("frontmatter: file must start with a --- fence");
|
|
9019
|
+
}
|
|
9020
|
+
const fields = {};
|
|
9021
|
+
let listKey = null;
|
|
9022
|
+
let i = 1;
|
|
9023
|
+
for (; i < lines.length; i++) {
|
|
9024
|
+
const line = lines[i];
|
|
9025
|
+
if (line.trim() === "---") break;
|
|
9026
|
+
if (line.trim() === "") continue;
|
|
9027
|
+
const listItem = /^\s+-\s+(.+)$/.exec(line);
|
|
9028
|
+
if (listItem) {
|
|
9029
|
+
if (!listKey) {
|
|
9030
|
+
throw new Error(`frontmatter line ${i + 1}: list item without a key`);
|
|
9031
|
+
}
|
|
9032
|
+
fields[listKey].push(unquote(listItem[1]));
|
|
9033
|
+
continue;
|
|
9034
|
+
}
|
|
9035
|
+
const pair = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
|
|
9036
|
+
if (!pair) {
|
|
9037
|
+
throw new Error(
|
|
9038
|
+
`frontmatter line ${i + 1}: expected "key: value" or "- item"`
|
|
9039
|
+
);
|
|
9040
|
+
}
|
|
9041
|
+
const key = pair[1];
|
|
9042
|
+
const rest = pair[2].trim();
|
|
9043
|
+
if (rest === "") {
|
|
9044
|
+
fields[key] = [];
|
|
9045
|
+
listKey = key;
|
|
9046
|
+
} else {
|
|
9047
|
+
fields[key] = unquote(rest);
|
|
9048
|
+
listKey = null;
|
|
9049
|
+
}
|
|
9050
|
+
}
|
|
9051
|
+
if (i >= lines.length) {
|
|
9052
|
+
throw new Error("frontmatter: missing closing --- fence");
|
|
9053
|
+
}
|
|
9054
|
+
return { fields, body: lines.slice(i + 1).join("\n") };
|
|
9055
|
+
}
|
|
9056
|
+
function scalar(fields, key) {
|
|
9057
|
+
const v = fields[key];
|
|
9058
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
9059
|
+
}
|
|
9060
|
+
function validateArticle(file, markdown) {
|
|
9061
|
+
const problems = [];
|
|
9062
|
+
if (isReservedFile(file)) {
|
|
9063
|
+
return { ok: false, problems: [`${file}: reserved OKF file name`] };
|
|
9064
|
+
}
|
|
9065
|
+
if (!FILE_NAME_RE.test(file)) {
|
|
9066
|
+
problems.push(`${file}: file name must be kebab-case and end in .md`);
|
|
9067
|
+
}
|
|
9068
|
+
let parsed = null;
|
|
9069
|
+
try {
|
|
9070
|
+
parsed = parseFrontmatter(markdown);
|
|
9071
|
+
} catch (err) {
|
|
9072
|
+
problems.push(
|
|
9073
|
+
`${file}: ${err instanceof Error ? err.message : String(err)}`
|
|
9074
|
+
);
|
|
9075
|
+
}
|
|
9076
|
+
if (parsed) {
|
|
9077
|
+
if (!scalar(parsed.fields, "type")) {
|
|
9078
|
+
problems.push(`${file}: frontmatter field "type" is required`);
|
|
9079
|
+
}
|
|
9080
|
+
if (!scalar(parsed.fields, "description")) {
|
|
9081
|
+
problems.push(`${file}: frontmatter field "description" is required`);
|
|
9082
|
+
}
|
|
9083
|
+
if (parsed.body.trim() === "") {
|
|
9084
|
+
problems.push(`${file}: article body is empty`);
|
|
9085
|
+
}
|
|
9086
|
+
}
|
|
9087
|
+
return { ok: problems.length === 0, problems };
|
|
9088
|
+
}
|
|
9089
|
+
function toCatalogEntry(file, markdown) {
|
|
9090
|
+
const { fields } = parseFrontmatter(markdown);
|
|
9091
|
+
const tags = Array.isArray(fields.tags) ? fields.tags : [];
|
|
9092
|
+
return {
|
|
9093
|
+
file,
|
|
9094
|
+
type: scalar(fields, "type") ?? "",
|
|
9095
|
+
title: scalar(fields, "title") ?? file.replace(/\.md$/, ""),
|
|
9096
|
+
description: scalar(fields, "description") ?? "",
|
|
9097
|
+
tags,
|
|
9098
|
+
resource: scalar(fields, "resource"),
|
|
9099
|
+
timestamp: scalar(fields, "timestamp")
|
|
9100
|
+
};
|
|
9101
|
+
}
|
|
9102
|
+
function buildCatalog(articles) {
|
|
9103
|
+
return articles.map(({ file, markdown }) => toCatalogEntry(file, markdown)).sort((a, b) => a.file.localeCompare(b.file));
|
|
9104
|
+
}
|
|
9105
|
+
function renderIndex(catalog, okfVersion = OKF_VERSION) {
|
|
9106
|
+
const types = [...new Set(catalog.map((e) => e.type))].sort();
|
|
9107
|
+
const sections = types.map((type) => {
|
|
9108
|
+
const rows = catalog.filter((e) => e.type === type).map((e) => `- [${e.title}](${e.file}) \u2014 ${e.description}`).join("\n");
|
|
9109
|
+
return `## ${type}
|
|
9110
|
+
|
|
9111
|
+
${rows}`;
|
|
9112
|
+
});
|
|
9113
|
+
return [
|
|
9114
|
+
"---",
|
|
9115
|
+
`okf_version: "${okfVersion}"`,
|
|
9116
|
+
"---",
|
|
9117
|
+
"",
|
|
9118
|
+
"# ZAM Knowledge Base",
|
|
9119
|
+
"",
|
|
9120
|
+
"Living reference knowledge for this repository in",
|
|
9121
|
+
"[Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog).",
|
|
9122
|
+
"Current truth only \u2014 the *why* behind it lives in [../adr/](../adr/)",
|
|
9123
|
+
"(ADR 2026-07-17). Do not edit by hand: write through the",
|
|
9124
|
+
"`zam_okf_upsert` MCP tool.",
|
|
9125
|
+
"",
|
|
9126
|
+
sections.join("\n\n"),
|
|
9127
|
+
""
|
|
9128
|
+
].join("\n");
|
|
9129
|
+
}
|
|
9130
|
+
function appendLog(existing, date, line) {
|
|
9131
|
+
const header = `## ${date}`;
|
|
9132
|
+
const entry = `- ${line}`;
|
|
9133
|
+
const trimmed = existing.trim();
|
|
9134
|
+
if (trimmed === "") {
|
|
9135
|
+
return `# Log
|
|
9136
|
+
|
|
9137
|
+
${header}
|
|
9138
|
+
|
|
9139
|
+
${entry}
|
|
9140
|
+
`;
|
|
9141
|
+
}
|
|
9142
|
+
const lines = trimmed.split("\n");
|
|
9143
|
+
const firstHeaderIdx = lines.findIndex((l) => l.startsWith("## "));
|
|
9144
|
+
if (firstHeaderIdx !== -1 && lines[firstHeaderIdx].trim() === header) {
|
|
9145
|
+
lines.splice(firstHeaderIdx + 2, 0, entry);
|
|
9146
|
+
return `${lines.join("\n")}
|
|
9147
|
+
`;
|
|
9148
|
+
}
|
|
9149
|
+
const insertAt = firstHeaderIdx === -1 ? lines.length : firstHeaderIdx;
|
|
9150
|
+
lines.splice(insertAt, 0, header, "", entry, "");
|
|
9151
|
+
return `${lines.join("\n")}
|
|
9152
|
+
`;
|
|
9153
|
+
}
|
|
9154
|
+
var OKF_VERSION, RESERVED_FILES, FILE_NAME_RE;
|
|
9155
|
+
var init_bundle = __esm({
|
|
9156
|
+
"src/cli/okf/bundle.ts"() {
|
|
9157
|
+
"use strict";
|
|
9158
|
+
OKF_VERSION = "0.1";
|
|
9159
|
+
RESERVED_FILES = ["index.md", "log.md"];
|
|
9160
|
+
FILE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*\.md$/;
|
|
9161
|
+
}
|
|
9162
|
+
});
|
|
9163
|
+
|
|
9164
|
+
// src/cli/okf/io.ts
|
|
9165
|
+
var io_exports = {};
|
|
9166
|
+
__export(io_exports, {
|
|
9167
|
+
DEFAULT_BUNDLE_DIR: () => DEFAULT_BUNDLE_DIR,
|
|
9168
|
+
loadBundle: () => loadBundle,
|
|
9169
|
+
resolveArticlePath: () => resolveArticlePath,
|
|
9170
|
+
upsertArticle: () => upsertArticle
|
|
9171
|
+
});
|
|
9172
|
+
import { mkdirSync as mkdirSync16, readdirSync as readdirSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
|
|
9173
|
+
import { join as join24, resolve as resolve7 } from "path";
|
|
9174
|
+
function resolveArticlePath(dir, file) {
|
|
9175
|
+
if (file.includes("/") || file.includes("\\") || file.includes("..")) {
|
|
9176
|
+
throw new Error(`invalid article file name: ${file}`);
|
|
9177
|
+
}
|
|
9178
|
+
if (isReservedFile(file)) {
|
|
9179
|
+
throw new Error(`refusing to address reserved file: ${file}`);
|
|
9180
|
+
}
|
|
9181
|
+
return join24(resolve7(dir), file);
|
|
9182
|
+
}
|
|
9183
|
+
function loadBundle(dir) {
|
|
9184
|
+
const root = resolve7(dir);
|
|
9185
|
+
let entries;
|
|
9186
|
+
try {
|
|
9187
|
+
entries = readdirSync3(root).filter(
|
|
9188
|
+
(name) => name.endsWith(".md") && !isReservedFile(name)
|
|
9189
|
+
);
|
|
9190
|
+
} catch {
|
|
9191
|
+
throw new Error(`OKF bundle directory not found: ${root}`);
|
|
9192
|
+
}
|
|
9193
|
+
const articles = entries.sort().map((file) => ({
|
|
9194
|
+
file,
|
|
9195
|
+
markdown: readFileSync18(join24(root, file), "utf8")
|
|
9196
|
+
}));
|
|
9197
|
+
const problems = articles.flatMap(
|
|
9198
|
+
({ file, markdown }) => validateArticle(file, markdown).problems
|
|
9199
|
+
);
|
|
9200
|
+
const catalog = problems.length === 0 ? buildCatalog(articles) : safeCatalog(articles);
|
|
9201
|
+
return { dir: root, articles, catalog, problems };
|
|
9202
|
+
}
|
|
9203
|
+
function safeCatalog(articles) {
|
|
9204
|
+
const entries = [];
|
|
9205
|
+
for (const { file, markdown } of articles) {
|
|
9206
|
+
try {
|
|
9207
|
+
entries.push(toCatalogEntry(file, markdown));
|
|
9208
|
+
} catch {
|
|
9209
|
+
}
|
|
9210
|
+
}
|
|
9211
|
+
return entries.sort((a, b) => a.file.localeCompare(b.file));
|
|
9212
|
+
}
|
|
9213
|
+
function upsertArticle(dir, file, markdown, today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
9214
|
+
const target = resolveArticlePath(dir, file);
|
|
9215
|
+
const validation = validateArticle(file, markdown);
|
|
9216
|
+
if (!validation.ok) return { validation };
|
|
9217
|
+
const root = resolve7(dir);
|
|
9218
|
+
mkdirSync16(root, { recursive: true });
|
|
9219
|
+
let created = true;
|
|
9220
|
+
try {
|
|
9221
|
+
readFileSync18(target, "utf8");
|
|
9222
|
+
created = false;
|
|
9223
|
+
} catch {
|
|
9224
|
+
}
|
|
9225
|
+
writeFileSync13(target, markdown, "utf8");
|
|
9226
|
+
const bundle = loadBundle(root);
|
|
9227
|
+
writeFileSync13(join24(root, "index.md"), renderIndex(bundle.catalog), "utf8");
|
|
9228
|
+
let log = "";
|
|
9229
|
+
try {
|
|
9230
|
+
log = readFileSync18(join24(root, "log.md"), "utf8");
|
|
9231
|
+
} catch {
|
|
9232
|
+
}
|
|
9233
|
+
const entry = bundle.catalog.find((e) => e.file === file);
|
|
9234
|
+
writeFileSync13(
|
|
9235
|
+
join24(root, "log.md"),
|
|
9236
|
+
appendLog(
|
|
9237
|
+
log,
|
|
9238
|
+
today,
|
|
9239
|
+
`**${created ? "Creation" : "Update"}** \u2014 [${entry?.title ?? file}](${file})`
|
|
9240
|
+
),
|
|
9241
|
+
"utf8"
|
|
9242
|
+
);
|
|
9243
|
+
return { validation, entry, created };
|
|
9244
|
+
}
|
|
9245
|
+
var DEFAULT_BUNDLE_DIR;
|
|
9246
|
+
var init_io = __esm({
|
|
9247
|
+
"src/cli/okf/io.ts"() {
|
|
9248
|
+
"use strict";
|
|
9249
|
+
init_bundle();
|
|
9250
|
+
DEFAULT_BUNDLE_DIR = "docs/okf";
|
|
9251
|
+
}
|
|
9252
|
+
});
|
|
9253
|
+
|
|
9254
|
+
// src/cli/commands/mcp.ts
|
|
9255
|
+
init_kernel();
|
|
9256
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
|
|
9257
|
+
import { dirname as dirname11, join as join25 } from "path";
|
|
9258
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
9259
|
+
import {
|
|
9260
|
+
RESOURCE_MIME_TYPE,
|
|
9261
|
+
registerAppResource,
|
|
9262
|
+
registerAppTool
|
|
9263
|
+
} from "@modelcontextprotocol/ext-apps/server";
|
|
9264
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9265
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9266
|
+
import { z } from "zod";
|
|
9267
|
+
|
|
9268
|
+
// src/vscode-extension/companion-evaluator.ts
|
|
9269
|
+
var ROUTABLE_EVALUATOR_IDS = [
|
|
9270
|
+
"native-mcp-host",
|
|
9271
|
+
"vscode-lm",
|
|
9272
|
+
"zam-text-model",
|
|
9273
|
+
"quick-mode"
|
|
9274
|
+
];
|
|
9275
|
+
var DETACHED_HARNESS_EVALUATOR_IDS = [
|
|
9276
|
+
"claude-code",
|
|
9277
|
+
"codex",
|
|
9278
|
+
"opencode",
|
|
9279
|
+
"goose"
|
|
9280
|
+
];
|
|
9281
|
+
function isEvaluatorId(value) {
|
|
9282
|
+
return typeof value === "string" && (ROUTABLE_EVALUATOR_IDS.includes(value) || DETACHED_HARNESS_EVALUATOR_IDS.includes(value));
|
|
9283
|
+
}
|
|
9284
|
+
function buildEvaluatorRoutes(inputs, selectedId) {
|
|
9285
|
+
return inputs.map((input) => {
|
|
9286
|
+
if (!input.routable && !input.reason) {
|
|
9287
|
+
throw new Error(
|
|
9288
|
+
`Unroutable evaluator "${input.id}" must carry an availability reason`
|
|
9289
|
+
);
|
|
9290
|
+
}
|
|
9291
|
+
return { ...input, selected: input.id === selectedId, active: false };
|
|
9292
|
+
});
|
|
9293
|
+
}
|
|
9294
|
+
var EvaluatorUnavailableError = class extends Error {
|
|
9295
|
+
evaluatorId;
|
|
9296
|
+
constructor(evaluatorId, reason) {
|
|
9297
|
+
super(`Evaluator "${evaluatorId}" is unavailable: ${reason}`);
|
|
9298
|
+
this.name = "EvaluatorUnavailableError";
|
|
9299
|
+
this.evaluatorId = evaluatorId;
|
|
9300
|
+
}
|
|
9301
|
+
};
|
|
9302
|
+
function activateSelectedEvaluator(routes, selectedId) {
|
|
9303
|
+
const route = routes.find((candidate) => candidate.id === selectedId);
|
|
9304
|
+
if (!route) {
|
|
9305
|
+
throw new EvaluatorUnavailableError(selectedId, "not configured");
|
|
9306
|
+
}
|
|
9307
|
+
if (!route.routable) {
|
|
9308
|
+
throw new EvaluatorUnavailableError(
|
|
9309
|
+
selectedId,
|
|
9310
|
+
route.reason ?? "not routable on this surface"
|
|
9311
|
+
);
|
|
9312
|
+
}
|
|
9313
|
+
return { ...route, active: true };
|
|
9314
|
+
}
|
|
9315
|
+
|
|
9316
|
+
// src/vscode-extension/companion-selection.ts
|
|
9317
|
+
function resolveSelection(candidates) {
|
|
9318
|
+
if (candidates.invocation !== void 0) {
|
|
9319
|
+
return {
|
|
9320
|
+
value: candidates.invocation,
|
|
9321
|
+
source: "invocation",
|
|
9322
|
+
sessionScoped: true
|
|
9323
|
+
};
|
|
9324
|
+
}
|
|
9325
|
+
if (candidates.manual !== void 0) {
|
|
9326
|
+
return { value: candidates.manual, source: "manual", sessionScoped: false };
|
|
9327
|
+
}
|
|
9328
|
+
if (candidates.persisted !== void 0) {
|
|
9329
|
+
return {
|
|
9330
|
+
value: candidates.persisted,
|
|
9331
|
+
source: "persisted",
|
|
9332
|
+
sessionScoped: false
|
|
9333
|
+
};
|
|
9334
|
+
}
|
|
9335
|
+
return {
|
|
9336
|
+
value: candidates.fallback,
|
|
9337
|
+
source: "default",
|
|
9338
|
+
sessionScoped: false
|
|
9339
|
+
};
|
|
9340
|
+
}
|
|
9341
|
+
function isPersistableSelection(result) {
|
|
9342
|
+
return result.source === "manual";
|
|
9343
|
+
}
|
|
9344
|
+
|
|
9345
|
+
// src/vscode-extension/companion-context.ts
|
|
9346
|
+
var COMPANION_SURFACES = [
|
|
9347
|
+
"recall",
|
|
9348
|
+
"graph",
|
|
9349
|
+
"settings",
|
|
9350
|
+
"studio"
|
|
9351
|
+
];
|
|
9352
|
+
function isCompanionSurface(value) {
|
|
9353
|
+
return typeof value === "string" && COMPANION_SURFACES.includes(value);
|
|
9354
|
+
}
|
|
9355
|
+
function isRecord2(value) {
|
|
9356
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9357
|
+
}
|
|
9358
|
+
var KNOWN_NATIVE_HOSTS = {
|
|
9359
|
+
"vscode-zam-companion": {
|
|
9360
|
+
normalizedId: "vscode-companion",
|
|
9361
|
+
label: "VS Code Companion"
|
|
9362
|
+
},
|
|
9363
|
+
"antigravity-zam-companion": {
|
|
9364
|
+
normalizedId: "antigravity-companion",
|
|
9365
|
+
label: "Antigravity Companion"
|
|
9366
|
+
}
|
|
9367
|
+
};
|
|
9368
|
+
function normalizeNativeHostIdentity(clientInfo, harnessOverride) {
|
|
9369
|
+
if (harnessOverride) {
|
|
9370
|
+
const known2 = KNOWN_NATIVE_HOSTS[harnessOverride];
|
|
9371
|
+
return {
|
|
9372
|
+
rawName: clientInfo?.name ?? harnessOverride,
|
|
9373
|
+
version: clientInfo?.version,
|
|
9374
|
+
normalizedId: known2?.normalizedId,
|
|
9375
|
+
label: known2?.label ?? harnessOverride
|
|
9376
|
+
};
|
|
9377
|
+
}
|
|
9378
|
+
if (!clientInfo) return void 0;
|
|
9379
|
+
const known = KNOWN_NATIVE_HOSTS[clientInfo.name];
|
|
9380
|
+
return {
|
|
9381
|
+
rawName: clientInfo.name,
|
|
9382
|
+
version: clientInfo.version,
|
|
9383
|
+
normalizedId: known?.normalizedId,
|
|
9384
|
+
label: known?.label ?? clientInfo.name
|
|
9385
|
+
};
|
|
9386
|
+
}
|
|
9387
|
+
function resolveCollapsedForSurface(state, surface) {
|
|
9388
|
+
return state?.[surface] ?? false;
|
|
9389
|
+
}
|
|
9390
|
+
function parseCompanionContextReadRequest(value) {
|
|
9391
|
+
if (!isRecord2(value) || !isCompanionSurface(value.surface)) {
|
|
9392
|
+
throw new Error("Invalid companion context read request");
|
|
9393
|
+
}
|
|
9394
|
+
const rawClientInfo = value.clientInfo;
|
|
9395
|
+
const clientInfo = isRecord2(rawClientInfo) && typeof rawClientInfo.name === "string" ? {
|
|
9396
|
+
name: rawClientInfo.name,
|
|
9397
|
+
version: typeof rawClientInfo.version === "string" ? rawClientInfo.version : void 0
|
|
9398
|
+
} : void 0;
|
|
9399
|
+
const harnessOverride = typeof value.harnessOverride === "string" ? value.harnessOverride : void 0;
|
|
9400
|
+
return { surface: value.surface, clientInfo, harnessOverride };
|
|
9401
|
+
}
|
|
9402
|
+
function parseCompanionContextWriteRequest(value) {
|
|
9403
|
+
if (!isRecord2(value) || !isCompanionSurface(value.surface)) {
|
|
9404
|
+
throw new Error("Invalid companion context write request");
|
|
8844
9405
|
}
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
const dotsInterval = setInterval(() => {
|
|
8853
|
-
process.stdout.write(".");
|
|
8854
|
-
attempts++;
|
|
8855
|
-
if (attempts % dotsPerLine === 0) process.stdout.write("\n");
|
|
8856
|
-
}, 500);
|
|
8857
|
-
try {
|
|
8858
|
-
const result = await Promise.race([fetchPromise, timeoutPromise]);
|
|
8859
|
-
clearInterval(dotsInterval);
|
|
8860
|
-
clearTimeout(timeoutId);
|
|
8861
|
-
if (result !== "timeout") {
|
|
8862
|
-
if (attempts > 0) process.stdout.write("\n");
|
|
8863
|
-
return result;
|
|
8864
|
-
}
|
|
8865
|
-
console.log(`
|
|
8866
|
-
\x1B[33m${t(locale, "local_ai_working")}\x1B[0m`);
|
|
8867
|
-
const { confirm } = await import("@inquirer/prompts");
|
|
8868
|
-
const keepWaiting = await confirm({
|
|
8869
|
-
message: t(locale, "keep_waiting"),
|
|
8870
|
-
default: true
|
|
8871
|
-
}).catch(() => false);
|
|
8872
|
-
if (!keepWaiting) {
|
|
8873
|
-
controller.abort();
|
|
8874
|
-
console.log(`\x1B[33m${t(locale, "proceeding_offline")}\x1B[0m
|
|
8875
|
-
`);
|
|
8876
|
-
throw new Error("User cancelled waiting for slow LLM response");
|
|
8877
|
-
}
|
|
8878
|
-
attempts = 0;
|
|
8879
|
-
} catch (err) {
|
|
8880
|
-
clearInterval(dotsInterval);
|
|
8881
|
-
clearTimeout(timeoutId);
|
|
8882
|
-
if (attempts > 0) process.stdout.write("\n");
|
|
8883
|
-
throw err;
|
|
9406
|
+
const userId = typeof value.userId === "string" ? value.userId : void 0;
|
|
9407
|
+
let evaluatorId;
|
|
9408
|
+
if (value.evaluatorId !== void 0) {
|
|
9409
|
+
if (!isEvaluatorId(value.evaluatorId)) {
|
|
9410
|
+
throw new Error(
|
|
9411
|
+
`Unknown evaluator id in companion context write request: ${String(value.evaluatorId)}`
|
|
9412
|
+
);
|
|
8884
9413
|
}
|
|
9414
|
+
evaluatorId = value.evaluatorId;
|
|
9415
|
+
}
|
|
9416
|
+
const collapsed = typeof value.collapsed === "boolean" ? value.collapsed : void 0;
|
|
9417
|
+
if (userId === void 0 && evaluatorId === void 0 && collapsed === void 0) {
|
|
9418
|
+
throw new Error(
|
|
9419
|
+
"Companion context write request must set userId, evaluatorId, and/or collapsed"
|
|
9420
|
+
);
|
|
8885
9421
|
}
|
|
9422
|
+
return { surface: value.surface, userId, evaluatorId, collapsed };
|
|
8886
9423
|
}
|
|
8887
|
-
|
|
8888
|
-
const
|
|
8889
|
-
const
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
domain: token.domain,
|
|
8905
|
-
bloomLevel: token.bloomLevel,
|
|
8906
|
-
sourceLinkContent,
|
|
8907
|
-
existingQuestion: token.question
|
|
8908
|
-
});
|
|
8909
|
-
if (generated.text.trim().length > 0) {
|
|
8910
|
-
return {
|
|
8911
|
-
question: generated.text,
|
|
8912
|
-
source: "llm",
|
|
8913
|
-
model: generated.model
|
|
8914
|
-
};
|
|
8915
|
-
}
|
|
8916
|
-
} catch {
|
|
9424
|
+
function buildCompanionContext(input) {
|
|
9425
|
+
const userSelection = resolveSelection(input.userSelection);
|
|
9426
|
+
const evaluatorSelection = resolveSelection(input.evaluatorSelection);
|
|
9427
|
+
const routes = buildEvaluatorRoutes(
|
|
9428
|
+
input.evaluatorRouteInputs,
|
|
9429
|
+
evaluatorSelection.value
|
|
9430
|
+
);
|
|
9431
|
+
let activeEvaluatorId;
|
|
9432
|
+
let activeEvaluatorError;
|
|
9433
|
+
try {
|
|
9434
|
+
activateSelectedEvaluator(routes, evaluatorSelection.value);
|
|
9435
|
+
activeEvaluatorId = evaluatorSelection.value;
|
|
9436
|
+
} catch (error) {
|
|
9437
|
+
if (error instanceof EvaluatorUnavailableError) {
|
|
9438
|
+
activeEvaluatorError = error;
|
|
9439
|
+
} else {
|
|
9440
|
+
throw error;
|
|
8917
9441
|
}
|
|
8918
9442
|
}
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
9443
|
+
const evaluators = activeEvaluatorId ? routes.map(
|
|
9444
|
+
(route) => route.id === activeEvaluatorId ? { ...route, active: true } : route
|
|
9445
|
+
) : routes;
|
|
9446
|
+
return {
|
|
9447
|
+
read: {
|
|
9448
|
+
surface: input.surface,
|
|
9449
|
+
nativeHost: input.nativeHost,
|
|
9450
|
+
user: {
|
|
9451
|
+
currentId: userSelection.value,
|
|
9452
|
+
persistedId: input.userSelection.persisted,
|
|
9453
|
+
source: userSelection.source
|
|
9454
|
+
},
|
|
9455
|
+
profiles: input.profiles ?? [],
|
|
9456
|
+
harnesses: input.harnesses,
|
|
9457
|
+
evaluators,
|
|
9458
|
+
selectedEvaluatorId: evaluatorSelection.value,
|
|
9459
|
+
activeEvaluatorId,
|
|
9460
|
+
collapsed: resolveCollapsedForSurface(input.collapsed, input.surface)
|
|
9461
|
+
},
|
|
9462
|
+
userSelection,
|
|
9463
|
+
evaluatorSelection,
|
|
9464
|
+
activeEvaluatorError
|
|
9465
|
+
};
|
|
9466
|
+
}
|
|
9467
|
+
|
|
9468
|
+
// src/cli/bridge-handlers.ts
|
|
9469
|
+
init_kernel();
|
|
9470
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
9471
|
+
import { join as join13 } from "path";
|
|
9472
|
+
|
|
9473
|
+
// src/cli/knowledge-contexts.ts
|
|
9474
|
+
init_kernel();
|
|
9475
|
+
async function resolveOperationKnowledgeContexts(db, requestedNames) {
|
|
9476
|
+
const explicitNames = [
|
|
9477
|
+
...new Set(requestedNames.map((name) => name.trim()).filter(Boolean))
|
|
9478
|
+
];
|
|
9479
|
+
const activeDefault = getActiveWorkspaceContext();
|
|
9480
|
+
const selectedNames = explicitNames.length > 0 ? explicitNames : activeDefault ? [activeDefault] : [];
|
|
9481
|
+
const contexts = [];
|
|
9482
|
+
for (const name of selectedNames) {
|
|
9483
|
+
const context = await getKnowledgeContextByName(db, name);
|
|
9484
|
+
if (!context) {
|
|
9485
|
+
const subject = explicitNames.length > 0 ? "Knowledge" : "Active knowledge";
|
|
9486
|
+
throw new Error(`${subject} context not found: ${name}`);
|
|
9487
|
+
}
|
|
9488
|
+
contexts.push(context);
|
|
8924
9489
|
}
|
|
8925
|
-
return
|
|
9490
|
+
return contexts;
|
|
8926
9491
|
}
|
|
8927
9492
|
|
|
9493
|
+
// src/cli/bridge-handlers.ts
|
|
9494
|
+
init_client();
|
|
9495
|
+
|
|
8928
9496
|
// src/cli/llm/embedder.ts
|
|
8929
9497
|
init_kernel();
|
|
9498
|
+
init_client();
|
|
8930
9499
|
var CANONICAL_EMBEDDING_MODEL_ID = "embeddinggemma-300m";
|
|
8931
9500
|
var EMBEDDINGGEMMA_ALIASES = /* @__PURE__ */ new Set([
|
|
8932
9501
|
"embeddinggemma",
|
|
@@ -11254,6 +11823,7 @@ import { Command } from "commander";
|
|
|
11254
11823
|
import { ulid as ulid10 } from "ulid";
|
|
11255
11824
|
|
|
11256
11825
|
// src/cli/adapters/source-reader.ts
|
|
11826
|
+
init_client();
|
|
11257
11827
|
import dns from "dns";
|
|
11258
11828
|
import fs from "fs";
|
|
11259
11829
|
var MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
@@ -11328,7 +11898,7 @@ async function readWebLink(url) {
|
|
|
11328
11898
|
redirect: "manual",
|
|
11329
11899
|
signal: controller.signal,
|
|
11330
11900
|
headers: {
|
|
11331
|
-
"User-Agent": "ZAM-Content-Studio/0.
|
|
11901
|
+
"User-Agent": "ZAM-Content-Studio/0.12.0"
|
|
11332
11902
|
}
|
|
11333
11903
|
});
|
|
11334
11904
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -28491,6 +29061,7 @@ function performInstallRepair(opts = {}, deps = {}) {
|
|
|
28491
29061
|
|
|
28492
29062
|
// src/cli/llm/capability-probe.ts
|
|
28493
29063
|
init_kernel();
|
|
29064
|
+
init_client();
|
|
28494
29065
|
var EMBEDDING_MODEL_HINTS = [
|
|
28495
29066
|
"embed",
|
|
28496
29067
|
"text-embedding",
|
|
@@ -28610,8 +29181,12 @@ function validateModelSave(entry, probe, now = () => (/* @__PURE__ */ new Date()
|
|
|
28610
29181
|
};
|
|
28611
29182
|
}
|
|
28612
29183
|
|
|
29184
|
+
// src/cli/commands/bridge.ts
|
|
29185
|
+
init_client();
|
|
29186
|
+
|
|
28613
29187
|
// src/cli/llm/vision.ts
|
|
28614
29188
|
init_kernel();
|
|
29189
|
+
init_client();
|
|
28615
29190
|
import { randomBytes } from "crypto";
|
|
28616
29191
|
import { readFileSync as readFileSync16 } from "fs";
|
|
28617
29192
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -28651,25 +29226,25 @@ async function observeUiSnapshotViaLLM(db, input) {
|
|
|
28651
29226
|
const imageUrls = [];
|
|
28652
29227
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input.imagePath);
|
|
28653
29228
|
if (isVideo) {
|
|
28654
|
-
const { mkdirSync:
|
|
29229
|
+
const { mkdirSync: mkdirSync17, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
|
|
28655
29230
|
const { execSync: execSync5 } = await import("child_process");
|
|
28656
29231
|
const tempDir = join20(
|
|
28657
29232
|
tmpdir2(),
|
|
28658
29233
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
28659
29234
|
);
|
|
28660
|
-
|
|
29235
|
+
mkdirSync17(tempDir, { recursive: true });
|
|
28661
29236
|
try {
|
|
28662
29237
|
execSync5(
|
|
28663
29238
|
`ffmpeg -i "${input.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
28664
29239
|
{ stdio: "ignore" }
|
|
28665
29240
|
);
|
|
28666
|
-
let files =
|
|
29241
|
+
let files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28667
29242
|
if (files.length === 0) {
|
|
28668
29243
|
execSync5(
|
|
28669
29244
|
`ffmpeg -i "${input.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
28670
29245
|
{ stdio: "ignore" }
|
|
28671
29246
|
);
|
|
28672
|
-
files =
|
|
29247
|
+
files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28673
29248
|
}
|
|
28674
29249
|
const maxFrames = cfg.maxFrames ?? 100;
|
|
28675
29250
|
let sampledFiles = files;
|
|
@@ -29062,6 +29637,7 @@ async function withOptionalDb(fn, onError = defaultErrorHandler) {
|
|
|
29062
29637
|
}
|
|
29063
29638
|
|
|
29064
29639
|
// src/cli/providers/config.ts
|
|
29640
|
+
init_client();
|
|
29065
29641
|
var VALID_API_FLAVORS = [
|
|
29066
29642
|
"chat-completions",
|
|
29067
29643
|
"anthropic-messages"
|
|
@@ -30487,7 +31063,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30487
31063
|
const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30488
31064
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
30489
31065
|
const outputPath = opts.output ?? join22(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
30490
|
-
const { existsSync: existsSync22, writeFileSync:
|
|
31066
|
+
const { existsSync: existsSync22, writeFileSync: writeFileSync14, openSync, closeSync } = await import("fs");
|
|
30491
31067
|
if (existsSync22(statePath)) {
|
|
30492
31068
|
jsonOut({
|
|
30493
31069
|
sessionId,
|
|
@@ -30542,7 +31118,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30542
31118
|
}
|
|
30543
31119
|
child.unref();
|
|
30544
31120
|
if (child.pid) {
|
|
30545
|
-
|
|
31121
|
+
writeFileSync14(
|
|
30546
31122
|
statePath,
|
|
30547
31123
|
JSON.stringify({
|
|
30548
31124
|
pid: child.pid,
|
|
@@ -30579,7 +31155,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30579
31155
|
}
|
|
30580
31156
|
const sessionId = opts.session;
|
|
30581
31157
|
const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30582
|
-
const { existsSync: existsSync22, readFileSync:
|
|
31158
|
+
const { existsSync: existsSync22, readFileSync: readFileSync20, rmSync: rmSync4 } = await import("fs");
|
|
30583
31159
|
if (!existsSync22(statePath)) {
|
|
30584
31160
|
jsonOut({
|
|
30585
31161
|
sessionId,
|
|
@@ -30588,7 +31164,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30588
31164
|
});
|
|
30589
31165
|
return;
|
|
30590
31166
|
}
|
|
30591
|
-
const state = JSON.parse(
|
|
31167
|
+
const state = JSON.parse(readFileSync20(statePath, "utf8"));
|
|
30592
31168
|
const { pid, outputPath } = state;
|
|
30593
31169
|
try {
|
|
30594
31170
|
process.kill(pid, "SIGINT");
|
|
@@ -30604,7 +31180,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30604
31180
|
};
|
|
30605
31181
|
let attempts = 0;
|
|
30606
31182
|
while (isProcessRunning(pid) && attempts < 20) {
|
|
30607
|
-
await new Promise((
|
|
31183
|
+
await new Promise((resolve8) => setTimeout(resolve8, 250));
|
|
30608
31184
|
attempts++;
|
|
30609
31185
|
}
|
|
30610
31186
|
if (isProcessRunning(pid)) {
|
|
@@ -33020,7 +33596,7 @@ function buildEvaluatorRouteInputs(harnessReport, nativeHost, clientSamplingCapa
|
|
|
33020
33596
|
const detachedStatusById = new Map(
|
|
33021
33597
|
harnessReport.harnesses.map((harness) => [harness.harness, harness])
|
|
33022
33598
|
);
|
|
33023
|
-
const isVscodeCompanion = nativeHost?.normalizedId === "vscode-companion";
|
|
33599
|
+
const isVscodeCompanion = nativeHost?.normalizedId === "vscode-companion" || nativeHost?.normalizedId === "antigravity-companion";
|
|
33024
33600
|
const nativeHostRoutable = !isVscodeCompanion && clientSamplingCapable;
|
|
33025
33601
|
const inputs = [
|
|
33026
33602
|
{
|
|
@@ -33040,8 +33616,14 @@ function buildEvaluatorRouteInputs(harnessReport, nativeHost, clientSamplingCapa
|
|
|
33040
33616
|
id: "vscode-lm",
|
|
33041
33617
|
displayIdentity: { provider: "VS Code language models" },
|
|
33042
33618
|
configured: true,
|
|
33043
|
-
routable:
|
|
33044
|
-
reason:
|
|
33619
|
+
routable: nativeHost?.normalizedId === "vscode-companion",
|
|
33620
|
+
reason: nativeHost?.normalizedId === "vscode-companion" ? void 0 : nativeHost?.normalizedId === "antigravity-companion" ? "Antigravity IDE does not support the VS Code language model API (vscode.lm)." : "VS Code language-model routing is only available from the VS Code Companion extension."
|
|
33621
|
+
},
|
|
33622
|
+
{
|
|
33623
|
+
id: "zam-text-model",
|
|
33624
|
+
displayIdentity: { provider: "ZAM text model" },
|
|
33625
|
+
configured: true,
|
|
33626
|
+
routable: true
|
|
33045
33627
|
}
|
|
33046
33628
|
];
|
|
33047
33629
|
for (const id of DETACHED_HARNESS_EVALUATOR_IDS) {
|
|
@@ -33067,9 +33649,11 @@ function toCompanionCollapsedState(raw) {
|
|
|
33067
33649
|
async function assembleCompanionContext(db, input) {
|
|
33068
33650
|
const companionConfig = getMachineCompanionConfig(input.configPath);
|
|
33069
33651
|
const persistedUserId = companionConfig.selectedUserId;
|
|
33652
|
+
const isAntigravity = input.nativeHost?.normalizedId === "antigravity-companion";
|
|
33653
|
+
const persistedEvaluatorIdRaw = isAntigravity ? companionConfig.selectedAntigravityEvaluatorId ?? companionConfig.selectedEvaluatorId : companionConfig.selectedVscodeEvaluatorId ?? companionConfig.selectedEvaluatorId;
|
|
33070
33654
|
const persistedEvaluatorId = isEvaluatorId(
|
|
33071
|
-
|
|
33072
|
-
) ?
|
|
33655
|
+
persistedEvaluatorIdRaw
|
|
33656
|
+
) ? persistedEvaluatorIdRaw : void 0;
|
|
33073
33657
|
const collapsedRaw = companionConfig.collapsed ?? {};
|
|
33074
33658
|
const harnessReport = getHarnessReportCached();
|
|
33075
33659
|
const clientSamplingCapable = input.clientSamplingCapable ?? false;
|
|
@@ -33081,7 +33665,7 @@ async function assembleCompanionContext(db, input) {
|
|
|
33081
33665
|
]);
|
|
33082
33666
|
const fallbackUserId = fallbackUserIdRaw ?? void 0;
|
|
33083
33667
|
const quickModeSettingIsOn = quickModeRaw === "true";
|
|
33084
|
-
const defaultEvaluatorId = quickModeSettingIsOn ? "quick-mode" : isVscodeCompanion ? "vscode-lm" : clientSamplingCapable ? "native-mcp-host" : "quick-mode";
|
|
33668
|
+
const defaultEvaluatorId = quickModeSettingIsOn ? "quick-mode" : isAntigravity ? "zam-text-model" : isVscodeCompanion ? "vscode-lm" : clientSamplingCapable ? "native-mcp-host" : "quick-mode";
|
|
33085
33669
|
const { read } = buildCompanionContext({
|
|
33086
33670
|
surface: input.surface,
|
|
33087
33671
|
nativeHost: input.nativeHost,
|
|
@@ -33146,7 +33730,13 @@ async function writeCompanionContext(db, request, options = {}) {
|
|
|
33146
33730
|
fallback: void 0
|
|
33147
33731
|
});
|
|
33148
33732
|
if (isPersistableSelection(selection) && selection.value) {
|
|
33149
|
-
|
|
33733
|
+
const nativeHost = normalizeNativeHostIdentity(options.clientInfo);
|
|
33734
|
+
const isAntigravity = nativeHost?.normalizedId === "antigravity-companion";
|
|
33735
|
+
if (isAntigravity) {
|
|
33736
|
+
update.selectedAntigravityEvaluatorId = selection.value;
|
|
33737
|
+
} else {
|
|
33738
|
+
update.selectedVscodeEvaluatorId = selection.value;
|
|
33739
|
+
}
|
|
33150
33740
|
reloadRequired = true;
|
|
33151
33741
|
}
|
|
33152
33742
|
}
|
|
@@ -33228,11 +33818,11 @@ async function publishUiIntent(app, input = {}, opts = {}) {
|
|
|
33228
33818
|
|
|
33229
33819
|
// src/cli/commands/mcp.ts
|
|
33230
33820
|
var __dirname = dirname11(fileURLToPath6(import.meta.url));
|
|
33231
|
-
var pkgPath =
|
|
33821
|
+
var pkgPath = join25(__dirname, "..", "..", "package.json");
|
|
33232
33822
|
if (!existsSync21(pkgPath)) {
|
|
33233
|
-
pkgPath =
|
|
33823
|
+
pkgPath = join25(__dirname, "..", "..", "..", "package.json");
|
|
33234
33824
|
}
|
|
33235
|
-
var pkg = JSON.parse(
|
|
33825
|
+
var pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
|
|
33236
33826
|
var STUDIO_RESOURCE_URI = "ui://zam/studio";
|
|
33237
33827
|
var RECALL_RESOURCE_URI = "ui://zam/recall";
|
|
33238
33828
|
var GRAPH_RESOURCE_URI = "ui://zam/graph";
|
|
@@ -33259,13 +33849,13 @@ var STUDIO_BRIDGE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
33259
33849
|
function loadPanelHtml(fileName, placeholderTitle) {
|
|
33260
33850
|
const candidates = [
|
|
33261
33851
|
// dist/cli/commands/mcp.js → dist/ui/
|
|
33262
|
-
|
|
33852
|
+
join25(__dirname, "..", "..", "ui", fileName),
|
|
33263
33853
|
// src/cli/commands/mcp.ts via tsx → <repo>/dist/ui/
|
|
33264
|
-
|
|
33854
|
+
join25(__dirname, "..", "..", "..", "dist", "ui", fileName)
|
|
33265
33855
|
];
|
|
33266
33856
|
for (const candidate of candidates) {
|
|
33267
33857
|
if (existsSync21(candidate)) {
|
|
33268
|
-
return
|
|
33858
|
+
return readFileSync19(candidate, "utf-8");
|
|
33269
33859
|
}
|
|
33270
33860
|
}
|
|
33271
33861
|
return `<!doctype html>
|
|
@@ -34000,6 +34590,117 @@ function createMcpServer(db) {
|
|
|
34000
34590
|
});
|
|
34001
34591
|
})
|
|
34002
34592
|
);
|
|
34593
|
+
server.registerTool(
|
|
34594
|
+
"zam_companion_sample",
|
|
34595
|
+
{
|
|
34596
|
+
description: "Perform LLM sampling via the server's configured LLM (fallback for companion when vscode-lm is empty)",
|
|
34597
|
+
inputSchema: {
|
|
34598
|
+
messages: z.array(
|
|
34599
|
+
z.object({
|
|
34600
|
+
role: z.enum(["user", "assistant"]),
|
|
34601
|
+
text: z.string()
|
|
34602
|
+
})
|
|
34603
|
+
)
|
|
34604
|
+
},
|
|
34605
|
+
annotations: {
|
|
34606
|
+
...commonAnnotations
|
|
34607
|
+
},
|
|
34608
|
+
_meta: {
|
|
34609
|
+
ui: { visibility: ["app"] }
|
|
34610
|
+
}
|
|
34611
|
+
},
|
|
34612
|
+
wrapHandler(
|
|
34613
|
+
async (params) => {
|
|
34614
|
+
const { sampleViaLocalLLM: sampleViaLocalLLM2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
34615
|
+
const messages = params.messages.map((m) => ({
|
|
34616
|
+
role: m.role,
|
|
34617
|
+
content: m.text
|
|
34618
|
+
}));
|
|
34619
|
+
return await sampleViaLocalLLM2(db, messages);
|
|
34620
|
+
}
|
|
34621
|
+
)
|
|
34622
|
+
);
|
|
34623
|
+
const okfBundleDirSchema = z.string().optional().describe("Bundle directory (default docs/okf under the server cwd)");
|
|
34624
|
+
server.registerTool(
|
|
34625
|
+
"zam_okf_catalog",
|
|
34626
|
+
{
|
|
34627
|
+
description: "List the OKF knowledge-base articles (type, title, description, tags, resource URL) plus conformance problems, if any",
|
|
34628
|
+
inputSchema: {
|
|
34629
|
+
bundle_dir: okfBundleDirSchema
|
|
34630
|
+
},
|
|
34631
|
+
annotations: {
|
|
34632
|
+
...commonAnnotations,
|
|
34633
|
+
readOnlyHint: true
|
|
34634
|
+
}
|
|
34635
|
+
},
|
|
34636
|
+
wrapHandler(async (params) => {
|
|
34637
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, loadBundle: loadBundle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34638
|
+
const bundle = loadBundle2(params.bundle_dir ?? DEFAULT_BUNDLE_DIR2);
|
|
34639
|
+
return {
|
|
34640
|
+
dir: bundle.dir,
|
|
34641
|
+
articles: bundle.catalog,
|
|
34642
|
+
problems: bundle.problems
|
|
34643
|
+
};
|
|
34644
|
+
})
|
|
34645
|
+
);
|
|
34646
|
+
server.registerTool(
|
|
34647
|
+
"zam_okf_read",
|
|
34648
|
+
{
|
|
34649
|
+
description: "Read one OKF knowledge-base article: raw markdown plus parsed frontmatter",
|
|
34650
|
+
inputSchema: {
|
|
34651
|
+
bundle_dir: okfBundleDirSchema,
|
|
34652
|
+
file: z.string().describe("Article file name, e.g. fsrs-scheduling.md")
|
|
34653
|
+
},
|
|
34654
|
+
annotations: {
|
|
34655
|
+
...commonAnnotations,
|
|
34656
|
+
readOnlyHint: true
|
|
34657
|
+
}
|
|
34658
|
+
},
|
|
34659
|
+
wrapHandler(async (params) => {
|
|
34660
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
34661
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34662
|
+
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
34663
|
+
const path = resolveArticlePath2(
|
|
34664
|
+
params.bundle_dir ?? DEFAULT_BUNDLE_DIR2,
|
|
34665
|
+
params.file
|
|
34666
|
+
);
|
|
34667
|
+
const markdown = readFileSync20(path, "utf8");
|
|
34668
|
+
const { fields } = parseFrontmatter2(markdown);
|
|
34669
|
+
return { file: params.file, frontmatter: fields, markdown };
|
|
34670
|
+
})
|
|
34671
|
+
);
|
|
34672
|
+
server.registerTool(
|
|
34673
|
+
"zam_okf_upsert",
|
|
34674
|
+
{
|
|
34675
|
+
description: "Create or update an OKF knowledge-base article through the validated write path: checks the frontmatter contract, regenerates index.md, and appends the log.md entry. Never edit bundle files directly.",
|
|
34676
|
+
inputSchema: {
|
|
34677
|
+
bundle_dir: okfBundleDirSchema,
|
|
34678
|
+
file: z.string().describe(
|
|
34679
|
+
"Kebab-case article file name ending in .md (permanent ID)"
|
|
34680
|
+
),
|
|
34681
|
+
markdown: z.string().describe(
|
|
34682
|
+
"Full article: --- frontmatter (type, title, description, tags, resource, timestamp) then the body"
|
|
34683
|
+
)
|
|
34684
|
+
},
|
|
34685
|
+
annotations: {
|
|
34686
|
+
...commonAnnotations
|
|
34687
|
+
}
|
|
34688
|
+
},
|
|
34689
|
+
wrapHandler(
|
|
34690
|
+
async (params) => {
|
|
34691
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, upsertArticle: upsertArticle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34692
|
+
const result = upsertArticle2(
|
|
34693
|
+
params.bundle_dir ?? DEFAULT_BUNDLE_DIR2,
|
|
34694
|
+
params.file,
|
|
34695
|
+
params.markdown
|
|
34696
|
+
);
|
|
34697
|
+
if (!result.validation.ok) {
|
|
34698
|
+
return { ok: false, problems: result.validation.problems };
|
|
34699
|
+
}
|
|
34700
|
+
return { ok: true, created: result.created, entry: result.entry };
|
|
34701
|
+
}
|
|
34702
|
+
)
|
|
34703
|
+
);
|
|
34003
34704
|
return server;
|
|
34004
34705
|
}
|
|
34005
34706
|
async function runMcpServer() {
|