@wrongstack/core 0.305.0 → 0.306.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chronicle/project-server.js +18 -48
- package/dist/coordination/agents/index.js +3 -2
- package/dist/coordination/agents/types.d.ts +1 -1
- package/dist/coordination/index.d.ts +1 -0
- package/dist/coordination/index.js +5 -2
- package/dist/coordination/mailbox-project-server.js +28 -57
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +2764 -2632
- package/dist/core/system-prompt-blocks.d.ts +1 -1
- package/dist/core/system-prompt-builder.d.ts +7 -1
- package/dist/core/system-prompt-glossary.d.ts +0 -23
- package/dist/defaults/index.js +3 -2
- package/dist/execution/index.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +898 -531
- package/dist/observability/index.js +7 -3
- package/dist/plugin/index.d.ts +4 -3
- package/dist/plugin/index.js +589 -143
- package/dist/plugins/auto-review-plugin.d.ts +14 -7
- package/dist/plugins/chimera-plugin.d.ts +15 -1
- package/dist/plugins/review-finding-integration.d.ts +15 -3
- package/dist/plugins/review-finding-parser.d.ts +36 -0
- package/dist/plugins/review-finding-types.d.ts +46 -0
- package/dist/plugins/review-finding-verification.d.ts +53 -0
- package/dist/plugins/review-report-integration.d.ts +1 -0
- package/dist/plugins/review-report-store.d.ts +7 -0
- package/dist/plugins/review-report-types.d.ts +14 -0
- package/dist/plugins/review-types.d.ts +74 -0
- package/dist/replay/replay-provider-runner.d.ts +5 -4
- package/dist/session-catalog/project-server.js +36 -65
- package/dist/tools/fallback-manage-tool-options.d.ts +9 -0
- package/dist/tools/index.js +91 -38
- package/dist/tools/one-shot-llm-tool.d.ts +6 -0
- package/dist/types/blocks.d.ts +10 -0
- package/dist/types/config/ui.d.ts +7 -4
- package/dist/types/index.js +21 -1
- package/dist/utils/index.js +8 -9
- package/instructions/agents/browser.md +1 -0
- package/instructions/agents/e2e.md +2 -0
- package/instructions/llm/chimera-review.md +52 -1
- package/instructions/system-lite.md +17 -6
- package/instructions/system-pro.md +25 -20
- package/instructions/system.md +25 -12
- package/package.json +3 -3
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// src/session-catalog/project-server.ts
|
|
4
4
|
import { randomBytes as randomBytes2, randomUUID as randomUUID2 } from "node:crypto";
|
|
5
|
-
import * as fs3 from "node:fs";
|
|
6
5
|
import * as fsp from "node:fs/promises";
|
|
7
6
|
import * as net from "node:net";
|
|
8
7
|
import * as path3 from "node:path";
|
|
8
|
+
import { bindProjectEndpoint } from "@wrongstack/persistence";
|
|
9
9
|
|
|
10
10
|
// src/security/file-permissions.ts
|
|
11
11
|
import { chmod } from "node:fs/promises";
|
|
@@ -177,17 +177,9 @@ function useDaemonPerfDefaults() {
|
|
|
177
177
|
|
|
178
178
|
// src/session-catalog/endpoint.ts
|
|
179
179
|
import { createHash } from "node:crypto";
|
|
180
|
-
import * as fs from "node:fs";
|
|
181
180
|
import * as os from "node:os";
|
|
182
181
|
import * as path from "node:path";
|
|
183
182
|
|
|
184
|
-
// src/utils/socket-path.ts
|
|
185
|
-
import {
|
|
186
|
-
assertUnixSocketPathWithinLimit,
|
|
187
|
-
checkUnixSocketPath,
|
|
188
|
-
unixSocketPathLimit
|
|
189
|
-
} from "@wrongstack/persistence";
|
|
190
|
-
|
|
191
183
|
// src/session-catalog/protocol.ts
|
|
192
184
|
var SESSION_CATALOG_PROTOCOL_VERSION = 1;
|
|
193
185
|
var SESSION_CATALOG_MAX_FRAME_CHARS = 4 * 1024 * 1024;
|
|
@@ -218,15 +210,10 @@ function sessionCatalogProjectServerEndpoint(projectDir) {
|
|
|
218
210
|
function sessionCatalogProjectServerMetadataPath(projectDir) {
|
|
219
211
|
return path.join(projectDir, SESSION_CATALOG_METADATA_FILE);
|
|
220
212
|
}
|
|
221
|
-
function ensureSessionCatalogSocketDirectory(endpoint2) {
|
|
222
|
-
if (process.platform === "win32") return;
|
|
223
|
-
assertUnixSocketPathWithinLimit(endpoint2, "session-catalog");
|
|
224
|
-
fs.mkdirSync(path.dirname(endpoint2), { recursive: true, mode: 448 });
|
|
225
|
-
}
|
|
226
213
|
|
|
227
214
|
// src/session-catalog/store.ts
|
|
228
215
|
import { createHash as createHash2, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
229
|
-
import * as
|
|
216
|
+
import * as fs from "node:fs";
|
|
230
217
|
import * as path2 from "node:path";
|
|
231
218
|
|
|
232
219
|
// src/coordination/sqlite-mailbox-schema.ts
|
|
@@ -632,7 +619,7 @@ var SessionCatalogStore = class {
|
|
|
632
619
|
constructor(projectDir) {
|
|
633
620
|
this.projectDir = projectDir;
|
|
634
621
|
this.sessionsDir = path2.join(projectDir, "sessions");
|
|
635
|
-
|
|
622
|
+
fs.mkdirSync(this.sessionsDir, { recursive: true, mode: 448 });
|
|
636
623
|
this.databasePath = path2.join(this.sessionsDir, "catalog.sqlite");
|
|
637
624
|
const Database = loadDatabaseSync();
|
|
638
625
|
this.db = new Database(this.databasePath);
|
|
@@ -650,12 +637,12 @@ var SessionCatalogStore = class {
|
|
|
650
637
|
this.db.close();
|
|
651
638
|
const quarantine = `${this.databasePath}.corrupt-${Date.now()}`;
|
|
652
639
|
try {
|
|
653
|
-
|
|
640
|
+
fs.renameSync(this.databasePath, quarantine);
|
|
654
641
|
} catch {
|
|
655
642
|
}
|
|
656
643
|
for (const suffix of ["-wal", "-shm"]) {
|
|
657
644
|
try {
|
|
658
|
-
|
|
645
|
+
fs.renameSync(`${this.databasePath}${suffix}`, `${quarantine}${suffix}`);
|
|
659
646
|
} catch {
|
|
660
647
|
}
|
|
661
648
|
}
|
|
@@ -881,7 +868,7 @@ var SessionCatalogStore = class {
|
|
|
881
868
|
if (this.maintenanceExists(targetSessionId))
|
|
882
869
|
throw conflict(`Session ${targetSessionId} is under maintenance`);
|
|
883
870
|
const catalog = this.db.prepare("SELECT 1 AS yes FROM sessions WHERE session_id=?").get(targetSessionId);
|
|
884
|
-
if (!catalog && !
|
|
871
|
+
if (!catalog && !fs.existsSync(this.containedPath(`${targetSessionId}.jsonl`)))
|
|
885
872
|
throw new Error(`Session not found: ${targetSessionId}`);
|
|
886
873
|
const reservationId = randomUUID();
|
|
887
874
|
const now = Date.now();
|
|
@@ -1027,7 +1014,7 @@ var SessionCatalogStore = class {
|
|
|
1027
1014
|
transcriptRelativePath = normalizedTranscript;
|
|
1028
1015
|
summaryRelativePath = normalizedSummary;
|
|
1029
1016
|
const transcript = this.containedPath(transcriptRelativePath);
|
|
1030
|
-
const stat =
|
|
1017
|
+
const stat = fs.existsSync(transcript) ? fs.statSync(transcript) : void 0;
|
|
1031
1018
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1032
1019
|
return this.transaction(() => {
|
|
1033
1020
|
const prior = this.db.prepare("SELECT summary_revision FROM sessions WHERE session_id=?").get(summary.id);
|
|
@@ -1150,7 +1137,7 @@ var SessionCatalogStore = class {
|
|
|
1150
1137
|
if (trimmed) summary.name = this.scrubber.scrub(trimmed).slice(0, 500);
|
|
1151
1138
|
else delete summary.name;
|
|
1152
1139
|
const summaryPath = this.containedPath(current.summaryRelativePath);
|
|
1153
|
-
|
|
1140
|
+
fs.mkdirSync(path2.dirname(summaryPath), { recursive: true, mode: 448 });
|
|
1154
1141
|
await atomicWrite(summaryPath, `${JSON.stringify(summary)}
|
|
1155
1142
|
`, { mode: 384 });
|
|
1156
1143
|
try {
|
|
@@ -1213,13 +1200,13 @@ var SessionCatalogStore = class {
|
|
|
1213
1200
|
path2.join(path2.dirname(transcript), path2.basename(sessionId))
|
|
1214
1201
|
];
|
|
1215
1202
|
const trashRoot = path2.join(this.sessionsDir, "_trash", lease.leaseId);
|
|
1216
|
-
|
|
1203
|
+
fs.mkdirSync(trashRoot, { recursive: true, mode: 448 });
|
|
1217
1204
|
const moved = [];
|
|
1218
1205
|
try {
|
|
1219
1206
|
artifacts.forEach((artifact, index) => {
|
|
1220
|
-
if (!
|
|
1207
|
+
if (!fs.existsSync(artifact)) return;
|
|
1221
1208
|
const target = path2.join(trashRoot, `${index}-${path2.basename(artifact)}`);
|
|
1222
|
-
|
|
1209
|
+
fs.renameSync(artifact, target);
|
|
1223
1210
|
moved.push({ from: artifact, to: target });
|
|
1224
1211
|
});
|
|
1225
1212
|
this.transaction(() => {
|
|
@@ -1234,17 +1221,17 @@ var SessionCatalogStore = class {
|
|
|
1234
1221
|
} catch (error) {
|
|
1235
1222
|
for (const item of moved.reverse()) {
|
|
1236
1223
|
try {
|
|
1237
|
-
|
|
1238
|
-
|
|
1224
|
+
fs.mkdirSync(path2.dirname(item.from), { recursive: true, mode: 448 });
|
|
1225
|
+
fs.renameSync(item.to, item.from);
|
|
1239
1226
|
} catch {
|
|
1240
1227
|
}
|
|
1241
1228
|
}
|
|
1242
1229
|
throw error;
|
|
1243
1230
|
}
|
|
1244
1231
|
try {
|
|
1245
|
-
|
|
1232
|
+
fs.rmSync(trashRoot, { recursive: true, force: true });
|
|
1246
1233
|
const trashParent = path2.dirname(trashRoot);
|
|
1247
|
-
if (
|
|
1234
|
+
if (fs.readdirSync(trashParent).length === 0) fs.rmdirSync(trashParent);
|
|
1248
1235
|
} catch {
|
|
1249
1236
|
}
|
|
1250
1237
|
}
|
|
@@ -1281,10 +1268,10 @@ var SessionCatalogStore = class {
|
|
|
1281
1268
|
for (const id of ids) {
|
|
1282
1269
|
try {
|
|
1283
1270
|
const summaryFile = this.containedPath(`${id}.summary.json`);
|
|
1284
|
-
const summary =
|
|
1271
|
+
const summary = fs.existsSync(summaryFile) ? parseJson(fs.readFileSync(summaryFile, "utf8")) : this.summarizeTranscript(id);
|
|
1285
1272
|
if (!summary || summary.id !== id) throw new Error("summary identity mismatch");
|
|
1286
1273
|
const transcript = this.containedPath(`${id}.jsonl`);
|
|
1287
|
-
const stat =
|
|
1274
|
+
const stat = fs.existsSync(transcript) ? fs.statSync(transcript) : void 0;
|
|
1288
1275
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1289
1276
|
this.db.prepare(
|
|
1290
1277
|
"INSERT INTO sessions(session_id,transcript_relative_path,summary_relative_path,summary_json,transcript_size,transcript_mtime_ms,summary_revision,indexed_at,damaged) VALUES (?,?,?,?,?,?,?,?,0)"
|
|
@@ -1325,7 +1312,7 @@ var SessionCatalogStore = class {
|
|
|
1325
1312
|
walkFiles(root, suffix) {
|
|
1326
1313
|
const result = [];
|
|
1327
1314
|
const visit = (dir) => {
|
|
1328
|
-
for (const entry of
|
|
1315
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
1329
1316
|
if (entry.isDirectory()) {
|
|
1330
1317
|
if (entry.name !== "_cas" && entry.name !== "_trash") visit(path2.join(dir, entry.name));
|
|
1331
1318
|
} else if (entry.isFile() && entry.name.endsWith(suffix))
|
|
@@ -1337,7 +1324,7 @@ var SessionCatalogStore = class {
|
|
|
1337
1324
|
}
|
|
1338
1325
|
summarizeTranscript(id) {
|
|
1339
1326
|
const file = this.containedPath(`${id}.jsonl`);
|
|
1340
|
-
const lines =
|
|
1327
|
+
const lines = fs.readFileSync(file, "utf8").split(/\r?\n/);
|
|
1341
1328
|
let start;
|
|
1342
1329
|
let endedAt;
|
|
1343
1330
|
let lastActivityAt;
|
|
@@ -1846,7 +1833,6 @@ async function stop(_reason) {
|
|
|
1846
1833
|
if (process.platform !== "win32") await fsp.rm(endpoint, { force: true }).catch(() => void 0);
|
|
1847
1834
|
await removeOwnedMetadata();
|
|
1848
1835
|
}
|
|
1849
|
-
ensureSessionCatalogSocketDirectory(endpoint);
|
|
1850
1836
|
var server = net.createServer((socket) => {
|
|
1851
1837
|
if (stopping) {
|
|
1852
1838
|
socket.destroy();
|
|
@@ -1870,42 +1856,28 @@ var server = net.createServer((socket) => {
|
|
|
1870
1856
|
scheduleIdleStop(disconnectedIdleMs);
|
|
1871
1857
|
});
|
|
1872
1858
|
});
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
}
|
|
1877
|
-
server.on("error", (error) => {
|
|
1878
|
-
if (error.code === "EADDRINUSE" && process.platform === "win32") {
|
|
1859
|
+
void (async () => {
|
|
1860
|
+
const bind = await bindProjectEndpoint({ server, endpoint, service: "session-catalog" });
|
|
1861
|
+
if (bind.outcome === "already-owned") {
|
|
1879
1862
|
process.exitCode = 0;
|
|
1880
1863
|
return;
|
|
1881
1864
|
}
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
probe.destroy();
|
|
1887
|
-
process.exitCode = 0;
|
|
1888
|
-
});
|
|
1889
|
-
probe.once("error", () => {
|
|
1890
|
-
probe.destroy();
|
|
1891
|
-
try {
|
|
1892
|
-
fs3.rmSync(endpoint, { force: true });
|
|
1893
|
-
} catch {
|
|
1894
|
-
}
|
|
1895
|
-
probing = false;
|
|
1896
|
-
listen();
|
|
1897
|
-
});
|
|
1865
|
+
if (bind.outcome === "failed") {
|
|
1866
|
+
process.stderr.write(`session-catalog project server failed: ${bind.error.message}
|
|
1867
|
+
`);
|
|
1868
|
+
process.exitCode = 1;
|
|
1898
1869
|
return;
|
|
1899
1870
|
}
|
|
1900
|
-
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
if (process.platform !== "win32") {
|
|
1904
|
-
try {
|
|
1905
|
-
fs3.chmodSync(endpoint, 384);
|
|
1906
|
-
} catch {
|
|
1907
|
-
}
|
|
1871
|
+
if (bind.reclaimedStaleEndpoint) {
|
|
1872
|
+
process.stderr.write(`session-catalog project server reclaimed stale endpoint ${endpoint}
|
|
1873
|
+
`);
|
|
1908
1874
|
}
|
|
1875
|
+
server.on("error", (error) => {
|
|
1876
|
+
if (stopping) return;
|
|
1877
|
+
process.stderr.write(`session-catalog project server error: ${error.message}
|
|
1878
|
+
`);
|
|
1879
|
+
process.exitCode = 1;
|
|
1880
|
+
});
|
|
1909
1881
|
try {
|
|
1910
1882
|
store = new SessionCatalogStore(parsed.projectDir);
|
|
1911
1883
|
void writeMetadata().then(() => metadataReadyResolve?.()).catch(() => void stop("metadata write failed"));
|
|
@@ -1914,8 +1886,7 @@ server.on("listening", () => {
|
|
|
1914
1886
|
return;
|
|
1915
1887
|
}
|
|
1916
1888
|
scheduleIdleStop();
|
|
1917
|
-
});
|
|
1889
|
+
})();
|
|
1918
1890
|
process.once("SIGINT", () => void stop("SIGINT"));
|
|
1919
1891
|
process.once("SIGTERM", () => void stop("SIGTERM"));
|
|
1920
|
-
listen();
|
|
1921
1892
|
//# sourceMappingURL=project-server.js.map
|
|
@@ -10,6 +10,15 @@ export interface FallbackManageToolOptions {
|
|
|
10
10
|
updateConfig: (mutate: (cfg: Record<string, unknown>) => void) => Promise<void>;
|
|
11
11
|
/** Optional secure interactive input callback for secrets such as API keys. */
|
|
12
12
|
requestInput?: ((prompt: string) => Promise<string>) | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Optional live provider/model switch (the host's switchProviderAndModel).
|
|
15
|
+
* When present, `leader_model_set` routes leader changes through it so the
|
|
16
|
+
* live agent context (provider instance, model, context caps) follows the
|
|
17
|
+
* config write. Returns an error string on failure, null on success —
|
|
18
|
+
* mirroring cli-main's switch callback. When absent, the tool persists the
|
|
19
|
+
* config and reports that the live session keeps its current model.
|
|
20
|
+
*/
|
|
21
|
+
switchProviderAndModel?: ((providerId: string, modelId: string) => Promise<string | null>) | undefined;
|
|
13
22
|
/** Optional logger for internal warnings. */
|
|
14
23
|
logger?: Logger | undefined;
|
|
15
24
|
}
|
package/dist/tools/index.js
CHANGED
|
@@ -3908,6 +3908,7 @@ var TOOLS = {
|
|
|
3908
3908
|
"glob",
|
|
3909
3909
|
"search",
|
|
3910
3910
|
"tree",
|
|
3911
|
+
"diff",
|
|
3911
3912
|
"write",
|
|
3912
3913
|
"edit",
|
|
3913
3914
|
"replace",
|
|
@@ -4939,7 +4940,7 @@ var VERIFY_AGENTS = [
|
|
|
4939
4940
|
id: "e2e",
|
|
4940
4941
|
name: "E2E",
|
|
4941
4942
|
role: "e2e",
|
|
4942
|
-
tools: [...TOOLS.build, "fetch", ...SPECIALIST_TOOLS.browser],
|
|
4943
|
+
tools: [...TOOLS.build, "fetch", "e2e_plan", ...SPECIALIST_TOOLS.browser],
|
|
4943
4944
|
prompt: agentPrompt("e2e")
|
|
4944
4945
|
},
|
|
4945
4946
|
budget: HEAVY_BUDGET,
|
|
@@ -5452,7 +5453,7 @@ var DOMAIN_AGENTS = [
|
|
|
5452
5453
|
id: "designer",
|
|
5453
5454
|
name: "Designer",
|
|
5454
5455
|
role: "designer",
|
|
5455
|
-
tools: [...TOOLS.docs],
|
|
5456
|
+
tools: [...TOOLS.docs, "design"],
|
|
5456
5457
|
prompt: agentPrompt("designer")
|
|
5457
5458
|
},
|
|
5458
5459
|
budget: MEDIUM_BUDGET,
|
|
@@ -6661,7 +6662,7 @@ function createFallbackChainManageTool(opts) {
|
|
|
6661
6662
|
name: FALLBACK_CHAIN_MANAGE_TOOL_NAME,
|
|
6662
6663
|
description: "View or change the active rate-limit fallback chain. When the primary model is overloaded (429/5xx), the agent rotates through this chain in order. Every new entry must be a FAVORITE model \u2014 add it via favorite_manage first. Use insert to place a fallback at a specific position; use remove to delete an entry.",
|
|
6663
6664
|
usageHint: '"list" to see the current chain. "add" with a favorite model to append. "insert" with an index (1-based) to place before that position. "remove" with index or model ref. "clear" to empty the chain (auto fallback takes over).',
|
|
6664
|
-
category: "
|
|
6665
|
+
category: "config",
|
|
6665
6666
|
inputSchema: FALLBACK_CHAIN_SCHEMA,
|
|
6666
6667
|
permission: "auto",
|
|
6667
6668
|
mutating: true,
|
|
@@ -6811,7 +6812,7 @@ function createFavoriteManageTool(opts) {
|
|
|
6811
6812
|
name: FAVORITE_MANAGE_TOOL_NAME,
|
|
6812
6813
|
description: "Manage your favorite provider/model list. Favorites are the only models that can be added to fallback chains and profiles. The LLM uses this tool to curate which models are available for fallback and role assignment.",
|
|
6813
6814
|
usageHint: 'Start with "list" to see current favorites. Use "add <provider/model>" to add. Use "remove <index|ref>" to remove.',
|
|
6814
|
-
category: "
|
|
6815
|
+
category: "config",
|
|
6815
6816
|
inputSchema: FAVORITE_MANAGE_SCHEMA,
|
|
6816
6817
|
permission: "auto",
|
|
6817
6818
|
mutating: true,
|
|
@@ -6891,7 +6892,7 @@ async function storeProviderKey(providers, input, keyValue, opts) {
|
|
|
6891
6892
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6892
6893
|
});
|
|
6893
6894
|
entry.apiKeys = existingKeys;
|
|
6894
|
-
entry.apiKey
|
|
6895
|
+
delete entry.apiKey;
|
|
6895
6896
|
if (input.setActive !== false) {
|
|
6896
6897
|
entry.activeKey = label;
|
|
6897
6898
|
}
|
|
@@ -6934,7 +6935,7 @@ function createSystemConfigViewTool(opts) {
|
|
|
6934
6935
|
name: SYSTEM_CONFIG_VIEW_TOOL_NAME,
|
|
6935
6936
|
description: "Get a comprehensive view of all provider, model, fallback, and matrix configuration. Shows the complete state across all configurable areas so you can see what is available and make informed decisions when assigning models, creating fallback profiles, or managing providers. Use the section parameter to focus on specific areas.",
|
|
6936
6937
|
usageHint: '"section: all" for everything. "section: providers" for configured providers and key status. "section: models" for leader model and favorites. "section: fallbacks" for chains, profiles, and toggles. "section: matrix" for per-role assignments. "section: refiner" for goal refinement config.',
|
|
6937
|
-
category: "
|
|
6938
|
+
category: "config",
|
|
6938
6939
|
inputSchema: SYSTEM_CONFIG_VIEW_SCHEMA,
|
|
6939
6940
|
permission: "auto",
|
|
6940
6941
|
mutating: false,
|
|
@@ -7238,7 +7239,7 @@ function createFallbackProfileManageTool(opts) {
|
|
|
7238
7239
|
name: FALLBACK_PROFILE_MANAGE_TOOL_NAME,
|
|
7239
7240
|
description: "Manage named fallback profiles. A profile is a reusable, ordered list of model references that can be assigned to agent roles. Every entry in a profile must be a FAVORITE model \u2014 add it via favorite_manage first. Use /setmodel or agent_model_assign to assign a profile to a role.",
|
|
7240
7241
|
usageHint: '"list" to see all profiles. "set" with name and chain (array of model refs) to create or replace a profile. "delete" with name to remove a profile.',
|
|
7241
|
-
category: "
|
|
7242
|
+
category: "config",
|
|
7242
7243
|
inputSchema: FALLBACK_PROFILE_SCHEMA,
|
|
7243
7244
|
permission: "auto",
|
|
7244
7245
|
mutating: true,
|
|
@@ -7349,7 +7350,7 @@ function createAgentModelAssignTool(opts) {
|
|
|
7349
7350
|
name: AGENT_MODEL_ASSIGN_TOOL_NAME,
|
|
7350
7351
|
description: "Assign a provider/model or a fallback profile to a specific agent role, phase, or the fleet-wide default. This is the LLM-accessible equivalent of /setmodel set. The provider+model combination must be in your favorites list (unless only clearing). Resolution precedence: exact role \u2192 phase \u2192 * \u2192 leader model.",
|
|
7351
7352
|
usageHint: 'Use "list" as role to see current assignments. Set with role + model, or role + provider + model, or role + profile. Set role + clear=true to remove a matrix entry. The provider/model must be a favorite.',
|
|
7352
|
-
category: "
|
|
7353
|
+
category: "config",
|
|
7353
7354
|
inputSchema: AGENT_MODEL_ASSIGN_SCHEMA,
|
|
7354
7355
|
permission: "auto",
|
|
7355
7356
|
mutating: true,
|
|
@@ -7497,7 +7498,7 @@ function createProviderManageTool(opts) {
|
|
|
7497
7498
|
name: PROVIDER_MANAGE_TOOL_NAME,
|
|
7498
7499
|
description: "View or configure provider entries. List all configured providers with their type, model lists, base URL, and key status. Add new providers, update their settings, or remove unused ones. API keys should be set via provider_key_set instead of passing them here \u2014 they are visible in the LLM output.",
|
|
7499
7500
|
usageHint: '"list" to see all providers. "add" with provider id and type to create. "configure" to update models, baseUrl, family, or envVars. "remove" to delete a provider. Use provider_key_set for API key management.',
|
|
7500
|
-
category: "
|
|
7501
|
+
category: "config",
|
|
7501
7502
|
inputSchema: PROVIDER_MANAGE_SCHEMA,
|
|
7502
7503
|
permission: "auto",
|
|
7503
7504
|
mutating: true,
|
|
@@ -7646,9 +7647,11 @@ function createProviderKeySetTool(opts) {
|
|
|
7646
7647
|
name: PROVIDER_KEY_SET_TOOL_NAME,
|
|
7647
7648
|
description: "Set the API key for a provider. For security, prefer using envVar (reads from environment variable, value never visible to the LLM) over passing the key directly. When neither key nor envVar is provided, the tool returns a prompt for interactive key entry \u2014 the UI will present an input field and the key is stored without LLM visibility.\n\nAfter setting a key, the provider becomes usable for model assignments and fallback chains. Add its models to favorites with favorite_manage to unlock them for fallback/profile use.",
|
|
7648
7649
|
usageHint: 'Preferred: provider_key_set({ provider: "openai", envVar: "OPENAI_API_KEY" }). For interactive input: provider_key_set({ provider: "openai" }) \u2014 the UI will prompt. Direct key: provider_key_set({ provider: "openai", key: "sk-..." }) \u2014 visible to LLM.',
|
|
7649
|
-
category: "
|
|
7650
|
+
category: "config",
|
|
7650
7651
|
inputSchema: PROVIDER_KEY_SET_SCHEMA,
|
|
7651
|
-
|
|
7652
|
+
// 'confirm', not 'auto' — this tool writes credentials to disk (and can
|
|
7653
|
+
// read arbitrary env vars into the config file), so the user must see it.
|
|
7654
|
+
permission: "confirm",
|
|
7652
7655
|
mutating: true,
|
|
7653
7656
|
riskTier: "standard",
|
|
7654
7657
|
icon: "settings",
|
|
@@ -7744,7 +7747,7 @@ function createLeaderModelSetTool(opts) {
|
|
|
7744
7747
|
name: LEADER_MODEL_SET_TOOL_NAME,
|
|
7745
7748
|
description: 'View or change the leader provider/model and system toggles. The leader is the primary model used for the main agent interactions. "set" changes it directly. "profile" derives it from a named fallback profile (first entry becomes leader, rest become the fallback chain). "toggle" controls fallbackAuto (smart default fallback) and favoriteModelsOnly (restrict auto-fallback to favorites only).',
|
|
7746
7749
|
usageHint: '"show" to see current state. "set" with provider+model to change. "profile" with name to derive from a profile. "toggle" with toggle name and value to change a boolean setting.',
|
|
7747
|
-
category: "
|
|
7750
|
+
category: "config",
|
|
7748
7751
|
inputSchema: LEADER_MODEL_SET_SCHEMA,
|
|
7749
7752
|
permission: "auto",
|
|
7750
7753
|
mutating: true,
|
|
@@ -7768,11 +7771,21 @@ function createLeaderModelSetTool(opts) {
|
|
|
7768
7771
|
if (!input.provider || !input.model) {
|
|
7769
7772
|
return { status: "error", message: 'Provide "provider" and "model" for the leader.' };
|
|
7770
7773
|
}
|
|
7774
|
+
if (opts.switchProviderAndModel) {
|
|
7775
|
+
const switchError = await opts.switchProviderAndModel(input.provider, input.model);
|
|
7776
|
+
if (switchError) {
|
|
7777
|
+
return {
|
|
7778
|
+
status: "error",
|
|
7779
|
+
message: `Could not switch to ${input.provider}/${input.model}: ${switchError}. Config was not changed.`
|
|
7780
|
+
};
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7771
7783
|
await opts.updateConfig((cfg) => {
|
|
7772
7784
|
cfg.provider = input.provider;
|
|
7773
7785
|
cfg.model = input.model;
|
|
7774
7786
|
});
|
|
7775
|
-
|
|
7787
|
+
const liveNote = opts.switchProviderAndModel ? "" : " (config updated \u2014 the live session keeps its current model until restart or /setmodel)";
|
|
7788
|
+
return { status: "ok", message: `\u2713 Leader \u2192 ${input.provider}/${input.model}${liveNote}` };
|
|
7776
7789
|
}
|
|
7777
7790
|
if (input.action === "profile") {
|
|
7778
7791
|
if (!input.profile) {
|
|
@@ -7791,15 +7804,25 @@ function createLeaderModelSetTool(opts) {
|
|
|
7791
7804
|
return { status: "error", message: `Cannot parse "${first}" as a valid model reference.` };
|
|
7792
7805
|
}
|
|
7793
7806
|
const rest = chain.slice(1);
|
|
7807
|
+
if (opts.switchProviderAndModel) {
|
|
7808
|
+
const switchError = await opts.switchProviderAndModel(provider, model);
|
|
7809
|
+
if (switchError) {
|
|
7810
|
+
return {
|
|
7811
|
+
status: "error",
|
|
7812
|
+
message: `Could not switch to ${provider}/${model}: ${switchError}. Config was not changed.`
|
|
7813
|
+
};
|
|
7814
|
+
}
|
|
7815
|
+
}
|
|
7794
7816
|
await opts.updateConfig((cfg) => {
|
|
7795
7817
|
cfg.provider = provider;
|
|
7796
7818
|
cfg.model = model;
|
|
7797
7819
|
cfg.fallbackModels = rest;
|
|
7798
7820
|
});
|
|
7821
|
+
const profileLiveNote = opts.switchProviderAndModel ? "" : "\n (config updated \u2014 the live session keeps its current model until restart or /setmodel)";
|
|
7799
7822
|
return {
|
|
7800
7823
|
status: "ok",
|
|
7801
7824
|
message: `\u2713 Leader \u2192 ${provider}/${model} (profile: ${input.profile})` + (rest.length > 0 ? `
|
|
7802
|
-
Fallback chain: ${rest.join(" \u2192 ")}` : "")
|
|
7825
|
+
Fallback chain: ${rest.join(" \u2192 ")}` : "") + profileLiveNote
|
|
7803
7826
|
};
|
|
7804
7827
|
}
|
|
7805
7828
|
if (input.action === "toggle") {
|
|
@@ -8283,20 +8306,22 @@ async function runEnable(name, deps) {
|
|
|
8283
8306
|
const known = Object.keys(all).join(", ");
|
|
8284
8307
|
return `Unknown server "${name}". Available presets: ${known}`;
|
|
8285
8308
|
}
|
|
8286
|
-
|
|
8309
|
+
const persistEnabled = () => updateJsonObjectFile(deps.configPath, (full) => {
|
|
8287
8310
|
const current = isMcpServerRecord(full.mcpServers) ? full.mcpServers : {};
|
|
8288
8311
|
setJsonPath(full, ["mcpServers", name], { ...current[name], ...cfg, enabled: true });
|
|
8289
8312
|
});
|
|
8290
8313
|
try {
|
|
8291
8314
|
const live = deps.registry.describe().find((s) => s.name === name);
|
|
8292
8315
|
if (live && live.state === "connected") {
|
|
8293
|
-
|
|
8316
|
+
await persistEnabled();
|
|
8317
|
+
return `Server "${name}" is already running (${live.toolCount} tools registered).`;
|
|
8294
8318
|
}
|
|
8295
8319
|
await deps.registry.start({ ...cfg, enabled: true });
|
|
8320
|
+
await persistEnabled();
|
|
8296
8321
|
const updated = deps.registry.describe().find((s) => s.name === name);
|
|
8297
|
-
return
|
|
8322
|
+
return `Enabled and started "${name}"${updated ? ` (${updated.toolCount} tools registered).` : "."}`;
|
|
8298
8323
|
} catch (err) {
|
|
8299
|
-
return
|
|
8324
|
+
return `Failed to start "${name}": ${toErrorMessage(err)}. Config was left unchanged (server stays disabled).`;
|
|
8300
8325
|
}
|
|
8301
8326
|
}
|
|
8302
8327
|
async function runDisable(name, deps) {
|
|
@@ -8370,34 +8395,34 @@ function isMcpServerRecord(value) {
|
|
|
8370
8395
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8371
8396
|
}
|
|
8372
8397
|
function bold(s) {
|
|
8373
|
-
return
|
|
8398
|
+
return s;
|
|
8374
8399
|
}
|
|
8375
8400
|
function dim(s) {
|
|
8376
|
-
return
|
|
8401
|
+
return s;
|
|
8377
8402
|
}
|
|
8378
8403
|
function green(s) {
|
|
8379
|
-
return
|
|
8404
|
+
return s;
|
|
8380
8405
|
}
|
|
8381
8406
|
function yellow(s) {
|
|
8382
|
-
return
|
|
8407
|
+
return s;
|
|
8383
8408
|
}
|
|
8384
8409
|
function red(s) {
|
|
8385
|
-
return
|
|
8410
|
+
return s;
|
|
8386
8411
|
}
|
|
8387
8412
|
function badge(state) {
|
|
8388
8413
|
switch (state) {
|
|
8389
8414
|
case "connected":
|
|
8390
|
-
return
|
|
8415
|
+
return "\u25CF connected";
|
|
8391
8416
|
case "connecting":
|
|
8392
|
-
return
|
|
8417
|
+
return "\u25D0 connecting";
|
|
8393
8418
|
case "reconnecting":
|
|
8394
|
-
return
|
|
8419
|
+
return "\u25D1 reconnecting";
|
|
8395
8420
|
case "disconnected":
|
|
8396
|
-
return
|
|
8421
|
+
return "\u25CB disconnected";
|
|
8397
8422
|
case "failed":
|
|
8398
|
-
return
|
|
8423
|
+
return "\u2717 failed";
|
|
8399
8424
|
default:
|
|
8400
|
-
return
|
|
8425
|
+
return state;
|
|
8401
8426
|
}
|
|
8402
8427
|
}
|
|
8403
8428
|
|
|
@@ -8462,13 +8487,19 @@ function createMcpUseTool(opts) {
|
|
|
8462
8487
|
const servers = registry.describe();
|
|
8463
8488
|
const serverInfo = servers.find((s) => s.name === serverName);
|
|
8464
8489
|
if (!serverInfo) {
|
|
8465
|
-
|
|
8490
|
+
throw new Error(
|
|
8491
|
+
`Server "${serverName}" not found. Available: ${servers.map((s) => s.name).join(", ") || "none"}.`
|
|
8492
|
+
);
|
|
8466
8493
|
}
|
|
8467
8494
|
if (serverInfo.state !== "connected") {
|
|
8468
|
-
|
|
8495
|
+
throw new Error(
|
|
8496
|
+
`Server "${serverName}" is not connected (state: ${serverInfo.state}). Use \`mcp_control({ action: "enable", server: "${serverName}" })\` first.`
|
|
8497
|
+
);
|
|
8469
8498
|
}
|
|
8470
|
-
|
|
8471
|
-
|
|
8499
|
+
const alreadyActive = registry.isActivated?.(serverName) === true;
|
|
8500
|
+
const didActivate = !alreadyActive && Boolean(registry.activateServer);
|
|
8501
|
+
if (didActivate) {
|
|
8502
|
+
registry.activateServer?.(serverName);
|
|
8472
8503
|
}
|
|
8473
8504
|
try {
|
|
8474
8505
|
const qualifiedName = mcpQualifiedToolName(serverName, toolName);
|
|
@@ -8476,7 +8507,7 @@ function createMcpUseTool(opts) {
|
|
|
8476
8507
|
if (!mcpTool) {
|
|
8477
8508
|
const allTools = toolRegistry.list().filter((t) => t.name.startsWith(mcpServerToolPrefix(serverName))).map((t) => t.name.replace(mcpServerToolPrefix(serverName), ""));
|
|
8478
8509
|
const hint = allTools.length > 0 ? `Available tools on "${serverName}": ${allTools.join(", ")}.` : `No tools found on "${serverName}". The server may not have published any tools.`;
|
|
8479
|
-
|
|
8510
|
+
throw new Error(`Tool "${toolName}" not found on server "${serverName}". ${hint}`);
|
|
8480
8511
|
}
|
|
8481
8512
|
const governedExecute = ctx.meta[GOVERNED_TOOL_EXECUTOR_META_KEY];
|
|
8482
8513
|
if (typeof governedExecute !== "function") {
|
|
@@ -8486,7 +8517,7 @@ function createMcpUseTool(opts) {
|
|
|
8486
8517
|
if (!result.success) throw new Error(result.error ?? "MCP tool execution failed");
|
|
8487
8518
|
return result.result;
|
|
8488
8519
|
} finally {
|
|
8489
|
-
if (registry.deactivateServer) {
|
|
8520
|
+
if (didActivate && registry.deactivateServer) {
|
|
8490
8521
|
registry.deactivateServer(serverName);
|
|
8491
8522
|
}
|
|
8492
8523
|
}
|
|
@@ -8781,6 +8812,7 @@ function asTextBlocks(system) {
|
|
|
8781
8812
|
|
|
8782
8813
|
// src/tools/one-shot-llm-tool.ts
|
|
8783
8814
|
var ONE_SHOT_LLM_TOOL_NAME = "llm";
|
|
8815
|
+
var MAX_TIMEOUT_MS = 12e4;
|
|
8784
8816
|
var INPUT_SCHEMA2 = {
|
|
8785
8817
|
type: "object",
|
|
8786
8818
|
properties: {
|
|
@@ -8857,9 +8889,10 @@ var INPUT_SCHEMA2 = {
|
|
|
8857
8889
|
},
|
|
8858
8890
|
timeoutMs: {
|
|
8859
8891
|
type: "number",
|
|
8860
|
-
description:
|
|
8892
|
+
description: `Hard timeout in ms (default 30s, clamped to a maximum of ${MAX_TIMEOUT_MS}).`
|
|
8861
8893
|
}
|
|
8862
|
-
}
|
|
8894
|
+
},
|
|
8895
|
+
additionalProperties: false
|
|
8863
8896
|
};
|
|
8864
8897
|
function createOneShotLLMTool(opts) {
|
|
8865
8898
|
const orchestrator = new OneShotOrchestrator({
|
|
@@ -8867,6 +8900,7 @@ function createOneShotLLMTool(opts) {
|
|
|
8867
8900
|
getConfig: opts.getConfig,
|
|
8868
8901
|
fallbackProfileManager: opts.fallbackProfileManager,
|
|
8869
8902
|
modelRouter: opts.modelRouter,
|
|
8903
|
+
statusTracker: opts.statusTracker,
|
|
8870
8904
|
logger: opts.logger,
|
|
8871
8905
|
wrapProviderCall: opts.wrapProviderCall
|
|
8872
8906
|
});
|
|
@@ -8875,8 +8909,23 @@ function createOneShotLLMTool(opts) {
|
|
|
8875
8909
|
description: "Make a one-shot LLM call with a system prompt and user input. Supports provider selection, model routing by role, fallback chains, and timeout. Returns the response text, model info, token usage, and whether a fallback was used. Use this for summarization, classification, extraction, and any single-turn LLM task.",
|
|
8876
8910
|
usageHint: "Provide `system` for the instruction and `userPrompt` for the input. Either set `model`+`providerId`, or have defaults configured on the tool. Set `fallbackModels` for resilience. Check `error` on the result for failure details.",
|
|
8877
8911
|
inputSchema: INPUT_SCHEMA2,
|
|
8912
|
+
// Metadata mirrors council-tool.ts — both are read-only meta tools that
|
|
8913
|
+
// spend tokens but never touch the workspace.
|
|
8914
|
+
category: "meta",
|
|
8878
8915
|
permission: "auto",
|
|
8879
8916
|
mutating: false,
|
|
8917
|
+
riskTier: "safe",
|
|
8918
|
+
maxOutputBytes: 262144,
|
|
8919
|
+
validate(input) {
|
|
8920
|
+
const hasPrompt = typeof input.userPrompt === "string" && input.userPrompt.trim().length > 0;
|
|
8921
|
+
const hasMessages = Array.isArray(input.messages) && input.messages.length > 0;
|
|
8922
|
+
if (!hasPrompt && !hasMessages) {
|
|
8923
|
+
return [
|
|
8924
|
+
"Provide `userPrompt` (a single user turn) or `messages` (a conversation array) \u2014 without either the llm tool has nothing to send to the model."
|
|
8925
|
+
];
|
|
8926
|
+
}
|
|
8927
|
+
return [];
|
|
8928
|
+
},
|
|
8880
8929
|
async execute(input, _ctx, { signal }) {
|
|
8881
8930
|
if (!input.model && !input.providerId && !opts.defaultModel && !opts.defaultProvider) {
|
|
8882
8931
|
return {
|
|
@@ -8893,7 +8942,11 @@ function createOneShotLLMTool(opts) {
|
|
|
8893
8942
|
...input,
|
|
8894
8943
|
signal: input.signal ? AbortSignal.any([input.signal, signal]) : signal,
|
|
8895
8944
|
model: input.model ?? opts.defaultModel,
|
|
8896
|
-
providerId: input.providerId ?? opts.defaultProvider
|
|
8945
|
+
providerId: input.providerId ?? opts.defaultProvider,
|
|
8946
|
+
// Clamp runaway timeouts (documented on the schema). Non-positive
|
|
8947
|
+
// values fall back to the orchestrator default rather than making the
|
|
8948
|
+
// call instantly un-completable.
|
|
8949
|
+
...typeof input.timeoutMs === "number" && input.timeoutMs > 0 ? { timeoutMs: Math.min(input.timeoutMs, MAX_TIMEOUT_MS) } : { timeoutMs: void 0 }
|
|
8897
8950
|
};
|
|
8898
8951
|
return orchestrator.call(effectiveInput);
|
|
8899
8952
|
}
|
|
@@ -17,6 +17,12 @@ export interface CreateOneShotLLMToolOptions {
|
|
|
17
17
|
/** Shared live FallbackProfileManager — required. */
|
|
18
18
|
fallbackProfileManager: OneShotOrchestratorOptions['fallbackProfileManager'];
|
|
19
19
|
modelRouter?: OneShotOrchestratorOptions['modelRouter'];
|
|
20
|
+
/**
|
|
21
|
+
* Shared provider/model status tracker. Without it the orchestrator never
|
|
22
|
+
* records failures/successes for `llm` calls, so provider health stays
|
|
23
|
+
* blind to this tool's traffic and blocked entries are not skipped.
|
|
24
|
+
*/
|
|
25
|
+
statusTracker?: OneShotOrchestratorOptions['statusTracker'];
|
|
20
26
|
logger?: OneShotOrchestratorOptions['logger'];
|
|
21
27
|
wrapProviderCall?: OneShotOrchestratorOptions['wrapProviderCall'];
|
|
22
28
|
/**
|
package/dist/types/blocks.d.ts
CHANGED
|
@@ -36,6 +36,16 @@ export interface ToolResultBlock {
|
|
|
36
36
|
name?: string | undefined;
|
|
37
37
|
content: string;
|
|
38
38
|
is_error?: boolean | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Provider cache boundary. Set at request-composition time on a CLONE of the
|
|
41
|
+
* trailing durable block (never on the stored message) so the conversation
|
|
42
|
+
* prefix becomes an incrementally extendable cache entry. Wires that use
|
|
43
|
+
* explicit markers (Anthropic) emit it; every other wire rebuilds
|
|
44
|
+
* tool_result content explicitly and drops it.
|
|
45
|
+
*/
|
|
46
|
+
cache_control?: {
|
|
47
|
+
type: 'ephemeral' | undefined;
|
|
48
|
+
};
|
|
39
49
|
/**
|
|
40
50
|
* Structured tool error information. Present on error results produced
|
|
41
51
|
* by the unified tool error taxonomy (tool-error-taxonomy.ts). Consumed
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* the TUI runtime can all reference the same string union without
|
|
6
6
|
* importing the TUI package (which would invert the dependency direction).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* This array is the CANONICAL list. The CLI `/theme` command and the boot
|
|
9
|
+
* theme adapter both derive their valid-id sets from it, so adding an entry
|
|
10
|
+
* here needs exactly ONE follow-up: a matching palette + `THEME_OPTIONS` row
|
|
11
|
+
* in `packages/tui/src/theme.ts`. Both are compile-enforced — `themePresets`
|
|
12
|
+
* is typed `Record<ThemePresetId, Theme>` (no cast) and the CLI's `THEME_META`
|
|
13
|
+
* is a total record — so a missing preset fails `tsc`, not the runtime.
|
|
11
14
|
*/
|
|
12
|
-
export declare const THEME_PRESET_IDS: readonly ['catppuccin', 'tokyo-night', 'nord', 'cyberpunk', 'dracula', 'gruvbox-dark', 'solarized-dark', 'one-dark', 'monokai', 'rose-pine', 'kanagawa', 'ayu-dark', 'everforest', 'night-owl', 'synthwave'];
|
|
15
|
+
export declare const THEME_PRESET_IDS: readonly ['catppuccin', 'tokyo-night', 'nord', 'cyberpunk', 'dracula', 'gruvbox-dark', 'solarized-dark', 'one-dark', 'monokai', 'rose-pine', 'kanagawa', 'ayu-dark', 'everforest', 'night-owl', 'synthwave', 'github-dark', 'material-ocean', 'nightfox', 'oxocarbon', 'catppuccin-macchiato', 'catppuccin-frappe', 'gruvbox-material', 'tokyo-night-storm', 'rose-pine-moon', 'zenburn', 'palenight', 'horizon', 'sonokai', 'edge-dark', 'moonfly', 'melange', 'poimandres', 'vitesse-dark', 'aura', 'dark-plus'];
|
|
13
16
|
export type ThemePresetId = (typeof THEME_PRESET_IDS)[number];
|
|
14
17
|
//# sourceMappingURL=ui.d.ts.map
|