@kaged/daemon
Long-lived HTTP+WebSocket server that routes operator sessions, dispatches primary agents through the harness, and orchestrates plugins, providers, and project lifecycles
139
source files
88
test files
~69.3k
lines
✓ 171 pass
tests
pass
typecheck
3w
lint
Test results 171
buildLaunchUrl (ADR-0040) > co-located (no ui_url) prints the daemon-local /launch URL
[0.690ms]
route ↔ handler sync > every contract handler has a route definition
[0.250ms]
route ↔ handler sync > known exceptions actually exist in route definitions
[0.100ms]
route ↔ handler sync > known exceptions do NOT have contract handlers (or remove them from exceptions)
[0.090ms]
context estimate handler > returns 503 when storage is unavailable
[19.75ms]
context estimate handler > returns 404 when session is not found
[23.83ms]
context estimate handler > returns the context estimate payload
[33.36ms]
context estimate handler > honors session modelOverride when resolving route
[23.83ms]
context estimate handler > throws when session modelOverride uses an unconfigured provider
[20.10ms]
context estimate handler > registers GET /api/v1/sessions/:id/context-estimate in contract handlers
[11.03ms]
parsePorcelainV2 — branch headers > parses a normal branch with upstream and ahead/behind
[4.06ms]
parsePorcelainV2 — branch headers > parses an unborn branch (initial — oid null)
[0.130ms]
parsePorcelainV2 — branch headers > parses detached HEAD
[0.090ms]
parsePorcelainV2 — branch headers > branch with no upstream sets upstream null and ahead/behind 0
[0.090ms]
parsePorcelainV2 — ordinary entries (type 1) > parses an unstaged modification (index='.', worktree='M')
[0.200ms]
parsePorcelainV2 — ordinary entries (type 1) > parses a staged + unstaged modification (MM)
[0.100ms]
parsePorcelainV2 — ordinary entries (type 1) > parses a staged addition (A.)
[0.060ms]
parsePorcelainV2 — ordinary entries (type 1) > path with spaces is preserved
[0.060ms]
parsePorcelainV2 — rename entries (type 2) > parses a rename with origPath in the next NUL element
[0.110ms]
parsePorcelainV2 — rename entries (type 2) > rename origPath does not appear as a separate entry
[0.090ms]
parsePorcelainV2 — unmerged entries (type u) > parses UU as both-modified
[0.110ms]
parsePorcelainV2 — unmerged entries (type u) > parses AA as both-added
[0.070ms]
parsePorcelainV2 — unmerged entries (type u) > parses AU as added-by-us
[0.050ms]
parsePorcelainV2 — unmerged entries (type u) > parses UA as added-by-them
[0.050ms]
parsePorcelainV2 — unmerged entries (type u) > parses DU as deleted-by-us
[0.050ms]
parsePorcelainV2 — unmerged entries (type u) > parses UD as deleted-by-them
[0.050ms]
parsePorcelainV2 — unmerged entries (type u) > parses DD as both-deleted
[0.050ms]
parsePorcelainV2 — untracked entries > parses an untracked file
[0.060ms]
parsePorcelainV2 — untracked entries > untracked path with spaces is preserved
[0.090ms]
parsePorcelainV2 — ignored entries are dropped > ! entries do not appear in the result
[0.110ms]
parsePorcelainV2 — mixed fixture > parses a realistic mixed status output
[0.240ms]
computeCounts > empty entries → all zero
[0.140ms]
computeCounts > counts staged, unstaged, untracked, conflicted independently
[0.070ms]
computeCounts > conflicted entries are excluded from staged/unstaged
[0.040ms]
computeCounts > untracked entries are excluded from staged/unstaged
[0.030ms]
isPathInsideRoot > accepts a path inside the root
[0.110ms]
isPathInsideRoot > accepts the root directory itself
[0.040ms]
isPathInsideRoot > rejects a sibling that shares a prefix (prefix-collision guard)
[0.030ms]
isPathInsideRoot > rejects a completely different path
[0.030ms]
isPathInsideRoot > handles trailing slashes on root
[0.030ms]
isPathInsideRoot > handles trailing slashes on candidate
[0.030ms]
isPathInsideRoot > rejects the parent of the root
[0.030ms]
isRelativePathSafe > accepts a simple relative path
[0.130ms]
isRelativePathSafe > accepts a nested relative path
[0.040ms]
isRelativePathSafe > rejects an absolute path
[0.030ms]
isRelativePathSafe > rejects ../ at the start (escapes root)
[0.030ms]
isRelativePathSafe > rejects mid-path ../ that escapes
[0.030ms]
isRelativePathSafe > accepts ../ that stays within root (a/../../b resolves to ../b)
[0.030ms]
isRelativePathSafe > accepts internal ../ that doesn't escape
[0.030ms]
isRelativePathSafe > rejects empty string
[0.020ms]
isRelativePathSafe > accepts . segments (current dir)
[0.030ms]
isRelativePathSafe > rejects NUL byte injection
[0.030ms]
mapGitError > index.lock exists → git.busy
[0.260ms]
mapGitError > 'not a git repository' → git.not_a_repo
[0.050ms]
mapGitError > SSH publickey denied → git.auth_failed
[0.060ms]
mapGitError > HTTP authentication failed → git.auth_failed
[0.050ms]
mapGitError > host key verification failed → git.host_key_unknown
[0.040ms]
mapGitError > non-fast-forward push → git.non_fast_forward
[0.040ms]
mapGitError > rejected push → git.non_fast_forward
[0.040ms]
mapGitError > generic non-zero exit → git.command_failed
[0.110ms]
mapGitError > stderr is truncated in the message for very long output
[0.080ms]
GitService integration — core loop > getRootSummaries on a clean repo returns valid root with zero counts
[30.67ms]
GitService integration — core loop > getStatus shows untracked files
[23.54ms]
GitService integration — core loop > stage → commit loop works end-to-end
[36.36ms]
GitService integration — core loop > commit with nothing staged throws git.nothing_staged
[26.41ms]
GitService integration — core loop > diff on an unstaged modification returns text diff
[21.88ms]
GitService integration — core loop > stage → unstage returns the file to unstaged
[35.38ms]
GitService integration — core loop > stash + stashPop round-trips a change
[51.30ms]
GitService integration — core loop > requireRoot throws git.root_not_configured for unknown scope
[20.84ms]
GitService integration — core loop > path with ../ traversal is rejected before any git call
[15.68ms]
GitService integration — core loop > getRootSummaries marks a non-repo directory as valid: false
[18.90ms]
happy path > dispatchPrimary integration > run completes with state 'done'
[23.71ms]
happy path > dispatchPrimary integration > assistant message persisted to storage
[18.86ms]
happy path > dispatchPrimary integration > persisted message has provider metadata
[19.03ms]
happy path > dispatchPrimary integration > session transitions back to idle
[18.90ms]
happy path > dispatchPrimary integration > runPrimary receives correct provider route
[18.27ms]
happy path > dispatchPrimary integration > runPrimary receives reconstructed operator message
[16.28ms]
happy path > dispatchPrimary integration > runPrimary receives system prompt content
[16.85ms]
happy path > dispatchPrimary integration > run state transitions through 'running' before 'done'
[15.14ms]
dispatchPrimary integration > config-missing: run fails when localConfigPath is null
[7.52ms]
dispatchPrimary integration > project-not-loaded: run fails when project not in config
[12.49ms]
dispatchPrimary integration > alias-not-found: run fails when model alias missing
[11.96ms]
dispatchPrimary integration > invalid-alias-target: run fails when target missing colon
[7.78ms]
dispatchPrimary integration > provider-not-configured: run fails when provider entry missing
[12.82ms]
dispatchPrimary integration > api-key-unresolved: run fails when provider has no key
[8.19ms]
dispatchPrimary integration > prompt-unreadable: run fails when prompt file doesn't exist
[13.00ms]
dispatchPrimary integration > dsl-invalid: run fails when project.yaml is malformed
[7.66ms]
error mid-stream > dispatchPrimary integration > run marked failed when runPrimary returns error
[13.22ms]
error mid-stream > dispatchPrimary integration > session still transitions to idle after error
[8.63ms]
error mid-stream > dispatchPrimary integration > error assistant message is still persisted
[14.01ms]
dispatchPrimary integration > runPrimary-throws: run fails gracefully
[12.81ms]
dispatchPrimary integration > runPrimary-throws: session transitions to idle
[8.58ms]
dispatchPrimary integration > api-key-env: resolves key from environment variable
[13.41ms]
dispatchPrimary integration > reconstructs full message history
[8.42ms]
dispatchPrimary integration > skips superseded messages in history
[12.50ms]
dispatchPrimary integration > all dispatch errors leave session in idle state
[6.75ms]
model override > dispatchPrimary integration > override bypasses alias resolution and routes directly
[11.77ms]
model override > dispatchPrimary integration > override works even when alias is missing
[13.77ms]
model override > dispatchPrimary integration > override with unknown provider fails
[7.67ms]
model override > dispatchPrimary integration > override with invalid format fails
[11.18ms]
model override > dispatchPrimary integration > override with no api key fails
[11.36ms]
abort controller registry > dispatchPrimary integration > controller is registered while runPrimary is executing
[8.34ms]
abort controller registry > dispatchPrimary integration > controller is deregistered after successful completion
[13.50ms]
abort controller registry > dispatchPrimary integration > controller is deregistered after error result
[8.30ms]
abort controller registry > dispatchPrimary integration > controller is deregistered after runPrimary throws
[14.25ms]
abort controller registry > dispatchPrimary integration > abort signal is passed to runPrimaryFn
[13.25ms]
abort controller registry > dispatchPrimary integration > getRunController returns undefined for unknown run
[6.29ms]
list project sessions summary > summary exposes kind for every session
[7.71ms]
list project sessions summary > summary resolves created_by_user from created_by
[8.56ms]
list project sessions summary > created_by_user is null when creator does not resolve
[6.93ms]
list project sessions summary > ?kind=workflow filters the list to workflow sessions
[5.99ms]
list project sessions summary > ?kind=chat returns only chat sessions
[8.55ms]
message regenerate handler > happy path unthrottled returns fresh run_id and supersedes later messages
[8.72ms]
message regenerate handler > returns 409 when target message is not operator role
[8.55ms]
message regenerate handler > returns 409 when session state is running
[6.89ms]
message regenerate handler > returns 409 when session state is queued
[5.85ms]
message regenerate handler > returns 409 when session state is paused
[8.33ms]
message regenerate handler > returns 409 when session state is ended
[7.16ms]
message regenerate handler > returns 409 when session state is failed
[5.76ms]
message regenerate handler > returns 404 for unknown session or unknown message
[8.56ms]
message regenerate handler > throttled path returns queued receipt and does not dispatch primary
[7.99ms]
message regenerate handler > mapMessageItem includes run error fields only for failed primary messages
[8.13ms]
previousRunEndedAtToolBoundary > empty history is not a boundary
[0.090ms]
previousRunEndedAtToolBoundary > history ending in an operator message is not a boundary
[0.080ms]
previousRunEndedAtToolBoundary > primary message ending on text is not a boundary
[0.080ms]
previousRunEndedAtToolBoundary > primary message carrying a tool call is a boundary
[0.080ms]
previousRunEndedAtToolBoundary > an injected system reminder after a tool-call primary does not mask the boundary
[0.040ms]
previousRunEndedAtToolBoundary > a superseded tool-call primary is ignored; older text primary wins
[0.030ms]
previousRunEndedAtToolBoundary > a disabled tool-call primary is ignored
[0.030ms]
previousRunEndedAtToolBoundary > most recent primary decides: text after a prior tool call is not a boundary
[0.040ms]
postMessageCore continue-suppression > continuation with a tool-call tail suppresses the operator message
[7.94ms]
postMessageCore continue-suppression > continuation with a text tail persists the operator message
[9.51ms]
postMessageCore continue-suppression > no continuation flag always persists the message (baseline)
[7.30ms]
postMessageCore continue-suppression > an operator literally typing 'continue' without the flag is never suppressed
[9.12ms]
postMessageCore continue-suppression > suppressed continuation still starts a new run
[7.66ms]
loadNativeBindings > tier 1: loads from KAGED_NATIVES_PATH when it points to a valid .node file
[98.43ms]
loadNativeBindings > tier 2: loads from beside execPath when .node is there
[119.71ms]
loadNativeBindings > tier 3: falls through to loadNative() when no .node file is found
[0.360ms]
loadNativeBindings > prefers tier 1 (KAGED_NATIVES_PATH) over tier 2 (beside execPath)
[142.93ms]
loadNativeBindings > caches bindings after the first successful call
[86.67ms]
loadNativeBindings > falls through to loadNative() when resolveNativesPath throws
[0.300ms]
loadNativeBindings > propagates require() error when a resolved .node file is corrupt
[0.800ms]
notification presence gate > zero sockets returns 0
[0.240ms]
notification presence gate > matching session socket counts
[0.230ms]
notification presence gate > non-matching session socket does not count
[0.110ms]
notification presence gate > session and system sockets roll up together
[0.240ms]
notification router > live operator sends only in-app
[1.69ms]
notification router > away operator sends all eligible tier-3 channels
[0.350ms]
notification router > silent bell suppresses tier-3 when away
[0.200ms]
notification router > run.completed is suppressed by attention-only routing
[0.270ms]
notification router > throwing channel is converted to failed outcome
[0.420ms]
notification router > rate limits duplicate attention events within five seconds
[0.380ms]
notification router > resolveRouting uses set-not-merge layers
[0.240ms]
notification normaliser > buildAttentionEvent creates a valid notification event
[2.40ms]
notification normaliser > buildCompletionEvent creates a valid completion notification
[0.100ms]
VAPID resolution > resolves inline PEM from local config
[2.66ms]
VAPID resolution > resolves PEM from env when config is absent
[0.750ms]
VAPID resolution > reads generated file when present
[1.07ms]
VAPID resolution > auto-generates a 0600 key file on first need
[1.05ms]
notification handlers > lists registered channels
[0.210ms]
notification handlers > returns VAPID public key when available
[0.170ms]
notification handlers > creates a push subscription
[0.380ms]
notification handlers > lists subscriptions for current operator
[0.210ms]
notification handlers > deletes a push subscription
[0.220ms]
notification handlers > sets session bell and returns updated session
[0.410ms]
notification handlers > dispatches synthetic notification test events
[0.470ms]
web push channel > encryptPushPayload produces a deterministic RFC8188 payload
[1.74ms]
web push channel > successful delivery returns delivered and sets VAPID headers
[2.80ms]
web push channel > 410 responses reap expired subscriptions
[1.49ms]
web push channel > 429 respects Retry-After backoff
[1.71ms]
web push channel > non-429 client errors are non-retryable
[1.52ms]