@botbotgo/agent-harness 0.0.109 → 0.0.111

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.
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.108";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.110";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.108";
1
+ export const AGENT_HARNESS_VERSION = "0.0.110";
@@ -16,6 +16,8 @@ export function finalizeInvocationResult(params) {
16
16
  const outputContent = extractOutputContent(result);
17
17
  const contentBlocks = extractContentBlocks(result);
18
18
  const structuredResponse = result.structuredResponse;
19
+ const files = asRecord(result.files);
20
+ const stateSnapshot = buildStateSnapshot(result);
19
21
  return {
20
22
  threadId,
21
23
  runId,
@@ -32,8 +34,8 @@ export function finalizeInvocationResult(params) {
32
34
  ...(structuredResponse !== undefined ? { structuredResponse } : {}),
33
35
  ...(outputContent !== undefined ? { outputContent } : {}),
34
36
  ...(contentBlocks.length > 0 ? { contentBlocks } : {}),
35
- ...(asRecord(result.files) ? { files: asRecord(result.files) } : {}),
36
- ...(buildStateSnapshot(result) ? { stateSnapshot: buildStateSnapshot(result) } : {}),
37
+ ...(files ? { files } : {}),
38
+ ...(stateSnapshot ? { stateSnapshot } : {}),
37
39
  upstreamResult: result,
38
40
  },
39
41
  };
@@ -1,22 +1,19 @@
1
- import { getBindingExecutionView } from "../support/compiled-binding.js";
1
+ import { bindingHasLangChainSubagentSupport, bindingHasMiddlewareKind, getBindingToolCount, } from "../support/compiled-binding.js";
2
+ import { isRecord as isRecordValue } from "../../utils/object.js";
2
3
  export function countConfiguredTools(binding) {
3
- return getBindingExecutionView(binding).primaryTools.length;
4
+ return getBindingToolCount(binding);
4
5
  }
5
6
  export function sleep(ms) {
6
7
  return new Promise((resolve) => setTimeout(resolve, ms));
7
8
  }
8
9
  export function hasConfiguredSubagentSupport(binding) {
9
- const params = getBindingExecutionView(binding).langchainParams;
10
- if (!params) {
11
- return false;
12
- }
13
- return (params.subagents?.length ?? 0) > 0 || params.generalPurposeAgent === true || Boolean(params.taskDescription?.trim());
10
+ return bindingHasLangChainSubagentSupport(binding);
14
11
  }
15
12
  export function hasConfiguredMiddlewareKind(binding, kind) {
16
- return getBindingExecutionView(binding).middlewareConfigs?.some((entry) => entry.kind === kind) ?? false;
13
+ return bindingHasMiddlewareKind(binding, kind);
17
14
  }
18
15
  export function isRecord(value) {
19
- return typeof value === "object" && value !== null && !Array.isArray(value);
16
+ return isRecordValue(value);
20
17
  }
21
18
  export function isObject(value) {
22
19
  return isRecord(value);
@@ -1,7 +1,5 @@
1
1
  import { salvageToolArgs } from "../../parsing/output-parsing.js";
2
- function isRecord(value) {
3
- return typeof value === "object" && value !== null && !Array.isArray(value);
4
- }
2
+ import { isRecord } from "../../../utils/object.js";
5
3
  function isObject(value) {
6
4
  return isRecord(value);
7
5
  }
@@ -30,6 +30,7 @@ export declare class AgentRuntimeAdapter {
30
30
  private resolveFilesystemBackend;
31
31
  private resolveBuiltinMiddlewareBackend;
32
32
  private createDeclaredMiddlewareResolverOptions;
33
+ private createAssemblyResolvers;
33
34
  private invokeBuiltinTaskTool;
34
35
  private resolveBuiltinMiddlewareTools;
35
36
  private resolveAutomaticSummarizationMiddleware;
@@ -149,57 +149,76 @@ export class AgentRuntimeAdapter {
149
149
  binding,
150
150
  };
151
151
  }
152
+ createAssemblyResolvers(binding, options = {}) {
153
+ return {
154
+ resolveModel: (model) => this.resolveModel(model),
155
+ resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
156
+ resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding),
157
+ createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
158
+ resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions = {}) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
159
+ resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
160
+ invokeBuiltinTaskTool: (currentBinding, toolInput, currentOptions = {}) => this.invokeBuiltinTaskTool(currentBinding, toolInput, currentOptions),
161
+ binding,
162
+ options,
163
+ };
164
+ }
152
165
  async invokeBuiltinTaskTool(binding, input, options = {}) {
166
+ const assembly = this.createAssemblyResolvers(binding, options);
153
167
  return invokeBuiltinTaskToolHelper({
154
168
  binding,
155
169
  toolInput: input,
156
170
  options,
157
- resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
158
- resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
159
- resolveModel: (model) => this.resolveModel(model),
160
- resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
171
+ resolveBuiltinMiddlewareBackend: assembly.resolveBuiltinMiddlewareBackend,
172
+ resolveSubagents: assembly.resolveSubagents,
173
+ resolveModel: assembly.resolveModel,
174
+ resolveTools: assembly.resolveTools,
161
175
  });
162
176
  }
163
177
  async resolveBuiltinMiddlewareTools(binding, options = {}) {
178
+ const assembly = this.createAssemblyResolvers(binding, options);
164
179
  return resolveBuiltinMiddlewareToolsHelper({
165
180
  binding,
166
181
  options,
167
- resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
168
- invokeBuiltinTaskTool: (currentBinding, toolInput, currentOptions) => this.invokeBuiltinTaskTool(currentBinding, toolInput, currentOptions),
182
+ resolveBuiltinMiddlewareBackend: assembly.resolveBuiltinMiddlewareBackend,
183
+ invokeBuiltinTaskTool: assembly.invokeBuiltinTaskTool,
169
184
  });
170
185
  }
171
186
  async resolveAutomaticSummarizationMiddleware(binding) {
187
+ const assembly = this.createAssemblyResolvers(binding);
172
188
  return resolveAutomaticSummarizationMiddlewareHelper({
173
189
  binding,
174
- createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
190
+ createDeclaredMiddlewareResolverOptions: assembly.createDeclaredMiddlewareResolverOptions,
175
191
  });
176
192
  }
177
193
  async resolveLangChainAutomaticMiddleware(binding) {
194
+ const assembly = this.createAssemblyResolvers(binding);
178
195
  return resolveLangChainAutomaticMiddlewareHelper({
179
196
  binding,
180
197
  resolveAutomaticSummarizationMiddleware: (currentBinding) => this.resolveAutomaticSummarizationMiddleware(currentBinding),
181
- resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding),
182
- resolveModel: (model) => this.resolveModel(model),
183
- resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
184
- resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
198
+ resolveFilesystemBackend: assembly.resolveFilesystemBackend,
199
+ resolveModel: assembly.resolveModel,
200
+ resolveTools: assembly.resolveTools,
201
+ resolveSubagents: assembly.resolveSubagents,
185
202
  });
186
203
  }
187
204
  async resolveMiddleware(binding, interruptOn) {
205
+ const assembly = this.createAssemblyResolvers(binding);
188
206
  return resolveMiddlewareHelper({
189
207
  binding,
190
208
  interruptOn,
191
209
  runtimeAdapterOptions: this.options,
192
- createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
210
+ createDeclaredMiddlewareResolverOptions: assembly.createDeclaredMiddlewareResolverOptions,
193
211
  resolveLangChainAutomaticMiddleware: (currentBinding) => this.resolveLangChainAutomaticMiddleware(currentBinding),
194
212
  });
195
213
  }
196
214
  async resolveSubagents(subagents, binding) {
215
+ const assembly = this.createAssemblyResolvers(binding);
197
216
  return resolveSubagentsHelper({
198
217
  subagents,
199
218
  binding,
200
- resolveModel: (model) => this.resolveModel(model),
201
- resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
202
- createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
219
+ resolveModel: assembly.resolveModel,
220
+ resolveTools: assembly.resolveTools,
221
+ createDeclaredMiddlewareResolverOptions: assembly.createDeclaredMiddlewareResolverOptions,
203
222
  });
204
223
  }
205
224
  async createLangChainRunnable(binding, options = {}) {
@@ -1,28 +1,37 @@
1
1
  import { normalizeRunPriority } from "./helpers.js";
2
- export async function recoverQueuedStartupRun(context, thread) {
3
- if (thread.status !== "queued") {
4
- return false;
2
+ async function failRecovery(context, thread, previousState, error, options = {}) {
3
+ await context.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
4
+ previousState,
5
+ error,
6
+ });
7
+ if (options.releaseRunClaim) {
8
+ await context.persistence.releaseRunClaim(thread.latestRunId);
5
9
  }
10
+ return true;
11
+ }
12
+ async function resolveRecoveryBinding(context, thread) {
6
13
  const runMeta = await context.persistence.getRunMeta(thread.threadId, thread.latestRunId);
7
14
  const binding = context.getBinding(runMeta.agentId);
8
15
  if (!binding) {
9
- return true;
10
- }
11
- const request = await context.persistence.getRunRequest(thread.threadId, thread.latestRunId);
12
- if (!request) {
13
- await context.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
14
- previousState: "queued",
15
- error: "missing persisted run request for queued run recovery",
16
- });
17
- return true;
16
+ return null;
18
17
  }
18
+ return {
19
+ binding,
20
+ agentId: runMeta.agentId,
21
+ };
22
+ }
23
+ async function executeRecoveredRun(context, input) {
24
+ const { thread, binding, agentId, request, previousState, emitResumeEvent } = input;
19
25
  const releaseRunSlot = await context.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
20
26
  try {
21
- await context.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
27
+ if (emitResumeEvent) {
28
+ await context.emit(thread.threadId, thread.latestRunId, emitResumeEvent.sequence, emitResumeEvent.eventType, emitResumeEvent.payload);
29
+ }
30
+ await context.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, agentId, {
22
31
  context: request.invocation?.context,
23
32
  state: request.invocation?.inputs,
24
33
  files: request.invocation?.attachments,
25
- previousState: "queued",
34
+ previousState,
26
35
  stateSequence: 103,
27
36
  approvalSequence: 104,
28
37
  });
@@ -32,6 +41,26 @@ export async function recoverQueuedStartupRun(context, thread) {
32
41
  }
33
42
  return true;
34
43
  }
44
+ export async function recoverQueuedStartupRun(context, thread) {
45
+ if (thread.status !== "queued") {
46
+ return false;
47
+ }
48
+ const resolved = await resolveRecoveryBinding(context, thread);
49
+ if (!resolved) {
50
+ return true;
51
+ }
52
+ const request = await context.persistence.getRunRequest(thread.threadId, thread.latestRunId);
53
+ if (!request) {
54
+ return failRecovery(context, thread, "queued", "missing persisted run request for queued run recovery");
55
+ }
56
+ return executeRecoveredRun(context, {
57
+ thread,
58
+ binding: resolved.binding,
59
+ agentId: resolved.agentId,
60
+ request,
61
+ previousState: "queued",
62
+ });
63
+ }
35
64
  export async function recoverRunningStartupRun(context, thread) {
36
65
  if (thread.status !== "running") {
37
66
  return false;
@@ -40,47 +69,36 @@ export async function recoverRunningStartupRun(context, thread) {
40
69
  if (!isStale) {
41
70
  return true;
42
71
  }
43
- const runMeta = await context.persistence.getRunMeta(thread.threadId, thread.latestRunId);
44
- const binding = context.getBinding(runMeta.agentId);
45
- if (!binding) {
72
+ const resolved = await resolveRecoveryBinding(context, thread);
73
+ if (!resolved) {
46
74
  return true;
47
75
  }
48
- if (!context.supportsRunningReplay(binding)) {
49
- await context.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
50
- previousState: "running",
51
- error: "stale running run cannot be replayed safely",
76
+ if (!context.supportsRunningReplay(resolved.binding)) {
77
+ return failRecovery(context, thread, "running", "stale running run cannot be replayed safely", {
78
+ releaseRunClaim: true,
52
79
  });
53
- await context.persistence.releaseRunClaim(thread.latestRunId);
54
- return true;
55
80
  }
56
81
  const request = await context.persistence.getRunRequest(thread.threadId, thread.latestRunId);
57
82
  if (!request) {
58
- await context.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
59
- previousState: "running",
60
- error: "missing persisted run request for stale running run recovery",
83
+ return failRecovery(context, thread, "running", "missing persisted run request for stale running run recovery", {
84
+ releaseRunClaim: true,
61
85
  });
62
- await context.persistence.releaseRunClaim(thread.latestRunId);
63
- return true;
64
86
  }
65
- const releaseRunSlot = await context.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
66
- try {
67
- await context.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
68
- resumeKind: "startup-running-recovery",
69
- state: "running",
70
- });
71
- await context.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
72
- context: request.invocation?.context,
73
- state: request.invocation?.inputs,
74
- files: request.invocation?.attachments,
75
- previousState: "running",
76
- stateSequence: 103,
77
- approvalSequence: 104,
78
- });
79
- }
80
- finally {
81
- await releaseRunSlot();
82
- }
83
- return true;
87
+ return executeRecoveredRun(context, {
88
+ thread,
89
+ binding: resolved.binding,
90
+ agentId: resolved.agentId,
91
+ request,
92
+ previousState: "running",
93
+ emitResumeEvent: {
94
+ sequence: 100,
95
+ eventType: "run.resumed",
96
+ payload: {
97
+ resumeKind: "startup-running-recovery",
98
+ state: "running",
99
+ },
100
+ },
101
+ });
84
102
  }
85
103
  export async function recoverResumingStartupRun(context, thread) {
86
104
  if (thread.status !== "resuming" || !context.recoveryConfig.resumeResumingRunsOnStartup) {
@@ -1,4 +1,14 @@
1
1
  import { isTerminalRunState } from "./helpers.js";
2
+ async function finalizeIfCancellationRequested(input) {
3
+ const cancellation = await input.getRunCancellation(input.runId);
4
+ if (!cancellation.requested) {
5
+ return null;
6
+ }
7
+ return input.finalizeCancelledRun(input.threadId, input.runId, input.previousState, cancellation.reason);
8
+ }
9
+ function normalizeContinuationState(previousState) {
10
+ return previousState === "queued" ? "running" : previousState;
11
+ }
2
12
  export async function resumeRun(runtime, options) {
3
13
  const approvalById = options.approvalId ? await runtime.getApprovalById(options.approvalId) : null;
4
14
  const thread = options.threadId
@@ -17,9 +27,15 @@ export async function resumeRun(runtime, options) {
17
27
  throw new Error(`Unknown agent ${thread.agentId}`);
18
28
  }
19
29
  const resumePayload = runtime.buildResumePayload(binding, approval, options);
20
- const cancellation = await runtime.getRunCancellation(runId);
21
- if (cancellation.requested) {
22
- return runtime.finalizeCancelledRun(threadId, runId, thread.status, cancellation.reason);
30
+ const cancelledBeforeResume = await finalizeIfCancellationRequested({
31
+ getRunCancellation: runtime.getRunCancellation,
32
+ finalizeCancelledRun: runtime.finalizeCancelledRun,
33
+ threadId,
34
+ runId,
35
+ previousState: thread.status,
36
+ });
37
+ if (cancelledBeforeResume) {
38
+ return cancelledBeforeResume;
23
39
  }
24
40
  const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
25
41
  await runtime.setRunState(threadId, runId, "resuming", checkpointRef);
@@ -53,9 +69,15 @@ export async function resumeRun(runtime, options) {
53
69
  try {
54
70
  const actual = await runtime.invoke(binding, "", threadId, runId, resumePayload, priorHistory);
55
71
  runtime.recordLlmSuccess(startedAt);
56
- const cancelledAfterInvoke = await runtime.getRunCancellation(runId);
57
- if (cancelledAfterInvoke.requested) {
58
- return runtime.finalizeCancelledRun(threadId, runId, "resuming", cancelledAfterInvoke.reason);
72
+ const cancelledAfterInvoke = await finalizeIfCancellationRequested({
73
+ getRunCancellation: runtime.getRunCancellation,
74
+ finalizeCancelledRun: runtime.finalizeCancelledRun,
75
+ threadId,
76
+ runId,
77
+ previousState: "resuming",
78
+ });
79
+ if (cancelledAfterInvoke) {
80
+ return cancelledAfterInvoke;
59
81
  }
60
82
  await runtime.clearRecoveryIntent(threadId, runId);
61
83
  const finalized = await runtime.finalizeContinuedRun(binding, threadId, runId, runInput, actual, {
@@ -125,9 +147,15 @@ export async function executeQueuedRunOperation(runtime, input) {
125
147
  output: "cancelled",
126
148
  };
127
149
  }
128
- const cancellation = await runtime.getRunCancellation(runId);
129
- if (cancellation.requested) {
130
- return runtime.finalizeCancelledRun(threadId, runId, previousState, cancellation.reason);
150
+ const cancelledBeforeInvoke = await finalizeIfCancellationRequested({
151
+ getRunCancellation: runtime.getRunCancellation,
152
+ finalizeCancelledRun: runtime.finalizeCancelledRun,
153
+ threadId,
154
+ runId,
155
+ previousState,
156
+ });
157
+ if (cancelledBeforeInvoke) {
158
+ return cancelledBeforeInvoke;
131
159
  }
132
160
  if (previousState === "queued") {
133
161
  await runtime.emit(threadId, runId, 101, "run.dequeued", {
@@ -140,18 +168,25 @@ export async function executeQueuedRunOperation(runtime, input) {
140
168
  previousState: "queued",
141
169
  });
142
170
  }
171
+ const continuationState = normalizeContinuationState(previousState);
143
172
  try {
144
173
  const actual = await runtime.invokeWithHistory(binding, message, threadId, runId, undefined, options.priorHistory, {
145
174
  context: options.context,
146
175
  state: options.state,
147
176
  files: options.files,
148
177
  });
149
- const cancelledAfterInvoke = await runtime.getRunCancellation(runId);
150
- if (cancelledAfterInvoke.requested) {
151
- return runtime.finalizeCancelledRun(threadId, runId, previousState === "queued" ? "running" : previousState, cancelledAfterInvoke.reason);
178
+ const cancelledAfterInvoke = await finalizeIfCancellationRequested({
179
+ getRunCancellation: runtime.getRunCancellation,
180
+ finalizeCancelledRun: runtime.finalizeCancelledRun,
181
+ threadId,
182
+ runId,
183
+ previousState: continuationState,
184
+ });
185
+ if (cancelledAfterInvoke) {
186
+ return cancelledAfterInvoke;
152
187
  }
153
188
  const finalized = await runtime.finalizeContinuedRun(binding, threadId, runId, message, actual, {
154
- previousState: previousState === "queued" ? "running" : previousState,
189
+ previousState: continuationState,
155
190
  stateSequence: options.stateSequence ?? 103,
156
191
  approvalSequence: options.approvalSequence ?? 104,
157
192
  });
@@ -163,7 +198,7 @@ export async function executeQueuedRunOperation(runtime, input) {
163
198
  catch (error) {
164
199
  await runtime.emitSyntheticFallback(threadId, runId, agentId, error);
165
200
  await runtime.setRunStateAndEmit(threadId, runId, 104, "failed", {
166
- previousState: previousState === "queued" ? "running" : previousState,
201
+ previousState: continuationState,
167
202
  error: error instanceof Error ? error.message : String(error),
168
203
  });
169
204
  return {
@@ -40,6 +40,7 @@ export declare class AgentHarnessRuntime {
40
40
  private defaultRunRoot;
41
41
  private getDefaultHostAgentId;
42
42
  private resolveSelectedAgentId;
43
+ private createPrepareRunStartRuntime;
43
44
  constructor(workspace: WorkspaceBundle, runtimeAdapterOptions?: RuntimeAdapterOptions);
44
45
  private createHealthMonitor;
45
46
  private recordLlmSuccess;
@@ -12,10 +12,10 @@ import { HealthMonitor, readHealthMonitorConfig, } from "./harness/system/health
12
12
  import { normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
13
13
  import { emitHarnessEvent, } from "./harness/events/events.js";
14
14
  import { createRuntimeEventOperations } from "./harness/events/runtime-event-operations.js";
15
- import { finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
15
+ import { appendAssistantMessage as appendLifecycleAssistantMessage, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
16
16
  import { createListenerDispatchRuntime } from "./harness/events/listener-runtime.js";
17
+ import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord, } from "./harness/run/resume.js";
17
18
  import { cancelRunOperation, executeQueuedRunOperation, resumeRun } from "./harness/run/run-operations.js";
18
- import { createResumeRunRuntime } from "./harness/run/resume-runtime.js";
19
19
  import { acquireRunSlot as acquireHarnessRunSlot } from "./harness/run/run-slot-acquisition.js";
20
20
  import { dropPendingRunSlot, enqueuePendingRunSlot } from "./harness/run/run-queue.js";
21
21
  import { getDefaultHostAgentId, resolveSelectedAgentId, routeAgentId } from "./harness/run/routing.js";
@@ -28,7 +28,6 @@ import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnab
28
28
  import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
29
29
  import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
30
30
  import { streamHarnessRun } from "./harness/run/stream-run.js";
31
- import { createStreamRunRuntime } from "./harness/run/stream-runtime.js";
32
31
  import { defaultRequestedAgentId, prepareRunStart } from "./harness/run/start-run.js";
33
32
  import { deleteThreadRecord, getPublicApproval, getThreadRecord, listPublicApprovals, } from "./harness/run/thread-records.js";
34
33
  export class AgentHarnessRuntime {
@@ -88,6 +87,16 @@ export class AgentHarnessRuntime {
88
87
  getThreadSummary: (currentThreadId) => this.getSession(currentThreadId),
89
88
  });
90
89
  }
90
+ createPrepareRunStartRuntime() {
91
+ return {
92
+ workspace: this.workspace,
93
+ policyEngine: this.policyEngine,
94
+ persistence: this.persistence,
95
+ resolveSelectedAgentId: (input, requestedAgentId, threadId) => this.resolveSelectedAgentId(input, requestedAgentId, threadId),
96
+ emitRunCreated: (threadId, runId, payload) => this.runtimeEventOperations.emitRunCreated(threadId, runId, payload),
97
+ acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
98
+ };
99
+ }
91
100
  constructor(workspace, runtimeAdapterOptions = {}) {
92
101
  this.workspace = workspace;
93
102
  this.runtimeAdapterOptions = runtimeAdapterOptions;
@@ -422,14 +431,7 @@ export class AgentHarnessRuntime {
422
431
  }).dispatchRunListeners(this.streamEvents(options), resolvedListeners);
423
432
  }
424
433
  const invocation = normalizeInvocationEnvelope(options);
425
- const { binding, selectedAgentId, threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart({
426
- workspace: this.workspace,
427
- policyEngine: this.policyEngine,
428
- persistence: this.persistence,
429
- resolveSelectedAgentId: (input, requestedAgentId, threadId) => this.resolveSelectedAgentId(input, requestedAgentId, threadId),
430
- emitRunCreated: (threadId, runId, payload) => this.runtimeEventOperations.emitRunCreated(threadId, runId, payload),
431
- acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
432
- }, {
434
+ const { binding, selectedAgentId, threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart(this.createPrepareRunStartRuntime(), {
433
435
  options,
434
436
  invocation,
435
437
  runCreatedPayload: (activeBinding, activeSelectedAgentId) => ({
@@ -473,14 +475,7 @@ export class AgentHarnessRuntime {
473
475
  }
474
476
  return;
475
477
  }
476
- const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart({
477
- workspace: this.workspace,
478
- policyEngine: this.policyEngine,
479
- persistence: this.persistence,
480
- resolveSelectedAgentId: (input, requestedAgentId, threadId) => this.resolveSelectedAgentId(input, requestedAgentId, threadId),
481
- emitRunCreated: (threadId, runId, payload) => this.runtimeEventOperations.emitRunCreated(threadId, runId, payload),
482
- acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
483
- }, {
478
+ const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart(this.createPrepareRunStartRuntime(), {
484
479
  options,
485
480
  invocation,
486
481
  runCreatedPayload: (_binding, activeSelectedAgentId) => ({
@@ -491,16 +486,6 @@ export class AgentHarnessRuntime {
491
486
  state: "running",
492
487
  }),
493
488
  });
494
- const streamRuntime = createStreamRunRuntime({
495
- persistence: this.persistence,
496
- runtimeAdapter: this.runtimeAdapter,
497
- emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
498
- setRunStateAndEmit: (threadId, runId, sequence, state, stateOptions) => this.setRunStateAndEmit(threadId, runId, sequence, state, stateOptions),
499
- requestApprovalAndEmit: (threadId, runId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence),
500
- loadPriorHistory: (threadId, runId) => this.loadPriorHistory(threadId, runId),
501
- invokeWithHistory: (binding, input, threadId, runId) => this.invokeWithHistory(binding, input, threadId, runId),
502
- emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
503
- });
504
489
  yield* streamHarnessRun({
505
490
  binding,
506
491
  input: options.input,
@@ -511,25 +496,40 @@ export class AgentHarnessRuntime {
511
496
  isNewThread,
512
497
  runCreatedEventPromise,
513
498
  releaseRunSlotPromise,
514
- ...streamRuntime,
499
+ loadPriorHistory: (threadId, runId) => this.loadPriorHistory(threadId, runId),
500
+ stream: (binding, message, threadId, priorHistory, streamOptions) => this.runtimeAdapter.stream(binding, message, threadId, priorHistory, streamOptions),
501
+ invokeWithHistory: (binding, input, threadId, runId) => this.invokeWithHistory(binding, input, threadId, runId),
502
+ emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
503
+ setRunStateAndEmit: (threadId, runId, sequence, state, stateOptions) => this.setRunStateAndEmit(threadId, runId, sequence, state, stateOptions),
504
+ requestApprovalAndEmit: (threadId, runId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence),
505
+ appendAssistantMessage: (threadId, runId, content) => appendLifecycleAssistantMessage(this.persistence, threadId, runId, content),
506
+ clearRunRequest: (threadId, runId) => this.persistence.clearRunRequest(threadId, runId),
507
+ emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
515
508
  });
516
509
  }
517
510
  async resume(options) {
518
- return resumeRun(createResumeRunRuntime({
519
- persistence: this.persistence,
520
- workspace: this.workspace,
521
- runtimeAdapter: this.runtimeAdapter,
511
+ return resumeRun({
512
+ getApprovalById: (approvalId) => this.persistence.getApproval(approvalId),
522
513
  getSession: (threadId) => this.getSession(threadId),
514
+ resolveApprovalRecord: (resumeOptions, thread) => resolveHarnessApprovalRecord(this.persistence, resumeOptions, thread),
515
+ getBinding: (agentId) => getWorkspaceBinding(this.workspace, agentId),
516
+ buildResumePayload: (binding, approval, resumeOptions) => buildHarnessResumePayload(binding, approval, resumeOptions),
523
517
  getRunCancellation: (runId) => this.getRunCancellation(runId),
524
518
  finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
519
+ setRunState: (threadId, runId, state, checkpointRef) => this.persistence.setRunState(threadId, runId, state, checkpointRef),
525
520
  acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
526
521
  resolvePersistedRunPriority: (threadId, runId) => this.resolvePersistedRunPriority(threadId, runId),
522
+ saveRecoveryIntent: (threadId, runId, payload) => this.persistence.saveRecoveryIntent(threadId, runId, payload),
527
523
  emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
524
+ resolveApproval: (threadId, runId, approvalId, resolution) => this.persistence.resolveApproval(threadId, runId, approvalId, resolution),
525
+ listThreadMessages: (threadId) => this.persistence.listThreadMessages(threadId),
528
526
  loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
527
+ invoke: (binding, input, threadId, runId, resumePayload, priorHistory) => this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, priorHistory),
529
528
  recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
530
529
  recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
530
+ clearRecoveryIntent: (threadId, runId) => this.persistence.clearRecoveryIntent(threadId, runId),
531
531
  finalizeContinuedRun: (binding, threadId, runId, input, actual, operationOptions) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, operationOptions),
532
- }), options);
532
+ }, options);
533
533
  }
534
534
  async restartConversation(options) {
535
535
  const thread = await this.getSession(options.threadId);
@@ -9,8 +9,12 @@ export type BindingExecutionView = {
9
9
  primaryModel?: CompiledModel;
10
10
  systemPrompt?: string;
11
11
  middlewareConfigs?: Array<Record<string, unknown>>;
12
+ middlewareKinds: Set<string>;
12
13
  interruptCompatibilityRules?: Record<string, boolean | object>;
13
14
  storeConfig?: Record<string, unknown>;
15
+ toolCount: number;
16
+ langChainSubagentSupport: boolean;
17
+ deepAgentSubagentCount: number;
14
18
  };
15
19
  export declare function getBindingExecutionView(binding: CompiledAgentBinding): BindingExecutionView;
16
20
  export declare function getBindingAdapterKind(binding: CompiledAgentBinding): string;
@@ -24,6 +28,9 @@ export declare function getBindingPrimaryModel(binding: CompiledAgentBinding): C
24
28
  export declare function getBindingRuntimeModel(binding: CompiledAgentBinding, slot: RuntimeModelSlot): CompiledModel | undefined;
25
29
  export declare function getBindingSystemPrompt(binding: CompiledAgentBinding): string | undefined;
26
30
  export declare function getBindingMiddlewareConfigs(binding: CompiledAgentBinding): Array<Record<string, unknown>> | undefined;
31
+ export declare function getBindingToolCount(binding: CompiledAgentBinding): number;
32
+ export declare function bindingHasMiddlewareKind(binding: CompiledAgentBinding, kind: string): boolean;
33
+ export declare function bindingHasLangChainSubagentSupport(binding: CompiledAgentBinding): boolean;
27
34
  export declare function getBindingInterruptCompatibilityRules(binding: CompiledAgentBinding): Record<string, boolean | object> | undefined;
28
35
  export declare function getBindingModelInit(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
29
36
  export declare function getBindingStoreConfig(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
@@ -1,8 +1,4 @@
1
- function asRecord(value) {
2
- return typeof value === "object" && value !== null && !Array.isArray(value)
3
- ? value
4
- : undefined;
5
- }
1
+ import { asRecord } from "../../utils/object.js";
6
2
  const bindingExecutionViewCache = new WeakMap();
7
3
  function deriveBindingExecutionView(binding) {
8
4
  const cached = bindingExecutionViewCache.get(binding);
@@ -19,6 +15,10 @@ function deriveBindingExecutionView(binding) {
19
15
  ? adapterParams
20
16
  : binding.deepAgentParams;
21
17
  const primaryTools = langchainParams?.tools ?? deepAgentParams?.tools ?? [];
18
+ const middlewareConfigs = langchainParams?.middleware ?? deepAgentParams?.middleware;
19
+ const middlewareKinds = new Set((middlewareConfigs ?? [])
20
+ .map((entry) => typeof entry.kind === "string" ? entry.kind : "")
21
+ .filter(Boolean));
22
22
  const view = {
23
23
  adapterKind,
24
24
  adapterConfig,
@@ -26,12 +26,18 @@ function deriveBindingExecutionView(binding) {
26
26
  deepAgentParams,
27
27
  executionParams: langchainParams ?? deepAgentParams,
28
28
  primaryTools,
29
+ toolCount: primaryTools.length,
29
30
  primaryModel: langchainParams?.model ?? deepAgentParams?.model,
30
31
  systemPrompt: langchainParams?.systemPrompt ?? deepAgentParams?.systemPrompt,
31
- middlewareConfigs: langchainParams?.middleware ?? deepAgentParams?.middleware,
32
+ middlewareConfigs,
33
+ middlewareKinds,
32
34
  interruptCompatibilityRules: deepAgentParams?.interruptOn ??
33
35
  binding.agent.langchainAgentConfig?.interruptOn,
34
36
  storeConfig: deepAgentParams?.store ?? binding.harnessRuntime?.store,
37
+ langChainSubagentSupport: (langchainParams?.subagents?.length ?? 0) > 0 ||
38
+ langchainParams?.generalPurposeAgent === true ||
39
+ Boolean(langchainParams?.taskDescription?.trim()),
40
+ deepAgentSubagentCount: deepAgentParams?.subagents?.length ?? 0,
35
41
  };
36
42
  bindingExecutionViewCache.set(binding, view);
37
43
  return view;
@@ -72,6 +78,15 @@ export function getBindingSystemPrompt(binding) {
72
78
  export function getBindingMiddlewareConfigs(binding) {
73
79
  return getBindingExecutionView(binding).middlewareConfigs;
74
80
  }
81
+ export function getBindingToolCount(binding) {
82
+ return getBindingExecutionView(binding).toolCount;
83
+ }
84
+ export function bindingHasMiddlewareKind(binding, kind) {
85
+ return getBindingExecutionView(binding).middlewareKinds.has(kind);
86
+ }
87
+ export function bindingHasLangChainSubagentSupport(binding) {
88
+ return getBindingExecutionView(binding).langChainSubagentSupport;
89
+ }
75
90
  export function getBindingInterruptCompatibilityRules(binding) {
76
91
  return getBindingExecutionView(binding).interruptCompatibilityRules;
77
92
  }
@@ -82,5 +97,5 @@ export function getBindingStoreConfig(binding) {
82
97
  return getBindingExecutionView(binding).storeConfig;
83
98
  }
84
99
  export function bindingHasSubagents(binding) {
85
- return (getBindingDeepAgentParams(binding)?.subagents.length ?? 0) > 0;
100
+ return getBindingExecutionView(binding).deepAgentSubagentCount > 0;
86
101
  }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parse } from "yaml";
4
+ import { isRecord } from "../../utils/object.js";
4
5
  const skillMetadataCache = new Map();
5
6
  const skillValidationCache = new Map();
6
7
  const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -8,9 +9,6 @@ function parseFrontmatterSource(document) {
8
9
  const match = document.match(/^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/);
9
10
  return match?.[1];
10
11
  }
11
- function isRecord(value) {
12
- return typeof value === "object" && value !== null && !Array.isArray(value);
13
- }
14
12
  function isJsonLikeValue(value) {
15
13
  if (value === null ||
16
14
  typeof value === "string" ||
@@ -0,0 +1,3 @@
1
+ export type UnknownRecord = Record<string, unknown>;
2
+ export declare function isRecord(value: unknown): value is UnknownRecord;
3
+ export declare function asRecord(value: unknown): UnknownRecord | undefined;
@@ -0,0 +1,6 @@
1
+ export function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ export function asRecord(value) {
5
+ return isRecord(value) ? value : undefined;
6
+ }
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import { getSkillInheritancePolicy, resolveToolTargets } from "../extensions.js";
3
3
  import { compileModel, compileTool } from "./resource-compilers.js";
4
4
  import { inferAgentCapabilities } from "./support/agent-capabilities.js";
5
+ import { getAgentExecutionConfigValue, getAgentExecutionObject, getAgentExecutionString } from "./support/agent-execution-config.js";
5
6
  import { discoverSkillPaths } from "./support/discovery.js";
6
7
  import { compileAgentMemories, getResilienceConfig, getRuntimeDefaults, getRuntimeMemoryDefaults, getRuntimeModelDefaults, getWorkspaceObject, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
7
8
  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. " +
@@ -11,9 +12,7 @@ function requireSkills(pathEntries, workspaceRoot) {
11
12
  return Array.from(new Set(discoverSkillPaths(pathEntries, workspaceRoot)));
12
13
  }
13
14
  function resolveInheritancePolicy(agent) {
14
- const langchainPolicy = typeof agent.langchainAgentConfig?.skillInheritancePolicy === "string" ? agent.langchainAgentConfig.skillInheritancePolicy : undefined;
15
- const deepagentPolicy = typeof agent.deepAgentConfig?.skillInheritancePolicy === "string" ? agent.deepAgentConfig.skillInheritancePolicy : undefined;
16
- return (langchainPolicy ?? deepagentPolicy ?? "default").trim();
15
+ return getAgentExecutionString(agent, "skillInheritancePolicy") ?? "default";
17
16
  }
18
17
  export function compileAgentSkills(workspaceRoot, agent, parentSkills = []) {
19
18
  const policyName = resolveInheritancePolicy(agent);
@@ -106,40 +105,28 @@ function buildSubagent(agent, workspaceRoot, models, tools, parentSkills, parent
106
105
  passthrough: execution.passthrough,
107
106
  };
108
107
  }
109
- function resolveDirectPrompt(agent) {
110
- return resolvePromptValue(agent.langchainAgentConfig?.systemPrompt);
111
- }
112
108
  function resolveSystemPrompt(agent) {
109
+ const prompt = resolvePromptValue(getAgentExecutionConfigValue(agent, "systemPrompt"));
113
110
  if (agent.executionMode !== "deepagent") {
114
- return resolveDirectPrompt(agent);
111
+ return prompt;
115
112
  }
116
- const deepagentPrompt = resolvePromptValue(agent.deepAgentConfig?.systemPrompt);
117
- return [deepagentPrompt ?? resolveDirectPrompt(agent), WORKSPACE_BOUNDARY_GUIDANCE].filter(Boolean).join("\n\n");
113
+ return [prompt, WORKSPACE_BOUNDARY_GUIDANCE].filter(Boolean).join("\n\n");
118
114
  }
119
115
  function resolveInterruptOn(agent) {
120
- if (agent.executionMode !== "deepagent") {
121
- return undefined;
122
- }
123
- return (agent.deepAgentConfig?.interruptOn ??
124
- agent.langchainAgentConfig?.interruptOn);
116
+ return getAgentExecutionObject(agent, "interruptOn");
125
117
  }
126
118
  function resolveResponseFormat(agent) {
127
- return agent.deepAgentConfig?.responseFormat ?? agent.langchainAgentConfig?.responseFormat;
119
+ return getAgentExecutionConfigValue(agent, "responseFormat");
128
120
  }
129
121
  function resolveContextSchema(agent) {
130
- return agent.deepAgentConfig?.contextSchema ?? agent.langchainAgentConfig?.contextSchema;
122
+ return getAgentExecutionConfigValue(agent, "contextSchema");
131
123
  }
132
124
  function resolveCompiledMiddleware(agent, models) {
133
- const middleware = agent.deepAgentConfig?.middleware ??
134
- agent.langchainAgentConfig?.middleware;
125
+ const middleware = getAgentExecutionConfigValue(agent, "middleware");
135
126
  return compileMiddlewareConfigs(middleware, models, agent.id);
136
127
  }
137
128
  function resolvePassthrough(agent) {
138
- const passthrough = typeof agent.deepAgentConfig?.passthrough === "object" && agent.deepAgentConfig.passthrough
139
- ? agent.deepAgentConfig.passthrough
140
- : typeof agent.langchainAgentConfig?.passthrough === "object" && agent.langchainAgentConfig.passthrough
141
- ? agent.langchainAgentConfig.passthrough
142
- : undefined;
129
+ const passthrough = getAgentExecutionObject(agent, "passthrough");
143
130
  return passthrough ? { ...passthrough } : undefined;
144
131
  }
145
132
  function compileSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory) {
@@ -166,9 +153,7 @@ function resolveBackendConfig(agent, refs) {
166
153
  if (agent.executionMode !== "deepagent") {
167
154
  return undefined;
168
155
  }
169
- const backendConfig = typeof agent.deepAgentConfig?.backend === "object" && agent.deepAgentConfig.backend
170
- ? agent.deepAgentConfig.backend
171
- : undefined;
156
+ const backendConfig = getAgentExecutionObject(agent, "backend");
172
157
  if (!backendConfig) {
173
158
  return undefined;
174
159
  }
@@ -221,11 +206,7 @@ function materializeWorkspaceObjectConfig(refs, ref, allowedKinds, ownerLabel) {
221
206
  return config;
222
207
  }
223
208
  function resolveStoreConfig(agent, refs) {
224
- const inlineStore = typeof agent.deepAgentConfig?.store === "object" && agent.deepAgentConfig.store
225
- ? agent.deepAgentConfig.store
226
- : typeof agent.langchainAgentConfig?.store === "object" && agent.langchainAgentConfig.store
227
- ? agent.langchainAgentConfig.store
228
- : undefined;
209
+ const inlineStore = getAgentExecutionObject(agent, "store");
229
210
  if (!inlineStore) {
230
211
  return undefined;
231
212
  }
@@ -237,15 +218,7 @@ function resolveStoreConfig(agent, refs) {
237
218
  return { config: inlineStore };
238
219
  }
239
220
  function resolveCheckpointerConfig(agent, refs) {
240
- const inlineAgentCheckpointer = typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer
241
- ? agent.deepAgentConfig.checkpointer
242
- : typeof agent.deepAgentConfig?.checkpointer === "boolean"
243
- ? agent.deepAgentConfig.checkpointer
244
- : typeof agent.langchainAgentConfig?.checkpointer === "object" && agent.langchainAgentConfig.checkpointer
245
- ? agent.langchainAgentConfig.checkpointer
246
- : typeof agent.langchainAgentConfig?.checkpointer === "boolean"
247
- ? agent.langchainAgentConfig.checkpointer
248
- : undefined;
221
+ const inlineAgentCheckpointer = getAgentExecutionConfigValue(agent, "checkpointer");
249
222
  if (inlineAgentCheckpointer === undefined) {
250
223
  return undefined;
251
224
  }
@@ -260,11 +233,7 @@ function resolveCheckpointerConfig(agent, refs) {
260
233
  return { config: inlineAgentCheckpointer };
261
234
  }
262
235
  function resolveRuntimeMemoryConfig(agent, refs) {
263
- const inlineRuntimeMemory = typeof agent.deepAgentConfig?.runtimeMemory === "object" && agent.deepAgentConfig.runtimeMemory
264
- ? agent.deepAgentConfig.runtimeMemory
265
- : typeof agent.langchainAgentConfig?.runtimeMemory === "object" && agent.langchainAgentConfig.runtimeMemory
266
- ? agent.langchainAgentConfig.runtimeMemory
267
- : undefined;
236
+ const inlineRuntimeMemory = getAgentExecutionObject(agent, "runtimeMemory");
268
237
  if (inlineRuntimeMemory) {
269
238
  if (isRefConfig(inlineRuntimeMemory)) {
270
239
  return {
@@ -276,34 +245,6 @@ function resolveRuntimeMemoryConfig(agent, refs) {
276
245
  const runtimeMemoryDefaults = getRuntimeMemoryDefaults(refs);
277
246
  return runtimeMemoryDefaults ? { config: runtimeMemoryDefaults } : undefined;
278
247
  }
279
- function resolveLangGraphWorkflowConfig(agent, refs) {
280
- const workflowConfig = typeof agent.langchainAgentConfig?.workflow === "object" && agent.langchainAgentConfig.workflow
281
- ? agent.langchainAgentConfig.workflow
282
- : typeof agent.langchainAgentConfig?.langgraph === "object" && agent.langchainAgentConfig.langgraph
283
- ? agent.langchainAgentConfig.langgraph
284
- : typeof agent.langchainAgentConfig?.passthrough === "object" &&
285
- agent.langchainAgentConfig.passthrough &&
286
- typeof agent.langchainAgentConfig.passthrough.workflow === "object" &&
287
- agent.langchainAgentConfig.passthrough.workflow
288
- ? agent.langchainAgentConfig.passthrough.workflow
289
- : typeof agent.langchainAgentConfig?.passthrough === "object" &&
290
- agent.langchainAgentConfig.passthrough &&
291
- typeof agent.langchainAgentConfig.passthrough.langgraph === "object" &&
292
- agent.langchainAgentConfig.passthrough.langgraph
293
- ? agent.langchainAgentConfig.passthrough.langgraph
294
- : undefined;
295
- if (!workflowConfig) {
296
- return undefined;
297
- }
298
- if (isRefConfig(workflowConfig)) {
299
- return {
300
- config: materializeWorkspaceObjectConfig(refs, workflowConfig.ref, [], `Agent ${agent.id} workflow`),
301
- };
302
- }
303
- return {
304
- config: workflowConfig,
305
- };
306
- }
307
248
  function resolveRuntimeModelRefs(agent, refs) {
308
249
  const merged = {
309
250
  ...(getRuntimeModelDefaults(refs) ?? {}),
@@ -365,28 +306,25 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
365
306
  },
366
307
  };
367
308
  if (agent.executionMode !== "deepagent") {
309
+ const langchainVersion = getAgentExecutionConfigValue(agent, "version", { executionMode: "langchain-v1" });
368
310
  const langchainAgentParams = {
369
311
  model: execution.model,
370
312
  tools: execution.tools,
371
313
  systemPrompt: execution.systemPrompt,
372
- stateSchema: agent.langchainAgentConfig?.stateSchema,
314
+ stateSchema: getAgentExecutionConfigValue(agent, "stateSchema", { executionMode: "langchain-v1" }),
373
315
  responseFormat: execution.responseFormat,
374
316
  contextSchema: execution.contextSchema,
375
- filesystem: typeof agent.langchainAgentConfig?.filesystem === "object" && agent.langchainAgentConfig.filesystem
376
- ? { ...agent.langchainAgentConfig.filesystem }
377
- : undefined,
317
+ filesystem: getAgentExecutionObject(agent, "filesystem", { executionMode: "langchain-v1" }),
378
318
  middleware: execution.middleware,
379
319
  passthrough: execution.passthrough,
380
320
  subagents: compileSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory),
381
321
  memory: compiledAgentMemory,
382
322
  skills: compiledAgentSkills,
383
- generalPurposeAgent: typeof agent.langchainAgentConfig?.generalPurposeAgent === "boolean" ? agent.langchainAgentConfig.generalPurposeAgent : undefined,
384
- taskDescription: typeof agent.langchainAgentConfig?.taskDescription === "string" && agent.langchainAgentConfig.taskDescription.trim()
385
- ? agent.langchainAgentConfig.taskDescription
386
- : undefined,
387
- includeAgentName: agent.langchainAgentConfig?.includeAgentName === "inline" ? "inline" : undefined,
388
- version: agent.langchainAgentConfig?.version === "v1" || agent.langchainAgentConfig?.version === "v2"
389
- ? agent.langchainAgentConfig.version
323
+ generalPurposeAgent: getAgentExecutionConfigValue(agent, "generalPurposeAgent", { executionMode: "langchain-v1" }),
324
+ taskDescription: getAgentExecutionString(agent, "taskDescription", { executionMode: "langchain-v1" }),
325
+ includeAgentName: getAgentExecutionConfigValue(agent, "includeAgentName", { executionMode: "langchain-v1" }) === "inline" ? "inline" : undefined,
326
+ version: langchainVersion === "v1" || langchainVersion === "v2"
327
+ ? langchainVersion
390
328
  : undefined,
391
329
  name: resolveAgentRuntimeName(agent),
392
330
  description: agent.description,
@@ -418,10 +356,8 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
418
356
  name: resolveAgentRuntimeName(agent),
419
357
  memory: compiledAgentMemory,
420
358
  skills: compiledAgentSkills,
421
- generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
422
- taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
423
- ? agent.deepAgentConfig.taskDescription
424
- : undefined,
359
+ generalPurposeAgent: getAgentExecutionConfigValue(agent, "generalPurposeAgent", { executionMode: "deepagent" }),
360
+ taskDescription: getAgentExecutionString(agent, "taskDescription", { executionMode: "deepagent" }),
425
361
  };
426
362
  return {
427
363
  ...base,
@@ -1,3 +1,4 @@
1
+ import { getAgentExecutionString } from "./agent-execution-config.js";
1
2
  function normalizeCapabilities(capabilities) {
2
3
  return {
3
4
  delegation: capabilities?.delegation === true,
@@ -29,12 +30,7 @@ export function isMemoryCapableBinding(binding) {
29
30
  return inferBindingCapabilities(binding).memory === true;
30
31
  }
31
32
  export function getAgentSystemPrompt(agent) {
32
- const deepagentPrompt = typeof agent.deepAgentConfig?.systemPrompt === "string" ? agent.deepAgentConfig.systemPrompt.trim() : "";
33
- if (deepagentPrompt) {
34
- return deepagentPrompt;
35
- }
36
- const langchainPrompt = typeof agent.langchainAgentConfig?.systemPrompt === "string" ? agent.langchainAgentConfig.systemPrompt.trim() : "";
37
- return langchainPrompt || undefined;
33
+ return getAgentExecutionString(agent, "systemPrompt");
38
34
  }
39
35
  export function hasAgentSystemPrompt(agent) {
40
36
  return typeof getAgentSystemPrompt(agent) === "string";
@@ -0,0 +1,18 @@
1
+ import type { ExecutionMode } from "../../contracts/core.js";
2
+ import type { ParsedAgentObject } from "../../contracts/types.js";
3
+ type AgentExecutionConfig = Record<string, unknown>;
4
+ export declare function getAgentExecutionConfigs(agent: ParsedAgentObject, executionMode?: ExecutionMode): AgentExecutionConfig[];
5
+ export declare function getAgentExecutionConfigValue<T = unknown>(agent: ParsedAgentObject, key: string, options?: {
6
+ aliases?: string[];
7
+ executionMode?: ExecutionMode;
8
+ }): T | undefined;
9
+ export declare function getAgentExecutionObject(agent: ParsedAgentObject, key: string, options?: {
10
+ aliases?: string[];
11
+ executionMode?: ExecutionMode;
12
+ }): AgentExecutionConfig | undefined;
13
+ export declare function getAgentExecutionString(agent: ParsedAgentObject, key: string, options?: {
14
+ aliases?: string[];
15
+ executionMode?: ExecutionMode;
16
+ trim?: boolean;
17
+ }): string | undefined;
18
+ export {};
@@ -0,0 +1,44 @@
1
+ import { asRecord } from "../../utils/object.js";
2
+ const agentExecutionViewsCache = new WeakMap();
3
+ function deriveAgentExecutionViews(agent) {
4
+ const cached = agentExecutionViewsCache.get(agent);
5
+ if (cached) {
6
+ return cached;
7
+ }
8
+ const langchainConfig = asRecord(agent.langchainAgentConfig);
9
+ const deepAgentConfig = asRecord(agent.deepAgentConfig);
10
+ const views = {
11
+ "langchain-v1": [langchainConfig, deepAgentConfig].filter((config) => config !== undefined),
12
+ deepagent: [deepAgentConfig, langchainConfig].filter((config) => config !== undefined),
13
+ };
14
+ agentExecutionViewsCache.set(agent, views);
15
+ return views;
16
+ }
17
+ function getConfigsForMode(agent, executionMode = agent.executionMode) {
18
+ return deriveAgentExecutionViews(agent)[executionMode];
19
+ }
20
+ export function getAgentExecutionConfigs(agent, executionMode = agent.executionMode) {
21
+ return getConfigsForMode(agent, executionMode);
22
+ }
23
+ export function getAgentExecutionConfigValue(agent, key, options = {}) {
24
+ const keys = [key, ...(options.aliases ?? [])];
25
+ for (const config of getConfigsForMode(agent, options.executionMode)) {
26
+ for (const candidate of keys) {
27
+ const value = config[candidate];
28
+ if (value !== undefined) {
29
+ return value;
30
+ }
31
+ }
32
+ }
33
+ return undefined;
34
+ }
35
+ export function getAgentExecutionObject(agent, key, options = {}) {
36
+ return asRecord(getAgentExecutionConfigValue(agent, key, options));
37
+ }
38
+ export function getAgentExecutionString(agent, key, options = {}) {
39
+ const value = getAgentExecutionConfigValue(agent, key, options);
40
+ if (typeof value !== "string") {
41
+ return undefined;
42
+ }
43
+ return options.trim === false ? value : value.trim() || undefined;
44
+ }
@@ -1,10 +1,8 @@
1
1
  import { hasAgentSystemPrompt, isDelegationCapableAgent, isMemoryCapableAgent, } from "./support/agent-capabilities.js";
2
+ import { getAgentExecutionConfigValue, getAgentExecutionConfigs } from "./support/agent-execution-config.js";
2
3
  const allowedExecutionModes = new Set(["deepagent", "langchain-v1"]);
3
4
  function validateCheckpointerConfig(agent) {
4
- const checkpointer = (typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer) ||
5
- (typeof agent.deepAgentConfig?.checkpointer === "boolean" ? agent.deepAgentConfig.checkpointer : undefined) ||
6
- (typeof agent.langchainAgentConfig?.checkpointer === "boolean" ? agent.langchainAgentConfig.checkpointer : undefined) ||
7
- (typeof agent.langchainAgentConfig?.checkpointer === "object" && agent.langchainAgentConfig.checkpointer);
5
+ const checkpointer = getAgentExecutionConfigValue(agent, "checkpointer");
8
6
  if (typeof checkpointer === "boolean") {
9
7
  return;
10
8
  }
@@ -19,10 +17,10 @@ function validateCheckpointerConfig(agent) {
19
17
  }
20
18
  }
21
19
  function validateMiddlewareConfig(agent) {
22
- const middlewareConfigs = [
23
- ...(agent.langchainAgentConfig?.middleware ?? []),
24
- ...(agent.deepAgentConfig?.middleware ?? []),
25
- ];
20
+ const middlewareConfigs = getAgentExecutionConfigs(agent).flatMap((config) => {
21
+ const middleware = config.middleware;
22
+ return Array.isArray(middleware) ? middleware : [];
23
+ });
26
24
  for (const config of middlewareConfigs) {
27
25
  if (typeof config !== "object" || config === null || Array.isArray(config)) {
28
26
  throw new Error(`Agent ${agent.id} middleware entries must be objects`);
@@ -59,9 +57,6 @@ export function validateAgent(agent) {
59
57
  if (!agent.description.trim()) {
60
58
  throw new Error(`Agent ${agent.id} description must not be empty`);
61
59
  }
62
- if (!isDelegationCapableAgent(agent) && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
63
- throw new Error(`Agent ${agent.id} cannot define subagents unless it uses a delegation-capable backend`);
64
- }
65
60
  if (!isMemoryCapableAgent(agent) && agent.memorySources.length > 0) {
66
61
  throw new Error(`Agent ${agent.id} cannot define memory unless it uses a memory-capable backend`);
67
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.109",
3
+ "version": "0.0.111",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
@@ -1,55 +0,0 @@
1
- import type { CompiledAgentBinding, InternalApprovalRecord, MessageContent, ResumeOptions, RunResult, ThreadSummary, TranscriptMessage, WorkspaceBundle } from "../../../contracts/types.js";
2
- import type { RuntimePersistence } from "../../../persistence/types.js";
3
- import type { AgentRuntimeAdapter } from "../../agent-runtime-adapter.js";
4
- type Binding = WorkspaceBundle["bindings"] extends Map<any, infer T> ? T : never;
5
- export declare function createResumeRunRuntime(input: {
6
- persistence: RuntimePersistence;
7
- workspace: WorkspaceBundle;
8
- runtimeAdapter: AgentRuntimeAdapter;
9
- getSession: (threadId: string) => Promise<ThreadSummary | null>;
10
- getRunCancellation: (runId: string) => Promise<{
11
- requested: boolean;
12
- reason?: string;
13
- }>;
14
- finalizeCancelledRun: (threadId: string, runId: string, previousState: RunResult["state"] | null, reason?: string) => Promise<RunResult>;
15
- acquireRunSlot: (threadId?: string, runId?: string, activeState?: RunResult["state"], priority?: number) => Promise<() => Promise<void>>;
16
- resolvePersistedRunPriority: (threadId: string, runId: string) => Promise<number>;
17
- emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<unknown>;
18
- loadRunInput: (threadId: string, runId: string) => Promise<MessageContent>;
19
- recordLlmSuccess: (startedAt: number) => void;
20
- recordLlmFailure: (startedAt: number) => void;
21
- finalizeContinuedRun: (binding: Binding, threadId: string, runId: string, input: MessageContent, actual: RunResult, options: {
22
- previousState: RunResult["state"] | null;
23
- stateSequence: number;
24
- approvalSequence?: number;
25
- }) => Promise<RunResult>;
26
- }): {
27
- getApprovalById: (approvalId: string) => Promise<InternalApprovalRecord | null>;
28
- getSession: (threadId: string) => Promise<ThreadSummary | null>;
29
- resolveApprovalRecord: (resumeOptions: ResumeOptions, thread: ThreadSummary) => Promise<InternalApprovalRecord>;
30
- getBinding: (agentId: string) => CompiledAgentBinding | undefined;
31
- buildResumePayload: (binding: CompiledAgentBinding, approval: InternalApprovalRecord, resumeOptions: ResumeOptions) => unknown;
32
- getRunCancellation: (runId: string) => Promise<{
33
- requested: boolean;
34
- reason?: string;
35
- }>;
36
- finalizeCancelledRun: (threadId: string, runId: string, previousState: RunResult["state"] | null, reason?: string) => Promise<RunResult>;
37
- setRunState: (threadId: string, runId: string, state: RunResult["state"], checkpointRef: string | null) => Promise<void>;
38
- acquireRunSlot: (threadId?: string, runId?: string, activeState?: RunResult["state"], priority?: number) => Promise<() => Promise<void>>;
39
- resolvePersistedRunPriority: (threadId: string, runId: string) => Promise<number>;
40
- saveRecoveryIntent: (threadId: string, runId: string, payload: Record<string, unknown>) => Promise<void>;
41
- emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<unknown>;
42
- resolveApproval: (threadId: string, runId: string, approvalId: string, resolution: "approved" | "edited" | "rejected" | "expired") => Promise<InternalApprovalRecord>;
43
- listThreadMessages: (threadId: string) => Promise<TranscriptMessage[]>;
44
- loadRunInput: (threadId: string, runId: string) => Promise<MessageContent>;
45
- invoke: (binding: CompiledAgentBinding, message: MessageContent, threadId: string, runId: string, resumePayload: unknown, priorHistory: TranscriptMessage[]) => Promise<RunResult>;
46
- recordLlmSuccess: (startedAt: number) => void;
47
- recordLlmFailure: (startedAt: number) => void;
48
- clearRecoveryIntent: (threadId: string, runId: string) => Promise<void>;
49
- finalizeContinuedRun: (binding: Binding, threadId: string, runId: string, message: MessageContent, actual: RunResult, options: {
50
- previousState: RunResult["state"] | null;
51
- stateSequence: number;
52
- approvalSequence?: number;
53
- }) => Promise<RunResult>;
54
- };
55
- export {};
@@ -1,26 +0,0 @@
1
- import { getWorkspaceBinding } from "../bindings.js";
2
- import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord } from "./resume.js";
3
- export function createResumeRunRuntime(input) {
4
- return {
5
- getApprovalById: (approvalId) => input.persistence.getApproval(approvalId),
6
- getSession: (threadId) => input.getSession(threadId),
7
- resolveApprovalRecord: (resumeOptions, thread) => resolveHarnessApprovalRecord(input.persistence, resumeOptions, thread),
8
- getBinding: (agentId) => getWorkspaceBinding(input.workspace, agentId),
9
- buildResumePayload: (binding, approval, resumeOptions) => buildHarnessResumePayload(binding, approval, resumeOptions),
10
- getRunCancellation: (runId) => input.getRunCancellation(runId),
11
- finalizeCancelledRun: (threadId, runId, previousState, reason) => input.finalizeCancelledRun(threadId, runId, previousState, reason),
12
- setRunState: (threadId, runId, state, checkpointRef) => input.persistence.setRunState(threadId, runId, state, checkpointRef),
13
- acquireRunSlot: (threadId, runId, activeState, priority) => input.acquireRunSlot(threadId, runId, activeState, priority),
14
- resolvePersistedRunPriority: (threadId, runId) => input.resolvePersistedRunPriority(threadId, runId),
15
- saveRecoveryIntent: (threadId, runId, payload) => input.persistence.saveRecoveryIntent(threadId, runId, payload),
16
- emit: (threadId, runId, sequence, eventType, payload) => input.emit(threadId, runId, sequence, eventType, payload),
17
- resolveApproval: (threadId, runId, approvalId, resolution) => input.persistence.resolveApproval(threadId, runId, approvalId, resolution),
18
- listThreadMessages: (threadId) => input.persistence.listThreadMessages(threadId),
19
- loadRunInput: (threadId, runId) => input.loadRunInput(threadId, runId),
20
- invoke: (binding, message, threadId, runId, resumePayload, priorHistory) => input.runtimeAdapter.invoke(binding, message, threadId, runId, resumePayload, priorHistory),
21
- recordLlmSuccess: (startedAt) => input.recordLlmSuccess(startedAt),
22
- recordLlmFailure: (startedAt) => input.recordLlmFailure(startedAt),
23
- clearRecoveryIntent: (threadId, runId) => input.persistence.clearRecoveryIntent(threadId, runId),
24
- finalizeContinuedRun: (binding, threadId, runId, message, actual, options) => input.finalizeContinuedRun(binding, threadId, runId, message, actual, options),
25
- };
26
- }
@@ -1,48 +0,0 @@
1
- import type { CompiledAgentBinding, HarnessEvent, MessageContent, RunResult, TranscriptMessage } from "../../../contracts/types.js";
2
- import type { RuntimePersistence } from "../../../persistence/types.js";
3
- import type { AgentRuntimeAdapter } from "../../agent-runtime-adapter.js";
4
- export declare function createStreamRunRuntime(input: {
5
- persistence: RuntimePersistence;
6
- runtimeAdapter: AgentRuntimeAdapter;
7
- emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<HarnessEvent>;
8
- setRunStateAndEmit: (threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
9
- previousState: string | null;
10
- checkpointRef?: string | null;
11
- error?: string;
12
- }) => Promise<HarnessEvent>;
13
- requestApprovalAndEmit: (threadId: string, runId: string, input: MessageContent, interruptContent: string | undefined, checkpointRef: string, sequence: number) => Promise<{
14
- approval: {
15
- approvalId: string;
16
- pendingActionId: string;
17
- };
18
- event: HarnessEvent;
19
- }>;
20
- loadPriorHistory: (threadId: string, runId: string) => Promise<TranscriptMessage[]>;
21
- invokeWithHistory: (binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string) => Promise<RunResult>;
22
- emitSyntheticFallback: (threadId: string, runId: string, selectedAgentId: string, error: unknown) => Promise<void>;
23
- }): {
24
- loadPriorHistory: (threadId: string, runId: string) => Promise<TranscriptMessage[]>;
25
- stream: (binding: CompiledAgentBinding, message: MessageContent, threadId: string, priorHistory: TranscriptMessage[], options: {
26
- context?: Record<string, unknown>;
27
- state?: Record<string, unknown>;
28
- files?: Record<string, unknown>;
29
- runId: string;
30
- }) => AsyncGenerator<string | import("../../parsing/stream-event-parsing.js").RuntimeStreamChunk, any, any>;
31
- invokeWithHistory: (binding: CompiledAgentBinding, message: MessageContent, threadId: string, runId: string) => Promise<RunResult>;
32
- emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<HarnessEvent>;
33
- setRunStateAndEmit: (threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
34
- previousState: string | null;
35
- checkpointRef?: string | null;
36
- error?: string;
37
- }) => Promise<HarnessEvent>;
38
- requestApprovalAndEmit: (threadId: string, runId: string, message: MessageContent, interruptContent: string | undefined, checkpointRef: string, sequence: number) => Promise<{
39
- approval: {
40
- approvalId: string;
41
- pendingActionId: string;
42
- };
43
- event: HarnessEvent;
44
- }>;
45
- appendAssistantMessage: (threadId: string, runId: string, content?: string) => Promise<void>;
46
- clearRunRequest: (threadId: string, runId: string) => Promise<void>;
47
- emitSyntheticFallback: (threadId: string, runId: string, selectedAgentId: string, error: unknown) => Promise<void>;
48
- };
@@ -1,14 +0,0 @@
1
- import { appendAssistantMessage as appendLifecycleAssistantMessage } from "./run-lifecycle.js";
2
- export function createStreamRunRuntime(input) {
3
- return {
4
- loadPriorHistory: (threadId, runId) => input.loadPriorHistory(threadId, runId),
5
- stream: (binding, message, threadId, priorHistory, options) => input.runtimeAdapter.stream(binding, message, threadId, priorHistory, options),
6
- invokeWithHistory: (binding, message, threadId, runId) => input.invokeWithHistory(binding, message, threadId, runId),
7
- emit: (threadId, runId, sequence, eventType, payload) => input.emit(threadId, runId, sequence, eventType, payload),
8
- setRunStateAndEmit: (threadId, runId, sequence, state, options) => input.setRunStateAndEmit(threadId, runId, sequence, state, options),
9
- requestApprovalAndEmit: (threadId, runId, message, interruptContent, checkpointRef, sequence) => input.requestApprovalAndEmit(threadId, runId, message, interruptContent, checkpointRef, sequence),
10
- appendAssistantMessage: (threadId, runId, content) => appendLifecycleAssistantMessage(input.persistence, threadId, runId, content),
11
- clearRunRequest: (threadId, runId) => input.persistence.clearRunRequest(threadId, runId),
12
- emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => input.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
13
- };
14
- }