@ai-matrx/alchemy 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +2 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.cjs.map +1 -1
- package/dist/actions.d.cts +192 -6
- package/dist/actions.d.ts +192 -6
- package/dist/actions.js +1 -1
- package/dist/actions.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +159 -16
- package/dist/core.d.ts +159 -16
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -16
- package/dist/index.d.ts +159 -16
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/menu.d.cts +169 -3
- package/dist/menu.d.ts +169 -3
- package/dist/operate-server.d.cts +5 -1
- package/dist/operate-server.d.ts +5 -1
- package/dist/operate.cjs +1 -1
- package/dist/operate.cjs.map +1 -1
- package/dist/operate.d.cts +159 -16
- package/dist/operate.d.ts +159 -16
- package/dist/operate.js +2 -2
- package/dist/operate.js.map +1 -1
- package/dist/ports.cjs.map +1 -1
- package/dist/ports.d.cts +170 -4
- package/dist/ports.d.ts +170 -4
- package/dist/react-host.d.cts +169 -3
- package/dist/react-host.d.ts +169 -3
- package/dist/react-overflow.d.cts +169 -3
- package/dist/react-overflow.d.ts +169 -3
- package/dist/surface.cjs +2 -0
- package/dist/surface.cjs.map +1 -0
- package/dist/surface.d.cts +1045 -0
- package/dist/surface.d.ts +1045 -0
- package/dist/surface.js +2 -0
- package/dist/surface.js.map +1 -0
- package/package.json +13 -3
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matrx Alchemy — Declare types (CONTRACT §2.1).
|
|
3
|
+
*
|
|
4
|
+
* A declaration is plain JSON data (chair ruling N2): no functions, no classes,
|
|
5
|
+
* no React. Runtime behaviour lives in `MountedSurface` (`/declare/host`) and in
|
|
6
|
+
* handler registrations, never here. Zero React, zero `@ai-matrx/agents`.
|
|
7
|
+
*/
|
|
8
|
+
type Json$1 = null | boolean | number | string | Json$1[] | {
|
|
9
|
+
[key: string]: Json$1;
|
|
10
|
+
};
|
|
11
|
+
/** Unchanged from `ui_surface_value_type_chk`. */
|
|
12
|
+
type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
|
|
13
|
+
type Classification = "ordinary" | "secret" | "credential";
|
|
14
|
+
/** Persisted (CONTRACT §2.7). Projection enforces it before any value leaves. */
|
|
15
|
+
interface Sensitivity {
|
|
16
|
+
/** false = never leaves through Operate. Default true. */
|
|
17
|
+
exportable: boolean;
|
|
18
|
+
/** secret/credential are always excluded; no caller can relax this. Default "ordinary". */
|
|
19
|
+
classification: Classification;
|
|
20
|
+
/** Pre-checked in the preparation workspace. Default true. */
|
|
21
|
+
includedByDefault: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface ValueGroup {
|
|
24
|
+
key: string;
|
|
25
|
+
label: string;
|
|
26
|
+
sortOrder: number;
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
/** The four runtime slots a capture fills (absorbs page-capture). */
|
|
30
|
+
type LiveSlot = "selection" | "on-demand" | "errors" | "requests";
|
|
31
|
+
interface DeclaredValue {
|
|
32
|
+
name: string;
|
|
33
|
+
label: string;
|
|
34
|
+
description: string;
|
|
35
|
+
valueType: ValueType;
|
|
36
|
+
/** Registered content-ir kind key (DB: kind_key). */
|
|
37
|
+
valueKind?: string;
|
|
38
|
+
alwaysAvailable: boolean;
|
|
39
|
+
typicalCharCount: number;
|
|
40
|
+
autoContext?: boolean;
|
|
41
|
+
group?: string;
|
|
42
|
+
sortOrder?: number;
|
|
43
|
+
sensitivity?: Partial<Sensitivity>;
|
|
44
|
+
/** Filled at capture time from a live slot instead of the scope. */
|
|
45
|
+
liveSlot?: LiveSlot;
|
|
46
|
+
/** "request" values drop out of "Only the data". Default "data". */
|
|
47
|
+
role?: "data" | "request";
|
|
48
|
+
}
|
|
49
|
+
/** Where a write lands. "stream" = applied live, op by op, into an editable text. */
|
|
50
|
+
type WriteMode = "draft" | "entity" | "ui" | "stream";
|
|
51
|
+
/** Who may apply WITHOUT a click. "queued" = filed for an organization's review. */
|
|
52
|
+
type ApplyPolicy = "manual" | "ask" | "auto" | "queued";
|
|
53
|
+
/** The WidgetHandle text op set, unchanged in meaning. */
|
|
54
|
+
type StreamOp = "replace" | "insert_before" | "insert_after" | "prepend" | "append" | "patch";
|
|
55
|
+
interface WriteTarget {
|
|
56
|
+
name: string;
|
|
57
|
+
label: string;
|
|
58
|
+
description: string;
|
|
59
|
+
valueType: ValueType;
|
|
60
|
+
valueKind?: string;
|
|
61
|
+
/** Read twin (evidence loop). Required when patchable or mode === "stream". */
|
|
62
|
+
updatesValue?: string;
|
|
63
|
+
patchable?: boolean;
|
|
64
|
+
approvalComparison?: "text-replacement";
|
|
65
|
+
mode: WriteMode;
|
|
66
|
+
/** Required iff mode === "stream". */
|
|
67
|
+
streamOps?: readonly StreamOp[];
|
|
68
|
+
/** Default "manual". Binding overrides may only tighten. */
|
|
69
|
+
applyPolicy?: ApplyPolicy;
|
|
70
|
+
destination?: boolean;
|
|
71
|
+
/** Present when the target writes into an item rather than the screen. */
|
|
72
|
+
itemType?: string;
|
|
73
|
+
group?: string;
|
|
74
|
+
sortOrder?: number;
|
|
75
|
+
}
|
|
76
|
+
/** One kind of thing inside a screen (a row, a message, a code block, a card). */
|
|
77
|
+
interface ItemType {
|
|
78
|
+
name: string;
|
|
79
|
+
label: string;
|
|
80
|
+
description: string;
|
|
81
|
+
/** Value names that together identify one item. */
|
|
82
|
+
identity: readonly string[];
|
|
83
|
+
levels?: readonly string[];
|
|
84
|
+
entityType?: string;
|
|
85
|
+
values: readonly DeclaredValue[];
|
|
86
|
+
writeTargets?: readonly WriteTarget[];
|
|
87
|
+
}
|
|
88
|
+
type Readiness = "verified" | "partial" | "stub";
|
|
89
|
+
/** `ui_surface_execution_mode_check`, unchanged. */
|
|
90
|
+
type ExecutionMode = "python-stream" | "nextjs-stream" | "browser-realtime" | "local-runtime";
|
|
91
|
+
interface ConfigNamespaceDeclaration {
|
|
92
|
+
namespace: string;
|
|
93
|
+
label: string;
|
|
94
|
+
description: string;
|
|
95
|
+
}
|
|
96
|
+
interface EvidenceSourceDeclaration {
|
|
97
|
+
kind: "processed_document";
|
|
98
|
+
idValue: string;
|
|
99
|
+
fileIdValue?: string;
|
|
100
|
+
labelValue?: string;
|
|
101
|
+
representation?: "clean" | "raw";
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* One screen's declaration. Owning packages add fields (agent roles, client
|
|
105
|
+
* tools) as EXTENSION SLOTS — by interface extension or declaration merging —
|
|
106
|
+
* and register a `DeclarationExtension` so validation and the sync cover them.
|
|
107
|
+
*/
|
|
108
|
+
interface SurfaceDeclaration {
|
|
109
|
+
surfaceName: string;
|
|
110
|
+
/** REQUIRED. `ui_surface.client_name`; must equal the surfaceName prefix. */
|
|
111
|
+
client: string;
|
|
112
|
+
/** REQUIRED. `ui_surface.execution_mode`. */
|
|
113
|
+
executionMode: ExecutionMode;
|
|
114
|
+
/** REQUIRED. `ui_surface.description` ("" only where the live row is empty). */
|
|
115
|
+
description: string;
|
|
116
|
+
label: string;
|
|
117
|
+
readiness: Readiness;
|
|
118
|
+
readinessNote?: string;
|
|
119
|
+
urlPattern?: string;
|
|
120
|
+
inheritsFrom?: string;
|
|
121
|
+
intro?: string;
|
|
122
|
+
overlayId?: string;
|
|
123
|
+
values: readonly DeclaredValue[];
|
|
124
|
+
groups?: readonly ValueGroup[];
|
|
125
|
+
writeTargets?: readonly WriteTarget[];
|
|
126
|
+
itemTypes?: readonly ItemType[];
|
|
127
|
+
liveSlots?: readonly LiveSlot[];
|
|
128
|
+
configNamespaces?: readonly ConfigNamespaceDeclaration[];
|
|
129
|
+
evidenceSources?: readonly EvidenceSourceDeclaration[];
|
|
130
|
+
skipBaselineValues?: boolean;
|
|
131
|
+
}
|
|
132
|
+
type ValueProvenance = {
|
|
133
|
+
kind: "own";
|
|
134
|
+
} | {
|
|
135
|
+
kind: "inherited";
|
|
136
|
+
from: string;
|
|
137
|
+
} | {
|
|
138
|
+
kind: "baseline";
|
|
139
|
+
};
|
|
140
|
+
type ResolvedValue = DeclaredValue & {
|
|
141
|
+
provenance: ValueProvenance;
|
|
142
|
+
groupKey: string;
|
|
143
|
+
/** Defaults filled. */
|
|
144
|
+
resolvedSensitivity: Sensitivity;
|
|
145
|
+
};
|
|
146
|
+
interface ResolvedSurfaceDeclaration extends SurfaceDeclaration {
|
|
147
|
+
contentHash: string;
|
|
148
|
+
values: readonly ResolvedValue[];
|
|
149
|
+
groups: readonly ValueGroup[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* ITEM KEYS. A value or write target is identified by (surfaceName, itemType ?? "", name).
|
|
153
|
+
* The qualified key "<itemType>.<name>" names an item value; a bare name is a screen value.
|
|
154
|
+
*/
|
|
155
|
+
type QualifiedValueKey = string;
|
|
156
|
+
/** RAW-FREE. What leaves the package about a clicked item. */
|
|
157
|
+
interface ResolvedItem {
|
|
158
|
+
itemType: string;
|
|
159
|
+
identity: Record<string, string>;
|
|
160
|
+
level?: string;
|
|
161
|
+
readOnly: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Surface Actions — the shapes (Applets CONTRACTS §6, FROZEN; exported here as a superset).
|
|
166
|
+
*
|
|
167
|
+
* An Action is one thing a surface can have done to it, by a person, an agent,
|
|
168
|
+
* an applet or an automation: a write target, a client tool, a Kind Action or a
|
|
169
|
+
* row in `ui.ui_surface_action`. All of them project onto `ActionDeclaration`
|
|
170
|
+
* (`./declarations.ts`) and run through ONE runner (`./surface-run.ts`). There is
|
|
171
|
+
* never a second registry: `name` IS the alchemy action id, and `category`
|
|
172
|
+
* defaults to "applet" in alchemy's closed `ActionCategory` union.
|
|
173
|
+
*
|
|
174
|
+
* Types only — no runtime code, no React, no `@ai-matrx/agents`. `/ports`
|
|
175
|
+
* imports from here, so this file imports nothing but local types.
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
/** Who started a run. */
|
|
179
|
+
type ActionCaller = "person" | "agent" | "applet" | "automation";
|
|
180
|
+
/** Where a run goes: the page's own handler, an aidream server action, a mandate, or a route. */
|
|
181
|
+
type ActionRuns = "page" | "server" | "mandate" | "navigate";
|
|
182
|
+
/** Who may run it without a click: "manual" = only a person; "ask" = proposed first; "auto" = runs. */
|
|
183
|
+
type ActionPolicy = "ask" | "auto" | "manual";
|
|
184
|
+
/** A registered kind slug (`KindRef`, content-ir) or an inline JSON Schema. */
|
|
185
|
+
type ActionInput = string | {
|
|
186
|
+
jsonSchema: object;
|
|
187
|
+
};
|
|
188
|
+
/** Who authored the row (`ui.ui_surface*.declared_by`). Code sync touches only "code". */
|
|
189
|
+
type ActionDeclaredBy = "code" | "database";
|
|
190
|
+
/** Where the Action came from — superset field so a projection loses nothing. */
|
|
191
|
+
interface ActionOrigin {
|
|
192
|
+
source: "write-target" | "client-tool" | "surface-action";
|
|
193
|
+
surfaceName?: string;
|
|
194
|
+
surfaceId?: string;
|
|
195
|
+
/** The source row's id, when it came from a row. */
|
|
196
|
+
id?: string;
|
|
197
|
+
declaredBy?: ActionDeclaredBy;
|
|
198
|
+
/** `ui.ui_surface_action.code_ref`: the code item a database Action names. */
|
|
199
|
+
codeRef?: string;
|
|
200
|
+
/** The source row's organization (an organization extension applies only to its members). */
|
|
201
|
+
organizationId?: string;
|
|
202
|
+
}
|
|
203
|
+
/** JSON only: what a lister (an agent, the menu, an applet) sees. */
|
|
204
|
+
interface ActionInfo {
|
|
205
|
+
name: string;
|
|
206
|
+
category: string;
|
|
207
|
+
label: string;
|
|
208
|
+
description: string;
|
|
209
|
+
runs: ActionRuns;
|
|
210
|
+
policy: ActionPolicy;
|
|
211
|
+
triggeredBy: ActionCaller[];
|
|
212
|
+
input?: ActionInput;
|
|
213
|
+
output?: string;
|
|
214
|
+
/** The surface that declared it (a child's override names the child). */
|
|
215
|
+
declaredOn: string;
|
|
216
|
+
target?: string;
|
|
217
|
+
updatesValue?: string;
|
|
218
|
+
itemType?: string;
|
|
219
|
+
/** Where it came from. "write-target" Actions go through the write door when one is bound (ALC-17). */
|
|
220
|
+
source?: ActionOrigin["source"];
|
|
221
|
+
}
|
|
222
|
+
interface ActionContext {
|
|
223
|
+
runId: string;
|
|
224
|
+
by: ActionCaller;
|
|
225
|
+
viewerId: string | null;
|
|
226
|
+
surfaceName?: string;
|
|
227
|
+
policy?: ActionPolicy;
|
|
228
|
+
}
|
|
229
|
+
/** The system's own sentence, never thrown into a UI. Superset of RecordsError (CONTRACTS §0 `AppletError`). */
|
|
230
|
+
interface ActionError {
|
|
231
|
+
code: string;
|
|
232
|
+
message: string;
|
|
233
|
+
retryable: boolean;
|
|
234
|
+
diagnostic?: string;
|
|
235
|
+
/** Superset: the one-click remedy a write door refusal names. */
|
|
236
|
+
remedy?: string;
|
|
237
|
+
}
|
|
238
|
+
/** CONTRACTS §0 `Result<T>`. */
|
|
239
|
+
type ActionResult<T = unknown> = {
|
|
240
|
+
ok: true;
|
|
241
|
+
data: T;
|
|
242
|
+
} | {
|
|
243
|
+
ok: false;
|
|
244
|
+
error: ActionError;
|
|
245
|
+
};
|
|
246
|
+
/** `POST /actions/run` (aidream) — the request. */
|
|
247
|
+
interface ServerActionRunRequest {
|
|
248
|
+
name: string;
|
|
249
|
+
input: Json$1;
|
|
250
|
+
by: ActionCaller;
|
|
251
|
+
surface_name?: string;
|
|
252
|
+
policy?: ActionPolicy;
|
|
253
|
+
}
|
|
254
|
+
/** `POST /actions/run` (aidream) — the response. */
|
|
255
|
+
interface ServerActionRunResponse {
|
|
256
|
+
status: "applied" | "proposed" | "refused";
|
|
257
|
+
message: string;
|
|
258
|
+
receipts: Json$1[];
|
|
259
|
+
proposal?: Json$1;
|
|
260
|
+
}
|
|
261
|
+
/** A mandate run request: by alias (an Action's `target`) or by key. */
|
|
262
|
+
type MandateRunRequest = ({
|
|
263
|
+
alias: string;
|
|
264
|
+
key?: never;
|
|
265
|
+
} | {
|
|
266
|
+
key: string;
|
|
267
|
+
alias?: never;
|
|
268
|
+
}) & {
|
|
269
|
+
variables: Record<string, Json$1>;
|
|
270
|
+
userInput?: string;
|
|
271
|
+
by: ActionCaller;
|
|
272
|
+
policy: ActionPolicy;
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* CONTRACTS §0/§3 `RunRef` — the serializable identity of a started mandate run. A mandate
|
|
276
|
+
* Action answers with THIS, not the run's result: the result arrives later on the event stream
|
|
277
|
+
* `run:<requestId>` (or `IntelligencePort.result(ref)`). Same fields as `@ai-matrx/agents`'s
|
|
278
|
+
* `RunRef`, declared here because alchemy sits below agents and never imports it.
|
|
279
|
+
*/
|
|
280
|
+
interface MandateRunRef {
|
|
281
|
+
requestId: string;
|
|
282
|
+
conversationId: string;
|
|
283
|
+
mandateKey: string;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* CONTRACTS §6 `SurfacePort` — the surface runtime as the frame sees it. Types
|
|
287
|
+
* only here; the in-page host is AP-0's.
|
|
288
|
+
*/
|
|
289
|
+
interface SurfacePort {
|
|
290
|
+
/** Joins the runtime chain. */
|
|
291
|
+
mount(name: string, parent: string | null): Promise<ActionResult<{
|
|
292
|
+
mountId: string;
|
|
293
|
+
}>>;
|
|
294
|
+
unmount(mountId: string): Promise<void>;
|
|
295
|
+
getValue(name: string): Promise<unknown>;
|
|
296
|
+
setValue(name: string, v: unknown): Promise<void>;
|
|
297
|
+
/** This mount's own Actions plus inherited ones. */
|
|
298
|
+
listActions(): Promise<ActionInfo[]>;
|
|
299
|
+
runAction(name: string, input: unknown, by: ActionCaller): Promise<ActionResult<unknown>>;
|
|
300
|
+
/** A "page" Action is routed to the frame as `{ action, input, runId }`; the frame answers here. */
|
|
301
|
+
completeAction(runId: string, result: ActionResult<unknown>): Promise<void>;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** A `ui.ui_surface_action` row. */
|
|
305
|
+
interface SurfaceActionRow {
|
|
306
|
+
id?: string;
|
|
307
|
+
surface_id: string;
|
|
308
|
+
surface_name: string;
|
|
309
|
+
/** '' = the screen itself. */
|
|
310
|
+
item_type: string;
|
|
311
|
+
name: string;
|
|
312
|
+
category: string;
|
|
313
|
+
label: string;
|
|
314
|
+
description: string;
|
|
315
|
+
input: ActionInput | null;
|
|
316
|
+
output: string | null;
|
|
317
|
+
runs: ActionRuns;
|
|
318
|
+
target: string | null;
|
|
319
|
+
triggered_by: ActionCaller[];
|
|
320
|
+
policy: ActionPolicy;
|
|
321
|
+
updates_value: string | null;
|
|
322
|
+
code_ref: string | null;
|
|
323
|
+
declared_by: ActionDeclaredBy;
|
|
324
|
+
sort_order: number;
|
|
325
|
+
/**
|
|
326
|
+
* The row's organization. A `declared_by='database'` row on a surface outside `applets/` is that
|
|
327
|
+
* organization's EXTENSION and applies only to its members (`readSurfaceChain`, aidream `visible_actions`).
|
|
328
|
+
*/
|
|
329
|
+
organization_id?: string | null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Surfaces under this prefix belong to one Applet; their database rows are never "extensions". */
|
|
333
|
+
declare const APPLET_SURFACE_PREFIX = "applets/";
|
|
334
|
+
interface ReadSurfaceChainOptions {
|
|
335
|
+
/** Most ancestors a surface may have (alchemy's declaration default when absent). */
|
|
336
|
+
maxDepth?: number;
|
|
337
|
+
/** ALL of the viewer's organization memberships. Absent = organization extensions are dropped. */
|
|
338
|
+
organizationIds?: readonly string[];
|
|
339
|
+
}
|
|
340
|
+
/** Whether a row applies to a viewer with these memberships (aidream `visible_actions`). */
|
|
341
|
+
declare function surfaceActionVisible(row: Pick<SurfaceActionRow, "declared_by" | "surface_name" | "organization_id">, organizationIds: readonly string[] | undefined): boolean;
|
|
342
|
+
declare function readSurfaceChain(client: object, surfaceName: string, options?: ReadSurfaceChainOptions): Promise<ActionResult<ActionInfo[]>>;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The click target (CONTRACT §2.4) in its own module: `/ports` references it,
|
|
346
|
+
* and `/ports` must stay free of every other package (types only).
|
|
347
|
+
*/
|
|
348
|
+
|
|
349
|
+
/** The transfer Payload's kinds (`actions/types.ts` asserts equality at compile time). */
|
|
350
|
+
type PayloadKind = "text" | "markdown" | "json" | "rows" | "registered";
|
|
351
|
+
/** What eligibility is computed from: everything declared at the click target. */
|
|
352
|
+
interface ClickTarget {
|
|
353
|
+
surface: {
|
|
354
|
+
instanceId: string;
|
|
355
|
+
surfaceName: string;
|
|
356
|
+
} | null;
|
|
357
|
+
/** The resolved declaration (inheritance applied). Eligibility decides by KEY existence over it. */
|
|
358
|
+
declaration: ResolvedSurfaceDeclaration | null;
|
|
359
|
+
/** Declared value keys with a read path here (screen names + qualified item keys). Key existence, not population. */
|
|
360
|
+
valueKeys: ReadonlySet<QualifiedValueKey>;
|
|
361
|
+
/** Scope chain, most general first: ["global", "<domain>", "<surfaceName>"]. */
|
|
362
|
+
scopeChain: readonly string[];
|
|
363
|
+
/** The surface's explicit exclusions (the one override). */
|
|
364
|
+
excludedActionIds: ReadonlySet<string>;
|
|
365
|
+
item: ResolvedItem | null;
|
|
366
|
+
level: string | null;
|
|
367
|
+
/** Offsets + the live range handle, editable or not. */
|
|
368
|
+
selection: {
|
|
369
|
+
text: string;
|
|
370
|
+
type: "editable" | "non-editable";
|
|
371
|
+
start: number;
|
|
372
|
+
end: number;
|
|
373
|
+
/** Opaque host handle (DOM Range / element) used to restore or stream into the selection. */
|
|
374
|
+
handle?: unknown;
|
|
375
|
+
} | null;
|
|
376
|
+
/** Payload kinds a capture of this target would produce (from declared value types/kinds). */
|
|
377
|
+
payloadKinds: readonly PayloadKind[];
|
|
378
|
+
/** Declared targets writable on this target right now. */
|
|
379
|
+
writable: readonly WriteTarget[];
|
|
380
|
+
readOnly: boolean;
|
|
381
|
+
/** True when any declared value here is secret/credential or non-exportable. */
|
|
382
|
+
restricted: boolean;
|
|
383
|
+
organizationId: string | null;
|
|
384
|
+
auth: {
|
|
385
|
+
authenticated: boolean;
|
|
386
|
+
admin: boolean;
|
|
387
|
+
creator: boolean;
|
|
388
|
+
};
|
|
389
|
+
/**
|
|
390
|
+
* Host-owned context a provider reads to act (the rich-document source, the
|
|
391
|
+
* editable field's callbacks). Opaque to the package; never crosses into the
|
|
392
|
+
* model. Providers that need it narrow it themselves.
|
|
393
|
+
*/
|
|
394
|
+
host?: unknown;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Operate — the format and delivery contract (Matrx Alchemy CONTRACT §2.3, ALC-16).
|
|
399
|
+
*
|
|
400
|
+
* A format is an ADAPTER: `supports` is synchronous and loads nothing; `load` dynamically
|
|
401
|
+
* imports the engine the first time the format is chosen (print for rich-html/html/pdf/docx/
|
|
402
|
+
* epub, SheetJS for xlsx). Delivery accepts only a SEALED artifact, so the bytes previewed are
|
|
403
|
+
* the bytes copied, downloaded or sent.
|
|
404
|
+
*/
|
|
405
|
+
|
|
406
|
+
/** A reference to a declared write target (resolved by the one write door, ALC-17). */
|
|
407
|
+
interface WriteTargetRef {
|
|
408
|
+
surfaceName?: string;
|
|
409
|
+
target: string;
|
|
410
|
+
item?: {
|
|
411
|
+
itemType: string;
|
|
412
|
+
identity: Record<string, string>;
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Every write returns one (CONTRACT §2.3; the door is `createWriteDoor`, ALC-17). An applied
|
|
417
|
+
* receipt may carry a `warning` with its `fix` (rule 10: a person's kind mismatch warns, never refuses).
|
|
418
|
+
* A refusal's `reason` is a `WriteRefusalReason` (`./door`).
|
|
419
|
+
*/
|
|
420
|
+
type Receipt = {
|
|
421
|
+
status: "applied";
|
|
422
|
+
to: WriteTargetRef;
|
|
423
|
+
sentence: string;
|
|
424
|
+
warning?: string;
|
|
425
|
+
fix?: string;
|
|
426
|
+
} | {
|
|
427
|
+
status: "queued";
|
|
428
|
+
to: WriteTargetRef;
|
|
429
|
+
sentence: string;
|
|
430
|
+
remedy: string;
|
|
431
|
+
} | {
|
|
432
|
+
status: "refused";
|
|
433
|
+
to: WriteTargetRef;
|
|
434
|
+
reason: string;
|
|
435
|
+
sentence: string;
|
|
436
|
+
remedy: string;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* THE ONE WRITE DOOR (Matrx Alchemy ALC-17; Applets CONTRACTS §6 "receipts through the one write door").
|
|
441
|
+
*
|
|
442
|
+
* Every write — a person's edit, a menu row, a destination send, an agent, an applet, an automation,
|
|
443
|
+
* an Action projected from a write target — lands through `door.write`. The door:
|
|
444
|
+
*
|
|
445
|
+
* 1. resolves the DECLARED write target (the surface's own, then its ancestors'); an undeclared
|
|
446
|
+
* target is refused by name;
|
|
447
|
+
* 2. enforces the mode: `stream` takes ops from the target's `streamOps`; ops on any other target
|
|
448
|
+
* need `patchable` (+ `updatesValue`); a value on a `stream` target is refused;
|
|
449
|
+
* 3. checks the kind (rule 10): a person's mismatch WARNS (the receipt is still applied, with a
|
|
450
|
+
* `warning` and a `fix`); an agent's, applet's or automation's malformed write is REFUSED;
|
|
451
|
+
* 4. applies the policy: manual → a person only; ask → a person's own act is the approval, anyone
|
|
452
|
+
* else asks through `approvals`; queued → filed through `review`; auto → applies;
|
|
453
|
+
* 5. applies through the handler: the mounted page's LIVE handler wins over the HEADLESS one;
|
|
454
|
+
* neither → refused `unapplicable` ("open <surface> to apply it");
|
|
455
|
+
* 6. turns stale / missing record / broken protected span into "cannot apply" with a one-click
|
|
456
|
+
* remedy (rule 11).
|
|
457
|
+
*
|
|
458
|
+
* Every outcome is a `Receipt` carrying the system's own sentence. `write` never throws.
|
|
459
|
+
*
|
|
460
|
+
* Who counts as a person: `person`, `menu` and `destination` (a destination send is a person's own
|
|
461
|
+
* click on "Send to …"). `agent`, `applet` and `automation` are not.
|
|
462
|
+
*
|
|
463
|
+
* Open-risk ruling — stream ops into an entity target: an entity write is ONE atomic save. Ops reach
|
|
464
|
+
* an entity target only when it is declared `patchable` (the ops are handed to the handler in one
|
|
465
|
+
* call and commit as one save); a non-patchable entity target refuses ops with the remedy "stream
|
|
466
|
+
* into the editor, then save". Op-by-op persistence into a record does not exist.
|
|
467
|
+
*/
|
|
468
|
+
|
|
469
|
+
/** Who is writing. `menu` and `destination` are a person's own act. */
|
|
470
|
+
type WriteCaller = "person" | "agent" | "applet" | "automation" | "destination" | "menu";
|
|
471
|
+
/** One text op for a `stream` (or `patchable`) target — the WidgetHandle op set. */
|
|
472
|
+
interface WriteOp {
|
|
473
|
+
op: StreamOp;
|
|
474
|
+
/** The text the op inserts or replaces with. */
|
|
475
|
+
text?: string;
|
|
476
|
+
/** What `replace` / `insert_before` / `insert_after` / `patch` anchor on. */
|
|
477
|
+
find?: string;
|
|
478
|
+
}
|
|
479
|
+
interface WriteRequest {
|
|
480
|
+
surfaceName: string;
|
|
481
|
+
/** A write target name; `"<itemType>.<name>"` names an item's target. */
|
|
482
|
+
target: string;
|
|
483
|
+
itemType?: string;
|
|
484
|
+
/** Which item, by its declared identity values. Required for an item target. */
|
|
485
|
+
itemIdentity?: Record<string, string>;
|
|
486
|
+
/** A whole value. Exactly one of `value` / `ops`. */
|
|
487
|
+
value?: unknown;
|
|
488
|
+
/** Text ops (stream / patchable targets). */
|
|
489
|
+
ops?: readonly WriteOp[];
|
|
490
|
+
by: WriteCaller;
|
|
491
|
+
runId?: string;
|
|
492
|
+
}
|
|
493
|
+
/** What a handler receives: the request plus the declared target it resolved to. */
|
|
494
|
+
interface WriteApplyRequest {
|
|
495
|
+
surfaceName: string;
|
|
496
|
+
/** The surface that declares the target (an ancestor when inherited). */
|
|
497
|
+
declaredOn: string;
|
|
498
|
+
target: WriteTarget;
|
|
499
|
+
item?: {
|
|
500
|
+
itemType: string;
|
|
501
|
+
identity: Record<string, string>;
|
|
502
|
+
};
|
|
503
|
+
value?: unknown;
|
|
504
|
+
ops?: readonly WriteOp[];
|
|
505
|
+
by: WriteCaller;
|
|
506
|
+
runId?: string;
|
|
507
|
+
}
|
|
508
|
+
/** Rule 11: the three "cannot apply" signals. */
|
|
509
|
+
type CannotApply = "stale" | "missing-record" | "broken-span";
|
|
510
|
+
/**
|
|
511
|
+
* A handler applies the write. Return nothing (or `{ status: "applied" }`) when it landed, or a
|
|
512
|
+
* `cannotApply` signal; THROWING an error whose `code` is a `CannotApply` signal means the same.
|
|
513
|
+
*/
|
|
514
|
+
type WriteHandlerResult = void | {
|
|
515
|
+
status: "applied";
|
|
516
|
+
sentence?: string;
|
|
517
|
+
} | {
|
|
518
|
+
status: "cannot-apply";
|
|
519
|
+
cannotApply: CannotApply;
|
|
520
|
+
sentence?: string;
|
|
521
|
+
remedy?: string;
|
|
522
|
+
};
|
|
523
|
+
type WriteHandler = (request: WriteApplyRequest, signal: AbortSignal) => WriteHandlerResult | Promise<WriteHandlerResult>;
|
|
524
|
+
/** Files a `queued` write for an organization's review. */
|
|
525
|
+
interface WriteReviewPort {
|
|
526
|
+
file(request: WriteApplyRequest): Promise<{
|
|
527
|
+
sentence?: string;
|
|
528
|
+
remedy?: string;
|
|
529
|
+
} | void>;
|
|
530
|
+
}
|
|
531
|
+
interface WriteDoor {
|
|
532
|
+
write(request: WriteRequest, signal?: AbortSignal): Promise<Receipt>;
|
|
533
|
+
/** A feature's write handler that works with no page mounted. Refuses a second one for the same target. */
|
|
534
|
+
registerHeadless(surfaceName: string, targetName: string, handler: WriteHandler): () => void;
|
|
535
|
+
/** A mounted page's handler; wins over the headless one while registered. Returns unregister. */
|
|
536
|
+
registerLive(surfaceName: string, targetName: string, handler: WriteHandler): () => void;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).
|
|
541
|
+
*
|
|
542
|
+
* The app binds these ONCE, at its boundary (where the Alchemy host is
|
|
543
|
+
* mounted). The package never imports the host.
|
|
544
|
+
*
|
|
545
|
+
* ── Ports arrive with the wave that needs them (chair ruling N8) ────────────
|
|
546
|
+
*
|
|
547
|
+
* This entry declares only the ports the current waves use:
|
|
548
|
+
*
|
|
549
|
+
* ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)
|
|
550
|
+
* ALC-14 `identity`, `persistence`
|
|
551
|
+
* ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`
|
|
552
|
+
*
|
|
553
|
+
* ALC-16 `transport`, `assets`, `server`
|
|
554
|
+
* PP-01a `window` (host-rendered windows for a live preparation session)
|
|
555
|
+
* PP-13a `transferKnobs` (organization default recipes)
|
|
556
|
+
*
|
|
557
|
+
* ALC-17 `door` (the one write door), `review` (queued writes)
|
|
558
|
+
*
|
|
559
|
+
* ── Absent-port semantics ───────────────────────────────────────────────────
|
|
560
|
+
*
|
|
561
|
+
* Every port except `diagnostics` is OPTIONAL. A port the host does not bind
|
|
562
|
+
* means that capability is ABSENT: the actions, formats or writes that need
|
|
563
|
+
* it are not offered at all. A missing port is never stubbed, never faked
|
|
564
|
+
* with a default that pretends to work, and never shown as a dead control.
|
|
565
|
+
*
|
|
566
|
+
* kinds absent → kind validation is absent (no value can be checked,
|
|
567
|
+
* so a kind-bearing write is not offered)
|
|
568
|
+
* diagnostics REQUIRED → nothing fails silently; contract breaks land here
|
|
569
|
+
* identity absent → organization-scoped declarations are absent
|
|
570
|
+
* persistence absent → per-person settings are absent (organization and
|
|
571
|
+
* platform defaults apply)
|
|
572
|
+
* store absent → actions that read named host state or run named
|
|
573
|
+
* host commands are absent
|
|
574
|
+
* notify absent → outcomes are not announced (diagnostics still captures)
|
|
575
|
+
* overlay absent → actions that open an overlay or navigate are absent
|
|
576
|
+
* icons absent → rows render label-only (a key is never shown as text)
|
|
577
|
+
* knobs absent → the platform default layout/density applies
|
|
578
|
+
* i18n absent → labels as written
|
|
579
|
+
* auth absent → a signed-out person does not see requiresAuth actions
|
|
580
|
+
* (sign-in-then-resume is the capability that is missing)
|
|
581
|
+
* dialogs absent → actions that need a host dialog are absent
|
|
582
|
+
* window absent → the preparation workspace window is absent (its
|
|
583
|
+
* Prepare entry is not offered; it never falls back
|
|
584
|
+
* to an overlay carrying the session as data)
|
|
585
|
+
* transferKnobs absent → no organization default recipe; a person's own
|
|
586
|
+
* default (persistence) or the package's Full applies
|
|
587
|
+
* serverActions absent → `runs: "server"` Actions refuse with "not available here"
|
|
588
|
+
* mandates absent → `runs: "mandate"` Actions refuse the same way
|
|
589
|
+
* pageActions absent → `runs: "page"` Actions refuse the same way
|
|
590
|
+
* approvals absent → a policy "ask" Action started by an agent, applet or automation is refused
|
|
591
|
+
* ("needs a person's approval"); a person's own click is the approval
|
|
592
|
+
* (`runs: "navigate"` goes through `overlay.navigate`; overlay absent → refused)
|
|
593
|
+
* door absent → write-target Actions dispatch to `pageActions` as before, and
|
|
594
|
+
* `Delivery.send` is refused with a remedy; bound → every such write goes
|
|
595
|
+
* through it and returns its receipt
|
|
596
|
+
* review absent → a write to a `queued` target is refused ("review isn't available here")
|
|
597
|
+
*
|
|
598
|
+
* Types only: this entry has no runtime code.
|
|
599
|
+
*/
|
|
600
|
+
/** JSON as it crosses a port. */
|
|
601
|
+
type Json = null | boolean | number | string | Json[] | {
|
|
602
|
+
[key: string]: Json;
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* content-ir's kind validator, injected so Alchemy validates without
|
|
606
|
+
* importing the app or the kind catalog. The host builds it from
|
|
607
|
+
* `@ai-matrx/content-ir`'s `createKindValidator` over its own
|
|
608
|
+
* `SchemaSourcePort` (which content-ir defines and exports; Alchemy never
|
|
609
|
+
* declares it).
|
|
610
|
+
*
|
|
611
|
+
* `unverifiable: true` marks a verdict where the kind could not be checked
|
|
612
|
+
* (not registered, no schema, uncompilable schema, catalog unreachable) — a
|
|
613
|
+
* skip, which is never a pass.
|
|
614
|
+
*/
|
|
615
|
+
interface KindValidatorPort {
|
|
616
|
+
validate(kindKey: string, value: unknown, signal: AbortSignal): Promise<{
|
|
617
|
+
ok: true;
|
|
618
|
+
} | {
|
|
619
|
+
ok: false;
|
|
620
|
+
sentence: string;
|
|
621
|
+
remedy: string;
|
|
622
|
+
unverifiable?: true;
|
|
623
|
+
}>;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Where contract breaks are captured. Required: nothing fails silently. */
|
|
627
|
+
interface DiagnosticsPort {
|
|
628
|
+
capture(error: unknown, context: {
|
|
629
|
+
area: "declare" | "operate" | "write" | "actions" | "menu";
|
|
630
|
+
detail: Json;
|
|
631
|
+
}): void;
|
|
632
|
+
}
|
|
633
|
+
/** Who is using the app right now, and in which organization. */
|
|
634
|
+
interface IdentityPort {
|
|
635
|
+
/** `null` while the host has no resolved identity yet. */
|
|
636
|
+
current(): {
|
|
637
|
+
userId: string;
|
|
638
|
+
organizationId: string | null;
|
|
639
|
+
isAuthenticated: boolean;
|
|
640
|
+
isAdmin: boolean;
|
|
641
|
+
} | null;
|
|
642
|
+
/** Called whenever `current()` may answer differently; returns unsubscribe. */
|
|
643
|
+
onChange(listener: () => void): () => void;
|
|
644
|
+
}
|
|
645
|
+
/** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */
|
|
646
|
+
interface PersistencePort {
|
|
647
|
+
readSetting<T extends Json>(key: string): Promise<T | null>;
|
|
648
|
+
writeSetting(key: string, value: Json): Promise<void>;
|
|
649
|
+
}
|
|
650
|
+
/** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */
|
|
651
|
+
interface StorePort {
|
|
652
|
+
/** A named, typed read the host registered ("activeConversationId", "organizationName"). */
|
|
653
|
+
read<T extends Json>(key: string): T | null;
|
|
654
|
+
/** A named command the host registered ("tasks.setPendingSource"); resolves with its result. */
|
|
655
|
+
command(name: string, input: Json): Promise<Json | void>;
|
|
656
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
657
|
+
}
|
|
658
|
+
interface NotifyPort {
|
|
659
|
+
success(sentence: string): void;
|
|
660
|
+
info(sentence: string): void;
|
|
661
|
+
/** Every failure names its remedy. */
|
|
662
|
+
error(sentence: string, remedy: string): void;
|
|
663
|
+
}
|
|
664
|
+
/** Overlays are dispatched through the host; the menu never renders a modal. */
|
|
665
|
+
interface OverlayHostPort {
|
|
666
|
+
open(overlayId: string, data: Json, instanceId?: string): void;
|
|
667
|
+
close(overlayId: string, instanceId?: string): void;
|
|
668
|
+
navigate(href: string): void;
|
|
669
|
+
}
|
|
670
|
+
/** Icon key → the host's icon component. The model only ever holds keys. */
|
|
671
|
+
interface IconResolverPort {
|
|
672
|
+
resolve(key: string): unknown | null;
|
|
673
|
+
}
|
|
674
|
+
type ContextArrangementKnob = "classic" | "tiered" | "command";
|
|
675
|
+
type DensityKnob = "comfortable" | "compact";
|
|
676
|
+
/** ORGANIZATION defaults only; every method takes the organization. */
|
|
677
|
+
interface MenuKnobsPort {
|
|
678
|
+
/** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */
|
|
679
|
+
placementLabel(placement: string, organizationId: string | null): string | null;
|
|
680
|
+
/** Replaces the constant DEFAULT_PLACEMENT_MODE. */
|
|
681
|
+
placementMode(placement: string, organizationId: string | null): "show" | "hide";
|
|
682
|
+
/** The organization's default layout and density (platform default when null: command / comfortable). */
|
|
683
|
+
layout(organizationId: string | null): {
|
|
684
|
+
context: ContextArrangementKnob;
|
|
685
|
+
density: DensityKnob;
|
|
686
|
+
} | null;
|
|
687
|
+
}
|
|
688
|
+
/** Label translation. Absent = labels as written. */
|
|
689
|
+
interface I18nPort {
|
|
690
|
+
t(key: string, fallback: string): string;
|
|
691
|
+
}
|
|
692
|
+
/** Sign in, then resume the action the person picked. */
|
|
693
|
+
interface AuthResumePort {
|
|
694
|
+
isSignedIn(): boolean;
|
|
695
|
+
/** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */
|
|
696
|
+
signInThenResume(pending: {
|
|
697
|
+
actionId: string;
|
|
698
|
+
contentFingerprint: string;
|
|
699
|
+
}): void;
|
|
700
|
+
takePending(contentFingerprint: string): {
|
|
701
|
+
actionId: string;
|
|
702
|
+
} | null;
|
|
703
|
+
}
|
|
704
|
+
/** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */
|
|
705
|
+
interface HostDialogsPort {
|
|
706
|
+
requestDelete?(target: ClickTarget): void;
|
|
707
|
+
requestConvert?(target: ClickTarget): void;
|
|
708
|
+
requestEditHistory?(target: ClickTarget): void;
|
|
709
|
+
requestFullPrint?(target: ClickTarget): void;
|
|
710
|
+
requestSaveTable?(table: {
|
|
711
|
+
headers: string[];
|
|
712
|
+
rows: string[][];
|
|
713
|
+
}): void;
|
|
714
|
+
requestFlashcard?(answer: string): void;
|
|
715
|
+
}
|
|
716
|
+
/** The ports a host binds. Optional = absent capability when unbound (see above). */
|
|
717
|
+
interface AlchemyHostPorts {
|
|
718
|
+
kinds?: KindValidatorPort;
|
|
719
|
+
/** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */
|
|
720
|
+
diagnostics: DiagnosticsPort;
|
|
721
|
+
identity?: IdentityPort;
|
|
722
|
+
persistence?: PersistencePort;
|
|
723
|
+
store?: StorePort;
|
|
724
|
+
notify?: NotifyPort;
|
|
725
|
+
overlay?: OverlayHostPort;
|
|
726
|
+
icons?: IconResolverPort;
|
|
727
|
+
/** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */
|
|
728
|
+
knobs?: MenuKnobsPort;
|
|
729
|
+
i18n?: I18nPort;
|
|
730
|
+
auth?: AuthResumePort;
|
|
731
|
+
dialogs?: HostDialogsPort;
|
|
732
|
+
/** Clipboard and download for this runtime (the browser default ships in /operate). */
|
|
733
|
+
transport?: TransportPort;
|
|
734
|
+
/** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */
|
|
735
|
+
assets?: AssetResolverPort;
|
|
736
|
+
/** Present when the host can reach aidream server operations; absent = server formats are not offered. */
|
|
737
|
+
server?: ServerPort;
|
|
738
|
+
/** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */
|
|
739
|
+
window?: WindowPort;
|
|
740
|
+
/** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */
|
|
741
|
+
transferKnobs?: TransferKnobsPort;
|
|
742
|
+
/** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */
|
|
743
|
+
serverActions?: ServerActionPort;
|
|
744
|
+
/** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */
|
|
745
|
+
mandates?: MandatePort;
|
|
746
|
+
/** The page's own handlers for `runs: "page"` Actions (in an applet frame: routed as `{ action, input, runId }`). */
|
|
747
|
+
pageActions?: PageActionPort;
|
|
748
|
+
/** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */
|
|
749
|
+
approvals?: ApprovalPort;
|
|
750
|
+
/** `createWriteDoor(...)`: every write to a declared target lands here and returns a receipt. */
|
|
751
|
+
door?: WriteDoor;
|
|
752
|
+
/** Files a write to a `queued` target for an organization's review. */
|
|
753
|
+
review?: WriteReviewPort;
|
|
754
|
+
}
|
|
755
|
+
interface ServerActionPort {
|
|
756
|
+
run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;
|
|
757
|
+
}
|
|
758
|
+
interface MandatePort {
|
|
759
|
+
/**
|
|
760
|
+
* STARTS the run and resolves with its `RunRef` (CONTRACTS §3/§6) — never the run's result, which
|
|
761
|
+
* arrives later on `run:<requestId>` / `IntelligencePort.result(ref)`. Rejects with the system's sentence.
|
|
762
|
+
*/
|
|
763
|
+
run(request: MandateRunRequest): Promise<MandateRunRef>;
|
|
764
|
+
}
|
|
765
|
+
interface ApprovalRequest {
|
|
766
|
+
action: string;
|
|
767
|
+
label: string;
|
|
768
|
+
description: string;
|
|
769
|
+
input: Json;
|
|
770
|
+
by: "agent" | "applet" | "automation";
|
|
771
|
+
surfaceName: string;
|
|
772
|
+
}
|
|
773
|
+
interface ApprovalPort {
|
|
774
|
+
/** Resolves true when the person approves; false when they decline or dismiss. */
|
|
775
|
+
ask(request: ApprovalRequest): Promise<boolean>;
|
|
776
|
+
}
|
|
777
|
+
interface PageActionPort {
|
|
778
|
+
dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation
|
|
782
|
+
* session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace
|
|
783
|
+
* never travels as overlay data; the package hands the host a render function and the host draws it
|
|
784
|
+
* inside its own window system, with the session still live in the closure.
|
|
785
|
+
*/
|
|
786
|
+
interface WindowSize {
|
|
787
|
+
width: number;
|
|
788
|
+
height: number;
|
|
789
|
+
}
|
|
790
|
+
interface WindowOpenOptions {
|
|
791
|
+
/** The window's title, as a person reads it. */
|
|
792
|
+
title: string;
|
|
793
|
+
/** Preferred size; `"full"` = near-full-screen. The host clamps to the viewport. */
|
|
794
|
+
size?: WindowSize | "full";
|
|
795
|
+
/**
|
|
796
|
+
* Stable identity. Opening an id that is already open focuses that window and draws the new
|
|
797
|
+
* render in it instead of opening a second one.
|
|
798
|
+
*/
|
|
799
|
+
instanceId?: string;
|
|
800
|
+
/** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */
|
|
801
|
+
onClose?: () => void;
|
|
802
|
+
}
|
|
803
|
+
interface WindowHandle {
|
|
804
|
+
readonly instanceId: string;
|
|
805
|
+
/** Close the window; idempotent. */
|
|
806
|
+
close(): void;
|
|
807
|
+
/** Bring the window to the front; a no-op once closed. */
|
|
808
|
+
focus(): void;
|
|
809
|
+
isOpen(): boolean;
|
|
810
|
+
}
|
|
811
|
+
interface WindowPort {
|
|
812
|
+
/**
|
|
813
|
+
* What the host really draws. `"window"`: non-blocking, movable, resizable. `"dialog"`: a blocking
|
|
814
|
+
* full-screen dialog — a surface on a dialog host never says "window" and never offers a second one.
|
|
815
|
+
*/
|
|
816
|
+
readonly presentation: "window" | "dialog";
|
|
817
|
+
/** True only when several windows can be open and usable at once. False = never offer "open another". */
|
|
818
|
+
readonly multiInstance: boolean;
|
|
819
|
+
/**
|
|
820
|
+
* Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in
|
|
821
|
+
* a React host) and is called by the host while the window is open.
|
|
822
|
+
*/
|
|
823
|
+
open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform
|
|
827
|
+
* default an admin sets, overridden by the organization. Never per-person — a person's own choice
|
|
828
|
+
* lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).
|
|
829
|
+
*/
|
|
830
|
+
interface TransferKnobsPort {
|
|
831
|
+
/**
|
|
832
|
+
* The organization's default recipe for one source kind (the platform default when the
|
|
833
|
+
* organization set none), or null when neither names one. A read that fails rejects; it never
|
|
834
|
+
* answers null for "could not ask".
|
|
835
|
+
*/
|
|
836
|
+
defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;
|
|
837
|
+
}
|
|
838
|
+
/** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */
|
|
839
|
+
interface TransportArtifact {
|
|
840
|
+
format: string;
|
|
841
|
+
plainText: string;
|
|
842
|
+
html?: string;
|
|
843
|
+
file?: {
|
|
844
|
+
filename: string;
|
|
845
|
+
mime: string;
|
|
846
|
+
bytes: Uint8Array;
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
type TransportOutcome = {
|
|
850
|
+
status: "success";
|
|
851
|
+
delivered: "clipboard" | "download-started";
|
|
852
|
+
mimeTypes: string[];
|
|
853
|
+
} | {
|
|
854
|
+
status: "degraded";
|
|
855
|
+
delivered: "plain-text";
|
|
856
|
+
reason: string;
|
|
857
|
+
} | {
|
|
858
|
+
status: "cancelled";
|
|
859
|
+
} | {
|
|
860
|
+
status: "error";
|
|
861
|
+
code: string;
|
|
862
|
+
message: string;
|
|
863
|
+
retryable: boolean;
|
|
864
|
+
};
|
|
865
|
+
/** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */
|
|
866
|
+
interface TransportPort {
|
|
867
|
+
copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
|
|
868
|
+
download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;
|
|
869
|
+
}
|
|
870
|
+
interface AssetResolverPort {
|
|
871
|
+
resolve(url: string, signal: AbortSignal): Promise<{
|
|
872
|
+
url: string;
|
|
873
|
+
} | null>;
|
|
874
|
+
}
|
|
875
|
+
type ServerResult = {
|
|
876
|
+
status: "bytes";
|
|
877
|
+
filename: string;
|
|
878
|
+
mime: string;
|
|
879
|
+
bytes: Uint8Array;
|
|
880
|
+
notices: readonly {
|
|
881
|
+
code: string;
|
|
882
|
+
sentence: string;
|
|
883
|
+
remedy: string;
|
|
884
|
+
}[];
|
|
885
|
+
} | {
|
|
886
|
+
status: "receipt";
|
|
887
|
+
receipt: {
|
|
888
|
+
status: string;
|
|
889
|
+
sentence: string;
|
|
890
|
+
remedy?: string;
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
interface ServerPort {
|
|
894
|
+
call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* THE ONE RUNNER for surface Actions (Applets CONTRACTS §6). Every caller — a
|
|
899
|
+
* person's click, an agent, an applet, an automation — runs an Action through
|
|
900
|
+
* here, so policy and caller rules live once.
|
|
901
|
+
*
|
|
902
|
+
* Policy "ask": a person's own click is the approval. Started by an agent, applet or automation,
|
|
903
|
+
* a "server" run carries the policy to aidream (which answers with a proposal); every other run
|
|
904
|
+
* asks the person through `ports.approvals` first, and is refused by name without that port.
|
|
905
|
+
*
|
|
906
|
+
* Dispatch by `runs`:
|
|
907
|
+
* "server" → `ports.serverActions.run({ name: target, input, by, surface_name, policy })`
|
|
908
|
+
* "mandate" → `ports.mandates.run({ alias: target, variables, by, policy })` — answers with the
|
|
909
|
+
* started run's `MandateRunRef`; the result streams later on `run:<requestId>`
|
|
910
|
+
* "navigate" → `ports.overlay.navigate(target)` with `{param}` filled from the input
|
|
911
|
+
* "page" → `ports.pageActions.dispatch(name, input, context)`
|
|
912
|
+
* A port the host did not bind means that capability is ABSENT: the run is
|
|
913
|
+
* refused with a sentence, never stubbed (ports rule).
|
|
914
|
+
*
|
|
915
|
+
* Write targets (ALC-17): an Action projected from a declared write target (`source: "write-target"`)
|
|
916
|
+
* goes through `ports.door.write` when a door is bound — the door applies the target's own policy
|
|
917
|
+
* (manual / ask / queued / auto) and its receipt becomes the result (`applied` / `queued` → ok with
|
|
918
|
+
* the receipt; refused → an error whose code is the refusal reason and whose `remedy` is the door's).
|
|
919
|
+
* Without a door it dispatches to `pageActions` as before.
|
|
920
|
+
*
|
|
921
|
+
* Never throws: every outcome is a `Result` whose error message is the
|
|
922
|
+
* system's own sentence.
|
|
923
|
+
*/
|
|
924
|
+
|
|
925
|
+
type SurfaceActionPorts = Pick<AlchemyHostPorts, "diagnostics" | "serverActions" | "mandates" | "pageActions" | "overlay" | "approvals" | "door">;
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* `createSurfacePort` — the one SurfacePort runtime (Applets CONTRACTS §6 + Amendment 2.1).
|
|
929
|
+
*
|
|
930
|
+
* A parent surface offers every Action and value it has; a user's or organization's surface
|
|
931
|
+
* extends it. This is the runtime a host binds once and hands to a frame:
|
|
932
|
+
*
|
|
933
|
+
* mount reads the chain from the DATABASE (`readSurfaceChain`); the frame's `parent`
|
|
934
|
+
* argument names the same thing and is not trusted over it
|
|
935
|
+
* get/setValue the surface's named values, cloned both ways; `setValue` emits `value:<name>`
|
|
936
|
+
* listActions never throws — a refusal goes to `ports.diagnostics` and answers `[]`
|
|
937
|
+
* runAction found by name in the mounted chain (or a fresh read), run through
|
|
938
|
+
* `runSurfaceAction` (the one runner); an Action that `updatesValue` sets it
|
|
939
|
+
* completeAction settles a pending page-Action run by its runId
|
|
940
|
+
*
|
|
941
|
+
* Page Actions: with `opts.pageActions` the host routes them (e.g. to the frame that declared the
|
|
942
|
+
* surface) and may park the run with `awaitPageAction(runId)`. Without it the port routes them
|
|
943
|
+
* itself: it emits `"actions"` `{ action, input, runId, by }` and waits for `completeAction`.
|
|
944
|
+
* `dispose()` settles every run this port parked with `host_disposed`.
|
|
945
|
+
*
|
|
946
|
+
* The package never imports the host or supabase: the client is structural (`./client.ts`), and
|
|
947
|
+
* every capability arrives as a port. Nothing here picks an organization.
|
|
948
|
+
*/
|
|
949
|
+
|
|
950
|
+
/** The stream a self-routed page Action travels on (CONTRACTS Amendment 2.1). */
|
|
951
|
+
declare const PAGE_ACTION_STREAM = "actions";
|
|
952
|
+
/** The stream a surface value travels on. */
|
|
953
|
+
declare const valueStream: (name: string) => string;
|
|
954
|
+
/** What travels on `"actions"`; the frame answers with `surface.completeAction(runId, result)`. */
|
|
955
|
+
interface PageActionEvent {
|
|
956
|
+
action: string;
|
|
957
|
+
input: unknown;
|
|
958
|
+
runId: string;
|
|
959
|
+
by?: ActionCaller;
|
|
960
|
+
}
|
|
961
|
+
/** The runner's ports the host binds; page Actions are routed by `pageActions` / the port itself. */
|
|
962
|
+
type SurfacePortRunPorts = Omit<SurfaceActionPorts, "pageActions">;
|
|
963
|
+
/** One settle function per parked page-Action run. */
|
|
964
|
+
type PendingPageActions = Map<string, (result: ActionResult<unknown>) => void>;
|
|
965
|
+
interface CreateSurfacePortOptions {
|
|
966
|
+
/** A supabase-js v2 client carrying the VIEWER's session. Typed `object` on purpose (see ./client.ts). */
|
|
967
|
+
client: object;
|
|
968
|
+
/** ALL of the viewer's memberships, for organization extensions. Absent = extensions are dropped. */
|
|
969
|
+
organizationIds?: () => Promise<readonly string[]>;
|
|
970
|
+
/** The viewer's user id, stamped on every run's context. */
|
|
971
|
+
viewerId?: () => Promise<string | null>;
|
|
972
|
+
/** The runner's capabilities. `diagnostics` is required; each absent port = that capability is absent. */
|
|
973
|
+
ports: SurfacePortRunPorts;
|
|
974
|
+
/** The host's own page-Action routing. Absent = the port emits `"actions"` and waits for `completeAction`. */
|
|
975
|
+
pageActions?: PageActionPort;
|
|
976
|
+
/**
|
|
977
|
+
* The host's event hub: `value:<name>` on `setValue`, and `"actions"` for self-routed page Actions.
|
|
978
|
+
* Return `false` when nobody is subscribed to the stream — a self-routed page Action is then
|
|
979
|
+
* refused `action_unhandled` instead of waiting for an answer that cannot come.
|
|
980
|
+
*/
|
|
981
|
+
emit(stream: string, payload: unknown): void | boolean;
|
|
982
|
+
/** Values seeded at mount; a value already set is never overwritten. */
|
|
983
|
+
seedValues?: () => Promise<Record<string, unknown>>;
|
|
984
|
+
/** The surface to read when nothing is mounted yet (e.g. the Applet's own `applets/<id>`). */
|
|
985
|
+
surfaceName?: () => Promise<string>;
|
|
986
|
+
/** Most ancestors a surface may have. */
|
|
987
|
+
maxDepth?: number;
|
|
988
|
+
/** Ids for mounts (`"mount"`) and runs (`"act"`). */
|
|
989
|
+
mintId?: (prefix: string) => string;
|
|
990
|
+
/**
|
|
991
|
+
* Share ONE map across the ports of one host tree, so a frame answering on its own host settles a
|
|
992
|
+
* run another host routed to it. Absent = this port's own map.
|
|
993
|
+
*/
|
|
994
|
+
pending?: PendingPageActions;
|
|
995
|
+
}
|
|
996
|
+
interface SurfacePortHandle extends SurfacePort {
|
|
997
|
+
/** Park a page-Action run until `completeAction(runId, …)` (or `dispose()`) settles it. */
|
|
998
|
+
awaitPageAction(runId: string): Promise<ActionResult<unknown>>;
|
|
999
|
+
/** Settle every run this port parked with `host_disposed`, and forget the mount and values. */
|
|
1000
|
+
dispose(): void;
|
|
1001
|
+
}
|
|
1002
|
+
declare function createSurfacePort(opts: CreateSurfacePortOptions): SurfacePortHandle;
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* The few reads the surface runtime makes through the VIEWER's supabase client, typed STRUCTURALLY.
|
|
1006
|
+
*
|
|
1007
|
+
* Alchemy never imports supabase or the host (package rule 13). Deliberately not
|
|
1008
|
+
* `SupabaseClient<Database>` either: a generated `Database` type makes `schema().from().select()`
|
|
1009
|
+
* enormous and a host passing its typed client hits TS2589. The host hands its client in as an
|
|
1010
|
+
* `object`; the ONE cast lives in `asSurfaceClient`, and every read names its schema out loud.
|
|
1011
|
+
*
|
|
1012
|
+
* Holds no key and opens no connection: the client carries the person's session, so row security
|
|
1013
|
+
* decides every answer.
|
|
1014
|
+
*/
|
|
1015
|
+
|
|
1016
|
+
/** What a supabase-js read resolves with. */
|
|
1017
|
+
interface SurfaceReadAnswer {
|
|
1018
|
+
data: unknown;
|
|
1019
|
+
error: {
|
|
1020
|
+
message: string;
|
|
1021
|
+
code?: string;
|
|
1022
|
+
details?: string;
|
|
1023
|
+
hint?: string;
|
|
1024
|
+
} | null;
|
|
1025
|
+
}
|
|
1026
|
+
/** The filter chain the reads use — a subset of supabase-js's `PostgrestFilterBuilder`. */
|
|
1027
|
+
interface SurfaceReadChain extends PromiseLike<SurfaceReadAnswer> {
|
|
1028
|
+
eq(column: string, value: unknown): SurfaceReadChain;
|
|
1029
|
+
in(column: string, values: readonly unknown[]): SurfaceReadChain;
|
|
1030
|
+
is(column: string, value: null): SurfaceReadChain;
|
|
1031
|
+
order(column: string, options?: {
|
|
1032
|
+
ascending?: boolean;
|
|
1033
|
+
}): SurfaceReadChain;
|
|
1034
|
+
maybeSingle(): PromiseLike<SurfaceReadAnswer>;
|
|
1035
|
+
}
|
|
1036
|
+
/** The part of a supabase-js v2 client the surface runtime reads through. */
|
|
1037
|
+
interface SurfaceClient {
|
|
1038
|
+
schema(name: string): {
|
|
1039
|
+
from(table: string): {
|
|
1040
|
+
select(columns: string): SurfaceReadChain;
|
|
1041
|
+
};
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
export { APPLET_SURFACE_PREFIX, type ActionCaller, type ActionContext, type ActionInfo, type ActionResult, type CreateSurfacePortOptions, type MandateRunRef, PAGE_ACTION_STREAM, type PageActionEvent, type PendingPageActions, type ReadSurfaceChainOptions, type SurfaceClient, type SurfacePort, type SurfacePortHandle, type SurfacePortRunPorts, type SurfaceReadAnswer, type SurfaceReadChain, createSurfacePort, readSurfaceChain, surfaceActionVisible, valueStream };
|