@xandout/libra-harness 0.1.128 → 0.1.130
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 +13 -114
- package/package.json +13 -68
- package/dist/extensions/code-tools/extension.test.d.ts +0 -2
- package/dist/extensions/code-tools/extension.test.d.ts.map +0 -1
- package/dist/extensions/code-tools/extension.test.js +0 -544
- package/dist/extensions/code-tools/extension.test.js.map +0 -1
- package/dist/extensions/disk-session/extension.test.d.ts +0 -2
- package/dist/extensions/disk-session/extension.test.d.ts.map +0 -1
- package/dist/extensions/disk-session/extension.test.js +0 -1101
- package/dist/extensions/disk-session/extension.test.js.map +0 -1
- package/dist/extensions/index.d.ts +0 -3
- package/dist/extensions/index.d.ts.map +0 -1
- package/dist/extensions/index.js +0 -2
- package/dist/extensions/index.js.map +0 -1
- package/dist/extensions/loader.d.ts +0 -205
- package/dist/extensions/loader.d.ts.map +0 -1
- package/dist/extensions/loader.js +0 -315
- package/dist/extensions/loader.js.map +0 -1
- package/dist/extensions/skills/extension.test.d.ts +0 -2
- package/dist/extensions/skills/extension.test.d.ts.map +0 -1
- package/dist/extensions/skills/extension.test.js +0 -184
- package/dist/extensions/skills/extension.test.js.map +0 -1
- package/dist/openai-provider/index.d.ts +0 -3
- package/dist/openai-provider/index.d.ts.map +0 -1
- package/dist/openai-provider/index.js +0 -2
- package/dist/openai-provider/index.js.map +0 -1
- package/dist/openai-provider/server.d.ts +0 -34
- package/dist/openai-provider/server.d.ts.map +0 -1
- package/dist/openai-provider/server.js +0 -335
- package/dist/openai-provider/server.js.map +0 -1
package/README.md
CHANGED
|
@@ -135,67 +135,25 @@ const loggingExtension: Extension = {
|
|
|
135
135
|
agent.use(loggingExtension)
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
### Built-in extensions
|
|
138
|
+
### Built-in extensions
|
|
139
139
|
|
|
140
|
-
Libra ships
|
|
140
|
+
Libra ships the extensions used by `libra-code` as explicit subpath exports:
|
|
141
141
|
|
|
142
142
|
```typescript
|
|
143
|
-
import { createLoggerExtension } from '@xandout/libra-harness/extensions/logger'
|
|
144
143
|
import { createDiskSessionExtension } from '@xandout/libra-harness/extensions/disk-session'
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
import { createCodeToolsExtension } from '@xandout/libra-harness/extensions/code-tools'
|
|
145
|
+
import { createStreamingExtension } from '@xandout/libra-harness/extensions/streaming'
|
|
146
|
+
import { createSkillExtension } from '@xandout/libra-harness/extensions/skills'
|
|
148
147
|
```
|
|
149
148
|
|
|
150
|
-
| Extension |
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
| structured-output | `@xandout/libra-harness/extensions/structured-output` | Validates LLM output against a JSON schema |
|
|
157
|
-
| mcp | `@xandout/libra-harness/extensions/mcp` | Connects to MCP servers, registers tools |
|
|
158
|
-
| skills | `@xandout/libra-harness/extensions/skills` | Loads Agent Skills from directories |
|
|
159
|
-
| filesystem | `@xandout/libra-harness/extensions/filesystem` | File read/write/list tools |
|
|
160
|
-
| scripts | `@xandout/libra-harness/extensions/scripts` | Runs shell scripts in pipeline stages |
|
|
161
|
-
| keyword-extractor | `@xandout/libra-harness/extensions/keyword-extractor` | Extracts keywords from messages (local NLP) |
|
|
162
|
-
| token-stats | `@xandout/libra-harness/extensions/token-stats` | Tracks token usage per turn |
|
|
163
|
-
| tool-buffer | `@xandout/libra-harness/extensions/tool-buffer` | Buffers and replays tool results |
|
|
164
|
-
| auto-steer | `@xandout/libra-harness/extensions/auto-steer` | Auto-injects steering messages based on conditions |
|
|
165
|
-
| emoji | `@xandout/libra-harness/extensions/emoji` | Decorates responses with an emoji prefix |
|
|
166
|
-
| timestamp | `@xandout/libra-harness/extensions/timestamp` | Records start/finish timestamps in metadata |
|
|
167
|
-
| disk-session | `@xandout/libra-harness/extensions/disk-session` | Disk-backed session history per session ID |
|
|
168
|
-
| mem-session | `@xandout/libra-harness/extensions/mem-session` | In-memory session history per session ID |
|
|
169
|
-
| memory | `@xandout/libra-harness/extensions/memory` | Long-term memory with LLM-based extraction |
|
|
170
|
-
|
|
171
|
-
**Priority** controls hook execution order within each lifecycle stage (higher = runs first, ties keep registration order). Set `priority` on any extension whose hooks must run before or after another extension's hooks.
|
|
172
|
-
|
|
173
|
-
See [`src/extensions/README.md`](src/extensions/README.md) for full API docs.
|
|
174
|
-
|
|
175
|
-
### Extension loader
|
|
176
|
-
|
|
177
|
-
For larger setups, `loadExtensions` accepts a mix of factory functions, `Extension` objects, and directory paths. It passes a shared config object to each factory, sorts by priority, and handles cleanup:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { loadExtensions, installExtensions, closeExtensions } from '@xandout/libra-harness/extensions'
|
|
181
|
-
import { createLoggerExtension } from '@xandout/libra-harness/extensions/logger'
|
|
182
|
-
import { createMcpExtension } from '@xandout/libra-harness/extensions/mcp'
|
|
183
|
-
|
|
184
|
-
const loaded = await loadExtensions(
|
|
185
|
-
[
|
|
186
|
-
createLoggerExtension, // factory — config passed automatically
|
|
187
|
-
createMcpExtension, // factory — opts out if no mcpConfigPaths
|
|
188
|
-
'./extensions', // directory — discovers extensions by extension.json
|
|
189
|
-
],
|
|
190
|
-
{ mcpConfigPaths: './mcpServers.json' },
|
|
191
|
-
)
|
|
149
|
+
| Extension | Description |
|
|
150
|
+
|-----------|-------------|
|
|
151
|
+
| disk-session | Disk-backed session history per session ID |
|
|
152
|
+
| code-tools | Coding, filesystem, shell, and task tools |
|
|
153
|
+
| streaming | Text, reasoning, and tool-input delta callbacks |
|
|
154
|
+
| skills | Agent Skill discovery and loading |
|
|
192
155
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// ... run turns ...
|
|
196
|
-
|
|
197
|
-
await closeExtensions(loaded) // calls close() on extensions that have one (e.g. MCP)
|
|
198
|
-
```
|
|
156
|
+
**Priority** controls hook execution order within each lifecycle stage (higher = runs first, ties keep registration order).
|
|
199
157
|
|
|
200
158
|
## Model Providers
|
|
201
159
|
|
|
@@ -268,49 +226,6 @@ class MyModel implements Model {
|
|
|
268
226
|
}
|
|
269
227
|
```
|
|
270
228
|
|
|
271
|
-
## Virtual Models (OpenAI-compatible provider)
|
|
272
|
-
|
|
273
|
-
Expose Libra agents as OpenAI-compatible models. Any framework that supports a custom OpenAI base URL can use your agents as models — with their own context, tools, extensions, and policy controls.
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
import { Agent } from '@xandout/libra-harness'
|
|
277
|
-
import { resolveModel } from '@xandout/libra-harness/models'
|
|
278
|
-
import { createOpenAICompatibleServer } from '@xandout/libra-harness/openai-provider'
|
|
279
|
-
|
|
280
|
-
const model = await resolveModel('deepseek/deepseek-v4-flash')
|
|
281
|
-
|
|
282
|
-
const server = createOpenAICompatibleServer({
|
|
283
|
-
agents: {
|
|
284
|
-
'research-agent': new Agent({ model, systemPrompt: 'You are a research assistant.' }),
|
|
285
|
-
'coding-agent': new Agent({ model, systemPrompt: 'You are a coding assistant.' }),
|
|
286
|
-
},
|
|
287
|
-
apiKeys: ['your-provider-key'],
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
server.listen(8787, '127.0.0.1')
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
Now any OpenAI-compatible client can call these agents as models:
|
|
294
|
-
|
|
295
|
-
```python
|
|
296
|
-
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="your-provider-key")
|
|
297
|
-
response = client.chat.completions.create(
|
|
298
|
-
model="research-agent",
|
|
299
|
-
messages=[{"role": "user", "content": "Research quantum computing"}],
|
|
300
|
-
)
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Features:
|
|
304
|
-
- `GET /v1/models` and `POST /v1/chat/completions`
|
|
305
|
-
- Bearer and `x-api-key` authentication
|
|
306
|
-
- Text, image, system, developer, assistant, and tool messages
|
|
307
|
-
- JSON and SSE streaming responses
|
|
308
|
-
- Client-defined tools (external tool calling with round-trip)
|
|
309
|
-
- Agent's own tools run internally (invisible to the caller)
|
|
310
|
-
- Per-agent hooks for moderation, context injection, output filtering
|
|
311
|
-
|
|
312
|
-
See [`docs/virtual-models.md`](docs/virtual-models.md) and [`docs/virtual-models-pii-dlp.md`](docs/virtual-models-pii-dlp.md) for concepts and the PII/DLP pattern.
|
|
313
|
-
|
|
314
229
|
## Hook Lifecycle
|
|
315
230
|
|
|
316
231
|
```
|
|
@@ -445,21 +360,6 @@ agent.use(streamingExtension)
|
|
|
445
360
|
|
|
446
361
|
When `onDelta` is not set, `AISdkModel` uses `doGenerate` (no streaming overhead). The core never interprets deltas — it simply passes the callback through.
|
|
447
362
|
|
|
448
|
-
## Examples
|
|
449
|
-
|
|
450
|
-
The `examples/` directory includes reference implementations:
|
|
451
|
-
|
|
452
|
-
- **`full-agent/`** — all built-in extensions via the loader, plus a local search-replace extension. Multi-turn session memory, MCP tools, skill loader, weather tool, streaming.
|
|
453
|
-
- **`basic-agent-concurrent/`** — single agent handling many concurrent users with session isolation and per-turn halt.
|
|
454
|
-
- **`subagents/`** — orchestrator agent delegating to specialized subagents via `createAgentTool`, with signal chaining and halt propagation.
|
|
455
|
-
- **`subagents-concurrent/`** — orchestrator fanning out to multiple subagents in parallel via `Promise.all`.
|
|
456
|
-
- **`structured-output/`** — `beforeResponse` hook that validates LLM output against a JSON schema.
|
|
457
|
-
- **`streaming/`** — `beforeLLM` hook that streams text/reasoning/tool-input deltas.
|
|
458
|
-
- **`openai-compatible-provider/`** — exposes multiple independent Libra agents as authenticated OpenAI-compatible models. Supports text, images, SSE streaming, and client-defined external tools.
|
|
459
|
-
- **`pii-dlp-provider/`** — proves the virtual model PII/DLP pattern: the LLM never sees real PII, the consumer never sees placeholders. Uses a CSV datasource with tool calling and full lifecycle logging.
|
|
460
|
-
- **`slack-bot/`** — full Slack bot with Socket Mode, block kit rendering, session persistence, MCP, skills, and OpenTelemetry tracing.
|
|
461
|
-
- **`large-document-mapper/`** — processes large documents in chunks with mapping and reduction.
|
|
462
|
-
|
|
463
363
|
## Architecture
|
|
464
364
|
|
|
465
365
|
- **Library-first** — no server, daemon, database, or queue required
|
|
@@ -470,8 +370,7 @@ The `examples/` directory includes reference implementations:
|
|
|
470
370
|
- **Multimodal** — text, images, documents, audio, and video in message content
|
|
471
371
|
- **Steerable & haltable** — per-turn controls via `RunHandle` or `ctx.turn`
|
|
472
372
|
- **Streamable** — text, reasoning, and tool-input deltas via `onDelta` callback
|
|
473
|
-
- **
|
|
474
|
-
- **Testable** — 354 tests with mock model
|
|
373
|
+
- **Testable** — comprehensive tests with mock models
|
|
475
374
|
|
|
476
375
|
### What the core owns
|
|
477
376
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xandout/libra-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.130",
|
|
4
4
|
"description": "A small, composable, hookable agent harness. Library-first.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,30 +14,10 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"import": "./dist/index.js"
|
|
16
16
|
},
|
|
17
|
-
"./extensions": {
|
|
18
|
-
"types": "./dist/extensions/index.d.ts",
|
|
19
|
-
"import": "./dist/extensions/index.js"
|
|
20
|
-
},
|
|
21
|
-
"./extensions/loader": {
|
|
22
|
-
"types": "./dist/extensions/loader.d.ts",
|
|
23
|
-
"import": "./dist/extensions/loader.js"
|
|
24
|
-
},
|
|
25
17
|
"./models": {
|
|
26
18
|
"types": "./dist/models/index.d.ts",
|
|
27
19
|
"import": "./dist/models/index.js"
|
|
28
20
|
},
|
|
29
|
-
"./models/ai-sdk-resolver": {
|
|
30
|
-
"types": "./dist/models/ai-sdk-resolver.d.ts",
|
|
31
|
-
"import": "./dist/models/ai-sdk-resolver.js"
|
|
32
|
-
},
|
|
33
|
-
"./models/routing-model": {
|
|
34
|
-
"types": "./dist/models/routing-model.d.ts",
|
|
35
|
-
"import": "./dist/models/routing-model.js"
|
|
36
|
-
},
|
|
37
|
-
"./openai-provider": {
|
|
38
|
-
"types": "./dist/openai-provider/index.d.ts",
|
|
39
|
-
"import": "./dist/openai-provider/index.js"
|
|
40
|
-
},
|
|
41
21
|
"./extensions/disk-session": {
|
|
42
22
|
"types": "./dist/extensions/disk-session/index.d.ts",
|
|
43
23
|
"import": "./dist/extensions/disk-session/index.js"
|
|
@@ -53,26 +33,19 @@
|
|
|
53
33
|
"./extensions/skills": {
|
|
54
34
|
"types": "./dist/extensions/skills/index.d.ts",
|
|
55
35
|
"import": "./dist/extensions/skills/index.js"
|
|
56
|
-
},
|
|
57
|
-
"./skills": {
|
|
58
|
-
"types": "./dist/extensions/skills/index.d.ts",
|
|
59
|
-
"import": "./dist/extensions/skills/index.js"
|
|
60
|
-
},
|
|
61
|
-
"./code-tools": {
|
|
62
|
-
"types": "./dist/extensions/code-tools/index.d.ts",
|
|
63
|
-
"import": "./dist/extensions/code-tools/index.js"
|
|
64
|
-
},
|
|
65
|
-
"./disk-session": {
|
|
66
|
-
"types": "./dist/extensions/disk-session/index.d.ts",
|
|
67
|
-
"import": "./dist/extensions/disk-session/index.js"
|
|
68
|
-
},
|
|
69
|
-
"./streaming": {
|
|
70
|
-
"types": "./dist/extensions/streaming/index.d.ts",
|
|
71
|
-
"import": "./dist/extensions/streaming/index.js"
|
|
72
36
|
}
|
|
73
37
|
},
|
|
74
38
|
"files": [
|
|
75
|
-
"dist",
|
|
39
|
+
"dist/*.js",
|
|
40
|
+
"dist/*.js.map",
|
|
41
|
+
"dist/*.d.ts",
|
|
42
|
+
"dist/*.d.ts.map",
|
|
43
|
+
"dist/models",
|
|
44
|
+
"dist/extensions/code-tools",
|
|
45
|
+
"dist/extensions/disk-session",
|
|
46
|
+
"dist/extensions/skills",
|
|
47
|
+
"dist/extensions/streaming",
|
|
48
|
+
"!dist/**/*.test.*",
|
|
76
49
|
"README.md"
|
|
77
50
|
],
|
|
78
51
|
"publishConfig": {
|
|
@@ -90,50 +63,22 @@
|
|
|
90
63
|
"node": ">=20"
|
|
91
64
|
},
|
|
92
65
|
"devDependencies": {
|
|
93
|
-
"@opentelemetry/sdk-trace-base": "^2.10.0",
|
|
94
66
|
"@types/node": "^26.2.0",
|
|
95
67
|
"@vitest/coverage-v8": "^4.1.10",
|
|
96
|
-
"fallow": "^3.22.0",
|
|
97
|
-
"quickjs-emscripten": "^0.32.0",
|
|
98
68
|
"typescript": "^7.0.2",
|
|
99
69
|
"vitest": "^4.1.10"
|
|
100
70
|
},
|
|
101
|
-
"peerDependencies": {
|
|
102
|
-
"@modelcontextprotocol/client": "^2.0.0",
|
|
103
|
-
"@opentelemetry/api": "^1.9.0",
|
|
104
|
-
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
105
|
-
"ai": "^7.0.0",
|
|
106
|
-
"quickjs-emscripten": "^0.32.0"
|
|
107
|
-
},
|
|
108
|
-
"peerDependenciesMeta": {
|
|
109
|
-
"ai": {
|
|
110
|
-
"optional": true
|
|
111
|
-
},
|
|
112
|
-
"@modelcontextprotocol/client": {
|
|
113
|
-
"optional": true
|
|
114
|
-
},
|
|
115
|
-
"@opentelemetry/api": {
|
|
116
|
-
"optional": true
|
|
117
|
-
},
|
|
118
|
-
"@opentelemetry/sdk-trace-base": {
|
|
119
|
-
"optional": true
|
|
120
|
-
},
|
|
121
|
-
"quickjs-emscripten": {
|
|
122
|
-
"optional": true
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
71
|
"dependencies": {
|
|
126
72
|
"@ai-sdk/anthropic": "^4.0.36",
|
|
127
73
|
"@ai-sdk/deepseek": "^3.0.36",
|
|
128
74
|
"@ai-sdk/google": "^4.0.39",
|
|
129
75
|
"@ai-sdk/openai": "^4.0.36",
|
|
130
76
|
"@ai-sdk/provider": "^4.0.7",
|
|
131
|
-
"ai": "^7.0.84"
|
|
132
|
-
"wink-eng-lite-web-model": "^1.8.1",
|
|
133
|
-
"wink-nlp": "^2.4.0"
|
|
77
|
+
"ai": "^7.0.84"
|
|
134
78
|
},
|
|
135
79
|
"scripts": {
|
|
136
80
|
"build": "tsc && for d in src/extensions/*/; do name=$(basename \"$d\") && if [ -f \"$d\"extension.json ]; then cp \"$d\"extension.json dist/extensions/\"$name\"/; fi; done && cp src/extensions/code-tools/tools/task-wrapper.sh dist/extensions/code-tools/tools/task-wrapper.sh && chmod +x dist/extensions/code-tools/tools/task-wrapper.sh",
|
|
81
|
+
"install:lc": "pnpm build && pnpm --dir packages/libra-code build && ln -sf \"$PWD/packages/libra-code/dist/index.js\" \"$(dirname \"$(command -v node)\")/lc\"",
|
|
137
82
|
"typecheck": "tsc --noEmit",
|
|
138
83
|
"test": "vitest run",
|
|
139
84
|
"test:watch": "vitest",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extension.test.d.ts","sourceRoot":"","sources":["../../../src/extensions/code-tools/extension.test.ts"],"names":[],"mappings":""}
|