@botbotgo/agent-harness 0.0.92 → 0.0.94

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 (41) hide show
  1. package/README.md +153 -31
  2. package/README.zh.md +108 -28
  3. package/dist/benchmark/upstream-runtime-ab-benchmark.d.ts +1 -1
  4. package/dist/benchmark/upstream-runtime-ab-benchmark.js +2 -1
  5. package/dist/config/workflows/langgraph-workflows.yaml +318 -0
  6. package/dist/contracts/types.d.ts +8 -3
  7. package/dist/init-project.js +7 -7
  8. package/dist/package-version.d.ts +1 -1
  9. package/dist/package-version.js +1 -1
  10. package/dist/runtime/agent-runtime-adapter.d.ts +49 -1
  11. package/dist/runtime/agent-runtime-adapter.js +1103 -50
  12. package/dist/runtime/harness.d.ts +2 -0
  13. package/dist/runtime/harness.js +55 -11
  14. package/dist/runtime/inventory.d.ts +1 -1
  15. package/dist/runtime/inventory.js +1 -1
  16. package/dist/runtime/langgraph-presets.d.ts +25 -0
  17. package/dist/runtime/langgraph-presets.js +165 -0
  18. package/dist/runtime/langgraph-profiles.d.ts +6 -0
  19. package/dist/runtime/langgraph-profiles.js +206 -0
  20. package/dist/runtime/policy-engine.js +0 -5
  21. package/dist/runtime/support/compiled-binding.d.ts +4 -1
  22. package/dist/runtime/support/compiled-binding.js +24 -2
  23. package/dist/runtime/support/harness-support.js +3 -3
  24. package/dist/runtime/support/runtime-entry.js +1 -1
  25. package/dist/workspace/agent-binding-compiler.js +111 -8
  26. package/dist/workspace/compile.js +1 -3
  27. package/dist/workspace/object-loader.js +46 -5
  28. package/dist/workspace/support/agent-capabilities.js +2 -2
  29. package/dist/workspace/support/workspace-ref-utils.d.ts +2 -1
  30. package/dist/workspace/support/workspace-ref-utils.js +21 -0
  31. package/dist/workspace/validate.js +1 -1
  32. package/package.json +2 -2
  33. /package/dist/config/{backends.yaml → catalogs/backends.yaml} +0 -0
  34. /package/dist/config/{embedding-models.yaml → catalogs/embedding-models.yaml} +0 -0
  35. /package/dist/config/{mcp.yaml → catalogs/mcp.yaml} +0 -0
  36. /package/dist/config/{models.yaml → catalogs/models.yaml} +0 -0
  37. /package/dist/config/{stores.yaml → catalogs/stores.yaml} +0 -0
  38. /package/dist/config/{tools.yaml → catalogs/tools.yaml} +0 -0
  39. /package/dist/config/{vector-stores.yaml → catalogs/vector-stores.yaml} +0 -0
  40. /package/dist/config/{runtime-memory.yaml → runtime/runtime-memory.yaml} +0 -0
  41. /package/dist/config/{workspace.yaml → runtime/workspace.yaml} +0 -0
@@ -102,6 +102,8 @@ export declare class AgentHarnessRuntime {
102
102
  private executeQueuedRun;
103
103
  private checkpointRefForState;
104
104
  private finalizeContinuedRun;
105
+ private synthesizeCompletedRun;
106
+ private reviewCompletedRun;
105
107
  private emitOutputDeltaAndCreateItem;
106
108
  private createContentBlocksItem;
107
109
  private createToolResultKey;
@@ -660,7 +660,7 @@ export class AgentHarnessRuntime {
660
660
  if (cancelledAfterInvoke.requested) {
661
661
  return this.finalizeCancelledRun(threadId, runId, previousState === "queued" ? "running" : previousState, cancelledAfterInvoke.reason);
662
662
  }
663
- const finalized = await this.finalizeContinuedRun(threadId, runId, input, actual, {
663
+ const finalized = await this.finalizeContinuedRun(binding, threadId, runId, input, actual, {
664
664
  previousState: previousState === "queued" ? "running" : previousState,
665
665
  stateSequence: options.stateSequence ?? 103,
666
666
  approvalSequence: options.approvalSequence ?? 104,
@@ -691,25 +691,68 @@ export class AgentHarnessRuntime {
691
691
  checkpointRefForState(threadId, runId, state) {
692
692
  return state === "waiting_for_approval" ? `checkpoints/${threadId}/${runId}/cp-1` : null;
693
693
  }
694
- async finalizeContinuedRun(threadId, runId, input, actual, options) {
694
+ async finalizeContinuedRun(binding, threadId, runId, input, actual, options) {
695
695
  let approval;
696
- await this.appendAssistantMessage(threadId, runId, actual.output);
696
+ const finalizedActual = actual.state === "completed"
697
+ ? await this.synthesizeCompletedRun(binding, input, actual)
698
+ : actual;
699
+ await this.appendAssistantMessage(threadId, runId, finalizedActual.output);
697
700
  const checkpointRef = this.checkpointRefForState(threadId, runId, actual.state);
698
- await this.setRunStateAndEmit(threadId, runId, options.stateSequence, actual.state, {
701
+ await this.setRunStateAndEmit(threadId, runId, options.stateSequence, finalizedActual.state, {
699
702
  previousState: options.previousState,
700
703
  checkpointRef,
701
704
  });
702
- if (actual.state === "waiting_for_approval" && options.approvalSequence) {
703
- approval = (await this.requestApprovalAndEmit(threadId, runId, input, actual.interruptContent, checkpointRef, options.approvalSequence)).approval;
705
+ if (finalizedActual.state === "waiting_for_approval" && options.approvalSequence) {
706
+ approval = (await this.requestApprovalAndEmit(threadId, runId, input, finalizedActual.interruptContent, checkpointRef, options.approvalSequence)).approval;
707
+ }
708
+ if (finalizedActual.state === "completed") {
709
+ await this.reviewCompletedRun(binding, threadId, runId, input, finalizedActual);
704
710
  }
705
711
  return {
706
- ...actual,
712
+ ...finalizedActual,
707
713
  threadId,
708
714
  runId,
709
- approvalId: approval?.approvalId ?? actual.approvalId,
710
- pendingActionId: approval?.pendingActionId ?? actual.pendingActionId,
715
+ approvalId: approval?.approvalId ?? finalizedActual.approvalId,
716
+ pendingActionId: approval?.pendingActionId ?? finalizedActual.pendingActionId,
711
717
  };
712
718
  }
719
+ async synthesizeCompletedRun(binding, input, actual) {
720
+ try {
721
+ const synthesized = await this.runtimeAdapter.synthesizeFinalResult(binding, input, actual);
722
+ if (!synthesized) {
723
+ return actual;
724
+ }
725
+ return {
726
+ ...actual,
727
+ output: synthesized.output,
728
+ finalMessageText: synthesized.finalMessageText,
729
+ metadata: {
730
+ ...(typeof actual.metadata === "object" && actual.metadata ? actual.metadata : {}),
731
+ finalSynthesis: {
732
+ modelId: synthesized.modelId,
733
+ },
734
+ },
735
+ };
736
+ }
737
+ catch {
738
+ return actual;
739
+ }
740
+ }
741
+ async reviewCompletedRun(binding, threadId, runId, input, actual) {
742
+ try {
743
+ const review = await this.runtimeAdapter.reviewRunResult(binding, input, actual);
744
+ if (!review) {
745
+ return;
746
+ }
747
+ await this.emit(threadId, runId, 7, "run.reviewed", {
748
+ modelId: review.modelId,
749
+ assessment: review.assessment,
750
+ });
751
+ }
752
+ catch {
753
+ // Review is advisory; do not fail the completed run if the review pass fails.
754
+ }
755
+ }
713
756
  async emitOutputDeltaAndCreateItem(threadId, runId, agentId, content) {
714
757
  await this.emit(threadId, runId, 3, "output.delta", {
715
758
  content,
@@ -1139,6 +1182,7 @@ export class AgentHarnessRuntime {
1139
1182
  context: invocation.context,
1140
1183
  state: invocation.state,
1141
1184
  files: invocation.files,
1185
+ runId,
1142
1186
  })) {
1143
1187
  if (chunk) {
1144
1188
  streamActivityObserved = true;
@@ -1425,7 +1469,7 @@ export class AgentHarnessRuntime {
1425
1469
  return this.finalizeCancelledRun(threadId, runId, "resuming", cancelledAfterInvoke.reason);
1426
1470
  }
1427
1471
  await this.persistence.clearRecoveryIntent(threadId, runId);
1428
- const finalized = await this.finalizeContinuedRun(threadId, runId, runInput, actual, {
1472
+ const finalized = await this.finalizeContinuedRun(binding, threadId, runId, runInput, actual, {
1429
1473
  previousState: "resuming",
1430
1474
  stateSequence: 7,
1431
1475
  approvalSequence: 8,
@@ -1668,7 +1712,7 @@ export class AgentHarnessRuntime {
1668
1712
  const actual = await this.runtimeAdapter.invoke(binding, "", thread.threadId, thread.latestRunId, recoveryIntent.resumePayload, priorHistory);
1669
1713
  this.healthMonitor.recordLlmSuccess(Date.now() - startedAt);
1670
1714
  await this.persistence.clearRecoveryIntent(thread.threadId, thread.latestRunId);
1671
- await this.finalizeContinuedRun(thread.threadId, thread.latestRunId, runInput, actual, {
1715
+ await this.finalizeContinuedRun(binding, thread.threadId, thread.latestRunId, runInput, actual, {
1672
1716
  previousState: "resuming",
1673
1717
  stateSequence: 101,
1674
1718
  approvalSequence: 102,
@@ -20,7 +20,7 @@ export type InventorySkillRecord = {
20
20
  export type InventoryAgentRecord = {
21
21
  id: string;
22
22
  description: string;
23
- role: "runtime-entry" | "specialist";
23
+ role: "agent" | "specialist";
24
24
  tools: InventoryToolRecord[];
25
25
  skills: InventorySkillRecord[];
26
26
  };
@@ -96,7 +96,7 @@ export function listAvailableAgents(workspace, options = {}) {
96
96
  const topLevel = listHostBindings(workspace).map((binding) => ({
97
97
  id: binding.agent.id,
98
98
  description: binding.agent.description,
99
- role: "runtime-entry",
99
+ role: "agent",
100
100
  tools: listAgentTools(workspace, binding.agent.id),
101
101
  skills: listAgentSkills(workspace, binding.agent.id, options),
102
102
  }));
@@ -0,0 +1,25 @@
1
+ type LangGraphPresetNode = {
2
+ id: string;
3
+ kind: string;
4
+ prompt?: string;
5
+ role?: string;
6
+ agent?: string;
7
+ tool?: string;
8
+ };
9
+ type LangGraphPresetEdge = {
10
+ from: string;
11
+ to: string;
12
+ when?: string;
13
+ };
14
+ export type LangGraphPresetWorkflow = {
15
+ entryNode: string;
16
+ nodes: LangGraphPresetNode[];
17
+ edges: LangGraphPresetEdge[];
18
+ };
19
+ export type LangGraphPresetName = "react" | "prompt-chaining" | "routing" | "parallelization" | "plan-execute" | "review-loop" | "evaluator-optimizer" | "approval-gate" | "handoff" | "orchestrator-workers";
20
+ type LangGraphPresetOptions = {
21
+ agent?: string;
22
+ };
23
+ export declare const SUPPORTED_LANGGRAPH_PRESETS: LangGraphPresetName[];
24
+ export declare function resolveLangGraphPresetWorkflow(presetName: string | undefined, options?: LangGraphPresetOptions): LangGraphPresetWorkflow | undefined;
25
+ export {};
@@ -0,0 +1,165 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ import type { LangGraphPresetWorkflow } from "./langgraph-presets.js";
2
+ export type LangGraphProfileName = "coding-runtime" | "personal-assistant" | "research-runtime" | "approval-review-runtime" | "claw-style-assistant" | "chat-operator" | "copilot-sidecar" | "task-delegation-hub";
3
+ type LangGraphProfileOptions = Record<string, unknown>;
4
+ export declare const SUPPORTED_LANGGRAPH_PROFILES: LangGraphProfileName[];
5
+ export declare function resolveLangGraphProfileWorkflow(profileName: string | undefined, options?: LangGraphProfileOptions): LangGraphPresetWorkflow | undefined;
6
+ export {};
@@ -0,0 +1,206 @@
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_FINALIZER_PROMPT = "Rewrite the current result into a concise user-facing final answer. Preserve facts and caveats.";
4
+ const DEFAULT_REPLANNER_PROMPT = "Refine the plan based on the reviewer feedback and current result. Return only the updated plan.";
5
+ export const SUPPORTED_LANGGRAPH_PROFILES = [
6
+ "coding-runtime",
7
+ "personal-assistant",
8
+ "research-runtime",
9
+ "approval-review-runtime",
10
+ "claw-style-assistant",
11
+ "chat-operator",
12
+ "copilot-sidecar",
13
+ "task-delegation-hub",
14
+ ];
15
+ function readStringOption(options, key) {
16
+ const value = options[key];
17
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
18
+ }
19
+ function requireAgentOption(profile, options, key) {
20
+ const value = readStringOption(options, key);
21
+ if (value) {
22
+ return value;
23
+ }
24
+ throw new Error(`LangGraph profile ${profile} requires with.${key}`);
25
+ }
26
+ function buildCodingRuntimeWorkflow(options) {
27
+ const coderAgent = requireAgentOption("coding-runtime", options, "coderAgent");
28
+ const verifierAgent = readStringOption(options, "verifierAgent");
29
+ const needsVerification = Boolean(verifierAgent) || options.runIntegrationVerification === true;
30
+ return {
31
+ entryNode: "planner",
32
+ nodes: [
33
+ { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
34
+ { id: "coder", kind: "agent", agent: coderAgent },
35
+ { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
36
+ { id: "replanner", kind: "llm", role: "replanner", prompt: DEFAULT_REPLANNER_PROMPT },
37
+ ...(needsVerification
38
+ ? [{ id: "approval", kind: "approval" }, { id: "verifier", kind: "agent", agent: verifierAgent ?? coderAgent }]
39
+ : []),
40
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
41
+ ],
42
+ edges: [
43
+ { from: "planner", to: "coder" },
44
+ { from: "coder", to: "reviewer", when: "has_result" },
45
+ { from: "reviewer", to: needsVerification ? "approval" : "finalizer", when: "review_ok" },
46
+ { from: "reviewer", to: "replanner", when: "review_incomplete" },
47
+ { from: "replanner", to: "coder", when: "has_plan" },
48
+ ...(needsVerification
49
+ ? [
50
+ { from: "approval", to: "verifier", when: "approval_approved" },
51
+ { from: "approval", to: "verifier", when: "approval_edited" },
52
+ { from: "verifier", to: "finalizer", when: "has_result" },
53
+ ]
54
+ : []),
55
+ ],
56
+ };
57
+ }
58
+ function buildAssistantWorkflow(options) {
59
+ const worker = readStringOption(options, "defaultWorkerAgent") ??
60
+ readStringOption(options, "defaultResearchAgent") ??
61
+ readStringOption(options, "defaultWritingAgent") ??
62
+ readStringOption(options, "defaultSchedulingAgent");
63
+ return {
64
+ entryNode: "intake",
65
+ nodes: [
66
+ { id: "intake", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
67
+ ...(worker ? [{ id: "worker", kind: "agent", agent: worker }] : [{ id: "lookup", kind: "tool", tool: "repo_inventory" }]),
68
+ { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
69
+ { id: "approval", kind: "approval" },
70
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
71
+ ],
72
+ edges: [
73
+ { from: "intake", to: worker ? "worker" : "lookup" },
74
+ { from: worker ? "worker" : "lookup", to: "reviewer", when: "has_result" },
75
+ { from: "reviewer", to: "approval", when: "review_incomplete" },
76
+ { from: "reviewer", to: "finalizer", when: "review_ok" },
77
+ { from: "approval", to: "finalizer", when: "approval_approved" },
78
+ { from: "approval", to: "finalizer", when: "approval_edited" },
79
+ ],
80
+ };
81
+ }
82
+ function buildResearchWorkflow(options) {
83
+ const gathererAgent = requireAgentOption("research-runtime", options, "gathererAgent");
84
+ const analystAgent = requireAgentOption("research-runtime", options, "analystAgent");
85
+ const synthesizerAgent = readStringOption(options, "synthesizerAgent");
86
+ return {
87
+ entryNode: "planner",
88
+ nodes: [
89
+ { id: "planner", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
90
+ { id: "gatherer", kind: "agent", agent: gathererAgent },
91
+ { id: "coverage-review", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
92
+ { id: "analyst", kind: "agent", agent: analystAgent },
93
+ ...(synthesizerAgent ? [{ id: "synthesizer", kind: "agent", agent: synthesizerAgent }] : []),
94
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
95
+ ],
96
+ edges: [
97
+ { from: "planner", to: "gatherer" },
98
+ { from: "gatherer", to: "coverage-review", when: "has_result" },
99
+ { from: "coverage-review", to: "analyst", when: "review_ok" },
100
+ { from: "coverage-review", to: "gatherer", when: "review_incomplete" },
101
+ { from: "analyst", to: synthesizerAgent ? "synthesizer" : "finalizer", when: "has_result" },
102
+ ...(synthesizerAgent ? [{ from: "synthesizer", to: "finalizer", when: "has_result" }] : []),
103
+ ],
104
+ };
105
+ }
106
+ function buildApprovalReviewWorkflow(options) {
107
+ const preparerAgent = requireAgentOption("approval-review-runtime", options, "preparerAgent");
108
+ const reviewerAgent = readStringOption(options, "reviewerAgent");
109
+ return {
110
+ entryNode: "scope",
111
+ nodes: [
112
+ { id: "scope", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
113
+ { id: "preparer", kind: "agent", agent: preparerAgent },
114
+ ...(reviewerAgent ? [{ id: "reviewer-agent", kind: "agent", agent: reviewerAgent }] : [{ id: "risk-review", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT }]),
115
+ { id: "approval", kind: "approval" },
116
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
117
+ ],
118
+ edges: [
119
+ { from: "scope", to: "preparer" },
120
+ { from: "preparer", to: reviewerAgent ? "reviewer-agent" : "risk-review", when: "has_result" },
121
+ { from: reviewerAgent ? "reviewer-agent" : "risk-review", to: "approval", when: "has_result" },
122
+ { from: "approval", to: "finalizer", when: "approval_approved" },
123
+ { from: "approval", to: "finalizer", when: "approval_edited" },
124
+ ],
125
+ };
126
+ }
127
+ function buildChatOperatorWorkflow(options) {
128
+ const routedAgent = readStringOption(options, "assistantAgent") ??
129
+ readStringOption(options, "researchAgent") ??
130
+ readStringOption(options, "codingAgent") ??
131
+ readStringOption(options, "approvalAgent");
132
+ if (!routedAgent) {
133
+ throw new Error("LangGraph profile chat-operator requires at least one target agent in with.assistantAgent, with.researchAgent, with.codingAgent, or with.approvalAgent");
134
+ }
135
+ return {
136
+ entryNode: "router",
137
+ nodes: [
138
+ { id: "router", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
139
+ { id: "worker", kind: "agent", agent: routedAgent },
140
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
141
+ ],
142
+ edges: [
143
+ { from: "router", to: "worker" },
144
+ { from: "worker", to: "finalizer", when: "has_result" },
145
+ ],
146
+ };
147
+ }
148
+ function buildCopilotSidecarWorkflow(options) {
149
+ const coderAgent = requireAgentOption("copilot-sidecar", options, "coderAgent");
150
+ return {
151
+ entryNode: "intake",
152
+ nodes: [
153
+ { id: "intake", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
154
+ { id: "coder", kind: "agent", agent: coderAgent },
155
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
156
+ ],
157
+ edges: [
158
+ { from: "intake", to: "coder" },
159
+ { from: "coder", to: "finalizer", when: "has_result" },
160
+ ],
161
+ };
162
+ }
163
+ function buildDelegationHubWorkflow(options) {
164
+ const workerAgent = readStringOption(options, "defaultWorkerFallback");
165
+ if (!workerAgent) {
166
+ throw new Error("LangGraph profile task-delegation-hub requires with.defaultWorkerFallback");
167
+ }
168
+ return {
169
+ entryNode: "router",
170
+ nodes: [
171
+ { id: "router", kind: "llm", role: "planner", prompt: DEFAULT_PLANNER_PROMPT },
172
+ { id: "worker", kind: "agent", agent: workerAgent },
173
+ { id: "reviewer", kind: "llm", role: "reviewer", prompt: DEFAULT_REVIEWER_PROMPT },
174
+ { id: "finalizer", kind: "llm", role: "finalizer", prompt: DEFAULT_FINALIZER_PROMPT },
175
+ ],
176
+ edges: [
177
+ { from: "router", to: "worker" },
178
+ { from: "worker", to: "reviewer", when: "has_result" },
179
+ { from: "reviewer", to: "finalizer", when: "review_ok" },
180
+ ],
181
+ };
182
+ }
183
+ export function resolveLangGraphProfileWorkflow(profileName, options = {}) {
184
+ switch (profileName) {
185
+ case undefined:
186
+ case "":
187
+ return undefined;
188
+ case "coding-runtime":
189
+ return buildCodingRuntimeWorkflow(options);
190
+ case "personal-assistant":
191
+ case "claw-style-assistant":
192
+ return buildAssistantWorkflow(options);
193
+ case "research-runtime":
194
+ return buildResearchWorkflow(options);
195
+ case "approval-review-runtime":
196
+ return buildApprovalReviewWorkflow(options);
197
+ case "chat-operator":
198
+ return buildChatOperatorWorkflow(options);
199
+ case "copilot-sidecar":
200
+ return buildCopilotSidecarWorkflow(options);
201
+ case "task-delegation-hub":
202
+ return buildDelegationHubWorkflow(options);
203
+ default:
204
+ throw new Error(`Unsupported LangGraph profile ${String(profileName)}. Supported profiles: ${SUPPORTED_LANGGRAPH_PROFILES.join(", ")}`);
205
+ }
206
+ }
@@ -1,13 +1,8 @@
1
1
  import { getPolicyEvaluators } from "../extensions.js";
2
- import { isRuntimeEntryBinding } from "./support/runtime-entry.js";
3
2
  export class PolicyEngine {
4
3
  evaluate(binding) {
5
4
  const reasons = [];
6
5
  let allowed = true;
7
- if (!isRuntimeEntryBinding(binding)) {
8
- allowed = false;
9
- reasons.push("internal subagents cannot be used as runtime entry agents");
10
- }
11
6
  for (const evaluator of getPolicyEvaluators()) {
12
7
  const decision = evaluator.evaluate(binding);
13
8
  if (!decision) {
@@ -1,12 +1,15 @@
1
- import type { CompiledAgentBinding, CompiledModel, CompiledTool, DeepAgentParams, LangChainAgentParams } from "../../contracts/types.js";
1
+ import type { CompiledAgentBinding, CompiledModel, CompiledTool, DeepAgentParams, LangChainAgentParams, RuntimeModelSlot } from "../../contracts/types.js";
2
2
  export declare function getBindingAdapterKind(binding: CompiledAgentBinding): string;
3
3
  export declare function getBindingAdapterConfig(binding: CompiledAgentBinding): Record<string, unknown>;
4
+ export declare function getBindingLangGraphWorkflow(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
5
+ export declare function getBindingLangGraphPreset(binding: CompiledAgentBinding): string | undefined;
4
6
  export declare function getBindingLangChainParams(binding: CompiledAgentBinding): LangChainAgentParams | undefined;
5
7
  export declare function getBindingDeepAgentParams(binding: CompiledAgentBinding): DeepAgentParams | undefined;
6
8
  export declare function isLangChainBinding(binding: CompiledAgentBinding): boolean;
7
9
  export declare function isDeepAgentBinding(binding: CompiledAgentBinding): boolean;
8
10
  export declare function getBindingPrimaryTools(binding: CompiledAgentBinding): CompiledTool[];
9
11
  export declare function getBindingPrimaryModel(binding: CompiledAgentBinding): CompiledModel | undefined;
12
+ export declare function getBindingRuntimeModel(binding: CompiledAgentBinding, slot: RuntimeModelSlot): CompiledModel | undefined;
10
13
  export declare function getBindingSystemPrompt(binding: CompiledAgentBinding): string | undefined;
11
14
  export declare function getBindingMiddlewareConfigs(binding: CompiledAgentBinding): Array<Record<string, unknown>> | undefined;
12
15
  export declare function getBindingInterruptCompatibilityRules(binding: CompiledAgentBinding): Record<string, boolean | object> | undefined;
@@ -9,9 +9,26 @@ export function getBindingAdapterKind(binding) {
9
9
  export function getBindingAdapterConfig(binding) {
10
10
  return binding.adapter?.config ?? {};
11
11
  }
12
+ export function getBindingLangGraphWorkflow(binding) {
13
+ const workflow = asRecord(getBindingAdapterConfig(binding).workflow);
14
+ if (workflow) {
15
+ return workflow;
16
+ }
17
+ return asRecord(asRecord(getBindingLangChainParams(binding)?.passthrough)?.workflow);
18
+ }
19
+ export function getBindingLangGraphPreset(binding) {
20
+ const adapterPreset = getBindingAdapterConfig(binding).preset;
21
+ if (typeof adapterPreset === "string" && adapterPreset.trim()) {
22
+ return adapterPreset.trim();
23
+ }
24
+ const passthroughPreset = asRecord(getBindingLangChainParams(binding)?.passthrough)?.preset;
25
+ return typeof passthroughPreset === "string" && passthroughPreset.trim()
26
+ ? passthroughPreset.trim()
27
+ : undefined;
28
+ }
12
29
  export function getBindingLangChainParams(binding) {
13
30
  const adapterParams = asRecord(getBindingAdapterConfig(binding).params);
14
- if (getBindingAdapterKind(binding) === "langchain-v1" && adapterParams) {
31
+ if ((getBindingAdapterKind(binding) === "langchain-v1" || getBindingAdapterKind(binding) === "langgraph") && adapterParams) {
15
32
  return adapterParams;
16
33
  }
17
34
  return binding.langchainAgentParams;
@@ -24,7 +41,9 @@ export function getBindingDeepAgentParams(binding) {
24
41
  return binding.deepAgentParams;
25
42
  }
26
43
  export function isLangChainBinding(binding) {
27
- return getBindingAdapterKind(binding) === "langchain-v1" || Boolean(binding.langchainAgentParams);
44
+ return getBindingAdapterKind(binding) === "langchain-v1" ||
45
+ getBindingAdapterKind(binding) === "langgraph" ||
46
+ Boolean(binding.langchainAgentParams);
28
47
  }
29
48
  export function isDeepAgentBinding(binding) {
30
49
  return getBindingAdapterKind(binding) === "deepagent" || Boolean(binding.deepAgentParams);
@@ -35,6 +54,9 @@ export function getBindingPrimaryTools(binding) {
35
54
  export function getBindingPrimaryModel(binding) {
36
55
  return binding.langchainAgentParams?.model ?? binding.deepAgentParams?.model;
37
56
  }
57
+ export function getBindingRuntimeModel(binding, slot) {
58
+ return binding.harnessRuntime.models?.[slot];
59
+ }
38
60
  export function getBindingSystemPrompt(binding) {
39
61
  return binding.langchainAgentParams?.systemPrompt ?? binding.deepAgentParams?.systemPrompt;
40
62
  }
@@ -131,8 +131,8 @@ export function createPendingApproval(threadId, runId, checkpointRef, input, int
131
131
  }
132
132
  export function inferRoutingBindings(workspace) {
133
133
  const runtimeEntryBindings = Array.from(workspace.bindings.values()).filter((binding) => isRuntimeEntryBinding(binding));
134
- const deepAgentHosts = runtimeEntryBindings.filter((binding) => binding.agent.executionMode === "deepagent" || Boolean(binding.deepAgentParams));
135
- const routingHosts = deepAgentHosts.length > 0 ? deepAgentHosts : runtimeEntryBindings;
134
+ const orchestrationHosts = runtimeEntryBindings.filter((binding) => binding.agent.executionMode === "deepagent" || binding.agent.executionMode === "langgraph" || Boolean(binding.deepAgentParams));
135
+ const routingHosts = orchestrationHosts.length > 0 ? orchestrationHosts : runtimeEntryBindings;
136
136
  const researchBinding = routingHosts.find((binding) => binding.agent.id === "research-lite" || binding.agent.id === "research");
137
137
  const directBinding = routingHosts.find((binding) => binding.agent.id === "direct");
138
138
  const delegationHosts = routingHosts.filter((binding) => isDelegationCapableBinding(binding));
@@ -151,6 +151,6 @@ export function inferRoutingBindings(workspace) {
151
151
  : delegationPreferredSecondary && delegationPreferredSecondary.agent.id !== primaryBinding?.agent.id
152
152
  ? delegationPreferredSecondary
153
153
  : routingHosts.find((binding) => binding.agent.id !== primaryBinding?.agent.id) ??
154
- (deepAgentHosts.length > 0 ? undefined : runtimeEntryBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id));
154
+ (orchestrationHosts.length > 0 ? undefined : runtimeEntryBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id));
155
155
  return { primaryBinding, secondaryBinding, researchBinding, hostBindings: runtimeEntryBindings };
156
156
  }