@wrongstack/core 0.73.1 → 0.82.6
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-bridge-C0Ze7Ldm.d.ts → agent-bridge-C9P_HPez.d.ts} +2 -2
- package/dist/{agent-subagent-runner-BmITbs1Q.d.ts → agent-subagent-runner-2Aq0jOSj.d.ts} +107 -102
- package/dist/{compactor-D_ExJajC.d.ts → compactor-CJq7LQev.d.ts} +3 -3
- package/dist/{config-Dy0CK_o6.d.ts → config-_DZ7dN-T.d.ts} +77 -75
- package/dist/{context-y87Jc5ei.d.ts → context-ToHAp4-U.d.ts} +119 -90
- package/dist/coordination/index.d.ts +16 -16
- package/dist/coordination/index.js +382 -43
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +31 -31
- package/dist/defaults/index.js +524 -110
- package/dist/defaults/index.js.map +1 -1
- package/dist/{director-state-BmYi3DGA.d.ts → director-state-CgIc30qi.d.ts} +19 -19
- package/dist/{events-BBAlxBuw.d.ts → events-DnRqXaZ3.d.ts} +77 -39
- package/dist/execution/index.d.ts +53 -53
- package/dist/execution/index.js +67 -23
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +9 -9
- package/dist/extension/index.js +8 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-store-C7jcumEh.d.ts → goal-store-DvWLNu52.d.ts} +4 -4
- package/dist/{index-yQbZ2NQx.d.ts → index-BNOLadHw.d.ts} +28 -28
- package/dist/{index-BN6i2Nfg.d.ts → index-N0_c4bHQ.d.ts} +45 -45
- package/dist/index.d.ts +233 -160
- package/dist/index.js +825 -160
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +9 -9
- package/dist/infrastructure/index.js +29 -7
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +14 -14
- package/dist/kernel/index.js +7 -0
- package/dist/kernel/index.js.map +1 -1
- package/dist/logger-B72yyPc6.d.ts +12 -0
- package/dist/{logger-bOzkF5LL.d.ts → logger-C_27pj9i.d.ts} +12 -4
- package/dist/{mcp-servers-T0O6UN_w.d.ts → mcp-servers-Dck3T85_.d.ts} +20 -20
- package/dist/{mode-BO4SEUIv.d.ts → mode-CHo2XtHs.d.ts} +4 -4
- package/dist/models/index.d.ts +10 -10
- package/dist/models/index.js +8 -2
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-BcYJDKLm.d.ts → models-registry-Be3osGt5.d.ts} +28 -28
- package/dist/{models-registry-Cuq1C8V9.d.ts → models-registry-Boz639EI.d.ts} +12 -12
- package/dist/{multi-agent-coordinator-BSBbZt0e.d.ts → multi-agent-coordinator-DllpCVkF.d.ts} +12 -12
- package/dist/{null-fleet-bus-BCIRT_nV.d.ts → null-fleet-bus-BY0AN-sr.d.ts} +129 -120
- package/dist/observability/index.d.ts +41 -41
- package/dist/observability/index.js.map +1 -1
- package/dist/{observability-BhnVLBLS.d.ts → observability-CoSNZdhX.d.ts} +4 -4
- package/dist/{parallel-eternal-engine-CjAYGaCw.d.ts → parallel-eternal-engine-D402RASp.d.ts} +49 -49
- package/dist/{path-resolver-BnqXa9Ze.d.ts → path-resolver-UPFTsDyD.d.ts} +6 -6
- package/dist/{permission-V5BLOrY6.d.ts → permission-14CChMmO.d.ts} +10 -8
- package/dist/{permission-policy-CBVx-d-8.d.ts → permission-policy-gW5htOo1.d.ts} +7 -7
- package/dist/{plan-templates-DBgrTGPu.d.ts → plan-templates-DRvPgkfZ.d.ts} +70 -32
- package/dist/{provider-runner-n3KkHT_w.d.ts → provider-runner-COAJM8tC.d.ts} +6 -6
- package/dist/{retry-policy-CG3qvH_e.d.ts → retry-policy-DSu6O6rD.d.ts} +4 -4
- package/dist/sdd/index.d.ts +47 -47
- package/dist/sdd/index.js +47 -22
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.d.ts +6 -6
- package/dist/security/index.js +7 -1
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-RvBR_YRW.d.ts → selector-11-fm95U.d.ts} +2 -2
- package/dist/{session-event-bridge-CDHxcmQU.d.ts → session-event-bridge-D0u-x576.d.ts} +7 -7
- package/dist/{session-reader-BIpwM60D.d.ts → session-reader-BQU-toaN.d.ts} +23 -23
- package/dist/{skill-CxuWrsKK.d.ts → skill-BJeF2DwY.d.ts} +1 -1
- package/dist/skills/index.d.ts +9 -9
- package/dist/skills/index.js +15 -3
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +15 -15
- package/dist/storage/index.js +398 -80
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-CA11g6Jo.d.ts → system-prompt-C0rLCeyn.d.ts} +16 -11
- package/dist/{task-graph-D1YQbpxF.d.ts → task-graph-CikNdRTG.d.ts} +22 -22
- package/dist/types/index.d.ts +25 -25
- package/dist/types/index.js +61 -12
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +46 -45
- package/dist/utils/index.js +64 -13
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-eMXnY1_X.d.ts → wstack-paths-BQMvEllz.d.ts} +10 -3
- package/package.json +1 -1
- package/dist/logger-DDd5C--Z.d.ts +0 -12
package/dist/defaults/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
2
|
import { randomBytes, randomUUID, createCipheriv, createDecipheriv, createHash } from 'crypto';
|
|
3
3
|
import * as fsp from 'fs/promises';
|
|
4
|
-
import * as
|
|
4
|
+
import * as path3 from 'path';
|
|
5
5
|
import { isAbsolute, resolve } from 'path';
|
|
6
6
|
import * as fs5 from 'fs';
|
|
7
7
|
import * as os from 'os';
|
|
@@ -32,9 +32,9 @@ __export(atomic_write_exports, {
|
|
|
32
32
|
ensureDir: () => ensureDir
|
|
33
33
|
});
|
|
34
34
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
35
|
-
const dir =
|
|
35
|
+
const dir = path3.dirname(targetPath);
|
|
36
36
|
await fsp.mkdir(dir, { recursive: true });
|
|
37
|
-
const tmp =
|
|
37
|
+
const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
38
38
|
try {
|
|
39
39
|
if (typeof content === "string") {
|
|
40
40
|
await fsp.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -108,8 +108,10 @@ function isStdoutTTY() {
|
|
|
108
108
|
}
|
|
109
109
|
function writeTo(s, stream) {
|
|
110
110
|
if (!stream || typeof stream.write !== "function") return false;
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
{
|
|
112
|
+
stream.write(s);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
113
115
|
}
|
|
114
116
|
function writeErr(s, stream = process.stderr) {
|
|
115
117
|
return writeTo(s, stream);
|
|
@@ -162,14 +164,16 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
162
164
|
file;
|
|
163
165
|
bindings;
|
|
164
166
|
pretty;
|
|
167
|
+
stderr;
|
|
165
168
|
constructor(opts = {}) {
|
|
166
169
|
this.level = opts.level ?? process.env.WRONGSTACK_LOG_LEVEL ?? "info";
|
|
167
170
|
this.file = opts.file;
|
|
168
171
|
this.bindings = opts.bindings ?? {};
|
|
169
172
|
this.pretty = opts.pretty ?? true;
|
|
173
|
+
this.stderr = opts.stderr !== false;
|
|
170
174
|
if (this.file) {
|
|
171
175
|
try {
|
|
172
|
-
fs5.mkdirSync(
|
|
176
|
+
fs5.mkdirSync(path3.dirname(this.file), { recursive: true });
|
|
173
177
|
} catch {
|
|
174
178
|
}
|
|
175
179
|
}
|
|
@@ -194,6 +198,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
194
198
|
level: this.level,
|
|
195
199
|
file: this.file,
|
|
196
200
|
pretty: this.pretty,
|
|
201
|
+
stderr: this.stderr,
|
|
197
202
|
bindings: { ...this.bindings, ...bindings }
|
|
198
203
|
});
|
|
199
204
|
}
|
|
@@ -213,6 +218,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
213
218
|
} catch {
|
|
214
219
|
}
|
|
215
220
|
}
|
|
221
|
+
if (!this.stderr) return;
|
|
216
222
|
if (r <= LEVEL_RANK.warn || this.level === "debug" || this.level === "trace") {
|
|
217
223
|
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
218
224
|
if (ctx !== void 0) {
|
|
@@ -239,6 +245,12 @@ function formatCtx(ctx) {
|
|
|
239
245
|
init_atomic_write();
|
|
240
246
|
|
|
241
247
|
// src/utils/message-invariants.ts
|
|
248
|
+
function expectDefined(value) {
|
|
249
|
+
if (value === null || value === void 0) {
|
|
250
|
+
throw new Error("Expected value to be defined");
|
|
251
|
+
}
|
|
252
|
+
return value;
|
|
253
|
+
}
|
|
242
254
|
function repairToolUseAdjacency(messages) {
|
|
243
255
|
const removedToolUses = [];
|
|
244
256
|
const removedToolResults = [];
|
|
@@ -246,7 +258,7 @@ function repairToolUseAdjacency(messages) {
|
|
|
246
258
|
let changed = false;
|
|
247
259
|
const out = [];
|
|
248
260
|
for (let i = 0; i < messages.length; i++) {
|
|
249
|
-
const original = messages[i];
|
|
261
|
+
const original = expectDefined(messages[i]);
|
|
250
262
|
let msg = original;
|
|
251
263
|
if (hasToolUse(msg)) {
|
|
252
264
|
const nextIds = toolResultIds(messages[i + 1]);
|
|
@@ -331,7 +343,23 @@ function isEmptyMessage(msg) {
|
|
|
331
343
|
}
|
|
332
344
|
|
|
333
345
|
// src/storage/session-store.ts
|
|
334
|
-
|
|
346
|
+
function expectDefined2(value) {
|
|
347
|
+
if (value === null || value === void 0) {
|
|
348
|
+
throw new Error("Expected value to be defined");
|
|
349
|
+
}
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
function sanitizeModel(model) {
|
|
353
|
+
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
354
|
+
}
|
|
355
|
+
function generateSessionId(startedAt, model) {
|
|
356
|
+
const date = startedAt.slice(0, 10);
|
|
357
|
+
const time = startedAt.slice(11, 19).replace(/:/g, "-");
|
|
358
|
+
const suffix = randomBytes(2).toString("hex");
|
|
359
|
+
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
360
|
+
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
361
|
+
}
|
|
362
|
+
var DefaultSessionStore = class _DefaultSessionStore {
|
|
335
363
|
dir;
|
|
336
364
|
events;
|
|
337
365
|
secretScrubber;
|
|
@@ -340,19 +368,29 @@ var DefaultSessionStore = class {
|
|
|
340
368
|
this.events = opts.events;
|
|
341
369
|
this.secretScrubber = opts.secretScrubber;
|
|
342
370
|
}
|
|
371
|
+
/** Absolute path to the session index file. */
|
|
372
|
+
get indexFile() {
|
|
373
|
+
return path3.join(this.dir, "_index.jsonl");
|
|
374
|
+
}
|
|
343
375
|
/** Join session ID to its absolute path within the store directory. */
|
|
344
376
|
sessionPath(id, ext) {
|
|
345
|
-
return
|
|
377
|
+
return path3.join(this.dir, `${id}${ext}`);
|
|
346
378
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Ensure the directory implied by the session ID exists. When the ID
|
|
381
|
+
* contains a date prefix like `2026-06-06/...`, this creates the date
|
|
382
|
+
* subdirectory so sessions group naturally by day.
|
|
383
|
+
*/
|
|
384
|
+
async ensureShardDir(id) {
|
|
385
|
+
const dirPath = path3.dirname(path3.join(this.dir, id));
|
|
386
|
+
await ensureDir(dirPath);
|
|
387
|
+
return dirPath;
|
|
350
388
|
}
|
|
351
389
|
async create(meta) {
|
|
352
390
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
353
|
-
const id = meta.id
|
|
391
|
+
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
354
392
|
const shardDir = await this.ensureShardDir(id);
|
|
355
|
-
const file =
|
|
393
|
+
const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
|
|
356
394
|
let handle;
|
|
357
395
|
try {
|
|
358
396
|
handle = await fsp.open(file, "a", 384);
|
|
@@ -366,7 +404,8 @@ var DefaultSessionStore = class {
|
|
|
366
404
|
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
367
405
|
dir: shardDir,
|
|
368
406
|
filePath: file,
|
|
369
|
-
secretScrubber: this.secretScrubber
|
|
407
|
+
secretScrubber: this.secretScrubber,
|
|
408
|
+
onClose: (s) => this.appendToIndex(s)
|
|
370
409
|
});
|
|
371
410
|
} catch (err) {
|
|
372
411
|
await handle.close().catch(() => {
|
|
@@ -397,7 +436,7 @@ var DefaultSessionStore = class {
|
|
|
397
436
|
provider: data.metadata.provider
|
|
398
437
|
},
|
|
399
438
|
this.events,
|
|
400
|
-
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
|
|
439
|
+
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber, onClose: (s) => this.appendToIndex(s) }
|
|
401
440
|
);
|
|
402
441
|
return { writer, data };
|
|
403
442
|
} catch (err) {
|
|
@@ -427,6 +466,15 @@ var DefaultSessionStore = class {
|
|
|
427
466
|
async list(limit = 20) {
|
|
428
467
|
try {
|
|
429
468
|
await ensureDir(this.dir);
|
|
469
|
+
const indexed = await this.readIndex();
|
|
470
|
+
if (indexed.length > 0) {
|
|
471
|
+
indexed.sort((a, b) => {
|
|
472
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
473
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
474
|
+
return a.id.localeCompare(b.id);
|
|
475
|
+
});
|
|
476
|
+
return indexed.slice(0, limit);
|
|
477
|
+
}
|
|
430
478
|
const ids = await this.collectSessionIds(this.dir);
|
|
431
479
|
const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
432
480
|
const out = sessions.filter((s) => s !== null);
|
|
@@ -440,16 +488,121 @@ var DefaultSessionStore = class {
|
|
|
440
488
|
return [];
|
|
441
489
|
}
|
|
442
490
|
}
|
|
443
|
-
|
|
444
|
-
|
|
491
|
+
// ── Session index (_index.jsonl) ─────────────────────────────────────────
|
|
492
|
+
//
|
|
493
|
+
// One JSON line per closed session, appended atomically on close().
|
|
494
|
+
// When a session is deleted, a tombstone {action:"delete",id:"..."} is
|
|
495
|
+
// appended. On read, tombstones filter out matching session entries.
|
|
496
|
+
// This keeps listing O(lines-in-index) instead of O(files-on-disk).
|
|
497
|
+
//
|
|
498
|
+
// The index auto-compacts every N appends to prevent unbounded growth
|
|
499
|
+
// from tombstones and duplicate entries (resume cycles).
|
|
500
|
+
indexAppendCount = 0;
|
|
501
|
+
static COMPACT_EVERY = 30;
|
|
502
|
+
/** Append a session summary to the index. */
|
|
503
|
+
async appendToIndex(summary) {
|
|
504
|
+
try {
|
|
505
|
+
await ensureDir(this.dir);
|
|
506
|
+
const line = JSON.stringify(summary) + "\n";
|
|
507
|
+
await fsp.appendFile(this.indexFile, line, "utf8");
|
|
508
|
+
this.indexAppendCount++;
|
|
509
|
+
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
510
|
+
await this.compactIndex();
|
|
511
|
+
this.indexAppendCount = 0;
|
|
512
|
+
}
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/** Append a tombstone entry for a deleted session. */
|
|
517
|
+
async writeTombstone(id) {
|
|
518
|
+
try {
|
|
519
|
+
await ensureDir(this.dir);
|
|
520
|
+
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
521
|
+
await fsp.appendFile(this.indexFile, line, "utf8");
|
|
522
|
+
this.indexAppendCount++;
|
|
523
|
+
} catch {
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Compact the index: read all entries, drop tombstones, deduplicate
|
|
528
|
+
* (keep latest per session), and rewrite. Atomic via temp+rename.
|
|
529
|
+
*/
|
|
530
|
+
async compactIndex() {
|
|
531
|
+
const entries = await this.readIndex();
|
|
532
|
+
if (entries.length === 0) return;
|
|
533
|
+
const tmp = `${this.indexFile}.compact.tmp`;
|
|
534
|
+
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
535
|
+
await fsp.writeFile(tmp, lines, "utf8");
|
|
536
|
+
await fsp.rename(tmp, this.indexFile);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Read the index file and return deduplicated session summaries.
|
|
540
|
+
* Entries with a matching tombstone are filtered out.
|
|
541
|
+
* Returns empty array when the index doesn't exist or is corrupt.
|
|
542
|
+
*/
|
|
543
|
+
async readIndex() {
|
|
544
|
+
let raw;
|
|
545
|
+
try {
|
|
546
|
+
raw = await fsp.readFile(this.indexFile, "utf8");
|
|
547
|
+
} catch {
|
|
548
|
+
return [];
|
|
549
|
+
}
|
|
550
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
551
|
+
const seen = /* @__PURE__ */ new Map();
|
|
552
|
+
for (const line of raw.split("\n")) {
|
|
553
|
+
if (!line.trim()) continue;
|
|
554
|
+
try {
|
|
555
|
+
const entry = JSON.parse(line);
|
|
556
|
+
if (entry.action === "delete" && entry.id) {
|
|
557
|
+
deleted.add(entry.id);
|
|
558
|
+
seen.delete(entry.id);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (entry.id && !deleted.has(entry.id)) {
|
|
562
|
+
seen.set(entry.id, entry);
|
|
563
|
+
}
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return Array.from(seen.values());
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Rebuild the index from disk by scanning all sessions and writing a
|
|
571
|
+
* fresh _index.jsonl. Useful after manual cleanup or index corruption.
|
|
572
|
+
*/
|
|
573
|
+
async rebuildIndex() {
|
|
574
|
+
const ids = await this.collectSessionIds(this.dir);
|
|
575
|
+
const summaries = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
576
|
+
const valid = summaries.filter((s) => s !== null);
|
|
577
|
+
const tmp = `${this.indexFile}.tmp`;
|
|
578
|
+
const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
579
|
+
await fsp.writeFile(tmp, lines, "utf8");
|
|
580
|
+
await fsp.rename(tmp, this.indexFile);
|
|
581
|
+
return valid.length;
|
|
582
|
+
}
|
|
583
|
+
/** Recursively collect session IDs from date-shard subdirectories.
|
|
584
|
+
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
|
|
585
|
+
* Skips `.jsonl`/`.summary.json` root files, dot-files, and
|
|
586
|
+
* sub-directories that belong to fleet/subagent sessions. */
|
|
587
|
+
async collectSessionIds(dir, prefix = "", depth = 0) {
|
|
445
588
|
const ids = [];
|
|
446
|
-
|
|
589
|
+
let entries;
|
|
590
|
+
try {
|
|
591
|
+
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
592
|
+
} catch {
|
|
593
|
+
return ids;
|
|
594
|
+
}
|
|
447
595
|
for (const entry of entries) {
|
|
448
|
-
|
|
596
|
+
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
597
|
+
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
598
|
+
continue;
|
|
449
599
|
if (entry.isDirectory()) {
|
|
450
|
-
|
|
600
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
601
|
+
ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
|
|
451
602
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
452
|
-
|
|
603
|
+
if (entry.name === "_index.jsonl") continue;
|
|
604
|
+
const base = entry.name.replace(/\.jsonl$/, "");
|
|
605
|
+
ids.push(prefix ? `${prefix}/${base}` : base);
|
|
453
606
|
}
|
|
454
607
|
}
|
|
455
608
|
return ids;
|
|
@@ -472,9 +625,70 @@ var DefaultSessionStore = class {
|
|
|
472
625
|
return summary;
|
|
473
626
|
}
|
|
474
627
|
}
|
|
475
|
-
|
|
476
|
-
|
|
628
|
+
/**
|
|
629
|
+
* Delete a session and all associated files: JSONL, summary, plan/todos
|
|
630
|
+
* sidecars, and the session directory (fleet.json, shared/, subagents/).
|
|
631
|
+
*/
|
|
632
|
+
async deleteSession(id) {
|
|
633
|
+
await fsp.unlink(this.sessionPath(id, ".jsonl")).catch(() => void 0);
|
|
477
634
|
await fsp.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
|
|
635
|
+
const shardDir = path3.dirname(path3.join(this.dir, id));
|
|
636
|
+
const base = path3.basename(id);
|
|
637
|
+
for (const ext of [".plan.json", ".todos.json"]) {
|
|
638
|
+
await fsp.unlink(path3.join(shardDir, `${base}${ext}`)).catch(() => void 0);
|
|
639
|
+
}
|
|
640
|
+
const sessDir = path3.join(shardDir, base);
|
|
641
|
+
await fsp.rm(sessDir, { recursive: true, force: true }).catch(() => void 0);
|
|
642
|
+
await this.writeTombstone(id);
|
|
643
|
+
}
|
|
644
|
+
async delete(id) {
|
|
645
|
+
await this.deleteSession(id);
|
|
646
|
+
}
|
|
647
|
+
async prune(maxAgeDays = 30) {
|
|
648
|
+
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
649
|
+
let deleted = 0;
|
|
650
|
+
let activeSessionId = null;
|
|
651
|
+
try {
|
|
652
|
+
const raw = await fsp.readFile(path3.join(this.dir, "active.json"), "utf8");
|
|
653
|
+
const active = JSON.parse(raw);
|
|
654
|
+
activeSessionId = active.sessionId ?? null;
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
const entries = await fsp.readdir(this.dir, { withFileTypes: true }).catch(() => []);
|
|
658
|
+
for (const entry of entries) {
|
|
659
|
+
if (!entry.isDirectory()) continue;
|
|
660
|
+
const dateDir = path3.join(this.dir, entry.name);
|
|
661
|
+
const files = await fsp.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
662
|
+
for (const file of files) {
|
|
663
|
+
if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
|
|
664
|
+
const jsonlPath = path3.join(dateDir, file.name);
|
|
665
|
+
try {
|
|
666
|
+
const stat5 = await fsp.stat(jsonlPath);
|
|
667
|
+
if (stat5.mtimeMs >= cutoff) continue;
|
|
668
|
+
} catch {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
const id = `${entry.name}/${file.name.replace(/\.jsonl$/, "")}`;
|
|
672
|
+
if (activeSessionId && id === activeSessionId) continue;
|
|
673
|
+
await this.deleteSession(id);
|
|
674
|
+
deleted++;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (deleted > 0) {
|
|
678
|
+
await this.compactIndex().catch(() => void 0);
|
|
679
|
+
}
|
|
680
|
+
for (const entry of entries) {
|
|
681
|
+
if (!entry.isDirectory()) continue;
|
|
682
|
+
const dateDir = path3.join(this.dir, entry.name);
|
|
683
|
+
try {
|
|
684
|
+
const remaining = await fsp.readdir(dateDir);
|
|
685
|
+
if (remaining.length === 0) {
|
|
686
|
+
await fsp.rmdir(dateDir).catch(() => void 0);
|
|
687
|
+
}
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return deleted;
|
|
478
692
|
}
|
|
479
693
|
async clearHistory(id) {
|
|
480
694
|
await this.ensureShardDir(id);
|
|
@@ -496,13 +710,42 @@ var DefaultSessionStore = class {
|
|
|
496
710
|
const data = await this.load(id);
|
|
497
711
|
const firstUser = data.events.find((e) => e.type === "user_input");
|
|
498
712
|
const title = firstUser && firstUser.type === "user_input" ? userInputTitle(firstUser.content) : "(empty session)";
|
|
713
|
+
let iterationCount = 0;
|
|
714
|
+
let toolCallCount = 0;
|
|
715
|
+
let toolErrorCount = 0;
|
|
716
|
+
let fileChangeCount = 0;
|
|
717
|
+
const toolBreakdown = {};
|
|
718
|
+
let outcome = void 0;
|
|
719
|
+
const lastEvent = data.events[data.events.length - 1];
|
|
720
|
+
for (const e of data.events) {
|
|
721
|
+
if (e.type === "in_flight_start") iterationCount++;
|
|
722
|
+
else if (e.type === "tool_call_start") {
|
|
723
|
+
toolCallCount++;
|
|
724
|
+
toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;
|
|
725
|
+
} else if (e.type === "tool_result" && e.isError) toolErrorCount++;
|
|
726
|
+
else if (e.type === "file_snapshot") fileChangeCount += e.files.length;
|
|
727
|
+
}
|
|
728
|
+
if (lastEvent?.type === "session_end") {
|
|
729
|
+
outcome = "completed";
|
|
730
|
+
} else if (lastEvent?.type === "in_flight_start") {
|
|
731
|
+
outcome = "aborted";
|
|
732
|
+
} else if (data.events.some((e) => e.type === "error")) {
|
|
733
|
+
outcome = "error";
|
|
734
|
+
}
|
|
499
735
|
return {
|
|
500
736
|
id,
|
|
501
737
|
title,
|
|
502
738
|
startedAt: data.metadata.startedAt,
|
|
739
|
+
endedAt: data.metadata.endedAt,
|
|
503
740
|
model: data.metadata.model ?? "unknown",
|
|
504
741
|
provider: data.metadata.provider ?? "unknown",
|
|
505
|
-
tokenTotal: data.usage.input + data.usage.output
|
|
742
|
+
tokenTotal: data.usage.input + data.usage.output,
|
|
743
|
+
iterationCount: iterationCount > 0 ? iterationCount : void 0,
|
|
744
|
+
toolCallCount: toolCallCount > 0 ? toolCallCount : void 0,
|
|
745
|
+
toolErrorCount: toolErrorCount > 0 ? toolErrorCount : void 0,
|
|
746
|
+
fileChangeCount: fileChangeCount > 0 ? fileChangeCount : void 0,
|
|
747
|
+
toolBreakdown: Object.keys(toolBreakdown).length > 0 ? toolBreakdown : {},
|
|
748
|
+
outcome
|
|
506
749
|
};
|
|
507
750
|
} catch {
|
|
508
751
|
return {
|
|
@@ -601,9 +844,10 @@ var FileSessionWriter = class {
|
|
|
601
844
|
this.meta = meta;
|
|
602
845
|
this.events = events;
|
|
603
846
|
this.resumed = opts.resumed ?? false;
|
|
604
|
-
this.manifestFile = opts.dir ?
|
|
847
|
+
this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
|
|
605
848
|
this.filePath = opts.filePath ?? "";
|
|
606
849
|
this.secretScrubber = opts.secretScrubber;
|
|
850
|
+
this.onCloseCb = opts.onClose;
|
|
607
851
|
this.summary = {
|
|
608
852
|
id,
|
|
609
853
|
title: "(empty session)",
|
|
@@ -633,6 +877,15 @@ var FileSessionWriter = class {
|
|
|
633
877
|
appendFailCount = 0;
|
|
634
878
|
lastAppendWarnAt = 0;
|
|
635
879
|
secretScrubber;
|
|
880
|
+
onCloseCb;
|
|
881
|
+
// ── Enriched summary tracking ──────────────────────────────────────────
|
|
882
|
+
iterationCount = 0;
|
|
883
|
+
toolCallCount = 0;
|
|
884
|
+
toolErrorCount = 0;
|
|
885
|
+
toolBreakdown = {};
|
|
886
|
+
fileChangeCount = 0;
|
|
887
|
+
compactionCount = 0;
|
|
888
|
+
outcome = void 0;
|
|
636
889
|
/**
|
|
637
890
|
* Scrub secrets out of conversation-turn events before they are observed
|
|
638
891
|
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
@@ -710,8 +963,22 @@ var FileSessionWriter = class {
|
|
|
710
963
|
observeForSummary(event) {
|
|
711
964
|
if (event.type === "tool_use") {
|
|
712
965
|
this.openToolUses.add(event.id);
|
|
966
|
+
} else if (event.type === "tool_call_start") {
|
|
967
|
+
this.toolCallCount++;
|
|
968
|
+
this.toolBreakdown[event.name] = (this.toolBreakdown[event.name] ?? 0) + 1;
|
|
713
969
|
} else if (event.type === "tool_result") {
|
|
714
970
|
this.openToolUses.delete(event.id);
|
|
971
|
+
if (event.isError) {
|
|
972
|
+
this.toolErrorCount++;
|
|
973
|
+
this.outcome = "error";
|
|
974
|
+
}
|
|
975
|
+
} else if (event.type === "file_snapshot") {
|
|
976
|
+
this.fileChangeCount += event.files.length;
|
|
977
|
+
} else if (event.type === "compaction") {
|
|
978
|
+
this.compactionCount++;
|
|
979
|
+
}
|
|
980
|
+
if (event.type === "error" || event.type === "provider_error") {
|
|
981
|
+
this.outcome = "error";
|
|
715
982
|
}
|
|
716
983
|
if (event.type === "user_input" && this.summary.title === "(empty session)") {
|
|
717
984
|
this.summary = { ...this.summary, title: userInputTitle(event.content) };
|
|
@@ -722,18 +989,35 @@ var FileSessionWriter = class {
|
|
|
722
989
|
} else if (event.type === "session_end") {
|
|
723
990
|
const total = event.usage.input + event.usage.output;
|
|
724
991
|
if (total > 0) this.summary = { ...this.summary, tokenTotal: total };
|
|
992
|
+
} else if (event.type === "in_flight_start") {
|
|
993
|
+
this.iterationCount++;
|
|
725
994
|
}
|
|
726
995
|
}
|
|
727
996
|
async close() {
|
|
728
997
|
if (this.closing) return;
|
|
729
998
|
this.closing = true;
|
|
730
999
|
this.closed = true;
|
|
1000
|
+
this.summary = {
|
|
1001
|
+
...this.summary,
|
|
1002
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1003
|
+
iterationCount: this.iterationCount,
|
|
1004
|
+
toolCallCount: this.toolCallCount,
|
|
1005
|
+
toolErrorCount: this.toolErrorCount,
|
|
1006
|
+
fileChangeCount: this.fileChangeCount,
|
|
1007
|
+
compactionCount: this.compactionCount > 0 ? this.compactionCount : void 0,
|
|
1008
|
+
toolBreakdown: { ...this.toolBreakdown },
|
|
1009
|
+
outcome: this.outcome ?? "completed"
|
|
1010
|
+
};
|
|
731
1011
|
if (this.manifestFile) {
|
|
732
1012
|
try {
|
|
733
1013
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
734
1014
|
} catch {
|
|
735
1015
|
}
|
|
736
1016
|
}
|
|
1017
|
+
try {
|
|
1018
|
+
await this.onCloseCb?.(this.summary);
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
737
1021
|
try {
|
|
738
1022
|
await this.handle.close();
|
|
739
1023
|
} catch {
|
|
@@ -775,7 +1059,7 @@ var FileSessionWriter = class {
|
|
|
775
1059
|
let targetCheckpointLine = -1;
|
|
776
1060
|
let afterTarget = false;
|
|
777
1061
|
for (let i = 0; i < lines.length; i++) {
|
|
778
|
-
const line = lines[i];
|
|
1062
|
+
const line = expectDefined2(lines[i]);
|
|
779
1063
|
if (!line.trim()) continue;
|
|
780
1064
|
let event;
|
|
781
1065
|
try {
|
|
@@ -883,7 +1167,7 @@ init_atomic_write();
|
|
|
883
1167
|
var QueueStore = class {
|
|
884
1168
|
file;
|
|
885
1169
|
constructor(opts) {
|
|
886
|
-
this.file =
|
|
1170
|
+
this.file = path3.join(opts.dir, "queue.json");
|
|
887
1171
|
}
|
|
888
1172
|
async write(items) {
|
|
889
1173
|
if (items.length === 0) {
|
|
@@ -953,7 +1237,7 @@ var DefaultAttachmentStore = class {
|
|
|
953
1237
|
let data = input.data;
|
|
954
1238
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
955
1239
|
await fsp.mkdir(this.spoolDir, { recursive: true });
|
|
956
|
-
spooledPath =
|
|
1240
|
+
spooledPath = path3.join(this.spoolDir, `${id}.bin`);
|
|
957
1241
|
await atomicWrite(spooledPath, input.data, {
|
|
958
1242
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
959
1243
|
});
|
|
@@ -1141,7 +1425,7 @@ ${body.trim()}`);
|
|
|
1141
1425
|
async remember(text, scope = "project-memory") {
|
|
1142
1426
|
return this.runSerialized(scope, async () => {
|
|
1143
1427
|
const file = this.files[scope];
|
|
1144
|
-
await ensureDir(
|
|
1428
|
+
await ensureDir(path3.dirname(file));
|
|
1145
1429
|
let existing = "";
|
|
1146
1430
|
try {
|
|
1147
1431
|
existing = await fsp.readFile(file, "utf8");
|
|
@@ -1789,7 +2073,7 @@ var RecoveryLock = class {
|
|
|
1789
2073
|
sessionStore;
|
|
1790
2074
|
probe;
|
|
1791
2075
|
constructor(opts) {
|
|
1792
|
-
this.file =
|
|
2076
|
+
this.file = path3.join(opts.dir, LOCK_FILE);
|
|
1793
2077
|
this.pid = opts.pid ?? process.pid;
|
|
1794
2078
|
this.hostname = opts.hostname ?? os.hostname();
|
|
1795
2079
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -1847,7 +2131,7 @@ var RecoveryLock = class {
|
|
|
1847
2131
|
* null return before calling this.
|
|
1848
2132
|
*/
|
|
1849
2133
|
async write(sessionId) {
|
|
1850
|
-
await ensureDir(
|
|
2134
|
+
await ensureDir(path3.dirname(this.file));
|
|
1851
2135
|
const lock = {
|
|
1852
2136
|
v: 1,
|
|
1853
2137
|
sessionId,
|
|
@@ -1951,6 +2235,12 @@ function compileUserRegex(pattern, flags) {
|
|
|
1951
2235
|
}
|
|
1952
2236
|
|
|
1953
2237
|
// src/storage/session-reader.ts
|
|
2238
|
+
function expectDefined3(value) {
|
|
2239
|
+
if (value === null || value === void 0) {
|
|
2240
|
+
throw new Error("Expected value to be defined");
|
|
2241
|
+
}
|
|
2242
|
+
return value;
|
|
2243
|
+
}
|
|
1954
2244
|
var DefaultSessionReader = class {
|
|
1955
2245
|
store;
|
|
1956
2246
|
constructor(opts) {
|
|
@@ -2012,7 +2302,7 @@ var DefaultSessionReader = class {
|
|
|
2012
2302
|
continue;
|
|
2013
2303
|
}
|
|
2014
2304
|
for (let i = 0; i < data.events.length; i++) {
|
|
2015
|
-
const ev = data.events[i];
|
|
2305
|
+
const ev = expectDefined3(data.events[i]);
|
|
2016
2306
|
if (allowedTypes && !allowedTypes.has(ev.type)) continue;
|
|
2017
2307
|
const text = eventText(ev);
|
|
2018
2308
|
if (text === null) continue;
|
|
@@ -2317,8 +2607,11 @@ var SessionAnalyzer = class {
|
|
|
2317
2607
|
}
|
|
2318
2608
|
calcDuration(events) {
|
|
2319
2609
|
if (events.length < 2) return 0;
|
|
2320
|
-
const
|
|
2321
|
-
const
|
|
2610
|
+
const firstEvent = events[0];
|
|
2611
|
+
const lastEvent = events[events.length - 1];
|
|
2612
|
+
if (!firstEvent || !lastEvent) return 0;
|
|
2613
|
+
const first = new Date(firstEvent.ts).getTime();
|
|
2614
|
+
const last = new Date(lastEvent.ts).getTime();
|
|
2322
2615
|
return last - first;
|
|
2323
2616
|
}
|
|
2324
2617
|
};
|
|
@@ -2431,7 +2724,7 @@ async function loadTodosCheckpoint(filePath) {
|
|
|
2431
2724
|
const parsed = JSON.parse(raw);
|
|
2432
2725
|
if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) return null;
|
|
2433
2726
|
return parsed.todos.filter(
|
|
2434
|
-
(t) => !!t && typeof t.id === "string" && typeof t.content === "string" && typeof t.status === "string"
|
|
2727
|
+
(t) => !!t && typeof t.id === "string" && typeof t.content === "string" && typeof t.status === "string" && (t.activeForm === void 0 || typeof t.activeForm === "string")
|
|
2435
2728
|
);
|
|
2436
2729
|
} catch {
|
|
2437
2730
|
return null;
|
|
@@ -3074,7 +3367,7 @@ var DefaultSecretVault = class {
|
|
|
3074
3367
|
} catch (err) {
|
|
3075
3368
|
if (err.code !== "ENOENT") throw err;
|
|
3076
3369
|
}
|
|
3077
|
-
fs5.mkdirSync(
|
|
3370
|
+
fs5.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
|
|
3078
3371
|
const key = randomBytes(KEY_BYTES);
|
|
3079
3372
|
try {
|
|
3080
3373
|
fs5.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
@@ -3143,7 +3436,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
3143
3436
|
}
|
|
3144
3437
|
const merged = deepMerge2(current, patch ?? {});
|
|
3145
3438
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
3146
|
-
await fsp.mkdir(
|
|
3439
|
+
await fsp.mkdir(path3.dirname(configPath), { recursive: true });
|
|
3147
3440
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3148
3441
|
await restrictFilePermissions(configPath);
|
|
3149
3442
|
}
|
|
@@ -3271,6 +3564,12 @@ function getDangerousCapabilities(toolOrCaps) {
|
|
|
3271
3564
|
init_atomic_write();
|
|
3272
3565
|
|
|
3273
3566
|
// src/utils/glob-match.ts
|
|
3567
|
+
function expectDefined4(value) {
|
|
3568
|
+
if (value === null || value === void 0) {
|
|
3569
|
+
throw new Error("Expected value to be defined");
|
|
3570
|
+
}
|
|
3571
|
+
return value;
|
|
3572
|
+
}
|
|
3274
3573
|
function escapeRegex(s) {
|
|
3275
3574
|
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
3276
3575
|
}
|
|
@@ -3282,7 +3581,7 @@ function getCachedGlob(pattern) {
|
|
|
3282
3581
|
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
3283
3582
|
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
3284
3583
|
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
3285
|
-
COMPILED_GLOB_CACHE.delete(keys[i]);
|
|
3584
|
+
COMPILED_GLOB_CACHE.delete(expectDefined4(keys[i]));
|
|
3286
3585
|
}
|
|
3287
3586
|
}
|
|
3288
3587
|
const re = compileGlob(pattern);
|
|
@@ -3367,9 +3666,9 @@ function getInputString(input, key) {
|
|
|
3367
3666
|
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
3368
3667
|
if (!projectRoot) return false;
|
|
3369
3668
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
3370
|
-
const resolved =
|
|
3371
|
-
const relative2 =
|
|
3372
|
-
return !!relative2 && !relative2.startsWith("..") && !
|
|
3669
|
+
const resolved = path3.resolve(projectRoot, rawPath);
|
|
3670
|
+
const relative2 = path3.relative(projectRoot, resolved);
|
|
3671
|
+
return !!relative2 && !relative2.startsWith("..") && !path3.isAbsolute(relative2);
|
|
3373
3672
|
}
|
|
3374
3673
|
function tokenizeShell(command) {
|
|
3375
3674
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
@@ -3379,7 +3678,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
|
|
|
3379
3678
|
if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
|
|
3380
3679
|
if (token.includes("*")) return true;
|
|
3381
3680
|
if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
|
|
3382
|
-
if (
|
|
3681
|
+
if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
|
|
3383
3682
|
return false;
|
|
3384
3683
|
}
|
|
3385
3684
|
function hasDangerousDeleteTarget(tokens, start, projectRoot) {
|
|
@@ -4035,7 +4334,7 @@ var DefaultSkillLoader = class {
|
|
|
4035
4334
|
const entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
4036
4335
|
for (const e of entries) {
|
|
4037
4336
|
if (!e.isDirectory()) continue;
|
|
4038
|
-
const skillFile =
|
|
4337
|
+
const skillFile = path3.join(dir, e.name, "SKILL.md");
|
|
4039
4338
|
try {
|
|
4040
4339
|
const raw = await fsp.readFile(skillFile, "utf8");
|
|
4041
4340
|
const meta = parseFrontmatter(raw);
|
|
@@ -4140,13 +4439,19 @@ function parseDescription(raw) {
|
|
|
4140
4439
|
const scope = [];
|
|
4141
4440
|
const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
|
|
4142
4441
|
if (coversMatch) {
|
|
4143
|
-
const items = coversMatch[1].replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
4442
|
+
const items = coversMatch[1] ?? "".replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
4144
4443
|
scope.push(...items);
|
|
4145
4444
|
}
|
|
4146
4445
|
return { trigger, scope };
|
|
4147
4446
|
}
|
|
4148
4447
|
|
|
4149
4448
|
// src/utils/json-repair.ts
|
|
4449
|
+
function expectDefined5(value) {
|
|
4450
|
+
if (value === null || value === void 0) {
|
|
4451
|
+
throw new Error("Expected value to be defined");
|
|
4452
|
+
}
|
|
4453
|
+
return value;
|
|
4454
|
+
}
|
|
4150
4455
|
function completePartialObject(s) {
|
|
4151
4456
|
if (!s.trim().startsWith("{")) return s;
|
|
4152
4457
|
if (tryParse(s).ok) return s;
|
|
@@ -4158,7 +4463,7 @@ function completePartialObject(s) {
|
|
|
4158
4463
|
let contentEnd = 0;
|
|
4159
4464
|
let stringBraceDepth = 0;
|
|
4160
4465
|
for (let i = 0; i < s.length; i++) {
|
|
4161
|
-
const ch = s[i];
|
|
4466
|
+
const ch = expectDefined5(s[i]);
|
|
4162
4467
|
if (inString) {
|
|
4163
4468
|
contentEnd = i + 1;
|
|
4164
4469
|
if (escaped) {
|
|
@@ -4543,6 +4848,12 @@ var DefaultProviderRunner = class {
|
|
|
4543
4848
|
};
|
|
4544
4849
|
|
|
4545
4850
|
// src/utils/token-estimate.ts
|
|
4851
|
+
function expectDefined6(value) {
|
|
4852
|
+
if (value === null || value === void 0) {
|
|
4853
|
+
throw new Error("Expected value to be defined");
|
|
4854
|
+
}
|
|
4855
|
+
return value;
|
|
4856
|
+
}
|
|
4546
4857
|
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
4547
4858
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
4548
4859
|
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
@@ -4552,7 +4863,7 @@ function getCachedEstimate(key, compute) {
|
|
|
4552
4863
|
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
4553
4864
|
const keys = [...ESTIMATE_CACHE.keys()];
|
|
4554
4865
|
for (let i = 0; i < Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4); i++) {
|
|
4555
|
-
ESTIMATE_CACHE.delete(keys[i]);
|
|
4866
|
+
ESTIMATE_CACHE.delete(expectDefined6(keys[i]));
|
|
4556
4867
|
}
|
|
4557
4868
|
}
|
|
4558
4869
|
const estimate = compute();
|
|
@@ -4942,7 +5253,7 @@ var IntelligentCompactor = class {
|
|
|
4942
5253
|
maxTokens: 1024
|
|
4943
5254
|
};
|
|
4944
5255
|
const ac = ctx.signal ? void 0 : new AbortController();
|
|
4945
|
-
const signal = ctx.signal ?? ac
|
|
5256
|
+
const signal = ctx.signal ?? ac?.signal;
|
|
4946
5257
|
const res = await this.provider.complete(req, { signal });
|
|
4947
5258
|
const textBlocks = res.content.filter(isTextBlock);
|
|
4948
5259
|
return textBlocks.map((b) => b.text).join("\n").trim() || "(empty summary)";
|
|
@@ -5045,6 +5356,12 @@ var IntelligentCompactor = class {
|
|
|
5045
5356
|
};
|
|
5046
5357
|
|
|
5047
5358
|
// src/models/llm-selector.ts
|
|
5359
|
+
function expectDefined7(value) {
|
|
5360
|
+
if (value === null || value === void 0) {
|
|
5361
|
+
throw new Error("Expected value to be defined");
|
|
5362
|
+
}
|
|
5363
|
+
return value;
|
|
5364
|
+
}
|
|
5048
5365
|
var DEFAULT_SYSTEM_PROMPT = `You are a context pruning assistant. Given a conversation history and a token budget, decide which message ranges are worth keeping verbatim and which should be collapsed into summaries.
|
|
5049
5366
|
|
|
5050
5367
|
Output a JSON object with this structure:
|
|
@@ -5085,7 +5402,7 @@ function formatMessages(messages, maxChars = 8e3) {
|
|
|
5085
5402
|
const lines = [];
|
|
5086
5403
|
let used = 0;
|
|
5087
5404
|
for (let i = 0; i < messages.length; i++) {
|
|
5088
|
-
const m = messages[i];
|
|
5405
|
+
const m = expectDefined7(messages[i]);
|
|
5089
5406
|
const role = m.role.padEnd(10, " ");
|
|
5090
5407
|
let text;
|
|
5091
5408
|
if (typeof m.content === "string") {
|
|
@@ -5150,7 +5467,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
5150
5467
|
let tokenCount = 0;
|
|
5151
5468
|
let startIdx = 0;
|
|
5152
5469
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
5153
|
-
const m = messages[i];
|
|
5470
|
+
const m = expectDefined7(messages[i]);
|
|
5154
5471
|
const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
|
|
5155
5472
|
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
5156
5473
|
0
|
|
@@ -5361,6 +5678,7 @@ Summarize the following message range:`;
|
|
|
5361
5678
|
let boundary = preserveIdx;
|
|
5362
5679
|
for (let i = preserveIdx; i < messages.length && i < preserveIdx + 6; i++) {
|
|
5363
5680
|
const m = messages[i];
|
|
5681
|
+
if (!m) continue;
|
|
5364
5682
|
if (m.role === "user" && this.hasTextContent(m)) {
|
|
5365
5683
|
boundary = i;
|
|
5366
5684
|
break;
|
|
@@ -5756,6 +6074,12 @@ function createToolOutputSerializer(opts = {}) {
|
|
|
5756
6074
|
}
|
|
5757
6075
|
|
|
5758
6076
|
// src/execution/tool-executor.ts
|
|
6077
|
+
function expectDefined8(value) {
|
|
6078
|
+
if (value === null || value === void 0) {
|
|
6079
|
+
throw new Error("Expected value to be defined");
|
|
6080
|
+
}
|
|
6081
|
+
return value;
|
|
6082
|
+
}
|
|
5759
6083
|
var ToolExecutor = class {
|
|
5760
6084
|
constructor(registry, opts) {
|
|
5761
6085
|
this.registry = registry;
|
|
@@ -6044,6 +6368,9 @@ ${post.additionalContext}` };
|
|
|
6044
6368
|
async runStreamedTool(tool, input, ctx, signal, toolUseId) {
|
|
6045
6369
|
let finalOutput;
|
|
6046
6370
|
let sawFinal = false;
|
|
6371
|
+
if (!tool.executeStream) {
|
|
6372
|
+
throw new Error(`Tool "${tool.name}" does not support streaming execution`);
|
|
6373
|
+
}
|
|
6047
6374
|
const stream = tool.executeStream(input, ctx, { signal });
|
|
6048
6375
|
for await (const ev of stream) {
|
|
6049
6376
|
if (ev.type === "final") {
|
|
@@ -6148,7 +6475,7 @@ function hasMalformedArguments(input) {
|
|
|
6148
6475
|
function extractMalformedRaw(input) {
|
|
6149
6476
|
if (!hasMalformedArguments(input)) return void 0;
|
|
6150
6477
|
const obj = input;
|
|
6151
|
-
const value = obj[Object.keys(obj)[0]];
|
|
6478
|
+
const value = obj[expectDefined8(Object.keys(obj)[0])];
|
|
6152
6479
|
if (value === void 0 || value === null) return void 0;
|
|
6153
6480
|
if (typeof value === "string") return value;
|
|
6154
6481
|
try {
|
|
@@ -6327,8 +6654,8 @@ var AutonomousRunner = class {
|
|
|
6327
6654
|
init_atomic_write();
|
|
6328
6655
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
6329
6656
|
function goalFilePath(projectRoot) {
|
|
6330
|
-
const hash = createHash("sha256").update(
|
|
6331
|
-
return
|
|
6657
|
+
const hash = createHash("sha256").update(path3.resolve(projectRoot)).digest("hex").slice(0, 12);
|
|
6658
|
+
return path3.join(os.homedir(), ".wrongstack", "projects", hash, "goal.json");
|
|
6332
6659
|
}
|
|
6333
6660
|
async function loadGoal(filePath) {
|
|
6334
6661
|
let raw;
|
|
@@ -7109,16 +7436,16 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
7109
7436
|
}
|
|
7110
7437
|
if (exceeded.length === 0) return [];
|
|
7111
7438
|
if (!this._onThreshold) {
|
|
7112
|
-
const first2 = exceeded[0];
|
|
7439
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
7113
7440
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
7114
7441
|
}
|
|
7115
7442
|
if (this._mode === "sync") {
|
|
7116
|
-
const first2 = exceeded[0];
|
|
7443
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
7117
7444
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
7118
7445
|
}
|
|
7119
7446
|
const bus = this._events;
|
|
7120
7447
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
7121
|
-
const first2 = exceeded[0];
|
|
7448
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
7122
7449
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
7123
7450
|
}
|
|
7124
7451
|
for (const entry of exceeded) {
|
|
@@ -7126,8 +7453,9 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
7126
7453
|
const decision2 = this._negotiateExtension(entry.kind, exceeded);
|
|
7127
7454
|
this._pendingNegotiations.set(entry.kind, decision2);
|
|
7128
7455
|
}
|
|
7129
|
-
const first = exceeded[0];
|
|
7456
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
7130
7457
|
const decision = this._pendingNegotiations.get(first.kind);
|
|
7458
|
+
if (!decision) throw new Error(`No pending negotiation for ${first.kind}`);
|
|
7131
7459
|
throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
|
|
7132
7460
|
}
|
|
7133
7461
|
/**
|
|
@@ -7149,8 +7477,11 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
7149
7477
|
* a fresh signal.
|
|
7150
7478
|
*/
|
|
7151
7479
|
async _negotiateExtension(kind, exceeded) {
|
|
7480
|
+
if (!this._onThreshold) {
|
|
7481
|
+
return "stop";
|
|
7482
|
+
}
|
|
7152
7483
|
try {
|
|
7153
|
-
const first = exceeded[0];
|
|
7484
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
7154
7485
|
const result = this._onThreshold({
|
|
7155
7486
|
kind: first.kind,
|
|
7156
7487
|
used: first.used,
|
|
@@ -9633,6 +9964,11 @@ function getAgentDefinition(role) {
|
|
|
9633
9964
|
|
|
9634
9965
|
// src/coordination/dispatcher.ts
|
|
9635
9966
|
var DEFAULT_DISPATCH_ROLE = "executor";
|
|
9967
|
+
var FALLBACK_DEFINITION = {
|
|
9968
|
+
config: { role: "unknown", name: "Unknown Agent" },
|
|
9969
|
+
budget: {},
|
|
9970
|
+
capability: { phase: "meta", summary: "", keywords: [] }
|
|
9971
|
+
};
|
|
9636
9972
|
function normalize(text) {
|
|
9637
9973
|
return ` ${text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
|
|
9638
9974
|
}
|
|
@@ -9660,7 +9996,7 @@ function scoreAgents(task, catalog = AGENT_CATALOG) {
|
|
|
9660
9996
|
}
|
|
9661
9997
|
function heuristicConfidence(candidates) {
|
|
9662
9998
|
if (candidates.length === 0) return 0;
|
|
9663
|
-
const top = candidates[0]
|
|
9999
|
+
const top = candidates[0]?.score ?? 0;
|
|
9664
10000
|
const second = candidates[1]?.score ?? 0;
|
|
9665
10001
|
const strength = Math.min(1, top / 3);
|
|
9666
10002
|
const margin = (top - second + 1) / (top + 1);
|
|
@@ -9676,7 +10012,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
9676
10012
|
if (top && confidence >= threshold) {
|
|
9677
10013
|
return {
|
|
9678
10014
|
role: top.role,
|
|
9679
|
-
definition: catalog[top.role],
|
|
10015
|
+
definition: catalog[top.role] ?? FALLBACK_DEFINITION,
|
|
9680
10016
|
confidence,
|
|
9681
10017
|
method: "heuristic",
|
|
9682
10018
|
reason: `Matched keywords: ${top.matched.slice(0, 4).join(", ")}`,
|
|
@@ -9684,7 +10020,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
9684
10020
|
};
|
|
9685
10021
|
}
|
|
9686
10022
|
if (opts.classifier) {
|
|
9687
|
-
const pool = (candidates.length > 0 ? candidates.slice(0, maxCandidates).map((c) => catalog[c.role]) : ALL_AGENT_DEFINITIONS).map((d) => ({
|
|
10023
|
+
const pool = (candidates.length > 0 ? candidates.slice(0, maxCandidates).map((c) => catalog[c.role] ?? FALLBACK_DEFINITION) : ALL_AGENT_DEFINITIONS).map((d) => ({
|
|
9688
10024
|
role: d.config.role,
|
|
9689
10025
|
name: d.config.name,
|
|
9690
10026
|
summary: d.capability.summary
|
|
@@ -9694,7 +10030,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
9694
10030
|
if (choice && catalog[choice.role]) {
|
|
9695
10031
|
return {
|
|
9696
10032
|
role: choice.role,
|
|
9697
|
-
definition: catalog[choice.role],
|
|
10033
|
+
definition: catalog[choice.role] ?? FALLBACK_DEFINITION,
|
|
9698
10034
|
confidence: 1,
|
|
9699
10035
|
method: "llm",
|
|
9700
10036
|
reason: choice.reason ?? "Selected by LLM classifier",
|
|
@@ -9707,17 +10043,17 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
9707
10043
|
if (top) {
|
|
9708
10044
|
return {
|
|
9709
10045
|
role: top.role,
|
|
9710
|
-
definition: catalog[top.role],
|
|
10046
|
+
definition: catalog[top.role] ?? FALLBACK_DEFINITION,
|
|
9711
10047
|
confidence,
|
|
9712
10048
|
method: "heuristic",
|
|
9713
10049
|
reason: `Weak match (${top.matched.slice(0, 3).join(", ") || "low signal"})`,
|
|
9714
10050
|
alternatives: candidates.slice(1, maxCandidates)
|
|
9715
10051
|
};
|
|
9716
10052
|
}
|
|
9717
|
-
const fallbackRole = catalog[DEFAULT_DISPATCH_ROLE] ? DEFAULT_DISPATCH_ROLE : Object.keys(catalog)[0];
|
|
10053
|
+
const fallbackRole = catalog[DEFAULT_DISPATCH_ROLE] ? DEFAULT_DISPATCH_ROLE : Object.keys(catalog)[0] ?? DEFAULT_DISPATCH_ROLE;
|
|
9718
10054
|
return {
|
|
9719
10055
|
role: fallbackRole,
|
|
9720
|
-
definition: catalog[fallbackRole],
|
|
10056
|
+
definition: catalog[fallbackRole] ?? FALLBACK_DEFINITION,
|
|
9721
10057
|
confidence: 0,
|
|
9722
10058
|
method: "fallback",
|
|
9723
10059
|
reason: "No keyword signal; defaulting to the generalist Executor",
|
|
@@ -10530,6 +10866,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
10530
10866
|
takeNextDispatchableTask() {
|
|
10531
10867
|
for (let i = 0; i < this.pendingTasks.length; i++) {
|
|
10532
10868
|
const task = this.pendingTasks[i];
|
|
10869
|
+
if (!task) continue;
|
|
10533
10870
|
const subagentId = task.subagentId ? this.isIdleSubagent(task.subagentId) ? task.subagentId : null : this.findIdleSubagent();
|
|
10534
10871
|
if (!subagentId) continue;
|
|
10535
10872
|
this.pendingTasks.splice(i, 1);
|
|
@@ -10721,14 +11058,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
10721
11058
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
10722
11059
|
if (idleExceeded && !wallExceeded) {
|
|
10723
11060
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
10724
|
-
reject(new BudgetExceededError("timeout", idleLimit, budget.idleMs()));
|
|
11061
|
+
reject(new BudgetExceededError("timeout", idleLimit ?? 0, budget.idleMs()));
|
|
10725
11062
|
return;
|
|
10726
11063
|
}
|
|
10727
11064
|
if (!wallExceeded) {
|
|
10728
11065
|
scheduleNext();
|
|
10729
11066
|
return;
|
|
10730
11067
|
}
|
|
10731
|
-
const limit = wallLimit;
|
|
11068
|
+
const limit = wallLimit ?? 0;
|
|
10732
11069
|
if (!budget.onThreshold) {
|
|
10733
11070
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
10734
11071
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -10887,6 +11224,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
10887
11224
|
};
|
|
10888
11225
|
|
|
10889
11226
|
// src/execution/parallel-eternal-engine.ts
|
|
11227
|
+
function expectDefined9(value) {
|
|
11228
|
+
if (value === null || value === void 0) {
|
|
11229
|
+
throw new Error("Expected value to be defined");
|
|
11230
|
+
}
|
|
11231
|
+
return value;
|
|
11232
|
+
}
|
|
10890
11233
|
function sleep2(ms) {
|
|
10891
11234
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
10892
11235
|
}
|
|
@@ -11045,7 +11388,7 @@ var ParallelEternalEngine = class {
|
|
|
11045
11388
|
// Fan-out
|
|
11046
11389
|
// -------------------------------------------------------------------------
|
|
11047
11390
|
async fanOut(goal, tasks) {
|
|
11048
|
-
const coordinator = this.coordinator;
|
|
11391
|
+
const coordinator = expectDefined9(this.coordinator);
|
|
11049
11392
|
const slotCount = Math.min(this.slots, tasks.length);
|
|
11050
11393
|
const routes = this.dispatchEnabled ? await Promise.all(
|
|
11051
11394
|
tasks.slice(0, slotCount).map(
|
|
@@ -11077,7 +11420,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
11077
11420
|
const routeInfo = [];
|
|
11078
11421
|
const spawnPromises = [];
|
|
11079
11422
|
for (let i = 0; i < slotCount; i++) {
|
|
11080
|
-
const task = tasks[i];
|
|
11423
|
+
const task = expectDefined9(tasks[i]);
|
|
11081
11424
|
const route = routes[i] ?? null;
|
|
11082
11425
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
11083
11426
|
const taskId = randomUUID();
|
|
@@ -11450,7 +11793,7 @@ var InMemoryBridgeTransport = class {
|
|
|
11450
11793
|
}
|
|
11451
11794
|
subscribe(agentId, handler) {
|
|
11452
11795
|
if (!this.subs.has(agentId)) this.subs.set(agentId, /* @__PURE__ */ new Set());
|
|
11453
|
-
this.subs.get(agentId)
|
|
11796
|
+
this.subs.get(agentId)?.add(handler);
|
|
11454
11797
|
return () => this.subs.get(agentId)?.delete(handler);
|
|
11455
11798
|
}
|
|
11456
11799
|
close(agentId) {
|
|
@@ -11568,6 +11911,12 @@ function createMessage(type, from, payload, to) {
|
|
|
11568
11911
|
priority: "normal"
|
|
11569
11912
|
};
|
|
11570
11913
|
}
|
|
11914
|
+
function expectDefined10(value) {
|
|
11915
|
+
if (value === null || value === void 0) {
|
|
11916
|
+
throw new Error("Expected value to be defined");
|
|
11917
|
+
}
|
|
11918
|
+
return value;
|
|
11919
|
+
}
|
|
11571
11920
|
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
11572
11921
|
var IS_WINDOWS = process.platform === "win32";
|
|
11573
11922
|
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
@@ -11581,7 +11930,7 @@ function globToRegex(pat) {
|
|
|
11581
11930
|
let i = 0;
|
|
11582
11931
|
let re = "^";
|
|
11583
11932
|
while (i < pat.length) {
|
|
11584
|
-
const c = pat[i];
|
|
11933
|
+
const c = expectDefined10(pat[i]);
|
|
11585
11934
|
if (c === "*") {
|
|
11586
11935
|
if (pat[i + 1] === "*") {
|
|
11587
11936
|
re += ".*";
|
|
@@ -11620,7 +11969,7 @@ function globToRegex(pat) {
|
|
|
11620
11969
|
}
|
|
11621
11970
|
function baseDir(pat) {
|
|
11622
11971
|
let i = pat.length - 1;
|
|
11623
|
-
while (i >= 0 && !GLOB_CHARS.has(pat[i]) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
11972
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined10(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
11624
11973
|
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
11625
11974
|
return cut < 0 ? "." : pat.slice(0, cut);
|
|
11626
11975
|
}
|
|
@@ -11845,7 +12194,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
11845
12194
|
this.emit("session.error", error);
|
|
11846
12195
|
throw error;
|
|
11847
12196
|
}
|
|
11848
|
-
for (const result of results
|
|
12197
|
+
for (const result of results?.flat() ?? []) {
|
|
11849
12198
|
await this.parseAndEmit(result);
|
|
11850
12199
|
}
|
|
11851
12200
|
const report = this.assembleReport();
|
|
@@ -11906,7 +12255,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
11906
12255
|
}
|
|
11907
12256
|
budgetForRole(role) {
|
|
11908
12257
|
if (this.options.budgetOverrides?.[role]) {
|
|
11909
|
-
return this.options.budgetOverrides[role];
|
|
12258
|
+
return this.options.budgetOverrides[role] ?? { maxIterations: 0, maxToolCalls: 0, timeoutMs: 0 };
|
|
11910
12259
|
}
|
|
11911
12260
|
const defaults = {
|
|
11912
12261
|
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
@@ -12359,7 +12708,7 @@ function makeSpawnTool(director, roster) {
|
|
|
12359
12708
|
});
|
|
12360
12709
|
const dispatchRole = dispatchResult.role;
|
|
12361
12710
|
if (roster?.[dispatchRole]) {
|
|
12362
|
-
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole]);
|
|
12711
|
+
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole] ?? {});
|
|
12363
12712
|
} else {
|
|
12364
12713
|
const def = dispatchResult.definition;
|
|
12365
12714
|
cfg = {
|
|
@@ -13842,7 +14191,7 @@ var Director = class _Director {
|
|
|
13842
14191
|
})),
|
|
13843
14192
|
usage: this.usage.snapshot()
|
|
13844
14193
|
};
|
|
13845
|
-
await fsp.mkdir(
|
|
14194
|
+
await fsp.mkdir(path3.dirname(this.manifestPath), { recursive: true });
|
|
13846
14195
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
13847
14196
|
return this.manifestPath;
|
|
13848
14197
|
}
|
|
@@ -14048,7 +14397,7 @@ var Director = class _Director {
|
|
|
14048
14397
|
*/
|
|
14049
14398
|
async readSession(subagentId, tail) {
|
|
14050
14399
|
if (!this.sessionsRoot) return null;
|
|
14051
|
-
const filePath =
|
|
14400
|
+
const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
14052
14401
|
let raw;
|
|
14053
14402
|
try {
|
|
14054
14403
|
raw = await fsp.readFile(filePath, "utf8");
|
|
@@ -14284,6 +14633,7 @@ function createDelegateTool(opts) {
|
|
|
14284
14633
|
if (typeof i.task !== "string" || !i.task.trim()) {
|
|
14285
14634
|
return { ok: false, error: "`task` is required." };
|
|
14286
14635
|
}
|
|
14636
|
+
const target = i.role ?? i.name ?? "subagent";
|
|
14287
14637
|
try {
|
|
14288
14638
|
let director = await opts.host.ensureDirector();
|
|
14289
14639
|
if (!director) {
|
|
@@ -14344,6 +14694,7 @@ function createDelegateTool(opts) {
|
|
|
14344
14694
|
if (!cfg.timeoutMs) {
|
|
14345
14695
|
cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
14346
14696
|
}
|
|
14697
|
+
opts.events?.emit("delegate.started", { target, task: i.task });
|
|
14347
14698
|
const subagentId = await director.spawn(cfg);
|
|
14348
14699
|
const taskId = await director.assign({
|
|
14349
14700
|
id: `${randomUUID()}`,
|
|
@@ -14378,6 +14729,17 @@ function createDelegateTool(opts) {
|
|
|
14378
14729
|
});
|
|
14379
14730
|
if ("__timeout" in result) {
|
|
14380
14731
|
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
14732
|
+
opts.events?.emit("delegate.completed", {
|
|
14733
|
+
target,
|
|
14734
|
+
task: i.task,
|
|
14735
|
+
ok: false,
|
|
14736
|
+
status: "host_timeout",
|
|
14737
|
+
summary: `[${target}] timed out \u2014 no result within ${Math.round(timeoutMs / 1e3)}s`,
|
|
14738
|
+
durationMs: timeoutMs,
|
|
14739
|
+
iterations: partial2?.events ?? 0,
|
|
14740
|
+
toolCalls: partial2?.toolUsesObserved ?? 0,
|
|
14741
|
+
subagentId
|
|
14742
|
+
});
|
|
14381
14743
|
return {
|
|
14382
14744
|
ok: false,
|
|
14383
14745
|
stopReason: "host_timeout",
|
|
@@ -14394,6 +14756,24 @@ function createDelegateTool(opts) {
|
|
|
14394
14756
|
const retryable = result.error?.retryable;
|
|
14395
14757
|
const backoffMs = result.error?.backoffMs;
|
|
14396
14758
|
const summary = buildDelegateSummary(i.role, result);
|
|
14759
|
+
let costUsd;
|
|
14760
|
+
try {
|
|
14761
|
+
costUsd = dir.snapshot().perSubagent[result.subagentId]?.cost;
|
|
14762
|
+
} catch {
|
|
14763
|
+
costUsd = void 0;
|
|
14764
|
+
}
|
|
14765
|
+
opts.events?.emit("delegate.completed", {
|
|
14766
|
+
target,
|
|
14767
|
+
task: i.task,
|
|
14768
|
+
ok: result.status === "success",
|
|
14769
|
+
status: result.status,
|
|
14770
|
+
summary,
|
|
14771
|
+
durationMs: result.durationMs,
|
|
14772
|
+
iterations: result.iterations,
|
|
14773
|
+
toolCalls: result.toolCalls,
|
|
14774
|
+
costUsd,
|
|
14775
|
+
subagentId: result.subagentId
|
|
14776
|
+
});
|
|
14397
14777
|
return {
|
|
14398
14778
|
ok: result.status === "success",
|
|
14399
14779
|
status: result.status,
|
|
@@ -14415,10 +14795,21 @@ function createDelegateTool(opts) {
|
|
|
14415
14795
|
summary
|
|
14416
14796
|
};
|
|
14417
14797
|
} catch (err) {
|
|
14798
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14799
|
+
opts.events?.emit("delegate.completed", {
|
|
14800
|
+
target,
|
|
14801
|
+
task: i.task,
|
|
14802
|
+
ok: false,
|
|
14803
|
+
status: "error",
|
|
14804
|
+
summary: `[${target}] failed \u2014 ${message}`,
|
|
14805
|
+
durationMs: 0,
|
|
14806
|
+
iterations: 0,
|
|
14807
|
+
toolCalls: 0
|
|
14808
|
+
});
|
|
14418
14809
|
return {
|
|
14419
14810
|
ok: false,
|
|
14420
14811
|
stopReason: "error",
|
|
14421
|
-
error:
|
|
14812
|
+
error: message
|
|
14422
14813
|
};
|
|
14423
14814
|
}
|
|
14424
14815
|
}
|
|
@@ -14505,13 +14896,13 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
14505
14896
|
if (!opts.sessionsRoot) return void 0;
|
|
14506
14897
|
const candidates = [];
|
|
14507
14898
|
if (opts.directorRunId) {
|
|
14508
|
-
candidates.push(
|
|
14899
|
+
candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
14509
14900
|
} else {
|
|
14510
14901
|
try {
|
|
14511
14902
|
const entries = await fsp.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
14512
14903
|
for (const entry of entries) {
|
|
14513
14904
|
if (entry.isDirectory()) {
|
|
14514
|
-
candidates.push(
|
|
14905
|
+
candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
14515
14906
|
}
|
|
14516
14907
|
}
|
|
14517
14908
|
} catch {
|
|
@@ -14558,9 +14949,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
14558
14949
|
let dir;
|
|
14559
14950
|
if (opts.store) {
|
|
14560
14951
|
store = opts.store;
|
|
14561
|
-
dir = opts.sessionsRoot ?
|
|
14952
|
+
dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
14562
14953
|
} else if (opts.sessionsRoot) {
|
|
14563
|
-
dir =
|
|
14954
|
+
dir = path3.join(opts.sessionsRoot, runId);
|
|
14564
14955
|
store = new DefaultSessionStore({ dir });
|
|
14565
14956
|
} else {
|
|
14566
14957
|
throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
|
|
@@ -14614,7 +15005,10 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
14614
15005
|
if (kind === "timeout" || kind === "idle_timeout") {
|
|
14615
15006
|
if (progress > lastTimeoutProgress) {
|
|
14616
15007
|
lastTimeoutProgress = progress;
|
|
14617
|
-
const next2 = Math.min(
|
|
15008
|
+
const next2 = Math.min(
|
|
15009
|
+
Math.ceil(limit * (1 + factor)),
|
|
15010
|
+
ceiling.timeoutMs ?? DEFAULT_CEILING.timeoutMs
|
|
15011
|
+
);
|
|
14618
15012
|
extend({ timeoutMs: next2 });
|
|
14619
15013
|
} else {
|
|
14620
15014
|
deny();
|
|
@@ -14628,7 +15022,7 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
14628
15022
|
}
|
|
14629
15023
|
extendCounts.set(kind, count + 1);
|
|
14630
15024
|
const field = FIELD_BY_KIND[kind];
|
|
14631
|
-
const cap = ceiling[field];
|
|
15025
|
+
const cap = ceiling[field] ?? DEFAULT_CEILING[field];
|
|
14632
15026
|
const next = Math.min(Math.ceil(limit * (1 + factor)), cap);
|
|
14633
15027
|
extend({ [field]: next });
|
|
14634
15028
|
})
|
|
@@ -14764,7 +15158,7 @@ var DefaultModelsRegistry = class {
|
|
|
14764
15158
|
this.overlay = opts.overlay;
|
|
14765
15159
|
this.overlayUrl = opts.overlayUrl;
|
|
14766
15160
|
this.overlayFile = opts.overlayFile;
|
|
14767
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
15161
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
14768
15162
|
}
|
|
14769
15163
|
async load(opts = {}) {
|
|
14770
15164
|
if (this.payload && !opts.force) return this.payload;
|
|
@@ -14977,7 +15371,7 @@ var DefaultModelsRegistry = class {
|
|
|
14977
15371
|
}
|
|
14978
15372
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
14979
15373
|
cacheLocation() {
|
|
14980
|
-
return
|
|
15374
|
+
return path3.resolve(this.cacheFile);
|
|
14981
15375
|
}
|
|
14982
15376
|
};
|
|
14983
15377
|
function hasEntries(payload) {
|
|
@@ -15273,7 +15667,7 @@ var DefaultModeStore = class {
|
|
|
15273
15667
|
}
|
|
15274
15668
|
async loadActiveMode() {
|
|
15275
15669
|
try {
|
|
15276
|
-
const configPath =
|
|
15670
|
+
const configPath = path3.join(this.configDir, "mode.json");
|
|
15277
15671
|
const content = await fsp.readFile(configPath, "utf8");
|
|
15278
15672
|
const data = JSON.parse(content);
|
|
15279
15673
|
this.activeModeId = data.activeMode ?? null;
|
|
@@ -15284,7 +15678,7 @@ var DefaultModeStore = class {
|
|
|
15284
15678
|
async saveActiveMode() {
|
|
15285
15679
|
try {
|
|
15286
15680
|
await fsp.mkdir(this.configDir, { recursive: true });
|
|
15287
|
-
const configPath =
|
|
15681
|
+
const configPath = path3.join(this.configDir, "mode.json");
|
|
15288
15682
|
await atomicWrite(
|
|
15289
15683
|
configPath,
|
|
15290
15684
|
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
@@ -15299,11 +15693,11 @@ async function loadProjectModes(modesDir) {
|
|
|
15299
15693
|
const entries = await fsp.readdir(modesDir);
|
|
15300
15694
|
for (const entry of entries) {
|
|
15301
15695
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
15302
|
-
const filePath =
|
|
15696
|
+
const filePath = path3.join(modesDir, entry);
|
|
15303
15697
|
const stat5 = await fsp.stat(filePath);
|
|
15304
15698
|
if (!stat5.isFile()) continue;
|
|
15305
15699
|
const content = await fsp.readFile(filePath, "utf8");
|
|
15306
|
-
const id =
|
|
15700
|
+
const id = path3.basename(entry, path3.extname(entry));
|
|
15307
15701
|
modes.push({
|
|
15308
15702
|
id,
|
|
15309
15703
|
name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -15319,7 +15713,7 @@ async function loadProjectModes(modesDir) {
|
|
|
15319
15713
|
async function loadUserModes(modesDir) {
|
|
15320
15714
|
const modes = [];
|
|
15321
15715
|
try {
|
|
15322
|
-
const manifestPath =
|
|
15716
|
+
const manifestPath = path3.join(modesDir, "modes.json");
|
|
15323
15717
|
const content = await fsp.readFile(manifestPath, "utf8");
|
|
15324
15718
|
const manifest = JSON.parse(content);
|
|
15325
15719
|
for (const mode of manifest.modes) {
|
|
@@ -15927,7 +16321,7 @@ var TaskTracker = class {
|
|
|
15927
16321
|
if (filter.type?.length && !filter.type.includes(n.type)) return false;
|
|
15928
16322
|
if (filter.assignee?.length && n.assignee && !filter.assignee.includes(n.assignee))
|
|
15929
16323
|
return false;
|
|
15930
|
-
if (filter.tags?.length && n.tags && !n.tags.some((t) => filter.tags
|
|
16324
|
+
if (filter.tags?.length && n.tags && !n.tags.some((t) => filter.tags?.includes(t)))
|
|
15931
16325
|
return false;
|
|
15932
16326
|
if (filter.specRequirementId && n.specRequirementId !== filter.specRequirementId)
|
|
15933
16327
|
return false;
|
|
@@ -16236,7 +16630,7 @@ var SpecStore = class {
|
|
|
16236
16630
|
indexPath;
|
|
16237
16631
|
constructor(opts) {
|
|
16238
16632
|
this.baseDir = opts.baseDir;
|
|
16239
|
-
this.indexPath =
|
|
16633
|
+
this.indexPath = path3.join(this.baseDir, "_index.json");
|
|
16240
16634
|
}
|
|
16241
16635
|
async save(spec) {
|
|
16242
16636
|
await ensureDir(this.baseDir);
|
|
@@ -16305,7 +16699,7 @@ var SpecStore = class {
|
|
|
16305
16699
|
return updated;
|
|
16306
16700
|
}
|
|
16307
16701
|
filePath(id) {
|
|
16308
|
-
return
|
|
16702
|
+
return path3.join(this.baseDir, `${id}.json`);
|
|
16309
16703
|
}
|
|
16310
16704
|
async readIndex() {
|
|
16311
16705
|
try {
|
|
@@ -16362,7 +16756,7 @@ var TaskGraphStore = class {
|
|
|
16362
16756
|
indexPath;
|
|
16363
16757
|
constructor(opts) {
|
|
16364
16758
|
this.baseDir = opts.baseDir;
|
|
16365
|
-
this.indexPath =
|
|
16759
|
+
this.indexPath = path3.join(this.baseDir, "_index.json");
|
|
16366
16760
|
}
|
|
16367
16761
|
async save(graph) {
|
|
16368
16762
|
await ensureDir(this.baseDir);
|
|
@@ -16400,7 +16794,7 @@ var TaskGraphStore = class {
|
|
|
16400
16794
|
}
|
|
16401
16795
|
}
|
|
16402
16796
|
filePath(id) {
|
|
16403
|
-
return
|
|
16797
|
+
return path3.join(this.baseDir, `${id}.json`);
|
|
16404
16798
|
}
|
|
16405
16799
|
async readIndex() {
|
|
16406
16800
|
try {
|
|
@@ -16441,6 +16835,12 @@ var TaskGraphStore = class {
|
|
|
16441
16835
|
};
|
|
16442
16836
|
|
|
16443
16837
|
// src/sdd/spec-builder.ts
|
|
16838
|
+
function expectDefined11(value) {
|
|
16839
|
+
if (value === null || value === void 0) {
|
|
16840
|
+
throw new Error("Expected value to be defined");
|
|
16841
|
+
}
|
|
16842
|
+
return value;
|
|
16843
|
+
}
|
|
16444
16844
|
function buildQuestioningPrompt(session, min, max) {
|
|
16445
16845
|
const answered = session.answers.length;
|
|
16446
16846
|
const remaining = Math.max(0, min - answered);
|
|
@@ -16486,7 +16886,7 @@ function buildQuestioningPrompt(session, min, max) {
|
|
|
16486
16886
|
if (answered > 0) {
|
|
16487
16887
|
lines.push("", "**Conversation so far:**");
|
|
16488
16888
|
for (let i = 0; i < answered; i++) {
|
|
16489
|
-
const a = session.answers[i];
|
|
16889
|
+
const a = expectDefined11(session.answers[i]);
|
|
16490
16890
|
lines.push(``, `Q${i + 1}: ${a.question}`, `A${i + 1}: ${a.answer}`);
|
|
16491
16891
|
}
|
|
16492
16892
|
}
|
|
@@ -17228,6 +17628,12 @@ function truncate2(str, maxLen) {
|
|
|
17228
17628
|
}
|
|
17229
17629
|
|
|
17230
17630
|
// src/sdd/critical-path.ts
|
|
17631
|
+
function expectDefined12(value) {
|
|
17632
|
+
if (value === null || value === void 0) {
|
|
17633
|
+
throw new Error("Expected value to be defined");
|
|
17634
|
+
}
|
|
17635
|
+
return value;
|
|
17636
|
+
}
|
|
17231
17637
|
function analyzeCriticalPath(graph) {
|
|
17232
17638
|
const nodes = Array.from(graph.nodes.values());
|
|
17233
17639
|
const topoOrder = topologicalSort(graph);
|
|
@@ -17236,9 +17642,9 @@ function analyzeCriticalPath(graph) {
|
|
|
17236
17642
|
for (const edge of graph.edges) {
|
|
17237
17643
|
if (edge.type === "depends_on") {
|
|
17238
17644
|
if (!blockedByMap.has(edge.from)) blockedByMap.set(edge.from, /* @__PURE__ */ new Set());
|
|
17239
|
-
blockedByMap.get(edge.from)
|
|
17645
|
+
blockedByMap.get(edge.from)?.add(edge.to);
|
|
17240
17646
|
if (!blocksMap.has(edge.to)) blocksMap.set(edge.to, /* @__PURE__ */ new Set());
|
|
17241
|
-
blocksMap.get(edge.to)
|
|
17647
|
+
blocksMap.get(edge.to)?.add(edge.from);
|
|
17242
17648
|
}
|
|
17243
17649
|
}
|
|
17244
17650
|
const readyTasks = [];
|
|
@@ -17303,7 +17709,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
|
|
|
17303
17709
|
const visited = /* @__PURE__ */ new Set();
|
|
17304
17710
|
const queue = [taskId];
|
|
17305
17711
|
while (queue.length > 0) {
|
|
17306
|
-
const current = queue.shift();
|
|
17712
|
+
const current = expectDefined12(queue.shift());
|
|
17307
17713
|
const blocked = blocksMap.get(current);
|
|
17308
17714
|
if (!blocked) continue;
|
|
17309
17715
|
for (const id of blocked) {
|
|
@@ -17328,7 +17734,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
17328
17734
|
for (const [taskId, blockers] of blockedByMap) {
|
|
17329
17735
|
for (const blockerId of blockers) {
|
|
17330
17736
|
if (!blocksMap.has(blockerId)) blocksMap.set(blockerId, /* @__PURE__ */ new Set());
|
|
17331
|
-
blocksMap.get(blockerId)
|
|
17737
|
+
blocksMap.get(blockerId)?.add(taskId);
|
|
17332
17738
|
}
|
|
17333
17739
|
}
|
|
17334
17740
|
const n = allIds.length;
|
|
@@ -17349,7 +17755,7 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
17349
17755
|
if (!changed) break;
|
|
17350
17756
|
}
|
|
17351
17757
|
let maxDist = 0;
|
|
17352
|
-
let maxId = allIds[0];
|
|
17758
|
+
let maxId = expectDefined12(allIds[0]);
|
|
17353
17759
|
for (const id of allIds) {
|
|
17354
17760
|
const d = dist.get(id) ?? 0;
|
|
17355
17761
|
if (d > maxDist) {
|
|
@@ -17818,6 +18224,12 @@ var SddTaskDecomposer = class {
|
|
|
17818
18224
|
return nodes.some((n) => n.status === "blocked");
|
|
17819
18225
|
}
|
|
17820
18226
|
};
|
|
18227
|
+
function expectDefined13(value) {
|
|
18228
|
+
if (value === null || value === void 0) {
|
|
18229
|
+
throw new Error("Expected value to be defined");
|
|
18230
|
+
}
|
|
18231
|
+
return value;
|
|
18232
|
+
}
|
|
17821
18233
|
var SddParallelRun = class {
|
|
17822
18234
|
constructor(opts) {
|
|
17823
18235
|
this.opts = opts;
|
|
@@ -17918,8 +18330,10 @@ var SddParallelRun = class {
|
|
|
17918
18330
|
"\u2022 Do not ask before routine in-project tool use; if a permission gate appears, wait for that flow.",
|
|
17919
18331
|
"\u2022 Keep output concise \u2014 summarize changes, do not transcribe files."
|
|
17920
18332
|
].join("\n");
|
|
18333
|
+
if (!this.coordinator) throw new Error("SDD parallel runner requires a coordinator");
|
|
18334
|
+
const coordinator = this.coordinator;
|
|
17921
18335
|
const spawns = subagentIds.map(
|
|
17922
|
-
(subagentId) =>
|
|
18336
|
+
(subagentId) => coordinator.spawn({
|
|
17923
18337
|
id: subagentId,
|
|
17924
18338
|
name: subagentId,
|
|
17925
18339
|
role: "executor",
|
|
@@ -17927,12 +18341,12 @@ var SddParallelRun = class {
|
|
|
17927
18341
|
})
|
|
17928
18342
|
);
|
|
17929
18343
|
const spawnResults = await Promise.all(spawns);
|
|
17930
|
-
if (!spawnResults.every((r) => r.subagentId)) {
|
|
18344
|
+
if (!spawnResults.every((r) => Boolean(r.subagentId))) {
|
|
17931
18345
|
throw new Error("One or more subagent spawns failed");
|
|
17932
18346
|
}
|
|
17933
18347
|
const assignPromises = tasks.map((task, i) => {
|
|
17934
18348
|
const spec = {
|
|
17935
|
-
id: taskIds[i],
|
|
18349
|
+
id: taskIds[i] ?? task.id,
|
|
17936
18350
|
description: [
|
|
17937
18351
|
directivePreamble,
|
|
17938
18352
|
"",
|
|
@@ -17941,15 +18355,15 @@ var SddParallelRun = class {
|
|
|
17941
18355
|
"",
|
|
17942
18356
|
task.description
|
|
17943
18357
|
].join("\n"),
|
|
17944
|
-
subagentId: subagentIds[i],
|
|
18358
|
+
subagentId: subagentIds[i] ?? spawnResults[i]?.subagentId ?? task.id,
|
|
17945
18359
|
timeoutMs: this.timeoutMs
|
|
17946
18360
|
};
|
|
17947
|
-
return this.coordinator
|
|
18361
|
+
return this.coordinator?.assign(spec);
|
|
17948
18362
|
});
|
|
17949
18363
|
await Promise.all(assignPromises);
|
|
17950
18364
|
let results;
|
|
17951
18365
|
try {
|
|
17952
|
-
results = await
|
|
18366
|
+
results = await coordinator.awaitTasks(taskIds);
|
|
17953
18367
|
} catch (err) {
|
|
17954
18368
|
results = taskIds.map((id) => ({
|
|
17955
18369
|
subagentId: "",
|
|
@@ -17964,8 +18378,8 @@ var SddParallelRun = class {
|
|
|
17964
18378
|
const successCount = results.filter((r) => r.status === "success").length;
|
|
17965
18379
|
const failCount = results.length - successCount;
|
|
17966
18380
|
for (let i = 0; i < results.length; i++) {
|
|
17967
|
-
const result = results[i];
|
|
17968
|
-
const taskId = taskIds[i];
|
|
18381
|
+
const result = expectDefined13(results[i]);
|
|
18382
|
+
const taskId = expectDefined13(taskIds[i]);
|
|
17969
18383
|
if (result.status === "success") {
|
|
17970
18384
|
this.opts.tracker.updateNodeStatus(taskId, "completed");
|
|
17971
18385
|
} else {
|