sello 0.1.16 → 0.1.17

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/README.md CHANGED
@@ -104,11 +104,13 @@ SELLO_SUBMIT_MODE=background
104
104
 
105
105
  Sello works with your own log server. Using `sello.build` is an optional convenience, not a protocol requirement.
106
106
 
107
- To scaffold a tiny emitter or HTTP route:
107
+ To scaffold a tiny demo for each boundary:
108
108
 
109
109
  ```bash
110
110
  npx --yes sello init-demo
111
111
  npx --yes sello init-http-demo
112
+ npx --yes sello init-mcp-demo
113
+ npx --yes sello init-a2a-demo
112
114
  ```
113
115
 
114
116
  ## Add Sello to an MCP Server
@@ -166,6 +168,15 @@ By default, Sello hashes the A2A JSON-RPC `method` and `params`, excluding reque
166
168
 
167
169
  See [docs/a2a.md](docs/a2a.md) for token handling, hash boundaries, unknown-method behavior, and action viewing.
168
170
 
171
+ ## Examples
172
+
173
+ | What you want to wire | Start here |
174
+ | --- | --- |
175
+ | A normal tool handler | [SDK Quickstart](docs/sdk-quickstart.md) and [`quickstart-tool.ts`](sdks/typescript/examples/quickstart-tool.ts) |
176
+ | An MCP tool | `npx --yes sello init-mcp-demo`, [MCP Integration](docs/mcp.md), and [`mcp-minimal-server.ts`](sdks/typescript/examples/mcp-minimal-server.ts) |
177
+ | An A2A message handler | `npx --yes sello init-a2a-demo`, [A2A Integration](docs/a2a.md), and [`a2a-minimal-server.ts`](sdks/typescript/examples/a2a-minimal-server.ts) |
178
+ | Verified action viewing | `npx sello actions` or `http://localhost:8787/actions` |
179
+
169
180
  ## See Logged Actions
170
181
 
171
182
  ```bash
@@ -207,8 +218,6 @@ Sello does not prove that the agent called every service it should have called,
207
218
  - [Protocol Walkthrough](docs/protocol-walkthrough.md): the primitive receipt loop for implementers.
208
219
  - [SPEC.md](SPEC.md): the Sello protocol draft.
209
220
  - [Notarized Agents paper](https://arxiv.org/abs/2606.04193): design rationale, threat model, and prior art.
210
- - [sdks/typescript/examples/mcp-minimal-server.ts](sdks/typescript/examples/mcp-minimal-server.ts): a small MCP integration.
211
- - [sdks/typescript/examples/a2a-minimal-server.ts](sdks/typescript/examples/a2a-minimal-server.ts): a small A2A integration.
212
221
  - [docs/security-review.md](docs/security-review.md) and [docs/sdk-security-audit.md](docs/sdk-security-audit.md): current review notes.
213
222
 
214
223
  The TypeScript SDK is published on [npm](https://www.npmjs.com/package/sello) and lives in [`sdks/typescript/`](sdks/typescript/). The Python SDK is published on [PyPI](https://pypi.org/project/sello/) and lives in [`sdks/python/`](sdks/python/). Both SDKs support the same service-side `sello.service()` flow; Python uses the `@receipts.tool(...)` decorator. MCP and A2A helpers are currently TypeScript-first. Live Rekor proof verification and production identity operations are still future work.
package/dist/cli/sello.js CHANGED
@@ -96,6 +96,12 @@ try {
96
96
  case "init-http-demo":
97
97
  initHttpDemoCommand(process.argv.slice(3));
98
98
  break;
99
+ case "init-mcp-demo":
100
+ initMcpDemoCommand(process.argv.slice(3));
101
+ break;
102
+ case "init-a2a-demo":
103
+ initA2aDemoCommand(process.argv.slice(3));
104
+ break;
99
105
  case "keys":
100
106
  keysCommand(process.argv.slice(3));
101
107
  break;
@@ -337,13 +343,13 @@ function initDemoCommand(args ) {
337
343
  console.log(`Created ${output}`);
338
344
  console.log("");
339
345
  console.log("Terminal 1: keep the local dev log running");
340
- console.log(" npx sello dev");
346
+ console.log(` ${devCommandHint()}`);
341
347
  console.log("");
342
348
  console.log("Terminal 2: emit and view a receipt");
343
349
  console.log(` node ${output}`);
344
350
  console.log(" npx sello actions");
345
351
  console.log("");
346
- console.log("Then open http://localhost:8787/actions");
352
+ console.log(`Then open ${actionViewerUrlHint()}`);
347
353
  }
348
354
 
349
355
  function initHttpDemoCommand(args ) {
@@ -359,7 +365,7 @@ function initHttpDemoCommand(args ) {
359
365
  console.log(`Created ${output}`);
360
366
  console.log("");
361
367
  console.log("Terminal 1: keep the local dev log running");
362
- console.log(" npx sello dev");
368
+ console.log(` ${devCommandHint()}`);
363
369
  console.log("");
364
370
  console.log("Terminal 2: start the route");
365
371
  console.log(` node ${output}`);
@@ -368,7 +374,51 @@ function initHttpDemoCommand(args ) {
368
374
  console.log(" npx sello call-http-demo");
369
375
  console.log(" npx sello actions");
370
376
  console.log("");
371
- console.log("Then open http://localhost:8787/actions");
377
+ console.log(`Then open ${actionViewerUrlHint()}`);
378
+ }
379
+
380
+ function initMcpDemoCommand(args ) {
381
+ const output = readFlag(args, "--output") ?? "sello-mcp-demo.mjs";
382
+ const force = args.includes("--force");
383
+
384
+ if (existsSync(output) && !force) {
385
+ throw new TypeError(`${output} already exists. Pass --force to overwrite it.`);
386
+ }
387
+
388
+ writeFileSync(output, mcpDemoSource(), { mode: 0o644 });
389
+
390
+ console.log(`Created ${output}`);
391
+ console.log("");
392
+ console.log("Terminal 1: keep the local dev log running");
393
+ console.log(` ${devCommandHint()}`);
394
+ console.log("");
395
+ console.log("Terminal 2: run the MCP-shaped tool call and view the receipt");
396
+ console.log(` node ${output}`);
397
+ console.log(" npx sello actions");
398
+ console.log("");
399
+ console.log(`Then open ${actionViewerUrlHint()}`);
400
+ }
401
+
402
+ function initA2aDemoCommand(args ) {
403
+ const output = readFlag(args, "--output") ?? "sello-a2a-demo.mjs";
404
+ const force = args.includes("--force");
405
+
406
+ if (existsSync(output) && !force) {
407
+ throw new TypeError(`${output} already exists. Pass --force to overwrite it.`);
408
+ }
409
+
410
+ writeFileSync(output, a2aDemoSource(), { mode: 0o644 });
411
+
412
+ console.log(`Created ${output}`);
413
+ console.log("");
414
+ console.log("Terminal 1: keep the local dev log running");
415
+ console.log(` ${devCommandHint()}`);
416
+ console.log("");
417
+ console.log("Terminal 2: run the A2A-shaped message and view the receipt");
418
+ console.log(` node ${output}`);
419
+ console.log(" npx sello actions");
420
+ console.log("");
421
+ console.log(`Then open ${actionViewerUrlHint()}`);
372
422
  }
373
423
 
374
424
  function keysCommand(args ) {
@@ -851,6 +901,8 @@ function printHelp() {
851
901
  sello call-http-demo [--url http://localhost:8790/calendar/events] [--title title]
852
902
  sello init-demo [--output emit-receipt.mjs] [--force]
853
903
  sello init-http-demo [--output sello-http-route.mjs] [--force]
904
+ sello init-mcp-demo [--output sello-mcp-demo.mjs] [--force]
905
+ sello init-a2a-demo [--output sello-a2a-demo.mjs] [--force]
854
906
  sello actions [--token agent-token]
855
907
  sello keys service
856
908
  sello inspect-env
@@ -917,6 +969,25 @@ function formatHttpDemoResponse(responseText ) {
917
969
  }
918
970
  }
919
971
 
972
+ function devCommandHint() {
973
+ const state = loadDevStateIfPresent();
974
+ const port = state ? portFromDevState(state) : undefined;
975
+ return port && port !== "8787" ? `npx sello dev --port ${port}` : "npx sello dev";
976
+ }
977
+
978
+ function actionViewerUrlHint() {
979
+ const state = loadDevStateIfPresent();
980
+ return state ? actionViewerUrl(state) : "http://localhost:8787/actions";
981
+ }
982
+
983
+ function portFromDevState(state ) {
984
+ try {
985
+ return new URL(state.logEndpoint).port;
986
+ } catch {
987
+ return undefined;
988
+ }
989
+ }
990
+
920
991
  function slug(value ) {
921
992
  return value
922
993
  .toLowerCase()
@@ -1080,3 +1151,124 @@ function slug(value) {
1080
1151
  }
1081
1152
  `;
1082
1153
  }
1154
+
1155
+ function mcpDemoSource() {
1156
+ return `import { readFileSync } from "node:fs";
1157
+ import { sello } from "sello";
1158
+
1159
+ const state = JSON.parse(readFileSync(".sello/dev.json", "utf8"));
1160
+
1161
+ const receipts = sello.service({
1162
+ service: state.serviceId,
1163
+ serviceKey: state.serviceKey,
1164
+ tokenIssuer: state.tokenIssuerPublicKey,
1165
+ log: sello.logs.http(state.logUrl, { endpoint: state.logEndpoint }),
1166
+ submit: { mode: "await" },
1167
+ });
1168
+
1169
+ const createEvent = receipts.mcpTool("calendar.create_event", async (args) => {
1170
+ const title = readString(args.title, "title");
1171
+ return {
1172
+ content: [
1173
+ {
1174
+ type: "text",
1175
+ text: "created " + title,
1176
+ },
1177
+ ],
1178
+ };
1179
+ });
1180
+
1181
+ const response = await createEvent(
1182
+ {
1183
+ title: "MCP launch checklist",
1184
+ },
1185
+ {
1186
+ requestInfo: {
1187
+ headers: new Headers({
1188
+ authorization: "Bearer " + state.agentToken,
1189
+ }),
1190
+ },
1191
+ },
1192
+ );
1193
+
1194
+ await receipts.flush();
1195
+
1196
+ console.log("MCP tool response:");
1197
+ console.log(JSON.stringify(response, null, 2));
1198
+
1199
+ function readString(value, name) {
1200
+ if (typeof value !== "string" || value.length === 0) {
1201
+ throw new TypeError(name + " must be a non-empty string");
1202
+ }
1203
+ return value;
1204
+ }
1205
+ `;
1206
+ }
1207
+
1208
+ function a2aDemoSource() {
1209
+ return `import { readFileSync } from "node:fs";
1210
+ import { sello } from "sello";
1211
+
1212
+ const state = JSON.parse(readFileSync(".sello/dev.json", "utf8"));
1213
+
1214
+ const receipts = sello.service({
1215
+ service: state.serviceId,
1216
+ serviceKey: state.serviceKey,
1217
+ tokenIssuer: state.tokenIssuerPublicKey,
1218
+ log: sello.logs.http(state.logUrl, { endpoint: state.logEndpoint }),
1219
+ submit: { mode: "await" },
1220
+ });
1221
+
1222
+ const sendMessage = receipts.a2aMessage(async (request) => ({
1223
+ jsonrpc: "2.0",
1224
+ id: request.id,
1225
+ result: {
1226
+ kind: "message",
1227
+ messageId: "calendar-reply-1",
1228
+ role: "agent",
1229
+ parts: [
1230
+ {
1231
+ kind: "text",
1232
+ text: "created " + readMessageText(request),
1233
+ },
1234
+ ],
1235
+ },
1236
+ }));
1237
+
1238
+ const response = await sendMessage(
1239
+ {
1240
+ jsonrpc: "2.0",
1241
+ id: "a2a-demo-1",
1242
+ method: "message/send",
1243
+ params: {
1244
+ message: {
1245
+ role: "user",
1246
+ parts: [
1247
+ {
1248
+ kind: "text",
1249
+ text: "A2A launch checklist",
1250
+ },
1251
+ ],
1252
+ },
1253
+ },
1254
+ },
1255
+ {
1256
+ headers: new Headers({
1257
+ authorization: "Bearer " + state.agentToken,
1258
+ }),
1259
+ },
1260
+ );
1261
+
1262
+ await receipts.flush();
1263
+
1264
+ console.log("A2A message response:");
1265
+ console.log(JSON.stringify(response, null, 2));
1266
+
1267
+ function readMessageText(request) {
1268
+ return request.params.message.parts
1269
+ .filter((part) => part.kind === "text")
1270
+ .map((part) => part.text)
1271
+ .join(" ") || "untitled";
1272
+ }
1273
+ `;
1274
+ }
package/docs/a2a.md CHANGED
@@ -122,6 +122,18 @@ For a specific agent token:
122
122
  npx sello actions --token <agent-token>
123
123
  ```
124
124
 
125
+ ## Runnable Demo
126
+
127
+ To generate a tiny A2A-shaped demo in your project:
128
+
129
+ ```bash
130
+ npx --yes sello init-a2a-demo
131
+ node sello-a2a-demo.mjs
132
+ npx --yes sello actions
133
+ ```
134
+
135
+ Keep `npx sello dev` running in another terminal while you run the generated file.
136
+
125
137
  ## Example
126
138
 
127
139
  See [`sdks/typescript/examples/a2a-minimal-server.ts`](../sdks/typescript/examples/a2a-minimal-server.ts) for a dependency-free A2A-shaped example. It wraps one `message/send` handler and leaves unknown methods unreceipted.
package/docs/mcp.md CHANGED
@@ -132,6 +132,18 @@ For a specific agent token:
132
132
  npx sello actions --token <agent-token>
133
133
  ```
134
134
 
135
+ ## Runnable Demo
136
+
137
+ To generate a tiny MCP-shaped demo in your project:
138
+
139
+ ```bash
140
+ npx --yes sello init-mcp-demo
141
+ node sello-mcp-demo.mjs
142
+ npx --yes sello actions
143
+ ```
144
+
145
+ Keep `npx sello dev` running in another terminal while you run the generated file.
146
+
135
147
  ## Example
136
148
 
137
149
  See [`sdks/typescript/examples/mcp-minimal-server.ts`](../sdks/typescript/examples/mcp-minimal-server.ts) for a dependency-free MCP-shaped example. It wraps one `tools/call` handler and leaves unknown tools unreceipted.
@@ -72,18 +72,43 @@ npx --yes sello call-http-demo
72
72
  npx --yes sello actions
73
73
  ```
74
74
 
75
+ To try the same local loop around MCP or A2A-shaped handlers:
76
+
77
+ ```bash
78
+ npx --yes sello init-mcp-demo
79
+ node sello-mcp-demo.mjs
80
+ npx --yes sello actions
81
+ ```
82
+
83
+ ```bash
84
+ npx --yes sello init-a2a-demo
85
+ node sello-a2a-demo.mjs
86
+ npx --yes sello actions
87
+ ```
88
+
75
89
  ## What Just Happened?
76
90
 
77
- `sello dev` created local development keys, a demo authorization token, a service registry, and a local transparency log. The wrapped tool or route verified the token before running your handler. After the handler returned, Sello signed an encrypted receipt for the observed action and submitted it to the local log. The log stored encrypted receipt data, not plaintext action details. `sello actions` used the owner key from local dev state to fetch, verify, decrypt, and print the action.
91
+ `sello dev` created local development keys, a demo authorization token, a service registry, and a local transparency log. The wrapped function, route, MCP tool, or A2A message callback verified the token before running your code. After your code returned, Sello signed an encrypted receipt for the observed action and submitted it to the local log. The log stored encrypted receipt data, not plaintext action details. `sello actions` used the owner key from local dev state to fetch, verify, decrypt, and print the action.
78
92
 
79
93
  Local dev state lives under `.sello/`. The encrypted dev log is stored in `.sello/dev-log.jsonl`, so receipts survive restarting `sello dev` without being committed to git.
80
94
 
95
+ ## Which Wrapper?
96
+
97
+ | Existing boundary | Use |
98
+ | --- | --- |
99
+ | Plain function or HTTP route | `receipts.tool("calendar.create_event", handler)` |
100
+ | MCP `tools/call` callback | `receipts.mcpTool("calendar.create_event", handler)` |
101
+ | A2A JSON-RPC message handler | `receipts.a2aMessage(handler)` |
102
+ | Owner action viewing | `npx sello actions` |
103
+
81
104
  ## Troubleshooting
82
105
 
83
106
  - **Port already in use:** run `npx sello dev --port 8791`.
84
107
  - **No actions found:** make sure `sello dev` is running from the same project folder where you emitted the receipt.
85
108
  - **Missing token:** run `npx sello dev` first so `.sello/dev.json` exists.
86
109
 
110
+ ## Contributing From This Repo
111
+
87
112
  Inside this repo, start the local log and action viewer:
88
113
 
89
114
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sello",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Reference implementation of the Sello protocol for service-signed AI agent receipts.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -97,6 +97,12 @@ try {
97
97
  case "init-http-demo":
98
98
  initHttpDemoCommand(process.argv.slice(3));
99
99
  break;
100
+ case "init-mcp-demo":
101
+ initMcpDemoCommand(process.argv.slice(3));
102
+ break;
103
+ case "init-a2a-demo":
104
+ initA2aDemoCommand(process.argv.slice(3));
105
+ break;
100
106
  case "keys":
101
107
  keysCommand(process.argv.slice(3));
102
108
  break;
@@ -338,13 +344,13 @@ function initDemoCommand(args: string[]): void {
338
344
  console.log(`Created ${output}`);
339
345
  console.log("");
340
346
  console.log("Terminal 1: keep the local dev log running");
341
- console.log(" npx sello dev");
347
+ console.log(` ${devCommandHint()}`);
342
348
  console.log("");
343
349
  console.log("Terminal 2: emit and view a receipt");
344
350
  console.log(` node ${output}`);
345
351
  console.log(" npx sello actions");
346
352
  console.log("");
347
- console.log("Then open http://localhost:8787/actions");
353
+ console.log(`Then open ${actionViewerUrlHint()}`);
348
354
  }
349
355
 
350
356
  function initHttpDemoCommand(args: string[]): void {
@@ -360,7 +366,7 @@ function initHttpDemoCommand(args: string[]): void {
360
366
  console.log(`Created ${output}`);
361
367
  console.log("");
362
368
  console.log("Terminal 1: keep the local dev log running");
363
- console.log(" npx sello dev");
369
+ console.log(` ${devCommandHint()}`);
364
370
  console.log("");
365
371
  console.log("Terminal 2: start the route");
366
372
  console.log(` node ${output}`);
@@ -369,7 +375,51 @@ function initHttpDemoCommand(args: string[]): void {
369
375
  console.log(" npx sello call-http-demo");
370
376
  console.log(" npx sello actions");
371
377
  console.log("");
372
- console.log("Then open http://localhost:8787/actions");
378
+ console.log(`Then open ${actionViewerUrlHint()}`);
379
+ }
380
+
381
+ function initMcpDemoCommand(args: string[]): void {
382
+ const output = readFlag(args, "--output") ?? "sello-mcp-demo.mjs";
383
+ const force = args.includes("--force");
384
+
385
+ if (existsSync(output) && !force) {
386
+ throw new TypeError(`${output} already exists. Pass --force to overwrite it.`);
387
+ }
388
+
389
+ writeFileSync(output, mcpDemoSource(), { mode: 0o644 });
390
+
391
+ console.log(`Created ${output}`);
392
+ console.log("");
393
+ console.log("Terminal 1: keep the local dev log running");
394
+ console.log(` ${devCommandHint()}`);
395
+ console.log("");
396
+ console.log("Terminal 2: run the MCP-shaped tool call and view the receipt");
397
+ console.log(` node ${output}`);
398
+ console.log(" npx sello actions");
399
+ console.log("");
400
+ console.log(`Then open ${actionViewerUrlHint()}`);
401
+ }
402
+
403
+ function initA2aDemoCommand(args: string[]): void {
404
+ const output = readFlag(args, "--output") ?? "sello-a2a-demo.mjs";
405
+ const force = args.includes("--force");
406
+
407
+ if (existsSync(output) && !force) {
408
+ throw new TypeError(`${output} already exists. Pass --force to overwrite it.`);
409
+ }
410
+
411
+ writeFileSync(output, a2aDemoSource(), { mode: 0o644 });
412
+
413
+ console.log(`Created ${output}`);
414
+ console.log("");
415
+ console.log("Terminal 1: keep the local dev log running");
416
+ console.log(` ${devCommandHint()}`);
417
+ console.log("");
418
+ console.log("Terminal 2: run the A2A-shaped message and view the receipt");
419
+ console.log(` node ${output}`);
420
+ console.log(" npx sello actions");
421
+ console.log("");
422
+ console.log(`Then open ${actionViewerUrlHint()}`);
373
423
  }
374
424
 
375
425
  function keysCommand(args: string[]): void {
@@ -852,6 +902,8 @@ function printHelp(): void {
852
902
  sello call-http-demo [--url http://localhost:8790/calendar/events] [--title title]
853
903
  sello init-demo [--output emit-receipt.mjs] [--force]
854
904
  sello init-http-demo [--output sello-http-route.mjs] [--force]
905
+ sello init-mcp-demo [--output sello-mcp-demo.mjs] [--force]
906
+ sello init-a2a-demo [--output sello-a2a-demo.mjs] [--force]
855
907
  sello actions [--token agent-token]
856
908
  sello keys service
857
909
  sello inspect-env
@@ -918,6 +970,25 @@ function formatHttpDemoResponse(responseText: string): string {
918
970
  }
919
971
  }
920
972
 
973
+ function devCommandHint(): string {
974
+ const state = loadDevStateIfPresent();
975
+ const port = state ? portFromDevState(state) : undefined;
976
+ return port && port !== "8787" ? `npx sello dev --port ${port}` : "npx sello dev";
977
+ }
978
+
979
+ function actionViewerUrlHint(): string {
980
+ const state = loadDevStateIfPresent();
981
+ return state ? actionViewerUrl(state) : "http://localhost:8787/actions";
982
+ }
983
+
984
+ function portFromDevState(state: DevState): string | undefined {
985
+ try {
986
+ return new URL(state.logEndpoint).port;
987
+ } catch {
988
+ return undefined;
989
+ }
990
+ }
991
+
921
992
  function slug(value: string): string {
922
993
  return value
923
994
  .toLowerCase()
@@ -1081,3 +1152,124 @@ function slug(value) {
1081
1152
  }
1082
1153
  `;
1083
1154
  }
1155
+
1156
+ function mcpDemoSource(): string {
1157
+ return `import { readFileSync } from "node:fs";
1158
+ import { sello } from "sello";
1159
+
1160
+ const state = JSON.parse(readFileSync(".sello/dev.json", "utf8"));
1161
+
1162
+ const receipts = sello.service({
1163
+ service: state.serviceId,
1164
+ serviceKey: state.serviceKey,
1165
+ tokenIssuer: state.tokenIssuerPublicKey,
1166
+ log: sello.logs.http(state.logUrl, { endpoint: state.logEndpoint }),
1167
+ submit: { mode: "await" },
1168
+ });
1169
+
1170
+ const createEvent = receipts.mcpTool("calendar.create_event", async (args) => {
1171
+ const title = readString(args.title, "title");
1172
+ return {
1173
+ content: [
1174
+ {
1175
+ type: "text",
1176
+ text: "created " + title,
1177
+ },
1178
+ ],
1179
+ };
1180
+ });
1181
+
1182
+ const response = await createEvent(
1183
+ {
1184
+ title: "MCP launch checklist",
1185
+ },
1186
+ {
1187
+ requestInfo: {
1188
+ headers: new Headers({
1189
+ authorization: "Bearer " + state.agentToken,
1190
+ }),
1191
+ },
1192
+ },
1193
+ );
1194
+
1195
+ await receipts.flush();
1196
+
1197
+ console.log("MCP tool response:");
1198
+ console.log(JSON.stringify(response, null, 2));
1199
+
1200
+ function readString(value, name) {
1201
+ if (typeof value !== "string" || value.length === 0) {
1202
+ throw new TypeError(name + " must be a non-empty string");
1203
+ }
1204
+ return value;
1205
+ }
1206
+ `;
1207
+ }
1208
+
1209
+ function a2aDemoSource(): string {
1210
+ return `import { readFileSync } from "node:fs";
1211
+ import { sello } from "sello";
1212
+
1213
+ const state = JSON.parse(readFileSync(".sello/dev.json", "utf8"));
1214
+
1215
+ const receipts = sello.service({
1216
+ service: state.serviceId,
1217
+ serviceKey: state.serviceKey,
1218
+ tokenIssuer: state.tokenIssuerPublicKey,
1219
+ log: sello.logs.http(state.logUrl, { endpoint: state.logEndpoint }),
1220
+ submit: { mode: "await" },
1221
+ });
1222
+
1223
+ const sendMessage = receipts.a2aMessage(async (request) => ({
1224
+ jsonrpc: "2.0",
1225
+ id: request.id,
1226
+ result: {
1227
+ kind: "message",
1228
+ messageId: "calendar-reply-1",
1229
+ role: "agent",
1230
+ parts: [
1231
+ {
1232
+ kind: "text",
1233
+ text: "created " + readMessageText(request),
1234
+ },
1235
+ ],
1236
+ },
1237
+ }));
1238
+
1239
+ const response = await sendMessage(
1240
+ {
1241
+ jsonrpc: "2.0",
1242
+ id: "a2a-demo-1",
1243
+ method: "message/send",
1244
+ params: {
1245
+ message: {
1246
+ role: "user",
1247
+ parts: [
1248
+ {
1249
+ kind: "text",
1250
+ text: "A2A launch checklist",
1251
+ },
1252
+ ],
1253
+ },
1254
+ },
1255
+ },
1256
+ {
1257
+ headers: new Headers({
1258
+ authorization: "Bearer " + state.agentToken,
1259
+ }),
1260
+ },
1261
+ );
1262
+
1263
+ await receipts.flush();
1264
+
1265
+ console.log("A2A message response:");
1266
+ console.log(JSON.stringify(response, null, 2));
1267
+
1268
+ function readMessageText(request) {
1269
+ return request.params.message.parts
1270
+ .filter((part) => part.kind === "text")
1271
+ .map((part) => part.text)
1272
+ .join(" ") || "untitled";
1273
+ }
1274
+ `;
1275
+ }