@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
|
@@ -10,90 +10,31 @@
|
|
|
10
10
|
* commands it uses repeatedly and codify them as reusable skills.
|
|
11
11
|
*
|
|
12
12
|
* Skill file location:
|
|
13
|
-
* .codebuddy/skills
|
|
13
|
+
* .codebuddy/skills/<authored-slug>/SKILL.md
|
|
14
14
|
*
|
|
15
15
|
* The tool generates valid SKILL.md frontmatter and validates that
|
|
16
16
|
* the slug does not already exist before writing.
|
|
17
17
|
*/
|
|
18
|
-
import * as fs from 'fs/promises';
|
|
19
18
|
import * as path from 'path';
|
|
20
|
-
import
|
|
21
|
-
import {
|
|
19
|
+
import * as yaml from 'yaml';
|
|
20
|
+
import { LiveSkillMutator, toAuthoredSkillName } from '../agent/self-improvement/skill-mutator.js';
|
|
22
21
|
export class CreateSkillTool {
|
|
23
|
-
|
|
24
|
-
return path.join(process.cwd(), '.codebuddy', 'skills', 'workspace');
|
|
25
|
-
}
|
|
26
|
-
async execute(input) {
|
|
27
|
-
const { name, description, body, tags = [], env = {}, requires = [], overwrite = false, } = input;
|
|
28
|
-
if (!name?.trim()) {
|
|
29
|
-
return { success: false, error: 'name is required' };
|
|
30
|
-
}
|
|
31
|
-
if (!description?.trim()) {
|
|
32
|
-
return { success: false, error: 'description is required' };
|
|
33
|
-
}
|
|
34
|
-
if (!body?.trim()) {
|
|
35
|
-
return { success: false, error: 'body is required' };
|
|
36
|
-
}
|
|
37
|
-
// Derive a filesystem-safe slug
|
|
38
|
-
const slug = name
|
|
39
|
-
.toLowerCase()
|
|
40
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
41
|
-
.replace(/^-+|-+$/g, '');
|
|
42
|
-
const skillDir = path.join(this.workspaceDir, slug);
|
|
43
|
-
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
44
|
-
// Check for existing skill
|
|
45
|
-
if (existsSync(skillFile) && !overwrite) {
|
|
46
|
-
return {
|
|
47
|
-
success: false,
|
|
48
|
-
error: `Skill "${slug}" already exists. Set overwrite: true to replace it.`,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
// Build YAML frontmatter
|
|
52
|
-
const fm = ['---', `name: ${name}`, `version: 1.0.0`, `description: ${description}`];
|
|
53
|
-
if (tags.length > 0) {
|
|
54
|
-
fm.push(`tags: [${tags.join(', ')}]`);
|
|
55
|
-
}
|
|
56
|
-
if (Object.keys(env).length > 0) {
|
|
57
|
-
fm.push('env:');
|
|
58
|
-
for (const [k, v] of Object.entries(env)) {
|
|
59
|
-
fm.push(` ${k}: ${v}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (requires.length > 0) {
|
|
63
|
-
fm.push(`requires: [${requires.join(', ')}]`);
|
|
64
|
-
}
|
|
65
|
-
fm.push('---');
|
|
66
|
-
const skillContent = `${fm.join('\n')}\n\n${body.trim()}\n`;
|
|
67
|
-
// Validate skill body for security (prevent malicious code in LLM-generated skills)
|
|
68
|
-
const validation = validateGeneratedCode(body);
|
|
69
|
-
if (!validation.safe) {
|
|
70
|
-
return {
|
|
71
|
-
success: false,
|
|
72
|
-
error: `Code validation failed:\n${formatValidationReport(validation)}\nSkill creation blocked for security reasons.`,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// Write to disk
|
|
22
|
+
async execute(input, cwd = process.cwd()) {
|
|
76
23
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
24
|
+
for (const field of ['name', 'description', 'body']) {
|
|
25
|
+
if (typeof input[field] !== 'string' || !input[field].trim())
|
|
26
|
+
return { success: false, error: `${field} is required` };
|
|
27
|
+
}
|
|
28
|
+
const name = toAuthoredSkillName(input.name);
|
|
29
|
+
const root = path.join(cwd, '.codebuddy', 'skills');
|
|
30
|
+
// Serialize metadata, including names containing ':' or YAML control characters.
|
|
31
|
+
const content = `---\n${yaml.stringify({ name, description: input.description, tags: input.tags ?? [], env: input.env ?? {}, requires: input.requires ?? [] })}---\n\n${input.body.trim()}\n`;
|
|
32
|
+
new LiveSkillMutator(root).create({ name, description: input.description, content }, { overwrite: input.overwrite === true });
|
|
33
|
+
return { success: true, output: `Skill created and loaded: ${name}\nPath: ${path.join(root, name, 'SKILL.md')}`, data: { name, loaded: true } };
|
|
79
34
|
}
|
|
80
|
-
catch (
|
|
81
|
-
return {
|
|
82
|
-
success: false,
|
|
83
|
-
error: `Failed to write skill: ${err instanceof Error ? err.message : String(err)}`,
|
|
84
|
-
};
|
|
35
|
+
catch (error) {
|
|
36
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
85
37
|
}
|
|
86
|
-
return {
|
|
87
|
-
success: true,
|
|
88
|
-
output: [
|
|
89
|
-
`✅ Skill created: ${name}`,
|
|
90
|
-
` Path: ${skillFile}`,
|
|
91
|
-
` Tags: ${tags.join(', ') || 'none'}`,
|
|
92
|
-
'',
|
|
93
|
-
'The skill is now available in the workspace registry.',
|
|
94
|
-
'Run `buddy hub search ${name}` or `/skills` to verify.',
|
|
95
|
-
].join('\n'),
|
|
96
|
-
};
|
|
97
38
|
}
|
|
98
39
|
getSchema() {
|
|
99
40
|
return {
|
|
@@ -50,6 +50,7 @@ function buildCreateOptions(input) {
|
|
|
50
50
|
skillRequest: normalizeString(input.skillRequest),
|
|
51
51
|
then: normalizeString(input.then),
|
|
52
52
|
preCheck: normalizeJsonOption(input.preCheck),
|
|
53
|
+
continuity: normalizeJsonOption(input.continuity),
|
|
53
54
|
deliver: normalizeDeliver(input.deliver),
|
|
54
55
|
format: normalizeString(input.format),
|
|
55
56
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { diagnoseLmResizer } from '../context/lm-resizer-diagnostics.js';
|
|
1
2
|
import type { ToolResult } from '../types/index.js';
|
|
2
3
|
export interface EnvDoctorData {
|
|
3
4
|
root: string;
|
|
@@ -6,6 +7,7 @@ export interface EnvDoctorData {
|
|
|
6
7
|
npmScripts: string[];
|
|
7
8
|
tools: Record<string, boolean>;
|
|
8
9
|
configFiles: string[];
|
|
10
|
+
lmResizer?: Awaited<ReturnType<typeof diagnoseLmResizer>>;
|
|
9
11
|
}
|
|
10
12
|
export declare class EnvDoctorTool {
|
|
11
13
|
readonly name = "env_doctor";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { diagnoseLmResizer } from '../context/lm-resizer-diagnostics.js';
|
|
1
2
|
import { execFile } from 'child_process';
|
|
2
3
|
import * as fs from 'fs/promises';
|
|
3
4
|
import * as path from 'path';
|
|
@@ -17,7 +18,7 @@ catch {
|
|
|
17
18
|
return false;
|
|
18
19
|
} }
|
|
19
20
|
async function hasTool(name) { try {
|
|
20
|
-
await execFileAsync('which', [name], { timeout: 3000 });
|
|
21
|
+
await execFileAsync(process.platform === 'win32' ? 'where.exe' : 'which', [name], { timeout: 3000 });
|
|
21
22
|
return true;
|
|
22
23
|
}
|
|
23
24
|
catch {
|
|
@@ -43,8 +44,8 @@ export class EnvDoctorTool {
|
|
|
43
44
|
for (const file of CONFIG_FILES)
|
|
44
45
|
if (await exists(path.join(root, file)))
|
|
45
46
|
configFiles.push(file);
|
|
46
|
-
const data = { root, nodeVersion: process.version, nodeModulesPresent: await exists(path.join(root, 'node_modules')), npmScripts, tools: { git: await hasTool('git'), docker: await hasTool('docker') }, configFiles };
|
|
47
|
-
return { success: true, output: `Node ${process.version}; ${npmScripts.length} npm scripts; node_modules ${data.nodeModulesPresent ? 'present' : 'missing'}
|
|
47
|
+
const data = { root, nodeVersion: process.version, nodeModulesPresent: await exists(path.join(root, 'node_modules')), npmScripts, tools: { git: await hasTool('git'), docker: await hasTool('docker') }, configFiles, lmResizer: await diagnoseLmResizer() };
|
|
48
|
+
return { success: true, output: `Node ${process.version}; ${npmScripts.length} npm scripts; node_modules ${data.nodeModulesPresent ? 'present' : 'missing'}\nLM Resizer (host): ${data.lmResizer?.binary}; tool-output=${data.lmResizer?.toolOutputSupported}; enabled=${data.lmResizer?.enabled}. ${data.lmResizer?.warning}\nHost binaries may not be available in the Bash sandbox; use configured MCP integrations or the host observation optimizer.`, data };
|
|
48
49
|
}
|
|
49
50
|
catch (error) {
|
|
50
51
|
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
@@ -39,7 +39,7 @@ export interface ExecuteCodeRpcInvokeResult {
|
|
|
39
39
|
error?: string;
|
|
40
40
|
truncated?: boolean;
|
|
41
41
|
}
|
|
42
|
-
export type ExecuteCodeRpcInvoker = (request: ExecuteCodeRpcInvokeRequest) => Promise<ExecuteCodeRpcInvokeResult>;
|
|
42
|
+
export type ExecuteCodeRpcInvoker = (request: ExecuteCodeRpcInvokeRequest, signal?: AbortSignal) => Promise<ExecuteCodeRpcInvokeResult>;
|
|
43
43
|
/** Truthy-flag parse — the single master gate. Read once, in the parent. */
|
|
44
44
|
export declare function isExecuteCodeToolRpcEnabled(raw?: string | undefined): boolean;
|
|
45
45
|
export declare function getExecuteCodeToolRpcAllowlist(raw?: string | undefined): Set<string>;
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* `peer-tool-bridge.ts` pattern.
|
|
31
31
|
*/
|
|
32
32
|
import { spawn } from 'child_process';
|
|
33
|
+
import { constants } from 'node:fs';
|
|
33
34
|
import { getRipgrepPath } from '../utils/ripgrep-path.js';
|
|
34
35
|
import * as fs from 'fs/promises';
|
|
35
36
|
import * as path from 'path';
|
|
@@ -93,9 +94,10 @@ export function createExecuteCodeRpcInvoker(options) {
|
|
|
93
94
|
const combinedAllowlist = new Set([...baseAllowlist, ...extraTools]);
|
|
94
95
|
const isFleetSafe = options.isFleetSafe ?? ((name) => getToolRegistry().isFleetSafe(name));
|
|
95
96
|
const workspaceRoot = path.resolve(options.workspaceRoot);
|
|
96
|
-
return async (request) => {
|
|
97
|
+
return async (request, signal) => {
|
|
97
98
|
const tool = request.tool;
|
|
98
99
|
try {
|
|
100
|
+
signal?.throwIfAborted();
|
|
99
101
|
if (!combinedAllowlist.has(tool)) {
|
|
100
102
|
return {
|
|
101
103
|
ok: false,
|
|
@@ -114,7 +116,7 @@ export function createExecuteCodeRpcInvoker(options) {
|
|
|
114
116
|
if (!executor) {
|
|
115
117
|
return { ok: false, error: `UNKNOWN_EXECUTE_CODE_RPC_TOOL: no executor for "${tool}"` };
|
|
116
118
|
}
|
|
117
|
-
const { output, truncated } = await executor(request.args, workspaceRoot);
|
|
119
|
+
const { output, truncated } = await executor(request.args, workspaceRoot, signal);
|
|
118
120
|
logger.debug('[execute-code-rpc] tool executed', { tool, truncated });
|
|
119
121
|
return { ok: true, output, truncated };
|
|
120
122
|
}
|
|
@@ -133,22 +135,34 @@ function assertInsideWorkspace(target, workspaceRoot) {
|
|
|
133
135
|
}
|
|
134
136
|
return absolute;
|
|
135
137
|
}
|
|
136
|
-
|
|
138
|
+
/** Resolve both sides physically; lexical containment alone follows escaping links. */
|
|
139
|
+
async function resolveWorkspacePath(target, workspaceRoot) {
|
|
140
|
+
const lexical = assertInsideWorkspace(target, workspaceRoot);
|
|
141
|
+
const root = await fs.realpath(workspaceRoot);
|
|
142
|
+
const resolved = await fs.realpath(lexical);
|
|
143
|
+
return assertInsideWorkspace(resolved, root);
|
|
144
|
+
}
|
|
145
|
+
const execViewFile = async (args, workspaceRoot, signal) => {
|
|
137
146
|
const filePath = args.file_path ?? args.path;
|
|
138
147
|
if (typeof filePath !== 'string' || filePath.length === 0) {
|
|
139
148
|
throw new Error('view_file: missing string file_path');
|
|
140
149
|
}
|
|
141
|
-
const resolved =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
throw new Error(`view_file: ${filePath} is not a regular file`);
|
|
145
|
-
}
|
|
146
|
-
const truncated = stat.size > READ_TRUNCATE_BYTES;
|
|
147
|
-
const limit = Math.min(stat.size, READ_TRUNCATE_BYTES);
|
|
148
|
-
if (limit <= 0)
|
|
149
|
-
return { output: '', truncated };
|
|
150
|
-
const handle = await fs.open(resolved, 'r');
|
|
150
|
+
const resolved = await resolveWorkspacePath(filePath, workspaceRoot);
|
|
151
|
+
signal?.throwIfAborted();
|
|
152
|
+
const handle = await fs.open(resolved, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
151
153
|
try {
|
|
154
|
+
// On Linux verify the opened descriptor too, before reading, against path swaps.
|
|
155
|
+
if (process.platform === 'linux') {
|
|
156
|
+
assertInsideWorkspace(await fs.realpath(`/proc/self/fd/${handle.fd}`), await fs.realpath(workspaceRoot));
|
|
157
|
+
}
|
|
158
|
+
const stat = await handle.stat();
|
|
159
|
+
if (!stat.isFile())
|
|
160
|
+
throw new Error(`view_file: ${filePath} is not a regular file`);
|
|
161
|
+
const truncated = stat.size > READ_TRUNCATE_BYTES;
|
|
162
|
+
const limit = Math.min(stat.size, READ_TRUNCATE_BYTES);
|
|
163
|
+
signal?.throwIfAborted();
|
|
164
|
+
if (limit <= 0)
|
|
165
|
+
return { output: '', truncated };
|
|
152
166
|
const buffer = Buffer.allocUnsafe(limit);
|
|
153
167
|
const { bytesRead } = await handle.read(buffer, 0, limit, 0);
|
|
154
168
|
return { output: buffer.subarray(0, bytesRead).toString('utf-8'), truncated };
|
|
@@ -157,12 +171,13 @@ const execViewFile = async (args, workspaceRoot) => {
|
|
|
157
171
|
await handle.close();
|
|
158
172
|
}
|
|
159
173
|
};
|
|
160
|
-
const execListDirectory = async (args, workspaceRoot) => {
|
|
174
|
+
const execListDirectory = async (args, workspaceRoot, signal) => {
|
|
161
175
|
const dirPath = args.path ?? args.directory ?? '.';
|
|
162
176
|
if (typeof dirPath !== 'string') {
|
|
163
177
|
throw new Error('list_directory: path must be a string');
|
|
164
178
|
}
|
|
165
|
-
const resolved =
|
|
179
|
+
const resolved = await resolveWorkspacePath(dirPath, workspaceRoot);
|
|
180
|
+
signal?.throwIfAborted();
|
|
166
181
|
const entries = await fs.readdir(resolved, { withFileTypes: true });
|
|
167
182
|
const lines = entries
|
|
168
183
|
.map((entry) => {
|
|
@@ -174,7 +189,7 @@ const execListDirectory = async (args, workspaceRoot) => {
|
|
|
174
189
|
const visible = truncated ? lines.slice(0, LIST_DIRECTORY_MAX_ENTRIES) : lines;
|
|
175
190
|
return { output: visible.join('\n'), truncated };
|
|
176
191
|
};
|
|
177
|
-
const execSearch = async (args, workspaceRoot) => {
|
|
192
|
+
const execSearch = async (args, workspaceRoot, signal) => {
|
|
178
193
|
const query = args.query ?? args.pattern;
|
|
179
194
|
const dirPath = args.path ?? '.';
|
|
180
195
|
if (typeof query !== 'string' || query.length === 0) {
|
|
@@ -183,10 +198,11 @@ const execSearch = async (args, workspaceRoot) => {
|
|
|
183
198
|
if (typeof dirPath !== 'string') {
|
|
184
199
|
throw new Error('search: path must be a string');
|
|
185
200
|
}
|
|
186
|
-
const resolved =
|
|
201
|
+
const resolved = await resolveWorkspacePath(dirPath, workspaceRoot);
|
|
202
|
+
signal?.throwIfAborted();
|
|
187
203
|
return await new Promise((resolve, reject) => {
|
|
188
|
-
const rgArgs = ['--no-heading', '--line-number', '--color', 'never', '--max-count', '50', '--', query, resolved];
|
|
189
|
-
const proc = spawn(getRipgrepPath(), rgArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
204
|
+
const rgArgs = ['--no-config', '--no-follow', '--no-heading', '--line-number', '--color', 'never', '--max-count', '50', '--', query, resolved];
|
|
205
|
+
const proc = spawn(getRipgrepPath(), rgArgs, { stdio: ['ignore', 'pipe', 'pipe'], signal });
|
|
190
206
|
let stdout = '';
|
|
191
207
|
let stderr = '';
|
|
192
208
|
let lineCount = 0;
|
|
@@ -38,6 +38,8 @@ export interface ExecuteCodeRunnerOptions {
|
|
|
38
38
|
* cannot exfiltrate secrets — even during pre-accept scoring.
|
|
39
39
|
*/
|
|
40
40
|
envMode?: 'inherit' | 'isolate';
|
|
41
|
+
/** Required OS confinement for authored computation; never falls back to unrestricted execution. */
|
|
42
|
+
confinement?: 'compute';
|
|
41
43
|
}
|
|
42
44
|
export interface ExecuteCodeResult {
|
|
43
45
|
kind: 'execute_code_result';
|
|
@@ -2,6 +2,7 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
|
+
import { confineComputeInvocation } from '../security/compute-confinement.js';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
6
7
|
import { isExecuteCodeToolRpcEnabled, } from './execute-code-rpc-invoker.js';
|
|
7
8
|
/**
|
|
@@ -58,10 +59,11 @@ export async function executeCode(input, options = {}) {
|
|
|
58
59
|
// the responder is ALWAYS active so every request gets a structured
|
|
59
60
|
// reply (denial when off / tool refused) — never a hang.
|
|
60
61
|
const rpcEnabled = (options.rpcEnabled ?? isExecuteCodeToolRpcEnabled()) && !!options.rpcInvoke;
|
|
61
|
-
const rpcSupported = language === 'javascript' || language === 'typescript' || language === 'python';
|
|
62
|
+
const rpcSupported = options.confinement !== 'compute' && (language === 'javascript' || language === 'typescript' || language === 'python');
|
|
62
63
|
const rpcDir = path.join(runDir, RPC_DIR_NAME);
|
|
63
64
|
const rpcMaxCalls = Math.max(1, options.rpcMaxCalls ?? RPC_DEFAULT_MAX_CALLS);
|
|
64
65
|
const rpcCallTimeoutMs = Math.max(1_000, options.rpcCallTimeoutMs ?? RPC_DEFAULT_CALL_TIMEOUT_MS);
|
|
66
|
+
await fs.mkdir(runDir, { recursive: true });
|
|
65
67
|
let scriptCode = code;
|
|
66
68
|
if (rpcSupported) {
|
|
67
69
|
await fs.mkdir(rpcDir, { recursive: true });
|
|
@@ -90,7 +92,11 @@ export async function executeCode(input, options = {}) {
|
|
|
90
92
|
const childEnv = options.envMode === 'isolate'
|
|
91
93
|
? buildIsolatedEnv(runDir, { ...env, ...runnerEnv })
|
|
92
94
|
: { ...process.env, ...env, ...runnerEnv };
|
|
93
|
-
const
|
|
95
|
+
const commandArgs = [...invocation.args, scriptPath, ...scriptArgs];
|
|
96
|
+
const launch = options.confinement === 'compute'
|
|
97
|
+
? confineComputeInvocation(invocation.command, commandArgs, runDir)
|
|
98
|
+
: { command: invocation.command, args: commandArgs };
|
|
99
|
+
const child = spawn(launch.command, launch.args, {
|
|
94
100
|
cwd: runDir,
|
|
95
101
|
env: childEnv,
|
|
96
102
|
windowsHide: true,
|
|
@@ -125,8 +131,7 @@ export async function executeCode(input, options = {}) {
|
|
|
125
131
|
clearTimeout(timer);
|
|
126
132
|
if (forceKillTimer)
|
|
127
133
|
clearTimeout(forceKillTimer);
|
|
128
|
-
rpcPoller?.stop();
|
|
129
|
-
resolve({ exitCode, signal });
|
|
134
|
+
void Promise.resolve(rpcPoller?.stop()).then(() => resolve({ exitCode, signal }));
|
|
130
135
|
});
|
|
131
136
|
});
|
|
132
137
|
const completedAt = (options.now ?? (() => new Date()))().toISOString();
|
|
@@ -292,11 +297,19 @@ function startRpcResponder(options) {
|
|
|
292
297
|
const seen = new Set();
|
|
293
298
|
let callCount = 0;
|
|
294
299
|
let stopped = false;
|
|
300
|
+
const controller = new AbortController();
|
|
295
301
|
let scanning = false;
|
|
302
|
+
let activeScan = Promise.resolve();
|
|
296
303
|
const writeResponse = async (id, payload) => {
|
|
304
|
+
if (stopped)
|
|
305
|
+
return;
|
|
297
306
|
const finalPath = path.join(options.rpcDir, `${id}.res.json`);
|
|
298
307
|
const tmpPath = path.join(options.rpcDir, `${id}.res.json.tmp`);
|
|
299
308
|
await fs.writeFile(tmpPath, JSON.stringify(payload), 'utf8');
|
|
309
|
+
if (stopped) {
|
|
310
|
+
await fs.rm(tmpPath, { force: true });
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
300
313
|
await fs.rename(tmpPath, finalPath);
|
|
301
314
|
};
|
|
302
315
|
const handleRequest = async (id, reqPath) => {
|
|
@@ -308,6 +321,8 @@ function startRpcResponder(options) {
|
|
|
308
321
|
await writeResponse(id, { ok: false, error: 'RPC_BAD_REQUEST: could not parse request JSON' });
|
|
309
322
|
return;
|
|
310
323
|
}
|
|
324
|
+
if (stopped)
|
|
325
|
+
return;
|
|
311
326
|
if (!options.enabled || !options.invoke) {
|
|
312
327
|
await writeResponse(id, {
|
|
313
328
|
ok: false,
|
|
@@ -331,14 +346,22 @@ function startRpcResponder(options) {
|
|
|
331
346
|
await writeResponse(id, { ok: false, error: 'RPC_BAD_REQUEST: missing string "tool"' });
|
|
332
347
|
return;
|
|
333
348
|
}
|
|
349
|
+
const callController = new AbortController();
|
|
350
|
+
const abort = () => callController.abort();
|
|
351
|
+
controller.signal.addEventListener('abort', abort, { once: true });
|
|
334
352
|
try {
|
|
335
|
-
|
|
353
|
+
if (stopped)
|
|
354
|
+
return;
|
|
355
|
+
const result = await withTimeout(options.invoke({ tool, args }, callController.signal), options.callTimeoutMs, `RPC_TOOL_TIMEOUT: tool "${tool}" exceeded ${options.callTimeoutMs}ms`, callController);
|
|
336
356
|
await writeResponse(id, result);
|
|
337
357
|
}
|
|
338
358
|
catch (error) {
|
|
339
359
|
const message = error instanceof Error ? error.message : String(error);
|
|
340
360
|
await writeResponse(id, { ok: false, error: message });
|
|
341
361
|
}
|
|
362
|
+
finally {
|
|
363
|
+
controller.signal.removeEventListener('abort', abort);
|
|
364
|
+
}
|
|
342
365
|
};
|
|
343
366
|
const scan = async () => {
|
|
344
367
|
if (scanning || stopped)
|
|
@@ -347,6 +370,8 @@ function startRpcResponder(options) {
|
|
|
347
370
|
try {
|
|
348
371
|
const entries = await fs.readdir(options.rpcDir).catch(() => []);
|
|
349
372
|
for (const entry of entries) {
|
|
373
|
+
if (stopped)
|
|
374
|
+
break;
|
|
350
375
|
if (!entry.endsWith('.req.json'))
|
|
351
376
|
continue;
|
|
352
377
|
const id = entry.slice(0, -'.req.json'.length);
|
|
@@ -366,26 +391,33 @@ function startRpcResponder(options) {
|
|
|
366
391
|
}
|
|
367
392
|
};
|
|
368
393
|
const interval = setInterval(() => {
|
|
369
|
-
|
|
394
|
+
if (!scanning && !stopped)
|
|
395
|
+
activeScan = scan();
|
|
370
396
|
}, RPC_POLL_INTERVAL_MS);
|
|
371
397
|
interval.unref?.();
|
|
372
398
|
return {
|
|
373
|
-
stop() {
|
|
399
|
+
async stop() {
|
|
374
400
|
stopped = true;
|
|
401
|
+
controller.abort();
|
|
375
402
|
clearInterval(interval);
|
|
403
|
+
await activeScan;
|
|
376
404
|
},
|
|
377
405
|
};
|
|
378
406
|
}
|
|
379
|
-
function withTimeout(promise, ms, message) {
|
|
407
|
+
function withTimeout(promise, ms, message, controller) {
|
|
380
408
|
return new Promise((resolve, reject) => {
|
|
381
|
-
const
|
|
409
|
+
const abort = () => { clearTimeout(timer); reject(new Error('RPC_CANCELLED')); };
|
|
410
|
+
const timer = setTimeout(() => {
|
|
411
|
+
reject(new Error(message));
|
|
412
|
+
controller.abort();
|
|
413
|
+
}, ms);
|
|
382
414
|
timer.unref?.();
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
415
|
+
controller.signal.addEventListener('abort', abort, { once: true });
|
|
416
|
+
if (controller.signal.aborted)
|
|
417
|
+
abort();
|
|
418
|
+
promise.then(resolve, reject).finally(() => {
|
|
387
419
|
clearTimeout(timer);
|
|
388
|
-
|
|
420
|
+
controller.signal.removeEventListener('abort', abort);
|
|
389
421
|
});
|
|
390
422
|
});
|
|
391
423
|
}
|
|
@@ -501,10 +533,12 @@ function buildRpcHelper(language) {
|
|
|
501
533
|
].join('\n');
|
|
502
534
|
}
|
|
503
535
|
// javascript / typescript
|
|
536
|
+
// Block-scoped dynamic imports avoid collisions with names in the user's module.
|
|
504
537
|
return [
|
|
505
|
-
|
|
506
|
-
"
|
|
507
|
-
"
|
|
538
|
+
'{',
|
|
539
|
+
"const { writeFileSync, renameSync, existsSync, readFileSync } = await import('node:fs');",
|
|
540
|
+
"const { join } = await import('node:path');",
|
|
541
|
+
"const { randomUUID } = await import('node:crypto');",
|
|
508
542
|
'globalThis.codebuddyToolCall = function codebuddyToolCall(tool, args) {',
|
|
509
543
|
' const dir = process.env.CODEBUDDY_EXECUTE_CODE_RPC_DIR;',
|
|
510
544
|
" if (!dir) return { ok: false, error: 'EXECUTE_CODE_TOOL_RPC_UNAVAILABLE' };",
|
|
@@ -524,6 +558,7 @@ function buildRpcHelper(language) {
|
|
|
524
558
|
' }',
|
|
525
559
|
" return { ok: false, error: 'RPC_RESPONSE_TIMEOUT' };",
|
|
526
560
|
'};',
|
|
561
|
+
'}',
|
|
527
562
|
'',
|
|
528
563
|
].join('\n');
|
|
529
564
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolResult } from '../types/index.js';
|
|
3
|
+
export declare const fleetRoomInputSchema: z.ZodDiscriminatedUnion<"action", [z.ZodObject<{
|
|
4
|
+
action: z.ZodLiteral<"send">;
|
|
5
|
+
content: z.ZodEffects<z.ZodString, string, string>;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
content: string;
|
|
8
|
+
action: "send";
|
|
9
|
+
}, {
|
|
10
|
+
content: string;
|
|
11
|
+
action: "send";
|
|
12
|
+
}>, z.ZodObject<{
|
|
13
|
+
action: z.ZodLiteral<"history">;
|
|
14
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
cursor: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
storeId: z.ZodString;
|
|
17
|
+
throughSeq: z.ZodNumber;
|
|
18
|
+
}, "strict", z.ZodTypeAny, {
|
|
19
|
+
storeId: string;
|
|
20
|
+
throughSeq: number;
|
|
21
|
+
}, {
|
|
22
|
+
storeId: string;
|
|
23
|
+
throughSeq: number;
|
|
24
|
+
}>>;
|
|
25
|
+
}, "strict", z.ZodTypeAny, {
|
|
26
|
+
action: "history";
|
|
27
|
+
limit?: number | undefined;
|
|
28
|
+
cursor?: {
|
|
29
|
+
storeId: string;
|
|
30
|
+
throughSeq: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
action: "history";
|
|
34
|
+
limit?: number | undefined;
|
|
35
|
+
cursor?: {
|
|
36
|
+
storeId: string;
|
|
37
|
+
throughSeq: number;
|
|
38
|
+
} | undefined;
|
|
39
|
+
}>, z.ZodObject<{
|
|
40
|
+
action: z.ZodLiteral<"status">;
|
|
41
|
+
}, "strict", z.ZodTypeAny, {
|
|
42
|
+
action: "status";
|
|
43
|
+
}, {
|
|
44
|
+
action: "status";
|
|
45
|
+
}>]>;
|
|
46
|
+
interface Cursor {
|
|
47
|
+
storeId: string;
|
|
48
|
+
throughSeq: number;
|
|
49
|
+
}
|
|
50
|
+
interface Message {
|
|
51
|
+
seq: number;
|
|
52
|
+
room: string;
|
|
53
|
+
author: string;
|
|
54
|
+
text: string;
|
|
55
|
+
event: {
|
|
56
|
+
id: string;
|
|
57
|
+
created_at: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface FleetRoomToolClient {
|
|
61
|
+
connect(): Promise<{
|
|
62
|
+
pubkey: string;
|
|
63
|
+
name: string;
|
|
64
|
+
rooms: unknown[];
|
|
65
|
+
}>;
|
|
66
|
+
publish(input: {
|
|
67
|
+
room: string;
|
|
68
|
+
content: string;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
id: string;
|
|
71
|
+
seq: number;
|
|
72
|
+
duplicate: boolean;
|
|
73
|
+
storeId: string;
|
|
74
|
+
}>;
|
|
75
|
+
fetch(filters: Array<{
|
|
76
|
+
'#h': string[];
|
|
77
|
+
limit: number;
|
|
78
|
+
}>, cursor?: Cursor, options?: {
|
|
79
|
+
maxMessages?: number;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
messages: Message[];
|
|
82
|
+
cursor: Cursor;
|
|
83
|
+
gap: boolean;
|
|
84
|
+
epochChanged: boolean;
|
|
85
|
+
truncated?: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
close(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
export interface FleetRoomToolConfig {
|
|
90
|
+
url: string;
|
|
91
|
+
room: string;
|
|
92
|
+
identityPath: string;
|
|
93
|
+
apiKey?: string;
|
|
94
|
+
jwt?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface FleetRoomToolDependencies {
|
|
97
|
+
env?: NodeJS.ProcessEnv;
|
|
98
|
+
createClient?: (config: FleetRoomToolConfig) => Promise<FleetRoomToolClient>;
|
|
99
|
+
/** Test seam; runtime budget is 15 seconds plus at most 1 second cleanup. */
|
|
100
|
+
timeoutMs?: number;
|
|
101
|
+
}
|
|
102
|
+
/** One bounded client lifecycle per invocation; never subscribes a background worker. */
|
|
103
|
+
export declare function executeFleetRoom(input: unknown, deps?: FleetRoomToolDependencies): Promise<ToolResult>;
|
|
104
|
+
export {};
|