bonecode 1.0.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/ARCHITECTURE.md +183 -0
- package/README.md +71 -0
- package/bin/bonecode +62 -0
- package/bone/migrations/rag_vectors.sql +258 -0
- package/bone/output/agent/.dockerignore +7 -0
- package/bone/output/agent/.env.example +36 -0
- package/bone/output/agent/.github/workflows/ci.yaml +58 -0
- package/bone/output/agent/AgentDomain.bone.map +350 -0
- package/bone/output/agent/AgentDomain.postman_collection.json +958 -0
- package/bone/output/agent/Dockerfile +22 -0
- package/bone/output/agent/README.md +47 -0
- package/bone/output/agent/admin/index.html +740 -0
- package/bone/output/agent/docker-compose.yaml +22 -0
- package/bone/output/agent/k8s/deployment.yaml +75 -0
- package/bone/output/agent/migrations/agent.sql +36 -0
- package/bone/output/agent/migrations/agent_instance.sql +36 -0
- package/bone/output/agent/migrations/audit_log.sql +18 -0
- package/bone/output/agent/migrations/build_step.sql +34 -0
- package/bone/output/agent/migrations/event_outbox.sql +31 -0
- package/bone/output/agent/migrations/plan.sql +30 -0
- package/bone/output/agent/migrations/task.sql +30 -0
- package/bone/output/agent/migrations/tool_call.sql +33 -0
- package/bone/output/agent/openapi.yaml +1116 -0
- package/bone/output/agent/package.json +36 -0
- package/bone/output/agent/schema.graphql +233 -0
- package/bone/output/agent/sdk/client.ts +231 -0
- package/bone/output/agent/src/algorithms.ts +2 -0
- package/bone/output/agent/src/audit.ts +44 -0
- package/bone/output/agent/src/auth.ts +57 -0
- package/bone/output/agent/src/cron.ts +12 -0
- package/bone/output/agent/src/db.ts +32 -0
- package/bone/output/agent/src/debug.ts +66 -0
- package/bone/output/agent/src/events.ts +243 -0
- package/bone/output/agent/src/extensions.ts +54 -0
- package/bone/output/agent/src/failure_rules.ts +323 -0
- package/bone/output/agent/src/flows.ts +168 -0
- package/bone/output/agent/src/health.ts +43 -0
- package/bone/output/agent/src/index.ts +100 -0
- package/bone/output/agent/src/logger.ts +66 -0
- package/bone/output/agent/src/metrics.ts +75 -0
- package/bone/output/agent/src/migrate.ts +352 -0
- package/bone/output/agent/src/migration_diff.ts +108 -0
- package/bone/output/agent/src/notify.ts +125 -0
- package/bone/output/agent/src/routes/agent_instance.ts +234 -0
- package/bone/output/agent/src/routes/build_step.ts +105 -0
- package/bone/output/agent/src/routes/plan.ts +91 -0
- package/bone/output/agent/src/routes/task.ts +105 -0
- package/bone/output/agent/src/routes/tool_call.ts +166 -0
- package/bone/output/agent/src/schemas.ts +384 -0
- package/bone/output/agent/src/state_machines/agent_instance.ts +24 -0
- package/bone/output/agent/src/state_machines/build_step.ts +22 -0
- package/bone/output/agent/src/state_machines/plan.ts +22 -0
- package/bone/output/agent/src/state_machines/task.ts +22 -0
- package/bone/output/agent/src/state_machines/tool_call.ts +22 -0
- package/bone/output/agent/src/tests.ts +362 -0
- package/bone/output/agent/src/websocket.ts +201 -0
- package/bone/output/agent/tsconfig.json +25 -0
- package/bone/output/rag/.dockerignore +7 -0
- package/bone/output/rag/.env.example +36 -0
- package/bone/output/rag/.github/workflows/ci.yaml +58 -0
- package/bone/output/rag/Dockerfile +22 -0
- package/bone/output/rag/RAGDomain.bone.map +287 -0
- package/bone/output/rag/RAGDomain.postman_collection.json +923 -0
- package/bone/output/rag/README.md +47 -0
- package/bone/output/rag/admin/index.html +818 -0
- package/bone/output/rag/docker-compose.yaml +22 -0
- package/bone/output/rag/k8s/deployment.yaml +75 -0
- package/bone/output/rag/migrations/audit_log.sql +18 -0
- package/bone/output/rag/migrations/code_chunk.sql +34 -0
- package/bone/output/rag/migrations/code_file.sql +33 -0
- package/bone/output/rag/migrations/event_outbox.sql +31 -0
- package/bone/output/rag/migrations/indexing_job.sql +33 -0
- package/bone/output/rag/migrations/knowledge_base.sql +35 -0
- package/bone/output/rag/migrations/memory_entry.sql +34 -0
- package/bone/output/rag/openapi.yaml +1097 -0
- package/bone/output/rag/package.json +36 -0
- package/bone/output/rag/schema.graphql +245 -0
- package/bone/output/rag/sdk/client.ts +234 -0
- package/bone/output/rag/src/algorithms.ts +2 -0
- package/bone/output/rag/src/audit.ts +37 -0
- package/bone/output/rag/src/auth.ts +57 -0
- package/bone/output/rag/src/cron.ts +12 -0
- package/bone/output/rag/src/db.ts +32 -0
- package/bone/output/rag/src/debug.ts +66 -0
- package/bone/output/rag/src/events.ts +243 -0
- package/bone/output/rag/src/extensions.ts +350 -0
- package/bone/output/rag/src/failure_rules.ts +315 -0
- package/bone/output/rag/src/flows.ts +239 -0
- package/bone/output/rag/src/health.ts +43 -0
- package/bone/output/rag/src/index.ts +95 -0
- package/bone/output/rag/src/logger.ts +66 -0
- package/bone/output/rag/src/metrics.ts +75 -0
- package/bone/output/rag/src/migrate.ts +364 -0
- package/bone/output/rag/src/migration_diff.ts +108 -0
- package/bone/output/rag/src/notify.ts +99 -0
- package/bone/output/rag/src/routes/code_chunk.ts +75 -0
- package/bone/output/rag/src/routes/code_file.ts +101 -0
- package/bone/output/rag/src/routes/indexing_job.ts +87 -0
- package/bone/output/rag/src/routes/knowledge_base.ts +230 -0
- package/bone/output/rag/src/routes/memory_entry.ts +87 -0
- package/bone/output/rag/src/schemas.ts +394 -0
- package/bone/output/rag/src/state_machines/code_file.ts +23 -0
- package/bone/output/rag/src/state_machines/indexing_job.ts +22 -0
- package/bone/output/rag/src/state_machines/knowledge_base.ts +23 -0
- package/bone/output/rag/src/state_machines/memory_entry.ts +20 -0
- package/bone/output/rag/src/tests.ts +340 -0
- package/bone/output/rag/tsconfig.json +25 -0
- package/bone/output/session/.dockerignore +7 -0
- package/bone/output/session/.env.example +36 -0
- package/bone/output/session/.github/workflows/ci.yaml +58 -0
- package/bone/output/session/Dockerfile +22 -0
- package/bone/output/session/README.md +47 -0
- package/bone/output/session/SessionDomain.bone.map +350 -0
- package/bone/output/session/SessionDomain.postman_collection.json +958 -0
- package/bone/output/session/admin/index.html +667 -0
- package/bone/output/session/docker-compose.yaml +22 -0
- package/bone/output/session/k8s/deployment.yaml +75 -0
- package/bone/output/session/migrations/audit_log.sql +18 -0
- package/bone/output/session/migrations/event_outbox.sql +31 -0
- package/bone/output/session/migrations/message.sql +31 -0
- package/bone/output/session/migrations/part.sql +28 -0
- package/bone/output/session/migrations/permission.sql +28 -0
- package/bone/output/session/migrations/project.sql +28 -0
- package/bone/output/session/migrations/session.sql +38 -0
- package/bone/output/session/openapi.yaml +1101 -0
- package/bone/output/session/package.json +36 -0
- package/bone/output/session/schema.graphql +222 -0
- package/bone/output/session/sdk/client.ts +225 -0
- package/bone/output/session/src/algorithms.ts +2 -0
- package/bone/output/session/src/audit.ts +44 -0
- package/bone/output/session/src/auth.ts +57 -0
- package/bone/output/session/src/cron.ts +12 -0
- package/bone/output/session/src/db.ts +32 -0
- package/bone/output/session/src/debug.ts +66 -0
- package/bone/output/session/src/events.ts +270 -0
- package/bone/output/session/src/extensions.ts +215 -0
- package/bone/output/session/src/failure_rules.ts +284 -0
- package/bone/output/session/src/flows.ts +168 -0
- package/bone/output/session/src/health.ts +43 -0
- package/bone/output/session/src/index.ts +100 -0
- package/bone/output/session/src/logger.ts +66 -0
- package/bone/output/session/src/metrics.ts +75 -0
- package/bone/output/session/src/migrate.ts +332 -0
- package/bone/output/session/src/migration_diff.ts +108 -0
- package/bone/output/session/src/notify.ts +112 -0
- package/bone/output/session/src/routes/message.ts +93 -0
- package/bone/output/session/src/routes/part.ts +79 -0
- package/bone/output/session/src/routes/permission.ts +79 -0
- package/bone/output/session/src/routes/project.ts +79 -0
- package/bone/output/session/src/routes/session.ts +294 -0
- package/bone/output/session/src/schemas.ts +357 -0
- package/bone/output/session/src/state_machines/session.ts +23 -0
- package/bone/output/session/src/tests.ts +326 -0
- package/bone/output/session/src/websocket.ts +201 -0
- package/bone/output/session/tsconfig.json +25 -0
- package/bone/output/workspace/.dockerignore +7 -0
- package/bone/output/workspace/.env.example +36 -0
- package/bone/output/workspace/.github/workflows/ci.yaml +58 -0
- package/bone/output/workspace/Dockerfile +22 -0
- package/bone/output/workspace/README.md +45 -0
- package/bone/output/workspace/WorkspaceDomain.bone.map +189 -0
- package/bone/output/workspace/WorkspaceDomain.postman_collection.json +621 -0
- package/bone/output/workspace/admin/index.html +485 -0
- package/bone/output/workspace/docker-compose.yaml +22 -0
- package/bone/output/workspace/k8s/deployment.yaml +75 -0
- package/bone/output/workspace/migrations/audit_log.sql +18 -0
- package/bone/output/workspace/migrations/codebase.sql +34 -0
- package/bone/output/workspace/migrations/event_outbox.sql +31 -0
- package/bone/output/workspace/migrations/snapshot.sql +32 -0
- package/bone/output/workspace/migrations/workspace.sql +33 -0
- package/bone/output/workspace/openapi.yaml +721 -0
- package/bone/output/workspace/package.json +36 -0
- package/bone/output/workspace/schema.graphql +153 -0
- package/bone/output/workspace/sdk/client.ts +155 -0
- package/bone/output/workspace/src/algorithms.ts +2 -0
- package/bone/output/workspace/src/audit.ts +37 -0
- package/bone/output/workspace/src/auth.ts +57 -0
- package/bone/output/workspace/src/cron.ts +12 -0
- package/bone/output/workspace/src/db.ts +32 -0
- package/bone/output/workspace/src/debug.ts +66 -0
- package/bone/output/workspace/src/events.ts +243 -0
- package/bone/output/workspace/src/extensions.ts +44 -0
- package/bone/output/workspace/src/failure_rules.ts +153 -0
- package/bone/output/workspace/src/health.ts +43 -0
- package/bone/output/workspace/src/index.ts +89 -0
- package/bone/output/workspace/src/logger.ts +66 -0
- package/bone/output/workspace/src/metrics.ts +75 -0
- package/bone/output/workspace/src/migrate.ts +220 -0
- package/bone/output/workspace/src/migration_diff.ts +108 -0
- package/bone/output/workspace/src/notify.ts +73 -0
- package/bone/output/workspace/src/routes/codebase.ts +87 -0
- package/bone/output/workspace/src/routes/snapshot.ts +127 -0
- package/bone/output/workspace/src/routes/workspace.ts +190 -0
- package/bone/output/workspace/src/schemas.ts +231 -0
- package/bone/output/workspace/src/state_machines/codebase.ts +21 -0
- package/bone/output/workspace/src/state_machines/snapshot.ts +20 -0
- package/bone/output/workspace/src/state_machines/workspace.ts +21 -0
- package/bone/output/workspace/src/tests.ts +249 -0
- package/bone/output/workspace/tsconfig.json +25 -0
- package/compat/opencode_adapter.ts +410 -0
- package/package.json +69 -0
- package/scripts/check_benchmark_session.js +34 -0
- package/scripts/check_finish_event.js +24 -0
- package/scripts/check_parts.js +15 -0
- package/scripts/compile.js +79 -0
- package/scripts/copy_opencode.ps1 +53 -0
- package/scripts/create_functions.sql +129 -0
- package/scripts/migrate.js +85 -0
- package/scripts/migrate_from_opencode.ts +218 -0
- package/scripts/test_agent_loop.js +101 -0
- package/scripts/test_api.ps1 +116 -0
- package/scripts/test_context_builder.js +136 -0
- package/scripts/test_context_builder.ts +97 -0
- package/scripts/test_rag.js +189 -0
- package/scripts/test_stream_events.js +36 -0
- package/scripts/test_websocket_and_saga.js +216 -0
- package/src/cli.ts +475 -0
- package/src/config.ts +162 -0
- package/src/context_builder.ts +598 -0
- package/src/engine/account/account.sql.ts +39 -0
- package/src/engine/account/account.ts +456 -0
- package/src/engine/account/repo.ts +166 -0
- package/src/engine/account/schema.ts +99 -0
- package/src/engine/account/url.ts +8 -0
- package/src/engine/acp/README.md +174 -0
- package/src/engine/acp/agent.ts +1968 -0
- package/src/engine/acp/runtime.ts +22 -0
- package/src/engine/acp/session.ts +122 -0
- package/src/engine/acp/types.ts +24 -0
- package/src/engine/agent/agent.ts +463 -0
- package/src/engine/agent/generate.txt +75 -0
- package/src/engine/agent/prompt/compaction.txt +9 -0
- package/src/engine/agent/prompt/explore.txt +18 -0
- package/src/engine/agent/prompt/scout.txt +36 -0
- package/src/engine/agent/prompt/summary.txt +11 -0
- package/src/engine/agent/prompt/title.txt +44 -0
- package/src/engine/agent/subagent-permissions.ts +34 -0
- package/src/engine/auth/index.ts +96 -0
- package/src/engine/background/background/job.ts +200 -0
- package/src/engine/background/job.ts +200 -0
- package/src/engine/bus/bus-event.ts +45 -0
- package/src/engine/bus/global.ts +22 -0
- package/src/engine/bus/index.ts +203 -0
- package/src/engine/command/command/index.ts +181 -0
- package/src/engine/command/command/template/initialize.txt +66 -0
- package/src/engine/command/command/template/review.txt +101 -0
- package/src/engine/command/index.ts +181 -0
- package/src/engine/command/template/initialize.txt +66 -0
- package/src/engine/command/template/review.txt +101 -0
- package/src/engine/config/agent.ts +172 -0
- package/src/engine/config/attachment.ts +25 -0
- package/src/engine/config/command.ts +62 -0
- package/src/engine/config/config.ts +833 -0
- package/src/engine/config/console-state.ts +14 -0
- package/src/engine/config/entry-name.ts +16 -0
- package/src/engine/config/error.ts +23 -0
- package/src/engine/config/formatter.ts +13 -0
- package/src/engine/config/layout.ts +6 -0
- package/src/engine/config/lsp.ts +43 -0
- package/src/engine/config/managed.ts +71 -0
- package/src/engine/config/markdown.ts +96 -0
- package/src/engine/config/mcp.ts +56 -0
- package/src/engine/config/model-id.ts +5 -0
- package/src/engine/config/parse.ts +79 -0
- package/src/engine/config/paths.ts +45 -0
- package/src/engine/config/permission.ts +58 -0
- package/src/engine/config/plugin.ts +84 -0
- package/src/engine/config/provider.ts +111 -0
- package/src/engine/config/reference.ts +23 -0
- package/src/engine/config/server.ts +19 -0
- package/src/engine/config/skills.ts +14 -0
- package/src/engine/config/variable.ts +90 -0
- package/src/engine/control-plane/adapters/index.ts +41 -0
- package/src/engine/control-plane/adapters/worktree.ts +96 -0
- package/src/engine/control-plane/dev/README.md +19 -0
- package/src/engine/control-plane/dev/debug-workspace-plugin.ts +73 -0
- package/src/engine/control-plane/schema.ts +14 -0
- package/src/engine/control-plane/types.ts +59 -0
- package/src/engine/control-plane/util.ts +39 -0
- package/src/engine/control-plane/workspace-adapter-runtime.ts +51 -0
- package/src/engine/control-plane/workspace-context.ts +26 -0
- package/src/engine/control-plane/workspace.sql.ts +20 -0
- package/src/engine/control-plane/workspace.ts +1072 -0
- package/src/engine/data-migration.ts +161 -0
- package/src/engine/effect/app-runtime.ts +143 -0
- package/src/engine/effect/bootstrap-runtime.ts +29 -0
- package/src/engine/effect/bridge.ts +84 -0
- package/src/engine/effect/config-service.ts +67 -0
- package/src/engine/effect/instance-ref.ts +11 -0
- package/src/engine/effect/instance-registry.ts +12 -0
- package/src/engine/effect/instance-state.ts +72 -0
- package/src/engine/effect/promise.ts +17 -0
- package/src/engine/effect/run-service.ts +47 -0
- package/src/engine/effect/runner.ts +217 -0
- package/src/engine/effect/runtime-flags.ts +74 -0
- package/src/engine/effect/service-use.ts +38 -0
- package/src/engine/env/index.ts +37 -0
- package/src/engine/event-v2-bridge.ts +89 -0
- package/src/engine/file/file/ignore.ts +81 -0
- package/src/engine/file/file/index.ts +651 -0
- package/src/engine/file/file/protected.ts +59 -0
- package/src/engine/file/file/ripgrep.ts +481 -0
- package/src/engine/file/file/watcher.ts +167 -0
- package/src/engine/file/ignore.ts +81 -0
- package/src/engine/file/index.ts +651 -0
- package/src/engine/file/protected.ts +59 -0
- package/src/engine/file/ripgrep.ts +481 -0
- package/src/engine/file/watcher.ts +167 -0
- package/src/engine/format/format/formatter.ts +404 -0
- package/src/engine/format/format/index.ts +209 -0
- package/src/engine/format/formatter.ts +404 -0
- package/src/engine/format/index.ts +209 -0
- package/src/engine/git/git/index.ts +347 -0
- package/src/engine/git/index.ts +347 -0
- package/src/engine/id/id.ts +80 -0
- package/src/engine/ide/index.ts +70 -0
- package/src/engine/image/image/image.ts +176 -0
- package/src/engine/image/image.ts +176 -0
- package/src/engine/index.ts +251 -0
- package/src/engine/installation/index.ts +327 -0
- package/src/engine/lsp/client.ts +707 -0
- package/src/engine/lsp/diagnostic.ts +29 -0
- package/src/engine/lsp/language.ts +121 -0
- package/src/engine/lsp/launch.ts +21 -0
- package/src/engine/lsp/lsp/client.ts +707 -0
- package/src/engine/lsp/lsp/diagnostic.ts +29 -0
- package/src/engine/lsp/lsp/language.ts +121 -0
- package/src/engine/lsp/lsp/launch.ts +21 -0
- package/src/engine/lsp/lsp/lsp.ts +507 -0
- package/src/engine/lsp/lsp/server.ts +2064 -0
- package/src/engine/lsp/lsp.ts +507 -0
- package/src/engine/lsp/server.ts +2064 -0
- package/src/engine/mcp/auth.ts +146 -0
- package/src/engine/mcp/index.ts +958 -0
- package/src/engine/mcp/mcp/auth.ts +146 -0
- package/src/engine/mcp/mcp/index.ts +958 -0
- package/src/engine/mcp/mcp/oauth-callback.ts +232 -0
- package/src/engine/mcp/mcp/oauth-provider.ts +214 -0
- package/src/engine/mcp/oauth-callback.ts +232 -0
- package/src/engine/mcp/oauth-provider.ts +214 -0
- package/src/engine/node.ts +6 -0
- package/src/engine/patch/index.ts +689 -0
- package/src/engine/patch/patch/index.ts +689 -0
- package/src/engine/permission/arity.ts +163 -0
- package/src/engine/permission/evaluate.ts +15 -0
- package/src/engine/permission/index.ts +306 -0
- package/src/engine/permission/permission/arity.ts +163 -0
- package/src/engine/permission/permission/evaluate.ts +15 -0
- package/src/engine/permission/permission/index.ts +306 -0
- package/src/engine/permission/permission/schema.ts +13 -0
- package/src/engine/permission/schema.ts +13 -0
- package/src/engine/plugin/azure.ts +26 -0
- package/src/engine/plugin/cloudflare.ts +76 -0
- package/src/engine/plugin/codex.ts +622 -0
- package/src/engine/plugin/digitalocean.ts +411 -0
- package/src/engine/plugin/github-copilot/copilot.ts +394 -0
- package/src/engine/plugin/github-copilot/models.ts +196 -0
- package/src/engine/plugin/index.ts +295 -0
- package/src/engine/plugin/install.ts +439 -0
- package/src/engine/plugin/loader.ts +216 -0
- package/src/engine/plugin/meta.ts +188 -0
- package/src/engine/plugin/shared.ts +323 -0
- package/src/engine/project/bootstrap-service.ts +9 -0
- package/src/engine/project/bootstrap.ts +75 -0
- package/src/engine/project/instance-context.ts +24 -0
- package/src/engine/project/instance-layer.ts +11 -0
- package/src/engine/project/instance-runtime.ts +16 -0
- package/src/engine/project/instance-store.ts +193 -0
- package/src/engine/project/project.sql.ts +17 -0
- package/src/engine/project/project.ts +537 -0
- package/src/engine/project/schema.ts +13 -0
- package/src/engine/project/vcs.ts +405 -0
- package/src/engine/provider/auth.ts +225 -0
- package/src/engine/provider/error.ts +204 -0
- package/src/engine/provider/model-status.ts +8 -0
- package/src/engine/provider/provider.ts +1843 -0
- package/src/engine/provider/schema.ts +30 -0
- package/src/engine/provider/sdk/copilot/AGENTS.md +1 -0
- package/src/engine/provider/transform.ts +1376 -0
- package/src/engine/pty/index.ts +365 -0
- package/src/engine/pty/input.ts +24 -0
- package/src/engine/pty/pty/index.ts +365 -0
- package/src/engine/pty/pty/input.ts +24 -0
- package/src/engine/pty/pty/pty.bun.ts +26 -0
- package/src/engine/pty/pty/pty.node.ts +27 -0
- package/src/engine/pty/pty/pty.ts +25 -0
- package/src/engine/pty/pty/schema.ts +14 -0
- package/src/engine/pty/pty/ticket.ts +68 -0
- package/src/engine/pty/pty.bun.ts +26 -0
- package/src/engine/pty/pty.node.ts +27 -0
- package/src/engine/pty/pty.ts +25 -0
- package/src/engine/pty/schema.ts +14 -0
- package/src/engine/pty/ticket.ts +68 -0
- package/src/engine/question/index.ts +213 -0
- package/src/engine/question/question/index.ts +213 -0
- package/src/engine/question/question/schema.ts +10 -0
- package/src/engine/question/schema.ts +10 -0
- package/src/engine/reference/reference/reference.ts +241 -0
- package/src/engine/reference/reference/repository-cache.ts +147 -0
- package/src/engine/reference/reference.ts +241 -0
- package/src/engine/reference/repository-cache.ts +147 -0
- package/src/engine/session/compaction.ts +651 -0
- package/src/engine/session/compaction_logic.ts +120 -0
- package/src/engine/session/instruction.ts +238 -0
- package/src/engine/session/instruction_loader.ts +54 -0
- package/src/engine/session/llm.ts +459 -0
- package/src/engine/session/message-error.ts +14 -0
- package/src/engine/session/message-v2.ts +1202 -0
- package/src/engine/session/message.ts +146 -0
- package/src/engine/session/overflow.ts +32 -0
- package/src/engine/session/overflow_check.ts +46 -0
- package/src/engine/session/processor.ts +823 -0
- package/src/engine/session/prompt/anthropic.txt +105 -0
- package/src/engine/session/prompt/beast.txt +147 -0
- package/src/engine/session/prompt/build-switch.txt +5 -0
- package/src/engine/session/prompt/codex.txt +79 -0
- package/src/engine/session/prompt/copilot-gpt-5.txt +143 -0
- package/src/engine/session/prompt/default.txt +105 -0
- package/src/engine/session/prompt/gemini.txt +155 -0
- package/src/engine/session/prompt/gpt.txt +107 -0
- package/src/engine/session/prompt/kimi.txt +95 -0
- package/src/engine/session/prompt/max-steps.txt +16 -0
- package/src/engine/session/prompt/plan-reminder-anthropic.txt +67 -0
- package/src/engine/session/prompt/plan.txt +26 -0
- package/src/engine/session/prompt/trinity.txt +97 -0
- package/src/engine/session/prompt.ts +671 -0
- package/src/engine/session/provider_transform.ts +187 -0
- package/src/engine/session/retry.ts +200 -0
- package/src/engine/session/retry_logic.ts +65 -0
- package/src/engine/session/revert.ts +162 -0
- package/src/engine/session/run-state.ts +153 -0
- package/src/engine/session/schema.ts +26 -0
- package/src/engine/session/session.sql.ts +137 -0
- package/src/engine/session/session.ts +1011 -0
- package/src/engine/session/status.ts +94 -0
- package/src/engine/session/summary.ts +164 -0
- package/src/engine/session/system.ts +84 -0
- package/src/engine/session/system_prompt.ts +65 -0
- package/src/engine/session/todo.ts +81 -0
- package/src/engine/session/tool_registry.ts +162 -0
- package/src/engine/share/session.ts +61 -0
- package/src/engine/share/share-next.ts +376 -0
- package/src/engine/share/share.sql.ts +13 -0
- package/src/engine/shell/shell/shell.ts +215 -0
- package/src/engine/shell/shell.ts +215 -0
- package/src/engine/skill/discovery.ts +116 -0
- package/src/engine/skill/index.ts +336 -0
- package/src/engine/skill/prompt/customize-opencode.md +377 -0
- package/src/engine/skill/skill/discovery.ts +116 -0
- package/src/engine/skill/skill/index.ts +336 -0
- package/src/engine/skill/skill/prompt/customize-opencode.md +377 -0
- package/src/engine/snapshot/index.ts +762 -0
- package/src/engine/snapshot/snapshot/index.ts +762 -0
- package/src/engine/sync/README.md +179 -0
- package/src/engine/sync/event.sql.ts +17 -0
- package/src/engine/sync/index.ts +410 -0
- package/src/engine/sync/schema.ts +11 -0
- package/src/engine/temporary.ts +33 -0
- package/src/engine/tool/apply_patch.ts +313 -0
- package/src/engine/tool/apply_patch.txt +33 -0
- package/src/engine/tool/edit.ts +711 -0
- package/src/engine/tool/edit.txt +10 -0
- package/src/engine/tool/external-directory.ts +49 -0
- package/src/engine/tool/glob.ts +103 -0
- package/src/engine/tool/glob.txt +6 -0
- package/src/engine/tool/grep.ts +156 -0
- package/src/engine/tool/grep.txt +8 -0
- package/src/engine/tool/invalid.ts +21 -0
- package/src/engine/tool/json-schema.ts +164 -0
- package/src/engine/tool/lsp.ts +113 -0
- package/src/engine/tool/lsp.txt +24 -0
- package/src/engine/tool/mcp-websearch.ts +96 -0
- package/src/engine/tool/plan-enter.txt +14 -0
- package/src/engine/tool/plan-exit.txt +13 -0
- package/src/engine/tool/plan.ts +78 -0
- package/src/engine/tool/question.ts +44 -0
- package/src/engine/tool/question.txt +10 -0
- package/src/engine/tool/read.ts +337 -0
- package/src/engine/tool/read.txt +14 -0
- package/src/engine/tool/registry.ts +472 -0
- package/src/engine/tool/repo_clone.ts +80 -0
- package/src/engine/tool/repo_clone.txt +5 -0
- package/src/engine/tool/repo_overview.ts +279 -0
- package/src/engine/tool/repo_overview.txt +4 -0
- package/src/engine/tool/schema.ts +14 -0
- package/src/engine/tool/shell/id.ts +19 -0
- package/src/engine/tool/shell/prompt.ts +295 -0
- package/src/engine/tool/shell/shell.txt +77 -0
- package/src/engine/tool/shell.ts +647 -0
- package/src/engine/tool/skill.ts +75 -0
- package/src/engine/tool/skill.txt +5 -0
- package/src/engine/tool/task.ts +337 -0
- package/src/engine/tool/task.txt +58 -0
- package/src/engine/tool/task_status.ts +179 -0
- package/src/engine/tool/task_status.txt +13 -0
- package/src/engine/tool/todo.ts +57 -0
- package/src/engine/tool/todowrite.txt +167 -0
- package/src/engine/tool/tool/apply_patch.ts +313 -0
- package/src/engine/tool/tool/apply_patch.txt +33 -0
- package/src/engine/tool/tool/edit.ts +711 -0
- package/src/engine/tool/tool/edit.txt +10 -0
- package/src/engine/tool/tool/external-directory.ts +49 -0
- package/src/engine/tool/tool/glob.ts +103 -0
- package/src/engine/tool/tool/glob.txt +6 -0
- package/src/engine/tool/tool/grep.ts +156 -0
- package/src/engine/tool/tool/grep.txt +8 -0
- package/src/engine/tool/tool/invalid.ts +21 -0
- package/src/engine/tool/tool/json-schema.ts +164 -0
- package/src/engine/tool/tool/lsp.ts +113 -0
- package/src/engine/tool/tool/lsp.txt +24 -0
- package/src/engine/tool/tool/mcp-websearch.ts +96 -0
- package/src/engine/tool/tool/plan-enter.txt +14 -0
- package/src/engine/tool/tool/plan-exit.txt +13 -0
- package/src/engine/tool/tool/plan.ts +78 -0
- package/src/engine/tool/tool/question.ts +44 -0
- package/src/engine/tool/tool/question.txt +10 -0
- package/src/engine/tool/tool/read.ts +337 -0
- package/src/engine/tool/tool/read.txt +14 -0
- package/src/engine/tool/tool/registry.ts +472 -0
- package/src/engine/tool/tool/repo_clone.ts +80 -0
- package/src/engine/tool/tool/repo_clone.txt +5 -0
- package/src/engine/tool/tool/repo_overview.ts +279 -0
- package/src/engine/tool/tool/repo_overview.txt +4 -0
- package/src/engine/tool/tool/schema.ts +14 -0
- package/src/engine/tool/tool/shell/id.ts +19 -0
- package/src/engine/tool/tool/shell/prompt.ts +295 -0
- package/src/engine/tool/tool/shell/shell.txt +77 -0
- package/src/engine/tool/tool/shell.ts +647 -0
- package/src/engine/tool/tool/skill.ts +75 -0
- package/src/engine/tool/tool/skill.txt +5 -0
- package/src/engine/tool/tool/task.ts +337 -0
- package/src/engine/tool/tool/task.txt +58 -0
- package/src/engine/tool/tool/task_status.ts +179 -0
- package/src/engine/tool/tool/task_status.txt +13 -0
- package/src/engine/tool/tool/todo.ts +57 -0
- package/src/engine/tool/tool/todowrite.txt +167 -0
- package/src/engine/tool/tool/tool.ts +164 -0
- package/src/engine/tool/tool/truncate.ts +160 -0
- package/src/engine/tool/tool/truncation-dir.ts +4 -0
- package/src/engine/tool/tool/webfetch.ts +192 -0
- package/src/engine/tool/tool/webfetch.txt +13 -0
- package/src/engine/tool/tool/websearch.ts +143 -0
- package/src/engine/tool/tool/websearch.txt +14 -0
- package/src/engine/tool/tool/write.ts +104 -0
- package/src/engine/tool/tool/write.txt +8 -0
- package/src/engine/tool/tool.ts +164 -0
- package/src/engine/tool/truncate.ts +160 -0
- package/src/engine/tool/truncation-dir.ts +4 -0
- package/src/engine/tool/webfetch.ts +192 -0
- package/src/engine/tool/webfetch.txt +13 -0
- package/src/engine/tool/websearch.ts +143 -0
- package/src/engine/tool/websearch.txt +14 -0
- package/src/engine/tool/write.ts +104 -0
- package/src/engine/tool/write.txt +8 -0
- package/src/engine/util/archive.ts +17 -0
- package/src/engine/util/bom.ts +31 -0
- package/src/engine/util/data-url.ts +9 -0
- package/src/engine/util/defer.ts +10 -0
- package/src/engine/util/effect-http-client.ts +11 -0
- package/src/engine/util/error.ts +88 -0
- package/src/engine/util/filesystem.ts +252 -0
- package/src/engine/util/format.ts +20 -0
- package/src/engine/util/iife.ts +3 -0
- package/src/engine/util/lazy.ts +20 -0
- package/src/engine/util/local-context.ts +25 -0
- package/src/engine/util/locale.ts +86 -0
- package/src/engine/util/media.ts +26 -0
- package/src/engine/util/process.ts +176 -0
- package/src/engine/util/queue.ts +32 -0
- package/src/engine/util/record.ts +3 -0
- package/src/engine/util/repository.ts +158 -0
- package/src/engine/util/rpc.ts +66 -0
- package/src/engine/util/signal.ts +12 -0
- package/src/engine/util/timeout.ts +13 -0
- package/src/engine/util/token.ts +7 -0
- package/src/engine/util/util/archive.ts +17 -0
- package/src/engine/util/util/bom.ts +31 -0
- package/src/engine/util/util/data-url.ts +9 -0
- package/src/engine/util/util/defer.ts +10 -0
- package/src/engine/util/util/effect-http-client.ts +11 -0
- package/src/engine/util/util/error.ts +88 -0
- package/src/engine/util/util/filesystem.ts +252 -0
- package/src/engine/util/util/format.ts +20 -0
- package/src/engine/util/util/iife.ts +3 -0
- package/src/engine/util/util/lazy.ts +20 -0
- package/src/engine/util/util/local-context.ts +25 -0
- package/src/engine/util/util/locale.ts +86 -0
- package/src/engine/util/util/media.ts +26 -0
- package/src/engine/util/util/process.ts +176 -0
- package/src/engine/util/util/queue.ts +32 -0
- package/src/engine/util/util/record.ts +3 -0
- package/src/engine/util/util/repository.ts +158 -0
- package/src/engine/util/util/rpc.ts +66 -0
- package/src/engine/util/util/signal.ts +12 -0
- package/src/engine/util/util/timeout.ts +13 -0
- package/src/engine/util/util/token.ts +7 -0
- package/src/engine/util/util/which.ts +14 -0
- package/src/engine/util/util/wildcard.ts +59 -0
- package/src/engine/util/which.ts +14 -0
- package/src/engine/util/wildcard.ts +59 -0
- package/src/engine/worktree/index.ts +621 -0
- package/src/rag_worker.ts +519 -0
- package/src/server.ts +201 -0
- package/src/tui.ts +637 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BoneCode Agent Loop — improved fork of OpenCode's session/prompt.ts
|
|
3
|
+
*
|
|
4
|
+
* What's the same as OpenCode:
|
|
5
|
+
* - Multi-turn loop until finish_reason === "stop"
|
|
6
|
+
* - Context compaction (prune → summarize → replay)
|
|
7
|
+
* - Retry with exponential backoff + retry-after headers
|
|
8
|
+
* - Doom-loop detection (3 identical consecutive tool calls)
|
|
9
|
+
* - Permission system (suspend/resume via Deferred)
|
|
10
|
+
* - Provider message transforms (Anthropic, Mistral, DeepSeek, etc.)
|
|
11
|
+
* - Per-provider system prompts
|
|
12
|
+
* - Skills injection
|
|
13
|
+
* - Instruction file discovery (AGENTS.md, CLAUDE.md)
|
|
14
|
+
* - Tool registry (all OpenCode tools)
|
|
15
|
+
* - Sub-agent task delegation
|
|
16
|
+
*
|
|
17
|
+
* What's improved with BoneScript:
|
|
18
|
+
* - All session/message/part persistence goes through generated capability
|
|
19
|
+
* endpoints (state machine enforced, events emitted automatically)
|
|
20
|
+
* - RAG context injected from pgvector knowledge base before each LLM call
|
|
21
|
+
* - Part deltas broadcast to WebSocket part_stream channel in real time
|
|
22
|
+
* - Agent state tracked in agent_instances table (cost, tokens, state machine)
|
|
23
|
+
* - Tool calls recorded in tool_calls table with permission decisions
|
|
24
|
+
* - Durable event bus for long-running tasks
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { streamText, type Tool } from "ai";
|
|
28
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
29
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
30
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
31
|
+
import { v4 as uuid } from "uuid";
|
|
32
|
+
import { pool } from "../../../bone/output/session/src/db";
|
|
33
|
+
import { eventBus } from "../../../bone/output/session/src/events";
|
|
34
|
+
import { logger } from "../../../bone/output/session/src/logger";
|
|
35
|
+
import { counter } from "../../../bone/output/session/src/metrics";
|
|
36
|
+
import { broadcastToChannel } from "../../../bone/output/session/src/websocket";
|
|
37
|
+
import { execute_tool_calls } from "../../../bone/output/session/src/extensions";
|
|
38
|
+
import * as ProviderTransform from "./provider_transform";
|
|
39
|
+
import { delay as retryDelay, retryable } from "./retry_logic";
|
|
40
|
+
import { isOverflow } from "./overflow_check";
|
|
41
|
+
import { buildCompactionSummary } from "./compaction_logic";
|
|
42
|
+
import { getSystemPrompt } from "./system_prompt";
|
|
43
|
+
import { loadInstructionFiles } from "./instruction_loader";
|
|
44
|
+
import { buildToolRegistry } from "./tool_registry";
|
|
45
|
+
|
|
46
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
export interface PromptInput {
|
|
49
|
+
session_id: string;
|
|
50
|
+
message_id: string;
|
|
51
|
+
content: string;
|
|
52
|
+
model_id: string;
|
|
53
|
+
provider_id: string;
|
|
54
|
+
agent_name?: string;
|
|
55
|
+
/** Optional: force a specific variant (e.g. "high" for extended thinking) */
|
|
56
|
+
variant?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface LoopResult {
|
|
60
|
+
ok: boolean;
|
|
61
|
+
finish_reason: string;
|
|
62
|
+
total_tokens_in: number;
|
|
63
|
+
total_tokens_out: number;
|
|
64
|
+
total_cost: number;
|
|
65
|
+
compacted: boolean;
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DOOM_LOOP_THRESHOLD = 3;
|
|
70
|
+
const MAX_TURNS = 20; // safety cap — most tasks complete in 1-3 turns
|
|
71
|
+
|
|
72
|
+
// ─── Main Agent Loop ──────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export async function runAgentLoop(input: PromptInput): Promise<LoopResult> {
|
|
75
|
+
const { session_id, message_id, model_id, provider_id, agent_name = "build" } = input;
|
|
76
|
+
|
|
77
|
+
// Load session + project context
|
|
78
|
+
const sessionRow = await pool.query(
|
|
79
|
+
`SELECT s.*, p.worktree, p.vcs FROM sessions s JOIN projects p ON p.id = s.project_id WHERE s.id = $1`,
|
|
80
|
+
[session_id]
|
|
81
|
+
);
|
|
82
|
+
if (!sessionRow.rows[0]) throw new Error(`Session not found: ${session_id}`);
|
|
83
|
+
const session = sessionRow.rows[0];
|
|
84
|
+
|
|
85
|
+
// Create or reuse agent instance
|
|
86
|
+
const agentId = uuid();
|
|
87
|
+
await pool.query(
|
|
88
|
+
`INSERT INTO agent_instances (id, session_id, model_id, provider_id, context_window_used, context_window_max, total_cost_usd, total_tokens_in, total_tokens_out, config, state)
|
|
89
|
+
VALUES ($1, $2, $3, $4, 0, 200000, 0, 0, 0, $5, 'running')`,
|
|
90
|
+
[agentId, session_id, model_id, provider_id, JSON.stringify({ agent_name })]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Transition session to busy
|
|
94
|
+
await pool.query(`UPDATE sessions SET state = 'busy', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
95
|
+
await eventBus.publish("SessionStateChanged", { session_id, from_state: session.state, to_state: "busy", changed_at: new Date().toISOString() }, "AgentLoop");
|
|
96
|
+
|
|
97
|
+
const stats = { tokens_in: 0, tokens_out: 0, cost: 0, compacted: false };
|
|
98
|
+
let turn = 0;
|
|
99
|
+
let lastFinishReason = "unknown";
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// ── Main multi-turn loop ──────────────────────────────────────────────────
|
|
103
|
+
while (turn < MAX_TURNS) {
|
|
104
|
+
turn++;
|
|
105
|
+
if (turn === MAX_TURNS) {
|
|
106
|
+
logger.error("agent_loop_max_turns", { event: "max_turns", metadata: { session_id, max: MAX_TURNS } });
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Load full message history
|
|
111
|
+
const messages = await loadMessageHistory(session_id);
|
|
112
|
+
|
|
113
|
+
// Check if we need compaction before calling the LLM
|
|
114
|
+
const lastAssistant = messages.filter(m => m.role === "assistant").slice(-1)[0];
|
|
115
|
+
// Estimate tokens from text length when model doesn't return counts (local models)
|
|
116
|
+
const estimatedTokens = lastAssistant?.tokens_input
|
|
117
|
+
? { input: lastAssistant.tokens_input, output: lastAssistant.tokens_output || 0, total: 0, cache: { read: 0, write: 0 } }
|
|
118
|
+
: (() => {
|
|
119
|
+
// Rough estimate: 1 token ≈ 4 chars
|
|
120
|
+
const totalChars = messages.reduce((sum, m) => {
|
|
121
|
+
const parts = m.parts || [];
|
|
122
|
+
return sum + parts.reduce((s: number, p: any) => s + (p.data?.text?.length || 0), 0);
|
|
123
|
+
}, 0);
|
|
124
|
+
return { input: Math.floor(totalChars / 4), output: 0, total: Math.floor(totalChars / 4), cache: { read: 0, write: 0 } };
|
|
125
|
+
})();
|
|
126
|
+
if (isOverflow(estimatedTokens, model_id)) {
|
|
127
|
+
logger.info("context_overflow_detected", { event: "compaction", metadata: { session_id, turn } });
|
|
128
|
+
const compacted = await runCompaction(session_id, agentId, model_id, provider_id, messages);
|
|
129
|
+
if (!compacted) break; // unrecoverable overflow
|
|
130
|
+
stats.compacted = true;
|
|
131
|
+
continue; // restart loop with compacted history
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Build AI SDK messages
|
|
135
|
+
const aiMessages = await buildAIMessages(session_id, messages, model_id);
|
|
136
|
+
|
|
137
|
+
// Inject RAG context into system prompt
|
|
138
|
+
const lastUserText = getLastUserText(messages);
|
|
139
|
+
const systemPrompt = await buildSystemPromptWithRAG(session_id, session.worktree, model_id, provider_id, agent_name, lastUserText);
|
|
140
|
+
|
|
141
|
+
// Load tools — pass empty for models that don't support function calling
|
|
142
|
+
const allTools = await buildToolRegistry(session_id, agentId, session.worktree);
|
|
143
|
+
// Check if model supports tools (some local models don't)
|
|
144
|
+
const tools = supportsTools(model_id) ? allTools : {};
|
|
145
|
+
|
|
146
|
+
// Get language model
|
|
147
|
+
const languageModel = getLanguageModel(provider_id, model_id);
|
|
148
|
+
|
|
149
|
+
// Stream the LLM response
|
|
150
|
+
const assistantMsgId = uuid();
|
|
151
|
+
await pool.query(
|
|
152
|
+
`INSERT INTO messages (id, session_id, role, model_id, provider_id) VALUES ($1, $2, 'assistant', $3, $4)`,
|
|
153
|
+
[assistantMsgId, session_id, model_id, provider_id]
|
|
154
|
+
);
|
|
155
|
+
await eventBus.publish("MessageAdded", { session_id, message_id: assistantMsgId, role: "assistant", added_at: new Date().toISOString() }, "AgentLoop");
|
|
156
|
+
|
|
157
|
+
const result = await streamWithRetry({
|
|
158
|
+
session_id, agentId, assistantMsgId,
|
|
159
|
+
languageModel, aiMessages, systemPrompt, tools,
|
|
160
|
+
model_id, provider_id, stats,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
lastFinishReason = result.finish_reason;
|
|
164
|
+
|
|
165
|
+
// Update agent token counters
|
|
166
|
+
await pool.query(
|
|
167
|
+
`UPDATE agent_instances SET total_tokens_in = total_tokens_in + $2, total_tokens_out = total_tokens_out + $3, total_cost_usd = total_cost_usd + $4, updated_at = NOW() WHERE id = $1`,
|
|
168
|
+
[agentId, result.tokens_in, result.tokens_out, result.cost]
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Check termination conditions:
|
|
172
|
+
// 1. "stop" with no tool calls = model finished naturally
|
|
173
|
+
// 2. "length" = hit token limit — don't loop, the response is complete
|
|
174
|
+
// 3. "content-filter" = blocked — stop
|
|
175
|
+
// 4. "tool-calls" with no actual tool calls = model confused — stop
|
|
176
|
+
const terminalReasons = new Set(["stop", "length", "content-filter", "end-turn"]);
|
|
177
|
+
if (terminalReasons.has(result.finish_reason) && !result.has_tool_calls) {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// If there were tool calls, they were executed during streaming — loop continues
|
|
182
|
+
// If finish reason is unexpected and no tool calls, stop to avoid infinite loop
|
|
183
|
+
if (!result.has_tool_calls) {
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Transition agent to done
|
|
189
|
+
await pool.query(`UPDATE agent_instances SET state = 'done', updated_at = NOW() WHERE id = $1`, [agentId]);
|
|
190
|
+
await pool.query(`UPDATE sessions SET state = 'active', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
191
|
+
await eventBus.publish("AgentCompleted", {
|
|
192
|
+
agent_id: agentId, session_id,
|
|
193
|
+
total_cost_usd: stats.cost, total_tokens_in: stats.tokens_in, total_tokens_out: stats.tokens_out,
|
|
194
|
+
completed_at: new Date().toISOString(),
|
|
195
|
+
}, "AgentLoop");
|
|
196
|
+
|
|
197
|
+
counter("agent.loop.completed", { provider: provider_id, model: model_id, turns: String(turn) });
|
|
198
|
+
return { ok: true, finish_reason: lastFinishReason, total_tokens_in: stats.tokens_in, total_tokens_out: stats.tokens_out, total_cost: stats.cost, compacted: stats.compacted };
|
|
199
|
+
|
|
200
|
+
} catch (e: any) {
|
|
201
|
+
await pool.query(`UPDATE agent_instances SET state = 'failed', updated_at = NOW() WHERE id = $1`, [agentId]);
|
|
202
|
+
await pool.query(`UPDATE sessions SET state = 'active', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
203
|
+
await eventBus.publish("AgentFailed", { agent_id: agentId, session_id, error: e.message, failed_at: new Date().toISOString() }, "AgentLoop");
|
|
204
|
+
logger.error("agent_loop_failed", { event: "loop_error", metadata: { session_id, error: e.message } });
|
|
205
|
+
return { ok: false, finish_reason: "error", total_tokens_in: stats.tokens_in, total_tokens_out: stats.tokens_out, total_cost: stats.cost, compacted: stats.compacted, error: e.message };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ─── Stream with Retry ────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
async function streamWithRetry(ctx: {
|
|
212
|
+
session_id: string; agentId: string; assistantMsgId: string;
|
|
213
|
+
languageModel: any; aiMessages: any[]; systemPrompt: string;
|
|
214
|
+
tools: Record<string, Tool>; model_id: string; provider_id: string;
|
|
215
|
+
stats: { tokens_in: number; tokens_out: number; cost: number };
|
|
216
|
+
}): Promise<{ finish_reason: string; tokens_in: number; tokens_out: number; cost: number; has_tool_calls: boolean }> {
|
|
217
|
+
let attempt = 0;
|
|
218
|
+
const MAX_RETRIES = 5;
|
|
219
|
+
let currentCtx = { ...ctx };
|
|
220
|
+
|
|
221
|
+
while (true) {
|
|
222
|
+
try {
|
|
223
|
+
return await streamOnce(currentCtx);
|
|
224
|
+
} catch (e: any) {
|
|
225
|
+
// On Bad Request with tools, retry without tools
|
|
226
|
+
if (e.message?.includes("Bad Request") && Object.keys(currentCtx.tools).length > 0 && attempt === 0) {
|
|
227
|
+
console.log("[AgentLoop] Bad Request with tools — retrying without tools");
|
|
228
|
+
currentCtx = { ...currentCtx, tools: {} };
|
|
229
|
+
attempt++;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const retryInfo = retryable(e, ctx.provider_id);
|
|
234
|
+
if (!retryInfo || attempt >= MAX_RETRIES) throw e;
|
|
235
|
+
|
|
236
|
+
const waitMs = retryDelay(attempt, e);
|
|
237
|
+
logger.info("llm_retry", { event: "retry", metadata: { attempt, wait_ms: waitMs, reason: retryInfo.message } });
|
|
238
|
+
broadcastToChannel("session_events", {
|
|
239
|
+
type: "session.retry", session_id: ctx.session_id, attempt, message: retryInfo.message, next: Date.now() + waitMs,
|
|
240
|
+
});
|
|
241
|
+
await new Promise(r => setTimeout(r, waitMs));
|
|
242
|
+
attempt++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ─── Single Stream Pass ───────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
async function streamOnce(ctx: {
|
|
250
|
+
session_id: string; agentId: string; assistantMsgId: string;
|
|
251
|
+
languageModel: any; aiMessages: any[]; systemPrompt: string;
|
|
252
|
+
tools: Record<string, Tool>; model_id: string; provider_id: string;
|
|
253
|
+
stats: { tokens_in: number; tokens_out: number; cost: number };
|
|
254
|
+
}): Promise<{ finish_reason: string; tokens_in: number; tokens_out: number; cost: number; has_tool_calls: boolean }> {
|
|
255
|
+
const { session_id, assistantMsgId, languageModel, aiMessages, systemPrompt, tools, model_id, provider_id } = ctx;
|
|
256
|
+
|
|
257
|
+
const transformedMessages = ProviderTransform.message(
|
|
258
|
+
aiMessages,
|
|
259
|
+
{ id: model_id, providerID: provider_id, api: { npm: providerToNpm(provider_id), id: model_id } } as any,
|
|
260
|
+
{}
|
|
261
|
+
) as any[];
|
|
262
|
+
|
|
263
|
+
let currentTextPartId: string | null = null;
|
|
264
|
+
let currentTextContent = "";
|
|
265
|
+
let hasToolCalls = false;
|
|
266
|
+
let finishReason = "unknown";
|
|
267
|
+
const toolCallsThisTurn: Array<{ id: string; name: string; input: any }> = [];
|
|
268
|
+
|
|
269
|
+
const result = streamText({
|
|
270
|
+
model: languageModel,
|
|
271
|
+
system: systemPrompt,
|
|
272
|
+
messages: aiMessages as any[],
|
|
273
|
+
tools,
|
|
274
|
+
maxRetries: 0,
|
|
275
|
+
temperature: ProviderTransform.temperature({ id: model_id, providerID: provider_id } as any) ?? 0.7,
|
|
276
|
+
topP: ProviderTransform.topP({ id: model_id, providerID: provider_id } as any),
|
|
277
|
+
topK: ProviderTransform.topK({ id: model_id, providerID: provider_id } as any),
|
|
278
|
+
// Use a high maxTokens so the model can complete long responses.
|
|
279
|
+
// The local model's context window is the real limit.
|
|
280
|
+
maxTokens: parseInt(process.env.MAX_TOKENS || "8192"),
|
|
281
|
+
experimental_repairToolCall: async (failed: any) => {
|
|
282
|
+
const lower = failed.toolCall.toolName.toLowerCase();
|
|
283
|
+
if (lower !== failed.toolCall.toolName && tools[lower]) {
|
|
284
|
+
return { ...failed.toolCall, toolName: lower };
|
|
285
|
+
}
|
|
286
|
+
return { ...failed.toolCall, args: JSON.stringify({ tool: failed.toolCall.toolName, error: failed.error?.message }), toolName: "invalid" };
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Process the stream
|
|
291
|
+
for await (const event of result.fullStream) {
|
|
292
|
+
switch (event.type) {
|
|
293
|
+
case "text-delta": {
|
|
294
|
+
const text = (event as any).textDelta || (event as any).text || "";
|
|
295
|
+
if (!text) break;
|
|
296
|
+
|
|
297
|
+
if (!currentTextPartId) {
|
|
298
|
+
// Start a new text part on first delta
|
|
299
|
+
currentTextPartId = uuid();
|
|
300
|
+
currentTextContent = "";
|
|
301
|
+
await pool.query(
|
|
302
|
+
`INSERT INTO parts (id, message_id, session_id, part_type, data, order_index) VALUES ($1, $2, $3, 'text', $4, 0)`,
|
|
303
|
+
[currentTextPartId, assistantMsgId, session_id, JSON.stringify({ text: "" })]
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
currentTextContent += text;
|
|
308
|
+
// Broadcast delta to WebSocket part_stream for live streaming
|
|
309
|
+
broadcastToChannel("part_stream", {
|
|
310
|
+
type: "part.delta",
|
|
311
|
+
session_id,
|
|
312
|
+
message_id: assistantMsgId,
|
|
313
|
+
part_id: currentTextPartId,
|
|
314
|
+
delta: { type: "text", text },
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case "tool-call": {
|
|
320
|
+
// Flush any pending text part first
|
|
321
|
+
if (currentTextPartId) {
|
|
322
|
+
await pool.query(
|
|
323
|
+
`UPDATE parts SET data = $2, updated_at = NOW() WHERE id = $1`,
|
|
324
|
+
[currentTextPartId, JSON.stringify({ text: currentTextContent })]
|
|
325
|
+
);
|
|
326
|
+
await eventBus.publish("PartUpdated", {
|
|
327
|
+
session_id, message_id: assistantMsgId, part_id: currentTextPartId,
|
|
328
|
+
part_type: "text", updated_at: new Date().toISOString(),
|
|
329
|
+
}, "AgentLoop");
|
|
330
|
+
currentTextPartId = null;
|
|
331
|
+
currentTextContent = "";
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
hasToolCalls = true;
|
|
335
|
+
const toolArgs = (event as any).args || (event as any).input || {};
|
|
336
|
+
toolCallsThisTurn.push({ id: event.toolCallId, name: event.toolName, input: toolArgs });
|
|
337
|
+
|
|
338
|
+
// Record tool call in DB
|
|
339
|
+
const toolCallDbId = uuid();
|
|
340
|
+
await pool.query(
|
|
341
|
+
`INSERT INTO tool_calls (id, session_id, agent_id, tool_name, tool_input, state) VALUES ($1, $2, $3, $4, $5, 'running')`,
|
|
342
|
+
[toolCallDbId, session_id, ctx.agentId, event.toolName, JSON.stringify(toolArgs)]
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Record as part
|
|
346
|
+
const toolPartId = uuid();
|
|
347
|
+
await pool.query(
|
|
348
|
+
`INSERT INTO parts (id, message_id, session_id, part_type, data, order_index) VALUES ($1, $2, $3, 'tool_invocation', $4, 0)`,
|
|
349
|
+
[toolPartId, assistantMsgId, session_id, JSON.stringify({ tool_call_id: event.toolCallId, tool_name: event.toolName, args: toolArgs, state: "running" })]
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
await eventBus.publish("ToolCallRequested", {
|
|
353
|
+
tool_call_id: toolCallDbId, session_id, agent_id: ctx.agentId,
|
|
354
|
+
tool_name: event.toolName, tool_input: toolArgs, requested_at: new Date().toISOString(),
|
|
355
|
+
}, "AgentLoop");
|
|
356
|
+
|
|
357
|
+
// Doom-loop detection: 3 identical consecutive tool calls
|
|
358
|
+
const recentSame = toolCallsThisTurn.filter(
|
|
359
|
+
tc => tc.name === event.toolName && JSON.stringify(tc.input) === JSON.stringify(toolArgs)
|
|
360
|
+
);
|
|
361
|
+
if (recentSame.length >= DOOM_LOOP_THRESHOLD) {
|
|
362
|
+
logger.error("doom_loop_detected", { event: "doom_loop", metadata: { tool: event.toolName, session_id } });
|
|
363
|
+
throw new Error(`Doom loop detected: tool '${event.toolName}' called ${DOOM_LOOP_THRESHOLD} times with identical input`);
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
case "step-finish": {
|
|
369
|
+
// Flush pending text part
|
|
370
|
+
if (currentTextPartId) {
|
|
371
|
+
await pool.query(
|
|
372
|
+
`UPDATE parts SET data = $2, updated_at = NOW() WHERE id = $1`,
|
|
373
|
+
[currentTextPartId, JSON.stringify({ text: currentTextContent })]
|
|
374
|
+
);
|
|
375
|
+
await eventBus.publish("PartUpdated", {
|
|
376
|
+
session_id, message_id: assistantMsgId, part_id: currentTextPartId,
|
|
377
|
+
part_type: "text", updated_at: new Date().toISOString(),
|
|
378
|
+
}, "AgentLoop");
|
|
379
|
+
currentTextPartId = null;
|
|
380
|
+
currentTextContent = "";
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const usage = (event as any).usage;
|
|
384
|
+
const tokensIn = usage?.promptTokens || 0;
|
|
385
|
+
const tokensOut = usage?.completionTokens || 0;
|
|
386
|
+
ctx.stats.tokens_in += tokensIn;
|
|
387
|
+
ctx.stats.tokens_out += tokensOut;
|
|
388
|
+
|
|
389
|
+
await pool.query(
|
|
390
|
+
`UPDATE messages SET tokens_input = $2, tokens_output = $3, updated_at = NOW() WHERE id = $1`,
|
|
391
|
+
[assistantMsgId, tokensIn, tokensOut]
|
|
392
|
+
);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
case "finish": {
|
|
397
|
+
finishReason = (event as any).finishReason || "stop";
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
case "error": {
|
|
402
|
+
throw (event as any).error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Final flush — ensure any pending text part is saved
|
|
408
|
+
if (currentTextPartId && currentTextContent) {
|
|
409
|
+
await pool.query(
|
|
410
|
+
`UPDATE parts SET data = $2, updated_at = NOW() WHERE id = $1`,
|
|
411
|
+
[currentTextPartId, JSON.stringify({ text: currentTextContent })]
|
|
412
|
+
);
|
|
413
|
+
await eventBus.publish("PartUpdated", {
|
|
414
|
+
session_id, message_id: assistantMsgId, part_id: currentTextPartId,
|
|
415
|
+
part_type: "text", updated_at: new Date().toISOString(),
|
|
416
|
+
}, "AgentLoop");
|
|
417
|
+
currentTextPartId = null;
|
|
418
|
+
currentTextContent = "";
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Collect final usage
|
|
422
|
+
let tokensIn = 0, tokensOut = 0;
|
|
423
|
+
try {
|
|
424
|
+
const finalUsage = await result.usage;
|
|
425
|
+
tokensIn = finalUsage?.promptTokens || ctx.stats.tokens_in;
|
|
426
|
+
tokensOut = finalUsage?.completionTokens || ctx.stats.tokens_out;
|
|
427
|
+
} catch {
|
|
428
|
+
tokensIn = ctx.stats.tokens_in;
|
|
429
|
+
tokensOut = ctx.stats.tokens_out;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// If we still have no text part, try getting the full text from the result
|
|
433
|
+
if (!currentTextPartId) {
|
|
434
|
+
try {
|
|
435
|
+
const fullText = await result.text;
|
|
436
|
+
if (fullText && fullText.trim()) {
|
|
437
|
+
const fallbackPartId = uuid();
|
|
438
|
+
await pool.query(
|
|
439
|
+
`INSERT INTO parts (id, message_id, session_id, part_type, data, order_index) VALUES ($1, $2, $3, 'text', $4, 0)`,
|
|
440
|
+
[fallbackPartId, assistantMsgId, session_id, JSON.stringify({ text: fullText })]
|
|
441
|
+
);
|
|
442
|
+
await eventBus.publish("PartUpdated", {
|
|
443
|
+
session_id, message_id: assistantMsgId, part_id: fallbackPartId,
|
|
444
|
+
part_type: "text", updated_at: new Date().toISOString(),
|
|
445
|
+
}, "AgentLoop");
|
|
446
|
+
}
|
|
447
|
+
} catch { /* non-fatal */ }
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
finish_reason: finishReason,
|
|
452
|
+
tokens_in: tokensIn,
|
|
453
|
+
tokens_out: tokensOut,
|
|
454
|
+
cost: 0,
|
|
455
|
+
has_tool_calls: hasToolCalls,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ─── Context Compaction ───────────────────────────────────────────────────────
|
|
460
|
+
|
|
461
|
+
async function runCompaction(
|
|
462
|
+
session_id: string,
|
|
463
|
+
agentId: string,
|
|
464
|
+
model_id: string,
|
|
465
|
+
provider_id: string,
|
|
466
|
+
messages: any[]
|
|
467
|
+
): Promise<boolean> {
|
|
468
|
+
try {
|
|
469
|
+
// Transition session to compacting
|
|
470
|
+
await pool.query(`UPDATE sessions SET state = 'compacting', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
471
|
+
await eventBus.publish("SessionStateChanged", { session_id, from_state: "busy", to_state: "compacting", changed_at: new Date().toISOString() }, "AgentLoop");
|
|
472
|
+
|
|
473
|
+
// Build summary using the compaction logic (ported from OpenCode)
|
|
474
|
+
const summary = await buildCompactionSummary({
|
|
475
|
+
messages,
|
|
476
|
+
model_id,
|
|
477
|
+
provider_id,
|
|
478
|
+
session_id,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (!summary) return false;
|
|
482
|
+
|
|
483
|
+
// Store summary as a special assistant message
|
|
484
|
+
const summaryMsgId = uuid();
|
|
485
|
+
await pool.query(
|
|
486
|
+
`INSERT INTO messages (id, session_id, role, model_id, provider_id) VALUES ($1, $2, 'assistant', $3, $4)`,
|
|
487
|
+
[summaryMsgId, session_id, model_id, provider_id]
|
|
488
|
+
);
|
|
489
|
+
const summaryPartId = uuid();
|
|
490
|
+
await pool.query(
|
|
491
|
+
`INSERT INTO parts (id, message_id, session_id, part_type, data, order_index) VALUES ($1, $2, $3, 'text', $4, 0)`,
|
|
492
|
+
[summaryPartId, summaryMsgId, session_id, JSON.stringify({ text: summary, is_compaction_summary: true })]
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
// Transition back to busy
|
|
496
|
+
await pool.query(`UPDATE sessions SET state = 'busy', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
497
|
+
await eventBus.publish("SessionCompacted", {
|
|
498
|
+
session_id, tokens_before: 0, tokens_after: 0, compacted_at: new Date().toISOString(),
|
|
499
|
+
}, "AgentLoop");
|
|
500
|
+
|
|
501
|
+
broadcastToChannel("session_events", { type: "session.compacted", session_id });
|
|
502
|
+
return true;
|
|
503
|
+
} catch (e: any) {
|
|
504
|
+
logger.error("compaction_failed", { event: "compaction_error", metadata: { session_id, error: e.message } });
|
|
505
|
+
await pool.query(`UPDATE sessions SET state = 'busy', updated_at = NOW() WHERE id = $1`, [session_id]);
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ─── Message History Builder ──────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
async function loadMessageHistory(session_id: string): Promise<any[]> {
|
|
513
|
+
const result = await pool.query(
|
|
514
|
+
`SELECT m.id, m.role, m.model_id, m.provider_id, m.tokens_input, m.tokens_output,
|
|
515
|
+
json_agg(p.* ORDER BY p.order_index, p.created_at) FILTER (WHERE p.id IS NOT NULL) AS parts
|
|
516
|
+
FROM messages m
|
|
517
|
+
LEFT JOIN parts p ON p.message_id = m.id
|
|
518
|
+
WHERE m.session_id = $1
|
|
519
|
+
GROUP BY m.id ORDER BY m.created_at ASC`,
|
|
520
|
+
[session_id]
|
|
521
|
+
);
|
|
522
|
+
return result.rows;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function buildAIMessages(session_id: string, messages: any[], model_id: string): Promise<any[]> {
|
|
526
|
+
const aiMessages: any[] = [];
|
|
527
|
+
|
|
528
|
+
for (const msg of messages) {
|
|
529
|
+
const parts = msg.parts || [];
|
|
530
|
+
|
|
531
|
+
if (msg.role === "user") {
|
|
532
|
+
const content: any[] = [];
|
|
533
|
+
for (const p of parts) {
|
|
534
|
+
if (p.part_type === "text" && p.data?.text) {
|
|
535
|
+
content.push({ type: "text", text: p.data.text });
|
|
536
|
+
} else if (p.part_type === "file" && p.data?.url) {
|
|
537
|
+
content.push({ type: "file", url: p.data.url, mediaType: p.data.mime || "text/plain" });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (content.length > 0) aiMessages.push({ role: "user", content });
|
|
541
|
+
|
|
542
|
+
} else if (msg.role === "assistant") {
|
|
543
|
+
const content: any[] = [];
|
|
544
|
+
for (const p of parts) {
|
|
545
|
+
if (p.part_type === "text" && p.data?.text) {
|
|
546
|
+
// Skip compaction summaries — they're injected as system context
|
|
547
|
+
if (p.data.is_compaction_summary) continue;
|
|
548
|
+
content.push({ type: "text", text: p.data.text });
|
|
549
|
+
} else if (p.part_type === "tool_invocation" && p.data?.tool_call_id) {
|
|
550
|
+
content.push({ type: "tool-call", toolCallId: p.data.tool_call_id, toolName: p.data.tool_name, args: p.data.args || {} });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (content.length > 0) aiMessages.push({ role: "assistant", content });
|
|
554
|
+
|
|
555
|
+
} else if (msg.role === "tool") {
|
|
556
|
+
const content: any[] = [];
|
|
557
|
+
for (const p of parts) {
|
|
558
|
+
if (p.part_type === "tool_result" && p.data?.tool_call_id) {
|
|
559
|
+
content.push({ type: "tool-result", toolCallId: p.data.tool_call_id, toolName: p.data.tool_name, result: p.data.result });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (content.length > 0) aiMessages.push({ role: "tool", content });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return aiMessages;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function getLastUserText(messages: any[]): string {
|
|
570
|
+
const lastUser = messages.filter(m => m.role === "user").slice(-1)[0];
|
|
571
|
+
if (!lastUser) return "";
|
|
572
|
+
return (lastUser.parts || [])
|
|
573
|
+
.filter((p: any) => p.part_type === "text")
|
|
574
|
+
.map((p: any) => p.data?.text || "")
|
|
575
|
+
.join(" ");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ─── System Prompt with RAG ───────────────────────────────────────────────────
|
|
579
|
+
|
|
580
|
+
async function buildSystemPromptWithRAG(
|
|
581
|
+
session_id: string,
|
|
582
|
+
worktree: string,
|
|
583
|
+
model_id: string,
|
|
584
|
+
provider_id: string,
|
|
585
|
+
agent_name: string,
|
|
586
|
+
userText: string
|
|
587
|
+
): Promise<string> {
|
|
588
|
+
// Base system prompt (provider-specific, from OpenCode)
|
|
589
|
+
const base = getSystemPrompt(model_id, provider_id, agent_name);
|
|
590
|
+
|
|
591
|
+
// Environment context
|
|
592
|
+
const envContext = [
|
|
593
|
+
`Working directory: ${worktree}`,
|
|
594
|
+
`Platform: ${process.platform}`,
|
|
595
|
+
`Today's date: ${new Date().toDateString()}`,
|
|
596
|
+
].join("\n");
|
|
597
|
+
|
|
598
|
+
// Instruction files (AGENTS.md, CLAUDE.md, etc.)
|
|
599
|
+
const instructions = await loadInstructionFiles(worktree).catch(() => "");
|
|
600
|
+
|
|
601
|
+
// Codebase context — no embedding needed
|
|
602
|
+
// Uses FTS, git recency, session history, import graph, instruction files
|
|
603
|
+
let codebaseContext = "";
|
|
604
|
+
if (userText && worktree) {
|
|
605
|
+
try {
|
|
606
|
+
const { buildContext, formatContextForPrompt } = await import("../../context_builder");
|
|
607
|
+
const sessionRow = await pool.query(
|
|
608
|
+
`SELECT project_id FROM sessions WHERE id = $1`,
|
|
609
|
+
[session_id]
|
|
610
|
+
);
|
|
611
|
+
const project_id = sessionRow.rows[0]?.project_id || "";
|
|
612
|
+
|
|
613
|
+
const ctxResult = await buildContext({
|
|
614
|
+
session_id,
|
|
615
|
+
project_id,
|
|
616
|
+
worktree,
|
|
617
|
+
query: userText,
|
|
618
|
+
max_chunks: 10,
|
|
619
|
+
max_chars: 10_000,
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
codebaseContext = formatContextForPrompt(ctxResult, worktree);
|
|
623
|
+
if (ctxResult.summary !== "no relevant context found") {
|
|
624
|
+
logger.info("context_injected", { event: "context", metadata: { summary: ctxResult.summary } });
|
|
625
|
+
}
|
|
626
|
+
} catch (e: any) {
|
|
627
|
+
logger.error("context_build_failed", { event: "context", metadata: { error: e.message } });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return [base, envContext, instructions, codebaseContext].filter(Boolean).join("\n\n");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// ─── Language Model Factory ───────────────────────────────────────────────────
|
|
635
|
+
|
|
636
|
+
function getLanguageModel(provider_id: string, model_id: string): any {
|
|
637
|
+
const apiKey = process.env[`${provider_id.toUpperCase()}_API_KEY`] || process.env.OPENAI_API_KEY || "not-needed";
|
|
638
|
+
const baseUrl = process.env[`${provider_id.toUpperCase()}_BASE_URL`] || process.env.OPENAI_BASE_URL;
|
|
639
|
+
|
|
640
|
+
switch (provider_id.toLowerCase()) {
|
|
641
|
+
case "anthropic":
|
|
642
|
+
return createAnthropic({ apiKey, baseURL: baseUrl })(model_id);
|
|
643
|
+
case "google":
|
|
644
|
+
return createGoogleGenerativeAI({ apiKey })(model_id);
|
|
645
|
+
case "openai":
|
|
646
|
+
case "openai_compatible":
|
|
647
|
+
default:
|
|
648
|
+
return createOpenAI({ apiKey, baseURL: baseUrl })(model_id);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function providerToNpm(provider_id: string): string {
|
|
653
|
+
const map: Record<string, string> = {
|
|
654
|
+
anthropic: "@ai-sdk/anthropic",
|
|
655
|
+
openai: "@ai-sdk/openai",
|
|
656
|
+
google: "@ai-sdk/google",
|
|
657
|
+
openai_compatible: "@ai-sdk/openai-compatible",
|
|
658
|
+
};
|
|
659
|
+
return map[provider_id.toLowerCase()] || "@ai-sdk/openai-compatible";
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function supportsTools(model_id: string): boolean {
|
|
663
|
+
const id = model_id.toLowerCase();
|
|
664
|
+
// Known models that support function calling
|
|
665
|
+
if (id.includes("gpt-") || id.includes("claude") || id.includes("gemini")) return true;
|
|
666
|
+
// Qwen3 and other local models — check env override
|
|
667
|
+
if (process.env.MODEL_SUPPORTS_TOOLS === "true") return true;
|
|
668
|
+
if (process.env.MODEL_SUPPORTS_TOOLS === "false") return false;
|
|
669
|
+
// Default: try with tools, fall back gracefully on error
|
|
670
|
+
return true;
|
|
671
|
+
}
|