@datalayer/agent-runtimes 1.5.89 → 1.5.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-embed/datalayer-app-main.js +38 -34
- package/lib/apps/embed/element.js +20 -5
- package/lib/apps/embed/visitorApp.d.ts +23 -45
- package/lib/apps/embed/visitorApp.js +65 -13
- package/lib/components/teams/A2ATeamGraph.d.ts +28 -7
- package/lib/components/teams/A2ATeamGraph.js +64 -27
- package/lib/components/teams/a2aTeamFlow.d.ts +16 -7
- package/lib/components/teams/a2aTeamFlow.js +17 -14
- package/lib/portable/hosts/agentClient.d.ts +8 -0
- package/lib/portable/hosts/agentClient.js +2 -0
- package/lib/portable/hosts/approvalRenderers.d.ts +10 -2
- package/lib/portable/hosts/approvalRenderers.js +81 -2
- package/lib/portable/hosts/contracts.d.ts +1 -0
- package/lib/portable/hosts/contracts.js +2 -0
- package/lib/portable/hosts/contracts.json +56 -2
- package/package.json +1 -1
- package/scripts/codegen/generate_mcp_servers.py +5 -16
|
@@ -44,6 +44,7 @@ export class HostKeyCredentials {
|
|
|
44
44
|
externalUserId: request.externalUserId,
|
|
45
45
|
hostKey,
|
|
46
46
|
place: this.shape.placeOf(request),
|
|
47
|
+
...(request.visitor ? { visitor: true } : {}),
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
/** The exchange for one host user and deployment, with a key in hand (a delivery, an action). */
|
|
@@ -53,6 +54,7 @@ export class HostKeyCredentials {
|
|
|
53
54
|
deployment_uid: input.deploymentUid,
|
|
54
55
|
external_user_id: input.externalUserId,
|
|
55
56
|
...(input.place ? { place: input.place } : {}),
|
|
57
|
+
...(input.visitor ? { visitor: true } : {}),
|
|
56
58
|
}),
|
|
57
59
|
headers: {
|
|
58
60
|
'content-type': 'application/json',
|
|
@@ -6,7 +6,9 @@ import type { ApprovalPresentation } from './contracts';
|
|
|
6
6
|
* presented is named by the renderer, or the card fails closed. An unknown
|
|
7
7
|
* tool, an argument the renderer does not know, an attachment, and every
|
|
8
8
|
* action that is not mail (reads, writes, other sends, destructive, financial,
|
|
9
|
-
* publishing, executing) are reviewed in Datalayer (decided by the user on 16 October 2026: mail only)
|
|
9
|
+
* publishing, executing) are reviewed in Datalayer (decided by the user on 16 October 2026: mail only),
|
|
10
|
+
* but for a company's `pap_confirm`, decided in a host with the operation's
|
|
11
|
+
* exact revision since 28 October 2026 (the user, WHATSAPP WA-43).
|
|
10
12
|
* They render ai-agents' presentation v1, a different input
|
|
11
13
|
* from `../approvals.ts` and `../approvalCard.ts`. Moved from the Slack
|
|
12
14
|
* service (DC-004).
|
|
@@ -39,7 +41,13 @@ type Renderer = Readonly<{
|
|
|
39
41
|
export declare const DATALAYER_ONLY: Set<"unknown" | "read" | "write" | "send" | "delete" | "publish" | "execute" | "financial">;
|
|
40
42
|
/** The words of a tool's name that make a send mail: contracts.json `mail_words`. */
|
|
41
43
|
export declare const MAIL_WORDS: ReadonlySet<string>;
|
|
42
|
-
|
|
44
|
+
export declare const isPapConfirm: (toolName: string) => boolean;
|
|
45
|
+
/** What a `pap_confirm` approval carries: contracts.json `presentation.pap_confirm_keys`. */
|
|
46
|
+
export declare const PAP_CONFIRM_KEYS: readonly ["key", "company", "summary", "terms", "operation_ref", "revision", "digest", "class", "session", "only_the_person", "was"];
|
|
47
|
+
/**
|
|
48
|
+
* The renderers, the first that matches wins: a company's `pap_confirm`
|
|
49
|
+
* (whose name says no class), then Datalayer-only before any other.
|
|
50
|
+
*/
|
|
43
51
|
export declare const RENDERERS: readonly Renderer[];
|
|
44
52
|
/**
|
|
45
53
|
* How a host shows an approval, by the renderer that knows its tool; with
|
|
@@ -98,6 +98,82 @@ const mail = {
|
|
|
98
98
|
return rendered('mail', presentation, fields, MAIL_KEYS, extra);
|
|
99
99
|
},
|
|
100
100
|
};
|
|
101
|
+
/** `pap_confirm`, as the gateway names it with or without a prefix. */
|
|
102
|
+
const PAP_CONFIRM = /(^|[_.:\-/])pap_confirm$/;
|
|
103
|
+
export const isPapConfirm = (toolName) => PAP_CONFIRM.test(toolName);
|
|
104
|
+
/** What a `pap_confirm` approval carries: contracts.json `presentation.pap_confirm_keys`. */
|
|
105
|
+
export const PAP_CONFIRM_KEYS = [
|
|
106
|
+
'key',
|
|
107
|
+
'company',
|
|
108
|
+
'summary',
|
|
109
|
+
'terms',
|
|
110
|
+
'operation_ref',
|
|
111
|
+
'revision',
|
|
112
|
+
'digest',
|
|
113
|
+
'class',
|
|
114
|
+
'session',
|
|
115
|
+
'only_the_person',
|
|
116
|
+
'was',
|
|
117
|
+
];
|
|
118
|
+
/**
|
|
119
|
+
* A company's operation the person confirms (WHATSAPP WA-43, decided by the
|
|
120
|
+
* user 28 October 2026): the company, its own summary, every term, the
|
|
121
|
+
* operation and its **exact revision** — a decision is on that revision and
|
|
122
|
+
* no other; a changed revision is another approval. A missing company,
|
|
123
|
+
* summary, operation, revision or digest fails the card closed.
|
|
124
|
+
*/
|
|
125
|
+
const papConfirm = {
|
|
126
|
+
name: 'pap-confirm',
|
|
127
|
+
matches: presentation => isPapConfirm(presentation.tool_name),
|
|
128
|
+
render(presentation) {
|
|
129
|
+
const args = presentation.arguments;
|
|
130
|
+
const fields = [];
|
|
131
|
+
const missing = [];
|
|
132
|
+
const extra = [];
|
|
133
|
+
const company = typeof args.company === 'string' ? args.company : '';
|
|
134
|
+
const summary = typeof args.summary === 'string' ? args.summary : '';
|
|
135
|
+
const operation = typeof args.operation_ref === 'string' ? args.operation_ref : '';
|
|
136
|
+
const revision = Number.isInteger(args.revision)
|
|
137
|
+
? args.revision
|
|
138
|
+
: undefined;
|
|
139
|
+
if (!company)
|
|
140
|
+
missing.push('the company');
|
|
141
|
+
else
|
|
142
|
+
fields.push({ label: 'Company', value: company });
|
|
143
|
+
if (!summary)
|
|
144
|
+
missing.push('what it does, in its words');
|
|
145
|
+
else
|
|
146
|
+
fields.push({ label: 'Summary', value: summary });
|
|
147
|
+
const termsAreFields = args.terms === undefined ||
|
|
148
|
+
args.terms === null ||
|
|
149
|
+
(typeof args.terms === 'object' && !Array.isArray(args.terms));
|
|
150
|
+
if (!termsAreFields)
|
|
151
|
+
extra.push('Its terms are not named one by one: review it in Datalayer.');
|
|
152
|
+
else
|
|
153
|
+
for (const [name, value] of Object.entries((args.terms ?? {})))
|
|
154
|
+
fields.push({ label: name, value: text(value) });
|
|
155
|
+
if (!operation)
|
|
156
|
+
missing.push('the operation');
|
|
157
|
+
else
|
|
158
|
+
fields.push({ label: 'Operation', value: operation });
|
|
159
|
+
if (revision === undefined)
|
|
160
|
+
missing.push('its exact revision');
|
|
161
|
+
else
|
|
162
|
+
fields.push({ label: 'Revision', value: String(revision) });
|
|
163
|
+
if (typeof args.digest !== 'string' || !args.digest)
|
|
164
|
+
missing.push('the digest of its terms');
|
|
165
|
+
if (typeof args.class === 'string' && args.class)
|
|
166
|
+
fields.push({ label: 'What it does', value: args.class });
|
|
167
|
+
const was = args.was && typeof args.was === 'object'
|
|
168
|
+
? args.was
|
|
169
|
+
: undefined;
|
|
170
|
+
if (was && was.revision !== undefined)
|
|
171
|
+
fields.push({ label: 'Replaces revision', value: text(was.revision) });
|
|
172
|
+
if (missing.length)
|
|
173
|
+
extra.push(`The request does not say ${missing.join(', ')}: review it in Datalayer.`);
|
|
174
|
+
return rendered('pap-confirm', presentation, fields, PAP_CONFIRM_KEYS, extra);
|
|
175
|
+
},
|
|
176
|
+
};
|
|
101
177
|
/** Everything but mail: shown, decided only in Datalayer. */
|
|
102
178
|
const datalayerOnly = {
|
|
103
179
|
name: 'datalayer-only',
|
|
@@ -122,8 +198,11 @@ const datalayerOnly = {
|
|
|
122
198
|
};
|
|
123
199
|
},
|
|
124
200
|
};
|
|
125
|
-
/**
|
|
126
|
-
|
|
201
|
+
/**
|
|
202
|
+
* The renderers, the first that matches wins: a company's `pap_confirm`
|
|
203
|
+
* (whose name says no class), then Datalayer-only before any other.
|
|
204
|
+
*/
|
|
205
|
+
export const RENDERERS = [papConfirm, datalayerOnly, mail];
|
|
127
206
|
/**
|
|
128
207
|
* How a host shows an approval, by the renderer that knows its tool; with
|
|
129
208
|
* none, nothing is decided in the host.
|
|
@@ -104,6 +104,8 @@ export const exchangeSchema = z.object({
|
|
|
104
104
|
expires_in: z.number().int().nonnegative(),
|
|
105
105
|
exp: z.number().int(),
|
|
106
106
|
principal_uid: z.string().min(1),
|
|
107
|
+
/** A person who never linked, answered as a visitor (WHATSAPP WA-03): `principal_uid` is `visitor:<hash>`. */
|
|
108
|
+
visitor: z.boolean().optional(),
|
|
107
109
|
host_uid: z.string().min(1),
|
|
108
110
|
deployment: z.object({
|
|
109
111
|
uid: z.string().min(1),
|
|
@@ -114,6 +114,45 @@
|
|
|
114
114
|
"direct"
|
|
115
115
|
]
|
|
116
116
|
},
|
|
117
|
+
"visitor_exchange": {
|
|
118
|
+
"about": "WHATSAPP WA-03 (decided 28 October 2026): a host's user who never linked, answered as a visitor when the installation's owner turned its visitors switch on and the deployment opens to somebody signed out. sub is visitor:<hash of the host's id with the installation's salt>; no link; a daily cap per visitor at the exchange (429).",
|
|
119
|
+
"request": {
|
|
120
|
+
"external_user_id": "US.13491208655302741918",
|
|
121
|
+
"deployment_uid": "dep-1",
|
|
122
|
+
"visitor": true
|
|
123
|
+
},
|
|
124
|
+
"response_keys": [
|
|
125
|
+
"success",
|
|
126
|
+
"access_token",
|
|
127
|
+
"token_type",
|
|
128
|
+
"expires_in",
|
|
129
|
+
"exp",
|
|
130
|
+
"principal_uid",
|
|
131
|
+
"visitor",
|
|
132
|
+
"host_uid",
|
|
133
|
+
"deployment",
|
|
134
|
+
"session_api"
|
|
135
|
+
],
|
|
136
|
+
"token_claims": [
|
|
137
|
+
"jti",
|
|
138
|
+
"iss",
|
|
139
|
+
"iat",
|
|
140
|
+
"exp",
|
|
141
|
+
"aud",
|
|
142
|
+
"sub",
|
|
143
|
+
"azp",
|
|
144
|
+
"host_kind",
|
|
145
|
+
"deployment_uid",
|
|
146
|
+
"app_uid",
|
|
147
|
+
"visitor",
|
|
148
|
+
"scope"
|
|
149
|
+
],
|
|
150
|
+
"sub_prefix": "visitor:",
|
|
151
|
+
"refusals": {
|
|
152
|
+
"visitors_off": 403,
|
|
153
|
+
"capped": 429
|
|
154
|
+
}
|
|
155
|
+
},
|
|
117
156
|
"whoami": {
|
|
118
157
|
"response_keys": [
|
|
119
158
|
"success",
|
|
@@ -124,7 +163,8 @@
|
|
|
124
163
|
"app_uid",
|
|
125
164
|
"link",
|
|
126
165
|
"scope",
|
|
127
|
-
"expires_in"
|
|
166
|
+
"expires_in",
|
|
167
|
+
"visitor"
|
|
128
168
|
]
|
|
129
169
|
},
|
|
130
170
|
"bind_session": {
|
|
@@ -328,7 +368,21 @@
|
|
|
328
368
|
"host_may_decide": true,
|
|
329
369
|
"reasons": [],
|
|
330
370
|
"digest": "0000000000000000000000000000000000000000000000000000000000000000"
|
|
331
|
-
}
|
|
371
|
+
},
|
|
372
|
+
"pap_confirm_keys": [
|
|
373
|
+
"key",
|
|
374
|
+
"company",
|
|
375
|
+
"summary",
|
|
376
|
+
"terms",
|
|
377
|
+
"operation_ref",
|
|
378
|
+
"revision",
|
|
379
|
+
"digest",
|
|
380
|
+
"class",
|
|
381
|
+
"session",
|
|
382
|
+
"only_the_person",
|
|
383
|
+
"was"
|
|
384
|
+
],
|
|
385
|
+
"pap_confirm_about": "WHATSAPP WA-43 (decided 28 October 2026): a company's pap_confirm (any gateway prefix) is decided in a host, on its exact revision; every other non-mail action stays in Datalayer."
|
|
332
386
|
},
|
|
333
387
|
"decision": {
|
|
334
388
|
"request": {
|
package/package.json
CHANGED
|
@@ -356,18 +356,6 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
356
356
|
|
|
357
357
|
def update_init_file(specs: list[dict[str, Any]], init_file: Path) -> None:
|
|
358
358
|
"""Update the __init__.py file with correct imports based on generated specs."""
|
|
359
|
-
# Generate list of MCP server constant names
|
|
360
|
-
server_constants = []
|
|
361
|
-
for spec in specs:
|
|
362
|
-
server_id = spec["id"]
|
|
363
|
-
version = spec.get("version", "0.0.1")
|
|
364
|
-
const_name = (
|
|
365
|
-
server_id.upper().replace("-", "_")
|
|
366
|
-
+ "_MCP_SERVER"
|
|
367
|
-
+ version_suffix(version)
|
|
368
|
-
)
|
|
369
|
-
server_constants.append(const_name)
|
|
370
|
-
|
|
371
359
|
# Read the current __init__.py
|
|
372
360
|
init_content = init_file.read_text()
|
|
373
361
|
|
|
@@ -386,9 +374,12 @@ def update_init_file(specs: list[dict[str, Any]], init_file: Path) -> None:
|
|
|
386
374
|
return
|
|
387
375
|
|
|
388
376
|
# Generate new import lines - all names sorted alphabetically (ruff/isort order)
|
|
377
|
+
# Only the catalogue and its readers are re-exported: entries are read by
|
|
378
|
+
# id, never by a version-named constant (decided 14 October 2026), so the
|
|
379
|
+
# *_MCP_SERVER_<version> constants stay in catalog_mcp_servers alone and a
|
|
380
|
+
# version bump touches no caller (test_catalogue_by_id.py holds it).
|
|
389
381
|
all_names = sorted(
|
|
390
|
-
|
|
391
|
-
+ [
|
|
382
|
+
[
|
|
392
383
|
"MCP_SERVER_CATALOG",
|
|
393
384
|
"check_env_vars_available",
|
|
394
385
|
"get_catalog_server",
|
|
@@ -423,8 +414,6 @@ def update_init_file(specs: list[dict[str, Any]], init_file: Path) -> None:
|
|
|
423
414
|
' "get_catalog_server",',
|
|
424
415
|
' "list_catalog_servers",',
|
|
425
416
|
]
|
|
426
|
-
for const in sorted(server_constants):
|
|
427
|
-
all_entries.append(f' "{const}",')
|
|
428
417
|
all_entries.append("]")
|
|
429
418
|
|
|
430
419
|
# Replace the section
|