@wrongstack/core 0.77.0 → 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-EWdqs8v6.d.ts → agent-bridge-C9P_HPez.d.ts} +2 -2
- package/dist/{agent-subagent-runner-D8qW8OSC.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 +318 -37
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +31 -31
- package/dist/defaults/index.js +419 -67
- 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-CYaoLN5_.d.ts → events-DnRqXaZ3.d.ts} +43 -42
- 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-DIxjTOga.d.ts → index-BNOLadHw.d.ts} +28 -28
- package/dist/{index-Dsda0uCn.d.ts → index-N0_c4bHQ.d.ts} +45 -45
- package/dist/index.d.ts +165 -165
- package/dist/index.js +593 -137
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +9 -9
- package/dist/infrastructure/index.js +13 -5
- 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-BppKxDqZ.d.ts → logger-C_27pj9i.d.ts} +6 -7
- 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-DpbG3wiy.d.ts → multi-agent-coordinator-DllpCVkF.d.ts} +12 -12
- package/dist/{null-fleet-bus-u5ys3lW_.d.ts → null-fleet-bus-BY0AN-sr.d.ts} +121 -121
- 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-Dn0P8Pbj.d.ts → parallel-eternal-engine-D402RASp.d.ts} +49 -49
- package/dist/{path-resolver-B32v2JIq.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-BcUwLlMQ.d.ts → plan-templates-DRvPgkfZ.d.ts} +65 -32
- package/dist/{provider-runner-CSi_7l0h.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 +378 -76
- 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 +45 -10
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +46 -45
- package/dist/utils/index.js +53 -12
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-D7evAFWM.d.ts → wstack-paths-BQMvEllz.d.ts} +2 -2
- 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));
|
|
@@ -7613,6 +7629,12 @@ function defaultFormatTaskInput(task) {
|
|
|
7613
7629
|
}
|
|
7614
7630
|
|
|
7615
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
|
+
}
|
|
7616
7638
|
function repairToolUseAdjacency(messages) {
|
|
7617
7639
|
const removedToolUses = [];
|
|
7618
7640
|
const removedToolResults = [];
|
|
@@ -7620,7 +7642,7 @@ function repairToolUseAdjacency(messages) {
|
|
|
7620
7642
|
let changed = false;
|
|
7621
7643
|
const out = [];
|
|
7622
7644
|
for (let i = 0; i < messages.length; i++) {
|
|
7623
|
-
const original = messages[i];
|
|
7645
|
+
const original = expectDefined2(messages[i]);
|
|
7624
7646
|
let msg = original;
|
|
7625
7647
|
if (hasToolUse(msg)) {
|
|
7626
7648
|
const nextIds = toolResultIds(messages[i + 1]);
|
|
@@ -7705,6 +7727,12 @@ function isEmptyMessage(msg) {
|
|
|
7705
7727
|
}
|
|
7706
7728
|
|
|
7707
7729
|
// src/storage/session-store.ts
|
|
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
|
+
}
|
|
7708
7736
|
function sanitizeModel(model) {
|
|
7709
7737
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
7710
7738
|
}
|
|
@@ -7715,7 +7743,7 @@ function generateSessionId(startedAt, model) {
|
|
|
7715
7743
|
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
7716
7744
|
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
7717
7745
|
}
|
|
7718
|
-
var DefaultSessionStore = class {
|
|
7746
|
+
var DefaultSessionStore = class _DefaultSessionStore {
|
|
7719
7747
|
dir;
|
|
7720
7748
|
events;
|
|
7721
7749
|
secretScrubber;
|
|
@@ -7724,6 +7752,10 @@ var DefaultSessionStore = class {
|
|
|
7724
7752
|
this.events = opts.events;
|
|
7725
7753
|
this.secretScrubber = opts.secretScrubber;
|
|
7726
7754
|
}
|
|
7755
|
+
/** Absolute path to the session index file. */
|
|
7756
|
+
get indexFile() {
|
|
7757
|
+
return path4.join(this.dir, "_index.jsonl");
|
|
7758
|
+
}
|
|
7727
7759
|
/** Join session ID to its absolute path within the store directory. */
|
|
7728
7760
|
sessionPath(id, ext) {
|
|
7729
7761
|
return path4.join(this.dir, `${id}${ext}`);
|
|
@@ -7756,7 +7788,8 @@ var DefaultSessionStore = class {
|
|
|
7756
7788
|
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
7757
7789
|
dir: shardDir,
|
|
7758
7790
|
filePath: file,
|
|
7759
|
-
secretScrubber: this.secretScrubber
|
|
7791
|
+
secretScrubber: this.secretScrubber,
|
|
7792
|
+
onClose: (s) => this.appendToIndex(s)
|
|
7760
7793
|
});
|
|
7761
7794
|
} catch (err) {
|
|
7762
7795
|
await handle.close().catch(() => {
|
|
@@ -7787,7 +7820,7 @@ var DefaultSessionStore = class {
|
|
|
7787
7820
|
provider: data.metadata.provider
|
|
7788
7821
|
},
|
|
7789
7822
|
this.events,
|
|
7790
|
-
{ 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) }
|
|
7791
7824
|
);
|
|
7792
7825
|
return { writer, data };
|
|
7793
7826
|
} catch (err) {
|
|
@@ -7817,6 +7850,15 @@ var DefaultSessionStore = class {
|
|
|
7817
7850
|
async list(limit = 20) {
|
|
7818
7851
|
try {
|
|
7819
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
|
+
}
|
|
7820
7862
|
const ids = await this.collectSessionIds(this.dir);
|
|
7821
7863
|
const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
7822
7864
|
const out = sessions.filter((s) => s !== null);
|
|
@@ -7830,16 +7872,121 @@ var DefaultSessionStore = class {
|
|
|
7830
7872
|
return [];
|
|
7831
7873
|
}
|
|
7832
7874
|
}
|
|
7833
|
-
|
|
7834
|
-
|
|
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) {
|
|
7835
7972
|
const ids = [];
|
|
7836
|
-
|
|
7973
|
+
let entries;
|
|
7974
|
+
try {
|
|
7975
|
+
entries = await fsp6.readdir(dir, { withFileTypes: true });
|
|
7976
|
+
} catch {
|
|
7977
|
+
return ids;
|
|
7978
|
+
}
|
|
7837
7979
|
for (const entry of entries) {
|
|
7838
|
-
|
|
7980
|
+
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
7981
|
+
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
7982
|
+
continue;
|
|
7839
7983
|
if (entry.isDirectory()) {
|
|
7840
|
-
|
|
7984
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
7985
|
+
ids.push(...await this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1));
|
|
7841
7986
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
7842
|
-
|
|
7987
|
+
if (entry.name === "_index.jsonl") continue;
|
|
7988
|
+
const base = entry.name.replace(/\.jsonl$/, "");
|
|
7989
|
+
ids.push(prefix ? `${prefix}/${base}` : base);
|
|
7843
7990
|
}
|
|
7844
7991
|
}
|
|
7845
7992
|
return ids;
|
|
@@ -7862,9 +8009,70 @@ var DefaultSessionStore = class {
|
|
|
7862
8009
|
return summary;
|
|
7863
8010
|
}
|
|
7864
8011
|
}
|
|
7865
|
-
|
|
7866
|
-
|
|
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);
|
|
7867
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;
|
|
7868
8076
|
}
|
|
7869
8077
|
async clearHistory(id) {
|
|
7870
8078
|
await this.ensureShardDir(id);
|
|
@@ -7886,13 +8094,42 @@ var DefaultSessionStore = class {
|
|
|
7886
8094
|
const data = await this.load(id);
|
|
7887
8095
|
const firstUser = data.events.find((e) => e.type === "user_input");
|
|
7888
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
|
+
}
|
|
7889
8119
|
return {
|
|
7890
8120
|
id,
|
|
7891
8121
|
title,
|
|
7892
8122
|
startedAt: data.metadata.startedAt,
|
|
8123
|
+
endedAt: data.metadata.endedAt,
|
|
7893
8124
|
model: data.metadata.model ?? "unknown",
|
|
7894
8125
|
provider: data.metadata.provider ?? "unknown",
|
|
7895
|
-
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
|
|
7896
8133
|
};
|
|
7897
8134
|
} catch {
|
|
7898
8135
|
return {
|
|
@@ -7991,9 +8228,10 @@ var FileSessionWriter = class {
|
|
|
7991
8228
|
this.meta = meta;
|
|
7992
8229
|
this.events = events;
|
|
7993
8230
|
this.resumed = opts.resumed ?? false;
|
|
7994
|
-
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`) : "";
|
|
7995
8232
|
this.filePath = opts.filePath ?? "";
|
|
7996
8233
|
this.secretScrubber = opts.secretScrubber;
|
|
8234
|
+
this.onCloseCb = opts.onClose;
|
|
7997
8235
|
this.summary = {
|
|
7998
8236
|
id,
|
|
7999
8237
|
title: "(empty session)",
|
|
@@ -8023,6 +8261,15 @@ var FileSessionWriter = class {
|
|
|
8023
8261
|
appendFailCount = 0;
|
|
8024
8262
|
lastAppendWarnAt = 0;
|
|
8025
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;
|
|
8026
8273
|
/**
|
|
8027
8274
|
* Scrub secrets out of conversation-turn events before they are observed
|
|
8028
8275
|
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
@@ -8100,8 +8347,22 @@ var FileSessionWriter = class {
|
|
|
8100
8347
|
observeForSummary(event) {
|
|
8101
8348
|
if (event.type === "tool_use") {
|
|
8102
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;
|
|
8103
8353
|
} else if (event.type === "tool_result") {
|
|
8104
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";
|
|
8105
8366
|
}
|
|
8106
8367
|
if (event.type === "user_input" && this.summary.title === "(empty session)") {
|
|
8107
8368
|
this.summary = { ...this.summary, title: userInputTitle(event.content) };
|
|
@@ -8112,18 +8373,35 @@ var FileSessionWriter = class {
|
|
|
8112
8373
|
} else if (event.type === "session_end") {
|
|
8113
8374
|
const total = event.usage.input + event.usage.output;
|
|
8114
8375
|
if (total > 0) this.summary = { ...this.summary, tokenTotal: total };
|
|
8376
|
+
} else if (event.type === "in_flight_start") {
|
|
8377
|
+
this.iterationCount++;
|
|
8115
8378
|
}
|
|
8116
8379
|
}
|
|
8117
8380
|
async close() {
|
|
8118
8381
|
if (this.closing) return;
|
|
8119
8382
|
this.closing = true;
|
|
8120
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
|
+
};
|
|
8121
8395
|
if (this.manifestFile) {
|
|
8122
8396
|
try {
|
|
8123
8397
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
8124
8398
|
} catch {
|
|
8125
8399
|
}
|
|
8126
8400
|
}
|
|
8401
|
+
try {
|
|
8402
|
+
await this.onCloseCb?.(this.summary);
|
|
8403
|
+
} catch {
|
|
8404
|
+
}
|
|
8127
8405
|
try {
|
|
8128
8406
|
await this.handle.close();
|
|
8129
8407
|
} catch {
|
|
@@ -8165,7 +8443,7 @@ var FileSessionWriter = class {
|
|
|
8165
8443
|
let targetCheckpointLine = -1;
|
|
8166
8444
|
let afterTarget = false;
|
|
8167
8445
|
for (let i = 0; i < lines.length; i++) {
|
|
8168
|
-
const line = lines[i];
|
|
8446
|
+
const line = expectDefined3(lines[i]);
|
|
8169
8447
|
if (!line.trim()) continue;
|
|
8170
8448
|
let event;
|
|
8171
8449
|
try {
|
|
@@ -8331,7 +8609,10 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
8331
8609
|
if (kind === "timeout" || kind === "idle_timeout") {
|
|
8332
8610
|
if (progress > lastTimeoutProgress) {
|
|
8333
8611
|
lastTimeoutProgress = progress;
|
|
8334
|
-
const next2 = Math.min(
|
|
8612
|
+
const next2 = Math.min(
|
|
8613
|
+
Math.ceil(limit * (1 + factor)),
|
|
8614
|
+
ceiling.timeoutMs ?? DEFAULT_CEILING.timeoutMs
|
|
8615
|
+
);
|
|
8335
8616
|
extend({ timeoutMs: next2 });
|
|
8336
8617
|
} else {
|
|
8337
8618
|
deny();
|
|
@@ -8345,7 +8626,7 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
8345
8626
|
}
|
|
8346
8627
|
extendCounts.set(kind, count + 1);
|
|
8347
8628
|
const field = FIELD_BY_KIND[kind];
|
|
8348
|
-
const cap = ceiling[field];
|
|
8629
|
+
const cap = ceiling[field] ?? DEFAULT_CEILING[field];
|
|
8349
8630
|
const next = Math.min(Math.ceil(limit * (1 + factor)), cap);
|
|
8350
8631
|
extend({ [field]: next });
|
|
8351
8632
|
})
|