@neurocode-ai/core 1.18.8
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/bin/neurocode +199 -0
- package/bunfig.toml +2 -0
- package/drizzle.config.ts +10 -0
- package/package.json +131 -0
- package/schema.json +2071 -0
- package/script/fix-node-pty.ts +28 -0
- package/script/migration.ts +196 -0
- package/src/account/sql.ts +39 -0
- package/src/account.ts +101 -0
- package/src/agent.ts +111 -0
- package/src/aisdk.ts +235 -0
- package/src/background-job.ts +365 -0
- package/src/catalog.ts +301 -0
- package/src/command.ts +64 -0
- package/src/config/agent.ts +25 -0
- package/src/config/attachments.ts +15 -0
- package/src/config/command.ts +12 -0
- package/src/config/compaction.ts +15 -0
- package/src/config/experimental.ts +18 -0
- package/src/config/formatter.ts +12 -0
- package/src/config/lsp.ts +18 -0
- package/src/config/markdown.ts +36 -0
- package/src/config/mcp.ts +48 -0
- package/src/config/plugin/agent.ts +179 -0
- package/src/config/plugin/command.ts +83 -0
- package/src/config/plugin/external.ts +91 -0
- package/src/config/plugin/provider.ts +113 -0
- package/src/config/plugin/reference.ts +70 -0
- package/src/config/plugin/skill.ts +50 -0
- package/src/config/plugin.ts +13 -0
- package/src/config/provider.ts +71 -0
- package/src/config/reference.ts +22 -0
- package/src/config/tool-output.ts +9 -0
- package/src/config/watcher.ts +7 -0
- package/src/config.ts +227 -0
- package/src/control-plane/move-session.ts +148 -0
- package/src/control-plane/workspace.sql.ts +20 -0
- package/src/credential/sql.ts +14 -0
- package/src/credential.ts +138 -0
- package/src/cross-spawn-spawner.ts +507 -0
- package/src/data-migration.sql.ts +6 -0
- package/src/database/database.ts +57 -0
- package/src/database/migration/20260127222353_familiar_lady_ursula.ts +107 -0
- package/src/database/migration/20260211171708_add_project_commands.ts +11 -0
- package/src/database/migration/20260213144116_wakeful_the_professor.ts +23 -0
- package/src/database/migration/20260225215848_workspace.ts +19 -0
- package/src/database/migration/20260227213759_add_session_workspace_id.ts +12 -0
- package/src/database/migration/20260228203230_blue_harpoon.ts +30 -0
- package/src/database/migration/20260303231226_add_workspace_fields.ts +15 -0
- package/src/database/migration/20260309230000_move_org_to_state.ts +15 -0
- package/src/database/migration/20260312043431_session_message_cursor.ts +16 -0
- package/src/database/migration/20260323234822_events.ts +26 -0
- package/src/database/migration/20260410174513_workspace-name.ts +29 -0
- package/src/database/migration/20260413175956_chief_energizer.ts +24 -0
- package/src/database/migration/20260423070820_add_icon_url_override.ts +14 -0
- package/src/database/migration/20260427172553_slow_nightmare.ts +30 -0
- package/src/database/migration/20260428004200_add_session_path.ts +11 -0
- package/src/database/migration/20260501142318_next_venus.ts +12 -0
- package/src/database/migration/20260504145000_add_sync_owner.ts +11 -0
- package/src/database/migration/20260507164347_add_workspace_time.ts +11 -0
- package/src/database/migration/20260510033149_session_usage.ts +56 -0
- package/src/database/migration/20260511000411_data_migration_state.ts +16 -0
- package/src/database/migration/20260511173437_session-metadata.ts +16 -0
- package/src/database/migration/20260601010001_normalize_storage_paths.ts +22 -0
- package/src/database/migration/20260601202201_amazing_prowler.ts +11 -0
- package/src/database/migration/20260602002951_lowly_union_jack.ts +24 -0
- package/src/database/migration/20260602182828_add_project_directories.ts +20 -0
- package/src/database/migration/20260603001617_session_message_projection_indexes.ts +19 -0
- package/src/database/migration/20260603040000_session_message_projection_order.ts +19 -0
- package/src/database/migration/20260603141458_session_input_inbox.ts +25 -0
- package/src/database/migration/20260603160727_jittery_ezekiel_stane.ts +20 -0
- package/src/database/migration/20260604172448_event_sourced_session_input.ts +47 -0
- package/src/database/migration/20260605003541_add_session_context_snapshot.ts +21 -0
- package/src/database/migration/20260605042240_add_context_epoch_agent.ts +11 -0
- package/src/database/migration/20260611035744_credential.ts +25 -0
- package/src/database/migration/20260611192811_lush_chimera.ts +25 -0
- package/src/database/migration/20260612174303_project_dir_strategy.ts +29 -0
- package/src/database/migration/20260622142730_simplify_session_context_epoch.ts +13 -0
- package/src/database/migration/20260622170816_reset_v2_session_state.ts +17 -0
- package/src/database/migration/20260622202450_simplify_session_input.ts +17 -0
- package/src/database/migration.gen.ts +44 -0
- package/src/database/migration.ts +81 -0
- package/src/database/path.ts +91 -0
- package/src/database/schema.gen.ts +274 -0
- package/src/database/schema.sql.ts +10 -0
- package/src/database/sqlite.bun.ts +183 -0
- package/src/database/sqlite.node.ts +178 -0
- package/src/database/sqlite.ts +8 -0
- package/src/effect/app-node-builder.ts +23 -0
- package/src/effect/app-node-platform.ts +18 -0
- package/src/effect/app-node.ts +14 -0
- package/src/effect/dfdf +1 -0
- package/src/effect/keyed-mutex.ts +45 -0
- package/src/effect/layer-node.ts +333 -0
- package/src/effect/memo-map.ts +3 -0
- package/src/effect/runtime.ts +21 -0
- package/src/effect/service-use.ts +43 -0
- package/src/event/sql.ts +25 -0
- package/src/event.ts +638 -0
- package/src/file-mutation.ts +207 -0
- package/src/file.ts +6 -0
- package/src/filesystem/fff.bun.ts +140 -0
- package/src/filesystem/fff.node.ts +138 -0
- package/src/filesystem/ignore.ts +67 -0
- package/src/filesystem/protected.ts +53 -0
- package/src/filesystem/search.ts +239 -0
- package/src/filesystem/watcher.ts +140 -0
- package/src/filesystem.ts +118 -0
- package/src/flag/flag.ts +78 -0
- package/src/fs-util.ts +274 -0
- package/src/git.ts +987 -0
- package/src/github-copilot/README.md +5 -0
- package/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
- package/src/github-copilot/chat/get-response-metadata.ts +15 -0
- package/src/github-copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/src/github-copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/github-copilot/chat/openai-compatible-chat-language-model.ts +815 -0
- package/src/github-copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/github-copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/github-copilot/chat/openai-compatible-prepare-tools.ts +83 -0
- package/src/github-copilot/copilot-provider.ts +100 -0
- package/src/github-copilot/openai-compatible-error.ts +27 -0
- package/src/github-copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/src/github-copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/github-copilot/responses/openai-config.ts +18 -0
- package/src/github-copilot/responses/openai-error.ts +22 -0
- package/src/github-copilot/responses/openai-responses-api-types.ts +214 -0
- package/src/github-copilot/responses/openai-responses-language-model.ts +1770 -0
- package/src/github-copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/src/github-copilot/responses/openai-responses-settings.ts +1 -0
- package/src/github-copilot/responses/tool/code-interpreter.ts +87 -0
- package/src/github-copilot/responses/tool/file-search.ts +127 -0
- package/src/github-copilot/responses/tool/image-generation.ts +114 -0
- package/src/github-copilot/responses/tool/local-shell.ts +64 -0
- package/src/github-copilot/responses/tool/web-search-preview.ts +103 -0
- package/src/github-copilot/responses/tool/web-search.ts +102 -0
- package/src/global.ts +87 -0
- package/src/id/id.ts +47 -0
- package/src/image/photon.ts +94 -0
- package/src/image.ts +81 -0
- package/src/installation/version.ts +8 -0
- package/src/instruction-context.ts +101 -0
- package/src/integration/connection.ts +12 -0
- package/src/integration.ts +520 -0
- package/src/location-mutation.ts +162 -0
- package/src/location-service-map.ts +18 -0
- package/src/location-services.ts +115 -0
- package/src/location.ts +39 -0
- package/src/markdown.d.ts +4 -0
- package/src/model.ts +41 -0
- package/src/models-dev.ts +260 -0
- package/src/npm-config.ts +40 -0
- package/src/npm.ts +269 -0
- package/src/oauth/page.ts +276 -0
- package/src/observability/logging.ts +71 -0
- package/src/observability/otlp.ts +79 -0
- package/src/observability/shared.ts +1 -0
- package/src/observability.ts +24 -0
- package/src/patch.ts +197 -0
- package/src/permission/saved.ts +79 -0
- package/src/permission/sql.ts +20 -0
- package/src/permission.ts +310 -0
- package/src/plugin/agent.ts +206 -0
- package/src/plugin/command/initialize.txt +65 -0
- package/src/plugin/command/review.txt +100 -0
- package/src/plugin/command.ts +25 -0
- package/src/plugin/host.ts +219 -0
- package/src/plugin/internal.ts +153 -0
- package/src/plugin/layer-map.example.ts +94 -0
- package/src/plugin/models-dev.ts +183 -0
- package/src/plugin/promise.ts +93 -0
- package/src/plugin/provider/alibaba.ts +15 -0
- package/src/plugin/provider/amazon-bedrock.ts +126 -0
- package/src/plugin/provider/anthropic.ts +27 -0
- package/src/plugin/provider/azure.ts +82 -0
- package/src/plugin/provider/cerebras.ts +26 -0
- package/src/plugin/provider/cloudflare-ai-gateway.ts +81 -0
- package/src/plugin/provider/cloudflare-workers-ai.ts +81 -0
- package/src/plugin/provider/cohere.ts +15 -0
- package/src/plugin/provider/deepinfra.ts +15 -0
- package/src/plugin/provider/dynamic.ts +31 -0
- package/src/plugin/provider/gateway.ts +15 -0
- package/src/plugin/provider/github-copilot.ts +52 -0
- package/src/plugin/provider/gitlab.ts +65 -0
- package/src/plugin/provider/google-vertex.ts +173 -0
- package/src/plugin/provider/google.ts +15 -0
- package/src/plugin/provider/groq.ts +15 -0
- package/src/plugin/provider/kilo.ts +21 -0
- package/src/plugin/provider/llmgateway.ts +26 -0
- package/src/plugin/provider/mistral.ts +15 -0
- package/src/plugin/provider/nvidia.ts +22 -0
- package/src/plugin/provider/openai-compatible.ts +17 -0
- package/src/plugin/provider/openai.ts +292 -0
- package/src/plugin/provider/opencode.ts +311 -0
- package/src/plugin/provider/openrouter.ts +36 -0
- package/src/plugin/provider/perplexity.ts +15 -0
- package/src/plugin/provider/sap-ai-core.ts +46 -0
- package/src/plugin/provider/snowflake-cortex.ts +89 -0
- package/src/plugin/provider/togetherai.ts +15 -0
- package/src/plugin/provider/venice.ts +15 -0
- package/src/plugin/provider/vercel.ts +27 -0
- package/src/plugin/provider/xai.ts +22 -0
- package/src/plugin/provider/zenmux.ts +21 -0
- package/src/plugin/provider.ts +71 -0
- package/src/plugin/skill/customize-neurocode.md +452 -0
- package/src/plugin/skill.ts +31 -0
- package/src/plugin/variant.ts +39 -0
- package/src/plugin.ts +167 -0
- package/src/policy.ts +49 -0
- package/src/process.ts +261 -0
- package/src/project/copy-strategies.ts +35 -0
- package/src/project/copy.ts +292 -0
- package/src/project/directories.ts +158 -0
- package/src/project/schema.ts +16 -0
- package/src/project/sql.ts +35 -0
- package/src/project.ts +136 -0
- package/src/provider.ts +25 -0
- package/src/pty/protocol.ts +37 -0
- package/src/pty/pty.bun.ts +26 -0
- package/src/pty/pty.node.ts +29 -0
- package/src/pty/pty.ts +25 -0
- package/src/pty/schema.ts +1 -0
- package/src/pty/ticket.ts +56 -0
- package/src/pty.ts +318 -0
- package/src/public-event-manifest.ts +7 -0
- package/src/question.ts +153 -0
- package/src/reference/guidance.ts +69 -0
- package/src/reference.ts +125 -0
- package/src/repository-cache.ts +259 -0
- package/src/repository.ts +214 -0
- package/src/ripgrep/binary.ts +132 -0
- package/src/ripgrep.ts +281 -0
- package/src/schema.ts +79 -0
- package/src/session/compaction.ts +241 -0
- package/src/session/context-epoch.ts +174 -0
- package/src/session/error.ts +24 -0
- package/src/session/event.ts +2 -0
- package/src/session/execution/local.ts +46 -0
- package/src/session/execution.ts +34 -0
- package/src/session/history.ts +101 -0
- package/src/session/info.ts +50 -0
- package/src/session/input.ts +288 -0
- package/src/session/message-updater.ts +397 -0
- package/src/session/message.ts +2 -0
- package/src/session/projector.ts +458 -0
- package/src/session/prompt.ts +1 -0
- package/src/session/revert.ts +121 -0
- package/src/session/run-coordinator.ts +104 -0
- package/src/session/runner/index.ts +28 -0
- package/src/session/runner/llm.ts +432 -0
- package/src/session/runner/max-steps.ts +16 -0
- package/src/session/runner/model.ts +218 -0
- package/src/session/runner/publish-llm-event.ts +423 -0
- package/src/session/runner/to-llm-message.ts +171 -0
- package/src/session/schema.ts +9 -0
- package/src/session/sql.ts +176 -0
- package/src/session/store.ts +63 -0
- package/src/session/todo.ts +78 -0
- package/src/session.ts +486 -0
- package/src/share/sql.ts +13 -0
- package/src/shell.ts +226 -0
- package/src/skill/discovery.ts +213 -0
- package/src/skill/guidance.ts +76 -0
- package/src/skill.ts +132 -0
- package/src/snapshot.ts +266 -0
- package/src/state.ts +128 -0
- package/src/system-context/builtins.ts +50 -0
- package/src/system-context/index.ts +320 -0
- package/src/system-context/registry.ts +49 -0
- package/src/tool/AGENTS.md +59 -0
- package/src/tool/application-tools.ts +57 -0
- package/src/tool/apply-patch.ts +219 -0
- package/src/tool/bash.ts +207 -0
- package/src/tool/builtins.ts +48 -0
- package/src/tool/edit.ts +223 -0
- package/src/tool/glob.ts +105 -0
- package/src/tool/grep.ts +137 -0
- package/src/tool/http-body.ts +30 -0
- package/src/tool/question.ts +94 -0
- package/src/tool/read-filesystem.ts +366 -0
- package/src/tool/read.ts +117 -0
- package/src/tool/registry.ts +147 -0
- package/src/tool/skill.ts +109 -0
- package/src/tool/todowrite.ts +62 -0
- package/src/tool/tool.ts +162 -0
- package/src/tool/tools.ts +13 -0
- package/src/tool/webfetch.ts +218 -0
- package/src/tool/websearch.ts +260 -0
- package/src/tool/write.ts +101 -0
- package/src/tool-output-store.ts +211 -0
- package/src/util/array.ts +10 -0
- package/src/util/binary.ts +41 -0
- package/src/util/effect-flock.ts +284 -0
- package/src/util/encode.ts +51 -0
- package/src/util/error.ts +70 -0
- package/src/util/flock.ts +358 -0
- package/src/util/glob.ts +34 -0
- package/src/util/hash.ts +11 -0
- package/src/util/identifier.ts +1 -0
- package/src/util/iife.ts +3 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/module.ts +10 -0
- package/src/util/path.ts +37 -0
- package/src/util/retry.ts +42 -0
- package/src/util/slug.ts +74 -0
- package/src/util/token.ts +5 -0
- package/src/util/which.ts +14 -0
- package/src/util/wildcard.ts +14 -0
- package/src/v1/config/agent.ts +89 -0
- package/src/v1/config/attachment.ts +25 -0
- package/src/v1/config/command.ts +13 -0
- package/src/v1/config/config.ts +192 -0
- package/src/v1/config/console-state.ts +16 -0
- package/src/v1/config/error.ts +39 -0
- package/src/v1/config/formatter.ts +13 -0
- package/src/v1/config/layout.ts +6 -0
- package/src/v1/config/lsp.ts +80 -0
- package/src/v1/config/mcp.ts +63 -0
- package/src/v1/config/migrate.ts +254 -0
- package/src/v1/config/permission.ts +50 -0
- package/src/v1/config/plugin.ts +9 -0
- package/src/v1/config/provider-options.ts +227 -0
- package/src/v1/config/provider.ts +121 -0
- package/src/v1/config/server.ts +19 -0
- package/src/v1/config/skills.ts +13 -0
- package/src/v1/permission.ts +33 -0
- package/src/v1/session.ts +68 -0
- package/src/v2-schema.ts +3 -0
- package/src/workspace.ts +6 -0
- package/sst-env.d.ts +10 -0
- package/test/agent.test.ts +131 -0
- package/test/application-tools.test.ts +287 -0
- package/test/background-job.test.ts +106 -0
- package/test/catalog.test.ts +353 -0
- package/test/command.test.ts +56 -0
- package/test/config/agent.test.ts +349 -0
- package/test/config/command.test.ts +83 -0
- package/test/config/config.test.ts +798 -0
- package/test/config/fixtures/plugin/directory-plugin.ts +13 -0
- package/test/config/plugin.test.ts +248 -0
- package/test/config/provider-options.test.ts +224 -0
- package/test/config/provider.test.ts +269 -0
- package/test/config/skill.test.ts +80 -0
- package/test/credential.test.ts +36 -0
- package/test/database-migration.test.ts +644 -0
- package/test/effect/cross-spawn-spawner.test.ts +426 -0
- package/test/effect/keyed-mutex.test.ts +73 -0
- package/test/effect/layer-node/layer-node-types.test.ts +143 -0
- package/test/effect/layer-node/layer-node.test.ts +261 -0
- package/test/effect/layer-node/node-build.test.ts +125 -0
- package/test/effect/observability.test.ts +109 -0
- package/test/event.test.ts +1124 -0
- package/test/file-mutation.test.ts +368 -0
- package/test/filesystem/filesystem.test.ts +387 -0
- package/test/filesystem/ignore.test.ts +10 -0
- package/test/filesystem/search.test.ts +44 -0
- package/test/filesystem/watcher.test.ts +266 -0
- package/test/fixture/effect-flock-worker.ts +60 -0
- package/test/fixture/flock-worker.ts +72 -0
- package/test/fixture/git.ts +49 -0
- package/test/fixture/location.ts +26 -0
- package/test/fixture/tmpdir.ts +25 -0
- package/test/fixtures/recordings/session-runner/openai-chat-streams-text.json +27 -0
- package/test/git.test.ts +164 -0
- package/test/github-copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/github-copilot/copilot-chat-model.test.ts +592 -0
- package/test/github-copilot/openai-responses-language-model.test.ts +206 -0
- package/test/global.test.ts +16 -0
- package/test/instruction-context.test.ts +323 -0
- package/test/integration.test.ts +349 -0
- package/test/legacy-event-schema.test.ts +16 -0
- package/test/lib/effect.ts +53 -0
- package/test/lib/tool.ts +20 -0
- package/test/location-filesystem.test.ts +69 -0
- package/test/location-layer.test.ts +226 -0
- package/test/location-mutation.test.ts +178 -0
- package/test/location.test.ts +42 -0
- package/test/model.test.ts +23 -0
- package/test/models.test.ts +290 -0
- package/test/move-session.test.ts +235 -0
- package/test/npm-config.test.ts +51 -0
- package/test/npm.test.ts +84 -0
- package/test/oauth-page.test.ts +15 -0
- package/test/patch.test.ts +68 -0
- package/test/permission.test.ts +315 -0
- package/test/plugin/command.test.ts +47 -0
- package/test/plugin/fixture.ts +52 -0
- package/test/plugin/fixtures/config-effect-plugin.ts +15 -0
- package/test/plugin/fixtures/config-promise-plugin.ts +13 -0
- package/test/plugin/fixtures/invalid-plugin.ts +1 -0
- package/test/plugin/fixtures/models-dev.json +14 -0
- package/test/plugin/fixtures/provider-factory.ts +9 -0
- package/test/plugin/host.ts +303 -0
- package/test/plugin/models-dev.test.ts +170 -0
- package/test/plugin/promise.test.ts +67 -0
- package/test/plugin/provider-alibaba.test.ts +92 -0
- package/test/plugin/provider-amazon-bedrock.test.ts +622 -0
- package/test/plugin/provider-anthropic.test.ts +94 -0
- package/test/plugin/provider-azure-cognitive-services.test.ts +199 -0
- package/test/plugin/provider-azure.test.ts +293 -0
- package/test/plugin/provider-cerebras.test.ts +136 -0
- package/test/plugin/provider-cloudflare-ai-gateway.test.ts +426 -0
- package/test/plugin/provider-cloudflare-workers-ai.test.ts +279 -0
- package/test/plugin/provider-cohere.test.ts +123 -0
- package/test/plugin/provider-deepinfra.test.ts +159 -0
- package/test/plugin/provider-dynamic.test.ts +184 -0
- package/test/plugin/provider-gateway.test.ts +115 -0
- package/test/plugin/provider-github-copilot.test.ts +278 -0
- package/test/plugin/provider-gitlab.test.ts +308 -0
- package/test/plugin/provider-google-vertex-anthropic.test.ts +264 -0
- package/test/plugin/provider-google-vertex.test.ts +387 -0
- package/test/plugin/provider-google.test.ts +80 -0
- package/test/plugin/provider-groq.test.ts +121 -0
- package/test/plugin/provider-kilo.test.ts +99 -0
- package/test/plugin/provider-llmgateway.test.ts +80 -0
- package/test/plugin/provider-mistral.test.ts +129 -0
- package/test/plugin/provider-nvidia.test.ts +97 -0
- package/test/plugin/provider-openai-compatible.test.ts +107 -0
- package/test/plugin/provider-openai.test.ts +176 -0
- package/test/plugin/provider-opencode.test.ts +423 -0
- package/test/plugin/provider-openrouter.test.ts +113 -0
- package/test/plugin/provider-perplexity.test.ts +122 -0
- package/test/plugin/provider-sap-ai-core.test.ts +159 -0
- package/test/plugin/provider-snowflake-cortex.test.ts +261 -0
- package/test/plugin/provider-togetherai.test.ts +129 -0
- package/test/plugin/provider-venice.test.ts +115 -0
- package/test/plugin/provider-vercel.test.ts +83 -0
- package/test/plugin/provider-xai.test.ts +125 -0
- package/test/plugin/provider-zenmux.test.ts +110 -0
- package/test/plugin/skill.test.ts +25 -0
- package/test/plugin/variant.test.ts +67 -0
- package/test/plugin.test.ts +71 -0
- package/test/policy.test.ts +85 -0
- package/test/preload.ts +5 -0
- package/test/process/process.test.ts +368 -0
- package/test/project-copy.test.ts +400 -0
- package/test/project-directories.test.ts +61 -0
- package/test/project.test.ts +221 -0
- package/test/provider-mistral.test.ts +282 -0
- package/test/provider-xai-responses.test.ts +32 -0
- package/test/pty/info-schema.test.ts +32 -0
- package/test/pty/protocol.test.ts +27 -0
- package/test/pty/pty-session.test.ts +240 -0
- package/test/pty/ticket.test.ts +62 -0
- package/test/question.test.ts +114 -0
- package/test/reference-guidance.test.ts +76 -0
- package/test/reference.test.ts +78 -0
- package/test/repository-cache.test.ts +153 -0
- package/test/repository.test.ts +71 -0
- package/test/ripgrep.test.ts +65 -0
- package/test/session-compaction.test.ts +27 -0
- package/test/session-create.test.ts +425 -0
- package/test/session-history.test.ts +165 -0
- package/test/session-projector.test.ts +533 -0
- package/test/session-prompt.test.ts +584 -0
- package/test/session-run-coordinator.test.ts +418 -0
- package/test/session-runner-message.test.ts +501 -0
- package/test/session-runner-model.test.ts +347 -0
- package/test/session-runner-recorded.test.ts +193 -0
- package/test/session-runner-tool-events.test.ts +136 -0
- package/test/session-runner-tool-registry.test.ts +452 -0
- package/test/session-runner.test.ts +3365 -0
- package/test/session-todo.test.ts +94 -0
- package/test/session-tool-progress.test.ts +158 -0
- package/test/shared-schema.test.ts +206 -0
- package/test/shell.test.ts +108 -0
- package/test/skill/guidance.test.ts +144 -0
- package/test/skill-discovery.test.ts +165 -0
- package/test/skill.test.ts +125 -0
- package/test/snapshot.test.ts +178 -0
- package/test/state.test.ts +115 -0
- package/test/system-context/builtins.test.ts +129 -0
- package/test/system-context/index.test.ts +307 -0
- package/test/system-context/registry.test.ts +114 -0
- package/test/tool-apply-patch.test.ts +436 -0
- package/test/tool-bash.test.ts +437 -0
- package/test/tool-edit.test.ts +434 -0
- package/test/tool-output-store.test.ts +247 -0
- package/test/tool-question.test.ts +162 -0
- package/test/tool-read-filesystem.test.ts +118 -0
- package/test/tool-read.test.ts +674 -0
- package/test/tool-skill.test.ts +149 -0
- package/test/tool-todowrite.test.ts +125 -0
- package/test/tool-webfetch.test.ts +281 -0
- package/test/tool-websearch.test.ts +316 -0
- package/test/tool-write.test.ts +305 -0
- package/test/util/effect-flock.test.ts +392 -0
- package/test/util/flock.test.ts +428 -0
- package/test/util/which.test.ts +100 -0
- package/tsconfig.json +7 -0
package/src/pty.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
export * as Pty from "./pty"
|
|
2
|
+
|
|
3
|
+
import { makeLocationNode } from "./effect/app-node"
|
|
4
|
+
import type { Disp, Proc } from "#pty"
|
|
5
|
+
import { Context, Effect, Layer, Schema, Types } from "effect"
|
|
6
|
+
import { Pty } from "@neurocode-ai/schema/pty"
|
|
7
|
+
import { Config } from "./config"
|
|
8
|
+
import { EventV2 } from "./event"
|
|
9
|
+
import { Location } from "./location"
|
|
10
|
+
import { PtyID } from "./pty/schema"
|
|
11
|
+
import { Shell } from "./shell"
|
|
12
|
+
import { lazy } from "./util/lazy"
|
|
13
|
+
|
|
14
|
+
const BUFFER_LIMIT = 1024 * 1024 * 2
|
|
15
|
+
// Exited sessions stay observable (status, exit code, retained output) until removed explicitly.
|
|
16
|
+
// Cap retention so abandoned terminals do not accumulate unbounded buffers.
|
|
17
|
+
const EXITED_LIMIT = 25
|
|
18
|
+
const pty = lazy(() => import("#pty"))
|
|
19
|
+
|
|
20
|
+
type Subscriber = {
|
|
21
|
+
readonly onData: (chunk: string) => void
|
|
22
|
+
readonly onEnd: (event: { exitCode?: number }) => void
|
|
23
|
+
active: boolean
|
|
24
|
+
detached: boolean
|
|
25
|
+
pending: string[]
|
|
26
|
+
end?: { exitCode?: number }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type Active = {
|
|
30
|
+
info: Info
|
|
31
|
+
process: Proc
|
|
32
|
+
buffer: string
|
|
33
|
+
bufferCursor: number
|
|
34
|
+
cursor: number
|
|
35
|
+
subscribers: Map<object, Subscriber>
|
|
36
|
+
listeners: Disp[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Info = Pty.Info
|
|
40
|
+
export type Info = Types.DeepMutable<typeof Info.Type>
|
|
41
|
+
|
|
42
|
+
export const CreateInput = Pty.CreateInput
|
|
43
|
+
|
|
44
|
+
export type CreateInput = Types.DeepMutable<typeof CreateInput.Type>
|
|
45
|
+
|
|
46
|
+
export const UpdateInput = Pty.UpdateInput
|
|
47
|
+
|
|
48
|
+
export type UpdateInput = Types.DeepMutable<typeof UpdateInput.Type>
|
|
49
|
+
|
|
50
|
+
export const Event = Pty.Event
|
|
51
|
+
|
|
52
|
+
export type AttachInput = {
|
|
53
|
+
// Absolute output cursor to replay from. -1 tails from the current end; omitted replays the full retained buffer.
|
|
54
|
+
readonly cursor?: number
|
|
55
|
+
// Callbacks fire synchronously from the native PTY data path; keep them non-blocking.
|
|
56
|
+
readonly onData: (chunk: string) => void
|
|
57
|
+
// Fired once when the session stops producing output: process exit (exitCode set), removal, or service teardown.
|
|
58
|
+
readonly onEnd: (event: { exitCode?: number }) => void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type Attachment = {
|
|
62
|
+
// Retained output from the requested cursor to the current end.
|
|
63
|
+
readonly replay: string
|
|
64
|
+
// Absolute output cursor after replay.
|
|
65
|
+
readonly cursor: number
|
|
66
|
+
readonly write: (data: string) => void
|
|
67
|
+
// Starts live delivery after the caller has applied replay and cursor metadata.
|
|
68
|
+
readonly activate: () => void
|
|
69
|
+
readonly detach: () => void
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Pty.NotFoundError", {
|
|
73
|
+
ptyID: PtyID,
|
|
74
|
+
}) {}
|
|
75
|
+
|
|
76
|
+
export class ExitedError extends Schema.TaggedErrorClass<ExitedError>()("Pty.ExitedError", {
|
|
77
|
+
ptyID: PtyID,
|
|
78
|
+
}) {}
|
|
79
|
+
|
|
80
|
+
export interface Interface {
|
|
81
|
+
readonly list: () => Effect.Effect<Info[]>
|
|
82
|
+
readonly get: (id: PtyID) => Effect.Effect<Info, NotFoundError>
|
|
83
|
+
readonly create: (input: CreateInput) => Effect.Effect<Info>
|
|
84
|
+
readonly update: (id: PtyID, input: UpdateInput) => Effect.Effect<Info, NotFoundError>
|
|
85
|
+
readonly remove: (id: PtyID) => Effect.Effect<void, NotFoundError>
|
|
86
|
+
readonly write: (id: PtyID, data: string) => Effect.Effect<void, NotFoundError>
|
|
87
|
+
readonly attach: (id: PtyID, input: AttachInput) => Effect.Effect<Attachment, NotFoundError | ExitedError>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Pty") {}
|
|
91
|
+
|
|
92
|
+
const layer = Layer.effect(
|
|
93
|
+
Service,
|
|
94
|
+
Effect.gen(function* () {
|
|
95
|
+
const events = yield* EventV2.Service
|
|
96
|
+
const location = yield* Location.Service
|
|
97
|
+
const config = yield* Config.Service
|
|
98
|
+
const context = yield* Effect.context()
|
|
99
|
+
const runFork = Effect.runForkWith(context)
|
|
100
|
+
const sessions = new Map<PtyID, Active>()
|
|
101
|
+
const exitOrder: PtyID[] = []
|
|
102
|
+
|
|
103
|
+
function notifyEnd(session: Active, event: { exitCode?: number }) {
|
|
104
|
+
for (const subscriber of session.subscribers.values()) {
|
|
105
|
+
if (!subscriber.active) {
|
|
106
|
+
subscriber.end = event
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
subscriber.onEnd(event)
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
session.subscribers.clear()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function teardown(session: Active) {
|
|
117
|
+
for (const listener of session.listeners) listener.dispose()
|
|
118
|
+
session.listeners.length = 0
|
|
119
|
+
if (session.info.status === "running") {
|
|
120
|
+
try {
|
|
121
|
+
session.process.kill()
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
notifyEnd(session, {})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
yield* Effect.addFinalizer(() =>
|
|
128
|
+
Effect.sync(() => {
|
|
129
|
+
for (const session of sessions.values()) teardown(session)
|
|
130
|
+
sessions.clear()
|
|
131
|
+
exitOrder.length = 0
|
|
132
|
+
}),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const requireSession = Effect.fn("Pty.requireSession")(function* (id: PtyID) {
|
|
136
|
+
const session = sessions.get(id)
|
|
137
|
+
if (!session) return yield* new NotFoundError({ ptyID: id })
|
|
138
|
+
return session
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const removeSession = Effect.fnUntraced(function* (id: PtyID) {
|
|
142
|
+
const session = sessions.get(id)
|
|
143
|
+
if (!session) return
|
|
144
|
+
sessions.delete(id)
|
|
145
|
+
const index = exitOrder.indexOf(id)
|
|
146
|
+
if (index !== -1) exitOrder.splice(index, 1)
|
|
147
|
+
yield* Effect.logInfo("removing session", { id })
|
|
148
|
+
teardown(session)
|
|
149
|
+
yield* events.publish(Event.Deleted, { id: session.info.id })
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const remove = Effect.fn("Pty.remove")(function* (id: PtyID) {
|
|
153
|
+
yield* requireSession(id)
|
|
154
|
+
yield* removeSession(id)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const list = Effect.fn("Pty.list")(function* () {
|
|
158
|
+
return Array.from(sessions.values()).map((session) => session.info)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const get = Effect.fn("Pty.get")(function* (id: PtyID) {
|
|
162
|
+
return (yield* requireSession(id)).info
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const create = Effect.fn("Pty.create")(function* (input: CreateInput) {
|
|
166
|
+
const id = PtyID.ascending()
|
|
167
|
+
const command = input.command || Shell.preferred(Config.latest(yield* config.entries(), "shell"))
|
|
168
|
+
const args = Shell.login(command) ? [...(input.args ?? []), "-l"] : [...(input.args ?? [])]
|
|
169
|
+
const cwd = input.cwd || location.directory
|
|
170
|
+
const env = {
|
|
171
|
+
...process.env,
|
|
172
|
+
...input.env,
|
|
173
|
+
TERM: "xterm-256color",
|
|
174
|
+
NEUROCODE_TERMINAL: "1",
|
|
175
|
+
} as Record<string, string>
|
|
176
|
+
if (process.platform === "win32") {
|
|
177
|
+
env.LC_ALL = "C.UTF-8"
|
|
178
|
+
env.LC_CTYPE = "C.UTF-8"
|
|
179
|
+
env.LANG = "C.UTF-8"
|
|
180
|
+
}
|
|
181
|
+
yield* Effect.logInfo("creating session", { id, cmd: command, args, cwd })
|
|
182
|
+
const { spawn } = yield* Effect.promise(() => pty())
|
|
183
|
+
const proc = yield* Effect.sync(() => spawn(command, args, { name: "xterm-256color", cwd, env }))
|
|
184
|
+
const info: Info = {
|
|
185
|
+
id,
|
|
186
|
+
title: input.title || `Terminal ${id.slice(-4)}`,
|
|
187
|
+
command,
|
|
188
|
+
args,
|
|
189
|
+
cwd,
|
|
190
|
+
status: "running",
|
|
191
|
+
pid: proc.pid,
|
|
192
|
+
}
|
|
193
|
+
const session: Active = {
|
|
194
|
+
info,
|
|
195
|
+
process: proc,
|
|
196
|
+
buffer: "",
|
|
197
|
+
bufferCursor: 0,
|
|
198
|
+
cursor: 0,
|
|
199
|
+
subscribers: new Map(),
|
|
200
|
+
listeners: [],
|
|
201
|
+
}
|
|
202
|
+
sessions.set(id, session)
|
|
203
|
+
session.listeners.push(
|
|
204
|
+
proc.onData((chunk) => {
|
|
205
|
+
session.cursor += chunk.length
|
|
206
|
+
for (const [token, subscriber] of session.subscribers.entries()) {
|
|
207
|
+
if (!subscriber.active) {
|
|
208
|
+
subscriber.pending.push(chunk)
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
subscriber.onData(chunk)
|
|
213
|
+
} catch {
|
|
214
|
+
session.subscribers.delete(token)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
session.buffer += chunk
|
|
218
|
+
if (session.buffer.length <= BUFFER_LIMIT) return
|
|
219
|
+
const excess = session.buffer.length - BUFFER_LIMIT
|
|
220
|
+
session.buffer = session.buffer.slice(excess)
|
|
221
|
+
session.bufferCursor += excess
|
|
222
|
+
}),
|
|
223
|
+
proc.onExit(({ exitCode }) => {
|
|
224
|
+
if (session.info.status === "exited") return
|
|
225
|
+
session.info.status = "exited"
|
|
226
|
+
session.info.exitCode = exitCode
|
|
227
|
+
notifyEnd(session, { exitCode })
|
|
228
|
+
exitOrder.push(id)
|
|
229
|
+
runFork(
|
|
230
|
+
Effect.gen(function* () {
|
|
231
|
+
yield* Effect.logInfo("session exited", { id, exitCode })
|
|
232
|
+
yield* events.publish(Event.Exited, { id, exitCode })
|
|
233
|
+
while (exitOrder.length > EXITED_LIMIT) {
|
|
234
|
+
const oldest = exitOrder[0]
|
|
235
|
+
if (!oldest) break
|
|
236
|
+
yield* removeSession(oldest)
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
)
|
|
240
|
+
}),
|
|
241
|
+
)
|
|
242
|
+
yield* events.publish(Event.Created, { info })
|
|
243
|
+
return info
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
const update = Effect.fn("Pty.update")(function* (id: PtyID, input: UpdateInput) {
|
|
247
|
+
const session = yield* requireSession(id)
|
|
248
|
+
if (input.title) session.info.title = input.title
|
|
249
|
+
if (input.size && session.info.status === "running") session.process.resize(input.size.cols, input.size.rows)
|
|
250
|
+
yield* events.publish(Event.Updated, { info: session.info })
|
|
251
|
+
return session.info
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const write = Effect.fn("Pty.write")(function* (id: PtyID, data: string) {
|
|
255
|
+
const session = yield* requireSession(id)
|
|
256
|
+
if (session.info.status === "running") session.process.write(data)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
const attach = Effect.fn("Pty.attach")(function* (id: PtyID, input: AttachInput) {
|
|
260
|
+
const session = yield* requireSession(id)
|
|
261
|
+
if (session.info.status !== "running") return yield* new ExitedError({ ptyID: id })
|
|
262
|
+
yield* Effect.logInfo("client attached to session", { id, directory: location.directory })
|
|
263
|
+
const token = {}
|
|
264
|
+
const subscriber: Subscriber = {
|
|
265
|
+
onData: input.onData,
|
|
266
|
+
onEnd: input.onEnd,
|
|
267
|
+
active: false,
|
|
268
|
+
detached: false,
|
|
269
|
+
pending: [],
|
|
270
|
+
}
|
|
271
|
+
session.subscribers.set(token, subscriber)
|
|
272
|
+
const start = session.bufferCursor
|
|
273
|
+
const end = session.cursor
|
|
274
|
+
const from =
|
|
275
|
+
input.cursor === -1
|
|
276
|
+
? end
|
|
277
|
+
: typeof input.cursor === "number" && Number.isSafeInteger(input.cursor)
|
|
278
|
+
? Math.max(0, input.cursor)
|
|
279
|
+
: 0
|
|
280
|
+
const replay = (() => {
|
|
281
|
+
if (!session.buffer || from >= end) return ""
|
|
282
|
+
const offset = Math.max(0, from - start)
|
|
283
|
+
if (offset >= session.buffer.length) return ""
|
|
284
|
+
return session.buffer.slice(offset)
|
|
285
|
+
})()
|
|
286
|
+
return {
|
|
287
|
+
replay,
|
|
288
|
+
cursor: end,
|
|
289
|
+
write: (data: string) => {
|
|
290
|
+
if (session.info.status === "running") session.process.write(data)
|
|
291
|
+
},
|
|
292
|
+
activate: () => {
|
|
293
|
+
if (subscriber.active || subscriber.detached) return
|
|
294
|
+
subscriber.active = true
|
|
295
|
+
try {
|
|
296
|
+
for (const chunk of subscriber.pending) subscriber.onData(chunk)
|
|
297
|
+
subscriber.pending.length = 0
|
|
298
|
+
if (subscriber.end) subscriber.onEnd(subscriber.end)
|
|
299
|
+
} catch {
|
|
300
|
+
session.subscribers.delete(token)
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
detach: () => {
|
|
304
|
+
subscriber.detached = true
|
|
305
|
+
subscriber.pending.length = 0
|
|
306
|
+
subscriber.end = undefined
|
|
307
|
+
session.subscribers.delete(token)
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
return Service.of({ list, get, create, update, remove, write, attach })
|
|
313
|
+
}),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer))
|
|
317
|
+
|
|
318
|
+
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node, Location.node, Config.node] })
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as PublicEventManifest from "./public-event-manifest"
|
|
2
|
+
|
|
3
|
+
import { Event } from "@neurocode-ai/schema/event"
|
|
4
|
+
import { EventManifest } from "@neurocode-ai/schema/event-manifest"
|
|
5
|
+
|
|
6
|
+
export const Definitions = EventManifest.ServerDefinitions
|
|
7
|
+
export const Latest = Event.latest(Definitions)
|
package/src/question.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export * as QuestionV2 from "./question"
|
|
2
|
+
|
|
3
|
+
import { makeLocationNode } from "./effect/app-node"
|
|
4
|
+
import { Context, Deferred, Effect, Layer, Schema } from "effect"
|
|
5
|
+
import { Question } from "@neurocode-ai/schema/question"
|
|
6
|
+
import { EventV2 } from "./event"
|
|
7
|
+
import { SessionSchema } from "./session/schema"
|
|
8
|
+
|
|
9
|
+
export const ID = Question.ID
|
|
10
|
+
export type ID = typeof ID.Type
|
|
11
|
+
|
|
12
|
+
export const Option = Question.Option
|
|
13
|
+
export type Option = typeof Option.Type
|
|
14
|
+
|
|
15
|
+
export const Info = Question.Info
|
|
16
|
+
export type Info = typeof Info.Type
|
|
17
|
+
|
|
18
|
+
export const Prompt = Question.Prompt
|
|
19
|
+
export type Prompt = typeof Prompt.Type
|
|
20
|
+
|
|
21
|
+
export const Tool = Question.Tool
|
|
22
|
+
export type Tool = typeof Tool.Type
|
|
23
|
+
|
|
24
|
+
export const Request = Question.Request
|
|
25
|
+
export type Request = typeof Request.Type
|
|
26
|
+
|
|
27
|
+
export const Answer = Question.Answer
|
|
28
|
+
export type Answer = typeof Answer.Type
|
|
29
|
+
|
|
30
|
+
export const Reply = Question.Reply
|
|
31
|
+
export type Reply = typeof Reply.Type
|
|
32
|
+
|
|
33
|
+
export const Event = Question.Event
|
|
34
|
+
|
|
35
|
+
export class RejectedError extends Schema.TaggedErrorClass<RejectedError>()("QuestionV2.RejectedError", {}) {
|
|
36
|
+
override get message() {
|
|
37
|
+
return "The user dismissed this question"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("QuestionV2.NotFoundError", {
|
|
42
|
+
requestID: ID,
|
|
43
|
+
}) {}
|
|
44
|
+
|
|
45
|
+
export interface AskInput {
|
|
46
|
+
readonly sessionID: SessionSchema.ID
|
|
47
|
+
readonly questions: ReadonlyArray<Info>
|
|
48
|
+
readonly tool?: Tool
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ReplyInput {
|
|
52
|
+
readonly requestID: ID
|
|
53
|
+
readonly answers: ReadonlyArray<Answer>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Interface {
|
|
57
|
+
readonly ask: (input: AskInput) => Effect.Effect<ReadonlyArray<Answer>, RejectedError>
|
|
58
|
+
readonly reply: (input: ReplyInput) => Effect.Effect<void, NotFoundError>
|
|
59
|
+
readonly reject: (requestID: ID) => Effect.Effect<void, NotFoundError>
|
|
60
|
+
readonly list: () => Effect.Effect<ReadonlyArray<Request>>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Question") {}
|
|
64
|
+
|
|
65
|
+
interface Pending {
|
|
66
|
+
readonly request: Request
|
|
67
|
+
readonly deferred: Deferred.Deferred<ReadonlyArray<Answer>, RejectedError>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Location-owned pending prompts. The Location layer map must materialize this
|
|
72
|
+
* layer once per embedded Location so replies cannot settle another Location's
|
|
73
|
+
* deferred request.
|
|
74
|
+
*/
|
|
75
|
+
const layer = Layer.effect(
|
|
76
|
+
Service,
|
|
77
|
+
Effect.gen(function* () {
|
|
78
|
+
const events = yield* EventV2.Service
|
|
79
|
+
const pending = new Map<ID, Pending>()
|
|
80
|
+
|
|
81
|
+
yield* Effect.addFinalizer(() =>
|
|
82
|
+
Effect.forEach(pending.values(), (item) => Deferred.fail(item.deferred, new RejectedError()), {
|
|
83
|
+
discard: true,
|
|
84
|
+
}).pipe(
|
|
85
|
+
Effect.ensuring(
|
|
86
|
+
Effect.sync(() => {
|
|
87
|
+
pending.clear()
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const ask = Effect.fn("QuestionV2.ask")((input: AskInput) =>
|
|
94
|
+
Effect.uninterruptibleMask((restore) =>
|
|
95
|
+
Effect.gen(function* () {
|
|
96
|
+
const id = ID.ascending()
|
|
97
|
+
const deferred = yield* Deferred.make<ReadonlyArray<Answer>, RejectedError>()
|
|
98
|
+
const request: Request = { id, ...input }
|
|
99
|
+
pending.set(id, { request, deferred })
|
|
100
|
+
return yield* events.publish(Event.Asked, request).pipe(
|
|
101
|
+
Effect.andThen(restore(Deferred.await(deferred))),
|
|
102
|
+
Effect.ensuring(
|
|
103
|
+
Effect.sync(() => {
|
|
104
|
+
pending.delete(id)
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
}),
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const reply = Effect.fn("QuestionV2.reply")((input: ReplyInput) =>
|
|
113
|
+
Effect.uninterruptible(
|
|
114
|
+
Effect.gen(function* () {
|
|
115
|
+
const existing = pending.get(input.requestID)
|
|
116
|
+
if (!existing) return yield* new NotFoundError({ requestID: input.requestID })
|
|
117
|
+
yield* events.publish(Event.Replied, {
|
|
118
|
+
sessionID: existing.request.sessionID,
|
|
119
|
+
requestID: existing.request.id,
|
|
120
|
+
answers: input.answers.map((answer) => [...answer]),
|
|
121
|
+
})
|
|
122
|
+
yield* Deferred.succeed(existing.deferred, input.answers)
|
|
123
|
+
pending.delete(input.requestID)
|
|
124
|
+
}),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const reject = Effect.fn("QuestionV2.reject")((requestID: ID) =>
|
|
129
|
+
Effect.uninterruptible(
|
|
130
|
+
Effect.gen(function* () {
|
|
131
|
+
const existing = pending.get(requestID)
|
|
132
|
+
if (!existing) return yield* new NotFoundError({ requestID })
|
|
133
|
+
yield* events.publish(Event.Rejected, {
|
|
134
|
+
sessionID: existing.request.sessionID,
|
|
135
|
+
requestID: existing.request.id,
|
|
136
|
+
})
|
|
137
|
+
yield* Deferred.fail(existing.deferred, new RejectedError())
|
|
138
|
+
pending.delete(requestID)
|
|
139
|
+
}),
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
const list = Effect.fn("QuestionV2.list")(function* () {
|
|
144
|
+
return Array.from(pending.values(), (item) => item.request)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
return Service.of({ ask, reply, reject, list })
|
|
148
|
+
}),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
export const locationLayer = layer
|
|
152
|
+
|
|
153
|
+
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node] })
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export * as ReferenceGuidance from "./guidance"
|
|
2
|
+
|
|
3
|
+
import { makeLocationNode } from "../effect/app-node"
|
|
4
|
+
import { Context, Effect, Layer, Schema } from "effect"
|
|
5
|
+
import { Reference } from "../reference"
|
|
6
|
+
import { SystemContext } from "../system-context/index"
|
|
7
|
+
|
|
8
|
+
const Summary = Schema.Struct({
|
|
9
|
+
name: Schema.String,
|
|
10
|
+
path: Schema.String,
|
|
11
|
+
description: Schema.String.pipe(Schema.optional),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const render = (references: ReadonlyArray<typeof Summary.Type>) =>
|
|
15
|
+
[
|
|
16
|
+
"Project references provide additional directories that can be accessed when relevant.",
|
|
17
|
+
"<available_references>",
|
|
18
|
+
...references.flatMap((reference) => [
|
|
19
|
+
" <reference>",
|
|
20
|
+
` <name>${reference.name}</name>`,
|
|
21
|
+
` <path>${reference.path}</path>`,
|
|
22
|
+
...(reference.description === undefined ? [] : [` <description>${reference.description}</description>`]),
|
|
23
|
+
" </reference>",
|
|
24
|
+
]),
|
|
25
|
+
"</available_references>",
|
|
26
|
+
].join("\n")
|
|
27
|
+
|
|
28
|
+
export interface Interface {
|
|
29
|
+
readonly load: () => Effect.Effect<SystemContext.SystemContext>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/ReferenceGuidance") {}
|
|
33
|
+
|
|
34
|
+
const layer = Layer.effect(
|
|
35
|
+
Service,
|
|
36
|
+
Effect.gen(function* () {
|
|
37
|
+
const references = yield* Reference.Service
|
|
38
|
+
|
|
39
|
+
return Service.of({
|
|
40
|
+
load: Effect.fn("ReferenceGuidance.load")(function* () {
|
|
41
|
+
const available = (yield* references.list())
|
|
42
|
+
.filter((reference) => reference.description !== undefined)
|
|
43
|
+
.map((reference) => ({
|
|
44
|
+
name: reference.name,
|
|
45
|
+
path: reference.path,
|
|
46
|
+
description: reference.description,
|
|
47
|
+
}))
|
|
48
|
+
.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
49
|
+
if (available.length === 0) return SystemContext.empty
|
|
50
|
+
return SystemContext.make({
|
|
51
|
+
key: SystemContext.Key.make("core/reference-guidance"),
|
|
52
|
+
codec: Schema.toCodecJson(Schema.Array(Summary)),
|
|
53
|
+
load: Effect.succeed(available),
|
|
54
|
+
baseline: render,
|
|
55
|
+
update: (_previous, current) =>
|
|
56
|
+
[
|
|
57
|
+
"The available project references have changed. This list supersedes the previous reference list.",
|
|
58
|
+
render(current),
|
|
59
|
+
].join("\n"),
|
|
60
|
+
removed: () => "Project reference guidance is no longer available. Do not use previously listed references.",
|
|
61
|
+
})
|
|
62
|
+
}),
|
|
63
|
+
})
|
|
64
|
+
}),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
export const locationLayer = layer
|
|
68
|
+
|
|
69
|
+
export const node = makeLocationNode({ service: Service, layer, deps: [Reference.node] })
|
package/src/reference.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export * as Reference from "./reference"
|
|
2
|
+
|
|
3
|
+
import { makeLocationNode } from "./effect/app-node"
|
|
4
|
+
import { Context, Effect, Layer, Scope, Types } from "effect"
|
|
5
|
+
import { Reference } from "@neurocode-ai/schema/reference"
|
|
6
|
+
import { Global } from "./global"
|
|
7
|
+
import { EventV2 } from "./event"
|
|
8
|
+
import { Repository } from "./repository"
|
|
9
|
+
import { RepositoryCache } from "./repository-cache"
|
|
10
|
+
import { AbsolutePath } from "./schema"
|
|
11
|
+
import { State } from "./state"
|
|
12
|
+
|
|
13
|
+
export const LocalSource = Reference.LocalSource
|
|
14
|
+
export type LocalSource = Reference.LocalSource
|
|
15
|
+
|
|
16
|
+
export const GitSource = Reference.GitSource
|
|
17
|
+
export type GitSource = Reference.GitSource
|
|
18
|
+
|
|
19
|
+
export const Source = Reference.Source
|
|
20
|
+
export type Source = Reference.Source
|
|
21
|
+
|
|
22
|
+
export const Event = Reference.Event
|
|
23
|
+
|
|
24
|
+
export const Info = Reference.Info
|
|
25
|
+
export type Info = Reference.Info
|
|
26
|
+
|
|
27
|
+
type Data = {
|
|
28
|
+
sources: Map<string, Types.DeepMutable<Source>>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type Draft = {
|
|
32
|
+
add(name: string, source: Source): void
|
|
33
|
+
remove(name: string): void
|
|
34
|
+
list(): readonly [string, Source][]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Interface extends State.Transformable<Draft> {
|
|
38
|
+
readonly list: () => Effect.Effect<Info[]>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class Service extends Context.Service<Service, Interface>()("@neurocode/v2/Reference") {}
|
|
42
|
+
|
|
43
|
+
const layer = Layer.effect(
|
|
44
|
+
Service,
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
const global = yield* Global.Service
|
|
47
|
+
const events = yield* EventV2.Service
|
|
48
|
+
const cache = yield* RepositoryCache.Service
|
|
49
|
+
const scope = yield* Scope.Scope
|
|
50
|
+
const materialized = new Map<string, Info>()
|
|
51
|
+
const state = State.create<Data, Draft>({
|
|
52
|
+
initial: () => ({ sources: new Map() }),
|
|
53
|
+
draft: (draft) => ({
|
|
54
|
+
add: (name, source) => draft.sources.set(name, source as Types.DeepMutable<Source>),
|
|
55
|
+
remove: (name) => draft.sources.delete(name),
|
|
56
|
+
list: () => Array.from(draft.sources.entries()) as [string, Source][],
|
|
57
|
+
}),
|
|
58
|
+
finalize: (draft) =>
|
|
59
|
+
Effect.gen(function* () {
|
|
60
|
+
materialized.clear()
|
|
61
|
+
for (const [name, source] of draft.list()) {
|
|
62
|
+
if (source.type === "local") {
|
|
63
|
+
materialized.set(
|
|
64
|
+
name,
|
|
65
|
+
new Info({
|
|
66
|
+
name,
|
|
67
|
+
path: source.path,
|
|
68
|
+
...(source.description === undefined ? {} : { description: source.description }),
|
|
69
|
+
...(source.hidden === undefined ? {} : { hidden: source.hidden }),
|
|
70
|
+
source,
|
|
71
|
+
}),
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
const repository = Repository.parse(source.repository)
|
|
76
|
+
if (!repository || !Repository.isRemote(repository)) continue
|
|
77
|
+
if (source.branch) {
|
|
78
|
+
try {
|
|
79
|
+
Repository.validateBranch(source.branch)
|
|
80
|
+
} catch {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
materialized.set(
|
|
85
|
+
name,
|
|
86
|
+
new Info({
|
|
87
|
+
name,
|
|
88
|
+
path: AbsolutePath.make(Repository.cachePath(global.repos, repository, source.branch)),
|
|
89
|
+
...(source.description === undefined ? {} : { description: source.description }),
|
|
90
|
+
...(source.hidden === undefined ? {} : { hidden: source.hidden }),
|
|
91
|
+
source,
|
|
92
|
+
}),
|
|
93
|
+
)
|
|
94
|
+
yield* cache.ensure({ reference: repository, branch: source.branch, refresh: true }).pipe(
|
|
95
|
+
Effect.catchCause((cause) =>
|
|
96
|
+
Effect.logWarning("failed to materialize reference", {
|
|
97
|
+
name,
|
|
98
|
+
repository: source.repository,
|
|
99
|
+
cause,
|
|
100
|
+
}),
|
|
101
|
+
),
|
|
102
|
+
Effect.forkIn(scope),
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
yield* events.publish(Event.Updated, {})
|
|
106
|
+
}),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return Service.of({
|
|
110
|
+
transform: state.transform,
|
|
111
|
+
reload: state.reload,
|
|
112
|
+
list: Effect.fn("Reference.list")(function* () {
|
|
113
|
+
return Array.from(materialized.values())
|
|
114
|
+
}),
|
|
115
|
+
})
|
|
116
|
+
}),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
export const locationLayer = layer
|
|
120
|
+
|
|
121
|
+
export const node = makeLocationNode({
|
|
122
|
+
service: Service,
|
|
123
|
+
layer,
|
|
124
|
+
deps: [Global.node, EventV2.node, RepositoryCache.node],
|
|
125
|
+
})
|