@noodleseed/agent-kit 0.44.0 → 0.45.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.
Files changed (44) hide show
  1. package/manifest.json +279 -247
  2. package/package.json +1 -1
  3. package/skills/claude-code/SKILL.md +1 -1
  4. package/skills/claude-code/authoring-mcp-servers/SKILL.md +1 -1
  5. package/skills/claude-code/building-mcp-apps/SKILL.md +1 -1
  6. package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +1 -1
  7. package/skills/claude-code/debugging-mcp-delivery/SKILL.md +1 -1
  8. package/skills/claude-code/deploying-mcp-services/SKILL.md +1 -1
  9. package/skills/claude-code/designing-mcp-products/SKILL.md +1 -1
  10. package/skills/claude-code/embedding-mcp-assistants/SKILL.md +1 -1
  11. package/skills/claude-code/examples/customer-auth/README.md +12 -5
  12. package/skills/claude-code/examples/food-ordering/README.md +1 -0
  13. package/skills/claude-code/examples/food-ordering/package.json +1 -1
  14. package/skills/claude-code/examples/food-ordering/src/helpers.ts +1 -0
  15. package/skills/claude-code/examples/food-ordering/src/views/ordering-flow.tsx +31 -27
  16. package/skills/claude-code/examples/food-ordering/src/views/ordering-result.ts +84 -0
  17. package/skills/claude-code/examples/food-ordering/test/ordering-flow.test.ts +187 -0
  18. package/skills/claude-code/executing-noodle-plans/SKILL.md +1 -1
  19. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
  20. package/skills/claude-code/references/embedded-assistant.md +10 -5
  21. package/skills/claude-code/references/widgets-and-apps.md +30 -10
  22. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +1 -1
  23. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
  24. package/skills/codex/SKILL.md +1 -1
  25. package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
  26. package/skills/codex/building-mcp-apps/SKILL.md +1 -1
  27. package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
  28. package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
  29. package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
  30. package/skills/codex/designing-mcp-products/SKILL.md +1 -1
  31. package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
  32. package/skills/codex/examples/customer-auth/README.md +12 -5
  33. package/skills/codex/examples/food-ordering/README.md +1 -0
  34. package/skills/codex/examples/food-ordering/package.json +1 -1
  35. package/skills/codex/examples/food-ordering/src/helpers.ts +1 -0
  36. package/skills/codex/examples/food-ordering/src/views/ordering-flow.tsx +31 -27
  37. package/skills/codex/examples/food-ordering/src/views/ordering-result.ts +84 -0
  38. package/skills/codex/examples/food-ordering/test/ordering-flow.test.ts +187 -0
  39. package/skills/codex/executing-noodle-plans/SKILL.md +1 -1
  40. package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
  41. package/skills/codex/references/embedded-assistant.md +10 -5
  42. package/skills/codex/references/widgets-and-apps.md +30 -10
  43. package/skills/codex/reporting-noodle-feedback/SKILL.md +1 -1
  44. package/skills/codex/verifying-mcp-delivery/SKILL.md +1 -1
@@ -254,6 +254,7 @@ For a customer-owned React renderer, use the renderer-free hook. It owns client
254
254
  "use client";
255
255
 
256
256
  import { useState } from "react";
257
+ import { NoodleAppView } from "@noodleseed/assistant/react";
257
258
  import { useNoodleAssistant } from "@noodleseed/assistant/react/client";
258
259
 
259
260
  export function CustomAssistant({ principalKey }: { principalKey: string }) {
@@ -327,9 +328,11 @@ export function CustomAssistant({ principalKey }: { principalKey: string }) {
327
328
  }
328
329
  if (part.type === "data-view") {
329
330
  return (
330
- <p key={part.data.id}>
331
- Trusted app view available: {part.data.title ?? part.data.resourceUri}
332
- </p>
331
+ <NoodleAppView
332
+ key={`${part.data.id}:${part.data.resourceUri}`}
333
+ client={client}
334
+ view={part.data}
335
+ />
333
336
  );
334
337
  }
335
338
  return <p key={index}>Unsupported assistant content.</p>;
@@ -364,7 +367,9 @@ export function CustomAssistant({ principalKey }: { principalKey: string }) {
364
367
 
365
368
  `principalKey` is a browser-local identity for the authenticated user/tenant and is never sent to Noodle. Change it whenever that principal changes; the hook then aborts and clears the previous session and transcript. The hook does not register `<noodle-assistant>` or render Noodle markup.
366
369
 
367
- The sample fails closed on input requests until you replace that branch with a form generated from `requestedSchema`. A custom renderer must show the complete confirmation review and both decisions, handle every part it supports, and surface an explicit unsupported state for the rest. For `data-view`, map `resourceUri` or `tool` plus the bounded/redacted `result` to a component already trusted by the application. Never inject `part.data.html`, assign it to `srcdoc`, or fetch a `ui://` URI; the managed element alone supplies Noodle’s sandbox host. Do not wrap this client in another chat transport or invent user messages for interaction continuations.
370
+ The sample fails closed on input requests until you replace that branch with a form generated from `requestedSchema`. A custom renderer must show the complete confirmation review and both decisions, handle every part it supports, and surface an explicit unsupported state for the rest. For `data-view`, use `NoodleAppView` to render the linked App or deliberately map `resourceUri`/tool plus the bounded redacted `result` to an application-trusted native component. JSON result data is not the linked App UI. Never inject `part.data.html`, assign it to `srcdoc`, fetch a `ui://` URI, or reproduce the bridge with a direct Ext Apps dependency. Do not wrap this client in another chat transport or invent user messages for interaction continuations.
371
+
372
+ `NoodleAppView` owns one bridge for the semantic view identity: client + `view.id` + `view.resourceUri`. It retains the iframe across fresh payload/callback objects from parent rerenders, reads current payloads through refs, and sends standard App teardown only when that semantic identity changes or the component unmounts. Do not key an ancestor by a view object or callback. If the embedding page sets Content-Security-Policy, include the Noodle service origin in both `connect-src` and `frame-src`.
368
373
 
369
374
  Outside React, use the same DOM-free client directly. It keeps the session token in memory, exposes a React-free `UIMessage` transcript with typed parts, and never registers a custom element:
370
375
 
@@ -416,7 +421,7 @@ if (pending) {
416
421
 
417
422
  `subscribeChat` immediately emits a detached `{ messages, status, error? }` snapshot and then emits as `UIMessage.parts` change. Text uses `text`; Noodle confirmations, input requests, tool results, and linked views use `data-confirmation`, `data-input-request`, `data-tool-result`, and `data-view`. Interaction data moves through pending/submitting/accepted/declined/cancelled. Use raw `subscribe(...)` only for transport/session lifecycle events that are not transcript content.
418
423
 
419
- `data-view` means a completed tool has a linked MCP App view. It carries the call/interaction id, tool, `ui://` identity, optional title, bounded/redacted public result, and—on current services—the self-contained bridged document. The standard element is an MCP Apps host and mounts that document behind a double iframe; a customer renderer ignores it and maps the identity/result to an application-trusted component. The standard element supports lifecycle, app tool/resource calls, ui/message, ui/update-model-context, links, resize, and inline/fullscreen; sampling, tasks, downloads, and remote DOM are not advertised. It also dispatches `assistant-view-available` for a customer-owned renderer.
424
+ `data-view` means a completed tool has a linked MCP App view. In a customer-owned React renderer, pass that typed part and the existing client to `NoodleAppView`; it retains one bridge for client + `view.id` + `view.resourceUri` and requests standard App teardown on semantic replacement or unmount. Deliberately map the bounded result to an application-trusted native component only when replacing the linked App UI.
420
425
 
421
426
  `clientContext` and typed `pageContext` are recomputed for each turn. `updateContext(...)` remains the legacy session-exchange context; `updatePageContext(...)` replaces the fresh per-turn application hint. `updateModelContext({ content, structuredContent })` publishes one cohesive renderer snapshot for later message turns without starting a turn; every call replaces the prior snapshot rather than merging fields. These are untrusted data, not conversation history or authorization input, and the boundaries reject credential-shaped or unbounded updates. A message may re-exchange once after a pre-execution `401`; the client never auto-retries interaction decisions. `tool_proposed.arguments` is a complete schema-aware review projection and, for connector-backed tools, names the sole exact connector version/operation/resolved arguments. Sensitive/write-only fields are redacted; truncating or omitting any non-sensitive action field fails closed. Accept is bound to the server-held action and claims at most one execution attempt—clients cannot replace it. Normal terminal outcomes scrub private arguments and continuations immediately; only an accepted action still executing retains them for the one-hour unknown-outcome recovery window, after which it records `interaction_outcome_unknown` and scrubs. Without downstream idempotency this is not an exactly-once business-effect guarantee. To reconcile a lost response, explicitly repeat the same id and decision: the service returns its durable stored outcome without re-execution.
422
427
 
@@ -26,13 +26,15 @@ Treat 1 MiB of uncompressed UTF-8 HTML as the recommended initial React-widget b
26
26
 
27
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.
28
28
 
29
+ Keep the complete `useToolInfo()` result. An empty result envelope is pending, `isError` is an explicit tool error, and any non-pending success must validate every required field and identifier before it becomes UI data. Render a malformed-result error when validation fails, and render dependent actions only after validation succeeds. Safe local input defaults may follow valid hydration; they must never fabricate a loaded business record.
30
+
29
31
  ## React hook surface
30
32
 
31
33
  Author views as React components. `generateHelpers<ServerDefinition>()` (from `@noodleseed/one/react`) returns the typed host hooks:
32
34
 
33
35
  | Hook | Use for |
34
36
  | :-- | :-- |
35
- | `useToolInfo` | Read the invoking tool result; `structuredContent` is the widget’s typed data payload. |
37
+ | `useToolInfo` | Read the complete invoking tool result: treat `{}` as pending, handle `isError`, validate every required `structuredContent` field and identifier, reject malformed success data, and render dependent actions only after validation succeeds. |
36
38
  | `useCallTool` | Call a tool from the widget — returns `{ status, callTool, callToolAsync, data, structuredContent, error, reset }`; target a model-visible tool or a hidden `tool` helper. |
37
39
  | `useViewState` | Persist per-widget UI state across re-renders and restores: `const [value, setValue] = useViewState("key", initial)`. |
38
40
  | `useLayout` | Read host layout: `{ theme, displayMode, locale?, host?, supports? }` (`displayMode` is `"inline"`/`"pip"`/`"fullscreen"`) — adapt styling to the host theme and mode. |
@@ -65,14 +67,27 @@ const { useToolInfo, useCallTool, useViewState, useLayout, useOpenExternal, useS
65
67
  generateHelpers<ServerDefinition>();
66
68
 
67
69
  type OrderResult = {
68
- readonly customer?: string;
69
- readonly item?: string;
70
- readonly total?: number;
71
- readonly checkoutUrl?: string;
70
+ readonly customer: string;
71
+ readonly item: string;
72
+ readonly total: number;
73
+ readonly checkoutUrl: string;
72
74
  };
73
75
 
76
+ function isOrderResult(value: unknown): value is OrderResult {
77
+ if (value === null || typeof value !== 'object') return false;
78
+ const result = value as Partial<OrderResult>;
79
+ return (
80
+ typeof result.customer === 'string' && result.customer.length > 0 &&
81
+ typeof result.item === 'string' && result.item.length > 0 &&
82
+ typeof result.total === 'number' && Number.isFinite(result.total) &&
83
+ typeof result.checkoutUrl === 'string' && result.checkoutUrl.startsWith('https://')
84
+ );
85
+ }
86
+
74
87
  export default function OrderStatus() {
75
- const shown = useToolInfo('show_order').structuredContent as OrderResult | undefined;
88
+ const toolInfo = useToolInfo('show_order');
89
+ const isPending = Object.keys(toolInfo).length === 0;
90
+ const shown = isOrderResult(toolInfo.structuredContent) ? toolInfo.structuredContent : undefined;
76
91
  const placeOrder = useCallTool('place_order'); // calls the widget-only helper tool
77
92
  const { supports } = useLayout();
78
93
  const openExternal = useOpenExternal();
@@ -85,15 +100,16 @@ export default function OrderStatus() {
85
100
  const checkoutUrl = shown?.checkoutUrl ?? '';
86
101
 
87
102
  useEffect(() => {
88
- if (!supports?.modelContext) return;
103
+ if (!shown || !supports?.modelContext) return;
89
104
  void updateModelContext({
90
105
  content: [{ type: 'text', text: `Pickup order: ${item}, total ${total}` }],
91
106
  structuredContent: { widget: { name: 'pickup-order', lifecycle: 'active' }, order: { item, total } },
92
107
  });
93
- }, [item, supports?.modelContext, total, updateModelContext]);
108
+ }, [item, shown, supports?.modelContext, total, updateModelContext]);
94
109
 
95
110
  async function submitOrder() {
96
- await placeOrder.callTool({ customer: shown?.customer ?? 'Guest', item });
111
+ if (!shown) return;
112
+ await placeOrder.callTool({ customer: shown.customer, item });
97
113
  if (supports?.modelContext) {
98
114
  await publishLifecycle('submitted', { surface: 'order', item, total, status: 'submitted' });
99
115
  }
@@ -102,9 +118,13 @@ export default function OrderStatus() {
102
118
  }
103
119
  }
104
120
 
121
+ if (isPending) return <Feedback status="loading">Loading the order…</Feedback>;
122
+ if (toolInfo.isError) return <Feedback status="error">Could not load the order.</Feedback>;
123
+ if (!shown) return <Feedback status="error">The order result was incomplete.</Feedback>;
124
+
105
125
  return (
106
126
  // data-llm is an inspection hint; useUpdateModelContext above is the model-visible channel.
107
- <Frame title="Pickup order" displayMode="auto" data-llm={`Pickup order for ${shown?.customer ?? 'Guest'}: ${item}, total ${total}`}>
127
+ <Frame title="Pickup order" displayMode="auto" data-llm={`Pickup order for ${shown.customer}: ${item}, total ${total}`}>
108
128
  <Flow variant="stack">
109
129
  <AsyncBoundary state={placeOrder} loading="Placing order…" error={(error) => error.message}>
110
130
  <Region title="Order" description="Choose one item for pickup.">
@@ -3,7 +3,7 @@ name: reporting-noodle-feedback
3
3
  description: "Use when a Noodle Seed bug, misleading instruction, missing capability, or concrete product improvement should be proposed to the user."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.44.0 hash:0f404109f4845683 -->
6
+ <!-- noodle-skill version:0.45.0 hash:0f404109f4845683 -->
7
7
 
8
8
  # reporting-noodle-feedback
9
9
 
@@ -3,7 +3,7 @@ name: verifying-mcp-delivery
3
3
  description: "Use when proving a Noodle Seed MCP project works at a named compile, local, connector, App, host, deployment, or production evidence level."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.44.0 hash:6ef6ef551e26b78e -->
6
+ <!-- noodle-skill version:0.45.0 hash:6ef6ef551e26b78e -->
7
7
 
8
8
  # verifying-mcp-delivery
9
9