@ultrakit/ultrakit 0.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/README.md +95 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5991 -0
- package/dist/template/.opencode/.env.example +196 -0
- package/dist/template/.opencode/.template-manifest.json +718 -0
- package/dist/template/.opencode/.version +1 -0
- package/dist/template/.opencode/AGENTS.md +573 -0
- package/dist/template/.opencode/AGENT_ALIGNMENT.md +564 -0
- package/dist/template/.opencode/README.md +79 -0
- package/dist/template/.opencode/agent/build.md +447 -0
- package/dist/template/.opencode/agent/explore.md +114 -0
- package/dist/template/.opencode/agent/general.md +201 -0
- package/dist/template/.opencode/agent/painter.md +83 -0
- package/dist/template/.opencode/agent/plan.md +469 -0
- package/dist/template/.opencode/agent/review.md +235 -0
- package/dist/template/.opencode/agent/scout.md +142 -0
- package/dist/template/.opencode/agent/vision.md +169 -0
- package/dist/template/.opencode/command/compound.md +240 -0
- package/dist/template/.opencode/command/create.md +298 -0
- package/dist/template/.opencode/command/curate.md +299 -0
- package/dist/template/.opencode/command/design.md +130 -0
- package/dist/template/.opencode/command/explore.md +170 -0
- package/dist/template/.opencode/command/handoff.md +149 -0
- package/dist/template/.opencode/command/health.md +356 -0
- package/dist/template/.opencode/command/init-context.md +297 -0
- package/dist/template/.opencode/command/init-user.md +125 -0
- package/dist/template/.opencode/command/init.md +115 -0
- package/dist/template/.opencode/command/iterate.md +200 -0
- package/dist/template/.opencode/command/lfg.md +173 -0
- package/dist/template/.opencode/command/plan.md +436 -0
- package/dist/template/.opencode/command/pr.md +166 -0
- package/dist/template/.opencode/command/research.md +128 -0
- package/dist/template/.opencode/command/resume.md +78 -0
- package/dist/template/.opencode/command/review-codebase.md +135 -0
- package/dist/template/.opencode/command/ship.md +390 -0
- package/dist/template/.opencode/command/status.md +126 -0
- package/dist/template/.opencode/command/ui-review.md +111 -0
- package/dist/template/.opencode/command/ui-slop-check.md +169 -0
- package/dist/template/.opencode/command/verify.md +173 -0
- package/dist/template/.opencode/context/README.md +29 -0
- package/dist/template/.opencode/context/git-context.md +32 -0
- package/dist/template/.opencode/dcp-prompts/defaults/README.md +40 -0
- package/dist/template/.opencode/dcp-prompts/defaults/compress-message.md +42 -0
- package/dist/template/.opencode/dcp-prompts/defaults/compress-range.md +59 -0
- package/dist/template/.opencode/dcp-prompts/defaults/context-limit-nudge.md +15 -0
- package/dist/template/.opencode/dcp-prompts/defaults/iteration-nudge.md +3 -0
- package/dist/template/.opencode/dcp-prompts/defaults/system.md +31 -0
- package/dist/template/.opencode/dcp-prompts/defaults/turn-nudge.md +7 -0
- package/dist/template/.opencode/dcp-prompts/overrides/compress-message.md +71 -0
- package/dist/template/.opencode/dcp.jsonc +115 -0
- package/dist/template/.opencode/memory/README.md +89 -0
- package/dist/template/.opencode/memory/_templates/design.md +59 -0
- package/dist/template/.opencode/memory/_templates/prd.md +203 -0
- package/dist/template/.opencode/memory/_templates/project.md +58 -0
- package/dist/template/.opencode/memory/_templates/proposal.md +38 -0
- package/dist/template/.opencode/memory/_templates/roadmap.md +93 -0
- package/dist/template/.opencode/memory/_templates/state.md +89 -0
- package/dist/template/.opencode/memory/_templates/tasks.md +198 -0
- package/dist/template/.opencode/memory/_templates/tech-stack.md +85 -0
- package/dist/template/.opencode/memory/_templates/user.md +26 -0
- package/dist/template/.opencode/memory/project/gotchas.md +67 -0
- package/dist/template/.opencode/memory/project/project.md +92 -0
- package/dist/template/.opencode/memory/project/roadmap.md +142 -0
- package/dist/template/.opencode/memory/project/state.md +84 -0
- package/dist/template/.opencode/memory/project/tech-stack.md +53 -0
- package/dist/template/.opencode/memory/project/user.md +45 -0
- package/dist/template/.opencode/memory/research/benchmark-framework.md +162 -0
- package/dist/template/.opencode/memory/research/ccpm-analysis.md +334 -0
- package/dist/template/.opencode/memory/research/context-management-analysis.md +685 -0
- package/dist/template/.opencode/memory/research/effectiveness-audit.md +213 -0
- package/dist/template/.opencode/memory/research/opencode-mcp-bug-report.md +129 -0
- package/dist/template/.opencode/memory/research/openspec-analysis.md +226 -0
- package/dist/template/.opencode/memory/session-context.md +40 -0
- package/dist/template/.opencode/opencode.json +1148 -0
- package/dist/template/.opencode/opencodex-fast.jsonc +3 -0
- package/dist/template/.opencode/package.json +21 -0
- package/dist/template/.opencode/plugin/README.md +81 -0
- package/dist/template/.opencode/plugin/copilot-auth.ts +1285 -0
- package/dist/template/.opencode/plugin/lib/capture.ts +177 -0
- package/dist/template/.opencode/plugin/lib/compact.ts +194 -0
- package/dist/template/.opencode/plugin/lib/compile.ts +253 -0
- package/dist/template/.opencode/plugin/lib/context.ts +198 -0
- package/dist/template/.opencode/plugin/lib/curator.ts +234 -0
- package/dist/template/.opencode/plugin/lib/db/graph.ts +253 -0
- package/dist/template/.opencode/plugin/lib/db/maintenance.ts +312 -0
- package/dist/template/.opencode/plugin/lib/db/observations.ts +304 -0
- package/dist/template/.opencode/plugin/lib/db/pipeline.ts +520 -0
- package/dist/template/.opencode/plugin/lib/db/schema.ts +687 -0
- package/dist/template/.opencode/plugin/lib/db/types.ts +284 -0
- package/dist/template/.opencode/plugin/lib/distill.ts +376 -0
- package/dist/template/.opencode/plugin/lib/index-generator.ts +170 -0
- package/dist/template/.opencode/plugin/lib/inject.ts +126 -0
- package/dist/template/.opencode/plugin/lib/lint.ts +359 -0
- package/dist/template/.opencode/plugin/lib/memory-admin-tools.ts +277 -0
- package/dist/template/.opencode/plugin/lib/memory-db.ts +100 -0
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +141 -0
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +399 -0
- package/dist/template/.opencode/plugin/lib/memory-tools.ts +535 -0
- package/dist/template/.opencode/plugin/lib/notify.ts +89 -0
- package/dist/template/.opencode/plugin/lib/operation-log.ts +110 -0
- package/dist/template/.opencode/plugin/lib/validate.ts +243 -0
- package/dist/template/.opencode/plugin/memory.ts +90 -0
- package/dist/template/.opencode/plugin/package.json +7 -0
- package/dist/template/.opencode/plugin/prompt-leverage.ts +191 -0
- package/dist/template/.opencode/plugin/rtk.ts +43 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +172 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-api-types.ts +72 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-chat-language-model.ts +835 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-chat-options.ts +30 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +48 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-prepare-tools.ts +92 -0
- package/dist/template/.opencode/plugin/sdk/copilot/copilot-provider.ts +101 -0
- package/dist/template/.opencode/plugin/sdk/copilot/index.ts +5 -0
- package/dist/template/.opencode/plugin/sdk/copilot/openai-compatible-error.ts +30 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-config.ts +18 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-error.ts +22 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-language-model.ts +1770 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/file-search.ts +127 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/image-generation.ts +114 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/local-shell.ts +64 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search.ts +102 -0
- package/dist/template/.opencode/plugin/sessions.ts +457 -0
- package/dist/template/.opencode/plugin/skill-mcp.ts +618 -0
- package/dist/template/.opencode/plugin/tsconfig.json +16 -0
- package/dist/template/.opencode/skill/accessibility-audit/SKILL.md +191 -0
- package/dist/template/.opencode/skill/agent-evals/SKILL.md +208 -0
- package/dist/template/.opencode/skill/agent-teams/SKILL.md +268 -0
- package/dist/template/.opencode/skill/anti-ai-slop/SKILL.md +76 -0
- package/dist/template/.opencode/skill/api-and-interface-design/SKILL.md +162 -0
- package/dist/template/.opencode/skill/augment-context-engine/SKILL.md +122 -0
- package/dist/template/.opencode/skill/augment-context-engine/mcp.json +6 -0
- package/dist/template/.opencode/skill/beads/SKILL.md +182 -0
- package/dist/template/.opencode/skill/beads/references/BEST_PRACTICES.md +27 -0
- package/dist/template/.opencode/skill/beads/references/BOUNDARIES.md +219 -0
- package/dist/template/.opencode/skill/beads/references/DEPENDENCIES.md +124 -0
- package/dist/template/.opencode/skill/beads/references/EXAMPLES.md +45 -0
- package/dist/template/.opencode/skill/beads/references/FILE_CLAIMING.md +101 -0
- package/dist/template/.opencode/skill/beads/references/GIT_SYNC.md +25 -0
- package/dist/template/.opencode/skill/beads/references/HIERARCHY.md +71 -0
- package/dist/template/.opencode/skill/beads/references/MULTI_AGENT.md +40 -0
- package/dist/template/.opencode/skill/beads/references/RESUMABILITY.md +177 -0
- package/dist/template/.opencode/skill/beads/references/SESSION_PROTOCOL.md +61 -0
- package/dist/template/.opencode/skill/beads/references/TASK_CREATION.md +38 -0
- package/dist/template/.opencode/skill/beads/references/TROUBLESHOOTING.md +38 -0
- package/dist/template/.opencode/skill/beads/references/WORKFLOWS.md +226 -0
- package/dist/template/.opencode/skill/brainstorming/SKILL.md +114 -0
- package/dist/template/.opencode/skill/brand-asset-protocol/SKILL.md +222 -0
- package/dist/template/.opencode/skill/chrome-devtools/SKILL.md +76 -0
- package/dist/template/.opencode/skill/chrome-devtools/mcp.json +19 -0
- package/dist/template/.opencode/skill/ci-cd-and-automation/SKILL.md +202 -0
- package/dist/template/.opencode/skill/cloudflare/SKILL.md +253 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/README.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/api.md +100 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/configuration.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/gotchas.md +59 -0
- package/dist/template/.opencode/skill/cloudflare/references/agents-sdk/patterns.md +89 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-gateway/README.md +695 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/api.md +38 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/configuration.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/gotchas.md +41 -0
- package/dist/template/.opencode/skill/cloudflare/references/ai-search/patterns.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/api.md +27 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/configuration.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/gotchas.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/analytics-engine/patterns.md +36 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/README.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/api.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/configuration.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/gotchas.md +28 -0
- package/dist/template/.opencode/skill/cloudflare/references/api/patterns.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/README.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/api.md +78 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/configuration.md +128 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/gotchas.md +51 -0
- package/dist/template/.opencode/skill/cloudflare/references/api-shield/patterns.md +145 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/api.md +50 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/configuration.md +53 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/gotchas.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/argo-smart-routing/patterns.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/configuration.md +58 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/gotchas.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/bindings/patterns.md +37 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/README.md +71 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/api.md +168 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/configuration.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/gotchas.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/bot-management/patterns.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/api.md +54 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/configuration.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/gotchas.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/browser-rendering/patterns.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/c3/README.md +264 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/README.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/api.md +176 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/configuration.md +164 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/gotchas.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/cache-reserve/patterns.md +180 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/api.md +43 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/configuration.md +56 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/gotchas.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/containers/patterns.md +40 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/README.md +85 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/api.md +198 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/configuration.md +151 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/gotchas.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/cron-triggers/patterns.md +122 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/README.md +92 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/api.md +141 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/gotchas.md +70 -0
- package/dist/template/.opencode/skill/cloudflare/references/d1/patterns.md +144 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/README.md +34 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/api.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/configuration.md +67 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/gotchas.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/ddos/patterns.md +158 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/api.md +89 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/configuration.md +116 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/gotchas.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/do-storage/patterns.md +112 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/README.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/api.md +152 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/configuration.md +148 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/gotchas.md +158 -0
- package/dist/template/.opencode/skill/cloudflare/references/durable-objects/patterns.md +255 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/api.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/configuration.md +63 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/gotchas.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-routing/patterns.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/email-workers/README.md +598 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/api.md +137 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/configuration.md +133 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/gotchas.md +184 -0
- package/dist/template/.opencode/skill/cloudflare/references/hyperdrive/patterns.md +176 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/configuration.md +45 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/gotchas.md +23 -0
- package/dist/template/.opencode/skill/cloudflare/references/images/patterns.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/README.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/api.md +114 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/configuration.md +92 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/gotchas.md +117 -0
- package/dist/template/.opencode/skill/cloudflare/references/kv/patterns.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/README.md +64 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/api.md +144 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/configuration.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/gotchas.md +187 -0
- package/dist/template/.opencode/skill/cloudflare/references/miniflare/patterns.md +211 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/README.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/api.md +240 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/gotchas.md +171 -0
- package/dist/template/.opencode/skill/cloudflare/references/network-interconnect/patterns.md +171 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/api.md +51 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/configuration.md +60 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/gotchas.md +36 -0
- package/dist/template/.opencode/skill/cloudflare/references/observability/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/README.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/api.md +200 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/configuration.md +228 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/gotchas.md +161 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages/patterns.md +145 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/README.md +57 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/api.md +201 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/configuration.md +159 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/gotchas.md +151 -0
- package/dist/template/.opencode/skill/cloudflare/references/pages-functions/patterns.md +190 -0
- package/dist/template/.opencode/skill/cloudflare/references/pipelines/README.md +664 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/README.md +107 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/api.md +194 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/configuration.md +216 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/gotchas.md +223 -0
- package/dist/template/.opencode/skill/cloudflare/references/pulumi/patterns.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/README.md +69 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/api.md +138 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/configuration.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/gotchas.md +112 -0
- package/dist/template/.opencode/skill/cloudflare/references/queues/patterns.md +155 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/README.md +61 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/api.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/configuration.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/gotchas.md +94 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2/patterns.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/README.md +18 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/api.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/configuration.md +39 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/gotchas.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-data-catalog/patterns.md +46 -0
- package/dist/template/.opencode/skill/cloudflare/references/r2-sql/README.md +512 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/README.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/api.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/configuration.md +63 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/gotchas.md +75 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtime-sfu/patterns.md +102 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/README.md +81 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/api.md +164 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/configuration.md +147 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/gotchas.md +172 -0
- package/dist/template/.opencode/skill/cloudflare/references/realtimekit/patterns.md +155 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/README.md +90 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/api.md +178 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/configuration.md +131 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/gotchas.md +156 -0
- package/dist/template/.opencode/skill/cloudflare/references/sandbox/patterns.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/README.md +58 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/api.md +182 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/configuration.md +140 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/gotchas.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/secrets-store/patterns.md +218 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/README.md +91 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/api.md +139 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/configuration.md +129 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/gotchas.md +87 -0
- package/dist/template/.opencode/skill/cloudflare/references/smart-placement/patterns.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/README.md +15 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/api.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/configuration.md +33 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/gotchas.md +21 -0
- package/dist/template/.opencode/skill/cloudflare/references/snippets/patterns.md +34 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/api.md +24 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/configuration.md +43 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/gotchas.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/spectrum/patterns.md +40 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/configuration.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/gotchas.md +44 -0
- package/dist/template/.opencode/skill/cloudflare/references/static-assets/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/README.md +103 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/api.md +204 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/configuration.md +127 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/gotchas.md +131 -0
- package/dist/template/.opencode/skill/cloudflare/references/stream/patterns.md +152 -0
- package/dist/template/.opencode/skill/cloudflare/references/tail-workers/README.md +640 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/README.md +76 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/api.md +159 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/configuration.md +156 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/gotchas.md +207 -0
- package/dist/template/.opencode/skill/cloudflare/references/terraform/patterns.md +135 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/README.md +82 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/api.md +105 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/configuration.md +113 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/gotchas.md +115 -0
- package/dist/template/.opencode/skill/cloudflare/references/tunnel/patterns.md +157 -0
- package/dist/template/.opencode/skill/cloudflare/references/turn/README.md +699 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/configuration.md +19 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/gotchas.md +27 -0
- package/dist/template/.opencode/skill/cloudflare/references/turnstile/patterns.md +41 -0
- package/dist/template/.opencode/skill/cloudflare/references/vectorize/README.md +682 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/README.md +14 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/api.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/configuration.md +44 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/gotchas.md +24 -0
- package/dist/template/.opencode/skill/cloudflare/references/waf/patterns.md +29 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/README.md +19 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/api.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/configuration.md +31 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/gotchas.md +28 -0
- package/dist/template/.opencode/skill/cloudflare/references/web-analytics/patterns.md +52 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/README.md +47 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/api.md +199 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/configuration.md +185 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/gotchas.md +203 -0
- package/dist/template/.opencode/skill/cloudflare/references/workerd/patterns.md +216 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/README.md +96 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/api.md +137 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/configuration.md +147 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/gotchas.md +99 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers/patterns.md +149 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-ai/README.md +116 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/README.md +48 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/api.md +169 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/configuration.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/gotchas.md +130 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-for-platforms/patterns.md +170 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/README.md +16 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/api.md +20 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/configuration.md +3 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/gotchas.md +35 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-playground/patterns.md +42 -0
- package/dist/template/.opencode/skill/cloudflare/references/workers-vpc/README.md +579 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/README.md +62 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/api.md +125 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/configuration.md +177 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/gotchas.md +136 -0
- package/dist/template/.opencode/skill/cloudflare/references/workflows/patterns.md +132 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/README.md +90 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/api.md +140 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/configuration.md +128 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/gotchas.md +93 -0
- package/dist/template/.opencode/skill/cloudflare/references/wrangler/patterns.md +150 -0
- package/dist/template/.opencode/skill/cloudflare/references/zaraz/README.md +360 -0
- package/dist/template/.opencode/skill/code-search-patterns/SKILL.md +253 -0
- package/dist/template/.opencode/skill/code-simplification/SKILL.md +211 -0
- package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +135 -0
- package/dist/template/.opencode/skill/condition-based-waiting/example.ts +158 -0
- package/dist/template/.opencode/skill/context-condensation/SKILL.md +149 -0
- package/dist/template/.opencode/skill/context-engineering/SKILL.md +176 -0
- package/dist/template/.opencode/skill/context-initialization/SKILL.md +69 -0
- package/dist/template/.opencode/skill/context-management/SKILL.md +390 -0
- package/dist/template/.opencode/skill/core-data-expert/SKILL.md +93 -0
- package/dist/template/.opencode/skill/core-data-expert/references/batch-operations.md +543 -0
- package/dist/template/.opencode/skill/core-data-expert/references/cloudkit-integration.md +259 -0
- package/dist/template/.opencode/skill/core-data-expert/references/concurrency.md +522 -0
- package/dist/template/.opencode/skill/core-data-expert/references/fetch-requests.md +643 -0
- package/dist/template/.opencode/skill/core-data-expert/references/glossary.md +233 -0
- package/dist/template/.opencode/skill/core-data-expert/references/migration.md +393 -0
- package/dist/template/.opencode/skill/core-data-expert/references/model-configuration.md +597 -0
- package/dist/template/.opencode/skill/core-data-expert/references/performance.md +300 -0
- package/dist/template/.opencode/skill/core-data-expert/references/persistent-history.md +553 -0
- package/dist/template/.opencode/skill/core-data-expert/references/project-audit.md +60 -0
- package/dist/template/.opencode/skill/core-data-expert/references/saving.md +574 -0
- package/dist/template/.opencode/skill/core-data-expert/references/stack-setup.md +625 -0
- package/dist/template/.opencode/skill/core-data-expert/references/testing.md +300 -0
- package/dist/template/.opencode/skill/core-data-expert/references/threading.md +589 -0
- package/dist/template/.opencode/skill/deep-research/SKILL.md +384 -0
- package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +176 -0
- package/dist/template/.opencode/skill/deprecation-and-migration/SKILL.md +189 -0
- package/dist/template/.opencode/skill/design-direction-advisor/SKILL.md +139 -0
- package/dist/template/.opencode/skill/design-system-audit/SKILL.md +153 -0
- package/dist/template/.opencode/skill/design-taste-frontend/SKILL.md +238 -0
- package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +320 -0
- package/dist/template/.opencode/skill/dispatching-parallel-agents/SKILL.md +191 -0
- package/dist/template/.opencode/skill/documentation-and-adrs/SKILL.md +220 -0
- package/dist/template/.opencode/skill/executing-plans/SKILL.md +247 -0
- package/dist/template/.opencode/skill/figma/SKILL.md +224 -0
- package/dist/template/.opencode/skill/figma-go/SKILL.md +65 -0
- package/dist/template/.opencode/skill/finishing-a-development-branch/SKILL.md +357 -0
- package/dist/template/.opencode/skill/frontend-design/SKILL.md +235 -0
- package/dist/template/.opencode/skill/frontend-design/references/animation/motion-advanced.md +224 -0
- package/dist/template/.opencode/skill/frontend-design/references/animation/motion-core.md +181 -0
- package/dist/template/.opencode/skill/frontend-design/references/canvas/execution.md +90 -0
- package/dist/template/.opencode/skill/frontend-design/references/canvas/philosophy.md +94 -0
- package/dist/template/.opencode/skill/frontend-design/references/design/color-system.md +111 -0
- package/dist/template/.opencode/skill/frontend-design/references/design/interaction.md +149 -0
- package/dist/template/.opencode/skill/frontend-design/references/design/typography-rules.md +106 -0
- package/dist/template/.opencode/skill/frontend-design/references/design/ux-writing.md +99 -0
- package/dist/template/.opencode/skill/frontend-design/references/shadcn/accessibility.md +132 -0
- package/dist/template/.opencode/skill/frontend-design/references/shadcn/core-components.md +153 -0
- package/dist/template/.opencode/skill/frontend-design/references/shadcn/form-components.md +158 -0
- package/dist/template/.opencode/skill/frontend-design/references/shadcn/setup.md +69 -0
- package/dist/template/.opencode/skill/frontend-design/references/shadcn/theming.md +152 -0
- package/dist/template/.opencode/skill/frontend-design/references/tailwind/responsive.md +112 -0
- package/dist/template/.opencode/skill/frontend-design/references/tailwind/utilities-layout.md +134 -0
- package/dist/template/.opencode/skill/frontend-design/references/tailwind/utilities-styling.md +165 -0
- package/dist/template/.opencode/skill/frontend-design/references/tailwind/v4-config.md +147 -0
- package/dist/template/.opencode/skill/frontend-design/references/tailwind/v4-features.md +128 -0
- package/dist/template/.opencode/skill/full-output-enforcement/SKILL.md +62 -0
- package/dist/template/.opencode/skill/gemini-large-context/SKILL.md +216 -0
- package/dist/template/.opencode/skill/gh-address-comments/SKILL.md +29 -0
- package/dist/template/.opencode/skill/gh-address-comments/scripts/fetch_comments.py +237 -0
- package/dist/template/.opencode/skill/gh-fix-ci/SKILL.md +38 -0
- package/dist/template/.opencode/skill/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- package/dist/template/.opencode/skill/hi-fi-prototype-html/SKILL.md +253 -0
- package/dist/template/.opencode/skill/high-end-visual-design/SKILL.md +111 -0
- package/dist/template/.opencode/skill/html-deck-export/SKILL.md +189 -0
- package/dist/template/.opencode/skill/incremental-implementation/SKILL.md +191 -0
- package/dist/template/.opencode/skill/index-knowledge/SKILL.md +413 -0
- package/dist/template/.opencode/skill/industrial-brutalist-ui/SKILL.md +105 -0
- package/dist/template/.opencode/skill/jira/SKILL.md +283 -0
- package/dist/template/.opencode/skill/jira/mcp.json +6 -0
- package/dist/template/.opencode/skill/memory-grounding/SKILL.md +68 -0
- package/dist/template/.opencode/skill/memory-system/SKILL.md +148 -0
- package/dist/template/.opencode/skill/minimalist-ui/SKILL.md +98 -0
- package/dist/template/.opencode/skill/mockup-to-code/SKILL.md +184 -0
- package/dist/template/.opencode/skill/opensrc/SKILL.md +284 -0
- package/dist/template/.opencode/skill/opensrc/references/architecture.md +176 -0
- package/dist/template/.opencode/skill/opensrc/references/cli-usage.md +176 -0
- package/dist/template/.opencode/skill/opensrc/references/registry-support.md +137 -0
- package/dist/template/.opencode/skill/pdf-extract/SKILL.md +438 -0
- package/dist/template/.opencode/skill/performance-optimization/SKILL.md +236 -0
- package/dist/template/.opencode/skill/playwright/SKILL.md +381 -0
- package/dist/template/.opencode/skill/playwright/mcp.json +16 -0
- package/dist/template/.opencode/skill/playwright/references/agent-browser-cli.md +405 -0
- package/dist/template/.opencode/skill/playwriter/SKILL.md +158 -0
- package/dist/template/.opencode/skill/polar/SKILL.md +102 -0
- package/dist/template/.opencode/skill/portless/SKILL.md +109 -0
- package/dist/template/.opencode/skill/prd/SKILL.md +146 -0
- package/dist/template/.opencode/skill/prd-task/SKILL.md +182 -0
- package/dist/template/.opencode/skill/prd-task/references/prd-schema.json +124 -0
- package/dist/template/.opencode/skill/prompt-leverage/SKILL.md +90 -0
- package/dist/template/.opencode/skill/prompt-leverage/references/framework.md +91 -0
- package/dist/template/.opencode/skill/prompt-leverage/scripts/augment_prompt.py +157 -0
- package/dist/template/.opencode/skill/react-best-practices/AGENTS.md +2410 -0
- package/dist/template/.opencode/skill/react-best-practices/README.md +123 -0
- package/dist/template/.opencode/skill/react-best-practices/SKILL.md +133 -0
- package/dist/template/.opencode/skill/react-best-practices/metadata.json +15 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/_sections.md +46 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/_template.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-defer-await.md +80 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-dependencies.md +36 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-parallel.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/template/.opencode/skill/react-best-practices/rules/server-serialization.md +38 -0
- package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +263 -0
- package/dist/template/.opencode/skill/reconcile/SKILL.md +183 -0
- package/dist/template/.opencode/skill/redesign-existing-projects/SKILL.md +191 -0
- package/dist/template/.opencode/skill/reflection-checkpoints/SKILL.md +183 -0
- package/dist/template/.opencode/skill/requesting-code-review/SKILL.md +443 -0
- package/dist/template/.opencode/skill/requesting-code-review/references/specialist-profiles.md +108 -0
- package/dist/template/.opencode/skill/requesting-code-review/review.md +160 -0
- package/dist/template/.opencode/skill/resend/SKILL.md +177 -0
- package/dist/template/.opencode/skill/resend/references/react-email.md +287 -0
- package/dist/template/.opencode/skill/resend/references/receive-email.md +248 -0
- package/dist/template/.opencode/skill/resend/references/send-email.md +318 -0
- package/dist/template/.opencode/skill/root-cause-tracing/SKILL.md +192 -0
- package/dist/template/.opencode/skill/root-cause-tracing/find-polluter.sh +63 -0
- package/dist/template/.opencode/skill/rtk-command-compression/SKILL.md +134 -0
- package/dist/template/.opencode/skill/screenshot/SKILL.md +48 -0
- package/dist/template/.opencode/skill/screenshot/scripts/ensure_macos_permissions.sh +54 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_display_info.swift +22 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_permissions.swift +40 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_window_info.swift +126 -0
- package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.ps1 +163 -0
- package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.py +585 -0
- package/dist/template/.opencode/skill/security-and-hardening/SKILL.md +296 -0
- package/dist/template/.opencode/skill/security-threat-model/SKILL.md +36 -0
- package/dist/template/.opencode/skill/security-threat-model/references/prompt-template.md +255 -0
- package/dist/template/.opencode/skill/security-threat-model/references/security-controls-and-assets.md +32 -0
- package/dist/template/.opencode/skill/sharing-skills/SKILL.md +214 -0
- package/dist/template/.opencode/skill/skill-creator/SKILL.md +181 -0
- package/dist/template/.opencode/skill/skill-installer/SKILL.md +58 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/github_utils.py +21 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/install-skill-from-github.py +313 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/list-skills.py +106 -0
- package/dist/template/.opencode/skill/structured-edit/SKILL.md +191 -0
- package/dist/template/.opencode/skill/subagent-driven-development/SKILL.md +237 -0
- package/dist/template/.opencode/skill/supabase/SKILL.md +130 -0
- package/dist/template/.opencode/skill/supabase/mcp.json +27 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/AGENTS.md +1490 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/SKILL.md +65 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/dist/template/.opencode/skill/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +244 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/architecture.md +39 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/delegation-worker-protocol.md +145 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/dependency-graph.md +50 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/drift-check.md +90 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/integration-beads.md +20 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/launch-flow.md +186 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/reconciler.md +172 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/tier-enforcement.md +78 -0
- package/dist/template/.opencode/skill/swarm-coordination/references/tmux-integration.md +134 -0
- package/dist/template/.opencode/skill/swift-concurrency/SKILL.md +266 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/actors.md +640 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-algorithms.md +822 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-await-basics.md +249 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-sequences.md +670 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/core-data.md +533 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/glossary.md +128 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/linting.md +142 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/memory-management.md +542 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/migration.md +1076 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/performance.md +574 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/sendable.md +578 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/tasks.md +604 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/testing.md +565 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/threading.md +452 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/SKILL.md +329 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-advanced.md +351 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/image-optimization.md +286 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/layout-best-practices.md +312 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/liquid-glass.md +377 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/list-patterns.md +153 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/modern-apis.md +400 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/performance-patterns.md +377 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/scroll-patterns.md +305 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/state-management.md +447 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/text-formatting.md +285 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/view-structure.md +276 -0
- package/dist/template/.opencode/skill/systematic-debugging/SKILL.md +402 -0
- package/dist/template/.opencode/skill/terse-output-mode/SKILL.md +95 -0
- package/dist/template/.opencode/skill/test-driven-development/SKILL.md +403 -0
- package/dist/template/.opencode/skill/testing-anti-patterns/SKILL.md +333 -0
- package/dist/template/.opencode/skill/think-in-code/SKILL.md +136 -0
- package/dist/template/.opencode/skill/using-git-worktrees/SKILL.md +259 -0
- package/dist/template/.opencode/skill/ux-quality-gates/SKILL.md +137 -0
- package/dist/template/.opencode/skill/v0/SKILL.md +158 -0
- package/dist/template/.opencode/skill/v1-run/SKILL.md +175 -0
- package/dist/template/.opencode/skill/v1-run/mcp.json +6 -0
- package/dist/template/.opencode/skill/vercel-deploy-claimable/SKILL.md +124 -0
- package/dist/template/.opencode/skill/vercel-deploy-claimable/scripts/deploy.sh +249 -0
- package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +357 -0
- package/dist/template/.opencode/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +171 -0
- package/dist/template/.opencode/skill/verification-gates/SKILL.md +63 -0
- package/dist/template/.opencode/skill/visual-analysis/SKILL.md +154 -0
- package/dist/template/.opencode/skill/web-design-guidelines/SKILL.md +46 -0
- package/dist/template/.opencode/skill/webclaw/SKILL.md +155 -0
- package/dist/template/.opencode/skill/workspace-setup/SKILL.md +76 -0
- package/dist/template/.opencode/skill/writing-plans/SKILL.md +320 -0
- package/dist/template/.opencode/skill/writing-skills/SKILL.md +324 -0
- package/dist/template/.opencode/skill/writing-skills/anthropic-best-practices.md +1173 -0
- package/dist/template/.opencode/skill/writing-skills/graphviz-conventions.dot +172 -0
- package/dist/template/.opencode/skill/writing-skills/persuasion-principles.md +220 -0
- package/dist/template/.opencode/skill/writing-skills/references/anti-patterns.md +25 -0
- package/dist/template/.opencode/skill/writing-skills/references/claude-search-optimization.md +140 -0
- package/dist/template/.opencode/skill/writing-skills/references/discovery-workflow.md +11 -0
- package/dist/template/.opencode/skill/writing-skills/references/file-organization.md +32 -0
- package/dist/template/.opencode/skill/writing-skills/references/flowcharts-and-examples.md +57 -0
- package/dist/template/.opencode/skill/writing-skills/references/rationalization-hardening.md +75 -0
- package/dist/template/.opencode/skill/writing-skills/references/testing-methodology.md +397 -0
- package/dist/template/.opencode/skill/writing-skills/references/testing-skill-types.md +52 -0
- package/dist/template/.opencode/tool/context7.ts +191 -0
- package/dist/template/.opencode/tool/grepsearch.ts +143 -0
- package/dist/template/.opencode/tsconfig.json +21 -0
- package/dist/template/.opencode/tui.json +15 -0
- package/package.json +78 -0
|
@@ -0,0 +1,1285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot Auth Plugin
|
|
3
|
+
* Simplified auth provider without token expiration checks
|
|
4
|
+
*
|
|
5
|
+
* Claude Reasoning Support:
|
|
6
|
+
* This plugin adds `thinking_budget` to the request body for Claude models.
|
|
7
|
+
* The Copilot API accepts this parameter and returns reasoning in the response.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: Response parsing for reasoning_text/reasoning_opaque is handled by
|
|
10
|
+
* the custom SDK at .opencode/plugin/sdk/copilot/ which properly converts
|
|
11
|
+
* these fields to AI SDK's reasoning content parts.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
16
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
17
|
+
|
|
18
|
+
const CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
19
|
+
|
|
20
|
+
// Logger function that will be set by the plugin
|
|
21
|
+
let log: (
|
|
22
|
+
level: "debug" | "info" | "warn" | "error",
|
|
23
|
+
message: string,
|
|
24
|
+
extra?: Record<string, any>,
|
|
25
|
+
) => void = () => {};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set the logger function from the plugin context
|
|
29
|
+
*/
|
|
30
|
+
function setLogger(client: any) {
|
|
31
|
+
log = (level, message, extra) => {
|
|
32
|
+
client.app
|
|
33
|
+
.log({
|
|
34
|
+
service: "copilot-auth",
|
|
35
|
+
level,
|
|
36
|
+
message,
|
|
37
|
+
extra,
|
|
38
|
+
})
|
|
39
|
+
.catch(() => {}); // Fire and forget, don't block on logging
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Add a small safety buffer when polling to avoid hitting the server
|
|
44
|
+
// slightly too early due to clock skew / timer drift.
|
|
45
|
+
const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000; // 3 seconds
|
|
46
|
+
|
|
47
|
+
const HEADERS = {
|
|
48
|
+
"User-Agent": "opencode/1.3.17",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const RESPONSES_API_ALTERNATE_INPUT_TYPES = [
|
|
52
|
+
"file_search_call",
|
|
53
|
+
"computer_call",
|
|
54
|
+
"computer_call_output",
|
|
55
|
+
"web_search_call",
|
|
56
|
+
"function_call",
|
|
57
|
+
"function_call_output",
|
|
58
|
+
"image_generation_call",
|
|
59
|
+
"code_interpreter_call",
|
|
60
|
+
"local_shell_call",
|
|
61
|
+
"local_shell_call_output",
|
|
62
|
+
"mcp_list_tools",
|
|
63
|
+
"mcp_approval_request",
|
|
64
|
+
"mcp_approval_response",
|
|
65
|
+
"mcp_call",
|
|
66
|
+
"reasoning",
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Expected ID prefixes per Responses API item type.
|
|
70
|
+
// The OpenAI Responses API validates that item IDs start with specific prefixes.
|
|
71
|
+
// GitHub Copilot's backend (especially GPT models) returns non-standard prefixes
|
|
72
|
+
// (e.g., "h_" instead of "fc_") or no prefix at all that the API then rejects on replay.
|
|
73
|
+
const RESPONSES_API_EXPECTED_PREFIXES: Record<string, string> = {
|
|
74
|
+
function_call: "fc_",
|
|
75
|
+
local_shell_call: "fc_",
|
|
76
|
+
// function_call_output.call_id prefix is validated directly in sanitizeResponseInputIds
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Role-based items (role: "assistant", "user", etc.) are internally typed as "message"
|
|
80
|
+
// by the OpenAI Responses API and their IDs must start with "msg_".
|
|
81
|
+
const RESPONSES_API_ROLE_PREFIX = "msg_";
|
|
82
|
+
|
|
83
|
+
function normalizeDomain(url: string): string {
|
|
84
|
+
return url.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const DEFAULT_COPILOT_API_BASE = "https://api.githubcopilot.com";
|
|
88
|
+
|
|
89
|
+
function toCopilotMessagesBase(url: string | undefined): string {
|
|
90
|
+
const base = (url?.trim() || DEFAULT_COPILOT_API_BASE)
|
|
91
|
+
.replace(/\/+$/, "")
|
|
92
|
+
.replace(/\/v1$/, "");
|
|
93
|
+
return `${base}/v1`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isClaudeCopilotModel(modelId: string, model: any): boolean {
|
|
97
|
+
return [modelId, model?.id, model?.api?.id]
|
|
98
|
+
.filter((value): value is string => typeof value === "string")
|
|
99
|
+
.some((value) => value.toLowerCase().includes("claude"));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getUrls(domain: string) {
|
|
103
|
+
return {
|
|
104
|
+
DEVICE_CODE_URL: `https://${domain}/login/device/code`,
|
|
105
|
+
ACCESS_TOKEN_URL: `https://${domain}/login/oauth/access_token`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
110
|
+
|
|
111
|
+
function getRequestUrl(input: RequestInfo | URL): string {
|
|
112
|
+
if (typeof input === "string") return input;
|
|
113
|
+
if (input instanceof URL) return input.toString();
|
|
114
|
+
if (typeof Request !== "undefined" && input instanceof Request) {
|
|
115
|
+
return input.url;
|
|
116
|
+
}
|
|
117
|
+
return input.toString();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function extractClaudeThinkingBudget(body: any): number | undefined {
|
|
121
|
+
const candidates = [body?.thinking_budget, body?.thinking?.budget_tokens];
|
|
122
|
+
|
|
123
|
+
for (const candidate of candidates) {
|
|
124
|
+
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
|
125
|
+
const normalized = Math.trunc(candidate);
|
|
126
|
+
if (normalized > 0) return normalized;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getErrorMessage(error: unknown): string {
|
|
134
|
+
if (error instanceof Error) return error.message;
|
|
135
|
+
if (typeof error === "string") return error;
|
|
136
|
+
try {
|
|
137
|
+
return JSON.stringify(error);
|
|
138
|
+
} catch {
|
|
139
|
+
return String(error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isTransientUpstreamTimeoutError(error: unknown): boolean {
|
|
144
|
+
const message = getErrorMessage(error).toLowerCase();
|
|
145
|
+
return (
|
|
146
|
+
message.includes("upstream idle timeout") ||
|
|
147
|
+
message.includes("mid_stream") ||
|
|
148
|
+
message.includes("sse read timed out") ||
|
|
149
|
+
message.includes("socket connection was closed unexpectedly") ||
|
|
150
|
+
message.includes("connection reset") ||
|
|
151
|
+
message.includes("econnreset") ||
|
|
152
|
+
message.includes("etimedout")
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Rate limit handling configuration
|
|
157
|
+
const RATE_LIMIT_CONFIG = {
|
|
158
|
+
maxDelayMs: 60000, // Cap at 60 seconds
|
|
159
|
+
defaultCooldownMs: 60000, // Default cooldown when Retry-After header is missing
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Local request shaping to smooth bursts before they hit Copilot limits
|
|
163
|
+
const REQUEST_SHAPING_CONFIG = {
|
|
164
|
+
tokensPerSecond: 1,
|
|
165
|
+
burstCapacity: 2,
|
|
166
|
+
maxQueueDelayMs: 15000,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Per-model rate limit state (in-memory, resets on restart)
|
|
170
|
+
interface RateLimitEntry {
|
|
171
|
+
rateLimitedUntil: number; // Unix timestamp (ms)
|
|
172
|
+
}
|
|
173
|
+
const rateLimitState = new Map<string, RateLimitEntry>();
|
|
174
|
+
const familyCircuitBreakerState = new Map<string, number>();
|
|
175
|
+
|
|
176
|
+
interface TokenBucketState {
|
|
177
|
+
tokens: number;
|
|
178
|
+
lastRefillAt: number;
|
|
179
|
+
}
|
|
180
|
+
const modelTokenBuckets = new Map<string, TokenBucketState>();
|
|
181
|
+
const modelQueueTail = new Map<string, Promise<void>>();
|
|
182
|
+
|
|
183
|
+
// Model fallback chains: same-family alternatives when a model is rate-limited
|
|
184
|
+
const MODEL_FALLBACK_CHAINS: Record<string, string[]> = {
|
|
185
|
+
// Claude family
|
|
186
|
+
"claude-opus-4.6": [
|
|
187
|
+
"claude-opus-4.5",
|
|
188
|
+
"claude-sonnet-4.6",
|
|
189
|
+
"claude-sonnet-4.5",
|
|
190
|
+
],
|
|
191
|
+
"claude-opus-4.5": [
|
|
192
|
+
"claude-opus-4.6",
|
|
193
|
+
"claude-sonnet-4.5",
|
|
194
|
+
"claude-sonnet-4.6",
|
|
195
|
+
],
|
|
196
|
+
"claude-sonnet-4.6": [
|
|
197
|
+
"claude-sonnet-4.5",
|
|
198
|
+
"claude-opus-4.6",
|
|
199
|
+
"claude-opus-4.5",
|
|
200
|
+
],
|
|
201
|
+
"claude-sonnet-4.5": [
|
|
202
|
+
"claude-sonnet-4.6",
|
|
203
|
+
"claude-opus-4.5",
|
|
204
|
+
"claude-opus-4.6",
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Parse the Retry-After header from a 429 response.
|
|
210
|
+
* Returns cooldown in milliseconds, or null if header is missing/unparseable.
|
|
211
|
+
*/
|
|
212
|
+
function parseRetryAfter(response: Response): number | null {
|
|
213
|
+
const header = response.headers.get("retry-after");
|
|
214
|
+
if (!header) return null;
|
|
215
|
+
// Try as seconds first (most common)
|
|
216
|
+
const seconds = parseInt(header, 10);
|
|
217
|
+
if (!isNaN(seconds) && seconds > 0) return seconds * 1000;
|
|
218
|
+
// Try as HTTP date
|
|
219
|
+
const date = Date.parse(header);
|
|
220
|
+
if (!isNaN(date)) return Math.max(0, date - Date.now());
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function clampCooldownMs(
|
|
225
|
+
value: number | null | undefined,
|
|
226
|
+
fallbackMs = 0,
|
|
227
|
+
): number {
|
|
228
|
+
return Math.min(
|
|
229
|
+
Math.max(value ?? fallbackMs, 0),
|
|
230
|
+
RATE_LIMIT_CONFIG.maxDelayMs,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getRateLimitRemainingMs(model: string): number | null {
|
|
235
|
+
const entry = rateLimitState.get(model);
|
|
236
|
+
if (!entry) return null;
|
|
237
|
+
const remaining = entry.rateLimitedUntil - Date.now();
|
|
238
|
+
if (remaining <= 0) {
|
|
239
|
+
rateLimitState.delete(model);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return remaining;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function getModelFamily(model: string): string[] {
|
|
246
|
+
const family = new Set<string>([
|
|
247
|
+
model,
|
|
248
|
+
...(MODEL_FALLBACK_CHAINS[model] || []),
|
|
249
|
+
]);
|
|
250
|
+
return [...family];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function getFamilyCircuitKey(model: string): string {
|
|
254
|
+
return getModelFamily(model).sort().join("|");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getFamilyCircuitRemainingMs(model: string): number {
|
|
258
|
+
const key = getFamilyCircuitKey(model);
|
|
259
|
+
const until = familyCircuitBreakerState.get(key);
|
|
260
|
+
if (!until) return 0;
|
|
261
|
+
const remaining = until - Date.now();
|
|
262
|
+
if (remaining <= 0) {
|
|
263
|
+
familyCircuitBreakerState.delete(key);
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
return remaining;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function openFamilyCircuitBreaker(model: string, cooldownMs: number): void {
|
|
270
|
+
const key = getFamilyCircuitKey(model);
|
|
271
|
+
familyCircuitBreakerState.set(key, Date.now() + clampCooldownMs(cooldownMs));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getFamilyMaxCooldownRemainingMs(model: string): number {
|
|
275
|
+
let maxRemaining = 0;
|
|
276
|
+
for (const candidate of getModelFamily(model)) {
|
|
277
|
+
const remaining = getRateLimitRemainingMs(candidate) ?? 0;
|
|
278
|
+
if (remaining > maxRemaining) maxRemaining = remaining;
|
|
279
|
+
}
|
|
280
|
+
return maxRemaining;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function formatRetryAfter(seconds: number): string {
|
|
284
|
+
if (seconds < 60) return `${seconds}s`;
|
|
285
|
+
const mins = Math.floor(seconds / 60);
|
|
286
|
+
const secs = seconds % 60;
|
|
287
|
+
return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function shapeRequestForModel(model: string): Promise<void> {
|
|
291
|
+
if (!model) return;
|
|
292
|
+
|
|
293
|
+
const previousTail = modelQueueTail.get(model) ?? Promise.resolve();
|
|
294
|
+
let releaseQueue: (() => void) | undefined;
|
|
295
|
+
const currentGate = new Promise<void>((resolve) => {
|
|
296
|
+
releaseQueue = resolve;
|
|
297
|
+
});
|
|
298
|
+
const currentTail = previousTail.then(() => currentGate);
|
|
299
|
+
modelQueueTail.set(model, currentTail);
|
|
300
|
+
|
|
301
|
+
let queueTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
302
|
+
try {
|
|
303
|
+
await Promise.race([
|
|
304
|
+
previousTail,
|
|
305
|
+
new Promise<void>((_, reject) => {
|
|
306
|
+
queueTimeout = setTimeout(() => {
|
|
307
|
+
reject(
|
|
308
|
+
new Error(
|
|
309
|
+
`[Copilot] Local request queue saturated for ${model}. Retry in ${formatRetryAfter(Math.ceil(REQUEST_SHAPING_CONFIG.maxQueueDelayMs / 1000))}.`,
|
|
310
|
+
),
|
|
311
|
+
);
|
|
312
|
+
}, REQUEST_SHAPING_CONFIG.maxQueueDelayMs);
|
|
313
|
+
}),
|
|
314
|
+
]);
|
|
315
|
+
|
|
316
|
+
const now = Date.now();
|
|
317
|
+
const bucket = modelTokenBuckets.get(model) ?? {
|
|
318
|
+
tokens: REQUEST_SHAPING_CONFIG.burstCapacity,
|
|
319
|
+
lastRefillAt: now,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const elapsedMs = Math.max(0, now - bucket.lastRefillAt);
|
|
323
|
+
const refillTokens =
|
|
324
|
+
(elapsedMs / 1000) * REQUEST_SHAPING_CONFIG.tokensPerSecond;
|
|
325
|
+
bucket.tokens = Math.min(
|
|
326
|
+
REQUEST_SHAPING_CONFIG.burstCapacity,
|
|
327
|
+
bucket.tokens + refillTokens,
|
|
328
|
+
);
|
|
329
|
+
bucket.lastRefillAt = now;
|
|
330
|
+
|
|
331
|
+
if (bucket.tokens < 1) {
|
|
332
|
+
const deficit = 1 - bucket.tokens;
|
|
333
|
+
const waitMs = Math.ceil(
|
|
334
|
+
(deficit / REQUEST_SHAPING_CONFIG.tokensPerSecond) * 1000,
|
|
335
|
+
);
|
|
336
|
+
if (waitMs > REQUEST_SHAPING_CONFIG.maxQueueDelayMs) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`[Copilot] Local request queue saturated for ${model}. Retry in ${formatRetryAfter(Math.ceil(waitMs / 1000))}.`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
log("info", `Local request shaping wait for ${model}`, {
|
|
342
|
+
wait_ms: waitMs,
|
|
343
|
+
});
|
|
344
|
+
await sleep(waitMs);
|
|
345
|
+
bucket.tokens = 0;
|
|
346
|
+
bucket.lastRefillAt = Date.now();
|
|
347
|
+
} else {
|
|
348
|
+
bucket.tokens -= 1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
modelTokenBuckets.set(model, bucket);
|
|
352
|
+
} finally {
|
|
353
|
+
if (queueTimeout) clearTimeout(queueTimeout);
|
|
354
|
+
releaseQueue?.();
|
|
355
|
+
if (modelQueueTail.get(model) === currentTail) {
|
|
356
|
+
modelQueueTail.delete(model);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function markModelRateLimited(model: string, cooldownMs: number): void {
|
|
362
|
+
const boundedCooldownMs = clampCooldownMs(cooldownMs);
|
|
363
|
+
rateLimitState.set(model, {
|
|
364
|
+
rateLimitedUntil: Date.now() + boundedCooldownMs,
|
|
365
|
+
});
|
|
366
|
+
log(
|
|
367
|
+
"info",
|
|
368
|
+
`Marked ${model} as rate-limited for ${Math.round(boundedCooldownMs / 1000)}s`,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Maximum length for item IDs in the OpenAI Responses API
|
|
373
|
+
const MAX_RESPONSE_API_ID_LENGTH = 64;
|
|
374
|
+
// OpenAI Responses API only allows: letters, numbers, underscores, dashes
|
|
375
|
+
const INVALID_ID_CHARS = /[^a-zA-Z0-9_-]/g;
|
|
376
|
+
|
|
377
|
+
/** Check if an ID contains characters not allowed by the Responses API */
|
|
378
|
+
function hasInvalidIdChars(id: string): boolean {
|
|
379
|
+
// Use a non-global regex for .test() to avoid lastIndex state bug
|
|
380
|
+
return /[^a-zA-Z0-9_-]/.test(id);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Sanitize an ID for the Responses API.
|
|
385
|
+
* Handles three issues from GitHub Copilot:
|
|
386
|
+
* 1. Invalid characters — Copilot IDs contain +, |, /, = (base64-like encoding)
|
|
387
|
+
* 2. Wrong prefix — GPT models return "h_" instead of "fc_" for function_call items
|
|
388
|
+
* 3. Excessive length — Copilot returns 400+ char IDs (max is 64)
|
|
389
|
+
*
|
|
390
|
+
* Approach matches anomalyco/opencode: replace invalid chars with "_", preserve prefix,
|
|
391
|
+
* truncate to 64 chars, strip trailing underscores.
|
|
392
|
+
*
|
|
393
|
+
* @param id - The original ID to sanitize
|
|
394
|
+
* @param forcedPrefix - If provided, use this prefix instead of the detected one.
|
|
395
|
+
* See: https://github.com/vercel/ai/issues/5171
|
|
396
|
+
*/
|
|
397
|
+
function sanitizeResponseId(id: string, forcedPrefix?: string): string {
|
|
398
|
+
if (!id) return id;
|
|
399
|
+
|
|
400
|
+
// Detect the original prefix (e.g., "fc_", "msg_", "call_", "resp_", "h_")
|
|
401
|
+
const prefixMatch = id.match(/^([a-z]+_)/);
|
|
402
|
+
const detectedPrefix = prefixMatch ? prefixMatch[1] : "";
|
|
403
|
+
const prefix = forcedPrefix ?? detectedPrefix;
|
|
404
|
+
|
|
405
|
+
// Strip the original prefix to get the core ID
|
|
406
|
+
const rawCore = id.slice(detectedPrefix.length);
|
|
407
|
+
// Replace invalid characters with underscores (same as anomalyco/opencode)
|
|
408
|
+
const cleanCore = rawCore.replace(INVALID_ID_CHARS, "_").replace(/_+$/g, "");
|
|
409
|
+
|
|
410
|
+
// Check if any sanitization is actually needed
|
|
411
|
+
const needsSanitization =
|
|
412
|
+
forcedPrefix ||
|
|
413
|
+
hasInvalidIdChars(rawCore) ||
|
|
414
|
+
id.length > MAX_RESPONSE_API_ID_LENGTH;
|
|
415
|
+
|
|
416
|
+
if (!needsSanitization) return id;
|
|
417
|
+
|
|
418
|
+
// If result fits within length and core is non-empty, use cleaned core directly
|
|
419
|
+
if (
|
|
420
|
+
cleanCore.length > 0 &&
|
|
421
|
+
prefix.length + cleanCore.length <= MAX_RESPONSE_API_ID_LENGTH
|
|
422
|
+
) {
|
|
423
|
+
return `${prefix}${cleanCore}`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Hash the full original ID for deterministic uniqueness when truncating
|
|
427
|
+
let hash = 0;
|
|
428
|
+
for (let i = 0; i < id.length; i++) {
|
|
429
|
+
hash = ((hash << 5) - hash + id.charCodeAt(i)) | 0;
|
|
430
|
+
}
|
|
431
|
+
const hashStr = Math.abs(hash).toString(36);
|
|
432
|
+
const maxMiddleLen =
|
|
433
|
+
MAX_RESPONSE_API_ID_LENGTH - prefix.length - hashStr.length - 1;
|
|
434
|
+
const middle = cleanCore.slice(0, Math.max(0, maxMiddleLen));
|
|
435
|
+
// Format: prefix + middle + "_" + hash (ensure total <= 64)
|
|
436
|
+
const result = `${prefix}${middle}_${hashStr}`.slice(
|
|
437
|
+
0,
|
|
438
|
+
MAX_RESPONSE_API_ID_LENGTH,
|
|
439
|
+
);
|
|
440
|
+
// Strip trailing underscores from truncation
|
|
441
|
+
return result.replace(/_+$/, "");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Check if an ID has the expected prefix for its item type.
|
|
446
|
+
* Handles both type-based items (function_call → "fc_") and
|
|
447
|
+
* role-based items (assistant/user/developer/system → "msg_").
|
|
448
|
+
* Returns the expected prefix if the ID is wrong, or null if it's fine.
|
|
449
|
+
*/
|
|
450
|
+
function getExpectedPrefix(item: any): string | null {
|
|
451
|
+
if (!item || typeof item !== "object") return null;
|
|
452
|
+
if (typeof item.id !== "string") return null;
|
|
453
|
+
|
|
454
|
+
// Type-based items: function_call, local_shell_call, etc.
|
|
455
|
+
if (item.type) {
|
|
456
|
+
const expected = RESPONSES_API_EXPECTED_PREFIXES[item.type];
|
|
457
|
+
if (expected && !item.id.startsWith(expected)) {
|
|
458
|
+
return expected;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Role-based items: assistant, user, developer, system → "msg_" prefix
|
|
463
|
+
if (item.role && !item.id.startsWith(RESPONSES_API_ROLE_PREFIX)) {
|
|
464
|
+
return RESPONSES_API_ROLE_PREFIX;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/** Check if a string ID needs sanitization (invalid chars or too long) */
|
|
471
|
+
function idNeedsSanitization(id: string): boolean {
|
|
472
|
+
return id.length > MAX_RESPONSE_API_ID_LENGTH || hasInvalidIdChars(id);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Sanitize all IDs in a Responses API input array.
|
|
477
|
+
*
|
|
478
|
+
* Handles THREE classes of invalid IDs:
|
|
479
|
+
* 1. Invalid characters — Copilot IDs contain +, |, /, = (only [a-zA-Z0-9_-] allowed)
|
|
480
|
+
* 2. Wrong prefix — Copilot GPT models return IDs like "h_xxx" instead of "fc_xxx"
|
|
481
|
+
* 3. Excessive length — Copilot returns 400+ char IDs that exceed the 64-char limit.
|
|
482
|
+
*
|
|
483
|
+
* Uses a two-pass approach:
|
|
484
|
+
* - Pass 1: Build an ID remap for all invalid IDs
|
|
485
|
+
* - Pass 2: Apply the remap to both `id` and `call_id` fields consistently,
|
|
486
|
+
* so function_call_output.call_id stays in sync with function_call.id
|
|
487
|
+
*/
|
|
488
|
+
function sanitizeResponseInputIds(input: any[]): any[] {
|
|
489
|
+
// Pass 1: Build ID remapping
|
|
490
|
+
const idRemap = new Map<string, string>();
|
|
491
|
+
|
|
492
|
+
for (const item of input) {
|
|
493
|
+
if (!item || typeof item !== "object") continue;
|
|
494
|
+
|
|
495
|
+
// Check for wrong prefix (e.g., function_call with "h_" instead of "fc_")
|
|
496
|
+
const expectedPrefix = getExpectedPrefix(item);
|
|
497
|
+
if (
|
|
498
|
+
expectedPrefix &&
|
|
499
|
+
typeof item.id === "string" &&
|
|
500
|
+
!idRemap.has(item.id)
|
|
501
|
+
) {
|
|
502
|
+
const newId = sanitizeResponseId(item.id, expectedPrefix);
|
|
503
|
+
if (newId !== item.id) {
|
|
504
|
+
idRemap.set(item.id, newId);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Check for invalid chars or excessive length on id
|
|
509
|
+
if (
|
|
510
|
+
typeof item.id === "string" &&
|
|
511
|
+
idNeedsSanitization(item.id) &&
|
|
512
|
+
!idRemap.has(item.id)
|
|
513
|
+
) {
|
|
514
|
+
idRemap.set(item.id, sanitizeResponseId(item.id));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Check for wrong prefix on call_id (defensive: handles truncated conversations
|
|
518
|
+
// where function_call_output appears without its corresponding function_call)
|
|
519
|
+
if (
|
|
520
|
+
item.type === "function_call_output" &&
|
|
521
|
+
typeof item.call_id === "string" &&
|
|
522
|
+
!item.call_id.startsWith("fc_") &&
|
|
523
|
+
!idRemap.has(item.call_id)
|
|
524
|
+
) {
|
|
525
|
+
const newCallId = sanitizeResponseId(item.call_id, "fc_");
|
|
526
|
+
if (newCallId !== item.call_id) {
|
|
527
|
+
idRemap.set(item.call_id, newCallId);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Check for invalid chars or excessive length on call_id
|
|
532
|
+
if (
|
|
533
|
+
typeof item.call_id === "string" &&
|
|
534
|
+
idNeedsSanitization(item.call_id) &&
|
|
535
|
+
!idRemap.has(item.call_id)
|
|
536
|
+
) {
|
|
537
|
+
idRemap.set(item.call_id, sanitizeResponseId(item.call_id));
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// No changes needed
|
|
542
|
+
if (idRemap.size === 0) return input;
|
|
543
|
+
|
|
544
|
+
// Pass 2: Apply remapping to both id and call_id fields
|
|
545
|
+
return input.map((item: any) => {
|
|
546
|
+
if (!item || typeof item !== "object") return item;
|
|
547
|
+
const sanitized = { ...item };
|
|
548
|
+
if (typeof sanitized.id === "string" && idRemap.has(sanitized.id)) {
|
|
549
|
+
sanitized.id = idRemap.get(sanitized.id);
|
|
550
|
+
}
|
|
551
|
+
if (
|
|
552
|
+
typeof sanitized.call_id === "string" &&
|
|
553
|
+
idRemap.has(sanitized.call_id)
|
|
554
|
+
) {
|
|
555
|
+
sanitized.call_id = idRemap.get(sanitized.call_id);
|
|
556
|
+
}
|
|
557
|
+
return sanitized;
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export const CopilotAuthPlugin: Plugin = async ({ client: sdk, directory }) => {
|
|
562
|
+
// Initialize logger with the SDK client
|
|
563
|
+
setLogger(sdk);
|
|
564
|
+
|
|
565
|
+
const localCopilotSdk = pathToFileURL(
|
|
566
|
+
path.join(directory, ".opencode", "plugin", "sdk", "copilot", "index.ts"),
|
|
567
|
+
).toString();
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
auth: {
|
|
571
|
+
provider: "github-copilot",
|
|
572
|
+
loader: async (getAuth, provider) => {
|
|
573
|
+
const info = await getAuth();
|
|
574
|
+
if (!info || info.type !== "oauth") return {};
|
|
575
|
+
|
|
576
|
+
// Enterprise URL support for baseURL
|
|
577
|
+
const enterpriseUrl = (info as any).enterpriseUrl;
|
|
578
|
+
const baseURL = enterpriseUrl
|
|
579
|
+
? `https://copilot-api.${normalizeDomain(enterpriseUrl)}`
|
|
580
|
+
: undefined;
|
|
581
|
+
|
|
582
|
+
if (provider && provider.models) {
|
|
583
|
+
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
584
|
+
model.cost = {
|
|
585
|
+
input: 0,
|
|
586
|
+
output: 0,
|
|
587
|
+
cache: {
|
|
588
|
+
read: 0,
|
|
589
|
+
write: 0,
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// OpenCode 1.14.33 routes Copilot Claude models through the
|
|
594
|
+
// Anthropic Messages API when Copilot /models reports /v1/messages.
|
|
595
|
+
// Keep that route: the local OpenAI-compatible SDK calls
|
|
596
|
+
// /chat/completions, which returns 404 for current Claude models.
|
|
597
|
+
if (isClaudeCopilotModel(modelId, model)) {
|
|
598
|
+
model.api.npm = "@ai-sdk/anthropic";
|
|
599
|
+
model.api.url = toCopilotMessagesBase(baseURL ?? model.api.url);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Route OpenAI-compatible Copilot models through the bundled
|
|
604
|
+
// @ai-sdk/github-copilot SDK. This SDK is shipped with OpenCode and
|
|
605
|
+
// supports both /chat/completions (gpt-4*, older) and /v1/responses
|
|
606
|
+
// (gpt-5.x reasoning models), so projects without local @ai-sdk/*
|
|
607
|
+
// dependencies can initialize the provider without ProviderInitError.
|
|
608
|
+
model.api.npm = "@ai-sdk/github-copilot";
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return {
|
|
613
|
+
baseURL,
|
|
614
|
+
apiKey: "",
|
|
615
|
+
async fetch(input, init) {
|
|
616
|
+
const info = await getAuth();
|
|
617
|
+
if (info.type !== "oauth") return fetch(input, init);
|
|
618
|
+
|
|
619
|
+
let isAgentCall = false;
|
|
620
|
+
let isVisionRequest = false;
|
|
621
|
+
let modifiedBody: any;
|
|
622
|
+
let isClaudeModel = false;
|
|
623
|
+
|
|
624
|
+
try {
|
|
625
|
+
const body =
|
|
626
|
+
typeof init?.body === "string"
|
|
627
|
+
? JSON.parse(init.body)
|
|
628
|
+
: init?.body;
|
|
629
|
+
|
|
630
|
+
const url = getRequestUrl(input);
|
|
631
|
+
|
|
632
|
+
// Check if this is a Claude model request
|
|
633
|
+
const modelId = body?.model || "";
|
|
634
|
+
isClaudeModel = modelId.toLowerCase().includes("claude");
|
|
635
|
+
|
|
636
|
+
// Completions API
|
|
637
|
+
if (body?.messages && url.includes("completions")) {
|
|
638
|
+
// Keep local logic: detect if any message is assistant/tool
|
|
639
|
+
isAgentCall = body.messages.some((msg: any) =>
|
|
640
|
+
["tool", "assistant"].includes(msg.role),
|
|
641
|
+
);
|
|
642
|
+
isVisionRequest = body.messages.some(
|
|
643
|
+
(msg: any) =>
|
|
644
|
+
Array.isArray(msg.content) &&
|
|
645
|
+
msg.content.some((part: any) => part.type === "image_url"),
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
// For Claude models, add thinking_budget to enable reasoning
|
|
649
|
+
// The Copilot API accepts this parameter and returns reasoning_text/reasoning_opaque
|
|
650
|
+
if (isClaudeModel) {
|
|
651
|
+
const thinkingBudget = extractClaudeThinkingBudget(body);
|
|
652
|
+
const isThinkingEnabled = thinkingBudget != null;
|
|
653
|
+
|
|
654
|
+
const cleanedMessages = body.messages.map(
|
|
655
|
+
(msg: any, idx: number) => {
|
|
656
|
+
if (msg.role !== "assistant") return msg;
|
|
657
|
+
|
|
658
|
+
// If thinking is disabled, strip all reasoning metadata to prevent
|
|
659
|
+
// stale reasoning context from continuing across turns.
|
|
660
|
+
if (!isThinkingEnabled) {
|
|
661
|
+
const {
|
|
662
|
+
reasoning_text: _reasoningText,
|
|
663
|
+
reasoning_opaque: _reasoningOpaque,
|
|
664
|
+
...baseMsg
|
|
665
|
+
} = msg;
|
|
666
|
+
if (!Array.isArray(baseMsg.content)) return baseMsg;
|
|
667
|
+
|
|
668
|
+
const cleanedContent = baseMsg.content.filter(
|
|
669
|
+
(part: any) => part.type !== "thinking",
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
...baseMsg,
|
|
674
|
+
content:
|
|
675
|
+
cleanedContent.length > 0 ? cleanedContent : null,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Log message structure for debugging
|
|
680
|
+
log("debug", `Processing assistant message ${idx}`, {
|
|
681
|
+
has_reasoning_text: !!msg.reasoning_text,
|
|
682
|
+
has_reasoning_opaque: !!msg.reasoning_opaque,
|
|
683
|
+
content_type: typeof msg.content,
|
|
684
|
+
content_is_array: Array.isArray(msg.content),
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
// If message has reasoning_text but no/invalid reasoning_opaque, remove reasoning
|
|
688
|
+
if (msg.reasoning_text && !msg.reasoning_opaque) {
|
|
689
|
+
log(
|
|
690
|
+
"warn",
|
|
691
|
+
`Removing reasoning_text without reasoning_opaque from message ${idx}`,
|
|
692
|
+
);
|
|
693
|
+
const { reasoning_text: _unused, ...cleanedMsg } = msg;
|
|
694
|
+
return cleanedMsg;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// If content is an array, strip ALL thinking blocks.
|
|
698
|
+
if (Array.isArray(msg.content)) {
|
|
699
|
+
const hasThinkingBlock = msg.content.some(
|
|
700
|
+
(part: any) => part.type === "thinking",
|
|
701
|
+
);
|
|
702
|
+
if (hasThinkingBlock) {
|
|
703
|
+
log(
|
|
704
|
+
"debug",
|
|
705
|
+
`Stripping all thinking blocks from message ${idx}`,
|
|
706
|
+
);
|
|
707
|
+
const cleanedContent = msg.content.filter(
|
|
708
|
+
(part: any) => part.type !== "thinking",
|
|
709
|
+
);
|
|
710
|
+
return {
|
|
711
|
+
...msg,
|
|
712
|
+
content:
|
|
713
|
+
cleanedContent.length > 0 ? cleanedContent : null,
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return msg;
|
|
719
|
+
},
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
const nextBody: Record<string, any> = {
|
|
723
|
+
...(modifiedBody || body),
|
|
724
|
+
messages: cleanedMessages,
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
if (isThinkingEnabled) {
|
|
728
|
+
nextBody.thinking_budget = thinkingBudget;
|
|
729
|
+
log("info", `Adding thinking_budget for Claude model`, {
|
|
730
|
+
model: modelId,
|
|
731
|
+
thinking_budget: thinkingBudget,
|
|
732
|
+
});
|
|
733
|
+
} else {
|
|
734
|
+
delete nextBody.thinking_budget;
|
|
735
|
+
log(
|
|
736
|
+
"info",
|
|
737
|
+
`Claude thinking disabled for this request (no thinking budget set)`,
|
|
738
|
+
{ model: modelId },
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Copilot OpenAI-compatible endpoint expects `thinking_budget`.
|
|
743
|
+
// Remove Anthropic-style `thinking` object to avoid mixed payloads.
|
|
744
|
+
delete nextBody.thinking;
|
|
745
|
+
|
|
746
|
+
modifiedBody = nextBody;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// For GPT models (o1, gpt-5, etc.), add reasoning parameter
|
|
750
|
+
const isGptModel =
|
|
751
|
+
modelId.toLowerCase().includes("gpt") ||
|
|
752
|
+
modelId.toLowerCase().includes("o1") ||
|
|
753
|
+
modelId.toLowerCase().includes("o3") ||
|
|
754
|
+
modelId.toLowerCase().includes("o4");
|
|
755
|
+
|
|
756
|
+
if (isGptModel && !isClaudeModel) {
|
|
757
|
+
// Get reasoning effort from body options or default to "medium"
|
|
758
|
+
const reasoningEffort =
|
|
759
|
+
body.reasoning?.effort ||
|
|
760
|
+
body.reasoningEffort ||
|
|
761
|
+
body.reasoning_effort ||
|
|
762
|
+
"medium";
|
|
763
|
+
|
|
764
|
+
modifiedBody = {
|
|
765
|
+
...(modifiedBody || body),
|
|
766
|
+
reasoning: {
|
|
767
|
+
effort: reasoningEffort,
|
|
768
|
+
},
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// Also pass through other reasoning options if present
|
|
772
|
+
if (body.reasoningSummary || body.reasoning?.summary) {
|
|
773
|
+
modifiedBody.reasoning.summary =
|
|
774
|
+
body.reasoningSummary || body.reasoning?.summary;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
log("info", `Adding reasoning for GPT model`, {
|
|
778
|
+
model: modelId,
|
|
779
|
+
reasoning_effort: reasoningEffort,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Responses API
|
|
785
|
+
if (body?.input) {
|
|
786
|
+
// Log raw IDs before sanitization for debugging
|
|
787
|
+
const rawIds = body.input
|
|
788
|
+
.filter((item: any) => item && typeof item === "object")
|
|
789
|
+
.flatMap((item: any) => [
|
|
790
|
+
item.id ? `id=${item.id}` : null,
|
|
791
|
+
item.call_id ? `call_id=${item.call_id}` : null,
|
|
792
|
+
])
|
|
793
|
+
.filter(Boolean);
|
|
794
|
+
if (rawIds.length > 0) {
|
|
795
|
+
log(
|
|
796
|
+
"debug",
|
|
797
|
+
"[ID-SANITIZE] Raw input IDs before sanitization",
|
|
798
|
+
{
|
|
799
|
+
ids: rawIds,
|
|
800
|
+
count: rawIds.length,
|
|
801
|
+
},
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Sanitize IDs from Copilot backend:
|
|
806
|
+
// 1. Wrong prefix — GPT models return "h_xxx" instead of "fc_xxx"
|
|
807
|
+
// 2. Excessive length — Copilot returns 400+ char IDs (max is 64)
|
|
808
|
+
const sanitizedInput = sanitizeResponseInputIds(body.input);
|
|
809
|
+
const refDiffers = sanitizedInput !== body.input;
|
|
810
|
+
const jsonDiffers =
|
|
811
|
+
refDiffers &&
|
|
812
|
+
JSON.stringify(sanitizedInput) !== JSON.stringify(body.input);
|
|
813
|
+
const inputWasSanitized = refDiffers && jsonDiffers;
|
|
814
|
+
|
|
815
|
+
log("debug", "[ID-SANITIZE] Sanitization result", {
|
|
816
|
+
was_sanitized: inputWasSanitized,
|
|
817
|
+
ref_differs: refDiffers,
|
|
818
|
+
json_differs: jsonDiffers,
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
if (inputWasSanitized) {
|
|
822
|
+
const fixes = body.input
|
|
823
|
+
.map((item: any, i: number) => ({
|
|
824
|
+
item,
|
|
825
|
+
i,
|
|
826
|
+
si: sanitizedInput[i],
|
|
827
|
+
}))
|
|
828
|
+
.filter(
|
|
829
|
+
({ item, si }: any) =>
|
|
830
|
+
item &&
|
|
831
|
+
si &&
|
|
832
|
+
(item.id !== si.id || item.call_id !== si.call_id),
|
|
833
|
+
);
|
|
834
|
+
log(
|
|
835
|
+
"info",
|
|
836
|
+
"[ID-SANITIZE] Fixed IDs in Responses API input",
|
|
837
|
+
{
|
|
838
|
+
items_fixed: fixes.length,
|
|
839
|
+
fixes: fixes.map(({ item, si }: any) => ({
|
|
840
|
+
type: item.type,
|
|
841
|
+
old_id: item.id,
|
|
842
|
+
new_id: si?.id,
|
|
843
|
+
old_call_id: item.call_id,
|
|
844
|
+
new_call_id: si?.call_id,
|
|
845
|
+
})),
|
|
846
|
+
},
|
|
847
|
+
);
|
|
848
|
+
modifiedBody = {
|
|
849
|
+
...(modifiedBody || body),
|
|
850
|
+
input: sanitizedInput,
|
|
851
|
+
};
|
|
852
|
+
} else {
|
|
853
|
+
log(
|
|
854
|
+
"debug",
|
|
855
|
+
"[ID-SANITIZE] No sanitization needed — all IDs valid",
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
isAgentCall = (sanitizedInput || body.input).some(
|
|
860
|
+
(item: any) =>
|
|
861
|
+
item?.role === "assistant" ||
|
|
862
|
+
(item?.type &&
|
|
863
|
+
RESPONSES_API_ALTERNATE_INPUT_TYPES.includes(item.type)),
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
isVisionRequest = body.input.some(
|
|
867
|
+
(item: any) =>
|
|
868
|
+
Array.isArray(item?.content) &&
|
|
869
|
+
item.content.some(
|
|
870
|
+
(part: any) => part.type === "input_image",
|
|
871
|
+
),
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Messages API (Anthropic style)
|
|
876
|
+
if (body?.messages && !url.includes("completions")) {
|
|
877
|
+
isAgentCall = body.messages.some((msg: any) =>
|
|
878
|
+
["tool", "assistant"].includes(msg.role),
|
|
879
|
+
);
|
|
880
|
+
isVisionRequest = body.messages.some(
|
|
881
|
+
(item: any) =>
|
|
882
|
+
Array.isArray(item?.content) &&
|
|
883
|
+
item.content.some(
|
|
884
|
+
(part: any) =>
|
|
885
|
+
part?.type === "image" ||
|
|
886
|
+
(part?.type === "tool_result" &&
|
|
887
|
+
Array.isArray(part?.content) &&
|
|
888
|
+
part.content.some(
|
|
889
|
+
(nested: any) => nested?.type === "image",
|
|
890
|
+
)),
|
|
891
|
+
),
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Sanitize tool definitions: strip non-standard fields like
|
|
896
|
+
// "custom" (wrapping eager_input_streaming) injected by
|
|
897
|
+
// runtime layers. These cause "Extra inputs are not permitted"
|
|
898
|
+
// on the Copilot API.
|
|
899
|
+
const toolSource = modifiedBody || body;
|
|
900
|
+
if (Array.isArray(toolSource?.tools)) {
|
|
901
|
+
let toolsChanged = false;
|
|
902
|
+
const sanitizedTools = toolSource.tools.map((tool: any) => {
|
|
903
|
+
if (!tool || typeof tool !== "object") return tool;
|
|
904
|
+
|
|
905
|
+
let result = tool;
|
|
906
|
+
|
|
907
|
+
// Strip top-level non-standard fields from tool object
|
|
908
|
+
if ("custom" in result || "eager_input_streaming" in result) {
|
|
909
|
+
toolsChanged = true;
|
|
910
|
+
const {
|
|
911
|
+
custom: _custom,
|
|
912
|
+
eager_input_streaming: _eis,
|
|
913
|
+
...clean
|
|
914
|
+
} = result;
|
|
915
|
+
result = clean;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Also check nested function object (Chat Completions format)
|
|
919
|
+
if (
|
|
920
|
+
result.function &&
|
|
921
|
+
typeof result.function === "object" &&
|
|
922
|
+
("custom" in result.function ||
|
|
923
|
+
"eager_input_streaming" in result.function)
|
|
924
|
+
) {
|
|
925
|
+
toolsChanged = true;
|
|
926
|
+
const {
|
|
927
|
+
custom: _c,
|
|
928
|
+
eager_input_streaming: _e,
|
|
929
|
+
...cleanFn
|
|
930
|
+
} = result.function;
|
|
931
|
+
result = { ...result, function: cleanFn };
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
return result;
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
if (toolsChanged) {
|
|
938
|
+
modifiedBody = {
|
|
939
|
+
...(modifiedBody || body),
|
|
940
|
+
tools: sanitizedTools,
|
|
941
|
+
};
|
|
942
|
+
log(
|
|
943
|
+
"debug",
|
|
944
|
+
"Stripped non-standard fields (custom/eager_input_streaming) from tool definitions",
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
} catch {}
|
|
949
|
+
|
|
950
|
+
const headers: Record<string, string> = {
|
|
951
|
+
"x-initiator": isAgentCall ? "agent" : "user",
|
|
952
|
+
...(init?.headers as Record<string, string>),
|
|
953
|
+
...HEADERS,
|
|
954
|
+
Authorization: `Bearer ${info.refresh}`,
|
|
955
|
+
"Openai-Intent": "conversation-edits",
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
if (isVisionRequest) {
|
|
959
|
+
headers["Copilot-Vision-Request"] = "true";
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Official only deletes lowercase "authorization"
|
|
963
|
+
delete headers["x-api-key"];
|
|
964
|
+
delete headers["authorization"];
|
|
965
|
+
|
|
966
|
+
// Prepare the final init object with potentially modified body
|
|
967
|
+
const finalInit = {
|
|
968
|
+
...init,
|
|
969
|
+
headers,
|
|
970
|
+
...(modifiedBody ? { body: JSON.stringify(modifiedBody) } : {}),
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
// Extract model from request body for rate limit tracking
|
|
974
|
+
let currentModel = "";
|
|
975
|
+
try {
|
|
976
|
+
const bodyObj =
|
|
977
|
+
typeof finalInit.body === "string"
|
|
978
|
+
? JSON.parse(finalInit.body)
|
|
979
|
+
: finalInit.body;
|
|
980
|
+
currentModel = bodyObj?.model || "";
|
|
981
|
+
} catch {}
|
|
982
|
+
|
|
983
|
+
// Pre-flight: fail fast if current model family is cooling down
|
|
984
|
+
const activeFinalInit: RequestInit = finalInit;
|
|
985
|
+
if (currentModel) {
|
|
986
|
+
const familyCooldownMs = Math.max(
|
|
987
|
+
getFamilyCircuitRemainingMs(currentModel),
|
|
988
|
+
getFamilyMaxCooldownRemainingMs(currentModel),
|
|
989
|
+
);
|
|
990
|
+
if (familyCooldownMs > 0) {
|
|
991
|
+
throw new Error(
|
|
992
|
+
`[Copilot] Rate limited: all fallback models cooling down. Retry in ${formatRetryAfter(Math.ceil(familyCooldownMs / 1000))}.`,
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const maxFetchAttempts = 2;
|
|
998
|
+
|
|
999
|
+
for (let attempt = 1; attempt <= maxFetchAttempts; attempt++) {
|
|
1000
|
+
try {
|
|
1001
|
+
if (currentModel) {
|
|
1002
|
+
await shapeRequestForModel(currentModel);
|
|
1003
|
+
}
|
|
1004
|
+
const response = await fetch(input, activeFinalInit);
|
|
1005
|
+
|
|
1006
|
+
if (response.status === 429) {
|
|
1007
|
+
try {
|
|
1008
|
+
await response.body?.cancel();
|
|
1009
|
+
} catch {}
|
|
1010
|
+
|
|
1011
|
+
const retryAfterMs = parseRetryAfter(response);
|
|
1012
|
+
const cooldownMs = clampCooldownMs(
|
|
1013
|
+
retryAfterMs,
|
|
1014
|
+
RATE_LIMIT_CONFIG.defaultCooldownMs,
|
|
1015
|
+
);
|
|
1016
|
+
|
|
1017
|
+
if (currentModel) {
|
|
1018
|
+
markModelRateLimited(currentModel, cooldownMs);
|
|
1019
|
+
openFamilyCircuitBreaker(currentModel, cooldownMs);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
throw new Error(
|
|
1023
|
+
`[Copilot] Rate limited: ${currentModel || "model"} cooling down. Retry in ${formatRetryAfter(Math.ceil(cooldownMs / 1000))}.`,
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// Response transformation is handled by the custom SDK at
|
|
1028
|
+
// .opencode/plugin/sdk/copilot/
|
|
1029
|
+
return response;
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
const errorMessage = getErrorMessage(error);
|
|
1032
|
+
if (
|
|
1033
|
+
errorMessage.includes("Rate limited") ||
|
|
1034
|
+
errorMessage.includes("Local request queue saturated")
|
|
1035
|
+
) {
|
|
1036
|
+
throw error instanceof Error
|
|
1037
|
+
? error
|
|
1038
|
+
: new Error(errorMessage);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (
|
|
1042
|
+
attempt < maxFetchAttempts &&
|
|
1043
|
+
isTransientUpstreamTimeoutError(error)
|
|
1044
|
+
) {
|
|
1045
|
+
const retryDelayMs = 750 * attempt;
|
|
1046
|
+
log(
|
|
1047
|
+
"warn",
|
|
1048
|
+
`Transient upstream timeout from Copilot, retrying request`,
|
|
1049
|
+
{
|
|
1050
|
+
model: currentModel || undefined,
|
|
1051
|
+
attempt,
|
|
1052
|
+
retry_delay_ms: retryDelayMs,
|
|
1053
|
+
error: errorMessage,
|
|
1054
|
+
},
|
|
1055
|
+
);
|
|
1056
|
+
await sleep(retryDelayMs);
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (isTransientUpstreamTimeoutError(error)) {
|
|
1061
|
+
throw new Error(
|
|
1062
|
+
`[Copilot] Upstream idle timeout while streaming ${currentModel || "request"}. Retry with a lower thinking budget or switch to a lower-latency Claude variant.`,
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
throw error;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
throw new Error(
|
|
1071
|
+
`[Copilot] Failed request after ${maxFetchAttempts} attempts.`,
|
|
1072
|
+
);
|
|
1073
|
+
},
|
|
1074
|
+
};
|
|
1075
|
+
},
|
|
1076
|
+
methods: [
|
|
1077
|
+
{
|
|
1078
|
+
type: "oauth",
|
|
1079
|
+
label: "Login with GitHub Copilot",
|
|
1080
|
+
prompts: [
|
|
1081
|
+
{
|
|
1082
|
+
type: "select",
|
|
1083
|
+
key: "deploymentType",
|
|
1084
|
+
message: "Select GitHub deployment type",
|
|
1085
|
+
options: [
|
|
1086
|
+
{
|
|
1087
|
+
label: "GitHub.com",
|
|
1088
|
+
value: "github.com",
|
|
1089
|
+
hint: "Public",
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
label: "GitHub Enterprise",
|
|
1093
|
+
value: "enterprise",
|
|
1094
|
+
hint: "Data residency or self-hosted",
|
|
1095
|
+
},
|
|
1096
|
+
],
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
type: "text",
|
|
1100
|
+
key: "enterpriseUrl",
|
|
1101
|
+
message: "Enter your GitHub Enterprise URL or domain",
|
|
1102
|
+
placeholder: "company.ghe.com or https://company.ghe.com",
|
|
1103
|
+
condition: (inputs: any) =>
|
|
1104
|
+
inputs.deploymentType === "enterprise",
|
|
1105
|
+
validate: (value: string) => {
|
|
1106
|
+
if (!value) return "URL or domain is required";
|
|
1107
|
+
try {
|
|
1108
|
+
const url = value.includes("://")
|
|
1109
|
+
? new URL(value)
|
|
1110
|
+
: new URL(`https://${value}`);
|
|
1111
|
+
if (!url.hostname)
|
|
1112
|
+
return "Please enter a valid URL or domain";
|
|
1113
|
+
return undefined;
|
|
1114
|
+
} catch {
|
|
1115
|
+
return "Please enter a valid URL (e.g., company.ghe.com or https://company.ghe.com)";
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
],
|
|
1120
|
+
async authorize(inputs: any = {}) {
|
|
1121
|
+
const deploymentType = inputs.deploymentType || "github.com";
|
|
1122
|
+
|
|
1123
|
+
let domain = "github.com";
|
|
1124
|
+
let actualProvider = "github-copilot";
|
|
1125
|
+
|
|
1126
|
+
if (deploymentType === "enterprise") {
|
|
1127
|
+
const enterpriseUrl = inputs.enterpriseUrl;
|
|
1128
|
+
domain = normalizeDomain(enterpriseUrl);
|
|
1129
|
+
actualProvider = "github-copilot-enterprise";
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
const urls = getUrls(domain);
|
|
1133
|
+
|
|
1134
|
+
const deviceResponse = await fetch(urls.DEVICE_CODE_URL, {
|
|
1135
|
+
method: "POST",
|
|
1136
|
+
headers: {
|
|
1137
|
+
Accept: "application/json",
|
|
1138
|
+
"Content-Type": "application/json",
|
|
1139
|
+
"User-Agent": "opencode/1.3.17",
|
|
1140
|
+
},
|
|
1141
|
+
body: JSON.stringify({
|
|
1142
|
+
client_id: CLIENT_ID,
|
|
1143
|
+
scope: "read:user",
|
|
1144
|
+
}),
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
if (!deviceResponse.ok) {
|
|
1148
|
+
throw new Error("Failed to initiate device authorization");
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const deviceData = await deviceResponse.json();
|
|
1152
|
+
|
|
1153
|
+
return {
|
|
1154
|
+
url: deviceData.verification_uri,
|
|
1155
|
+
instructions: `Enter code: ${deviceData.user_code}`,
|
|
1156
|
+
method: "auto",
|
|
1157
|
+
callback: async () => {
|
|
1158
|
+
while (true) {
|
|
1159
|
+
const response = await fetch(urls.ACCESS_TOKEN_URL, {
|
|
1160
|
+
method: "POST",
|
|
1161
|
+
headers: {
|
|
1162
|
+
Accept: "application/json",
|
|
1163
|
+
"Content-Type": "application/json",
|
|
1164
|
+
"User-Agent": "opencode/1.3.17",
|
|
1165
|
+
},
|
|
1166
|
+
body: JSON.stringify({
|
|
1167
|
+
client_id: CLIENT_ID,
|
|
1168
|
+
device_code: deviceData.device_code,
|
|
1169
|
+
grant_type:
|
|
1170
|
+
"urn:ietf:params:oauth:grant-type:device_code",
|
|
1171
|
+
}),
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
if (!response.ok) return { type: "failed" };
|
|
1175
|
+
|
|
1176
|
+
const data = await response.json();
|
|
1177
|
+
|
|
1178
|
+
if (data.access_token) {
|
|
1179
|
+
const result: {
|
|
1180
|
+
type: "success";
|
|
1181
|
+
refresh: string;
|
|
1182
|
+
access: string;
|
|
1183
|
+
expires: number;
|
|
1184
|
+
provider?: string;
|
|
1185
|
+
enterpriseUrl?: string;
|
|
1186
|
+
} = {
|
|
1187
|
+
type: "success",
|
|
1188
|
+
refresh: data.access_token,
|
|
1189
|
+
access: data.access_token,
|
|
1190
|
+
expires: 0,
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
if (actualProvider === "github-copilot-enterprise") {
|
|
1194
|
+
result.provider = "github-copilot-enterprise";
|
|
1195
|
+
result.enterpriseUrl = domain;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
return result;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (data.error === "authorization_pending") {
|
|
1202
|
+
await sleep(
|
|
1203
|
+
deviceData.interval * 1000 +
|
|
1204
|
+
OAUTH_POLLING_SAFETY_MARGIN_MS,
|
|
1205
|
+
);
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (data.error === "slow_down") {
|
|
1210
|
+
// Based on the RFC spec, we must add 5 seconds to our current polling interval.
|
|
1211
|
+
let newInterval = (deviceData.interval + 5) * 1000;
|
|
1212
|
+
|
|
1213
|
+
if (
|
|
1214
|
+
data.interval &&
|
|
1215
|
+
typeof data.interval === "number" &&
|
|
1216
|
+
data.interval > 0
|
|
1217
|
+
) {
|
|
1218
|
+
newInterval = data.interval * 1000;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
await sleep(newInterval + OAUTH_POLLING_SAFETY_MARGIN_MS);
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (data.error) return { type: "failed" };
|
|
1226
|
+
|
|
1227
|
+
await sleep(
|
|
1228
|
+
deviceData.interval * 1000 + OAUTH_POLLING_SAFETY_MARGIN_MS,
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1232
|
+
};
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
],
|
|
1236
|
+
},
|
|
1237
|
+
// Hook to add custom headers for Claude reasoning support
|
|
1238
|
+
"chat.headers": async (input: any, output: any) => {
|
|
1239
|
+
// Only apply to GitHub Copilot provider
|
|
1240
|
+
if (!input.model?.providerID?.includes("github-copilot")) return;
|
|
1241
|
+
|
|
1242
|
+
// Add Anthropic beta header for interleaved thinking (extended reasoning)
|
|
1243
|
+
// This is required for Claude models to return thinking blocks
|
|
1244
|
+
if (input.model?.api?.npm === "@ai-sdk/anthropic") {
|
|
1245
|
+
output.headers["anthropic-beta"] = "interleaved-thinking-2025-05-14";
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// When using the local Copilot SDK, let the SDK/message pipeline decide
|
|
1249
|
+
// initiator semantics. Overriding x-initiator here can break Responses routing.
|
|
1250
|
+
if (input.model?.api?.npm === localCopilotSdk) return;
|
|
1251
|
+
|
|
1252
|
+
// Mark subagent sessions as agent-initiated (matching standard Copilot tools)
|
|
1253
|
+
try {
|
|
1254
|
+
const session = await sdk.session
|
|
1255
|
+
.get({
|
|
1256
|
+
path: {
|
|
1257
|
+
id: input.sessionID,
|
|
1258
|
+
},
|
|
1259
|
+
throwOnError: true,
|
|
1260
|
+
})
|
|
1261
|
+
.catch(() => undefined);
|
|
1262
|
+
if (session?.data?.parentID) {
|
|
1263
|
+
output.headers["x-initiator"] = "agent";
|
|
1264
|
+
}
|
|
1265
|
+
} catch {
|
|
1266
|
+
// Ignore errors from session lookup
|
|
1267
|
+
}
|
|
1268
|
+
},
|
|
1269
|
+
// Hook to strip maxOutputTokens for GPT models (matches upstream Copilot CLI behavior)
|
|
1270
|
+
"chat.params": async (input: any, output: any) => {
|
|
1271
|
+
if (!input.model?.providerID?.includes("github-copilot")) return;
|
|
1272
|
+
|
|
1273
|
+
// Copilot rejects eager tool streaming on Anthropic Messages routing.
|
|
1274
|
+
if (input.model?.api?.npm === "@ai-sdk/anthropic") {
|
|
1275
|
+
output.options ??= {};
|
|
1276
|
+
output.options.toolStreaming = false;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// GPT models don't support maxOutputTokens through the Copilot proxy
|
|
1280
|
+
if (input.model?.api?.id?.includes("gpt")) {
|
|
1281
|
+
output.maxOutputTokens = undefined;
|
|
1282
|
+
}
|
|
1283
|
+
},
|
|
1284
|
+
};
|
|
1285
|
+
};
|