dsh-reference-anything 0.2.5 → 0.3.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/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;
@@ -110,6 +98,19 @@ export declare const packageUpdateResultSchema: z.ZodReadonly<z.ZodObject<{
110
98
  version: z.ZodString;
111
99
  restartRequired: z.ZodBoolean;
112
100
  }, z.core.$strip>>;
101
+ export declare const referenceUiModeInputSchema: z.ZodReadonly<z.ZodObject<{
102
+ mode: z.ZodEnum<{
103
+ plugin: "plugin";
104
+ official: "official";
105
+ }>;
106
+ }, z.core.$strip>>;
107
+ export declare const referenceUiSwitchResultSchema: z.ZodReadonly<z.ZodObject<{
108
+ mode: z.ZodEnum<{
109
+ plugin: "plugin";
110
+ official: "official";
111
+ }>;
112
+ restartRequired: z.ZodLiteral<false>;
113
+ }, z.core.$strip>>;
113
114
  export declare const providerStatsSchema: z.ZodReadonly<z.ZodObject<{
114
115
  provider: z.ZodEnum<{
115
116
  chatgpt: "chatgpt";
package/lib/contract.js CHANGED
@@ -1,9 +1,5 @@
1
1
  import { z } from 'zod';
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) } };
2
+ import { providerSchema, referenceUiModeSchema, settingsRecordSchema } from "./wire.js";
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();
@@ -33,6 +29,8 @@ export const packageUpdateStatusSchema = z.object({
33
29
  export const packageUpdateResultSchema = z.object({
34
30
  version: z.string(), restartRequired: z.boolean(),
35
31
  }).readonly();
32
+ export const referenceUiModeInputSchema = z.object({ mode: referenceUiModeSchema }).readonly();
33
+ export const referenceUiSwitchResultSchema = z.object({ mode: referenceUiModeSchema, restartRequired: z.literal(false) }).readonly();
36
34
  export const providerStatsSchema = z.object({
37
35
  provider: providerSchema, conversations: z.number().int().nonnegative(), lastSyncedAt: z.string(),
38
36
  status: z.enum(['ready', 'syncing', 'error', 'empty']), error: z.string().optional(),
@@ -72,10 +70,9 @@ export const providerSyncStateSchema = z.object({
72
70
  lastSyncAt: z.string(), lastCompleteScanAt: z.string(), error: z.string(),
73
71
  }).readonly();
74
72
  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
73
  descriptor('search', [{ name: 'input', wire: 'input', source: 'json', codec: strict('SearchInput', searchInputSchema) }], strict('SearchResult[]', z.array(searchResultSchema)), true),
78
74
  descriptor('health', [], strict('Health', healthSchema), true),
75
+ descriptor('quickHealth', [], strict('Health', healthSchema), true),
79
76
  descriptor('profiles', [], strict('BrowserProfile[]', z.array(browserProfileSchema)), true),
80
77
  descriptor('discoverOpenCli', [], strict('OpenCliDiscovery', openCliDiscoverySchema), true),
81
78
  descriptor('installOpenCli', [], strict('OpenCliDiscovery', openCliDiscoverySchema), true),
@@ -84,6 +81,7 @@ export const REFERENCE_ANYTHING_INVOCATIONS = [
84
81
  descriptor('updateStatus', [], strict('PackageUpdateStatus', packageUpdateStatusSchema), true),
85
82
  descriptor('checkUpdate', [], strict('PackageUpdateStatus', packageUpdateStatusSchema), true),
86
83
  descriptor('installUpdate', [], strict('PackageUpdateResult', packageUpdateResultSchema), true),
84
+ descriptor('switchReferenceUiMode', [{ name: 'input', wire: 'input', source: 'json', codec: strict('ReferenceUiModeInput', referenceUiModeInputSchema) }], strict('ReferenceUiSwitchResult', referenceUiSwitchResultSchema), true),
87
85
  descriptor('stats', [], strict('ProviderStats[]', z.array(providerStatsSchema))),
88
86
  descriptor('syncStart', [{ name: 'input', wire: 'input', source: 'json', codec: strict('SyncStart', syncStartSchema) }], strict('JobId', z.string())),
89
87
  descriptor('syncStatus', [{ name: 'input', wire: 'input', source: 'json', codec: strict('JobInput', jobInputSchema) }], strict('SyncStatus?', syncStatusSchema.optional())),
package/lib/host.d.ts CHANGED
@@ -1,26 +1,17 @@
1
1
  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
+ import type { ReferenceUiMode } from './wire.ts';
4
5
  import type { SyncMode } from './sync/index.ts';
5
- import type { Agent } from '@deepseek-ai/dsh-agent';
6
6
  export declare class ReferenceAnythingRemote extends TypertRemoteService {
7
7
  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
8
  search(input: {
19
9
  query: string;
20
10
  provider?: ChatProvider;
21
11
  limit: number;
22
12
  }, signal: AbortSignal): Promise<import("./sources/web-chat/index.ts").ConversationSearchResult[]>;
23
13
  health(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliHealth>;
14
+ quickHealth(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliHealth>;
24
15
  profiles(signal: AbortSignal): Promise<import("./opencli.ts").BrowserProfile[]>;
25
16
  discoverOpenCli(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliDiscovery>;
26
17
  installOpenCli(signal: AbortSignal): Promise<import("./opencli.ts").OpenCliDiscovery>;
@@ -29,6 +20,9 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
29
20
  updateStatus(signal: AbortSignal): Promise<import("./update.ts").PackageUpdateStatus>;
30
21
  checkUpdate(signal: AbortSignal): Promise<import("./update.ts").PackageUpdateStatus>;
31
22
  installUpdate(signal: AbortSignal): Promise<import("./update.ts").PackageUpdateResult>;
23
+ switchReferenceUiMode(input: {
24
+ mode: ReferenceUiMode;
25
+ }, signal: AbortSignal): Promise<import("./profile-mode.ts").ReferenceUiSwitchResult>;
32
26
  stats(): import("./store/store.ts").ProviderStats[];
33
27
  syncStart(input: {
34
28
  providers: ChatProvider[];
@@ -52,31 +46,38 @@ export declare class ReferenceAnythingRemote extends TypertRemoteService {
52
46
  enabledProviders: ("chatgpt" | "claude" | "gemini" | "deepseek" | "grok" | "kimi")[];
53
47
  maxReadTurns: number;
54
48
  inputRenderMode: "pill" | "raw-text";
49
+ referenceUiMode?: "plugin" | "official" | undefined;
55
50
  picker?: {
51
+ displayMode: "collapse" | "native-scroll";
56
52
  commands: {
57
53
  enabled: boolean;
58
54
  order: number;
59
55
  limit: number;
56
+ maxCandidates: number;
60
57
  };
61
58
  skills: {
62
59
  enabled: boolean;
63
60
  order: number;
64
61
  limit: number;
62
+ maxCandidates: number;
65
63
  };
66
64
  files: {
67
65
  enabled: boolean;
68
66
  order: number;
69
67
  limit: number;
68
+ maxCandidates: number;
70
69
  };
71
70
  sessions: {
72
71
  enabled: boolean;
73
72
  order: number;
74
73
  limit: number;
74
+ maxCandidates: number;
75
75
  };
76
76
  conversations: {
77
77
  enabled: boolean;
78
78
  order: number;
79
79
  limit: number;
80
+ maxCandidates: number;
80
81
  };
81
82
  } | undefined;
82
83
  }>;
package/lib/host.js CHANGED
@@ -1,18 +1,13 @@
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";
2
+ import { switchReferenceUiMode as switchProfileReferenceUiMode } from "./profile-mode.js";
4
3
  export class ReferenceAnythingRemote extends TypertRemoteService {
5
4
  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
5
  search(input, signal) {
12
6
  signal.throwIfAborted();
13
7
  return this.ctx.referenceChatHistory.search(input.query, input.provider, input.limit, signal);
14
8
  }
15
9
  health(signal) { return this.ctx.referenceChatHistory.health(signal); }
10
+ quickHealth(signal) { return this.ctx.referenceChatHistory.quickHealth(signal); }
16
11
  profiles(signal) { return this.ctx.referenceChatHistory.profiles(signal); }
17
12
  discoverOpenCli(signal) { return this.ctx.referenceChatHistory.discoverOpenCli(signal); }
18
13
  installOpenCli(signal) { return this.ctx.referenceChatHistory.installOpenCli(signal); }
@@ -21,6 +16,19 @@ export class ReferenceAnythingRemote extends TypertRemoteService {
21
16
  updateStatus(signal) { return this.ctx.referenceChatHistory.updateStatus(signal); }
22
17
  checkUpdate(signal) { return this.ctx.referenceChatHistory.checkUpdate(signal); }
23
18
  installUpdate(signal) { return this.ctx.referenceChatHistory.installUpdate(signal); }
19
+ async switchReferenceUiMode(input, signal) {
20
+ const settings = this.ctx.referenceChatHistory.getSettings();
21
+ const previousMode = settings.referenceUiMode ?? 'plugin';
22
+ const result = await switchProfileReferenceUiMode({ mode: input.mode, signal });
23
+ try {
24
+ await this.ctx.referenceChatHistory.updateSettings({ ...settings, referenceUiMode: input.mode });
25
+ }
26
+ catch (error) {
27
+ await switchProfileReferenceUiMode({ mode: previousMode }).catch(() => undefined);
28
+ throw error;
29
+ }
30
+ return result;
31
+ }
24
32
  stats() { return this.ctx.referenceChatHistory.stats(); }
25
33
  syncStart(input) {
26
34
  return this.ctx.referenceChatHistory.sync.start(input.providers, input.mode);
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.1";
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.1';
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
- const queueKey = [this.executable, ...this.prefixArgs, this.profile, site].join('\0');
212
- return serializeSiteCommand(queueKey, async () => {
213
- // Pin adapter traffic to one persistent background session. Persistence
214
- // suppresses OpenCLI's close-window action; the adapter closes only its
215
- // completed task tab and leaves the background container reusable.
216
- const stdout = await this.raw([
217
- site, operation, ...args,
218
- '--site-session', 'persistent', '--window', 'background', '-f', 'json',
219
- ], signal);
220
- try {
221
- const parsed = JSON.parse(stdout);
222
- if (!Array.isArray(parsed) || parsed.some(row => !row || typeof row !== 'object' || Array.isArray(row)))
223
- throw new Error('expected object array');
224
- return parsed;
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);
@@ -0,0 +1,30 @@
1
+ import type { ReferenceUiMode } from './wire.ts';
2
+ export declare const OFFICIAL_REFERENCE_PATCH_BEGIN = "# BEGIN dsh-reference-anything managed official-reference override";
3
+ export declare const OFFICIAL_REFERENCE_PATCH_END = "# END dsh-reference-anything managed official-reference override";
4
+ export interface ReferenceUiSwitchResult {
5
+ mode: ReferenceUiMode;
6
+ restartRequired: false;
7
+ }
8
+ interface SwitchReferenceUiModeOptions {
9
+ mode: ReferenceUiMode;
10
+ packageRoot?: string;
11
+ dshHome?: string;
12
+ signal?: AbortSignal;
13
+ }
14
+ /**
15
+ * Switch only the visible @ implementation while keeping this package alive.
16
+ *
17
+ * The bundle patch remains untouched so updating or reinstalling the package
18
+ * cannot erase the user's choice. The plugin Host and settings entry remain
19
+ * active in both modes; the client separately registers or disposes its own
20
+ * picker sources.
21
+ */
22
+ export declare function switchReferenceUiMode(options: SwitchReferenceUiModeOptions): Promise<ReferenceUiSwitchResult>;
23
+ /** Write the managed override without exposing a partially-written YAML file. */
24
+ export declare function writeReferenceUiModeOverride(path: string, mode: ReferenceUiMode, signal?: AbortSignal): Promise<void>;
25
+ /**
26
+ * Preserve every user-owned row and append one idempotent block at the end,
27
+ * where Cordis' ordered patch semantics make it the effective override.
28
+ */
29
+ export declare function withReferenceUiModeOverride(source: string, mode: ReferenceUiMode): string;
30
+ export {};
@@ -0,0 +1,90 @@
1
+ import { readFile, rename, rm, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { findOwningProfileDir } from "./update.js";
4
+ export const OFFICIAL_REFERENCE_PATCH_BEGIN = '# BEGIN dsh-reference-anything managed official-reference override';
5
+ export const OFFICIAL_REFERENCE_PATCH_END = '# END dsh-reference-anything managed official-reference override';
6
+ /**
7
+ * Switch only the visible @ implementation while keeping this package alive.
8
+ *
9
+ * The bundle patch remains untouched so updating or reinstalling the package
10
+ * cannot erase the user's choice. The plugin Host and settings entry remain
11
+ * active in both modes; the client separately registers or disposes its own
12
+ * picker sources.
13
+ */
14
+ export async function switchReferenceUiMode(options) {
15
+ options.signal?.throwIfAborted();
16
+ const profileDir = await findOwningProfileDir(options.packageRoot, options.dshHome);
17
+ await writeReferenceUiModeOverride(join(profileDir, 'cordis.patch.yml'), options.mode, options.signal);
18
+ return { mode: options.mode, restartRequired: false };
19
+ }
20
+ /** Write the managed override without exposing a partially-written YAML file. */
21
+ export async function writeReferenceUiModeOverride(path, mode, signal) {
22
+ signal?.throwIfAborted();
23
+ let source = '';
24
+ try {
25
+ source = await readFile(path, { encoding: 'utf8', signal });
26
+ }
27
+ catch (error) {
28
+ const code = error.code;
29
+ if (code !== 'ENOENT')
30
+ throw error;
31
+ }
32
+ const next = withReferenceUiModeOverride(source, mode);
33
+ if (next === source)
34
+ return;
35
+ const temporary = join(dirname(path), `.cordis.patch.yml.dsh-reference-anything-${String(process.pid)}-${String(Date.now())}.tmp`);
36
+ try {
37
+ await writeFile(temporary, next, { encoding: 'utf8', signal });
38
+ signal?.throwIfAborted();
39
+ await rename(temporary, path);
40
+ }
41
+ catch (error) {
42
+ await rm(temporary, { force: true }).catch(() => undefined);
43
+ throw error;
44
+ }
45
+ }
46
+ /**
47
+ * Preserve every user-owned row and append one idempotent block at the end,
48
+ * where Cordis' ordered patch semantics make it the effective override.
49
+ */
50
+ export function withReferenceUiModeOverride(source, mode) {
51
+ const eol = source.includes('\r\n') ? '\r\n' : '\n';
52
+ const lines = source.split(/\r?\n/);
53
+ const beginRows = markerRows(lines, OFFICIAL_REFERENCE_PATCH_BEGIN);
54
+ const endRows = markerRows(lines, OFFICIAL_REFERENCE_PATCH_END);
55
+ if (beginRows.length > 1 || endRows.length > 1 || beginRows.length !== endRows.length) {
56
+ throw new Error('the managed official-reference block in cordis.patch.yml is malformed');
57
+ }
58
+ if (beginRows.length === 1) {
59
+ const begin = beginRows[0];
60
+ const end = endRows[0];
61
+ if (end < begin)
62
+ throw new Error('the managed official-reference block in cordis.patch.yml is malformed');
63
+ lines.splice(begin, end - begin + 1);
64
+ }
65
+ removeEmptyArrayDocument(lines);
66
+ while (lines.length > 0 && lines.at(-1)?.trim() === '')
67
+ lines.pop();
68
+ const block = [
69
+ OFFICIAL_REFERENCE_PATCH_BEGIN,
70
+ '# Switch only the visible @ implementation; Reference Anything stays loaded.',
71
+ '- id: ui-reference',
72
+ ` disabled: ${mode === 'official' ? 'false' : 'true'}`,
73
+ OFFICIAL_REFERENCE_PATCH_END,
74
+ ];
75
+ return [...lines, ...(lines.length > 0 ? [''] : []), ...block, ''].join(eol);
76
+ }
77
+ function markerRows(lines, marker) {
78
+ const rows = [];
79
+ lines.forEach((line, index) => { if (line.trim() === marker)
80
+ rows.push(index); });
81
+ return rows;
82
+ }
83
+ /** `[]` is the valid empty profile document, but cannot precede list rows. */
84
+ function removeEmptyArrayDocument(lines) {
85
+ const meaningful = lines
86
+ .map((line, index) => ({ index, text: line.trim() }))
87
+ .filter(row => row.text !== '' && !row.text.startsWith('#'));
88
+ if (meaningful.length === 1 && meaningful[0]?.text === '[]')
89
+ lines.splice(meaningful[0].index, 1);
90
+ }
@@ -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
- * Two schemes are expanded. `dsh-ref:` names material outside the harness and
12
- * is resolved through `ctx.references`. `dsh-session:` names another harness
13
- * session and is delegated to `ctx.sessionReferenceResolver`, which owns
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>;