@johpaz/hive-sdk 0.4.4 → 0.4.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/CHANGELOG.md +20 -4
- package/README.md +48 -7
- package/SECURITY.md +17 -0
- package/docs/API-AGENTS.md +430 -0
- package/docs/API-ARTIFACTS.md +55 -0
- package/docs/API-CONTEXT-COMPILER.md +285 -0
- package/docs/API-CRON.md +188 -0
- package/docs/API-DAG-SCHEDULER.md +291 -0
- package/docs/API-HOOKS.md +147 -0
- package/docs/API-RESILIENCE.md +45 -0
- package/docs/API-SERVICES.md +458 -0
- package/docs/API-SESSIONS.md +146 -0
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +499 -0
- package/docs/API-WORKERS-EVENTS.md +311 -0
- package/docs/HIVE-HARNESS.md +232 -0
- package/docs/INDEX.md +198 -0
- package/docs/SECURITY-GUARDRAILS.md +87 -0
- package/docs/TEMPLATE-HIVE-APP.md +360 -0
- package/docs/UPGRADING.md +65 -0
- package/docs/assets/logoblack.png +0 -0
- package/docs/assets/logocolor-dark.png +0 -0
- package/docs/assets/logocolorbg.png +0 -0
- package/docs/plans/2026-09-05-office-dependency-hardening-design.md +28 -0
- package/docs/plans/2026-09-06-dependency-audit-remediation-design.md +25 -0
- package/docs/plans/2026-09-06-pptx-image-size-remediation-design.md +54 -0
- package/docs/plans/2026-09-06-typescript7-bun142-documentation-design.md +48 -0
- package/package.json +5 -4
- package/packages/core/src/agent/llm-providers/hiveagents.ts +2 -2
- package/packages/core/src/agent/providers/index.ts +17 -1
- package/packages/core/src/api/createAgent.ts +4 -2
- package/packages/core/src/gateway/server.ts +1 -1
- package/packages/core/src/mcp/transports/sse.ts +11 -3
- package/packages/core/src/mcp/transports/websocket.ts +11 -9
- package/packages/core/src/tool-runtime/tool-worker.ts +3 -1
- package/packages/core/src/tools/office/office-escribir-pptx.ts +3 -1
- package/packages/core/src/vendor/pptxgenjs/LICENSE +21 -0
- package/packages/core/src/vendor/pptxgenjs/README.md +17 -0
- package/packages/core/src/vendor/pptxgenjs/pptxgen.es.d.ts +17 -0
- package/packages/core/src/vendor/pptxgenjs/pptxgen.es.js +7368 -0
- package/packages/core/src/voice/index.ts +4 -4
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# API Reference — Workers y Eventos
|
|
2
|
+
|
|
3
|
+
## Índice
|
|
4
|
+
|
|
5
|
+
1. [Bun Workers](#bun-workers)
|
|
6
|
+
2. [createWorker](#createworker)
|
|
7
|
+
3. [WorkerPool](#workerpool)
|
|
8
|
+
4. [AgentBus](#agentbus)
|
|
9
|
+
5. [EventBus](#eventbus)
|
|
10
|
+
6. [Canvas Events](#canvas-events)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Bun Workers
|
|
15
|
+
|
|
16
|
+
Hive SDK soporta **workers individuales con Bun Workers**. Cada worker corre en un thread aislado (`Bun.Worker` con `{ smol: true }`), con su propio system prompt y configuración.
|
|
17
|
+
|
|
18
|
+
### Casos de uso
|
|
19
|
+
|
|
20
|
+
- **Workers especializados**: Un worker para research, otro para coding, otro para review
|
|
21
|
+
- **Paralelismo**: Ejecutar múltiples tareas simultáneamente sin bloquear el hilo principal
|
|
22
|
+
- **Aislamiento**: Cada worker tiene su propio contexto y no interfere con otros
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## createWorker
|
|
27
|
+
|
|
28
|
+
Crea un worker individual con Bun Workers.
|
|
29
|
+
|
|
30
|
+
### Firma
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { createWorker } from "@johpaz/hive-sdk";
|
|
34
|
+
|
|
35
|
+
const worker = createWorker(config: WorkerConfig): WorkerInstance
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### WorkerConfig
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface WorkerConfig {
|
|
42
|
+
name: string; // Identificador del worker
|
|
43
|
+
agentId?: string; // ID del agente en DB (default: name)
|
|
44
|
+
systemPrompt?: string; // System prompt personalizado
|
|
45
|
+
model?: string; // Modelo LLM
|
|
46
|
+
provider?: string; // Provider LLM
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### WorkerInstance
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface WorkerInstance {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly id: string;
|
|
56
|
+
|
|
57
|
+
// Ejecutar y esperar resultado
|
|
58
|
+
run(message: string, opts?: {
|
|
59
|
+
threadId?: string;
|
|
60
|
+
channel?: string;
|
|
61
|
+
}): Promise<string>;
|
|
62
|
+
|
|
63
|
+
// Streaming de resultados
|
|
64
|
+
runStream(message: string, opts?: {
|
|
65
|
+
threadId?: string;
|
|
66
|
+
channel?: string;
|
|
67
|
+
}): AsyncGenerator<WorkerChunk>;
|
|
68
|
+
|
|
69
|
+
// Terminar el worker
|
|
70
|
+
terminate(): void;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Ejemplo básico
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { createWorker } from "@johpaz/hive-sdk";
|
|
78
|
+
|
|
79
|
+
const researcher = createWorker({
|
|
80
|
+
name: "researcher",
|
|
81
|
+
systemPrompt: `
|
|
82
|
+
You are a research specialist.
|
|
83
|
+
Provide concise, factual summaries with citations.
|
|
84
|
+
Always verify facts before presenting them.
|
|
85
|
+
`,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const result = await researcher.run("Latest advances in quantum computing 2025");
|
|
89
|
+
console.log(result);
|
|
90
|
+
|
|
91
|
+
researcher.terminate();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Streaming
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const stream = researcher.runStream("Explain quantum entanglement");
|
|
98
|
+
|
|
99
|
+
for await (const chunk of stream) {
|
|
100
|
+
if (chunk.type === "chunk") {
|
|
101
|
+
console.log("Chunk:", chunk.chunk);
|
|
102
|
+
} else if (chunk.type === "result") {
|
|
103
|
+
console.log("Final:", chunk.content);
|
|
104
|
+
} else if (chunk.type === "error") {
|
|
105
|
+
console.error("Error:", chunk.error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## WorkerPool
|
|
113
|
+
|
|
114
|
+
Gestiona un pool de Bun Workers para ejecución paralela.
|
|
115
|
+
|
|
116
|
+
### Firma
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { WorkerPool } from "@johpaz/hive-sdk";
|
|
120
|
+
|
|
121
|
+
const pool = new WorkerPool(config?: WorkerPoolConfig);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### WorkerPoolConfig
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
interface WorkerPoolConfig {
|
|
128
|
+
maxWorkers?: number; // Default: 4
|
|
129
|
+
taskTimeoutMs?: number; // Default: 120000
|
|
130
|
+
workerConfig?: WorkerConfig;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Métodos
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Ejecutar una tarea
|
|
138
|
+
pool.execute(task: PoolTask): Promise<PoolTaskResult>
|
|
139
|
+
|
|
140
|
+
// Ejecutar múltiples tareas en paralelo
|
|
141
|
+
pool.executeBatch(tasks: PoolTask[]): Promise<PoolTaskResult[]>
|
|
142
|
+
|
|
143
|
+
// Ejecutar con límite de concurrencia
|
|
144
|
+
pool.executeWithConcurrency(tasks: PoolTask[], concurrency: number): Promise<PoolTaskResult[]>
|
|
145
|
+
|
|
146
|
+
// Estadísticas
|
|
147
|
+
pool.stats // { total, busy, idle }
|
|
148
|
+
|
|
149
|
+
// Cerrar todos los workers
|
|
150
|
+
pool.shutdown(): void
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Ejemplo: Batch processing
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { WorkerPool } from "@johpaz/hive-sdk";
|
|
157
|
+
|
|
158
|
+
const pool = new WorkerPool({
|
|
159
|
+
maxWorkers: 4,
|
|
160
|
+
workerConfig: {
|
|
161
|
+
name: "analyzer",
|
|
162
|
+
systemPrompt: "You analyze text and extract key insights.",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const articles = [
|
|
167
|
+
{ id: "a1", message: "Summarize article about AI..." },
|
|
168
|
+
{ id: "a2", message: "Summarize article about climate..." },
|
|
169
|
+
{ id: "a3", message: "Summarize article about space..." },
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
const results = await pool.executeBatch(articles);
|
|
173
|
+
|
|
174
|
+
for (const result of results) {
|
|
175
|
+
console.log(`${result.taskId}: ${result.result} (${result.durationMs}ms)`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pool.shutdown();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Ejemplo: Concurrency limit
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Procesar 100 tareas pero solo 5 a la vez
|
|
185
|
+
const tasks = Array.from({ length: 100 }, (_, i) => ({
|
|
186
|
+
id: `task-${i}`,
|
|
187
|
+
message: `Process item ${i}`,
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
const results = await pool.executeWithConcurrency(tasks, 5);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## CLI: hives add-worker
|
|
196
|
+
|
|
197
|
+
Genera un Bun Worker en tu proyecto:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
cd my-project
|
|
201
|
+
hives add-worker researcher
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Crea `src/workers/researcher.worker.ts`:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { createWorker } from "@johpaz/hive-sdk";
|
|
208
|
+
|
|
209
|
+
export const researcherWorker = createWorker({
|
|
210
|
+
name: "researcher",
|
|
211
|
+
systemPrompt: `You are the ResearcherWorker specialist...`,
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## AgentBus
|
|
218
|
+
|
|
219
|
+
Sistema de eventos singleton para comunicación entre agentes.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { agentBus, getUnreadMessagesForWorker } from "@johpaz/hive-sdk";
|
|
223
|
+
|
|
224
|
+
// Publicar evento
|
|
225
|
+
agentBus.publish("worker:task_started", { taskId: "task-1" }, "worker-1");
|
|
226
|
+
|
|
227
|
+
// Suscribirse
|
|
228
|
+
const unsub = agentBus.subscribe("worker:task_completed", (data) => {
|
|
229
|
+
console.log("Task completed:", data);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Unsubscribe
|
|
233
|
+
unsub();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Métodos Helper
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import {
|
|
240
|
+
getUnreadMessagesForWorker,
|
|
241
|
+
getProjectMessageHistory,
|
|
242
|
+
} from "@johpaz/hive-sdk";
|
|
243
|
+
|
|
244
|
+
const messages = getUnreadMessagesForWorker("worker-1");
|
|
245
|
+
const history = getProjectMessageHistory("project-1");
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## EventBus
|
|
251
|
+
|
|
252
|
+
EventBus global singleton (eventos del sistema).
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { eventBus } from "@johpaz/hive-sdk";
|
|
256
|
+
|
|
257
|
+
// Escuchar eventos
|
|
258
|
+
eventBus.on("agent:start", (data) => {
|
|
259
|
+
console.log("Agent started:", data);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Emitir eventos
|
|
263
|
+
eventBus.emit("agent:complete", { agentId: "a1", result: "ok" });
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Canvas Events
|
|
269
|
+
|
|
270
|
+
Eventos de actualización visual del canvas.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { emitCanvas, subscribeCanvas, unsubscribeCanvas } from "@johpaz/hive-sdk";
|
|
274
|
+
|
|
275
|
+
// Suscribirse a eventos canvas
|
|
276
|
+
const handler = (data: any) => console.log("Canvas:", data);
|
|
277
|
+
subscribeCanvas(handler);
|
|
278
|
+
|
|
279
|
+
// Emitir evento
|
|
280
|
+
emitCanvas("canvas:node_update", {
|
|
281
|
+
nodeId: "agent-1",
|
|
282
|
+
changes: { status: "thinking" },
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Desuscribirse
|
|
286
|
+
unsubscribeCanvas(handler);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### CanvasManager
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { CanvasManager } from "@johpaz/hive-sdk";
|
|
293
|
+
|
|
294
|
+
const canvas = new CanvasManager();
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Canvas
|
|
300
|
+
|
|
301
|
+
El estado visual de un enjambre corriendo, para quien quiera pintarlo.
|
|
302
|
+
|
|
303
|
+
| | |
|
|
304
|
+
|---|---|
|
|
305
|
+
| `getCanvasSnapshot()` | El estado completo, para arrancar una vista. |
|
|
306
|
+
| `subscribeCanvas(fn)` / `unsubscribeCanvas(fn)` | Cambios en vivo. |
|
|
307
|
+
| `emitCanvas(evento, datos)` | Publicar un cambio. |
|
|
308
|
+
| `emitDelegationStarted` / `emitDelegationFinished` | Una delegación empieza y termina — es lo que dibuja las aristas entre agentes. |
|
|
309
|
+
| `emitWorkEvent` | Un hito de trabajo dentro de un nodo. |
|
|
310
|
+
|
|
311
|
+
*Documentación Hive SDK — ver `version` en package.json*
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Hive Harness — durable task execution
|
|
2
|
+
|
|
3
|
+
The `harness` module (`@johpaz/hive-sdk/harness`) is the SDK's durable-execution
|
|
4
|
+
layer: a HiveDB-backed job queue with crash recovery, checkpointable runs,
|
|
5
|
+
retry with backoff, idempotent submission, goal verification, and proof
|
|
6
|
+
packets. It's what lets a host app (a `hive-app`, or a production service like
|
|
7
|
+
Hive Cloud) survive a process restart mid-task without losing work or
|
|
8
|
+
double-executing a tool call.
|
|
9
|
+
|
|
10
|
+
It is deliberately **not** wired into the agent loop automatically, and it has
|
|
11
|
+
no built-in notion of "chat" vs "project task" vs any other app-specific job
|
|
12
|
+
type — job `type` and run `kind` are plain strings. The host app defines its
|
|
13
|
+
own vocabulary and registers executors for it. This is the same infrastructure
|
|
14
|
+
that powers `hive`'s durable-queue harness, generalized so any SDK consumer
|
|
15
|
+
can reuse it instead of re-implementing crash-safe job execution from scratch.
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
`@johpaz/hive-sdk/harness` is a **barrel, not a directory**: until 0.1.5 it
|
|
20
|
+
carried its own copies of the job store, run store and reconcile helpers, in
|
|
21
|
+
parallel with the ones in `storage/` and `agent/`. Two job stores over the same
|
|
22
|
+
HiveDB collections is one thing with two possible states, so the duplicates were
|
|
23
|
+
removed and the subpath now re-exports the single implementation. The paths below
|
|
24
|
+
are where each piece actually lives, relative to `packages/core/src/`.
|
|
25
|
+
|
|
26
|
+
| Piece | File | Responsibility |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `JobDoc` / `AgentRunDoc` / `ProofPacketDoc` | `storage/collections.ts` | HiveDB document shapes |
|
|
29
|
+
| collection helpers | `storage/hive.ts` | `nextId`, `updateDoc`, `findByAny`, `col` — primitives HiveDB's `Collection` doesn't provide directly |
|
|
30
|
+
| `job-store` | `gateway/job-store.ts` | Durable job persistence: claim/lease/complete/fail/retry, all via OCC |
|
|
31
|
+
| `run-store` | `agent/run-store.ts` | Checkpoint + lease for a single durable run (messages, iteration/token counters, pending tool calls) |
|
|
32
|
+
| `durable-queue` | `gateway/durable-queue.ts` | `DurableLaneQueue` — FIFO+priority per lane, global concurrency cap, executor registry |
|
|
33
|
+
| `goal-runner` | `agent/goal-runner.ts` | `runGoal()` / `verifyGoal()` — deterministic check tool or LLM verifier, single goal or a list of acceptance criteria |
|
|
34
|
+
| `run-epoch` | `agent/run-epoch.ts` | Fixed-worker epoch fingerprint (provider/model/app-version/tool-catalog) |
|
|
35
|
+
| `proof-packet` | `agent/proof-packet.ts` | Compressed evidence artifact for a completed run |
|
|
36
|
+
| `boot-id` | `storage/boot-id.ts` | `getBootId()` — identifies this process run, so a crash is distinguishable from a restart |
|
|
37
|
+
| `reconcile` | `storage/reconcile.ts` | `reconcileOnBoot()` — crash repair + retention cap, call once at startup |
|
|
38
|
+
|
|
39
|
+
`test/harness-barrel.test.ts` pins this contract: the subpath must keep exporting
|
|
40
|
+
the same names, pointing at the single implementation rather than at copies.
|
|
41
|
+
|
|
42
|
+
## Ready-made executors
|
|
43
|
+
|
|
44
|
+
The queue knows how to enqueue, retry and recover after a crash — but not how to
|
|
45
|
+
*do* anything. Registering executors used to be entirely on whoever built on the
|
|
46
|
+
SDK, and that is ~420 lines of wiring (epoch, proof packets, acceptance checks,
|
|
47
|
+
delegation fan-in) before running a single durable swarm.
|
|
48
|
+
|
|
49
|
+
Two now ship with the SDK:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { initHarnessExecutors, registerExecutor } from "@johpaz/hive-sdk/harness";
|
|
53
|
+
|
|
54
|
+
initHarnessExecutors(); // worker_task + goal_run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **`worker_task`** — runs a delegated worker in an isolated context, verifies
|
|
58
|
+
its acceptance criteria, builds the proof packet and notifies the agent bus.
|
|
59
|
+
- **`goal_run`** — orchestrates multiple turns against a goal until it verifies
|
|
60
|
+
or the budget runs out.
|
|
61
|
+
|
|
62
|
+
**`chat_turn` is deliberately absent.** What a "channel" is and how a token is
|
|
63
|
+
streamed is the application's decision — in hive it depends on its HTTP server.
|
|
64
|
+
Register your own:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
registerExecutor("chat_turn", async (job, signal, callbacks) => { /* ... */ });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Registering stays opt-in: `initHarnessExecutors()` is never called for you,
|
|
71
|
+
because which job types this process executes is the app's call. Use
|
|
72
|
+
`getRegisteredExecutorTypes()` to check what got wired — a job enqueued without
|
|
73
|
+
an executor fails when it is claimed, not when it is enqueued, which is far from
|
|
74
|
+
where the mistake is.
|
|
75
|
+
|
|
76
|
+
## Durable queue semantics
|
|
77
|
+
|
|
78
|
+
- **Lanes**: a lane (e.g. a session id, or `task:<id>`) runs at most one job
|
|
79
|
+
at a time, FIFO within the lane, ordered by `priority` then creation order.
|
|
80
|
+
- **Global concurrency**: `maxGlobalConcurrency` caps how many jobs run at
|
|
81
|
+
once across all lanes (default 4). Types listed in `nonRetryableTypes`
|
|
82
|
+
(default `["chat_turn"]`) bypass this cap — a busy batch of background jobs
|
|
83
|
+
must not make an interactive/user-facing job type stop responding.
|
|
84
|
+
- **Leases**: a claimed job gets a lease (default 30 min); the queue renews
|
|
85
|
+
it every 30s while executing. A lease that expires (crashed process) is
|
|
86
|
+
reclaimed to `pending` or marked `interrupted` once `attempts >=
|
|
87
|
+
max_attempts` — checked by `reconcileOnBoot()` at startup and by the
|
|
88
|
+
queue's periodic maintenance tick thereafter.
|
|
89
|
+
- **Executors**: register one per job type with `registerExecutor(type, fn)`.
|
|
90
|
+
An executor receives the `JobDoc`, an `AbortSignal` (fired on cancel or
|
|
91
|
+
`taskTimeoutMs`), and any live callbacks passed to `enqueue()`.
|
|
92
|
+
|
|
93
|
+
## Retry & backoff
|
|
94
|
+
|
|
95
|
+
Two independent retry mechanisms:
|
|
96
|
+
|
|
97
|
+
1. **Crash retries** (`attempts` / `max_attempts`) — bumped on every claim,
|
|
98
|
+
checked by `reclaimOrInterrupt` after a lease expires. This is about
|
|
99
|
+
*the process dying*, not the job failing logically.
|
|
100
|
+
2. **Logical-failure retries** (`retry_count` / `JobRetryPolicy`) — when an
|
|
101
|
+
executor returns `{ok: false, retryable: true}` (the default unless set
|
|
102
|
+
`false`), `failJobOrRetry` reschedules the job with exponential backoff +
|
|
103
|
+
jitter instead of failing it immediately:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
delay = min(maxDelayMs, initialDelayMs * backoffMultiplier ** retryCount)
|
|
107
|
+
* (1 + jitter * random())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Once `retryCount >= policy.maxRetries`, the job fails terminally. Types in
|
|
111
|
+
`nonRetryableTypes` never take this path — a failed interactive turn
|
|
112
|
+
should surface to the user immediately, not silently retry later.
|
|
113
|
+
|
|
114
|
+
## Idempotency
|
|
115
|
+
|
|
116
|
+
`createJob`/`enqueue` accept an optional `idempotency_key`. A repeated key
|
|
117
|
+
returns the existing job (whatever its status — pending, running, completed,
|
|
118
|
+
or terminally failed) instead of creating a duplicate, so a retried HTTP
|
|
119
|
+
request from a caller doesn't double-enqueue work.
|
|
120
|
+
|
|
121
|
+
## Goal verification & acceptance criteria
|
|
122
|
+
|
|
123
|
+
`verifyGoal()` answers "was this met" for a single goal or — when
|
|
124
|
+
`acceptance` criteria are supplied — for each criterion independently (its
|
|
125
|
+
own optional `checkTool`, or an LLM judgment against its own description).
|
|
126
|
+
The overall verdict is the conjunction of all criteria. The harness has no
|
|
127
|
+
built-in tool registry: pass a `runCheckTool` callback that resolves a
|
|
128
|
+
`checkTool` name to something the host app can actually execute.
|
|
129
|
+
|
|
130
|
+
## Proof packets
|
|
131
|
+
|
|
132
|
+
`buildProofPacket()` persists a compressed evidence artifact once a run
|
|
133
|
+
finishes: intended outcome, per-criterion results, checks run, evidence
|
|
134
|
+
snippets, known limits, and the run's fixed-worker epoch. Useful as an
|
|
135
|
+
audit trail without having to replay the full run transcript.
|
|
136
|
+
|
|
137
|
+
## Setup
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import {
|
|
141
|
+
ensureHarnessIndexes,
|
|
142
|
+
reconcileOnBoot,
|
|
143
|
+
initDurableQueue,
|
|
144
|
+
registerExecutor,
|
|
145
|
+
getBootId,
|
|
146
|
+
} from "@johpaz/hive-sdk/harness";
|
|
147
|
+
|
|
148
|
+
await ensureHarnessIndexes(); // idempotent — safe every boot
|
|
149
|
+
await reconcileOnBoot(getBootId());
|
|
150
|
+
|
|
151
|
+
registerExecutor("my_job_type", async (job, signal) => {
|
|
152
|
+
// ... do the work, honoring `signal` for cancellation/timeout
|
|
153
|
+
return { ok: true, result: "done" };
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const queue = initDurableQueue({ maxGlobalConcurrency: 4 });
|
|
157
|
+
await queue.enqueue({ lane: "session-1", type: "my_job_type", run_id: "r1", payload: {} });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## API reference
|
|
161
|
+
|
|
162
|
+
Everything below is exported from `@johpaz/hive-sdk/harness`. Grouped by what
|
|
163
|
+
you reach for, not alphabetically.
|
|
164
|
+
|
|
165
|
+
### Wiring it up
|
|
166
|
+
|
|
167
|
+
| | |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `ensureHarnessIndexes()` | Creates the indexes the collections need. Idempotent, safe on every boot. |
|
|
170
|
+
| `reconcileOnBoot(bootId)` | Crash repair: reclaims leases from a previous process and interrupts what cannot resume. Call once at startup, before the queue. |
|
|
171
|
+
| `getBootId()` / `resetBootId()` | Identifies this process run, which is what distinguishes a crash from a restart. |
|
|
172
|
+
| `initDurableQueue(opts)` / `getDurableQueue()` | The queue itself. |
|
|
173
|
+
| `initHarnessExecutors()` | Registers the bundled `worker_task` and `goal_run`. |
|
|
174
|
+
| `registerExecutor(type, fn)` | Your own job types. |
|
|
175
|
+
| `getRegisteredExecutorTypes()` | What this process can actually execute — a job enqueued without an executor fails when claimed, not when enqueued. |
|
|
176
|
+
| `setHarnessExecutorMCPManager(m)` | MCP tools for delegated workers. Optional: a swarm without MCP is valid. |
|
|
177
|
+
| `registerTerminalHook(fn)` / `runTerminalHook(job, outcome)` | Runs when a job reaches a terminal state, however it got there. |
|
|
178
|
+
|
|
179
|
+
### Jobs
|
|
180
|
+
|
|
181
|
+
`createJob` · `getJob` · `claimJob` · `renewLease` · `completeJob` · `failJob` ·
|
|
182
|
+
`failJobOrRetry` · `cancelJob` · `reclaimOrInterrupt`
|
|
183
|
+
|
|
184
|
+
Queries: `findPendingJobsByLane` · `findAllPendingJobs` · `findExpiredLeases` ·
|
|
185
|
+
`findByIdempotencyKey`
|
|
186
|
+
|
|
187
|
+
Retry policy: `loadJobRetryPolicy` · `computeBackoffDelay` — see
|
|
188
|
+
[Retry & backoff](#retry--backoff) for why crash-retries and logical-failure
|
|
189
|
+
retries are counted separately.
|
|
190
|
+
|
|
191
|
+
### Runs
|
|
192
|
+
|
|
193
|
+
A *run* is one durable execution of an agent: its messages, its counters and its
|
|
194
|
+
lease.
|
|
195
|
+
|
|
196
|
+
`createRun` · `getRun` · `checkpoint` · `bumpTurn` · `completeRun` · `failRun` ·
|
|
197
|
+
`interruptRun` · `reclaimRun`
|
|
198
|
+
|
|
199
|
+
Queries: `findRunsByThread` · `findRunsByStatus` · `findExpiredRuns`
|
|
200
|
+
|
|
201
|
+
Leases: `startLeaseRenewal` · `stopLeaseRenewal` · `stopAllLeaseRenewals` — a run
|
|
202
|
+
that stops renewing is treated as dead and reclaimed, which is what makes crash
|
|
203
|
+
recovery work.
|
|
204
|
+
|
|
205
|
+
Reading back a checkpoint: `deserializeCheckpoint` · `deserializeAcceptance` ·
|
|
206
|
+
`deserializeEpoch`
|
|
207
|
+
|
|
208
|
+
### Epoch
|
|
209
|
+
|
|
210
|
+
`buildRunEpoch` fingerprints provider, model, app version and tool catalog. A run
|
|
211
|
+
resumed under a different epoch is not the same run — the model or the tools
|
|
212
|
+
changed underneath it — and the harness will not silently continue it.
|
|
213
|
+
|
|
214
|
+
### Goals and proof
|
|
215
|
+
|
|
216
|
+
`runGoal` · `verifyGoal` · `interpretCheckResult` · `buildProofPacket` ·
|
|
217
|
+
`findProofPacketsByRun`
|
|
218
|
+
|
|
219
|
+
### Collection primitives
|
|
220
|
+
|
|
221
|
+
`col` · `nextId` · `updateDoc` · `findByAny` · `toIndexable` · `fromIndexable` ·
|
|
222
|
+
`NO_PARENT`
|
|
223
|
+
|
|
224
|
+
Re-exported for convenience: the harness stores everything in HiveDB, and a
|
|
225
|
+
consumer that needs a query the helpers above do not cover can write it without
|
|
226
|
+
importing from a second subpath.
|
|
227
|
+
|
|
228
|
+
### Document shapes
|
|
229
|
+
|
|
230
|
+
`JobDoc` · `AgentRunDoc` · `ProofPacketDoc`
|
|
231
|
+
|
|
232
|
+
*Documentación Hive SDK — ver `version` en package.json*
|