@axiom-lattice/core 3.0.3 → 3.0.5
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/index.d.mts +143 -100
- package/dist/index.d.ts +143 -100
- package/dist/index.js +771 -400
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +639 -269
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9394,8 +9394,11 @@ Please select a valid tool from the list above.`
|
|
|
9394
9394
|
* The only place to access request.tools (all available tools).
|
|
9395
9395
|
* Identifies unknown tools and stores error info in metadata.
|
|
9396
9396
|
*
|
|
9397
|
-
* Key:
|
|
9398
|
-
*
|
|
9397
|
+
* Key: Strip valid tool_calls and only keep unknown ones in the returned
|
|
9398
|
+
* AIMessage. Valid calls must NOT be preserved: afterModel jumps to "model"
|
|
9399
|
+
* and skips ToolNode, so a preserved valid call would have no ToolMessage
|
|
9400
|
+
* and the next model call would be rejected with a 400 dangling tool_calls
|
|
9401
|
+
* error. Stripped valid calls are re-issued by the model in the next round.
|
|
9399
9402
|
*/
|
|
9400
9403
|
wrapModelCall: async (request, handler) => {
|
|
9401
9404
|
const availableTools = request.tools || [];
|
|
@@ -9425,10 +9428,11 @@ Please select a valid tool from the list above.`
|
|
|
9425
9428
|
toolCallId: toolCall.id,
|
|
9426
9429
|
errorMessage: errorMessageTemplate(toolCall.name, availableToolNames)
|
|
9427
9430
|
}));
|
|
9431
|
+
const unknownToolIds = new Set(unknownToolCalls.map((toolCall) => toolCall.id));
|
|
9432
|
+
const remainingToolCalls = aiResponse.tool_calls.filter((toolCall) => unknownToolIds.has(toolCall.id));
|
|
9428
9433
|
const modifiedResponse = new AIMessage({
|
|
9429
9434
|
content: aiResponse.content,
|
|
9430
|
-
tool_calls:
|
|
9431
|
-
// Key: preserve all tool_calls, don't delete unknown
|
|
9435
|
+
tool_calls: remainingToolCalls,
|
|
9432
9436
|
response_metadata: {
|
|
9433
9437
|
...aiResponse.response_metadata,
|
|
9434
9438
|
_unknownToolErrors: unknownToolErrors
|
|
@@ -9481,6 +9485,68 @@ Please select a valid tool from the list above.`
|
|
|
9481
9485
|
});
|
|
9482
9486
|
}
|
|
9483
9487
|
|
|
9488
|
+
// src/deep_agent_new/middleware/patch_tool_calls.ts
|
|
9489
|
+
import {
|
|
9490
|
+
createMiddleware as createMiddleware9,
|
|
9491
|
+
ToolMessage as ToolMessage3,
|
|
9492
|
+
AIMessage as AIMessage2
|
|
9493
|
+
} from "langchain";
|
|
9494
|
+
function createPatchToolCallsMiddleware() {
|
|
9495
|
+
return createMiddleware9({
|
|
9496
|
+
name: "patchToolCallsMiddleware",
|
|
9497
|
+
beforeAgent: async (state) => {
|
|
9498
|
+
const messages = state.messages;
|
|
9499
|
+
if (!messages || messages.length === 0) {
|
|
9500
|
+
return;
|
|
9501
|
+
}
|
|
9502
|
+
const replacements = [];
|
|
9503
|
+
for (let i = 0; i < messages.length; i++) {
|
|
9504
|
+
const msg = messages[i];
|
|
9505
|
+
if (AIMessage2.isInstance(msg) && msg.tool_calls != null) {
|
|
9506
|
+
const respondedIds = /* @__PURE__ */ new Set();
|
|
9507
|
+
for (const toolCall of msg.tool_calls) {
|
|
9508
|
+
if (!toolCall.id) continue;
|
|
9509
|
+
const correspondingToolMsg = messages.slice(i).find(
|
|
9510
|
+
(m) => ToolMessage3.isInstance(m) && m.tool_call_id === toolCall.id
|
|
9511
|
+
);
|
|
9512
|
+
if (correspondingToolMsg) {
|
|
9513
|
+
respondedIds.add(toolCall.id);
|
|
9514
|
+
}
|
|
9515
|
+
}
|
|
9516
|
+
const remainingToolCalls = msg.tool_calls.filter(
|
|
9517
|
+
(toolCall) => toolCall.id && respondedIds.has(toolCall.id)
|
|
9518
|
+
);
|
|
9519
|
+
if (remainingToolCalls.length === msg.tool_calls.length) {
|
|
9520
|
+
continue;
|
|
9521
|
+
}
|
|
9522
|
+
const additionalKwargs = { ...msg.additional_kwargs };
|
|
9523
|
+
delete additionalKwargs.tool_calls;
|
|
9524
|
+
if (!msg.id) continue;
|
|
9525
|
+
replacements.push(
|
|
9526
|
+
new AIMessage2({
|
|
9527
|
+
id: msg.id,
|
|
9528
|
+
content: msg.content,
|
|
9529
|
+
name: msg.name,
|
|
9530
|
+
tool_calls: remainingToolCalls,
|
|
9531
|
+
additional_kwargs: additionalKwargs,
|
|
9532
|
+
response_metadata: msg.response_metadata
|
|
9533
|
+
})
|
|
9534
|
+
);
|
|
9535
|
+
}
|
|
9536
|
+
}
|
|
9537
|
+
if (replacements.length === 0) {
|
|
9538
|
+
return;
|
|
9539
|
+
}
|
|
9540
|
+
return {
|
|
9541
|
+
messages: replacements
|
|
9542
|
+
};
|
|
9543
|
+
}
|
|
9544
|
+
});
|
|
9545
|
+
}
|
|
9546
|
+
|
|
9547
|
+
// src/agent_lattice/builders/commonMiddleware.ts
|
|
9548
|
+
import { summarizationMiddleware } from "langchain";
|
|
9549
|
+
|
|
9484
9550
|
// src/plugin/metaSerializer.ts
|
|
9485
9551
|
function tryExtractTools(plugin) {
|
|
9486
9552
|
if (!plugin.middleware) return [];
|
|
@@ -10717,6 +10783,12 @@ actually achieve, not just what to build:
|
|
|
10717
10783
|
|
|
10718
10784
|
Record the goal model in the parent task's description ([[task-tracking]]).
|
|
10719
10785
|
|
|
10786
|
+
**HARD RULE \u2014 never guess the target.** If the goal, expected output,
|
|
10787
|
+
consumer, or usable state is unclear at ANY point before writing test
|
|
10788
|
+
cases, you MUST ask the user via ask_user_to_clarify \u2014 never proceed
|
|
10789
|
+
with an assumed expectation. A test case written against a guessed
|
|
10790
|
+
expectation validates the wrong thing. When in doubt, ask.
|
|
10791
|
+
|
|
10720
10792
|
## Goal-Driven Validation (apply to EVERY sub-skill workflow)
|
|
10721
10793
|
|
|
10722
10794
|
The agent evaluates goal achievement ITSELF via multi-dimensional test
|
|
@@ -10802,16 +10874,29 @@ Do NOT use reviewer as:
|
|
|
10802
10874
|
verification. If findings show config errors, fix and re-check.`,
|
|
10803
10875
|
"task-tracking": `---
|
|
10804
10876
|
name: task-tracking
|
|
10805
|
-
description: Manage persistent tasks
|
|
10806
|
-
|
|
10807
|
-
|
|
10877
|
+
description: Manage persistent tasks with manage_task. Universal rule:
|
|
10878
|
+
once the goal is clear and you know what to do, create the task FIRST
|
|
10879
|
+
then execute. Track parent/subtasks, update status to reflect reality,
|
|
10880
|
+
resume interrupted work. Applies to ANY multi-step agent work \u2014 not
|
|
10881
|
+
just agent building.
|
|
10808
10882
|
metadata:
|
|
10809
10883
|
domain: agent-building
|
|
10810
10884
|
verified: unverified
|
|
10811
10885
|
---
|
|
10812
10886
|
# Task Tracking \u2014 manage_task for Agent Workflows
|
|
10813
10887
|
|
|
10814
|
-
|
|
10888
|
+
**Task management is the ongoing record of a goal and its acceptance
|
|
10889
|
+
criteria** \u2014 it answers at any moment: what are we achieving, and what
|
|
10890
|
+
does "done" look like. Create a task when the goal is clear; keep its
|
|
10891
|
+
Objective and Acceptance Criteria current as work proceeds; change
|
|
10892
|
+
status only when the criteria are actually met.
|
|
10893
|
+
|
|
10894
|
+
**Universal principle**: whenever the goal is understood and the work
|
|
10895
|
+
is about to start, create a task BEFORE executing. If you can write an
|
|
10896
|
+
Objective and Acceptance Criteria, it deserves a task. This is not
|
|
10897
|
+
optional and not limited to agent-building \u2014 it applies to any
|
|
10898
|
+
multi-step work (learning, building, modifying skills, fixing, anything
|
|
10899
|
+
with a clear goal).
|
|
10815
10900
|
|
|
10816
10901
|
## When to create (and when NOT)
|
|
10817
10902
|
|
|
@@ -10828,13 +10913,29 @@ Do NOT create tasks for:
|
|
|
10828
10913
|
|
|
10829
10914
|
## Setup
|
|
10830
10915
|
|
|
10916
|
+
**A task is a living record of the GOAL + ACCEPTANCE CRITERIA** \u2014 not a
|
|
10917
|
+
todo label. Every task's description must carry:
|
|
10918
|
+
|
|
10919
|
+
- **Objective** \u2014 one measurable sentence: what result to achieve
|
|
10920
|
+
- **Acceptance Criteria** \u2014 checkboxes that define "done": when ALL
|
|
10921
|
+
are checked, the task is verifiably complete
|
|
10922
|
+
|
|
10923
|
+
The task is created when the goal is confirmed, and its description is
|
|
10924
|
+
CONTINUALLY UPDATED as the work progresses (spec evolves, criteria are
|
|
10925
|
+
met, new criteria emerge). Status changes only when the criteria are
|
|
10926
|
+
truly met \u2014 never as a workaround.
|
|
10927
|
+
|
|
10831
10928
|
- **Create the parent task when the scope is confirmed** \u2014 before
|
|
10832
10929
|
starting the first real work phase (probe/design/build):
|
|
10833
|
-
\`manage_task create(title: <goal>, description: <
|
|
10930
|
+
\`manage_task create(title: <goal>, description: <Objective + Acceptance Criteria>, ownerType: "agent")\`
|
|
10834
10931
|
Record the returned parent task id.
|
|
10835
10932
|
- **Create a subtask per phase** as you start each phase (probe /
|
|
10836
10933
|
design / build / eval / retro):
|
|
10837
|
-
\`manage_task create(title: <phase>, parentId: <parent>, ownerType: "agent")\`
|
|
10934
|
+
\`manage_task create(title: <phase>, description: <Objective + Acceptance Criteria>, parentId: <parent>, ownerType: "agent")\`
|
|
10935
|
+
- **Update the description as work proceeds**: append progress, mark
|
|
10936
|
+
criteria \`[x]\`, revise criteria when the goal model/spec changes.
|
|
10937
|
+
The task tracks the target and its acceptance \u2014 read it to know what
|
|
10938
|
+
"done" means, keep it current so it always reflects reality.
|
|
10838
10939
|
|
|
10839
10940
|
## Status discipline \u2014 MANDATORY
|
|
10840
10941
|
|
|
@@ -11023,6 +11124,10 @@ Always set metadata on agent creation. At minimum:
|
|
|
11023
11124
|
- verified: "unverified" (upgraded after eval passes)
|
|
11024
11125
|
- version: "1.0" (bump on each update_agent)
|
|
11025
11126
|
- source: the material name or "user-description"
|
|
11127
|
+
- role: "orchestrator" | "sub-agent" \u2014 set when the agent is part of a
|
|
11128
|
+
parent+subAgents structure (role clarity, User Interaction Rules).
|
|
11129
|
+
The eval project is NOT recorded \u2014 it is derived by naming convention
|
|
11130
|
+
(eval-{agent-id}, see [[eval-verify]] Setup).
|
|
11026
11131
|
When trust upgrades, update both the skill's verified frontmatter and
|
|
11027
11132
|
the agent's metadata.verified \u2014 they must stay in sync.
|
|
11028
11133
|
|
|
@@ -11138,16 +11243,32 @@ description: Run agent evaluations, interpret results, fix failures, and
|
|
|
11138
11243
|
metadata:
|
|
11139
11244
|
domain: agent-building
|
|
11140
11245
|
verified: unverified
|
|
11246
|
+
subSkills:
|
|
11247
|
+
- eval-design-tests
|
|
11248
|
+
- eval-run-and-govern
|
|
11141
11249
|
---
|
|
11142
11250
|
# Eval Verify \u2014 Run Evaluations and Upgrade Trust
|
|
11143
11251
|
|
|
11144
11252
|
## Setup
|
|
11145
11253
|
|
|
11146
11254
|
0. Load [[eval-design-tests]] for case design guidance
|
|
11147
|
-
1.
|
|
11148
|
-
|
|
11255
|
+
1. **One eval project per agent**, named \`eval-{agent-id}\`:
|
|
11256
|
+
\`read_eval list_projects\` \u2192 find "eval-{agent-id}"
|
|
11257
|
+
Exists \u2192 reuse projectId. New \u2192 create_project(name: "eval-{agent-id}")
|
|
11258
|
+
- The agent-id is the eval project's subject. Observability: from an
|
|
11259
|
+
agent's id you can find its eval project by naming convention.
|
|
11260
|
+
- Orchestrator + subAgents \u2192 one eval project per sub-agent
|
|
11261
|
+
(eval-{sub-agent-id}) PLUS one integration eval project for the
|
|
11262
|
+
parent (eval-{parent-id}) \u2014 see Layered verification below.
|
|
11149
11263
|
2. \`manage_eval create_case(suiteId, ...)\` per sample or scenario
|
|
11150
11264
|
Required: inputMessage, steps=[{agent_id}], outputType, contentAssertion
|
|
11265
|
+
**contentAssertion MUST come from the confirmed expected output spec**
|
|
11266
|
+
(learn-capability Phase 2.6) \u2014 never invent expectations at
|
|
11267
|
+
case-writing time. If a needed expectation is not in the spec, extend
|
|
11268
|
+
the spec with user confirmation first.
|
|
11269
|
+
**HARD RULE**: if the target/expected output is unclear at this
|
|
11270
|
+
point, STOP and ask the user (ask_user_to_clarify) \u2014 do not write a
|
|
11271
|
+
case with a guessed expectation.
|
|
11151
11272
|
|
|
11152
11273
|
## Suites per skill, by source
|
|
11153
11274
|
|
|
@@ -11491,10 +11612,20 @@ async function resolveConnections(type, connections, tenantId2) {
|
|
|
11491
11612
|
throw err;
|
|
11492
11613
|
}
|
|
11493
11614
|
}
|
|
11494
|
-
async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised, tenantId2) {
|
|
11615
|
+
async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised, tenantId2, model) {
|
|
11495
11616
|
const middlewares = [];
|
|
11496
11617
|
middlewares.push(createUnknownToolHandlerMiddleware());
|
|
11497
11618
|
middlewares.push(createModelSelectorMiddleware());
|
|
11619
|
+
middlewares.push(createPatchToolCallsMiddleware());
|
|
11620
|
+
if (model) {
|
|
11621
|
+
middlewares.push(
|
|
11622
|
+
summarizationMiddleware({
|
|
11623
|
+
model,
|
|
11624
|
+
trigger: { tokens: 17e4 },
|
|
11625
|
+
keep: { messages: 6 }
|
|
11626
|
+
})
|
|
11627
|
+
);
|
|
11628
|
+
}
|
|
11498
11629
|
const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
|
|
11499
11630
|
const clawConfig = middlewareConfigs.find((m) => m.type === "claw");
|
|
11500
11631
|
const needsFilesystemBackend = filesystemConfig?.enabled || clawConfig?.enabled;
|
|
@@ -11826,7 +11957,7 @@ var ReActAgentGraphBuilder = class {
|
|
|
11826
11957
|
const stateSchema2 = createReactAgentSchema(params.stateSchema);
|
|
11827
11958
|
const middlewareConfigs = params.middleware || [];
|
|
11828
11959
|
const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
|
|
11829
|
-
const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, void 0, params.tenantId);
|
|
11960
|
+
const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, void 0, params.tenantId, params.model);
|
|
11830
11961
|
return createAgent({
|
|
11831
11962
|
model: params.model,
|
|
11832
11963
|
tools,
|
|
@@ -11844,17 +11975,16 @@ var ReActAgentGraphBuilder = class {
|
|
|
11844
11975
|
import {
|
|
11845
11976
|
createAgent as createAgent3,
|
|
11846
11977
|
humanInTheLoopMiddleware as humanInTheLoopMiddleware2,
|
|
11847
|
-
anthropicPromptCachingMiddleware
|
|
11848
|
-
summarizationMiddleware
|
|
11978
|
+
anthropicPromptCachingMiddleware
|
|
11849
11979
|
} from "langchain";
|
|
11850
11980
|
|
|
11851
11981
|
// src/deep_agent_new/middleware/subagents.ts
|
|
11852
11982
|
import { z as z42 } from "zod/v3";
|
|
11853
11983
|
import {
|
|
11854
|
-
createMiddleware as
|
|
11984
|
+
createMiddleware as createMiddleware11,
|
|
11855
11985
|
createAgent as createAgent2,
|
|
11856
11986
|
tool as tool40,
|
|
11857
|
-
ToolMessage as
|
|
11987
|
+
ToolMessage as ToolMessage4,
|
|
11858
11988
|
humanInTheLoopMiddleware
|
|
11859
11989
|
} from "langchain";
|
|
11860
11990
|
import { Command as Command3, getCurrentTaskInput as getCurrentTaskInput2, GraphInterrupt as GraphInterrupt3 } from "@langchain/langgraph";
|
|
@@ -13800,7 +13930,7 @@ var AgentInstanceManager = class _AgentInstanceManager {
|
|
|
13800
13930
|
var agentInstanceManager = AgentInstanceManager.getInstance();
|
|
13801
13931
|
|
|
13802
13932
|
// src/middlewares/taskMiddleware.ts
|
|
13803
|
-
import { createMiddleware as
|
|
13933
|
+
import { createMiddleware as createMiddleware10, tool as tool39 } from "langchain";
|
|
13804
13934
|
import { z as z41 } from "zod";
|
|
13805
13935
|
import { GraphInterrupt as GraphInterrupt2, interrupt as interrupt3 } from "@langchain/langgraph";
|
|
13806
13936
|
function getRunConfig(config) {
|
|
@@ -14108,26 +14238,37 @@ function createTaskMiddleware() {
|
|
|
14108
14238
|
});
|
|
14109
14239
|
}
|
|
14110
14240
|
};
|
|
14111
|
-
return
|
|
14241
|
+
return createMiddleware10({
|
|
14112
14242
|
name: "TaskMiddleware",
|
|
14113
14243
|
contextSchema,
|
|
14114
14244
|
wrapModelCall: async (request, handler) => {
|
|
14115
14245
|
const taskPrompt = `## Task Management
|
|
14116
14246
|
|
|
14117
|
-
You
|
|
14247
|
+
You have the \`manage_task\` tool to track work. Task management is the
|
|
14248
|
+
ongoing record of a GOAL and its ACCEPTANCE CRITERIA.
|
|
14118
14249
|
|
|
14119
|
-
### When to create a task
|
|
14250
|
+
### When to create a task (universal rule)
|
|
14251
|
+
- The goal is clear and you are about to start real work \u2192 create the
|
|
14252
|
+
parent task FIRST (with Objective + Acceptance Criteria in the
|
|
14253
|
+
description), then execute. This is a core duty, not optional.
|
|
14120
14254
|
- The user explicitly asks you to track, manage, or follow up on work
|
|
14121
14255
|
- The work spans multiple sessions or might need resumption later
|
|
14122
14256
|
- The user needs to review or approve output before it is considered done
|
|
14123
14257
|
- There are multiple independent work items the user wants visibility into
|
|
14124
14258
|
|
|
14125
14259
|
### When NOT to create a task
|
|
14260
|
+
- Goal not yet clear (still clarifying) \u2014 clarify first, then create
|
|
14126
14261
|
- One-shot lookups or simple Q&A ("what is X?", "search for Y")
|
|
14127
14262
|
- Internal exploration steps you take to understand the problem (use \`write_todos\` for your execution plan instead)
|
|
14128
14263
|
- Trivial single-step actions that complete in the same turn
|
|
14129
14264
|
- Conversational or informational requests with no deliverable
|
|
14130
14265
|
|
|
14266
|
+
### Keep the task current
|
|
14267
|
+
A task is the living record of the goal + its acceptance criteria.
|
|
14268
|
+
Update the description as work proceeds: check off criteria as met,
|
|
14269
|
+
revise criteria when scope changes, append progress. Status changes
|
|
14270
|
+
only when the criteria are truly met.
|
|
14271
|
+
|
|
14131
14272
|
### Ownership defaults
|
|
14132
14273
|
- No params: ownerType defaults to "user" with current user's ID
|
|
14133
14274
|
- ownerType="agent": auto-fills ownerId from current agent (subtask for yourself)
|
|
@@ -14181,6 +14322,29 @@ var taskPlugin = {
|
|
|
14181
14322
|
skills: {
|
|
14182
14323
|
"task-definition": `## Using manage_task
|
|
14183
14324
|
|
|
14325
|
+
### When to create a task (universal rule)
|
|
14326
|
+
|
|
14327
|
+
Create a task BEFORE executing whenever the goal is clear and you know
|
|
14328
|
+
what to do \u2014 not just for long or complex work:
|
|
14329
|
+
|
|
14330
|
+
- Goal is understood and you are about to start real work \u2192 create the
|
|
14331
|
+
parent task FIRST, then execute. The task tracks the work.
|
|
14332
|
+
- Goal is NOT yet clear (still clarifying, gathering requirements) \u2192
|
|
14333
|
+
do NOT create a task yet. Clarify first, create the task once scope
|
|
14334
|
+
is defined.
|
|
14335
|
+
- One-shot lookups, trivial single-step actions, or internal reasoning
|
|
14336
|
+
\u2192 no task needed.
|
|
14337
|
+
|
|
14338
|
+
Rule of thumb: if you can write an Objective and Acceptance Criteria
|
|
14339
|
+
for it, create the task before doing it. Work without a task = work
|
|
14340
|
+
without a contract.
|
|
14341
|
+
|
|
14342
|
+
**A task is the living record of the goal + its acceptance criteria.**
|
|
14343
|
+
Keep the description current as work proceeds: update the Objective
|
|
14344
|
+
when the target evolves, check off criteria as they are met, revise
|
|
14345
|
+
criteria when scope changes. Reading the task always tells you what
|
|
14346
|
+
"done" means; an outdated task is a broken contract.
|
|
14347
|
+
|
|
14184
14348
|
### Task description format
|
|
14185
14349
|
|
|
14186
14350
|
When creating a task with manage_task, write the description in this Markdown structure:
|
|
@@ -14382,7 +14546,7 @@ function returnCommandWithStateUpdate(result, toolCallId) {
|
|
|
14382
14546
|
update: {
|
|
14383
14547
|
...stateUpdate,
|
|
14384
14548
|
messages: [
|
|
14385
|
-
new
|
|
14549
|
+
new ToolMessage4({
|
|
14386
14550
|
content: lastMessage?.content || "Task Failed to complete",
|
|
14387
14551
|
tool_call_id: toolCallId,
|
|
14388
14552
|
name: "task"
|
|
@@ -14575,7 +14739,7 @@ You are executing a persistent task (ID: ${input.taskId}). Use manage_task.updat
|
|
|
14575
14739
|
return new Command3({
|
|
14576
14740
|
update: {
|
|
14577
14741
|
messages: [
|
|
14578
|
-
new
|
|
14742
|
+
new ToolMessage4({
|
|
14579
14743
|
content: `Async task started: ${subagent_thread_id}
|
|
14580
14744
|
${description}
|
|
14581
14745
|
The result will be delivered as a notification when complete. Do not poll.`,
|
|
@@ -14609,7 +14773,7 @@ The result will be delivered as a notification when complete. Do not poll.`,
|
|
|
14609
14773
|
return new Command3({
|
|
14610
14774
|
update: {
|
|
14611
14775
|
messages: [
|
|
14612
|
-
new
|
|
14776
|
+
new ToolMessage4({
|
|
14613
14777
|
content: error instanceof Error ? error.message : "Task Failed to complete",
|
|
14614
14778
|
tool_call_id: config.toolCall.id,
|
|
14615
14779
|
name: "task"
|
|
@@ -14846,7 +15010,7 @@ function createSubAgentMiddleware(options) {
|
|
|
14846
15010
|
);
|
|
14847
15011
|
}
|
|
14848
15012
|
const effectiveSystemPrompt = allowAsync ? systemPrompt + getAsyncPromptText() : systemPrompt;
|
|
14849
|
-
return
|
|
15013
|
+
return createMiddleware11({
|
|
14850
15014
|
name: "subAgentMiddleware",
|
|
14851
15015
|
tools: allTools,
|
|
14852
15016
|
wrapModelCall: async (request, handler) => {
|
|
@@ -14865,53 +15029,6 @@ ${effectiveSystemPrompt}` : effectiveSystemPrompt;
|
|
|
14865
15029
|
});
|
|
14866
15030
|
}
|
|
14867
15031
|
|
|
14868
|
-
// src/deep_agent_new/middleware/patch_tool_calls.ts
|
|
14869
|
-
import {
|
|
14870
|
-
createMiddleware as createMiddleware11,
|
|
14871
|
-
ToolMessage as ToolMessage4,
|
|
14872
|
-
AIMessage as AIMessage2
|
|
14873
|
-
} from "langchain";
|
|
14874
|
-
function createPatchToolCallsMiddleware() {
|
|
14875
|
-
return createMiddleware11({
|
|
14876
|
-
name: "patchToolCallsMiddleware",
|
|
14877
|
-
beforeAgent: async (state) => {
|
|
14878
|
-
const messages = state.messages;
|
|
14879
|
-
if (!messages || messages.length === 0) {
|
|
14880
|
-
return;
|
|
14881
|
-
}
|
|
14882
|
-
const patchedMessages = [];
|
|
14883
|
-
for (let i = 0; i < messages.length; i++) {
|
|
14884
|
-
const msg = messages[i];
|
|
14885
|
-
patchedMessages.push(msg);
|
|
14886
|
-
if (AIMessage2.isInstance(msg) && msg.tool_calls != null) {
|
|
14887
|
-
for (const toolCall of msg.tool_calls) {
|
|
14888
|
-
const correspondingToolMsg = messages.slice(i).find(
|
|
14889
|
-
(m) => ToolMessage4.isInstance(m) && m.tool_call_id === toolCall.id
|
|
14890
|
-
);
|
|
14891
|
-
if (!correspondingToolMsg) {
|
|
14892
|
-
const toolMsg = `Tool call ${toolCall.name} with id ${toolCall.id} was cancelled - another message came in before it could be completed.`;
|
|
14893
|
-
patchedMessages.push(
|
|
14894
|
-
new ToolMessage4({
|
|
14895
|
-
content: toolMsg,
|
|
14896
|
-
name: toolCall.name,
|
|
14897
|
-
tool_call_id: toolCall.id
|
|
14898
|
-
})
|
|
14899
|
-
);
|
|
14900
|
-
}
|
|
14901
|
-
}
|
|
14902
|
-
}
|
|
14903
|
-
}
|
|
14904
|
-
if (patchedMessages.length === messages.length) {
|
|
14905
|
-
return;
|
|
14906
|
-
}
|
|
14907
|
-
return {
|
|
14908
|
-
messages: patchedMessages.slice(messages.length)
|
|
14909
|
-
// only the new ToolMessage patches
|
|
14910
|
-
};
|
|
14911
|
-
}
|
|
14912
|
-
});
|
|
14913
|
-
}
|
|
14914
|
-
|
|
14915
15032
|
// src/deep_agent_new/middleware/date.ts
|
|
14916
15033
|
import { createMiddleware as createMiddleware12, tool as tool41 } from "langchain";
|
|
14917
15034
|
import { z as z43 } from "zod";
|
|
@@ -17824,36 +17941,20 @@ ${BASE_PROMPT}` : BASE_PROMPT;
|
|
|
17824
17941
|
createFilesystemMiddleware({
|
|
17825
17942
|
backend: filesystemBackend
|
|
17826
17943
|
}),
|
|
17827
|
-
// Subagent middleware: Automatic conversation summarization when token limits are approached
|
|
17828
|
-
summarizationMiddleware({
|
|
17829
|
-
model,
|
|
17830
|
-
trigger: { tokens: 17e4 },
|
|
17831
|
-
keep: { messages: 6 }
|
|
17832
|
-
}),
|
|
17833
17944
|
// Subagent middleware: Anthropic prompt caching for improved performance
|
|
17834
17945
|
anthropicPromptCachingMiddleware({
|
|
17835
17946
|
unsupportedModelBehavior: "ignore"
|
|
17836
17947
|
}),
|
|
17837
|
-
// Subagent middleware: Patches tool calls for compatibility
|
|
17838
|
-
createPatchToolCallsMiddleware(),
|
|
17839
17948
|
...customMiddleware
|
|
17840
17949
|
],
|
|
17841
17950
|
defaultInterruptOn: interruptOn,
|
|
17842
17951
|
subagents,
|
|
17843
17952
|
generalPurposeAgent: true
|
|
17844
17953
|
}),
|
|
17845
|
-
// Automatically summarizes conversation history when token limits are approached
|
|
17846
|
-
summarizationMiddleware({
|
|
17847
|
-
model,
|
|
17848
|
-
trigger: { tokens: 17e4 },
|
|
17849
|
-
keep: { messages: 6 }
|
|
17850
|
-
}),
|
|
17851
17954
|
// Enables Anthropic prompt caching for improved performance and reduced costs
|
|
17852
17955
|
anthropicPromptCachingMiddleware({
|
|
17853
17956
|
unsupportedModelBehavior: "ignore"
|
|
17854
|
-
})
|
|
17855
|
-
// Patches tool calls to ensure compatibility across different model providers
|
|
17856
|
-
createPatchToolCallsMiddleware()
|
|
17957
|
+
})
|
|
17857
17958
|
];
|
|
17858
17959
|
if (interruptOn) {
|
|
17859
17960
|
middleware.push(humanInTheLoopMiddleware2({ interruptOn }));
|
|
@@ -17908,7 +18009,7 @@ var DeepAgentGraphBuilder = class {
|
|
|
17908
18009
|
}));
|
|
17909
18010
|
const middlewareConfigs = params.middleware || [];
|
|
17910
18011
|
const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
|
|
17911
|
-
const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, true, params.tenantId);
|
|
18012
|
+
const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, true, params.tenantId, params.model);
|
|
17912
18013
|
const deepAgent = createDeepAgent({
|
|
17913
18014
|
tools,
|
|
17914
18015
|
model: params.model,
|
|
@@ -19524,7 +19625,7 @@ var TeamAgentGraphBuilder = class {
|
|
|
19524
19625
|
});
|
|
19525
19626
|
const middlewareConfigs = params.middleware || [];
|
|
19526
19627
|
let filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
|
|
19527
|
-
const middlewares = await createCommonMiddlewares(middlewareConfigs);
|
|
19628
|
+
const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, void 0, void 0, params.model);
|
|
19528
19629
|
if (!filesystemBackend) {
|
|
19529
19630
|
filesystemBackend = async (config2) => {
|
|
19530
19631
|
return new StateBackend(config2);
|
|
@@ -19941,7 +20042,7 @@ var WorkflowAgentGraphBuilder = class {
|
|
|
19941
20042
|
const checkpointer = getCheckpointSaver("default");
|
|
19942
20043
|
const tools = params.tools.map((t) => t.executor).filter(Boolean);
|
|
19943
20044
|
const middlewareConfigs = params.middleware || [];
|
|
19944
|
-
const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, false, params.tenantId);
|
|
20045
|
+
const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, false, params.tenantId, params.model);
|
|
19945
20046
|
const askMiddlewares = await createCommonMiddlewares([
|
|
19946
20047
|
{
|
|
19947
20048
|
id: "ask_user_to_clarify",
|
|
@@ -19951,7 +20052,7 @@ var WorkflowAgentGraphBuilder = class {
|
|
|
19951
20052
|
enabled: true,
|
|
19952
20053
|
config: {}
|
|
19953
20054
|
}
|
|
19954
|
-
], void 0, false);
|
|
20055
|
+
], void 0, false, void 0, params.model);
|
|
19955
20056
|
const noWrapMiddlewares = stripWrapModelCallHook(middlewares);
|
|
19956
20057
|
const noWrapAskMiddlewares = stripWrapModelCallHook(askMiddlewares);
|
|
19957
20058
|
console.log(`[WF BUILDER] building default agent | toolCount=${tools.length} | middlewareCount=${middlewares.length}`);
|
|
@@ -21628,10 +21729,19 @@ Call the \`skill\` tool with skill_name: "agent-architecture" to load the
|
|
|
21628
21729
|
authoritative workflow. Never announce that you will follow a skill \u2014
|
|
21629
21730
|
load it and follow its content. If the load fails, retry once, then report it.
|
|
21630
21731
|
|
|
21732
|
+
TASK MANAGEMENT IS A CORE DUTY, not a per-skill option. Whenever the
|
|
21733
|
+
goal is clear and you know what to do, create a task FIRST (manage_task)
|
|
21734
|
+
before executing \u2014 for any multi-step work: learning, building,
|
|
21735
|
+
modifying, fixing, anything with an Objective and Acceptance Criteria.
|
|
21736
|
+
Subtasks per work item. Status must reflect reality. See [[task-tracking]].
|
|
21737
|
+
The sub-skills below only ADD their own task details on top of this
|
|
21738
|
+
universal duty.
|
|
21739
|
+
|
|
21631
21740
|
Your sub-skills (accessible via the MOC or direct loading):
|
|
21632
21741
|
- [[learn-capability]] \u2014 Learn from any source material \u2192 skills + agents
|
|
21633
21742
|
- [[agent-build]] \u2014 Design and build single agents (REACT/DEEP_AGENT)
|
|
21634
21743
|
- [[design-workflow]] \u2014 Design workflow agents
|
|
21744
|
+
- [[eval-verify]] \u2014 Run evaluations with fix loop, hold-out, trust upgrade
|
|
21635
21745
|
- [[task-tracking]] \u2014 Manage persistent tasks (manage_task)
|
|
21636
21746
|
- [[completion-gate]] \u2014 THE rule: no agent is "done" without eval
|
|
21637
21747
|
- [[domain-moc]] \u2014 Create the domain MOC (mandatory per learning run)
|
|
@@ -21791,7 +21901,15 @@ var agentArchitectConfig = {
|
|
|
21791
21901
|
id: "task",
|
|
21792
21902
|
type: "task",
|
|
21793
21903
|
name: "Task",
|
|
21794
|
-
description: "
|
|
21904
|
+
description: "Core architect duty: once the goal is clear, create tasks to track every multi-step piece of work (learning, building, modifying, fixing). Subtasks per work item; status reflects reality.",
|
|
21905
|
+
enabled: true,
|
|
21906
|
+
config: {}
|
|
21907
|
+
},
|
|
21908
|
+
{
|
|
21909
|
+
id: "code_eval",
|
|
21910
|
+
type: "code_eval",
|
|
21911
|
+
name: "Code Evaluation",
|
|
21912
|
+
description: "Execute shell commands in the sandbox to support agent creation: unzip files, process data, run helper scripts. Distinct from the eval assessment system (run_eval).",
|
|
21795
21913
|
enabled: true,
|
|
21796
21914
|
config: {}
|
|
21797
21915
|
},
|
|
@@ -25413,6 +25531,15 @@ function parseJudgeVerdict(raw) {
|
|
|
25413
25531
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
25414
25532
|
}
|
|
25415
25533
|
}
|
|
25534
|
+
var MAX_INTERRUPT_RESUMES = 5;
|
|
25535
|
+
function resolveInterruptResponse(policy, interrupt5) {
|
|
25536
|
+
if (policy.mode === "auto-approve") return policy.value ?? "\u540C\u610F";
|
|
25537
|
+
if (policy.mode === "auto-reject") return policy.value ?? "\u62D2\u7EDD";
|
|
25538
|
+
return policy.value ?? "";
|
|
25539
|
+
}
|
|
25540
|
+
function interruptValueText(value) {
|
|
25541
|
+
return typeof value === "string" ? value : JSON.stringify(value ?? "");
|
|
25542
|
+
}
|
|
25416
25543
|
var _LatticeEval = class _LatticeEval {
|
|
25417
25544
|
constructor(config = {}) {
|
|
25418
25545
|
this.inMemoryLogs = [];
|
|
@@ -25479,7 +25606,8 @@ var _LatticeEval = class _LatticeEval {
|
|
|
25479
25606
|
return acc;
|
|
25480
25607
|
}, {});
|
|
25481
25608
|
}
|
|
25482
|
-
async executeAgentStep(step, threadId, inputMessage, files) {
|
|
25609
|
+
async executeAgentStep(step, threadId, inputMessage, files, interruptPolicy) {
|
|
25610
|
+
const hitlEvents = [];
|
|
25483
25611
|
this.log("Executing agent step", {
|
|
25484
25612
|
agent_id: step.agent_id,
|
|
25485
25613
|
thread_id: threadId,
|
|
@@ -25497,19 +25625,74 @@ var _LatticeEval = class _LatticeEval {
|
|
|
25497
25625
|
};
|
|
25498
25626
|
const agent = agentInstanceManager.getAgent(agentParams);
|
|
25499
25627
|
try {
|
|
25500
|
-
const
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25628
|
+
const stepInput = {
|
|
25629
|
+
message: step.override_message || inputMessage,
|
|
25630
|
+
files: this.buildFileEntries(files)
|
|
25631
|
+
};
|
|
25632
|
+
let result = await agent.invokeWithState({ input: stepInput });
|
|
25633
|
+
let resumeCount = 0;
|
|
25634
|
+
let pendingInterrupt;
|
|
25635
|
+
let interrupts = Array.isArray(result?.__interrupt__) ? result.__interrupt__ : [];
|
|
25636
|
+
while (interrupts.length > 0) {
|
|
25637
|
+
const interrupt5 = interrupts[0];
|
|
25638
|
+
if (!interrupt5) break;
|
|
25639
|
+
const policy = interruptPolicy;
|
|
25640
|
+
if (!policy || policy.mode === "stop" || resumeCount >= MAX_INTERRUPT_RESUMES) {
|
|
25641
|
+
pendingInterrupt = interrupt5;
|
|
25642
|
+
break;
|
|
25504
25643
|
}
|
|
25505
|
-
|
|
25644
|
+
const response = resolveInterruptResponse(policy, interrupt5);
|
|
25645
|
+
hitlEvents.push({
|
|
25646
|
+
type: "interrupt",
|
|
25647
|
+
id: interrupt5.id,
|
|
25648
|
+
value: interrupt5.value
|
|
25649
|
+
});
|
|
25650
|
+
this.log("Auto-resolving HITL interrupt", {
|
|
25651
|
+
agent_id: step.agent_id,
|
|
25652
|
+
thread_id: threadId,
|
|
25653
|
+
mode: policy.mode,
|
|
25654
|
+
interrupt_id: interrupt5.id,
|
|
25655
|
+
response,
|
|
25656
|
+
resume_count: resumeCount + 1
|
|
25657
|
+
});
|
|
25658
|
+
result = await agent.invokeWithState({ input: stepInput, command: { resume: response } });
|
|
25659
|
+
hitlEvents.push({
|
|
25660
|
+
type: "interrupt_response",
|
|
25661
|
+
id: interrupt5.id,
|
|
25662
|
+
mode: policy.mode,
|
|
25663
|
+
response
|
|
25664
|
+
});
|
|
25665
|
+
resumeCount++;
|
|
25666
|
+
interrupts = Array.isArray(result?.__interrupt__) ? result.__interrupt__ : [];
|
|
25667
|
+
}
|
|
25668
|
+
if (pendingInterrupt) {
|
|
25669
|
+
hitlEvents.push({
|
|
25670
|
+
type: "interrupt",
|
|
25671
|
+
id: pendingInterrupt.id,
|
|
25672
|
+
value: pendingInterrupt.value
|
|
25673
|
+
});
|
|
25674
|
+
this.log("Agent step interrupted by HITL (human input requested)", {
|
|
25675
|
+
agent_id: step.agent_id,
|
|
25676
|
+
thread_id: threadId,
|
|
25677
|
+
interrupt_id: pendingInterrupt.id,
|
|
25678
|
+
auto_resolved: resumeCount
|
|
25679
|
+
});
|
|
25680
|
+
} else {
|
|
25681
|
+
this.log("Agent step completed", {
|
|
25682
|
+
agent_id: step.agent_id,
|
|
25683
|
+
thread_id: threadId,
|
|
25684
|
+
response_keys: result ? Object.keys(result) : [],
|
|
25685
|
+
auto_resolved: resumeCount
|
|
25686
|
+
});
|
|
25687
|
+
}
|
|
25506
25688
|
const responseData = { success: true, ...result };
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25689
|
+
return {
|
|
25690
|
+
threadId,
|
|
25691
|
+
responseData,
|
|
25692
|
+
interrupted: pendingInterrupt ? true : void 0,
|
|
25693
|
+
interrupt: pendingInterrupt,
|
|
25694
|
+
hitlEvents
|
|
25695
|
+
};
|
|
25513
25696
|
} catch (error) {
|
|
25514
25697
|
const message = error instanceof Error ? error.message : String(error);
|
|
25515
25698
|
this.log("Agent step failed", {
|
|
@@ -25572,15 +25755,32 @@ var _LatticeEval = class _LatticeEval {
|
|
|
25572
25755
|
});
|
|
25573
25756
|
let currentThreadId = threadId;
|
|
25574
25757
|
let lastResponseData = null;
|
|
25758
|
+
let interrupt5;
|
|
25575
25759
|
for (const step of evalCase.steps) {
|
|
25576
25760
|
const result = await this.executeAgentStep(
|
|
25577
25761
|
step,
|
|
25578
25762
|
currentThreadId,
|
|
25579
25763
|
evalCase.input.message,
|
|
25580
|
-
evalCase.input.files || {}
|
|
25764
|
+
evalCase.input.files || {},
|
|
25765
|
+
evalCase.interruptPolicy
|
|
25581
25766
|
);
|
|
25582
25767
|
currentThreadId = result.threadId;
|
|
25583
25768
|
lastResponseData = result.responseData;
|
|
25769
|
+
for (const evt of result.hitlEvents) {
|
|
25770
|
+
if (evt.type === "interrupt") {
|
|
25771
|
+
this.lastMessages.push({
|
|
25772
|
+
role: "interrupt",
|
|
25773
|
+
content: `HITL \u6682\u505C\uFF1AAgent \u8BF7\u6C42\u4EBA\u5DE5\u8F93\u5165 \u2014 ${interruptValueText(evt.value)}`,
|
|
25774
|
+
id: evt.id
|
|
25775
|
+
});
|
|
25776
|
+
} else {
|
|
25777
|
+
this.lastMessages.push({
|
|
25778
|
+
role: "interrupt_response",
|
|
25779
|
+
content: `\u5DF2\u81EA\u52A8\u54CD\u5E94\uFF08\u6D4B\u8BD5\u7B56\u7565 ${evt.mode}\uFF09\uFF1A${evt.response}`,
|
|
25780
|
+
id: evt.id
|
|
25781
|
+
});
|
|
25782
|
+
}
|
|
25783
|
+
}
|
|
25584
25784
|
const existingIds = new Set(this.lastMessages.map((m) => m.id).filter(Boolean));
|
|
25585
25785
|
if (result.responseData?.messages && Array.isArray(result.responseData.messages)) {
|
|
25586
25786
|
for (const msg of result.responseData.messages) {
|
|
@@ -25602,6 +25802,13 @@ var _LatticeEval = class _LatticeEval {
|
|
|
25602
25802
|
} else {
|
|
25603
25803
|
content = String(msg.content || "");
|
|
25604
25804
|
}
|
|
25805
|
+
if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
|
|
25806
|
+
const toolCallStr = msg.tool_calls.map(
|
|
25807
|
+
(tc) => `tool_call: ${tc.name}(${JSON.stringify(tc.args ?? {})})`
|
|
25808
|
+
).join("\n");
|
|
25809
|
+
content = content ? `${content}
|
|
25810
|
+
${toolCallStr}` : toolCallStr;
|
|
25811
|
+
}
|
|
25605
25812
|
this.lastMessages.push({
|
|
25606
25813
|
role,
|
|
25607
25814
|
content,
|
|
@@ -25613,13 +25820,21 @@ var _LatticeEval = class _LatticeEval {
|
|
|
25613
25820
|
}
|
|
25614
25821
|
}
|
|
25615
25822
|
}
|
|
25823
|
+
if (result.interrupted) {
|
|
25824
|
+
interrupt5 = result.interrupt;
|
|
25825
|
+
this.log("Case paused for HITL \u2014 remaining steps skipped; judge will evaluate the pause", {
|
|
25826
|
+
case_id: evalCase.caseId,
|
|
25827
|
+
interrupt_id: interrupt5?.id
|
|
25828
|
+
});
|
|
25829
|
+
break;
|
|
25830
|
+
}
|
|
25616
25831
|
}
|
|
25617
25832
|
this.log("All agent steps completed", {
|
|
25618
25833
|
case_id: evalCase.caseId,
|
|
25619
25834
|
final_thread_id: currentThreadId,
|
|
25620
25835
|
message_count: this.lastMessages.length
|
|
25621
25836
|
});
|
|
25622
|
-
const finalOutput = this.extractFinalMessage(lastResponseData);
|
|
25837
|
+
const finalOutput = interrupt5 ? typeof interrupt5.value === "string" ? interrupt5.value : JSON.stringify(interrupt5.value ?? "") : this.extractFinalMessage(lastResponseData);
|
|
25623
25838
|
this.lastFinalOutput = finalOutput;
|
|
25624
25839
|
const trajectory = this.buildTrajectory();
|
|
25625
25840
|
this.log("Final output extracted", {
|
|
@@ -25678,6 +25893,8 @@ ${rubricsSection}
|
|
|
25678
25893
|
3. **\u8FC7\u7A0B\u6821\u9A8C**\uFF1A\u5982\u679C"\u6267\u884C\u8FC7\u7A0B"\u663E\u793A Agent \u672A\u6267\u884C\u5FC5\u8981\u7684\u4E2D\u95F4\u6B65\u9AA4\uFF08\u5982\u5E94\u8C03\u7528\u5DE5\u5177\u800C\u672A\u8C03\u7528\uFF09\uFF0C\u5373\u4F7F\u6700\u7EC8\u8F93\u51FA\u770B\u4F3C\u5408\u7406\uFF0C\u4E5F\u5E94\u5728\u5BF9\u5E94\u6307\u6807\u4E0A\u6263\u5206\u3002
|
|
25679
25894
|
4. **\u8BC1\u636E\u5BFC\u5411**\uFF1A\u5728\u7ED9\u51FA\u539F\u56E0\uFF08reason\uFF09\u65F6\uFF0C\u5FC5\u987B\u5F15\u7528\u6267\u884C\u8FC7\u7A0B\u6216\u6700\u7EC8\u8F93\u51FA\u4E2D\u7684\u5177\u4F53\u5185\u5BB9\u3002
|
|
25680
25895
|
5. **\u52A0\u6743\u8BA1\u7B97**\uFF1A\u6700\u7EC8\u5206\u6570\u4E3A\u5404\u9879\u6307\u6807\u5F97\u5206\u4E0E\u5176\u6743\u91CD\u7684\u4E58\u79EF\u4E4B\u548C\uFF080-100\u5206\u5236\uFF09\u3002
|
|
25896
|
+
6. **HITL \u4E2D\u65AD\u5224\u5B9A**\uFF1A\u5982\u679C\u6267\u884C\u8FC7\u7A0B\u4E2D\u51FA\u73B0\u300CHITL \u6682\u505C\uFF1AAgent \u8BF7\u6C42\u4EBA\u5DE5\u8F93\u5165\u300D\u6761\u76EE\uFF0C\u8BF4\u660E Agent \u5728\u7B49\u5F85\u4EBA\u5DE5\u786E\u8BA4\u3002\u8BF7\u628A\u5B83\u5F53\u4F5C\u88AB\u6D4B\u4E1A\u52A1\u884C\u4E3A\u6765\u8BC4\u5224\uFF1A\u82E5\u671F\u671B\u8F93\u51FA\u8981\u6C42\u81EA\u4E3B\u5B8C\u6210\uFF08\u5982"\u65E0\u9700\u786E\u8BA4\u81EA\u52A8\u6267\u884C"\uFF09\uFF0C\u8BF7\u6C42\u4EBA\u5DE5\u8F93\u5165\u5E94\u5224\u5931\u8D25\uFF1B\u82E5\u671F\u671B\u8F93\u51FA\u8981\u6C42\u5148\u83B7\u5F97\u786E\u8BA4\u6216\u6279\u51C6\uFF08\u5982"\u6267\u884C\u524D\u5FC5\u987B\u8BF7\u6C42\u6279\u51C6"\uFF09\uFF0C\u8BF7\u6C42\u4EBA\u5DE5\u8F93\u5165\u662F\u6B63\u786E\u884C\u4E3A\uFF0C\u5E94\u7ED3\u5408\u5176\u65F6\u673A\u4E0E\u5185\u5BB9\u5224\u5B9A\u4E3A\u901A\u8FC7\u6216\u6309\u6307\u6807\u7ED9\u5206\u3002
|
|
25897
|
+
7. **HITL \u81EA\u52A8\u54CD\u5E94\u5224\u5B9A**\uFF1A\u5982\u679C\u300CHITL \u6682\u505C\u300D\u6761\u76EE\u4E4B\u540E\u51FA\u73B0\u300C\u5DF2\u81EA\u52A8\u54CD\u5E94\uFF08\u6D4B\u8BD5\u7B56\u7565 auto-approve/auto-reject/canned-response\uFF09\u300D\u6761\u76EE\uFF0C\u8BF4\u660E\u6D4B\u8BD5\u6846\u67B6\u6CE8\u5165\u4E86\u4EBA\u5DE5\u56DE\u590D\u3001\u6D41\u7A0B\u5DF2\u7EE7\u7EED\u2014\u2014\u8BF7\u6309**\u5B8C\u6574\u6D41\u7A0B**\u8BC4\u5224\u6682\u505C\u4E4B\u540E\u7684\u884C\u4E3A\uFF08\u5982\u6279\u51C6\u540E\u662F\u5426\u6B63\u786E\u6267\u884C\u4E86\u64CD\u4F5C\uFF09\uFF0C\u5E76\u6838\u5BF9\u81EA\u52A8\u54CD\u5E94\u5185\u5BB9\u662F\u5426\u7B26\u5408\u4EBA\u5DE5\u56DE\u590D\u7684\u5408\u7406\u9884\u671F\u3002
|
|
25681
25898
|
|
|
25682
25899
|
# \u8F93\u51FA\u683C\u5F0F\uFF08\u4EC5JSON\uFF09
|
|
25683
25900
|
\u4F60\u5FC5\u987B\u4EC5\u4EE5 JSON \u683C\u5F0F\u56DE\u590D\uFF0C\u7ED3\u6784\u5982\u4E0B\uFF1A
|
|
@@ -25832,7 +26049,9 @@ ${rubricsSection}
|
|
|
25832
26049
|
pass,
|
|
25833
26050
|
final_score: finalScore,
|
|
25834
26051
|
dimension_results: dimensionResults,
|
|
25835
|
-
summary: parsedResult.summary || testResultContent
|
|
26052
|
+
summary: parsedResult.summary || testResultContent,
|
|
26053
|
+
interrupted: interrupt5 ? true : void 0,
|
|
26054
|
+
interrupt: interrupt5 ? { id: interrupt5.id, value: interrupt5.value } : void 0
|
|
25836
26055
|
};
|
|
25837
26056
|
}
|
|
25838
26057
|
};
|
|
@@ -25847,6 +26066,8 @@ async function evaluateLatticeCaseWithLogs(evalCase, config) {
|
|
|
25847
26066
|
return {
|
|
25848
26067
|
caseId: evalCase.caseId,
|
|
25849
26068
|
result,
|
|
26069
|
+
interrupted: result?.interrupted,
|
|
26070
|
+
interrupt: result?.interrupt,
|
|
25850
26071
|
duration_ms: meta.duration_ms,
|
|
25851
26072
|
thread_id: meta.thread_id,
|
|
25852
26073
|
judge_thread_id: meta.judge_thread_id,
|
|
@@ -25929,7 +26150,8 @@ function resolveTemplateCase(templateCase, templates) {
|
|
|
25929
26150
|
eval: {
|
|
25930
26151
|
content_assertion: templateCase.eval.content_assertion,
|
|
25931
26152
|
eval_rubrics: templateCase.eval.eval_rubrics || template.default_case.eval?.eval_rubrics
|
|
25932
|
-
}
|
|
26153
|
+
},
|
|
26154
|
+
interruptPolicy: templateCase.interruptPolicy ?? template.default_case.interruptPolicy
|
|
25933
26155
|
};
|
|
25934
26156
|
return resolvedCase;
|
|
25935
26157
|
}
|
|
@@ -25995,6 +26217,8 @@ var LatticeEvalSuite = class {
|
|
|
25995
26217
|
result: run.result,
|
|
25996
26218
|
error: run.error,
|
|
25997
26219
|
error_stack: run.error_stack,
|
|
26220
|
+
interrupted: run.interrupted,
|
|
26221
|
+
interrupt: run.interrupt,
|
|
25998
26222
|
duration_ms: run.duration_ms,
|
|
25999
26223
|
thread_id: run.thread_id,
|
|
26000
26224
|
judge_thread_id: run.judge_thread_id,
|
|
@@ -26027,6 +26251,8 @@ var LatticeEvalSuite = class {
|
|
|
26027
26251
|
result: run.result,
|
|
26028
26252
|
error: run.error,
|
|
26029
26253
|
error_stack: run.error_stack,
|
|
26254
|
+
interrupted: run.interrupted,
|
|
26255
|
+
interrupt: run.interrupt,
|
|
26030
26256
|
duration_ms: run.duration_ms,
|
|
26031
26257
|
thread_id: run.thread_id,
|
|
26032
26258
|
judge_thread_id: run.judge_thread_id,
|
|
@@ -26236,24 +26462,29 @@ Running batch: ${this.project.projectName} (${this.getSuiteNames().length} suite
|
|
|
26236
26462
|
let total_cases = 0;
|
|
26237
26463
|
let passed_cases = 0;
|
|
26238
26464
|
let failed_cases = 0;
|
|
26465
|
+
let interrupted_cases = 0;
|
|
26239
26466
|
const suites = [];
|
|
26240
26467
|
for (const [suiteName, caseResults] of results.entries()) {
|
|
26241
26468
|
const suiteTotal = caseResults.length;
|
|
26242
26469
|
const suitePassed = caseResults.filter((r) => r.result?.pass).length;
|
|
26470
|
+
const suiteInterrupted = caseResults.filter((r) => r.interrupted).length;
|
|
26243
26471
|
const suiteFailed = suiteTotal - suitePassed;
|
|
26244
26472
|
total_cases += suiteTotal;
|
|
26245
26473
|
passed_cases += suitePassed;
|
|
26246
26474
|
failed_cases += suiteFailed;
|
|
26475
|
+
interrupted_cases += suiteInterrupted;
|
|
26247
26476
|
suites.push({
|
|
26248
26477
|
suiteName,
|
|
26249
26478
|
total_cases: suiteTotal,
|
|
26250
26479
|
passed_cases: suitePassed,
|
|
26251
26480
|
failed_cases: suiteFailed,
|
|
26481
|
+
interrupted_cases: suiteInterrupted,
|
|
26252
26482
|
cases: caseResults.map((r) => ({
|
|
26253
26483
|
caseId: r.caseId,
|
|
26254
26484
|
pass: r.result?.pass,
|
|
26255
26485
|
final_score: r.result?.final_score,
|
|
26256
|
-
error: r.error
|
|
26486
|
+
error: r.error,
|
|
26487
|
+
interrupted: r.interrupted
|
|
26257
26488
|
}))
|
|
26258
26489
|
});
|
|
26259
26490
|
}
|
|
@@ -26271,13 +26502,14 @@ Running batch: ${this.project.projectName} (${this.getSuiteNames().length} suite
|
|
|
26271
26502
|
total_cases,
|
|
26272
26503
|
passed_cases,
|
|
26273
26504
|
failed_cases,
|
|
26505
|
+
interrupted_cases,
|
|
26274
26506
|
pass_rate: total_cases > 0 ? passed_cases / total_cases : 0
|
|
26275
26507
|
},
|
|
26276
26508
|
suites
|
|
26277
26509
|
};
|
|
26278
26510
|
console.log(`
|
|
26279
26511
|
=== Summary ===`);
|
|
26280
|
-
console.log(`Total: ${report.summary.total_cases} | Passed: ${report.summary.passed_cases} | Failed: ${report.summary.failed_cases} | Pass Rate: ${(report.summary.pass_rate * 100).toFixed(2)}%`);
|
|
26512
|
+
console.log(`Total: ${report.summary.total_cases} | Passed: ${report.summary.passed_cases} | Failed: ${report.summary.failed_cases} | Interrupted: ${report.summary.interrupted_cases} | Pass Rate: ${(report.summary.pass_rate * 100).toFixed(2)}%`);
|
|
26281
26513
|
return { batch_id, results, report };
|
|
26282
26514
|
}
|
|
26283
26515
|
};
|
|
@@ -28226,10 +28458,21 @@ Write assertions as objective, verifiable natural language:
|
|
|
28226
28458
|
- steps: [{agent_id: "a"}, {agent_id: "b", override_message: "Based on..."}] for chain
|
|
28227
28459
|
- outputType: "message_content" or "file_content"
|
|
28228
28460
|
|
|
28461
|
+
## Designing HITL Cases
|
|
28462
|
+
If the flow under test PAUSES for human input (HITL \u2014 the agent requests approval/confirmation), configure interruptPolicy on the case \u2014 otherwise the run stops at the pause and the flow AFTER the human input is never tested:
|
|
28463
|
+
|
|
28464
|
+
- interruptPolicy: {mode: "auto-approve"} \u2014 inject "\u540C\u610F"; tests the full flow after approval (e.g. payment executed after approval). Use value to override the injected text.
|
|
28465
|
+
- interruptPolicy: {mode: "auto-reject"} \u2014 inject "\u62D2\u7EDD"; tests the rejection path.
|
|
28466
|
+
- interruptPolicy: {mode: "canned-response", value: "..."} \u2014 inject an exact human reply; tests behavior under a specific response.
|
|
28467
|
+
- Omit (default "stop") \u2014 the case pauses at the request; the judge evaluates whether pausing was the correct behavior (e.g. "must request approval" PASSES; "must be autonomous" FAILS).
|
|
28468
|
+
|
|
28469
|
+
Choose per the assertion: if the assertion describes what happens AFTER the human input, you MUST set an auto-resolve policy.
|
|
28470
|
+
|
|
28229
28471
|
## Checklist
|
|
28230
28472
|
1. Check existing assets with read_eval to avoid duplication
|
|
28231
28473
|
2. Start with 3-5 high-signal cases
|
|
28232
|
-
3.
|
|
28474
|
+
3. If the flow contains a human-approval step and the assertion covers what happens AFTER it, set interruptPolicy (auto-approve / auto-reject / canned-response) \u2014 never leave it unset in that case
|
|
28475
|
+
4. Confirm with user before calling manage_eval
|
|
28233
28476
|
`,
|
|
28234
28477
|
"eval-run-and-govern": `---
|
|
28235
28478
|
name: eval-run-and-govern
|
|
@@ -28239,8 +28482,11 @@ description: Run agent evaluations, interpret results, diagnose failures, and re
|
|
|
28239
28482
|
# Agent Governance Loop
|
|
28240
28483
|
|
|
28241
28484
|
1. Discover project \u2192 read_eval list_projects
|
|
28242
|
-
2. Start evaluation \u2192 run_eval start(projectId) \u2014
|
|
28243
|
-
|
|
28485
|
+
2. Start evaluation \u2192 run_eval start(projectId) \u2014 SYNCHRONOUS by default:
|
|
28486
|
+
blocks up to ~150s and returns the FINAL RESULTS in one call.
|
|
28487
|
+
Hold-out (validation) runs return aggregates only.
|
|
28488
|
+
3. If still running (or use wait: false for fire-and-forget) \u2192 poll
|
|
28489
|
+
run_eval status(runId, sleepMs) \u2014 pass sleepMs to pace (15s, 30s, 60s, max 120s)
|
|
28244
28490
|
4. If runnerAlive=false and status=running \u2192 ORPHANED (gateway restart).
|
|
28245
28491
|
resume(runId) marks it failed automatically \u2014 then start a new run.
|
|
28246
28492
|
5. Get results \u2192 read_eval get_run_results(runId) for per-case dimension scores
|
|
@@ -28261,7 +28507,26 @@ Use read_eval get_run_results for multiple runs and present comparison.
|
|
|
28261
28507
|
`
|
|
28262
28508
|
};
|
|
28263
28509
|
|
|
28510
|
+
// src/tool_lattice/withToolTimeout.ts
|
|
28511
|
+
function withToolTimeout(executor, timeoutMs = 18e4) {
|
|
28512
|
+
return async (input, exeConfig) => {
|
|
28513
|
+
return new Promise((resolve4, reject) => {
|
|
28514
|
+
const timer = setTimeout(() => {
|
|
28515
|
+
reject(new Error(`Tool execution timed out after ${timeoutMs}ms`));
|
|
28516
|
+
}, timeoutMs);
|
|
28517
|
+
executor(input, exeConfig).then((result) => {
|
|
28518
|
+
clearTimeout(timer);
|
|
28519
|
+
resolve4(result);
|
|
28520
|
+
}).catch((err) => {
|
|
28521
|
+
clearTimeout(timer);
|
|
28522
|
+
reject(err);
|
|
28523
|
+
});
|
|
28524
|
+
});
|
|
28525
|
+
};
|
|
28526
|
+
}
|
|
28527
|
+
|
|
28264
28528
|
// src/middlewares/evalMiddleware.ts
|
|
28529
|
+
var RUN_EVAL_SYNC_WAIT_MS = 15e4;
|
|
28265
28530
|
function getStore() {
|
|
28266
28531
|
return getStoreLattice("default", "eval").store;
|
|
28267
28532
|
}
|
|
@@ -28288,6 +28553,25 @@ function sanitize(obj) {
|
|
|
28288
28553
|
}
|
|
28289
28554
|
return out;
|
|
28290
28555
|
}
|
|
28556
|
+
function aggregateHoldoutResults(results) {
|
|
28557
|
+
const passed = results.filter((r) => r.pass).length;
|
|
28558
|
+
const interrupted = results.filter((r) => r.interrupted).length;
|
|
28559
|
+
return {
|
|
28560
|
+
holdout: true,
|
|
28561
|
+
passedCases: passed,
|
|
28562
|
+
failedCases: results.length - passed,
|
|
28563
|
+
interruptedCases: interrupted,
|
|
28564
|
+
passRate: results.length > 0 ? passed / results.length : 0,
|
|
28565
|
+
totalCases: results.length
|
|
28566
|
+
};
|
|
28567
|
+
}
|
|
28568
|
+
async function runWithResults(tid, store, svc, run, runnerAlive) {
|
|
28569
|
+
const results = run.status === "completed" ? await store.getResultsByRun(tid, run.id) : void 0;
|
|
28570
|
+
if (run.holdout && results) {
|
|
28571
|
+
return { ...run, runnerAlive, results: aggregateHoldoutResults(results) };
|
|
28572
|
+
}
|
|
28573
|
+
return { ...run, runnerAlive, results };
|
|
28574
|
+
}
|
|
28291
28575
|
function createReadEvalTool() {
|
|
28292
28576
|
const schema6 = z66.object({
|
|
28293
28577
|
action: z66.enum([
|
|
@@ -28347,13 +28631,8 @@ function createReadEvalTool() {
|
|
|
28347
28631
|
if (!run) return JSON.stringify({ success: false, error: "Run not found" });
|
|
28348
28632
|
const results = await store.getResultsByRun(tid, input.runId);
|
|
28349
28633
|
if (run.holdout) {
|
|
28350
|
-
const passed = results.filter((r) => r.pass).length;
|
|
28351
28634
|
data = {
|
|
28352
|
-
|
|
28353
|
-
passedCases: passed,
|
|
28354
|
-
failedCases: results.length - passed,
|
|
28355
|
-
passRate: results.length > 0 ? passed / results.length : 0,
|
|
28356
|
-
totalCases: results.length,
|
|
28635
|
+
...aggregateHoldoutResults(results),
|
|
28357
28636
|
message: "Hold-out run \u2014 per-case results withheld. Only aggregates are available."
|
|
28358
28637
|
};
|
|
28359
28638
|
} else {
|
|
@@ -28388,6 +28667,7 @@ ACTIONS:
|
|
|
28388
28667
|
- get_run_results(runId) \u2014 per-case results with dimension scores.
|
|
28389
28668
|
For HOLD-OUT (validation-only) runs: returns AGGREGATES ONLY
|
|
28390
28669
|
(passRate, counts) \u2014 per-case details are withheld by design.
|
|
28670
|
+
Cases paused for human input (HITL) carry interrupted=true and are judged \u2014 the judge evaluates whether pausing was correct business behavior.
|
|
28391
28671
|
- get_project_report(projectId) \u2014 aggregated stats across all runs`,
|
|
28392
28672
|
schema: schema6
|
|
28393
28673
|
}
|
|
@@ -28418,7 +28698,11 @@ function createManageEvalTool() {
|
|
|
28418
28698
|
steps: z66.array(z66.object({ agent_id: z66.string(), override_message: z66.string().optional() })).optional(),
|
|
28419
28699
|
outputType: z66.enum(["file_content", "message_content"]).optional(),
|
|
28420
28700
|
contentAssertion: z66.string().optional(),
|
|
28421
|
-
rubrics: z66.array(z66.object({ name: z66.string(), weight: z66.number(), description: z66.string() })).optional()
|
|
28701
|
+
rubrics: z66.array(z66.object({ name: z66.string(), weight: z66.number(), description: z66.string() })).optional(),
|
|
28702
|
+
interruptPolicy: z66.object({
|
|
28703
|
+
mode: z66.enum(["stop", "auto-approve", "auto-reject", "canned-response"]).describe("stop=judge the pause; auto-approve/auto-reject/canned-response=resume the agent to test the flow after the human input"),
|
|
28704
|
+
value: z66.string().optional().describe("Response to inject (defaults: \u540C\u610F / \u62D2\u7EDD; required for canned-response)")
|
|
28705
|
+
}).optional().describe("Optional for create_case/update_case \u2014 how HITL interrupts are handled")
|
|
28422
28706
|
});
|
|
28423
28707
|
return tool62(
|
|
28424
28708
|
async (input, exeConfig) => {
|
|
@@ -28476,7 +28760,8 @@ function createManageEvalTool() {
|
|
|
28476
28760
|
steps: input.steps,
|
|
28477
28761
|
outputType: input.outputType,
|
|
28478
28762
|
contentAssertion: input.contentAssertion,
|
|
28479
|
-
rubrics: input.rubrics
|
|
28763
|
+
rubrics: input.rubrics,
|
|
28764
|
+
interruptPolicy: input.interruptPolicy
|
|
28480
28765
|
});
|
|
28481
28766
|
break;
|
|
28482
28767
|
case "update_case":
|
|
@@ -28484,7 +28769,8 @@ function createManageEvalTool() {
|
|
|
28484
28769
|
inputMessage: input.inputMessage,
|
|
28485
28770
|
contentAssertion: input.contentAssertion,
|
|
28486
28771
|
steps: input.steps,
|
|
28487
|
-
rubrics: input.rubrics
|
|
28772
|
+
rubrics: input.rubrics,
|
|
28773
|
+
interruptPolicy: input.interruptPolicy
|
|
28488
28774
|
});
|
|
28489
28775
|
break;
|
|
28490
28776
|
case "delete_case":
|
|
@@ -28509,9 +28795,13 @@ Project: create_project(name, description?, judgeModelKey?, concurrency?) | upda
|
|
|
28509
28795
|
**When creating a project from within a workspace, the workspace/project context is
|
|
28510
28796
|
automatically bound \u2014 eval runs will execute in the same workspace.**
|
|
28511
28797
|
Suite: create_suite(projectId, name) | update_suite | delete_suite
|
|
28512
|
-
Case: create_case(suiteId, inputMessage, steps, outputType, contentAssertion, inputFiles?, rubrics?)
|
|
28798
|
+
Case: create_case(suiteId, inputMessage, steps, outputType, contentAssertion, inputFiles?, rubrics?, interruptPolicy?)
|
|
28513
28799
|
steps is [{agent_id, override_message?}]. outputType is "file_content" or "message_content".
|
|
28514
|
-
rubrics is [{name, weight, description}]. | update_case | delete_case
|
|
28800
|
+
rubrics is [{name, weight, description}]. | update_case | delete_case
|
|
28801
|
+
interruptPolicy: {mode: "stop"|"auto-approve"|"auto-reject"|"canned-response", value?} \u2014 how HITL interrupts are handled:
|
|
28802
|
+
stop (default): case pauses at the human-input request; the judge evaluates the pause as business behavior.
|
|
28803
|
+
auto-approve / auto-reject: the runner injects approval/rejection and tests the FULL flow after the pause.
|
|
28804
|
+
canned-response: injects the exact value (simulates a specific human reply).`,
|
|
28515
28805
|
schema: schema6
|
|
28516
28806
|
}
|
|
28517
28807
|
);
|
|
@@ -28522,100 +28812,121 @@ function createRunEvalTool() {
|
|
|
28522
28812
|
projectId: z66.string().optional().describe("Required for start"),
|
|
28523
28813
|
suiteIds: z66.array(z66.string()).optional().describe("Optional for start \u2014 only run these suites (e.g. dev set only). Omit to run all."),
|
|
28524
28814
|
caseIds: z66.array(z66.string()).optional().describe("Optional for start \u2014 only run these cases across the selected suites. Omit to run all cases in those suites."),
|
|
28525
|
-
runId: z66.string().optional().describe("Required for status, resume, abort")
|
|
28815
|
+
runId: z66.string().optional().describe("Required for status, resume, abort"),
|
|
28816
|
+
sleepMs: z66.number().int().min(0).max(12e4).optional().describe("Optional for status \u2014 sleep this many ms BEFORE checking the run, to pace polling (e.g. 15000 \u2192 30000 \u2192 60000 \u2192 120000). Omit to check immediately."),
|
|
28817
|
+
wait: z66.boolean().optional().describe("Optional for start \u2014 defaults to true: block synchronously (up to ~150s) and return final results in one call. Set false to return the runId immediately and poll.")
|
|
28526
28818
|
});
|
|
28527
28819
|
return tool62(
|
|
28528
|
-
|
|
28529
|
-
|
|
28530
|
-
|
|
28531
|
-
|
|
28532
|
-
|
|
28533
|
-
|
|
28534
|
-
|
|
28535
|
-
|
|
28536
|
-
|
|
28537
|
-
|
|
28538
|
-
|
|
28539
|
-
|
|
28540
|
-
|
|
28541
|
-
|
|
28542
|
-
|
|
28543
|
-
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
|
|
28547
|
-
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
await store.
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28568
|
-
}
|
|
28820
|
+
withToolTimeout(
|
|
28821
|
+
async (input, exeConfig) => {
|
|
28822
|
+
const tid = tenantId(exeConfig);
|
|
28823
|
+
if (!tid) {
|
|
28824
|
+
return JSON.stringify({ success: false, error: "No tenant context. Agent must be invoked through gateway." });
|
|
28825
|
+
}
|
|
28826
|
+
try {
|
|
28827
|
+
const store = getStore();
|
|
28828
|
+
const svc = getEvalRunService();
|
|
28829
|
+
let data;
|
|
28830
|
+
switch (input.action) {
|
|
28831
|
+
case "start": {
|
|
28832
|
+
const ctx = workspaceContext(exeConfig);
|
|
28833
|
+
const runId = await svc.startRun(tid, input.projectId, input.suiteIds, input.caseIds, ctx);
|
|
28834
|
+
if (input.wait === false) {
|
|
28835
|
+
data = sanitize({ runId, message: "Run started. Poll with run_eval status (backoff: 15s\u219230s\u219260s\u2192max 120s)." });
|
|
28836
|
+
break;
|
|
28837
|
+
}
|
|
28838
|
+
let timer;
|
|
28839
|
+
try {
|
|
28840
|
+
await Promise.race([
|
|
28841
|
+
svc.waitForRun(runId).catch(() => {
|
|
28842
|
+
}),
|
|
28843
|
+
new Promise((resolve4) => {
|
|
28844
|
+
timer = setTimeout(resolve4, RUN_EVAL_SYNC_WAIT_MS);
|
|
28845
|
+
})
|
|
28846
|
+
]);
|
|
28847
|
+
} finally {
|
|
28848
|
+
if (timer) clearTimeout(timer);
|
|
28849
|
+
}
|
|
28850
|
+
const run = await store.getRunById(tid, runId);
|
|
28851
|
+
if (!run) return JSON.stringify({ success: false, error: "Run not found" });
|
|
28852
|
+
if (run.status === "running") {
|
|
28853
|
+
data = sanitize({
|
|
28854
|
+
runId,
|
|
28855
|
+
status: "running",
|
|
28856
|
+
runnerAlive: svc.isRunning(runId),
|
|
28857
|
+
message: `Run not finished within ${RUN_EVAL_SYNC_WAIT_MS / 1e3}s \u2014 poll with run_eval status(runId, sleepMs) (e.g. 15000, doubling up to 120000), or abort with run_eval abort.`
|
|
28858
|
+
});
|
|
28859
|
+
break;
|
|
28860
|
+
}
|
|
28861
|
+
data = sanitize({ synced: true, ...await runWithResults(tid, store, svc, run, svc.isRunning(runId)) });
|
|
28569
28862
|
break;
|
|
28570
28863
|
}
|
|
28571
|
-
|
|
28572
|
-
|
|
28573
|
-
|
|
28574
|
-
|
|
28575
|
-
|
|
28576
|
-
|
|
28577
|
-
|
|
28578
|
-
holdout: true,
|
|
28579
|
-
passedCases: passed,
|
|
28580
|
-
failedCases: results.length - passed,
|
|
28581
|
-
passRate: results.length > 0 ? passed / results.length : 0,
|
|
28582
|
-
totalCases: results.length
|
|
28583
|
-
}
|
|
28584
|
-
});
|
|
28864
|
+
case "status": {
|
|
28865
|
+
if (input.sleepMs && input.sleepMs > 0) {
|
|
28866
|
+
await new Promise((resolve4) => setTimeout(resolve4, input.sleepMs));
|
|
28867
|
+
}
|
|
28868
|
+
const run = await store.getRunById(tid, input.runId);
|
|
28869
|
+
if (!run) return JSON.stringify({ success: false, error: "Run not found" });
|
|
28870
|
+
data = sanitize({ ...run, runnerAlive: svc.isRunning(input.runId) });
|
|
28585
28871
|
break;
|
|
28586
28872
|
}
|
|
28587
|
-
|
|
28588
|
-
|
|
28589
|
-
|
|
28590
|
-
|
|
28591
|
-
|
|
28592
|
-
|
|
28593
|
-
|
|
28594
|
-
|
|
28595
|
-
|
|
28873
|
+
case "resume": {
|
|
28874
|
+
const run = await store.getRunById(tid, input.runId);
|
|
28875
|
+
if (!run) return JSON.stringify({ success: false, error: "Run not found" });
|
|
28876
|
+
const runnerAlive = svc.isRunning(input.runId);
|
|
28877
|
+
if (run.status === "running" && !runnerAlive) {
|
|
28878
|
+
await store.updateRunStatus(tid, run.id, {
|
|
28879
|
+
status: "failed",
|
|
28880
|
+
error: "Gateway restarted \u2014 run orphaned",
|
|
28881
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
28882
|
+
});
|
|
28883
|
+
data = sanitize({
|
|
28884
|
+
...run,
|
|
28885
|
+
status: "failed",
|
|
28886
|
+
runnerAlive: false,
|
|
28887
|
+
message: "Run was orphaned \u2014 marked failed. Start a new run."
|
|
28888
|
+
});
|
|
28889
|
+
break;
|
|
28890
|
+
}
|
|
28891
|
+
data = sanitize(await runWithResults(tid, store, svc, run, runnerAlive));
|
|
28892
|
+
break;
|
|
28893
|
+
}
|
|
28894
|
+
case "abort": {
|
|
28895
|
+
const run = await store.getRunById(tid, input.runId);
|
|
28896
|
+
if (!run) return JSON.stringify({ success: false, error: "Run not found" });
|
|
28897
|
+
const ok = await svc.abortRun(input.runId);
|
|
28898
|
+
data = sanitize({ aborted: ok });
|
|
28899
|
+
break;
|
|
28900
|
+
}
|
|
28901
|
+
default:
|
|
28902
|
+
return JSON.stringify({ success: false, error: `Unknown action: ${input.action}` });
|
|
28596
28903
|
}
|
|
28597
|
-
|
|
28598
|
-
|
|
28904
|
+
return JSON.stringify({ success: true, data });
|
|
28905
|
+
} catch (e) {
|
|
28906
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
28599
28907
|
}
|
|
28600
|
-
return JSON.stringify({ success: true, data });
|
|
28601
|
-
} catch (e) {
|
|
28602
|
-
return JSON.stringify({ success: false, error: e.message });
|
|
28603
28908
|
}
|
|
28604
|
-
|
|
28909
|
+
),
|
|
28605
28910
|
{
|
|
28606
28911
|
name: "run_eval",
|
|
28607
|
-
description: `Execute and manage evaluation runs.
|
|
28912
|
+
description: `Execute and manage evaluation runs.
|
|
28608
28913
|
|
|
28609
28914
|
ACTIONS:
|
|
28610
|
-
- start(projectId, suiteIds?, caseIds?) \u2014 begin evaluation (optionally only the listed suites, and/or only the listed cases).
|
|
28611
|
-
|
|
28612
|
-
|
|
28915
|
+
- start(projectId, suiteIds?, caseIds?, wait?) \u2014 begin evaluation (optionally only the listed suites, and/or only the listed cases).
|
|
28916
|
+
wait=true (DEFAULT): SYNCHRONOUS \u2014 blocks up to ~150s and returns the FINAL RESULTS in one call:
|
|
28917
|
+
{ status, results } with per-case pass/score (hold-out/validation runs: aggregates only \u2014 per-case details withheld by design).
|
|
28918
|
+
Cases paused for human input (HITL) carry interrupted=true and ARE judged \u2014 the judge evaluates whether requesting
|
|
28919
|
+
the human was the correct behavior (assertions like "must approve first" PASS; "must be autonomous" FAIL). interruptedCases counts these.
|
|
28920
|
+
If the run exceeds 150s: returns { runId, status: "running" } \u2014 NOT an error \u2014 then poll with status/resume until completed.
|
|
28921
|
+
wait=false: fire-and-forget \u2014 returns { runId } immediately; poll with status.
|
|
28922
|
+
- status(runId, sleepMs?) \u2014 sleep sleepMs first (to pace polling), then return current status + runnerAlive flag:
|
|
28923
|
+
\u2022 runnerAlive=true, status=running: keep polling \u2014 call status(runId, sleepMs) with backoff 15s\u219230s\u219260s\u2192max 120s
|
|
28613
28924
|
\u2022 runnerAlive=false, status=running: ORPHANED \u2014 resume marks it failed automatically; then start a new run
|
|
28614
28925
|
\u2022 status=completed: get results with read_eval get_run_results or run_eval resume
|
|
28615
28926
|
- resume(runId) \u2014 reconnect from new conversation. Returns status + results if completed.
|
|
28616
28927
|
- abort(runId) \u2014 cancel running evaluation.
|
|
28617
28928
|
|
|
28618
|
-
Polling: start at 15s, double each time, max 120s between polls. Batch reports.`,
|
|
28929
|
+
Polling (only needed with wait=false or after a sync timeout): call status(runId, sleepMs) so the tool sleeps before checking; start at 15s, double each time, max 120s between polls. Batch reports.`,
|
|
28619
28930
|
schema: schema6
|
|
28620
28931
|
}
|
|
28621
28932
|
);
|
|
@@ -28911,67 +29222,64 @@ verification choice, then start benchmarking.
|
|
|
28911
29222
|
|
|
28912
29223
|
## Task Tracking \u2014 see [[task-tracking]]
|
|
28913
29224
|
|
|
28914
|
-
**
|
|
28915
|
-
|
|
28916
|
-
|
|
28917
|
-
|
|
28918
|
-
|
|
28919
|
-
|
|
28920
|
-
|
|
28921
|
-
never mark a subtask completed while eval
|
|
28922
|
-
runs with manage_task list.
|
|
29225
|
+
**Universal principle**: once the goal is clear and you know what to
|
|
29226
|
+
do, create the parent task BEFORE executing (manage_task create, see
|
|
29227
|
+
[[task-tracking]]). In this workflow: after Phase 0 clarification
|
|
29228
|
+
completes and the user confirmed the path (end of 0.5), create the
|
|
29229
|
+
parent task; then a subtask per phase as you start it. The parent task
|
|
29230
|
+
description carries the GOAL MODEL (0.1.5) as Objective + Acceptance
|
|
29231
|
+
Criteria; the expected output spec (2.6) updates the criteria. Update
|
|
29232
|
+
status to reflect reality \u2014 never mark a subtask completed while eval
|
|
29233
|
+
fails. Resume interrupted runs with manage_task list.
|
|
28923
29234
|
|
|
28924
29235
|
Widgets: call \`load_guidelines\` ONCE before your first \`show_widget\`
|
|
28925
29236
|
(show_widget hard-requires it), then reuse.
|
|
28926
29237
|
|
|
28927
29238
|
---
|
|
28928
29239
|
|
|
28929
|
-
## Phase 1:
|
|
28930
|
-
|
|
28931
|
-
The
|
|
28932
|
-
|
|
28933
|
-
|
|
28934
|
-
|
|
28935
|
-
|
|
28936
|
-
|
|
28937
|
-
|
|
28938
|
-
|
|
28939
|
-
|
|
28940
|
-
|
|
28941
|
-
|
|
28942
|
-
|
|
28943
|
-
|
|
28944
|
-
|
|
28945
|
-
|
|
28946
|
-
|
|
28947
|
-
|
|
28948
|
-
|
|
28949
|
-
|
|
28950
|
-
the
|
|
28951
|
-
- **
|
|
28952
|
-
|
|
28953
|
-
|
|
28954
|
-
|
|
28955
|
-
|
|
28956
|
-
|
|
28957
|
-
|
|
28958
|
-
|
|
28959
|
-
|
|
28960
|
-
|
|
28961
|
-
|
|
28962
|
-
|
|
28963
|
-
|
|
28964
|
-
|
|
28965
|
-
|
|
28966
|
-
|
|
28967
|
-
|
|
28968
|
-
|
|
28969
|
-
|
|
28970
|
-
|
|
28971
|
-
|
|
28972
|
-
discover existing agents with relevant capabilities (see \xA75).
|
|
28973
|
-
For \u2460, look for agents with data-access tools (SQL / API). For \u2461, look
|
|
28974
|
-
for agents with independence.
|
|
29240
|
+
## Phase 1: Explore (goal-driven path finding)
|
|
29241
|
+
|
|
29242
|
+
The goal model (0.1.5) is set. Now EXPLORE how to achieve it \u2014 actively
|
|
29243
|
+
hunt for the path, do not passively read. Three exploration fronts:
|
|
29244
|
+
|
|
29245
|
+
**A. Existing assets (reuse, don't rebuild):**
|
|
29246
|
+
- \`list_agents\` / \`load_skills\` \u2192 are there existing agents or skills
|
|
29247
|
+
that already do part of this? Reuse them (update_agent if needed)
|
|
29248
|
+
instead of building from scratch. This is a goal-relevant check, not
|
|
29249
|
+
a materials step.
|
|
29250
|
+
- \`list_tools\` / \`list_middleware_types\` \u2192 what capabilities exist
|
|
29251
|
+
that the goal needs (parsing, data access, browser...)?
|
|
29252
|
+
- \`list_connections\` \u2192 are the data sources the goal depends on
|
|
29253
|
+
already connected?
|
|
29254
|
+
- If verification will happen (0.2 \u2460 or \u2461): concurrently discover
|
|
29255
|
+
executor candidates (see \xA75). \u2460 \u2192 data-access tools; \u2461 \u2192 independence.
|
|
29256
|
+
|
|
29257
|
+
**B. Material probing (by material type):**
|
|
29258
|
+
- **User-description**: the requirements come from the conversation.
|
|
29259
|
+
Extract goal, inputs, outputs, constraints \u2014 then explore the
|
|
29260
|
+
implementation path (A + feasibility): what assets exist, what tools
|
|
29261
|
+
are needed, what blockers stand between the goal and its achievement.
|
|
29262
|
+
- **Document** (PDF / spec / manual): benchmark engines as needed \u2014
|
|
29263
|
+
parse directly with the chosen engine (0.3 \u2460-\u2464) or run
|
|
29264
|
+
document-parser-benchmark. Engine selection IS distilled knowledge:
|
|
29265
|
+
it builds the agent (engine's parse_document into middleware), seeds
|
|
29266
|
+
the skill (feature signature + winning engine), and designs the tests
|
|
29267
|
+
(engine output as case baseline input).
|
|
29268
|
+
- **API spec**: read directly \u2014 endpoints, schemas, examples.
|
|
29269
|
+
- **Conversation**: extract workflow, decisions, corrections.
|
|
29270
|
+
- **Spreadsheet**: parse cells directly.
|
|
29271
|
+
|
|
29272
|
+
**C. Feasibility (path blockers):**
|
|
29273
|
+
- What stands between the goal and achievement? Missing tools, missing
|
|
29274
|
+
connections, data access, permission constraints, ambiguous
|
|
29275
|
+
requirements.
|
|
29276
|
+
- Does the goal require orchestration (\u2192 Phase 2 split decision)?
|
|
29277
|
+
- Surface these in the recommendation (Phase 1.5) \u2014 the user decides
|
|
29278
|
+
the path, informed by what exploration found.
|
|
29279
|
+
|
|
29280
|
+
Exploration is COMPLETE when you can answer: what exists to reuse,
|
|
29281
|
+
what must be built, what tools/connections are needed, and what blocks
|
|
29282
|
+
the goal. Do not go to design without this map.
|
|
28975
29283
|
|
|
28976
29284
|
---
|
|
28977
29285
|
|
|
@@ -28986,17 +29294,21 @@ candidate and assess (Validation Agent Design \xA70) \u2014 state which are
|
|
|
28986
29294
|
usable and which are not, with reasons. For \u2460, the executor needs data
|
|
28987
29295
|
tools + independence. For \u2461, independence only. If no candidate fits,
|
|
28988
29296
|
plan to build one via \xA75.
|
|
28989
|
-
Present
|
|
29297
|
+
Present the EXPLORATION map as widget \u2014 what exists to reuse, what
|
|
29298
|
+
must be built, tools/connections needed, blockers found \u2014 then
|
|
29299
|
+
recommend the IMPLEMENTATION PATH: reuse existing X, build new Y,
|
|
29300
|
+
split or single agent (Phase 2 input). MUST call
|
|
28990
29301
|
\`ask_user_to_clarify\` NOW:
|
|
28991
29302
|
{
|
|
28992
29303
|
"questions": [{
|
|
28993
|
-
"question": "Confirm the
|
|
29304
|
+
"question": "Confirm the recommended path?",
|
|
28994
29305
|
"options": ["Confirm", "Adjust"],
|
|
28995
29306
|
"type": "single",
|
|
28996
29307
|
"required": true
|
|
28997
29308
|
}]
|
|
28998
29309
|
}
|
|
28999
|
-
Skills planning belongs to Phase 2 \u2014 this phase presents
|
|
29310
|
+
Skills planning belongs to Phase 2 \u2014 this phase presents the path, not
|
|
29311
|
+
the detailed plan.
|
|
29000
29312
|
|
|
29001
29313
|
---
|
|
29002
29314
|
|
|
@@ -29064,9 +29376,53 @@ user-description material this IS the core phase; for material-based
|
|
|
29064
29376
|
learning it designs the agent that runs the learned skill. Agent
|
|
29065
29377
|
metadata (verified/version/source) must be set on creation.
|
|
29066
29378
|
|
|
29379
|
+
## Phase 2.6: Expected Output Specification (mandatory \u2014 goal-driven)
|
|
29380
|
+
|
|
29381
|
+
**Expectations come FIRST, before writing the skill.** You cannot write
|
|
29382
|
+
a skill (or test cases) without a target. Define the expected output
|
|
29383
|
+
specification from the goal model (0.1.5: real goal / consumer / usable
|
|
29384
|
+
state) BEFORE Phase 3:
|
|
29385
|
+
|
|
29386
|
+
**HARD RULE \u2014 never guess the target.** If the goal, the expected
|
|
29387
|
+
output, the consumer, or the usable state is unclear at ANY point
|
|
29388
|
+
before writing test cases, you MUST ask the user via
|
|
29389
|
+
\`ask_user_to_clarify\` \u2014 do NOT proceed with an assumed expectation.
|
|
29390
|
+
A test case written against a guessed expectation is worthless: it
|
|
29391
|
+
validates the wrong thing. When in doubt, ask.
|
|
29392
|
+
|
|
29393
|
+
Per skill, define the EXPECTED OUTPUT SPEC (based on intent 0.1 and
|
|
29394
|
+
consumer 0.1.5):
|
|
29395
|
+
- Extract data \u2192 expected fields (names, types, formats), required vs
|
|
29396
|
+
optional, output structure (JSON schema shape, table columns)
|
|
29397
|
+
- Validate rules \u2192 expected judgment outcomes (pass/fail conditions),
|
|
29398
|
+
boundary values, and the reason format
|
|
29399
|
+
- Execute workflow \u2192 expected step sequence, decision points, final
|
|
29400
|
+
outcome shape
|
|
29401
|
+
- Answer knowledge \u2192 expected answer form (with/without sources,
|
|
29402
|
+
length, structure)
|
|
29403
|
+
|
|
29404
|
+
This spec IS the acceptance standard. Phase 4 contentAssertion must be
|
|
29405
|
+
derived from it (not invented at case-writing time). Present the
|
|
29406
|
+
expected output spec to the user and MUST call \`ask_user_to_clarify\`
|
|
29407
|
+
NOW per skill:
|
|
29408
|
+
{
|
|
29409
|
+
"questions": [{
|
|
29410
|
+
"question": "Confirm the expected output spec for {skill-name}?",
|
|
29411
|
+
"options": ["Confirm", "Adjust"],
|
|
29412
|
+
"type": "single",
|
|
29413
|
+
"required": true,
|
|
29414
|
+
"allowOther": true
|
|
29415
|
+
}]
|
|
29416
|
+
}
|
|
29417
|
+
Record the confirmed spec in the parent task description. This replaces
|
|
29418
|
+
guess-then-confirm: the skill is written TO MEET the spec, and test
|
|
29419
|
+
cases assert AGAINST the spec \u2014 no expectation is invented later.
|
|
29420
|
+
|
|
29067
29421
|
## Phase 3: Create Skills
|
|
29068
29422
|
|
|
29069
|
-
Write SKILL.md to \`/root/.agents/skills/{name}/SKILL.md\` one at a time
|
|
29423
|
+
Write SKILL.md to \`/root/.agents/skills/{name}/SKILL.md\` one at a time,
|
|
29424
|
+
designed TO MEET the expected output spec confirmed in Phase 2.6 \u2014 the
|
|
29425
|
+
skill encodes how to produce the spec's expected output.
|
|
29070
29426
|
Show the skill content in text first, then MUST call
|
|
29071
29427
|
\`ask_user_to_clarify\` NOW per skill:
|
|
29072
29428
|
{
|
|
@@ -29295,7 +29651,8 @@ Both modes use the same agent type \u2014 skill only, no domain tools:
|
|
|
29295
29651
|
verified: "unverified", # upgraded after eval passes
|
|
29296
29652
|
version: "1.0", # bump on each update_agent
|
|
29297
29653
|
source: "{material name}", # provenance
|
|
29298
|
-
skill: "skill-name"
|
|
29654
|
+
skill: "skill-name",
|
|
29655
|
+
role: "orchestrator" | "sub-agent" # only for parent+subAgents structure
|
|
29299
29656
|
}
|
|
29300
29657
|
)
|
|
29301
29658
|
|
|
@@ -29359,7 +29716,8 @@ This learning loop adds its own scenario rules:
|
|
|
29359
29716
|
- Business usability (output reaches the goal's "usable state")
|
|
29360
29717
|
- Consumer fit (format/contract satisfies who uses the result)
|
|
29361
29718
|
contentAssertion must encode the usable state from the goal model
|
|
29362
|
-
(0.1.5),
|
|
29719
|
+
(0.1.5), derived from the EXPECTED OUTPUT SPEC (Phase 2.6) \u2014 never
|
|
29720
|
+
invented at case-writing time.
|
|
29363
29721
|
|
|
29364
29722
|
1. One suite per skill per source: cases test "can this skill do it" \u2014
|
|
29365
29723
|
never mix skills in one suite
|
|
@@ -29391,12 +29749,23 @@ Run evaluation, fix loop, hold-out validation, trust upgrade. See
|
|
|
29391
29749
|
[[eval-verify]] for the full workflow. The eval-design-tests and
|
|
29392
29750
|
eval-run-and-govern skills cover case design and run governance.
|
|
29393
29751
|
|
|
29752
|
+
**One eval project per agent**, named \`eval-{agent-id}\` \u2014 every agent
|
|
29753
|
+
built by this workflow gets its own eval project (see eval-verify
|
|
29754
|
+
Setup). Orchestrator + subAgents \u2192 one eval project per sub-agent plus
|
|
29755
|
+
one integration eval for the parent.
|
|
29756
|
+
|
|
29394
29757
|
Learning-specific suite guidance:
|
|
29395
29758
|
- 0.2 \u2461 \u2192 {skill}-user-sample; samples \u22658 \u2192 also {skill}-validation
|
|
29396
29759
|
- 0.2 \u2460 \u2192 {skill}-api-verified (single step, \xA74.1)
|
|
29397
29760
|
- User-description material: {skill}-requirement-derived \u2014 cases from
|
|
29398
29761
|
user's described requirements
|
|
29399
29762
|
|
|
29763
|
+
**Case expectations come from the confirmed spec** (Phase 2.6): the
|
|
29764
|
+
contentAssertion of every case must be derived from the expected output
|
|
29765
|
+
spec, NOT invented at case-writing time. If a case needs an expectation
|
|
29766
|
+
not in the spec, go back and extend the spec with user confirmation
|
|
29767
|
+
first \u2014 never guess expectations on the fly.
|
|
29768
|
+
|
|
29400
29769
|
[[completion-gate]] applies \u2014 eval must pass before declaring done.
|
|
29401
29770
|
|
|
29402
29771
|
## Phase 5: Retrospective
|
|
@@ -29429,11 +29798,12 @@ base is wanted (it is extra work beyond the skill).
|
|
|
29429
29798
|
## Fallback
|
|
29430
29799
|
|
|
29431
29800
|
- All engines fail \u2192 suggest text version or different format.
|
|
29432
|
-
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29801
|
+
- Eval runtime unavailable (no eval agent / service down) \u2192 still
|
|
29802
|
+
DESIGN and CREATE the eval project with test cases (every agent MUST
|
|
29803
|
+
have an eval \u2014 no skip). If the eval cannot RUN now, deliver with
|
|
29804
|
+
trust capped at human-reviewed and state: "Test framework created;
|
|
29805
|
+
run the evaluation once the eval service is available." Judge-only
|
|
29806
|
+
scoring (when run) does NOT unlock machine-confirmed.
|
|
29437
29807
|
- No test files \u2192 user-described scenarios as contentAssertion.
|
|
29438
29808
|
- run_eval orphaned (resume shows runnerAlive=false) \u2192 \`run_eval resume(runId)\`
|
|
29439
29809
|
marks it failed automatically; then \`run_eval start(projectId)\` to restart.
|