@xandout/libra-harness 0.1.128 → 0.1.129

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 (30) hide show
  1. package/README.md +13 -114
  2. package/package.json +12 -68
  3. package/dist/extensions/code-tools/extension.test.d.ts +0 -2
  4. package/dist/extensions/code-tools/extension.test.d.ts.map +0 -1
  5. package/dist/extensions/code-tools/extension.test.js +0 -544
  6. package/dist/extensions/code-tools/extension.test.js.map +0 -1
  7. package/dist/extensions/disk-session/extension.test.d.ts +0 -2
  8. package/dist/extensions/disk-session/extension.test.d.ts.map +0 -1
  9. package/dist/extensions/disk-session/extension.test.js +0 -1101
  10. package/dist/extensions/disk-session/extension.test.js.map +0 -1
  11. package/dist/extensions/index.d.ts +0 -3
  12. package/dist/extensions/index.d.ts.map +0 -1
  13. package/dist/extensions/index.js +0 -2
  14. package/dist/extensions/index.js.map +0 -1
  15. package/dist/extensions/loader.d.ts +0 -205
  16. package/dist/extensions/loader.d.ts.map +0 -1
  17. package/dist/extensions/loader.js +0 -315
  18. package/dist/extensions/loader.js.map +0 -1
  19. package/dist/extensions/skills/extension.test.d.ts +0 -2
  20. package/dist/extensions/skills/extension.test.d.ts.map +0 -1
  21. package/dist/extensions/skills/extension.test.js +0 -184
  22. package/dist/extensions/skills/extension.test.js.map +0 -1
  23. package/dist/openai-provider/index.d.ts +0 -3
  24. package/dist/openai-provider/index.d.ts.map +0 -1
  25. package/dist/openai-provider/index.js +0 -2
  26. package/dist/openai-provider/index.js.map +0 -1
  27. package/dist/openai-provider/server.d.ts +0 -34
  28. package/dist/openai-provider/server.d.ts.map +0 -1
  29. package/dist/openai-provider/server.js +0 -335
  30. 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 (`@xandout/libra-harness/extras`)
138
+ ### Built-in extensions
139
139
 
140
- Libra ships with a set of optional extensions under `@xandout/libra-harness/extras`. Each is importable via its own subpath — import only what you need:
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
- agent.use(createLoggerExtension())
147
- agent.use(createDiskSessionExtension({ dir: './sessions' }))
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 | Import | Description |
151
- |-----------|--------|-------------|
152
- | logger | `@xandout/libra-harness/extensions/logger` | Logs each lifecycle stage |
153
- | streaming | `@xandout/libra-harness/extensions/streaming` | Streams text/reasoning/tool-input deltas |
154
- | otel | `@xandout/libra-harness/extensions/otel` | OpenTelemetry tracing (JSONL or OTLP export) |
155
- | weather-tool | `@xandout/libra-harness/extensions/weather-tool` | Registers a `get_weather` tool |
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
- installExtensions(loaded, agent)
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
- - **Virtual models** — expose agents as OpenAI-compatible models with full provider-side control
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.128",
3
+ "version": "0.1.129",
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,47 +63,18 @@
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",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=extension.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.test.d.ts","sourceRoot":"","sources":["../../../src/extensions/code-tools/extension.test.ts"],"names":[],"mappings":""}