@ai-matrx/chat 0.2.0 → 0.2.1
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 +12 -0
- package/dist/agent-copy/AlchemySurfaceBridge.d.ts +1 -1
- package/dist/agent-copy/AlchemySurfaceBridge.js +1 -1
- package/dist/agent-copy/buildAgentPayload.d.ts +1 -1
- package/dist/agent-copy/buildAgentPayload.js +1 -1
- package/dist/agent-copy/page-capture/pageCapture.d.ts +1 -1
- package/dist/agents/components/inputs/smart-input/AgentTextarea.d.ts +1 -2
- package/dist/agents/components/inputs/smart-input/AgentTextarea.js +0 -12
- package/dist/agents/components/inputs/smart-input/InputActionButtons.js +2 -2
- package/dist/agents/components/inputs/smart-input/SmartAgentInputStacked.js +3 -2
- package/dist/agents/components/inputs/smart-input/SmartInputFileDropTarget.d.ts +8 -1
- package/dist/agents/components/inputs/smart-input/SmartInputFileDropTarget.js +3 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerModeSwitch.js +10 -15
- package/dist/surfaces/manifests/_baseline.manifest.d.ts +59 -0
- package/dist/surfaces/manifests/_baseline.manifest.js +56 -1
- package/dist/surfaces/runtime/SurfaceRuntimeContext.d.ts +1 -1
- package/dist/surfaces/runtime/custom-field-targets.d.ts +6 -6
- package/dist/surfaces/runtime/custom-field-targets.js +7 -21
- package/dist/surfaces/runtime/surface-feedback.d.ts +7 -9
- package/dist/surfaces/runtime/surface-feedback.js +8 -21
- package/dist/surfaces/runtime/surface-writeback.d.ts +5 -2
- package/dist/surfaces/runtime/surface-writeback.js +171 -135
- package/dist/surfaces/runtime/window-forms.d.ts +2 -2
- package/dist/surfaces/runtime/window-forms.js +3 -9
- package/dist/utils/connectors/marks.d.ts +2 -2
- package/dist/utils/connectors/marks.js +2 -9
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
### Re-sync with matrx-frontend's packages/chat (P27)
|
|
5
|
+
|
|
6
|
+
- Carries every frontend-side change made to `packages/chat` after the 0.2.0 copy: a pasted image
|
|
7
|
+
attaches once (react-dropzone owns paste; the hand-rolled paste handler is gone; the host's
|
|
8
|
+
no-paste setting reaches the drop target), the Chat/Work/Advanced switch is the design system's
|
|
9
|
+
`SegmentedControl`, the send tooltip uses `isMacLike` and the Connect chip the one GitHub glyph,
|
|
10
|
+
surfaces import `@ai-matrx/alchemy/operate` (ALC-16/17: platform write targets, Save to
|
|
11
|
+
document/workbook through the one door, `receipt.result` carries what the page's handler made).
|
|
12
|
+
- Fixes the copy's stale host suites (they import `testing/fake-db`, as the frontend moved them).
|
|
13
|
+
- Consumer action: none.
|
|
14
|
+
|
|
3
15
|
## 0.2.0
|
|
4
16
|
### The full chat system moves in (P27, PACKAGE-INDEPENDENCE)
|
|
5
17
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type SurfaceHandle } from "@ai-matrx/
|
|
2
|
+
import { type SurfaceHandle } from "@ai-matrx/alchemy/operate";
|
|
3
3
|
import type { SurfaceScopePayload } from "../surfaces/types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Creates a handle for this exact React mount. Hook-only runtime registrations
|
|
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useId, useRef } from "react";
|
|
4
4
|
import {
|
|
5
5
|
createSurfaceTransferHandle
|
|
6
|
-
} from "@ai-matrx/
|
|
6
|
+
} from "@ai-matrx/alchemy/operate";
|
|
7
7
|
import { ContentTransferSurfaceProvider } from "@ai-matrx/design-system/content-transfer";
|
|
8
8
|
import { getSurfaceManifest } from "../host/ui-slots.js";
|
|
9
9
|
function useAlchemySurfaceHandle(surfaceName, getScope) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Compatibility import path; agent envelope serialization is package-owned. */
|
|
2
|
-
export { buildAgentPayload, fenceJsonBlock, type AgentPayloadInput, } from "@ai-matrx/
|
|
2
|
+
export { buildAgentPayload, fenceJsonBlock, type AgentPayloadInput, } from "@ai-matrx/alchemy/operate";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* sections, and the recent requests with their timings. Pure: no React, no
|
|
15
15
|
* network — the suite drives it directly.
|
|
16
16
|
*/
|
|
17
|
-
import type { AgentPayloadInput } from "@ai-matrx/
|
|
17
|
+
import type { AgentPayloadInput } from "@ai-matrx/alchemy/operate";
|
|
18
18
|
import type { AlchemyGroomerConfig } from "@ai-matrx/design-system/content-transfer";
|
|
19
19
|
export type PageCaptureKind = "table-page" | "record" | "dialog" | "admin-page";
|
|
20
20
|
/** A chosen thing, by name and id. Either may be null (not chosen / not loaded). */
|
|
@@ -21,7 +21,6 @@ interface AgentTextareaProps {
|
|
|
21
21
|
singleRow?: boolean;
|
|
22
22
|
uploadRoot?: string;
|
|
23
23
|
uploadPath?: string;
|
|
24
|
-
enablePasteImages?: boolean;
|
|
25
24
|
surfaceKey?: string;
|
|
26
25
|
/**
|
|
27
26
|
* The key an unsent draft is also kept under, so it survives a reload of a
|
|
@@ -62,5 +61,5 @@ interface AgentTextareaProps {
|
|
|
62
61
|
/** With `composerType`: each text line is one 32px composer row (splash · page). */
|
|
63
62
|
composerRows?: boolean;
|
|
64
63
|
}
|
|
65
|
-
export declare function AgentTextarea({ conversationId, compact, singleRow, uploadRoot, uploadPath,
|
|
64
|
+
export declare function AgentTextarea({ conversationId, compact, singleRow, uploadRoot, uploadPath, surfaceKey, draftAlias, disableSend, initiallyExpanded, autoFocus, showExpandToggle, placeholder, minHeightPx, flush, composerType, composerRows, maxHeightPx, textMenu, }: AgentTextareaProps): React.JSX.Element;
|
|
66
65
|
export {};
|
|
@@ -23,14 +23,12 @@ import {
|
|
|
23
23
|
selectComposerHasSomethingToSend,
|
|
24
24
|
selectIsExecuting
|
|
25
25
|
} from "../../../redux/execution-system/selectors/aggregate.selectors.js";
|
|
26
|
-
import { useClipboardPaste } from "../../../../host/ui-slots.js";
|
|
27
26
|
import { focusUnlessTypingElsewhere } from "../../../../utils/dom/focus-guard.js";
|
|
28
27
|
import {
|
|
29
28
|
composerKeyIntent,
|
|
30
29
|
intentTakesTheKey
|
|
31
30
|
} from "../../../../ui/composer/composerSubmit.js";
|
|
32
31
|
import { readVerticalChrome, snapToLineGrid } from "./textarea-line-grid.js";
|
|
33
|
-
import { usePasteImageResource } from "../resources/usePasteImageResource.js";
|
|
34
32
|
import { useInstanceInputUndoRedo } from "../../../hooks/useInstanceInputUndoRedo.js";
|
|
35
33
|
import { ComposerDraftNotice } from "./ComposerDraftNotice.js";
|
|
36
34
|
import { ComposerToolsNotice } from "./ComposerToolsNotice.js";
|
|
@@ -48,7 +46,6 @@ function AgentTextarea({
|
|
|
48
46
|
singleRow = false,
|
|
49
47
|
uploadRoot = "userContent",
|
|
50
48
|
uploadPath = "agent-attachments",
|
|
51
|
-
enablePasteImages = true,
|
|
52
49
|
surfaceKey,
|
|
53
50
|
draftAlias,
|
|
54
51
|
disableSend = false,
|
|
@@ -128,15 +125,6 @@ function AgentTextarea({
|
|
|
128
125
|
if (disableSend) return;
|
|
129
126
|
dispatch(interruptAndSend({ conversationId, surfaceKey }));
|
|
130
127
|
}, [disableSend, conversationId, surfaceKey, dispatch]);
|
|
131
|
-
const handlePasteImage = usePasteImageResource(conversationId, {
|
|
132
|
-
uploadRoot,
|
|
133
|
-
uploadPath
|
|
134
|
-
});
|
|
135
|
-
useClipboardPaste({
|
|
136
|
-
textareaRef,
|
|
137
|
-
onPasteImage: handlePasteImage,
|
|
138
|
-
disabled: !enablePasteImages
|
|
139
|
-
});
|
|
140
128
|
const handleTextChange = useCallback(
|
|
141
129
|
(value) => {
|
|
142
130
|
dispatch(
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
smartExecute,
|
|
37
37
|
cancelExecution
|
|
38
38
|
} from "../../../redux/execution-system/thunks/smart-execute.thunk.js";
|
|
39
|
+
import { isMacLike } from "../../../hooks/useAgentUndoRedo.js";
|
|
39
40
|
import { MicDeviceMenu } from "../../../../host/ui-slots.js";
|
|
40
41
|
import { Button, SplitButton } from "@ai-matrx/design-system/controls";
|
|
41
42
|
import { cn } from "@ai-matrx/design-system";
|
|
@@ -212,7 +213,6 @@ function ComposerSendSlot({
|
|
|
212
213
|
);
|
|
213
214
|
return /* @__PURE__ */ jsx(Fragment, { children: children(hasSomethingToSend) });
|
|
214
215
|
}
|
|
215
|
-
const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
216
216
|
function Keys({ keys }) {
|
|
217
217
|
return /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center gap-0.5", children: keys.map((key) => /* @__PURE__ */ jsx(
|
|
218
218
|
"kbd",
|
|
@@ -230,7 +230,7 @@ function ComposerSendButton({
|
|
|
230
230
|
disabled,
|
|
231
231
|
onSend
|
|
232
232
|
}) {
|
|
233
|
-
const mod =
|
|
233
|
+
const mod = isMacLike() ? "\u2318" : "Ctrl";
|
|
234
234
|
const rows = isExecuting ? [
|
|
235
235
|
{ label: "Queue for when it finishes", keys: submitOnEnter ? ["\u21B5"] : [mod, "\u21B5"] },
|
|
236
236
|
...submitOnEnter ? [{ label: "Steer in now", keys: [mod, "\u21B5"] }] : [],
|
|
@@ -77,6 +77,7 @@ function SmartAgentInputStacked({
|
|
|
77
77
|
conversationId,
|
|
78
78
|
uploadRoot,
|
|
79
79
|
uploadPath,
|
|
80
|
+
pasteFiles: enablePasteImages,
|
|
80
81
|
className: cn(
|
|
81
82
|
"relative flex w-full min-h-0 flex-col gap-1.5 border border-border bg-card transition-colors focus-within:border-foreground/25",
|
|
82
83
|
"[&_[data-variable-row]]:px-1.5 [&_[data-variable-heading]]:px-1.5 [&>input[type=file]]:!hidden",
|
|
@@ -152,7 +153,6 @@ function SmartAgentInputStacked({
|
|
|
152
153
|
compact: isCompact,
|
|
153
154
|
uploadRoot,
|
|
154
155
|
uploadPath,
|
|
155
|
-
enablePasteImages,
|
|
156
156
|
surfaceKey,
|
|
157
157
|
disableSend: sendBlocked,
|
|
158
158
|
initiallyExpanded: expandRequestKey > 0,
|
|
@@ -192,6 +192,7 @@ function SmartAgentInputStacked({
|
|
|
192
192
|
conversationId,
|
|
193
193
|
uploadRoot,
|
|
194
194
|
uploadPath,
|
|
195
|
+
pasteFiles: enablePasteImages,
|
|
195
196
|
className: cn(
|
|
196
197
|
"relative flex w-full min-h-0 flex-col gap-1 rounded-[18px] border p-1.5",
|
|
197
198
|
"border-glass-edge bg-glass shadow-glass backdrop-blur-glass backdrop-saturate-glass transition-[border-color,background-color,box-shadow]",
|
|
@@ -230,7 +231,6 @@ function SmartAgentInputStacked({
|
|
|
230
231
|
compact: true,
|
|
231
232
|
uploadRoot,
|
|
232
233
|
uploadPath,
|
|
233
|
-
enablePasteImages,
|
|
234
234
|
surfaceKey,
|
|
235
235
|
disableSend: sendBlocked,
|
|
236
236
|
autoFocus: false,
|
|
@@ -280,6 +280,7 @@ function SmartAgentInputStacked({
|
|
|
280
280
|
conversationId,
|
|
281
281
|
uploadRoot,
|
|
282
282
|
uploadPath,
|
|
283
|
+
pasteFiles: enablePasteImages,
|
|
283
284
|
className: cardClassName,
|
|
284
285
|
children: [
|
|
285
286
|
/* @__PURE__ */ jsx(
|
|
@@ -2,7 +2,14 @@ interface SmartInputFileDropTargetProps extends Omit<React.ComponentPropsWithout
|
|
|
2
2
|
conversationId: string;
|
|
3
3
|
uploadRoot?: string;
|
|
4
4
|
uploadPath?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Pasted files (a screenshot, Cmd/Ctrl+V of a copied file) attach through
|
|
7
|
+
* the same upload as a drop. react-dropzone OWNS paste-to-upload (v20+,
|
|
8
|
+
* on by default); nothing else in the input may also catch the paste, or
|
|
9
|
+
* every pasted image attaches twice (2026-10-06). False = paste stays text.
|
|
10
|
+
*/
|
|
11
|
+
pasteFiles?: boolean;
|
|
5
12
|
children: React.ReactNode;
|
|
6
13
|
}
|
|
7
|
-
export declare function SmartInputFileDropTarget({ conversationId, uploadRoot, uploadPath, className, children, ...rootProps }: SmartInputFileDropTargetProps): import("react").JSX.Element;
|
|
14
|
+
export declare function SmartInputFileDropTarget({ conversationId, uploadRoot, uploadPath, pasteFiles, className, children, ...rootProps }: SmartInputFileDropTargetProps): import("react").JSX.Element;
|
|
8
15
|
export {};
|
|
@@ -8,6 +8,7 @@ function SmartInputFileDropTarget({
|
|
|
8
8
|
conversationId,
|
|
9
9
|
uploadRoot,
|
|
10
10
|
uploadPath,
|
|
11
|
+
pasteFiles = true,
|
|
11
12
|
className,
|
|
12
13
|
children,
|
|
13
14
|
...rootProps
|
|
@@ -19,7 +20,8 @@ function SmartInputFileDropTarget({
|
|
|
19
20
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
20
21
|
onDrop: (files) => void uploadResources(files),
|
|
21
22
|
noClick: true,
|
|
22
|
-
noKeyboard: true
|
|
23
|
+
noKeyboard: true,
|
|
24
|
+
noPaste: !pasteFiles
|
|
23
25
|
});
|
|
24
26
|
return /* @__PURE__ */ jsxs(
|
|
25
27
|
"div",
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
COMPOSER_MODE_LABELS
|
|
17
17
|
} from "./composer-types.js";
|
|
18
18
|
import { useComposerMode } from "./useComposerMode.js";
|
|
19
|
-
import {
|
|
19
|
+
import { SegmentedControl } from "@ai-matrx/design-system/controls";
|
|
20
20
|
function ComposerModeSwitch({
|
|
21
21
|
initialMode,
|
|
22
22
|
size = "bar",
|
|
@@ -30,20 +30,15 @@ function ComposerModeSwitch({
|
|
|
30
30
|
const fit = useCenterControlFit(cellRef, [fullRef, triggerRef], mode);
|
|
31
31
|
const measured = useSyncExternalStore(noopSubscribe, () => true, () => false);
|
|
32
32
|
const renderSegments = (interactive) => /* @__PURE__ */ jsx(
|
|
33
|
-
|
|
33
|
+
SegmentedControl,
|
|
34
34
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
),
|
|
43
|
-
children: COMPOSER_MODES.map((value) => {
|
|
44
|
-
const on = value === mode;
|
|
45
|
-
return /* @__PURE__ */ jsx(Button, { variant: "quiet", pressed: !!on, role: interactive ? "tab" : void 0, "aria-selected": interactive ? on : void 0, "aria-pressed": interactive ? void 0 : on, tabIndex: interactive ? void 0 : -1, onClick: interactive ? () => setMode(value) : void 0, children: COMPOSER_MODE_LABELS[value] }, value);
|
|
46
|
-
})
|
|
35
|
+
"aria-label": "Composer mode",
|
|
36
|
+
value: mode,
|
|
37
|
+
onValueChange: (value) => {
|
|
38
|
+
if (interactive) setMode(value);
|
|
39
|
+
},
|
|
40
|
+
data: COMPOSER_MODES.map((value) => ({ value, label: COMPOSER_MODE_LABELS[value] })),
|
|
41
|
+
className: cn("shrink-0", !interactive && "w-max max-w-none", className)
|
|
47
42
|
}
|
|
48
43
|
);
|
|
49
44
|
if (panel) return renderSegments(true);
|
|
@@ -61,7 +56,7 @@ function ComposerModeSwitch({
|
|
|
61
56
|
className: "relative flex w-full min-w-0 justify-center",
|
|
62
57
|
"data-route-nav-inflow": fit.inflow ? "" : void 0,
|
|
63
58
|
children: [
|
|
64
|
-
/* @__PURE__ */ jsxs("div", { "aria-hidden": true, className: "pointer-events-none invisible absolute left-0 top-0", children: [
|
|
59
|
+
/* @__PURE__ */ jsxs("div", { "aria-hidden": true, inert: true, className: "pointer-events-none invisible absolute left-0 top-0", children: [
|
|
65
60
|
/* @__PURE__ */ jsx("div", { ref: fullRef, className: "w-max max-w-none", children: renderSegments(false) }),
|
|
66
61
|
/* @__PURE__ */ jsx("span", { ref: triggerRef, "data-route-nav-min": true, className: cn(triggerClass, "w-max max-w-none"), children: triggerFace })
|
|
67
62
|
] }),
|
|
@@ -150,6 +150,65 @@ export declare const PLATFORM_CONTEXT_VALUES: {
|
|
|
150
150
|
readonly sortOrder: 9997;
|
|
151
151
|
};
|
|
152
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* THE BASELINE SURFACE — where the platform's own write targets are declared
|
|
155
|
+
* (Matrx Alchemy ALC-17). Every surface inherits it: the one write door reads
|
|
156
|
+
* it as the root ancestor of every surface (`surfaceWriteDeclarations` in
|
|
157
|
+
* `runtime/surface-writeback.ts`), so a write to a platform target from ANY
|
|
158
|
+
* surface resolves here, and the platform's headless handlers are registered
|
|
159
|
+
* on this name. It is not a page and is never mounted.
|
|
160
|
+
*/
|
|
161
|
+
export declare const BASELINE_SURFACE_NAME = "matrx-platform/baseline";
|
|
162
|
+
/** `surface_feedback` value bounds — the description and the check read the same numbers. */
|
|
163
|
+
export declare const SURFACE_FEEDBACK_KINDS: readonly ["missing_capability", "wrong_or_unclear_description", "bug", "missing_data", "suggestion"];
|
|
164
|
+
export declare const SURFACE_FEEDBACK_MESSAGE_MIN = 10;
|
|
165
|
+
export declare const SURFACE_FEEDBACK_MESSAGE_MAX = 4000;
|
|
166
|
+
/** At most this many custom fields per `custom_fields_add` write. */
|
|
167
|
+
export declare const CUSTOM_FIELDS_MAX_PER_WRITE = 10;
|
|
168
|
+
/**
|
|
169
|
+
* THE PLATFORM WRITE TARGETS, declared once on the baseline surface. Each has
|
|
170
|
+
* the apply policy it always had: window fields and custom fields are `ask`
|
|
171
|
+
* (an agent's write waits for the person's card; a person's own write is the
|
|
172
|
+
* approval); feedback is `auto` (it changes nothing on the page). The runtime
|
|
173
|
+
* files word the live offer from these (`customFieldsTarget()` adds the
|
|
174
|
+
* mounted sections), and register the handlers that apply them.
|
|
175
|
+
*/
|
|
176
|
+
export declare const PLATFORM_WRITE_TARGETS: {
|
|
177
|
+
readonly window_form_fields: {
|
|
178
|
+
readonly name: "window_form_fields";
|
|
179
|
+
readonly label: "Fields in an open window";
|
|
180
|
+
readonly description: 'Change fields in an open window that has no registered surface (listed in your context as window::<title>). Value: { "window": "<title exactly as listed>", "changes": [{ "field": "<field key>", "value": <new value> }] }. A text or number field takes a string or number, a checkbox or switch takes true/false, a list takes the text of one of its choices (a wrong choice is refused with the real ones). Every change is checked against the field\'s rules before anything lands; the person approves first, and the window\'s own Save still decides.';
|
|
181
|
+
readonly valueType: "object";
|
|
182
|
+
readonly mode: "draft";
|
|
183
|
+
readonly applyPolicy: "ask";
|
|
184
|
+
};
|
|
185
|
+
readonly surface_feedback: {
|
|
186
|
+
readonly name: "surface_feedback";
|
|
187
|
+
readonly label: "Feedback about this page";
|
|
188
|
+
readonly description: string;
|
|
189
|
+
readonly valueType: "object";
|
|
190
|
+
readonly mode: "entity";
|
|
191
|
+
readonly applyPolicy: "auto";
|
|
192
|
+
};
|
|
193
|
+
readonly custom_fields_add: {
|
|
194
|
+
readonly name: "custom_fields_add";
|
|
195
|
+
readonly label: "Add custom fields";
|
|
196
|
+
readonly description: string;
|
|
197
|
+
readonly valueType: "object";
|
|
198
|
+
readonly mode: "entity";
|
|
199
|
+
readonly applyPolicy: "ask";
|
|
200
|
+
};
|
|
201
|
+
readonly custom_fields_set: {
|
|
202
|
+
readonly name: "custom_fields_set";
|
|
203
|
+
readonly label: "Fill in custom fields";
|
|
204
|
+
readonly description: string;
|
|
205
|
+
readonly valueType: "object";
|
|
206
|
+
readonly mode: "entity";
|
|
207
|
+
readonly applyPolicy: "ask";
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
export type PlatformWriteTargetName = keyof typeof PLATFORM_WRITE_TARGETS;
|
|
211
|
+
export declare function isPlatformWriteTarget(name: string): name is PlatformWriteTargetName;
|
|
153
212
|
/** Names only the platform writes — a manifest declaring one is refused. */
|
|
154
213
|
export declare const PLATFORM_RESERVED_NAMES: {
|
|
155
214
|
values: ReadonlyArray<string>;
|
|
@@ -101,9 +101,57 @@ const PLATFORM_CONTEXT_VALUES = {
|
|
|
101
101
|
sortOrder: 9997
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
+
const BASELINE_SURFACE_NAME = "matrx-platform/baseline";
|
|
105
|
+
const SURFACE_FEEDBACK_KINDS = [
|
|
106
|
+
"missing_capability",
|
|
107
|
+
"wrong_or_unclear_description",
|
|
108
|
+
"bug",
|
|
109
|
+
"missing_data",
|
|
110
|
+
"suggestion"
|
|
111
|
+
];
|
|
112
|
+
const SURFACE_FEEDBACK_MESSAGE_MIN = 10;
|
|
113
|
+
const SURFACE_FEEDBACK_MESSAGE_MAX = 4e3;
|
|
114
|
+
const CUSTOM_FIELDS_MAX_PER_WRITE = 10;
|
|
115
|
+
const PLATFORM_WRITE_TARGETS = {
|
|
116
|
+
window_form_fields: {
|
|
117
|
+
name: "window_form_fields",
|
|
118
|
+
label: "Fields in an open window",
|
|
119
|
+
description: `Change fields in an open window that has no registered surface (listed in your context as window::<title>). Value: { "window": "<title exactly as listed>", "changes": [{ "field": "<field key>", "value": <new value> }] }. A text or number field takes a string or number, a checkbox or switch takes true/false, a list takes the text of one of its choices (a wrong choice is refused with the real ones). Every change is checked against the field's rules before anything lands; the person approves first, and the window's own Save still decides.`,
|
|
120
|
+
valueType: "object",
|
|
121
|
+
mode: "draft",
|
|
122
|
+
applyPolicy: "ask"
|
|
123
|
+
},
|
|
124
|
+
surface_feedback: {
|
|
125
|
+
name: "surface_feedback",
|
|
126
|
+
label: "Feedback about this page",
|
|
127
|
+
description: `Save feedback about this page (surface) for the team that builds it \u2014 it changes NOTHING on the page and nobody is asked. Use it when something about the page got in your way: a write you needed does not exist, a description was wrong or unclear, data you needed was missing or arrived in an awkward form, or something broke. Also use it when the person asks you to give feedback on this page. Value: { "kind": "${SURFACE_FEEDBACK_KINDS.join(" | ")}", "message": "<what happened and what would have helped, ${SURFACE_FEEDBACK_MESSAGE_MIN}-${SURFACE_FEEDBACK_MESSAGE_MAX} characters>", "target_or_value": "<optional: the write target or value name it concerns>" }. It is filed for the surface named on this line; pass \`surface\` to file it for another open surface instead.`,
|
|
128
|
+
valueType: "object",
|
|
129
|
+
mode: "entity",
|
|
130
|
+
applyPolicy: "auto"
|
|
131
|
+
},
|
|
132
|
+
custom_fields_add: {
|
|
133
|
+
name: "custom_fields_add",
|
|
134
|
+
label: "Add custom fields",
|
|
135
|
+
description: `Add custom fields (new columns) to the kind of record shown on this page \u2014 SAVED immediately after the person approves, and every record of that kind gets the field (empty until filled in). Value: { "entity": "<entity token, required only when more than one section is listed>", "fields": [{ "label": "<the field's name as a person reads it>", "type": "<one of the types below; default text>" }] } \u2014 1 to ${CUSTOM_FIELDS_MAX_PER_WRITE} fields. Refused before the person is asked: a name already used, a name asked for twice, an unknown type, or a person who may not add fields (the store's reason is returned). This adds the FIELD only; it does not fill in a value.`,
|
|
136
|
+
valueType: "object",
|
|
137
|
+
mode: "entity",
|
|
138
|
+
applyPolicy: "ask"
|
|
139
|
+
},
|
|
140
|
+
custom_fields_set: {
|
|
141
|
+
name: "custom_fields_set",
|
|
142
|
+
label: "Fill in custom fields",
|
|
143
|
+
description: `Set this record's custom-field values \u2014 SAVED immediately after the person approves; a field left out is left alone. Value: { "entity": "<entity token, only when more than one section is listed>", "values": { "<field name or key>": <value> } }. A number field takes a number, a date field an ISO date (YYYY-MM-DD), text fields a string; null clears a value. Refused before the person is asked: a field that does not exist (the existing ones are named), or a person who may not fill them in. To add a NEW field use custom_fields_add.`,
|
|
144
|
+
valueType: "object",
|
|
145
|
+
mode: "entity",
|
|
146
|
+
applyPolicy: "ask"
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function isPlatformWriteTarget(name) {
|
|
150
|
+
return Object.prototype.hasOwnProperty.call(PLATFORM_WRITE_TARGETS, name);
|
|
151
|
+
}
|
|
104
152
|
const PLATFORM_RESERVED_NAMES = {
|
|
105
153
|
values: Object.keys(PLATFORM_CONTEXT_VALUES),
|
|
106
|
-
writeTargets:
|
|
154
|
+
writeTargets: Object.keys(PLATFORM_WRITE_TARGETS)
|
|
107
155
|
};
|
|
108
156
|
const PAGE_OFF_WITHHELD = [
|
|
109
157
|
"route_brief",
|
|
@@ -140,16 +188,23 @@ function withAllBaselines(surfaceSpecific) {
|
|
|
140
188
|
}
|
|
141
189
|
export {
|
|
142
190
|
AMBIENT_VALUES,
|
|
191
|
+
BASELINE_SURFACE_NAME,
|
|
143
192
|
BASELINE_VALUES,
|
|
144
193
|
BASELINE_VALUE_NAMES,
|
|
194
|
+
CUSTOM_FIELDS_MAX_PER_WRITE,
|
|
145
195
|
OWN_CONVERSATION_WITHHELD,
|
|
146
196
|
PAGELESS_CONTENT_INLINE_CEILING,
|
|
147
197
|
PAGE_OFF_WITHHELD,
|
|
148
198
|
PERSON_CONTEXT_VALUES,
|
|
149
199
|
PLATFORM_CONTEXT_VALUES,
|
|
150
200
|
PLATFORM_RESERVED_NAMES,
|
|
201
|
+
PLATFORM_WRITE_TARGETS,
|
|
151
202
|
POINTER_INLINE_CEILINGS,
|
|
203
|
+
SURFACE_FEEDBACK_KINDS,
|
|
204
|
+
SURFACE_FEEDBACK_MESSAGE_MAX,
|
|
205
|
+
SURFACE_FEEDBACK_MESSAGE_MIN,
|
|
152
206
|
allBaseline,
|
|
207
|
+
isPlatformWriteTarget,
|
|
153
208
|
mergeBaselineValues,
|
|
154
209
|
pickBaseline,
|
|
155
210
|
withAllBaselines
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { SurfaceScopePayload } from "../types.js";
|
|
3
3
|
import type { InstanceContextEntry } from "../../agents/types/instance.types.js";
|
|
4
|
-
import type { SurfaceHandle } from "@ai-matrx/
|
|
4
|
+
import type { SurfaceHandle } from "@ai-matrx/alchemy/operate";
|
|
5
5
|
import type { ApplySurfaceWriteOptions } from "./surface-writeback.js";
|
|
6
6
|
/**
|
|
7
7
|
* What a write handler may RETURN so the caller (and, for an agent write, the
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* that page could add a custom field; an agent on the same page could not —
|
|
9
9
|
* nothing offered it. Declaring a target on every manifest would be 643 copies
|
|
10
10
|
* of one sentence, so this is a PLATFORM target like `surface_feedback` and
|
|
11
|
-
* `window_form_fields`: declared
|
|
12
|
-
* whenever at least one
|
|
13
|
-
*
|
|
11
|
+
* `window_form_fields`: declared once on the baseline surface every surface
|
|
12
|
+
* inherits (`_baseline.manifest.ts`), offered whenever at least one
|
|
13
|
+
* custom-fields section is mounted, and written through the one write door
|
|
14
|
+
* (`surface-writeback.ts` `applyPlatformWrite`) to the section's own door.
|
|
14
15
|
*
|
|
15
16
|
* The section owns the write: it hands its host a door (records-ui
|
|
16
17
|
* `agentDoor` prop → `CustomFieldsAgentDoor`), which checks the whole request
|
|
@@ -23,11 +24,10 @@
|
|
|
23
24
|
*/
|
|
24
25
|
import type { SurfaceWriteTarget } from "../types.js";
|
|
25
26
|
import type { SurfaceWriteOutcome } from "./SurfaceRuntimeContext.js";
|
|
26
|
-
export declare const CUSTOM_FIELDS_TARGET_NAME
|
|
27
|
-
export declare const CUSTOM_FIELDS_SET_TARGET_NAME
|
|
27
|
+
export declare const CUSTOM_FIELDS_TARGET_NAME: "custom_fields_add";
|
|
28
|
+
export declare const CUSTOM_FIELDS_SET_TARGET_NAME: "custom_fields_set";
|
|
28
29
|
/** The surface value a section contributes when its surface declares it (baseline `custom_fields`). */
|
|
29
30
|
export declare const CUSTOM_FIELDS_VALUE_NAME = "custom_fields";
|
|
30
|
-
export declare const CUSTOM_FIELDS_MAX_PER_WRITE = 10;
|
|
31
31
|
export declare function providerOwnsCustomFields(surfaceName: string): boolean;
|
|
32
32
|
export interface CustomFieldAddRequest {
|
|
33
33
|
label: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
1
|
+
import { CUSTOM_FIELDS_MAX_PER_WRITE, PLATFORM_WRITE_TARGETS } from "../manifests/_baseline.manifest.js";
|
|
2
|
+
const CUSTOM_FIELDS_TARGET_NAME = PLATFORM_WRITE_TARGETS.custom_fields_add.name;
|
|
3
|
+
const CUSTOM_FIELDS_SET_TARGET_NAME = PLATFORM_WRITE_TARGETS.custom_fields_set.name;
|
|
3
4
|
const CUSTOM_FIELDS_VALUE_NAME = "custom_fields";
|
|
4
|
-
const CUSTOM_FIELDS_MAX_PER_WRITE = 10;
|
|
5
5
|
const PROVIDER_OWNED_CUSTOM_FIELDS = /* @__PURE__ */ new Set(["matrx-user/notes"]);
|
|
6
6
|
function providerOwnsCustomFields(surfaceName) {
|
|
7
7
|
return PROVIDER_OWNED_CUSTOM_FIELDS.has(surfaceName);
|
|
@@ -31,20 +31,13 @@ function describeDoor(door) {
|
|
|
31
31
|
const may = s.mayAdd === true ? "you may add" : s.mayAdd === null ? "still checking who may add" : `adding is refused: ${s.refusal ?? "this person may not add a field here"}`;
|
|
32
32
|
return `entity "${s.entityToken}" (${what}, record ${s.recordId}) \u2014 fields: ${held}; ${may}`;
|
|
33
33
|
}
|
|
34
|
-
const BASE_DESCRIPTION = `Add custom fields (new columns) to the kind of record shown on this page \u2014 SAVED immediately after the person approves, and every record of that kind gets the field (empty until filled in). Value: { "entity": "<entity token, required only when more than one section is listed>", "fields": [{ "label": "<the field's name as a person reads it>", "type": "<one of the types below; default text>" }] } \u2014 1 to ${CUSTOM_FIELDS_MAX_PER_WRITE} fields. Refused before the person is asked: a name already used, a name asked for twice, an unknown type, or a person who may not add fields (the store's reason is returned). This adds the FIELD only; it does not fill in a value.`;
|
|
35
34
|
function customFieldsTarget() {
|
|
36
35
|
const mounted = listCustomFieldsDoors();
|
|
37
36
|
const types = mounted[0]?.state().types ?? [];
|
|
38
37
|
const typeLine = types.length > 0 ? ` Types: ${types.map((t) => `${t.value} (${t.label})`).join(", ")}.` : "";
|
|
39
38
|
const sections = mounted.length > 0 ? ` Sections on this page: ${mounted.map(describeDoor).join(" | ")}.` : "";
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
label: "Add custom fields",
|
|
43
|
-
description: `${BASE_DESCRIPTION}${typeLine}${sections}`,
|
|
44
|
-
valueType: "object",
|
|
45
|
-
mode: "entity",
|
|
46
|
-
applyPolicy: "ask"
|
|
47
|
-
};
|
|
39
|
+
const declared = PLATFORM_WRITE_TARGETS.custom_fields_add;
|
|
40
|
+
return { ...declared, description: `${declared.description}${typeLine}${sections}` };
|
|
48
41
|
}
|
|
49
42
|
function customFieldsProblems(value, mounted = listCustomFieldsDoors()) {
|
|
50
43
|
const none = { door: null, fields: [] };
|
|
@@ -151,14 +144,8 @@ function hasCustomFieldsSetDoors() {
|
|
|
151
144
|
function customFieldsSetTarget() {
|
|
152
145
|
const mounted = listCustomFieldsDoors().filter((d) => typeof d.setValues === "function");
|
|
153
146
|
const sections = mounted.length > 0 ? ` Sections on this page: ${mounted.map(describeDoor).join(" | ")}.` : "";
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
label: "Fill in custom fields",
|
|
157
|
-
description: `Set this record's custom-field values \u2014 SAVED immediately after the person approves; a field left out is left alone. Value: { "entity": "<entity token, only when more than one section is listed>", "values": { "<field name or key>": <value> } }. A number field takes a number, a date field an ISO date (YYYY-MM-DD), text fields a string; null clears a value. Refused before the person is asked: a field that does not exist (the existing ones are named), or a person who may not fill them in. To add a NEW field use ${CUSTOM_FIELDS_TARGET_NAME}.${sections}`,
|
|
158
|
-
valueType: "object",
|
|
159
|
-
mode: "entity",
|
|
160
|
-
applyPolicy: "ask"
|
|
161
|
-
};
|
|
147
|
+
const declared = PLATFORM_WRITE_TARGETS.custom_fields_set;
|
|
148
|
+
return { ...declared, description: `${declared.description}${sections}` };
|
|
162
149
|
}
|
|
163
150
|
function customFieldsSetProblems(value, mounted = listCustomFieldsDoors().filter((d) => typeof d.setValues === "function")) {
|
|
164
151
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -197,7 +184,6 @@ async function applyCustomFieldsSetWrite(value) {
|
|
|
197
184
|
};
|
|
198
185
|
}
|
|
199
186
|
export {
|
|
200
|
-
CUSTOM_FIELDS_MAX_PER_WRITE,
|
|
201
187
|
CUSTOM_FIELDS_SET_TARGET_NAME,
|
|
202
188
|
CUSTOM_FIELDS_TARGET_NAME,
|
|
203
189
|
CUSTOM_FIELDS_VALUE_NAME,
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* exam dates", "create_classes never said X"). The team reads it the next time
|
|
8
8
|
* it updates that surface (`pnpm surface:feedback --surface <name>`).
|
|
9
9
|
*
|
|
10
|
-
* Like `window_form_fields` (`window-forms.ts`) it is declared
|
|
11
|
-
*
|
|
12
|
-
* one registered surface is mounted, and
|
|
13
|
-
*
|
|
10
|
+
* Like `window_form_fields` (`window-forms.ts`) it is declared once on the
|
|
11
|
+
* baseline surface (`_baseline.manifest.ts`): `listLiveWriteTargets` offers it
|
|
12
|
+
* whenever at least one registered surface is mounted, and it is written
|
|
13
|
+
* through the one write door (`surface-writeback.ts` `applyPlatformWrite`). It changes NOTHING on the page, so
|
|
14
14
|
* it is `auto` — the person is never interrupted by a card for it.
|
|
15
15
|
*
|
|
16
16
|
* Storage is the central triage table `users.user_feedback`, written through
|
|
@@ -20,18 +20,16 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { ChatFeedbackInput, ChatFeedbackType } from "../../host/contract.js";
|
|
22
22
|
import type { SurfaceWriteTarget } from "../types.js";
|
|
23
|
+
import { SURFACE_FEEDBACK_KINDS } from "../manifests/_baseline.manifest.js";
|
|
23
24
|
import type { SurfaceWriteOutcome } from "./SurfaceRuntimeContext.js";
|
|
24
|
-
export declare const SURFACE_FEEDBACK_TARGET_NAME
|
|
25
|
-
export declare const SURFACE_FEEDBACK_KINDS: readonly ["missing_capability", "wrong_or_unclear_description", "bug", "missing_data", "suggestion"];
|
|
25
|
+
export declare const SURFACE_FEEDBACK_TARGET_NAME: "surface_feedback";
|
|
26
26
|
export type SurfaceFeedbackKind = (typeof SURFACE_FEEDBACK_KINDS)[number];
|
|
27
|
-
export declare const SURFACE_FEEDBACK_MESSAGE_MIN = 10;
|
|
28
|
-
export declare const SURFACE_FEEDBACK_MESSAGE_MAX = 4000;
|
|
29
27
|
export interface SurfaceFeedbackValue {
|
|
30
28
|
kind: SurfaceFeedbackKind;
|
|
31
29
|
message: string;
|
|
32
30
|
target_or_value?: string;
|
|
33
31
|
}
|
|
34
|
-
/** The platform target — declared once; offered whenever a surface is mounted. */
|
|
32
|
+
/** The platform target — declared once on the baseline surface (`_baseline.manifest.ts`); offered whenever a surface is mounted. */
|
|
35
33
|
export declare const SURFACE_FEEDBACK_TARGET: SurfaceWriteTarget;
|
|
36
34
|
/**
|
|
37
35
|
* Pre-write check. Throws a sentence the agent can act on; nothing is written
|
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
import { getChatHost } from "../../host/configure.js";
|
|
2
2
|
import { getStoreSingleton } from "../../store/store-singleton.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
];
|
|
11
|
-
const SURFACE_FEEDBACK_MESSAGE_MIN = 10;
|
|
12
|
-
const SURFACE_FEEDBACK_MESSAGE_MAX = 4e3;
|
|
3
|
+
import {
|
|
4
|
+
PLATFORM_WRITE_TARGETS,
|
|
5
|
+
SURFACE_FEEDBACK_KINDS,
|
|
6
|
+
SURFACE_FEEDBACK_MESSAGE_MAX,
|
|
7
|
+
SURFACE_FEEDBACK_MESSAGE_MIN
|
|
8
|
+
} from "../manifests/_baseline.manifest.js";
|
|
9
|
+
const SURFACE_FEEDBACK_TARGET_NAME = PLATFORM_WRITE_TARGETS.surface_feedback.name;
|
|
13
10
|
const KIND_LIST = SURFACE_FEEDBACK_KINDS.join(" | ");
|
|
14
|
-
const SURFACE_FEEDBACK_TARGET =
|
|
15
|
-
name: SURFACE_FEEDBACK_TARGET_NAME,
|
|
16
|
-
label: "Feedback about this page",
|
|
17
|
-
description: `Save feedback about this page (surface) for the team that builds it \u2014 it changes NOTHING on the page and nobody is asked. Use it when something about the page got in your way: a write you needed does not exist, a description was wrong or unclear, data you needed was missing or arrived in an awkward form, or something broke. Also use it when the person asks you to give feedback on this page. Value: { "kind": "${KIND_LIST}", "message": "<what happened and what would have helped, ${SURFACE_FEEDBACK_MESSAGE_MIN}-${SURFACE_FEEDBACK_MESSAGE_MAX} characters>", "target_or_value": "<optional: the write target or value name it concerns>" }. It is filed for the surface named on this line; pass \`surface\` to file it for another open surface instead.`,
|
|
18
|
-
valueType: "object",
|
|
19
|
-
mode: "entity",
|
|
20
|
-
applyPolicy: "auto"
|
|
21
|
-
};
|
|
11
|
+
const SURFACE_FEEDBACK_TARGET = PLATFORM_WRITE_TARGETS.surface_feedback;
|
|
22
12
|
const ALLOWED_KEYS = /* @__PURE__ */ new Set(["kind", "message", "target_or_value"]);
|
|
23
13
|
function validateSurfaceFeedback(value) {
|
|
24
14
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -101,9 +91,6 @@ async function saveSurfaceFeedback(value, ctx) {
|
|
|
101
91
|
};
|
|
102
92
|
}
|
|
103
93
|
export {
|
|
104
|
-
SURFACE_FEEDBACK_KINDS,
|
|
105
|
-
SURFACE_FEEDBACK_MESSAGE_MAX,
|
|
106
|
-
SURFACE_FEEDBACK_MESSAGE_MIN,
|
|
107
94
|
SURFACE_FEEDBACK_TARGET,
|
|
108
95
|
SURFACE_FEEDBACK_TARGET_NAME,
|
|
109
96
|
buildSurfaceFeedbackInput,
|
|
@@ -233,8 +233,11 @@ export declare function registerSurfaceWritePolicies(policies: WritePolicyMap, k
|
|
|
233
233
|
export declare const surfaceWriteApprovals: ApprovalPort;
|
|
234
234
|
/**
|
|
235
235
|
* The declarations the door reads: every manifest, with each target's
|
|
236
|
-
* EFFECTIVE policy
|
|
237
|
-
*
|
|
236
|
+
* EFFECTIVE policy, plus the BASELINE surface (`BASELINE_SURFACE_NAME`), which
|
|
237
|
+
* declares the platform's own targets (`PLATFORM_WRITE_TARGETS`). The only
|
|
238
|
+
* ancestry is the baseline: a resolved manifest already carries what it
|
|
239
|
+
* inherits from its parents, so a surface's own target resolves on the surface
|
|
240
|
+
* that is writing, and a platform target on the baseline every surface sits on.
|
|
238
241
|
*/
|
|
239
242
|
export declare const surfaceWriteDeclarations: WriteDoorDeclarations;
|
|
240
243
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { contentIrKindValidator } from "../../host/content-ir-slots.js";
|
|
3
3
|
import { getManifest } from "./registry.js";
|
|
4
|
+
import {
|
|
5
|
+
BASELINE_SURFACE_NAME,
|
|
6
|
+
isPlatformWriteTarget,
|
|
7
|
+
PLATFORM_WRITE_TARGETS
|
|
8
|
+
} from "../manifests/_baseline.manifest.js";
|
|
4
9
|
import {
|
|
5
10
|
isSurfaceWritePatch,
|
|
6
11
|
resolveSurfaceWritePatch
|
|
@@ -350,6 +355,9 @@ const surfaceWriteKinds = {
|
|
|
350
355
|
};
|
|
351
356
|
const surfaceWriteDeclarations = {
|
|
352
357
|
get(surfaceName) {
|
|
358
|
+
if (surfaceName === BASELINE_SURFACE_NAME) {
|
|
359
|
+
return { writeTargets: Object.values(PLATFORM_WRITE_TARGETS) };
|
|
360
|
+
}
|
|
353
361
|
const manifest = getManifest(surfaceName);
|
|
354
362
|
if (!manifest) return void 0;
|
|
355
363
|
return {
|
|
@@ -359,6 +367,9 @@ const surfaceWriteDeclarations = {
|
|
|
359
367
|
})),
|
|
360
368
|
...manifest.itemTypes ? { itemTypes: manifest.itemTypes } : {}
|
|
361
369
|
};
|
|
370
|
+
},
|
|
371
|
+
ancestry(surfaceName) {
|
|
372
|
+
return surfaceName === BASELINE_SURFACE_NAME ? [] : [BASELINE_SURFACE_NAME];
|
|
362
373
|
}
|
|
363
374
|
};
|
|
364
375
|
let surfaceDoor = null;
|
|
@@ -375,6 +386,7 @@ function loadSurfaceWriteDoor() {
|
|
|
375
386
|
})
|
|
376
387
|
).then((door) => {
|
|
377
388
|
routeMountedPages(door);
|
|
389
|
+
registerPlatformHandlers(door);
|
|
378
390
|
return door;
|
|
379
391
|
});
|
|
380
392
|
surfaceDoor = loading;
|
|
@@ -444,6 +456,7 @@ function routeMountedPages(door) {
|
|
|
444
456
|
const routePageWrite = (request) => {
|
|
445
457
|
const seam = request.runId ? seamWrites.get(request.runId) : void 0;
|
|
446
458
|
if (seam) return seam.apply();
|
|
459
|
+
if (isPlatformWriteTarget(request.target.name)) return applyPlatformHeadless(request);
|
|
447
460
|
return applyMountedPageWrite(request);
|
|
448
461
|
};
|
|
449
462
|
async function applyMountedPageWrite(request) {
|
|
@@ -462,7 +475,12 @@ async function applyMountedPageWrite(request) {
|
|
|
462
475
|
}
|
|
463
476
|
if (handler.validate) await handler.validate(request.value);
|
|
464
477
|
const outcome = toWriteOutcome(await handler.apply(request.value));
|
|
465
|
-
return {
|
|
478
|
+
return {
|
|
479
|
+
status: "applied",
|
|
480
|
+
...outcome?.summary ? { sentence: outcome.summary } : {},
|
|
481
|
+
// What the page's handler produced (ids of created records) rides the receipt (alchemy 0.12.3).
|
|
482
|
+
...outcome?.data !== void 0 ? { result: outcome.data } : {}
|
|
483
|
+
};
|
|
466
484
|
}
|
|
467
485
|
async function writeThroughDoor(request, conversationId, apply, approve) {
|
|
468
486
|
const door = await loadSurfaceWriteDoor();
|
|
@@ -743,17 +761,8 @@ async function applySurfaceWrite(targetName, rawValue, opts) {
|
|
|
743
761
|
async function applySurfaceWriteNow(targetName, rawValue, opts) {
|
|
744
762
|
const registry = opts?.source ?? getGlobalSurfaceRegistry();
|
|
745
763
|
const onScreen = registry.kind === "global";
|
|
746
|
-
if (onScreen && targetName
|
|
747
|
-
return
|
|
748
|
-
}
|
|
749
|
-
if (onScreen && targetName === SURFACE_FEEDBACK_TARGET_NAME) {
|
|
750
|
-
return applySurfaceFeedbackWrite(rawValue, opts);
|
|
751
|
-
}
|
|
752
|
-
if (onScreen && targetName === CUSTOM_FIELDS_TARGET_NAME) {
|
|
753
|
-
return applyCustomFieldsTargetWrite(rawValue, opts, "add");
|
|
754
|
-
}
|
|
755
|
-
if (onScreen && targetName === CUSTOM_FIELDS_SET_TARGET_NAME) {
|
|
756
|
-
return applyCustomFieldsTargetWrite(rawValue, opts, "set");
|
|
764
|
+
if (onScreen && isPlatformWriteTarget(targetName)) {
|
|
765
|
+
return applyPlatformWrite(targetName, rawValue, opts);
|
|
757
766
|
}
|
|
758
767
|
const stack = registry.stack().filter(
|
|
759
768
|
(entry) => !opts?.surfaceName || entry.surfaceName === opts.surfaceName
|
|
@@ -984,157 +993,184 @@ async function applySurfaceWriteNow(targetName, rawValue, opts) {
|
|
|
984
993
|
}
|
|
985
994
|
);
|
|
986
995
|
}
|
|
987
|
-
async function
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1003
|
-
try {
|
|
1004
|
-
await applyWindowFormChanges(rawValue);
|
|
1005
|
-
if (!opts?.quiet) toast.success(`${target.label} \u2014 filled in. Review and save.`);
|
|
1006
|
-
return { ok: true, surfaceName, target, change: writeReceipt(target, rawValue, NOT_READ) };
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
const message = error instanceof Error ? error.message : `Applying "${target.label}" failed.`;
|
|
1009
|
-
if (error instanceof SurfaceWriteRefusalError) {
|
|
1010
|
-
return { ok: false, refused: true, error: message };
|
|
996
|
+
async function applyPlatformTarget(name, value, context) {
|
|
997
|
+
switch (name) {
|
|
998
|
+
case "window_form_fields":
|
|
999
|
+
await applyWindowFormChanges(value);
|
|
1000
|
+
return void 0;
|
|
1001
|
+
case "surface_feedback": {
|
|
1002
|
+
validateSurfaceFeedback(value);
|
|
1003
|
+
return saveSurfaceFeedback(value, {
|
|
1004
|
+
surfaceName: context.surfaceName,
|
|
1005
|
+
route: typeof window === "undefined" ? "" : window.location.pathname,
|
|
1006
|
+
organizationId: await feedbackOrganizationId(),
|
|
1007
|
+
...context.conversationId ? { conversationId: context.conversationId } : {},
|
|
1008
|
+
...context.agentId ? { agentId: context.agentId } : {},
|
|
1009
|
+
...context.actorLabel ? { agentName: context.actorLabel } : {}
|
|
1010
|
+
});
|
|
1011
1011
|
}
|
|
1012
|
-
|
|
1012
|
+
case "custom_fields_add":
|
|
1013
|
+
if (!hasCustomFieldsDoors()) refuseSurfaceWrite("No custom-fields section is open on this page.");
|
|
1014
|
+
validateCustomFieldsWrite(value);
|
|
1015
|
+
return asRefusal(() => applyCustomFieldsWrite(value));
|
|
1016
|
+
case "custom_fields_set":
|
|
1017
|
+
if (!hasCustomFieldsSetDoors()) refuseSurfaceWrite("No custom-fields section is open on this page.");
|
|
1018
|
+
validateCustomFieldsSetWrite(value);
|
|
1019
|
+
return asRefusal(() => applyCustomFieldsSetWrite(value));
|
|
1013
1020
|
}
|
|
1014
1021
|
}
|
|
1015
|
-
async function
|
|
1016
|
-
const target = SURFACE_FEEDBACK_TARGET;
|
|
1017
|
-
const stack = getSurfaceRuntimeStack();
|
|
1018
|
-
const mounted = stack.map((entry) => entry.surfaceName);
|
|
1019
|
-
if (stack.length === 0) {
|
|
1020
|
-
return failUnapplicable(
|
|
1021
|
-
unapplicableMessage(target.name, "This screen mounts no registered surface to give feedback on."),
|
|
1022
|
-
{ targetName: target.name }
|
|
1023
|
-
);
|
|
1024
|
-
}
|
|
1025
|
-
const runtime = opts?.surfaceName ? stack.find((entry) => entry.surfaceName === opts.surfaceName) : stack[0];
|
|
1026
|
-
if (!runtime) {
|
|
1027
|
-
return refuseBeforeApproval(
|
|
1028
|
-
`Surface "${opts?.surfaceName}" is not open here, so feedback cannot be filed for it. Open surfaces: ${mounted.join(", ")}. Nothing was saved.`,
|
|
1029
|
-
{ targetName: target.name, surfaceName: opts?.surfaceName, mounted }
|
|
1030
|
-
);
|
|
1031
|
-
}
|
|
1032
|
-
const surfaceName = runtime.surfaceName;
|
|
1033
|
-
const typed = coerceDeclaredValueType(target, rawValue);
|
|
1034
|
-
if (!typed.ok) {
|
|
1035
|
-
return refuseBeforeApproval(typed.error, { targetName: target.name, surfaceName });
|
|
1036
|
-
}
|
|
1037
|
-
const value = typed.value;
|
|
1022
|
+
async function asRefusal(run) {
|
|
1038
1023
|
try {
|
|
1039
|
-
|
|
1024
|
+
return await run();
|
|
1040
1025
|
} catch (error) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
{ targetName: target.name, surfaceName }
|
|
1044
|
-
);
|
|
1045
|
-
}
|
|
1046
|
-
if ((opts?.origin ?? "user") === "agent") {
|
|
1047
|
-
const verdict = await agentWriteAllowed(
|
|
1048
|
-
target,
|
|
1049
|
-
surfaceName,
|
|
1050
|
-
opts?.actorLabel,
|
|
1051
|
-
value,
|
|
1052
|
-
opts?.requestApproval,
|
|
1053
|
-
runtime
|
|
1054
|
-
);
|
|
1055
|
-
if (verdict !== true) return verdict;
|
|
1056
|
-
}
|
|
1057
|
-
let organizationId = readActiveOrganizationId();
|
|
1058
|
-
if (!organizationId) {
|
|
1059
|
-
const resolved = await awaitEffectiveOrganizationId();
|
|
1060
|
-
if (resolved.status === "ready") organizationId = resolved.organizationId;
|
|
1061
|
-
else
|
|
1062
|
-
return {
|
|
1063
|
-
ok: false,
|
|
1064
|
-
refused: true,
|
|
1065
|
-
phase: "apply",
|
|
1066
|
-
error: resolved.cause === "unreadable" ? "The person's organization could not be read just now, so nothing was saved. Tell them their feedback was not filed and to send it again in a moment." : "No organization is selected, and every feedback row is filed under one, so nothing was saved. Tell the person their feedback could not be filed until they pick their organization from the avatar menu, then send it again."
|
|
1067
|
-
};
|
|
1026
|
+
if (error instanceof SurfaceWriteRefusalError) throw error;
|
|
1027
|
+
throw new SurfaceWriteRefusalError(error instanceof Error && error.message ? error.message : "The write did not complete.");
|
|
1068
1028
|
}
|
|
1029
|
+
}
|
|
1030
|
+
async function feedbackOrganizationId() {
|
|
1031
|
+
const active = readActiveOrganizationId();
|
|
1032
|
+
if (active) return active;
|
|
1033
|
+
const resolved = await awaitEffectiveOrganizationId();
|
|
1034
|
+
if (resolved.status === "ready") return resolved.organizationId;
|
|
1035
|
+
refuseSurfaceWrite(
|
|
1036
|
+
resolved.cause === "unreadable" ? "The person's organization could not be read just now, so nothing was saved. Tell them their feedback was not filed and to send it again in a moment." : "No organization is selected, and every feedback row is filed under one, so nothing was saved. Tell the person their feedback could not be filed until they pick their organization from the avatar menu, then send it again."
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
async function applyPlatformHeadless(request) {
|
|
1040
|
+
const name = request.target.name;
|
|
1041
|
+
if (!isPlatformWriteTarget(name)) throw new Error(`"${name}" is not a platform write target.`);
|
|
1042
|
+
if (request.ops) throw new Error(`"${request.target.label}" takes a whole value; send it instead of edits.`);
|
|
1069
1043
|
try {
|
|
1070
|
-
const outcome = await
|
|
1071
|
-
surfaceName
|
|
1072
|
-
route: typeof window === "undefined" ? "" : window.location.pathname,
|
|
1073
|
-
organizationId,
|
|
1074
|
-
...opts?.conversationId ? { conversationId: opts.conversationId } : {},
|
|
1075
|
-
...opts?.agentId ? { agentId: opts.agentId } : {},
|
|
1076
|
-
...opts?.actorLabel ? { agentName: opts.actorLabel } : {}
|
|
1044
|
+
const outcome = await applyPlatformTarget(name, request.value, {
|
|
1045
|
+
surfaceName: request.surfaceName === BASELINE_SURFACE_NAME ? "" : request.surfaceName
|
|
1077
1046
|
});
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1047
|
+
return {
|
|
1048
|
+
status: "applied",
|
|
1049
|
+
...outcome?.summary ? { sentence: outcome.summary } : {},
|
|
1050
|
+
...outcome?.data !== void 0 ? { result: outcome.data } : {}
|
|
1051
|
+
};
|
|
1082
1052
|
} catch (error) {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1053
|
+
if (error instanceof SurfaceWriteRefusalError) throw reportedError(error.message);
|
|
1054
|
+
throw error;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
let platformHandlersOn = null;
|
|
1058
|
+
function registerPlatformHandlers(door) {
|
|
1059
|
+
if (platformHandlersOn === door) return;
|
|
1060
|
+
platformHandlersOn = door;
|
|
1061
|
+
for (const name of Object.keys(PLATFORM_WRITE_TARGETS)) {
|
|
1062
|
+
door.registerHeadless(BASELINE_SURFACE_NAME, name, (request) => applyPlatformHeadless(request));
|
|
1088
1063
|
}
|
|
1089
1064
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (
|
|
1065
|
+
function platformTarget(name) {
|
|
1066
|
+
if (name === CUSTOM_FIELDS_TARGET_NAME) return customFieldsTarget();
|
|
1067
|
+
if (name === CUSTOM_FIELDS_SET_TARGET_NAME) return customFieldsSetTarget();
|
|
1068
|
+
return PLATFORM_WRITE_TARGETS[name];
|
|
1069
|
+
}
|
|
1070
|
+
async function applyPlatformWrite(name, rawValue, opts) {
|
|
1071
|
+
const stack = getSurfaceRuntimeStack();
|
|
1072
|
+
let runtime = stack[0];
|
|
1073
|
+
if (name === SURFACE_FEEDBACK_TARGET_NAME) {
|
|
1074
|
+
const mounted = stack.map((entry) => entry.surfaceName);
|
|
1075
|
+
if (stack.length === 0) {
|
|
1076
|
+
return failUnapplicable(
|
|
1077
|
+
unapplicableMessage(name, "This screen mounts no registered surface to give feedback on."),
|
|
1078
|
+
{ targetName: name }
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
runtime = opts?.surfaceName ? stack.find((entry) => entry.surfaceName === opts.surfaceName) : stack[0];
|
|
1082
|
+
if (!runtime) {
|
|
1083
|
+
return refuseBeforeApproval(
|
|
1084
|
+
`Surface "${opts?.surfaceName}" is not open here, so feedback cannot be filed for it. Open surfaces: ${mounted.join(", ")}. Nothing was saved.`,
|
|
1085
|
+
{ targetName: name, surfaceName: opts?.surfaceName, mounted }
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (name === CUSTOM_FIELDS_TARGET_NAME ? !hasCustomFieldsDoors() : name === CUSTOM_FIELDS_SET_TARGET_NAME && !hasCustomFieldsSetDoors()) {
|
|
1093
1090
|
return failUnapplicable(
|
|
1094
1091
|
unapplicableMessage(name, "No custom-fields section is open on this page."),
|
|
1095
1092
|
{ targetName: name }
|
|
1096
1093
|
);
|
|
1097
1094
|
}
|
|
1098
|
-
const target =
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1095
|
+
const target = platformTarget(name);
|
|
1096
|
+
const surfaceName = runtime?.surfaceName ?? "";
|
|
1097
|
+
const approvalRuntime = runtime ?? { surfaceName, getScope: () => ({}) };
|
|
1101
1098
|
const typed = coerceDeclaredValueType(target, rawValue);
|
|
1102
1099
|
if (!typed.ok) {
|
|
1103
|
-
return refuseBeforeApproval(typed.error, { targetName:
|
|
1100
|
+
return refuseBeforeApproval(typed.error, { targetName: name, surfaceName });
|
|
1104
1101
|
}
|
|
1105
|
-
const value = typed.value;
|
|
1102
|
+
const value = name === WINDOW_FORM_TARGET_NAME ? labelWindowFormWrite(typed.value) : typed.value;
|
|
1106
1103
|
try {
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1104
|
+
if (name === SURFACE_FEEDBACK_TARGET_NAME) validateSurfaceFeedback(value);
|
|
1105
|
+
if (name === CUSTOM_FIELDS_TARGET_NAME) validateCustomFieldsWrite(value);
|
|
1106
|
+
if (name === CUSTOM_FIELDS_SET_TARGET_NAME) validateCustomFieldsSetWrite(value);
|
|
1109
1107
|
} catch (error) {
|
|
1110
1108
|
return refuseBeforeApproval(
|
|
1111
1109
|
error instanceof Error ? error.message : `"${target.label}" refused this value.`,
|
|
1112
|
-
{ targetName:
|
|
1110
|
+
{ targetName: name, surfaceName }
|
|
1113
1111
|
);
|
|
1114
1112
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
opts?.actorLabel,
|
|
1120
|
-
value,
|
|
1121
|
-
opts?.requestApproval,
|
|
1122
|
-
primary ?? { surfaceName, getScope: () => ({}) }
|
|
1123
|
-
);
|
|
1113
|
+
const origin = opts?.origin ?? "user";
|
|
1114
|
+
const askAgent = () => agentWriteAllowed(target, surfaceName, opts?.actorLabel, value, opts?.requestApproval, approvalRuntime);
|
|
1115
|
+
if (origin === "agent" && target.applyPolicy === "auto") {
|
|
1116
|
+
const verdict = await askAgent();
|
|
1124
1117
|
if (verdict !== true) return verdict;
|
|
1125
1118
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
return
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1119
|
+
let settled;
|
|
1120
|
+
const approve = origin === "agent" ? async () => {
|
|
1121
|
+
const verdict = await askAgent();
|
|
1122
|
+
if (verdict === true) return true;
|
|
1123
|
+
settled = verdict;
|
|
1124
|
+
if (!verdict.ok && verdict.declined) return false;
|
|
1125
|
+
throw reportedError(verdict.ok ? `"${target.label}" was not applied.` : verdict.error);
|
|
1126
|
+
} : void 0;
|
|
1127
|
+
const apply = async () => {
|
|
1128
|
+
try {
|
|
1129
|
+
const outcome = await applyPlatformTarget(name, value, {
|
|
1130
|
+
surfaceName,
|
|
1131
|
+
...opts?.conversationId ? { conversationId: opts.conversationId } : {},
|
|
1132
|
+
...opts?.agentId ? { agentId: opts.agentId } : {},
|
|
1133
|
+
...opts?.actorLabel ? { actorLabel: opts.actorLabel } : {}
|
|
1134
|
+
});
|
|
1135
|
+
if (!opts?.quiet) {
|
|
1136
|
+
if (name === WINDOW_FORM_TARGET_NAME) toast.success(`${target.label} \u2014 filled in. Review and save.`);
|
|
1137
|
+
else if (name === SURFACE_FEEDBACK_TARGET_NAME) toast.success("Feedback saved for the team.", { description: surfaceName });
|
|
1138
|
+
else if (outcome?.summary) toast.success(outcome.summary);
|
|
1139
|
+
}
|
|
1140
|
+
settled = {
|
|
1141
|
+
ok: true,
|
|
1142
|
+
surfaceName,
|
|
1143
|
+
target,
|
|
1144
|
+
...outcome ? { outcome } : {},
|
|
1145
|
+
change: writeReceipt(target, value, NOT_READ)
|
|
1146
|
+
};
|
|
1147
|
+
return { status: "applied", ...outcome?.summary ? { sentence: outcome.summary } : {} };
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
const message = error instanceof Error && error.message ? error.message : `Applying "${target.label}" failed.`;
|
|
1150
|
+
if (error instanceof SurfaceWriteRefusalError) {
|
|
1151
|
+
settled = name === WINDOW_FORM_TARGET_NAME ? { ok: false, refused: true, error: message } : { ok: false, refused: true, phase: "apply", error: message };
|
|
1152
|
+
} else {
|
|
1153
|
+
const failure = fail(message, { targetName: name, surfaceName, error });
|
|
1154
|
+
settled = failure.ok ? failure : { ...failure, phase: "apply" };
|
|
1155
|
+
}
|
|
1156
|
+
throw reportedError(message);
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
const receipt = await writeThroughDoor(
|
|
1160
|
+
{ surfaceName: surfaceName || BASELINE_SURFACE_NAME, target: name, value, by: origin === "agent" ? "agent" : "person" },
|
|
1161
|
+
opts?.conversationId ?? null,
|
|
1162
|
+
apply,
|
|
1163
|
+
approve
|
|
1164
|
+
);
|
|
1165
|
+
if (receipt.status === "applied" && receipt.warning && !opts?.quiet) {
|
|
1166
|
+
toast.warning(receipt.warning, receipt.fix ? { description: receipt.fix } : void 0);
|
|
1137
1167
|
}
|
|
1168
|
+
const result = settled ?? fail(receipt.status === "refused" ? receipt.sentence : `"${target.label}" was not applied: ${receipt.sentence}`, {
|
|
1169
|
+
targetName: name,
|
|
1170
|
+
surfaceName,
|
|
1171
|
+
receipt
|
|
1172
|
+
});
|
|
1173
|
+
return { ...result, receipt };
|
|
1138
1174
|
}
|
|
1139
1175
|
const SURFACE_WRITE_TOOL_NAME = "apply_surface_write";
|
|
1140
1176
|
function listAgentWritableTargets(source) {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* never read or written.
|
|
31
31
|
*/
|
|
32
32
|
import type { SurfaceWriteTarget } from "../types.js";
|
|
33
|
-
export declare const WINDOW_FORM_TARGET_NAME
|
|
33
|
+
export declare const WINDOW_FORM_TARGET_NAME: "window_form_fields";
|
|
34
34
|
/** The marker a REGISTERED layer puts on its root element. */
|
|
35
35
|
export declare const SURFACE_LAYER_ATTRIBUTE = "data-surface-layer";
|
|
36
36
|
export type WindowFormKind = "dialog" | "window" | "sheet" | "drawer";
|
|
@@ -48,7 +48,7 @@ export interface WindowForm {
|
|
|
48
48
|
kind: WindowFormKind;
|
|
49
49
|
fields: WindowFormField[];
|
|
50
50
|
}
|
|
51
|
-
/** The platform target — declared
|
|
51
|
+
/** The platform target — declared once on the baseline surface (`_baseline.manifest.ts`), offered only while a window form is open. */
|
|
52
52
|
export declare const WINDOW_FORM_TARGET: SurfaceWriteTarget;
|
|
53
53
|
/** Every open unregistered window that holds at least one field. */
|
|
54
54
|
export declare function readWindowForms(): WindowForm[];
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { refuseSurfaceWrite } from "./surface-writeback.js";
|
|
2
|
-
|
|
2
|
+
import { PLATFORM_WRITE_TARGETS } from "../manifests/_baseline.manifest.js";
|
|
3
|
+
const WINDOW_FORM_TARGET_NAME = PLATFORM_WRITE_TARGETS.window_form_fields.name;
|
|
3
4
|
const SURFACE_LAYER_ATTRIBUTE = "data-surface-layer";
|
|
4
|
-
const WINDOW_FORM_TARGET =
|
|
5
|
-
name: WINDOW_FORM_TARGET_NAME,
|
|
6
|
-
label: "Fields in an open window",
|
|
7
|
-
description: `Change fields in an open window that has no registered surface (listed in your context as window::<title>). Value: { "window": "<title exactly as listed>", "changes": [{ "field": "<field key>", "value": <new value> }] }. A text or number field takes a string or number, a checkbox or switch takes true/false, a list takes the text of one of its choices (a wrong choice is refused with the real ones). Every change is checked against the field's rules before anything lands; the person approves first, and the window's own Save still decides.`,
|
|
8
|
-
valueType: "object",
|
|
9
|
-
mode: "draft",
|
|
10
|
-
applyPolicy: "ask"
|
|
11
|
-
};
|
|
5
|
+
const WINDOW_FORM_TARGET = PLATFORM_WRITE_TARGETS.window_form_fields;
|
|
12
6
|
const WINDOW_SELECTOR = '[role="dialog"], [role="alertdialog"], [data-window-panel]';
|
|
13
7
|
const FIELD_SELECTOR = 'input, textarea, select, [contenteditable="true"], [role="switch"], [role="checkbox"], [role="combobox"]';
|
|
14
8
|
const SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -29,7 +29,7 @@ export declare function YouTubeMark({ colored, className }: ConnectorLogoProps):
|
|
|
29
29
|
export declare function GoogleDocsMark({ colored, className }: ConnectorLogoProps): import("react").JSX.Element;
|
|
30
30
|
/** Google Sheets — the green page with a grid. */
|
|
31
31
|
export declare function GoogleSheetsMark({ colored, className }: ConnectorLogoProps): import("react").JSX.Element;
|
|
32
|
-
/** GitHub — the
|
|
33
|
-
export declare
|
|
32
|
+
/** GitHub — the ONE GitHub glyph (`@ai-matrx/icons/brand`), as a connector mark. */
|
|
33
|
+
export declare const GitHubMark: ConnectorLogo;
|
|
34
34
|
/** Microsoft — the four squares. */
|
|
35
35
|
export declare function MicrosoftMark({ colored, className }: ConnectorLogoProps): import("react").JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Github } from "@ai-matrx/icons/brand";
|
|
2
3
|
import { cn } from "@ai-matrx/design-system";
|
|
3
4
|
function MarkSvg({
|
|
4
5
|
className,
|
|
@@ -195,15 +196,7 @@ function GoogleSheetsMark({ colored = false, className }) {
|
|
|
195
196
|
)
|
|
196
197
|
] });
|
|
197
198
|
}
|
|
198
|
-
|
|
199
|
-
return /* @__PURE__ */ jsx(MarkSvg, { className, children: /* @__PURE__ */ jsx(
|
|
200
|
-
"path",
|
|
201
|
-
{
|
|
202
|
-
fill: "currentColor",
|
|
203
|
-
d: "M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 21.07c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.09-.73.09-.73 1.2.09 1.83 1.24 1.83 1.24 1.07 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.61-2.8 5.63-5.48 5.92.42.36.81 1.1.81 2.22l-.01 3.29c0 .31.2.69.82.57A12 12 0 0 0 12 .3Z"
|
|
204
|
-
}
|
|
205
|
-
) });
|
|
206
|
-
}
|
|
199
|
+
const GitHubMark = lucideMark(Github);
|
|
207
200
|
function MicrosoftMark({ colored = false, className }) {
|
|
208
201
|
const c = (hex) => colored ? hex : "currentColor";
|
|
209
202
|
return /* @__PURE__ */ jsxs(MarkSvg, { className, children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as per-module subpaths @ai-matrx/chat/<domain>/<module> over a host contract (/host, /host/react) whose only required value is a db; plus the kernel compatibility entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) until P28.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -201,21 +201,21 @@
|
|
|
201
201
|
"server-only": "^0.0.1",
|
|
202
202
|
"uuid": "^14.0.2",
|
|
203
203
|
"zod": "^4.6.5",
|
|
204
|
-
"@ai-matrx/agents": "latest",
|
|
205
204
|
"@ai-matrx/alchemy": "latest",
|
|
205
|
+
"@ai-matrx/associations": "latest",
|
|
206
|
+
"@ai-matrx/browser-audio": "latest",
|
|
206
207
|
"@ai-matrx/canvas": "latest",
|
|
207
208
|
"@ai-matrx/content-ir": "latest",
|
|
208
|
-
"@ai-matrx/
|
|
209
|
-
"@ai-matrx/associations": "latest",
|
|
209
|
+
"@ai-matrx/agents": "latest",
|
|
210
210
|
"@ai-matrx/content-ir-react": "latest",
|
|
211
211
|
"@ai-matrx/data": "latest",
|
|
212
|
-
"@ai-matrx/
|
|
212
|
+
"@ai-matrx/design-system": "latest",
|
|
213
213
|
"@ai-matrx/icons": "latest",
|
|
214
214
|
"@ai-matrx/kit": "latest",
|
|
215
215
|
"@ai-matrx/media": "latest",
|
|
216
|
+
"@ai-matrx/diff": "latest",
|
|
216
217
|
"@ai-matrx/print": "latest",
|
|
217
218
|
"@ai-matrx/realtime": "latest",
|
|
218
|
-
"@ai-matrx/design-system": "latest",
|
|
219
219
|
"@ai-matrx/records": "latest",
|
|
220
220
|
"@ai-matrx/records-ui": "latest",
|
|
221
221
|
"@ai-matrx/tap-target": "latest"
|