@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
|
@@ -410,7 +410,7 @@ var InMemoryBridgeTransport = class {
|
|
|
410
410
|
}
|
|
411
411
|
subscribe(agentId, handler) {
|
|
412
412
|
if (!this.subs.has(agentId)) this.subs.set(agentId, /* @__PURE__ */ new Set());
|
|
413
|
-
this.subs.get(agentId)
|
|
413
|
+
this.subs.get(agentId)?.add(handler);
|
|
414
414
|
return () => this.subs.get(agentId)?.delete(handler);
|
|
415
415
|
}
|
|
416
416
|
close(agentId) {
|
|
@@ -528,6 +528,12 @@ function createMessage(type, from, payload, to) {
|
|
|
528
528
|
priority: "normal"
|
|
529
529
|
};
|
|
530
530
|
}
|
|
531
|
+
function expectDefined(value) {
|
|
532
|
+
if (value === null || value === void 0) {
|
|
533
|
+
throw new Error("Expected value to be defined");
|
|
534
|
+
}
|
|
535
|
+
return value;
|
|
536
|
+
}
|
|
531
537
|
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
532
538
|
var IS_WINDOWS = process.platform === "win32";
|
|
533
539
|
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
@@ -541,7 +547,7 @@ function globToRegex(pat) {
|
|
|
541
547
|
let i = 0;
|
|
542
548
|
let re = "^";
|
|
543
549
|
while (i < pat.length) {
|
|
544
|
-
const c = pat[i];
|
|
550
|
+
const c = expectDefined(pat[i]);
|
|
545
551
|
if (c === "*") {
|
|
546
552
|
if (pat[i + 1] === "*") {
|
|
547
553
|
re += ".*";
|
|
@@ -580,7 +586,7 @@ function globToRegex(pat) {
|
|
|
580
586
|
}
|
|
581
587
|
function baseDir(pat) {
|
|
582
588
|
let i = pat.length - 1;
|
|
583
|
-
while (i >= 0 && !GLOB_CHARS.has(pat[i]) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
589
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
584
590
|
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
585
591
|
return cut < 0 ? "." : pat.slice(0, cut);
|
|
586
592
|
}
|
|
@@ -811,7 +817,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
811
817
|
this.emit("session.error", error);
|
|
812
818
|
throw error;
|
|
813
819
|
}
|
|
814
|
-
for (const result of results
|
|
820
|
+
for (const result of results?.flat() ?? []) {
|
|
815
821
|
await this.parseAndEmit(result);
|
|
816
822
|
}
|
|
817
823
|
const report = this.assembleReport();
|
|
@@ -872,7 +878,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
872
878
|
}
|
|
873
879
|
budgetForRole(role) {
|
|
874
880
|
if (this.options.budgetOverrides?.[role]) {
|
|
875
|
-
return this.options.budgetOverrides[role];
|
|
881
|
+
return this.options.budgetOverrides[role] ?? { maxIterations: 0, maxToolCalls: 0, timeoutMs: 0 };
|
|
876
882
|
}
|
|
877
883
|
const defaults = {
|
|
878
884
|
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
@@ -3461,6 +3467,11 @@ function getAgentDefinition(role) {
|
|
|
3461
3467
|
|
|
3462
3468
|
// src/coordination/dispatcher.ts
|
|
3463
3469
|
var DEFAULT_DISPATCH_ROLE = "executor";
|
|
3470
|
+
var FALLBACK_DEFINITION = {
|
|
3471
|
+
config: { role: "unknown", name: "Unknown Agent" },
|
|
3472
|
+
budget: {},
|
|
3473
|
+
capability: { phase: "meta", summary: "", keywords: [] }
|
|
3474
|
+
};
|
|
3464
3475
|
function normalize(text) {
|
|
3465
3476
|
return ` ${text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
|
|
3466
3477
|
}
|
|
@@ -3488,7 +3499,7 @@ function scoreAgents(task, catalog = AGENT_CATALOG) {
|
|
|
3488
3499
|
}
|
|
3489
3500
|
function heuristicConfidence(candidates) {
|
|
3490
3501
|
if (candidates.length === 0) return 0;
|
|
3491
|
-
const top = candidates[0]
|
|
3502
|
+
const top = candidates[0]?.score ?? 0;
|
|
3492
3503
|
const second = candidates[1]?.score ?? 0;
|
|
3493
3504
|
const strength = Math.min(1, top / 3);
|
|
3494
3505
|
const margin = (top - second + 1) / (top + 1);
|
|
@@ -3504,7 +3515,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
3504
3515
|
if (top && confidence >= threshold) {
|
|
3505
3516
|
return {
|
|
3506
3517
|
role: top.role,
|
|
3507
|
-
definition: catalog[top.role],
|
|
3518
|
+
definition: catalog[top.role] ?? FALLBACK_DEFINITION,
|
|
3508
3519
|
confidence,
|
|
3509
3520
|
method: "heuristic",
|
|
3510
3521
|
reason: `Matched keywords: ${top.matched.slice(0, 4).join(", ")}`,
|
|
@@ -3512,7 +3523,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
3512
3523
|
};
|
|
3513
3524
|
}
|
|
3514
3525
|
if (opts.classifier) {
|
|
3515
|
-
const pool = (candidates.length > 0 ? candidates.slice(0, maxCandidates).map((c) => catalog[c.role]) : ALL_AGENT_DEFINITIONS).map((d) => ({
|
|
3526
|
+
const pool = (candidates.length > 0 ? candidates.slice(0, maxCandidates).map((c) => catalog[c.role] ?? FALLBACK_DEFINITION) : ALL_AGENT_DEFINITIONS).map((d) => ({
|
|
3516
3527
|
role: d.config.role,
|
|
3517
3528
|
name: d.config.name,
|
|
3518
3529
|
summary: d.capability.summary
|
|
@@ -3522,7 +3533,7 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
3522
3533
|
if (choice && catalog[choice.role]) {
|
|
3523
3534
|
return {
|
|
3524
3535
|
role: choice.role,
|
|
3525
|
-
definition: catalog[choice.role],
|
|
3536
|
+
definition: catalog[choice.role] ?? FALLBACK_DEFINITION,
|
|
3526
3537
|
confidence: 1,
|
|
3527
3538
|
method: "llm",
|
|
3528
3539
|
reason: choice.reason ?? "Selected by LLM classifier",
|
|
@@ -3535,17 +3546,17 @@ async function dispatchAgent(task, opts = {}) {
|
|
|
3535
3546
|
if (top) {
|
|
3536
3547
|
return {
|
|
3537
3548
|
role: top.role,
|
|
3538
|
-
definition: catalog[top.role],
|
|
3549
|
+
definition: catalog[top.role] ?? FALLBACK_DEFINITION,
|
|
3539
3550
|
confidence,
|
|
3540
3551
|
method: "heuristic",
|
|
3541
3552
|
reason: `Weak match (${top.matched.slice(0, 3).join(", ") || "low signal"})`,
|
|
3542
3553
|
alternatives: candidates.slice(1, maxCandidates)
|
|
3543
3554
|
};
|
|
3544
3555
|
}
|
|
3545
|
-
const fallbackRole = catalog[DEFAULT_DISPATCH_ROLE] ? DEFAULT_DISPATCH_ROLE : Object.keys(catalog)[0];
|
|
3556
|
+
const fallbackRole = catalog[DEFAULT_DISPATCH_ROLE] ? DEFAULT_DISPATCH_ROLE : Object.keys(catalog)[0] ?? DEFAULT_DISPATCH_ROLE;
|
|
3546
3557
|
return {
|
|
3547
3558
|
role: fallbackRole,
|
|
3548
|
-
definition: catalog[fallbackRole],
|
|
3559
|
+
definition: catalog[fallbackRole] ?? FALLBACK_DEFINITION,
|
|
3549
3560
|
confidence: 0,
|
|
3550
3561
|
method: "fallback",
|
|
3551
3562
|
reason: "No keyword signal; defaulting to the generalist Executor",
|
|
@@ -3624,7 +3635,7 @@ function makeSpawnTool(director, roster) {
|
|
|
3624
3635
|
});
|
|
3625
3636
|
const dispatchRole = dispatchResult.role;
|
|
3626
3637
|
if (roster?.[dispatchRole]) {
|
|
3627
|
-
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole]);
|
|
3638
|
+
cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole] ?? {});
|
|
3628
3639
|
} else {
|
|
3629
3640
|
const def = dispatchResult.definition;
|
|
3630
3641
|
cfg = {
|
|
@@ -4451,16 +4462,16 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4451
4462
|
}
|
|
4452
4463
|
if (exceeded.length === 0) return [];
|
|
4453
4464
|
if (!this._onThreshold) {
|
|
4454
|
-
const first2 = exceeded[0];
|
|
4465
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
4455
4466
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
4456
4467
|
}
|
|
4457
4468
|
if (this._mode === "sync") {
|
|
4458
|
-
const first2 = exceeded[0];
|
|
4469
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
4459
4470
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
4460
4471
|
}
|
|
4461
4472
|
const bus = this._events;
|
|
4462
4473
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
4463
|
-
const first2 = exceeded[0];
|
|
4474
|
+
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
4464
4475
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
4465
4476
|
}
|
|
4466
4477
|
for (const entry of exceeded) {
|
|
@@ -4468,8 +4479,9 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4468
4479
|
const decision2 = this._negotiateExtension(entry.kind, exceeded);
|
|
4469
4480
|
this._pendingNegotiations.set(entry.kind, decision2);
|
|
4470
4481
|
}
|
|
4471
|
-
const first = exceeded[0];
|
|
4482
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
4472
4483
|
const decision = this._pendingNegotiations.get(first.kind);
|
|
4484
|
+
if (!decision) throw new Error(`No pending negotiation for ${first.kind}`);
|
|
4473
4485
|
throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
|
|
4474
4486
|
}
|
|
4475
4487
|
/**
|
|
@@ -4491,8 +4503,11 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4491
4503
|
* a fresh signal.
|
|
4492
4504
|
*/
|
|
4493
4505
|
async _negotiateExtension(kind, exceeded) {
|
|
4506
|
+
if (!this._onThreshold) {
|
|
4507
|
+
return "stop";
|
|
4508
|
+
}
|
|
4494
4509
|
try {
|
|
4495
|
-
const first = exceeded[0];
|
|
4510
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
4496
4511
|
const result = this._onThreshold({
|
|
4497
4512
|
kind: first.kind,
|
|
4498
4513
|
used: first.used,
|
|
@@ -5515,6 +5530,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5515
5530
|
takeNextDispatchableTask() {
|
|
5516
5531
|
for (let i = 0; i < this.pendingTasks.length; i++) {
|
|
5517
5532
|
const task = this.pendingTasks[i];
|
|
5533
|
+
if (!task) continue;
|
|
5518
5534
|
const subagentId = task.subagentId ? this.isIdleSubagent(task.subagentId) ? task.subagentId : null : this.findIdleSubagent();
|
|
5519
5535
|
if (!subagentId) continue;
|
|
5520
5536
|
this.pendingTasks.splice(i, 1);
|
|
@@ -5706,14 +5722,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5706
5722
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
5707
5723
|
if (idleExceeded && !wallExceeded) {
|
|
5708
5724
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
5709
|
-
reject(new BudgetExceededError("timeout", idleLimit, budget.idleMs()));
|
|
5725
|
+
reject(new BudgetExceededError("timeout", idleLimit ?? 0, budget.idleMs()));
|
|
5710
5726
|
return;
|
|
5711
5727
|
}
|
|
5712
5728
|
if (!wallExceeded) {
|
|
5713
5729
|
scheduleNext();
|
|
5714
5730
|
return;
|
|
5715
5731
|
}
|
|
5716
|
-
const limit = wallLimit;
|
|
5732
|
+
const limit = wallLimit ?? 0;
|
|
5717
5733
|
if (!budget.onThreshold) {
|
|
5718
5734
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
5719
5735
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -7131,6 +7147,7 @@ function createDelegateTool(opts) {
|
|
|
7131
7147
|
if (typeof i.task !== "string" || !i.task.trim()) {
|
|
7132
7148
|
return { ok: false, error: "`task` is required." };
|
|
7133
7149
|
}
|
|
7150
|
+
const target = i.role ?? i.name ?? "subagent";
|
|
7134
7151
|
try {
|
|
7135
7152
|
let director = await opts.host.ensureDirector();
|
|
7136
7153
|
if (!director) {
|
|
@@ -7191,6 +7208,7 @@ function createDelegateTool(opts) {
|
|
|
7191
7208
|
if (!cfg.timeoutMs) {
|
|
7192
7209
|
cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
7193
7210
|
}
|
|
7211
|
+
opts.events?.emit("delegate.started", { target, task: i.task });
|
|
7194
7212
|
const subagentId = await director.spawn(cfg);
|
|
7195
7213
|
const taskId = await director.assign({
|
|
7196
7214
|
id: `${randomUUID()}`,
|
|
@@ -7225,6 +7243,17 @@ function createDelegateTool(opts) {
|
|
|
7225
7243
|
});
|
|
7226
7244
|
if ("__timeout" in result) {
|
|
7227
7245
|
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
7246
|
+
opts.events?.emit("delegate.completed", {
|
|
7247
|
+
target,
|
|
7248
|
+
task: i.task,
|
|
7249
|
+
ok: false,
|
|
7250
|
+
status: "host_timeout",
|
|
7251
|
+
summary: `[${target}] timed out \u2014 no result within ${Math.round(timeoutMs / 1e3)}s`,
|
|
7252
|
+
durationMs: timeoutMs,
|
|
7253
|
+
iterations: partial2?.events ?? 0,
|
|
7254
|
+
toolCalls: partial2?.toolUsesObserved ?? 0,
|
|
7255
|
+
subagentId
|
|
7256
|
+
});
|
|
7228
7257
|
return {
|
|
7229
7258
|
ok: false,
|
|
7230
7259
|
stopReason: "host_timeout",
|
|
@@ -7241,6 +7270,24 @@ function createDelegateTool(opts) {
|
|
|
7241
7270
|
const retryable = result.error?.retryable;
|
|
7242
7271
|
const backoffMs = result.error?.backoffMs;
|
|
7243
7272
|
const summary = buildDelegateSummary(i.role, result);
|
|
7273
|
+
let costUsd;
|
|
7274
|
+
try {
|
|
7275
|
+
costUsd = dir.snapshot().perSubagent[result.subagentId]?.cost;
|
|
7276
|
+
} catch {
|
|
7277
|
+
costUsd = void 0;
|
|
7278
|
+
}
|
|
7279
|
+
opts.events?.emit("delegate.completed", {
|
|
7280
|
+
target,
|
|
7281
|
+
task: i.task,
|
|
7282
|
+
ok: result.status === "success",
|
|
7283
|
+
status: result.status,
|
|
7284
|
+
summary,
|
|
7285
|
+
durationMs: result.durationMs,
|
|
7286
|
+
iterations: result.iterations,
|
|
7287
|
+
toolCalls: result.toolCalls,
|
|
7288
|
+
costUsd,
|
|
7289
|
+
subagentId: result.subagentId
|
|
7290
|
+
});
|
|
7244
7291
|
return {
|
|
7245
7292
|
ok: result.status === "success",
|
|
7246
7293
|
status: result.status,
|
|
@@ -7262,10 +7309,21 @@ function createDelegateTool(opts) {
|
|
|
7262
7309
|
summary
|
|
7263
7310
|
};
|
|
7264
7311
|
} catch (err) {
|
|
7312
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7313
|
+
opts.events?.emit("delegate.completed", {
|
|
7314
|
+
target,
|
|
7315
|
+
task: i.task,
|
|
7316
|
+
ok: false,
|
|
7317
|
+
status: "error",
|
|
7318
|
+
summary: `[${target}] failed \u2014 ${message}`,
|
|
7319
|
+
durationMs: 0,
|
|
7320
|
+
iterations: 0,
|
|
7321
|
+
toolCalls: 0
|
|
7322
|
+
});
|
|
7265
7323
|
return {
|
|
7266
7324
|
ok: false,
|
|
7267
7325
|
stopReason: "error",
|
|
7268
|
-
error:
|
|
7326
|
+
error: message
|
|
7269
7327
|
};
|
|
7270
7328
|
}
|
|
7271
7329
|
}
|
|
@@ -7571,6 +7629,12 @@ function defaultFormatTaskInput(task) {
|
|
|
7571
7629
|
}
|
|
7572
7630
|
|
|
7573
7631
|
// src/utils/message-invariants.ts
|
|
7632
|
+
function expectDefined2(value) {
|
|
7633
|
+
if (value === null || value === void 0) {
|
|
7634
|
+
throw new Error("Expected value to be defined");
|
|
7635
|
+
}
|
|
7636
|
+
return value;
|
|
7637
|
+
}
|
|
7574
7638
|
function repairToolUseAdjacency(messages) {
|
|
7575
7639
|
const removedToolUses = [];
|
|
7576
7640
|
const removedToolResults = [];
|
|
@@ -7578,7 +7642,7 @@ function repairToolUseAdjacency(messages) {
|
|
|
7578
7642
|
let changed = false;
|
|
7579
7643
|
const out = [];
|
|
7580
7644
|
for (let i = 0; i < messages.length; i++) {
|
|
7581
|
-
const original = messages[i];
|
|
7645
|
+
const original = expectDefined2(messages[i]);
|
|
7582
7646
|
let msg = original;
|
|
7583
7647
|
if (hasToolUse(msg)) {
|
|
7584
7648
|
const nextIds = toolResultIds(messages[i + 1]);
|
|
@@ -7663,7 +7727,23 @@ function isEmptyMessage(msg) {
|
|
|
7663
7727
|
}
|
|
7664
7728
|
|
|
7665
7729
|
// src/storage/session-store.ts
|
|
7666
|
-
|
|
7730
|
+
function expectDefined3(value) {
|
|
7731
|
+
if (value === null || value === void 0) {
|
|
7732
|
+
throw new Error("Expected value to be defined");
|
|
7733
|
+
}
|
|
7734
|
+
return value;
|
|
7735
|
+
}
|
|
7736
|
+
function sanitizeModel(model) {
|
|
7737
|
+
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
7738
|
+
}
|
|
7739
|
+
function generateSessionId(startedAt, model) {
|
|
7740
|
+
const date = startedAt.slice(0, 10);
|
|
7741
|
+
const time = startedAt.slice(11, 19).replace(/:/g, "-");
|
|
7742
|
+
const suffix = randomBytes(2).toString("hex");
|
|
7743
|
+
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
7744
|
+
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
7745
|
+
}
|
|
7746
|
+
var DefaultSessionStore = class _DefaultSessionStore {
|
|
7667
7747
|
dir;
|
|
7668
7748
|
events;
|
|
7669
7749
|
secretScrubber;
|
|
@@ -7672,19 +7752,29 @@ var DefaultSessionStore = class {
|
|
|
7672
7752
|
this.events = opts.events;
|
|
7673
7753
|
this.secretScrubber = opts.secretScrubber;
|
|
7674
7754
|
}
|
|
7755
|
+
/** Absolute path to the session index file. */
|
|
7756
|
+
get indexFile() {
|
|
7757
|
+
return path4.join(this.dir, "_index.jsonl");
|
|
7758
|
+
}
|
|
7675
7759
|
/** Join session ID to its absolute path within the store directory. */
|
|
7676
7760
|
sessionPath(id, ext) {
|
|
7677
7761
|
return path4.join(this.dir, `${id}${ext}`);
|
|
7678
7762
|
}
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7763
|
+
/**
|
|
7764
|
+
* Ensure the directory implied by the session ID exists. When the ID
|
|
7765
|
+
* contains a date prefix like `2026-06-06/...`, this creates the date
|
|
7766
|
+
* subdirectory so sessions group naturally by day.
|
|
7767
|
+
*/
|
|
7768
|
+
async ensureShardDir(id) {
|
|
7769
|
+
const dirPath = path4.dirname(path4.join(this.dir, id));
|
|
7770
|
+
await ensureDir(dirPath);
|
|
7771
|
+
return dirPath;
|
|
7682
7772
|
}
|
|
7683
7773
|
async create(meta) {
|
|
7684
7774
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7685
|
-
const id = meta.id
|
|
7775
|
+
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
7686
7776
|
const shardDir = await this.ensureShardDir(id);
|
|
7687
|
-
const file = path4.join(shardDir, `${id}.jsonl`);
|
|
7777
|
+
const file = path4.join(shardDir, `${path4.basename(id)}.jsonl`);
|
|
7688
7778
|
let handle;
|
|
7689
7779
|
try {
|
|
7690
7780
|
handle = await fsp6.open(file, "a", 384);
|
|
@@ -7698,7 +7788,8 @@ var DefaultSessionStore = class {
|
|
|
7698
7788
|
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
7699
7789
|
dir: shardDir,
|
|
7700
7790
|
filePath: file,
|
|
7701
|
-
secretScrubber: this.secretScrubber
|
|
7791
|
+
secretScrubber: this.secretScrubber,
|
|
7792
|
+
onClose: (s) => this.appendToIndex(s)
|
|
7702
7793
|
});
|
|
7703
7794
|
} catch (err) {
|
|
7704
7795
|
await handle.close().catch(() => {
|
|
@@ -7729,7 +7820,7 @@ var DefaultSessionStore = class {
|
|
|
7729
7820
|
provider: data.metadata.provider
|
|
7730
7821
|
},
|
|
7731
7822
|
this.events,
|
|
7732
|
-
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
|
|
7823
|
+
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber, onClose: (s) => this.appendToIndex(s) }
|
|
7733
7824
|
);
|
|
7734
7825
|
return { writer, data };
|
|
7735
7826
|
} catch (err) {
|
|
@@ -7759,6 +7850,15 @@ var DefaultSessionStore = class {
|
|
|
7759
7850
|
async list(limit = 20) {
|
|
7760
7851
|
try {
|
|
7761
7852
|
await ensureDir(this.dir);
|
|
7853
|
+
const indexed = await this.readIndex();
|
|
7854
|
+
if (indexed.length > 0) {
|
|
7855
|
+
indexed.sort((a, b) => {
|
|
7856
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
7857
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
7858
|
+
return a.id.localeCompare(b.id);
|
|
7859
|
+
});
|
|
7860
|
+
return indexed.slice(0, limit);
|
|
7861
|
+
}
|
|
7762
7862
|
const ids = await this.collectSessionIds(this.dir);
|
|
7763
7863
|
const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
7764
7864
|
const out = sessions.filter((s) => s !== null);
|
|
@@ -7772,16 +7872,121 @@ var DefaultSessionStore = class {
|
|
|
7772
7872
|
return [];
|
|
7773
7873
|
}
|
|
7774
7874
|
}
|
|
7775
|
-
|
|
7776
|
-
|
|
7875
|
+
// ── Session index (_index.jsonl) ─────────────────────────────────────────
|
|
7876
|
+
//
|
|
7877
|
+
// One JSON line per closed session, appended atomically on close().
|
|
7878
|
+
// When a session is deleted, a tombstone {action:"delete",id:"..."} is
|
|
7879
|
+
// appended. On read, tombstones filter out matching session entries.
|
|
7880
|
+
// This keeps listing O(lines-in-index) instead of O(files-on-disk).
|
|
7881
|
+
//
|
|
7882
|
+
// The index auto-compacts every N appends to prevent unbounded growth
|
|
7883
|
+
// from tombstones and duplicate entries (resume cycles).
|
|
7884
|
+
indexAppendCount = 0;
|
|
7885
|
+
static COMPACT_EVERY = 30;
|
|
7886
|
+
/** Append a session summary to the index. */
|
|
7887
|
+
async appendToIndex(summary) {
|
|
7888
|
+
try {
|
|
7889
|
+
await ensureDir(this.dir);
|
|
7890
|
+
const line = JSON.stringify(summary) + "\n";
|
|
7891
|
+
await fsp6.appendFile(this.indexFile, line, "utf8");
|
|
7892
|
+
this.indexAppendCount++;
|
|
7893
|
+
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
7894
|
+
await this.compactIndex();
|
|
7895
|
+
this.indexAppendCount = 0;
|
|
7896
|
+
}
|
|
7897
|
+
} catch {
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
/** Append a tombstone entry for a deleted session. */
|
|
7901
|
+
async writeTombstone(id) {
|
|
7902
|
+
try {
|
|
7903
|
+
await ensureDir(this.dir);
|
|
7904
|
+
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
7905
|
+
await fsp6.appendFile(this.indexFile, line, "utf8");
|
|
7906
|
+
this.indexAppendCount++;
|
|
7907
|
+
} catch {
|
|
7908
|
+
}
|
|
7909
|
+
}
|
|
7910
|
+
/**
|
|
7911
|
+
* Compact the index: read all entries, drop tombstones, deduplicate
|
|
7912
|
+
* (keep latest per session), and rewrite. Atomic via temp+rename.
|
|
7913
|
+
*/
|
|
7914
|
+
async compactIndex() {
|
|
7915
|
+
const entries = await this.readIndex();
|
|
7916
|
+
if (entries.length === 0) return;
|
|
7917
|
+
const tmp = `${this.indexFile}.compact.tmp`;
|
|
7918
|
+
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
7919
|
+
await fsp6.writeFile(tmp, lines, "utf8");
|
|
7920
|
+
await fsp6.rename(tmp, this.indexFile);
|
|
7921
|
+
}
|
|
7922
|
+
/**
|
|
7923
|
+
* Read the index file and return deduplicated session summaries.
|
|
7924
|
+
* Entries with a matching tombstone are filtered out.
|
|
7925
|
+
* Returns empty array when the index doesn't exist or is corrupt.
|
|
7926
|
+
*/
|
|
7927
|
+
async readIndex() {
|
|
7928
|
+
let raw;
|
|
7929
|
+
try {
|
|
7930
|
+
raw = await fsp6.readFile(this.indexFile, "utf8");
|
|
7931
|
+
} catch {
|
|
7932
|
+
return [];
|
|
7933
|
+
}
|
|
7934
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
7935
|
+
const seen = /* @__PURE__ */ new Map();
|
|
7936
|
+
for (const line of raw.split("\n")) {
|
|
7937
|
+
if (!line.trim()) continue;
|
|
7938
|
+
try {
|
|
7939
|
+
const entry = JSON.parse(line);
|
|
7940
|
+
if (entry.action === "delete" && entry.id) {
|
|
7941
|
+
deleted.add(entry.id);
|
|
7942
|
+
seen.delete(entry.id);
|
|
7943
|
+
continue;
|
|
7944
|
+
}
|
|
7945
|
+
if (entry.id && !deleted.has(entry.id)) {
|
|
7946
|
+
seen.set(entry.id, entry);
|
|
7947
|
+
}
|
|
7948
|
+
} catch {
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
return Array.from(seen.values());
|
|
7952
|
+
}
|
|
7953
|
+
/**
|
|
7954
|
+
* Rebuild the index from disk by scanning all sessions and writing a
|
|
7955
|
+
* fresh _index.jsonl. Useful after manual cleanup or index corruption.
|
|
7956
|
+
*/
|
|
7957
|
+
async rebuildIndex() {
|
|
7958
|
+
const ids = await this.collectSessionIds(this.dir);
|
|
7959
|
+
const summaries = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
7960
|
+
const valid = summaries.filter((s) => s !== null);
|
|
7961
|
+
const tmp = `${this.indexFile}.tmp`;
|
|
7962
|
+
const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
7963
|
+
await fsp6.writeFile(tmp, lines, "utf8");
|
|
7964
|
+
await fsp6.rename(tmp, this.indexFile);
|
|
7965
|
+
return valid.length;
|
|
7966
|
+
}
|
|
7967
|
+
/** Recursively collect session IDs from date-shard subdirectories.
|
|
7968
|
+
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
|
|
7969
|
+
* Skips `.jsonl`/`.summary.json` root files, dot-files, and
|
|
7970
|
+
* sub-directories that belong to fleet/subagent sessions. */
|
|
7971
|
+
async collectSessionIds(dir, prefix = "", depth = 0) {
|
|
7777
7972
|
const ids = [];
|
|
7778
|
-
|
|
7973
|
+
let entries;
|
|
7974
|
+
try {
|
|
7975
|
+
entries = await fsp6.readdir(dir, { withFileTypes: true });
|
|
7976
|
+
} catch {
|
|
7977
|
+
return ids;
|
|
7978
|
+
}
|
|
7779
7979
|
for (const entry of entries) {
|
|
7780
|
-
|
|
7980
|
+
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
7981
|
+
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
7982
|
+
continue;
|
|
7781
7983
|
if (entry.isDirectory()) {
|
|
7782
|
-
|
|
7984
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
7985
|
+
ids.push(...await this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1));
|
|
7783
7986
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
7784
|
-
|
|
7987
|
+
if (entry.name === "_index.jsonl") continue;
|
|
7988
|
+
const base = entry.name.replace(/\.jsonl$/, "");
|
|
7989
|
+
ids.push(prefix ? `${prefix}/${base}` : base);
|
|
7785
7990
|
}
|
|
7786
7991
|
}
|
|
7787
7992
|
return ids;
|
|
@@ -7804,9 +8009,70 @@ var DefaultSessionStore = class {
|
|
|
7804
8009
|
return summary;
|
|
7805
8010
|
}
|
|
7806
8011
|
}
|
|
7807
|
-
|
|
7808
|
-
|
|
8012
|
+
/**
|
|
8013
|
+
* Delete a session and all associated files: JSONL, summary, plan/todos
|
|
8014
|
+
* sidecars, and the session directory (fleet.json, shared/, subagents/).
|
|
8015
|
+
*/
|
|
8016
|
+
async deleteSession(id) {
|
|
8017
|
+
await fsp6.unlink(this.sessionPath(id, ".jsonl")).catch(() => void 0);
|
|
7809
8018
|
await fsp6.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
|
|
8019
|
+
const shardDir = path4.dirname(path4.join(this.dir, id));
|
|
8020
|
+
const base = path4.basename(id);
|
|
8021
|
+
for (const ext of [".plan.json", ".todos.json"]) {
|
|
8022
|
+
await fsp6.unlink(path4.join(shardDir, `${base}${ext}`)).catch(() => void 0);
|
|
8023
|
+
}
|
|
8024
|
+
const sessDir = path4.join(shardDir, base);
|
|
8025
|
+
await fsp6.rm(sessDir, { recursive: true, force: true }).catch(() => void 0);
|
|
8026
|
+
await this.writeTombstone(id);
|
|
8027
|
+
}
|
|
8028
|
+
async delete(id) {
|
|
8029
|
+
await this.deleteSession(id);
|
|
8030
|
+
}
|
|
8031
|
+
async prune(maxAgeDays = 30) {
|
|
8032
|
+
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
8033
|
+
let deleted = 0;
|
|
8034
|
+
let activeSessionId = null;
|
|
8035
|
+
try {
|
|
8036
|
+
const raw = await fsp6.readFile(path4.join(this.dir, "active.json"), "utf8");
|
|
8037
|
+
const active = JSON.parse(raw);
|
|
8038
|
+
activeSessionId = active.sessionId ?? null;
|
|
8039
|
+
} catch {
|
|
8040
|
+
}
|
|
8041
|
+
const entries = await fsp6.readdir(this.dir, { withFileTypes: true }).catch(() => []);
|
|
8042
|
+
for (const entry of entries) {
|
|
8043
|
+
if (!entry.isDirectory()) continue;
|
|
8044
|
+
const dateDir = path4.join(this.dir, entry.name);
|
|
8045
|
+
const files = await fsp6.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
8046
|
+
for (const file of files) {
|
|
8047
|
+
if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
|
|
8048
|
+
const jsonlPath = path4.join(dateDir, file.name);
|
|
8049
|
+
try {
|
|
8050
|
+
const stat4 = await fsp6.stat(jsonlPath);
|
|
8051
|
+
if (stat4.mtimeMs >= cutoff) continue;
|
|
8052
|
+
} catch {
|
|
8053
|
+
continue;
|
|
8054
|
+
}
|
|
8055
|
+
const id = `${entry.name}/${file.name.replace(/\.jsonl$/, "")}`;
|
|
8056
|
+
if (activeSessionId && id === activeSessionId) continue;
|
|
8057
|
+
await this.deleteSession(id);
|
|
8058
|
+
deleted++;
|
|
8059
|
+
}
|
|
8060
|
+
}
|
|
8061
|
+
if (deleted > 0) {
|
|
8062
|
+
await this.compactIndex().catch(() => void 0);
|
|
8063
|
+
}
|
|
8064
|
+
for (const entry of entries) {
|
|
8065
|
+
if (!entry.isDirectory()) continue;
|
|
8066
|
+
const dateDir = path4.join(this.dir, entry.name);
|
|
8067
|
+
try {
|
|
8068
|
+
const remaining = await fsp6.readdir(dateDir);
|
|
8069
|
+
if (remaining.length === 0) {
|
|
8070
|
+
await fsp6.rmdir(dateDir).catch(() => void 0);
|
|
8071
|
+
}
|
|
8072
|
+
} catch {
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
return deleted;
|
|
7810
8076
|
}
|
|
7811
8077
|
async clearHistory(id) {
|
|
7812
8078
|
await this.ensureShardDir(id);
|
|
@@ -7828,13 +8094,42 @@ var DefaultSessionStore = class {
|
|
|
7828
8094
|
const data = await this.load(id);
|
|
7829
8095
|
const firstUser = data.events.find((e) => e.type === "user_input");
|
|
7830
8096
|
const title = firstUser && firstUser.type === "user_input" ? userInputTitle(firstUser.content) : "(empty session)";
|
|
8097
|
+
let iterationCount = 0;
|
|
8098
|
+
let toolCallCount = 0;
|
|
8099
|
+
let toolErrorCount = 0;
|
|
8100
|
+
let fileChangeCount = 0;
|
|
8101
|
+
const toolBreakdown = {};
|
|
8102
|
+
let outcome = void 0;
|
|
8103
|
+
const lastEvent = data.events[data.events.length - 1];
|
|
8104
|
+
for (const e of data.events) {
|
|
8105
|
+
if (e.type === "in_flight_start") iterationCount++;
|
|
8106
|
+
else if (e.type === "tool_call_start") {
|
|
8107
|
+
toolCallCount++;
|
|
8108
|
+
toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;
|
|
8109
|
+
} else if (e.type === "tool_result" && e.isError) toolErrorCount++;
|
|
8110
|
+
else if (e.type === "file_snapshot") fileChangeCount += e.files.length;
|
|
8111
|
+
}
|
|
8112
|
+
if (lastEvent?.type === "session_end") {
|
|
8113
|
+
outcome = "completed";
|
|
8114
|
+
} else if (lastEvent?.type === "in_flight_start") {
|
|
8115
|
+
outcome = "aborted";
|
|
8116
|
+
} else if (data.events.some((e) => e.type === "error")) {
|
|
8117
|
+
outcome = "error";
|
|
8118
|
+
}
|
|
7831
8119
|
return {
|
|
7832
8120
|
id,
|
|
7833
8121
|
title,
|
|
7834
8122
|
startedAt: data.metadata.startedAt,
|
|
8123
|
+
endedAt: data.metadata.endedAt,
|
|
7835
8124
|
model: data.metadata.model ?? "unknown",
|
|
7836
8125
|
provider: data.metadata.provider ?? "unknown",
|
|
7837
|
-
tokenTotal: data.usage.input + data.usage.output
|
|
8126
|
+
tokenTotal: data.usage.input + data.usage.output,
|
|
8127
|
+
iterationCount: iterationCount > 0 ? iterationCount : void 0,
|
|
8128
|
+
toolCallCount: toolCallCount > 0 ? toolCallCount : void 0,
|
|
8129
|
+
toolErrorCount: toolErrorCount > 0 ? toolErrorCount : void 0,
|
|
8130
|
+
fileChangeCount: fileChangeCount > 0 ? fileChangeCount : void 0,
|
|
8131
|
+
toolBreakdown: Object.keys(toolBreakdown).length > 0 ? toolBreakdown : {},
|
|
8132
|
+
outcome
|
|
7838
8133
|
};
|
|
7839
8134
|
} catch {
|
|
7840
8135
|
return {
|
|
@@ -7933,9 +8228,10 @@ var FileSessionWriter = class {
|
|
|
7933
8228
|
this.meta = meta;
|
|
7934
8229
|
this.events = events;
|
|
7935
8230
|
this.resumed = opts.resumed ?? false;
|
|
7936
|
-
this.manifestFile = opts.dir ? path4.join(opts.dir, `${id}.summary.json`) : "";
|
|
8231
|
+
this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
|
|
7937
8232
|
this.filePath = opts.filePath ?? "";
|
|
7938
8233
|
this.secretScrubber = opts.secretScrubber;
|
|
8234
|
+
this.onCloseCb = opts.onClose;
|
|
7939
8235
|
this.summary = {
|
|
7940
8236
|
id,
|
|
7941
8237
|
title: "(empty session)",
|
|
@@ -7965,6 +8261,15 @@ var FileSessionWriter = class {
|
|
|
7965
8261
|
appendFailCount = 0;
|
|
7966
8262
|
lastAppendWarnAt = 0;
|
|
7967
8263
|
secretScrubber;
|
|
8264
|
+
onCloseCb;
|
|
8265
|
+
// ── Enriched summary tracking ──────────────────────────────────────────
|
|
8266
|
+
iterationCount = 0;
|
|
8267
|
+
toolCallCount = 0;
|
|
8268
|
+
toolErrorCount = 0;
|
|
8269
|
+
toolBreakdown = {};
|
|
8270
|
+
fileChangeCount = 0;
|
|
8271
|
+
compactionCount = 0;
|
|
8272
|
+
outcome = void 0;
|
|
7968
8273
|
/**
|
|
7969
8274
|
* Scrub secrets out of conversation-turn events before they are observed
|
|
7970
8275
|
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
@@ -8042,8 +8347,22 @@ var FileSessionWriter = class {
|
|
|
8042
8347
|
observeForSummary(event) {
|
|
8043
8348
|
if (event.type === "tool_use") {
|
|
8044
8349
|
this.openToolUses.add(event.id);
|
|
8350
|
+
} else if (event.type === "tool_call_start") {
|
|
8351
|
+
this.toolCallCount++;
|
|
8352
|
+
this.toolBreakdown[event.name] = (this.toolBreakdown[event.name] ?? 0) + 1;
|
|
8045
8353
|
} else if (event.type === "tool_result") {
|
|
8046
8354
|
this.openToolUses.delete(event.id);
|
|
8355
|
+
if (event.isError) {
|
|
8356
|
+
this.toolErrorCount++;
|
|
8357
|
+
this.outcome = "error";
|
|
8358
|
+
}
|
|
8359
|
+
} else if (event.type === "file_snapshot") {
|
|
8360
|
+
this.fileChangeCount += event.files.length;
|
|
8361
|
+
} else if (event.type === "compaction") {
|
|
8362
|
+
this.compactionCount++;
|
|
8363
|
+
}
|
|
8364
|
+
if (event.type === "error" || event.type === "provider_error") {
|
|
8365
|
+
this.outcome = "error";
|
|
8047
8366
|
}
|
|
8048
8367
|
if (event.type === "user_input" && this.summary.title === "(empty session)") {
|
|
8049
8368
|
this.summary = { ...this.summary, title: userInputTitle(event.content) };
|
|
@@ -8054,18 +8373,35 @@ var FileSessionWriter = class {
|
|
|
8054
8373
|
} else if (event.type === "session_end") {
|
|
8055
8374
|
const total = event.usage.input + event.usage.output;
|
|
8056
8375
|
if (total > 0) this.summary = { ...this.summary, tokenTotal: total };
|
|
8376
|
+
} else if (event.type === "in_flight_start") {
|
|
8377
|
+
this.iterationCount++;
|
|
8057
8378
|
}
|
|
8058
8379
|
}
|
|
8059
8380
|
async close() {
|
|
8060
8381
|
if (this.closing) return;
|
|
8061
8382
|
this.closing = true;
|
|
8062
8383
|
this.closed = true;
|
|
8384
|
+
this.summary = {
|
|
8385
|
+
...this.summary,
|
|
8386
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8387
|
+
iterationCount: this.iterationCount,
|
|
8388
|
+
toolCallCount: this.toolCallCount,
|
|
8389
|
+
toolErrorCount: this.toolErrorCount,
|
|
8390
|
+
fileChangeCount: this.fileChangeCount,
|
|
8391
|
+
compactionCount: this.compactionCount > 0 ? this.compactionCount : void 0,
|
|
8392
|
+
toolBreakdown: { ...this.toolBreakdown },
|
|
8393
|
+
outcome: this.outcome ?? "completed"
|
|
8394
|
+
};
|
|
8063
8395
|
if (this.manifestFile) {
|
|
8064
8396
|
try {
|
|
8065
8397
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
8066
8398
|
} catch {
|
|
8067
8399
|
}
|
|
8068
8400
|
}
|
|
8401
|
+
try {
|
|
8402
|
+
await this.onCloseCb?.(this.summary);
|
|
8403
|
+
} catch {
|
|
8404
|
+
}
|
|
8069
8405
|
try {
|
|
8070
8406
|
await this.handle.close();
|
|
8071
8407
|
} catch {
|
|
@@ -8107,7 +8443,7 @@ var FileSessionWriter = class {
|
|
|
8107
8443
|
let targetCheckpointLine = -1;
|
|
8108
8444
|
let afterTarget = false;
|
|
8109
8445
|
for (let i = 0; i < lines.length; i++) {
|
|
8110
|
-
const line = lines[i];
|
|
8446
|
+
const line = expectDefined3(lines[i]);
|
|
8111
8447
|
if (!line.trim()) continue;
|
|
8112
8448
|
let event;
|
|
8113
8449
|
try {
|
|
@@ -8273,7 +8609,10 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
8273
8609
|
if (kind === "timeout" || kind === "idle_timeout") {
|
|
8274
8610
|
if (progress > lastTimeoutProgress) {
|
|
8275
8611
|
lastTimeoutProgress = progress;
|
|
8276
|
-
const next2 = Math.min(
|
|
8612
|
+
const next2 = Math.min(
|
|
8613
|
+
Math.ceil(limit * (1 + factor)),
|
|
8614
|
+
ceiling.timeoutMs ?? DEFAULT_CEILING.timeoutMs
|
|
8615
|
+
);
|
|
8277
8616
|
extend({ timeoutMs: next2 });
|
|
8278
8617
|
} else {
|
|
8279
8618
|
deny();
|
|
@@ -8287,7 +8626,7 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
8287
8626
|
}
|
|
8288
8627
|
extendCounts.set(kind, count + 1);
|
|
8289
8628
|
const field = FIELD_BY_KIND[kind];
|
|
8290
|
-
const cap = ceiling[field];
|
|
8629
|
+
const cap = ceiling[field] ?? DEFAULT_CEILING[field];
|
|
8291
8630
|
const next = Math.min(Math.ceil(limit * (1 + factor)), cap);
|
|
8292
8631
|
extend({ [field]: next });
|
|
8293
8632
|
})
|