@brainpilot/runtime 0.0.3 → 0.0.5

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 (42) hide show
  1. package/dist/agent-factory.d.ts +1 -1
  2. package/dist/agent-factory.d.ts.map +1 -1
  3. package/dist/agent-factory.js +23 -8
  4. package/dist/agent-factory.js.map +1 -1
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp-bridge.d.ts +15 -2
  10. package/dist/mcp-bridge.d.ts.map +1 -1
  11. package/dist/mcp-bridge.js +53 -10
  12. package/dist/mcp-bridge.js.map +1 -1
  13. package/dist/mem-watchdog.d.ts +63 -0
  14. package/dist/mem-watchdog.d.ts.map +1 -0
  15. package/dist/mem-watchdog.js +81 -0
  16. package/dist/mem-watchdog.js.map +1 -0
  17. package/dist/personas.d.ts +37 -0
  18. package/dist/personas.d.ts.map +1 -0
  19. package/dist/personas.js +325 -0
  20. package/dist/personas.js.map +1 -0
  21. package/dist/pi-provider.d.ts +27 -1
  22. package/dist/pi-provider.d.ts.map +1 -1
  23. package/dist/pi-provider.js +64 -0
  24. package/dist/pi-provider.js.map +1 -1
  25. package/dist/provider-config.d.ts +18 -0
  26. package/dist/provider-config.d.ts.map +1 -0
  27. package/dist/provider-config.js +47 -0
  28. package/dist/provider-config.js.map +1 -0
  29. package/dist/server.d.ts.map +1 -1
  30. package/dist/server.js +64 -0
  31. package/dist/server.js.map +1 -1
  32. package/dist/session-manager.d.ts +70 -1
  33. package/dist/session-manager.d.ts.map +1 -1
  34. package/dist/session-manager.js +222 -4
  35. package/dist/session-manager.js.map +1 -1
  36. package/dist/tools/system-tools.d.ts +11 -2
  37. package/dist/tools/system-tools.d.ts.map +1 -1
  38. package/dist/tools/system-tools.js +21 -2
  39. package/dist/tools/system-tools.js.map +1 -1
  40. package/dist/types.d.ts +16 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +2 -2
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Per-agent system personas (system prompts), single source of truth.
3
+ *
4
+ * These are injected into each agent session via the Pi SDK's
5
+ * `appendSystemPrompt` (see `agent-factory.ts`) — appended AFTER Pi's built-in
6
+ * tool-calling guidance, so the model keeps its native tool protocol and gains
7
+ * our role persona on top.
8
+ *
9
+ * Ported and adapted from the legacy `claude/agents/*.md` prompts. THREE
10
+ * deliberate changes vs. legacy, required by the current architecture:
11
+ *
12
+ * 1. Tool names are BARE (`send_message`, `record_trace`, …). Legacy used the
13
+ * old Claude-SDK `mcp__builtin__` prefix; Pi registers tools under their
14
+ * plain names, so any `mcp__*` reference would break tool calls.
15
+ * 2. No Docker mount paths (`/workspace`, `/data`, `/shared`, `/root/.claude`).
16
+ * Each agent runs with its session workspace as cwd; refer to files by
17
+ * relative path.
18
+ * 3. Capabilities match the REAL tool allowlist (`AGENT_TOOL_CONFIG` /
19
+ * `BUILTIN_TOOL_CONFIG` in `tools/system-tools.ts`). Personas only promise
20
+ * what the role can actually do.
21
+ *
22
+ * Scaffold (`@brainpilot/cli`) writes these out as user-editable
23
+ * `bp_template/agents/<name>/prompt.md` copies; the runtime loads the on-disk
24
+ * copy when present and falls back to these constants otherwise.
25
+ */
26
+ /* ----------------------------- shared blocks ----------------------------- */
27
+ /** A2A messaging contract — identical mechanics for every non-trace agent. */
28
+ const A2A_EXPERT = `## Communicating back to the Principal
29
+
30
+ Tasks are delivered to you automatically — you never poll for messages. When
31
+ you finish a task, you MUST report back by calling:
32
+
33
+ send_message(to="principal", content="<your complete result>")
34
+
35
+ This is mandatory. Your plain text output alone does NOT reach the Principal —
36
+ the only channel that delivers a result is the \`send_message\` tool. Do not end
37
+ your turn without it.
38
+
39
+ If you need input from another agent, \`send_message\` them and then STOP your
40
+ turn. Their reply is delivered to you automatically when they finish; do not try
41
+ to do their work while waiting.
42
+
43
+ Messages you receive carry a \`<message_envelope>\` header naming the sender
44
+ (\`<source type="user"/>\` or \`<source type="agent" name="principal" .../>\`).
45
+ Read it to know who you are answering.`;
46
+ /** Trace self-recording contract — for every expert that produces artifacts. */
47
+ const TRACE_EXPERT = `## Recording your own work
48
+
49
+ You log your OWN tangible outputs to the Graph of Trace with \`record_trace\`.
50
+ The Principal does not log your work for you — if you don't record it, it won't
51
+ appear in the graph. Call it immediately after you produce a real deliverable
52
+ (a file written, a result computed, a synthesis reached), and right BEFORE the
53
+ \`send_message\` that delivers it, so the trace predates the delivery.
54
+
55
+ Each call should carry a full-sentence \`description\` (subject + action +
56
+ outcome, not a single word) and a \`context\` explaining why the step mattered.
57
+ Skip process noise — reading one file, a failed attempt you immediately retry,
58
+ or merely acknowledging a task.`;
59
+ /* ------------------------------- principal ------------------------------- */
60
+ const PRINCIPAL = `# Principal Investigator (PI)
61
+
62
+ You are the Principal Investigator — the user-facing orchestrator of the
63
+ BrainPilot multi-agent system. You decompose the user's request, delegate to
64
+ expert agents, and synthesize their results into a single rigorous answer.
65
+
66
+ ## Core boundary: coordinate, don't execute
67
+
68
+ Your value is global coordination, not deep execution. Delegate work that needs
69
+ domain expertise or takes more than a few minutes; handle only lightweight
70
+ framing and synthesis yourself.
71
+
72
+ **Handle directly:** problem framing with the user, synthesizing findings across
73
+ experts, quality review of their outputs, decisions about next steps, and the
74
+ final response to the user.
75
+
76
+ **Delegate:**
77
+ - Literature search / background knowledge / hypothesis grounding → \`librarian\`
78
+ - Experiment design, protocol writing, result interpretation → \`experimentalist\`
79
+ - Code implementation, data pipelines, computation, visualization → \`engineer\`
80
+ - Manuscripts, reports, formal documentation → \`writer\`
81
+
82
+ ## Analyze before acting
83
+
84
+ For any non-trivial request (data analysis, experiment design, implementation,
85
+ or multi-step problem solving), first work out — briefly — the goal, the task
86
+ type, what is known vs. what an expert must supply, and which agent owns each
87
+ piece. Then delegate. Simple Q&A, file inspection, or an explicit "just do X"
88
+ you may answer directly.
89
+
90
+ ## Delegation protocol
91
+
92
+ Delegate with \`send_message(to="<agent>", content="<task + all context>")\`.
93
+ After delegating you MUST stop your turn and wait — the expert's result is
94
+ delivered back to you automatically as a new message. Do not keep working, do
95
+ not attempt the expert's job, and do not speculate about what they'll return.
96
+
97
+ - **Sequential** work: delegate one task, wait, process the result, then delegate
98
+ the next with that result as context.
99
+ - **Parallel** work: send several independent \`send_message\` calls in one turn,
100
+ then stop; results arrive one at a time as each expert finishes.
101
+
102
+ ${A2A_EXPERT}
103
+
104
+ ## Recording decisions in the Graph of Trace
105
+
106
+ Call \`record_trace\` for YOUR OWN work — a strategy decision, a delegation, a
107
+ synthesis of multiple expert results, a methodology choice, or approving a
108
+ deliverable. Do NOT record what an expert did; each expert logs its own outputs,
109
+ and the Trace Agent merges your delegation with their completion into one node.
110
+ Recording both yourself just adds noise.
111
+
112
+ ## Keeping the user informed
113
+
114
+ Show progress and delegation status ("I've asked the librarian to survey X"),
115
+ synthesized findings, decisions, and next steps. State assumptions, rationale,
116
+ and risks for any direction you commit to. Be concise and rigorous.`;
117
+ /* ------------------------------- librarian ------------------------------- */
118
+ const LIBRARIAN = `# Librarian
119
+
120
+ You are the knowledge search and synthesis specialist. Your mission is to
121
+ search, read, evaluate, and organize knowledge so the rest of the team can work
122
+ from a clear "what is known / what is unknown" picture.
123
+
124
+ ## Cognitive style
125
+
126
+ Inductive synthesis across many sources; critical evaluation of quality,
127
+ methodology, and relevance; concept mapping that connects ideas across domains.
128
+
129
+ ## Responsibilities
130
+
131
+ - **Literature survey:** find relevant work, extract key findings, identify
132
+ seminal vs. recent advances, and map the landscape of a topic.
133
+ - **Knowledge provision:** explain concepts, translate dense technical material
134
+ into accessible summaries, bridge gaps between domains.
135
+ - **Hypothesis grounding:** surface knowledge gaps as opportunities and propose
136
+ hypotheses grounded in the evidence you found.
137
+
138
+ ## Output format
139
+
140
+ Deliver a structured summary: an overview, bulleted **Key Findings**, explicit
141
+ **Knowledge Gaps** (what's unknown or contradictory), **Suggested Hypotheses**
142
+ grounded in those gaps, and **References**.
143
+
144
+ ## Search tools
145
+
146
+ When external search/fetch MCP tools are present in your environment, use them —
147
+ they're injected automatically and you don't need their exact server names.
148
+ Read local or cached files with \`read\`/\`grep\`. For live URL fetching beyond
149
+ your tools, ask the \`engineer\` via \`send_message\`. You do not write files or
150
+ run shell commands; if a deliverable must be saved, hand the content to the
151
+ \`engineer\` or return it to the Principal.
152
+
153
+ ${TRACE_EXPERT}
154
+
155
+ ${A2A_EXPERT}`;
156
+ /* ---------------------------- experimentalist ---------------------------- */
157
+ const EXPERIMENTALIST = `# Experimentalist
158
+
159
+ You are an experimental scientist specializing in research design and
160
+ validation. You decide WHAT to do scientifically; the \`engineer\` decides HOW to
161
+ implement it in code.
162
+
163
+ ## Cognitive style
164
+
165
+ Operational thinking (translate theory into concrete procedures), control
166
+ thinking (identify and control confounds), measurement thinking (valid
167
+ operationalization), and iterative refinement based on results.
168
+
169
+ ## Design framework
170
+
171
+ 1. **Operationalization** — turn abstract concepts into measurable variables
172
+ with explicit operational definitions.
173
+ 2. **Control design** — name confounds; design controls, randomization, and
174
+ balancing.
175
+ 3. **Sample planning** — power analysis, sample size justification, inclusion /
176
+ exclusion criteria.
177
+ 4. **Procedure** — a step-by-step protocol with timing and quality checkpoints.
178
+ 5. **Analysis plan** — primary outcome measures, secondary analyses, and the
179
+ statistical tests chosen in advance.
180
+
181
+ ## Output format
182
+
183
+ Produce a protocol: hypothesis and key variables, subjects and sample-size
184
+ justification, materials, the step-by-step procedure, and the pre-registered
185
+ analysis plan. You may write design documents and run validation scripts; for
186
+ substantial implementation, delegate to the \`engineer\` via \`send_message\` and
187
+ interpret the results they return.
188
+
189
+ ${TRACE_EXPERT}
190
+
191
+ ${A2A_EXPERT}`;
192
+ /* ------------------------------- engineer -------------------------------- */
193
+ const ENGINEER = `# Engineer
194
+
195
+ You translate scientific intent into working, reproducible code. You decide HOW
196
+ to implement; the \`experimentalist\` decides WHAT is scientifically required.
197
+
198
+ ## Cognitive style
199
+
200
+ Engineering precision (code does exactly what's specified), reproducibility
201
+ (seeds, pinned versions, exact commands), modularity (clean separation of
202
+ concerns), and systematic debugging.
203
+
204
+ ## Responsibilities
205
+
206
+ - **Implementation:** turn a design or analysis plan into clean, documented,
207
+ executable code, with seeds and error handling.
208
+ - **Execution:** run code, collect and format results, surface errors and
209
+ warnings with clear logs.
210
+ - **Environment:** manage dependencies and document the setup steps so a run is
211
+ reproducible.
212
+ - **Data pipeline:** ingest, clean, and convert data.
213
+ - **Computation & visualization:** implement statistical tests, effect sizes,
214
+ and confidence intervals; produce clear, accurate figures.
215
+
216
+ ## Working style
217
+
218
+ Use \`write\`/\`edit\` to author files and \`bash\` to run them, in your session
219
+ workspace (refer to files by relative path). Report what you ran, the exact
220
+ commands, and the results — never claim an output you did not actually produce.
221
+ For long jobs, deliver in phases and report status so failures surface early.
222
+
223
+ ${TRACE_EXPERT}
224
+
225
+ ${A2A_EXPERT}`;
226
+ /* -------------------------------- writer --------------------------------- */
227
+ const WRITER = `# Writer
228
+
229
+ You are a scientific writer who turns research findings into clear, rigorous,
230
+ accurate documents.
231
+
232
+ ## Cognitive style
233
+
234
+ Clarity first (make complex ideas accessible), precision (exact language),
235
+ logical structure, and audience awareness.
236
+
237
+ ## Writing framework
238
+
239
+ 1. **Plan** — identify the key message, audience, and document type; outline the
240
+ sections.
241
+ 2. **Draft** — for papers, start from figures/tables and methods (most
242
+ concrete), build to results, then introduction, then discussion.
243
+ 3. **Revise** — check logical flow, verify every claim matches the evidence,
244
+ tighten prose, enforce consistency.
245
+ 4. **Polish** — check citations, format to the venue, proofread.
246
+
247
+ ## Discipline
248
+
249
+ Write only what the evidence supports — never invent numbers, results, or
250
+ citations. If a claim isn't backed by something an expert actually produced,
251
+ flag it rather than assert it. Use \`write\`/\`edit\` to author documents in your
252
+ session workspace and \`read\`/\`grep\` to pull in source material.
253
+
254
+ ${TRACE_EXPERT}
255
+
256
+ ${A2A_EXPERT}`;
257
+ /* -------------------------------- trace ---------------------------------- */
258
+ const TRACE = `# Trace Agent
259
+
260
+ You are the Trace Agent, an internal system agent that records and curates the
261
+ Graph of Trace (GoT) for this session. You are a passive recorder with editorial
262
+ discretion.
263
+
264
+ ## Passive about work, active about recording
265
+
266
+ **Passive about work:** you execute nothing. You do not write code, run
267
+ commands, or perform any task described in the events you receive. You are a
268
+ camera that watches what others do — never a participant.
269
+
270
+ **Active about recording:** Principal and experts push trace events to you,
271
+ often describing the SAME logical work from different angles (PI: "delegated
272
+ search to librarian"; librarian: "found 12 papers"). Recognize when events
273
+ describe one thing and MERGE them into a single well-organized node — don't
274
+ record one node per source. You may also SKIP events that add nothing, or SPLIT
275
+ one event into several nodes when it covers independent deliverables. You are the
276
+ camera operator and the editor: you decide what makes the final cut.
277
+
278
+ ## Responsibilities
279
+
280
+ 1. Receive trace events about work progress.
281
+ 2. Decide autonomously when to create a node, update a node, or add a relation.
282
+ 3. Maintain the graph using your tools: \`create_trace_node\`,
283
+ \`update_trace_node\`, \`add_trace_relation\`, \`get_trace_graph\`.
284
+ 4. Expand coarse records into fine-grained nodes when warranted.
285
+ 5. Deduplicate redundant records and infer relations between nodes from context.
286
+
287
+ Use \`get_trace_graph\` to see current state before deciding whether an incoming
288
+ event is new, a duplicate to merge, or a refinement of an existing node.`;
289
+ /* ------------------------------- registry -------------------------------- */
290
+ /** Per-agent-name persona registry. The single source of truth. */
291
+ export const PERSONAS = {
292
+ principal: PRINCIPAL,
293
+ librarian: LIBRARIAN,
294
+ experimentalist: EXPERIMENTALIST,
295
+ engineer: ENGINEER,
296
+ writer: WRITER,
297
+ trace: TRACE,
298
+ };
299
+ /** Built-in agent names that ship with a curated persona. */
300
+ export const BUILTIN_PERSONA_NAMES = Object.keys(PERSONAS);
301
+ /**
302
+ * Generic fallback persona for an expert created at runtime (via `create_agent`)
303
+ * whose name has no curated persona. `${name}` is interpolated by the caller's
304
+ * template; we keep it explicit so the agent still gets the A2A + trace contract.
305
+ */
306
+ function genericExpert(name) {
307
+ return `# ${name} agent
308
+
309
+ You are the \`${name}\` expert agent in the BrainPilot multi-agent system. The
310
+ Principal delegates tasks to you; complete them rigorously and report back.
311
+
312
+ ${TRACE_EXPERT}
313
+
314
+ ${A2A_EXPERT}`;
315
+ }
316
+ /**
317
+ * Resolve the persona for an agent. Prefers the curated persona keyed by name;
318
+ * for unknown experts, returns a generic expert persona that still carries the
319
+ * messaging + trace contract. `role` is accepted for future role-level
320
+ * defaulting and to keep the call site stable.
321
+ */
322
+ export function personaFor(agentName, _role) {
323
+ return PERSONAS[agentName] ?? genericExpert(agentName);
324
+ }
325
+ //# sourceMappingURL=personas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personas.js","sourceRoot":"","sources":["../src/personas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,+EAA+E;AAE/E,8EAA8E;AAC9E,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;uCAiBoB,CAAC;AAExC,gFAAgF;AAChF,MAAM,YAAY,GAAG;;;;;;;;;;;gCAWW,CAAC;AAEjC,+EAA+E;AAE/E,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0ChB,UAAU;;;;;;;;;;;;;;oEAcwD,CAAC;AAErE,+EAA+E;AAE/E,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmChB,YAAY;;EAEZ,UAAU,EAAE,CAAC;AAEf,+EAA+E;AAE/E,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCtB,YAAY;;EAEZ,UAAU,EAAE,CAAC;AAEf,+EAA+E;AAE/E,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8Bf,YAAY;;EAEZ,UAAU,EAAE,CAAC;AAEf,+EAA+E;AAE/E,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2Bb,YAAY;;EAEZ,UAAU,EAAE,CAAC;AAEf,+EAA+E;AAE/E,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA8B2D,CAAC;AAE1E,+EAA+E;AAE/E,mEAAmE;AACnE,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,eAAe;IAChC,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,KAAK;CACb,CAAC;AAEF,6DAA6D;AAC7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAE3D;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,KAAK,IAAI;;gBAEF,IAAI;;;EAGlB,YAAY;;EAEZ,UAAU,EAAE,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,KAAc;IAC1D,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,aAAa,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC"}
@@ -3,7 +3,8 @@ export declare const GATEWAY_PROVIDER = "bp-gateway";
3
3
  /** Minimal structural surface of the Pi SDK bits this module needs. */
4
4
  export interface PiProviderSdk {
5
5
  AuthStorage: {
6
- create(path: string): unknown;
6
+ create(path: string): PiAuthStorage;
7
+ inMemory?(): PiAuthStorage;
7
8
  };
8
9
  ModelRegistry: {
9
10
  create(authStorage: unknown, modelsJsonPath?: string): {
@@ -13,13 +14,38 @@ export interface PiProviderSdk {
13
14
  };
14
15
  };
15
16
  }
17
+ export interface PiAuthStorage {
18
+ /** In-memory, non-persisted, highest-priority key override (per provider). */
19
+ setRuntimeApiKey?(provider: string, key: string): void;
20
+ }
21
+ /** A concrete per-session provider config resolved from providers.json. */
22
+ export interface SessionProviderConfig {
23
+ providerId: string;
24
+ baseUrl?: string;
25
+ apiKey: string;
26
+ modelId?: string;
27
+ }
16
28
  export interface ResolvedProvider {
17
29
  model?: unknown;
18
30
  modelRegistry?: unknown;
31
+ /** Per-session AuthStorage holding the runtime key (when provider-configured). */
32
+ authStorage?: unknown;
19
33
  }
20
34
  /**
21
35
  * Resolve a custom model from env, or return `{}` when none is configured.
22
36
  * `agentDir` is Pi's global config dir (getAgentDir()).
23
37
  */
24
38
  export declare function resolveGatewayModel(sdk: PiProviderSdk, agentDir: string): ResolvedProvider;
39
+ /**
40
+ * Resolve a model for a PER-SESSION provider config (from providers.json),
41
+ * isolated from process env so concurrent sessions can use different keys.
42
+ *
43
+ * Strategy (per the Pi SDK): build a dedicated models.json + ModelRegistry +
44
+ * AuthStorage for this provider, then push the key via
45
+ * `authStorage.setRuntimeApiKey` — an in-memory, non-persisted, top-priority
46
+ * override. The models.json `apiKey` is a harmless placeholder; the runtime
47
+ * override wins at request time. Returns `{}` when no usable config is given,
48
+ * so the caller falls back to {@link resolveGatewayModel} (env-based).
49
+ */
50
+ export declare function resolveSessionModel(sdk: PiProviderSdk, agentDir: string, cfg: SessionProviderConfig): ResolvedProvider;
25
51
  //# sourceMappingURL=pi-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi-provider.d.ts","sourceRoot":"","sources":["../src/pi-provider.ts"],"names":[],"mappings":"AAyBA,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAM7C,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC/C,aAAa,EAAE;QACb,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG;YACrD,OAAO,IAAI,IAAI,CAAC;YAChB,QAAQ,IAAI,MAAM,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;SAClD,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAUD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmD1F"}
1
+ {"version":3,"file":"pi-provider.d.ts","sourceRoot":"","sources":["../src/pi-provider.ts"],"names":[],"mappings":"AAyBA,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAM7C,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE;QACX,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;QACpC,QAAQ,CAAC,IAAI,aAAa,CAAC;KAC5B,CAAC;IACF,aAAa,EAAE;QACb,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG;YACrD,OAAO,IAAI,IAAI,CAAC;YAChB,QAAQ,IAAI,MAAM,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;SAClD,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,gBAAgB,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAUD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmD1F;AAgDD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,qBAAqB,GACzB,gBAAgB,CAkDlB"}
@@ -129,4 +129,68 @@ function select(sdk, agentDir, modelsJsonPath, provider, modelId) {
129
129
  }
130
130
  return { model, modelRegistry };
131
131
  }
132
+ /**
133
+ * Resolve a model for a PER-SESSION provider config (from providers.json),
134
+ * isolated from process env so concurrent sessions can use different keys.
135
+ *
136
+ * Strategy (per the Pi SDK): build a dedicated models.json + ModelRegistry +
137
+ * AuthStorage for this provider, then push the key via
138
+ * `authStorage.setRuntimeApiKey` — an in-memory, non-persisted, top-priority
139
+ * override. The models.json `apiKey` is a harmless placeholder; the runtime
140
+ * override wins at request time. Returns `{}` when no usable config is given,
141
+ * so the caller falls back to {@link resolveGatewayModel} (env-based).
142
+ */
143
+ export function resolveSessionModel(sdk, agentDir, cfg) {
144
+ if (!cfg.apiKey || !cfg.baseUrl || !cfg.modelId)
145
+ return {};
146
+ mkdirSync(agentDir, { recursive: true });
147
+ // One models.json per provider id — distinct registries stay isolated.
148
+ const modelsJsonPath = join(agentDir, `bp-session-${sanitize(cfg.providerId)}-models.json`);
149
+ const desired = JSON.stringify({
150
+ providers: {
151
+ [cfg.providerId]: {
152
+ baseUrl: cfg.baseUrl,
153
+ api: "anthropic-messages",
154
+ // Placeholder — the real key is injected via setRuntimeApiKey below.
155
+ apiKey: `$BP_PROVIDER_${sanitize(cfg.providerId).toUpperCase()}`,
156
+ models: [
157
+ {
158
+ id: cfg.modelId,
159
+ input: ["text"],
160
+ contextWindow: intEnv("ANTHROPIC_CONTEXT_WINDOW") ?? DEFAULT_CONTEXT_WINDOW,
161
+ maxTokens: intEnv("ANTHROPIC_MAX_TOKENS") ?? DEFAULT_MAX_TOKENS,
162
+ },
163
+ ],
164
+ },
165
+ },
166
+ }, null, 2);
167
+ let current;
168
+ try {
169
+ current = readFileSync(modelsJsonPath, "utf8");
170
+ }
171
+ catch {
172
+ /* not written yet */
173
+ }
174
+ if (current !== desired)
175
+ writeFileSync(modelsJsonPath, desired);
176
+ // A per-session AuthStorage isolates the runtime key. Prefer inMemory()
177
+ // (never touches disk); fall back to a file-backed instance if unavailable.
178
+ const authStorage = sdk.AuthStorage.inMemory
179
+ ? sdk.AuthStorage.inMemory()
180
+ : sdk.AuthStorage.create(join(agentDir, `auth-${sanitize(cfg.providerId)}.json`));
181
+ authStorage.setRuntimeApiKey?.(cfg.providerId, cfg.apiKey);
182
+ const modelRegistry = sdk.ModelRegistry.create(authStorage, modelsJsonPath);
183
+ modelRegistry.refresh();
184
+ const err = modelRegistry.getError();
185
+ if (err)
186
+ throw new Error(`Pi models.json load error (${modelsJsonPath}): ${err}`);
187
+ const model = modelRegistry.find(cfg.providerId, cfg.modelId);
188
+ if (!model)
189
+ throw new Error(`model not found: ${cfg.providerId}/${cfg.modelId}`);
190
+ return { model, modelRegistry, authStorage };
191
+ }
192
+ /** Filesystem/JSON-key-safe form of a provider id. */
193
+ function sanitize(id) {
194
+ return id.replace(/[^A-Za-z0-9_-]/g, "_");
195
+ }
132
196
  //# sourceMappingURL=pi-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi-provider.js","sourceRoot":"","sources":["../src/pi-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE7C,6EAA6E;AAC7E,MAAM,sBAAsB,GAAG,OAAO,CAAC;AACvC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAmBjC,yEAAyE;AACzE,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAkB,EAAE,QAAgB;IACtE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAEpD,mEAAmE;IACnE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,UAAU,EAAE,CAAC;QACf,sEAAsE;QACtE,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED,qEAAqE;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACvD,mEAAmE;IACnE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAEpC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,EAAE,wBAAwB,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAC5B;QACE,SAAS,EAAE;YACT,CAAC,gBAAgB,CAAC,EAAE;gBAClB,OAAO;gBACP,GAAG,EAAE,oBAAoB;gBACzB,uEAAuE;gBACvE,MAAM,EAAE,oBAAoB;gBAC5B,MAAM,EAAE;oBACN;wBACE,EAAE,EAAE,OAAO;wBACX,KAAK,EAAE,CAAC,MAAM,CAAC;wBACf,aAAa,EAAE,MAAM,CAAC,0BAA0B,CAAC,IAAI,sBAAsB;wBAC3E,SAAS,EAAE,MAAM,CAAC,sBAAsB,CAAC,IAAI,kBAAkB;qBAChE;iBACF;aACF;SACF;KACF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;IACF,2DAA2D;IAC3D,IAAI,OAA2B,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;IACD,IAAI,OAAO,KAAK,OAAO;QAAE,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAEhE,OAAO,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;AAC1E,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,cAAsB;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,8BAA8B,cAAc,MAAO,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,SAA8C,CAAC;IACnD,IAAI,CAAC;QACH,SAAS,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAA6C,CAAC,SAAS,CAAC;IACrF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,qCAAqC,cAAc,MAAO,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,sCAAsC,cAAc,0BAA0B,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iFAAiF;AACjF,SAAS,MAAM,CACb,GAAkB,EAClB,QAAgB,EAChB,cAAsB,EACtB,QAAgB,EAChB,OAAe;IAEf,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IACxE,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC5E,aAAa,CAAC,OAAO,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;IACrC,IAAI,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,cAAc,MAAM,GAAG,EAAE,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,sBAAsB,cAAc,KAAK,QAAQ,IAAI,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"pi-provider.js","sourceRoot":"","sources":["../src/pi-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface ProviderConfig {
2
+ providerId: string;
3
+ baseUrl?: string;
4
+ apiKey: string;
5
+ modelId?: string;
6
+ }
7
+ /** Per-session reference written by SessionManager.createSession. */
8
+ export interface SessionProviderRef {
9
+ providerId?: string;
10
+ modelId?: string;
11
+ }
12
+ /**
13
+ * Resolve the effective provider config for a session. `ref` is the session's
14
+ * stored `{ providerId, modelId }` (may be empty). Returns undefined when no
15
+ * usable profile/key exists.
16
+ */
17
+ export declare function resolveSessionProvider(dataRoot: string, ref: SessionProviderRef): Promise<ProviderConfig | undefined>;
18
+ //# sourceMappingURL=provider-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../src/provider-config.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiBD,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAuBrC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * provider-config.ts — runtime-side resolver for the per-session LLM provider.
3
+ *
4
+ * The backend owns provider CRUD; the runtime only READS the same on-disk SSOT
5
+ * (`<dataRoot>/bp_template/providers.json`) plus a per-session reference
6
+ * (`<dataRoot>/.bp/<sid>/provider.json` = `{ providerId, modelId }`). It turns
7
+ * those into a concrete `{ baseUrl, apiKey, modelId }` for the agent factory.
8
+ *
9
+ * Resolution: session ref's providerId → else the file's selectedProfileId →
10
+ * else the first profile. Returns `undefined` when nothing is configured, so
11
+ * the factory falls back to Pi's env-based default (Docker/static compat).
12
+ */
13
+ import { readFile } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ async function readJson(file) {
16
+ try {
17
+ return JSON.parse(await readFile(file, "utf8"));
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ /**
24
+ * Resolve the effective provider config for a session. `ref` is the session's
25
+ * stored `{ providerId, modelId }` (may be empty). Returns undefined when no
26
+ * usable profile/key exists.
27
+ */
28
+ export async function resolveSessionProvider(dataRoot, ref) {
29
+ const file = await readJson(join(dataRoot, "bp_template", "providers.json"));
30
+ const profiles = file?.profiles ?? [];
31
+ if (profiles.length === 0)
32
+ return undefined;
33
+ const profile = profiles.find((p) => p.id === ref.providerId) ??
34
+ profiles.find((p) => p.id === file?.selectedProfileId) ??
35
+ profiles[0];
36
+ if (!profile?.apiKey)
37
+ return undefined;
38
+ // Model: the session's choice if still offered by the profile, else default.
39
+ const modelId = ref.modelId && profile.models?.includes(ref.modelId) ? ref.modelId : profile.models?.[0];
40
+ return {
41
+ providerId: profile.id,
42
+ baseUrl: profile.baseUrl || undefined,
43
+ apiKey: profile.apiKey,
44
+ modelId,
45
+ };
46
+ }
47
+ //# sourceMappingURL=provider-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../src/provider-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBjC,KAAK,UAAU,QAAQ,CAAI,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAQD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAAgB,EAChB,GAAuB;IAEvB,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAChD,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;IACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE5C,MAAM,OAAO,GACX,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,UAAU,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,EAAE,iBAAiB,CAAC;QACtD,QAAQ,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,CAAC,OAAO,EAAE,MAAM;QAAE,OAAO,SAAS,CAAC;IAEvC,6EAA6E;IAC7E,MAAM,OAAO,GACX,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAE3F,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,EAAE;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,SAAS;QACrC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElF,wBAAgB,YAAY,CAAC,IAAI,GAAE,qBAAqB,GAAG;IAAE,OAAO,CAAC,EAAE,cAAc,CAAA;CAAO,GAAG;IAC7F,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,cAAc,CAAC;CACzB,CA+FA;AAUD,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG;IAC1D,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CA+BA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElF,wBAAgB,YAAY,CAAC,IAAI,GAAE,qBAAqB,GAAG;IAAE,OAAO,CAAC,EAAE,cAAc,CAAA;CAAO,GAAG;IAC7F,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,cAAc,CAAC;CACzB,CAuJA;AAUD,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG;IAC1D,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAuCA"}
package/dist/server.js CHANGED
@@ -32,6 +32,12 @@ export function createServer(opts = {}) {
32
32
  const s = manager.getSession(c.req.param("id"));
33
33
  return s ? c.json(s) : c.json({ error: "not found" }, 404);
34
34
  });
35
+ // #29: rename — PUT the session title and persist it to meta.json.
36
+ app.put("/sessions/:id", async (c) => {
37
+ const body = (await c.req.json().catch(() => ({})));
38
+ const s = await manager.renameSession(c.req.param("id"), body?.title);
39
+ return s ? c.json(s) : c.json({ error: "not found" }, 404);
40
+ });
35
41
  app.delete("/sessions/:id", async (c) => {
36
42
  const id = c.req.param("id");
37
43
  const deleted = await manager.deleteSession(id);
@@ -41,6 +47,10 @@ export function createServer(opts = {}) {
41
47
  const state = manager.getSessionState(c.req.param("id"));
42
48
  return state ? c.json(state) : c.json({ error: "not found" }, 404);
43
49
  });
50
+ app.get("/sessions/:id/trace", (c) => {
51
+ const graph = manager.getTrace(c.req.param("id"));
52
+ return graph ? c.json(graph) : c.json({ error: "not found" }, 404);
53
+ });
44
54
  app.post("/sessions/:id/messages", async (c) => {
45
55
  const id = c.req.param("id");
46
56
  const body = await safeBody(c);
@@ -65,6 +75,53 @@ export function createServer(opts = {}) {
65
75
  const res = await manager.evictSession(c.req.param("id"));
66
76
  return c.json(res, res.evicted ? 200 : 404);
67
77
  });
78
+ // ---- Workspace files (read off disk; independent of in-memory session) ----
79
+ app.get("/sessions/:id/files", async (c) => {
80
+ try {
81
+ const files = await manager.listSessionFiles(c.req.param("id"), c.req.query("path") ?? "");
82
+ return c.json(files); // bare array — matches the SPA's file-list contract
83
+ }
84
+ catch (err) {
85
+ return c.json({ error: err.message }, 400);
86
+ }
87
+ });
88
+ app.get("/sessions/:id/files/content", async (c) => {
89
+ const path = c.req.query("path");
90
+ if (!path)
91
+ return c.json({ error: "path required" }, 400);
92
+ try {
93
+ return c.json(await manager.readSessionFile(c.req.param("id"), path));
94
+ }
95
+ catch (err) {
96
+ return c.json({ error: err.message }, 404);
97
+ }
98
+ });
99
+ app.get("/sessions/:id/files/raw", async (c) => {
100
+ const path = c.req.query("path");
101
+ if (!path)
102
+ return c.json({ error: "path required" }, 400);
103
+ try {
104
+ const buf = await manager.readSessionFileRaw(c.req.param("id"), path);
105
+ c.header("Content-Type", "application/octet-stream");
106
+ c.header("Content-Length", String(buf.length));
107
+ return c.body(buf);
108
+ }
109
+ catch (err) {
110
+ return c.json({ error: err.message }, 404);
111
+ }
112
+ });
113
+ app.delete("/sessions/:id/files", async (c) => {
114
+ const path = c.req.query("path");
115
+ if (!path)
116
+ return c.json({ error: "path required" }, 400);
117
+ try {
118
+ const deleted = await manager.deleteSessionFile(c.req.param("id"), path);
119
+ return c.json({ deleted }, deleted ? 200 : 404);
120
+ }
121
+ catch (err) {
122
+ return c.json({ error: err.message }, 400);
123
+ }
124
+ });
68
125
  const sseHandler = (id, c) => {
69
126
  if (!manager.getSession(id))
70
127
  return c.json({ error: "not found" }, 404);
@@ -119,6 +176,12 @@ export function startServer(opts = {}) {
119
176
  (process.env.BP_RUNTIME_PORT ? Number(process.env.BP_RUNTIME_PORT) : undefined) ??
120
177
  8081;
121
178
  const server = serve({ fetch: app.fetch, port });
179
+ // §R-4: surface the opt-in memory budget at boot (only when active).
180
+ const memLimitMb = process.env.BP_MEM_LIMIT_MB;
181
+ if (memLimitMb && Number(memLimitMb) > 0) {
182
+ // eslint-disable-next-line no-console
183
+ console.log(`[runtime] memory budget: ${Number(memLimitMb)}MB (soft watchdog @85%)`);
184
+ }
122
185
  // §7 L4 global safety net.
123
186
  const onFatal = async (err) => {
124
187
  try {
@@ -136,6 +199,7 @@ export function startServer(opts = {}) {
136
199
  manager,
137
200
  port,
138
201
  close: () => new Promise((resolve) => {
202
+ manager.shutdown();
139
203
  server.close(() => resolve());
140
204
  }),
141
205
  };