dsh-reference-anything 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.
Files changed (66) hide show
  1. package/LICENSE +22 -0
  2. package/NOTICE.md +54 -0
  3. package/README.md +228 -0
  4. package/cordis.patch.yml +63 -0
  5. package/images/logo.png +0 -0
  6. package/lib/client/index.d.ts +3 -0
  7. package/lib/client.js +18151 -0
  8. package/lib/contract.d.ts +300 -0
  9. package/lib/contract.js +102 -0
  10. package/lib/errors.d.ts +56 -0
  11. package/lib/errors.js +21 -0
  12. package/lib/host.d.ts +102 -0
  13. package/lib/host.js +55 -0
  14. package/lib/index.d.ts +83 -0
  15. package/lib/index.js +153 -0
  16. package/lib/opencli.d.ts +116 -0
  17. package/lib/opencli.js +356 -0
  18. package/lib/reference.d.ts +49 -0
  19. package/lib/reference.js +228 -0
  20. package/lib/render.d.ts +101 -0
  21. package/lib/render.js +250 -0
  22. package/lib/retain.d.ts +35 -0
  23. package/lib/retain.js +116 -0
  24. package/lib/search.d.ts +75 -0
  25. package/lib/search.js +126 -0
  26. package/lib/serialize.d.ts +22 -0
  27. package/lib/serialize.js +24 -0
  28. package/lib/sources/file.d.ts +100 -0
  29. package/lib/sources/file.js +236 -0
  30. package/lib/sources/web-chat/index.d.ts +114 -0
  31. package/lib/sources/web-chat/index.js +359 -0
  32. package/lib/store/spec.d.ts +312 -0
  33. package/lib/store/spec.js +94 -0
  34. package/lib/store/store.d.ts +189 -0
  35. package/lib/store/store.js +638 -0
  36. package/lib/sync/index.d.ts +91 -0
  37. package/lib/sync/index.js +425 -0
  38. package/lib/tool.d.ts +62 -0
  39. package/lib/tool.js +364 -0
  40. package/lib/typert.d.ts +2 -0
  41. package/lib/typert.js +34 -0
  42. package/lib/types.d.ts +215 -0
  43. package/lib/types.js +12 -0
  44. package/lib/uri-codec.d.ts +5 -0
  45. package/lib/uri-codec.js +39 -0
  46. package/lib/uri.d.ts +59 -0
  47. package/lib/uri.js +86 -0
  48. package/lib/web.d.ts +4 -0
  49. package/lib/web.js +11 -0
  50. package/lib/window.d.ts +23 -0
  51. package/lib/window.js +35 -0
  52. package/lib/wire.d.ts +117 -0
  53. package/lib/wire.js +49 -0
  54. package/lib/workspace.d.ts +18 -0
  55. package/lib/workspace.js +81 -0
  56. package/opencli-plugin/chatgpt.js +125 -0
  57. package/opencli-plugin/claude.js +134 -0
  58. package/opencli-plugin/common.js +307 -0
  59. package/opencli-plugin/deepseek.js +152 -0
  60. package/opencli-plugin/gemini.js +165 -0
  61. package/opencli-plugin/grok.js +123 -0
  62. package/opencli-plugin/kimi.js +123 -0
  63. package/opencli-plugin/opencli-plugin.json +6 -0
  64. package/opencli-plugin/package.json +8 -0
  65. package/opencli-plugin/since-guard.js +45 -0
  66. package/package.json +180 -0
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
3
+ import { providerSchema, settingsRecordSchema } from "../wire.js";
4
+ export { providerSchema, settingsRecordSchema };
5
+ export const storedAttachmentSchema = z.object({
6
+ attachmentId: z.string(),
7
+ kind: z.enum(['image', 'file']).default('file'),
8
+ name: z.string(),
9
+ mimeType: z.string(),
10
+ size: z.number(),
11
+ status: z.enum(['available', 'unavailable', 'expired']),
12
+ locator: z.string().optional(),
13
+ });
14
+ export const storedTurnSchema = z.object({
15
+ ordinal: z.number().int().nonnegative(),
16
+ messageId: z.string(),
17
+ parentId: z.string(),
18
+ branchId: z.string(),
19
+ role: z.enum(['user', 'assistant']),
20
+ text: z.string(),
21
+ createdAt: z.string(),
22
+ attachments: z.array(storedAttachmentSchema),
23
+ });
24
+ export const conversationRecordSchema = z.object({
25
+ provider: providerSchema,
26
+ accountScope: z.string(),
27
+ externalId: z.string(),
28
+ title: z.string(),
29
+ url: z.string(),
30
+ currentRevision: z.string().optional(),
31
+ createdAt: z.string(),
32
+ updatedAt: z.string(),
33
+ messageCount: z.number().int().nonnegative(),
34
+ partial: z.boolean(),
35
+ remoteMissing: z.boolean(),
36
+ syncedAt: z.string(),
37
+ });
38
+ export const revisionRecordSchema = z.object({
39
+ conversationKey: z.string(),
40
+ revision: z.string(),
41
+ contentHash: z.string(),
42
+ turnCount: z.number().int().nonnegative(),
43
+ activeBranch: z.string(),
44
+ chunkKeys: z.array(z.string()),
45
+ partial: z.boolean(),
46
+ syncedAt: z.string(),
47
+ expiresAt: z.string(),
48
+ });
49
+ export const turnChunkRecordSchema = z.object({
50
+ conversationKey: z.string(),
51
+ revision: z.string(),
52
+ index: z.number().int().nonnegative(),
53
+ turns: z.array(storedTurnSchema).max(50),
54
+ });
55
+ export const attachmentRecordSchema = storedAttachmentSchema.extend({
56
+ conversationKey: z.string(),
57
+ revision: z.string(),
58
+ ordinal: z.number().int().nonnegative(),
59
+ });
60
+ export const syncStateRecordSchema = z.object({
61
+ provider: providerSchema,
62
+ profile: z.string(),
63
+ accountScope: z.string(),
64
+ cursor: z.string(),
65
+ status: z.enum(['idle', 'running', 'cancelled', 'failed']),
66
+ lastSyncAt: z.string(),
67
+ lastCompleteScanAt: z.string(),
68
+ error: z.string(),
69
+ completed: z.number().int().nonnegative(),
70
+ total: z.number().int().nonnegative(),
71
+ // Both carry `.default()` so rows written before background sync existed
72
+ // still parse at the durable read boundary — the domain validates with
73
+ // `schema.parse`, so the default lands in memory and no version bump (which
74
+ // would reject the whole medium) is needed.
75
+ /** Provider-level failures in a row; drives how long auto-sync skips this provider. */
76
+ consecutiveFailures: z.number().int().nonnegative().default(0),
77
+ /** ISO instant before which auto-sync leaves this provider alone. Manual syncs ignore it. */
78
+ nextEligibleAt: z.string().default(''),
79
+ });
80
+ export const referenceAnythingDomainSpec = defineDomain({
81
+ name: 'reference_anything',
82
+ version: 1,
83
+ global: {
84
+ schema: settingsRecordSchema,
85
+ initial: { opencliPath: 'opencli', profile: '', detailConcurrency: 8, autoSync: false, syncOnStartup: false, autoSyncMinutes: 60, historyMode: 'metadata-only', enabledProviders: [...providerSchema.options], maxReadTurns: 10, inputRenderMode: 'pill' },
86
+ },
87
+ tables: {
88
+ conversations: domainTable(conversationRecordSchema),
89
+ revisions: domainTable(revisionRecordSchema),
90
+ turn_chunks: domainTable(turnChunkRecordSchema),
91
+ attachments: domainTable(attachmentRecordSchema),
92
+ sync_states: domainTable(syncStateRecordSchema),
93
+ },
94
+ });
@@ -0,0 +1,189 @@
1
+ import type { Domain, KvTable } from '@deepseek-ai/dsh-storage-domain';
2
+ import type { ReferenceSnapshot, ReferenceWindow } from '../types.ts';
3
+ import type { AttachmentRecord, ChatProvider, ConversationRecord, RevisionRecord, SettingsRecord, SyncStateRecord, TurnChunkRecord } from './spec.ts';
4
+ import type { referenceAnythingDomainSpec } from './spec.ts';
5
+ export declare const TURNS_PER_CHUNK = 50;
6
+ /**
7
+ * Shortest query that may trigger a body scan. One character matches almost
8
+ * every conversation, which is neither useful nor worth the scan.
9
+ */
10
+ export declare const CONTENT_MIN_QUERY = 2;
11
+ /** Conversations a single body scan may examine, newest first. */
12
+ export declare const CONTENT_SCAN_LIMIT = 300;
13
+ /** Characters of one conversation a body scan reads before giving up on it. */
14
+ export declare const CONTENT_SCAN_CHARS = 200000;
15
+ export interface ProviderConversationRow {
16
+ provider: ChatProvider;
17
+ accountScope: string;
18
+ id: string;
19
+ title: string;
20
+ url: string;
21
+ createdAt: string;
22
+ updatedAt: string;
23
+ messageCount: number;
24
+ cursor: string;
25
+ partial: boolean;
26
+ }
27
+ export interface ProviderTurnRow {
28
+ conversationId: string;
29
+ ordinal: number;
30
+ messageId: string;
31
+ parentId: string;
32
+ branchId: string;
33
+ activeBranch: boolean;
34
+ role: 'user' | 'assistant';
35
+ text: string;
36
+ createdAt: string;
37
+ attachmentsJson: string;
38
+ partial: boolean;
39
+ }
40
+ export interface ProviderStats {
41
+ provider: ChatProvider;
42
+ conversations: number;
43
+ lastSyncedAt: string;
44
+ status: 'ready' | 'syncing' | 'error' | 'empty';
45
+ error?: string;
46
+ }
47
+ export interface StorageStats {
48
+ bytes: number;
49
+ conversations: number;
50
+ remoteMissing: number;
51
+ oldAccountConversations: number;
52
+ }
53
+ /** Durable, per-provider sync status — survives page reloads, unlike any one in-flight job's status. */
54
+ export interface ProviderSyncState {
55
+ provider: ChatProvider;
56
+ status: 'idle' | 'running' | 'cancelled' | 'failed';
57
+ lastSyncAt: string;
58
+ lastCompleteScanAt: string;
59
+ error: string;
60
+ }
61
+ /** How a conversation earned its place in a result list. */
62
+ export type MatchedVia = 'recent' | 'title' | 'content';
63
+ /** One scored discovery result. */
64
+ export interface ConversationMatch {
65
+ readonly key: string;
66
+ readonly row: ConversationRecord;
67
+ readonly via: MatchedVia;
68
+ /** Excerpt around the body hit; UI-only, and present only when `via` is `content`. */
69
+ readonly snippet?: string;
70
+ }
71
+ type RefDomain = Domain<typeof referenceAnythingDomainSpec>;
72
+ export declare class ConversationStore {
73
+ readonly domain: RefDomain;
74
+ readonly conversations: KvTable<string, ConversationRecord>;
75
+ readonly revisions: KvTable<string, RevisionRecord>;
76
+ readonly chunks: KvTable<string, TurnChunkRecord>;
77
+ readonly attachments: KvTable<string, AttachmentRecord>;
78
+ readonly syncStates: KvTable<string, SyncStateRecord>;
79
+ private readonly observedAccountScopes;
80
+ constructor(domain: RefDomain);
81
+ setActiveAccountScope(provider: ChatProvider, accountScope: string | undefined): void;
82
+ get settings(): SettingsRecord;
83
+ setSettings(settings: SettingsRecord): Promise<void>;
84
+ /** Remove every persisted transcript while retaining the title directory. */
85
+ clearMirrorContent(): Promise<void>;
86
+ static conversationKey(provider: ChatProvider, accountScope: string, externalId: string): string;
87
+ static parseConversationKey(key: string): [ChatProvider, string, string];
88
+ /**
89
+ * Rank conversations for a discovery surface, best match first.
90
+ *
91
+ * Three passes, each only reached when the one before it left room. Titles
92
+ * are matched loosely (see {@link scoreTitle}) because the `@` mention
93
+ * query can never contain a space; bodies are searched only when titles
94
+ * came up short, which is what makes the auto-generated "New chat" titles
95
+ * findable at all without paying for a scan on every keystroke.
96
+ * @param query - user query, provider prefix already stripped.
97
+ * @param provider - restrict to one provider.
98
+ * @param limit - maximum results.
99
+ * @returns matches, best first, capped at `limit`.
100
+ */
101
+ list(query: string, provider: ChatProvider | undefined, limit: number): ConversationMatch[];
102
+ stats(providers: readonly ChatProvider[]): ProviderStats[];
103
+ /**
104
+ * Search one conversation's stored turns for `pattern`.
105
+ *
106
+ * Reads chunks by key off the current revision rather than iterating the
107
+ * chunk table, whose `entries()` copies every record in the domain. Matching
108
+ * runs against the turn text in place — no lowercased copy is built, so the
109
+ * excerpt keeps its original casing and nothing is cached beside the
110
+ * records the domain already holds in memory.
111
+ * @returns the excerpt around the first hit, or undefined when there is none.
112
+ */
113
+ private findInBody;
114
+ /**
115
+ * Page through every stored conversation, including ones the provider no
116
+ * longer lists — a management view needs to surface `remoteMissing` rows
117
+ * so they can be cleaned up, unlike {@link list}'s mention/search surface.
118
+ */
119
+ page(query: string, provider: ChatProvider | undefined, limit: number, offset: number): {
120
+ items: Array<[string, ConversationRecord]>;
121
+ total: number;
122
+ };
123
+ /**
124
+ * Permanently purge one conversation and everything derived from it.
125
+ *
126
+ * A sync job racing this call can resurrect a broken row (see
127
+ * {@link WebChatHistoryService.remove}, which guards against that at the
128
+ * service layer) — this method itself makes no atomicity promise beyond
129
+ * "delete whatever currently references this key."
130
+ * @returns whether a conversation with this key existed.
131
+ */
132
+ remove(conversationKey: string): Promise<boolean>;
133
+ /** Approximate payload bytes held by this domain; filesystem overhead is excluded. */
134
+ storageStats(): StorageStats;
135
+ /** Permanently remove every row a complete remote scan marked as absent. */
136
+ removeRemoteMissing(): Promise<number>;
137
+ /** Permanently remove conversations and sync metadata owned by inactive accounts. */
138
+ removeOldAccounts(): Promise<number>;
139
+ removeProvider(provider: ChatProvider): Promise<number>;
140
+ removeOlderThan(days: number, now?: number): Promise<number>;
141
+ /**
142
+ * One row per provider, aggregated across every account scope it has ever
143
+ * synced under. `status`/`error`/`lastSyncAt` come from whichever attempt is
144
+ * most recent — success or failure — so a background failure is never
145
+ * hidden behind an older successful scope's row. `lastCompleteScanAt` is
146
+ * the max across all of that provider's rows, so it never regresses just
147
+ * because the latest attempt failed before finishing.
148
+ */
149
+ syncStateSummary(): ProviderSyncState[];
150
+ /**
151
+ * Record what the provider's listing says about one conversation.
152
+ *
153
+ * Skips the write when nothing but `syncedAt` would change: under the JSON
154
+ * storage backend a single record write re-serializes and fsyncs the entire
155
+ * domain, so an unchanged history would otherwise cost one whole-mirror
156
+ * rewrite per conversation on every pass.
157
+ * @returns the conversation key, written or not.
158
+ */
159
+ putConversation(row: ProviderConversationRow, accountScope: string): Promise<string>;
160
+ needsDetail(key: string, row: ProviderConversationRow, full: boolean): boolean;
161
+ /**
162
+ * Store one fetched transcript as an immutable revision and make it current.
163
+ *
164
+ * @param conversationKey - the conversation being updated; it must already exist.
165
+ * @param rows - the provider's turns, in any order.
166
+ * @param next - the listing row this transcript was fetched for. Supplying
167
+ * it folds the metadata update into the same write that publishes the
168
+ * revision, which is what keeps `updatedAt` — the only change signal
169
+ * {@link needsDetail} has — from advancing ahead of the content it describes.
170
+ * @returns the revision id, `sha256:<hex>` over the normalized turns.
171
+ */
172
+ commitRevision(conversationKey: string, rows: readonly ProviderTurnRow[], next?: ProviderConversationRow): Promise<string>;
173
+ /**
174
+ * Most recently observed logged-in account per provider.
175
+ *
176
+ * A non-empty account scope is written as soon as account discovery and
177
+ * listing complete, even if later per-conversation work fails. Older local
178
+ * accounts remain manageable, but must not leak back into the @ picker.
179
+ */
180
+ activeAccountScopes(): ReadonlyMap<ChatProvider, string | undefined>;
181
+ private oldAccountConversationKeys;
182
+ /** Keep exactly the latest transcript revision for a conversation. */
183
+ private removeSupersededRevisions;
184
+ read(conversationKey: string, window: ReferenceWindow): ReferenceSnapshot;
185
+ attachment(conversationKey: string, revision: string, attachmentId: string): AttachmentRecord | undefined;
186
+ markRemoteMissing(provider: ChatProvider, accountScope: string, seen: ReadonlySet<string>): Promise<void>;
187
+ collectExpired(now?: number): Promise<void>;
188
+ }
189
+ export {};