@mrciphersmith/keryx 0.2.49 → 0.2.51
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/README.md +13 -0
- package/dist/cli.js +2495 -451
- package/package.json +1 -1
- package/src/gdgraph/enrich.ts +24 -7
- package/src/gdgraph/treesitter/adapter.test.ts +145 -2
- package/src/gdgraph/treesitter/adapter.ts +85 -1
package/dist/cli.js
CHANGED
|
@@ -186,14 +186,14 @@ var init_config_dir = __esm(() => {
|
|
|
186
186
|
// src/lib/fs.ts
|
|
187
187
|
var exports_fs = {};
|
|
188
188
|
__export(exports_fs, {
|
|
189
|
-
|
|
190
|
-
withFileLock: () => withFileLock2,
|
|
191
|
-
toPosix: () => toPosix,
|
|
192
|
-
pathExists: () => pathExists,
|
|
193
|
-
isPathInside: () => isPathInside,
|
|
194
|
-
isNotFound: () => isNotFound,
|
|
189
|
+
DEFAULT_LOCK_STALE_MS: () => DEFAULT_LOCK_STALE_MS,
|
|
195
190
|
isLockHeld: () => isLockHeld,
|
|
196
|
-
|
|
191
|
+
isNotFound: () => isNotFound,
|
|
192
|
+
isPathInside: () => isPathInside,
|
|
193
|
+
pathExists: () => pathExists,
|
|
194
|
+
toPosix: () => toPosix,
|
|
195
|
+
withFileLock: () => withFileLock2,
|
|
196
|
+
writeFileAtomic: () => writeFileAtomic
|
|
197
197
|
});
|
|
198
198
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
199
199
|
import { access, mkdir, readFile, rename, rm, stat, utimes, writeFile } from "fs/promises";
|
|
@@ -5372,13 +5372,13 @@ var init_service2 = __esm(() => {
|
|
|
5372
5372
|
// src/lib/shell-config.ts
|
|
5373
5373
|
var exports_shell_config = {};
|
|
5374
5374
|
__export(exports_shell_config, {
|
|
5375
|
-
|
|
5376
|
-
saveShellConfig: () => saveShellConfig,
|
|
5377
|
-
saveProviderBaseUrl: () => saveProviderBaseUrl,
|
|
5378
|
-
saveApiKey: () => saveApiKey,
|
|
5379
|
-
loadShellConfig: () => loadShellConfig,
|
|
5375
|
+
applySavedApiKeys: () => applySavedApiKeys,
|
|
5380
5376
|
envWithSavedApiKeys: () => envWithSavedApiKeys,
|
|
5381
|
-
|
|
5377
|
+
loadShellConfig: () => loadShellConfig,
|
|
5378
|
+
saveApiKey: () => saveApiKey,
|
|
5379
|
+
saveProviderBaseUrl: () => saveProviderBaseUrl,
|
|
5380
|
+
saveShellConfig: () => saveShellConfig,
|
|
5381
|
+
shellConfigPath: () => shellConfigPath
|
|
5382
5382
|
});
|
|
5383
5383
|
import { existsSync as existsSync7 } from "fs";
|
|
5384
5384
|
import path35 from "path";
|
|
@@ -5987,6 +5987,40 @@ function createTreesitterSpec(cwd, config) {
|
|
|
5987
5987
|
load: (ctx) => new TreesitterAdapter(cwd, config, ctx.dep)
|
|
5988
5988
|
};
|
|
5989
5989
|
}
|
|
5990
|
+
async function loadTreesitterDepLiteral() {
|
|
5991
|
+
try {
|
|
5992
|
+
return await import("web-tree-sitter");
|
|
5993
|
+
} catch {
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
async function resolveTreesitterCapability(cwd, config, resolve = resolveCapability) {
|
|
5998
|
+
const spec = createTreesitterSpec(cwd, config);
|
|
5999
|
+
try {
|
|
6000
|
+
if (!await isCapabilityEnabled(cwd, spec.id)) {
|
|
6001
|
+
return null;
|
|
6002
|
+
}
|
|
6003
|
+
const literalDep = await loadTreesitterDepLiteral();
|
|
6004
|
+
if (literalDep === undefined) {
|
|
6005
|
+
return await resolve(cwd, spec);
|
|
6006
|
+
}
|
|
6007
|
+
const adapter = spec.load({ dep: literalDep, asset: null });
|
|
6008
|
+
let available;
|
|
6009
|
+
try {
|
|
6010
|
+
available = await adapter.isAvailable();
|
|
6011
|
+
} catch {
|
|
6012
|
+
warnCapabilityDegraded(spec.id, "adapter availability check threw");
|
|
6013
|
+
return null;
|
|
6014
|
+
}
|
|
6015
|
+
if (!available) {
|
|
6016
|
+
warnCapabilityDegraded(spec.id, "adapter reported unavailable");
|
|
6017
|
+
return null;
|
|
6018
|
+
}
|
|
6019
|
+
return adapter;
|
|
6020
|
+
} catch {
|
|
6021
|
+
return null;
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
5990
6024
|
|
|
5991
6025
|
class TreesitterAdapter {
|
|
5992
6026
|
cwd;
|
|
@@ -6102,6 +6136,8 @@ function compareCalls2(a, b) {
|
|
|
6102
6136
|
return a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0;
|
|
6103
6137
|
}
|
|
6104
6138
|
var init_adapter = __esm(() => {
|
|
6139
|
+
init_seam();
|
|
6140
|
+
init_warn_once();
|
|
6105
6141
|
init_extract();
|
|
6106
6142
|
init_grammars();
|
|
6107
6143
|
});
|
|
@@ -6113,13 +6149,13 @@ __export(exports_enrich, {
|
|
|
6113
6149
|
});
|
|
6114
6150
|
import { mkdir as mkdir18, writeFile as writeFile20 } from "fs/promises";
|
|
6115
6151
|
import path48 from "path";
|
|
6116
|
-
async function enrichBuildWithSymbols(cwd, files, resolve
|
|
6152
|
+
async function enrichBuildWithSymbols(cwd, files, resolve) {
|
|
6117
6153
|
const config = await loadGdgraphConfig(cwd);
|
|
6118
|
-
const
|
|
6154
|
+
const treesitterConfig = {
|
|
6119
6155
|
languages: config.treesitter.languages,
|
|
6120
6156
|
grammarsPath: config.treesitter.grammarsPath
|
|
6121
|
-
}
|
|
6122
|
-
const adapter = await resolve(cwd,
|
|
6157
|
+
};
|
|
6158
|
+
const adapter = resolve ? await resolve(cwd, createTreesitterSpec(cwd, treesitterConfig)) : await resolveTreesitterCapability(cwd, treesitterConfig);
|
|
6123
6159
|
if (!adapter) {
|
|
6124
6160
|
return { enriched: false, symbols: 0, calls: 0 };
|
|
6125
6161
|
}
|
|
@@ -6746,10 +6782,10 @@ var init_target = () => {};
|
|
|
6746
6782
|
// src/gdgraph/query.ts
|
|
6747
6783
|
var exports_query = {};
|
|
6748
6784
|
__export(exports_query, {
|
|
6749
|
-
|
|
6750
|
-
getOrphans: () => getOrphans,
|
|
6785
|
+
getAffected: () => getAffected,
|
|
6751
6786
|
getCycles: () => getCycles,
|
|
6752
|
-
|
|
6787
|
+
getOrphans: () => getOrphans,
|
|
6788
|
+
loadGraph: () => loadGraph
|
|
6753
6789
|
});
|
|
6754
6790
|
import { readFile as readFile24 } from "fs/promises";
|
|
6755
6791
|
import path51 from "path";
|
|
@@ -7388,12 +7424,12 @@ var init_repomap = __esm(() => {
|
|
|
7388
7424
|
// src/sync/provenance.ts
|
|
7389
7425
|
var exports_provenance = {};
|
|
7390
7426
|
__export(exports_provenance, {
|
|
7391
|
-
|
|
7392
|
-
readProvenance: () => readProvenance,
|
|
7393
|
-
provenancePath: () => provenancePath,
|
|
7394
|
-
gitHead: () => gitHead,
|
|
7427
|
+
SYNCED_MODULES: () => SYNCED_MODULES,
|
|
7395
7428
|
gitCmd: () => gitCmd,
|
|
7396
|
-
|
|
7429
|
+
gitHead: () => gitHead,
|
|
7430
|
+
provenancePath: () => provenancePath,
|
|
7431
|
+
readProvenance: () => readProvenance,
|
|
7432
|
+
recordProvenance: () => recordProvenance
|
|
7397
7433
|
});
|
|
7398
7434
|
import { mkdir as mkdir21, readFile as readFile25, writeFile as writeFile23 } from "fs/promises";
|
|
7399
7435
|
import path54 from "path";
|
|
@@ -7455,11 +7491,11 @@ var init_provenance = __esm(() => {
|
|
|
7455
7491
|
// src/ctx/orient.ts
|
|
7456
7492
|
var exports_orient = {};
|
|
7457
7493
|
__export(exports_orient, {
|
|
7458
|
-
|
|
7459
|
-
uncommittedCodeCount: () => uncommittedCodeCount,
|
|
7460
|
-
metaprojectIndexContext: () => metaprojectIndexContext,
|
|
7494
|
+
buildOrientation: () => buildOrientation,
|
|
7461
7495
|
graphContext: () => graphContext,
|
|
7462
|
-
|
|
7496
|
+
metaprojectIndexContext: () => metaprojectIndexContext,
|
|
7497
|
+
uncommittedCodeCount: () => uncommittedCodeCount,
|
|
7498
|
+
wikiContext: () => wikiContext
|
|
7463
7499
|
});
|
|
7464
7500
|
import path55 from "path";
|
|
7465
7501
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -7666,9 +7702,9 @@ var init_orient = __esm(() => {
|
|
|
7666
7702
|
// src/gdgraph/symbols-capability.ts
|
|
7667
7703
|
var exports_symbols_capability = {};
|
|
7668
7704
|
__export(exports_symbols_capability, {
|
|
7669
|
-
|
|
7705
|
+
TREESITTER_CAPABILITY: () => TREESITTER_CAPABILITY,
|
|
7670
7706
|
isTreesitterEnabled: () => isTreesitterEnabled,
|
|
7671
|
-
|
|
7707
|
+
setTreesitterEnabled: () => setTreesitterEnabled
|
|
7672
7708
|
});
|
|
7673
7709
|
function treesitterEntry(enabled) {
|
|
7674
7710
|
return {
|
|
@@ -8081,9 +8117,9 @@ function titleSimilarity(a, b) {
|
|
|
8081
8117
|
// src/wiki/collect.ts
|
|
8082
8118
|
var exports_collect = {};
|
|
8083
8119
|
__export(exports_collect, {
|
|
8084
|
-
|
|
8120
|
+
collectPages: () => collectPages,
|
|
8085
8121
|
computeModuleKeyFiles: () => computeModuleKeyFiles,
|
|
8086
|
-
|
|
8122
|
+
keyFilesForPage: () => keyFilesForPage
|
|
8087
8123
|
});
|
|
8088
8124
|
import { readFile as readFile29, readdir as readdir6 } from "fs/promises";
|
|
8089
8125
|
import path58 from "path";
|
|
@@ -8581,18 +8617,18 @@ var init_backlinks = __esm(() => {
|
|
|
8581
8617
|
// src/wiki/service.ts
|
|
8582
8618
|
var exports_service = {};
|
|
8583
8619
|
__export(exports_service, {
|
|
8584
|
-
|
|
8585
|
-
wikiStatus: () => wikiStatus,
|
|
8586
|
-
wikiPruneOrphans: () => wikiPruneOrphans,
|
|
8587
|
-
wikiPagesForFile: () => wikiPagesForFile,
|
|
8588
|
-
wikiGenerateIndex: () => wikiGenerateIndex,
|
|
8589
|
-
wikiCreatePage: () => wikiCreatePage,
|
|
8590
|
-
wikiCollect: () => wikiCollect,
|
|
8591
|
-
wikiCheckLinks: () => wikiCheckLinks,
|
|
8592
|
-
validModuleNames: () => validModuleNames,
|
|
8593
|
-
moduleNameFromProjectPath: () => moduleNameFromProjectPath,
|
|
8620
|
+
createGdWikiService: () => createGdWikiService,
|
|
8594
8621
|
extractModuleApi: () => extractModuleApi,
|
|
8595
|
-
|
|
8622
|
+
moduleNameFromProjectPath: () => moduleNameFromProjectPath,
|
|
8623
|
+
validModuleNames: () => validModuleNames,
|
|
8624
|
+
wikiCheckLinks: () => wikiCheckLinks,
|
|
8625
|
+
wikiCollect: () => wikiCollect,
|
|
8626
|
+
wikiCreatePage: () => wikiCreatePage,
|
|
8627
|
+
wikiGenerateIndex: () => wikiGenerateIndex,
|
|
8628
|
+
wikiPagesForFile: () => wikiPagesForFile,
|
|
8629
|
+
wikiPruneOrphans: () => wikiPruneOrphans,
|
|
8630
|
+
wikiStatus: () => wikiStatus,
|
|
8631
|
+
wikiValidate: () => wikiValidate
|
|
8596
8632
|
});
|
|
8597
8633
|
import { spawn as spawn3 } from "child_process";
|
|
8598
8634
|
import { mkdir as mkdir23, readFile as readFile30, readdir as readdir7, rm as rm3, writeFile as writeFile25 } from "fs/promises";
|
|
@@ -11454,9 +11490,396 @@ var init_anthropic_provider = __esm(() => {
|
|
|
11454
11490
|
};
|
|
11455
11491
|
});
|
|
11456
11492
|
|
|
11493
|
+
// src/harness/provider/compat/openai-compat-provider.ts
|
|
11494
|
+
function isPlainObject4(value) {
|
|
11495
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11496
|
+
}
|
|
11497
|
+
function asRecord3(value) {
|
|
11498
|
+
return isPlainObject4(value) ? value : {};
|
|
11499
|
+
}
|
|
11500
|
+
function asArray(value) {
|
|
11501
|
+
return Array.isArray(value) ? value : [];
|
|
11502
|
+
}
|
|
11503
|
+
function asString3(value) {
|
|
11504
|
+
return typeof value === "string" ? value : undefined;
|
|
11505
|
+
}
|
|
11506
|
+
function asNumber3(value) {
|
|
11507
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11508
|
+
}
|
|
11509
|
+
function retryableFor2(kind, fallback) {
|
|
11510
|
+
const concrete = defaultRetryable(kind);
|
|
11511
|
+
return concrete === undefined ? fallback : concrete;
|
|
11512
|
+
}
|
|
11513
|
+
function mergeUsage2(promptTokens, completionTokens, totalTokens) {
|
|
11514
|
+
const usage = { exact: true };
|
|
11515
|
+
if (promptTokens !== undefined) {
|
|
11516
|
+
usage.inputTokens = promptTokens;
|
|
11517
|
+
}
|
|
11518
|
+
if (completionTokens !== undefined) {
|
|
11519
|
+
usage.outputTokens = completionTokens;
|
|
11520
|
+
}
|
|
11521
|
+
if (totalTokens !== undefined) {
|
|
11522
|
+
usage.totalTokens = totalTokens;
|
|
11523
|
+
} else if (promptTokens !== undefined || completionTokens !== undefined) {
|
|
11524
|
+
usage.totalTokens = (promptTokens ?? 0) + (completionTokens ?? 0);
|
|
11525
|
+
}
|
|
11526
|
+
return usage;
|
|
11527
|
+
}
|
|
11528
|
+
function classifyHttpError2(status) {
|
|
11529
|
+
if (status >= 500) {
|
|
11530
|
+
return { kind: "unavailable", retryable: retryableFor2("unavailable", true), message: "" };
|
|
11531
|
+
}
|
|
11532
|
+
return { kind: "invalid_request", retryable: retryableFor2("invalid_request", false), message: "" };
|
|
11533
|
+
}
|
|
11534
|
+
|
|
11535
|
+
class OpenAiCompatEngine {
|
|
11536
|
+
deps;
|
|
11537
|
+
identity;
|
|
11538
|
+
constructor(deps, identity) {
|
|
11539
|
+
this.deps = deps;
|
|
11540
|
+
this.identity = identity;
|
|
11541
|
+
}
|
|
11542
|
+
get label() {
|
|
11543
|
+
return this.identity.providerLabel ?? this.identity.providerId;
|
|
11544
|
+
}
|
|
11545
|
+
describe() {
|
|
11546
|
+
const capabilities = {
|
|
11547
|
+
streaming: true,
|
|
11548
|
+
toolCalls: true,
|
|
11549
|
+
parallelToolCalls: true,
|
|
11550
|
+
structuredOutput: false,
|
|
11551
|
+
reasoningMetadata: false,
|
|
11552
|
+
promptCaching: false,
|
|
11553
|
+
vision: false,
|
|
11554
|
+
tokenCounting: false,
|
|
11555
|
+
modelListing: false
|
|
11556
|
+
};
|
|
11557
|
+
return {
|
|
11558
|
+
capabilities,
|
|
11559
|
+
descriptor: { providerId: this.identity.providerId, providerRevision: this.identity.providerRevision }
|
|
11560
|
+
};
|
|
11561
|
+
}
|
|
11562
|
+
descriptorDocument() {
|
|
11563
|
+
return {
|
|
11564
|
+
schemaVersion: 1,
|
|
11565
|
+
providerId: this.identity.providerId,
|
|
11566
|
+
providerRevision: this.identity.providerRevision,
|
|
11567
|
+
models: [{ modelId: this.identity.defaultModel.modelId, revision: this.identity.defaultModel.revision }],
|
|
11568
|
+
capabilities: {
|
|
11569
|
+
streaming: true,
|
|
11570
|
+
tools: true,
|
|
11571
|
+
parallelToolCalls: true,
|
|
11572
|
+
cancellation: true,
|
|
11573
|
+
structuredOutput: false
|
|
11574
|
+
},
|
|
11575
|
+
remoteState: { storage: false, retention: false, continuation: false }
|
|
11576
|
+
};
|
|
11577
|
+
}
|
|
11578
|
+
async* stream(request, opts) {
|
|
11579
|
+
let sequence = 0;
|
|
11580
|
+
const stamp = (body) => ({ ...body, sequence: sequence++, attemptId: opts.attemptId });
|
|
11581
|
+
const errorEvent = (error) => stamp({ kind: "provider_error", error });
|
|
11582
|
+
const grant = this.deps.grant;
|
|
11583
|
+
if (grant === undefined || grant.network !== true) {
|
|
11584
|
+
yield errorEvent({
|
|
11585
|
+
kind: "invalid_request",
|
|
11586
|
+
retryable: retryableFor2("invalid_request", false),
|
|
11587
|
+
message: `a network capability grant is required to reach the ${this.label} API`
|
|
11588
|
+
});
|
|
11589
|
+
return;
|
|
11590
|
+
}
|
|
11591
|
+
const baseUrl = grant.baseUrl ?? this.identity.defaultBaseUrl;
|
|
11592
|
+
let host;
|
|
11593
|
+
try {
|
|
11594
|
+
host = new URL(baseUrl).hostname;
|
|
11595
|
+
} catch {
|
|
11596
|
+
host = baseUrl;
|
|
11597
|
+
}
|
|
11598
|
+
const permitted = !isPrivateEgressHost(host) || grant.allowLoopback === true && isLoopbackHost(host);
|
|
11599
|
+
if (!permitted) {
|
|
11600
|
+
yield errorEvent({
|
|
11601
|
+
kind: "invalid_request",
|
|
11602
|
+
retryable: retryableFor2("invalid_request", false),
|
|
11603
|
+
message: `egress to a private/loopback/link-local/metadata host is denied: ${host}`
|
|
11604
|
+
});
|
|
11605
|
+
return;
|
|
11606
|
+
}
|
|
11607
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${grant.chatPath ?? "/v1/chat/completions"}`;
|
|
11608
|
+
const messages = [];
|
|
11609
|
+
if (request.systemInstruction.length > 0) {
|
|
11610
|
+
messages.push({ role: "system", content: request.systemInstruction });
|
|
11611
|
+
}
|
|
11612
|
+
for (const linked of linkToolCalls(request.messages)) {
|
|
11613
|
+
const message = linked.message;
|
|
11614
|
+
if (message.role === "tool") {
|
|
11615
|
+
if (linked.linkedToolCallId !== undefined) {
|
|
11616
|
+
messages.push({ role: "tool", tool_call_id: linked.linkedToolCallId, content: message.content });
|
|
11617
|
+
continue;
|
|
11618
|
+
}
|
|
11619
|
+
messages.push({ role: "user", content: `Tool result:
|
|
11620
|
+
${message.content}` });
|
|
11621
|
+
continue;
|
|
11622
|
+
}
|
|
11623
|
+
if (message.role === "assistant" && message.content.length === 0 && linked.linkedCalls.length === 0) {
|
|
11624
|
+
continue;
|
|
11625
|
+
}
|
|
11626
|
+
if (message.role === "assistant" && linked.linkedCalls.length > 0) {
|
|
11627
|
+
messages.push({
|
|
11628
|
+
role: "assistant",
|
|
11629
|
+
content: message.content,
|
|
11630
|
+
tool_calls: linked.linkedCalls.map((call) => ({
|
|
11631
|
+
id: call.id,
|
|
11632
|
+
type: "function",
|
|
11633
|
+
function: { name: call.name, arguments: call.arguments }
|
|
11634
|
+
}))
|
|
11635
|
+
});
|
|
11636
|
+
continue;
|
|
11637
|
+
}
|
|
11638
|
+
messages.push({ role: message.role, content: message.content });
|
|
11639
|
+
}
|
|
11640
|
+
const payload = {
|
|
11641
|
+
model: request.modelId,
|
|
11642
|
+
stream: true,
|
|
11643
|
+
messages,
|
|
11644
|
+
...request.tools !== undefined ? {
|
|
11645
|
+
tools: request.tools.map((tool) => ({
|
|
11646
|
+
type: "function",
|
|
11647
|
+
function: {
|
|
11648
|
+
name: tool.name,
|
|
11649
|
+
...tool.description !== undefined ? { description: tool.description } : {},
|
|
11650
|
+
parameters: tool.inputSchema
|
|
11651
|
+
}
|
|
11652
|
+
}))
|
|
11653
|
+
} : {}
|
|
11654
|
+
};
|
|
11655
|
+
const headers = { "content-type": "application/json" };
|
|
11656
|
+
if (grant.apiKey !== undefined && grant.apiKey.length > 0) {
|
|
11657
|
+
headers.authorization = `Bearer ${grant.apiKey}`;
|
|
11658
|
+
}
|
|
11659
|
+
if (grant.headers !== undefined) {
|
|
11660
|
+
Object.assign(headers, grant.headers);
|
|
11661
|
+
}
|
|
11662
|
+
const init = {
|
|
11663
|
+
method: "POST",
|
|
11664
|
+
headers,
|
|
11665
|
+
body: JSON.stringify(payload),
|
|
11666
|
+
...opts.signal !== undefined ? { signal: opts.signal } : {}
|
|
11667
|
+
};
|
|
11668
|
+
let response;
|
|
11669
|
+
try {
|
|
11670
|
+
response = await this.deps.fetch(url, init);
|
|
11671
|
+
} catch (cause) {
|
|
11672
|
+
if (opts.signal?.aborted === true) {
|
|
11673
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor2("cancelled", false), message: "attempt cancelled" });
|
|
11674
|
+
return;
|
|
11675
|
+
}
|
|
11676
|
+
yield errorEvent({
|
|
11677
|
+
kind: "unavailable",
|
|
11678
|
+
retryable: retryableFor2("unavailable", true),
|
|
11679
|
+
message: `network request to the ${this.label} API failed: ${String(cause)}`
|
|
11680
|
+
});
|
|
11681
|
+
return;
|
|
11682
|
+
}
|
|
11683
|
+
if (!response.ok) {
|
|
11684
|
+
const error = classifyHttpError2(response.status);
|
|
11685
|
+
let providerMessage = `${this.label} API returned HTTP ${response.status}`;
|
|
11686
|
+
try {
|
|
11687
|
+
const parsed = asRecord3(JSON.parse(await response.text()));
|
|
11688
|
+
const detail = asString3(asRecord3(parsed.error).message);
|
|
11689
|
+
if (detail !== undefined && detail.length > 0) {
|
|
11690
|
+
providerMessage = detail;
|
|
11691
|
+
}
|
|
11692
|
+
} catch {}
|
|
11693
|
+
error.message = providerMessage;
|
|
11694
|
+
yield stamp({ kind: "provider_error", error });
|
|
11695
|
+
return;
|
|
11696
|
+
}
|
|
11697
|
+
let bodyText;
|
|
11698
|
+
try {
|
|
11699
|
+
bodyText = await response.text();
|
|
11700
|
+
} catch (cause) {
|
|
11701
|
+
const aborted = opts.signal?.aborted === true || typeof cause === "object" && cause !== null && cause.name === "AbortError";
|
|
11702
|
+
if (aborted) {
|
|
11703
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor2("cancelled", false), message: "attempt cancelled" });
|
|
11704
|
+
return;
|
|
11705
|
+
}
|
|
11706
|
+
yield errorEvent({
|
|
11707
|
+
kind: "malformed",
|
|
11708
|
+
retryable: retryableFor2("malformed", false),
|
|
11709
|
+
message: `${this.label} SSE body read failed: ${String(cause)}`
|
|
11710
|
+
});
|
|
11711
|
+
return;
|
|
11712
|
+
}
|
|
11713
|
+
if (bodyText.length === 0) {
|
|
11714
|
+
yield errorEvent({
|
|
11715
|
+
kind: "malformed",
|
|
11716
|
+
retryable: retryableFor2("malformed", false),
|
|
11717
|
+
message: "empty response body"
|
|
11718
|
+
});
|
|
11719
|
+
return;
|
|
11720
|
+
}
|
|
11721
|
+
const parser = new AnthropicSSEParser;
|
|
11722
|
+
const records = parser.push(bodyText);
|
|
11723
|
+
const torn = parser.flush();
|
|
11724
|
+
const bodies = [];
|
|
11725
|
+
let sawStart = false;
|
|
11726
|
+
let sawFinish = false;
|
|
11727
|
+
let sawDone = false;
|
|
11728
|
+
let malformed;
|
|
11729
|
+
const pendingTools = new Map;
|
|
11730
|
+
const toolCallKey = (toolCall) => {
|
|
11731
|
+
const index = asNumber3(toolCall.index);
|
|
11732
|
+
if (index !== undefined) {
|
|
11733
|
+
return `idx:${index}`;
|
|
11734
|
+
}
|
|
11735
|
+
const id = asString3(toolCall.id);
|
|
11736
|
+
if (id !== undefined && id.length > 0) {
|
|
11737
|
+
return `id:${id}`;
|
|
11738
|
+
}
|
|
11739
|
+
return "idx:0";
|
|
11740
|
+
};
|
|
11741
|
+
const flushPendingToolEnds = () => {
|
|
11742
|
+
for (const acc of pendingTools.values()) {
|
|
11743
|
+
if (acc.ended) {
|
|
11744
|
+
continue;
|
|
11745
|
+
}
|
|
11746
|
+
if (!acc.started) {
|
|
11747
|
+
const startBody = { kind: "tool_call_start", toolCallId: acc.id };
|
|
11748
|
+
if (acc.name.length > 0) {
|
|
11749
|
+
startBody.toolName = acc.name;
|
|
11750
|
+
}
|
|
11751
|
+
bodies.push(startBody);
|
|
11752
|
+
acc.started = true;
|
|
11753
|
+
}
|
|
11754
|
+
bodies.push({ kind: "tool_call_end", toolCallId: acc.id, input: acc.arguments });
|
|
11755
|
+
acc.ended = true;
|
|
11756
|
+
}
|
|
11757
|
+
pendingTools.clear();
|
|
11758
|
+
};
|
|
11759
|
+
for (const record of records) {
|
|
11760
|
+
const trimmed = record.data.trim();
|
|
11761
|
+
if (trimmed === "[DONE]") {
|
|
11762
|
+
sawDone = true;
|
|
11763
|
+
flushPendingToolEnds();
|
|
11764
|
+
continue;
|
|
11765
|
+
}
|
|
11766
|
+
if (!sawStart) {
|
|
11767
|
+
sawStart = true;
|
|
11768
|
+
bodies.push({ kind: "model_start" });
|
|
11769
|
+
}
|
|
11770
|
+
let parsed;
|
|
11771
|
+
try {
|
|
11772
|
+
parsed = JSON.parse(record.data);
|
|
11773
|
+
} catch {
|
|
11774
|
+
malformed = {
|
|
11775
|
+
kind: "malformed",
|
|
11776
|
+
retryable: retryableFor2("malformed", false),
|
|
11777
|
+
message: `${this.label} SSE data line was not valid JSON`
|
|
11778
|
+
};
|
|
11779
|
+
break;
|
|
11780
|
+
}
|
|
11781
|
+
const data = asRecord3(parsed);
|
|
11782
|
+
if (data.usage !== undefined) {
|
|
11783
|
+
const usage = asRecord3(data.usage);
|
|
11784
|
+
bodies.push({
|
|
11785
|
+
kind: "usage_update",
|
|
11786
|
+
usage: mergeUsage2(asNumber3(usage.prompt_tokens), asNumber3(usage.completion_tokens), asNumber3(usage.total_tokens))
|
|
11787
|
+
});
|
|
11788
|
+
}
|
|
11789
|
+
const choice0 = asRecord3(asArray(data.choices)[0]);
|
|
11790
|
+
const delta = asRecord3(choice0.delta);
|
|
11791
|
+
const reasoning = asString3(delta.reasoning) ?? asString3(delta.reasoning_content);
|
|
11792
|
+
if (reasoning !== undefined && reasoning.length > 0) {
|
|
11793
|
+
bodies.push({ kind: "reasoning_delta", text: reasoning });
|
|
11794
|
+
}
|
|
11795
|
+
const content = asString3(delta.content);
|
|
11796
|
+
if (content !== undefined && content.length > 0) {
|
|
11797
|
+
bodies.push({ kind: "text_delta", text: content });
|
|
11798
|
+
}
|
|
11799
|
+
for (const rawToolCall of asArray(delta.tool_calls)) {
|
|
11800
|
+
const toolCall = asRecord3(rawToolCall);
|
|
11801
|
+
const fn = asRecord3(toolCall.function);
|
|
11802
|
+
const toolCallId = asString3(toolCall.id);
|
|
11803
|
+
const toolName = asString3(fn.name);
|
|
11804
|
+
const argumentsFragment = asString3(fn.arguments) ?? "";
|
|
11805
|
+
const key = toolCallKey(toolCall);
|
|
11806
|
+
let acc = pendingTools.get(key);
|
|
11807
|
+
if (acc === undefined) {
|
|
11808
|
+
acc = {
|
|
11809
|
+
id: toolCallId ?? `call_${key.replace(/[^a-zA-Z0-9_:-]/g, "_")}`,
|
|
11810
|
+
name: toolName ?? "",
|
|
11811
|
+
arguments: "",
|
|
11812
|
+
started: false,
|
|
11813
|
+
ended: false
|
|
11814
|
+
};
|
|
11815
|
+
pendingTools.set(key, acc);
|
|
11816
|
+
}
|
|
11817
|
+
if (toolCallId !== undefined && toolCallId.length > 0) {
|
|
11818
|
+
acc.id = toolCallId;
|
|
11819
|
+
}
|
|
11820
|
+
if (toolName !== undefined && toolName.length > 0) {
|
|
11821
|
+
acc.name = toolName;
|
|
11822
|
+
}
|
|
11823
|
+
if (!acc.started) {
|
|
11824
|
+
const startBody = { kind: "tool_call_start", toolCallId: acc.id };
|
|
11825
|
+
if (acc.name.length > 0) {
|
|
11826
|
+
startBody.toolName = acc.name;
|
|
11827
|
+
}
|
|
11828
|
+
bodies.push(startBody);
|
|
11829
|
+
acc.started = true;
|
|
11830
|
+
}
|
|
11831
|
+
if (argumentsFragment.length > 0) {
|
|
11832
|
+
acc.arguments += argumentsFragment;
|
|
11833
|
+
bodies.push({
|
|
11834
|
+
kind: "tool_call_delta",
|
|
11835
|
+
toolCallId: acc.id,
|
|
11836
|
+
inputDelta: argumentsFragment
|
|
11837
|
+
});
|
|
11838
|
+
}
|
|
11839
|
+
}
|
|
11840
|
+
const finishReason = asString3(choice0.finish_reason);
|
|
11841
|
+
if (finishReason !== undefined && finishReason.length > 0) {
|
|
11842
|
+
sawFinish = true;
|
|
11843
|
+
flushPendingToolEnds();
|
|
11844
|
+
}
|
|
11845
|
+
}
|
|
11846
|
+
if (malformed === undefined && torn.length > 0) {
|
|
11847
|
+
malformed = {
|
|
11848
|
+
kind: "malformed",
|
|
11849
|
+
retryable: retryableFor2("malformed", false),
|
|
11850
|
+
message: `${this.label} SSE stream ended mid-record (torn stream)`
|
|
11851
|
+
};
|
|
11852
|
+
}
|
|
11853
|
+
if (malformed === undefined) {
|
|
11854
|
+
flushPendingToolEnds();
|
|
11855
|
+
}
|
|
11856
|
+
if (malformed === undefined && sawStart && (sawDone || sawFinish)) {
|
|
11857
|
+
bodies.push({ kind: "model_end" });
|
|
11858
|
+
}
|
|
11859
|
+
for (const body of bodies) {
|
|
11860
|
+
if (opts.signal?.aborted === true) {
|
|
11861
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor2("cancelled", false), message: "attempt cancelled" });
|
|
11862
|
+
return;
|
|
11863
|
+
}
|
|
11864
|
+
yield stamp(body);
|
|
11865
|
+
}
|
|
11866
|
+
if (opts.signal?.aborted === true) {
|
|
11867
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor2("cancelled", false), message: "attempt cancelled" });
|
|
11868
|
+
return;
|
|
11869
|
+
}
|
|
11870
|
+
if (malformed !== undefined) {
|
|
11871
|
+
yield stamp({ kind: "provider_error", error: malformed });
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
}
|
|
11875
|
+
var init_openai_compat_provider = __esm(() => {
|
|
11876
|
+
init_guard2();
|
|
11877
|
+
init_provider_port();
|
|
11878
|
+
});
|
|
11879
|
+
|
|
11457
11880
|
// src/harness/provider/fake-provider.ts
|
|
11458
11881
|
import { createHash as createHash5 } from "crypto";
|
|
11459
|
-
function
|
|
11882
|
+
function isPlainObject5(value) {
|
|
11460
11883
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11461
11884
|
}
|
|
11462
11885
|
function canonicalize(value) {
|
|
@@ -11483,7 +11906,7 @@ function requestHashOf(request) {
|
|
|
11483
11906
|
}
|
|
11484
11907
|
function extractUnknownExtensions(payload) {
|
|
11485
11908
|
const provider = payload.provider;
|
|
11486
|
-
if (!
|
|
11909
|
+
if (!isPlainObject5(provider)) {
|
|
11487
11910
|
return;
|
|
11488
11911
|
}
|
|
11489
11912
|
const extensions = {};
|
|
@@ -11561,7 +11984,7 @@ class FakeProvider {
|
|
|
11561
11984
|
}
|
|
11562
11985
|
case "tool_call": {
|
|
11563
11986
|
const input2 = payload.input;
|
|
11564
|
-
if (!
|
|
11987
|
+
if (!isPlainObject5(input2)) {
|
|
11565
11988
|
yield emit({
|
|
11566
11989
|
kind: "provider_error",
|
|
11567
11990
|
error: {
|
|
@@ -11587,7 +12010,7 @@ class FakeProvider {
|
|
|
11587
12010
|
}
|
|
11588
12011
|
case "finish": {
|
|
11589
12012
|
const usage = payload.usage;
|
|
11590
|
-
if (
|
|
12013
|
+
if (isPlainObject5(usage)) {
|
|
11591
12014
|
yield emit({ kind: "usage_update", usage: mapUsage(usage) });
|
|
11592
12015
|
}
|
|
11593
12016
|
const body = { kind: "model_end" };
|
|
@@ -11615,49 +12038,143 @@ var init_fake_provider = __esm(() => {
|
|
|
11615
12038
|
init_provider_port();
|
|
11616
12039
|
});
|
|
11617
12040
|
|
|
11618
|
-
// src/harness/provider/
|
|
11619
|
-
function
|
|
12041
|
+
// src/harness/provider/gemini/gemini-provider.ts
|
|
12042
|
+
function isPlainObject6(value) {
|
|
11620
12043
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11621
12044
|
}
|
|
11622
|
-
function
|
|
11623
|
-
return
|
|
12045
|
+
function asRecord4(value) {
|
|
12046
|
+
return isPlainObject6(value) ? value : {};
|
|
11624
12047
|
}
|
|
11625
|
-
function
|
|
12048
|
+
function asArray2(value) {
|
|
11626
12049
|
return Array.isArray(value) ? value : [];
|
|
11627
12050
|
}
|
|
11628
|
-
function
|
|
12051
|
+
function asString4(value) {
|
|
11629
12052
|
return typeof value === "string" ? value : undefined;
|
|
11630
12053
|
}
|
|
11631
|
-
function
|
|
12054
|
+
function asNumber4(value) {
|
|
11632
12055
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11633
12056
|
}
|
|
11634
|
-
function
|
|
12057
|
+
function asBoolean(value) {
|
|
12058
|
+
return value === true;
|
|
12059
|
+
}
|
|
12060
|
+
function toGeminiContents(messages) {
|
|
12061
|
+
const out = [];
|
|
12062
|
+
for (const linked of linkToolCalls(messages)) {
|
|
12063
|
+
const message = linked.message;
|
|
12064
|
+
if (message.role === "system") {
|
|
12065
|
+
continue;
|
|
12066
|
+
}
|
|
12067
|
+
if (message.role === "assistant" && message.content.length === 0 && linked.linkedCalls.length === 0) {
|
|
12068
|
+
continue;
|
|
12069
|
+
}
|
|
12070
|
+
if (message.role === "tool" && linked.linkedToolCallId !== undefined) {
|
|
12071
|
+
const call = linked.message.toolCallId;
|
|
12072
|
+
out.push({
|
|
12073
|
+
role: "user",
|
|
12074
|
+
parts: [
|
|
12075
|
+
{
|
|
12076
|
+
functionResponse: {
|
|
12077
|
+
name: findToolName(messages, linked.linkedToolCallId) ?? "",
|
|
12078
|
+
id: call,
|
|
12079
|
+
response: parseFunctionResponse(message.content)
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
]
|
|
12083
|
+
});
|
|
12084
|
+
continue;
|
|
12085
|
+
}
|
|
12086
|
+
if (message.role === "assistant" && linked.linkedCalls.length > 0) {
|
|
12087
|
+
const parts = [];
|
|
12088
|
+
if (message.content.length > 0) {
|
|
12089
|
+
parts.push({ text: message.content });
|
|
12090
|
+
}
|
|
12091
|
+
for (const call of linked.linkedCalls) {
|
|
12092
|
+
parts.push({
|
|
12093
|
+
functionCall: { name: call.name, id: call.id, args: parseToolInput2(call.arguments) }
|
|
12094
|
+
});
|
|
12095
|
+
}
|
|
12096
|
+
out.push({ role: "model", parts });
|
|
12097
|
+
continue;
|
|
12098
|
+
}
|
|
12099
|
+
out.push({
|
|
12100
|
+
role: message.role === "assistant" ? "model" : "user",
|
|
12101
|
+
parts: [{ text: message.content }]
|
|
12102
|
+
});
|
|
12103
|
+
}
|
|
12104
|
+
return out;
|
|
12105
|
+
}
|
|
12106
|
+
function findToolName(messages, toolCallId) {
|
|
12107
|
+
for (const message of messages) {
|
|
12108
|
+
if (message.role !== "assistant" || !Array.isArray(message.toolCalls)) {
|
|
12109
|
+
continue;
|
|
12110
|
+
}
|
|
12111
|
+
const match = message.toolCalls.find((call) => call.id === toolCallId);
|
|
12112
|
+
if (match !== undefined) {
|
|
12113
|
+
return match.name;
|
|
12114
|
+
}
|
|
12115
|
+
}
|
|
12116
|
+
return;
|
|
12117
|
+
}
|
|
12118
|
+
function parseFunctionResponse(rawContent) {
|
|
12119
|
+
if (rawContent.trim().length === 0) {
|
|
12120
|
+
return {};
|
|
12121
|
+
}
|
|
12122
|
+
try {
|
|
12123
|
+
const parsed = JSON.parse(rawContent);
|
|
12124
|
+
return isPlainObject6(parsed) ? parsed : { result: parsed };
|
|
12125
|
+
} catch {
|
|
12126
|
+
return { result: rawContent };
|
|
12127
|
+
}
|
|
12128
|
+
}
|
|
12129
|
+
function parseToolInput2(rawArguments) {
|
|
12130
|
+
if (rawArguments.trim().length === 0) {
|
|
12131
|
+
return {};
|
|
12132
|
+
}
|
|
12133
|
+
try {
|
|
12134
|
+
return asRecord4(JSON.parse(rawArguments));
|
|
12135
|
+
} catch {
|
|
12136
|
+
return {};
|
|
12137
|
+
}
|
|
12138
|
+
}
|
|
12139
|
+
function retryableFor3(kind, fallback) {
|
|
11635
12140
|
const concrete = defaultRetryable(kind);
|
|
11636
12141
|
return concrete === undefined ? fallback : concrete;
|
|
11637
12142
|
}
|
|
11638
|
-
function
|
|
12143
|
+
function mergeUsage3(promptTokens, candidatesTokens, totalTokens) {
|
|
11639
12144
|
const usage = { exact: true };
|
|
11640
12145
|
if (promptTokens !== undefined) {
|
|
11641
12146
|
usage.inputTokens = promptTokens;
|
|
11642
12147
|
}
|
|
11643
|
-
if (
|
|
11644
|
-
usage.outputTokens =
|
|
12148
|
+
if (candidatesTokens !== undefined) {
|
|
12149
|
+
usage.outputTokens = candidatesTokens;
|
|
11645
12150
|
}
|
|
11646
12151
|
if (totalTokens !== undefined) {
|
|
11647
12152
|
usage.totalTokens = totalTokens;
|
|
11648
|
-
} else if (promptTokens !== undefined ||
|
|
11649
|
-
usage.totalTokens = (promptTokens ?? 0) + (
|
|
12153
|
+
} else if (promptTokens !== undefined || candidatesTokens !== undefined) {
|
|
12154
|
+
usage.totalTokens = (promptTokens ?? 0) + (candidatesTokens ?? 0);
|
|
11650
12155
|
}
|
|
11651
12156
|
return usage;
|
|
11652
12157
|
}
|
|
11653
|
-
function
|
|
11654
|
-
if (
|
|
11655
|
-
return { kind: "
|
|
12158
|
+
function classifyGeminiError(httpStatus, rpcStatus) {
|
|
12159
|
+
if (rpcStatus === "UNAUTHENTICATED" || httpStatus === 401 || httpStatus === 403) {
|
|
12160
|
+
return { kind: "authentication", retryable: retryableFor3("authentication", false), message: "" };
|
|
11656
12161
|
}
|
|
11657
|
-
|
|
12162
|
+
if (rpcStatus === "RESOURCE_EXHAUSTED" || httpStatus === 429) {
|
|
12163
|
+
return { kind: "rate_limit", retryable: retryableFor3("rate_limit", true), message: "" };
|
|
12164
|
+
}
|
|
12165
|
+
if (rpcStatus === "UNAVAILABLE" || httpStatus >= 500) {
|
|
12166
|
+
return { kind: "unavailable", retryable: retryableFor3("unavailable", true), message: "" };
|
|
12167
|
+
}
|
|
12168
|
+
if (rpcStatus === "INVALID_ARGUMENT" || httpStatus === 400) {
|
|
12169
|
+
return { kind: "invalid_request", retryable: retryableFor3("invalid_request", false), message: "" };
|
|
12170
|
+
}
|
|
12171
|
+
if (httpStatus >= 400) {
|
|
12172
|
+
return { kind: "invalid_request", retryable: retryableFor3("invalid_request", false), message: "" };
|
|
12173
|
+
}
|
|
12174
|
+
return { kind: "unknown", retryable: retryableFor3("unknown", false), message: "" };
|
|
11658
12175
|
}
|
|
11659
12176
|
|
|
11660
|
-
class
|
|
12177
|
+
class GeminiProvider {
|
|
11661
12178
|
deps;
|
|
11662
12179
|
constructor(deps) {
|
|
11663
12180
|
this.deps = deps;
|
|
@@ -11667,22 +12184,22 @@ class OllamaProvider {
|
|
|
11667
12184
|
streaming: true,
|
|
11668
12185
|
toolCalls: true,
|
|
11669
12186
|
parallelToolCalls: true,
|
|
11670
|
-
structuredOutput:
|
|
11671
|
-
reasoningMetadata:
|
|
11672
|
-
promptCaching:
|
|
11673
|
-
vision:
|
|
12187
|
+
structuredOutput: true,
|
|
12188
|
+
reasoningMetadata: true,
|
|
12189
|
+
promptCaching: true,
|
|
12190
|
+
vision: true,
|
|
11674
12191
|
tokenCounting: false,
|
|
11675
12192
|
modelListing: false
|
|
11676
12193
|
};
|
|
11677
12194
|
return {
|
|
11678
12195
|
capabilities,
|
|
11679
|
-
descriptor: { providerId: "
|
|
12196
|
+
descriptor: { providerId: "gemini", providerRevision: PROVIDER_REVISION2 }
|
|
11680
12197
|
};
|
|
11681
12198
|
}
|
|
11682
12199
|
descriptorDocument() {
|
|
11683
12200
|
return {
|
|
11684
12201
|
schemaVersion: 1,
|
|
11685
|
-
providerId: "
|
|
12202
|
+
providerId: "gemini",
|
|
11686
12203
|
providerRevision: PROVIDER_REVISION2,
|
|
11687
12204
|
models: [{ modelId: DEFAULT_MODEL2.modelId, revision: DEFAULT_MODEL2.revision }],
|
|
11688
12205
|
capabilities: {
|
|
@@ -11690,7 +12207,7 @@ class OllamaProvider {
|
|
|
11690
12207
|
tools: true,
|
|
11691
12208
|
parallelToolCalls: true,
|
|
11692
12209
|
cancellation: true,
|
|
11693
|
-
structuredOutput:
|
|
12210
|
+
structuredOutput: true
|
|
11694
12211
|
},
|
|
11695
12212
|
remoteState: { storage: false, retention: false, continuation: false }
|
|
11696
12213
|
};
|
|
@@ -11700,11 +12217,12 @@ class OllamaProvider {
|
|
|
11700
12217
|
const stamp = (body) => ({ ...body, sequence: sequence++, attemptId: opts.attemptId });
|
|
11701
12218
|
const errorEvent = (error) => stamp({ kind: "provider_error", error });
|
|
11702
12219
|
const grant = this.deps.grant;
|
|
11703
|
-
|
|
12220
|
+
const redact = (message) => grant !== undefined && grant.apiKey.length > 0 ? message.split(grant.apiKey).join("[redacted]") : message;
|
|
12221
|
+
if (grant === undefined || grant.network !== true || typeof grant.apiKey !== "string" || grant.apiKey.length === 0) {
|
|
11704
12222
|
yield errorEvent({
|
|
11705
|
-
kind: "
|
|
11706
|
-
retryable:
|
|
11707
|
-
message: "
|
|
12223
|
+
kind: "authentication",
|
|
12224
|
+
retryable: retryableFor3("authentication", false),
|
|
12225
|
+
message: "network capability grant with an apiKey is required to reach the Gemini API"
|
|
11708
12226
|
});
|
|
11709
12227
|
return;
|
|
11710
12228
|
}
|
|
@@ -11715,70 +12233,459 @@ class OllamaProvider {
|
|
|
11715
12233
|
} catch {
|
|
11716
12234
|
host = baseUrl;
|
|
11717
12235
|
}
|
|
11718
|
-
|
|
11719
|
-
if (!permitted) {
|
|
12236
|
+
if (isPrivateEgressHost(host)) {
|
|
11720
12237
|
yield errorEvent({
|
|
11721
12238
|
kind: "invalid_request",
|
|
11722
|
-
retryable:
|
|
11723
|
-
message: `egress to a private/loopback/link-local/metadata host is denied: ${host}`
|
|
12239
|
+
retryable: retryableFor3("invalid_request", false),
|
|
12240
|
+
message: redact(`egress to a private/loopback/link-local/metadata host is denied: ${host}`)
|
|
11724
12241
|
});
|
|
11725
12242
|
return;
|
|
11726
12243
|
}
|
|
11727
|
-
const url = `${baseUrl.replace(/\/+$/, "")}
|
|
11728
|
-
const
|
|
11729
|
-
|
|
11730
|
-
|
|
12244
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/v1beta/models/${encodeURIComponent(request.modelId)}:streamGenerateContent?alt=sse`;
|
|
12245
|
+
const headers = {
|
|
12246
|
+
"x-goog-api-key": grant.apiKey,
|
|
12247
|
+
"content-type": "application/json"
|
|
12248
|
+
};
|
|
12249
|
+
const payload = {
|
|
12250
|
+
contents: toGeminiContents(request.messages),
|
|
12251
|
+
...request.systemInstruction.length > 0 ? { systemInstruction: { parts: [{ text: request.systemInstruction }] } } : {},
|
|
12252
|
+
...request.tools !== undefined && request.tools.length > 0 ? {
|
|
12253
|
+
tools: [
|
|
12254
|
+
{
|
|
12255
|
+
functionDeclarations: request.tools.map((tool) => ({
|
|
12256
|
+
name: tool.name,
|
|
12257
|
+
...tool.description !== undefined ? { description: tool.description } : {},
|
|
12258
|
+
parameters: tool.inputSchema
|
|
12259
|
+
}))
|
|
12260
|
+
}
|
|
12261
|
+
]
|
|
12262
|
+
} : {},
|
|
12263
|
+
generationConfig: {
|
|
12264
|
+
maxOutputTokens: request.budget.maxOutputTokens,
|
|
12265
|
+
...request.options?.temperature !== undefined ? { temperature: request.options.temperature } : {}
|
|
12266
|
+
}
|
|
12267
|
+
};
|
|
12268
|
+
const init = {
|
|
12269
|
+
method: "POST",
|
|
12270
|
+
headers,
|
|
12271
|
+
body: JSON.stringify(payload),
|
|
12272
|
+
...opts.signal !== undefined ? { signal: opts.signal } : {}
|
|
12273
|
+
};
|
|
12274
|
+
let response;
|
|
12275
|
+
try {
|
|
12276
|
+
response = await this.deps.fetch(url, init);
|
|
12277
|
+
} catch (cause) {
|
|
12278
|
+
if (opts.signal?.aborted === true) {
|
|
12279
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor3("cancelled", false), message: "attempt cancelled" });
|
|
12280
|
+
return;
|
|
12281
|
+
}
|
|
12282
|
+
yield errorEvent({
|
|
12283
|
+
kind: "unavailable",
|
|
12284
|
+
retryable: retryableFor3("unavailable", true),
|
|
12285
|
+
message: redact(`network request to the Gemini API failed: ${String(cause)}`)
|
|
12286
|
+
});
|
|
12287
|
+
return;
|
|
11731
12288
|
}
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
12289
|
+
if (!response.ok) {
|
|
12290
|
+
let error;
|
|
12291
|
+
let providerMessage = `Gemini API returned HTTP ${response.status}`;
|
|
12292
|
+
try {
|
|
12293
|
+
const parsed = asRecord4(JSON.parse(await response.text()));
|
|
12294
|
+
const envelope = asRecord4(parsed.error);
|
|
12295
|
+
const rpcStatus = asString4(envelope.status);
|
|
12296
|
+
const httpCode = asNumber4(envelope.code) ?? response.status;
|
|
12297
|
+
error = classifyGeminiError(httpCode, rpcStatus);
|
|
12298
|
+
const detail = asString4(envelope.message);
|
|
12299
|
+
if (detail !== undefined && detail.length > 0) {
|
|
12300
|
+
providerMessage = detail;
|
|
12301
|
+
}
|
|
12302
|
+
} catch {
|
|
12303
|
+
error = classifyGeminiError(response.status, undefined);
|
|
12304
|
+
}
|
|
12305
|
+
error.message = redact(providerMessage);
|
|
12306
|
+
yield stamp({ kind: "provider_error", error });
|
|
12307
|
+
return;
|
|
12308
|
+
}
|
|
12309
|
+
let bodyText;
|
|
12310
|
+
try {
|
|
12311
|
+
bodyText = await response.text();
|
|
12312
|
+
} catch (cause) {
|
|
12313
|
+
const aborted = opts.signal?.aborted === true || typeof cause === "object" && cause !== null && cause.name === "AbortError";
|
|
12314
|
+
if (aborted) {
|
|
12315
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor3("cancelled", false), message: "attempt cancelled" });
|
|
12316
|
+
return;
|
|
12317
|
+
}
|
|
12318
|
+
yield errorEvent({
|
|
12319
|
+
kind: "malformed",
|
|
12320
|
+
retryable: retryableFor3("malformed", false),
|
|
12321
|
+
message: redact(`Gemini SSE body read failed: ${String(cause)}`)
|
|
12322
|
+
});
|
|
12323
|
+
return;
|
|
12324
|
+
}
|
|
12325
|
+
if (bodyText.length === 0) {
|
|
12326
|
+
yield errorEvent({
|
|
12327
|
+
kind: "malformed",
|
|
12328
|
+
retryable: retryableFor3("malformed", false),
|
|
12329
|
+
message: redact("empty response body")
|
|
12330
|
+
});
|
|
12331
|
+
return;
|
|
12332
|
+
}
|
|
12333
|
+
const parser = new AnthropicSSEParser;
|
|
12334
|
+
const records = parser.push(bodyText);
|
|
12335
|
+
const torn = parser.flush();
|
|
12336
|
+
const bodies = [];
|
|
12337
|
+
let sawFirstChunk = false;
|
|
12338
|
+
let sawFinish = false;
|
|
12339
|
+
let malformed;
|
|
12340
|
+
let promptTokens;
|
|
12341
|
+
let candidatesTokens;
|
|
12342
|
+
let totalTokens;
|
|
12343
|
+
let cachedContentTokens;
|
|
12344
|
+
let thoughtsTokens;
|
|
12345
|
+
for (const record of records) {
|
|
12346
|
+
let parsed;
|
|
12347
|
+
try {
|
|
12348
|
+
parsed = JSON.parse(record.data);
|
|
12349
|
+
} catch {
|
|
12350
|
+
malformed = {
|
|
12351
|
+
kind: "malformed",
|
|
12352
|
+
retryable: retryableFor3("malformed", false),
|
|
12353
|
+
message: redact("Gemini SSE data line was not valid JSON")
|
|
12354
|
+
};
|
|
12355
|
+
break;
|
|
12356
|
+
}
|
|
12357
|
+
const chunk = asRecord4(parsed);
|
|
12358
|
+
if (!sawFirstChunk) {
|
|
12359
|
+
sawFirstChunk = true;
|
|
12360
|
+
bodies.push({ kind: "model_start" });
|
|
12361
|
+
}
|
|
12362
|
+
const candidates = asArray2(chunk.candidates);
|
|
12363
|
+
const firstCandidate = asRecord4(candidates[0]);
|
|
12364
|
+
const content = asRecord4(firstCandidate.content);
|
|
12365
|
+
const parts = asArray2(content.parts);
|
|
12366
|
+
for (const rawPart of parts) {
|
|
12367
|
+
const part = asRecord4(rawPart);
|
|
12368
|
+
const text = asString4(part.text);
|
|
12369
|
+
if (text !== undefined) {
|
|
12370
|
+
if (asBoolean(part.thought)) {
|
|
12371
|
+
bodies.push({ kind: "reasoning_delta", text });
|
|
12372
|
+
} else {
|
|
12373
|
+
bodies.push({ kind: "text_delta", text });
|
|
12374
|
+
}
|
|
11737
12375
|
continue;
|
|
11738
12376
|
}
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
12377
|
+
const functionCall = asRecord4(part.functionCall);
|
|
12378
|
+
const callName = asString4(functionCall.name);
|
|
12379
|
+
if (callName !== undefined) {
|
|
12380
|
+
const callId = asString4(functionCall.id) ?? callName;
|
|
12381
|
+
const argsInput = JSON.stringify(asRecord4(functionCall.args));
|
|
12382
|
+
bodies.push({ kind: "tool_call_start", toolCallId: callId, toolName: callName });
|
|
12383
|
+
bodies.push({ kind: "tool_call_end", toolCallId: callId, input: argsInput });
|
|
12384
|
+
}
|
|
11742
12385
|
}
|
|
11743
|
-
|
|
12386
|
+
const usageMetadata = asRecord4(chunk.usageMetadata);
|
|
12387
|
+
if (Object.keys(usageMetadata).length > 0) {
|
|
12388
|
+
promptTokens = asNumber4(usageMetadata.promptTokenCount) ?? promptTokens;
|
|
12389
|
+
candidatesTokens = asNumber4(usageMetadata.candidatesTokenCount) ?? candidatesTokens;
|
|
12390
|
+
totalTokens = asNumber4(usageMetadata.totalTokenCount) ?? totalTokens;
|
|
12391
|
+
cachedContentTokens = asNumber4(usageMetadata.cachedContentTokenCount) ?? cachedContentTokens;
|
|
12392
|
+
thoughtsTokens = asNumber4(usageMetadata.thoughtsTokenCount) ?? thoughtsTokens;
|
|
12393
|
+
}
|
|
12394
|
+
const finishReason = asString4(firstCandidate.finishReason);
|
|
12395
|
+
if (finishReason !== undefined && finishReason.length > 0) {
|
|
12396
|
+
sawFinish = true;
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
if (promptTokens !== undefined || candidatesTokens !== undefined || totalTokens !== undefined) {
|
|
12400
|
+
const usage = mergeUsage3(promptTokens, candidatesTokens, totalTokens);
|
|
12401
|
+
const unknownExtensions = {};
|
|
12402
|
+
if (cachedContentTokens !== undefined) {
|
|
12403
|
+
unknownExtensions["gemini.cached_content_tokens"] = cachedContentTokens;
|
|
12404
|
+
}
|
|
12405
|
+
if (thoughtsTokens !== undefined) {
|
|
12406
|
+
unknownExtensions["gemini.thoughts_tokens"] = thoughtsTokens;
|
|
12407
|
+
}
|
|
12408
|
+
bodies.push({
|
|
12409
|
+
kind: "usage_update",
|
|
12410
|
+
usage,
|
|
12411
|
+
...Object.keys(unknownExtensions).length > 0 ? { unknownExtensions } : {}
|
|
12412
|
+
});
|
|
12413
|
+
}
|
|
12414
|
+
if (sawFinish) {
|
|
12415
|
+
bodies.push({ kind: "model_end" });
|
|
12416
|
+
}
|
|
12417
|
+
if (malformed === undefined) {
|
|
12418
|
+
if (torn.length > 0) {
|
|
12419
|
+
malformed = {
|
|
12420
|
+
kind: "malformed",
|
|
12421
|
+
retryable: retryableFor3("malformed", false),
|
|
12422
|
+
message: redact("Gemini SSE stream ended mid-record (torn stream)")
|
|
12423
|
+
};
|
|
12424
|
+
} else if (sawFirstChunk && !sawFinish) {
|
|
12425
|
+
malformed = {
|
|
12426
|
+
kind: "malformed",
|
|
12427
|
+
retryable: retryableFor3("malformed", false),
|
|
12428
|
+
message: redact("Gemini SSE stream ended before a finishReason was reported (truncated stream)")
|
|
12429
|
+
};
|
|
12430
|
+
}
|
|
12431
|
+
}
|
|
12432
|
+
for (const body of bodies) {
|
|
12433
|
+
if (opts.signal?.aborted === true) {
|
|
12434
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor3("cancelled", false), message: "attempt cancelled" });
|
|
12435
|
+
return;
|
|
12436
|
+
}
|
|
12437
|
+
yield stamp(body);
|
|
12438
|
+
}
|
|
12439
|
+
if (opts.signal?.aborted === true) {
|
|
12440
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor3("cancelled", false), message: "attempt cancelled" });
|
|
12441
|
+
return;
|
|
12442
|
+
}
|
|
12443
|
+
if (malformed !== undefined) {
|
|
12444
|
+
yield stamp({ kind: "provider_error", error: malformed });
|
|
12445
|
+
}
|
|
12446
|
+
}
|
|
12447
|
+
}
|
|
12448
|
+
var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com", PROVIDER_REVISION2 = "gemini-2026-08-20", DEFAULT_MODEL2;
|
|
12449
|
+
var init_gemini_provider = __esm(() => {
|
|
12450
|
+
init_guard2();
|
|
12451
|
+
init_provider_port();
|
|
12452
|
+
DEFAULT_MODEL2 = {
|
|
12453
|
+
modelId: "gemini-2.5-flash",
|
|
12454
|
+
revision: "2.5"
|
|
12455
|
+
};
|
|
12456
|
+
});
|
|
12457
|
+
|
|
12458
|
+
// src/harness/provider/ollama/ollama-provider.ts
|
|
12459
|
+
class OllamaProvider {
|
|
12460
|
+
engine;
|
|
12461
|
+
constructor(deps) {
|
|
12462
|
+
this.engine = new OpenAiCompatEngine(deps, {
|
|
12463
|
+
defaultBaseUrl: DEFAULT_BASE_URL3,
|
|
12464
|
+
providerRevision: PROVIDER_REVISION3,
|
|
12465
|
+
providerId: "ollama",
|
|
12466
|
+
providerLabel: "Ollama",
|
|
12467
|
+
defaultModel: DEFAULT_MODEL3
|
|
12468
|
+
});
|
|
12469
|
+
}
|
|
12470
|
+
describe() {
|
|
12471
|
+
return this.engine.describe();
|
|
12472
|
+
}
|
|
12473
|
+
descriptorDocument() {
|
|
12474
|
+
return this.engine.descriptorDocument();
|
|
12475
|
+
}
|
|
12476
|
+
stream(request, opts) {
|
|
12477
|
+
return this.engine.stream(request, opts);
|
|
12478
|
+
}
|
|
12479
|
+
}
|
|
12480
|
+
var DEFAULT_BASE_URL3 = "http://localhost:11434", PROVIDER_REVISION3 = "ollama-2024-10-22", DEFAULT_MODEL3;
|
|
12481
|
+
var init_ollama_provider = __esm(() => {
|
|
12482
|
+
init_openai_compat_provider();
|
|
12483
|
+
DEFAULT_MODEL3 = {
|
|
12484
|
+
modelId: "llama3.1:latest",
|
|
12485
|
+
revision: "latest"
|
|
12486
|
+
};
|
|
12487
|
+
});
|
|
12488
|
+
|
|
12489
|
+
// src/harness/provider/openai/openai-provider.ts
|
|
12490
|
+
function isPlainObject7(value) {
|
|
12491
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12492
|
+
}
|
|
12493
|
+
function asRecord5(value) {
|
|
12494
|
+
return isPlainObject7(value) ? value : {};
|
|
12495
|
+
}
|
|
12496
|
+
function asString5(value) {
|
|
12497
|
+
return typeof value === "string" ? value : undefined;
|
|
12498
|
+
}
|
|
12499
|
+
function asNumber5(value) {
|
|
12500
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
12501
|
+
}
|
|
12502
|
+
function toResponsesInput(messages) {
|
|
12503
|
+
const out = [];
|
|
12504
|
+
for (const linked of linkToolCalls(messages)) {
|
|
12505
|
+
const message = linked.message;
|
|
12506
|
+
if (message.role === "tool") {
|
|
12507
|
+
if (linked.linkedToolCallId !== undefined) {
|
|
12508
|
+
out.push({ type: "function_call_output", call_id: linked.linkedToolCallId, output: message.content });
|
|
11744
12509
|
continue;
|
|
11745
12510
|
}
|
|
11746
|
-
|
|
11747
|
-
|
|
12511
|
+
out.push({ type: "message", role: "user", content: [{ type: "input_text", text: message.content }] });
|
|
12512
|
+
continue;
|
|
12513
|
+
}
|
|
12514
|
+
if (message.role === "assistant" && message.content.length === 0 && linked.linkedCalls.length === 0) {
|
|
12515
|
+
continue;
|
|
12516
|
+
}
|
|
12517
|
+
if (message.role === "assistant" && linked.linkedCalls.length > 0) {
|
|
12518
|
+
if (message.content.length > 0) {
|
|
12519
|
+
out.push({
|
|
12520
|
+
type: "message",
|
|
11748
12521
|
role: "assistant",
|
|
11749
|
-
content: message.content
|
|
11750
|
-
tool_calls: linked.linkedCalls.map((call) => ({
|
|
11751
|
-
id: call.id,
|
|
11752
|
-
type: "function",
|
|
11753
|
-
function: { name: call.name, arguments: call.arguments }
|
|
11754
|
-
}))
|
|
12522
|
+
content: [{ type: "output_text", text: message.content }]
|
|
11755
12523
|
});
|
|
11756
|
-
continue;
|
|
11757
12524
|
}
|
|
11758
|
-
|
|
12525
|
+
for (const call of linked.linkedCalls) {
|
|
12526
|
+
out.push({ type: "function_call", call_id: call.id, name: call.name, arguments: call.arguments });
|
|
12527
|
+
}
|
|
12528
|
+
continue;
|
|
12529
|
+
}
|
|
12530
|
+
out.push({
|
|
12531
|
+
type: "message",
|
|
12532
|
+
role: message.role === "assistant" ? "assistant" : "user",
|
|
12533
|
+
content: [{ type: message.role === "assistant" ? "output_text" : "input_text", text: message.content }]
|
|
12534
|
+
});
|
|
12535
|
+
}
|
|
12536
|
+
return out;
|
|
12537
|
+
}
|
|
12538
|
+
function retryableFor4(kind, fallback) {
|
|
12539
|
+
const concrete = defaultRetryable(kind);
|
|
12540
|
+
return concrete === undefined ? fallback : concrete;
|
|
12541
|
+
}
|
|
12542
|
+
function mergeUsage4(inputTokens, outputTokens, totalTokens) {
|
|
12543
|
+
const usage = { exact: true };
|
|
12544
|
+
if (inputTokens !== undefined) {
|
|
12545
|
+
usage.inputTokens = inputTokens;
|
|
12546
|
+
}
|
|
12547
|
+
if (outputTokens !== undefined) {
|
|
12548
|
+
usage.outputTokens = outputTokens;
|
|
12549
|
+
}
|
|
12550
|
+
if (totalTokens !== undefined) {
|
|
12551
|
+
usage.totalTokens = totalTokens;
|
|
12552
|
+
} else if (inputTokens !== undefined || outputTokens !== undefined) {
|
|
12553
|
+
usage.totalTokens = (inputTokens ?? 0) + (outputTokens ?? 0);
|
|
12554
|
+
}
|
|
12555
|
+
return usage;
|
|
12556
|
+
}
|
|
12557
|
+
function extractErrorFields(data) {
|
|
12558
|
+
const nested = asRecord5(data.error);
|
|
12559
|
+
const code = asString5(nested.code) ?? asString5(data.code);
|
|
12560
|
+
const message = asString5(nested.message) ?? asString5(data.message);
|
|
12561
|
+
const param = asString5(nested.param) ?? asString5(data.param);
|
|
12562
|
+
const out = {};
|
|
12563
|
+
if (code !== undefined) {
|
|
12564
|
+
out.code = code;
|
|
12565
|
+
}
|
|
12566
|
+
if (message !== undefined) {
|
|
12567
|
+
out.message = message;
|
|
12568
|
+
}
|
|
12569
|
+
if (param !== undefined) {
|
|
12570
|
+
out.param = param;
|
|
12571
|
+
}
|
|
12572
|
+
return out;
|
|
12573
|
+
}
|
|
12574
|
+
function classifyHttpError3(status, headers, code) {
|
|
12575
|
+
if (status === 401) {
|
|
12576
|
+
return { kind: "authentication", retryable: retryableFor4("authentication", false), message: "" };
|
|
12577
|
+
}
|
|
12578
|
+
if (status === 429) {
|
|
12579
|
+
const error = { kind: "rate_limit", retryable: retryableFor4("rate_limit", true), message: "" };
|
|
12580
|
+
const retryAfter = headers.get("retry-after");
|
|
12581
|
+
const seconds = retryAfter === null ? undefined : Number.parseInt(retryAfter, 10);
|
|
12582
|
+
if (seconds !== undefined && Number.isFinite(seconds)) {
|
|
12583
|
+
error.retryAfterMs = seconds * 1000;
|
|
12584
|
+
}
|
|
12585
|
+
return error;
|
|
12586
|
+
}
|
|
12587
|
+
if (status === 400) {
|
|
12588
|
+
if (code === "context_length_exceeded") {
|
|
12589
|
+
return { kind: "context_overflow", retryable: retryableFor4("context_overflow", false), message: "" };
|
|
11759
12590
|
}
|
|
12591
|
+
return { kind: "invalid_request", retryable: retryableFor4("invalid_request", false), message: "" };
|
|
12592
|
+
}
|
|
12593
|
+
if (status >= 500) {
|
|
12594
|
+
return { kind: "unavailable", retryable: retryableFor4("unavailable", true), message: "" };
|
|
12595
|
+
}
|
|
12596
|
+
if (status >= 400) {
|
|
12597
|
+
return { kind: "invalid_request", retryable: retryableFor4("invalid_request", false), message: "" };
|
|
12598
|
+
}
|
|
12599
|
+
return { kind: "unknown", retryable: retryableFor4("unknown", false), message: "" };
|
|
12600
|
+
}
|
|
12601
|
+
|
|
12602
|
+
class OpenAiProvider {
|
|
12603
|
+
deps;
|
|
12604
|
+
constructor(deps) {
|
|
12605
|
+
this.deps = deps;
|
|
12606
|
+
}
|
|
12607
|
+
describe() {
|
|
12608
|
+
const capabilities = {
|
|
12609
|
+
streaming: true,
|
|
12610
|
+
toolCalls: true,
|
|
12611
|
+
parallelToolCalls: true,
|
|
12612
|
+
structuredOutput: true,
|
|
12613
|
+
reasoningMetadata: true,
|
|
12614
|
+
promptCaching: true,
|
|
12615
|
+
vision: false,
|
|
12616
|
+
tokenCounting: false,
|
|
12617
|
+
modelListing: false
|
|
12618
|
+
};
|
|
12619
|
+
return {
|
|
12620
|
+
capabilities,
|
|
12621
|
+
descriptor: { providerId: "openai", providerRevision: PROVIDER_REVISION4 }
|
|
12622
|
+
};
|
|
12623
|
+
}
|
|
12624
|
+
descriptorDocument() {
|
|
12625
|
+
return {
|
|
12626
|
+
schemaVersion: 1,
|
|
12627
|
+
providerId: "openai",
|
|
12628
|
+
providerRevision: PROVIDER_REVISION4,
|
|
12629
|
+
models: [{ modelId: DEFAULT_MODEL4.modelId, revision: DEFAULT_MODEL4.revision }],
|
|
12630
|
+
capabilities: {
|
|
12631
|
+
streaming: true,
|
|
12632
|
+
tools: true,
|
|
12633
|
+
parallelToolCalls: true,
|
|
12634
|
+
cancellation: true,
|
|
12635
|
+
structuredOutput: true
|
|
12636
|
+
},
|
|
12637
|
+
remoteState: { storage: false, retention: false, continuation: false }
|
|
12638
|
+
};
|
|
12639
|
+
}
|
|
12640
|
+
async* stream(request, opts) {
|
|
12641
|
+
let sequence = 0;
|
|
12642
|
+
const stamp = (body) => ({ ...body, sequence: sequence++, attemptId: opts.attemptId });
|
|
12643
|
+
const errorEvent = (error) => stamp({ kind: "provider_error", error });
|
|
12644
|
+
const grant = this.deps.grant;
|
|
12645
|
+
const redact = (message) => grant !== undefined && grant.apiKey.length > 0 ? message.split(grant.apiKey).join("[redacted]") : message;
|
|
12646
|
+
if (grant === undefined || grant.network !== true || typeof grant.apiKey !== "string" || grant.apiKey.length === 0) {
|
|
12647
|
+
yield errorEvent({
|
|
12648
|
+
kind: "authentication",
|
|
12649
|
+
retryable: retryableFor4("authentication", false),
|
|
12650
|
+
message: "network capability grant with an apiKey is required to reach the OpenAI API"
|
|
12651
|
+
});
|
|
12652
|
+
return;
|
|
12653
|
+
}
|
|
12654
|
+
const baseUrl = grant.baseUrl ?? DEFAULT_BASE_URL4;
|
|
12655
|
+
let host;
|
|
12656
|
+
try {
|
|
12657
|
+
host = new URL(baseUrl).hostname;
|
|
12658
|
+
} catch {
|
|
12659
|
+
host = baseUrl;
|
|
12660
|
+
}
|
|
12661
|
+
if (isPrivateEgressHost(host)) {
|
|
12662
|
+
yield errorEvent({
|
|
12663
|
+
kind: "invalid_request",
|
|
12664
|
+
retryable: retryableFor4("invalid_request", false),
|
|
12665
|
+
message: redact(`egress to a private/loopback/link-local/metadata host is denied: ${host}`)
|
|
12666
|
+
});
|
|
12667
|
+
return;
|
|
12668
|
+
}
|
|
12669
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/v1/responses`;
|
|
12670
|
+
const headers = {
|
|
12671
|
+
authorization: `Bearer ${grant.apiKey}`,
|
|
12672
|
+
"content-type": "application/json"
|
|
12673
|
+
};
|
|
11760
12674
|
const payload = {
|
|
11761
12675
|
model: request.modelId,
|
|
12676
|
+
instructions: request.systemInstruction,
|
|
12677
|
+
input: toResponsesInput(request.messages),
|
|
11762
12678
|
stream: true,
|
|
11763
|
-
messages,
|
|
11764
12679
|
...request.tools !== undefined ? {
|
|
11765
12680
|
tools: request.tools.map((tool) => ({
|
|
11766
12681
|
type: "function",
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
}))
|
|
12682
|
+
name: tool.name,
|
|
12683
|
+
...tool.description !== undefined ? { description: tool.description } : {},
|
|
12684
|
+
parameters: tool.inputSchema
|
|
12685
|
+
})),
|
|
12686
|
+
parallel_tool_calls: true
|
|
11773
12687
|
} : {}
|
|
11774
12688
|
};
|
|
11775
|
-
const headers = { "content-type": "application/json" };
|
|
11776
|
-
if (grant.apiKey !== undefined && grant.apiKey.length > 0) {
|
|
11777
|
-
headers.authorization = `Bearer ${grant.apiKey}`;
|
|
11778
|
-
}
|
|
11779
|
-
if (grant.headers !== undefined) {
|
|
11780
|
-
Object.assign(headers, grant.headers);
|
|
11781
|
-
}
|
|
11782
12689
|
const init = {
|
|
11783
12690
|
method: "POST",
|
|
11784
12691
|
headers,
|
|
@@ -11790,27 +12697,28 @@ ${message.content}` });
|
|
|
11790
12697
|
response = await this.deps.fetch(url, init);
|
|
11791
12698
|
} catch (cause) {
|
|
11792
12699
|
if (opts.signal?.aborted === true) {
|
|
11793
|
-
yield errorEvent({ kind: "cancelled", retryable:
|
|
12700
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor4("cancelled", false), message: "attempt cancelled" });
|
|
11794
12701
|
return;
|
|
11795
12702
|
}
|
|
11796
12703
|
yield errorEvent({
|
|
11797
12704
|
kind: "unavailable",
|
|
11798
|
-
retryable:
|
|
11799
|
-
message: `network request to the
|
|
12705
|
+
retryable: retryableFor4("unavailable", true),
|
|
12706
|
+
message: redact(`network request to the OpenAI API failed: ${String(cause)}`)
|
|
11800
12707
|
});
|
|
11801
12708
|
return;
|
|
11802
12709
|
}
|
|
11803
12710
|
if (!response.ok) {
|
|
11804
|
-
|
|
11805
|
-
let providerMessage = `Ollama API returned HTTP ${response.status}`;
|
|
12711
|
+
let bodyRecord = {};
|
|
11806
12712
|
try {
|
|
11807
|
-
|
|
11808
|
-
const detail = asString3(asRecord3(parsed.error).message);
|
|
11809
|
-
if (detail !== undefined && detail.length > 0) {
|
|
11810
|
-
providerMessage = detail;
|
|
11811
|
-
}
|
|
12713
|
+
bodyRecord = asRecord5(JSON.parse(await response.text()));
|
|
11812
12714
|
} catch {}
|
|
11813
|
-
|
|
12715
|
+
const fields = extractErrorFields(bodyRecord);
|
|
12716
|
+
const error = classifyHttpError3(response.status, response.headers, fields.code);
|
|
12717
|
+
let providerMessage = `OpenAI API returned HTTP ${response.status}`;
|
|
12718
|
+
if (fields.message !== undefined && fields.message.length > 0) {
|
|
12719
|
+
providerMessage = fields.message;
|
|
12720
|
+
}
|
|
12721
|
+
error.message = redact(providerMessage);
|
|
11814
12722
|
yield stamp({ kind: "provider_error", error });
|
|
11815
12723
|
return;
|
|
11816
12724
|
}
|
|
@@ -11820,21 +12728,21 @@ ${message.content}` });
|
|
|
11820
12728
|
} catch (cause) {
|
|
11821
12729
|
const aborted = opts.signal?.aborted === true || typeof cause === "object" && cause !== null && cause.name === "AbortError";
|
|
11822
12730
|
if (aborted) {
|
|
11823
|
-
yield errorEvent({ kind: "cancelled", retryable:
|
|
12731
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor4("cancelled", false), message: "attempt cancelled" });
|
|
11824
12732
|
return;
|
|
11825
12733
|
}
|
|
11826
12734
|
yield errorEvent({
|
|
11827
12735
|
kind: "malformed",
|
|
11828
|
-
retryable:
|
|
11829
|
-
message: `
|
|
12736
|
+
retryable: retryableFor4("malformed", false),
|
|
12737
|
+
message: redact(`OpenAI SSE body read failed: ${String(cause)}`)
|
|
11830
12738
|
});
|
|
11831
12739
|
return;
|
|
11832
12740
|
}
|
|
11833
12741
|
if (bodyText.length === 0) {
|
|
11834
12742
|
yield errorEvent({
|
|
11835
12743
|
kind: "malformed",
|
|
11836
|
-
retryable:
|
|
11837
|
-
message: "empty response body"
|
|
12744
|
+
retryable: retryableFor4("malformed", false),
|
|
12745
|
+
message: redact("empty response body")
|
|
11838
12746
|
});
|
|
11839
12747
|
return;
|
|
11840
12748
|
}
|
|
@@ -11842,149 +12750,193 @@ ${message.content}` });
|
|
|
11842
12750
|
const records = parser.push(bodyText);
|
|
11843
12751
|
const torn = parser.flush();
|
|
11844
12752
|
const bodies = [];
|
|
12753
|
+
const pendingTools = new Map;
|
|
11845
12754
|
let sawStart = false;
|
|
11846
|
-
let
|
|
11847
|
-
let sawDone = false;
|
|
12755
|
+
let sawCompleted = false;
|
|
11848
12756
|
let malformed;
|
|
11849
|
-
|
|
11850
|
-
const toolCallKey = (toolCall) => {
|
|
11851
|
-
const index = asNumber3(toolCall.index);
|
|
11852
|
-
if (index !== undefined) {
|
|
11853
|
-
return `idx:${index}`;
|
|
11854
|
-
}
|
|
11855
|
-
const id = asString3(toolCall.id);
|
|
11856
|
-
if (id !== undefined && id.length > 0) {
|
|
11857
|
-
return `id:${id}`;
|
|
11858
|
-
}
|
|
11859
|
-
return "idx:0";
|
|
11860
|
-
};
|
|
11861
|
-
const flushPendingToolEnds = () => {
|
|
11862
|
-
for (const acc of pendingTools.values()) {
|
|
11863
|
-
if (acc.ended) {
|
|
11864
|
-
continue;
|
|
11865
|
-
}
|
|
11866
|
-
if (!acc.started) {
|
|
11867
|
-
const startBody = { kind: "tool_call_start", toolCallId: acc.id };
|
|
11868
|
-
if (acc.name.length > 0) {
|
|
11869
|
-
startBody.toolName = acc.name;
|
|
11870
|
-
}
|
|
11871
|
-
bodies.push(startBody);
|
|
11872
|
-
acc.started = true;
|
|
11873
|
-
}
|
|
11874
|
-
bodies.push({ kind: "tool_call_end", toolCallId: acc.id, input: acc.arguments });
|
|
11875
|
-
acc.ended = true;
|
|
11876
|
-
}
|
|
11877
|
-
pendingTools.clear();
|
|
11878
|
-
};
|
|
12757
|
+
let terminalError;
|
|
11879
12758
|
for (const record of records) {
|
|
11880
|
-
const trimmed = record.data.trim();
|
|
11881
|
-
if (trimmed === "[DONE]") {
|
|
11882
|
-
sawDone = true;
|
|
11883
|
-
flushPendingToolEnds();
|
|
11884
|
-
continue;
|
|
11885
|
-
}
|
|
11886
|
-
if (!sawStart) {
|
|
11887
|
-
sawStart = true;
|
|
11888
|
-
bodies.push({ kind: "model_start" });
|
|
11889
|
-
}
|
|
11890
12759
|
let parsed;
|
|
11891
12760
|
try {
|
|
11892
12761
|
parsed = JSON.parse(record.data);
|
|
11893
12762
|
} catch {
|
|
11894
12763
|
malformed = {
|
|
11895
12764
|
kind: "malformed",
|
|
11896
|
-
retryable:
|
|
11897
|
-
message: "
|
|
12765
|
+
retryable: retryableFor4("malformed", false),
|
|
12766
|
+
message: redact("OpenAI SSE data line was not valid JSON")
|
|
11898
12767
|
};
|
|
11899
12768
|
break;
|
|
11900
12769
|
}
|
|
11901
|
-
const data =
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
usage: mergeUsage2(asNumber3(usage.prompt_tokens), asNumber3(usage.completion_tokens), asNumber3(usage.total_tokens))
|
|
11907
|
-
});
|
|
11908
|
-
}
|
|
11909
|
-
const choice0 = asRecord3(asArray(data.choices)[0]);
|
|
11910
|
-
const delta = asRecord3(choice0.delta);
|
|
11911
|
-
const reasoning = asString3(delta.reasoning) ?? asString3(delta.reasoning_content);
|
|
11912
|
-
if (reasoning !== undefined && reasoning.length > 0) {
|
|
11913
|
-
bodies.push({ kind: "reasoning_delta", text: reasoning });
|
|
11914
|
-
}
|
|
11915
|
-
const content = asString3(delta.content);
|
|
11916
|
-
if (content !== undefined && content.length > 0) {
|
|
11917
|
-
bodies.push({ kind: "text_delta", text: content });
|
|
11918
|
-
}
|
|
11919
|
-
for (const rawToolCall of asArray(delta.tool_calls)) {
|
|
11920
|
-
const toolCall = asRecord3(rawToolCall);
|
|
11921
|
-
const fn = asRecord3(toolCall.function);
|
|
11922
|
-
const toolCallId = asString3(toolCall.id);
|
|
11923
|
-
const toolName = asString3(fn.name);
|
|
11924
|
-
const argumentsFragment = asString3(fn.arguments) ?? "";
|
|
11925
|
-
const key = toolCallKey(toolCall);
|
|
11926
|
-
let acc = pendingTools.get(key);
|
|
11927
|
-
if (acc === undefined) {
|
|
11928
|
-
acc = {
|
|
11929
|
-
id: toolCallId ?? `call_${key.replace(/[^a-zA-Z0-9_:-]/g, "_")}`,
|
|
11930
|
-
name: toolName ?? "",
|
|
11931
|
-
arguments: "",
|
|
11932
|
-
started: false,
|
|
11933
|
-
ended: false
|
|
11934
|
-
};
|
|
11935
|
-
pendingTools.set(key, acc);
|
|
12770
|
+
const data = asRecord5(parsed);
|
|
12771
|
+
const eventType = asString5(data.type) ?? asString5(record.event);
|
|
12772
|
+
switch (eventType) {
|
|
12773
|
+
case "response.created": {
|
|
12774
|
+
break;
|
|
11936
12775
|
}
|
|
11937
|
-
|
|
11938
|
-
|
|
12776
|
+
case "response.output_item.added": {
|
|
12777
|
+
const item = asRecord5(data.item);
|
|
12778
|
+
if (asString5(item.type) === "function_call") {
|
|
12779
|
+
if (!sawStart) {
|
|
12780
|
+
sawStart = true;
|
|
12781
|
+
bodies.push({ kind: "model_start" });
|
|
12782
|
+
}
|
|
12783
|
+
const itemId = asString5(data.item_id) ?? asString5(item.id) ?? "";
|
|
12784
|
+
const callId = asString5(item.call_id) ?? itemId;
|
|
12785
|
+
const toolName = asString5(item.name);
|
|
12786
|
+
pendingTools.set(itemId, { callId, name: toolName ?? "", arguments: asString5(item.arguments) ?? "" });
|
|
12787
|
+
const startBody = { kind: "tool_call_start", toolCallId: callId };
|
|
12788
|
+
if (toolName !== undefined) {
|
|
12789
|
+
startBody.toolName = toolName;
|
|
12790
|
+
}
|
|
12791
|
+
bodies.push(startBody);
|
|
12792
|
+
}
|
|
12793
|
+
break;
|
|
11939
12794
|
}
|
|
11940
|
-
|
|
11941
|
-
|
|
12795
|
+
case "response.output_text.delta": {
|
|
12796
|
+
if (!sawStart) {
|
|
12797
|
+
sawStart = true;
|
|
12798
|
+
bodies.push({ kind: "model_start" });
|
|
12799
|
+
}
|
|
12800
|
+
const text = asString5(data.delta);
|
|
12801
|
+
const body = { kind: "text_delta" };
|
|
12802
|
+
if (text !== undefined) {
|
|
12803
|
+
body.text = text;
|
|
12804
|
+
}
|
|
12805
|
+
bodies.push(body);
|
|
12806
|
+
break;
|
|
11942
12807
|
}
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
12808
|
+
case "response.reasoning_summary_text.delta": {
|
|
12809
|
+
if (!sawStart) {
|
|
12810
|
+
sawStart = true;
|
|
12811
|
+
bodies.push({ kind: "model_start" });
|
|
11947
12812
|
}
|
|
11948
|
-
|
|
11949
|
-
|
|
12813
|
+
const text = asString5(data.delta);
|
|
12814
|
+
if (text !== undefined && text.length > 0) {
|
|
12815
|
+
bodies.push({ kind: "reasoning_delta", text });
|
|
12816
|
+
}
|
|
12817
|
+
break;
|
|
11950
12818
|
}
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
12819
|
+
case "response.function_call_arguments.delta": {
|
|
12820
|
+
const itemId = asString5(data.item_id) ?? "";
|
|
12821
|
+
const fragment = asString5(data.delta) ?? "";
|
|
12822
|
+
const pending = pendingTools.get(itemId);
|
|
12823
|
+
const toolCallId = pending?.callId ?? itemId;
|
|
12824
|
+
if (pending !== undefined) {
|
|
12825
|
+
pending.arguments += fragment;
|
|
12826
|
+
}
|
|
12827
|
+
if (fragment.length > 0) {
|
|
12828
|
+
bodies.push({ kind: "tool_call_delta", toolCallId, inputDelta: fragment });
|
|
12829
|
+
}
|
|
12830
|
+
break;
|
|
12831
|
+
}
|
|
12832
|
+
case "response.function_call_arguments.done": {
|
|
12833
|
+
const itemId = asString5(data.item_id) ?? "";
|
|
12834
|
+
const pending = pendingTools.get(itemId);
|
|
12835
|
+
const fullArguments = asString5(data.arguments) ?? pending?.arguments ?? "";
|
|
12836
|
+
const toolCallId = pending?.callId ?? itemId;
|
|
12837
|
+
bodies.push({ kind: "tool_call_end", toolCallId, input: fullArguments });
|
|
12838
|
+
pendingTools.delete(itemId);
|
|
12839
|
+
break;
|
|
11958
12840
|
}
|
|
12841
|
+
case "response.output_item.done": {
|
|
12842
|
+
const item = asRecord5(data.item);
|
|
12843
|
+
if (asString5(item.type) === "function_call") {
|
|
12844
|
+
const itemId = asString5(data.item_id) ?? asString5(item.id) ?? "";
|
|
12845
|
+
const pending = pendingTools.get(itemId);
|
|
12846
|
+
if (pending !== undefined) {
|
|
12847
|
+
const fullArguments = asString5(item.arguments) ?? pending.arguments;
|
|
12848
|
+
bodies.push({ kind: "tool_call_end", toolCallId: pending.callId, input: fullArguments });
|
|
12849
|
+
pendingTools.delete(itemId);
|
|
12850
|
+
}
|
|
12851
|
+
}
|
|
12852
|
+
break;
|
|
12853
|
+
}
|
|
12854
|
+
case "response.completed": {
|
|
12855
|
+
sawCompleted = true;
|
|
12856
|
+
const usage = asRecord5(asRecord5(data.response).usage);
|
|
12857
|
+
const inputTokens = asNumber5(usage.input_tokens);
|
|
12858
|
+
const outputTokens = asNumber5(usage.output_tokens);
|
|
12859
|
+
const totalTokens = asNumber5(usage.total_tokens);
|
|
12860
|
+
const reasoningTokens = asNumber5(asRecord5(usage.output_tokens_details).reasoning_tokens);
|
|
12861
|
+
const usageBody = {
|
|
12862
|
+
kind: "usage_update",
|
|
12863
|
+
usage: mergeUsage4(inputTokens, outputTokens, totalTokens)
|
|
12864
|
+
};
|
|
12865
|
+
if (reasoningTokens !== undefined) {
|
|
12866
|
+
usageBody.unknownExtensions = { "openai.reasoning_tokens": reasoningTokens };
|
|
12867
|
+
}
|
|
12868
|
+
bodies.push(usageBody);
|
|
12869
|
+
bodies.push({ kind: "model_end" });
|
|
12870
|
+
break;
|
|
12871
|
+
}
|
|
12872
|
+
case "response.failed":
|
|
12873
|
+
case "response.incomplete": {
|
|
12874
|
+
const fields = extractErrorFields(asRecord5(data.response));
|
|
12875
|
+
const code = fields.code;
|
|
12876
|
+
let kind = "unknown";
|
|
12877
|
+
if (code === "context_length_exceeded") {
|
|
12878
|
+
kind = "context_overflow";
|
|
12879
|
+
} else if (code !== undefined && code.length > 0) {
|
|
12880
|
+
kind = "invalid_request";
|
|
12881
|
+
}
|
|
12882
|
+
let message = fields.message;
|
|
12883
|
+
if (message === undefined || message.length === 0) {
|
|
12884
|
+
message = kind === "context_overflow" ? "OpenAI Responses API returned an error with no message; likely context-length overflow" : "OpenAI Responses API returned an error with no message";
|
|
12885
|
+
}
|
|
12886
|
+
terminalError = { kind, retryable: retryableFor4(kind, false), message: redact(message) };
|
|
12887
|
+
break;
|
|
12888
|
+
}
|
|
12889
|
+
case "error": {
|
|
12890
|
+
const fields = extractErrorFields(data);
|
|
12891
|
+
const code = fields.code;
|
|
12892
|
+
let kind = "unknown";
|
|
12893
|
+
if (code === "context_length_exceeded") {
|
|
12894
|
+
kind = "context_overflow";
|
|
12895
|
+
} else if (code !== undefined && code.length > 0) {
|
|
12896
|
+
kind = "invalid_request";
|
|
12897
|
+
}
|
|
12898
|
+
let message = fields.message;
|
|
12899
|
+
if (message === undefined || message.length === 0) {
|
|
12900
|
+
message = kind === "context_overflow" ? "OpenAI Responses API returned an error with no message; likely context-length overflow" : "OpenAI Responses API returned an error with no message; likely context-length overflow (unconfirmed cause)";
|
|
12901
|
+
}
|
|
12902
|
+
terminalError = { kind, retryable: retryableFor4(kind, false), message: redact(message) };
|
|
12903
|
+
break;
|
|
12904
|
+
}
|
|
12905
|
+
default:
|
|
12906
|
+
break;
|
|
11959
12907
|
}
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
sawFinish = true;
|
|
11963
|
-
flushPendingToolEnds();
|
|
12908
|
+
if (terminalError !== undefined) {
|
|
12909
|
+
break;
|
|
11964
12910
|
}
|
|
11965
12911
|
}
|
|
11966
|
-
if (malformed === undefined &&
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
12912
|
+
if (malformed === undefined && terminalError === undefined) {
|
|
12913
|
+
if (torn.length > 0) {
|
|
12914
|
+
malformed = {
|
|
12915
|
+
kind: "malformed",
|
|
12916
|
+
retryable: retryableFor4("malformed", false),
|
|
12917
|
+
message: redact("OpenAI SSE stream ended mid-record (torn stream)")
|
|
12918
|
+
};
|
|
12919
|
+
} else if (sawStart && !sawCompleted) {
|
|
12920
|
+
malformed = {
|
|
12921
|
+
kind: "malformed",
|
|
12922
|
+
retryable: retryableFor4("malformed", false),
|
|
12923
|
+
message: redact("OpenAI SSE stream ended before response.completed (truncated stream)")
|
|
12924
|
+
};
|
|
12925
|
+
}
|
|
11978
12926
|
}
|
|
11979
12927
|
for (const body of bodies) {
|
|
11980
12928
|
if (opts.signal?.aborted === true) {
|
|
11981
|
-
yield errorEvent({ kind: "cancelled", retryable:
|
|
12929
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor4("cancelled", false), message: "attempt cancelled" });
|
|
11982
12930
|
return;
|
|
11983
12931
|
}
|
|
11984
12932
|
yield stamp(body);
|
|
11985
12933
|
}
|
|
11986
12934
|
if (opts.signal?.aborted === true) {
|
|
11987
|
-
yield errorEvent({ kind: "cancelled", retryable:
|
|
12935
|
+
yield errorEvent({ kind: "cancelled", retryable: retryableFor4("cancelled", false), message: "attempt cancelled" });
|
|
12936
|
+
return;
|
|
12937
|
+
}
|
|
12938
|
+
if (terminalError !== undefined) {
|
|
12939
|
+
yield stamp({ kind: "provider_error", error: terminalError });
|
|
11988
12940
|
return;
|
|
11989
12941
|
}
|
|
11990
12942
|
if (malformed !== undefined) {
|
|
@@ -11992,13 +12944,13 @@ ${message.content}` });
|
|
|
11992
12944
|
}
|
|
11993
12945
|
}
|
|
11994
12946
|
}
|
|
11995
|
-
var
|
|
11996
|
-
var
|
|
12947
|
+
var DEFAULT_BASE_URL4 = "https://api.openai.com", PROVIDER_REVISION4 = "openai-responses-2026-08", DEFAULT_MODEL4;
|
|
12948
|
+
var init_openai_provider = __esm(() => {
|
|
11997
12949
|
init_guard2();
|
|
11998
12950
|
init_provider_port();
|
|
11999
|
-
|
|
12000
|
-
modelId: "
|
|
12001
|
-
revision: "
|
|
12951
|
+
DEFAULT_MODEL4 = {
|
|
12952
|
+
modelId: "gpt-4.1",
|
|
12953
|
+
revision: "2026-08"
|
|
12002
12954
|
};
|
|
12003
12955
|
});
|
|
12004
12956
|
|
|
@@ -12012,6 +12964,20 @@ function makeProvider(name, _model, opts) {
|
|
|
12012
12964
|
}
|
|
12013
12965
|
return new AnthropicProvider({ fetch: opts.fetch, grant: { network: true, apiKey } });
|
|
12014
12966
|
}
|
|
12967
|
+
if (name === "openai") {
|
|
12968
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
12969
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
12970
|
+
return new FakeProvider([]);
|
|
12971
|
+
}
|
|
12972
|
+
return new OpenAiProvider({ fetch: opts.fetch, grant: { network: true, apiKey } });
|
|
12973
|
+
}
|
|
12974
|
+
if (name === "gemini") {
|
|
12975
|
+
const apiKey = env.GEMINI_API_KEY !== undefined && env.GEMINI_API_KEY.length > 0 ? env.GEMINI_API_KEY : env.GOOGLE_API_KEY;
|
|
12976
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
12977
|
+
return new FakeProvider([]);
|
|
12978
|
+
}
|
|
12979
|
+
return new GeminiProvider({ fetch: opts.fetch, grant: { network: true, apiKey } });
|
|
12980
|
+
}
|
|
12015
12981
|
if (name === "ollama") {
|
|
12016
12982
|
return new OllamaProvider({
|
|
12017
12983
|
fetch: opts.fetch,
|
|
@@ -12032,18 +12998,26 @@ function makeProvider(name, _model, opts) {
|
|
|
12032
12998
|
...compat.chatPath !== undefined ? { chatPath: compat.chatPath } : {},
|
|
12033
12999
|
...apiKey !== undefined ? { apiKey } : {}
|
|
12034
13000
|
};
|
|
12035
|
-
return new
|
|
12036
|
-
fetch: opts.fetch,
|
|
12037
|
-
grant
|
|
12038
|
-
});
|
|
13001
|
+
return new OpenAiCompatEngine({ fetch: opts.fetch, grant }, OLLAMA_COMPAT_IDENTITY);
|
|
12039
13002
|
}
|
|
12040
13003
|
return new FakeProvider([]);
|
|
12041
13004
|
}
|
|
13005
|
+
var OLLAMA_COMPAT_IDENTITY;
|
|
12042
13006
|
var init_make_provider = __esm(() => {
|
|
12043
13007
|
init_providers();
|
|
12044
13008
|
init_anthropic_provider();
|
|
13009
|
+
init_openai_compat_provider();
|
|
12045
13010
|
init_fake_provider();
|
|
13011
|
+
init_gemini_provider();
|
|
12046
13012
|
init_ollama_provider();
|
|
13013
|
+
init_openai_provider();
|
|
13014
|
+
OLLAMA_COMPAT_IDENTITY = {
|
|
13015
|
+
defaultBaseUrl: "http://localhost:11434",
|
|
13016
|
+
providerRevision: "ollama-2024-10-22",
|
|
13017
|
+
providerId: "ollama",
|
|
13018
|
+
providerLabel: "Ollama",
|
|
13019
|
+
defaultModel: { modelId: "llama3.1:latest", revision: "latest" }
|
|
13020
|
+
};
|
|
12047
13021
|
});
|
|
12048
13022
|
|
|
12049
13023
|
// src/harness/provider/single-turn.ts
|
|
@@ -12062,6 +13036,14 @@ function hasCredential(provider, env) {
|
|
|
12062
13036
|
const key = env.ANTHROPIC_API_KEY;
|
|
12063
13037
|
return key !== undefined && key.length > 0;
|
|
12064
13038
|
}
|
|
13039
|
+
if (provider === "openai") {
|
|
13040
|
+
const key = env.OPENAI_API_KEY;
|
|
13041
|
+
return key !== undefined && key.length > 0;
|
|
13042
|
+
}
|
|
13043
|
+
if (provider === "gemini") {
|
|
13044
|
+
const key = env.GEMINI_API_KEY ?? env.GOOGLE_API_KEY;
|
|
13045
|
+
return key !== undefined && key.length > 0;
|
|
13046
|
+
}
|
|
12065
13047
|
const compat = providerByName(provider);
|
|
12066
13048
|
if (compat) {
|
|
12067
13049
|
if (compat.requiresApiKey === false) {
|
|
@@ -12078,6 +13060,8 @@ function hasCredential(provider, env) {
|
|
|
12078
13060
|
function keyedProviderCandidates() {
|
|
12079
13061
|
return [
|
|
12080
13062
|
"anthropic",
|
|
13063
|
+
"openai",
|
|
13064
|
+
"gemini",
|
|
12081
13065
|
...OPENAI_COMPAT_PROVIDERS.filter((provider) => provider.requiresApiKey !== false).map((p) => p.name)
|
|
12082
13066
|
];
|
|
12083
13067
|
}
|
|
@@ -12153,7 +13137,9 @@ var init_single_turn = __esm(() => {
|
|
|
12153
13137
|
init_shell_config();
|
|
12154
13138
|
DEFAULT_MODELS = {
|
|
12155
13139
|
anthropic: "claude-haiku-4-5-20251001",
|
|
12156
|
-
ollama: "llama3.2"
|
|
13140
|
+
ollama: "llama3.2",
|
|
13141
|
+
openai: "gpt-5.6-luna",
|
|
13142
|
+
gemini: "gemini-2.5-flash-lite"
|
|
12157
13143
|
};
|
|
12158
13144
|
});
|
|
12159
13145
|
|
|
@@ -13196,23 +14182,23 @@ var init_slate = __esm(() => {
|
|
|
13196
14182
|
// src/flow/store.ts
|
|
13197
14183
|
var exports_store = {};
|
|
13198
14184
|
__export(exports_store, {
|
|
13199
|
-
|
|
13200
|
-
slugify: () => slugify2,
|
|
13201
|
-
resolveFlowDir: () => resolveFlowDir,
|
|
13202
|
-
readFlow: () => readFlow,
|
|
13203
|
-
readAcCriteria: () => readAcCriteria,
|
|
13204
|
-
nextFlowId: () => nextFlowId,
|
|
13205
|
-
migrateFlow: () => migrateFlow,
|
|
13206
|
-
listFlowDirs: () => listFlowDirs,
|
|
13207
|
-
groupFlowDirsById: () => groupFlowDirsById,
|
|
13208
|
-
flowsRoot: () => flowsRoot,
|
|
13209
|
-
flowIdOf: () => flowIdOf,
|
|
13210
|
-
duplicateFlowIds: () => duplicateFlowIds,
|
|
13211
|
-
deriveFlowWork: () => deriveFlowWork,
|
|
13212
|
-
assertAcIntact: () => assertAcIntact,
|
|
13213
|
-
appendJournal: () => appendJournal,
|
|
14185
|
+
acChecksum: () => acChecksum,
|
|
13214
14186
|
acPath: () => acPath,
|
|
13215
|
-
|
|
14187
|
+
appendJournal: () => appendJournal,
|
|
14188
|
+
assertAcIntact: () => assertAcIntact,
|
|
14189
|
+
deriveFlowWork: () => deriveFlowWork,
|
|
14190
|
+
duplicateFlowIds: () => duplicateFlowIds,
|
|
14191
|
+
flowIdOf: () => flowIdOf,
|
|
14192
|
+
flowsRoot: () => flowsRoot,
|
|
14193
|
+
groupFlowDirsById: () => groupFlowDirsById,
|
|
14194
|
+
listFlowDirs: () => listFlowDirs,
|
|
14195
|
+
migrateFlow: () => migrateFlow,
|
|
14196
|
+
nextFlowId: () => nextFlowId,
|
|
14197
|
+
readAcCriteria: () => readAcCriteria,
|
|
14198
|
+
readFlow: () => readFlow,
|
|
14199
|
+
resolveFlowDir: () => resolveFlowDir,
|
|
14200
|
+
slugify: () => slugify2,
|
|
14201
|
+
writeFlow: () => writeFlow
|
|
13216
14202
|
});
|
|
13217
14203
|
import { createHash as createHash7 } from "crypto";
|
|
13218
14204
|
import { appendFile as appendFile2, mkdir as mkdir26, readFile as readFile32, readdir as readdir8 } from "fs/promises";
|
|
@@ -16069,7 +17055,7 @@ function renderWrapUpMemoryEntry(input2) {
|
|
|
16069
17055
|
|
|
16070
17056
|
Version: 0.1.0
|
|
16071
17057
|
Type: task-note
|
|
16072
|
-
Status:
|
|
17058
|
+
Status: accepted
|
|
16073
17059
|
Confidence: medium
|
|
16074
17060
|
|
|
16075
17061
|
## Summary
|
|
@@ -16169,7 +17155,7 @@ function renderWrapUpDecisionPage(input2) {
|
|
|
16169
17155
|
|
|
16170
17156
|
Version: 0.1.0
|
|
16171
17157
|
Type: decision
|
|
16172
|
-
Status:
|
|
17158
|
+
Status: accepted
|
|
16173
17159
|
|
|
16174
17160
|
## Summary
|
|
16175
17161
|
|
|
@@ -17897,7 +18883,7 @@ function buildAgentSystemInstruction(orient, ctx = {}) {
|
|
|
17897
18883
|
Project orientation (trusted context):
|
|
17898
18884
|
${trimmed}`;
|
|
17899
18885
|
}
|
|
17900
|
-
function
|
|
18886
|
+
function parseToolInput3(raw) {
|
|
17901
18887
|
const text = raw.trim();
|
|
17902
18888
|
if (text.length === 0) {
|
|
17903
18889
|
return {};
|
|
@@ -17940,7 +18926,7 @@ function stableStringify2(value) {
|
|
|
17940
18926
|
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify2(obj[k])}`).join(",")}}`;
|
|
17941
18927
|
}
|
|
17942
18928
|
function toolCallHash(name, input2) {
|
|
17943
|
-
const parsed =
|
|
18929
|
+
const parsed = parseToolInput3(input2);
|
|
17944
18930
|
return `${name}\x00${stableStringify2(parsed)}`;
|
|
17945
18931
|
}
|
|
17946
18932
|
function budgetUsed(state) {
|
|
@@ -18371,7 +19357,7 @@ ${modelOutput}` : modelOutput,
|
|
|
18371
19357
|
}
|
|
18372
19358
|
if (options.slateSession !== undefined && options.slateSession.opened === true) {
|
|
18373
19359
|
try {
|
|
18374
|
-
const touchedPaths = extractTouchedFromToolInput(call.name,
|
|
19360
|
+
const touchedPaths = extractTouchedFromToolInput(call.name, parseToolInput3(call.input));
|
|
18375
19361
|
const touch = await recordSlateTouch(options.slateSession.dir, touchedPaths, {
|
|
18376
19362
|
runtime: { provider: deps.providerId, model: deps.modelId }
|
|
18377
19363
|
});
|
|
@@ -18630,7 +19616,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
18630
19616
|
if (tool === undefined) {
|
|
18631
19617
|
return { output: `unknown tool: ${call.name}`, isError: true };
|
|
18632
19618
|
}
|
|
18633
|
-
const input2 =
|
|
19619
|
+
const input2 = parseToolInput3(call.input);
|
|
18634
19620
|
const validation = validateAgainstSchemaObject(tool.definition.inputSchema, input2);
|
|
18635
19621
|
if (!validation.valid) {
|
|
18636
19622
|
const detail = validation.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
|
|
@@ -23336,7 +24322,7 @@ var init_github = __esm(() => {
|
|
|
23336
24322
|
});
|
|
23337
24323
|
|
|
23338
24324
|
// src/harness/tool/metaproject-adapter.ts
|
|
23339
|
-
import { readFile as readFile55 } from "fs/promises";
|
|
24325
|
+
import { readdir as readdir14, readFile as readFile55 } from "fs/promises";
|
|
23340
24326
|
import { isAbsolute, join, relative, resolve } from "path";
|
|
23341
24327
|
function errorMessage(cause) {
|
|
23342
24328
|
return cause instanceof Error ? cause.message : String(cause);
|
|
@@ -23357,6 +24343,131 @@ function confineToWiki(cwd, candidate) {
|
|
|
23357
24343
|
}
|
|
23358
24344
|
return target;
|
|
23359
24345
|
}
|
|
24346
|
+
function confineToSkills(cwd, candidate) {
|
|
24347
|
+
const skillsRoot = join(cwd, ".metaproject", "skills", "gdskills");
|
|
24348
|
+
const target = resolve(cwd, candidate);
|
|
24349
|
+
const rel = relative(skillsRoot, target);
|
|
24350
|
+
if (rel === "") {
|
|
24351
|
+
return null;
|
|
24352
|
+
}
|
|
24353
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
24354
|
+
return null;
|
|
24355
|
+
}
|
|
24356
|
+
return target;
|
|
24357
|
+
}
|
|
24358
|
+
function stripSkillFieldQuotes(value) {
|
|
24359
|
+
if (value.length >= 2) {
|
|
24360
|
+
const first = value[0];
|
|
24361
|
+
const last = value[value.length - 1];
|
|
24362
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
24363
|
+
return value.slice(1, -1);
|
|
24364
|
+
}
|
|
24365
|
+
}
|
|
24366
|
+
return value;
|
|
24367
|
+
}
|
|
24368
|
+
function parseSkillFrontmatter(content) {
|
|
24369
|
+
if (!content.startsWith("---")) {
|
|
24370
|
+
return {};
|
|
24371
|
+
}
|
|
24372
|
+
const end = content.indexOf(`
|
|
24373
|
+
---`, 3);
|
|
24374
|
+
if (end === -1) {
|
|
24375
|
+
return {};
|
|
24376
|
+
}
|
|
24377
|
+
const lines = content.slice(3, end).split(`
|
|
24378
|
+
`);
|
|
24379
|
+
let description;
|
|
24380
|
+
const triggers = [];
|
|
24381
|
+
let inTriggers = false;
|
|
24382
|
+
for (const line of lines) {
|
|
24383
|
+
const descMatch = /^description:\s*(.*)$/.exec(line);
|
|
24384
|
+
if (descMatch !== null && descMatch[1] !== undefined) {
|
|
24385
|
+
description = stripSkillFieldQuotes(descMatch[1].trim());
|
|
24386
|
+
inTriggers = false;
|
|
24387
|
+
continue;
|
|
24388
|
+
}
|
|
24389
|
+
if (/^triggers:\s*$/.test(line)) {
|
|
24390
|
+
inTriggers = true;
|
|
24391
|
+
continue;
|
|
24392
|
+
}
|
|
24393
|
+
if (inTriggers) {
|
|
24394
|
+
const itemMatch = /^\s+-\s*(.+)$/.exec(line);
|
|
24395
|
+
if (itemMatch !== null && itemMatch[1] !== undefined) {
|
|
24396
|
+
triggers.push(stripSkillFieldQuotes(itemMatch[1].trim()));
|
|
24397
|
+
continue;
|
|
24398
|
+
}
|
|
24399
|
+
inTriggers = false;
|
|
24400
|
+
}
|
|
24401
|
+
}
|
|
24402
|
+
return { ...description !== undefined ? { description } : {}, ...triggers.length > 0 ? { triggers } : {} };
|
|
24403
|
+
}
|
|
24404
|
+
async function parseCatalogSummaries(cwd) {
|
|
24405
|
+
const summaries = new Map;
|
|
24406
|
+
let content;
|
|
24407
|
+
try {
|
|
24408
|
+
content = await readFile55(join(cwd, ".metaproject", "skills", "catalog.md"), "utf8");
|
|
24409
|
+
} catch {
|
|
24410
|
+
return summaries;
|
|
24411
|
+
}
|
|
24412
|
+
const rowPattern = /^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*$/;
|
|
24413
|
+
for (const line of content.split(`
|
|
24414
|
+
`)) {
|
|
24415
|
+
const match = rowPattern.exec(line);
|
|
24416
|
+
if (match === null) {
|
|
24417
|
+
continue;
|
|
24418
|
+
}
|
|
24419
|
+
const [, name, , purpose] = match;
|
|
24420
|
+
if (name === undefined || purpose === undefined || name === "Skill" || /^-+$/.test(name)) {
|
|
24421
|
+
continue;
|
|
24422
|
+
}
|
|
24423
|
+
summaries.set(name, purpose);
|
|
24424
|
+
}
|
|
24425
|
+
return summaries;
|
|
24426
|
+
}
|
|
24427
|
+
async function walkSkillCatalog(cwd) {
|
|
24428
|
+
const root = join(cwd, ".metaproject", "skills", "gdskills");
|
|
24429
|
+
const entries = [];
|
|
24430
|
+
let categoryDirs;
|
|
24431
|
+
try {
|
|
24432
|
+
categoryDirs = await readdir14(root, { withFileTypes: true });
|
|
24433
|
+
} catch {
|
|
24434
|
+
return [];
|
|
24435
|
+
}
|
|
24436
|
+
const catalogSummaries = await parseCatalogSummaries(cwd);
|
|
24437
|
+
for (const categoryDir of categoryDirs) {
|
|
24438
|
+
if (!categoryDir.isDirectory()) {
|
|
24439
|
+
continue;
|
|
24440
|
+
}
|
|
24441
|
+
const categoryPath = join(root, categoryDir.name);
|
|
24442
|
+
let skillDirs;
|
|
24443
|
+
try {
|
|
24444
|
+
skillDirs = await readdir14(categoryPath, { withFileTypes: true });
|
|
24445
|
+
} catch {
|
|
24446
|
+
continue;
|
|
24447
|
+
}
|
|
24448
|
+
for (const skillDir of skillDirs) {
|
|
24449
|
+
if (!skillDir.isDirectory()) {
|
|
24450
|
+
continue;
|
|
24451
|
+
}
|
|
24452
|
+
const skillMdPath = join(categoryPath, skillDir.name, "SKILL.md");
|
|
24453
|
+
let content;
|
|
24454
|
+
try {
|
|
24455
|
+
content = await readFile55(skillMdPath, "utf8");
|
|
24456
|
+
} catch {
|
|
24457
|
+
continue;
|
|
24458
|
+
}
|
|
24459
|
+
const { description, triggers } = parseSkillFrontmatter(content);
|
|
24460
|
+
entries.push({
|
|
24461
|
+
name: skillDir.name,
|
|
24462
|
+
path: relative(cwd, skillMdPath),
|
|
24463
|
+
category: categoryDir.name,
|
|
24464
|
+
description: description ?? catalogSummaries.get(skillDir.name) ?? "",
|
|
24465
|
+
...triggers !== undefined ? { triggers } : {}
|
|
24466
|
+
});
|
|
24467
|
+
}
|
|
24468
|
+
}
|
|
24469
|
+
return entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
24470
|
+
}
|
|
23360
24471
|
function createMetaprojectAdapter(cwd, overrides = {}) {
|
|
23361
24472
|
const deps = { ...DEFAULT_DEPS, ...overrides };
|
|
23362
24473
|
const gdgraph = deps.createGdgraphService();
|
|
@@ -23624,6 +24735,36 @@ function createMetaprojectAdapter(cwd, overrides = {}) {
|
|
|
23624
24735
|
} catch (cause) {
|
|
23625
24736
|
return { file: input2.file, backlinks: [], error: errorMessage(cause) };
|
|
23626
24737
|
}
|
|
24738
|
+
},
|
|
24739
|
+
async skillsCatalog() {
|
|
24740
|
+
const skills = await walkSkillCatalog(cwd);
|
|
24741
|
+
return { skills, generatedAt: deps.now() };
|
|
24742
|
+
},
|
|
24743
|
+
async loadSkill(input2) {
|
|
24744
|
+
const catalog = await walkSkillCatalog(cwd);
|
|
24745
|
+
const byName = catalog.find((entry) => entry.name === input2.name);
|
|
24746
|
+
if (byName !== undefined) {
|
|
24747
|
+
try {
|
|
24748
|
+
const content = await readFile55(join(cwd, byName.path), "utf8");
|
|
24749
|
+
return { name: input2.name, path: byName.path, content, found: true };
|
|
24750
|
+
} catch {
|
|
24751
|
+
return { name: input2.name, path: "", content: "", found: false };
|
|
24752
|
+
}
|
|
24753
|
+
}
|
|
24754
|
+
const confined = confineToSkills(cwd, input2.name);
|
|
24755
|
+
if (confined === null) {
|
|
24756
|
+
return { name: input2.name, path: "", content: "", found: false };
|
|
24757
|
+
}
|
|
24758
|
+
const byPath = catalog.find((entry) => join(cwd, entry.path) === confined);
|
|
24759
|
+
if (byPath === undefined) {
|
|
24760
|
+
return { name: input2.name, path: "", content: "", found: false };
|
|
24761
|
+
}
|
|
24762
|
+
try {
|
|
24763
|
+
const content = await readFile55(confined, "utf8");
|
|
24764
|
+
return { name: input2.name, path: byPath.path, content, found: true };
|
|
24765
|
+
} catch {
|
|
24766
|
+
return { name: input2.name, path: "", content: "", found: false };
|
|
24767
|
+
}
|
|
23627
24768
|
}
|
|
23628
24769
|
};
|
|
23629
24770
|
}
|
|
@@ -23660,6 +24801,7 @@ var init_metaproject_adapter = __esm(() => {
|
|
|
23660
24801
|
}),
|
|
23661
24802
|
wikiAsk,
|
|
23662
24803
|
wikiPagesForFile,
|
|
24804
|
+
now: () => new Date().toISOString(),
|
|
23663
24805
|
repomapCompute: async (cwd, options) => {
|
|
23664
24806
|
const [graph, config] = await Promise.all([loadGraph(cwd), loadGdgraphConfig(cwd)]);
|
|
23665
24807
|
return computeRepomap(graph, config, options);
|
|
@@ -23734,6 +24876,24 @@ function formatWiki(result) {
|
|
|
23734
24876
|
}
|
|
23735
24877
|
return { output: result.content.length > 0 ? result.content : "(empty page)", isError: false };
|
|
23736
24878
|
}
|
|
24879
|
+
function formatSkillsCatalog(result) {
|
|
24880
|
+
if (result.skills.length === 0) {
|
|
24881
|
+
return { output: "No skills found under .metaproject/skills/gdskills/.", isError: false };
|
|
24882
|
+
}
|
|
24883
|
+
const lines = result.skills.map((skill2) => {
|
|
24884
|
+
const triggers = skill2.triggers !== undefined && skill2.triggers.length > 0 ? ` [triggers: ${skill2.triggers.join(", ")}]` : "";
|
|
24885
|
+
return ` - ${skill2.name} (${skill2.category}) \u2014 ${skill2.description || "(no description)"}${triggers}
|
|
24886
|
+
${skill2.path}`;
|
|
24887
|
+
});
|
|
24888
|
+
return { output: [`Skills (${result.skills.length}):`, ...lines].join(`
|
|
24889
|
+
`), isError: false };
|
|
24890
|
+
}
|
|
24891
|
+
function formatSkillLoad(result) {
|
|
24892
|
+
if (!result.found) {
|
|
24893
|
+
return { output: `skill_load: no skill found for '${result.name}'.`, isError: true };
|
|
24894
|
+
}
|
|
24895
|
+
return { output: result.content, isError: false };
|
|
24896
|
+
}
|
|
23737
24897
|
function formatPath(result) {
|
|
23738
24898
|
if (result.error !== undefined) {
|
|
23739
24899
|
return { output: `graph_path failed: ${result.error}`, isError: true };
|
|
@@ -23847,7 +25007,7 @@ function toInteractiveTools(ops, port) {
|
|
|
23847
25007
|
invoke: (input2) => op.invoke(port, input2)
|
|
23848
25008
|
}));
|
|
23849
25009
|
}
|
|
23850
|
-
var AFFECTED_OUTPUT_SCHEMA, QUERY_OUTPUT_SCHEMA, MEMORY_OUTPUT_SCHEMA, WIKI_OUTPUT_SCHEMA, SEARCH_OUTPUT_SCHEMA, PATH_OUTPUT_SCHEMA, TEST_RELATED_OUTPUT_SCHEMA, HEALTH_OUTPUT_SCHEMA, SYMBOL_OUTPUT_SCHEMA, REPOMAP_OUTPUT_SCHEMA, WIKI_ASK_OUTPUT_SCHEMA, WIKI_BACKLINKS_OUTPUT_SCHEMA, FLOW_STATUS_OUTPUT_SCHEMA, METAPROJECT_OPERATIONS;
|
|
25010
|
+
var AFFECTED_OUTPUT_SCHEMA, QUERY_OUTPUT_SCHEMA, MEMORY_OUTPUT_SCHEMA, WIKI_OUTPUT_SCHEMA, SEARCH_OUTPUT_SCHEMA, PATH_OUTPUT_SCHEMA, TEST_RELATED_OUTPUT_SCHEMA, HEALTH_OUTPUT_SCHEMA, SYMBOL_OUTPUT_SCHEMA, REPOMAP_OUTPUT_SCHEMA, WIKI_ASK_OUTPUT_SCHEMA, WIKI_BACKLINKS_OUTPUT_SCHEMA, FLOW_STATUS_OUTPUT_SCHEMA, SKILLS_CATALOG_OUTPUT_SCHEMA, SKILL_LOAD_OUTPUT_SCHEMA, METAPROJECT_OPERATIONS;
|
|
23851
25011
|
var init_metaproject_operations = __esm(() => {
|
|
23852
25012
|
AFFECTED_OUTPUT_SCHEMA = {
|
|
23853
25013
|
type: "object",
|
|
@@ -23984,6 +25144,24 @@ var init_metaproject_operations = __esm(() => {
|
|
|
23984
25144
|
},
|
|
23985
25145
|
required: ["flows"]
|
|
23986
25146
|
};
|
|
25147
|
+
SKILLS_CATALOG_OUTPUT_SCHEMA = {
|
|
25148
|
+
type: "object",
|
|
25149
|
+
properties: {
|
|
25150
|
+
skills: { type: "array" },
|
|
25151
|
+
generatedAt: { type: "string" }
|
|
25152
|
+
},
|
|
25153
|
+
required: ["skills", "generatedAt"]
|
|
25154
|
+
};
|
|
25155
|
+
SKILL_LOAD_OUTPUT_SCHEMA = {
|
|
25156
|
+
type: "object",
|
|
25157
|
+
properties: {
|
|
25158
|
+
name: { type: "string" },
|
|
25159
|
+
path: { type: "string" },
|
|
25160
|
+
content: { type: "string" },
|
|
25161
|
+
found: { type: "boolean" }
|
|
25162
|
+
},
|
|
25163
|
+
required: ["name", "path", "content", "found"]
|
|
25164
|
+
};
|
|
23987
25165
|
METAPROJECT_OPERATIONS = [
|
|
23988
25166
|
{
|
|
23989
25167
|
name: "search_code",
|
|
@@ -24260,6 +25438,47 @@ var init_metaproject_operations = __esm(() => {
|
|
|
24260
25438
|
const id = typeof input2.id === "string" && input2.id.trim().length > 0 ? input2.id.trim() : undefined;
|
|
24261
25439
|
return formatFlowStatus(await port.flowStatus(id !== undefined ? { id } : {}));
|
|
24262
25440
|
}
|
|
25441
|
+
},
|
|
25442
|
+
{
|
|
25443
|
+
name: "skills_catalog",
|
|
25444
|
+
risk: "read",
|
|
25445
|
+
module: "gdskills",
|
|
25446
|
+
description: "List every skill discovered under .metaproject/skills/gdskills/ \u2014 name, category, description, and " + "triggers for each. Use this instead of reading .metaproject/index.md or catalog.md to find which skill " + "applies to a task. No input.",
|
|
25447
|
+
inputSchema: {
|
|
25448
|
+
type: "object",
|
|
25449
|
+
properties: {},
|
|
25450
|
+
additionalProperties: false
|
|
25451
|
+
},
|
|
25452
|
+
outputSchema: SKILLS_CATALOG_OUTPUT_SCHEMA,
|
|
25453
|
+
invoke: async (port) => {
|
|
25454
|
+
if (port.skillsCatalog === undefined) {
|
|
25455
|
+
return { output: "skills_catalog is not available in this session.", isError: true };
|
|
25456
|
+
}
|
|
25457
|
+
return formatSkillsCatalog(await port.skillsCatalog({}));
|
|
25458
|
+
}
|
|
25459
|
+
},
|
|
25460
|
+
{
|
|
25461
|
+
name: "skill_load",
|
|
25462
|
+
risk: "read",
|
|
25463
|
+
module: "gdskills",
|
|
25464
|
+
description: `Load one skill's full SKILL.md content by name (e.g. "flow-orchestrator") or exact project-relative ` + "path, as returned by `skills_catalog`. Input: { name: string }.",
|
|
25465
|
+
inputSchema: {
|
|
25466
|
+
type: "object",
|
|
25467
|
+
properties: { name: { type: "string" } },
|
|
25468
|
+
required: ["name"],
|
|
25469
|
+
additionalProperties: false
|
|
25470
|
+
},
|
|
25471
|
+
outputSchema: SKILL_LOAD_OUTPUT_SCHEMA,
|
|
25472
|
+
invoke: async (port, input2) => {
|
|
25473
|
+
if (port.loadSkill === undefined) {
|
|
25474
|
+
return { output: "skill_load is not available in this session.", isError: true };
|
|
25475
|
+
}
|
|
25476
|
+
const name = requireString(input2, "name", "skill_load");
|
|
25477
|
+
if ("error" in name) {
|
|
25478
|
+
return name.error;
|
|
25479
|
+
}
|
|
25480
|
+
return formatSkillLoad(await port.loadSkill({ name: name.value }));
|
|
25481
|
+
}
|
|
24263
25482
|
}
|
|
24264
25483
|
];
|
|
24265
25484
|
});
|
|
@@ -24513,30 +25732,30 @@ var init_staleness2 = __esm(() => {
|
|
|
24513
25732
|
// src/wiki/enrich.ts
|
|
24514
25733
|
var exports_enrich2 = {};
|
|
24515
25734
|
__export(exports_enrich2, {
|
|
24516
|
-
|
|
24517
|
-
validateEnrichedMarkdown: () => validateEnrichedMarkdown,
|
|
24518
|
-
setFrontmatterStatus: () => setFrontmatterStatus,
|
|
24519
|
-
selectPages: () => selectPages,
|
|
24520
|
-
saveResumeState: () => saveResumeState,
|
|
24521
|
-
resumeStatePath: () => resumeStatePath,
|
|
24522
|
-
resolveEnrichProviderModel: () => resolveEnrichProviderModel,
|
|
24523
|
-
repairEnrichedFrontmatter: () => repairEnrichedFrontmatter,
|
|
24524
|
-
planWikiEnrich: () => planWikiEnrich,
|
|
24525
|
-
parseBatchResponse: () => parseBatchResponse,
|
|
24526
|
-
mapPool: () => mapPool,
|
|
24527
|
-
loadResumeState: () => loadResumeState,
|
|
24528
|
-
isWikiEnrichIntent: () => isWikiEnrichIntent,
|
|
24529
|
-
hasYamlFrontmatter: () => hasYamlFrontmatter,
|
|
24530
|
-
hasCredential: () => hasCredential,
|
|
24531
|
-
groupLightPagesIntoBatches: () => groupLightPagesIntoBatches,
|
|
24532
|
-
extractYamlFrontmatterBlock: () => extractYamlFrontmatterBlock,
|
|
24533
|
-
ensureWikiFrontmatter: () => ensureWikiFrontmatter,
|
|
24534
|
-
defaultEnrichProgress: () => defaultEnrichProgress,
|
|
24535
|
-
buildBatchUserPrompt: () => buildBatchUserPrompt,
|
|
24536
|
-
batchGroupKey: () => batchGroupKey,
|
|
24537
|
-
MAX_CONCURRENCY: () => MAX_CONCURRENCY,
|
|
25735
|
+
DEFAULT_CONCURRENCY: () => DEFAULT_CONCURRENCY,
|
|
24538
25736
|
DEFAULT_MAX_OUTPUT_TOKENS: () => DEFAULT_MAX_OUTPUT_TOKENS,
|
|
24539
|
-
|
|
25737
|
+
MAX_CONCURRENCY: () => MAX_CONCURRENCY,
|
|
25738
|
+
batchGroupKey: () => batchGroupKey,
|
|
25739
|
+
buildBatchUserPrompt: () => buildBatchUserPrompt,
|
|
25740
|
+
defaultEnrichProgress: () => defaultEnrichProgress,
|
|
25741
|
+
ensureWikiFrontmatter: () => ensureWikiFrontmatter,
|
|
25742
|
+
extractYamlFrontmatterBlock: () => extractYamlFrontmatterBlock,
|
|
25743
|
+
groupLightPagesIntoBatches: () => groupLightPagesIntoBatches,
|
|
25744
|
+
hasCredential: () => hasCredential,
|
|
25745
|
+
hasYamlFrontmatter: () => hasYamlFrontmatter,
|
|
25746
|
+
isWikiEnrichIntent: () => isWikiEnrichIntent,
|
|
25747
|
+
loadResumeState: () => loadResumeState,
|
|
25748
|
+
mapPool: () => mapPool,
|
|
25749
|
+
parseBatchResponse: () => parseBatchResponse,
|
|
25750
|
+
planWikiEnrich: () => planWikiEnrich,
|
|
25751
|
+
repairEnrichedFrontmatter: () => repairEnrichedFrontmatter,
|
|
25752
|
+
resolveEnrichProviderModel: () => resolveEnrichProviderModel,
|
|
25753
|
+
resumeStatePath: () => resumeStatePath,
|
|
25754
|
+
saveResumeState: () => saveResumeState,
|
|
25755
|
+
selectPages: () => selectPages,
|
|
25756
|
+
setFrontmatterStatus: () => setFrontmatterStatus,
|
|
25757
|
+
validateEnrichedMarkdown: () => validateEnrichedMarkdown,
|
|
25758
|
+
wikiEnrich: () => wikiEnrich
|
|
24540
25759
|
});
|
|
24541
25760
|
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
24542
25761
|
import { readFile as readFile57, writeFile as writeFile36 } from "fs/promises";
|
|
@@ -25588,9 +26807,9 @@ var init_enrich2 = __esm(() => {
|
|
|
25588
26807
|
// src/sync/hooks.ts
|
|
25589
26808
|
var exports_hooks = {};
|
|
25590
26809
|
__export(exports_hooks, {
|
|
25591
|
-
|
|
26810
|
+
SYNC_HOOKS: () => SYNC_HOOKS,
|
|
25592
26811
|
installSyncHooks: () => installSyncHooks,
|
|
25593
|
-
|
|
26812
|
+
uninstallSyncHooks: () => uninstallSyncHooks
|
|
25594
26813
|
});
|
|
25595
26814
|
import { chmod as chmod3, mkdir as mkdir42, readFile as readFile59, writeFile as writeFile38 } from "fs/promises";
|
|
25596
26815
|
import path111 from "path";
|
|
@@ -25773,8 +26992,8 @@ var init_reflect = __esm(() => {
|
|
|
25773
26992
|
// src/lib/narrate.ts
|
|
25774
26993
|
var exports_narrate = {};
|
|
25775
26994
|
__export(exports_narrate, {
|
|
25776
|
-
|
|
25777
|
-
|
|
26995
|
+
formatMissingCredentialHint: () => formatMissingCredentialHint,
|
|
26996
|
+
narrate: () => narrate
|
|
25778
26997
|
});
|
|
25779
26998
|
function formatMissingCredentialHint(provider) {
|
|
25780
26999
|
const env = envWithSavedApiKeys(process.env);
|
|
@@ -26206,9 +27425,9 @@ var init_memory = __esm(() => {
|
|
|
26206
27425
|
// src/lib/contained-path.ts
|
|
26207
27426
|
var exports_contained_path = {};
|
|
26208
27427
|
__export(exports_contained_path, {
|
|
26209
|
-
|
|
27428
|
+
resolveContainedPath: () => resolveContainedPath,
|
|
26210
27429
|
resolveContainedPathSync: () => resolveContainedPathSync,
|
|
26211
|
-
|
|
27430
|
+
resolveProjectRoot: () => resolveProjectRoot2
|
|
26212
27431
|
});
|
|
26213
27432
|
import { realpath as realpath3 } from "fs/promises";
|
|
26214
27433
|
import { existsSync as existsSync21, realpathSync as realpathSync2 } from "fs";
|
|
@@ -32398,10 +33617,72 @@ var GENERIC_RUNTIME = {
|
|
|
32398
33617
|
validate: mcpValidate("generic"),
|
|
32399
33618
|
hasManaged: mcpHasManaged
|
|
32400
33619
|
};
|
|
33620
|
+
function readVscodeServers(settings) {
|
|
33621
|
+
return typeof settings.servers === "object" && settings.servers !== null && !Array.isArray(settings.servers) ? { ...settings.servers } : {};
|
|
33622
|
+
}
|
|
33623
|
+
function vscodeManagedEntry(projectRoot) {
|
|
33624
|
+
return {
|
|
33625
|
+
type: "stdio",
|
|
33626
|
+
...mcpServerEntry(projectRoot),
|
|
33627
|
+
[MCP_MANAGED_KEY]: MCP_MANAGED_SENTINEL
|
|
33628
|
+
};
|
|
33629
|
+
}
|
|
33630
|
+
function vscodeMerge(settings, projectRoot) {
|
|
33631
|
+
const servers = readVscodeServers(settings);
|
|
33632
|
+
servers[MCP_SERVER_NAME] = vscodeManagedEntry(projectRoot);
|
|
33633
|
+
settings.servers = servers;
|
|
33634
|
+
return settings;
|
|
33635
|
+
}
|
|
33636
|
+
function vscodeStrip(settings) {
|
|
33637
|
+
if (typeof settings.servers !== "object" || settings.servers === null || Array.isArray(settings.servers)) {
|
|
33638
|
+
return settings;
|
|
33639
|
+
}
|
|
33640
|
+
const servers = { ...settings.servers };
|
|
33641
|
+
if (isManagedEntry(servers[MCP_SERVER_NAME])) {
|
|
33642
|
+
delete servers[MCP_SERVER_NAME];
|
|
33643
|
+
}
|
|
33644
|
+
if (Object.keys(servers).length > 0)
|
|
33645
|
+
settings.servers = servers;
|
|
33646
|
+
else
|
|
33647
|
+
delete settings.servers;
|
|
33648
|
+
return settings;
|
|
33649
|
+
}
|
|
33650
|
+
function vscodeValidate(settings) {
|
|
33651
|
+
const errors = [];
|
|
33652
|
+
const servers = settings.servers;
|
|
33653
|
+
if (typeof servers !== "object" || servers === null || Array.isArray(servers)) {
|
|
33654
|
+
errors.push("vscode: servers is missing or not an object");
|
|
33655
|
+
return errors;
|
|
33656
|
+
}
|
|
33657
|
+
const entry = servers[MCP_SERVER_NAME];
|
|
33658
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
33659
|
+
errors.push(`vscode: missing servers.${MCP_SERVER_NAME} entry`);
|
|
33660
|
+
return errors;
|
|
33661
|
+
}
|
|
33662
|
+
if (entry.type !== "stdio") {
|
|
33663
|
+
errors.push(`vscode: servers.${MCP_SERVER_NAME}.type must be "stdio"`);
|
|
33664
|
+
}
|
|
33665
|
+
if (entry.command !== MCP_SERVER_COMMAND) {
|
|
33666
|
+
errors.push(`vscode: servers.${MCP_SERVER_NAME}.command must be "${MCP_SERVER_COMMAND}"`);
|
|
33667
|
+
}
|
|
33668
|
+
return errors;
|
|
33669
|
+
}
|
|
33670
|
+
function vscodeHasManaged(settings) {
|
|
33671
|
+
return isManagedEntry(readVscodeServers(settings)[MCP_SERVER_NAME]);
|
|
33672
|
+
}
|
|
33673
|
+
var VSCODE_RUNTIME = {
|
|
33674
|
+
id: "vscode",
|
|
33675
|
+
settingsPath: (root) => path40.join(root, ".vscode", "mcp.json"),
|
|
33676
|
+
merge: vscodeMerge,
|
|
33677
|
+
strip: vscodeStrip,
|
|
33678
|
+
validate: vscodeValidate,
|
|
33679
|
+
hasManaged: vscodeHasManaged
|
|
33680
|
+
};
|
|
32401
33681
|
var MCP_CLIENT_RUNTIMES = [
|
|
32402
33682
|
CURSOR_RUNTIME,
|
|
32403
33683
|
CLAUDE_RUNTIME2,
|
|
32404
33684
|
OPENCODE_RUNTIME,
|
|
33685
|
+
VSCODE_RUNTIME,
|
|
32405
33686
|
GENERIC_RUNTIME
|
|
32406
33687
|
];
|
|
32407
33688
|
var ALL_RUNTIME_IDS = ["cursor", "claude", "opencode"];
|
|
@@ -32428,6 +33709,21 @@ function renderMcpClientSnippet(projectRoot) {
|
|
|
32428
33709
|
return `${JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: mcpServerEntry(projectRoot) } }, null, 2)}
|
|
32429
33710
|
`;
|
|
32430
33711
|
}
|
|
33712
|
+
async function mcpClientStatus(projectRoot, ids = mcpRuntimeIds()) {
|
|
33713
|
+
const absoluteProjectRoot = path40.resolve(projectRoot);
|
|
33714
|
+
const { runtimes } = resolveMcpRuntimes(ids);
|
|
33715
|
+
const statuses = [];
|
|
33716
|
+
for (const runtime of runtimes) {
|
|
33717
|
+
const file = runtime.settingsPath(absoluteProjectRoot);
|
|
33718
|
+
if (file === null) {
|
|
33719
|
+
statuses.push({ id: runtime.id, filePath: null, connected: false });
|
|
33720
|
+
continue;
|
|
33721
|
+
}
|
|
33722
|
+
const settings = await readSettings2(file);
|
|
33723
|
+
statuses.push({ id: runtime.id, filePath: file, connected: runtime.hasManaged(settings) });
|
|
33724
|
+
}
|
|
33725
|
+
return statuses;
|
|
33726
|
+
}
|
|
32431
33727
|
async function readSettings2(file) {
|
|
32432
33728
|
if (!await pathExists(file)) {
|
|
32433
33729
|
return {};
|
|
@@ -32471,7 +33767,7 @@ function buildMcpModuleEntry() {
|
|
|
32471
33767
|
expose: {
|
|
32472
33768
|
tools: true,
|
|
32473
33769
|
resources: true,
|
|
32474
|
-
modules: ["gdgraph", "gdctx", "security", "flow", "memory", "health", "testing", "wiki", "standard", "sac"]
|
|
33770
|
+
modules: ["gdgraph", "gdctx", "security", "flow", "memory", "health", "testing", "wiki", "standard", "sac", "gdskills"]
|
|
32475
33771
|
}
|
|
32476
33772
|
};
|
|
32477
33773
|
}
|
|
@@ -32499,16 +33795,18 @@ protocol adapter \u2014 it defines no new module logic.
|
|
|
32499
33795
|
requires \`http.enabled=true\` in this module's manifest entry).
|
|
32500
33796
|
- \`keryx mcp serve --cwd <project-root>\` \u2014 expose a specific project,
|
|
32501
33797
|
independent of the MCP client's launch directory.
|
|
32502
|
-
- \`keryx mcp install --runtime <cursor|claude|opencode|generic|all> [--dry-run]\` \u2014
|
|
33798
|
+
- \`keryx mcp install --runtime <cursor|claude|opencode|vscode|generic|all> [--dry-run]\` \u2014
|
|
32503
33799
|
wire this project into an editor/agent: writes a project-local client
|
|
32504
33800
|
config (cursor \u2192 \`.cursor/mcp.json\`, claude \u2192 \`.mcp.json\`, opencode \u2192
|
|
32505
|
-
\`opencode.json\`) and sets
|
|
32506
|
-
prints the change without
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
cursor + claude +
|
|
32511
|
-
|
|
33801
|
+
\`opencode.json\`, vscode \u2192 \`.vscode/mcp.json\`) and sets
|
|
33802
|
+
\`modules.mcp.enabled=true\`. \`--dry-run\` prints the change without
|
|
33803
|
+
writing anything. This is the command to run when a user asks to
|
|
33804
|
+
"connect" or "enable" MCP for this project \u2014 it is the full, real setup
|
|
33805
|
+
step; hand-editing a client config file directly is unnecessary and skips
|
|
33806
|
+
setting \`modules.mcp.enabled\`. \`all\` expands to cursor + claude +
|
|
33807
|
+
opencode; \`vscode\` is opt-in only (not bundled into \`all\`) \u2014 request it
|
|
33808
|
+
explicitly with \`--runtime vscode\`.
|
|
33809
|
+
- \`keryx mcp uninstall --runtime <cursor|claude|opencode|vscode|generic|all>\` \u2014
|
|
32512
33810
|
remove the managed client config again.
|
|
32513
33811
|
- **codex CLI**: not a \`--runtime\` here \u2014 codex's client config is a single
|
|
32514
33812
|
GLOBAL \`~/.codex/config.toml\`, not a project-local file, and it already
|
|
@@ -32790,7 +34088,14 @@ skill's Reporting section).
|
|
|
32790
34088
|
}
|
|
32791
34089
|
|
|
32792
34090
|
// src/commands/init.ts
|
|
32793
|
-
var MCP_INIT_RUNTIMES = [
|
|
34091
|
+
var MCP_INIT_RUNTIMES = [
|
|
34092
|
+
"cursor",
|
|
34093
|
+
"claude",
|
|
34094
|
+
"opencode",
|
|
34095
|
+
"vscode",
|
|
34096
|
+
"generic",
|
|
34097
|
+
"skip"
|
|
34098
|
+
];
|
|
32794
34099
|
async function initCommand(args) {
|
|
32795
34100
|
const options = parseInitArgs(args);
|
|
32796
34101
|
if (options.help) {
|
|
@@ -35962,7 +37267,7 @@ the derived layers in step.
|
|
|
35962
37267
|
init_args();
|
|
35963
37268
|
init_fs();
|
|
35964
37269
|
init_json();
|
|
35965
|
-
import { readdir as
|
|
37270
|
+
import { readdir as readdir18, readFile as readFile64 } from "fs/promises";
|
|
35966
37271
|
import path120 from "path";
|
|
35967
37272
|
|
|
35968
37273
|
// src/gdskills/contracts.ts
|
|
@@ -36094,7 +37399,7 @@ async function validateValue(value, schema, valuePath, errors, rootSchema, schem
|
|
|
36094
37399
|
});
|
|
36095
37400
|
}
|
|
36096
37401
|
}
|
|
36097
|
-
if (
|
|
37402
|
+
if (isPlainObject8(value)) {
|
|
36098
37403
|
const required2 = schema.required ?? [];
|
|
36099
37404
|
for (const key of required2) {
|
|
36100
37405
|
if (!(key in value)) {
|
|
@@ -36114,7 +37419,7 @@ async function validateValue(value, schema, valuePath, errors, rootSchema, schem
|
|
|
36114
37419
|
path: `${valuePath}.${key}`,
|
|
36115
37420
|
message: "Additional property is not allowed"
|
|
36116
37421
|
});
|
|
36117
|
-
} else if (
|
|
37422
|
+
} else if (isPlainObject8(schema.additionalProperties)) {
|
|
36118
37423
|
await validateValue(nestedValue, schema.additionalProperties, `${valuePath}.${key}`, errors, rootSchema, schemaCache);
|
|
36119
37424
|
}
|
|
36120
37425
|
}
|
|
@@ -36173,11 +37478,11 @@ function matchesType3(value, type) {
|
|
|
36173
37478
|
if (entry === "integer")
|
|
36174
37479
|
return Number.isInteger(value);
|
|
36175
37480
|
if (entry === "object")
|
|
36176
|
-
return
|
|
37481
|
+
return isPlainObject8(value);
|
|
36177
37482
|
return typeof value === entry;
|
|
36178
37483
|
});
|
|
36179
37484
|
}
|
|
36180
|
-
function
|
|
37485
|
+
function isPlainObject8(value) {
|
|
36181
37486
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36182
37487
|
}
|
|
36183
37488
|
function formatType(type) {
|
|
@@ -36594,7 +37899,7 @@ function unique(values) {
|
|
|
36594
37899
|
// src/gdskills/export.ts
|
|
36595
37900
|
init_fs();
|
|
36596
37901
|
init_json();
|
|
36597
|
-
import { copyFile as copyFile3, mkdir as mkdir45, readdir as
|
|
37902
|
+
import { copyFile as copyFile3, mkdir as mkdir45, readdir as readdir16, writeFile as writeFile40 } from "fs/promises";
|
|
36598
37903
|
import path117 from "path";
|
|
36599
37904
|
|
|
36600
37905
|
// src/gdskills/resolve.ts
|
|
@@ -36644,7 +37949,7 @@ async function normalizePackagePath(candidate) {
|
|
|
36644
37949
|
// src/gdskills/export-plugin.ts
|
|
36645
37950
|
init_fs();
|
|
36646
37951
|
init_json();
|
|
36647
|
-
import { copyFile as copyFile2, mkdir as mkdir44, readdir as
|
|
37952
|
+
import { copyFile as copyFile2, mkdir as mkdir44, readdir as readdir15, readFile as readFile62, writeFile as writeFile39 } from "fs/promises";
|
|
36648
37953
|
import path116 from "path";
|
|
36649
37954
|
var SAFE_DIRS = ["references", "templates", "assets", "scripts"];
|
|
36650
37955
|
function skillTitle(skillMd, fallback) {
|
|
@@ -36666,7 +37971,7 @@ function skillVersion(skillMd) {
|
|
|
36666
37971
|
return skillMd.match(/^Version:\s*(.+)$/m)?.[1]?.trim() ?? "0.1.0";
|
|
36667
37972
|
}
|
|
36668
37973
|
async function listFiles(root) {
|
|
36669
|
-
const entries = await
|
|
37974
|
+
const entries = await readdir15(root, { withFileTypes: true });
|
|
36670
37975
|
const files = [];
|
|
36671
37976
|
for (const entry of entries) {
|
|
36672
37977
|
const full = path116.join(root, entry.name);
|
|
@@ -36856,7 +38161,7 @@ async function copyDirectoryIfExists(sourceDir, targetDir) {
|
|
|
36856
38161
|
}
|
|
36857
38162
|
}
|
|
36858
38163
|
async function listFiles2(root) {
|
|
36859
|
-
const entries = await
|
|
38164
|
+
const entries = await readdir16(root, { withFileTypes: true });
|
|
36860
38165
|
const files = [];
|
|
36861
38166
|
for (const entry of entries) {
|
|
36862
38167
|
const entryPath = path117.join(root, entry.name);
|
|
@@ -36877,7 +38182,7 @@ function inferModuleFromPackageRoot(packageRoot) {
|
|
|
36877
38182
|
|
|
36878
38183
|
// src/gdskills/sync.ts
|
|
36879
38184
|
init_fs();
|
|
36880
|
-
import { copyFile as copyFile4, mkdir as mkdir46, readdir as
|
|
38185
|
+
import { copyFile as copyFile4, mkdir as mkdir46, readdir as readdir17, writeFile as writeFile41 } from "fs/promises";
|
|
36881
38186
|
import path118 from "path";
|
|
36882
38187
|
async function syncRuntimeSkills(projectRoot, options) {
|
|
36883
38188
|
const metaprojectRoot = path118.join(projectRoot, ".metaproject");
|
|
@@ -36942,7 +38247,7 @@ function validateSyncTarget(projectRoot, targetRoot) {
|
|
|
36942
38247
|
}
|
|
36943
38248
|
}
|
|
36944
38249
|
async function listSkillArtifactDirs(sourceRoot) {
|
|
36945
|
-
const entries = await
|
|
38250
|
+
const entries = await readdir17(sourceRoot, { withFileTypes: true });
|
|
36946
38251
|
const dirs = [];
|
|
36947
38252
|
for (const entry of entries) {
|
|
36948
38253
|
if (!entry.isDirectory()) {
|
|
@@ -36972,7 +38277,7 @@ async function copyDirectory(sourceDir, targetDir) {
|
|
|
36972
38277
|
}
|
|
36973
38278
|
}
|
|
36974
38279
|
async function listFiles3(root) {
|
|
36975
|
-
const entries = await
|
|
38280
|
+
const entries = await readdir17(root, { withFileTypes: true });
|
|
36976
38281
|
const files = [];
|
|
36977
38282
|
for (const entry of entries) {
|
|
36978
38283
|
const entryPath = path118.join(root, entry.name);
|
|
@@ -38150,7 +39455,7 @@ async function listJsonFiles(root) {
|
|
|
38150
39455
|
if (!await pathExists(root)) {
|
|
38151
39456
|
return [];
|
|
38152
39457
|
}
|
|
38153
|
-
const entries = await
|
|
39458
|
+
const entries = await readdir18(root, { withFileTypes: true });
|
|
38154
39459
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path120.join(root, entry.name));
|
|
38155
39460
|
}
|
|
38156
39461
|
function countVerificationStatuses(reports) {
|
|
@@ -38326,14 +39631,14 @@ init_service6();
|
|
|
38326
39631
|
// src/health/history.ts
|
|
38327
39632
|
init_fs();
|
|
38328
39633
|
init_util();
|
|
38329
|
-
import { readdir as
|
|
39634
|
+
import { readdir as readdir19, readFile as readFile65 } from "fs/promises";
|
|
38330
39635
|
import path121 from "path";
|
|
38331
39636
|
async function loadHistory(cwd, limit = 20) {
|
|
38332
39637
|
const dir = path121.join(dataRoot2(cwd), "history");
|
|
38333
39638
|
if (!await pathExists(dir)) {
|
|
38334
39639
|
return [];
|
|
38335
39640
|
}
|
|
38336
|
-
const files = (await
|
|
39641
|
+
const files = (await readdir19(dir)).filter((file) => file.endsWith(".json")).sort();
|
|
38337
39642
|
const points = [];
|
|
38338
39643
|
for (const file of files.slice(-limit)) {
|
|
38339
39644
|
try {
|
|
@@ -39320,7 +40625,7 @@ init_args();
|
|
|
39320
40625
|
init_validator();
|
|
39321
40626
|
init_fs();
|
|
39322
40627
|
init_store2();
|
|
39323
|
-
import { mkdir as mkdir48, readFile as readFile66, readdir as
|
|
40628
|
+
import { mkdir as mkdir48, readFile as readFile66, readdir as readdir20 } from "fs/promises";
|
|
39324
40629
|
import path124 from "path";
|
|
39325
40630
|
|
|
39326
40631
|
// src/review/types.ts
|
|
@@ -39748,7 +41053,7 @@ async function resolveReviewPackagePath(cwd, ref) {
|
|
|
39748
41053
|
if (!await pathExists(reviewsDir)) {
|
|
39749
41054
|
continue;
|
|
39750
41055
|
}
|
|
39751
|
-
for (const entry of await
|
|
41056
|
+
for (const entry of await readdir20(reviewsDir, { withFileTypes: true })) {
|
|
39752
41057
|
if (entry.isDirectory() && entry.name === ref) {
|
|
39753
41058
|
return path124.join(reviewsDir, entry.name);
|
|
39754
41059
|
}
|
|
@@ -40135,7 +41440,7 @@ var SCHEMA_REGISTRY2 = {
|
|
|
40135
41440
|
|
|
40136
41441
|
// src/standard/validate.ts
|
|
40137
41442
|
var DATE_TIME_PATTERN3 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
40138
|
-
function
|
|
41443
|
+
function isPlainObject9(value) {
|
|
40139
41444
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40140
41445
|
}
|
|
40141
41446
|
function matchesType4(value, type) {
|
|
@@ -40148,7 +41453,7 @@ function matchesType4(value, type) {
|
|
|
40148
41453
|
if (entry === "integer")
|
|
40149
41454
|
return Number.isInteger(value);
|
|
40150
41455
|
if (entry === "object")
|
|
40151
|
-
return
|
|
41456
|
+
return isPlainObject9(value);
|
|
40152
41457
|
return typeof value === entry;
|
|
40153
41458
|
});
|
|
40154
41459
|
}
|
|
@@ -40223,7 +41528,7 @@ function walk4(value, schema, valuePath, rootSchema, errors) {
|
|
|
40223
41528
|
errors.push({ path: valuePath, message: "Expected array items to be unique" });
|
|
40224
41529
|
}
|
|
40225
41530
|
}
|
|
40226
|
-
if (
|
|
41531
|
+
if (isPlainObject9(value)) {
|
|
40227
41532
|
for (const key of schema.required ?? []) {
|
|
40228
41533
|
if (!(key in value)) {
|
|
40229
41534
|
errors.push({ path: `${valuePath}.${key}`, message: "Missing required property" });
|
|
@@ -40236,7 +41541,7 @@ function walk4(value, schema, valuePath, rootSchema, errors) {
|
|
|
40236
41541
|
walk4(nested, nestedSchema, `${valuePath}.${key}`, rootSchema, errors);
|
|
40237
41542
|
} else if (schema.additionalProperties === false) {
|
|
40238
41543
|
errors.push({ path: `${valuePath}.${key}`, message: "Additional property is not allowed" });
|
|
40239
|
-
} else if (
|
|
41544
|
+
} else if (isPlainObject9(schema.additionalProperties)) {
|
|
40240
41545
|
walk4(nested, schema.additionalProperties, `${valuePath}.${key}`, rootSchema, errors);
|
|
40241
41546
|
}
|
|
40242
41547
|
}
|
|
@@ -40319,7 +41624,7 @@ async function validateWorkspace2(cwd) {
|
|
|
40319
41624
|
errors.push(issue("module-schema", `module "${key}" ${schemaError.path.replace(/^\$\.?/, "") || "(root)"}: ${schemaError.message}`, `Fix the modules.${key} entry in metaproject.json.`));
|
|
40320
41625
|
}
|
|
40321
41626
|
}
|
|
40322
|
-
if (
|
|
41627
|
+
if (isPlainObject9(manifest.paths)) {
|
|
40323
41628
|
for (const [key, value] of Object.entries(manifest.paths)) {
|
|
40324
41629
|
if (typeof value !== "string") {
|
|
40325
41630
|
continue;
|
|
@@ -40427,7 +41732,7 @@ async function runCapabilities(cwd) {
|
|
|
40427
41732
|
init_fs();
|
|
40428
41733
|
init_json();
|
|
40429
41734
|
import path128 from "path";
|
|
40430
|
-
import { readdir as
|
|
41735
|
+
import { readdir as readdir21 } from "fs/promises";
|
|
40431
41736
|
function llmsPath(cwd) {
|
|
40432
41737
|
return path128.join(cwd, ".metaproject", "llms.txt");
|
|
40433
41738
|
}
|
|
@@ -40480,7 +41785,7 @@ async function collectArtifactIndex(cwd) {
|
|
|
40480
41785
|
const walk5 = async (dir) => {
|
|
40481
41786
|
let entries;
|
|
40482
41787
|
try {
|
|
40483
|
-
entries = await
|
|
41788
|
+
entries = await readdir21(dir, { withFileTypes: true });
|
|
40484
41789
|
} catch {
|
|
40485
41790
|
return;
|
|
40486
41791
|
}
|
|
@@ -41251,7 +42556,7 @@ Usage:
|
|
|
41251
42556
|
}
|
|
41252
42557
|
|
|
41253
42558
|
// src/commands/security.ts
|
|
41254
|
-
import { mkdir as mkdir51, readdir as
|
|
42559
|
+
import { mkdir as mkdir51, readdir as readdir22, readFile as readFile72, writeFile as writeFile45 } from "fs/promises";
|
|
41255
42560
|
import path131 from "path";
|
|
41256
42561
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
41257
42562
|
init_args();
|
|
@@ -41718,7 +43023,7 @@ async function collectManifestFiles(target) {
|
|
|
41718
43023
|
}
|
|
41719
43024
|
let entries;
|
|
41720
43025
|
try {
|
|
41721
|
-
entries = await
|
|
43026
|
+
entries = await readdir22(target, { withFileTypes: true });
|
|
41722
43027
|
} catch {
|
|
41723
43028
|
return [target];
|
|
41724
43029
|
}
|
|
@@ -42574,10 +43879,10 @@ async function resolveLedgerState(ledger, checkpointPath, verifier) {
|
|
|
42574
43879
|
}
|
|
42575
43880
|
var policyRefPattern = /^\.\/.+/;
|
|
42576
43881
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42577
|
-
var
|
|
43882
|
+
var asString6 = (value) => typeof value === "string" && value.length > 0;
|
|
42578
43883
|
var isBaselineArtifact = (artifact) => artifact.schemaVersion === "1.0" && artifact.kind === "deterministic-baseline" && isImmutableVersion2(artifact.version) && artifact.selection === "eligible-ids-in-input-order";
|
|
42579
|
-
var isCandidateArtifact = (artifact) => artifact.schemaVersion === "1.0" && artifact.kind === "offline-selection-advisor" && isImmutableVersion2(artifact.version) &&
|
|
42580
|
-
var isEvaluationReport = (report) => report.schemaVersion === "1.0" &&
|
|
43884
|
+
var isCandidateArtifact = (artifact) => artifact.schemaVersion === "1.0" && artifact.kind === "offline-selection-advisor" && isImmutableVersion2(artifact.version) && asString6(artifact.output) && typeof artifact.mutations === "boolean";
|
|
43885
|
+
var isEvaluationReport = (report) => report.schemaVersion === "1.0" && asString6(report.status) && asString6(report.baselineVersion) && asString6(report.baselineDigest) && reportHashPattern.test(report.baselineDigest) && asString6(report.candidateVersion) && asString6(report.candidateDigest) && reportHashPattern.test(report.candidateDigest) && asString6(report.corpusVersion) && asString6(report.corpusDigest) && reportHashPattern.test(report.corpusDigest) && asString6(report.reportDigest) && reportHashPattern.test(report.reportDigest) && asString6(report.sandboxProfileDigest) && reportHashPattern.test(report.sandboxProfileDigest) && isRecord3(report.train) && typeof report.train.status === "string" && typeof report.train.cases === "number" && isRecord3(report.holdout) && typeof report.holdout.status === "string" && typeof report.holdout.cases === "number" && isRecord3(report.adversarial) && typeof report.adversarial.status === "string" && typeof report.adversarial.cases === "number" && Array.isArray(report.candidateSelectedIds) && Array.isArray(report.reasons);
|
|
42581
43886
|
var isConfigRecord = (value) => typeof value.enabled === "boolean" && typeof value.killSwitch === "boolean";
|
|
42582
43887
|
async function readPinnedJson(input2) {
|
|
42583
43888
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input2.workspaceRoot, kind: "artifact", uri: input2.uri });
|
|
@@ -42627,7 +43932,7 @@ async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
|
42627
43932
|
const corpusDigest = configJson.corpusDigest;
|
|
42628
43933
|
const evaluationRef = configJson.evaluationReportRef;
|
|
42629
43934
|
const evaluationDigest = configJson.evaluationDigest;
|
|
42630
|
-
if (!
|
|
43935
|
+
if (!asString6(candidateRef) || !asString6(candidateDigest) || !asString6(baselineRef) || !asString6(baselineDigest) || !asString6(corpusRef) || !asString6(corpusDigest) || !asString6(evaluationRef) || !asString6(evaluationDigest))
|
|
42631
43936
|
return canonicalFallback;
|
|
42632
43937
|
if (!workspacePathPattern3.test(candidateRef) || !workspacePathPattern3.test(baselineRef) || !workspacePathPattern3.test(corpusRef) || !workspacePathPattern3.test(evaluationRef))
|
|
42633
43938
|
return canonicalFallback;
|
|
@@ -42742,7 +44047,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
|
|
|
42742
44047
|
const corpusDigest = configJson.corpusDigest;
|
|
42743
44048
|
const evaluationRef = configJson.evaluationReportRef;
|
|
42744
44049
|
const evaluationDigest = configJson.evaluationDigest;
|
|
42745
|
-
if (!
|
|
44050
|
+
if (!asString6(candidateRef) || !asString6(candidateDigest) || !asString6(baselineRef) || !asString6(baselineDigest) || !asString6(corpusRef) || !asString6(corpusDigest) || !asString6(evaluationRef) || !asString6(evaluationDigest)) {
|
|
42746
44051
|
fail("config-pins", "pins are present but not non-empty strings");
|
|
42747
44052
|
return finalize(true, enabled, killSwitch);
|
|
42748
44053
|
}
|
|
@@ -43437,7 +44742,7 @@ function buildToolRegistry() {
|
|
|
43437
44742
|
// src/mcp/resources.ts
|
|
43438
44743
|
init_fs();
|
|
43439
44744
|
import path135 from "path";
|
|
43440
|
-
import { readdir as
|
|
44745
|
+
import { readdir as readdir23, readFile as readFile76, stat as stat7 } from "fs/promises";
|
|
43441
44746
|
var URI_PREFIX = "metaproject://";
|
|
43442
44747
|
function mimeForPath(filePath) {
|
|
43443
44748
|
if (filePath.endsWith(".json") || filePath.endsWith(".jsonl")) {
|
|
@@ -43464,7 +44769,7 @@ async function walkFiles(root) {
|
|
|
43464
44769
|
const out = [];
|
|
43465
44770
|
let entries;
|
|
43466
44771
|
try {
|
|
43467
|
-
entries = await
|
|
44772
|
+
entries = await readdir23(root, { withFileTypes: true });
|
|
43468
44773
|
} catch {
|
|
43469
44774
|
return [];
|
|
43470
44775
|
}
|
|
@@ -43486,7 +44791,7 @@ async function listArtifacts(cwd) {
|
|
|
43486
44791
|
const listings = [];
|
|
43487
44792
|
let modules;
|
|
43488
44793
|
try {
|
|
43489
|
-
modules = await
|
|
44794
|
+
modules = await readdir23(base, { withFileTypes: true });
|
|
43490
44795
|
} catch {
|
|
43491
44796
|
return [];
|
|
43492
44797
|
}
|
|
@@ -43778,7 +45083,7 @@ async function mcpCommand(args2 = [], cwd = process.cwd()) {
|
|
|
43778
45083
|
printMcpHelp();
|
|
43779
45084
|
process.exitCode = 1;
|
|
43780
45085
|
}
|
|
43781
|
-
var RUNTIME_USAGE = `<cursor|claude|opencode|generic|all>`;
|
|
45086
|
+
var RUNTIME_USAGE = `<cursor|claude|opencode|vscode|generic|all>`;
|
|
43782
45087
|
function parseRequestedRuntimes(args2, fallback) {
|
|
43783
45088
|
const runtimeArg = optionValue(args2, "--runtime") ?? fallback;
|
|
43784
45089
|
return runtimeArg.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -43862,7 +45167,7 @@ function printMcpHelp() {
|
|
|
43862
45167
|
{ flag: "--dry-run", desc: "install only: print the planned change and write nothing." }
|
|
43863
45168
|
]);
|
|
43864
45169
|
heading("Notes");
|
|
43865
|
-
console.log(` ${style.dim("`install` writes a project-local MCP client config (cursor \u2192 .cursor/mcp.json, claude \u2192 .mcp.json, opencode \u2192 opencode.json), sets modules.mcp.enabled=true, and prints a snippet for `generic`.")}`);
|
|
45170
|
+
console.log(` ${style.dim("`install` writes a project-local MCP client config (cursor \u2192 .cursor/mcp.json, claude \u2192 .mcp.json, opencode \u2192 opencode.json, vscode \u2192 .vscode/mcp.json), sets modules.mcp.enabled=true, and prints a snippet for `generic`. `vscode` is opt-in only \u2014 not included in `all`.")}`);
|
|
43866
45171
|
console.log(` ${style.dim("Requires the optional @modelcontextprotocol/sdk to serve. Disabled by default (modules.mcp.enabled=false). `install` only probes the SDK \u2014 it never installs it or opens a network connection.")}`);
|
|
43867
45172
|
}
|
|
43868
45173
|
|
|
@@ -45328,6 +46633,9 @@ function buildDefaultMaskProviders(openaiCompat) {
|
|
|
45328
46633
|
out.push({ envKey: p.envKey, baseUrl: p.baseUrl });
|
|
45329
46634
|
}
|
|
45330
46635
|
out.push({ envKey: "ANTHROPIC_API_KEY", baseUrl: "https://api.anthropic.com" });
|
|
46636
|
+
out.push({ envKey: "OPENAI_API_KEY", baseUrl: "https://api.openai.com" });
|
|
46637
|
+
out.push({ envKey: "GEMINI_API_KEY", baseUrl: "https://generativelanguage.googleapis.com" });
|
|
46638
|
+
out.push({ envKey: "GOOGLE_API_KEY", baseUrl: "https://generativelanguage.googleapis.com" });
|
|
45331
46639
|
return out;
|
|
45332
46640
|
}
|
|
45333
46641
|
function parseMaskMode(raw) {
|
|
@@ -45827,6 +47135,8 @@ init_workspace_service();
|
|
|
45827
47135
|
var HARNESS_PROVIDER_OPTIONS = [
|
|
45828
47136
|
"fake",
|
|
45829
47137
|
"anthropic",
|
|
47138
|
+
"openai",
|
|
47139
|
+
"gemini",
|
|
45830
47140
|
"ollama",
|
|
45831
47141
|
...OPENAI_COMPAT_PROVIDERS.map((provider) => provider.name)
|
|
45832
47142
|
];
|
|
@@ -45992,6 +47302,20 @@ async function harnessCommand(args2, deps) {
|
|
|
45992
47302
|
return;
|
|
45993
47303
|
}
|
|
45994
47304
|
}
|
|
47305
|
+
if (provider === "openai") {
|
|
47306
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
47307
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
47308
|
+
console.log("OPENAI_API_KEY is not set: the openai provider is required to have a credential and fails closed (no network was contacted).");
|
|
47309
|
+
return;
|
|
47310
|
+
}
|
|
47311
|
+
}
|
|
47312
|
+
if (provider === "gemini") {
|
|
47313
|
+
const apiKey = env.GEMINI_API_KEY !== undefined && env.GEMINI_API_KEY.length > 0 ? env.GEMINI_API_KEY : env.GOOGLE_API_KEY;
|
|
47314
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
47315
|
+
console.log("GEMINI_API_KEY (or GOOGLE_API_KEY) is not set: the gemini provider is required to have a credential and fails closed (no network was contacted).");
|
|
47316
|
+
return;
|
|
47317
|
+
}
|
|
47318
|
+
}
|
|
45995
47319
|
const providerPort = makeProvider(provider, model, {
|
|
45996
47320
|
fetch: fetchImpl,
|
|
45997
47321
|
env,
|
|
@@ -46597,7 +47921,7 @@ async function buildApprovalContext(port, command) {
|
|
|
46597
47921
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
46598
47922
|
|
|
46599
47923
|
// src/harness/tool/builtin/interactive-tools.ts
|
|
46600
|
-
import { readdir as
|
|
47924
|
+
import { readdir as readdir24 } from "fs/promises";
|
|
46601
47925
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "path";
|
|
46602
47926
|
import { realpathSync as realpathSync4 } from "fs";
|
|
46603
47927
|
var MAX_READ_BYTES = 20000;
|
|
@@ -46649,7 +47973,7 @@ function builtinReadOnlyTools(root) {
|
|
|
46649
47973
|
return { output: `path escapes the project root: ${requested}`, isError: true };
|
|
46650
47974
|
}
|
|
46651
47975
|
try {
|
|
46652
|
-
const entries = await
|
|
47976
|
+
const entries = await readdir24(target, { withFileTypes: true });
|
|
46653
47977
|
const lines = entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).sort();
|
|
46654
47978
|
return { output: lines.length > 0 ? lines.join(`
|
|
46655
47979
|
`) : "(empty)", isError: false };
|
|
@@ -50330,7 +51654,7 @@ function buildClaudeResumeArgv(sessionRef, message2, input2) {
|
|
|
50330
51654
|
function asObject(value) {
|
|
50331
51655
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
50332
51656
|
}
|
|
50333
|
-
function
|
|
51657
|
+
function asString7(value) {
|
|
50334
51658
|
return typeof value === "string" ? value : undefined;
|
|
50335
51659
|
}
|
|
50336
51660
|
function asFiniteNumber(value) {
|
|
@@ -50343,16 +51667,16 @@ function truncate3(text, limit) {
|
|
|
50343
51667
|
var CLAUDE_UNMAPPED_SYSTEM_SUBTYPES = ["hook_started", "hook_response"];
|
|
50344
51668
|
var CLAUDE_UNMAPPED_LINE_TYPES = ["rate_limit_event"];
|
|
50345
51669
|
function systemEvents(obj) {
|
|
50346
|
-
const subtype =
|
|
51670
|
+
const subtype = asString7(obj.subtype);
|
|
50347
51671
|
if (subtype === "init") {
|
|
50348
|
-
const sessionRef =
|
|
51672
|
+
const sessionRef = asString7(obj.session_id);
|
|
50349
51673
|
return [sessionRef === undefined ? { kind: "child_started" } : { kind: "child_started", sessionRef }];
|
|
50350
51674
|
}
|
|
50351
51675
|
if (subtype === "api_retry") {
|
|
50352
51676
|
const attempt = asFiniteNumber(obj.attempt);
|
|
50353
51677
|
const max = asFiniteNumber(obj.max_retries);
|
|
50354
51678
|
const status = asFiniteNumber(obj.error_status);
|
|
50355
|
-
const error2 =
|
|
51679
|
+
const error2 = asString7(obj.error);
|
|
50356
51680
|
const parts = [
|
|
50357
51681
|
attempt === undefined ? "api retry" : `api retry ${attempt}${max === undefined ? "" : `/${max}`}`,
|
|
50358
51682
|
status === undefined ? undefined : `status ${status}`,
|
|
@@ -50363,32 +51687,32 @@ function systemEvents(obj) {
|
|
|
50363
51687
|
return [];
|
|
50364
51688
|
}
|
|
50365
51689
|
function toolResultDetail(content) {
|
|
50366
|
-
const direct =
|
|
51690
|
+
const direct = asString7(content);
|
|
50367
51691
|
if (direct !== undefined)
|
|
50368
51692
|
return truncate3(direct, TOOL_DETAIL_LIMIT);
|
|
50369
51693
|
if (!Array.isArray(content))
|
|
50370
51694
|
return;
|
|
50371
|
-
const text = content.map((entry) =>
|
|
51695
|
+
const text = content.map((entry) => asString7(asObject(entry)?.text)).filter((entry) => entry !== undefined).join(`
|
|
50372
51696
|
`);
|
|
50373
51697
|
return text.length === 0 ? undefined : truncate3(text, TOOL_DETAIL_LIMIT);
|
|
50374
51698
|
}
|
|
50375
51699
|
function assistantBlockEvent(block) {
|
|
50376
|
-
switch (
|
|
51700
|
+
switch (asString7(block.type)) {
|
|
50377
51701
|
case "tool_use": {
|
|
50378
|
-
const name =
|
|
51702
|
+
const name = asString7(block.name) ?? "unknown";
|
|
50379
51703
|
const detail = block.input === undefined ? undefined : truncate3(JSON.stringify(block.input), TOOL_DETAIL_LIMIT);
|
|
50380
51704
|
return detail === undefined ? { kind: "tool_call", name } : { kind: "tool_call", name, detail };
|
|
50381
51705
|
}
|
|
50382
51706
|
case "text":
|
|
50383
|
-
return { kind: "assistant_text", text:
|
|
51707
|
+
return { kind: "assistant_text", text: asString7(block.text) ?? "" };
|
|
50384
51708
|
case "thinking":
|
|
50385
|
-
return { kind: "thinking", text:
|
|
51709
|
+
return { kind: "thinking", text: asString7(block.thinking) ?? asString7(block.text) ?? "" };
|
|
50386
51710
|
default:
|
|
50387
51711
|
return;
|
|
50388
51712
|
}
|
|
50389
51713
|
}
|
|
50390
51714
|
function userBlockEvent(block) {
|
|
50391
|
-
if (
|
|
51715
|
+
if (asString7(block.type) !== "tool_result")
|
|
50392
51716
|
return;
|
|
50393
51717
|
const detail = toolResultDetail(block.content);
|
|
50394
51718
|
return detail === undefined ? { kind: "tool_result" } : { kind: "tool_result", detail };
|
|
@@ -50426,14 +51750,14 @@ function usageEvent(obj) {
|
|
|
50426
51750
|
};
|
|
50427
51751
|
}
|
|
50428
51752
|
function describeFailure(obj) {
|
|
50429
|
-
const subtype =
|
|
51753
|
+
const subtype = asString7(obj.subtype) ?? "unknown";
|
|
50430
51754
|
const parts = [`result.subtype "${subtype}"`];
|
|
50431
|
-
const text =
|
|
51755
|
+
const text = asString7(obj.result);
|
|
50432
51756
|
if (text !== undefined && text.length > 0)
|
|
50433
51757
|
parts.push(truncate3(text, TOOL_DETAIL_LIMIT));
|
|
50434
51758
|
const errors = obj.errors;
|
|
50435
51759
|
if (Array.isArray(errors)) {
|
|
50436
|
-
const flat = errors.map((entry) =>
|
|
51760
|
+
const flat = errors.map((entry) => asString7(entry)).filter((entry) => entry !== undefined);
|
|
50437
51761
|
if (flat.length > 0)
|
|
50438
51762
|
parts.push(truncate3(flat.join("; "), TOOL_DETAIL_LIMIT));
|
|
50439
51763
|
}
|
|
@@ -50443,10 +51767,10 @@ function describeFailure(obj) {
|
|
|
50443
51767
|
return parts.join(" \u2014 ");
|
|
50444
51768
|
}
|
|
50445
51769
|
function resultEvents(obj) {
|
|
50446
|
-
const subtype =
|
|
51770
|
+
const subtype = asString7(obj.subtype);
|
|
50447
51771
|
const succeeded = subtype === undefined ? obj.is_error === false : subtype === "success";
|
|
50448
51772
|
const terminal = succeeded ? (() => {
|
|
50449
|
-
const text =
|
|
51773
|
+
const text = asString7(obj.result);
|
|
50450
51774
|
return text === undefined ? { kind: "child_finished" } : { kind: "child_finished", text };
|
|
50451
51775
|
})() : { kind: "child_failed", message: describeFailure(obj) };
|
|
50452
51776
|
const usage = usageEvent(obj);
|
|
@@ -50465,7 +51789,7 @@ function parseClaudeEvents(line) {
|
|
|
50465
51789
|
const obj = asObject(parsed);
|
|
50466
51790
|
if (obj === undefined)
|
|
50467
51791
|
return [];
|
|
50468
|
-
switch (
|
|
51792
|
+
switch (asString7(obj.type)) {
|
|
50469
51793
|
case "system":
|
|
50470
51794
|
return systemEvents(obj);
|
|
50471
51795
|
case "assistant":
|
|
@@ -50495,13 +51819,13 @@ function isRecognisedClaudeLine(line) {
|
|
|
50495
51819
|
const obj = asObject(parsed);
|
|
50496
51820
|
if (obj === undefined)
|
|
50497
51821
|
return false;
|
|
50498
|
-
const type =
|
|
51822
|
+
const type = asString7(obj.type);
|
|
50499
51823
|
if (type === undefined)
|
|
50500
51824
|
return false;
|
|
50501
51825
|
if (CLAUDE_UNMAPPED_LINE_TYPES.includes(type))
|
|
50502
51826
|
return true;
|
|
50503
51827
|
if (type === "system") {
|
|
50504
|
-
const subtype =
|
|
51828
|
+
const subtype = asString7(obj.subtype) ?? "";
|
|
50505
51829
|
return subtype === "init" || subtype === "api_retry" || CLAUDE_UNMAPPED_SYSTEM_SUBTYPES.includes(subtype);
|
|
50506
51830
|
}
|
|
50507
51831
|
return type === "assistant" || type === "user" || type === "result";
|
|
@@ -50595,9 +51919,9 @@ function parseCodexEvents(line) {
|
|
|
50595
51919
|
const record = readJsonObject(line);
|
|
50596
51920
|
if (record === undefined)
|
|
50597
51921
|
return [];
|
|
50598
|
-
switch (
|
|
51922
|
+
switch (asString8(record.type)) {
|
|
50599
51923
|
case "thread.started": {
|
|
50600
|
-
const threadId =
|
|
51924
|
+
const threadId = asString8(record.thread_id);
|
|
50601
51925
|
return [threadId === undefined ? { kind: "child_started" } : { kind: "child_started", sessionRef: threadId }];
|
|
50602
51926
|
}
|
|
50603
51927
|
case "turn.started":
|
|
@@ -50615,7 +51939,7 @@ function parseCodexEvents(line) {
|
|
|
50615
51939
|
case "turn.failed":
|
|
50616
51940
|
return [{ kind: "child_failed", message: parseFailureMessage(record.error) }];
|
|
50617
51941
|
case "error":
|
|
50618
|
-
return [{ kind: "retry", message:
|
|
51942
|
+
return [{ kind: "retry", message: asString8(record.message) ?? "codex reported a non-terminal error" }];
|
|
50619
51943
|
default:
|
|
50620
51944
|
return [];
|
|
50621
51945
|
}
|
|
@@ -50636,7 +51960,7 @@ function isRecognisedCodexLine(line) {
|
|
|
50636
51960
|
const record = readJsonObject(line);
|
|
50637
51961
|
if (record === undefined)
|
|
50638
51962
|
return false;
|
|
50639
|
-
const type =
|
|
51963
|
+
const type = asString8(record.type);
|
|
50640
51964
|
return type !== undefined && RECOGNISED_TYPES.has(type);
|
|
50641
51965
|
}
|
|
50642
51966
|
var NARRATED_FAILURE_LINE = /^\s*(error\b|usage:)/i;
|
|
@@ -50696,25 +52020,25 @@ function readJsonObject(line) {
|
|
|
50696
52020
|
return;
|
|
50697
52021
|
}
|
|
50698
52022
|
}
|
|
50699
|
-
function
|
|
52023
|
+
function asString8(value) {
|
|
50700
52024
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
50701
52025
|
}
|
|
50702
|
-
function
|
|
52026
|
+
function asNumber6(value) {
|
|
50703
52027
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
50704
52028
|
}
|
|
50705
52029
|
function parseCompletedItem(raw) {
|
|
50706
52030
|
if (typeof raw !== "object" || raw === null)
|
|
50707
52031
|
return [];
|
|
50708
52032
|
const item = raw;
|
|
50709
|
-
switch (
|
|
52033
|
+
switch (asString8(item.type)) {
|
|
50710
52034
|
case "command_execution": {
|
|
50711
|
-
const command =
|
|
52035
|
+
const command = asString8(item.command);
|
|
50712
52036
|
return [
|
|
50713
52037
|
command === undefined ? { kind: "tool_call", name: "command_execution" } : { kind: "tool_call", name: "command_execution", detail: command }
|
|
50714
52038
|
];
|
|
50715
52039
|
}
|
|
50716
52040
|
case "agent_message": {
|
|
50717
|
-
const text =
|
|
52041
|
+
const text = asString8(item.text);
|
|
50718
52042
|
return text === undefined ? [] : [{ kind: "assistant_text", text }];
|
|
50719
52043
|
}
|
|
50720
52044
|
default:
|
|
@@ -50725,8 +52049,8 @@ function parseUsage(raw) {
|
|
|
50725
52049
|
if (typeof raw !== "object" || raw === null)
|
|
50726
52050
|
return;
|
|
50727
52051
|
const usage = raw;
|
|
50728
|
-
const inputTokens =
|
|
50729
|
-
const outputTokens =
|
|
52052
|
+
const inputTokens = asNumber6(usage.input_tokens);
|
|
52053
|
+
const outputTokens = asNumber6(usage.output_tokens);
|
|
50730
52054
|
if (inputTokens === undefined && outputTokens === undefined)
|
|
50731
52055
|
return;
|
|
50732
52056
|
return {
|
|
@@ -50737,11 +52061,11 @@ function parseUsage(raw) {
|
|
|
50737
52061
|
}
|
|
50738
52062
|
function parseFailureMessage(raw) {
|
|
50739
52063
|
if (typeof raw === "object" && raw !== null) {
|
|
50740
|
-
const message2 =
|
|
52064
|
+
const message2 = asString8(raw.message);
|
|
50741
52065
|
if (message2 !== undefined)
|
|
50742
52066
|
return message2;
|
|
50743
52067
|
}
|
|
50744
|
-
return
|
|
52068
|
+
return asString8(raw) ?? "codex reported a failed turn without a message";
|
|
50745
52069
|
}
|
|
50746
52070
|
function lastTerminalEvent(events) {
|
|
50747
52071
|
for (let i = events.length - 1;i >= 0; i -= 1) {
|
|
@@ -51627,7 +52951,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
51627
52951
|
// package.json
|
|
51628
52952
|
var package_default = {
|
|
51629
52953
|
name: "@mrciphersmith/keryx",
|
|
51630
|
-
version: "0.2.
|
|
52954
|
+
version: "0.2.51",
|
|
51631
52955
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
51632
52956
|
private: false,
|
|
51633
52957
|
publishConfig: {
|
|
@@ -52888,7 +54212,7 @@ init_store3();
|
|
|
52888
54212
|
init_proposal_lifecycle();
|
|
52889
54213
|
init_workspace_service();
|
|
52890
54214
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
52891
|
-
import { readdir as
|
|
54215
|
+
import { readdir as readdir25 } from "fs/promises";
|
|
52892
54216
|
import path148 from "path";
|
|
52893
54217
|
|
|
52894
54218
|
// src/sac/lifecycle-flag.ts
|
|
@@ -52938,6 +54262,7 @@ async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
|
52938
54262
|
}
|
|
52939
54263
|
|
|
52940
54264
|
// src/sac/catch-up.ts
|
|
54265
|
+
init_proposal_evidence();
|
|
52941
54266
|
async function buildCatchUp(input2) {
|
|
52942
54267
|
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
52943
54268
|
collectProposals(input2.cwd, input2.workspaceId),
|
|
@@ -52957,8 +54282,20 @@ async function collectProposals(cwd, workspaceId) {
|
|
|
52957
54282
|
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
52958
54283
|
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
52959
54284
|
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
52960
|
-
const fresh = await
|
|
52961
|
-
|
|
54285
|
+
const [fresh, note2] = await Promise.all([
|
|
54286
|
+
proposalService.isEvidenceFresh(proposal, actor),
|
|
54287
|
+
readSidecarNote(cwd, group.workspace.id, proposal.id)
|
|
54288
|
+
]);
|
|
54289
|
+
return {
|
|
54290
|
+
type: "proposal",
|
|
54291
|
+
workspaceId: group.workspace.id,
|
|
54292
|
+
proposalId: proposal.id,
|
|
54293
|
+
fresh,
|
|
54294
|
+
kind: proposal.kind,
|
|
54295
|
+
author: proposal.author,
|
|
54296
|
+
createdAt: proposal.createdAt,
|
|
54297
|
+
note: note2
|
|
54298
|
+
};
|
|
52962
54299
|
}));
|
|
52963
54300
|
}
|
|
52964
54301
|
async function classifySession(session) {
|
|
@@ -53019,7 +54356,7 @@ async function isSlateEngaged(dir) {
|
|
|
53019
54356
|
if (await pathExists(path148.join(dir, "terminal-state.json")))
|
|
53020
54357
|
return true;
|
|
53021
54358
|
try {
|
|
53022
|
-
const entries = await
|
|
54359
|
+
const entries = await readdir25(path148.join(dir, "slate-archive"));
|
|
53023
54360
|
return entries.length > 0;
|
|
53024
54361
|
} catch {
|
|
53025
54362
|
return false;
|
|
@@ -53047,7 +54384,7 @@ async function readNewestUnboundCandidate(dir) {
|
|
|
53047
54384
|
const archiveDir = path148.join(dir, "slate-archive");
|
|
53048
54385
|
let entries;
|
|
53049
54386
|
try {
|
|
53050
|
-
entries = (await
|
|
54387
|
+
entries = (await readdir25(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
53051
54388
|
} catch {
|
|
53052
54389
|
return;
|
|
53053
54390
|
}
|
|
@@ -53084,7 +54421,7 @@ async function readNewestWrapUpOutcome(dir) {
|
|
|
53084
54421
|
const archiveDir = path148.join(dir, "slate-archive");
|
|
53085
54422
|
let entries;
|
|
53086
54423
|
try {
|
|
53087
|
-
entries = (await
|
|
54424
|
+
entries = (await readdir25(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
53088
54425
|
} catch {
|
|
53089
54426
|
return;
|
|
53090
54427
|
}
|
|
@@ -53529,7 +54866,7 @@ function openFlows(otui, chrome, options) {
|
|
|
53529
54866
|
|
|
53530
54867
|
// src/tui/busy-dispatch.ts
|
|
53531
54868
|
function classifyBusyDispatch(params) {
|
|
53532
|
-
const { line, commandName, isSessionInfo, isFlows, isWorkspace, isReview } = params;
|
|
54869
|
+
const { line, commandName, isSessionInfo, isFlows, isWorkspace, isReview, isMcp } = params;
|
|
53533
54870
|
if (commandName === "/exit")
|
|
53534
54871
|
return "exit";
|
|
53535
54872
|
if (commandName === "/help")
|
|
@@ -53548,7 +54885,7 @@ function classifyBusyDispatch(params) {
|
|
|
53548
54885
|
return "copy";
|
|
53549
54886
|
if (commandName === "/mode")
|
|
53550
54887
|
return "mode";
|
|
53551
|
-
const isBusyReadonlyCommand = isSessionInfo || isFlows || isWorkspace || isReview;
|
|
54888
|
+
const isBusyReadonlyCommand = isSessionInfo || isFlows || isWorkspace || isReview || isMcp;
|
|
53552
54889
|
if (isBusyReadonlyCommand && isSessionInfo)
|
|
53553
54890
|
return "session-info";
|
|
53554
54891
|
if (isBusyReadonlyCommand && isFlows)
|
|
@@ -53557,6 +54894,8 @@ function classifyBusyDispatch(params) {
|
|
|
53557
54894
|
return "workspace";
|
|
53558
54895
|
if (isBusyReadonlyCommand && isReview)
|
|
53559
54896
|
return "review";
|
|
54897
|
+
if (isBusyReadonlyCommand && isMcp)
|
|
54898
|
+
return "mcp";
|
|
53560
54899
|
if (commandName !== undefined || line.startsWith("/"))
|
|
53561
54900
|
return "deferred";
|
|
53562
54901
|
return "not-a-command";
|
|
@@ -53816,7 +55155,8 @@ function openWorkspace(otui, chrome, options) {
|
|
|
53816
55155
|
var REVIEW_COMMAND = "/review";
|
|
53817
55156
|
var REVIEW_FOOTER = [
|
|
53818
55157
|
{ key: "[/]", label: "item" },
|
|
53819
|
-
{ key: "a y", label: "accept" },
|
|
55158
|
+
{ key: "a y", label: "accept proposal" },
|
|
55159
|
+
{ key: "d y", label: "decline proposal" },
|
|
53820
55160
|
{ key: "\u2191/\u2193", label: "scroll" },
|
|
53821
55161
|
{ key: "\u2190/\u2192", label: "tabs" },
|
|
53822
55162
|
{ key: "esc", label: "close" }
|
|
@@ -53834,7 +55174,7 @@ var TYPE_LABEL = {
|
|
|
53834
55174
|
function summarizeReviewItem(item) {
|
|
53835
55175
|
switch (item.type) {
|
|
53836
55176
|
case "proposal":
|
|
53837
|
-
return `${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
55177
|
+
return `${item.kind} ${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
53838
55178
|
case "blocked":
|
|
53839
55179
|
return `${item.sessionId} \u2014 ${item.terminalState.reason}`;
|
|
53840
55180
|
case "unbound-candidate":
|
|
@@ -53858,9 +55198,13 @@ function describeReviewItem(item) {
|
|
|
53858
55198
|
return [
|
|
53859
55199
|
`Proposal ${item.proposalId}`,
|
|
53860
55200
|
`Workspace ${item.workspaceId}`,
|
|
55201
|
+
`Kind ${item.kind}`,
|
|
55202
|
+
`Author ${item.author}`,
|
|
55203
|
+
`Created ${item.createdAt}`,
|
|
53861
55204
|
`Evidence ${item.fresh ? "fresh" : "stale \u2014 evidence has drifted since this proposal was created; re-run wrap-up before deciding"}`,
|
|
55205
|
+
...item.note !== undefined ? ["", `Note ${item.note}`] : [],
|
|
53862
55206
|
"",
|
|
53863
|
-
`
|
|
55207
|
+
`Dismiss (archive with no decision) from a terminal: keryx workspace review ${item.workspaceId} ${item.proposalId} --decision dismissed`
|
|
53864
55208
|
];
|
|
53865
55209
|
case "blocked":
|
|
53866
55210
|
return [
|
|
@@ -53913,25 +55257,37 @@ function describeGroupOutcome(g) {
|
|
|
53913
55257
|
}
|
|
53914
55258
|
}
|
|
53915
55259
|
}
|
|
55260
|
+
var DECISION_VERB = { accept: "Accept", decline: "Decline" };
|
|
55261
|
+
var DECISION_ING = { accept: "Accepting", decline: "Declining" };
|
|
55262
|
+
var DECISION_DONE = { accept: "Accepted", decline: "Declined" };
|
|
55263
|
+
var DECISION_COMMAND = {
|
|
55264
|
+
accept: "running `keryx workspace confirm-review` then `keryx workspace review`",
|
|
55265
|
+
decline: "running `keryx workspace review --decision rejected`"
|
|
55266
|
+
};
|
|
53916
55267
|
function formatReviewDetailLines(item, status) {
|
|
53917
55268
|
if (item === undefined) {
|
|
53918
55269
|
return ["No item selected.", "", "Press Enter (or click a row) on the Review tab to view one."];
|
|
53919
55270
|
}
|
|
53920
55271
|
const lines = describeReviewItem(item);
|
|
53921
55272
|
if (item.type !== "proposal") {
|
|
55273
|
+
if (status.kind === "unavailable") {
|
|
55274
|
+
return [...lines, "", `[${status.decision === "accept" ? "a" : "d"}] does nothing here \u2014 accept/decline only apply to a pending proposal, not to this item.`];
|
|
55275
|
+
}
|
|
53922
55276
|
return lines;
|
|
53923
55277
|
}
|
|
53924
55278
|
const withAction = [...lines, ""];
|
|
53925
55279
|
if (status.kind === "armed") {
|
|
53926
|
-
withAction.push(
|
|
55280
|
+
withAction.push(`Press [y] to CONFIRM ${status.decision}, any other key cancels.`);
|
|
53927
55281
|
} else if (status.kind === "running") {
|
|
53928
|
-
withAction.push(
|
|
55282
|
+
withAction.push(`${DECISION_ING[status.decision]}\u2026 ${DECISION_COMMAND[status.decision]}.`);
|
|
53929
55283
|
} else if (status.kind === "done" && status.outcome.ok) {
|
|
53930
|
-
withAction.push(
|
|
55284
|
+
withAction.push(`\u2713 ${DECISION_DONE[status.decision]}.`);
|
|
53931
55285
|
} else if (status.kind === "done" && !status.outcome.ok) {
|
|
53932
|
-
withAction.push(`\u2717
|
|
55286
|
+
withAction.push(`\u2717 ${DECISION_VERB[status.decision]} failed: ${status.outcome.message}`);
|
|
55287
|
+
} else if (status.kind === "unavailable") {
|
|
55288
|
+
withAction.push(`[${status.decision === "accept" ? "a" : "d"}] does nothing \u2014 no ${status.decision} handler is configured for this modal.`);
|
|
53933
55289
|
} else {
|
|
53934
|
-
withAction.push("[a] Accept this proposal");
|
|
55290
|
+
withAction.push("[a] Accept this proposal [d] Decline this proposal");
|
|
53935
55291
|
}
|
|
53936
55292
|
return withAction;
|
|
53937
55293
|
}
|
|
@@ -54028,17 +55384,19 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
54028
55384
|
status = { kind: "idle" };
|
|
54029
55385
|
paintSelection();
|
|
54030
55386
|
};
|
|
54031
|
-
const
|
|
55387
|
+
const handlerFor = (decision) => decision === "accept" ? options.acceptProposal : options.declineProposal;
|
|
55388
|
+
const runDecision = (decision) => {
|
|
54032
55389
|
const item = items[selected];
|
|
54033
|
-
|
|
55390
|
+
const run = handlerFor(decision);
|
|
55391
|
+
if (item === undefined || item.type !== "proposal" || run === undefined) {
|
|
54034
55392
|
return;
|
|
54035
55393
|
}
|
|
54036
|
-
status = { kind: "running" };
|
|
55394
|
+
status = { kind: "running", decision };
|
|
54037
55395
|
paintSelection();
|
|
54038
|
-
|
|
54039
|
-
status = { kind: "done", outcome };
|
|
55396
|
+
run(item).then((outcome) => {
|
|
55397
|
+
status = { kind: "done", decision, outcome };
|
|
54040
55398
|
if (outcome.ok) {
|
|
54041
|
-
options.
|
|
55399
|
+
options.onResolved?.(item);
|
|
54042
55400
|
}
|
|
54043
55401
|
paintSelection();
|
|
54044
55402
|
});
|
|
@@ -54078,7 +55436,7 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
54078
55436
|
const onDetail = handle.activeTab() === "detail";
|
|
54079
55437
|
if (onDetail && status.kind === "armed") {
|
|
54080
55438
|
if (token === "y") {
|
|
54081
|
-
|
|
55439
|
+
runDecision(status.decision);
|
|
54082
55440
|
} else {
|
|
54083
55441
|
status = { kind: "idle" };
|
|
54084
55442
|
paintSelection();
|
|
@@ -54097,8 +55455,9 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
54097
55455
|
handle.setTab("detail");
|
|
54098
55456
|
return;
|
|
54099
55457
|
}
|
|
54100
|
-
if (onDetail && token === "a"
|
|
54101
|
-
|
|
55458
|
+
if (onDetail && (token === "a" || token === "d") && status.kind !== "running") {
|
|
55459
|
+
const decision = token === "a" ? "accept" : "decline";
|
|
55460
|
+
status = items[selected]?.type === "proposal" && handlerFor(decision) !== undefined ? { kind: "armed", decision } : { kind: "unavailable", decision };
|
|
54102
55461
|
paintSelection();
|
|
54103
55462
|
return;
|
|
54104
55463
|
}
|
|
@@ -54162,6 +55521,228 @@ async function acceptProposalViaShell(run, workspaceId, proposalId) {
|
|
|
54162
55521
|
}
|
|
54163
55522
|
return { ok: true };
|
|
54164
55523
|
}
|
|
55524
|
+
async function declineProposalViaShell(run, workspaceId, proposalId) {
|
|
55525
|
+
const decline = await run(`keryx workspace review ${shQuote(workspaceId)} ${shQuote(proposalId)} --decision rejected`);
|
|
55526
|
+
if (decline.isError) {
|
|
55527
|
+
return { ok: false, message: decline.output };
|
|
55528
|
+
}
|
|
55529
|
+
return { ok: true };
|
|
55530
|
+
}
|
|
55531
|
+
|
|
55532
|
+
// src/tui/mcp-inspector.ts
|
|
55533
|
+
var MCP_INSPECTOR_FOOTER = [
|
|
55534
|
+
{ key: "\u2191/\u2193", label: "select" },
|
|
55535
|
+
{ key: "c/d", label: "connect/disconnect" },
|
|
55536
|
+
{ key: "y", label: "confirm" },
|
|
55537
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
55538
|
+
{ key: "esc", label: "close" }
|
|
55539
|
+
];
|
|
55540
|
+
var MCP_TOOLS_COMMAND = "/mcp";
|
|
55541
|
+
function isMcpToolsCommand(line) {
|
|
55542
|
+
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
55543
|
+
return token === MCP_TOOLS_COMMAND;
|
|
55544
|
+
}
|
|
55545
|
+
var RUNTIME_LABELS = {
|
|
55546
|
+
cursor: "Cursor",
|
|
55547
|
+
claude: "Claude Code",
|
|
55548
|
+
opencode: "opencode",
|
|
55549
|
+
vscode: "VS Code",
|
|
55550
|
+
generic: "Generic (manual)"
|
|
55551
|
+
};
|
|
55552
|
+
function runtimeLabel(id) {
|
|
55553
|
+
return RUNTIME_LABELS[id] ?? id;
|
|
55554
|
+
}
|
|
55555
|
+
function formatToolsListLines(tools) {
|
|
55556
|
+
if (tools.length === 0) {
|
|
55557
|
+
return ["No tools available."];
|
|
55558
|
+
}
|
|
55559
|
+
return tools.map((tool) => {
|
|
55560
|
+
const risk = (tool.risk ?? "read").padEnd(6);
|
|
55561
|
+
const name = tool.name.padEnd(28);
|
|
55562
|
+
return `${name} ${risk} ${tool.description ?? ""}`.trimEnd();
|
|
55563
|
+
});
|
|
55564
|
+
}
|
|
55565
|
+
function isActionable(id) {
|
|
55566
|
+
return id !== "generic";
|
|
55567
|
+
}
|
|
55568
|
+
function formatMcpListLines(runtimes, selected, status) {
|
|
55569
|
+
if (runtimes.length === 0) {
|
|
55570
|
+
return ["No MCP client runtimes registered."];
|
|
55571
|
+
}
|
|
55572
|
+
return runtimes.map((runtime, index) => {
|
|
55573
|
+
const mark = index === selected ? ">" : " ";
|
|
55574
|
+
const label = runtimeLabel(runtime.id).padEnd(20);
|
|
55575
|
+
const statusText = runtime.connected ? "\u25CF connected" : "\u25CB not connected";
|
|
55576
|
+
let action = "";
|
|
55577
|
+
if (!isActionable(runtime.id)) {
|
|
55578
|
+
action = " (copy snippet manually)";
|
|
55579
|
+
} else if (status.kind === "armed" && status.target.id === runtime.id) {
|
|
55580
|
+
action = ` [press y to ${status.target.action}]`;
|
|
55581
|
+
} else if (status.kind === "running" && status.target.id === runtime.id) {
|
|
55582
|
+
action = ` ${status.target.action === "connect" ? "connecting\u2026" : "disconnecting\u2026"}`;
|
|
55583
|
+
} else if (status.kind === "done" && status.target.id === runtime.id) {
|
|
55584
|
+
action = status.outcome.ok ? " \u2713 done" : ` \u2717 ${status.outcome.message}`;
|
|
55585
|
+
} else {
|
|
55586
|
+
action = runtime.connected ? " [d] disconnect" : " [c] connect";
|
|
55587
|
+
}
|
|
55588
|
+
return `${mark} ${label} ${statusText}${action}`;
|
|
55589
|
+
});
|
|
55590
|
+
}
|
|
55591
|
+
function presentMcpTools(openModal2, otui, chrome, options) {
|
|
55592
|
+
const runtimes = options.runtimes.map((r) => ({ ...r }));
|
|
55593
|
+
let mcpSelected = 0;
|
|
55594
|
+
let toolsScroll = 0;
|
|
55595
|
+
let mcpScroll = 0;
|
|
55596
|
+
let status = { kind: "idle" };
|
|
55597
|
+
let toolsNode;
|
|
55598
|
+
let mcpNode;
|
|
55599
|
+
let unsubscribeKey;
|
|
55600
|
+
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
55601
|
+
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
55602
|
+
const toolLines = () => formatToolsListLines(options.tools);
|
|
55603
|
+
const mcpLines = () => formatMcpListLines(runtimes, mcpSelected, status);
|
|
55604
|
+
const paint = () => {
|
|
55605
|
+
toolsScroll = clampScroll3(toolsScroll, toolLines().length, bodyRows);
|
|
55606
|
+
mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
|
|
55607
|
+
mcpScroll = clampScroll3(mcpScroll, mcpLines().length, bodyRows);
|
|
55608
|
+
if (toolsNode !== undefined) {
|
|
55609
|
+
toolsNode.content = windowLines3(toolLines(), toolsScroll, bodyRows).join(`
|
|
55610
|
+
`);
|
|
55611
|
+
}
|
|
55612
|
+
if (mcpNode !== undefined) {
|
|
55613
|
+
mcpNode.content = windowLines3(mcpLines(), mcpScroll, bodyRows).join(`
|
|
55614
|
+
`);
|
|
55615
|
+
}
|
|
55616
|
+
};
|
|
55617
|
+
const moveMcpSelection = (next) => {
|
|
55618
|
+
if (runtimes.length === 0) {
|
|
55619
|
+
return;
|
|
55620
|
+
}
|
|
55621
|
+
const clamped = Math.min(runtimes.length - 1, Math.max(0, next));
|
|
55622
|
+
if (clamped === mcpSelected) {
|
|
55623
|
+
return;
|
|
55624
|
+
}
|
|
55625
|
+
mcpSelected = clamped;
|
|
55626
|
+
status = { kind: "idle" };
|
|
55627
|
+
paint();
|
|
55628
|
+
};
|
|
55629
|
+
const runAction = () => {
|
|
55630
|
+
if (status.kind !== "armed") {
|
|
55631
|
+
return;
|
|
55632
|
+
}
|
|
55633
|
+
const target = status.target;
|
|
55634
|
+
status = { kind: "running", target };
|
|
55635
|
+
paint();
|
|
55636
|
+
const fn = target.action === "connect" ? options.connect : options.disconnect;
|
|
55637
|
+
fn(target.id).then((outcome) => {
|
|
55638
|
+
status = { kind: "done", target, outcome };
|
|
55639
|
+
if (outcome.ok) {
|
|
55640
|
+
const row = runtimes.find((r) => r.id === target.id);
|
|
55641
|
+
if (row !== undefined) {
|
|
55642
|
+
row.connected = target.action === "connect";
|
|
55643
|
+
}
|
|
55644
|
+
}
|
|
55645
|
+
options.onStatusChange?.(runtimes);
|
|
55646
|
+
paint();
|
|
55647
|
+
});
|
|
55648
|
+
};
|
|
55649
|
+
const handle = openModal2(otui, chrome, {
|
|
55650
|
+
title: "Tools & MCP",
|
|
55651
|
+
tabs: [
|
|
55652
|
+
{ id: "tools", label: "Tools" },
|
|
55653
|
+
{ id: "mcp", label: "MCP" }
|
|
55654
|
+
],
|
|
55655
|
+
initialTab: "tools",
|
|
55656
|
+
footer: MCP_INSPECTOR_FOOTER,
|
|
55657
|
+
renderTab: (tabId, body, ctx) => {
|
|
55658
|
+
const renderer = options.renderer ?? chrome?.renderer;
|
|
55659
|
+
const parent = body;
|
|
55660
|
+
const ctor = otui.TextRenderable;
|
|
55661
|
+
if (parent.add === undefined || ctor === undefined) {
|
|
55662
|
+
return;
|
|
55663
|
+
}
|
|
55664
|
+
if (tabId === "tools") {
|
|
55665
|
+
toolsScroll = clampScroll3(toolsScroll, toolLines().length, bodyRows);
|
|
55666
|
+
toolsNode = new ctor(renderer, { id: "mcp-tools-body", content: windowLines3(toolLines(), toolsScroll, bodyRows).join(`
|
|
55667
|
+
`) });
|
|
55668
|
+
parent.add(toolsNode);
|
|
55669
|
+
return;
|
|
55670
|
+
}
|
|
55671
|
+
mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
|
|
55672
|
+
mcpNode = new ctor(renderer, { id: "mcp-mcp-body", content: windowLines3(mcpLines(), mcpScroll, bodyRows).join(`
|
|
55673
|
+
`) });
|
|
55674
|
+
parent.add(mcpNode);
|
|
55675
|
+
},
|
|
55676
|
+
onClose: () => {
|
|
55677
|
+
unsubscribeKey?.();
|
|
55678
|
+
}
|
|
55679
|
+
});
|
|
55680
|
+
if (handle === undefined) {
|
|
55681
|
+
return;
|
|
55682
|
+
}
|
|
55683
|
+
if (options.onKeypress !== undefined) {
|
|
55684
|
+
unsubscribeKey = options.onKeypress((key) => {
|
|
55685
|
+
const token = key.name || key.sequence;
|
|
55686
|
+
const onMcp = handle.activeTab() === "mcp";
|
|
55687
|
+
if (onMcp && status.kind === "armed") {
|
|
55688
|
+
if (token === "y") {
|
|
55689
|
+
runAction();
|
|
55690
|
+
} else {
|
|
55691
|
+
status = { kind: "idle" };
|
|
55692
|
+
paint();
|
|
55693
|
+
}
|
|
55694
|
+
return;
|
|
55695
|
+
}
|
|
55696
|
+
if (onMcp && token === "c") {
|
|
55697
|
+
const row = runtimes[mcpSelected];
|
|
55698
|
+
if (row !== undefined && isActionable(row.id) && !row.connected && status.kind !== "running") {
|
|
55699
|
+
status = { kind: "armed", target: { id: row.id, action: "connect" } };
|
|
55700
|
+
paint();
|
|
55701
|
+
}
|
|
55702
|
+
return;
|
|
55703
|
+
}
|
|
55704
|
+
if (onMcp && token === "d") {
|
|
55705
|
+
const row = runtimes[mcpSelected];
|
|
55706
|
+
if (row !== undefined && isActionable(row.id) && row.connected && status.kind !== "running") {
|
|
55707
|
+
status = { kind: "armed", target: { id: row.id, action: "disconnect" } };
|
|
55708
|
+
paint();
|
|
55709
|
+
}
|
|
55710
|
+
return;
|
|
55711
|
+
}
|
|
55712
|
+
if (token === "up" || token === "k") {
|
|
55713
|
+
if (onMcp) {
|
|
55714
|
+
moveMcpSelection(mcpSelected - 1);
|
|
55715
|
+
} else {
|
|
55716
|
+
toolsScroll = clampScroll3(toolsScroll - 1, toolLines().length, bodyRows);
|
|
55717
|
+
paint();
|
|
55718
|
+
}
|
|
55719
|
+
return;
|
|
55720
|
+
}
|
|
55721
|
+
if (token === "down" || token === "j") {
|
|
55722
|
+
if (onMcp) {
|
|
55723
|
+
moveMcpSelection(mcpSelected + 1);
|
|
55724
|
+
} else {
|
|
55725
|
+
toolsScroll = clampScroll3(toolsScroll + 1, toolLines().length, bodyRows);
|
|
55726
|
+
paint();
|
|
55727
|
+
}
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
if (token === "pageup" || token === "pagedown") {
|
|
55731
|
+
const step = token === "pageup" ? -bodyRows : bodyRows;
|
|
55732
|
+
if (onMcp) {
|
|
55733
|
+
mcpScroll = clampScroll3(mcpScroll + step, mcpLines().length, bodyRows);
|
|
55734
|
+
} else {
|
|
55735
|
+
toolsScroll = clampScroll3(toolsScroll + step, toolLines().length, bodyRows);
|
|
55736
|
+
}
|
|
55737
|
+
paint();
|
|
55738
|
+
}
|
|
55739
|
+
});
|
|
55740
|
+
}
|
|
55741
|
+
return handle;
|
|
55742
|
+
}
|
|
55743
|
+
function openMcpTools(otui, chrome, options) {
|
|
55744
|
+
return presentMcpTools((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
55745
|
+
}
|
|
54165
55746
|
|
|
54166
55747
|
// src/tui/tui-shell.ts
|
|
54167
55748
|
init_slate();
|
|
@@ -54532,6 +56113,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
54532
56113
|
description: "Show project-wide items needing review (proposals, blocked sessions)",
|
|
54533
56114
|
modes: AGENT_ONLY
|
|
54534
56115
|
},
|
|
56116
|
+
{
|
|
56117
|
+
name: "/mcp",
|
|
56118
|
+
description: "Show available tools and MCP client connect status",
|
|
56119
|
+
modes: AGENT_ONLY
|
|
56120
|
+
},
|
|
54535
56121
|
{
|
|
54536
56122
|
name: "/compact",
|
|
54537
56123
|
description: "Compact model context \u2014 /compact [focus] (archive kept)",
|
|
@@ -54921,6 +56507,38 @@ init_providers();
|
|
|
54921
56507
|
init_patch_risk();
|
|
54922
56508
|
init_shell_config();
|
|
54923
56509
|
|
|
56510
|
+
// src/mcp-client/wire.ts
|
|
56511
|
+
var STANDARD_ELICITATION_PARAM_KEYS = new Set(["message", "requestedSchema"]);
|
|
56512
|
+
|
|
56513
|
+
// src/mcp-client/elicitation.ts
|
|
56514
|
+
init_command_risk();
|
|
56515
|
+
function extractCodexCommand(vendor) {
|
|
56516
|
+
const raw = vendor.codex_command;
|
|
56517
|
+
if (!Array.isArray(raw))
|
|
56518
|
+
return;
|
|
56519
|
+
return raw.every((entry) => typeof entry === "string") ? raw : undefined;
|
|
56520
|
+
}
|
|
56521
|
+
var SHELL_BASENAMES = new Set(["zsh", "bash", "sh", "ksh", "dash", "fish"]);
|
|
56522
|
+
var MCP_ELICITATION_TOOL_PREFIX = "mcp_elicitation:";
|
|
56523
|
+
function describeElicitationPrompt(tool, inputJson) {
|
|
56524
|
+
if (!tool.startsWith(MCP_ELICITATION_TOOL_PREFIX))
|
|
56525
|
+
return;
|
|
56526
|
+
let parsed;
|
|
56527
|
+
try {
|
|
56528
|
+
parsed = JSON.parse(inputJson);
|
|
56529
|
+
} catch {
|
|
56530
|
+
return { message: inputJson, command: undefined };
|
|
56531
|
+
}
|
|
56532
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
56533
|
+
return { message: inputJson, command: undefined };
|
|
56534
|
+
}
|
|
56535
|
+
const obj = parsed;
|
|
56536
|
+
const message2 = typeof obj.message === "string" && obj.message.trim().length > 0 ? obj.message : "codex is requesting approval for an action";
|
|
56537
|
+
const vendor = typeof obj.vendor === "object" && obj.vendor !== null ? obj.vendor : {};
|
|
56538
|
+
const command = extractCodexCommand(vendor)?.join(" ");
|
|
56539
|
+
return { message: message2, command };
|
|
56540
|
+
}
|
|
56541
|
+
|
|
54924
56542
|
// src/lib/permission-mode-config.ts
|
|
54925
56543
|
init_permission_mode();
|
|
54926
56544
|
init_config_dir();
|
|
@@ -55078,6 +56696,10 @@ function onKeypress2(r, handler) {
|
|
|
55078
56696
|
return () => r._internalKeyInput.offInternal("keypress", handler);
|
|
55079
56697
|
}
|
|
55080
56698
|
function showComposerChoice(otui, r, dock, request) {
|
|
56699
|
+
if (dock.visible === true) {
|
|
56700
|
+
request.onBusy?.();
|
|
56701
|
+
return Promise.resolve(request.cancelId);
|
|
56702
|
+
}
|
|
55081
56703
|
return new Promise((resolve3) => {
|
|
55082
56704
|
const options = request.options.map((o) => ({
|
|
55083
56705
|
...o,
|
|
@@ -58556,6 +60178,275 @@ ${rows.join(`
|
|
|
58556
60178
|
(none)`}
|
|
58557
60179
|
`;
|
|
58558
60180
|
}
|
|
60181
|
+
async function selectSearchProviderAndReport(controller, onSystem, providerId) {
|
|
60182
|
+
const result = await controller.select(providerId);
|
|
60183
|
+
if (!result.ok) {
|
|
60184
|
+
if (result.reason === "not-configured") {
|
|
60185
|
+
onSystem?.(`Cannot select '${providerId}': provider is not configured.
|
|
60186
|
+
`);
|
|
60187
|
+
} else if (result.reason === "not-connected") {
|
|
60188
|
+
onSystem?.(`Cannot select '${providerId}': provider is not connected (run /search-provider ${providerId} <params> to test).
|
|
60189
|
+
`);
|
|
60190
|
+
} else {
|
|
60191
|
+
onSystem?.(`Cannot select '${providerId}': ${result.reason}.
|
|
60192
|
+
`);
|
|
60193
|
+
}
|
|
60194
|
+
return;
|
|
60195
|
+
}
|
|
60196
|
+
onSystem?.(`Search provider '${providerId}' selected.
|
|
60197
|
+
`);
|
|
60198
|
+
}
|
|
60199
|
+
function promptSearchFieldStep(otui, r, field3, value) {
|
|
60200
|
+
return new Promise((resolve3) => {
|
|
60201
|
+
const box = overlayBox(otui, r, "search-field-picker");
|
|
60202
|
+
r.root.add(box);
|
|
60203
|
+
box.add(new otui.TextRenderable(r, { id: "sf-title", content: otui.t`${otui.bold(field3.label)} ${otui.dim(`${field3.required ? "required" : "optional"} \xB7 Enter \xB7 Esc to go back`)}` }));
|
|
60204
|
+
const input2 = new otui.InputRenderable(r, { id: "sf-input", value, marginTop: 1 });
|
|
60205
|
+
box.add(input2);
|
|
60206
|
+
input2.focus();
|
|
60207
|
+
const cleanup = () => {
|
|
60208
|
+
unsub();
|
|
60209
|
+
input2.blur();
|
|
60210
|
+
r.root.remove(box);
|
|
60211
|
+
};
|
|
60212
|
+
const unsub = onKeypress4(r, (key) => {
|
|
60213
|
+
if (key.name === "escape") {
|
|
60214
|
+
cleanup();
|
|
60215
|
+
resolve3({ kind: "back" });
|
|
60216
|
+
key.preventDefault();
|
|
60217
|
+
key.stopPropagation();
|
|
60218
|
+
}
|
|
60219
|
+
});
|
|
60220
|
+
input2.on(otui.InputRenderableEvents.ENTER, () => {
|
|
60221
|
+
const entered = input2.value.trim();
|
|
60222
|
+
cleanup();
|
|
60223
|
+
resolve3({ kind: "value", value: entered });
|
|
60224
|
+
});
|
|
60225
|
+
});
|
|
60226
|
+
}
|
|
60227
|
+
function promptSearchCredentialStep(otui, r, opts) {
|
|
60228
|
+
return new Promise((resolve3) => {
|
|
60229
|
+
const box = overlayBox(otui, r, "search-credential-picker");
|
|
60230
|
+
r.root.add(box);
|
|
60231
|
+
box.add(new otui.TextRenderable(r, { id: "sc-title", content: otui.t`${otui.bold(`Paste your ${opts.label}`)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
|
|
60232
|
+
box.add(new otui.TextRenderable(r, {
|
|
60233
|
+
id: "sc-note",
|
|
60234
|
+
content: otui.t`${otui.dim("Saved to your keryx config dir (owner-only, 0600)")}`,
|
|
60235
|
+
marginTop: 1
|
|
60236
|
+
}));
|
|
60237
|
+
const keyInput = new otui.InputRenderable(r, { id: "sc-input", placeholder: "...", marginTop: 1 });
|
|
60238
|
+
box.add(keyInput);
|
|
60239
|
+
keyInput.focus();
|
|
60240
|
+
const cleanup = () => {
|
|
60241
|
+
unsub();
|
|
60242
|
+
keyInput.blur();
|
|
60243
|
+
r.root.remove(box);
|
|
60244
|
+
};
|
|
60245
|
+
const unsub = onKeypress4(r, (key) => {
|
|
60246
|
+
if (key.name === "escape") {
|
|
60247
|
+
cleanup();
|
|
60248
|
+
resolve3({ kind: "back" });
|
|
60249
|
+
key.preventDefault();
|
|
60250
|
+
key.stopPropagation();
|
|
60251
|
+
}
|
|
60252
|
+
});
|
|
60253
|
+
keyInput.on(otui.InputRenderableEvents.ENTER, () => {
|
|
60254
|
+
const value = keyInput.value.trim();
|
|
60255
|
+
cleanup();
|
|
60256
|
+
resolve3(value.length > 0 ? { kind: "key", value } : { kind: "skip" });
|
|
60257
|
+
});
|
|
60258
|
+
});
|
|
60259
|
+
}
|
|
60260
|
+
function promptSetActiveProviderStep(otui, r) {
|
|
60261
|
+
return new Promise((resolve3) => {
|
|
60262
|
+
const box = overlayBox(otui, r, "search-active-picker");
|
|
60263
|
+
r.root.add(box);
|
|
60264
|
+
box.add(new otui.TextRenderable(r, { id: "sa-title", content: otui.t`${otui.bold("Set as active provider after a successful test?")} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to go back)")}` }));
|
|
60265
|
+
const select = new otui.SelectRenderable(r, {
|
|
60266
|
+
id: "sa-select",
|
|
60267
|
+
width: 60,
|
|
60268
|
+
height: selectBoxHeight(2, true),
|
|
60269
|
+
options: [
|
|
60270
|
+
{ name: "Yes", description: "select it once the test passes" },
|
|
60271
|
+
{ name: "No", description: "leave it configured but inactive" }
|
|
60272
|
+
],
|
|
60273
|
+
selectedTextColor: "#ffd166"
|
|
60274
|
+
});
|
|
60275
|
+
box.add(select);
|
|
60276
|
+
select.focus();
|
|
60277
|
+
const cleanup = () => {
|
|
60278
|
+
unsub();
|
|
60279
|
+
select.blur();
|
|
60280
|
+
r.root.remove(box);
|
|
60281
|
+
};
|
|
60282
|
+
const unsub = onKeypress4(r, (key) => {
|
|
60283
|
+
if (key.name === "escape") {
|
|
60284
|
+
cleanup();
|
|
60285
|
+
resolve3(undefined);
|
|
60286
|
+
key.preventDefault();
|
|
60287
|
+
key.stopPropagation();
|
|
60288
|
+
}
|
|
60289
|
+
});
|
|
60290
|
+
select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
60291
|
+
const chosen = select.getSelectedOption();
|
|
60292
|
+
cleanup();
|
|
60293
|
+
resolve3(chosen === null ? undefined : chosen.name === "Yes");
|
|
60294
|
+
});
|
|
60295
|
+
});
|
|
60296
|
+
}
|
|
60297
|
+
function pickSearchProviderStep(otui, r, providers) {
|
|
60298
|
+
return new Promise((resolve3) => {
|
|
60299
|
+
const box = overlayBox(otui, r, "search-provider-picker");
|
|
60300
|
+
r.root.add(box);
|
|
60301
|
+
box.add(new otui.TextRenderable(r, { id: "spp-title", content: otui.t`${otui.bold("Select a search provider")} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to cancel)")}` }));
|
|
60302
|
+
const labelOf = (p) => `${p.id} (${p.displayName})`;
|
|
60303
|
+
const select = new otui.SelectRenderable(r, {
|
|
60304
|
+
id: "spp-select",
|
|
60305
|
+
width: 60,
|
|
60306
|
+
height: selectBoxHeight(providers.length, true),
|
|
60307
|
+
showScrollIndicator: true,
|
|
60308
|
+
options: providers.map((p) => ({ name: labelOf(p), description: p.kind })),
|
|
60309
|
+
selectedTextColor: "#ffd166"
|
|
60310
|
+
});
|
|
60311
|
+
box.add(select);
|
|
60312
|
+
select.focus();
|
|
60313
|
+
const cleanup = () => {
|
|
60314
|
+
unsub();
|
|
60315
|
+
select.blur();
|
|
60316
|
+
r.root.remove(box);
|
|
60317
|
+
};
|
|
60318
|
+
const unsub = onKeypress4(r, (key) => {
|
|
60319
|
+
if (key.name === "escape") {
|
|
60320
|
+
cleanup();
|
|
60321
|
+
resolve3(undefined);
|
|
60322
|
+
key.preventDefault();
|
|
60323
|
+
key.stopPropagation();
|
|
60324
|
+
}
|
|
60325
|
+
});
|
|
60326
|
+
select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
60327
|
+
const chosen = select.getSelectedOption();
|
|
60328
|
+
cleanup();
|
|
60329
|
+
resolve3(chosen === null ? undefined : providers.find((p) => labelOf(p) === chosen.name));
|
|
60330
|
+
});
|
|
60331
|
+
});
|
|
60332
|
+
}
|
|
60333
|
+
async function runSearchProviderFieldsStep(otui, r, provider, seed) {
|
|
60334
|
+
const subSteps = [
|
|
60335
|
+
...provider.fields.map((field3) => ({ kind: "field", field: field3 })),
|
|
60336
|
+
...provider.credentialSchema.required ? [{ kind: "credential" }] : [],
|
|
60337
|
+
{ kind: "toggle" }
|
|
60338
|
+
];
|
|
60339
|
+
const fields = { ...seed.fields };
|
|
60340
|
+
let credential2 = seed.credential;
|
|
60341
|
+
let setActive = seed.setActive;
|
|
60342
|
+
let index = 0;
|
|
60343
|
+
while (index < subSteps.length) {
|
|
60344
|
+
const step = subSteps[index];
|
|
60345
|
+
if (step === undefined)
|
|
60346
|
+
break;
|
|
60347
|
+
if (step.kind === "field") {
|
|
60348
|
+
const result = await promptSearchFieldStep(otui, r, step.field, fields[step.field.id] ?? step.field.defaultValue ?? "");
|
|
60349
|
+
if (result.kind === "back") {
|
|
60350
|
+
index -= 1;
|
|
60351
|
+
if (index < 0)
|
|
60352
|
+
return { kind: "back" };
|
|
60353
|
+
continue;
|
|
60354
|
+
}
|
|
60355
|
+
fields[step.field.id] = result.value;
|
|
60356
|
+
index += 1;
|
|
60357
|
+
continue;
|
|
60358
|
+
}
|
|
60359
|
+
if (step.kind === "credential") {
|
|
60360
|
+
const result = await promptSearchCredentialStep(otui, r, {
|
|
60361
|
+
label: provider.credentialSchema.label ?? `${provider.displayName} credential`
|
|
60362
|
+
});
|
|
60363
|
+
if (result.kind === "back") {
|
|
60364
|
+
index -= 1;
|
|
60365
|
+
if (index < 0)
|
|
60366
|
+
return { kind: "back" };
|
|
60367
|
+
continue;
|
|
60368
|
+
}
|
|
60369
|
+
credential2 = result.kind === "key" ? result.value : undefined;
|
|
60370
|
+
index += 1;
|
|
60371
|
+
continue;
|
|
60372
|
+
}
|
|
60373
|
+
const toggle = await promptSetActiveProviderStep(otui, r);
|
|
60374
|
+
if (toggle === undefined) {
|
|
60375
|
+
index -= 1;
|
|
60376
|
+
if (index < 0)
|
|
60377
|
+
return { kind: "back" };
|
|
60378
|
+
continue;
|
|
60379
|
+
}
|
|
60380
|
+
setActive = toggle;
|
|
60381
|
+
index += 1;
|
|
60382
|
+
}
|
|
60383
|
+
return { kind: "done", fields, credential: credential2, setActive };
|
|
60384
|
+
}
|
|
60385
|
+
function runSearchProviderTestStep(otui, r, controller, provider, fields, credential2, setActive) {
|
|
60386
|
+
return new Promise((resolve3) => {
|
|
60387
|
+
const box = overlayBox(otui, r, "search-test-picker");
|
|
60388
|
+
r.root.add(box);
|
|
60389
|
+
const status = new otui.TextRenderable(r, { id: "st-title", content: otui.t`${otui.bold(`Testing '${provider.id}'`)} ${otui.dim("...")}` });
|
|
60390
|
+
box.add(status);
|
|
60391
|
+
let settled;
|
|
60392
|
+
const cleanup = () => {
|
|
60393
|
+
unsub();
|
|
60394
|
+
r.root.remove(box);
|
|
60395
|
+
};
|
|
60396
|
+
const unsub = onKeypress4(r, (key) => {
|
|
60397
|
+
if (settled === "failure" && key.name === "escape") {
|
|
60398
|
+
cleanup();
|
|
60399
|
+
resolve3("retry");
|
|
60400
|
+
key.preventDefault();
|
|
60401
|
+
key.stopPropagation();
|
|
60402
|
+
} else if (settled === "success" && (key.name === "return" || key.name === "linefeed" || key.name === "kpenter")) {
|
|
60403
|
+
cleanup();
|
|
60404
|
+
resolve3("done");
|
|
60405
|
+
key.preventDefault();
|
|
60406
|
+
key.stopPropagation();
|
|
60407
|
+
}
|
|
60408
|
+
});
|
|
60409
|
+
(async () => {
|
|
60410
|
+
controller.configure(provider.id, { ...provider.defaults, ...fields }, credential2);
|
|
60411
|
+
const tested = await controller.test(provider.id);
|
|
60412
|
+
if (!tested.ok) {
|
|
60413
|
+
settled = "failure";
|
|
60414
|
+
const reason = tested.reason === "missing-credential" ? "missing credential" : "connection validation failed";
|
|
60415
|
+
status.content = otui.t`${otui.red("\u2717")} ${otui.bold(`'${provider.id}' test failed: ${reason}`)} ${otui.dim("(Esc to go back and retry)")}`;
|
|
60416
|
+
return;
|
|
60417
|
+
}
|
|
60418
|
+
settled = "success";
|
|
60419
|
+
if (!setActive) {
|
|
60420
|
+
status.content = otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured and tested successfully`)} ${otui.dim("(Enter to close)")}`;
|
|
60421
|
+
return;
|
|
60422
|
+
}
|
|
60423
|
+
const selected = await controller.select(provider.id);
|
|
60424
|
+
status.content = selected.ok ? otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured, tested, and set as active`)} ${otui.dim("(Enter to close)")}` : otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured and tested`)} ${otui.dim(`but could not be set active (${selected.reason ?? "unknown"})`)} ${otui.dim("(Enter to close)")}`;
|
|
60425
|
+
})();
|
|
60426
|
+
});
|
|
60427
|
+
}
|
|
60428
|
+
async function searchProviderWizardInTui(otui, r, controller) {
|
|
60429
|
+
const providers = controller.configurable();
|
|
60430
|
+
providerLoop:
|
|
60431
|
+
while (true) {
|
|
60432
|
+
const provider = await pickSearchProviderStep(otui, r, providers);
|
|
60433
|
+
if (provider === undefined) {
|
|
60434
|
+
return;
|
|
60435
|
+
}
|
|
60436
|
+
let seed = { fields: { ...provider.defaults }, credential: undefined, setActive: false };
|
|
60437
|
+
while (true) {
|
|
60438
|
+
const step2 = await runSearchProviderFieldsStep(otui, r, provider, seed);
|
|
60439
|
+
if (step2.kind === "back") {
|
|
60440
|
+
continue providerLoop;
|
|
60441
|
+
}
|
|
60442
|
+
seed = { fields: step2.fields, credential: step2.credential, setActive: step2.setActive };
|
|
60443
|
+
const result = await runSearchProviderTestStep(otui, r, controller, provider, seed.fields, seed.credential, seed.setActive);
|
|
60444
|
+
if (result === "done") {
|
|
60445
|
+
return;
|
|
60446
|
+
}
|
|
60447
|
+
}
|
|
60448
|
+
}
|
|
60449
|
+
}
|
|
58559
60450
|
function promptBaseUrlStep(otui, r, label, baseUrl2) {
|
|
58560
60451
|
return new Promise((resolve3) => {
|
|
58561
60452
|
const box = overlayBox(otui, r, "base-url-picker");
|
|
@@ -59032,7 +60923,13 @@ async function launchTuiAgentShell(opts) {
|
|
|
59032
60923
|
const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("0 tokens")}` });
|
|
59033
60924
|
sidebar.add(sbContext);
|
|
59034
60925
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-tools-k", content: otui.t`${otui.dim("Tools")}`, marginTop: 1 }));
|
|
59035
|
-
sidebar.add(new otui.TextRenderable(r, {
|
|
60926
|
+
sidebar.add(new otui.TextRenderable(r, {
|
|
60927
|
+
id: "sb-tools-v",
|
|
60928
|
+
content: otui.t`${otui.dim(`${deps.tools.length} available`)}`,
|
|
60929
|
+
onMouseDown: () => {
|
|
60930
|
+
showTools();
|
|
60931
|
+
}
|
|
60932
|
+
}));
|
|
59036
60933
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-status-k", content: otui.t`${otui.dim("Status")}`, marginTop: 1 }));
|
|
59037
60934
|
const sbWorkers = new otui.TextRenderable(r, {
|
|
59038
60935
|
id: "sb-status-v",
|
|
@@ -59332,6 +61229,51 @@ async function launchTuiAgentShell(opts) {
|
|
|
59332
61229
|
}));
|
|
59333
61230
|
return id === "allow";
|
|
59334
61231
|
}
|
|
61232
|
+
if (tool.startsWith(MCP_ELICITATION_TOOL_PREFIX)) {
|
|
61233
|
+
const described = describeElicitationPrompt(tool, inputJson) ?? { message: inputJson, command: undefined };
|
|
61234
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
61235
|
+
id: `ap${uid++}`,
|
|
61236
|
+
content: otui.t`${otui.yellow("\u2699 codex is requesting approval")}`
|
|
61237
|
+
}));
|
|
61238
|
+
transcript.add(new otui.TextRenderable(r, { id: `ap${uid++}`, content: otui.t`${otui.dim(described.message)}` }));
|
|
61239
|
+
if (described.command !== undefined) {
|
|
61240
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
61241
|
+
id: `ap${uid++}`,
|
|
61242
|
+
content: otui.t`${otui.dim(`command: ${described.command}`)}`
|
|
61243
|
+
}));
|
|
61244
|
+
}
|
|
61245
|
+
if (meta?.destructive === true) {
|
|
61246
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
61247
|
+
id: `ap${uid++}`,
|
|
61248
|
+
content: otui.t`${otui.yellow("deletes a file, touches .git/, or touches many files in one call")}`
|
|
61249
|
+
}));
|
|
61250
|
+
}
|
|
61251
|
+
if (meta?.credentials === true) {
|
|
61252
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
61253
|
+
id: `ap${uid++}`,
|
|
61254
|
+
content: otui.t`${otui.yellow("touches the agent's own permission/credential files")}`
|
|
61255
|
+
}));
|
|
61256
|
+
}
|
|
61257
|
+
chrome.hideMenu();
|
|
61258
|
+
setMainAgent("blocked", "approval");
|
|
61259
|
+
const elicitationSubtitle = described.message.length > 80 ? `${described.message.slice(0, 77)}\u2026` : described.message;
|
|
61260
|
+
const id = await showComposerChoice(otui, r, chrome.dock, {
|
|
61261
|
+
title: "Approve codex elicitation?",
|
|
61262
|
+
subtitle: elicitationSubtitle,
|
|
61263
|
+
cancelId: "deny",
|
|
61264
|
+
options: [
|
|
61265
|
+
{ id: "allow", label: "Approve", description: "codex proceeds with this action", recommended: true },
|
|
61266
|
+
{ id: "deny", label: "Deny", description: "codex's action is refused" }
|
|
61267
|
+
]
|
|
61268
|
+
});
|
|
61269
|
+
input2.focus();
|
|
61270
|
+
setMainAgent("running", id === "allow" ? "write" : "denied");
|
|
61271
|
+
transcript.add(new otui.TextRenderable(r, {
|
|
61272
|
+
id: `ap${uid++}`,
|
|
61273
|
+
content: id === "allow" ? otui.t`${otui.green("\u25C7 codex elicitation approved")}` : otui.t`${otui.red("\u25C7 codex elicitation denied")}`
|
|
61274
|
+
}));
|
|
61275
|
+
return id === "allow";
|
|
61276
|
+
}
|
|
59335
61277
|
const ev = evaluateShellApproval({
|
|
59336
61278
|
inputJson,
|
|
59337
61279
|
...meta !== undefined ? { meta } : {},
|
|
@@ -59762,7 +61704,8 @@ Staying in the current session.
|
|
|
59762
61704
|
openReview(otui, chrome, {
|
|
59763
61705
|
items,
|
|
59764
61706
|
acceptProposal: (item) => acceptProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
59765
|
-
|
|
61707
|
+
declineProposal: (item) => declineProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
61708
|
+
onResolved: () => {
|
|
59766
61709
|
refreshReviewSidebar();
|
|
59767
61710
|
},
|
|
59768
61711
|
renderer: r,
|
|
@@ -59770,6 +61713,38 @@ Staying in the current session.
|
|
|
59770
61713
|
});
|
|
59771
61714
|
})();
|
|
59772
61715
|
};
|
|
61716
|
+
const showTools = () => {
|
|
61717
|
+
(async () => {
|
|
61718
|
+
const cwd = inspectorCwd();
|
|
61719
|
+
const runtimes = await mcpClientStatus(cwd, mcpRuntimeIds());
|
|
61720
|
+
openMcpTools(otui, chrome, {
|
|
61721
|
+
tools: deps.tools.map((t) => t.definition),
|
|
61722
|
+
runtimes,
|
|
61723
|
+
connect: async (id) => {
|
|
61724
|
+
try {
|
|
61725
|
+
const report = await installMcpClient(cwd, [id]);
|
|
61726
|
+
const outcome = report.outcomes[0];
|
|
61727
|
+
if (outcome !== undefined && outcome.errors.length > 0) {
|
|
61728
|
+
return { ok: false, message: outcome.errors.join("; ") };
|
|
61729
|
+
}
|
|
61730
|
+
return { ok: true };
|
|
61731
|
+
} catch (error2) {
|
|
61732
|
+
return { ok: false, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
61733
|
+
}
|
|
61734
|
+
},
|
|
61735
|
+
disconnect: async (id) => {
|
|
61736
|
+
try {
|
|
61737
|
+
await uninstallMcpClient(cwd, [id]);
|
|
61738
|
+
return { ok: true };
|
|
61739
|
+
} catch (error2) {
|
|
61740
|
+
return { ok: false, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
61741
|
+
}
|
|
61742
|
+
},
|
|
61743
|
+
renderer: r,
|
|
61744
|
+
...inspectorKeys
|
|
61745
|
+
});
|
|
61746
|
+
})();
|
|
61747
|
+
};
|
|
59773
61748
|
const runModeCommand = (line) => {
|
|
59774
61749
|
const modeArgs = line.trim().split(/\s+/).slice(1).filter((p) => p.length > 0);
|
|
59775
61750
|
const wanted = modeArgs[0] ?? "";
|
|
@@ -59777,6 +61752,7 @@ Staying in the current session.
|
|
|
59777
61752
|
const applyMode = async (next) => {
|
|
59778
61753
|
if (next === "auto") {
|
|
59779
61754
|
chrome.hideMenu();
|
|
61755
|
+
let blockedByOpenDialog = false;
|
|
59780
61756
|
const confirmId = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
|
|
59781
61757
|
title: "Switch to auto mode?",
|
|
59782
61758
|
subtitle: "Skips confirmation for EVERY action, including destructive commands. Only credential-touching commands still ask.",
|
|
@@ -59784,9 +61760,16 @@ Staying in the current session.
|
|
|
59784
61760
|
options: [
|
|
59785
61761
|
{ id: "confirm", label: "Confirm", description: "I understand the risk" },
|
|
59786
61762
|
{ id: "cancel", label: "Cancel", description: "Keep the current mode", recommended: true }
|
|
59787
|
-
]
|
|
61763
|
+
],
|
|
61764
|
+
onBusy: () => {
|
|
61765
|
+
blockedByOpenDialog = true;
|
|
61766
|
+
chrome.showToast("Answer the open approval first, then retry /mode.");
|
|
61767
|
+
}
|
|
59788
61768
|
}));
|
|
59789
61769
|
input2.focus();
|
|
61770
|
+
if (blockedByOpenDialog) {
|
|
61771
|
+
return;
|
|
61772
|
+
}
|
|
59790
61773
|
if (confirmId !== "confirm") {
|
|
59791
61774
|
chrome.showToast("Cancelled \u2014 mode unchanged.");
|
|
59792
61775
|
return;
|
|
@@ -59816,6 +61799,7 @@ Staying in the current session.
|
|
|
59816
61799
|
const stored = getProjectPermissionMode(sessionCwd);
|
|
59817
61800
|
chrome.hideMenu();
|
|
59818
61801
|
(async () => {
|
|
61802
|
+
let blockedByOpenDialog = false;
|
|
59819
61803
|
const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
|
|
59820
61804
|
title: `Permission mode (current: ${permissionMode})`,
|
|
59821
61805
|
subtitle: stored !== undefined ? `Project default: ${stored}` : "No project default set.",
|
|
@@ -59825,9 +61809,16 @@ Staying in the current session.
|
|
|
59825
61809
|
label: m,
|
|
59826
61810
|
description: MODE_PICKER_DESCRIPTIONS[m],
|
|
59827
61811
|
recommended: m === permissionMode
|
|
59828
|
-
}))
|
|
61812
|
+
})),
|
|
61813
|
+
onBusy: () => {
|
|
61814
|
+
blockedByOpenDialog = true;
|
|
61815
|
+
chrome.showToast("Answer the open approval first, then retry /mode.");
|
|
61816
|
+
}
|
|
59829
61817
|
}));
|
|
59830
61818
|
input2.focus();
|
|
61819
|
+
if (blockedByOpenDialog) {
|
|
61820
|
+
return;
|
|
61821
|
+
}
|
|
59831
61822
|
if (isPermissionMode(id) && id !== permissionMode) {
|
|
59832
61823
|
await applyMode(id);
|
|
59833
61824
|
}
|
|
@@ -60225,7 +62216,8 @@ Staying in the current session.
|
|
|
60225
62216
|
isSessionInfo: isSessionInfoCommand(line),
|
|
60226
62217
|
isFlows: isFlowsCommand(line),
|
|
60227
62218
|
isWorkspace: isWorkspaceCommand(line),
|
|
60228
|
-
isReview: isReviewCommand(line)
|
|
62219
|
+
isReview: isReviewCommand(line),
|
|
62220
|
+
isMcp: isMcpToolsCommand(line)
|
|
60229
62221
|
});
|
|
60230
62222
|
switch (decision) {
|
|
60231
62223
|
case "exit": {
|
|
@@ -60333,6 +62325,10 @@ Staying in the current session.
|
|
|
60333
62325
|
showReview();
|
|
60334
62326
|
return;
|
|
60335
62327
|
}
|
|
62328
|
+
case "mcp": {
|
|
62329
|
+
showTools();
|
|
62330
|
+
return;
|
|
62331
|
+
}
|
|
60336
62332
|
case "deferred": {
|
|
60337
62333
|
transcript.add(new otui.TextRenderable(r, {
|
|
60338
62334
|
id: `c${uid++}`,
|
|
@@ -60356,6 +62352,7 @@ Staying in the current session.
|
|
|
60356
62352
|
return;
|
|
60357
62353
|
}
|
|
60358
62354
|
(async () => {
|
|
62355
|
+
let blockedByOpenDialog = false;
|
|
60359
62356
|
const chosen = await showComposerChoice(otui, r, chrome.dock, {
|
|
60360
62357
|
title: "Main agent is busy",
|
|
60361
62358
|
subtitle: line,
|
|
@@ -60363,8 +62360,17 @@ Staying in the current session.
|
|
|
60363
62360
|
{ id: "main", label: "Main queue", description: "queue for the main agent; remove/edit/force later", recommended: true },
|
|
60364
62361
|
{ id: "side", label: "Side-1", description: "read-only answer, outside main history (as before)" }
|
|
60365
62362
|
],
|
|
60366
|
-
cancelId: "side"
|
|
62363
|
+
cancelId: "side",
|
|
62364
|
+
onBusy: () => {
|
|
62365
|
+
blockedByOpenDialog = true;
|
|
62366
|
+
chrome.showToast("Answer the open approval first, then resend.");
|
|
62367
|
+
}
|
|
60367
62368
|
});
|
|
62369
|
+
if (blockedByOpenDialog) {
|
|
62370
|
+
input2.value = line;
|
|
62371
|
+
input2.focus();
|
|
62372
|
+
return;
|
|
62373
|
+
}
|
|
60368
62374
|
if (chosen === "main") {
|
|
60369
62375
|
const id = `mq${mainQueueSeq++}`;
|
|
60370
62376
|
mainQueue.push({ id, question: line, displayQuestion: displayLine });
|
|
@@ -60459,7 +62465,8 @@ Staying in the current session.
|
|
|
60459
62465
|
const args2 = parseSearchProviderArgs(line.slice(16));
|
|
60460
62466
|
const all = searchProviderController.configurable();
|
|
60461
62467
|
if (args2.providerId === undefined) {
|
|
60462
|
-
|
|
62468
|
+
await chrome.withOverlay(() => searchProviderWizardInTui(otui, r, searchProviderController));
|
|
62469
|
+
input2.focus();
|
|
60463
62470
|
return;
|
|
60464
62471
|
}
|
|
60465
62472
|
const descriptor = all.find((candidate) => candidate.id === args2.providerId);
|
|
@@ -60488,11 +62495,18 @@ Staying in the current session.
|
|
|
60488
62495
|
const providerId = args2.providerId;
|
|
60489
62496
|
if (providerId === undefined) {
|
|
60490
62497
|
const selectable = searchProviderController.selectable();
|
|
60491
|
-
io.onSystem?.(describeSearchProviderList("Connected search providers (use /search-connect <id> to select):", selectable));
|
|
60492
62498
|
if (selectable.length === 0) {
|
|
62499
|
+
io.onSystem?.(describeSearchProviderList("Connected search providers (use /search-connect <id> to select):", selectable));
|
|
60493
62500
|
io.onSystem?.(`No connected search providers found. Run /search-provider first.
|
|
60494
62501
|
`);
|
|
62502
|
+
return;
|
|
62503
|
+
}
|
|
62504
|
+
const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, r, selectable));
|
|
62505
|
+
input2.focus();
|
|
62506
|
+
if (picked === undefined) {
|
|
62507
|
+
return;
|
|
60495
62508
|
}
|
|
62509
|
+
await selectSearchProviderAndReport(searchProviderController, io.onSystem, picked.id);
|
|
60496
62510
|
return;
|
|
60497
62511
|
}
|
|
60498
62512
|
const normalizedProviderId = searchProviderController.configurable().find((candidate) => candidate.id === providerId)?.id;
|
|
@@ -60501,22 +62515,7 @@ Staying in the current session.
|
|
|
60501
62515
|
`);
|
|
60502
62516
|
return;
|
|
60503
62517
|
}
|
|
60504
|
-
|
|
60505
|
-
if (!result.ok) {
|
|
60506
|
-
if (result.reason === "not-configured") {
|
|
60507
|
-
io.onSystem?.(`Cannot select '${providerId}': provider is not configured.
|
|
60508
|
-
`);
|
|
60509
|
-
} else if (result.reason === "not-connected") {
|
|
60510
|
-
io.onSystem?.(`Cannot select '${providerId}': provider is not connected (run /search-provider ${providerId} <params> to test).
|
|
60511
|
-
`);
|
|
60512
|
-
} else {
|
|
60513
|
-
io.onSystem?.(`Cannot select '${providerId}': ${result.reason}.
|
|
60514
|
-
`);
|
|
60515
|
-
}
|
|
60516
|
-
return;
|
|
60517
|
-
}
|
|
60518
|
-
io.onSystem?.(`Search provider '${providerId}' selected.
|
|
60519
|
-
`);
|
|
62518
|
+
await selectSearchProviderAndReport(searchProviderController, io.onSystem, normalizedProviderId);
|
|
60520
62519
|
})();
|
|
60521
62520
|
return;
|
|
60522
62521
|
}
|
|
@@ -60550,6 +62549,10 @@ Staying in the current session.
|
|
|
60550
62549
|
showReview();
|
|
60551
62550
|
return;
|
|
60552
62551
|
}
|
|
62552
|
+
if (isMcpToolsCommand(command.name)) {
|
|
62553
|
+
showTools();
|
|
62554
|
+
return;
|
|
62555
|
+
}
|
|
60553
62556
|
if (command.name === "/copy") {
|
|
60554
62557
|
const target = newestBlock();
|
|
60555
62558
|
if (target === undefined || !copyBlock(target.id)) {
|
|
@@ -61256,6 +63259,8 @@ init_guard2();
|
|
|
61256
63259
|
init_providers();
|
|
61257
63260
|
var DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
61258
63261
|
var ANTHROPIC_MODELS = ["claude-sonnet-5", "claude-opus-4-8", "claude-haiku-4-5"];
|
|
63262
|
+
var OPENAI_MODELS = ["gpt-5.6", "gpt-5.6-terra", "gpt-5.6-luna"];
|
|
63263
|
+
var GEMINI_MODELS = ["gemini-3.7-flash", "gemini-2.5-pro", "gemini-2.5-flash-lite"];
|
|
61259
63264
|
var FAKE_MODELS = ["fake-echo"];
|
|
61260
63265
|
function isEmbeddingModel(model) {
|
|
61261
63266
|
const name = typeof model.name === "string" ? model.name.toLowerCase() : "";
|
|
@@ -61316,6 +63321,14 @@ async function detectProviders(deps) {
|
|
|
61316
63321
|
if (typeof anthropicKey === "string" && anthropicKey.length > 0) {
|
|
61317
63322
|
detected.push({ name: "anthropic", models: [...ANTHROPIC_MODELS] });
|
|
61318
63323
|
}
|
|
63324
|
+
const openaiKey = deps.env.OPENAI_API_KEY;
|
|
63325
|
+
if (typeof openaiKey === "string" && openaiKey.length > 0) {
|
|
63326
|
+
detected.push({ name: "openai", models: [...OPENAI_MODELS] });
|
|
63327
|
+
}
|
|
63328
|
+
const geminiKey = deps.env.GEMINI_API_KEY ?? deps.env.GOOGLE_API_KEY;
|
|
63329
|
+
if (typeof geminiKey === "string" && geminiKey.length > 0) {
|
|
63330
|
+
detected.push({ name: "gemini", models: [...GEMINI_MODELS] });
|
|
63331
|
+
}
|
|
61319
63332
|
for (const p of OPENAI_COMPAT_PROVIDERS) {
|
|
61320
63333
|
if (!isProviderPlatformSupported(p, platform)) {
|
|
61321
63334
|
continue;
|
|
@@ -62154,6 +64167,37 @@ ${GUTTER}${style.dim("[y/N] ")}`);
|
|
|
62154
64167
|
const approved2 = /^y(es)?$/i.test(answer2);
|
|
62155
64168
|
out(approved2 ? style.green(`approved
|
|
62156
64169
|
`) : style.red(`denied
|
|
64170
|
+
`));
|
|
64171
|
+
if (!approved2) {
|
|
64172
|
+
return false;
|
|
64173
|
+
}
|
|
64174
|
+
return meta?.fingerprint !== undefined ? { approved: true, fingerprint: meta.fingerprint } : true;
|
|
64175
|
+
}
|
|
64176
|
+
if (tool.startsWith(MCP_ELICITATION_TOOL_PREFIX)) {
|
|
64177
|
+
const described = describeElicitationPrompt(tool, input2) ?? { message: input2, command: undefined };
|
|
64178
|
+
out(`
|
|
64179
|
+
${GUTTER}${style.yellow("Approve codex elicitation?")}
|
|
64180
|
+
`);
|
|
64181
|
+
out(`${indentBlock(described.message, GUTTER)}
|
|
64182
|
+
`);
|
|
64183
|
+
if (described.command !== undefined) {
|
|
64184
|
+
out(`${GUTTER}${style.dim(`command: ${described.command}`)}
|
|
64185
|
+
`);
|
|
64186
|
+
}
|
|
64187
|
+
if (meta?.destructive === true) {
|
|
64188
|
+
out(`${GUTTER}${style.yellow("deletes a file, touches .git/, or touches many files in one call")}
|
|
64189
|
+
`);
|
|
64190
|
+
}
|
|
64191
|
+
if (meta?.credentials === true) {
|
|
64192
|
+
out(`${GUTTER}${style.yellow("touches the agent's own permission/credential files")}
|
|
64193
|
+
`);
|
|
64194
|
+
}
|
|
64195
|
+
out(`
|
|
64196
|
+
${GUTTER}${style.dim("[y/N] ")}`);
|
|
64197
|
+
const answer2 = (await readLine() ?? "").trim();
|
|
64198
|
+
const approved2 = /^y(es)?$/i.test(answer2);
|
|
64199
|
+
out(approved2 ? style.green(`approved
|
|
64200
|
+
`) : style.red(`denied
|
|
62157
64201
|
`));
|
|
62158
64202
|
if (!approved2) {
|
|
62159
64203
|
return false;
|
|
@@ -65129,7 +67173,7 @@ function printHelp17() {
|
|
|
65129
67173
|
|
|
65130
67174
|
// src/commands/update.ts
|
|
65131
67175
|
import { spawn as spawn5 } from "child_process";
|
|
65132
|
-
import { chmod as chmod4, mkdir as mkdir55, readFile as readFile79, readdir as
|
|
67176
|
+
import { chmod as chmod4, mkdir as mkdir55, readFile as readFile79, readdir as readdir26, writeFile as writeFile49 } from "fs/promises";
|
|
65133
67177
|
import { access as access4, constants as constants2, existsSync as existsSync30 } from "fs";
|
|
65134
67178
|
import path156 from "path";
|
|
65135
67179
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
@@ -65481,7 +67525,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
65481
67525
|
}
|
|
65482
67526
|
let dirEntries;
|
|
65483
67527
|
try {
|
|
65484
|
-
dirEntries = (await
|
|
67528
|
+
dirEntries = (await readdir26(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
|
|
65485
67529
|
} catch {
|
|
65486
67530
|
return null;
|
|
65487
67531
|
}
|
|
@@ -65775,7 +67819,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
65775
67819
|
return pages;
|
|
65776
67820
|
}
|
|
65777
67821
|
async function listMarkdownFiles(root) {
|
|
65778
|
-
const entries = await
|
|
67822
|
+
const entries = await readdir26(root, { withFileTypes: true });
|
|
65779
67823
|
const files = [];
|
|
65780
67824
|
for (const entry of entries) {
|
|
65781
67825
|
const fullPath = path156.join(root, entry.name);
|
|
@@ -66215,7 +68259,7 @@ async function runPostUpdateHooks(projectRoot) {
|
|
|
66215
68259
|
if (!await pathExists(hooksDir)) {
|
|
66216
68260
|
return;
|
|
66217
68261
|
}
|
|
66218
|
-
const entries = (await
|
|
68262
|
+
const entries = (await readdir26(hooksDir)).sort();
|
|
66219
68263
|
for (const entry of entries) {
|
|
66220
68264
|
const hookPath = path156.join(hooksDir, entry);
|
|
66221
68265
|
try {
|
|
@@ -67921,7 +69965,7 @@ function oracleRates(score) {
|
|
|
67921
69965
|
rates.recall = deriveRate(score.truePositives, score.goldSize, ORACLE_RELIABILITY);
|
|
67922
69966
|
return Object.keys(rates).length > 0 ? rates : undefined;
|
|
67923
69967
|
}
|
|
67924
|
-
var
|
|
69968
|
+
var DEFAULT_MODEL5 = "gdgraph-oracle";
|
|
67925
69969
|
var GOLD_KIND_LABELS = {
|
|
67926
69970
|
"co-change": "co-change prediction",
|
|
67927
69971
|
dependency: "graph correctness"
|
|
@@ -67960,7 +70004,7 @@ function scoreGoldRun(input2, named, options = {}) {
|
|
|
67960
70004
|
variant: "baseline",
|
|
67961
70005
|
run_id: `${taskId}#1`,
|
|
67962
70006
|
ladder: options.ladder ?? "metastore",
|
|
67963
|
-
model: options.model ??
|
|
70007
|
+
model: options.model ?? DEFAULT_MODEL5,
|
|
67964
70008
|
cacheState: options.cacheState ?? "unknown",
|
|
67965
70009
|
leakageAssertion: options.leakageAssertion ?? "not-applicable",
|
|
67966
70010
|
caseKind: "deterministic",
|
|
@@ -68204,7 +70248,7 @@ function buildEvidenceBundle(input2, options = {}) {
|
|
|
68204
70248
|
run: {
|
|
68205
70249
|
target: score.target,
|
|
68206
70250
|
variant,
|
|
68207
|
-
model: options.model ??
|
|
70251
|
+
model: options.model ?? DEFAULT_MODEL5,
|
|
68208
70252
|
seed,
|
|
68209
70253
|
cacheState: options.cacheState ?? "unknown",
|
|
68210
70254
|
startedAt: timestamp,
|
|
@@ -69195,7 +71239,7 @@ keryx workspace list-proposals [<workspace-id>]`);
|
|
|
69195
71239
|
}
|
|
69196
71240
|
function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
69197
71241
|
const sections = [];
|
|
69198
|
-
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss proposal ${item.proposalId} in workspace ${item.workspaceId}? ` + `Recommendation: ${item.fresh ? "evidence is fresh \u2014 review now (`keryx workspace review " + item.workspaceId + " " + item.proposalId + " --decision <accepted|rejected|dismissed>`)" : "evidence has drifted since this proposal was created \u2014 treat as stale, re-run wrap-up before deciding"}.`));
|
|
71242
|
+
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss ${item.kind} proposal ${item.proposalId} in workspace ${item.workspaceId}` + `${item.note !== undefined ? `: "${item.note}"` : ""}? ` + `Recommendation: ${item.fresh ? "evidence is fresh \u2014 review now (`keryx workspace review " + item.workspaceId + " " + item.proposalId + " --decision <accepted|rejected|dismissed>`)" : "evidence has drifted since this proposal was created \u2014 treat as stale, re-run wrap-up before deciding"}.`));
|
|
69199
71243
|
sections.push(renderSection("Blocked sessions (stopped unattended)", report.blocked, (item) => `- Session ${item.sessionId} stopped unattended (${item.terminalState.reason}) at ${item.terminalState.occurredAt}. Resume it, or archive and move on? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to resume and unblock it.`));
|
|
69200
71244
|
sections.push(renderSection("Unbound candidates (wrap-up ran, no workspace bound)", report.unboundCandidates, (item) => `- Session ${item.sessionId} produced untriaged seeds with no workspace bound (${item.summary}). Bind to a workspace and propose, or discard? ` + `Recommendation: pick a workspace, then \`keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}\` (evidence: ${item.evidencePath}).`));
|
|
69201
71245
|
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt} with no proposal, terminal state, or unbound-candidate artifact recorded. Investigate, or ignore? ` + `Recommendation: \`keryx sessions list\` / \`keryx shell -r ${item.sessionId}\` to see what happened.`));
|
|
@@ -69403,6 +71447,6 @@ if (import.meta.main) {
|
|
|
69403
71447
|
});
|
|
69404
71448
|
}
|
|
69405
71449
|
export {
|
|
69406
|
-
|
|
69407
|
-
|
|
71450
|
+
CLI_ROUTES,
|
|
71451
|
+
main
|
|
69408
71452
|
};
|