dsh-github-copilot 0.4.0-alpha.18

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.
Files changed (75) hide show
  1. package/AGENTS.md +233 -0
  2. package/CONTRIBUTING.md +46 -0
  3. package/LICENSE +21 -0
  4. package/README.md +365 -0
  5. package/README.zh.md +376 -0
  6. package/SECURITY.md +29 -0
  7. package/cordis.patch.yml +16 -0
  8. package/deployment-baseline.json +752 -0
  9. package/docs/agent-readiness.md +95 -0
  10. package/docs/images/copilot-auth-card-signed-in.png +0 -0
  11. package/docs/images/copilot-device-code-copy.png +0 -0
  12. package/docs/images/copilot-model-freshness.png +0 -0
  13. package/docs/images/copilot-model-refreshing.png +0 -0
  14. package/docs/images/copilot-provider-authorization.png +0 -0
  15. package/docs/images/copilot-provider-entry.png +0 -0
  16. package/docs/images/github-copilot-auth-flow.gif +0 -0
  17. package/docs/model-compatibility-acceptance.md +46 -0
  18. package/docs/npm-distribution.md +134 -0
  19. package/docs/session-search-routing.md +53 -0
  20. package/docs/single-route-migration.md +115 -0
  21. package/lib/client.js +7153 -0
  22. package/lib/client.js.map +1 -0
  23. package/lib/index.js +5384 -0
  24. package/lib/remote.js +162 -0
  25. package/lib/routed-web.js +80 -0
  26. package/lib/search-routing-pFLux0W7.js +763 -0
  27. package/lib/types/account-model-auth.d.ts +15 -0
  28. package/lib/types/account-model-catalog.d.ts +67 -0
  29. package/lib/types/account-model-source.d.ts +85 -0
  30. package/lib/types/authorization-controller.d.ts +100 -0
  31. package/lib/types/client.d.ts +106 -0
  32. package/lib/types/compact-account.d.ts +42 -0
  33. package/lib/types/compatibility.d.ts +17 -0
  34. package/lib/types/config.d.ts +42 -0
  35. package/lib/types/content-file.d.ts +10 -0
  36. package/lib/types/copilot-auth.d.ts +23 -0
  37. package/lib/types/copilot-grant.d.ts +11 -0
  38. package/lib/types/copilot-identity.d.ts +6 -0
  39. package/lib/types/copilot-request.d.ts +11 -0
  40. package/lib/types/current-provider.d.ts +59 -0
  41. package/lib/types/deepseek-search-fallback.d.ts +11 -0
  42. package/lib/types/failure.d.ts +38 -0
  43. package/lib/types/http.d.ts +48 -0
  44. package/lib/types/index.d.ts +39 -0
  45. package/lib/types/migration-status.d.ts +50 -0
  46. package/lib/types/model-protocol.d.ts +31 -0
  47. package/lib/types/pi-provider-bridge.d.ts +22 -0
  48. package/lib/types/plan.d.ts +148 -0
  49. package/lib/types/preview-provider.d.ts +35 -0
  50. package/lib/types/preview-route.d.ts +63 -0
  51. package/lib/types/probe.d.ts +39 -0
  52. package/lib/types/reasoning-presentation.d.ts +55 -0
  53. package/lib/types/remote.d.ts +151 -0
  54. package/lib/types/responses-reasoning-text.d.ts +24 -0
  55. package/lib/types/responses-reasoning.d.ts +7 -0
  56. package/lib/types/route-ownership.d.ts +70 -0
  57. package/lib/types/routed-web.d.ts +38 -0
  58. package/lib/types/search-backend.d.ts +16 -0
  59. package/lib/types/search-routing.d.ts +43 -0
  60. package/lib/types/serialize.d.ts +109 -0
  61. package/lib/types/sse.d.ts +28 -0
  62. package/lib/types/temporary-models.d.ts +25 -0
  63. package/lib/types/tool-schema-compat.d.ts +17 -0
  64. package/lib/types/traditional-search.d.ts +17 -0
  65. package/lib/types/types.d.ts +69 -0
  66. package/lib/types/usage.d.ts +26 -0
  67. package/lib/types/watchdog.d.ts +22 -0
  68. package/lib/types/web-delegate.d.ts +12 -0
  69. package/lib/types/web-search-routing-card.d.ts +10 -0
  70. package/lib/types/web-search-routing-config.d.ts +16 -0
  71. package/lib/types/wire-anthropic.d.ts +37 -0
  72. package/lib/types/wire.d.ts +56 -0
  73. package/lib/web-delegate.js +9 -0
  74. package/package.json +200 -0
  75. package/scripts/check-search-composition.mjs +191 -0
@@ -0,0 +1,95 @@
1
+ # Agent readiness audit
2
+
3
+ Tracking: [#73](https://github.com/cloga/dsh-github-copilot/issues/73). Audited baseline: `d9d0954` (2026-09-05). This is an evidence inventory, not a blanket certification of live Copilot behavior.
4
+
5
+ ## Scope and method
6
+
7
+ Read the instruction entrypoints, source ownership boundaries, test/TypeScript configuration, tooling, packaging, CI/release workflows and public Remote contracts. Two independent native DSH reviewers examined verification/release and runtime/diagnostics. All reproduction uses synthetic fixtures. No production credential inspection, sign-in, sign-out, model/search calls, profile installation or DSH restart is part of this audit.
8
+
9
+ The existing project already has useful architecture boundaries, strict Remote codecs, exact Core pins, credential normalization, broad unit tests, bilingual docs and an immutable-release policy. The gaps were mostly between what an agent could observe and what it was allowed to claim.
10
+
11
+ ## Improvements with executable evidence
12
+
13
+ | Area | Audit evidence at baseline | Change / acceptance check |
14
+ |---|---|---|
15
+ | Onboarding/discovery | Commands scattered across prose; no structured task-to-files map | `agent-contract.json`, `scripts/agent.mjs describe/plan`, `verify:agent` check real paths and scripts |
16
+ | Safe preflight | `status()` and `inspectGitHubCopilotProviderProfile()` sound read-only but reconcile settings | Separate repository-only `agent:doctor`; output lists `notChecked`, actionable checks and stable exit codes; Node tooling tests cover missing dependencies |
17
+ | Attribution | AGENTS/CONTRIBUTING required unspecified co-author; historical bot was copied | Actual-tool `Assisted-by`; DSH example does not invent a bot email; contract validator guards against mandatory copied co-author policy |
18
+ | Runtime floor | Declared Node >=22.0.0 but locked pi-ai requires >=22.19.0 | Align package/baseline to >=22.19.0; recommend Node 24 LTS; CI still uses 24, so the minimum is a dependency floor, not a live Node22 certification |
19
+ | Compiler evidence | `tsconfig.json` included only src; expanded compilation found 17 diagnostics in tests/configs | `typecheck:tests`, accurate mock/brand/narrowing fixes; no blanket ignores; real Core fixture remains checked only in its pinned checkout |
20
+ | Vacuous assertion | Cordis DisposableList was indexed as an array, allowing undefined UID comparisons | Use iterable conversion and assert defined identity before equality in loader tests |
21
+ | Build evidence | verify:package checked Host existence, then claimed built Host verified | Import real built Host without the Vitest Typert alias; assert exported contract and label the evidence as import-only |
22
+ | Archive delivery | Pack/checksum did not check actual exported files or README media; images were omitted | `verify:tarball` inspects bounded tar data without extraction; rejects paths/links/duplicates/unexpected files/trailing payload; compares the full normalized manifest and every packed file to the checkout/build; checks exports and README media; CI/release run it after packing |
23
+ | External verifier safety | Fixed fixture path could overwrite then delete a pre-existing upstream file | Exclusive create, pinned clean tracked sources, physical-path containment, identity/byte-guarded cleanup and bounded child; twelve synthetic failure/sentinel tests including a Windows junction |
24
+ | Release prerequisite | Tag workflow could run independently of complete platform/Core matrix | Reuse CI through workflow_call; publish job needs matrix success on tag revision; contract verifier checks the dependency |
25
+ | Session model correctness | Inline preflight checked provider but plan sends default-route candidate.model | Historical guard matched provider AND model and delegated mismatches; alpha.8 replaces default-derived planning with initiating-owner/request identity (see #99 below) |
26
+ | Documentation freshness | Contribution/PR/bug forms omitted alpha.1; build/activation/install evidence blurred | Updated templates, exact approval boundaries, evidence matrix and retry/rollback guidance |
27
+ | Workspace hygiene | Broad lib ignore hid scripts/lib; root packs/.env not ignored | Root-anchor generated /lib, ignore tarballs/secrets; .editorconfig specifies new-file formatting without rewriting history |
28
+
29
+ Run `pnpm verify` for the local checks. Then `pnpm pack --pack-destination artifacts` and `pnpm verify:tarball -- artifacts/dsh-github-copilot-<package-version>.tgz`. The task planner emits the actual path from package.json. Consult the PR's final results for commands actually executed and current CI status; this document does not hard-code a perpetual passing test count.
30
+
31
+ ## Evidence ladder
32
+
33
+ 1. **Source markers** prove required seams/test names are present, not semantics.
34
+ 2. **Typechecks and synthetic tests** verify local assumptions, not installed account eligibility.
35
+ 3. **Built import/loader checks** prove modules load without test aliases; they do not call apply().
36
+ 4. **Pinned upstream config fixtures** prove configuration against those sources, not the entire companion Host on every runtime.
37
+ 5. **Archive/checksum inspection** proves shipped bytes and packaging consistency, not authenticated requests.
38
+ 6. **Explicit live acceptance** must separately check installed versus loaded version, actual session model, normal stream/tools, and selected search provider. A page returning HTTP 200 proves only reachability.
39
+
40
+ Missing evidence is `notChecked`, not success. Repository CLI plans do not grant permission to merge, publish, change credentials or install.
41
+
42
+ ## Runtime lifecycle follow-up (#75)
43
+
44
+ - **Read-only status delivered:** `status()` and `describeGitHubCopilotProviderProfile()` now share pure planning and return minimal authentication/catalog/route state without mutation, OAuth refresh or network. Explicit `reconcile()` and its UI control perform stored-snapshot repair. Errors preserve configured authentication and expose classified route diagnostics. Tests prove repeated reads are write/network-free, repair is explicit, and conflicts/failures do not leak raw errors.
45
+ - **Proof invalidation delivered:** both search surfaces listen to the exact Copilot credential key and evict successful/failed plans lazily. Attach/settings/events make no network requests. Cancellation and generation-bound candidates prevent stale in-flight proof, account replacement during final auth, fallback-spelling clones and delayed streams from using another generation's authorization. Disposal aborts pending probes and prevents further spelling attempts. Tests use actual synthetic grant changes, not only event emission.
46
+ - **Ownership checks delivered:** raw settings preimages/postimages, validated version-2 journal, revision-checked writes, exact created-profile shape checks, and leaf-only header cleanup protect user additions/edits. Resolved schema defaults do not count as user edits. Tests cover interrupted writes, manual edits, marker loss, legacy markers, process restart and same-process registration resets.
47
+ - **Deliberate recovery limit:** Core does not expose durable registration identity or a cross-namespace transaction. Revision integers/recorded process epochs cannot authorize replay after a namespace reload. Therefore a later invocation never reapplies an unresolved prepared activation/restoration; it keeps the journal and reports conflict for review. Steady postimages may prepare fresh restoration; already-restored targets may clear a marker without rewriting the route. Legacy journals and custom model preimages also fail conservatively rather than guessing ownership. Post-write marker checks detect interference but do not create atomic route/marker/credential commits.
48
+
49
+ ## Provider-integrated account controls follow-up (#93)
50
+
51
+ The provider integration introduced in `0.4.0-alpha.5` replaces the older independent-footer-only layout, not the underlying route architecture. The lifecycle from `0.4.0-alpha.6` (#95) remains unchanged in planned `0.4.0-alpha.7`; #97 adds only last-success timestamp presentation:
52
+
53
+ - **Surface ownership:** when an existing configured canonical `github-copilot` provider card is mounted, embed login/status/**Refresh models**/**Manage** there and suppress the separate footer account controller. If no such row is mounted, keep footer fallback usable; old Core retains its settings section. The shared account-state owner, including its in-flight authorization state, survives transfer only while another eligible surface remains mounted. Last-surface unmount or nonoverlapping declaration replacement stops polling. A later controller reads status and separately ensures missing/idle/stale/error/loading signed-in metadata without replaying the old forced-login action. Manual **Refresh models** now lives in **Manage**; errors retain a visible **Retry**.
54
+ - **Discovery triggers (#95):** successful explicit UI **Start sign-in**/account switch forces one bounded discovery after immediate or polled completion. Opening Models separately calls non-forcing `ensureModels()` once for missing/idle/stale/error/loading signed-in metadata; error re-entry retries only after shared cooldown with no same-mount loop; loading joins the existing Host flight to observe completion without extra network. Fresh ready cache needs no request, and true unavailable/empty models do not auto-retry. Status/details remain network-free, not the entire opening flow. Background credential/reset events clear Client state and read status, not force discovery on every token event; next open/use ensures metadata.
55
+ - **Freshness and authority (#95):** `github-copilot` settings default to `accountModelTtlMs: 86400000` (24h maximum reuse) and `accountModelFailureCooldownMs: 300000` (5min failure cooldown). One shared Host flight serves callers; no periodic metadata polling timer (the timestamp's display-only timer is separate). Last same-account metadata may display during TTL refresh/loading/error but never authorize requests. Credential/account/permission invalidation or proof expiry revokes request evidence immediately; the TTL does not extend tokens. Definitive `UNKNOWN_MODEL` triggers one bounded metadata refresh without message replay/model switching; generic HTTP/network errors are not guessed into that category.
56
+ - **Last-success display (#97):** `snapshot.discoveredAt` appears beside the model count outside **Manage**, without a duplicate inside. Past times use English relative text; the tooltip and accessible semantic `time` expose the full local date, time and time zone. Missing/invalid timestamps or no account hide it; future times use an absolute label. One mounted 60-second display-only timer updates the relative text without RPC/status/discovery, and is disposed when the timestamp disappears or the view unmounts. Pending/failed refresh keeps the last success time; successful refresh replaces it and sign-out clears it. Cache/discovery and authorization rules are unchanged.
57
+ - **Honest integration limit:** the published provider-card slot is additive and cannot replace Core **Edit/Delete**. Normal plugin discovery needs no manual model definitions, but the native editor stays available. Embedding account controls does not merge/remove canonical `github-copilot` and managed `github-copilot-preview`, copy credentials, rewrite configuration/history or migrate selection. Any optional real route removal still requires the [separate reviewed migration](./single-route-migration.md).
58
+ - **Acceptance still required:** test missing/idle/stale/error/loading open-time ensure versus fresh-cache no-request, shared Host flight, TTL/cooldown boundaries, display-only stale data, immediate revocation, definitive unknown-model refresh without replay, Manage-only manual refresh and visible Retry. Retain provider/footer/old-Core fallback, overlapping-owner transfer, final-unmount cleanup and once-per-explicit-login tests. Read the implementation PR's actual results before claiming these checks passed; documentation is not runtime or live OAuth evidence.
59
+ - **Browser/media evidence:** primary `copilot-model-freshness.png` and `copilot-model-refreshing.png` now show the actual alpha.7 built Client in isolated Edge with synthetic Remote/provider-shell fixtures. The timestamp check advanced a fake clock from 8 to 10 minutes with zero additional RPC, retained the prior time during pending/error, replaced it after successful refresh, cleared it and disposed the timer on sign-out, and verified a safe 375 px layout. Host TTL/cooldown timing is unit-test evidence, not screenshot evidence; no live Core or production authorization is claimed. Provider-entry/authorization PNGs remain historical alpha.5; older compact-account GIF/PNGs remain alpha.3.
60
+
61
+ ## Session-scoped Copilot follow-up (#99, planned alpha.8)
62
+
63
+ - **V3 ownership:** one global Host account and shared metadata source supply many models; explicitly selected/history-backed Sessions retain independent model context. Search facts use the captured initiating Session's effective request-header/config or explicit `GenerateOptions`, not another Session or future default C. Plans are cached per owner to prevent A/B different-model reuse/cancellation. No global current-model/search-status card is added.
64
+ - **Cold entrypoints:** Chat picker and `/model` `listModels()` ensure the shared managed source without visiting Settings first. Actual cold managed search performs a non-forcing shared ensure before deriving facts. The same 24h maximum TTL, 5min cooldown, account generation and capability/probe checks remain.
65
+ - **Native API limits:** a new Copilot draft warns that saving a native profile adds another real group, not another account. Add cannot be vetoed and Save cannot be disabled through the public additive slot. Public `session.selectModel` also writes the future global default; unselected empty Sessions can inherit it. Do not claim enforced one-route registration or immutable empty Sessions.
66
+ - **Delivery boundary:** code does not auto-migrate configuration, credentials, selections or history. After release, the operator must resolve approved Session/default choices separately; the config-only v1 Ops command performs no automated selection writes, then may compare-and-swap only the reviewed user-native profile path after ruling out base/journal conflicts, and read back registration. Any search allowlist change to `github-copilot-preview` is a separate reviewed Ops edit, never silent broadening. See [migration](./single-route-migration.md).
67
+ - **Request-context correction:** `Agent.options` remains Core's activation seed, not selected-model evidence. Real `installModelSelection` overrides request/assembly; the Session records effective `requestHeader().config` before tools. Capture the initiating Session's proven request context or use explicit `GenerateOptions`; otherwise traditional search is unavailable. Pending new selection before its next request must not expose the prior header as current prompt guidance.
68
+ - **Acceptance/evidence:** use real `installModelSelection` to check concurrent A/B models against unchanged activation/default seed C, reject unproven or stale-header prompt guidance after a pending selection, cold Chat and `/model` discovery, cold search/probe, owner invalidation, missing initiator, Add warning with Save retained, and CAS-conflict refusal/readback. The retained alpha.7 screenshots illustrate timestamps, not these new behaviors or a completed migration; consult the implementation/ops results separately.
69
+
70
+ ## Live migration evidence follow-up (#101, planned alpha.9)
71
+
72
+ - **Entry point:** no-argument `githubCopilot.migrationStatus()` reads public live leaves synchronously. Generic `session/list` may be stale and inventory lacks loaded-version evidence. The new result carries loaded build `plugin.name`/`plugin.version`, protocol 1, observation time, five structural capability flags and `complete.sessions/defaultSelection/routes`; unknown/incomplete required selection or route evidence must fail the maintenance gate, not be treated as absence. Idle Agents normally have `activeRequestSelection: null`.
73
+ - **Session scope:** pending model projection → recorded request-header config → current default only for genuinely empty live Sessions with known projection state. Each row has selection provenance; a running Agent's separate `activeRequestSelection` is its latest recorded header, not proof of an in-flight LLM call. `routes.nativeConfigured`, `nativeRegistered` and `managedRegistered` distinguish effective config from registry state.
74
+ - **Read-only boundary:** no authorization status/discovery, credential access, network, settings/Session mutations, normal UI or global model/search card. Seven ordinary authorization Remotes retain their existing codec; the eighth Remote uses its own strict `GitHubCopilotMigrationStatus` codec. Source ownership is `src/migration-status.ts` plus the controller/Remote bridge; focused evidence is `tests/migration-status.spec.ts`.
75
+ - **Limits and Ops handoff:** `historyScope: live-agents-only` excludes cold history. Require operator acknowledgement that older conversations may need explicit selection on resume. Version/capability self-reports are not full Desktop/Core byte attestation or an atomic cross-namespace guarantee; recheck just before CAS. The separate planned Ops `tools/migrate-copilot-managed-route.ps1` in `cloga/dsh-windows-ops` is config-only v1, with no automatic Session/default writes, plugin install, restart or full Desktop-baseline acceptance. This document does not establish its publication, installation or a completed live migration.
76
+ - **Acceptance:** verify loaded identity, missing/malformed seams and incomplete evidence, fresh live-Agent enumeration, pending-versus-recorded selections, truly empty defaults, running-header caveats, effective-config/registry distinction, bounded strict codec and zero side effects. Read actual test/Ops results rather than treating this checklist as a passing report.
77
+
78
+ ## Core alpha2 compatibility follow-up (#105, planned alpha.11)
79
+
80
+ - **Exact targets:** retain rc.2, rc.1 development dependencies, `0.1.3-alpha.1` (`d347e703908d0406b7a7ef80e3a0e594d86b2215`) and `0.1.5-alpha.1` (`5dda764ed3aa172535a7967b06ff95d9cbfe536a`); add current `0.1.5-alpha.2` (`b2e3b2a0125854567a4a5fcba75782e42fe84901`), `0.1.5-rc.1` (`183f08e9c6dde7e36cd2318eaee70b0da08fb35e`) and `0.1.5-rc.2` (`fb2c4b9e698e30edb738bca4cf0618587db7d203`). Peer admission is not full runtime proof.
81
+ - **Actual break and narrow fix:** alpha2 `ResolvedPiAiProviderProfile` adds required `modelErrors`, optional `piProvider` and optional `catalogError`. `PiAiAdapter.modelOf` unconditionally calls `profile.modelErrors.get(model)`. The plugin-owned profile now supplies an empty `Map<string, string>` because only already validated account models enter its provider. It retains an actual `piProvider`, so no fabricated `catalogError` is needed. Unknown/rejected models remain fail-closed. No upstream provider, prototype, model catalog or dependency artifact is patched.
82
+ - **Runtime regression:** the existing unchanged tagged-source resolver attests exact Git HEAD, clean tracked sources, public package/export paths and real Context/LlmRuntime/PiAiAdapter class identity. `preview-route.spec.ts` resolves, prepares and synthetically streams an account model through the actual selected adapter, not a stub; the same suite runs against all three alpha pins and the rc.1 development adapter. Session/projection and eight strict Remote fixtures run on both 0.1.5 alphas. File projection and opaque replay remain native-owned.
83
+ - **Other consumed seams audited:** alpha1-to-alpha2 settings mutation/CAS and section installation, credential/authorization APIs, Agent/default-model selection, Session request header and model-selection projection, public gateway, provider-card slot props and Chat assistant-node registration retain the consumed contract. Core adds optional provider-directory `error`, repair UI and Add gating; these do not authorize plugin route migration or alter the account-controller ownership. SessionController file reveal/desktop APIs and API-remotes feedback contributions are additive and unconsumed. Chat file-link closing gains a session argument, but this plugin delegates native rendering and does not call that seam.
84
+ - **In-band system authority:** the broader 0.1.5 audit found newer system-role messages would be demoted by the legacy Anthropic inline serializer. The preflight now delegates any such Anthropic request unchanged to Core before auth/probe/wire; leading and nonleading positions are covered. This deliberately limits inline hosted search, does not rewrite histories, and leaves the legacy Responses inline mapping of system content to user input text unchanged (not filtering). Managed requests always delegate to native transport.
85
+ - **Evidence boundary:** CI extends the Windows/Linux pinned matrix and release rechecks the current tagged runtime before packing. Local tests use synthetic credentials/network and unchanged Core source fixtures only; no Core implementation changes/build, live install, OAuth, provider calls, Desktop restart or GitHub publication is implied. Consult the task's exact command results for passes/skips; documentation and static markers alone cannot certify runtime or live behavior.
86
+
87
+ ## Remaining limitations and follow-up acceptance criteria
88
+
89
+ - **Initiator compatibility:** planned alpha.8 derives search from the captured initiating Session's effective request-header/config or explicit `GenerateOptions`, never a global default. Without `agents.currentInitiator`, traditional hosted search reports a named unavailable diagnostic. Explicit marked `GenerateOptions` still supports guarded inline routing, possibly uncached when no owner exists. An OAuth credential notification during initial lazy discovery is indistinguishable from external account change via public status: the first search fails closed with `WEB_PROVIDER_UNAVAILABLE` before probe/wire; a later user/driver request may retry, never automatically. Do not claim seamless first-attempt credential refresh. Verify the initiating-context seam on each pinned Core before extending compatibility claims. Capability/probe and route allowlists still gate search; owner isolation is not unrestricted search support.
90
+ - **Live evidence:** readonly status describes the stored account snapshot, not fresh entitlement or a successful request. A credential update during an in-flight search fails closed; a new explicit request can retry with fresh proof. There is intentionally no automatic retry loop.
91
+ - **Browser acceptance:** pure React element tests and synthetic screenshots do not establish clipboard permissions, keyboard focus, narrow layouts, pending RPC unmount behavior or a loaded Client update. Add mounted/browser tests with synthetic codes before calling those behaviors fully certified.
92
+ - **Minimum runtime/full baseline activation:** local and CI Node24 plus source/config checks are not full Node22.19/live-plugin certifications. Run those explicitly before expanding deployment claims.
93
+ - **Security-sensitive diagnostics:** any suspected credential/session/endpoint exposure requires private triage under SECURITY.md with synthetic-sentinel tests. No real secret exposure was established by this audit; do not publish raw logs or provider bodies as evidence.
94
+
95
+ No attempt is made here to rewrite old commits/tags, auto-enable account models, alter live profile-wide Web provider selection, or introduce a second LLM adapter. The runtime guard can be reverted independently; the tooling and documentation remain useful without installing a plugin release.
@@ -0,0 +1,46 @@
1
+ # Copilot compatibility acceptance
2
+
3
+ This change is not complete when a GPT-6-only prototype works or a PR is merely opened. The requested result is a plugin-only, data-driven Copilot integration, including public reasoning summaries, followed by verified delivery and local upgrade.
4
+
5
+ ## Scope
6
+
7
+ Follow the [plugin-only boundary](../AGENTS.md#plugin-only-implementation-boundary). Reuse the published DSH/pi-ai adapter and OAuth interfaces. Do not modify Core, dependency artifacts, private registries, or shared upstream catalogs. A plugin-owned Copilot route may serve multiple account-advertised models; this is configuration and lifecycle integration, not another implementation of generic wire serialization.
8
+
9
+ ## Required behavior
10
+
11
+ 1. **Account-driven discovery.** Read the provider's model metadata with bounded requests and account-scoped cache lifetime. A new model with sufficient supported metadata must not require another model-ID patch or plugin release. A new model name alone is insufficient evidence of its protocol or capabilities.
12
+ 2. **Protocol selection from evidence.** Use advertised `supported_endpoints` for Responses, Chat Completions, or Anthropic Messages. Native pi catalog choices may be retained only when compatible with the advertised endpoints. Do not infer a protocol from a GPT, Gemini, Claude, or other name prefix. Missing or unsupported metadata produces an explicit diagnostic, not a guessed route.
13
+ 3. **Capabilities stay distinct.** Preserve context, input and output limits separately; do not invent large limits or unsupported reasoning levels. Respect disabled/unconfigured policy, picker eligibility and tool/stream support. Provider metadata does not authorize changing account policy.
14
+ 4. **One credential lifecycle.** All plugin-owned routes share the canonical Host-only Copilot OAuth record. Model discovery, refresh and in-flight requests remain bound to the same account generation. Account switches, entitlement removal, disposal and stale callbacks must fail closed without copying credentials or changing other providers.
15
+ 5. **Thinking is included.** Preserve public summary deltas and final-only summaries without duplicate text. Validate selected effort and keep native default semantics. Empty or encrypted-only responses must not become fabricated explanations. Native replay, images and file projection retain their owning adapter's behavior; opaque replay is not displayed or rewritten into raw reasoning text.
16
+ 6. **pi 0.85.1 compatibility.** Pin and test the requested published SDK version, not just a GitHub release label. Its Copilot GPT-6 catalog protocol must not override contrary provider endpoint evidence. An updated catalog entry is not sufficient grounds to retire a correction if the protocol/capabilities are still wrong.
17
+
18
+ ## Regression evidence
19
+
20
+ - Include GPT-6 Astra, Gemini 3.8 Flash and GPT-5.6 Sol Fast metadata fixtures, plus arbitrary unseen IDs on all three supported protocols. The unseen-ID cases must pass without adding those IDs to implementation tables.
21
+ - Cover missing/conflicting endpoints, missing/invalid limits, duplicate conflicting IDs, explicit denial, stale account snapshots, unsupported reasoning labels, unknown fields and hostile extra getters.
22
+ - Exercise real published adapter/SDK code against local synthetic HTTP for all supported protocols, including tools, public summaries, two-turn opaque replay, images and interruption/retry paths. Synthetic results are not claims of live account availability.
23
+ - Run source/test typechecks, complete repository tests, built Host/Client/Remote smoke, archive verification, and relevant unchanged-Core compatibility fixtures. Do not suppress type or test failures to bridge incompatible SDK versions.
24
+
25
+ ## Explicit public-interface limits
26
+
27
+ - The published Core model-info contract exposes combined context capacity, not a separate prompt-token budget. The plugin preserves the provider's input limit but cannot claim Core automatically enforces it. When that input limit is smaller than the combined context, the discovered model must show `INPUT_LIMIT_NOT_ENFORCED_BY_CORE`; server rejection remains possible. Do not silently replace combined context capacity with an invented input budget or add a Core patch.
28
+ - Advertised reasoning labels that the native SDK cannot express must show `REASONING_EFFORTS_UNSUPPORTED` rather than being silently hidden or guessed. Such a warning need not disable ordinary model requests.
29
+ - The Core-facing adapter integration uses the public `streamSimple` path. Its advanced protocol-specific `stream` entry must reject explicitly rather than accepting incompatible client objects from another SDK version. The event-stream compatibility boundary must verify the full public surface at compile time and preserve the original stream object.
30
+ - Cached catalog entries do not authorize requests independently. A change in the current grant's model-permission list invalidates old proofs even when the token and account identity are unchanged; a new server-enabled model absent from the earlier grant list remains discoverable without rewriting the grant.
31
+
32
+ ## Delivery and local acceptance
33
+
34
+ - Open the plugin PR with tests, evidence limits, migrations and rollback. Merge only after required checks and review are satisfied; the user has explicitly included merge in this task's acceptance scope.
35
+ - Publish the aligned version through the protected Release workflow and verify the release/tag/commit/assets/checksum.
36
+ - Upgrade the user's actual local profile to that verified plugin artifact; do not infer the profile from a README example. Verify installed bytes and loaded runtime separately.
37
+ - The user has requested local upgrade. Explain any session-interrupting restart before doing it and obtain acknowledgement of that interruption. Do not install a modified Core as part of the upgrade.
38
+ - Report remaining limitations honestly, including unavailable provider metadata, unsupported protocols, optional summaries and any unperformed live checks.
39
+
40
+ ## Metadata references
41
+
42
+ - [Microsoft Copilot API types](https://github.com/microsoft/vscode/blob/main/src/typings/copilot-api.d.ts)
43
+ - [Endpoint-driven Copilot discovery implementation reference](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/models.ts)
44
+ - [pi v0.85.1 release](https://github.com/earendil-works/pi/releases/tag/v0.85.1)
45
+
46
+ These references guide parsing and tests; they are not a guarantee that the upstream discovery schema will never change. Schema drift must be surfaced rather than silently guessed.
@@ -0,0 +1,134 @@
1
+ # Default dual distribution
2
+
3
+ Refs #43. This change supersedes the original Release-only distribution decision
4
+ at the user's request; that closed issue did not originally request npm.
5
+
6
+ Every new version is distributed through both an immutable GitHub Release and
7
+ the public npm registry. The name `dsh-github-copilot` remains provisional until
8
+ an authorized maintainer verifies availability or ownership. Never publish an
9
+ empty placeholder package, reuse historical alpha.17, change immutable assets,
10
+ or describe a local archive as a published package.
11
+
12
+ ## Authorization and readiness
13
+
14
+ Use only organizationally approved registry access and build environments.
15
+ A blocked corporate registry is not authorization to use a VPN, proxy, mirror,
16
+ personal device or GitHub Actions as a bypass. Obtain the applicable approval
17
+ before any registry traffic or publication. Local tests use synthetic registry
18
+ responses; they do not establish registry connectivity, ownership or live
19
+ Desktop compatibility.
20
+
21
+ Before the first release, confirm the package name and the maintainer's write
22
+ access, public repository visibility, 2FA, approved publication environment, and
23
+ the next unused version in both registries. Only then merge the prepared version
24
+ through the normal reviewed PR path. Version changes include package.json,
25
+ deployment-baseline.json and both README URLs; `publishConfig.tag` must agree
26
+ with the channel. Do not silently rename a taken package.
27
+
28
+ ## First package bootstrap is a maintainer operation
29
+
30
+ The normal main pipeline publishes the GitHub Release first, then requires npm
31
+ publication. Until bootstrap/trusted publishing is configured, the npm step
32
+ fails explicitly and the workflow is **not fully delivered**. This is intentional,
33
+ not a successful skipped step. The already published GitHub archive is retained.
34
+
35
+ 1. From the verified immutable Release, obtain its original versioned tarball
36
+ and `SHA256SUMS`. Verify the annotated tag/commit, Release state, asset digest,
37
+ archive contents and SHA-256. Do not repack or edit this archive.
38
+ 2. In an approved environment, the authorized maintainer verifies `npm whoami`
39
+ is the intended account (`cloga`) and performs the first ordinary
40
+ `npm publish <original-release.tgz> --access public --tag alpha --ignore-scripts --registry=https://registry.npmjs.org/`
41
+ with interactive 2FA or explicitly authorized legitimate publishing
42
+ credentials. `<original-release.tgz>` is the exact downloaded real artifact,
43
+ not a directory or placeholder. Do not print, copy or commit credentials, or
44
+ weaken 2FA to make this succeed.
45
+ 3. Read back the exact npm version and compare `dist.integrity` with the
46
+ archive's SHA-512 SRI. Configure trusted publishing, then rerun the failed
47
+ CI release job on its original revision. It recovers the same bytes and
48
+ verifies the existing npm version without another publish.
49
+
50
+ **`npm stage publish` cannot bootstrap a nonexistent package.** The
51
+ [official npm stage prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-stage/)
52
+ require the package to already exist. For an existing package, staged publishing
53
+ is an optional separately approved process: a maintainer reviews Staged Packages
54
+ on npmjs.com and approves with 2FA. Staged is **pending approval**, not npm live.
55
+ It reserves the version; a conflicting normal publish must fail, not reject or
56
+ overwrite the stage automatically. Approval can update its immutable chosen
57
+ dist-tag, so review channel ordering again before approval. This repository does
58
+ not add a long-term manual/opt-in step to normal releases.
59
+
60
+ ## Trusted publishing for subsequent versions
61
+
62
+ [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) requires
63
+ npm CLI >=11.5.1 and Node >=22.14.0. The publishing workflow uses Node 24 and
64
+ pins npm 11.5.1; package development retains the pinned pnpm version.
65
+
66
+ In the package settings, an authorized maintainer configures:
67
+
68
+ | Field | Value |
69
+ | --- | --- |
70
+ | Provider | GitHub Actions, GitHub-hosted runner |
71
+ | Organization or user | `cloga` |
72
+ | Repository | `dsh-github-copilot` |
73
+ | Workflow filename | `ci.yml` (the caller, **not** `release.yml`) |
74
+ | Environment | Unset; the current release job declares none |
75
+ | Allowed actions | Enable direct `npm publish` |
76
+
77
+ Both caller and reusable child grant `id-token: write`. New trusted publisher
78
+ configurations may default to stage-only permission: direct publish permission
79
+ is an explicit maintainer choice, not implied by a successful stage. No
80
+ `NPM_TOKEN` or `NODE_AUTH_TOKEN` is configured in normal release CI; missing OIDC
81
+ fails loudly rather than falling back to persistent tokens. npm creates
82
+ provenance automatically for supported public-repository trusted publishes.
83
+ Do not change token/2FA policy as part of an automated repair.
84
+
85
+ ## Same bytes, recovery and tags
86
+
87
+ The repository-wide release concurrency group serializes releases and remains
88
+ non-cancelling. External/manual publishers must coordinate with it: npm does
89
+ not provide atomic compare-and-swap for a dist-tag. Do not run concurrent manual
90
+ publishes or approvals while a release job is publishing.
91
+
92
+ After the full compatibility gate, a new release packs one tarball with
93
+ `pnpm --config.ignore-scripts=true pack --pack-destination artifacts`; verification
94
+ already ran prepack's baseline check and built the package, so packing does not
95
+ rebuild it. A retry
96
+ first inspects the existing exact tag/Release and downloads the original
97
+ uploaded archive. Recovery verifies asset sizes/digests, checksum and local
98
+ build equality; it never repacks or overwrites an existing archive. Partial
99
+ drafts with a tarball can recover their checksum; published incomplete or
100
+ non-immutable Releases fail closed. GitHub publishing reconciles its immutable
101
+ assets, and npm publishes the same archive with lifecycle scripts disabled.
102
+
103
+ The npm step reads package/version state before writing. Only E404 means
104
+ absence; auth, TLS, timeout, malformed metadata and registry failures stop
105
+ delivery. An existing exact version is accepted only with matching SHA-512 SRI
106
+ and a channel tag at that version or a newer one. Conflicting bytes fail closed.
107
+ An uncertain write is not retried in-process; rerun the workflow to reconcile.
108
+ A matching version with an older/missing tag requires maintainer review; the
109
+ workflow does not silently repair it with separate dist-tag writes.
110
+
111
+ `alpha`, `beta`, and `rc` versions use those tags, never `latest`. Stable
112
+ versions use `latest`. SemVer comparison (not lexical sorting) prevents tag
113
+ downgrades. Publishing a missing older version when its tag already points to a
114
+ newer version fails before writing; reconciling an already published older
115
+ version does not lower that pointer.
116
+
117
+ Report GitHub URL/tag/commit/asset/SHA-256 and npm version/channel/SRI separately.
118
+ GitHub success followed by npm failure is partial delivery, not success.
119
+ There is no historical bulk backfill and no silent npm opt-out.
120
+
121
+ ## Installation preflight still applies
122
+
123
+ After both channels are verified, official Desktop accepts the exact
124
+ `dsh-github-copilot@<version>` npm spec in its package manager. It does not accept
125
+ the Release URL or local tarball in that UI. Follow the existing README
126
+ `scripts/check-search-composition.mjs` preflight before changing configuration;
127
+ the script remains in the package. A successful package lookup or install does
128
+ not prove runtime activation or a successful Copilot request.
129
+
130
+ For separately managed CLI profiles only, retain the required `--profile` on
131
+ `dsh plugin` commands. Never use the CLI to write a reserved Desktop-managed
132
+ profile, copy into node_modules, patch Core or restart active Sessions without
133
+ their separate approvals. Installed-on-disk and loaded-runtime evidence remain
134
+ distinct.
@@ -0,0 +1,53 @@
1
+ # Provider-aware web search routing (issues #112 and #118, alpha.17)
2
+
3
+ ## Requested behavior
4
+
5
+ The initiating Session chooses the native route, not the global default model. `github-copilot-search-routing.searchMode: auto` prefers eligible native search for canonical Copilot and verified plugin-owned `github-copilot-preview` selections, then uses `defaultSearchProvider` for every model without eligible native search. `fixed` always uses that provider. `none` disables only the default/fixed path; chat remains available. Provider-specific credentials and model selection remain provider-owned: `github-copilot.searchModel` supplies the explicit account-authorized Responses model when Copilot search runs independently of the chat model.
6
+
7
+ All implementation belongs to this plugin. No Core source, deployed Core artifact, prototype, private registry, credential copy or global chat-model choice is modified. The original official WebRuntime configuration, including its selected search/fetch providers, is preserved. The routed facade mirrors public search-provider registrations so exact-id selection works for official and community providers without reading Core's private registry.
8
+
9
+ ## Composition
10
+
11
+ Modern Core may mount `tool-web` in agent presets while disabling its historical Host row. Modifying that disabled row is insufficient. The bundle therefore changes the service composition, not the consumer or preset:
12
+
13
+ 1. An id-and-name-guarded patch moves the existing `web` / `@deepseek-ai/dsh-web` row into the named `github-copilot-original-web` service realm without replacing its config.
14
+ 2. The plugin's `web-delegate` entry exposes that same official service through a public, Fiber-owned bridge. It does not read any provider registry internals.
15
+ 3. The plugin-owned `routed-web` service occupies the ordinary Host `web` scope. It forwards provider registration and fetch to the original service while retaining Fiber-scoped handles for exact-id search dispatch.
16
+ 4. Auto-mode Copilot searches use the captured initiating-session route. Fixed/default Copilot searches use the explicit provider-owned `searchModel`. Other provider ids dispatch through the mirrored public registration. A separate private official WebRuntime supplies native request validation and source capping on every routed branch.
17
+ 5. Native `tool-web` consumers, including those in agent presets, retain their original schemas, configured query/source limits, batching, execution policies, timeout metadata, formatting and presentation.
18
+
19
+ This is a plugin-owned subclass and explicit public service composition, not a replacement of Core prototypes or a mutation of a live registered service. It affects all `ctx.web.search` consumers with eligible Copilot context, not only one tool name. The bundle requires the standard enabled top-level official Host `web` row, static provider config and no prior isolation. The patch name guard does not make its separate inserted rows conditional: installing into a custom/missing/disabled/isolated web composition can prevent startup. Run the required read-only `scripts/check-search-composition.mjs` preflight over the existing profile before installing; it uses public readers and composition without calling write-capable `loadProfile` normalization. It compares a routing-free baseline with the actual post-install candidate at the existing bundle position (or the append position for first install), including profile, home and supplied launcher patches. Nonempty disposable `cordis.yml` roots are refused because normal CLI launch resets them; late isolation resets or an original web row inserted only after the routing guard are also refused. `dsh plugin add` does not automatically enforce this step. Unsupported shapes must be refused before mutation, not described as safe no-ops.
20
+
21
+ ## Routing and fallback
22
+
23
+ `src/search-routing.ts` retains the native Copilot primary/fallback policy using captured provider/model leaves, a verified managed-route ownership flag and operation-local dependencies. The outer routed-web policy chooses whether to invoke that native path or an exact registered provider. It never substitutes the future global chat model. When `github-copilot-hosted` is selected independently, it uses only the explicit provider-owned `github-copilot.searchModel`; successful empty results do not trigger another fallback.
24
+
25
+ `src/deepseek-search-fallback.ts` lazily constructs the public official `DeepSeekSearchProvider`; the upstream class owns its wire protocol, redirect policy and result parsing. The helper reads the existing `web-search-deepseek` settings, public launch-environment snapshot and credential service at operation time, and records the auxiliary request without auth headers/credential fields on the captured Session. Fallback API bases must be HTTP(S), without userinfo, query or fragment; invalid bases fail with a fixed diagnostic before auth, recording or fetch, preventing conventional URL-embedded credentials from entering the request log. Allowed bases/paths are not rewritten. The adapter id identifies the implementation; a custom configured endpoint need not be the default DeepSeek host. No credentials are retained as a new store.
26
+
27
+ Fallback notices are part of `WebSearchResult.content`, not merely an outer Tool result `content` or `meta`: Core may regenerate those outer fields from the canonical value. The notice names DeepSeek fallback, a bounded error reason and possible DeepSeek API charges, and explicitly says that the result is not from Copilot. Per-query notices remain available when the official consumer merges multiple queries. No search-time approval dialog is introduced.
28
+
29
+ `routeWebSearch` defaults to true; disabling it delegates through the original official service. Plugin-owned cross-provider settings live under `github-copilot-search-routing`: `searchMode` is `auto` or `fixed`, and `defaultSearchProvider` is an exact registered provider id or `none`. The Models page owns this routing card. `github-copilot.searchModel` remains provider-owned and is required when `github-copilot-hosted` serves independently. Existing `searchFallback` continues to govern failure after auto mode has selected native Copilot search only when `deepseek-official` is also the configured default; another selected default is never silently replaced by a paid DeepSeek request. The existing master enable switch, provider allowlist, lazy account metadata and native capability proof remain in effect.
30
+
31
+ ## Review hardening
32
+
33
+ The initiating router also captures the actual managed proof lifetime and its live grant deadline; ordinary metadata TTL refresh does not extend a captured deadline or count as credential revocation. Read-observed credential changes and native authorization failures invalidate continuity before generic probe/plan errors can become fallback eligibility. The same operation-local guard is checked in the native fallback's pre-dispatch recording callback, after asynchronous credential resolution and before either recording or fetching. Rejecting only after the fallback response arrives would be too late to prevent charges.
34
+
35
+ The added regressions cover silent expiry/replacement, native final assertions, cold discovery, supported TTL refresh, delayed fallback credentials, literal-key authorization boundaries, cancellation, and legitimate transport-fallback controls. Supplemental public-API and in-memory reproductions are not a substitute for full CI on the final repaired commit or separately authorized live acceptance.
36
+
37
+ ## Original verification inventory (before additional review regressions)
38
+
39
+ - `tests/search-routing.spec.ts`: 20 synthetic policy regressions covering route preservation, captured selections, fallback disclosures, cancellation, proof invalidation, unknown fallback provider refusal and simultaneous operations.
40
+ - `tests/routed-web.spec.ts`: 16 tests using actual official AgentRegistry, ToolRuntime, SystemPrompt, scoped official search consumers and WebRuntime instances. They cover native validation/caps, exact-id registered-provider dispatch, duplicate-query collapse, surrounding execution middleware, pre-execute denial, simultaneous Copilot/DeepSeek calls, unchanged fetch, provider Fiber disposal and fallback notices surviving canonical rendering/web-card metadata. Retained handles are rejected after facade disposal; in-flight Copilot work is aborted and drained; the official WebRuntime prototype remains unchanged.
41
+ - `tests/deepseek-search-fallback.spec.ts`: 37 keyless tests composing the real public DeepSeek provider, covering option snapshots, settings/env/credential precedence, request recording, cancellation, redirects, result parsing, safe backend/error metadata and rejection of unsafe API bases. Concurrent searches retain distinct endpoint/model disclosures from their own actual option snapshots.
42
+ - `tests/scripts/search-composition.test.mjs`: 22 pre-install checks, including real public parser use without initialization/normalization or secret output; custom/dynamic/nested/disabled/isolated web layouts are rejected before installation.
43
+ - `tests/routed-web-loader.spec.ts`: public patch composition checks plus an actual Loader boot of the built facade/delegate and official consumers. Since Vitest lacks Node's exposed internal module loader, this test resolves this plugin's public export names to file URLs before Loader import; the realm/config patches and built implementations are unchanged. Main authorization/chat activation is disabled in this isolated test and replaced only by a synthetic routing service. This is not live account acceptance.
44
+ - Existing inline-search tests use their pre-existing synthetic context; its fixture now models public `ctx.provide` registration and disposal required by the new router service. Nine additional main-router cases exercise non-Copilot delegation, owned managed primary search, real official fallback transport with synthetic responses, disabling fallback/routing, allowlist exclusion, user-facing disclosure guidance and credential invalidation before any fallback request.
45
+ - `verify-tagged-core.mjs` now selects the policy, real-consumer and fallback suites in addition to prior adapter tests. CI/release install the unchanged official search dependency closure; tooling regressions reject missing/reordered closure steps. This is test wiring, not proof that the remote matrix has run.
46
+
47
+ ## Remaining before shipping
48
+
49
+ 1. Complete the full verification gate and update deployment/agent evidence inventories, public export smoke and both READMEs.
50
+ 2. Add end-to-end coverage of the main router's managed metadata, fallback credentials/options, cancellations during discovery/fallback, mixed multi-query provenance, and complete unload/reload behavior.
51
+ 3. Run retained exact Core-baseline fixtures, not only the development artifact. Do not infer seven-pin runtime compatibility from type checks.
52
+ 4. Align the new package version, release metadata and package/archive tests.
53
+ 5. Run bounded live Copilot hosted-search and permitted-fallback acceptance separately. A successful chat request does not prove hosted search. No live profile installation, global search selection change or restart has occurred for this work.
@@ -0,0 +1,115 @@
1
+ # Optional migration to the account-discovered Copilot route
2
+
3
+ [English guide](../README.md) · [中文操作说明](#中文操作说明)
4
+
5
+ ## Result and boundaries
6
+
7
+ **V3 target:** one global Copilot account, many shared account models, and independent explicitly selected/history-backed Sessions. Plugin code does not auto-migrate configuration or enforce a single registration. Actual managed-only deployment is an approved Ops step after release: the operator resolves approved Session/default choices separately, then config-only v1 maintenance may CAS-remove the reviewed user-native profile and verify registry readback. The maintenance command does not write Session/default selections. No bulk history/settings/credential mutation or hidden groups.
8
+
9
+ The new native Copilot draft warns that **Save** adds another real model group, not a second account. Public additive APIs cannot veto native **Add** or disable **Save**. This is warning-only; users can recreate a native group after migration. Public `session.selectModel` also saves the future global default, so unselected empty Sessions may inherit it even while other selected/history-backed Sessions retain their own context. There is no global current-model/search-status card.
10
+
11
+ New installations use **GitHub Copilot**, with the stable actual route ID `github-copilot-preview`. The planned `0.4.0-alpha.9` UI embeds login, status, **Refresh models** and **Manage** in an existing configured canonical `github-copilot` provider card while suppressing the separate footer account controller. When no such row is mounted, footer fallback or the old-Core settings section remains usable. The shared account-state owner survives transfer only while another eligible surface remains mounted. Unmounting the last surface or replacing declarations without overlapping mounts stops polling. A later controller reads status and separately ensures missing/idle/stale/error/loading signed-in metadata, without replaying the old forced-login action. Manual **Refresh models** is inside **Manage**; errors expose **Retry**. This is account-control integration, not a merger or removal of the two actual routes; credentials, configuration, history and selection stay unchanged by that integration.
12
+
13
+ Normal discovery needs neither manual model definitions nor routine refresh clicks; the additive slot still cannot replace Core **Edit/Delete**, and the native editor remains. Successful explicit UI sign-in/account switch forces one discovery. Opening Models separately uses non-forcing `ensureModels()` for missing/idle/stale/error/loading signed-in metadata; error re-entry can retry after shared cooldown, never in a same-mount loop; loading joins the existing Host flight without extra network. Fresh ready cache makes no request, and true unavailable/empty models do not auto-retry. Status/details remain network-free. Defaults under `github-copilot` are `accountModelTtlMs: 86400000` (24h maximum reuse) and `accountModelFailureCooldownMs: 300000` (5min); shared Host single flight, no periodic metadata polling. A separate mounted 60-second display-only timer updates the relative timestamp without RPC/status/discovery calls. Background credential/reset events clear Client state and read status, not force every token event; next open/use ensures metadata.
14
+
15
+ The alpha.7 last-success timestamp (`snapshot.discoveredAt`) appears once beside the count outside **Manage**: English relative text with a full local date/time/zone tooltip and accessible semantic `time`. Missing/invalid timestamps or no account hide it; future values use absolute text. Pending/error preserves the last success, a successful refresh supplies a new time and sign-out clears it. The display timer is disposed with the timestamp or unmount; it does not alter cache/discovery behavior.
16
+
17
+ Last same-account metadata may display during TTL refresh/loading/error, never authorize requests. Credential/account/permission invalidation or proof expiry immediately revokes evidence; TTL does not extend tokens. Definitive `UNKNOWN_MODEL` triggers one bounded metadata refresh, with no message replay or model switching; generic HTTP/network errors are not guessed to mean unknown models. New models still require supported account metadata. The README's primary alpha.7 model-freshness/model-refreshing PNGs show the built Client in isolated Edge with synthetic Remote/provider-shell data, not live Core or production authorization. Host TTL/cooldown timing requires unit tests, not screenshots. The provider-entry/authorization PNGs remain historical alpha.5 and older compact-account GIF/PNGs alpha.3.
18
+
19
+ Existing `llm-pi-ai.providers.github-copilot` profiles are not automatically removed. Until you explicitly remove a reviewed legacy profile, both real routes may remain listed. This guide removes that configuration, not merely its display: after removal and catalog refresh, the composer picker and `/model` both list only the managed Copilot group. Discovery failures produce diagnostics; they do not enable a fallback static catalog.
20
+
21
+ There is still only one OAuth record, `llm-pi-ai/github-copilot`. Keep the `llm-pi-ai` plugin and authorization services mounted: an empty provider configuration does not mean the OAuth method must be removed. **Do not sign out, delete credentials, remove the companion or remove the native authorization plugin to hide a model group.** Do not use `models: []` as a disable mechanism; it can mean the default catalog, not no models.
22
+
23
+ This is a manual migration with separate approvals for installation, any real model request, default/session changes, Host interruption and configuration edits. Reading this guide or installing a new package does not authorize those changes.
24
+
25
+ ## Read-only readiness and maintenance scope (alpha.9)
26
+
27
+ Use no-argument `githubCopilot.migrationStatus()` for a fresh observation of all live Agent Sessions, rather than relying on possibly stale generic `session/list` or unversioned plugin inventory. It reports the loaded plugin build's `plugin.name/version`, `protocolVersion: 1`, `observedAt`, `capabilities` (`agentsList`, `sessionProjections`, `settingsCas`, `providerRegistry`, `defaultSelection`) and `complete` flags for sessions/default/routes. Missing capability, false completeness or null required selection/route evidence means unknown; do not infer a safe absence. Idle Agents normally report `activeRequestSelection: null`.
28
+
29
+ `effectiveSelection` follows pending projection, then request-header config, then default only for genuinely empty live Sessions with known projection state; `selectionSource` identifies that choice. Running Agents separately expose `activeRequestSelection`, the latest recorded header—not proof of an in-flight LLM call. Route flags distinguish native effective configuration from native/managed registration. The method does not call auth status/discovery, access credentials/network or mutate settings/Sessions, and adds no normal UI.
30
+
31
+ Protocol v1 declares `historyScope: live-agents-only`: cold stored histories are not inspected. Before migration the operator must acknowledge that those conversations may need explicit model selection on resume. Loaded-version and structural capability self-reports do not attest all Desktop/Core bytes, and the observation is not an atomic cross-namespace transaction; recheck immediately before CAS.
32
+
33
+ The planned `tools/migrate-copilot-managed-route.ps1` in `cloga/dsh-windows-ops` is separate **config-only v1 maintenance after plugin release**. It does not automate Session/default selection writes, inspect cold history, install/restart DSH, or certify a full Desktop baseline. Resolve any required Session/default choices manually with separate approval before running it. The planned path is not evidence that the command is published/installed or that live migration has completed.
34
+
35
+ ## Review before removal
36
+
37
+ 1. **Run the updated plugin first.** Confirm the active profile has actually loaded the absence-preserving plugin and matching Host/Client artifacts targeted by `0.4.0-alpha.9`. The provider-integrated UI does not require this optional route removal. Installed-on-disk is not loaded-runtime evidence. Do not remove the profile while an older plugin is running: older versions may recreate it during login, startup or authentication refresh. Use a published, checksum-verified release when available; a planned version number is not proof of publication.
38
+ 2. **Verify account discovery.** Open Models, confirm sign-in, let automatic ensure complete if needed, and inspect accepted models, rejected IDs and capability warnings. Use **Retry** on error or **Manage → Refresh models** only for an intentional forced update. Confirm the intended model exists on `github-copilot-preview`. Metadata acceptance is not a successful transport test. An optional explicit test request needs the user's approval and consumes the provider's normal resources; report its result separately. Do not repeatedly sign in or disable validation to force a missing model.
39
+ 3. **Choose defaults and current sessions deliberately.** Select accepted managed models only for the Sessions explicitly approved for migration, then confirm the approved future default. Core `session.selectModel` also writes that default; do not assume an empty unselected Session is unaffected. Keep the same model ID and reasoning effort only when the managed route actually advertises and supports them. Otherwise ask for a supported choice; do not guess a replacement or silently change effort. Verify the selections before removing their previous route.
40
+ 4. **Inspect other references.** Review presets, task configurations and paused conversations for selections of `github-copilot`. Do not bulk-rewrite conversation records, replay state, provenance, presets or task files. Change each future-use selection explicitly where supported. Paused historical sessions that still select the old route will require a new model selection when resumed. Keeping their history intact is not a guarantee that the old route remains runnable after removal.
41
+ 5. **Check configuration ownership.** Review the effective canonical profile and its source. If a base composition or another settings layer still supplies it, an ownership journal is present or conflicted, or another process is editing settings, stop for review. Do not delete a journal/marker, override inherited configuration or patch Core to force the result. Verified legacy journal restoration remains its own conservative process; migration does not grant ownership of arbitrary user fields.
42
+
43
+ If any prerequisite is uncertain, keep the existing profile until resolved. Merely showing two groups is safer than silently losing a usable route.
44
+
45
+ ## Apply the reviewed Ops migration after release
46
+
47
+ 1. Confirm the released plugin is installed and loaded with approval. Query `migrationStatus()` and check protocol/version, required capabilities and completeness; unknown evidence blocks maintenance. Record current Session selections and future default; manually resolve any approved selection blockers outside config-only v1 and query readiness again. Obtain the cold-history acknowledgement; the command never writes those selections. Any required Host interruption still needs explicit permission and an active-session check.
48
+ 2. Preserve a **private settings-only backup** in a user-approved location outside Git/shared docs; do not print/upload it or read/copy OAuth credentials. Read the current user-native profile, namespace revision, effective base layers and ownership journal. Stop if inherited/base configuration, journal ambiguity or concurrent change prevents a safe removal.
49
+ 3. Through the reviewed public settings path, **compare-and-swap (CAS)** against the observed revision and remove only the user-native `llm-pi-ai.providers.github-copilot` path. Preserve the `llm-pi-ai` mount/section, companion settings, credentials, all other providers and user fields. A conflict or already-absent path is not permission to retry blindly, rewrite whole settings, delete a journal or patch Core.
50
+ 4. Read back persisted/effective settings and the real provider registry. Then check the composer and `/model` lists: only report a single managed group when the canonical registration is actually absent and no other layer supplies it. A hidden card or successful write alone is not this evidence. Cold `listModels()` can ensure shared discovery without opening Settings first.
51
+ 5. Separately review any nonempty hosted-search `providers` allowlist. Ops may replace the reviewed legacy route entry with `github-copilot-preview` when approved; keep unrelated entries and never clear/broaden the list implicitly. Plugin code does not make this migration for you.
52
+ 6. Confirm credentials remain configured, approved selections/default match the review, and other selected/history-backed Sessions and histories are intact. Public `session.selectModel` also saves the future default, so unselected empty Sessions may inherit it. `route: not-configured` describes optional canonical absence, not login failure or proof of successful model/search calls.
53
+
54
+ A previously stuck **Deleting…** dialog is not proof the configuration was or was not removed. This change does not establish a fix for that hang. Do not click Delete repeatedly; inspect persisted configuration after the authorized stop before deciding whether any edit is required.
55
+
56
+ ## History, search and rollback
57
+
58
+ - The route ID `github-copilot-preview` does not change, preserving existing managed-route selections. This migration does not create a transparent alias from `github-copilot`, rewrite history or guarantee seamless continuation of old canonical sessions. Select a supported managed model explicitly when resuming those sessions, or retain/restore the canonical profile if its availability is still required.
59
+ - Managed conversations use the published native adapter. Provider-hosted `ctx.web` search remains Responses-only with account/discovery/probe/allowlist checks, using the captured initiating Session's effective request-header/config or explicit `GenerateOptions`, with per-owner plans rather than a global default. `Agent.options` is only the activation seed, not selected-model evidence. Without proven request context, traditional search is unavailable; a pending new selection must not reuse old headers as current prompt guidance. Cold managed search ensures shared metadata first. Missing `agents.currentInitiator` makes traditional search unavailable; explicit marked request paths remain guarded. OAuth notification during initial discovery can fail the first search closed before probe/wire; a later user/driver request may retry, not automatically. The custom inline search path applies only to a still-configured legacy canonical route; removing that route does not turn managed conversations into the legacy inline transport.
60
+ - For rollback, stop the relevant Host with permission and compare current settings with the private backup. Restore only the reviewed canonical profile fields and any default changes the user explicitly wants reversed. Do not overwrite the entire current settings file over unrelated later edits. Preserve unresolved journals for review. Restart with permission and verify registration, credentials and selections separately; a restored profile is not proof of a successful model call.
61
+ - Downgrading to older plugin code can recreate a canonical profile. Record that behavior before an approved package rollback; do not mistake the reappearing group for a second OAuth account.
62
+
63
+ ## 中文操作说明
64
+
65
+ ### 目标与限制
66
+
67
+ **V3 目标:**一个全局 Copilot 账号、多个共享账号模型,已显式选择/有历史选择的 Session 独立。插件代码不自动迁移配置、不强制唯一注册;实际只保留托管路由是发布后获准的 Ops 步骤:操作者先单独处理批准的 Session/默认值选择,仅配置 v1 维护再按 CAS 移除审核过的 user-native profile 并回读 registry。维护命令不写 Session/默认选择。禁止批量改写历史/设置/凭据或隐藏分组。
68
+
69
+ 新建原生 Copilot draft 警告:**Save** 增加另一真实模型分组,而非第二账号。公开追加式 API 无法否决 **Add** 或禁用 **Save**;它只是警告,用户在迁移后仍能重新添加原生分组。公开 `session.selectModel` 同时保存未来全局默认值,因此其它已选/历史会话虽保留自身上下文,未选择的空会话仍可能继承新默认值。不新增全局当前模型/搜索状态卡片。
70
+
71
+ 新安装使用显示为 **GitHub Copilot** 的账号发现路由,真实 ID 保持 `github-copilot-preview`。计划版本 `0.4.0-alpha.9` 在已有配置的 canonical `github-copilot` provider card 挂载时,将登录、状态、**Refresh models** 和 **Manage** 嵌入其中,抑制独立页脚账号控制器;没有此类行挂载时仍保留页脚或旧 Core settings section fallback。只有另一个符合条件的表面仍保持挂载时,切换才会保留共享账号状态 owner。最后一个表面卸载,或声明替换前后没有挂载重叠时,轮询会停止。之后新建控制器会读状态并另行确保缺失/idle/过期/error/loading的已登录元数据,不重放旧的强制登录动作。手动 **Refresh models** 位于 **Manage** 内,错误提供 **Retry**。这只是账号控件集成,不是合并/移除两条真实路由,也不会因此改写凭据、配置、历史或选择。
72
+
73
+ 正常发现无需手工定义模型或常规手动刷新;公开 slot 仍不能替换 Core **Edit/Delete**,原生编辑器保留。显式界面登录/切换账号成功后强制发现一次;打开 Models 会另外通过非强制 `ensureModels()` 确保缺失/idle/过期/error/loading的已登录元数据,error 重开可在共享冷却结束后重试,但无同次挂载循环;loading 加入已有 Host 请求,不增加网络请求。新鲜 ready 不拉取,真正 unavailable 且空模型不自动重试。状态/详情本身仍无网络。`github-copilot` 默认 `accountModelTtlMs: 86400000`(24 小时最大复用)、`accountModelFailureCooldownMs: 300000`(5 分钟失败冷却),Host 合并为一个在途发现,不设周期元数据轮询;另有挂载期间每 60 秒更新相对时间文字的纯显示定时器,不调用 RPC/status/discovery。后台凭据/reset 事件清状态并只读查询,不对每个 token 事件强制发现;下次打开/使用再确保元数据。
74
+
75
+ alpha.7 的上次成功时间(`snapshot.discoveredAt`)只在模型数量旁、**Manage** 外显示一次:相对文字为英文,tooltip 和可访问语义 `time` 提供完整本地日期、时间及时区。缺失/无效/无账号时隐藏,未来时间显示绝对值。等待/失败保留上次成功时间,刷新成功才更新,退出登录清除;时间消失或卸载时清理显示定时器。缓存和发现生命周期不变。
76
+
77
+ 同账号旧元数据可在 TTL 刷新/loading/error 时展示,但不能授权请求。凭据/账号/权限失效或 proof 到期立即撤销证据,TTL 不延长 token。明确 `UNKNOWN_MODEL` 只触发一次有界元数据刷新,不重放消息或切换模型;普通 HTTP/网络错误不能猜成模型不存在。新模型仍需支持的账号元数据。README 首先展示的 alpha.7 model-freshness/model-refreshing PNG 来自实际构建 Client 的隔离 Edge 与合成 Remote/provider-shell 数据,不是真实 Core 或生产授权。Host TTL/冷却时序需单元测试,不能靠截图证明。provider-entry/authorization PNG 仍为 alpha.5 历史示意,旧紧凑账号 GIF/PNG 为 alpha.3。
78
+
79
+ 升级不会自动删除已有 `llm-pi-ai.providers.github-copilot`。只有用户真正移除经过审核的旧配置后,composer 与 `/model` 才都会只列出托管 Copilot 分组,不是仅隐藏第二条路由。始终保留 `llm-pi-ai` 插件、授权服务和唯一 OAuth record `llm-pi-ai/github-copilot`;不要退出登录、删除凭据或卸载授权插件来隐藏分组,也不要用 `models: []` 冒充禁用。
80
+
81
+ 安装、真实测试请求、默认模型/会话选择修改、停止 Host 和离线配置修改都需要各自明确授权。本文不是自动执行这些操作的许可。
82
+
83
+ ### 只读就绪检查与维护范围(alpha.9)
84
+
85
+ 使用无参数 `githubCopilot.migrationStatus()` 新鲜读取所有 live Agent Session,而不是依赖可能过时的通用 `session/list` 或不含加载版本的 inventory。结果提供加载构建 `plugin.name/version`、`protocolVersion: 1`、`observedAt`、五个 `capabilities` 标记(`agentsList`、`sessionProjections`、`settingsCas`、`providerRegistry`、`defaultSelection`)及 sessions/default/routes 的 `complete` 标记。缺失能力、false 完整性或必要选择/路由证据为 null 表示未知,不能推断安全缺失;idle Agent 的 `activeRequestSelection: null` 是正常值。
86
+
87
+ `effectiveSelection` 优先 pending projection,其次 request-header config;只有 projection 已知、确实无 pending/header 的空 live Session 才取默认值,`selectionSource` 表明来源。running Agent 的 `activeRequestSelection` 另报最近记录的 header,不证明 LLM 调用正在进行。路由标记区分有效 native 配置与 native/managed 注册。方法不执行 auth status/发现、不访问凭据/网络、不修改 settings/Session,也不新增常规界面。
88
+
89
+ 协议 v1 的 `historyScope: live-agents-only` 不扫描冷存储历史,操作者必须确认知悉旧对话恢复时可能需要显式重选模型。加载版本/结构能力自报告不是完整 Desktop/Core 字节核验,也不是跨 namespace 原子事务;CAS 前立即复查。
90
+
91
+ `cloga/dsh-windows-ops` 计划的 `tools/migrate-copilot-managed-route.ps1` 是插件发布后独立的**仅配置 v1 维护**:不自动写 Session/默认值、不读冷历史、不安装/重启 DSH、不验收完整 Desktop 基线。若有选择阻塞,须先另行授权、手工处理;计划路径不表示命令已发布/安装或真实迁移已经完成。
92
+
93
+ ### 移除前审核
94
+
95
+ 1. **先实际运行新版本。** 确认目标 `0.4.0-alpha.9` 中保留 canonical 缺失的插件已在活动 profile 加载,Host 与 Client 匹配。provider 集成界面不要求执行这项可选路由移除。使用已发布且校验通过的制品;计划版本和磁盘安装都不是已发布/已加载的证明。旧版本可能在启动、登录或认证刷新时重建被删 profile,不能先删除再期待旧进程保留缺失。
96
+ 2. **验证发现结果。** 打开 Models 确认登录,等待按需自动 ensure 完成,检查目标模型是否在 `github-copilot-preview` 的接受列表及能力警告中。错误可点 **Retry**;有意强制更新时使用 **Manage → Refresh models**。元数据成功不等于真实调用成功;可选测试请求须明确批准,正常消耗供应方资源,结果单独报告。不要重复登录或关闭校验强行恢复模型。
97
+ 3. **有意迁移选择。** 只为明确批准迁移的 Session 选择托管模型,再确认获准的未来默认值。Core `session.selectModel` 也会保存该默认值,不能假设未选择的空会话不受影响。只有账号明确公布且 SDK 支持时才保留同一模型 ID/思考强度;否则请用户选择,不猜测替代项。检查 preset、定时任务和暂停会话的 canonical 引用,逐项审核未来选择,不批量改写历史、回放、来源记录或 preset 文件。
98
+ 4. **检查所有权与并发。** 若 base composition/其它配置层仍提供 canonical profile,存在 journal 冲突,或其它进程正在编辑 settings,停止并审核。不强删 marker,不覆盖继承配置,不修改 Core。旧 journal 的精确恢复流程仍需保留其安全检查。
99
+
100
+ ### 发布后执行明确批准的 Ops 迁移
101
+
102
+ 1. 经批准安装并实际加载已发布插件;调用 `migrationStatus()` 检查协议/版本、必要能力与完整性,未知证据阻止维护。记录当前 Session 选择及未来默认值;如有获准的选择阻塞,在仅配置 v1 之外手工处理后再次查询。取得冷历史范围确认;命令不写这些选择。若需要中断 Host,仍须另行批准并检查活动会话。
103
+ 2. 仅将设置私密备份到用户批准、Git/共享文档之外的位置,不打印/上传,不读取或复制 OAuth 凭据。检查当前 user-native profile、namespace revision、有效 base 层及所有权 journal;有继承配置、journal 歧义或并发变更则停止。
104
+ 3. 通过已审核的公开 settings 路径,按读取到的 revision 做 **compare-and-swap(CAS)**,只移除 user-native **`llm-pi-ai.providers.github-copilot`**。保留 `llm-pi-ai` 挂载/section、companion 设置、凭据、其它 provider 和用户字段。冲突或路径已不存在时不盲重试,不整份改写设置、不删 journal、不改 Core。
105
+ 4. 回读持久化/有效设置及真实 provider registry,再检查 composer 与 `/model`。只有 canonical 注册确实消失、其它层也不提供时,才能报告只剩托管分组;隐藏卡片或写入成功本身不够。冷启动 `listModels()` 可直接确保共享发现,无需先打开 Settings。
106
+ 5. 单独审核 hosted-search 非空 `providers` allowlist。经批准,Ops 可将审核过的旧 route 条目改为 `github-copilot-preview`,保留无关条目;不得隐式清空或扩大列表。插件代码不会代做迁移。
107
+ 6. 确认凭据仍已配置、获准 Session/默认值符合审核、其它已选/历史会话及历史保持原样。公开 `session.selectModel` 同时保存未来默认值,未选择的空会话可能继承它。`route: not-configured` 仅表示可选 canonical 缺失,不证明发现/模型/搜索调用成功。
108
+
109
+ ### 历史、搜索与回退
110
+
111
+ 旧 `github-copilot-preview` ID 保持不变。canonical 历史不改写,也不会被自动别名映射;仍选旧路由的暂停会话恢复时须显式选择支持的托管模型。如果必须继续原路由,应暂缓移除或恢复其 profile,不能宣称旧路由仍无缝可用。
112
+
113
+ 托管普通对话交给原生 adapter;`ctx.web` 托管搜索仍只支持通过账号元数据/probe/allowlist 检查的 Responses 模型,按捕获的发起 Session 有效 request-header/config 或显式 `GenerateOptions` 与独立 owner plan 处理,不用全局默认值。`Agent.options` 仅是激活 seed,不是已选模型证据;无可证明的请求上下文时传统搜索不可用,新选择尚未发起下一请求时不能拿旧 header 当当前 prompt 指引;冷启动先确保共享元数据。缺少 `agents.currentInitiator` 时传统搜索不可用,显式带标记请求仍受既有检查保护。首次发现中的 OAuth 通知可能让该次搜索在 probe/wire 前保守失败,之后用户/driver 可以重新请求,不自动重试。自定义 inline 只用于仍配置的旧 canonical 路由,删除旧路由不会把托管对话变成旧 inline 路径。
114
+
115
+ 回退同样先取得停止 Host 的许可,对照私密备份,只恢复经过审核的 canonical profile,以及用户明确要撤回的默认值变更。不要用整份旧文件覆盖后来无关改动;保留冲突 journal 以供审核。获准重启后分别验证注册、登录和选择;配置恢复不等于模型调用成功。降级旧插件可能重新创建 canonical profile,须事先说明,不能将再出现的分组当成第二个 OAuth 账号。