@wrongstack/core 0.9.4 → 0.9.19
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/{agent-subagent-runner-DaF_EgRG.d.ts → agent-subagent-runner-C4qt9e5Y.d.ts} +1 -1
- package/dist/{config-SkMIDN9L.d.ts → config-CWva0qoL.d.ts} +4 -0
- package/dist/coordination/index.d.ts +8 -7
- package/dist/coordination/index.js +672 -46
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +12 -11
- package/dist/defaults/index.js +780 -23
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +7 -6
- package/dist/execution/index.js +1 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +4 -3
- package/dist/{index-CP8638Wm.d.ts → index-aizK8olO.d.ts} +3 -2
- package/dist/{index-Bsha5K4D.d.ts → index-p95HQ22A.d.ts} +3 -2
- package/dist/index.d.ts +22 -16
- package/dist/index.js +861 -35
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/kernel/index.d.ts +4 -3
- package/dist/{mcp-servers-BouUWYW6.d.ts → mcp-servers-BkVEqkRe.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DTXF2aAl.d.ts → multi-agent-coordinator-bRaI_aD1.d.ts} +1 -1
- package/dist/{null-fleet-bus-Chrc_3Pp.d.ts → null-fleet-bus-DKM3Iy9d.d.ts} +183 -3
- package/dist/{secret-scrubber-DttNiGYA.d.ts → permission-bPuzAy4x.d.ts} +1 -6
- package/dist/{permission-policy-BpCGYBud.d.ts → permission-policy-BUQSutpl.d.ts} +8 -1
- package/dist/{plan-templates-envSmNlZ.d.ts → plan-templates-fkQTyz3U.d.ts} +25 -1
- package/dist/sdd/index.d.ts +5 -4
- package/dist/sdd/index.js +1 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/secret-scrubber-3MHDDAtm.d.ts +6 -0
- package/dist/{secret-scrubber-QSeI0ADi.d.ts → secret-scrubber-7rSC_emZ.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -3
- package/dist/security/index.js +37 -6
- package/dist/security/index.js.map +1 -1
- package/dist/storage/index.d.ts +3 -2
- package/dist/storage/index.js +88 -5
- package/dist/storage/index.js.map +1 -1
- package/dist/{tool-executor-CsktM3h9.d.ts → tool-executor-Boo3dekH.d.ts} +1 -1
- package/dist/types/index.d.ts +6 -5
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +12 -1
- package/dist/utils/index.js +85 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/api-design/SKILL.md +139 -0
- package/skills/audit-log/SKILL.md +87 -14
- package/skills/bug-hunter/SKILL.md +42 -19
- package/skills/docker-deploy/SKILL.md +155 -0
- package/skills/git-flow/SKILL.md +53 -1
- package/skills/multi-agent/SKILL.md +42 -0
- package/skills/node-modern/SKILL.md +57 -1
- package/skills/observability/SKILL.md +134 -0
- package/skills/prompt-engineering/SKILL.md +46 -19
- package/skills/react-modern/SKILL.md +92 -1
- package/skills/refactor-planner/SKILL.md +49 -1
- package/skills/sdd/SKILL.md +12 -1
- package/skills/security-scanner/SKILL.md +46 -1
- package/skills/skill-creator/SKILL.md +49 -52
- package/skills/testing/SKILL.md +170 -0
- package/skills/typescript-strict/SKILL.md +98 -1
package/dist/index.js
CHANGED
|
@@ -4124,14 +4124,100 @@ function deepEqual(a, b) {
|
|
|
4124
4124
|
return false;
|
|
4125
4125
|
}
|
|
4126
4126
|
|
|
4127
|
+
// src/utils/json-repair.ts
|
|
4128
|
+
function completePartialObject(s) {
|
|
4129
|
+
let result = s;
|
|
4130
|
+
const trimmed = result.trim();
|
|
4131
|
+
if (!trimmed.startsWith("{")) return s;
|
|
4132
|
+
for (let pass = 0; pass < 3; pass++) {
|
|
4133
|
+
let braceDepth = 0;
|
|
4134
|
+
let inString2 = false;
|
|
4135
|
+
let escaped2 = false;
|
|
4136
|
+
let foundClose = false;
|
|
4137
|
+
for (const ch of result) {
|
|
4138
|
+
if (escaped2) {
|
|
4139
|
+
escaped2 = false;
|
|
4140
|
+
continue;
|
|
4141
|
+
}
|
|
4142
|
+
if (ch === "\\") {
|
|
4143
|
+
escaped2 = true;
|
|
4144
|
+
continue;
|
|
4145
|
+
}
|
|
4146
|
+
if (ch === '"') {
|
|
4147
|
+
inString2 = !inString2;
|
|
4148
|
+
continue;
|
|
4149
|
+
}
|
|
4150
|
+
if (inString2) continue;
|
|
4151
|
+
if (ch === "{") {
|
|
4152
|
+
braceDepth++;
|
|
4153
|
+
foundClose = false;
|
|
4154
|
+
} else if (ch === "}") {
|
|
4155
|
+
braceDepth--;
|
|
4156
|
+
if (braceDepth === 0) foundClose = true;
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
if (foundClose || braceDepth <= 0) break;
|
|
4160
|
+
result += "}".repeat(braceDepth);
|
|
4161
|
+
}
|
|
4162
|
+
if (tryParse(result).ok) return result;
|
|
4163
|
+
let inString = false;
|
|
4164
|
+
let escaped = false;
|
|
4165
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
4166
|
+
const ch = result[i];
|
|
4167
|
+
if (escaped) {
|
|
4168
|
+
escaped = false;
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4171
|
+
if (ch === "\\") {
|
|
4172
|
+
escaped = true;
|
|
4173
|
+
continue;
|
|
4174
|
+
}
|
|
4175
|
+
if (ch === '"') {
|
|
4176
|
+
let nextNonWs;
|
|
4177
|
+
for (let j = i + 1; j < result.length; j++) {
|
|
4178
|
+
const nc = result[j];
|
|
4179
|
+
if (nc === " " || nc === " " || nc === "\n" || nc === "\r") continue;
|
|
4180
|
+
nextNonWs = result[j];
|
|
4181
|
+
break;
|
|
4182
|
+
}
|
|
4183
|
+
if (nextNonWs === ":") {
|
|
4184
|
+
continue;
|
|
4185
|
+
} else {
|
|
4186
|
+
inString = !inString;
|
|
4187
|
+
continue;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
if (inString) {
|
|
4192
|
+
result = result.trimEnd();
|
|
4193
|
+
if (result.endsWith("\\")) result = result.slice(0, -1);
|
|
4194
|
+
let depth = 0;
|
|
4195
|
+
for (const ch of result) {
|
|
4196
|
+
if (ch === "{") depth++;
|
|
4197
|
+
else if (ch === "}") depth = Math.max(0, depth - 1);
|
|
4198
|
+
}
|
|
4199
|
+
result += '"' + "}".repeat(Math.max(1, depth));
|
|
4200
|
+
}
|
|
4201
|
+
return result;
|
|
4202
|
+
}
|
|
4203
|
+
function tryParse(s) {
|
|
4204
|
+
try {
|
|
4205
|
+
return { ok: true, value: JSON.parse(s) };
|
|
4206
|
+
} catch {
|
|
4207
|
+
return { ok: false };
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4127
4211
|
// src/storage/session-store.ts
|
|
4128
4212
|
init_atomic_write();
|
|
4129
4213
|
var DefaultSessionStore = class {
|
|
4130
4214
|
dir;
|
|
4131
4215
|
events;
|
|
4216
|
+
secretScrubber;
|
|
4132
4217
|
constructor(opts) {
|
|
4133
4218
|
this.dir = opts.dir;
|
|
4134
4219
|
this.events = opts.events;
|
|
4220
|
+
this.secretScrubber = opts.secretScrubber;
|
|
4135
4221
|
}
|
|
4136
4222
|
/** Join session ID to its absolute path within the store directory. */
|
|
4137
4223
|
sessionPath(id, ext) {
|
|
@@ -4156,7 +4242,11 @@ var DefaultSessionStore = class {
|
|
|
4156
4242
|
);
|
|
4157
4243
|
}
|
|
4158
4244
|
try {
|
|
4159
|
-
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
4245
|
+
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
4246
|
+
dir: shardDir,
|
|
4247
|
+
filePath: file,
|
|
4248
|
+
secretScrubber: this.secretScrubber
|
|
4249
|
+
});
|
|
4160
4250
|
} catch (err) {
|
|
4161
4251
|
await handle.close().catch(() => {
|
|
4162
4252
|
});
|
|
@@ -4186,7 +4276,7 @@ var DefaultSessionStore = class {
|
|
|
4186
4276
|
provider: data.metadata.provider
|
|
4187
4277
|
},
|
|
4188
4278
|
this.events,
|
|
4189
|
-
{ resumed: true, dir: this.dir, filePath: file }
|
|
4279
|
+
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
|
|
4190
4280
|
);
|
|
4191
4281
|
return { writer, data };
|
|
4192
4282
|
} catch (err) {
|
|
@@ -4392,6 +4482,7 @@ var FileSessionWriter = class {
|
|
|
4392
4482
|
this.resumed = opts.resumed ?? false;
|
|
4393
4483
|
this.manifestFile = opts.dir ? path6.join(opts.dir, `${id}.summary.json`) : "";
|
|
4394
4484
|
this.filePath = opts.filePath ?? "";
|
|
4485
|
+
this.secretScrubber = opts.secretScrubber;
|
|
4395
4486
|
this.summary = {
|
|
4396
4487
|
id,
|
|
4397
4488
|
title: "(empty session)",
|
|
@@ -4420,6 +4511,29 @@ var FileSessionWriter = class {
|
|
|
4420
4511
|
resumed;
|
|
4421
4512
|
appendFailCount = 0;
|
|
4422
4513
|
lastAppendWarnAt = 0;
|
|
4514
|
+
secretScrubber;
|
|
4515
|
+
/**
|
|
4516
|
+
* Scrub secrets out of conversation-turn events before they are observed
|
|
4517
|
+
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
4518
|
+
* `user_input` / `llm_response` carry free-form user/model text; other event
|
|
4519
|
+
* types either have no secret-bearing content or are already scrubbed
|
|
4520
|
+
* upstream (tool results). Returns the event unchanged when no scrubber is
|
|
4521
|
+
* configured.
|
|
4522
|
+
*/
|
|
4523
|
+
scrubEvent(event) {
|
|
4524
|
+
const s = this.secretScrubber;
|
|
4525
|
+
if (!s) return event;
|
|
4526
|
+
if (event.type === "user_input") {
|
|
4527
|
+
return {
|
|
4528
|
+
...event,
|
|
4529
|
+
content: typeof event.content === "string" ? s.scrub(event.content) : s.scrubObject(event.content)
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
if (event.type === "llm_response") {
|
|
4533
|
+
return { ...event, content: s.scrubObject(event.content) };
|
|
4534
|
+
}
|
|
4535
|
+
return event;
|
|
4536
|
+
}
|
|
4423
4537
|
promptIndex = 0;
|
|
4424
4538
|
pendingFileSnapshots = [];
|
|
4425
4539
|
/** Tracks open tool_use IDs during the current run to serialize on close for resume. */
|
|
@@ -4452,9 +4566,10 @@ var FileSessionWriter = class {
|
|
|
4452
4566
|
this.initDone = true;
|
|
4453
4567
|
await this.writeSessionStartLazy();
|
|
4454
4568
|
}
|
|
4455
|
-
this.
|
|
4569
|
+
const scrubbed = this.scrubEvent(event);
|
|
4570
|
+
this.observeForSummary(scrubbed);
|
|
4456
4571
|
try {
|
|
4457
|
-
await this.handle.appendFile(`${JSON.stringify(
|
|
4572
|
+
await this.handle.appendFile(`${JSON.stringify(scrubbed)}
|
|
4458
4573
|
`, "utf8");
|
|
4459
4574
|
} catch (err) {
|
|
4460
4575
|
this.appendFailCount++;
|
|
@@ -5077,6 +5192,7 @@ function isSecretField2(name) {
|
|
|
5077
5192
|
}
|
|
5078
5193
|
|
|
5079
5194
|
// src/storage/config-loader.ts
|
|
5195
|
+
init_atomic_write();
|
|
5080
5196
|
var BEHAVIOR_DEFAULTS = {
|
|
5081
5197
|
version: 1,
|
|
5082
5198
|
context: {
|
|
@@ -5237,6 +5353,40 @@ var DefaultConfigLoader = class {
|
|
|
5237
5353
|
}
|
|
5238
5354
|
return Object.freeze(cfg);
|
|
5239
5355
|
}
|
|
5356
|
+
/**
|
|
5357
|
+
* Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
|
|
5358
|
+
* by the vault (if provided). The file is isolated from the main config
|
|
5359
|
+
* hierarchy to prevent accidental commits.
|
|
5360
|
+
*/
|
|
5361
|
+
async persistSyncConfig(cfg) {
|
|
5362
|
+
let toWrite = { ...cfg };
|
|
5363
|
+
if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
|
|
5364
|
+
toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
|
|
5365
|
+
}
|
|
5366
|
+
await atomicWrite(this.paths.syncConfig, JSON.stringify(toWrite, null, 2), { mode: 384 });
|
|
5367
|
+
}
|
|
5368
|
+
/**
|
|
5369
|
+
* Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
|
|
5370
|
+
* the token if a vault is available. Returns null if the file doesn't exist.
|
|
5371
|
+
* This is separate from main config loading because sync.json is intentionally
|
|
5372
|
+
* isolated — it should never be part of project-local or env-driven config.
|
|
5373
|
+
*/
|
|
5374
|
+
async loadSyncConfig() {
|
|
5375
|
+
try {
|
|
5376
|
+
const raw = await fsp2.readFile(this.paths.syncConfig, "utf8");
|
|
5377
|
+
const parsed = safeParse(raw);
|
|
5378
|
+
if (!parsed.ok || !parsed.value) return null;
|
|
5379
|
+
if (this.vault) {
|
|
5380
|
+
const decrypted = decryptConfigSecrets2({ sync: parsed.value }, this.vault);
|
|
5381
|
+
return decrypted.sync ?? null;
|
|
5382
|
+
}
|
|
5383
|
+
return parsed.value;
|
|
5384
|
+
} catch (err) {
|
|
5385
|
+
if (err.code === "ENOENT") return null;
|
|
5386
|
+
console.warn("[config] Failed to load sync config:", err);
|
|
5387
|
+
return null;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5240
5390
|
async readJson(file) {
|
|
5241
5391
|
let raw;
|
|
5242
5392
|
try {
|
|
@@ -6160,7 +6310,11 @@ var DefaultPermissionPolicy = class {
|
|
|
6160
6310
|
return { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
|
|
6161
6311
|
}
|
|
6162
6312
|
if (this.sessionAllowed.has(subjectKey)) {
|
|
6163
|
-
return {
|
|
6313
|
+
return {
|
|
6314
|
+
permission: "auto",
|
|
6315
|
+
source: "trust",
|
|
6316
|
+
reason: "session soft allow (user pressed yes)"
|
|
6317
|
+
};
|
|
6164
6318
|
}
|
|
6165
6319
|
if (entry?.deny && subject && matchAny(entry.deny, subject)) {
|
|
6166
6320
|
return { permission: "deny", source: "deny", reason: "matched deny pattern" };
|
|
@@ -6180,7 +6334,11 @@ var DefaultPermissionPolicy = class {
|
|
|
6180
6334
|
const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
6181
6335
|
if (decision === "always") {
|
|
6182
6336
|
await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
|
|
6183
|
-
return {
|
|
6337
|
+
return {
|
|
6338
|
+
permission: "auto",
|
|
6339
|
+
source: "user",
|
|
6340
|
+
reason: "destructive yolo always-allowed"
|
|
6341
|
+
};
|
|
6184
6342
|
}
|
|
6185
6343
|
if (decision === "deny") {
|
|
6186
6344
|
await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
|
|
@@ -6188,13 +6346,22 @@ var DefaultPermissionPolicy = class {
|
|
|
6188
6346
|
}
|
|
6189
6347
|
return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
|
|
6190
6348
|
}
|
|
6191
|
-
return {
|
|
6349
|
+
return {
|
|
6350
|
+
permission: "confirm",
|
|
6351
|
+
source: "yolo_destructive",
|
|
6352
|
+
riskTier: "destructive",
|
|
6353
|
+
reason: "destructive tool needs explicit approval even in yolo mode"
|
|
6354
|
+
};
|
|
6192
6355
|
}
|
|
6193
6356
|
return { permission: "auto", source: "yolo" };
|
|
6194
6357
|
}
|
|
6195
6358
|
if (tool.name === "write" && subject) {
|
|
6196
6359
|
if (ctx.hasRead(subject)) {
|
|
6197
|
-
return {
|
|
6360
|
+
return {
|
|
6361
|
+
permission: "auto",
|
|
6362
|
+
source: "context",
|
|
6363
|
+
reason: "file already read in this session"
|
|
6364
|
+
};
|
|
6198
6365
|
}
|
|
6199
6366
|
}
|
|
6200
6367
|
if (tool.permission === "auto" && !tool.mutating) {
|
|
@@ -6302,6 +6469,10 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
6302
6469
|
// arbitrary shell — use exec for constrained shell
|
|
6303
6470
|
"write",
|
|
6304
6471
|
// arbitrary file write
|
|
6472
|
+
"edit",
|
|
6473
|
+
// arbitrary in-project file modification (equivalent to write)
|
|
6474
|
+
"replace",
|
|
6475
|
+
// arbitrary multi-file find/replace (equivalent to write)
|
|
6305
6476
|
"scaffold",
|
|
6306
6477
|
// arbitrary file generation outside project root
|
|
6307
6478
|
"patch",
|
|
@@ -6311,12 +6482,22 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
6311
6482
|
"exec"
|
|
6312
6483
|
// restricted shell but with arbitrary command args
|
|
6313
6484
|
]);
|
|
6485
|
+
/**
|
|
6486
|
+
* Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
|
|
6487
|
+
* unknown capability — they may wrap a shell or filesystem. They are
|
|
6488
|
+
* fail-closed here: not auto-approved for subagents by default, so the
|
|
6489
|
+
* leader must allow them explicitly per-spawn.
|
|
6490
|
+
*/
|
|
6491
|
+
static isMcpTool(name) {
|
|
6492
|
+
return name.startsWith("mcp__");
|
|
6493
|
+
}
|
|
6314
6494
|
async evaluate(tool) {
|
|
6315
|
-
|
|
6495
|
+
const blocked = _AutoApprovePermissionPolicy.DENY.has(tool.name) || _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
6496
|
+
if (tool.permission === "deny" || blocked) {
|
|
6316
6497
|
return {
|
|
6317
6498
|
permission: "deny",
|
|
6318
6499
|
source: "subagent_guard",
|
|
6319
|
-
reason:
|
|
6500
|
+
reason: blocked ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny"
|
|
6320
6501
|
};
|
|
6321
6502
|
}
|
|
6322
6503
|
return { permission: "auto", source: "yolo" };
|
|
@@ -6561,7 +6742,12 @@ function safeJsonOrRaw(s) {
|
|
|
6561
6742
|
try {
|
|
6562
6743
|
return JSON.parse(s);
|
|
6563
6744
|
} catch {
|
|
6564
|
-
|
|
6745
|
+
const repaired = completePartialObject(s);
|
|
6746
|
+
try {
|
|
6747
|
+
return JSON.parse(repaired);
|
|
6748
|
+
} catch {
|
|
6749
|
+
return { _raw: repaired };
|
|
6750
|
+
}
|
|
6565
6751
|
}
|
|
6566
6752
|
}
|
|
6567
6753
|
function handleToolUseStop(state, ev) {
|
|
@@ -11283,11 +11469,46 @@ Working rules:
|
|
|
11283
11469
|
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
11284
11470
|
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
11285
11471
|
};
|
|
11472
|
+
var CRITIC_AGENT = {
|
|
11473
|
+
id: "critic",
|
|
11474
|
+
name: "Critic",
|
|
11475
|
+
role: "critic",
|
|
11476
|
+
prompt: `You are the Critic agent. Your job is to evaluate code quality,
|
|
11477
|
+
architectural decisions, and proposed changes against project conventions,
|
|
11478
|
+
engineering standards, and known quality gates. You do not write code \u2014
|
|
11479
|
+
you judge it.
|
|
11480
|
+
|
|
11481
|
+
Scope:
|
|
11482
|
+
- Evaluate bug severity and fix quality from Bug Hunter reports
|
|
11483
|
+
- Score refactoring plans from Refactor Planner (risk, completeness, trade-offs)
|
|
11484
|
+
- Flag gaps in test coverage, error handling, and edge case coverage
|
|
11485
|
+
- Assess whether a proposed change aligns with existing project patterns
|
|
11486
|
+
- Detect over-engineering or under-engineering relative to the problem scope
|
|
11487
|
+
|
|
11488
|
+
Input format you accept:
|
|
11489
|
+
{ "task": "evaluate | score | review", "subject": "bug_report | refactor_plan | diff", "focus": "correctness | maintainability | risk | all" }
|
|
11490
|
+
|
|
11491
|
+
Output: Markdown critic report:
|
|
11492
|
+
- ## Overall Score (0-10 with rationale)
|
|
11493
|
+
- ## Strengths (what's solid)
|
|
11494
|
+
- ## Weaknesses (what needs work)
|
|
11495
|
+
- ## Specific Concerns (with file:line when applicable)
|
|
11496
|
+
- ## Verdict: **Approve / Needs Revision / Reject**
|
|
11497
|
+
|
|
11498
|
+
Working rules:
|
|
11499
|
+
- Be specific \u2014 "looks fine" is not a review. Cite concrete evidence.
|
|
11500
|
+
- When scoring, explain the delta from a perfect score.
|
|
11501
|
+
- If you have no basis to evaluate a concern, say so rather than speculating.
|
|
11502
|
+
- Prioritise correctness over style; correctness issues block approval.
|
|
11503
|
+
- Score thresholds: \u22657 = Approve, 4-6 = Needs Revision, <4 = Reject`
|
|
11504
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
11505
|
+
};
|
|
11286
11506
|
var FLEET_ROSTER = {
|
|
11287
11507
|
"audit-log": AUDIT_LOG_AGENT,
|
|
11288
11508
|
"bug-hunter": BUG_HUNTER_AGENT,
|
|
11289
11509
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
11290
11510
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
11511
|
+
"critic": CRITIC_AGENT,
|
|
11291
11512
|
...Object.fromEntries(
|
|
11292
11513
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
11293
11514
|
)
|
|
@@ -11297,6 +11518,7 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
11297
11518
|
"bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
11298
11519
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
11299
11520
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
11521
|
+
"critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
11300
11522
|
...Object.fromEntries(
|
|
11301
11523
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
11302
11524
|
)
|
|
@@ -11377,10 +11599,10 @@ FLEET_ROSTER_BUDGETS["gemini-cli"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterati
|
|
|
11377
11599
|
FLEET_ROSTER_BUDGETS["copilot"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 };
|
|
11378
11600
|
FLEET_ROSTER_BUDGETS["openhands"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 };
|
|
11379
11601
|
FLEET_ROSTER_BUDGETS["goose"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 };
|
|
11380
|
-
|
|
11602
|
+
var FLEET_ROSTER_WITHACP = {
|
|
11381
11603
|
...FLEET_ROSTER,
|
|
11382
11604
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
11383
|
-
}
|
|
11605
|
+
};
|
|
11384
11606
|
|
|
11385
11607
|
// src/coordination/multi-agent-coordinator.ts
|
|
11386
11608
|
var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
@@ -12768,6 +12990,106 @@ var FleetUsageAggregator = class {
|
|
|
12768
12990
|
snap.lastEventAt = e.ts;
|
|
12769
12991
|
}
|
|
12770
12992
|
};
|
|
12993
|
+
|
|
12994
|
+
// src/coordination/subagent-nicknames.ts
|
|
12995
|
+
var NICKNAME_POOL = {
|
|
12996
|
+
// Physics & fundamental sciences
|
|
12997
|
+
"einstein": { name: "Einstein", domain: "physics" },
|
|
12998
|
+
"newton": { name: "Newton", domain: "physics" },
|
|
12999
|
+
"feynman": { name: "Feynman", domain: "physics" },
|
|
13000
|
+
"dirac": { name: "Dirac", domain: "physics" },
|
|
13001
|
+
"bohr": { name: "Bohr", domain: "physics" },
|
|
13002
|
+
"planck": { name: "Planck", domain: "physics" },
|
|
13003
|
+
"curie": { name: "Curie", domain: "physics" },
|
|
13004
|
+
"fermi": { name: "Fermi", domain: "physics" },
|
|
13005
|
+
"heisenberg": { name: "Heisenberg", domain: "physics" },
|
|
13006
|
+
"schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
|
|
13007
|
+
// Mathematics
|
|
13008
|
+
"euclid": { name: "Euclid", domain: "math" },
|
|
13009
|
+
"gauss": { name: "Gauss", domain: "math" },
|
|
13010
|
+
"turing": { name: "Turing", domain: "math" },
|
|
13011
|
+
"poincare": { name: "Poincar\xE9", domain: "math" },
|
|
13012
|
+
"riemann": { name: "Riemann", domain: "math" },
|
|
13013
|
+
"hilbert": { name: "Hilbert", domain: "math" },
|
|
13014
|
+
"pythagoras": { name: "Pythagoras", domain: "math" },
|
|
13015
|
+
// Computing & information theory
|
|
13016
|
+
"von-neumann": { name: "Von Neumann", domain: "computing" },
|
|
13017
|
+
"shannon": { name: "Shannon", domain: "computing" },
|
|
13018
|
+
"hopper": { name: "Hopper", domain: "computing" },
|
|
13019
|
+
"backus": { name: "Backus", domain: "computing" },
|
|
13020
|
+
"knuth": { name: "Knuth", domain: "computing" },
|
|
13021
|
+
"torvalds": { name: "Torvalds", domain: "computing" },
|
|
13022
|
+
"stallman": { name: "Stallman", domain: "computing" },
|
|
13023
|
+
"berners-lee": { name: "Berners-Lee", domain: "computing" },
|
|
13024
|
+
"babbage": { name: "Babbage", domain: "computing" },
|
|
13025
|
+
"lovelace": { name: "Lovelace", domain: "computing" },
|
|
13026
|
+
"klein": { name: "Klein", domain: "computing" },
|
|
13027
|
+
// Electronics & electrical engineering
|
|
13028
|
+
"edison": { name: "Edison", domain: "ee" },
|
|
13029
|
+
"tesla": { name: "Tesla", domain: "ee" },
|
|
13030
|
+
"faraday": { name: "Faraday", domain: "ee" },
|
|
13031
|
+
"maxwell": { name: "Maxwell", domain: "ee" },
|
|
13032
|
+
"ohm": { name: "Ohm", domain: "ee" },
|
|
13033
|
+
"bell": { name: "Bell", domain: "ee" },
|
|
13034
|
+
"marconi": { name: "Marconi", domain: "ee" },
|
|
13035
|
+
"lamarr": { name: "Lamarr", domain: "ee" },
|
|
13036
|
+
// General science / multi-disciplinary
|
|
13037
|
+
"darwin": { name: "Darwin", domain: "biology" },
|
|
13038
|
+
"mendel": { name: "Mendel", domain: "biology" },
|
|
13039
|
+
"pasteur": { name: "Pasteur", domain: "biology" },
|
|
13040
|
+
"hawking": { name: "Hawking", domain: "cosmology" },
|
|
13041
|
+
"sagan": { name: "Sagan", domain: "cosmology" },
|
|
13042
|
+
// Chemistry / materials
|
|
13043
|
+
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
13044
|
+
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
13045
|
+
};
|
|
13046
|
+
var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
|
|
13047
|
+
var DOMAIN_PREFERENCES = {
|
|
13048
|
+
"security": ["shannon", "turing", "lamarr", "stallman"],
|
|
13049
|
+
"bug-hunter": ["darwin", "curie", "feynman", "fermi"],
|
|
13050
|
+
"refactor": ["gauss", "hilbert", "euclid", "planck"],
|
|
13051
|
+
"audit-log": ["sagan", "hawking", "poincare", "newton"],
|
|
13052
|
+
"planner": ["hilbert", "gauss", "turing", "euclid"],
|
|
13053
|
+
"researcher": ["sagan", "hawking", "darwin", "pasteur"],
|
|
13054
|
+
"explorer": ["marconi", "bell", "columbus", "polo"],
|
|
13055
|
+
"testing": ["pasteur", "curie", "fermi", "bohr"],
|
|
13056
|
+
"frontend": ["lovelace", "hopper", "babbage", "backus"],
|
|
13057
|
+
"backend": ["torvalds", "stallman", "von-neumann", "backus"],
|
|
13058
|
+
"database": ["turing", "shannon", "backus", "knuth"],
|
|
13059
|
+
"devops": ["tesla", "edison", "faraday", "bell"],
|
|
13060
|
+
"security-scanner": ["shannon", "turing", "lamarr", "stallman"],
|
|
13061
|
+
"refactor-planner": ["gauss", "hilbert", "planck", "newton"],
|
|
13062
|
+
"architect": ["von-neumann", "turing", "gauss", "hilbert"],
|
|
13063
|
+
"critic": ["einstein", "feynman", "dirac", "bohr"],
|
|
13064
|
+
"e2e": ["hopper", "bell", "marconi", "tesla"],
|
|
13065
|
+
"performance": ["knuth", "gauss", "planck", "feynman"],
|
|
13066
|
+
"chaos": ["tesla", "edison", "curie", "fermi"],
|
|
13067
|
+
"cost": ["oh", "bell", "marconi", "tesla"],
|
|
13068
|
+
// default fallback
|
|
13069
|
+
"default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
|
|
13070
|
+
};
|
|
13071
|
+
function assignNickname(role, used) {
|
|
13072
|
+
const preferences = [
|
|
13073
|
+
...DOMAIN_PREFERENCES[role] ?? [],
|
|
13074
|
+
...DOMAIN_PREFERENCES["default"] ?? []
|
|
13075
|
+
];
|
|
13076
|
+
for (const key of preferences) {
|
|
13077
|
+
if (!used.has(key)) {
|
|
13078
|
+
return `${NICKNAME_POOL[key].name} (${formatRole(role)})`;
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
13081
|
+
for (const entry of ALL_NICKNAMES) {
|
|
13082
|
+
const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
|
|
13083
|
+
if (key && !used.has(key)) {
|
|
13084
|
+
return `${entry.name} (${formatRole(role)})`;
|
|
13085
|
+
}
|
|
13086
|
+
}
|
|
13087
|
+
const counter = used.size + 1;
|
|
13088
|
+
return `Scientist #${counter} (${formatRole(role)})`;
|
|
13089
|
+
}
|
|
13090
|
+
function formatRole(role) {
|
|
13091
|
+
return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
13092
|
+
}
|
|
12771
13093
|
function makeSpawnTool(director, roster) {
|
|
12772
13094
|
const inputSchema = {
|
|
12773
13095
|
type: "object",
|
|
@@ -12992,7 +13314,6 @@ function makeFleetSessionTool(director) {
|
|
|
12992
13314
|
type: "object",
|
|
12993
13315
|
properties: {
|
|
12994
13316
|
subagentId: { type: "string", description: "Subagent id to read the transcript of." },
|
|
12995
|
-
/** Number of trailing lines to return (last N JSONL lines). Default: all. */
|
|
12996
13317
|
tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
|
|
12997
13318
|
},
|
|
12998
13319
|
required: ["subagentId"]
|
|
@@ -13028,8 +13349,6 @@ function makeFleetHealthTool(director) {
|
|
|
13028
13349
|
id: s.id,
|
|
13029
13350
|
status: s.status,
|
|
13030
13351
|
lastEventAt: usage?.lastEventAt,
|
|
13031
|
-
// Budget pressure: fraction of each limit consumed if we have it.
|
|
13032
|
-
// BudgetWarning events carry used/limit ratios; surface them here.
|
|
13033
13352
|
budgetPressure: {
|
|
13034
13353
|
iterations: usage?.iterations,
|
|
13035
13354
|
toolCalls: usage?.toolCalls,
|
|
@@ -13041,6 +13360,415 @@ function makeFleetHealthTool(director) {
|
|
|
13041
13360
|
}
|
|
13042
13361
|
};
|
|
13043
13362
|
}
|
|
13363
|
+
function makeCollabDebugTool(director) {
|
|
13364
|
+
return {
|
|
13365
|
+
name: "collab_debug",
|
|
13366
|
+
description: "Start a collaborative debugging session: BugHunter, RefactorPlanner, and Critic run in parallel on the same target files. BugHunter finds bugs and emits bug.found events. RefactorPlanner listens for bug.found and emits refactor.plan events. Critic evaluates both and emits critic.evaluation events. Returns a structured report with overall verdict (approve / needs_revision / reject).",
|
|
13367
|
+
permission: "auto",
|
|
13368
|
+
mutating: false,
|
|
13369
|
+
inputSchema: {
|
|
13370
|
+
type: "object",
|
|
13371
|
+
properties: {
|
|
13372
|
+
targetPaths: {
|
|
13373
|
+
type: "array",
|
|
13374
|
+
items: { type: "string" },
|
|
13375
|
+
description: "File paths / glob patterns to scan for bugs."
|
|
13376
|
+
},
|
|
13377
|
+
timeoutMs: {
|
|
13378
|
+
type: "number",
|
|
13379
|
+
description: "Timeout in ms. Default: 600000 (10 minutes)."
|
|
13380
|
+
}
|
|
13381
|
+
},
|
|
13382
|
+
required: ["targetPaths"]
|
|
13383
|
+
},
|
|
13384
|
+
async execute(input) {
|
|
13385
|
+
const i = input;
|
|
13386
|
+
if (!i.targetPaths?.length) {
|
|
13387
|
+
return { error: "collab_debug: targetPaths is required and must be non-empty." };
|
|
13388
|
+
}
|
|
13389
|
+
const options = {
|
|
13390
|
+
targetPaths: i.targetPaths,
|
|
13391
|
+
timeoutMs: i.timeoutMs
|
|
13392
|
+
};
|
|
13393
|
+
try {
|
|
13394
|
+
const report = await director.spawnCollab(options);
|
|
13395
|
+
return {
|
|
13396
|
+
sessionId: report.sessionId,
|
|
13397
|
+
overallVerdict: report.overallVerdict,
|
|
13398
|
+
bugCount: report.bugs.length,
|
|
13399
|
+
planCount: report.refactorPlans.length,
|
|
13400
|
+
evaluationCount: report.evaluations.length,
|
|
13401
|
+
summary: report.summary,
|
|
13402
|
+
bugs: report.bugs,
|
|
13403
|
+
refactorPlans: report.refactorPlans,
|
|
13404
|
+
evaluations: report.evaluations
|
|
13405
|
+
};
|
|
13406
|
+
} catch (err) {
|
|
13407
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13408
|
+
return { error: "collab_debug failed: " + msg };
|
|
13409
|
+
}
|
|
13410
|
+
}
|
|
13411
|
+
};
|
|
13412
|
+
}
|
|
13413
|
+
function makeFleetEmitTool(director) {
|
|
13414
|
+
return {
|
|
13415
|
+
name: "fleet_emit",
|
|
13416
|
+
description: "Emit a structured event on the FleetBus. Use this when you find a bug (bug.found), complete a refactor plan (refactor.plan), or finish an evaluation (critic.evaluation). Events are routed to other agents in the collab session.",
|
|
13417
|
+
permission: "auto",
|
|
13418
|
+
mutating: false,
|
|
13419
|
+
inputSchema: {
|
|
13420
|
+
type: "object",
|
|
13421
|
+
properties: {
|
|
13422
|
+
type: {
|
|
13423
|
+
type: "string",
|
|
13424
|
+
description: "Event type: bug.found | refactor.plan | critic.evaluation"
|
|
13425
|
+
},
|
|
13426
|
+
payload: {
|
|
13427
|
+
type: "object",
|
|
13428
|
+
description: "Event payload matching the event type."
|
|
13429
|
+
}
|
|
13430
|
+
},
|
|
13431
|
+
required: ["type", "payload"]
|
|
13432
|
+
},
|
|
13433
|
+
async execute(input) {
|
|
13434
|
+
const i = input;
|
|
13435
|
+
director.fleet.emit({
|
|
13436
|
+
subagentId: director.id,
|
|
13437
|
+
ts: Date.now(),
|
|
13438
|
+
type: i.type,
|
|
13439
|
+
payload: i.payload
|
|
13440
|
+
});
|
|
13441
|
+
return { ok: true, event: i.type };
|
|
13442
|
+
}
|
|
13443
|
+
};
|
|
13444
|
+
}
|
|
13445
|
+
var CollabSession = class extends EventEmitter {
|
|
13446
|
+
sessionId;
|
|
13447
|
+
options;
|
|
13448
|
+
snapshot;
|
|
13449
|
+
director;
|
|
13450
|
+
fleetBus;
|
|
13451
|
+
subagentIds = /* @__PURE__ */ new Map();
|
|
13452
|
+
// role → subagentId
|
|
13453
|
+
bugs = /* @__PURE__ */ new Map();
|
|
13454
|
+
plans = /* @__PURE__ */ new Map();
|
|
13455
|
+
evaluations = /* @__PURE__ */ new Map();
|
|
13456
|
+
disposers = new Array();
|
|
13457
|
+
settled = false;
|
|
13458
|
+
timeoutMs;
|
|
13459
|
+
constructor(director, fleetBus, options) {
|
|
13460
|
+
super();
|
|
13461
|
+
this.sessionId = randomUUID();
|
|
13462
|
+
this.options = options;
|
|
13463
|
+
this.director = director;
|
|
13464
|
+
this.fleetBus = fleetBus;
|
|
13465
|
+
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
13466
|
+
if (options.prebuiltSnapshot) {
|
|
13467
|
+
this.snapshot = options.prebuiltSnapshot;
|
|
13468
|
+
} else {
|
|
13469
|
+
this.snapshot = {
|
|
13470
|
+
id: this.sessionId,
|
|
13471
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13472
|
+
files: []
|
|
13473
|
+
};
|
|
13474
|
+
}
|
|
13475
|
+
}
|
|
13476
|
+
/**
|
|
13477
|
+
* Read the target files from disk and populate the snapshot.
|
|
13478
|
+
* Call this after construction if you did not provide a prebuiltSnapshot
|
|
13479
|
+
* and want the session to operate on real file contents.
|
|
13480
|
+
*/
|
|
13481
|
+
async buildSnapshot() {
|
|
13482
|
+
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
13483
|
+
for (const filePath of this.options.targetPaths) {
|
|
13484
|
+
try {
|
|
13485
|
+
const content = await fsp2.readFile(filePath, "utf8");
|
|
13486
|
+
const ext = filePath.split(".").pop() ?? "";
|
|
13487
|
+
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
13488
|
+
this.snapshot.files.push({ path: filePath, content, language });
|
|
13489
|
+
} catch {
|
|
13490
|
+
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
13491
|
+
}
|
|
13492
|
+
}
|
|
13493
|
+
return this.snapshot;
|
|
13494
|
+
}
|
|
13495
|
+
/**
|
|
13496
|
+
* Start the collaborative session: snapshot files, spawn all three agents,
|
|
13497
|
+
* wire up event routing, and wait for completion.
|
|
13498
|
+
*/
|
|
13499
|
+
async start() {
|
|
13500
|
+
if (this.settled) throw new Error("session already settled");
|
|
13501
|
+
this.settled = true;
|
|
13502
|
+
await this.buildSnapshot();
|
|
13503
|
+
this.wireFleetBus();
|
|
13504
|
+
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
13505
|
+
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
13506
|
+
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
13507
|
+
this.spawnAgent("critic", this.buildCriticTask())
|
|
13508
|
+
]);
|
|
13509
|
+
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
13510
|
+
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
13511
|
+
this.subagentIds.set("critic", criticId);
|
|
13512
|
+
const timeout = new Promise((_, reject) => {
|
|
13513
|
+
setTimeout(
|
|
13514
|
+
() => reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`)),
|
|
13515
|
+
this.timeoutMs
|
|
13516
|
+
);
|
|
13517
|
+
});
|
|
13518
|
+
let results;
|
|
13519
|
+
try {
|
|
13520
|
+
results = await Promise.race([
|
|
13521
|
+
Promise.all([
|
|
13522
|
+
this.director.awaitTasks([bugHunterId]),
|
|
13523
|
+
this.director.awaitTasks([refactorPlannerId]),
|
|
13524
|
+
this.director.awaitTasks([criticId])
|
|
13525
|
+
]),
|
|
13526
|
+
timeout
|
|
13527
|
+
]);
|
|
13528
|
+
} catch (err) {
|
|
13529
|
+
this.cleanup();
|
|
13530
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
13531
|
+
this.emit("session.error", error);
|
|
13532
|
+
throw error;
|
|
13533
|
+
}
|
|
13534
|
+
for (const result of results.flat()) {
|
|
13535
|
+
await this.parseAndEmit(result);
|
|
13536
|
+
}
|
|
13537
|
+
const report = this.assembleReport();
|
|
13538
|
+
this.cleanup();
|
|
13539
|
+
this.emit("session.done", report);
|
|
13540
|
+
return report;
|
|
13541
|
+
}
|
|
13542
|
+
/**
|
|
13543
|
+
* Parse a completed agent's final text output and route the structured
|
|
13544
|
+
* findings onto the FleetBus. Agents are instructed to emit one JSON object
|
|
13545
|
+
* per finding / plan / evaluation; we scan the output for balanced JSON
|
|
13546
|
+
* objects (tolerating prose, markdown, and ```json fences around them) and
|
|
13547
|
+
* dispatch each by its discriminating key. The wired FleetBus listeners then
|
|
13548
|
+
* collect them into the bugs / plans / evaluations maps.
|
|
13549
|
+
*
|
|
13550
|
+
* Failed or empty results are ignored, and a single malformed object never
|
|
13551
|
+
* aborts the collation pass — see {@link extractJsonObjects}.
|
|
13552
|
+
*/
|
|
13553
|
+
async parseAndEmit(result) {
|
|
13554
|
+
if (result.status !== "success" || result.result == null) return;
|
|
13555
|
+
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
13556
|
+
for (const obj of this.extractJsonObjects(text)) {
|
|
13557
|
+
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
13558
|
+
if (!type) continue;
|
|
13559
|
+
this.fleetBus.emit({
|
|
13560
|
+
subagentId: result.subagentId,
|
|
13561
|
+
taskId: result.taskId,
|
|
13562
|
+
ts: Date.now(),
|
|
13563
|
+
type,
|
|
13564
|
+
payload: obj
|
|
13565
|
+
});
|
|
13566
|
+
}
|
|
13567
|
+
}
|
|
13568
|
+
/**
|
|
13569
|
+
* Extract every top-level JSON object embedded in free-form agent text.
|
|
13570
|
+
* Scans for balanced braces while respecting string literals and escape
|
|
13571
|
+
* sequences, so prose, markdown headers, and code fences around the JSON
|
|
13572
|
+
* are skipped. Malformed spans are dropped silently rather than thrown.
|
|
13573
|
+
*/
|
|
13574
|
+
extractJsonObjects(text) {
|
|
13575
|
+
const objects = [];
|
|
13576
|
+
let depth = 0;
|
|
13577
|
+
let start = -1;
|
|
13578
|
+
let inString = false;
|
|
13579
|
+
let escaped = false;
|
|
13580
|
+
for (let i = 0; i < text.length; i++) {
|
|
13581
|
+
const ch = text[i];
|
|
13582
|
+
if (inString) {
|
|
13583
|
+
if (escaped) escaped = false;
|
|
13584
|
+
else if (ch === "\\") escaped = true;
|
|
13585
|
+
else if (ch === '"') inString = false;
|
|
13586
|
+
continue;
|
|
13587
|
+
}
|
|
13588
|
+
if (ch === '"') {
|
|
13589
|
+
inString = true;
|
|
13590
|
+
} else if (ch === "{") {
|
|
13591
|
+
if (depth === 0) start = i;
|
|
13592
|
+
depth++;
|
|
13593
|
+
} else if (ch === "}" && depth > 0) {
|
|
13594
|
+
depth--;
|
|
13595
|
+
if (depth === 0 && start >= 0) {
|
|
13596
|
+
const candidate = text.slice(start, i + 1);
|
|
13597
|
+
try {
|
|
13598
|
+
const parsed = JSON.parse(candidate);
|
|
13599
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13600
|
+
objects.push(parsed);
|
|
13601
|
+
}
|
|
13602
|
+
} catch {
|
|
13603
|
+
}
|
|
13604
|
+
start = -1;
|
|
13605
|
+
}
|
|
13606
|
+
}
|
|
13607
|
+
}
|
|
13608
|
+
return objects;
|
|
13609
|
+
}
|
|
13610
|
+
async spawnAgent(role, taskBrief) {
|
|
13611
|
+
const cfg = {
|
|
13612
|
+
id: `${role}-${this.sessionId}`,
|
|
13613
|
+
name: role,
|
|
13614
|
+
role
|
|
13615
|
+
// No fleet_emit tool — agents output structured JSON, the Director parses
|
|
13616
|
+
// results and emits FleetBus events. This keeps the agent simple and the
|
|
13617
|
+
// routing deterministic.
|
|
13618
|
+
};
|
|
13619
|
+
const subagentId = await this.director.spawn(cfg);
|
|
13620
|
+
await this.director.assign({
|
|
13621
|
+
id: randomUUID(),
|
|
13622
|
+
subagentId,
|
|
13623
|
+
description: taskBrief
|
|
13624
|
+
});
|
|
13625
|
+
return subagentId;
|
|
13626
|
+
}
|
|
13627
|
+
buildBugHunterTask() {
|
|
13628
|
+
this.options.targetPaths.join(", ");
|
|
13629
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
13630
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
13631
|
+
${f.content}`).join("\n\n");
|
|
13632
|
+
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
13633
|
+
|
|
13634
|
+
Target files:
|
|
13635
|
+
${fileContents}
|
|
13636
|
+
|
|
13637
|
+
For each bug found, write ONE valid JSON line to the scratchpad with this exact structure:
|
|
13638
|
+
{ "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } }.
|
|
13639
|
+
After scanning all files, write your full markdown bug report to the shared scratchpad at:
|
|
13640
|
+
${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
13641
|
+
}
|
|
13642
|
+
buildRefactorPlannerTask() {
|
|
13643
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
13644
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
13645
|
+
${f.content}`).join("\n\n");
|
|
13646
|
+
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
13647
|
+
|
|
13648
|
+
Target files:
|
|
13649
|
+
${fileContents}
|
|
13650
|
+
|
|
13651
|
+
Listen for bug.found events from BugHunter on the fleet bus. For each bug, create a refactor plan and write one JSON line per plan:
|
|
13652
|
+
{ "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [...], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } }.
|
|
13653
|
+
After planning, write your full markdown plan to:
|
|
13654
|
+
${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
13655
|
+
}
|
|
13656
|
+
buildCriticTask() {
|
|
13657
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
13658
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
13659
|
+
${f.content}`).join("\n\n");
|
|
13660
|
+
return `You are Critic. Evaluate bug findings and refactor plans as they arrive via fleet bus events.
|
|
13661
|
+
|
|
13662
|
+
Target files:
|
|
13663
|
+
${fileContents}
|
|
13664
|
+
|
|
13665
|
+
Subscribe to bug.found events (from BugHunter) and refactor.plan events (from RefactorPlanner). For each subject, write one JSON line per evaluation:
|
|
13666
|
+
{ "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": [...], "weaknesses": [...], "concerns": [...] } }.
|
|
13667
|
+
After all evaluations, write your markdown critic report to:
|
|
13668
|
+
${scratchpad}/critic-report-${this.sessionId}.md`;
|
|
13669
|
+
}
|
|
13670
|
+
wireFleetBus() {
|
|
13671
|
+
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
13672
|
+
const payload = e.payload;
|
|
13673
|
+
if (payload?.finding) {
|
|
13674
|
+
this.bugs.set(payload.finding.id, payload.finding);
|
|
13675
|
+
this.emit("bug.found", payload);
|
|
13676
|
+
}
|
|
13677
|
+
});
|
|
13678
|
+
this.disposers.push(d1);
|
|
13679
|
+
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
13680
|
+
const payload = e.payload;
|
|
13681
|
+
if (payload?.plan) {
|
|
13682
|
+
this.plans.set(payload.plan.id, payload.plan);
|
|
13683
|
+
this.emit("refactor.plan", payload);
|
|
13684
|
+
}
|
|
13685
|
+
});
|
|
13686
|
+
this.disposers.push(d2);
|
|
13687
|
+
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
13688
|
+
const payload = e.payload;
|
|
13689
|
+
if (payload?.evaluation) {
|
|
13690
|
+
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
13691
|
+
this.emit("critic.evaluation", payload);
|
|
13692
|
+
}
|
|
13693
|
+
});
|
|
13694
|
+
this.disposers.push(d3);
|
|
13695
|
+
}
|
|
13696
|
+
assembleReport() {
|
|
13697
|
+
const bugList = Array.from(this.bugs.values());
|
|
13698
|
+
const planList = Array.from(this.plans.values());
|
|
13699
|
+
const evalList = Array.from(this.evaluations.values());
|
|
13700
|
+
const verdictOrder = {
|
|
13701
|
+
approve: 0,
|
|
13702
|
+
needs_revision: 1,
|
|
13703
|
+
reject: 2
|
|
13704
|
+
};
|
|
13705
|
+
const overallVerdict = evalList.reduce(
|
|
13706
|
+
(worst, eval_) => {
|
|
13707
|
+
const w = verdictOrder[worst];
|
|
13708
|
+
const c = verdictOrder[eval_.verdict];
|
|
13709
|
+
return c > w ? eval_.verdict : worst;
|
|
13710
|
+
},
|
|
13711
|
+
"approve"
|
|
13712
|
+
);
|
|
13713
|
+
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict);
|
|
13714
|
+
return {
|
|
13715
|
+
sessionId: this.sessionId,
|
|
13716
|
+
startedAt: this.snapshot.createdAt,
|
|
13717
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13718
|
+
targetPaths: this.options.targetPaths,
|
|
13719
|
+
bugs: bugList,
|
|
13720
|
+
refactorPlans: planList,
|
|
13721
|
+
evaluations: evalList,
|
|
13722
|
+
overallVerdict,
|
|
13723
|
+
summary
|
|
13724
|
+
};
|
|
13725
|
+
}
|
|
13726
|
+
buildMarkdownSummary(bugs, plans, evals, overallVerdict) {
|
|
13727
|
+
const lines = [
|
|
13728
|
+
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
13729
|
+
"",
|
|
13730
|
+
`**Target:** ${this.options.targetPaths.join(", ")}`,
|
|
13731
|
+
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
13732
|
+
""
|
|
13733
|
+
];
|
|
13734
|
+
if (bugs.length > 0) {
|
|
13735
|
+
lines.push("### Bugs Found", "");
|
|
13736
|
+
for (const b of bugs) {
|
|
13737
|
+
lines.push(
|
|
13738
|
+
`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
|
|
13739
|
+
);
|
|
13740
|
+
}
|
|
13741
|
+
lines.push("");
|
|
13742
|
+
}
|
|
13743
|
+
if (plans.length > 0) {
|
|
13744
|
+
lines.push("### Refactor Plans", "");
|
|
13745
|
+
for (const p of plans) {
|
|
13746
|
+
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
13747
|
+
for (const phase of p.phases) {
|
|
13748
|
+
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
13749
|
+
}
|
|
13750
|
+
}
|
|
13751
|
+
lines.push("");
|
|
13752
|
+
}
|
|
13753
|
+
if (evals.length > 0) {
|
|
13754
|
+
lines.push("### Critic Evaluations", "");
|
|
13755
|
+
for (const e of evals) {
|
|
13756
|
+
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
13757
|
+
for (const c of e.concerns) {
|
|
13758
|
+
if (c.severity === "blocking") {
|
|
13759
|
+
lines.push(` - ${c.description}`);
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13762
|
+
}
|
|
13763
|
+
lines.push("");
|
|
13764
|
+
}
|
|
13765
|
+
return lines.join("\n");
|
|
13766
|
+
}
|
|
13767
|
+
cleanup() {
|
|
13768
|
+
for (const dispose of this.disposers) dispose();
|
|
13769
|
+
this.disposers.length = 0;
|
|
13770
|
+
}
|
|
13771
|
+
};
|
|
13044
13772
|
|
|
13045
13773
|
// src/coordination/director.ts
|
|
13046
13774
|
var FleetSpawnBudgetError = class extends Error {
|
|
@@ -13119,6 +13847,8 @@ var Director = class {
|
|
|
13119
13847
|
subagentBridges = /* @__PURE__ */ new Map();
|
|
13120
13848
|
/** Tracks per-spawn config + assigned task ids for manifest writing. */
|
|
13121
13849
|
manifestEntries = /* @__PURE__ */ new Map();
|
|
13850
|
+
/** Tracks assigned nicknames so the same name is never reused in one fleet. */
|
|
13851
|
+
_usedNicknames = /* @__PURE__ */ new Set();
|
|
13122
13852
|
manifestPath;
|
|
13123
13853
|
roster;
|
|
13124
13854
|
directorPreamble;
|
|
@@ -13412,9 +14142,21 @@ var Director = class {
|
|
|
13412
14142
|
}
|
|
13413
14143
|
}
|
|
13414
14144
|
let result;
|
|
14145
|
+
const needsNickname = config.name === config.role || !config.name || config.name === "subagent";
|
|
14146
|
+
if (needsNickname) {
|
|
14147
|
+
const role = config.role ?? "subagent";
|
|
14148
|
+
if (this.fleetManager) {
|
|
14149
|
+
this.fleetManager.assignNicknameAndRecord("", config, priceLookup);
|
|
14150
|
+
} else {
|
|
14151
|
+
config.name = assignNickname(role, this._usedNicknames);
|
|
14152
|
+
this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
|
|
14153
|
+
}
|
|
14154
|
+
}
|
|
13415
14155
|
result = await this.coordinator.spawn(config);
|
|
13416
14156
|
if (this.fleetManager) {
|
|
13417
|
-
|
|
14157
|
+
if (!needsNickname) {
|
|
14158
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
14159
|
+
}
|
|
13418
14160
|
} else {
|
|
13419
14161
|
this.spawnCount += 1;
|
|
13420
14162
|
this.subagentMeta.set(result.subagentId, {
|
|
@@ -13850,7 +14592,9 @@ var Director = class {
|
|
|
13850
14592
|
makeFleetStatusTool(this),
|
|
13851
14593
|
makeFleetUsageTool(this),
|
|
13852
14594
|
makeFleetSessionTool(this),
|
|
13853
|
-
makeFleetHealthTool(this)
|
|
14595
|
+
makeFleetHealthTool(this),
|
|
14596
|
+
makeCollabDebugTool(this),
|
|
14597
|
+
makeFleetEmitTool(this)
|
|
13854
14598
|
];
|
|
13855
14599
|
return t2;
|
|
13856
14600
|
}
|
|
@@ -13862,6 +14606,17 @@ var Director = class {
|
|
|
13862
14606
|
async acquireCheckpointLock() {
|
|
13863
14607
|
return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
|
|
13864
14608
|
}
|
|
14609
|
+
/**
|
|
14610
|
+
* Start a collaborative debugging session: BugHunter, RefactorPlanner,
|
|
14611
|
+
* and Critic run in parallel on the same target files, with findings
|
|
14612
|
+
* flowing through the FleetBus (bug.found → refactor.plan →
|
|
14613
|
+
* critic.evaluation). Returns a structured CollabDebugReport when all
|
|
14614
|
+
* three agents complete or the session times out.
|
|
14615
|
+
*/
|
|
14616
|
+
async spawnCollab(options) {
|
|
14617
|
+
const session = new CollabSession(this, this.fleet, options);
|
|
14618
|
+
return session.start();
|
|
14619
|
+
}
|
|
13865
14620
|
/**
|
|
13866
14621
|
* Resume from a prior checkpoint snapshot (loaded via
|
|
13867
14622
|
* `loadDirectorState()`). Re-attach to the fleet mid-flight so
|
|
@@ -15623,10 +16378,10 @@ var AISpecBuilder = class {
|
|
|
15623
16378
|
async saveSession() {
|
|
15624
16379
|
if (!this.sessionPath) return;
|
|
15625
16380
|
try {
|
|
15626
|
-
const
|
|
16381
|
+
const fsp19 = await import('fs/promises');
|
|
15627
16382
|
const path31 = await import('path');
|
|
15628
16383
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
15629
|
-
await
|
|
16384
|
+
await fsp19.mkdir(path31.dirname(this.sessionPath), { recursive: true });
|
|
15630
16385
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
15631
16386
|
} catch {
|
|
15632
16387
|
}
|
|
@@ -15635,8 +16390,8 @@ var AISpecBuilder = class {
|
|
|
15635
16390
|
async loadSession() {
|
|
15636
16391
|
if (!this.sessionPath) return false;
|
|
15637
16392
|
try {
|
|
15638
|
-
const
|
|
15639
|
-
const raw = await
|
|
16393
|
+
const fsp19 = await import('fs/promises');
|
|
16394
|
+
const raw = await fsp19.readFile(this.sessionPath, "utf8");
|
|
15640
16395
|
const loaded = JSON.parse(raw);
|
|
15641
16396
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
15642
16397
|
this.session = loaded;
|
|
@@ -15650,8 +16405,8 @@ var AISpecBuilder = class {
|
|
|
15650
16405
|
async deleteSession() {
|
|
15651
16406
|
if (!this.sessionPath) return;
|
|
15652
16407
|
try {
|
|
15653
|
-
const
|
|
15654
|
-
await
|
|
16408
|
+
const fsp19 = await import('fs/promises');
|
|
16409
|
+
await fsp19.unlink(this.sessionPath);
|
|
15655
16410
|
} catch {
|
|
15656
16411
|
}
|
|
15657
16412
|
}
|
|
@@ -18785,7 +19540,25 @@ var CloudSync = class {
|
|
|
18785
19540
|
baseTreeSha,
|
|
18786
19541
|
`Sync ${cfg.categories.join(", ")} \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
18787
19542
|
);
|
|
18788
|
-
|
|
19543
|
+
try {
|
|
19544
|
+
await this.updateRef(token, owner, repoName, branch, commitSha);
|
|
19545
|
+
} catch (err) {
|
|
19546
|
+
if (err instanceof Error && err.message.includes("422")) {
|
|
19547
|
+
const remote = await this.getRef(token, owner, repoName, branch);
|
|
19548
|
+
const currentSha = remote.object.sha;
|
|
19549
|
+
const rebasedCommitSha = await this.createCommit(
|
|
19550
|
+
token,
|
|
19551
|
+
owner,
|
|
19552
|
+
repoName,
|
|
19553
|
+
newTreeSha,
|
|
19554
|
+
currentSha,
|
|
19555
|
+
`Sync ${cfg.categories.join(", ")} \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
19556
|
+
);
|
|
19557
|
+
await this.updateRef(token, owner, repoName, branch, rebasedCommitSha);
|
|
19558
|
+
} else {
|
|
19559
|
+
throw err;
|
|
19560
|
+
}
|
|
19561
|
+
}
|
|
18789
19562
|
const syncState = {
|
|
18790
19563
|
version: 1,
|
|
18791
19564
|
sha: commitSha,
|
|
@@ -20806,16 +21579,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
20806
21579
|
}
|
|
20807
21580
|
const index = Number.parseInt(reportId, 10) - 1;
|
|
20808
21581
|
if (!Number.isNaN(index) && reports[index]) {
|
|
20809
|
-
const { readFile:
|
|
20810
|
-
const content = await
|
|
21582
|
+
const { readFile: readFile35 } = await import('fs/promises');
|
|
21583
|
+
const content = await readFile35(join(reportsDir, reports[index]), "utf-8");
|
|
20811
21584
|
return { message: `# Security Report
|
|
20812
21585
|
|
|
20813
21586
|
${content}` };
|
|
20814
21587
|
}
|
|
20815
21588
|
const match = reports.find((r) => r.includes(reportId));
|
|
20816
21589
|
if (match) {
|
|
20817
|
-
const { readFile:
|
|
20818
|
-
const content = await
|
|
21590
|
+
const { readFile: readFile35 } = await import('fs/promises');
|
|
21591
|
+
const content = await readFile35(join(reportsDir, match), "utf-8");
|
|
20819
21592
|
return { message: `# Security Report
|
|
20820
21593
|
|
|
20821
21594
|
${content}` };
|
|
@@ -20898,6 +21671,8 @@ var FleetManager = class {
|
|
|
20898
21671
|
pendingTasks = /* @__PURE__ */ new Map();
|
|
20899
21672
|
subagentMeta = /* @__PURE__ */ new Map();
|
|
20900
21673
|
priceLookups = /* @__PURE__ */ new Map();
|
|
21674
|
+
/** Tracks which nickname keys are already assigned — prevents collisions. */
|
|
21675
|
+
_usedNicknames = /* @__PURE__ */ new Set();
|
|
20901
21676
|
/** The coordinator (wired via setCoordinator by Director after construction). */
|
|
20902
21677
|
coordinator = null;
|
|
20903
21678
|
constructor(opts = {}) {
|
|
@@ -20978,6 +21753,30 @@ var FleetManager = class {
|
|
|
20978
21753
|
}
|
|
20979
21754
|
return null;
|
|
20980
21755
|
}
|
|
21756
|
+
/**
|
|
21757
|
+
* Assign a memorable nickname (e.g. "Einstein (Bug Hunter)") to the config,
|
|
21758
|
+
* record it so the same name is never reused, then record the spawn.
|
|
21759
|
+
*
|
|
21760
|
+
* Call this INSTEAD of `recordSpawn` when you want automatic nicknames.
|
|
21761
|
+
* The nickname is written back to `config.name` BEFORE the coordinator
|
|
21762
|
+
* sees the config, so the manifest, logs, and fleet UI all show it.
|
|
21763
|
+
*/
|
|
21764
|
+
assignNicknameAndRecord(subagentId, config, priceLookup) {
|
|
21765
|
+
const role = config.role ?? "subagent";
|
|
21766
|
+
const nickname = assignNickname(role, this._usedNicknames);
|
|
21767
|
+
const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
21768
|
+
this._usedNicknames.add(baseKey);
|
|
21769
|
+
config.name = nickname;
|
|
21770
|
+
this.recordSpawn(subagentId, config, priceLookup);
|
|
21771
|
+
return nickname;
|
|
21772
|
+
}
|
|
21773
|
+
/**
|
|
21774
|
+
* Returns the set of already-assigned nickname keys — useful for debugging
|
|
21775
|
+
* and testing.
|
|
21776
|
+
*/
|
|
21777
|
+
get usedNicknames() {
|
|
21778
|
+
return this._usedNicknames;
|
|
21779
|
+
}
|
|
20981
21780
|
/**
|
|
20982
21781
|
* Records a spawn: increments counter, stores metadata, updates state checkpoint,
|
|
20983
21782
|
* and schedules a debounced manifest write. Call AFTER the coordinator
|
|
@@ -23391,10 +24190,28 @@ var DefaultPluginAPI = class {
|
|
|
23391
24190
|
}
|
|
23392
24191
|
this.pipelines = readonlyPipelines;
|
|
23393
24192
|
const tr = init.toolRegistry;
|
|
24193
|
+
const isOfficial = init.official === true;
|
|
24194
|
+
const assertCanMutateTool = (name, op) => {
|
|
24195
|
+
if (isOfficial) return;
|
|
24196
|
+
const currentOwner = tr.ownerOf(name);
|
|
24197
|
+
if (currentOwner === void 0) return;
|
|
24198
|
+
const ownedSolelyByMe = currentOwner.split("+").every((seg) => seg === owner);
|
|
24199
|
+
if (!ownedSolelyByMe) {
|
|
24200
|
+
throw new Error(
|
|
24201
|
+
`Plugin "${owner}" may not ${op} tool "${name}" \u2014 it is owned by "${currentOwner}". Only official (first-party) plugins may modify tools they do not own.`
|
|
24202
|
+
);
|
|
24203
|
+
}
|
|
24204
|
+
};
|
|
23394
24205
|
this.tools = {
|
|
23395
24206
|
register: (t2) => tr.register(t2, owner),
|
|
23396
|
-
unregister: (name) =>
|
|
23397
|
-
|
|
24207
|
+
unregister: (name) => {
|
|
24208
|
+
assertCanMutateTool(name, "unregister");
|
|
24209
|
+
return tr.unregister(name);
|
|
24210
|
+
},
|
|
24211
|
+
wrap: (name, wrapper) => {
|
|
24212
|
+
assertCanMutateTool(name, "wrap");
|
|
24213
|
+
tr.wrap(name, wrapper, owner);
|
|
24214
|
+
},
|
|
23398
24215
|
get: (name) => tr.get(name),
|
|
23399
24216
|
list: () => tr.list()
|
|
23400
24217
|
};
|
|
@@ -25442,9 +26259,12 @@ function dim2(s) {
|
|
|
25442
26259
|
}
|
|
25443
26260
|
|
|
25444
26261
|
// src/plugins/sync-plugin.ts
|
|
26262
|
+
init_atomic_write();
|
|
25445
26263
|
function createSyncPlugin(opts) {
|
|
25446
26264
|
let cloud = null;
|
|
25447
26265
|
let configStore;
|
|
26266
|
+
let vault;
|
|
26267
|
+
let syncConfigPath;
|
|
25448
26268
|
return {
|
|
25449
26269
|
name: "wstack-sync",
|
|
25450
26270
|
version: "1.0.0",
|
|
@@ -25456,6 +26276,8 @@ function createSyncPlugin(opts) {
|
|
|
25456
26276
|
const rawConfig = api.config;
|
|
25457
26277
|
const paths = opts?.paths ?? rawConfig.paths;
|
|
25458
26278
|
configStore = opts?.configStore ?? rawConfig.configStore;
|
|
26279
|
+
vault = opts?.vault ?? rawConfig.vault;
|
|
26280
|
+
syncConfigPath = paths?.syncConfig;
|
|
25459
26281
|
if (!paths || !configStore) {
|
|
25460
26282
|
api.log.warn("[sync] paths or configStore not available \u2014 /sync disabled");
|
|
25461
26283
|
return;
|
|
@@ -25471,7 +26293,7 @@ function createSyncPlugin(opts) {
|
|
|
25471
26293
|
}
|
|
25472
26294
|
);
|
|
25473
26295
|
void cloud.loadState();
|
|
25474
|
-
api.slashCommands.register(buildSyncCommand(cloud, configStore));
|
|
26296
|
+
api.slashCommands.register(buildSyncCommand(cloud, configStore, vault, syncConfigPath));
|
|
25475
26297
|
api.log.info("[sync] loaded \u2014 /sync available. Run /sync to get started.");
|
|
25476
26298
|
},
|
|
25477
26299
|
teardown(api) {
|
|
@@ -25483,7 +26305,7 @@ function createSyncPlugin(opts) {
|
|
|
25483
26305
|
}
|
|
25484
26306
|
};
|
|
25485
26307
|
}
|
|
25486
|
-
function buildSyncCommand(cloud, configStore) {
|
|
26308
|
+
function buildSyncCommand(cloud, configStore, vault, syncConfigPath) {
|
|
25487
26309
|
return {
|
|
25488
26310
|
name: "sync",
|
|
25489
26311
|
description: "Cloud sync: /sync [status|enable|disable|push|pull|categories]",
|
|
@@ -25511,13 +26333,17 @@ function buildSyncCommand(cloud, configStore) {
|
|
|
25511
26333
|
if (!repo || !repo.includes("/")) {
|
|
25512
26334
|
return { message: 'Invalid repo format. Expected "owner/repo".' };
|
|
25513
26335
|
}
|
|
26336
|
+
const storedToken = vault ? vault.encrypt(token) : token;
|
|
25514
26337
|
const syncConfig = {
|
|
25515
26338
|
enabled: true,
|
|
25516
26339
|
repo,
|
|
25517
|
-
githubToken:
|
|
26340
|
+
githubToken: storedToken,
|
|
25518
26341
|
categories: cats.length > 0 ? cats : ALL_SYNC_CATEGORIES,
|
|
25519
26342
|
lastSyncedAt: void 0
|
|
25520
26343
|
};
|
|
26344
|
+
if (syncConfigPath) {
|
|
26345
|
+
await atomicWrite(syncConfigPath, JSON.stringify(syncConfig, null, 2), { mode: 384 });
|
|
26346
|
+
}
|
|
25521
26347
|
configStore.update({ sync: syncConfig });
|
|
25522
26348
|
await cloud.loadState();
|
|
25523
26349
|
return {
|
|
@@ -26344,6 +27170,6 @@ ${formatPlan(updated)}`
|
|
|
26344
27170
|
};
|
|
26345
27171
|
}
|
|
26346
27172
|
|
|
26347
|
-
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CheckpointManager, CloudSync, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeLLMClassifier, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
27173
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CheckpointManager, CloudSync, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
26348
27174
|
//# sourceMappingURL=index.js.map
|
|
26349
27175
|
//# sourceMappingURL=index.js.map
|