@wrongstack/core 0.270.0 → 0.272.0
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-PcHQl_UQ.d.ts → agent-bridge-jVSZiygR.d.ts} +1 -1
- package/dist/{agent-subagent-runner-SHJW7t8q.d.ts → agent-subagent-runner-DOLIwBRo.d.ts} +7 -7
- package/dist/{brain-BYcK__Ym.d.ts → brain-CdbbJWi3.d.ts} +71 -1
- package/dist/{compactor-C2RKEBtC.d.ts → compactor-72ug-ZRB.d.ts} +1 -1
- package/dist/{config-C_ae2k86.d.ts → config-D2DGoGSQ.d.ts} +29 -2
- package/dist/{context-Dp87Bcaq.d.ts → context-Dw55zZ_Q.d.ts} +110 -1
- package/dist/coordination/index.d.ts +121 -17
- package/dist/coordination/index.js +738 -74
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +599 -86
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +23 -18
- package/dist/execution/index.js +136 -41
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +36 -6
- package/dist/execution/prompt-enhancer.js +35 -9
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-Bvrz1P3f.d.ts → global-mailbox-CQj_C9Dp.d.ts} +139 -3
- package/dist/{goal-preamble-CA_4yiGQ.d.ts → goal-preamble-ZXDjjR1y.d.ts} +9 -9
- package/dist/{goal-store-DhuJoUNG.d.ts → goal-store-CcJBd-g1.d.ts} +1 -1
- package/dist/hq/index.d.ts +93 -6
- package/dist/hq/index.js +616 -46
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-whDfTANu.d.ts → index-2Lhk5v0o.d.ts} +2 -2
- package/dist/{index-CZQ6Pwbs.d.ts → index-BL7BAx0p.d.ts} +8 -8
- package/dist/{index-W4VJCzHa.d.ts → index-Qo4kTzgw.d.ts} +5 -5
- package/dist/index.d.ts +96 -56
- package/dist/index.js +1938 -349
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +5 -3
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DJdZiRcv.d.ts → mcp-servers-DS-YUXvF.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +28 -5
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-C3a-2-Yd.d.ts → models-registry-DP6pGHet.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CJSpTe5O.d.ts → multi-agent-coordinator-BvbdNQ14.d.ts} +1 -1
- package/dist/{null-fleet-bus-QVshIsDx.d.ts → null-fleet-bus-BxTfXBKo.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-D9y5Pkcc.d.ts → parallel-eternal-engine-Cf-GTegR.d.ts} +9 -9
- package/dist/{path-resolver-CnQ8SIfh.d.ts → path-resolver-DztfnFcv.d.ts} +3 -3
- package/dist/{permission-CvYQNUqZ.d.ts → permission-CC7XFYWG.d.ts} +1 -1
- package/dist/{permission-policy-D5Ss8j4B.d.ts → permission-policy-cYR4RJmw.d.ts} +2 -2
- package/dist/{pipeline-l_zzFRh3.d.ts → pipeline-sNIkhXeB.d.ts} +2 -2
- package/dist/{plan-templates-NtPgyeJA.d.ts → plan-templates-DYiKFmEb.d.ts} +11 -5
- package/dist/{provider-model-resolve-d5poT5y0.d.ts → provider-model-resolve-dYAbTs_i.d.ts} +3 -3
- package/dist/{provider-runner-gkctlQV_.d.ts → provider-runner-Dw8x0F7u.d.ts} +3 -3
- package/dist/{retry-policy-CtFhfwa8.d.ts → retry-policy-BV7nzeAd.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +2 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-BLsVmTIK.d.ts → secret-vault-eMBKfheR.d.ts} +9 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +137 -10
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CXl2_y9W.d.ts → selector-C4ORTOid.d.ts} +1 -1
- package/dist/{session-event-bridge-Ccud20CC.d.ts → session-event-bridge-CeNpUL9w.d.ts} +1 -1
- package/dist/{session-reader-ZeXQmsmE.d.ts → session-reader-BepLSnGL.d.ts} +1 -1
- package/dist/storage/index.d.ts +45 -13
- package/dist/storage/index.js +374 -113
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +9 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +202 -41
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -4
- package/dist/utils/index.js +48 -9
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ import { isAbsolute, resolve } from 'path';
|
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import { hostname } from 'os';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
|
+
import { createReadStream } from 'fs';
|
|
9
|
+
import { createInterface } from 'readline';
|
|
8
10
|
|
|
9
11
|
// src/coordination/brain.ts
|
|
10
12
|
var ObservableBrainArbiter = class {
|
|
@@ -5877,6 +5879,68 @@ Working rules:
|
|
|
5877
5879
|
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
5878
5880
|
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
5879
5881
|
};
|
|
5882
|
+
var SHADOW_AGENT = {
|
|
5883
|
+
id: "shadow-agent",
|
|
5884
|
+
name: "Shadow",
|
|
5885
|
+
role: "shadow-agent",
|
|
5886
|
+
prompt: `You are the Shadow Agent \u2014 a silent background monitor for the WrongStack fleet.
|
|
5887
|
+
|
|
5888
|
+
Your job is to observe, detect anomalies, and be ready to intervene \u2014 but only when commanded.
|
|
5889
|
+
|
|
5890
|
+
## Core Responsibilities
|
|
5891
|
+
|
|
5892
|
+
1. **Fleet Monitoring** (every 30s)
|
|
5893
|
+
- Call \`fleet_status\` + \`fleet_health\` on each heartbeat
|
|
5894
|
+
- Track what each agent is doing (task descriptions)
|
|
5895
|
+
- Detect stuck agents (>5min no events), idle agents, crashed agents
|
|
5896
|
+
|
|
5897
|
+
2. **FleetBus Subscription**
|
|
5898
|
+
- Subscribe to \`subagent.*\` events to track lifecycle
|
|
5899
|
+
- Subscribe to \`tool.executed\` to monitor activity
|
|
5900
|
+
- Track agent joins (subagent.started) and leaves (subagent.stopped)
|
|
5901
|
+
|
|
5902
|
+
3. **Mailbox Surveillance**
|
|
5903
|
+
- Monitor for \`control\` type messages starting with "hoop"
|
|
5904
|
+
- Detect orphan assigns (assign without result within 5min)
|
|
5905
|
+
- Cross-session awareness via shared project mailbox
|
|
5906
|
+
|
|
5907
|
+
4. **Spike Detection**
|
|
5908
|
+
- Track task duration per agent
|
|
5909
|
+
- Flag agents that spawn and die within <5 seconds
|
|
5910
|
+
- Log spike events with reason (completed/error/killed/timeout)
|
|
5911
|
+
|
|
5912
|
+
5. **Intervention Commands**
|
|
5913
|
+
Parse these from mailbox control messages:
|
|
5914
|
+
- \`hoop <agentId>\` \u2014 terminate specific agent
|
|
5915
|
+
- \`hoop all\` \u2014 terminate all running agents
|
|
5916
|
+
- \`shadow status\` \u2014 report current fleet snapshot
|
|
5917
|
+
- \`shadow mute\` \u2014 pause heartbeat monitoring
|
|
5918
|
+
- \`shadow resume\` \u2014 resume heartbeat monitoring
|
|
5919
|
+
- \`shadow interval <ms>\` \u2014 change heartbeat interval
|
|
5920
|
+
- \`shadow model <model-id>\` \u2014 change analysis model
|
|
5921
|
+
|
|
5922
|
+
## Operating Rules
|
|
5923
|
+
|
|
5924
|
+
- **Silent by default**: Use DEBUG level logging unless anomaly detected
|
|
5925
|
+
- **Deterministic**: Same state always produces same actions \u2014 no randomness
|
|
5926
|
+
- **Report on anomaly**: When anomaly detected, use \`mail_send\` to broadcast warning
|
|
5927
|
+
- **Never auto-intervene**: Always report unless explicitly commanded
|
|
5928
|
+
- **Minimal footprint**: Small state, efficient snapshots
|
|
5929
|
+
|
|
5930
|
+
## Startup Sequence
|
|
5931
|
+
|
|
5932
|
+
1. Send broadcast: \`shadow:started { intervalMs, model, startTime }\`
|
|
5933
|
+
2. Subscribe to FleetBus for all relevant events
|
|
5934
|
+
3. Schedule heartbeat cron job at configured interval
|
|
5935
|
+
4. Wait for commands or anomalies
|
|
5936
|
+
|
|
5937
|
+
## Shutdown Sequence
|
|
5938
|
+
|
|
5939
|
+
1. Cancel all cron jobs (\`cron_cancel\`)
|
|
5940
|
+
2. Send broadcast: \`shadow:stopped { reason, finalState }\`
|
|
5941
|
+
3. Clean up FleetBus subscriptions`
|
|
5942
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
5943
|
+
};
|
|
5880
5944
|
var CRITIC_AGENT = {
|
|
5881
5945
|
id: "critic",
|
|
5882
5946
|
name: "Critic",
|
|
@@ -5917,6 +5981,7 @@ var FLEET_ROSTER = {
|
|
|
5917
5981
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
5918
5982
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
5919
5983
|
"critic": CRITIC_AGENT,
|
|
5984
|
+
"shadow-agent": SHADOW_AGENT,
|
|
5920
5985
|
...Object.fromEntries(
|
|
5921
5986
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
5922
5987
|
)
|
|
@@ -5928,6 +5993,8 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
5928
5993
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
5929
5994
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
5930
5995
|
"critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
5996
|
+
"shadow-agent": { timeoutMs: 24 * 60 * 60 * 1e3, maxIterations: 1e4, maxToolCalls: 5e3 },
|
|
5997
|
+
// Long-running background monitor
|
|
5931
5998
|
...Object.fromEntries(
|
|
5932
5999
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
5933
6000
|
)
|
|
@@ -8748,6 +8815,8 @@ function generateSessionId(startedAt, model) {
|
|
|
8748
8815
|
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
8749
8816
|
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
8750
8817
|
}
|
|
8818
|
+
|
|
8819
|
+
// src/storage/session-store.ts
|
|
8751
8820
|
var DefaultSessionStore = class _DefaultSessionStore {
|
|
8752
8821
|
dir;
|
|
8753
8822
|
events;
|
|
@@ -8765,6 +8834,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8765
8834
|
_loadCache = /* @__PURE__ */ new Map();
|
|
8766
8835
|
_indexCache = null;
|
|
8767
8836
|
static LOAD_CACHE_MAX_ENTRIES = 50;
|
|
8837
|
+
static LIST_SCAN_CONCURRENCY = 32;
|
|
8768
8838
|
constructor(opts) {
|
|
8769
8839
|
this.dir = opts.dir;
|
|
8770
8840
|
this.events = opts.events;
|
|
@@ -8987,15 +9057,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8987
9057
|
});
|
|
8988
9058
|
return indexed.slice(0, limit);
|
|
8989
9059
|
}
|
|
8990
|
-
|
|
8991
|
-
const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
8992
|
-
const out = sessions.filter((s) => s !== null);
|
|
8993
|
-
out.sort((a, b) => {
|
|
8994
|
-
if (a.startedAt < b.startedAt) return 1;
|
|
8995
|
-
if (a.startedAt > b.startedAt) return -1;
|
|
8996
|
-
return a.id.localeCompare(b.id);
|
|
8997
|
-
});
|
|
8998
|
-
return out.slice(0, limit);
|
|
9060
|
+
return await this.listFromDirectoryScan(limit);
|
|
8999
9061
|
} catch {
|
|
9000
9062
|
return [];
|
|
9001
9063
|
}
|
|
@@ -9120,43 +9182,102 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9120
9182
|
this._indexCache = null;
|
|
9121
9183
|
return valid.length;
|
|
9122
9184
|
}
|
|
9185
|
+
async listFromDirectoryScan(limit) {
|
|
9186
|
+
const refs = await this.collectSessionFiles(this.dir);
|
|
9187
|
+
const candidates = await mapWithConcurrency(
|
|
9188
|
+
refs,
|
|
9189
|
+
_DefaultSessionStore.LIST_SCAN_CONCURRENCY,
|
|
9190
|
+
async (ref) => {
|
|
9191
|
+
const manifest = await this.readSummaryManifest(ref.id);
|
|
9192
|
+
if (manifest) return { summary: manifest, needsBackfill: false };
|
|
9193
|
+
const summary = await this.summaryHeaderFor(ref);
|
|
9194
|
+
return summary ? { summary, needsBackfill: true } : null;
|
|
9195
|
+
}
|
|
9196
|
+
);
|
|
9197
|
+
const out = candidates.filter((s) => s !== null);
|
|
9198
|
+
out.sort((a, b) => compareSessionSummaries(a.summary, b.summary));
|
|
9199
|
+
const selected = out.slice(0, limit);
|
|
9200
|
+
const summaries = await mapWithConcurrency(
|
|
9201
|
+
selected,
|
|
9202
|
+
Math.min(_DefaultSessionStore.LIST_SCAN_CONCURRENCY, Math.max(1, limit)),
|
|
9203
|
+
async (candidate) => {
|
|
9204
|
+
if (!candidate.needsBackfill) return candidate.summary;
|
|
9205
|
+
return await this.summaryFor(candidate.summary.id).catch(() => candidate.summary);
|
|
9206
|
+
}
|
|
9207
|
+
);
|
|
9208
|
+
return summaries.filter((s) => s !== null);
|
|
9209
|
+
}
|
|
9210
|
+
async collectSessionFiles(dir, prefix = "", depth = 0) {
|
|
9211
|
+
let entries;
|
|
9212
|
+
try {
|
|
9213
|
+
entries = await fsp6.readdir(dir, { withFileTypes: true });
|
|
9214
|
+
} catch {
|
|
9215
|
+
return [];
|
|
9216
|
+
}
|
|
9217
|
+
const dirEntries = [];
|
|
9218
|
+
const files = [];
|
|
9219
|
+
for (const entry of entries) {
|
|
9220
|
+
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
9221
|
+
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
9222
|
+
continue;
|
|
9223
|
+
if (entry.isDirectory()) {
|
|
9224
|
+
dirEntries.push(entry);
|
|
9225
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
9226
|
+
if (entry.name === "_index.jsonl") continue;
|
|
9227
|
+
const base = entry.name.replace(/\.jsonl$/, "");
|
|
9228
|
+
const id = prefix ? `${prefix}/${base}` : base;
|
|
9229
|
+
files.push({ id, filePath: path5.join(dir, entry.name) });
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
const childFileArrays = await Promise.all(
|
|
9233
|
+
dirEntries.map((entry) => {
|
|
9234
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
9235
|
+
return this.collectSessionFiles(path5.join(dir, entry.name), childPrefix, depth + 1);
|
|
9236
|
+
})
|
|
9237
|
+
);
|
|
9238
|
+
return [...childFileArrays.flat(), ...files];
|
|
9239
|
+
}
|
|
9123
9240
|
/** Recursively collect session IDs from date-shard subdirectories.
|
|
9124
9241
|
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
|
|
9125
9242
|
* Skips `.jsonl`/`.summary.json` root files, dot-files, and
|
|
9126
9243
|
* sub-directories that belong to fleet/subagent sessions. */
|
|
9127
9244
|
async collectSessionIds(dir, prefix = "", depth = 0) {
|
|
9128
|
-
const ids = [];
|
|
9129
9245
|
let entries;
|
|
9130
9246
|
try {
|
|
9131
9247
|
entries = await fsp6.readdir(dir, { withFileTypes: true });
|
|
9132
9248
|
} catch {
|
|
9133
|
-
return
|
|
9249
|
+
return [];
|
|
9134
9250
|
}
|
|
9251
|
+
const dirEntries = [];
|
|
9252
|
+
const fileIds = [];
|
|
9135
9253
|
for (const entry of entries) {
|
|
9136
9254
|
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
9137
9255
|
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
9138
9256
|
continue;
|
|
9139
9257
|
if (entry.isDirectory()) {
|
|
9140
|
-
|
|
9141
|
-
ids.push(...await this.collectSessionIds(path5.join(dir, entry.name), childPrefix, depth + 1));
|
|
9258
|
+
dirEntries.push(entry);
|
|
9142
9259
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
9143
9260
|
if (entry.name === "_index.jsonl") continue;
|
|
9144
9261
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
9145
|
-
|
|
9262
|
+
fileIds.push(prefix ? `${prefix}/${base}` : base);
|
|
9146
9263
|
}
|
|
9147
9264
|
}
|
|
9148
|
-
|
|
9265
|
+
const childIdArrays = await Promise.all(
|
|
9266
|
+
dirEntries.map((entry) => {
|
|
9267
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
9268
|
+
return this.collectSessionIds(path5.join(dir, entry.name), childPrefix, depth + 1);
|
|
9269
|
+
})
|
|
9270
|
+
);
|
|
9271
|
+
return [...childIdArrays.flat(), ...fileIds];
|
|
9149
9272
|
}
|
|
9150
9273
|
async summaryFor(id) {
|
|
9151
9274
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
9152
9275
|
const t0 = Date.now();
|
|
9153
9276
|
let outcome = "success";
|
|
9154
9277
|
let errorMsg;
|
|
9278
|
+
const fromManifest = await this.readSummaryManifest(id, t0);
|
|
9279
|
+
if (fromManifest) return fromManifest;
|
|
9155
9280
|
try {
|
|
9156
|
-
const raw = await fsp6.readFile(manifest, "utf8");
|
|
9157
|
-
this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
|
|
9158
|
-
return JSON.parse(raw);
|
|
9159
|
-
} catch {
|
|
9160
9281
|
const full = this.sessionPath(id, ".jsonl");
|
|
9161
9282
|
const stat7 = await fsp6.stat(full);
|
|
9162
9283
|
const summary = await this.summarize(id, stat7.mtime.toISOString());
|
|
@@ -9175,6 +9296,78 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9175
9296
|
errorMsg = "summary fallback \u2014 manifest rebuilt";
|
|
9176
9297
|
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
9177
9298
|
return summary;
|
|
9299
|
+
} catch (err) {
|
|
9300
|
+
outcome = "failure";
|
|
9301
|
+
errorMsg = toErrorMessage(err);
|
|
9302
|
+
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
9303
|
+
return {
|
|
9304
|
+
id,
|
|
9305
|
+
title: "(damaged)",
|
|
9306
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9307
|
+
model: "unknown",
|
|
9308
|
+
provider: "unknown",
|
|
9309
|
+
tokenTotal: 0
|
|
9310
|
+
};
|
|
9311
|
+
}
|
|
9312
|
+
}
|
|
9313
|
+
async readSummaryManifest(id, startTime = Date.now()) {
|
|
9314
|
+
const manifest = this.sessionPath(id, ".summary.json");
|
|
9315
|
+
try {
|
|
9316
|
+
const raw = await fsp6.readFile(manifest, "utf8");
|
|
9317
|
+
this.emitRead(id, manifest, "summary", "success", Date.now() - startTime);
|
|
9318
|
+
return JSON.parse(raw);
|
|
9319
|
+
} catch {
|
|
9320
|
+
return null;
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
async summaryHeaderFor(ref) {
|
|
9324
|
+
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
9325
|
+
try {
|
|
9326
|
+
const stat7 = await fsp6.stat(ref.filePath);
|
|
9327
|
+
if (!stat7.isFile()) {
|
|
9328
|
+
return {
|
|
9329
|
+
id: ref.id,
|
|
9330
|
+
title: "(damaged)",
|
|
9331
|
+
startedAt: stat7.mtime.toISOString(),
|
|
9332
|
+
model: "unknown",
|
|
9333
|
+
provider: "unknown",
|
|
9334
|
+
tokenTotal: 0
|
|
9335
|
+
};
|
|
9336
|
+
}
|
|
9337
|
+
mtime = stat7.mtime.toISOString();
|
|
9338
|
+
} catch {
|
|
9339
|
+
return null;
|
|
9340
|
+
}
|
|
9341
|
+
try {
|
|
9342
|
+
for await (const event of this.iterSessionEvents(ref.filePath)) {
|
|
9343
|
+
if (event.type === "session_start") {
|
|
9344
|
+
return {
|
|
9345
|
+
id: ref.id,
|
|
9346
|
+
title: "(empty session)",
|
|
9347
|
+
startedAt: event.ts,
|
|
9348
|
+
model: event.model ?? "unknown",
|
|
9349
|
+
provider: event.provider ?? "unknown",
|
|
9350
|
+
tokenTotal: 0
|
|
9351
|
+
};
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
return {
|
|
9355
|
+
id: ref.id,
|
|
9356
|
+
title: "(empty session)",
|
|
9357
|
+
startedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
9358
|
+
model: "unknown",
|
|
9359
|
+
provider: "unknown",
|
|
9360
|
+
tokenTotal: 0
|
|
9361
|
+
};
|
|
9362
|
+
} catch {
|
|
9363
|
+
return {
|
|
9364
|
+
id: ref.id,
|
|
9365
|
+
title: "(damaged)",
|
|
9366
|
+
startedAt: mtime,
|
|
9367
|
+
model: "unknown",
|
|
9368
|
+
provider: "unknown",
|
|
9369
|
+
tokenTotal: 0
|
|
9370
|
+
};
|
|
9178
9371
|
}
|
|
9179
9372
|
}
|
|
9180
9373
|
/**
|
|
@@ -9299,39 +9492,62 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9299
9492
|
}
|
|
9300
9493
|
async summarize(id, mtime) {
|
|
9301
9494
|
try {
|
|
9302
|
-
const
|
|
9303
|
-
|
|
9304
|
-
|
|
9495
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
9496
|
+
let title = "(empty session)";
|
|
9497
|
+
let startedAt = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
9498
|
+
let endedAt;
|
|
9499
|
+
let model = "unknown";
|
|
9500
|
+
let provider = "unknown";
|
|
9501
|
+
let tokenIn = 0;
|
|
9502
|
+
let tokenOut = 0;
|
|
9305
9503
|
let iterationCount = 0;
|
|
9306
9504
|
let toolCallCount = 0;
|
|
9307
9505
|
let toolErrorCount = 0;
|
|
9308
9506
|
let fileChangeCount = 0;
|
|
9309
9507
|
const toolBreakdown = {};
|
|
9310
9508
|
let outcome;
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9509
|
+
let lastEventType;
|
|
9510
|
+
let hasError = false;
|
|
9511
|
+
let sawStart = false;
|
|
9512
|
+
for await (const e of this.iterSessionEvents(file)) {
|
|
9513
|
+
lastEventType = e.type;
|
|
9514
|
+
if (e.type === "session_start") {
|
|
9515
|
+
if (!sawStart) {
|
|
9516
|
+
sawStart = true;
|
|
9517
|
+
startedAt = e.ts;
|
|
9518
|
+
model = e.model ?? "unknown";
|
|
9519
|
+
provider = e.provider ?? "unknown";
|
|
9520
|
+
}
|
|
9521
|
+
} else if (e.type === "session_end") {
|
|
9522
|
+
endedAt = e.ts;
|
|
9523
|
+
} else if (e.type === "user_input") {
|
|
9524
|
+
if (title === "(empty session)") title = userInputTitle(e.content);
|
|
9525
|
+
} else if (e.type === "llm_response") {
|
|
9526
|
+
tokenIn += e.usage.input ?? 0;
|
|
9527
|
+
tokenOut += e.usage.output ?? 0;
|
|
9528
|
+
} else if (e.type === "in_flight_start") iterationCount++;
|
|
9314
9529
|
else if (e.type === "tool_call_start") {
|
|
9315
9530
|
toolCallCount++;
|
|
9316
9531
|
toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;
|
|
9317
9532
|
} else if (e.type === "tool_result" && e.isError) toolErrorCount++;
|
|
9318
9533
|
else if (e.type === "file_snapshot") fileChangeCount += e.files.length;
|
|
9534
|
+
else if (e.type === "error" || e.type === "provider_error") hasError = true;
|
|
9319
9535
|
}
|
|
9320
|
-
if (
|
|
9536
|
+
if (lastEventType === "session_end") {
|
|
9321
9537
|
outcome = "completed";
|
|
9322
|
-
} else if (
|
|
9538
|
+
} else if (lastEventType === "in_flight_start") {
|
|
9323
9539
|
outcome = "aborted";
|
|
9324
|
-
} else if (
|
|
9540
|
+
} else if (hasError) {
|
|
9325
9541
|
outcome = "error";
|
|
9326
9542
|
}
|
|
9327
9543
|
return {
|
|
9328
9544
|
id,
|
|
9329
9545
|
title,
|
|
9330
|
-
startedAt
|
|
9331
|
-
endedAt
|
|
9332
|
-
model
|
|
9333
|
-
provider
|
|
9334
|
-
tokenTotal:
|
|
9546
|
+
startedAt,
|
|
9547
|
+
endedAt,
|
|
9548
|
+
model,
|
|
9549
|
+
provider,
|
|
9550
|
+
tokenTotal: tokenIn + tokenOut,
|
|
9335
9551
|
iterationCount: iterationCount > 0 ? iterationCount : void 0,
|
|
9336
9552
|
toolCallCount: toolCallCount > 0 ? toolCallCount : void 0,
|
|
9337
9553
|
toolErrorCount: toolErrorCount > 0 ? toolErrorCount : void 0,
|
|
@@ -9350,6 +9566,25 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9350
9566
|
};
|
|
9351
9567
|
}
|
|
9352
9568
|
}
|
|
9569
|
+
async *iterSessionEvents(file) {
|
|
9570
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
9571
|
+
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
|
9572
|
+
try {
|
|
9573
|
+
for await (const line of lines) {
|
|
9574
|
+
if (!line.trim()) continue;
|
|
9575
|
+
try {
|
|
9576
|
+
const parsed = JSON.parse(line);
|
|
9577
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
9578
|
+
yield parsed;
|
|
9579
|
+
}
|
|
9580
|
+
} catch {
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9583
|
+
} finally {
|
|
9584
|
+
lines.close();
|
|
9585
|
+
stream.destroy();
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9353
9588
|
metaFromEvents(id, events) {
|
|
9354
9589
|
const start = events.find((e) => e.type === "session_start");
|
|
9355
9590
|
const end = events.findLast((e) => e.type === "session_end");
|
|
@@ -10013,6 +10248,27 @@ function userInputTitle(content) {
|
|
|
10013
10248
|
const text = typeof content === "string" ? content : content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
10014
10249
|
return (text || "(non-text input)").slice(0, 60);
|
|
10015
10250
|
}
|
|
10251
|
+
function compareSessionSummaries(a, b) {
|
|
10252
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
10253
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
10254
|
+
return a.id.localeCompare(b.id);
|
|
10255
|
+
}
|
|
10256
|
+
async function mapWithConcurrency(items, concurrency, fn) {
|
|
10257
|
+
if (items.length === 0) return [];
|
|
10258
|
+
const out = new Array(items.length);
|
|
10259
|
+
let next = 0;
|
|
10260
|
+
const workerCount = Math.min(Math.max(1, concurrency), items.length);
|
|
10261
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
10262
|
+
for (; ; ) {
|
|
10263
|
+
const idx = next++;
|
|
10264
|
+
if (idx >= items.length) return;
|
|
10265
|
+
const item = items[idx];
|
|
10266
|
+
if (item !== void 0) out[idx] = await fn(item);
|
|
10267
|
+
}
|
|
10268
|
+
});
|
|
10269
|
+
await Promise.all(workers);
|
|
10270
|
+
return out;
|
|
10271
|
+
}
|
|
10016
10272
|
|
|
10017
10273
|
// src/coordination/director-session.ts
|
|
10018
10274
|
function makeDirectorSessionFactory(opts) {
|
|
@@ -10487,6 +10743,10 @@ var DefaultMailbox = class {
|
|
|
10487
10743
|
_messageCache = null;
|
|
10488
10744
|
_messageCacheMtime = -1;
|
|
10489
10745
|
_messageCacheSize = -1;
|
|
10746
|
+
/** Primary index: recipient → Set of messages (points into _messageCache). */
|
|
10747
|
+
_byTo = /* @__PURE__ */ new Map();
|
|
10748
|
+
/** Secondary index: sender → Set of messages (points into _messageCache). */
|
|
10749
|
+
_byFrom = /* @__PURE__ */ new Map();
|
|
10490
10750
|
constructor(sessionDir) {
|
|
10491
10751
|
this.filePath = path5.join(sessionDir, MAILBOX_FILE);
|
|
10492
10752
|
}
|
|
@@ -10522,12 +10782,30 @@ var DefaultMailbox = class {
|
|
|
10522
10782
|
}
|
|
10523
10783
|
// ── Query ─────────────────────────────────────────────────────────────
|
|
10524
10784
|
async query(q) {
|
|
10525
|
-
const
|
|
10785
|
+
const needFullScan = q.unreadBy !== void 0 || q.since !== void 0;
|
|
10786
|
+
let candidates;
|
|
10787
|
+
if (needFullScan) {
|
|
10788
|
+
candidates = await this._readAllCached();
|
|
10789
|
+
} else {
|
|
10790
|
+
await this._readAllCached();
|
|
10791
|
+
if (q.to !== void 0) {
|
|
10792
|
+
const direct = this._byTo.get(q.to);
|
|
10793
|
+
const broadcast = this._byTo.get("*");
|
|
10794
|
+
const combined = /* @__PURE__ */ new Map();
|
|
10795
|
+
if (direct) for (const m of direct) combined.set(m.id, m);
|
|
10796
|
+
if (broadcast) for (const m of broadcast) combined.set(m.id, m);
|
|
10797
|
+
candidates = Array.from(combined.values());
|
|
10798
|
+
} else if (q.from !== void 0) {
|
|
10799
|
+
candidates = Array.from(this._byFrom.get(q.from) ?? []);
|
|
10800
|
+
} else {
|
|
10801
|
+
candidates = await this._readAllCached();
|
|
10802
|
+
}
|
|
10803
|
+
}
|
|
10526
10804
|
const limit = q.limit ?? 50;
|
|
10527
10805
|
const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
|
|
10528
10806
|
const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
|
|
10529
10807
|
const filtered = [];
|
|
10530
|
-
for (const msg of
|
|
10808
|
+
for (const msg of candidates) {
|
|
10531
10809
|
if (q.to !== void 0 && msg.to !== q.to && msg.to !== "*") continue;
|
|
10532
10810
|
if (q.from !== void 0 && msg.from !== q.from) continue;
|
|
10533
10811
|
if (q.unreadBy !== void 0 && q.unreadBy in msg.readBy) continue;
|
|
@@ -10628,6 +10906,8 @@ var DefaultMailbox = class {
|
|
|
10628
10906
|
this._messageCache = null;
|
|
10629
10907
|
this._messageCacheMtime = -1;
|
|
10630
10908
|
this._messageCacheSize = -1;
|
|
10909
|
+
this._byTo.clear();
|
|
10910
|
+
this._byFrom.clear();
|
|
10631
10911
|
}
|
|
10632
10912
|
async clearAll() {
|
|
10633
10913
|
await withFileLock(this.filePath, async () => {
|
|
@@ -10686,36 +10966,81 @@ var DefaultMailbox = class {
|
|
|
10686
10966
|
async _readAll() {
|
|
10687
10967
|
try {
|
|
10688
10968
|
const raw = await fsp6.readFile(this.filePath, "utf8");
|
|
10689
|
-
|
|
10690
|
-
const messages = [];
|
|
10691
|
-
for (const line of lines) {
|
|
10692
|
-
try {
|
|
10693
|
-
const parsed = JSON.parse(line);
|
|
10694
|
-
if (!parsed["readBy"]) {
|
|
10695
|
-
const readBy = {};
|
|
10696
|
-
if (parsed["read"] && parsed["readAt"]) {
|
|
10697
|
-
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
10698
|
-
}
|
|
10699
|
-
parsed["readBy"] = readBy;
|
|
10700
|
-
delete parsed["read"];
|
|
10701
|
-
delete parsed["readAt"];
|
|
10702
|
-
}
|
|
10703
|
-
messages.push(parsed);
|
|
10704
|
-
} catch {
|
|
10705
|
-
}
|
|
10706
|
-
}
|
|
10707
|
-
return messages;
|
|
10969
|
+
return this._parseLines(raw);
|
|
10708
10970
|
} catch (err) {
|
|
10709
10971
|
if (err.code === "ENOENT") return [];
|
|
10710
10972
|
throw err;
|
|
10711
10973
|
}
|
|
10712
10974
|
}
|
|
10975
|
+
/**
|
|
10976
|
+
* Read only newly-appended bytes from the file and append them to the
|
|
10977
|
+
* in-memory cache, avoiding a full re-read when the file only grew.
|
|
10978
|
+
*/
|
|
10979
|
+
async _readNewMessagesOnly(fd, oldSize, newSize) {
|
|
10980
|
+
const tailLen = newSize - oldSize;
|
|
10981
|
+
const buf = Buffer.alloc(tailLen);
|
|
10982
|
+
await fd.read(buf, 0, tailLen, oldSize);
|
|
10983
|
+
const tail = buf.toString("utf8");
|
|
10984
|
+
for (const line of tail.split(LINE_SEPARATOR)) {
|
|
10985
|
+
if (!line.trim()) continue;
|
|
10986
|
+
try {
|
|
10987
|
+
const parsed = JSON.parse(line);
|
|
10988
|
+
if (!parsed["readBy"]) {
|
|
10989
|
+
const readBy = {};
|
|
10990
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
10991
|
+
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
10992
|
+
}
|
|
10993
|
+
parsed["readBy"] = readBy;
|
|
10994
|
+
delete parsed["read"];
|
|
10995
|
+
delete parsed["readAt"];
|
|
10996
|
+
}
|
|
10997
|
+
const msg = parsed;
|
|
10998
|
+
this._messageCache.push(msg);
|
|
10999
|
+
this._indexMsg(msg);
|
|
11000
|
+
} catch {
|
|
11001
|
+
}
|
|
11002
|
+
}
|
|
11003
|
+
return this._messageCache;
|
|
11004
|
+
}
|
|
11005
|
+
/** Parse a JSONL string into MailboxMessage[], including migration. */
|
|
11006
|
+
_parseLines(raw) {
|
|
11007
|
+
const lines = raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
|
|
11008
|
+
const messages = [];
|
|
11009
|
+
for (const line of lines) {
|
|
11010
|
+
try {
|
|
11011
|
+
const parsed = JSON.parse(line);
|
|
11012
|
+
if (!parsed["readBy"]) {
|
|
11013
|
+
const readBy = {};
|
|
11014
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
11015
|
+
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
11016
|
+
}
|
|
11017
|
+
parsed["readBy"] = readBy;
|
|
11018
|
+
delete parsed["read"];
|
|
11019
|
+
delete parsed["readAt"];
|
|
11020
|
+
}
|
|
11021
|
+
messages.push(parsed);
|
|
11022
|
+
} catch {
|
|
11023
|
+
}
|
|
11024
|
+
}
|
|
11025
|
+
return messages;
|
|
11026
|
+
}
|
|
10713
11027
|
async _readAllCached() {
|
|
10714
11028
|
try {
|
|
10715
11029
|
const st = await fsp6.stat(this.filePath);
|
|
10716
11030
|
if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
|
|
10717
11031
|
return this._messageCache;
|
|
10718
11032
|
}
|
|
11033
|
+
if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
|
|
11034
|
+
const fd = await fsp6.open(this.filePath, "r");
|
|
11035
|
+
try {
|
|
11036
|
+
const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
|
|
11037
|
+
this._messageCacheMtime = st.mtimeMs;
|
|
11038
|
+
this._messageCacheSize = st.size;
|
|
11039
|
+
return updated;
|
|
11040
|
+
} finally {
|
|
11041
|
+
await fd.close();
|
|
11042
|
+
}
|
|
11043
|
+
}
|
|
10719
11044
|
const all = await this._readAll();
|
|
10720
11045
|
this._setMessageCache(all, st.mtimeMs, st.size);
|
|
10721
11046
|
return all;
|
|
@@ -10732,9 +11057,12 @@ var DefaultMailbox = class {
|
|
|
10732
11057
|
this._messageCache = null;
|
|
10733
11058
|
this._messageCacheMtime = -1;
|
|
10734
11059
|
this._messageCacheSize = -1;
|
|
11060
|
+
this._byTo.clear();
|
|
11061
|
+
this._byFrom.clear();
|
|
10735
11062
|
return;
|
|
10736
11063
|
}
|
|
10737
11064
|
this._messageCache = messages;
|
|
11065
|
+
this._buildIndexes(messages);
|
|
10738
11066
|
if (mtime !== void 0 && size !== void 0) {
|
|
10739
11067
|
this._messageCacheMtime = mtime;
|
|
10740
11068
|
this._messageCacheSize = size;
|
|
@@ -10752,9 +11080,35 @@ var DefaultMailbox = class {
|
|
|
10752
11080
|
this._messageCache = null;
|
|
10753
11081
|
this._messageCacheMtime = -1;
|
|
10754
11082
|
this._messageCacheSize = -1;
|
|
11083
|
+
this._byTo.clear();
|
|
11084
|
+
this._byFrom.clear();
|
|
10755
11085
|
return;
|
|
10756
11086
|
}
|
|
10757
11087
|
this._messageCache.push(msg);
|
|
11088
|
+
this._indexMsg(msg);
|
|
11089
|
+
}
|
|
11090
|
+
/** Rebuild both indexes from a full message list. */
|
|
11091
|
+
_buildIndexes(messages) {
|
|
11092
|
+
this._byTo.clear();
|
|
11093
|
+
this._byFrom.clear();
|
|
11094
|
+
for (const msg of messages) {
|
|
11095
|
+
this._indexMsg(msg);
|
|
11096
|
+
}
|
|
11097
|
+
}
|
|
11098
|
+
/** Add a single message to both indexes. */
|
|
11099
|
+
_indexMsg(msg) {
|
|
11100
|
+
const toSet = this._byTo.get(msg.to);
|
|
11101
|
+
if (toSet) {
|
|
11102
|
+
toSet.add(msg);
|
|
11103
|
+
} else {
|
|
11104
|
+
this._byTo.set(msg.to, /* @__PURE__ */ new Set([msg]));
|
|
11105
|
+
}
|
|
11106
|
+
const fromSet = this._byFrom.get(msg.from);
|
|
11107
|
+
if (fromSet) {
|
|
11108
|
+
fromSet.add(msg);
|
|
11109
|
+
} else {
|
|
11110
|
+
this._byFrom.set(msg.from, /* @__PURE__ */ new Set([msg]));
|
|
11111
|
+
}
|
|
10758
11112
|
}
|
|
10759
11113
|
};
|
|
10760
11114
|
var BrainMonitor = class {
|
|
@@ -11330,30 +11684,69 @@ var GlobalMailbox = class {
|
|
|
11330
11684
|
async _readMessages() {
|
|
11331
11685
|
try {
|
|
11332
11686
|
const raw = await fsp6.readFile(this.messagePath, "utf8");
|
|
11333
|
-
|
|
11334
|
-
const messages = [];
|
|
11335
|
-
for (const line of lines) {
|
|
11336
|
-
try {
|
|
11337
|
-
const parsed = JSON.parse(line);
|
|
11338
|
-
if (!parsed["readBy"]) {
|
|
11339
|
-
const readBy = {};
|
|
11340
|
-
if (parsed["read"] && parsed["readAt"]) {
|
|
11341
|
-
readBy[parsed["to"]] = parsed["readAt"];
|
|
11342
|
-
}
|
|
11343
|
-
parsed["readBy"] = readBy;
|
|
11344
|
-
delete parsed["read"];
|
|
11345
|
-
delete parsed["readAt"];
|
|
11346
|
-
}
|
|
11347
|
-
messages.push(parsed);
|
|
11348
|
-
} catch {
|
|
11349
|
-
}
|
|
11350
|
-
}
|
|
11351
|
-
return messages;
|
|
11687
|
+
return this._parseLines(raw);
|
|
11352
11688
|
} catch (err) {
|
|
11353
11689
|
if (err.code === "ENOENT") return [];
|
|
11354
11690
|
throw err;
|
|
11355
11691
|
}
|
|
11356
11692
|
}
|
|
11693
|
+
/**
|
|
11694
|
+
* Read only newly-appended bytes from the file and append them to the
|
|
11695
|
+
* in-memory cache. This avoids re-reading and re-parsing the entire file
|
|
11696
|
+
* when another process appended messages since our last read.
|
|
11697
|
+
*
|
|
11698
|
+
* Only safe when the file grew in size (messages are append-only). When
|
|
11699
|
+
* the file was rewritten (ack/purge changed existing content), callers
|
|
11700
|
+
* must fall back to {@link _readMessages}.
|
|
11701
|
+
*
|
|
11702
|
+
* @returns The (now up-to-date) message cache.
|
|
11703
|
+
*/
|
|
11704
|
+
async _readNewMessagesOnly(fd, oldSize, newSize) {
|
|
11705
|
+
const tailLen = newSize - oldSize;
|
|
11706
|
+
const buf = Buffer.alloc(tailLen);
|
|
11707
|
+
await fd.read(buf, 0, tailLen, oldSize);
|
|
11708
|
+
const tail = buf.toString("utf8");
|
|
11709
|
+
for (const line of tail.split(LINE_SEPARATOR2)) {
|
|
11710
|
+
if (!line.trim()) continue;
|
|
11711
|
+
try {
|
|
11712
|
+
const parsed = JSON.parse(line);
|
|
11713
|
+
if (!parsed["readBy"]) {
|
|
11714
|
+
const readBy = {};
|
|
11715
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
11716
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
11717
|
+
}
|
|
11718
|
+
parsed["readBy"] = readBy;
|
|
11719
|
+
delete parsed["read"];
|
|
11720
|
+
delete parsed["readAt"];
|
|
11721
|
+
}
|
|
11722
|
+
this._messageCache.push(parsed);
|
|
11723
|
+
} catch {
|
|
11724
|
+
}
|
|
11725
|
+
}
|
|
11726
|
+
return this._messageCache;
|
|
11727
|
+
}
|
|
11728
|
+
/** Parse a JSONL string into MailboxMessage[], including migration. */
|
|
11729
|
+
_parseLines(raw) {
|
|
11730
|
+
const lines = raw.split(LINE_SEPARATOR2).filter((l) => l.trim().length > 0);
|
|
11731
|
+
const messages = [];
|
|
11732
|
+
for (const line of lines) {
|
|
11733
|
+
try {
|
|
11734
|
+
const parsed = JSON.parse(line);
|
|
11735
|
+
if (!parsed["readBy"]) {
|
|
11736
|
+
const readBy = {};
|
|
11737
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
11738
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
11739
|
+
}
|
|
11740
|
+
parsed["readBy"] = readBy;
|
|
11741
|
+
delete parsed["read"];
|
|
11742
|
+
delete parsed["readAt"];
|
|
11743
|
+
}
|
|
11744
|
+
messages.push(parsed);
|
|
11745
|
+
} catch {
|
|
11746
|
+
}
|
|
11747
|
+
}
|
|
11748
|
+
return messages;
|
|
11749
|
+
}
|
|
11357
11750
|
/**
|
|
11358
11751
|
* Read messages, then adopt the result as the in-memory cache. Use this
|
|
11359
11752
|
* from writers that just took the file lock — the read reflects the
|
|
@@ -11373,6 +11766,10 @@ var GlobalMailbox = class {
|
|
|
11373
11766
|
* stat matches the cached mtime+size we return the cached array — no
|
|
11374
11767
|
* file read and no JSON.parse — collapsing the per-iteration query
|
|
11375
11768
|
* cost on the mailbox-loop hot path.
|
|
11769
|
+
*
|
|
11770
|
+
* When the file only grew (new messages appended by another process),
|
|
11771
|
+
* we read and parse just the tail bytes instead of the entire file.
|
|
11772
|
+
* This avoids re-parsing the full 10K-message history on every check.
|
|
11376
11773
|
*/
|
|
11377
11774
|
async _readMessagesCached() {
|
|
11378
11775
|
try {
|
|
@@ -11380,6 +11777,17 @@ var GlobalMailbox = class {
|
|
|
11380
11777
|
if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
|
|
11381
11778
|
return this._messageCache;
|
|
11382
11779
|
}
|
|
11780
|
+
if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
|
|
11781
|
+
const fd = await fsp6.open(this.messagePath, "r");
|
|
11782
|
+
try {
|
|
11783
|
+
const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
|
|
11784
|
+
this._messageCacheMtime = st.mtimeMs;
|
|
11785
|
+
this._messageCacheSize = st.size;
|
|
11786
|
+
return updated;
|
|
11787
|
+
} finally {
|
|
11788
|
+
await fd.close();
|
|
11789
|
+
}
|
|
11790
|
+
}
|
|
11383
11791
|
const all = await this._readMessages();
|
|
11384
11792
|
this._setMessageCache(all, st.mtimeMs, st.size);
|
|
11385
11793
|
return all;
|
|
@@ -14814,7 +15222,263 @@ ${input.detail}`
|
|
|
14814
15222
|
this.onCoordinatorEvent?.(event);
|
|
14815
15223
|
}
|
|
14816
15224
|
};
|
|
15225
|
+
var AgentMonitorService = class {
|
|
15226
|
+
_fleetBus;
|
|
15227
|
+
_events;
|
|
15228
|
+
_transcriptsDir;
|
|
15229
|
+
_maxEntries;
|
|
15230
|
+
_streamEnabled;
|
|
15231
|
+
_onEntry;
|
|
15232
|
+
/** Per-subagent virtual sessions. */
|
|
15233
|
+
_sessions = /* @__PURE__ */ new Map();
|
|
15234
|
+
/** Disposers for FleetBus subscriptions, keyed by subagentId. */
|
|
15235
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
15236
|
+
/** Generic fleet-wide subscription disposer. */
|
|
15237
|
+
_fleetDisposer;
|
|
15238
|
+
/** Track whether service is running. */
|
|
15239
|
+
_started = false;
|
|
15240
|
+
constructor(opts) {
|
|
15241
|
+
this._fleetBus = opts.fleetBus;
|
|
15242
|
+
this._events = opts.events;
|
|
15243
|
+
this._transcriptsDir = opts.transcriptsDir;
|
|
15244
|
+
this._maxEntries = opts.maxEntriesPerAgent ?? 500;
|
|
15245
|
+
this._streamEnabled = opts.streamEnabled ?? false;
|
|
15246
|
+
this._onEntry = opts.onEntry;
|
|
15247
|
+
}
|
|
15248
|
+
// ── Public API ────────────────────────────────────────────────────
|
|
15249
|
+
/** Set the FleetBus to listen on. Must be called before `start()`. */
|
|
15250
|
+
setFleetBus(bus) {
|
|
15251
|
+
this._fleetBus = bus;
|
|
15252
|
+
}
|
|
15253
|
+
get streamEnabled() {
|
|
15254
|
+
return this._streamEnabled;
|
|
15255
|
+
}
|
|
15256
|
+
/** Enable/disable streaming agent conversations to the main chat timeline. */
|
|
15257
|
+
setStreamEnabled(enabled) {
|
|
15258
|
+
this._streamEnabled = enabled;
|
|
15259
|
+
}
|
|
15260
|
+
/** Get a snapshot of all known agent sessions. */
|
|
15261
|
+
getAllSessions() {
|
|
15262
|
+
return Array.from(this._sessions.values());
|
|
15263
|
+
}
|
|
15264
|
+
/** Get a specific agent's virtual session, or undefined. */
|
|
15265
|
+
getSession(subagentId) {
|
|
15266
|
+
return this._sessions.get(subagentId);
|
|
15267
|
+
}
|
|
15268
|
+
/** Get transcript entries for a specific agent, newest first. */
|
|
15269
|
+
getTranscript(subagentId, limit = 50) {
|
|
15270
|
+
const session = this._sessions.get(subagentId);
|
|
15271
|
+
if (!session) return [];
|
|
15272
|
+
return session.transcript.slice(-limit).reverse();
|
|
15273
|
+
}
|
|
15274
|
+
/** Set a callback for each new timeline entry (HQ bridge). */
|
|
15275
|
+
setOnEntry(handler) {
|
|
15276
|
+
this._onEntry = handler;
|
|
15277
|
+
}
|
|
15278
|
+
// ── Lifecycle ──────────────────────────────────────────────────────
|
|
15279
|
+
/** Start listening to FleetBus events. */
|
|
15280
|
+
start() {
|
|
15281
|
+
if (this._started) return;
|
|
15282
|
+
if (!this._fleetBus) {
|
|
15283
|
+
this._started = true;
|
|
15284
|
+
return;
|
|
15285
|
+
}
|
|
15286
|
+
this._started = true;
|
|
15287
|
+
this._fleetDisposer = this._fleetBus.onAny((event) => {
|
|
15288
|
+
this._routeEvent(event.subagentId, event.type, event.payload);
|
|
15289
|
+
});
|
|
15290
|
+
}
|
|
15291
|
+
/** Stop listening and clean up all subscriptions. */
|
|
15292
|
+
stop() {
|
|
15293
|
+
if (!this._started) return;
|
|
15294
|
+
this._started = false;
|
|
15295
|
+
if (this._fleetDisposer) {
|
|
15296
|
+
this._fleetDisposer();
|
|
15297
|
+
this._fleetDisposer = void 0;
|
|
15298
|
+
}
|
|
15299
|
+
for (const disposer of this._subscriptions.values()) {
|
|
15300
|
+
disposer();
|
|
15301
|
+
}
|
|
15302
|
+
this._subscriptions.clear();
|
|
15303
|
+
}
|
|
15304
|
+
/** Ensure a subagent is being tracked. Called when a subagent spawns. */
|
|
15305
|
+
trackSubagent(subagentId, agentName, task) {
|
|
15306
|
+
if (this._sessions.has(subagentId)) return;
|
|
15307
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15308
|
+
const session = {
|
|
15309
|
+
subagentId,
|
|
15310
|
+
agentName,
|
|
15311
|
+
createdAt: now,
|
|
15312
|
+
status: "spawned",
|
|
15313
|
+
task,
|
|
15314
|
+
transcript: []
|
|
15315
|
+
};
|
|
15316
|
+
this._sessions.set(subagentId, session);
|
|
15317
|
+
this._addEntry(subagentId, {
|
|
15318
|
+
id: this._uid(),
|
|
15319
|
+
subagentId,
|
|
15320
|
+
agentName,
|
|
15321
|
+
ts: now,
|
|
15322
|
+
kind: "system",
|
|
15323
|
+
content: task ? `\u{1F3AF} Spawned: ${task}` : "\u{1F916} Agent spawned",
|
|
15324
|
+
iteration: 0
|
|
15325
|
+
});
|
|
15326
|
+
this._events.emit("agent.status_changed", {
|
|
15327
|
+
subagentId,
|
|
15328
|
+
agentName,
|
|
15329
|
+
status: "spawned",
|
|
15330
|
+
ts: now,
|
|
15331
|
+
summary: task,
|
|
15332
|
+
task
|
|
15333
|
+
});
|
|
15334
|
+
}
|
|
15335
|
+
/** Mark a subagent as completed/failed/etc. Called on subagent finish. */
|
|
15336
|
+
completeSubagent(subagentId, status, summary) {
|
|
15337
|
+
const session = this._sessions.get(subagentId);
|
|
15338
|
+
if (!session) return;
|
|
15339
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15340
|
+
session.status = status;
|
|
15341
|
+
this._addEntry(subagentId, {
|
|
15342
|
+
id: this._uid(),
|
|
15343
|
+
subagentId,
|
|
15344
|
+
agentName: session.agentName,
|
|
15345
|
+
ts: now,
|
|
15346
|
+
kind: "status",
|
|
15347
|
+
content: summary ?? `Agent ${status}`,
|
|
15348
|
+
iteration: 999
|
|
15349
|
+
});
|
|
15350
|
+
this._events.emit("agent.status_changed", {
|
|
15351
|
+
subagentId,
|
|
15352
|
+
agentName: session.agentName,
|
|
15353
|
+
status,
|
|
15354
|
+
ts: now,
|
|
15355
|
+
summary,
|
|
15356
|
+
task: session.task
|
|
15357
|
+
});
|
|
15358
|
+
}
|
|
15359
|
+
// ── Internal ───────────────────────────────────────────────────────
|
|
15360
|
+
_routeEvent(subagentId, type, payload) {
|
|
15361
|
+
const session = this._sessions.get(subagentId);
|
|
15362
|
+
if (!session) return;
|
|
15363
|
+
switch (type) {
|
|
15364
|
+
case "provider.text_delta": {
|
|
15365
|
+
const text = payload.text;
|
|
15366
|
+
if (!text || text.length === 0) return;
|
|
15367
|
+
const iteration = payload.iteration ?? 0;
|
|
15368
|
+
this._addEntry(subagentId, {
|
|
15369
|
+
id: this._uid(),
|
|
15370
|
+
subagentId,
|
|
15371
|
+
agentName: session.agentName,
|
|
15372
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15373
|
+
kind: "text",
|
|
15374
|
+
content: text,
|
|
15375
|
+
iteration
|
|
15376
|
+
});
|
|
15377
|
+
break;
|
|
15378
|
+
}
|
|
15379
|
+
case "provider.thinking_delta": {
|
|
15380
|
+
const text = payload.text;
|
|
15381
|
+
if (!text || text.length === 0) return;
|
|
15382
|
+
const iteration = payload.iteration ?? 0;
|
|
15383
|
+
this._addEntry(subagentId, {
|
|
15384
|
+
id: this._uid(),
|
|
15385
|
+
subagentId,
|
|
15386
|
+
agentName: session.agentName,
|
|
15387
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15388
|
+
kind: "text",
|
|
15389
|
+
content: `\u{1F9E0} ${text}`,
|
|
15390
|
+
iteration
|
|
15391
|
+
});
|
|
15392
|
+
break;
|
|
15393
|
+
}
|
|
15394
|
+
case "tool.started": {
|
|
15395
|
+
const name = payload.name;
|
|
15396
|
+
if (!name) return;
|
|
15397
|
+
this._addEntry(subagentId, {
|
|
15398
|
+
id: this._uid(),
|
|
15399
|
+
subagentId,
|
|
15400
|
+
agentName: session.agentName,
|
|
15401
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15402
|
+
kind: "tool_use",
|
|
15403
|
+
content: `\u{1F527} ${name}()`,
|
|
15404
|
+
iteration: payload.iteration ?? 0,
|
|
15405
|
+
toolName: name
|
|
15406
|
+
});
|
|
15407
|
+
break;
|
|
15408
|
+
}
|
|
15409
|
+
case "tool.executed": {
|
|
15410
|
+
const name = payload.name;
|
|
15411
|
+
const ok = payload.ok;
|
|
15412
|
+
const durationMs = payload.durationMs;
|
|
15413
|
+
if (!name) return;
|
|
15414
|
+
const statusIcon = ok ? "\u2705" : "\u274C";
|
|
15415
|
+
const duration = durationMs !== void 0 ? ` (${durationMs}ms)` : "";
|
|
15416
|
+
this._addEntry(subagentId, {
|
|
15417
|
+
id: this._uid(),
|
|
15418
|
+
subagentId,
|
|
15419
|
+
agentName: session.agentName,
|
|
15420
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15421
|
+
kind: "tool_result",
|
|
15422
|
+
content: `${statusIcon} ${name}${duration}`,
|
|
15423
|
+
iteration: payload.iteration ?? 0,
|
|
15424
|
+
toolName: name,
|
|
15425
|
+
toolOk: ok
|
|
15426
|
+
});
|
|
15427
|
+
break;
|
|
15428
|
+
}
|
|
15429
|
+
case "iteration.completed": {
|
|
15430
|
+
const index = payload.index ?? 0;
|
|
15431
|
+
if (index > 0 && index % 5 === 0) {
|
|
15432
|
+
this._addEntry(subagentId, {
|
|
15433
|
+
id: this._uid(),
|
|
15434
|
+
subagentId,
|
|
15435
|
+
agentName: session.agentName,
|
|
15436
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15437
|
+
kind: "status",
|
|
15438
|
+
content: `\u{1F504} Iteration ${index}`,
|
|
15439
|
+
iteration: index
|
|
15440
|
+
});
|
|
15441
|
+
}
|
|
15442
|
+
break;
|
|
15443
|
+
}
|
|
15444
|
+
}
|
|
15445
|
+
}
|
|
15446
|
+
_addEntry(subagentId, entry) {
|
|
15447
|
+
const session = this._sessions.get(subagentId);
|
|
15448
|
+
if (!session) return;
|
|
15449
|
+
session.transcript.push(entry);
|
|
15450
|
+
if (session.transcript.length > this._maxEntries) {
|
|
15451
|
+
session.transcript.splice(0, session.transcript.length - this._maxEntries);
|
|
15452
|
+
}
|
|
15453
|
+
this._appendToFile(subagentId, entry).catch(() => {
|
|
15454
|
+
});
|
|
15455
|
+
this._events.emit("agent.timeline.message", {
|
|
15456
|
+
subagentId: entry.subagentId,
|
|
15457
|
+
agentName: entry.agentName,
|
|
15458
|
+
content: entry.content,
|
|
15459
|
+
kind: entry.kind === "tool_result" ? "tool_use" : entry.kind === "system" ? "status" : entry.kind,
|
|
15460
|
+
iteration: entry.iteration,
|
|
15461
|
+
ts: entry.ts,
|
|
15462
|
+
toolName: entry.toolName,
|
|
15463
|
+
costUsd: entry.costUsd
|
|
15464
|
+
});
|
|
15465
|
+
this._onEntry?.(entry);
|
|
15466
|
+
}
|
|
15467
|
+
async _appendToFile(subagentId, entry) {
|
|
15468
|
+
const dir = path5.join(this._transcriptsDir, subagentId);
|
|
15469
|
+
await fsp6.mkdir(dir, { recursive: true });
|
|
15470
|
+
const filePath = path5.join(dir, "transcript.jsonl");
|
|
15471
|
+
const line = JSON.stringify(entry) + "\n";
|
|
15472
|
+
await fsp6.appendFile(filePath, line, { encoding: "utf8" });
|
|
15473
|
+
}
|
|
15474
|
+
_uid() {
|
|
15475
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
15476
|
+
}
|
|
15477
|
+
};
|
|
15478
|
+
function createAgentMonitorService(opts) {
|
|
15479
|
+
return new AgentMonitorService(opts);
|
|
15480
|
+
}
|
|
14817
15481
|
|
|
14818
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
15482
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AgentMonitorService, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createAgentMonitorService, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
14819
15483
|
//# sourceMappingURL=index.js.map
|
|
14820
15484
|
//# sourceMappingURL=index.js.map
|