juneau 0.7.0 → 0.7.1
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/README.md +52 -7
- package/dist/index.cjs +8 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +187 -187
- package/dist/index.js.map +1 -1
- package/dist/server/createSkillSet.d.ts.map +1 -1
- package/dist/server/index.cjs +16 -9
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +228 -146
- package/dist/server/index.js.map +1 -1
- package/dist/server/skillIndex.d.ts +20 -4
- package/dist/server/skillIndex.d.ts.map +1 -1
- package/dist/server/types.d.ts +43 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/withSkillDispatch.d.ts +24 -0
- package/dist/server/withSkillDispatch.d.ts.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -187,7 +187,7 @@ export const myAdapter: AiBackendAdapter = {
|
|
|
187
187
|
If your backend uses ai-sdk, Juneau ships server-side helpers that eliminate the boilerplate of history mapping, activity streaming, and tool failure recovery. Import from the `/server` subpath — zod stays out of the browser bundle.
|
|
188
188
|
|
|
189
189
|
```ts
|
|
190
|
-
import { toSdkMessages, createSkillSet, buildSkillIndex, selectSkills, withToolRecovery } from 'juneau/server';
|
|
190
|
+
import { toSdkMessages, createSkillSet, buildSkillIndex, selectSkills, selectSkillsById, withToolRecovery, withSkillDispatch } from 'juneau/server';
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
**Full integration in ~15 lines:**
|
|
@@ -240,29 +240,72 @@ execute: async ({ query }, { emit }) => {
|
|
|
240
240
|
|
|
241
241
|
Emitted parts share the activity buffer and are drained by `streamToWire` at the same points — they appear in the stream in emit order, before the model's text response.
|
|
242
242
|
|
|
243
|
-
Skill metadata: `title` (human-readable name), `instructions?` (agent workflow text — lazily loaded via `selectSkills`), `readOnly?` (default `true`), `requiresConfirmation?` (default `false`), `tools?` (skill composition — validated at startup, `createSkillSet` throws on a reference to an unknown skill).
|
|
243
|
+
Skill metadata: `title` (human-readable name), `id` (numeric, required — chosen by the consumer, must be unique across the skill map; `createSkillSet` throws at startup on duplicates), `instructions?` (agent workflow text — lazily loaded via `selectSkills` / `selectSkillsById`), `readOnly?` (default `true`), `requiresConfirmation?` (default `false`), `tools?` (skill composition — validated at startup, `createSkillSet` throws on a reference to an unknown skill).
|
|
244
244
|
|
|
245
|
-
`SkillSet` members: `tools`, `skills`, `calledSkillNames`, `drainActivities()`, `hadFailure`, `failureContext`.
|
|
245
|
+
`SkillSet` members: `tools`, `skills`, `skillsById` (Map<number, SkillDefinition>), `calledSkillNames`, `calledSkillIds`, `drainActivities()`, `hadFailure`, `failureContext`.
|
|
246
246
|
|
|
247
247
|
`SkillSetOptions`: `language?` — selects label variant (`'en'` default). `debug?` — emit `console.debug` logs per skill execution (default: `false`).
|
|
248
248
|
|
|
249
249
|
#### `buildSkillIndex(skillSet)`
|
|
250
250
|
|
|
251
|
-
Generates a compact one-liner-per-skill index for the system prompt — registry-driven, so the prompt never drifts from the actual skills:
|
|
251
|
+
Generates a compact one-liner-per-skill index for the system prompt — registry-driven, so the prompt never drifts from the actual skills. Each line includes a numeric ID so the model can request skills by ID rather than by name:
|
|
252
252
|
|
|
253
253
|
```
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
[1] invoiceSearch (read): Find invoices by number, supplier, date, or status.
|
|
255
|
+
[2] invoiceApprove (write, requires confirmation): Approve an invoice.
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
#### `selectSkills(skillSet, skillNames)`
|
|
259
259
|
|
|
260
|
-
Returns concatenated `instructions` for the given skill names — typically `skillSet.calledSkillNames` after phase 1
|
|
260
|
+
Returns concatenated `instructions` for the given skill names — typically `skillSet.calledSkillNames` after phase 1. Workflow instructions load lazily, only for skills the model actually used.
|
|
261
|
+
|
|
262
|
+
#### `selectSkillsById(skillSet, skillIds)`
|
|
263
|
+
|
|
264
|
+
Same as `selectSkills` but resolves by numeric ID — use with `skillSet.calledSkillIds` to avoid string comparisons entirely. Unknown IDs and skills without instructions are skipped silently.
|
|
261
265
|
|
|
262
266
|
#### `streamToWire(fullStream, skillSet?, options?)`
|
|
263
267
|
|
|
264
268
|
Converts ai-sdk `fullStream` to Juneau wire SSE strings. Handles all ai-sdk v7 text chunk types (`text-delta` and `text`), drains activity buffer at the right moment, emits `done` at the end. Pass `{ debug: true }` to log every chunk received from ai-sdk.
|
|
265
269
|
|
|
270
|
+
#### `withSkillDispatch(options)`
|
|
271
|
+
|
|
272
|
+
Deliberate 2-phase skill dispatch — the clean alternative to sending every skill's full instructions on every request.
|
|
273
|
+
|
|
274
|
+
**Phase 1:** stream with `buildSkillIndex` as the system prompt and thin tool definitions. The model picks a skill by calling a tool — `calledSkillIds` is populated as tools fire.
|
|
275
|
+
|
|
276
|
+
**Phase 2:** always runs when at least one skill was called. Receives the full workflow instructions for the called skills (via `selectSkillsById`) and the complete phase 1 message history including tool-call and tool-result turns. The model is called again without tools so it reads the instructions and produces a text response.
|
|
277
|
+
|
|
278
|
+
If no skill was called (model answered directly), the phase 1 text is emitted as-is and the stream ends cleanly — no phase 2 needed.
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
for await (const chunk of withSkillDispatch({
|
|
282
|
+
phase1: () => streamText({
|
|
283
|
+
model,
|
|
284
|
+
system: buildSkillIndex(skillSet), // compact index: [1] search (read): ...
|
|
285
|
+
messages: sdkMessages,
|
|
286
|
+
tools: skillSet.tools,
|
|
287
|
+
maxSteps: 1,
|
|
288
|
+
}),
|
|
289
|
+
phase2: (instructions, history) => streamText({
|
|
290
|
+
model,
|
|
291
|
+
system: instructions, // full workflow text for the chosen skill only
|
|
292
|
+
messages: history, // full phase 1 history incl. tool-call + tool-result
|
|
293
|
+
}),
|
|
294
|
+
skillSet,
|
|
295
|
+
onFinish: ({ text }) => saveAssistantReply(text),
|
|
296
|
+
})) {
|
|
297
|
+
res.write(chunk);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
| Option | Type | Description |
|
|
302
|
+
|---|---|---|
|
|
303
|
+
| `phase1` | `() => ToolRecoveryStreamResult` | Phase 1 stream — model picks a skill via tool call |
|
|
304
|
+
| `phase2` | `(instructions, messages) => { fullStream }` | Phase 2 stream — model executes with full instructions |
|
|
305
|
+
| `skillSet` | `SkillSet` | The skill set used in phase 1 |
|
|
306
|
+
| `onFinish` | `({ text }) => void` | Called once before `done` with accumulated text. Not called on error paths. |
|
|
307
|
+
| `debug` | `boolean` | Log phase decisions to `console.debug`. Default: `false`. |
|
|
308
|
+
|
|
266
309
|
#### `withToolRecovery(options)`
|
|
267
310
|
|
|
268
311
|
Multi-phase pattern for Gemini-style tool calls. Phase 1 streams with tools. Phase 2 triggers only when a tool failed **and** no text was produced — it calls the model without tools and injects the failure context to force a text response. Phase 3 (optional) handles the silent-success case — Gemini 2.5 Flash often treats a successful tool call as its complete response and never writes text. When the tool succeeded but no text was produced, `phase3` receives the full phase 1 message history (resolved from the ai-sdk result's `messages` promise — includes tool-call and tool-result turns, which Gemini requires to accept the history) so a second model call without tools can summarise the result. Errors thrown by phase 2/3 are emitted as wire `error` events instead of a silent `done`. Pass `debug: true` to log phase decisions, the resolved phase 3 history, and the `textProduced` / `hadFailure` state at each decision point.
|
|
@@ -283,6 +326,8 @@ yield* withToolRecovery({
|
|
|
283
326
|
|
|
284
327
|
The wire/message types shared with the client — `AiMessage`, `AiMessageRole`, `AiMessagePart`, `AiTextPart`, `AiSerializedMessage`, `AiStreamEvent`, `JuneauWireEvent` (and its member types) — are also re-exported from `juneau/server`, so backend code never needs to import from the client entry.
|
|
285
328
|
|
|
329
|
+
Server-only types: `SkillSet`, `SkillDefinition`, `SkillExecuteContext`, `SkillSetOptions`, `SkillDispatchOptions`, `ToolRecoveryOptions`, `ToolRecoveryStreamResult`, `StreamToWireOptions`, `CoreMessage`.
|
|
330
|
+
|
|
286
331
|
---
|
|
287
332
|
|
|
288
333
|
### Juneau wire protocol — for Juneau-compatible backends
|