soulnet-dsh 0.1.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/cordis.patch.yml +40 -0
  4. package/lib/client.js +5427 -0
  5. package/lib/client.js.map +1 -0
  6. package/lib/commands.js +94 -0
  7. package/lib/commands.js.map +1 -0
  8. package/lib/events-Cz7g9aXF.js +134 -0
  9. package/lib/events-Cz7g9aXF.js.map +1 -0
  10. package/lib/events-DHPK3pow.js +134 -0
  11. package/lib/events-DHPK3pow.js.map +1 -0
  12. package/lib/events-DViXNRAZ.js +9 -0
  13. package/lib/events-DViXNRAZ.js.map +1 -0
  14. package/lib/index.js +2547 -0
  15. package/lib/index.js.map +1 -0
  16. package/lib/policy-HQKLctKr.js +96 -0
  17. package/lib/policy-HQKLctKr.js.map +1 -0
  18. package/lib/send-BL_Yr40O.js +33 -0
  19. package/lib/send-BL_Yr40O.js.map +1 -0
  20. package/lib/send-DcYQC5CC.js +127 -0
  21. package/lib/send-DcYQC5CC.js.map +1 -0
  22. package/lib/sessions.js +1197 -0
  23. package/lib/sessions.js.map +1 -0
  24. package/lib/tools.js +342 -0
  25. package/lib/tools.js.map +1 -0
  26. package/lib/types/alter-state.d.ts +185 -0
  27. package/lib/types/api/index.d.ts +52 -0
  28. package/lib/types/client/A2ANode.d.ts +4 -0
  29. package/lib/types/client/AlterPane.d.ts +9 -0
  30. package/lib/types/client/ChatPane.d.ts +13 -0
  31. package/lib/types/client/DraftCard.d.ts +11 -0
  32. package/lib/types/client/FriendComposer.d.ts +20 -0
  33. package/lib/types/client/FriendList.d.ts +12 -0
  34. package/lib/types/client/FriendPane.d.ts +13 -0
  35. package/lib/types/client/Inbox.d.ts +9 -0
  36. package/lib/types/client/InboxOverlay.d.ts +8 -0
  37. package/lib/types/client/Onboarding.d.ts +4 -0
  38. package/lib/types/client/SettingsSection.d.ts +29 -0
  39. package/lib/types/client/SidebarEntry.d.ts +9 -0
  40. package/lib/types/client/SoulmirrorPage.d.ts +12 -0
  41. package/lib/types/client/SoulmirrorView.d.ts +5 -0
  42. package/lib/types/client/a2a-node.d.ts +46 -0
  43. package/lib/types/client/alter-ui.d.ts +35 -0
  44. package/lib/types/client/api.d.ts +424 -0
  45. package/lib/types/client/inbox-state.d.ts +57 -0
  46. package/lib/types/client/index.d.ts +30 -0
  47. package/lib/types/client/locales.d.ts +239 -0
  48. package/lib/types/client/page-state.d.ts +116 -0
  49. package/lib/types/client/page-store.d.ts +96 -0
  50. package/lib/types/client/panel-store.d.ts +30 -0
  51. package/lib/types/client/styles.d.ts +2 -0
  52. package/lib/types/commands/index.d.ts +24 -0
  53. package/lib/types/drafts.d.ts +50 -0
  54. package/lib/types/events.d.ts +61 -0
  55. package/lib/types/friend-settings.d.ts +41 -0
  56. package/lib/types/index.d.ts +43 -0
  57. package/lib/types/network/fake.d.ts +10 -0
  58. package/lib/types/network/jsonrpc.d.ts +52 -0
  59. package/lib/types/network/send.d.ts +19 -0
  60. package/lib/types/network/soulnet.d.ts +123 -0
  61. package/lib/types/network/types.d.ts +203 -0
  62. package/lib/types/persona.d.ts +45 -0
  63. package/lib/types/policy.d.ts +105 -0
  64. package/lib/types/sessions/index.d.ts +182 -0
  65. package/lib/types/settings.d.ts +58 -0
  66. package/lib/types/tools/define.d.ts +35 -0
  67. package/lib/types/tools/index.d.ts +57 -0
  68. package/lib/types-zwvjR1n5.js +29 -0
  69. package/lib/types-zwvjR1n5.js.map +1 -0
  70. package/package.json +133 -0
  71. package/presets/soulmirror-chat/agent.cordis.yml +24 -0
  72. package/presets/soulmirror-chat/preset.yml +3 -0
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Browser side of the host HTTP API (see ../api/index.ts): typed fetch
3
+ * helpers, a Server-Sent-Events subscription, and one small shared store
4
+ * (React `useSyncExternalStore` shape) holding the last known network state.
5
+ * Same-origin requests only; dsh web serves the bundle and `/soulmirror/api/`
6
+ * from the same loopback server.
7
+ */
8
+ import { type InboxState, type MailNotice } from './inbox-state.ts';
9
+ export declare const API_BASE = "/soulmirror/api/";
10
+ export interface ApiIdentity {
11
+ fp: string;
12
+ name: string;
13
+ cardUri: string;
14
+ createdAt?: string;
15
+ }
16
+ export type ReplyTier = 'notify' | 'draft' | 'auto';
17
+ export interface ApiFriend {
18
+ fp: string;
19
+ name: string;
20
+ remark?: string;
21
+ cardName?: string;
22
+ online?: boolean;
23
+ unread: number;
24
+ count: number;
25
+ lastTs?: number;
26
+ lastBody?: string;
27
+ typing?: boolean;
28
+ /** Per-friend diplomacy protocol override (friends.yaml `protocol`). */
29
+ protocol?: string;
30
+ /** Effective reply tier (P3); `tierExplicit` when stored for this friend rather than the global default. */
31
+ tier?: ReplyTier;
32
+ tierExplicit?: true;
33
+ /** Pending drafts of the alter to this friend (P4). */
34
+ drafts?: number;
35
+ }
36
+ /** What woke a turn of the alter session. */
37
+ export interface ApiTrigger {
38
+ kind: 'owner' | 'inbound' | 'inbound-auto' | 'unknown';
39
+ fp?: string;
40
+ name?: string;
41
+ messageId?: string;
42
+ }
43
+ export type ApiSendOutcome = 'sent' | 'draft-queued' | 'refused' | 'failed';
44
+ /** The alter's latest state (host `session.latest`, SSE `alter`; see src/alter-state.ts). */
45
+ export interface ApiAlterLatest {
46
+ instruction?: {
47
+ text: string;
48
+ ts: number;
49
+ seq: number;
50
+ };
51
+ note?: {
52
+ text: string;
53
+ ts: number;
54
+ seq: number;
55
+ turn: number;
56
+ };
57
+ sent?: {
58
+ body: string;
59
+ fingerprint?: string;
60
+ ts: number;
61
+ outcome?: ApiSendOutcome;
62
+ gate?: string;
63
+ draftId?: string;
64
+ detail?: string;
65
+ };
66
+ turn?: {
67
+ turn: number;
68
+ reason: string;
69
+ failed: boolean;
70
+ ts: number;
71
+ message?: string;
72
+ open: boolean;
73
+ };
74
+ trigger: ApiTrigger;
75
+ seq: number;
76
+ }
77
+ export interface ApiAlterState {
78
+ sessionId: string;
79
+ status: 'idle' | 'running';
80
+ latest: ApiAlterLatest;
81
+ }
82
+ /** One item of the "My alter" transcript (host `session.history`; see src/alter-state.ts `chatFromEvents`). */
83
+ export type ApiChatItem = {
84
+ kind: 'owner';
85
+ key: string;
86
+ ts: number;
87
+ text: string;
88
+ revise?: {
89
+ name: string;
90
+ fp: string;
91
+ };
92
+ } | {
93
+ kind: 'alter';
94
+ key: string;
95
+ ts: number;
96
+ text: string;
97
+ turn: number;
98
+ } | {
99
+ kind: 'inbound';
100
+ key: string;
101
+ ts: number;
102
+ fp: string;
103
+ name: string;
104
+ id: string;
105
+ body: string;
106
+ auto: boolean;
107
+ type?: string;
108
+ } | {
109
+ kind: 'send';
110
+ key: string;
111
+ ts: number;
112
+ fp: string;
113
+ body: string;
114
+ outcome?: ApiSendOutcome;
115
+ gate?: string;
116
+ draftId?: string;
117
+ detail?: string;
118
+ auto: boolean;
119
+ } | {
120
+ kind: 'note';
121
+ key: string;
122
+ ts: number;
123
+ note: 'draft-approved' | 'draft-rejected' | 'draft-revise';
124
+ fp: string;
125
+ text: string;
126
+ draftId?: string;
127
+ } | {
128
+ kind: 'turn-failed';
129
+ key: string;
130
+ ts: number;
131
+ turn: number;
132
+ reason: string;
133
+ message?: string;
134
+ };
135
+ export interface ApiChat {
136
+ items: ApiChatItem[];
137
+ running: boolean;
138
+ seq: number;
139
+ }
140
+ export interface ApiHistory {
141
+ sessionId: string | null;
142
+ status: 'idle' | 'running';
143
+ chat: ApiChat;
144
+ }
145
+ /** A pending draft of the alter (P4; src/drafts.ts). */
146
+ export interface ApiDraft {
147
+ id: string;
148
+ fp: string;
149
+ name: string;
150
+ body: string;
151
+ createdAt: string;
152
+ reason: string;
153
+ trigger?: {
154
+ kind: string;
155
+ fp?: string;
156
+ name?: string;
157
+ messageId?: string;
158
+ };
159
+ sessionId?: string;
160
+ }
161
+ /** Alter-wide settings as the host resolves them (live). */
162
+ export interface ApiAlterConfig {
163
+ sessionId: string | null;
164
+ status: 'idle' | 'running';
165
+ defaultTier: ReplyTier;
166
+ autoReplyPerHour: number;
167
+ directSend: boolean;
168
+ protocolPath: string;
169
+ protocolExists: boolean;
170
+ legacyFriendSessions: Record<string, string>;
171
+ }
172
+ export interface ApiPending {
173
+ id: string;
174
+ fp: string;
175
+ name: string;
176
+ greeting: string;
177
+ createdAt?: string;
178
+ }
179
+ /** One archived conversation entry (`conversation.get`, `message.send`, SSE `outbound`). */
180
+ export interface ApiEntry {
181
+ seq: number;
182
+ dir: 'in' | 'out';
183
+ id: string;
184
+ body: string;
185
+ ts: number;
186
+ type?: string;
187
+ auto?: true;
188
+ status?: string;
189
+ artifactName?: string;
190
+ }
191
+ export interface ApiStatus {
192
+ backend: 'fake' | 'soulnet';
193
+ state: 'starting' | 'ready' | 'restarting' | 'stopped' | 'error';
194
+ pid?: number;
195
+ restarts: number;
196
+ lastError?: string;
197
+ relay?: string;
198
+ home?: string;
199
+ protocol?: string;
200
+ version?: string;
201
+ binary?: string;
202
+ binarySource?: 'setting' | 'platform-package' | 'path' | 'plugin-bin' | string;
203
+ }
204
+ export interface ApiState {
205
+ backend: 'fake' | 'soulnet';
206
+ status: ApiStatus;
207
+ home: string;
208
+ settingsNamespace: string;
209
+ identity: ApiIdentity | null;
210
+ friends: ApiFriend[];
211
+ pending: ApiPending[];
212
+ drafts: ApiDraft[];
213
+ alter?: ApiAlterConfig;
214
+ error?: string;
215
+ }
216
+ export declare class ApiError extends Error {
217
+ readonly code: number;
218
+ readonly httpStatus: number;
219
+ readonly name = "ApiError";
220
+ constructor(message: string, code: number, httpStatus: number);
221
+ }
222
+ export declare const api: {
223
+ state: () => Promise<ApiState>;
224
+ createIdentity: (name: string) => Promise<{
225
+ identity: ApiIdentity;
226
+ }>;
227
+ parseCard: (uri: string) => Promise<{
228
+ fp: string;
229
+ name: string;
230
+ uri: string;
231
+ }>;
232
+ addFriend: (cardUri: string, note?: string) => Promise<{
233
+ friend: ApiFriend;
234
+ }>;
235
+ accept: (id: string, note?: string) => Promise<{
236
+ friend: ApiFriend;
237
+ }>;
238
+ reject: (id: string) => Promise<{
239
+ ok: true;
240
+ }>;
241
+ markRead: (fp: string, seq?: number) => Promise<{
242
+ ok: true;
243
+ }>;
244
+ /** Archive of one friend: `since` = only seq > since, `limit` = the last N entries. */
245
+ conversation: (fp: string, opts?: {
246
+ since?: number;
247
+ limit?: number;
248
+ }) => Promise<{
249
+ entries: ApiEntry[];
250
+ typing: boolean;
251
+ }>;
252
+ /** Debug: direct send to a friend through the peer (bypasses the alter); answers the archived entry. */
253
+ send: (fp: string, body: string) => Promise<{
254
+ entry: ApiEntry;
255
+ receipt: {
256
+ id: string;
257
+ seq?: number;
258
+ status: string;
259
+ };
260
+ }>;
261
+ typing: (fp: string, on: boolean) => Promise<{
262
+ ok: true;
263
+ }>;
264
+ presence: (fps: readonly string[]) => Promise<{
265
+ online: Record<string, boolean>;
266
+ }>;
267
+ /** The owner instructs their alter (an owner user/message + a woken turn in the alter session). */
268
+ instruct: (text: string) => Promise<{
269
+ sessionId: string;
270
+ messageId: string;
271
+ state: ApiAlterState | null;
272
+ }>;
273
+ /** The alter's latest state (null = no session yet). */
274
+ sessionLatest: () => Promise<{
275
+ state: ApiAlterState | null;
276
+ }>;
277
+ /** The alter transcript (last `limit` items). */
278
+ sessionHistory: (limit?: number) => Promise<ApiHistory>;
279
+ /** Pending drafts (all, or one friend's). */
280
+ drafts: (fp?: string) => Promise<{
281
+ drafts: ApiDraft[];
282
+ }>;
283
+ /** Decide a draft: approve (optionally edited), reject, or send it back to the alter with feedback. */
284
+ decideDraft: (id: string, decision: {
285
+ action: "approve";
286
+ body?: string;
287
+ } | {
288
+ action: "reject";
289
+ } | {
290
+ action: "revise";
291
+ feedback: string;
292
+ }) => Promise<{
293
+ ok: true;
294
+ draft: ApiDraft;
295
+ entry?: ApiEntry;
296
+ }>;
297
+ /** Note / protocol override (peer) and reply tier (plugin) of a friend; `tier: ''` = back to the default. */
298
+ friendSet: (fp: string, patch: {
299
+ note?: string;
300
+ protocol?: string;
301
+ tier?: ReplyTier | "";
302
+ }) => Promise<{
303
+ friend: ApiFriend;
304
+ }>;
305
+ friendCard: (fp: string) => Promise<{
306
+ fp: string;
307
+ name: string;
308
+ uri: string;
309
+ }>;
310
+ protocolGet: () => Promise<{
311
+ text: string;
312
+ path: string;
313
+ exists: boolean;
314
+ }>;
315
+ protocolSet: (text: string) => Promise<{
316
+ ok: true;
317
+ text: string;
318
+ path: string;
319
+ }>;
320
+ };
321
+ export type NetworkEventFrame = {
322
+ kind: 'message';
323
+ message: {
324
+ id: string;
325
+ from: string;
326
+ name: string;
327
+ body: string;
328
+ ts: number;
329
+ seq?: number;
330
+ type?: string;
331
+ auto?: true;
332
+ artifactName?: string;
333
+ };
334
+ }
335
+ /** An outbound entry was archived (the alter sent, a draft was approved, or the debug direct send): `fp` is the friend. */
336
+ | {
337
+ kind: 'outbound';
338
+ fp: string;
339
+ entry: ApiEntry;
340
+ } | {
341
+ kind: 'typing';
342
+ fp: string;
343
+ on: boolean;
344
+ } | {
345
+ kind: 'friend_request';
346
+ request: ApiPending;
347
+ } | {
348
+ kind: 'friend_accept';
349
+ friend: ApiFriend;
350
+ } | {
351
+ kind: 'presence';
352
+ fp: string;
353
+ online: boolean;
354
+ } | {
355
+ kind: 'status';
356
+ status: ApiStatus;
357
+ }
358
+ /** The alter's state changed (a turn, a note, a send …). */
359
+ | {
360
+ kind: 'alter';
361
+ state: ApiAlterState;
362
+ }
363
+ /** A pending draft was stored or decided. */
364
+ | {
365
+ kind: 'draft';
366
+ action: 'added' | 'removed';
367
+ draft: ApiDraft;
368
+ decision?: 'approved' | 'rejected' | 'revise';
369
+ };
370
+ export interface NetworkStoreSnapshot {
371
+ readonly state: ApiState | undefined;
372
+ readonly loading: boolean;
373
+ readonly error: string | undefined;
374
+ /** fp → typing flag (live, from SSE). */
375
+ readonly typing: Readonly<Record<string, boolean>>;
376
+ readonly status: ApiStatus | undefined;
377
+ /** Bumps on every SSE frame so lists can refetch. */
378
+ readonly revision: number;
379
+ /**
380
+ * Friends / pending / drafts as folded from the last `/state` answer plus
381
+ * the SSE frames since (optimistic; replaced by the next refetch). The
382
+ * sidebar badge, the page's middle column and the toast read this, not
383
+ * `state` directly.
384
+ */
385
+ readonly inbox: InboxState;
386
+ }
387
+ /**
388
+ * One store per browser page: `refresh()` pulls `/state`, `connect()` keeps the
389
+ * SSE stream open (typing, friend events, backend status) while someone
390
+ * subscribes. Components read it through `useSyncExternalStore`.
391
+ */
392
+ export declare class NetworkStore {
393
+ private snapshot;
394
+ private readonly listeners;
395
+ private readonly mailListeners;
396
+ private readonly frameListeners;
397
+ private source;
398
+ private refreshTimer;
399
+ private inflight;
400
+ getSnapshot: () => NetworkStoreSnapshot;
401
+ subscribe: (listener: () => void) => (() => void);
402
+ /**
403
+ * Subscribe to new-mail notices (one per inbound `message` frame). The
404
+ * caller decides whether to show a cue (e.g. only when that thread is not
405
+ * on screen). Independent of the snapshot subscription; does not open the
406
+ * SSE stream by itself.
407
+ */
408
+ onMail: (listener: (notice: MailNotice) => void) => (() => void);
409
+ /** Subscribe to every SSE frame as it arrives (the page's stores fold `message` / `outbound` / `alter` / `draft`). */
410
+ onFrame: (listener: (frame: NetworkEventFrame) => void) => (() => void);
411
+ /** Fold a `presence` answer (fp -> online) into the friend rows. */
412
+ applyPresence: (online: Readonly<Record<string, boolean>>) => void;
413
+ /** Fold an outbound entry we just archived ourselves (preview / time of the friend row). */
414
+ applyOutbound: (fp: string, entry: ApiEntry) => void;
415
+ private set;
416
+ /** The owner read a friend's conversation: zero its unread locally and tell the host. */
417
+ markRead: (fp: string) => Promise<void>;
418
+ refresh: () => Promise<void>;
419
+ /** Debounced refresh after SSE frames that change lists. */
420
+ private scheduleRefresh;
421
+ private connect;
422
+ private disconnect;
423
+ }
424
+ export declare const networkStore: NetworkStore;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Pure inbox state: the friend list as the browser sees it (from
3
+ * `/soulmirror/api/state`) folded with the live SSE frames (`message`,
4
+ * `outbound`, `presence`, `typing`, `friend_accept`, `friend_request`,
5
+ * `draft`) so the sidebar badge, the page's middle column and the new-mail
6
+ * cue update instantly — the authoritative `/state` refetch that follows a
7
+ * frame replaces the optimistic fold. No React, no DOM: unit-tested under
8
+ * node (test/inbox-state.test.ts).
9
+ */
10
+ import type { ApiDraft, ApiFriend, ApiPending, ApiState, NetworkEventFrame } from './api.ts';
11
+ export interface InboxFriend extends ApiFriend {
12
+ }
13
+ /** The slice of the network state the inbox surfaces read. */
14
+ export interface InboxState {
15
+ readonly friends: readonly InboxFriend[];
16
+ readonly pending: readonly ApiPending[];
17
+ /** Pending drafts of the alter (P4), oldest first. */
18
+ readonly drafts: readonly ApiDraft[];
19
+ /** The alter session id (host), when known. */
20
+ readonly alterSessionId: string | undefined;
21
+ /** fp → typing flag (live, never persisted). */
22
+ readonly typing: Readonly<Record<string, boolean>>;
23
+ }
24
+ /** What the new-mail cue needs: who wrote, what, and the alter session id (the toast is suppressed while it is on screen). */
25
+ export interface MailNotice {
26
+ readonly id: string;
27
+ readonly fp: string;
28
+ readonly name: string;
29
+ readonly body: string;
30
+ readonly ts: number;
31
+ readonly sessionId: string | undefined;
32
+ }
33
+ export declare const EMPTY_INBOX: InboxState;
34
+ /** Unread total over all friends (the sidebar badge number). */
35
+ export declare function unreadTotal(friends: readonly Pick<InboxFriend, 'unread'>[]): number;
36
+ /** Friends sorted for the inbox: unread first, then by last message time (desc), then by name. */
37
+ export declare function sortInbox(friends: readonly InboxFriend[]): InboxFriend[];
38
+ /** Pending drafts for one friend, oldest first. */
39
+ export declare function draftsFor(state: Pick<InboxState, 'drafts'>, fp: string): ApiDraft[];
40
+ /** Replace the folded state with a fresh `/state` answer (keeps the live typing flags and, for rows without `online`, the last known presence). */
41
+ export declare function fromApiState(previous: InboxState, state: ApiState): InboxState;
42
+ /**
43
+ * Fold one SSE frame into the state. Returns the next state and, for
44
+ * `message` frames, the notice the new-mail cue should show.
45
+ */
46
+ export declare function applyFrame(state: InboxState, frame: NetworkEventFrame): {
47
+ state: InboxState;
48
+ notice?: MailNotice;
49
+ };
50
+ /** The owner read a conversation: zero its unread count locally (the host/peer cursor moves in parallel). */
51
+ export declare function markReadLocal(state: InboxState, fp: string): InboxState;
52
+ /** Should the new-mail cue fire? Only while the alter session is not the one on screen. */
53
+ export declare function shouldNotify(notice: MailNotice, currentSessionId: string | undefined): boolean;
54
+ /** Compact relative time for the inbox row: "now", "5m", "3h", "2d", else a short date. */
55
+ export declare function formatAge(ts: number | undefined, now?: number): string;
56
+ /** One-line preview of a message body for the inbox row. */
57
+ export declare function previewOf(body: string | undefined, max?: number): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * soulnet-dsh browser half (served as
3
+ * /plugins/soulnet-dsh/client.js).
4
+ *
5
+ * Registers: zh/en dictionaries, the `a2a-message` Conversation Node
6
+ * Definition + keyed Chat renderer (inbound mail bubbles / plugin notes in
7
+ * the native alter session), the "SoulMirror" `conversation.view` tab (opens
8
+ * the page), the `sidebar.footer.action` entry (SoulMirror button + unread
9
+ * badge → toggles the page), two `shell.overlay` entries (`soulmirror-page`:
10
+ * the full page — the pinned "My alter" chat, the friend list and the
11
+ * read-only friend threads, right of dsh's sidebar; `soulmirror-inbox`: the
12
+ * new-mail toast), the `settings.section` "SoulMirror network", the
13
+ * `settings.onboarding` identity step, and the client side of the slash
14
+ * commands (`/friends` and `/soulmirror` popups open the page). The P3
15
+ * composer takeover of friend sessions is gone with the friend sessions
16
+ * (P4): the alter session keeps dsh's native composer. All cross-plugin
17
+ * collaboration goes through cordis services (`ctx.slots`, `ctx.locale`,
18
+ * `ctx.conversationEvents`, `ctx.sessions`, `ctx.settingsScope`,
19
+ * `ctx.commandUi`); the only value imports are react, ui-primitives and this
20
+ * package's own files (bundle purity).
21
+ */
22
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
23
+ export type { A2AChatData } from './a2a-node.ts';
24
+ export type { SoulmirrorKey } from './locales.ts';
25
+ export type { InboxState, MailNotice } from './inbox-state.ts';
26
+ export type { ThreadEntry, ThreadState } from './page-state.ts';
27
+ export declare const inject: string[];
28
+ /** Whether a session cwd is the SoulMirror workspace (`<home>/a2a`, e.g. `~/.soulnet/a2a`). */
29
+ export declare function isSoulmirrorCwd(cwd: string | undefined): boolean;
30
+ export declare function apply(ctx: ClientContext): void;