Commit Graph

44 Commits

Author SHA1 Message Date
xingyue e067a2f25a refactor: rebrand npm packages @uncaged/* → @united-workforce/*
CI / check (pull_request) Failing after 9m51s
Package mapping:
- @uncaged/cli-workflow → @united-workforce/cli
- @uncaged/workflow-protocol → @united-workforce/protocol
- @uncaged/workflow-util → @united-workforce/util
- @uncaged/workflow-util-agent → @united-workforce/util-agent
- @uncaged/workflow-agent-hermes → @united-workforce/agent-hermes
- @uncaged/workflow-agent-claude-code → @united-workforce/agent-claude-code
- @uncaged/workflow-agent-builtin → @united-workforce/agent-builtin
- @uncaged/workflow-dashboard → @united-workforce/dashboard

Changes:
- 8 package.json name + dependency refs
- 82 files: import statements updated
- .changeset/config.json updated
- CLAUDE.md updated
- bunfig.toml restored for preload

CLI command (uwf) and directory names unchanged.

Closes shazhou/united-workforce#8
2026-06-02 20:56:06 +08:00
xiaomo ef0174a6f1 chore: migrate @uncaged/json-cas to @ocas/core, @uncaged/json-cas-fs to @ocas/fs
- Replace all package.json dependencies
- Update all imports across 7 packages + scripts
- cas_ref → ocas_ref in schema definitions
- listByType() adapted for ListEntry[] return type
- Update CLAUDE.md references

Fixes #585
2026-06-02 02:51:21 +00:00
xiaoju d310d43ab8 feat(step-read): store assembled prompt in CAS, add --prompt flag
Store the fully assembled prompt sent to each agent in CAS as a text
node, referenced from StepNodePayload.assembledPrompt. This enables
exact reproduction of what the agent received for debugging hallucinations.

Changes:
- workflow-protocol: StepRecord + STEP_NODE_SCHEMA add assembledPrompt field
- workflow-util-agent: AgentRunResult includes assembledPrompt, run.ts stores it
- workflow-util-agent: schemas register TEXT_SCHEMA for prompt storage
- workflow-agent-claude-code: return assembled prompt from buildClaudeCodePrompt
- workflow-agent-hermes: return assembled prompt from buildHermesPrompt
- workflow-agent-builtin: return empty prompt (no prompt assembly)
- cli-workflow: step read --prompt renders the stored prompt
- All test fixtures updated for new field

Legacy steps without assembledPrompt show 'Prompt not recorded' message.

小橘 🍊
2026-05-29 01:42:43 +00:00
xiaoju 4193157124 refactor(hermes): clean up loadHermesSessionFromDb
- Remove unnecessary Promise.resolve() wrappers (sync function)
- Use try/finally for db.close() instead of manual close at each exit
- Flatten nested try/catch

Follow-up to #535 review nits.

小橘 🍊
2026-05-26 14:27:31 +00:00
xiaoju 37f4203b40 fix(hermes): add SQLite fallback for loadHermesSession (#535)
When sessions.write_json_snapshots is disabled, Hermes only writes to
state.db (SQLite). loadHermesSession now falls back to reading from
~/.hermes/state.db when the JSON file is missing.

- Add getHermesDbPath() and loadHermesSessionFromDb() functions
- Use bun:sqlite with readonly mode, try-catch for graceful errors
- JSON file still takes priority (fast path)
- Filter messages to user/assistant/tool roles
- Convert unix timestamps to ISO 8601 strings
2026-05-26 14:19:15 +00:00
xingyue 6324122168 fix(uwf-hermes): read turn data from Hermes session file instead of ACP stream
Closes #519

The ACP protocol's tool_call updates only carry a display title (not a
structured tool name) and omit rawInput for polished tools, making the
reconstructed messages unusable for step read/show.

Changes:
- hermes.ts: storePromptResult reads ~/.hermes/sessions/session_{id}.json
  via loadHermesSession() instead of using ACP-reconstructed messages
- acp-client.ts: strip message/tool-call collection logic, keep only
  text chunk accumulation for final response extraction
- step.ts: TurnData gains role + toolCalls fields; formatTurnBody
  renders them in step read markdown output
- README: document sessions.write_json_snapshots requirement
2026-05-25 22:21:03 +08:00
xiaoju dfae96ad45 style: fix biome import ordering after package rename 2026-05-25 11:26:01 +00:00
xiaoju ca223a19c6 refactor: rename workflow-agent-kit → workflow-util-agent, merge workflow-moderator into cli-workflow
- Rename packages/workflow-agent-kit → packages/workflow-util-agent
- Update all imports, tsconfig references, docs
- Delete dead file packages/workflow-util-agent/src/build-agent-prompt.ts
- Merge workflow-moderator (62 LOC) into cli-workflow/src/moderator/
- Move workflow-moderator to legacy-packages/
- Add mustache dependency to cli-workflow
- Update publish-all.mjs

Fixes #512
2026-05-25 10:51:16 +00:00
xiaoju c2c849df7e fix(agent-kit): provide full thread context to first-time participating roles
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
2026-05-24 15:56:39 +00:00
xiaoju 1d174ee5c9 fix(agent-kit): separate session cache per agent
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
2026-05-24 09:16:06 +00:00
xingyue 221919448e refactor: reduce cognitive complexity in session-detail and acp-client
Extract helper functions to bring parseClaudeCodeStreamOutput (37→≤15)
and handleSessionUpdate (24→≤15) within complexity limits. Add tests.

Fixes #448
2026-05-24 00:41:39 +08:00
xingyue 1abc3b4cf4 chore: fix all biome lint errors across monorepo
- Fix import ordering (organizeImports) across multiple packages
- Replace forEach with for...of loops (noForEach)
- Replace non-null assertions with fallback values (noNonNullAssertion)
- Add biome-ignore comments for justified noExplicitAny usages
- Remove parameter properties, use explicit class properties (noParameterProperties)
- Fix string concatenation to template literals (useTemplate)
- Fix format issues (CSS, TypeScript)
- Add tailwindDirectives CSS parser config in biome.json
- Replace var with const (noVar)

Result: 0 errors, 12 warnings (all cognitive complexity, acceptable)
2026-05-23 18:39:02 +08:00
xiaoju 1afaeacd57 feat: extract session cache to agent-kit, add resume to claude-code agent
Move getCachedSessionId/setCachedSessionId from workflow-agent-hermes
into workflow-agent-kit so all agent adapters can share the same
session cache logic.

Add cross-process session resume to workflow-agent-claude-code:
on re-entry (isFirstVisit=false), look up the cached sessionId and
use 'claude --resume' to continue with full conversation history.

Cache file renamed from hermes-sessions.json to agent-sessions.json
to reflect its shared nature.

Refs #418
2026-05-23 07:44:02 +00:00
xiaoju aad2792754 fix(hermes): disable ACP session/resume by default
Hermes ACP _restore fails for custom providers — resolve_runtime_provider
throws and base_url/api_mode are lost, causing resume to silently create a
new session with no history. Prompt then returns empty text or refusal.

Disable resume by default. Set UWF_HERMES_RESUME=1 to opt back in.

Includes investigation notes in docs/investigations/.

Refs #418
2026-05-23 07:23:14 +00:00
xiaoju 4b45f4e6d1 feat: replace edgePrompt null check with isFirstVisit (Phase 2)
- Add isFirstVisit: boolean to AgentContext
- Compute from steps history: !steps.some(s => s.role === role)
- hermes.ts: use isFirstVisit for first-entry vs re-entry logic
- buildInitialPrompt: always append edgePrompt as Moderator Instruction
- edgePrompt is never blanked — always the real moderator instruction
- New tests for first-visit, re-entry, and fallback scenarios

Refs #405, #407, #404
2026-05-23 04:54:11 +00:00
xiaoju 3d6399c0e3 feat: make edge prompt required (Phase 1)
- Transition.prompt: string | null → string
- EvaluateResult.prompt: string | null → string
- AgentContext.edgePrompt: string | null → string
- CLI YAML validation rejects missing prompt
- All tests updated

Phase 2 will replace edgePrompt === null checks with findLastRoleIndex.

Refs #405, #406, #404
2026-05-23 04:28:58 +00:00
xiaoju 638329a562 feat: edge prompt + session resume implementation (#402)
- buildContinuationPrompt: incremental prompt for role re-entry
- buildHermesPrompt: dual-mode (initial vs continuation)
- session-cache: thread:role → hermes sessionId mapping
- HermesAcpClient.resume(): session/resume JSON-RPC
- Fallback: cache miss or resume fail → initial prompt
- UWF_NO_RESUME env to skip cache
- solve-issue.yaml: reviewer→developer edge prompt
- Tests updated for EvaluateResult + continuation prompt

Refs #402
2026-05-23 03:57:04 +00:00
xiaoju d5d05334f5 fix: ACP client permission handling and process cleanup
Two bugs fixed:
1. request_permission messages (JSON-RPC requests with both id+method) were
   silently swallowed by the response handler, causing hermes to hang waiting
   for permission approval. Now properly distinguish responses (id only) from
   server requests (id+method).
2. uwf-hermes process never exited after completing because the hermes ACP
   subprocess was still alive. Now explicitly close the ACP client after
   agent completion so the subprocess terminates.

小橘 <xiaoju@shazhou.work>
2026-05-22 14:51:43 +00:00
xiaoju 844f5438fe fix: replace @agentclientprotocol/sdk with readline-based JSON-RPC
The official TS SDK's ndJsonStream hangs indefinitely on prompt()
for sessions with 20+ messages (solve-issue planner). Root cause
appears to be a stream backpressure issue in the SDK's ReadableStream
adapter.

Switch back to readline-based line parsing which reliably receives
all JSON-RPC responses. Also handle session/request_permission
inline (auto-approve, yolo mode equivalent).

Ref #398
2026-05-22 14:34:27 +00:00
xiaoju f90614a622 feat: collect structured turns from ACP session updates
UwfAcpClient now tracks all session/update events:
- agent_message_chunk → assistant message content
- agent_thought_chunk → assistant reasoning
- tool_call → pending tool invocation (name + rawInput)
- tool_call_update (completed/failed) → assistant tool_call + tool result

Messages are accumulated across prompts (same session) and stored
via storeHermesSessionDetail, restoring the full structured detail
(turns with tool calls, reasoning) that was lost in the initial ACP
migration.

Ref #398
2026-05-22 13:13:02 +00:00
xiaoju 68af555313 fix: share ACP client across run/continue for session continuity
The client is now created once in createHermesAgent() and shared by
runHermes and continueHermes closures. This preserves conversation
context during frontmatter retry loops — continue() sends a follow-up
prompt on the same ACP session instead of starting a new one.

Client is cleaned up via process.on('exit').

Ref #398
2026-05-22 13:06:14 +00:00
xiaoju 025695dbe9 refactor: use @agentclientprotocol/sdk instead of hand-rolled JSON-RPC
Replace 250-line custom ACP client with official TypeScript SDK.
Uses ClientSideConnection + ndJsonStream for stdio transport.
Same public API (connect/prompt/close), 115 lines, zero custom protocol code.

Ref #398
2026-05-22 12:58:55 +00:00
xiaoju 96584e481f refactor: replace spawnHermes with HermesAcpClient
Remove spawnHermes, spawnHermesChat, spawnHermesResume, parseSessionId,
and buildResultFromSession. runHermes and continueHermes now use
HermesAcpClient for structured JSON-RPC communication.

Session ID comes directly from ACP protocol, eliminating #380 race
condition. Agent output collected via streaming chunks instead of
session file loading.

Phase 2 of RFC #398
Fixes #380
2026-05-22 12:18:14 +00:00
xiaoju 766ec7ddc2 feat: add HermesAcpClient for structured ACP communication
Implements JSON-RPC client that communicates with `hermes acp` via
stdin/stdout. Replaces fragile stdout/stderr parsing with structured
protocol: initialize → session/new → session/prompt → collect chunks.

Session ID comes directly from protocol response, eliminating the
race condition in #380.

Phase 1 of RFC #398
2026-05-22 12:15:09 +00:00
xiaoju 7ff90cef4f feat: agent session protocol — sessionId in result, continue support, frontmatter retry
Breaking changes:
- AgentRunResult now requires sessionId field
- AgentOptions now requires continue function
- Agent CLI outputs JSON {stepHash, sessionId} instead of plain CAS hash
- Engine parses JSON output (with legacy CAS hash fallback)

New features:
- Frontmatter validation retry: if agent output lacks valid frontmatter,
  engine calls agent.continue() up to 2 times with correction message
- Session tracking: sessionId flows from agent → engine → StepOutput
- Hermes agent: session parse failure is now a hard error (no raw text fallback)
- Hermes agent: supports --resume for continue sessions

Closes #384
2026-05-22 09:13:05 +00:00
xiaoju a3114bf840 chore: apply biome formatting across codebase 2026-05-22 02:06:05 +00:00
xiaoju 505f85e3c4 feat: add buildRolePrompt in agent-kit, integrate with uwf-hermes
- New buildRolePrompt() in workflow-agent-kit: four-phase prompt assembly
  with fallback to systemPrompt
- Export from agent-kit index
- Update uwf-hermes to use buildRolePrompt instead of raw systemPrompt
- Add tests for all modes: four-phase, legacy, mixed

Refs #359, #362
2026-05-21 02:31:56 +00:00
xiaoju 7b9cb6a9c8 chore: rename uwf-* → workflow-*, cli-uwf → cli-workflow
Reclaim the workflow-* package names now that legacy packages are archived.

Package renames:
- @uncaged/uwf-protocol → @uncaged/workflow-protocol
- @uncaged/uwf-moderator → @uncaged/workflow-moderator
- @uncaged/uwf-agent-kit → @uncaged/workflow-agent-kit
- @uncaged/uwf-agent-hermes → @uncaged/workflow-agent-hermes
- @uncaged/cli-uwf → @uncaged/cli-workflow

All internal imports, tsconfig references, and docs updated.
CLI binary name 'uwf' unchanged.

小橘 🍊(NEKO Team)
2026-05-19 07:52:16 +00:00
xiaoju d63d58ccb5 chore: reorganize repo — legacy packages to legacy-packages/, templates to examples/
- Move 15 old workflow-* packages to legacy-packages/ (inactive, preserved for reference)
- Rename templates/ → examples/ for clarity
- Rewrite docs/architecture.md to reflect current uwf architecture
- Active packages remain in packages/: cli-uwf, uwf-agent-hermes, uwf-agent-kit, uwf-moderator, uwf-protocol, workflow-util

小橘 🍊(NEKO Team)
2026-05-19 07:19:40 +00:00
Scott Wei 7dadf874e1 refactor(workflow-protocol): require AgentFn Opt generic
Make AgentFn<Opt> always take a mandatory options argument, removing
the void conditional overload. Simplify createAgentAdapter, restore
exports needed by tests, and fix CLI test bundles to use cas.put
instead of disallowed @uncaged/* imports.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-16 18:23:07 +08:00
xiaoju 5bbac3e4f7 chore: internalize unused exports across all packages
Audit public API surfaces using reachability analysis from application
entry points (Worker, CLI, Dashboard). Symbols not reachable from any
application's customization tree are removed from package index.ts files.

Source files and internal usage are untouched — only the public export
surface is narrowed.

Changes by package:
- workflow-util: -7 exports (base32 internals, logger config types)
- workflow-cas: -12 exports (merkle internals, serialization details)
- workflow-execute: -24 exports (engine internals, LLM extract details)
- workflow-reactor: -4 exports (reactor config/invocation internals)
- workflow-register: -8 exports (redundant protocol re-exports, internal YAML fns)
- workflow-runtime: curated re-export subset (stop full protocol re-export)
- workflow-util-agent: -5 exports (internal agent helpers)
- workflow-agent-cursor: -1 export (validateCursorAgentConfig)
- workflow-agent-hermes: -1 export (validateHermesAgentConfig)

Note: workflow-protocol index.ts unchanged — downstream packages still
import removed symbols via internal paths. Protocol cleanup requires
updating workflow-runtime/src/types.ts first (separate PR).

Refs #273, #274, #275, #276, #277, #278, #279, #280, #281, #282
2026-05-16 09:58:56 +00:00
xiaoju 2a1b7b0aeb refactor(agents): migrate LLM/Hermes/Cursor to createAgentAdapter
- LLM: AgentFn<{prompt}> + createAgentAdapter, chatCompletionText unchanged
- Hermes: AgentFn<{prompt}> + createAgentAdapter, config validation in extract
- Cursor: AgentFn<{prompt, workspace}> + createAgentAdapter, workspace
  extraction moved to extract fn, AgentFn itself only receives resolved options

All public API signatures preserved. createTextAdapter/TextProducerFn retained.

Closes #261, Phase 2 of #252
2026-05-14 10:22:37 +00:00
xiaoju 94f725c50b refactor: cursor-agent uses runtime.extract for workspace detection
- Remove llmProvider and workspace from CursorAgentConfig (now just command/model/timeout)
- extractWorkspacePath uses runtime.extract + runtime.cas instead of standalone reactor
- TextProducerFn signature gains runtime parameter: (ctx, prompt, runtime)
- develop-entry.ts hardcodes cursor-agent path, no more env var dependency
- Drop @uncaged/workflow-reactor dep from workflow-agent-cursor
- Update tests for simplified config

小橘 <xiaoju@shazhou.work>
2026-05-13 15:51:43 +00:00
xiaoju cfe4543d39 refactor!: remove deprecated Agent types, introduce Adapter-first API
BREAKING CHANGES:
- Remove AgentFn, AgentFnResult, AgentBinding from workflow-protocol
- Remove wrapAgentAsAdapter from workflow-util-agent
- workflowAsAgent → workflowAdapter (old name kept as deprecated re-export)

New APIs:
- createTextAdapter(producer) — bridges text-producing functions to AdapterFn
- TextProducerFn, TextAdapterResult types
- workflowAdapter() — direct AdapterFn for child workflow delegation

All agent packages (cursor, hermes, llm) now return AdapterFn directly,
no wrapping needed. Bundle entries simplified accordingly.

小橘 🍊(NEKO Team)
2026-05-13 08:03:27 +00:00
xingyue c5fd84432f fix(agent): defer config validation to call time
Bundle top-level code runs during `workflow add` (descriptor extraction),
but agent config env vars (e.g. WORKFLOW_HERMES_COMMAND) are only available
at `workflow run` time. Deferring validation prevents premature throws.
2026-05-12 12:58:06 +08:00
xingyue 1b62cec0a2 feat(agent): require absolute path for command in hermes/cursor agent configs
BREAKING: HermesAgentConfig.command and CursorAgentConfig.command are now
required string fields (absolute path to CLI binary). Validation rejects
non-absolute paths at construction time.

- Eliminates PATH resolution ambiguity in spawned worker processes
- spawnCli: explicit env: process.env for clarity
- bundle-entry: WORKFLOW_CURSOR_COMMAND is now required
- Updated tests for both agents
2026-05-12 12:52:48 +08:00
xingyue ecc348f182 feat(agent): add command config to hermes/cursor agents + explicit env inheritance
- HermesAgentConfig.command: override hermes CLI path (default: "hermes")
- CursorAgentConfig.command: override cursor-agent CLI path (default: "cursor-agent")
- spawnCli: explicit env: process.env for clarity and future extensibility
- bundle-entry: read WORKFLOW_CURSOR_COMMAND from env
2026-05-12 12:49:28 +08:00
xiaoju 0f28e9b61a refactor: remove non-index re-exports
Remove re-exports from non-index files to enforce folder module discipline:
- workflow-agent-hermes: remove buildAgentPrompt re-export
- workflow-agent-cursor: remove buildAgentPrompt re-export
- cli-workflow/cli-dispatch: remove CommandEntry/CommandGroup/DispatchFn/getCommandRegistry re-exports

小橘 <xiaoju@shazhou.work>
2026-05-09 04:19:49 +00:00
xiaoju e9e4960714 refactor(workflow): migrate downstream packages to workflow-runtime (Phase 2+3)
- Verify createWorkflow in runtime has zero I/O imports
- Migrate agent-cursor, agent-hermes to pure workflow-runtime dependency
- Migrate agent-llm, util-agent, templates to dual dependency
  (runtime for types, engine for CAS/merkle/buildDescriptor)
- All 377 tests passing

Refs #121, relates #123 #124
2026-05-08 06:33:52 +00:00
xiaoju 84de74721d feat: Merkle node format + content → CAS
- MerkleNode type: { type, payload, children } serialized as YAML
- RoleOutput.content → contentHash (CAS hash of Merkle content node)
- Engine stores content in global CAS before writing to .data.jsonl
- create-workflow puts content as Merkle node, merges contentHash into refs
- fork/parse adapted for contentHash format
- buildAgentPrompt now async, reads content from CAS
- Bundle validator allows @uncaged/workflow import
- 150 tests passing

BREAKING: .data.jsonl no longer contains inline content

Fixes #41
2026-05-07 13:14:01 +00:00
xiaoju 99a137422c feat: add ExtractFn utility, cursor agent workspace from thread context
- New ExtractFn = <T>(schema, prompt) => (ctx) => Promise<T>
- createExtract(provider) creates an LLM-backed ExtractFn
- CursorAgent removes workdir config, uses ExtractFn to resolve workspace from ThreadContext at runtime
- buildAgentPrompt(ctx) — reads systemPrompt from ctx.currentRole

小橘 <xiaoju@shazhou.work>
2026-05-07 00:17:31 +00:00
xiaoju fa9163e462 refactor: all-agentic architecture — roles as pure data, agent binding at runtime
BREAKING: Major architecture change.

- RoleDefinition = pure data (systemPrompt + schema + dryRunMeta)
- AgentFn = (ctx: ThreadContext) => Promise<string>, reads ctx.currentRole
- WorkflowDefinition decoupled from agents, bound via AgentBinding at runtime
- createWorkflow(def, binding, extract) replaces createRoleModerator
- Meta extraction moved into engine loop
- Delete workflow-util-role package (createRole, decorators, extract all gone)
- Role packages become pure data exports
- Agent packages updated to single-arg AgentFn

小橘 <xiaoju@shazhou.work>
2026-05-06 14:14:33 +00:00
xiaoju 2a71454c10 refactor: extract @uncaged/workflow-util-agent + smart prompt
- New package: spawn-cli + build-agent-prompt shared utils
- Smart prompt: start + meta summaries for middle steps + last step full
- Cursor/Hermes adapters now import from util-agent (no duplicate code)
- 109 tests pass, biome clean

Closes #14
小橘 <xiaoju@shazhou.work>
2026-05-06 07:17:59 +00:00
xiaoju f21014fcdd feat: @uncaged/workflow-agent-cursor + @uncaged/workflow-agent-hermes
- Cursor adapter: spawn cursor-agent CLI, auto/specified model
- Hermes adapter: spawn hermes chat CLI
- Both: AgentFn interface, no nerve-core deps, Result-based config validation
- 93 tests pass, biome clean

Closes #10, Closes #11
小橘 <xiaoju@shazhou.work>
2026-05-06 06:54:24 +00:00