@deepstrike/sdk 0.2.70 → 0.2.72

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 (77) hide show
  1. package/README.md +57 -41
  2. package/dist/advanced/public.d.ts +24 -0
  3. package/dist/advanced/public.js +18 -0
  4. package/dist/agent-facade.d.ts +96 -0
  5. package/dist/agent-facade.js +317 -0
  6. package/dist/agent-ir.d.ts +11 -5
  7. package/dist/agent-ir.js +42 -26
  8. package/dist/canonical-prefix-allowlist.d.ts +6 -0
  9. package/dist/canonical-prefix-allowlist.js +30 -0
  10. package/dist/evals/public.d.ts +50 -0
  11. package/dist/evals/public.js +25 -0
  12. package/dist/guardrail.d.ts +4 -1
  13. package/dist/handoff-target.d.ts +2 -0
  14. package/dist/handoff-target.js +7 -1
  15. package/dist/index.d.ts +14 -30
  16. package/dist/index.js +7 -16
  17. package/dist/kernel.d.ts +2 -2
  18. package/dist/knowledge/public.d.ts +2 -0
  19. package/dist/knowledge/public.js +1 -1
  20. package/dist/knowledge/source.d.ts +7 -0
  21. package/dist/knowledge/source.js +20 -1
  22. package/dist/memory/protocols.d.ts +2 -2
  23. package/dist/projection-pairs.d.ts +43 -0
  24. package/dist/projection-pairs.js +9 -0
  25. package/dist/providers/anthropic-adapter.d.ts +2 -2
  26. package/dist/providers/anthropic.d.ts +4 -4
  27. package/dist/providers/base.d.ts +5 -5
  28. package/dist/providers/content-normalization.d.ts +4 -4
  29. package/dist/providers/gemini-adapter.d.ts +2 -2
  30. package/dist/providers/gemini.d.ts +3 -3
  31. package/dist/providers/ollama-adapter.d.ts +2 -2
  32. package/dist/providers/ollama.d.ts +2 -2
  33. package/dist/providers/openai-chat.d.ts +4 -4
  34. package/dist/providers/openai-responses-adapter.d.ts +2 -2
  35. package/dist/providers/openai-responses.d.ts +2 -2
  36. package/dist/providers/openai.d.ts +4 -4
  37. package/dist/providers/protocol-adapter.d.ts +2 -2
  38. package/dist/providers/protocol-capabilities.d.ts +1 -0
  39. package/dist/providers/protocol-capabilities.js +3 -0
  40. package/dist/providers/public.d.ts +4 -2
  41. package/dist/providers/public.js +2 -1
  42. package/dist/providers/replay-validator.d.ts +3 -3
  43. package/dist/runtime/archive.d.ts +7 -7
  44. package/dist/runtime/canonical-kernel-step.d.ts +2 -2
  45. package/dist/runtime/context-manager.d.ts +56 -0
  46. package/dist/runtime/context-manager.js +112 -0
  47. package/dist/runtime/eval.d.ts +2 -2
  48. package/dist/runtime/kernel-step.d.ts +5 -5
  49. package/dist/runtime/provider-replay.d.ts +2 -2
  50. package/dist/runtime/public.d.ts +22 -0
  51. package/dist/runtime/public.js +11 -0
  52. package/dist/runtime/replay-fixture.d.ts +3 -3
  53. package/dist/runtime/replay-fixture.js +1 -1
  54. package/dist/runtime/replay-provider.d.ts +4 -4
  55. package/dist/runtime/replay-provider.js +1 -1
  56. package/dist/runtime/runner.d.ts +17 -5
  57. package/dist/runtime/runner.js +110 -37
  58. package/dist/runtime/session-log.d.ts +1 -1
  59. package/dist/runtime/session-repair.d.ts +2 -2
  60. package/dist/runtime/workflow-control-flow.d.ts +1 -1
  61. package/dist/runtime/workflow-control-flow.js +16 -2
  62. package/dist/runtime-classification.d.ts +161 -0
  63. package/dist/runtime-classification.js +66 -0
  64. package/dist/runtime-language.d.ts +32 -0
  65. package/dist/runtime-language.js +51 -0
  66. package/dist/skill.d.ts +31 -5
  67. package/dist/types/agent.d.ts +17 -4
  68. package/dist/types.d.ts +22 -12
  69. package/dist/workflow/definition.d.ts +19 -0
  70. package/dist/workflow/definition.js +29 -0
  71. package/dist/workflow/public.d.ts +3 -1
  72. package/dist/workflow/public.js +1 -0
  73. package/package.json +23 -2
  74. package/dist/compat/anthropic/mcp.d.ts +0 -15
  75. package/dist/compat/anthropic/mcp.js +0 -10
  76. package/dist/compat/openai/agent.d.ts +0 -34
  77. package/dist/compat/openai/agent.js +0 -24
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  </a>
5
5
  </p>
6
6
 
7
- # DeepStrike Node.js SDK
7
+ # DeepStrike Node.js SDK (0.2.72)
8
8
 
9
9
  Build Node.js Agents with providers, typed tools, memory, Skills, delegation, workflows, and durable sessions. The SDK keeps the Agent's long-running work explicit through stream events, SessionLog evidence, tool policies, and host-provided integrations.
10
10
 
@@ -40,11 +40,8 @@ The correct platform package is selected automatically via `optionalDependencies
40
40
 
41
41
  ```typescript
42
42
  import {
43
- FileSessionLog,
44
- LocalExecutionPlane,
45
- RuntimeRunner,
43
+ createAgent,
46
44
  OpenAIResponsesProvider,
47
- collectText,
48
45
  tool,
49
46
  } from "@deepstrike/sdk"
50
47
 
@@ -56,71 +53,90 @@ const add = tool("add", "Add two numbers.", {
56
53
  required: ["x", "y"],
57
54
  }, async ({ x, y }) => String(Number(x) + Number(y)))
58
55
 
59
- const plane = new LocalExecutionPlane().register(add)
60
- const runner = new RuntimeRunner({
56
+ const agent = createAgent({
57
+ name: "math",
61
58
  provider,
62
- executionPlane: plane,
63
- sessionLog: new FileSessionLog(".deepstrike/sessions"),
64
- maxTokens: 4096,
59
+ tools: [add],
65
60
  })
66
61
 
67
- const result = await collectText(runner.run({
68
- sessionId: "math-1",
69
- goal: "What is 17 + 28?",
70
- }))
71
- console.log(result)
62
+ const result = await agent.run("What is 17 + 28?")
63
+ console.log(result.output)
72
64
  ```
73
65
 
74
- Same-session continuity is explicit via `sessionId`:
66
+ Same-session continuity is explicit via `agent.session()`:
75
67
 
76
68
  ```typescript
77
- await collectText(runner.run({ sessionId: "chat-1", goal: "My name is Ada." }))
78
- const reply = await collectText(runner.run({ sessionId: "chat-1", goal: "What is my name?" }))
69
+ const session = agent.session("chat-1")
70
+ await session.run("My name is Ada.")
71
+ const reply = await session.run("What is my name?")
72
+ console.log(reply.output)
79
73
  ```
80
74
 
81
- Use `InMemorySessionLog` for process-local sessions or `FileSessionLog` when replay should survive restarts. `wake(sessionId)` resumes from the event log without inserting a duplicate `run_started` event.
75
+ Session persistence and recovery are configured on the Agent; applications use `session.resume()` rather than rebuilding a runner.
82
76
 
83
77
  ### Package layout
84
78
 
85
- The root export is the **intent layer** — what you reach for to run an agent, run a workflow, author a tool, or pick a provider (~30 symbols). Advanced machinery lives behind subpaths, so the common surface stays small and tree-shakeable:
79
+ The root export is the **Agent intent layer** — what you reach for to define and run an Agent, author a tool, or pick a provider. Advanced machinery lives behind subpaths, so the common surface stays small and tree-shakeable:
86
80
 
87
81
  | Import | Contains |
88
82
  |--------|----------|
89
- | `@deepstrike/sdk` | `runAgent` · `runFanout` · `RuntimeRunner` · `tool` · `LocalExecutionPlane` · `InMemorySessionLog`/`FileSessionLog` · `AnthropicProvider`/`OpenAIProvider`/`OpenAIResponsesProvider` · `createProvider` · `Governance` · `AgentPool` · `operationAbortSignal` · core types |
83
+ | `@deepstrike/sdk` | `createAgent` · `Agent`/`RunResult` · `tool` · `AnthropicProvider`/`OpenAIProvider`/`OpenAIResponsesProvider` · `createProvider` · core types |
90
84
  | `@deepstrike/sdk/providers` | backend factories (`deepseek`, `kimi`, `qwen`, `glm`, `minimax`, `gemini`, `ollama`), profiles, `CircuitBreaker` |
91
85
  | `@deepstrike/sdk/workflow` | `SubAgentOrchestrator`, `spawnStandalone`, reducers, contracts, handoff/modes, agent + spec types |
92
86
  | `@deepstrike/sdk/planes` | `WorktreeExecutionPlane`, `ProcessSandboxPlane`, `McpProxyPlane`, `RemoteVpcPlane`, archive/credential stores |
93
87
  | `@deepstrike/sdk/memory` | `MemoryStore`, `WorkingMemory`, `InMemoryMemoryStore`, `rankMemories`, `extractSessionMemories`, `KnowledgeSource` |
94
88
  | `@deepstrike/sdk/harness` | `AttemptLoop`, body/judge/carry policies, `judge` |
95
89
  | `@deepstrike/sdk/os` | profiles, `KernelPrimitivesDashboard`, `primitiveForKind` / `KernelPrimitive`, signals, `PermissionManager`, replay-testing utilities |
90
+ | `@deepstrike/sdk/runtime` | RuntimeRunner, SessionLog, runtime projections, and host execution helpers |
91
+ | `@deepstrike/sdk/evals` | Public evaluation language: `judge`, criteria, verdicts, and schemas |
92
+ | `@deepstrike/sdk/advanced` | Kernel diagnostics and low-level orchestration escape hatches |
93
+
94
+ > **Migration from 0.2.71:** see [`MIGRATION-v0.2.71-to-v0.2.72.md`](../MIGRATION-v0.2.71-to-v0.2.72.md) for the AgentDefinition, message, runtime binding, workflow and package changes.
96
95
 
97
- > **Migration from 0.2.x:** the kernel-lowering converters (`*ToKernel`), low-level prompt/eval builders, and the `OpenAIChatProvider` alias are no longer exported from root; backend providers, planes, memory, harness, and OS utilities moved to the subpaths above. See [`MIGRATION-v0.2.30.md`](./MIGRATION-v0.2.30.md).
96
+ The recipes below the Agent section that mention `RuntimeRunner` are advanced implementation examples. Import it from `@deepstrike/sdk/advanced`; application code should use the Agent and Session methods shown above.
98
97
 
99
98
  ### Recipes — the canonical entry points
100
99
 
101
- Most apps need one of three shapes. Start with the facades and drop down to `RuntimeRunner` only when you need streaming, signals, memory, or governance hooks.
100
+ Most apps start with one executable Agent. Streaming, sessions, memory, delegation, governance, and workflows are exposed from the Agent and Session objects.
102
101
 
103
102
  ```typescript
104
- import { runAgent, runFanout } from "@deepstrike/sdk"
103
+ import { createAgent } from "@deepstrike/sdk"
105
104
 
106
- // 1) Single agent one prompt, one model, the text back.
107
- const answer = await runAgent({ provider, goal: "What is 17 + 28?", tools: [add] })
105
+ const agent = createAgent({ name: "researcher", provider, tools: [add] })
106
+ const answer = await agent.run("What is 17 + 28?")
107
+ console.log(answer.output)
108
108
 
109
- // 2) Parallel fan-out synthesize N workers, then a synthesis pass, over the kernel-gated DAG.
110
- // Bootstraps and tears down its own kernel, so it's safe from a stateless request handler.
111
- const { synthesis } = await runFanout({
112
- provider,
113
- tasks: [
114
- "Summarize the security posture of the auth module",
115
- "Summarize the data-retention posture",
116
- ],
117
- synthesize: "Combine the worker findings into one risk summary.",
109
+ for await (const event of agent.stream("Summarize the auth module")) {
110
+ if (event.type === "text_delta") process.stdout.write(event.delta)
111
+ }
112
+
113
+ const delegated = await agent.delegate({ goal: "Check the data-retention posture" })
114
+ console.log(delegated.output)
115
+ ```
116
+
117
+ For parallel work and dependency graphs, use `agent.workflow(...)`. Kernel scheduling and run isolation remain internal to the Agent facade.
118
+
119
+ ### Multimodal input
120
+
121
+ Pass image or audio parts through `AgentRunOptions.attachments`. The runner persists them with the
122
+ session and avoids injecting the same attachment twice when a session continues or resumes:
123
+
124
+ ```typescript
125
+ const session = agent.session("conversation-1")
126
+
127
+ await session.run("先看看这张图", {
128
+ attachments: [{
129
+ type: "image",
130
+ source: { kind: "url", url: "https://storage.example.com/signed/image.png" },
131
+ mediaType: "image/png",
132
+ }],
118
133
  })
119
134
 
120
- // 3) Full control — sub-agents, governance, signals, streaming, resume → use RuntimeRunner directly.
135
+ await session.run("继续解释其中的内容")
121
136
  ```
122
137
 
123
- `runFanout` is sugar over the **standalone `runWorkflow`** path: with no active `run()`, `runner.runWorkflow(spec)` auto-bootstraps a kernel that owns the DAG (governed · resumable), drives it, and tears it down — exactly what a Vercel/Lambda handler needs. See [Dynamic workflows](#dynamic-workflows). For parallel work you can also give each worker its own `RuntimeRunner`; `RuntimeRunner` carries per-run state, so **never share one instance across concurrent runs** — use a fresh instance per worker (or the `AgentPool` primitive).
138
+ The current public `ContentPart` contract supports text, image, audio, and tool-result parts.
139
+ Video and file attachments require an explicit content-type extension and provider conversion.
124
140
 
125
141
  ### Deploying to serverless / bundlers
126
142
 
@@ -308,7 +324,7 @@ Providers take an **options object** and share a `CircuitBreaker`. `extensions`
308
324
  `OpenAIProvider` with an options object — no more positional `baseURL` hole:
309
325
 
310
326
  ```typescript
311
- import { OpenAIProvider } from "@deepstrike/sdk"
327
+ import { OpenAIProvider } from "@deepstrike/sdk/advanced"
312
328
 
313
329
  const provider = new OpenAIProvider({
314
330
  apiKey,
@@ -479,7 +495,7 @@ No configuration is required. Pass a `PayloadStore` through `RuntimeOptions.payl
479
495
  ## Tools
480
496
 
481
497
  ```typescript
482
- import { tool } from "@deepstrike/sdk"
498
+ import { tool } from "@deepstrike/sdk/advanced"
483
499
  import { readFile } from "@deepstrike/sdk/workflow"
484
500
 
485
501
  plane.register(tool("search", "Search.", schema, async (args) => ...))
@@ -646,7 +662,7 @@ Session events: `memory_written`, `memory_queried`, `memory_validation_failed`,
646
662
  Every run loads `governancePolicy` into the kernel via `load_governance_policy`. The kernel enforces rules **before** tools execute:
647
663
 
648
664
  ```typescript
649
- import type { GovernancePolicy } from "@deepstrike/sdk"
665
+ import type { GovernancePolicy } from "@deepstrike/sdk/advanced"
650
666
 
651
667
  const policy: GovernancePolicy = {
652
668
  rules: [
@@ -680,7 +696,7 @@ Default when omitted: allow-all (`DEFAULT_NATIVE_GOVERNANCE_POLICY`).
680
696
  `Governance` wraps the native governance evaluator for SDK-side use (tests, custom gates). It is **not** wired automatically into `RuntimeRunner` — use `governancePolicy` for run-time enforcement.
681
697
 
682
698
  ```typescript
683
- import { Governance } from "@deepstrike/sdk"
699
+ import { Governance } from "@deepstrike/sdk/advanced"
684
700
 
685
701
  const gov = new Governance("allow")
686
702
  gov.addPermissionRule("danger.*", "deny")
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Advanced escape hatch for runtime authors, diagnostics, and compatibility with the
3
+ * implementation-oriented test harness. Ordinary applications should use the root Agent API.
4
+ */
5
+ export * from "../index.js";
6
+ export { RuntimeRunner, collectText } from "../runtime/runner.js";
7
+ export type { RuntimeOptions } from "../runtime/runner.js";
8
+ export { runAgent, runFanout } from "../runtime/facade.js";
9
+ export type { RunAgentOptions, RunFanoutOptions } from "../runtime/facade.js";
10
+ export { LocalExecutionPlane } from "../runtime/execution-plane.js";
11
+ export type { ExecutionPlane, RunContext } from "../runtime/execution-plane.js";
12
+ export { InMemorySessionLog, FileSessionLog } from "../runtime/session-log.js";
13
+ export type { SessionLog, SessionEvent, SessionEventKind } from "../runtime/session-log.js";
14
+ export * from "../types/agent.js";
15
+ export * from "../runtime/run-group.js";
16
+ export * from "../runtime/event-stream.js";
17
+ export * from "../runtime/reliability.js";
18
+ export * from "../runtime/turn-policy.js";
19
+ export * from "../runtime/reactive-session.js";
20
+ export * from "../runtime/reaction-checkpoint.js";
21
+ export { projectAgentRun, projectAgentContext, projectAgentCapabilities, projectAgentGovernance, projectAgentDelegation } from "../agent-ir.js";
22
+ export type { AgentDescriptor } from "../agent-ir.js";
23
+ export { FileKernelJournal, InMemoryKernelJournal, JournalCasConflictError, JournalIntegrityError, JournalIoError, diagnoseKernelJournal, } from "../runtime/public.js";
24
+ export type { CheckpointCandidate, InstalledCheckpoint, JournalAppendReceipt, JournalEntry, JournalHead, JournalPruneReceipt, JournalRecordInput, KernelJournal, KernelJournalDiagnosis, ContextPrepared, ContextPrepareJson, ContextVerifyJson, ContextProviderPreparationRequest, EvolutionRuntime, EvolutionStore, InvocationOutcome, ModelInvocation, ProviderAttempt, ProviderAttemptRecord, ProviderAttemptStatus, UsageAccountingPolicy, ModelUsageSettlement, } from "../runtime/public.js";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Advanced escape hatch for runtime authors, diagnostics, and compatibility with the
3
+ * implementation-oriented test harness. Ordinary applications should use the root Agent API.
4
+ */
5
+ export * from "../index.js";
6
+ export { RuntimeRunner, collectText } from "../runtime/runner.js";
7
+ export { runAgent, runFanout } from "../runtime/facade.js";
8
+ export { LocalExecutionPlane } from "../runtime/execution-plane.js";
9
+ export { InMemorySessionLog, FileSessionLog } from "../runtime/session-log.js";
10
+ export * from "../types/agent.js";
11
+ export * from "../runtime/run-group.js";
12
+ export * from "../runtime/event-stream.js";
13
+ export * from "../runtime/reliability.js";
14
+ export * from "../runtime/turn-policy.js";
15
+ export * from "../runtime/reactive-session.js";
16
+ export * from "../runtime/reaction-checkpoint.js";
17
+ export { projectAgentRun, projectAgentContext, projectAgentCapabilities, projectAgentGovernance, projectAgentDelegation } from "../agent-ir.js";
18
+ export { FileKernelJournal, InMemoryKernelJournal, JournalCasConflictError, JournalIntegrityError, JournalIoError, diagnoseKernelJournal, } from "../runtime/public.js";
@@ -0,0 +1,96 @@
1
+ import { type AgentOptions, type ModelRef } from "./agent.js";
2
+ import { type SessionLog } from "./runtime/session-log.js";
3
+ import { type ExecutionPlane } from "./runtime/execution-plane.js";
4
+ import { type RuntimeOptions } from "./runtime/runner.js";
5
+ import type { LLMProvider, StreamEvent, TokenUsage, ContentPart } from "./types.js";
6
+ import type { RegisteredTool } from "./tools/index.js";
7
+ import type { MemoryRecord, MemoryRecall, MemoryScope, MemoryStore, MemoryKind } from "./memory/protocols.js";
8
+ import type { WorkflowSpec, WorkflowOutcome, KernelAgentRole } from "./types/agent.js";
9
+ export interface AgentDefinition extends Omit<AgentOptions, "model" | "name"> {
10
+ name?: string;
11
+ /** Public model identity. Runtime resolves this through a provider binding. */
12
+ model?: ModelRef;
13
+ /** Optional host binding retained for local/custom execution. */
14
+ provider?: LLMProvider;
15
+ tools?: RegisteredTool[];
16
+ executionPlane?: ExecutionPlane;
17
+ sessionLog?: SessionLog;
18
+ maxTokens?: number;
19
+ memoryStore?: MemoryStore;
20
+ memoryScope?: MemoryScope;
21
+ runtimeOptions?: Pick<RuntimeOptions, "memoryPolicy" | "governancePolicy" | "signalSource" | "signalPolicy" | "resourceQuota" | "onPermissionRequest" | "payloadStore" | "runGroup" | "subAgentOrchestrator" | "reducers" | "providerFor" | "initialMemory" | "skillCatalog" | "knowledgeSource" | "contextManager">;
22
+ }
23
+ export interface AgentRunOptions {
24
+ session?: SessionRef;
25
+ maxTurns?: number;
26
+ signal?: AbortSignal;
27
+ metadata?: Record<string, unknown>;
28
+ onPermissionRequest?: RuntimeOptions["onPermissionRequest"];
29
+ /** Multimodal user input attached to this run and persisted in the session log. */
30
+ attachments?: ContentPart[];
31
+ }
32
+ export interface SessionRef {
33
+ id: string;
34
+ }
35
+ export interface RunResult<T = string> {
36
+ output: T;
37
+ runId: string;
38
+ sessionId: string;
39
+ status: "completed" | "partial" | "failed" | "cancelled";
40
+ usage?: TokenUsage;
41
+ outputValidation?: {
42
+ ok: boolean;
43
+ errors: string[];
44
+ };
45
+ }
46
+ export interface AgentSession extends SessionRef {
47
+ run(goal: string, options?: Omit<AgentRunOptions, "session">): Promise<RunResult>;
48
+ stream(goal: string, options?: Omit<AgentRunOptions, "session">): AsyncIterable<StreamEvent>;
49
+ resume(options?: Omit<AgentRunOptions, "session">): AsyncIterable<StreamEvent>;
50
+ interrupt(reason?: "user" | "deadline" | "lease_lost" | "host_shutdown"): void;
51
+ }
52
+ export interface MemoryInput {
53
+ name: string;
54
+ content: string;
55
+ description?: string;
56
+ kind?: MemoryKind;
57
+ confidence?: number;
58
+ pinned?: boolean;
59
+ ttlDays?: number;
60
+ }
61
+ export interface RecallOptions {
62
+ topK?: number;
63
+ kinds?: MemoryKind[];
64
+ minScore?: number;
65
+ }
66
+ export interface DelegationRequest {
67
+ goal: string;
68
+ role?: KernelAgentRole;
69
+ /** Optional declared handoff target. When handoffs are declared, this is required and allowlisted. */
70
+ target?: import("./handoff-target.js").AgentRef;
71
+ }
72
+ export interface DelegationResult {
73
+ output: string;
74
+ status: "completed" | "partial" | "failed";
75
+ nodeId?: string;
76
+ }
77
+ /** The executable host handle created from an AgentDefinition. */
78
+ export interface AgentRuntime {
79
+ readonly name: string;
80
+ readonly definition: Readonly<AgentDefinition>;
81
+ run(goal: string, options?: AgentRunOptions): Promise<RunResult>;
82
+ stream(goal: string, options?: AgentRunOptions): AsyncIterable<StreamEvent>;
83
+ session(id?: string): AgentSession;
84
+ remember(input: MemoryInput): Promise<MemoryRecord>;
85
+ recall(query: string, options?: RecallOptions): Promise<MemoryRecall[]>;
86
+ delegate(request: DelegationRequest): Promise<DelegationResult>;
87
+ workflow(spec: WorkflowSpec, options?: {
88
+ session?: SessionRef;
89
+ }): Promise<WorkflowOutcome>;
90
+ listen(options?: {
91
+ session?: SessionRef;
92
+ leaseMs?: number;
93
+ }): Promise<RunResult | null>;
94
+ close(): Promise<void>;
95
+ }
96
+ export declare function createAgent(definition: AgentDefinition): AgentRuntime;
@@ -0,0 +1,317 @@
1
+ import { normalizeAgent } from "./agent-ir.js";
2
+ import { InMemorySessionLog } from "./runtime/session-log.js";
3
+ import { LocalExecutionPlane } from "./runtime/execution-plane.js";
4
+ import { RuntimeRunner } from "./runtime/runner.js";
5
+ import { extractJsonValue, schemaInstruction, validateAgainstSchema } from "./runtime/output-schema.js";
6
+ import { McpProxyPlane } from "./runtime/mcp-proxy-plane.js";
7
+ import { EnvCredentialVault } from "./runtime/credential-vault.js";
8
+ import { agentRefName } from "./handoff-target.js";
9
+ import { createTextKnowledgeSource } from "./knowledge/public.js";
10
+ function sessionId(ref) {
11
+ return ref?.id ?? `session-${crypto.randomUUID()}`;
12
+ }
13
+ function statusFromDone(status) {
14
+ if (status === "completed" || status === "done")
15
+ return "completed";
16
+ if (status === "cancelled" || status === "user" || status === "deadline" || status === "lease_lost" || status === "host_shutdown")
17
+ return "cancelled";
18
+ if (status === "failed" || status === "error")
19
+ return "failed";
20
+ return "partial";
21
+ }
22
+ function mergeGuardrailPolicies(base, guardrails) {
23
+ const policies = [base, ...(guardrails ?? []).map(guardrail => guardrail.policy)].filter((policy) => policy !== undefined);
24
+ if (!policies.length)
25
+ return undefined;
26
+ return {
27
+ ...(policies.some(policy => policy.defaultAction === "deny") ? { defaultAction: "deny" } : {}),
28
+ rules: policies.flatMap(policy => policy.rules ?? []),
29
+ vetoes: [...new Set(policies.flatMap(policy => policy.vetoes ?? []))],
30
+ rateLimits: policies.flatMap(policy => policy.rateLimits ?? []),
31
+ constraints: policies.flatMap(policy => policy.constraints ?? []),
32
+ ...(policies.some(policy => policy.surfaceDeniedInSystem === false) ? { surfaceDeniedInSystem: false } : {}),
33
+ };
34
+ }
35
+ class AgentSessionImpl {
36
+ owner;
37
+ id;
38
+ constructor(owner, id) {
39
+ this.owner = owner;
40
+ this.id = id;
41
+ }
42
+ run(goal, options) {
43
+ return this.owner.run(goal, { ...options, session: { id: this.id } });
44
+ }
45
+ stream(goal, options) {
46
+ return this.owner.stream(goal, { ...options, session: { id: this.id } });
47
+ }
48
+ resume(options) {
49
+ return this.owner.resume(this.id, options);
50
+ }
51
+ interrupt(reason = "user") {
52
+ this.owner.interrupt(reason);
53
+ }
54
+ }
55
+ class AgentRuntimeImpl {
56
+ name;
57
+ definition;
58
+ sessionLog;
59
+ activeRunner = null;
60
+ mcpPlane;
61
+ mcpConnection;
62
+ constructor(definition) {
63
+ this.definition = Object.freeze({ ...definition });
64
+ this.name = normalizeAgent(definition).name;
65
+ this.sessionLog = definition.sessionLog ?? new InMemorySessionLog();
66
+ }
67
+ session(id = `session-${crypto.randomUUID()}`) {
68
+ return new AgentSessionImpl(this, id);
69
+ }
70
+ async remember(input) {
71
+ const store = this.definition.memoryStore;
72
+ const scope = this.definition.memoryScope;
73
+ if (!store || !scope)
74
+ throw new Error("agent memory requires memoryStore and memoryScope");
75
+ const now = Date.now();
76
+ const record = {
77
+ record_id: crypto.randomUUID(),
78
+ scope,
79
+ name: input.name,
80
+ kind: input.kind ?? "reference",
81
+ content: input.content,
82
+ description: input.description ?? "",
83
+ provenance: { author: "host", trust: "user_asserted", evidence_refs: [] },
84
+ created_at: now,
85
+ updated_at: now,
86
+ recall_count: 0,
87
+ confidence: input.confidence ?? 1,
88
+ links: [],
89
+ pinned: input.pinned ?? false,
90
+ ...(input.ttlDays !== undefined ? { ttl_days: input.ttlDays } : {}),
91
+ };
92
+ await store.put(this.name, record);
93
+ return record;
94
+ }
95
+ async recall(query, options = {}) {
96
+ const store = this.definition.memoryStore;
97
+ const scope = this.definition.memoryScope;
98
+ if (!store || !scope)
99
+ throw new Error("agent memory requires memoryStore and memoryScope");
100
+ const request = {
101
+ scope,
102
+ query,
103
+ top_k: options.topK ?? 8,
104
+ kinds: options.kinds ?? [],
105
+ ...(options.minScore !== undefined ? { min_score: options.minScore } : {}),
106
+ };
107
+ return store.search(this.name, request);
108
+ }
109
+ async delegate(request) {
110
+ const handoffs = this.definition.handoffs ?? [];
111
+ if (handoffs.length) {
112
+ if (!request.target)
113
+ throw new Error(`agent "${this.name}" requires an explicit handoff target`);
114
+ const targetName = agentRefName(request.target);
115
+ const allowed = handoffs.some(handoff => {
116
+ return agentRefName(handoff.agent) === targetName;
117
+ });
118
+ if (!allowed)
119
+ throw new Error(`agent "${this.name}" cannot hand off to "${targetName}"`);
120
+ }
121
+ const spec = {
122
+ nodes: [{
123
+ task: { goal: request.goal },
124
+ role: request.role ?? "explore",
125
+ isolation: "read_only",
126
+ contextInheritance: "system_only",
127
+ }],
128
+ };
129
+ const outcome = await this.workflow(spec);
130
+ const node = outcome.nodeOutcomes[0];
131
+ const nodeId = node?.nodeId;
132
+ return {
133
+ output: nodeId ? outcome.outputs[nodeId] ?? "" : "",
134
+ status: node?.status === "completed" ? "completed" : node?.status === "failed" ? "failed" : "partial",
135
+ ...(nodeId ? { nodeId } : {}),
136
+ };
137
+ }
138
+ async workflow(spec, options = {}) {
139
+ const runner = this.createRunner({});
140
+ await this.prepareMcp();
141
+ this.activeRunner = runner;
142
+ try {
143
+ return await runner.runWorkflow(spec, { sessionId: sessionId(options.session) });
144
+ }
145
+ finally {
146
+ this.activeRunner = null;
147
+ }
148
+ }
149
+ async listen(options = {}) {
150
+ const source = this.definition.runtimeOptions?.signalSource;
151
+ if (!source)
152
+ throw new Error("agent signals require runtimeOptions.signalSource");
153
+ const claim = await source.claimSignal(this.name, options.leaseMs);
154
+ if (!claim)
155
+ return null;
156
+ const payload = claim.signal.payload;
157
+ const goal = typeof payload.goal === "string"
158
+ ? payload.goal
159
+ : typeof payload.summary === "string"
160
+ ? payload.summary
161
+ : JSON.stringify(payload);
162
+ try {
163
+ const result = await this.run(goal, options.session ? { session: options.session } : {});
164
+ await source.ackSignal(claim);
165
+ return result;
166
+ }
167
+ catch (error) {
168
+ await source.nackSignal(claim);
169
+ throw error;
170
+ }
171
+ }
172
+ stream(goal, options = {}) {
173
+ const session = sessionId(options.session);
174
+ const owner = this;
175
+ return (async function* () {
176
+ const runner = owner.createRunner(options);
177
+ await owner.prepareMcp();
178
+ owner.activeRunner = runner;
179
+ const abort = () => runner.interrupt("user");
180
+ if (options.signal) {
181
+ if (options.signal.aborted)
182
+ runner.interrupt("user");
183
+ else
184
+ options.signal.addEventListener("abort", abort, { once: true });
185
+ }
186
+ const stream = runner.run({ sessionId: session, goal, ...(options.attachments?.length ? { attachments: options.attachments } : {}) });
187
+ yield* owner.clearRunnerAfter(stream, options.signal, abort);
188
+ })();
189
+ }
190
+ async run(goal, options = {}) {
191
+ const session = sessionId(options.session);
192
+ const events = [];
193
+ for await (const event of this.stream(goal, { ...options, session: { id: session } }))
194
+ events.push(event);
195
+ const done = [...events].reverse().find(event => event.type === "done");
196
+ const error = [...events].reverse().find(event => event.type === "error");
197
+ const persisted = await this.sessionLog.read(session);
198
+ const started = [...persisted].reverse().find(entry => entry.event.kind === "run_started");
199
+ const usageEvent = [...events].reverse().find(event => event.type === "usage");
200
+ const output = events.filter(event => event.type === "text_delta").map(event => String(event.delta ?? "")).join("");
201
+ const outputValidation = this.definition.outputSchema
202
+ ? validateAgainstSchema(extractJsonValue(output), this.definition.outputSchema)
203
+ : undefined;
204
+ return {
205
+ output,
206
+ runId: started?.event.kind === "run_started" ? started.event.run_id : `run-${crypto.randomUUID()}`,
207
+ sessionId: session,
208
+ status: error || outputValidation && !outputValidation.ok ? "failed" : statusFromDone(done?.status ?? "partial"),
209
+ ...(outputValidation ? { outputValidation } : {}),
210
+ ...(usageEvent?.totalTokens !== undefined ? {
211
+ usage: {
212
+ inputTokens: usageEvent.inputTokens ?? 0,
213
+ outputTokens: usageEvent.outputTokens ?? 0,
214
+ totalTokens: usageEvent.totalTokens,
215
+ },
216
+ } : {}),
217
+ };
218
+ }
219
+ async *resume(id, options = {}) {
220
+ const runner = this.createRunner(options);
221
+ await this.prepareMcp();
222
+ this.activeRunner = runner;
223
+ yield* this.clearRunnerAfter(runner.wake(id), options.signal, () => runner.interrupt("user"));
224
+ }
225
+ interrupt(reason = "user") {
226
+ this.activeRunner?.interrupt(reason);
227
+ }
228
+ async close() {
229
+ await this.mcpConnection;
230
+ await this.mcpPlane?.disconnect();
231
+ this.mcpPlane = undefined;
232
+ this.mcpConnection = undefined;
233
+ }
234
+ async prepareMcp() {
235
+ if (!this.mcpPlane || this.mcpConnection) {
236
+ await this.mcpConnection;
237
+ return;
238
+ }
239
+ this.mcpConnection = this.mcpPlane.connect();
240
+ await this.mcpConnection;
241
+ }
242
+ createRunner(options) {
243
+ const model = this.definition.model;
244
+ const provider = this.definition.provider
245
+ ?? (typeof model === "string" ? this.definition.runtimeOptions?.providerFor?.(model) : undefined);
246
+ if (!provider) {
247
+ throw new Error(`agent "${this.name}" has no runtime provider binding for model ${typeof this.definition.model === "string" ? this.definition.model : "(unresolved)"}`);
248
+ }
249
+ if (this.definition.executionPlane && this.definition.mcpServers?.length) {
250
+ throw new Error("agent mcpServers cannot be combined with a custom executionPlane");
251
+ }
252
+ const plane = this.definition.executionPlane
253
+ ?? (this.definition.mcpServers?.length
254
+ ? (() => {
255
+ const servers = Object.fromEntries(this.definition.mcpServers.map(server => {
256
+ if (server.transport.kind !== "stdio") {
257
+ throw new Error(`agent MCP transport "${server.transport.kind}" is not supported by the local runtime`);
258
+ }
259
+ if (server.auth && Object.keys(server.auth).length > 0) {
260
+ throw new Error(`agent MCP server "${server.name ?? server.transport.command}" auth requires an explicit CredentialVault binding`);
261
+ }
262
+ return [server.name ?? server.transport.command, {
263
+ command: server.transport.command,
264
+ ...(server.transport.args ? { args: server.transport.args } : {}),
265
+ }];
266
+ }));
267
+ this.mcpPlane ??= new McpProxyPlane({ servers, vault: new EnvCredentialVault() });
268
+ return this.mcpPlane;
269
+ })()
270
+ : (this.definition.tools ?? []).reduce((current, currentTool) => current.register(currentTool), new LocalExecutionPlane()));
271
+ if (this.definition.mcpServers?.length && this.definition.tools?.length) {
272
+ plane.register(...this.definition.tools);
273
+ }
274
+ const runtime = {
275
+ provider,
276
+ ...(mergeGuardrailPolicies(this.definition.runtimeOptions?.governancePolicy, this.definition.guardrails)
277
+ ? { governancePolicy: mergeGuardrailPolicies(this.definition.runtimeOptions?.governancePolicy, this.definition.guardrails) }
278
+ : {}),
279
+ ...(this.definition.capabilityFilter ? { capabilityFilter: this.definition.capabilityFilter } : {}),
280
+ executionPlane: plane,
281
+ sessionLog: this.sessionLog,
282
+ maxTokens: this.definition.maxTokens ?? 32_000,
283
+ ...(this.definition.instructions || this.definition.outputSchema ? {
284
+ systemPrompt: [
285
+ this.definition.instructions,
286
+ this.definition.outputSchema ? schemaInstruction(this.definition.outputSchema) : undefined,
287
+ ].filter((part) => Boolean(part)).join("\n\n"),
288
+ } : {}),
289
+ ...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
290
+ ...(this.definition.memoryStore ? { memoryStore: this.definition.memoryStore } : {}),
291
+ ...(this.definition.memoryScope ? { memoryScope: this.definition.memoryScope } : {}),
292
+ ...(this.definition.skills?.length ? { skillCatalog: this.definition.skills } : {}),
293
+ ...(!this.definition.runtimeOptions?.knowledgeSource && this.definition.knowledge?.some(item => item.source.kind === "text") ? {
294
+ knowledgeSource: createTextKnowledgeSource(this.definition.knowledge
295
+ .filter((item) => item.source.kind === "text")
296
+ .map(item => ({ id: item.id, name: item.name, content: item.source.content }))),
297
+ } : {}),
298
+ agentId: this.name,
299
+ ...(this.definition.runtimeOptions ?? {}),
300
+ ...(options.onPermissionRequest ? { onPermissionRequest: options.onPermissionRequest } : {}),
301
+ };
302
+ return new RuntimeRunner(runtime);
303
+ }
304
+ async *clearRunnerAfter(stream, signal, abort) {
305
+ try {
306
+ yield* stream;
307
+ }
308
+ finally {
309
+ if (signal && abort)
310
+ signal.removeEventListener("abort", abort);
311
+ this.activeRunner = null;
312
+ }
313
+ }
314
+ }
315
+ export function createAgent(definition) {
316
+ return new AgentRuntimeImpl(definition);
317
+ }