@ai-matrx/alchemy 0.6.2 → 0.8.2

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 (113) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/actions.cjs +374 -0
  3. package/dist/actions.cjs.map +1 -0
  4. package/dist/actions.d.cts +773 -0
  5. package/dist/actions.d.ts +773 -0
  6. package/dist/actions.js +351 -0
  7. package/dist/actions.js.map +1 -0
  8. package/dist/checks.cjs +31 -2
  9. package/dist/checks.cjs.map +1 -1
  10. package/dist/checks.d.cts +24 -1
  11. package/dist/checks.d.ts +24 -1
  12. package/dist/checks.js +31 -2
  13. package/dist/checks.js.map +1 -1
  14. package/dist/core.cjs +1757 -2
  15. package/dist/core.cjs.map +1 -1
  16. package/dist/core.d.cts +595 -1
  17. package/dist/core.d.ts +595 -1
  18. package/dist/core.js +1749 -2
  19. package/dist/core.js.map +1 -1
  20. package/dist/declare.cjs +12 -10
  21. package/dist/declare.cjs.map +1 -1
  22. package/dist/declare.d.cts +18 -2
  23. package/dist/declare.d.ts +18 -2
  24. package/dist/declare.js +12 -10
  25. package/dist/declare.js.map +1 -1
  26. package/dist/index.cjs +1757 -2
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +595 -1
  29. package/dist/index.d.ts +595 -1
  30. package/dist/index.js +1749 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/menu.cjs +436 -0
  33. package/dist/menu.cjs.map +1 -0
  34. package/dist/menu.d.cts +834 -0
  35. package/dist/menu.d.ts +834 -0
  36. package/dist/menu.js +413 -0
  37. package/dist/menu.js.map +1 -0
  38. package/dist/operate-document.cjs +163 -0
  39. package/dist/operate-document.cjs.map +1 -0
  40. package/dist/operate-document.d.cts +132 -0
  41. package/dist/operate-document.d.ts +132 -0
  42. package/dist/operate-document.js +130 -0
  43. package/dist/operate-document.js.map +1 -0
  44. package/dist/operate-rich-html.cjs +129 -0
  45. package/dist/operate-rich-html.cjs.map +1 -0
  46. package/dist/operate-rich-html.d.cts +136 -0
  47. package/dist/operate-rich-html.d.ts +136 -0
  48. package/dist/operate-rich-html.js +96 -0
  49. package/dist/operate-rich-html.js.map +1 -0
  50. package/dist/operate-server.cjs +157 -0
  51. package/dist/operate-server.cjs.map +1 -0
  52. package/dist/operate-server.d.cts +183 -0
  53. package/dist/operate-server.d.ts +183 -0
  54. package/dist/operate-server.js +134 -0
  55. package/dist/operate-server.js.map +1 -0
  56. package/dist/operate-xlsx.cjs +248 -0
  57. package/dist/operate-xlsx.cjs.map +1 -0
  58. package/dist/operate-xlsx.d.cts +129 -0
  59. package/dist/operate-xlsx.d.ts +129 -0
  60. package/dist/operate-xlsx.js +215 -0
  61. package/dist/operate-xlsx.js.map +1 -0
  62. package/dist/operate.cjs +1774 -0
  63. package/dist/operate.cjs.map +1 -0
  64. package/dist/operate.d.cts +595 -0
  65. package/dist/operate.d.ts +595 -0
  66. package/dist/operate.js +1750 -0
  67. package/dist/operate.js.map +1 -0
  68. package/dist/ports.cjs.map +1 -1
  69. package/dist/ports.d.cts +361 -3
  70. package/dist/ports.d.ts +361 -3
  71. package/dist/react-bar.cjs +686 -0
  72. package/dist/react-bar.cjs.map +1 -0
  73. package/dist/react-bar.d.cts +379 -0
  74. package/dist/react-bar.d.ts +379 -0
  75. package/dist/react-bar.js +665 -0
  76. package/dist/react-bar.js.map +1 -0
  77. package/dist/react-host.cjs +301 -0
  78. package/dist/react-host.cjs.map +1 -0
  79. package/dist/react-host.d.cts +820 -0
  80. package/dist/react-host.d.ts +820 -0
  81. package/dist/react-host.js +269 -0
  82. package/dist/react-host.js.map +1 -0
  83. package/dist/react-menu.cjs +735 -0
  84. package/dist/react-menu.cjs.map +1 -0
  85. package/dist/react-menu.d.cts +370 -0
  86. package/dist/react-menu.d.ts +370 -0
  87. package/dist/react-menu.js +722 -0
  88. package/dist/react-menu.js.map +1 -0
  89. package/dist/react-overflow.cjs +592 -0
  90. package/dist/react-overflow.cjs.map +1 -0
  91. package/dist/react-overflow.d.cts +817 -0
  92. package/dist/react-overflow.d.ts +817 -0
  93. package/dist/react-overflow.js +571 -0
  94. package/dist/react-overflow.js.map +1 -0
  95. package/dist/react-palette.cjs +489 -0
  96. package/dist/react-palette.cjs.map +1 -0
  97. package/dist/react-palette.d.cts +359 -0
  98. package/dist/react-palette.d.ts +359 -0
  99. package/dist/react-palette.js +457 -0
  100. package/dist/react-palette.js.map +1 -0
  101. package/dist/react-selection.cjs +617 -0
  102. package/dist/react-selection.cjs.map +1 -0
  103. package/dist/react-selection.d.cts +357 -0
  104. package/dist/react-selection.d.ts +357 -0
  105. package/dist/react-selection.js +596 -0
  106. package/dist/react-selection.js.map +1 -0
  107. package/dist/react-sheet.cjs +616 -0
  108. package/dist/react-sheet.cjs.map +1 -0
  109. package/dist/react-sheet.d.cts +361 -0
  110. package/dist/react-sheet.d.ts +361 -0
  111. package/dist/react-sheet.js +595 -0
  112. package/dist/react-sheet.js.map +1 -0
  113. package/package.json +148 -5
@@ -0,0 +1,773 @@
1
+ import { TransferOutcome } from '@ai-matrx/kit/content-transfer';
2
+
3
+ /**
4
+ * Matrx Alchemy — Declare types (CONTRACT §2.1).
5
+ *
6
+ * A declaration is plain JSON data (chair ruling N2): no functions, no classes,
7
+ * no React. Runtime behaviour lives in `MountedSurface` (`/declare/host`) and in
8
+ * handler registrations, never here. Zero React, zero `@ai-matrx/agents`.
9
+ */
10
+ type Json$1 = null | boolean | number | string | Json$1[] | {
11
+ [key: string]: Json$1;
12
+ };
13
+ /** Unchanged from `ui_surface_value_type_chk`. */
14
+ type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
15
+ type Classification = "ordinary" | "secret" | "credential";
16
+ /** Persisted (CONTRACT §2.7). Projection enforces it before any value leaves. */
17
+ interface Sensitivity {
18
+ /** false = never leaves through Operate. Default true. */
19
+ exportable: boolean;
20
+ /** secret/credential are always excluded; no caller can relax this. Default "ordinary". */
21
+ classification: Classification;
22
+ /** Pre-checked in the preparation workspace. Default true. */
23
+ includedByDefault: boolean;
24
+ }
25
+ interface ValueGroup {
26
+ key: string;
27
+ label: string;
28
+ sortOrder: number;
29
+ description?: string;
30
+ }
31
+ /** The four runtime slots a capture fills (absorbs page-capture). */
32
+ type LiveSlot = "selection" | "on-demand" | "errors" | "requests";
33
+ interface DeclaredValue {
34
+ name: string;
35
+ label: string;
36
+ description: string;
37
+ valueType: ValueType;
38
+ /** Registered content-ir kind key (DB: kind_key). */
39
+ valueKind?: string;
40
+ alwaysAvailable: boolean;
41
+ typicalCharCount: number;
42
+ autoContext?: boolean;
43
+ group?: string;
44
+ sortOrder?: number;
45
+ sensitivity?: Partial<Sensitivity>;
46
+ /** Filled at capture time from a live slot instead of the scope. */
47
+ liveSlot?: LiveSlot;
48
+ /** "request" values drop out of "Only the data". Default "data". */
49
+ role?: "data" | "request";
50
+ }
51
+ /** Where a write lands. "stream" = applied live, op by op, into an editable text. */
52
+ type WriteMode = "draft" | "entity" | "ui" | "stream";
53
+ /** Who may apply WITHOUT a click. "queued" = filed for an organization's review. */
54
+ type ApplyPolicy = "manual" | "ask" | "auto" | "queued";
55
+ /** The WidgetHandle text op set, unchanged in meaning. */
56
+ type StreamOp = "replace" | "insert_before" | "insert_after" | "prepend" | "append" | "patch";
57
+ interface WriteTarget {
58
+ name: string;
59
+ label: string;
60
+ description: string;
61
+ valueType: ValueType;
62
+ valueKind?: string;
63
+ /** Read twin (evidence loop). Required when patchable or mode === "stream". */
64
+ updatesValue?: string;
65
+ patchable?: boolean;
66
+ approvalComparison?: "text-replacement";
67
+ mode: WriteMode;
68
+ /** Required iff mode === "stream". */
69
+ streamOps?: readonly StreamOp[];
70
+ /** Default "manual". Binding overrides may only tighten. */
71
+ applyPolicy?: ApplyPolicy;
72
+ destination?: boolean;
73
+ /** Present when the target writes into an item rather than the screen. */
74
+ itemType?: string;
75
+ group?: string;
76
+ sortOrder?: number;
77
+ }
78
+ /** One kind of thing inside a screen (a row, a message, a code block, a card). */
79
+ interface ItemType {
80
+ name: string;
81
+ label: string;
82
+ description: string;
83
+ /** Value names that together identify one item. */
84
+ identity: readonly string[];
85
+ levels?: readonly string[];
86
+ entityType?: string;
87
+ values: readonly DeclaredValue[];
88
+ writeTargets?: readonly WriteTarget[];
89
+ }
90
+ type Readiness = "verified" | "partial" | "stub";
91
+ /** `ui_surface_execution_mode_check`, unchanged. */
92
+ type ExecutionMode = "python-stream" | "nextjs-stream" | "browser-realtime" | "local-runtime";
93
+ interface ConfigNamespaceDeclaration {
94
+ namespace: string;
95
+ label: string;
96
+ description: string;
97
+ }
98
+ interface EvidenceSourceDeclaration {
99
+ kind: "processed_document";
100
+ idValue: string;
101
+ fileIdValue?: string;
102
+ labelValue?: string;
103
+ representation?: "clean" | "raw";
104
+ }
105
+ /**
106
+ * One screen's declaration. Owning packages add fields (agent roles, client
107
+ * tools) as EXTENSION SLOTS — by interface extension or declaration merging —
108
+ * and register a `DeclarationExtension` so validation and the sync cover them.
109
+ */
110
+ interface SurfaceDeclaration {
111
+ surfaceName: string;
112
+ /** REQUIRED. `ui_surface.client_name`; must equal the surfaceName prefix. */
113
+ client: string;
114
+ /** REQUIRED. `ui_surface.execution_mode`. */
115
+ executionMode: ExecutionMode;
116
+ /** REQUIRED. `ui_surface.description` ("" only where the live row is empty). */
117
+ description: string;
118
+ label: string;
119
+ readiness: Readiness;
120
+ readinessNote?: string;
121
+ urlPattern?: string;
122
+ inheritsFrom?: string;
123
+ intro?: string;
124
+ overlayId?: string;
125
+ values: readonly DeclaredValue[];
126
+ groups?: readonly ValueGroup[];
127
+ writeTargets?: readonly WriteTarget[];
128
+ itemTypes?: readonly ItemType[];
129
+ liveSlots?: readonly LiveSlot[];
130
+ configNamespaces?: readonly ConfigNamespaceDeclaration[];
131
+ evidenceSources?: readonly EvidenceSourceDeclaration[];
132
+ skipBaselineValues?: boolean;
133
+ }
134
+ type ValueProvenance = {
135
+ kind: "own";
136
+ } | {
137
+ kind: "inherited";
138
+ from: string;
139
+ } | {
140
+ kind: "baseline";
141
+ };
142
+ type ResolvedValue = DeclaredValue & {
143
+ provenance: ValueProvenance;
144
+ groupKey: string;
145
+ /** Defaults filled. */
146
+ resolvedSensitivity: Sensitivity;
147
+ };
148
+ interface ResolvedSurfaceDeclaration extends SurfaceDeclaration {
149
+ contentHash: string;
150
+ values: readonly ResolvedValue[];
151
+ groups: readonly ValueGroup[];
152
+ }
153
+ /**
154
+ * ITEM KEYS. A value or write target is identified by (surfaceName, itemType ?? "", name).
155
+ * The qualified key "<itemType>.<name>" names an item value; a bare name is a screen value.
156
+ */
157
+ type QualifiedValueKey = string;
158
+ /** Live slot payloads (page-capture's shapes, kept). */
159
+ interface LiveValues {
160
+ selection?: {
161
+ choices: Record<string, Json$1>;
162
+ text: string | null;
163
+ };
164
+ "on-demand"?: readonly OnDemandSection[];
165
+ errors?: readonly string[];
166
+ requests?: readonly RecentRequest[];
167
+ }
168
+ interface OnDemandSection {
169
+ id: string;
170
+ title: string;
171
+ description?: string;
172
+ role: "data" | "request";
173
+ brief?: Json$1;
174
+ load(signal: AbortSignal): Promise<Json$1>;
175
+ }
176
+ interface RecentRequest {
177
+ method: string;
178
+ path: string;
179
+ status: string;
180
+ httpStatus?: number;
181
+ durationMs?: number;
182
+ requestId?: string;
183
+ client?: string;
184
+ requestBody?: Json$1;
185
+ requestBodyNote?: string;
186
+ errorSentence?: string;
187
+ timestamp: number;
188
+ }
189
+ /** RAW-FREE. What leaves the package about a clicked item. */
190
+ interface ResolvedItem {
191
+ itemType: string;
192
+ identity: Record<string, string>;
193
+ level?: string;
194
+ readOnly: boolean;
195
+ }
196
+
197
+ /**
198
+ * The click target (CONTRACT §2.4) in its own module: `/ports` references it,
199
+ * and `/ports` must stay free of every other package (types only).
200
+ */
201
+
202
+ /** The six universal verbs — the ONLY rows that may grey (R1 c). */
203
+ declare const UNIVERSAL_VERBS: readonly ["copy", "cut", "paste", "undo", "redo", "find"];
204
+ type UniversalVerb = (typeof UNIVERSAL_VERBS)[number];
205
+ /** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */
206
+ type PayloadKind = "text" | "markdown" | "json" | "rows" | "registered";
207
+ /** What eligibility is computed from: everything declared at the click target. */
208
+ interface ClickTarget {
209
+ surface: {
210
+ instanceId: string;
211
+ surfaceName: string;
212
+ } | null;
213
+ /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */
214
+ declaration: ResolvedSurfaceDeclaration | null;
215
+ /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */
216
+ valueKeys: ReadonlySet<QualifiedValueKey>;
217
+ /** Scope chain, most general first: ["global", "<domain>", "<surfaceName>"]. */
218
+ scopeChain: readonly string[];
219
+ /** The surface's explicit exclusions (the one override). */
220
+ excludedActionIds: ReadonlySet<string>;
221
+ item: ResolvedItem | null;
222
+ level: string | null;
223
+ /** Offsets + the live range handle, editable or not. */
224
+ selection: {
225
+ text: string;
226
+ type: "editable" | "non-editable";
227
+ start: number;
228
+ end: number;
229
+ /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */
230
+ handle?: unknown;
231
+ } | null;
232
+ /** Payload kinds a capture of this target would produce (from declared value types/kinds). */
233
+ payloadKinds: readonly PayloadKind[];
234
+ /** Declared targets writable on this target right now. */
235
+ writable: readonly WriteTarget[];
236
+ readOnly: boolean;
237
+ /** True when any declared value here is secret/credential or non-exportable. */
238
+ restricted: boolean;
239
+ organizationId: string | null;
240
+ auth: {
241
+ authenticated: boolean;
242
+ admin: boolean;
243
+ creator: boolean;
244
+ };
245
+ /**
246
+ * Host-owned context a provider reads to act (the rich-document source, the
247
+ * editable field's callbacks). Opaque to the package; never crosses into the
248
+ * model. Providers that need it narrow it themselves.
249
+ */
250
+ host?: unknown;
251
+ }
252
+
253
+ /**
254
+ * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).
255
+ *
256
+ * The app binds these ONCE, at its boundary (where the Alchemy host is
257
+ * mounted). The package never imports the host.
258
+ *
259
+ * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────
260
+ *
261
+ * This entry declares only the ports the current waves use:
262
+ *
263
+ * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)
264
+ * ALC-14 `identity`, `persistence`
265
+ * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`
266
+ *
267
+ * ALC-16 `transport`, `assets`, `server`
268
+ *
269
+ * ALC-17 adds approvals/bodies/receipts.
270
+ *
271
+ * ── Absent-port semantics ───────────────────────────────────────────────────
272
+ *
273
+ * Every port except `diagnostics` is OPTIONAL. A port the host does not bind
274
+ * means that capability is ABSENT: the actions, formats or writes that need
275
+ * it are not offered at all. A missing port is never stubbed, never faked
276
+ * with a default that pretends to work, and never shown as a dead control.
277
+ *
278
+ * kinds absent → kind validation is absent (no value can be checked,
279
+ * so a kind-bearing write is not offered)
280
+ * diagnostics REQUIRED → nothing fails silently; contract breaks land here
281
+ * identity absent → organization-scoped declarations are absent
282
+ * persistence absent → per-person settings are absent (organization and
283
+ * platform defaults apply)
284
+ * store absent → actions that read named host state or run named
285
+ * host commands are absent
286
+ * notify absent → outcomes are not announced (diagnostics still captures)
287
+ * overlay absent → actions that open an overlay or navigate are absent
288
+ * icons absent → rows render label-only (a key is never shown as text)
289
+ * knobs absent → the platform default layout/density applies
290
+ * i18n absent → labels as written
291
+ * auth absent → a signed-out person does not see requiresAuth actions
292
+ * (sign-in-then-resume is the capability that is missing)
293
+ * dialogs absent → actions that need a host dialog are absent
294
+ *
295
+ * Types only: this entry has no runtime code.
296
+ */
297
+ /** JSON as it crosses a port. */
298
+ type Json = null | boolean | number | string | Json[] | {
299
+ [key: string]: Json;
300
+ };
301
+ /**
302
+ * content-ir's kind validator, injected so Alchemy validates without
303
+ * importing the app or the kind catalog. The host builds it from
304
+ * `@ai-matrx/content-ir`'s `createKindValidator` over its own
305
+ * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never
306
+ * declares it).
307
+ *
308
+ * `unverifiable: true` marks a verdict where the kind could not be checked
309
+ * (not registered, no schema, uncompilable schema, catalog unreachable) — a
310
+ * skip, which is never a pass.
311
+ */
312
+ interface KindValidatorPort {
313
+ validate(kindKey: string, value: unknown, signal: AbortSignal): Promise<{
314
+ ok: true;
315
+ } | {
316
+ ok: false;
317
+ sentence: string;
318
+ remedy: string;
319
+ unverifiable?: true;
320
+ }>;
321
+ }
322
+
323
+ /** Where contract breaks are captured. Required: nothing fails silently. */
324
+ interface DiagnosticsPort {
325
+ capture(error: unknown, context: {
326
+ area: "declare" | "operate" | "write" | "actions" | "menu";
327
+ detail: Json;
328
+ }): void;
329
+ }
330
+ /** Who is using the app right now, and in which organization. */
331
+ interface IdentityPort {
332
+ /** `null` while the host has no resolved identity yet. */
333
+ current(): {
334
+ userId: string;
335
+ organizationId: string | null;
336
+ isAuthenticated: boolean;
337
+ isAdmin: boolean;
338
+ } | null;
339
+ /** Called whenever `current()` may answer differently; returns unsubscribe. */
340
+ onChange(listener: () => void): () => void;
341
+ }
342
+ /** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */
343
+ interface PersistencePort {
344
+ readSetting<T extends Json>(key: string): Promise<T | null>;
345
+ writeSetting(key: string, value: Json): Promise<void>;
346
+ }
347
+ /** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */
348
+ interface StorePort {
349
+ /** A named, typed read the host registered ("activeConversationId", "organizationName"). */
350
+ read<T extends Json>(key: string): T | null;
351
+ /** A named command the host registered ("tasks.setPendingSource"); resolves with its result. */
352
+ command(name: string, input: Json): Promise<Json | void>;
353
+ subscribe(key: string, listener: () => void): () => void;
354
+ }
355
+ interface NotifyPort {
356
+ success(sentence: string): void;
357
+ info(sentence: string): void;
358
+ /** Every failure names its remedy. */
359
+ error(sentence: string, remedy: string): void;
360
+ }
361
+ /** Overlays are dispatched through the host; the menu never renders a modal. */
362
+ interface OverlayHostPort {
363
+ open(overlayId: string, data: Json, instanceId?: string): void;
364
+ close(overlayId: string, instanceId?: string): void;
365
+ navigate(href: string): void;
366
+ }
367
+ /** Icon key → the host's icon component. The model only ever holds keys. */
368
+ interface IconResolverPort {
369
+ resolve(key: string): unknown | null;
370
+ }
371
+ type ContextArrangementKnob = "classic" | "tiered" | "command";
372
+ type DensityKnob = "comfortable" | "compact";
373
+ /** ORGANIZATION defaults only; every method takes the organization. */
374
+ interface MenuKnobsPort {
375
+ /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */
376
+ placementLabel(placement: string, organizationId: string | null): string | null;
377
+ /** Replaces the constant DEFAULT_PLACEMENT_MODE. */
378
+ placementMode(placement: string, organizationId: string | null): "show" | "hide";
379
+ /** The organization's default layout and density (platform default when null: command / comfortable). */
380
+ layout(organizationId: string | null): {
381
+ context: ContextArrangementKnob;
382
+ density: DensityKnob;
383
+ } | null;
384
+ }
385
+ /** Label translation. Absent = labels as written. */
386
+ interface I18nPort {
387
+ t(key: string, fallback: string): string;
388
+ }
389
+ /** Sign in, then resume the action the person picked. */
390
+ interface AuthResumePort {
391
+ isSignedIn(): boolean;
392
+ /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */
393
+ signInThenResume(pending: {
394
+ actionId: string;
395
+ contentFingerprint: string;
396
+ }): void;
397
+ takePending(contentFingerprint: string): {
398
+ actionId: string;
399
+ } | null;
400
+ }
401
+ /** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */
402
+ interface HostDialogsPort {
403
+ requestDelete?(target: ClickTarget): void;
404
+ requestConvert?(target: ClickTarget): void;
405
+ requestEditHistory?(target: ClickTarget): void;
406
+ requestFullPrint?(target: ClickTarget): void;
407
+ requestSaveTable?(table: {
408
+ headers: string[];
409
+ rows: string[][];
410
+ }): void;
411
+ requestFlashcard?(answer: string): void;
412
+ }
413
+ /** The ports a host binds. Optional = absent capability when unbound (see above). */
414
+ interface AlchemyHostPorts {
415
+ kinds?: KindValidatorPort;
416
+ /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */
417
+ diagnostics: DiagnosticsPort;
418
+ identity?: IdentityPort;
419
+ persistence?: PersistencePort;
420
+ store?: StorePort;
421
+ notify?: NotifyPort;
422
+ overlay?: OverlayHostPort;
423
+ icons?: IconResolverPort;
424
+ /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */
425
+ knobs?: MenuKnobsPort;
426
+ i18n?: I18nPort;
427
+ auth?: AuthResumePort;
428
+ dialogs?: HostDialogsPort;
429
+ /** Clipboard and download for this runtime (the browser default ships in /operate). */
430
+ transport?: TransportPort;
431
+ /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */
432
+ assets?: AssetResolverPort;
433
+ /** Present when the host can reach aidream server operations; absent = server formats are not offered. */
434
+ server?: ServerPort;
435
+ }
436
+ /** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */
437
+ interface TransportArtifact {
438
+ format: string;
439
+ plainText: string;
440
+ html?: string;
441
+ file?: {
442
+ filename: string;
443
+ mime: string;
444
+ bytes: Uint8Array;
445
+ };
446
+ }
447
+ type TransportOutcome = {
448
+ status: "success";
449
+ delivered: "clipboard" | "download-started";
450
+ mimeTypes: string[];
451
+ } | {
452
+ status: "degraded";
453
+ delivered: "plain-text";
454
+ reason: string;
455
+ } | {
456
+ status: "cancelled";
457
+ } | {
458
+ status: "error";
459
+ code: string;
460
+ message: string;
461
+ retryable: boolean;
462
+ };
463
+ /** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */
464
+ interface TransportPort {
465
+ copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
466
+ download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
467
+ }
468
+ interface AssetResolverPort {
469
+ resolve(url: string, signal: AbortSignal): Promise<{
470
+ url: string;
471
+ } | null>;
472
+ }
473
+ type ServerResult = {
474
+ status: "bytes";
475
+ filename: string;
476
+ mime: string;
477
+ bytes: Uint8Array;
478
+ notices: readonly {
479
+ code: string;
480
+ sentence: string;
481
+ remedy: string;
482
+ }[];
483
+ } | {
484
+ status: "receipt";
485
+ receipt: {
486
+ status: string;
487
+ sentence: string;
488
+ remedy?: string;
489
+ };
490
+ };
491
+ interface ServerPort {
492
+ call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;
493
+ }
494
+
495
+ /**
496
+ * Matrx Alchemy — Actions types (CONTRACT §2.4).
497
+ *
498
+ * One registry of everything a person can do to a thing. Providers plug in;
499
+ * eligibility is computed from what is declared at the click target; duplicate
500
+ * ids are refused; an action that does not apply is ABSENT (chair ruling R1).
501
+ *
502
+ * Framework-neutral: zero React, zero `@ai-matrx/agents`. Icons are string KEYS
503
+ * resolved by the host's `IconResolverPort`; labels go through `I18nPort`.
504
+ */
505
+
506
+ /**
507
+ * Chair ruling R1: an action that does not apply or cannot run now is ABSENT.
508
+ * Only the six universal verbs may answer "unavailable-verb": greyed in the
509
+ * strip/bar, the sentence as tooltip. `requiresAuth` is NOT unavailability.
510
+ */
511
+ type Eligibility = {
512
+ status: "absent";
513
+ } | {
514
+ status: "available";
515
+ } | {
516
+ status: "unavailable-verb";
517
+ verb: UniversalVerb;
518
+ sentence: string;
519
+ };
520
+ /** rich-document's ActionCategory, unchanged, plus the three the context menu groups by. */
521
+ type ActionCategory = "feedback" | "copy" | "export" | "save" | "edit" | "share" | "creator" | "app" | "listen" | "study" | "ask" | "ai" | "admin" | "clipboard" | "history" | "surface-info";
522
+ declare const ACTION_CATEGORIES: readonly ActionCategory[];
523
+ /**
524
+ * An optional named section inside the action's group (a grid's cell / row /
525
+ * column). `primary` marks the thing the person clicked: every layout renders
526
+ * it first, inline, heading kept (context-menu-v3's primary section law).
527
+ */
528
+ interface ActionSection {
529
+ id: string;
530
+ label: string;
531
+ labelKey?: string;
532
+ icon?: string;
533
+ primary?: boolean;
534
+ }
535
+ interface Action {
536
+ /** Globally unique. A duplicate is refused (never overwritten). */
537
+ id: string;
538
+ /** The provider that registered it — filled by the registry. */
539
+ provider?: string;
540
+ /** Resolved through I18nPort.t(labelKey ?? id, label). */
541
+ label: string | ((target: ClickTarget) => string);
542
+ labelKey?: string;
543
+ /** Icon KEY resolved by IconResolverPort — the model stays framework-neutral. */
544
+ icon?: string;
545
+ /** Host styling hint for the glyph (rich-document iconColor). Renderers may ignore it. */
546
+ iconTone?: string;
547
+ /** Replaces `icon` while a state holds (a spinner while speaking). */
548
+ stateIcon?(target: ClickTarget): {
549
+ icon: string;
550
+ spin?: boolean;
551
+ } | null;
552
+ hint?: string;
553
+ hintKey?: string;
554
+ description?: string;
555
+ category: ActionCategory;
556
+ /** Which universal verb this clipboard action is (addressed by role in every layout). */
557
+ verb?: UniversalVerb;
558
+ order?: number;
559
+ /** Bar layout slot. Default "overflow". */
560
+ placement?: "primary" | "overflow" | "both";
561
+ section?: ActionSection;
562
+ /** A signed-out person still sees it; running goes through AuthResumePort (sign in, then resume). */
563
+ requiresAuth?: boolean;
564
+ /** Declared write targets this action lands through. Absent on a read-only target. */
565
+ writes?: readonly string[];
566
+ destructive?: boolean;
567
+ preserveSelection?: boolean;
568
+ /** Link rows navigate instead of running. */
569
+ href?: string | ((target: ClickTarget) => string);
570
+ eligible(target: ClickTarget): Eligibility;
571
+ /** Toggle/pressed state: a pressed button or a checked row. */
572
+ pressed?(target: ClickTarget): boolean;
573
+ /** Present = the row is a checkbox (pressed is its state). */
574
+ toggle?: boolean;
575
+ subscribe?(onChange: () => void, target: ClickTarget): () => void;
576
+ /** Submenus whose rows load on open (agent lists). */
577
+ expand?(target: ClickTarget, signal: AbortSignal): Promise<readonly Action[]>;
578
+ /** Shown centred when `expand` resolves empty. */
579
+ emptyLabel?: string;
580
+ run(target: ClickTarget, context: ActionRunContext): Promise<TransferOutcome | void> | TransferOutcome | void;
581
+ }
582
+ /** One removed read, recorded (kit content-transfer's omission shape). */
583
+ interface ReadOmission {
584
+ path: string;
585
+ reason: "excluded" | "filtered";
586
+ classification?: "secret" | "credential";
587
+ }
588
+ /**
589
+ * PROJECTION BEFORE PROVIDERS. Providers never receive a raw read. Every read
590
+ * they can make is projected first: secret and credential values, values
591
+ * declared `exportable: false`, and values nobody declared are removed, with an
592
+ * omission recorded. Raw reads never leave the package.
593
+ */
594
+ interface ProjectedReads {
595
+ values(signal: AbortSignal): Promise<{
596
+ values: Record<string, Json$1>;
597
+ omissions: readonly ReadOmission[];
598
+ }>;
599
+ item(signal: AbortSignal): Promise<{
600
+ values: Record<string, Json$1>;
601
+ omissions: readonly ReadOmission[];
602
+ } | null>;
603
+ live(signal: AbortSignal): Promise<LiveValues>;
604
+ }
605
+ /**
606
+ * What a running action receives. `door` (ALC-17) and `operate` (ALC-16) join
607
+ * with their waves; until then those capabilities are absent (N8).
608
+ */
609
+ interface ActionRunContext {
610
+ reads: ProjectedReads;
611
+ ports: AlchemyHostPorts;
612
+ signal: AbortSignal;
613
+ }
614
+ interface ActionProvider {
615
+ id: string;
616
+ /** T0 providers are known at mount; T1 providers load with the menu on first open. */
617
+ tier: "T0" | "T1";
618
+ /**
619
+ * The ids this provider can ever yield, when known up front. Lets the
620
+ * registry refuse a duplicate at registration instead of at first open.
621
+ */
622
+ declaredIds?(): readonly string[];
623
+ actions(target: ClickTarget): readonly Action[] | Promise<readonly Action[]>;
624
+ }
625
+ interface ResolvedAction {
626
+ action: Action;
627
+ eligibility: Exclude<Eligibility, {
628
+ status: "absent";
629
+ }>;
630
+ }
631
+ interface ResolveRestriction {
632
+ exclude?: readonly string[];
633
+ categories?: readonly ActionCategory[];
634
+ }
635
+ interface ActionRegistry {
636
+ /** Throws DuplicateActionError naming both providers; nothing is overwritten. */
637
+ register(provider: ActionProvider): () => void;
638
+ /** restrict: a host may remove ids or categories, never add ineligible ones. */
639
+ resolve(target: ClickTarget, restrict?: ResolveRestriction): Promise<readonly ResolvedAction[]>;
640
+ /** Registered provider ids, in registration order. */
641
+ providers(): readonly string[];
642
+ /** Change notification (a provider registered or unregistered). */
643
+ subscribe(listener: () => void): () => void;
644
+ }
645
+
646
+ /**
647
+ * The one action registry (CONTRACT §2.4).
648
+ *
649
+ * - Duplicate ids are REFUSED, never overwritten (rich-document's registry
650
+ * silently overwrote — R12). A duplicate known at registration throws; one
651
+ * that only appears when a provider yields it is dropped and captured.
652
+ * - Eligibility is one pure pass over the resolved click target, under
653
+ * "restrict, never relax": a host may remove ids or categories, never add.
654
+ * - R1: an action that does not apply is ABSENT. Only the six universal verbs
655
+ * (category "clipboard") may answer `unavailable-verb`, with a sentence; any
656
+ * other action answering it is a contract break → absent + diagnostics.
657
+ */
658
+
659
+ declare class DuplicateActionError extends Error {
660
+ readonly actionId: string;
661
+ readonly providers: readonly [string, string];
662
+ constructor(actionId: string, first: string, second: string);
663
+ }
664
+ declare class DuplicateProviderError extends Error {
665
+ readonly providerId: string;
666
+ constructor(providerId: string);
667
+ }
668
+ interface ActionRegistryOptions {
669
+ /** `diagnostics` is required (nothing fails silently); `auth` decides whether requiresAuth actions reach a signed-out person. */
670
+ ports: Pick<AlchemyHostPorts, "diagnostics" | "auth">;
671
+ }
672
+ /**
673
+ * Eligibility of one action at one target — the ONE pure pass (CONTRACT §1.6).
674
+ * Exported for providers' tests; hosts call `registry.resolve`.
675
+ */
676
+ declare function computeEligibility(action: Action, target: ClickTarget, options: {
677
+ restrict?: ResolveRestriction;
678
+ authResumable: boolean;
679
+ }): Eligibility | {
680
+ status: "contract-break";
681
+ reason: string;
682
+ };
683
+ declare function createActionRegistry(options: ActionRegistryOptions): ActionRegistry;
684
+ /** A provider over a fixed list; its ids are declared up front so duplicates fail at registration. */
685
+ declare function staticActionProvider(id: string, actions: readonly Action[], tier?: ActionProvider["tier"]): ActionProvider;
686
+ /** Always-available eligibility, for actions with no rule of their own. */
687
+ declare const alwaysEligible: () => Eligibility;
688
+
689
+ /**
690
+ * `@ai-matrx/alchemy/declare/host` — HOST-BINDING types only (CONTRACT §2.1, §2.6).
691
+ * Never imported by providers (`/declare`, `/actions`, `/menu`, `/operate` see only
692
+ * the raw-free `ResolvedItem`). Types only: no runtime code.
693
+ */
694
+
695
+ interface ItemAnchor {
696
+ /** The host element the person clicked. */
697
+ element: unknown;
698
+ level?: string;
699
+ }
700
+ /** Raw item reads: consumed only by projection and the write handler that owns the target. */
701
+ interface InternalResolvedItem extends ResolvedItem {
702
+ readValues(signal: AbortSignal): Promise<Record<string, unknown>>;
703
+ }
704
+ /** A mounted instance of a declared surface. Structural handle, never a global name lookup. */
705
+ interface MountedSurface {
706
+ instanceId: string;
707
+ surfaceName: string;
708
+ isMounted(): boolean;
709
+ /** Same revision before and after a read, or capture refuses with "surface-changed". */
710
+ getRevision?(): string;
711
+ unsavedChanges?(): boolean;
712
+ readValues(signal: AbortSignal): Promise<Record<string, unknown>>;
713
+ readLive?(signal: AbortSignal): Promise<LiveValues>;
714
+ resolveItem?(anchor: ItemAnchor): InternalResolvedItem | null;
715
+ }
716
+
717
+ interface ProjectionSource {
718
+ surface: MountedSurface | null;
719
+ declaration: ResolvedSurfaceDeclaration | null;
720
+ item: InternalResolvedItem | null;
721
+ }
722
+ /** Keep declared, ordinary, exportable values; record every removal. */
723
+ declare function projectValues(raw: Record<string, unknown>, declared: readonly Pick<DeclaredValue, "name" | "sensitivity">[], pathPrefix?: string): {
724
+ values: Record<string, Json$1>;
725
+ omissions: ReadOmission[];
726
+ };
727
+ declare function createProjectedReads(source: ProjectionSource): ProjectedReads;
728
+
729
+ interface ClickTargetInput {
730
+ surface?: ClickTarget["surface"];
731
+ declaration?: ResolvedSurfaceDeclaration | null;
732
+ item?: ResolvedItem | null;
733
+ level?: string | null;
734
+ selection?: ClickTarget["selection"];
735
+ payloadKinds?: ClickTarget["payloadKinds"];
736
+ /** Defaults to the item's readOnly (or false). */
737
+ readOnly?: boolean;
738
+ /** Extra exclusions on top of the declaration's own. */
739
+ excludedActionIds?: Iterable<string>;
740
+ scopeChain?: readonly string[];
741
+ organizationId?: string | null;
742
+ auth?: Partial<ClickTarget["auth"]>;
743
+ /** Write targets the host can serve right now; defaults to every declared target for the screen/item. */
744
+ writable?: readonly WriteTarget[];
745
+ host?: unknown;
746
+ }
747
+ declare function createClickTarget(input?: ClickTargetInput): ClickTarget;
748
+
749
+ /**
750
+ * The one run path (T1e). Every layout runs an action through here, so the
751
+ * sign-in-then-resume rule, projection and failure capture live once.
752
+ */
753
+
754
+ interface RunActionOptions {
755
+ ports: AlchemyHostPorts;
756
+ /** Raw sources; projected before the action sees them. */
757
+ source?: ProjectionSource;
758
+ /** Stable fingerprint of the content, for sign-in-then-resume. */
759
+ contentFingerprint?: string;
760
+ signal?: AbortSignal;
761
+ }
762
+ type RunResult = {
763
+ status: "ran";
764
+ outcome: TransferOutcome | void;
765
+ } | {
766
+ status: "auth-pending";
767
+ } | {
768
+ status: "failed";
769
+ error: unknown;
770
+ };
771
+ declare function runAction(action: Action, target: ClickTarget, options: RunActionOptions): Promise<RunResult>;
772
+
773
+ export { ACTION_CATEGORIES, type Action, type ActionCategory, type ActionProvider, type ActionRegistry, type ActionRegistryOptions, type ActionRunContext, type ActionSection, type ClickTarget, type ClickTargetInput, DuplicateActionError, DuplicateProviderError, type Eligibility, type ProjectedReads, type ProjectionSource, type ReadOmission, type ResolveRestriction, type ResolvedAction, type RunActionOptions, type RunResult, UNIVERSAL_VERBS, type UniversalVerb, alwaysEligible, computeEligibility, createActionRegistry, createClickTarget, createProjectedReads, projectValues, runAction, staticActionProvider };