@botbotgo/agent-harness 0.0.326 → 0.0.328
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/chat-stream.js +33 -27
- package/dist/cli/main.js +30 -3
- package/dist/contracts/runtime-requests.d.ts +1 -2
- package/dist/contracts/runtime-scheduling.d.ts +1 -1
- package/dist/flow/flow-graph-upstream.js +3 -7
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/projections/request-events.js +0 -1
- package/dist/resource/isolation.js +51 -10
- package/dist/resources/toolkit.mjs +183 -0
- package/dist/resources/tools/cancel_request.mjs +1 -1
- package/dist/resources/tools/fetch_url.mjs +1 -1
- package/dist/resources/tools/http_request.mjs +1 -1
- package/dist/resources/tools/inspect_approvals.mjs +1 -1
- package/dist/resources/tools/inspect_artifacts.mjs +1 -1
- package/dist/resources/tools/inspect_events.mjs +1 -1
- package/dist/resources/tools/inspect_requests.mjs +1 -1
- package/dist/resources/tools/inspect_sessions.mjs +1 -1
- package/dist/resources/tools/list_files.mjs +1 -1
- package/dist/resources/tools/read_artifact.mjs +1 -1
- package/dist/resources/tools/request_approval.mjs +1 -1
- package/dist/resources/tools/run_command.mjs +1 -1
- package/dist/resources/tools/schedule_task.mjs +1 -1
- package/dist/resources/tools/search_files.mjs +1 -1
- package/dist/resources/tools/send_message.mjs +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +0 -9
- package/dist/runtime/adapter/compat/deepagent-compat.js +0 -22
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +4 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +239 -8
- package/dist/runtime/adapter/local-tool-invocation.js +53 -0
- package/dist/runtime/adapter/middleware-assembly.js +174 -29
- package/dist/runtime/adapter/runtime-adapter-support.js +1 -2
- package/dist/runtime/adapter/stream-event-projection.d.ts +17 -0
- package/dist/runtime/adapter/stream-event-projection.js +217 -4
- package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +0 -3
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +37 -17
- package/dist/runtime/adapter/tool/resolved-tool.js +29 -3
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -3
- package/dist/runtime/agent-runtime-adapter.js +12 -33
- package/dist/runtime/agent-runtime-assembly.d.ts +3 -21
- package/dist/runtime/agent-runtime-assembly.js +4 -56
- package/dist/runtime/harness/run/inspection.js +21 -5
- package/dist/runtime/harness/run/run-operations.js +2 -1
- package/dist/runtime/harness/run/stream-run.d.ts +3 -1
- package/dist/runtime/harness/run/stream-run.js +206 -30
- package/dist/runtime/harness.js +3 -0
- package/dist/runtime/parsing/output-content.js +11 -4
- package/dist/runtime/parsing/output-recovery.d.ts +3 -0
- package/dist/runtime/parsing/output-recovery.js +57 -11
- package/dist/runtime/parsing/output-tool-args.d.ts +4 -0
- package/dist/runtime/parsing/output-tool-args.js +122 -0
- package/dist/runtime/parsing/stream-event-parsing.js +37 -3
- package/dist/runtime/support/harness-support.d.ts +1 -0
- package/dist/runtime/support/harness-support.js +44 -2
- package/dist/tools.js +34 -4
- package/package.json +8 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError } from "../../agent-runtime-adapter.js";
|
|
2
|
+
import { ExecutionReconciliationError } from "../../adapter/flow/stream-runtime.js";
|
|
2
3
|
import { buildRequestPlanState, summarizeBuiltinWriteTodosArgs } from "../../adapter/runtime-adapter-support.js";
|
|
3
|
-
import { renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
|
|
4
|
+
import { describeRuntimeError, renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
|
|
4
5
|
import { getBindingPrimaryModel } from "../../support/compiled-binding.js";
|
|
5
6
|
import { createContentBlocksItem, createToolResultKey, } from "../events/streaming.js";
|
|
6
7
|
import { projectRuntimeSurfaceFromSingleUpstreamEvent } from "./inspection.js";
|
|
@@ -19,10 +20,64 @@ function createInitialPlanState(sessionId, requestId, updatedAt) {
|
|
|
19
20
|
completed: 0,
|
|
20
21
|
failed: 0,
|
|
21
22
|
cancelled: 0,
|
|
22
|
-
blocked: 0,
|
|
23
23
|
},
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
function planStateHasUnfinishedItems(planState) {
|
|
27
|
+
if (!planState) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return planState.summary.pending > 0 || planState.summary.inProgress > 0;
|
|
31
|
+
}
|
|
32
|
+
function planStateHasActiveItems(planState) {
|
|
33
|
+
if (!planState) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return planState.summary.pending > 0 || planState.summary.inProgress > 0;
|
|
37
|
+
}
|
|
38
|
+
function readTerminalStructuredStatus(value) {
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
try {
|
|
41
|
+
return readTerminalStructuredStatus(JSON.parse(value));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "object" || value === null) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const typed = value;
|
|
51
|
+
if (typed.status === "completed") {
|
|
52
|
+
return typed.status;
|
|
53
|
+
}
|
|
54
|
+
return (readTerminalStructuredStatus(typed.structuredResponse)
|
|
55
|
+
?? readTerminalStructuredStatus(typed.output)
|
|
56
|
+
?? readTerminalStructuredStatus(typed.data));
|
|
57
|
+
}
|
|
58
|
+
function reconcilePlanStateToTerminalStatus(planState, status, updatedAt) {
|
|
59
|
+
const items = planState.items.map((item) => ({
|
|
60
|
+
...item,
|
|
61
|
+
status: item.status === "pending" || item.status === "in_progress"
|
|
62
|
+
? status
|
|
63
|
+
: item.status,
|
|
64
|
+
}));
|
|
65
|
+
const summary = {
|
|
66
|
+
total: items.length,
|
|
67
|
+
pending: 0,
|
|
68
|
+
inProgress: 0,
|
|
69
|
+
completed: items.filter((item) => item.status === "completed").length,
|
|
70
|
+
failed: items.filter((item) => item.status === "failed").length,
|
|
71
|
+
cancelled: items.filter((item) => item.status === "cancelled").length,
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
...planState,
|
|
75
|
+
version: planState.version + 1,
|
|
76
|
+
updatedAt,
|
|
77
|
+
items,
|
|
78
|
+
summary,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
26
81
|
function getPlanStateFromToolResult(input) {
|
|
27
82
|
if (typeof input.output !== "object" || input.output === null) {
|
|
28
83
|
return null;
|
|
@@ -80,6 +135,26 @@ function buildPlanStateSignature(planState) {
|
|
|
80
135
|
function countStructuredTodoIds(items) {
|
|
81
136
|
return items.filter((item) => typeof item.id === "string" && item.id.length > 0).length;
|
|
82
137
|
}
|
|
138
|
+
async function emitPlanStateUpdate(options, agentId, planState) {
|
|
139
|
+
await options.saveRequestPlanState?.(options.sessionId, options.requestId, planState);
|
|
140
|
+
return [{
|
|
141
|
+
type: "plan-state",
|
|
142
|
+
sessionId: options.sessionId,
|
|
143
|
+
requestId: options.requestId,
|
|
144
|
+
agentId,
|
|
145
|
+
planState,
|
|
146
|
+
}];
|
|
147
|
+
}
|
|
148
|
+
async function refreshPlanStateFromPersistence(options, currentPlanState) {
|
|
149
|
+
const persistedPlanState = await options.loadRequestPlanState?.(options.sessionId, options.requestId);
|
|
150
|
+
if (!persistedPlanState) {
|
|
151
|
+
return currentPlanState;
|
|
152
|
+
}
|
|
153
|
+
if (!currentPlanState || persistedPlanState.version >= currentPlanState.version) {
|
|
154
|
+
return persistedPlanState;
|
|
155
|
+
}
|
|
156
|
+
return currentPlanState;
|
|
157
|
+
}
|
|
83
158
|
function shouldEmitPlanState(currentPlanState, nextPlanState) {
|
|
84
159
|
if (!currentPlanState || currentPlanState.items.length === 0) {
|
|
85
160
|
return true;
|
|
@@ -210,7 +285,6 @@ function summarizePlanState(planState) {
|
|
|
210
285
|
planState.summary.inProgress > 0 ? `${planState.summary.inProgress} in progress` : "",
|
|
211
286
|
planState.summary.pending > 0 ? `${planState.summary.pending} pending` : "",
|
|
212
287
|
planState.summary.completed > 0 ? `${planState.summary.completed} completed` : "",
|
|
213
|
-
planState.summary.blocked > 0 ? `${planState.summary.blocked} blocked` : "",
|
|
214
288
|
planState.summary.failed > 0 ? `${planState.summary.failed} failed` : "",
|
|
215
289
|
].filter((value) => value.length > 0);
|
|
216
290
|
if (counts.length === 0) {
|
|
@@ -228,6 +302,9 @@ function createSurfaceCommentary(surfaceItem) {
|
|
|
228
302
|
return `Running tool ${name}.`;
|
|
229
303
|
}
|
|
230
304
|
if (surfaceItem.status === "completed") {
|
|
305
|
+
if (name.toLowerCase() === "task") {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
231
308
|
return `Tool ${name} completed.`;
|
|
232
309
|
}
|
|
233
310
|
if (surfaceItem.status === "failed") {
|
|
@@ -252,7 +329,7 @@ function createSurfaceCommentary(surfaceItem) {
|
|
|
252
329
|
return `Delegating work to ${name}.`;
|
|
253
330
|
}
|
|
254
331
|
if (surfaceItem.status === "completed") {
|
|
255
|
-
return
|
|
332
|
+
return null;
|
|
256
333
|
}
|
|
257
334
|
if (surfaceItem.status === "failed") {
|
|
258
335
|
return `Delegation to ${name} failed.`;
|
|
@@ -360,6 +437,7 @@ export async function* streamHarnessRun(options) {
|
|
|
360
437
|
let streamActivityObserved = false;
|
|
361
438
|
let nonUpstreamStreamActivityObserved = false;
|
|
362
439
|
let assistantOutput = "";
|
|
440
|
+
let assistantOutputCameFromInvokeFallback = false;
|
|
363
441
|
const bufferAssistantTextUntilCompletion = true;
|
|
364
442
|
let currentAgentId = options.selectedAgentId;
|
|
365
443
|
let currentAgentName = formatAgentName(options.selectedAgentId);
|
|
@@ -425,19 +503,32 @@ export async function* streamHarnessRun(options) {
|
|
|
425
503
|
planStateVersion = upstreamPlanState.version;
|
|
426
504
|
lastPlanStateSignature = signature;
|
|
427
505
|
currentPlanState = upstreamPlanState;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
requestId: options.requestId,
|
|
432
|
-
agentId: currentAgentId,
|
|
433
|
-
planState: upstreamPlanState,
|
|
434
|
-
};
|
|
506
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, upstreamPlanState)) {
|
|
507
|
+
yield item;
|
|
508
|
+
}
|
|
435
509
|
const commentary = summarizePlanState(upstreamPlanState);
|
|
436
510
|
if (commentary) {
|
|
437
511
|
yield* emitCommentary(commentary);
|
|
438
512
|
}
|
|
439
513
|
}
|
|
440
514
|
}
|
|
515
|
+
const terminalStructuredStatus = readTerminalStructuredStatus(normalizedChunk.event);
|
|
516
|
+
if (terminalStructuredStatus && currentPlanState && planStateHasActiveItems(currentPlanState)) {
|
|
517
|
+
const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, terminalStructuredStatus, new Date().toISOString());
|
|
518
|
+
const signature = buildPlanStateSignature(reconciledPlanState);
|
|
519
|
+
if (signature !== lastPlanStateSignature) {
|
|
520
|
+
planStateVersion = reconciledPlanState.version;
|
|
521
|
+
lastPlanStateSignature = signature;
|
|
522
|
+
currentPlanState = reconciledPlanState;
|
|
523
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
|
|
524
|
+
yield item;
|
|
525
|
+
}
|
|
526
|
+
const commentary = summarizePlanState(reconciledPlanState);
|
|
527
|
+
if (commentary) {
|
|
528
|
+
yield* emitCommentary(commentary);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
441
532
|
upstreamEventOrdinal += 1;
|
|
442
533
|
const projectionBinding = options.getBinding(currentAgentId) ?? options.binding;
|
|
443
534
|
const surfaceProjection = projectRuntimeSurfaceFromSingleUpstreamEvent({
|
|
@@ -553,7 +644,7 @@ export async function* streamHarnessRun(options) {
|
|
|
553
644
|
type: "tool-result",
|
|
554
645
|
sessionId: options.sessionId,
|
|
555
646
|
requestId: options.requestId,
|
|
556
|
-
agentId:
|
|
647
|
+
agentId: currentAgentId,
|
|
557
648
|
toolName: normalizedChunk.toolName,
|
|
558
649
|
output: normalizedChunk.output,
|
|
559
650
|
isError: normalizedChunk.isError,
|
|
@@ -578,19 +669,32 @@ export async function* streamHarnessRun(options) {
|
|
|
578
669
|
if (signature !== lastPlanStateSignature && shouldEmitPlanState(currentPlanState, planState)) {
|
|
579
670
|
lastPlanStateSignature = signature;
|
|
580
671
|
currentPlanState = planState;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
requestId: options.requestId,
|
|
585
|
-
agentId: currentAgentId,
|
|
586
|
-
planState,
|
|
587
|
-
};
|
|
672
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, planState)) {
|
|
673
|
+
yield item;
|
|
674
|
+
}
|
|
588
675
|
const commentary = summarizePlanState(planState);
|
|
589
676
|
if (commentary) {
|
|
590
677
|
yield* emitCommentary(commentary);
|
|
591
678
|
}
|
|
592
679
|
}
|
|
593
680
|
}
|
|
681
|
+
const terminalStructuredStatus = readTerminalStructuredStatus(normalizedChunk.output);
|
|
682
|
+
if (terminalStructuredStatus && currentPlanState && planStateHasActiveItems(currentPlanState)) {
|
|
683
|
+
const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, terminalStructuredStatus, new Date().toISOString());
|
|
684
|
+
const signature = buildPlanStateSignature(reconciledPlanState);
|
|
685
|
+
if (signature !== lastPlanStateSignature) {
|
|
686
|
+
planStateVersion = reconciledPlanState.version;
|
|
687
|
+
lastPlanStateSignature = signature;
|
|
688
|
+
currentPlanState = reconciledPlanState;
|
|
689
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
|
|
690
|
+
yield item;
|
|
691
|
+
}
|
|
692
|
+
const commentary = summarizePlanState(reconciledPlanState);
|
|
693
|
+
if (commentary) {
|
|
694
|
+
yield* emitCommentary(commentary);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
594
698
|
continue;
|
|
595
699
|
}
|
|
596
700
|
emitted = true;
|
|
@@ -614,6 +718,7 @@ export async function* streamHarnessRun(options) {
|
|
|
614
718
|
assistantOutput = toolErrors.join("\n\n");
|
|
615
719
|
emitted = true;
|
|
616
720
|
}
|
|
721
|
+
currentPlanState = await refreshPlanStateFromPersistence(options, currentPlanState);
|
|
617
722
|
if (!assistantOutput) {
|
|
618
723
|
const actual = await options.invokeWithHistory(options.binding, options.input, options.sessionId, options.requestId);
|
|
619
724
|
if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
|
|
@@ -622,6 +727,7 @@ export async function* streamHarnessRun(options) {
|
|
|
622
727
|
if (actual.output) {
|
|
623
728
|
assistantOutput = actual.output;
|
|
624
729
|
emitted = true;
|
|
730
|
+
assistantOutputCameFromInvokeFallback = true;
|
|
625
731
|
}
|
|
626
732
|
const finalPlanState = getLatestPlanStateFromExecutedToolResults({
|
|
627
733
|
sessionId: options.sessionId,
|
|
@@ -636,19 +742,42 @@ export async function* streamHarnessRun(options) {
|
|
|
636
742
|
planStateVersion = finalPlanState.version;
|
|
637
743
|
lastPlanStateSignature = signature;
|
|
638
744
|
currentPlanState = finalPlanState;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
requestId: options.requestId,
|
|
643
|
-
agentId: currentAgentId,
|
|
644
|
-
planState: finalPlanState,
|
|
645
|
-
};
|
|
745
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, finalPlanState)) {
|
|
746
|
+
yield item;
|
|
747
|
+
}
|
|
646
748
|
const commentary = summarizePlanState(finalPlanState);
|
|
647
749
|
if (commentary) {
|
|
648
750
|
yield* emitCommentary(commentary);
|
|
649
751
|
}
|
|
650
752
|
}
|
|
651
753
|
}
|
|
754
|
+
currentPlanState = await refreshPlanStateFromPersistence(options, currentPlanState);
|
|
755
|
+
const terminalStructuredStatus = readTerminalStructuredStatus(actual.structuredResponse);
|
|
756
|
+
if (terminalStructuredStatus && currentPlanState && planStateHasActiveItems(currentPlanState)) {
|
|
757
|
+
const reconciledPlanState = reconcilePlanStateToTerminalStatus(currentPlanState, terminalStructuredStatus, new Date().toISOString());
|
|
758
|
+
const signature = buildPlanStateSignature(reconciledPlanState);
|
|
759
|
+
if (signature !== lastPlanStateSignature) {
|
|
760
|
+
planStateVersion = reconciledPlanState.version;
|
|
761
|
+
lastPlanStateSignature = signature;
|
|
762
|
+
currentPlanState = reconciledPlanState;
|
|
763
|
+
for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
|
|
764
|
+
yield item;
|
|
765
|
+
}
|
|
766
|
+
const commentary = summarizePlanState(reconciledPlanState);
|
|
767
|
+
if (commentary) {
|
|
768
|
+
yield* emitCommentary(commentary);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
currentPlanState = await refreshPlanStateFromPersistence(options, currentPlanState);
|
|
774
|
+
if (assistantOutputCameFromInvokeFallback
|
|
775
|
+
&& nonUpstreamStreamActivityObserved
|
|
776
|
+
&& planStateHasActiveItems(currentPlanState)) {
|
|
777
|
+
throw new ExecutionReconciliationError("Agent ended while the streamed plan state still had unfinished work.");
|
|
778
|
+
}
|
|
779
|
+
if (planStateHasActiveItems(currentPlanState)) {
|
|
780
|
+
throw new ExecutionReconciliationError("Agent ended while the streamed plan state still had unfinished work.");
|
|
652
781
|
}
|
|
653
782
|
if (assistantOutput && bufferAssistantTextUntilCompletion) {
|
|
654
783
|
yield {
|
|
@@ -686,13 +815,15 @@ export async function* streamHarnessRun(options) {
|
|
|
686
815
|
catch (error) {
|
|
687
816
|
const shouldRetryAfterStreamingCompatibilityError = !assistantOutput &&
|
|
688
817
|
isOpenAICompatibleStreamingCompatibilityError(options.binding, error);
|
|
689
|
-
if ((emitted || streamActivityObserved)
|
|
818
|
+
if ((emitted || streamActivityObserved)
|
|
819
|
+
&& !shouldRetryAfterStreamingCompatibilityError) {
|
|
690
820
|
const runtimeFailure = renderRuntimeFailure(error);
|
|
821
|
+
const detailedError = describeRuntimeError(error);
|
|
691
822
|
yield {
|
|
692
823
|
type: "event",
|
|
693
824
|
event: await options.setRequestStateAndEmit(options.sessionId, options.requestId, 6, "failed", {
|
|
694
825
|
previousState: "running",
|
|
695
|
-
error:
|
|
826
|
+
error: detailedError,
|
|
696
827
|
}),
|
|
697
828
|
};
|
|
698
829
|
yield {
|
|
@@ -717,11 +848,12 @@ export async function* streamHarnessRun(options) {
|
|
|
717
848
|
}
|
|
718
849
|
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
719
850
|
const runtimeFailure = renderRuntimeFailure(error);
|
|
851
|
+
const detailedError = describeRuntimeError(error);
|
|
720
852
|
yield {
|
|
721
853
|
type: "event",
|
|
722
854
|
event: await options.setRequestStateAndEmit(options.sessionId, options.requestId, 6, "failed", {
|
|
723
855
|
previousState: "running",
|
|
724
|
-
error:
|
|
856
|
+
error: detailedError,
|
|
725
857
|
}),
|
|
726
858
|
};
|
|
727
859
|
yield {
|
|
@@ -744,6 +876,36 @@ export async function* streamHarnessRun(options) {
|
|
|
744
876
|
};
|
|
745
877
|
return;
|
|
746
878
|
}
|
|
879
|
+
if (error instanceof ExecutionReconciliationError) {
|
|
880
|
+
const runtimeFailure = renderRuntimeFailure(error);
|
|
881
|
+
const detailedError = describeRuntimeError(error);
|
|
882
|
+
yield {
|
|
883
|
+
type: "event",
|
|
884
|
+
event: await options.setRequestStateAndEmit(options.sessionId, options.requestId, 6, "failed", {
|
|
885
|
+
previousState: "running",
|
|
886
|
+
error: detailedError,
|
|
887
|
+
}),
|
|
888
|
+
};
|
|
889
|
+
yield {
|
|
890
|
+
type: "content",
|
|
891
|
+
sessionId: options.sessionId,
|
|
892
|
+
requestId: options.requestId,
|
|
893
|
+
agentId: options.selectedAgentId,
|
|
894
|
+
content: runtimeFailure,
|
|
895
|
+
};
|
|
896
|
+
yield {
|
|
897
|
+
type: "result",
|
|
898
|
+
result: {
|
|
899
|
+
sessionId: options.sessionId,
|
|
900
|
+
requestId: options.requestId,
|
|
901
|
+
agentId: currentAgentId,
|
|
902
|
+
state: "failed",
|
|
903
|
+
output: runtimeFailure,
|
|
904
|
+
finalMessageText: runtimeFailure,
|
|
905
|
+
},
|
|
906
|
+
};
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
747
909
|
try {
|
|
748
910
|
syntheticFallback = {
|
|
749
911
|
strategy: "stream-to-invoke",
|
|
@@ -757,6 +919,19 @@ export async function* streamHarnessRun(options) {
|
|
|
757
919
|
if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
|
|
758
920
|
yield createContentBlocksItem(options.sessionId, options.requestId, options.selectedAgentId, actual.contentBlocks);
|
|
759
921
|
}
|
|
922
|
+
if (actual.output) {
|
|
923
|
+
yield {
|
|
924
|
+
type: "event",
|
|
925
|
+
event: await options.emit(options.sessionId, options.requestId, 3, "output.delta", { content: actual.output }),
|
|
926
|
+
};
|
|
927
|
+
yield {
|
|
928
|
+
type: "content",
|
|
929
|
+
sessionId: options.sessionId,
|
|
930
|
+
requestId: options.requestId,
|
|
931
|
+
agentId: currentAgentId,
|
|
932
|
+
content: actual.output,
|
|
933
|
+
};
|
|
934
|
+
}
|
|
760
935
|
const terminalEvent = await options.setRequestStateAndEmit(options.sessionId, options.requestId, 6, actual.state, {
|
|
761
936
|
previousState: "running",
|
|
762
937
|
});
|
|
@@ -798,11 +973,12 @@ export async function* streamHarnessRun(options) {
|
|
|
798
973
|
};
|
|
799
974
|
await options.emitSyntheticFallback(options.sessionId, options.requestId, options.selectedAgentId, syntheticFallback);
|
|
800
975
|
const runtimeFailure = renderRuntimeFailure(invokeError);
|
|
976
|
+
const detailedError = describeRuntimeError(invokeError);
|
|
801
977
|
yield {
|
|
802
978
|
type: "event",
|
|
803
979
|
event: await options.setRequestStateAndEmit(options.sessionId, options.requestId, 6, "failed", {
|
|
804
980
|
previousState: "running",
|
|
805
|
-
error:
|
|
981
|
+
error: detailedError,
|
|
806
982
|
}),
|
|
807
983
|
};
|
|
808
984
|
yield {
|
package/dist/runtime/harness.js
CHANGED
|
@@ -215,6 +215,7 @@ export class AgentHarnessRuntime {
|
|
|
215
215
|
runtimeAdapterOptions: {
|
|
216
216
|
...runtimeAdapterOptions,
|
|
217
217
|
scheduleManager: runtimeAdapterOptions.scheduleManager ?? this.scheduleManager,
|
|
218
|
+
bindingResolver: runtimeAdapterOptions.bindingResolver ?? ((agentId) => this.workspace.bindings.get(agentId)),
|
|
218
219
|
functionToolContextResolver: runtimeAdapterOptions.functionToolContextResolver ?? ((input) => this.buildFunctionToolContext(input)),
|
|
219
220
|
},
|
|
220
221
|
checkpointers: this.checkpointers,
|
|
@@ -1303,6 +1304,8 @@ export class AgentHarnessRuntime {
|
|
|
1303
1304
|
clearRequestInput: (sessionId, requestId) => this.persistence.clearRequestInput(sessionId, requestId),
|
|
1304
1305
|
updateRequestInspection: (sessionId, requestId, patch) => this.persistence.updateRequestInspection(sessionId, requestId, patch),
|
|
1305
1306
|
appendRequestTraceItem: (sessionId, requestId, item) => this.persistence.appendRequestTraceItem(sessionId, requestId, item),
|
|
1307
|
+
loadRequestPlanState: (sessionId, requestId) => this.persistence.getRequestPlanState(sessionId, requestId),
|
|
1308
|
+
saveRequestPlanState: (sessionId, requestId, planState) => this.persistence.saveRequestPlanState(sessionId, requestId, planState),
|
|
1306
1309
|
emitSyntheticFallback: (sessionId, requestId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(sessionId, requestId, selectedAgentId, error),
|
|
1307
1310
|
});
|
|
1308
1311
|
for await (const item of stream) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AIMessage } from "langchain";
|
|
2
|
-
import { salvageToolArgs, isLikelyToolArgsObject, normalizeKnownToolArgs, tryParseJson } from "./output-tool-args.js";
|
|
2
|
+
import { salvageFunctionLikeToolCall, salvageToolArgs, isLikelyToolArgsObject, normalizeKnownToolArgs, tryParseJson } from "./output-tool-args.js";
|
|
3
3
|
export function sanitizeVisibleText(value) {
|
|
4
4
|
return value
|
|
5
5
|
.replace(/[A-Za-z0-9_]*Middleware\.after_model/g, "")
|
|
@@ -403,19 +403,26 @@ function normalizeAgentMessage(value) {
|
|
|
403
403
|
})
|
|
404
404
|
.filter((toolCall) => toolCall !== null)
|
|
405
405
|
: [];
|
|
406
|
-
const recoveredToolCalls = normalizedInvalidToolCalls.filter((toolCall) => typeof toolCall.args === "object" && !!toolCall.args && !Array.isArray(toolCall.args));
|
|
407
406
|
const normalizedContent = typeof typed.content === "string" || Array.isArray(typed.content)
|
|
408
407
|
? typed.content
|
|
409
408
|
: typeof typed.content === "object" && typed.content
|
|
410
409
|
? readTextContent(typed.content)
|
|
411
410
|
: "";
|
|
411
|
+
const recoveredToolCalls = normalizedInvalidToolCalls.filter((toolCall) => typeof toolCall.args === "object" && !!toolCall.args && !Array.isArray(toolCall.args));
|
|
412
|
+
const functionLikeToolCall = normalizedToolCalls.length === 0 && recoveredToolCalls.length === 0 && typeof normalizedContent === "string"
|
|
413
|
+
? salvageFunctionLikeToolCall(normalizedContent)
|
|
414
|
+
: null;
|
|
412
415
|
return new AIMessage({
|
|
413
|
-
content: normalizedContent,
|
|
416
|
+
content: functionLikeToolCall ? "" : normalizedContent,
|
|
414
417
|
name: typeof typed.name === "string" ? typed.name : undefined,
|
|
415
418
|
additional_kwargs: typeof typed.additional_kwargs === "object" && typed.additional_kwargs ? typed.additional_kwargs : {},
|
|
416
419
|
response_metadata: typeof typed.response_metadata === "object" && typed.response_metadata ? typed.response_metadata : {},
|
|
417
420
|
id: typeof typed.id === "string" ? typed.id : undefined,
|
|
418
|
-
tool_calls: [
|
|
421
|
+
tool_calls: [
|
|
422
|
+
...normalizedToolCalls,
|
|
423
|
+
...recoveredToolCalls,
|
|
424
|
+
...(functionLikeToolCall ? [{ name: functionLikeToolCall.name, args: functionLikeToolCall.args }] : []),
|
|
425
|
+
],
|
|
419
426
|
invalid_tool_calls: normalizedInvalidToolCalls.filter((toolCall) => toolCall.type !== "tool_call"),
|
|
420
427
|
usage_metadata: typeof typed.usage_metadata === "object" && typed.usage_metadata ? typed.usage_metadata : undefined,
|
|
421
428
|
});
|
|
@@ -11,6 +11,9 @@ export declare function resolveExecutionWithoutToolEvidenceInstruction(request:
|
|
|
11
11
|
export declare function resolveExecutionWithoutToolEvidenceTextInstruction(request: unknown, assistantText: string, toolCallEvidence?: boolean, resultEvidence?: {
|
|
12
12
|
hasWriteTodosEvidence?: boolean;
|
|
13
13
|
hasToolResultEvidence?: boolean;
|
|
14
|
+
hasIncompletePlanState?: boolean;
|
|
15
|
+
hasOpenTaskDelegation?: boolean;
|
|
16
|
+
hasMissingDelegatedExecutionEvidence?: boolean;
|
|
14
17
|
}): string | null;
|
|
15
18
|
export declare function resolveToolCallRecoveryInstruction(error: unknown): string | null;
|
|
16
19
|
export declare function appendToolRecoveryInstruction(input: unknown, instruction: string): unknown;
|
|
@@ -1,5 +1,38 @@
|
|
|
1
|
-
import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, INTERNAL_RUNTIME_SPILL_PATH_INSTRUCTION, STRICT_TOOL_JSON_INSTRUCTION, WORKSPACE_RELATIVE_PATH_INSTRUCTION, WRITE_TODOS_DESCRIPTIVE_CONTENT_INSTRUCTION, WRITE_TODOS_FULL_ENTRY_INSTRUCTION, WRITE_TODOS_NON_EMPTY_INITIAL_LIST_INSTRUCTION, } from "../prompts/runtime-prompts.js";
|
|
1
|
+
import { AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, INTERNAL_RUNTIME_SPILL_PATH_INSTRUCTION, STRICT_TOOL_JSON_INSTRUCTION, WORKSPACE_RELATIVE_PATH_INSTRUCTION, WRITE_TODOS_DESCRIPTIVE_CONTENT_INSTRUCTION, WRITE_TODOS_FULL_ENTRY_INSTRUCTION, WRITE_TODOS_NON_EMPTY_INITIAL_LIST_INSTRUCTION, } from "../prompts/runtime-prompts.js";
|
|
2
2
|
import { wrapNormalizedMessage, readTextContent } from "./output-content.js";
|
|
3
|
+
function collectRequestMessages(request) {
|
|
4
|
+
if (typeof request !== "object" || !request || Array.isArray(request)) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
const typed = request;
|
|
8
|
+
return Array.isArray(typed.messages)
|
|
9
|
+
? typed.messages.filter((message) => typeof message === "object" && !!message && !Array.isArray(message))
|
|
10
|
+
: [];
|
|
11
|
+
}
|
|
12
|
+
function readMessageRole(message) {
|
|
13
|
+
return typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
|
|
14
|
+
}
|
|
15
|
+
function readLatestUserRequestText(request) {
|
|
16
|
+
const messages = collectRequestMessages(request);
|
|
17
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
18
|
+
const message = messages[index];
|
|
19
|
+
if (readMessageRole(message) !== "user") {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const content = readTextContent(message.content).trim();
|
|
23
|
+
if (content) {
|
|
24
|
+
return content;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
function readSystemInstructionText(request) {
|
|
30
|
+
return collectRequestMessages(request)
|
|
31
|
+
.filter((message) => readMessageRole(message) === "system")
|
|
32
|
+
.map((message) => readTextContent(message.content).trim())
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join("\n\n");
|
|
35
|
+
}
|
|
3
36
|
function isToolCallParseFailure(error) {
|
|
4
37
|
return error instanceof Error && /error parsing tool call:/i.test(error.message);
|
|
5
38
|
}
|
|
@@ -72,20 +105,33 @@ export function isRetrySafeInvalidToolSelectionError(value) {
|
|
|
72
105
|
return !!text && /is not a valid tool, try one of \[/i.test(text);
|
|
73
106
|
}
|
|
74
107
|
export function shouldValidateExecutionWithoutToolEvidence(request) {
|
|
75
|
-
|
|
76
|
-
|
|
108
|
+
const userText = readLatestUserRequestText(request);
|
|
109
|
+
if (userText) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return readSystemInstructionText(request).length > 0;
|
|
77
113
|
}
|
|
78
114
|
export function resolveExecutionWithoutToolEvidenceInstruction(request, result) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return null;
|
|
115
|
+
const assistantText = readTextContent(result).trim();
|
|
116
|
+
return resolveExecutionWithoutToolEvidenceTextInstruction(request, assistantText, false, {});
|
|
82
117
|
}
|
|
83
118
|
export function resolveExecutionWithoutToolEvidenceTextInstruction(request, assistantText, toolCallEvidence = false, resultEvidence = {}) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
119
|
+
if (!shouldValidateExecutionWithoutToolEvidence(request)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const normalizedText = assistantText.trim();
|
|
123
|
+
const hasUnfinishedExecution = resultEvidence.hasIncompletePlanState === true
|
|
124
|
+
|| resultEvidence.hasOpenTaskDelegation === true
|
|
125
|
+
|| resultEvidence.hasMissingDelegatedExecutionEvidence === true;
|
|
126
|
+
if (!normalizedText || !hasUnfinishedExecution) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const hasExecutionEvidence = toolCallEvidence
|
|
130
|
+
|| resultEvidence.hasWriteTodosEvidence === true
|
|
131
|
+
|| resultEvidence.hasToolResultEvidence === true;
|
|
132
|
+
return hasExecutionEvidence
|
|
133
|
+
? AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION
|
|
134
|
+
: EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION;
|
|
89
135
|
}
|
|
90
136
|
export function resolveToolCallRecoveryInstruction(error) {
|
|
91
137
|
if (isRepairableWriteTodosEmptyFailure(error))
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export declare function tryParseJson(value: string): unknown | null;
|
|
2
|
+
export declare function salvageFunctionLikeToolCall(value: unknown): {
|
|
3
|
+
name: string;
|
|
4
|
+
args: Record<string, unknown>;
|
|
5
|
+
} | null;
|
|
2
6
|
export declare function salvageToolArgs(value: unknown): Record<string, unknown> | null;
|
|
3
7
|
export declare function normalizeKnownToolArgs(toolName: unknown, args: Record<string, unknown>): Record<string, unknown>;
|
|
4
8
|
export declare function isLikelyToolArgsObject(value: unknown): boolean;
|