@noodleseed/agent-kit 0.22.0 → 0.23.1
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/manifest.json +35 -35
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +1 -1
- package/skills/claude-code/examples/acme-bistro/src/server.ts +1 -1
- package/skills/claude-code/examples/acme-discovery/src/server.ts +1 -1
- package/skills/claude-code/examples/acme-tasks/src/server.ts +4 -2
- package/skills/claude-code/examples/acme-tasks/test/server.test.ts +9 -0
- package/skills/claude-code/examples/food-ordering/README.md +7 -3
- package/skills/claude-code/examples/food-ordering/src/helpers.ts +2 -0
- package/skills/claude-code/examples/food-ordering/src/server.ts +47 -2
- package/skills/claude-code/examples/food-ordering/src/views/ordering-flow.tsx +56 -3
- package/skills/claude-code/examples/food-ordering/test/server.test.ts +46 -0
- package/skills/claude-code/references/authoring-workflow.md +46 -1
- package/skills/claude-code/references/cli-commands.md +1 -0
- package/skills/claude-code/references/compile-errors.md +6 -1
- package/skills/claude-code/references/deploy-and-ops.md +5 -0
- package/skills/claude-code/references/embedded-assistant.md +90 -9
- package/skills/claude-code/references/sdk-surface.md +2 -2
- package/skills/claude-code/references/widgets-and-apps.md +32 -7
- package/skills/codex/SKILL.md +1 -1
- package/skills/codex/examples/acme-bistro/src/server.ts +1 -1
- package/skills/codex/examples/acme-discovery/src/server.ts +1 -1
- package/skills/codex/examples/acme-tasks/src/server.ts +4 -2
- package/skills/codex/examples/acme-tasks/test/server.test.ts +9 -0
- package/skills/codex/examples/food-ordering/README.md +7 -3
- package/skills/codex/examples/food-ordering/src/helpers.ts +2 -0
- package/skills/codex/examples/food-ordering/src/server.ts +47 -2
- package/skills/codex/examples/food-ordering/src/views/ordering-flow.tsx +56 -3
- package/skills/codex/examples/food-ordering/test/server.test.ts +46 -0
- package/skills/codex/references/authoring-workflow.md +46 -1
- package/skills/codex/references/cli-commands.md +1 -0
- package/skills/codex/references/compile-errors.md +6 -1
- package/skills/codex/references/deploy-and-ops.md +5 -0
- package/skills/codex/references/embedded-assistant.md +90 -9
- package/skills/codex/references/sdk-surface.md +2 -2
- package/skills/codex/references/widgets-and-apps.md +32 -7
package/manifest.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"packageVersion": "0.
|
|
2
|
+
"packageVersion": "0.23.1",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"path": "skills/codex/SKILL.md",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "92202210906f0c056e4e34c169be95e4c52dd73c2c11f8074b60bacc4e24af28",
|
|
7
7
|
"agentTarget": "codex"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"path": "skills/codex/references/sdk-surface.md",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "23b901198b1d6a2cb5c140810b2fc4d58b7008c2afb2fdd57a8e2993333a7600",
|
|
12
12
|
"agentTarget": "codex"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "skills/codex/references/cli-commands.md",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "abcd6d76e1c9e22e3104b04ca2eb436a0ee19ee114648689a7ba43e164007b34",
|
|
17
17
|
"agentTarget": "codex"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "skills/codex/references/compile-errors.md",
|
|
26
|
-
"sha256": "
|
|
26
|
+
"sha256": "1ed667250f8f14625012bd8e931812c5d80bac323fa2f39a79ec83dc119fc385",
|
|
27
27
|
"agentTarget": "codex"
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"path": "skills/codex/references/authoring-workflow.md",
|
|
31
|
-
"sha256": "
|
|
31
|
+
"sha256": "3f4767bfe361090e172c7554f1121fe415e618f3a90304498535d177b0c633be",
|
|
32
32
|
"agentTarget": "codex"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "skills/codex/references/embedded-assistant.md",
|
|
36
|
-
"sha256": "
|
|
36
|
+
"sha256": "3e96c4f322cadbd509957781005060a8526c8e60e610bcb7246edc1e86349bf9",
|
|
37
37
|
"agentTarget": "codex"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"path": "skills/codex/references/widgets-and-apps.md",
|
|
51
|
-
"sha256": "
|
|
51
|
+
"sha256": "7e77eb1520883ad8bd0492fcefae97dfdb0e5bbe9351a7c554d19ee1f7c087cc",
|
|
52
52
|
"agentTarget": "codex"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
"path": "skills/codex/references/deploy-and-ops.md",
|
|
66
|
-
"sha256": "
|
|
66
|
+
"sha256": "d029cb301ae2d06337f573a356c98106f283a1ef8f5f905fb63fe4d4bdf5c599",
|
|
67
67
|
"agentTarget": "codex"
|
|
68
68
|
},
|
|
69
69
|
{
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"path": "skills/codex/examples/acme-bistro/src/server.ts",
|
|
121
|
-
"sha256": "
|
|
121
|
+
"sha256": "35b735b5451499dad7b5bba9f4a0e0e57cc579d1ca8bcdd75317a044ed72c91b",
|
|
122
122
|
"agentTarget": "codex"
|
|
123
123
|
},
|
|
124
124
|
{
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
"path": "skills/codex/examples/acme-discovery/src/server.ts",
|
|
176
|
-
"sha256": "
|
|
176
|
+
"sha256": "0d5a6c50a126b379ffcc67b5089665e079f2aa5f7ddb1c6a5a99d6a6d37669e4",
|
|
177
177
|
"agentTarget": "codex"
|
|
178
178
|
},
|
|
179
179
|
{
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
},
|
|
229
229
|
{
|
|
230
230
|
"path": "skills/codex/examples/acme-tasks/src/server.ts",
|
|
231
|
-
"sha256": "
|
|
231
|
+
"sha256": "769982ba567460af08d79517d6bf259ce4e8f7882cd3525fadbd5b22639e08ff",
|
|
232
232
|
"agentTarget": "codex"
|
|
233
233
|
},
|
|
234
234
|
{
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
},
|
|
244
244
|
{
|
|
245
245
|
"path": "skills/codex/examples/acme-tasks/test/server.test.ts",
|
|
246
|
-
"sha256": "
|
|
246
|
+
"sha256": "1a6c729cceae3cf5ac5fea9081eb61c686a0a72c8a90539e5b0dee4307a4ff81",
|
|
247
247
|
"agentTarget": "codex"
|
|
248
248
|
},
|
|
249
249
|
{
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
},
|
|
279
279
|
{
|
|
280
280
|
"path": "skills/codex/examples/food-ordering/README.md",
|
|
281
|
-
"sha256": "
|
|
281
|
+
"sha256": "1e8a08868efa70fe79bacc02d27effa5a65fbb5b497e76a7ce1268586ec559dc",
|
|
282
282
|
"agentTarget": "codex"
|
|
283
283
|
},
|
|
284
284
|
{
|
|
@@ -293,12 +293,12 @@
|
|
|
293
293
|
},
|
|
294
294
|
{
|
|
295
295
|
"path": "skills/codex/examples/food-ordering/src/helpers.ts",
|
|
296
|
-
"sha256": "
|
|
296
|
+
"sha256": "91f8dec747e4eb050c2e8dc8c22679443f22468c7fe7cf821e97f42eadb629e5",
|
|
297
297
|
"agentTarget": "codex"
|
|
298
298
|
},
|
|
299
299
|
{
|
|
300
300
|
"path": "skills/codex/examples/food-ordering/src/server.ts",
|
|
301
|
-
"sha256": "
|
|
301
|
+
"sha256": "55942f7b6424ea6019479ab2df98afd0d5c33e154acddaf6638cf78d144da639",
|
|
302
302
|
"agentTarget": "codex"
|
|
303
303
|
},
|
|
304
304
|
{
|
|
@@ -308,7 +308,7 @@
|
|
|
308
308
|
},
|
|
309
309
|
{
|
|
310
310
|
"path": "skills/codex/examples/food-ordering/src/views/ordering-flow.tsx",
|
|
311
|
-
"sha256": "
|
|
311
|
+
"sha256": "57690826caf1c7d4cd6da95a82fc8df3be1106f0de75716a3ccbb4dece2a953e",
|
|
312
312
|
"agentTarget": "codex"
|
|
313
313
|
},
|
|
314
314
|
{
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
},
|
|
319
319
|
{
|
|
320
320
|
"path": "skills/codex/examples/food-ordering/test/server.test.ts",
|
|
321
|
-
"sha256": "
|
|
321
|
+
"sha256": "6819457eed88b302d8369a24dc22581066d25c9092dca3a1141366665e246e52",
|
|
322
322
|
"agentTarget": "codex"
|
|
323
323
|
},
|
|
324
324
|
{
|
|
@@ -378,17 +378,17 @@
|
|
|
378
378
|
},
|
|
379
379
|
{
|
|
380
380
|
"path": "skills/claude-code/SKILL.md",
|
|
381
|
-
"sha256": "
|
|
381
|
+
"sha256": "a2729d4dd959a01b7ff39f375855c4e0026f95adae8d1390748c9f7c8198ef38",
|
|
382
382
|
"agentTarget": "claude-code"
|
|
383
383
|
},
|
|
384
384
|
{
|
|
385
385
|
"path": "skills/claude-code/references/sdk-surface.md",
|
|
386
|
-
"sha256": "
|
|
386
|
+
"sha256": "23b901198b1d6a2cb5c140810b2fc4d58b7008c2afb2fdd57a8e2993333a7600",
|
|
387
387
|
"agentTarget": "claude-code"
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
"path": "skills/claude-code/references/cli-commands.md",
|
|
391
|
-
"sha256": "
|
|
391
|
+
"sha256": "abcd6d76e1c9e22e3104b04ca2eb436a0ee19ee114648689a7ba43e164007b34",
|
|
392
392
|
"agentTarget": "claude-code"
|
|
393
393
|
},
|
|
394
394
|
{
|
|
@@ -398,17 +398,17 @@
|
|
|
398
398
|
},
|
|
399
399
|
{
|
|
400
400
|
"path": "skills/claude-code/references/compile-errors.md",
|
|
401
|
-
"sha256": "
|
|
401
|
+
"sha256": "1ed667250f8f14625012bd8e931812c5d80bac323fa2f39a79ec83dc119fc385",
|
|
402
402
|
"agentTarget": "claude-code"
|
|
403
403
|
},
|
|
404
404
|
{
|
|
405
405
|
"path": "skills/claude-code/references/authoring-workflow.md",
|
|
406
|
-
"sha256": "
|
|
406
|
+
"sha256": "3f4767bfe361090e172c7554f1121fe415e618f3a90304498535d177b0c633be",
|
|
407
407
|
"agentTarget": "claude-code"
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
"path": "skills/claude-code/references/embedded-assistant.md",
|
|
411
|
-
"sha256": "
|
|
411
|
+
"sha256": "3e96c4f322cadbd509957781005060a8526c8e60e610bcb7246edc1e86349bf9",
|
|
412
412
|
"agentTarget": "claude-code"
|
|
413
413
|
},
|
|
414
414
|
{
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
},
|
|
424
424
|
{
|
|
425
425
|
"path": "skills/claude-code/references/widgets-and-apps.md",
|
|
426
|
-
"sha256": "
|
|
426
|
+
"sha256": "7e77eb1520883ad8bd0492fcefae97dfdb0e5bbe9351a7c554d19ee1f7c087cc",
|
|
427
427
|
"agentTarget": "claude-code"
|
|
428
428
|
},
|
|
429
429
|
{
|
|
@@ -438,7 +438,7 @@
|
|
|
438
438
|
},
|
|
439
439
|
{
|
|
440
440
|
"path": "skills/claude-code/references/deploy-and-ops.md",
|
|
441
|
-
"sha256": "
|
|
441
|
+
"sha256": "d029cb301ae2d06337f573a356c98106f283a1ef8f5f905fb63fe4d4bdf5c599",
|
|
442
442
|
"agentTarget": "claude-code"
|
|
443
443
|
},
|
|
444
444
|
{
|
|
@@ -493,7 +493,7 @@
|
|
|
493
493
|
},
|
|
494
494
|
{
|
|
495
495
|
"path": "skills/claude-code/examples/acme-bistro/src/server.ts",
|
|
496
|
-
"sha256": "
|
|
496
|
+
"sha256": "35b735b5451499dad7b5bba9f4a0e0e57cc579d1ca8bcdd75317a044ed72c91b",
|
|
497
497
|
"agentTarget": "claude-code"
|
|
498
498
|
},
|
|
499
499
|
{
|
|
@@ -548,7 +548,7 @@
|
|
|
548
548
|
},
|
|
549
549
|
{
|
|
550
550
|
"path": "skills/claude-code/examples/acme-discovery/src/server.ts",
|
|
551
|
-
"sha256": "
|
|
551
|
+
"sha256": "0d5a6c50a126b379ffcc67b5089665e079f2aa5f7ddb1c6a5a99d6a6d37669e4",
|
|
552
552
|
"agentTarget": "claude-code"
|
|
553
553
|
},
|
|
554
554
|
{
|
|
@@ -603,7 +603,7 @@
|
|
|
603
603
|
},
|
|
604
604
|
{
|
|
605
605
|
"path": "skills/claude-code/examples/acme-tasks/src/server.ts",
|
|
606
|
-
"sha256": "
|
|
606
|
+
"sha256": "769982ba567460af08d79517d6bf259ce4e8f7882cd3525fadbd5b22639e08ff",
|
|
607
607
|
"agentTarget": "claude-code"
|
|
608
608
|
},
|
|
609
609
|
{
|
|
@@ -618,7 +618,7 @@
|
|
|
618
618
|
},
|
|
619
619
|
{
|
|
620
620
|
"path": "skills/claude-code/examples/acme-tasks/test/server.test.ts",
|
|
621
|
-
"sha256": "
|
|
621
|
+
"sha256": "1a6c729cceae3cf5ac5fea9081eb61c686a0a72c8a90539e5b0dee4307a4ff81",
|
|
622
622
|
"agentTarget": "claude-code"
|
|
623
623
|
},
|
|
624
624
|
{
|
|
@@ -653,7 +653,7 @@
|
|
|
653
653
|
},
|
|
654
654
|
{
|
|
655
655
|
"path": "skills/claude-code/examples/food-ordering/README.md",
|
|
656
|
-
"sha256": "
|
|
656
|
+
"sha256": "1e8a08868efa70fe79bacc02d27effa5a65fbb5b497e76a7ce1268586ec559dc",
|
|
657
657
|
"agentTarget": "claude-code"
|
|
658
658
|
},
|
|
659
659
|
{
|
|
@@ -668,12 +668,12 @@
|
|
|
668
668
|
},
|
|
669
669
|
{
|
|
670
670
|
"path": "skills/claude-code/examples/food-ordering/src/helpers.ts",
|
|
671
|
-
"sha256": "
|
|
671
|
+
"sha256": "91f8dec747e4eb050c2e8dc8c22679443f22468c7fe7cf821e97f42eadb629e5",
|
|
672
672
|
"agentTarget": "claude-code"
|
|
673
673
|
},
|
|
674
674
|
{
|
|
675
675
|
"path": "skills/claude-code/examples/food-ordering/src/server.ts",
|
|
676
|
-
"sha256": "
|
|
676
|
+
"sha256": "55942f7b6424ea6019479ab2df98afd0d5c33e154acddaf6638cf78d144da639",
|
|
677
677
|
"agentTarget": "claude-code"
|
|
678
678
|
},
|
|
679
679
|
{
|
|
@@ -683,7 +683,7 @@
|
|
|
683
683
|
},
|
|
684
684
|
{
|
|
685
685
|
"path": "skills/claude-code/examples/food-ordering/src/views/ordering-flow.tsx",
|
|
686
|
-
"sha256": "
|
|
686
|
+
"sha256": "57690826caf1c7d4cd6da95a82fc8df3be1106f0de75716a3ccbb4dece2a953e",
|
|
687
687
|
"agentTarget": "claude-code"
|
|
688
688
|
},
|
|
689
689
|
{
|
|
@@ -693,7 +693,7 @@
|
|
|
693
693
|
},
|
|
694
694
|
{
|
|
695
695
|
"path": "skills/claude-code/examples/food-ordering/test/server.test.ts",
|
|
696
|
-
"sha256": "
|
|
696
|
+
"sha256": "6819457eed88b302d8369a24dc22581066d25c9092dca3a1141366665e246e52",
|
|
697
697
|
"agentTarget": "claude-code"
|
|
698
698
|
},
|
|
699
699
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/agent-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Self-checking, self-updating agent skills for the Noodle Seed CLI. Authored in this repo by @noodle-borg/agent-kit; this is the published, independently-versioned canonical skills artifact the CLI fetches and verifies.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -29,7 +29,7 @@ const menuItemOutput = z.object({
|
|
|
29
29
|
// Tool annotations for host planners: the menu read is read-only; cart edits are local writes; checkout
|
|
30
30
|
// opens an external (payment) link.
|
|
31
31
|
const readOnly = annotations.readOnly();
|
|
32
|
-
const localWrite = annotations.localAction({ destructive: false });
|
|
32
|
+
const localWrite = annotations.localAction({ destructive: false, confirm: false });
|
|
33
33
|
const openLink = annotations.openAction();
|
|
34
34
|
|
|
35
35
|
export default server(
|
|
@@ -83,7 +83,7 @@ const discoverInput = z.object({
|
|
|
83
83
|
// shortlisting is a local non-destructive write.
|
|
84
84
|
const readOnly = annotations.readOnly();
|
|
85
85
|
const openLink = annotations.openAction();
|
|
86
|
-
const localWrite = annotations.localAction({ destructive: false });
|
|
86
|
+
const localWrite = annotations.localAction({ destructive: false, confirm: false });
|
|
87
87
|
|
|
88
88
|
const destinationOutput = z.object({
|
|
89
89
|
id: z.string(),
|
|
@@ -28,6 +28,8 @@ const priority = z.enum(['high', 'medium', 'low']);
|
|
|
28
28
|
// non-destructive writes.
|
|
29
29
|
const readOnly = annotations.readOnly();
|
|
30
30
|
const localWrite = annotations.localAction({ destructive: false });
|
|
31
|
+
const confirmedWrite = annotations.localAction({ destructive: false, confirm: true });
|
|
32
|
+
const widgetWrite = annotations.localAction({ destructive: false, confirm: false });
|
|
31
33
|
|
|
32
34
|
const taskOutput = z.object({
|
|
33
35
|
id: z.string(),
|
|
@@ -104,7 +106,7 @@ export default server(
|
|
|
104
106
|
// Flow 3 — Complete: mark a task done. Model-visible so the model can complete on request.
|
|
105
107
|
tool('complete_task', {
|
|
106
108
|
description: 'Mark an Acme task complete. Pass the task id and its title.',
|
|
107
|
-
annotations:
|
|
109
|
+
annotations: confirmedWrite,
|
|
108
110
|
input: z.object({
|
|
109
111
|
task: z.string(),
|
|
110
112
|
title: z.string().min(1),
|
|
@@ -122,7 +124,7 @@ export default server(
|
|
|
122
124
|
tool('set_priority', {
|
|
123
125
|
visibility: ['app'],
|
|
124
126
|
description: 'Re-prioritize a task from the list widget.',
|
|
125
|
-
annotations:
|
|
127
|
+
annotations: widgetWrite,
|
|
126
128
|
input: z.object({
|
|
127
129
|
task: z.string(),
|
|
128
130
|
priority,
|
|
@@ -19,4 +19,13 @@ describe('acme-tasks example', () => {
|
|
|
19
19
|
const text = JSON.stringify(await app.toManifest());
|
|
20
20
|
expect(text).toMatch(/"tasks":\[\{"id":"email_vendor".*"priority":"high"/);
|
|
21
21
|
});
|
|
22
|
+
|
|
23
|
+
it('opts the conversational completion action into runtime confirmation', async () => {
|
|
24
|
+
const manifest = await app.toManifest();
|
|
25
|
+
const completeTask = manifest.tools.find((candidate) => candidate.name === 'complete_task');
|
|
26
|
+
const addTask = manifest.tools.find((candidate) => candidate.name === 'add_task');
|
|
27
|
+
|
|
28
|
+
expect(completeTask?.annotations?.confirm).toBe(true);
|
|
29
|
+
expect(addTask?.annotations).not.toHaveProperty('confirm');
|
|
30
|
+
});
|
|
22
31
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Food Ordering
|
|
2
2
|
|
|
3
3
|
**Owns:** The flagship consumer ordering MCP App example: React view authoring, app-only helper tools,
|
|
4
|
-
caller-scoped cart state handles,
|
|
5
|
-
CSP/permissions metadata,
|
|
4
|
+
caller-scoped cart state handles, invocation context, model-visible widget state/lifecycle, packaged image
|
|
5
|
+
assets, portable structured elicitation, checkout handoff policy, host actions, CSP/permissions metadata,
|
|
6
|
+
and widget preview coverage.
|
|
6
7
|
|
|
7
8
|
Food Ordering is a generic, synthetic version of a live marketplace ordering app. It lets a user search
|
|
8
9
|
stores, browse menus, customize an item, build a multi-line cart, review the order, and hand off checkout to
|
|
@@ -14,10 +15,13 @@ private customer data.
|
|
|
14
15
|
| Capability | Example |
|
|
15
16
|
| :--- | :--- |
|
|
16
17
|
| Public entry tool | `open_ordering` returns structured fallback content and renders the React widget |
|
|
17
|
-
| App-only helper tools | `search_stores`, `load_menu`, `load_item`, `read_cart`, `sync_cart`, `prepare_checkout` |
|
|
18
|
+
| App-only helper tools | `search_stores`, `load_menu`, `load_item`, `read_cart`, `sync_cart`, `prepare_checkout`; mutating widget-owned helpers use `confirm: false` (equivalent to omission) and execute directly because action hints alone never gate |
|
|
18
19
|
| Durable cart state | `server(..., { state: { handles: { cart } }, use: { state } })` with caller scope and revision checks |
|
|
19
20
|
| React app runtime kit | `@noodleseed/one/react` supplies app flow, shell/nav/view, async state, form, quantity, choice, and handoff primitives |
|
|
20
21
|
| Multi-step widget flow | One React shell navigates stores, menu, item customization, cart, review, and handoff views through `useAppFlow` |
|
|
22
|
+
| Invocation context | `server.context` sets locale/time-zone defaults, derives an ambient service area/date, and makes the same snapshot available to tools and the reserved `noodle_context` MCP adapter |
|
|
23
|
+
| Structured missing input | `plan_order` uses `ctx.elicit` to collect a fulfilment method and date in embedded/headless renderers and bidirectional MCP adapters; the stateless hosted MCP endpoint does not yet carry the server-initiated exchange |
|
|
24
|
+
| Model-visible widget state | `useUpdateModelContext` publishes one cohesive replacement snapshot when supported; `useWidgetLifecycle` auto-publishes mounted/cancelled/dismissed and reports author-owned submitted milestones for future context (not host-presentation proof), while the user-triggered submit pairs `useSendFollowUpMessage` for an immediate reply |
|
|
21
25
|
| Handoff | `handoff.allowedDomains` allows only `https://orders.example.com` checkout URLs |
|
|
22
26
|
| Progressive enhancement | Non-Apps hosts still receive stores, featured items, and a readable fallback summary |
|
|
23
27
|
|
|
@@ -174,7 +174,10 @@ type CartInput = {
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
const readOnly = annotations.readOnly();
|
|
177
|
-
|
|
177
|
+
// These writes are app-only controls inside the cart widget. The widget already presents the
|
|
178
|
+
// reviewed state and explicit button; `confirm: false` documents direct execution and is equivalent
|
|
179
|
+
// to omission because action/open-world hints alone never enable the confirmation gate.
|
|
180
|
+
const action = annotations.openAction({ destructive: false, confirm: false });
|
|
178
181
|
|
|
179
182
|
function checkoutUrl(customer: string): string {
|
|
180
183
|
return `https://orders.example.com/checkout?customer=${encodeURIComponent(customer)}`;
|
|
@@ -198,6 +201,16 @@ export default server(
|
|
|
198
201
|
title: 'Food Ordering',
|
|
199
202
|
version: '1.0.0',
|
|
200
203
|
use: { state },
|
|
204
|
+
context: {
|
|
205
|
+
defaults: { locale: 'en-US', timeZone: 'America/New_York' },
|
|
206
|
+
ambient: {
|
|
207
|
+
output: z.object({ serviceArea: z.string(), orderingDate: z.string() }),
|
|
208
|
+
fulfil: ({ context }) => ({
|
|
209
|
+
serviceArea: 'Harbor District',
|
|
210
|
+
orderingDate: context.temporal.localDate,
|
|
211
|
+
}),
|
|
212
|
+
},
|
|
213
|
+
},
|
|
201
214
|
state: {
|
|
202
215
|
handles: {
|
|
203
216
|
cart: {
|
|
@@ -239,13 +252,17 @@ export default server(
|
|
|
239
252
|
customer: z.string(),
|
|
240
253
|
stores: z.array(storeShape),
|
|
241
254
|
featuredItems: z.array(menuItemShape),
|
|
255
|
+
localDate: z.string(),
|
|
256
|
+
serviceArea: z.string(),
|
|
242
257
|
fallback: z.string(),
|
|
243
258
|
}),
|
|
244
|
-
fulfil: ({ input }) => ({
|
|
259
|
+
fulfil: ({ input, context }) => ({
|
|
245
260
|
status: 'Ready to build a food order.',
|
|
246
261
|
customer: input.customer,
|
|
247
262
|
stores,
|
|
248
263
|
featuredItems: menu,
|
|
264
|
+
localDate: context.temporal.localDate,
|
|
265
|
+
serviceArea: context.ambient.serviceArea,
|
|
249
266
|
fallback: 'Open stores: Harbor Noodles (Noodles), Garden Wraps (Vegetarian).',
|
|
250
267
|
}),
|
|
251
268
|
viewTitle: 'Food ordering',
|
|
@@ -363,6 +380,34 @@ export default server(
|
|
|
363
380
|
}),
|
|
364
381
|
fulfil: () => ({ stores, featuredItems: menu }),
|
|
365
382
|
}),
|
|
383
|
+
tool('plan_order', {
|
|
384
|
+
description:
|
|
385
|
+
'Collect a fulfilment method and requested date as structured input, then return a reviewable order plan without placing an order.',
|
|
386
|
+
annotations: readOnly,
|
|
387
|
+
input: z.object({ customer: z.string().default('Guest') }),
|
|
388
|
+
output: z.object({
|
|
389
|
+
customer: z.string(),
|
|
390
|
+
method: z.enum(['pickup', 'delivery']),
|
|
391
|
+
requestedDate: z.string(),
|
|
392
|
+
serviceArea: z.string(),
|
|
393
|
+
}),
|
|
394
|
+
fulfil: ({ input, context, elicit }) => {
|
|
395
|
+
const preference = elicit({
|
|
396
|
+
id: 'choose_fulfilment',
|
|
397
|
+
message: 'How should we fulfil this order?',
|
|
398
|
+
input: z.object({
|
|
399
|
+
method: z.enum(['pickup', 'delivery']).describe('Fulfilment method'),
|
|
400
|
+
requestedDate: z.string().describe('Requested date').meta({ format: 'date' }),
|
|
401
|
+
}),
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
customer: input.customer,
|
|
405
|
+
method: preference.method,
|
|
406
|
+
requestedDate: preference.requestedDate,
|
|
407
|
+
serviceArea: context.ambient.serviceArea,
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
}),
|
|
366
411
|
tool('show_capabilities', {
|
|
367
412
|
description: 'Return a concise summary for the standalone widget capability preview.',
|
|
368
413
|
annotations: readOnly,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ActionBar,
|
|
4
4
|
AppShell,
|
|
@@ -19,7 +19,9 @@ import {
|
|
|
19
19
|
useLayout,
|
|
20
20
|
useSendFollowUpMessage,
|
|
21
21
|
useToolInfo,
|
|
22
|
+
useUpdateModelContext,
|
|
22
23
|
useViewState,
|
|
24
|
+
useWidgetLifecycle,
|
|
23
25
|
View,
|
|
24
26
|
ViewStack,
|
|
25
27
|
} from '../helpers.js';
|
|
@@ -90,7 +92,7 @@ function modifierLabel(value: string): string {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
export default function OrderingFlow() {
|
|
93
|
-
const { displayMode, theme } = useLayout();
|
|
95
|
+
const { displayMode, supports, theme } = useLayout();
|
|
94
96
|
const entry = structured<{
|
|
95
97
|
readonly customer?: string;
|
|
96
98
|
readonly stores?: readonly Store[];
|
|
@@ -106,6 +108,8 @@ export default function OrderingFlow() {
|
|
|
106
108
|
const setView = flow.navigate;
|
|
107
109
|
const handoff = useHandoff();
|
|
108
110
|
const sendFollowUpMessage = useSendFollowUpMessage();
|
|
111
|
+
const updateModelContext = useUpdateModelContext();
|
|
112
|
+
const publishLifecycle = useWidgetLifecycle('ordering-flow');
|
|
109
113
|
const searchStores = useCallTool('search_stores');
|
|
110
114
|
const loadMenu = useCallTool('load_menu');
|
|
111
115
|
const loadItem = useCallTool('load_item');
|
|
@@ -134,6 +138,7 @@ export default function OrderingFlow() {
|
|
|
134
138
|
const setRevision = (value: number) => revisionStore.setState({ value });
|
|
135
139
|
const [quantity, setQuantity] = useState(1);
|
|
136
140
|
const [selectedModifiers, setSelectedModifiers] = useState<readonly string[]>([]);
|
|
141
|
+
const [lifecycle, setLifecycle] = useState<'active' | 'submitted'>('active');
|
|
137
142
|
|
|
138
143
|
const storeData = structured<{ readonly stores?: readonly Store[] }>(searchStores.data);
|
|
139
144
|
const menuData = structured<{
|
|
@@ -170,6 +175,37 @@ export default function OrderingFlow() {
|
|
|
170
175
|
const llmSummary = `${view} view for ${customer}; ${cart.lines.length} cart lines; subtotal ${currency(
|
|
171
176
|
cart.subtotal,
|
|
172
177
|
)}`;
|
|
178
|
+
const canUpdateModelContext = supports?.modelContext === true;
|
|
179
|
+
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (!canUpdateModelContext) return;
|
|
182
|
+
// Each call replaces model context, so publish one cohesive snapshot of current surface state.
|
|
183
|
+
void updateModelContext({
|
|
184
|
+
content: [{ type: 'text', text: `Food ordering: ${llmSummary}; ${lifecycle}.` }],
|
|
185
|
+
structuredContent: {
|
|
186
|
+
widget: { name: 'ordering-flow', lifecycle },
|
|
187
|
+
ordering: {
|
|
188
|
+
view,
|
|
189
|
+
customer,
|
|
190
|
+
cartLines: cart.lines.length,
|
|
191
|
+
subtotal: cart.subtotal,
|
|
192
|
+
selectedStoreId: selectedStoreId ?? null,
|
|
193
|
+
selectedItemId: selectedItemId ?? null,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}, [
|
|
198
|
+
canUpdateModelContext,
|
|
199
|
+
cart.lines.length,
|
|
200
|
+
cart.subtotal,
|
|
201
|
+
customer,
|
|
202
|
+
lifecycle,
|
|
203
|
+
llmSummary,
|
|
204
|
+
selectedItemId,
|
|
205
|
+
selectedStoreId,
|
|
206
|
+
updateModelContext,
|
|
207
|
+
view,
|
|
208
|
+
]);
|
|
173
209
|
|
|
174
210
|
async function chooseStore(store: Store) {
|
|
175
211
|
setSelectedStoreId(store.id);
|
|
@@ -246,9 +282,26 @@ export default function OrderingFlow() {
|
|
|
246
282
|
readonly revision?: number;
|
|
247
283
|
readonly checkoutUrl?: string;
|
|
248
284
|
}>(result);
|
|
249
|
-
|
|
285
|
+
const preparedCart = prepared?.cart ?? cart;
|
|
286
|
+
setCart(preparedCart);
|
|
250
287
|
setRevision(prepared?.revision ?? revision + 1);
|
|
288
|
+
setLifecycle('submitted');
|
|
251
289
|
setView('handoff');
|
|
290
|
+
if (canUpdateModelContext) {
|
|
291
|
+
await publishLifecycle('submitted', {
|
|
292
|
+
view: 'handoff',
|
|
293
|
+
customer,
|
|
294
|
+
cartLines: preparedCart.lines.length,
|
|
295
|
+
subtotal: preparedCart.subtotal,
|
|
296
|
+
selectedStoreId: preparedCart.selectedStoreId ?? null,
|
|
297
|
+
selectedItemId: selectedItemId ?? null,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (supports?.followUpMessage) {
|
|
301
|
+
await sendFollowUpMessage({
|
|
302
|
+
prompt: 'Checkout is prepared. Confirm the cart summary and explain the final handoff.',
|
|
303
|
+
});
|
|
304
|
+
}
|
|
252
305
|
}
|
|
253
306
|
|
|
254
307
|
return (
|
|
@@ -10,6 +10,13 @@ describe('food-ordering example', () => {
|
|
|
10
10
|
const manifest = (await app.toManifest()) as {
|
|
11
11
|
server: {
|
|
12
12
|
name: string;
|
|
13
|
+
context?: {
|
|
14
|
+
defaults?: { locale?: string; timeZone?: string };
|
|
15
|
+
ambient?: {
|
|
16
|
+
outputSchema?: unknown;
|
|
17
|
+
fulfilment?: { output?: unknown };
|
|
18
|
+
};
|
|
19
|
+
};
|
|
13
20
|
};
|
|
14
21
|
handoff?: { allowedDomains?: string[] };
|
|
15
22
|
state?: { handles?: Record<string, { kind: string; scope: string }> };
|
|
@@ -17,7 +24,9 @@ describe('food-ordering example', () => {
|
|
|
17
24
|
tools: Array<{
|
|
18
25
|
name: string;
|
|
19
26
|
visibility?: string[];
|
|
27
|
+
annotations?: Record<string, unknown>;
|
|
20
28
|
output?: unknown;
|
|
29
|
+
fulfilment?: { steps?: unknown[]; output?: unknown };
|
|
21
30
|
}>;
|
|
22
31
|
widgets?: Array<{
|
|
23
32
|
name: string;
|
|
@@ -27,6 +36,17 @@ describe('food-ordering example', () => {
|
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
expect(manifest.server.name).toBe('food_ordering');
|
|
39
|
+
expect(manifest.server.context).toMatchObject({
|
|
40
|
+
defaults: { locale: 'en-US', timeZone: 'America/New_York' },
|
|
41
|
+
ambient: {
|
|
42
|
+
fulfilment: {
|
|
43
|
+
output: {
|
|
44
|
+
serviceArea: 'Harbor District',
|
|
45
|
+
orderingDate: '${context.temporal.localDate}',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
30
50
|
expect(manifest.state?.handles?.cart).toMatchObject({
|
|
31
51
|
kind: 'cart',
|
|
32
52
|
scope: 'caller',
|
|
@@ -52,7 +72,33 @@ describe('food-ordering example', () => {
|
|
|
52
72
|
expect(tools.get(helper)?.visibility).toEqual(['app']);
|
|
53
73
|
}
|
|
54
74
|
expect(JSON.stringify(tools.get('open_ordering'))).toContain('featuredItems');
|
|
75
|
+
expect(JSON.stringify(tools.get('open_ordering'))).toContain('${context.temporal.localDate}');
|
|
76
|
+
expect(JSON.stringify(tools.get('open_ordering'))).toContain('${context.ambient.serviceArea}');
|
|
55
77
|
expect(JSON.stringify(tools.get('sync_cart'))).toContain('revision');
|
|
78
|
+
expect(tools.get('sync_cart')?.annotations?.confirm).toBe(false);
|
|
79
|
+
expect(tools.get('prepare_checkout')?.annotations?.confirm).toBe(false);
|
|
80
|
+
expect(tools.get('plan_order')?.fulfilment).toMatchObject({
|
|
81
|
+
steps: [
|
|
82
|
+
{
|
|
83
|
+
id: 'choose_fulfilment',
|
|
84
|
+
elicit: {
|
|
85
|
+
message: 'How should we fulfil this order?',
|
|
86
|
+
requestedSchema: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
method: { type: 'string', enum: ['pickup', 'delivery'] },
|
|
90
|
+
requestedDate: { type: 'string', format: 'date' },
|
|
91
|
+
},
|
|
92
|
+
required: ['method', 'requestedDate'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
output: {
|
|
98
|
+
method: '${steps.choose_fulfilment.method}',
|
|
99
|
+
requestedDate: '${steps.choose_fulfilment.requestedDate}',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
56
102
|
expect(manifest.widgets?.map((widget) => widget.name)).toContain('capabilities_card');
|
|
57
103
|
});
|
|
58
104
|
});
|