@terreno/mcp 57.9.0 → 57.11.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/dist/docs/upgrades/57.10.1.md +54 -0
- package/dist/docs/upgrades/57.11.0.md +80 -0
- package/dist/docs/versioned/next/README.md +1 -0
- package/dist/docs/versioned/next/explanation/README.md +4 -0
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
- package/dist/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
- package/dist/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
- package/dist/docs/versioned/next/explanation/ai-observability.md +101 -0
- package/dist/docs/versioned/next/explanation/durable-agent-harness.md +544 -0
- package/dist/docs/versioned/next/explanation/example-coverage.md +3 -1
- package/dist/docs/versioned/next/explanation/local-first-data.md +1 -0
- package/dist/docs/versioned/next/explanation/loop-engineering.md +1 -1
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
- package/dist/docs/versioned/next/explanation/typescript-configuration.md +1 -1
- package/dist/docs/versioned/next/explanation/versioning-policy.md +1 -1
- package/dist/docs/versioned/next/how-to/README.md +7 -0
- package/dist/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
- package/dist/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
- package/dist/docs/versioned/next/how-to/ai-feature-development.md +156 -0
- package/dist/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
- package/dist/docs/versioned/next/how-to/call-external-apis.md +1 -1
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
- package/dist/docs/versioned/next/how-to/circleci.md +5 -3
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +31 -1
- package/dist/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
- package/dist/docs/versioned/next/how-to/preview-demo.md +7 -3
- package/dist/docs/versioned/next/how-to/run-harness-on-multiple-instances.md +93 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -1
- package/dist/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
- package/dist/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
- package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
- package/dist/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
- package/dist/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
- package/dist/docs/versioned/next/reference/README.md +3 -0
- package/dist/docs/versioned/next/reference/admin-config.md +2 -0
- package/dist/docs/versioned/next/reference/admin-frontend.md +216 -0
- package/dist/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
- package/dist/docs/versioned/next/reference/ai-harness.md +1974 -0
- package/dist/docs/versioned/next/reference/ai.md +251 -25
- package/dist/docs/versioned/next/reference/api.md +110 -2
- package/dist/docs/versioned/next/reference/blocks.md +178 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +2 -1
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +11 -0
- package/dist/docs/versioned/next/reference/mcp-server.md +17 -0
- package/dist/docs/versioned/next/reference/syncdb.md +156 -6
- package/dist/docs/versioned/next/reference/test.md +11 -2
- package/dist/docs/versioned/next/reference/ui.md +331 -6
- package/dist/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
- package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
- package/dist/docs/versioned/next/tasks/ai-observability.md +233 -104
- package/dist/docs/versioned/next/tasks/durable-agent-harness.md +185 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +37 -0
- package/dist/tools.js.map +1 -1
- package/package.json +3 -2
- package/src/docs/upgrades/57.10.1.md +54 -0
- package/src/docs/upgrades/57.11.0.md +80 -0
- package/src/docs/versioned/next/README.md +1 -0
- package/src/docs/versioned/next/explanation/README.md +4 -0
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +2 -2
- package/src/docs/versioned/next/explanation/agent-ui-asks.md +299 -0
- package/src/docs/versioned/next/explanation/agent-ui-blocks.md +67 -0
- package/src/docs/versioned/next/explanation/ai-observability.md +101 -0
- package/src/docs/versioned/next/explanation/durable-agent-harness.md +544 -0
- package/src/docs/versioned/next/explanation/example-coverage.md +3 -1
- package/src/docs/versioned/next/explanation/local-first-data.md +1 -0
- package/src/docs/versioned/next/explanation/loop-engineering.md +1 -1
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +29 -1
- package/src/docs/versioned/next/explanation/typescript-configuration.md +1 -1
- package/src/docs/versioned/next/explanation/versioning-policy.md +1 -1
- package/src/docs/versioned/next/how-to/README.md +7 -0
- package/src/docs/versioned/next/how-to/agent-ui-asks.md +730 -0
- package/src/docs/versioned/next/how-to/agent-ui-blocks.md +104 -0
- package/src/docs/versioned/next/how-to/ai-feature-development.md +156 -0
- package/src/docs/versioned/next/how-to/build-a-durable-workflow.md +232 -0
- package/src/docs/versioned/next/how-to/call-external-apis.md +1 -1
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +2 -0
- package/src/docs/versioned/next/how-to/circleci.md +5 -3
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +31 -1
- package/src/docs/versioned/next/how-to/observe-llm-calls.md +197 -0
- package/src/docs/versioned/next/how-to/preview-demo.md +7 -3
- package/src/docs/versioned/next/how-to/run-harness-on-multiple-instances.md +93 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -1
- package/src/docs/versioned/next/how-to/ship-a-new-task-version.md +76 -0
- package/src/docs/versioned/next/how-to/upgrade-terreno.md +1 -1
- package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +688 -0
- package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +10 -6
- package/src/docs/versioned/next/implementationPlans/ai-observability.md +475 -157
- package/src/docs/versioned/next/implementationPlans/durable-agent-harness.md +340 -0
- package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -1
- package/src/docs/versioned/next/reference/README.md +3 -0
- package/src/docs/versioned/next/reference/admin-config.md +2 -0
- package/src/docs/versioned/next/reference/admin-frontend.md +216 -0
- package/src/docs/versioned/next/reference/agent-ui-asks.md +1059 -0
- package/src/docs/versioned/next/reference/ai-harness.md +1974 -0
- package/src/docs/versioned/next/reference/ai.md +251 -25
- package/src/docs/versioned/next/reference/api.md +110 -2
- package/src/docs/versioned/next/reference/blocks.md +178 -0
- package/src/docs/versioned/next/reference/environment-variables.md +2 -1
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +11 -0
- package/src/docs/versioned/next/reference/mcp-server.md +17 -0
- package/src/docs/versioned/next/reference/syncdb.md +156 -6
- package/src/docs/versioned/next/reference/test.md +11 -2
- package/src/docs/versioned/next/reference/ui.md +331 -6
- package/src/docs/versioned/next/tasks/agent-ui-asks.md +213 -0
- package/src/docs/versioned/next/tasks/agent-ui-blocks.md +2 -1
- package/src/docs/versioned/next/tasks/ai-observability.md +233 -104
- package/src/docs/versioned/next/tasks/durable-agent-harness.md +185 -0
|
@@ -10,6 +10,7 @@ React Native UI component library (a large component library). Layout (Box, Page
|
|
|
10
10
|
- Actions: `Button`, `IconButton`, `Link`
|
|
11
11
|
- Feedback: `Spinner`, `Modal`, `Toast`
|
|
12
12
|
- Notifications: `NotificationBell`, `NotificationInbox`, `NotificationPreferences`
|
|
13
|
+
- AI chat: `GPTChat`, `AskCard` (agent asks in the transcript), `SimpleAskCard` (an ask's simple card for narrow layouts), `BlocksView` (a whole-reply YAML document)
|
|
13
14
|
- Authentication: `SocialLoginButton`, `LoginScreen`, `SignUpScreen`
|
|
14
15
|
- Theming: `TerrenoProvider`, `useTheme`, custom icon registry (`icons` prop)
|
|
15
16
|
- **Type re-exports:** `StyleProp`, `ViewStyle` (re-exported from react-native to avoid version conflicts)
|
|
@@ -32,7 +33,7 @@ supported and is convenient when startup cost is not material:
|
|
|
32
33
|
import {Box, DataTable, Icon} from "@terreno/ui";
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
Heavy optional widgets (`GPTChat`, `EmojiSelector`, `MarkdownEditor`, consent flows, `ChartCard`, `Scorecard`, `SparklineChart`, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`, and related admin tools) are
|
|
36
|
+
Heavy optional widgets (`GPTChat`, `AskCard`, `BlocksView`, `EmojiSelector`, `MarkdownEditor`, consent flows, `ChartCard`, `Scorecard`, `SparklineChart`, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`, and related admin tools) are
|
|
36
37
|
re-exported from the root entry through lazy boundaries. Importing them from `@terreno/ui` stays type-compatible, but
|
|
37
38
|
their implementation modules load on first render instead of during the initial root import. `DashboardGrid` stays eager.
|
|
38
39
|
`MarkdownView` and `DataTable` header info defer `react-native-markdown-display`;
|
|
@@ -250,6 +251,18 @@ Buttons automatically size to their content unless `fullWidth` is specified:
|
|
|
250
251
|
|
|
251
252
|
Internally, Button sets `alignSelf: 'flex-start'` when `fullWidth={false}` to prevent stretching in column layouts.
|
|
252
253
|
|
|
254
|
+
A label stays on one line by default, so a long label can make the button wider than its container.
|
|
255
|
+
Set `wrapText` to keep the button inside its container instead: the label wraps onto centered lines
|
|
256
|
+
and the button grows taller. A `size="sm"` button grows from its 28px height instead of clipping the
|
|
257
|
+
second line. `SimpleAskCard` and `AskCard` set `wrapText` on their answer buttons, because their
|
|
258
|
+
labels come from the agent.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<Box direction="row" width={160}>
|
|
262
|
+
<Button onClick={handleSave} text="Save all changes now" wrapText />
|
|
263
|
+
</Box>
|
|
264
|
+
```
|
|
265
|
+
|
|
253
266
|
### TextField grow and maxHeight
|
|
254
267
|
|
|
255
268
|
`grow` makes a multiline field expand with its content. Pair it with `maxHeight` (pixels) to cap the expansion; past the cap the content scrolls.
|
|
@@ -390,10 +403,321 @@ The attach control (`FilePickerButton`) opens an anchored dropdown with **Photo
|
|
|
390
403
|
/>
|
|
391
404
|
```
|
|
392
405
|
|
|
406
|
+
Pass `uiBlocks` to render each assistant message through `BlocksView`. A streaming message
|
|
407
|
+
shows each finished top-level block and a spinner for the block still arriving. `reply`
|
|
408
|
+
calls `onSubmit` with the button text. `open` and `select` call `onBlockAction`. `callback`
|
|
409
|
+
calls `onBlockCallback` and the button stays loading until that promise settles. Return
|
|
410
|
+
`{replace: "block", blocks}` to swap that block, or `{text}` to place an assistant message
|
|
411
|
+
immediately after the message that started the callback. A result that arrives after
|
|
412
|
+
`currentHistoryId` changes is dropped. `hostActions` disables callback names the host did
|
|
413
|
+
not list. `resolveDataset` loads `ref` datasets. `resolveImage` turns a `file:` image id
|
|
414
|
+
into a URL.
|
|
415
|
+
|
|
416
|
+
```tsx
|
|
417
|
+
<GPTChat
|
|
418
|
+
currentMessages={messages}
|
|
419
|
+
histories={histories}
|
|
420
|
+
hostActions={["export_csv"]}
|
|
421
|
+
onBlockAction={handleBlockAction}
|
|
422
|
+
onBlockCallback={handleBlockCallback}
|
|
423
|
+
onCreateHistory={onCreateHistory}
|
|
424
|
+
onDeleteHistory={onDeleteHistory}
|
|
425
|
+
onSelectHistory={onSelectHistory}
|
|
426
|
+
onSubmit={onSubmit}
|
|
427
|
+
uiBlocks
|
|
428
|
+
/>
|
|
429
|
+
```
|
|
430
|
+
|
|
393
431
|
Operator steps: [Add a GPT chat mascot](../how-to/add-gpt-chat-mascot.md). Demo story: `GPTChat` → `Mascot`.
|
|
432
|
+
Blocks playground: `BlocksPlayground`.
|
|
394
433
|
The example AI screen demonstrates a consumer selecting one of four bundled mascot
|
|
395
434
|
images once per mount.
|
|
396
435
|
|
|
436
|
+
#### Asks
|
|
437
|
+
|
|
438
|
+
`GPTChat` shows an [agent ask](agent-ui-asks.md) as an `AskCard` in the transcript. Set `ask` on
|
|
439
|
+
the ask's `tool-call` message and pass `onAskSubmit`:
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
<GPTChat
|
|
443
|
+
askErrors={askErrors}
|
|
444
|
+
currentMessages={currentMessages}
|
|
445
|
+
histories={histories}
|
|
446
|
+
onAskSubmit={handleAskSubmit}
|
|
447
|
+
onCreateHistory={onCreateHistory}
|
|
448
|
+
onDeleteHistory={onDeleteHistory}
|
|
449
|
+
onSelectHistory={onSelectHistory}
|
|
450
|
+
onSubmit={onSubmit}
|
|
451
|
+
/>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
| Prop or field | Type | Description |
|
|
455
|
+
| --- | --- | --- |
|
|
456
|
+
| `GPTChatMessage.ask` | `ChatAsk` | On a `tool-call` message: the ask (`kind`, `input`), its `toolCallId`, `status` (`pending`, `answered`, or `cancelled`), and optional `response` and `simple` card. The chat shows an `AskCard` instead of the tool call. |
|
|
457
|
+
| `onAskSubmit` | `(submission: {toolCallId, response}) => void \| Promise<void>` | Called when the user answers a pending ask. `response` is the answer envelope. The pressed control shows a loading state until the promise settles. Without it, asks show but cannot be answered. |
|
|
458
|
+
| `askErrors` | `Record<string, AskValidationError[]>` | Errors for the last answer to each ask, keyed by tool call id, such as the `fields` of a 400 `Invalid askResponse`. Shown inside the card. |
|
|
459
|
+
| `resolveAskFiles` | `AskFilesResolver` | Turns the files picked for a `files` ask into the refs its answer sends. Defaults to `resolveAskFilesAsDataUrls`. Pass an uploader to send `{fileId}` refs; see [Accept uploads with or without GCS](../how-to/agent-ui-asks.md#accept-uploads-with-or-without-gcs). |
|
|
460
|
+
|
|
461
|
+
- The ask's `tool-result` message stays in `currentMessages` but is not shown. Keep it: message
|
|
462
|
+
indexes must match the stored `prompts` rows that ratings use. When `ask.response` is unset, the
|
|
463
|
+
card reads the answer from that message.
|
|
464
|
+
- A pending ask takes focus when it appears: DOM focus on the card, labelled with the ask's
|
|
465
|
+
`title`, on web; accessibility focus on the question, an accessibility header, on native.
|
|
466
|
+
- Answered and cancelled asks collapse to a one-line summary.
|
|
467
|
+
|
|
468
|
+
The example AI screen, `example-frontend/app/(tabs)/ai.tsx`, handles the stream events, saved
|
|
469
|
+
rows, answers, and errors. Steps: [Add agent asks to a chat](../how-to/agent-ui-asks.md).
|
|
470
|
+
|
|
471
|
+
### BlocksView
|
|
472
|
+
|
|
473
|
+
`BlocksView` paints a whole-reply document from `@terreno/blocks`. Pass the assistant text
|
|
474
|
+
as `document`. Leaf blocks in this slice are `heading`, `text`, `metric`, `badge`,
|
|
475
|
+
`divider`, and `context`. `columns` is a row from the `md` breakpoint and a stack on `sm`.
|
|
476
|
+
`card` groups children under an optional title. A `callout` draws `Banner` and is not dismissible. An `image` draws `Image` and requires `alt`; pass `imageHosts` to allow `https` hosts. A `details` block draws `Accordion`. A `chart` draws `LineChart`, `BarChart`,
|
|
477
|
+
`AreaChart`, or `DonutChart` from an inline dataset or from `points`. The series is only
|
|
478
|
+
`{label, value}` — a point color in the document is not passed through. A `table` draws
|
|
479
|
+
`DataTable`. A `ref` dataset stays empty until `resolveDataset` returns its rows, and the
|
|
480
|
+
chart shows `loading` while that promise is in flight. A rejected fetch clears that
|
|
481
|
+
loading state. A segmented control highlights the option whose `data` matches the target
|
|
482
|
+
chart or table. A `select` action switches a table the same way it switches a chart.
|
|
483
|
+
An image `file:` ref is not passed to `Image` until `resolveImage` returns a URL; the
|
|
484
|
+
alt text still shows.
|
|
485
|
+
|
|
486
|
+
A string that is not a document becomes one `text` block. A document that fails
|
|
487
|
+
`validateBlocks` shows a `Banner` with the first three errors and the raw text inside a
|
|
488
|
+
collapsed `Accordion`.
|
|
489
|
+
|
|
490
|
+
An `actions` block draws `Button` and `SegmentedControl`. `onAction` receives
|
|
491
|
+
`{action, blockId, elementId}` for reply, open, select, and callback. A `select`
|
|
492
|
+
action also stores the chosen dataset on the target chart for this view. Pass
|
|
493
|
+
`hostActions` to disable callback buttons whose names are not in that list. Omit
|
|
494
|
+
`hostActions` and every callback stays enabled. `pendingElementIds` shows those
|
|
495
|
+
buttons as loading. `overrides` replaces a block by id.
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
<BlocksView
|
|
499
|
+
document={reply}
|
|
500
|
+
hostActions={["export_csv"]}
|
|
501
|
+
onAction={handleAction}
|
|
502
|
+
testID="assistant-blocks"
|
|
503
|
+
/>
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Demo story: `BlocksView`.
|
|
507
|
+
|
|
508
|
+
### HtmlFrame
|
|
509
|
+
|
|
510
|
+
`HtmlFrame` paints one `html` block. On web it is an `<iframe sandbox="" referrerpolicy="no-referrer">` whose `srcdoc` starts with a Content-Security-Policy meta tag (`default-src 'none'`). On native it is a WebView with JavaScript off, and navigation after the first load is rejected. `BlocksView` renders that frame only when `allowHtml` is true and `streaming` is false. Otherwise the block is a card that says the preview is off or still arriving. `GPTChat` passes `allowHtml` through.
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
<HtmlFrame height="md" html="<h1>Invoice</h1>" title="Invoice preview" />
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### AskCard
|
|
517
|
+
|
|
518
|
+
One agent ask in a chat transcript: controls while it is pending, a summary line after. `GPTChat`
|
|
519
|
+
renders it for messages with `ask`. Render it directly in a custom transcript. A saved ask whose
|
|
520
|
+
input no longer validates, or whose `accept` answer has no `content` object, shows the generic
|
|
521
|
+
line ("You answered this question.") and no sent values.
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
<AskCard ask={ask} errors={errors} onSubmit={handleAskSubmit} testID="plan-ask" />
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
| Prop | Type | Description |
|
|
528
|
+
| --- | --- | --- |
|
|
529
|
+
| `ask` | `ChatAsk` | The ask. Pending asks are interactive. Answered and cancelled asks show a summary. |
|
|
530
|
+
| `errors` | `AskValidationError[]` | Errors for the last answer, shown under the controls |
|
|
531
|
+
| `onSubmit` | `AskSubmitHandler` | Called with `{toolCallId, response}`. Without it, the card cannot be answered: buttons and the select are disabled, and radio and checkbox options show as plain text. When the promise it returns rejects, the ask stays open and the card shows "Your answer could not be sent. Try again." until the next answer. |
|
|
532
|
+
| `promptRef` | `React.Ref<Text>` (React Native) | Receives the question's native text, which is an accessibility header. `GPTChat` uses it to move screen reader focus to a pending ask on native. |
|
|
533
|
+
| `resolveAskFiles` | `AskFilesResolver` | For a `files` ask: turns the picked files into refs on Submit. Defaults to data URLs. `GPTChat` passes its own `resolveAskFiles`. |
|
|
534
|
+
| `testID` | string | Defaults to `ask-card`. `GPTChat` passes `gpt-ask-<toolCallId>`. |
|
|
535
|
+
|
|
536
|
+
A saved ask whose input no longer passes `validateAskInput` shows "This question cannot be shown.
|
|
537
|
+
Send a message to continue." while pending, and "You answered this question." once answered.
|
|
538
|
+
|
|
539
|
+
`choice` controls:
|
|
540
|
+
|
|
541
|
+
| Ask | Controls |
|
|
542
|
+
| --- | --- |
|
|
543
|
+
| The simple card has a button for every option (`handoff: false`), and every option label is at most 20 characters | The card's buttons as quick replies, then Skip unless `allowDecline` is `false`. A tap answers. |
|
|
544
|
+
| Up to 8 options | `RadioField`, then Submit (`submitLabel`) and Skip |
|
|
545
|
+
| More than 8 options | Searchable `SelectField`, then Submit and Skip |
|
|
546
|
+
| `select: "many"` | `MultiselectField` with a hint such as "Choose 1 to 3.", then a `TextField` titled `otherLabel` (default "Other") when `allowOther` is `true`, then Submit and Skip |
|
|
547
|
+
|
|
548
|
+
The selection starts on the ask's `default`. Submit is enabled only when `validateAskResponse`
|
|
549
|
+
accepts the selection. For `select: "many"`, the answer lists the checked ids in option order and
|
|
550
|
+
sends the Other text trimmed, leaving `other` out when the field is blank. When the checked
|
|
551
|
+
options and the Other text add up to more than `maxSelected`, the checkboxes say so ("You chose
|
|
552
|
+
4. Choose at most 3."). An Other text over 500 characters, or a server error at `content.other`,
|
|
553
|
+
shows on the Other field. When `ask.simple` is absent, the card derives it with `toSimpleCard`. An
|
|
554
|
+
ask whose input fails `validateAskInput` shows "This question cannot be shown. Send a message to
|
|
555
|
+
continue." instead of controls.
|
|
556
|
+
|
|
557
|
+
`confirm` controls: the simple card's two buttons in its order, the approve button (`confirmLabel`,
|
|
558
|
+
default "Confirm") first and the deny button (`denyLabel`, default "Cancel") last. The approve
|
|
559
|
+
button uses the `destructive` variant when the ask sets `destructive: true`, else `primary`; the
|
|
560
|
+
deny button uses `ghost`. Skip follows only when `allowDecline` is `true`. A tap answers
|
|
561
|
+
`{"confirmed": true}` or `{"confirmed": false}`. Without `onSubmit`, both buttons are disabled.
|
|
562
|
+
|
|
563
|
+
`markdown` controls: a `MarkdownEditorField` (edit and preview, at most 320 pt tall) that starts
|
|
564
|
+
on the ask's `initial` draft and shows `placeholder` while empty, then Submit (`submitLabel`) and
|
|
565
|
+
Skip unless `allowDecline` is `false`. Under the editor a hint gives the length and the bounds,
|
|
566
|
+
such as "1,240 / 2,000 characters. At least 20." Submit is enabled only when
|
|
567
|
+
`validateAskResponse` accepts the text, and it sends `{markdown, changed}` with `changed` true
|
|
568
|
+
when the text differs from `initial`. Text over `maxLength`, or a server error at
|
|
569
|
+
`content.markdown`, shows on the editor. Without `onSubmit`, the editor and both buttons are
|
|
570
|
+
disabled. An answered markdown ask shows the sent text under its summary; text over 280
|
|
571
|
+
characters shows a preview cut at a word, with Show all and Show less.
|
|
572
|
+
|
|
573
|
+
`form` controls: one field per entry in `fields`, in order, titled with the field's `label`
|
|
574
|
+
("(required)" appended for required fields) and its `helperText`, then Submit (`submitLabel`) and
|
|
575
|
+
Skip unless `allowDecline` is `false`. Each field uses the `@terreno/ui` control for its type:
|
|
576
|
+
|
|
577
|
+
| Field `type` | Control | Sends |
|
|
578
|
+
| --- | --- | --- |
|
|
579
|
+
| `text`, `email`, `url` | `TextField` (`text`, `email`, `url`) | The trimmed text |
|
|
580
|
+
| `phone` | `TextField` (`phoneNumber`) | E.164, such as `+14155552671`, when the number parses (as a US number without a country code); else the trimmed text |
|
|
581
|
+
| `number` | `TextField` | A number when the text is plain decimal notation; else the text, so validation says why |
|
|
582
|
+
| `textarea` | `TextArea` | The trimmed text |
|
|
583
|
+
| `date` | `DateTimeField` (`date`) | `YYYY-MM-DD` |
|
|
584
|
+
| `time` | `DateTimeField` (`time`) with its time zone picker (the device's zone to start) | 24-hour `HH:mm` as shown; the zone is not sent |
|
|
585
|
+
| `datetime` | `DateTimeField` (`datetime`) with a time zone picker | An ISO datetime with the chosen zone's offset, such as `2026-10-01T09:30:00-07:00` |
|
|
586
|
+
| `boolean` | `BooleanField` | `true` or `false`, always |
|
|
587
|
+
| `select` | `SelectField` | The option id |
|
|
588
|
+
| `multiselect` | `MultiselectField` | The checked option ids |
|
|
589
|
+
|
|
590
|
+
Fields start on their `default`. Blank fields are left out of `values`. Submit is enabled only
|
|
591
|
+
when `validateAskResponse` accepts the values and no date, time, or datetime field holds an
|
|
592
|
+
unfinished entry. `DateTimeField` reports that through `onEntryStatusChange`, such as
|
|
593
|
+
"0 / 5 / 026": the field says "Enter a complete date, or clear it." until the user finishes it,
|
|
594
|
+
and clearing every part leaves the field out. After the user edits a field, it says what is
|
|
595
|
+
wrong in plain words, such as "Enter a number from 1 to 500." or "This field is required." A
|
|
596
|
+
server error whose path is `content.values.<id>` (or an item under it) shows on that field until
|
|
597
|
+
the user edits it; other errors show under the form. Without `onSubmit`, every field and both
|
|
598
|
+
buttons are disabled. An answered form lists each sent field as its label and a readable value
|
|
599
|
+
(Yes or No, option labels, "Oct 1, 2026", "9:30 AM", text shortened to one line of 80
|
|
600
|
+
characters) under its summary.
|
|
601
|
+
|
|
602
|
+
`files` controls: a hint built from the counts and types, such as "Up to 3 files: images, PDFs or
|
|
603
|
+
text files.", then a "Choose files" `FilePickerButton` ("Add files" once some are picked), the
|
|
604
|
+
picked files as an `AttachmentPreview` with a remove control on each, then Submit (`submitLabel`,
|
|
605
|
+
default "Submit") and Skip unless `allowDecline` is `false`. The picker offers Photo Library only
|
|
606
|
+
when `accept` has `image`, and its document picker offers only the accepted MIME types. It allows
|
|
607
|
+
several files when `maxFiles` is more than 1 and is disabled once `maxFiles` files are picked.
|
|
608
|
+
Submit is enabled only when `validateAskResponse` accepts the picked names, types, and sizes. On
|
|
609
|
+
Submit, the card calls `resolveAskFiles` with the picked files and sends `{files: refs}`; Submit
|
|
610
|
+
shows a spinner and Skip is disabled until the answer is sent. When the ask ends another way while
|
|
611
|
+
the resolver runs, such as an answer from another tab, its refs are not sent. If the resolver throws, the ask
|
|
612
|
+
stays open and the card shows "The files could not be sent. Try again, or pick them again." Server
|
|
613
|
+
errors, such as `MIME_MISMATCH`, show under the picked files. Each file's type is
|
|
614
|
+
`selectedFileMimeType(file)`, so a CSV a picker reports as `application/vnd.ms-excel` is sent as
|
|
615
|
+
`text/csv`.
|
|
616
|
+
|
|
617
|
+
| How the ask ended | Summary |
|
|
618
|
+
| --- | --- |
|
|
619
|
+
| `files` `accept` | You sent `<n>` files: `<filenames>`, or You sent 1 file: `<filename>` |
|
|
620
|
+
| `form` `accept` | You sent the form (`<n>` fields) |
|
|
621
|
+
| `markdown` `accept` | You approved the draft as is (`changed: false`), or You edited the draft (`<n>` characters) |
|
|
622
|
+
| `confirm` `accept` | You confirmed: `<confirmLabel>`, or You declined: `<denyLabel>`, with the default labels when the ask sets none |
|
|
623
|
+
| `choice` `accept` | You chose: `<option labels>`. With Other text, it adds "`<otherLabel>`: `<text>`" (the label defaults to "Other"), or shows only that when no option was checked. An empty `select: "many"` answer shows "You chose none of the options." |
|
|
624
|
+
| `decline` | You skipped this question. |
|
|
625
|
+
| `cancel` with `user_sent_message` | Not answered: you sent a message instead. |
|
|
626
|
+
| `cancel` with `one_ask_at_a_time` | Not asked: the assistant asked another question first. |
|
|
627
|
+
| Any other `cancel`, or status `cancelled` without a response | This question was cancelled. |
|
|
628
|
+
| Status `answered` without a response | You answered this question. |
|
|
629
|
+
|
|
630
|
+
| Element | testID |
|
|
631
|
+
| --- | --- |
|
|
632
|
+
| Card | `{testID}` |
|
|
633
|
+
| Quick reply row | `{testID}-quick-replies` |
|
|
634
|
+
| Quick reply or Skip button | `{testID}-button-<button id>`, such as `{testID}-button-option:team` or `{testID}-button-skip` |
|
|
635
|
+
| Confirm button row | `{testID}-confirm-buttons` |
|
|
636
|
+
| Confirm approve or deny button | `{testID}-button-approve`, `{testID}-button-deny` |
|
|
637
|
+
| Radio options (up to 8) | `{testID}-radio` |
|
|
638
|
+
| Select | `{testID}-select` |
|
|
639
|
+
| Checkboxes (`select: "many"`) | `{testID}-multiselect` |
|
|
640
|
+
| Other text field | `{testID}-other` |
|
|
641
|
+
| Markdown editor, and its text input | `{testID}-editor`, `{testID}-editor-input` |
|
|
642
|
+
| Sent markdown under an answered ask, and its Show all toggle | `{testID}-answer`, `{testID}-answer-toggle` |
|
|
643
|
+
| Form field, and its wrapper | `{testID}-field-<field id>` (a `BooleanField` switch is `{testID}-field-<field id>.switch`), `{testID}-form-field-<field id>` |
|
|
644
|
+
| Sent form values under an answered ask | `{testID}-answer` |
|
|
645
|
+
| Files hint, picker, and picked files | `{testID}-hint`, `{testID}-picker`, `{testID}-selected` |
|
|
646
|
+
| Files that could not be sent | `{testID}-resolve-error` |
|
|
647
|
+
| Radio or checkbox options as plain text, without `onSubmit` | `{testID}-options` |
|
|
648
|
+
| Submit | `{testID}-submit` |
|
|
649
|
+
| Answer errors | `{testID}-errors` |
|
|
650
|
+
| Summary | `{testID}-summary` |
|
|
651
|
+
| Ask that cannot be shown | `{testID}-invalid` |
|
|
652
|
+
| Answer that could not be sent | `{testID}-submit-error` |
|
|
653
|
+
|
|
654
|
+
Types: `AskCardProps`, `ChatAsk`, `ChatAskState`, `ChatAskStatus`, `AskSubmission`,
|
|
655
|
+
`AskSubmitHandler`, `AskFilesResolver`. Demo story: `AskCard`.
|
|
656
|
+
|
|
657
|
+
File ref helpers:
|
|
658
|
+
|
|
659
|
+
| Export | Description |
|
|
660
|
+
| --- | --- |
|
|
661
|
+
| `AskFilesResolver` | `(files: SelectedFile[]) => Promise<AskFileRef[]>`. Throw to keep the ask open. |
|
|
662
|
+
| `resolveAskFilesAsDataUrls` | The default resolver: every file as a `{url}` data URL |
|
|
663
|
+
| `selectedFileToDataUrlRef(file)` | One picked file as `{filename, mimeType, size, url}`. The data URL's media type is `selectedFileMimeType(file)`, and `size` is the decoded byte count. |
|
|
664
|
+
| `selectedFileMimeType(file)` | The picked file's normalized type. When the picker reports none, `application/octet-stream`, or `application/vnd.ms-excel` (Windows reports CSV files so), the type comes from the extension: `.csv`, `.gif`, `.jpeg`, `.jpg`, `.json`, `.pdf`, `.png`, `.txt`, `.webp`. Otherwise the reported type is kept. |
|
|
665
|
+
| `normalizeMimeType(mimeType)` | Drops parameters and lowercases: `Text/CSV; charset=utf-8` becomes `text/csv` |
|
|
666
|
+
|
|
667
|
+
`FilePickerButton` props used by the files card, also available to any caller:
|
|
668
|
+
|
|
669
|
+
| Prop | Type | Description |
|
|
670
|
+
| --- | --- | --- |
|
|
671
|
+
| `documentTypes` | `string[]` | MIME types the document picker offers. Defaults to PDF, text, CSV, and JSON. An empty list hides Document. |
|
|
672
|
+
| `includeImages` | boolean | Offer Photo Library. Defaults to `true`. When `false`, the button opens the document picker directly. |
|
|
673
|
+
| `text` | string | Shows an outline button with this label instead of the paperclip icon |
|
|
674
|
+
|
|
675
|
+
`SelectedFile.size` is the file's size in bytes when the picker reports it.
|
|
676
|
+
|
|
677
|
+
### SimpleAskCard
|
|
678
|
+
|
|
679
|
+
Any agent ask as its [simple card](agent-ui-asks.md#simple-cards): the title, the question, and up
|
|
680
|
+
to three full-width buttons that each send an exact answer. Use it in narrow layouts, such as a
|
|
681
|
+
watch-sized preview. It reads the card's `kind` only to word the handoff line.
|
|
682
|
+
|
|
683
|
+
```tsx
|
|
684
|
+
const [runTurn] = useGpthistoriesTurnMutation();
|
|
685
|
+
|
|
686
|
+
<SimpleAskCard
|
|
687
|
+
card={pendingAsk.simple}
|
|
688
|
+
onPress={(button) =>
|
|
689
|
+
runTurn({
|
|
690
|
+
body: {buttonId: button.id, surface: "compact", toolCallId: pendingAsk.toolCallId},
|
|
691
|
+
id: historyId,
|
|
692
|
+
})
|
|
693
|
+
}
|
|
694
|
+
pendingButtonId={sendingButtonId}
|
|
695
|
+
/>
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
| Prop | Type | Description |
|
|
699
|
+
| --- | --- | --- |
|
|
700
|
+
| `card` | `SimpleCard` | `pendingAsk.simple` from the server, or `simple` on an `{ask}` event |
|
|
701
|
+
| `onPress` | `(button: SimpleCardButton) => void \| Promise<void>` | Called with the pressed button. Send `{toolCallId: card.toolCallId, buttonId: button.id}` to the history's [`turn` action](agent-ui-asks.md#headless-endpoints), or send `button.response` as the answer. |
|
|
702
|
+
| `pendingButtonId` | string? | The button whose answer is still sending. It shows a spinner, the other buttons are disabled, and presses are ignored. |
|
|
703
|
+
| `testID` | string? | Defaults to `simple-ask-card` |
|
|
704
|
+
|
|
705
|
+
- A card with `handoff: true` shows "Continue on your phone" under the question, because its
|
|
706
|
+
buttons cannot give every answer. A `markdown` card shows "Edit on your phone" instead, a
|
|
707
|
+
`form` card "Fill it in on your phone", and a `files` card "Upload on your phone".
|
|
708
|
+
- Button styles map to `Button` variants the same way as `AskCard` quick replies: `primary` to
|
|
709
|
+
`primary`, `default` to `outline`, `destructive` to `destructive`, and `cancel` to `ghost`.
|
|
710
|
+
|
|
711
|
+
| Element | testID |
|
|
712
|
+
| --- | --- |
|
|
713
|
+
| Card | `{testID}` |
|
|
714
|
+
| Continue on your phone, or Edit on your phone | `{testID}-handoff` |
|
|
715
|
+
| Button | `{testID}-button-<button id>`, such as `{testID}-button-option:team` |
|
|
716
|
+
|
|
717
|
+
Types: `SimpleAskCardProps`, and `SimpleCard` and `SimpleCardButton` from `@terreno/blocks`. Demo
|
|
718
|
+
story: `SimpleAskCard`. Its demo answers a plan card on a watch-sized 198×242 pt screen, and its
|
|
719
|
+
"Every fixture" story draws every valid fixture's card at that size.
|
|
720
|
+
|
|
397
721
|
### SplitPage
|
|
398
722
|
|
|
399
723
|
Master-detail layout. Pass `listViewData` plus `renderListViewItem` for the list, and
|
|
@@ -401,11 +725,12 @@ Master-detail layout. Pass `listViewData` plus `renderListViewItem` for the list
|
|
|
401
725
|
screens the detail replaces the list until the user goes back.
|
|
402
726
|
|
|
403
727
|
`desktopChildrenMinWidth` opts into a minimum pixel width for each desktop child when there
|
|
404
|
-
are one or two children. `
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
728
|
+
are one or two children. `childColumnRounding` sets the border radius on each detail child
|
|
729
|
+
column container (defaults to `md`, 4px); the list column is excluded. `narrowViewportChildLabels`
|
|
730
|
+
opts into a labeled full-width pager on the narrow viewport. `narrowBelowWidth` uses that
|
|
731
|
+
viewport when the window is at or below the given pixel width; when omitted, the narrow
|
|
732
|
+
viewport follows `isNarrowViewport()`. These props are web only; the native `SplitPage`
|
|
733
|
+
ignores them. See `SplitPageProps` for when each prop applies and what is ignored.
|
|
409
734
|
`IconButton`'s `backgroundOpacity` tints only that button's background.
|
|
410
735
|
|
|
411
736
|
```typescript
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Task List: Agent UI Asks
|
|
2
|
+
|
|
3
|
+
**Status:** Phases 1–2 complete and Roast-passed (in review, [PR #1402](https://github.com/TerrenoLabs/terreno/pull/1402)). Phase 5 is the body-component IP (formerly Agent UI Blocks), folded in 2026-10-01 (D28). Phase 3 starts after B1.1 and B2.1.
|
|
4
|
+
**Supporting skills:** `ai-prompt-governance`, `terreno-ui`, `terreno-backend-api`, `mongoose-schema-safety`, `backend-test-env`, `update-docs`, `verify-ui-changes`.
|
|
5
|
+
|
|
6
|
+
Every task is a vertical slice: contract, producer and/or renderer, docs, and Bun tests.
|
|
7
|
+
Work the frontier (tasks whose blockers are complete). Body-component decisions and
|
|
8
|
+
Blocks AC1–AC20 live in [`docs/implementationPlans/agent-ui-asks.md`](../implementationPlans/agent-ui-asks.md)
|
|
9
|
+
under [Body components](../implementationPlans/agent-ui-asks.md#body-components-agent-ui-blocks).
|
|
10
|
+
The design record remains [`docs/implementationPlans/agent-ui-blocks.md`](../implementationPlans/agent-ui-blocks.md).
|
|
11
|
+
|
|
12
|
+
Tracer: `ask_choice` (select one) through `/gpt/prompt` pause → `askResponse` resume → `GPTChat` card; then the same ask answered by `buttonId` through `POST /gpt/histories/:id/turn`.
|
|
13
|
+
|
|
14
|
+
### Phase 1: Tracer — "pick one" end to end
|
|
15
|
+
|
|
16
|
+
- [x] **Task 1.1**: `ask_choice` (select one) pause and resume, proven on the server
|
|
17
|
+
- Delivers: `@terreno/blocks` asks module with the shared ask fields, `choice` (`select: one` only), `askResponseSchema`, `validateAskInput`, `validateAskResponse`, `toSimpleCard` + `simpleCardSchema` (choice-one rule), `ASK_LIMITS`, `ASK_ERROR_CODES`, and `askPromptSection`. If Agent UI Blocks Task 1.1 has not landed, this task creates the `blocks/` package scaffold exactly as that task lists it. `@terreno/ai`: `asks` route option; `createAskTools(["choice"])` (Zod `inputSchema` + `outputSchema`, no `execute`); `TERRENO_ASKS_SYSTEM_PROMPT` at the top of `prompts.ts`; the `/gpt/prompt` turn logic moved into `chatTurn.ts` behind an event sink so Task 1.3 can drive it without SSE; pause on an ask tool call (SSE `{ask}` with `simple`, `done.pendingAsk`, `GptHistory.pendingAsk` with `simple` and `responseMessages`, a `tool-call` row with `ask.status`); a second ask in one step is stored as `cancel` (`one_ask_at_a_time`); resume via `askResponse` (validate → tool-result row → replay `responseMessages` + tool result → stream, `{askResolved}` first); `cancel` when a `prompt` arrives while an ask is pending; 400 / 403 / 409 paths; `buildMessages` includes ask pairs; `AIRequest.metadata.ask`.
|
|
18
|
+
- Files: `blocks/src/asks/{schema,limits,errors,validateInput,validateResponse,simpleCard,prompt}.ts`, `blocks/src/asks/fixtures/{valid,invalid}/*.json`, `blocks/src/asks/*.test.ts`, `blocks/src/index.ts`; `ai/package.json`, `ai/src/service/asks.ts`, `ai/src/service/chatTurn.ts`, `ai/src/service/prompts.ts`, `ai/src/routes/gpt.ts`, `ai/src/routes/gptHistories.ts` (server-owned `pendingAsk`, read-only in OpenAPI), `ai/src/models/gptHistory.ts`, `ai/src/service/aiService.ts`, `ai/src/types/index.ts`, `ai/src/index.ts`, `ai/src/aiApp.ts`, `ai/src/routes/gpt.test.ts`, `ai/src/service/aiService.test.ts`, `ai/src/service/asks.test.ts`; package registration (root `package.json`, `knip.jsonc`, `codecov.yml`, `bun.lock`, `.circleci/*`, `.github/workflows/{blocks-ci,ai-ci,cd,publish-on-tag}.yml`, CI scripts under `scripts/`, `example-backend/Dockerfile`); `example-backend/src/__snapshots__/openapi.test.ts.snap`.
|
|
19
|
+
- Blocked by: none (IP approval)
|
|
20
|
+
- Docs: `docs/reference/agent-ui-asks.md` (new: envelope, `choice`, limits, error codes, SSE events, wire example), `docs/reference/ai.md` (`asks` option, `askResponse` body, full SSE event table, `GptHistory.pendingAsk`), `docs/explanation/agent-ui-asks.md` (new: why client-side tool calls, the round trip, how asks and blocks divide the work), `docs/reference/README.md`, `docs/explanation/README.md`.
|
|
21
|
+
- Acceptance: AC1, AC2, and AC15 for `choice` (select one); AC3, AC4, AC5, AC6, AC7, AC8 (the `chatTurn.ts` move is covered by AC8's unchanged-stream regression); mongoose-schema-safety checklist applied to `GptHistory` (additive, optional, described); ai-prompt-governance checklist applied (constant, mock-model tests with normal, edge, and adversarial inputs); `bun test blocks/ ai/` green.
|
|
22
|
+
|
|
23
|
+
- [x] **Task 1.2**: "Pick one" in `GPTChat`, the example apps, and the demo
|
|
24
|
+
- Delivers: `GPTChatMessage.ask`; `GPTChat` props `onAskSubmit` and `askErrors`; `AskCard` + `AskChoice` (the simple card's buttons for ≤ 3 options with labels ≤ 20, `RadioField` for ≤ 8, searchable `SelectField` above 8); Skip; inline errors; loading while submitting; answered summary; pending ask restored from history stays interactive. example-backend passes `asks: true`; when no AI model is configured (no `GEMINI_API_KEY`, no Vertex, no per-request key), it uses a scripted demo agent instead of the canned demo reply, so every shipped ask can be tried in the example app without an API key (user directive 2026-09-27: "ensure we have an example-frontend that uses this in the gpt chat so we can test it out"). example-frontend `ai.tsx` handles `{ask}` / `{askResolved}` / `done.pendingAsk` and posts `askResponse` with the same streaming reader as a prompt. Demo `AskCard` story. e2e mock streams an ask, accepts the answer, and streams a continuation.
|
|
25
|
+
- Files: `ui/package.json`, `ui/src/asks/AskCard.tsx`, `ui/src/asks/AskChoice.tsx`, `ui/src/asks/askSummary.ts`, `ui/src/GPTChat.tsx`, `ui/src/lazyBoundaries/heavyOptionalExports.tsx`, `ui/src/index.tsx`, `ui/src/asks/*.test.tsx`, `ui/src/GPTChat.test.tsx`; `demo/stories/AskCard.stories.tsx`, `demo/story-config/AskCard.config.tsx`, `demo/demoConfig.tsx`; `example-backend/src/api/ai.ts`; `example-frontend/app/(tabs)/ai.tsx`, `example-frontend/e2e/helpers/mockGpt.ts`, `example-frontend/e2e/ai-chat.spec.ts`.
|
|
26
|
+
- Blocked by: 1.1
|
|
27
|
+
- Docs: `docs/reference/ui.md` (`GPTChat` ask props, `AskCard`), `docs/how-to/agent-ui-asks.md` (new: enable asks on the backend, handle them in the frontend), `docs/explanation/example-coverage.md` (capability row), `docs/how-to/README.md`; regenerate component reference (`cd ui && bun run compile && bun run types`, then `bun run website:generate`).
|
|
28
|
+
- Acceptance: AC9 for `choice`; AC12; `bun run check:demo-coverage` green; screenshots of pending, error, and answered states and a recording of the example-app round trip under `/opt/cursor/artifacts/` (verify-ui-changes).
|
|
29
|
+
|
|
30
|
+
- [x] **Task 1.3**: Small-screen path — simple cards, compact surface, headless endpoints
|
|
31
|
+
- Delivers: `modelRouter` actions on `/gpt/histories`: `collectionActions.pendingAsks` (`GET`, `IsAuthenticated`, caller's histories only) and `instanceActions.turn` (`POST`, `IsOwner`; body one of `{prompt}`, `{askResponse}`, `{toolCallId, buttonId}`, plus `surface`), both driving `chatTurn.ts` with a buffering sink and returning `{text, pendingAsk?}`; `buttonId` resolves to the stored card's `response` (`UNKNOWN_BUTTON` otherwise); the turn finishes and saves after a client disconnect. `surface: "compact"` on `/gpt/prompt` and `turn` narrows the ask tools and adds the compact prompt line (D25). JSON Schema export of the simple card and headless bodies for native clients. `SimpleAskCard` in `@terreno/ui` and a demo story that renders every fixture's card in a 198×242 pt watch-sized frame.
|
|
32
|
+
- Files: `ai/src/routes/gptHistories.ts`, `ai/src/service/chatTurn.ts`, `ai/src/service/asks.ts`, `ai/src/service/prompts.ts`, `ai/src/routes/gptHistories.test.ts`, `ai/src/routes/gpt.test.ts`; `blocks/src/asks/schema.ts` (compact variants), `blocks/src/asks/jsonSchema.ts`, tests; `ui/src/asks/SimpleAskCard.tsx`, `ui/src/asks/SimpleAskCard.test.tsx`, `ui/src/index.tsx`; `ui/src/Common.ts` and `ui/src/Button.tsx` (opt-in `wrapText` prop, off by default, set only by `SimpleAskCard` and `AskChoice`), `ui/src/Button.test.tsx`, `ui/src/asks/AskChoice.tsx`, `ui/src/asks/AskCard.test.tsx`; `demo/stories/SimpleAskCard.stories.tsx`, `demo/story-config/SimpleAskCard.config.tsx`, `demo/stories/Button.stories.tsx`, `demo/story-config/Button.config.tsx`, `demo/demoConfig.tsx`; `example-frontend/store/openApiSdk.ts` (regenerated with `bun run sdk`).
|
|
33
|
+
- Blocked by: 1.2
|
|
34
|
+
- Docs: `docs/reference/agent-ui-asks.md` (simple card contract, rule table, compact mode, headless endpoints, JSON Schema location), `docs/reference/ai.md` (`surface`, the two actions), `docs/how-to/agent-ui-asks.md` ("answer asks from an Apple Watch or another small client": SwiftUI `URLSession` sketch, token hand-off over WatchConnectivity, why the watch always sends `surface: "compact"`), `docs/explanation/agent-ui-asks.md` (why cards carry exact answers; watch paths and their follow-ups), `docs/reference/ui.md` (`SimpleAskCard`, Button `wrapText`); regenerate component reference (`bun run website:generate`).
|
|
35
|
+
- Acceptance: AC16, AC17, AC18; AC15 for `choice` cards made in compact mode; watch-frame screenshot under `/opt/cursor/artifacts/` (verify-ui-changes).
|
|
36
|
+
|
|
37
|
+
### Phase 2: Remaining ask kinds
|
|
38
|
+
|
|
39
|
+
- [x] **Task 2.1**: `choice` many and "Other"
|
|
40
|
+
- Delivers: `select: many`, `minSelected` / `maxSelected`, `allowOther` + `otherLabel`; `MultiselectField` plus an Other `TextField`; `SELECTION_COUNT` and `OTHER_NOT_ALLOWED`; simple-card rules for many-select and for single-select with more options than fit (Use suggested / Skip / handoff).
|
|
41
|
+
- Files: `blocks/src/asks/schema.ts`, `blocks/src/asks/validateResponse.ts`, `blocks/src/asks/simpleCard.ts`, fixtures, tests; `ui/src/asks/AskChoice.tsx`, tests; `demo/stories/AskCard.stories.tsx`. Also touched: `blocks/src/asks/{errors,limits,prompt}.ts`, `blocks/src/index.ts`; `ai/src/service/{asks,prompts}.ts` (full and compact `ask_choice` descriptions); `ui/src/asks/askSummary.ts`; `demo/stories/SimpleAskCard.stories.tsx`, `demo/story-config/AskCard.config.tsx`; `example-backend/src/api/demoAgent.ts` ("pick toppings" scenario); `docs/reference/ui.md`, `docs/how-to/agent-ui-asks.md`, `docs/explanation/{agent-ui-asks,example-coverage}.md`.
|
|
42
|
+
- Blocked by: 1.3
|
|
43
|
+
- Docs: `docs/reference/agent-ui-asks.md` (`choice` fields, card rules).
|
|
44
|
+
- Acceptance: AC1, AC2, AC9, and AC15 for many-select and Other; screenshot.
|
|
45
|
+
|
|
46
|
+
- [x] **Task 2.2**: `confirm`
|
|
47
|
+
- Delivers: `ask_confirm` (`confirmLabel` / `denyLabel` ≤ 20 chars, `destructive`, `allowDecline` default `false`); two `Button`s, `variant="destructive"` when set; simple card Approve / Deny with the D26 destructive marking; offered in compact mode; prompt guidance to confirm before irreversible tool calls.
|
|
48
|
+
- Files: `blocks/src/asks/*`, `ai/src/service/asks.ts`, `ai/src/service/prompts.ts`, `ui/src/asks/AskConfirm.tsx`, tests, story. Also touched: `ui/src/asks/askControls.tsx` (controls shared with `AskChoice`), `ui/src/asks/{AskCard,AskChoice}.tsx`, `ui/src/asks/askSummary.ts`; `demo/stories/{AskCard,SimpleAskCard}.stories.tsx`, `demo/story-config/AskCard.config.tsx`; `example-backend/src/api/demoAgent.ts` ("send the weekly report" and "archive old chats" scenarios); `example-frontend/store/{openApiSdk,sdk}.ts`, `example-frontend/app/(tabs)/ai.tsx`; `docs/reference/ui.md`, `docs/explanation/{agent-ui-asks,example-coverage}.md`.
|
|
49
|
+
- Blocked by: 1.3
|
|
50
|
+
- Docs: `docs/reference/agent-ui-asks.md`, `docs/how-to/agent-ui-asks.md` ("confirm before a destructive tool").
|
|
51
|
+
- Acceptance: AC1, AC2, AC9, and AC15 for `confirm`; AC16 includes `ask_confirm`; prompt snapshot test lists `confirm` only when enabled.
|
|
52
|
+
|
|
53
|
+
- [x] **Task 2.3**: `markdown`
|
|
54
|
+
- Delivers: `ask_markdown` (`initial`, `placeholder`, `minLength`, `maxLength` ≤ 20,000); `MarkdownEditorField`; answer `{markdown, changed}`; long answers collapse in the summary; simple card Approve draft / Cancel with `handoff`.
|
|
55
|
+
- Files: `blocks/src/asks/*`, `ai/src/service/asks.ts`, `ui/src/asks/AskMarkdown.tsx`, tests, story. Also touched: `ai/src/service/prompts.ts` (`ask_markdown` description); `ui/src/asks/{AskCard,SimpleAskCard}.tsx` ("Edit on your phone" handoff line), `ui/src/asks/askSummary.ts`; `demo/stories/{AskCard,SimpleAskCard}.stories.tsx`, `demo/story-config/AskCard.config.tsx`; `example-backend/src/api/demoAgent.ts` ("draft an announcement" scenario); `example-frontend/store/{openApiSdk,sdk}.ts`, `example-frontend/app/(tabs)/ai.tsx`; `docs/reference/ui.md`, `docs/how-to/agent-ui-asks.md`, `docs/explanation/{agent-ui-asks,example-coverage}.md`.
|
|
56
|
+
- Blocked by: 1.3
|
|
57
|
+
- Docs: `docs/reference/agent-ui-asks.md`.
|
|
58
|
+
- Acceptance: AC1, AC2 (`TOO_LONG`), AC9, and AC15 for `markdown`; `changed` is false when the text is unchanged; no Approve button when `initial` breaks the length rules.
|
|
59
|
+
|
|
60
|
+
- [x] **Task 2.4**: `form`
|
|
61
|
+
- Delivers: `ask_form` with 1–8 flat fields (`text`, `textarea`, `email`, `url`, `phone`, `number`, `date`, `time`, `datetime`, `boolean`, `select`, `multiselect`); per-type rules; Luxon ISO validation (revised in Pick: Zod ISO validators); renderer maps each field to `Field` by type; simple card Submit defaults / Cancel with `handoff`.
|
|
62
|
+
- Files: `blocks/src/asks/*`, `ai/src/service/asks.ts`, `ui/src/asks/AskForm.tsx`, tests, story. Also touched: `blocks/src/asks/formValues.ts` (`formDefaultValues`, `formTextMaxLength`), `blocks/src/index.ts`; `ai/src/service/prompts.ts` (`ask_form` description); `ui/src/asks/askFormDraft.ts` (draft, picker, and error-text helpers), `ui/src/asks/{AskCard,SimpleAskCard}.tsx` ("Fill it in on your phone" handoff line), `ui/src/asks/askSummary.ts`; `demo/stories/{AskCard,SimpleAskCard}.stories.tsx`, `demo/story-config/AskCard.config.tsx`; `example-backend/src/api/demoAgent.ts` ("invoice details" scenario), `example-backend/src/__snapshots__/openapi.test.ts.snap`; `example-frontend/store/{openApiSdk,sdk}.ts`, `example-frontend/app/(tabs)/ai.tsx`; `docs/reference/ui.md`, `docs/how-to/agent-ui-asks.md`, `docs/explanation/{agent-ui-asks,example-coverage}.md`.
|
|
63
|
+
- Blocked by: 1.3
|
|
64
|
+
- Docs: `docs/reference/agent-ui-asks.md` (field-type table).
|
|
65
|
+
- Acceptance: AC1, AC2 (`REQUIRED_FIELD`, `FIELD_TYPE_MISMATCH`, `OUT_OF_RANGE`, `INVALID_DATE`), AC9, and AC15 for `form`; one fixture per field type.
|
|
66
|
+
|
|
67
|
+
- [x] **Task 2.5**: `files`
|
|
68
|
+
- Delivers: `ask_files` (`accept`, `minFiles`, `maxFiles`); `FilePickerButton` + `AttachmentPreview` renderer; file refs `{fileId}` or `{url}` per D5; server ref resolution, owner check, byte-level MIME sniffing, size and count caps; `toModelOutput` with `image-data` / `file-data` / text parts; simple card Skip + `handoff`; an example-frontend helper that uploads through `/files/upload` when available and falls back to data URLs.
|
|
69
|
+
- Files: `blocks/src/asks/*`, `ai/src/service/askFiles.ts`, `ai/src/service/asks.ts`, `ai/src/routes/gpt.ts`, tests; `ui/src/asks/AskFiles.tsx`, tests, story; `example-frontend/app/(tabs)/ai.tsx`. Also touched: `blocks/src/asks/files.ts` (byte sniffer, data URL parsing), `blocks/schemas/*`; `ai/src/service/{chatTurn,fileStorage}.ts`, `ai/src/routes/files.ts` (upload `id`), `ai/src/aiApp.ts`, `ai/src/types/index.ts`; `ui/src/asks/askFileRefs.ts`, `ui/src/asks/{AskCard,SimpleAskCard,askSummary}.ts(x)`, `ui/src/FilePickerButton.tsx`, `ui/src/GPTChat.tsx`; `demo/stories/{AskCard,SimpleAskCard}.stories.tsx`, `demo/story-config/AskCard.config.tsx`; `example-backend/src/api/{ai,demoAgent}.ts` ("upload a receipt" scenario), `example-backend/src/__snapshots__/openapi.test.ts.snap`; `example-frontend/lib/gptAsks.ts` (`createAskFilesResolver`), `example-frontend/store/{openApiSdk,sdk}.ts`, `example-frontend/e2e/{ai-chat.spec.ts,helpers/mockGpt.ts}`; `docs/reference/{agent-ui-asks,ai,ui}.md`, `docs/how-to/agent-ui-asks.md`, `docs/explanation/{agent-ui-asks,example-coverage}.md`.
|
|
70
|
+
- Blocked by: 1.3
|
|
71
|
+
- Docs: `docs/reference/agent-ui-asks.md` (`files`, storage modes), `docs/how-to/agent-ui-asks.md` ("accept uploads with or without GCS").
|
|
72
|
+
- Acceptance: AC10; AC1, AC2 (`FILE_TYPE_NOT_ACCEPTED`, `FILE_TOO_LARGE`, `FILE_COUNT`, `FILE_NOT_OWNED`, `MIME_MISMATCH`), AC9, and AC15 for `files`.
|
|
73
|
+
|
|
74
|
+
- [x] **Task 2.6**: Server-enforced approval for host tools
|
|
75
|
+
- Delivers: host tools with AI SDK `needsApproval` pause on `tool-approval-request` as a server-made `confirm` ask (`origin: "approval"`, `toolName`, `approvalId`); `AsksOptions.approvals[toolName]` customizes the prompt and labels, with a default "Allow <toolName>?"; the answer appends `tool-approval-response` and resumes, so the SDK runs or denies the tool; works through `/gpt/prompt` and `turn`; `AIRequest.metadata.ask.origin`. example-backend adds a `deleteCompletedTodos` tool with `needsApproval: true` and a destructive approval prompt.
|
|
76
|
+
- Files: `ai/src/service/asks.ts`, `ai/src/service/chatTurn.ts`, `ai/src/types/index.ts`, `ai/src/routes/gpt.test.ts`, `ai/src/routes/gptHistories.test.ts`; `example-backend/src/api/ai.ts`.
|
|
77
|
+
- Blocked by: 2.2
|
|
78
|
+
- Docs: `docs/reference/agent-ui-asks.md` (approval asks), `docs/reference/ai.md` (`asks.approvals`), `docs/how-to/agent-ui-asks.md` ("require approval before a tool runs").
|
|
79
|
+
- Acceptance: AC19; AC15 for approval cards (destructive approve button per D26).
|
|
80
|
+
|
|
81
|
+
### Phase 3: HTML and display additions
|
|
82
|
+
|
|
83
|
+
Deferred until B1.1 and B2.1 in this file are Roast-passed (D28, revised 2026-10-01).
|
|
84
|
+
|
|
85
|
+
- [x] **Task 3.1**: Sandboxed `html` block
|
|
86
|
+
- Delivers: `html` block schema (`title`, `height: sm|md|lg`, `html` ≤ 100,000 bytes) and `HTML_DISABLED` / `HTML_TOO_LARGE` in `@terreno/blocks`; `uiBlocks.html` server option; `sanitizeHtml` in `@terreno/ai` applied to the final document (re-sent with `{replace: text}` when changed); `HtmlFrame` (web `iframe sandbox=""` + injected CSP meta; native WebView with JavaScript and navigation off); `html` renderer in `BlocksView` with a streaming placeholder and an `allowHtml` gate.
|
|
87
|
+
- Files: `blocks/src/schema.ts`, `blocks/src/errors.ts`, fixtures, tests; `root package.json` (catalog `sanitize-html`), `ai/package.json`, `ai/src/service/sanitizeHtml.ts`, `ai/src/routes/gpt.ts`, tests; `ui/src/HtmlFrame.tsx`, `ui/src/blocks/blockRenderers.tsx`, `ui/src/GPTChat.tsx`, tests; `demo/stories/HtmlFrame.stories.tsx`, `demo/story-config/HtmlFrame.config.tsx`, `demo/demoConfig.tsx`.
|
|
88
|
+
- Blocked by: 1.1, B1.1, B2.1
|
|
89
|
+
- Docs: `docs/reference/blocks.md` (`html` row and `uiBlocks.html`; the page exists once Agent UI Blocks Task 1.1 lands), `docs/explanation/agent-ui-asks.md` (threat model: XSS, phishing, exfiltration, clickjacking), `docs/reference/ui.md` (`HtmlFrame`, `allowHtml`); regenerate component reference (`bun run website:generate`).
|
|
90
|
+
- Acceptance: AC11; `bun run check:licenses` green with the new dependency; screenshot of a sanitized invoice preview on web.
|
|
91
|
+
|
|
92
|
+
- [x] **Task 3.2**: `callout`, `image`, and `details` blocks
|
|
93
|
+
- Delivers: three display blocks rendered with `Banner` (not dismissible), `Image` (`alt` required; sources per D27, including the `uiBlocks.imageHosts` allowlist and `IMAGE_HOST_NOT_ALLOWED`), and `Accordion`; schema, lint, renderer, fixtures.
|
|
94
|
+
- Files: `blocks/src/schema.ts`, `blocks/src/errors.ts`, fixtures, tests; `ai/src/routes/gpt.ts` (`imageHosts` passed to validation), tests; `ui/src/blocks/blockRenderers.tsx`, tests; `demo/stories/BlocksView.stories.tsx`.
|
|
95
|
+
- Blocked by: B1.1, B2.1
|
|
96
|
+
- Docs: `docs/reference/blocks.md` (three rows, `imageHosts`), `docs/explanation/agent-ui-asks.md` (why image hosts are allowlisted).
|
|
97
|
+
- Acceptance: AC20; `BlocksView` renders each block with `@terreno/ui` components only.
|
|
98
|
+
|
|
99
|
+
### Phase 4: Wrap-up
|
|
100
|
+
|
|
101
|
+
- [x] **Task 4.1**: Changelog, rules, docs indexes, final gate
|
|
102
|
+
- Split (2026-09-27, Pick–Roast loop): Phase 3 is blocked on another plan, so this task ships the wrap-up for Phases 1–2 now; Tasks 3.1 and 3.2 add their own changelog, rules, and index lines when they land.
|
|
103
|
+
- Delivers: changelog entry; agent rules updated in their canonical source and regenerated; every new page linked from its README; `.github` and knip config updated for new files.
|
|
104
|
+
- Files: `changelog/unreleased/agent-ui-asks.md`, `.rulesync/rules/ai/00-ai.md`, `.rulesync/rules/ui/00-ui.md`, `docs/how-to/README.md`, `docs/reference/README.md`, `docs/explanation/README.md`, `knip.jsonc`.
|
|
105
|
+
- Blocked by: 1.3, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 3.1, 3.2
|
|
106
|
+
- Docs: as listed; `bun run rules`.
|
|
107
|
+
- Acceptance: AC13, AC14; `bun run website:build` and `bun run rules:check` green.
|
|
108
|
+
|
|
109
|
+
### Phase 5: Body components — whole-reply YAML
|
|
110
|
+
|
|
111
|
+
Folded from [`docs/tasks/agent-ui-blocks.md`](./agent-ui-blocks.md) on 2026-10-01 (D28).
|
|
112
|
+
Ids here are B1.1–B4.2 so they do not collide with asks tasks. Acceptance ids are
|
|
113
|
+
Blocks AC1–AC20 in the asks plan. `@terreno/blocks` already exists; B1.1 extends it.
|
|
114
|
+
Chart components from PR #1302 are on master, so B2.2 is not blocked on that PR.
|
|
115
|
+
Wire format is whole-reply YAML (BD2): no fences required.
|
|
116
|
+
|
|
117
|
+
- [x] **Task B1.1**: Zod schema, parser, structural validation for leaf and layout blocks
|
|
118
|
+
- Delivers: `parseBlocks(text)` (strips an optional surrounding fence, YAML or JSON, `NOT_A_DOCUMENT` when not a mapping with `v`), `wrapAsTextDocument(text)`, and `validateBlocks(doc)` reject unknown keys, wrong enums, and wrong top-level key order (`v` → `datasets` → `blocks`, `KEY_ORDER`) with `{path, code, message, fix}`; `v: 1` required; heading/text/metric/badge/divider/context/columns/card blocks and their enums exist. The package scaffold from the original Task 1.1 already shipped with asks; this task adds the display schema, catalog `yaml`, and fixtures. It does not add chart, table, actions, datasets, or partial parse.
|
|
119
|
+
- Files: `blocks/package.json` (`yaml`), `blocks/src/index.ts`, `blocks/src/schema.ts`, `blocks/src/limits.ts`, `blocks/src/errors.ts`, `blocks/src/parse.ts`, `blocks/src/validate.ts`, `blocks/src/fixtures/valid/*.yaml`, `blocks/src/fixtures/invalid/*.yaml`, `blocks/src/*.test.ts`; root `package.json` (catalog `yaml`).
|
|
120
|
+
- Blocked by: none
|
|
121
|
+
- Docs: `docs/reference/blocks.md` (new: grammar tables, limits, error codes for this slice), `docs/explanation/agent-ui-blocks.md` (new: why a closed catalog, why whole-reply YAML, ownership), `docs/reference/README.md`, `docs/explanation/README.md`.
|
|
122
|
+
- Acceptance: Blocks AC1 for the leaf + layout blocks, including a prose-only `text` document; Blocks AC2 for structural codes (`UNKNOWN_KEY`, `INVALID_ENUM`, `MISSING_REQUIRED`, `UNSUPPORTED_VERSION`, `DEPTH_EXCEEDED`, `TOO_MANY_BLOCKS`, `KEY_ORDER`, `NOT_A_DOCUMENT`); YAML anchors/tags rejected (`YAML_FEATURE_DISALLOWED`); `bun test blocks/` green.
|
|
123
|
+
|
|
124
|
+
- [x] **Task B1.2**: Datasets, chart and table schema, semantic lint
|
|
125
|
+
- Delivers: `datasets` map with `inline` and `ref`; `chart` and `table` blocks; lint for dataset refs, column existence/type, row arity, id uniqueness, BD7 limits, chart heuristics as warnings.
|
|
126
|
+
- Files: `blocks/src/schema.ts`, `blocks/src/lint.ts`, `blocks/src/validate.ts`, `blocks/src/fixtures/**`, `blocks/src/lint.test.ts`, `blocks/src/validate.perf.test.ts`.
|
|
127
|
+
- Blocked by: B1.1
|
|
128
|
+
- Docs: `docs/reference/blocks.md` (dataset sources, chart, table, warning codes).
|
|
129
|
+
- Acceptance: Blocks AC2 semantic codes (`DATASET_NOT_FOUND`, `COLUMN_NOT_FOUND`, `COLUMN_TYPE_MISMATCH`, `ROW_ARITY_MISMATCH`, `DUPLICATE_ID`, `DATASET_TOO_LARGE`, `TABLE_TOO_WIDE`, `TOO_MANY_POINTS`) and warnings (`BAR_TOO_MANY_CATEGORIES`, `DONUT_TOO_MANY_SLICES`, `LINE_SINGLE_POINT`); Blocks AC18 (`ref` half); Blocks AC3; doc-code parity test.
|
|
130
|
+
|
|
131
|
+
- [x] **Task B1.3**: Actions schema, partial parser, JSON Schema, prompt section, CLI
|
|
132
|
+
- Delivers: `actions` with `button`/`segmented` and `reply` / `open` / `select` / `callback`; `UNKNOWN_HOST_ACTION` when an allowlist is supplied; `parseBlocksPartial`; `blocksJsonSchema`; `blocksPromptSection`; `terreno-blocks validate` CLI.
|
|
133
|
+
- Files: `blocks/src/schema.ts`, `blocks/src/parsePartial.ts`, `blocks/src/jsonSchema.ts`, `blocks/src/prompt.ts`, `blocks/src/cli.ts`, `blocks/package.json` (`bin`), `blocks/src/fixtures/partial/*.yaml`, tests.
|
|
134
|
+
- Blocked by: B1.2
|
|
135
|
+
- Docs: `docs/reference/blocks.md` (actions, partial parsing, CLI), `docs/how-to/agent-ui-blocks.md` ("validate a document locally" only).
|
|
136
|
+
- Acceptance: Blocks AC4; Blocks AC20 (`parsePartial` half); `SELECT_TARGET_INVALID`; CLI exit codes 0/1; prompt snapshot equals `BLOCK_LIMITS`.
|
|
137
|
+
|
|
138
|
+
- [x] **Task B2.1**: `BlocksView` for leaf and layout blocks
|
|
139
|
+
- Delivers: `BlocksView` renders heading/text/metric/badge/divider/context/columns/card; invalid → `Banner` + collapsed raw YAML; non-document → `wrapAsTextDocument`; `columns` stacks on `sm`.
|
|
140
|
+
- Files: `ui/package.json`, `ui/src/blocks/BlocksView.tsx`, `ui/src/blocks/blockRenderers.tsx`, `ui/src/blocks/BlocksError.tsx`, `ui/src/Common.ts`, `ui/src/lazyBoundaries/heavyOptionalExports.tsx`, `ui/src/index.tsx`, tests, `demo/stories/BlocksView.stories.tsx`, `demo/story-config/BlocksView.config.tsx`, `demo/demoConfig.tsx`.
|
|
141
|
+
- Blocked by: B1.1
|
|
142
|
+
- Docs: `docs/reference/ui.md`, `docs/explanation/agent-ui-blocks.md`.
|
|
143
|
+
- Acceptance: Blocks AC5 for the covered blocks; Blocks AC6; `bun run check:demo-coverage`; screenshot under `/opt/cursor/artifacts/`.
|
|
144
|
+
|
|
145
|
+
- [x] **Task B2.2**: Chart and table blocks bound to datasets
|
|
146
|
+
- Delivers: `chart` and `table` renderers; `resolveDataset` / `useResolvedDatasets`; no per-point `color` for the agent.
|
|
147
|
+
- Files: `ui/src/blocks/blockRenderers.tsx`, `ui/src/blocks/datasetToPoints.ts`, `ui/src/blocks/useResolvedDatasets.ts`, tests, demo story.
|
|
148
|
+
- Blocked by: B1.2, B2.1
|
|
149
|
+
- Docs: `docs/reference/ui.md`, `docs/how-to/charts-and-dashboards.md` (one paragraph).
|
|
150
|
+
- Acceptance: Blocks AC5 for chart/table; `datasetToPoints` and `useResolvedDatasets` tests; screenshot of bar + donut.
|
|
151
|
+
|
|
152
|
+
- [x] **Task B2.3**: Actions, `onAction`, segmented dataset switch
|
|
153
|
+
- Delivers: `Button` / `SegmentedControl`; `onAction`; local `select`; disabled unknown callbacks; `pendingElementIds`; `overrides`.
|
|
154
|
+
- Files: `ui/src/blocks/BlocksView.tsx`, `ui/src/blocks/useBlockSelections.ts`, `ui/src/Common.ts`, tests, story.
|
|
155
|
+
- Blocked by: B1.3, B2.2
|
|
156
|
+
- Docs: `docs/reference/ui.md`, `docs/explanation/agent-ui-blocks.md`.
|
|
157
|
+
- Acceptance: Blocks AC8; Blocks AC19 (`BlocksView` half).
|
|
158
|
+
|
|
159
|
+
- [x] **Task B2.4**: `GPTChat` `uiBlocks` mode and Blocks Playground
|
|
160
|
+
- Delivers: `uiBlocks` renders assistant messages through `BlocksView` (partial while streaming); `onBlockAction`, `onBlockCallback`, `resolveDataset`; playground story.
|
|
161
|
+
- Files: `ui/src/GPTChat.tsx`, `ui/src/GPTChat.test.tsx`, `ui/src/Common.ts`, `demo/stories/BlocksPlayground.stories.tsx`, `demo/story-config/BlocksPlayground.config.tsx`, `demo/demoConfig.tsx`.
|
|
162
|
+
- Blocked by: B2.3
|
|
163
|
+
- Docs: `docs/reference/ui.md`, `docs/how-to/agent-ui-blocks.md`.
|
|
164
|
+
- Acceptance: Blocks AC7; Blocks AC19 (`GPTChat` half); Blocks AC20 (`GPTChat` half); `uiBlocks` off regression; playground recording.
|
|
165
|
+
|
|
166
|
+
- [x] **Task B3.1**: `uiBlocks` route option, prompt, post-stream validation, repair
|
|
167
|
+
- Delivers: `addGptRoutes` `uiBlocks` option; final-step document; SSE `{blocks}` before `{done}`; optional one repair pass; errors stored for the next turn.
|
|
168
|
+
- Files: `ai/package.json`, `ai/src/service/prompts.ts`, `ai/src/routes/gpt.ts`, `ai/src/types/index.ts`, `ai/src/routes/gpt.test.ts`.
|
|
169
|
+
- Blocked by: B1.3
|
|
170
|
+
- Docs: `docs/reference/ai.md`, `docs/how-to/agent-ui-blocks.md`.
|
|
171
|
+
- Acceptance: Blocks AC9; Blocks AC18 (`UNKNOWN_HOST_ACTION` half); prompt constant at top of `prompts.ts`; no change when `uiBlocks` is off.
|
|
172
|
+
|
|
173
|
+
- [x] **Task B3.4**: `AIDataset`, `registerAiDataset`, `GET /gpt/datasets/:id`
|
|
174
|
+
- Delivers: model, TTL, row cap, grain bucketing, LTTB, pagination, `IsOwner`.
|
|
175
|
+
- Files: `ai/src/models/aiDataset.ts`, `ai/src/service/aiDatasets.ts`, `ai/src/routes/gptDatasets.ts`, `ai/src/routes/gpt.ts`, tests.
|
|
176
|
+
- Blocked by: B1.2
|
|
177
|
+
- Docs: `docs/reference/ai.md`, `docs/explanation/agent-ui-blocks.md`, `docs/how-to/agent-ui-blocks.md`.
|
|
178
|
+
- Acceptance: Blocks AC16; mongoose-schema-safety on the new model.
|
|
179
|
+
|
|
180
|
+
- [x] **Task B3.5**: `POST /gpt/actions`
|
|
181
|
+
- Delivers: host callback route, payload validation, 10 s timeout, `ui_action` log.
|
|
182
|
+
- Files: `ai/src/routes/gptActions.ts`, `ai/src/routes/gpt.ts`, `ai/src/types/index.ts`, `ai/src/routes/gptActions.test.ts`, `ai/src/models/aiRequest.ts`.
|
|
183
|
+
- Blocked by: B3.1
|
|
184
|
+
- Docs: `docs/reference/ai.md`, `docs/how-to/agent-ui-blocks.md`, `docs/explanation/agent-ui-blocks.md`.
|
|
185
|
+
- Acceptance: Blocks AC17.
|
|
186
|
+
|
|
187
|
+
- [x] **Task B3.2**: `AIService.generateBlocks`
|
|
188
|
+
- Delivers: object output, `validateBlocks`, one repair retry, `requestType: "ui_blocks"`.
|
|
189
|
+
- Files: `ai/src/service/aiService.ts`, `ai/src/types/index.ts`, `ai/src/service/aiService.test.ts`.
|
|
190
|
+
- Blocked by: B3.1
|
|
191
|
+
- Docs: `docs/reference/ai.md`.
|
|
192
|
+
- Acceptance: Blocks AC10.
|
|
193
|
+
|
|
194
|
+
- [x] **Task B3.3**: MCP validator tool
|
|
195
|
+
- Delivers: `terreno_validate_ui_blocks`.
|
|
196
|
+
- Files: `mcp-server/package.json`, `mcp-server/src/tools.ts`, `mcp-server/src/tools.test.ts`.
|
|
197
|
+
- Blocked by: B1.3
|
|
198
|
+
- Docs: `docs/reference/mcp-server.md`, `.rulesync/rules/mcp-server/00-mcp-server.md`.
|
|
199
|
+
- Acceptance: Blocks AC11.
|
|
200
|
+
|
|
201
|
+
- [x] **Task B4.1**: example-frontend renders blocks and handles actions
|
|
202
|
+
- Delivers: AI tab `uiBlocks`, dataset and action wiring, e2e mock document.
|
|
203
|
+
- Files: `example-frontend/app/(tabs)/ai.tsx`, `example-frontend/store/openApiSdk.ts`, `example-frontend/e2e/helpers/mockGpt.ts`, `example-frontend/e2e/ai-chat.spec.ts`.
|
|
204
|
+
- Blocked by: B2.4, B3.1, B3.4, B3.5
|
|
205
|
+
- Docs: `docs/how-to/agent-ui-blocks.md`, `docs/explanation/example-coverage.md`.
|
|
206
|
+
- Acceptance: Blocks AC13; recording under `/opt/cursor/artifacts/`.
|
|
207
|
+
|
|
208
|
+
- [x] **Task B4.2**: example-backend enable, changelog, docs sweep
|
|
209
|
+
- Delivers: `uiBlocks` host action and `todoStats` dataset tool; changelog; indexes; rules.
|
|
210
|
+
- Files: `example-backend/src/server.ts`, `example-backend/src/ai/hostActions.ts`, `example-backend/src/ai/tools.ts`, `changelog/unreleased/agent-ui-blocks.md`, docs READMEs, `.rulesync/rules/ui/00-ui.md`.
|
|
211
|
+
- Blocked by: B4.1, B3.2, B3.3
|
|
212
|
+
- Docs: as listed.
|
|
213
|
+
- Acceptance: Blocks AC14, Blocks AC15.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Task List: Agent UI Blocks
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Folded into [`docs/tasks/agent-ui-asks.md`](./agent-ui-asks.md) Phase 5 on 2026-10-01. Do not Pick from this file. Task 1.1 here is B1.1 there. Wire format is whole-reply YAML (D2 = b).
|
|
4
4
|
**Supporting skills:** `terreno-ui`, `ai-prompt-governance`, `update-docs`, `verify-ui-changes`, `backend-test-env`, `terreno-backend-api`.
|
|
5
5
|
|
|
6
6
|
Every task is a vertical slice: contract + renderer or producer + docs + Bun tests. Work
|
|
7
7
|
the frontier (blockers complete). Phase 2 chart/table work is additionally blocked on
|
|
8
8
|
[PR #1302](https://github.com/TerrenoLabs/terreno/pull/1302) merging to `master`.
|
|
9
|
+
Agent UI Asks Tasks 3.1 and 3.2 (`html`, `callout`, `image`, `details` blocks; [`agent-ui-asks.md`](./agent-ui-asks.md)) wait on Tasks 1.1 and 2.1 here.
|
|
9
10
|
|
|
10
11
|
### Phase 1: Contract — `@terreno/blocks`
|
|
11
12
|
|