u-foo 2.5.14 → 3.0.0
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/package.json +1 -1
- package/src/agents/prompts/native/environment.js +20 -8
- package/src/code/agent.js +517 -112
- package/src/code/commands.js +77 -0
- package/src/code/context/artifactGc.js +292 -0
- package/src/code/context/artifactIndex.js +161 -0
- package/src/code/context/artifacts.js +183 -0
- package/src/code/context/assembler.js +703 -0
- package/src/code/context/executionSegment.js +292 -0
- package/src/code/context/index.js +28 -0
- package/src/code/context/planGraph.js +1410 -0
- package/src/code/context/planGraphService.js +857 -0
- package/src/code/context/planMode.js +398 -0
- package/src/code/context/planProjection.js +432 -0
- package/src/code/context/projectSnapshot.js +201 -0
- package/src/code/context/promptLayers.js +175 -0
- package/src/code/context/reducers.js +328 -0
- package/src/code/context/stableJson.js +29 -0
- package/src/code/context/stateCommit.js +414 -0
- package/src/code/context/toolRuntime.js +172 -0
- package/src/code/context/transcript.js +182 -0
- package/src/code/context/transcriptSync.js +106 -0
- package/src/code/context/userInteraction.js +457 -0
- package/src/code/context/userNudge.js +116 -0
- package/src/code/context/workingSet.js +323 -0
- package/src/code/dispatch.js +20 -1
- package/src/code/index.js +8 -0
- package/src/code/modelCommand.js +87 -0
- package/src/code/nativeRunner.js +625 -34
- package/src/code/repl.js +196 -50
- package/src/code/runtime/agentWakeup.js +58 -0
- package/src/code/runtime/graphOwner.js +41 -0
- package/src/code/runtime/graphYieldRouter.js +42 -0
- package/src/code/runtime/index.js +15 -0
- package/src/code/runtime/loopMailbox.js +124 -0
- package/src/code/runtime/runtimeEvents.js +39 -0
- package/src/code/runtime/taskControl.js +565 -0
- package/src/code/runtime/taskFocus.js +165 -0
- package/src/code/runtime/taskLoop.js +383 -0
- package/src/code/runtime/taskRun.js +187 -0
- package/src/code/runtime/toolProvenance.js +70 -0
- package/src/code/runtime/workspaceLease.js +208 -0
- package/src/code/sessionStore.js +217 -15
- package/src/code/skills/index.js +10 -0
- package/src/code/skills/injection.js +66 -3
- package/src/code/skills/loader.js +21 -0
- package/src/code/skills/manifest.js +87 -0
- package/src/code/skills/render.js +15 -1
- package/src/code/taskDecomposer.js +56 -2
- package/src/code/tools/artifactRead.js +40 -0
- package/src/code/tools/askUser.js +11 -0
- package/src/code/tools/planGraph.js +29 -0
- package/src/code/tui.js +2 -0
- package/src/code/usageStore.js +15 -0
- package/src/ui/format/index.js +285 -45
- package/src/ui/format/markdownRenderer.js +436 -71
- package/src/ui/ink/ChatApp.js +39 -8
- package/src/ui/ink/UcodeApp.js +592 -43
- package/src/ui/ink/chatLogModel.js +102 -21
package/src/code/nativeRunner.js
CHANGED
|
@@ -6,12 +6,50 @@ const {
|
|
|
6
6
|
} = require("../agents/providers/credentials/kimi");
|
|
7
7
|
const { runToolCall } = require("./dispatch");
|
|
8
8
|
const { appendUsageRecord } = require("./usageStore");
|
|
9
|
+
const {
|
|
10
|
+
persistToolResultToContext,
|
|
11
|
+
sanitizeModelMessages,
|
|
12
|
+
} = require("./context/assembler");
|
|
13
|
+
const { systemBlocksToAnthropicPayload } = require("./context/promptLayers");
|
|
14
|
+
const { parseStructuredSideEffects } = require("./context/stateCommit");
|
|
15
|
+
const {
|
|
16
|
+
emptyExecutionState,
|
|
17
|
+
} = require("./context/executionSegment");
|
|
18
|
+
const {
|
|
19
|
+
normalizePlanGraphCommand,
|
|
20
|
+
runPlanGraphCommand,
|
|
21
|
+
activePlanRequiresExpansion,
|
|
22
|
+
} = require("./context/planGraphService");
|
|
23
|
+
const { planModeBlocksDirectTool } = require("./context/planMode");
|
|
24
|
+
const {
|
|
25
|
+
drainUserPrompts,
|
|
26
|
+
clearUserPrompts,
|
|
27
|
+
formatUserReminderMessage,
|
|
28
|
+
ensurePendingUserPrompts,
|
|
29
|
+
} = require("./context/userNudge");
|
|
30
|
+
const {
|
|
31
|
+
runAskUserTool,
|
|
32
|
+
syncInteractionFromPlanGraph,
|
|
33
|
+
hasPendingUserInteraction,
|
|
34
|
+
getPendingUserInteraction,
|
|
35
|
+
} = require("./context/userInteraction");
|
|
36
|
+
const { checkWriteAllowed } = require("./runtime/workspaceLease");
|
|
37
|
+
const { stableStringify } = require("./context/stableJson");
|
|
9
38
|
const { getReadToolDescription } = require("../agents/prompts/native/toolDescriptions/read");
|
|
10
39
|
const { getWriteToolDescription } = require("../agents/prompts/native/toolDescriptions/write");
|
|
11
40
|
const { getEditToolDescription } = require("../agents/prompts/native/toolDescriptions/edit");
|
|
12
41
|
const { getBashToolDescription } = require("../agents/prompts/native/toolDescriptions/bash");
|
|
13
42
|
|
|
14
|
-
const CORE_TOOL_NAMES = new Set([
|
|
43
|
+
const CORE_TOOL_NAMES = new Set([
|
|
44
|
+
"read",
|
|
45
|
+
"write",
|
|
46
|
+
"edit",
|
|
47
|
+
"bash",
|
|
48
|
+
"artifact_read",
|
|
49
|
+
"plan_graph",
|
|
50
|
+
"ask_user",
|
|
51
|
+
]);
|
|
52
|
+
const EXECUTABLE_GRAPH_TOOLS = new Set(["read", "write", "edit", "bash", "artifact_read"]);
|
|
15
53
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
16
54
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1";
|
|
17
55
|
const DEFAULT_KIMI_BASE_URL = "https://api.kimi.com/coding/v1";
|
|
@@ -20,7 +58,8 @@ const DEFAULT_KIMI_MODEL = "k3";
|
|
|
20
58
|
// to 200 (fork). We count individual tool calls (not turns), so 100 leaves headroom
|
|
21
59
|
// for non-trivial tasks while still catching runaway loops. Override via env.
|
|
22
60
|
const DEFAULT_MAX_NATIVE_TOOL_CALLS = 100;
|
|
23
|
-
const DEFAULT_MAX_NATIVE_TOOL_ERRORS =
|
|
61
|
+
const DEFAULT_MAX_NATIVE_TOOL_ERRORS = 20;
|
|
62
|
+
const DEFAULT_NATIVE_TIMEOUT_MS = 43200000; // 12 hours
|
|
24
63
|
// Anthropic Messages rejects max_tokens above the model's real cap (64K on
|
|
25
64
|
// current models), so the transports use different defaults. Override either
|
|
26
65
|
// via UFOO_UCODE_MAX_TOKENS (positive integer).
|
|
@@ -43,7 +82,7 @@ function nowMs() {
|
|
|
43
82
|
|
|
44
83
|
function normalizeTimeoutMs(value) {
|
|
45
84
|
const parsed = Number(value);
|
|
46
|
-
if (!Number.isFinite(parsed)) return
|
|
85
|
+
if (!Number.isFinite(parsed)) return DEFAULT_NATIVE_TIMEOUT_MS;
|
|
47
86
|
return Math.max(1000, Math.floor(parsed));
|
|
48
87
|
}
|
|
49
88
|
|
|
@@ -143,7 +182,7 @@ function enforceNativeToolBudget({
|
|
|
143
182
|
}
|
|
144
183
|
}
|
|
145
184
|
|
|
146
|
-
function createGuards({ signal = null, timeoutMs =
|
|
185
|
+
function createGuards({ signal = null, timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS } = {}) {
|
|
147
186
|
const startedAt = nowMs();
|
|
148
187
|
const budgetMs = normalizeTimeoutMs(timeoutMs);
|
|
149
188
|
|
|
@@ -169,7 +208,9 @@ function createGuards({ signal = null, timeoutMs = 300000 } = {}) {
|
|
|
169
208
|
function emitToolEvent(callback, event = {}) {
|
|
170
209
|
if (typeof callback !== "function") return;
|
|
171
210
|
try {
|
|
172
|
-
|
|
211
|
+
const payload = event && typeof event === "object" ? { ...event } : {};
|
|
212
|
+
if (payload.origin == null) delete payload.origin;
|
|
213
|
+
callback(payload);
|
|
173
214
|
} catch {
|
|
174
215
|
// ignore callback failures
|
|
175
216
|
}
|
|
@@ -364,6 +405,151 @@ function buildCoreToolSpecs() {
|
|
|
364
405
|
},
|
|
365
406
|
},
|
|
366
407
|
},
|
|
408
|
+
{
|
|
409
|
+
type: "function",
|
|
410
|
+
function: {
|
|
411
|
+
name: "artifact_read",
|
|
412
|
+
description: [
|
|
413
|
+
"Read previously stored tool output by artifactId.",
|
|
414
|
+
"This does not read workspace files; use `read` for repository paths.",
|
|
415
|
+
"Optionally read a slice with startLine/endLine, maxChars, or tailLines.",
|
|
416
|
+
].join(" "),
|
|
417
|
+
parameters: {
|
|
418
|
+
type: "object",
|
|
419
|
+
properties: {
|
|
420
|
+
artifactId: { type: "string" },
|
|
421
|
+
sessionId: { type: "string" },
|
|
422
|
+
startLine: { type: "integer" },
|
|
423
|
+
endLine: { type: "integer" },
|
|
424
|
+
maxChars: { type: "integer" },
|
|
425
|
+
tailLines: { type: "integer" },
|
|
426
|
+
},
|
|
427
|
+
required: ["artifactId"],
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
type: "function",
|
|
433
|
+
function: {
|
|
434
|
+
name: "plan_graph",
|
|
435
|
+
description: [
|
|
436
|
+
"Manage the persistent Plan Graph and asynchronous TaskRuns.",
|
|
437
|
+
"Use create, patch, inspect, or cancel_graph for graph operations, and control for TaskRun lifecycle.",
|
|
438
|
+
"`control.start_task` starts a `task_loop` asynchronously and returns immediately.",
|
|
439
|
+
"Use `inline_llm` for work handled by the current graph owner,",
|
|
440
|
+
"`expand` for tasks that must be lowered into child nodes,",
|
|
441
|
+
"and `task_loop` for asynchronous work in an independent TaskLoop.",
|
|
442
|
+
"Do not call `plan_graph` together with data-plane tools in the same assistant turn.",
|
|
443
|
+
].join(" "),
|
|
444
|
+
parameters: {
|
|
445
|
+
type: "object",
|
|
446
|
+
properties: {
|
|
447
|
+
operation: {
|
|
448
|
+
type: "string",
|
|
449
|
+
enum: [
|
|
450
|
+
"create",
|
|
451
|
+
"patch",
|
|
452
|
+
"inspect",
|
|
453
|
+
"clear",
|
|
454
|
+
"cancel_graph",
|
|
455
|
+
"control",
|
|
456
|
+
],
|
|
457
|
+
description: [
|
|
458
|
+
"create/patch/inspect/cancel_graph mutate or inspect the graph spec;",
|
|
459
|
+
"control runs TaskRun lifecycle and node status actions.",
|
|
460
|
+
].join(" "),
|
|
461
|
+
},
|
|
462
|
+
graph: {
|
|
463
|
+
type: "object",
|
|
464
|
+
description: "Full graph for create (objective + nodes). group is input sugar only.",
|
|
465
|
+
},
|
|
466
|
+
operations: {
|
|
467
|
+
type: "array",
|
|
468
|
+
description: [
|
|
469
|
+
"Patch ops only: add_node, expand_node, add_dependency, remove_dependency.",
|
|
470
|
+
"Status actions (complete_task, skip_node, cancel_subtree) belong under control.actions.",
|
|
471
|
+
].join(" "),
|
|
472
|
+
items: { type: "object" },
|
|
473
|
+
},
|
|
474
|
+
actions: {
|
|
475
|
+
type: "array",
|
|
476
|
+
description: [
|
|
477
|
+
"Control actions: start_task, cancel_task, fail_task, complete_task, skip_node, cancel_subtree.",
|
|
478
|
+
"complete_task with taskRunId finishes a TaskLoop TaskRun;",
|
|
479
|
+
"complete_task with nodeId finishes a waiting_llm inline task owned by the graph owner.",
|
|
480
|
+
].join(" "),
|
|
481
|
+
items: { type: "object" },
|
|
482
|
+
},
|
|
483
|
+
reason: {
|
|
484
|
+
type: "string",
|
|
485
|
+
description: "Optional reason for cancel_graph or fail/cancel task.",
|
|
486
|
+
},
|
|
487
|
+
commandId: {
|
|
488
|
+
type: "string",
|
|
489
|
+
description: [
|
|
490
|
+
"Optional idempotency key for explicit replay.",
|
|
491
|
+
"When omitted, the Runtime should derive one from the tool invocation when available.",
|
|
492
|
+
].join(" "),
|
|
493
|
+
},
|
|
494
|
+
expectedSpecRevision: {
|
|
495
|
+
type: "integer",
|
|
496
|
+
description: "Optional optimistic concurrency token for patch.",
|
|
497
|
+
},
|
|
498
|
+
graphId: {
|
|
499
|
+
type: "string",
|
|
500
|
+
description: "Optional graph id check for patch/control.",
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
required: ["operation"],
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
type: "function",
|
|
509
|
+
function: {
|
|
510
|
+
name: "ask_user",
|
|
511
|
+
description: [
|
|
512
|
+
"Ask the user for input and pause the current Agent loop until the reply arrives.",
|
|
513
|
+
"Use only when user input is required to proceed, not for routine updates or decisions the agent can safely make.",
|
|
514
|
+
"`kind=approval` requests yes/no confirmation; `kind=choice` presents the supplied options; `kind=chat` requests free text.",
|
|
515
|
+
"This must be the only tool call in the turn.",
|
|
516
|
+
"The reply is returned only as this tool result, not as a separate user message or pending user prompt.",
|
|
517
|
+
"After the tool returns, continue from the answer and do not ask the same question again.",
|
|
518
|
+
"Running TaskRuns are not paused automatically.",
|
|
519
|
+
].join(" "),
|
|
520
|
+
parameters: {
|
|
521
|
+
type: "object",
|
|
522
|
+
properties: {
|
|
523
|
+
kind: {
|
|
524
|
+
type: "string",
|
|
525
|
+
enum: ["approval", "choice", "chat"],
|
|
526
|
+
description: "Interaction type.",
|
|
527
|
+
},
|
|
528
|
+
prompt: {
|
|
529
|
+
type: "string",
|
|
530
|
+
description: "Question shown to the user.",
|
|
531
|
+
},
|
|
532
|
+
options: {
|
|
533
|
+
type: "array",
|
|
534
|
+
description: "For choice: option labels (or {key,label} objects). Ignored for chat.",
|
|
535
|
+
items: {
|
|
536
|
+
oneOf: [
|
|
537
|
+
{ type: "string" },
|
|
538
|
+
{
|
|
539
|
+
type: "object",
|
|
540
|
+
properties: {
|
|
541
|
+
key: { type: "string" },
|
|
542
|
+
label: { type: "string" },
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
required: ["kind", "prompt"],
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
},
|
|
367
553
|
];
|
|
368
554
|
}
|
|
369
555
|
|
|
@@ -375,7 +561,7 @@ function buildAnthropicToolSpecs() {
|
|
|
375
561
|
}));
|
|
376
562
|
}
|
|
377
563
|
|
|
378
|
-
function createRequestController({ signal = null, timeoutMs =
|
|
564
|
+
function createRequestController({ signal = null, timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS } = {}) {
|
|
379
565
|
const controller = new AbortController();
|
|
380
566
|
let timedOut = false;
|
|
381
567
|
|
|
@@ -437,11 +623,7 @@ function normalizeToolName(value = "") {
|
|
|
437
623
|
}
|
|
438
624
|
|
|
439
625
|
function toJsonString(value) {
|
|
440
|
-
|
|
441
|
-
return JSON.stringify(value);
|
|
442
|
-
} catch {
|
|
443
|
-
return String(value || "");
|
|
444
|
-
}
|
|
626
|
+
return stableStringify(value);
|
|
445
627
|
}
|
|
446
628
|
|
|
447
629
|
function parseSseBlocks(text = "") {
|
|
@@ -490,7 +672,17 @@ function normalizeToolCallArgs(raw = "") {
|
|
|
490
672
|
return {};
|
|
491
673
|
}
|
|
492
674
|
|
|
493
|
-
function runCoreTool({
|
|
675
|
+
function runCoreTool({
|
|
676
|
+
tool = "",
|
|
677
|
+
args = {},
|
|
678
|
+
workspaceRoot = process.cwd(),
|
|
679
|
+
onToolEvent = null,
|
|
680
|
+
sessionId = "",
|
|
681
|
+
onArtifactPersisted = null,
|
|
682
|
+
executionState = null,
|
|
683
|
+
origin = null,
|
|
684
|
+
resume = null,
|
|
685
|
+
} = {}) {
|
|
494
686
|
const normalizedTool = normalizeToolName(tool);
|
|
495
687
|
if (!normalizedTool) {
|
|
496
688
|
emitToolEvent(onToolEvent, {
|
|
@@ -498,6 +690,7 @@ function runCoreTool({ tool = "", args = {}, workspaceRoot = process.cwd(), onTo
|
|
|
498
690
|
phase: "error",
|
|
499
691
|
args: args && typeof args === "object" ? { ...args } : {},
|
|
500
692
|
error: `unsupported tool: ${tool}`,
|
|
693
|
+
origin,
|
|
501
694
|
});
|
|
502
695
|
return {
|
|
503
696
|
ok: false,
|
|
@@ -506,16 +699,101 @@ function runCoreTool({ tool = "", args = {}, workspaceRoot = process.cwd(), onTo
|
|
|
506
699
|
}
|
|
507
700
|
|
|
508
701
|
const safeArgs = args && typeof args === "object" ? { ...args } : {};
|
|
702
|
+
if (normalizedTool === "artifact_read" && sessionId && !safeArgs.sessionId) {
|
|
703
|
+
safeArgs.sessionId = sessionId;
|
|
704
|
+
}
|
|
509
705
|
emitToolEvent(onToolEvent, {
|
|
510
706
|
tool: normalizedTool,
|
|
511
707
|
phase: "start",
|
|
512
708
|
args: safeArgs,
|
|
513
709
|
error: "",
|
|
710
|
+
origin,
|
|
514
711
|
});
|
|
515
712
|
|
|
713
|
+
if (normalizedTool === "plan_graph") {
|
|
714
|
+
const state = executionState && typeof executionState === "object"
|
|
715
|
+
? executionState
|
|
716
|
+
: emptyExecutionState();
|
|
717
|
+
const result = runPlanGraphCommand(safeArgs, {
|
|
718
|
+
executionState: state,
|
|
719
|
+
autoAdvance: true,
|
|
720
|
+
parallel: true,
|
|
721
|
+
runTool: ({ node, args: nestedArgs, tool: nestedTool, stepId }) => {
|
|
722
|
+
const nested = runCoreTool({
|
|
723
|
+
tool: nestedTool,
|
|
724
|
+
args: nestedArgs,
|
|
725
|
+
workspaceRoot,
|
|
726
|
+
onToolEvent,
|
|
727
|
+
sessionId,
|
|
728
|
+
onArtifactPersisted,
|
|
729
|
+
executionState: state,
|
|
730
|
+
origin: {
|
|
731
|
+
kind: "plan_graph",
|
|
732
|
+
graphId: String(state.planGraph && state.planGraph.graphId || ""),
|
|
733
|
+
graphRevision: Number(state.planGraph && state.planGraph.specRevision) || 0,
|
|
734
|
+
commandRevision: Number(state.planGraph && state.planGraph.specRevision) || 0,
|
|
735
|
+
nodeId: stepId || (node && node.id) || "",
|
|
736
|
+
attempt: Number(node && node.attempt) || 0,
|
|
737
|
+
},
|
|
738
|
+
});
|
|
739
|
+
return nested;
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
if (result.ok === false) {
|
|
743
|
+
emitToolEvent(onToolEvent, {
|
|
744
|
+
tool: "plan_graph",
|
|
745
|
+
phase: "error",
|
|
746
|
+
args: safeArgs,
|
|
747
|
+
error: Array.isArray(result.errors)
|
|
748
|
+
? result.errors.map((e) => e.message || e.code).join("; ")
|
|
749
|
+
: "plan_graph rejected",
|
|
750
|
+
origin,
|
|
751
|
+
});
|
|
752
|
+
} else {
|
|
753
|
+
syncInteractionFromPlanGraph(result.executionState || state);
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
...result.modelPayload,
|
|
757
|
+
ok: result.status === "accepted",
|
|
758
|
+
executionState: result.executionState || state,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (normalizedTool === "ask_user") {
|
|
763
|
+
const state = executionState && typeof executionState === "object"
|
|
764
|
+
? executionState
|
|
765
|
+
: emptyExecutionState();
|
|
766
|
+
const result = runAskUserTool(safeArgs, {
|
|
767
|
+
executionState: state,
|
|
768
|
+
resume: resume || null,
|
|
769
|
+
});
|
|
770
|
+
const ok = result.ok !== false && result.status !== "rejected";
|
|
771
|
+
emitToolEvent(onToolEvent, {
|
|
772
|
+
tool: "ask_user",
|
|
773
|
+
phase: ok ? "end" : "error",
|
|
774
|
+
args: safeArgs,
|
|
775
|
+
result: result.modelPayload || result,
|
|
776
|
+
error: ok ? "" : (result.error || "ask_user rejected"),
|
|
777
|
+
origin,
|
|
778
|
+
});
|
|
779
|
+
return {
|
|
780
|
+
...(result.modelPayload || result),
|
|
781
|
+
ok,
|
|
782
|
+
status: result.status,
|
|
783
|
+
waiting_user: Boolean(result.waiting_user || result.status === "waiting_user"),
|
|
784
|
+
interactionId: result.interactionId || "",
|
|
785
|
+
executionState: result.executionState || state,
|
|
786
|
+
deferToolResult: ok && result.status === "waiting_user",
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const toolOptions = { workspaceRoot, cwd: workspaceRoot };
|
|
791
|
+
if (normalizedTool === "artifact_read" && sessionId) {
|
|
792
|
+
toolOptions.sessionId = sessionId;
|
|
793
|
+
}
|
|
516
794
|
const result = runToolCall(
|
|
517
795
|
{ tool: normalizedTool, args: safeArgs },
|
|
518
|
-
|
|
796
|
+
toolOptions,
|
|
519
797
|
);
|
|
520
798
|
|
|
521
799
|
if (!result || result.ok === false) {
|
|
@@ -524,9 +802,34 @@ function runCoreTool({ tool = "", args = {}, workspaceRoot = process.cwd(), onTo
|
|
|
524
802
|
phase: "error",
|
|
525
803
|
args: safeArgs,
|
|
526
804
|
error: String((result && result.error) || `${normalizedTool} failed`),
|
|
805
|
+
origin,
|
|
527
806
|
});
|
|
807
|
+
return result;
|
|
528
808
|
}
|
|
529
809
|
|
|
810
|
+
if (normalizedTool !== "artifact_read" && EXECUTABLE_GRAPH_TOOLS.has(normalizedTool)) {
|
|
811
|
+
const persisted = persistToolResultToContext({
|
|
812
|
+
workspaceRoot,
|
|
813
|
+
sessionId,
|
|
814
|
+
tool: normalizedTool,
|
|
815
|
+
args: safeArgs,
|
|
816
|
+
rawResult: result,
|
|
817
|
+
});
|
|
818
|
+
if (typeof onArtifactPersisted === "function") {
|
|
819
|
+
try {
|
|
820
|
+
onArtifactPersisted(persisted);
|
|
821
|
+
} catch {
|
|
822
|
+
// ignore
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const payload = persisted.modelPayload || result;
|
|
826
|
+
if (origin) payload.origin = origin;
|
|
827
|
+
return payload;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (origin && result && typeof result === "object") {
|
|
831
|
+
return { ...result, origin };
|
|
832
|
+
}
|
|
530
833
|
return result;
|
|
531
834
|
}
|
|
532
835
|
|
|
@@ -548,7 +851,7 @@ async function runSseRequest({
|
|
|
548
851
|
headers = {},
|
|
549
852
|
payload = {},
|
|
550
853
|
signal = null,
|
|
551
|
-
timeoutMs =
|
|
854
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
552
855
|
onPhase = null,
|
|
553
856
|
onNonStream,
|
|
554
857
|
onEvent,
|
|
@@ -640,7 +943,7 @@ async function runOpenAiLikeTurn({
|
|
|
640
943
|
onThinkingDelta = null,
|
|
641
944
|
onPhase = null,
|
|
642
945
|
signal = null,
|
|
643
|
-
timeoutMs =
|
|
946
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
644
947
|
} = {}) {
|
|
645
948
|
const payload = {
|
|
646
949
|
model,
|
|
@@ -890,12 +1193,13 @@ async function runAnthropicTurn({
|
|
|
890
1193
|
apiKey = "",
|
|
891
1194
|
model = "",
|
|
892
1195
|
systemPrompt = "",
|
|
1196
|
+
systemBlocks = null,
|
|
893
1197
|
messages = [],
|
|
894
1198
|
onTextDelta = null,
|
|
895
1199
|
onThinkingDelta = null,
|
|
896
1200
|
onPhase = null,
|
|
897
1201
|
signal = null,
|
|
898
|
-
timeoutMs =
|
|
1202
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
899
1203
|
} = {}) {
|
|
900
1204
|
const payload = {
|
|
901
1205
|
model,
|
|
@@ -908,17 +1212,19 @@ async function runAnthropicTurn({
|
|
|
908
1212
|
if (thinkingBudget > 0) {
|
|
909
1213
|
payload.thinking = { type: "enabled", budget_tokens: thinkingBudget };
|
|
910
1214
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1215
|
+
if (Array.isArray(systemBlocks) && systemBlocks.length > 0) {
|
|
1216
|
+
payload.system = systemBlocksToAnthropicPayload(systemBlocks);
|
|
1217
|
+
} else {
|
|
1218
|
+
const systemText = String(systemPrompt || "").trim();
|
|
1219
|
+
if (systemText) {
|
|
1220
|
+
payload.system = [
|
|
1221
|
+
{
|
|
1222
|
+
type: "text",
|
|
1223
|
+
text: systemText,
|
|
1224
|
+
cache_control: { ...ANTHROPIC_CACHE_CONTROL },
|
|
1225
|
+
},
|
|
1226
|
+
];
|
|
1227
|
+
}
|
|
922
1228
|
}
|
|
923
1229
|
|
|
924
1230
|
const headers = {
|
|
@@ -1294,18 +1600,23 @@ async function runNativeLoop({
|
|
|
1294
1600
|
workspaceRoot = process.cwd(),
|
|
1295
1601
|
prompt = "",
|
|
1296
1602
|
systemPrompt = "",
|
|
1603
|
+
systemBlocks = null,
|
|
1297
1604
|
historyMessages = [],
|
|
1298
1605
|
model = "",
|
|
1299
1606
|
baseUrl = "",
|
|
1300
1607
|
apiKey = "",
|
|
1301
1608
|
provider = "",
|
|
1302
|
-
timeoutMs =
|
|
1609
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1303
1610
|
onStreamDelta = null,
|
|
1304
1611
|
onThinkingDelta = null,
|
|
1305
1612
|
onPhase = null,
|
|
1306
1613
|
onToolEvent = null,
|
|
1614
|
+
onArtifactPersisted = null,
|
|
1615
|
+
sessionId = "",
|
|
1307
1616
|
signal = null,
|
|
1308
1617
|
guards,
|
|
1618
|
+
executionState: initialExecutionState = null,
|
|
1619
|
+
resume = false,
|
|
1309
1620
|
} = {}) {
|
|
1310
1621
|
const requestModel = String(model || "").trim();
|
|
1311
1622
|
if (!requestModel) {
|
|
@@ -1317,25 +1628,46 @@ async function runNativeLoop({
|
|
|
1317
1628
|
throw new Error("ucode baseUrl is not configured");
|
|
1318
1629
|
}
|
|
1319
1630
|
|
|
1320
|
-
const messages = cloneMessageList(historyMessages);
|
|
1321
|
-
|
|
1631
|
+
const messages = sanitizeModelMessages(cloneMessageList(historyMessages));
|
|
1632
|
+
if (!resume) {
|
|
1633
|
+
transport.prepareMessages({ messages, systemPrompt, prompt });
|
|
1634
|
+
}
|
|
1322
1635
|
|
|
1323
1636
|
let aggregated = "";
|
|
1324
1637
|
let streamed = false;
|
|
1325
1638
|
let toolCallsExecuted = 0;
|
|
1326
1639
|
let toolErrors = 0;
|
|
1640
|
+
let executionState = initialExecutionState && typeof initialExecutionState === "object"
|
|
1641
|
+
? initialExecutionState
|
|
1642
|
+
: emptyExecutionState();
|
|
1643
|
+
if (typeof executionState.planMode !== "boolean") executionState.planMode = false;
|
|
1644
|
+
ensurePendingUserPrompts(executionState);
|
|
1327
1645
|
const toolBudget = resolveNativeToolBudget();
|
|
1328
1646
|
const usage = createUsageTotals();
|
|
1329
1647
|
|
|
1648
|
+
function injectPendingUserReminders() {
|
|
1649
|
+
const nudges = drainUserPrompts(executionState);
|
|
1650
|
+
if (nudges.length === 0) return;
|
|
1651
|
+
const waiting = executionState.planGraph && executionState.planGraph.waitingFor
|
|
1652
|
+
? executionState.planGraph.waitingFor
|
|
1653
|
+
: null;
|
|
1654
|
+
const content = formatUserReminderMessage(nudges, { waitingFor: waiting });
|
|
1655
|
+
if (!content) return;
|
|
1656
|
+
messages.push({ role: "user", content });
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1330
1659
|
while (true) {
|
|
1331
1660
|
guards.ensureActive();
|
|
1332
1661
|
|
|
1662
|
+
injectPendingUserReminders();
|
|
1663
|
+
|
|
1333
1664
|
const turnResult = await transport.runTurn({
|
|
1334
1665
|
url: requestUrl,
|
|
1335
1666
|
apiKey,
|
|
1336
1667
|
model: requestModel,
|
|
1337
1668
|
provider,
|
|
1338
1669
|
systemPrompt,
|
|
1670
|
+
systemBlocks,
|
|
1339
1671
|
messages,
|
|
1340
1672
|
signal,
|
|
1341
1673
|
timeoutMs,
|
|
@@ -1358,8 +1690,36 @@ async function runNativeLoop({
|
|
|
1358
1690
|
const toolCalls = transport.getToolCalls(turnResult);
|
|
1359
1691
|
|
|
1360
1692
|
if (toolCalls.length === 0) {
|
|
1361
|
-
transport.appendFinalAssistantMessage({ messages, turnResult });
|
|
1362
1693
|
const text = String(turnResult.text || "").trim();
|
|
1694
|
+
const sideEffects = parseStructuredSideEffects(text);
|
|
1695
|
+
const planCommand = sideEffects ? normalizePlanGraphCommand(sideEffects) : null;
|
|
1696
|
+
if (planCommand) {
|
|
1697
|
+
transport.appendFinalAssistantMessage({ messages, turnResult });
|
|
1698
|
+
const planResult = runCoreTool({
|
|
1699
|
+
tool: "plan_graph",
|
|
1700
|
+
args: planCommand,
|
|
1701
|
+
workspaceRoot,
|
|
1702
|
+
onToolEvent,
|
|
1703
|
+
sessionId,
|
|
1704
|
+
onArtifactPersisted,
|
|
1705
|
+
executionState,
|
|
1706
|
+
origin: { kind: "legacy_side_effect", source: planCommand.source || "legacy" },
|
|
1707
|
+
});
|
|
1708
|
+
if (planResult && planResult.executionState) {
|
|
1709
|
+
executionState = planResult.executionState;
|
|
1710
|
+
}
|
|
1711
|
+
messages.push({
|
|
1712
|
+
role: "user",
|
|
1713
|
+
content: JSON.stringify({
|
|
1714
|
+
type: "plan_graph_result",
|
|
1715
|
+
...((planResult && planResult.status)
|
|
1716
|
+
? planResult
|
|
1717
|
+
: { status: "rejected", ok: false, error: "plan_graph failed" }),
|
|
1718
|
+
}),
|
|
1719
|
+
});
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
transport.appendFinalAssistantMessage({ messages, turnResult });
|
|
1363
1723
|
if (!aggregated.trim() && text) {
|
|
1364
1724
|
aggregated = text;
|
|
1365
1725
|
}
|
|
@@ -1369,6 +1729,7 @@ async function runNativeLoop({
|
|
|
1369
1729
|
toolCallsExecuted,
|
|
1370
1730
|
messages,
|
|
1371
1731
|
usage,
|
|
1732
|
+
executionState,
|
|
1372
1733
|
};
|
|
1373
1734
|
}
|
|
1374
1735
|
|
|
@@ -1380,21 +1741,180 @@ async function runNativeLoop({
|
|
|
1380
1741
|
toolCallsExecuted,
|
|
1381
1742
|
messages,
|
|
1382
1743
|
usage,
|
|
1744
|
+
executionState,
|
|
1383
1745
|
};
|
|
1384
1746
|
}
|
|
1385
1747
|
|
|
1748
|
+
const callNames = pendingCalls.map((call) => String(call.name || "").trim().toLowerCase());
|
|
1749
|
+
const hasPlanGraph = callNames.includes("plan_graph");
|
|
1750
|
+
const hasAskUser = callNames.includes("ask_user");
|
|
1751
|
+
const hasDataTool = callNames.some((name) => EXECUTABLE_GRAPH_TOOLS.has(name));
|
|
1752
|
+
if (hasPlanGraph && hasDataTool) {
|
|
1753
|
+
// prepareToolCalls already appended the assistant tool_calls / tool_use
|
|
1754
|
+
// message; every declared call must get a contiguous tool result.
|
|
1755
|
+
const collectedResults = [];
|
|
1756
|
+
for (const pending of pendingCalls) {
|
|
1757
|
+
transport.appendToolResult({
|
|
1758
|
+
messages,
|
|
1759
|
+
collected: collectedResults,
|
|
1760
|
+
call: pending,
|
|
1761
|
+
toolResult: {
|
|
1762
|
+
ok: false,
|
|
1763
|
+
status: "rejected",
|
|
1764
|
+
error: "Do not mix plan_graph with data-plane tools in the same turn",
|
|
1765
|
+
code: "MIXED_PLAN_AND_DATA_TOOLS",
|
|
1766
|
+
},
|
|
1767
|
+
});
|
|
1768
|
+
toolCallsExecuted += 1;
|
|
1769
|
+
toolErrors += 1;
|
|
1770
|
+
}
|
|
1771
|
+
if (typeof transport.flushToolResults === "function") {
|
|
1772
|
+
transport.flushToolResults({ messages, collected: collectedResults });
|
|
1773
|
+
}
|
|
1774
|
+
continue;
|
|
1775
|
+
}
|
|
1776
|
+
if (hasAskUser && pendingCalls.length > 1) {
|
|
1777
|
+
const collectedResults = [];
|
|
1778
|
+
for (const pending of pendingCalls) {
|
|
1779
|
+
transport.appendToolResult({
|
|
1780
|
+
messages,
|
|
1781
|
+
collected: collectedResults,
|
|
1782
|
+
call: pending,
|
|
1783
|
+
toolResult: {
|
|
1784
|
+
ok: false,
|
|
1785
|
+
status: "rejected",
|
|
1786
|
+
error: "ask_user must be the only tool call in the turn",
|
|
1787
|
+
code: "ASK_USER_MUST_BE_ALONE",
|
|
1788
|
+
},
|
|
1789
|
+
});
|
|
1790
|
+
toolCallsExecuted += 1;
|
|
1791
|
+
toolErrors += 1;
|
|
1792
|
+
}
|
|
1793
|
+
if (typeof transport.flushToolResults === "function") {
|
|
1794
|
+
transport.flushToolResults({ messages, collected: collectedResults });
|
|
1795
|
+
}
|
|
1796
|
+
continue;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1386
1799
|
const collectedResults = [];
|
|
1800
|
+
let deferredAskUser = null;
|
|
1387
1801
|
for (const pending of pendingCalls) {
|
|
1802
|
+
const pendingName = String(pending.name || "").trim().toLowerCase();
|
|
1803
|
+
if (
|
|
1804
|
+
EXECUTABLE_GRAPH_TOOLS.has(pendingName)
|
|
1805
|
+
&& activePlanRequiresExpansion(executionState.planGraph)
|
|
1806
|
+
) {
|
|
1807
|
+
const blocked = {
|
|
1808
|
+
ok: false,
|
|
1809
|
+
status: "rejected",
|
|
1810
|
+
errors: [{
|
|
1811
|
+
code: "ACTIVE_PLAN_REQUIRES_EXPANSION",
|
|
1812
|
+
message: "Active plan is waiting on a task; use plan_graph expand_node or control.complete_task instead of direct tools",
|
|
1813
|
+
}],
|
|
1814
|
+
};
|
|
1815
|
+
toolCallsExecuted += 1;
|
|
1816
|
+
toolErrors += 1;
|
|
1817
|
+
transport.appendToolResult({
|
|
1818
|
+
messages,
|
|
1819
|
+
collected: collectedResults,
|
|
1820
|
+
call: pending,
|
|
1821
|
+
toolResult: blocked,
|
|
1822
|
+
});
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
if (planModeBlocksDirectTool(pendingName, executionState)) {
|
|
1826
|
+
const blocked = {
|
|
1827
|
+
ok: false,
|
|
1828
|
+
status: "rejected",
|
|
1829
|
+
errors: [{
|
|
1830
|
+
code: "PLAN_MODE_BLOCKS_SIDE_EFFECT",
|
|
1831
|
+
message: "Plan mode is on; use plan_graph for write/edit/bash, or ask the user to /plan off",
|
|
1832
|
+
}],
|
|
1833
|
+
};
|
|
1834
|
+
toolCallsExecuted += 1;
|
|
1835
|
+
toolErrors += 1;
|
|
1836
|
+
transport.appendToolResult({
|
|
1837
|
+
messages,
|
|
1838
|
+
collected: collectedResults,
|
|
1839
|
+
call: pending,
|
|
1840
|
+
toolResult: blocked,
|
|
1841
|
+
});
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
const leaseCheck = checkWriteAllowed(executionState, {
|
|
1845
|
+
tool: pendingName,
|
|
1846
|
+
originKind: "agent_loop",
|
|
1847
|
+
});
|
|
1848
|
+
if (!leaseCheck.ok) {
|
|
1849
|
+
const blocked = {
|
|
1850
|
+
ok: false,
|
|
1851
|
+
status: "rejected",
|
|
1852
|
+
errors: [{
|
|
1853
|
+
code: leaseCheck.code || "WORKSPACE_WRITE_LEASE_HELD",
|
|
1854
|
+
message: leaseCheck.message
|
|
1855
|
+
|| "Workspace write lease held by an active TaskRun",
|
|
1856
|
+
owner: leaseCheck.owner || null,
|
|
1857
|
+
}],
|
|
1858
|
+
};
|
|
1859
|
+
toolCallsExecuted += 1;
|
|
1860
|
+
toolErrors += 1;
|
|
1861
|
+
transport.appendToolResult({
|
|
1862
|
+
messages,
|
|
1863
|
+
collected: collectedResults,
|
|
1864
|
+
call: pending,
|
|
1865
|
+
toolResult: blocked,
|
|
1866
|
+
});
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
const toolCallId = pending.source && (pending.source.id || (pending.source.function && pending.source.id))
|
|
1871
|
+
? String(pending.source.id || "")
|
|
1872
|
+
: "";
|
|
1873
|
+
const resumeForAsk = pendingName === "ask_user"
|
|
1874
|
+
? {
|
|
1875
|
+
toolCallId: toolCallId || String((pending.source && pending.source.id) || `call_${randomUUID()}`),
|
|
1876
|
+
toolName: "ask_user",
|
|
1877
|
+
call: {
|
|
1878
|
+
name: pending.name,
|
|
1879
|
+
args: pending.args,
|
|
1880
|
+
source: pending.source,
|
|
1881
|
+
},
|
|
1882
|
+
}
|
|
1883
|
+
: null;
|
|
1884
|
+
|
|
1388
1885
|
const toolResult = runCoreTool({
|
|
1389
1886
|
tool: pending.name,
|
|
1390
1887
|
args: pending.args,
|
|
1391
1888
|
workspaceRoot,
|
|
1392
1889
|
onToolEvent,
|
|
1890
|
+
sessionId,
|
|
1891
|
+
onArtifactPersisted,
|
|
1892
|
+
executionState,
|
|
1893
|
+
resume: resumeForAsk,
|
|
1393
1894
|
});
|
|
1895
|
+
if (toolResult && toolResult.executionState) {
|
|
1896
|
+
executionState = toolResult.executionState;
|
|
1897
|
+
}
|
|
1394
1898
|
toolCallsExecuted += 1;
|
|
1395
1899
|
if (!toolResult || toolResult.ok === false) {
|
|
1396
1900
|
toolErrors += 1;
|
|
1397
1901
|
}
|
|
1902
|
+
|
|
1903
|
+
if (pendingName === "ask_user" && toolResult && toolResult.deferToolResult) {
|
|
1904
|
+
// Attach resume metadata onto pending interaction for contiguous tool_result later.
|
|
1905
|
+
const pendingInteraction = getPendingUserInteraction(executionState);
|
|
1906
|
+
if (pendingInteraction) {
|
|
1907
|
+
pendingInteraction.resume = {
|
|
1908
|
+
...(pendingInteraction.resume || {}),
|
|
1909
|
+
...(resumeForAsk || {}),
|
|
1910
|
+
mode: "ask_user",
|
|
1911
|
+
transport: provider === "anthropic" ? "anthropic-messages" : "openai-chat",
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
deferredAskUser = { call: pending, interactionId: toolResult.interactionId || "" };
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1398
1918
|
enforceNativeToolBudget({
|
|
1399
1919
|
toolCallsExecuted,
|
|
1400
1920
|
toolErrors,
|
|
@@ -1414,23 +1934,79 @@ async function runNativeLoop({
|
|
|
1414
1934
|
if (typeof transport.flushToolResults === "function") {
|
|
1415
1935
|
transport.flushToolResults({ messages, collected: collectedResults });
|
|
1416
1936
|
}
|
|
1937
|
+
|
|
1938
|
+
if (deferredAskUser) {
|
|
1939
|
+
return {
|
|
1940
|
+
text: aggregated,
|
|
1941
|
+
streamed,
|
|
1942
|
+
toolCallsExecuted,
|
|
1943
|
+
messages,
|
|
1944
|
+
usage,
|
|
1945
|
+
executionState,
|
|
1946
|
+
waitingUserInteraction: true,
|
|
1947
|
+
interactionId: deferredAskUser.interactionId || "",
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
if (hasPendingUserInteraction(executionState)) {
|
|
1952
|
+
// Checkpoint approval synced from plan_graph — pause for TUI.
|
|
1953
|
+
return {
|
|
1954
|
+
text: aggregated,
|
|
1955
|
+
streamed,
|
|
1956
|
+
toolCallsExecuted,
|
|
1957
|
+
messages,
|
|
1958
|
+
usage,
|
|
1959
|
+
executionState,
|
|
1960
|
+
waitingUserInteraction: true,
|
|
1961
|
+
interactionId: (getPendingUserInteraction(executionState) || {}).id || "",
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1417
1964
|
}
|
|
1418
1965
|
}
|
|
1419
1966
|
|
|
1967
|
+
function appendAnswerToolResult(messages = [], resume = null, answer = {}) {
|
|
1968
|
+
const call = resume && resume.call ? resume.call : null;
|
|
1969
|
+
if (!call || !call.source) return { ok: false, error: "missing deferred tool call" };
|
|
1970
|
+
const transportName = String(resume.transport || "openai-chat");
|
|
1971
|
+
const content = clipText(toJsonString(answer), 12000);
|
|
1972
|
+
if (transportName === "anthropic-messages") {
|
|
1973
|
+
messages.push({
|
|
1974
|
+
role: "user",
|
|
1975
|
+
content: [{
|
|
1976
|
+
type: "tool_result",
|
|
1977
|
+
tool_use_id: String(call.source.id || resume.toolCallId || ""),
|
|
1978
|
+
content,
|
|
1979
|
+
is_error: false,
|
|
1980
|
+
}],
|
|
1981
|
+
});
|
|
1982
|
+
} else {
|
|
1983
|
+
messages.push({
|
|
1984
|
+
role: "tool",
|
|
1985
|
+
tool_call_id: String(call.source.id || resume.toolCallId || ""),
|
|
1986
|
+
content,
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
return { ok: true };
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1420
1992
|
async function runNativeAgentTask({
|
|
1421
1993
|
workspaceRoot = process.cwd(),
|
|
1422
1994
|
prompt = "",
|
|
1423
1995
|
systemPrompt = "",
|
|
1996
|
+
systemBlocks = null,
|
|
1424
1997
|
provider = "",
|
|
1425
1998
|
model = "",
|
|
1426
1999
|
messages = [],
|
|
1427
2000
|
sessionId = "",
|
|
1428
|
-
timeoutMs =
|
|
2001
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1429
2002
|
onStreamDelta = null,
|
|
1430
2003
|
onThinkingDelta = null,
|
|
1431
2004
|
onPhase = null,
|
|
1432
2005
|
onToolEvent = null,
|
|
2006
|
+
onArtifactPersisted = null,
|
|
1433
2007
|
signal = null,
|
|
2008
|
+
executionState = null,
|
|
2009
|
+
resume = false,
|
|
1434
2010
|
} = {}) {
|
|
1435
2011
|
const guards = createGuards({ signal, timeoutMs });
|
|
1436
2012
|
const nextSessionId = String(sessionId || "").trim() || `native-${randomUUID()}`;
|
|
@@ -1450,7 +2026,7 @@ async function runNativeAgentTask({
|
|
|
1450
2026
|
try {
|
|
1451
2027
|
guards.ensureActive();
|
|
1452
2028
|
|
|
1453
|
-
if (!promptText) {
|
|
2029
|
+
if (!resume && !promptText) {
|
|
1454
2030
|
return {
|
|
1455
2031
|
ok: false,
|
|
1456
2032
|
error: "empty task",
|
|
@@ -1488,8 +2064,9 @@ async function runNativeAgentTask({
|
|
|
1488
2064
|
const runResult = await runNativeLoop({
|
|
1489
2065
|
transport,
|
|
1490
2066
|
workspaceRoot,
|
|
1491
|
-
prompt: promptText,
|
|
2067
|
+
prompt: resume ? "" : promptText,
|
|
1492
2068
|
systemPrompt,
|
|
2069
|
+
systemBlocks,
|
|
1493
2070
|
historyMessages: messages,
|
|
1494
2071
|
model: runtime.model,
|
|
1495
2072
|
baseUrl: runtime.baseUrl,
|
|
@@ -1500,8 +2077,12 @@ async function runNativeAgentTask({
|
|
|
1500
2077
|
onThinkingDelta,
|
|
1501
2078
|
onPhase,
|
|
1502
2079
|
onToolEvent,
|
|
2080
|
+
onArtifactPersisted,
|
|
2081
|
+
sessionId: nextSessionId,
|
|
1503
2082
|
signal,
|
|
1504
2083
|
guards,
|
|
2084
|
+
executionState,
|
|
2085
|
+
resume: Boolean(resume),
|
|
1505
2086
|
});
|
|
1506
2087
|
|
|
1507
2088
|
const outputText = String(runResult.text || "").trim() || (
|
|
@@ -1531,26 +2112,36 @@ async function runNativeAgentTask({
|
|
|
1531
2112
|
messages: cloneMessageList(runResult.messages),
|
|
1532
2113
|
sessionId: nextSessionId,
|
|
1533
2114
|
usage,
|
|
2115
|
+
executionState: runResult.executionState || executionState || null,
|
|
1534
2116
|
// The loop marks streamed=true whenever it receives a stream callback;
|
|
1535
2117
|
// only report it when the caller actually registered one.
|
|
1536
2118
|
streamed: Boolean(runResult.streamed) && typeof onStreamDelta === "function",
|
|
2119
|
+
waitingUserInteraction: Boolean(runResult.waitingUserInteraction),
|
|
2120
|
+
interactionId: runResult.interactionId || "",
|
|
1537
2121
|
};
|
|
1538
2122
|
} catch (err) {
|
|
1539
2123
|
const message = err && err.message ? err.message : "native runner failed";
|
|
2124
|
+
if (executionState && typeof executionState === "object") {
|
|
2125
|
+
clearUserPrompts(executionState);
|
|
2126
|
+
}
|
|
1540
2127
|
return {
|
|
1541
2128
|
ok: false,
|
|
1542
2129
|
error: message,
|
|
1543
2130
|
output: partialOutput.trim(),
|
|
1544
2131
|
sessionId: nextSessionId,
|
|
1545
2132
|
streamed: false,
|
|
2133
|
+
executionState: executionState || null,
|
|
1546
2134
|
};
|
|
1547
2135
|
}
|
|
1548
2136
|
}
|
|
1549
2137
|
|
|
1550
2138
|
module.exports = {
|
|
1551
2139
|
runNativeAgentTask,
|
|
2140
|
+
appendAnswerToolResult,
|
|
1552
2141
|
resolveRuntimeConfig,
|
|
1553
2142
|
resolveCompletionUrl,
|
|
1554
2143
|
resolveAnthropicMessagesUrl,
|
|
1555
2144
|
resolveTransport,
|
|
2145
|
+
buildCoreToolSpecs,
|
|
2146
|
+
buildAnthropicToolSpecs,
|
|
1556
2147
|
};
|