@brainpilot/runtime 0.0.4 → 0.0.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.
Files changed (82) hide show
  1. package/README.md +61 -0
  2. package/dist/agent-error.d.ts +51 -0
  3. package/dist/agent-error.d.ts.map +1 -0
  4. package/dist/agent-error.js +163 -0
  5. package/dist/agent-error.js.map +1 -0
  6. package/dist/agent-factory.d.ts.map +1 -1
  7. package/dist/agent-factory.js +45 -10
  8. package/dist/agent-factory.js.map +1 -1
  9. package/dist/events.d.ts +18 -0
  10. package/dist/events.d.ts.map +1 -1
  11. package/dist/events.js +24 -0
  12. package/dist/events.js.map +1 -1
  13. package/dist/extensions/agent-status.d.ts +91 -0
  14. package/dist/extensions/agent-status.d.ts.map +1 -0
  15. package/dist/extensions/agent-status.js +103 -0
  16. package/dist/extensions/agent-status.js.map +1 -0
  17. package/dist/extensions/trace-reminder.d.ts +94 -0
  18. package/dist/extensions/trace-reminder.d.ts.map +1 -0
  19. package/dist/extensions/trace-reminder.js +153 -0
  20. package/dist/extensions/trace-reminder.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/mailbox.d.ts +37 -1
  26. package/dist/mailbox.d.ts.map +1 -1
  27. package/dist/mailbox.js +79 -2
  28. package/dist/mailbox.js.map +1 -1
  29. package/dist/mas-agent.d.ts +74 -12
  30. package/dist/mas-agent.d.ts.map +1 -1
  31. package/dist/mas-agent.js +158 -33
  32. package/dist/mas-agent.js.map +1 -1
  33. package/dist/materialize-skills.d.ts +40 -0
  34. package/dist/materialize-skills.d.ts.map +1 -0
  35. package/dist/materialize-skills.js +141 -0
  36. package/dist/materialize-skills.js.map +1 -0
  37. package/dist/mcp-bridge.d.ts +15 -2
  38. package/dist/mcp-bridge.d.ts.map +1 -1
  39. package/dist/mcp-bridge.js +53 -10
  40. package/dist/mcp-bridge.js.map +1 -1
  41. package/dist/mem-watchdog.d.ts +63 -0
  42. package/dist/mem-watchdog.d.ts.map +1 -0
  43. package/dist/mem-watchdog.js +81 -0
  44. package/dist/mem-watchdog.js.map +1 -0
  45. package/dist/mock-agent.d.ts.map +1 -1
  46. package/dist/mock-agent.js +13 -1
  47. package/dist/mock-agent.js.map +1 -1
  48. package/dist/personas.d.ts +16 -0
  49. package/dist/personas.d.ts.map +1 -1
  50. package/dist/personas.js +651 -8
  51. package/dist/personas.js.map +1 -1
  52. package/dist/pi-provider.d.ts +32 -1
  53. package/dist/pi-provider.d.ts.map +1 -1
  54. package/dist/pi-provider.js +70 -0
  55. package/dist/pi-provider.js.map +1 -1
  56. package/dist/provider-config.d.ts +23 -0
  57. package/dist/provider-config.d.ts.map +1 -0
  58. package/dist/provider-config.js +49 -0
  59. package/dist/provider-config.js.map +1 -0
  60. package/dist/server.d.ts +2 -2
  61. package/dist/server.d.ts.map +1 -1
  62. package/dist/server.js +146 -8
  63. package/dist/server.js.map +1 -1
  64. package/dist/session-manager.d.ts +367 -8
  65. package/dist/session-manager.d.ts.map +1 -1
  66. package/dist/session-manager.js +1082 -39
  67. package/dist/session-manager.js.map +1 -1
  68. package/dist/tools/skill-search.d.ts +53 -0
  69. package/dist/tools/skill-search.d.ts.map +1 -0
  70. package/dist/tools/skill-search.js +269 -0
  71. package/dist/tools/skill-search.js.map +1 -0
  72. package/dist/tools/system-tools.d.ts +22 -1
  73. package/dist/tools/system-tools.d.ts.map +1 -1
  74. package/dist/tools/system-tools.js +149 -21
  75. package/dist/tools/system-tools.js.map +1 -1
  76. package/dist/trace.d.ts +27 -1
  77. package/dist/trace.d.ts.map +1 -1
  78. package/dist/trace.js +60 -3
  79. package/dist/trace.js.map +1 -1
  80. package/dist/types.d.ts +61 -5
  81. package/dist/types.d.ts.map +1 -1
  82. package/package.json +6 -2
@@ -1,4 +1,4 @@
1
- import type { AgUiEvent, AgentStatus, Session } from "@brainpilot/protocol";
1
+ import { type AgUiEvent, type AgentStatus, type FileContent, type FileEntry, type Session, type SessionTokenUsage, type TraceGraph } from "@brainpilot/protocol";
2
2
  import { MasAgent } from "./mas-agent.js";
3
3
  import { McpBridge } from "./mcp-bridge.js";
4
4
  import type { AgentSessionFactory, EventListener } from "./types.js";
@@ -11,17 +11,71 @@ export interface SessionManagerOptions {
11
11
  persist?: boolean;
12
12
  /** Override the external MCP bridge (default: real stdio bridge). Tests inject a fake. */
13
13
  mcpBridge?: McpBridge;
14
+ /**
15
+ * Absolute path to the directory of user-editable skills loaded through Pi's
16
+ * native skill pipeline (`additionalSkillPaths`). When omitted, defaults to
17
+ * `<dataRoot>/bp_template/skills`. The built-in skill content is materialized
18
+ * here on first use (see `materializeSkills`).
19
+ */
20
+ skillsDir?: string;
21
+ /**
22
+ * Absolute path to the directory backing the `skill_search` tool (the second
23
+ * skill-loading path — long-tail domain library NOT exposed via Pi's
24
+ * `<available_skills>`). When omitted, defaults to
25
+ * `<dataRoot>/bp_template/skills-router`. Materialized alongside `skillsDir`.
26
+ */
27
+ routerSkillsDir?: string;
28
+ /**
29
+ * Memory budget in bytes (issue #20 / R-4). When set, an opt-in soft watchdog
30
+ * refuses new work past ~85% of the budget. Defaults to `BP_MEM_LIMIT_MB`
31
+ * (parsed to bytes); `null`/absent → feature disabled, no behavior change.
32
+ * Tests inject this directly.
33
+ */
34
+ memLimitBytes?: number | null;
35
+ /** Override the watchdog's RSS reader (tests inject; default reads real RSS). */
36
+ readRss?: () => number;
37
+ /**
38
+ * Max estimated tokens per tool result before truncation (issue #80).
39
+ * When a tool returns content exceeding this threshold, the result is
40
+ * truncated, the full content is saved to the session workspace, and a
41
+ * warning is surfaced in the chat. Default: 64000 (~224KB text).
42
+ * Set to 0 to disable truncation entirely.
43
+ * Env override: BP_MAX_TOOL_RESULT_TOKENS.
44
+ */
45
+ maxToolResultTokens?: number;
14
46
  }
47
+ /**
48
+ * Conservative token estimation from character count (issue #80).
49
+ * English text averages ~4 chars/token; CJK text ~1-2 chars/token.
50
+ * 3.5 gives a safety margin — we'd rather truncate slightly early than
51
+ * overflow the provider's context window. Exported for tests.
52
+ */
53
+ export declare function estimateTokens(text: string): number;
15
54
  export declare class SessionManager {
16
55
  private readonly sessions;
17
56
  private readonly dataRoot;
18
57
  private readonly agentFactory;
19
58
  private readonly persist;
20
59
  private lastActivityAt;
60
+ private readonly deliveryLoops;
21
61
  private mcpBridge;
22
62
  private mcpTools;
23
63
  private mcpLoaded;
64
+ private readonly skillsDir;
65
+ private readonly routerSkillsDir;
66
+ private skillsMaterialized;
67
+ private readonly memWatchdog;
68
+ private readonly maxToolResultTokens;
24
69
  constructor(opts?: SessionManagerOptions);
70
+ /**
71
+ * Materialize the bundled @brainpilot/skills content into `this.skillsDir`
72
+ * (skip-if-exists) so Pi's native skill pipeline can load it. Idempotent —
73
+ * runs at most once per manager. Called at server startup (so skills exist and
74
+ * are user-visible before any agent runs, incl. Docker pure-compose where no
75
+ * CLI scaffold ran) AND lazily before the first non-trace agent. Best-effort:
76
+ * skills are a convenience, not a hard dependency, so failures are swallowed.
77
+ */
78
+ ensureSkillsMaterialized(): Promise<void>;
25
79
  /**
26
80
  * Load external MCP tools once. No-op in mock mode (BP_MOCK=1) and when no
27
81
  * `mcp_servers.json` is present, so the default path stays zero-overhead.
@@ -29,13 +83,73 @@ export declare class SessionManager {
29
83
  private ensureMcpTools;
30
84
  private bpDir;
31
85
  private workspaceDir;
86
+ /**
87
+ * #60: composer uploads in single-user mode are POSTed against the literal
88
+ * sandbox id `"local"` (the web `LOCAL_SANDBOX.id`), because a file can be
89
+ * attached in the draft composer *before* the real session exists. They land
90
+ * in `workspaces/local/` — but the agent's cwd is `workspaces/<sessionId>/`,
91
+ * so without this it can't read the file the user just attached. We treat
92
+ * `workspaces/local/` as a staging area and drain it into the real session
93
+ * workspace right before the agent runs (see drainLocalUploads).
94
+ */
95
+ private static readonly UPLOAD_STAGING_SID;
96
+ /**
97
+ * #97: max CONSECUTIVE failed delivery runs for one expert before the failure
98
+ * is escalated to the principal instead of self-retried. Matches the legacy
99
+ * circuit-breaker threshold (3). Only `retryable` errors consume retries;
100
+ * a `fatal` error escalates on the first failure regardless of this cap.
101
+ */
102
+ private static readonly MAX_DELIVERY_RETRIES;
32
103
  private historyPath;
33
- /** Skills shared by every session (user-editable `bp_template/skills/`). */
34
- private templateSkillsDir;
35
- /** This session's own skill dir (`.bp/<sid>/skills/`), overrides/augments the template. */
36
- private sessionSkillsDir;
37
104
  /** User-editable persona override for an agent (`bp_template/agents/<name>/prompt.md`). */
38
105
  private agentPromptPath;
106
+ /**
107
+ * Resolve a workspace-relative path to an absolute one, refusing anything that
108
+ * escapes the session's `workspaces/<sid>/` root (path traversal guard). This
109
+ * is the single enforcement point for all file routes.
110
+ *
111
+ * The SPA addresses files with a `/workspace`-rooted convention
112
+ * (`/workspace`, `/workspace/sub/file.txt`); we normalize that to a path
113
+ * relative to the on-disk workspace root before resolving.
114
+ */
115
+ private resolveWorkspacePath;
116
+ /** List one directory level under the session workspace (default: root). */
117
+ listSessionFiles(sid: string, rel?: string): Promise<FileEntry[]>;
118
+ /** Read a workspace text file as UTF-8. */
119
+ readSessionFile(sid: string, rel: string): Promise<FileContent>;
120
+ /** Read a workspace file's raw bytes (images/PDF/download). */
121
+ readSessionFileRaw(sid: string, rel: string): Promise<Buffer>;
122
+ /** Delete a workspace file. Returns false if it was already gone. */
123
+ deleteSessionFile(sid: string, rel: string): Promise<boolean>;
124
+ /**
125
+ * #47: write an uploaded file into the session workspace. Content arrives
126
+ * base64-encoded (binary-safe over the JSON byte chain). The same
127
+ * `resolveWorkspacePath` guard prevents path traversal; parent dirs are
128
+ * created so an upload like `docs/foo.pdf` works. The file lands in the
129
+ * agent's cwd, so it can `read` it by its workspace-relative path.
130
+ * `maxBytes` (default 20 MiB) bounds the decoded size.
131
+ */
132
+ writeSessionFile(sid: string, rel: string, contentBase64: string, maxBytes?: number): Promise<{
133
+ path: string;
134
+ size: number;
135
+ }>;
136
+ /**
137
+ * #60: drain the composer upload staging area (`workspaces/local/`) into a
138
+ * real session's workspace so the agent — whose cwd is `workspaces/<sid>/` —
139
+ * can read files the user attached in the draft composer (when no real
140
+ * session id existed yet, the web uploads against the literal `"local"`
141
+ * sandbox id). Called right before the agent runs.
142
+ *
143
+ * Move semantics: each staged entry is renamed into the session workspace
144
+ * (an existing same-named entry in the session is left untouched and the
145
+ * staged copy is discarded), then the staging area is emptied so files never
146
+ * leak into the next session. No-op when the target IS the staging sid, or
147
+ * when the staging dir is missing/empty. Best-effort: never throws — a copy
148
+ * failure must not block the user's prompt.
149
+ */
150
+ private drainLocalUploads;
151
+ /** Recursively copy a file or directory tree (drainLocalUploads fallback). */
152
+ private copyEntry;
39
153
  /**
40
154
  * Resolve an agent's system persona. Prefers the user-editable on-disk
41
155
  * `bp_template/agents/<name>/prompt.md` (so personas can be tuned without a
@@ -46,8 +160,27 @@ export declare class SessionManager {
46
160
  createSession(input?: {
47
161
  id?: string;
48
162
  title?: string;
163
+ providerId?: string;
164
+ modelId?: string;
165
+ },
166
+ /**
167
+ * Internal restore path (see `restoreFromDisk`): when provided, the entry
168
+ * inherits the on-disk meta.json timestamps verbatim instead of stamping
169
+ * fresh ones, and `writeMeta` is skipped so the canonical file is not
170
+ * clobbered with boot-time values. Public callers should not pass this.
171
+ */
172
+ _restore?: {
173
+ createdAt: string;
174
+ updatedAt: string;
175
+ lastActivityAt: number;
49
176
  }): Promise<Session>;
50
177
  getSession(id: string): Session | undefined;
178
+ /**
179
+ * Update a session's title and persist it to meta.json (#29). A blank or
180
+ * non-string title is ignored (idempotent) so the call can't wipe a title.
181
+ * Returns the updated session, or undefined if the session is unknown.
182
+ */
183
+ renameSession(id: string, title?: unknown): Promise<Session | undefined>;
51
184
  listSessions(): Session[];
52
185
  deleteSession(id: string): Promise<boolean>;
53
186
  /** Evict idle session: stop agents + drop from memory, KEEP disk (§修正2). */
@@ -56,17 +189,182 @@ export declare class SessionManager {
56
189
  agentsKilled: number;
57
190
  }>;
58
191
  /** Send a user message to an agent (default principal). §7 L3 isolated. */
59
- sendMessage(sessionId: string, content: string, agentName?: string): Promise<{
192
+ sendMessage(sessionId: string, content: string, agentName?: string, opts?: {
193
+ uuid?: string;
194
+ }): Promise<{
60
195
  accepted: boolean;
61
196
  runId?: string;
62
197
  }>;
63
- /** Interrupt a session (or a specific agent). */
198
+ /**
199
+ * Ask the terminal user a question on behalf of `agent`. Emits a
200
+ * `user_input_request` event and returns a promise that resolves when
201
+ * `resolveInput` is called with the matching request_id, or rejects if the
202
+ * session is interrupted/evicted. Blocks the calling tool's turn.
203
+ */
204
+ private requestUserInput;
205
+ /**
206
+ * Resolve an outstanding ask_user request. Returns false when the session or
207
+ * request_id is unknown/already consumed (stale answer). Pure lookup; never
208
+ * throws — the server handles 404 for unknown sessions before calling.
209
+ */
210
+ resolveInput(sessionId: string, requestId: string, answer: string): boolean;
211
+ /**
212
+ * Interrupt a session (or a specific agent).
213
+ *
214
+ * Targeted (`agentName` given): abort just that agent. Mailboxes and the
215
+ * principal are left untouched — a narrow "stop this one expert" contract.
216
+ *
217
+ * Whole-session (`agentName` omitted, the Stop button — #90): abort EVERY
218
+ * agent (incl. their running script subprocesses, via Pi `session.abort()`),
219
+ * then clear ALL mailboxes so a queued message can't re-wake a stopped agent,
220
+ * surface a user-facing system_message, and immediately prompt the principal
221
+ * one run with an interrupt notice so PI knows the user interrupted and should
222
+ * await further instructions.
223
+ */
64
224
  interrupt(sessionId: string, agentName?: string): Promise<boolean>;
225
+ /**
226
+ * #90: after a whole-session Stop, prompt the principal one run with an
227
+ * interrupt notice. Mirrors `sendMessage`'s fire-and-track run accounting but
228
+ * emits NO role:"user" text chunk — the notice is system context, not a user
229
+ * bubble. The principal should acknowledge briefly and await the user.
230
+ */
231
+ private notifyPrincipalInterrupted;
232
+ /** Test/diagnostic accessor: number of queued messages in `agent`'s inbox. */
233
+ mailboxCount(sessionId: string, agent: string): number;
234
+ /**
235
+ * Wrap a SystemTool so its execute() results are guarded against overflowing
236
+ * the model's context window (issue #80). When truncation triggers, the full
237
+ * result is saved to `<workspace>/.truncated/` and a system_message warning
238
+ * is emitted. No-op when maxToolResultTokens is 0.
239
+ */
240
+ private wrapToolWithTruncation;
241
+ /**
242
+ * Estimate tokens in a tool result, truncate if over budget, save the full
243
+ * content to the session workspace, and emit a warning event.
244
+ */
245
+ private truncateToolResult;
65
246
  /** Ensure an agent exists (create or resurrect). */
66
247
  ensureAgent(sessionId: string, name: string): Promise<MasAgent>;
248
+ /**
249
+ * 意图二 fallback — the trace-reminder extension calls this (via the factory's
250
+ * `onUnreplied`) when an expert was reminded once and STILL did not
251
+ * `send_message` its delegator (the "silence" path; a hard *error* run is
252
+ * handled separately). We write a NEUTRAL system note into the REAL delegator's
253
+ * mailbox and wake it so it never dead-waits. The delegator is whoever last
254
+ * delegated to this expert (#97 directed escalation), falling back to the
255
+ * principal. This fires during the expert's run (before the clean-run cleanup
256
+ * in `runDeliveryLoop`), so the delegator record is still present. The note
257
+ * only states the fact — the expert ended without delivering a result — and
258
+ * deliberately gives NO directive ("re-delegate", "proceed without it"): the
259
+ * delegator decides what to do. Best-effort: a failed write must never break
260
+ * the agent loop.
261
+ */
262
+ private writeFallbackToDelegator;
263
+ /**
264
+ * #97: snapshot the live team status for injection into the principal's turn
265
+ * (via the agent-status extension's Pi `context` hook). Lists every agent —
266
+ * INCLUDING the principal itself, so it sees its own inbox backlog — with its
267
+ * authoritative status and the number of messages still queued unread in its
268
+ * inbox (`mailbox.count`). Excludes the trace agent (an internal recorder) and
269
+ * any stopped agent (destroyed; irrelevant to current coordination). Returns
270
+ * "" when nothing is worth reporting so the extension injects nothing.
271
+ */
272
+ private renderAgentStatus;
67
273
  destroyAgent(sessionId: string, name: string): Promise<void>;
274
+ /**
275
+ * #76: wake `name` to consume its mailbox. Fire-and-forget — `send_message`
276
+ * calls this after writing; the actual run happens in a serial delivery loop.
277
+ * The re-entrancy guard (`deliveryLoops`) means concurrent wakes for the same
278
+ * agent collapse into the one already-running loop (which re-drains after each
279
+ * turn), so an agent's `prompt` is never invoked concurrently.
280
+ */
281
+ private wakeAgent;
282
+ /**
283
+ * Drain `name`'s inbox and run it, looping so messages that arrive *during* a
284
+ * turn are picked up without a second external wake. Each iteration atomically
285
+ * drains the inbox, ensures the agent, wraps the messages as
286
+ * `<message_envelope>`s (the format the A2A persona documents), and prompts.
287
+ * `MasAgent.prompt` is error-isolated (never throws), so a failed expert turn
288
+ * ends the loop cleanly rather than rejecting. A `session_state` frame is
289
+ * emitted on entry and exit so the derived run-active flag reflects the
290
+ * delegated work even across the await gap between the sender finishing and
291
+ * the target starting.
292
+ */
293
+ private runDeliveryLoop;
294
+ /**
295
+ * #97: react to a failed delegated expert run. Returns true when a self-retry
296
+ * was queued (the loop should continue and re-drain the agent's own inbox),
297
+ * false when the failure was escalated to the principal (the loop should stop).
298
+ *
299
+ * Policy:
300
+ * - `retryable` (rate limit / 5xx / network) AND under the retry cap →
301
+ * re-wake the SAME expert with a neutral system nudge in its own inbox, and
302
+ * surface a `warning` to the user ("retrying n/N"). Re-running may succeed.
303
+ * - `fatal` (auth / missing key / forbidden), OR the cap is reached →
304
+ * escalate: write a NEUTRAL error note to the principal's mailbox + wake it,
305
+ * surface an `error` to the user, and reset the streak so a future task to
306
+ * this expert starts fresh.
307
+ */
308
+ private handleDeliveryError;
309
+ /**
310
+ * #97 directed escalation: resolve who an expert's failure/silence should be
311
+ * reported to. Returns the recorded delegator ONLY when it is a still-live,
312
+ * non-trace agent other than the expert itself (a destroyed/stopped delegator
313
+ * would be wrongly resurrected by the wake, and a self/system target is
314
+ * nonsensical). Otherwise falls back to `principal`, the root coordinator,
315
+ * which always exists and owns un-rooted work.
316
+ */
317
+ private delegatorFor;
318
+ /**
319
+ * #97 error escalation: write a NEUTRAL, error-flavored system note into the
320
+ * REAL delegator's mailbox and wake it, so whoever delegated the work (the
321
+ * principal, or another agent in a chain like auditor→engineer) learns the
322
+ * expert failed rather than dead-waiting. Distinct from
323
+ * `writeFallbackToDelegator` (the "silence" path): this one states an ERROR
324
+ * occurred and carries the error headline as context, but — like the silence
325
+ * note — gives NO directive ("re-delegate" / "proceed"): the delegator decides.
326
+ * Best-effort; never breaks the loop.
327
+ */
328
+ private writeErrorToDelegator;
329
+ /**
330
+ * Wrap drained mailbox messages in the `<message_envelope>` header the A2A
331
+ * persona (`personas.ts`) tells agents to expect, so the model knows who sent
332
+ * each message and why. User-origin messages declare `<source type="user"/>`;
333
+ * agent-origin ones name the sender.
334
+ *
335
+ * 意图一·触发点2 (Pi-native hooks): when the PRINCIPAL receives a message from
336
+ * another agent (not the user — i.e. an expert reporting back), append a single
337
+ * static line nudging it to record_trace any real decision it makes while
338
+ * processing the reply. Stateless, loop-free (at most one line per delivery).
339
+ */
340
+ private renderEnvelopes;
68
341
  /** §10 polling fallback: list agents with authoritative status. */
69
342
  listAgents(sessionId: string): AgentStatus[];
343
+ /**
344
+ * #76: a session is "running" whenever ANY non-trace agent is running, or a
345
+ * mailbox delivery loop is pending for a non-trace target (the loop is
346
+ * registered synchronously inside `send_message`, so this closes the await gap
347
+ * between the sender finishing its turn and the delegated target starting —
348
+ * without it the flag would flicker false in that window). The trace agent is
349
+ * a real spawned agent (record_trace dispatches `trace_event` envelopes into
350
+ * its mailbox and it owns the Graph of Trace as editor, see
351
+ * `system-tools.ts:createRecordTraceTool`), but it is excluded from the
352
+ * AGGREGATE: a trace recording isn't "the user's task is still running". It
353
+ * is still LISTED in `agents[]` with its own status so the Agents panel shows
354
+ * its idle/running transitions live.
355
+ */
356
+ private deriveRunActive;
357
+ /**
358
+ * #70/#76: emit the authoritative live snapshot as a `CUSTOM:session_state`
359
+ * event. This is the wholesale source the web Agents panel replaces its
360
+ * agents list from; it is pushed on every agent status transition
361
+ * (`onStatusChange`), an initial frame in `sendMessage`, and on delivery-loop
362
+ * entry/exit. `runState.active` is DERIVED (any non-trace agent running / a
363
+ * pending delivery), so a delegated expert keeps the run visibly active. The
364
+ * ring buffer replays the last frame on reconnect, so a re-subscribing client
365
+ * recovers the current snapshot. Shape matches `SessionStateSnapshotSchema`.
366
+ */
367
+ private emitSessionState;
70
368
  getSessionState(sessionId: string): {
71
369
  runState: {
72
370
  active: boolean;
@@ -74,13 +372,53 @@ export declare class SessionManager {
74
372
  };
75
373
  agents: AgentStatus[];
76
374
  lastActivityTs: string;
375
+ tokenUsage: SessionTokenUsage;
77
376
  } | undefined;
377
+ /** The session's Graph of Trace (reasoning DAG), or undefined if no session. */
378
+ getTrace(sessionId: string): TraceGraph | undefined;
379
+ /**
380
+ * Read persisted AG-UI events for a session from `.bp/<sid>/events.jsonl`.
381
+ * Used by the web to rehydrate chat history after a runtime restart (the
382
+ * in-memory bus ring buffer only carries `recent()` for live SSE replay).
383
+ *
384
+ * The file is read line-by-line and unparseable lines are skipped so a
385
+ * single corrupt record doesn't poison the whole history.
386
+ *
387
+ * `limit` caps the returned array; when total > limit we return the **tail**
388
+ * (most recent events) for lightweight callers. Default 1000, positive
389
+ * limits are capped at 5000. `limit <= 0` returns the full log and is used by
390
+ * the web rehydrate path so long sessions are not sliced through the middle
391
+ * of a streamed message.
392
+ *
393
+ * Returns `undefined` if the session id isn't in memory — this method is
394
+ * only useful for known sessions (call `restoreFromDisk` first if needed).
395
+ */
396
+ readEventHistory(sessionId: string, opts?: {
397
+ limit?: number;
398
+ }): Promise<{
399
+ events: AgUiEvent[];
400
+ total: number;
401
+ truncated: boolean;
402
+ } | undefined>;
78
403
  metrics(): {
79
404
  activeSessions: number;
80
405
  runningAgents: number;
81
406
  lastActivityAt: string | null;
82
407
  memRss: number;
408
+ memLimitBytes: number | null;
409
+ memRatio: number | null;
83
410
  };
411
+ /**
412
+ * Stop background work (memory watchdog). Called on graceful shutdown so the
413
+ * poll interval doesn't outlive the manager. Idempotent.
414
+ */
415
+ shutdown(): void;
416
+ /**
417
+ * Rising-edge handler when RSS crosses the soft memory threshold (§R-4).
418
+ * Warns every currently-loaded session once so in-flight users see the
419
+ * back-off; new sessions/messages are then refused at their entry points.
420
+ */
421
+ private onMemoryThrottle;
84
422
  subscribe(sessionId: string, listener: EventListener): (() => void) | undefined;
85
423
  recentEvents(sessionId: string): AgUiEvent[];
86
424
  /** §7: flush all persisted state before exit. */
@@ -88,8 +426,29 @@ export declare class SessionManager {
88
426
  private touch;
89
427
  private toSession;
90
428
  private writeMeta;
429
+ private providerRefPath;
430
+ /** Persist this session's `{ providerId, modelId }` reference (no key). */
431
+ private writeProviderRef;
432
+ /** Load a session's stored provider ref from disk (restore path). */
433
+ private readProviderRef;
91
434
  private loadTrace;
92
- /** Restore session list from disk (§10 策略A: agents start idle, lazily revived). */
435
+ private usagePath;
436
+ /** Persist cumulative token usage (best-effort; never throws). */
437
+ private writeUsage;
438
+ /** Rehydrate cumulative token usage from disk (restore path). */
439
+ private loadUsage;
440
+ /**
441
+ * Restore session list from disk. Reads `<dataRoot>/.bp/<id>/meta.json` for
442
+ * every directory and recreates the session entry with its original
443
+ * timestamps preserved (provider ref, mailbox, trace also rehydrate via the
444
+ * normal `createSession` restore path). §10 策略A: agents start idle and
445
+ * are lazily revived when the user actually sends a message.
446
+ *
447
+ * Idempotent — sessions already in memory are skipped, not reset.
448
+ *
449
+ * Returns the ids that were restored this call (i.e. excluding ones that
450
+ * were already loaded or whose meta.json was missing / malformed).
451
+ */
93
452
  restoreFromDisk(): Promise<string[]>;
94
453
  }
95
454
  //# sourceMappingURL=session-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,iBAAiB,EAEtB,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,QAAQ,EAA6B,MAAM,gBAAgB,CAAC;AAMrE,OAAO,EAAE,SAAS,EAAwB,MAAM,iBAAiB,CAAC;AAIlE,OAAO,KAAK,EAAa,mBAAmB,EAAE,aAAa,EAAgC,MAAM,YAAY,CAAC;AA2D9G,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AASD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA2BD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,cAAc,CAAK;IAM3B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IAInD,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,SAAS,CAAS;IAK1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAKnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,kBAAkB,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAGjD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;gBAEjC,IAAI,GAAE,qBAA0B;IAmC5C;;;;;;;OAOG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB/C;;;OAGG;YACW,cAAc;IAgB5B,OAAO,CAAC,KAAK;IAGb,OAAO,CAAC,YAAY;IAGpB;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAW;IACrD;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAK;IACjD,OAAO,CAAC,WAAW;IAGnB,2FAA2F;IAC3F,OAAO,CAAC,eAAe;IAMvB;;;;;;;;OAQG;IACH,OAAO,CAAC,oBAAoB;IAa5B,4EAA4E;IACtE,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAgCnE,2CAA2C;IACrC,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAMrE,+DAA+D;IACzD,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKnE,qEAAqE;IAC/D,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnE;;;;;;;OAOG;IACG,gBAAgB,CACpB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,QAAQ,SAAmB,GAC1B,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAc1C;;;;;;;;;;;;;OAaG;YACW,iBAAiB;IA+C/B,8EAA8E;YAChE,SAAS;IAavB;;;;;OAKG;YACW,WAAW;IAgBnB,aAAa,CACjB,KAAK,GAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAClF;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAC1E,OAAO,CAAC,OAAO,CAAC;IAwEnB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAK3C;;;;OAIG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAW9E,YAAY,IAAI,OAAO,EAAE;IAInB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAajD,4EAA4E;IACtE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAsBnF,2EAA2E;IACrE,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,SAAc,EACvB,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAC3B,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA0DjD;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAU3E;;;;;;;;;;;;OAYG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCxE;;;;;OAKG;IACH,OAAO,CAAC,0BAA0B;IAyBlC,8EAA8E;IAC9E,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAMtD;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAsB9B;;;OAGG;YACW,kBAAkB;IA8DhC,oDAAoD;IAC9C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+FrE;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,wBAAwB;IAehC;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAQnB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlE;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAoBjB;;;;;;;;;;OAUG;YACW,eAAe;IAgC7B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,mBAAmB;IAoD3B;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;;;;;OASG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,eAAe;IAoBvB,mEAAmE;IACnE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAgB5C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,eAAe;IAavB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAWxB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;QAClC,QAAQ,EAAE;YAAE,MAAM,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC;QACpD,MAAM,EAAE,WAAW,EAAE,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,iBAAiB,CAAC;KAC/B,GAAG,SAAS;IAWb,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAKnD;;;;;;;;;;;;;;;;OAgBG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC5B,OAAO,CAAC;QAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAoClF,OAAO,IAAI;QACT,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB;IAiBD;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAIhB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAWxB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;IAK/E,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAM5C,iDAAiD;IAC3C,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,SAAS;YAIH,SAAS;IAavB,OAAO,CAAC,eAAe;IAIvB,2EAA2E;YAC7D,gBAAgB;IAU9B,qEAAqE;YACvD,eAAe;YAUf,SAAS;IASvB,OAAO,CAAC,SAAS;IAIjB,kEAAkE;YACpD,UAAU;IAUxB,iEAAiE;YACnD,SAAS;IAcvB;;;;;;;;;;;OAWG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CA8C3C"}