129 Commits

Author SHA1 Message Date
xiaoju 4a43a7f3dd refactor: update all consumers to import from @uncaged/workflow
- workflow-utils, workflow-meta: import workflow types from @uncaged/workflow
- adapter-cursor, adapter-hermes: same
- cli: same
- core: remove workflow re-exports, no longer depends on @uncaged/workflow

Phase 5+6 of #320, Testing: #323
2026-05-05 11:01:08 +00:00
xiaoju cee65bbd87 refactor(workflow): move IPC, worker, manager from daemon to @uncaged/workflow
- Move workflow IPC types (StartThread, ResumeThread, etc.) to workflow/ipc.ts
- Move workflow-worker.ts, workflow-manager.ts, workflow-manager-support.ts
- Move worker-runtime.ts and worker-signals.ts (shared infrastructure)
- Daemon now imports workflow runtime from @uncaged/workflow
- Export WORKFLOW_WORKER_PATH for daemon to spawn workers

Phase 3+4 of #320, Testing: #322
2026-05-05 10:41:59 +00:00
xiaoju 8dd82d99da refactor(core): remove WorkflowTrigger from SenseTrigger — shell only
Senses trigger shell commands only. Workflows are invoked via CLI.

SenseTrigger is now { command: string } — no discriminated union.

Closes #318

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 12:33:38 +00:00
xiaoju 52a03d7de4 refactor(core): rename workflow→trigger in sense return, capture shell stderr
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 10:47:22 +00:00
xiaoju b9b804eac5 feat(core): sense trigger supports arbitrary shell commands
Extend SenseComputeReturn to support shell triggers in addition to workflow
triggers via a discriminated union (kind: 'shell' | 'workflow').

Shell triggers execute a command string in the sense worker subprocess
(spawned detached). The kernel logs 'shell-launch' events without involving
the workflow manager.

Breaking change: WorkflowTrigger now requires kind: 'workflow'.
New ShellTrigger type: { kind: 'shell', command: string }.
SenseTrigger = WorkflowTrigger | ShellTrigger.

Closes #315
2026-05-02 10:00:23 +00:00
xiaoju 02c5e8bea6 fix(daemon): harden state persistence, ReadonlyArray triggers
1. writeState: atomic write via temp file + rename
2. readState: distinguish missing file vs corrupt JSON (warn on error)
3. executeCompute: write disk before updating memory state
4. SenseInfo.triggers: ReadonlyArray<string>
5. CLAUDE.md: added Sense State Persistence docs

Fixes #313
2026-05-01 12:01:50 +00:00
xiaoju fc7fc9158c docs: update all docs/conventions for stateful sense, remove stale refs
Phase 4 of RFC #308: Stateful Sense refactor.

- CLAUDE.md: updated diagram, tables, examples (no more Signal)
- Cleaned stale Signal Bus / DrizzleDB / _signals / retention refs
  across READMEs, .cursor rules, copilot instructions, .knowledge
- Removed drizzle-orm from core package.json (no longer used)
- Updated pnpm-lock.yaml

Refs #308
2026-05-01 10:09:01 +00:00
xiaoju 5e054facb2 refactor(daemon): stateful sense engine — JSON state, remove Signal Bus
Phase 2 of RFC #308: Stateful Sense refactor.

- SenseRuntime uses JSON file persistence instead of SQLite/Drizzle
- Sense compute now receives state and returns { state, workflow }
- IPC: replaced SignalMessage with ComputeResultMessage
- Removed Signal Bus entirely (on[] now uses reverse-index in scheduler)
- sense-scheduler.onSenseCompleted() triggers dependent senses
- kernel no longer constructs Signal objects or calls routeSenseComputeOutput
- Removed drizzle-orm dependency from daemon package

Refs #308, closes #310
2026-05-01 09:50:46 +00:00
xiaomo 49f3d91d1b chore: dead code cleanup — remove unused exports and fix stale docs
- Delete createEchoAgent (daemon, never referenced)
- Delete isDryRun (workflow-utils, deprecated, always false)
- Delete KNOWN_AGENT_ADAPTER_IDS (core, never referenced)
- Remove parseDurationStringToMs, labelSenseTrigger from core public API
- Remove spawnSafe re-export from workflow-utils
- Fix core/README.md stale API names
- Clean stale hermes-options.ts comment

Closes #302
2026-04-30 14:29:45 +00:00
xiaomo a1b1d5eaf1 chore: RFC-006 Phase 4 cleanup — delete worker-fork-support.ts
- Move formatChildExitSummary/formatCapturedStderrTail to worker-runtime.ts
- Move ignoreSessionBroadcastSignals to new worker-signals.ts
- Delete worker-fork-support.ts (teeCapturedStderr no longer used)
- Update .knowledge/worker-isolation.md and architecture.md for WorkerRuntime
- All 167 tests pass, biome check clean

Closes #283
2026-04-30 14:17:16 +00:00
xiaomo 0455f928f5 fix: address review feedback (星月) for Phase 3
1. sendToWorker: IPC send failure now marks thread as failed + dequeues next
2. crashLimitBlocked Set: prevents new startWorkflow from bypassing crash limit
3. "respawning" log skipped when crash limit is active
4. logWorkflowEvent payload: unknown | null (project convention, not ?:)
2026-04-30 14:10:06 +00:00
xiaomo dc4454d23e refactor: migrate workflow-manager process logic to WorkerRuntime (RFC-006 Phase 3)
- workflow-manager.ts: 792 → 498 lines, no more fork/ChildProcess/crash counting
- Extract pure functions to workflow-manager-support.ts (256 lines)
- WorkerRuntime gains: onCrashLimitReached callback, WorkerDrainOpts for per-call timeout
- worker-pool.ts updated for new evict/drain signature (null opts)
- All 167 daemon tests pass

Closes #282
2026-04-30 13:56:43 +00:00
xiaomo e67ddc58d8 fix: address review feedback (星月)
1. trySendSync: wrap child.send in try/catch — IPC race between connected check and send
2. gracefulStop: same try/catch for shutdown send
3. Remove crashTimestamps reset on ready — crash window detection was being bypassed
2026-04-30 13:41:31 +00:00
xiaomo 4dffcb636b fix: resolve 2 failing tests after WorkerRuntime migration
- Add trySendSync() for synchronous send when worker is ready+connected
- sendCompute uses sync path first, async fallback for cold start
- Add forwardStderr, allowRespawn, hasDisconnectedChild, onReady(key,msg)
- Tests: add connected:true to mocks, flush async fork microtasks
- All 167 daemon tests pass
2026-04-30 13:34:10 +00:00
xiaomo c34ec46416 feat(daemon): WorkerRuntime — generic message-routed process manager (closes #280)
RFC-006 Phase 1: ManagedWorker state machine + WorkerRuntime<K> with
cold start, crash respawn, drain/evict, graceful shutdown.
8 test cases covering all lifecycle scenarios.
2026-04-30 13:09:19 +00:00
xiaoju c98e14e9e6 refactor(cli): single-package workspace init and root dist build (#274)
Init templates match ~/.uncaged-nerve: scripts/build.mjs writes dist/senses/*/index.js and dist/workflows/*/index.js; drop @uncaged/nerve-skills from generated package.json; refresh Cursor skills rule copy.

Sense worker sends full compute result on signal IPC so the kernel can route workflow triggers; update e2e harness paths (migrations under senses/, noop under dist/workflows).

Fixes #274

Made-with: Cursor
2026-04-30 10:17:44 +00:00
xiaoju d9c86c49ae refactor(daemon): load sense/workflow bundles from dist/ directory
Workspace build output moved from senses/<name>/index.js and
workflows/<name>/dist/index.js to dist/senses/<name>/index.js
and dist/workflows/<name>/index.js.

Refs #274
小橘 <xiaoju@shazhou.work>
2026-04-30 09:16:25 +00:00
xiaomo de8c7c5150 fix: address review — revert unrelated sense-worker change, restore isDryRun as deprecated 2026-04-30 08:00:46 +00:00
xiaomo f799cee51f refactor(cli,docs): RFC-005 Phase 4 — update templates, tests, docs (closes #271) 2026-04-30 07:24:11 +00:00
xiaomo d13b59e787 refactor(daemon): RFC-005 Phase 3 — workflow-worker uses ThreadContext (closes #270) 2026-04-30 07:10:58 +00:00
xiaomo 975f15c66d refactor(workflow-utils): RFC-005 Phase 2 — adapt to ThreadContext, new AgentFn signature (closes #269) 2026-04-30 06:59:15 +00:00
xiaoju 2387b73141 fix(daemon): remove stale exports openPeerDb, loadComputeFn from index
These functions were renamed/removed from sense-runtime.ts but index.ts
still re-exported them, causing rslib build to fail (no JS output).

小橘 <xiaoju@shazhou.work>
2026-04-30 05:56:24 +00:00
xiaoju b27a6aced8 feat: sense compute returns ComputeResult<T> with workflow trigger support
- SenseComputeFn returns ComputeResult<T> = null | { signal, workflow }
- sense-runtime persists result.signal, not result itself
- sense-worker sends workflow trigger message when workflow is non-null
- New SenseWorkflowTriggerMessage in IPC protocol
- Knowledge card updated to match

— 小橘 🍊(NEKO Team)
2026-04-30 00:37:16 +00:00
xiaoju 748df10e6a fix: remove AbortSignal from SenseComputeFn
Compute is truly zero-arg now: () => Promise<T | null>.
Runtime handles timeout via Promise.race, sense doesn't need signal.

— 小橘 🍊(NEKO Team)
2026-04-30 00:22:57 +00:00
xiaoju 8c9adf08c5 refactor: pure sense compute — no db, no peers
SenseComputeFn is now (signal: AbortSignal) => Promise<T | null>.
sense-runtime handles db.insert when compute returns non-null.
Senses export { compute, table } — SenseModule type added to core.

Closes #264
Refs #260

— 小橘 🍊(NEKO Team)
2026-04-30 00:07:49 +00:00
xiaoju 9b4ab6225a refactor(core): remove WorkflowSpec and compileWorkflowSpec
Add createRole to workflow-utils wrapping AgentFn, Zod meta, and extractMetaOrThrow.

Refs #252

Signed-off-by: 小橘 🍊(NEKO Team) <dev@uncaged.ai>
Made-with: Cursor
2026-04-29 09:54:13 +00:00
小橘 🍊(NEKO Team) 77500ee6dd feat(daemon): pass dryRun into compileWorkflowSpec extractFn
Adds optional CursorAdapter.mode for ask/plan. Export zodMeta from workflow-utils for RoleSpec meta schemas.

Made-with: Cursor
2026-04-29 09:35:12 +00:00
xiaoju ede59ebcc2 feat(core): remove AgentRegistry, roles declare adapter directly
RoleSpec uses adapter: AgentFn; timeouts are configured via adapter factories.

nerve.yaml no longer accepts agents:; extract merge is global to role only.

Added cursorAdapter/hermesAdapter defaults; removed daemon registry and deps.

Signed-off-by: 小橘 🍊(NEKO Team)
Made-with: Cursor
2026-04-29 08:40:37 +00:00
xiaoju b7d9a37981 feat: RFC-003 adapter plugin architecture + dynamic prompts
AgentRegistry plugin model:
- createAgentRegistry(agents, adapterFactories) — second param for adapter map
- Echo adapter built-in, cursor/hermes via factory injection
- Unknown type throws with available adapter list

Dynamic prompts:
- RoleSpec.prompt: string | ((start, messages) => Promise<string>)
- compileWorkflowSpec handles both static and dynamic prompts

Adapter packages:
- @uncaged/nerve-adapter-cursor — cursor-agent CLI spawn
- @uncaged/nerve-adapter-hermes — hermes CLI subagent spawn
- Each with own spawn-safe (inline, avoids circular dep)
- Moved spawn logic from workflow-utils, kept role factories as thin wrappers

Kernel integration:
- defaultAgentAdapterFactories() registers cursor + hermes
- Hot-reload passes factories on rebuild

Ref: #234
2026-04-29 07:24:19 +00:00
xiaoju a1dda1d731 feat(daemon,cli): RFC-003 Phase 5 — Integration (hot-reload + validate)
- Kernel: rebuild AgentRegistry on config hot-reload, log agent_registry_reload
- Running threads unaffected, new threads use rebuilt registry
- nerve validate: check agent name refs in WorkflowSpec source files
- nerve validate: verify adapter type is known (KNOWN_AGENT_ADAPTER_IDS)
- nerve validate: require extract config when WorkflowSpec agent refs exist
- Tests: kernel reload (mock), validate (missing/valid/extract/adapter)

Closes #239
Ref: #234
2026-04-29 05:23:59 +00:00
xiaoju 1218b5ddbd feat(core,daemon): RFC-003 Phase 4 — WorkflowSpec Compiler
- WorkflowSpec + RoleSpec types in packages/core
- compileWorkflowSpec: WorkflowSpec → WorkflowDefinition (daemon)
- resolveRoleTimeoutMs: two-level timeout (role override > agent default)
- parseDurationStringToMs extracted to shared duration.ts
- AgentRegistry.getAgentConfig for timeout lookup
- Tests: 10 new cases (compile shape, agent→extract flow, timeout resolution)
- Backward compat: hand-written Role<Meta> unchanged

Closes #238
Ref: #234
2026-04-29 05:11:29 +00:00
xiaoju 88bd30a1e4 feat(daemon): RFC-003 Phase 2 — AgentRegistry + echo adapter
- createAgentRegistry(agents) returns { get(name): AgentFn }
- get() throws with agent name in message if not found
- Echo adapter (type: 'echo') returns prompt as-is for testing
- Tests: 5 cases covering get/throw/echo/multi-agent/AbortSignal

Closes #236
Ref: #234
2026-04-29 04:49:22 +00:00
xiaoju 36e5aed1b1 feat(core): RFC-003 Phase 1 — agent config types + nerve.yaml schema
- Add AgentFn, WorkflowContext (workdir + AbortSignal), ExtractFn, ExtractError
- Add AgentConfig, ExtractConfig types to NerveConfig
- Extend parseNerveConfig: agents (kebab-case keys) + extract sections
- Export all new types from @nerve/core
- Add config parse tests (7 new tests)
- Update all existing test fixtures with agents/extract fields

Closes #235
Ref: #234
2026-04-29 04:43:08 +00:00
xiaoju ce79dbea7e fix(cli): include __start__ message in nerve thread show
When 'nerve thread show' is called without --before, the initial user
prompt (__start__ message) is now displayed first, followed by the most
recent role rounds within the budget.

- Add getThreadStartMessage() to LogStore
- Modify buildThreadCommandOutput to accept optional startRow
- Pass start message from threadShowCommand when before===0
- Add tests for new behavior

Fixes #231
2026-04-28 15:08:23 +00:00
xiaoju e56a01d88a refactor(daemon): workflows must be bundled to dist/, daemon only loads dist/index.js
- workflow-worker: loadWorkflowDefinition only looks for dist/index.js
- file-watcher: watch workflows/*/dist/**/*.js instead of *.ts
- file-watcher: sense watch uses .js regex pattern
- nerve create workflow: scaffold includes package.json with esbuild build script
- Updated all related tests

Fixes #219
2026-04-28 05:25:38 +00:00
xingyue 0388c6010a fix(daemon): unskip and fix 16 daemon tests
- kernel-workflow-integration (8): mock children now respond to compute
  messages with signal replies, matching real worker behavior
- kernel (2): add ready + compute response to mocks, flush with
  runAllTimersAsync
- phase6-integration (3) + kernel-integration (3): replace setInterval
  polling with async/await setTimeout loop, increase timeouts

All 165 daemon tests pass, zero skips.

Closes #213
2026-04-28 12:46:12 +08:00
xiaoju a03ab64c3e chore(daemon): skip 16 flaky/broken kernel tests
8 flaky tests (worker spawn timing issues):
- phase6-integration: 3 tests
- kernel-integration: 3 tests
- kernel: 2 tests

8 broken tests (routeResult undefined bug):
- kernel-workflow-integration: 8 tests

Tracked in #TBD for proper fix.
2026-04-28 04:32:10 +00:00
xiaomo b4c78a62aa Merge pull request 'feat(workflow-utils): role factory templates #208' (#209) from feat/208-role-factories into main 2026-04-28 01:55:14 +00:00
xiaoju 2529c68062 feat(workflow-utils): role factory templates — createCursorRole, createHermesRole, createLlmRole, createReActRole
- Add role-types.ts with all shared types (CliPromptFn, LlmPromptFn, MetaExtractConfig, etc.)
- Add role-factories.ts with 4 factory functions
- Add llm-chat.ts with chatCompletionText and reActIterativeChat
- Add hermes-agent.ts and hermes-options.ts for Hermes CLI integration
- Add threadId to StartStep meta (core + daemon)
- Add model param to cursorAgent options
- Tests for all 4 factories

Refs #208
2026-04-28 01:50:44 +00:00
xiaomo e159a9b7ca refactor: redesign workflow trigger — signal entails workflow (#204)
Breaking change: compute() returns null | { signal: T; workflow: WorkflowTrigger | null }
- WorkflowTrigger is a structured type (name, maxRounds, prompt, dryRun)
- Signal is always emitted before workflow launch (causal chain)
- CLI: nerve workflow trigger <name> --max-rounds N --prompt '...' --dry-run
- Remove pipe-separated directive format

Fixes #204
2026-04-27 13:23:31 +00:00
xiaoju b269f76b33 refactor(daemon): rename reflex-scheduler → sense-scheduler
Rename ReflexScheduler to SenseScheduler, update all file names,
imports, comments, test descriptions, and log source values.

Fixes #202
2026-04-27 12:07:22 +00:00
xiaoju 2b2895e5be refactor: remove legacy reflexes backward-compat code
BREAKING CHANGE: NerveConfig no longer has 'reflexes' field.
SenseReflexConfig/ReflexConfig types removed.
Config with top-level 'reflexes' array now errors instead of migrating.
Use sense-level 'interval' and 'on' fields instead.

- Remove reflexes from NerveConfig type
- Remove legacy parsing, deprecation warning, buildReflexesFromSenses
- Simplify reflex-scheduler to only read sense-level config
- Rename senseTriggerLabelsWithFallback → senseTriggerLabels
- Delete all legacy reflexes test cases
- -639/+114 lines

Fixes #199
2026-04-27 11:11:37 +00:00
xiaoju ef40512977 refactor(daemon): reflex-scheduler reads from sense config with legacy fallback
- Scheduler iterates config.senses directly for interval/on
- Falls back to config.reflexes when sense has no inline triggers
- kernel.ts uses senseTriggerLabelsWithFallback for display
- New core export: senseTriggerLabelsWithFallback
- All 166 daemon tests pass

Fixes #194
2026-04-27 10:46:12 +00:00
xiaoju 453294a465 fix(daemon): guard against undefined in sense signal persistence
Use loose equality (!=) to catch both null and undefined from compute(),
preventing undefined values from being persisted to SQLite.

Fixes #192
2026-04-27 10:35:26 +00:00
xiaoju 81663ad524 fix(core): remove stale import process breaking core build
Fixes #191
2026-04-27 10:33:36 +00:00
xiaoju c6c3e0142d chore: bump version to 0.5.0
小橘 🍊(NEKO Team)
2026-04-27 09:40:34 +00:00
xiaoju 03e103d400 refactor: move experimental-warning-suppression from core to daemon
core package should remain platform-agnostic without direct process access.
The suppression module belongs in daemon where Node.js APIs are expected.

小橘 🍊(NEKO Team)
2026-04-27 09:30:16 +00:00
xingyue f9c591fdf2 refactor: reduce cognitive complexity in 3 functions (#184)
Co-authored-by: 星月 <xingyue@shazhou.work>
Co-committed-by: 星月 <xingyue@shazhou.work>
2026-04-27 09:10:05 +00:00
xiaomo d70e74afde fix: suppress ExperimentalWarning for node:sqlite (#179)
Add side-effect module in @uncaged/nerve-core that filters ExperimentalWarning
from process.emit. Imported at all entry points: CLI, daemon bootstrap,
sense-worker, and workflow-worker.
2026-04-27 08:17:52 +00:00
xingyue 40accf3b7c refactor(daemon): optimize _signals prune query
Replace NOT IN subquery with OFFSET-based cutoff for better
performance at large retention values.

Ref: PR #177 review feedback from @xiaomo
2026-04-27 15:56:43 +08:00