@dogpile/sdk 0.2.1 → 0.3.0

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 (49) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +86 -655
  3. package/dist/browser/index.js +337 -22
  4. package/dist/browser/index.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/runtime/broadcast.d.ts +1 -0
  8. package/dist/runtime/broadcast.d.ts.map +1 -1
  9. package/dist/runtime/broadcast.js +27 -6
  10. package/dist/runtime/broadcast.js.map +1 -1
  11. package/dist/runtime/coordinator.d.ts +1 -0
  12. package/dist/runtime/coordinator.d.ts.map +1 -1
  13. package/dist/runtime/coordinator.js +45 -8
  14. package/dist/runtime/coordinator.js.map +1 -1
  15. package/dist/runtime/engine.d.ts.map +1 -1
  16. package/dist/runtime/engine.js +5 -0
  17. package/dist/runtime/engine.js.map +1 -1
  18. package/dist/runtime/sequential.d.ts +1 -0
  19. package/dist/runtime/sequential.d.ts.map +1 -1
  20. package/dist/runtime/sequential.js +24 -6
  21. package/dist/runtime/sequential.js.map +1 -1
  22. package/dist/runtime/shared.d.ts +1 -0
  23. package/dist/runtime/shared.d.ts.map +1 -1
  24. package/dist/runtime/shared.js +24 -6
  25. package/dist/runtime/shared.js.map +1 -1
  26. package/dist/runtime/termination.d.ts +6 -1
  27. package/dist/runtime/termination.d.ts.map +1 -1
  28. package/dist/runtime/termination.js +75 -0
  29. package/dist/runtime/termination.js.map +1 -1
  30. package/dist/runtime/validation.d.ts.map +1 -1
  31. package/dist/runtime/validation.js +22 -0
  32. package/dist/runtime/validation.js.map +1 -1
  33. package/dist/runtime/wrap-up.d.ts +26 -0
  34. package/dist/runtime/wrap-up.d.ts.map +1 -0
  35. package/dist/runtime/wrap-up.js +178 -0
  36. package/dist/runtime/wrap-up.js.map +1 -0
  37. package/dist/types.d.ts +68 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +3 -2
  40. package/src/index.ts +3 -1
  41. package/src/runtime/broadcast.ts +49 -19
  42. package/src/runtime/coordinator.ts +83 -27
  43. package/src/runtime/engine.ts +6 -0
  44. package/src/runtime/sequential.ts +45 -19
  45. package/src/runtime/shared.ts +45 -19
  46. package/src/runtime/termination.ts +100 -0
  47. package/src/runtime/validation.ts +25 -0
  48. package/src/runtime/wrap-up.ts +257 -0
  49. package/src/types.ts +70 -0
package/README.md CHANGED
@@ -1,8 +1,19 @@
1
1
  # Dogpile
2
2
 
3
- Dogpile is a strict TypeScript SDK for turning one mission into a replayable multi-agent coordination run. It is inspired by arXiv 2603.28990v1, "Drop the Hierarchy and Roles", but it is packaged like an application SDK: bring your own model provider, pick a protocol, stream events if you need them, and persist the trace wherever your product already stores work.
3
+ [![npm version](https://img.shields.io/npm/v/@dogpile/sdk?color=0f766e&label=npm)](https://www.npmjs.com/package/@dogpile/sdk)
4
+ [![Release Validation](https://github.com/bubstack/dogpile/actions/workflows/release-validation.yml/badge.svg)](https://github.com/bubstack/dogpile/actions/workflows/release-validation.yml)
5
+ [![Node.js >=22](https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=white)](package.json)
6
+ [![TypeScript](https://img.shields.io/badge/types-TypeScript-3178c6?logo=typescript&logoColor=white)](src/index.ts)
7
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
8
+ [![pnpm 10.33.0](https://img.shields.io/badge/pnpm-10.33.0-f69220?logo=pnpm&logoColor=white)](package.json)
4
9
 
5
- The useful bit is the contract. Dogpile does not own your credentials, pricing table, storage, queue, or UI. It owns the coordination loop: agent turns, protocol events, transcripts, cost aggregation, cancellation, termination policy, typed errors, and replayable result shapes.
10
+ Dogpile is the TypeScript coordination layer for product teams that want
11
+ multi-agent work without handing their application to an agent framework.
12
+
13
+ Give Dogpile one mission and one model boundary. It runs a coordination
14
+ protocol, records every turn, streams the run if you need a live UI, totals
15
+ usage and cost, and hands back a replayable artifact your app can store
16
+ wherever work already lives.
6
17
 
7
18
  ```ts
8
19
  import { Dogpile } from "@dogpile/sdk";
@@ -18,54 +29,57 @@ console.log(result.output);
18
29
  console.log(result.trace.events);
19
30
  ```
20
31
 
21
- ## Research Basis
32
+ Dogpile is useful when a single model answer is too brittle, but a full agent
33
+ platform would own too much:
22
34
 
23
- Dogpile's protocol vocabulary and paper-faithfulness examples are based on:
35
+ - release planning that needs a planner, critic, and synthesizer
36
+ - policy or support workflows that need independent review before final text
37
+ - product research where protocol choice should be visible and repeatable
38
+ - eval harnesses that compare `sequential`, `broadcast`, `shared`, and
39
+ `coordinator` runs against the same mission
40
+ - application features that need agent progress events, cancellation, budgets,
41
+ typed failures, and saved traces instead of a black-box response string
24
42
 
25
- Dochkina, V. (2026). *Drop the Hierarchy and Roles: How Self-Organizing LLM Agents Outperform Designed Structures*. arXiv:2603.28990 [cs.AI]. https://doi.org/10.48550/arXiv.2603.28990
43
+ ## The Contract
26
44
 
27
- ```bibtex
28
- @misc{dochkina2026drop,
29
- title = {Drop the Hierarchy and Roles: How Self-Organizing LLM Agents Outperform Designed Structures},
30
- author = {Dochkina, Victoria},
31
- year = {2026},
32
- eprint = {2603.28990},
33
- archivePrefix = {arXiv},
34
- primaryClass = {cs.AI},
35
- doi = {10.48550/arXiv.2603.28990},
36
- url = {https://arxiv.org/abs/2603.28990}
37
- }
38
- ```
45
+ Dogpile keeps the coordination loop strict and leaves the rest with you.
39
46
 
40
- ## Why Dogpile
47
+ Dogpile owns:
41
48
 
42
- - **Provider-neutral by default.** Any object with `id` and `generate(request)` can be the model boundary.
43
- - **Four first-party protocols.** Use `sequential`, `broadcast`, `shared`, or `coordinator` without changing the result contract.
44
- - **Live when you need it.** `Dogpile.stream()` yields the same event shapes that land in the final trace.
45
- - **Replayable by construction.** Completed runs return a JSON-serializable trace with inputs, events, provider calls, transcript, accounting, and final output.
46
- - **Application-owned effects.** Runtime tools, web search, code execution, credentials, and persistence stay under caller policy.
47
- - **Small public surface.** The package exports the SDK root, browser entrypoint, runtime subpaths, type subpath, and OpenAI-compatible provider adapter.
49
+ - agent turns, transcripts, protocol events, and final synthesis
50
+ - first-party `sequential`, `broadcast`, `shared`, and `coordinator` protocols
51
+ - streaming events that match the final trace
52
+ - cancellation, timeouts, budgets, termination policy, and typed errors
53
+ - JSON-serializable run artifacts for replay, audit, evals, and debugging
48
54
 
49
- ## Documentation
55
+ Your application owns:
50
56
 
51
- - [Developer usage guide](https://github.com/bubstack/dogpile/blob/main/docs/developer-usage.md): API choices, providers, protocols, streaming, termination, tools, replay, errors, browser usage, and repo commands.
52
- - [Examples](examples/README.md): repeatable protocol comparison and live OpenAI-compatible execution.
53
- - [Changelog](CHANGELOG.md): release notes and public-surface changes.
57
+ - credentials, provider SDKs, model routing, retries, and failover
58
+ - pricing tables and cost estimation
59
+ - persistence, queues, permissions, UI, and tool side effects
60
+ - any production policy around web search, code execution, or other tools
61
+
62
+ That boundary is the value proposition: Dogpile gives you coordinated model
63
+ work you can observe and replay, without taking over the product surface around
64
+ it.
54
65
 
55
- ## Choose Your Entry Point
66
+ ## Choose Your Path
56
67
 
57
68
  | Need | Start here |
58
69
  | --- | --- |
59
- | One application workflow | `Dogpile.pile({ intent, model })` |
60
- | Functional API without the namespace | `run({ intent, model })` |
61
- | Live UI or logs | `Dogpile.stream({ intent, model })` |
62
- | Repeated controlled runs | `Dogpile.createEngine({ protocol, tier, model })` |
63
- | Load a saved trace | `Dogpile.replay(trace)` |
64
- | Direct compatible HTTP endpoint | `createOpenAICompatibleProvider(options)` |
70
+ | Run one coordinated mission | `Dogpile.pile({ intent, model })` |
71
+ | Keep a UI or log view live | `Dogpile.stream({ intent, model })` |
72
+ | Compare coordination strategies | Pick `sequential`, `broadcast`, `shared`, or `coordinator` |
73
+ | Reuse fixed settings across many runs | `Dogpile.createEngine({ protocol, tier, model })` |
74
+ | Save and reload a completed run | Persist `result.trace`, then call `Dogpile.replay(trace)` |
75
+ | Use direct HTTP with OpenAI-compatible servers | `createOpenAICompatibleProvider(options)` |
65
76
 
66
77
  ## Install
67
78
 
68
- Dogpile ships to npm as `@dogpile/sdk`, a pure TypeScript package with its own provider-neutral model interface. The package root has no provider SDK peer dependency: pass any object that implements `ConfiguredModelProvider`, or use the built-in dependency-free OpenAI-compatible adapter for direct HTTP calls.
79
+ Dogpile ships to npm as `@dogpile/sdk`, a pure TypeScript package with its own
80
+ provider-neutral model interface. The package root has no provider SDK peer
81
+ dependency: pass any object that implements `ConfiguredModelProvider`, or use
82
+ the built-in dependency-free OpenAI-compatible adapter for direct HTTP calls.
69
83
 
70
84
  ```sh
71
85
  pnpm add @dogpile/sdk
@@ -83,139 +97,17 @@ yarn add @dogpile/sdk
83
97
  bun add @dogpile/sdk
84
98
  ```
85
99
 
86
- Dogpile itself does not read API keys or any other environment variables. Your provider object owns credentials, routing, pricing, retries, and any vendor SDKs.
87
-
88
- The SDK supports only Node.js LTS 22 / 24, Bun latest, and browser ESM runtimes. Core APIs are stateless and do not require filesystem access, a database, or a session store. Browser-aware bundlers can use the package root's `browser` export condition, and direct browser ESM consumers can import the bundled entrypoint from `@dogpile/sdk/browser`.
89
-
90
- ### Packed Tarball Quickstart Setup
91
-
92
- Use the packed-tarball path when validating the exact package that will be
93
- published. Packing from this repository requires Node.js LTS 22 or 24 and
94
- pnpm 10.33.0. Running the quickstart from a consumer project requires one of
95
- the supported runtimes: Node.js LTS 22 / 24 or Bun latest.
96
-
97
- From the Dogpile repository, build and pack the SDK:
98
-
99
- ```sh
100
- pnpm install
101
- pnpm run build
102
- pnpm pack --pack-destination ./packed
103
- ```
104
-
105
- The local tarball is named `dogpile-sdk-0.2.1.tgz` for the scoped package
106
- `@dogpile/sdk@0.2.1`. Install that tarball into a fresh consumer project:
107
-
108
- ```sh
109
- mkdir ../dogpile-quickstart
110
- cd ../dogpile-quickstart
111
- pnpm init
112
- pnpm add ../dogpile/packed/dogpile-sdk-0.2.1.tgz
113
- ```
114
-
115
- Equivalent install commands for other supported package managers are:
116
-
117
- ```sh
118
- npm install ../dogpile/packed/dogpile-sdk-0.2.1.tgz
119
- yarn add ../dogpile/packed/dogpile-sdk-0.2.1.tgz
120
- bun add ../dogpile/packed/dogpile-sdk-0.2.1.tgz
121
- ```
122
-
123
- ## Versioning and Stability
124
-
125
- Dogpile follows semantic versioning for published packages:
126
-
127
- - Patch releases fix bugs, tighten docs, or add tests without changing public behavior.
128
- - Minor releases add backward-compatible APIs, protocols, event fields, or runtime support.
129
- - Major releases may change public contracts, remove deprecated APIs, or alter protocol semantics.
130
-
131
- The current public surface includes the package root exports, high-level `Dogpile.pile()`, `run()`, `stream()`, `createEngine()`, the dependency-free OpenAI-compatible provider adapter, protocol and tier discriminated unions, event unions, trace/result types, and runtime portability guarantees for Node.js LTS 22 / 24, Bun latest, and browser ESM runtimes.
132
-
133
- Dogpile treats documented `dist` entrypoints, their runtime implementation dependencies, JavaScript source maps, declaration maps, original TypeScript sources for shipped runtime/browser/provider files, `README.md`, `CHANGELOG.md`, and `LICENSE` as the publishable package payload. Demo, benchmark, deterministic testing, and internal helper files are repository-only and stay out of the npm tarball. Core runtime code must remain pure TypeScript, storage-free, and free of Node-only dependencies so the same package can run across the supported Node.js, Bun, and browser ESM runtimes.
134
-
135
- ## Release Verification
136
-
137
- Before publishing, run the local package gates:
138
-
139
- ```sh
140
- pnpm run package:identity
141
- pnpm run package:artifacts
142
- pnpm run browser:smoke
143
- pnpm run benchmark:baseline
144
- pnpm run quickstart:smoke
145
- pnpm run verify
146
- pnpm run pack:check
147
- pnpm run publish:check
148
- ```
149
-
150
- What each gate proves:
151
-
152
- - `package:identity` asserts the scoped npm package name `@dogpile/sdk`, the current release identity, required package metadata, and release-facing references in source, docs, tests, and CI.
153
- - `package:artifacts` verifies that package metadata references only emitted runtime JavaScript and TypeScript declaration files covered by `package.json` `files`.
154
- - `browser:smoke` rebuilds the browser ESM bundle and imports `@dogpile/sdk` through the package root `browser` condition.
155
- - `benchmark:baseline` rebuilds `dist`, runs the deterministic protocol-loop timing harness, and prints repeatable JSON for local before/after comparisons.
156
- - `quickstart:smoke` creates a real `pnpm pack` tarball, installs it into a fresh consumer project, and asserts the dependency and lockfile resolve `@dogpile/sdk` from the `.tgz` instead of `workspace:` or `link:` metadata.
157
- - `quickstart:smoke` also verifies installed entrypoints and `dist` imports do not resolve through local source imports, imports every public package subpath from the installed tarball, runs the marked README quickstart, runs `tsc --noEmit` from the consumer project, verifies private helper files are absent from the installed tarball, and proves private helper subpaths remain blocked by package exports.
158
- - `consumer:smoke` is kept as the same packed-tarball quickstart smoke command for compatibility.
159
- - `verify` rebuilds `dist`, runs the package artifact guard, runs the packed-tarball quickstart smoke, runs strict typecheck, and then runs the test suite.
160
- - `pack:check` runs package identity, rebuilds `dist`, verifies package artifacts, runs the packed-tarball quickstart smoke, checks packed JavaScript source maps and declaration maps, and finishes with `npm pack --dry-run`.
161
- - `publish:check` runs `verify`, reruns the package artifact guard, and then runs `npm publish --dry-run` so the package metadata, export map, and publishable files are checked without publishing.
162
-
163
- The release identity is `@dogpile/sdk@0.2.1`. A real `pnpm pack` or `npm pack` for this scoped package produces the local tarball `dogpile-sdk-0.2.1.tgz`; the dry-run package gate must report that tarball filename and the scoped npm package name before publish. See `CHANGELOG.md` for release notes and breaking-change documentation.
164
-
165
- The browser ESM target is emitted at `dist/browser/index.js` with `dist/browser/index.js.map`; both the package root `browser` condition and the explicit `@dogpile/sdk/browser` subpath resolve to that bundled artifact.
166
-
167
- ### Required CI Status Checks
168
-
169
- Before publishing from `main` or a `release/**` branch, GitHub branch protection or release review must require these `Release Validation` workflow checks to pass:
170
-
171
- - `Release Validation / Required Node.js 22 full suite`
172
- - `Release Validation / Required Node.js 24 full suite`
173
- - `Release Validation / Required Bun latest full suite`
174
- - `Release Validation / Required browser bundle smoke`
175
- - `Release Validation / Required packed-tarball quickstart smoke`
176
- - `Release Validation / Required pack:check package artifact`
177
-
178
- Do not publish `@dogpile/sdk` unless all Node LTS matrix entries, the Bun latest suite, the browser bundle smoke job, the packed-tarball quickstart smoke job, and the dependent `pack:check` package artifact job are green.
179
-
180
- ### Automated npm Publishing
181
-
182
- `.github/workflows/npm-publish.yml` publishes `@dogpile/sdk` from GitHub Actions
183
- when a GitHub Release is published. It can also be started manually with
184
- `workflow_dispatch`; the manual path defaults to a dry run.
185
-
186
- The publish workflow uses npm Trusted Publishing/OIDC rather than a long-lived
187
- npm automation token. Configure the package's trusted publisher on npmjs.com
188
- with:
189
-
190
- - Organization or user: `bubstack`
191
- - Repository: `dogpile`
192
- - Workflow filename: `npm-publish.yml`
193
- - Environment name: `npm`
194
-
195
- Before the first automated release, publish the initial package version from an
196
- npm account with owner access to the `@dogpile` scope:
197
-
198
- ```sh
199
- npm publish --access public
200
- ```
201
-
202
- That first manual publish creates the `@dogpile/sdk` package settings page on
203
- npmjs.com. After it exists, add the Trusted Publisher fields above, then use
204
- GitHub Releases or the manual workflow dry-run path for future releases. The
205
- local safety check for the first publish is:
100
+ Dogpile itself does not read API keys or any other environment variables. Your
101
+ provider object owns credentials, routing, pricing, retries, and any vendor
102
+ SDKs.
206
103
 
207
- ```sh
208
- pnpm run publish:check
209
- ```
210
-
211
- The workflow grants `id-token: write`, runs on a GitHub-hosted Node.js 24
212
- runner, installs the latest npm CLI, runs `pnpm run publish:check`, verifies the
213
- target version is not already published, and then runs
214
- `npm publish --access public`. Keep the `npm` GitHub environment protected for
215
- release review if this repository should require human approval before a
216
- published GitHub Release can reach the npm registry.
104
+ The SDK supports only Node.js LTS 22 / 24, Bun latest, and browser ESM runtimes.
105
+ Core APIs are stateless and do not require filesystem access, a database, or a
106
+ session store. Browser-aware bundlers can use the package root's `browser`
107
+ export condition, and direct browser ESM consumers can import the bundled
108
+ entrypoint from `@dogpile/sdk/browser`.
217
109
 
218
- ## Import
110
+ ## Quickstart
219
111
 
220
112
  Use the branded high-level API for application code:
221
113
 
@@ -358,498 +250,37 @@ const provider = createOpenAICompatibleProvider({
358
250
  });
359
251
  ```
360
252
 
361
- ### Provider Boundary
362
-
363
- `ConfiguredModelProvider` is the only model contract Dogpile core needs:
364
-
365
- ```ts
366
- const provider = {
367
- id: "my-provider",
368
- async generate(request) {
369
- const response = await myModelClient.complete({
370
- messages: request.messages,
371
- temperature: request.temperature,
372
- signal: request.signal
373
- });
374
-
375
- return {
376
- text: response.text,
377
- usage: response.usage,
378
- costUsd: response.costUsd
379
- };
380
- }
381
- };
382
- ```
383
-
384
- The provider owns vendor SDKs, credentials, model names, retries, routing,
385
- pricing, and telemetry. Dogpile owns protocol orchestration, events, traces,
386
- termination policy, and replayable result shapes.
387
-
388
- ### OpenAI-Compatible Provider Configuration
389
-
390
- `createOpenAICompatibleProvider()` returns Dogpile's
391
- `ConfiguredModelProvider`, which is the value passed to `Dogpile.pile()`,
392
- `run()`, `stream()`, or `createEngine()`.
393
-
394
- ```ts
395
- const provider = createOpenAICompatibleProvider({
396
- id: "openai:gpt-4.1-mini",
397
- model: "gpt-4.1-mini",
398
- apiKey: process.env.OPENAI_API_KEY,
399
- maxOutputTokens: 1_024,
400
- extraBody: {
401
- reasoning_effort: "low"
402
- },
403
- costEstimator({ usage }) {
404
- return usage ? usage.totalTokens * 0.0000003 : undefined;
405
- }
406
- });
407
- ```
408
-
409
- The configuration object supports:
410
-
411
- - `model`: required model id sent to the compatible chat-completions endpoint.
412
- - `apiKey`: optional bearer token. Dogpile does not read environment variables;
413
- pass the credential explicitly when the endpoint requires one.
414
- - `baseURL`: endpoint root; defaults to `https://api.openai.com/v1`.
415
- - `path`: request path under `baseURL`; defaults to `/chat/completions`.
416
- - `id`: stable provider id stored in events, traces, and errors; when omitted,
417
- Dogpile uses `openai-compatible:<model>`.
418
- - `fetch`: optional fetch-compatible implementation for tests, custom runtimes,
419
- proxies, or instrumentation.
420
- - `costEstimator`: caller-owned usage-to-USD function. Dogpile passes
421
- `{ providerId, request, response, usage }` and records the returned number as
422
- `costUsd`; Dogpile does not bundle model pricing.
423
- - `headers`: optional headers merged into the request. `authorization` is set
424
- from `apiKey` unless you provide it yourself.
425
- - `maxOutputTokens`: optional positive integer sent as `max_tokens`.
426
- - `extraBody`: optional JSON object merged into the request body before
427
- Dogpile sets `model`, `messages`, `temperature`, and `max_tokens`.
428
-
429
- During each model turn, Dogpile supplies a provider-neutral `ModelRequest` and
430
- the adapter builds an OpenAI-compatible chat-completions request with this
431
- mapping:
432
-
433
- | Dogpile field | Request field | Behavior |
434
- | --- | --- | --- |
435
- | `request.messages[].role` and `request.messages[].content` | `messages[].role` and `messages[].content` | Preserves message order and role/content values. |
436
- | `request.temperature` | `temperature` | Forwards the protocol-selected sampling temperature. |
437
- | `request.signal` | `signal` | Passes caller cancellation through to `fetch`. |
438
- | `request.metadata` | Not forwarded | Remains Dogpile trace/protocol metadata and is available to `costEstimator` through `request`. |
439
- | `model` | `model` | Sends the configured model id. |
440
- | `maxOutputTokens` | `max_tokens` | Sends the configured output cap when present. |
441
- | `extraBody` | request body | Merges caller-owned provider options before canonical Dogpile fields are written. |
442
-
443
- OpenAI-compatible responses are normalized back into Dogpile's stable public
444
- provider types with this mapping:
445
-
446
- | Response field | Dogpile field | Behavior |
447
- | --- | --- | --- |
448
- | `choices[0].message.content` | `ModelResponse.text` | Becomes the completed model-turn text. String content and text parts are supported. |
449
- | `choices[0].finish_reason` | `finishReason` | Maps `stop`, `length`, content-filter, and tool-call finish reasons to Dogpile's provider-neutral finish reason union. |
450
- | `usage.prompt_tokens` / `usage.completion_tokens` / `usage.total_tokens` | `usage` | Maps input/output/total tokens when all counts are available. |
451
- | Caller `costEstimator` return value | `costUsd` | Calls `costEstimator({ providerId, request, response, usage })`; Dogpile does not ship or infer a pricing table. |
452
- | `id`, `object`, `created`, `model`, and `usage` | `metadata.openAICompatible` | Stores JSON-compatible response metadata. |
453
- | HTTP/provider failures | `DogpileError` | Wraps failures with stable string codes such as `provider-rate-limited`, `provider-authentication`, `provider-invalid-request`, and `provider-unavailable`. |
454
-
455
- ## Runtime Tool Input Validation
456
-
457
- Runtime tools can define an optional `validateInput(input)` hook when their
458
- JSON schema is not enough to enforce the executable contract. Dogpile validates
459
- the tool definition at registration time, before protocol execution begins; if
460
- `validateInput` is present, it must be callable. Invalid registrations fail with
461
- `DogpileError` code `"invalid-configuration"` and a `detail.path` such as
462
- `tools[0].validateInput`.
463
-
464
- For each registered tool call, Dogpile emits the `tool-call` event, builds the
465
- `RuntimeToolExecutionContext`, then calls `validateInput()` with the normalized
466
- JSON object input immediately before `execute()`. If the hook is omitted, the
467
- input is treated as valid. If the hook returns `{ type: "invalid", issues }`,
468
- Dogpile does not call `execute()`. Instead it returns and emits a
469
- `RuntimeToolErrorResult` with `error.code: "invalid-input"`, `retryable: false`,
470
- and `error.detail.issues` copied from the validation result. This is tool-result
471
- data, not a thrown `DogpileError`, so traces and transcripts remain replayable.
472
-
473
- Tool authors should keep `inputSchema` as the model-visible JSON contract and
474
- use `validateInput()` for runtime-only checks such as cross-field constraints,
475
- caller policy, adapter limits, or stricter type narrowing before side effects.
476
- Return serializable `RuntimeToolValidationIssue` objects for ordinary bad model
477
- or caller input instead of throwing. Keep the hook deterministic and side-effect
478
- free because it runs on every execution of that tool; put network calls,
479
- filesystem work, sandbox execution, and other effects inside `execute()` after
480
- validation has passed.
481
-
482
- ```ts
483
- import { type RuntimeTool } from "@dogpile/sdk";
484
-
485
- interface LookupInput {
486
- readonly query?: string;
487
- }
488
-
489
- const lookupTool: RuntimeTool<LookupInput> = {
490
- identity: {
491
- id: "app.tools.lookup",
492
- name: "lookup",
493
- description: "Look up release context."
494
- },
495
- inputSchema: {
496
- kind: "json-schema",
497
- schema: {
498
- type: "object",
499
- properties: {
500
- query: { type: "string", minLength: 1 }
501
- },
502
- required: ["query"],
503
- additionalProperties: false
504
- }
505
- },
506
- validateInput(input) {
507
- return typeof input.query === "string" && input.query.trim().length > 0
508
- ? { type: "valid" }
509
- : {
510
- type: "invalid",
511
- issues: [
512
- {
513
- code: "missing-field",
514
- path: "query",
515
- message: "query is required."
516
- }
517
- ]
518
- };
519
- },
520
- execute(input, context) {
521
- return {
522
- type: "success",
523
- toolCallId: context.toolCallId,
524
- tool: this.identity,
525
- output: {
526
- answer: `result for ${input.query}`
527
- }
528
- };
529
- }
530
- };
531
- ```
532
-
533
- ## DogpileError Codes
534
-
535
- `DogpileError` and `DogpileErrorCode` are exported from `@dogpile/sdk`. The
536
- string `code` values below are the stable public contract for JavaScript callers,
537
- TypeScript discriminated-union handling, retry policy, and observability. When
538
- `retryable` is present, prefer it over a hard-coded policy; the handling column
539
- describes the default caller posture when provider metadata does not override
540
- it.
541
-
542
- | Code | When Dogpile emits it | Caller handling |
543
- | --- | --- | --- |
544
- | `invalid-configuration` | Public entrypoint or adapter validation fails before a model turn starts, including malformed `run()`, `stream()`, `createEngine()`, runtime tool, provider registration, or OpenAI-compatible adapter options. `detail.path` points at the failing input. | Treat as a caller/configuration bug. Do not retry the same request; fix the option named by `detail.path`. |
545
- | `aborted` | A caller `AbortSignal`, `StreamHandle.cancel()`, or provider-facing abort failure cancels an active run or stream. | Treat as intentional cancellation. Stop consuming the run, clean up UI/state, and start a new request only if the user asks. |
546
- | `timeout` | Dogpile's own `budget.timeoutMs` deadline expires and Dogpile aborts the active provider-facing request. | Safe to retry with a larger timeout, smaller workload, cheaper/faster tier, or different provider. |
547
- | `provider-authentication` | The configured provider reports authentication or authorization failure, including HTTP 401/403 or API key loading errors. | Do not blindly retry. Refresh credentials, permissions, provider configuration, or account state. |
548
- | `provider-invalid-request` | The provider SDK rejects the model request shape, prompt, tool choice/input, argument set, or type validation before a valid provider response is produced. | Treat as a request-construction bug. Fix the prompt/tool/options payload before retrying. |
549
- | `provider-invalid-response` | The provider returns an empty, unparsable, schema-invalid, or no-output response that the configured adapter cannot normalize. | Usually safe to retry once or fail over; log `detail` because repeated failures may indicate provider drift or an unsupported response shape. |
550
- | `provider-not-found` | The provider SDK reports a missing provider, model, route, or resource, including HTTP 404. | Do not retry unchanged. Verify the configured model id, provider id, deployment, and account access. |
551
- | `provider-rate-limited` | The provider indicates quota, contention, or rate limiting, including HTTP 409/429. | Retry with backoff or switch providers; surface quota pressure when retries are exhausted. |
552
- | `provider-timeout` | The provider or upstream gateway times out the request, including HTTP 408/504. | Retry with backoff, reduce prompt/work size, or fail over to another provider. |
553
- | `provider-unavailable` | The provider is temporarily unavailable, including HTTP 5xx responses or retry exhaustion. | Retry with backoff or fail over. Preserve the original `providerId` and `detail` for incident correlation. |
554
- | `provider-unsupported` | The provider/model does not support a requested function or model version. | Do not retry unchanged. Disable the unsupported feature or choose a model that supports it. |
555
- | `provider-error` | The configured adapter reports a generic provider failure that Dogpile cannot map to a narrower provider code. | Check `retryable` and `detail.statusCode` when present. Retry transient status codes; otherwise inspect provider diagnostics. |
556
- | `unknown` | Dogpile catches an unrecognized provider or adapter failure with no stable mapping. | Treat conservatively: log `detail`, avoid assuming retry safety unless `retryable` is true, and add handling once the underlying failure is understood. |
557
-
558
- Use `stream()` or `Dogpile.stream()` when you need a live event log, and `createEngine()` when a research harness needs reusable low-level protocol settings across many missions.
559
-
560
- ## Benchmark Artifacts
561
-
562
- Benchmark runners and deterministic provider fixtures remain repository test
563
- harnesses in this release. They are intentionally not exported from
564
- `@dogpile/sdk`.
565
- Repository tests and benchmark harnesses that need those helpers import them
566
- from the source-only internal path `../internal.js`, which resolves to
567
- `src/internal.ts` in the TypeScript source tree.
568
- Consumer applications should build reproducibility artifacts from the public
569
- `RunResult`, `Trace`, `transcript`, `eventLog`, and cost summary returned by
570
- `run()`, `stream()`, or `Dogpile.pile()`.
571
-
572
- ## Single-Call Workflow Contract
573
-
574
- Application code starts with `Dogpile.pile()`: provide the mission and a model
575
- provider adapter. When `protocol`, `tier`, and `budget` are omitted, Dogpile
576
- uses the default application flow: Sequential coordination, the `balanced`
577
- cost/quality tier, and no hard budget caps beyond the tier preset.
578
-
579
- ```ts
580
- const result = await Dogpile.pile({
581
- intent: "Plan the safest SDK v1 release sequence.",
582
- model: provider
583
- });
584
-
585
- console.log(result.output);
586
- console.log(result.trace.events);
587
- console.log(result.transcript);
588
- ```
589
-
590
- Pass explicit controls when the application needs a non-default protocol,
591
- cost tier, or hard budget cap:
592
-
593
- ```ts
594
- const result = await Dogpile.pile({
595
- intent: "Plan the safest SDK v1 release sequence.",
596
- protocol: "sequential",
597
- tier: "balanced",
598
- model: provider,
599
- budget: { maxTokens: 20_000 }
600
- });
601
- ```
602
-
603
- Use an explicit protocol configuration when the workflow needs custom
604
- coordination limits, and pair it with an explicit cost tier plus hard caps:
605
-
606
- ```ts
607
- import { Dogpile, type Budget, type ProtocolConfig } from "@dogpile/sdk";
608
-
609
- const protocol = {
610
- kind: "broadcast",
611
- maxRounds: 2
612
- } satisfies ProtocolConfig;
613
-
614
- const budget = {
615
- tier: "quality",
616
- maxUsd: 0.5,
617
- maxTokens: 24_000,
618
- qualityWeight: 0.85
619
- } satisfies Budget;
620
-
621
- const result = await Dogpile.pile({
622
- intent: "Produce a release-risk brief with independent reviewer opinions.",
623
- protocol,
624
- tier: budget.tier,
625
- model: provider,
626
- budget: {
627
- maxUsd: budget.maxUsd,
628
- maxTokens: budget.maxTokens,
629
- qualityWeight: budget.qualityWeight
630
- }
631
- });
632
-
633
- console.log(result.output);
634
- console.log(result.trace.protocol);
635
- console.log(result.trace.events);
636
- ```
637
-
638
- The required inputs are:
639
-
640
- - `intent`: the mission the agent collective should solve.
641
- - `model`: a caller-owned `ConfiguredModelProvider`, backed by your direct provider client, a compatible HTTP endpoint, or a test fixture.
642
-
643
- Optional inputs refine the run without changing the core contract:
644
-
645
- - `protocol`: `"coordinator"`, `"sequential"`, `"broadcast"`, or `"shared"`, or an explicit protocol config such as `{ kind: "broadcast", maxRounds: 2 }`; omitted protocols default to `"sequential"`. Named broadcast runs default to two rounds: an intention broadcast followed by final decisions. Shared runs may include `organizationalMemory` so every agent sees the same prior-memory snapshot without seeing current-run peer updates.
646
- - `tier`: `"fast"`, `"balanced"`, or `"quality"`; omitted tiers default to `"balanced"`.
647
- - `budget`: hard caps layered over the tier, for example `{ maxUsd: 0.25, maxTokens: 20_000, timeoutMs: 60_000, qualityWeight: 0.7 }`. When `timeoutMs` expires, Dogpile aborts the active provider-facing request and rejects with `DogpileError` code `"timeout"`.
648
- - `agents`: an explicit roster of `{ id, role, instructions? }` participants.
649
- - `temperature`: an override for the tier-selected default sampling temperature.
650
-
651
- Invalid caller configuration is rejected before any protocol turn starts with
652
- `DogpileError` code `"invalid-configuration"` and `retryable: false`. The
653
- error `detail` includes `kind: "configuration-validation"`, the failing
654
- `path`, the `rule`, and the expected shape. Validation covers required
655
- `intent`, provider registrations, protocol and tier enums, positive turn/round
656
- limits, non-negative budget caps, normalized `qualityWeight`, agent and tool
657
- shapes, termination policies, `AbortSignal` inputs, and built-in provider
658
- adapter options.
659
-
660
- Every completed call returns the same result shape:
661
-
662
- ```ts
663
- type RunResult = {
664
- output: string;
665
- eventLog: RunEventLog;
666
- trace: Trace;
667
- transcript: readonly TranscriptEntry[];
668
- usage: RunUsage;
669
- metadata: RunMetadata;
670
- accounting: RunAccounting;
671
- cost: CostSummary;
672
- quality?: number;
673
- };
674
- ```
675
-
676
- - `output` is the final synthesized answer.
677
- - `eventLog` is the complete non-streaming event log with ordered event types, count, and events.
678
- - `trace` is a JSON-serializable replay artifact containing the run id, protocol, tier, model provider id, agents used, ordered event log, and transcript.
679
- - `trace.events` is the full event log for coordination moments: `role-assignment`, `agent-turn`, `broadcast`, and `final`. Agent-turn and broadcast records include `decision` when model output follows Dogpile's structured `role_selected`, `participation`, `rationale`, and `contribution` labels.
680
- - `transcript` is the ordered list of model-visible agent turns with `agentId`, `role`, `input`, `output`, and optional parsed `decision`; it matches `trace.transcript` for ergonomic access.
681
- - `usage` reports aggregate USD and token accounting.
682
- - `metadata` exposes run id, protocol, tier, model provider id, participating agents, and start/completion timestamps.
683
- - `accounting` bundles the selected tier, optional budget caps, termination policy, final usage/cost, budget state snapshots, and cap utilization.
684
- - `cost` is retained as a compatibility alias for `usage`.
685
-
686
- ## Replay Trace Contract
687
-
688
- `result.trace` is the canonical replay artifact for a completed run. It is
689
- versioned by `schemaVersion`, JSON-serializable, and contains all SDK-owned
690
- state required to inspect or reproduce the coordination path without Dogpile
691
- storage. Callers own persistence and may save the trace as JSON, NDJSON-derived
692
- records, object storage, or database rows.
693
-
694
- The required trace sections are:
695
-
696
- - `inputs`: normalized mission, protocol config, tier, model provider id,
697
- agent roster, and temperature.
698
- - `budget`: selected tier, caller caps, and the serializable termination policy
699
- used by the run.
700
- - `seed`: caller seed metadata, or an explicit `source: "none"` record when no
701
- seed was supplied.
702
- - `events`: the ordered event log emitted during execution.
703
- - `protocolDecisions`: one replay decision per event, with `eventIndex`
704
- pointing at the corresponding `events[eventIndex]`.
705
- - `providerCalls`: every provider request and provider response, ordered by
706
- execution and keyed by stable `callId` values such as
707
- `${runId}:provider-call:1`.
708
- - `budgetStateChanges`: cumulative cost snapshots derived from cost-bearing
709
- events.
710
- - `transcript`: ordered model-visible prompt/output turns.
711
- - `finalOutput`: terminal output, final cost, completion timestamp, and
712
- transcript link.
713
-
714
- Event ordering is authoritative. `trace.events`, `result.eventLog.events`, and
715
- the live events yielded by `stream()` use the same order for completed runs.
716
- `result.eventLog.eventTypes` is exactly `trace.events.map(event => event.type)`,
717
- and `protocolDecisions[n].eventIndex === n` for each recorded coordination
718
- moment. The terminal event is always `final` for a successful run, and
719
- `trace.finalOutput.output` matches `result.output`.
720
-
721
- Provider responses are preserved in `trace.providerCalls`, not inferred from
722
- final text. Each provider call stores the exact provider-neutral `ModelRequest`
723
- handed to the configured adapter and the exact `ModelResponse` returned by that
724
- adapter, including optional usage and USD cost. For current protocol runners,
725
- provider calls are ordered one-to-one with transcript entries and completed
726
- `agent-turn` events: the `n`th provider response text is the `n`th transcript
727
- output, and the `n`th transcript input is the final user message in the `n`th
728
- provider request. Streaming providers may additionally emit
729
- `model-output-chunk` events before the completed `agent-turn`; the completed
730
- provider response remains the replay source for the final turn text.
731
-
732
- Use `Dogpile.stream()` when the UI or harness needs the event log as it happens.
733
- The stream yields the same `RunEvent` values that will later appear in
734
- `result.trace.events`, and the final result is available through
735
- `handle.result`. Pass `signal` to propagate caller cancellation through
736
- streamed provider requests, or call `handle.cancel()` to abort the active
737
- provider-facing request, close the stream iterator, mark `handle.status` as
738
- `"cancelled"`, and reject `handle.result` with `DogpileError` code
739
- `"aborted"`.
740
-
741
- ```ts
742
- const abortController = new AbortController();
743
- const handle = Dogpile.stream({
744
- intent: "Compare release risks across protocol variants.",
745
- protocol: "broadcast",
746
- tier: "quality",
747
- model: provider,
748
- budget: { maxTokens: 12_000 },
749
- signal: abortController.signal
750
- });
751
-
752
- for await (const event of handle) {
753
- if (event.type === "agent-turn") {
754
- renderTurn(event.agentId, event.output);
755
- }
756
- }
757
-
758
- // Later, if the user leaves the view or stops the workflow:
759
- // handle.cancel();
760
-
761
- const result = await handle.result;
762
- ```
763
-
764
- Use `replay()` / `Dogpile.replay()` or `replayStream()` /
765
- `Dogpile.replayStream()` when loading a trace artifact you already persisted.
766
- The replay entrypoints accept the saved `Trace` object and reconstruct the same
767
- public `RunResult`, event stream, and transcript without calling a model,
768
- reading disk, or using SDK-managed storage:
769
-
770
- ```ts
771
- import { Dogpile, replay, replayStream, type Trace } from "@dogpile/sdk";
772
-
773
- const trace = await loadJson<Trace>("run-trace.json");
774
-
775
- const result = replay(trace);
776
- const sameResult = Dogpile.replay(trace);
777
- const replayHandle = replayStream(trace);
778
-
779
- for await (const event of replayHandle) {
780
- renderReplayEvent(event);
781
- }
782
-
783
- console.log(result.output);
784
- console.log(result.eventLog.events);
785
- console.log(sameResult.transcript);
786
- console.log((await replayHandle.result).output);
787
- ```
788
-
789
- Researchers can drop below the single-call surface with `createEngine()` while
790
- keeping the same stateless result contract:
791
-
792
- ```ts
793
- const engine = Dogpile.createEngine({
794
- protocol: { kind: "sequential", maxTurns: 4 },
795
- tier: "balanced",
796
- model: provider,
797
- agents
798
- });
799
-
800
- const reproductionRun = await engine.run("Reproduce the paper triage task.");
801
- ```
253
+ ## Documentation
802
254
 
803
- For a lower-level reproduction harness, keep protocol settings and agents fixed
804
- on the engine, capture only the streaming events you need for live analysis, and
805
- persist the completed trace/transcript yourself:
255
+ - [Developer usage guide](https://github.com/bubstack/dogpile/blob/main/docs/developer-usage.md):
256
+ deeper API choices, providers, protocols, streaming, termination, tools,
257
+ replay, errors, browser usage, and repo commands.
258
+ - [API and trace reference](https://github.com/bubstack/dogpile/blob/main/docs/reference.md):
259
+ provider contracts,
260
+ OpenAI-compatible mapping, runtime tool validation, errors, result shapes, and
261
+ replay traces.
262
+ - [Release and package guide](https://github.com/bubstack/dogpile/blob/main/docs/release.md):
263
+ versioning, packed tarball
264
+ checks, release validation, CI status checks, and npm publishing.
265
+ - [Examples](examples/README.md): repeatable protocol comparison and live
266
+ OpenAI-compatible execution.
267
+ - [Changelog](CHANGELOG.md): release notes and public-surface changes.
806
268
 
807
- ```ts
808
- import {
809
- Dogpile,
810
- type AgentSpec,
811
- type ProtocolConfig,
812
- type RunEvent,
813
- type TranscriptEntry
814
- } from "@dogpile/sdk";
269
+ ## Research Basis
815
270
 
816
- const protocol = {
817
- kind: "shared",
818
- maxTurns: 4
819
- } satisfies ProtocolConfig;
820
-
821
- const agents = [
822
- { id: "a", role: "solver", instructions: "Solve the task directly." },
823
- { id: "b", role: "auditor", instructions: "Challenge weak assumptions." },
824
- { id: "c", role: "editor", instructions: "Merge the strongest answer." }
825
- ] satisfies readonly AgentSpec[];
826
-
827
- const engine = Dogpile.createEngine({
828
- protocol,
829
- tier: "quality",
830
- model: provider,
831
- agents,
832
- temperature: 0.1,
833
- budget: { maxTokens: 16_000, qualityWeight: 0.9 }
834
- });
271
+ Dogpile's protocol vocabulary and paper-faithfulness examples are based on:
835
272
 
836
- const handle = engine.stream("Re-run the L3 release readiness triage fixture.");
837
- const eventLog: RunEvent[] = [];
273
+ Dochkina, V. (2026). *Drop the Hierarchy and Roles: How Self-Organizing LLM Agents Outperform Designed Structures*. arXiv:2603.28990 [cs.AI]. https://doi.org/10.48550/arXiv.2603.28990
838
274
 
839
- for await (const event of handle) {
840
- if (event.type === "agent-turn" || event.type === "broadcast") {
841
- eventLog.push(event);
842
- }
275
+ ```bibtex
276
+ @misc{dochkina2026drop,
277
+ title = {Drop the Hierarchy and Roles: How Self-Organizing LLM Agents Outperform Designed Structures},
278
+ author = {Dochkina, Victoria},
279
+ year = {2026},
280
+ eprint = {2603.28990},
281
+ archivePrefix = {arXiv},
282
+ primaryClass = {cs.AI},
283
+ doi = {10.48550/arXiv.2603.28990},
284
+ url = {https://arxiv.org/abs/2603.28990}
843
285
  }
844
-
845
- const result = await handle.result;
846
- const transcript: readonly TranscriptEntry[] = result.transcript;
847
- const replayArtifact = {
848
- output: result.output,
849
- eventLog,
850
- transcript,
851
- trace: result.trace
852
- };
853
-
854
- await saveJson(replayArtifact);
855
286
  ```