@botbotgo/agent-harness 0.0.95 → 0.0.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +1 -114
  2. package/README.zh.md +1 -70
  3. package/dist/api.d.ts +5 -5
  4. package/dist/config/workflows/langgraph-workflows.yaml +363 -111
  5. package/dist/config/workflows/runtime-profiles.yaml +94 -0
  6. package/dist/contracts/core.d.ts +9 -0
  7. package/dist/contracts/core.js +1 -0
  8. package/dist/contracts/runtime.d.ts +421 -0
  9. package/dist/contracts/runtime.js +1 -0
  10. package/dist/contracts/types.d.ts +3 -571
  11. package/dist/contracts/types.js +3 -1
  12. package/dist/contracts/workspace.d.ts +229 -0
  13. package/dist/contracts/workspace.js +1 -0
  14. package/dist/package-version.d.ts +1 -1
  15. package/dist/package-version.js +1 -1
  16. package/dist/runtime/adapter/compat/deepagent-compat.d.ts +16 -0
  17. package/dist/runtime/adapter/compat/deepagent-compat.js +45 -0
  18. package/dist/runtime/adapter/compat/openai-compatible.d.ts +2 -0
  19. package/dist/runtime/adapter/compat/openai-compatible.js +43 -0
  20. package/dist/runtime/adapter/index.d.ts +15 -0
  21. package/dist/runtime/adapter/index.js +15 -0
  22. package/dist/runtime/adapter/langgraph/presets.js +165 -0
  23. package/dist/runtime/{langgraph-profiles.d.ts → adapter/langgraph/profiles.d.ts} +1 -1
  24. package/dist/runtime/adapter/langgraph/profiles.js +206 -0
  25. package/dist/runtime/adapter/model/invocation-request.d.ts +10 -0
  26. package/dist/runtime/adapter/model/invocation-request.js +46 -0
  27. package/dist/runtime/adapter/model/message-assembly.d.ts +6 -0
  28. package/dist/runtime/adapter/model/message-assembly.js +21 -0
  29. package/dist/runtime/adapter/model/model-providers.d.ts +2 -0
  30. package/dist/runtime/adapter/model/model-providers.js +27 -0
  31. package/dist/runtime/adapter/resilience.d.ts +12 -0
  32. package/dist/runtime/adapter/resilience.js +60 -0
  33. package/dist/runtime/{declared-middleware.d.ts → adapter/tool/declared-middleware.d.ts} +1 -1
  34. package/dist/runtime/adapter/tool/interrupt-policy.d.ts +8 -0
  35. package/dist/runtime/adapter/tool/interrupt-policy.js +34 -0
  36. package/dist/runtime/adapter/tool/provider-tool.d.ts +2 -0
  37. package/dist/runtime/adapter/tool/provider-tool.js +25 -0
  38. package/dist/runtime/adapter/tool/resolved-tool.d.ts +18 -0
  39. package/dist/runtime/adapter/tool/resolved-tool.js +62 -0
  40. package/dist/runtime/adapter/tool/tool-arguments.d.ts +7 -0
  41. package/dist/runtime/adapter/tool/tool-arguments.js +87 -0
  42. package/dist/runtime/{tool-hitl.d.ts → adapter/tool/tool-hitl.d.ts} +2 -2
  43. package/dist/runtime/adapter/tool/tool-name-mapping.d.ts +13 -0
  44. package/dist/runtime/adapter/tool/tool-name-mapping.js +101 -0
  45. package/dist/runtime/agent-runtime-adapter.d.ts +5 -20
  46. package/dist/runtime/agent-runtime-adapter.js +42 -544
  47. package/dist/runtime/checkpoint-maintenance.d.ts +1 -45
  48. package/dist/runtime/checkpoint-maintenance.js +1 -259
  49. package/dist/runtime/file-checkpoint-saver.d.ts +1 -20
  50. package/dist/runtime/file-checkpoint-saver.js +1 -106
  51. package/dist/runtime/{event-bus.d.ts → harness/events/event-bus.d.ts} +1 -1
  52. package/dist/runtime/{event-sink.d.ts → harness/events/event-sink.d.ts} +1 -1
  53. package/dist/runtime/{event-sink.js → harness/events/event-sink.js} +1 -1
  54. package/dist/runtime/harness/events/events.d.ts +23 -0
  55. package/dist/runtime/harness/events/events.js +61 -0
  56. package/dist/runtime/harness/events/streaming.d.ts +19 -0
  57. package/dist/runtime/harness/events/streaming.js +96 -0
  58. package/dist/runtime/harness/index.d.ts +16 -0
  59. package/dist/runtime/harness/index.js +16 -0
  60. package/dist/runtime/harness/run/helpers.d.ts +33 -0
  61. package/dist/runtime/harness/run/helpers.js +74 -0
  62. package/dist/runtime/harness/run/resources.d.ts +7 -0
  63. package/dist/runtime/harness/run/resources.js +58 -0
  64. package/dist/runtime/harness/run/resume.d.ts +6 -0
  65. package/dist/runtime/harness/run/resume.js +56 -0
  66. package/dist/runtime/harness/run/routing.d.ts +12 -0
  67. package/dist/runtime/harness/run/routing.js +47 -0
  68. package/dist/runtime/harness/run/run-lifecycle.d.ts +37 -0
  69. package/dist/runtime/harness/run/run-lifecycle.js +109 -0
  70. package/dist/runtime/harness/run/run-queue.d.ts +17 -0
  71. package/dist/runtime/harness/run/run-queue.js +43 -0
  72. package/dist/runtime/{health-monitor.d.ts → harness/system/health-monitor.d.ts} +3 -3
  73. package/dist/runtime/{health-monitor.js → harness/system/health-monitor.js} +2 -2
  74. package/dist/runtime/{inventory.d.ts → harness/system/inventory.d.ts} +2 -2
  75. package/dist/runtime/{inventory.js → harness/system/inventory.js} +4 -4
  76. package/dist/runtime/{policy-engine.d.ts → harness/system/policy-engine.d.ts} +1 -1
  77. package/dist/runtime/{policy-engine.js → harness/system/policy-engine.js} +1 -1
  78. package/dist/runtime/{skill-requirements.d.ts → harness/system/skill-requirements.d.ts} +1 -1
  79. package/dist/runtime/{skill-requirements.js → harness/system/skill-requirements.js} +1 -1
  80. package/dist/runtime/{thread-memory-sync.d.ts → harness/system/thread-memory-sync.d.ts} +2 -2
  81. package/dist/runtime/{thread-memory-sync.js → harness/system/thread-memory-sync.js} +1 -1
  82. package/dist/runtime/harness.d.ts +2 -7
  83. package/dist/runtime/harness.js +158 -477
  84. package/dist/runtime/index.d.ts +7 -7
  85. package/dist/runtime/index.js +7 -7
  86. package/dist/runtime/maintenance/checkpoint-maintenance.d.ts +45 -0
  87. package/dist/runtime/maintenance/checkpoint-maintenance.js +259 -0
  88. package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +20 -0
  89. package/dist/runtime/maintenance/file-checkpoint-saver.js +106 -0
  90. package/dist/runtime/maintenance/index.d.ts +4 -0
  91. package/dist/runtime/maintenance/index.js +4 -0
  92. package/dist/runtime/{runtime-record-maintenance.d.ts → maintenance/runtime-record-maintenance.d.ts} +1 -1
  93. package/dist/runtime/{runtime-record-maintenance.js → maintenance/runtime-record-maintenance.js} +2 -2
  94. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +9 -0
  95. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +39 -0
  96. package/dist/runtime/parsing/stream-event-parsing.d.ts +6 -0
  97. package/dist/runtime/parsing/stream-event-parsing.js +231 -0
  98. package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +1 -9
  99. package/dist/runtime/sqlite-maintained-checkpoint-saver.js +1 -39
  100. package/dist/runtime/support/harness-support.d.ts +4 -4
  101. package/dist/runtime/support/harness-support.js +14 -3
  102. package/dist/runtime/support/runtime-factories.d.ts +1 -1
  103. package/dist/runtime/support/runtime-factories.js +1 -1
  104. package/dist/workspace/agent-binding-compiler.js +39 -3
  105. package/dist/workspace/object-loader.js +5 -1
  106. package/package.json +4 -4
  107. package/dist/runtime/langgraph-presets.js +0 -165
  108. package/dist/runtime/langgraph-profiles.js +0 -206
  109. /package/dist/runtime/{langgraph-presets.d.ts → adapter/langgraph/presets.d.ts} +0 -0
  110. /package/dist/runtime/{declared-middleware.js → adapter/tool/declared-middleware.js} +0 -0
  111. /package/dist/runtime/{tool-hitl.js → adapter/tool/tool-hitl.js} +0 -0
  112. /package/dist/runtime/{event-bus.js → harness/events/event-bus.js} +0 -0
  113. /package/dist/runtime/{store.d.ts → harness/system/store.d.ts} +0 -0
  114. /package/dist/runtime/{store.js → harness/system/store.js} +0 -0
@@ -1,4 +1,235 @@
1
1
  import { extractReasoningText, extractVisibleOutput, hasToolCalls, readTextContent } from "./output-parsing.js";
2
+ function asObject(value) {
3
+ return typeof value === "object" && value !== null ? value : undefined;
4
+ }
5
+ function extractCustomStreamData(event) {
6
+ const typed = asObject(event);
7
+ if (!typed) {
8
+ return null;
9
+ }
10
+ const eventName = typeof typed.event === "string" ? typed.event : "";
11
+ const ns = Array.isArray(typed.ns) ? typed.ns.filter((item) => typeof item === "string") : undefined;
12
+ const data = asObject(typed.data);
13
+ if (eventName === "on_custom_event") {
14
+ return {
15
+ ...(ns && ns.length > 0 ? { ns } : {}),
16
+ data: typed.data,
17
+ };
18
+ }
19
+ if (eventName === "on_chain_stream" && data && "custom" in data) {
20
+ return {
21
+ ...(ns && ns.length > 0 ? { ns } : {}),
22
+ data: data.custom,
23
+ };
24
+ }
25
+ return null;
26
+ }
27
+ export function normalizeUpstreamRuntimeEvent(event) {
28
+ const typed = asObject(event);
29
+ const tags = Array.isArray(typed?.tags) ? typed.tags.filter((item) => typeof item === "string") : undefined;
30
+ const ns = Array.isArray(typed?.ns) ? typed.ns.filter((item) => typeof item === "string") : undefined;
31
+ const name = typeof typed?.name === "string" ? typed.name : undefined;
32
+ const eventName = typeof typed?.event === "string" ? typed.event : undefined;
33
+ const runType = typeof typed?.run_type === "string" ? typed.run_type : undefined;
34
+ const data = asObject(typed?.data);
35
+ const metadata = asObject(typed?.metadata);
36
+ const normalized = normalizeUpstreamEventShape({
37
+ raw: event,
38
+ event: eventName,
39
+ name,
40
+ runType,
41
+ metadata,
42
+ tags,
43
+ data,
44
+ ns,
45
+ });
46
+ const streamPart = normalizeCompatibleStreamPart({
47
+ raw: event,
48
+ event: eventName,
49
+ name,
50
+ runType,
51
+ metadata,
52
+ tags,
53
+ data,
54
+ ns,
55
+ normalized,
56
+ });
57
+ return {
58
+ format: "langgraph-v2",
59
+ normalized,
60
+ raw: event,
61
+ event: eventName,
62
+ name,
63
+ runType,
64
+ data,
65
+ metadata,
66
+ ...(tags && tags.length > 0 ? { tags } : {}),
67
+ ...(ns && ns.length > 0 ? { ns } : {}),
68
+ streamPart,
69
+ };
70
+ }
71
+ function normalizeUpstreamEventShape(event) {
72
+ const nodeName = event.name;
73
+ const ns = event.ns;
74
+ const interruptPayload = extractInterruptPayload(event.raw);
75
+ if (interruptPayload) {
76
+ return {
77
+ kind: "interrupt",
78
+ interrupt: parseMaybeJson(interruptPayload),
79
+ ...(ns && ns.length > 0 ? { ns } : {}),
80
+ ...(nodeName ? { nodeName } : {}),
81
+ };
82
+ }
83
+ const reasoning = extractReasoningStreamOutput(event.raw);
84
+ if (reasoning) {
85
+ return {
86
+ kind: "reasoning-delta",
87
+ text: reasoning,
88
+ ...(ns && ns.length > 0 ? { ns } : {}),
89
+ ...(nodeName ? { nodeName } : {}),
90
+ };
91
+ }
92
+ const visibleText = extractVisibleStreamOutput(event.raw);
93
+ if (visibleText) {
94
+ return {
95
+ kind: "text-delta",
96
+ source: "model",
97
+ text: visibleText,
98
+ ...(ns && ns.length > 0 ? { ns } : {}),
99
+ ...(nodeName ? { nodeName } : {}),
100
+ };
101
+ }
102
+ const stateText = extractStateStreamOutput(event.raw);
103
+ if (stateText) {
104
+ return {
105
+ kind: "text-delta",
106
+ source: "state",
107
+ text: stateText,
108
+ ...(ns && ns.length > 0 ? { ns } : {}),
109
+ ...(nodeName ? { nodeName } : {}),
110
+ };
111
+ }
112
+ if (event.event === "on_tool_start" || (event.event === "on_chain_start" && event.runType === "tool")) {
113
+ return {
114
+ kind: "tool-start",
115
+ toolName: nodeName ?? "tool",
116
+ ...(event.data && "input" in event.data ? { input: event.data.input } : {}),
117
+ ...(ns && ns.length > 0 ? { ns } : {}),
118
+ ...(nodeName ? { nodeName } : {}),
119
+ };
120
+ }
121
+ const toolResult = extractToolResult(event.raw);
122
+ if (toolResult) {
123
+ return {
124
+ kind: "tool-end",
125
+ toolName: toolResult.toolName,
126
+ ...(toolResult.output !== undefined ? { output: toolResult.output } : {}),
127
+ ...(toolResult.isError !== undefined ? { isError: toolResult.isError } : {}),
128
+ ...(ns && ns.length > 0 ? { ns } : {}),
129
+ ...(nodeName ? { nodeName } : {}),
130
+ };
131
+ }
132
+ const agentStep = extractAgentStep(event.raw);
133
+ if (agentStep) {
134
+ return {
135
+ kind: "agent-step",
136
+ label: agentStep,
137
+ ...(ns && ns.length > 0 ? { ns } : {}),
138
+ ...(nodeName ? { nodeName } : {}),
139
+ };
140
+ }
141
+ return {
142
+ kind: "run-event",
143
+ eventName: event.event ?? "unknown",
144
+ ...(event.data ? { data: event.data } : {}),
145
+ ...(ns && ns.length > 0 ? { ns } : {}),
146
+ ...(nodeName ? { nodeName } : {}),
147
+ };
148
+ }
149
+ function normalizeCompatibleStreamPart(event) {
150
+ const custom = extractCustomCompatibleStreamPart(event.raw);
151
+ if (custom) {
152
+ return custom;
153
+ }
154
+ const ns = event.ns ?? [];
155
+ const messageChunk = extractMessageCompatibleStreamPart(event);
156
+ if (messageChunk) {
157
+ return {
158
+ type: "messages",
159
+ ns,
160
+ data: [messageChunk, buildStreamPartMetadata(event)],
161
+ };
162
+ }
163
+ return {
164
+ type: "updates",
165
+ ns,
166
+ data: buildUpdateCompatiblePayload(event),
167
+ };
168
+ }
169
+ function buildStreamPartMetadata(event) {
170
+ return {
171
+ ...(event.metadata ?? {}),
172
+ ...(event.event ? { event: event.event } : {}),
173
+ ...(event.name ? { name: event.name } : {}),
174
+ ...(event.runType ? { run_type: event.runType } : {}),
175
+ ...(event.tags && event.tags.length > 0 ? { tags: event.tags } : {}),
176
+ };
177
+ }
178
+ function extractMessageCompatibleStreamPart(event) {
179
+ const typed = asObject(event.raw);
180
+ const data = asObject(typed?.data);
181
+ if (event.event === "on_chat_model_stream" && data && "chunk" in data) {
182
+ return data.chunk;
183
+ }
184
+ if (event.normalized.kind === "tool-end") {
185
+ return {
186
+ type: "tool",
187
+ name: event.normalized.toolName,
188
+ content: event.normalized.output,
189
+ ...(event.normalized.isError !== undefined ? { isError: event.normalized.isError } : {}),
190
+ };
191
+ }
192
+ return null;
193
+ }
194
+ function buildUpdateCompatiblePayload(event) {
195
+ const key = event.name ?? event.event ?? "event";
196
+ return {
197
+ [key]: extractUpdateNodePayload(event),
198
+ };
199
+ }
200
+ function extractUpdateNodePayload(event) {
201
+ const data = event.data;
202
+ if (event.event === "on_chain_stream" && data && "chunk" in data) {
203
+ return data.chunk;
204
+ }
205
+ if ((event.event === "on_tool_start" || event.event === "on_chain_start") && data && "input" in data) {
206
+ return { input: data.input };
207
+ }
208
+ if ((event.event === "on_tool_end" || event.event === "on_chain_end" || event.event === "on_chat_model_end") && data && "output" in data) {
209
+ return data.output;
210
+ }
211
+ if ((event.event === "on_tool_error" || event.event === "on_chain_error") && data) {
212
+ return {
213
+ ...(data.output !== undefined ? { output: data.output } : {}),
214
+ ...(data.error !== undefined ? { error: data.error } : {}),
215
+ };
216
+ }
217
+ if (event.normalized.kind === "interrupt") {
218
+ return { __interrupt__: event.normalized.interrupt };
219
+ }
220
+ return data ?? {};
221
+ }
222
+ export function extractCustomCompatibleStreamPart(event) {
223
+ const custom = extractCustomStreamData(event);
224
+ if (!custom) {
225
+ return null;
226
+ }
227
+ return {
228
+ type: "custom",
229
+ ns: custom.ns ?? [],
230
+ data: custom.data,
231
+ };
232
+ }
2
233
  function formatStepValue(value, maxLength = 120) {
3
234
  if (value == null)
4
235
  return "";
@@ -1,9 +1 @@
1
- import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
- import type { RunnableConfig } from "@langchain/core/runnables";
3
- export declare class ManagedSqliteSaver extends SqliteSaver {
4
- constructor(db: ConstructorParameters<typeof SqliteSaver>[0]);
5
- prepareMaintenance(): void;
6
- setup(): void;
7
- put(config: RunnableConfig, checkpoint: Parameters<SqliteSaver["put"]>[1], metadata: Parameters<SqliteSaver["put"]>[2]): Promise<RunnableConfig<Record<string, any>>>;
8
- deleteThread(threadId: string): Promise<void>;
9
- }
1
+ export * from "./maintenance/sqlite-maintained-checkpoint-saver.js";
@@ -1,39 +1 @@
1
- import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
2
- export class ManagedSqliteSaver extends SqliteSaver {
3
- constructor(db) {
4
- super(db);
5
- }
6
- prepareMaintenance() {
7
- this.setup();
8
- }
9
- setup() {
10
- super.setup();
11
- this.db.exec(`
12
- CREATE TABLE IF NOT EXISTS checkpoint_maintenance_meta (
13
- thread_id TEXT NOT NULL,
14
- checkpoint_ns TEXT NOT NULL DEFAULT '',
15
- checkpoint_id TEXT NOT NULL,
16
- created_at_ms INTEGER NOT NULL,
17
- PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
18
- );`);
19
- }
20
- async put(config, checkpoint, metadata) {
21
- const result = await super.put(config, checkpoint, metadata);
22
- const threadId = result.configurable?.thread_id;
23
- const checkpointNs = result.configurable?.checkpoint_ns ?? "";
24
- const checkpointId = result.configurable?.checkpoint_id;
25
- if (!threadId || !checkpointId) {
26
- throw new Error("Missing checkpoint identity after SqliteSaver.put");
27
- }
28
- this.db
29
- .prepare(`INSERT OR IGNORE INTO checkpoint_maintenance_meta
30
- (thread_id, checkpoint_ns, checkpoint_id, created_at_ms)
31
- VALUES (?, ?, ?, ?)`)
32
- .run(threadId, checkpointNs, checkpointId, Date.now());
33
- return result;
34
- }
35
- async deleteThread(threadId) {
36
- await super.deleteThread(threadId);
37
- this.db.prepare(`DELETE FROM checkpoint_maintenance_meta WHERE thread_id = ?`).run(threadId);
38
- }
39
- }
1
+ export * from "./maintenance/sqlite-maintained-checkpoint-saver.js";
@@ -21,8 +21,8 @@ export declare function heuristicRoute(input: string, primaryBinding?: {
21
21
  export declare function createHarnessEvent(threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>, source?: HarnessEvent["source"]): HarnessEvent;
22
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
- primaryBinding: import("../../contracts/types.js").CompiledAgentBinding;
25
- secondaryBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
26
- researchBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
27
- hostBindings: import("../../contracts/types.js").CompiledAgentBinding[];
24
+ primaryBinding: import("../../contracts/workspace.js").CompiledAgentBinding;
25
+ secondaryBinding: import("../../contracts/workspace.js").CompiledAgentBinding | undefined;
26
+ researchBinding: import("../../contracts/workspace.js").CompiledAgentBinding | undefined;
27
+ hostBindings: import("../../contracts/workspace.js").CompiledAgentBinding[];
28
28
  };
@@ -133,14 +133,25 @@ export function inferRoutingBindings(workspace) {
133
133
  const runtimeEntryBindings = Array.from(workspace.bindings.values()).filter((binding) => isRuntimeEntryBinding(binding));
134
134
  const orchestrationHosts = runtimeEntryBindings.filter((binding) => binding.agent.executionMode === "deepagent" || binding.agent.executionMode === "langgraph" || Boolean(binding.deepAgentParams));
135
135
  const routingHosts = orchestrationHosts.length > 0 ? orchestrationHosts : runtimeEntryBindings;
136
+ const deepAgentHosts = routingHosts.filter((binding) => binding.agent.executionMode === "deepagent" || Boolean(binding.deepAgentParams));
137
+ const nonDeepAgentHosts = routingHosts.filter((binding) => !deepAgentHosts.includes(binding));
136
138
  const researchBinding = routingHosts.find((binding) => binding.agent.id === "research-lite" || binding.agent.id === "research");
137
139
  const directBinding = routingHosts.find((binding) => binding.agent.id === "direct");
138
140
  const delegationHosts = routingHosts.filter((binding) => isDelegationCapableBinding(binding));
141
+ const deepAgentDelegationHosts = deepAgentHosts.filter((binding) => isDelegationCapableBinding(binding));
142
+ const nonDeepAgentDelegationHosts = nonDeepAgentHosts.filter((binding) => isDelegationCapableBinding(binding));
139
143
  const lightweightHosts = routingHosts.filter((binding) => !isDelegationCapableBinding(binding));
140
144
  const defaultOrchestratingHost = routingHosts.find((binding) => binding.agent.id === "orchestra") ??
141
- delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
142
- delegationHosts[0];
143
- const delegationPreferredSecondary = delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
145
+ deepAgentDelegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
146
+ deepAgentDelegationHosts[0] ??
147
+ deepAgentHosts[0] ??
148
+ nonDeepAgentDelegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
149
+ nonDeepAgentDelegationHosts[0] ??
150
+ nonDeepAgentHosts[0];
151
+ const delegationPreferredSecondary = deepAgentDelegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
152
+ deepAgentDelegationHosts[0] ??
153
+ nonDeepAgentDelegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
154
+ nonDeepAgentDelegationHosts[0] ??
144
155
  delegationHosts[0];
145
156
  const genericLightweightHost = lightweightHosts.find((binding) => binding.agent.id !== researchBinding?.agent.id);
146
157
  const primaryBinding = defaultOrchestratingHost ?? directBinding ?? genericLightweightHost ?? routingHosts[0] ?? runtimeEntryBindings[0];
@@ -1,3 +1,3 @@
1
- import { type StoreLike } from "../store.js";
1
+ import { type StoreLike } from "../harness/system/store.js";
2
2
  export declare function createStoreForConfig(storeConfig: Record<string, unknown>, runRoot: string): StoreLike;
3
3
  export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown> | boolean, runRoot: string): unknown;
@@ -3,7 +3,7 @@ import { MemorySaver } from "@langchain/langgraph";
3
3
  import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
4
4
  import { FileCheckpointSaver } from "../file-checkpoint-saver.js";
5
5
  import { ManagedSqliteSaver } from "../sqlite-maintained-checkpoint-saver.js";
6
- import { createInMemoryStore, FileBackedStore } from "../store.js";
6
+ import { createInMemoryStore, FileBackedStore } from "../harness/system/store.js";
7
7
  export function createStoreForConfig(storeConfig, runRoot) {
8
8
  const kind = typeof storeConfig.kind === "string" ? storeConfig.kind : "FileStore";
9
9
  switch (kind) {
@@ -261,6 +261,22 @@ function resolveLangGraphWorkflowConfig(agent, refs) {
261
261
  agent.langchainAgentConfig.passthrough.with
262
262
  ? { ...agent.langchainAgentConfig.passthrough.with }
263
263
  : undefined;
264
+ const workspaceProfile = profile ? getWorkspaceObject(refs, `runtime-profile/${profile}`) : undefined;
265
+ if (workspaceProfile && workspaceProfile.kind !== "runtime-profile") {
266
+ throw new Error(`Agent ${agent.id} profile ${profile} does not resolve to a RuntimeProfile object`);
267
+ }
268
+ const workspaceProfileValue = workspaceProfile?.value && typeof workspaceProfile.value === "object"
269
+ ? workspaceProfile.value
270
+ : undefined;
271
+ const workspaceProfileDefaults = typeof workspaceProfileValue?.defaults === "object" && workspaceProfileValue.defaults
272
+ ? workspaceProfileValue.defaults
273
+ : undefined;
274
+ const mergedProfileWith = workspaceProfileDefaults || profileWith
275
+ ? {
276
+ ...(workspaceProfileDefaults ?? {}),
277
+ ...(profileWith ?? {}),
278
+ }
279
+ : undefined;
264
280
  const preset = typeof agent.langchainAgentConfig?.preset === "string" && agent.langchainAgentConfig.preset.trim()
265
281
  ? agent.langchainAgentConfig.preset.trim()
266
282
  : typeof agent.langchainAgentConfig?.passthrough === "object" &&
@@ -284,21 +300,41 @@ function resolveLangGraphWorkflowConfig(agent, refs) {
284
300
  agent.langchainAgentConfig.passthrough.langgraph
285
301
  ? agent.langchainAgentConfig.passthrough.langgraph
286
302
  : undefined;
303
+ const profileWorkflowRef = typeof workspaceProfileValue?.workflowRef === "string" && workspaceProfileValue.workflowRef.trim()
304
+ ? workspaceProfileValue.workflowRef.trim()
305
+ : typeof workspaceProfileValue?.workflow === "string" && workspaceProfileValue.workflow.trim()
306
+ ? workspaceProfileValue.workflow.trim()
307
+ : typeof workspaceProfileValue?.workflow === "object" &&
308
+ workspaceProfileValue.workflow &&
309
+ typeof workspaceProfileValue.workflow.ref === "string" &&
310
+ workspaceProfileValue.workflow.ref.trim()
311
+ ? workspaceProfileValue.workflow.ref.trim()
312
+ : undefined;
313
+ const resolvedProfileWorkflowConfig = !workflowConfig && profileWorkflowRef
314
+ ? materializeWorkspaceObjectConfig(refs, profileWorkflowRef, ["langgraph-workflow"], `Agent ${agent.id} profile ${profile} workflow`)
315
+ : undefined;
287
316
  if (!workflowConfig) {
288
- return profile || preset ? { ...(profile ? { profile } : {}), ...(profileWith ? { with: profileWith } : {}), ...(preset ? { preset } : {}) } : undefined;
317
+ return profile || preset || resolvedProfileWorkflowConfig
318
+ ? {
319
+ ...(profile ? { profile } : {}),
320
+ ...(mergedProfileWith ? { with: mergedProfileWith } : {}),
321
+ ...(preset ? { preset } : {}),
322
+ ...(resolvedProfileWorkflowConfig ? { config: resolvedProfileWorkflowConfig } : {}),
323
+ }
324
+ : undefined;
289
325
  }
290
326
  if (isRefConfig(workflowConfig)) {
291
327
  return {
292
328
  config: materializeWorkspaceObjectConfig(refs, workflowConfig.ref, ["langgraph-workflow"], `Agent ${agent.id} workflow`),
293
329
  ...(profile ? { profile } : {}),
294
- ...(profileWith ? { with: profileWith } : {}),
330
+ ...(mergedProfileWith ? { with: mergedProfileWith } : {}),
295
331
  ...(preset ? { preset } : {}),
296
332
  };
297
333
  }
298
334
  return {
299
335
  config: workflowConfig,
300
336
  ...(profile ? { profile } : {}),
301
- ...(profileWith ? { with: profileWith } : {}),
337
+ ...(mergedProfileWith ? { with: mergedProfileWith } : {}),
302
338
  ...(preset ? { preset } : {}),
303
339
  };
304
340
  }
@@ -152,6 +152,8 @@ function normalizeKind(kind) {
152
152
  return "runtime-memory";
153
153
  case "LangGraphWorkflow":
154
154
  return "langgraph-workflow";
155
+ case "RuntimeProfile":
156
+ return "runtime-profile";
155
157
  case "Prompt":
156
158
  return "prompt";
157
159
  case "McpServer":
@@ -473,7 +475,9 @@ async function objectItemsFromDocument(document, sourcePath) {
473
475
  ? normalizeCatalogSpec(document, { defaultKind: "McpServer" })
474
476
  : catalogKind === "LangGraphWorkflows"
475
477
  ? normalizeCatalogSpec(document, { defaultKind: "LangGraphWorkflow" })
476
- : [];
478
+ : catalogKind === "RuntimeProfiles"
479
+ ? normalizeCatalogSpec(document, { defaultKind: "RuntimeProfile" })
480
+ : [];
477
481
  if (catalogItems.length > 0) {
478
482
  return catalogItems;
479
483
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.95",
3
+ "version": "0.0.97",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
@@ -53,9 +53,9 @@
53
53
  "scripts": {
54
54
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
55
55
  "check": "tsc -p tsconfig.json --noEmit",
56
- "test": "vitest run test/hello-file.test.ts test/public-api.test.ts test/runtime-health.test.ts test/memory-runtime.test.ts test/sqlite-persistence.test.ts test/runtime-queue-lease.test.ts test/runtime-cancel.test.ts test/runtime-record-maintenance.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/stock-research-app-config.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/product-boundary-docs.test.ts test/long-term-memory-docs.test.ts test/local-docs-persistence-inventory.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts test/upstream-runtime-ab-benchmark.test.ts test/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts test/coding-agent-guide.test.ts",
57
- "test:upstream-ab-real": "vitest run test/upstream-runtime-ab-real.test.ts",
58
- "test:real-providers": "vitest run test/real-provider-harness.test.ts",
56
+ "test": "vitest run $(find test -name '*.test.ts' -print | sort)",
57
+ "test:upstream-ab-real": "vitest run test/benchmark/upstream-runtime-ab-real.test.ts",
58
+ "test:real-providers": "vitest run test/providers/real-provider-harness.test.ts",
59
59
  "release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
60
60
  "release:pack": "npm pack --dry-run",
61
61
  "release:publish": "npm publish --access public --registry https://registry.npmjs.org/"
@@ -1,165 +0,0 @@
1
- const DEFAULT_PLANNER_PROMPT = "You are the workflow planner. Produce a concise execution plan for the user request before execution starts.";
2
- const DEFAULT_REVIEWER_PROMPT = "Review the current result, call out missing verification or obvious gaps, and state whether the work looks sufficient.";
3
- const DEFAULT_REPLANNER_PROMPT = "Refine the plan based on the reviewer feedback and current result. Return only the updated plan.";
4
- const DEFAULT_FINALIZER_PROMPT = "Rewrite the current result into a concise user-facing final answer. Preserve facts and caveats.";
5
- export const SUPPORTED_LANGGRAPH_PRESETS = [
6
- "react",
7
- "prompt-chaining",
8
- "routing",
9
- "parallelization",
10
- "plan-execute",
11
- "review-loop",
12
- "evaluator-optimizer",
13
- "approval-gate",
14
- "handoff",
15
- "orchestrator-workers",
16
- ];
17
- function requireAgent(preset, options) {
18
- if (typeof options.agent === "string" && options.agent.trim()) {
19
- return options.agent.trim();
20
- }
21
- throw new Error(`LangGraph preset ${preset} requires config.agent`);
22
- }
23
- export function resolveLangGraphPresetWorkflow(presetName, options = {}) {
24
- switch (presetName) {
25
- case undefined:
26
- case "":
27
- return undefined;
28
- case "react":
29
- return {
30
- entryNode: "executor",
31
- nodes: [
32
- { id: "executor", kind: "agent" },
33
- ],
34
- edges: [],
35
- };
36
- case "prompt-chaining":
37
- case "plan-execute":
38
- return {
39
- entryNode: "planner",
40
- nodes: [
41
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
42
- { id: "executor", kind: "agent" },
43
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
44
- ],
45
- edges: [
46
- { from: "planner", to: "executor" },
47
- { from: "executor", to: "finalizer", when: "has_result" },
48
- ],
49
- };
50
- case "routing": {
51
- const agent = requireAgent("routing", options);
52
- return {
53
- entryNode: "planner",
54
- nodes: [
55
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
56
- { id: "worker", kind: "agent", agent },
57
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
58
- ],
59
- edges: [
60
- { from: "planner", to: "worker" },
61
- { from: "worker", to: "finalizer", when: "has_result" },
62
- ],
63
- };
64
- }
65
- case "parallelization": {
66
- const agent = requireAgent("parallelization", options);
67
- return {
68
- entryNode: "planner",
69
- nodes: [
70
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
71
- { id: "worker", kind: "agent", agent },
72
- { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
73
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
74
- ],
75
- edges: [
76
- { from: "planner", to: "worker" },
77
- { from: "worker", to: "reviewer", when: "has_result" },
78
- { from: "reviewer", to: "finalizer", when: "review_ok" },
79
- ],
80
- };
81
- }
82
- case "review-loop":
83
- return {
84
- entryNode: "planner",
85
- nodes: [
86
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
87
- { id: "executor", kind: "agent" },
88
- { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
89
- { id: "replanner", kind: "llm", role: "replanner", prompt: DEFAULT_REPLANNER_PROMPT },
90
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
91
- ],
92
- edges: [
93
- { from: "planner", to: "executor" },
94
- { from: "executor", to: "reviewer", when: "has_result" },
95
- { from: "reviewer", to: "finalizer", when: "review_ok" },
96
- { from: "reviewer", to: "replanner", when: "review_incomplete" },
97
- { from: "replanner", to: "executor", when: "has_plan" },
98
- ],
99
- };
100
- case "evaluator-optimizer":
101
- return {
102
- entryNode: "executor",
103
- nodes: [
104
- { id: "executor", kind: "agent" },
105
- { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
106
- { id: "replanner", kind: "llm", role: "replanner", prompt: DEFAULT_REPLANNER_PROMPT },
107
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
108
- ],
109
- edges: [
110
- { from: "executor", to: "reviewer", when: "has_result" },
111
- { from: "reviewer", to: "finalizer", when: "review_ok" },
112
- { from: "reviewer", to: "replanner", when: "review_incomplete" },
113
- { from: "replanner", to: "executor", when: "has_plan" },
114
- ],
115
- };
116
- case "approval-gate":
117
- return {
118
- entryNode: "planner",
119
- nodes: [
120
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
121
- { id: "approval", kind: "approval" },
122
- { id: "executor", kind: "agent" },
123
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
124
- ],
125
- edges: [
126
- { from: "planner", to: "approval" },
127
- { from: "approval", to: "executor", when: "approval_approved" },
128
- { from: "approval", to: "executor", when: "approval_edited" },
129
- { from: "executor", to: "finalizer", when: "has_result" },
130
- ],
131
- };
132
- case "handoff": {
133
- const agent = requireAgent("handoff", options);
134
- return {
135
- entryNode: "worker",
136
- nodes: [
137
- { id: "worker", kind: "agent", agent },
138
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
139
- ],
140
- edges: [
141
- { from: "worker", to: "finalizer", when: "has_result" },
142
- ],
143
- };
144
- }
145
- case "orchestrator-workers": {
146
- const agent = requireAgent("orchestrator-workers", options);
147
- return {
148
- entryNode: "planner",
149
- nodes: [
150
- { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
151
- { id: "worker", kind: "agent", agent },
152
- { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
153
- { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
154
- ],
155
- edges: [
156
- { from: "planner", to: "worker" },
157
- { from: "worker", to: "reviewer", when: "has_result" },
158
- { from: "reviewer", to: "finalizer", when: "review_ok" },
159
- ],
160
- };
161
- }
162
- default:
163
- throw new Error(`Unsupported LangGraph preset ${String(presetName)}. Supported presets: ${SUPPORTED_LANGGRAPH_PRESETS.join(", ")}`);
164
- }
165
- }