@datalayer/agent-runtimes 1.3.111 → 1.3.112

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 (44) hide show
  1. package/dist-embed/datalayer-app-main.js +4 -4
  2. package/lib/apps/apps/appspec.js +2 -0
  3. package/lib/apps/apps/checks.js +10 -6
  4. package/lib/examples/PapSignInDisplayExample.d.ts +4 -0
  5. package/lib/examples/PapSignInDisplayExample.js +23 -0
  6. package/lib/examples/example-selector.js +1 -0
  7. package/lib/examples/index.d.ts +1 -0
  8. package/lib/examples/index.js +1 -0
  9. package/lib/examples/main.js +2 -0
  10. package/lib/examples/pap/PapSignInDisplayView.d.ts +15 -0
  11. package/lib/examples/pap/PapSignInDisplayView.js +49 -0
  12. package/lib/examples/pap/PapTraceView.d.ts +2 -3
  13. package/lib/examples/pap/PapTraceView.js +3 -4
  14. package/lib/examples/pap/papSignInCases.d.ts +12 -0
  15. package/lib/examples/pap/papSignInCases.js +88 -0
  16. package/lib/protocols/a2a/index.d.ts +52 -0
  17. package/lib/protocols/a2a/index.js +164 -0
  18. package/lib/protocols/agentProtocols.d.ts +15 -0
  19. package/lib/protocols/agentProtocols.js +47 -0
  20. package/lib/protocols/agentic-commerce/index.d.ts +86 -0
  21. package/lib/protocols/agentic-commerce/index.js +267 -0
  22. package/lib/protocols/agentic-commerce/models.d.ts +3292 -0
  23. package/lib/protocols/agentic-commerce/models.js +10 -0
  24. package/lib/protocols/commerce.d.ts +42 -0
  25. package/lib/protocols/commerce.js +47 -0
  26. package/lib/protocols/core.d.ts +44 -0
  27. package/lib/protocols/core.js +62 -0
  28. package/lib/protocols/mcp/index.d.ts +4 -0
  29. package/lib/protocols/mcp/index.js +37 -0
  30. package/lib/protocols/pact/index.d.ts +139 -0
  31. package/lib/protocols/pact/index.js +357 -0
  32. package/lib/protocols/ucp/index.d.ts +79 -0
  33. package/lib/protocols/ucp/index.js +277 -0
  34. package/lib/protocols/web-bot-auth/index.d.ts +161 -0
  35. package/lib/protocols/web-bot-auth/index.js +427 -0
  36. package/lib/specs/actions.js +4 -0
  37. package/lib/specs/apps.d.ts +2 -0
  38. package/lib/specs/apps.js +481 -0
  39. package/lib/specs/appspecSchema.js +200 -2
  40. package/lib/types/agentspecs.d.ts +2 -0
  41. package/package.json +4 -1
  42. package/scripts/codegen/generate_agentic_commerce.sh +74 -0
  43. package/scripts/codegen/generate_apps.py +11 -0
  44. package/scripts/validate_pap_examples.py +27 -2
@@ -272,6 +272,7 @@ function parsePap(data) {
272
272
  scopes: texts(company.scopes),
273
273
  extensions: texts(company.extensions),
274
274
  personalAgents: oneOf(company.personal_agents, ['any', 'registered'], 'any'),
275
+ registeredAgents: texts(company.registered_agents),
275
276
  operations: records(company.operations).map(operation => ({
276
277
  action: text(operation.action),
277
278
  tool: text(operation.tool),
@@ -304,6 +305,7 @@ function dumpPap(pap) {
304
305
  .list('scopes', pap.company.scopes)
305
306
  .list('extensions', pap.company.extensions)
306
307
  .value('personal_agents', pap.company.personalAgents, 'any')
308
+ .list('registered_agents', pap.company.registeredAgents)
307
309
  .list('operations', pap.company.operations.map(operation => new Writer()
308
310
  .text('action', operation.action, '\u0000')
309
311
  .text('tool', operation.tool, '\u0000')
@@ -771,12 +771,16 @@ export function documentShapeProblems(document) {
771
771
  ]) {
772
772
  text(d[key], key);
773
773
  }
774
- // A drawing is named as it is in the profile's sets, `AstronautIcon`.
775
- for (const key of ['avatar', 'banner']) {
776
- const name = typeof d[key] === 'string' ? d[key].trim() : '';
777
- if (name && !/^[A-Z][A-Za-z0-9]{0,63}$/.test(name)) {
778
- at(key, 'is named as its drawing is, `AstronautIcon`');
779
- }
774
+ // An avatar is kept as a person's is (core's avatarChoice): `emoji:<emoji>`
775
+ // or `@datalayer/icons-react:<Name>Icon`; a banner by its drawing's name.
776
+ const avatar = typeof d.avatar === 'string' ? d.avatar.trim() : '';
777
+ if (avatar &&
778
+ !/^(emoji:[^\sA-Za-z0-9]{1,32}|@datalayer\/icons-react:[A-Z][A-Za-z0-9]{0,62}Icon)$/u.test(avatar)) {
779
+ at('avatar', 'is `emoji:<emoji>` or `@datalayer/icons-react:<Name>Icon`, as `emoji:👀`');
780
+ }
781
+ const banner = typeof d.banner === 'string' ? d.banner.trim() : '';
782
+ if (banner && !/^[A-Z][A-Za-z0-9]{0,63}$/.test(banner)) {
783
+ at('banner', 'is named as its drawing is, `SvgTutorialsHero`');
780
784
  }
781
785
  for (const key of [
782
786
  'skills',
@@ -0,0 +1,4 @@
1
+ /** The PAP sign-in card drawn from a tool result's display, as a Reactor workspace plugin. */
2
+ import React from 'react';
3
+ declare const PapSignInDisplayExample: React.FC;
4
+ export default PapSignInDisplayExample;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /** The PAP sign-in card drawn from a tool result's display, as a Reactor workspace plugin. */
7
+ import { useMemo } from 'react';
8
+ import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
9
+ import { LoopEmbed } from '../apps';
10
+ import { definePapExamplePlugin } from '../apps/plugins/pap-example';
11
+ import { ThemedProvider } from './utils/themedProvider';
12
+ setupPrimerPortals();
13
+ const PapSignInDisplayPlugin = definePapExamplePlugin({
14
+ key: 'sign-in-display',
15
+ title: 'PAP Sign-In Display',
16
+ description: 'A sign-in tool’s PapSignIn display: the model reads its model view, the page draws the card.',
17
+ load: () => import('./pap/PapSignInDisplayView'),
18
+ });
19
+ const PapSignInDisplayExample = () => {
20
+ const plugins = useMemo(() => [PapSignInDisplayPlugin], []);
21
+ return (_jsx(ThemedProvider, { children: _jsx(Box, { height: "100vh", minHeight: 0, children: _jsx(LoopEmbed, { target: "browser", agentId: "loop-shell", defaultEditor: "none", showHeader: true, plugins: plugins }) }) }));
22
+ };
23
+ export default PapSignInDisplayExample;
@@ -144,6 +144,7 @@ export const EXAMPLE_ENTRIES = [
144
144
  makeEntry('PapConversationStreamingExample', () => import('./PapConversationStreamingExample'), 'Replay a PAP conversation over SSE: display-only text deltas, complete events, and a Last-Event-ID resume.'),
145
145
  makeEntry('PapConversationControlExample', () => import('./PapConversationControlExample'), 'Replay conversation control: a Direct Conversation, a handoff to a person, and closing.'),
146
146
  makeEntry('PapOperationsExample', () => import('./PapOperationsExample'), 'Replay PAP Operations v1: exact approval of one revision, terms changed, at-most-once confirmation, and cancellation.'),
147
+ makeEntry('PapSignInDisplayExample', () => import('./PapSignInDisplayExample'), 'The sign-in card as a display: a PapSignIn tool result drawn with resolveToolResult and ReactorDisplay, its link and code never in the model view.'),
147
148
  makeEntry('AgentNotificationsExample', () => import('./AgentNotificationsExample'), 'Notifications and event routing example.'),
148
149
  makeEntry('AgentOutputsExample', () => import('./AgentOutputsExample'), 'Structured outputs and rendering patterns.'),
149
150
  makeEntry('AgentParametersExample', () => import('./AgentParametersExample'), 'Launch-time parameterized agent creation with JSON schema.'),
@@ -40,6 +40,7 @@ export { default as PapMediatedSignInExample } from './PapMediatedSignInExample'
40
40
  export { default as PapConversationStreamingExample } from './PapConversationStreamingExample';
41
41
  export { default as PapConversationControlExample } from './PapConversationControlExample';
42
42
  export { default as PapOperationsExample } from './PapOperationsExample';
43
+ export { default as PapSignInDisplayExample } from './PapSignInDisplayExample';
43
44
  export { default as ScenesExample } from './ScenesExample';
44
45
  export { default as AgentEvalsExample } from './AgentEvalsExample';
45
46
  export { default as AgentGuardrailsExample } from './AgentGuardrailsExample';
@@ -46,6 +46,7 @@ export { default as PapMediatedSignInExample } from './PapMediatedSignInExample'
46
46
  export { default as PapConversationStreamingExample } from './PapConversationStreamingExample';
47
47
  export { default as PapConversationControlExample } from './PapConversationControlExample';
48
48
  export { default as PapOperationsExample } from './PapOperationsExample';
49
+ export { default as PapSignInDisplayExample } from './PapSignInDisplayExample';
49
50
  export { default as ScenesExample } from './ScenesExample';
50
51
  export { default as AgentEvalsExample } from './AgentEvalsExample';
51
52
  export { default as AgentGuardrailsExample } from './AgentGuardrailsExample';
@@ -96,6 +96,8 @@ const ANONYMOUS_EXAMPLES = new Set([
96
96
  'PapConversationStreamingExample',
97
97
  'PapConversationControlExample',
98
98
  'PapOperationsExample',
99
+ // The sign-in card drawn from a tool result's display: fixtures, no runtime.
100
+ 'PapSignInDisplayExample',
99
101
  // The floating assistant: the character, its balloon and its motions need
100
102
  // no runtime; a conversation does, and says so when there is none.
101
103
  'AssistantExample',
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The sign-in card as a display (REACTOR_UI RU-1), drawn as the chat draws it.
3
+ *
4
+ * A tool that starts a sign-in answers with a `PapSignIn` display
5
+ * (`application/vnd.datalayer.pap.sign-in+json`). Agent Runtimes gives the
6
+ * model the display's model view — no page to open, no code — and sends the
7
+ * page the same result with the whole display in `_meta["reactor.ui/display"]`
8
+ * (`agent_runtimes/transports/agui_displays.py`). The page finds it with
9
+ * `resolveToolResult` and draws it with `ReactorDisplay`, which picks the
10
+ * renderer the `pap-sign-in` plugin contributes. A plain `pap_sign_in` result,
11
+ * with no display, is found through the plugin's tool binding instead.
12
+ */
13
+ import React from 'react';
14
+ declare const PapSignInDisplayView: React.FC;
15
+ export default PapSignInDisplayView;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * The sign-in card as a display (REACTOR_UI RU-1), drawn as the chat draws it.
8
+ *
9
+ * A tool that starts a sign-in answers with a `PapSignIn` display
10
+ * (`application/vnd.datalayer.pap.sign-in+json`). Agent Runtimes gives the
11
+ * model the display's model view — no page to open, no code — and sends the
12
+ * page the same result with the whole display in `_meta["reactor.ui/display"]`
13
+ * (`agent_runtimes/transports/agui_displays.py`). The page finds it with
14
+ * `resolveToolResult` and draws it with `ReactorDisplay`, which picks the
15
+ * renderer the `pap-sign-in` plugin contributes. A plain `pap_sign_in` result,
16
+ * with no display, is found through the plugin's tool binding instead.
17
+ */
18
+ import { useMemo, useState } from 'react';
19
+ import { Box } from '@datalayer/primer-addons';
20
+ import { Heading, Label, SegmentedControl, Text } from '@primer/react';
21
+ import { useReactorPlatform } from '@datalayer/reactor/react';
22
+ import { DISPLAY_META_KEY, resolveToolResult } from '@datalayer/reactor/ui';
23
+ import { ReactorDisplay } from '@datalayer/reactor/ui/react';
24
+ import { PapExamplePage, PapResult } from './PapExamplePage';
25
+ import { PAP_SIGN_IN_CASES, modelRead } from './papSignInCases';
26
+ const PapSignInDisplayView = () => {
27
+ const platform = useReactorPlatform();
28
+ const [index, setIndex] = useState(0);
29
+ const chosen = PAP_SIGN_IN_CASES[index];
30
+ const display = useMemo(() => resolveToolResult(platform, {
31
+ toolName: chosen.toolName,
32
+ result: chosen.result,
33
+ toolCallId: `call-${chosen.id}`,
34
+ }), [platform, chosen]);
35
+ const meta = chosen.result._meta;
36
+ const embedded = meta !== undefined && DISPLAY_META_KEY in meta;
37
+ const fallback = (_jsx(Text, { sx: { color: 'fg.muted' }, children: "No renderer claimed this result." }));
38
+ const context = {
39
+ toolCallId: `call-${chosen.id}`,
40
+ toolName: chosen.toolName,
41
+ name: chosen.toolName,
42
+ args: {},
43
+ result: chosen.result,
44
+ status: 'complete',
45
+ defaultUI: fallback,
46
+ };
47
+ return (_jsxs(PapExamplePage, { eyebrow: "Reactor UI display", title: "PAP Sign-In Display", description: "A sign-in tool answers with a PapSignIn display. The model reads its model view; the page receives the whole display beside it and draws the Sign in at a company card with resolveToolResult and ReactorDisplay.", children: [_jsx(SegmentedControl, { "aria-label": "Sign-in result", onChange: setIndex, sx: { alignSelf: 'flex-start' }, children: PAP_SIGN_IN_CASES.map((item, at) => (_jsx(SegmentedControl.Button, { selected: at === index, children: item.label }, item.id))) }), _jsx(Text, { sx: { color: 'fg.muted' }, children: chosen.explanation }), _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', null, '1fr 1fr'], gap: 3, alignItems: "start", children: [_jsx(PapResult, { title: "What the model read", value: modelRead(chosen.result) }), _jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.default", p: 3, "data-pap-sign-in-page": chosen.id, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: 2, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 2 }, children: "What the page drew" }), _jsx(Label, { variant: embedded ? 'success' : 'attention', children: embedded ? 'embedded display' : 'tool binding' })] }), display ? (_jsx(ReactorDisplay, { display: display, context: context, fallback: fallback })) : (fallback)] })] })] }));
48
+ };
49
+ export default PapSignInDisplayView;
@@ -3,9 +3,8 @@
3
3
  *
4
4
  * The traces are generated from the PAP Python SDK's demo worlds by
5
5
  * `examples/personal-agent-protocol/traces/generate.py`: real keys, proofs
6
- * and HTTP between the parties, recorded and redacted. The TypeScript SDK
7
- * does not implement these features yet, so this view calls no SDK function:
8
- * it shows what the user sees, what each party holds, and the sequence of
6
+ * and HTTP between the parties, recorded and redacted. This view calls no
7
+ * SDK function: it shows what the user sees, what each party holds, and the sequence of
9
8
  * exchanges between them, all synchronized on one step.
10
9
  */
11
10
  import React from 'react';
@@ -8,9 +8,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
8
8
  *
9
9
  * The traces are generated from the PAP Python SDK's demo worlds by
10
10
  * `examples/personal-agent-protocol/traces/generate.py`: real keys, proofs
11
- * and HTTP between the parties, recorded and redacted. The TypeScript SDK
12
- * does not implement these features yet, so this view calls no SDK function:
13
- * it shows what the user sees, what each party holds, and the sequence of
11
+ * and HTTP between the parties, recorded and redacted. This view calls no
12
+ * SDK function: it shows what the user sees, what each party holds, and the sequence of
14
13
  * exchanges between them, all synchronized on one step.
15
14
  */
16
15
  import { useEffect, useMemo, useRef, useState } from 'react';
@@ -191,7 +190,7 @@ export const PapTraceView = ({ trace, eyebrow = 'PAP · Replayed trace', }) => {
191
190
  const go = (next) => setStepIndex(Math.max(0, Math.min(trace.steps.length - 1, next)));
192
191
  const active = new Set(step.sequence.flatMap(item => [item.from, item.to]));
193
192
  const observers = trace.parties.slice(1);
194
- return (_jsxs(PapExamplePage, { eyebrow: eyebrow, title: trace.title, description: `${trace.description} Replayed from a trace the PAP Python SDK's demo world generated (${trace.generator}); every token, code, proof and assertion is a placeholder. These features need the PAP release after 0.3.3.`, maxWidth: 1240, children: [_jsx(Box, { display: "flex", gap: 2, flexWrap: "wrap", p: 3, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, children: trace.parties.map(party => (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", mr: 3, children: [_jsx(Avatar, { party: party, tone: toneOf(trace.parties, party.id) }), _jsxs(Box, { children: [_jsx(Text, { as: "div", sx: { fontWeight: 700, fontSize: 1 }, children: party.name }), _jsx(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: party.role })] })] }, party.id))) }), _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(IconButton, { icon: ChevronLeftIcon, "aria-label": "Previous step", disabled: stepIndex === 0, onClick: () => go(stepIndex - 1) }), _jsx(Box, { display: "flex", gap: 2, overflow: "auto", flex: 1, "aria-label": "Trace steps", children: trace.steps.map((candidate, index) => (_jsxs(Button, { variant: index === stepIndex ? 'primary' : 'default', onClick: () => go(index), sx: { flexShrink: 0 }, children: [index + 1, ". ", candidate.title] }, candidate.title))) }), _jsx(IconButton, { icon: ChevronRightIcon, "aria-label": "Next step", disabled: stepIndex === trace.steps.length - 1, onClick: () => go(stepIndex + 1) })] }), _jsxs(Box, { display: "grid", sx: {
193
+ return (_jsxs(PapExamplePage, { eyebrow: eyebrow, title: trace.title, description: `${trace.description} Replayed from a trace the PAP Python SDK's demo world generated (${trace.generator}); every token, code, proof and assertion is a placeholder.`, maxWidth: 1240, children: [_jsx(Box, { display: "flex", gap: 2, flexWrap: "wrap", p: 3, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, children: trace.parties.map(party => (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", mr: 3, children: [_jsx(Avatar, { party: party, tone: toneOf(trace.parties, party.id) }), _jsxs(Box, { children: [_jsx(Text, { as: "div", sx: { fontWeight: 700, fontSize: 1 }, children: party.name }), _jsx(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: party.role })] })] }, party.id))) }), _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(IconButton, { icon: ChevronLeftIcon, "aria-label": "Previous step", disabled: stepIndex === 0, onClick: () => go(stepIndex - 1) }), _jsx(Box, { display: "flex", gap: 2, overflow: "auto", flex: 1, "aria-label": "Trace steps", children: trace.steps.map((candidate, index) => (_jsxs(Button, { variant: index === stepIndex ? 'primary' : 'default', onClick: () => go(index), sx: { flexShrink: 0 }, children: [index + 1, ". ", candidate.title] }, candidate.title))) }), _jsx(IconButton, { icon: ChevronRightIcon, "aria-label": "Next step", disabled: stepIndex === trace.steps.length - 1, onClick: () => go(stepIndex + 1) })] }), _jsxs(Box, { display: "grid", sx: {
195
194
  gridTemplateColumns: ['1fr', '1fr', 'minmax(300px, 0.7fr) 1.5fr'],
196
195
  }, gap: 4, alignItems: "start", children: [_jsx(Phone, { trace: trace, stepIndex: stepIndex }), _jsxs(Box, { display: "flex", flexDirection: "column", gap: 3, minWidth: 0, children: [_jsxs(Box, { p: 3, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: ["Step ", stepIndex + 1, " of ", trace.steps.length, " \u00B7 ", step.channel] }), _jsx(Heading, { as: "h2", sx: { mt: 1, fontSize: 3 }, children: step.title }), _jsx(Text, { as: "p", sx: { mt: 2, mb: 0, color: 'fg.muted' }, children: step.summary })] }), _jsx(Sequence, { trace: trace, step: step }), _jsx(Box, { display: "grid", sx: {
197
196
  gridTemplateColumns: [
@@ -0,0 +1,12 @@
1
+ export type PapSignInCase = {
2
+ id: string;
3
+ label: string;
4
+ toolName: string;
5
+ /** What the page receives for the tool call. */
6
+ result: Record<string, unknown>;
7
+ explanation: string;
8
+ };
9
+ /** The results a page receives for a sign-in, as Agent Runtimes sends them. */
10
+ export declare const PAP_SIGN_IN_CASES: PapSignInCase[];
11
+ /** What the model read: the result without the page's `_meta`. */
12
+ export declare function modelRead(result: Record<string, unknown>): unknown;
@@ -0,0 +1,88 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The results a page receives for a sign-in (REACTOR_UI RU-1), as Agent
7
+ * Runtimes sends them: a display's model view with the whole display beside
8
+ * it in `_meta`, or a plain `pap_sign_in` answer. Fictional company, no
9
+ * credential.
10
+ */
11
+ import { DISPLAY_META_KEY } from '@datalayer/reactor/ui';
12
+ import { PAP_SIGN_IN_TYPE } from '../../apps/plugins/pap-sign-in';
13
+ const EXPIRES_AT = '2026-10-12T12:15:00+00:00';
14
+ const DEVICE_PAGE = 'https://manzanita.example/device?user_code=WDJB-MJHT';
15
+ const DEVICE_CODE = 'WDJB-MJHT';
16
+ /** The results a page receives for a sign-in, as Agent Runtimes sends them. */
17
+ export const PAP_SIGN_IN_CASES = [
18
+ {
19
+ id: 'device',
20
+ label: 'Device sign-in',
21
+ toolName: 'pap_manzanita_example_pap_sign_in',
22
+ explanation: 'The model view the model read, with the whole display beside it in _meta: the page gets the link and the code, the model got neither.',
23
+ result: {
24
+ type: PAP_SIGN_IN_TYPE,
25
+ text: 'Sign in at manzanita.example: open its sign-in page, shown here, and enter the code shown with it.',
26
+ data: { mode: 'device', expires_at: EXPIRES_AT, sent_to: null },
27
+ _meta: {
28
+ [DISPLAY_META_KEY]: {
29
+ type: PAP_SIGN_IN_TYPE,
30
+ version: 1,
31
+ id: 'sign-in-device',
32
+ text: 'Sign in at manzanita.example: open its sign-in page, shown here, and enter the code shown with it.',
33
+ data: {
34
+ mode: 'device',
35
+ open: DEVICE_PAGE,
36
+ code: DEVICE_CODE,
37
+ expires_at: EXPIRES_AT,
38
+ sent_to: null,
39
+ },
40
+ actions: [],
41
+ },
42
+ },
43
+ },
44
+ },
45
+ {
46
+ id: 'direct',
47
+ label: 'Direct sign-in',
48
+ toolName: 'pap_manzanita_example_pap_sign_in',
49
+ explanation: 'A Direct Sign-In: the company’s own page, then back. No code; the page alone gets the link.',
50
+ result: {
51
+ type: PAP_SIGN_IN_TYPE,
52
+ text: 'Sign in at manzanita.example: open its sign-in page, shown here, sign in there, and come back.',
53
+ data: { mode: 'direct', expires_at: EXPIRES_AT, sent_to: null },
54
+ _meta: {
55
+ [DISPLAY_META_KEY]: {
56
+ type: PAP_SIGN_IN_TYPE,
57
+ version: 1,
58
+ id: 'sign-in-direct',
59
+ text: 'Sign in at manzanita.example: open its sign-in page, shown here, sign in there, and come back.',
60
+ data: {
61
+ mode: 'direct',
62
+ open: 'https://manzanita.example/authorize?request_uri=urn%3Aexample',
63
+ expires_at: EXPIRES_AT,
64
+ sent_to: null,
65
+ },
66
+ actions: [],
67
+ },
68
+ },
69
+ },
70
+ },
71
+ {
72
+ id: 'plain',
73
+ label: 'Plain result',
74
+ toolName: 'pap_sign_in',
75
+ explanation: 'A pap_sign_in answer that carries no display: the plugin’s tool binding claims the tool by its name, and the card reads the answer itself.',
76
+ result: {
77
+ status: 'waiting_for_the_person',
78
+ mode: 'device',
79
+ open: DEVICE_PAGE,
80
+ code: DEVICE_CODE,
81
+ expires_at: EXPIRES_AT,
82
+ },
83
+ },
84
+ ];
85
+ /** What the model read: the result without the page's `_meta`. */
86
+ export function modelRead(result) {
87
+ return Object.fromEntries(Object.entries(result).filter(([key]) => key !== '_meta'));
88
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * A2A as a Reactor Plugin (AGENT_PROTOCOLS PR-02): Agent Card extensions
3
+ * read, published from what is contributed, and negotiated.
4
+ *
5
+ * Each A2A extension this runtime implements is a Contribution Point of the
6
+ * plugin, `a2a.extension.<name>`: `orchestration` and `face` (Datalayer's,
7
+ * defaults `datalayer`), and `other` for any extension by its URI (no
8
+ * default, every contribution used). The twin of `agent_runtimes.protocols.a2a`.
9
+ *
10
+ * @module protocols/a2a
11
+ */
12
+ import { type ReactorPlatformView } from '@datalayer/reactor';
13
+ export declare const A2A_PLUGIN = "agent-runtimes.protocols.a2a";
14
+ export declare const ORCHESTRATION_URI = "https://datalayer.ai/extensions/orchestration/v1";
15
+ export declare const FACE_URI = "https://datalayer.ai/extensions/face/v1";
16
+ /** One extension an Agent Card advertises. */
17
+ export interface CardExtension {
18
+ uri: string;
19
+ description?: string;
20
+ required?: boolean;
21
+ params?: Record<string, unknown>;
22
+ }
23
+ /** What an extension may read to say its params on one agent's card. */
24
+ export interface CardContext {
25
+ agentId: string;
26
+ apiPrefix?: string;
27
+ face?: Record<string, string>;
28
+ }
29
+ /** One A2A extension this runtime implements. */
30
+ export interface A2AExtension {
31
+ uri: string;
32
+ description: string;
33
+ required: boolean;
34
+ /** Its params on this card; `undefined` when it is not published there. */
35
+ params(context: CardContext): Record<string, unknown> | undefined;
36
+ }
37
+ export declare const A2AOrchestrationExtensions: import("@datalayer/reactor").ContributionPoint<A2AExtension>;
38
+ export declare const A2AFaceExtensions: import("@datalayer/reactor").ContributionPoint<A2AExtension>;
39
+ export declare const A2AOtherExtensions: import("@datalayer/reactor").ContributionPoint<A2AExtension>;
40
+ export declare const A2APlugin: import("@datalayer/reactor").ReactorPlugin<Record<string, never>, unknown, unknown>;
41
+ /** Every extension a card advertises, in its order. */
42
+ export declare function extensionsOf(card: unknown): CardExtension[];
43
+ export declare function findExtension(card: unknown, uri: string): CardExtension | undefined;
44
+ /** The extensions a card requires that the caller does not support. */
45
+ export declare function unsupportedRequired(card: unknown, supported: Iterable<string>): string[];
46
+ /** What one agent's card publishes: what is contributed, then what it declares. */
47
+ export declare function cardExtensions(reactor: ReactorPlatformView, context: CardContext, declared?: CardExtension[]): CardExtension[];
48
+ /**
49
+ * The extensions a client activates with an agent: those of its card that
50
+ * are implemented here. Throws when the card requires one that is not.
51
+ */
52
+ export declare function negotiateExtensions(card: unknown, reactor: ReactorPlatformView): string[];
@@ -0,0 +1,164 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * A2A as a Reactor Plugin (AGENT_PROTOCOLS PR-02): Agent Card extensions
7
+ * read, published from what is contributed, and negotiated.
8
+ *
9
+ * Each A2A extension this runtime implements is a Contribution Point of the
10
+ * plugin, `a2a.extension.<name>`: `orchestration` and `face` (Datalayer's,
11
+ * defaults `datalayer`), and `other` for any extension by its URI (no
12
+ * default, every contribution used). The twin of `agent_runtimes.protocols.a2a`.
13
+ *
14
+ * @module protocols/a2a
15
+ */
16
+ import { contribution, defineContributionPoint, definePlugin, } from '@datalayer/reactor';
17
+ import { DEFAULT_ORDER, ProtocolDescriptions } from '../core';
18
+ export const A2A_PLUGIN = 'agent-runtimes.protocols.a2a';
19
+ export const ORCHESTRATION_URI = 'https://datalayer.ai/extensions/orchestration/v1';
20
+ export const FACE_URI = 'https://datalayer.ai/extensions/face/v1';
21
+ export const A2AOrchestrationExtensions = defineContributionPoint('a2a.extension.orchestration');
22
+ export const A2AFaceExtensions = defineContributionPoint('a2a.extension.face');
23
+ export const A2AOtherExtensions = defineContributionPoint('a2a.extension.other');
24
+ const EXTENSION_POINTS = [
25
+ A2AOrchestrationExtensions,
26
+ A2AFaceExtensions,
27
+ A2AOtherExtensions,
28
+ ];
29
+ const orchestration = {
30
+ uri: ORCHESTRATION_URI,
31
+ description: 'Pause at a checkpoint and resume from it; steer a task while it works.',
32
+ required: false,
33
+ params: context => ({
34
+ pause: `${context.apiPrefix ?? ''}/a2a/pause`,
35
+ steer: `${context.apiPrefix ?? ''}/a2a/steer`,
36
+ }),
37
+ };
38
+ const face = {
39
+ uri: FACE_URI,
40
+ description: 'Its face: an emoji, and an avatar when it has one.',
41
+ required: false,
42
+ params: context => (context.face ? { ...context.face } : undefined),
43
+ };
44
+ export const A2APlugin = definePlugin({
45
+ name: A2A_PLUGIN,
46
+ version: '1.0.0',
47
+ description: 'Agent2Agent 1.0: the extensions an agent card publishes and a client negotiates.',
48
+ contributionPoints: EXTENSION_POINTS,
49
+ contributes: [
50
+ contribution(ProtocolDescriptions, {
51
+ id: 'a2a',
52
+ name: 'Agent2Agent',
53
+ layer: 'transport',
54
+ version: '1.0',
55
+ roles: ['client', 'server'],
56
+ status: 'implemented',
57
+ specification: 'https://a2a-protocol.org/latest/specification/',
58
+ plugin: A2A_PLUGIN,
59
+ points: EXTENSION_POINTS.map(point => point.id),
60
+ }, { id: 'a2a', order: DEFAULT_ORDER }),
61
+ contribution(A2AOrchestrationExtensions, orchestration, {
62
+ id: 'datalayer',
63
+ order: DEFAULT_ORDER,
64
+ }),
65
+ contribution(A2AFaceExtensions, face, {
66
+ id: 'datalayer',
67
+ order: DEFAULT_ORDER,
68
+ }),
69
+ ],
70
+ });
71
+ function assertUri(uri) {
72
+ if (typeof uri !== 'string' || !/^[a-z][a-z0-9+.-]*:/i.test(uri)) {
73
+ throw new Error(`an extension is named by a URI, not ${String(uri)}`);
74
+ }
75
+ }
76
+ /** Every extension a card advertises, in its order. */
77
+ export function extensionsOf(card) {
78
+ const raw = card
79
+ ?.capabilities?.extensions;
80
+ if (raw === undefined || raw === null) {
81
+ return [];
82
+ }
83
+ if (!Array.isArray(raw)) {
84
+ throw new Error('capabilities.extensions is a list');
85
+ }
86
+ const out = raw.map(entry => {
87
+ const { uri, description, required, params } = entry;
88
+ assertUri(uri);
89
+ return {
90
+ uri,
91
+ description: description ?? '',
92
+ required: Boolean(required),
93
+ params: params ?? {},
94
+ };
95
+ });
96
+ if (new Set(out.map(e => e.uri)).size !== out.length) {
97
+ throw new Error('the card advertises an extension twice');
98
+ }
99
+ return out;
100
+ }
101
+ export function findExtension(card, uri) {
102
+ return extensionsOf(card).find(extension => extension.uri === uri);
103
+ }
104
+ /** The extensions a card requires that the caller does not support. */
105
+ export function unsupportedRequired(card, supported) {
106
+ const known = new Set(supported);
107
+ return extensionsOf(card)
108
+ .filter(extension => extension.required && !known.has(extension.uri))
109
+ .map(extension => extension.uri);
110
+ }
111
+ function providers(reactor) {
112
+ const out = [];
113
+ for (const point of EXTENSION_POINTS) {
114
+ const contributions = reactor.getContributions(point);
115
+ if (point === A2AOtherExtensions) {
116
+ out.push(...contributions.map(c => c.value));
117
+ }
118
+ else if (contributions.length) {
119
+ if (contributions[1] &&
120
+ contributions[1].order === contributions[0].order) {
121
+ throw new Error(`two implementations of ${point.id} at the same order`);
122
+ }
123
+ out.push(contributions[0].value);
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+ /** What one agent's card publishes: what is contributed, then what it declares. */
129
+ export function cardExtensions(reactor, context, declared = []) {
130
+ const out = [];
131
+ for (const provider of providers(reactor)) {
132
+ const params = provider.params(context);
133
+ if (params !== undefined) {
134
+ out.push({
135
+ uri: provider.uri,
136
+ description: provider.description,
137
+ required: provider.required,
138
+ params,
139
+ });
140
+ }
141
+ }
142
+ for (const extension of declared) {
143
+ assertUri(extension.uri);
144
+ if (out.some(e => e.uri === extension.uri)) {
145
+ throw new Error(`extension ${extension.uri} is published twice`);
146
+ }
147
+ out.push(extension);
148
+ }
149
+ return out;
150
+ }
151
+ /**
152
+ * The extensions a client activates with an agent: those of its card that
153
+ * are implemented here. Throws when the card requires one that is not.
154
+ */
155
+ export function negotiateExtensions(card, reactor) {
156
+ const implemented = new Set(providers(reactor).map(provider => provider.uri));
157
+ const missing = unsupportedRequired(card, implemented);
158
+ if (missing.length) {
159
+ throw new Error(`the agent requires extensions not implemented here: ${missing.join(', ')}`);
160
+ }
161
+ return extensionsOf(card)
162
+ .map(extension => extension.uri)
163
+ .filter(uri => implemented.has(uri));
164
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The `agent-protocols` Reactor Extension: the protocol plugins, delivered
3
+ * together. An Extension only groups plugins for delivery; it has no
4
+ * lifecycle and contributes nothing (AGENT_PROTOCOLS §3.1).
5
+ *
6
+ * @module protocols/agentProtocols
7
+ */
8
+ import { type PlatformInput, type ReactorPlatform } from '@datalayer/reactor';
9
+ export declare const AGENT_PROTOCOLS_EXTENSION = "agent-protocols";
10
+ export declare const AgentProtocolsExtension: import("@datalayer/reactor").ReactorExtension;
11
+ /**
12
+ * A started Reactor with the `agent-protocols` Extension, then the host's
13
+ * plugins: their lower-order contributions replace the protocols' defaults.
14
+ */
15
+ export declare function buildProtocolsReactor(plugins?: PlatformInput[]): ReactorPlatform;
@@ -0,0 +1,47 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The `agent-protocols` Reactor Extension: the protocol plugins, delivered
7
+ * together. An Extension only groups plugins for delivery; it has no
8
+ * lifecycle and contributes nothing (AGENT_PROTOCOLS §3.1).
9
+ *
10
+ * @module protocols/agentProtocols
11
+ */
12
+ import { buildReactorFromPlugins, defineExtension, } from '@datalayer/reactor';
13
+ import { A2APlugin } from './a2a';
14
+ import { AgenticCommercePlugin } from './agentic-commerce';
15
+ import { ProtocolsCorePlugin } from './core';
16
+ import { McpPlugin } from './mcp';
17
+ import { PactPlugin } from './pact';
18
+ import { UcpPlugin } from './ucp';
19
+ import { WebBotAuthPlugin } from './web-bot-auth';
20
+ export const AGENT_PROTOCOLS_EXTENSION = 'agent-protocols';
21
+ export const AgentProtocolsExtension = defineExtension({
22
+ name: AGENT_PROTOCOLS_EXTENSION,
23
+ version: '1.0.0',
24
+ displayName: 'Agent protocols',
25
+ description: 'MCP, A2A, Web Bot Auth, UCP, the Agentic Commerce Protocol and PACT, each a plugin; their extensions its contribution points.',
26
+ plugins: [
27
+ ProtocolsCorePlugin,
28
+ McpPlugin,
29
+ A2APlugin,
30
+ WebBotAuthPlugin,
31
+ UcpPlugin,
32
+ AgenticCommercePlugin,
33
+ PactPlugin,
34
+ ],
35
+ });
36
+ /**
37
+ * A started Reactor with the `agent-protocols` Extension, then the host's
38
+ * plugins: their lower-order contributions replace the protocols' defaults.
39
+ */
40
+ export function buildProtocolsReactor(plugins = []) {
41
+ const reactor = buildReactorFromPlugins([
42
+ AgentProtocolsExtension,
43
+ ...plugins,
44
+ ]);
45
+ reactor.start();
46
+ return reactor;
47
+ }