@phuetz/code-buddy 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fr.md +56 -0
- package/README.md +73 -16
- package/codebuddy-runtime.json +5 -5
- package/dist/agent/codebuddy-agent.d.ts +18 -0
- package/dist/agent/codebuddy-agent.js +45 -8
- package/dist/agent/execution/agent-executor.d.ts +0 -9
- package/dist/agent/execution/agent-executor.js +165 -12
- package/dist/agent/execution/context-pipeline.d.ts +9 -0
- package/dist/agent/execution/context-pipeline.js +64 -2
- package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
- package/dist/agent/execution/tool-loop-guard.js +165 -0
- package/dist/agent/execution/tool-selection-strategy.js +19 -2
- package/dist/agent/facades/session-facade.d.ts +0 -6
- package/dist/agent/facades/session-facade.js +0 -45
- package/dist/agent/hermes-claw-migrate.d.ts +0 -21
- package/dist/agent/hermes-claw-migrate.js +9 -4
- package/dist/agent/hermes-protocol-gateways.js +8 -1
- package/dist/agent/repo-profiler.js +17 -4
- package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
- package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
- package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
- package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
- package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
- package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
- package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
- package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
- package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
- package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
- package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
- package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
- package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
- package/dist/agent/self-improvement/proposal-store.js +79 -17
- package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
- package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
- package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
- package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
- package/dist/agent/self-improvement/skill-engine.js +385 -13
- package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
- package/dist/agent/self-improvement/skill-gate.js +17 -2
- package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
- package/dist/agent/self-improvement/skill-mutator.js +51 -15
- package/dist/agent/self-improvement/skill-types.d.ts +2 -1
- package/dist/agent/self-improvement/types.d.ts +11 -0
- package/dist/agent/tool-handler.d.ts +1 -1
- package/dist/agent/tool-handler.js +28 -9
- package/dist/auth/profile-manager.js +1 -1
- package/dist/channels/companion-channel-turn.d.ts +53 -4
- package/dist/channels/companion-channel-turn.js +218 -16
- package/dist/channels/slash-parity.d.ts +4 -6
- package/dist/channels/slash-parity.js +9 -44
- package/dist/cli/session-commands.d.ts +9 -0
- package/dist/cli/session-commands.js +45 -5
- package/dist/cli/session-picker.d.ts +43 -0
- package/dist/cli/session-picker.js +128 -0
- package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
- package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
- package/dist/codebuddy/fleet-tool-defs.d.ts +1 -0
- package/dist/codebuddy/fleet-tool-defs.js +24 -0
- package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
- package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
- package/dist/codebuddy/providers/provider-openai-compat.js +1 -1
- package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
- package/dist/codebuddy/ragchat-tool-defs.js +19 -0
- package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
- package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
- package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
- package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
- package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
- package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
- package/dist/codebuddy/tools.d.ts +0 -6
- package/dist/codebuddy/tools.js +25 -3
- package/dist/commands/cli/daemon-commands.js +3 -1
- package/dist/commands/cli/evolve-command.js +21 -5
- package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
- package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
- package/dist/commands/cli/fleet-commands.js +29 -0
- package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
- package/dist/commands/cli/fleet-mission-commands.js +72 -0
- package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
- package/dist/commands/cli/fleet-rooms-commands.js +289 -0
- package/dist/commands/cli/tools-commands.js +2 -1
- package/dist/commands/cli/triage-command.d.ts +8 -0
- package/dist/commands/cli/triage-command.js +72 -0
- package/dist/commands/cli/utility-commands.js +25 -1
- package/dist/commands/client-dispatcher.d.ts +7 -0
- package/dist/commands/client-dispatcher.js +73 -21
- package/dist/commands/cron-cli/index.d.ts +3 -1
- package/dist/commands/cron-cli/index.js +25 -0
- package/dist/commands/device-auth.d.ts +10 -0
- package/dist/commands/device-auth.js +91 -0
- package/dist/commands/enhanced-command-handler.d.ts +9 -0
- package/dist/commands/enhanced-command-handler.js +28 -6
- package/dist/commands/handlers/agent-handlers.d.ts +1 -1
- package/dist/commands/handlers/agent-handlers.js +4 -4
- package/dist/commands/handlers/channel-handlers.js +53 -2
- package/dist/commands/handlers/export-handlers.d.ts +3 -2
- package/dist/commands/handlers/export-handlers.js +55 -28
- package/dist/commands/handlers/extra-handlers.js +5 -1
- package/dist/commands/handlers/grill-me-handler.js +26 -18
- package/dist/commands/handlers/memory-handlers.d.ts +6 -2
- package/dist/commands/handlers/memory-handlers.js +35 -15
- package/dist/commands/handlers/missing-handlers.d.ts +2 -2
- package/dist/commands/handlers/missing-handlers.js +4 -4
- package/dist/commands/handlers/resources-handler.d.ts +12 -0
- package/dist/commands/handlers/resources-handler.js +52 -0
- package/dist/commands/handlers/starter-handlers.d.ts +1 -1
- package/dist/commands/handlers/starter-handlers.js +32 -2
- package/dist/commands/handlers/switch-handler.d.ts +1 -1
- package/dist/commands/handlers/switch-handler.js +15 -0
- package/dist/commands/handlers/test-handlers.js +4 -86
- package/dist/commands/handlers/ui-handlers.js +11 -5
- package/dist/commands/login-chatgpt.d.ts +8 -0
- package/dist/commands/login-chatgpt.js +17 -0
- package/dist/commands/mcp-import.d.ts +11 -0
- package/dist/commands/mcp-import.js +67 -0
- package/dist/commands/mcp-invoke.d.ts +2 -0
- package/dist/commands/mcp-invoke.js +68 -0
- package/dist/commands/mcp.js +4 -0
- package/dist/commands/pipeline.d.ts +7 -1
- package/dist/commands/pipeline.js +104 -7
- package/dist/commands/resources.d.ts +2 -0
- package/dist/commands/resources.js +64 -0
- package/dist/commands/skills-cli/index.js +35 -12
- package/dist/commands/slash/builtin-commands.js +16 -3
- package/dist/commands/slash/index.d.ts +3 -1
- package/dist/commands/slash/index.js +2 -0
- package/dist/commands/slash/surfaces.d.ts +53 -0
- package/dist/commands/slash/surfaces.js +177 -0
- package/dist/commands/slash/types.d.ts +17 -0
- package/dist/commands/try.d.ts +1 -1
- package/dist/commands/try.js +1 -1
- package/dist/companion/companion-identity.d.ts +43 -0
- package/dist/companion/companion-identity.js +136 -0
- package/dist/companion/companion-toolset.d.ts +66 -0
- package/dist/companion/companion-toolset.js +368 -0
- package/dist/companion/companion-turn.d.ts +10 -2
- package/dist/companion/companion-turn.js +38 -1
- package/dist/config/code-exec-policy.d.ts +25 -0
- package/dist/config/code-exec-policy.js +39 -0
- package/dist/config/model-tools.d.ts +9 -0
- package/dist/config/model-tools.js +4 -0
- package/dist/context/context-manager-v2.js +2 -2
- package/dist/context/lm-resizer-compressor.js +3 -2
- package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
- package/dist/context/lm-resizer-diagnostics.js +30 -0
- package/dist/context/tool-output-masking.js +3 -4
- package/dist/curator/curator.d.ts +6 -0
- package/dist/curator/curator.js +53 -3
- package/dist/daemon/cron-agent-bridge.d.ts +6 -0
- package/dist/daemon/cron-agent-bridge.js +31 -4
- package/dist/doctor/index.d.ts +11 -2
- package/dist/doctor/index.js +84 -21
- package/dist/doctor/integrations.d.ts +179 -0
- package/dist/doctor/integrations.js +227 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -0
- package/dist/fleet/collaboration.d.ts +44 -0
- package/dist/fleet/collaboration.js +170 -0
- package/dist/fleet/fleet-listener.d.ts +1 -0
- package/dist/fleet/fleet-listener.js +87 -46
- package/dist/fleet/fleet-registry.d.ts +2 -0
- package/dist/fleet/resource-catalog.d.ts +245 -0
- package/dist/fleet/resource-catalog.js +211 -0
- package/dist/fleet/rooms/room-access.d.ts +138 -0
- package/dist/fleet/rooms/room-access.js +196 -0
- package/dist/fleet/rooms/room-client.d.ts +158 -0
- package/dist/fleet/rooms/room-client.js +685 -0
- package/dist/fleet/rooms/room-event.d.ts +139 -0
- package/dist/fleet/rooms/room-event.js +330 -0
- package/dist/fleet/rooms/room-filter.d.ts +48 -0
- package/dist/fleet/rooms/room-filter.js +152 -0
- package/dist/fleet/rooms/room-hub.d.ts +154 -0
- package/dist/fleet/rooms/room-hub.js +363 -0
- package/dist/fleet/rooms/room-identity.d.ts +26 -0
- package/dist/fleet/rooms/room-identity.js +82 -0
- package/dist/fleet/rooms/room-lock.d.ts +34 -0
- package/dist/fleet/rooms/room-lock.js +180 -0
- package/dist/fleet/rooms/room-observations.d.ts +7 -0
- package/dist/fleet/rooms/room-observations.js +61 -0
- package/dist/fleet/rooms/room-server.d.ts +28 -0
- package/dist/fleet/rooms/room-server.js +75 -0
- package/dist/fleet/rooms/room-store.d.ts +119 -0
- package/dist/fleet/rooms/room-store.js +448 -0
- package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
- package/dist/fleet/rooms/room-ws-bridge.js +137 -0
- package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
- package/dist/fleet/sensory-mission-bridge.js +136 -0
- package/dist/git/worktree-sessions.js +1 -0
- package/dist/harness/contract.d.ts +4 -4
- package/dist/harness/fleet-supervisor.d.ts +16 -0
- package/dist/harness/fleet-supervisor.js +64 -0
- package/dist/harness/index.d.ts +4 -0
- package/dist/harness/index.js +4 -0
- package/dist/harness/mission-runner.d.ts +3 -0
- package/dist/harness/mission-runner.js +53 -0
- package/dist/harness/mission-store.d.ts +93 -0
- package/dist/harness/mission-store.js +297 -0
- package/dist/harness/tool-harness.d.ts +56 -0
- package/dist/harness/tool-harness.js +153 -0
- package/dist/hooks/use-enhanced-input.js +48 -4
- package/dist/hooks/use-input-handler.d.ts +1 -0
- package/dist/hooks/use-input-handler.js +82 -22
- package/dist/hooks/use-input-history.js +1 -0
- package/dist/identity/lisa-introspection.js +5 -2
- package/dist/identity/operational-self-model.d.ts +2 -0
- package/dist/identity/operational-self-model.js +4 -0
- package/dist/index.js +52 -6
- package/dist/mcp/client.js +6 -0
- package/dist/mcp/config.d.ts +20 -0
- package/dist/mcp/config.js +121 -8
- package/dist/mcp/import-normalize.d.ts +12 -0
- package/dist/mcp/import-normalize.js +114 -0
- package/dist/mcp/transports.d.ts +10 -5
- package/dist/mcp/transports.js +61 -99
- package/dist/mcp/types.d.ts +5 -0
- package/dist/media/comfyui-recipe-contract.d.ts +7 -7
- package/dist/memory/facts-memory.d.ts +11 -1
- package/dist/memory/facts-memory.js +47 -8
- package/dist/memory/memory-candidate-queue.d.ts +2 -2
- package/dist/memory/memory-candidate-queue.js +6 -6
- package/dist/memory/persistent-memory.d.ts +13 -8
- package/dist/memory/persistent-memory.js +77 -30
- package/dist/nodes/device-node.js +5 -9
- package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
- package/dist/observability/run-event-writer.d.ts +21 -0
- package/dist/observability/run-event-writer.js +81 -0
- package/dist/observability/run-store.d.ts +5 -0
- package/dist/observability/run-store.js +18 -13
- package/dist/observability/run-trajectory.js +1 -1
- package/dist/optimization/prompt-cache.d.ts +19 -2
- package/dist/optimization/prompt-cache.js +41 -6
- package/dist/orchestration/orchestrator.d.ts +21 -0
- package/dist/orchestration/orchestrator.js +122 -15
- package/dist/persistence/session-content.d.ts +8 -0
- package/dist/persistence/session-content.js +76 -0
- package/dist/persistence/session-handoff.d.ts +64 -0
- package/dist/persistence/session-handoff.js +80 -0
- package/dist/persistence/session-history.d.ts +4 -0
- package/dist/persistence/session-history.js +38 -0
- package/dist/persistence/session-store.d.ts +9 -0
- package/dist/persistence/session-store.js +48 -20
- package/dist/personas/persona-manager.js +17 -1
- package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
- package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
- package/dist/plugins/plugin-manager.js +10 -1
- package/dist/prompts/prompt-manager.js +11 -4
- package/dist/prompts/system-base.js +6 -2
- package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
- package/dist/protocols/a2a/codebuddy-executor.js +9 -8
- package/dist/protocols/a2a/index.d.ts +1 -1
- package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
- package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
- package/dist/protocols/a2a/peer-config.d.ts +20 -0
- package/dist/protocols/a2a/peer-config.js +55 -0
- package/dist/providers/codex-oauth.js +30 -6
- package/dist/providers/model-provider-compat.js +2 -1
- package/dist/sandbox/os-sandbox.d.ts +1 -1
- package/dist/scheduler/cron-scheduler.d.ts +15 -2
- package/dist/scheduler/cron-scheduler.js +35 -4
- package/dist/scheduler/job-notepad.d.ts +69 -0
- package/dist/scheduler/job-notepad.js +339 -0
- package/dist/security/audit-logger.d.ts +1 -1
- package/dist/security/compute-confinement.d.ts +4 -0
- package/dist/security/compute-confinement.js +89 -0
- package/dist/security/session-encryption.d.ts +4 -0
- package/dist/security/session-encryption.js +31 -0
- package/dist/sensory/voice-loop.d.ts +17 -0
- package/dist/sensory/voice-loop.js +72 -15
- package/dist/server/auth/device-session-context.d.ts +9 -0
- package/dist/server/auth/device-session-context.js +13 -0
- package/dist/server/auth/device-store.d.ts +89 -0
- package/dist/server/auth/device-store.js +261 -0
- package/dist/server/auth/device-token.d.ts +3 -0
- package/dist/server/auth/device-token.js +6 -0
- package/dist/server/auth/jwt.js +4 -0
- package/dist/server/index.d.ts +0 -10
- package/dist/server/index.js +28 -0
- package/dist/server/middleware/auth.js +3 -2
- package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
- package/dist/server/routes/a2a-jsonrpc.js +66 -0
- package/dist/server/routes/a2a-protocol.d.ts +12 -0
- package/dist/server/routes/a2a-protocol.js +1 -1
- package/dist/server/routes/device-auth.d.ts +4 -0
- package/dist/server/routes/device-auth.js +40 -0
- package/dist/server/types.d.ts +4 -0
- package/dist/server/websocket/handler.d.ts +13 -0
- package/dist/server/websocket/handler.js +102 -17
- package/dist/services/prompt-builder.d.ts +2 -0
- package/dist/services/prompt-builder.js +13 -5
- package/dist/services/runtime-settings-context.d.ts +73 -0
- package/dist/services/runtime-settings-context.js +69 -0
- package/dist/skills/executor.js +2 -0
- package/dist/skills/hub.js +16 -14
- package/dist/skills/local-inventory.d.ts +21 -0
- package/dist/skills/local-inventory.js +72 -0
- package/dist/skills/skill-loader.js +2 -2
- package/dist/skills/skill-usage-store.d.ts +49 -0
- package/dist/skills/skill-usage-store.js +121 -0
- package/dist/testing/ai-integration-tests.js +8 -5
- package/dist/themes/theme-manager.d.ts +12 -0
- package/dist/themes/theme-manager.js +29 -0
- package/dist/themes/theme-schema.d.ts +24 -24
- package/dist/tools/a2a-call-tool.d.ts +34 -0
- package/dist/tools/a2a-call-tool.js +75 -0
- package/dist/tools/bash/streaming-executor.d.ts +1 -0
- package/dist/tools/bash/streaming-executor.js +78 -90
- package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
- package/dist/tools/code-exec-preflight-runner.js +254 -0
- package/dist/tools/code-exec-preflight.d.ts +44 -0
- package/dist/tools/code-exec-preflight.js +501 -0
- package/dist/tools/code-exec-tool.d.ts +48 -1
- package/dist/tools/code-exec-tool.js +191 -47
- package/dist/tools/comment-watcher.js +29 -12
- package/dist/tools/core-code-inspection.d.ts +56 -0
- package/dist/tools/core-code-inspection.js +86 -0
- package/dist/tools/create-skill-tool.d.ts +2 -3
- package/dist/tools/create-skill-tool.js +16 -75
- package/dist/tools/cronjob-tool.d.ts +1 -0
- package/dist/tools/cronjob-tool.js +1 -0
- package/dist/tools/env-doctor-tool.d.ts +2 -0
- package/dist/tools/env-doctor-tool.js +4 -3
- package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
- package/dist/tools/execute-code-rpc-invoker.js +35 -19
- package/dist/tools/execute-code-runner.d.ts +2 -0
- package/dist/tools/execute-code-runner.js +52 -17
- package/dist/tools/fleet-room-tool.d.ts +104 -0
- package/dist/tools/fleet-room-tool.js +124 -0
- package/dist/tools/integration-tool-hints.d.ts +4 -0
- package/dist/tools/integration-tool-hints.js +19 -0
- package/dist/tools/list-peers-tool.d.ts +3 -0
- package/dist/tools/list-peers-tool.js +11 -4
- package/dist/tools/media-generation-tool.d.ts +16 -1
- package/dist/tools/media-generation-tool.js +341 -9
- package/dist/tools/merge-conflict-tool.js +53 -25
- package/dist/tools/metadata.d.ts +1 -5
- package/dist/tools/metadata.js +21 -28
- package/dist/tools/ragchat-tool.d.ts +39 -0
- package/dist/tools/ragchat-tool.js +105 -0
- package/dist/tools/registry/fleet-tools.d.ts +9 -0
- package/dist/tools/registry/fleet-tools.js +22 -1
- package/dist/tools/registry/knowledge-tools.d.ts +2 -2
- package/dist/tools/registry/knowledge-tools.js +2 -2
- package/dist/tools/registry/lessons-tools.d.ts +7 -0
- package/dist/tools/registry/lessons-tools.js +43 -3
- package/dist/tools/registry/memory-tools.d.ts +1 -1
- package/dist/tools/registry/memory-tools.js +8 -8
- package/dist/tools/registry/research-tools.d.ts +1 -0
- package/dist/tools/registry/research-tools.js +4 -13
- package/dist/tools/registry/self-describe-tools.d.ts +1 -1
- package/dist/tools/registry/self-describe-tools.js +43 -30
- package/dist/tools/resource-catalog-tool.d.ts +38 -0
- package/dist/tools/resource-catalog-tool.js +46 -0
- package/dist/tools/skills-inspection-tool.js +30 -1
- package/dist/tools/stream-tool-output.d.ts +3 -0
- package/dist/tools/stream-tool-output.js +33 -0
- package/dist/tools/tool-call-scheduler.d.ts +10 -0
- package/dist/tools/tool-call-scheduler.js +58 -0
- package/dist/tools/tool-effect.d.ts +5 -0
- package/dist/tools/tool-effect.js +26 -0
- package/dist/tools/tool-search.d.ts +12 -11
- package/dist/tools/tool-search.js +67 -26
- package/dist/tools/types.d.ts +6 -0
- package/dist/ui/components/ChatHistory.d.ts +1 -1
- package/dist/ui/components/ChatHistory.js +2 -2
- package/dist/ui/components/ChatInput.d.ts +2 -1
- package/dist/ui/components/ChatInput.js +42 -88
- package/dist/ui/components/ChatInterface.d.ts +4 -2
- package/dist/ui/components/ChatInterface.js +27 -63
- package/dist/ui/components/CommandSuggestions.d.ts +0 -1
- package/dist/ui/components/CommandSuggestions.js +4 -6
- package/dist/ui/components/FileAutocomplete.js +1 -1
- package/dist/ui/components/KeyboardHelp.js +8 -6
- package/dist/ui/components/ModelSelection.js +6 -5
- package/dist/ui/components/StatusBar.d.ts +2 -1
- package/dist/ui/components/StatusBar.js +8 -7
- package/dist/ui/context/theme-context.js +3 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/utils/markdown-renderer.js +4 -13
- package/dist/utils/atomic-write.d.ts +22 -0
- package/dist/utils/atomic-write.js +29 -0
- package/dist/utils/bounded-output.d.ts +16 -0
- package/dist/utils/bounded-output.js +63 -0
- package/dist/utils/device-store-file.d.ts +4 -0
- package/dist/utils/device-store-file.js +40 -0
- package/dist/utils/export-manager.d.ts +9 -0
- package/dist/utils/export-manager.js +19 -0
- package/dist/utils/first-use-hints.d.ts +25 -0
- package/dist/utils/first-use-hints.js +58 -0
- package/dist/utils/history-manager.js +1 -1
- package/dist/utils/update-notifier.js +16 -18
- package/dist/wizard/environment-detection.d.ts +5 -1
- package/dist/wizard/environment-detection.js +49 -7
- package/dist/wizard/onboarding.d.ts +5 -1
- package/dist/wizard/onboarding.js +90 -30
- package/dist/wizard/provider-onboarding.d.ts +2 -0
- package/dist/wizard/provider-onboarding.js +55 -17
- package/package.json +7 -3
package/dist/server/types.d.ts
CHANGED
|
@@ -133,6 +133,10 @@ export interface JwtPayload {
|
|
|
133
133
|
type?: 'api_key' | 'user';
|
|
134
134
|
/** Optional operator role stamped by `buddy token --role`. */
|
|
135
135
|
role?: 'user' | 'admin';
|
|
136
|
+
/** Android device proof claims; absent on legacy tokens. */
|
|
137
|
+
amr?: string[];
|
|
138
|
+
profile?: 'agent' | 'companion';
|
|
139
|
+
identity?: 'owner';
|
|
136
140
|
}
|
|
137
141
|
export interface AuthenticatedRequest {
|
|
138
142
|
auth: {
|
|
@@ -26,6 +26,13 @@ interface ConnectionState {
|
|
|
26
26
|
keyId?: string;
|
|
27
27
|
/** Paired device id when authenticated via the device-pairing flow. */
|
|
28
28
|
deviceId?: string;
|
|
29
|
+
/** Signed Android identity, available to companion extensions. */
|
|
30
|
+
profile?: 'agent' | 'companion';
|
|
31
|
+
identity?: 'owner';
|
|
32
|
+
amr?: readonly string[];
|
|
33
|
+
deviceAuthExpiresAt?: number;
|
|
34
|
+
/** Server configuration secret, used only for the new device token path. */
|
|
35
|
+
deviceJwtSecret?: string;
|
|
29
36
|
scopes: string[];
|
|
30
37
|
/** No-auth network clients remain transport-visible but cannot run agent chat. */
|
|
31
38
|
anonymousRemote?: boolean;
|
|
@@ -77,6 +84,9 @@ export interface WebSocketExtensionPrincipal {
|
|
|
77
84
|
/** Server-derived principal id; request payloads cannot override it. */
|
|
78
85
|
readonly id: string;
|
|
79
86
|
readonly source: string;
|
|
87
|
+
readonly profile?: 'agent' | 'companion';
|
|
88
|
+
readonly identity?: 'owner';
|
|
89
|
+
readonly amr?: readonly string[];
|
|
80
90
|
readonly scopes: readonly string[];
|
|
81
91
|
readonly loopback: boolean;
|
|
82
92
|
readonly secure: boolean;
|
|
@@ -179,6 +189,7 @@ export declare function produceCompanionReply(message: string, options?: {
|
|
|
179
189
|
history?: CompanionHistoryTurn[];
|
|
180
190
|
/** Photos the phone attached — already validated by `validateChatAttachments`. */
|
|
181
191
|
attachments?: ValidatedChatAttachment[];
|
|
192
|
+
userId?: string;
|
|
182
193
|
}): Promise<string | {
|
|
183
194
|
text: string;
|
|
184
195
|
image?: {
|
|
@@ -186,6 +197,8 @@ export declare function produceCompanionReply(message: string, options?: {
|
|
|
186
197
|
data: string;
|
|
187
198
|
};
|
|
188
199
|
kind?: 'selfie' | 'text';
|
|
200
|
+
imagePath?: string;
|
|
201
|
+
historySuffix?: string;
|
|
189
202
|
}>;
|
|
190
203
|
/**
|
|
191
204
|
* Setup WebSocket server
|
|
@@ -7,6 +7,10 @@ import { validateApiKey } from '../auth/api-keys.js';
|
|
|
7
7
|
import { logger } from "../../utils/logger.js";
|
|
8
8
|
import { isOriginAllowed } from '../origin-check.js';
|
|
9
9
|
import { verifyToken } from '../auth/jwt.js';
|
|
10
|
+
import { getDeviceAuthStore } from '../auth/device-store.js';
|
|
11
|
+
import { withDeviceSessionIdentity } from '../auth/device-session-context.js';
|
|
12
|
+
import { getPermissionModeManager } from '../../security/permission-modes.js';
|
|
13
|
+
import { ConfirmationService } from '../../utils/confirmation-service.js';
|
|
10
14
|
import { isDirectLoopbackRequest } from '../middleware/auth.js';
|
|
11
15
|
import { authenticateDevice, getGatewayPairingStore, isDevicePairingRequired } from '../../gateway/device-pairing.js';
|
|
12
16
|
import { gatewayServerVersion, GATEWAY_PROTOCOL_VERSION } from '../../gateway/protocol.js';
|
|
@@ -84,6 +88,7 @@ function extensionPrincipal(state) {
|
|
|
84
88
|
return Object.freeze({
|
|
85
89
|
id,
|
|
86
90
|
source,
|
|
91
|
+
...sessionIdentityClaims(state),
|
|
87
92
|
scopes: Object.freeze(state.authenticated ? [...state.scopes] : []),
|
|
88
93
|
loopback: state.loopback === true,
|
|
89
94
|
secure: state.secure === true,
|
|
@@ -138,7 +143,16 @@ function cleanupWebSocketExtensions(state) {
|
|
|
138
143
|
}
|
|
139
144
|
listeners.clear();
|
|
140
145
|
}
|
|
141
|
-
function resetWebSocketExtensionsForIdentityChange(state) {
|
|
146
|
+
function resetWebSocketExtensionsForIdentityChange(state, deviceIdentity = false) {
|
|
147
|
+
if (deviceIdentity || state.profile !== undefined) {
|
|
148
|
+
state.agent?.dispose?.();
|
|
149
|
+
state.agent = undefined;
|
|
150
|
+
state.agentInitializing = undefined;
|
|
151
|
+
}
|
|
152
|
+
state.profile = undefined;
|
|
153
|
+
state.identity = undefined;
|
|
154
|
+
state.amr = undefined;
|
|
155
|
+
state.deviceAuthExpiresAt = undefined;
|
|
142
156
|
state.approvalCapable = false;
|
|
143
157
|
cleanupWebSocketExtensions(state);
|
|
144
158
|
state.extensionsCleaned = false;
|
|
@@ -146,6 +160,17 @@ function resetWebSocketExtensionsForIdentityChange(state) {
|
|
|
146
160
|
// companion conversation.
|
|
147
161
|
state.companionHistory = undefined;
|
|
148
162
|
}
|
|
163
|
+
/** Conditional spreads preserve the exact legacy principal and auth payload. */
|
|
164
|
+
function sessionIdentityClaims(state) {
|
|
165
|
+
return {
|
|
166
|
+
...(state.profile ? { profile: state.profile } : {}),
|
|
167
|
+
...(state.identity ? { identity: state.identity } : {}),
|
|
168
|
+
...(state.amr ? { amr: state.amr } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function deviceSessionIsActive(state) {
|
|
172
|
+
return state.deviceAuthExpiresAt === undefined || (state.deviceAuthExpiresAt > Date.now() && !!state.deviceId && getDeviceAuthStore().isActive(state.deviceId));
|
|
173
|
+
}
|
|
149
174
|
/**
|
|
150
175
|
* Add a message type without leaking the underlying socket or ConnectionState.
|
|
151
176
|
* The returned function removes exactly this registration and is idempotent.
|
|
@@ -432,24 +457,40 @@ messageHandlers.set('authenticate', async (ws, state, payload) => {
|
|
|
432
457
|
if (token) {
|
|
433
458
|
// JWT_SECRET is required - if not set, authentication will fail (secure by default)
|
|
434
459
|
const jwtSecret = process.env.JWT_SECRET;
|
|
435
|
-
|
|
460
|
+
const configuredToken = state.deviceJwtSecret ? verifyToken(token, state.deviceJwtSecret) : null;
|
|
461
|
+
const deviceToken = Array.isArray(configuredToken?.amr) && configuredToken.amr.includes('device') ? configuredToken : null;
|
|
462
|
+
if (!jwtSecret && !deviceToken) {
|
|
436
463
|
sendError(ws, 'CONFIG_ERROR', 'Server JWT configuration missing');
|
|
437
464
|
return;
|
|
438
465
|
}
|
|
439
|
-
const decoded = verifyToken(token, jwtSecret);
|
|
466
|
+
const decoded = deviceToken ?? verifyToken(token, jwtSecret);
|
|
440
467
|
if (decoded) {
|
|
441
|
-
|
|
468
|
+
const isDevice = Array.isArray(decoded.amr) && decoded.amr.includes('device');
|
|
469
|
+
if (isDevice && (!Number.isFinite(decoded.exp) || decoded.exp * 1000 <= Date.now() || !getDeviceAuthStore().isActive(decoded.sub))) {
|
|
470
|
+
sendError(ws, 'AUTH_FAILED', 'Invalid credentials');
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
resetWebSocketExtensionsForIdentityChange(state, decoded.profile !== undefined);
|
|
442
474
|
state.authenticated = true;
|
|
443
475
|
state.userId = decoded.userId ?? decoded.sub;
|
|
444
476
|
state.keyId = undefined;
|
|
445
|
-
state.deviceId = undefined;
|
|
477
|
+
state.deviceId = isDevice ? decoded.sub : undefined;
|
|
478
|
+
state.deviceAuthExpiresAt = isDevice ? decoded.exp * 1000 : undefined;
|
|
479
|
+
state.profile = decoded.profile === 'agent' || decoded.profile === 'companion' ? decoded.profile : undefined;
|
|
480
|
+
state.identity = decoded.identity === 'owner' ? 'owner' : undefined;
|
|
481
|
+
state.amr = Array.isArray(decoded.amr) ? Object.freeze(decoded.amr.filter((method) => typeof method === 'string')) : undefined;
|
|
446
482
|
state.scopes = decoded.scopes || ['chat'];
|
|
447
483
|
state.anonymousRemote = false;
|
|
448
484
|
if (approvalCapable === true)
|
|
449
485
|
state.approvalCapable = true;
|
|
486
|
+
if (state.profile === 'agent') {
|
|
487
|
+
// Native Android is an approval surface even when the PWA is disabled.
|
|
488
|
+
state.approvalCapable = true;
|
|
489
|
+
wireMobileConfirmationBridge({ broadcast, collectApprovalSurfaceIds, registerExtension: registerWebSocketExtension });
|
|
490
|
+
}
|
|
450
491
|
send(ws, {
|
|
451
492
|
type: 'authenticated',
|
|
452
|
-
payload: { userId: state.userId, scopes: state.scopes },
|
|
493
|
+
payload: { userId: state.userId, scopes: state.scopes, ...sessionIdentityClaims(state) },
|
|
453
494
|
timestamp: new Date().toISOString(),
|
|
454
495
|
});
|
|
455
496
|
return;
|
|
@@ -507,17 +548,22 @@ export async function produceCompanionReply(message, options = {}) {
|
|
|
507
548
|
const result = await runCompanionTurn(message, {
|
|
508
549
|
surface: 'mobile',
|
|
509
550
|
includeImageBytes: true,
|
|
551
|
+
...(options.userId ? { userId: options.userId } : {}),
|
|
510
552
|
...(options.history ? { history: options.history } : {}),
|
|
511
553
|
...(options.attachments?.length ? { attachments: options.attachments } : {}),
|
|
512
554
|
});
|
|
513
|
-
if (result.image || result.kind === 'selfie') {
|
|
555
|
+
if (result.image || result.kind === 'selfie' || result.imagePath) {
|
|
514
556
|
return {
|
|
515
557
|
text: result.text,
|
|
516
558
|
...(result.image ? { image: result.image } : {}),
|
|
517
559
|
kind: result.kind,
|
|
560
|
+
...(result.imagePath ? { imagePath: result.imagePath } : {}),
|
|
561
|
+
...(result.historySuffix ? { historySuffix: result.historySuffix } : {}),
|
|
518
562
|
};
|
|
519
563
|
}
|
|
520
|
-
return result.
|
|
564
|
+
return result.historySuffix
|
|
565
|
+
? { text: result.text, kind: 'text', historySuffix: result.historySuffix }
|
|
566
|
+
: result.text;
|
|
521
567
|
}
|
|
522
568
|
/** Persistence identity for this connection, or undefined (memory-only). */
|
|
523
569
|
function companionHistoryIdentity(state) {
|
|
@@ -535,7 +581,9 @@ function companionHistoryFor(state) {
|
|
|
535
581
|
}
|
|
536
582
|
/** Record one companion exchange; never stores image bytes. */
|
|
537
583
|
function rememberCompanionTurn(state, userText, produced) {
|
|
538
|
-
const
|
|
584
|
+
const baseAssistantText = typeof produced === 'string' ? produced : produced.text;
|
|
585
|
+
const historySuffix = typeof produced === 'string' ? '' : (produced.historySuffix ?? '');
|
|
586
|
+
const assistantText = `${baseAssistantText}${historySuffix}`.trim();
|
|
539
587
|
const kind = typeof produced === 'string' ? undefined : produced.kind;
|
|
540
588
|
state.companionHistory = appendCompanionHistory(companionHistoryFor(state), [
|
|
541
589
|
{ role: 'user', content: userText },
|
|
@@ -638,7 +686,8 @@ messageHandlers.set('chat', async (ws, state, payload) => {
|
|
|
638
686
|
}
|
|
639
687
|
const turn = { cancelled: false, abortDelivered: false };
|
|
640
688
|
state.activeTurn = turn;
|
|
641
|
-
const assistant =
|
|
689
|
+
const assistant = state.profile === 'agent' ? 'agent'
|
|
690
|
+
: typeof assistantRaw === 'string' ? assistantRaw.trim() : state.profile ?? 'agent';
|
|
642
691
|
const peerId = typeof peerIdRaw === 'string' ? peerIdRaw.trim() : '';
|
|
643
692
|
// Photos are accepted only for the companion; every other assistant keeps the
|
|
644
693
|
// exact payload contract it had.
|
|
@@ -689,6 +738,7 @@ messageHandlers.set('chat', async (ws, state, payload) => {
|
|
|
689
738
|
const history = companionHistoryFor(state);
|
|
690
739
|
const produced = await produceCompanionReply(userText, {
|
|
691
740
|
history,
|
|
741
|
+
userId: state.userId,
|
|
692
742
|
...(imageAttachments.length ? { attachments: imageAttachments } : {}),
|
|
693
743
|
});
|
|
694
744
|
spoken = typeof produced === 'string' ? produced : produced.text;
|
|
@@ -1069,13 +1119,22 @@ messageHandlers.set('peer:request', async (ws, state, payload) => {
|
|
|
1069
1119
|
sendError(ws, 'UNAUTHORIZED', 'Authentication required');
|
|
1070
1120
|
return;
|
|
1071
1121
|
}
|
|
1072
|
-
if (!state.scopes.includes('peer:invoke')) {
|
|
1073
|
-
sendError(ws, 'FORBIDDEN', 'peer:invoke scope required');
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
1122
|
// payload is the request frame { id, method, params, traceId?, depth? }
|
|
1077
1123
|
const frame = (payload ?? {});
|
|
1078
1124
|
const requestId = frame.id ?? '';
|
|
1125
|
+
if (!state.scopes.includes('peer:invoke')) {
|
|
1126
|
+
// Match the rejected RPC so callers fail immediately instead of timing out.
|
|
1127
|
+
send(ws, {
|
|
1128
|
+
type: 'peer:response',
|
|
1129
|
+
payload: {
|
|
1130
|
+
id: requestId || 'unknown',
|
|
1131
|
+
ok: false,
|
|
1132
|
+
error: { code: 'FORBIDDEN', message: 'peer:invoke scope required' },
|
|
1133
|
+
},
|
|
1134
|
+
timestamp: new Date().toISOString(),
|
|
1135
|
+
});
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1079
1138
|
if (!checkRateLimit(state, 'peerRequestCount', 'peerWindowStart', RATE_LIMITS.peerRequestsPerMinute, 60_000)) {
|
|
1080
1139
|
send(ws, {
|
|
1081
1140
|
type: 'peer:response',
|
|
@@ -1165,9 +1224,28 @@ async function processMessage(ws, state, data) {
|
|
|
1165
1224
|
...(id ? { id } : {}),
|
|
1166
1225
|
...(requestId ? { requestId } : {}),
|
|
1167
1226
|
};
|
|
1227
|
+
const invoke = async () => {
|
|
1228
|
+
if (type !== 'authenticate' && !deviceSessionIsActive(state)) {
|
|
1229
|
+
state.authenticated = false;
|
|
1230
|
+
state.approvalCapable = false;
|
|
1231
|
+
abortActiveTurn(state);
|
|
1232
|
+
sendError(ws, 'AUTH_FAILED', 'Invalid credentials', id);
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const run = () => handler(ws, state, payload ?? {}, envelope);
|
|
1236
|
+
const withIdentity = () => state.profile || state.identity || state.amr
|
|
1237
|
+
? withDeviceSessionIdentity({
|
|
1238
|
+
...sessionIdentityClaims(state), ...(state.deviceId ? { deviceId: state.deviceId } : {}),
|
|
1239
|
+
}, run)
|
|
1240
|
+
: run();
|
|
1241
|
+
if (state.profile === 'agent') {
|
|
1242
|
+
return getPermissionModeManager().withModeAsync('default', () => ConfirmationService.getInstance().withApprovalContextAsync(`ws:${state.id}`, withIdentity));
|
|
1243
|
+
}
|
|
1244
|
+
return withIdentity();
|
|
1245
|
+
};
|
|
1168
1246
|
if (laneBypassMessageTypes.has(type)) {
|
|
1169
1247
|
try {
|
|
1170
|
-
await
|
|
1248
|
+
await invoke();
|
|
1171
1249
|
}
|
|
1172
1250
|
catch (error) {
|
|
1173
1251
|
sendError(ws, 'HANDLER_ERROR', error instanceof Error ? error.message : String(error), id);
|
|
@@ -1185,14 +1263,14 @@ async function processMessage(ws, state, data) {
|
|
|
1185
1263
|
? payload
|
|
1186
1264
|
: {};
|
|
1187
1265
|
const peerRequestId = typeof frame.id === 'string' ? frame.id : 'unknown';
|
|
1188
|
-
await enqueuePeerHandler(state, () => enqueueMessage(`${sessionKey}:peer:${peerRequestId}`,
|
|
1266
|
+
await enqueuePeerHandler(state, () => enqueueMessage(`${sessionKey}:peer:${peerRequestId}`, invoke, {
|
|
1189
1267
|
parallel: true,
|
|
1190
1268
|
// Channel default is 120s; cold local Ollama peer.chat exceeded it (GK17).
|
|
1191
1269
|
timeout: PEER_REQUEST_LANE_TIMEOUT_MS,
|
|
1192
1270
|
}));
|
|
1193
1271
|
}
|
|
1194
1272
|
else {
|
|
1195
|
-
await enqueueMessage(sessionKey,
|
|
1273
|
+
await enqueueMessage(sessionKey, invoke);
|
|
1196
1274
|
}
|
|
1197
1275
|
}
|
|
1198
1276
|
catch (error) {
|
|
@@ -1267,6 +1345,7 @@ export async function setupWebSocket(server, config) {
|
|
|
1267
1345
|
const state = {
|
|
1268
1346
|
id: generateConnectionId(),
|
|
1269
1347
|
authenticated: !config.authEnabled, // Auto-auth if auth disabled
|
|
1348
|
+
deviceJwtSecret: config.jwtSecret,
|
|
1270
1349
|
scopes: config.authEnabled
|
|
1271
1350
|
? []
|
|
1272
1351
|
: [
|
|
@@ -1416,6 +1495,8 @@ function toBroadcastTarget(state) {
|
|
|
1416
1495
|
export function collectApprovalSurfaceIds() {
|
|
1417
1496
|
const ids = [];
|
|
1418
1497
|
for (const state of connections.values()) {
|
|
1498
|
+
if (!isMobilePwaEnabled() && state.profile !== 'agent')
|
|
1499
|
+
continue;
|
|
1419
1500
|
if (!state.authenticated)
|
|
1420
1501
|
continue;
|
|
1421
1502
|
if (state.anonymousRemote)
|
|
@@ -1424,6 +1505,8 @@ export function collectApprovalSurfaceIds() {
|
|
|
1424
1505
|
continue;
|
|
1425
1506
|
if (!state.scopes.includes('tools'))
|
|
1426
1507
|
continue;
|
|
1508
|
+
if (!deviceSessionIsActive(state))
|
|
1509
|
+
continue;
|
|
1427
1510
|
ids.push(state.id);
|
|
1428
1511
|
}
|
|
1429
1512
|
return ids;
|
|
@@ -1434,6 +1517,8 @@ export function broadcast(message, scopeFilter, targetFilter) {
|
|
|
1434
1517
|
for (const [ws, state] of connections.entries()) {
|
|
1435
1518
|
if (!state.authenticated)
|
|
1436
1519
|
continue;
|
|
1520
|
+
if (!deviceSessionIsActive(state))
|
|
1521
|
+
continue;
|
|
1437
1522
|
if (scopeFilter && !state.scopes.includes(scopeFilter))
|
|
1438
1523
|
continue;
|
|
1439
1524
|
if (targetFilter && !targetFilter(toBroadcastTarget(state)))
|
|
@@ -130,6 +130,8 @@ export declare class PromptBuilder {
|
|
|
130
130
|
* would otherwise tempt the LLM to hallucinate JSON tool calls.
|
|
131
131
|
*/
|
|
132
132
|
buildForQuery(message: string, systemPromptId: string | undefined, modelName: string, customInstructions: string | null): Promise<string>;
|
|
133
|
+
/** Keep prompt reads bound to the same project/bot manager as tool writes. */
|
|
134
|
+
setPersistentMemory(manager: PersistentMemoryManager): void;
|
|
133
135
|
/**
|
|
134
136
|
* Update configuration
|
|
135
137
|
*/
|
|
@@ -515,16 +515,20 @@ export class PromptBuilder {
|
|
|
515
515
|
const { getCodeExplorerManager } = await import('../plugins/code-explorer/CodeExplorerManager.js');
|
|
516
516
|
const fresh = getCodeExplorerManager(this.config.cwd).getFreshness();
|
|
517
517
|
if (fresh.stale) {
|
|
518
|
-
const
|
|
518
|
+
const state = fresh.unverified
|
|
519
|
+
? 'could not be verified against Git HEAD'
|
|
520
|
+
: 'does not match Git HEAD';
|
|
519
521
|
staleNote =
|
|
520
|
-
`\n⚠️ The Code Explorer index
|
|
521
|
-
`
|
|
522
|
-
|
|
522
|
+
`\n⚠️ The Code Explorer index ${state}. Its answers may miss current code; ` +
|
|
523
|
+
`treat relationship results as approximate and suggest re-running ` +
|
|
524
|
+
`\`code-explorer analyze --incremental\` if they look wrong.`;
|
|
523
525
|
}
|
|
524
526
|
}
|
|
525
527
|
catch { /* freshness best-effort */ }
|
|
526
528
|
const codeExplorerBlock = `<code_explorer_priority>\n` +
|
|
527
|
-
`Code Explorer is connected
|
|
529
|
+
`Code Explorer is connected through MCP on the host. Use these MCP tools directly; ` +
|
|
530
|
+
`do not run the host code-explorer binary through Bash, which may execute inside an isolated container. ` +
|
|
531
|
+
`For questions about code relationships — ` +
|
|
528
532
|
`callers/callees, blast radius / impact ("what breaks if I change X"), dead code, cycles, ` +
|
|
529
533
|
`coupling, complexity — PREFER its MCP tools (\`${p}impact\`, ` +
|
|
530
534
|
`\`${p}context\`, \`${p}query\`, \`${p}find_cycles\`, …) ` +
|
|
@@ -921,6 +925,10 @@ Output formatting discipline:
|
|
|
921
925
|
});
|
|
922
926
|
return this.buildSystemPrompt(systemPromptId, modelName, customInstructions, gates, message);
|
|
923
927
|
}
|
|
928
|
+
/** Keep prompt reads bound to the same project/bot manager as tool writes. */
|
|
929
|
+
setPersistentMemory(manager) {
|
|
930
|
+
this.persistentMemory = manager;
|
|
931
|
+
}
|
|
924
932
|
/**
|
|
925
933
|
* Update configuration
|
|
926
934
|
*/
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface RuntimeSettingsEvidence {
|
|
2
|
+
surface?: string;
|
|
3
|
+
model?: string;
|
|
4
|
+
provider?: string;
|
|
5
|
+
maxToolRounds?: number;
|
|
6
|
+
}
|
|
7
|
+
/** Explicit allowlist: never serialize configuration objects, env, endpoints or credentials. */
|
|
8
|
+
export declare function getRuntimeSettingsSnapshot(evidence?: RuntimeSettingsEvidence): {
|
|
9
|
+
guidance: string;
|
|
10
|
+
fleet?: {
|
|
11
|
+
supported: boolean;
|
|
12
|
+
configuredConnections: number;
|
|
13
|
+
statusTool: string;
|
|
14
|
+
statusArguments: {
|
|
15
|
+
includeCapabilities: boolean;
|
|
16
|
+
};
|
|
17
|
+
delegationTool: string;
|
|
18
|
+
routingTool: string;
|
|
19
|
+
connectionCommand: string;
|
|
20
|
+
scope: string;
|
|
21
|
+
} | undefined;
|
|
22
|
+
theme: {
|
|
23
|
+
active: string;
|
|
24
|
+
available: (string | undefined)[];
|
|
25
|
+
customOverrides: {
|
|
26
|
+
colors: string[];
|
|
27
|
+
avatars: string[];
|
|
28
|
+
};
|
|
29
|
+
lastPreferenceSave: boolean | null;
|
|
30
|
+
changeCommand: string;
|
|
31
|
+
statusCommand: string;
|
|
32
|
+
};
|
|
33
|
+
permissionMode?: import("../security/permission-modes.js").PermissionMode | undefined;
|
|
34
|
+
programmaticToolCalling: {
|
|
35
|
+
guidance?: string | undefined;
|
|
36
|
+
tool: string;
|
|
37
|
+
policy: import("../config/code-exec-policy.js").CodeExecPolicy;
|
|
38
|
+
source: "default" | "model" | "env";
|
|
39
|
+
};
|
|
40
|
+
maxToolRounds?: number | undefined;
|
|
41
|
+
surface: string;
|
|
42
|
+
model: string;
|
|
43
|
+
provider: string;
|
|
44
|
+
} | {
|
|
45
|
+
guidance: string;
|
|
46
|
+
fleet?: {
|
|
47
|
+
supported: boolean;
|
|
48
|
+
configuredConnections: number;
|
|
49
|
+
statusTool: string;
|
|
50
|
+
statusArguments: {
|
|
51
|
+
includeCapabilities: boolean;
|
|
52
|
+
};
|
|
53
|
+
delegationTool: string;
|
|
54
|
+
routingTool: string;
|
|
55
|
+
connectionCommand: string;
|
|
56
|
+
scope: string;
|
|
57
|
+
} | undefined;
|
|
58
|
+
theme: null;
|
|
59
|
+
permissionMode?: import("../security/permission-modes.js").PermissionMode | undefined;
|
|
60
|
+
programmaticToolCalling: {
|
|
61
|
+
guidance?: string | undefined;
|
|
62
|
+
tool: string;
|
|
63
|
+
policy: import("../config/code-exec-policy.js").CodeExecPolicy;
|
|
64
|
+
source: "default" | "model" | "env";
|
|
65
|
+
};
|
|
66
|
+
maxToolRounds?: number | undefined;
|
|
67
|
+
surface: string;
|
|
68
|
+
model: string;
|
|
69
|
+
provider: string;
|
|
70
|
+
};
|
|
71
|
+
export declare function formatRuntimeSettingsContext(evidence: RuntimeSettingsEvidence): string;
|
|
72
|
+
/** Ensure operational questions can reach the existing tools even with RAG selection. */
|
|
73
|
+
export declare function runtimeInspectionTools(query: string): string[];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { getThemeManager } from '../themes/theme-manager.js';
|
|
2
|
+
import { getFleetRegistry } from '../fleet/fleet-registry.js';
|
|
3
|
+
import { getPermissionModeManager } from '../security/permission-modes.js';
|
|
4
|
+
import { CODE_EXEC_OFF_NOTICE, CODE_EXEC_PREFER_HINT, resolveCodeExecPolicy } from '../config/code-exec-policy.js';
|
|
5
|
+
function identifier(value) {
|
|
6
|
+
return typeof value === 'string' && /^[a-zA-Z0-9_.:/-]{1,160}$/.test(value) ? value : undefined;
|
|
7
|
+
}
|
|
8
|
+
/** P5: effective code_exec policy and its short model-facing guidance. */
|
|
9
|
+
function programmaticToolCallingSnapshot(model) {
|
|
10
|
+
const resolved = resolveCodeExecPolicy(identifier(model));
|
|
11
|
+
return {
|
|
12
|
+
tool: 'code_exec',
|
|
13
|
+
policy: resolved.policy,
|
|
14
|
+
source: resolved.source,
|
|
15
|
+
...(resolved.policy === 'prefer' ? { guidance: CODE_EXEC_PREFER_HINT } : {}),
|
|
16
|
+
...(resolved.policy === 'off' ? { guidance: CODE_EXEC_OFF_NOTICE } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Explicit allowlist: never serialize configuration objects, env, endpoints or credentials. */
|
|
20
|
+
export function getRuntimeSettingsSnapshot(evidence = {}) {
|
|
21
|
+
const cli = evidence.surface === 'cli';
|
|
22
|
+
const manager = cli ? getThemeManager() : undefined;
|
|
23
|
+
return {
|
|
24
|
+
surface: identifier(evidence.surface) ?? 'unknown',
|
|
25
|
+
model: identifier(evidence.model) ?? 'unknown',
|
|
26
|
+
provider: identifier(evidence.provider) ?? 'unknown',
|
|
27
|
+
...(Number.isSafeInteger(evidence.maxToolRounds) && evidence.maxToolRounds > 0
|
|
28
|
+
? { maxToolRounds: evidence.maxToolRounds } : {}),
|
|
29
|
+
programmaticToolCalling: programmaticToolCallingSnapshot(evidence.model),
|
|
30
|
+
...(cli ? { permissionMode: getPermissionModeManager().getMode() } : {}),
|
|
31
|
+
...(manager ? {
|
|
32
|
+
theme: {
|
|
33
|
+
active: identifier(manager.getCurrentTheme().id) ?? 'custom',
|
|
34
|
+
available: manager.getAvailableThemes().map(theme => identifier(theme.id)).filter(Boolean),
|
|
35
|
+
customOverrides: manager.getOverrideKeys(),
|
|
36
|
+
lastPreferenceSave: manager.getPreferenceSaveStatus(),
|
|
37
|
+
changeCommand: '/theme <id>',
|
|
38
|
+
statusCommand: '/theme status',
|
|
39
|
+
},
|
|
40
|
+
} : { theme: null }),
|
|
41
|
+
...(cli ? { fleet: {
|
|
42
|
+
supported: true,
|
|
43
|
+
configuredConnections: getFleetRegistry().list().length,
|
|
44
|
+
statusTool: 'list_peers',
|
|
45
|
+
statusArguments: { includeCapabilities: true },
|
|
46
|
+
delegationTool: 'peer_delegate',
|
|
47
|
+
routingTool: 'route_peer',
|
|
48
|
+
connectionCommand: '/fleet listen <ws-url> --name <id>',
|
|
49
|
+
scope: 'Configured connections only; an empty registry does not prove no other Buddy is running on the network. Do not claim local-only capability. Probe list_peers before asserting availability.',
|
|
50
|
+
} } : {}),
|
|
51
|
+
guidance: 'Live settings at observation time. Unknown values are not inferred from saved preferences. A null theme means no CLI theme is attested for this surface. Theme changes require /theme <id>; editing a config file alone does not apply a live change.',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function formatRuntimeSettingsContext(evidence) {
|
|
55
|
+
return '<runtime_settings ephemeral="true">\n' +
|
|
56
|
+
JSON.stringify(getRuntimeSettingsSnapshot(evidence)) + '\n</runtime_settings>';
|
|
57
|
+
}
|
|
58
|
+
/** Ensure operational questions can reach the existing tools even with RAG selection. */
|
|
59
|
+
export function runtimeInspectionTools(query) {
|
|
60
|
+
const text = query.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
|
61
|
+
if (/\b(fleet|flotte|peers?|autres? (?:code[ -]?)?budd(?:y|ies)|other (?:code[ -]?)?budd(?:y|ies)|instances? (?:actives?|buddy)|buddy.*ensemble)\b/.test(text)) {
|
|
62
|
+
return ['list_peers', 'route_peer', 'peer_delegate'];
|
|
63
|
+
}
|
|
64
|
+
if (/\b(theme|parametres?|parametrage|settings|configuration actuelle|current configuration)\b/.test(text)) {
|
|
65
|
+
return ['self_describe'];
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=runtime-settings-context.js.map
|
package/dist/skills/executor.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { EventEmitter } from 'events';
|
|
7
7
|
import { SkillVariableResolver } from './skill-enhancements.js';
|
|
8
8
|
import { resolveBashInjections } from './bash-injection.js';
|
|
9
|
+
import { recordSkillActivity } from './skill-usage-store.js';
|
|
9
10
|
export const DEFAULT_EXECUTOR_CONFIG = {
|
|
10
11
|
timeout: 60000,
|
|
11
12
|
stepMode: false,
|
|
@@ -29,6 +30,7 @@ export class SkillExecutor extends EventEmitter {
|
|
|
29
30
|
const startTime = Date.now();
|
|
30
31
|
const toolCalls = [];
|
|
31
32
|
this.emit('skill:start', skill, context);
|
|
33
|
+
recordSkillActivity(skill.metadata.name, 'use', { source: 'executor' });
|
|
32
34
|
try {
|
|
33
35
|
// Check requirements
|
|
34
36
|
const reqCheck = this.checkRequirements(skill, context);
|
package/dist/skills/hub.js
CHANGED
|
@@ -23,19 +23,21 @@ export { generateSkillSigningKeyPair, signSkillContent, signRegistryIndexPayload
|
|
|
23
23
|
// ============================================================================
|
|
24
24
|
// Constants
|
|
25
25
|
// ============================================================================
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
function defaultHubConfig() {
|
|
27
|
+
return {
|
|
28
|
+
registryUrl: 'https://hub.codebuddy.dev/api/v1',
|
|
29
|
+
cacheDir: path.join(os.homedir(), '.codebuddy', 'hub', 'cache'),
|
|
30
|
+
skillsDir: path.join(os.homedir(), '.codebuddy', 'skills', 'managed'),
|
|
31
|
+
lockfilePath: path.join(os.homedir(), '.codebuddy', 'hub', 'lock.json'),
|
|
32
|
+
tapsPath: path.join(os.homedir(), '.codebuddy', 'hub', 'taps.json'),
|
|
33
|
+
trustedKeysPath: path.join(os.homedir(), '.codebuddy', 'hub', 'trusted-keys.json'),
|
|
34
|
+
requireSignedInstalls: false,
|
|
35
|
+
githubApiBaseUrl: 'https://api.github.com',
|
|
36
|
+
githubRawBaseUrl: 'https://raw.githubusercontent.com',
|
|
37
|
+
autoUpdate: false,
|
|
38
|
+
checkIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
39
|
+
};
|
|
40
|
+
}
|
|
39
41
|
const LOCKFILE_VERSION = 1;
|
|
40
42
|
const TAPS_FILE_VERSION = 1;
|
|
41
43
|
const TRUSTED_KEYS_FILE_VERSION = 1;
|
|
@@ -107,7 +109,7 @@ export class SkillsHub extends EventEmitter {
|
|
|
107
109
|
cacheTtlMs = 5 * 60 * 1000; // 5 minutes
|
|
108
110
|
constructor(config = {}) {
|
|
109
111
|
super();
|
|
110
|
-
this.config = { ...
|
|
112
|
+
this.config = { ...defaultHubConfig(), ...config };
|
|
111
113
|
this.lockfile = this.readLockfile();
|
|
112
114
|
this.ensureDirectories();
|
|
113
115
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InstalledSkillStatus } from './hub.js';
|
|
2
|
+
import type { SkillRequirements } from './types.js';
|
|
3
|
+
export interface LocalSkillEntry {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
source: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
requires?: SkillRequirements;
|
|
10
|
+
available: boolean;
|
|
11
|
+
reason?: 'disabled' | 'missing-file' | 'integrity-mismatch' | 'invalid-skill';
|
|
12
|
+
}
|
|
13
|
+
/** Read the same local skill tiers without activating skills or starting watchers. */
|
|
14
|
+
export declare function readLocalSkillInventory(installed: InstalledSkillStatus[]): Promise<{
|
|
15
|
+
entries: LocalSkillEntry[];
|
|
16
|
+
errors: Array<{
|
|
17
|
+
path: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function readLocalSkillContent(skill: LocalSkillEntry): Promise<string>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { getBundledSkillsPath } from './index.js';
|
|
3
|
+
import { SkillRegistry } from './registry.js';
|
|
4
|
+
import { SkillManager } from './skill-manager.js';
|
|
5
|
+
/** Read the same local skill tiers without activating skills or starting watchers. */
|
|
6
|
+
export async function readLocalSkillInventory(installed) {
|
|
7
|
+
const registry = new SkillRegistry({ bundledPath: getBundledSkillsPath(), watchEnabled: false });
|
|
8
|
+
const errors = [];
|
|
9
|
+
registry.on('skill:error', (path, error) => errors.push({ path, message: error.message }));
|
|
10
|
+
await registry.load();
|
|
11
|
+
const entries = new Map();
|
|
12
|
+
// Built-in legacy specializations still exposed by /skill. Do not load custom
|
|
13
|
+
// files through the legacy parser: SKILL.md files must pass registry validation.
|
|
14
|
+
const manager = new SkillManager();
|
|
15
|
+
for (const name of manager.getAvailableSkills()) {
|
|
16
|
+
const skill = manager.getSkill(name);
|
|
17
|
+
entries.set(name, { name, description: skill.description, source: 'builtin', available: true });
|
|
18
|
+
}
|
|
19
|
+
for (const skill of registry.list()) {
|
|
20
|
+
entries.set(skill.metadata.name, {
|
|
21
|
+
name: skill.metadata.name,
|
|
22
|
+
description: skill.metadata.description,
|
|
23
|
+
source: skill.tier,
|
|
24
|
+
path: skill.sourcePath,
|
|
25
|
+
version: skill.metadata.version,
|
|
26
|
+
requires: skill.metadata.requires,
|
|
27
|
+
available: skill.enabled !== false,
|
|
28
|
+
...(skill.enabled === false ? { reason: 'disabled' } : {}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
for (const skill of installed) {
|
|
32
|
+
let previous = entries.get(skill.name);
|
|
33
|
+
let reason = skill.enabled === false ? 'disabled'
|
|
34
|
+
: !skill.exists ? 'missing-file'
|
|
35
|
+
: !skill.integrityOk ? 'integrity-mismatch' : undefined;
|
|
36
|
+
if (!reason) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = await registry.registerSkillFile(skill.path, 'managed');
|
|
39
|
+
if (registry.get(parsed.metadata.name)?.sourcePath !== skill.path) {
|
|
40
|
+
reason = 'invalid-skill';
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
previous = { name: skill.name, description: parsed.metadata.description,
|
|
44
|
+
source: 'hub', available: true, requires: parsed.metadata.requires };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
reason = 'invalid-skill';
|
|
49
|
+
errors.push({ path: skill.path, message: error instanceof Error ? error.message : String(error) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Hub lifecycle state takes precedence; never present a disabled or altered
|
|
53
|
+
// installed skill as usable via a same-name bundled fallback.
|
|
54
|
+
entries.set(skill.name, {
|
|
55
|
+
name: skill.name,
|
|
56
|
+
description: previous?.description ?? '',
|
|
57
|
+
requires: previous?.requires,
|
|
58
|
+
source: 'hub',
|
|
59
|
+
path: skill.path,
|
|
60
|
+
version: skill.version,
|
|
61
|
+
available: !reason,
|
|
62
|
+
...(reason ? { reason } : {}),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return { entries: [...entries.values()], errors };
|
|
66
|
+
}
|
|
67
|
+
export async function readLocalSkillContent(skill) {
|
|
68
|
+
if (skill.path)
|
|
69
|
+
return readFile(skill.path, 'utf-8');
|
|
70
|
+
return new SkillManager().getSkill(skill.name)?.systemPrompt ?? '';
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=local-inventory.js.map
|