@kaged/plugin-host
JSON-RPC stdio transport, plugin process lifecycle with health checks and rate limiting, multi-plugin supervisor with role enforcement and tool registry
15
source files
14
test files
~7.4k
lines
✓ 322 pass
tests
pass
typecheck
clean
lint
Test results 322
parseCapability: filesystem > read:fs parses correctly
[0.150ms]
parseCapability: filesystem > write:fs parses correctly
[0.030ms]
parseCapability: filesystem > read:fs rejects relative path
[0.100ms]
parseCapability: filesystem > write:fs rejects relative path
[0.050ms]
parseCapability: exec > exec parses correctly
[0.040ms]
parseCapability: exec > exec with full binary path
[0.030ms]
parseCapability: exec > exec rejects missing path
[0.040ms]
parseCapability: exec > exec rejects relative path
[0.030ms]
parseCapability: exec > exec rejects empty binary
[0.030ms]
parseCapability: net > net:[] → none
[0.030ms]
parseCapability: net > net:* → all
[0.020ms]
parseCapability: net > net:host:port → host_port
[0.090ms]
parseCapability: net > net:host:* → host_all
[0.030ms]
parseCapability: net > net rejects invalid port
[0.030ms]
parseCapability: net > net rejects port 0
[0.030ms]
parseCapability: net > net rejects port 70000
[0.040ms]
parseCapability: net > net rejects empty host
[0.060ms]
parseCapability: storage > kaged:storage:read
[0.020ms]
parseCapability: storage > kaged:storage:write
[0.010ms]
parseCapability: unrecognized > unknown format throws
[0.030ms]
parseCapability: unrecognized > error includes the offending capability string
[0.050ms]
parseCapabilities > parses array of capabilities
[0.110ms]
parseCapabilities > empty array returns empty
[0.020ms]
validateCapabilitiesUsed > all used capabilities are declared → empty violations
[0.090ms]
validateCapabilitiesUsed > extra used capabilities → returns violations
[0.020ms]
validateCapabilitiesUsed > empty used → no violations
[0.020ms]
validateCapabilitiesUsed > empty declared, non-empty used → all are violations
[0.020ms]
mergePluginConfig > merges disjoint project and system config
[1.29ms]
mergePluginConfig > empty configs merge to empty object
[0.080ms]
mergePluginConfig > project-only config (no system schema) passes through
[0.050ms]
mergePluginConfig > system-only config (no project schema) passes through
[0.040ms]
mergePluginConfig > rejects project config containing system-only fields
[0.070ms]
mergePluginConfig > multiple system-only fields in project config produce multiple errors
[0.090ms]
mergePluginConfig > system config fields not in system schema are still merged
[0.050ms]
mergePluginConfig > system config values take precedence over project config for non-system-schema fields
[0.040ms]
mergePluginConfigWithAgentOverrides > agent overrides project config fields
[0.110ms]
mergePluginConfigWithAgentOverrides > empty agent overrides preserves base config
[0.040ms]
mergePluginConfigWithAgentOverrides > rejects agent overrides that touch system config fields
[0.050ms]
mergePluginConfigWithAgentOverrides > agent overrides add new project-side fields
[0.040ms]
JsonRpcConnection: sendRequest > sends valid JSON-RPC request with incrementing ids
[2.32ms]
JsonRpcConnection: sendRequest > resolves when response arrives
[0.680ms]
JsonRpcConnection: sendRequest > rejects on error response
[0.280ms]
JsonRpcConnection: sendRequest > rejects on timeout
[51.42ms]
JsonRpcConnection: sendRequest > rejects when connection is closed
[0.160ms]
JsonRpcConnection: sendNotification > sends notification without id
[0.130ms]
JsonRpcConnection: sendNotification > skips sending when closed
[0.050ms]
JsonRpcConnection: sendResponse > sends success response
[0.180ms]
JsonRpcConnection: sendErrorResponse > sends error response
[0.150ms]
JsonRpcConnection: incoming message routing > routes notifications to onNotification callback
[10.36ms]
JsonRpcConnection: incoming message routing > routes requests to onRequest callback
[11.91ms]
JsonRpcConnection: incoming message routing > reports protocol error for batch messages
[12.22ms]
JsonRpcConnection: incoming message routing > reports protocol error for invalid JSON
[10.88ms]
JsonRpcConnection: incoming message routing > reports protocol error for response with unknown id
[12.23ms]
JsonRpcConnection: multi-line buffering > handles multiple messages in one chunk
[12.40ms]
JsonRpcConnection: multi-line buffering > handles message split across chunks
[17.86ms]
JsonRpcConnection: multi-line buffering > skips empty lines
[12.20ms]
JsonRpcConnection: oversize message rejection > rejects messages exceeding max size
[10.87ms]
JsonRpcConnection: close behavior > isClosed returns true after close()
[0.360ms]
JsonRpcConnection: close behavior > close() rejects pending requests
[0.590ms]
JsonRpcConnection: close behavior > close() is idempotent
[0.200ms]
JsonRpcConnection: close behavior > stream close triggers onClose and rejects pending
[0.640ms]
JsonRpcConnection: concurrent requests > routes responses to correct pending requests
[1.18ms]
validateKnobValue — range > accepts value within bounds
[0.460ms]
validateKnobValue — range > accepts min boundary
[0.070ms]
validateKnobValue — range > accepts max boundary
[0.060ms]
validateKnobValue — range > rejects below min
[0.300ms]
validateKnobValue — range > rejects above max
[0.080ms]
validateKnobValue — range > rejects non-number
[0.080ms]
validateKnobValue — range > rejects NaN
[0.060ms]
validateKnobValue — range > rejects invalid step
[0.070ms]
validateKnobValue — range > accepts valid step 0.3
[0.080ms]
validateKnobValue — int_range > accepts integer within bounds
[0.210ms]
validateKnobValue — int_range > rejects float
[0.110ms]
validateKnobValue — int_range > rejects below min
[0.090ms]
validateKnobValue — int_range > rejects above max
[0.060ms]
validateKnobValue — int_range > rejects non-number
[0.060ms]
validateKnobValue — int_range > validates step alignment
[0.120ms]
validateKnobValue — enum > accepts valid value
[0.190ms]
validateKnobValue — enum > rejects invalid value
[0.080ms]
validateKnobValue — enum > rejects non-string
[0.060ms]
validateKnobValue — boolean > accepts true
[0.100ms]
validateKnobValue — boolean > accepts false
[0.040ms]
validateKnobValue — boolean > rejects string 'true'
[0.080ms]
validateKnobValue — text > accepts valid short lowercase string
[0.240ms]
validateKnobValue — text > rejects exceeding max_length
[0.080ms]
validateKnobValue — text > rejects pattern mismatch
[0.070ms]
validateKnobValue — text > rejects non-string
[0.050ms]
validateKnobValue — text > text without constraints accepts any string
[0.080ms]
validateKnobValue — multiline > accepts multiline string
[0.120ms]
validateKnobValue — multiline > rejects exceeding max_length
[0.090ms]
validateKnobValue — multiline > rejects non-string
[0.060ms]
validateKnobValue — model_alias > accepts null
[0.110ms]
validateKnobValue — model_alias > accepts valid string
[0.070ms]
validateKnobValue — model_alias > rejects empty string
[0.070ms]
validateKnobValue — model_alias > rejects non-string non-null
[0.060ms]
validateKnobValue — path > accepts config: prefix
[0.310ms]
validateKnobValue — path > accepts project: prefix
[0.070ms]
validateKnobValue — path > rejects other prefix
[0.110ms]
validateKnobValue — path > rejects non-string
[0.070ms]
validateKnobValue — path > path without prefixes constraint accepts anything
[0.070ms]
validateAllKnobValues > validates all values successfully
[0.270ms]
validateAllKnobValues > collects multiple errors
[0.110ms]
validateAllKnobValues > reports unknown knob names
[0.090ms]
validateAllKnobValues > empty values returns no errors
[0.070ms]
parseManifest: roles field (ADR-0024) > accepts roles: [observer]
[45.11ms]
parseManifest: roles field (ADR-0024) > accepts roles: [observer, compactor]
[3.58ms]
parseManifest: roles field (ADR-0024) > defaults roles to [observer] when hooks is non-empty and roles omitted
[2.52ms]
parseManifest: roles field (ADR-0024) > defaults roles to [] when no hooks are subscribed
[2.05ms]
parseManifest: roles field (ADR-0024) > rejects unknown role
[3.56ms]
parseManifest: roles field (ADR-0024) > rejects compactor role without pre_compact hook subscription
[2.04ms]
parseManifest: roles field (ADR-0024) > rejects duplicate roles
[1.18ms]
parseManifest: hooks field (ADR-0023) > accepts on_session_start
[0.720ms]
parseManifest: hooks field (ADR-0023) > accepts on_session_idle
[0.640ms]
parseManifest: hooks field (ADR-0023) > accepts pre_compact
[0.600ms]
parseManifest: hooks field (ADR-0023) > accepts all four hooks together
[0.660ms]
parseManifest: hooks field (ADR-0023) > defaults hooks to [] when omitted
[0.590ms]
parseManifest: hooks field (ADR-0023) > rejects unknown hook name
[0.620ms]
parseManifest: hooks field (ADR-0023) > rejects duplicate hook entries
[0.670ms]
parseManifest: tools field (ADR-0023) > accepts tools declaration
[2.44ms]
parseManifest: tools field (ADR-0023) > defaults tools to [] when omitted
[0.550ms]
parseManifest: tools field (ADR-0023) > rejects tool name with uppercase
[0.800ms]
parseManifest: tools field (ADR-0023) > rejects tool name with dots (must be single segment; prefix is applied at registration)
[0.890ms]
parseManifest: tools field (ADR-0023) > rejects duplicate tool names within same manifest
[0.930ms]
parseManifest: tools field (ADR-0023) > rejects tool missing required parameters_schema
[0.600ms]
parseManifest: methods optional when tools or hooks present (ADR-0023) > methods empty + tools non-empty is OK
[0.740ms]
parseManifest: methods optional when tools or hooks present (ADR-0023) > methods empty + hooks non-empty is OK
[0.510ms]
parseManifest: methods optional when tools or hooks present (ADR-0023) > methods omitted + tools non-empty is OK
[0.610ms]
parseManifest: system_config_schema (ADR-0023) > accepts system_config_schema
[0.730ms]
parseManifest: system_config_schema (ADR-0023) > defaults systemConfigSchema to undefined when omitted
[0.460ms]
parseManifest: system_config_schema (ADR-0023) > rejects field-name collision between config_schema and system_config_schema
[0.870ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=range
[1.33ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=enum + labels map
[1.06ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=int_range
[0.890ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=boolean
[0.820ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=text
[0.800ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=path + prefixes
[0.890ms]
parseManifest: knobs (ADR-0024) > accepts knob with type=model_alias
[0.830ms]
parseManifest: knobs (ADR-0024) > rejects knob with unknown type
[0.900ms]
parseManifest: knobs (ADR-0024) > rejects knob with binds_to referencing a non-existent config_schema field
[0.980ms]
parseManifest: knobs (ADR-0024) > rejects knob with binds_to pointing into system_config_schema
[1.10ms]
parseManifest: knobs (ADR-0024) > rejects knob with binds_to that does not start with 'config.'
[0.890ms]
parseManifest: knobs (ADR-0024) > defaults knobs to empty record when omitted
[0.450ms]
parseManifest: hook_timeout_sec (ADR-0023) > accepts a valid hook_timeout_sec
[0.480ms]
parseManifest: hook_timeout_sec (ADR-0023) > defaults hook_timeout_sec to 10
[0.660ms]
parseManifest: hook_timeout_sec (ADR-0023) > rejects hook_timeout_sec greater than 60
[0.490ms]
parseManifest: memory-markdown manifest integration > the actual memory-markdown manifest at plugins/project/memory-markdown/kaged-plugin.yaml parses
[17.23ms]
parseManifest: valid manifests > parses full manifest
[4.92ms]
parseManifest: valid manifests > parses minimal manifest with defaults
[0.680ms]
parseManifest: invalid YAML > throws ManifestValidationError on broken YAML
[0.820ms]
parseManifest: schema violations > missing name
[0.460ms]
parseManifest: schema violations > invalid name format (uppercase)
[0.460ms]
parseManifest: schema violations > name too long
[0.460ms]
parseManifest: schema violations > missing methods AND missing hooks AND missing tools (plugin does nothing)
[0.400ms]
parseManifest: schema violations > empty methods array AND no hooks AND no tools (plugin does nothing)
[0.440ms]
parseManifest: schema violations > invalid method name (reserved prefix)
[0.510ms]
parseManifest: schema violations > invalid method name (single segment)
[0.430ms]
parseManifest: schema violations > description too long
[0.520ms]
parseManifest: schema violations > shutdown_timeout_sec too high
[0.490ms]
parseManifest: schema violations > health_interval_sec too low
[0.470ms]
parseManifest: schema violations > invalid homepage URL
[0.940ms]
parseManifest: schema violations > error includes issues array
[0.350ms]
PluginCallContext: canonical shape > daemon-level context has all agent fields null
[0.150ms]
PluginCallContext: canonical shape > agent-run context has all four agent fields populated
[0.050ms]
PluginCallContext: canonical shape > nested subagent path is accepted
[0.040ms]
PluginCallContext: canonical shape > operator_id may be null in insecure-mode daemon-level call
[0.040ms]
PluginCallContext: canonical shape > request_id is always required (non-empty string)
[0.080ms]
PluginCallContext: invariant — agent fields are all-or-none > project_id set but agent_path null is a contract violation
[0.060ms]
PluginCallContext: invariant — agent fields are all-or-none > agent_path set but project_id null is a contract violation
[0.060ms]
PluginCallContext: invariant — agent fields are all-or-none > session_id set but agent_path null is a contract violation
[0.050ms]
PluginCallContext: invariant — agent fields are all-or-none > project_id+agent_path set but session_id null is a contract violation
[0.050ms]
PluginCallContext: invariant — agent fields are all-or-none > error message names which fields are inconsistent
[0.070ms]
buildPluginCallContext: helper builder > daemon-level call: operator + request_id only
[0.060ms]
buildPluginCallContext: helper builder > agent-run: all four fields propagated
[0.030ms]
buildPluginCallContext: helper builder > builder validates the invariant — partial agent fields throw
[0.060ms]
buildPluginCallContext: helper builder > missing request_id throws
[0.030ms]
buildPluginCallContext: helper builder > null operator_id is accepted (insecure-mode daemon-level call)
[0.040ms]
PluginProcess.fireHook: gating > returns not_running when plugin is disabled
[0.710ms]
PluginProcess.fireHook: gating > returns not_subscribed when plugin does not list the hook
[26.54ms]
PluginProcess.fireHook: gating > returns not_subscribed for pre_compact when only on_session_start subscribed
[23.08ms]
PluginProcess.fireHook: successful dispatch > on_session_start sends kaged.hook.on_session_start with _context and returns inject
[35.96ms]
PluginProcess.fireHook: successful dispatch > on_session_idle sends transcript params and returns ok with null result
[33.70ms]
PluginProcess.fireHook: successful dispatch > pre_compact passes role and strategy params
[33.89ms]
PluginProcess.fireHook: failure handling > JSON-RPC error response returns error status with audit
[35.77ms]
PluginProcess.fireHook: failure handling > timeout returns timeout status with audit
[1.03s]
PluginProcess.fireHook: failure handling > uses hookTimeoutSec from manifest (not default METHOD_CALL_TIMEOUT_MS)
[1.02s]
PluginSupervisor.fireHook > returns empty array for empty pluginNames
[1.11ms]
PluginSupervisor.fireHook > returns not_found for unknown plugin
[0.380ms]
PluginSupervisor.fireHook > fires hooks in order on registered plugins (not_running since disabled)
[1.03ms]
PluginSupervisor.fireHook > mixed results: found + not_found + not_subscribed
[0.560ms]
plugin machine: disabled → spawning (enable) > disabled + enable → spawning
[0.120ms]
plugin machine: disabled → spawning (enable) > emits spawn_process, send_initialize, audit
[0.180ms]
plugin machine: disabled → spawning (enable) > running + enable → TransitionError
[0.300ms]
plugin machine: spawning → running (handshake_ok) > spawning + handshake_ok → running
[0.110ms]
plugin machine: spawning → running (handshake_ok) > emits send_initialized, reset_failure_counter, audit
[0.120ms]
plugin machine: spawning → running (handshake_ok) > running + handshake_ok → TransitionError
[0.130ms]
plugin machine: spawning → failed (handshake_fail) > spawning + handshake_fail → failed
[0.210ms]
plugin machine: spawning → failed (handshake_fail) > emits kill_process, mark_failed, audit
[0.150ms]
plugin machine: crash with backoff > running + crash (first failure) → crashed with 1s backoff
[0.200ms]
plugin machine: crash with backoff > running + crash (2nd failure) → crashed with 2s backoff
[0.180ms]
plugin machine: crash with backoff > running + crash (4th failure) → crashed with 8s backoff
[0.080ms]
plugin machine: crash with backoff > running + crash (5th failure) → failed
[0.120ms]
plugin machine: crash with backoff > spawning + crash → crashed (process died during handshake)
[0.090ms]
plugin machine: crash with backoff > disabled + crash → TransitionError
[0.120ms]
plugin machine: health_fail_limit > running + health_fail_limit (first) → crashed
[0.290ms]
plugin machine: health_fail_limit > running + health_fail_limit (5th) → failed
[0.080ms]
plugin machine: health_fail_limit > disabled + health_fail_limit → TransitionError
[0.130ms]
plugin machine: crashed → spawning (restart) > crashed + restart → spawning
[0.130ms]
plugin machine: crashed → spawning (restart) > running + restart → TransitionError
[0.130ms]
plugin machine: running → stopped (shutdown) > running + shutdown → stopped
[0.160ms]
plugin machine: running → stopped (shutdown) > disabled + shutdown → TransitionError
[0.100ms]
plugin machine: any → disabled (disable) > running + disable → disabled (kills process)
[0.200ms]
plugin machine: any → disabled (disable) > spawning + disable → disabled (kills process)
[0.110ms]
plugin machine: any → disabled (disable) > crashed + disable → disabled (no kill needed)
[0.090ms]
plugin machine: any → disabled (disable) > failed + disable → disabled
[0.050ms]
plugin machine: any → disabled (disable) > stopped + disable → disabled
[0.050ms]
plugin machine: any → disabled (disable) > disabled + disable → TransitionError
[0.110ms]
plugin machine: failed → spawning (operator_enable) > failed + operator_enable → spawning
[0.220ms]
plugin machine: failed → spawning (operator_enable) > running + operator_enable → TransitionError
[0.090ms]
plugin machine: failed → spawning (operator_enable) > disabled + operator_enable → TransitionError
[0.120ms]
plugin machine: full lifecycle > disabled → enable → handshake → running → shutdown → stopped
[0.140ms]
plugin machine: full lifecycle > crash → restart → handshake → running
[0.120ms]
plugin machine: full lifecycle > repeated crashes → failed → operator_enable → spawning
[0.090ms]
PluginTransitionError properties > includes from and event
[0.170ms]
PluginProcess: initial state > starts in disabled state
[0.360ms]
PluginProcess: enable + handshake > enable transitions to spawning and sends initialize
[26.43ms]
PluginProcess: enable + handshake > handshake failure on name mismatch → failed state
[23.48ms]
PluginProcess: enable + handshake > handshake failure on api_version mismatch → failed state
[24.03ms]
PluginProcess: enable + handshake > handshake failure on capability overreach → failed state
[22.62ms]
PluginProcess: spawn environment > builds correct spawn env with protected keys
[13.27ms]
PluginProcess: method calls > callMethod sends request with _context and returns result
[36.28ms]
PluginProcess: method calls > callMethod throws when not running
[0.580ms]
PluginProcess: notification handling > forwards plugin notifications via callback
[37.24ms]
PluginProcess: state change tracking > fires onStateChange for each transition
[23.84ms]
PluginProcess: shutdown > sends shutdown notification
[25.74ms]
PluginProcess: shutdown > shutdown on non-running state is no-op
[0.100ms]
PluginProcess: disable > disable from running kills process
[23.18ms]
PluginSupervisor: registration > registerPlugin adds a plugin
[0.500ms]
PluginSupervisor: registration > registerPlugin rejects duplicate names
[0.320ms]
PluginSupervisor: registration > registerPlugin rejects after startAll
[1.29ms]
PluginSupervisor: registration > registerFromYaml parses YAML and registers
[1.93ms]
PluginSupervisor: listing > listPlugins returns sorted list
[1.59ms]
PluginSupervisor: listing > listPlugins returns empty for fresh supervisor
[0.120ms]
PluginSupervisor: listing > getPluginInfo returns correct details
[0.280ms]
PluginSupervisor: listing > getPluginInfo returns null for unknown plugin
[0.070ms]
PluginSupervisor: listing > getPluginState returns state for registered plugin
[0.170ms]
PluginSupervisor: listing > getPluginState returns null for unknown plugin
[0.060ms]
PluginSupervisor: hasPlugin > returns true for registered plugin
[0.180ms]
PluginSupervisor: hasPlugin > returns false for unregistered plugin
[0.060ms]
PluginSupervisor: enable/disable > enablePlugin throws for unknown plugin
[0.380ms]
PluginSupervisor: enable/disable > disablePlugin throws for unknown plugin
[0.330ms]
PluginSupervisor: callMethod > callMethod throws for unknown plugin
[0.230ms]
PluginSupervisor: isStarted flag > isStarted is false initially
[0.080ms]
PluginSupervisor: isStarted flag > isStarted is true after startAll
[0.240ms]
PluginSupervisor: isStarted flag > isStarted is false after stopAll
[0.230ms]
PluginSupervisor: isStarted flag > startAll is idempotent
[0.260ms]
PluginSupervisor: audit events > emits plugin.disabled audit for disabled plugins on register
[0.320ms]
PluginSupervisor: multiple plugins > registers and lists multiple plugins
[0.330ms]
PluginSupervisor: stopAll with no plugins > stopAll on empty supervisor completes
[0.270ms]
isValidMethodName > valid: two segments
[1.49ms]
isValidMethodName > valid: three segments
[0.100ms]
isValidMethodName > valid: four segments
[0.050ms]
isValidMethodName > valid: with underscores
[0.050ms]
isValidMethodName > valid: with digits
[0.040ms]
isValidMethodName > invalid: single segment
[0.030ms]
isValidMethodName > invalid: five segments
[0.050ms]
isValidMethodName > invalid: uppercase
[0.050ms]
isValidMethodName > invalid: starts with digit
[0.050ms]
isValidMethodName > invalid: hyphens
[0.040ms]
isValidMethodName > invalid: empty
[0.030ms]
isValidMethodName > invalid: segment starts with digit
[0.040ms]
isReservedMethod > kaged.* is reserved
[0.170ms]
isReservedMethod > system.* is reserved
[0.060ms]
isReservedMethod > presets.list is not reserved
[0.050ms]
validatePluginMethodName > valid method passes
[0.140ms]
validatePluginMethodName > invalid format throws ProtocolError
[0.130ms]
validatePluginMethodName > reserved name throws ProtocolError
[0.090ms]
validatePluginMethodName > error code is -32600
[0.130ms]
validatePluginMethods > all valid passes
[0.200ms]
validatePluginMethods > one invalid throws
[0.180ms]
JSON-RPC message classification > request
[0.150ms]
JSON-RPC message classification > notification (no id)
[0.110ms]
JSON-RPC message classification > success response
[0.090ms]
JSON-RPC message classification > error response
[0.070ms]
JSON-RPC message classification > batch
[0.080ms]
JSON-RPC message classification > null is not any type
[0.080ms]
classifyMessage > classifies request
[0.130ms]
classifyMessage > classifies notification
[0.090ms]
classifyMessage > classifies response
[0.090ms]
classifyMessage > classifies batch
[0.080ms]
classifyMessage > invalid JSON → invalid
[0.060ms]
classifyMessage > valid JSON but not JSON-RPC → invalid
[0.090ms]
JSON_RPC_ERRORS > has expected codes
[0.210ms]
pluginStoragePrefix > simple name
[0.110ms]
pluginStoragePrefix > hyphenated name converted to underscores
[0.070ms]
pluginStoragePrefix > name with no hyphens
[0.040ms]
validateAgentPluginRoles > empty manifests list returns no errors
[0.210ms]
validateAgentPluginRoles > single observer returns no errors
[0.220ms]
validateAgentPluginRoles > single compactor returns no errors
[0.090ms]
validateAgentPluginRoles > multiple observers returns no errors
[0.130ms]
validateAgentPluginRoles > two compactors returns error on the second
[0.170ms]
validateAgentPluginRoles > three compactors returns errors on second and third
[0.170ms]
validateAgentPluginRoles > compactor + observers returns no errors
[0.090ms]
validateAgentPluginRoles > observers + two compactors returns error only on second compactor
[0.120ms]
validateAgentPluginRoles > plugins with empty roles are fine
[0.130ms]
validateAgentPluginRoles > dual-role plugin (observer+compactor) counts as the compactor
[0.130ms]
prefixToolName > joins plugin name and tool name with dot
[0.100ms]
prefixToolName > single-segment plugin name
[0.050ms]
checkBuiltInNamespaceCollision > returns null for non-colliding names
[0.180ms]
checkBuiltInNamespaceCollision > returns warning for 'file'
[0.100ms]
checkBuiltInNamespaceCollision > returns warning for 'search'
[0.050ms]
checkBuiltInNamespaceCollision > returns warning for 'lsp'
[0.050ms]
checkBuiltInNamespaceCollision > returns warning for 'dap'
[0.040ms]
checkBuiltInNamespaceCollision > returns warning for 'kaged'
[0.050ms]
checkBuiltInNamespaceCollision > does not match partial overlaps like 'file-utils'
[0.050ms]
buildToolRegistry > empty manifests list returns empty result
[0.360ms]
buildToolRegistry > registers tools with prefixed names
[0.420ms]
buildToolRegistry > detects tool name collision between plugins
[0.200ms]
buildToolRegistry > first plugin wins on collision — second is rejected
[0.130ms]
buildToolRegistry > multiple plugins with different tools coexist
[0.160ms]
buildToolRegistry > emits warning for built-in namespace collision
[0.120ms]
buildToolRegistry > plugin with no tools contributes nothing
[0.100ms]
buildToolRegistry > rejects prefixed names that don't match the pattern
[0.110ms]