@ai-matrx/alchemy 0.8.18 → 0.8.20

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 (63) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/actions.d.cts +69 -0
  3. package/dist/actions.d.ts +69 -0
  4. package/dist/alchemy.svg +1 -1
  5. package/dist/core.cjs +11 -5
  6. package/dist/core.cjs.map +1 -1
  7. package/dist/core.d.cts +646 -19
  8. package/dist/core.d.ts +646 -19
  9. package/dist/core.js +11 -5
  10. package/dist/core.js.map +1 -1
  11. package/dist/index.cjs +11 -5
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +646 -19
  14. package/dist/index.d.ts +646 -19
  15. package/dist/index.js +11 -5
  16. package/dist/index.js.map +1 -1
  17. package/dist/menu.d.cts +69 -0
  18. package/dist/menu.d.ts +69 -0
  19. package/dist/operate-document.cjs.map +1 -1
  20. package/dist/operate-document.js.map +1 -1
  21. package/dist/operate-rich-html.cjs.map +1 -1
  22. package/dist/operate-rich-html.js.map +1 -1
  23. package/dist/operate-server.cjs.map +1 -1
  24. package/dist/operate-server.d.cts +7 -0
  25. package/dist/operate-server.d.ts +7 -0
  26. package/dist/operate-server.js.map +1 -1
  27. package/dist/operate-xlsx.cjs +1 -1
  28. package/dist/operate-xlsx.cjs.map +1 -1
  29. package/dist/operate-xlsx.js +1 -1
  30. package/dist/operate-xlsx.js.map +1 -1
  31. package/dist/operate.cjs +11 -5
  32. package/dist/operate.cjs.map +1 -1
  33. package/dist/operate.d.cts +646 -19
  34. package/dist/operate.d.ts +646 -19
  35. package/dist/operate.js +11 -5
  36. package/dist/operate.js.map +1 -1
  37. package/dist/portal.svg +1 -1
  38. package/dist/ports.cjs.map +1 -1
  39. package/dist/ports.d.cts +70 -1
  40. package/dist/ports.d.ts +70 -1
  41. package/dist/react-bar.cjs +1 -1
  42. package/dist/react-bar.cjs.map +1 -1
  43. package/dist/react-bar.js +1 -1
  44. package/dist/react-bar.js.map +1 -1
  45. package/dist/react-host.d.cts +69 -0
  46. package/dist/react-host.d.ts +69 -0
  47. package/dist/react-menu.cjs +1 -1
  48. package/dist/react-menu.cjs.map +1 -1
  49. package/dist/react-menu.js +1 -1
  50. package/dist/react-menu.js.map +1 -1
  51. package/dist/react-overflow.cjs.map +1 -1
  52. package/dist/react-overflow.d.cts +69 -0
  53. package/dist/react-overflow.d.ts +69 -0
  54. package/dist/react-overflow.js.map +1 -1
  55. package/dist/react-selection.cjs +1 -1
  56. package/dist/react-selection.cjs.map +1 -1
  57. package/dist/react-selection.js +1 -1
  58. package/dist/react-selection.js.map +1 -1
  59. package/dist/react-sheet.cjs +1 -1
  60. package/dist/react-sheet.cjs.map +1 -1
  61. package/dist/react-sheet.js +1 -1
  62. package/dist/react-sheet.js.map +1 -1
  63. package/package.json +3 -3
package/dist/core.d.cts CHANGED
@@ -2,8 +2,8 @@
2
2
  * Framework-neutral transfer contracts (Alchemy Operate, CONTRACT §2.3). All data crossing this boundary is JSON.
3
3
  * Moved from `@ai-matrx/kit/content-transfer` in ALC-16; the format-adapter contract lives in `./contract`.
4
4
  */
5
- type Json = null | boolean | number | string | Json[] | {
6
- [key: string]: Json;
5
+ type Json$1 = null | boolean | number | string | Json$1[] | {
6
+ [key: string]: Json$1;
7
7
  };
8
8
  type Payload =
9
9
  /** A text payload may name its language (code blocks): downloads take its extension, Markdown fences it. */
@@ -16,15 +16,15 @@ type Payload =
16
16
  text: string;
17
17
  } | {
18
18
  kind: "json";
19
- value: Json;
19
+ value: Json$1;
20
20
  } | {
21
21
  kind: "rows";
22
- rows: Record<string, Json>[];
22
+ rows: Record<string, Json$1>[];
23
23
  columns: Column[];
24
24
  } | {
25
25
  kind: "registered";
26
26
  format: string;
27
- value: Json;
27
+ value: Json$1;
28
28
  };
29
29
  type Column = {
30
30
  id: string;
@@ -38,9 +38,9 @@ type Column = {
38
38
  dataType?: string;
39
39
  order?: number;
40
40
  required?: boolean;
41
- defaultValue?: Json;
42
- validation?: Json;
43
- metadata?: Json;
41
+ defaultValue?: Json$1;
42
+ validation?: Json$1;
43
+ metadata?: Json$1;
44
44
  };
45
45
  };
46
46
  type ExportProjection = {
@@ -91,21 +91,21 @@ type Snapshot = RawSnapshot & {
91
91
  type QuerySnapshot = {
92
92
  sourceId: string;
93
93
  revision: string;
94
- query: Json;
94
+ query: Json$1;
95
95
  order: {
96
96
  field: string;
97
97
  direction: "asc" | "desc";
98
98
  }[];
99
99
  };
100
100
  type ExportPage = {
101
- rows: Record<string, Json>[];
101
+ rows: Record<string, Json$1>[];
102
102
  nextCursor: string | null;
103
103
  coverage: Coverage;
104
104
  consistencyToken: string | null;
105
105
  };
106
106
  type CapturedAllMatching = {
107
107
  query: QuerySnapshot;
108
- getRowId(row: Record<string, Json>): string;
108
+ getRowId(row: Record<string, Json$1>): string;
109
109
  page(request: {
110
110
  query: QuerySnapshot;
111
111
  cursor: string | null;
@@ -145,7 +145,7 @@ type Draft = {
145
145
  };
146
146
  type Transform = {
147
147
  id: string;
148
- apply(draft: Draft, options: Json, signal: AbortSignal): Promise<Draft>;
148
+ apply(draft: Draft, options: Json$1, signal: AbortSignal): Promise<Draft>;
149
149
  };
150
150
  type Artifact = {
151
151
  snapshotId: string;
@@ -282,6 +282,16 @@ interface AgentPayloadEnvironment {
282
282
  declare function fenceJsonBlock(json: string): string;
283
283
  /** Capture location at invocation; environment injection makes non-browser use explicit. */
284
284
  declare function buildAgentPayload(input: AgentPayloadInput, environment?: AgentPayloadEnvironment): string;
285
+ /**
286
+ * The envelope, plus where its JSON body sits: `body` is the exact JSON text written between the
287
+ * fence lines, starting at `bodyOffset` (so a span map of the body lands in the envelope at that
288
+ * offset — PP-06a).
289
+ */
290
+ declare function assembleAgentPayload(input: AgentPayloadInput, environment?: AgentPayloadEnvironment): {
291
+ text: string;
292
+ body: string;
293
+ bodyOffset: number;
294
+ };
285
295
 
286
296
  declare class ContentTransferError extends Error {
287
297
  readonly code: string;
@@ -306,9 +316,9 @@ type BoundAllMatching = {
306
316
  type NormalizeTransferJsonOptions = {
307
317
  omitUndefinedObjectProperties?: boolean;
308
318
  };
309
- declare function normalizeTransferJson(value: unknown, options?: NormalizeTransferJsonOptions): Json;
319
+ declare function normalizeTransferJson(value: unknown, options?: NormalizeTransferJsonOptions): Json$1;
310
320
  /** Column ids are identities, never an alternative data address. */
311
- declare function readTransferCell(row: Record<string, Json>, column: Column): Json | undefined;
321
+ declare function readTransferCell(row: Record<string, Json$1>, column: Column): Json$1 | undefined;
312
322
  declare function projectSnapshot(raw: RawSnapshot, projection: ExportProjection): Snapshot;
313
323
  declare function directSource(payload: Payload, options?: Partial<Pick<RawSnapshot, "id" | "sourceId" | "revision" | "label" | "coverage" | "unsavedChanges" | "sections" | "limits"> & {
314
324
  projection: ExportProjection;
@@ -347,13 +357,15 @@ declare function reduceJson(draft: Draft, options: {
347
357
  maxArrayItems?: number | null;
348
358
  }): Draft;
349
359
  declare function collectAllMatching(c: BoundAllMatching, signal: AbortSignal): Promise<{
350
- rows: Record<string, Json>[];
360
+ rows: Record<string, Json$1>[];
351
361
  coverage: RawSnapshot["coverage"];
352
362
  omissions: Omission[];
353
363
  error?: ContentTransferError;
354
364
  }>;
355
365
  /** Preserve artifact-byte identity even if a consumer mutates a returned view. */
356
366
  declare function sealTransferArtifact(artifact: Artifact): Artifact;
367
+ /** The columns a text serialization writes: visible, exportable, never secret or credential. */
368
+ declare function exportedColumns(columns: readonly Column[]): Column[];
357
369
  declare function serialize(draft: Draft, format: "plain" | "markdown" | "json" | "compact-json" | "csv" | "tsv" | "html", options?: {
358
370
  spreadsheetSafe?: boolean;
359
371
  filename?: string;
@@ -393,11 +405,522 @@ declare function createSurfaceTransferHandle(options: {
393
405
  /** Applies explicit preparation/source limits with exact omissions; token target is advisory. */
394
406
  declare function applyTransferLimits(draft: Draft, limits: Partial<TransferLimits>): Draft;
395
407
 
408
+ /** Unchanged from `ui_surface_value_type_chk`. */
409
+ type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
410
+ type Classification = "ordinary" | "secret" | "credential";
411
+ /** Persisted (CONTRACT §2.7). Projection enforces it before any value leaves. */
412
+ interface Sensitivity {
413
+ /** false = never leaves through Operate. Default true. */
414
+ exportable: boolean;
415
+ /** secret/credential are always excluded; no caller can relax this. Default "ordinary". */
416
+ classification: Classification;
417
+ /** Pre-checked in the preparation workspace. Default true. */
418
+ includedByDefault: boolean;
419
+ }
420
+ interface ValueGroup {
421
+ key: string;
422
+ label: string;
423
+ sortOrder: number;
424
+ description?: string;
425
+ }
426
+ /** The four runtime slots a capture fills (absorbs page-capture). */
427
+ type LiveSlot = "selection" | "on-demand" | "errors" | "requests";
428
+ interface DeclaredValue {
429
+ name: string;
430
+ label: string;
431
+ description: string;
432
+ valueType: ValueType;
433
+ /** Registered content-ir kind key (DB: kind_key). */
434
+ valueKind?: string;
435
+ alwaysAvailable: boolean;
436
+ typicalCharCount: number;
437
+ autoContext?: boolean;
438
+ group?: string;
439
+ sortOrder?: number;
440
+ sensitivity?: Partial<Sensitivity>;
441
+ /** Filled at capture time from a live slot instead of the scope. */
442
+ liveSlot?: LiveSlot;
443
+ /** "request" values drop out of "Only the data". Default "data". */
444
+ role?: "data" | "request";
445
+ }
446
+ /** Where a write lands. "stream" = applied live, op by op, into an editable text. */
447
+ type WriteMode = "draft" | "entity" | "ui" | "stream";
448
+ /** Who may apply WITHOUT a click. "queued" = filed for an organization's review. */
449
+ type ApplyPolicy = "manual" | "ask" | "auto" | "queued";
450
+ /** The WidgetHandle text op set, unchanged in meaning. */
451
+ type StreamOp = "replace" | "insert_before" | "insert_after" | "prepend" | "append" | "patch";
452
+ interface WriteTarget {
453
+ name: string;
454
+ label: string;
455
+ description: string;
456
+ valueType: ValueType;
457
+ valueKind?: string;
458
+ /** Read twin (evidence loop). Required when patchable or mode === "stream". */
459
+ updatesValue?: string;
460
+ patchable?: boolean;
461
+ approvalComparison?: "text-replacement";
462
+ mode: WriteMode;
463
+ /** Required iff mode === "stream". */
464
+ streamOps?: readonly StreamOp[];
465
+ /** Default "manual". Binding overrides may only tighten. */
466
+ applyPolicy?: ApplyPolicy;
467
+ destination?: boolean;
468
+ /** Present when the target writes into an item rather than the screen. */
469
+ itemType?: string;
470
+ group?: string;
471
+ sortOrder?: number;
472
+ }
473
+ /** One kind of thing inside a screen (a row, a message, a code block, a card). */
474
+ interface ItemType {
475
+ name: string;
476
+ label: string;
477
+ description: string;
478
+ /** Value names that together identify one item. */
479
+ identity: readonly string[];
480
+ levels?: readonly string[];
481
+ entityType?: string;
482
+ values: readonly DeclaredValue[];
483
+ writeTargets?: readonly WriteTarget[];
484
+ }
485
+ type Readiness = "verified" | "partial" | "stub";
486
+ /** `ui_surface_execution_mode_check`, unchanged. */
487
+ type ExecutionMode = "python-stream" | "nextjs-stream" | "browser-realtime" | "local-runtime";
488
+ interface ConfigNamespaceDeclaration {
489
+ namespace: string;
490
+ label: string;
491
+ description: string;
492
+ }
493
+ interface EvidenceSourceDeclaration {
494
+ kind: "processed_document";
495
+ idValue: string;
496
+ fileIdValue?: string;
497
+ labelValue?: string;
498
+ representation?: "clean" | "raw";
499
+ }
500
+ /**
501
+ * One screen's declaration. Owning packages add fields (agent roles, client
502
+ * tools) as EXTENSION SLOTS — by interface extension or declaration merging —
503
+ * and register a `DeclarationExtension` so validation and the sync cover them.
504
+ */
505
+ interface SurfaceDeclaration {
506
+ surfaceName: string;
507
+ /** REQUIRED. `ui_surface.client_name`; must equal the surfaceName prefix. */
508
+ client: string;
509
+ /** REQUIRED. `ui_surface.execution_mode`. */
510
+ executionMode: ExecutionMode;
511
+ /** REQUIRED. `ui_surface.description` ("" only where the live row is empty). */
512
+ description: string;
513
+ label: string;
514
+ readiness: Readiness;
515
+ readinessNote?: string;
516
+ urlPattern?: string;
517
+ inheritsFrom?: string;
518
+ intro?: string;
519
+ overlayId?: string;
520
+ values: readonly DeclaredValue[];
521
+ groups?: readonly ValueGroup[];
522
+ writeTargets?: readonly WriteTarget[];
523
+ itemTypes?: readonly ItemType[];
524
+ liveSlots?: readonly LiveSlot[];
525
+ configNamespaces?: readonly ConfigNamespaceDeclaration[];
526
+ evidenceSources?: readonly EvidenceSourceDeclaration[];
527
+ skipBaselineValues?: boolean;
528
+ }
529
+ type ValueProvenance = {
530
+ kind: "own";
531
+ } | {
532
+ kind: "inherited";
533
+ from: string;
534
+ } | {
535
+ kind: "baseline";
536
+ };
537
+ type ResolvedValue = DeclaredValue & {
538
+ provenance: ValueProvenance;
539
+ groupKey: string;
540
+ /** Defaults filled. */
541
+ resolvedSensitivity: Sensitivity;
542
+ };
543
+ interface ResolvedSurfaceDeclaration extends SurfaceDeclaration {
544
+ contentHash: string;
545
+ values: readonly ResolvedValue[];
546
+ groups: readonly ValueGroup[];
547
+ }
548
+ /**
549
+ * ITEM KEYS. A value or write target is identified by (surfaceName, itemType ?? "", name).
550
+ * The qualified key "<itemType>.<name>" names an item value; a bare name is a screen value.
551
+ */
552
+ type QualifiedValueKey = string;
553
+ /** RAW-FREE. What leaves the package about a clicked item. */
554
+ interface ResolvedItem {
555
+ itemType: string;
556
+ identity: Record<string, string>;
557
+ level?: string;
558
+ readOnly: boolean;
559
+ }
560
+
561
+ /**
562
+ * The click target (CONTRACT §2.4) in its own module: `/ports` references it,
563
+ * and `/ports` must stay free of every other package (types only).
564
+ */
565
+
566
+ /** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */
567
+ type PayloadKind = "text" | "markdown" | "json" | "rows" | "registered";
568
+ /** What eligibility is computed from: everything declared at the click target. */
569
+ interface ClickTarget {
570
+ surface: {
571
+ instanceId: string;
572
+ surfaceName: string;
573
+ } | null;
574
+ /** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */
575
+ declaration: ResolvedSurfaceDeclaration | null;
576
+ /** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */
577
+ valueKeys: ReadonlySet<QualifiedValueKey>;
578
+ /** Scope chain, most general first: ["global", "<domain>", "<surfaceName>"]. */
579
+ scopeChain: readonly string[];
580
+ /** The surface's explicit exclusions (the one override). */
581
+ excludedActionIds: ReadonlySet<string>;
582
+ item: ResolvedItem | null;
583
+ level: string | null;
584
+ /** Offsets + the live range handle, editable or not. */
585
+ selection: {
586
+ text: string;
587
+ type: "editable" | "non-editable";
588
+ start: number;
589
+ end: number;
590
+ /** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */
591
+ handle?: unknown;
592
+ } | null;
593
+ /** Payload kinds a capture of this target would produce (from declared value types/kinds). */
594
+ payloadKinds: readonly PayloadKind[];
595
+ /** Declared targets writable on this target right now. */
596
+ writable: readonly WriteTarget[];
597
+ readOnly: boolean;
598
+ /** True when any declared value here is secret/credential or non-exportable. */
599
+ restricted: boolean;
600
+ organizationId: string | null;
601
+ auth: {
602
+ authenticated: boolean;
603
+ admin: boolean;
604
+ creator: boolean;
605
+ };
606
+ /**
607
+ * Host-owned context a provider reads to act (the rich-document source, the
608
+ * editable field's callbacks). Opaque to the package; never crosses into the
609
+ * model. Providers that need it narrow it themselves.
610
+ */
611
+ host?: unknown;
612
+ }
613
+
614
+ /**
615
+ * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).
616
+ *
617
+ * The app binds these ONCE, at its boundary (where the Alchemy host is
618
+ * mounted). The package never imports the host.
619
+ *
620
+ * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────
621
+ *
622
+ * This entry declares only the ports the current waves use:
623
+ *
624
+ * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)
625
+ * ALC-14 `identity`, `persistence`
626
+ * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`
627
+ *
628
+ * ALC-16 `transport`, `assets`, `server`
629
+ * PP-01a `window` (host-rendered windows for a live preparation session)
630
+ * PP-13a `transferKnobs` (organization default recipes)
631
+ *
632
+ * ALC-17 adds approvals/bodies/receipts.
633
+ *
634
+ * ── Absent-port semantics ───────────────────────────────────────────────────
635
+ *
636
+ * Every port except `diagnostics` is OPTIONAL. A port the host does not bind
637
+ * means that capability is ABSENT: the actions, formats or writes that need
638
+ * it are not offered at all. A missing port is never stubbed, never faked
639
+ * with a default that pretends to work, and never shown as a dead control.
640
+ *
641
+ * kinds absent → kind validation is absent (no value can be checked,
642
+ * so a kind-bearing write is not offered)
643
+ * diagnostics REQUIRED → nothing fails silently; contract breaks land here
644
+ * identity absent → organization-scoped declarations are absent
645
+ * persistence absent → per-person settings are absent (organization and
646
+ * platform defaults apply)
647
+ * store absent → actions that read named host state or run named
648
+ * host commands are absent
649
+ * notify absent → outcomes are not announced (diagnostics still captures)
650
+ * overlay absent → actions that open an overlay or navigate are absent
651
+ * icons absent → rows render label-only (a key is never shown as text)
652
+ * knobs absent → the platform default layout/density applies
653
+ * i18n absent → labels as written
654
+ * auth absent → a signed-out person does not see requiresAuth actions
655
+ * (sign-in-then-resume is the capability that is missing)
656
+ * dialogs absent → actions that need a host dialog are absent
657
+ * window absent → the preparation workspace window is absent (its
658
+ * Prepare entry is not offered; it never falls back
659
+ * to an overlay carrying the session as data)
660
+ * transferKnobs absent → no organization default recipe; a person's own
661
+ * default (persistence) or the package's Full applies
662
+ *
663
+ * Types only: this entry has no runtime code.
664
+ */
665
+ /** JSON as it crosses a port. */
666
+ type Json = null | boolean | number | string | Json[] | {
667
+ [key: string]: Json;
668
+ };
669
+ /**
670
+ * content-ir's kind validator, injected so Alchemy validates without
671
+ * importing the app or the kind catalog. The host builds it from
672
+ * `@ai-matrx/content-ir`'s `createKindValidator` over its own
673
+ * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never
674
+ * declares it).
675
+ *
676
+ * `unverifiable: true` marks a verdict where the kind could not be checked
677
+ * (not registered, no schema, uncompilable schema, catalog unreachable) — a
678
+ * skip, which is never a pass.
679
+ */
680
+ interface KindValidatorPort {
681
+ validate(kindKey: string, value: unknown, signal: AbortSignal): Promise<{
682
+ ok: true;
683
+ } | {
684
+ ok: false;
685
+ sentence: string;
686
+ remedy: string;
687
+ unverifiable?: true;
688
+ }>;
689
+ }
690
+
691
+ /** Where contract breaks are captured. Required: nothing fails silently. */
692
+ interface DiagnosticsPort {
693
+ capture(error: unknown, context: {
694
+ area: "declare" | "operate" | "write" | "actions" | "menu";
695
+ detail: Json;
696
+ }): void;
697
+ }
698
+ /** Who is using the app right now, and in which organization. */
699
+ interface IdentityPort {
700
+ /** `null` while the host has no resolved identity yet. */
701
+ current(): {
702
+ userId: string;
703
+ organizationId: string | null;
704
+ isAuthenticated: boolean;
705
+ isAdmin: boolean;
706
+ } | null;
707
+ /** Called whenever `current()` may answer differently; returns unsubscribe. */
708
+ onChange(listener: () => void): () => void;
709
+ }
710
+ /** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */
711
+ interface PersistencePort {
712
+ readSetting<T extends Json>(key: string): Promise<T | null>;
713
+ writeSetting(key: string, value: Json): Promise<void>;
714
+ }
715
+ /** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */
716
+ interface StorePort {
717
+ /** A named, typed read the host registered ("activeConversationId", "organizationName"). */
718
+ read<T extends Json>(key: string): T | null;
719
+ /** A named command the host registered ("tasks.setPendingSource"); resolves with its result. */
720
+ command(name: string, input: Json): Promise<Json | void>;
721
+ subscribe(key: string, listener: () => void): () => void;
722
+ }
723
+ interface NotifyPort {
724
+ success(sentence: string): void;
725
+ info(sentence: string): void;
726
+ /** Every failure names its remedy. */
727
+ error(sentence: string, remedy: string): void;
728
+ }
729
+ /** Overlays are dispatched through the host; the menu never renders a modal. */
730
+ interface OverlayHostPort {
731
+ open(overlayId: string, data: Json, instanceId?: string): void;
732
+ close(overlayId: string, instanceId?: string): void;
733
+ navigate(href: string): void;
734
+ }
735
+ /** Icon key → the host's icon component. The model only ever holds keys. */
736
+ interface IconResolverPort {
737
+ resolve(key: string): unknown | null;
738
+ }
739
+ type ContextArrangementKnob = "classic" | "tiered" | "command";
740
+ type DensityKnob = "comfortable" | "compact";
741
+ /** ORGANIZATION defaults only; every method takes the organization. */
742
+ interface MenuKnobsPort {
743
+ /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */
744
+ placementLabel(placement: string, organizationId: string | null): string | null;
745
+ /** Replaces the constant DEFAULT_PLACEMENT_MODE. */
746
+ placementMode(placement: string, organizationId: string | null): "show" | "hide";
747
+ /** The organization's default layout and density (platform default when null: command / comfortable). */
748
+ layout(organizationId: string | null): {
749
+ context: ContextArrangementKnob;
750
+ density: DensityKnob;
751
+ } | null;
752
+ }
753
+ /** Label translation. Absent = labels as written. */
754
+ interface I18nPort {
755
+ t(key: string, fallback: string): string;
756
+ }
757
+ /** Sign in, then resume the action the person picked. */
758
+ interface AuthResumePort {
759
+ isSignedIn(): boolean;
760
+ /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */
761
+ signInThenResume(pending: {
762
+ actionId: string;
763
+ contentFingerprint: string;
764
+ }): void;
765
+ takePending(contentFingerprint: string): {
766
+ actionId: string;
767
+ } | null;
768
+ }
769
+ /** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */
770
+ interface HostDialogsPort {
771
+ requestDelete?(target: ClickTarget): void;
772
+ requestConvert?(target: ClickTarget): void;
773
+ requestEditHistory?(target: ClickTarget): void;
774
+ requestFullPrint?(target: ClickTarget): void;
775
+ requestSaveTable?(table: {
776
+ headers: string[];
777
+ rows: string[][];
778
+ }): void;
779
+ requestFlashcard?(answer: string): void;
780
+ }
781
+ /** The ports a host binds. Optional = absent capability when unbound (see above). */
782
+ interface AlchemyHostPorts {
783
+ kinds?: KindValidatorPort;
784
+ /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */
785
+ diagnostics: DiagnosticsPort;
786
+ identity?: IdentityPort;
787
+ persistence?: PersistencePort;
788
+ store?: StorePort;
789
+ notify?: NotifyPort;
790
+ overlay?: OverlayHostPort;
791
+ icons?: IconResolverPort;
792
+ /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */
793
+ knobs?: MenuKnobsPort;
794
+ i18n?: I18nPort;
795
+ auth?: AuthResumePort;
796
+ dialogs?: HostDialogsPort;
797
+ /** Clipboard and download for this runtime (the browser default ships in /operate). */
798
+ transport?: TransportPort;
799
+ /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */
800
+ assets?: AssetResolverPort;
801
+ /** Present when the host can reach aidream server operations; absent = server formats are not offered. */
802
+ server?: ServerPort;
803
+ /** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */
804
+ window?: WindowPort;
805
+ /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */
806
+ transferKnobs?: TransferKnobsPort;
807
+ }
808
+ /**
809
+ * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation
810
+ * session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace
811
+ * never travels as overlay data; the package hands the host a render function and the host draws it
812
+ * inside its own window system, with the session still live in the closure.
813
+ */
814
+ interface WindowSize {
815
+ width: number;
816
+ height: number;
817
+ }
818
+ interface WindowOpenOptions {
819
+ /** The window's title, as a person reads it. */
820
+ title: string;
821
+ /** Preferred size; `"full"` = near-full-screen. The host clamps to the viewport. */
822
+ size?: WindowSize | "full";
823
+ /**
824
+ * Stable identity. Opening an id that is already open focuses that window and draws the new
825
+ * render in it instead of opening a second one.
826
+ */
827
+ instanceId?: string;
828
+ /** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */
829
+ onClose?: () => void;
830
+ }
831
+ interface WindowHandle {
832
+ readonly instanceId: string;
833
+ /** Close the window; idempotent. */
834
+ close(): void;
835
+ /** Bring the window to the front; a no-op once closed. */
836
+ focus(): void;
837
+ isOpen(): boolean;
838
+ }
839
+ interface WindowPort {
840
+ /**
841
+ * What the host really draws. `"window"`: non-blocking, movable, resizable. `"dialog"`: a blocking
842
+ * full-screen dialog — a surface on a dialog host never says "window" and never offers a second one.
843
+ */
844
+ readonly presentation: "window" | "dialog";
845
+ /** True only when several windows can be open and usable at once. False = never offer "open another". */
846
+ readonly multiInstance: boolean;
847
+ /**
848
+ * Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in
849
+ * a React host) and is called by the host while the window is open.
850
+ */
851
+ open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;
852
+ }
853
+ /**
854
+ * ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform
855
+ * default an admin sets, overridden by the organization. Never per-person — a person's own choice
856
+ * lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).
857
+ */
858
+ interface TransferKnobsPort {
859
+ /**
860
+ * The organization's default recipe for one source kind (the platform default when the
861
+ * organization set none), or null when neither names one. A read that fails rejects; it never
862
+ * answers null for "could not ask".
863
+ */
864
+ defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;
865
+ }
866
+ /** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */
867
+ interface TransportArtifact {
868
+ format: string;
869
+ plainText: string;
870
+ html?: string;
871
+ file?: {
872
+ filename: string;
873
+ mime: string;
874
+ bytes: Uint8Array;
875
+ };
876
+ }
877
+ type TransportOutcome = {
878
+ status: "success";
879
+ delivered: "clipboard" | "download-started";
880
+ mimeTypes: string[];
881
+ } | {
882
+ status: "degraded";
883
+ delivered: "plain-text";
884
+ reason: string;
885
+ } | {
886
+ status: "cancelled";
887
+ } | {
888
+ status: "error";
889
+ code: string;
890
+ message: string;
891
+ retryable: boolean;
892
+ };
893
+ /** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */
894
+ interface TransportPort {
895
+ copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
896
+ download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
897
+ }
396
898
  interface AssetResolverPort {
397
899
  resolve(url: string, signal: AbortSignal): Promise<{
398
900
  url: string;
399
901
  } | null>;
400
902
  }
903
+ type ServerResult = {
904
+ status: "bytes";
905
+ filename: string;
906
+ mime: string;
907
+ bytes: Uint8Array;
908
+ notices: readonly {
909
+ code: string;
910
+ sentence: string;
911
+ remedy: string;
912
+ }[];
913
+ } | {
914
+ status: "receipt";
915
+ receipt: {
916
+ status: string;
917
+ sentence: string;
918
+ remedy?: string;
919
+ };
920
+ };
921
+ interface ServerPort {
922
+ call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;
923
+ }
401
924
 
402
925
  /**
403
926
  * Operate — the format and delivery contract (Matrx Alchemy CONTRACT §2.3, ALC-16).
@@ -432,7 +955,7 @@ type TransferOperation = {
432
955
  } | {
433
956
  kind: "transform";
434
957
  transform: string;
435
- options?: Json;
958
+ options?: Json$1;
436
959
  } | {
437
960
  kind: "prepare-for-ai";
438
961
  instruction?: string;
@@ -477,7 +1000,7 @@ interface ServerOperation {
477
1000
  statement: string;
478
1001
  supports(payload: Payload): boolean;
479
1002
  /** Projected draft only; sensitivity already applied. */
480
- toInput(draft: Draft): Json;
1003
+ toInput(draft: Draft): Json$1;
481
1004
  }
482
1005
  /** Delivery results: `copy` and `download` only ever deliver, degrade, cancel or fail (CONTRACT R16). */
483
1006
  type DeliveryOutcome = Extract<TransferOutcome, {
@@ -559,7 +1082,7 @@ interface PreparationSession {
559
1082
  readonly snapshot: Snapshot;
560
1083
  readonly draft: Draft;
561
1084
  /** Deterministic transform, AI preparation or any registered transform — bumps draft.revision. */
562
- transform(id: string, options: Json, signal: AbortSignal): Promise<Draft>;
1085
+ transform(id: string, options: Json$1, signal: AbortSignal): Promise<Draft>;
563
1086
  /** A manual edit. */
564
1087
  edit(payload: Payload): Draft;
565
1088
  /** Keep only these sections (ids from the snapshot). */
@@ -594,4 +1117,108 @@ declare function createDelivery(ports?: {
594
1117
  };
595
1118
  }): Delivery;
596
1119
 
597
- export { type AgentPayloadEnvironment, type AgentPayloadInput, type AiPreparation, type Artifact, type BoundAllMatching, type BuiltInFormatId, type CapturedAllMatching, type CapturedSource, type Column, ContentTransferError, type Coverage, type Delivery, type DeliveryOutcome, type Draft, type ExportPage, type ExportProjection, FORMAT_ADAPTERS, type FormatAdapter, type FormatEngine, type FormatOptions, type Json, type NormalizeTransferJsonOptions, type Notice, type Omission, type PageAction, type Payload, type PreparationOptions, type PreparationSession, type QuerySnapshot, type RawSnapshot, type Receipt, type SealedArtifact, type Section, type ServerOperation, type Snapshot, type Source, type SurfaceHandle, type TransferLimits, type TransferOperation, type TransferOutcome, type TransferPreferenceOverrides, type TransferPreferences, type TransferScope, type TransferSurfaceDeclaration, type TransferTarget, type Transform, type Transport, type WriteTargetRef, applySectionSelection, applyTransferLimits, buildAgentPayload, capture, collectAllMatching, createBrowserTransport, createDelivery, createDraft, createSurfaceTransferHandle, directSource, editDraft, eligibleFormats, extensionForLanguage, fenceJsonBlock, formatAdapter, isSealed, normalizeTransferJson, openPreparation, projectSnapshot, readTransferCell, reduceJson, refreshDraft, resetDraft, resolveTransferPreferences, resolveTransferRegistry, sealArtifact, sealDraft, sealTransferArtifact, serialize, sourceChanged, transformRows, undoDraft };
1120
+ /**
1121
+ * Serializer span maps (Preparation parity PP-06a): every node of the draft payload → the exact
1122
+ * span of the SEALED text it became.
1123
+ *
1124
+ * A span map is computed from the sealed artifact's own `plainText` (the bytes the preview shows,
1125
+ * the clipboard receives and the download holds), never from a second rendering of the draft — so
1126
+ * a span can never describe text that is not there. Offsets are UTF-16 code units into
1127
+ * `artifact.plainText`, `[start, end)`, exactly what `String.prototype.slice` and a DOM text range
1128
+ * take.
1129
+ *
1130
+ * Paths are the JSON Pointers the rest of Operate already uses for omissions:
1131
+ * "" the whole payload (a text or Markdown payload is one node)
1132
+ * "/messages/0/content" a node inside a JSON payload
1133
+ * "/rows" the row array (JSON formats of a rows payload)
1134
+ * "/rows/3" one row (its whole line in CSV / TSV / Markdown / plain)
1135
+ * "/rows/3/name" one cell (the column's own path under the row)
1136
+ * "/columns/name" one header cell (CSV / TSV / Markdown / plain), keyed by column id
1137
+ *
1138
+ * Spans nest (a row contains its cells, an object its members) and never partially overlap.
1139
+ * PP-06b–d (the structure trees) and PP-07 (the removed-text diff) highlight through this map.
1140
+ */
1141
+
1142
+ interface OutputSpan {
1143
+ /** JSON Pointer of the node in the draft payload (see the module header). */
1144
+ path: string;
1145
+ /** UTF-16 offset of the node's first character in `artifact.plainText`. */
1146
+ start: number;
1147
+ /** UTF-16 offset one past the node's last character. */
1148
+ end: number;
1149
+ }
1150
+ type SpanMap = readonly OutputSpan[];
1151
+ /**
1152
+ * Spans of every value in a JSON text, found by reading the text itself (whatever writer laid it
1153
+ * out: pretty, compact, minified, escaped). Pre-order: a container before its members.
1154
+ * Throws when the text is not exactly one JSON value.
1155
+ */
1156
+ declare function jsonTextSpans(text: string, basePath?: string, offset?: number): OutputSpan[];
1157
+ /**
1158
+ * The span map of a sealed artifact built from `draft` (same snapshot, same draft revision).
1159
+ * Covers the text formats the preparation workspace previews: plain, markdown, json,
1160
+ * compact-json, csv, tsv, and html's plain text. Binary and document formats have no text to map
1161
+ * and are refused by name.
1162
+ */
1163
+ declare function spanMap(draft: Draft, artifact: Artifact): SpanMap;
1164
+ /** The same spans at `offset` characters later — a map carried inside a larger sealed text. */
1165
+ declare function offsetSpans(spans: SpanMap, offset: number): OutputSpan[];
1166
+ /**
1167
+ * The AI envelope with the span map of its data: every node of `input.data` → its span in the
1168
+ * envelope text. The body is read where the envelope wrote it, so the spans stay exact even where
1169
+ * the envelope escapes `<`, `>` and `&`; for data without those characters they are the bare JSON's
1170
+ * spans moved by exactly the envelope header.
1171
+ */
1172
+ declare function agentPayloadWithSpans(input: AgentPayloadInput, environment?: AgentPayloadEnvironment): {
1173
+ text: string;
1174
+ spans: SpanMap;
1175
+ };
1176
+
1177
+ /**
1178
+ * Default preparation recipes (Preparation parity PP-13a): which recipe a preparation workspace
1179
+ * opens with for one source kind, layered exactly like every setting on the platform —
1180
+ *
1181
+ * the person's own default (PersistencePort, setting "alchemy.transfer.default_recipe")
1182
+ * → the organization default (TransferKnobsPort — the Feature Knob ladder: organization → platform)
1183
+ * → built in (null recipe: the workspace opens on Full, nothing trimmed)
1184
+ *
1185
+ * The recipe KEY is the declaration / source-kind id (`"cx.conversation"`, `"data.table-rows"`), the
1186
+ * same string in every host, so a recipe saved on the dashboard is the recipe the frontend opens.
1187
+ * Both stores hold one map, source-kind id → recipe; this module is the only reader and writer of the
1188
+ * person's map, so the merge rules live once.
1189
+ *
1190
+ * Nothing fails silently: a rung that cannot be read or holds a malformed value is skipped with a
1191
+ * notice (and captured in diagnostics), and the answer says which rung it came from. A write never
1192
+ * merges into a map it failed to read.
1193
+ */
1194
+
1195
+ /** The person's setting (PersistencePort) — one map of source-kind id → recipe. */
1196
+ declare const TRANSFER_DEFAULT_RECIPE_SETTING = "alchemy.transfer.default_recipe";
1197
+ type RecipeOrigin = "person" | "organization" | "built-in";
1198
+ interface ResolvedRecipe {
1199
+ /** The recipe to open with; null = built in (Full, nothing trimmed). */
1200
+ recipe: {
1201
+ [key: string]: Json;
1202
+ } | null;
1203
+ from: RecipeOrigin;
1204
+ /** Rungs that were skipped, each with what to do about it. */
1205
+ notices: {
1206
+ sentence: string;
1207
+ remedy: string;
1208
+ }[];
1209
+ }
1210
+ /** The recipe key for a source kind: the declaration / source-kind id itself, validated. */
1211
+ declare function recipeKey(sourceKind: string): string;
1212
+ type RecipePorts = Pick<AlchemyHostPorts, "persistence" | "transferKnobs" | "diagnostics">;
1213
+ /** Which recipe a workspace for `sourceKind` opens with, and where it came from. */
1214
+ declare function resolveDefaultRecipe(ports: RecipePorts, sourceKind: string, organizationId: string | null): Promise<ResolvedRecipe>;
1215
+ /**
1216
+ * Set (recipe) or clear (null) the person's own default for one source kind, keeping every other
1217
+ * source kind's default. Throws a sentence when nothing was saved; clearing the last entry clears
1218
+ * the setting itself, so the person inherits the organization default again.
1219
+ */
1220
+ declare function setPersonDefaultRecipe(ports: Pick<AlchemyHostPorts, "persistence">, sourceKind: string, recipe: {
1221
+ [key: string]: Json;
1222
+ } | null): Promise<void>;
1223
+
1224
+ export { type AgentPayloadEnvironment, type AgentPayloadInput, type AiPreparation, type Artifact, type BoundAllMatching, type BuiltInFormatId, type CapturedAllMatching, type CapturedSource, type Column, ContentTransferError, type Coverage, type Delivery, type DeliveryOutcome, type Draft, type ExportPage, type ExportProjection, FORMAT_ADAPTERS, type FormatAdapter, type FormatEngine, type FormatOptions, type Json$1 as Json, type NormalizeTransferJsonOptions, type Notice, type Omission, type OutputSpan, type PageAction, type Payload, type PreparationOptions, type PreparationSession, type QuerySnapshot, type RawSnapshot, type Receipt, type RecipeOrigin, type ResolvedRecipe, type SealedArtifact, type Section, type ServerOperation, type Snapshot, type Source, type SpanMap, type SurfaceHandle, TRANSFER_DEFAULT_RECIPE_SETTING, type TransferLimits, type TransferOperation, type TransferOutcome, type TransferPreferenceOverrides, type TransferPreferences, type TransferScope, type TransferSurfaceDeclaration, type TransferTarget, type Transform, type Transport, type WriteTargetRef, agentPayloadWithSpans, applySectionSelection, applyTransferLimits, assembleAgentPayload, buildAgentPayload, capture, collectAllMatching, createBrowserTransport, createDelivery, createDraft, createSurfaceTransferHandle, directSource, editDraft, eligibleFormats, exportedColumns, extensionForLanguage, fenceJsonBlock, formatAdapter, isSealed, jsonTextSpans, normalizeTransferJson, offsetSpans, openPreparation, projectSnapshot, readTransferCell, recipeKey, reduceJson, refreshDraft, resetDraft, resolveDefaultRecipe, resolveTransferPreferences, resolveTransferRegistry, sealArtifact, sealDraft, sealTransferArtifact, serialize, setPersonDefaultRecipe, sourceChanged, spanMap, transformRows, undoDraft };