Fixed issue #469 where `uwf step list`, `uwf step show`, and `uwf thread read`
failed with "thread not active" error when called on completed threads.
The root cause was that resolveHeadHash() in shared.ts only checked threads.yaml
(active threads index) but never fell back to history.jsonl (completed threads log).
Changes:
- Updated resolveHeadHash() in shared.ts to check history.jsonl as fallback
- Changed error message from "thread not active" to "thread not found"
- Added comprehensive test coverage:
- Unit tests for resolveHeadHash() with active/completed/missing threads
- Integration tests for cmdStepList() with completed threads
- Integration tests for cmdStepShow() with completed threads
- Regression tests for cmdThreadRead() with completed threads
All commands now work identically for active and completed threads.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a role participates for the first time (e.g. committer), it previously
only received the system prompt + last step output, missing the full thread
history. This caused hallucination as the role had to guess what happened.
Changes:
- build-continuation-prompt.ts: detect first-time roles and include all
steps' meta + content for last 2-3 steps (within quota)
- context.ts: add isFirstVisit detection helper
- types.ts: add isFirstVisit field to AgentContext
- hermes.ts: pass isFirstVisit through to prompt builder
Fixes#473
Implements enhanced filtering and pagination for the `uwf thread list` command
to support workflows with large numbers of threads.
Changes:
- Add --page, --page-size parameters for pagination (default: page 1, size 20)
- Add --since, --until time filters supporting multiple formats (ISO8601, relative like "2h", "1d")
- Add --workflow filter to show threads for specific workflow
- Add --sort parameter (newest-first, oldest-first, alphabetical)
- Add pagination metadata in JSON output (page, pageSize, totalThreads, totalPages, hasMore)
- Implement parseRelativeTime() for human-friendly time expressions (1h, 30m, 2d, 1w)
- Add comprehensive unit tests for filters, pagination, and time parsing
- Update CLI help text with new parameters and examples
Fixes#471
- Add `content: string | null` to RoleStep type
- Resolve contentHash → text for the last step when building ThreadContext
- Update buildAgentPrompt to include <output> tag with step content
- Add 16k content quota with truncation
- Update tests
Previously step list showed raw CAS refs for detail fields.
Now detail is recursively expanded (like output already was),
since every turn is individually hashed and walkable.
Refs #463
Each agent now maintains its own session cache file instead of sharing
a single agent-sessions.json. This prevents session ID conflicts when
multiple agents operate on the same thread+role pair.
Changes:
- getCachePath() now takes agentName parameter
- getCachedSessionId/setCachedSessionId require agentName as first param
- Cache files named <agent>-sessions.json (e.g., hermes-sessions.json)
- Agent wrappers inject their agent name into cache calls
- Add comprehensive tests for session cache isolation
- Handle malformed JSON gracefully (treat as empty cache)
Fixes#461
Changed uwf thread read to wrap role prompts and agent outputs in XML tags
(<prompt> and <output>) instead of markdown headings (### Prompt, ### Content).
This prevents Claude Code from treating step outputs as structural headings.
- Updated formatStepPrompt to use <prompt>...</prompt> tags
- Updated formatStepContent to use <output>...</output> tags
- Added comprehensive test suite in thread-read-xml-tags.test.ts
- Updated existing tests to verify XML tag behavior
Fixes#459
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactored runBuiltinLoop function to reduce cognitive complexity from 30 to below 15 by extracting helper functions:
- shouldInjectDeadlineWarning: checks if deadline warning should be shown
- shouldProcessToolCalls: determines if tool calls should be processed
- extractFinalText: extracts last assistant message content
- injectDeadlineWarning: injects deadline warning message
- handleTextOnlyTurn: handles text-only turn logic
- handleToolCallTurn: handles tool call turn logic
- processLoopIteration: processes a single loop iteration
Added 24 new unit tests for the extracted helper functions, bringing total test count to 41 (all passing). All existing behavior is preserved.
Fixes#444
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit implements issue #456, adding two related capabilities to the uwf CLI:
1. **Background execution mode** for `uwf thread step` (via `--background` flag)
- Spawns agent execution in a detached background process
- Returns immediately with thread ID and background status
- Maintains marker files to track running processes
- Supports `--count` option to run multiple steps in background
- Prevents concurrent execution of the same thread
2. **Running threads query** command (`uwf thread running`)
- Lists all threads currently executing in background
- Returns thread ID, workflow, current role, PID, and start time
- Automatically filters out stale markers (dead processes)
- Empty list when no threads are running
**Key changes:**
- **workflow-protocol**: Added `RunningThreadItem`, `RunningThreadsOutput` types
Updated `StepOutput` to include `background: boolean | null` field
- **cli-workflow/background**: New module for process management
- Marker file creation/deletion (atomic operations)
- PID liveness checking
- Stale marker cleanup
- Running threads query
- **cli-workflow/commands/thread**:
- Updated `cmdThreadStep` to support `--background` and `--_background-worker` flags
- Added `cmdThreadStepBackground` for spawning detached processes
- Added `cmdThreadRunning` to list running threads
- Updated `cmdThreadKill` to terminate background processes
- **cli-workflow/cli**: Added CLI routing for new commands and flags
**Integration:**
- `uwf thread kill` now terminates background processes before archiving
- Foreground execution checks for existing background process and fails if found
- Background worker creates/cleans up marker files automatically
- Marker files stored in `~/.uncaged/workflow/running/*.json`
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The turns array items in CLAUDE_CODE_DETAIL_SCHEMA were missing
format: 'cas_ref', so expandDeep in step-details couldn't resolve
turn hashes to their payloads. Hermes schema already had this.
小橘 <xiaoju@shazhou.work>
Per CLAUDE.md convention, use `string | null` instead of `?:` in the
isFirstConditionalSibling helper function parameter types.
Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Extract helper functions (resolveThreadId, getThreadHead, listThreadSteps,
displayStepDetails, displayThreadRead) to reduce nesting and improve
readability. Also adds test coverage for the refactored functions.
Fixes#446
- Root README: add all 9 packages to table, update architecture diagram,
refresh CLI reference from uwf --help
- New READMEs for 8 packages (cli-workflow, workflow-protocol,
workflow-moderator, workflow-agent-kit, workflow-agent-hermes,
workflow-agent-builtin, workflow-agent-claude-code, workflow-dashboard)
- Updated workflow-util README to match current exports
- All API sections verified against src/index.ts exports
小橘 🍊(NEKO Team)
- Nudge turns don't consume turn budget (up to MAX_NUDGES=3), prevents
wasting agent work capacity on bookkeeping
- Inject deadline warning when 3 turns remain, telling agent to wrap up
- Agent can use status:failed to gracefully exit if it can't finish
- Inject user message when 3 turns remain, telling agent to wrap up
- Prompt tells agent to use status:failed if it can't finish in time
- Prevents wasting all turns without producing any frontmatter output
- Remove stale test file from dogfood agent run
copilot-api returns tool_calls even when tools field is omitted from
the request (infers from message history). Now the loop explicitly
nullifies tool_calls when noTools=true.
When the agent's first run output fails frontmatter extraction, the
retry loop (via options.continue) would replace agentResult entirely,
causing the 1-turn continuation detail to overwrite the original
multi-turn detail containing all tool-call history.
Now we capture primaryDetailHash from the first run and always use it
for the persisted StepNode, regardless of how many retries occur.
Fixes#439
LLM sometimes emits plain text (e.g. 'Now I'll write the tests...')
without calling tools, which the loop treated as final output. Now
the loop detects this and injects a user message nudging the LLM
to either continue using tools or output frontmatter with ---.
Agent was using all continue turns to keep calling tools instead of
outputting the required frontmatter. Now continue runs with noTools=true,
forcing LLM to emit text-only response.
Also supports null tools in chatCompletionWithTools to omit tools from
the API request entirely.
Agent was wasting turns exploring the filesystem because it didn't
know its working directory. Now the system prompt includes:
'Your working directory is: /path/to/cwd'
- Add stripPreamble() to handle LLM output with text before ---
- Strengthen system prompt: CRITICAL instruction for --- at position 0
- Fixes frontmatter parsing failures on first output turn
Previously runBuiltinWithMessages deleted the session jsonl after each
run/continue call. This meant the createAgent retry mechanism (which
calls continue on frontmatter validation failure) would lose all
previous turn data — each continue started with an empty jsonl.
Now the session jsonl accumulates across run + continue calls, so the
final storeBuiltinDetail captures all turns. The jsonl file is left
behind for debugging; it's small and can be cleaned up on next startup.
Also add a workflow hint to the system prompt reminding the LLM to use
tools before outputting frontmatter, preventing premature text-only
responses on the first turn.
- Add 4-strategy resolution priority: CAS hash → file path → local discovery → global registry
- Add helper functions: isFilePath, workflowFileExists, findWorkflowInDir, findWorkflowInParents
- Refactor resolveWorkflowCasRef to support direct hash, explicit paths, and parent traversal
- Add comprehensive test suite with 24 tests covering all strategies and edge cases
- Support .workflow/ and .workflows/ directories with .yaml/.yml extensions
- All 60 tests pass across 5 test files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Disable aliasDuplicateObjects in YAML stringify to prevent &a1/*a1
anchors when multiple steps have identical output
- Fix unused discoverAgents function (prefixed with _) and format issue
in setup.ts
Skip acp-client 'prompt() collects structured messages' and
resume-e2e 'resume() after close' — both require live LLM calls
and fail intermittently in CI.
Each turn (assistant response / tool result) is appended to a JSONL file
at ~/.uncaged/workflow/sessions/<sessionId>.jsonl during the loop.
On completion, the JSONL is read back, each turn is stored as a CAS node,
and the detail payload references them as a flat turns[] array in
chronological order. The session file is then deleted.
Benefits:
- Real-time observability: tail -f the JSONL to watch loop progress
- Crash recovery: partial JSONL survives process death
- Zero write contention: one file per session
- Detail stays a flat array for easy consumption by CLI/dashboard
Changes:
- New session.ts: initSessionDir, appendSessionTurn, readSessionTurns, removeSession
- loop.ts: append JSONL each turn instead of accumulating in-memory
- detail.ts: reads session JSONL → persists turns to CAS → stores detail
- agent.ts: passes storageRoot/sessionId to loop, cleans up session on completion
- types.ts: remove index from TurnPayload (order is implicit in JSONL/array)
- schemas.ts: sync with type changes
Ref: #433
buildClaudeCodePrompt was dropping ctx.edgePrompt entirely — the graph
transition instruction (e.g. 'Implement the plan') never reached the agent.
Now appended as '## Current Instruction' at the end of the prompt.
- StepRecord adds edgePrompt field (backward compat: defaults to "")
- StepNode CAS schema includes edgePrompt
- writeStepNode persists ctx.edgePrompt
- buildHistory exposes edgePrompt in StepContext
- buildBuiltinMessages reconstructs multi-turn moderator↔agent conversation:
system = role prompt + output format (stable prefix)
per prior visit: user (edgePrompt + inter-step summary) + assistant (output)
current: user (edgePrompt + recent summary)
- Zero extra persistence — pure function of CAS chain
- Stable prefix for LLM prompt cache hits
- 10 builtin tests pass, all other package tests pass