@botbotgo/agent-harness 0.0.42 → 0.0.43
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 +29 -10
- package/dist/config/agents/direct.yaml +8 -7
- package/dist/config/agents/orchestra.yaml +8 -6
- package/dist/contracts/types.d.ts +27 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +8 -6
- package/dist/persistence/file-store.js +2 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +1 -0
- package/dist/runtime/agent-runtime-adapter.js +62 -42
- package/dist/runtime/harness.d.ts +2 -0
- package/dist/runtime/harness.js +98 -12
- package/dist/runtime/inventory.js +2 -1
- package/dist/runtime/support/compiled-binding.d.ts +15 -0
- package/dist/runtime/support/compiled-binding.js +56 -0
- package/dist/runtime/support/harness-support.d.ts +2 -2
- package/dist/runtime/support/harness-support.js +14 -12
- package/dist/workspace/agent-binding-compiler.js +50 -25
- package/dist/workspace/object-loader.js +43 -2
- package/dist/workspace/support/agent-capabilities.d.ts +7 -0
- package/dist/workspace/support/agent-capabilities.js +30 -0
- package/dist/workspace/validate.js +9 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ What it provides:
|
|
|
15
15
|
|
|
16
16
|
- a small runtime API centered on `createAgentHarness(...)`, `run(...)`, `subscribe(...)`, inspection methods, and `stop(...)`
|
|
17
17
|
- YAML-defined runtime assembly for hosts, models, routing, recovery, concurrency, MCP, and maintenance policy
|
|
18
|
-
-
|
|
18
|
+
- backend-adapted execution with current DeepAgents-first defaults and LangChain v1 compatibility
|
|
19
19
|
- local `resources/tools/` and `resources/skills/` loading
|
|
20
20
|
- persisted runs, threads, approvals, events, and resumable checkpoints
|
|
21
21
|
|
|
@@ -66,7 +66,7 @@ try {
|
|
|
66
66
|
## Feature List
|
|
67
67
|
|
|
68
68
|
- Workspace runtime for multi-agent applications
|
|
69
|
-
-
|
|
69
|
+
- Generic runtime contract with backend-adapted execution
|
|
70
70
|
- YAML-defined host routing
|
|
71
71
|
- Auto-discovered local tools and SKILL packages
|
|
72
72
|
- MCP bridge support for agent-declared MCP servers
|
|
@@ -107,13 +107,15 @@ const result = await run(runtime, {
|
|
|
107
107
|
});
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Each run creates or continues a persisted thread and returns `threadId`, `runId`, `state`, and
|
|
110
|
+
Each run creates or continues a persisted thread and returns `threadId`, `runId`, `state`, and the final user-facing output text.
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
The preferred runtime-facing way to pass execution metadata is `invocation`:
|
|
113
113
|
|
|
114
|
-
- `context`
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
114
|
+
- `invocation.context` for request-scoped context
|
|
115
|
+
- `invocation.inputs` for additional structured runtime inputs
|
|
116
|
+
- `invocation.attachments` for attachment-like payloads that the active backend can interpret
|
|
117
|
+
|
|
118
|
+
For compatibility, `context`, `state`, and `files` are still accepted as existing aliases and are normalized into the same runtime invocation envelope.
|
|
117
119
|
|
|
118
120
|
### Let The Runtime Route
|
|
119
121
|
|
|
@@ -161,7 +163,7 @@ const approvals = await listApprovals(runtime, { status: "pending" });
|
|
|
161
163
|
const approval = approvals[0] ? await getApproval(runtime, approvals[0].approvalId) : null;
|
|
162
164
|
```
|
|
163
165
|
|
|
164
|
-
These methods return runtime-facing records, not raw persistence or backend objects.
|
|
166
|
+
These methods return runtime-facing records, not raw persistence or backend checkpoint objects.
|
|
165
167
|
|
|
166
168
|
### Bridge MCP Servers Into Agents
|
|
167
169
|
|
|
@@ -233,9 +235,26 @@ Put stable project context here. Do not use it as mutable long-term memory.
|
|
|
233
235
|
|
|
234
236
|
### `config/agents/*.yaml`
|
|
235
237
|
|
|
236
|
-
|
|
238
|
+
Prefer the generic agent form and declare the current execution backend explicitly:
|
|
239
|
+
|
|
240
|
+
```yaml
|
|
241
|
+
apiVersion: agent-harness/v1alpha1
|
|
242
|
+
kind: Agent
|
|
243
|
+
metadata:
|
|
244
|
+
name: orchestra
|
|
245
|
+
spec:
|
|
246
|
+
modelRef: model/default
|
|
247
|
+
execution:
|
|
248
|
+
backend: deepagent
|
|
249
|
+
systemPrompt: Coordinate the request.
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`kind: DeepAgent` and `kind: LangChainAgent` remain supported as compatibility forms, but `kind: Agent` is the recommended product-facing entry point.
|
|
253
|
+
|
|
254
|
+
Common fields include:
|
|
237
255
|
|
|
238
256
|
- `modelRef`
|
|
257
|
+
- `execution.backend`
|
|
239
258
|
- `systemPrompt`
|
|
240
259
|
- `tools`
|
|
241
260
|
- `skills`
|
|
@@ -262,7 +281,7 @@ Keep runtime extension source under `resources/`. Keep tests outside the publish
|
|
|
262
281
|
|
|
263
282
|
## Design Notes
|
|
264
283
|
|
|
265
|
-
- `agent-harness` should
|
|
284
|
+
- `agent-harness` should keep the public runtime contract generic while mapping cleanly onto current backend capabilities
|
|
266
285
|
- agent-level execution behavior stays upstream
|
|
267
286
|
- application-level orchestration and lifecycle management stays in the harness
|
|
268
287
|
- checkpoint resume is treated as a system-managed runtime behavior, not a primary public abstraction
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# agent-harness feature: schema version for this declarative config object.
|
|
2
2
|
apiVersion: agent-harness/v1alpha1
|
|
3
3
|
# agent-harness feature: object type discriminator.
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# rather than the upstream `createDeepAgent(...)` runtime.
|
|
7
|
-
kind: LangChainAgent
|
|
4
|
+
# Prefer the generic `Agent` form and select the concrete execution backend under `spec.execution`.
|
|
5
|
+
kind: Agent
|
|
8
6
|
metadata:
|
|
9
7
|
# agent-harness feature: stable object id used for refs and host-agent selection.
|
|
10
8
|
name: direct
|
|
11
9
|
# agent-harness feature: human-readable summary for inventory and UI.
|
|
12
10
|
description: Manual low-latency host for direct answers and lightweight inventory lookup. Do not use it as the default executor for tool-heavy or specialist-shaped tasks.
|
|
13
11
|
spec:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
execution:
|
|
13
|
+
# Current backend adapter for this host profile.
|
|
14
|
+
backend: langchain-v1
|
|
15
|
+
# =====================
|
|
16
|
+
# Runtime Agent Features
|
|
17
|
+
# =====================
|
|
17
18
|
# Shared LangChain v1 / DeepAgents feature: model ref for the underlying LLM used by the
|
|
18
19
|
# direct-response agent. This should point at a cheap, fast, general-purpose chat model,
|
|
19
20
|
# because `direct` is intended to be the low-latency path for simple requests.
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# agent-harness feature: schema version for this declarative config object.
|
|
2
2
|
apiVersion: agent-harness/v1alpha1
|
|
3
3
|
# agent-harness feature: object type discriminator.
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
kind: DeepAgent
|
|
4
|
+
# Prefer the generic `Agent` form and select the concrete execution backend under `spec.execution`.
|
|
5
|
+
kind: Agent
|
|
7
6
|
metadata:
|
|
8
7
|
# agent-harness feature: stable object id used for refs and default DeepAgents name inference.
|
|
9
8
|
name: orchestra
|
|
10
9
|
# agent-harness feature: human-readable summary for inventory and UI.
|
|
11
10
|
description: Default execution host. Answer directly when possible, use local tools and skills first, and delegate only when a specialist is a better fit. Not a reflex delegation-only planner.
|
|
12
11
|
spec:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
execution:
|
|
13
|
+
# Current backend adapter for this host profile.
|
|
14
|
+
backend: deepagent
|
|
15
|
+
# =====================
|
|
16
|
+
# Runtime Agent Features
|
|
17
|
+
# =====================
|
|
16
18
|
# DeepAgents aligned feature: model ref for the underlying LLM used by `createDeepAgent(...)`.
|
|
17
19
|
modelRef: model/default
|
|
18
20
|
# Shared LangChain v1 / DeepAgents feature: checkpointer config passed into the upstream runtime.
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
export type ExecutionMode = "deepagent" | "langchain-v1";
|
|
2
2
|
export declare const AUTO_AGENT_ID = "auto";
|
|
3
|
+
export type RuntimeCapabilities = {
|
|
4
|
+
delegation?: boolean;
|
|
5
|
+
memory?: boolean;
|
|
6
|
+
};
|
|
3
7
|
export type RunState = "running" | "waiting_for_approval" | "resuming" | "completed" | "failed";
|
|
4
8
|
export type ParsedAgentObject = {
|
|
5
9
|
id: string;
|
|
6
10
|
executionMode: ExecutionMode;
|
|
11
|
+
capabilities?: RuntimeCapabilities;
|
|
7
12
|
description: string;
|
|
8
13
|
modelRef: string;
|
|
9
14
|
runRoot?: string;
|
|
@@ -175,6 +180,10 @@ export type CompiledTool = {
|
|
|
175
180
|
};
|
|
176
181
|
export type CompiledAgentBinding = {
|
|
177
182
|
agent: ParsedAgentObject;
|
|
183
|
+
adapter?: {
|
|
184
|
+
kind: string;
|
|
185
|
+
config: Record<string, unknown>;
|
|
186
|
+
};
|
|
178
187
|
langchainAgentParams?: LangChainAgentParams;
|
|
179
188
|
directAgentParams?: LangChainAgentParams;
|
|
180
189
|
deepAgentParams?: DeepAgentParams;
|
|
@@ -182,6 +191,7 @@ export type CompiledAgentBinding = {
|
|
|
182
191
|
runRoot: string;
|
|
183
192
|
workspaceRoot?: string;
|
|
184
193
|
hostFacing: boolean;
|
|
194
|
+
capabilities?: RuntimeCapabilities;
|
|
185
195
|
checkpointer?: Record<string, unknown> | boolean;
|
|
186
196
|
store?: Record<string, unknown>;
|
|
187
197
|
};
|
|
@@ -239,11 +249,14 @@ export type RunResult = {
|
|
|
239
249
|
runId: string;
|
|
240
250
|
state: RunState;
|
|
241
251
|
output: string;
|
|
252
|
+
finalMessageText?: string;
|
|
242
253
|
interruptContent?: string;
|
|
243
254
|
agentId?: string;
|
|
244
255
|
approvalId?: string;
|
|
245
256
|
pendingActionId?: string;
|
|
246
257
|
delegationId?: string;
|
|
258
|
+
artifacts?: ArtifactRecord[];
|
|
259
|
+
metadata?: Record<string, unknown>;
|
|
247
260
|
};
|
|
248
261
|
export type RunListeners = {
|
|
249
262
|
onChunk?: (chunk: string) => void | Promise<void>;
|
|
@@ -264,10 +277,17 @@ export type MessageContentPart = {
|
|
|
264
277
|
image_url: string;
|
|
265
278
|
};
|
|
266
279
|
export type MessageContent = string | MessageContentPart[];
|
|
280
|
+
export type InvocationEnvelope = {
|
|
281
|
+
context?: Record<string, unknown>;
|
|
282
|
+
inputs?: Record<string, unknown>;
|
|
283
|
+
attachments?: Record<string, unknown>;
|
|
284
|
+
capabilities?: Record<string, unknown>;
|
|
285
|
+
};
|
|
267
286
|
export type RunStartOptions = {
|
|
268
287
|
agentId?: string;
|
|
269
288
|
input: MessageContent;
|
|
270
289
|
threadId?: string;
|
|
290
|
+
invocation?: InvocationEnvelope;
|
|
271
291
|
context?: Record<string, unknown>;
|
|
272
292
|
state?: Record<string, unknown>;
|
|
273
293
|
files?: Record<string, unknown>;
|
|
@@ -285,6 +305,9 @@ export type RunOptions = RunStartOptions | RunDecisionOptions;
|
|
|
285
305
|
export type HarnessStreamItem = {
|
|
286
306
|
type: "event";
|
|
287
307
|
event: HarnessEvent;
|
|
308
|
+
} | {
|
|
309
|
+
type: "result";
|
|
310
|
+
result: RunResult;
|
|
288
311
|
} | {
|
|
289
312
|
type: "content";
|
|
290
313
|
threadId: string;
|
|
@@ -322,6 +345,7 @@ export type ThreadRunRecord = {
|
|
|
322
345
|
runId: string;
|
|
323
346
|
agentId: string;
|
|
324
347
|
executionMode: string;
|
|
348
|
+
adapterKind?: string;
|
|
325
349
|
createdAt: string;
|
|
326
350
|
updatedAt: string;
|
|
327
351
|
state: RunState;
|
|
@@ -362,13 +386,15 @@ export type ApprovalRecord = {
|
|
|
362
386
|
pendingActionId: string;
|
|
363
387
|
threadId: string;
|
|
364
388
|
runId: string;
|
|
365
|
-
toolCallId: string;
|
|
366
389
|
toolName: string;
|
|
367
390
|
status: "pending" | "approved" | "edited" | "rejected" | "expired";
|
|
368
391
|
requestedAt: string;
|
|
369
392
|
resolvedAt: string | null;
|
|
370
393
|
allowedDecisions: Array<"approve" | "edit" | "reject">;
|
|
371
394
|
inputPreview: Record<string, unknown>;
|
|
395
|
+
};
|
|
396
|
+
export type InternalApprovalRecord = ApprovalRecord & {
|
|
397
|
+
toolCallId: string;
|
|
372
398
|
checkpointRef: string;
|
|
373
399
|
eventRefs: string[];
|
|
374
400
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.42";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.42";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ArtifactListing, ArtifactRecord, DelegationRecord, HarnessEvent, InternalApprovalRecord, RunState, ThreadSummary, ThreadRunRecord, TranscriptMessage } from "../contracts/types.js";
|
|
2
2
|
type ThreadMeta = {
|
|
3
3
|
threadId: string;
|
|
4
4
|
workspaceId: string;
|
|
@@ -13,6 +13,7 @@ type RunMeta = {
|
|
|
13
13
|
threadId: string;
|
|
14
14
|
agentId: string;
|
|
15
15
|
executionMode: string;
|
|
16
|
+
adapterKind?: string;
|
|
16
17
|
createdAt: string;
|
|
17
18
|
updatedAt: string;
|
|
18
19
|
};
|
|
@@ -56,6 +57,7 @@ export declare class FilePersistence {
|
|
|
56
57
|
runId: string;
|
|
57
58
|
agentId: string;
|
|
58
59
|
executionMode: string;
|
|
60
|
+
adapterKind?: string;
|
|
59
61
|
createdAt: string;
|
|
60
62
|
}): Promise<void>;
|
|
61
63
|
setRunState(threadId: string, runId: string, state: RunState, checkpointRef?: string | null): Promise<void>;
|
|
@@ -66,14 +68,14 @@ export declare class FilePersistence {
|
|
|
66
68
|
getThreadMeta(threadId: string): Promise<ThreadMeta | null>;
|
|
67
69
|
listThreadRuns(threadId: string): Promise<ThreadRunRecord[]>;
|
|
68
70
|
listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
|
|
69
|
-
listApprovals(): Promise<
|
|
70
|
-
getApproval(approvalId: string): Promise<
|
|
71
|
-
getRunApprovals(threadId: string, runId: string): Promise<
|
|
71
|
+
listApprovals(): Promise<InternalApprovalRecord[]>;
|
|
72
|
+
getApproval(approvalId: string): Promise<InternalApprovalRecord | null>;
|
|
73
|
+
getRunApprovals(threadId: string, runId: string): Promise<InternalApprovalRecord[]>;
|
|
72
74
|
getRunMeta(threadId: string, runId: string): Promise<RunMeta>;
|
|
73
75
|
getRunLifecycle(threadId: string, runId: string): Promise<Lifecycle>;
|
|
74
76
|
listDelegations(): Promise<DelegationRecord[]>;
|
|
75
|
-
createApproval(record:
|
|
76
|
-
resolveApproval(threadId: string, runId: string, approvalId: string, status:
|
|
77
|
+
createApproval(record: InternalApprovalRecord): Promise<void>;
|
|
78
|
+
resolveApproval(threadId: string, runId: string, approvalId: string, status: InternalApprovalRecord["status"]): Promise<InternalApprovalRecord>;
|
|
77
79
|
createDelegation(record: DelegationRecord): Promise<void>;
|
|
78
80
|
updateDelegation(threadId: string, runId: string, delegationId: string, patch: Partial<DelegationRecord>): Promise<DelegationRecord>;
|
|
79
81
|
createArtifact(threadId: string, runId: string, artifact: ArtifactRecord, content: unknown): Promise<ArtifactRecord>;
|
|
@@ -54,6 +54,7 @@ export class FilePersistence {
|
|
|
54
54
|
threadId: input.threadId,
|
|
55
55
|
agentId: input.agentId,
|
|
56
56
|
executionMode: input.executionMode,
|
|
57
|
+
adapterKind: input.adapterKind ?? input.executionMode,
|
|
57
58
|
createdAt: input.createdAt,
|
|
58
59
|
updatedAt: input.createdAt,
|
|
59
60
|
};
|
|
@@ -218,6 +219,7 @@ export class FilePersistence {
|
|
|
218
219
|
runId: meta.runId,
|
|
219
220
|
agentId: meta.agentId,
|
|
220
221
|
executionMode: meta.executionMode,
|
|
222
|
+
adapterKind: meta.adapterKind ?? meta.executionMode,
|
|
221
223
|
createdAt: meta.createdAt,
|
|
222
224
|
updatedAt: meta.updatedAt,
|
|
223
225
|
state: lifecycle.state,
|
|
@@ -27,6 +27,7 @@ export declare class AgentRuntimeAdapter {
|
|
|
27
27
|
private buildToolNameMapping;
|
|
28
28
|
private buildAgentMessages;
|
|
29
29
|
private buildInvocationRequest;
|
|
30
|
+
private buildStateSnapshot;
|
|
30
31
|
private buildRawModelMessages;
|
|
31
32
|
private resolveTools;
|
|
32
33
|
private normalizeInterruptPolicy;
|
|
@@ -12,8 +12,9 @@ import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, ex
|
|
|
12
12
|
import { wrapToolForExecution } from "./tool-hitl.js";
|
|
13
13
|
import { resolveDeclaredMiddleware } from "./declared-middleware.js";
|
|
14
14
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
15
|
+
import { getBindingDeepAgentParams, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingMiddlewareConfigs, getBindingModelInit, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
15
16
|
function countConfiguredTools(binding) {
|
|
16
|
-
return binding.
|
|
17
|
+
return getBindingPrimaryTools(binding).length;
|
|
17
18
|
}
|
|
18
19
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
19
20
|
const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -142,6 +143,11 @@ function hasCallableToolHandler(value) {
|
|
|
142
143
|
const typed = value;
|
|
143
144
|
return typeof typed.invoke === "function" || typeof typed.call === "function" || typeof typed.func === "function";
|
|
144
145
|
}
|
|
146
|
+
function asRecord(value) {
|
|
147
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
148
|
+
? { ...value }
|
|
149
|
+
: undefined;
|
|
150
|
+
}
|
|
145
151
|
function normalizeResolvedToolSchema(resolvedTool) {
|
|
146
152
|
const schema = resolvedTool.schema;
|
|
147
153
|
if (schema && typeof schema.parse === "function" && "_def" in schema) {
|
|
@@ -172,10 +178,10 @@ export class AgentRuntimeAdapter {
|
|
|
172
178
|
this.options = options;
|
|
173
179
|
}
|
|
174
180
|
resolveBindingTimeout(binding) {
|
|
175
|
-
return resolveTimeoutMs(binding
|
|
181
|
+
return resolveTimeoutMs(getBindingModelInit(binding)?.timeout);
|
|
176
182
|
}
|
|
177
183
|
resolveStreamIdleTimeout(binding) {
|
|
178
|
-
const configuredIdleTimeout = resolveTimeoutMs(binding
|
|
184
|
+
const configuredIdleTimeout = resolveTimeoutMs(getBindingModelInit(binding)?.streamIdleTimeout);
|
|
179
185
|
if (configuredIdleTimeout) {
|
|
180
186
|
return configuredIdleTimeout;
|
|
181
187
|
}
|
|
@@ -284,35 +290,38 @@ export class AgentRuntimeAdapter {
|
|
|
284
290
|
};
|
|
285
291
|
}
|
|
286
292
|
applyStrictToolJsonInstruction(binding) {
|
|
287
|
-
if (binding
|
|
293
|
+
if (isLangChainBinding(binding)) {
|
|
294
|
+
const params = getBindingLangChainParams(binding);
|
|
288
295
|
return {
|
|
289
296
|
...binding,
|
|
290
297
|
langchainAgentParams: {
|
|
291
|
-
...
|
|
292
|
-
systemPrompt: [
|
|
298
|
+
...params,
|
|
299
|
+
systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
|
|
293
300
|
},
|
|
294
301
|
};
|
|
295
302
|
}
|
|
296
|
-
if (binding
|
|
303
|
+
if (isDeepAgentBinding(binding)) {
|
|
304
|
+
const params = getBindingDeepAgentParams(binding);
|
|
297
305
|
return {
|
|
298
306
|
...binding,
|
|
299
307
|
deepAgentParams: {
|
|
300
|
-
...
|
|
301
|
-
systemPrompt: [
|
|
308
|
+
...params,
|
|
309
|
+
systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
|
|
302
310
|
},
|
|
303
311
|
};
|
|
304
312
|
}
|
|
305
313
|
return binding;
|
|
306
314
|
}
|
|
307
315
|
async synthesizeDeepAgentAnswer(binding, input, result) {
|
|
308
|
-
|
|
316
|
+
const params = getBindingDeepAgentParams(binding);
|
|
317
|
+
if (!params) {
|
|
309
318
|
return "";
|
|
310
319
|
}
|
|
311
320
|
const toolContext = extractToolFallbackContext(result);
|
|
312
321
|
if (!toolContext) {
|
|
313
322
|
return "";
|
|
314
323
|
}
|
|
315
|
-
const model = (await this.resolveModel(
|
|
324
|
+
const model = (await this.resolveModel(params.model));
|
|
316
325
|
if (!model?.invoke) {
|
|
317
326
|
return "";
|
|
318
327
|
}
|
|
@@ -365,6 +374,14 @@ export class AgentRuntimeAdapter {
|
|
|
365
374
|
messages: this.buildAgentMessages(history, input),
|
|
366
375
|
};
|
|
367
376
|
}
|
|
377
|
+
buildStateSnapshot(result) {
|
|
378
|
+
const snapshot = { ...result };
|
|
379
|
+
delete snapshot.messages;
|
|
380
|
+
delete snapshot.__interrupt__;
|
|
381
|
+
delete snapshot.structuredResponse;
|
|
382
|
+
delete snapshot.files;
|
|
383
|
+
return Object.keys(snapshot).length > 0 ? snapshot : undefined;
|
|
384
|
+
}
|
|
368
385
|
buildRawModelMessages(systemPrompt, history, input) {
|
|
369
386
|
const messages = [];
|
|
370
387
|
if (systemPrompt) {
|
|
@@ -424,14 +441,10 @@ export class AgentRuntimeAdapter {
|
|
|
424
441
|
return compiled.size > 0 ? Object.fromEntries(compiled.entries()) : undefined;
|
|
425
442
|
}
|
|
426
443
|
resolveInterruptOn(binding) {
|
|
427
|
-
|
|
428
|
-
return this.compileInterruptOn(binding.deepAgentParams.tools, binding.deepAgentParams.interruptOn);
|
|
429
|
-
}
|
|
430
|
-
return this.compileInterruptOn(binding.langchainAgentParams?.tools ?? [], binding.agent.langchainAgentConfig?.interruptOn);
|
|
444
|
+
return this.compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding));
|
|
431
445
|
}
|
|
432
446
|
async resolveMiddleware(binding, interruptOn) {
|
|
433
|
-
const declarativeMiddleware = await resolveDeclaredMiddleware(binding
|
|
434
|
-
binding.deepAgentParams?.middleware, {
|
|
447
|
+
const declarativeMiddleware = await resolveDeclaredMiddleware(getBindingMiddlewareConfigs(binding), {
|
|
435
448
|
resolveModel: (model) => this.resolveModel(model),
|
|
436
449
|
resolveCustom: this.options.declaredMiddlewareResolver,
|
|
437
450
|
binding,
|
|
@@ -484,30 +497,31 @@ export class AgentRuntimeAdapter {
|
|
|
484
497
|
})));
|
|
485
498
|
}
|
|
486
499
|
async create(binding) {
|
|
487
|
-
if (binding
|
|
500
|
+
if (isLangChainBinding(binding)) {
|
|
501
|
+
const params = getBindingLangChainParams(binding);
|
|
488
502
|
const interruptOn = this.resolveInterruptOn(binding);
|
|
489
|
-
const model = (await this.resolveModel(
|
|
490
|
-
const tools = this.resolveTools(
|
|
503
|
+
const model = (await this.resolveModel(params.model));
|
|
504
|
+
const tools = this.resolveTools(params.tools, binding);
|
|
491
505
|
if (tools.length > 0 && typeof model.bindTools !== "function") {
|
|
492
|
-
throw new Error(`Agent ${binding.agent.id} configures ${tools.length} tool(s), but resolved model ${
|
|
506
|
+
throw new Error(`Agent ${binding.agent.id} configures ${tools.length} tool(s), but resolved model ${params.model.id} does not support tool binding.`);
|
|
493
507
|
}
|
|
494
508
|
return createAgent({
|
|
495
509
|
model: model,
|
|
496
510
|
tools: tools,
|
|
497
|
-
systemPrompt:
|
|
498
|
-
stateSchema:
|
|
499
|
-
responseFormat:
|
|
500
|
-
contextSchema:
|
|
511
|
+
systemPrompt: params.systemPrompt,
|
|
512
|
+
stateSchema: params.stateSchema,
|
|
513
|
+
responseFormat: params.responseFormat,
|
|
514
|
+
contextSchema: params.contextSchema,
|
|
501
515
|
middleware: (await this.resolveMiddleware(binding, interruptOn)),
|
|
502
516
|
checkpointer: this.resolveCheckpointer(binding),
|
|
503
517
|
store: this.options.storeResolver?.(binding),
|
|
504
|
-
includeAgentName:
|
|
505
|
-
version:
|
|
506
|
-
name:
|
|
507
|
-
description:
|
|
518
|
+
includeAgentName: params.includeAgentName,
|
|
519
|
+
version: params.version,
|
|
520
|
+
name: params.name,
|
|
521
|
+
description: params.description,
|
|
508
522
|
});
|
|
509
523
|
}
|
|
510
|
-
const params = binding
|
|
524
|
+
const params = getBindingDeepAgentParams(binding);
|
|
511
525
|
if (!params) {
|
|
512
526
|
throw new Error(`Agent ${binding.agent.id} has no runnable params`);
|
|
513
527
|
}
|
|
@@ -532,10 +546,8 @@ export class AgentRuntimeAdapter {
|
|
|
532
546
|
return createDeepAgent(deepAgentConfig);
|
|
533
547
|
}
|
|
534
548
|
async route(input, primaryBinding, secondaryBinding, options = {}) {
|
|
535
|
-
const routeModelConfig = primaryBinding
|
|
536
|
-
|
|
537
|
-
secondaryBinding.langchainAgentParams?.model ??
|
|
538
|
-
secondaryBinding.deepAgentParams?.model;
|
|
549
|
+
const routeModelConfig = getBindingPrimaryModel(primaryBinding) ??
|
|
550
|
+
getBindingPrimaryModel(secondaryBinding);
|
|
539
551
|
if (!routeModelConfig) {
|
|
540
552
|
throw new Error("No router model configuration available");
|
|
541
553
|
}
|
|
@@ -587,13 +599,21 @@ export class AgentRuntimeAdapter {
|
|
|
587
599
|
throw new Error(emptyAssistantMessageFailure);
|
|
588
600
|
}
|
|
589
601
|
const output = visibleOutput || synthesizedOutput || toolFallback || JSON.stringify(result, null, 2);
|
|
602
|
+
const finalMessageText = sanitizeVisibleText(output);
|
|
590
603
|
return {
|
|
591
604
|
threadId,
|
|
592
605
|
runId,
|
|
593
606
|
agentId: binding.agent.id,
|
|
594
607
|
state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? "waiting_for_approval" : "completed",
|
|
595
608
|
interruptContent,
|
|
596
|
-
output:
|
|
609
|
+
output: finalMessageText,
|
|
610
|
+
finalMessageText,
|
|
611
|
+
metadata: {
|
|
612
|
+
...(result.structuredResponse !== undefined ? { structuredResponse: result.structuredResponse } : {}),
|
|
613
|
+
...(asRecord(result.files) ? { files: asRecord(result.files) } : {}),
|
|
614
|
+
...(this.buildStateSnapshot(result) ? { stateSnapshot: this.buildStateSnapshot(result) } : {}),
|
|
615
|
+
upstreamResult: result,
|
|
616
|
+
},
|
|
597
617
|
};
|
|
598
618
|
}
|
|
599
619
|
async *stream(binding, input, threadId, history = [], options = {}) {
|
|
@@ -601,9 +621,9 @@ export class AgentRuntimeAdapter {
|
|
|
601
621
|
const invokeTimeoutMs = this.resolveBindingTimeout(binding);
|
|
602
622
|
const streamIdleTimeoutMs = this.resolveStreamIdleTimeout(binding);
|
|
603
623
|
const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
|
|
604
|
-
if (binding
|
|
605
|
-
const langchainParams = binding
|
|
606
|
-
const resolvedModel = (await this.resolveModel(
|
|
624
|
+
if (isLangChainBinding(binding)) {
|
|
625
|
+
const langchainParams = getBindingLangChainParams(binding);
|
|
626
|
+
const resolvedModel = (await this.resolveModel(langchainParams.model));
|
|
607
627
|
const tools = this.resolveTools(langchainParams.tools, binding);
|
|
608
628
|
const canUseDirectModelStream = tools.length === 0 || typeof resolvedModel.bindTools !== "function";
|
|
609
629
|
const model = canUseDirectModelStream
|
|
@@ -615,7 +635,7 @@ export class AgentRuntimeAdapter {
|
|
|
615
635
|
// agent loop and only adds an extra model round-trip before the runnable path.
|
|
616
636
|
if (canUseDirectModelStream && typeof model.stream === "function") {
|
|
617
637
|
let emitted = false;
|
|
618
|
-
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(
|
|
638
|
+
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(getBindingSystemPrompt(binding), history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
|
|
619
639
|
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream", streamDeadlineAt, invokeTimeoutMs)) {
|
|
620
640
|
const delta = readStreamDelta(chunk);
|
|
621
641
|
if (delta) {
|
|
@@ -637,7 +657,7 @@ export class AgentRuntimeAdapter {
|
|
|
637
657
|
const request = this.buildInvocationRequest(history, input, options);
|
|
638
658
|
if (typeof runnable.streamEvents === "function") {
|
|
639
659
|
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2", ...(options.context ? { context: options.context } : {}) }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
|
|
640
|
-
const allowVisibleStreamDeltas =
|
|
660
|
+
const allowVisibleStreamDeltas = isLangChainBinding(binding);
|
|
641
661
|
let emittedOutput = "";
|
|
642
662
|
let emittedToolError = false;
|
|
643
663
|
const seenTerminalOutputs = new Set();
|
|
@@ -662,7 +682,7 @@ export class AgentRuntimeAdapter {
|
|
|
662
682
|
}
|
|
663
683
|
}
|
|
664
684
|
}
|
|
665
|
-
if (binding
|
|
685
|
+
if (isDeepAgentBinding(binding)) {
|
|
666
686
|
const stateStreamOutput = extractStateStreamOutput(event);
|
|
667
687
|
if (stateStreamOutput) {
|
|
668
688
|
const nextOutput = computeIncrementalOutput(emittedOutput, sanitizeVisibleText(stateStreamOutput));
|
|
@@ -702,7 +722,7 @@ export class AgentRuntimeAdapter {
|
|
|
702
722
|
return;
|
|
703
723
|
}
|
|
704
724
|
}
|
|
705
|
-
if (binding
|
|
725
|
+
if (isLangChainBinding(binding) && typeof runnable.stream === "function") {
|
|
706
726
|
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent stream start", "stream");
|
|
707
727
|
let emitted = false;
|
|
708
728
|
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs)) {
|
|
@@ -24,6 +24,8 @@ export declare class AgentHarnessRuntime {
|
|
|
24
24
|
private readonly concurrencyConfig;
|
|
25
25
|
private activeRunSlots;
|
|
26
26
|
private readonly pendingRunSlots;
|
|
27
|
+
private toPublicApprovalRecord;
|
|
28
|
+
private normalizeInvocationEnvelope;
|
|
27
29
|
private listHostBindings;
|
|
28
30
|
private defaultRunRoot;
|
|
29
31
|
private heuristicRoute;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -15,6 +15,7 @@ import { FileBackedStore } from "./store.js";
|
|
|
15
15
|
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
16
16
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
17
17
|
import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
|
|
18
|
+
import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig } from "./support/compiled-binding.js";
|
|
18
19
|
export class AgentHarnessRuntime {
|
|
19
20
|
workspace;
|
|
20
21
|
runtimeAdapterOptions;
|
|
@@ -39,6 +40,19 @@ export class AgentHarnessRuntime {
|
|
|
39
40
|
concurrencyConfig;
|
|
40
41
|
activeRunSlots = 0;
|
|
41
42
|
pendingRunSlots = [];
|
|
43
|
+
toPublicApprovalRecord(approval) {
|
|
44
|
+
const { toolCallId: _toolCallId, checkpointRef: _checkpointRef, eventRefs: _eventRefs, ...publicApproval } = approval;
|
|
45
|
+
return publicApproval;
|
|
46
|
+
}
|
|
47
|
+
normalizeInvocationEnvelope(options) {
|
|
48
|
+
const invocation = options.invocation;
|
|
49
|
+
return {
|
|
50
|
+
context: invocation?.context ?? options.context,
|
|
51
|
+
state: invocation?.inputs ?? options.state,
|
|
52
|
+
files: invocation?.attachments ?? options.files,
|
|
53
|
+
invocation,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
42
56
|
listHostBindings() {
|
|
43
57
|
return inferRoutingBindings(this.workspace).hostBindings;
|
|
44
58
|
}
|
|
@@ -79,7 +93,7 @@ export class AgentHarnessRuntime {
|
|
|
79
93
|
return requestedAgentId;
|
|
80
94
|
}
|
|
81
95
|
resolveStore(binding) {
|
|
82
|
-
const storeConfig = binding
|
|
96
|
+
const storeConfig = binding ? getBindingStoreConfig(binding) : undefined;
|
|
83
97
|
const cacheKey = storeConfig ? JSON.stringify(storeConfig) : undefined;
|
|
84
98
|
if (!storeConfig) {
|
|
85
99
|
return this.defaultStore;
|
|
@@ -184,7 +198,7 @@ export class AgentHarnessRuntime {
|
|
|
184
198
|
if (!binding) {
|
|
185
199
|
throw new Error(`Unknown agent ${agentId}`);
|
|
186
200
|
}
|
|
187
|
-
return binding
|
|
201
|
+
return getBindingPrimaryTools(binding);
|
|
188
202
|
}
|
|
189
203
|
resolveAgentTools(agentId) {
|
|
190
204
|
const binding = this.getBinding(agentId);
|
|
@@ -259,10 +273,11 @@ export class AgentHarnessRuntime {
|
|
|
259
273
|
return false;
|
|
260
274
|
}
|
|
261
275
|
return true;
|
|
262
|
-
});
|
|
276
|
+
}).map((approval) => this.toPublicApprovalRecord(approval));
|
|
263
277
|
}
|
|
264
278
|
async getApproval(approvalId) {
|
|
265
|
-
|
|
279
|
+
const approval = await this.persistence.getApproval(approvalId);
|
|
280
|
+
return approval ? this.toPublicApprovalRecord(approval) : null;
|
|
266
281
|
}
|
|
267
282
|
async createToolMcpServer(options) {
|
|
268
283
|
const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
|
|
@@ -340,7 +355,8 @@ export class AgentHarnessRuntime {
|
|
|
340
355
|
threadId,
|
|
341
356
|
runId,
|
|
342
357
|
agentId: binding.agent.id,
|
|
343
|
-
executionMode: binding
|
|
358
|
+
executionMode: getBindingAdapterKind(binding),
|
|
359
|
+
adapterKind: getBindingAdapterKind(binding),
|
|
344
360
|
createdAt,
|
|
345
361
|
});
|
|
346
362
|
return { threadId, runId, createdAt };
|
|
@@ -500,6 +516,7 @@ export class AgentHarnessRuntime {
|
|
|
500
516
|
}
|
|
501
517
|
async dispatchRunListeners(stream, listeners) {
|
|
502
518
|
let latestEvent;
|
|
519
|
+
let latestResult;
|
|
503
520
|
let output = "";
|
|
504
521
|
for await (const item of stream) {
|
|
505
522
|
if (item.type === "event") {
|
|
@@ -507,6 +524,10 @@ export class AgentHarnessRuntime {
|
|
|
507
524
|
await this.notifyListener(listeners.onEvent, item.event);
|
|
508
525
|
continue;
|
|
509
526
|
}
|
|
527
|
+
if (item.type === "result") {
|
|
528
|
+
latestResult = item.result;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
510
531
|
if (item.type === "content") {
|
|
511
532
|
output += item.content;
|
|
512
533
|
await this.notifyListener(listeners.onChunk, item.content);
|
|
@@ -531,6 +552,13 @@ export class AgentHarnessRuntime {
|
|
|
531
552
|
if (!latestEvent) {
|
|
532
553
|
throw new Error("run did not emit any events");
|
|
533
554
|
}
|
|
555
|
+
if (latestResult) {
|
|
556
|
+
return {
|
|
557
|
+
...latestResult,
|
|
558
|
+
output: latestResult.output || output,
|
|
559
|
+
finalMessageText: latestResult.finalMessageText ?? latestResult.output ?? output,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
534
562
|
const thread = await this.getThread(latestEvent.threadId);
|
|
535
563
|
if (!thread) {
|
|
536
564
|
throw new Error(`Unknown thread ${latestEvent.threadId}`);
|
|
@@ -561,6 +589,7 @@ export class AgentHarnessRuntime {
|
|
|
561
589
|
}
|
|
562
590
|
const releaseRunSlot = await this.acquireRunSlot();
|
|
563
591
|
try {
|
|
592
|
+
const invocation = this.normalizeInvocationEnvelope(options);
|
|
564
593
|
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
565
594
|
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
566
595
|
if (!binding) {
|
|
@@ -575,13 +604,13 @@ export class AgentHarnessRuntime {
|
|
|
575
604
|
agentId: binding.agent.id,
|
|
576
605
|
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
577
606
|
selectedAgentId,
|
|
578
|
-
executionMode: binding
|
|
607
|
+
executionMode: getBindingAdapterKind(binding),
|
|
579
608
|
});
|
|
580
609
|
try {
|
|
581
610
|
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId, undefined, {
|
|
582
|
-
context:
|
|
583
|
-
state:
|
|
584
|
-
files:
|
|
611
|
+
context: invocation.context,
|
|
612
|
+
state: invocation.state,
|
|
613
|
+
files: invocation.files,
|
|
585
614
|
});
|
|
586
615
|
const finalized = await this.finalizeContinuedRun(threadId, runId, options.input, actual, {
|
|
587
616
|
previousState: null,
|
|
@@ -615,6 +644,7 @@ export class AgentHarnessRuntime {
|
|
|
615
644
|
async *streamEvents(options) {
|
|
616
645
|
const releaseRunSlot = await this.acquireRunSlot();
|
|
617
646
|
try {
|
|
647
|
+
const invocation = this.normalizeInvocationEnvelope(options);
|
|
618
648
|
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
619
649
|
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
620
650
|
if (!binding) {
|
|
@@ -644,9 +674,9 @@ export class AgentHarnessRuntime {
|
|
|
644
674
|
let assistantOutput = "";
|
|
645
675
|
const toolErrors = [];
|
|
646
676
|
for await (const chunk of this.runtimeAdapter.stream(binding, options.input, threadId, priorHistory, {
|
|
647
|
-
context:
|
|
648
|
-
state:
|
|
649
|
-
files:
|
|
677
|
+
context: invocation.context,
|
|
678
|
+
state: invocation.state,
|
|
679
|
+
files: invocation.files,
|
|
650
680
|
})) {
|
|
651
681
|
if (chunk) {
|
|
652
682
|
const normalizedChunk = typeof chunk === "string"
|
|
@@ -669,6 +699,20 @@ export class AgentHarnessRuntime {
|
|
|
669
699
|
type: "event",
|
|
670
700
|
event: approvalRequest.event,
|
|
671
701
|
};
|
|
702
|
+
yield {
|
|
703
|
+
type: "result",
|
|
704
|
+
result: {
|
|
705
|
+
threadId,
|
|
706
|
+
runId,
|
|
707
|
+
agentId: selectedAgentId,
|
|
708
|
+
state: "waiting_for_approval",
|
|
709
|
+
output: assistantOutput,
|
|
710
|
+
finalMessageText: assistantOutput,
|
|
711
|
+
interruptContent: normalizedChunk.content,
|
|
712
|
+
approvalId: approvalRequest.approval.approvalId,
|
|
713
|
+
pendingActionId: approvalRequest.approval.pendingActionId,
|
|
714
|
+
},
|
|
715
|
+
};
|
|
672
716
|
return;
|
|
673
717
|
}
|
|
674
718
|
if (normalizedChunk.kind === "reasoning") {
|
|
@@ -728,6 +772,17 @@ export class AgentHarnessRuntime {
|
|
|
728
772
|
}
|
|
729
773
|
}
|
|
730
774
|
await this.appendAssistantMessage(threadId, runId, assistantOutput);
|
|
775
|
+
yield {
|
|
776
|
+
type: "result",
|
|
777
|
+
result: {
|
|
778
|
+
threadId,
|
|
779
|
+
runId,
|
|
780
|
+
agentId: selectedAgentId,
|
|
781
|
+
state: "completed",
|
|
782
|
+
output: assistantOutput,
|
|
783
|
+
finalMessageText: assistantOutput,
|
|
784
|
+
},
|
|
785
|
+
};
|
|
731
786
|
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "completed", {
|
|
732
787
|
previousState: null,
|
|
733
788
|
}) };
|
|
@@ -753,6 +808,17 @@ export class AgentHarnessRuntime {
|
|
|
753
808
|
agentId: selectedAgentId,
|
|
754
809
|
content: renderRuntimeFailure(error),
|
|
755
810
|
};
|
|
811
|
+
yield {
|
|
812
|
+
type: "result",
|
|
813
|
+
result: {
|
|
814
|
+
threadId,
|
|
815
|
+
runId,
|
|
816
|
+
agentId: selectedAgentId,
|
|
817
|
+
state: "failed",
|
|
818
|
+
output: renderRuntimeFailure(error),
|
|
819
|
+
finalMessageText: renderRuntimeFailure(error),
|
|
820
|
+
},
|
|
821
|
+
};
|
|
756
822
|
return;
|
|
757
823
|
}
|
|
758
824
|
try {
|
|
@@ -761,6 +827,15 @@ export class AgentHarnessRuntime {
|
|
|
761
827
|
if (actual.output) {
|
|
762
828
|
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, actual.output);
|
|
763
829
|
}
|
|
830
|
+
yield {
|
|
831
|
+
type: "result",
|
|
832
|
+
result: {
|
|
833
|
+
...actual,
|
|
834
|
+
threadId,
|
|
835
|
+
runId,
|
|
836
|
+
agentId: selectedAgentId,
|
|
837
|
+
},
|
|
838
|
+
};
|
|
764
839
|
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, actual.state, {
|
|
765
840
|
previousState: null,
|
|
766
841
|
}) };
|
|
@@ -779,6 +854,17 @@ export class AgentHarnessRuntime {
|
|
|
779
854
|
agentId: selectedAgentId,
|
|
780
855
|
content: renderRuntimeFailure(invokeError),
|
|
781
856
|
};
|
|
857
|
+
yield {
|
|
858
|
+
type: "result",
|
|
859
|
+
result: {
|
|
860
|
+
threadId,
|
|
861
|
+
runId,
|
|
862
|
+
agentId: selectedAgentId,
|
|
863
|
+
state: "failed",
|
|
864
|
+
output: renderRuntimeFailure(invokeError),
|
|
865
|
+
finalMessageText: renderRuntimeFailure(invokeError),
|
|
866
|
+
},
|
|
867
|
+
};
|
|
782
868
|
return;
|
|
783
869
|
}
|
|
784
870
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readSkillMetadata } from "./support/skill-metadata.js";
|
|
2
|
+
import { getBindingPrimaryTools } from "./support/compiled-binding.js";
|
|
2
3
|
function listHostBindings(workspace) {
|
|
3
4
|
return Array.from(workspace.bindings.values()).filter((binding) => binding.harnessRuntime.hostFacing);
|
|
4
5
|
}
|
|
@@ -31,7 +32,7 @@ export function listAgentTools(workspace, agentId) {
|
|
|
31
32
|
if (!binding) {
|
|
32
33
|
return [];
|
|
33
34
|
}
|
|
34
|
-
return dedupeTools(binding
|
|
35
|
+
return dedupeTools(getBindingPrimaryTools(binding));
|
|
35
36
|
}
|
|
36
37
|
export function listAgentSkills(workspace, agentId) {
|
|
37
38
|
const binding = findAgentBinding(workspace, agentId);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CompiledAgentBinding, CompiledModel, CompiledTool, DeepAgentParams, LangChainAgentParams } from "../../contracts/types.js";
|
|
2
|
+
export declare function getBindingAdapterKind(binding: CompiledAgentBinding): string;
|
|
3
|
+
export declare function getBindingAdapterConfig(binding: CompiledAgentBinding): Record<string, unknown>;
|
|
4
|
+
export declare function getBindingLangChainParams(binding: CompiledAgentBinding): LangChainAgentParams | undefined;
|
|
5
|
+
export declare function getBindingDeepAgentParams(binding: CompiledAgentBinding): DeepAgentParams | undefined;
|
|
6
|
+
export declare function isLangChainBinding(binding: CompiledAgentBinding): boolean;
|
|
7
|
+
export declare function isDeepAgentBinding(binding: CompiledAgentBinding): boolean;
|
|
8
|
+
export declare function getBindingPrimaryTools(binding: CompiledAgentBinding): CompiledTool[];
|
|
9
|
+
export declare function getBindingPrimaryModel(binding: CompiledAgentBinding): CompiledModel | undefined;
|
|
10
|
+
export declare function getBindingSystemPrompt(binding: CompiledAgentBinding): string | undefined;
|
|
11
|
+
export declare function getBindingMiddlewareConfigs(binding: CompiledAgentBinding): Array<Record<string, unknown>> | undefined;
|
|
12
|
+
export declare function getBindingInterruptCompatibilityRules(binding: CompiledAgentBinding): Record<string, boolean | object> | undefined;
|
|
13
|
+
export declare function getBindingModelInit(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
|
|
14
|
+
export declare function getBindingStoreConfig(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
|
|
15
|
+
export declare function bindingHasSubagents(binding: CompiledAgentBinding): boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function asRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: undefined;
|
|
5
|
+
}
|
|
6
|
+
export function getBindingAdapterKind(binding) {
|
|
7
|
+
return binding.adapter?.kind ?? binding.agent.executionMode;
|
|
8
|
+
}
|
|
9
|
+
export function getBindingAdapterConfig(binding) {
|
|
10
|
+
return binding.adapter?.config ?? {};
|
|
11
|
+
}
|
|
12
|
+
export function getBindingLangChainParams(binding) {
|
|
13
|
+
const adapterParams = asRecord(getBindingAdapterConfig(binding).params);
|
|
14
|
+
if (getBindingAdapterKind(binding) === "langchain-v1" && adapterParams) {
|
|
15
|
+
return adapterParams;
|
|
16
|
+
}
|
|
17
|
+
return binding.langchainAgentParams;
|
|
18
|
+
}
|
|
19
|
+
export function getBindingDeepAgentParams(binding) {
|
|
20
|
+
const adapterParams = asRecord(getBindingAdapterConfig(binding).params);
|
|
21
|
+
if (getBindingAdapterKind(binding) === "deepagent" && adapterParams) {
|
|
22
|
+
return adapterParams;
|
|
23
|
+
}
|
|
24
|
+
return binding.deepAgentParams;
|
|
25
|
+
}
|
|
26
|
+
export function isLangChainBinding(binding) {
|
|
27
|
+
return getBindingAdapterKind(binding) === "langchain-v1" || Boolean(binding.langchainAgentParams);
|
|
28
|
+
}
|
|
29
|
+
export function isDeepAgentBinding(binding) {
|
|
30
|
+
return getBindingAdapterKind(binding) === "deepagent" || Boolean(binding.deepAgentParams);
|
|
31
|
+
}
|
|
32
|
+
export function getBindingPrimaryTools(binding) {
|
|
33
|
+
return binding.langchainAgentParams?.tools ?? binding.deepAgentParams?.tools ?? [];
|
|
34
|
+
}
|
|
35
|
+
export function getBindingPrimaryModel(binding) {
|
|
36
|
+
return binding.langchainAgentParams?.model ?? binding.deepAgentParams?.model;
|
|
37
|
+
}
|
|
38
|
+
export function getBindingSystemPrompt(binding) {
|
|
39
|
+
return binding.langchainAgentParams?.systemPrompt ?? binding.deepAgentParams?.systemPrompt;
|
|
40
|
+
}
|
|
41
|
+
export function getBindingMiddlewareConfigs(binding) {
|
|
42
|
+
return binding.langchainAgentParams?.middleware ?? binding.deepAgentParams?.middleware;
|
|
43
|
+
}
|
|
44
|
+
export function getBindingInterruptCompatibilityRules(binding) {
|
|
45
|
+
return binding.deepAgentParams?.interruptOn ??
|
|
46
|
+
binding.agent.langchainAgentConfig?.interruptOn;
|
|
47
|
+
}
|
|
48
|
+
export function getBindingModelInit(binding) {
|
|
49
|
+
return getBindingPrimaryModel(binding)?.init;
|
|
50
|
+
}
|
|
51
|
+
export function getBindingStoreConfig(binding) {
|
|
52
|
+
return binding.deepAgentParams?.store ?? binding.harnessRuntime.store;
|
|
53
|
+
}
|
|
54
|
+
export function bindingHasSubagents(binding) {
|
|
55
|
+
return (binding.deepAgentParams?.subagents.length ?? 0) > 0;
|
|
56
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HarnessEvent, InternalApprovalRecord, WorkspaceBundle } from "../../contracts/types.js";
|
|
2
2
|
export declare function renderRuntimeFailure(error: unknown): string;
|
|
3
3
|
export declare function renderToolFailure(toolName: string, output: unknown): string;
|
|
4
4
|
export declare function parseInterruptContent(content: string): {
|
|
@@ -19,7 +19,7 @@ export declare function heuristicRoute(input: string, primaryBinding?: {
|
|
|
19
19
|
};
|
|
20
20
|
}): string;
|
|
21
21
|
export declare function createHarnessEvent(threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>, source?: HarnessEvent["source"]): HarnessEvent;
|
|
22
|
-
export declare function createPendingApproval(threadId: string, runId: string, checkpointRef: string, input: string, interruptContent?: string):
|
|
22
|
+
export declare function createPendingApproval(threadId: string, runId: string, checkpointRef: string, input: string, interruptContent?: string): InternalApprovalRecord;
|
|
23
23
|
export declare function inferRoutingBindings(workspace: WorkspaceBundle): {
|
|
24
24
|
primaryBinding: import("../../contracts/types.js").CompiledAgentBinding;
|
|
25
25
|
secondaryBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPersistentId } from "../../utils/id.js";
|
|
2
|
+
import { isDelegationCapableBinding } from "../../workspace/support/agent-capabilities.js";
|
|
2
3
|
export function renderRuntimeFailure(error) {
|
|
3
4
|
const message = error instanceof Error ? error.message : String(error);
|
|
4
5
|
return `runtime_error=${message}`.trim();
|
|
@@ -131,20 +132,21 @@ export function inferRoutingBindings(workspace) {
|
|
|
131
132
|
const hostBindings = Array.from(workspace.bindings.values()).filter((binding) => binding.harnessRuntime.hostFacing);
|
|
132
133
|
const researchBinding = hostBindings.find((binding) => binding.agent.id === "research-lite" || binding.agent.id === "research");
|
|
133
134
|
const directBinding = hostBindings.find((binding) => binding.agent.id === "direct");
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
135
|
+
const delegationHosts = hostBindings.filter((binding) => isDelegationCapableBinding(binding));
|
|
136
|
+
const lightweightHosts = hostBindings.filter((binding) => !isDelegationCapableBinding(binding));
|
|
137
|
+
const defaultOrchestratingHost = hostBindings.find((binding) => binding.agent.id === "orchestra") ??
|
|
138
|
+
delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
139
|
+
delegationHosts[0];
|
|
140
|
+
const delegationPreferredSecondary = delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
141
|
+
delegationHosts[0];
|
|
142
|
+
const genericLightweightHost = lightweightHosts.find((binding) => binding.agent.id !== researchBinding?.agent.id);
|
|
143
|
+
const primaryBinding = defaultOrchestratingHost ?? directBinding ?? genericLightweightHost ?? hostBindings[0];
|
|
144
|
+
const secondaryBinding = genericLightweightHost && genericLightweightHost.agent.id !== primaryBinding?.agent.id
|
|
145
|
+
? genericLightweightHost
|
|
144
146
|
: directBinding && directBinding.agent.id !== primaryBinding?.agent.id
|
|
145
147
|
? directBinding
|
|
146
|
-
:
|
|
147
|
-
?
|
|
148
|
+
: delegationPreferredSecondary && delegationPreferredSecondary.agent.id !== primaryBinding?.agent.id
|
|
149
|
+
? delegationPreferredSecondary
|
|
148
150
|
: hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id);
|
|
149
151
|
return { primaryBinding, secondaryBinding, researchBinding, hostBindings };
|
|
150
152
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { getSkillInheritancePolicy, resolveToolTargets } from "../extensions.js";
|
|
3
3
|
import { compileModel, compileTool } from "./resource-compilers.js";
|
|
4
|
+
import { inferAgentCapabilities } from "./support/agent-capabilities.js";
|
|
4
5
|
import { discoverSkillPaths } from "./support/discovery.js";
|
|
5
6
|
import { compileAgentMemories, getRuntimeDefaults, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
|
|
6
7
|
const WORKSPACE_BOUNDARY_GUIDANCE = "Keep repository and file exploration bounded to the current workspace root unless the user explicitly asks for broader host or filesystem access. " +
|
|
@@ -168,10 +169,21 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
168
169
|
: path.join(workspaceRoot, "run-data");
|
|
169
170
|
const base = {
|
|
170
171
|
agent,
|
|
172
|
+
adapter: {
|
|
173
|
+
kind: agent.executionMode,
|
|
174
|
+
config: agent.executionMode === "deepagent"
|
|
175
|
+
? {
|
|
176
|
+
deepAgent: true,
|
|
177
|
+
}
|
|
178
|
+
: {
|
|
179
|
+
langchainV1: true,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
171
182
|
harnessRuntime: {
|
|
172
183
|
runRoot,
|
|
173
184
|
workspaceRoot,
|
|
174
185
|
hostFacing: !internalSubagent,
|
|
186
|
+
capabilities: inferAgentCapabilities(agent),
|
|
175
187
|
...(checkpointer ? { checkpointer: checkpointer.config } : {}),
|
|
176
188
|
...(store ? { store: store.config } : {}),
|
|
177
189
|
},
|
|
@@ -194,37 +206,50 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
194
206
|
};
|
|
195
207
|
return {
|
|
196
208
|
...base,
|
|
209
|
+
adapter: {
|
|
210
|
+
kind: "langchain-v1",
|
|
211
|
+
config: {
|
|
212
|
+
params: langchainAgentParams,
|
|
213
|
+
},
|
|
214
|
+
},
|
|
197
215
|
langchainAgentParams,
|
|
198
216
|
directAgentParams: langchainAgentParams,
|
|
199
217
|
};
|
|
200
218
|
}
|
|
219
|
+
const deepAgentParams = {
|
|
220
|
+
model: compiledAgentModel,
|
|
221
|
+
tools: requireTools(tools, agent.toolRefs, agent.id),
|
|
222
|
+
systemPrompt: resolveSystemPrompt(agent),
|
|
223
|
+
responseFormat: agent.deepAgentConfig?.responseFormat,
|
|
224
|
+
contextSchema: agent.deepAgentConfig?.contextSchema,
|
|
225
|
+
middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
|
|
226
|
+
description: agent.description,
|
|
227
|
+
subagents: agent.subagentRefs.map((ref) => {
|
|
228
|
+
const subagent = agents.get(resolveRefId(ref));
|
|
229
|
+
if (!subagent) {
|
|
230
|
+
throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
|
|
231
|
+
}
|
|
232
|
+
return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
|
|
233
|
+
}),
|
|
234
|
+
interruptOn: resolveInterruptOn(agent),
|
|
235
|
+
...(backend ? { backend: backend.config } : {}),
|
|
236
|
+
...(store ? { store: store.config } : {}),
|
|
237
|
+
name: resolveAgentRuntimeName(agent),
|
|
238
|
+
memory: compiledAgentMemory,
|
|
239
|
+
skills: compiledAgentSkills,
|
|
240
|
+
generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
|
|
241
|
+
taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
|
|
242
|
+
? agent.deepAgentConfig.taskDescription
|
|
243
|
+
: undefined,
|
|
244
|
+
};
|
|
201
245
|
return {
|
|
202
246
|
...base,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
contextSchema: agent.deepAgentConfig?.contextSchema,
|
|
209
|
-
middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
|
|
210
|
-
description: agent.description,
|
|
211
|
-
subagents: agent.subagentRefs.map((ref) => {
|
|
212
|
-
const subagent = agents.get(resolveRefId(ref));
|
|
213
|
-
if (!subagent) {
|
|
214
|
-
throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
|
|
215
|
-
}
|
|
216
|
-
return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
|
|
217
|
-
}),
|
|
218
|
-
interruptOn: resolveInterruptOn(agent),
|
|
219
|
-
...(backend ? { backend: backend.config } : {}),
|
|
220
|
-
...(store ? { store: store.config } : {}),
|
|
221
|
-
name: resolveAgentRuntimeName(agent),
|
|
222
|
-
memory: compiledAgentMemory,
|
|
223
|
-
skills: compiledAgentSkills,
|
|
224
|
-
generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
|
|
225
|
-
taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
|
|
226
|
-
? agent.deepAgentConfig.taskDescription
|
|
227
|
-
: undefined,
|
|
247
|
+
adapter: {
|
|
248
|
+
kind: "deepagent",
|
|
249
|
+
config: {
|
|
250
|
+
params: deepAgentParams,
|
|
251
|
+
},
|
|
228
252
|
},
|
|
253
|
+
deepAgentParams,
|
|
229
254
|
};
|
|
230
255
|
}
|
|
@@ -88,6 +88,8 @@ function normalizeNamedResourceSpec(document, kind) {
|
|
|
88
88
|
}
|
|
89
89
|
function normalizeKind(kind) {
|
|
90
90
|
switch (kind) {
|
|
91
|
+
case "Agent":
|
|
92
|
+
return "agent";
|
|
91
93
|
case "LangChainAgent":
|
|
92
94
|
return "langchain-agent";
|
|
93
95
|
case "DeepAgent":
|
|
@@ -209,6 +211,37 @@ function readObjectArray(items) {
|
|
|
209
211
|
.map((item) => ({ ...item }));
|
|
210
212
|
return records.length > 0 ? records : undefined;
|
|
211
213
|
}
|
|
214
|
+
function readCapabilities(value) {
|
|
215
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
const typed = value;
|
|
219
|
+
const capabilities = {
|
|
220
|
+
...(typeof typed.delegation === "boolean" ? { delegation: typed.delegation } : {}),
|
|
221
|
+
...(typeof typed.memory === "boolean" ? { memory: typed.memory } : {}),
|
|
222
|
+
};
|
|
223
|
+
return Object.keys(capabilities).length > 0 ? capabilities : undefined;
|
|
224
|
+
}
|
|
225
|
+
function readExecutionConfig(value) {
|
|
226
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
227
|
+
? { ...value }
|
|
228
|
+
: undefined;
|
|
229
|
+
}
|
|
230
|
+
function resolveExecutionBackend(item, current) {
|
|
231
|
+
const execution = readExecutionConfig(item.execution) ?? readExecutionConfig(current?.execution);
|
|
232
|
+
const backend = typeof execution?.backend === "string"
|
|
233
|
+
? execution.backend.trim().toLowerCase()
|
|
234
|
+
: typeof execution?.mode === "string"
|
|
235
|
+
? execution.mode.trim().toLowerCase()
|
|
236
|
+
: undefined;
|
|
237
|
+
if (backend === "langchain-v1" || backend === "langchain" || backend === "langchain-agent") {
|
|
238
|
+
return "langchain-v1";
|
|
239
|
+
}
|
|
240
|
+
if (backend === "deepagent" || backend === "deepagents") {
|
|
241
|
+
return "deepagent";
|
|
242
|
+
}
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
212
245
|
function readSharedAgentConfig(item) {
|
|
213
246
|
const middleware = readMiddlewareArray(item.middleware);
|
|
214
247
|
return {
|
|
@@ -244,12 +277,16 @@ export function parseAgentItem(item, sourcePath) {
|
|
|
244
277
|
const subagentRefs = readRefArray(item.subagents);
|
|
245
278
|
const subagentPathRefs = readPathArray(item.subagents);
|
|
246
279
|
const kind = typeof item.kind === "string" ? item.kind : "agent";
|
|
247
|
-
const executionMode = String((
|
|
280
|
+
const executionMode = String(resolveExecutionBackend(item) ??
|
|
281
|
+
(kind === "langchain-agent" ? "langchain-v1" : undefined) ??
|
|
248
282
|
(kind === "deepagent" ? "deepagent" : undefined) ??
|
|
249
283
|
"deepagent");
|
|
250
284
|
return {
|
|
251
285
|
id: String(item.id),
|
|
252
286
|
executionMode: executionMode,
|
|
287
|
+
capabilities: readCapabilities(item.capabilities) ?? (executionMode === "deepagent"
|
|
288
|
+
? { delegation: true, memory: true }
|
|
289
|
+
: { delegation: false, memory: false }),
|
|
253
290
|
description: String(item.description ?? ""),
|
|
254
291
|
modelRef: readSingleRef(item.modelRef) ?? "",
|
|
255
292
|
runRoot: typeof item.runRoot === "string" ? item.runRoot : undefined,
|
|
@@ -469,7 +506,7 @@ async function readNamedModelItems(root) {
|
|
|
469
506
|
return records;
|
|
470
507
|
}
|
|
471
508
|
function isAgentKind(kind) {
|
|
472
|
-
return kind === "deepagent" || kind === "langchain-agent";
|
|
509
|
+
return kind === "deepagent" || kind === "langchain-agent" || kind === "agent";
|
|
473
510
|
}
|
|
474
511
|
async function readConfigAgentItems(configRoot) {
|
|
475
512
|
const records = await readYamlItems(configRoot, "agents", { recursive: true });
|
|
@@ -517,6 +554,10 @@ export async function readToolModuleItems(root) {
|
|
|
517
554
|
return records;
|
|
518
555
|
}
|
|
519
556
|
function inferExecutionMode(item, current) {
|
|
557
|
+
const explicitExecution = resolveExecutionBackend(item, current);
|
|
558
|
+
if (explicitExecution) {
|
|
559
|
+
return explicitExecution;
|
|
560
|
+
}
|
|
520
561
|
const kind = typeof item.kind === "string" ? item.kind : typeof current?.kind === "string" ? current.kind : undefined;
|
|
521
562
|
if (kind === "langchain-agent") {
|
|
522
563
|
return "langchain-v1";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompiledAgentBinding, ParsedAgentObject, RuntimeCapabilities } from "../../contracts/types.js";
|
|
2
|
+
export declare function inferAgentCapabilities(agent: ParsedAgentObject): RuntimeCapabilities;
|
|
3
|
+
export declare function inferBindingCapabilities(binding: CompiledAgentBinding): RuntimeCapabilities;
|
|
4
|
+
export declare function isDelegationCapableAgent(agent: ParsedAgentObject): boolean;
|
|
5
|
+
export declare function isMemoryCapableAgent(agent: ParsedAgentObject): boolean;
|
|
6
|
+
export declare function isDelegationCapableBinding(binding: CompiledAgentBinding): boolean;
|
|
7
|
+
export declare function isMemoryCapableBinding(binding: CompiledAgentBinding): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function normalizeCapabilities(capabilities) {
|
|
2
|
+
return {
|
|
3
|
+
delegation: capabilities?.delegation === true,
|
|
4
|
+
memory: capabilities?.memory === true,
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export function inferAgentCapabilities(agent) {
|
|
8
|
+
if (agent.capabilities) {
|
|
9
|
+
return normalizeCapabilities(agent.capabilities);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
delegation: agent.executionMode === "deepagent",
|
|
13
|
+
memory: agent.executionMode === "deepagent",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function inferBindingCapabilities(binding) {
|
|
17
|
+
return normalizeCapabilities(binding.harnessRuntime.capabilities ?? binding.agent.capabilities ?? inferAgentCapabilities(binding.agent));
|
|
18
|
+
}
|
|
19
|
+
export function isDelegationCapableAgent(agent) {
|
|
20
|
+
return inferAgentCapabilities(agent).delegation === true;
|
|
21
|
+
}
|
|
22
|
+
export function isMemoryCapableAgent(agent) {
|
|
23
|
+
return inferAgentCapabilities(agent).memory === true;
|
|
24
|
+
}
|
|
25
|
+
export function isDelegationCapableBinding(binding) {
|
|
26
|
+
return inferBindingCapabilities(binding).delegation === true;
|
|
27
|
+
}
|
|
28
|
+
export function isMemoryCapableBinding(binding) {
|
|
29
|
+
return inferBindingCapabilities(binding).memory === true;
|
|
30
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDelegationCapableAgent, isMemoryCapableAgent } from "./support/agent-capabilities.js";
|
|
1
2
|
const allowedExecutionModes = new Set(["deepagent", "langchain-v1"]);
|
|
2
3
|
function hasPromptContent(value) {
|
|
3
4
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -52,14 +53,14 @@ export function validateAgent(agent) {
|
|
|
52
53
|
if (!agent.description.trim()) {
|
|
53
54
|
throw new Error(`Agent ${agent.id} description must not be empty`);
|
|
54
55
|
}
|
|
55
|
-
if (agent
|
|
56
|
-
throw new Error(`Agent ${agent.id} cannot define subagents unless
|
|
56
|
+
if (!isDelegationCapableAgent(agent) && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
|
|
57
|
+
throw new Error(`Agent ${agent.id} cannot define subagents unless it uses a delegation-capable backend`);
|
|
57
58
|
}
|
|
58
|
-
if (agent
|
|
59
|
-
throw new Error(`Agent ${agent.id} cannot define memory unless
|
|
59
|
+
if (!isMemoryCapableAgent(agent) && agent.memorySources.length > 0) {
|
|
60
|
+
throw new Error(`Agent ${agent.id} cannot define memory unless it uses a memory-capable backend`);
|
|
60
61
|
}
|
|
61
|
-
if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && agent
|
|
62
|
-
throw new Error(`Agent ${agent.id} must use
|
|
62
|
+
if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && !isDelegationCapableAgent(agent)) {
|
|
63
|
+
throw new Error(`Agent ${agent.id} must use a delegation-capable backend when subagents are defined`);
|
|
63
64
|
}
|
|
64
65
|
validateCheckpointerConfig(agent);
|
|
65
66
|
validateMiddlewareConfig(agent);
|
|
@@ -83,8 +84,8 @@ export function validateTopology(agents) {
|
|
|
83
84
|
if (!referencedSubagentIds.has(agent.id)) {
|
|
84
85
|
continue;
|
|
85
86
|
}
|
|
86
|
-
if (agent
|
|
87
|
-
throw new Error(`Subagent ${agent.id} must use
|
|
87
|
+
if (!isDelegationCapableAgent(agent)) {
|
|
88
|
+
throw new Error(`Subagent ${agent.id} must use a delegation-capable backend`);
|
|
88
89
|
}
|
|
89
90
|
if (!hasPromptContent(agent.deepAgentConfig?.systemPrompt)) {
|
|
90
91
|
throw new Error(`Subagent ${agent.id} requires deepagent.systemPrompt`);
|