@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.
- package/dist/agent-factory.d.ts +1 -1
- package/dist/agent-factory.d.ts.map +1 -1
- package/dist/agent-factory.js +23 -8
- package/dist/agent-factory.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-bridge.d.ts +15 -2
- package/dist/mcp-bridge.d.ts.map +1 -1
- package/dist/mcp-bridge.js +53 -10
- package/dist/mcp-bridge.js.map +1 -1
- package/dist/mem-watchdog.d.ts +63 -0
- package/dist/mem-watchdog.d.ts.map +1 -0
- package/dist/mem-watchdog.js +81 -0
- package/dist/mem-watchdog.js.map +1 -0
- package/dist/personas.d.ts +37 -0
- package/dist/personas.d.ts.map +1 -0
- package/dist/personas.js +325 -0
- package/dist/personas.js.map +1 -0
- package/dist/pi-provider.d.ts +27 -1
- package/dist/pi-provider.d.ts.map +1 -1
- package/dist/pi-provider.js +64 -0
- package/dist/pi-provider.js.map +1 -1
- package/dist/provider-config.d.ts +18 -0
- package/dist/provider-config.d.ts.map +1 -0
- package/dist/provider-config.js +47 -0
- package/dist/provider-config.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +64 -0
- package/dist/server.js.map +1 -1
- package/dist/session-manager.d.ts +70 -1
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +222 -4
- package/dist/session-manager.js.map +1 -1
- package/dist/tools/system-tools.d.ts +11 -2
- package/dist/tools/system-tools.d.ts.map +1 -1
- package/dist/tools/system-tools.js +21 -2
- package/dist/tools/system-tools.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/personas.js
ADDED
|
@@ -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"}
|
package/dist/pi-provider.d.ts
CHANGED
|
@@ -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):
|
|
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;
|
|
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"}
|
package/dist/pi-provider.js
CHANGED
|
@@ -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
|
package/dist/pi-provider.js.map
CHANGED
|
@@ -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;
|
|
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;AAqCjC,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;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAkB,EAClB,QAAgB,EAChB,GAA0B;IAE1B,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAE3D,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,uEAAuE;IACvE,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAC5B;QACE,SAAS,EAAE;YACT,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,GAAG,EAAE,oBAAoB;gBACzB,qEAAqE;gBACrE,MAAM,EAAE,gBAAgB,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,EAAE;gBAChE,MAAM,EAAE;oBACN;wBACE,EAAE,EAAE,GAAG,CAAC,OAAO;wBACf,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,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,wEAAwE;IACxE,4EAA4E;IAC5E,MAAM,WAAW,GAAkB,GAAG,CAAC,WAAW,CAAC,QAAQ;QACzD,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE;QAC5B,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACpF,WAAW,CAAC,gBAAgB,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAE3D,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,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC;AAC/C,CAAC;AAED,sDAAsD;AACtD,SAAS,QAAQ,CAAC,EAAU;IAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -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"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
};
|