@nebutra/agent-runtime 0.2.1 → 0.2.2

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 (101) hide show
  1. package/LICENSE +21 -676
  2. package/dist/adapters/index.d.ts +24 -5
  3. package/dist/adapters/index.js +27 -0
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/chunk-Q62VKHIT.js +178 -0
  6. package/dist/chunk-Q62VKHIT.js.map +1 -0
  7. package/dist/{chunk-MUF7ZZTO.js → chunk-R5HOSQUW.js} +2 -1
  8. package/dist/{chunk-MUF7ZZTO.js.map → chunk-R5HOSQUW.js.map} +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +14 -17
  11. package/dist/index.js.map +1 -1
  12. package/dist/orchestration.d.ts +42 -1
  13. package/dist/orchestration.js +7 -3
  14. package/dist/sandbox.js +1 -1
  15. package/package.json +80 -30
  16. package/.turbo/turbo-build.log +0 -130
  17. package/.turbo/turbo-test.log +0 -47
  18. package/.turbo/turbo-typecheck.log +0 -4
  19. package/CHANGELOG.md +0 -250
  20. package/dist/chunk-MX2WL43P.js +0 -90
  21. package/dist/chunk-MX2WL43P.js.map +0 -1
  22. package/examples/pulsar-quickstart.ts +0 -35
  23. package/examples/resume-branch.ts +0 -45
  24. package/examples/subagent-fanout.ts +0 -20
  25. package/src/adapters/dispatcher-sse.test.ts +0 -218
  26. package/src/adapters/dispatcher-sse.ts +0 -222
  27. package/src/adapters/index.ts +0 -18
  28. package/src/adapters/mcp-catalog.test.ts +0 -213
  29. package/src/adapters/mcp-catalog.ts +0 -188
  30. package/src/adapters/prisma-rollout.test.ts +0 -153
  31. package/src/adapters/prisma-rollout.ts +0 -104
  32. package/src/agent-runtime.test.ts +0 -176
  33. package/src/artifact-stream.test.ts +0 -330
  34. package/src/artifact-stream.ts +0 -453
  35. package/src/channel-gateway.test.ts +0 -432
  36. package/src/channel-gateway.ts +0 -357
  37. package/src/cli.ts +0 -49
  38. package/src/code-review.test.ts +0 -501
  39. package/src/code-review.ts +0 -495
  40. package/src/command-suggestions.test.ts +0 -251
  41. package/src/command-suggestions.ts +0 -338
  42. package/src/commands.test.ts +0 -184
  43. package/src/commands.ts +0 -140
  44. package/src/commit-message.test.ts +0 -249
  45. package/src/commit-message.ts +0 -180
  46. package/src/context-compaction.test.ts +0 -522
  47. package/src/context-compaction.ts +0 -438
  48. package/src/definitions.test.ts +0 -78
  49. package/src/definitions.ts +0 -190
  50. package/src/deployment-status.test.ts +0 -215
  51. package/src/deployment-status.ts +0 -227
  52. package/src/design-context.test.ts +0 -195
  53. package/src/design-context.ts +0 -198
  54. package/src/dispatcher.test.ts +0 -234
  55. package/src/dispatcher.ts +0 -189
  56. package/src/durable-turn.test.ts +0 -209
  57. package/src/durable-turn.ts +0 -135
  58. package/src/edit-planner.test.ts +0 -204
  59. package/src/edit-planner.ts +0 -325
  60. package/src/fuzzy-match.test.ts +0 -311
  61. package/src/fuzzy-match.ts +0 -444
  62. package/src/hook-pipeline.test.ts +0 -279
  63. package/src/hook-pipeline.ts +0 -373
  64. package/src/inbound-admission.test.ts +0 -394
  65. package/src/inbound-admission.ts +0 -246
  66. package/src/index.ts +0 -49
  67. package/src/loop.test.ts +0 -161
  68. package/src/loop.ts +0 -211
  69. package/src/mcp-bridge.test.ts +0 -165
  70. package/src/mcp-bridge.ts +0 -81
  71. package/src/memory-provider.test.ts +0 -232
  72. package/src/memory-provider.ts +0 -257
  73. package/src/model.ts +0 -168
  74. package/src/orchestration.test.ts +0 -53
  75. package/src/orchestration.ts +0 -146
  76. package/src/permission-ruleset.test.ts +0 -301
  77. package/src/permission-ruleset.ts +0 -1
  78. package/src/policy.ts +0 -151
  79. package/src/project-repo.test.ts +0 -232
  80. package/src/project-repo.ts +0 -311
  81. package/src/protocol.ts +0 -159
  82. package/src/pulsar.test.ts +0 -156
  83. package/src/pulsar.ts +0 -322
  84. package/src/rollout-store-persistent.test.ts +0 -217
  85. package/src/rollout-store-persistent.ts +0 -166
  86. package/src/rollout.ts +0 -150
  87. package/src/sandbox.ts +0 -113
  88. package/src/session-share.test.ts +0 -360
  89. package/src/session-share.ts +0 -310
  90. package/src/skill-distillation.test.ts +0 -177
  91. package/src/skill-distillation.ts +0 -369
  92. package/src/skills.test.ts +0 -277
  93. package/src/skills.ts +0 -277
  94. package/src/subagents.test.ts +0 -290
  95. package/src/subagents.ts +0 -332
  96. package/src/tools.test.ts +0 -12
  97. package/src/tools.ts +0 -129
  98. package/src/workbench.test.ts +0 -0
  99. package/src/workbench.ts +0 -0
  100. package/tsconfig.json +0 -12
  101. package/tsup.config.ts +0 -36
package/CHANGELOG.md DELETED
@@ -1,250 +0,0 @@
1
- # @nebutra/agent-runtime
2
-
3
- ## 0.2.1
4
-
5
- ### Patch Changes
6
-
7
- - Publish registry package metadata under the MIT license.
8
-
9
- - Updated dependencies []:
10
- - @nebutra/ai-primitives@0.1.1
11
- - @nebutra/capability-kit@0.2.1
12
- - @nebutra/execution-policy@0.1.1
13
- - @nebutra/mcp@0.1.2
14
-
15
- ## 0.2.0
16
-
17
- ### Minor Changes
18
-
19
- - [`fab751f`](https://github.com/Nebutra/Nebutra-Sailor/commit/fab751f39639251bfa006b1bfbcd8cd62f94626a) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - New adapter subpaths inside `@nebutra/agent-runtime`: concrete reusable port
20
- adapters for the runtime package.
21
- - `mcp-catalog`: adapts `@nebutra/mcp` `serverRegistry`/`mcpClient` into the
22
- `McpServerCatalogPort` / `McpClientLike` ports — plan-gated, tenant
23
- fail-closed, schema-less MCP tools still yield usable definitions,
24
- composes with `activateMcpTools`.
25
- - `dispatcher-sse`: runtime-agnostic transport for `ProtocolDispatcher` using
26
- only Web-standard `Request`/`Response` — JSON-RPC handler (never throws),
27
- incremental `text/event-stream` SSE with error-frame + `AbortSignal`
28
- cancellation, listener→async-iterable notification bridge.
29
-
30
- Dependency-injected (no hidden globals), tenant fail-closed, zero framework
31
- lock-in. The durable rollout-store backend adapter is intentionally not
32
- included (a correct system-of-record needs a fail-loud datastore; pending a
33
- governance decision — documented in the README).
34
-
35
- - [`4865ac9`](https://github.com/Nebutra/Nebutra-Sailor/commit/4865ac9bff6dd83635bc33e4aeb348b087dee7c7) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the IDE-coding-agent delta (over the already-absorbed CLI core).
36
- - `code-review`: a pure unified-diff parser, branch-base resolution, a
37
- confidence-banded review prompt (CRITICAL ≥95% / WARNING ≥85% /
38
- SUGGESTION ≥75% / below → omit) with an untrusted-content injection guard
39
- over diff + commit messages, an advisory/no-edit invariant, a fail-closed
40
- finding parser (`ReviewParseError` rather than a fabricated empty result),
41
- and a deterministic post-review mode handoff.
42
- - `context-compaction`: in-session, mid-turn context-overflow recovery — a
43
- 1.3× token-correction estimator, a 60%-floored budget, greedy whole-message
44
- packing with an oversized-message clipped-transcript fallback,
45
- bounded-concurrency chunk summarization with a preserve-rubric prompt, a
46
- recursive binary reduce capped at an output token limit, and a fail-closed
47
- `"compact"` retry sentinel (never silently drops context).
48
- - `commit-message` (WRAP): a Conventional-Commits prompt + git-context shape +
49
- "regenerate materially different" negative constraint + fence/quote
50
- stripping + bounded retry over the existing injected small-model seam.
51
-
52
- Tenant-scoped / fail-closed where stateful; pure where stateless; the only
53
- impurity is an injected model/summarize port. 632 package tests.
54
-
55
- - [`67f9cd0`](https://github.com/Nebutra/Nebutra-Sailor/commit/67f9cd02c0ee908f0579efadc4292d535a758c16) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Durable rollout-store backend (the agent-runtime system-of-record).
56
- - New Prisma model `AgentRolloutLine` → table `agent_rollout_lines`, additive
57
- migration `20260519000000_add_agent_rollout_store` (one CREATE TABLE).
58
- - `createPrismaRolloutPersistence` in `@nebutra/agent-runtime-adapters`:
59
- fail-loud `RolloutPersistencePort` over a minimal injected
60
- `PrismaRolloutDelegate` (no generated-client / `@nebutra/db` dependency;
61
- per-tenant resolver for RLS). 6 TDD tests.
62
- - Gateway route store is env-gated: `AGENT_ROLLOUT_DURABLE=1` selects the
63
- durable Postgres store, default stays in-memory. Activation is a standard
64
- migrate+generate deploy step (ADR 2026-05-19), not faked durability.
65
-
66
- - [`bb1b126`](https://github.com/Nebutra/Nebutra-Sailor/commit/bb1b126e82379d88da10f43a86d9d07bccc3a224) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the git-backed-project delta (over the already-absorbed core).
67
- - `project-repo`: durable project = git repo. Metadata + per-conversation
68
- jsonl logs persisted as committed files (one commit per turn); history /
69
- restore by commit SHA (commit graph IS the history — distinct from
70
- workbench's in-memory snapshot ring); injected `GitHostPort` (no git
71
- vendor lock-in); tenant ownership gate (`ownsRepo`, default-deny).
72
- - `deployment-status`: commit-keyed lifecycle (`idle|deploying|live|
73
- failed`), latest-status + timeline-from-commits derivations, explicit
74
- `advanceState` machine; preview domain from an injected suffix (no
75
- hardcoded host).
76
-
77
- Tenant-scoped & fail-closed; pure data/logic; git + deploy hosts injected.
78
- Resolved a `CommitRef` barrel collision (deployment-status structural type
79
- renamed `DeploymentCommitRef`). 252 package tests.
80
-
81
- - [`da6bfea`](https://github.com/Nebutra/Nebutra-Sailor/commit/da6bfeaf6c323a9aecefdd65c481a9852aee25b9) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Add `@nebutra/agent-runtime`: a multi-tenant agent-runtime grammar.
82
- - New package re-expresses a coding-agent runtime _design_ in Sailor grammar —
83
- thread/turn/item model + event lifecycle, two-axis approval/capability
84
- policy, uniform tool/MCP abstraction, event-sourced rollout with compaction,
85
- and an external-sandbox delegation seam.
86
- - Every serialization scope, store key, and dispatch is tenant-scoped;
87
- cross-tenant requests can never share a serial lane.
88
- - No infra change and no in-process untrusted-code execution: the default
89
- executor fails closed; real execution is delegated behind `ExternalSandbox`.
90
- - Adds an off-by-default `agent-runtime-demo` feature flag gating a demo route
91
- in `apps/web`.
92
-
93
- - [`3c5c43e`](https://github.com/Nebutra/Nebutra-Sailor/commit/3c5c43e1c07c03da936d2948408f8abb4b113321) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the differentiated coding-agent-harness capabilities (the delta over
94
- the already-absorbed core).
95
- - `definitions`: shared layered-tier resolver (precedence + dual
96
- availability/enabled gate) + frontmatter parser — backs commands, skills,
97
- subagents from one kernel.
98
- - `skills`: two-phase progressive disclosure (token-budget listing,
99
- first-party never-truncated, path-activation), lazy body expansion,
100
- per-skill model/tool override merge.
101
- - `hook-pipeline`: 18-event taxonomy, config matcher/`if` resolver,
102
- structured decision protocol, multi-transport (function/http/prompt),
103
- parallel fan-out with deterministic precedence merge; wraps the bare
104
- `ToolHooks` as the in-process transport.
105
- - `commands`: unified command=skill model with dual user/model front-doors
106
- and a pure arg-substitution + templating expander.
107
- - `subagents`: definition resolver (tool allow−deny), fork-vs-spawn
108
- context-boundary contract, typed terminal-envelope result contract, and a
109
- tenant-scoped Task lifecycle registry.
110
-
111
- All tenant-scoped & fail-closed; pure data/logic (no FS/shell/TUI — those
112
- transports deliberately dropped for multi-tenant safety). 135 package tests.
113
-
114
- - [`a29b2d9`](https://github.com/Nebutra/Nebutra-Sailor/commit/a29b2d997416edc4e421b31b777724ae70e4da54) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Add the agent loop runner — the turn engine.
115
- - `runTurn()` drives a turn `loop { model_call → emit items → execute tools
116
- → feed results back }` until the model stops requesting tools or a bounded
117
- step ceiling is hit. Single-threaded (shared context, no conflicting
118
- sub-agent decisions).
119
- - The model call is abstracted behind `ModelInvoker`, WRAPping an existing
120
- model stack rather than re-porting provider/routing/fallback.
121
- - Every terminal item and the turn outcome are appended to the tenant-scoped
122
- rollout (resumable by replay); tool calls pass the approval gate
123
- (`resolveRuleDecision` + server-initiated `ApprovalGate`); unapproved tools
124
- are never dispatched. Internal failures surface as `turn.failed`, never
125
- thrown to the caller.
126
-
127
- - [`cd34724`](https://github.com/Nebutra/Nebutra-Sailor/commit/cd347242ab6874931a74aa72aa9a0d183f7dd5d3) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the multi-channel-gateway delta (over the already-absorbed core).
128
- - `channel-gateway`: channel-agnostic `InboundMessage`/`OutboundMessage`
129
- normalization, a `ChannelAdapter` port (parseInbound returns null for
130
- non-message events, never throws on junk), tenant-scoped `ChannelRegistry`,
131
- and outbound route-back through the originating channel.
132
- - `inbound-admission`: deterministic tenant-prefixed `resolveSessionKey`
133
- (group/DM/thread bindings, cross-tenant non-colliding), allowlist gating
134
- (exact / `*` / `prefix:*`, closed-by-default), mention gating (group
135
- requires @mention or reply-to-assistant; DM never), a time-injected
136
- `InboundDebouncer`, composed by `admitInbound`.
137
-
138
- Tenant-scoped & fail-closed; pure data/logic; transport + time injected.
139
- Only the clean contracts were re-expressed (the messy source impl is not
140
- ported). 365 package tests.
141
-
142
- - [`447e948`](https://github.com/Nebutra/Nebutra-Sailor/commit/447e948a7153446e7bc3769395c1687c9f27c991) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Close the runtime gap — four capabilities, all tenant-scoped & fail-closed,
143
- each behind an injectable port so the package keeps zero datastore/queue
144
- dependency ("no infra change" stays honest):
145
- - **Durable / resumable turn** (`durable-turn.ts`): `createDurableTurn` wraps
146
- `runTurn` behind a `DurableTurnQueuePort`; `resume` replays the rollout and
147
- re-drives an unfinished turn, idempotent and cross-tenant isolated.
148
- - **Protocol dispatcher** (`dispatcher.ts`): transport-agnostic JSON-RPC
149
- dispatcher serving the method registry — envelope validation fail-closed,
150
- per-`scopeKey` serialization (cross-tenant never shares a lane), errors
151
- mapped to JSON-RPC codes, validated notification stream.
152
- - **MCP activation** (`mcp-bridge.ts`): `activateMcpTools` adapts tenant/plan
153
- -visible MCP-server tools into the uniform `ToolRegistry` via an injectable
154
- `McpServerCatalogPort`; duplicate names skipped, empty tenant fails closed.
155
- - **Persistent rollout store** (`rollout-store-persistent.ts`):
156
- `PersistentRolloutStore` over a `RolloutPersistencePort` (satisfiable by an
157
- `@nebutra/audit`/`@nebutra/db` adapter) — monotonic per-(tenant,thread)
158
- seq, faithful round-trip, cross-tenant isolation, typed round-trip errors.
159
-
160
- - [`b1b9b72`](https://github.com/Nebutra/Nebutra-Sailor/commit/b1b9b7251ef6b1b84e5211dd9b4ae14a1be4b05a) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the self-improving-loop delta (over the already-absorbed core).
161
- - `memory-provider`: pluggable cross-session `MemoryProvider` port (full
162
- lifecycle: initialize/prefetch/syncTurn/onSessionEnd/onSessionSwitch/
163
- onPreCompress/onDelegation/systemPromptBlock) + a `MemoryManager` that
164
- degrades safely + memory-context injection defense (recalled text
165
- banner-wrapped as reference-not-instructions; a split-safe streaming
166
- scrubber strips model-forged memory banners). Vendor backends injected.
167
- - `skill-distillation`: the learning loop — eligibility gate (only
168
- non-trivial successful trajectories), `distillSkill` (deterministic
169
- prompt → injected synthesize → skill record with `allowedTools` clamped
170
- to tools the experience actually used = least privilege), `improveSkill`
171
- (versioned, tenant-locked), pure `shouldNudgePersist` heuristic.
172
-
173
- Tenant-scoped & fail-closed; pure data/logic; LLM synthesis + memory
174
- backends are injected ports. 288 package tests.
175
-
176
- - [`5ed8c38`](https://github.com/Nebutra/Nebutra-Sailor/commit/5ed8c38362e86bad51786383b81cc921b11d1b18) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the coding-agent share/permission delta (over the already-absorbed core).
177
- - `permission-ruleset`: `Rule{permission,pattern,action:allow|deny|ask}` +
178
- `evaluate(...rulesets)` first-match over both dimensions with fail-safe
179
- `ask` default, a `wildcardMatch` with the faithful trailing `" *"`-optional
180
- rule (no catastrophic backtracking), and a bash-command-prefix/arity
181
- extractor (`commandPrefix`/`commandPermissionKey`).
182
- - `session-share`: mint a read-only `ShareRecord{id,url,secret}` for a
183
- session, revoke it, public secret-gated `verifyViewer` (no tenantId —
184
- constant-time compare; documented threat model). Injected id-mint /
185
- url-builder / sync-sink / store ports; kill-switch; persist-first
186
- best-effort sync.
187
-
188
- Owner plane tenant-scoped & fail-closed; viewer plane secret-gated; pure
189
- where stateless; transport/crypto/store injected. 496 package tests.
190
-
191
- - [`77c0105`](https://github.com/Nebutra/Nebutra-Sailor/commit/77c0105ce89a4ff0f5e86229cce9c2561c1b1c5f) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the site-clone / targeted-edit delta (over the already-absorbed core).
192
- - `design-context`: website → structured generation seed
193
- `{content, brand{colors,fonts}, screenshot, title}` — pure normalizer +
194
- injected `ScrapeProvider` port (no network/provider lock-in),
195
- `toGenerationSeed` bounded deterministic prompt block.
196
- - `edit-planner`: pattern-driven `analyzeEditIntent` (7 EditTypes) +
197
- `selectFilesForEdit` (primary/context split + deterministic system-prompt
198
- builder) + generalised fast-apply (`parseEditBlocks` / `applyEditBlock`
199
- with an injected merger). Targeted edit planning vs regenerate-all.
200
-
201
- Tenant-scoped & fail-closed; pure data/logic; scrape + LLM-merge are
202
- injected ports. 219 package tests.
203
-
204
- - [`1e2d929`](https://github.com/Nebutra/Nebutra-Sailor/commit/1e2d9298f4f093cbe81199e7dcd3991116d01ea2) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the terminal-suggestion delta (over the already-absorbed core).
205
- - `fuzzy-match`: smart-case subsequence fuzzy match returning
206
- `{score, indices}` (lowercase query ⇒ case-insensitive; any uppercase ⇒
207
- case-sensitive), case-insensitive + ignore-spaces variants (indices map to
208
- original text), anchored glob-wildcard (`* ? [a-z] \`, no catastrophic
209
- backtracking), stable `rankByFuzzy`. Pure, dependency-free.
210
- - `command-suggestions`: `classifyMatch` with deterministic
211
- exact > prefix > fuzzy score banding, `rankSuggestions` (history-boost +
212
- shorter-text + stable tie-breaks), `dedupeByText` (history-wins,
213
- case-insensitive), tenant-scoped `SuggestionHistoryStore` (recency ring,
214
- cross-tenant isolation). Fuzzy matcher injected.
215
-
216
- Tenant-scoped & fail-closed where stateful; pure data/logic. The source's
217
- generated `command-signatures-v2` JS bundle was deliberately not ported.
218
- 426 package tests.
219
-
220
- - [`509338e`](https://github.com/Nebutra/Nebutra-Sailor/commit/509338ee5ffc537c108f06cd1fc24a811a5e05b8) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Concrete Track-B coupling for the agent-runtime external-sandbox seam.
221
- - Add `createHttpSandbox(baseUrl)`: an `ExternalSandbox` that delegates
222
- execution over HTTP to the decoupled Rust isolator
223
- (`backends/rust/sandbox`, `POST /api/v1/sandbox/exec`).
224
- - Non-2xx isolator responses (e.g. a fail-closed 403 refusal) surface as
225
- `SandboxDelegationError` and are never coerced into a fabricated result.
226
- - The Rust isolator now mirrors the `SandboxExecRequest` /
227
- `SandboxExecResult` / `CapabilityPolicy` wire shapes and is fail-closed
228
- until a real isolation backend (Wasmtime/Firecracker) is wired.
229
-
230
- - [`efe764d`](https://github.com/Nebutra/Nebutra-Sailor/commit/efe764d60085352eae304dc37dc22fda8920e587) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Absorb the web-app-builder delta (the differentiated capabilities over the
231
- already-absorbed harness core).
232
- - `artifact-stream`: streaming artifact/action protocol — a chunk-fed
233
- incremental parser for an `<artifact>` of ordered
234
- `<action type=file|shell|start|build|data>` blocks (split-tag safe, no
235
- duplicate emit), plus an action runner with a
236
- `pending→running→complete|aborted|failed` state-machine, submission-order
237
- queue, halt-on-failure, and execution delegated through injected ports
238
- (no in-process exec / host FS). Distinct from tool-calling.
239
- - `workbench`: tenant-scoped project-state model — immutable FileMap with
240
- auto-derived folders, apply-file-mutation, snapshot/restore with
241
- deep-copy isolation, bounded snapshot history, project diff; behind a
242
- `ProjectPersistencePort` (in-memory ref impl mirroring the rollout-store
243
- seam).
244
-
245
- All tenant-scoped & fail-closed, pure data/logic. 181 package tests.
246
-
247
- ### Patch Changes
248
-
249
- - Updated dependencies [[`5d3d7e6`](https://github.com/Nebutra/Nebutra-Sailor/commit/5d3d7e6c59cae5aa242bb988b75a9888cfd0db39)]:
250
- - @nebutra/mcp@0.1.1
@@ -1,90 +0,0 @@
1
- // src/orchestration.ts
2
- function fail(message, suggestion) {
3
- throw new Error(`${message}. Suggestion: ${suggestion}`);
4
- }
5
- function validateBrief(brief) {
6
- if (!brief.id.trim()) fail("brief.id is required", "Use a stable role or task id.");
7
- if (!brief.objective.trim()) {
8
- fail("brief.objective is required", "Write a concrete objective before dispatching.");
9
- }
10
- if (brief.allowedTools.length === 0) {
11
- fail("brief.allowedTools is required", "Constrain each subagent to an explicit tool scope.");
12
- }
13
- if (brief.boundaries.length === 0) {
14
- fail("brief.boundaries is required", "State at least one boundary for the worker.");
15
- }
16
- if (brief.budget.tokenLimit <= 0 || brief.budget.durationMs <= 0 || brief.budget.costUsd < 0) {
17
- fail("brief.budget is invalid", "Set positive token/time caps and a non-negative cost cap.");
18
- }
19
- }
20
- function dependencySet(briefs) {
21
- const dependencies = /* @__PURE__ */ new Set();
22
- for (const brief of briefs) {
23
- for (const dep of brief.dependsOn ?? []) dependencies.add(dep);
24
- }
25
- return dependencies;
26
- }
27
- function topologicalOrder(briefs) {
28
- const byId = new Map(briefs.map((brief) => [brief.id, brief]));
29
- const visited = /* @__PURE__ */ new Set();
30
- const visiting = /* @__PURE__ */ new Set();
31
- const ordered = [];
32
- const visit = (brief) => {
33
- if (visited.has(brief.id)) return;
34
- if (visiting.has(brief.id)) {
35
- fail(
36
- `subagent dependency cycle includes '${brief.id}'`,
37
- "Remove the cycle or collapse the dependent work into one sequential brief."
38
- );
39
- }
40
- visiting.add(brief.id);
41
- for (const dep of brief.dependsOn ?? []) {
42
- const dependency = byId.get(dep);
43
- if (dependency) visit(dependency);
44
- }
45
- visiting.delete(brief.id);
46
- visited.add(brief.id);
47
- ordered.push(brief);
48
- };
49
- for (const brief of briefs) visit(brief);
50
- return ordered;
51
- }
52
- function planSubagentDispatch(briefs, options = {}) {
53
- if (briefs.length === 0) {
54
- fail("at least one brief is required", "Create a concrete worker brief before dispatching.");
55
- }
56
- for (const brief of briefs) validateBrief(brief);
57
- const dependencies = dependencySet(briefs);
58
- if ((options.strategy ?? "auto") === "fanout" && dependencies.size > 0) {
59
- fail(
60
- "fan-out cannot run briefs that depend on each other",
61
- "Use sequential dispatch for dependent work or split independent briefs only."
62
- );
63
- }
64
- const order = topologicalOrder(briefs);
65
- const strategy = options.strategy === "fanout" || options.strategy === "auto" && dependencies.size === 0 ? "fanout" : "sequential";
66
- return { strategy, order };
67
- }
68
- async function fanOutSubagents(briefs, run) {
69
- const plan = planSubagentDispatch(briefs, { strategy: "fanout" });
70
- return Promise.all(plan.order.map((brief) => run(brief)));
71
- }
72
- function costReport(results) {
73
- return {
74
- totalInputTokens: results.reduce((sum, result) => sum + result.usage.inputTokens, 0),
75
- totalOutputTokens: results.reduce((sum, result) => sum + result.usage.outputTokens, 0),
76
- totalReasoningTokens: results.reduce(
77
- (sum, result) => sum + result.usage.reasoningOutputTokens,
78
- 0
79
- ),
80
- maxDurationMs: results.reduce((max, result) => Math.max(max, result.durationMs), 0),
81
- subagents: results.length
82
- };
83
- }
84
-
85
- export {
86
- planSubagentDispatch,
87
- fanOutSubagents,
88
- costReport
89
- };
90
- //# sourceMappingURL=chunk-MX2WL43P.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/orchestration.ts"],"sourcesContent":["import type { TurnUsage } from \"./model\";\n\nexport interface BudgetCap {\n readonly durationMs: number;\n readonly costUsd: number;\n readonly tokenLimit: number;\n}\n\nexport interface Brief {\n readonly id: string;\n readonly objective: string;\n readonly outputFormat: Record<string, unknown>;\n readonly allowedTools: readonly string[];\n readonly contextRefs: readonly string[];\n readonly boundaries: readonly string[];\n readonly budget: BudgetCap;\n readonly dependsOn?: readonly string[];\n}\n\nexport type DispatchStrategy = \"auto\" | \"sequential\" | \"fanout\";\n\nexport interface DispatchPlan {\n readonly strategy: Exclude<DispatchStrategy, \"auto\">;\n readonly order: readonly Brief[];\n}\n\nexport interface DispatchPlanOptions {\n readonly strategy?: DispatchStrategy;\n}\n\nexport interface SubagentResult {\n readonly briefId: string;\n readonly output: unknown;\n readonly usage: TurnUsage;\n readonly durationMs: number;\n}\n\nexport interface SubagentCostReport {\n readonly totalInputTokens: number;\n readonly totalOutputTokens: number;\n readonly totalReasoningTokens: number;\n readonly maxDurationMs: number;\n readonly subagents: number;\n}\n\nfunction fail(message: string, suggestion: string): never {\n throw new Error(`${message}. Suggestion: ${suggestion}`);\n}\n\nfunction validateBrief(brief: Brief): void {\n if (!brief.id.trim()) fail(\"brief.id is required\", \"Use a stable role or task id.\");\n if (!brief.objective.trim()) {\n fail(\"brief.objective is required\", \"Write a concrete objective before dispatching.\");\n }\n if (brief.allowedTools.length === 0) {\n fail(\"brief.allowedTools is required\", \"Constrain each subagent to an explicit tool scope.\");\n }\n if (brief.boundaries.length === 0) {\n fail(\"brief.boundaries is required\", \"State at least one boundary for the worker.\");\n }\n if (brief.budget.tokenLimit <= 0 || brief.budget.durationMs <= 0 || brief.budget.costUsd < 0) {\n fail(\"brief.budget is invalid\", \"Set positive token/time caps and a non-negative cost cap.\");\n }\n}\n\nfunction dependencySet(briefs: readonly Brief[]): Set<string> {\n const dependencies = new Set<string>();\n for (const brief of briefs) {\n for (const dep of brief.dependsOn ?? []) dependencies.add(dep);\n }\n return dependencies;\n}\n\nfunction topologicalOrder(briefs: readonly Brief[]): Brief[] {\n const byId = new Map(briefs.map((brief) => [brief.id, brief]));\n const visited = new Set<string>();\n const visiting = new Set<string>();\n const ordered: Brief[] = [];\n\n const visit = (brief: Brief): void => {\n if (visited.has(brief.id)) return;\n if (visiting.has(brief.id)) {\n fail(\n `subagent dependency cycle includes '${brief.id}'`,\n \"Remove the cycle or collapse the dependent work into one sequential brief.\",\n );\n }\n visiting.add(brief.id);\n for (const dep of brief.dependsOn ?? []) {\n const dependency = byId.get(dep);\n if (dependency) visit(dependency);\n }\n visiting.delete(brief.id);\n visited.add(brief.id);\n ordered.push(brief);\n };\n\n for (const brief of briefs) visit(brief);\n return ordered;\n}\n\nexport function planSubagentDispatch(\n briefs: readonly Brief[],\n options: DispatchPlanOptions = {},\n): DispatchPlan {\n if (briefs.length === 0) {\n fail(\"at least one brief is required\", \"Create a concrete worker brief before dispatching.\");\n }\n for (const brief of briefs) validateBrief(brief);\n\n const dependencies = dependencySet(briefs);\n if ((options.strategy ?? \"auto\") === \"fanout\" && dependencies.size > 0) {\n fail(\n \"fan-out cannot run briefs that depend on each other\",\n \"Use sequential dispatch for dependent work or split independent briefs only.\",\n );\n }\n\n const order = topologicalOrder(briefs);\n const strategy =\n options.strategy === \"fanout\" || (options.strategy === \"auto\" && dependencies.size === 0)\n ? \"fanout\"\n : \"sequential\";\n return { strategy, order };\n}\n\nexport async function fanOutSubagents(\n briefs: readonly Brief[],\n run: (brief: Brief) => Promise<SubagentResult>,\n): Promise<readonly SubagentResult[]> {\n const plan = planSubagentDispatch(briefs, { strategy: \"fanout\" });\n return Promise.all(plan.order.map((brief) => run(brief)));\n}\n\nexport function costReport(results: readonly SubagentResult[]): SubagentCostReport {\n return {\n totalInputTokens: results.reduce((sum, result) => sum + result.usage.inputTokens, 0),\n totalOutputTokens: results.reduce((sum, result) => sum + result.usage.outputTokens, 0),\n totalReasoningTokens: results.reduce(\n (sum, result) => sum + result.usage.reasoningOutputTokens,\n 0,\n ),\n maxDurationMs: results.reduce((max, result) => Math.max(max, result.durationMs), 0),\n subagents: results.length,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,35 +0,0 @@
1
- import { z } from "zod";
2
- import { InMemoryRolloutStore, Pulsar, RuntimeToolRegistry } from "../src";
3
-
4
- const tools = new RuntimeToolRegistry();
5
- tools.register(
6
- { name: "echo", description: "Echo a value", inputSchema: z.object({ value: z.string() }) },
7
- async (input: { value: string }) => input.value,
8
- );
9
-
10
- const pulsar = Pulsar.builder()
11
- .withTenant("local")
12
- .withConfig({
13
- model: "local",
14
- provider: "injected",
15
- approvalPolicy: "on_request",
16
- capabilityPolicy: "external_sandbox",
17
- })
18
- .withModel({
19
- async invoke() {
20
- return { emissions: [{ kind: "text", text: "hello from Pulsar" }] };
21
- },
22
- })
23
- .withTools(tools)
24
- .withRolloutStore(new InMemoryRolloutStore())
25
- .withApprovalGate({
26
- async request() {
27
- return { kind: "approved" };
28
- },
29
- })
30
- .build();
31
-
32
- const thread = await pulsar.startPlay("hello_play", "Say hello");
33
- for await (const event of thread.subscribe()) {
34
- process.stdout.write(`${JSON.stringify(event)}\n`);
35
- }
@@ -1,45 +0,0 @@
1
- import { InMemoryRolloutStore, Pulsar, RuntimeToolRegistry } from "../src";
2
-
3
- const eventLog = {
4
- async commit() {
5
- return "event_1";
6
- },
7
- async branchFrom(id: string, name: string) {
8
- return { name, from: id, at: new Date().toISOString() };
9
- },
10
- };
11
-
12
- const pulsar = Pulsar.builder()
13
- .withTenant("local")
14
- .withConfig({
15
- model: "local",
16
- provider: "injected",
17
- approvalPolicy: "on_request",
18
- capabilityPolicy: "external_sandbox",
19
- })
20
- .withModel({
21
- async invoke() {
22
- return { emissions: [{ kind: "text", text: "branch me" }] };
23
- },
24
- })
25
- .withTools(new RuntimeToolRegistry())
26
- .withRolloutStore(new InMemoryRolloutStore())
27
- .withApprovalGate({
28
- async request() {
29
- return { kind: "approved" };
30
- },
31
- })
32
- .withEventLog(eventLog)
33
- .build();
34
-
35
- const thread = await pulsar.startPlay("branch_play", "Run once");
36
- let itemId = "";
37
- for await (const event of thread.subscribe()) {
38
- if (event.type === "item.completed") itemId = event.item.id;
39
- }
40
-
41
- const resumed = await pulsar.resume(thread.id);
42
- process.stdout.write(`${JSON.stringify({ resumed: resumed.id })}\n`);
43
- process.stdout.write(
44
- `${JSON.stringify(await pulsar.branchFromItem(thread.id, itemId, "variant"))}\n`,
45
- );
@@ -1,20 +0,0 @@
1
- import { type Brief, costReport, fanOutSubagents } from "../src";
2
-
3
- const brief = (id: string): Brief => ({
4
- id,
5
- objective: `produce ${id}`,
6
- outputFormat: { type: "object" },
7
- allowedTools: ["read"],
8
- contextRefs: [],
9
- boundaries: ["do not mutate parent state"],
10
- budget: { durationMs: 1_000, costUsd: 0.01, tokenLimit: 1_000 },
11
- });
12
-
13
- const results = await fanOutSubagents([brief("visual"), brief("copy")], async (item) => ({
14
- briefId: item.id,
15
- output: { ok: true },
16
- usage: { inputTokens: 10, cachedInputTokens: 0, outputTokens: 2, reasoningOutputTokens: 0 },
17
- durationMs: 12,
18
- }));
19
-
20
- process.stdout.write(`${JSON.stringify(costReport(results))}\n`);