@agent-native/core 0.182.1 → 0.182.2-nightly-20260917225811
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/corpus/README.md +1 -1
- package/corpus/templates/clips/app/root.tsx +59 -10
- package/corpus/templates/clips/changelog/2026-09-17-desktop-popover-closes-when-clicking-away.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-17-self-hosted-extension-sign-in.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +45 -25
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +43 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +55 -6
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +107 -19
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +1 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +54 -22
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +174 -14
- package/corpus/templates/design/actions/apply-visual-edit.ts +3 -1
- package/corpus/templates/design/actions/create-component.ts +173 -12
- package/corpus/templates/design/actions/create-design-from-template.ts +50 -35
- package/corpus/templates/design/actions/create-design-version.ts +21 -0
- package/corpus/templates/design/actions/create-file.ts +35 -33
- package/corpus/templates/design/actions/delete-design.ts +29 -28
- package/corpus/templates/design/actions/delete-file.ts +154 -23
- package/corpus/templates/design/actions/generate-design.ts +28 -16
- package/corpus/templates/design/actions/get-component-details.ts +272 -33
- package/corpus/templates/design/actions/get-design-version.ts +9 -1
- package/corpus/templates/design/actions/go-to-main-component.ts +37 -35
- package/corpus/templates/design/actions/index-components.ts +241 -126
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +12 -9
- package/corpus/templates/design/actions/present-design-variants.ts +68 -72
- package/corpus/templates/design/actions/rename-component.ts +294 -0
- package/corpus/templates/design/actions/rename-screen.ts +10 -10
- package/corpus/templates/design/actions/update-file.ts +327 -330
- package/corpus/templates/design/actions/update-screen-source.ts +2 -43
- package/corpus/templates/design/app/components/design/EditPanel.tsx +17 -1
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +228 -24
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +17 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +151 -32
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +4 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +8 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +55 -33
- package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +4 -2
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +273 -60
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +9 -2
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +57 -9
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +13 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +14 -0
- package/corpus/templates/design/app/components/design/types.ts +9 -1
- package/corpus/templates/design/app/hooks/use-design-versions.ts +3 -2
- package/corpus/templates/design/app/i18n-breakpoints.ts +0 -110
- package/corpus/templates/design/app/i18n-data.ts +0 -10
- package/corpus/templates/design/app/pages/DesignEditor.tsx +669 -161
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +20 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +21 -3
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-component.ts +44 -3
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +69 -47
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +157 -52
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +76 -1
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +152 -44
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +131 -2
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +5 -1
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +26 -1
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +58 -8
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +63 -2
- package/corpus/templates/design/app/pages/design-editor/history.ts +3 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +74 -22
- package/corpus/templates/design/app/pages/design-editor/source-publication.ts +14 -2
- package/corpus/templates/design/changelog/2026-09-16-design-components-retain-linked-identities-when-edited-and-d.md +6 -0
- package/corpus/templates/design/changelog/2026-09-16-fix-design-nested-layer-dragging-and-screen-duplication-pari.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-drag-and-drop-keeps-duplicates-cross-screen-moves-and.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-preserves-reusable-component-identity-and-props-acros.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-open-vector-paths-no-longer-show-fills-and-interact-dimensio.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-screen-deletion-is-immediate-and-undoable-with-history-that-.md +6 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/index.html +98 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/inline-component.html +65 -0
- package/corpus/templates/design/e2e/helpers.ts +6 -1
- package/corpus/templates/design/server/lib/design-data-mutation.ts +9 -5
- package/corpus/templates/design/server/lib/design-snapshot.ts +28 -16
- package/corpus/templates/design/server/lib/design-versions.ts +208 -130
- package/corpus/templates/design/server/lib/fusion-screens.ts +46 -20
- package/corpus/templates/design/server/lib/import-design-files.ts +5 -1
- package/corpus/templates/design/server/lib/localhost-connection.ts +54 -0
- package/corpus/templates/design/server/source-workspace.ts +412 -356
- package/corpus/templates/design/shared/component-library.ts +36 -6
- package/corpus/templates/design/shared/component-model.ts +12 -2
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +231 -5
- package/corpus/templates/design/shared/source-mode.ts +22 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/AssistantChat.d.ts +4 -1
- package/dist/client/AssistantChat.js +26 -3
- package/dist/client/chat/agentkit-protocol.js +148 -34
- package/dist/client/chat/message-components.js +7 -5
- package/dist/client/chat/tool-call-display.js +4 -3
- package/dist/client/conversation/AgentConversation.js +4 -2
- package/dist/client/tool-display.d.ts +9 -0
- package/dist/client/tool-display.js +15 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.js +2 -0
- package/dist/localization/core-messages/de-DE.js +2 -0
- package/dist/localization/core-messages/en-US.d.ts +2 -0
- package/dist/localization/core-messages/en-US.js +2 -0
- package/dist/localization/core-messages/es-ES.js +2 -0
- package/dist/localization/core-messages/fr-FR.js +2 -0
- package/dist/localization/core-messages/hi-IN.js +2 -0
- package/dist/localization/core-messages/ja-JP.js +2 -0
- package/dist/localization/core-messages/ko-KR.js +2 -0
- package/dist/localization/core-messages/pt-BR.js +2 -0
- package/dist/localization/core-messages/zh-CN.js +2 -0
- package/dist/localization/core-messages/zh-TW.js +2 -0
- package/dist/localization/core-messages.d.ts +2 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/package.json +3 -3
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +0 -66
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
buildCodeLayerProjection,
|
|
23
23
|
type CodeLayerSource,
|
|
24
24
|
} from "./code-layer.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
COMPONENT_ID_ATTR,
|
|
27
|
+
COMPONENT_REF_ATTR,
|
|
28
|
+
componentNameFor,
|
|
29
|
+
} from "./component-model.js";
|
|
26
30
|
|
|
27
31
|
export interface ComponentLibraryFile {
|
|
28
32
|
id: string;
|
|
@@ -34,6 +38,10 @@ export interface ComponentLibraryFile {
|
|
|
34
38
|
export interface ComponentLibraryEntry {
|
|
35
39
|
/** Component name from `data-agent-native-component`. */
|
|
36
40
|
name: string;
|
|
41
|
+
/** Opaque id on a canonical component root, when present. */
|
|
42
|
+
componentId?: string;
|
|
43
|
+
/** Opaque id referenced by a linked component instance, when present. */
|
|
44
|
+
componentRef?: string;
|
|
37
45
|
fileId: string;
|
|
38
46
|
filename: string;
|
|
39
47
|
/** `data-agent-native-node-id` (or fallback) of the instance root. */
|
|
@@ -50,7 +58,12 @@ export interface ComponentLibraryEntry {
|
|
|
50
58
|
export function scanComponentLibrary(
|
|
51
59
|
files: ComponentLibraryFile[],
|
|
52
60
|
): ComponentLibraryEntry[] {
|
|
53
|
-
const entries:
|
|
61
|
+
const entries: Array<
|
|
62
|
+
Omit<ComponentLibraryEntry, "name"> & {
|
|
63
|
+
name?: string;
|
|
64
|
+
}
|
|
65
|
+
> = [];
|
|
66
|
+
const namesByIdentity = new Map<string, string>();
|
|
54
67
|
|
|
55
68
|
for (const file of files) {
|
|
56
69
|
const html = file.content ?? "";
|
|
@@ -68,9 +81,15 @@ export function scanComponentLibrary(
|
|
|
68
81
|
});
|
|
69
82
|
|
|
70
83
|
for (const node of projection.nodes) {
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
84
|
+
const name = componentNameFor(node) ?? undefined;
|
|
85
|
+
const componentId =
|
|
86
|
+
node.dataAttributes[COMPONENT_ID_ATTR]?.trim() || undefined;
|
|
87
|
+
const componentRef =
|
|
88
|
+
node.dataAttributes[COMPONENT_REF_ATTR]?.trim() || undefined;
|
|
89
|
+
if (!name && !componentId && !componentRef) continue;
|
|
90
|
+
|
|
91
|
+
if (name && componentId) namesByIdentity.set(componentId, name);
|
|
92
|
+
if (name && componentRef) namesByIdentity.set(componentRef, name);
|
|
74
93
|
|
|
75
94
|
// `node.id` is an ephemeral id scoped to this one projection call — it
|
|
76
95
|
// is NOT the same as the durable `data-agent-native-node-id` attribute
|
|
@@ -85,6 +104,8 @@ export function scanComponentLibrary(
|
|
|
85
104
|
|
|
86
105
|
entries.push({
|
|
87
106
|
name,
|
|
107
|
+
componentId,
|
|
108
|
+
componentRef,
|
|
88
109
|
fileId: file.id,
|
|
89
110
|
filename: file.filename,
|
|
90
111
|
nodeId,
|
|
@@ -93,7 +114,16 @@ export function scanComponentLibrary(
|
|
|
93
114
|
}
|
|
94
115
|
}
|
|
95
116
|
|
|
96
|
-
return entries
|
|
117
|
+
return entries.flatMap((entry) => {
|
|
118
|
+
const name =
|
|
119
|
+
entry.name ??
|
|
120
|
+
(entry.componentRef
|
|
121
|
+
? namesByIdentity.get(entry.componentRef)
|
|
122
|
+
: entry.componentId
|
|
123
|
+
? namesByIdentity.get(entry.componentId)
|
|
124
|
+
: undefined);
|
|
125
|
+
return name ? [{ ...entry, name }] : [];
|
|
126
|
+
});
|
|
97
127
|
}
|
|
98
128
|
|
|
99
129
|
/**
|
|
@@ -44,6 +44,15 @@ export const COMPONENT_OVERRIDES_ATTR = "data-agent-native-component-overrides";
|
|
|
44
44
|
/** Prefix for simple prop attributes stamped next to the component root. */
|
|
45
45
|
export const COMPONENT_PROP_PREFIX = "data-agent-native-prop-";
|
|
46
46
|
|
|
47
|
+
/** Return the identity that survives projection rebuilds and reloads. */
|
|
48
|
+
export function stableComponentNodeId(node: CodeLayerNode): string {
|
|
49
|
+
return node.dataAttributes["data-agent-native-node-id"]?.trim() || node.id;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function componentIndexId(designId: string, name: string): string {
|
|
53
|
+
return `ci_${designId}_${name.toLowerCase().replace(/[^a-z0-9]/g, "_")}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
// ─── Extracted prop value ─────────────────────────────────────────────────────
|
|
48
57
|
|
|
49
58
|
export interface ComponentPropValue {
|
|
@@ -228,14 +237,15 @@ export function instanceFromNode(
|
|
|
228
237
|
const alpineDataRaw = node.attributes["x-data"];
|
|
229
238
|
const alpineData =
|
|
230
239
|
typeof alpineDataRaw === "string" ? alpineDataRaw : undefined;
|
|
240
|
+
const stableNodeId = stableComponentNodeId(node);
|
|
231
241
|
|
|
232
242
|
return {
|
|
233
|
-
instanceId:
|
|
243
|
+
instanceId: stableNodeId,
|
|
234
244
|
name,
|
|
235
245
|
props: extractProps(node),
|
|
236
246
|
alpineData,
|
|
237
247
|
selector: node.selector,
|
|
238
|
-
nodeId:
|
|
248
|
+
nodeId: stableNodeId,
|
|
239
249
|
componentIndexId,
|
|
240
250
|
componentId: node.dataAttributes[COMPONENT_ID_ATTR]?.trim() || undefined,
|
|
241
251
|
componentRef: node.dataAttributes[COMPONENT_REF_ATTR]?.trim() || undefined,
|
|
@@ -32,7 +32,18 @@ export type LocalJsxLeafIntent =
|
|
|
32
32
|
from?: string;
|
|
33
33
|
to?: string;
|
|
34
34
|
}
|
|
35
|
-
| { kind: "style"; property: string; value: string }
|
|
35
|
+
| { kind: "style"; property: string; value: string }
|
|
36
|
+
| {
|
|
37
|
+
kind: "attribute";
|
|
38
|
+
name: string;
|
|
39
|
+
value: string;
|
|
40
|
+
expectedValue?: string;
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
kind: "attributes";
|
|
44
|
+
values: Record<string, string>;
|
|
45
|
+
expectedValues?: Record<string, string | undefined>;
|
|
46
|
+
};
|
|
36
47
|
|
|
37
48
|
export interface LocalJsxVisualEditResult {
|
|
38
49
|
content: string;
|
|
@@ -51,6 +62,23 @@ interface OpeningTag {
|
|
|
51
62
|
selfClosing: boolean;
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
export interface LocalJsxLiteralProp {
|
|
66
|
+
name: string;
|
|
67
|
+
value: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isEscaped(value: string, index: number): boolean {
|
|
71
|
+
let backslashes = 0;
|
|
72
|
+
for (
|
|
73
|
+
let cursor = index - 1;
|
|
74
|
+
cursor >= 0 && value[cursor] === "\\";
|
|
75
|
+
cursor--
|
|
76
|
+
) {
|
|
77
|
+
backslashes += 1;
|
|
78
|
+
}
|
|
79
|
+
return backslashes % 2 === 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
54
82
|
function offsetAt(
|
|
55
83
|
content: string,
|
|
56
84
|
line: number,
|
|
@@ -108,7 +136,7 @@ function scanOpeningTags(content: string): OpeningTag[] {
|
|
|
108
136
|
) {
|
|
109
137
|
const quote = content[start];
|
|
110
138
|
for (start += 1; start < content.length; start += 1) {
|
|
111
|
-
if (content[start] === quote && content
|
|
139
|
+
if (content[start] === quote && !isEscaped(content, start)) break;
|
|
112
140
|
}
|
|
113
141
|
continue;
|
|
114
142
|
}
|
|
@@ -127,7 +155,7 @@ function scanOpeningTags(content: string): OpeningTag[] {
|
|
|
127
155
|
) {
|
|
128
156
|
const char = content[index] ?? "";
|
|
129
157
|
if (quote) {
|
|
130
|
-
if (char === quote && content
|
|
158
|
+
if (char === quote && !isEscaped(content, index)) quote = "";
|
|
131
159
|
continue;
|
|
132
160
|
}
|
|
133
161
|
if (char === '"' || char === "'") {
|
|
@@ -153,6 +181,104 @@ function scanOpeningTags(content: string): OpeningTag[] {
|
|
|
153
181
|
return tags;
|
|
154
182
|
}
|
|
155
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Read quoted component props from the authored opening tag at a verified
|
|
186
|
+
* source anchor. Dynamic and shorthand attributes make the full literal prop
|
|
187
|
+
* set uncertain, so this reader fails closed; spreads can shadow every prop.
|
|
188
|
+
*/
|
|
189
|
+
export function readLiteralJsxPropsAtAnchor(args: {
|
|
190
|
+
content: string;
|
|
191
|
+
anchor: LocalJsxSourceAnchor;
|
|
192
|
+
}): LocalJsxLiteralProp[] | undefined {
|
|
193
|
+
const { content, anchor } = args;
|
|
194
|
+
if (
|
|
195
|
+
anchor.positionPrecision !== "authored" ||
|
|
196
|
+
(anchor.runtimeMultiplicity ?? 1) !== 1 ||
|
|
197
|
+
anchor.scope === "repeated-render" ||
|
|
198
|
+
anchor.scope === "shared-component-definition"
|
|
199
|
+
) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
if (offsetAt(content, anchor.line, anchor.column) === null) return undefined;
|
|
203
|
+
const candidates = scanOpeningTags(content).filter((tag) => {
|
|
204
|
+
const position = positionAt(content, tag.start);
|
|
205
|
+
return (
|
|
206
|
+
position.line === anchor.line &&
|
|
207
|
+
Math.abs(position.column - anchor.column) <= 1
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
if (candidates.length !== 1) return undefined;
|
|
211
|
+
|
|
212
|
+
const tag = candidates[0]!;
|
|
213
|
+
const opening = content.slice(tag.start, tag.end);
|
|
214
|
+
if (/\{\s*\.\.\./.test(opening)) return undefined;
|
|
215
|
+
|
|
216
|
+
const props: LocalJsxLiteralProp[] = [];
|
|
217
|
+
const reserved = new Set(["children", "key", "ref"]);
|
|
218
|
+
let sawUnsupported = false;
|
|
219
|
+
let index = 1 + tag.name.length;
|
|
220
|
+
while (index < opening.length - 1) {
|
|
221
|
+
while (/\s/.test(opening[index] ?? "")) index += 1;
|
|
222
|
+
if (opening[index] === ">" || opening.slice(index, index + 2) === "/>")
|
|
223
|
+
break;
|
|
224
|
+
if (opening[index] === "{") {
|
|
225
|
+
let depth = 0;
|
|
226
|
+
do {
|
|
227
|
+
const char = opening[index];
|
|
228
|
+
if (char === "{") depth += 1;
|
|
229
|
+
else if (char === "}") depth -= 1;
|
|
230
|
+
index += 1;
|
|
231
|
+
} while (index < opening.length && depth > 0);
|
|
232
|
+
if (depth > 0) return undefined;
|
|
233
|
+
sawUnsupported = true;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const nameMatch = /^[A-Za-z_:][A-Za-z0-9:_.-]*/.exec(opening.slice(index));
|
|
238
|
+
if (!nameMatch) return undefined;
|
|
239
|
+
const name = nameMatch[0]!;
|
|
240
|
+
index += name.length;
|
|
241
|
+
while (/\s/.test(opening[index] ?? "")) index += 1;
|
|
242
|
+
if (opening[index] !== "=") {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
index += 1;
|
|
246
|
+
while (/\s/.test(opening[index] ?? "")) index += 1;
|
|
247
|
+
const quote = opening[index];
|
|
248
|
+
if (quote !== '"' && quote !== "'") {
|
|
249
|
+
let depth = 0;
|
|
250
|
+
if (opening[index] === "{") {
|
|
251
|
+
do {
|
|
252
|
+
const char = opening[index];
|
|
253
|
+
if (char === "{") depth += 1;
|
|
254
|
+
else if (char === "}") depth -= 1;
|
|
255
|
+
index += 1;
|
|
256
|
+
} while (index < opening.length && depth > 0);
|
|
257
|
+
}
|
|
258
|
+
if (depth > 0) return undefined;
|
|
259
|
+
sawUnsupported = true;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
index += 1;
|
|
263
|
+
const valueStart = index;
|
|
264
|
+
while (
|
|
265
|
+
index < opening.length &&
|
|
266
|
+
(opening[index] !== quote || isEscaped(opening, index))
|
|
267
|
+
) {
|
|
268
|
+
index += 1;
|
|
269
|
+
}
|
|
270
|
+
if (index >= opening.length) return undefined;
|
|
271
|
+
if (!reserved.has(name)) {
|
|
272
|
+
props.push({
|
|
273
|
+
name,
|
|
274
|
+
value: decodeJsxAttributeValue(opening.slice(valueStart, index)),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
index += 1;
|
|
278
|
+
}
|
|
279
|
+
return sawUnsupported && props.length === 0 ? undefined : props;
|
|
280
|
+
}
|
|
281
|
+
|
|
156
282
|
function fail(
|
|
157
283
|
content: string,
|
|
158
284
|
status: LocalJsxVisualEditResult["result"]["status"],
|
|
@@ -190,6 +316,32 @@ function jsxText(value: string): string {
|
|
|
190
316
|
.join("}");
|
|
191
317
|
}
|
|
192
318
|
|
|
319
|
+
function jsxAttributeValue(value: string): string {
|
|
320
|
+
return value
|
|
321
|
+
.split("&")
|
|
322
|
+
.join("&")
|
|
323
|
+
.split('"')
|
|
324
|
+
.join(""")
|
|
325
|
+
.split("<")
|
|
326
|
+
.join("<")
|
|
327
|
+
.split(">")
|
|
328
|
+
.join(">");
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function decodeJsxAttributeValue(value: string): string {
|
|
332
|
+
return value
|
|
333
|
+
.replace(/"/g, '"')
|
|
334
|
+
.replace(/'/g, "'")
|
|
335
|
+
.replace(/</g, "<")
|
|
336
|
+
.replace(/>/g, ">")
|
|
337
|
+
.replace(/&/g, "&");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function jsxAttributeName(name: string): string | null {
|
|
341
|
+
const normalized = name.trim();
|
|
342
|
+
return /^[A-Za-z_:][A-Za-z0-9:_.-]*$/.test(normalized) ? normalized : null;
|
|
343
|
+
}
|
|
344
|
+
|
|
193
345
|
function camelProperty(property: string): string | null {
|
|
194
346
|
const value = property
|
|
195
347
|
.trim()
|
|
@@ -203,11 +355,11 @@ export function planLocalJsxVisualEdit(args: {
|
|
|
203
355
|
intent: LocalJsxLeafIntent;
|
|
204
356
|
}): LocalJsxVisualEditResult {
|
|
205
357
|
const { content, anchor, intent } = args;
|
|
206
|
-
if (anchor.positionPrecision
|
|
358
|
+
if (anchor.positionPrecision !== "authored") {
|
|
207
359
|
return fail(
|
|
208
360
|
content,
|
|
209
361
|
"needsAgent",
|
|
210
|
-
"The source anchor
|
|
362
|
+
"The source anchor does not have verified authored coordinates, so it cannot be seeked safely in the authored file.",
|
|
211
363
|
);
|
|
212
364
|
}
|
|
213
365
|
if (
|
|
@@ -353,6 +505,80 @@ export function planLocalJsxVisualEdit(args: {
|
|
|
353
505
|
);
|
|
354
506
|
}
|
|
355
507
|
|
|
508
|
+
if (intent.kind === "attribute" || intent.kind === "attributes") {
|
|
509
|
+
const values =
|
|
510
|
+
intent.kind === "attribute"
|
|
511
|
+
? { [intent.name]: intent.value }
|
|
512
|
+
: intent.values;
|
|
513
|
+
const entries = Object.entries(values);
|
|
514
|
+
if (entries.length === 0) {
|
|
515
|
+
return fail(
|
|
516
|
+
content,
|
|
517
|
+
"unsupported",
|
|
518
|
+
"At least one JSX attribute is required.",
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
let nextOpening = opening;
|
|
523
|
+
for (const [rawName, value] of entries) {
|
|
524
|
+
const name = jsxAttributeName(rawName);
|
|
525
|
+
if (!name) {
|
|
526
|
+
return fail(
|
|
527
|
+
content,
|
|
528
|
+
"unsupported",
|
|
529
|
+
`The JSX attribute name "${rawName}" is not a safe literal identifier.`,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
533
|
+
const literal = new RegExp(
|
|
534
|
+
`(\\s${escapedName}\\s*=\\s*)(["'])((?:\\\\.|(?!\\2)[\\s\\S])*)\\2`,
|
|
535
|
+
"s",
|
|
536
|
+
);
|
|
537
|
+
const literalMatch = literal.exec(nextOpening);
|
|
538
|
+
const expectedValue =
|
|
539
|
+
intent.kind === "attribute" && rawName === intent.name
|
|
540
|
+
? intent.expectedValue
|
|
541
|
+
: intent.kind === "attributes"
|
|
542
|
+
? intent.expectedValues?.[rawName]
|
|
543
|
+
: undefined;
|
|
544
|
+
if (expectedValue !== undefined) {
|
|
545
|
+
if (
|
|
546
|
+
!literalMatch ||
|
|
547
|
+
decodeJsxAttributeValue(literalMatch[3] ?? "") !== expectedValue
|
|
548
|
+
) {
|
|
549
|
+
return fail(
|
|
550
|
+
content,
|
|
551
|
+
"conflict",
|
|
552
|
+
`The JSX prop "${name}" changed since the selected instance was read. Refresh and retry.`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (literalMatch) {
|
|
557
|
+
nextOpening = nextOpening.replace(
|
|
558
|
+
literal,
|
|
559
|
+
(_match, prefix: string) => `${prefix}"${jsxAttributeValue(value)}"`,
|
|
560
|
+
);
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (new RegExp(`\\s${escapedName}(?:\\s|=|/?>)`).test(nextOpening)) {
|
|
564
|
+
return fail(
|
|
565
|
+
content,
|
|
566
|
+
"needsAgent",
|
|
567
|
+
`The JSX attribute "${name}" is dynamic or shorthand and requires semantic source inspection.`,
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const insertAt = nextOpening.lastIndexOf(tag.selfClosing ? "/>" : ">");
|
|
571
|
+
nextOpening = `${nextOpening.slice(0, insertAt)} ${name}="${jsxAttributeValue(value)}"${nextOpening.slice(insertAt)}`;
|
|
572
|
+
}
|
|
573
|
+
return replaceRange(
|
|
574
|
+
content,
|
|
575
|
+
tag.start,
|
|
576
|
+
tag.end,
|
|
577
|
+
nextOpening,
|
|
578
|
+
"Literal JSX attributes updated.",
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
356
582
|
const property = camelProperty(intent.property);
|
|
357
583
|
if (!property)
|
|
358
584
|
return fail(
|
|
@@ -117,6 +117,28 @@ export type ElementProvenanceFramework =
|
|
|
117
117
|
| "angular"
|
|
118
118
|
| "lwc";
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Runtime component boundary discovered by a framework bridge. This is
|
|
122
|
+
* deliberately separate from the durable component annotations used by the
|
|
123
|
+
* inline component model: the bridge can identify an unannotated React
|
|
124
|
+
* boundary before promotion, while promotion still writes the canonical
|
|
125
|
+
* `data-agent-native-component` marker.
|
|
126
|
+
*/
|
|
127
|
+
export interface RuntimeComponentIdentity {
|
|
128
|
+
componentId: string;
|
|
129
|
+
instanceId: string;
|
|
130
|
+
name: string;
|
|
131
|
+
framework: ElementProvenanceFramework;
|
|
132
|
+
sourceFile?: string;
|
|
133
|
+
line?: number;
|
|
134
|
+
column?: number;
|
|
135
|
+
method?: ElementProvenanceMethod;
|
|
136
|
+
ownerKey?: string;
|
|
137
|
+
props: Array<{ name: string; value: string }>;
|
|
138
|
+
writeCapability: "authored-jsx-literal" | "unsupported";
|
|
139
|
+
reason?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
export const ELEMENT_PROVENANCE_METHODS: readonly ElementProvenanceMethod[] = [
|
|
121
143
|
"data-attribute",
|
|
122
144
|
"debug-source",
|
|
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
186
186
|
duplicate?: undefined;
|
|
187
187
|
eventId?: undefined;
|
|
188
188
|
} | {
|
|
189
|
-
error?: undefined;
|
|
190
189
|
accepted: boolean;
|
|
191
190
|
duplicate: boolean;
|
|
192
191
|
eventId: string;
|
|
192
|
+
error?: undefined;
|
|
193
193
|
}>;
|
|
@@ -168,8 +168,9 @@ export declare function resolveAssistantChatRunningState({ forceStopped, isRunti
|
|
|
168
168
|
isRunning: boolean;
|
|
169
169
|
showRunningInUI: boolean;
|
|
170
170
|
};
|
|
171
|
-
export declare function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, labels, }: {
|
|
171
|
+
export declare function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, runningActivityTool, isAutoResuming, isReconnecting, hasReconnectContent, labels, }: {
|
|
172
172
|
runningActivityLabel: string | null | undefined;
|
|
173
|
+
runningActivityTool?: string | null;
|
|
173
174
|
isAutoResuming: boolean;
|
|
174
175
|
isReconnecting: boolean;
|
|
175
176
|
hasReconnectContent: boolean;
|
|
@@ -183,6 +184,8 @@ export declare function resolveAssistantChatRunningStatusLabel({ runningActivity
|
|
|
183
184
|
preparing?: (activity: string) => string;
|
|
184
185
|
writing?: (activity: string) => string;
|
|
185
186
|
stillGenerating?: (activity: string) => string;
|
|
187
|
+
runningTool?: (toolName: string) => string;
|
|
188
|
+
toolDisplayName?: (toolName: string) => string;
|
|
186
189
|
};
|
|
187
190
|
}): string;
|
|
188
191
|
export declare function resolveAssistantChatComposerPlaceholder(composerPlaceholder: string | null | undefined): string;
|
|
@@ -43,6 +43,7 @@ import { claimRunStream, createRunStreamToken, ownsRunStream, releaseRunStream,
|
|
|
43
43
|
import { signOut } from "./sign-out.js";
|
|
44
44
|
import { AgentAutoContinueSignal, readSSEStreamRaw, settleInterruptedToolCalls, } from "./sse-event-processor.js";
|
|
45
45
|
import { ThinkingDisplayProvider } from "./thinking-display.js";
|
|
46
|
+
import { humanizeToolName, runningToolLabel, toolLabel, } from "./tool-display.js";
|
|
46
47
|
import { callAction, useActionMutation, useActionQuery } from "./use-action.js";
|
|
47
48
|
import { useAgentEngineConfigured } from "./use-agent-engine-configured.js";
|
|
48
49
|
import { appendChatThreadScopeParams, } from "./use-chat-threads.js";
|
|
@@ -1129,7 +1130,7 @@ export function resolveAssistantChatRunningState({ forceStopped, isRuntimeRunnin
|
|
|
1129
1130
|
showRunningInUI: !forceStopped && !hasTerminalRunError && (isRunning || isAutoResuming),
|
|
1130
1131
|
};
|
|
1131
1132
|
}
|
|
1132
|
-
export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, labels = {
|
|
1133
|
+
export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, runningActivityTool, isAutoResuming, isReconnecting, hasReconnectContent, labels = {
|
|
1133
1134
|
thinking: "Thinking",
|
|
1134
1135
|
resuming: "Resuming",
|
|
1135
1136
|
stillWorking: "Still working",
|
|
@@ -1143,6 +1144,15 @@ export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, i
|
|
|
1143
1144
|
if (runningActivityLabel === "Contacting model") {
|
|
1144
1145
|
return labels.contactingModel ?? "Contacting model";
|
|
1145
1146
|
}
|
|
1147
|
+
// `tool_start` stores the English `runningToolLabel(tool)`. The tool name
|
|
1148
|
+
// rides along in state, so re-derive the label here instead of shipping the
|
|
1149
|
+
// stored English through to the status line.
|
|
1150
|
+
const activityTool = runningActivityTool?.trim();
|
|
1151
|
+
if (activityTool &&
|
|
1152
|
+
labels.runningTool &&
|
|
1153
|
+
runningActivityLabel === runningToolLabel(activityTool)) {
|
|
1154
|
+
return labels.runningTool(activityTool);
|
|
1155
|
+
}
|
|
1146
1156
|
const localizedActivityPatterns = [
|
|
1147
1157
|
[/^Starting (.+)\.\.\.$/, labels.starting],
|
|
1148
1158
|
[/^Preparing (.+)\.\.\.$/, labels.preparing],
|
|
@@ -1151,8 +1161,16 @@ export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, i
|
|
|
1151
1161
|
];
|
|
1152
1162
|
for (const [pattern, translateActivity] of localizedActivityPatterns) {
|
|
1153
1163
|
const match = runningActivityLabel.match(pattern);
|
|
1154
|
-
if (match?.[1] && translateActivity)
|
|
1155
|
-
|
|
1164
|
+
if (match?.[1] && translateActivity) {
|
|
1165
|
+
// The captured activity is the derived action name. When it matches the
|
|
1166
|
+
// tool this run is on, prefer the app's catalog label for it.
|
|
1167
|
+
const activity = activityTool &&
|
|
1168
|
+
labels.toolDisplayName &&
|
|
1169
|
+
match[1] === humanizeToolName(activityTool)
|
|
1170
|
+
? labels.toolDisplayName(activityTool)
|
|
1171
|
+
: match[1];
|
|
1172
|
+
return translateActivity(activity);
|
|
1173
|
+
}
|
|
1156
1174
|
}
|
|
1157
1175
|
return runningActivityLabel;
|
|
1158
1176
|
}
|
|
@@ -2123,6 +2141,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2123
2141
|
// ongoing work instead of exposing "Reconnecting" mid-chat.
|
|
2124
2142
|
const runningStatusLabel = resolveAssistantChatRunningStatusLabel({
|
|
2125
2143
|
runningActivityLabel,
|
|
2144
|
+
runningActivityTool,
|
|
2126
2145
|
isAutoResuming,
|
|
2127
2146
|
isReconnecting,
|
|
2128
2147
|
hasReconnectContent: reconnectContent.length > 0,
|
|
@@ -2136,6 +2155,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2136
2155
|
preparing: (activity) => t("agentChat.status.preparing", { activity }),
|
|
2137
2156
|
writing: (activity) => t("agentChat.status.writing", { activity }),
|
|
2138
2157
|
stillGenerating: (activity) => t("agentChat.status.stillGenerating", { activity }),
|
|
2158
|
+
runningTool: (toolName) => t("agentChat.status.runningTool", {
|
|
2159
|
+
activity: toolLabel(t, toolName),
|
|
2160
|
+
}),
|
|
2161
|
+
toolDisplayName: (toolName) => toolLabel(t, toolName),
|
|
2139
2162
|
},
|
|
2140
2163
|
});
|
|
2141
2164
|
const reconnectActivityContent = useMemo(() => isReconnecting || reconnectFrozen
|