@phuetz/code-buddy 2.0.0 → 2.2.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 +74 -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 +176 -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.d.ts +7 -0
- package/dist/agent/execution/tool-selection-strategy.js +32 -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 +2 -0
- package/dist/codebuddy/fleet-tool-defs.js +55 -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.d.ts +7 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +29 -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 +12 -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.d.ts +13 -0
- package/dist/commands/mcp.js +66 -21
- 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 +103 -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 +197 -0
- package/dist/doctor/integrations.js +241 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -0
- package/dist/errors/crash-handler.js +2 -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.d.ts +11 -0
- package/dist/mcp/client.js +68 -3
- 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/mcp-oauth-constants.d.ts +8 -0
- package/dist/mcp/mcp-oauth-constants.js +9 -0
- package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
- package/dist/mcp/mcp-oauth-provider.js +241 -0
- package/dist/mcp/mcp-oauth.d.ts +60 -1
- package/dist/mcp/mcp-oauth.js +241 -65
- package/dist/mcp/transports.d.ts +14 -5
- package/dist/mcp/transports.js +112 -101
- 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 +16 -6
- package/dist/services/runtime-settings-context.d.ts +81 -0
- package/dist/services/runtime-settings-context.js +79 -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 +30 -28
- package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
- package/dist/tools/peer-tool-invoke-tool.js +396 -0
- 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 +21 -3
- package/dist/tools/registry/fleet-tools.js +137 -4
- package/dist/tools/registry/index.d.ts +1 -1
- package/dist/tools/registry/index.js +2 -2
- 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/config-validation/schema.d.ts +2 -2
- 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/graceful-shutdown.d.ts +8 -0
- package/dist/utils/graceful-shutdown.js +23 -7
- 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
|
@@ -7,6 +7,7 @@ import { ConfirmationService } from "../utils/confirmation-service.js";
|
|
|
7
7
|
import { useEnhancedInput } from "./use-enhanced-input.js";
|
|
8
8
|
import { getErrorMessage } from "../types/index.js";
|
|
9
9
|
import { filterCommandSuggestions } from "../ui/components/CommandSuggestions.js";
|
|
10
|
+
import { isModelCompatibleWithProvider } from "../providers/model-provider-compat.js";
|
|
10
11
|
import { loadModelConfig } from "../utils/model-config.js";
|
|
11
12
|
// Import enhanced features
|
|
12
13
|
import { getSlashCommandManager } from "../commands/slash-commands.js";
|
|
@@ -18,11 +19,13 @@ import { extractFileReference, getFileSuggestions } from "../ui/components/FileA
|
|
|
18
19
|
import { getInteractionLogger } from "../logging/interaction-logger.js";
|
|
19
20
|
import { maybeContinueGoalAfterTurn } from "../goals/goal-loop.js";
|
|
20
21
|
import { logger } from '../utils/logger.js';
|
|
22
|
+
import { takeFirstUseHint } from "../utils/first-use-hints.js";
|
|
21
23
|
// Import history manager for persistent command history
|
|
22
24
|
import { getHistoryManager } from "../utils/history-manager.js";
|
|
23
25
|
export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProcessing, setIsStreaming, setTokenCount, setProcessingTime, setCurrentActivity, processingStartTime, isProcessing, isStreaming, isConfirmationActive = false, appendStreamingContent, finalizeStreamingEntry, updateToolCallEntry, }) {
|
|
24
26
|
const [showCommandSuggestions, setShowCommandSuggestions] = useState(false);
|
|
25
27
|
const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
|
|
28
|
+
const commandSelectionMoved = useRef(false);
|
|
26
29
|
const [showModelSelection, setShowModelSelection] = useState(false);
|
|
27
30
|
const [selectedModelIndex, setSelectedModelIndex] = useState(0);
|
|
28
31
|
const [showFileAutocomplete, setShowFileAutocomplete] = useState(false);
|
|
@@ -33,6 +36,22 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
33
36
|
const sessionFlags = confirmationService.getSessionFlags();
|
|
34
37
|
return sessionFlags.allOperations;
|
|
35
38
|
});
|
|
39
|
+
const runningTurn = useRef(false);
|
|
40
|
+
const pendingMessages = useRef([]);
|
|
41
|
+
const [queuedMessageCount, setQueuedMessageCount] = useState(0);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (isProcessing || isStreaming || runningTurn.current)
|
|
44
|
+
return;
|
|
45
|
+
const next = pendingMessages.current.shift();
|
|
46
|
+
if (next !== undefined) {
|
|
47
|
+
setQueuedMessageCount(pendingMessages.current.length);
|
|
48
|
+
// No turn is streaming here, so a tip entry cannot split a streaming message.
|
|
49
|
+
const tip = takeFirstUseHint('message_queued');
|
|
50
|
+
if (tip)
|
|
51
|
+
setChatHistory((prev) => [...prev, { type: "assistant", content: `💡 ${tip}`, timestamp: new Date() }]);
|
|
52
|
+
void processUserMessage(next);
|
|
53
|
+
}
|
|
54
|
+
}, [isProcessing, isStreaming, queuedMessageCount]);
|
|
36
55
|
// Track last escape time for double-escape detection
|
|
37
56
|
const lastEscapeTimeRef = useRef(0);
|
|
38
57
|
const DOUBLE_ESCAPE_THRESHOLD = 500; // ms
|
|
@@ -115,6 +134,15 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
115
134
|
return false; // Let default escape handling work
|
|
116
135
|
};
|
|
117
136
|
const handleCommandSuggestionsNav = (key) => {
|
|
137
|
+
const commandName = input.trim().split(/\s+/)[0]?.slice(1);
|
|
138
|
+
// Enter runs an explicitly typed command; Tab is completion only.
|
|
139
|
+
if (key.return && !commandSelectionMoved.current && commandName &&
|
|
140
|
+
(getSlashCommandManager().getCommand(commandName) || commandName === 'models')) {
|
|
141
|
+
setShowCommandSuggestions(false);
|
|
142
|
+
setSelectedCommandIndex(0);
|
|
143
|
+
void handleInputSubmit(input);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
118
146
|
const filteredSuggestions = filterCommandSuggestions(commandSuggestions, input);
|
|
119
147
|
if (filteredSuggestions.length === 0) {
|
|
120
148
|
setShowCommandSuggestions(false);
|
|
@@ -123,14 +151,16 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
123
151
|
}
|
|
124
152
|
else {
|
|
125
153
|
if (key.upArrow) {
|
|
154
|
+
commandSelectionMoved.current = true;
|
|
126
155
|
setSelectedCommandIndex((prev) => prev === 0 ? filteredSuggestions.length - 1 : prev - 1);
|
|
127
156
|
return true;
|
|
128
157
|
}
|
|
129
158
|
if (key.downArrow) {
|
|
159
|
+
commandSelectionMoved.current = true;
|
|
130
160
|
setSelectedCommandIndex((prev) => (prev + 1) % filteredSuggestions.length);
|
|
131
161
|
return true;
|
|
132
162
|
}
|
|
133
|
-
if (key.tab || key.return) {
|
|
163
|
+
if (key.tab || key.return || (key.rightArrow && !key.ctrl && !key.meta && !key.shift && cursorPosition === input.length)) {
|
|
134
164
|
const safeIndex = Math.min(selectedCommandIndex, filteredSuggestions.length - 1);
|
|
135
165
|
const selectedSuggestion = filteredSuggestions[safeIndex];
|
|
136
166
|
let newInput;
|
|
@@ -151,7 +181,14 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
151
181
|
}
|
|
152
182
|
else {
|
|
153
183
|
// For commands, just use the command
|
|
154
|
-
newInput = selectedSuggestion.command
|
|
184
|
+
newInput = selectedSuggestion.command;
|
|
185
|
+
if (key.return) {
|
|
186
|
+
setShowCommandSuggestions(false);
|
|
187
|
+
setSelectedCommandIndex(0);
|
|
188
|
+
void handleInputSubmit(newInput);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
newInput += ' ';
|
|
155
192
|
}
|
|
156
193
|
setInput(newInput);
|
|
157
194
|
setCursorPosition(newInput.length);
|
|
@@ -163,6 +200,13 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
163
200
|
return false;
|
|
164
201
|
};
|
|
165
202
|
const handleModelSelectionNav = (key) => {
|
|
203
|
+
if (key.leftArrow || key.escape) {
|
|
204
|
+
setShowModelSelection(false);
|
|
205
|
+
setSelectedModelIndex(0);
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
if (availableModels.length === 0)
|
|
209
|
+
return true;
|
|
166
210
|
if (key.upArrow) {
|
|
167
211
|
setSelectedModelIndex((prev) => prev === 0 ? availableModels.length - 1 : prev - 1);
|
|
168
212
|
return true;
|
|
@@ -171,18 +215,8 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
171
215
|
setSelectedModelIndex((prev) => (prev + 1) % availableModels.length);
|
|
172
216
|
return true;
|
|
173
217
|
}
|
|
174
|
-
if (key.tab || key.return) {
|
|
218
|
+
if (key.tab || key.return || key.rightArrow) {
|
|
175
219
|
const selectedModel = availableModels[selectedModelIndex];
|
|
176
|
-
// Delegate to Dispatcher implicitly via handleDirectCommand?
|
|
177
|
-
// No, UI navigation logic remains here, but the action can be manual.
|
|
178
|
-
// Or we can construct a command string and let dispatcher handle it.
|
|
179
|
-
// But we have state setters here.
|
|
180
|
-
// Let's keep UI state manipulation here for selection, but action execution via command if possible.
|
|
181
|
-
// Actually, standard behavior:
|
|
182
|
-
// agent.setModel(selectedModel.model);
|
|
183
|
-
// updateCurrentModel(selectedModel.model);
|
|
184
|
-
// ...
|
|
185
|
-
// We can use a helper, but for now let's leave this UI logic as is, or use handleDirectCommand("/models " + model)
|
|
186
220
|
if (selectedModel) {
|
|
187
221
|
handleDirectCommand(`/models ${selectedModel.model}`);
|
|
188
222
|
}
|
|
@@ -190,7 +224,7 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
190
224
|
setSelectedModelIndex(0);
|
|
191
225
|
return true;
|
|
192
226
|
}
|
|
193
|
-
return
|
|
227
|
+
return true; // Keep typing from modifying the draft while choosing a model.
|
|
194
228
|
};
|
|
195
229
|
const handleFileAutocompleteNav = (key) => {
|
|
196
230
|
if (fileSuggestions.length > 0) {
|
|
@@ -202,7 +236,7 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
202
236
|
setSelectedFileIndex((prev) => (prev + 1) % fileSuggestions.length);
|
|
203
237
|
return true;
|
|
204
238
|
}
|
|
205
|
-
if (key.tab || key.return) {
|
|
239
|
+
if (key.tab || key.return || (key.rightArrow && !key.ctrl && !key.meta && !key.shift && cursorPosition === input.length)) {
|
|
206
240
|
const selectedFile = fileSuggestions[selectedFileIndex];
|
|
207
241
|
const { startPos } = extractFileReference(input);
|
|
208
242
|
if (selectedFile && startPos >= 0) {
|
|
@@ -336,12 +370,19 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
336
370
|
await handleDirectCommand(userInput);
|
|
337
371
|
}
|
|
338
372
|
else {
|
|
339
|
-
|
|
373
|
+
if (runningTurn.current || pendingMessages.current.length > 0) {
|
|
374
|
+
pendingMessages.current.push(userInput);
|
|
375
|
+
setQueuedMessageCount(pendingMessages.current.length);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
await processUserMessage(userInput);
|
|
379
|
+
}
|
|
340
380
|
}
|
|
341
381
|
}
|
|
342
382
|
};
|
|
343
383
|
// Removed handleShellBypass as it's now in ClientCommandDispatcher
|
|
344
384
|
const handleInputChange = (newInput) => {
|
|
385
|
+
commandSelectionMoved.current = false;
|
|
345
386
|
// Update command suggestions based on input
|
|
346
387
|
if (newInput.startsWith("/")) {
|
|
347
388
|
setShowCommandSuggestions(true);
|
|
@@ -369,11 +410,17 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
369
410
|
onSubmit: handleInputSubmit,
|
|
370
411
|
onSpecialKey: handleSpecialKey,
|
|
371
412
|
disabled: isConfirmationActive,
|
|
413
|
+
multiline: true,
|
|
372
414
|
});
|
|
373
415
|
// Hook up the actual input handling
|
|
374
416
|
useInput((inputChar, key) => {
|
|
417
|
+
// A picker owns the keyboard, including text-editing shortcuts such as Ctrl+J.
|
|
418
|
+
if (showModelSelection) {
|
|
419
|
+
handleModelSelectionNav(key);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
375
422
|
handleInput(inputChar, key);
|
|
376
|
-
});
|
|
423
|
+
}, { isActive: !isConfirmationActive });
|
|
377
424
|
// Update command suggestions when input changes
|
|
378
425
|
useEffect(() => {
|
|
379
426
|
handleInputChange(input);
|
|
@@ -387,9 +434,13 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
387
434
|
}));
|
|
388
435
|
}, []);
|
|
389
436
|
// Load models from configuration with fallback to defaults
|
|
437
|
+
const current = agent.getCurrentModel?.();
|
|
438
|
+
const provider = agent.getClient?.().getCurrentProvider?.();
|
|
390
439
|
const availableModels = useMemo(() => {
|
|
391
|
-
|
|
392
|
-
|
|
440
|
+
const models = [...(current ? [{ model: current }] : []), ...loadModelConfig()];
|
|
441
|
+
return models.filter((option, index) => models.findIndex(other => other.model === option.model) === index &&
|
|
442
|
+
isModelCompatibleWithProvider(option.model, provider));
|
|
443
|
+
}, [current, provider]);
|
|
393
444
|
const handleDirectCommand = async (input) => {
|
|
394
445
|
const context = {
|
|
395
446
|
agent,
|
|
@@ -411,6 +462,7 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
411
462
|
return await ClientCommandDispatcher.dispatch(input, context);
|
|
412
463
|
};
|
|
413
464
|
const processUserMessage = async (userInput) => {
|
|
465
|
+
runningTurn.current = true;
|
|
414
466
|
const mySeq = ++turnSeqRef.current;
|
|
415
467
|
goalInterruptedRef.current = false;
|
|
416
468
|
const userEntry = {
|
|
@@ -431,7 +483,8 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
431
483
|
}
|
|
432
484
|
setIsProcessing(true);
|
|
433
485
|
setCurrentActivity?.('Sending to LLM...');
|
|
434
|
-
|
|
486
|
+
// The submit handler already cleared the sent text. A queued turn must
|
|
487
|
+
// preserve whatever draft the user is now editing.
|
|
435
488
|
let fullResponseContent = "";
|
|
436
489
|
try {
|
|
437
490
|
setIsStreaming(true);
|
|
@@ -622,6 +675,10 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
622
675
|
setIsStreaming(false);
|
|
623
676
|
setCurrentActivity?.('');
|
|
624
677
|
}
|
|
678
|
+
// Persist the finished turn before a queued message or exit can start (never throws).
|
|
679
|
+
await agent.persistInteractiveSession();
|
|
680
|
+
runningTurn.current = false;
|
|
681
|
+
setIsStreaming(false);
|
|
625
682
|
setIsProcessing(false);
|
|
626
683
|
setCurrentActivity?.('');
|
|
627
684
|
processingStartTime.current = 0;
|
|
@@ -648,10 +705,12 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
648
705
|
}
|
|
649
706
|
// Skip the continuation if a newer turn started while we were judging —
|
|
650
707
|
// the user's message preempts the loop and gets judged after its turn.
|
|
651
|
-
if (outcome?.continuationPrompt && turnSeqRef.current === mySeq) {
|
|
708
|
+
if (outcome?.continuationPrompt && turnSeqRef.current === mySeq && pendingMessages.current.length === 0) {
|
|
652
709
|
const continuation = outcome.continuationPrompt;
|
|
653
710
|
setTimeout(() => {
|
|
654
|
-
|
|
711
|
+
if (turnSeqRef.current === mySeq && !runningTurn.current && pendingMessages.current.length === 0) {
|
|
712
|
+
void processUserMessage(continuation);
|
|
713
|
+
}
|
|
655
714
|
}, 50);
|
|
656
715
|
}
|
|
657
716
|
}
|
|
@@ -673,6 +732,7 @@ export function useInputHandler({ agent, chatHistory, setChatHistory, setIsProce
|
|
|
673
732
|
availableModels,
|
|
674
733
|
agent,
|
|
675
734
|
autoEditEnabled,
|
|
735
|
+
queuedMessageCount,
|
|
676
736
|
handleInputSubmit,
|
|
677
737
|
};
|
|
678
738
|
}
|
|
@@ -165,7 +165,8 @@ const DESCRIBE_PATTERNS = [
|
|
|
165
165
|
/\bde quoi (?:es tu|etes vous) (?:fait|faite|faits|faites|compose|composee|composes|composees)\b/,
|
|
166
166
|
/\bqui (?:es tu|etes vous)\b/,
|
|
167
167
|
/\b(?:quell?e est )?(?:ton|votre) architecture\b/,
|
|
168
|
-
/\
|
|
168
|
+
/\bquels? (?:sont )?(?:tes|vos) (?:capteurs?|outils?|modules?)(?: (?:sont|restent|semblent))?(?: (?:actifs?|disponibles?|operationnels?))?\b/,
|
|
169
|
+
/^(?:tes|vos) (?:capteurs?|outils?|modules?)(?: (?:sont|restent|semblent))?(?: (?:actifs?|disponibles?|operationnels?))?\s*[?.!]*$/,
|
|
169
170
|
/\bquels? (?:modules?|composants?|outils?) (?:as tu|avez vous)\b/,
|
|
170
171
|
/\bquell?es? (?:sont )?(?:tes|vos) limit(?:e|es|ation|ations)\b/,
|
|
171
172
|
/\b(?:lisa|l assistant|l assistante|l agent)\b.{0,96}\b(?:conscient|consciente|conscience d elle meme|modele d elle meme|se connaitre)\b/,
|
|
@@ -354,7 +355,9 @@ export function renderLisaOperationalSelfResponse(model, raw, intent) {
|
|
|
354
355
|
}
|
|
355
356
|
/** Classify a request about Lisa's own technical implementation. */
|
|
356
357
|
export function classifyLisaIntrospection(raw) {
|
|
357
|
-
|
|
358
|
+
// Using tools to do a task does not make the tools the inspection target.
|
|
359
|
+
// Keep any explicit target that follows (e.g. 'inspect your own code').
|
|
360
|
+
const text = normalizeIntrospectionText(raw).replace(/\b(?:(?:utilise|utilisez|utiliser) (?:tes|vos)|use your) (?:outils|tools)\b/g, '');
|
|
358
361
|
if (!text)
|
|
359
362
|
return null;
|
|
360
363
|
// A request for introspection of the user's life, emotions, relationship, or
|
|
@@ -44,6 +44,8 @@ export interface OperationalSelfArea {
|
|
|
44
44
|
state: 'verified' | 'partial' | 'unavailable';
|
|
45
45
|
}
|
|
46
46
|
export interface CompanionRuntimeEvidence {
|
|
47
|
+
/** Live CLI theme, only when attested by that UI host. */
|
|
48
|
+
theme?: string;
|
|
47
49
|
model?: string;
|
|
48
50
|
provider?: string;
|
|
49
51
|
/** False when the report is built locally without dispatching a model. */
|
|
@@ -809,6 +809,10 @@ function runtimeFacts(runtime, observedAt) {
|
|
|
809
809
|
: undefined));
|
|
810
810
|
facts.push(fact(observedAt, 'turn.surface', 'Surface', surface ? 'verified' : 'unknown', surface || 'inconnue', ['agent turn metadata']));
|
|
811
811
|
facts.push(fact(observedAt, 'turn.permission', 'Mode de permission', permissionMode ? 'verified' : 'unknown', permissionMode || 'inconnu', ['agent turn metadata']));
|
|
812
|
+
const theme = surface === 'cli' ? normalizedRuntimeIdentifier(runtime.theme) : undefined;
|
|
813
|
+
if (theme) {
|
|
814
|
+
facts.push(fact(observedAt, 'turn.theme', 'Thème CLI actif', 'verified', theme, ['in-process Ink ThemeManager']));
|
|
815
|
+
}
|
|
812
816
|
const registered = normalizedToolNames(runtime.registeredToolNames);
|
|
813
817
|
const exposed = normalizedToolNames(runtime.exposedToolNames);
|
|
814
818
|
facts.push(fact(observedAt, 'tools.registered', 'Outils enregistrés', runtime.registeredToolNames !== undefined ? 'verified' : 'unknown', runtime.registeredToolNames !== undefined
|
package/dist/index.js
CHANGED
|
@@ -1328,7 +1328,7 @@ program
|
|
|
1328
1328
|
.option("--no-emoji", "disable emoji in output")
|
|
1329
1329
|
.option("--list-models", "list available models from the API endpoint and exit")
|
|
1330
1330
|
.option("--continue", "continue from the most recent saved session (like mistral-vibe)")
|
|
1331
|
-
.option("--resume
|
|
1331
|
+
.option("--resume [sessionId]", "resume a specific session by ID (supports partial matching); without an ID, pick a recent session")
|
|
1332
1332
|
.option("--search-sessions <query>", "search saved sessions by content")
|
|
1333
1333
|
.option("--max-price <dollars>", "maximum cost in dollars before stopping (like mistral-vibe)", "10.0")
|
|
1334
1334
|
.option("--auto-approve", "automatically approve all tool executions (like mistral-vibe)")
|
|
@@ -1519,6 +1519,14 @@ program
|
|
|
1519
1519
|
cli.info(` ${lastSession.messages.length} messages, last accessed: ${lastSession.lastAccessedAt.toLocaleString()}\n`);
|
|
1520
1520
|
}
|
|
1521
1521
|
// Handle --resume flag (resume specific session by ID, like mistral-vibe)
|
|
1522
|
+
if (options.resume === true) {
|
|
1523
|
+
// P6: `--resume` without an ID opens the recent-session picker.
|
|
1524
|
+
const { pickRecentSession } = await import("./cli/session-commands.js");
|
|
1525
|
+
const picked = await pickRecentSession(20);
|
|
1526
|
+
if (!picked)
|
|
1527
|
+
process.exit(process.exitCode ?? 1);
|
|
1528
|
+
options.resume = picked;
|
|
1529
|
+
}
|
|
1522
1530
|
if (options.resume) {
|
|
1523
1531
|
const { getSessionStore } = await import("./persistence/session-store.js");
|
|
1524
1532
|
const sessionStore = getSessionStore();
|
|
@@ -1534,7 +1542,9 @@ program
|
|
|
1534
1542
|
}
|
|
1535
1543
|
await sessionStore.resumeSession(session.id);
|
|
1536
1544
|
cli.info(`📂 Resuming session: ${session.name} (${session.id.slice(0, 8)})`);
|
|
1537
|
-
cli.info(` ${session.messages.length} messages, last accessed: ${session.lastAccessedAt.toLocaleString()}
|
|
1545
|
+
cli.info(` ${session.messages.length} messages, last accessed: ${session.lastAccessedAt.toLocaleString()}`);
|
|
1546
|
+
const { buildSessionRecap, formatSessionRecap } = await import("./cli/session-picker.js");
|
|
1547
|
+
cli.info(`${formatSessionRecap(buildSessionRecap(session)).join("\n")}\n`);
|
|
1538
1548
|
}
|
|
1539
1549
|
// Load environment before changing cwd so root .env values (API keys) remain available
|
|
1540
1550
|
// even when --directory points to a workspace without its own .env file.
|
|
@@ -1900,6 +1910,7 @@ program
|
|
|
1900
1910
|
await ensureUserSettingsDirectory();
|
|
1901
1911
|
recordStartupPhase('user-settings-done');
|
|
1902
1912
|
// ── Crash recovery: detect unclean shutdown and offer session resume ──
|
|
1913
|
+
let crashResumed = false;
|
|
1903
1914
|
if (!options.resume && !options.continue) {
|
|
1904
1915
|
try {
|
|
1905
1916
|
const { checkCrashRecovery, clearRecoveryFiles } = await import('./errors/crash-recovery.js');
|
|
@@ -1921,6 +1932,7 @@ program
|
|
|
1921
1932
|
const session = await sessionStore.getSessionByPartialId(recovery.sessionId);
|
|
1922
1933
|
if (session) {
|
|
1923
1934
|
await sessionStore.resumeSession(session.id);
|
|
1935
|
+
crashResumed = true;
|
|
1924
1936
|
cli.info(` Resuming session: ${session.name} (${session.messages.length} messages)`);
|
|
1925
1937
|
}
|
|
1926
1938
|
else {
|
|
@@ -1978,7 +1990,24 @@ program
|
|
|
1978
1990
|
// Only the interactive TUI enables it, so cron/headless/sub-agent runs cannot
|
|
1979
1991
|
// trigger a review (recursion + cost safety).
|
|
1980
1992
|
agent.enableBackgroundReview();
|
|
1981
|
-
|
|
1993
|
+
// Memory fact reconciliation (remember tool and /remember) must use the
|
|
1994
|
+
// provider this session runs on, never a provider auto-detected from env.
|
|
1995
|
+
const { setFactsMemorySessionClient } = await import('./memory/facts-memory.js');
|
|
1996
|
+
setFactsMemorySessionClient(agent.getClient());
|
|
1997
|
+
// A resumed session (--resume, --continue, crash recovery) must reach both the model
|
|
1998
|
+
// history and the chat view; resumeSession() alone only selects the store's current session.
|
|
1999
|
+
let initialHistory;
|
|
2000
|
+
if (options.resume || options.continue || crashResumed) {
|
|
2001
|
+
const { getSessionStore } = await import('./persistence/session-store.js');
|
|
2002
|
+
const resumedStore = getSessionStore();
|
|
2003
|
+
const resumedId = resumedStore.getCurrentSessionId();
|
|
2004
|
+
const resumed = resumedId ? await resumedStore.loadSession(resumedId) : null;
|
|
2005
|
+
if (resumed) {
|
|
2006
|
+
agent.hydratePersistedSession(resumed);
|
|
2007
|
+
initialHistory = agent.getChatHistory();
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
render(React.createElement(ChatInterface, { agent, initialMessage, initialHistory }), inkOptions);
|
|
1982
2011
|
// Initialize plugin system in background (non-blocking)
|
|
1983
2012
|
setImmediate(async () => {
|
|
1984
2013
|
try {
|
|
@@ -2733,12 +2762,13 @@ program
|
|
|
2733
2762
|
cli.error(LOGIN_NEEDS_BROWSER_MESSAGE);
|
|
2734
2763
|
process.exit(1);
|
|
2735
2764
|
}
|
|
2736
|
-
const {
|
|
2765
|
+
const { getCodexAuthFilePath } = await import("./providers/codex-oauth.js");
|
|
2766
|
+
const { loginChatGptWithBrowser } = await import('./commands/login-chatgpt.js');
|
|
2737
2767
|
cli.stdout("🔐 ChatGPT login");
|
|
2738
|
-
cli.stdout("Opening your browser
|
|
2768
|
+
cli.stdout("Opening your browser for ChatGPT sign-in ...");
|
|
2739
2769
|
cli.stdout("Sign in with your ChatGPT account, then return to this terminal.\n");
|
|
2740
2770
|
try {
|
|
2741
|
-
const auth = await
|
|
2771
|
+
const auth = await loginChatGptWithBrowser(cli);
|
|
2742
2772
|
cli.stdout("✅ Authenticated successfully");
|
|
2743
2773
|
if (auth.email)
|
|
2744
2774
|
cli.stdout(` Account: ${auth.email}`);
|
|
@@ -3080,6 +3110,10 @@ addLazyCommandGroup(program, 'curator', 'Propose-only maintenance report (memory
|
|
|
3080
3110
|
const { registerCuratorCommand } = await import('./commands/curator-cli.js');
|
|
3081
3111
|
registerCuratorCommand(program);
|
|
3082
3112
|
});
|
|
3113
|
+
addLazyCommandGroup(program, 'triage', 'Write a local redacted support bundle and prompt (no network, launches nothing)', async () => {
|
|
3114
|
+
const { registerTriageCommand } = await import('./commands/cli/triage-command.js');
|
|
3115
|
+
registerTriageCommand(program);
|
|
3116
|
+
});
|
|
3083
3117
|
addLazyCommandGroup(program, 'gateway-pairing', 'Operator approval for gateway device pairing', async () => {
|
|
3084
3118
|
const { registerGatewayPairingCommands } = await import('./commands/cli/native-engine-commands.js');
|
|
3085
3119
|
registerGatewayPairingCommands(program);
|
|
@@ -3116,6 +3150,14 @@ addLazyCommand(program, 'token', 'Mint a signed JWT for the API and the mobile P
|
|
|
3116
3150
|
const { createTokenCommand } = await import('./commands/token.js');
|
|
3117
3151
|
return createTokenCommand();
|
|
3118
3152
|
});
|
|
3153
|
+
addLazyCommand(program, 'pair', 'Pair an Android authenticator with a local one-time code', async () => {
|
|
3154
|
+
const { createPairCommand } = await import('./commands/device-auth.js');
|
|
3155
|
+
return createPairCommand();
|
|
3156
|
+
});
|
|
3157
|
+
addLazyCommand(program, 'devices', 'Manage Android authentication devices', async () => {
|
|
3158
|
+
const { createDevicesCommand } = await import('./commands/device-auth.js');
|
|
3159
|
+
return createDevicesCommand();
|
|
3160
|
+
});
|
|
3119
3161
|
addLazyCommandGroup(program, 'fleet', 'Inspect Fleet routing and dispatch policy decisions', async () => {
|
|
3120
3162
|
const { registerFleetCommands } = await import('./commands/cli/fleet-commands.js');
|
|
3121
3163
|
registerFleetCommands(program);
|
|
@@ -3298,6 +3340,10 @@ addLazyCommand(program, 'lessons', 'Manage lessons learned — self-improvement
|
|
|
3298
3340
|
const { createLessonsCommand } = await import('./commands/lessons.js');
|
|
3299
3341
|
return createLessonsCommand();
|
|
3300
3342
|
});
|
|
3343
|
+
addLazyCommand(program, 'resources', 'Explicit network resource inventory and read-only health selection', async () => {
|
|
3344
|
+
const { createResourcesCommand } = await import('./commands/resources.js');
|
|
3345
|
+
return createResourcesCommand();
|
|
3346
|
+
});
|
|
3301
3347
|
// Spec — BMAD-inspired spec-driven, review-gated work pipeline
|
|
3302
3348
|
addLazyCommandGroup(program, 'spec', 'Spec-driven, review-gated work pipeline (durable stories; approve before implementing)', async () => {
|
|
3303
3349
|
const { createSpecCommand } = await import('./commands/spec.js');
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -14,9 +14,20 @@ export declare class MCPManager extends EventEmitter {
|
|
|
14
14
|
private serverStatuses;
|
|
15
15
|
private retryCounts;
|
|
16
16
|
private healthCheckIntervals;
|
|
17
|
+
private reconnectTimers;
|
|
18
|
+
/**
|
|
19
|
+
* Bumped by removeServer. A still-running addServerInternal must not overwrite
|
|
20
|
+
* 'disconnected' with 'error' nor schedule autoReconnect (which would reopen
|
|
21
|
+
* the OAuth browser ~1s after an explicit cancel).
|
|
22
|
+
*/
|
|
23
|
+
private connectEpochs;
|
|
17
24
|
private serverAddPromises;
|
|
18
25
|
private initializationPromise;
|
|
19
26
|
addServer(config: MCPServerConfig): Promise<void>;
|
|
27
|
+
private currentEpoch;
|
|
28
|
+
private bumpConnectEpoch;
|
|
29
|
+
private assertConnectCurrent;
|
|
30
|
+
private clearReconnectTimer;
|
|
20
31
|
private addServerInternal;
|
|
21
32
|
private startHealthCheck;
|
|
22
33
|
private stopHealthCheck;
|
package/dist/mcp/client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { mcpToolAllowed, validateMCPToolFilter } from './import-normalize.js';
|
|
1
2
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
3
|
import { EventEmitter } from "events";
|
|
3
4
|
import { createTransport } from "./transports.js";
|
|
5
|
+
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
4
6
|
import { logger } from "../utils/logger.js";
|
|
5
7
|
export const DEFAULT_MCP_INIT_TIMEOUT_MS = 15_000;
|
|
6
8
|
const MAX_MCP_INIT_TIMEOUT_MS = 10 * 60_000;
|
|
@@ -25,9 +27,19 @@ export class MCPManager extends EventEmitter {
|
|
|
25
27
|
serverStatuses = new Map();
|
|
26
28
|
retryCounts = new Map();
|
|
27
29
|
healthCheckIntervals = new Map();
|
|
30
|
+
reconnectTimers = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Bumped by removeServer. A still-running addServerInternal must not overwrite
|
|
33
|
+
* 'disconnected' with 'error' nor schedule autoReconnect (which would reopen
|
|
34
|
+
* the OAuth browser ~1s after an explicit cancel).
|
|
35
|
+
*/
|
|
36
|
+
connectEpochs = new Map();
|
|
28
37
|
serverAddPromises = new Map();
|
|
29
38
|
initializationPromise = null;
|
|
30
39
|
async addServer(config) {
|
|
40
|
+
if (config.enabled === false)
|
|
41
|
+
return;
|
|
42
|
+
validateMCPToolFilter(config.toolFilter);
|
|
31
43
|
if (this.serverStatuses.get(config.name) === 'connected' &&
|
|
32
44
|
this.clients.has(config.name) &&
|
|
33
45
|
this.transports.has(config.name)) {
|
|
@@ -47,9 +59,30 @@ export class MCPManager extends EventEmitter {
|
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
}
|
|
62
|
+
currentEpoch(serverName) {
|
|
63
|
+
return this.connectEpochs.get(serverName) ?? 0;
|
|
64
|
+
}
|
|
65
|
+
bumpConnectEpoch(serverName) {
|
|
66
|
+
const next = this.currentEpoch(serverName) + 1;
|
|
67
|
+
this.connectEpochs.set(serverName, next);
|
|
68
|
+
return next;
|
|
69
|
+
}
|
|
70
|
+
assertConnectCurrent(serverName, epoch) {
|
|
71
|
+
if (this.currentEpoch(serverName) !== epoch) {
|
|
72
|
+
throw new Error(`MCP server "${serverName}" connection cancelled`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
clearReconnectTimer(serverName) {
|
|
76
|
+
const timer = this.reconnectTimers.get(serverName);
|
|
77
|
+
if (timer) {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
this.reconnectTimers.delete(serverName);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
50
82
|
async addServerInternal(config) {
|
|
51
83
|
this.serverConfigs.set(config.name, config);
|
|
52
84
|
this.serverStatuses.set(config.name, 'connecting');
|
|
85
|
+
const epoch = this.currentEpoch(config.name);
|
|
53
86
|
try {
|
|
54
87
|
// Handle legacy stdio-only configuration
|
|
55
88
|
let transportConfig = config.transport;
|
|
@@ -76,8 +109,24 @@ export class MCPManager extends EventEmitter {
|
|
|
76
109
|
});
|
|
77
110
|
this.clients.set(config.name, client);
|
|
78
111
|
// Connect
|
|
79
|
-
|
|
80
|
-
|
|
112
|
+
let sdkTransport = await transport.connect();
|
|
113
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
114
|
+
try {
|
|
115
|
+
await client.connect(sdkTransport);
|
|
116
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
// First connection may end in UnauthorizedError once the OAuth code has been
|
|
120
|
+
// exchanged (SDK contract): reconnect once with the stored token.
|
|
121
|
+
if (!(error instanceof UnauthorizedError) || transportConfig.auth?.type !== 'oauth')
|
|
122
|
+
throw error;
|
|
123
|
+
await transport.disconnect().catch(() => undefined);
|
|
124
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
125
|
+
sdkTransport = await transport.connect();
|
|
126
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
127
|
+
await client.connect(sdkTransport);
|
|
128
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
129
|
+
}
|
|
81
130
|
// Drain the captured MCP stderr to the logger. The stream only exists
|
|
82
131
|
// for stdio transports created with stderr:'pipe' (see StdioTransport);
|
|
83
132
|
// without a consumer the PassThrough back-pressures the child once its
|
|
@@ -92,8 +141,11 @@ export class MCPManager extends EventEmitter {
|
|
|
92
141
|
}
|
|
93
142
|
// List available tools
|
|
94
143
|
const toolsResult = await client.listTools();
|
|
144
|
+
this.assertConnectCurrent(config.name, epoch);
|
|
95
145
|
// Register tools
|
|
96
146
|
for (const tool of toolsResult.tools) {
|
|
147
|
+
if (!mcpToolAllowed(tool.name, config.toolFilter))
|
|
148
|
+
continue;
|
|
97
149
|
const mcpTool = {
|
|
98
150
|
name: `mcp__${config.name}__${tool.name}`,
|
|
99
151
|
description: tool.description || `Tool from ${config.name} server`,
|
|
@@ -108,6 +160,10 @@ export class MCPManager extends EventEmitter {
|
|
|
108
160
|
this.emit('serverAdded', config.name, toolsResult.tools.length);
|
|
109
161
|
}
|
|
110
162
|
catch (error) {
|
|
163
|
+
if (this.currentEpoch(config.name) !== epoch) {
|
|
164
|
+
// Explicit removeServer raced this connect; keep disconnected.
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
111
167
|
this.serverStatuses.set(config.name, 'error');
|
|
112
168
|
this.handleServerError(config.name, error);
|
|
113
169
|
throw error;
|
|
@@ -153,8 +209,13 @@ export class MCPManager extends EventEmitter {
|
|
|
153
209
|
if (retryCount < maxRetries) {
|
|
154
210
|
this.retryCounts.set(serverName, retryCount + 1);
|
|
155
211
|
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);
|
|
212
|
+
const epochAtError = this.currentEpoch(serverName);
|
|
156
213
|
logger.info(`Attempting to reconnect to ${serverName} in ${delay}ms (attempt ${retryCount + 1}/${maxRetries})`);
|
|
157
|
-
|
|
214
|
+
this.clearReconnectTimer(serverName);
|
|
215
|
+
const timer = setTimeout(async () => {
|
|
216
|
+
this.reconnectTimers.delete(serverName);
|
|
217
|
+
if (this.currentEpoch(serverName) !== epochAtError)
|
|
218
|
+
return;
|
|
158
219
|
try {
|
|
159
220
|
await this.removeServer(serverName);
|
|
160
221
|
await this.addServer(config);
|
|
@@ -163,6 +224,7 @@ export class MCPManager extends EventEmitter {
|
|
|
163
224
|
logger.debug(`Reconnection attempt failed for ${serverName}`, { reconnectError });
|
|
164
225
|
}
|
|
165
226
|
}, delay);
|
|
227
|
+
this.reconnectTimers.set(serverName, timer);
|
|
166
228
|
}
|
|
167
229
|
else {
|
|
168
230
|
logger.error(`Max reconnection attempts reached for ${serverName}`);
|
|
@@ -173,6 +235,9 @@ export class MCPManager extends EventEmitter {
|
|
|
173
235
|
return this.serverStatuses.get(serverName);
|
|
174
236
|
}
|
|
175
237
|
async removeServer(serverName) {
|
|
238
|
+
this.bumpConnectEpoch(serverName);
|
|
239
|
+
this.clearReconnectTimer(serverName);
|
|
240
|
+
this.serverAddPromises.delete(serverName);
|
|
176
241
|
this.stopHealthCheck(serverName);
|
|
177
242
|
this.serverStatuses.set(serverName, 'disconnected');
|
|
178
243
|
// Remove tools
|
package/dist/mcp/config.d.ts
CHANGED
|
@@ -7,8 +7,28 @@ export type { MCPConfig } from "./types.js";
|
|
|
7
7
|
export interface LoadMCPConfigOptions {
|
|
8
8
|
/** Include disabled entries for inventory and diagnostics. Runtime callers omit this. */
|
|
9
9
|
includeDisabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Project directory whose `.codebuddy/mcp.json` and `.codebuddy/settings.json` are read.
|
|
12
|
+
* Defaults to `process.cwd()` (historical behaviour). Lets diagnostics inspect another
|
|
13
|
+
* project without `process.chdir()`, which is global state shared across awaits.
|
|
14
|
+
*/
|
|
15
|
+
cwd?: string;
|
|
10
16
|
}
|
|
11
17
|
export declare function loadMCPConfig(options?: LoadMCPConfigOptions): MCPConfig;
|
|
18
|
+
export interface MCPConfigReadOnlyResult {
|
|
19
|
+
servers: MCPServerConfig[];
|
|
20
|
+
/** Sanitized warnings (no config content, no secrets). Empty when all sources are missing/valid. */
|
|
21
|
+
warnings: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Strictly read-only MCP configuration loader for diagnostics. Reads the same
|
|
25
|
+
* three sources with the same precedence, disabled masking, and env resolution
|
|
26
|
+
* as `loadMCPConfig`, but never restores/renames/chmods/creates config or
|
|
27
|
+
* backups/temporaries. Corrupt or unreadable sources yield sanitized warnings
|
|
28
|
+
* instead of being silently treated as "no server configured". Missing config
|
|
29
|
+
* is normal and produces no warning.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadMCPConfigReadOnly(options?: LoadMCPConfigOptions): MCPConfigReadOnlyResult;
|
|
12
32
|
export declare function saveMCPConfig(config: MCPConfig): void;
|
|
13
33
|
export declare function addMCPServer(config: MCPServerConfig): void;
|
|
14
34
|
export declare function removeMCPServer(serverName: string): void;
|