@noodleseed/agent-kit 0.26.0 → 0.28.0

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "packageVersion": "0.26.0",
2
+ "packageVersion": "0.28.0",
3
3
  "files": [
4
4
  {
5
5
  "path": "skills/codex/SKILL.md",
6
- "sha256": "49a9f988ee0abf92640cc2bcb27204e6eff3a042378a4bf1a38c93671b72d943",
6
+ "sha256": "46a6304e7263dc8d466e8a63cbfa8e236e81c12d32ac248d75114ea07696baae",
7
7
  "agentTarget": "codex"
8
8
  },
9
9
  {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  {
25
25
  "path": "skills/codex/references/compile-errors.md",
26
- "sha256": "d67bafe1aea7fb38c374a61cbebfa7af11eb5f3bdb8552ddb367918a2d415294",
26
+ "sha256": "f3e791d3eb7b95641791b9e63efd03852aa517d9b29e535f447fed3080fd61e4",
27
27
  "agentTarget": "codex"
28
28
  },
29
29
  {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  {
35
35
  "path": "skills/codex/references/embedded-assistant.md",
36
- "sha256": "3909d5b6e2f556105a332ae8688225104627e6001d3bd05e8dd14d1585d32f0c",
36
+ "sha256": "16ef1df5ad641f6b36fa9530f920c3588f77f929c4d609fe4aeffd0dc11abc94",
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": "3aee540c20e23f73d78b4ab1dc8b7dba9b71cf213c8570431190cf6ef48506e6",
51
+ "sha256": "921d05b7f7a8044f4b9c4f8816972839eb792499b99f095ff1fbcda89a2b48b3",
52
52
  "agentTarget": "codex"
53
53
  },
54
54
  {
@@ -228,7 +228,7 @@
228
228
  },
229
229
  {
230
230
  "path": "skills/codex/examples/acme-tasks/src/server.ts",
231
- "sha256": "769982ba567460af08d79517d6bf259ce4e8f7882cd3525fadbd5b22639e08ff",
231
+ "sha256": "514cca681ffd5f686364cb2d3b8ab27c9145b1aa1e2bf22d665fa1935ac55ce6",
232
232
  "agentTarget": "codex"
233
233
  },
234
234
  {
@@ -268,7 +268,7 @@
268
268
  },
269
269
  {
270
270
  "path": "skills/codex/examples/customer-auth/src/server.ts",
271
- "sha256": "517e7e1b744e7aa7758c70c0f53e79588b9b92641af18acd487e0a9a1d6e5c4a",
271
+ "sha256": "cbc7647ad0c67860020f9b5a7ebbb1ac8ca817d619184821fbcbf7970291f05a",
272
272
  "agentTarget": "codex"
273
273
  },
274
274
  {
@@ -278,7 +278,7 @@
278
278
  },
279
279
  {
280
280
  "path": "skills/codex/examples/food-ordering/README.md",
281
- "sha256": "1e8a08868efa70fe79bacc02d27effa5a65fbb5b497e76a7ce1268586ec559dc",
281
+ "sha256": "41fdd5b9ca345a5f0daf4633cb53e65f11d1f211f283f6ba902db3366f872809",
282
282
  "agentTarget": "codex"
283
283
  },
284
284
  {
@@ -378,7 +378,7 @@
378
378
  },
379
379
  {
380
380
  "path": "skills/claude-code/SKILL.md",
381
- "sha256": "b8ab63da424b86234affee76b69c470d2f68d7fc820d50399014bd5130e36619",
381
+ "sha256": "0c0dd5641e72b641a87e4814eb6d0b8e46e3c9bb23abfe213763a2a36777f6d5",
382
382
  "agentTarget": "claude-code"
383
383
  },
384
384
  {
@@ -398,7 +398,7 @@
398
398
  },
399
399
  {
400
400
  "path": "skills/claude-code/references/compile-errors.md",
401
- "sha256": "d67bafe1aea7fb38c374a61cbebfa7af11eb5f3bdb8552ddb367918a2d415294",
401
+ "sha256": "f3e791d3eb7b95641791b9e63efd03852aa517d9b29e535f447fed3080fd61e4",
402
402
  "agentTarget": "claude-code"
403
403
  },
404
404
  {
@@ -408,7 +408,7 @@
408
408
  },
409
409
  {
410
410
  "path": "skills/claude-code/references/embedded-assistant.md",
411
- "sha256": "3909d5b6e2f556105a332ae8688225104627e6001d3bd05e8dd14d1585d32f0c",
411
+ "sha256": "16ef1df5ad641f6b36fa9530f920c3588f77f929c4d609fe4aeffd0dc11abc94",
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": "3aee540c20e23f73d78b4ab1dc8b7dba9b71cf213c8570431190cf6ef48506e6",
426
+ "sha256": "921d05b7f7a8044f4b9c4f8816972839eb792499b99f095ff1fbcda89a2b48b3",
427
427
  "agentTarget": "claude-code"
428
428
  },
429
429
  {
@@ -603,7 +603,7 @@
603
603
  },
604
604
  {
605
605
  "path": "skills/claude-code/examples/acme-tasks/src/server.ts",
606
- "sha256": "769982ba567460af08d79517d6bf259ce4e8f7882cd3525fadbd5b22639e08ff",
606
+ "sha256": "514cca681ffd5f686364cb2d3b8ab27c9145b1aa1e2bf22d665fa1935ac55ce6",
607
607
  "agentTarget": "claude-code"
608
608
  },
609
609
  {
@@ -643,7 +643,7 @@
643
643
  },
644
644
  {
645
645
  "path": "skills/claude-code/examples/customer-auth/src/server.ts",
646
- "sha256": "517e7e1b744e7aa7758c70c0f53e79588b9b92641af18acd487e0a9a1d6e5c4a",
646
+ "sha256": "cbc7647ad0c67860020f9b5a7ebbb1ac8ca817d619184821fbcbf7970291f05a",
647
647
  "agentTarget": "claude-code"
648
648
  },
649
649
  {
@@ -653,7 +653,7 @@
653
653
  },
654
654
  {
655
655
  "path": "skills/claude-code/examples/food-ordering/README.md",
656
- "sha256": "1e8a08868efa70fe79bacc02d27effa5a65fbb5b497e76a7ce1268586ec559dc",
656
+ "sha256": "41fdd5b9ca345a5f0daf4633cb53e65f11d1f211f283f6ba902db3366f872809",
657
657
  "agentTarget": "claude-code"
658
658
  },
659
659
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/agent-kit",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
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",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: noodle-seed
3
3
  description: Use when building, validating, testing, deploying, or operating a local or hosted Noodle Seed MCP server or app authored in TypeScript with the noodle CLI.
4
- version: 0.26.0
4
+ version: 0.28.0
5
5
  hash: b4fc528d406e2149
6
6
  ---
7
7
 
@@ -55,6 +55,7 @@ export default server(
55
55
  [
56
56
  // Flow 2 — Prioritize / Today: render the list so the human triages and the model can speak to it.
57
57
  tool('list_today', {
58
+ title: 'Show today’s tasks',
58
59
  description: 'Show today’s Acme Tasks and render the task-list widget.',
59
60
  annotations: readOnly,
60
61
  input: z.object({ focus: z.string().default('today') }),
@@ -86,11 +87,12 @@ export default server(
86
87
  }),
87
88
  // Flow 1 — Capture: add a task from natural language ("remind me to email the vendor").
88
89
  tool('add_task', {
90
+ title: 'Add a task',
89
91
  description: 'Capture a new Acme task with a title and priority.',
90
92
  annotations: localWrite,
91
93
  input: z.object({
92
- title: z.string(),
93
- priority: priority.default('medium'),
94
+ title: z.string().meta({ title: 'Task' }),
95
+ priority: priority.default('medium').meta({ title: 'Priority' }),
94
96
  }),
95
97
  output: z.object({
96
98
  status: z.string(),
@@ -105,11 +107,12 @@ export default server(
105
107
  }),
106
108
  // Flow 3 — Complete: mark a task done. Model-visible so the model can complete on request.
107
109
  tool('complete_task', {
108
- description: 'Mark an Acme task complete. Pass the task id and its title.',
110
+ title: 'Complete task',
111
+ description: 'This will mark the selected task complete for everyone using Acme Tasks.',
109
112
  annotations: confirmedWrite,
110
113
  input: z.object({
111
- task: z.string(),
112
- title: z.string().min(1),
114
+ task: z.string().meta({ title: 'Task ID' }),
115
+ title: z.string().min(1).meta({ title: 'Task' }),
113
116
  }),
114
117
  output: z.object({
115
118
  status: z.string(),
@@ -122,12 +125,13 @@ export default server(
122
125
  }),
123
126
  // Flow 2 helper (widget-only): re-prioritize a task from the list widget.
124
127
  tool('set_priority', {
128
+ title: 'Change task priority',
125
129
  visibility: ['app'],
126
130
  description: 'Re-prioritize a task from the list widget.',
127
131
  annotations: widgetWrite,
128
132
  input: z.object({
129
- task: z.string(),
130
- priority,
133
+ task: z.string().meta({ title: 'Task ID' }),
134
+ priority: priority.meta({ title: 'New priority' }),
131
135
  }),
132
136
  output: z.object({
133
137
  status: z.string(),
@@ -115,11 +115,12 @@ export default server(
115
115
  },
116
116
  [
117
117
  tool('list_org_apps', {
118
+ title: 'List organization apps',
118
119
  description: 'List NoodleSeed.com apps for an organization from the dev app API.',
119
120
  input: z.object({
120
- org_id: z.string(),
121
- skip: z.number().int().min(0).optional(),
122
- limit: z.number().int().min(1).max(100).optional(),
121
+ org_id: z.string().meta({ title: 'Organization' }),
122
+ skip: z.number().int().min(0).optional().meta({ title: 'Starting item' }),
123
+ limit: z.number().int().min(1).max(100).optional().meta({ title: 'Maximum results' }),
123
124
  }),
124
125
  output: z.object({
125
126
  result: z.unknown(),
@@ -137,6 +138,7 @@ export default server(
137
138
  },
138
139
  }),
139
140
  tool('list_my_organizations', {
141
+ title: 'List my organizations',
140
142
  description: 'List the NoodleSeed.com organizations the signed-in customer belongs to.',
141
143
  contextProvider: true,
142
144
  input: z.object({}),
@@ -32,6 +32,10 @@ error/retry, and success states must remain readable without nested vertical scr
32
32
 
33
33
  Like the comprehensive default `noodle init my-app` scaffold, this flagship keeps the server feature-rich
34
34
  while making each individual widget view focused; server capability breadth and screen density are separate.
35
+ The compiled initial widget should normally remain under the 1 MiB performance recommendation; Noodle Seed's
36
+ hard ceilings are 10 MiB per compiled widget and 20 MiB across one deployment. Run `noodle check` to see raw
37
+ and gzip-estimated sizes, and keep menu images or large live datasets in assets/resources and app-only tools
38
+ rather than embedding them into the initial HTML bundle.
35
39
 
36
40
  ## Local Author Loop
37
41
 
@@ -51,6 +51,8 @@ Run `noodle validate` (add `--json` for the machine-readable envelope, `--fix-pr
51
51
  | `duplicate_widget_tool` | A tool is bound to more than one widget; bind each tool to a single widget. |
52
52
  | `invalid_widget_binding` | Fix the `data-bind`/binding expression in the widget; it does not resolve against the tool output. |
53
53
  | `invalid_widget_state_handle` | Correct the state handle reference; declare it under `server(..., { state: { handles } })` and reference it by its declared name. |
54
+ | `widget_html_too_large` | Reduce the compiled widget below 10 MiB UTF-8 (or raw HTML below 256 KiB) by moving large media and dynamic data into hosted resources or bounded app-only tools. |
55
+ | `widget_html_total_too_large` | Reduce aggregate widget HTML below 20 MiB UTF-8; share or externalize large payloads instead of duplicating them across initial widget resources. |
54
56
  | `invalid_asset` | Fix the `asset("./path")` reference; the file must exist and be a supported asset type. |
55
57
  | `invalid_capability_requirement` | Correct the declared capability/permission requirement to a supported value. |
56
58
  | `state_secret_field` | Remove the secret-shaped field from widget/handle state; secrets must never be stored in state or sent to widgets. |
@@ -59,7 +59,7 @@ Use `noodle commands --json` before proposing command flags; do not invent flags
59
59
 
60
60
  ## Customize the presentation
61
61
 
62
- Keep identity and semantic light/dark colors in the one server-level `branding` block. Put assistant-only structure in the bounded `presentation` object; it accepts curated primitives rather than raw HTML, CSS, SVG, class names, or callbacks:
62
+ Keep portable identity and semantic light/dark colors in the one server-level `branding` block. Put assistant-only structure in the bounded `presentation` object; it accepts curated primitives rather than raw HTML, CSS, SVG, class names, or callbacks:
63
63
 
64
64
  ```ts
65
65
  assistant: embeddedAssistant({
@@ -82,7 +82,9 @@ assistant: embeddedAssistant({
82
82
 
83
83
  The Atlas-style product treatment above is the maximum deployment-configurable presentation. The bounded surface covers panel treatment, launcher icon/size/session pulse, header mark/status badge, composer controls, and message treatment; it does not accept custom header actions, structured empty-state layouts, footers, spectacle variants/effects, or tenant code.
84
84
 
85
- Omitted fields retain the polished Halo-derived baseline. Precedence is host slots/public `--ns-assistant-*` variables for application-specific integration, then the deployed semantic presentation, then the built-in defaults. Prefer `server.ts` configuration first so every embedding app receives the same assistant after redeploy; use slots only for behavior or content that is genuinely owned by that particular host page.
85
+ Omitted fields retain the quiet premium baseline. For exact application-owned color roles, pass the typed React `appearance={{ light: { panel: { surface, text, border }, composer: {...}, confirmation: {...}, primaryButton: {...} }, dark: {...} }}` prop or assign the same object to `element.appearance`. It covers canvas, panel, header, messages, composer, suggestions, confirmation, buttons, launcher, code, and the MCP App frame. Exact colors are preserved and low contrast emits `assistant-appearance-warning`. Precedence is host appearance object, host slots/public `--ns-assistant-*` variables, deployed semantic presentation, then defaults. Prefer `server.ts` configuration first so every embedding app receives the same assistant after redeploy.
86
+
87
+ Give every business action a portable `tool(..., { title: "Complete task", description: "This will mark the task complete for everyone.", input: z.object({ task: z.string().meta({ title: "Task" }) }) })` title. The standard confirmation uses the tool title/description plus schema field `title`, `description`, and `format`; it shows Confirm and Don't proceed and keeps technical action details secondary. Do not put JSON or implementation names in business-facing copy.
86
88
 
87
89
  ## Configure and deploy
88
90
 
@@ -22,6 +22,8 @@ Generated widgets, official examples, and agent-authored MCP Apps must start wit
22
22
 
23
23
  The default composition rule is: build the smallest useful conversational surface. Inline has one purpose, one primary action, and at most two visible actions. Use progressive disclosure or a later conversational turn for secondary detail; request fullscreen only when the user asks or the task genuinely needs it. Never use nested scrolling. At 280px and wider, the widget must remain one-column, readable, touch-safe, and free of horizontal overflow. Remove secondary chrome before shrinking essential content.
24
24
 
25
+ Treat 1 MiB of uncompressed UTF-8 HTML as the recommended initial React-widget budget, not a compatibility wall. Noodle Seed accepts up to 10 MiB per compiled widget and 20 MiB across a deployment; `noodle check` reports raw and gzip-estimated sizes. Keep large media and dynamic datasets in hosted assets/resources or bounded app-only tool responses instead of inflating the initial widget resource.
26
+
25
27
  Every production widget handles loading, empty, partial, stale, error, retry, and success states. Prefill fields from known tool results and choose safe, reversible defaults; preserve the user’s work across rerenders, and never preselect a consequential action. Use public React primitives and branding tokens. Never author against `ns-*`, `nsr-*`, or example-local `--nw-*` classes/tokens; those are implementation details, not alternate design systems.
26
28
 
27
29
  ## React hook surface
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: noodle-seed
3
3
  description: Use when building, validating, testing, deploying, or operating a local or hosted Noodle Seed MCP server or app authored in TypeScript with the noodle CLI.
4
- version: 0.26.0
4
+ version: 0.28.0
5
5
  hash: cd25535e1d6dbf4f
6
6
  ---
7
7
 
@@ -55,6 +55,7 @@ export default server(
55
55
  [
56
56
  // Flow 2 — Prioritize / Today: render the list so the human triages and the model can speak to it.
57
57
  tool('list_today', {
58
+ title: 'Show today’s tasks',
58
59
  description: 'Show today’s Acme Tasks and render the task-list widget.',
59
60
  annotations: readOnly,
60
61
  input: z.object({ focus: z.string().default('today') }),
@@ -86,11 +87,12 @@ export default server(
86
87
  }),
87
88
  // Flow 1 — Capture: add a task from natural language ("remind me to email the vendor").
88
89
  tool('add_task', {
90
+ title: 'Add a task',
89
91
  description: 'Capture a new Acme task with a title and priority.',
90
92
  annotations: localWrite,
91
93
  input: z.object({
92
- title: z.string(),
93
- priority: priority.default('medium'),
94
+ title: z.string().meta({ title: 'Task' }),
95
+ priority: priority.default('medium').meta({ title: 'Priority' }),
94
96
  }),
95
97
  output: z.object({
96
98
  status: z.string(),
@@ -105,11 +107,12 @@ export default server(
105
107
  }),
106
108
  // Flow 3 — Complete: mark a task done. Model-visible so the model can complete on request.
107
109
  tool('complete_task', {
108
- description: 'Mark an Acme task complete. Pass the task id and its title.',
110
+ title: 'Complete task',
111
+ description: 'This will mark the selected task complete for everyone using Acme Tasks.',
109
112
  annotations: confirmedWrite,
110
113
  input: z.object({
111
- task: z.string(),
112
- title: z.string().min(1),
114
+ task: z.string().meta({ title: 'Task ID' }),
115
+ title: z.string().min(1).meta({ title: 'Task' }),
113
116
  }),
114
117
  output: z.object({
115
118
  status: z.string(),
@@ -122,12 +125,13 @@ export default server(
122
125
  }),
123
126
  // Flow 2 helper (widget-only): re-prioritize a task from the list widget.
124
127
  tool('set_priority', {
128
+ title: 'Change task priority',
125
129
  visibility: ['app'],
126
130
  description: 'Re-prioritize a task from the list widget.',
127
131
  annotations: widgetWrite,
128
132
  input: z.object({
129
- task: z.string(),
130
- priority,
133
+ task: z.string().meta({ title: 'Task ID' }),
134
+ priority: priority.meta({ title: 'New priority' }),
131
135
  }),
132
136
  output: z.object({
133
137
  status: z.string(),
@@ -115,11 +115,12 @@ export default server(
115
115
  },
116
116
  [
117
117
  tool('list_org_apps', {
118
+ title: 'List organization apps',
118
119
  description: 'List NoodleSeed.com apps for an organization from the dev app API.',
119
120
  input: z.object({
120
- org_id: z.string(),
121
- skip: z.number().int().min(0).optional(),
122
- limit: z.number().int().min(1).max(100).optional(),
121
+ org_id: z.string().meta({ title: 'Organization' }),
122
+ skip: z.number().int().min(0).optional().meta({ title: 'Starting item' }),
123
+ limit: z.number().int().min(1).max(100).optional().meta({ title: 'Maximum results' }),
123
124
  }),
124
125
  output: z.object({
125
126
  result: z.unknown(),
@@ -137,6 +138,7 @@ export default server(
137
138
  },
138
139
  }),
139
140
  tool('list_my_organizations', {
141
+ title: 'List my organizations',
140
142
  description: 'List the NoodleSeed.com organizations the signed-in customer belongs to.',
141
143
  contextProvider: true,
142
144
  input: z.object({}),
@@ -32,6 +32,10 @@ error/retry, and success states must remain readable without nested vertical scr
32
32
 
33
33
  Like the comprehensive default `noodle init my-app` scaffold, this flagship keeps the server feature-rich
34
34
  while making each individual widget view focused; server capability breadth and screen density are separate.
35
+ The compiled initial widget should normally remain under the 1 MiB performance recommendation; Noodle Seed's
36
+ hard ceilings are 10 MiB per compiled widget and 20 MiB across one deployment. Run `noodle check` to see raw
37
+ and gzip-estimated sizes, and keep menu images or large live datasets in assets/resources and app-only tools
38
+ rather than embedding them into the initial HTML bundle.
35
39
 
36
40
  ## Local Author Loop
37
41
 
@@ -51,6 +51,8 @@ Run `noodle validate` (add `--json` for the machine-readable envelope, `--fix-pr
51
51
  | `duplicate_widget_tool` | A tool is bound to more than one widget; bind each tool to a single widget. |
52
52
  | `invalid_widget_binding` | Fix the `data-bind`/binding expression in the widget; it does not resolve against the tool output. |
53
53
  | `invalid_widget_state_handle` | Correct the state handle reference; declare it under `server(..., { state: { handles } })` and reference it by its declared name. |
54
+ | `widget_html_too_large` | Reduce the compiled widget below 10 MiB UTF-8 (or raw HTML below 256 KiB) by moving large media and dynamic data into hosted resources or bounded app-only tools. |
55
+ | `widget_html_total_too_large` | Reduce aggregate widget HTML below 20 MiB UTF-8; share or externalize large payloads instead of duplicating them across initial widget resources. |
54
56
  | `invalid_asset` | Fix the `asset("./path")` reference; the file must exist and be a supported asset type. |
55
57
  | `invalid_capability_requirement` | Correct the declared capability/permission requirement to a supported value. |
56
58
  | `state_secret_field` | Remove the secret-shaped field from widget/handle state; secrets must never be stored in state or sent to widgets. |
@@ -59,7 +59,7 @@ Use `noodle commands --json` before proposing command flags; do not invent flags
59
59
 
60
60
  ## Customize the presentation
61
61
 
62
- Keep identity and semantic light/dark colors in the one server-level `branding` block. Put assistant-only structure in the bounded `presentation` object; it accepts curated primitives rather than raw HTML, CSS, SVG, class names, or callbacks:
62
+ Keep portable identity and semantic light/dark colors in the one server-level `branding` block. Put assistant-only structure in the bounded `presentation` object; it accepts curated primitives rather than raw HTML, CSS, SVG, class names, or callbacks:
63
63
 
64
64
  ```ts
65
65
  assistant: embeddedAssistant({
@@ -82,7 +82,9 @@ assistant: embeddedAssistant({
82
82
 
83
83
  The Atlas-style product treatment above is the maximum deployment-configurable presentation. The bounded surface covers panel treatment, launcher icon/size/session pulse, header mark/status badge, composer controls, and message treatment; it does not accept custom header actions, structured empty-state layouts, footers, spectacle variants/effects, or tenant code.
84
84
 
85
- Omitted fields retain the polished Halo-derived baseline. Precedence is host slots/public `--ns-assistant-*` variables for application-specific integration, then the deployed semantic presentation, then the built-in defaults. Prefer `server.ts` configuration first so every embedding app receives the same assistant after redeploy; use slots only for behavior or content that is genuinely owned by that particular host page.
85
+ Omitted fields retain the quiet premium baseline. For exact application-owned color roles, pass the typed React `appearance={{ light: { panel: { surface, text, border }, composer: {...}, confirmation: {...}, primaryButton: {...} }, dark: {...} }}` prop or assign the same object to `element.appearance`. It covers canvas, panel, header, messages, composer, suggestions, confirmation, buttons, launcher, code, and the MCP App frame. Exact colors are preserved and low contrast emits `assistant-appearance-warning`. Precedence is host appearance object, host slots/public `--ns-assistant-*` variables, deployed semantic presentation, then defaults. Prefer `server.ts` configuration first so every embedding app receives the same assistant after redeploy.
86
+
87
+ Give every business action a portable `tool(..., { title: "Complete task", description: "This will mark the task complete for everyone.", input: z.object({ task: z.string().meta({ title: "Task" }) }) })` title. The standard confirmation uses the tool title/description plus schema field `title`, `description`, and `format`; it shows Confirm and Don't proceed and keeps technical action details secondary. Do not put JSON or implementation names in business-facing copy.
86
88
 
87
89
  ## Configure and deploy
88
90
 
@@ -22,6 +22,8 @@ Generated widgets, official examples, and agent-authored MCP Apps must start wit
22
22
 
23
23
  The default composition rule is: build the smallest useful conversational surface. Inline has one purpose, one primary action, and at most two visible actions. Use progressive disclosure or a later conversational turn for secondary detail; request fullscreen only when the user asks or the task genuinely needs it. Never use nested scrolling. At 280px and wider, the widget must remain one-column, readable, touch-safe, and free of horizontal overflow. Remove secondary chrome before shrinking essential content.
24
24
 
25
+ Treat 1 MiB of uncompressed UTF-8 HTML as the recommended initial React-widget budget, not a compatibility wall. Noodle Seed accepts up to 10 MiB per compiled widget and 20 MiB across a deployment; `noodle check` reports raw and gzip-estimated sizes. Keep large media and dynamic datasets in hosted assets/resources or bounded app-only tool responses instead of inflating the initial widget resource.
26
+
25
27
  Every production widget handles loading, empty, partial, stale, error, retry, and success states. Prefill fields from known tool results and choose safe, reversible defaults; preserve the user’s work across rerenders, and never preselect a consequential action. Use public React primitives and branding tokens. Never author against `ns-*`, `nsr-*`, or example-local `--nw-*` classes/tokens; those are implementation details, not alternate design systems.
26
28
 
27
29
  ## React hook surface