@wrongstack/core 0.10.2 → 0.10.3
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-DBBGP5O4.d.ts → agent-bridge-D_XcS2HL.d.ts} +3 -1
- package/dist/{agent-subagent-runner-KwufTWwn.d.ts → agent-subagent-runner-DuRtZmhl.d.ts} +13 -3
- package/dist/coordination/index.d.ts +8 -8
- package/dist/coordination/index.js +83 -21
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +11 -11
- package/dist/defaults/index.js +89 -23
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-Bw1K5E6K.d.ts → events-BrQiweXN.d.ts} +1 -1
- package/dist/execution/index.d.ts +6 -6
- package/dist/execution/index.js +32 -10
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +2 -2
- package/dist/{index-DgQWnZxP.d.ts → index-DAHEM_II.d.ts} +1 -1
- package/dist/{index-DIt5TIaF.d.ts → index-DnBHrz2y.d.ts} +2 -2
- package/dist/index.d.ts +17 -17
- package/dist/index.js +226 -101
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/kernel/index.d.ts +3 -3
- package/dist/kernel/index.js.map +1 -1
- package/dist/{multi-agent-C1O8EFzb.d.ts → multi-agent-C8Z1i__e.d.ts} +2 -2
- package/dist/{multi-agent-coordinator-DoVSBFNx.d.ts → multi-agent-coordinator-CjG-FZKj.d.ts} +8 -3
- package/dist/{null-fleet-bus-T-s3JaOt.d.ts → null-fleet-bus-B3WdVY9i.d.ts} +28 -23
- package/dist/observability/index.d.ts +1 -1
- package/dist/{path-resolver-BfyK-I7w.d.ts → path-resolver-DumKAi0n.d.ts} +1 -1
- package/dist/{plan-templates-BWs6Je7j.d.ts → plan-templates-BUP4o-ds.d.ts} +2 -2
- package/dist/{provider-runner-BmPe2RuU.d.ts → provider-runner-Dlv8Fvw9.d.ts} +1 -1
- package/dist/sdd/index.d.ts +4 -4
- package/dist/sdd/index.js +30 -8
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/index.js.map +1 -1
- package/dist/{session-reader-CCSBfQmd.d.ts → session-reader-bfgsy2a0.d.ts} +7 -3
- package/dist/storage/index.d.ts +29 -10
- package/dist/storage/index.js +131 -65
- package/dist/storage/index.js.map +1 -1
- package/dist/{tool-executor-D7RxLtg4.d.ts → tool-executor-D87-_5Of.d.ts} +1 -1
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.js +25 -5
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
package/dist/storage/index.js
CHANGED
|
@@ -30,8 +30,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
30
30
|
}
|
|
31
31
|
let mode;
|
|
32
32
|
try {
|
|
33
|
-
const
|
|
34
|
-
mode =
|
|
33
|
+
const stat5 = await fsp.stat(targetPath);
|
|
34
|
+
mode = stat5.mode & 511;
|
|
35
35
|
} catch {
|
|
36
36
|
mode = opts.mode;
|
|
37
37
|
}
|
|
@@ -297,8 +297,8 @@ var DefaultSessionStore = class {
|
|
|
297
297
|
return JSON.parse(raw);
|
|
298
298
|
} catch {
|
|
299
299
|
const full = this.sessionPath(id, ".jsonl");
|
|
300
|
-
const
|
|
301
|
-
const summary = await this.summarize(id,
|
|
300
|
+
const stat5 = await fsp.stat(full);
|
|
301
|
+
const summary = await this.summarize(id, stat5.mtime.toISOString());
|
|
302
302
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
303
303
|
console.warn(
|
|
304
304
|
`[session-store] Failed to write manifest for "${id}":`,
|
|
@@ -1776,11 +1776,27 @@ var DefaultSessionReader = class {
|
|
|
1776
1776
|
const data = await this.store.load(sessionId);
|
|
1777
1777
|
for (const e of data.events) yield e;
|
|
1778
1778
|
}
|
|
1779
|
-
async search(q, sessionId) {
|
|
1779
|
+
async search(q, sessionId, sessionQuery) {
|
|
1780
1780
|
const limit = q.limit ?? 100;
|
|
1781
1781
|
const matcher = buildMatcher(q);
|
|
1782
1782
|
const allowedTypes = q.types ? new Set(q.types) : null;
|
|
1783
|
-
|
|
1783
|
+
let ids;
|
|
1784
|
+
if (sessionId) {
|
|
1785
|
+
ids = [sessionId];
|
|
1786
|
+
} else {
|
|
1787
|
+
const sessions = await this.store.list(1e3);
|
|
1788
|
+
const titleNeedle = sessionQuery?.titleContains?.toLowerCase();
|
|
1789
|
+
const filtered = sessions.filter((s) => {
|
|
1790
|
+
if (sessionQuery?.since && s.startedAt < sessionQuery.since) return false;
|
|
1791
|
+
if (sessionQuery?.until && s.startedAt > sessionQuery.until) return false;
|
|
1792
|
+
if (sessionQuery?.provider && s.provider !== sessionQuery.provider) return false;
|
|
1793
|
+
if (sessionQuery?.model && s.model !== sessionQuery.model) return false;
|
|
1794
|
+
if (sessionQuery?.minTokens !== void 0 && s.tokenTotal < sessionQuery.minTokens) return false;
|
|
1795
|
+
if (titleNeedle && !s.title.toLowerCase().includes(titleNeedle)) return false;
|
|
1796
|
+
return true;
|
|
1797
|
+
});
|
|
1798
|
+
ids = filtered.map((s) => s.id);
|
|
1799
|
+
}
|
|
1784
1800
|
const hits = [];
|
|
1785
1801
|
for (const id of ids) {
|
|
1786
1802
|
let data;
|
|
@@ -2183,6 +2199,8 @@ var ReplayLogStore = class {
|
|
|
2183
2199
|
writeChains = /* @__PURE__ */ new Map();
|
|
2184
2200
|
/** Per-session hash → entry index, kept in memory after the first load. */
|
|
2185
2201
|
cache = /* @__PURE__ */ new Map();
|
|
2202
|
+
/** Per-session entry count on disk, to detect when compaction is needed. */
|
|
2203
|
+
diskCount = /* @__PURE__ */ new Map();
|
|
2186
2204
|
maxEntries;
|
|
2187
2205
|
constructor(opts) {
|
|
2188
2206
|
this.dir = opts.dir;
|
|
@@ -2205,17 +2223,30 @@ var ReplayLogStore = class {
|
|
|
2205
2223
|
request: input.request,
|
|
2206
2224
|
response: input.response
|
|
2207
2225
|
};
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2226
|
+
cache.set(hash, entry);
|
|
2227
|
+
const count = (this.diskCount.get(input.sessionId) ?? 0) + 1;
|
|
2228
|
+
if (count > this.maxEntries) {
|
|
2229
|
+
await this.compact(input.sessionId, cache);
|
|
2230
|
+
} else {
|
|
2231
|
+
await fsp.appendFile(this.filePath(input.sessionId), JSON.stringify(entry) + "\n", "utf8");
|
|
2232
|
+
this.diskCount.set(input.sessionId, count);
|
|
2233
|
+
}
|
|
2216
2234
|
});
|
|
2217
2235
|
return hash;
|
|
2218
2236
|
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Compact the replay log to keep only the most recent maxEntries.
|
|
2239
|
+
* Called when entry count exceeds the cap. Rewrites the entire file
|
|
2240
|
+
* but only happens O(n / maxEntries) times per session.
|
|
2241
|
+
*/
|
|
2242
|
+
async compact(sessionId, cache) {
|
|
2243
|
+
const all = [...cache.values()];
|
|
2244
|
+
const keep = all.slice(-this.maxEntries);
|
|
2245
|
+
await this.writeAll(sessionId, keep);
|
|
2246
|
+
cache.clear();
|
|
2247
|
+
for (const e of keep) cache.set(e.hash, e);
|
|
2248
|
+
this.diskCount.set(sessionId, keep.length);
|
|
2249
|
+
}
|
|
2219
2250
|
// ── Reads ───────────────────────────────────────────────────────────────
|
|
2220
2251
|
/**
|
|
2221
2252
|
* Look up an entry by hash. Returns `null` when the request has
|
|
@@ -2299,6 +2330,7 @@ var ReplayLogStore = class {
|
|
|
2299
2330
|
cache = /* @__PURE__ */ new Map();
|
|
2300
2331
|
for (const e of all) cache.set(e.hash, e);
|
|
2301
2332
|
this.cache.set(sessionId, cache);
|
|
2333
|
+
this.diskCount.set(sessionId, all.length);
|
|
2302
2334
|
return cache;
|
|
2303
2335
|
}
|
|
2304
2336
|
enqueue(sessionId, fn) {
|
|
@@ -2328,14 +2360,50 @@ var SessionRecovery = class {
|
|
|
2328
2360
|
*/
|
|
2329
2361
|
async detectStale(sessionId) {
|
|
2330
2362
|
const fp = this.filePath(sessionId);
|
|
2331
|
-
|
|
2363
|
+
const TAIL_SIZE = 8192;
|
|
2364
|
+
let stat5;
|
|
2332
2365
|
try {
|
|
2333
|
-
|
|
2366
|
+
stat5 = await fsp.stat(fp);
|
|
2334
2367
|
} catch (err) {
|
|
2335
2368
|
if (err.code === "ENOENT") return null;
|
|
2336
2369
|
return null;
|
|
2337
2370
|
}
|
|
2338
|
-
|
|
2371
|
+
if (stat5.size === 0) return null;
|
|
2372
|
+
const position = Math.max(0, stat5.size - TAIL_SIZE);
|
|
2373
|
+
const buf = Buffer.alloc(TAIL_SIZE);
|
|
2374
|
+
let fh;
|
|
2375
|
+
try {
|
|
2376
|
+
fh = await fsp.open(fp, "r");
|
|
2377
|
+
const { bytesRead } = await fh.read(buf, 0, TAIL_SIZE, position);
|
|
2378
|
+
let eventCount = 0;
|
|
2379
|
+
const raw = buf.subarray(0, bytesRead).toString("utf8");
|
|
2380
|
+
for (const line of raw.split("\n")) {
|
|
2381
|
+
if (line.trim()) eventCount++;
|
|
2382
|
+
}
|
|
2383
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2384
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2385
|
+
try {
|
|
2386
|
+
const ev = JSON.parse(lines[i]);
|
|
2387
|
+
if (ev.type === "in_flight_start") {
|
|
2388
|
+
return {
|
|
2389
|
+
sessionId,
|
|
2390
|
+
path: fp,
|
|
2391
|
+
lastEventTs: ev.ts,
|
|
2392
|
+
context: ev.context,
|
|
2393
|
+
eventCount
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
return null;
|
|
2397
|
+
} catch {
|
|
2398
|
+
continue;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
return null;
|
|
2402
|
+
} catch {
|
|
2403
|
+
return null;
|
|
2404
|
+
} finally {
|
|
2405
|
+
if (fh) await fh.close();
|
|
2406
|
+
}
|
|
2339
2407
|
}
|
|
2340
2408
|
/**
|
|
2341
2409
|
* Generate a recovery plan for a session. The plan describes
|
|
@@ -2416,46 +2484,22 @@ var SessionRecovery = class {
|
|
|
2416
2484
|
}
|
|
2417
2485
|
return path11.join(this.dir, `${sessionId}.jsonl`);
|
|
2418
2486
|
}
|
|
2419
|
-
/**
|
|
2420
|
-
* Stream-parse the last few lines of a JSONL log. We do NOT load
|
|
2421
|
-
* the whole file into memory — for long-running sessions the log
|
|
2422
|
-
* can be megabytes. Instead we read tail-ward and find the last
|
|
2423
|
-
* `in_flight_start` / `in_flight_end` pair.
|
|
2424
|
-
*/
|
|
2425
|
-
async parseForStale(sessionId, fp, raw) {
|
|
2426
|
-
const lines = raw.split("\n");
|
|
2427
|
-
let lastEvent = null;
|
|
2428
|
-
let eventCount = 0;
|
|
2429
|
-
for (const line of lines) {
|
|
2430
|
-
if (!line.trim()) continue;
|
|
2431
|
-
try {
|
|
2432
|
-
const ev = JSON.parse(line);
|
|
2433
|
-
lastEvent = ev;
|
|
2434
|
-
eventCount++;
|
|
2435
|
-
} catch {
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
if (!lastEvent) return null;
|
|
2439
|
-
if (lastEvent.type === "in_flight_start") {
|
|
2440
|
-
return {
|
|
2441
|
-
sessionId,
|
|
2442
|
-
path: fp,
|
|
2443
|
-
lastEventTs: lastEvent.ts,
|
|
2444
|
-
context: lastEvent.context,
|
|
2445
|
-
eventCount
|
|
2446
|
-
};
|
|
2447
|
-
}
|
|
2448
|
-
return null;
|
|
2449
|
-
}
|
|
2450
2487
|
};
|
|
2451
2488
|
var GENESIS_PREV = "0".repeat(64);
|
|
2489
|
+
var DEFAULT_FSYNC_EVERY = 100;
|
|
2452
2490
|
var ToolAuditLog = class {
|
|
2453
2491
|
dir;
|
|
2454
2492
|
/** In-memory cache of the last entry's hash (per session), to compute chains efficiently. */
|
|
2455
2493
|
tailHash = /* @__PURE__ */ new Map();
|
|
2494
|
+
/** In-memory counter for entry indices — avoids re-reading the file on every write. */
|
|
2495
|
+
tailIndex = /* @__PURE__ */ new Map();
|
|
2496
|
+
/** Tracks writes since last fsync, per session. */
|
|
2497
|
+
unSyncedWrites = /* @__PURE__ */ new Map();
|
|
2456
2498
|
writeChains = /* @__PURE__ */ new Map();
|
|
2499
|
+
fsyncEvery;
|
|
2457
2500
|
constructor(opts) {
|
|
2458
2501
|
this.dir = opts.dir;
|
|
2502
|
+
this.fsyncEvery = opts.fsyncEvery ?? DEFAULT_FSYNC_EVERY;
|
|
2459
2503
|
}
|
|
2460
2504
|
/**
|
|
2461
2505
|
* Append a tool call/result pair to the chain. Returns the
|
|
@@ -2467,6 +2511,7 @@ var ToolAuditLog = class {
|
|
|
2467
2511
|
let entry = null;
|
|
2468
2512
|
await this.enqueue(input.sessionId, async () => {
|
|
2469
2513
|
const prevHash = this.tailHash.get(input.sessionId) ?? GENESIS_PREV;
|
|
2514
|
+
const index = this.tailIndex.get(input.sessionId) ?? 0;
|
|
2470
2515
|
const id = randomUUID();
|
|
2471
2516
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2472
2517
|
const content = {
|
|
@@ -2477,12 +2522,11 @@ var ToolAuditLog = class {
|
|
|
2477
2522
|
toolUseId: input.toolUseId,
|
|
2478
2523
|
input: input.input,
|
|
2479
2524
|
output: input.output,
|
|
2480
|
-
isError: input.isError
|
|
2525
|
+
isError: input.isError,
|
|
2526
|
+
index
|
|
2481
2527
|
};
|
|
2482
2528
|
const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
|
|
2483
2529
|
entry = {
|
|
2484
|
-
index: this.tailHash.has(input.sessionId) ? -1 : 0,
|
|
2485
|
-
// placeholder; recomputed below
|
|
2486
2530
|
id,
|
|
2487
2531
|
ts,
|
|
2488
2532
|
prevHash,
|
|
@@ -2491,15 +2535,12 @@ var ToolAuditLog = class {
|
|
|
2491
2535
|
toolUseId: input.toolUseId,
|
|
2492
2536
|
input: input.input,
|
|
2493
2537
|
output: input.output,
|
|
2494
|
-
isError: input.isError
|
|
2538
|
+
isError: input.isError,
|
|
2539
|
+
index
|
|
2495
2540
|
};
|
|
2496
|
-
const existing = await this.readAll(input.sessionId);
|
|
2497
|
-
const index = existing.length;
|
|
2498
|
-
const finalContent = { ...content, index };
|
|
2499
|
-
const finalHash = createHash("sha256").update(stableStringify2(finalContent), "utf8").digest("hex");
|
|
2500
|
-
entry = { ...entry, index, hash: finalHash };
|
|
2501
2541
|
await this.appendLine(input.sessionId, entry);
|
|
2502
|
-
this.tailHash.set(input.sessionId,
|
|
2542
|
+
this.tailHash.set(input.sessionId, hash);
|
|
2543
|
+
this.tailIndex.set(input.sessionId, index + 1);
|
|
2503
2544
|
});
|
|
2504
2545
|
return entry;
|
|
2505
2546
|
}
|
|
@@ -2581,9 +2622,34 @@ var ToolAuditLog = class {
|
|
|
2581
2622
|
}
|
|
2582
2623
|
async appendLine(sessionId, entry) {
|
|
2583
2624
|
const fp = this.filePath(sessionId);
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2586
|
-
|
|
2625
|
+
const line = JSON.stringify(entry) + "\n";
|
|
2626
|
+
await fsp.appendFile(fp, line, "utf8");
|
|
2627
|
+
const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;
|
|
2628
|
+
this.unSyncedWrites.set(sessionId, count);
|
|
2629
|
+
if (this.fsyncEvery !== Infinity && count % this.fsyncEvery === 0) {
|
|
2630
|
+
await this.sync(sessionId, fp);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Explicitly sync the file to disk. Called automatically every
|
|
2635
|
+
* `fsyncEvery` writes, and available for callers who want to
|
|
2636
|
+
* force a sync before closing or during graceful shutdown.
|
|
2637
|
+
*/
|
|
2638
|
+
async flush(sessionId) {
|
|
2639
|
+
await this.sync(sessionId, this.filePath(sessionId));
|
|
2640
|
+
}
|
|
2641
|
+
async sync(sessionId, fp) {
|
|
2642
|
+
try {
|
|
2643
|
+
const fh = await fsp.open(fp, "r+");
|
|
2644
|
+
try {
|
|
2645
|
+
await fh.sync();
|
|
2646
|
+
} finally {
|
|
2647
|
+
await fh.close();
|
|
2648
|
+
}
|
|
2649
|
+
} catch {
|
|
2650
|
+
} finally {
|
|
2651
|
+
this.unSyncedWrites.set(sessionId, 0);
|
|
2652
|
+
}
|
|
2587
2653
|
}
|
|
2588
2654
|
enqueue(sessionId, fn) {
|
|
2589
2655
|
const prev = this.writeChains.get(sessionId) ?? Promise.resolve();
|
|
@@ -3787,8 +3853,8 @@ var CloudSync = class {
|
|
|
3787
3853
|
const localPath = this.categoryToPath(cat);
|
|
3788
3854
|
if (!localPath) continue;
|
|
3789
3855
|
try {
|
|
3790
|
-
const
|
|
3791
|
-
if (
|
|
3856
|
+
const stat5 = await fsp.stat(localPath);
|
|
3857
|
+
if (stat5.isDirectory()) {
|
|
3792
3858
|
const files = await this.walkDir(localPath, localPath);
|
|
3793
3859
|
for (const file of files) {
|
|
3794
3860
|
const content = await fsp.readFile(file, "utf8");
|
|
@@ -3813,8 +3879,8 @@ var CloudSync = class {
|
|
|
3813
3879
|
const localPath = this.categoryToPath(cat);
|
|
3814
3880
|
if (!localPath) continue;
|
|
3815
3881
|
try {
|
|
3816
|
-
const
|
|
3817
|
-
if (
|
|
3882
|
+
const stat5 = await fsp.stat(localPath);
|
|
3883
|
+
if (stat5.isDirectory()) {
|
|
3818
3884
|
const files = await this.walkDir(localPath, localPath);
|
|
3819
3885
|
for (const file of files) {
|
|
3820
3886
|
const content = await fsp.readFile(file);
|