@axiom-lattice/core 3.0.4 → 3.0.6

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.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: Preserve all tool_calls in original AIMessage, including unknown ones.
9398
- * This allows the model to see what it selected in the next round.
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: aiResponse.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,30 @@ 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
+
10792
+ ## Knowledge in Skills (apply to EVERY sub-skill workflow)
10793
+
10794
+ **Domain knowledge lives in SKILL.md files, not in prompts.** The
10795
+ deliverable's knowledge (rules, formats, decision logic, procedures) is
10796
+ authored as skills; the executable (agent prompt, workflow step) stays
10797
+ THIN \u2014 role/process only, loading knowledge via "Load [[skill-name]]
10798
+ and follow it". Never write domain knowledge directly into a system
10799
+ prompt or a workflow step's prompt.
10800
+
10801
+ Why: knowledge in prompts cannot be reused, individually verified, or
10802
+ evolved. Knowledge in skills is shared (subSkills), regression-tested
10803
+ ([[eval-verify]]), and improved without touching the executable.
10804
+
10805
+ Applies to every construction path: [[learn-capability]] (skills are
10806
+ the primary output), [[agent-build]] (agent prompt thin, loads skill),
10807
+ [[design-workflow]] (steps reference [[skill-name]] or ref to
10808
+ skill-loading agents). All three follow this single principle.
10809
+
10720
10810
  ## Goal-Driven Validation (apply to EVERY sub-skill workflow)
10721
10811
 
10722
10812
  The agent evaluates goal achievement ITSELF via multi-dimensional test
@@ -10736,6 +10826,29 @@ green = goal achieved (per [[completion-gate]] and [[eval-verify]]).
10736
10826
  The goal model is the acceptance standard \u2014 contentAssertion must
10737
10827
  encode the usable state, not just technical correctness.
10738
10828
 
10829
+ ## Undefined Tasks (outside the skill map)
10830
+
10831
+ If the request does not match any sub-skill workflow: do NOT guess, do
10832
+ NOT refuse, do NOT force-fit an existing flow. Follow the
10833
+ EXPLORE \u2192 PROPOSE \u2192 CONFIRM protocol:
10834
+
10835
+ 1. **Explore** \u2014 inventory before proposing anything:
10836
+ \`list_agents\` / \`load_skills\` (existing assets), \`list_tools\` /
10837
+ \`list_middleware_types\` (capabilities), \`list_connections\`
10838
+ (data sources), eval projects (verification), docs at hand.
10839
+ Goal: know what is reusable and what is missing.
10840
+ 2. **Propose** \u2014 present 2-3 concrete options, each with: what it
10841
+ does, cost, risk, and what it needs (new tools / new skills /
10842
+ approvals).
10843
+ 3. **Confirm** \u2014 the user picks an option or adjusts it. Never
10844
+ execute without a chosen option.
10845
+ 4. **New capability needed?** (new tool type, new skill, new
10846
+ connection) \u2014 include creating it ([[create-skill]] / connection
10847
+ setup) IN the proposed option; never silently proceed without it.
10848
+ 5. **Boundary honesty** \u2014 state clearly what the architect cannot do
10849
+ (e.g. deploy to production, monitor runtime, change frontend),
10850
+ and give the alternative \u2014 never overpromise or silently refuse.
10851
+
10739
10852
  ## Skill Map
10740
10853
  - [[learn-capability]] \u2014 Learn from any source material (user
10741
10854
  description, documents, API specs, conversations, spreadsheets) and
@@ -10802,16 +10915,29 @@ Do NOT use reviewer as:
10802
10915
  verification. If findings show config errors, fix and re-check.`,
10803
10916
  "task-tracking": `---
10804
10917
  name: task-tracking
10805
- description: Manage persistent tasks for agent creation workflows. Use
10806
- manage_task to create parent/subtasks, track progress, and resume
10807
- interrupted runs. Applies to ALL agent building processes.
10918
+ description: Manage persistent tasks with manage_task. Universal rule:
10919
+ once the goal is clear and you know what to do, create the task FIRST
10920
+ then execute. Track parent/subtasks, update status to reflect reality,
10921
+ resume interrupted work. Applies to ANY multi-step agent work \u2014 not
10922
+ just agent building.
10808
10923
  metadata:
10809
10924
  domain: agent-building
10810
10925
  verified: unverified
10811
10926
  ---
10812
10927
  # Task Tracking \u2014 manage_task for Agent Workflows
10813
10928
 
10814
- Every agent creation workflow is a multi-step process \u2014 track it.
10929
+ **Task management is the ongoing record of a goal and its acceptance
10930
+ criteria** \u2014 it answers at any moment: what are we achieving, and what
10931
+ does "done" look like. Create a task when the goal is clear; keep its
10932
+ Objective and Acceptance Criteria current as work proceeds; change
10933
+ status only when the criteria are actually met.
10934
+
10935
+ **Universal principle**: whenever the goal is understood and the work
10936
+ is about to start, create a task BEFORE executing. If you can write an
10937
+ Objective and Acceptance Criteria, it deserves a task. This is not
10938
+ optional and not limited to agent-building \u2014 it applies to any
10939
+ multi-step work (learning, building, modifying skills, fixing, anything
10940
+ with a clear goal).
10815
10941
 
10816
10942
  ## When to create (and when NOT)
10817
10943
 
@@ -10828,13 +10954,32 @@ Do NOT create tasks for:
10828
10954
 
10829
10955
  ## Setup
10830
10956
 
10957
+ **A task is a living record of the GOAL + ACCEPTANCE CRITERIA** \u2014 not a
10958
+ todo label. Every task's description must carry:
10959
+
10960
+ - **Goal Model** \u2014 the full goal model ([[agent-architecture|Goal
10961
+ Model]]): real goal, consumer (who uses the result), usable state
10962
+ (what "done and usable" means concretely)
10963
+ - **Objective** \u2014 one measurable sentence: what result to achieve
10964
+ - **Acceptance Criteria** \u2014 checkboxes that define "done": when ALL
10965
+ are checked, the task is verifiably complete
10966
+
10967
+ The task is created when the goal is confirmed, and its description is
10968
+ CONTINUALLY UPDATED as the work progresses (spec evolves, criteria are
10969
+ met, new criteria emerge). Status changes only when the criteria are
10970
+ truly met \u2014 never as a workaround.
10971
+
10831
10972
  - **Create the parent task when the scope is confirmed** \u2014 before
10832
10973
  starting the first real work phase (probe/design/build):
10833
- \`manage_task create(title: <goal>, description: <summary>, ownerType: "agent")\`
10974
+ \`manage_task create(title: <goal>, description: <Objective + Acceptance Criteria>, ownerType: "agent")\`
10834
10975
  Record the returned parent task id.
10835
10976
  - **Create a subtask per phase** as you start each phase (probe /
10836
10977
  design / build / eval / retro):
10837
- \`manage_task create(title: <phase>, parentId: <parent>, ownerType: "agent")\`
10978
+ \`manage_task create(title: <phase>, description: <Objective + Acceptance Criteria>, parentId: <parent>, ownerType: "agent")\`
10979
+ - **Update the description as work proceeds**: append progress, mark
10980
+ criteria \`[x]\`, revise criteria when the goal model/spec changes.
10981
+ The task tracks the target and its acceptance \u2014 read it to know what
10982
+ "done" means, keep it current so it always reflects reality.
10838
10983
 
10839
10984
  ## Status discipline \u2014 MANDATORY
10840
10985
 
@@ -10965,8 +11110,13 @@ When unsure, use \`show_widget\` for visual comparison.
10965
11110
  - **Follow [[agent-architecture|Goal Model]]** \u2014 establish the goal
10966
11111
  model (real goal / consumer / usable state) and design the agent to
10967
11112
  achieve it; verification is goal-driven ([[agent-architecture|Goal-Driven Validation]]).
10968
- - **NEVER build before confirming.** Design \u2192 ask \u2192 wait for "yes" \u2192
10969
- only then build. No exceptions.
11113
+ - **Follow [[agent-architecture|Knowledge in Skills]]** \u2014 the prompt is
11114
+ thin (role/behavior); domain knowledge lives in SKILL.md which the
11115
+ agent loads ("Load [[skill-name]] and follow it"). Never write
11116
+ domain knowledge directly into a system prompt.
11117
+ - **NEVER build before confirming.** Design \u2192 confirm via
11118
+ \`ask_user_to_clarify\` \u2192 wait for approval \u2192 only then build.
11119
+ No exceptions.
10970
11120
  - **Track with tasks once scope is clear.** After requirements are
10971
11121
  clarified, create the parent task ([[task-tracking]]) before starting
10972
11122
  design. Don't create tasks during clarification.
@@ -10979,18 +11129,23 @@ When unsure, use \`show_widget\` for visual comparison.
10979
11129
 
10980
11130
  ## REACT design steps
10981
11131
 
10982
- 1. Understand the goal (who uses it? inputs? outputs?)
11132
+ 1. **Establish the goal model FIRST** \u2014 real goal / user expectation /
11133
+ consumer / usable state ([[agent-architecture|Goal Model]]); record
11134
+ it in the parent task. Design, build, and verification all derive
11135
+ from it. Only then:
10983
11136
  2. Choose middleware \u2014 call \`list_tools\` and \`list_middleware_types\`
10984
11137
  first. MUST include \`ask_user_to_clarify\` if the agent needs
10985
11138
  confirmation or clarifying questions.
10986
11139
  3. Write the system prompt: role \u2192 workflow \u2192 constraints
10987
11140
  4. Present the design with \`show_widget\`
10988
- 5. Ask for explicit approval \u2014 do NOT build until confirmed
11141
+ 5. Confirm via \`ask_user_to_clarify\` \u2014 do NOT build until approved
10989
11142
  6. Build with \`create_agent\`
10990
11143
 
10991
11144
  ## DEEP_AGENT design steps
10992
11145
 
10993
- 1. Domain analysis \u2014 explain why DEEP_AGENT is the right choice
11146
+ 1. **Establish the goal model FIRST** \u2014 real goal / consumer / usable
11147
+ state, recorded in the parent task ([[agent-architecture|Goal
11148
+ Model]]); then explain why DEEP_AGENT is the right choice
10994
11149
  2. Capability mapping with \`show_widget\`
10995
11150
  3. System prompt emphasizes dynamic todo workflow (analyze \u2192 break
10996
11151
  into todos \u2192 work one at a time \u2192 refine). Middleware: code_eval,
@@ -11142,6 +11297,9 @@ description: Run agent evaluations, interpret results, fix failures, and
11142
11297
  metadata:
11143
11298
  domain: agent-building
11144
11299
  verified: unverified
11300
+ subSkills:
11301
+ - eval-design-tests
11302
+ - eval-run-and-govern
11145
11303
  ---
11146
11304
  # Eval Verify \u2014 Run Evaluations and Upgrade Trust
11147
11305
 
@@ -11158,6 +11316,13 @@ verified: unverified
11158
11316
  parent (eval-{parent-id}) \u2014 see Layered verification below.
11159
11317
  2. \`manage_eval create_case(suiteId, ...)\` per sample or scenario
11160
11318
  Required: inputMessage, steps=[{agent_id}], outputType, contentAssertion
11319
+ **contentAssertion MUST come from the confirmed expected output spec**
11320
+ (learn-capability Phase 2.6) \u2014 never invent expectations at
11321
+ case-writing time. If a needed expectation is not in the spec, extend
11322
+ the spec with user confirmation first.
11323
+ **HARD RULE**: if the target/expected output is unclear at this
11324
+ point, STOP and ask the user (ask_user_to_clarify) \u2014 do not write a
11325
+ case with a guessed expectation.
11161
11326
 
11162
11327
  ## Suites per skill, by source
11163
11328
 
@@ -11167,22 +11332,70 @@ verified: unverified
11167
11332
  (hold-out, never run during fix loop)
11168
11333
  - 0.2 \u2460 \u2192 {skill}-api-verified \u2014 queryability assertion, single step
11169
11334
 
11170
- ## Layered verification (orchestrator + subAgents)
11171
-
11172
- When the design has a parent deep_agent with subAgents (learn-capability
11173
- Phase 2), verification is layered:
11174
- - **Each sub-agent**: its OWN eval project (eval-{sub-agent-id}) \u2014 the
11175
- sub capability is verified independently, with its own fix loop.
11176
- - **The parent agent**: an integration eval project (eval-{parent-id}).
11177
- Integration cases: full end-to-end task input \u2192 parent invokes
11178
- sub-agents \u2192 final aggregated output \u2192 contentAssertion on the final
11179
- result. This verifies ORCHESTRATION (does the parent call the right
11180
- sub-agents in the right order and aggregate correctly).
11181
- - **Parent trust upgrade** requires BOTH: all sub-agent evals pass AND
11182
- the parent's integration eval passes. The parent's metadata
11183
- (verified/source) records this dependency.
11184
- - Independent agents (no parent) keep single-level eval \u2014 no integration
11185
- layer needed.
11335
+ ## Layered verification (orchestrator + components)
11336
+
11337
+ When the design delegates to other agents, verification is layered.
11338
+ "Orchestrator" = a parent deep_agent with subAgents (learn-capability
11339
+ Phase 2) OR a workflow with \`ref\` steps ([[design-workflow]]).
11340
+ "Components" = the subAgents / ref'd agents it calls. Order is MANDATORY:
11341
+ **component evals first, integration second** \u2014 never run the
11342
+ integration eval before every component eval passes.
11343
+
11344
+ - **Each component**: its OWN eval project (eval-{sub-agent-id} /
11345
+ eval-{ref-agent-id}) \u2014 the sub capability is verified independently,
11346
+ with its own fix loop.
11347
+ - **The orchestrator**: an integration eval project (eval-{parent-id} /
11348
+ eval-{workflow-id}). Integration cases: full end-to-end task input \u2192
11349
+ orchestrator invokes components \u2192 final aggregated output \u2192
11350
+ contentAssertion on the final result. This verifies ORCHESTRATION
11351
+ (does the orchestrator call the right components in the right order
11352
+ and aggregate correctly).
11353
+ - **Workflow integration cases** also include branch paths and \`ask\`
11354
+ handling (see Workflow testing below) \u2014 but only AFTER the ref'd
11355
+ agents are independently verified.
11356
+ - **Orchestrator trust upgrade** requires BOTH: all component evals pass
11357
+ AND the orchestrator's integration eval passes. The orchestrator's
11358
+ metadata (verified/source) records this dependency.
11359
+ - Independent agents (no parent, no ref) keep single-level eval \u2014 no
11360
+ integration layer needed.
11361
+
11362
+ ## Workflow testing (WORKFLOW-type agents)
11363
+
11364
+ Workflows compile to the same agent registry and run through the same
11365
+ eval path \u2014 same project naming (eval-{agent-id}), same case structure
11366
+ (inputMessage + steps + contentAssertion). Design differs because the
11367
+ pipeline is DETERMINISTIC:
11368
+
11369
+ - **One case per branch path** \u2014 each if/map/parallel route gets a case
11370
+ whose inputMessage drives it down that path; contentAssertion = the
11371
+ exact output that path must produce (from the expected output spec,
11372
+ Phase 1.5 in [[design-workflow]]).
11373
+ - **Goal-driven dimensions** \u2014 cases cover all four dimensions
11374
+ ([[agent-architecture|Goal-Driven Validation]]), not just happy paths:
11375
+ - Functional correctness \u2014 each branch path produces the right result
11376
+ - Edge robustness \u2014 empty input, if-condition not met, map source
11377
+ empty, malformed data: the pipeline must fail gracefully or take
11378
+ the designed fallback, not crash
11379
+ - Business usability \u2014 output reaches the usable state (usable-state
11380
+ cases come from the confirmed spec, never invented)
11381
+ - Consumer fit \u2014 exact fields/format for system consumers, readable
11382
+ for human consumers
11383
+ - **Data contract cases** \u2014 intermediate \`{{refs}}\` handoffs and
11384
+ \`map\` source shapes are contracts; a contract broken mid-pipeline
11385
+ only surfaces at the end. One case per non-trivial handoff asserting
11386
+ the intermediate output shape (source data + step output).
11387
+ - **\`ask\` steps** \u2014 case interruptPolicy controls them
11388
+ (mode: stop | auto-approve | auto-reject | canned-response):
11389
+ - \`stop\` \u2192 the run pauses at the ask; assert the partial output
11390
+ BEFORE the interaction point
11391
+ - auto-approve / auto-reject / canned-response \u2192 supply the response
11392
+ and continue; assert the flow AFTER the interaction point
11393
+ - \`value\` holds the response text (defaults "\u540C\u610F"/"\u62D2\u7EDD" for
11394
+ approve/reject)
11395
+ - **Exact assertions** \u2014 deterministic pipeline means expected outputs
11396
+ are precise; judge still scores semantics on top.
11397
+ - **Trust upgrade is the same gate** \u2014 [[completion-gate]] applies to
11398
+ workflows: no eval \u2192 stays configured, never verified.
11186
11399
 
11187
11400
  ## Run
11188
11401
 
@@ -11248,11 +11461,39 @@ verified: unverified
11248
11461
  # Design Workflow \u2014 WORKFLOW Agent Design
11249
11462
 
11250
11463
  Use the WORKFLOW type when the process is fully known \u2014 a deterministic
11251
- state machine with pre-defined paths.
11464
+ state machine with pre-defined paths. If the process is NOT fully known
11465
+ (open-ended, needs dynamic decomposition) \u2192 use [[learn-capability]] /
11466
+ [[agent-build]] (REACT / DEEP_AGENT) instead.
11252
11467
  Follow [[agent-architecture|User Interaction Rules]] and
11253
11468
  [[agent-architecture|Goal Model]] \u2014 establish the goal model (real
11254
11469
  goal / consumer / usable state) before designing, and design steps
11255
11470
  that achieve it. Acceptance = workflow outcome meets the usable state.
11471
+ Follow [[agent-architecture|Knowledge in Skills]]: workflow steps
11472
+ orchestrate; domain knowledge lives in SKILL.md. Never write domain
11473
+ knowledge directly into a step's prompt \u2014 load it via [[skill-name]]
11474
+ or delegate to an agent that loads the skill.
11475
+
11476
+ ## CRITICAL RULES
11477
+ - **NEVER build before confirming.** Design \u2192 present the flow as a
11478
+ widget \u2192 discuss step-by-step with the user \u2192 confirm via
11479
+ \`ask_user_to_clarify\` (blocking approval) \u2192 only then call
11480
+ \`create_workflow\`. No exceptions.
11481
+ - **Always visualize the design** \u2014 present with \`show_widget\` as a
11482
+ Flowchart (every step, branch, \`ask\` interaction point) \u2014 never a
11483
+ bare text list (see Visual communication below).
11484
+ - **One decision at a time.** Each message asks exactly one question.
11485
+ - **Track with tasks once scope is clear.** Create the parent task
11486
+ ([[task-tracking]]) before designing; record the expected output spec
11487
+ (Phase 1.5) in it.
11488
+
11489
+ ## Visual communication
11490
+
11491
+ Use \`show_widget\` for all structure explanations \u2014 never ASCII art.
11492
+ | Scenario | What |
11493
+ |----------|------|
11494
+ | Workflow flow | Flowchart (steps, branches, ask points) |
11495
+ | Step-level comparison | Comparison cards |
11496
+ | Data flow / {{refs}} | Flowchart |
11256
11497
 
11257
11498
  ## Phase 0: Load Skills
11258
11499
 
@@ -11262,20 +11503,122 @@ that achieve it. Acceptance = workflow outcome meets the usable state.
11262
11503
 
11263
11504
  ## Phase 1: Design
11264
11505
 
11265
- 1. Analyze the process. Map every step, branch, data dependency.
11266
- 2. Design using the YAML linear DSL (steps, parallel, map, if, ask).
11267
- 3. Present the design as a widget.
11268
- 4. Confirm with user before building.
11506
+ 1. **Establish the goal model FIRST** \u2014 real goal / user expectation /
11507
+ consumer / usable state ([[agent-architecture|Goal Model]]); record
11508
+ it in the parent task. It drives the expected output spec (Phase
11509
+ 1.5) and verification (Phase 4). Then analyze the process: map
11510
+ every step, branch, data dependency.
11511
+ 2. **Choose implementation mode per step \u2014 ASK the user** (present as
11512
+ comparison cards). Each step's logic is either inline or \`ref\`:
11513
+ - **inline prompt** \u2014 logic lives in the step's prompt. Fast, no
11514
+ extra agents. Cost: not reusable, no own tools, verified ONLY via
11515
+ the integration eval. OK for trivial one-off glue steps.
11516
+ - **ref sub-agent** \u2014 the step delegates to a registered agent with
11517
+ its own tools/model/skills (built via [[agent-build]], prompt =
11518
+ "Load [[skill-name]] and follow it"). Reusable, independently
11519
+ verified (Phase 2.6). Use when the step needs tools, non-trivial
11520
+ or reusable logic, or independent verification.
11521
+ Present the per-step choice with trade-offs and let the user
11522
+ decide \u2014 NEVER silently pick inline or ref. When in doubt, ask.
11523
+ 3. **Identify knowledge per step** \u2014 for each step, determine the domain
11524
+ knowledge it needs:
11525
+ - Existing skill covers it \u2192 reference [[skill-name]] in the step
11526
+ - No skill yet, but the knowledge is reusable or non-trivial \u2192
11527
+ plan to create it (Phase 1.5)
11528
+ - Trivial one-off logic \u2192 may stay inline in the prompt (accept the
11529
+ trade-off: it is not reusable or individually verifiable)
11530
+ 4. Design using the YAML linear DSL (steps, parallel, map, if, ask).
11531
+ 5. **Present the design as a Flowchart widget** (\`show_widget\`) \u2014 every
11532
+ step, branch, and \`ask\` interaction point. Walk through it with the
11533
+ user step-by-step (each step's responsibility, branch logic, ask
11534
+ points). CONFIRM via \`ask_user_to_clarify\` \u2014 never build without
11535
+ explicit user approval.
11536
+
11537
+ ## Phase 1.5: Expected Output Specification (mandatory \u2014 goal-driven)
11538
+
11539
+ Define the workflow's EXPECTED OUTPUT SPEC from the goal model BEFORE
11540
+ writing skills or building: what the final outcome looks like, per
11541
+ consumer (0.1.5). This is the acceptance standard \u2014 [[eval-verify]]
11542
+ contentAssertion derives from it. HARD RULE: if the target/expected
11543
+ output is unclear, ask the user \u2014 never guess.
11544
+ Present the spec, confirm with the user, record in the parent task.
11545
+
11546
+ ## Phase 2: Create Skills (for missing knowledge)
11547
+
11548
+ For each planned skill (Phase 1.2): write SKILL.md (frontmatter +
11549
+ body encoding the domain rules). Present each for user approval.
11550
+ When 3+ skills share a domain \u2192 create a MOC ([[domain-moc]]).
11551
+ If a ref step needs an agent \u2192 build it via [[agent-build]] (agent
11552
+ prompt = "Load [[skill-name]] and follow it" \u2014 thin, knowledge in
11553
+ skill). Order: sub-agents/skills first, then the workflow that
11554
+ references them.
11555
+
11556
+ ## Phase 2.6: Verify components FIRST (mandatory)
11557
+
11558
+ Every agent referenced by a \`ref\` step is a component with its OWN
11559
+ independent eval (eval-{ref-agent-id}) \u2014 run it and pass it BEFORE
11560
+ building the integration eval. The workflow cannot be considered tested
11561
+ until: \u2460 each ref'd agent's eval passes independently, \u2461 then the
11562
+ workflow's integration eval (branch paths + ask handling) passes. See
11563
+ [[eval-verify|Layered verification]].
11564
+
11565
+ ## Phase 3: Build
11566
+
11567
+ 1. **Configure middleware & tools for the workflow itself** \u2014 inline
11568
+ steps run on the workflow's own model/tools: call
11569
+ \`list_middleware_types\` first; add what the workflow needs \u2014 skill
11570
+ (if steps load [[skill-name]]), widget, ask_user_to_clarify, etc.
11571
+ Tool filtering via \`allowedTools\`. \`ref\` steps use the ref'd
11572
+ agent's own tools/model \u2014 nothing to configure here. Choose
11573
+ \`modelKey\` only when a specific model is required (default
11574
+ otherwise).
11575
+ 2. Call \`create_workflow\` with \`skillLoaded: true\` \u2014 steps reference
11576
+ [[skill-name]] or \`ref\` to skill-loading agents.
11577
+ 3. Then \`validate_workflow(id)\`.
11578
+
11579
+ ## Phase 4: Test (mandatory \u2014 no eval, no trust tier)
11580
+
11581
+ The authoritative verification is [[eval-verify]] \u2014 cases derive from
11582
+ the expected output spec (Phase 1.5). A workflow without a passing eval
11583
+ stays at "configured" forever \u2014 trust can never upgrade
11584
+ ([[completion-gate]], no skip option).
11585
+
11586
+ **Testing is managed through the eval project (eval-{workflow-id})
11587
+ and its cases \u2014 the same governance as agents.** Temporary or quick
11588
+ checks (ad-hoc runs, previewing behavior) may use [[review-agent]] as
11589
+ an interactive pre-check \u2014 but that is NOT the workflow's test suite:
11590
+ it never upgrades trust and never replaces the eval project. Only the
11591
+ eval project's cases passing determine "tested".
11592
+
11593
+ **Test order \u2014 components first, then integration:**
11594
+ 1. Each \`ref\`'d agent: its OWN eval (eval-{ref-agent-id}) must pass
11595
+ independently (Phase 2.6) \u2014 fix it in isolation, not through the
11596
+ workflow.
11597
+ 2. Then the workflow's integration eval (eval-{workflow-id}): one case
11598
+ per branch path (if/map/parallel); \`ask\` steps via case
11599
+ interruptPolicy (auto-approve/canned-response to test the flow AFTER
11600
+ the pause, stop to test up to the pause); assertions are exact \u2014
11601
+ the pipeline is deterministic.
11602
+
11603
+ Workflow trust upgrade requires BOTH layers passing.
11604
+ [[review-agent]] is an optional cheap pre-check only.
11605
+
11606
+ ## Editing workflows
11607
+
11608
+ Get the current YAML \u2192 present the diff \u2192 confirm with the user \u2192
11609
+ \`update_workflow(id, ...)\`. Never re-create.
11610
+ After ANY change: verified resets to unverified and the eval is re-run
11611
+ ([[eval-verify]]) \u2014 the change is not done until the eval passes again.
11612
+ Deleting: warn if any step \`ref\`s it \u2192 confirm \u2192 \`delete_agent\`.
11269
11613
 
11270
- ## Phase 2: Build
11271
-
11272
- Call \`create_workflow\` with \`skillLoaded: true\`, then
11273
- \`validate_workflow(id)\`.
11274
-
11275
- ## Phase 3: Test
11614
+ ## Metadata
11276
11615
 
11277
- Ask user if they want to test \u2014 the authoritative verification is
11278
- [[eval-verify]]. [[review-agent]] is an optional cheap pre-check only.
11616
+ Always set metadata on workflow creation. At minimum:
11617
+ - verified: "unverified" (upgraded after eval passes)
11618
+ - version: "1.0" (bump on each update)
11619
+ - source: the material name or "user-description"
11620
+ When trust upgrades, update BOTH the skill's verified frontmatter and
11621
+ the workflow's metadata.verified \u2014 they must stay in sync.
11279
11622
 
11280
11623
  ## No edges, state fields, or end step
11281
11624
  The engine auto-generates them. Steps execute top-to-bottom in written
@@ -11501,10 +11844,20 @@ async function resolveConnections(type, connections, tenantId2) {
11501
11844
  throw err;
11502
11845
  }
11503
11846
  }
11504
- async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised, tenantId2) {
11847
+ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised, tenantId2, model) {
11505
11848
  const middlewares = [];
11506
11849
  middlewares.push(createUnknownToolHandlerMiddleware());
11507
11850
  middlewares.push(createModelSelectorMiddleware());
11851
+ middlewares.push(createPatchToolCallsMiddleware());
11852
+ if (model) {
11853
+ middlewares.push(
11854
+ summarizationMiddleware({
11855
+ model,
11856
+ trigger: { tokens: 17e4 },
11857
+ keep: { messages: 6 }
11858
+ })
11859
+ );
11860
+ }
11508
11861
  const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
11509
11862
  const clawConfig = middlewareConfigs.find((m) => m.type === "claw");
11510
11863
  const needsFilesystemBackend = filesystemConfig?.enabled || clawConfig?.enabled;
@@ -11836,7 +12189,7 @@ var ReActAgentGraphBuilder = class {
11836
12189
  const stateSchema2 = createReactAgentSchema(params.stateSchema);
11837
12190
  const middlewareConfigs = params.middleware || [];
11838
12191
  const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
11839
- const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, void 0, params.tenantId);
12192
+ const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, void 0, params.tenantId, params.model);
11840
12193
  return createAgent({
11841
12194
  model: params.model,
11842
12195
  tools,
@@ -11854,17 +12207,16 @@ var ReActAgentGraphBuilder = class {
11854
12207
  import {
11855
12208
  createAgent as createAgent3,
11856
12209
  humanInTheLoopMiddleware as humanInTheLoopMiddleware2,
11857
- anthropicPromptCachingMiddleware,
11858
- summarizationMiddleware
12210
+ anthropicPromptCachingMiddleware
11859
12211
  } from "langchain";
11860
12212
 
11861
12213
  // src/deep_agent_new/middleware/subagents.ts
11862
12214
  import { z as z42 } from "zod/v3";
11863
12215
  import {
11864
- createMiddleware as createMiddleware10,
12216
+ createMiddleware as createMiddleware11,
11865
12217
  createAgent as createAgent2,
11866
12218
  tool as tool40,
11867
- ToolMessage as ToolMessage3,
12219
+ ToolMessage as ToolMessage4,
11868
12220
  humanInTheLoopMiddleware
11869
12221
  } from "langchain";
11870
12222
  import { Command as Command3, getCurrentTaskInput as getCurrentTaskInput2, GraphInterrupt as GraphInterrupt3 } from "@langchain/langgraph";
@@ -13810,7 +14162,7 @@ var AgentInstanceManager = class _AgentInstanceManager {
13810
14162
  var agentInstanceManager = AgentInstanceManager.getInstance();
13811
14163
 
13812
14164
  // src/middlewares/taskMiddleware.ts
13813
- import { createMiddleware as createMiddleware9, tool as tool39 } from "langchain";
14165
+ import { createMiddleware as createMiddleware10, tool as tool39 } from "langchain";
13814
14166
  import { z as z41 } from "zod";
13815
14167
  import { GraphInterrupt as GraphInterrupt2, interrupt as interrupt3 } from "@langchain/langgraph";
13816
14168
  function getRunConfig(config) {
@@ -14118,26 +14470,37 @@ function createTaskMiddleware() {
14118
14470
  });
14119
14471
  }
14120
14472
  };
14121
- return createMiddleware9({
14473
+ return createMiddleware10({
14122
14474
  name: "TaskMiddleware",
14123
14475
  contextSchema,
14124
14476
  wrapModelCall: async (request, handler) => {
14125
14477
  const taskPrompt = `## Task Management
14126
14478
 
14127
- You can use the \`manage_task\` tool to create persistent tasks for user-visible work tracking.
14479
+ You have the \`manage_task\` tool to track work. Task management is the
14480
+ ongoing record of a GOAL and its ACCEPTANCE CRITERIA.
14128
14481
 
14129
- ### When to create a task
14482
+ ### When to create a task (universal rule)
14483
+ - The goal is clear and you are about to start real work \u2192 create the
14484
+ parent task FIRST (with Objective + Acceptance Criteria in the
14485
+ description), then execute. This is a core duty, not optional.
14130
14486
  - The user explicitly asks you to track, manage, or follow up on work
14131
14487
  - The work spans multiple sessions or might need resumption later
14132
14488
  - The user needs to review or approve output before it is considered done
14133
14489
  - There are multiple independent work items the user wants visibility into
14134
14490
 
14135
14491
  ### When NOT to create a task
14492
+ - Goal not yet clear (still clarifying) \u2014 clarify first, then create
14136
14493
  - One-shot lookups or simple Q&A ("what is X?", "search for Y")
14137
14494
  - Internal exploration steps you take to understand the problem (use \`write_todos\` for your execution plan instead)
14138
14495
  - Trivial single-step actions that complete in the same turn
14139
14496
  - Conversational or informational requests with no deliverable
14140
14497
 
14498
+ ### Keep the task current
14499
+ A task is the living record of the goal + its acceptance criteria.
14500
+ Update the description as work proceeds: check off criteria as met,
14501
+ revise criteria when scope changes, append progress. Status changes
14502
+ only when the criteria are truly met.
14503
+
14141
14504
  ### Ownership defaults
14142
14505
  - No params: ownerType defaults to "user" with current user's ID
14143
14506
  - ownerType="agent": auto-fills ownerId from current agent (subtask for yourself)
@@ -14191,6 +14554,29 @@ var taskPlugin = {
14191
14554
  skills: {
14192
14555
  "task-definition": `## Using manage_task
14193
14556
 
14557
+ ### When to create a task (universal rule)
14558
+
14559
+ Create a task BEFORE executing whenever the goal is clear and you know
14560
+ what to do \u2014 not just for long or complex work:
14561
+
14562
+ - Goal is understood and you are about to start real work \u2192 create the
14563
+ parent task FIRST, then execute. The task tracks the work.
14564
+ - Goal is NOT yet clear (still clarifying, gathering requirements) \u2192
14565
+ do NOT create a task yet. Clarify first, create the task once scope
14566
+ is defined.
14567
+ - One-shot lookups, trivial single-step actions, or internal reasoning
14568
+ \u2192 no task needed.
14569
+
14570
+ Rule of thumb: if you can write an Objective and Acceptance Criteria
14571
+ for it, create the task before doing it. Work without a task = work
14572
+ without a contract.
14573
+
14574
+ **A task is the living record of the goal + its acceptance criteria.**
14575
+ Keep the description current as work proceeds: update the Objective
14576
+ when the target evolves, check off criteria as they are met, revise
14577
+ criteria when scope changes. Reading the task always tells you what
14578
+ "done" means; an outdated task is a broken contract.
14579
+
14194
14580
  ### Task description format
14195
14581
 
14196
14582
  When creating a task with manage_task, write the description in this Markdown structure:
@@ -14392,7 +14778,7 @@ function returnCommandWithStateUpdate(result, toolCallId) {
14392
14778
  update: {
14393
14779
  ...stateUpdate,
14394
14780
  messages: [
14395
- new ToolMessage3({
14781
+ new ToolMessage4({
14396
14782
  content: lastMessage?.content || "Task Failed to complete",
14397
14783
  tool_call_id: toolCallId,
14398
14784
  name: "task"
@@ -14585,7 +14971,7 @@ You are executing a persistent task (ID: ${input.taskId}). Use manage_task.updat
14585
14971
  return new Command3({
14586
14972
  update: {
14587
14973
  messages: [
14588
- new ToolMessage3({
14974
+ new ToolMessage4({
14589
14975
  content: `Async task started: ${subagent_thread_id}
14590
14976
  ${description}
14591
14977
  The result will be delivered as a notification when complete. Do not poll.`,
@@ -14619,7 +15005,7 @@ The result will be delivered as a notification when complete. Do not poll.`,
14619
15005
  return new Command3({
14620
15006
  update: {
14621
15007
  messages: [
14622
- new ToolMessage3({
15008
+ new ToolMessage4({
14623
15009
  content: error instanceof Error ? error.message : "Task Failed to complete",
14624
15010
  tool_call_id: config.toolCall.id,
14625
15011
  name: "task"
@@ -14856,7 +15242,7 @@ function createSubAgentMiddleware(options) {
14856
15242
  );
14857
15243
  }
14858
15244
  const effectiveSystemPrompt = allowAsync ? systemPrompt + getAsyncPromptText() : systemPrompt;
14859
- return createMiddleware10({
15245
+ return createMiddleware11({
14860
15246
  name: "subAgentMiddleware",
14861
15247
  tools: allTools,
14862
15248
  wrapModelCall: async (request, handler) => {
@@ -14875,53 +15261,6 @@ ${effectiveSystemPrompt}` : effectiveSystemPrompt;
14875
15261
  });
14876
15262
  }
14877
15263
 
14878
- // src/deep_agent_new/middleware/patch_tool_calls.ts
14879
- import {
14880
- createMiddleware as createMiddleware11,
14881
- ToolMessage as ToolMessage4,
14882
- AIMessage as AIMessage2
14883
- } from "langchain";
14884
- function createPatchToolCallsMiddleware() {
14885
- return createMiddleware11({
14886
- name: "patchToolCallsMiddleware",
14887
- beforeAgent: async (state) => {
14888
- const messages = state.messages;
14889
- if (!messages || messages.length === 0) {
14890
- return;
14891
- }
14892
- const patchedMessages = [];
14893
- for (let i = 0; i < messages.length; i++) {
14894
- const msg = messages[i];
14895
- patchedMessages.push(msg);
14896
- if (AIMessage2.isInstance(msg) && msg.tool_calls != null) {
14897
- for (const toolCall of msg.tool_calls) {
14898
- const correspondingToolMsg = messages.slice(i).find(
14899
- (m) => ToolMessage4.isInstance(m) && m.tool_call_id === toolCall.id
14900
- );
14901
- if (!correspondingToolMsg) {
14902
- const toolMsg = `Tool call ${toolCall.name} with id ${toolCall.id} was cancelled - another message came in before it could be completed.`;
14903
- patchedMessages.push(
14904
- new ToolMessage4({
14905
- content: toolMsg,
14906
- name: toolCall.name,
14907
- tool_call_id: toolCall.id
14908
- })
14909
- );
14910
- }
14911
- }
14912
- }
14913
- }
14914
- if (patchedMessages.length === messages.length) {
14915
- return;
14916
- }
14917
- return {
14918
- messages: patchedMessages.slice(messages.length)
14919
- // only the new ToolMessage patches
14920
- };
14921
- }
14922
- });
14923
- }
14924
-
14925
15264
  // src/deep_agent_new/middleware/date.ts
14926
15265
  import { createMiddleware as createMiddleware12, tool as tool41 } from "langchain";
14927
15266
  import { z as z43 } from "zod";
@@ -17834,36 +18173,20 @@ ${BASE_PROMPT}` : BASE_PROMPT;
17834
18173
  createFilesystemMiddleware({
17835
18174
  backend: filesystemBackend
17836
18175
  }),
17837
- // Subagent middleware: Automatic conversation summarization when token limits are approached
17838
- summarizationMiddleware({
17839
- model,
17840
- trigger: { tokens: 17e4 },
17841
- keep: { messages: 6 }
17842
- }),
17843
18176
  // Subagent middleware: Anthropic prompt caching for improved performance
17844
18177
  anthropicPromptCachingMiddleware({
17845
18178
  unsupportedModelBehavior: "ignore"
17846
18179
  }),
17847
- // Subagent middleware: Patches tool calls for compatibility
17848
- createPatchToolCallsMiddleware(),
17849
18180
  ...customMiddleware
17850
18181
  ],
17851
18182
  defaultInterruptOn: interruptOn,
17852
18183
  subagents,
17853
18184
  generalPurposeAgent: true
17854
18185
  }),
17855
- // Automatically summarizes conversation history when token limits are approached
17856
- summarizationMiddleware({
17857
- model,
17858
- trigger: { tokens: 17e4 },
17859
- keep: { messages: 6 }
17860
- }),
17861
18186
  // Enables Anthropic prompt caching for improved performance and reduced costs
17862
18187
  anthropicPromptCachingMiddleware({
17863
18188
  unsupportedModelBehavior: "ignore"
17864
- }),
17865
- // Patches tool calls to ensure compatibility across different model providers
17866
- createPatchToolCallsMiddleware()
18189
+ })
17867
18190
  ];
17868
18191
  if (interruptOn) {
17869
18192
  middleware.push(humanInTheLoopMiddleware2({ interruptOn }));
@@ -17918,7 +18241,7 @@ var DeepAgentGraphBuilder = class {
17918
18241
  }));
17919
18242
  const middlewareConfigs = params.middleware || [];
17920
18243
  const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
17921
- const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, true, params.tenantId);
18244
+ const middlewares = await createCommonMiddlewares(middlewareConfigs, filesystemBackend, true, params.tenantId, params.model);
17922
18245
  const deepAgent = createDeepAgent({
17923
18246
  tools,
17924
18247
  model: params.model,
@@ -19534,7 +19857,7 @@ var TeamAgentGraphBuilder = class {
19534
19857
  });
19535
19858
  const middlewareConfigs = params.middleware || [];
19536
19859
  let filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
19537
- const middlewares = await createCommonMiddlewares(middlewareConfigs);
19860
+ const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, void 0, void 0, params.model);
19538
19861
  if (!filesystemBackend) {
19539
19862
  filesystemBackend = async (config2) => {
19540
19863
  return new StateBackend(config2);
@@ -19951,7 +20274,7 @@ var WorkflowAgentGraphBuilder = class {
19951
20274
  const checkpointer = getCheckpointSaver("default");
19952
20275
  const tools = params.tools.map((t) => t.executor).filter(Boolean);
19953
20276
  const middlewareConfigs = params.middleware || [];
19954
- const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, false, params.tenantId);
20277
+ const middlewares = await createCommonMiddlewares(middlewareConfigs, void 0, false, params.tenantId, params.model);
19955
20278
  const askMiddlewares = await createCommonMiddlewares([
19956
20279
  {
19957
20280
  id: "ask_user_to_clarify",
@@ -19961,7 +20284,7 @@ var WorkflowAgentGraphBuilder = class {
19961
20284
  enabled: true,
19962
20285
  config: {}
19963
20286
  }
19964
- ], void 0, false);
20287
+ ], void 0, false, void 0, params.model);
19965
20288
  const noWrapMiddlewares = stripWrapModelCallHook(middlewares);
19966
20289
  const noWrapAskMiddlewares = stripWrapModelCallHook(askMiddlewares);
19967
20290
  console.log(`[WF BUILDER] building default agent | toolCount=${tools.length} | middlewareCount=${middlewares.length}`);
@@ -21638,6 +21961,40 @@ Call the \`skill\` tool with skill_name: "agent-architecture" to load the
21638
21961
  authoritative workflow. Never announce that you will follow a skill \u2014
21639
21962
  load it and follow its content. If the load fails, retry once, then report it.
21640
21963
 
21964
+ TASK MANAGEMENT IS A CORE DUTY, not a per-skill option. Whenever the
21965
+ goal is clear and you know what to do, create a task FIRST (manage_task)
21966
+ before executing \u2014 for any multi-step work: learning, building,
21967
+ modifying, fixing, anything with an Objective and Acceptance Criteria.
21968
+ - **Check for duplicates BEFORE creating** \u2014 always manage_task
21969
+ action: "list" first (filter ownerType: "agent"). If a task with the
21970
+ same objective already exists (e.g. from an interrupted session),
21971
+ RESUME it instead of creating a new one.
21972
+ - **Decompose into subtasks** \u2014 after the parent task, create a
21973
+ subtask per work item / phase (e.g. design, build, eval), each with
21974
+ its own Objective + Acceptance Criteria.
21975
+ - **Update on completion** \u2014 every finished subtask and the parent:
21976
+ manage_task update(status: "completed", result: "what was done").
21977
+ Use interrupted/failed with summary/failureReason when blocked or
21978
+ unable. Status must always reflect reality \u2014 never leave a finished
21979
+ task dangling in an in-progress state.
21980
+ See [[task-tracking]].
21981
+ The sub-skills below only ADD their own task details on top of this
21982
+ universal duty.
21983
+
21984
+ BUILD GATES \u2014 hard behavioral requirements, no exceptions, no skipping:
21985
+ - Creating a WORKFLOW ([[design-workflow]]): \u2460 show the design as a
21986
+ Flowchart widget (every step, branch, ask point) \u2461 walk through it
21987
+ step-by-step with the user \u2462 ask inline-vs-ref per step \u2463 CONFIRM via
21988
+ ask_user_to_clarify \u2014 only then call create_workflow.
21989
+ - Creating an AGENT ([[agent-build]]): \u2460 present the design with
21990
+ show_widget \u2461 confirm via ask_user_to_clarify \u2014 only then call
21991
+ create_agent.
21992
+ - Both: if the goal model (real goal / consumer / usable state) is
21993
+ unclear, ask BEFORE designing \u2014 never guess.
21994
+ The skills document WHY and HOW; these gates are the unskippable
21995
+ minimum. If you cannot satisfy a gate (e.g. user says skip), record it
21996
+ and proceed only on the user's explicit instruction.
21997
+
21641
21998
  Your sub-skills (accessible via the MOC or direct loading):
21642
21999
  - [[learn-capability]] \u2014 Learn from any source material \u2192 skills + agents
21643
22000
  - [[agent-build]] \u2014 Design and build single agents (REACT/DEEP_AGENT)
@@ -21802,7 +22159,15 @@ var agentArchitectConfig = {
21802
22159
  id: "task",
21803
22160
  type: "task",
21804
22161
  name: "Task",
21805
- description: "Track learning processes and fix loops with approval gates",
22162
+ 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.",
22163
+ enabled: true,
22164
+ config: {}
22165
+ },
22166
+ {
22167
+ id: "code_eval",
22168
+ type: "code_eval",
22169
+ name: "Code Evaluation",
22170
+ 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).",
21806
22171
  enabled: true,
21807
22172
  config: {}
21808
22173
  },
@@ -25424,6 +25789,15 @@ function parseJudgeVerdict(raw) {
25424
25789
  return { error: error instanceof Error ? error.message : String(error) };
25425
25790
  }
25426
25791
  }
25792
+ var MAX_INTERRUPT_RESUMES = 5;
25793
+ function resolveInterruptResponse(policy, interrupt5) {
25794
+ if (policy.mode === "auto-approve") return policy.value ?? "\u540C\u610F";
25795
+ if (policy.mode === "auto-reject") return policy.value ?? "\u62D2\u7EDD";
25796
+ return policy.value ?? "";
25797
+ }
25798
+ function interruptValueText(value) {
25799
+ return typeof value === "string" ? value : JSON.stringify(value ?? "");
25800
+ }
25427
25801
  var _LatticeEval = class _LatticeEval {
25428
25802
  constructor(config = {}) {
25429
25803
  this.inMemoryLogs = [];
@@ -25490,7 +25864,8 @@ var _LatticeEval = class _LatticeEval {
25490
25864
  return acc;
25491
25865
  }, {});
25492
25866
  }
25493
- async executeAgentStep(step, threadId, inputMessage, files) {
25867
+ async executeAgentStep(step, threadId, inputMessage, files, interruptPolicy) {
25868
+ const hitlEvents = [];
25494
25869
  this.log("Executing agent step", {
25495
25870
  agent_id: step.agent_id,
25496
25871
  thread_id: threadId,
@@ -25508,19 +25883,74 @@ var _LatticeEval = class _LatticeEval {
25508
25883
  };
25509
25884
  const agent = agentInstanceManager.getAgent(agentParams);
25510
25885
  try {
25511
- const result = await agent.invoke({
25512
- input: {
25513
- message: step.override_message || inputMessage,
25514
- files: this.buildFileEntries(files)
25886
+ const stepInput = {
25887
+ message: step.override_message || inputMessage,
25888
+ files: this.buildFileEntries(files)
25889
+ };
25890
+ let result = await agent.invokeWithState({ input: stepInput });
25891
+ let resumeCount = 0;
25892
+ let pendingInterrupt;
25893
+ let interrupts = Array.isArray(result?.__interrupt__) ? result.__interrupt__ : [];
25894
+ while (interrupts.length > 0) {
25895
+ const interrupt5 = interrupts[0];
25896
+ if (!interrupt5) break;
25897
+ const policy = interruptPolicy;
25898
+ if (!policy || policy.mode === "stop" || resumeCount >= MAX_INTERRUPT_RESUMES) {
25899
+ pendingInterrupt = interrupt5;
25900
+ break;
25515
25901
  }
25516
- });
25902
+ const response = resolveInterruptResponse(policy, interrupt5);
25903
+ hitlEvents.push({
25904
+ type: "interrupt",
25905
+ id: interrupt5.id,
25906
+ value: interrupt5.value
25907
+ });
25908
+ this.log("Auto-resolving HITL interrupt", {
25909
+ agent_id: step.agent_id,
25910
+ thread_id: threadId,
25911
+ mode: policy.mode,
25912
+ interrupt_id: interrupt5.id,
25913
+ response,
25914
+ resume_count: resumeCount + 1
25915
+ });
25916
+ result = await agent.invokeWithState({ input: stepInput, command: { resume: response } });
25917
+ hitlEvents.push({
25918
+ type: "interrupt_response",
25919
+ id: interrupt5.id,
25920
+ mode: policy.mode,
25921
+ response
25922
+ });
25923
+ resumeCount++;
25924
+ interrupts = Array.isArray(result?.__interrupt__) ? result.__interrupt__ : [];
25925
+ }
25926
+ if (pendingInterrupt) {
25927
+ hitlEvents.push({
25928
+ type: "interrupt",
25929
+ id: pendingInterrupt.id,
25930
+ value: pendingInterrupt.value
25931
+ });
25932
+ this.log("Agent step interrupted by HITL (human input requested)", {
25933
+ agent_id: step.agent_id,
25934
+ thread_id: threadId,
25935
+ interrupt_id: pendingInterrupt.id,
25936
+ auto_resolved: resumeCount
25937
+ });
25938
+ } else {
25939
+ this.log("Agent step completed", {
25940
+ agent_id: step.agent_id,
25941
+ thread_id: threadId,
25942
+ response_keys: result ? Object.keys(result) : [],
25943
+ auto_resolved: resumeCount
25944
+ });
25945
+ }
25517
25946
  const responseData = { success: true, ...result };
25518
- this.log("Agent step completed", {
25519
- agent_id: step.agent_id,
25520
- thread_id: threadId,
25521
- response_keys: result ? Object.keys(result) : []
25522
- });
25523
- return { threadId, responseData };
25947
+ return {
25948
+ threadId,
25949
+ responseData,
25950
+ interrupted: pendingInterrupt ? true : void 0,
25951
+ interrupt: pendingInterrupt,
25952
+ hitlEvents
25953
+ };
25524
25954
  } catch (error) {
25525
25955
  const message = error instanceof Error ? error.message : String(error);
25526
25956
  this.log("Agent step failed", {
@@ -25583,15 +26013,32 @@ var _LatticeEval = class _LatticeEval {
25583
26013
  });
25584
26014
  let currentThreadId = threadId;
25585
26015
  let lastResponseData = null;
26016
+ let interrupt5;
25586
26017
  for (const step of evalCase.steps) {
25587
26018
  const result = await this.executeAgentStep(
25588
26019
  step,
25589
26020
  currentThreadId,
25590
26021
  evalCase.input.message,
25591
- evalCase.input.files || {}
26022
+ evalCase.input.files || {},
26023
+ evalCase.interruptPolicy
25592
26024
  );
25593
26025
  currentThreadId = result.threadId;
25594
26026
  lastResponseData = result.responseData;
26027
+ for (const evt of result.hitlEvents) {
26028
+ if (evt.type === "interrupt") {
26029
+ this.lastMessages.push({
26030
+ role: "interrupt",
26031
+ content: `HITL \u6682\u505C\uFF1AAgent \u8BF7\u6C42\u4EBA\u5DE5\u8F93\u5165 \u2014 ${interruptValueText(evt.value)}`,
26032
+ id: evt.id
26033
+ });
26034
+ } else {
26035
+ this.lastMessages.push({
26036
+ role: "interrupt_response",
26037
+ content: `\u5DF2\u81EA\u52A8\u54CD\u5E94\uFF08\u6D4B\u8BD5\u7B56\u7565 ${evt.mode}\uFF09\uFF1A${evt.response}`,
26038
+ id: evt.id
26039
+ });
26040
+ }
26041
+ }
25595
26042
  const existingIds = new Set(this.lastMessages.map((m) => m.id).filter(Boolean));
25596
26043
  if (result.responseData?.messages && Array.isArray(result.responseData.messages)) {
25597
26044
  for (const msg of result.responseData.messages) {
@@ -25613,6 +26060,13 @@ var _LatticeEval = class _LatticeEval {
25613
26060
  } else {
25614
26061
  content = String(msg.content || "");
25615
26062
  }
26063
+ if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
26064
+ const toolCallStr = msg.tool_calls.map(
26065
+ (tc) => `tool_call: ${tc.name}(${JSON.stringify(tc.args ?? {})})`
26066
+ ).join("\n");
26067
+ content = content ? `${content}
26068
+ ${toolCallStr}` : toolCallStr;
26069
+ }
25616
26070
  this.lastMessages.push({
25617
26071
  role,
25618
26072
  content,
@@ -25624,13 +26078,21 @@ var _LatticeEval = class _LatticeEval {
25624
26078
  }
25625
26079
  }
25626
26080
  }
26081
+ if (result.interrupted) {
26082
+ interrupt5 = result.interrupt;
26083
+ this.log("Case paused for HITL \u2014 remaining steps skipped; judge will evaluate the pause", {
26084
+ case_id: evalCase.caseId,
26085
+ interrupt_id: interrupt5?.id
26086
+ });
26087
+ break;
26088
+ }
25627
26089
  }
25628
26090
  this.log("All agent steps completed", {
25629
26091
  case_id: evalCase.caseId,
25630
26092
  final_thread_id: currentThreadId,
25631
26093
  message_count: this.lastMessages.length
25632
26094
  });
25633
- const finalOutput = this.extractFinalMessage(lastResponseData);
26095
+ const finalOutput = interrupt5 ? typeof interrupt5.value === "string" ? interrupt5.value : JSON.stringify(interrupt5.value ?? "") : this.extractFinalMessage(lastResponseData);
25634
26096
  this.lastFinalOutput = finalOutput;
25635
26097
  const trajectory = this.buildTrajectory();
25636
26098
  this.log("Final output extracted", {
@@ -25689,6 +26151,8 @@ ${rubricsSection}
25689
26151
  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
25690
26152
  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
25691
26153
  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
26154
+ 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
26155
+ 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
25692
26156
 
25693
26157
  # \u8F93\u51FA\u683C\u5F0F\uFF08\u4EC5JSON\uFF09
25694
26158
  \u4F60\u5FC5\u987B\u4EC5\u4EE5 JSON \u683C\u5F0F\u56DE\u590D\uFF0C\u7ED3\u6784\u5982\u4E0B\uFF1A
@@ -25843,7 +26307,9 @@ ${rubricsSection}
25843
26307
  pass,
25844
26308
  final_score: finalScore,
25845
26309
  dimension_results: dimensionResults,
25846
- summary: parsedResult.summary || testResultContent
26310
+ summary: parsedResult.summary || testResultContent,
26311
+ interrupted: interrupt5 ? true : void 0,
26312
+ interrupt: interrupt5 ? { id: interrupt5.id, value: interrupt5.value } : void 0
25847
26313
  };
25848
26314
  }
25849
26315
  };
@@ -25858,6 +26324,8 @@ async function evaluateLatticeCaseWithLogs(evalCase, config) {
25858
26324
  return {
25859
26325
  caseId: evalCase.caseId,
25860
26326
  result,
26327
+ interrupted: result?.interrupted,
26328
+ interrupt: result?.interrupt,
25861
26329
  duration_ms: meta.duration_ms,
25862
26330
  thread_id: meta.thread_id,
25863
26331
  judge_thread_id: meta.judge_thread_id,
@@ -25940,7 +26408,8 @@ function resolveTemplateCase(templateCase, templates) {
25940
26408
  eval: {
25941
26409
  content_assertion: templateCase.eval.content_assertion,
25942
26410
  eval_rubrics: templateCase.eval.eval_rubrics || template.default_case.eval?.eval_rubrics
25943
- }
26411
+ },
26412
+ interruptPolicy: templateCase.interruptPolicy ?? template.default_case.interruptPolicy
25944
26413
  };
25945
26414
  return resolvedCase;
25946
26415
  }
@@ -26006,6 +26475,8 @@ var LatticeEvalSuite = class {
26006
26475
  result: run.result,
26007
26476
  error: run.error,
26008
26477
  error_stack: run.error_stack,
26478
+ interrupted: run.interrupted,
26479
+ interrupt: run.interrupt,
26009
26480
  duration_ms: run.duration_ms,
26010
26481
  thread_id: run.thread_id,
26011
26482
  judge_thread_id: run.judge_thread_id,
@@ -26038,6 +26509,8 @@ var LatticeEvalSuite = class {
26038
26509
  result: run.result,
26039
26510
  error: run.error,
26040
26511
  error_stack: run.error_stack,
26512
+ interrupted: run.interrupted,
26513
+ interrupt: run.interrupt,
26041
26514
  duration_ms: run.duration_ms,
26042
26515
  thread_id: run.thread_id,
26043
26516
  judge_thread_id: run.judge_thread_id,
@@ -26247,24 +26720,29 @@ Running batch: ${this.project.projectName} (${this.getSuiteNames().length} suite
26247
26720
  let total_cases = 0;
26248
26721
  let passed_cases = 0;
26249
26722
  let failed_cases = 0;
26723
+ let interrupted_cases = 0;
26250
26724
  const suites = [];
26251
26725
  for (const [suiteName, caseResults] of results.entries()) {
26252
26726
  const suiteTotal = caseResults.length;
26253
26727
  const suitePassed = caseResults.filter((r) => r.result?.pass).length;
26728
+ const suiteInterrupted = caseResults.filter((r) => r.interrupted).length;
26254
26729
  const suiteFailed = suiteTotal - suitePassed;
26255
26730
  total_cases += suiteTotal;
26256
26731
  passed_cases += suitePassed;
26257
26732
  failed_cases += suiteFailed;
26733
+ interrupted_cases += suiteInterrupted;
26258
26734
  suites.push({
26259
26735
  suiteName,
26260
26736
  total_cases: suiteTotal,
26261
26737
  passed_cases: suitePassed,
26262
26738
  failed_cases: suiteFailed,
26739
+ interrupted_cases: suiteInterrupted,
26263
26740
  cases: caseResults.map((r) => ({
26264
26741
  caseId: r.caseId,
26265
26742
  pass: r.result?.pass,
26266
26743
  final_score: r.result?.final_score,
26267
- error: r.error
26744
+ error: r.error,
26745
+ interrupted: r.interrupted
26268
26746
  }))
26269
26747
  });
26270
26748
  }
@@ -26282,13 +26760,14 @@ Running batch: ${this.project.projectName} (${this.getSuiteNames().length} suite
26282
26760
  total_cases,
26283
26761
  passed_cases,
26284
26762
  failed_cases,
26763
+ interrupted_cases,
26285
26764
  pass_rate: total_cases > 0 ? passed_cases / total_cases : 0
26286
26765
  },
26287
26766
  suites
26288
26767
  };
26289
26768
  console.log(`
26290
26769
  === Summary ===`);
26291
- 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)}%`);
26770
+ 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)}%`);
26292
26771
  return { batch_id, results, report };
26293
26772
  }
26294
26773
  };
@@ -28237,10 +28716,21 @@ Write assertions as objective, verifiable natural language:
28237
28716
  - steps: [{agent_id: "a"}, {agent_id: "b", override_message: "Based on..."}] for chain
28238
28717
  - outputType: "message_content" or "file_content"
28239
28718
 
28719
+ ## Designing HITL Cases
28720
+ 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:
28721
+
28722
+ - 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.
28723
+ - interruptPolicy: {mode: "auto-reject"} \u2014 inject "\u62D2\u7EDD"; tests the rejection path.
28724
+ - interruptPolicy: {mode: "canned-response", value: "..."} \u2014 inject an exact human reply; tests behavior under a specific response.
28725
+ - 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).
28726
+
28727
+ Choose per the assertion: if the assertion describes what happens AFTER the human input, you MUST set an auto-resolve policy.
28728
+
28240
28729
  ## Checklist
28241
28730
  1. Check existing assets with read_eval to avoid duplication
28242
28731
  2. Start with 3-5 high-signal cases
28243
- 3. Confirm with user before calling manage_eval
28732
+ 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
28733
+ 4. Confirm with user before calling manage_eval
28244
28734
  `,
28245
28735
  "eval-run-and-govern": `---
28246
28736
  name: eval-run-and-govern
@@ -28250,8 +28740,11 @@ description: Run agent evaluations, interpret results, diagnose failures, and re
28250
28740
  # Agent Governance Loop
28251
28741
 
28252
28742
  1. Discover project \u2192 read_eval list_projects
28253
- 2. Start evaluation \u2192 run_eval start(projectId) \u2014 ASYNC, may take minutes
28254
- 3. Poll status \u2192 run_eval status(runId) with backoff: 15s, 30s, 60s, max 120s
28743
+ 2. Start evaluation \u2192 run_eval start(projectId) \u2014 SYNCHRONOUS by default:
28744
+ blocks up to ~150s and returns the FINAL RESULTS in one call.
28745
+ Hold-out (validation) runs return aggregates only.
28746
+ 3. If still running (or use wait: false for fire-and-forget) \u2192 poll
28747
+ run_eval status(runId, sleepMs) \u2014 pass sleepMs to pace (15s, 30s, 60s, max 120s)
28255
28748
  4. If runnerAlive=false and status=running \u2192 ORPHANED (gateway restart).
28256
28749
  resume(runId) marks it failed automatically \u2014 then start a new run.
28257
28750
  5. Get results \u2192 read_eval get_run_results(runId) for per-case dimension scores
@@ -28320,10 +28813,12 @@ function sanitize(obj) {
28320
28813
  }
28321
28814
  function aggregateHoldoutResults(results) {
28322
28815
  const passed = results.filter((r) => r.pass).length;
28816
+ const interrupted = results.filter((r) => r.interrupted).length;
28323
28817
  return {
28324
28818
  holdout: true,
28325
28819
  passedCases: passed,
28326
28820
  failedCases: results.length - passed,
28821
+ interruptedCases: interrupted,
28327
28822
  passRate: results.length > 0 ? passed / results.length : 0,
28328
28823
  totalCases: results.length
28329
28824
  };
@@ -28394,13 +28889,8 @@ function createReadEvalTool() {
28394
28889
  if (!run) return JSON.stringify({ success: false, error: "Run not found" });
28395
28890
  const results = await store.getResultsByRun(tid, input.runId);
28396
28891
  if (run.holdout) {
28397
- const passed = results.filter((r) => r.pass).length;
28398
28892
  data = {
28399
- holdout: true,
28400
- passedCases: passed,
28401
- failedCases: results.length - passed,
28402
- passRate: results.length > 0 ? passed / results.length : 0,
28403
- totalCases: results.length,
28893
+ ...aggregateHoldoutResults(results),
28404
28894
  message: "Hold-out run \u2014 per-case results withheld. Only aggregates are available."
28405
28895
  };
28406
28896
  } else {
@@ -28435,6 +28925,7 @@ ACTIONS:
28435
28925
  - get_run_results(runId) \u2014 per-case results with dimension scores.
28436
28926
  For HOLD-OUT (validation-only) runs: returns AGGREGATES ONLY
28437
28927
  (passRate, counts) \u2014 per-case details are withheld by design.
28928
+ Cases paused for human input (HITL) carry interrupted=true and are judged \u2014 the judge evaluates whether pausing was correct business behavior.
28438
28929
  - get_project_report(projectId) \u2014 aggregated stats across all runs`,
28439
28930
  schema: schema6
28440
28931
  }
@@ -28465,7 +28956,11 @@ function createManageEvalTool() {
28465
28956
  steps: z66.array(z66.object({ agent_id: z66.string(), override_message: z66.string().optional() })).optional(),
28466
28957
  outputType: z66.enum(["file_content", "message_content"]).optional(),
28467
28958
  contentAssertion: z66.string().optional(),
28468
- rubrics: z66.array(z66.object({ name: z66.string(), weight: z66.number(), description: z66.string() })).optional()
28959
+ rubrics: z66.array(z66.object({ name: z66.string(), weight: z66.number(), description: z66.string() })).optional(),
28960
+ interruptPolicy: z66.object({
28961
+ 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"),
28962
+ value: z66.string().optional().describe("Response to inject (defaults: \u540C\u610F / \u62D2\u7EDD; required for canned-response)")
28963
+ }).optional().describe("Optional for create_case/update_case \u2014 how HITL interrupts are handled")
28469
28964
  });
28470
28965
  return tool62(
28471
28966
  async (input, exeConfig) => {
@@ -28523,7 +29018,8 @@ function createManageEvalTool() {
28523
29018
  steps: input.steps,
28524
29019
  outputType: input.outputType,
28525
29020
  contentAssertion: input.contentAssertion,
28526
- rubrics: input.rubrics
29021
+ rubrics: input.rubrics,
29022
+ interruptPolicy: input.interruptPolicy
28527
29023
  });
28528
29024
  break;
28529
29025
  case "update_case":
@@ -28531,7 +29027,8 @@ function createManageEvalTool() {
28531
29027
  inputMessage: input.inputMessage,
28532
29028
  contentAssertion: input.contentAssertion,
28533
29029
  steps: input.steps,
28534
- rubrics: input.rubrics
29030
+ rubrics: input.rubrics,
29031
+ interruptPolicy: input.interruptPolicy
28535
29032
  });
28536
29033
  break;
28537
29034
  case "delete_case":
@@ -28556,9 +29053,13 @@ Project: create_project(name, description?, judgeModelKey?, concurrency?) | upda
28556
29053
  **When creating a project from within a workspace, the workspace/project context is
28557
29054
  automatically bound \u2014 eval runs will execute in the same workspace.**
28558
29055
  Suite: create_suite(projectId, name) | update_suite | delete_suite
28559
- Case: create_case(suiteId, inputMessage, steps, outputType, contentAssertion, inputFiles?, rubrics?)
29056
+ Case: create_case(suiteId, inputMessage, steps, outputType, contentAssertion, inputFiles?, rubrics?, interruptPolicy?)
28560
29057
  steps is [{agent_id, override_message?}]. outputType is "file_content" or "message_content".
28561
- rubrics is [{name, weight, description}]. | update_case | delete_case`,
29058
+ rubrics is [{name, weight, description}]. | update_case | delete_case
29059
+ interruptPolicy: {mode: "stop"|"auto-approve"|"auto-reject"|"canned-response", value?} \u2014 how HITL interrupts are handled:
29060
+ stop (default): case pauses at the human-input request; the judge evaluates the pause as business behavior.
29061
+ auto-approve / auto-reject: the runner injects approval/rejection and tests the FULL flow after the pause.
29062
+ canned-response: injects the exact value (simulates a specific human reply).`,
28562
29063
  schema: schema6
28563
29064
  }
28564
29065
  );
@@ -28672,6 +29173,8 @@ ACTIONS:
28672
29173
  - start(projectId, suiteIds?, caseIds?, wait?) \u2014 begin evaluation (optionally only the listed suites, and/or only the listed cases).
28673
29174
  wait=true (DEFAULT): SYNCHRONOUS \u2014 blocks up to ~150s and returns the FINAL RESULTS in one call:
28674
29175
  { status, results } with per-case pass/score (hold-out/validation runs: aggregates only \u2014 per-case details withheld by design).
29176
+ Cases paused for human input (HITL) carry interrupted=true and ARE judged \u2014 the judge evaluates whether requesting
29177
+ the human was the correct behavior (assertions like "must approve first" PASS; "must be autonomous" FAIL). interruptedCases counts these.
28675
29178
  If the run exceeds 150s: returns { runId, status: "running" } \u2014 NOT an error \u2014 then poll with status/resume until completed.
28676
29179
  wait=false: fire-and-forget \u2014 returns { runId } immediately; poll with status.
28677
29180
  - status(runId, sleepMs?) \u2014 sleep sleepMs first (to pace polling), then return current status + runnerAlive flag:
@@ -28977,67 +29480,64 @@ verification choice, then start benchmarking.
28977
29480
 
28978
29481
  ## Task Tracking \u2014 see [[task-tracking]]
28979
29482
 
28980
- **Create the parent task when the task is actually defined** \u2014 after
28981
- Phase 0 clarification is complete and the user confirmed the path
28982
- (fresh vs incremental). Do NOT create tasks during clarification:
28983
- while asking questions (0.0-0.4) you don't know what the task is yet.
28984
- Once the scope is clear (end of 0.5), that is the moment to create:
28985
- manage_task create("Learn [material]", ownerType: "agent"). Then a
28986
- subtask per phase as you start it. Update status to reflect reality \u2014
28987
- never mark a subtask completed while eval fails. Resume interrupted
28988
- runs with manage_task list.
29483
+ **Universal principle**: once the goal is clear and you know what to
29484
+ do, create the parent task BEFORE executing (manage_task create, see
29485
+ [[task-tracking]]). In this workflow: after Phase 0 clarification
29486
+ completes and the user confirmed the path (end of 0.5), create the
29487
+ parent task; then a subtask per phase as you start it. The parent task
29488
+ description carries the GOAL MODEL (0.1.5) as Objective + Acceptance
29489
+ Criteria; the expected output spec (2.6) updates the criteria. Update
29490
+ status to reflect reality \u2014 never mark a subtask completed while eval
29491
+ fails. Resume interrupted runs with manage_task list.
28989
29492
 
28990
29493
  Widgets: call \`load_guidelines\` ONCE before your first \`show_widget\`
28991
29494
  (show_widget hard-requires it), then reuse.
28992
29495
 
28993
29496
  ---
28994
29497
 
28995
- ## Phase 1: Probe (material-dependent)
28996
-
28997
- The probing strategy depends on the material type from Phase 0.0:
28998
-
28999
- **User-description material**: the requirements come from the
29000
- conversation itself \u2014 no probing needed. Extract the agent's goal,
29001
- inputs, outputs, and constraints from what the user described. Go
29002
- straight to design.
29003
-
29004
- **Document material** (PDF / spec / manual):
29005
- If the engine was chosen in Phase 0 (0.3 \u2460-\u2464): skip the comparison \u2014
29006
- parse directly with \`parse_document\` using the chosen engine
29007
- (file_path, engine, output_path per file).
29008
- Otherwise: run the document-parser-benchmark subagent via \`task\` on each file.
29009
- Collect engine scores, parsed output (via \`read_file\`), and feature signatures.
29010
-
29011
- **Engine selection IS distilled knowledge, not just setup.** For document
29012
- material, the benchmark answers "which engine parses THIS document (or
29013
- this class of document) best?" \u2014 that answer is knowledge that drives
29014
- the whole rest of the run:
29015
- - **Builds the agent**: the chosen engine's \`parse_document\` goes into
29016
- the production agent's middleware/tools.
29017
- - **Designs the tests**: the chosen engine's parsed output becomes the
29018
- baseline input for eval cases \u2014 cases feed parsed output to the agent
29019
- and assert correct extraction from it.
29020
- - **Seeds the skill**: the feature signature (tables? scans? mixed
29021
- zh/en?) plus the winning engine becomes a skill note ("for PO PDFs
29022
- with tables, use textin") reusable for future similar documents.
29023
- So when the user wants an agent whose purpose is document PARSING (not
29024
- extraction), the workflow is the same: benchmark to learn the engine
29025
- choice, then build the agent around that engine and test against its
29026
- output. Do NOT treat parsing as a pure tool-assembly task \u2014 the engine
29027
- choice is unknown knowledge until probed.
29028
-
29029
- **API spec material**: read the spec directly \u2014 no parsing engine needed.
29030
- Extract endpoints, schemas, request/response examples from the text.
29031
-
29032
- **Conversation material**: extract the workflow, decisions, and corrections
29033
- from the conversation context \u2014 no parsing engine needed.
29034
-
29035
- **Spreadsheet material**: parse cells directly \u2014 structured data needs
29036
- no engine comparison.
29037
- If verification will happen (0.2 \u2460 or \u2461): concurrently, \`list_agents\` to
29038
- discover existing agents with relevant capabilities (see \xA75).
29039
- For \u2460, look for agents with data-access tools (SQL / API). For \u2461, look
29040
- for agents with independence.
29498
+ ## Phase 1: Explore (goal-driven path finding)
29499
+
29500
+ The goal model (0.1.5) is set. Now EXPLORE how to achieve it \u2014 actively
29501
+ hunt for the path, do not passively read. Three exploration fronts:
29502
+
29503
+ **A. Existing assets (reuse, don't rebuild):**
29504
+ - \`list_agents\` / \`load_skills\` \u2192 are there existing agents or skills
29505
+ that already do part of this? Reuse them (update_agent if needed)
29506
+ instead of building from scratch. This is a goal-relevant check, not
29507
+ a materials step.
29508
+ - \`list_tools\` / \`list_middleware_types\` \u2192 what capabilities exist
29509
+ that the goal needs (parsing, data access, browser...)?
29510
+ - \`list_connections\` \u2192 are the data sources the goal depends on
29511
+ already connected?
29512
+ - If verification will happen (0.2 \u2460 or \u2461): concurrently discover
29513
+ executor candidates (see \xA75). \u2460 \u2192 data-access tools; \u2461 \u2192 independence.
29514
+
29515
+ **B. Material probing (by material type):**
29516
+ - **User-description**: the requirements come from the conversation.
29517
+ Extract goal, inputs, outputs, constraints \u2014 then explore the
29518
+ implementation path (A + feasibility): what assets exist, what tools
29519
+ are needed, what blockers stand between the goal and its achievement.
29520
+ - **Document** (PDF / spec / manual): benchmark engines as needed \u2014
29521
+ parse directly with the chosen engine (0.3 \u2460-\u2464) or run
29522
+ document-parser-benchmark. Engine selection IS distilled knowledge:
29523
+ it builds the agent (engine's parse_document into middleware), seeds
29524
+ the skill (feature signature + winning engine), and designs the tests
29525
+ (engine output as case baseline input).
29526
+ - **API spec**: read directly \u2014 endpoints, schemas, examples.
29527
+ - **Conversation**: extract workflow, decisions, corrections.
29528
+ - **Spreadsheet**: parse cells directly.
29529
+
29530
+ **C. Feasibility (path blockers):**
29531
+ - What stands between the goal and achievement? Missing tools, missing
29532
+ connections, data access, permission constraints, ambiguous
29533
+ requirements.
29534
+ - Does the goal require orchestration (\u2192 Phase 2 split decision)?
29535
+ - Surface these in the recommendation (Phase 1.5) \u2014 the user decides
29536
+ the path, informed by what exploration found.
29537
+
29538
+ Exploration is COMPLETE when you can answer: what exists to reuse,
29539
+ what must be built, what tools/connections are needed, and what blocks
29540
+ the goal. Do not go to design without this map.
29041
29541
 
29042
29542
  ---
29043
29543
 
@@ -29052,17 +29552,21 @@ candidate and assess (Validation Agent Design \xA70) \u2014 state which are
29052
29552
  usable and which are not, with reasons. For \u2460, the executor needs data
29053
29553
  tools + independence. For \u2461, independence only. If no candidate fits,
29054
29554
  plan to build one via \xA75.
29055
- Present probe results as widget, then MUST call
29555
+ Present the EXPLORATION map as widget \u2014 what exists to reuse, what
29556
+ must be built, tools/connections needed, blockers found \u2014 then
29557
+ recommend the IMPLEMENTATION PATH: reuse existing X, build new Y,
29558
+ split or single agent (Phase 2 input). MUST call
29056
29559
  \`ask_user_to_clarify\` NOW:
29057
29560
  {
29058
29561
  "questions": [{
29059
- "question": "Confirm the recommendation?",
29562
+ "question": "Confirm the recommended path?",
29060
29563
  "options": ["Confirm", "Adjust"],
29061
29564
  "type": "single",
29062
29565
  "required": true
29063
29566
  }]
29064
29567
  }
29065
- Skills planning belongs to Phase 2 \u2014 this phase presents data, not plans.
29568
+ Skills planning belongs to Phase 2 \u2014 this phase presents the path, not
29569
+ the detailed plan.
29066
29570
 
29067
29571
  ---
29068
29572
 
@@ -29130,9 +29634,53 @@ user-description material this IS the core phase; for material-based
29130
29634
  learning it designs the agent that runs the learned skill. Agent
29131
29635
  metadata (verified/version/source) must be set on creation.
29132
29636
 
29637
+ ## Phase 2.6: Expected Output Specification (mandatory \u2014 goal-driven)
29638
+
29639
+ **Expectations come FIRST, before writing the skill.** You cannot write
29640
+ a skill (or test cases) without a target. Define the expected output
29641
+ specification from the goal model (0.1.5: real goal / consumer / usable
29642
+ state) BEFORE Phase 3:
29643
+
29644
+ **HARD RULE \u2014 never guess the target.** If the goal, the expected
29645
+ output, the consumer, or the usable state is unclear at ANY point
29646
+ before writing test cases, you MUST ask the user via
29647
+ \`ask_user_to_clarify\` \u2014 do NOT proceed with an assumed expectation.
29648
+ A test case written against a guessed expectation is worthless: it
29649
+ validates the wrong thing. When in doubt, ask.
29650
+
29651
+ Per skill, define the EXPECTED OUTPUT SPEC (based on intent 0.1 and
29652
+ consumer 0.1.5):
29653
+ - Extract data \u2192 expected fields (names, types, formats), required vs
29654
+ optional, output structure (JSON schema shape, table columns)
29655
+ - Validate rules \u2192 expected judgment outcomes (pass/fail conditions),
29656
+ boundary values, and the reason format
29657
+ - Execute workflow \u2192 expected step sequence, decision points, final
29658
+ outcome shape
29659
+ - Answer knowledge \u2192 expected answer form (with/without sources,
29660
+ length, structure)
29661
+
29662
+ This spec IS the acceptance standard. Phase 4 contentAssertion must be
29663
+ derived from it (not invented at case-writing time). Present the
29664
+ expected output spec to the user and MUST call \`ask_user_to_clarify\`
29665
+ NOW per skill:
29666
+ {
29667
+ "questions": [{
29668
+ "question": "Confirm the expected output spec for {skill-name}?",
29669
+ "options": ["Confirm", "Adjust"],
29670
+ "type": "single",
29671
+ "required": true,
29672
+ "allowOther": true
29673
+ }]
29674
+ }
29675
+ Record the confirmed spec in the parent task description. This replaces
29676
+ guess-then-confirm: the skill is written TO MEET the spec, and test
29677
+ cases assert AGAINST the spec \u2014 no expectation is invented later.
29678
+
29133
29679
  ## Phase 3: Create Skills
29134
29680
 
29135
- Write SKILL.md to \`/root/.agents/skills/{name}/SKILL.md\` one at a time.
29681
+ Write SKILL.md to \`/root/.agents/skills/{name}/SKILL.md\` one at a time,
29682
+ designed TO MEET the expected output spec confirmed in Phase 2.6 \u2014 the
29683
+ skill encodes how to produce the spec's expected output.
29136
29684
  Show the skill content in text first, then MUST call
29137
29685
  \`ask_user_to_clarify\` NOW per skill:
29138
29686
  {
@@ -29426,7 +29974,8 @@ This learning loop adds its own scenario rules:
29426
29974
  - Business usability (output reaches the goal's "usable state")
29427
29975
  - Consumer fit (format/contract satisfies who uses the result)
29428
29976
  contentAssertion must encode the usable state from the goal model
29429
- (0.1.5), not just technical correctness.
29977
+ (0.1.5), derived from the EXPECTED OUTPUT SPEC (Phase 2.6) \u2014 never
29978
+ invented at case-writing time.
29430
29979
 
29431
29980
  1. One suite per skill per source: cases test "can this skill do it" \u2014
29432
29981
  never mix skills in one suite
@@ -29469,6 +30018,12 @@ Learning-specific suite guidance:
29469
30018
  - User-description material: {skill}-requirement-derived \u2014 cases from
29470
30019
  user's described requirements
29471
30020
 
30021
+ **Case expectations come from the confirmed spec** (Phase 2.6): the
30022
+ contentAssertion of every case must be derived from the expected output
30023
+ spec, NOT invented at case-writing time. If a case needs an expectation
30024
+ not in the spec, go back and extend the spec with user confirmation
30025
+ first \u2014 never guess expectations on the fly.
30026
+
29472
30027
  [[completion-gate]] applies \u2014 eval must pass before declaring done.
29473
30028
 
29474
30029
  ## Phase 5: Retrospective