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.
- package/AGENTS.md +233 -0
- package/CONTRIBUTING.md +46 -0
- package/LICENSE +21 -0
- package/README.md +365 -0
- package/README.zh.md +376 -0
- package/SECURITY.md +29 -0
- package/cordis.patch.yml +16 -0
- package/deployment-baseline.json +752 -0
- package/docs/agent-readiness.md +95 -0
- package/docs/images/copilot-auth-card-signed-in.png +0 -0
- package/docs/images/copilot-device-code-copy.png +0 -0
- package/docs/images/copilot-model-freshness.png +0 -0
- package/docs/images/copilot-model-refreshing.png +0 -0
- package/docs/images/copilot-provider-authorization.png +0 -0
- package/docs/images/copilot-provider-entry.png +0 -0
- package/docs/images/github-copilot-auth-flow.gif +0 -0
- package/docs/model-compatibility-acceptance.md +46 -0
- package/docs/npm-distribution.md +134 -0
- package/docs/session-search-routing.md +53 -0
- package/docs/single-route-migration.md +115 -0
- package/lib/client.js +7153 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +5384 -0
- package/lib/remote.js +162 -0
- package/lib/routed-web.js +80 -0
- package/lib/search-routing-pFLux0W7.js +763 -0
- package/lib/types/account-model-auth.d.ts +15 -0
- package/lib/types/account-model-catalog.d.ts +67 -0
- package/lib/types/account-model-source.d.ts +85 -0
- package/lib/types/authorization-controller.d.ts +100 -0
- package/lib/types/client.d.ts +106 -0
- package/lib/types/compact-account.d.ts +42 -0
- package/lib/types/compatibility.d.ts +17 -0
- package/lib/types/config.d.ts +42 -0
- package/lib/types/content-file.d.ts +10 -0
- package/lib/types/copilot-auth.d.ts +23 -0
- package/lib/types/copilot-grant.d.ts +11 -0
- package/lib/types/copilot-identity.d.ts +6 -0
- package/lib/types/copilot-request.d.ts +11 -0
- package/lib/types/current-provider.d.ts +59 -0
- package/lib/types/deepseek-search-fallback.d.ts +11 -0
- package/lib/types/failure.d.ts +38 -0
- package/lib/types/http.d.ts +48 -0
- package/lib/types/index.d.ts +39 -0
- package/lib/types/migration-status.d.ts +50 -0
- package/lib/types/model-protocol.d.ts +31 -0
- package/lib/types/pi-provider-bridge.d.ts +22 -0
- package/lib/types/plan.d.ts +148 -0
- package/lib/types/preview-provider.d.ts +35 -0
- package/lib/types/preview-route.d.ts +63 -0
- package/lib/types/probe.d.ts +39 -0
- package/lib/types/reasoning-presentation.d.ts +55 -0
- package/lib/types/remote.d.ts +151 -0
- package/lib/types/responses-reasoning-text.d.ts +24 -0
- package/lib/types/responses-reasoning.d.ts +7 -0
- package/lib/types/route-ownership.d.ts +70 -0
- package/lib/types/routed-web.d.ts +38 -0
- package/lib/types/search-backend.d.ts +16 -0
- package/lib/types/search-routing.d.ts +43 -0
- package/lib/types/serialize.d.ts +109 -0
- package/lib/types/sse.d.ts +28 -0
- package/lib/types/temporary-models.d.ts +25 -0
- package/lib/types/tool-schema-compat.d.ts +17 -0
- package/lib/types/traditional-search.d.ts +17 -0
- package/lib/types/types.d.ts +69 -0
- package/lib/types/usage.d.ts +26 -0
- package/lib/types/watchdog.d.ts +22 -0
- package/lib/types/web-delegate.d.ts +12 -0
- package/lib/types/web-search-routing-card.d.ts +10 -0
- package/lib/types/web-search-routing-config.d.ts +16 -0
- package/lib/types/wire-anthropic.d.ts +37 -0
- package/lib/types/wire.d.ts +56 -0
- package/lib/web-delegate.js +9 -0
- package/package.json +200 -0
- package/scripts/check-search-composition.mjs +191 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idle watchdog for the inline wire path: aborts when no data arrives for a
|
|
3
|
+
* configurable interval. The timeout resets on every `reset()` call, so a
|
|
4
|
+
* slow but alive stream never trips it.
|
|
5
|
+
* @module dsh-github-copilot/watchdog
|
|
6
|
+
*/
|
|
7
|
+
/** Abort-triggering idle timer. */
|
|
8
|
+
export declare class IdleWatchdog {
|
|
9
|
+
private readonly ms;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
private readonly controller;
|
|
12
|
+
private timer;
|
|
13
|
+
/**
|
|
14
|
+
* @param ms - the idle bound in milliseconds.
|
|
15
|
+
*/
|
|
16
|
+
constructor(ms: number);
|
|
17
|
+
/** Restart the idle window. Call after every received event. */
|
|
18
|
+
reset(): void;
|
|
19
|
+
/** Stop the timer and release the signal. */
|
|
20
|
+
dispose(): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=watchdog.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { WebRuntime } from '@deepseek-ai/dsh-web';
|
|
3
|
+
declare module '@deepseek-ai/cordis' {
|
|
4
|
+
interface Context {
|
|
5
|
+
githubCopilotOriginalWeb: WebRuntime;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Expose the unchanged official service from the bundle's named web isolate. */
|
|
9
|
+
export declare const name = "github-copilot-web-delegate";
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
export declare function apply(ctx: Context): void;
|
|
12
|
+
//# sourceMappingURL=web-delegate.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
|
+
interface SearchRoutingCardProps {
|
|
4
|
+
readonly settings: ClientContext['remote']['settings'];
|
|
5
|
+
readonly copilot: ClientContext['remote']['githubCopilot'];
|
|
6
|
+
}
|
|
7
|
+
/** Models-page card for plugin-owned cross-provider search routing. */
|
|
8
|
+
export declare function WebSearchRoutingCard(props: SearchRoutingCardProps): ReactElement;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=web-search-routing-card.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import z from '@deepseek-ai/schemastery';
|
|
2
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
3
|
+
/** Plugin-owned settings namespace for cross-provider web-search routing. */
|
|
4
|
+
export declare const WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE: SettingsNamespace;
|
|
5
|
+
/** Sentinel that disables the default/fixed search backend. */
|
|
6
|
+
export declare const NO_DEFAULT_SEARCH_PROVIDER = "none";
|
|
7
|
+
/** Cross-provider policy applied by this plugin's routed web facade. */
|
|
8
|
+
export interface WebSearchRoutingConfig {
|
|
9
|
+
/** Auto prefers the initiating model's native search; fixed always uses the configured provider. */
|
|
10
|
+
searchMode?: 'auto' | 'fixed';
|
|
11
|
+
/** Registered search-provider id used by fixed mode and as auto mode's fallback. */
|
|
12
|
+
defaultSearchProvider?: string;
|
|
13
|
+
}
|
|
14
|
+
/** User-facing routing settings; provider-specific credentials and models remain provider-owned. */
|
|
15
|
+
export declare const WebSearchRoutingConfigSchema: z<WebSearchRoutingConfig>;
|
|
16
|
+
//# sourceMappingURL=web-search-routing-config.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Messages wire for the inline web search plugin: intercepts an
|
|
3
|
+
* agent-loop model call, builds a Messages streaming request carrying the
|
|
4
|
+
* server-side `web_search_20250305` tool, and maps the Anthropic event
|
|
5
|
+
* stream onto the harness StreamChunk contract. Server-side search blocks
|
|
6
|
+
* (`server_tool_use` / `web_search_tool_result`) are skipped — the gateway
|
|
7
|
+
* executes the search inside the same request and the model answers from
|
|
8
|
+
* its results in the same turn. The generator NEVER throws; every failure
|
|
9
|
+
* becomes a finish chunk.
|
|
10
|
+
* @module dsh-github-copilot/wire-anthropic
|
|
11
|
+
*/
|
|
12
|
+
import type { GenerateOptions, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
13
|
+
import type { SearchPlanCandidate } from './plan.ts';
|
|
14
|
+
import type { InlineConfig } from './config.ts';
|
|
15
|
+
import type { InlineHooks } from './wire.ts';
|
|
16
|
+
/** Map an Anthropic usage object onto the harness TokenUsage shape. */
|
|
17
|
+
export declare function mapAnthropicUsage(usage: {
|
|
18
|
+
readonly input_tokens?: number;
|
|
19
|
+
readonly output_tokens?: number;
|
|
20
|
+
readonly cache_read_input_tokens?: number;
|
|
21
|
+
readonly cache_creation_input_tokens?: number;
|
|
22
|
+
readonly output_tokens_details?: {
|
|
23
|
+
readonly thinking_tokens?: number;
|
|
24
|
+
};
|
|
25
|
+
}): TokenUsage;
|
|
26
|
+
/**
|
|
27
|
+
* Run one inline model call through the Anthropic Messages API with the
|
|
28
|
+
* server-side web search tool injected. Never throws; always ends with a
|
|
29
|
+
* finish chunk.
|
|
30
|
+
* @param request - the frozen agent-loop request.
|
|
31
|
+
* @param candidate - the settled endpoint facts (probe-verified).
|
|
32
|
+
* @param hooks - credential hooks.
|
|
33
|
+
* @param cfg - the current plugin configuration.
|
|
34
|
+
* @returns the StreamChunk stream for the loop's assembler.
|
|
35
|
+
*/
|
|
36
|
+
export declare function inlineAnthropicStream(request: GenerateOptions, candidate: SearchPlanCandidate, hooks: InlineHooks, cfg: InlineConfig): AsyncGenerator<StreamChunk>;
|
|
37
|
+
//# sourceMappingURL=wire-anthropic.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The short-circuit wire: intercepts an agent-loop model call, builds a
|
|
3
|
+
* streaming request carrying the server-side `web_search` tool, and maps the
|
|
4
|
+
* SSE events onto the harness StreamChunk contract. The generator NEVER
|
|
5
|
+
* throws — every failure becomes a finish chunk, exactly what the loop's
|
|
6
|
+
* catch-less `for await` requires. {@link inlineWireStream} settles the plan
|
|
7
|
+
* first and then dispatches to the protocol the VERIFIED candidate speaks, so
|
|
8
|
+
* the wire always matches the probe verdict (never the chat route snapshot).
|
|
9
|
+
*
|
|
10
|
+
* Server-side `web_search_call` items are skipped: the gateway executes the
|
|
11
|
+
* search inside the same request and the model answers from its results in
|
|
12
|
+
* the same turn, so the harness never sees a tool call. (UI search cards
|
|
13
|
+
* were removed — the added latency outweighed the visual value.)
|
|
14
|
+
* @module dsh-github-copilot/wire
|
|
15
|
+
*/
|
|
16
|
+
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
17
|
+
import type { SearchPlan, SearchPlanCandidate } from './plan.ts';
|
|
18
|
+
import type { ResponsesReasoningOptions } from './serialize.ts';
|
|
19
|
+
import type { ResolvedRequestAuth } from './copilot-request.ts';
|
|
20
|
+
import type { InlineConfig } from './config.ts';
|
|
21
|
+
/** Credential and cancellation hooks the stream resolves per operation. */
|
|
22
|
+
export interface InlineHooks {
|
|
23
|
+
/** Resolve one credential reference through the credentials seam. */
|
|
24
|
+
resolveApiKey: (candidate: SearchPlanCandidate) => Promise<string | ResolvedRequestAuth | undefined>;
|
|
25
|
+
/** Validate effective model/profile reasoning and return its mapped wire value. */
|
|
26
|
+
resolveResponsesReasoning?: (request: GenerateOptions, candidate: SearchPlanCandidate) => ResponsesReasoningOptions | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether the request carries an image attachment. Image blocks retain the
|
|
30
|
+
* harness attachment object that the official vision channel must consume.
|
|
31
|
+
*/
|
|
32
|
+
export declare function contentHasImageAttachments(request: GenerateOptions): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Run one inline model call through whichever wire the settled candidate
|
|
35
|
+
* speaks. The plan is settled HERE — once — so the protocol choice follows
|
|
36
|
+
* the probe verdict instead of the chat route snapshot taken before the
|
|
37
|
+
* probe ran.
|
|
38
|
+
* @param request - the frozen agent-loop request.
|
|
39
|
+
* @param plan - the search plan owning endpoint facts.
|
|
40
|
+
* @param hooks - credential hooks.
|
|
41
|
+
* @param cfg - the current plugin configuration.
|
|
42
|
+
* @returns the StreamChunk stream for the loop's assembler.
|
|
43
|
+
*/
|
|
44
|
+
export declare function inlineWireStream(request: GenerateOptions, plan: SearchPlan, hooks: InlineHooks, cfg: InlineConfig): AsyncGenerator<StreamChunk>;
|
|
45
|
+
/**
|
|
46
|
+
* Run one inline model call: fetch the Responses endpoint with the
|
|
47
|
+
* server-side web tool injected and translate the SSE stream into harness
|
|
48
|
+
* chunks. Never throws; always ends with a finish chunk.
|
|
49
|
+
* @param request - the frozen agent-loop request.
|
|
50
|
+
* @param candidate - the settled endpoint facts (probe-verified).
|
|
51
|
+
* @param hooks - credential hooks.
|
|
52
|
+
* @param cfg - the current plugin configuration.
|
|
53
|
+
* @returns the StreamChunk stream for the loop's assembler.
|
|
54
|
+
*/
|
|
55
|
+
export declare function inlineStream(request: GenerateOptions, candidate: SearchPlanCandidate, hooks: InlineHooks, cfg: InlineConfig): AsyncGenerator<StreamChunk>;
|
|
56
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region lib/types/web-delegate.js
|
|
2
|
+
/** Expose the unchanged official service from the bundle's named web isolate. */
|
|
3
|
+
const name = "github-copilot-web-delegate";
|
|
4
|
+
const inject = ["web"];
|
|
5
|
+
function apply(ctx) {
|
|
6
|
+
ctx.provide("githubCopilotOriginalWeb", ctx.web);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { apply, inject, name };
|
package/package.json
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-github-copilot",
|
|
3
|
+
"description": "DSH companion for GitHub Copilot sign-in, account-aware model profiles, tool compatibility, and provider-hosted search.",
|
|
4
|
+
"version": "0.4.0-alpha.18",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public",
|
|
7
|
+
"registry": "https://registry.npmjs.org/",
|
|
8
|
+
"tag": "alpha"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/cloga/dsh-github-copilot.git"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./lib/types/index.d.ts",
|
|
18
|
+
"default": "./lib/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./client": {
|
|
21
|
+
"types": "./lib/types/client.d.ts",
|
|
22
|
+
"default": "./lib/client.js"
|
|
23
|
+
},
|
|
24
|
+
"./remote": {
|
|
25
|
+
"types": "./lib/types/remote.d.ts",
|
|
26
|
+
"default": "./lib/remote.js"
|
|
27
|
+
},
|
|
28
|
+
"./web-delegate": {
|
|
29
|
+
"types": "./lib/types/web-delegate.d.ts",
|
|
30
|
+
"default": "./lib/web-delegate.js"
|
|
31
|
+
},
|
|
32
|
+
"./routed-web": {
|
|
33
|
+
"types": "./lib/types/routed-web.d.ts",
|
|
34
|
+
"default": "./lib/routed-web.js"
|
|
35
|
+
},
|
|
36
|
+
"./deployment-baseline.json": "./deployment-baseline.json",
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"cordis.patch.yml",
|
|
41
|
+
"deployment-baseline.json",
|
|
42
|
+
"lib/*.js",
|
|
43
|
+
"lib/*.js.map",
|
|
44
|
+
"lib/types/**/*.d.ts",
|
|
45
|
+
"README.md",
|
|
46
|
+
"README.zh.md",
|
|
47
|
+
"CONTRIBUTING.md",
|
|
48
|
+
"SECURITY.md",
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"docs/agent-readiness.md",
|
|
51
|
+
"docs/model-compatibility-acceptance.md",
|
|
52
|
+
"docs/single-route-migration.md",
|
|
53
|
+
"docs/session-search-routing.md",
|
|
54
|
+
"docs/npm-distribution.md",
|
|
55
|
+
"scripts/check-search-composition.mjs",
|
|
56
|
+
"docs/images/"
|
|
57
|
+
],
|
|
58
|
+
"keywords": [
|
|
59
|
+
"dsh",
|
|
60
|
+
"deepseek-harness",
|
|
61
|
+
"plugin",
|
|
62
|
+
"cordis",
|
|
63
|
+
"github-copilot",
|
|
64
|
+
"oauth",
|
|
65
|
+
"model-provider",
|
|
66
|
+
"hosted-search",
|
|
67
|
+
"web-search",
|
|
68
|
+
"openai-responses",
|
|
69
|
+
"typescript"
|
|
70
|
+
],
|
|
71
|
+
"license": "MIT",
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=22.19.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
77
|
+
"@deepseek-ai/dsh-agent": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-authorization": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-settings-models": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-credentials": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-llm": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-settings": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-web": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-web-search-deepseek": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
|
|
92
|
+
"react": "^18.2.0"
|
|
93
|
+
},
|
|
94
|
+
"peerDependenciesMeta": {
|
|
95
|
+
"@deepseek-ai/dsh-web-search-deepseek": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@deepseek-ai/dsh-launch-environment": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"@deepseek-ai/dsh-api-remotes": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@deepseek-ai/dsh-authorization": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@deepseek-ai/dsh-client-ui-settings-models": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
116
|
+
"@deepseek-ai/dsh-typert-protocol": {
|
|
117
|
+
"optional": true
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"dependencies": {
|
|
121
|
+
"@deepseek-ai/dsh-authorization": "0.1.2-rc.1",
|
|
122
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
123
|
+
"@earendil-works/pi-ai": "0.85.1",
|
|
124
|
+
"zod": "^4.4.3"
|
|
125
|
+
},
|
|
126
|
+
"devDependencies": {
|
|
127
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
128
|
+
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
|
129
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
|
|
130
|
+
"@deepseek-ai/dsh-api-gateway": "0.1.2-rc.1",
|
|
131
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
|
|
132
|
+
"@deepseek-ai/dsh-app-boot": "0.1.2-rc.1",
|
|
133
|
+
"@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
|
|
134
|
+
"@deepseek-ai/dsh-authorization": "0.1.2-rc.1",
|
|
135
|
+
"@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
|
|
136
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-client-ui-settings-models": "0.1.2-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-fs": "0.1.2-rc.1",
|
|
141
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.2-rc.1",
|
|
142
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
143
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.2-rc.1",
|
|
144
|
+
"@deepseek-ai/dsh-scope": "0.1.2-rc.1",
|
|
145
|
+
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
146
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
|
|
147
|
+
"@deepseek-ai/dsh-tool-web": "0.1.2-rc.1",
|
|
148
|
+
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
149
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
|
|
150
|
+
"@deepseek-ai/dsh-typert-registry": "0.1.2-rc.1",
|
|
151
|
+
"@deepseek-ai/dsh-web": "0.1.2-rc.1",
|
|
152
|
+
"@deepseek-ai/dsh-web-search-deepseek": "0.1.2-rc.1",
|
|
153
|
+
"@types/node": "^22.0.0",
|
|
154
|
+
"@types/react": "~18.3.1",
|
|
155
|
+
"jsdom": "^26.1.0",
|
|
156
|
+
"react": "^18.2.0",
|
|
157
|
+
"tsdown": "^0.22.2",
|
|
158
|
+
"typescript": "^5.9.0",
|
|
159
|
+
"vitest": "^3.2.0"
|
|
160
|
+
},
|
|
161
|
+
"main": "lib/index.js",
|
|
162
|
+
"types": "lib/types/index.d.ts",
|
|
163
|
+
"imports": {
|
|
164
|
+
"#package.json": "./package.json"
|
|
165
|
+
},
|
|
166
|
+
"dsh": {
|
|
167
|
+
"bundle": {
|
|
168
|
+
"patch": "./cordis.patch.yml"
|
|
169
|
+
},
|
|
170
|
+
"client": {
|
|
171
|
+
"inject": [
|
|
172
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
173
|
+
"@deepseek-ai/dsh-client-ui-settings-models"
|
|
174
|
+
],
|
|
175
|
+
"platform": "web"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"scripts": {
|
|
179
|
+
"build": "pnpm clean && tsc -p tsconfig.json && tsdown",
|
|
180
|
+
"clean": "node -e \"require('node:fs').rmSync('lib', { recursive: true, force: true })\"",
|
|
181
|
+
"test": "vitest run",
|
|
182
|
+
"test:watch": "vitest",
|
|
183
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
184
|
+
"typecheck:tests": "tsc -p tsconfig.tests.json",
|
|
185
|
+
"test:scripts": "node --test \"tests/scripts/*.test.mjs\"",
|
|
186
|
+
"agent:describe": "node scripts/agent.mjs describe --json",
|
|
187
|
+
"agent:doctor": "node scripts/agent.mjs doctor --json",
|
|
188
|
+
"agent:plan": "node scripts/agent.mjs plan",
|
|
189
|
+
"preflight:search": "node scripts/check-search-composition.mjs",
|
|
190
|
+
"verify:agent": "node scripts/verify-agent-contract.mjs",
|
|
191
|
+
"verify:tarball": "node scripts/verify-tarball.mjs",
|
|
192
|
+
"verify:baseline": "node scripts/verify-deployment-baseline.mjs",
|
|
193
|
+
"verify:package": "node scripts/verify-package.mjs",
|
|
194
|
+
"verify:controlled-core": "node scripts/verify-controlled-core.mjs",
|
|
195
|
+
"verify:reasoning-ui": "node scripts/verify-reasoning-presentation.mjs",
|
|
196
|
+
"verify:upstream": "node scripts/verify-upstream-baseline.mjs",
|
|
197
|
+
"release:check": "node scripts/release-policy.mjs",
|
|
198
|
+
"verify": "pnpm verify:agent && pnpm typecheck && pnpm typecheck:tests && pnpm verify:baseline && pnpm build && pnpm test && pnpm test:scripts && pnpm verify:package"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { resolve, join, isAbsolute } from 'node:path'
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const realm = 'github-copilot-original-web'
|
|
8
|
+
const reserved = new Set(['github-copilot-web-delegate', 'github-copilot-routed-web'])
|
|
9
|
+
|
|
10
|
+
/** Structural pre-install validation only; never imports/boots configured plugins. */
|
|
11
|
+
export function checkSearchComposition(entries) {
|
|
12
|
+
const reasons = new Set()
|
|
13
|
+
const matches = []
|
|
14
|
+
function walk(rows, depth) {
|
|
15
|
+
if (!Array.isArray(rows)) { reasons.add('INVALID_ENTRY_LIST'); return }
|
|
16
|
+
for (const entry of rows) {
|
|
17
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { reasons.add('INVALID_ENTRY'); continue }
|
|
18
|
+
if (entry.id === 'web') matches.push({ entry, depth })
|
|
19
|
+
if (entry.name === '@deepseek-ai/dsh-web' && entry.id !== 'web') reasons.add('ADDITIONAL_OFFICIAL_WEB_SERVICE')
|
|
20
|
+
if (reserved.has(entry.id) || ['dsh-github-copilot/web-delegate', 'dsh-github-copilot/routed-web'].includes(entry.name)) reasons.add('ROUTING_ENTRY_CONFLICT')
|
|
21
|
+
if (entry.isolate !== undefined) {
|
|
22
|
+
if (!entry.isolate || typeof entry.isolate !== 'object' || Array.isArray(entry.isolate)) reasons.add('UNKNOWN_ISOLATION')
|
|
23
|
+
else {
|
|
24
|
+
if (Object.values(entry.isolate).some(value => value === realm)) reasons.add('ROUTING_REALM_CONFLICT')
|
|
25
|
+
if (Object.values(entry.isolate).some(value => typeof value !== 'boolean' && typeof value !== 'string')) reasons.add('UNKNOWN_ISOLATION')
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (entry.group === true) walk(entry.config, depth + 1)
|
|
29
|
+
else if (entry.group !== undefined && entry.group !== false) reasons.add('UNKNOWN_GROUP')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
walk(entries, 0)
|
|
33
|
+
if (matches.length !== 1) reasons.add(matches.length === 0 ? 'WEB_ROW_MISSING' : 'MULTIPLE_WEB_ROWS')
|
|
34
|
+
for (const { entry, depth } of matches) {
|
|
35
|
+
if (depth !== 0) reasons.add('WEB_NOT_TOP_LEVEL')
|
|
36
|
+
if (entry.name !== '@deepseek-ai/dsh-web') reasons.add('CUSTOM_WEB_SERVICE')
|
|
37
|
+
if (entry.group === true || Object.keys(entry).some(key => !['id', 'name', 'config', 'disabled', 'group', 'isolate'].includes(key))) reasons.add('WEB_ENTRY_REQUIRES_REVIEW')
|
|
38
|
+
if (entry.disabled !== undefined && entry.disabled !== false) reasons.add('WEB_DISABLED_OR_DYNAMIC')
|
|
39
|
+
if (entry.config !== undefined && (!entry.config || typeof entry.config !== 'object' || Array.isArray(entry.config)
|
|
40
|
+
|| Object.entries(entry.config).some(([key, value]) => !['searchProvider', 'fetchProvider'].includes(key) || typeof value !== 'string'))) reasons.add('WEB_CONFIG_REQUIRES_REVIEW')
|
|
41
|
+
if (entry.isolate !== undefined && (!entry.isolate || typeof entry.isolate !== 'object' || Array.isArray(entry.isolate) || Object.keys(entry.isolate).length > 0)) reasons.add('PREEXISTING_WEB_ISOLATION')
|
|
42
|
+
}
|
|
43
|
+
return { schemaVersion: 1, supported: reasons.size === 0, reasons: [...reasons].sort(), scope: 'pre-install-structural-only; no boot, credentials, network or writes' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function reportCandidate(reasons) {
|
|
47
|
+
return { schemaVersion: 1, supported: reasons.size === 0, reasons: [...reasons].sort(), scope: 'candidate-structural-only; no boot, credentials, network or writes' }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function hasExpression(value) {
|
|
55
|
+
if (!value || typeof value !== 'object') return false
|
|
56
|
+
return Object.entries(value).some(([key, child]) => key === '__jsExpr' || key === '__js' || hasExpression(child))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function accountRows(entries) {
|
|
60
|
+
const matches = []
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
if (!isRecord(entry)) continue
|
|
63
|
+
if (entry.id === 'github-copilot' || entry.name === 'dsh-github-copilot') matches.push(entry)
|
|
64
|
+
if (entry.group === true && Array.isArray(entry.config)) matches.push(...accountRows(entry.config))
|
|
65
|
+
}
|
|
66
|
+
return matches
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Only the bundle's reviewed, static service wiring is automatically supported. */
|
|
70
|
+
function routingRowValid(entry, name, isolated, account = false) {
|
|
71
|
+
if (!entry || entry.name !== name) return false
|
|
72
|
+
const keys = ['id', 'name', 'disabled', 'group', 'isolate', ...(account ? ['config'] : [])]
|
|
73
|
+
if (Object.keys(entry).some(key => !keys.includes(key))) return false
|
|
74
|
+
if (entry.disabled !== undefined && entry.disabled !== false) return false
|
|
75
|
+
if (entry.group !== undefined && entry.group !== false) return false
|
|
76
|
+
if (isolated) {
|
|
77
|
+
if (!isDeepStrictEqual(entry.isolate, { web: realm })) return false
|
|
78
|
+
} else if (entry.isolate !== undefined && (!isRecord(entry.isolate) || Object.keys(entry.isolate).length !== 0)) return false
|
|
79
|
+
return entry.config === undefined || (account && isRecord(entry.config) && !hasExpression(entry.config))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function candidateViable(entries, baseline) {
|
|
83
|
+
const original = baseline.find(entry => entry.id === 'web')
|
|
84
|
+
const web = entries.find(entry => entry.id === 'web')
|
|
85
|
+
if (!original || !web || !isDeepStrictEqual(web.isolate, { web: realm })) return false
|
|
86
|
+
for (const [id, name, isolated, account] of [
|
|
87
|
+
['github-copilot-web-delegate', 'dsh-github-copilot/web-delegate', true, false],
|
|
88
|
+
['github-copilot-routed-web', 'dsh-github-copilot/routed-web', false, false],
|
|
89
|
+
['github-copilot', 'dsh-github-copilot', false, true],
|
|
90
|
+
]) {
|
|
91
|
+
const matches = entries.filter(entry => entry.id === id)
|
|
92
|
+
if (matches.length !== 1 || !routingRowValid(matches[0], name, isolated, account)) return false
|
|
93
|
+
}
|
|
94
|
+
// Apart from the two new service rows and the original web's realm, the
|
|
95
|
+
// candidate must preserve the entire routing-free tree, including user config.
|
|
96
|
+
// These are parsed, owned config objects, never live Loader/Service objects.
|
|
97
|
+
const restored = entries.filter(entry => !reserved.has(entry.id)).map(entry => {
|
|
98
|
+
if (entry.id !== 'web') return entry
|
|
99
|
+
const copy = { ...entry }
|
|
100
|
+
if (Object.hasOwn(original, 'isolate')) copy.isolate = original.isolate
|
|
101
|
+
else delete copy.isolate
|
|
102
|
+
return copy
|
|
103
|
+
})
|
|
104
|
+
return isDeepStrictEqual(restored, baseline)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Read and compose the proposed normal launch without loadProfile's initialization/normalization writes. */
|
|
108
|
+
export async function inspectProfileSearchComposition({ profileDir, home, installAnchor, patches = [] }, suppliedBoot) {
|
|
109
|
+
const native = suppliedBoot ?? await import(pathToFileURL(createRequire(installAnchor).resolve('@deepseek-ai/dsh-app-boot')).href)
|
|
110
|
+
const { readProfileManifest, resolveBundleDir, loadOverlayPatches, loadOptionalPatches, composeEntries } = native
|
|
111
|
+
const bin = 'copilot-search-preflight'
|
|
112
|
+
const manifest = readProfileManifest(bin, profileDir)
|
|
113
|
+
const reasons = new Set()
|
|
114
|
+
// Public CLI prepareProfile rewrites cordis.yml to [] before normal launch.
|
|
115
|
+
// Never certify rows that only exist in that disposable file, or rewrite it.
|
|
116
|
+
const root = loadOptionalPatches(bin, join(profileDir, 'cordis.yml'))
|
|
117
|
+
if (root?.length) return reportCandidate(new Set(['NONEMPTY_DISPOSABLE_PROFILE_ROOT']))
|
|
118
|
+
const candidate = loadOverlayPatches(bin, fileURLToPath(new URL('../cordis.patch.yml', import.meta.url)))
|
|
119
|
+
const layers = []
|
|
120
|
+
const baselineLayers = []
|
|
121
|
+
let copilotLayers = 0
|
|
122
|
+
let candidateIndex
|
|
123
|
+
function addCandidate() {
|
|
124
|
+
candidateIndex ??= layers.length
|
|
125
|
+
layers.push(candidate)
|
|
126
|
+
// The routing-free comparison retains the account row and its user config.
|
|
127
|
+
baselineLayers.push([{ insert: [{ id: 'github-copilot', name: 'dsh-github-copilot' }] }])
|
|
128
|
+
}
|
|
129
|
+
for (const name of manifest.dsh?.profile?.bundles ?? []) {
|
|
130
|
+
const directory = resolveBundleDir(bin, name, installAnchor, profileDir)
|
|
131
|
+
const bundle = readProfileManifest(bin, directory)
|
|
132
|
+
if (bundle.name === 'dsh-github-copilot') {
|
|
133
|
+
copilotLayers++
|
|
134
|
+
// Updating replaces our existing slot, not its position in bundle order.
|
|
135
|
+
addCandidate()
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
if (typeof bundle.dsh?.bundle?.patch !== 'string') throw new Error('INVALID_BUNDLE_MANIFEST')
|
|
139
|
+
const layer = loadOverlayPatches(bin, resolve(directory, bundle.dsh.bundle.patch))
|
|
140
|
+
layers.push(layer)
|
|
141
|
+
baselineLayers.push(layer)
|
|
142
|
+
}
|
|
143
|
+
if (copilotLayers === 0) addCandidate()
|
|
144
|
+
const late = [
|
|
145
|
+
loadOptionalPatches(bin, join(profileDir, 'cordis.patch.yml')) ?? [],
|
|
146
|
+
loadOptionalPatches(bin, join(home, 'cordis.patch.yml')) ?? [],
|
|
147
|
+
...patches.map(file => loadOverlayPatches(bin, file)),
|
|
148
|
+
]
|
|
149
|
+
const warn = () => reasons.add('UNAPPLIED_PATCH_REQUIRES_REVIEW')
|
|
150
|
+
// Public composeEntries flattens each complete stack in one pass. Repeatedly
|
|
151
|
+
// patching a previously composed tree would change the include's id index.
|
|
152
|
+
const beforeGuard = composeEntries(layers.slice(0, candidateIndex), warn)
|
|
153
|
+
const baseline = composeEntries([...baselineLayers, ...late], warn)
|
|
154
|
+
for (const entries of [beforeGuard, baseline]) {
|
|
155
|
+
for (const reason of checkSearchComposition(entries).reasons) reasons.add(reason)
|
|
156
|
+
}
|
|
157
|
+
const entries = composeEntries([...layers, ...late], warn)
|
|
158
|
+
if (copilotLayers > 1 || accountRows(entries).length !== 1) reasons.add('DUPLICATE_COPILOT_ACCOUNT_ENTRY')
|
|
159
|
+
if (!candidateViable(entries, baseline)) reasons.add('CANDIDATE_ROUTING_NOT_VIABLE')
|
|
160
|
+
return reportCandidate(reasons)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parse(args) {
|
|
164
|
+
const result = { patches: [] }
|
|
165
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
166
|
+
const value = args[index + 1]
|
|
167
|
+
if (!value || !isAbsolute(value)) throw new Error('INVALID_ARGUMENTS')
|
|
168
|
+
const names = { '--profile-dir': 'profileDir', '--home': 'home', '--install-anchor': 'installAnchor' }
|
|
169
|
+
const key = Object.hasOwn(names, args[index]) ? names[args[index]] : undefined
|
|
170
|
+
if (args[index] === '--patch') result.patches.push(value)
|
|
171
|
+
else if (!key || result[key]) throw new Error('INVALID_ARGUMENTS')
|
|
172
|
+
else result[key] = value
|
|
173
|
+
}
|
|
174
|
+
if (!result.profileDir || !result.home || !result.installAnchor) throw new Error('INVALID_ARGUMENTS')
|
|
175
|
+
return result
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
179
|
+
try {
|
|
180
|
+
const options = parse(process.argv.slice(2))
|
|
181
|
+
// Refuse missing profiles before any public reader could initialize one.
|
|
182
|
+
await readFile(join(options.profileDir, 'package.json'), 'utf8')
|
|
183
|
+
const result = await inspectProfileSearchComposition(options)
|
|
184
|
+
console.log(JSON.stringify(result))
|
|
185
|
+
if (!result.supported) process.exitCode = 1
|
|
186
|
+
} catch {
|
|
187
|
+
// Parse/provider-config errors can contain credentials. Never echo them.
|
|
188
|
+
console.log(JSON.stringify({ schemaVersion: 1, supported: false, reasons: ['PREFLIGHT_INPUT_OR_PUBLIC_API_FAILED'], scope: 'read-only; no boot/install attempted' }))
|
|
189
|
+
process.exitCode = 2
|
|
190
|
+
}
|
|
191
|
+
}
|