@ai-matrx/chat 0.3.5 → 0.3.9
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 +30 -0
- package/dist/agents/components/chat/ChatNewClient.d.ts +6 -1
- package/dist/agents/components/chat/ChatNewClient.js +10 -3
- package/dist/agents/components/chat/begin-fresh-chat.d.ts +2 -0
- package/dist/agents/components/chat/begin-fresh-chat.js +18 -1
- package/dist/agents/components/inputs/smart-input/InputActionButtons.d.ts +4 -0
- package/dist/agents/components/inputs/smart-input/InputActionButtons.js +16 -9
- package/dist/agents/components/inputs/smart-input/RunControlsMenu.d.ts +2 -0
- package/dist/agents/components/inputs/smart-input/RunControlsMenu.js +2 -0
- package/dist/agents/components/inputs/smart-input/SmartAgentInputStacked.js +1 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPeek.js +1 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +4 -6
- package/dist/agents/components/inputs/smart-input/composer/ComposerOutput.js +9 -4
- package/dist/agents/components/inputs/smart-input/composer/ComposerPlusMenu.d.ts +3 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerPlusMenu.js +2 -2
- package/dist/agents/components/inputs/smart-input/composer/composer-types.d.ts +5 -0
- package/dist/agents/components/inputs/variable-input-variations/AgentVariableCards.js +1 -1
- package/dist/agents/components/shared/AgentOptionsMenu.js +2 -2
- package/dist/agents/components/shared/AgentPeekDuplicateButton.d.ts +6 -3
- package/dist/agents/components/shared/AgentPeekDuplicateButton.js +8 -7
- package/dist/agents/redux/agent-definition/slice.d.ts +16 -1
- package/dist/agents/redux/agent-definition/slice.js +20 -0
- package/dist/agents/redux/execution-system/message-crud/edit-message.thunk.js +3 -6
- package/dist/agents/redux/execution-system/message-crud/fork-and-resubmit-from-message.thunk.js +1 -1
- package/dist/agents/types/agent-definition.types.d.ts +29 -1
- package/dist/compute/targets.d.ts +2 -6
- package/dist/context/sources/scopes.d.ts +3 -3
- package/dist/host/ui-slots.js +1 -1
- package/dist/host/window-openers.d.ts +21 -5
- package/dist/surfaces/lib/ui-surfaces-agent-writes.d.ts +61 -0
- package/dist/surfaces/lib/ui-surfaces-agent-writes.js +305 -0
- package/dist/surfaces/lib/ui-surfaces-scope.d.ts +40 -0
- package/dist/surfaces/lib/ui-surfaces-scope.js +116 -0
- package/dist/tool-call-visualization/effects/toolStateEffects.d.ts +1 -1
- package/dist/tool-call-visualization/registry/registry.js +0 -2
- package/dist/voice-agent/relay/VoiceRelayBar.d.ts +7 -1
- package/dist/voice-agent/relay/VoiceRelayBar.js +90 -24
- package/package.json +5 -5
|
@@ -116,15 +116,13 @@ export interface ChatComputeTargetsSource {
|
|
|
116
116
|
error: string | null;
|
|
117
117
|
fetchInstances: (opts?: {
|
|
118
118
|
limit?: number;
|
|
119
|
-
[option: string]: unknown;
|
|
120
119
|
}) => Promise<unknown>;
|
|
121
120
|
createInstance: (request: {
|
|
122
121
|
organization_id: string;
|
|
123
122
|
template?: string;
|
|
124
|
-
tier
|
|
123
|
+
tier: "ec2" | "hosted";
|
|
125
124
|
ttl_seconds?: number;
|
|
126
125
|
labels?: Record<string, string>;
|
|
127
|
-
[field: string]: unknown;
|
|
128
126
|
}) => Promise<{
|
|
129
127
|
instance: SandboxInstance | null;
|
|
130
128
|
error: string | null;
|
|
@@ -190,15 +188,13 @@ export declare const useSandboxInstances: () => {
|
|
|
190
188
|
error: string | null;
|
|
191
189
|
fetchInstances: (opts?: {
|
|
192
190
|
limit?: number;
|
|
193
|
-
[option: string]: unknown;
|
|
194
191
|
}) => Promise<unknown>;
|
|
195
192
|
createInstance: (request: {
|
|
196
193
|
organization_id: string;
|
|
197
194
|
template?: string;
|
|
198
|
-
tier
|
|
195
|
+
tier: "ec2" | "hosted";
|
|
199
196
|
ttl_seconds?: number;
|
|
200
197
|
labels?: Record<string, string>;
|
|
201
|
-
[field: string]: unknown;
|
|
202
198
|
}) => Promise<{
|
|
203
199
|
instance: SandboxInstance | null;
|
|
204
200
|
error: string | null;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* have no honest default and throw, naming themselves.
|
|
18
18
|
*/
|
|
19
19
|
import type { ComponentType } from "react";
|
|
20
|
+
import type { AssociationSourceEdge, AssociationTargetEdge } from "@ai-matrx/associations";
|
|
20
21
|
import type { ChatDatabase, Json } from "../../host/db-types.js";
|
|
21
22
|
import { type AnyComponent, type AnyFn } from "../../host/registered-source.js";
|
|
22
23
|
export type ContextItemRow = ChatDatabase["context"]["Tables"]["context_items"]["Row"];
|
|
@@ -61,7 +62,6 @@ export interface ScopeTreeOrganization {
|
|
|
61
62
|
projects?: Array<{
|
|
62
63
|
id: string;
|
|
63
64
|
name: string;
|
|
64
|
-
[field: string]: unknown;
|
|
65
65
|
}>;
|
|
66
66
|
}
|
|
67
67
|
export interface ContextItemValue {
|
|
@@ -175,10 +175,10 @@ export interface AssociationEdgeRow {
|
|
|
175
175
|
}
|
|
176
176
|
export interface ChatAssociationsService {
|
|
177
177
|
listForTargets(targetType: string, targetIds: string[], ...rest: any[]): Promise<ScopesRpcResult<{
|
|
178
|
-
edges:
|
|
178
|
+
edges: AssociationTargetEdge[];
|
|
179
179
|
}>>;
|
|
180
180
|
listForSources(sourceType: string, sourceIds: string[], ...rest: any[]): Promise<ScopesRpcResult<{
|
|
181
|
-
edges:
|
|
181
|
+
edges: AssociationSourceEdge[];
|
|
182
182
|
}>>;
|
|
183
183
|
add(input: any): Promise<ScopesRpcResult<any>>;
|
|
184
184
|
remove(input: any): Promise<ScopesRpcResult<any>>;
|
package/dist/host/ui-slots.js
CHANGED
|
@@ -410,7 +410,7 @@ const loadCodeEditHistoryThunk = slotFn("loadCodeEditHistoryThunk");
|
|
|
410
410
|
const applySkillStreamEvent = slotFn("applySkillStreamEvent");
|
|
411
411
|
const isSkillStreamEvent = slotFn("isSkillStreamEvent", () => false);
|
|
412
412
|
const materializeMessageArtifacts = slotFn("materializeMessageArtifacts");
|
|
413
|
-
const reconcileMessagesArtifacts = slotFn("reconcileMessagesArtifacts", () => void 0);
|
|
413
|
+
const reconcileMessagesArtifacts = slotFn("reconcileMessagesArtifacts", async () => void 0);
|
|
414
414
|
const noteBrowserActivity = slotFn("noteBrowserActivity");
|
|
415
415
|
const selectCloudBrowserRunLive = slotFn("selectCloudBrowserRunLive");
|
|
416
416
|
const adoptCloudBrowserRunFromStream = slotFn("adoptCloudBrowserRunFromStream");
|
|
@@ -27,7 +27,7 @@ export interface OpenAgentIdWindowOptions {
|
|
|
27
27
|
agentId?: string | null;
|
|
28
28
|
}
|
|
29
29
|
export type OpenAgentAdminFindUsagesWindowOptions = OpenAgentIdWindowOptions;
|
|
30
|
-
export type OpenAgentCreateAppWindowOptions =
|
|
30
|
+
export type OpenAgentCreateAppWindowOptions = Record<string, never>;
|
|
31
31
|
export type OpenAgentDataStorageWindowOptions = OpenAgentIdWindowOptions;
|
|
32
32
|
export type OpenAgentFindUsagesWindowOptions = OpenAgentIdWindowOptions;
|
|
33
33
|
export type OpenAgentInterfaceVariationsWindowOptions = OpenAgentIdWindowOptions;
|
|
@@ -222,7 +222,7 @@ export interface EditorPrimaryAction {
|
|
|
222
222
|
label: string;
|
|
223
223
|
variant?: "default" | "secondary" | "outline" | "destructive" | "ghost" | "link";
|
|
224
224
|
}
|
|
225
|
-
|
|
225
|
+
interface OpenFullScreenMarkdownEditorOptionsBase {
|
|
226
226
|
instanceId?: string;
|
|
227
227
|
content?: string;
|
|
228
228
|
mode?: unknown;
|
|
@@ -236,14 +236,29 @@ export interface OpenFullScreenMarkdownEditorOptions {
|
|
|
236
236
|
showSaveButton?: boolean;
|
|
237
237
|
showCopyButton?: boolean;
|
|
238
238
|
primaryActions?: EditorPrimaryAction[];
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
}
|
|
240
|
+
type OpenFullScreenMarkdownEditorHandlers = {
|
|
241
|
+
onSave: (content: string) => Promise<void>;
|
|
242
|
+
onAction?: never;
|
|
241
243
|
onEvent?: (event: {
|
|
242
244
|
type: "save";
|
|
243
245
|
content: string;
|
|
244
246
|
action?: string;
|
|
245
247
|
}) => void | Promise<void>;
|
|
246
|
-
}
|
|
248
|
+
} | {
|
|
249
|
+
onSave?: never;
|
|
250
|
+
onAction: (action: string, content: string) => Promise<void>;
|
|
251
|
+
onEvent?: (event: {
|
|
252
|
+
type: "save";
|
|
253
|
+
content: string;
|
|
254
|
+
action?: string;
|
|
255
|
+
}) => void | Promise<void>;
|
|
256
|
+
} | {
|
|
257
|
+
onSave?: undefined;
|
|
258
|
+
onAction?: undefined;
|
|
259
|
+
onEvent?: undefined;
|
|
260
|
+
};
|
|
261
|
+
export type OpenFullScreenMarkdownEditorOptions = OpenFullScreenMarkdownEditorOptionsBase & OpenFullScreenMarkdownEditorHandlers;
|
|
247
262
|
export interface ChatFullScreenEditorHandle {
|
|
248
263
|
instanceId: string;
|
|
249
264
|
callbackGroupId: string | null;
|
|
@@ -361,3 +376,4 @@ export declare function useOpenTopicalMapWindow(): (opts: OpenTopicalMapWindowOp
|
|
|
361
376
|
export declare function useOpenScratchpadPanel(): (opts?: OpenScratchpadPanelOptions) => ChatWindowHandle;
|
|
362
377
|
export declare function useOpenWorkingDocumentPanel(): (opts: OpenWorkingDocumentPanelOptions) => ChatWindowHandle;
|
|
363
378
|
export declare function useOpenWorkingDocumentWindow(): (opts: OpenWorkingDocumentWindowOptions) => ChatInstanceWindowHandle;
|
|
379
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsers for the UI Surfaces registry's agent write targets
|
|
3
|
+
* (`create_surfaces` / `update_surfaces` / `delete_surfaces` on
|
|
4
|
+
* `matrx-admin/ui-surfaces`). Each reads the agent's WHOLE list, reports every
|
|
5
|
+
* problem at once, and returns plans the page saves through its own service
|
|
6
|
+
* functions. No I/O here — unit-tested in `__tests__/ui-surfaces-agent-writes.test.ts`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_PARENT = "matrx-default/default";
|
|
9
|
+
export declare const DEFAULT_SORT_ORDER = 150;
|
|
10
|
+
/** What a parser needs to know about one existing surface. */
|
|
11
|
+
export interface ExistingSurface {
|
|
12
|
+
name: string;
|
|
13
|
+
has_manifest: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SurfaceWriteContext {
|
|
16
|
+
existing: readonly ExistingSurface[];
|
|
17
|
+
clientNames: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export interface CreateSurfacePlan {
|
|
20
|
+
name: string;
|
|
21
|
+
client_name: string;
|
|
22
|
+
label: string | null;
|
|
23
|
+
description: string;
|
|
24
|
+
parent_surface_name: string | null;
|
|
25
|
+
sort_order: number;
|
|
26
|
+
is_active: boolean;
|
|
27
|
+
executor_name: string | null;
|
|
28
|
+
url_pattern: string | null;
|
|
29
|
+
}
|
|
30
|
+
export interface UpdateSurfacePatch {
|
|
31
|
+
description?: string | null;
|
|
32
|
+
parent_surface_name?: string | null;
|
|
33
|
+
sort_order?: number;
|
|
34
|
+
is_active?: boolean;
|
|
35
|
+
executor_name?: string | null;
|
|
36
|
+
url_pattern?: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface UpdateSurfacePlan {
|
|
39
|
+
name: string;
|
|
40
|
+
patch: UpdateSurfacePatch;
|
|
41
|
+
changed: string[];
|
|
42
|
+
}
|
|
43
|
+
export declare function parseCreateSurfacesValue(value: unknown, ctx: SurfaceWriteContext): CreateSurfacePlan[];
|
|
44
|
+
export declare function parseUpdateSurfacesValue(value: unknown, ctx: SurfaceWriteContext): UpdateSurfacePlan[];
|
|
45
|
+
export declare function parseDeleteSurfacesValue(value: unknown, ctx: SurfaceWriteContext): ExistingSurface[];
|
|
46
|
+
/** Fields the New surface dialog holds — the `new_surface_draft` target. */
|
|
47
|
+
export interface NewSurfaceDraftFields {
|
|
48
|
+
client?: string;
|
|
49
|
+
local?: string;
|
|
50
|
+
/** A surface name, or null for a root surface. */
|
|
51
|
+
parent_surface_name?: string | null;
|
|
52
|
+
/** A tier label: Pages | Specialized | Overlays | Editor variants | Debug. */
|
|
53
|
+
tier?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare const NEW_SURFACE_TIERS: readonly ["Pages", "Specialized", "Overlays", "Editor variants", "Debug"];
|
|
57
|
+
/**
|
|
58
|
+
* Read the `new_surface_draft` value: an OBJECT with any of { name, parent_surface_name,
|
|
59
|
+
* tier, description }. Every problem is reported at once; nothing is saved.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseNewSurfaceDraftValue(value: unknown, ctx: SurfaceWriteContext): NewSurfaceDraftFields;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectProblems,
|
|
3
|
+
ListLevelProblem,
|
|
4
|
+
ProblemList,
|
|
5
|
+
readCollectionList,
|
|
6
|
+
repeatsProblem
|
|
7
|
+
} from "../runtime/collection-write-targets.js";
|
|
8
|
+
const DEFAULT_PARENT = "matrx-default/default";
|
|
9
|
+
const DEFAULT_SORT_ORDER = 150;
|
|
10
|
+
const LOCAL_RE = /^[a-z0-9][a-z0-9-/]*$/;
|
|
11
|
+
const UPDATE_FIELDS = [
|
|
12
|
+
"description",
|
|
13
|
+
"parent_surface_name",
|
|
14
|
+
"sort_order",
|
|
15
|
+
"is_active",
|
|
16
|
+
"executor_name",
|
|
17
|
+
"url_pattern"
|
|
18
|
+
];
|
|
19
|
+
const MANIFEST_OWNED = /* @__PURE__ */ new Set([
|
|
20
|
+
"description",
|
|
21
|
+
"parent_surface_name",
|
|
22
|
+
"is_active",
|
|
23
|
+
"url_pattern"
|
|
24
|
+
]);
|
|
25
|
+
function asObject(raw) {
|
|
26
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
27
|
+
throw new Error("must be an object.");
|
|
28
|
+
return raw;
|
|
29
|
+
}
|
|
30
|
+
function optionalText(obj, key, problems, nullable) {
|
|
31
|
+
if (!(key in obj)) return void 0;
|
|
32
|
+
const v = obj[key];
|
|
33
|
+
if (v === null && nullable) return null;
|
|
34
|
+
if (typeof v !== "string") {
|
|
35
|
+
problems.add(`${key} must be a string${nullable ? " or null" : ""}.`);
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
return v.trim();
|
|
39
|
+
}
|
|
40
|
+
function readSortOrder(obj, problems) {
|
|
41
|
+
if (!("sort_order" in obj)) return void 0;
|
|
42
|
+
const v = obj.sort_order;
|
|
43
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 100) {
|
|
44
|
+
problems.add(
|
|
45
|
+
"sort_order must be a whole number of at least 100 (0-99 is reserved)."
|
|
46
|
+
);
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
return v;
|
|
50
|
+
}
|
|
51
|
+
function readParent(obj, names, problems, self) {
|
|
52
|
+
const parent = optionalText(obj, "parent_surface_name", problems, true);
|
|
53
|
+
if (typeof parent === "string") {
|
|
54
|
+
if (!parent) return null;
|
|
55
|
+
if (parent === self) {
|
|
56
|
+
problems.add("a surface cannot be its own parent.");
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
if (!names.has(parent)) {
|
|
60
|
+
problems.add(`parent_surface_name "${parent}" is not a surface in the registry.`);
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return parent;
|
|
65
|
+
}
|
|
66
|
+
function nameOfRaw(raw) {
|
|
67
|
+
if (typeof raw === "string") return raw;
|
|
68
|
+
if (raw && typeof raw === "object" && typeof raw.name === "string")
|
|
69
|
+
return raw.name;
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
function parseCreateSurfacesValue(value, ctx) {
|
|
73
|
+
const target = "create_surfaces";
|
|
74
|
+
const list = readCollectionList(target, "surfaces", value);
|
|
75
|
+
const names = new Set(ctx.existing.map((s) => s.name));
|
|
76
|
+
const clients = new Set(ctx.clientNames);
|
|
77
|
+
return collectProblems(
|
|
78
|
+
target,
|
|
79
|
+
list,
|
|
80
|
+
(raw) => {
|
|
81
|
+
const obj = asObject(raw);
|
|
82
|
+
const problems = new ProblemList(target);
|
|
83
|
+
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
84
|
+
let client = "";
|
|
85
|
+
if (!name) problems.add('name is required ("<client>/<local>").');
|
|
86
|
+
else {
|
|
87
|
+
const slash = name.indexOf("/");
|
|
88
|
+
client = slash > 0 ? name.slice(0, slash) : "";
|
|
89
|
+
const local = slash > 0 ? name.slice(slash + 1) : "";
|
|
90
|
+
if (!client || !local)
|
|
91
|
+
problems.add(`name "${name}" must be "<client>/<local>".`);
|
|
92
|
+
else {
|
|
93
|
+
if (!clients.has(client))
|
|
94
|
+
problems.add(
|
|
95
|
+
`client "${client}" is not a UI client (one of: ${[...clients].join(", ")}).`
|
|
96
|
+
);
|
|
97
|
+
if (!LOCAL_RE.test(local))
|
|
98
|
+
problems.add(
|
|
99
|
+
`the local part "${local}" may use only lowercase letters, digits, hyphens and slashes.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const label = optionalText(obj, "label", problems, true);
|
|
104
|
+
const description = optionalText(obj, "description", problems, true);
|
|
105
|
+
const parent = readParent(obj, names, problems);
|
|
106
|
+
const sort = readSortOrder(obj, problems);
|
|
107
|
+
const executor = optionalText(obj, "executor_name", problems, true);
|
|
108
|
+
const url = optionalText(obj, "url_pattern", problems, true);
|
|
109
|
+
let isActive = true;
|
|
110
|
+
if ("is_active" in obj) {
|
|
111
|
+
if (typeof obj.is_active !== "boolean")
|
|
112
|
+
problems.add("is_active must be true or false.");
|
|
113
|
+
else isActive = obj.is_active;
|
|
114
|
+
}
|
|
115
|
+
problems.throwIfAny();
|
|
116
|
+
return {
|
|
117
|
+
name,
|
|
118
|
+
client_name: client,
|
|
119
|
+
label: label || null,
|
|
120
|
+
description: description ?? "",
|
|
121
|
+
parent_surface_name: parent === void 0 ? DEFAULT_PARENT : parent,
|
|
122
|
+
sort_order: sort ?? DEFAULT_SORT_ORDER,
|
|
123
|
+
is_active: isActive,
|
|
124
|
+
executor_name: executor || null,
|
|
125
|
+
url_pattern: url || null
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
nameOf: nameOfRaw,
|
|
130
|
+
listChecks: (items) => [
|
|
131
|
+
repeatsProblem(target, items.map((i) => i.name), "name"),
|
|
132
|
+
...items.filter((i) => i.name && names.has(i.name)).map(
|
|
133
|
+
(i) => `${target}[${i.index}] "${i.name}" already exists in the registry \u2014 use update_surfaces to change it.`
|
|
134
|
+
)
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function parseUpdateSurfacesValue(value, ctx) {
|
|
140
|
+
const target = "update_surfaces";
|
|
141
|
+
const list = readCollectionList(target, "surfaces", value);
|
|
142
|
+
const byName = new Map(ctx.existing.map((s) => [s.name, s]));
|
|
143
|
+
const names = new Set(byName.keys());
|
|
144
|
+
return collectProblems(
|
|
145
|
+
target,
|
|
146
|
+
list,
|
|
147
|
+
(raw) => {
|
|
148
|
+
const obj = asObject(raw);
|
|
149
|
+
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
150
|
+
if (!name) throw new Error("name is required (from surfaces).");
|
|
151
|
+
const current = byName.get(name);
|
|
152
|
+
if (!current)
|
|
153
|
+
throw new ListLevelProblem(
|
|
154
|
+
`${target}: "${name}" is not a surface in the registry.`
|
|
155
|
+
);
|
|
156
|
+
const problems = new ProblemList(target);
|
|
157
|
+
const unknown = Object.keys(obj).filter(
|
|
158
|
+
(k) => k !== "name" && !UPDATE_FIELDS.includes(k)
|
|
159
|
+
);
|
|
160
|
+
if (unknown.length)
|
|
161
|
+
problems.add(
|
|
162
|
+
`cannot change ${unknown.join(", ")} here (changeable: ${UPDATE_FIELDS.join(", ")}).`
|
|
163
|
+
);
|
|
164
|
+
if (current.has_manifest) {
|
|
165
|
+
const owned = Object.keys(obj).filter((k) => MANIFEST_OWNED.has(k));
|
|
166
|
+
if (owned.length)
|
|
167
|
+
problems.add(
|
|
168
|
+
`"${name}" has a code manifest, which owns ${owned.join(", ")} \u2014 Sync manifests would overwrite the change. Change the manifest in code; here only sort_order and executor_name may change.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const patch = {};
|
|
172
|
+
const description = optionalText(obj, "description", problems, true);
|
|
173
|
+
if (description !== void 0) patch.description = description || null;
|
|
174
|
+
const parent = readParent(obj, names, problems, name);
|
|
175
|
+
if (parent !== void 0) patch.parent_surface_name = parent;
|
|
176
|
+
const sort = readSortOrder(obj, problems);
|
|
177
|
+
if (sort !== void 0) patch.sort_order = sort;
|
|
178
|
+
if ("is_active" in obj) {
|
|
179
|
+
if (typeof obj.is_active !== "boolean")
|
|
180
|
+
problems.add("is_active must be true or false.");
|
|
181
|
+
else patch.is_active = obj.is_active;
|
|
182
|
+
}
|
|
183
|
+
const executor = optionalText(obj, "executor_name", problems, true);
|
|
184
|
+
if (executor !== void 0) patch.executor_name = executor || null;
|
|
185
|
+
const url = optionalText(obj, "url_pattern", problems, true);
|
|
186
|
+
if (url !== void 0) patch.url_pattern = url || null;
|
|
187
|
+
problems.throwIfAny();
|
|
188
|
+
const changed = Object.keys(patch);
|
|
189
|
+
if (changed.length === 0) throw new Error("sends no field to change.");
|
|
190
|
+
return { name, patch, changed };
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
nameOf: nameOfRaw,
|
|
194
|
+
listChecks: (items) => [
|
|
195
|
+
repeatsProblem(target, items.map((i) => i.name), "surface")
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
function parseDeleteSurfacesValue(value, ctx) {
|
|
201
|
+
const target = "delete_surfaces";
|
|
202
|
+
const list = readCollectionList(target, "surfaces", value);
|
|
203
|
+
const byName = new Map(ctx.existing.map((s) => [s.name, s]));
|
|
204
|
+
return collectProblems(
|
|
205
|
+
target,
|
|
206
|
+
list,
|
|
207
|
+
(raw) => {
|
|
208
|
+
const name = nameOfRaw(raw)?.trim();
|
|
209
|
+
if (!name) throw new Error('must be a surface name or { "name": "\u2026" }.');
|
|
210
|
+
const current = byName.get(name);
|
|
211
|
+
if (!current)
|
|
212
|
+
throw new ListLevelProblem(
|
|
213
|
+
`${target}: "${name}" is not a surface in the registry.`
|
|
214
|
+
);
|
|
215
|
+
if (current.has_manifest)
|
|
216
|
+
throw new Error(
|
|
217
|
+
`"${name}" has a code manifest, so code owns it: remove the manifest in code, or deactivate it instead.`
|
|
218
|
+
);
|
|
219
|
+
return current;
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
nameOf: nameOfRaw,
|
|
223
|
+
listChecks: (items) => [
|
|
224
|
+
repeatsProblem(target, items.map((i) => i.name), "surface")
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const NEW_SURFACE_TIERS = [
|
|
230
|
+
"Pages",
|
|
231
|
+
"Specialized",
|
|
232
|
+
"Overlays",
|
|
233
|
+
"Editor variants",
|
|
234
|
+
"Debug"
|
|
235
|
+
];
|
|
236
|
+
function parseNewSurfaceDraftValue(value, ctx) {
|
|
237
|
+
const target = "new_surface_draft";
|
|
238
|
+
const problems = new ProblemList(target);
|
|
239
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
240
|
+
problems.add(
|
|
241
|
+
"new_surface_draft expects a JSON OBJECT like { name, parent_surface_name, tier, description }."
|
|
242
|
+
);
|
|
243
|
+
problems.throwIfAny();
|
|
244
|
+
}
|
|
245
|
+
const obj = value;
|
|
246
|
+
const allowed = ["name", "parent_surface_name", "tier", "description"];
|
|
247
|
+
const unknown = Object.keys(obj).filter((k) => !allowed.includes(k));
|
|
248
|
+
if (unknown.length)
|
|
249
|
+
problems.add(
|
|
250
|
+
`the dialog has no ${unknown.join(", ")} field (fields: ${allowed.join(", ")}).`
|
|
251
|
+
);
|
|
252
|
+
const out = {};
|
|
253
|
+
if ("name" in obj) {
|
|
254
|
+
const name = typeof obj.name === "string" ? obj.name.trim() : "";
|
|
255
|
+
const slash = name.indexOf("/");
|
|
256
|
+
const client = slash > 0 ? name.slice(0, slash) : "";
|
|
257
|
+
const local = slash > 0 ? name.slice(slash + 1) : "";
|
|
258
|
+
if (!client || !local) problems.add(`name must be "<client>/<local>".`);
|
|
259
|
+
else {
|
|
260
|
+
if (!ctx.clientNames.includes(client))
|
|
261
|
+
problems.add(
|
|
262
|
+
`client "${client}" is not a UI client (one of: ${ctx.clientNames.join(", ")}).`
|
|
263
|
+
);
|
|
264
|
+
if (!LOCAL_RE.test(local))
|
|
265
|
+
problems.add(
|
|
266
|
+
`the local part "${local}" may use only lowercase letters, digits, hyphens and slashes.`
|
|
267
|
+
);
|
|
268
|
+
if (ctx.existing.some((s) => s.name === name))
|
|
269
|
+
problems.add(`"${name}" already exists in the registry.`);
|
|
270
|
+
out.client = client;
|
|
271
|
+
out.local = local;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if ("parent_surface_name" in obj) {
|
|
275
|
+
const parent = readParent(
|
|
276
|
+
obj,
|
|
277
|
+
new Set(ctx.existing.map((s) => s.name)),
|
|
278
|
+
problems
|
|
279
|
+
);
|
|
280
|
+
if (parent !== void 0) out.parent_surface_name = parent;
|
|
281
|
+
}
|
|
282
|
+
if ("tier" in obj) {
|
|
283
|
+
if (typeof obj.tier !== "string" || !NEW_SURFACE_TIERS.includes(obj.tier))
|
|
284
|
+
problems.add(`tier must be one of: ${NEW_SURFACE_TIERS.join(", ")}.`);
|
|
285
|
+
else out.tier = obj.tier;
|
|
286
|
+
}
|
|
287
|
+
if ("description" in obj) {
|
|
288
|
+
if (typeof obj.description !== "string")
|
|
289
|
+
problems.add("description must be a string.");
|
|
290
|
+
else out.description = obj.description;
|
|
291
|
+
}
|
|
292
|
+
problems.throwIfAny();
|
|
293
|
+
if (Object.keys(out).length === 0)
|
|
294
|
+
throw new Error("new_surface_draft sends no field to fill. Nothing was changed.");
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
DEFAULT_PARENT,
|
|
299
|
+
DEFAULT_SORT_ORDER,
|
|
300
|
+
NEW_SURFACE_TIERS,
|
|
301
|
+
parseCreateSurfacesValue,
|
|
302
|
+
parseDeleteSurfacesValue,
|
|
303
|
+
parseNewSurfaceDraftValue,
|
|
304
|
+
parseUpdateSurfacesValue
|
|
305
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure scope builder for the UI Surfaces registry page
|
|
3
|
+
* (`matrx-admin/ui-surfaces`, `/administration/ui/surfaces`).
|
|
4
|
+
*
|
|
5
|
+
* Called by `SurfacesContainer` with state the page already rendered — it
|
|
6
|
+
* never fetches (the runtime polls it every 400 ms). Not loaded yet → the
|
|
7
|
+
* registry keys are omitted; a failed load reports only `load_state`.
|
|
8
|
+
*/
|
|
9
|
+
import type { SurfaceScopePayload } from "../types.js";
|
|
10
|
+
import { type UiSurfaceScopeEntry, type UiSurfacesFilterScope, type UiSurfacesRegistryCounts } from "../manifests/admin-ui-surfaces.manifest.js";
|
|
11
|
+
import { type SurfaceWithStats } from "../services/surfaces.service.js";
|
|
12
|
+
import type { SurfacesFilterState } from "../host-surface-types.js";
|
|
13
|
+
import type { NewSurfaceDraftScope } from "../host-surface-types.js";
|
|
14
|
+
/** How many rows `surface_list` carries up front (~4,000 chars). */
|
|
15
|
+
export declare const SURFACE_LIST_MAX_ROWS = 40;
|
|
16
|
+
export type UiSurfacesDialog = "new_surface" | "new_client" | "candidates" | "sync_manifests" | "drift_report";
|
|
17
|
+
export interface UiSurfacesScopeInput {
|
|
18
|
+
loading: boolean;
|
|
19
|
+
error: string | null;
|
|
20
|
+
/** Every row loaded (empty until the first successful load). */
|
|
21
|
+
surfaces: readonly SurfaceWithStats[];
|
|
22
|
+
/** The rows after the page's own filters, in the page's order. */
|
|
23
|
+
visible: readonly SurfaceWithStats[];
|
|
24
|
+
clientNames: readonly string[];
|
|
25
|
+
manifestedNames: ReadonlySet<string>;
|
|
26
|
+
filters: SurfacesFilterState;
|
|
27
|
+
readinessCounts: UiSurfacesRegistryCounts["readiness"];
|
|
28
|
+
candidatesAvailable: number;
|
|
29
|
+
/** The drift report's total (countDriftIssues); null until it loads. */
|
|
30
|
+
driftIssues: number | null;
|
|
31
|
+
peekedName: string | null;
|
|
32
|
+
openDialog: UiSurfacesDialog | null;
|
|
33
|
+
/** The New surface dialog's live values; null while it is closed. */
|
|
34
|
+
newSurfaceDraft: NewSurfaceDraftScope | null;
|
|
35
|
+
}
|
|
36
|
+
export declare function filtersToScope(f: SurfacesFilterState): UiSurfacesFilterScope;
|
|
37
|
+
export declare function toSurfaceScopeEntry(row: SurfaceWithStats, manifestedNames: ReadonlySet<string>): UiSurfaceScopeEntry;
|
|
38
|
+
/** The condensed on-screen list as one XML bundle. */
|
|
39
|
+
export declare function buildSurfaceListBundle(visible: readonly SurfaceWithStats[], manifestedNames: ReadonlySet<string>, filters: UiSurfacesFilterScope): string;
|
|
40
|
+
export declare function buildUiSurfacesScope(input: UiSurfacesScopeInput): SurfaceScopePayload;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { getManifest } from "../runtime/registry.js";
|
|
2
|
+
import {
|
|
3
|
+
createAdminUiSurfacesScope
|
|
4
|
+
} from "../manifests/admin-ui-surfaces.manifest.js";
|
|
5
|
+
import {
|
|
6
|
+
readinessBucketOf,
|
|
7
|
+
tierFor
|
|
8
|
+
} from "../services/surfaces.service.js";
|
|
9
|
+
import { surfaceCheckState } from "../utils/surface-check-ledger.js";
|
|
10
|
+
import { xmlElement, xmlList } from "../runtime/context-bundle.js";
|
|
11
|
+
const SURFACE_LIST_MAX_ROWS = 40;
|
|
12
|
+
function filtersToScope(f) {
|
|
13
|
+
return {
|
|
14
|
+
client: f.client === "__all__" ? "all" : f.client,
|
|
15
|
+
status: f.status,
|
|
16
|
+
check_age: f.checked,
|
|
17
|
+
manifest: f.manifest,
|
|
18
|
+
parent: f.parent === "__all__" ? "all" : f.parent === "__none__" ? "none" : f.parent,
|
|
19
|
+
readiness: f.readiness
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function toSurfaceScopeEntry(row, manifestedNames) {
|
|
23
|
+
return {
|
|
24
|
+
name: row.name,
|
|
25
|
+
label: row.label ?? null,
|
|
26
|
+
client_name: row.client_name,
|
|
27
|
+
executor_name: row.executor_name ?? null,
|
|
28
|
+
parent_surface_name: row.parent_surface_name ?? null,
|
|
29
|
+
sort_order: row.sort_order,
|
|
30
|
+
tier: tierFor(row.sort_order).label,
|
|
31
|
+
is_active: row.is_active !== false,
|
|
32
|
+
readiness: readinessBucketOf(row),
|
|
33
|
+
readiness_note: row.readiness_note ?? null,
|
|
34
|
+
has_manifest: manifestedNames.has(row.name),
|
|
35
|
+
value_count: row.surfaceValueCount,
|
|
36
|
+
declared_value_count: getManifest(row.name)?.values.length ?? null,
|
|
37
|
+
agent_count: row.agentCount,
|
|
38
|
+
tool_count: row.toolCount,
|
|
39
|
+
check_state: surfaceCheckState(row),
|
|
40
|
+
last_checked_at: row.last_checked_at ?? null,
|
|
41
|
+
last_checked_by: row.last_checked_by ?? null,
|
|
42
|
+
url_pattern: row.url_pattern ?? null,
|
|
43
|
+
description: row.description ?? null
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildSurfaceListBundle(visible, manifestedNames, filters) {
|
|
47
|
+
const active = Object.fromEntries(
|
|
48
|
+
Object.entries(filters).filter(([, v]) => v !== "all")
|
|
49
|
+
);
|
|
50
|
+
return xmlList(
|
|
51
|
+
"surfaces",
|
|
52
|
+
visible,
|
|
53
|
+
(row) => xmlElement("s", {
|
|
54
|
+
name: row.name,
|
|
55
|
+
label: row.label,
|
|
56
|
+
client: row.client_name,
|
|
57
|
+
parent: row.parent_surface_name,
|
|
58
|
+
tier: `${tierFor(row.sort_order).label} ${row.sort_order}`,
|
|
59
|
+
readiness: readinessBucketOf(row),
|
|
60
|
+
values: row.surfaceValueCount,
|
|
61
|
+
declared: getManifest(row.name)?.values.length,
|
|
62
|
+
agents: row.agentCount,
|
|
63
|
+
tools: row.toolCount,
|
|
64
|
+
checked: surfaceCheckState(row),
|
|
65
|
+
active: row.is_active !== false,
|
|
66
|
+
manifest: manifestedNames.has(row.name)
|
|
67
|
+
}),
|
|
68
|
+
{ maxRows: SURFACE_LIST_MAX_ROWS, attrs: active }
|
|
69
|
+
) || '<surfaces total="0"/>';
|
|
70
|
+
}
|
|
71
|
+
function buildUiSurfacesScope(input) {
|
|
72
|
+
const filters = filtersToScope(input.filters);
|
|
73
|
+
const extras = {
|
|
74
|
+
...input.peekedName ? { peeked_surface: input.peekedName } : {},
|
|
75
|
+
...input.openDialog ? { open_dialog: input.openDialog } : {},
|
|
76
|
+
...input.newSurfaceDraft ? { new_surface_draft: input.newSurfaceDraft } : {}
|
|
77
|
+
};
|
|
78
|
+
if (input.error) {
|
|
79
|
+
return createAdminUiSurfacesScope({
|
|
80
|
+
load_state: `error: ${input.error}`,
|
|
81
|
+
filters,
|
|
82
|
+
...extras
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (input.loading && input.surfaces.length === 0) {
|
|
86
|
+
return createAdminUiSurfacesScope({ load_state: "loading", filters, ...extras });
|
|
87
|
+
}
|
|
88
|
+
const surfaces = input.surfaces.map(
|
|
89
|
+
(s) => toSurfaceScopeEntry(s, input.manifestedNames)
|
|
90
|
+
);
|
|
91
|
+
return createAdminUiSurfacesScope({
|
|
92
|
+
load_state: "ready",
|
|
93
|
+
filters,
|
|
94
|
+
surface_list: buildSurfaceListBundle(input.visible, input.manifestedNames, filters),
|
|
95
|
+
surfaces,
|
|
96
|
+
registry_counts: {
|
|
97
|
+
total: surfaces.length,
|
|
98
|
+
active: surfaces.filter((s) => s.is_active).length,
|
|
99
|
+
manifests: input.manifestedNames.size,
|
|
100
|
+
unused: surfaces.filter((s) => s.agent_count === 0 && s.tool_count === 0).length,
|
|
101
|
+
visible: input.visible.length,
|
|
102
|
+
readiness: input.readinessCounts,
|
|
103
|
+
candidates_available: input.candidatesAvailable,
|
|
104
|
+
...input.driftIssues !== null ? { drift_issues: input.driftIssues } : {}
|
|
105
|
+
},
|
|
106
|
+
client_names: [...input.clientNames],
|
|
107
|
+
...extras
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
SURFACE_LIST_MAX_ROWS,
|
|
112
|
+
buildSurfaceListBundle,
|
|
113
|
+
buildUiSurfacesScope,
|
|
114
|
+
filtersToScope,
|
|
115
|
+
toSurfaceScopeEntry
|
|
116
|
+
};
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* that dispatches the feature's refetch thunk(s). Known not-yet-covered
|
|
28
28
|
* writers (no canonical Redux refetch today, or their state is hook-local):
|
|
29
29
|
* picklist (session-cached `usePickListDetail`), dictionary, workbook,
|
|
30
|
-
* dataset
|
|
30
|
+
* dataset, document. The working document is already reconciled by
|
|
31
31
|
* its own `context_changed` re-read; the scratchpad is agent-readonly.
|
|
32
32
|
*/
|
|
33
33
|
import type { ChatRootState } from "../../store/root-state.js";
|
|
@@ -1066,7 +1066,6 @@ const APP_FEATURE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
1066
1066
|
"user_todos",
|
|
1067
1067
|
"document",
|
|
1068
1068
|
"dataset",
|
|
1069
|
-
"usertable_create",
|
|
1070
1069
|
"knowledge_search",
|
|
1071
1070
|
"document_search",
|
|
1072
1071
|
"knowledge_browse",
|
|
@@ -1202,7 +1201,6 @@ const TOOL_GLYPHS = {
|
|
|
1202
1201
|
update_plan: { icon: ListTodo, accent: "primary" },
|
|
1203
1202
|
document: { icon: FileText, accent: "slate" },
|
|
1204
1203
|
dataset: { icon: Table2, accent: "cyan" },
|
|
1205
|
-
usertable_create: { icon: Table2, accent: "cyan" },
|
|
1206
1204
|
workbook: { icon: Sheet, accent: "green" },
|
|
1207
1205
|
dictionary: { icon: BookText, accent: "amber" },
|
|
1208
1206
|
note: { icon: NotebookPen, accent: "amber" },
|