@datalayer/agent-runtimes 1.3.84 → 1.3.86
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/lib/chat/ChatFloating.d.ts +5 -4
- package/lib/chat/ChatFloating.js +8 -6
- package/lib/chat/ChatLanguage.d.ts +17 -0
- package/lib/chat/ChatLanguage.js +29 -0
- package/lib/chat/assistant/AssistantStage.d.ts +4 -4
- package/lib/chat/assistant/AssistantStage.js +4 -2
- package/lib/chat/assistant/BalloonParts.d.ts +2 -1
- package/lib/chat/assistant/BalloonParts.js +12 -5
- package/lib/chat/assistant/BalloonVisual.js +3 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
- package/lib/chat/assistant/ConversationBalloon.js +3 -2
- package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
- package/lib/chat/assistant/SpeechBalloon.js +10 -7
- package/lib/chat/assistant/toolLine.d.ts +8 -4
- package/lib/chat/assistant/toolLine.js +13 -9
- package/lib/chat/base/ChatBase.js +14 -11
- package/lib/chat/display/EmptyState.js +3 -1
- package/lib/chat/index.d.ts +2 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/messages/ChatMessages.js +3 -1
- package/lib/chat/messages/TurnFooter.js +3 -1
- package/lib/chat/prompt/InputPrompt.js +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +3 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
- package/lib/chat/tools/ToolApprovalBanner.js +5 -3
- package/lib/chat/tools/ToolApprovalDialog.js +5 -3
- package/lib/chat/tools/ToolCallDisplay.js +29 -25
- package/lib/chat/words.d.ts +102 -0
- package/lib/chat/words.js +460 -0
- package/lib/components/a2ui/custom/index.d.ts +39 -0
- package/lib/components/a2ui/custom/index.js +95 -0
- package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
- package/lib/components/a2ui/custom/sandbox.js +188 -0
- package/lib/components/a2ui/datalayer/index.d.ts +1 -1
- package/lib/components/a2ui/datalayer/index.js +1 -1
- package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
- package/lib/components/a2ui/datalayer/schema.js +12 -1
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +12 -1
- package/lib/components/a2ui/visibleWhen.js +24 -3
- package/lib/components/inspector/AgentInspector.d.ts +25 -44
- package/lib/components/inspector/AgentInspector.js +27 -215
- package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
- package/lib/components/inspector/AgentInspectorDialog.js +2 -2
- package/lib/components/inspector/a2aSpans.d.ts +51 -0
- package/lib/components/inspector/a2aSpans.js +373 -0
- package/lib/components/inspector/agentSpans.d.ts +120 -0
- package/lib/components/inspector/agentSpans.js +253 -0
- package/lib/components/inspector/chatSpans.d.ts +36 -0
- package/lib/components/inspector/chatSpans.js +50 -0
- package/lib/components/inspector/classify.d.ts +11 -8
- package/lib/components/inspector/classify.js +23 -24
- package/lib/components/inspector/index.d.ts +7 -7
- package/lib/components/inspector/index.js +7 -7
- package/lib/components/teams/A2ATeamGraph.d.ts +117 -22
- package/lib/components/teams/A2ATeamGraph.js +217 -97
- package/lib/components/teams/SceneTranscript.d.ts +29 -0
- package/lib/components/teams/SceneTranscript.js +54 -0
- package/lib/components/teams/SceneView.d.ts +41 -0
- package/lib/components/teams/SceneView.js +28 -0
- package/lib/components/teams/index.d.ts +4 -0
- package/lib/components/teams/index.js +4 -0
- package/lib/components/teams/sceneStage.d.ts +120 -0
- package/lib/components/teams/sceneStage.js +170 -0
- package/lib/components/teams/sceneTranscript.d.ts +74 -0
- package/lib/components/teams/sceneTranscript.js +294 -0
- package/lib/components/teams/teamConnections.d.ts +2 -2
- package/lib/components/teams/teamConnections.js +3 -3
- package/lib/components/teams/useA2ATeam.d.ts +94 -22
- package/lib/components/teams/useA2ATeam.js +356 -140
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/examples/ScenesExample.d.ts +26 -0
- package/lib/examples/ScenesExample.js +241 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/exampleGroups.js +3 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +4 -0
- package/lib/loop/apps/AppComposer.d.ts +2 -1
- package/lib/loop/apps/AppComposer.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +2 -1
- package/lib/loop/apps/AppRenderer.js +34 -25
- package/lib/loop/apps/appspec.d.ts +11 -1
- package/lib/loop/apps/appspec.js +115 -1
- package/lib/loop/apps/checks.d.ts +8 -0
- package/lib/loop/apps/checks.js +106 -4
- package/lib/loop/apps/customComponents.d.ts +41 -0
- package/lib/loop/apps/customComponents.js +260 -0
- package/lib/loop/apps/hostTools.d.ts +8 -1
- package/lib/loop/apps/hostTools.js +9 -0
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +1 -0
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +11 -3
- package/lib/loop/embed/AppEmbed.js +42 -16
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +61 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/hostBridge.d.ts +28 -2
- package/lib/loop/embed/hostBridge.js +22 -2
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
- package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
- package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
- package/lib/loop/plugins/app-components/index.d.ts +26 -0
- package/lib/loop/plugins/app-components/index.js +56 -0
- package/lib/loop/plugins/app-elements/AppElements.js +4 -1
- package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
- package/lib/loop/plugins/app-page/AppPage.js +106 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
- package/lib/loop/plugins/app-page/appPageModel.js +120 -4
- package/lib/loop/plugins/app-page/index.d.ts +1 -0
- package/lib/loop/plugins/app-page/index.js +1 -0
- package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
- package/lib/loop/plugins/app-page/pageRun.js +111 -0
- package/lib/loop/plugins/assistant/index.js +8 -6
- package/lib/loop/plugins/chat/ChatView.js +6 -4
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/chat/index.js +2 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1384 -231
- package/lib/specs/appspecSchema.js +191 -1
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +8 -2
- package/lib/specs/scenes.d.ts +21 -0
- package/lib/specs/scenes.js +1191 -0
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/types/agentspecs.d.ts +70 -0
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/scenes.d.ts +207 -0
- package/lib/types/scenes.js +5 -0
- package/lib/types/teams.d.ts +13 -2
- package/package.json +4 -4
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_apps.py +4 -2
- package/scripts/codegen/generate_scenes.py +301 -0
- package/scripts/codegen/generate_teams.py +15 -2
- package/lib/components/inspector/JsonTree.d.ts +0 -24
- package/lib/components/inspector/JsonTree.js +0 -89
- package/lib/components/inspector/a2aInspect.d.ts +0 -56
- package/lib/components/inspector/a2aInspect.js +0 -478
- package/lib/components/inspector/agentInspector.d.ts +0 -179
- package/lib/components/inspector/agentInspector.js +0 -267
- package/lib/components/inspector/chatInspect.d.ts +0 -38
- package/lib/components/inspector/chatInspect.js +0 -65
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useIAMStore } from '@datalayer/core/lib/state/substates/IAMState';
|
|
3
4
|
import { Box } from '@datalayer/primer-addons';
|
|
4
5
|
import { signal } from '@datalayer/reactor';
|
|
5
6
|
import { useContributions, useSignalValue } from '@datalayer/reactor/react';
|
|
6
7
|
import { catalogOfBlocks } from '../../../components/a2ui';
|
|
7
8
|
import { LoopCanvasBlock, LoopChatTurn, } from '../../core';
|
|
8
9
|
import { InlineSurface, SURFACE_CATALOG_ID, } from '../a2ui-surface/InlineSurface';
|
|
9
|
-
import {
|
|
10
|
+
import { useOwnComponents } from '../a2ui-surface/ownComponents';
|
|
11
|
+
import { appPageAction, appPageData, appPageMessages, pageInputsOf, pageOf, } from './appPageModel';
|
|
12
|
+
import { openPageSession, outputsData, runPageIn, } from './pageRun';
|
|
13
|
+
/** How long the page waits after an input changed before it runs (P-05). */
|
|
14
|
+
export const PAGE_RUN_DELAY_MS = 300;
|
|
10
15
|
/** No chat in the workspace: the page reads a turn that never starts. */
|
|
11
16
|
const NO_TURN = signal({ id: 0, status: 'idle' });
|
|
12
17
|
const NO_CONVERSATION = signal([]);
|
|
@@ -42,13 +47,15 @@ export function AppPage({ app, workspace }) {
|
|
|
42
47
|
const blocks = useContributions(LoopCanvasBlock);
|
|
43
48
|
const contributed = [...new Set(blocks.map(entry => entry.value.id))].sort();
|
|
44
49
|
const drawnWith = contributed.join(',');
|
|
50
|
+
// The renderers of the components its developer wrote (P-17).
|
|
51
|
+
const own = useOwnComponents();
|
|
45
52
|
const drawing = useMemo(() => {
|
|
46
53
|
const missing = blocksMissing(componentsOnPage(messages), contributed);
|
|
47
54
|
if (missing) {
|
|
48
55
|
return { problem: missing };
|
|
49
56
|
}
|
|
50
57
|
try {
|
|
51
|
-
return { catalog: catalogOfBlocks(contributed) };
|
|
58
|
+
return { catalog: catalogOfBlocks(contributed, own) };
|
|
52
59
|
}
|
|
53
60
|
catch (error) {
|
|
54
61
|
return {
|
|
@@ -56,9 +63,97 @@ export function AppPage({ app, workspace }) {
|
|
|
56
63
|
};
|
|
57
64
|
}
|
|
58
65
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59
|
-
}, [messages, drawnWith]);
|
|
60
|
-
const data = useMemo(() => appPageData(app, turn, conversation), [app, turn, conversation]);
|
|
66
|
+
}, [messages, drawnWith, own]);
|
|
61
67
|
const [refusal, setRefusal] = useState(null);
|
|
68
|
+
// A widget's page written in its code (P-05): its outputs, as last answered.
|
|
69
|
+
const page = pageOf(app);
|
|
70
|
+
const [outputs, setOutputs] = useState({});
|
|
71
|
+
const data = useMemo(() => ({
|
|
72
|
+
...appPageData(app, turn, conversation),
|
|
73
|
+
...outputsData(outputs),
|
|
74
|
+
}), [app, turn, conversation, outputs]);
|
|
75
|
+
const token = useIAMStore(state => state.token) ?? '';
|
|
76
|
+
const pageContext = useRef({ serverUrl: '', agentId: '' });
|
|
77
|
+
pageContext.current = {
|
|
78
|
+
serverUrl: workspace.sandbox?.agentBaseUrl || workspace.serverUrl || '',
|
|
79
|
+
agentId: workspace.agentId || app.id,
|
|
80
|
+
token: token || undefined,
|
|
81
|
+
};
|
|
82
|
+
const pageSession = useRef(null);
|
|
83
|
+
const pageRunning = useRef(false);
|
|
84
|
+
const pageNext = useRef(null);
|
|
85
|
+
const pageSent = useRef('');
|
|
86
|
+
const alive = useRef(true);
|
|
87
|
+
// The latest inputs win: one run at a time, the last ones written next.
|
|
88
|
+
const runPage = useCallback((inputs) => {
|
|
89
|
+
pageNext.current = inputs;
|
|
90
|
+
if (pageRunning.current) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
pageRunning.current = true;
|
|
94
|
+
void (async () => {
|
|
95
|
+
while (pageNext.current && alive.current) {
|
|
96
|
+
const next = pageNext.current;
|
|
97
|
+
pageNext.current = null;
|
|
98
|
+
const context = pageContext.current;
|
|
99
|
+
try {
|
|
100
|
+
pageSession.current ??= openPageSession(context);
|
|
101
|
+
const turn = await runPageIn(context, await pageSession.current, next);
|
|
102
|
+
if (!alive.current)
|
|
103
|
+
break;
|
|
104
|
+
if ('refused' in turn) {
|
|
105
|
+
setRefusal(turn.refused);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
setRefusal(null);
|
|
109
|
+
setOutputs(turn.outputs);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// No session: the next change opens one again.
|
|
114
|
+
pageSession.current = null;
|
|
115
|
+
if (alive.current) {
|
|
116
|
+
setRefusal(error instanceof Error ? error.message : String(error));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
pageRunning.current = false;
|
|
121
|
+
})();
|
|
122
|
+
}, []);
|
|
123
|
+
// A live page runs as its inputs change, from the start (their defaults).
|
|
124
|
+
const unwatch = useRef(null);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
alive.current = true;
|
|
127
|
+
return () => {
|
|
128
|
+
alive.current = false;
|
|
129
|
+
unwatch.current?.();
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
const onSurface = useCallback((surface) => {
|
|
133
|
+
if (!page?.live) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
unwatch.current?.();
|
|
137
|
+
let timer;
|
|
138
|
+
const changed = (value) => {
|
|
139
|
+
const inputs = pageInputsOf(app, value);
|
|
140
|
+
const said = JSON.stringify(inputs);
|
|
141
|
+
if (said === pageSent.current) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
timer = setTimeout(() => {
|
|
146
|
+
pageSent.current = said;
|
|
147
|
+
runPage(inputs);
|
|
148
|
+
}, PAGE_RUN_DELAY_MS);
|
|
149
|
+
};
|
|
150
|
+
const watching = surface.dataModel.subscribe('/inputs', changed);
|
|
151
|
+
changed(watching.value);
|
|
152
|
+
unwatch.current = () => {
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
watching.unsubscribe();
|
|
155
|
+
};
|
|
156
|
+
}, [app, page?.live, runPage]);
|
|
62
157
|
// The workspace changes as the chat reports itself; the handler reads the latest.
|
|
63
158
|
const workspaceRef = useRef(workspace);
|
|
64
159
|
workspaceRef.current = workspace;
|
|
@@ -72,7 +167,10 @@ export function AppPage({ app, workspace }) {
|
|
|
72
167
|
return;
|
|
73
168
|
}
|
|
74
169
|
setRefusal(null);
|
|
75
|
-
if ('
|
|
170
|
+
if ('runPage' in outcome) {
|
|
171
|
+
runPage(outcome.runPage);
|
|
172
|
+
}
|
|
173
|
+
else if ('stop' in outcome) {
|
|
76
174
|
controls.stop?.();
|
|
77
175
|
}
|
|
78
176
|
else if ('newChat' in outcome) {
|
|
@@ -102,9 +200,9 @@ export function AppPage({ app, workspace }) {
|
|
|
102
200
|
surface?.dataModel.set(path, '');
|
|
103
201
|
}
|
|
104
202
|
}
|
|
105
|
-
}, [app]);
|
|
203
|
+
}, [app, runPage]);
|
|
106
204
|
return (_jsx(Box, { "data-testid": "app-page", sx: { height: '100%', minHeight: 0, overflow: 'auto', p: 3 }, children: 'problem' in drawing ? (_jsx(Box, { role: "status", sx: { color: 'fg.muted', fontSize: 1 }, children: drawing.problem })) : (_jsx(InlineSurface
|
|
107
205
|
// A new list of blocks is a new surface, drawn with it.
|
|
108
|
-
, { messages: messages, data: data, onAction: onAction, validationError: refusal, catalog: drawing.catalog }, drawnWith)) }));
|
|
206
|
+
, { messages: messages, data: data, onAction: onAction, validationError: refusal, catalog: drawing.catalog, ...(page ? { onSurface } : {}) }, drawnWith)) }));
|
|
109
207
|
}
|
|
110
208
|
export default AppPage;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* @module loop/plugins/app-page/appPageModel
|
|
25
25
|
*/
|
|
26
|
-
import type { AppFormField, AppFormSchema, AppSpec } from '../../../types/agentspecs';
|
|
26
|
+
import type { AppFormField, AppFormSchema, AppPageSpec, AppSpec } from '../../../types/agentspecs';
|
|
27
27
|
import type { ChatTurnSnapshot, ConversationEntry } from '../../core';
|
|
28
28
|
/** The id of the editor surface an application's page is drawn in. */
|
|
29
29
|
export declare const APP_PAGE_SURFACE = "app-page";
|
|
@@ -92,6 +92,12 @@ export declare function settingDefaults(settings: AppFormSchema | undefined): Re
|
|
|
92
92
|
* shown and written. Empty lists for a decision, or a kind not known.
|
|
93
93
|
*/
|
|
94
94
|
export declare function appKindPaths(app: Pick<AppSpec, 'kind' | 'interface'>): AppKindPaths;
|
|
95
|
+
/** The page its code writes (P-05): a widget's, or none. */
|
|
96
|
+
export declare const pageOf: (app: Pick<AppSpec, "kind" | "interface">) => AppPageSpec | undefined;
|
|
97
|
+
/** The path an output of its page is shown at (P-05). */
|
|
98
|
+
export declare const outputPath: (name: string) => string;
|
|
99
|
+
/** The action that runs a widget's page written in its code (P-05). */
|
|
100
|
+
export declare const PAGE_ACTION = "page";
|
|
95
101
|
/**
|
|
96
102
|
* Whether the workspace draws the application's page: a chat, a widget or a
|
|
97
103
|
* worker whose layout has one (`page`, `split`). A `chat` layout is the
|
|
@@ -149,6 +155,12 @@ export declare function givenFiles(files: unknown): {
|
|
|
149
155
|
* them against the settings' form.
|
|
150
156
|
*/
|
|
151
157
|
export declare function settingsOf(app: Pick<AppSpec, 'interface'>, inputs: unknown): Record<string, unknown>;
|
|
158
|
+
/**
|
|
159
|
+
* The inputs of a widget's page written in its code (P-05), as the page
|
|
160
|
+
* holds them, typed as its form says, for the session's `page` action: the
|
|
161
|
+
* runtime checks them against the same form.
|
|
162
|
+
*/
|
|
163
|
+
export declare function pageInputsOf(app: Pick<AppSpec, 'kind' | 'interface'>, inputs: unknown): Record<string, unknown>;
|
|
152
164
|
/**
|
|
153
165
|
* What the page did besides its message, for the session API: the action of
|
|
154
166
|
* the block pressed, the files given with it, the settings it holds. Sent
|
|
@@ -182,6 +194,10 @@ export type AppPageOutcome = {
|
|
|
182
194
|
stop: true;
|
|
183
195
|
} | {
|
|
184
196
|
newChat: true;
|
|
197
|
+
}
|
|
198
|
+
/** A widget's page, to run on these inputs (P-05): no message is sent. */
|
|
199
|
+
| {
|
|
200
|
+
runPage: Record<string, unknown>;
|
|
185
201
|
} | {
|
|
186
202
|
refused: string;
|
|
187
203
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
+
import { pageOutputShows } from '../../apps/appspec';
|
|
5
6
|
/** The id of the editor surface an application's page is drawn in. */
|
|
6
7
|
export const APP_PAGE_SURFACE = 'app-page';
|
|
7
8
|
/** The id of the A2UI surface itself, inside it. */
|
|
@@ -178,12 +179,49 @@ export function appKindPaths(app) {
|
|
|
178
179
|
meaning: `The setting “${settingTitle(id, field)}”.`,
|
|
179
180
|
words: settingTitle(id, field),
|
|
180
181
|
}));
|
|
182
|
+
// A widget's page written in its code (P-05): its inputs taken, its
|
|
183
|
+
// outputs published, and the action that runs it.
|
|
184
|
+
const page = pageOf(app);
|
|
185
|
+
if (!page) {
|
|
186
|
+
return {
|
|
187
|
+
...kind,
|
|
188
|
+
publishes: [...kind.publishes, ...inputs],
|
|
189
|
+
accepts: [...kind.accepts, ...inputs],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const pageInputs = settingFields(page.inputs).map(([id, field]) => ({
|
|
193
|
+
path: inputPath(id),
|
|
194
|
+
meaning: `The input “${settingTitle(id, field)}” of its page.`,
|
|
195
|
+
words: settingTitle(id, field),
|
|
196
|
+
}));
|
|
197
|
+
const outputs = page.outputs.map(output => ({
|
|
198
|
+
path: outputPath(output.name),
|
|
199
|
+
meaning: `What its page shows as “${output.title || output.name}”, drawn with a ${output.component}, for its inputs.`,
|
|
200
|
+
words: output.title || output.name,
|
|
201
|
+
...(output.component === 'Table' || output.component === 'Chart'
|
|
202
|
+
? { list: true }
|
|
203
|
+
: {}),
|
|
204
|
+
}));
|
|
181
205
|
return {
|
|
182
206
|
...kind,
|
|
183
|
-
publishes: [...kind.publishes, ...inputs],
|
|
184
|
-
accepts: [...kind.accepts, ...inputs],
|
|
207
|
+
publishes: [...kind.publishes, ...inputs, ...pageInputs, ...outputs],
|
|
208
|
+
accepts: [...kind.accepts, ...inputs, ...pageInputs],
|
|
209
|
+
actions: [...kind.actions, PAGE],
|
|
185
210
|
};
|
|
186
211
|
}
|
|
212
|
+
/** The page its code writes (P-05): a widget's, or none. */
|
|
213
|
+
export const pageOf = (app) => app.kind === 'widget' ? app.interface.page : undefined;
|
|
214
|
+
/** The path an output of its page is shown at (P-05). */
|
|
215
|
+
export const outputPath = (name) => `/outputs/${name}`;
|
|
216
|
+
/** The action that runs a widget's page written in its code (P-05). */
|
|
217
|
+
export const PAGE_ACTION = 'page';
|
|
218
|
+
const PAGE = {
|
|
219
|
+
name: PAGE_ACTION,
|
|
220
|
+
meaning: 'Run its page on its inputs: every value written under /inputs, its outputs shown in place at /outputs.',
|
|
221
|
+
words: 'Run its page',
|
|
222
|
+
context: [],
|
|
223
|
+
asks: {},
|
|
224
|
+
};
|
|
187
225
|
/** The components its surface has, or none. */
|
|
188
226
|
const surfaceComponents = (app) => app.interface.surface?.components ?? [];
|
|
189
227
|
/**
|
|
@@ -257,7 +295,10 @@ export function appPageInitialData(app) {
|
|
|
257
295
|
// Each setting at its default, as the settings' form holds it; a
|
|
258
296
|
// ChoicePicker a builder placed at a setting holds a list of the values
|
|
259
297
|
// chosen.
|
|
260
|
-
const inputs =
|
|
298
|
+
const inputs = {
|
|
299
|
+
...settingDefaults(app.interface.settings),
|
|
300
|
+
...settingDefaults(pageOf(app)?.inputs),
|
|
301
|
+
};
|
|
261
302
|
for (const node of app.interface.surface?.components ?? []) {
|
|
262
303
|
const bound = node.value?.path;
|
|
263
304
|
const id = typeof bound === 'string' && bound.startsWith('/inputs/')
|
|
@@ -269,6 +310,9 @@ export function appPageInitialData(app) {
|
|
|
269
310
|
}
|
|
270
311
|
tree.inputs = inputs;
|
|
271
312
|
}
|
|
313
|
+
if (pageOf(app)) {
|
|
314
|
+
tree.outputs = {};
|
|
315
|
+
}
|
|
272
316
|
if (paths.accepts.some(entry => entry.path === DRAFT.path)) {
|
|
273
317
|
tree.draft = '';
|
|
274
318
|
}
|
|
@@ -362,11 +406,23 @@ export function givenFiles(files) {
|
|
|
362
406
|
* them against the settings' form.
|
|
363
407
|
*/
|
|
364
408
|
export function settingsOf(app, inputs) {
|
|
409
|
+
return formValuesOf(app.interface.settings, inputs);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* The inputs of a widget's page written in its code (P-05), as the page
|
|
413
|
+
* holds them, typed as its form says, for the session's `page` action: the
|
|
414
|
+
* runtime checks them against the same form.
|
|
415
|
+
*/
|
|
416
|
+
export function pageInputsOf(app, inputs) {
|
|
417
|
+
return formValuesOf(pageOf(app)?.inputs, inputs);
|
|
418
|
+
}
|
|
419
|
+
/** A form's values as the page holds them, each typed as its field says. */
|
|
420
|
+
function formValuesOf(form, inputs) {
|
|
365
421
|
const values = inputs && typeof inputs === 'object'
|
|
366
422
|
? inputs
|
|
367
423
|
: {};
|
|
368
424
|
const settings = {};
|
|
369
|
-
for (const [id, field] of settingFields(
|
|
425
|
+
for (const [id, field] of settingFields(form)) {
|
|
370
426
|
const held = values[id];
|
|
371
427
|
if (held === undefined || held === null) {
|
|
372
428
|
continue;
|
|
@@ -425,6 +481,8 @@ export function appPageAction(app, event, read, lastInputs = '') {
|
|
|
425
481
|
...(settings ? { settings } : {}),
|
|
426
482
|
});
|
|
427
483
|
switch (event.name) {
|
|
484
|
+
case PAGE_ACTION:
|
|
485
|
+
return { runPage: pageInputsOf(app, read('/inputs')) };
|
|
428
486
|
case 'stop':
|
|
429
487
|
return { stop: true };
|
|
430
488
|
case 'new':
|
|
@@ -486,6 +544,10 @@ const button = (id, label, name, primary = false) => [
|
|
|
486
544
|
* filled — no action, no button: they go with the next run.
|
|
487
545
|
*/
|
|
488
546
|
function settingsForm(app) {
|
|
547
|
+
const page = pageOf(app);
|
|
548
|
+
if (page) {
|
|
549
|
+
return pageForm(app, page);
|
|
550
|
+
}
|
|
489
551
|
return app.interface.settings && settingFields(app.interface.settings).length
|
|
490
552
|
? [
|
|
491
553
|
{
|
|
@@ -499,6 +561,55 @@ function settingsForm(app) {
|
|
|
499
561
|
]
|
|
500
562
|
: [];
|
|
501
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* A widget's page written in its code (P-05): one Form of its settings and
|
|
566
|
+
* its inputs, written at `/inputs` as they are filled — the page runs its
|
|
567
|
+
* code on them as they change — then each output, under its title, drawn
|
|
568
|
+
* with its component from `/outputs/<name>`; a Run button when it is not
|
|
569
|
+
* live.
|
|
570
|
+
*/
|
|
571
|
+
function pageForm(app, page) {
|
|
572
|
+
const settings = app.interface.settings;
|
|
573
|
+
const schema = {
|
|
574
|
+
...page.inputs,
|
|
575
|
+
type: 'object',
|
|
576
|
+
properties: { ...settings?.properties, ...page.inputs.properties },
|
|
577
|
+
required: [...(settings?.required ?? []), ...(page.inputs.required ?? [])],
|
|
578
|
+
};
|
|
579
|
+
const ui = { ...app.interface.settingsUi, ...page.inputsUi };
|
|
580
|
+
const outputs = page.outputs.flatMap(output => [
|
|
581
|
+
...(output.title
|
|
582
|
+
? [
|
|
583
|
+
{
|
|
584
|
+
id: `output-${output.name}-title`,
|
|
585
|
+
component: 'Text',
|
|
586
|
+
text: output.title,
|
|
587
|
+
variant: 'h4',
|
|
588
|
+
},
|
|
589
|
+
]
|
|
590
|
+
: []),
|
|
591
|
+
{
|
|
592
|
+
...output.props,
|
|
593
|
+
id: `output-${output.name}`,
|
|
594
|
+
component: output.component,
|
|
595
|
+
[pageOutputShows(output.component, app.interface.customComponents) ??
|
|
596
|
+
'text']: {
|
|
597
|
+
path: outputPath(output.name),
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
]);
|
|
601
|
+
return [
|
|
602
|
+
{
|
|
603
|
+
id: 'inputs',
|
|
604
|
+
component: 'Form',
|
|
605
|
+
schema,
|
|
606
|
+
...(Object.keys(ui).length > 0 ? { ui } : {}),
|
|
607
|
+
values: { path: '/inputs' },
|
|
608
|
+
},
|
|
609
|
+
...(page.live ? [] : button('run-page', 'Run', PAGE_ACTION, true)),
|
|
610
|
+
...outputs,
|
|
611
|
+
];
|
|
612
|
+
}
|
|
502
613
|
/**
|
|
503
614
|
* The page drawn when the application has a page layout and no surface
|
|
504
615
|
* composed: what its kind publishes, its settings as inputs, and the
|
|
@@ -512,6 +623,11 @@ export function defaultAppSurface(app) {
|
|
|
512
623
|
body.push(text('question', '/question', 'h3'), text('answer', '/answer'), text('status', '/status', 'caption'), ...inputs);
|
|
513
624
|
break;
|
|
514
625
|
case 'widget':
|
|
626
|
+
if (pageOf(app)) {
|
|
627
|
+
// Its page is its code's (P-05): the inputs, and what it shows for them.
|
|
628
|
+
body.push(...inputs);
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
515
631
|
body.push(...inputs, { id: 'actions', component: 'Row', children: ['run', 'stop'] }, ...button('run', 'Run', 'run', true), ...button('stop', 'Stop', 'stop'), text('status', '/status', 'caption'), text('output', '/output'));
|
|
516
632
|
break;
|
|
517
633
|
case 'worker':
|
|
@@ -41,4 +41,5 @@ export type AppHostPageProps = {
|
|
|
41
41
|
export declare function defineAppHostPagePlugin(app: AppSpec, Page: ComponentType<AppHostPageProps>): ReactorPlugin<Record<string, never>, unknown, unknown>;
|
|
42
42
|
export { AppPage, type AppPageProps } from './AppPage';
|
|
43
43
|
export * from './appPageModel';
|
|
44
|
+
export * from './pageRun';
|
|
44
45
|
export default defineAppPagePlugin;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A widget's page written in its code, run on its inputs (LOOP P-05).
|
|
3
|
+
*
|
|
4
|
+
* The page opens a session of the application's agent on its runtime
|
|
5
|
+
* (`POST /api/v1/apps/sessions`), then, each time an input changes, sends
|
|
6
|
+
* them all as the session's `page` action (`POST …/sessions/{uid}/actions`,
|
|
7
|
+
* `{name: "page", payload: {inputs}}`). The runtime checks them against the
|
|
8
|
+
* page's form — refused with 422 and a sentence — runs its code's
|
|
9
|
+
* `@app.page` on them, and answers its outputs as a `CUSTOM` event named
|
|
10
|
+
* `loop.page`, `{inputs, outputs}`, which the page shows in place. Nothing is
|
|
11
|
+
* said in the conversation.
|
|
12
|
+
*
|
|
13
|
+
* @module loop/plugins/app-page/pageRun
|
|
14
|
+
*/
|
|
15
|
+
/** The `CUSTOM` event that says what a page shows. */
|
|
16
|
+
export declare const LOOP_PAGE = "loop.page";
|
|
17
|
+
/** Where the page reaches the application's session API, and as whom. */
|
|
18
|
+
export type PageRunContext = {
|
|
19
|
+
/** The agent-runtimes server that runs the application. */
|
|
20
|
+
serverUrl: string;
|
|
21
|
+
/** The application's agent there. */
|
|
22
|
+
agentId: string;
|
|
23
|
+
/** The person's token; none on the machine itself. */
|
|
24
|
+
token?: string;
|
|
25
|
+
fetcher?: typeof fetch;
|
|
26
|
+
};
|
|
27
|
+
/** What a turn of the session API said: the events of its stream, parsed. */
|
|
28
|
+
export declare function streamEvents(stream: string): Array<Record<string, unknown>>;
|
|
29
|
+
/** What a run of the page answered: its outputs, by name, or why there are none. */
|
|
30
|
+
export type PageTurn = {
|
|
31
|
+
outputs: Record<string, unknown>;
|
|
32
|
+
inputs: Record<string, unknown>;
|
|
33
|
+
} | {
|
|
34
|
+
refused: string;
|
|
35
|
+
};
|
|
36
|
+
/** The page's turn, read from the stream the runtime answered. */
|
|
37
|
+
export declare function pageTurnOf(stream: string): PageTurn;
|
|
38
|
+
/** Open the session its page runs in; its uid, as the runtime says it. */
|
|
39
|
+
export declare function openPageSession(context: PageRunContext): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Run the page on its inputs in a session: its outputs, or the runtime's
|
|
42
|
+
* sentence — its form refusing an input, its code failing.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runPageIn(context: PageRunContext, uid: string, inputs: Record<string, unknown>): Promise<PageTurn>;
|
|
45
|
+
/** Each output as the page's data holds it, by path. */
|
|
46
|
+
export declare function outputsData(outputs: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A widget's page written in its code, run on its inputs (LOOP P-05).
|
|
7
|
+
*
|
|
8
|
+
* The page opens a session of the application's agent on its runtime
|
|
9
|
+
* (`POST /api/v1/apps/sessions`), then, each time an input changes, sends
|
|
10
|
+
* them all as the session's `page` action (`POST …/sessions/{uid}/actions`,
|
|
11
|
+
* `{name: "page", payload: {inputs}}`). The runtime checks them against the
|
|
12
|
+
* page's form — refused with 422 and a sentence — runs its code's
|
|
13
|
+
* `@app.page` on them, and answers its outputs as a `CUSTOM` event named
|
|
14
|
+
* `loop.page`, `{inputs, outputs}`, which the page shows in place. Nothing is
|
|
15
|
+
* said in the conversation.
|
|
16
|
+
*
|
|
17
|
+
* @module loop/plugins/app-page/pageRun
|
|
18
|
+
*/
|
|
19
|
+
/** The `CUSTOM` event that says what a page shows. */
|
|
20
|
+
export const LOOP_PAGE = 'loop.page';
|
|
21
|
+
/** What a turn of the session API said: the events of its stream, parsed. */
|
|
22
|
+
export function streamEvents(stream) {
|
|
23
|
+
const events = [];
|
|
24
|
+
for (const block of stream.split('\n\n')) {
|
|
25
|
+
const data = block
|
|
26
|
+
.split('\n')
|
|
27
|
+
.filter(line => line.startsWith('data:'))
|
|
28
|
+
.map(line => line.slice('data:'.length).trim())
|
|
29
|
+
.join('');
|
|
30
|
+
if (!data)
|
|
31
|
+
continue;
|
|
32
|
+
try {
|
|
33
|
+
events.push(JSON.parse(data));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Not an event: skipped, as a reader of the stream skips it.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return events;
|
|
40
|
+
}
|
|
41
|
+
/** The page's turn, read from the stream the runtime answered. */
|
|
42
|
+
export function pageTurnOf(stream) {
|
|
43
|
+
let shown = null;
|
|
44
|
+
for (const event of streamEvents(stream)) {
|
|
45
|
+
if (event.type === 'RUN_ERROR') {
|
|
46
|
+
return { refused: String(event.message ?? 'Its page could not run.') };
|
|
47
|
+
}
|
|
48
|
+
if (event.type === 'CUSTOM' && event.name === LOOP_PAGE) {
|
|
49
|
+
const value = (event.value ?? {});
|
|
50
|
+
shown = { outputs: value.outputs ?? {}, inputs: value.inputs ?? {} };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (shown ?? {
|
|
54
|
+
refused: 'Its page showed nothing: its code answered no outputs.',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const base = (context) => `${context.serverUrl.replace(/\/+$/, '')}/api/v1/apps/sessions`;
|
|
58
|
+
async function posted(context, url, body) {
|
|
59
|
+
const response = await (context.fetcher ?? fetch)(url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'content-type': 'application/json',
|
|
63
|
+
...(context.token ? { Authorization: `Bearer ${context.token}` } : {}),
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
let detail = '';
|
|
69
|
+
try {
|
|
70
|
+
const said = (await response.json());
|
|
71
|
+
detail = typeof said.detail === 'string' ? said.detail : '';
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// No sentence: the status says it.
|
|
75
|
+
}
|
|
76
|
+
throw new Error(detail || `Its page was refused (${response.status}).`);
|
|
77
|
+
}
|
|
78
|
+
return response.text();
|
|
79
|
+
}
|
|
80
|
+
/** Open the session its page runs in; its uid, as the runtime says it. */
|
|
81
|
+
export async function openPageSession(context) {
|
|
82
|
+
const stream = await posted(context, base(context), {
|
|
83
|
+
agent: context.agentId,
|
|
84
|
+
});
|
|
85
|
+
for (const event of streamEvents(stream)) {
|
|
86
|
+
if (event.type === 'CUSTOM' && event.name === 'loop.session') {
|
|
87
|
+
const uid = event.value?.uid;
|
|
88
|
+
if (typeof uid === 'string' && uid) {
|
|
89
|
+
return uid;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw new Error('The runtime opened no session for its page.');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Run the page on its inputs in a session: its outputs, or the runtime's
|
|
97
|
+
* sentence — its form refusing an input, its code failing.
|
|
98
|
+
*/
|
|
99
|
+
export async function runPageIn(context, uid, inputs) {
|
|
100
|
+
try {
|
|
101
|
+
const stream = await posted(context, `${base(context)}/${encodeURIComponent(uid)}/actions`, { name: 'page', payload: { inputs } });
|
|
102
|
+
return pageTurnOf(stream);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return { refused: error instanceof Error ? error.message : String(error) };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Each output as the page's data holds it, by path. */
|
|
109
|
+
export function outputsData(outputs) {
|
|
110
|
+
return Object.fromEntries(Object.entries(outputs).map(([name, value]) => [`/outputs/${name}`, value]));
|
|
111
|
+
}
|
|
@@ -9,7 +9,7 @@ import { pageLayoutPanelOpen, togglePagePanel, } from '@datalayer/primer-addons/
|
|
|
9
9
|
import { AssistantStage } from '../../../chat/assistant/AssistantStage';
|
|
10
10
|
import { decisionsAskerAt } from '../../../chat/assistant/decisions';
|
|
11
11
|
import { peekLine } from '../../../chat/assistant/ConversationBalloon';
|
|
12
|
-
import {
|
|
12
|
+
import { assistantStateOf, keepAway, keptAway, latestSaying, } from '../../../chat/assistant/state';
|
|
13
13
|
import { useApproveToolRequest, useRejectToolRequest, useToolApprovalsQuery, } from '../../../hooks/useToolApprovals';
|
|
14
14
|
import { approvalsOfApp, ruleOfApproval } from '../app-rules/AppRulesCard';
|
|
15
15
|
import { useViewportDrag } from '../../../chat/useViewportDrag';
|
|
@@ -17,6 +17,7 @@ import { LoopAssistantCharacter, LoopAssistantMenu, LoopChatLayout, LoopChatTurn
|
|
|
17
17
|
import { assistantCharacterFor } from '../assistant-characters';
|
|
18
18
|
import { useOptionalSandboxService } from '../agents/useSandboxService';
|
|
19
19
|
import { TARGET_SPECS } from '../agents/switchable';
|
|
20
|
+
import { useChatWords } from '../../../chat/ChatLanguage';
|
|
20
21
|
export const LOOP_ASSISTANT_PLUGIN_NAME = '@datalayer/loop-plugin-assistant';
|
|
21
22
|
/** The character's size, in pixels, as the chat's own assistant. */
|
|
22
23
|
const SIZE = 88;
|
|
@@ -72,6 +73,7 @@ function focusComposer() {
|
|
|
72
73
|
composer?.focus();
|
|
73
74
|
}
|
|
74
75
|
export function LoopAssistant({ app, person, appId, decisions, balloon: displayGiven = 'history', workspace, }) {
|
|
76
|
+
const chatText = useChatWords();
|
|
75
77
|
// The balloon's display, as its menu chose it; the Appspec's until then.
|
|
76
78
|
const [chosenDisplay, setChosenDisplay] = useState();
|
|
77
79
|
const display = chosenDisplay ?? displayGiven;
|
|
@@ -203,18 +205,18 @@ export function LoopAssistant({ app, person, appId, decisions, balloon: displayG
|
|
|
203
205
|
const atWork = turn.status === 'streaming' || turn.status === 'thinking';
|
|
204
206
|
const current = display === 'current';
|
|
205
207
|
const balloon = state === 'paused'
|
|
206
|
-
? { text:
|
|
208
|
+
? { text: chatText.paused }
|
|
207
209
|
: approval
|
|
208
|
-
? { text:
|
|
210
|
+
? { text: chatText.waitingForYou, approval }
|
|
209
211
|
: presence === 'waiting'
|
|
210
|
-
? { text:
|
|
212
|
+
? { text: chatText.waitingOpen }
|
|
211
213
|
: tool
|
|
212
214
|
? { text: turn.activity ?? '', tool, busy: true }
|
|
213
215
|
: current && (atWork || unheard)
|
|
214
216
|
? {
|
|
215
217
|
text: turn.status === 'thinking'
|
|
216
|
-
?
|
|
217
|
-
: (saying?.text ??
|
|
218
|
+
? chatText.thinking
|
|
219
|
+
: (saying?.text ?? chatText.thinking),
|
|
218
220
|
more: saying?.more,
|
|
219
221
|
speaking: turn.status === 'streaming',
|
|
220
222
|
busy: atWork,
|
|
@@ -49,6 +49,7 @@ import { NONE_EDITOR, seedEditorChoice, setEditorOptions, } from '../shell/edito
|
|
|
49
49
|
import { FaceDrawing, PresenceFace, PresenceLine, } from '../../../chat/presence/Presence';
|
|
50
50
|
import { presenceState, presenceToolOf, } from '../../../chat/presence/presenceStatus';
|
|
51
51
|
import { latestSaying, newestIsAnswer, } from '../../../chat/assistant/state';
|
|
52
|
+
import { useChatWords } from '../../../chat/ChatLanguage';
|
|
52
53
|
/** No editor: the conversation on its own. */
|
|
53
54
|
const NO_SURFACE = '';
|
|
54
55
|
/**
|
|
@@ -64,6 +65,7 @@ const FULLSCREEN_HINT_PERIOD_MS = 1400;
|
|
|
64
65
|
*/
|
|
65
66
|
const FACE_LARGE = parseInt(loopShapeVars['--loop-face-large'], 10);
|
|
66
67
|
export default function ChatView({ workspace }) {
|
|
68
|
+
const chatText = useChatWords();
|
|
67
69
|
/* The active theme's own colours. Read from the store rather than a
|
|
68
70
|
provider, so a workspace mounted without the addons theme still gets the
|
|
69
71
|
default palette instead of throwing. */
|
|
@@ -76,7 +78,7 @@ export default function ChatView({ workspace }) {
|
|
|
76
78
|
// where it sets anything else about this plugin.
|
|
77
79
|
const reactor = useReactorPlatform();
|
|
78
80
|
const config = reactor.getConfig(CHAT_PLUGIN_NAME);
|
|
79
|
-
const placeholder = config?.placeholder
|
|
81
|
+
const placeholder = config?.placeholder || chatText.placeholder;
|
|
80
82
|
const defaultSurface = config?.defaultSurface ?? 'notebook';
|
|
81
83
|
/*
|
|
82
84
|
* The input-prompt plugin's own configuration, read from here rather than
|
|
@@ -1040,8 +1042,8 @@ export default function ChatView({ workspace }) {
|
|
|
1040
1042
|
const chatDisabled = gateBlocked || keyExpired || Boolean(inPageRefusal) || ambient.disabled;
|
|
1041
1043
|
const disabledReason = keyExpired
|
|
1042
1044
|
? expiredKeyIsTemporary
|
|
1043
|
-
?
|
|
1044
|
-
:
|
|
1045
|
+
? chatText.demoKeyExpired
|
|
1046
|
+
: chatText.keyExpired
|
|
1045
1047
|
: (inPageRefusal ??
|
|
1046
1048
|
gateReason ??
|
|
1047
1049
|
(ambient.disabled ? ambient.disableReason : undefined));
|
|
@@ -1773,7 +1775,7 @@ export default function ChatView({ workspace }) {
|
|
|
1773
1775
|
renderToolResult: chatExtras.renderToolResult, showTurnFooter: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true, onContextSnapshot: handleContextSnapshot, onLoadingChange: handleLoadingChange, onItemsChange: handleMessagesChange, onDisplayItemsChange: handleDisplayItemsChange, enableStreaming: true }) }), besideChat && !promptHidden ? prompt : null] }) }));
|
|
1774
1776
|
// Off when the host asked for the menu alone: the composer's suggestions
|
|
1775
1777
|
// control carries the same openers (`suggestions` above).
|
|
1776
|
-
const chipsEl = config?.suggestionLabels !== false && chatSuggestions.length > 0 ? (_jsx(Box, { "aria-label":
|
|
1778
|
+
const chipsEl = config?.suggestionLabels !== false && chatSuggestions.length > 0 ? (_jsx(Box, { "aria-label": chatText.suggestedPrompts, sx: { display: 'flex', flexWrap: 'wrap', gap: 2, px: 2, pb: '6px' }, children: chatSuggestions.map(item => (_jsx(Box, { as: "button", type: "button",
|
|
1777
1779
|
// The whole request, where the chip shows only its summary.
|
|
1778
1780
|
title: item.message, disabled: busy || chatDisabled, onClick: () => void handleSend(item.message), sx: {
|
|
1779
1781
|
appearance: 'none',
|