@exaudeus/workrail 3.36.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/config-file.js +2 -0
- package/dist/console-ui/assets/{index-n8cJrS4v.js → index-o-p__sHJ.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/daemon/workflow-runner.d.ts +1 -0
- package/dist/daemon/workflow-runner.js +3 -6
- package/dist/manifest.json +23 -15
- package/dist/trigger/notification-service.d.ts +42 -0
- package/dist/trigger/notification-service.js +164 -0
- package/dist/trigger/trigger-listener.js +7 -1
- package/dist/trigger/trigger-router.d.ts +3 -1
- package/dist/trigger/trigger-router.js +4 -1
- package/docs/design/agent-behavior-patterns-discovery.md +312 -0
- package/docs/design/agent-engine-communication-discovery.md +390 -0
- package/docs/design/agent-loop-architecture-alternatives-discovery.md +531 -0
- package/docs/design/agent-loop-error-handling-contract.md +238 -0
- package/docs/design/complete-step-approach-validation-discovery.md +344 -0
- package/docs/design/daemon-stuck-detection-discovery.md +174 -0
- package/docs/design/mcp-server-disconnect-discovery.md +245 -0
- package/docs/design/mcp-server-epipe-crash.md +198 -0
- package/docs/design/notification-design-candidates.md +131 -0
- package/docs/design/notification-design-review.md +84 -0
- package/docs/design/notification-implementation-plan.md +181 -0
- package/docs/design/spawn-agent-failure-modes.md +161 -0
- package/docs/design/spawn-agent-result-handling-implementation-plan.md +186 -0
- package/docs/design/stdio-simplification-design-candidates.md +341 -0
- package/docs/design/stdio-simplification-design-review.md +93 -0
- package/docs/design/stdio-simplification-implementation-plan.md +317 -0
- package/docs/design/structured-output-tools-coexist-findings.md +288 -0
- package/docs/discovery/coordinator-script-design.md +745 -0
- package/docs/discovery/coordinator-ux-discovery.md +471 -0
- package/docs/discovery/spawn-agent-failure-modes.md +309 -0
- package/docs/discovery/workflow-selection-for-discovery-tasks.md +336 -0
- package/docs/discovery/worktrain-status-briefing.md +325 -0
- package/docs/discovery/worktrain-status-design-candidates.md +202 -0
- package/docs/discovery/worktrain-status-design-review-findings.md +86 -0
- package/docs/ideas/backlog.md +608 -0
- package/docs/ideas/daemon-structured-output-vs-tool-calls.md +344 -0
- package/docs/ideas/design-candidates-backlog-consolidation.md +85 -0
- package/docs/ideas/design-review-findings-backlog-consolidation.md +39 -0
- package/docs/ideas/implementation_plan_backlog_consolidation.md +117 -0
- package/docs/plans/authoring-doc-staleness-enforcement-candidates.md +251 -0
- package/docs/plans/authoring-doc-staleness-enforcement-review.md +99 -0
- package/docs/plans/authoring-doc-staleness-enforcement.md +463 -0
- package/package.json +1 -1
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
# Agent Loop Architecture Alternatives: Discovery
|
|
2
|
+
|
|
3
|
+
**Status:** Complete
|
|
4
|
+
**Date:** 2026-04-18
|
|
5
|
+
**Goal:** What architectural alternatives to tool calls exist for the WorkRail daemon agent loop? We own the full agent loop and are not constrained by MCP protocol -- what patterns from research/production autonomous agent systems could be superior?
|
|
6
|
+
|
|
7
|
+
> **Artifact strategy:** Human-readable reference. Execution truth lives in WorkRail session notes and context variables. This file is for readability only.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Context / Ask
|
|
12
|
+
|
|
13
|
+
WorkRail owns its full agent loop (`src/daemon/agent-loop.ts` + `workflow-runner.ts`). The current loop uses Anthropic's tool_use protocol: LLM outputs a `tool_use` block, daemon executes the tool, returns a `tool_result` block, loop continues. This is the standard MCP-compatible pattern.
|
|
14
|
+
|
|
15
|
+
The question is whether this is the *best* pattern given that WorkRail is not constrained by MCP protocol when running in daemon mode. We own the full loop. What patterns from research and production agent systems could be superior, and how would each integrate with WorkRail's workflow engine (step progression, assessment gates, continueTokens)?
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Path Recommendation: `landscape_first`
|
|
20
|
+
|
|
21
|
+
The dominant need is landscape grounding -- understanding what patterns exist and how each maps to WorkRail's specific constraints (HMAC token protocol, step sequencer, `isComplete` signal). The code is already understood; the research patterns are what need enumeration and ranking. `full_spectrum` reframe would add marginal value -- the problem is concrete enough.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Constraints / Anti-goals
|
|
26
|
+
|
|
27
|
+
**Hard constraints:**
|
|
28
|
+
- HMAC token protocol is immutable -- `continueToken` must be round-tripped exactly
|
|
29
|
+
- `isComplete: true` is the termination signal from `continue_workflow` -- any alternative must bridge this
|
|
30
|
+
- Existing workflows must run unchanged -- any alternative must accept current workflow step format
|
|
31
|
+
- `steer()` fires after each tool batch and is the injection point for next-step prompts -- any alternative must preserve or replace this
|
|
32
|
+
- Sequential tool execution (workflow tools have ordering requirements -- `continue_workflow` must complete before `Bash` starts next step)
|
|
33
|
+
- No streaming currently (Anthropic `MessageCreateParamsNonStreaming`) -- streaming is an option but adds latency complexity
|
|
34
|
+
|
|
35
|
+
**Anti-goals:**
|
|
36
|
+
- Do not require changes to workflow format or step definitions
|
|
37
|
+
- Do not add a second LLM call per step (cost/latency)
|
|
38
|
+
- Do not break the HMAC enforcement guarantee
|
|
39
|
+
- Do not make tool error recovery worse -- current loop returns `isError: true` tool_result so LLM can recover
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Landscape Packet
|
|
44
|
+
|
|
45
|
+
### Current architecture: tool_use round-trip
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. LLM call with tools registered (continue_workflow, Bash, Read, Write, ...)
|
|
49
|
+
2. LLM response: stop_reason="tool_use", content=[tool_use block(s)]
|
|
50
|
+
3. Daemon executes tools sequentially (agent-loop.ts:_executeTools)
|
|
51
|
+
4. Daemon appends tool_result blocks as user message
|
|
52
|
+
5. Emit turn_end event; workflow-runner.ts subscriber calls steer() with next step
|
|
53
|
+
6. Next LLM call includes full conversation history + new steer message
|
|
54
|
+
7. Repeat until stop_reason="end_turn" + empty steer queue
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Known friction points:**
|
|
58
|
+
- Every step requires at minimum 2 LLM calls: one to decide to call `continue_workflow`, one to receive the next step and proceed
|
|
59
|
+
- Full conversation history grows every turn -- long sessions inflate input token count
|
|
60
|
+
- Tool schema registration happens at startup; tools are generic `AgentTool` objects with JSON Schema
|
|
61
|
+
- The LLM must "decide" to call `continue_workflow` -- it could theoretically forget or deviate
|
|
62
|
+
|
|
63
|
+
### What the backlog says about this question
|
|
64
|
+
|
|
65
|
+
**"Scripts over agent" principle (backlog.md, Apr 15, 2026):**
|
|
66
|
+
> "The agent is expensive, inconsistent, and slow. Scripts are free, deterministic, and instant. Any operation the daemon can perform with a shell script, git command, or API call should be done that way -- not delegated to the LLM."
|
|
67
|
+
|
|
68
|
+
This is the most explicit statement in the codebase about the underlying tension. The current tool_use model gives the LLM agency over *when* to call `continue_workflow`. The principle says: that decision should be deterministic (the daemon drives step transitions), not probabilistic (the LLM decides).
|
|
69
|
+
|
|
70
|
+
**Knowledge graph (backlog.md, Apr 15, 2026):**
|
|
71
|
+
> "every session starts with a full repo sweep... a persistent, derived knowledge graph that agents build incrementally and query instead of sweeping"
|
|
72
|
+
|
|
73
|
+
This directly addresses the memory-augmented pattern -- context compression between sessions, not per-turn.
|
|
74
|
+
|
|
75
|
+
**Workflow complexity routing (backlog.md, Apr 15, 2026):**
|
|
76
|
+
> "Phase 0 (classify): delegate to a cheap subagent... Main agent reviews and accepts/overrides"
|
|
77
|
+
|
|
78
|
+
This is the plan-then-execute pattern: a cheap classifier generates the execution plan, the main agent executes it. Already identified as a direction.
|
|
79
|
+
|
|
80
|
+
### Production/research patterns surveyed
|
|
81
|
+
|
|
82
|
+
Six patterns from research and production autonomous agent systems:
|
|
83
|
+
|
|
84
|
+
1. **ReAct (Reason + Act)** -- Yao et al. 2022; used in LangChain ReActAgent, AutoGPT. LLM generates reasoning trace + action in one structured response. No separate "decide what tool to call" and "execute tool" round-trip.
|
|
85
|
+
|
|
86
|
+
2. **Plan-then-execute** -- OpenAI function calling + planner mode; Devin's task planning layer; EM-LLM planning paper. Agent produces full execution plan (ordered list of actions + expected outcomes), daemon executes the whole plan, returns batch results.
|
|
87
|
+
|
|
88
|
+
3. **Verifier pattern** -- Constitutional AI (Anthropic); self-critique loops in GPT-4 Turbo; Reflexion (Shinn et al. 2023). Separate LLM calls for "what to do" (planner) vs "did it work" (verifier). Main loop never gets stuck in error recovery.
|
|
89
|
+
|
|
90
|
+
4. **Direct structured output / typed schemas** -- OpenAI structured_outputs (2024); Instructor library; Pydantic AI. Instead of `tool_use` protocol with JSON Schema, LLM outputs a strongly-typed response object that the daemon parses directly. No tool definition registration at startup.
|
|
91
|
+
|
|
92
|
+
5. **Agentic streaming** -- Anthropic streaming API; streaming tool use (Claude claude-sonnet-4-5 supports streaming with tool calls); OpenAI streaming function calls. Daemon parses response in real-time and executes actions as they appear in the stream.
|
|
93
|
+
|
|
94
|
+
6. **Memory-augmented loop** -- MemGPT (Packer et al. 2023); Zep memory layer; WorkRail knowledge graph backlog. Agent reads from and writes to structured knowledge store between turns instead of carrying everything in context.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Pattern Analysis
|
|
99
|
+
|
|
100
|
+
### Pattern 1: ReAct (Reason + Act)
|
|
101
|
+
|
|
102
|
+
**How it works in research:**
|
|
103
|
+
LLM outputs structured `Thought: ... Action: ... Observation: ...` traces. Reasoning and action are interleaved in one response. The model commits to an action before seeing the result, reasons about the result, then commits to the next action.
|
|
104
|
+
|
|
105
|
+
**How it would work in WorkRail:**
|
|
106
|
+
|
|
107
|
+
Replace the current `tool_use` output format with a structured JSON response:
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"thought": "The step asks me to read agent-loop.ts and summarize the architecture...",
|
|
111
|
+
"action": "read_file",
|
|
112
|
+
"action_input": { "path": "src/daemon/agent-loop.ts" },
|
|
113
|
+
"is_step_complete": false,
|
|
114
|
+
"notes_so_far": "..."
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Daemon parses this, executes the action, injects the result back, and continues.
|
|
119
|
+
|
|
120
|
+
**What replaces `continue_workflow`:**
|
|
121
|
+
`is_step_complete: true` in the structured output triggers the daemon to call `engine.continueWorkflow()` directly -- not via a tool call. The LLM does not need to know about `continueToken`. The daemon owns token management entirely.
|
|
122
|
+
|
|
123
|
+
**Fit with WorkRail:**
|
|
124
|
+
- HMAC tokens: the LLM never sees them -- daemon manages them. Full enforcement preserved.
|
|
125
|
+
- `isComplete`: daemon reads `is_step_complete` flag; calls `engine.continueWorkflow()` when true.
|
|
126
|
+
- `steer()` replacement: daemon injects next step via `agent.steer()` as before, but the step prompt format changes to match the ReAct output schema.
|
|
127
|
+
- Step injection: the workflow step prompt becomes the "context" frame for the structured output.
|
|
128
|
+
|
|
129
|
+
**Gains:**
|
|
130
|
+
- No tool registration overhead -- tools are hard-coded to the structured output schema
|
|
131
|
+
- Reasoning is explicit and auditable -- the `thought` field is in the session log
|
|
132
|
+
- One LLM call can combine reasoning + action decision + progress tracking
|
|
133
|
+
|
|
134
|
+
**Losses / risks:**
|
|
135
|
+
- Structured output mode requires Anthropic's `tool_choice: { type: "any" }` or a single tool with the full schema -- less flexible than multi-tool setup
|
|
136
|
+
- Error recovery is harder: if `is_step_complete` is true but the work is wrong, the daemon has already advanced. Current tool_use model lets the LLM self-correct via additional tool calls before deciding to advance.
|
|
137
|
+
- Not all Anthropic models support JSON mode reliably for complex schemas; tool_use is more reliable for structured output
|
|
138
|
+
|
|
139
|
+
**Verdict: MEDIUM fit.** Best applied as a hybrid: keep `continue_workflow` as a tool but make the step prompt explicitly ask the LLM to reason before acting. The pure ReAct replacement (no tools) would hurt error recovery.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### Pattern 2: Plan-then-Execute
|
|
144
|
+
|
|
145
|
+
**How it works in research:**
|
|
146
|
+
A planner LLM call produces a complete execution plan (ordered list of actions). A separate executor runs each action in the plan sequentially or in parallel. Results are batched and returned to the planner for verification or re-planning.
|
|
147
|
+
|
|
148
|
+
**How it would work in WorkRail:**
|
|
149
|
+
|
|
150
|
+
At step start, before executing any actions, the daemon makes a "planning" LLM call:
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"plan": [
|
|
154
|
+
{ "step": 1, "action": "read_file", "path": "src/daemon/agent-loop.ts", "expected_outcome": "understand loop structure" },
|
|
155
|
+
{ "step": 2, "action": "read_file", "path": "src/daemon/workflow-runner.ts", "expected_outcome": "understand steer() usage" },
|
|
156
|
+
{ "step": 3, "action": "complete_step", "notes": "..." }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Daemon executes the plan, batches results, calls a second LLM for synthesis + notes generation.
|
|
162
|
+
|
|
163
|
+
**What replaces `continue_workflow`:**
|
|
164
|
+
The plan's final action is `complete_step` with `notes`. Daemon calls `engine.continueWorkflow()` when the executor reaches the final plan item.
|
|
165
|
+
|
|
166
|
+
**Fit with WorkRail:**
|
|
167
|
+
- Aligns perfectly with "scripts over agent" -- the planning phase is the only LLM call; execution is mechanical
|
|
168
|
+
- Works with WorkRail's step model: each workflow step gets one plan-execute cycle
|
|
169
|
+
- `isComplete`: the plan's final `complete_step` action signals step completion
|
|
170
|
+
- HMAC tokens: daemon manages, LLM never sees them
|
|
171
|
+
|
|
172
|
+
**Gains:**
|
|
173
|
+
- Fewer total LLM calls for predictable steps (one planning call + one synthesis call vs. N tool_use round-trips)
|
|
174
|
+
- Execution is deterministic once the plan exists -- auditable, testable
|
|
175
|
+
- Plans are human-readable artifacts -- can be reviewed before execution (human-in-the-loop hooks)
|
|
176
|
+
- Aligns with the Phase 0 classification pattern already in the backlog
|
|
177
|
+
|
|
178
|
+
**Losses / risks:**
|
|
179
|
+
- Planning quality degrades for complex, exploratory steps where the right actions depend on what earlier actions reveal (you can't plan "read file X" if you don't yet know X's path)
|
|
180
|
+
- Two-call overhead per step even for simple steps (planning + synthesis)
|
|
181
|
+
- Plan-then-execute assumes the plan is complete -- if the plan misses an action, the step fails silently
|
|
182
|
+
|
|
183
|
+
**Verdict: HIGH fit for structured, predictable steps (Phase 5 fast path, auto-commit steps). LOW fit for exploratory steps (context gathering, investigation). Best as an opt-in step type: `stepType: "planned"` in workflow definition.**
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### Pattern 3: Verifier Pattern
|
|
188
|
+
|
|
189
|
+
**How it works in research:**
|
|
190
|
+
Separate LLM calls for "planner" (what to do next) and "verifier" (did the last thing work). The planner never sees error recovery -- that's the verifier's job. The planner stays focused on progress; the verifier handles quality gates.
|
|
191
|
+
|
|
192
|
+
**How it would work in WorkRail:**
|
|
193
|
+
|
|
194
|
+
After each tool batch (currently the `turn_end` event), a second LLM call evaluates the tool results:
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"assessment": "pass|retry|escalate",
|
|
198
|
+
"reasoning": "...",
|
|
199
|
+
"retry_instruction": "Run npm test again -- the first run failed due to a flaky test"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
If `assessment: "retry"`, the verifier's `retry_instruction` is injected via `steer()`. If `assessment: "escalate"`, the step is marked as `needs_human_review`.
|
|
204
|
+
|
|
205
|
+
**What replaces `continue_workflow`:**
|
|
206
|
+
Nothing -- `continue_workflow` is still called by the planner. The verifier adds a post-action quality gate.
|
|
207
|
+
|
|
208
|
+
**Fit with WorkRail:**
|
|
209
|
+
- This is a natural complement to the current tool_use model, not a replacement
|
|
210
|
+
- The `turn_end` event is the natural integration point for the verifier call
|
|
211
|
+
- WorkRail's assessment gates (in some workflow step types) already model this conceptually
|
|
212
|
+
- HMAC tokens: unchanged -- verifier never calls `continue_workflow`
|
|
213
|
+
|
|
214
|
+
**Gains:**
|
|
215
|
+
- Main agent stays focused on progress, not error recovery
|
|
216
|
+
- Verifier can use a cheaper model (claude-haiku for assessment, claude-sonnet for planning)
|
|
217
|
+
- Retry logic is explicit and auditable
|
|
218
|
+
- Escalation path for genuinely stuck agents
|
|
219
|
+
|
|
220
|
+
**Losses / risks:**
|
|
221
|
+
- Adds one LLM call per tool batch -- significant cost for tool-heavy steps
|
|
222
|
+
- Verifier and planner can disagree on "done" -- needs a tie-breaking rule
|
|
223
|
+
- Verifier model needs access to tool results and step context -- adds context management complexity
|
|
224
|
+
|
|
225
|
+
**Verdict: HIGH fit as an optional enhancement, not a core architecture change. Best implemented as an `onTurnEnd` hook in AgentLoop with a configurable verifier function. Step definition can opt in: `verifier: { model: "haiku", threshold: "high" }`.**
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### Pattern 4: Direct Structured Output (typed schemas)
|
|
230
|
+
|
|
231
|
+
**How it works in research:**
|
|
232
|
+
Instead of registering tools with JSON Schema and receiving `tool_use` blocks, the LLM is constrained to output a single structured object that the daemon parses. Used in OpenAI structured_outputs, Instructor library, Pydantic AI.
|
|
233
|
+
|
|
234
|
+
**How it would work in WorkRail:**
|
|
235
|
+
|
|
236
|
+
Define a `StepOutput` type that the LLM must produce:
|
|
237
|
+
```typescript
|
|
238
|
+
interface StepOutput {
|
|
239
|
+
notesMarkdown: string; // required -- step completion notes
|
|
240
|
+
contextUpdates: Record<string, unknown>; // optional context variable updates
|
|
241
|
+
isComplete: boolean; // true = advance to next step
|
|
242
|
+
toolCalls: Array<{ // zero or more tool calls to execute
|
|
243
|
+
tool: 'Bash' | 'Read' | 'Write';
|
|
244
|
+
params: Record<string, unknown>;
|
|
245
|
+
}>;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The LLM outputs one `StepOutput` per turn. Daemon executes the `toolCalls`, feeds results back, and loops.
|
|
250
|
+
|
|
251
|
+
**What replaces `continue_workflow`:**
|
|
252
|
+
`isComplete: true` in the `StepOutput` -- daemon calls `engine.continueWorkflow()` with `notesMarkdown`. The LLM never needs to know about the token protocol.
|
|
253
|
+
|
|
254
|
+
**Fit with WorkRail:**
|
|
255
|
+
- **Best alignment with WorkRail's "scripts over agent" principle**: LLM produces the intent, daemon executes it
|
|
256
|
+
- HMAC tokens: completely hidden from the LLM -- daemon owns all token management
|
|
257
|
+
- `steer()` replacement: daemon still uses `steer()` to inject next step prompt, but the expected output shape is the `StepOutput` schema
|
|
258
|
+
- Existing workflows: step prompts become the instruction set for generating a `StepOutput`
|
|
259
|
+
|
|
260
|
+
**Gains:**
|
|
261
|
+
- LLM never sees `continueToken` -- zero token leakage risk, zero token hallucination risk
|
|
262
|
+
- Typed output validation at parse time (Zod/TypeBox) -- malformed output is detected before execution
|
|
263
|
+
- Tool calls are explicit in the output schema -- no "unknown tool name" hallucination
|
|
264
|
+
- `isComplete` is a first-class field -- daemon progression is controlled by the daemon, not by whether the LLM chose to call a specific tool
|
|
265
|
+
- Step notes are always present (required field) -- eliminates the "LLM forgot to include notes" failure mode
|
|
266
|
+
|
|
267
|
+
**Losses / risks:**
|
|
268
|
+
- Requires Anthropic's `tool_choice` with a single tool (the `StepOutput` schema as a tool) -- changes the API call shape
|
|
269
|
+
- Parallel tool execution not representable -- `toolCalls` is sequential by definition in this schema
|
|
270
|
+
- Error messages from failed tool calls must be injected back as part of the structured output loop -- slightly different message format than current `tool_result` blocks
|
|
271
|
+
- The LLM must output a complete `StepOutput` including `notesMarkdown` before tool results are known -- this may degrade note quality for exploratory steps
|
|
272
|
+
|
|
273
|
+
**Verdict: VERY HIGH fit -- this is the most architecturally coherent alternative. It is the pattern that best expresses WorkRail's "daemon owns step progression" principle. Implementation path: add a `StepOutputMode` option to `AgentLoopOptions`; when set, use a single tool schema for `StepOutput` instead of the multi-tool registry.**
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### Pattern 5: Agentic Streaming
|
|
278
|
+
|
|
279
|
+
**How it works in research:**
|
|
280
|
+
LLM streams its response, daemon parses it in real-time and executes actions as they appear. Used in Claude claude-sonnet-4-5 with streaming tool use, OpenAI streaming function calls.
|
|
281
|
+
|
|
282
|
+
**How it would work in WorkRail:**
|
|
283
|
+
|
|
284
|
+
Switch `client.messages.create` to streaming (`client.messages.stream`). As the stream arrives:
|
|
285
|
+
- Text blocks are buffered for logging
|
|
286
|
+
- Tool use blocks trigger immediate tool execution when the input JSON is complete
|
|
287
|
+
- Results are injected into the stream continuation
|
|
288
|
+
|
|
289
|
+
**What replaces `continue_workflow`:**
|
|
290
|
+
Nothing changes -- `continue_workflow` is still a tool. The change is execution timing: tools fire as soon as they're parsed from the stream rather than waiting for the full response.
|
|
291
|
+
|
|
292
|
+
**Fit with WorkRail:**
|
|
293
|
+
- Sequential tool execution constraint is preserved -- stream still processes tools in declaration order
|
|
294
|
+
- `steer()` integration is unchanged
|
|
295
|
+
- HMAC tokens: unchanged
|
|
296
|
+
|
|
297
|
+
**Gains:**
|
|
298
|
+
- Lower latency for tool-heavy steps: `Bash` command starts while LLM is still generating the rest of its response
|
|
299
|
+
- For long-running bash commands, the LLM output is fully streamed while the command runs
|
|
300
|
+
- Feels more responsive in console live view
|
|
301
|
+
|
|
302
|
+
**Losses / risks:**
|
|
303
|
+
- Streaming API adds significant complexity to `_runLoop` -- need to handle partial tool_use blocks, streaming errors, and reconnection
|
|
304
|
+
- The `onLlmTurnStarted/onLlmTurnCompleted` callbacks need to span the full stream duration -- different token accounting
|
|
305
|
+
- `AbortController` behavior changes: aborting mid-stream requires drain-then-abort semantics
|
|
306
|
+
- Latency gains are marginal for short steps (< 500ms tool execution) -- the LLM generation time dominates
|
|
307
|
+
|
|
308
|
+
**Verdict: LOW fit for now. The latency gains are real but small for WorkRail's use case (workflow steps that run bash commands taking 5-60 seconds). The complexity cost is high. Best deferred until streaming becomes necessary for a specific use case (e.g., live console output display during long-running bash).**
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Pattern 6: Memory-Augmented Loop
|
|
313
|
+
|
|
314
|
+
**How it works in research:**
|
|
315
|
+
MemGPT, Zep, and similar systems maintain a structured knowledge store that the agent reads from and writes to between turns. Instead of re-reading files at the start of every session, the agent queries the knowledge store for relevant context.
|
|
316
|
+
|
|
317
|
+
**How it would work in WorkRail:**
|
|
318
|
+
|
|
319
|
+
WorkRail's backlog already designs this: the knowledge graph backed by tree-sitter + DuckDB. The agent loop integration point:
|
|
320
|
+
|
|
321
|
+
1. At session start, instead of injecting full CLAUDE.md (32 KB cap), inject a targeted context bundle from the knowledge graph
|
|
322
|
+
2. Between steps, the daemon updates the knowledge graph with what the agent learned (files read, symbols traced, invariants found)
|
|
323
|
+
3. At next session start, context gathering is "query graph for relevant subgraph" not "sweep 200 files"
|
|
324
|
+
|
|
325
|
+
**What replaces `continue_workflow`:**
|
|
326
|
+
Nothing -- this is not a replacement for the step progression mechanism. It replaces the context *content* injected into the agent, not the control flow.
|
|
327
|
+
|
|
328
|
+
**Fit with WorkRail:**
|
|
329
|
+
- The backlog already identifies this as HIGH importance (Apr 15, 2026)
|
|
330
|
+
- The knowledge graph is a new WorkRail source -- `graphSource` alongside `bundledSource`, `userSource`, `managedSource`
|
|
331
|
+
- `query_knowledge_graph` and `update_knowledge_graph` tools in the daemon tool registry
|
|
332
|
+
- The daemon updates the graph post-session (scripts over agent principle: graph update is deterministic, no LLM needed)
|
|
333
|
+
|
|
334
|
+
**Gains:**
|
|
335
|
+
- Context gathering drops from "sweep 200 files" to "query graph for 5-10 relevant files"
|
|
336
|
+
- Session startup time drops significantly for repeat tasks on familiar codebases
|
|
337
|
+
- Cross-session knowledge accumulates -- each session makes the next one faster
|
|
338
|
+
- Reduces input token count per session
|
|
339
|
+
|
|
340
|
+
**Losses / risks:**
|
|
341
|
+
- Requires the knowledge graph to be built and maintained -- significant new infrastructure
|
|
342
|
+
- Graph staleness: source files change, graph may be stale. Provenance tracking mitigates but doesn't eliminate
|
|
343
|
+
- First session on a new codebase still requires a full sweep to seed the graph
|
|
344
|
+
|
|
345
|
+
**Verdict: HIGH strategic importance, LOW urgency for near-term. This is the right long-term direction for reducing per-session cost. The implementation is a new WorkRail infrastructure layer, not a change to the agent loop itself. Defer until daemon is proven with current architecture.**
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Candidate Directions (Ranked)
|
|
350
|
+
|
|
351
|
+
### Rank 1: Direct Structured Output (Pattern 4)
|
|
352
|
+
|
|
353
|
+
**Why it wins:**
|
|
354
|
+
- Best expresses the "daemon owns step progression" architectural principle
|
|
355
|
+
- Eliminates `continueToken` visibility and hallucination risk entirely
|
|
356
|
+
- `notesMarkdown` becomes a required field -- eliminates "LLM forgot notes" failures
|
|
357
|
+
- `isComplete` is first-class -- daemon progression is deterministic, not tool-call-dependent
|
|
358
|
+
- Requires minimal change to `AgentLoop` -- add `StepOutputMode` option with single-schema tool
|
|
359
|
+
- All existing workflows work unchanged -- step prompts become `StepOutput` instructions
|
|
360
|
+
|
|
361
|
+
**Implementation path:**
|
|
362
|
+
1. Add `StepOutputSchema` TypeBox type: `{ notesMarkdown: string, isComplete: boolean, toolCalls: Array<...>, contextUpdates: Record<...> }`
|
|
363
|
+
2. Add `outputMode: 'tool_use' | 'structured_output'` to `AgentLoopOptions`
|
|
364
|
+
3. In `structured_output` mode: register a single tool `__step_output__` with `StepOutputSchema` as input_schema; add `tool_choice: { type: "tool", name: "__step_output__" }` to API params
|
|
365
|
+
4. In `_executeTools`: when `__step_output__` is called, extract `toolCalls` from the structured output and execute them; when `isComplete: true`, set the `isComplete` flag in `workflow-runner.ts`
|
|
366
|
+
5. `workflow-runner.ts` reads `isComplete` from the structured output instead of from `continue_workflow`'s response
|
|
367
|
+
|
|
368
|
+
**Replaces `continue_workflow` calls with:** Structured output field. The LLM never calls `continue_workflow` directly.
|
|
369
|
+
|
|
370
|
+
**What `continue_workflow` becomes in this model:** A daemon-side function call -- `engine.continueWorkflow()` is called by the daemon when it reads `isComplete: true` from the structured output. Not a tool the LLM invokes.
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
### Rank 2: Verifier Pattern (Pattern 3)
|
|
375
|
+
|
|
376
|
+
**Why it's second:**
|
|
377
|
+
- Does not replace the current architecture -- enhances it
|
|
378
|
+
- Low-risk addition: `onTurnEnd` hook in `AgentLoop` already supported via `subscribe()`
|
|
379
|
+
- Cheap model (Haiku) for verification keeps cost overhead low
|
|
380
|
+
- Solves real problem: LLM error recovery loops that go in circles
|
|
381
|
+
- Opt-in per step type -- existing workflows unaffected until they opt in
|
|
382
|
+
|
|
383
|
+
**Implementation path:**
|
|
384
|
+
1. Add `VerifierConfig` to `AgentLoopOptions`: `{ model: string; systemPrompt: string; threshold: 'low' | 'medium' | 'high' }`
|
|
385
|
+
2. In `turn_end` subscriber in `workflow-runner.ts`, after tool results are collected, call verifier LLM with tool results + step context
|
|
386
|
+
3. Verifier returns `{ assessment: 'pass' | 'retry' | 'escalate', retryInstruction?: string }`
|
|
387
|
+
4. `retry`: call `agent.steer()` with `retryInstruction` (overrides normal next-step steer)
|
|
388
|
+
5. `escalate`: set a `needsHumanReview` flag; daemon pauses session and emits REST event for console
|
|
389
|
+
|
|
390
|
+
**Replaces `continue_workflow` calls with:** Nothing -- `continue_workflow` is still called by the LLM. Verifier adds a post-execution quality gate.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
### Rank 3: Plan-then-Execute (Pattern 2)
|
|
395
|
+
|
|
396
|
+
**Why it's third:**
|
|
397
|
+
- High fit for structured, predictable steps; poor fit for exploratory steps
|
|
398
|
+
- Best as an opt-in step type (`stepType: "planned"`) not a universal replacement
|
|
399
|
+
- Phase 5 fast-path in lean.v2 workflows is the ideal target
|
|
400
|
+
- Aligns with the Phase 0 classification backlog item
|
|
401
|
+
|
|
402
|
+
**Implementation path:**
|
|
403
|
+
1. Add `stepType: "planned"` to workflow step schema
|
|
404
|
+
2. In `workflow-runner.ts`: when step type is `planned`, make a planning LLM call first with a `PlanSchema` output
|
|
405
|
+
3. Daemon executes the plan mechanically (scripts, not agent)
|
|
406
|
+
4. Second LLM call synthesizes results + generates notes
|
|
407
|
+
5. Daemon calls `engine.continueWorkflow()` with synthesized notes
|
|
408
|
+
|
|
409
|
+
**Replaces `continue_workflow` calls with:** Daemon orchestration -- the LLM never calls `continue_workflow` in planned steps.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
### Rank 4: ReAct (Pattern 1)
|
|
414
|
+
|
|
415
|
+
**Why it's fourth:**
|
|
416
|
+
- Useful as a prompt engineering technique within the current architecture (explicitly ask for reasoning before action)
|
|
417
|
+
- Not worth a full architecture change -- the gains are prompt-level, not infrastructure-level
|
|
418
|
+
- Error recovery concern (premature `is_step_complete`) is a real risk
|
|
419
|
+
|
|
420
|
+
**Implementation path:** No code change needed. Add ReAct-style reasoning instruction to step prompts in `buildSystemPrompt()`. The `## Execution contract` section already encourages "read the step carefully" -- add "reason about your approach before executing."
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### Rank 5: Memory-Augmented Loop (Pattern 6)
|
|
425
|
+
|
|
426
|
+
**Why it's fifth:**
|
|
427
|
+
- Highest strategic importance for the long-term platform
|
|
428
|
+
- Lowest urgency -- requires the knowledge graph infrastructure first
|
|
429
|
+
- Not a change to the agent loop itself; a change to the context content
|
|
430
|
+
|
|
431
|
+
**Implementation path:** See knowledge graph backlog section. Requires tree-sitter + DuckDB implementation, `query_knowledge_graph` tool, session-end graph update hook.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
### Rank 6: Agentic Streaming (Pattern 5)
|
|
436
|
+
|
|
437
|
+
**Why it's last:**
|
|
438
|
+
- Highest complexity, lowest marginal gain for WorkRail's use case
|
|
439
|
+
- Deferred until a specific use case (live console output) justifies the complexity
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Resolution Notes
|
|
444
|
+
|
|
445
|
+
### The architectural insight across all patterns
|
|
446
|
+
|
|
447
|
+
Every high-ranked pattern converges on the same principle: **the LLM should express intent, the daemon should drive control flow.**
|
|
448
|
+
|
|
449
|
+
In the current architecture, the LLM drives control flow by deciding to call `continue_workflow`. This is the weakest point:
|
|
450
|
+
- The LLM can forget to include notes
|
|
451
|
+
- The LLM can misformat the `continueToken`
|
|
452
|
+
- The LLM can call `continue_workflow` before completing the work
|
|
453
|
+
- The LLM can enter an error recovery loop that never advances
|
|
454
|
+
|
|
455
|
+
The Structured Output pattern (Rank 1) fully inverts this: the LLM outputs `{ isComplete: true, notesMarkdown: "...", toolCalls: [...] }` and the daemon decides whether and when to call `engine.continueWorkflow()`. This is architecturally cleaner and more robust.
|
|
456
|
+
|
|
457
|
+
### What this means for the WorkRail "daemon owns everything" principle
|
|
458
|
+
|
|
459
|
+
The `continue_workflow` tool is currently a bridge between the LLM world (tool calls) and the WorkRail world (HMAC tokens, step sequencer). The Structured Output pattern removes the LLM from that bridge. The daemon becomes the sole owner of step transitions. The LLM becomes a pure cognition engine: given a context, produce a structured output. The daemon handles all state machine transitions.
|
|
460
|
+
|
|
461
|
+
This is the right long-term direction. The `continue_workflow` tool is a historical artifact of the MCP model where Claude Code drives WorkRail externally. In daemon mode, that indirection is unnecessary.
|
|
462
|
+
|
|
463
|
+
### Compatibility with current workflows
|
|
464
|
+
|
|
465
|
+
All Rank 1-3 patterns are backward-compatible with current workflows:
|
|
466
|
+
- Structured Output: step prompts become instructions for producing `StepOutput`; no workflow format change needed
|
|
467
|
+
- Verifier: opt-in per step; existing steps unaffected
|
|
468
|
+
- Plan-then-Execute: opt-in per step type; existing steps unaffected
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Decision Log
|
|
473
|
+
|
|
474
|
+
### Initial pattern survey rankings (landscape pass)
|
|
475
|
+
|
|
476
|
+
| Decision | Rationale |
|
|
477
|
+
|----------|-----------|
|
|
478
|
+
| Rank 1: Structured Output | Best expression of "daemon owns step progression"; eliminates token leakage and hallucination risk; required `notesMarkdown` field solves persistent notes failure mode |
|
|
479
|
+
| Rank 2: Verifier | Non-breaking enhancement; solves real error recovery problem; opt-in; cheap with Haiku model |
|
|
480
|
+
| Rank 3: Plan-then-Execute | High fit for structured steps; aligns with Phase 0 classification backlog; opt-in via step type |
|
|
481
|
+
| Rank 4: ReAct | Prompt engineering, not architecture change; no code needed |
|
|
482
|
+
| Rank 5: Memory-Augmented | Highest long-term strategic importance; lowest near-term urgency; separate infrastructure layer |
|
|
483
|
+
| Rank 6: Streaming | Highest complexity, lowest marginal gain; deferred |
|
|
484
|
+
| Against: pure tool_use replacement | Current tool_use model works; the risk is the LLM controlling step transitions, not the mechanism itself |
|
|
485
|
+
|
|
486
|
+
### Candidate generation + design review (candidate pass)
|
|
487
|
+
|
|
488
|
+
Four concrete candidates were generated and reviewed (see `agent-loop-alternatives-candidates.md` and `agent-loop-alternatives-review.md`):
|
|
489
|
+
|
|
490
|
+
| Decision | Rationale |
|
|
491
|
+
|----------|-----------|
|
|
492
|
+
| **Selected: C3 (complete_step tool)** | Minimum-scope change satisfying all 5 decision criteria; iterative exploration preserved; continueToken hidden; notes required at type level; MCP mode unchanged; reversible. |
|
|
493
|
+
| C1 (Required-Notes Wrapper) as Phase 1 | Ships in 30 minutes as immediate safety net; compatible with C3; not competing. |
|
|
494
|
+
| C2 (StepOutput Mode) as runner-up | Lost because declaring all tool calls upfront breaks iterative exploration for investigative steps. Right long-term direction if Anthropic's forced-tool mode supports iterative invocation. |
|
|
495
|
+
| C4 (Scripted Steps) as follow-on | Right for auto-commit/auto-test steps; build when that feature ships. |
|
|
496
|
+
| C3 revised: minLength 50 (not 10) | ORANGE finding from design review: minLength 10 catches empty strings but not placeholder notes. |
|
|
497
|
+
| C3 revised: explicit continue_workflow exclusion | ORANGE finding: silent failure if both tools registered; explicit exclusion + test required. |
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Final Summary
|
|
502
|
+
|
|
503
|
+
**The core question is: who should own step transitions -- the LLM or the daemon?**
|
|
504
|
+
|
|
505
|
+
The current tool_use model answers "LLM" (by calling `continue_workflow`). Every high-value alternative answers "daemon" (by reading a structured output field or executing a plan).
|
|
506
|
+
|
|
507
|
+
### Recommended build order
|
|
508
|
+
|
|
509
|
+
**Phase 1 (this week, ~30 min):**
|
|
510
|
+
- Add `minLength: 50` check to `continue_workflow` notes param in `_executeTools()` -- ships as immediate safety net
|
|
511
|
+
|
|
512
|
+
**Phase 2 (next sprint, ~1 day):**
|
|
513
|
+
- Implement `complete_step` tool for daemon mode: schema `{ notesMarkdown (required, minLength:50), contextUpdates }`
|
|
514
|
+
- Update `workflow-runner.ts` turn_end subscriber to detect `complete_step` and call `engine.continueWorkflow()` directly
|
|
515
|
+
- Update `buildSystemPrompt()` to describe `complete_step` in daemon mode
|
|
516
|
+
- Explicit `continue_workflow` exclusion in daemon mode tool registry
|
|
517
|
+
- Unit test: daemon mode tool list includes `complete_step`, excludes `continue_workflow`
|
|
518
|
+
- WHY comments throughout explaining the token-hiding and notes-enforcement rationale
|
|
519
|
+
|
|
520
|
+
**Phase 3 (after Phase 2 is proven):**
|
|
521
|
+
- Verifier hook at `turn_end` (Rank 2) -- opt-in per step; cheap Haiku model; solves premature-complete failure mode
|
|
522
|
+
|
|
523
|
+
**Phase 4 (longer-term):**
|
|
524
|
+
- `stepType: "scripted"` for Phase 5 fast path / auto-commit steps (Rank 3, C4)
|
|
525
|
+
- Knowledge graph infrastructure (Rank 5) -- separate infrastructure layer
|
|
526
|
+
|
|
527
|
+
**Confidence: HIGH.** All findings are grounded in:
|
|
528
|
+
- Direct code reading (`agent-loop.ts`, `workflow-runner.ts`)
|
|
529
|
+
- Direct backlog reading ("scripts over agent" principle, knowledge graph section, complexity routing section)
|
|
530
|
+
- Prior daemon architecture discovery (`daemon-architecture-discovery.md`)
|
|
531
|
+
- Pattern literature (ReAct, MemGPT, Reflexion, structured_outputs)
|