@datalayer/agent-runtimes 1.5.45 → 1.5.47
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/dist-embed/datalayer-app-main.js +4 -4
- package/lib/portable/conversation.d.ts +6 -0
- package/lib/portable/conversation.js +29 -1
- package/lib/portable/hosts/__tests__/fixtures/lock-with-jupyter.json +40 -0
- package/lib/portable/hosts/__tests__/fixtures/npm-ls-with-jupyter.json +21 -0
- package/lib/portable/hosts/agentClient.d.ts +167 -0
- package/lib/portable/hosts/agentClient.js +346 -0
- package/lib/portable/hosts/approvalRenderers.d.ts +43 -0
- package/lib/portable/hosts/approvalRenderers.js +180 -0
- package/lib/portable/hosts/client.d.ts +95 -0
- package/lib/portable/hosts/client.js +174 -0
- package/lib/portable/hosts/contracts.d.ts +199 -0
- package/lib/portable/hosts/contracts.js +235 -0
- package/lib/portable/hosts/contracts.json +381 -0
- package/lib/portable/hosts/deadLetters.d.ts +41 -0
- package/lib/portable/hosts/deadLetters.js +124 -0
- package/lib/portable/hosts/deliveries.d.ts +76 -0
- package/lib/portable/hosts/deliveries.js +147 -0
- package/lib/portable/hosts/forbiddenGraph.d.ts +77 -0
- package/lib/portable/hosts/forbiddenGraph.js +174 -0
- package/lib/portable/hosts/health.d.ts +35 -0
- package/lib/portable/hosts/health.js +92 -0
- package/lib/portable/hosts/index.d.ts +40 -0
- package/lib/portable/hosts/index.js +45 -0
- package/lib/portable/hosts/observability.d.ts +67 -0
- package/lib/portable/hosts/observability.js +171 -0
- package/lib/portable/hosts/postgresQueue.d.ts +55 -0
- package/lib/portable/hosts/postgresQueue.js +217 -0
- package/lib/portable/hosts/queue.d.ts +47 -0
- package/lib/portable/hosts/queue.js +63 -0
- package/lib/portable/hosts/quotas.d.ts +81 -0
- package/lib/portable/hosts/quotas.js +147 -0
- package/lib/portable/hosts/rateLimit.d.ts +13 -0
- package/lib/portable/hosts/rateLimit.js +8 -0
- package/lib/portable/hosts/resolution.d.ts +54 -0
- package/lib/portable/hosts/resolution.js +46 -0
- package/lib/portable/hosts/schema.d.ts +19 -0
- package/lib/portable/hosts/schema.js +81 -0
- package/lib/portable/hosts/secretCipher.d.ts +23 -0
- package/lib/portable/hosts/secretCipher.js +45 -0
- package/lib/portable/hosts/sessionUid.d.ts +7 -0
- package/lib/portable/hosts/sessionUid.js +28 -0
- package/lib/portable/hosts/turnState.d.ts +45 -0
- package/lib/portable/hosts/turnState.js +113 -0
- package/lib/portable/index.d.ts +2 -0
- package/lib/portable/index.js +3 -0
- package/lib/portable/semantic-events.json +480 -0
- package/lib/portable/semantic.d.ts +78 -0
- package/lib/portable/semantic.js +100 -0
- package/lib/portable/semanticOfAgUi.d.ts +41 -0
- package/lib/portable/semanticOfAgUi.js +108 -0
- package/lib/protocols/A2AAdapter.js +19 -0
- package/lib/test-setup.js +17 -14
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/** A string field of an event, or the fallback: never an object stringified. */
|
|
6
|
+
const stringOf = (value, fallback = '') => typeof value === 'string' ? value : fallback;
|
|
7
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
+
/** The turn a `loop.turn` event names, or `undefined` for any other event. */
|
|
9
|
+
export function turnOf(event) {
|
|
10
|
+
if (event.type !== 'CUSTOM' || event.name !== 'loop.turn') {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const value = isRecord(event.value) ? event.value : {};
|
|
14
|
+
return {
|
|
15
|
+
replayed: value.replayed === true,
|
|
16
|
+
session: stringOf(value.session),
|
|
17
|
+
state: stringOf(value.state),
|
|
18
|
+
turn: stringOf(value.turn),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** What a tool's result says of itself: JSON when it is JSON, else nothing. */
|
|
22
|
+
function resultRecord(content) {
|
|
23
|
+
if (isRecord(content)) {
|
|
24
|
+
return content;
|
|
25
|
+
}
|
|
26
|
+
if (typeof content !== 'string') {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(content);
|
|
31
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The question a turn ends on (`RUN_FINISHED.result.asking`), as
|
|
39
|
+
* a host asks it, or `undefined` when the value is not a question. A kind a
|
|
40
|
+
* host does not know is answered in Datalayer, as a form (SLACK S-307):
|
|
41
|
+
* never guessed to be text. (`conversation.ts`'s `questionOf`, for a native
|
|
42
|
+
* client, throws on such a kind instead.)
|
|
43
|
+
*/
|
|
44
|
+
export function askQuestionOf(value) {
|
|
45
|
+
if (!isRecord(value) || typeof value.prompt !== 'string') {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const kind = value.kind === 'text' ||
|
|
49
|
+
value.kind === 'choice' ||
|
|
50
|
+
value.kind === 'file' ||
|
|
51
|
+
value.kind === 'form'
|
|
52
|
+
? value.kind
|
|
53
|
+
: 'form';
|
|
54
|
+
const options = Array.isArray(value.options)
|
|
55
|
+
? value.options.filter((one) => typeof one === 'string')
|
|
56
|
+
: undefined;
|
|
57
|
+
return kind === 'choice'
|
|
58
|
+
? { kind, prompt: value.prompt, options: options ?? [] }
|
|
59
|
+
: { kind, prompt: value.prompt };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The semantic events (version 1) one AG-UI event means: none for an event a
|
|
63
|
+
* host does not show. `tools` keeps each running call's name by its id, for
|
|
64
|
+
* its result.
|
|
65
|
+
*/
|
|
66
|
+
export function semanticOf(event, tools) {
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case 'TEXT_MESSAGE_CONTENT':
|
|
69
|
+
case 'TEXT_MESSAGE_CHUNK':
|
|
70
|
+
return typeof event.delta === 'string' && event.delta
|
|
71
|
+
? [{ kind: 'text', text: event.delta }]
|
|
72
|
+
: [];
|
|
73
|
+
case 'TOOL_CALL_START': {
|
|
74
|
+
const id = stringOf(event.toolCallId);
|
|
75
|
+
const name = stringOf(event.toolCallName, 'a tool');
|
|
76
|
+
tools.set(id, name);
|
|
77
|
+
return [{ kind: 'tool', id, name, status: 'running' }];
|
|
78
|
+
}
|
|
79
|
+
case 'TOOL_CALL_RESULT': {
|
|
80
|
+
const id = stringOf(event.toolCallId);
|
|
81
|
+
const name = tools.get(id) ?? 'a tool';
|
|
82
|
+
const result = resultRecord(event.content);
|
|
83
|
+
if (result?.pending_approval === true) {
|
|
84
|
+
return [{ kind: 'approval', toolCallId: id, toolName: name }];
|
|
85
|
+
}
|
|
86
|
+
const failed = result?.success === false || result?.isError === true;
|
|
87
|
+
return [
|
|
88
|
+
{ kind: 'tool', id, name, status: failed ? 'failed' : 'complete' },
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
case 'STEP_STARTED':
|
|
92
|
+
return typeof event.stepName === 'string' && event.stepName
|
|
93
|
+
? [{ kind: 'progress', label: event.stepName }]
|
|
94
|
+
: [];
|
|
95
|
+
case 'RUN_FINISHED': {
|
|
96
|
+
const asking = askQuestionOf(isRecord(event.result) ? event.result.asking : undefined);
|
|
97
|
+
return asking
|
|
98
|
+
? [{ kind: 'ask', question: asking }]
|
|
99
|
+
: [{ kind: 'completion' }];
|
|
100
|
+
}
|
|
101
|
+
case 'RUN_ERROR':
|
|
102
|
+
return [
|
|
103
|
+
{ kind: 'error', message: stringOf(event.message, 'The run failed.') },
|
|
104
|
+
];
|
|
105
|
+
default:
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { ORCHESTRATION_EXTENSION_URI, ENVELOPE_KEY as DATALAYER_META_KEY, readUsageMeta, } from '@datalayer/agent-teams';
|
|
11
11
|
import { generateMessageId, createAssistantMessage } from '../types/messages';
|
|
12
12
|
import { BaseProtocolAdapter } from './BaseProtocolAdapter';
|
|
13
|
+
import { VOICE_SAY_EVENT, VOICE_SETTINGS_EVENT } from '../voice/sayAloud';
|
|
13
14
|
/**
|
|
14
15
|
* The header fasta2a-based workers (`agent-runtimes`'s own, and any other
|
|
15
16
|
* built on it) actually read to activate an extension for one request —
|
|
@@ -432,6 +433,24 @@ export class A2AAdapter extends BaseProtocolAdapter {
|
|
|
432
433
|
if (eventKind === 'status-update') {
|
|
433
434
|
const state = task.status?.state;
|
|
434
435
|
console.log('[A2AAdapter] Status update:', state, 'final:', task.final);
|
|
436
|
+
// The agent's voice and the lines it says aloud, in a working status
|
|
437
|
+
// message's metadata (VOICE.md VO-36): activities the chat says and
|
|
438
|
+
// captions, as AG-UI's `voice.settings` and `voice.say` custom events.
|
|
439
|
+
const voice = task.status?.message?.metadata?.voice;
|
|
440
|
+
if (voice?.settings) {
|
|
441
|
+
this.emit({
|
|
442
|
+
type: 'activity',
|
|
443
|
+
activity: { type: VOICE_SETTINGS_EVENT, data: voice.settings },
|
|
444
|
+
timestamp: new Date(),
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (voice?.say) {
|
|
448
|
+
this.emit({
|
|
449
|
+
type: 'activity',
|
|
450
|
+
activity: { type: VOICE_SAY_EVENT, data: voice.say },
|
|
451
|
+
timestamp: new Date(),
|
|
452
|
+
});
|
|
453
|
+
}
|
|
435
454
|
if (task.final &&
|
|
436
455
|
(state === 'completed' || state === 'failed' || state === 'canceled')) {
|
|
437
456
|
// Only read the envelope once the worker actually named the
|
package/lib/test-setup.js
CHANGED
|
@@ -68,20 +68,23 @@ class MockDataTransfer {
|
|
|
68
68
|
// Add missing DOM APIs to global scope
|
|
69
69
|
global.DragEvent = MockDragEvent;
|
|
70
70
|
global.DataTransfer = MockDataTransfer;
|
|
71
|
-
// Mock window.matchMedia
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
71
|
+
// Mock window.matchMedia. A test that runs in node (`@vitest-environment
|
|
72
|
+
// node`, the portable host code: plans/DISCORD.md §4.2) has no window.
|
|
73
|
+
if (typeof window !== 'undefined') {
|
|
74
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
75
|
+
writable: true,
|
|
76
|
+
value: (query) => ({
|
|
77
|
+
matches: false,
|
|
78
|
+
media: query,
|
|
79
|
+
onchange: null,
|
|
80
|
+
addListener: () => { }, // deprecated
|
|
81
|
+
removeListener: () => { }, // deprecated
|
|
82
|
+
addEventListener: () => { },
|
|
83
|
+
removeEventListener: () => { },
|
|
84
|
+
dispatchEvent: () => { },
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
85
88
|
// Mock Simple-related modules to prevent import errors
|
|
86
89
|
import { vi } from 'vitest';
|
|
87
90
|
vi.mock('@datalayer/jupyter-react', () => ({
|