@nebutra/agent-runtime 0.2.0 → 0.2.1

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 (78) hide show
  1. package/.turbo/turbo-build.log +65 -50
  2. package/.turbo/turbo-test.log +39 -36
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/CHANGELOG.md +12 -15
  5. package/README.md +2 -0
  6. package/dist/adapters/dispatcher-sse.js +1 -0
  7. package/dist/adapters/index.js +1 -0
  8. package/dist/adapters/mcp-catalog.js +1 -0
  9. package/dist/adapters/prisma-rollout.js +1 -0
  10. package/dist/chunk-424PT5DM.js +23 -0
  11. package/dist/chunk-424PT5DM.js.map +1 -0
  12. package/dist/{chunk-NN7DATXA.js → chunk-4Y25ZTKI.js} +3 -3
  13. package/dist/chunk-4Y25ZTKI.js.map +1 -0
  14. package/dist/{chunk-BJBBR3QA.js → chunk-D4YAPLOW.js} +4 -4
  15. package/dist/chunk-D4YAPLOW.js.map +1 -0
  16. package/dist/{chunk-ZMYX5VBU.js → chunk-GQZKYWFT.js} +23 -7
  17. package/dist/chunk-GQZKYWFT.js.map +1 -0
  18. package/dist/chunk-KCNN4QUQ.js +255 -0
  19. package/dist/chunk-KCNN4QUQ.js.map +1 -0
  20. package/dist/chunk-MX2WL43P.js +90 -0
  21. package/dist/chunk-MX2WL43P.js.map +1 -0
  22. package/dist/{chunk-PGGWSUTM.js → chunk-NI4EDT4T.js} +2 -2
  23. package/dist/chunk-NI4EDT4T.js.map +1 -0
  24. package/dist/{chunk-5N4644PB.js → chunk-SD2ZJ7XG.js} +4 -4
  25. package/dist/cli.d.ts +2 -0
  26. package/dist/cli.js +52 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/commands.js +1 -0
  29. package/dist/definitions.js +1 -0
  30. package/dist/dispatcher.js +1 -0
  31. package/dist/durable-turn.js +3 -2
  32. package/dist/hook-pipeline.js +1 -0
  33. package/dist/index.d.ts +8 -85
  34. package/dist/index.js +237 -119
  35. package/dist/index.js.map +1 -1
  36. package/dist/loop.d.ts +2 -2
  37. package/dist/loop.js +3 -2
  38. package/dist/mcp-bridge.d.ts +3 -3
  39. package/dist/mcp-bridge.js +3 -2
  40. package/dist/model.js +1 -0
  41. package/dist/orchestration.d.ts +43 -0
  42. package/dist/orchestration.js +12 -0
  43. package/dist/orchestration.js.map +1 -0
  44. package/dist/policy.js +1 -0
  45. package/dist/protocol.js +1 -0
  46. package/dist/pulsar.d.ts +78 -0
  47. package/dist/pulsar.js +17 -0
  48. package/dist/pulsar.js.map +1 -0
  49. package/dist/rollout-store-persistent.js +1 -0
  50. package/dist/rollout.js +1 -0
  51. package/dist/sandbox.js +1 -0
  52. package/dist/skills.js +2 -1
  53. package/dist/subagents.js +1 -0
  54. package/dist/tools.d.ts +4 -3
  55. package/dist/tools.js +5 -3
  56. package/examples/pulsar-quickstart.ts +35 -0
  57. package/examples/resume-branch.ts +45 -0
  58. package/examples/subagent-fanout.ts +20 -0
  59. package/package.json +12 -5
  60. package/src/cli.ts +49 -0
  61. package/src/context-compaction.ts +6 -2
  62. package/src/index.ts +2 -0
  63. package/src/loop.ts +2 -2
  64. package/src/mcp-bridge.ts +8 -3
  65. package/src/orchestration.test.ts +53 -0
  66. package/src/orchestration.ts +146 -0
  67. package/src/permission-ruleset.ts +1 -200
  68. package/src/pulsar.test.ts +156 -0
  69. package/src/pulsar.ts +322 -0
  70. package/src/skills.ts +30 -8
  71. package/src/tools.test.ts +12 -0
  72. package/src/tools.ts +5 -2
  73. package/tsup.config.ts +4 -1
  74. package/dist/chunk-BJBBR3QA.js.map +0 -1
  75. package/dist/chunk-NN7DATXA.js.map +0 -1
  76. package/dist/chunk-PGGWSUTM.js.map +0 -1
  77. package/dist/chunk-ZMYX5VBU.js.map +0 -1
  78. /package/dist/{chunk-5N4644PB.js.map → chunk-SD2ZJ7XG.js.map} +0 -0
package/dist/loop.d.ts CHANGED
@@ -2,7 +2,7 @@ import { TurnConfig, TurnUsage, ThreadEvent } from './model.js';
2
2
  import { ReviewDecision, RuleDecision, ApprovalPolicy } from './policy.js';
3
3
  import { ServerRequest } from './protocol.js';
4
4
  import { RolloutStore } from './rollout.js';
5
- import { ToolRegistry } from './tools.js';
5
+ import { RuntimeToolRegistry } from './tools.js';
6
6
  import 'zod';
7
7
 
8
8
  /**
@@ -60,7 +60,7 @@ interface RunTurnDeps {
60
60
  readonly config: TurnConfig;
61
61
  readonly approvalPolicy: ApprovalPolicy;
62
62
  readonly model: ModelInvoker;
63
- readonly tools: ToolRegistry;
63
+ readonly tools: RuntimeToolRegistry;
64
64
  readonly store: RolloutStore;
65
65
  readonly approvalGate: ApprovalGate;
66
66
  /** Defaults to: everything requires a prompt (safe). */
package/dist/loop.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  runTurn
3
- } from "./chunk-BJBBR3QA.js";
4
- import "./chunk-CGRCUKGT.js";
3
+ } from "./chunk-D4YAPLOW.js";
5
4
  import "./chunk-37BBB2P2.js";
5
+ import "./chunk-CGRCUKGT.js";
6
+ import "./chunk-424PT5DM.js";
6
7
  export {
7
8
  runTurn
8
9
  };
@@ -1,4 +1,4 @@
1
- import { ToolDefinition, ToolRegistry, McpClientLike } from './tools.js';
1
+ import { ToolDefinition, RuntimeToolRegistry, McpClientLike } from './tools.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -36,11 +36,11 @@ interface ActivateMcpToolsResult {
36
36
  }
37
37
  /**
38
38
  * List the tenant/plan-visible MCP tools, adapt each via {@link adaptMcpTool},
39
- * and register them into the {@link ToolRegistry}. A tool whose name is already
39
+ * and register them into the {@link RuntimeToolRegistry}. A tool whose name is already
40
40
  * registered is skipped (reported, never thrown). Empty/blank tenantId fails
41
41
  * closed before any catalog call.
42
42
  */
43
- declare function activateMcpTools(registry: ToolRegistry, catalog: McpServerCatalogPort, client: McpClientLike, ctx: {
43
+ declare function activateMcpTools(registry: RuntimeToolRegistry, catalog: McpServerCatalogPort, client: McpClientLike, ctx: {
44
44
  tenantId: string;
45
45
  plan?: string;
46
46
  }): Promise<ActivateMcpToolsResult>;
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  activateMcpTools
3
- } from "./chunk-PGGWSUTM.js";
4
- import "./chunk-NN7DATXA.js";
3
+ } from "./chunk-NI4EDT4T.js";
4
+ import "./chunk-4Y25ZTKI.js";
5
+ import "./chunk-424PT5DM.js";
5
6
  export {
6
7
  activateMcpTools
7
8
  };
package/dist/model.js CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  isTurnTerminal,
3
3
  mergeTurnConfig
4
4
  } from "./chunk-6EGG2OZC.js";
5
+ import "./chunk-424PT5DM.js";
5
6
  export {
6
7
  isTurnTerminal,
7
8
  mergeTurnConfig
@@ -0,0 +1,43 @@
1
+ import { TurnUsage } from './model.js';
2
+
3
+ interface BudgetCap {
4
+ readonly durationMs: number;
5
+ readonly costUsd: number;
6
+ readonly tokenLimit: number;
7
+ }
8
+ interface Brief {
9
+ readonly id: string;
10
+ readonly objective: string;
11
+ readonly outputFormat: Record<string, unknown>;
12
+ readonly allowedTools: readonly string[];
13
+ readonly contextRefs: readonly string[];
14
+ readonly boundaries: readonly string[];
15
+ readonly budget: BudgetCap;
16
+ readonly dependsOn?: readonly string[];
17
+ }
18
+ type DispatchStrategy = "auto" | "sequential" | "fanout";
19
+ interface DispatchPlan {
20
+ readonly strategy: Exclude<DispatchStrategy, "auto">;
21
+ readonly order: readonly Brief[];
22
+ }
23
+ interface DispatchPlanOptions {
24
+ readonly strategy?: DispatchStrategy;
25
+ }
26
+ interface SubagentResult {
27
+ readonly briefId: string;
28
+ readonly output: unknown;
29
+ readonly usage: TurnUsage;
30
+ readonly durationMs: number;
31
+ }
32
+ interface SubagentCostReport {
33
+ readonly totalInputTokens: number;
34
+ readonly totalOutputTokens: number;
35
+ readonly totalReasoningTokens: number;
36
+ readonly maxDurationMs: number;
37
+ readonly subagents: number;
38
+ }
39
+ declare function planSubagentDispatch(briefs: readonly Brief[], options?: DispatchPlanOptions): DispatchPlan;
40
+ declare function fanOutSubagents(briefs: readonly Brief[], run: (brief: Brief) => Promise<SubagentResult>): Promise<readonly SubagentResult[]>;
41
+ declare function costReport(results: readonly SubagentResult[]): SubagentCostReport;
42
+
43
+ export { type Brief, type BudgetCap, type DispatchPlan, type DispatchPlanOptions, type DispatchStrategy, type SubagentCostReport, type SubagentResult, costReport, fanOutSubagents, planSubagentDispatch };
@@ -0,0 +1,12 @@
1
+ import {
2
+ costReport,
3
+ fanOutSubagents,
4
+ planSubagentDispatch
5
+ } from "./chunk-MX2WL43P.js";
6
+ import "./chunk-424PT5DM.js";
7
+ export {
8
+ costReport,
9
+ fanOutSubagents,
10
+ planSubagentDispatch
11
+ };
12
+ //# sourceMappingURL=orchestration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/policy.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  resolveRuleDecision,
10
10
  writableRootSchema
11
11
  } from "./chunk-CGRCUKGT.js";
12
+ import "./chunk-424PT5DM.js";
12
13
  export {
13
14
  DEFAULT_APPROVAL_POLICY,
14
15
  DEFAULT_CAPABILITY_POLICY,
package/dist/protocol.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  resolveScope,
6
6
  scopeKey
7
7
  } from "./chunk-FUG5DT2C.js";
8
+ import "./chunk-424PT5DM.js";
8
9
  export {
9
10
  METHOD_REGISTRY,
10
11
  NOTIFICATIONS,
@@ -0,0 +1,78 @@
1
+ import { ModelInvoker, ApprovalGate } from './loop.js';
2
+ import { TurnConfig, ThreadEvent } from './model.js';
3
+ import { ApprovalPolicy } from './policy.js';
4
+ import { RolloutStore } from './rollout.js';
5
+ import { RuntimeToolRegistry } from './tools.js';
6
+ import './protocol.js';
7
+ import 'zod';
8
+
9
+ type EventLogKind = "llm_call" | "tool_call" | "sub_agent_call" | "content_write";
10
+ interface PulsarEventInput {
11
+ readonly traceId: string;
12
+ readonly kind: EventLogKind;
13
+ readonly affected: readonly string[];
14
+ readonly parent: string | null;
15
+ readonly snapshot?: Record<string, string>;
16
+ }
17
+ interface PulsarBranchRecord {
18
+ readonly name: string;
19
+ readonly from: string;
20
+ readonly at: string;
21
+ }
22
+ interface PulsarEventLogPort {
23
+ commit(event: PulsarEventInput): Promise<string>;
24
+ branchFrom(id: string, name: string): Promise<PulsarBranchRecord>;
25
+ }
26
+ interface StartThreadOptions {
27
+ readonly threadId?: string;
28
+ readonly maxSteps?: number;
29
+ }
30
+ interface PulsarDeps {
31
+ readonly tenantId: string;
32
+ readonly config: TurnConfig;
33
+ readonly model: ModelInvoker;
34
+ readonly tools: RuntimeToolRegistry;
35
+ readonly store: RolloutStore;
36
+ readonly approvalGate: ApprovalGate;
37
+ readonly approvalPolicy: ApprovalPolicy;
38
+ readonly eventLog?: PulsarEventLogPort;
39
+ }
40
+ declare class PulsarThread {
41
+ #private;
42
+ readonly id: string;
43
+ readonly playId: string;
44
+ readonly tenantId: string;
45
+ constructor(input: {
46
+ readonly id: string;
47
+ readonly playId: string;
48
+ readonly tenantId: string;
49
+ readonly deps: PulsarDeps;
50
+ readonly mode: "start" | "resume";
51
+ readonly userInput: string;
52
+ readonly maxSteps?: number;
53
+ });
54
+ subscribe(): AsyncGenerator<ThreadEvent>;
55
+ }
56
+ declare class PulsarBuilder {
57
+ #private;
58
+ withTenant(tenantId: string): this;
59
+ withConfig(config: TurnConfig): this;
60
+ withModel(model: ModelInvoker): this;
61
+ withTools(tools: RuntimeToolRegistry): this;
62
+ withRolloutStore(store: RolloutStore): this;
63
+ withApprovalGate(approvalGate: ApprovalGate): this;
64
+ withApprovalPolicy(policy: ApprovalPolicy): this;
65
+ withEventLog(eventLog: PulsarEventLogPort): this;
66
+ build(): Pulsar;
67
+ }
68
+ declare class Pulsar {
69
+ #private;
70
+ static readonly itemCommitIds: Map<string, string>;
71
+ constructor(deps: PulsarDeps);
72
+ static builder(): PulsarBuilder;
73
+ startPlay(playId: string, input: string, options?: StartThreadOptions): Promise<PulsarThread>;
74
+ resume(threadId: string): Promise<PulsarThread>;
75
+ branchFromItem(threadId: string, itemId: string, name: string): Promise<PulsarBranchRecord>;
76
+ }
77
+
78
+ export { Pulsar, type PulsarBranchRecord, PulsarBuilder, type PulsarDeps, type PulsarEventInput, type PulsarEventLogPort, PulsarThread, type StartThreadOptions };
package/dist/pulsar.js ADDED
@@ -0,0 +1,17 @@
1
+ import {
2
+ Pulsar,
3
+ PulsarBuilder,
4
+ PulsarThread
5
+ } from "./chunk-KCNN4QUQ.js";
6
+ import "./chunk-D4YAPLOW.js";
7
+ import "./chunk-37BBB2P2.js";
8
+ import "./chunk-4Y25ZTKI.js";
9
+ import "./chunk-6EGG2OZC.js";
10
+ import "./chunk-CGRCUKGT.js";
11
+ import "./chunk-424PT5DM.js";
12
+ export {
13
+ Pulsar,
14
+ PulsarBuilder,
15
+ PulsarThread
16
+ };
17
+ //# sourceMappingURL=pulsar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -2,6 +2,7 @@ import {
2
2
  PersistentRolloutStore,
3
3
  RoundTripError
4
4
  } from "./chunk-2DA6Q6TN.js";
5
+ import "./chunk-424PT5DM.js";
5
6
  export {
6
7
  PersistentRolloutStore,
7
8
  RoundTripError
package/dist/rollout.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  replay,
6
6
  sanitizeForPersist
7
7
  } from "./chunk-37BBB2P2.js";
8
+ import "./chunk-424PT5DM.js";
8
9
  export {
9
10
  InMemoryRolloutStore,
10
11
  PERSISTED_OUTPUT_MAX_BYTES,
package/dist/sandbox.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  assertSafePosture,
6
6
  createHttpSandbox
7
7
  } from "./chunk-MUF7ZZTO.js";
8
+ import "./chunk-424PT5DM.js";
8
9
  export {
9
10
  NoExecutorConfiguredError,
10
11
  REFUSING_SANDBOX,
package/dist/skills.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  buildSkillListing,
3
3
  expandSkill
4
- } from "./chunk-ZMYX5VBU.js";
4
+ } from "./chunk-GQZKYWFT.js";
5
5
  import "./chunk-7BUOF367.js";
6
+ import "./chunk-424PT5DM.js";
6
7
  export {
7
8
  buildSkillListing,
8
9
  expandSkill
package/dist/subagents.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  resolveSubagentTools,
9
9
  settleDeferred
10
10
  } from "./chunk-5YS7WAPS.js";
11
+ import "./chunk-424PT5DM.js";
11
12
  export {
12
13
  TASK_KINDS,
13
14
  TASK_STATUSES,
package/dist/tools.d.ts CHANGED
@@ -46,16 +46,17 @@ interface ToolHooks {
46
46
  readonly postToolUse?: (name: string, output: unknown, ctx: ToolDispatchContext) => Promise<void>;
47
47
  }
48
48
  /**
49
- * registry -> router -> orchestrator pipeline. MCP tools register via
49
+ * runtime registry -> router -> orchestrator pipeline. MCP tools register via
50
50
  * {@link adaptMcpTool} and are dispatched identically to native tools.
51
51
  */
52
- declare class ToolRegistry {
52
+ declare class RuntimeToolRegistry {
53
53
  #private;
54
54
  constructor(hooks?: ToolHooks);
55
55
  register<I, O>(definition: ToolDefinition<I, O>, handler: ToolHandler<I, O>, origin?: ToolOrigin): void;
56
56
  list(): readonly RegisteredTool[];
57
57
  dispatch(name: string, rawInput: unknown, ctx: ToolDispatchContext): Promise<unknown>;
58
58
  }
59
+
59
60
  /** Minimal shape of an MCP client (satisfied by `@nebutra/mcp`'s `mcpClient`). */
60
61
  interface McpClientLike {
61
62
  executeTool(name: string, args: unknown, ctx: {
@@ -74,4 +75,4 @@ declare function adaptMcpTool(server: string, definition: ToolDefinition, client
74
75
  origin: ToolOrigin;
75
76
  };
76
77
 
77
- export { type McpClientLike, type RegisteredTool, type ToolDefinition, type ToolDispatchContext, type ToolHandler, type ToolHooks, type ToolOrigin, ToolRegistry, adaptMcpTool };
78
+ export { type McpClientLike, type RegisteredTool, RuntimeToolRegistry, type ToolDefinition, type ToolDispatchContext, type ToolHandler, type ToolHooks, type ToolOrigin, RuntimeToolRegistry as ToolRegistry, adaptMcpTool };
package/dist/tools.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import {
2
- ToolRegistry,
2
+ RuntimeToolRegistry,
3
3
  adaptMcpTool
4
- } from "./chunk-NN7DATXA.js";
4
+ } from "./chunk-4Y25ZTKI.js";
5
+ import "./chunk-424PT5DM.js";
5
6
  export {
6
- ToolRegistry,
7
+ RuntimeToolRegistry,
8
+ RuntimeToolRegistry as ToolRegistry,
7
9
  adaptMcpTool
8
10
  };
9
11
  //# sourceMappingURL=tools.js.map
@@ -0,0 +1,35 @@
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
+ }
@@ -0,0 +1,45 @@
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
+ );
@@ -0,0 +1,20 @@
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`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/agent-runtime",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,10 +14,12 @@
14
14
  "gaps": [
15
15
  "Track-B kernel sidecar transport not yet wired",
16
16
  "Durable-turn queue binding is interface-only",
17
- "Rollout store backed by in-memory reference impl only"
17
+ "Rollout store backed by in-memory reference impl only",
18
+ "Pulsar facade is runnable but still delegates model execution through injected ports"
18
19
  ],
19
20
  "featureId": "agent-runtime",
20
21
  "category": "ai",
22
+ "surface": "agent-runtime",
21
23
  "summary": "Multi-tenant agent-runtime grammar: thread/turn/item model, approval+capability policy, tool/MCP abstraction, event-sourced rollout, external-sandbox delegation seam"
22
24
  },
23
25
  "main": "./src/index.ts",
@@ -26,6 +28,8 @@
26
28
  ".": "./src/index.ts",
27
29
  "./model": "./src/model.ts",
28
30
  "./protocol": "./src/protocol.ts",
31
+ "./pulsar": "./src/pulsar.ts",
32
+ "./orchestration": "./src/orchestration.ts",
29
33
  "./policy": "./src/policy.ts",
30
34
  "./tools": "./src/tools.ts",
31
35
  "./rollout": "./src/rollout.ts",
@@ -36,10 +40,13 @@
36
40
  "./adapters/prisma-rollout": "./src/adapters/prisma-rollout.ts"
37
41
  },
38
42
  "dependencies": {
39
- "zod": "^4.3.6"
43
+ "zod": "^4.3.6",
44
+ "@nebutra/ai-primitives": "0.1.1",
45
+ "@nebutra/capability-kit": "0.2.1",
46
+ "@nebutra/execution-policy": "0.1.1"
40
47
  },
41
48
  "peerDependencies": {
42
- "@nebutra/mcp": "0.1.1"
49
+ "@nebutra/mcp": "0.1.2"
43
50
  },
44
51
  "peerDependenciesMeta": {
45
52
  "@nebutra/mcp": {
@@ -51,7 +58,7 @@
51
58
  "tsup": "^8.5.1",
52
59
  "typescript": "^5.9.3",
53
60
  "vitest": "^4.0.18",
54
- "@nebutra/mcp": "0.1.1"
61
+ "@nebutra/mcp": "0.1.2"
55
62
  },
56
63
  "homepage": "https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/ai/agent-runtime#readme",
57
64
  "repository": {
package/src/cli.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { readCapabilityDebug } from "@nebutra/capability-kit/debug";
2
+
3
+ const command = process.argv[2] ?? "doctor";
4
+
5
+ if (command === "doctor") {
6
+ process.stdout.write(
7
+ `${JSON.stringify(
8
+ {
9
+ capability: "agent-runtime",
10
+ ok: true,
11
+ checks: ["thread-turn-item", "rollout-store", "pulsar-facade", "subagent-briefs"],
12
+ },
13
+ null,
14
+ 2,
15
+ )}\n`,
16
+ );
17
+ } else if (command === "debug" || command === "replay") {
18
+ const threadId = process.argv[3];
19
+ process.stdout.write(
20
+ `${JSON.stringify(
21
+ {
22
+ capability: "agent-runtime",
23
+ threadId,
24
+ entries: await readCapabilityDebug("agent-runtime", { limit: 20 }),
25
+ suggestion:
26
+ "Use a RolloutStore-backed host to replay full thread events; this CLI reports local debug lines.",
27
+ },
28
+ null,
29
+ 2,
30
+ )}\n`,
31
+ );
32
+ } else if (command === "subagent-trace" || command === "subagent-cost") {
33
+ const threadId = process.argv[3];
34
+ process.stdout.write(
35
+ `${JSON.stringify(
36
+ {
37
+ capability: "agent-runtime",
38
+ command,
39
+ threadId,
40
+ entries: await readCapabilityDebug("agent-runtime", { limit: 20 }),
41
+ },
42
+ null,
43
+ 2,
44
+ )}\n`,
45
+ );
46
+ } else {
47
+ process.stderr.write(`Unknown agent-runtime command: ${command}\n`);
48
+ process.exitCode = 1;
49
+ }
@@ -73,6 +73,10 @@
73
73
  * is never mutated; every transformation builds new arrays/strings.
74
74
  */
75
75
 
76
+ import {
77
+ type BaseTokenEstimator,
78
+ estimateTokens as estimatePrimitiveTokens,
79
+ } from "@nebutra/ai-primitives";
76
80
  import { z } from "zod";
77
81
 
78
82
  /** Tokenizers undercount real BPE by ~15-30%; scale the base estimate up. */
@@ -134,7 +138,7 @@ export type Transcript = readonly Message[];
134
138
  export type Summarize = (prompt: string) => Promise<string>;
135
139
 
136
140
  /** Caller-injected cheap token heuristic; defaults to chars/4. */
137
- export type BaseEstimator = (text: string) => number;
141
+ export type BaseEstimator = BaseTokenEstimator;
138
142
 
139
143
  /** The eligibility signal this module keys on (also the failure sentinel). */
140
144
  export const COMPACT_SENTINEL = "compact";
@@ -170,7 +174,7 @@ const compactInputSchema = z.object({
170
174
  * from the model's server-side counting. Always rounds UP — see header.
171
175
  */
172
176
  export function estimateTokens(text: string, base: BaseEstimator = defaultBase): number {
173
- return Math.ceil(base(text) * TOKEN_CORRECTION);
177
+ return estimatePrimitiveTokens(text, { base, correction: TOKEN_CORRECTION });
174
178
  }
175
179
 
176
180
  /**
package/src/index.ts CHANGED
@@ -32,10 +32,12 @@ export * from "./loop";
32
32
  export * from "./mcp-bridge";
33
33
  export * from "./memory-provider";
34
34
  export * from "./model";
35
+ export * from "./orchestration";
35
36
  export * from "./permission-ruleset";
36
37
  export * from "./policy";
37
38
  export * from "./project-repo";
38
39
  export * from "./protocol";
40
+ export * from "./pulsar";
39
41
  export * from "./rollout";
40
42
  export * from "./rollout-store-persistent";
41
43
  export * from "./sandbox";
package/src/loop.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  } from "./policy";
26
26
  import type { ServerRequest } from "./protocol";
27
27
  import { type RolloutLine, type RolloutStore, sanitizeForPersist } from "./rollout";
28
- import type { ToolDispatchContext, ToolRegistry } from "./tools";
28
+ import type { RuntimeToolRegistry, ToolDispatchContext } from "./tools";
29
29
 
30
30
  /** A single thing the model emitted in one round. */
31
31
  export type ModelEmission =
@@ -68,7 +68,7 @@ export interface RunTurnDeps {
68
68
  readonly config: TurnConfig;
69
69
  readonly approvalPolicy: ApprovalPolicy;
70
70
  readonly model: ModelInvoker;
71
- readonly tools: ToolRegistry;
71
+ readonly tools: RuntimeToolRegistry;
72
72
  readonly store: RolloutStore;
73
73
  readonly approvalGate: ApprovalGate;
74
74
  /** Defaults to: everything requires a prompt (safe). */
package/src/mcp-bridge.ts CHANGED
@@ -13,7 +13,12 @@
13
13
 
14
14
  import { z } from "zod";
15
15
 
16
- import { adaptMcpTool, type McpClientLike, type ToolDefinition, type ToolRegistry } from "./tools";
16
+ import {
17
+ adaptMcpTool,
18
+ type McpClientLike,
19
+ type RuntimeToolRegistry,
20
+ type ToolDefinition,
21
+ } from "./tools";
17
22
 
18
23
  /**
19
24
  * Port over an MCP server catalog (satisfied by `@nebutra/mcp`'s
@@ -41,12 +46,12 @@ export interface ActivateMcpToolsResult {
41
46
 
42
47
  /**
43
48
  * List the tenant/plan-visible MCP tools, adapt each via {@link adaptMcpTool},
44
- * and register them into the {@link ToolRegistry}. A tool whose name is already
49
+ * and register them into the {@link RuntimeToolRegistry}. A tool whose name is already
45
50
  * registered is skipped (reported, never thrown). Empty/blank tenantId fails
46
51
  * closed before any catalog call.
47
52
  */
48
53
  export async function activateMcpTools(
49
- registry: ToolRegistry,
54
+ registry: RuntimeToolRegistry,
50
55
  catalog: McpServerCatalogPort,
51
56
  client: McpClientLike,
52
57
  ctx: { tenantId: string; plan?: string },