@datalayer/agent-runtimes 1.5.43 → 1.5.45

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.
Files changed (91) hide show
  1. package/dist-embed/chunks/1f30a-DxwAt2Ba.js +1 -0
  2. package/dist-embed/chunks/1f310-AqUM9a8q.js +1 -0
  3. package/dist-embed/chunks/1f324-8MHLOhTj.js +1 -0
  4. package/dist-embed/chunks/1f326-YSBr3aXI.js +1 -0
  5. package/dist-embed/chunks/1f327-Ie-nQBNO.js +1 -0
  6. package/dist-embed/chunks/1f33e-Cfl2WM63.js +1 -0
  7. package/dist-embed/chunks/1f372-3XLDlXkE.js +1 -0
  8. package/dist-embed/chunks/1f373-BPUJt2bp.js +1 -0
  9. package/dist-embed/chunks/1f39b-CSNtbAXr.js +1 -0
  10. package/dist-embed/chunks/1f3ab-BUCNQHz1.js +1 -0
  11. package/dist-embed/chunks/1f41b-Dre1z0gP.js +1 -0
  12. package/dist-embed/chunks/1f441-D8yah4C4.js +1 -0
  13. package/dist-embed/chunks/1f465-CPle6Hjq.js +1 -0
  14. package/dist-embed/chunks/1f4b9-aacFyckt.js +1 -0
  15. package/dist-embed/chunks/1f4be-DPAcAU4R.js +1 -0
  16. package/dist-embed/chunks/1f4c1-Ch6aJ0cj.js +1 -0
  17. package/dist-embed/chunks/1f4c2-Dx7BKmLO.js +1 -0
  18. package/dist-embed/chunks/1f4c4-CNvjvC5k.js +1 -0
  19. package/dist-embed/chunks/1f4d3-GTxRgjv7.js +1 -0
  20. package/dist-embed/chunks/1f4d6-COOZY-Q_.js +1 -0
  21. package/dist-embed/chunks/1f4dc-BhoW4cjD.js +1 -0
  22. package/dist-embed/chunks/1f4e1-DezU8ow-.js +1 -0
  23. package/dist-embed/chunks/1f4e2-DsStZsI-.js +1 -0
  24. package/dist-embed/chunks/1f4e7-BBi7c_zx.js +1 -0
  25. package/dist-embed/chunks/1f500-D9xRo-IT.js +1 -0
  26. package/dist-embed/chunks/1f501-CzKeDPcw.js +1 -0
  27. package/dist-embed/chunks/1f504-BYIHMoDP.js +1 -0
  28. package/dist-embed/chunks/1f52d-2QxCLKhU.js +1 -0
  29. package/dist-embed/chunks/1f552-4d0Y0kn7.js +1 -0
  30. package/dist-embed/chunks/1f58b-Cp8F_HBg.js +1 -0
  31. package/dist-embed/chunks/1f5b2-D6BUULuh.js +1 -0
  32. package/dist-embed/chunks/1f5c3-DjkgJLaz.js +1 -0
  33. package/dist-embed/chunks/1f5d1-C4Kpe4HL.js +1 -0
  34. package/dist-embed/chunks/1f5dc-DyLO7aB9.js +1 -0
  35. package/dist-embed/chunks/1f604-CEekcaYE.js +1 -0
  36. package/dist-embed/chunks/1f917-CyyS9fgf.js +1 -0
  37. package/dist-embed/chunks/1f9d1-200d-2696-D9eSB-0q.js +1 -0
  38. package/dist-embed/chunks/1fa79-DHhIZbWB.js +1 -0
  39. package/dist-embed/chunks/1fa9d-DUI9e77m.js +1 -0
  40. package/dist-embed/chunks/1fa9f-10ALsMaD.js +1 -0
  41. package/dist-embed/chunks/23f3-ASjqFsbT.js +1 -0
  42. package/dist-embed/chunks/2611-BnnmLm-K.js +1 -0
  43. package/dist-embed/chunks/2702-CfLyp35r.js +1 -0
  44. package/dist-embed/chunks/270f-CLwo30mk.js +1 -0
  45. package/dist-embed/chunks/2728-Qg6be-6p.js +1 -0
  46. package/dist-embed/chunks/2b1c-Uc1aRwAf.js +1 -0
  47. package/dist-embed/chunks/{ChatView-kCmBbnDa.js → ChatView-Dy-IgGFL.js} +1 -1
  48. package/dist-embed/datalayer-app-main.js +111 -106
  49. package/lib/apps/plugins/chat/ChatView.js +2 -2
  50. package/lib/chat/ChatFloating.js +1 -1
  51. package/lib/chat/ChatInline.js +43 -22
  52. package/lib/chat/base/ChatBase.js +66 -10
  53. package/lib/chat/messages/ChatMessageList.d.ts +7 -1
  54. package/lib/chat/messages/ChatMessageList.js +8 -2
  55. package/lib/components/agents/AgentSummary.d.ts +2 -24
  56. package/lib/components/agents/AgentSummary.js +3 -19
  57. package/lib/hooks/useChat.js +110 -144
  58. package/lib/portable/activity.d.ts +78 -0
  59. package/lib/portable/activity.js +129 -0
  60. package/lib/portable/fixtures.d.ts +7 -0
  61. package/lib/portable/fixtures.js +50 -0
  62. package/lib/portable/index.d.ts +4 -1
  63. package/lib/portable/index.js +7 -1
  64. package/lib/portable/lifecycle.d.ts +77 -0
  65. package/lib/portable/lifecycle.js +135 -0
  66. package/lib/portable/runtimes.d.ts +68 -0
  67. package/lib/portable/runtimes.js +69 -0
  68. package/lib/protocols/VercelAIAdapter.js +11 -0
  69. package/lib/specs/voices.js +62 -0
  70. package/lib/types/chat.d.ts +10 -0
  71. package/lib/voice/ComposerVoice.js +12 -3
  72. package/lib/voice/VoiceMenu.d.ts +2 -1
  73. package/lib/voice/VoiceMenu.js +2 -2
  74. package/lib/voice/captions.d.ts +31 -0
  75. package/lib/voice/captions.js +156 -0
  76. package/lib/voice/chatVoice.d.ts +5 -2
  77. package/lib/voice/chatVoice.js +10 -4
  78. package/lib/voice/choices.d.ts +2 -0
  79. package/lib/voice/choices.js +6 -0
  80. package/lib/voice/handsFree.d.ts +12 -0
  81. package/lib/voice/handsFree.js +18 -0
  82. package/lib/voice/index.d.ts +1 -0
  83. package/lib/voice/index.js +1 -0
  84. package/lib/voice/sayAloud.d.ts +25 -0
  85. package/lib/voice/sayAloud.js +34 -0
  86. package/lib/voice/serverHearing.d.ts +63 -2
  87. package/lib/voice/serverHearing.js +311 -7
  88. package/lib/voice/settings.d.ts +12 -0
  89. package/lib/voice/settings.js +9 -0
  90. package/package.json +1 -1
  91. package/scripts/voice/measure.py +77 -17
@@ -27,7 +27,7 @@ export { conversationEventsOf, sanitizeAssistantContent, snapshotOfMessages, } f
27
27
  export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf, } from './presence';
28
28
  // One contract for every renderer of a conversation's components.
29
29
  export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, } from './views';
30
- export { conversationFixture } from './fixtures';
30
+ export { activityFixture, conversationFixture } from './fixtures';
31
31
  // The protocol displays a run's tool results carry (MOBILE M-037).
32
32
  export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor,
33
33
  // A coding agent's diff and terminal (SHIELD §4.3, SH-16).
@@ -42,5 +42,11 @@ export { SIGN_IN_POLL_MS, SIGN_IN_STATUS_PATH, signInStandsOf, signInStatusClien
42
42
  export { emptyFields, fieldProblems, MAX_VALUE_LENGTH, PROTECTED_INPUT_WORDS, protectedInputClient, protectedRequestOf, ProtectedRequestGone, sentWords, submitBody, submitWords, ticketComplete, ticketOfPage, } from './protectedInput';
43
43
  // How a streamed turn went, measured without its words (MOBILE M-036).
44
44
  export { failureOf, otlpMetricsOf, RunMeter, } from './runMeasures';
45
+ // What an application is doing, and its record's entries (LOOP R-15, MOBILE M-015b).
46
+ export { ACTIVITY_WORDS, activityOf, entryOf, entrySaid, mergeActivityPages, runEndOf, scheduledSays, sessionSentence, wakesOn, } from './activity';
47
+ // A deployment's pause, resume and reset, and its recorded events (LOOP R-17, MOBILE M-041b).
48
+ export { deploymentEventLine, deploymentEventOf, notDeletedOf, resetBody, resetCountsOf, resetLines, stateChangeBody, stoppedWorkLine, stoppedWorkOf, } from './lifecycle';
49
+ // A person's agent runtimes, and an agent's summary (MOBILE M-014b).
50
+ export { agentRuntimesOf, agentSummaryOfRuntime, CREATING_STATUSES, isAgentRuntime, isCreatingAgent, runtimeOf, } from './runtimes';
45
51
  // The runtime's routes a native client drives.
46
52
  export { agentRunClient, agentRuntimesUrlOf, appSessionsClient, RuntimeRefusal, } from './sessions';
@@ -0,0 +1,77 @@
1
+ /**
2
+ * A deployment's lifecycle as its owner drives it (LOOP R-17, D-04): pause
3
+ * and resume one exact version, reset what it holds, and the events recorded
4
+ * with who did each. The requests' bodies, the answers read and the words
5
+ * said — the same in the Studio and Datalayer Mobile (MOBILE M-041b). Pure:
6
+ * each client sends them with its own authentication.
7
+ *
8
+ * @module portable/lifecycle
9
+ */
10
+ /** Whether a deployment answers now. */
11
+ export type DeploymentState = 'live' | 'paused';
12
+ /** Pause or resume, for one exact version: `PATCH /apps/deployments/{uid}`'s body. */
13
+ export declare const stateChangeBody: (version: number, state: DeploymentState) => {
14
+ version: number;
15
+ state: DeploymentState;
16
+ };
17
+ /** What pausing stopped, and what it could not, with why. */
18
+ export type StoppedWork = {
19
+ stopped: {
20
+ what: string;
21
+ runtime: string;
22
+ }[];
23
+ notStopped: {
24
+ what: string;
25
+ runtime: string;
26
+ why: string;
27
+ }[];
28
+ };
29
+ export declare function stoppedWorkOf(raw: Record<string, unknown>): StoppedWork;
30
+ /** What pausing did, in a sentence. */
31
+ export declare function stoppedWorkLine(work: StoppedWork): string;
32
+ /** What a Reset deletes, counted for its confirmation. */
33
+ export type ResetCounts = {
34
+ conversations: number;
35
+ entries: number;
36
+ schedules: number;
37
+ /**
38
+ * Why its memories stay: they are its application's, not the
39
+ * deployment's (LOOP R-18) — forgotten under *What it remembers*.
40
+ */
41
+ memoriesNote: string;
42
+ };
43
+ export declare function resetCountsOf(raw: Record<string, unknown>): ResetCounts;
44
+ /** The Reset confirmed against the counts the person was shown: `POST …/reset`'s body. */
45
+ export declare const resetBody: (counts: Pick<ResetCounts, "conversations" | "schedules">) => {
46
+ conversations: number;
47
+ schedules: number;
48
+ };
49
+ /** What a Reset could not delete, with why: `POST …/reset`'s answer. */
50
+ export declare function notDeletedOf(body: Record<string, unknown>): {
51
+ what: string;
52
+ why: string;
53
+ }[];
54
+ /**
55
+ * What goes, as the confirmation lists it — "12 conversations, 2
56
+ * schedules" — and what does not, said apart.
57
+ */
58
+ export declare function resetLines(counts: ResetCounts): {
59
+ goes: string;
60
+ stays: string[];
61
+ };
62
+ /** What happened to a deployment, recorded with who did it (D-04, R-17). */
63
+ export type DeploymentEventKind = 'promoted' | 'rolled_back' | 'paused' | 'resumed' | 'reset';
64
+ /** Who promoted, rolled back, paused, resumed or reset a deployment, and when. */
65
+ export type DeploymentEvent = {
66
+ uid: string;
67
+ kind: DeploymentEventKind;
68
+ actor: string;
69
+ at: string;
70
+ details: Record<string, unknown>;
71
+ };
72
+ export declare function deploymentEventOf(raw: Record<string, unknown>): DeploymentEvent;
73
+ /**
74
+ * One event, in a line: "Paused by eric, 4 Oct 2026, 10:12", "Rolled back
75
+ * from version 3 to version 2 by eric, …".
76
+ */
77
+ export declare function deploymentEventLine(event: DeploymentEvent, locale?: string): string;
@@ -0,0 +1,135 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /** Pause or resume, for one exact version: `PATCH /apps/deployments/{uid}`'s body. */
6
+ export const stateChangeBody = (version, state) => {
7
+ if (!Number.isInteger(version) || version < 1) {
8
+ throw new Error(`A deployment's state is changed for one exact version: ${String(version)} is none.`);
9
+ }
10
+ return { version, state };
11
+ };
12
+ const listOf = (raw) => Array.isArray(raw)
13
+ ? raw.filter(item => item && typeof item === 'object')
14
+ : [];
15
+ const said = (value) => typeof value === 'string' ? value : value == null ? '' : String(value);
16
+ const count = (value) => {
17
+ const n = Number(value);
18
+ return Number.isFinite(n) && n > 0 ? Math.trunc(n) : 0;
19
+ };
20
+ const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
21
+ export function stoppedWorkOf(raw) {
22
+ return {
23
+ stopped: listOf(raw.stopped).map(item => ({
24
+ what: said(item.what),
25
+ runtime: said(item.runtime),
26
+ })),
27
+ notStopped: listOf(raw.not_stopped).map(item => ({
28
+ what: said(item.what),
29
+ runtime: said(item.runtime),
30
+ why: said(item.why),
31
+ })),
32
+ };
33
+ }
34
+ /** What pausing did, in a sentence. */
35
+ export function stoppedWorkLine(work) {
36
+ const stopped = work.stopped.length;
37
+ const first = stopped === 0
38
+ ? 'Paused: nothing was running.'
39
+ : `Paused: ${plural(stopped, 'session')} in progress stopped.`;
40
+ if (!work.notStopped.length) {
41
+ return first;
42
+ }
43
+ const missed = work.notStopped
44
+ .map(item => `${item.what}${item.runtime ? ` (${item.runtime})` : ''}: ${item.why}`)
45
+ .join(' ');
46
+ return `${first} Not stopped — ${missed}`;
47
+ }
48
+ export function resetCountsOf(raw) {
49
+ return {
50
+ conversations: count(raw.conversations),
51
+ entries: count(raw.entries),
52
+ schedules: count(raw.schedules),
53
+ memoriesNote: typeof raw.memories_note === 'string' ? raw.memories_note : '',
54
+ };
55
+ }
56
+ /** The Reset confirmed against the counts the person was shown: `POST …/reset`'s body. */
57
+ export const resetBody = (counts) => ({
58
+ conversations: counts.conversations,
59
+ schedules: counts.schedules,
60
+ });
61
+ /** What a Reset could not delete, with why: `POST …/reset`'s answer. */
62
+ export function notDeletedOf(body) {
63
+ return listOf(body.not_deleted).map(item => ({
64
+ what: said(item.what),
65
+ why: said(item.why),
66
+ }));
67
+ }
68
+ /**
69
+ * What goes, as the confirmation lists it — "12 conversations, 2
70
+ * schedules" — and what does not, said apart.
71
+ */
72
+ export function resetLines(counts) {
73
+ return {
74
+ goes: [
75
+ plural(counts.conversations, 'conversation'),
76
+ plural(counts.schedules, 'schedule'),
77
+ ].join(', '),
78
+ stays: [
79
+ 'The deployment, its address and the version it runs stay.',
80
+ ...(counts.memoriesNote ? [counts.memoriesNote] : []),
81
+ ],
82
+ };
83
+ }
84
+ const EVENT_KINDS = [
85
+ 'promoted',
86
+ 'rolled_back',
87
+ 'paused',
88
+ 'resumed',
89
+ 'reset',
90
+ ];
91
+ export function deploymentEventOf(raw) {
92
+ if (!EVENT_KINDS.includes(raw.kind)) {
93
+ throw new Error(`A deployment's event of no known kind: ${String(raw.kind)}.`);
94
+ }
95
+ return {
96
+ uid: said(raw.uid),
97
+ kind: raw.kind,
98
+ actor: said(raw.actor_handle || raw.actor_uid || ''),
99
+ at: typeof raw.at === 'string' ? raw.at : '',
100
+ details: raw.details && typeof raw.details === 'object'
101
+ ? raw.details
102
+ : {},
103
+ };
104
+ }
105
+ /**
106
+ * One event, in a line: "Paused by eric, 4 Oct 2026, 10:12", "Rolled back
107
+ * from version 3 to version 2 by eric, …".
108
+ */
109
+ export function deploymentEventLine(event, locale) {
110
+ const from = count(event.details.from);
111
+ const to = count(event.details.to);
112
+ const moved = from && to ? ` from version ${from} to version ${to}` : '';
113
+ const verb = {
114
+ promoted: `Promoted${moved}`,
115
+ rolled_back: `Rolled back${moved}`,
116
+ paused: 'Paused',
117
+ resumed: 'Resumed',
118
+ reset: 'Reset',
119
+ }[event.kind];
120
+ const when = event.at
121
+ ? new Date(event.at).toLocaleString(locale, {
122
+ dateStyle: 'medium',
123
+ timeStyle: 'short',
124
+ })
125
+ : '';
126
+ const deleted = event.details.deleted;
127
+ const what = event.kind === 'reset' && deleted
128
+ ? ` — ${plural(count(deleted.conversations), 'conversation')} and ${plural(count(deleted.schedules), 'schedule')} deleted`
129
+ : '';
130
+ // Paused by an owner of its owner's organization (I-09): said as such.
131
+ const forOrganization = event.kind === 'paused' && event.details.by === 'organization_owner'
132
+ ? ', an owner of your organization'
133
+ : '';
134
+ return `${verb}${event.actor ? ` by ${event.actor}${forOrganization}` : ''}${when ? `, ${when}` : ''}${what}`;
135
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * A person's agent runtimes as the runtimes service lists them
3
+ * (`GET /api/runtimes/v1/runtimes`), and an agent's summary and status as
4
+ * every client says them — the web's `AgentSummary` badge and Datalayer
5
+ * Mobile's Agents (MOBILE M-014b). A raw runtime carries no application
6
+ * capabilities: no Rules, Protocols or Activity are read from it.
7
+ *
8
+ * @module portable/runtimes
9
+ */
10
+ /** One agent runtime, as a client lists it. */
11
+ export type AgentRuntime = {
12
+ uid: string;
13
+ runtimeName: string;
14
+ givenName: string;
15
+ environment: string;
16
+ /** The agentspec it runs, when the runtime says. */
17
+ agentSpecId: string;
18
+ type: string;
19
+ state: string;
20
+ startedAt: string;
21
+ expiredAt: string;
22
+ /** Its Jupyter address, from which its agents' is read (`agentRuntimesUrlOf`). */
23
+ ingress: string;
24
+ };
25
+ /** A runtime of the runtimes service's answer. */
26
+ export declare function runtimeOf(raw: Record<string, unknown>): AgentRuntime;
27
+ /** The runtimes service's list, agent runtimes only: notebooks and terminals belong to the web workspace. */
28
+ export declare function agentRuntimesOf(body: Record<string, unknown>): AgentRuntime[];
29
+ /** An agent runtime, not a notebook's or a terminal's. */
30
+ export declare const isAgentRuntime: (runtime: AgentRuntime) => boolean;
31
+ /** What a summary of an agent says of it. */
32
+ export interface AgentSummaryData {
33
+ agentName: string;
34
+ location: string;
35
+ specId?: string;
36
+ /**
37
+ * The framework turning this agent's loop, as its spec declares it —
38
+ * `pydantic-ai` server-side, `vercel-ai` in the page.
39
+ */
40
+ harness?: string;
41
+ /**
42
+ * The runtime variant the host chose: `<location>-<harness>`.
43
+ *
44
+ * Shown next to the harness rather than instead of it, because they answer
45
+ * different questions and can disagree: the harness is what the spec asked
46
+ * for, the variant is what it actually got.
47
+ */
48
+ variant?: string;
49
+ status?: string;
50
+ baseUrl?: string;
51
+ sandboxBaseUrl?: string;
52
+ agentId?: string;
53
+ isReady?: boolean;
54
+ error?: string;
55
+ }
56
+ /**
57
+ * Statuses that represent an agent actively being created/started. The spinner
58
+ * only shows for these; app-navigation states ("selected", "switching") and
59
+ * settled states ("ready", "running", ...) never spin.
60
+ */
61
+ export declare const CREATING_STATUSES: ReadonlySet<string>;
62
+ /**
63
+ * An agent genuinely being created: it is not ready, has no id yet, and its
64
+ * status is one of creation or startup.
65
+ */
66
+ export declare function isCreatingAgent(summary: AgentSummaryData): boolean;
67
+ /** The summary of an agent the runtimes service lists: named as given, at Datalayer. */
68
+ export declare function agentSummaryOfRuntime(runtime: AgentRuntime): AgentSummaryData;
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
6
+ const text = (value) => typeof value === 'string' ? value : '';
7
+ /** A runtime of the runtimes service's answer. */
8
+ export function runtimeOf(raw) {
9
+ const environment = isRecord(raw.environment) ? raw.environment : {};
10
+ return {
11
+ uid: text(raw.uid),
12
+ runtimeName: text(raw.runtime_name) || text(raw.pod_name),
13
+ givenName: text(raw.given_name),
14
+ environment: text(environment.title) ||
15
+ text(environment.name) ||
16
+ text(raw.environment_title) ||
17
+ text(raw.environment_name),
18
+ agentSpecId: text(raw.agent_spec_id) || text(raw.attached_agent_spec_id_s),
19
+ type: text(raw.type),
20
+ state: text(raw.state) || text(raw.status) || 'running',
21
+ startedAt: text(raw.started_at),
22
+ expiredAt: text(raw.expired_at),
23
+ ingress: text(raw.ingress),
24
+ };
25
+ }
26
+ /** The runtimes service's list, agent runtimes only: notebooks and terminals belong to the web workspace. */
27
+ export function agentRuntimesOf(body) {
28
+ const listed = Array.isArray(body.runtimes) ? body.runtimes : [];
29
+ return listed
30
+ .filter(isRecord)
31
+ .map(runtimeOf)
32
+ .filter(runtime => runtime.runtimeName && isAgentRuntime(runtime));
33
+ }
34
+ /** An agent runtime, not a notebook's or a terminal's. */
35
+ export const isAgentRuntime = (runtime) => runtime.type === 'agent' ||
36
+ Boolean(runtime.agentSpecId) ||
37
+ /agent/i.test(runtime.environment);
38
+ /**
39
+ * Statuses that represent an agent actively being created/started. The spinner
40
+ * only shows for these; app-navigation states ("selected", "switching") and
41
+ * settled states ("ready", "running", ...) never spin.
42
+ */
43
+ export const CREATING_STATUSES = new Set([
44
+ 'creating',
45
+ 'starting',
46
+ 'launching',
47
+ 'connecting',
48
+ 'pending',
49
+ 'resuming',
50
+ ]);
51
+ /**
52
+ * An agent genuinely being created: it is not ready, has no id yet, and its
53
+ * status is one of creation or startup.
54
+ */
55
+ export function isCreatingAgent(summary) {
56
+ return (!summary.isReady &&
57
+ !summary.agentId &&
58
+ CREATING_STATUSES.has((summary.status || '').toLowerCase()));
59
+ }
60
+ /** The summary of an agent the runtimes service lists: named as given, at Datalayer. */
61
+ export function agentSummaryOfRuntime(runtime) {
62
+ return {
63
+ agentName: runtime.givenName || runtime.runtimeName,
64
+ location: 'datalayer',
65
+ specId: runtime.agentSpecId || undefined,
66
+ status: runtime.state,
67
+ isReady: !CREATING_STATUSES.has(runtime.state.toLowerCase()),
68
+ };
69
+ }
@@ -627,6 +627,17 @@ export class VercelAIAdapter extends BaseProtocolAdapter {
627
627
  });
628
628
  }
629
629
  }
630
+ else if (typeof event.type === 'string' &&
631
+ event.type.startsWith('data-')) {
632
+ // What the server says besides the conversation's text, as
633
+ // AG-UI's custom events say it: `data-voice.say`, a line the
634
+ // agent says aloud, and `data-voice.settings` (VOICE.md VO-36).
635
+ this.emit({
636
+ type: 'activity',
637
+ activity: { type: event.type, data: event.data },
638
+ timestamp: new Date(),
639
+ });
640
+ }
630
641
  }
631
642
  catch (parseError) {
632
643
  console.error('[VercelAIAdapter] Failed to parse SSE data:', parseError);
@@ -100,6 +100,68 @@ export const SPEECH_MODEL_CATALOGUE = {
100
100
  },
101
101
  ],
102
102
  },
103
+ 'moonshine-base-en-server': {
104
+ id: 'moonshine-base-en-server',
105
+ version: '0.0.1',
106
+ name: 'Moonshine Base (English, server)',
107
+ description: "Speech to text on Datalayer's servers, in English: Useful Sensors' Moonshine Base (61M parameters), the same 8-bit ONNX export the browser runs, run by onnxruntime on the CPU with a greedy decoder. Its encoder is as long as the speech, with no 30-second window: measured beside Whisper small for short spoken turns (decision 18).",
108
+ provider: 'usefulsensors',
109
+ task: 'stt',
110
+ engine: 'onnxruntime',
111
+ dtype: 'q8',
112
+ languages: ['en'],
113
+ where: ['server'],
114
+ streaming: false,
115
+ licence: {
116
+ weights: 'MIT',
117
+ code: 'MIT',
118
+ },
119
+ attribution: '',
120
+ upstream: 'https://huggingface.co/UsefulSensors/moonshine-base',
121
+ revision: 'b1e9b6aae3c3c7298f10c3798393fdf38e8fbbad',
122
+ files: [
123
+ {
124
+ path: 'config.json',
125
+ sha256: 'fab7241d1e9fc6c2370c4c6dfb5da79bb54d67ed9ab6b507ac51d29d2abe01d1',
126
+ size: 922,
127
+ },
128
+ {
129
+ path: 'generation_config.json',
130
+ sha256: 'f9b3f711b57be7def2e50a8942f64f36ee0a55fad5b84ff93a687b6c5bcc1d44',
131
+ size: 147,
132
+ },
133
+ {
134
+ path: 'preprocessor_config.json',
135
+ sha256: 'fa43a7017ef85cd1d0fba0d9aae77c8adb16990ae6f11115631f41ec5d8aa679',
136
+ size: 128,
137
+ },
138
+ {
139
+ path: 'special_tokens_map.json',
140
+ sha256: 'ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356',
141
+ size: 3,
142
+ },
143
+ {
144
+ path: 'tokenizer.json',
145
+ sha256: '7b913404bdd039af4756783218af4440bc07fb7d6d8258d677e34f95b3ec416f',
146
+ size: 3761754,
147
+ },
148
+ {
149
+ path: 'tokenizer_config.json',
150
+ sha256: 'edaee394565d428ea98a663ae7209cdcfeefc5585c42d7a570ff7c986df2cd15',
151
+ size: 135735,
152
+ },
153
+ {
154
+ path: 'onnx/encoder_model_quantized.onnx',
155
+ sha256: '1dd9ab0a7f987113d30affcba5a068d11c8f90fa0223caa3e491ade431ad9751',
156
+ size: 20513063,
157
+ },
158
+ {
159
+ path: 'onnx/decoder_model_merged_quantized.onnx',
160
+ sha256: 'cc9f3cd6698a369c6008b41aa60aa3fb3322e7f03c9bdf19d8e6b7200afca4f3',
161
+ size: 42498870,
162
+ },
163
+ ],
164
+ },
103
165
  'moonshine-base-en': {
104
166
  id: 'moonshine-base-en',
105
167
  version: '0.0.1',
@@ -689,6 +689,11 @@ export interface ChatBaseProps {
689
689
  * Absent while its answers are not heard.
690
690
  */
691
691
  stopSpeaking?: () => void;
692
+ /**
693
+ * Marks the answer the person spoke over *interrupted* (VOICE.md
694
+ * VO-13), for a host whose composer holds the microphone.
695
+ */
696
+ markInterrupted: () => void;
692
697
  } | null) => void;
693
698
  /**
694
699
  * Reports whether the agent's voice is speaking, for a host that draws the
@@ -1013,6 +1018,11 @@ export interface ChatBaseProps {
1013
1018
  voice?: ChatVoice;
1014
1019
  /** The agent is speaking: the composer offers *Stop speaking* (`Esc`). */
1015
1020
  voiceSpeaking?: boolean;
1021
+ /**
1022
+ * The sentence the host's speaker is saying, marked in the conversation
1023
+ * (VOICE.md VO-65); a chat that speaks for itself marks its own.
1024
+ */
1025
+ voiceSentence?: string;
1016
1026
  /** Stops the agent's voice: the composer's *Stop speaking*, and the microphone opening (VO-13). */
1017
1027
  onStopSpeaking?: () => void;
1018
1028
  /**
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useRef } from 'react';
3
3
  import { DeviceHearing } from './hearing';
4
4
  import { HandsFree } from './HandsFree';
5
- import { ServerHearing, hearingPlace } from './serverHearing';
5
+ import { AutoHearing, ServerHearing, hearingPlace } from './serverHearing';
6
6
  import { VoiceInput } from './VoiceInput';
7
7
  import { VoiceMenu } from './VoiceMenu';
8
8
  /** Whether something rides on a run as a spoken message's metadata (VO-27). */
@@ -24,13 +24,20 @@ export function ComposerVoice({ voice, input, setInput, onSpoken, send, disabled
24
24
  return undefined;
25
25
  }
26
26
  const device = new DeviceHearing(voice.modelsUrl, voice.engines);
27
- return place === 'server' && voice.speechUrl
27
+ const server = voice.speechUrl
28
28
  ? new ServerHearing(device, {
29
29
  speechUrl: voice.speechUrl,
30
30
  token: voice.token,
31
31
  detect: voice.detect,
32
32
  })
33
- : device;
33
+ : undefined;
34
+ // `where: auto` (§5): the device when it hears the language and its model
35
+ // loads, the server otherwise — and back to the device when the server
36
+ // refuses (a visitor's day spent).
37
+ if (server && !voice.detect && (voice.where ?? 'auto') === 'auto') {
38
+ return new AutoHearing(device, server, voice.language);
39
+ }
40
+ return place === 'server' && server ? server : device;
34
41
  }, [
35
42
  voice.input,
36
43
  voice.modelsUrl,
@@ -39,6 +46,8 @@ export function ComposerVoice({ voice, input, setInput, onSpoken, send, disabled
39
46
  voice.speechUrl,
40
47
  voice.token,
41
48
  voice.detect,
49
+ voice.where,
50
+ voice.language,
42
51
  ]);
43
52
  // What the box held when the microphone opened: the words heard go after it.
44
53
  const base = useRef('');
@@ -2,7 +2,8 @@
2
2
  * The composer's voice menu (VOICE.md VO-14, VO-21, VO-25, VO-30): beside
3
3
  * the microphone, what a person chooses for themselves — the language they
4
4
  * speak and hear, when answers are heard, the voice, its speed and loudness,
5
- * *Send what I say*, the microphone, and *Keep speech on this device*. Each
5
+ * how long they pause before they are done (VO-69), *Send what I say*, the
6
+ * microphone, and *Keep speech on this device*. Each
6
7
  * choice is kept in the person's settings and heard at once by every chat
7
8
  * on the page; *Use the application's* forgets them all.
8
9
  *
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useState } from 'react';
3
3
  import { ActionList, ActionMenu, IconButton } from '@primer/react';
4
4
  import { GearIcon } from '@primer/octicons-react';
5
- import { OUTPUT_CHOICES, RATE_CHOICES, VOLUME_CHOICES, catalogueVoiceSpeaks, voiceLanguages, voicesSpeaking, } from './choices';
5
+ import { OUTPUT_CHOICES, PAUSE_CHOICES, RATE_CHOICES, VOLUME_CHOICES, catalogueVoiceSpeaks, voiceLanguages, voicesSpeaking, } from './choices';
6
6
  import { withVoiceSetting } from './settings';
7
7
  import { useVoiceSettings } from './useVoiceSettings';
8
8
  /** The microphones the browser lists; named only once one was allowed. */
@@ -28,6 +28,6 @@ export function VoiceMenu({ voice, answers, disabled = false, }) {
28
28
  void microphones().then(setDevices);
29
29
  }
30
30
  }, children: [_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { icon: GearIcon, size: "small", variant: "invisible", "aria-label": "Voice settings", "data-voice-menu": "", disabled: disabled }) }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Language" }), voiceLanguages().map(language => (_jsx(ActionList.Item, { selected: language.value === voice.language, onSelect: () => choose('language', language.value), "data-voice-language": language.value, children: language.label }, language.value)))] }), answers && !onDevice ? (_jsxs(_Fragment, { children: [_jsx(ActionList.Divider, {}), _jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Answers heard" }), OUTPUT_CHOICES.map(output => (_jsx(ActionList.Item, { selected: output.value === voice.output, onSelect: () => choose('output', output.value), "data-voice-output": output.value, children: output.label }, output.value)))] }), voices.length > 0 ? (_jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Voice" }), voices.map(spoken => (_jsx(ActionList.Item, { selected: spoken.id === voice.voice, onSelect: () => choose('voice', spoken.id), "data-voice-voice": spoken.id, children: spoken.name }, spoken.id)))] })) : null, _jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Speed" }), RATE_CHOICES.map(rate => (_jsx(ActionList.Item, { selected: rate.value === (voice.rate ?? 1), onSelect: () => choose('rate', rate.value), "data-voice-rate": rate.value, children: rate.label }, rate.value)))] }), _jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Loudness" }), VOLUME_CHOICES.map(volume => (_jsx(ActionList.Item, { selected: volume.value === (voice.volume ?? 1), onSelect: () => choose('volume', volume.value), "data-voice-volume": volume.value, children: volume.label }, volume.value)))] })] })) : null, named.length > 1 ? (_jsxs(_Fragment, { children: [_jsx(ActionList.Divider, {}), _jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Microphone" }), named.map(device => (_jsx(ActionList.Item, { selected: device.deviceId ===
31
- (voice.microphoneId ?? named[0].deviceId), onSelect: () => choose('microphoneId', device.deviceId), "data-voice-microphone": device.deviceId, children: device.label }, device.deviceId)))] })] })) : null, _jsx(ActionList.Divider, {}), _jsxs(ActionList.Group, { selectionVariant: "multiple", children: [_jsxs(ActionList.Item, { selected: Boolean(voice.sendWhatISay), onSelect: () => choose('sendWhatISay', !voice.sendWhatISay), "data-voice-send-what-i-say": "", children: ["Send what I say", _jsx(ActionList.Description, { variant: "block", children: "Sent as soon as it is heard, not left in the box." })] }), voice.speechUrl && !onDevice ? (_jsxs(ActionList.Item, { selected: detect, onSelect: () => choose('detectLanguage', !detect), "data-voice-detect": "", children: ["Detect the language I speak", _jsx(ActionList.Description, { variant: "block", children: "Heard on Datalayer\u2019s servers by Whisper, which finds the language; nothing is kept there." })] })) : null, _jsxs(ActionList.Item, { selected: onDevice, onSelect: () => choose('onDeviceOnly', !onDevice), "data-voice-on-device": "", children: ["Keep speech on this device", _jsx(ActionList.Description, { variant: "block", children: "Nothing is sent to Datalayer\u2019s speech service: you are heard in the browser, answers are not spoken." })] })] }), Object.keys(settings).length > 0 ? (_jsxs(_Fragment, { children: [_jsx(ActionList.Divider, {}), _jsx(ActionList.Item, { onSelect: () => keep({}), "data-voice-settings-reset": "", children: "Use the application\u2019s" })] })) : null] }) })] }));
31
+ (voice.microphoneId ?? named[0].deviceId), onSelect: () => choose('microphoneId', device.deviceId), "data-voice-microphone": device.deviceId, children: device.label }, device.deviceId)))] })] })) : null, _jsx(ActionList.Divider, {}), _jsxs(ActionList.Group, { selectionVariant: "single", children: [_jsx(ActionList.GroupHeading, { children: "Pauses before I am done" }), PAUSE_CHOICES.map(pause => (_jsx(ActionList.Item, { selected: pause.value === (settings.pause ?? 1), onSelect: () => choose('pause', pause.value === 1 ? undefined : pause.value), "data-voice-pause": pause.value, children: pause.label }, pause.value)))] }), _jsx(ActionList.Divider, {}), _jsxs(ActionList.Group, { selectionVariant: "multiple", children: [_jsxs(ActionList.Item, { selected: Boolean(voice.sendWhatISay), onSelect: () => choose('sendWhatISay', !voice.sendWhatISay), "data-voice-send-what-i-say": "", children: ["Send what I say", _jsx(ActionList.Description, { variant: "block", children: "Sent as soon as it is heard, not left in the box." })] }), voice.speechUrl && !onDevice ? (_jsxs(ActionList.Item, { selected: detect, onSelect: () => choose('detectLanguage', !detect), "data-voice-detect": "", children: ["Detect the language I speak", _jsx(ActionList.Description, { variant: "block", children: "Heard on Datalayer\u2019s servers by Whisper, which finds the language; nothing is kept there." })] })) : null, _jsxs(ActionList.Item, { selected: onDevice, onSelect: () => choose('onDeviceOnly', !onDevice), "data-voice-on-device": "", children: ["Keep speech on this device", _jsx(ActionList.Description, { variant: "block", children: "Nothing is sent to Datalayer\u2019s speech service: you are heard in the browser, answers are not spoken." })] })] }), Object.keys(settings).length > 0 ? (_jsxs(_Fragment, { children: [_jsx(ActionList.Divider, {}), _jsx(ActionList.Item, { onSelect: () => keep({}), "data-voice-settings-reset": "", children: "Use the application\u2019s" })] })) : null] }) })] }));
32
32
  }
33
33
  export default VoiceMenu;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Captions always (VOICE.md VO-65): the sentence being said is marked in the
3
+ * open conversation, not by colour alone. The answer that holds it is marked
4
+ * `aria-current` and `data-voice-saying` with a bar beside it; the sentence
5
+ * itself is underlined in place through the CSS Custom Highlight API, which
6
+ * draws over the text without touching the DOM React owns. A browser without
7
+ * that API keeps the answer's mark.
8
+ *
9
+ * @module voice/captions
10
+ */
11
+ import { type RefObject } from 'react';
12
+ /** The name the sentence's highlight is registered under. */
13
+ export declare const SAID_HIGHLIGHT = "voice-said";
14
+ /**
15
+ * The id of the answer a sentence is being said from: the newest answer of
16
+ * the agent whose words hold it, or none.
17
+ */
18
+ export declare function saidMessageId(items: readonly unknown[], sentence: string | undefined): string | undefined;
19
+ /**
20
+ * Where a sentence is in what an element shows, as a `Range` over its text
21
+ * — across the bold, the links and the line breaks it is drawn with — or
22
+ * none when the element does not show it as said (a code block said as *a
23
+ * code block*).
24
+ */
25
+ export declare function sentenceRange(root: Node, sentence: string): Range | undefined;
26
+ /**
27
+ * Underlines `sentence` in the answer marked `data-voice-saying` under
28
+ * `root`, for as long as it is said; the mark goes when it ends. Drawn
29
+ * again whenever `version` changes (the answer still being written).
30
+ */
31
+ export declare function useSaidSentence(root: RefObject<HTMLElement | null>, sentence: string | undefined, version: unknown): void;