@datalayer/agent-runtimes 1.5.87 → 1.5.89

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 (50) hide show
  1. package/dist-embed/datalayer-app-main.js +22 -9
  2. package/lib/apps/apps/checks.d.ts +1 -1
  3. package/lib/apps/apps/checks.js +1 -1
  4. package/lib/apps/apps/computer.d.ts +1 -1
  5. package/lib/apps/apps/computer.js +1 -1
  6. package/lib/apps/apps/pluginPair.d.ts +1 -1
  7. package/lib/apps/apps/rules.d.ts +1 -1
  8. package/lib/apps/apps/rules.js +2 -2
  9. package/lib/apps/apps/visitorToken.d.ts +1 -1
  10. package/lib/apps/apps/visitorToken.js +1 -1
  11. package/lib/apps/core/appFiles.js +1 -1
  12. package/lib/apps/plugins/a2ui-surface/toolResult.d.ts +1 -1
  13. package/lib/apps/plugins/a2ui-surface/toolResult.js +1 -1
  14. package/lib/apps/plugins/agent-outputs/sheets.d.ts +76 -0
  15. package/lib/apps/plugins/agent-outputs/sheets.js +230 -0
  16. package/lib/apps/plugins/app-page/appPageModel.d.ts +1 -1
  17. package/lib/apps/plugins/app-page/appPageModel.js +1 -1
  18. package/lib/chat/messages/ChatMarkdown.d.ts +3 -1
  19. package/lib/chat/messages/ChatMarkdown.js +4 -1
  20. package/lib/chat/messages/codeBlocks.d.ts +50 -0
  21. package/lib/chat/messages/codeBlocks.js +81 -0
  22. package/lib/examples/AgentOutputsExample.d.ts +7 -0
  23. package/lib/examples/AgentOutputsExample.js +47 -7
  24. package/lib/examples/utils/agentOutputsPlugins.d.ts +2 -0
  25. package/lib/examples/utils/agentOutputsPlugins.js +27 -0
  26. package/lib/portable/hosts/client.d.ts +11 -1
  27. package/lib/portable/hosts/client.js +12 -6
  28. package/lib/portable/hosts/contracts.json +9 -3
  29. package/lib/portable/hosts/index.d.ts +1 -1
  30. package/lib/portable/records.d.ts +1 -1
  31. package/lib/portable/records.js +1 -1
  32. package/lib/portable/semantic-events.json +3 -3
  33. package/lib/portable/semanticOfAgUi.d.ts +1 -1
  34. package/lib/runtimes/variants.d.ts +1 -1
  35. package/lib/runtimes/variants.js +1 -1
  36. package/lib/specs/agents/agents.d.ts +1 -0
  37. package/lib/specs/agents/agents.js +79 -8
  38. package/lib/specs/apps.js +32 -32
  39. package/lib/specs/frontendTools.d.ts +1 -0
  40. package/lib/specs/frontendTools.js +18 -0
  41. package/lib/specs/harnesses.d.ts +1 -0
  42. package/lib/specs/harnesses.js +62 -19
  43. package/lib/specs/index.d.ts +1 -1
  44. package/lib/specs/index.js +1 -1
  45. package/lib/specs/mcpServers.js +20 -50
  46. package/lib/types/mcp.d.ts +4 -2
  47. package/package.json +2 -1
  48. package/scripts/codegen/generate_apps.py +1 -1
  49. package/scripts/codegen/generate_mcp_servers.py +30 -4
  50. package/scripts/validate_pap_examples.py +1 -1
@@ -19,6 +19,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  * We subscribe to the chat store, detect the output type of the latest
20
20
  * assistant message, auto-switch the sidebar tab, and render the payload
21
21
  * inline in the sidebar.
22
+ *
23
+ * A spreadsheet (a ```csv block) is also drawn in the chat message itself, by
24
+ * the `AgentOutputsSheetsPlugin` renderer the chat reads from its
25
+ * `ChatCodeBlock` point; the message's grid and the Sheet tab's are one
26
+ * workbook. The Cells agent tools (`AgentOutputsSheetToolsPlugin`, the
27
+ * `cells` bundle the spec's `frontend_tools` names) act on the last one, so
28
+ * "Chart the spreadsheet" inserts a chart that appears on both grids.
22
29
  */
23
30
  /// <reference types="vite/client" />
24
31
  import { useEffect, useState, useRef, useMemo } from 'react';
@@ -26,15 +33,16 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
26
33
  import { Text, Button, Heading, Label } from '@primer/react';
27
34
  import { TableIcon, FileIcon, CodeIcon, GraphIcon, DownloadIcon, } from '@primer/octicons-react';
28
35
  import { Box } from '@datalayer/primer-addons';
36
+ import { CellsProvider, Spreadsheet } from '@datalayer/cells';
29
37
  import { AuthRequiredView } from './components/index.js';
30
38
  import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel/index.js';
31
39
  import { ThemedProvider } from './utils/themedProvider.js';
32
40
  import { uniqueAgentId } from './utils/agentId.js';
33
41
  import { LoopEmbed } from '../apps/index.js';
34
- import { AgentOutputsPlugin } from '../apps/plugins/agent-outputs/index.js';
42
+ import { downloadSheet, parseSheetBlock, useFitHeight, useSheetModel, } from '../apps/plugins/agent-outputs/sheets.js';
43
+ import { AGENT_OUTPUTS_LOOP_PLUGINS } from './utils/agentOutputsPlugins.js';
35
44
  import { useChatStore } from '../stores/chatStore.js';
36
45
  import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl.js';
37
- const LOOP_PLUGINS_AGENTOUT = [AgentOutputsPlugin];
38
46
  const queryClient = new QueryClient();
39
47
  // ─── Constants ─────────────────────────────────────────────────────────────
40
48
  const AGENT_NAME = 'outputs-example-agent';
@@ -57,8 +65,6 @@ const expandCompactMarkdownTable = (text) => {
57
65
  return text.replace(/\|\|/g, '|\n|');
58
66
  };
59
67
  const EXT_LIKE_INFOS = new Set([
60
- 'csv',
61
- 'tsv',
62
68
  'txt',
63
69
  'md',
64
70
  'markdown',
@@ -119,6 +125,20 @@ const detectOutput = (m) => {
119
125
  }
120
126
  // File: info string is a known extension OR first line declares a filename.
121
127
  const filenameMatch = firstLine.match(/^#\s*filename:\s*([\w.-]+)/i);
128
+ // Spreadsheet: a CSV or TSV, by its fence (`csv`, `tsv`, `spreadsheet`,
129
+ // `cells`) or a `# filename:` ending in .csv or .tsv — read as the chat's
130
+ // renderer reads it, so both grids show the same workbook.
131
+ const sheet = parseSheetBlock(info, rawBody);
132
+ if (sheet) {
133
+ return {
134
+ tab: 'sheet',
135
+ payload: sheet.source,
136
+ filename: sheet.name,
137
+ extension: sheet.extension,
138
+ sheet,
139
+ messageId: m.id,
140
+ };
141
+ }
122
142
  if (filenameMatch || EXT_LIKE_INFOS.has(info)) {
123
143
  const filename = filenameMatch?.[1] ?? (info ? `output.${info}` : 'output.txt');
124
144
  return {
@@ -257,6 +277,24 @@ const ChartView = ({ source }) => {
257
277
  }
258
278
  return (_jsx(ReactECharts, { option: option, style: { height: 280, width: '100%' } }));
259
279
  };
280
+ // ─── Spreadsheet renderer (Datalayer Cells) ───────────────────────────────
281
+ /**
282
+ * A CSV or TSV the agent wrote, in a Datalayer Cells grid: the workbook the
283
+ * chat message shows too (read once, on this thread), editable here, and
284
+ * where the agent's chart goes.
285
+ */
286
+ const SheetView = ({ block }) => {
287
+ const { model, error } = useSheetModel(block);
288
+ // As tall as its data and charts (up to 640 px), so a chart the agent adds shows.
289
+ const height = useFitHeight(model, 200, 640);
290
+ if (error) {
291
+ return (_jsxs(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: ["Could not read the spreadsheet: ", error] }));
292
+ }
293
+ if (!model) {
294
+ return _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Reading\u2026" });
295
+ }
296
+ return (_jsx(CellsProvider, { model: model, children: _jsx(Box, { height: height, border: "1px solid", borderColor: "border.muted", children: _jsx(Spreadsheet, { model: model, "aria-label": block.name }) }) }));
297
+ };
260
298
  // ─── Inner component (rendered after auth) ─────────────────────────────────
261
299
  const AgentOutputsInner = () => {
262
300
  // The Loop creates the agent from the plugin's blueprint when it connects;
@@ -312,10 +350,11 @@ const AgentOutputsInner = () => {
312
350
  document.body.removeChild(a);
313
351
  URL.revokeObjectURL(url);
314
352
  };
315
- return (_jsxs(Box, { height: "100%", display: "flex", flexDirection: "column", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, px: 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", flexShrink: 0, children: [_jsx(TableIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: "Agent Outputs \u2014 Local Runtime" })] }), _jsxs(Box, { flex: 1, minHeight: 0, display: "flex", children: [_jsx(Box, { flex: 1, minWidth: 0, children: _jsx(LoopEmbed, { serverUrl: agentBaseUrl, target: "local", showAgentVariants: true, agentId: agentName, editors: false, showHeader: true, plugins: LOOP_PLUGINS_AGENTOUT }) }), _jsxs(Box, { width: 420, borderLeft: "1px solid", borderColor: "border.default", display: "flex", flexDirection: "column", overflow: "hidden", children: [_jsx(Box, { display: "flex", borderBottom: "1px solid", borderColor: "border.default", flexShrink: 0, children: [
353
+ return (_jsxs(Box, { height: "100%", display: "flex", flexDirection: "column", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, px: 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", flexShrink: 0, children: [_jsx(TableIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: "Agent Outputs \u2014 Local Runtime" })] }), _jsxs(Box, { flex: 1, minHeight: 0, display: "flex", children: [_jsx(Box, { flex: 1, minWidth: 0, children: _jsx(LoopEmbed, { serverUrl: agentBaseUrl, target: "local", showAgentVariants: true, agentId: agentName, editors: false, showHeader: true, plugins: AGENT_OUTPUTS_LOOP_PLUGINS }) }), _jsxs(Box, { width: 420, borderLeft: "1px solid", borderColor: "border.default", display: "flex", flexDirection: "column", overflow: "hidden", children: [_jsx(Box, { display: "flex", borderBottom: "1px solid", borderColor: "border.default", flexShrink: 0, children: [
316
354
  { key: 'table', icon: TableIcon, label: 'Table' },
317
355
  { key: 'json', icon: CodeIcon, label: 'JSON' },
318
356
  { key: 'chart', icon: GraphIcon, label: 'Chart' },
357
+ { key: 'sheet', icon: TableIcon, label: 'Sheet' },
319
358
  { key: 'files', icon: FileIcon, label: 'Files' },
320
359
  ].map(t => {
321
360
  const n = countByTab(t.key);
@@ -328,14 +367,15 @@ const AgentOutputsInner = () => {
328
367
  borderColor: activeTab === t.key ? 'accent.fg' : 'transparent',
329
368
  fontWeight: activeTab === t.key ? 'bold' : 'normal',
330
369
  }, children: [t.label, n > 0 ? ` (${n})` : ''] }, t.key));
331
- }) }), _jsxs(Box, { p: 3, flex: 1, overflow: "auto", children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mb: 2 }, children: [activeTab.charAt(0).toUpperCase() + activeTab.slice(1), " outputs"] }), filtered.length === 0 ? (_jsxs(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: ["No ", activeTab, " outputs yet. Use one of the openers in the chat to produce one \u2014 the side panel will switch to the matching tab automatically."] })) : (filtered.map((d, idx) => (_jsxs(Box, { p: 2, mb: 2, border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, gap: 2, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: d.filename ?? `${d.tab} output` }), _jsx(Label, { size: "small", variant: "secondary", children: d.tab })] }), d.tab === 'table' && _jsx(MarkdownTable, { source: d.payload }), d.tab === 'json' && (_jsx(Box, { as: "pre", bg: "canvas.default", p: 2, borderRadius: 2, fontFamily: "mono", fontSize: 0, maxHeight: 320, overflow: "auto", whiteSpace: "pre-wrap", m: 0, children: (() => {
370
+ }) }), _jsxs(Box, { p: 3, flex: 1, overflow: "auto", children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mb: 2 }, children: [activeTab.charAt(0).toUpperCase() + activeTab.slice(1), " outputs"] }), filtered.length === 0 ? (_jsxs(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: ["No ", activeTab, " outputs yet. Use one of the openers in the chat to produce one \u2014 the side panel will switch to the matching tab automatically.", activeTab === 'sheet' &&
371
+ ' For a spreadsheet, ask for the data as CSV, for example: "Give me twelve months of sales by region as a CSV".'] })) : (filtered.map((d, idx) => (_jsxs(Box, { p: 2, mb: 2, border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, gap: 2, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: d.filename ?? `${d.tab} output` }), _jsx(Label, { size: "small", variant: "secondary", children: d.tab })] }), d.tab === 'table' && _jsx(MarkdownTable, { source: d.payload }), d.tab === 'json' && (_jsx(Box, { as: "pre", bg: "canvas.default", p: 2, borderRadius: 2, fontFamily: "mono", fontSize: 0, maxHeight: 320, overflow: "auto", whiteSpace: "pre-wrap", m: 0, children: (() => {
332
372
  try {
333
373
  return JSON.stringify(JSON.parse(d.payload), null, 2);
334
374
  }
335
375
  catch {
336
376
  return d.payload;
337
377
  }
338
- })() })), d.tab === 'chart' && _jsx(ChartView, { source: d.payload }), d.tab === 'files' && (_jsxs(_Fragment, { children: [_jsx(Box, { as: "pre", bg: "canvas.default", p: 2, borderRadius: 2, fontFamily: "mono", fontSize: 0, maxHeight: 200, overflow: "auto", whiteSpace: "pre-wrap", m: 0, mb: 2, children: d.payload }), _jsxs(Button, { size: "small", leadingVisual: DownloadIcon, onClick: () => download(d), children: ["Download ", d.filename ?? 'file'] })] }))] }, d.messageId + ':' + idx))))] })] })] })] }));
378
+ })() })), d.tab === 'chart' && _jsx(ChartView, { source: d.payload }), d.tab === 'sheet' && d.sheet && (_jsxs(_Fragment, { children: [_jsx(SheetView, { block: d.sheet }), _jsx(Box, { mt: 2, children: _jsxs(Button, { size: "small", leadingVisual: DownloadIcon, onClick: () => downloadSheet(d.sheet), children: ["Download ", d.filename ?? 'spreadsheet'] }) })] })), d.tab === 'files' && (_jsxs(_Fragment, { children: [_jsx(Box, { as: "pre", bg: "canvas.default", p: 2, borderRadius: 2, fontFamily: "mono", fontSize: 0, maxHeight: 200, overflow: "auto", whiteSpace: "pre-wrap", m: 0, mb: 2, children: d.payload }), _jsxs(Button, { size: "small", leadingVisual: DownloadIcon, onClick: () => download(d), children: ["Download ", d.filename ?? 'file'] })] }))] }, d.messageId + ':' + idx))))] })] })] })] }));
339
379
  };
340
380
  // ─── Sync token to core IAM store ──────────────────────────────────────────
341
381
  const syncTokenToIamStore = (token) => {
@@ -0,0 +1,2 @@
1
+ /** The Loop's plugins on the outputs page: the agent, the chat's grids, the grid's tools. */
2
+ export declare const AGENT_OUTPUTS_LOOP_PLUGINS: import("@datalayer/reactor").ReactorPlugin<Record<string, never>, unknown, unknown>[];
@@ -0,0 +1,27 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The Loop plugins of the outputs example (`AgentOutputsExample`): the
7
+ * `example-output` agent, the chat's spreadsheet renderer, and the Cells
8
+ * agent tools on the last spreadsheet answered. The agent runs on a server,
9
+ * so the page names the bundle whose tools it hands it — `cells`, the grid's
10
+ * tools, run on this page's reactor — through the chat extras.
11
+ *
12
+ * Apart from the page, so a test reads the list without mounting the Loop.
13
+ *
14
+ * @module examples/utils/agentOutputsPlugins
15
+ */
16
+ import { AgentOutputsPlugin } from '../../apps/plugins/agent-outputs/index.js';
17
+ import { AgentOutputsSheetsPlugin, AgentOutputsSheetToolsPlugin, } from '../../apps/plugins/agent-outputs/sheets.js';
18
+ import { createChatExtrasPlugin } from '../../apps/plugins/chat-extras/index.js';
19
+ const SHEET_EXTRAS = createChatExtrasPlugin();
20
+ SHEET_EXTRAS.setExtras({ agentToolBundles: ['cells'] });
21
+ /** The Loop's plugins on the outputs page: the agent, the chat's grids, the grid's tools. */
22
+ export const AGENT_OUTPUTS_LOOP_PLUGINS = [
23
+ AgentOutputsPlugin,
24
+ AgentOutputsSheetsPlugin,
25
+ AgentOutputsSheetToolsPlugin,
26
+ SHEET_EXTRAS.plugin,
27
+ ];
@@ -35,16 +35,26 @@ export type BoundHost = Readonly<{
35
35
  * with its key in `X-Datalayer-Host-Key`, or as a person, with the host token
36
36
  * the exchange answered. Any host speaks them.
37
37
  */
38
+ /** Which app a host runs as: Datalayer's own, or one the person brought (`custom`). */
39
+ export type HostProvider = 'datalayer' | 'custom';
38
40
  export declare class DatalayerHostClient {
39
41
  #private;
40
42
  readonly apiUrl: string;
41
43
  readonly fetchImplementation: typeof fetch;
42
44
  constructor(apiUrl: string, fetchImplementation?: typeof fetch);
43
- /** Bind the installation its owner set up, with the code their browser carried. */
45
+ /**
46
+ * Bind the installation its owner set up, with the code their browser
47
+ * carried, asserting the app this host runs as: `datalayer` for
48
+ * Datalayer's own app, `custom` with the app's id for one the person
49
+ * brought. Datalayer refuses a provider that is not the one the
50
+ * installation was registered for (409), and a bind without one (422).
51
+ */
44
52
  bind(input: {
45
53
  code: string;
46
54
  externalId: string;
47
55
  name?: string;
56
+ provider: HostProvider;
57
+ customAppId?: string;
48
58
  }): Promise<BoundHost>;
49
59
  /** The installation was removed from its host: it ends at Datalayer (revocation). */
50
60
  uninstalled(hostKey: string): Promise<void>;
@@ -21,11 +21,6 @@ export class HostRefusedError extends Error {
21
21
  this.what = what;
22
22
  }
23
23
  }
24
- /**
25
- * The generic host routes of ai-agents a host calls besides the credential exchange: as the installation,
26
- * with its key in `X-Datalayer-Host-Key`, or as a person, with the host token
27
- * the exchange answered. Any host speaks them.
28
- */
29
24
  export class DatalayerHostClient {
30
25
  apiUrl;
31
26
  fetchImplementation;
@@ -54,13 +49,24 @@ export class DatalayerHostClient {
54
49
  throw new HostRefusedError(response.status, typeof body.detail === 'string' ? body.detail : 'refused', what);
55
50
  return schema.parse(body);
56
51
  }
57
- /** Bind the installation its owner set up, with the code their browser carried. */
52
+ /**
53
+ * Bind the installation its owner set up, with the code their browser
54
+ * carried, asserting the app this host runs as: `datalayer` for
55
+ * Datalayer's own app, `custom` with the app's id for one the person
56
+ * brought. Datalayer refuses a provider that is not the one the
57
+ * installation was registered for (409), and a bind without one (422).
58
+ */
58
59
  async bind(input) {
60
+ if (input.provider === 'custom' && !input.customAppId) {
61
+ throw new Error("A custom app's bind names its app id (customAppId): Datalayer refuses it otherwise.");
62
+ }
59
63
  const bound = await this.#call('Datalayer did not bind the installation', '/apps/hosts/bind', {
60
64
  body: {
61
65
  code: input.code,
62
66
  external_id: input.externalId,
63
67
  name: input.name ?? '',
68
+ provider: input.provider,
69
+ custom_app_id: input.provider === 'custom' ? (input.customAppId ?? '') : '',
64
70
  },
65
71
  method: 'POST',
66
72
  }, bindSchema);
@@ -36,7 +36,9 @@
36
36
  "request": {
37
37
  "code": "dlhc_…",
38
38
  "external_id": "A0123:-:T0123",
39
- "name": "Acme"
39
+ "name": "Acme",
40
+ "provider": "datalayer",
41
+ "custom_app_id": ""
40
42
  },
41
43
  "response_keys": [
42
44
  "success",
@@ -452,7 +454,9 @@
452
454
  "bind_request": {
453
455
  "code": "dlhc_…",
454
456
  "external_id": "123456789012345678:234567890123456789",
455
- "name": "Acme"
457
+ "name": "Acme",
458
+ "provider": "datalayer",
459
+ "custom_app_id": ""
456
460
  },
457
461
  "external_user_id": "567890123456789012",
458
462
  "thread": {
@@ -501,7 +505,9 @@
501
505
  "bind_request": {
502
506
  "code": "dlhc_…",
503
507
  "external_id": "102290129340398:106540352242922",
504
- "name": "Acme"
508
+ "name": "Acme",
509
+ "provider": "custom",
510
+ "custom_app_id": "1234567890"
505
511
  },
506
512
  "external_user_id": "US.13491208655302741918",
507
513
  "external_user_name": "Alice",
@@ -21,7 +21,7 @@ export { AgUiFrames, readRunStream, type AgUiEvent } from '../agui';
21
21
  export * from '../semantic';
22
22
  export { askQuestionOf, semanticOf, turnOf, type TurnInfo, } from '../semanticOfAgUi';
23
23
  export * from './contracts';
24
- export { DatalayerHostClient, HostRefusedError, type BoundHost, type HostSelf, } from './client';
24
+ export { DatalayerHostClient, HostRefusedError, type BoundHost, type HostProvider, type HostSelf, } from './client';
25
25
  export { agUiEventsOf, assertTrustedStreamUrl, DevelopmentCredentials, HostKeyCredentials, hostSessionOf, HostStarters, HttpAgentClient, InstallationCredentials, lastAnswer, type AgentClient, type HostCredentials, type HostKeyLookup, type HostSession, type HostShape, type HostTurnRequest, type Place, type SessionBinder, type Starter, type StarterSource, type TurnProvenance, type TurnStream, } from './agentClient';
26
26
  export { hostSessionUid } from './sessionUid';
27
27
  export { DATALAYER_ONLY, MAIL_WORDS, RENDERERS, renderApproval, type RenderedAction, type RenderedField, } from './approvalRenderers';
@@ -15,7 +15,7 @@ export declare const RUN_END_WORDS: Record<string, string>;
15
15
  /**
16
16
  * The mark around what the runtime tells an application of a turn a personal
17
17
  * agent asked over PAP or PACT, before the request (`asked_for_said`,
18
- * `ASKED_FOR_TAG` in agent_runtimes.loop.apps.pap_company).
18
+ * `ASKED_FOR_TAG` in agent_runtimes.apps.pap_company).
19
19
  */
20
20
  export declare const ASKED_FOR_TAG = "asked-for";
21
21
  /**
@@ -23,7 +23,7 @@ export const RUN_END_WORDS = {
23
23
  /**
24
24
  * The mark around what the runtime tells an application of a turn a personal
25
25
  * agent asked over PAP or PACT, before the request (`asked_for_said`,
26
- * `ASKED_FOR_TAG` in agent_runtimes.loop.apps.pap_company).
26
+ * `ASKED_FOR_TAG` in agent_runtimes.apps.pap_company).
27
27
  */
28
28
  export const ASKED_FOR_TAG = 'asked-for';
29
29
  /**
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "about": "The semantic agent events a chat host renders (Slack, Discord: DC-005, S-004, S-205), and the AG-UI events of agent-runtimes' session API (agent_runtimes/routes/apps.py, agent_runtimes/loop/apps/sessions.py, agent_runtimes/loop/apps/external_turns.py) each reduces to. src/portable/semanticOfAgUi.ts reduces every case (src/portable/__tests__/semantic.unit.test.ts); agent_runtimes/tests/test_semantic_events_contract.py holds the cases to what the runtime emits.",
3
+ "about": "The semantic agent events a chat host renders (Slack, Discord: DC-005, S-004, S-205), and the AG-UI events of agent-runtimes' session API (agent_runtimes/routes/apps.py, agent_runtimes/apps/sessions.py, agent_runtimes/apps/external_turns.py) each reduces to. src/portable/semanticOfAgUi.ts reduces every case (src/portable/__tests__/semantic.unit.test.ts); agent_runtimes/tests/test_semantic_events_contract.py holds the cases to what the runtime emits.",
4
4
  "kinds": [
5
5
  "text",
6
6
  "progress",
@@ -23,9 +23,9 @@
23
23
  "table": "a Markdown table in the answer (src/portable/semantic.ts answerParts); no AG-UI event carries one",
24
24
  "file": "none in agent-runtimes 1.5.45: reserved, rendered as a safe HTTPS link when produced",
25
25
  "citation": "an HTTPS Markdown link in the answer (answerParts); no AG-UI event carries one",
26
- "ask": "AG-UI RUN_FINISHED with result.asking (agent_runtimes/loop/apps/sessions.py question_of); the run stream carries no loop.ask event",
26
+ "ask": "AG-UI RUN_FINISHED with result.asking (agent_runtimes/apps/sessions.py question_of); the run stream carries no loop.ask event",
27
27
  "completion": "AG-UI RUN_FINISHED without asking",
28
- "stop": "the host itself, after its own Stop: a stopped session's stream ends without RUN_FINISHED or RUN_ERROR (agent_runtimes/loop/apps/sessions.py stop, _close); no AG-UI event carries it",
28
+ "stop": "the host itself, after its own Stop: a stopped session's stream ends without RUN_FINISHED or RUN_ERROR (agent_runtimes/apps/sessions.py stop, _close); no AG-UI event carries it",
29
29
  "error": "AG-UI RUN_ERROR message",
30
30
  "sign-in": "AG-UI TOOL_CALL_RESULT of pap_sign_in (or pact_sign_in, with or without a gateway prefix) whose result is a sign-in waiting for the person with an https page (src/portable/signIn.ts readPapSignIn), after the tool's complete row; a page that is not https gives the row only"
31
31
  },
@@ -4,7 +4,7 @@
4
4
  * `readRunStream` (`agui.ts`); this says what each event means to a host.
5
5
  *
6
6
  * The runtime's `loop.turn` event says which turn a stream is and where it
7
- * stands (`running`, `finished`, `failed`: agent_runtimes/loop/apps/
7
+ * stands (`running`, `finished`, `failed`: agent_runtimes/apps/
8
8
  * external_turns.py); a stopped session's stream ends without a run end,
9
9
  * so `stop` is never read from an event: the host that pressed Stop says it.
10
10
  * A question is read from `RUN_FINISHED.result.asking` only: the run stream
@@ -37,7 +37,7 @@ export declare const CUSTOM_HARNESS = "custom";
37
37
  * when one does.
38
38
  *
39
39
  * A harness the catalogue offers (`agentspecs/harnesses`, SH-01) — Claude
40
- * Code, Codex, OpenCode, Gemini CLI — is served by the Datalayer runtime
40
+ * Code, Codex, OpenCode, pi — is served by the Datalayer runtime
41
41
  * through its ACP adapter (`ACPAgent`, SH-10): the runtime launches the
42
42
  * harness as its subprocess, so the runtime's locations run it and the
43
43
  * browser, which has no process to launch, does not. Three are refused:
@@ -73,7 +73,7 @@ export const CUSTOM_HARNESS = 'custom';
73
73
  * when one does.
74
74
  *
75
75
  * A harness the catalogue offers (`agentspecs/harnesses`, SH-01) — Claude
76
- * Code, Codex, OpenCode, Gemini CLI — is served by the Datalayer runtime
76
+ * Code, Codex, OpenCode, pi — is served by the Datalayer runtime
77
77
  * through its ACP adapter (`ACPAgent`, SH-10): the runtime launches the
78
78
  * harness as its subprocess, so the runtime's locations run it and the
79
79
  * browser, which has no process to launch, does not. Three are refused:
@@ -48,6 +48,7 @@ export declare const EXAMPLE_HARNESS_GEMINI_CLI_AGENTSPEC_0_0_1: Agentspec;
48
48
  export declare const EXAMPLE_HARNESS_GOOSE_AGENTSPEC_0_0_1: Agentspec;
49
49
  export declare const EXAMPLE_HARNESS_OPENCODE_AGENTSPEC_0_0_1: Agentspec;
50
50
  export declare const EXAMPLE_HARNESS_OPENHANDS_AGENTSPEC_0_0_1: Agentspec;
51
+ export declare const EXAMPLE_HARNESS_PI_AGENTSPEC_0_0_1: Agentspec;
51
52
  export declare const EXAMPLE_HOOKS_AGENTSPEC_0_0_1: Agentspec;
52
53
  export declare const EXAMPLE_HUMAN_APPROVED_AUTOMATION_AGENTSPEC_0_0_1: Agentspec;
53
54
  export declare const EXAMPLE_HUMAN_IN_THE_LOOP_AGENTSPEC_0_0_1: Agentspec;
@@ -5,7 +5,7 @@
5
5
  import { ALPHAVANTAGE_MCP_SERVER_0_0_1, CHART_MCP_SERVER_0_0_1, EARTHDATA_MCP_SERVER_0_0_1, EURUS_MCP_SERVER_0_0_1, FILESYSTEM_MCP_SERVER_0_0_1, GITHUB_MCP_SERVER_0_0_1, GOOGLE_WORKSPACE_MCP_SERVER_0_0_1, HUGGINGFACE_MCP_SERVER_0_0_1, KAGGLE_MCP_SERVER_0_0_1, ODOO_MCP_SERVER_0_0_1, SALESFORCE_MCP_SERVER_0_0_1, SLACK_MCP_SERVER_0_0_1, TAVILY_MCP_SERVER_0_0_1, } from '../mcpServers.js';
6
6
  import { ACCOUNTING_SKILL_SPEC_0_0_1, CRAWL_SKILL_SPEC_0_0_1, EVENTS_SKILL_SPEC_0_0_1, GITHUB_SKILL_SPEC_0_0_1, JOKES_SKILL_SPEC_0_0_1, PDF_SKILL_SPEC_0_0_1, TEXT_SUMMARIZER_SKILL_SPEC_0_0_1, } from '../skills.js';
7
7
  import { DECIDE_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_CREATE_PLAN_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_CURRENT_TIME_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_DISPLAY_RECIPE_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_GENERATE_HAIKU_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_GENERATE_TASK_STEPS_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_GET_WEATHER_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_RENDER_A2UI_SURFACE_BACKEND_TOOL_SPEC_0_0_1, EXAMPLE_UPDATE_PLAN_STEP_BACKEND_TOOL_SPEC_0_0_1, RUNTIME_ECHO_BACKEND_TOOL_SPEC_0_0_1, RUNTIME_SEND_MAIL_BACKEND_TOOL_SPEC_0_0_1, RUNTIME_SENSITIVE_ECHO_BACKEND_TOOL_SPEC_0_0_1, } from '../backendTools.js';
8
- import { DECKS_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_EDIT_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_PROPOSE_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_READ_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_FRONTEND_TOOL_SPEC_0_0_1, LEXICAL_DOCUMENT_FRONTEND_TOOL_SPEC_0_0_1, } from '../frontendTools.js';
8
+ import { CELLS_FRONTEND_TOOL_SPEC_0_0_1, DECKS_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_EDIT_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_PROPOSE_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_READ_FRONTEND_TOOL_SPEC_0_0_1, JUPYTER_NOTEBOOK_FRONTEND_TOOL_SPEC_0_0_1, LEXICAL_DOCUMENT_FRONTEND_TOOL_SPEC_0_0_1, } from '../frontendTools.js';
9
9
  // ============================================================================
10
10
  // MCP Server Lookup
11
11
  // ============================================================================
@@ -100,6 +100,8 @@ const TOOL_MAP = {
100
100
  * Map frontend tool IDs to FrontendToolSpec objects.
101
101
  */
102
102
  const FRONTEND_TOOL_MAP = {
103
+ 'cells:0.0.1': CELLS_FRONTEND_TOOL_SPEC_0_0_1,
104
+ cells: CELLS_FRONTEND_TOOL_SPEC_0_0_1,
103
105
  'decks:0.0.1': DECKS_FRONTEND_TOOL_SPEC_0_0_1,
104
106
  decks: DECKS_FRONTEND_TOOL_SPEC_0_0_1,
105
107
  'jupyter-notebook-edit:0.0.1': JUPYTER_NOTEBOOK_EDIT_FRONTEND_TOOL_SPEC_0_0_1,
@@ -2918,6 +2920,54 @@ export const EXAMPLE_HARNESS_OPENHANDS_AGENTSPEC_0_0_1 = {
2918
2920
  parameters: undefined,
2919
2921
  subagents: undefined,
2920
2922
  };
2923
+ export const EXAMPLE_HARNESS_PI_AGENTSPEC_0_0_1 = {
2924
+ id: 'example-harness-pi',
2925
+ version: '0.0.1',
2926
+ name: 'A pi Agent',
2927
+ description: `pi as a Datalayer agent: launched in the runtime over the Agent Client Protocol, its files, terminals and permissions decided by the Shield under the application's Rules, every action in the record. Needs the account's ANTHROPIC_API_KEY.`,
2928
+ tags: ['coding', 'harness', 'acp'],
2929
+ domain: undefined,
2930
+ enabled: false,
2931
+ model: undefined,
2932
+ mcpServers: [],
2933
+ skills: [].filter(Boolean),
2934
+ backendTools: [],
2935
+ frontendTools: [],
2936
+ environmentName: 'ai-agents-env',
2937
+ icon: undefined,
2938
+ emoji: undefined,
2939
+ color: undefined,
2940
+ suggestions: [],
2941
+ welcomeMessage: undefined,
2942
+ welcomeNotebook: undefined,
2943
+ welcomeDocument: undefined,
2944
+ sandboxVariant: 'jupyter-server',
2945
+ harness: 'pi',
2946
+ systemPrompt: undefined,
2947
+ systemPromptCodemodeAddons: undefined,
2948
+ goal: undefined,
2949
+ delegable: [],
2950
+ protocol: undefined,
2951
+ ui: [],
2952
+ protocols: [],
2953
+ trigger: undefined,
2954
+ modelConfig: undefined,
2955
+ mcpServerTools: undefined,
2956
+ guardrails: undefined,
2957
+ evals: undefined,
2958
+ codemode: undefined,
2959
+ output: undefined,
2960
+ advanced: undefined,
2961
+ checkpoints: undefined,
2962
+ authorizationPolicy: undefined,
2963
+ notifications: undefined,
2964
+ memory: 'ephemeral',
2965
+ preHooks: undefined,
2966
+ postHooks: undefined,
2967
+ toolHooks: undefined,
2968
+ parameters: undefined,
2969
+ subagents: undefined,
2970
+ };
2921
2971
  export const EXAMPLE_HOOKS_AGENTSPEC_0_0_1 = {
2922
2972
  id: 'example-hooks',
2923
2973
  version: '0.0.1',
@@ -3949,7 +3999,7 @@ export const EXAMPLE_OUTPUT_AGENTSPEC_0_0_1 = {
3949
3999
  id: 'example-output',
3950
4000
  version: '0.0.1',
3951
4001
  name: 'Example Output Agent',
3952
- description: `Demonstrates structured response rendering (table, json, chart, and file) for the AgentOutputsExample side panel, which detects the block in each answer and renders it.`,
4002
+ description: `Demonstrates structured response rendering (table, json, chart, spreadsheet and file) for the AgentOutputsExample side panel, which detects the block in each answer and renders it; a spreadsheet is drawn with Datalayer Cells, in the chat and in the panel, and charted on the grid through the Cells frontend tools.`,
3953
4003
  tags: ['workflow', 'visualization', 'automation'],
3954
4004
  domain: undefined,
3955
4005
  enabled: false,
@@ -3958,7 +4008,7 @@ export const EXAMPLE_OUTPUT_AGENTSPEC_0_0_1 = {
3958
4008
  skills: [].filter(Boolean),
3959
4009
  backendTools: [],
3960
4010
  disableToolApprovals: true,
3961
- frontendTools: [],
4011
+ frontendTools: [FRONTEND_TOOL_MAP['cells:0.0.1']],
3962
4012
  environmentName: 'ai-agents-env',
3963
4013
  icon: 'table',
3964
4014
  emoji: '📦',
@@ -3977,23 +4027,43 @@ export const EXAMPLE_OUTPUT_AGENTSPEC_0_0_1 = {
3977
4027
  summary: 'Sales chart',
3978
4028
  },
3979
4029
  {
3980
- text: 'Create a CSV file named sales.csv with sample sales for the last 7 days, in a ```csv fenced block whose first line is `# filename: sales.csv`.',
3981
- summary: 'CSV file',
4030
+ text: 'Give me a spreadsheet of twelve months of sales by region (North, South, Online), with a Month column, as one ```csv fenced block whose first line is `# filename: sales.csv`.',
4031
+ summary: 'Sales spreadsheet',
4032
+ },
4033
+ {
4034
+ text: 'Chart the sales spreadsheet: a column chart of the three regions by month, titled Sales by region, inserted on the grid below the data.',
4035
+ summary: 'Chart the spreadsheet',
4036
+ },
4037
+ {
4038
+ text: 'Create a YAML file named config.yaml for a small web service (name, port, log level and two routes), in a ```yaml fenced block whose first line is `# filename: config.yaml`.',
4039
+ summary: 'YAML file',
3982
4040
  },
3983
4041
  ],
3984
- welcomeMessage: 'Example output agent ready. Ask for a TABLE, JSON, a CHART or a FILE and I will answer with exactly one structured output block.',
4042
+ welcomeMessage: 'Example output agent ready. Ask for a TABLE, JSON, a CHART, a SPREADSHEET or a FILE and I will answer with exactly one structured output block; once a spreadsheet is shown, ask me to chart it on its grid.',
3985
4043
  welcomeNotebook: undefined,
3986
4044
  welcomeDocument: undefined,
3987
4045
  sandboxVariant: 'jupyter-server',
3988
4046
  harness: 'pydantic-ai',
3989
4047
  systemPrompt: `You are the Example Output Agent.
3990
- The user may request one of exactly four output modes: TABLE, JSON, CHART, or FILE. For each response, emit exactly one mode and nothing else: no prose before or after the block. Answer from what you know: do not run code or call tools to produce the block.
4048
+ The user may request one of exactly six output modes: TABLE, JSON, CHART, SPREADSHEET, FILE or SHEET CHART. For the first five, emit exactly one mode and nothing else: no prose before or after the block. Answer from what you know: do not run code or call tools to produce the block.
3991
4049
  Formatting rules: - TABLE: Return one GitHub-flavored markdown table, one row per line,
3992
4050
  never several rows joined on a single line.
3993
4051
  - JSON: Return exactly one fenced \`\`\`json code block. - CHART: Return exactly one fenced \`\`\`json code block whose first line is
3994
4052
  "// chart" and whose remaining body is valid ECharts option JSON.
4053
+ - SPREADSHEET: Return exactly one fenced \`\`\`csv code block whose first
4054
+ line is "# filename: <name>.csv", then a header row, then one row per
4055
+ line, comma-separated, numbers without thousands separators or units.
3995
4056
  - FILE: Return exactly one fenced code block whose info string is a file
3996
- extension, and whose first line is "# filename: <name.ext>".
4057
+ extension other than csv or tsv, and whose first line is
4058
+ "# filename: <name.ext>".
4059
+ - SHEET CHART: when asked to chart, plot or graph the spreadsheet already
4060
+ shown (the last SPREADSHEET answer), do not write a block: work on the
4061
+ grid with the Cells tools. Call cells_read_range with no range to find
4062
+ the data, then cells_insert_chart over that range (headers included) with
4063
+ the type and title asked for, at the first column two rows below the
4064
+ data (for data in A1:D13, at A15), unless told where. Answer in one short
4065
+ sentence naming the chart and its range. If the tools say no grid is
4066
+ shown, ask for the spreadsheet first.
3997
4067
 
3998
4068
  If mode is ambiguous, default to JSON.`,
3999
4069
  systemPromptCodemodeAddons: undefined,
@@ -13858,6 +13928,7 @@ export const AGENTSPECS = {
13858
13928
  'example-harness-goose': EXAMPLE_HARNESS_GOOSE_AGENTSPEC_0_0_1,
13859
13929
  'example-harness-opencode': EXAMPLE_HARNESS_OPENCODE_AGENTSPEC_0_0_1,
13860
13930
  'example-harness-openhands': EXAMPLE_HARNESS_OPENHANDS_AGENTSPEC_0_0_1,
13931
+ 'example-harness-pi': EXAMPLE_HARNESS_PI_AGENTSPEC_0_0_1,
13861
13932
  'example-hooks': EXAMPLE_HOOKS_AGENTSPEC_0_0_1,
13862
13933
  'example-human-approved-automation': EXAMPLE_HUMAN_APPROVED_AUTOMATION_AGENTSPEC_0_0_1,
13863
13934
  'example-human-in-the-loop': EXAMPLE_HUMAN_IN_THE_LOOP_AGENTSPEC_0_0_1,