@datalayer/agent-runtimes 1.3.59 → 1.3.61
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 +11 -1
- package/lib/chat/ChatFloating.js +142 -23
- package/lib/chat/assistant/AssistantStage.d.ts +13 -5
- package/lib/chat/assistant/AssistantStage.js +5 -3
- package/lib/chat/assistant/ConversationBalloon.d.ts +71 -0
- package/lib/chat/assistant/ConversationBalloon.js +102 -0
- package/lib/chat/assistant/DecisionAsk.d.ts +31 -0
- package/lib/chat/assistant/DecisionAsk.js +96 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +17 -4
- package/lib/chat/assistant/SpeechBalloon.js +20 -14
- package/lib/chat/assistant/decisions.d.ts +104 -0
- package/lib/chat/assistant/decisions.js +161 -0
- package/lib/chat/assistant/state.d.ts +1 -1
- package/lib/chat/assistant/state.js +1 -1
- package/lib/chat/base/ChatBase.js +22 -4
- package/lib/chat/marks/toolMarks.js +2 -2
- package/lib/config/AgentConfiguration.d.ts +2 -1
- package/lib/config/AgentConfiguration.js +2 -2
- package/lib/examples/AgentDecideExample.d.ts +24 -0
- package/lib/examples/AgentDecideExample.js +73 -0
- package/lib/examples/AgentNotificationsExample.js +1 -1
- package/lib/examples/AgentSkillsExample.js +1 -1
- package/lib/examples/AgentspecsExample.js +3 -3
- package/lib/examples/ChatAssistantExample.js +12 -1
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +3 -0
- package/lib/loop/apps/appspec.js +4 -4
- package/lib/loop/apps/checks.js +6 -6
- package/lib/loop/apps/rules.js +2 -2
- package/lib/loop/plugins/assistant/index.d.ts +14 -2
- package/lib/loop/plugins/assistant/index.js +22 -5
- package/lib/specs/actions.d.ts +1 -1
- package/lib/specs/actions.js +1 -1
- package/lib/specs/agents/agents.js +197 -192
- package/lib/specs/apps.js +13 -13
- package/lib/specs/appspecSchema.js +3 -3
- package/lib/specs/backendTools.d.ts +31 -0
- package/lib/specs/{tools.js → backendTools.js} +48 -48
- package/lib/specs/cogs.js +8 -7
- package/lib/specs/frames.js +5 -5
- package/lib/specs/index.d.ts +1 -1
- package/lib/specs/index.js +1 -1
- package/lib/stores/agentRuntimeStore.js +2 -2
- package/lib/stories/loop/ReferenceScreens.d.ts +6 -2
- package/lib/stories/loop/ReferenceScreens.js +63 -6
- package/lib/types/agentspecs.d.ts +7 -5
- package/lib/types/chat.d.ts +12 -0
- package/lib/types/config.d.ts +2 -2
- package/lib/types/tools.d.ts +3 -3
- package/package.json +1 -1
- package/scripts/codegen/generate_agents.py +23 -12
- package/scripts/codegen/generate_apps.py +3 -2
- package/scripts/codegen/{generate_tools.py → generate_backend_tools.py} +29 -29
- package/scripts/codegen/generate_cogs.py +2 -2
- package/scripts/codegen/generate_frames.py +2 -2
- package/lib/specs/tools.d.ts +0 -31
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Decide: typed decisions asked of Jev (`cloudflare:wrk/typesafe/jev`), the
|
|
8
|
+
* typed-decision model, from the chat and from the floating assistant.
|
|
9
|
+
*
|
|
10
|
+
* The *Decide* application of the catalogue (agentspecs `apps/decide.yaml`)
|
|
11
|
+
* in the LOOP workspace, on the Local target: its agent — `example-simple`
|
|
12
|
+
* with the `decide` tool — runs on the local agent-runtimes server, and
|
|
13
|
+
* answers a question about a text by asking a typed decision. The floating
|
|
14
|
+
* assistant beside it offers *Ask a decision* in its balloon: the situation,
|
|
15
|
+
* the question and its type, asked through the same server's
|
|
16
|
+
* `/api/v1/configure/inference/decisions`, the route that asks as `decide`
|
|
17
|
+
* does, with the same token.
|
|
18
|
+
*
|
|
19
|
+
* The server needs ai-inference: start it with
|
|
20
|
+
* `AGENT_RUNTIMES_INFERENCE_PROVIDER_OVERRIDE=datalayer`,
|
|
21
|
+
* `DATALAYER_AI_INFERENCE_URL=https://r1.datalayer.run` and
|
|
22
|
+
* `DATALAYER_AI_INFERENCE_API_KEY` set from `DATALAYER_API_KEY`. Another
|
|
23
|
+
* server than `http://localhost:8765` is named with `?agentRuntimesUrl=`.
|
|
24
|
+
*
|
|
25
|
+
* @module examples/AgentDecideExample
|
|
26
|
+
*/
|
|
27
|
+
import { useMemo } from 'react';
|
|
28
|
+
import { configurePlugin } from '@datalayer/reactor';
|
|
29
|
+
import { Heading, Text } from '@primer/react';
|
|
30
|
+
import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
|
|
31
|
+
import { ThemedProvider } from './utils/themedProvider';
|
|
32
|
+
import { resolveExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
|
|
33
|
+
import { AppRenderer, appDatalayerCreatePayload, } from '../loop/apps/AppRenderer';
|
|
34
|
+
import { AssistantCharactersPlugin } from '../loop/plugins/assistant-characters';
|
|
35
|
+
import { LoopAssistantPlugin } from '../loop/plugins/assistant';
|
|
36
|
+
import { APP_CATALOGUE } from '../specs/apps';
|
|
37
|
+
setupPrimerPortals();
|
|
38
|
+
/** The local server: `?agentRuntimesUrl=` when given, else the examples' own. */
|
|
39
|
+
function serverUrlOf() {
|
|
40
|
+
const named = typeof window === 'undefined'
|
|
41
|
+
? null
|
|
42
|
+
: new URLSearchParams(window.location.search).get('agentRuntimesUrl');
|
|
43
|
+
return resolveExampleAgentRuntimesUrl('local', named ?? undefined);
|
|
44
|
+
}
|
|
45
|
+
const AgentDecideExample = () => {
|
|
46
|
+
const app = APP_CATALOGUE['decide'];
|
|
47
|
+
const serverUrl = useMemo(serverUrlOf, []);
|
|
48
|
+
// Made once: a new plugin on a render restarts the workspace.
|
|
49
|
+
const plugins = useMemo(() => [
|
|
50
|
+
AssistantCharactersPlugin,
|
|
51
|
+
configurePlugin(LoopAssistantPlugin, {
|
|
52
|
+
app: app.interface.assistant ?? undefined,
|
|
53
|
+
decisions: { serverUrl },
|
|
54
|
+
}),
|
|
55
|
+
], [app, serverUrl]);
|
|
56
|
+
// The Local agent is the application's: created under its id with its
|
|
57
|
+
// spec, so that the server runs its instructions, its tool and its rule.
|
|
58
|
+
const localAgent = useMemo(() => ({ createPayload: appDatalayerCreatePayload(app) }), [app]);
|
|
59
|
+
return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: {
|
|
60
|
+
height: '100vh',
|
|
61
|
+
minHeight: 0,
|
|
62
|
+
display: 'flex',
|
|
63
|
+
bg: 'canvas.default',
|
|
64
|
+
}, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0, minHeight: 0 }, children: _jsx(AppRenderer, { app: app, serverUrl: serverUrl, target: "local", localAgent: localAgent, plugins: plugins }) }), _jsxs(Box, { as: "aside", sx: {
|
|
65
|
+
width: 320,
|
|
66
|
+
flexShrink: 0,
|
|
67
|
+
p: 3,
|
|
68
|
+
borderLeft: '1px solid',
|
|
69
|
+
borderColor: 'border.default',
|
|
70
|
+
overflowY: 'auto',
|
|
71
|
+
}, children: [_jsxs(Heading, { as: "h2", sx: { fontSize: 2, mb: 2 }, children: [app.emoji, " Decide"] }), _jsx(Text, { as: "p", sx: { fontSize: 1, mb: 2 }, children: "Typed decisions asked of Jev, the typed-decision model: yes or no with its probability, one of named options, or a score, each with its confidence." }), _jsxs(Text, { as: "p", sx: { fontSize: 1, mb: 2 }, children: [_jsx("strong", { children: "In the chat" }), ": ask \u201CIs this ticket urgent? \u2018Help! My payouts have been failing for 3 days.\u2019\u201D Its agent calls the ", _jsx("code", { children: "decide" }), " tool, a read it does without asking."] }), _jsxs(Text, { as: "p", sx: { fontSize: 1, mb: 2 }, children: [_jsx("strong", { children: "From the assistant" }), ": hover the character below and choose ", _jsx("em", { children: "Ask a decision" }), " \u2014 the situation, the question, and its type. The answer comes back in its balloon."] }), _jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted' }, children: ["Runs on the local agent-runtimes server at ", serverUrl, ", which asks Jev through ai-inference."] })] })] }) }));
|
|
72
|
+
};
|
|
73
|
+
export default AgentDecideExample;
|
|
@@ -602,7 +602,7 @@ githubClientId, kaggleToken, }) => {
|
|
|
602
602
|
setGoal(spec.goal || '');
|
|
603
603
|
setSystemPrompt(spec.systemPrompt || spec.goal || DEFAULT_SYSTEM_PROMPT);
|
|
604
604
|
setSystemPromptCodemodeAddons(spec.systemPromptCodemodeAddons || '');
|
|
605
|
-
setTools(spec.
|
|
605
|
+
setTools(spec.backendTools || []);
|
|
606
606
|
setSandboxVariant(spec.sandboxVariant || '');
|
|
607
607
|
if (spec.model) {
|
|
608
608
|
setModel(spec.model);
|
|
@@ -650,7 +650,7 @@ githubClientId, kaggleToken, }) => {
|
|
|
650
650
|
protocol: normalizedProtocol,
|
|
651
651
|
systemPrompt: systemPrompt || undefined,
|
|
652
652
|
systemPromptCodemodeAddons: systemPromptCodemodeAddons || undefined,
|
|
653
|
-
tools,
|
|
653
|
+
backendTools: tools,
|
|
654
654
|
sandboxVariant: sandboxVariant || undefined,
|
|
655
655
|
skills: selectedSkills,
|
|
656
656
|
mcpServers: selectedMcpServers.map(server => ({
|
|
@@ -786,7 +786,7 @@ githubClientId, kaggleToken, }) => {
|
|
|
786
786
|
model: model,
|
|
787
787
|
system_prompt: systemPrompt || DEFAULT_SYSTEM_PROMPT,
|
|
788
788
|
system_prompt_codemode_addons: systemPromptCodemodeAddons || undefined,
|
|
789
|
-
|
|
789
|
+
backend_tools: tools,
|
|
790
790
|
sandbox_variant: sandboxVariant || undefined,
|
|
791
791
|
enable_skills: enableSkills,
|
|
792
792
|
enable_codemode: enableCodemode,
|
|
@@ -21,6 +21,15 @@ import { ChatFloating } from '../chat';
|
|
|
21
21
|
import { AssistantCharactersPlugin, assistantCharacterNamed, assistantCharactersOf, } from '../loop/plugins/assistant-characters';
|
|
22
22
|
import { OwlCharacterPlugin } from './utils/owlCharacterPlugin';
|
|
23
23
|
import { readAcsCharacter, readClippyCharacter, } from '../chat/assistant/formats';
|
|
24
|
+
/**
|
|
25
|
+
* The agent-runtimes server the assistant talks to: `?agentRuntimesUrl=`
|
|
26
|
+
* when given, else this machine's on port 8765. It serves the `assistant`
|
|
27
|
+
* agent, and asks Jev the decisions asked from the balloon.
|
|
28
|
+
*/
|
|
29
|
+
const SERVER = ((typeof window === 'undefined'
|
|
30
|
+
? null
|
|
31
|
+
: new URLSearchParams(window.location.search).get('agentRuntimesUrl')) ||
|
|
32
|
+
'http://127.0.0.1:8765').replace(/\/+$/, '');
|
|
24
33
|
/** The plugins this page enables: Datalayer's characters and the owl's. */
|
|
25
34
|
const reactor = buildReactorFromPlugins([
|
|
26
35
|
AssistantCharactersPlugin,
|
|
@@ -75,6 +84,8 @@ const ChatAssistantExample = () => {
|
|
|
75
84
|
catch (error) {
|
|
76
85
|
setLoadError(error instanceof Error ? error.message : String(error));
|
|
77
86
|
}
|
|
78
|
-
} }), loadError && (_jsx(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mt: 2 }, children: loadError })), typeof character !== 'string' && (_jsxs(Text, { as: "p", sx: { mt: 2 }, children: ["Playing ", character.name, "."] }))] })] }), _jsx(ChatFloating, { defaultViewMode: "assistant", assistantCharacter: drawn, protocol: "vercel-ai", endpoint:
|
|
87
|
+
} }), loadError && (_jsx(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mt: 2 }, children: loadError })), typeof character !== 'string' && (_jsxs(Text, { as: "p", sx: { mt: 2 }, children: ["Playing ", character.name, "."] }))] })] }), _jsx(ChatFloating, { defaultViewMode: "assistant", assistantCharacter: drawn, protocol: "vercel-ai", endpoint: `${SERVER}/api/v1/vercel-ai/assistant`, title: "Assistant", description: "Hello! Ask me anything about this page.", position: "bottom-right", useStore: false,
|
|
88
|
+
// Ask a decision beside the composer: Jev, through this server.
|
|
89
|
+
decisions: { serverUrl: SERVER } }, typeof character === 'string' ? character : character.sprite)] }) }));
|
|
79
90
|
};
|
|
80
91
|
export default ChatAssistantExample;
|
|
@@ -98,6 +98,7 @@ export const EXAMPLE_ENTRIES = [
|
|
|
98
98
|
makeEntry('AgentCompactionExample', () => import('./AgentCompactionExample'), 'Set a context token budget and watch history compaction summarize older messages, with live from/to token and timing details.', ['example', 'agent', 'compaction', 'context', 'tokens']),
|
|
99
99
|
makeEntry('AgentCodemodeExample', () => import('./AgentCodemodeExample'), 'Code mode execution and tool orchestration example.'),
|
|
100
100
|
makeEntry('AgentCodeSandboxesExample', () => import('./AgentCodeSandboxesExample'), 'Launch sandbox-variant agent specs and compare code execution across backends.', ['example', 'agent', 'sandbox', 'codemode']),
|
|
101
|
+
makeEntry('AgentDecideExample', () => import('./AgentDecideExample'), 'Decide: typed decisions asked of Jev — yes or no, a choice, a score — from the chat, where the agent calls decide, and from the floating assistant’s Ask a decision.', ['example', 'agent', 'loop', 'app', 'decisions', 'jev', 'assistant']),
|
|
101
102
|
makeEntry('AgentEvalsExample', () => import('./AgentEvalsExample'), 'Evaluation workflows for agent outputs.'),
|
|
102
103
|
makeEntry('AgentGuardrailsExample', () => import('./AgentGuardrailsExample'), 'Guardrails and safety checks for agent runs.'),
|
|
103
104
|
makeEntry('AgentHooksExample', () => import('./AgentHooksExample'), 'Pre-hooks and post-hooks lifecycle execution example.'),
|
package/lib/examples/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { default as AgentCheckpointsExample } from './AgentCheckpointsExample';
|
|
|
25
25
|
export { default as AgentCompactionExample } from './AgentCompactionExample';
|
|
26
26
|
export { default as AgentCodemodeExample } from './AgentCodemodeExample';
|
|
27
27
|
export { default as AgentCodeSandboxesExample } from './AgentCodeSandboxesExample';
|
|
28
|
+
export { default as AgentDecideExample } from './AgentDecideExample';
|
|
28
29
|
export { default as AgentEvalsExample } from './AgentEvalsExample';
|
|
29
30
|
export { default as AgentGuardrailsExample } from './AgentGuardrailsExample';
|
|
30
31
|
export { default as AgentHooksExample } from './AgentHooksExample';
|
package/lib/examples/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export { default as AgentCheckpointsExample } from './AgentCheckpointsExample';
|
|
|
31
31
|
export { default as AgentCompactionExample } from './AgentCompactionExample';
|
|
32
32
|
export { default as AgentCodemodeExample } from './AgentCodemodeExample';
|
|
33
33
|
export { default as AgentCodeSandboxesExample } from './AgentCodeSandboxesExample';
|
|
34
|
+
export { default as AgentDecideExample } from './AgentDecideExample';
|
|
34
35
|
export { default as AgentEvalsExample } from './AgentEvalsExample';
|
|
35
36
|
export { default as AgentGuardrailsExample } from './AgentGuardrailsExample';
|
|
36
37
|
export { default as AgentHooksExample } from './AgentHooksExample';
|
package/lib/examples/main.js
CHANGED
|
@@ -70,6 +70,9 @@ const ANONYMOUS_EXAMPLES = new Set([
|
|
|
70
70
|
// An application beside its computer, on the local agent-runtimes server:
|
|
71
71
|
// nothing allocated on Datalayer, and the machine itself needs no token.
|
|
72
72
|
'LoopAppComputerExample',
|
|
73
|
+
// Decide, on the local agent-runtimes server as above: Jev is asked
|
|
74
|
+
// through the server's ai-inference, with the server's own key.
|
|
75
|
+
'AgentDecideExample',
|
|
73
76
|
// Runs on the browser sandbox: nothing to allocate, nothing to sign into.
|
|
74
77
|
'LoopShellExample',
|
|
75
78
|
// Temporarily anonymous so the Jupyter output surface can be driven and
|
package/lib/loop/apps/appspec.js
CHANGED
|
@@ -89,7 +89,7 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
89
89
|
instructions: '',
|
|
90
90
|
model: '',
|
|
91
91
|
skills: [],
|
|
92
|
-
|
|
92
|
+
backendTools: [],
|
|
93
93
|
context: [],
|
|
94
94
|
contents: [],
|
|
95
95
|
connections: [],
|
|
@@ -147,7 +147,7 @@ const KNOWN_KEYS = [
|
|
|
147
147
|
'instructions',
|
|
148
148
|
'model',
|
|
149
149
|
'skills',
|
|
150
|
-
'
|
|
150
|
+
'backend_tools',
|
|
151
151
|
'context',
|
|
152
152
|
'contents',
|
|
153
153
|
'connections',
|
|
@@ -341,7 +341,7 @@ export function parseAppspec(document) {
|
|
|
341
341
|
instructions: text(data.instructions),
|
|
342
342
|
model: text(data.model),
|
|
343
343
|
skills: texts(data.skills),
|
|
344
|
-
|
|
344
|
+
backendTools: texts(data.backend_tools),
|
|
345
345
|
context: texts(data.context),
|
|
346
346
|
contents: texts(data.contents),
|
|
347
347
|
connections: records(data.connections).map(parseConnection),
|
|
@@ -541,7 +541,7 @@ export function dumpAppspec(app) {
|
|
|
541
541
|
.text('instructions', app.instructions)
|
|
542
542
|
.text('model', app.model)
|
|
543
543
|
.list('skills', app.skills)
|
|
544
|
-
.list('
|
|
544
|
+
.list('backend_tools', app.backendTools)
|
|
545
545
|
.list('context', app.context)
|
|
546
546
|
.list('contents', app.contents)
|
|
547
547
|
.list('connections', app.connections.map(connection => new Writer()
|
package/lib/loop/apps/checks.js
CHANGED
|
@@ -36,7 +36,7 @@ import { getModel } from '../../specs/models';
|
|
|
36
36
|
import { getNotificationSpec } from '../../specs/notifications';
|
|
37
37
|
import { getSkillSpec } from '../../specs/skills';
|
|
38
38
|
import { getTeamSpec } from '../../specs/teams';
|
|
39
|
-
import {
|
|
39
|
+
import { getBackendToolSpec } from '../../specs/backendTools';
|
|
40
40
|
import { getTrack } from '../../specs/tracks';
|
|
41
41
|
import { pluginsOffSetupNotes } from '../plugins/canvas-blocks';
|
|
42
42
|
import { isAssistantCharacterId, parseAppspec } from './appspec';
|
|
@@ -173,8 +173,8 @@ function referenceProblems(app, organizationFrames) {
|
|
|
173
173
|
if (!getSkillSpec(idOf(ref)))
|
|
174
174
|
missing('skill', ref);
|
|
175
175
|
}
|
|
176
|
-
for (const ref of app.
|
|
177
|
-
if (!
|
|
176
|
+
for (const ref of app.backendTools) {
|
|
177
|
+
if (!getBackendToolSpec(idOf(ref)))
|
|
178
178
|
missing('tool', ref);
|
|
179
179
|
}
|
|
180
180
|
for (const ref of app.checks.guards) {
|
|
@@ -241,7 +241,7 @@ function referenceProblems(app, organizationFrames) {
|
|
|
241
241
|
continue;
|
|
242
242
|
const [server, name] = splitRef(target);
|
|
243
243
|
if (server === undefined) {
|
|
244
|
-
if (!
|
|
244
|
+
if (!getBackendToolSpec(name)) {
|
|
245
245
|
problems.push(`The rule “${rule.action}” names the tool “${target}”, which the catalogue does not have.`);
|
|
246
246
|
}
|
|
247
247
|
continue;
|
|
@@ -339,8 +339,8 @@ function setupNotes(app) {
|
|
|
339
339
|
}
|
|
340
340
|
for (const ref of app.skills)
|
|
341
341
|
note('skill', ref, getSkillSpec(idOf(ref)));
|
|
342
|
-
for (const ref of app.
|
|
343
|
-
note('tool', ref,
|
|
342
|
+
for (const ref of app.backendTools)
|
|
343
|
+
note('tool', ref, getBackendToolSpec(idOf(ref)));
|
|
344
344
|
for (const ref of app.checks.guards)
|
|
345
345
|
note('Guard', ref, own(GUARD_CATALOGUE, idOf(ref)));
|
|
346
346
|
for (const ref of app.checks.gates)
|
package/lib/loop/apps/rules.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
*
|
|
35
35
|
* @module loop/apps/rules
|
|
36
36
|
*/
|
|
37
|
-
import { SERVER_ACTIONS,
|
|
37
|
+
import { SERVER_ACTIONS, BACKEND_TOOL_ACTIONS } from '../../specs/actions';
|
|
38
38
|
/** The four behaviours, from the freest to the most restricted. */
|
|
39
39
|
export const BEHAVIOURS = [
|
|
40
40
|
'do_it',
|
|
@@ -138,7 +138,7 @@ function entryOf(server, name) {
|
|
|
138
138
|
export function classesOf(ref, args) {
|
|
139
139
|
const [server, name] = splitRef(ref);
|
|
140
140
|
if (server === undefined) {
|
|
141
|
-
return [...(own(
|
|
141
|
+
return [...(own(BACKEND_TOOL_ACTIONS, name) ?? [])];
|
|
142
142
|
}
|
|
143
143
|
const [classes, conditions] = entryOf(server, name);
|
|
144
144
|
for (const condition of conditions) {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* while the conversation is out of sight, holds there the approval it waits
|
|
10
10
|
* on, answered with *Approve* or *Deny* on the path every approval of it is
|
|
11
11
|
* answered on (T-23, U-19), opens the conversation when clicked, and is
|
|
12
|
-
* dragged about and sent away as the chat's own assistant is (T-27).
|
|
12
|
+
* dragged about and sent away as the chat's own assistant is (T-27). Given
|
|
13
|
+
* the runtime to ask (`decisions`), its balloon offers *Ask a decision*: a
|
|
14
|
+
* typed decision asked of Jev through the runtime, answered there.
|
|
13
15
|
*
|
|
14
16
|
* The workspace's chat is the conversation: on a page layout, its side panel
|
|
15
17
|
* is opened and closed; where the conversation is always on screen, a click
|
|
@@ -33,6 +35,16 @@ export type LoopAssistantConfig = {
|
|
|
33
35
|
* the balloon says an approval waits and the conversation answers it.
|
|
34
36
|
*/
|
|
35
37
|
appId?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The runtime decisions are asked at — its
|
|
40
|
+
* `/api/v1/configure/inference/decisions`, as the `decide` tool asks — and
|
|
41
|
+
* the token to ask with, if it needs one: the balloon offers *Ask a
|
|
42
|
+
* decision*. Without it, it does not.
|
|
43
|
+
*/
|
|
44
|
+
decisions?: {
|
|
45
|
+
serverUrl: string;
|
|
46
|
+
token?: string;
|
|
47
|
+
};
|
|
36
48
|
};
|
|
37
49
|
/**
|
|
38
50
|
* What the character acts out (T-22), from what the application is doing as
|
|
@@ -43,6 +55,6 @@ export declare function assistantStateOfTurn(turn: ChatTurnSnapshot, presence: P
|
|
|
43
55
|
arriving?: boolean;
|
|
44
56
|
leaving?: boolean;
|
|
45
57
|
}): AssistantState;
|
|
46
|
-
export declare function LoopAssistant({ app, person, appId, }: LoopAssistantConfig): JSX.Element | null;
|
|
58
|
+
export declare function LoopAssistant({ app, person, appId, decisions, }: LoopAssistantConfig): JSX.Element | null;
|
|
47
59
|
export declare const LoopAssistantPlugin: import("@datalayer/reactor").ReactorPlugin<LoopAssistantConfig, unknown, unknown>;
|
|
48
60
|
export default LoopAssistantPlugin;
|
|
@@ -7,6 +7,8 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
7
7
|
import { useIAMStore } from '@datalayer/core/lib/state/substates/IAMState';
|
|
8
8
|
import { pageLayoutPanelOpen, togglePagePanel, } from '@datalayer/primer-addons/lib/reactor';
|
|
9
9
|
import { AssistantStage } from '../../../chat/assistant/AssistantStage';
|
|
10
|
+
import { decisionsAskerAt } from '../../../chat/assistant/decisions';
|
|
11
|
+
import { peekLine } from '../../../chat/assistant/ConversationBalloon';
|
|
10
12
|
import { ASSISTANT_WORDS, assistantStateOf, keepAway, keptAway, latestSaying, } from '../../../chat/assistant/state';
|
|
11
13
|
import { useApproveToolRequest, useRejectToolRequest, useToolApprovalsQuery, } from '../../../hooks/useToolApprovals';
|
|
12
14
|
import { approvalsOfApp, ruleOfApproval } from '../app-rules/AppRulesCard';
|
|
@@ -67,7 +69,7 @@ function focusComposer() {
|
|
|
67
69
|
const composer = document.querySelector('[data-chat-composer] textarea, [data-chat-composer] [contenteditable="true"]');
|
|
68
70
|
composer?.focus();
|
|
69
71
|
}
|
|
70
|
-
export function LoopAssistant({ app, person, appId, }) {
|
|
72
|
+
export function LoopAssistant({ app, person, appId, decisions, }) {
|
|
71
73
|
const contributed = useContributions(LoopAssistantCharacter).map(entry => entry.value);
|
|
72
74
|
const chosen = assistantCharacterFor(contributed, { app, person });
|
|
73
75
|
const turnEntries = useContributions(LoopChatTurn);
|
|
@@ -110,6 +112,9 @@ export function LoopAssistant({ app, person, appId, }) {
|
|
|
110
112
|
}, [open, saying?.id]);
|
|
111
113
|
const stageRef = useRef(null);
|
|
112
114
|
const drag = useViewportDrag(stageRef, { whole: true });
|
|
115
|
+
const decide = useMemo(() => decisions?.serverUrl
|
|
116
|
+
? decisionsAskerAt(decisions.serverUrl, decisions.token)
|
|
117
|
+
: undefined, [decisions?.serverUrl, decisions?.token]);
|
|
113
118
|
if (away !== 'none') {
|
|
114
119
|
return null;
|
|
115
120
|
}
|
|
@@ -138,7 +143,14 @@ export function LoopAssistant({ app, person, appId, }) {
|
|
|
138
143
|
: presence === 'waiting'
|
|
139
144
|
? { text: ASSISTANT_WORDS.waiting }
|
|
140
145
|
: unheard && saying
|
|
141
|
-
? {
|
|
146
|
+
? {
|
|
147
|
+
// A peek: the first words; the rest is in the conversation.
|
|
148
|
+
...peekLine(saying.text),
|
|
149
|
+
onDismiss: () => {
|
|
150
|
+
setHeardId(saying.id);
|
|
151
|
+
setFresh(false);
|
|
152
|
+
},
|
|
153
|
+
}
|
|
142
154
|
: { text: 'Click me to open the conversation.' };
|
|
143
155
|
const insist = state === 'greeting' ||
|
|
144
156
|
presence === 'waiting' ||
|
|
@@ -162,16 +174,21 @@ export function LoopAssistant({ app, person, appId, }) {
|
|
|
162
174
|
};
|
|
163
175
|
return (_jsxs(_Fragment, { children: [appId && signedIn ? (_jsx(WaitingApprovals, { appId: appId, onWaiting: setApproval })) : null, _jsx(AssistantStage, { character: chosen.character, state: state, size: SIZE, place: drag.position
|
|
164
176
|
? { left: `${drag.position.left}px`, top: `${drag.position.top}px` }
|
|
165
|
-
: { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, insist: insist, onDismiss: onDismiss })] }));
|
|
177
|
+
: { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, insist: insist, onDismiss: onDismiss, decide: decide })] }));
|
|
166
178
|
}
|
|
167
179
|
export const LoopAssistantPlugin = definePlugin({
|
|
168
180
|
name: LOOP_ASSISTANT_PLUGIN_NAME,
|
|
169
|
-
config: {
|
|
181
|
+
config: {
|
|
182
|
+
app: undefined,
|
|
183
|
+
person: undefined,
|
|
184
|
+
appId: undefined,
|
|
185
|
+
decisions: undefined,
|
|
186
|
+
},
|
|
170
187
|
displayName: 'Floating assistant',
|
|
171
188
|
description: 'The chat as a character on the page: the application’s own, or the one the person chose, from the characters the enabled plugins contribute.',
|
|
172
189
|
octicon: 'paperclip',
|
|
173
190
|
build: ({ config }) => {
|
|
174
|
-
const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId }));
|
|
191
|
+
const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId, decisions: config.decisions }));
|
|
175
192
|
return {
|
|
176
193
|
components: [
|
|
177
194
|
{ id: 'loop-assistant', slot: LoopSlots.root, Component: Configured },
|
package/lib/specs/actions.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { ActionClass, AppBehaviour, AppEscalation, ServerActionsSpec } from
|
|
|
11
11
|
/** The classes of action, from the one that changes nothing. */
|
|
12
12
|
export declare const ACTION_CLASSES: ActionClass[];
|
|
13
13
|
/** What each tool of the tools catalogue does, by id. */
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const BACKEND_TOOL_ACTIONS: Record<string, ActionClass[]>;
|
|
15
15
|
/**
|
|
16
16
|
* What each MCP server's tools do, by server id. `checked` is the day the
|
|
17
17
|
* names were read off the running server; absent when nobody looked.
|
package/lib/specs/actions.js
CHANGED
|
@@ -12,7 +12,7 @@ export const ACTION_CLASSES = [
|
|
|
12
12
|
'publish',
|
|
13
13
|
];
|
|
14
14
|
/** What each tool of the tools catalogue does, by id. */
|
|
15
|
-
export const
|
|
15
|
+
export const BACKEND_TOOL_ACTIONS = {
|
|
16
16
|
'create-plan': ['read'],
|
|
17
17
|
'current-time': ['read'],
|
|
18
18
|
decide: ['read'],
|