@datalayer/agent-runtimes 1.3.58 → 1.3.60
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 +6 -2
- package/lib/chat/assistant/AssistantStage.d.ts +7 -1
- package/lib/chat/assistant/AssistantStage.js +5 -3
- package/lib/chat/assistant/DecisionAsk.d.ts +31 -0
- package/lib/chat/assistant/DecisionAsk.js +96 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +11 -2
- package/lib/chat/assistant/SpeechBalloon.js +5 -4
- package/lib/chat/assistant/decisions.d.ts +104 -0
- package/lib/chat/assistant/decisions.js +161 -0
- 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/ChatAssistantGalleryExample.d.ts +12 -0
- package/lib/examples/ChatAssistantGalleryExample.js +181 -0
- package/lib/examples/example-selector.js +2 -0
- package/lib/examples/index.d.ts +2 -0
- package/lib/examples/index.js +2 -0
- package/lib/examples/main.js +5 -0
- package/lib/examples/utils/AssistantGalleryGrid.d.ts +53 -0
- package/lib/examples/utils/AssistantGalleryGrid.js +123 -0
- package/lib/examples/utils/assistantGallery.d.ts +42 -0
- package/lib/examples/utils/assistantGallery.js +102 -0
- package/lib/examples/utils/testSpriteCharacter.d.ts +9 -0
- package/lib/examples/utils/testSpriteCharacter.js +60 -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 +13 -4
- 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/pictures-main.js +16 -1
- package/lib/types/agentspecs.d.ts +7 -5
- 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,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { ASSISTANT_WORDS, latestSaying, } from '../../chat/assistant/state';
|
|
6
|
+
/** Every state, in the order an assistant lives them, then stepped aside. */
|
|
7
|
+
export const GALLERY_POSES = [
|
|
8
|
+
'idle',
|
|
9
|
+
'greeting',
|
|
10
|
+
'thinking',
|
|
11
|
+
'working',
|
|
12
|
+
'waiting',
|
|
13
|
+
'speaking',
|
|
14
|
+
'paused',
|
|
15
|
+
'aside',
|
|
16
|
+
'goodbye',
|
|
17
|
+
];
|
|
18
|
+
/** How the gallery names each pose. */
|
|
19
|
+
export const GALLERY_POSE_LABELS = {
|
|
20
|
+
idle: 'Idle',
|
|
21
|
+
greeting: 'Greeting',
|
|
22
|
+
thinking: 'Thinking',
|
|
23
|
+
working: 'Working',
|
|
24
|
+
waiting: 'Waiting',
|
|
25
|
+
speaking: 'Speaking',
|
|
26
|
+
paused: 'Paused',
|
|
27
|
+
aside: 'Stepped aside',
|
|
28
|
+
goodbye: 'Goodbye',
|
|
29
|
+
};
|
|
30
|
+
/** The state the stage acts for a pose: aside, it is idle under what covers it. */
|
|
31
|
+
export function stateOfPose(pose) {
|
|
32
|
+
return pose === 'aside' ? 'idle' : pose;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* What the agent said, as a conversation would hold it: each entry is read
|
|
36
|
+
* by `latestSaying`, as the floating assistant reads the chat — Markdown made
|
|
37
|
+
* plain, a long answer cut with *Open the conversation*.
|
|
38
|
+
*/
|
|
39
|
+
export const SAMPLE_CONVERSATIONS = [
|
|
40
|
+
[
|
|
41
|
+
{ id: 'u1', role: 'user', content: 'Who wrote about late deliveries?' },
|
|
42
|
+
{
|
|
43
|
+
id: 'a1',
|
|
44
|
+
role: 'assistant',
|
|
45
|
+
content: 'Three customers wrote about **late deliveries** this week: Ada, Grace and Alan.',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
{ id: 'u2', role: 'user', content: 'Is the notebook up to date?' },
|
|
50
|
+
{ id: 't2', role: 'assistant', toolName: 'readCell', content: '' },
|
|
51
|
+
{
|
|
52
|
+
id: 'a2',
|
|
53
|
+
role: 'assistant',
|
|
54
|
+
content: 'Yes — the last cell ran at 9:40 and its [chart](#chart) shows the new week.',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
{ id: 'u3', role: 'user', content: 'Summarise the quarter.' },
|
|
59
|
+
{
|
|
60
|
+
id: 'a3',
|
|
61
|
+
role: 'assistant',
|
|
62
|
+
content: 'Revenue grew 12% on the quarter, carried by the renewals in March. Churn held at 2.1%, the same as the last two quarters, and the new plan took 140 accounts in its first six weeks. Support tickets fell by a fifth after the onboarding change, and the slowest region, the north, closed half its gap with the others. The full table, with each region and month, is in the notebook.',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
];
|
|
66
|
+
/** The saying of sample `index` (wrapping), as the balloon shows it. */
|
|
67
|
+
export function sampleSaying(index) {
|
|
68
|
+
const conversation = SAMPLE_CONVERSATIONS[((index % SAMPLE_CONVERSATIONS.length) + SAMPLE_CONVERSATIONS.length) %
|
|
69
|
+
SAMPLE_CONVERSATIONS.length];
|
|
70
|
+
const saying = latestSaying(conversation);
|
|
71
|
+
if (!saying) {
|
|
72
|
+
throw new Error(`The sample conversation ${index} says nothing.`);
|
|
73
|
+
}
|
|
74
|
+
return { text: saying.text, more: saying.more };
|
|
75
|
+
}
|
|
76
|
+
/** The approval the gallery waits on (T-23): answered in the balloon. */
|
|
77
|
+
export function sampleApproval(onApprove, onDeny) {
|
|
78
|
+
return {
|
|
79
|
+
id: 'gallery-approval',
|
|
80
|
+
asks: 'send_reply',
|
|
81
|
+
why: 'Send a reply to a customer: ask me first',
|
|
82
|
+
others: 1,
|
|
83
|
+
onApprove,
|
|
84
|
+
onDeny,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The balloon for a pose: waiting holds the approval, paused says so, and
|
|
89
|
+
* any other pose says the sample's words when asked to (`saying`).
|
|
90
|
+
*/
|
|
91
|
+
export function balloonForPose(pose, { saying, approval, }) {
|
|
92
|
+
if (pose === 'waiting') {
|
|
93
|
+
return { text: ASSISTANT_WORDS.approval, approval };
|
|
94
|
+
}
|
|
95
|
+
if (pose === 'paused') {
|
|
96
|
+
return { text: ASSISTANT_WORDS.paused };
|
|
97
|
+
}
|
|
98
|
+
if (pose === 'goodbye' || pose === 'aside') {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return saying;
|
|
102
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AssistantCharacterData } from '../../chat/assistant/formats/types';
|
|
2
|
+
/** The character's `agent.js`, as clippy.js registers one. */
|
|
3
|
+
export declare const TEST_SPRITE_AGENT_JS = "\nclippy.ready('Pixel', {\n overlayCount: 1,\n framesize: [48, 48],\n animations: {\n Idle1_1: { frames: [{ duration: 1200, images: [[0, 0]] }] },\n Thinking: { frames: [{ duration: 1200, images: [[48, 0]] }] },\n Processing: { frames: [\n { duration: 240, images: [[96, 0]] },\n { duration: 240, images: [[144, 0]] },\n ] },\n GetAttention: { frames: [{ duration: 900, images: [[192, 0]] }] },\n Sleep: { frames: [{ duration: 1600, images: [[240, 0]] }] },\n Greeting: { frames: [{ duration: 900, images: [[288, 0]] }] },\n Explain: { frames: [\n { duration: 200, images: [[336, 0]] },\n { duration: 200, images: [[384, 0]] },\n ] },\n GoodBye: { frames: [{ duration: 900, images: [[432, 0]] }] },\n },\n});\n";
|
|
4
|
+
/** Its sprite sheet, `map.png` (480×48), base64. */
|
|
5
|
+
export declare const TEST_SPRITE_MAP_PNG_BASE64 = "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";
|
|
6
|
+
/** The sprite sheet's bytes. */
|
|
7
|
+
export declare function testSpriteMapPng(): Uint8Array;
|
|
8
|
+
/** The test sprite, read as a person's clippy.js files are. */
|
|
9
|
+
export declare function readTestSpriteCharacter(): Promise<AssistantCharacterData>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A small character of our own in the clippy.js shape (LOOP T-26), for the
|
|
7
|
+
* floating assistant's gallery: an `agent.js` and a `map.png` sprite sheet
|
|
8
|
+
* of ten 48×48 cells, read through the same reader a person's files go
|
|
9
|
+
* through (`readClippyCharacter`). It is a test sprite drawn for Datalayer —
|
|
10
|
+
* no file of Microsoft's is in the repository (§6.9).
|
|
11
|
+
*
|
|
12
|
+
* Its animations carry the names the Office characters use, so the reader's
|
|
13
|
+
* `stateAnimations` gives every state of the assistant one of its own:
|
|
14
|
+
* `Idle1_1` idles, `Thinking`, `Processing`, `GetAttention`, `Sleep`,
|
|
15
|
+
* `Greeting`, `Explain` and `GoodBye`.
|
|
16
|
+
*
|
|
17
|
+
* @module examples/utils/testSpriteCharacter
|
|
18
|
+
*/
|
|
19
|
+
import { readClippyCharacter } from '../../chat/assistant/formats/clippy';
|
|
20
|
+
/** The character's `agent.js`, as clippy.js registers one. */
|
|
21
|
+
export const TEST_SPRITE_AGENT_JS = `
|
|
22
|
+
clippy.ready('Pixel', {
|
|
23
|
+
overlayCount: 1,
|
|
24
|
+
framesize: [48, 48],
|
|
25
|
+
animations: {
|
|
26
|
+
Idle1_1: { frames: [{ duration: 1200, images: [[0, 0]] }] },
|
|
27
|
+
Thinking: { frames: [{ duration: 1200, images: [[48, 0]] }] },
|
|
28
|
+
Processing: { frames: [
|
|
29
|
+
{ duration: 240, images: [[96, 0]] },
|
|
30
|
+
{ duration: 240, images: [[144, 0]] },
|
|
31
|
+
] },
|
|
32
|
+
GetAttention: { frames: [{ duration: 900, images: [[192, 0]] }] },
|
|
33
|
+
Sleep: { frames: [{ duration: 1600, images: [[240, 0]] }] },
|
|
34
|
+
Greeting: { frames: [{ duration: 900, images: [[288, 0]] }] },
|
|
35
|
+
Explain: { frames: [
|
|
36
|
+
{ duration: 200, images: [[336, 0]] },
|
|
37
|
+
{ duration: 200, images: [[384, 0]] },
|
|
38
|
+
] },
|
|
39
|
+
GoodBye: { frames: [{ duration: 900, images: [[432, 0]] }] },
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
`;
|
|
43
|
+
/** Its sprite sheet, `map.png` (480×48), base64. */
|
|
44
|
+
export const TEST_SPRITE_MAP_PNG_BASE64 = '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';
|
|
45
|
+
/** The sprite sheet's bytes. */
|
|
46
|
+
export function testSpriteMapPng() {
|
|
47
|
+
const binary = atob(TEST_SPRITE_MAP_PNG_BASE64);
|
|
48
|
+
const bytes = new Uint8Array(binary.length);
|
|
49
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
50
|
+
bytes[index] = binary.charCodeAt(index);
|
|
51
|
+
}
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
/** The test sprite, read as a person's clippy.js files are. */
|
|
55
|
+
export function readTestSpriteCharacter() {
|
|
56
|
+
return readClippyCharacter({
|
|
57
|
+
agentJs: TEST_SPRITE_AGENT_JS,
|
|
58
|
+
mapPng: new Blob([testSpriteMapPng()], { type: 'image/png' }),
|
|
59
|
+
});
|
|
60
|
+
}
|
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,7 @@ 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';
|
|
10
11
|
import { ASSISTANT_WORDS, assistantStateOf, keepAway, keptAway, latestSaying, } from '../../../chat/assistant/state';
|
|
11
12
|
import { useApproveToolRequest, useRejectToolRequest, useToolApprovalsQuery, } from '../../../hooks/useToolApprovals';
|
|
12
13
|
import { approvalsOfApp, ruleOfApproval } from '../app-rules/AppRulesCard';
|
|
@@ -67,7 +68,7 @@ function focusComposer() {
|
|
|
67
68
|
const composer = document.querySelector('[data-chat-composer] textarea, [data-chat-composer] [contenteditable="true"]');
|
|
68
69
|
composer?.focus();
|
|
69
70
|
}
|
|
70
|
-
export function LoopAssistant({ app, person, appId, }) {
|
|
71
|
+
export function LoopAssistant({ app, person, appId, decisions, }) {
|
|
71
72
|
const contributed = useContributions(LoopAssistantCharacter).map(entry => entry.value);
|
|
72
73
|
const chosen = assistantCharacterFor(contributed, { app, person });
|
|
73
74
|
const turnEntries = useContributions(LoopChatTurn);
|
|
@@ -110,6 +111,9 @@ export function LoopAssistant({ app, person, appId, }) {
|
|
|
110
111
|
}, [open, saying?.id]);
|
|
111
112
|
const stageRef = useRef(null);
|
|
112
113
|
const drag = useViewportDrag(stageRef, { whole: true });
|
|
114
|
+
const decide = useMemo(() => decisions?.serverUrl
|
|
115
|
+
? decisionsAskerAt(decisions.serverUrl, decisions.token)
|
|
116
|
+
: undefined, [decisions?.serverUrl, decisions?.token]);
|
|
113
117
|
if (away !== 'none') {
|
|
114
118
|
return null;
|
|
115
119
|
}
|
|
@@ -162,16 +166,21 @@ export function LoopAssistant({ app, person, appId, }) {
|
|
|
162
166
|
};
|
|
163
167
|
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
168
|
? { 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 })] }));
|
|
169
|
+
: { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, insist: insist, onDismiss: onDismiss, decide: decide })] }));
|
|
166
170
|
}
|
|
167
171
|
export const LoopAssistantPlugin = definePlugin({
|
|
168
172
|
name: LOOP_ASSISTANT_PLUGIN_NAME,
|
|
169
|
-
config: {
|
|
173
|
+
config: {
|
|
174
|
+
app: undefined,
|
|
175
|
+
person: undefined,
|
|
176
|
+
appId: undefined,
|
|
177
|
+
decisions: undefined,
|
|
178
|
+
},
|
|
170
179
|
displayName: 'Floating assistant',
|
|
171
180
|
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
181
|
octicon: 'paperclip',
|
|
173
182
|
build: ({ config }) => {
|
|
174
|
-
const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId }));
|
|
183
|
+
const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId, decisions: config.decisions }));
|
|
175
184
|
return {
|
|
176
185
|
components: [
|
|
177
186
|
{ 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'],
|