dsh-reference-anything 0.2.5 → 0.3.1
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/NOTICE.md +5 -34
- package/README.md +78 -49
- package/cordis.patch.yml +18 -20
- package/lib/client.js +910 -1439
- package/lib/contract.d.ts +1 -13
- package/lib/contract.js +2 -7
- package/lib/host.d.ts +7 -11
- package/lib/host.js +1 -7
- package/lib/opencli.d.ts +5 -1
- package/lib/opencli.js +26 -43
- package/lib/reference.d.ts +3 -11
- package/lib/reference.js +14 -55
- package/lib/sources/web-chat/index.d.ts +1 -0
- package/lib/sources/web-chat/index.js +5 -0
- package/lib/store/spec.d.ts +9 -0
- package/lib/sync/index.js +4 -4
- package/lib/typert.js +1 -2
- package/lib/web.js +1 -1
- package/lib/wire.d.ts +20 -0
- package/lib/wire.js +13 -7
- package/opencli-plugin/common.js +65 -21
- package/opencli-plugin/opencli-plugin.json +1 -1
- package/opencli-plugin/package.json +1 -1
- package/package.json +17 -12
- package/lib/workspace.d.ts +0 -18
- package/lib/workspace.js +0 -81
package/lib/contract.d.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { InvocationDescriptor } from '@deepseek-ai/dsh-typert-protocol';
|
|
3
|
-
export declare const workspaceEntrySchema: z.ZodReadonly<z.ZodObject<{
|
|
4
|
-
path: z.ZodString;
|
|
5
|
-
kind: z.ZodEnum<{
|
|
6
|
-
file: "file";
|
|
7
|
-
directory: "directory";
|
|
8
|
-
}>;
|
|
9
|
-
}, z.core.$strip>>;
|
|
10
|
-
export declare const sessionCandidateSchema: z.ZodReadonly<z.ZodObject<{
|
|
11
|
-
sessionId: z.ZodString;
|
|
12
|
-
label: z.ZodString;
|
|
13
|
-
cwd: z.ZodOptional<z.ZodString>;
|
|
14
|
-
createdAt: z.ZodNumber;
|
|
15
|
-
}, z.core.$strip>>;
|
|
16
3
|
export declare const searchInputSchema: z.ZodReadonly<z.ZodObject<{
|
|
17
4
|
query: z.ZodString;
|
|
18
5
|
provider: z.ZodOptional<z.ZodEnum<{
|
|
@@ -79,6 +66,7 @@ export declare const healthSchema: z.ZodReadonly<z.ZodObject<{
|
|
|
79
66
|
daemonVersion: z.ZodOptional<z.ZodString>;
|
|
80
67
|
daemonStale: z.ZodBoolean;
|
|
81
68
|
connectivityOk: z.ZodBoolean;
|
|
69
|
+
connectivityChecked: z.ZodBoolean;
|
|
82
70
|
pluginVersion: z.ZodOptional<z.ZodString>;
|
|
83
71
|
adapterCommandsReady: z.ZodBoolean;
|
|
84
72
|
adapterCompatible: z.ZodBoolean;
|
package/lib/contract.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { providerSchema, settingsRecordSchema } from "./wire.js";
|
|
3
|
-
export const workspaceEntrySchema = z.object({ path: z.string(), kind: z.enum(['file', 'directory']) }).readonly();
|
|
4
|
-
export const sessionCandidateSchema = z.object({ sessionId: z.string(), label: z.string(), cwd: z.string().optional(), createdAt: z.number() }).readonly();
|
|
5
|
-
const agentLookup = { name: 'agent', wire: 'agentId', source: 'lookup', lookup: 'agent',
|
|
6
|
-
codec: { mode: 'strict', typeSymbol: '@deepseek-ai/dsh-session/types#SessionId', schema: z.string().min(1) } };
|
|
7
3
|
export const searchInputSchema = z.object({
|
|
8
4
|
query: z.string(), provider: providerSchema.optional(), limit: z.number().int().min(1).max(100),
|
|
9
5
|
}).readonly();
|
|
@@ -19,7 +15,7 @@ export const healthSchema = z.object({
|
|
|
19
15
|
daemonRunning: z.boolean(), extensionConnected: z.boolean(), extensionState: extensionStateSchema,
|
|
20
16
|
extensionVersion: z.string().optional(), profileCount: z.number().int().nonnegative().optional(),
|
|
21
17
|
opencliCompatible: z.boolean(), daemonVersion: z.string().optional(), daemonStale: z.boolean(),
|
|
22
|
-
connectivityOk: z.boolean(), pluginVersion: z.string().optional(), adapterCommandsReady: z.boolean(), adapterCompatible: z.boolean(),
|
|
18
|
+
connectivityOk: z.boolean(), connectivityChecked: z.boolean(), pluginVersion: z.string().optional(), adapterCommandsReady: z.boolean(), adapterCompatible: z.boolean(),
|
|
23
19
|
versionError: z.string().optional(), daemonError: z.string().optional(), pluginError: z.string().optional(), doctorError: z.string().optional(),
|
|
24
20
|
}).readonly();
|
|
25
21
|
export const browserProfileSchema = z.object({ id: z.string(), alias: z.string().optional(), connected: z.boolean(), isDefault: z.boolean() }).readonly();
|
|
@@ -72,10 +68,9 @@ export const providerSyncStateSchema = z.object({
|
|
|
72
68
|
lastSyncAt: z.string(), lastCompleteScanAt: z.string(), error: z.string(),
|
|
73
69
|
}).readonly();
|
|
74
70
|
export const REFERENCE_ANYTHING_INVOCATIONS = [
|
|
75
|
-
descriptor('workspaceSearch', [agentLookup], strict('WorkspaceEntry[]', z.array(workspaceEntrySchema)), true),
|
|
76
|
-
descriptor('sessionSearch', [agentLookup, { name: 'input', wire: 'input', source: 'json', codec: strict('SessionSearchInput', z.object({ query: z.string(), limit: z.number().int().min(1).max(100) }).readonly()) }], strict('SessionCandidate[]', z.array(sessionCandidateSchema)), true),
|
|
77
71
|
descriptor('search', [{ name: 'input', wire: 'input', source: 'json', codec: strict('SearchInput', searchInputSchema) }], strict('SearchResult[]', z.array(searchResultSchema)), true),
|
|
78
72
|
descriptor('health', [], strict('Health', healthSchema), true),
|
|
73
|
+
descriptor('quickHealth', [], strict('Health', healthSchema), true),
|
|
79
74
|
descriptor('profiles', [], strict('BrowserProfile[]', z.array(browserProfileSchema)), true),
|
|
80
75
|
descriptor('discoverOpenCli', [], strict('OpenCliDiscovery', openCliDiscoverySchema), true),
|
|
81
76
|
descriptor('installOpenCli', [], strict('OpenCliDiscovery', openCliDiscoverySchema), true),
|
package/lib/host.d.ts
CHANGED
|
@@ -2,25 +2,15 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
2
2
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
3
3
|
import type { ChatProvider, SettingsRecord } from './store/spec.ts';
|
|
4
4
|
import type { SyncMode } from './sync/index.ts';
|
|
5
|
-
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
6
5
|
export declare class ReferenceAnythingRemote extends TypertRemoteService {
|
|
7
6
|
constructor(ctx: Context);
|
|
8
|
-
workspaceSearch(agent: Agent, signal: AbortSignal): Promise<import("./workspace.ts").WorkspaceEntry[]>;
|
|
9
|
-
sessionSearch(agent: Agent, input: {
|
|
10
|
-
query: string;
|
|
11
|
-
limit: number;
|
|
12
|
-
}, signal: AbortSignal): Promise<{
|
|
13
|
-
sessionId: string;
|
|
14
|
-
label: string;
|
|
15
|
-
cwd?: string;
|
|
16
|
-
createdAt: number;
|
|
17
|
-
}[]>;
|
|
18
7
|
search(input: {
|
|
19
8
|
query: string;
|
|
20
9
|
provider?: ChatProvider;
|
|
21
10
|
limit: number;
|
|
22
11
|
}, signal: AbortSignal): Promise<import("./sources/web-chat/index.ts").ConversationSearchResult[]>;
|
|
23
12
|
health(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliHealth>;
|
|
13
|
+
quickHealth(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliHealth>;
|
|
24
14
|
profiles(signal: AbortSignal): Promise<import("./opencli.ts").BrowserProfile[]>;
|
|
25
15
|
discoverOpenCli(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliDiscovery>;
|
|
26
16
|
installOpenCli(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliDiscovery>;
|
|
@@ -53,30 +43,36 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
|
|
|
53
43
|
maxReadTurns: number;
|
|
54
44
|
inputRenderMode: "pill" | "raw-text";
|
|
55
45
|
picker?: {
|
|
46
|
+
displayMode: "collapse" | "native-scroll";
|
|
56
47
|
commands: {
|
|
57
48
|
enabled: boolean;
|
|
58
49
|
order: number;
|
|
59
50
|
limit: number;
|
|
51
|
+
maxCandidates: number;
|
|
60
52
|
};
|
|
61
53
|
skills: {
|
|
62
54
|
enabled: boolean;
|
|
63
55
|
order: number;
|
|
64
56
|
limit: number;
|
|
57
|
+
maxCandidates: number;
|
|
65
58
|
};
|
|
66
59
|
files: {
|
|
67
60
|
enabled: boolean;
|
|
68
61
|
order: number;
|
|
69
62
|
limit: number;
|
|
63
|
+
maxCandidates: number;
|
|
70
64
|
};
|
|
71
65
|
sessions: {
|
|
72
66
|
enabled: boolean;
|
|
73
67
|
order: number;
|
|
74
68
|
limit: number;
|
|
69
|
+
maxCandidates: number;
|
|
75
70
|
};
|
|
76
71
|
conversations: {
|
|
77
72
|
enabled: boolean;
|
|
78
73
|
order: number;
|
|
79
74
|
limit: number;
|
|
75
|
+
maxCandidates: number;
|
|
80
76
|
};
|
|
81
77
|
} | undefined;
|
|
82
78
|
}>;
|
package/lib/host.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
2
|
-
import { encodeSessionReferenceUri } from '@deepseek-ai/dsh-session-reference';
|
|
3
|
-
import { indexWorkspace } from "./workspace.js";
|
|
4
2
|
export class ReferenceAnythingRemote extends TypertRemoteService {
|
|
5
3
|
constructor(ctx) { super(ctx, 'referenceAnything'); }
|
|
6
|
-
workspaceSearch(agent, signal) { return indexWorkspace(agent, signal); }
|
|
7
|
-
async sessionSearch(agent, input, signal) {
|
|
8
|
-
const rows = await this.ctx.sessionReferenceResolver.listCandidates(agent, input.query, input.limit, signal);
|
|
9
|
-
return rows.map(row => ({ ...row, sessionId: encodeSessionReferenceUri(row.sessionId) }));
|
|
10
|
-
}
|
|
11
4
|
search(input, signal) {
|
|
12
5
|
signal.throwIfAborted();
|
|
13
6
|
return this.ctx.referenceChatHistory.search(input.query, input.provider, input.limit, signal);
|
|
14
7
|
}
|
|
15
8
|
health(signal) { return this.ctx.referenceChatHistory.health(signal); }
|
|
9
|
+
quickHealth(signal) { return this.ctx.referenceChatHistory.quickHealth(signal); }
|
|
16
10
|
profiles(signal) { return this.ctx.referenceChatHistory.profiles(signal); }
|
|
17
11
|
discoverOpenCli(signal) { return this.ctx.referenceChatHistory.discoverOpenCli(signal); }
|
|
18
12
|
installOpenCli(signal) { return this.ctx.referenceChatHistory.installOpenCli(signal); }
|
package/lib/opencli.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChatProvider } from './store/spec.ts';
|
|
2
2
|
import type { ProviderConversationRow, ProviderTurnRow } from './store/store.ts';
|
|
3
3
|
export declare const MIN_OPENCLI_VERSION = "1.8.6";
|
|
4
|
-
export declare const MIN_ADAPTER_VERSION = "0.2.
|
|
4
|
+
export declare const MIN_ADAPTER_VERSION = "0.2.2";
|
|
5
5
|
export declare const OPENCLI_NPM_PACKAGE = "@jackwener/opencli";
|
|
6
6
|
export type OpenCliErrorCode = 'EXTENSION_NOT_CONNECTED' | 'PROVIDER_TIMEOUT' | 'PROVIDER_NOT_LOGGED_IN' | 'PROVIDER_ACCOUNT_MISMATCH' | 'OPENCLI_CONFIGURATION' | 'OPENCLI_OUTPUT_TOO_LARGE' | 'OPENCLI_FAILED' | 'OPENCLI_INSTALL_FAILED';
|
|
7
7
|
export declare class OpenCliError extends Error {
|
|
@@ -32,6 +32,7 @@ export interface OpenCliHealth {
|
|
|
32
32
|
daemonVersion?: string;
|
|
33
33
|
daemonStale: boolean;
|
|
34
34
|
connectivityOk: boolean;
|
|
35
|
+
connectivityChecked: boolean;
|
|
35
36
|
pluginVersion?: string;
|
|
36
37
|
adapterCommandsReady: boolean;
|
|
37
38
|
adapterCompatible: boolean;
|
|
@@ -96,6 +97,9 @@ export declare class OpenCliRunner {
|
|
|
96
97
|
detail(provider: ChatProvider, id: string, signal?: AbortSignal, accountScope?: string): Promise<ProviderTurnRow[]>;
|
|
97
98
|
attachment(provider: ChatProvider, locator: string, output: string, maxBytes: number, signal?: AbortSignal): Promise<Record<string, unknown>>;
|
|
98
99
|
health(signal?: AbortSignal): Promise<OpenCliHealth>;
|
|
100
|
+
/** Inspect local OpenCLI, daemon, extension and plugin state without an active connectivity probe. */
|
|
101
|
+
quickHealth(signal?: AbortSignal): Promise<OpenCliHealth>;
|
|
102
|
+
private inspectHealth;
|
|
99
103
|
/** Lightweight executable validation used by automatic path discovery. */
|
|
100
104
|
version(signal?: AbortSignal): Promise<string>;
|
|
101
105
|
/** Confirm the executable also exposes OpenCLI's plugin command, not merely a semver-looking --version. */
|
package/lib/opencli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { promisify } from 'node:util';
|
|
|
5
5
|
import { createHash } from 'node:crypto';
|
|
6
6
|
const execFile = promisify(nodeExecFile);
|
|
7
7
|
export const MIN_OPENCLI_VERSION = '1.8.6';
|
|
8
|
-
export const MIN_ADAPTER_VERSION = '0.2.
|
|
8
|
+
export const MIN_ADAPTER_VERSION = '0.2.2';
|
|
9
9
|
export const OPENCLI_NPM_PACKAGE = '@jackwener/opencli';
|
|
10
10
|
const SITE = {
|
|
11
11
|
chatgpt: 'dsh-chatgpt', claude: 'dsh-claude', gemini: 'dsh-gemini',
|
|
@@ -13,13 +13,6 @@ const SITE = {
|
|
|
13
13
|
};
|
|
14
14
|
const REQUIRED_ADAPTER_COMMANDS = ['chatgpt', 'claude', 'gemini', 'deepseek', 'grok', 'kimi'];
|
|
15
15
|
const ADAPTER_PLUGIN_NAME = 'dsh-chat-history';
|
|
16
|
-
/**
|
|
17
|
-
* A persistent OpenCLI site session owns one mutable browser lease. Serialize
|
|
18
|
-
* commands that target the same executable/profile/site across runner
|
|
19
|
-
* instances so concurrent detail workers cannot navigate or close each
|
|
20
|
-
* other's active tab. Different provider sites still run in parallel.
|
|
21
|
-
*/
|
|
22
|
-
const SITE_COMMAND_TAILS = new Map();
|
|
23
16
|
export class OpenCliError extends Error {
|
|
24
17
|
code;
|
|
25
18
|
constructor(message, code, options) {
|
|
@@ -119,9 +112,16 @@ export class OpenCliRunner {
|
|
|
119
112
|
return rows[0] ?? {};
|
|
120
113
|
}
|
|
121
114
|
async health(signal) {
|
|
115
|
+
return this.inspectHealth(true, signal);
|
|
116
|
+
}
|
|
117
|
+
/** Inspect local OpenCLI, daemon, extension and plugin state without an active connectivity probe. */
|
|
118
|
+
async quickHealth(signal) {
|
|
119
|
+
return this.inspectHealth(false, signal);
|
|
120
|
+
}
|
|
121
|
+
async inspectHealth(checkConnectivity, signal) {
|
|
122
122
|
const [version, daemon, plugins, doctor] = await Promise.all([
|
|
123
123
|
this.probe(['--version'], signal), this.probe(['daemon', 'status'], signal), this.probe(['plugin', 'list'], signal),
|
|
124
|
-
this.probe(['doctor'], signal),
|
|
124
|
+
checkConnectivity ? this.probe(['doctor'], signal) : Promise.resolve({ value: '', diagnostic: '' }),
|
|
125
125
|
]);
|
|
126
126
|
const status = parseDaemonStatus(daemon.value);
|
|
127
127
|
const cliVersion = normalizeVersion(version.value);
|
|
@@ -137,7 +137,7 @@ export class OpenCliRunner {
|
|
|
137
137
|
return {
|
|
138
138
|
version: cliVersion, daemon: daemon.value.trim(), pluginInstalled,
|
|
139
139
|
daemonRunning: status.daemonRunning, extensionConnected: status.extensionConnected, extensionState: status.extensionState,
|
|
140
|
-
opencliCompatible, daemonStale, connectivityOk,
|
|
140
|
+
opencliCompatible, daemonStale, connectivityOk, connectivityChecked: checkConnectivity,
|
|
141
141
|
...(status.extensionVersion ? { extensionVersion: status.extensionVersion } : {}),
|
|
142
142
|
...(status.profileCount !== undefined ? { profileCount: status.profileCount } : {}),
|
|
143
143
|
...(daemonVersion ? { daemonVersion: normalizeVersion(daemonVersion) } : {}),
|
|
@@ -147,7 +147,7 @@ export class OpenCliRunner {
|
|
|
147
147
|
...(version.error ? { versionError: version.error } : {}),
|
|
148
148
|
...(daemon.error ? { daemonError: daemon.error } : {}),
|
|
149
149
|
...(plugins.error || adapterLoadError ? { pluginError: plugins.error || adapterLoadError } : {}),
|
|
150
|
-
...(doctor.error ? { doctorError: doctor.error } : {}),
|
|
150
|
+
...('error' in doctor && doctor.error ? { doctorError: doctor.error } : {}),
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
/** Lightweight executable validation used by automatic path discovery. */
|
|
@@ -208,25 +208,21 @@ export class OpenCliRunner {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
async json(site, operation, args, signal) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
catch (error) {
|
|
227
|
-
throw new OpenCliError('OpenCLI returned malformed JSON', 'OPENCLI_CONFIGURATION', { cause: error });
|
|
228
|
-
}
|
|
229
|
-
});
|
|
211
|
+
// Match the sync-index lifecycle: every command gets its own temporary
|
|
212
|
+
// background tab, so providers and detail workers can run independently.
|
|
213
|
+
const stdout = await this.raw([
|
|
214
|
+
site, operation, ...args,
|
|
215
|
+
'--site-session', 'ephemeral', '--window', 'background', '-f', 'json',
|
|
216
|
+
], signal);
|
|
217
|
+
try {
|
|
218
|
+
const parsed = JSON.parse(stdout);
|
|
219
|
+
if (!Array.isArray(parsed) || parsed.some(row => !row || typeof row !== 'object' || Array.isArray(row)))
|
|
220
|
+
throw new Error('expected object array');
|
|
221
|
+
return parsed;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
throw new OpenCliError('OpenCLI returned malformed JSON', 'OPENCLI_CONFIGURATION', { cause: error });
|
|
225
|
+
}
|
|
230
226
|
}
|
|
231
227
|
async raw(args, signal) {
|
|
232
228
|
return (await this.command(args, signal)).stdout;
|
|
@@ -263,19 +259,6 @@ function adapterPluginLoadError(diagnostic = '') {
|
|
|
263
259
|
.filter(line => /Plugin dsh-chat-history\//i.test(line));
|
|
264
260
|
return [...new Set(lines)].join('\n').slice(0, 2_000);
|
|
265
261
|
}
|
|
266
|
-
async function serializeSiteCommand(key, task) {
|
|
267
|
-
const previous = SITE_COMMAND_TAILS.get(key) ?? Promise.resolve();
|
|
268
|
-
const current = previous.then(task, task);
|
|
269
|
-
const settled = current.then(() => undefined, () => undefined);
|
|
270
|
-
SITE_COMMAND_TAILS.set(key, settled);
|
|
271
|
-
try {
|
|
272
|
-
return await current;
|
|
273
|
-
}
|
|
274
|
-
finally {
|
|
275
|
-
if (SITE_COMMAND_TAILS.get(key) === settled)
|
|
276
|
-
SITE_COMMAND_TAILS.delete(key);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
262
|
/** Locate a working OpenCLI without asking a command-line newcomer for PATH details. */
|
|
280
263
|
export async function discoverOpenCli(configured = 'opencli', signal) {
|
|
281
264
|
const candidates = openCliCandidates(configured);
|
package/lib/reference.d.ts
CHANGED
|
@@ -8,11 +8,9 @@
|
|
|
8
8
|
* the loop durably appends, so injected material is logged by construction and
|
|
9
9
|
* a replay reconstructs the request exactly.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* is resolved through `ctx.references`.
|
|
13
|
-
*
|
|
14
|
-
* session-surface semantics — compaction folding, shadowed events, cited event
|
|
15
|
-
* seqs — that do not generalize to an outside conversation.
|
|
11
|
+
* This plugin owns only `dsh-ref:`, which names material outside the harness
|
|
12
|
+
* and is resolved through `ctx.references`. Native file and DSH-session
|
|
13
|
+
* references are owned end-to-end by their official DSH services.
|
|
16
14
|
*
|
|
17
15
|
* @module dsh-reference-anything/reference
|
|
18
16
|
*/
|
|
@@ -32,12 +30,6 @@ export interface Config {
|
|
|
32
30
|
* background, and the byte budget multiplies by the same factor.
|
|
33
31
|
*/
|
|
34
32
|
maxReferences?: number;
|
|
35
|
-
/**
|
|
36
|
-
* Also expand `dsh-session:` mentions. Deployments that deliberately do not
|
|
37
|
-
* mount the cross-session resolver set this false, so such a mention is
|
|
38
|
-
* refused as unsupported rather than probed for on every message.
|
|
39
|
-
*/
|
|
40
|
-
serveSessionScheme?: boolean;
|
|
41
33
|
}
|
|
42
34
|
/** Runtime schema for {@link Config}. */
|
|
43
35
|
export declare const Config: z<Config>;
|
package/lib/reference.js
CHANGED
|
@@ -8,21 +8,17 @@
|
|
|
8
8
|
* the loop durably appends, so injected material is logged by construction and
|
|
9
9
|
* a replay reconstructs the request exactly.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* is resolved through `ctx.references`.
|
|
13
|
-
*
|
|
14
|
-
* session-surface semantics — compaction folding, shadowed events, cited event
|
|
15
|
-
* seqs — that do not generalize to an outside conversation.
|
|
11
|
+
* This plugin owns only `dsh-ref:`, which names material outside the harness
|
|
12
|
+
* and is resolved through `ctx.references`. Native file and DSH-session
|
|
13
|
+
* references are owned end-to-end by their official DSH services.
|
|
16
14
|
*
|
|
17
15
|
* @module dsh-reference-anything/reference
|
|
18
16
|
*/
|
|
19
17
|
import z from '@deepseek-ai/schemastery';
|
|
20
18
|
import { boundContextSummary, createUserMessage, freezeMessage } from '@deepseek-ai/dsh-llm';
|
|
21
|
-
import { SESSION_REFERENCE_SCHEME, parseSessionReferenceText, } from '@deepseek-ai/dsh-session-reference';
|
|
22
19
|
import { ReferenceAnythingError } from "./errors.js";
|
|
23
20
|
import { renderDeferredReferences } from "./render.js";
|
|
24
21
|
import { mayContainReference, parseReferenceText } from "./uri.js";
|
|
25
|
-
import { WORKSPACE_REFERENCE_SCHEME, parseWorkspaceReferenceText, renderWorkspaceReferences } from "./workspace.js";
|
|
26
22
|
/** Cordis plugin name used by loader diagnostics. */
|
|
27
23
|
export const name = 'reference-mention';
|
|
28
24
|
/** The registry this expander reads through. */
|
|
@@ -32,7 +28,6 @@ export const MAX_REFERENCES = 3;
|
|
|
32
28
|
/** Runtime schema for {@link Config}. */
|
|
33
29
|
export const Config = z.object({
|
|
34
30
|
maxReferences: z.number().step(1).min(1).max(MAX_REFERENCES).default(MAX_REFERENCES),
|
|
35
|
-
serveSessionScheme: z.boolean().default(true),
|
|
36
31
|
});
|
|
37
32
|
/**
|
|
38
33
|
* Register the pre-step mention expander.
|
|
@@ -41,7 +36,6 @@ export const Config = z.object({
|
|
|
41
36
|
*/
|
|
42
37
|
export function apply(ctx, config = {}) {
|
|
43
38
|
const maxReferences = Math.min(config.maxReferences ?? MAX_REFERENCES, MAX_REFERENCES);
|
|
44
|
-
const serveSessionScheme = config.serveSessionScheme ?? true;
|
|
45
39
|
ctx.on('agent/pre-step', async ({ agent, signal }, next) => {
|
|
46
40
|
const decision = await next();
|
|
47
41
|
if (decision.kind === 'reject' || signal.aborted)
|
|
@@ -49,7 +43,7 @@ export function apply(ctx, config = {}) {
|
|
|
49
43
|
// The overwhelmingly common case is a prompt with no mentions at all, and
|
|
50
44
|
// this runs on every step of every turn. Screening with substring scans
|
|
51
45
|
// before any parsing keeps that case free.
|
|
52
|
-
const target = decision.messages.findIndex(candidate => candidate.source.kind === 'user' && messageText(candidate).some(
|
|
46
|
+
const target = decision.messages.findIndex(candidate => candidate.source.kind === 'user' && messageText(candidate).some(mayContainReference));
|
|
53
47
|
if (target < 0)
|
|
54
48
|
return decision;
|
|
55
49
|
const original = decision.messages[target];
|
|
@@ -57,7 +51,7 @@ export function apply(ctx, config = {}) {
|
|
|
57
51
|
return decision;
|
|
58
52
|
let parsed;
|
|
59
53
|
try {
|
|
60
|
-
parsed = parseMessage(original
|
|
54
|
+
parsed = parseMessage(original);
|
|
61
55
|
}
|
|
62
56
|
catch (error) {
|
|
63
57
|
// A malformed URI inside an explicit mention is the user asking for
|
|
@@ -65,11 +59,11 @@ export function apply(ctx, config = {}) {
|
|
|
65
59
|
// reference silently resolves to nothing.
|
|
66
60
|
return entered(decision, target, [notice(describe(error))], original);
|
|
67
61
|
}
|
|
68
|
-
if (parsed.references.length === 0
|
|
62
|
+
if (parsed.references.length === 0)
|
|
69
63
|
return decision;
|
|
70
64
|
let contexts;
|
|
71
65
|
try {
|
|
72
|
-
contexts = await resolve(ctx, agent, parsed,
|
|
66
|
+
contexts = await resolve(ctx, agent, parsed, maxReferences, signal);
|
|
73
67
|
}
|
|
74
68
|
catch (error) {
|
|
75
69
|
if (signal.aborted)
|
|
@@ -85,28 +79,13 @@ export function apply(ctx, config = {}) {
|
|
|
85
79
|
* Authorize and render the named references without reading their bodies.
|
|
86
80
|
* The agent decides whether the current request warrants `reference_read`.
|
|
87
81
|
*/
|
|
88
|
-
async function resolve(ctx, agent, parsed,
|
|
82
|
+
async function resolve(ctx, agent, parsed, maxReferences, signal) {
|
|
89
83
|
const contexts = [];
|
|
90
|
-
if (parsed.workspaceReferences.length > 0) {
|
|
91
|
-
contexts.push(createUserMessage({
|
|
92
|
-
content: [{ type: 'text', text: await renderWorkspaceReferences(agent, parsed.workspaceReferences) }],
|
|
93
|
-
source: { kind: 'plugin', plugin: name, form: 'recall' },
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
if (parsed.sessionReferences.length > 0) {
|
|
97
|
-
const resolver = ctx.get('sessionReferenceResolver');
|
|
98
|
-
if (resolver === undefined) {
|
|
99
|
-
throw new ReferenceAnythingError('this deployment cannot reference other harness sessions: the cross-session resolver is not mounted', 'SOURCE_UNAVAILABLE');
|
|
100
|
-
}
|
|
101
|
-
const prepared = await resolver.prepare(agent, contentOf(parsed.message), [...parsed.sessionReferences], signal);
|
|
102
|
-
if (prepared.additionalContext !== undefined)
|
|
103
|
-
contexts.push(prepared.additionalContext);
|
|
104
|
-
}
|
|
105
84
|
if (parsed.references.length > 0) {
|
|
106
85
|
const inputs = dedupe(parsed.references);
|
|
107
|
-
if (inputs.length >
|
|
108
|
-
throw new ReferenceAnythingError(`a message may reference at most ${
|
|
109
|
-
+ `${
|
|
86
|
+
if (inputs.length > maxReferences) {
|
|
87
|
+
throw new ReferenceAnythingError(`a message may reference at most ${maxReferences} outside conversation`
|
|
88
|
+
+ `${maxReferences === 1 ? '' : 's'}; this one named ${inputs.length}`, 'REFERENCE_TOO_MANY');
|
|
110
89
|
}
|
|
111
90
|
for (const input of inputs)
|
|
112
91
|
ctx.references.grant(String(agent.session.id), input.ref);
|
|
@@ -134,28 +113,16 @@ function entered(decision, target, contexts, message) {
|
|
|
134
113
|
const replaced = decision.messages.with(target, message);
|
|
135
114
|
return { kind: 'enter', messages: replaced.toSpliced(target, 0, ...contexts) };
|
|
136
115
|
}
|
|
137
|
-
/** Rewrite
|
|
138
|
-
function parseMessage(message
|
|
116
|
+
/** Rewrite external-conversation mentions to readable text, preserving message identity. */
|
|
117
|
+
function parseMessage(message) {
|
|
139
118
|
const references = [];
|
|
140
|
-
const sessionReferences = [];
|
|
141
|
-
const workspaceReferences = [];
|
|
142
119
|
let changed = false;
|
|
143
120
|
const content = message.content.map((block) => {
|
|
144
121
|
if (block.type !== 'text')
|
|
145
122
|
return block;
|
|
146
123
|
const own = parseReferenceText(block.text);
|
|
147
124
|
references.push(...own.references);
|
|
148
|
-
|
|
149
|
-
if (text.includes(WORKSPACE_REFERENCE_SCHEME)) {
|
|
150
|
-
const workspace = parseWorkspaceReferenceText(text);
|
|
151
|
-
workspaceReferences.push(...workspace.references);
|
|
152
|
-
text = workspace.text;
|
|
153
|
-
}
|
|
154
|
-
if (serveSessionScheme && text.includes(SESSION_REFERENCE_SCHEME)) {
|
|
155
|
-
const session = parseSessionReferenceText(text);
|
|
156
|
-
sessionReferences.push(...session.references);
|
|
157
|
-
text = session.text;
|
|
158
|
-
}
|
|
125
|
+
const text = own.text;
|
|
159
126
|
if (text === block.text)
|
|
160
127
|
return block;
|
|
161
128
|
changed = true;
|
|
@@ -166,8 +133,6 @@ function parseMessage(message, serveSessionScheme) {
|
|
|
166
133
|
return {
|
|
167
134
|
message: changed ? freezeMessage({ ...message, content }) : message,
|
|
168
135
|
references,
|
|
169
|
-
sessionReferences,
|
|
170
|
-
workspaceReferences,
|
|
171
136
|
};
|
|
172
137
|
}
|
|
173
138
|
/** First-appearance order, one entry per distinct reference. */
|
|
@@ -183,15 +148,9 @@ function dedupe(references) {
|
|
|
183
148
|
}
|
|
184
149
|
return unique;
|
|
185
150
|
}
|
|
186
|
-
function mentions(text, serveSessionScheme) {
|
|
187
|
-
return mayContainReference(text) || text.includes(WORKSPACE_REFERENCE_SCHEME) || (serveSessionScheme && text.includes(SESSION_REFERENCE_SCHEME));
|
|
188
|
-
}
|
|
189
151
|
function messageText(message) {
|
|
190
152
|
return message.content.flatMap(block => block.type === 'text' ? [block.text] : []);
|
|
191
153
|
}
|
|
192
|
-
function contentOf(message) {
|
|
193
|
-
return message.content.map(block => ({ ...block }));
|
|
194
|
-
}
|
|
195
154
|
function notice(summary) {
|
|
196
155
|
return createUserMessage({
|
|
197
156
|
content: [{
|
|
@@ -94,6 +94,7 @@ export default class WebChatHistoryService extends Service implements ReferenceS
|
|
|
94
94
|
installOpenCli(signal?: AbortSignal): Promise<OpenCliDiscovery>;
|
|
95
95
|
updateSettings(value: unknown): Promise<SettingsRecord>;
|
|
96
96
|
health(signal?: AbortSignal): Promise<import("../../opencli.ts").OpenCliHealth>;
|
|
97
|
+
quickHealth(signal?: AbortSignal): Promise<import("../../opencli.ts").OpenCliHealth>;
|
|
97
98
|
profiles(signal?: AbortSignal): Promise<import("../../opencli.ts").BrowserProfile[]>;
|
|
98
99
|
private runner;
|
|
99
100
|
installAdapter(signal?: AbortSignal): Promise<boolean>;
|
|
@@ -229,6 +229,11 @@ export default class WebChatHistoryService extends Service {
|
|
|
229
229
|
return new OpenCliRunner({ executable: settings.opencliPath, profile: settings.profile,
|
|
230
230
|
timeoutMs: this.config.timeoutMs, maxStdoutBytes: this.config.maxStdoutBytes }).health(signal);
|
|
231
231
|
}
|
|
232
|
+
quickHealth(signal) {
|
|
233
|
+
const settings = this.store.settings;
|
|
234
|
+
return new OpenCliRunner({ executable: settings.opencliPath, profile: settings.profile,
|
|
235
|
+
timeoutMs: this.config.timeoutMs, maxStdoutBytes: this.config.maxStdoutBytes }).quickHealth(signal);
|
|
236
|
+
}
|
|
232
237
|
profiles(signal) {
|
|
233
238
|
const settings = this.store.settings;
|
|
234
239
|
return new OpenCliRunner({ executable: settings.opencliPath, profile: '',
|
package/lib/store/spec.d.ts
CHANGED
|
@@ -193,30 +193,39 @@ export declare const referenceAnythingDomainSpec: {
|
|
|
193
193
|
"raw-text": "raw-text";
|
|
194
194
|
}>>;
|
|
195
195
|
picker: z.ZodOptional<z.ZodObject<{
|
|
196
|
+
displayMode: z.ZodDefault<z.ZodEnum<{
|
|
197
|
+
collapse: "collapse";
|
|
198
|
+
"native-scroll": "native-scroll";
|
|
199
|
+
}>>;
|
|
196
200
|
commands: z.ZodObject<{
|
|
197
201
|
enabled: z.ZodBoolean;
|
|
198
202
|
order: z.ZodNumber;
|
|
199
203
|
limit: z.ZodNumber;
|
|
204
|
+
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
200
205
|
}, z.core.$strip>;
|
|
201
206
|
skills: z.ZodObject<{
|
|
202
207
|
enabled: z.ZodBoolean;
|
|
203
208
|
order: z.ZodNumber;
|
|
204
209
|
limit: z.ZodNumber;
|
|
210
|
+
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
205
211
|
}, z.core.$strip>;
|
|
206
212
|
files: z.ZodObject<{
|
|
207
213
|
enabled: z.ZodBoolean;
|
|
208
214
|
order: z.ZodNumber;
|
|
209
215
|
limit: z.ZodNumber;
|
|
216
|
+
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
210
217
|
}, z.core.$strip>;
|
|
211
218
|
sessions: z.ZodObject<{
|
|
212
219
|
enabled: z.ZodBoolean;
|
|
213
220
|
order: z.ZodNumber;
|
|
214
221
|
limit: z.ZodNumber;
|
|
222
|
+
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
215
223
|
}, z.core.$strip>;
|
|
216
224
|
conversations: z.ZodObject<{
|
|
217
225
|
enabled: z.ZodBoolean;
|
|
218
226
|
order: z.ZodNumber;
|
|
219
227
|
limit: z.ZodNumber;
|
|
228
|
+
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
220
229
|
}, z.core.$strip>;
|
|
221
230
|
}, z.core.$strip>>;
|
|
222
231
|
}, z.core.$strip>;
|
package/lib/sync/index.js
CHANGED
|
@@ -109,9 +109,9 @@ export class ConversationSyncManager {
|
|
|
109
109
|
const failures = new Map();
|
|
110
110
|
let succeeded = 0;
|
|
111
111
|
try {
|
|
112
|
-
|
|
112
|
+
for (const provider of job.providers) {
|
|
113
113
|
if (job.controller.signal.aborted)
|
|
114
|
-
|
|
114
|
+
break;
|
|
115
115
|
const started = Date.now();
|
|
116
116
|
try {
|
|
117
117
|
await this.syncProvider(job, provider, mode, options);
|
|
@@ -123,11 +123,11 @@ export class ConversationSyncManager {
|
|
|
123
123
|
// logged-out account would otherwise permanently starve every
|
|
124
124
|
// provider after it in this list.
|
|
125
125
|
if (job.controller.signal.aborted)
|
|
126
|
-
|
|
126
|
+
break;
|
|
127
127
|
failures.set(provider, describe(error));
|
|
128
128
|
this.logger?.warn(`reference sync: ${provider} failed: ${describe(error)}`);
|
|
129
129
|
}
|
|
130
|
-
}
|
|
130
|
+
}
|
|
131
131
|
job.status = job.controller.signal.aborted ? 'cancelled'
|
|
132
132
|
: failures.size === 0 ? 'complete'
|
|
133
133
|
: succeeded === 0 ? 'failed' : 'partial';
|
package/lib/typert.js
CHANGED
|
@@ -5,10 +5,9 @@ export const TYPERT_MANIFEST = {
|
|
|
5
5
|
key: 'referenceAnything', exportName: 'ReferenceAnythingRemote',
|
|
6
6
|
description: 'Local conversation mirror, synchronization, and settings for @Conversation.', tags: [],
|
|
7
7
|
members: [
|
|
8
|
-
{ kind: 'method', name: 'workspaceSearch', signature: 'workspaceSearch(agent: Agent, signal: AbortSignal): Promise<WorkspaceEntry[]>' },
|
|
9
|
-
{ kind: 'method', name: 'sessionSearch', signature: 'sessionSearch(agent: Agent, input: SessionSearchInput, signal: AbortSignal): Promise<SessionCandidate[]>' },
|
|
10
8
|
{ kind: 'method', name: 'search', signature: 'search(input: SearchInput): Promise<SearchResult[]>' },
|
|
11
9
|
{ kind: 'method', name: 'health', signature: 'health(signal: AbortSignal): Promise<Health>' },
|
|
10
|
+
{ kind: 'method', name: 'quickHealth', signature: 'quickHealth(signal: AbortSignal): Promise<Health>' },
|
|
12
11
|
{ kind: 'method', name: 'profiles', signature: 'profiles(signal: AbortSignal): Promise<BrowserProfile[]>' },
|
|
13
12
|
{ kind: 'method', name: 'discoverOpenCli', signature: 'discoverOpenCli(signal: AbortSignal): Promise<OpenCliDiscovery>' },
|
|
14
13
|
{ kind: 'method', name: 'installOpenCli', signature: 'installOpenCli(signal: AbortSignal): Promise<OpenCliDiscovery>' },
|
package/lib/web.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReferenceAnythingRemote } from "./host.js";
|
|
2
2
|
import { TYPERT_MANIFEST } from "./typert.js";
|
|
3
3
|
export const name = 'reference-anything-web';
|
|
4
|
-
export const inject = ['typert', 'referenceChatHistory'
|
|
4
|
+
export const inject = ['typert', 'referenceChatHistory'];
|
|
5
5
|
export function apply(ctx) {
|
|
6
6
|
new ReferenceAnythingRemote(ctx);
|
|
7
7
|
ctx.effect(() => {
|