@noodleseed/agent-kit 0.27.0 → 0.29.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 +15 -15
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +1 -1
- package/skills/claude-code/examples/acme-tasks/README.md +4 -0
- package/skills/claude-code/examples/acme-tasks/src/server.ts +14 -7
- package/skills/claude-code/examples/customer-auth/src/server.ts +5 -3
- package/skills/claude-code/references/authoring-workflow.md +1 -1
- package/skills/claude-code/references/embedded-assistant.md +6 -4
- package/skills/claude-code/references/sdk-surface.md +1 -1
- package/skills/codex/SKILL.md +1 -1
- package/skills/codex/examples/acme-tasks/README.md +4 -0
- package/skills/codex/examples/acme-tasks/src/server.ts +14 -7
- package/skills/codex/examples/customer-auth/src/server.ts +5 -3
- package/skills/codex/references/authoring-workflow.md +1 -1
- package/skills/codex/references/embedded-assistant.md +6 -4
- package/skills/codex/references/sdk-surface.md +1 -1
package/manifest.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"packageVersion": "0.
|
|
2
|
+
"packageVersion": "0.29.0",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"path": "skills/codex/SKILL.md",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "748275d8f350af4390d07fccb571bedf0ea6dc9c7c074f28fe3a493f41bcbef1",
|
|
7
7
|
"agentTarget": "codex"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"path": "skills/codex/references/sdk-surface.md",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "769ac9217397298ec8b25236a008d11a84406a7fab6584ae929e5eacc2054cdc",
|
|
12
12
|
"agentTarget": "codex"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"path": "skills/codex/references/authoring-workflow.md",
|
|
31
|
-
"sha256": "
|
|
31
|
+
"sha256": "d553234ffd7c4d8d22624123b6c91f359f8643ae305584742b241763c8868eeb",
|
|
32
32
|
"agentTarget": "codex"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "skills/codex/references/embedded-assistant.md",
|
|
36
|
-
"sha256": "
|
|
36
|
+
"sha256": "7b9de2f1033291312b2b4cc58bffa3e1cb0c8eeed6a77505daab5dc492bb00db",
|
|
37
37
|
"agentTarget": "codex"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
},
|
|
199
199
|
{
|
|
200
200
|
"path": "skills/codex/examples/acme-tasks/README.md",
|
|
201
|
-
"sha256": "
|
|
201
|
+
"sha256": "3fbc0821badfea43af4aab131d09f7c953095e0d241de7ea5cfcc3491b70fce5",
|
|
202
202
|
"agentTarget": "codex"
|
|
203
203
|
},
|
|
204
204
|
{
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
},
|
|
229
229
|
{
|
|
230
230
|
"path": "skills/codex/examples/acme-tasks/src/server.ts",
|
|
231
|
-
"sha256": "
|
|
231
|
+
"sha256": "0abd695218d0fd73abde31b2ea16742a028721044ef3c565bdc88cdbb10f19aa",
|
|
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": "
|
|
271
|
+
"sha256": "cbc7647ad0c67860020f9b5a7ebbb1ac8ca817d619184821fbcbf7970291f05a",
|
|
272
272
|
"agentTarget": "codex"
|
|
273
273
|
},
|
|
274
274
|
{
|
|
@@ -378,12 +378,12 @@
|
|
|
378
378
|
},
|
|
379
379
|
{
|
|
380
380
|
"path": "skills/claude-code/SKILL.md",
|
|
381
|
-
"sha256": "
|
|
381
|
+
"sha256": "4b22a1c1b1f113567d61aa9ccddb1b47c61f07c899dc195577dde61535508dfd",
|
|
382
382
|
"agentTarget": "claude-code"
|
|
383
383
|
},
|
|
384
384
|
{
|
|
385
385
|
"path": "skills/claude-code/references/sdk-surface.md",
|
|
386
|
-
"sha256": "
|
|
386
|
+
"sha256": "769ac9217397298ec8b25236a008d11a84406a7fab6584ae929e5eacc2054cdc",
|
|
387
387
|
"agentTarget": "claude-code"
|
|
388
388
|
},
|
|
389
389
|
{
|
|
@@ -403,12 +403,12 @@
|
|
|
403
403
|
},
|
|
404
404
|
{
|
|
405
405
|
"path": "skills/claude-code/references/authoring-workflow.md",
|
|
406
|
-
"sha256": "
|
|
406
|
+
"sha256": "d553234ffd7c4d8d22624123b6c91f359f8643ae305584742b241763c8868eeb",
|
|
407
407
|
"agentTarget": "claude-code"
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
"path": "skills/claude-code/references/embedded-assistant.md",
|
|
411
|
-
"sha256": "
|
|
411
|
+
"sha256": "7b9de2f1033291312b2b4cc58bffa3e1cb0c8eeed6a77505daab5dc492bb00db",
|
|
412
412
|
"agentTarget": "claude-code"
|
|
413
413
|
},
|
|
414
414
|
{
|
|
@@ -573,7 +573,7 @@
|
|
|
573
573
|
},
|
|
574
574
|
{
|
|
575
575
|
"path": "skills/claude-code/examples/acme-tasks/README.md",
|
|
576
|
-
"sha256": "
|
|
576
|
+
"sha256": "3fbc0821badfea43af4aab131d09f7c953095e0d241de7ea5cfcc3491b70fce5",
|
|
577
577
|
"agentTarget": "claude-code"
|
|
578
578
|
},
|
|
579
579
|
{
|
|
@@ -603,7 +603,7 @@
|
|
|
603
603
|
},
|
|
604
604
|
{
|
|
605
605
|
"path": "skills/claude-code/examples/acme-tasks/src/server.ts",
|
|
606
|
-
"sha256": "
|
|
606
|
+
"sha256": "0abd695218d0fd73abde31b2ea16742a028721044ef3c565bdc88cdbb10f19aa",
|
|
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": "
|
|
646
|
+
"sha256": "cbc7647ad0c67860020f9b5a7ebbb1ac8ca817d619184821fbcbf7970291f05a",
|
|
647
647
|
"agentTarget": "claude-code"
|
|
648
648
|
},
|
|
649
649
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/agent-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.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",
|
|
@@ -30,6 +30,10 @@ on the flows.
|
|
|
30
30
|
invents a task.
|
|
31
31
|
- **Two users** — tools are atomic and model-fillable ("high priority" → `priority: "high"`), and each
|
|
32
32
|
returns a spoken-ready status so the model can confirm in one turn.
|
|
33
|
+
- **Cross-host confirmation** — `complete_task` keeps `confirm: true`. The server's explicit
|
|
34
|
+
`interactions.confirmationFallback: 'host'` uses Noodle confirmation in capable/embedded hosts and trusts
|
|
35
|
+
ChatGPT's native write approval only when the stateless transport cannot present that form. Backend
|
|
36
|
+
authorization remains independent.
|
|
33
37
|
|
|
34
38
|
## Wireframe (one screen: the three flows in place)
|
|
35
39
|
|
|
@@ -43,6 +43,9 @@ export default server(
|
|
|
43
43
|
{
|
|
44
44
|
title: 'Acme Tasks',
|
|
45
45
|
version: '1.0.0',
|
|
46
|
+
// ChatGPT's stateless MCP lane cannot carry Noodle's standard confirmation form. Keep
|
|
47
|
+
// confirm:true for capable/embedded hosts, but explicitly trust native host approval there.
|
|
48
|
+
interactions: { confirmationFallback: 'host' },
|
|
46
49
|
branding: {
|
|
47
50
|
name: 'Acme Tasks',
|
|
48
51
|
accent: '#7C3AED',
|
|
@@ -55,6 +58,7 @@ export default server(
|
|
|
55
58
|
[
|
|
56
59
|
// Flow 2 — Prioritize / Today: render the list so the human triages and the model can speak to it.
|
|
57
60
|
tool('list_today', {
|
|
61
|
+
title: 'Show today’s tasks',
|
|
58
62
|
description: 'Show today’s Acme Tasks and render the task-list widget.',
|
|
59
63
|
annotations: readOnly,
|
|
60
64
|
input: z.object({ focus: z.string().default('today') }),
|
|
@@ -86,11 +90,12 @@ export default server(
|
|
|
86
90
|
}),
|
|
87
91
|
// Flow 1 — Capture: add a task from natural language ("remind me to email the vendor").
|
|
88
92
|
tool('add_task', {
|
|
93
|
+
title: 'Add a task',
|
|
89
94
|
description: 'Capture a new Acme task with a title and priority.',
|
|
90
95
|
annotations: localWrite,
|
|
91
96
|
input: z.object({
|
|
92
|
-
title: z.string(),
|
|
93
|
-
priority: priority.default('medium'),
|
|
97
|
+
title: z.string().meta({ title: 'Task' }),
|
|
98
|
+
priority: priority.default('medium').meta({ title: 'Priority' }),
|
|
94
99
|
}),
|
|
95
100
|
output: z.object({
|
|
96
101
|
status: z.string(),
|
|
@@ -105,11 +110,12 @@ export default server(
|
|
|
105
110
|
}),
|
|
106
111
|
// Flow 3 — Complete: mark a task done. Model-visible so the model can complete on request.
|
|
107
112
|
tool('complete_task', {
|
|
108
|
-
|
|
113
|
+
title: 'Complete task',
|
|
114
|
+
description: 'This will mark the selected task complete for everyone using Acme Tasks.',
|
|
109
115
|
annotations: confirmedWrite,
|
|
110
116
|
input: z.object({
|
|
111
|
-
task: z.string(),
|
|
112
|
-
title: z.string().min(1),
|
|
117
|
+
task: z.string().meta({ title: 'Task ID' }),
|
|
118
|
+
title: z.string().min(1).meta({ title: 'Task' }),
|
|
113
119
|
}),
|
|
114
120
|
output: z.object({
|
|
115
121
|
status: z.string(),
|
|
@@ -122,12 +128,13 @@ export default server(
|
|
|
122
128
|
}),
|
|
123
129
|
// Flow 2 helper (widget-only): re-prioritize a task from the list widget.
|
|
124
130
|
tool('set_priority', {
|
|
131
|
+
title: 'Change task priority',
|
|
125
132
|
visibility: ['app'],
|
|
126
133
|
description: 'Re-prioritize a task from the list widget.',
|
|
127
134
|
annotations: widgetWrite,
|
|
128
135
|
input: z.object({
|
|
129
|
-
task: z.string(),
|
|
130
|
-
priority,
|
|
136
|
+
task: z.string().meta({ title: 'Task ID' }),
|
|
137
|
+
priority: priority.meta({ title: 'New priority' }),
|
|
131
138
|
}),
|
|
132
139
|
output: z.object({
|
|
133
140
|
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({}),
|
|
@@ -273,7 +273,7 @@ tool('prepare_time_off', {
|
|
|
273
273
|
});
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
-
Use a stable lowercase/number/underscore id and a flat form of string/number/integer/boolean, string choices or multi-select, with optional `email`, `uri`, `date`, or `date-time` formats. Nested objects and credential-shaped fields fail with `invalid_elicitation_schema`. Every interactive flow must place all `ctx.elicit` calls before its first connector operation or compilation fails with `invalid_elicitation_flow`. Embedded/headless clients receive `input_requested`; bidirectional MCP transports map the primitive to standard form `elicitation/create`. An adapter that cannot carry the request
|
|
276
|
+
Use a stable lowercase/number/underscore id and a flat form of string/number/integer/boolean, string choices or multi-select, with optional `email`, `uri`, `date`, or `date-time` formats. Nested objects and credential-shaped fields fail with `invalid_elicitation_schema`. Every interactive flow must place all `ctx.elicit` calls before its first connector operation or compilation fails with `invalid_elicitation_flow`. Embedded/headless clients receive `input_requested`; bidirectional MCP transports map the primitive to standard form `elicitation/create`. An adapter that cannot carry the request returns a structured non-executing `interaction_unavailable` result. Accept validates and resumes without rerunning completed steps; invalid content returns `arg_invalid` and leaves the same interaction pending; decline/cancel stop. Elicitation gathers missing input and does not replace confirmation. In a flow marked `confirm: true`, every eligible `input_requested` precedes `tool_proposed`; the final proposal reviews the original input, elicited values, and sole exact connector action. Accept is bound to that action and only then may execution start. A confirmable flow may contain at most one connector operation; additional operations fail with `invalid_confirmation_flow`. MCP uses final standard form confirmation on capable bidirectional transports and fails closed otherwise. Setting `interactions: { confirmationFallback: "host" }` in the server options explicitly trusts native host approval only when confirmation transport is unavailable and no elicited input is needed; it is never inferred from client name and does not replace authorization. Omitted or `false` annotations execute directly; hints alone never gate. `annotations.action({ confirm: true })` explicitly enables confirmation; `annotations.action({ confirm: false })` explicitly preserves direct execution.
|
|
277
277
|
|
|
278
278
|
## Compute connector example
|
|
279
279
|
|
|
@@ -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
|
|
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
|
|
|
@@ -174,7 +176,7 @@ The callback records declarative fulfilment at author time; the shared runtime e
|
|
|
174
176
|
|
|
175
177
|
## Structured missing input
|
|
176
178
|
|
|
177
|
-
A tool authored with `ctx.elicit({ id, message, input })` produces `input_requested` when it reaches
|
|
179
|
+
A tool authored with `ctx.elicit({ id, message, input })` produces `input_requested` when it reaches missing input. Built-in and headless renderers present it and call `respond(id, { action: "accept", content })`; decline/cancel stop. Accepted content is schema-validated and completed steps are not rerun; invalid content returns `arg_invalid` and keeps the interaction pending. Elicitation gathers an input and does not approve a later write. Every interactive flow collects elicited input before its first connector operation; every eligible `input_requested` precedes `tool_proposed`. In a `confirm: true` flow, the final proposal reviews original input, elicited values, and the sole exact connector action; a confirmable flow has at most one connector operation. Accept is bound to that action. Bidirectional MCP and the embedded assistant use the same advertised endpoints and interaction-event protocol: missing input maps to standard `elicitation/create`, and an adapter that cannot carry that request fails safely with a structured non-executing result. The same negotiated channel carries the final affirmative confirmation. Setting `interactions: { confirmationFallback: "host" }` in server options explicitly trusts native host approval only when confirmation transport is unavailable and no elicited input is required; embedded/headless confirmation remains Noodle-owned. Omitted or false annotations execute directly; hints never gate.
|
|
178
180
|
|
|
179
181
|
## Verified session context (identity and claims)
|
|
180
182
|
|
|
@@ -281,7 +283,7 @@ if (pendingId) {
|
|
|
281
283
|
|
|
282
284
|
`view_available` 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. It 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.
|
|
283
285
|
|
|
284
|
-
`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
|
|
286
|
+
`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.
|
|
285
287
|
|
|
286
288
|
## Toolchain requirements
|
|
287
289
|
|
|
@@ -127,7 +127,7 @@ prompt('summarize_ticket', {
|
|
|
127
127
|
|
|
128
128
|
### Non-trivial tool: ctx connectors, annotations, visibility, async
|
|
129
129
|
|
|
130
|
-
`ctx` is `{ input, user, connectors }`. Bind connectors with `use` on the server, then call one inside `fulfil` to record a step. `annotations.readOnly()` declares a closed-world safe read. TypeScript action helpers enforce confirmation only with `{ confirm: true }`; omitted or `false` executes directly, and action/destructive/open-world hints alone never enable the gate. `visibility` defaults to `['model', 'app']` — set `['app']` to hide a helper from the model. `fulfil` may be `async` (the compiler awaits it while recording).
|
|
130
|
+
`ctx` is `{ input, user, connectors }`. Bind connectors with `use` on the server, then call one inside `fulfil` to record a step. `annotations.readOnly()` declares a closed-world safe read. TypeScript action helpers enforce confirmation only with `{ confirm: true }`; omitted or `false` executes directly, and action/destructive/open-world hints alone never enable the gate. For stateless hosts that cannot present Noodle confirmation, set `interactions: { confirmationFallback: 'host' }` in the `server` options to explicitly trust native host write approval; omission remains fail-closed and the fallback never supplies missing `ctx.elicit` input. `visibility` defaults to `['model', 'app']` — set `['app']` to hide a helper from the model. `fulfil` may be `async` (the compiler awaits it while recording).
|
|
131
131
|
|
|
132
132
|
```ts
|
|
133
133
|
import { annotations, connector, server, tool, z } from '@noodleseed/one';
|
package/skills/codex/SKILL.md
CHANGED
|
@@ -30,6 +30,10 @@ on the flows.
|
|
|
30
30
|
invents a task.
|
|
31
31
|
- **Two users** — tools are atomic and model-fillable ("high priority" → `priority: "high"`), and each
|
|
32
32
|
returns a spoken-ready status so the model can confirm in one turn.
|
|
33
|
+
- **Cross-host confirmation** — `complete_task` keeps `confirm: true`. The server's explicit
|
|
34
|
+
`interactions.confirmationFallback: 'host'` uses Noodle confirmation in capable/embedded hosts and trusts
|
|
35
|
+
ChatGPT's native write approval only when the stateless transport cannot present that form. Backend
|
|
36
|
+
authorization remains independent.
|
|
33
37
|
|
|
34
38
|
## Wireframe (one screen: the three flows in place)
|
|
35
39
|
|
|
@@ -43,6 +43,9 @@ export default server(
|
|
|
43
43
|
{
|
|
44
44
|
title: 'Acme Tasks',
|
|
45
45
|
version: '1.0.0',
|
|
46
|
+
// ChatGPT's stateless MCP lane cannot carry Noodle's standard confirmation form. Keep
|
|
47
|
+
// confirm:true for capable/embedded hosts, but explicitly trust native host approval there.
|
|
48
|
+
interactions: { confirmationFallback: 'host' },
|
|
46
49
|
branding: {
|
|
47
50
|
name: 'Acme Tasks',
|
|
48
51
|
accent: '#7C3AED',
|
|
@@ -55,6 +58,7 @@ export default server(
|
|
|
55
58
|
[
|
|
56
59
|
// Flow 2 — Prioritize / Today: render the list so the human triages and the model can speak to it.
|
|
57
60
|
tool('list_today', {
|
|
61
|
+
title: 'Show today’s tasks',
|
|
58
62
|
description: 'Show today’s Acme Tasks and render the task-list widget.',
|
|
59
63
|
annotations: readOnly,
|
|
60
64
|
input: z.object({ focus: z.string().default('today') }),
|
|
@@ -86,11 +90,12 @@ export default server(
|
|
|
86
90
|
}),
|
|
87
91
|
// Flow 1 — Capture: add a task from natural language ("remind me to email the vendor").
|
|
88
92
|
tool('add_task', {
|
|
93
|
+
title: 'Add a task',
|
|
89
94
|
description: 'Capture a new Acme task with a title and priority.',
|
|
90
95
|
annotations: localWrite,
|
|
91
96
|
input: z.object({
|
|
92
|
-
title: z.string(),
|
|
93
|
-
priority: priority.default('medium'),
|
|
97
|
+
title: z.string().meta({ title: 'Task' }),
|
|
98
|
+
priority: priority.default('medium').meta({ title: 'Priority' }),
|
|
94
99
|
}),
|
|
95
100
|
output: z.object({
|
|
96
101
|
status: z.string(),
|
|
@@ -105,11 +110,12 @@ export default server(
|
|
|
105
110
|
}),
|
|
106
111
|
// Flow 3 — Complete: mark a task done. Model-visible so the model can complete on request.
|
|
107
112
|
tool('complete_task', {
|
|
108
|
-
|
|
113
|
+
title: 'Complete task',
|
|
114
|
+
description: 'This will mark the selected task complete for everyone using Acme Tasks.',
|
|
109
115
|
annotations: confirmedWrite,
|
|
110
116
|
input: z.object({
|
|
111
|
-
task: z.string(),
|
|
112
|
-
title: z.string().min(1),
|
|
117
|
+
task: z.string().meta({ title: 'Task ID' }),
|
|
118
|
+
title: z.string().min(1).meta({ title: 'Task' }),
|
|
113
119
|
}),
|
|
114
120
|
output: z.object({
|
|
115
121
|
status: z.string(),
|
|
@@ -122,12 +128,13 @@ export default server(
|
|
|
122
128
|
}),
|
|
123
129
|
// Flow 2 helper (widget-only): re-prioritize a task from the list widget.
|
|
124
130
|
tool('set_priority', {
|
|
131
|
+
title: 'Change task priority',
|
|
125
132
|
visibility: ['app'],
|
|
126
133
|
description: 'Re-prioritize a task from the list widget.',
|
|
127
134
|
annotations: widgetWrite,
|
|
128
135
|
input: z.object({
|
|
129
|
-
task: z.string(),
|
|
130
|
-
priority,
|
|
136
|
+
task: z.string().meta({ title: 'Task ID' }),
|
|
137
|
+
priority: priority.meta({ title: 'New priority' }),
|
|
131
138
|
}),
|
|
132
139
|
output: z.object({
|
|
133
140
|
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({}),
|
|
@@ -273,7 +273,7 @@ tool('prepare_time_off', {
|
|
|
273
273
|
});
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
-
Use a stable lowercase/number/underscore id and a flat form of string/number/integer/boolean, string choices or multi-select, with optional `email`, `uri`, `date`, or `date-time` formats. Nested objects and credential-shaped fields fail with `invalid_elicitation_schema`. Every interactive flow must place all `ctx.elicit` calls before its first connector operation or compilation fails with `invalid_elicitation_flow`. Embedded/headless clients receive `input_requested`; bidirectional MCP transports map the primitive to standard form `elicitation/create`. An adapter that cannot carry the request
|
|
276
|
+
Use a stable lowercase/number/underscore id and a flat form of string/number/integer/boolean, string choices or multi-select, with optional `email`, `uri`, `date`, or `date-time` formats. Nested objects and credential-shaped fields fail with `invalid_elicitation_schema`. Every interactive flow must place all `ctx.elicit` calls before its first connector operation or compilation fails with `invalid_elicitation_flow`. Embedded/headless clients receive `input_requested`; bidirectional MCP transports map the primitive to standard form `elicitation/create`. An adapter that cannot carry the request returns a structured non-executing `interaction_unavailable` result. Accept validates and resumes without rerunning completed steps; invalid content returns `arg_invalid` and leaves the same interaction pending; decline/cancel stop. Elicitation gathers missing input and does not replace confirmation. In a flow marked `confirm: true`, every eligible `input_requested` precedes `tool_proposed`; the final proposal reviews the original input, elicited values, and sole exact connector action. Accept is bound to that action and only then may execution start. A confirmable flow may contain at most one connector operation; additional operations fail with `invalid_confirmation_flow`. MCP uses final standard form confirmation on capable bidirectional transports and fails closed otherwise. Setting `interactions: { confirmationFallback: "host" }` in the server options explicitly trusts native host approval only when confirmation transport is unavailable and no elicited input is needed; it is never inferred from client name and does not replace authorization. Omitted or `false` annotations execute directly; hints alone never gate. `annotations.action({ confirm: true })` explicitly enables confirmation; `annotations.action({ confirm: false })` explicitly preserves direct execution.
|
|
277
277
|
|
|
278
278
|
## Compute connector example
|
|
279
279
|
|
|
@@ -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
|
|
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
|
|
|
@@ -174,7 +176,7 @@ The callback records declarative fulfilment at author time; the shared runtime e
|
|
|
174
176
|
|
|
175
177
|
## Structured missing input
|
|
176
178
|
|
|
177
|
-
A tool authored with `ctx.elicit({ id, message, input })` produces `input_requested` when it reaches
|
|
179
|
+
A tool authored with `ctx.elicit({ id, message, input })` produces `input_requested` when it reaches missing input. Built-in and headless renderers present it and call `respond(id, { action: "accept", content })`; decline/cancel stop. Accepted content is schema-validated and completed steps are not rerun; invalid content returns `arg_invalid` and keeps the interaction pending. Elicitation gathers an input and does not approve a later write. Every interactive flow collects elicited input before its first connector operation; every eligible `input_requested` precedes `tool_proposed`. In a `confirm: true` flow, the final proposal reviews original input, elicited values, and the sole exact connector action; a confirmable flow has at most one connector operation. Accept is bound to that action. Bidirectional MCP and the embedded assistant use the same advertised endpoints and interaction-event protocol: missing input maps to standard `elicitation/create`, and an adapter that cannot carry that request fails safely with a structured non-executing result. The same negotiated channel carries the final affirmative confirmation. Setting `interactions: { confirmationFallback: "host" }` in server options explicitly trusts native host approval only when confirmation transport is unavailable and no elicited input is required; embedded/headless confirmation remains Noodle-owned. Omitted or false annotations execute directly; hints never gate.
|
|
178
180
|
|
|
179
181
|
## Verified session context (identity and claims)
|
|
180
182
|
|
|
@@ -281,7 +283,7 @@ if (pendingId) {
|
|
|
281
283
|
|
|
282
284
|
`view_available` 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. It 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.
|
|
283
285
|
|
|
284
|
-
`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
|
|
286
|
+
`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.
|
|
285
287
|
|
|
286
288
|
## Toolchain requirements
|
|
287
289
|
|
|
@@ -127,7 +127,7 @@ prompt('summarize_ticket', {
|
|
|
127
127
|
|
|
128
128
|
### Non-trivial tool: ctx connectors, annotations, visibility, async
|
|
129
129
|
|
|
130
|
-
`ctx` is `{ input, user, connectors }`. Bind connectors with `use` on the server, then call one inside `fulfil` to record a step. `annotations.readOnly()` declares a closed-world safe read. TypeScript action helpers enforce confirmation only with `{ confirm: true }`; omitted or `false` executes directly, and action/destructive/open-world hints alone never enable the gate. `visibility` defaults to `['model', 'app']` — set `['app']` to hide a helper from the model. `fulfil` may be `async` (the compiler awaits it while recording).
|
|
130
|
+
`ctx` is `{ input, user, connectors }`. Bind connectors with `use` on the server, then call one inside `fulfil` to record a step. `annotations.readOnly()` declares a closed-world safe read. TypeScript action helpers enforce confirmation only with `{ confirm: true }`; omitted or `false` executes directly, and action/destructive/open-world hints alone never enable the gate. For stateless hosts that cannot present Noodle confirmation, set `interactions: { confirmationFallback: 'host' }` in the `server` options to explicitly trust native host write approval; omission remains fail-closed and the fallback never supplies missing `ctx.elicit` input. `visibility` defaults to `['model', 'app']` — set `['app']` to hide a helper from the model. `fulfil` may be `async` (the compiler awaits it while recording).
|
|
131
131
|
|
|
132
132
|
```ts
|
|
133
133
|
import { annotations, connector, server, tool, z } from '@noodleseed/one';
|