@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.
- package/README.md +57 -41
- package/dist/advanced/public.d.ts +24 -0
- package/dist/advanced/public.js +18 -0
- package/dist/agent-facade.d.ts +96 -0
- package/dist/agent-facade.js +317 -0
- package/dist/agent-ir.d.ts +11 -5
- package/dist/agent-ir.js +42 -26
- package/dist/canonical-prefix-allowlist.d.ts +6 -0
- package/dist/canonical-prefix-allowlist.js +30 -0
- package/dist/evals/public.d.ts +50 -0
- package/dist/evals/public.js +25 -0
- package/dist/guardrail.d.ts +4 -1
- package/dist/handoff-target.d.ts +2 -0
- package/dist/handoff-target.js +7 -1
- package/dist/index.d.ts +14 -30
- package/dist/index.js +7 -16
- package/dist/kernel.d.ts +2 -2
- package/dist/knowledge/public.d.ts +2 -0
- package/dist/knowledge/public.js +1 -1
- package/dist/knowledge/source.d.ts +7 -0
- package/dist/knowledge/source.js +20 -1
- package/dist/memory/protocols.d.ts +2 -2
- package/dist/projection-pairs.d.ts +43 -0
- package/dist/projection-pairs.js +9 -0
- package/dist/providers/anthropic-adapter.d.ts +2 -2
- package/dist/providers/anthropic.d.ts +4 -4
- package/dist/providers/base.d.ts +5 -5
- package/dist/providers/content-normalization.d.ts +4 -4
- package/dist/providers/gemini-adapter.d.ts +2 -2
- package/dist/providers/gemini.d.ts +3 -3
- package/dist/providers/ollama-adapter.d.ts +2 -2
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/openai-chat.d.ts +4 -4
- package/dist/providers/openai-responses-adapter.d.ts +2 -2
- package/dist/providers/openai-responses.d.ts +2 -2
- package/dist/providers/openai.d.ts +4 -4
- package/dist/providers/protocol-adapter.d.ts +2 -2
- package/dist/providers/protocol-capabilities.d.ts +1 -0
- package/dist/providers/protocol-capabilities.js +3 -0
- package/dist/providers/public.d.ts +4 -2
- package/dist/providers/public.js +2 -1
- package/dist/providers/replay-validator.d.ts +3 -3
- package/dist/runtime/archive.d.ts +7 -7
- package/dist/runtime/canonical-kernel-step.d.ts +2 -2
- package/dist/runtime/context-manager.d.ts +56 -0
- package/dist/runtime/context-manager.js +112 -0
- package/dist/runtime/eval.d.ts +2 -2
- package/dist/runtime/kernel-step.d.ts +5 -5
- package/dist/runtime/provider-replay.d.ts +2 -2
- package/dist/runtime/public.d.ts +22 -0
- package/dist/runtime/public.js +11 -0
- package/dist/runtime/replay-fixture.d.ts +3 -3
- package/dist/runtime/replay-fixture.js +1 -1
- package/dist/runtime/replay-provider.d.ts +4 -4
- package/dist/runtime/replay-provider.js +1 -1
- package/dist/runtime/runner.d.ts +17 -5
- package/dist/runtime/runner.js +110 -37
- package/dist/runtime/session-log.d.ts +1 -1
- package/dist/runtime/session-repair.d.ts +2 -2
- package/dist/runtime/workflow-control-flow.d.ts +1 -1
- package/dist/runtime/workflow-control-flow.js +16 -2
- package/dist/runtime-classification.d.ts +161 -0
- package/dist/runtime-classification.js +66 -0
- package/dist/runtime-language.d.ts +32 -0
- package/dist/runtime-language.js +51 -0
- package/dist/skill.d.ts +31 -5
- package/dist/types/agent.d.ts +17 -4
- package/dist/types.d.ts +22 -12
- package/dist/workflow/definition.d.ts +19 -0
- package/dist/workflow/definition.js +29 -0
- package/dist/workflow/public.d.ts +3 -1
- package/dist/workflow/public.js +1 -0
- package/package.json +23 -2
- package/dist/compat/anthropic/mcp.d.ts +0 -15
- package/dist/compat/anthropic/mcp.js +0 -10
- package/dist/compat/openai/agent.d.ts +0 -34
- 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
|
-
|
|
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
|
|
60
|
-
|
|
56
|
+
const agent = createAgent({
|
|
57
|
+
name: "math",
|
|
61
58
|
provider,
|
|
62
|
-
|
|
63
|
-
sessionLog: new FileSessionLog(".deepstrike/sessions"),
|
|
64
|
-
maxTokens: 4096,
|
|
59
|
+
tools: [add],
|
|
65
60
|
})
|
|
66
61
|
|
|
67
|
-
const result = await
|
|
68
|
-
|
|
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 `
|
|
66
|
+
Same-session continuity is explicit via `agent.session()`:
|
|
75
67
|
|
|
76
68
|
```typescript
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
|
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` | `
|
|
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
|
-
|
|
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
|
|
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 {
|
|
103
|
+
import { createAgent } from "@deepstrike/sdk"
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
const answer = await
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
135
|
+
await session.run("继续解释其中的内容")
|
|
121
136
|
```
|
|
122
137
|
|
|
123
|
-
|
|
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
|
+
}
|