dsh-better-copilot 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +28 -6
- package/dist/client.js +98 -0
- package/dist/guard.d.ts +37 -2
- package/dist/guard.js +82 -1
- package/dist/identity.d.ts +20 -0
- package/dist/identity.js +53 -7
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/plugin.js +18 -20
- package/dist/schema.d.ts +18 -1
- package/dist/schema.js +31 -28
- package/package.json +41 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — read-only subagent route badge
|
|
4
|
+
|
|
5
|
+
- Add a browser client half that shows an addressed subagent's effective model
|
|
6
|
+
and reasoning effort in the Web session header, beside the title. The label is
|
|
7
|
+
text only: no selector, no click target, no Host write, because the child's
|
|
8
|
+
route is fixed at creation and reused on resume.
|
|
9
|
+
- Read the route from the existing `modelSelection` session projection and show
|
|
10
|
+
an adapter-defaulted effort as `default`. Provider/model/effort ids render
|
|
11
|
+
verbatim; catalog display names are not available to client plugins.
|
|
12
|
+
- Keep ordinary conversations and the DSH-rendered subagent catalog untouched.
|
|
13
|
+
Ship the bundle through `./client` and a `dsh.client` declaration, with render
|
|
14
|
+
tests and the publication allowlist updated.
|
|
15
|
+
|
|
16
|
+
## 0.1.1 — DSH 0.2.2-rc.1 compatibility
|
|
17
|
+
|
|
18
|
+
- Retarget the manifest, peer ranges and development pins to DSH `0.2.2-rc.1`
|
|
19
|
+
with `cordis ~4.1.0` and `schemastery ~3.19.0`.
|
|
20
|
+
- Move the Gemini tool-declaration guard from the `system-prompt/assemble`
|
|
21
|
+
waterfall to the Copilot wire. `0.2.2-rc.1` assembles tool declarations
|
|
22
|
+
outside the presentation waterfall, so the guard now rewrites the serialized
|
|
23
|
+
request body inside the same refcounted interceptor that carries the wire
|
|
24
|
+
identity. It still adds only a missing root `properties` bucket, still matches
|
|
25
|
+
configured providers and model families, keeps `SKIPPED_*` reason codes and
|
|
26
|
+
`logFixes` summaries, and now runs even when `DSH_COPILOT_WIRE_IDENTITY=off`.
|
|
27
|
+
- Export `installCopilotRequestTransform`, `guardCopilotRequestBody`,
|
|
28
|
+
`GuardedBody` and `MAX_GUARDED_BODY_BYTES`; `./guard` keeps exporting
|
|
29
|
+
`matchesRoute`, `normalizeToolSchemas`, `classifyParameters`,
|
|
30
|
+
`TOOL_SCHEMA_SKIPPED` and their types.
|
|
31
|
+
- Follow the adapter's `1_000_000`/`128_000` profile defaults, its
|
|
32
|
+
`llm-pi-ai/request-headers` waterfall and the branded reasoning-effort id.
|
|
33
|
+
|
|
3
34
|
## 0.1.0 — consolidated Copilot plugin
|
|
4
35
|
|
|
5
36
|
First release of `dsh-better-copilot`, merging three independently installable
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ packages. Their behaviour is consolidated here; see the migration notes below.
|
|
|
19
19
|
|
|
20
20
|
## Compatibility
|
|
21
21
|
|
|
22
|
-
- DSH and DSH peer packages: `0.2.
|
|
22
|
+
- DSH and DSH peer packages: `0.2.2-rc.1`.
|
|
23
23
|
- `@earendil-works/pi-ai`: `^1.1.0` (host-aligned peer range; tested at `1.1.0` and `1.3.0`).
|
|
24
24
|
- Node.js: `>=22.19.0`.
|
|
25
25
|
|
|
@@ -92,11 +92,33 @@ existing Web search settings card is DeepSeek-specific and is not a chooser.
|
|
|
92
92
|
|
|
93
93
|
## Guard
|
|
94
94
|
|
|
95
|
-
The guard is
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
The guard is a Copilot wire-body transform. `0.2.2-rc.1` assembles tool
|
|
96
|
+
declarations outside the presentation waterfall and treats them as
|
|
97
|
+
provider-owned, so the last supported seam is the serialized request the adapter
|
|
98
|
+
dispatches. Inside the same refcounted, trusted-path-checked interceptor that
|
|
99
|
+
carries the wire identity, it adds an empty root `properties` bucket to object
|
|
100
|
+
tool schemas that lack one for a configured Copilot route and model family, and
|
|
101
|
+
leaves every other byte of the request untouched. It never replaces an adapter or
|
|
102
|
+
another plugin's settings, and a guard failure dispatches the original body.
|
|
103
|
+
|
|
104
|
+
## Subagent model badge (Web)
|
|
105
|
+
|
|
106
|
+
The package now carries a small browser client half. When a subagent
|
|
107
|
+
conversation is open in the Web GUI, the session header shows the child's
|
|
108
|
+
effective route as muted text next to the title — for example
|
|
109
|
+
`gpt-6.1-sol · high`. The label is read-only: the plugin renders no selector and
|
|
110
|
+
writes nothing back, because the Host fixes a child's provider, model and
|
|
111
|
+
reasoning effort when the subagent is created and reuses the recorded route on
|
|
112
|
+
resume. An adapter-defaulted effort appears as `default`, and the tooltip and
|
|
113
|
+
accessible name say that the route is fixed.
|
|
114
|
+
|
|
115
|
+
The badge reads the child Session's `modelSelection` projection; it adds no Host
|
|
116
|
+
service, configuration field or RPC. Ids are shown exactly as the Host records
|
|
117
|
+
them, because catalog display names are not exposed to third-party client
|
|
118
|
+
plugins. Ordinary conversations are unchanged, and the subagent catalog
|
|
119
|
+
dropdown is untouched: DSH renders those rows in its own client package rather
|
|
120
|
+
than through an extension slot. The browser half is published as `./client`
|
|
121
|
+
and declared through `dsh.client`; no additional wiring is needed.
|
|
100
122
|
|
|
101
123
|
## Migration from the three predecessor packages
|
|
102
124
|
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference path="./client.types.d.ts" />
|
|
3
|
+
/* Read-only subagent route badge for the Web conversation header.
|
|
4
|
+
* The Host fixes a subagent's provider, model and reasoning effort when the
|
|
5
|
+
* child is created, so this client half only presents them. It never renders a
|
|
6
|
+
* selection control and writes nothing back to the Session.
|
|
7
|
+
*/
|
|
8
|
+
window.__ModuleLoader__.load({
|
|
9
|
+
id: 'dsh-better-copilot',
|
|
10
|
+
factory(require) {
|
|
11
|
+
const React = require('react');
|
|
12
|
+
const h = React.createElement;
|
|
13
|
+
const NS = 'better-copilot.subagent';
|
|
14
|
+
const SLOT = 'conversation.session.header.actions';
|
|
15
|
+
const ID = 'better-copilot-subagent-model';
|
|
16
|
+
const en = {
|
|
17
|
+
'badge.defaultEffort': 'default',
|
|
18
|
+
'badge.fixed': "Model and effort are fixed for this subagent's lifetime.",
|
|
19
|
+
'badge.aria': 'Subagent model {model} from {provider}, reasoning effort {effort}. {fixed}',
|
|
20
|
+
};
|
|
21
|
+
const zh = {
|
|
22
|
+
'badge.defaultEffort': '默认',
|
|
23
|
+
'badge.fixed': '该子智能体的模型与推理强度在创建时已固定。',
|
|
24
|
+
'badge.aria': '子智能体模型 {model}({provider}),推理强度 {effort}。{fixed}',
|
|
25
|
+
};
|
|
26
|
+
/** Validate one projected selection; anything malformed is not rendered. */
|
|
27
|
+
function readSelection(value) {
|
|
28
|
+
if (typeof value !== 'object' || value === null)
|
|
29
|
+
return null;
|
|
30
|
+
const candidate = value;
|
|
31
|
+
const provider = typeof candidate.provider === 'string' ? candidate.provider : '';
|
|
32
|
+
const model = typeof candidate.model === 'string' ? candidate.model : '';
|
|
33
|
+
if (provider === '' || model === '')
|
|
34
|
+
return null;
|
|
35
|
+
const reasoningEffort = typeof candidate.reasoningEffort === 'string' && candidate.reasoningEffort !== ''
|
|
36
|
+
? candidate.reasoningEffort
|
|
37
|
+
: undefined;
|
|
38
|
+
return reasoningEffort === undefined ? { provider, model } : { provider, model, reasoningEffort };
|
|
39
|
+
}
|
|
40
|
+
/** Read the effective selection from the modelSelection wire view. */
|
|
41
|
+
function selectedOf(view) {
|
|
42
|
+
if (typeof view !== 'object' || view === null)
|
|
43
|
+
return null;
|
|
44
|
+
const projected = view;
|
|
45
|
+
return readSelection(projected.next) ?? readSelection(projected.lastUsed);
|
|
46
|
+
}
|
|
47
|
+
/** Compose the visible label, tooltip and accessible name for one route. */
|
|
48
|
+
function subagentBadge(selection, t) {
|
|
49
|
+
const explicit = selection.reasoningEffort;
|
|
50
|
+
const effort = explicit === undefined ? t('badge.defaultEffort') : explicit;
|
|
51
|
+
const route = explicit === undefined
|
|
52
|
+
? `${selection.provider}/${selection.model}`
|
|
53
|
+
: `${selection.provider}/${selection.model} · ${explicit}`;
|
|
54
|
+
return {
|
|
55
|
+
label: `${selection.model} · ${effort}`,
|
|
56
|
+
title: `${route} — ${t('badge.fixed')}`,
|
|
57
|
+
aria: t('badge.aria', {
|
|
58
|
+
provider: selection.provider,
|
|
59
|
+
model: selection.model,
|
|
60
|
+
effort,
|
|
61
|
+
fixed: t('badge.fixed'),
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const badgeStyle = {
|
|
66
|
+
display: 'inline-block',
|
|
67
|
+
maxWidth: '26ch',
|
|
68
|
+
overflow: 'hidden',
|
|
69
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
70
|
+
fontSize: '12px',
|
|
71
|
+
lineHeight: '18px',
|
|
72
|
+
whiteSpace: 'nowrap',
|
|
73
|
+
textOverflow: 'ellipsis',
|
|
74
|
+
verticalAlign: 'middle',
|
|
75
|
+
cursor: 'default',
|
|
76
|
+
};
|
|
77
|
+
/** Read-only header cell: visible only for an addressed subagent's own route. */
|
|
78
|
+
function SubagentModelBadge({ sessionId, useSessions, useProjection, t }) {
|
|
79
|
+
const subagent = useSessions((state) => state.byId[sessionId]?.origin === 'subagent');
|
|
80
|
+
const view = useProjection('modelSelection');
|
|
81
|
+
if (!subagent)
|
|
82
|
+
return null;
|
|
83
|
+
const selection = selectedOf(view);
|
|
84
|
+
if (selection === null)
|
|
85
|
+
return null;
|
|
86
|
+
const badge = subagentBadge(selection, t);
|
|
87
|
+
return h('span', { style: badgeStyle, title: badge.title, 'aria-label': badge.aria }, badge.label);
|
|
88
|
+
}
|
|
89
|
+
function apply(ctx) {
|
|
90
|
+
ctx.effect(() => ctx.locale.register(NS, { en, zh }));
|
|
91
|
+
ctx.slots.inject(SLOT, () => ctx.slots.register({ name: SLOT, id: ID, order: 15, locale: NS }, SubagentModelBadge));
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
inject: ['slots', 'locale'],
|
|
95
|
+
apply,
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
package/dist/guard.d.ts
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import type { GuardSettings, SkippedTool } from './guard-types.js';
|
|
2
|
+
export { classifyParameters, matchesRoute, normalizeToolSchemas, TOOL_SCHEMA_SKIPPED } from './schema.js';
|
|
3
|
+
export type { NormalizeResult, ParameterDecision } from './schema.js';
|
|
3
4
|
export type { GuardSettings, PromptAssembly, SkippedTool, ToolSchema } from './guard-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* The largest serialized Copilot request this guard will parse. A request that
|
|
7
|
+
* carries tool declarations is far smaller; anything past this bound is left
|
|
8
|
+
* byte-identical rather than copied and re-parsed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MAX_GUARDED_BODY_BYTES: number;
|
|
11
|
+
/** One rewritten request body plus what the guard changed and refused to change. */
|
|
12
|
+
export interface GuardedBody {
|
|
13
|
+
/** The re-serialized body, or `undefined` when the request stays byte-identical. */
|
|
14
|
+
readonly body: string | undefined;
|
|
15
|
+
/** True only when at least one declaration gained a properties bucket. */
|
|
16
|
+
readonly changed: boolean;
|
|
17
|
+
/** Tool names whose declaration gained an empty properties bucket. */
|
|
18
|
+
readonly fixed: string[];
|
|
19
|
+
/** Tools left byte-identical, with a controlled reason. */
|
|
20
|
+
readonly skipped: readonly SkippedTool[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalize tool declarations on one serialized Copilot inference request.
|
|
24
|
+
*
|
|
25
|
+
* The Gemini route measures this at the HTTP boundary: it rejects a request whose
|
|
26
|
+
* tool declaration carries an object parameter schema with no properties bucket.
|
|
27
|
+
* The presentation assembly no longer exposes declarations to listeners, so the
|
|
28
|
+
* guard rewrites the exact serialized body instead — lexically the last point
|
|
29
|
+
* before dispatch, and the same place the failure is measured.
|
|
30
|
+
*
|
|
31
|
+
* Nothing is invented: a body that is not JSON, carries no tool array, exceeds
|
|
32
|
+
* the bound, or targets another route or model family is returned untouched.
|
|
33
|
+
*
|
|
34
|
+
* @param body - the serialized request body as the adapter built it.
|
|
35
|
+
* @param settings - one configuration snapshot for this request.
|
|
36
|
+
* @returns the rewrite result; `body` stays `undefined` unless something changed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function guardCopilotRequestBody(body: string, settings: GuardSettings): GuardedBody;
|
package/dist/guard.js
CHANGED
|
@@ -1 +1,82 @@
|
|
|
1
|
-
|
|
1
|
+
import { classifyParameters, matchesRoute } from './schema.js';
|
|
2
|
+
// The guard subpath keeps publishing its classification primitives: they are the
|
|
3
|
+
// same rules the wire rewrite applies, and consumers may classify a declaration
|
|
4
|
+
// without installing the transform.
|
|
5
|
+
export { classifyParameters, matchesRoute, normalizeToolSchemas, TOOL_SCHEMA_SKIPPED } from './schema.js';
|
|
6
|
+
/**
|
|
7
|
+
* The largest serialized Copilot request this guard will parse. A request that
|
|
8
|
+
* carries tool declarations is far smaller; anything past this bound is left
|
|
9
|
+
* byte-identical rather than copied and re-parsed.
|
|
10
|
+
*/
|
|
11
|
+
export const MAX_GUARDED_BODY_BYTES = 8 * 1024 * 1024;
|
|
12
|
+
/** The route this plugin owns; the wire guard runs only on its endpoints. */
|
|
13
|
+
const COPILOT_ROUTE = 'github-copilot';
|
|
14
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
15
|
+
/**
|
|
16
|
+
* Locate one tool declaration's parameter schema on either Copilot inference surface.
|
|
17
|
+
*
|
|
18
|
+
* Chat completions carries `{ type: 'function', function: { name, parameters } }`
|
|
19
|
+
* while Responses carries `{ type: 'function', name, parameters }`. Both are the
|
|
20
|
+
* OpenAI-compatible shape the adapter serializes; anything else is not ours.
|
|
21
|
+
*/
|
|
22
|
+
function declarationOf(tool) {
|
|
23
|
+
if (!isRecord(tool) || tool.type !== 'function')
|
|
24
|
+
return undefined;
|
|
25
|
+
const holder = isRecord(tool.function) ? tool.function : tool;
|
|
26
|
+
return typeof holder.name === 'string' && holder.name.length > 0 ? { holder, name: holder.name } : undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalize tool declarations on one serialized Copilot inference request.
|
|
30
|
+
*
|
|
31
|
+
* The Gemini route measures this at the HTTP boundary: it rejects a request whose
|
|
32
|
+
* tool declaration carries an object parameter schema with no properties bucket.
|
|
33
|
+
* The presentation assembly no longer exposes declarations to listeners, so the
|
|
34
|
+
* guard rewrites the exact serialized body instead — lexically the last point
|
|
35
|
+
* before dispatch, and the same place the failure is measured.
|
|
36
|
+
*
|
|
37
|
+
* Nothing is invented: a body that is not JSON, carries no tool array, exceeds
|
|
38
|
+
* the bound, or targets another route or model family is returned untouched.
|
|
39
|
+
*
|
|
40
|
+
* @param body - the serialized request body as the adapter built it.
|
|
41
|
+
* @param settings - one configuration snapshot for this request.
|
|
42
|
+
* @returns the rewrite result; `body` stays `undefined` unless something changed.
|
|
43
|
+
*/
|
|
44
|
+
export function guardCopilotRequestBody(body, settings) {
|
|
45
|
+
const unchanged = { body: undefined, changed: false, fixed: [], skipped: [] };
|
|
46
|
+
if (typeof body !== 'string' || body.length === 0 || body.length > MAX_GUARDED_BODY_BYTES)
|
|
47
|
+
return unchanged;
|
|
48
|
+
// A cheap pre-check keeps message-heavy bodies out of the JSON parser entirely.
|
|
49
|
+
if (!body.includes('"tools"'))
|
|
50
|
+
return unchanged;
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(body);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return unchanged;
|
|
57
|
+
}
|
|
58
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.tools) || parsed.tools.length === 0)
|
|
59
|
+
return unchanged;
|
|
60
|
+
if (!matchesRoute(COPILOT_ROUTE, parsed.model, settings))
|
|
61
|
+
return unchanged;
|
|
62
|
+
const declarations = parsed.tools.map(declarationOf);
|
|
63
|
+
if (declarations.every(declaration => declaration === undefined))
|
|
64
|
+
return unchanged;
|
|
65
|
+
const fixed = [];
|
|
66
|
+
const skipped = [];
|
|
67
|
+
for (const declaration of declarations) {
|
|
68
|
+
if (declaration === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
const decision = classifyParameters(declaration.holder.parameters);
|
|
71
|
+
if (decision.fix) {
|
|
72
|
+
fixed.push(declaration.name);
|
|
73
|
+
declaration.holder.parameters = { ...declaration.holder.parameters, properties: {} };
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (decision.reason !== undefined)
|
|
77
|
+
skipped.push({ tool: declaration.name, reason: decision.reason });
|
|
78
|
+
}
|
|
79
|
+
if (fixed.length === 0)
|
|
80
|
+
return { body: undefined, changed: false, fixed, skipped };
|
|
81
|
+
return { body: JSON.stringify(parsed), changed: true, fixed, skipped };
|
|
82
|
+
}
|
package/dist/identity.d.ts
CHANGED
|
@@ -49,6 +49,16 @@ export declare function isCopilotRequestPath(pathname: string): boolean;
|
|
|
49
49
|
export declare function rewriteCopilotRequestHeaders(url: URL, headers: Headers, identity: CopilotWireIdentity): boolean;
|
|
50
50
|
/** `full` is the default; `basic` is the minimal fingerprint fix; `0` disables. */
|
|
51
51
|
export declare function wireIdentityMode(env?: NodeJS.ProcessEnv): WireIdentitySetting;
|
|
52
|
+
/**
|
|
53
|
+
* Rewrite one serialized Copilot request body before dispatch.
|
|
54
|
+
*
|
|
55
|
+
* Returning a string replaces the body; `undefined` keeps it byte-identical.
|
|
56
|
+
* A throw is contained like every other pre-dispatch normalization failure.
|
|
57
|
+
*/
|
|
58
|
+
export type CopilotRequestTransform = (request: {
|
|
59
|
+
readonly url: URL;
|
|
60
|
+
readonly body: string;
|
|
61
|
+
}) => string | undefined;
|
|
52
62
|
/**
|
|
53
63
|
* Install a process-wide Copilot header interceptor.
|
|
54
64
|
*
|
|
@@ -59,3 +69,13 @@ export declare function wireIdentityMode(env?: NodeJS.ProcessEnv): WireIdentityS
|
|
|
59
69
|
* current fetch rather than reusing stale installation state.
|
|
60
70
|
*/
|
|
61
71
|
export declare function installCopilotIdentityHeaders(identity?: CopilotWireIdentity): () => void;
|
|
72
|
+
/**
|
|
73
|
+
* Install a process-wide Copilot request-body normalizer.
|
|
74
|
+
*
|
|
75
|
+
* It shares the header interceptor's controller, trust checks and release
|
|
76
|
+
* discipline, so both halves are installed once and released independently. A
|
|
77
|
+
* body is normalized only when the request is a trusted Copilot inference call
|
|
78
|
+
* and the caller passes a serialized string body; every other shape is
|
|
79
|
+
* delegated untouched.
|
|
80
|
+
*/
|
|
81
|
+
export declare function installCopilotRequestTransform(transform: CopilotRequestTransform): () => void;
|
package/dist/identity.js
CHANGED
|
@@ -196,19 +196,29 @@ function createWrapper(state, downstream) {
|
|
|
196
196
|
return downstream(input, init);
|
|
197
197
|
let url;
|
|
198
198
|
let headers;
|
|
199
|
+
let body;
|
|
199
200
|
try {
|
|
200
201
|
url = requestUrl(input);
|
|
201
202
|
if (!isTrustedCopilotUrl(url) || !isCopilotRequestPath(url.pathname))
|
|
202
203
|
return downstream(input, init);
|
|
203
204
|
headers = effectiveHeaders(input, init);
|
|
204
|
-
|
|
205
|
+
if (state.identity !== null)
|
|
206
|
+
rewriteCopilotRequestHeaders(url, headers, state.identity);
|
|
207
|
+
if (state.transform !== null && typeof init?.body === 'string') {
|
|
208
|
+
body = state.transform({ url, body: init.body });
|
|
209
|
+
// A replaced body usually changes length, so its framing cannot be reused.
|
|
210
|
+
if (body !== undefined) {
|
|
211
|
+
headers.delete('content-length');
|
|
212
|
+
headers.delete('transfer-encoding');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
205
215
|
}
|
|
206
216
|
catch {
|
|
207
217
|
// Only pre-dispatch normalization failures are fail-open; the request is
|
|
208
218
|
// delegated once and its eventual rejection is never caught or retried.
|
|
209
219
|
return downstream(input, init);
|
|
210
220
|
}
|
|
211
|
-
return downstream(input, { ...init, headers });
|
|
221
|
+
return downstream(input, body === undefined ? { ...init, headers } : { ...init, headers, body });
|
|
212
222
|
};
|
|
213
223
|
state.wrappers.add(wrapper);
|
|
214
224
|
state.downstreams.set(wrapper, downstream);
|
|
@@ -237,34 +247,70 @@ function attach(state) {
|
|
|
237
247
|
* current fetch rather than reusing stale installation state.
|
|
238
248
|
*/
|
|
239
249
|
export function installCopilotIdentityHeaders(identity = createCopilotWireIdentity()) {
|
|
250
|
+
return install({ identity });
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Install a process-wide Copilot request-body normalizer.
|
|
254
|
+
*
|
|
255
|
+
* It shares the header interceptor's controller, trust checks and release
|
|
256
|
+
* discipline, so both halves are installed once and released independently. A
|
|
257
|
+
* body is normalized only when the request is a trusted Copilot inference call
|
|
258
|
+
* and the caller passes a serialized string body; every other shape is
|
|
259
|
+
* delegated untouched.
|
|
260
|
+
*/
|
|
261
|
+
export function installCopilotRequestTransform(transform) {
|
|
262
|
+
return install({ transform });
|
|
263
|
+
}
|
|
264
|
+
function install(options) {
|
|
240
265
|
if (typeof globalThis.fetch !== 'function')
|
|
241
266
|
return () => { };
|
|
242
267
|
if (controller === undefined) {
|
|
243
268
|
controller = {
|
|
244
269
|
active: true,
|
|
245
|
-
identity,
|
|
270
|
+
identity: null,
|
|
271
|
+
transform: null,
|
|
246
272
|
installs: 0,
|
|
273
|
+
transforms: 0,
|
|
247
274
|
wrappers: new WeakSet(),
|
|
248
275
|
downstreams: new WeakMap(),
|
|
249
276
|
};
|
|
250
277
|
}
|
|
251
278
|
const state = controller;
|
|
279
|
+
const ownsIdentity = options.identity !== undefined, ownsTransform = options.transform !== undefined;
|
|
280
|
+
// The first live identity install owns it; later concurrent installs share it.
|
|
281
|
+
if (ownsIdentity && state.installs === 0)
|
|
282
|
+
state.identity = options.identity;
|
|
283
|
+
// A body transform is per installer, and the latest live one wins.
|
|
284
|
+
if (ownsTransform)
|
|
285
|
+
state.transform = options.transform;
|
|
252
286
|
try {
|
|
253
287
|
attach(state);
|
|
254
288
|
}
|
|
255
289
|
catch (error) {
|
|
256
|
-
if (state.installs === 0 && controller === state)
|
|
290
|
+
if (state.installs === 0 && state.transforms === 0 && controller === state)
|
|
257
291
|
controller = undefined;
|
|
258
292
|
throw error;
|
|
259
293
|
}
|
|
260
|
-
|
|
294
|
+
if (ownsIdentity)
|
|
295
|
+
state.installs += 1;
|
|
296
|
+
if (ownsTransform)
|
|
297
|
+
state.transforms += 1;
|
|
261
298
|
let released = false;
|
|
262
299
|
return () => {
|
|
263
300
|
if (released)
|
|
264
301
|
return;
|
|
265
302
|
released = true;
|
|
266
|
-
|
|
267
|
-
|
|
303
|
+
if (ownsIdentity) {
|
|
304
|
+
state.installs -= 1;
|
|
305
|
+
if (state.installs === 0)
|
|
306
|
+
state.identity = null;
|
|
307
|
+
}
|
|
308
|
+
if (ownsTransform) {
|
|
309
|
+
state.transforms -= 1;
|
|
310
|
+
if (state.transforms === 0)
|
|
311
|
+
state.transform = null;
|
|
312
|
+
}
|
|
313
|
+
if (state.installs > 0 || state.transforms > 0)
|
|
268
314
|
return;
|
|
269
315
|
state.active = false;
|
|
270
316
|
if (state.current !== undefined && globalThis.fetch === state.current.wrapper) {
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ export declare const syncCopilotCatalog: (credentials?: CredentialReader, authOp
|
|
|
9
9
|
export { DEFAULT_SEARCH_MODEL } from './config.js';
|
|
10
10
|
export { liveCost, selectAccountModels } from './catalog.js';
|
|
11
11
|
export { DiscoveryError } from './discovery.js';
|
|
12
|
-
export { createCopilotWireIdentity, installCopilotIdentityHeaders, isCopilotRequestPath, isTrustedCopilotUrl, rewriteCopilotRequestHeaders, wireIdentityMode, } from './identity.js';
|
|
12
|
+
export { createCopilotWireIdentity, installCopilotIdentityHeaders, installCopilotRequestTransform, isCopilotRequestPath, isTrustedCopilotUrl, rewriteCopilotRequestHeaders, wireIdentityMode, } from './identity.js';
|
|
13
|
+
export { guardCopilotRequestBody, MAX_GUARDED_BODY_BYTES } from './guard.js';
|
|
14
|
+
export type { GuardedBody } from './guard.js';
|
|
13
15
|
export { COPILOT_SEARCH_PROVIDER_ID, CopilotSearchProvider } from './provider.js';
|
|
14
16
|
export { matchesRoute, normalizeToolSchemas, TOOL_SCHEMA_SKIPPED } from './schema.js';
|
|
15
17
|
export { loadRuntime } from './runtime.js';
|
|
@@ -19,6 +21,6 @@ export type { BetterCopilotDependencies, BetterCopilotPlugin } from './plugin.js
|
|
|
19
21
|
export type { AccountModelOptions } from './catalog.js';
|
|
20
22
|
export type { NormalizeResult } from './schema.js';
|
|
21
23
|
export type { AccountSelection, CopilotAuthOptions as CatalogAuthOptions, CredentialReader, ModelCatalog, SyncResult, } from './catalog-types.js';
|
|
22
|
-
export type { CopilotWireIdentity, CopilotWireIdentityOverrides, WireIdentityMode, WireIdentitySetting, } from './identity.js';
|
|
24
|
+
export type { CopilotRequestTransform, CopilotWireIdentity, CopilotWireIdentityOverrides, WireIdentityMode, WireIdentitySetting, } from './identity.js';
|
|
23
25
|
export type { CopilotAuthOptions as SearchAuthOptions, CopilotSearchOptions, ResolvedCopilotAuth, SearchMode, } from './search-types.js';
|
|
24
26
|
export type { GuardSettings, PromptAssembly, SkippedTool, ToolSchema } from './guard-types.js';
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,8 @@ export const syncCopilotCatalog = (credentials, authOptions) => plugin.sync(cred
|
|
|
11
11
|
export { DEFAULT_SEARCH_MODEL } from './config.js';
|
|
12
12
|
export { liveCost, selectAccountModels } from './catalog.js';
|
|
13
13
|
export { DiscoveryError } from './discovery.js';
|
|
14
|
-
export { createCopilotWireIdentity, installCopilotIdentityHeaders, isCopilotRequestPath, isTrustedCopilotUrl, rewriteCopilotRequestHeaders, wireIdentityMode, } from './identity.js';
|
|
14
|
+
export { createCopilotWireIdentity, installCopilotIdentityHeaders, installCopilotRequestTransform, isCopilotRequestPath, isTrustedCopilotUrl, rewriteCopilotRequestHeaders, wireIdentityMode, } from './identity.js';
|
|
15
|
+
export { guardCopilotRequestBody, MAX_GUARDED_BODY_BYTES } from './guard.js';
|
|
15
16
|
export { COPILOT_SEARCH_PROVIDER_ID, CopilotSearchProvider } from './provider.js';
|
|
16
17
|
export { matchesRoute, normalizeToolSchemas, TOOL_SCHEMA_SKIPPED } from './schema.js';
|
|
17
18
|
export { loadRuntime } from './runtime.js';
|
package/dist/plugin.js
CHANGED
|
@@ -2,9 +2,10 @@ import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
|
|
|
2
2
|
import { createCopilotAuthResolver } from './auth.js';
|
|
3
3
|
import { createConfig } from './config.js';
|
|
4
4
|
import { DiscoveryError, syncCopilotCatalog } from './discovery.js';
|
|
5
|
-
import { createCopilotWireIdentity, installCopilotIdentityHeaders, wireIdentityMode } from './identity.js';
|
|
5
|
+
import { createCopilotWireIdentity, installCopilotIdentityHeaders, installCopilotRequestTransform, wireIdentityMode } from './identity.js';
|
|
6
6
|
import { CopilotSearchProvider } from './provider.js';
|
|
7
|
-
import {
|
|
7
|
+
import { TOOL_SCHEMA_SKIPPED } from './schema.js';
|
|
8
|
+
import { guardCopilotRequestBody } from './guard.js';
|
|
8
9
|
export const name = 'better-copilot';
|
|
9
10
|
/** Search and guard are optional capabilities; the adapter requires these two. */
|
|
10
11
|
export const inject = ['llm', 'credentials'];
|
|
@@ -89,33 +90,30 @@ export function createPlugin(dependencies) {
|
|
|
89
90
|
ctx.effect(() => install(), 'copilot: wire identity headers');
|
|
90
91
|
});
|
|
91
92
|
}
|
|
92
|
-
// 2. Gemini tool-declaration guard:
|
|
93
|
+
// 2. Gemini tool-declaration guard: one contained wire transform. The
|
|
94
|
+
// assembly waterfall no longer sees provider-owned declarations, so the
|
|
95
|
+
// guard rewrites the serialized request at the boundary that measures
|
|
96
|
+
// the defect. It is installed even when wire identity is opted out.
|
|
93
97
|
contain('tool-schema guard', 'Gemini tool-schema normalization', () => {
|
|
94
|
-
|
|
95
|
-
// An upstream failure belongs to the downstream chain and propagates unchanged.
|
|
96
|
-
const downstream = await next();
|
|
98
|
+
const transform = ({ body }) => {
|
|
97
99
|
try {
|
|
98
100
|
const settings = guardSettings(config.toolSchema);
|
|
99
|
-
|
|
100
|
-
return downstream;
|
|
101
|
-
const result = normalizeToolSchemas(downstream.tools);
|
|
101
|
+
const result = guardCopilotRequestBody(body, settings);
|
|
102
102
|
if (settings.logFixes) {
|
|
103
|
-
if (result.fixed.length > 0)
|
|
104
|
-
log('info', 'better-copilot: added a properties bucket to %d tool schema(s)
|
|
105
|
-
|
|
106
|
-
if (result.skipped.length > 0) {
|
|
103
|
+
if (result.fixed.length > 0)
|
|
104
|
+
log('info', 'better-copilot: added a properties bucket to %d tool schema(s) on the Copilot wire', result.fixed.length);
|
|
105
|
+
if (result.skipped.length > 0)
|
|
107
106
|
log('warn', 'better-copilot: %s left %d tool schema(s) unchanged: %s', TOOL_SCHEMA_SKIPPED, result.skipped.length, JSON.stringify(result.skipped));
|
|
108
|
-
}
|
|
109
107
|
}
|
|
110
|
-
|
|
111
|
-
return downstream;
|
|
112
|
-
return { ...downstream, tools: result.tools };
|
|
108
|
+
return result.body;
|
|
113
109
|
}
|
|
114
110
|
catch {
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
// The request is delegated byte-identical; a guard failure never fails a call.
|
|
112
|
+
log('warn', 'better-copilot: skipped one request after an internal error');
|
|
113
|
+
return undefined;
|
|
117
114
|
}
|
|
118
|
-
}
|
|
115
|
+
};
|
|
116
|
+
ctx.effect(() => installCopilotRequestTransform(transform), 'copilot: tool-schema wire normalization');
|
|
119
117
|
});
|
|
120
118
|
// 3. Search provider: mounts only when the web service exists.
|
|
121
119
|
contain('search provider', 'Copilot web search', () => {
|
package/dist/schema.d.ts
CHANGED
|
@@ -11,14 +11,31 @@ export interface NormalizeResult {
|
|
|
11
11
|
/** Tools left byte-identical, with a controlled reason. */
|
|
12
12
|
readonly skipped: readonly SkippedTool[];
|
|
13
13
|
}
|
|
14
|
+
/** The verdict for one declaration's parameter schema. */
|
|
15
|
+
export type ParameterDecision = {
|
|
16
|
+
readonly fix: true;
|
|
17
|
+
} | {
|
|
18
|
+
readonly fix: false;
|
|
19
|
+
readonly reason?: string;
|
|
20
|
+
};
|
|
14
21
|
/**
|
|
15
|
-
*
|
|
22
|
+
* Decide whether one root parameter schema needs an empty properties bucket.
|
|
16
23
|
*
|
|
17
24
|
* The GitHub Copilot Gemini route rejects a request whose tool declaration carries
|
|
18
25
|
* an object parameter schema with no properties bucket (measured: HTTP 400 with a
|
|
19
26
|
* plain-text body). Adding the bucket is JSON-Schema neutral and does not change
|
|
20
27
|
* unions, required fields, additionalProperties or descriptions. Nothing else is
|
|
21
28
|
* rewritten: references, non-object schemas and every nested node stay untouched.
|
|
29
|
+
*
|
|
30
|
+
* @param parameters - the root schema exactly as the declaration carries it.
|
|
31
|
+
* @returns whether to add the bucket, or the controlled reason it stays untouched.
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyParameters(parameters: unknown): ParameterDecision;
|
|
34
|
+
/**
|
|
35
|
+
* Add an empty root properties bucket to object parameter schemas that lack one.
|
|
36
|
+
*
|
|
37
|
+
* @param tools - the declarations one assembly or request carries.
|
|
38
|
+
* @returns the rewritten declarations plus every fixed and skipped tool name.
|
|
22
39
|
*/
|
|
23
40
|
export declare function normalizeToolSchemas(tools: readonly ToolSchema[]): NormalizeResult;
|
|
24
41
|
/**
|
package/dist/schema.js
CHANGED
|
@@ -7,48 +7,51 @@ const isPlainObject = (value) => {
|
|
|
7
7
|
return prototype === Object.prototype || prototype === null;
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Decide whether one root parameter schema needs an empty properties bucket.
|
|
11
11
|
*
|
|
12
12
|
* The GitHub Copilot Gemini route rejects a request whose tool declaration carries
|
|
13
13
|
* an object parameter schema with no properties bucket (measured: HTTP 400 with a
|
|
14
14
|
* plain-text body). Adding the bucket is JSON-Schema neutral and does not change
|
|
15
15
|
* unions, required fields, additionalProperties or descriptions. Nothing else is
|
|
16
16
|
* rewritten: references, non-object schemas and every nested node stay untouched.
|
|
17
|
+
*
|
|
18
|
+
* @param parameters - the root schema exactly as the declaration carries it.
|
|
19
|
+
* @returns whether to add the bucket, or the controlled reason it stays untouched.
|
|
20
|
+
*/
|
|
21
|
+
export function classifyParameters(parameters) {
|
|
22
|
+
if (parameters === undefined)
|
|
23
|
+
return { fix: false, reason: 'SKIPPED_MISSING_PARAMETERS' };
|
|
24
|
+
if (!isPlainObject(parameters))
|
|
25
|
+
return { fix: false, reason: 'SKIPPED_NOT_OBJECT' };
|
|
26
|
+
if (Object.hasOwn(parameters, '$ref') || Object.hasOwn(parameters, '$dynamicRef'))
|
|
27
|
+
return { fix: false, reason: 'SKIPPED_REFERENCE' };
|
|
28
|
+
if (parameters.type !== 'object')
|
|
29
|
+
return { fix: false, reason: 'SKIPPED_NOT_OBJECT_TYPE' };
|
|
30
|
+
if (parameters.properties !== undefined) {
|
|
31
|
+
return isPlainObject(parameters.properties) ? { fix: false } : { fix: false, reason: 'SKIPPED_INVALID_PROPERTIES' };
|
|
32
|
+
}
|
|
33
|
+
return { fix: true };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Add an empty root properties bucket to object parameter schemas that lack one.
|
|
37
|
+
*
|
|
38
|
+
* @param tools - the declarations one assembly or request carries.
|
|
39
|
+
* @returns the rewritten declarations plus every fixed and skipped tool name.
|
|
17
40
|
*/
|
|
18
41
|
export function normalizeToolSchemas(tools) {
|
|
19
42
|
const fixed = [];
|
|
20
43
|
const skipped = [];
|
|
21
44
|
const output = [];
|
|
22
45
|
for (const tool of tools) {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
output.push(tool);
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (!isPlainObject(parameters)) {
|
|
30
|
-
skipped.push({ tool: tool.name, reason: 'SKIPPED_NOT_OBJECT' });
|
|
31
|
-
output.push(tool);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (Object.hasOwn(parameters, '$ref') || Object.hasOwn(parameters, '$dynamicRef')) {
|
|
35
|
-
skipped.push({ tool: tool.name, reason: 'SKIPPED_REFERENCE' });
|
|
36
|
-
output.push(tool);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (parameters.type !== 'object') {
|
|
40
|
-
skipped.push({ tool: tool.name, reason: 'SKIPPED_NOT_OBJECT_TYPE' });
|
|
41
|
-
output.push(tool);
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (parameters.properties !== undefined) {
|
|
45
|
-
if (!isPlainObject(parameters.properties))
|
|
46
|
-
skipped.push({ tool: tool.name, reason: 'SKIPPED_INVALID_PROPERTIES' });
|
|
47
|
-
output.push(tool);
|
|
46
|
+
const decision = classifyParameters(tool.parameters);
|
|
47
|
+
if (decision.fix) {
|
|
48
|
+
fixed.push(tool.name);
|
|
49
|
+
output.push({ ...tool, parameters: { ...tool.parameters, properties: {} } });
|
|
48
50
|
continue;
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
if (decision.reason !== undefined)
|
|
53
|
+
skipped.push({ tool: tool.name, reason: decision.reason });
|
|
54
|
+
output.push(tool);
|
|
52
55
|
}
|
|
53
56
|
return { tools: output, changed: fixed.length > 0, fixed, skipped };
|
|
54
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-better-copilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Account-aware GitHub Copilot model discovery, Copilot-compatible wire identity, native Responses web search and a Copilot Gemini tool-declaration guard in one DeepSeek Harness plugin.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"types": "./dist/guard.d.ts",
|
|
31
31
|
"import": "./dist/guard.js"
|
|
32
32
|
},
|
|
33
|
+
"./client": "./dist/client.js",
|
|
33
34
|
"./package.json": "./package.json"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
@@ -39,19 +40,6 @@
|
|
|
39
40
|
"CHANGELOG.md",
|
|
40
41
|
"LICENSE"
|
|
41
42
|
],
|
|
42
|
-
"scripts": {
|
|
43
|
-
"build": "tsc -p tsconfig.json",
|
|
44
|
-
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json",
|
|
45
|
-
"test": "pnpm run build && node --import tsx --test test/catalog.test.ts test/discovery.test.ts test/identity.test.ts test/secrets.test.ts test/catalog-plugin.test.ts test/config.test.ts test/composition.test.ts test/auth.test.ts test/refresh.test.ts test/provider.test.ts test/responses.test.ts test/search-plugin.test.ts test/schema.test.ts test/guard-plugin.test.ts test/patch.test.ts",
|
|
46
|
-
"test:integration": "pnpm run build && node --import tsx --test test/integration/nested-layout.test.ts test/integration/catalog-runtime.test.ts test/integration/enterprise.test.ts test/integration/identity-wire.test.ts test/integration/search-runtime.test.ts test/integration/guard-runtime.test.ts test/integration/scoped.test.ts test/integration/wire.test.ts",
|
|
47
|
-
"test:types": "pnpm run build && tsc -p test/types/tsconfig.json",
|
|
48
|
-
"test:pack": "pnpm run build && tsx ../../scripts/check-pack.ts .",
|
|
49
|
-
"check": "pnpm run typecheck && tsx ../../scripts/check.ts",
|
|
50
|
-
"check:secrets": "tsx ../../scripts/check-secrets.ts",
|
|
51
|
-
"prepare": "pnpm run build",
|
|
52
|
-
"prepublishOnly": "pnpm run verify",
|
|
53
|
-
"verify": "pnpm run check:secrets && pnpm run check && pnpm run test && pnpm run test:integration && pnpm run test:types && pnpm run test:pack"
|
|
54
|
-
},
|
|
55
43
|
"keywords": [
|
|
56
44
|
"deepseek-harness",
|
|
57
45
|
"dsh",
|
|
@@ -75,39 +63,58 @@
|
|
|
75
63
|
},
|
|
76
64
|
"engines": {
|
|
77
65
|
"node": ">=22.19.0",
|
|
78
|
-
"dsh": "0.2.
|
|
66
|
+
"dsh": "0.2.2-rc.1"
|
|
79
67
|
},
|
|
80
68
|
"publishConfig": {
|
|
81
69
|
"access": "public",
|
|
82
70
|
"registry": "https://registry.npmjs.org/"
|
|
83
71
|
},
|
|
84
72
|
"peerDependencies": {
|
|
85
|
-
"@deepseek-ai/cordis": "~4.0
|
|
86
|
-
"@deepseek-ai/dsh-credentials": "0.2.
|
|
87
|
-
"@deepseek-ai/dsh-launch-environment": "0.2.
|
|
88
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.2.
|
|
89
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
90
|
-
"@deepseek-ai/dsh-web": "0.2.
|
|
91
|
-
"@deepseek-ai/schemastery": "~3.
|
|
73
|
+
"@deepseek-ai/cordis": "~4.1.0",
|
|
74
|
+
"@deepseek-ai/dsh-credentials": "0.2.2-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-launch-environment": "0.2.2-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.2.2-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-web": "0.2.2-rc.1",
|
|
79
|
+
"@deepseek-ai/schemastery": "~3.19.0",
|
|
92
80
|
"@earendil-works/pi-ai": "^1.1.0"
|
|
93
81
|
},
|
|
94
82
|
"devDependencies": {
|
|
95
|
-
"@deepseek-ai/cordis": "4.0
|
|
96
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
97
|
-
"@deepseek-ai/dsh-credentials": "0.2.
|
|
98
|
-
"@deepseek-ai/dsh-launch-environment": "0.2.
|
|
99
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.2.
|
|
100
|
-
"@deepseek-ai/dsh-scope": "0.2.
|
|
101
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
102
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
103
|
-
"@deepseek-ai/dsh-web": "0.2.
|
|
104
|
-
"@deepseek-ai/schemastery": "3.
|
|
105
|
-
"@earendil-works/pi-ai": "1.3.0"
|
|
83
|
+
"@deepseek-ai/cordis": "4.1.0",
|
|
84
|
+
"@deepseek-ai/dsh-agent": "0.2.2-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-credentials": "0.2.2-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-launch-environment": "0.2.2-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.2.2-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-scope": "0.2.2-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-session": "0.2.2-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-web": "0.2.2-rc.1",
|
|
92
|
+
"@deepseek-ai/schemastery": "3.19.0",
|
|
93
|
+
"@earendil-works/pi-ai": "1.3.0",
|
|
94
|
+
"@types/react": "^19.3.0",
|
|
95
|
+
"@types/react-dom": "^19.3.0",
|
|
96
|
+
"react": "^19.3.0",
|
|
97
|
+
"react-dom": "^19.3.0"
|
|
106
98
|
},
|
|
107
99
|
"dsh": {
|
|
108
100
|
"manifestVersion": 1,
|
|
109
101
|
"bundle": {
|
|
110
102
|
"patch": "./cordis.patch.yml"
|
|
103
|
+
},
|
|
104
|
+
"client": {
|
|
105
|
+
"platform": "web",
|
|
106
|
+
"immediately": true
|
|
111
107
|
}
|
|
108
|
+
},
|
|
109
|
+
"scripts": {
|
|
110
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
|
|
111
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json && tsc -p tsconfig.client.json --noEmit",
|
|
112
|
+
"test": "pnpm run build && node --import tsx --test test/catalog.test.ts test/discovery.test.ts test/identity.test.ts test/secrets.test.ts test/catalog-plugin.test.ts test/config.test.ts test/composition.test.ts test/auth.test.ts test/refresh.test.ts test/provider.test.ts test/responses.test.ts test/search-plugin.test.ts test/schema.test.ts test/guard.test.ts test/guard-plugin.test.ts test/patch.test.ts test/client.render.test.ts",
|
|
113
|
+
"test:integration": "pnpm run build && node --import tsx --test test/integration/nested-layout.test.ts test/integration/catalog-runtime.test.ts test/integration/enterprise.test.ts test/integration/identity-wire.test.ts test/integration/search-runtime.test.ts test/integration/guard-runtime.test.ts test/integration/wire.test.ts",
|
|
114
|
+
"test:types": "pnpm run build && tsc -p test/types/tsconfig.json",
|
|
115
|
+
"test:pack": "pnpm run build && tsx ../../scripts/check-pack.ts .",
|
|
116
|
+
"check": "pnpm run typecheck && tsx ../../scripts/check.ts",
|
|
117
|
+
"check:secrets": "tsx ../../scripts/check-secrets.ts",
|
|
118
|
+
"verify": "pnpm run check:secrets && pnpm run check && pnpm run test && pnpm run test:integration && pnpm run test:types && pnpm run test:pack"
|
|
112
119
|
}
|
|
113
|
-
}
|
|
120
|
+
}
|