@kitn.ai/ui 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/{Icon-BnFKqqtY.js → Icon-DObhsRtm.js} +1 -1
- package/dist/{action-icons-CBskfBMz.js → action-icons-CcgNN2oT.js} +1 -1
- package/dist/arrow-down-CjRU3JgV.js +1 -0
- package/dist/arrow-left-Bnpb1UEW.js +1 -0
- package/dist/{artifact-CvXEm1lS.js → artifact-Bwt7OgcR.js} +1 -1
- package/dist/{attachments-C43ZegEq.js → attachments-Qr4kg-2b.js} +1 -1
- package/dist/{audio-visualizer-B-79y1Tj.js → audio-visualizer-DToYdfSc.js} +1 -1
- package/dist/{badge-BpUbq8c0.js → badge-CdEu_jn2.js} +1 -1
- package/dist/{bell-sn17Ip9v.js → bell-DQAAOpDz.js} +1 -1
- package/dist/{button-BH6rN0U-.js → button-DkuOnGLy.js} +1 -1
- package/dist/{card-renderer-Cb1gQmth.js → card-renderer-Jx70m6_l.js} +1 -1
- package/dist/{card-routing-DifQgS7n.js → card-routing-0YidER_3.js} +1 -1
- package/dist/check-njkVdZu2.js +1 -0
- package/dist/{checkbox-Dc9Yov_B.js → checkbox-D7iRB3nL.js} +1 -1
- package/dist/{checkbox-group-B1kDLLYk.js → checkbox-group-B2_rpoPM.js} +1 -1
- package/dist/chevron-down-B72s6PXR.js +1 -0
- package/dist/chevron-right-BatuHzUm.js +1 -0
- package/dist/{choice-card-BT3c1S8g.js → choice-card-m0BYqqOK.js} +1 -1
- package/dist/circle-check-B62pODB3.js +1 -0
- package/dist/{circle-x-wobWkoSx.js → circle-x-BPG0iPst.js} +1 -1
- package/dist/circle-zx0ByzY9.js +1 -0
- package/dist/code-block-ClMLKb7C.js +1 -0
- package/dist/{collapsible-CPJbf6FM.js → collapsible-B4DlWicL.js} +1 -1
- package/dist/components/chat-thread.d.ts +89 -1
- package/dist/components/conversation-item.d.ts +16 -0
- package/dist/components/conversation-panel.d.ts +29 -0
- package/dist/components/home-panel.d.ts +34 -0
- package/dist/components/widget-tab-bar.d.ts +16 -0
- package/dist/{composer-CjngLzal.js → composer-sQ0HcvyG.js} +3 -3
- package/dist/{confirm-card-DCx_A4bU.js → confirm-card-rQWDQsnC.js} +1 -1
- package/dist/construct-cli.es.js +182 -14
- package/dist/{context-BY8V-Nlp.js → context-0kg4ZX1J.js} +1 -1
- package/dist/controllable-CnGpMF6r.js +1 -0
- package/dist/conversation-item-BxwQLaFJ.js +1 -0
- package/dist/conversation-list-BCfGN1JU.js +1 -0
- package/dist/{copy-B6DO4PqB.js → copy-n9u9rqpi.js} +1 -1
- package/dist/{create-tween-CnMHviKB.js → create-tween-C-q-JCM-.js} +1 -1
- package/dist/{create-tween-D1aiGZZh.js → create-tween-CRrJXxBd.js} +1 -1
- package/dist/{create-tween-BPzaefTp.js → create-tween-DZ3RRDHD.js} +1 -1
- package/dist/{create-tween-DUSQUKpr.js → create-tween-uPkArjIG.js} +1 -1
- package/dist/custom-elements.json +51 -2
- package/dist/{default-input-CucQEOqH.js → default-input-CaMrJkgH.js} +1 -1
- package/dist/define-By-uL588.js +1 -0
- package/dist/define.js +2 -2
- package/dist/define.server.js +2 -2
- package/dist/{disclosure-CbLtajUQ.js → disclosure-WUImBbDv.js} +1 -1
- package/dist/{dropdown-BwhViJFd.js → dropdown-wZVeJOMd.js} +1 -1
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkbox-group.js +1 -1
- package/dist/elements/checkbox.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-item.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.js +3 -3
- package/dist/elements/dropdown.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-grid.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/radio-group.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/select.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slider.js +1 -1
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +20 -4
- package/dist/{ellipsis-DFr4Vqr3.js → ellipsis-CbOpJnU3.js} +1 -1
- package/dist/{embed-5s_vU4J0.js → embed-CrfjkbUG.js} +1 -1
- package/dist/{external-link-u9d85ORQ.js → external-link-ByXAwBBQ.js} +1 -1
- package/dist/{file-text-CX7_x42o.js → file-text-DMib2qwt.js} +1 -1
- package/dist/{file-tree-D3kBjXI5.js → file-tree-CJqM2aP7.js} +1 -1
- package/dist/{folder-V-YdIL8f.js → folder-_BAb929C.js} +1 -1
- package/dist/{form-BRNUMCN2.js → form-DSZgZQZk.js} +1 -1
- package/dist/{hover-card-DaeiYrTA.js → hover-card-BQbul9eq.js} +1 -1
- package/dist/{icon-BpixdZNq.js → icon-CdcfRhYI.js} +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2842 -2703
- package/dist/index.server.js +2820 -2681
- package/dist/{info-8mFsIjS3.js → info-DsFjGuI_.js} +1 -1
- package/dist/{input-d1Yuu-hz.js → input-DKbmkMos.js} +1 -1
- package/dist/kai.es.js +1 -1
- package/dist/{kbd-961zMPFB.js → kbd-PvdGhX4s.js} +1 -1
- package/dist/{link-preview-DIIbV2Sd.js → link-preview-DbM1866N.js} +1 -1
- package/dist/{link-DlZZBGaR.js → link-zF8PzWfF.js} +1 -1
- package/dist/{loader-YQszHFaW.js → loader-BM4CEz4G.js} +1 -1
- package/dist/{markdown-CVk1MPsj.js → markdown-LMvmAyBk.js} +7 -7
- package/dist/mcp.es.js +78 -4
- package/dist/{message-CwKVEdZg.js → message-BSsgvHvf.js} +1 -1
- package/dist/{message-DS8lzHc1.js → message-BYZECyek.js} +1 -1
- package/dist/message-circle-DruwObfX.js +1 -0
- package/dist/message-square-BG6q6mlm.js +1 -0
- package/dist/{minimize-2-UEZmP2QT.js → minimize-2-BQRoBV1V.js} +1 -1
- package/dist/{model-switcher-Kk1jEQ5t.js → model-switcher-CMpCOYcB.js} +1 -1
- package/dist/{overlay-C1od6LOz.js → overlay-Cb-MkyDa.js} +1 -1
- package/dist/{panel-right-ryET0yyn.js → panel-right-i8warFxT.js} +1 -1
- package/dist/{paperclip-BDNoZFJP.js → paperclip-ByY3UVO6.js} +1 -1
- package/dist/play-BvKKq3fX.js +1 -0
- package/dist/primitives/conversation-store.d.ts +54 -0
- package/dist/{progress-bar-BvYX0aoy.js → progress-bar-jVAHvMUf.js} +1 -1
- package/dist/{prompt-suggestion-BRFu2lcu.js → prompt-suggestion-CD-qtNgC.js} +1 -1
- package/dist/{radio-DcL23cNJ.js → radio-BsoBMhkF.js} +1 -1
- package/dist/react/index.d.ts +393 -0
- package/dist/react.js +20 -20
- package/dist/{reasoning-Cackq4Rh.js → reasoning-4kBPtlTx.js} +1 -1
- package/dist/register-impl-B1hLkrI8.js +424 -0
- package/dist/{resizable-Cp9HUYih.js → resizable-uPmY9VY-.js} +1 -1
- package/dist/{rotate-ccw-0DLRDHH5.js → rotate-ccw-B2xn93py.js} +1 -1
- package/dist/{rotate-cw-DOB9IGcU.js → rotate-cw-CMVLoCyH.js} +1 -1
- package/dist/{scroll-area-emVmWP4K.js → scroll-area-BqLUo8ZZ.js} +1 -1
- package/dist/{scroll-button-CeP_gv1x.js → scroll-button-DslXiJyL.js} +1 -1
- package/dist/{select-CWmu00tS.js → select-2cLRtPGT.js} +1 -1
- package/dist/{separator-CE6vly6z.js → separator-BWQtNJcM.js} +1 -1
- package/dist/{settings-CE-78SvZ.js → settings-BBR63jLg.js} +1 -1
- package/dist/{settings-group-ZFWNxFQR.js → settings-group-B2ciy-Iw.js} +1 -1
- package/dist/{skeleton-BebdP4mf.js → skeleton-CkHI-HgC.js} +1 -1
- package/dist/{slider-BAKRYUlz.js → slider-DDR9U-i0.js} +1 -1
- package/dist/{solid-BE5ui6sy.js → solid-BzCFdL25.js} +7720 -7058
- package/dist/{solid-DFhsce3O.js → solid-DruVOXnf.js} +5864 -5283
- package/dist/solid.d.ts +6 -2
- package/dist/solid.js +217 -210
- package/dist/solid.server.js +217 -210
- package/dist/{source-B0FlguSx.js → source-GEnFF528.js} +1 -1
- package/dist/{star-CBMZvLuN.js → star-CilHq8GP.js} +1 -1
- package/dist/store-Ciyk6eZG.js +1 -0
- package/dist/{switch-CViARZCy.js → switch-KkeQIbuH.js} +1 -1
- package/dist/{tasks-card-MjCcOhR_.js → tasks-card-BrN4bggJ.js} +1 -1
- package/dist/{text-shimmer-DKlwRF2G.js → text-shimmer-Dcj1MHuq.js} +1 -1
- package/dist/{textarea-3ay2Me2-.js → textarea-Dpvbngbg.js} +1 -1
- package/dist/theme.tokens.css +28 -0
- package/dist/{thumbs-up-DQcu2HMO.js → thumbs-up-CsT9Wzb9.js} +1 -1
- package/dist/{toast-store-C6yKOhbk.js → toast-store-tgK6MuMv.js} +1 -1
- package/dist/{tool-BCYNlfKy.js → tool-DzUjIBT8.js} +1 -1
- package/dist/{tooltip-05zI4hjs.js → tooltip-Bkkg_4t0.js} +1 -1
- package/dist/{trash-2-BR1RxGky.js → trash-2-jBKxjGGQ.js} +1 -1
- package/dist/{triangle-alert-DHohDOCy.js → triangle-alert-DlQ68ppf.js} +1 -1
- package/dist/types.d.ts +48 -3
- package/dist/ui/dock.d.ts +29 -0
- package/dist/{upload-BbGW6A_u.js → upload-TcAHXv9W.js} +1 -1
- package/dist/{url-scheme-policy-DHpoTJwB.js → url-scheme-policy-DR5DgvkM.js} +1 -1
- package/dist/{use-card-resolution-CJW4grFa.js → use-card-resolution-DaZqAdWf.js} +1 -1
- package/dist/{variant-aurora-xiMPty8f.js → variant-aurora-BhLuYxJR.js} +1 -1
- package/dist/{variant-aurora-BUOVgPOB.js → variant-aurora-CNmfHhcx.js} +2 -2
- package/dist/{variant-aurora-CUsUJvFI.js → variant-aurora-D6GsdTPA.js} +2 -2
- package/dist/{variant-aurora-CW2i4bfr.js → variant-aurora-DbW_OWL2.js} +1 -1
- package/dist/{variant-custom-D91HjAKz.js → variant-custom-BRvaNHSi.js} +1 -1
- package/dist/{variant-custom-BQtOnmeO.js → variant-custom-BvYHR62b.js} +1 -1
- package/dist/{variant-custom-CAnzzppW.js → variant-custom-DMTrEO5w.js} +2 -2
- package/dist/{variant-custom-CAm9L-E5.js → variant-custom-DV2x254s.js} +2 -2
- package/dist/{variant-wave-Ca1Ls4_y.js → variant-wave-BNMGrDfj.js} +2 -2
- package/dist/{variant-wave-uY5crU_K.js → variant-wave-BpDd8t3Z.js} +1 -1
- package/dist/{variant-wave-CxX39DKd.js → variant-wave-CyBueXj9.js} +2 -2
- package/dist/{variant-wave-DYyzIIZv.js → variant-wave-sp-qwsdI.js} +1 -1
- package/dist/x-Z5UntkIJ.js +1 -0
- package/frameworks/react/index.tsx +14 -4
- package/llms-full.txt +7 -2
- package/package.json +1 -1
- package/src/agent-tooling/README.md +192 -0
- package/src/agent-tooling/catalog/invariants.ts +2 -2
- package/src/agent-tooling/construct/cli.ts +16 -0
- package/src/agent-tooling/construct/codegen.ts +309 -27
- package/src/agent-tooling/construct/construct.v1.schema.json +69 -0
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +11 -2
- package/src/agent-tooling/construct/schema.ts +81 -0
- package/src/components/chat-thread.tsx +532 -36
- package/src/components/conversation-item.tsx +23 -0
- package/src/components/conversation-panel.tsx +118 -0
- package/src/components/home-panel.tsx +159 -0
- package/src/components/widget-tab-bar.tsx +73 -0
- package/src/elements/chat.tsx +94 -2
- package/src/elements/compiled.css +1 -1
- package/src/elements/element-meta.json +49 -6
- package/src/elements/element-nonscalar.json +2 -0
- package/src/elements/element-types.d.ts +20 -4
- package/src/index.ts +3 -1
- package/src/primitives/conversation-store.ts +248 -0
- package/src/solid.ts +10 -2
- package/src/types.ts +45 -3
- package/src/ui/dock.tsx +47 -1
- package/theme.css +28 -0
- package/dist/arrow-down-Br0qtpsz.js +0 -1
- package/dist/arrow-left-Dj7xgE12.js +0 -1
- package/dist/check-DPeeMUYx.js +0 -1
- package/dist/chevron-down-CHSWk7ZT.js +0 -1
- package/dist/chevron-right-B7l_fMgx.js +0 -1
- package/dist/circle-Bb0yOpqM.js +0 -1
- package/dist/circle-check-BGZpCiz7.js +0 -1
- package/dist/code-block-CUQ5mtv0.js +0 -1
- package/dist/controllable-D43BKmtX.js +0 -1
- package/dist/conversation-list-D4lLU_yS.js +0 -1
- package/dist/define-CGGX-7Vg.js +0 -1
- package/dist/message-circle-Xf8Q-vZp.js +0 -1
- package/dist/message-square-Cxnwgkns.js +0 -1
- package/dist/play-OFIvYXUQ.js +0 -1
- package/dist/register-impl-C7rENvPK.js +0 -424
- package/dist/store-7eoadydQ.js +0 -1
- package/dist/x-C5m1JFgi.js +0 -1
package/llms-full.txt
CHANGED
|
@@ -1520,7 +1520,7 @@ _No events._
|
|
|
1520
1520
|
| `cards` | — | `undefined \| { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } \| { kind: "dismissed"; at?: undefined \| string } \| { kind: "expired"; reason?: undefined \| string; at?: undefined \| string } }[]` | The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. |
|
|
1521
1521
|
| `types` | — | `undefined \| Record<string, string>` | Optional type→tag overrides/additions (merged over the built-ins). Property: `el.types`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
|
|
1522
1522
|
| `schemas` | — | `undefined \| Record<string, object>` | JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `types`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.schemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name, matching `mergeCardTags`, where your entry is spread over ours. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. See `CardSchemaMap` in components/card-renderer.tsx. |
|
|
1523
|
-
| `policy` | — | `undefined \| { onSubmit?: undefined \| ((cardId: string, data: unknown) => void); onAction?: undefined \| ((cardId: string, action: string, payload?: unknown) => void); onSendPrompt?: undefined \| ((text: string, opts: { mode: "compose" \| "send"; context?: unknown
|
|
1523
|
+
| `policy` | — | `undefined \| { onSubmit?: undefined \| ((cardId: string, data: unknown) => void); onAction?: undefined \| ((cardId: string, action: string, payload?: unknown) => void); onSendPrompt?: undefined \| ((text: string, opts: { mode: "compose" \| "send"; context?: unknown }) => void); onOpen?: undefined \| ((url: string, target: "tab" \| "artifact") => void); onState?: undefined \| ((cardId: string, patch: unknown) => void); onDismiss?: undefined \| ((cardId: string) => void); onReopen?: undefined \| ((cardId: string) => void); onError?: undefined \| ((cardId: string, message: string) => void); maxSendPromptMode?: undefined \| "compose" \| "send" }` | Optional CardPolicy handling child events. Property: `el.policy`. |
|
|
1524
1524
|
| `validateCards` | `validate-cards` | `undefined \| false \| true` | Validate each envelope's `data` against the schema for its type before rendering it, using a built-in's own schema or yours from `schemas`. Default `true`; set `validate-cards="false"` (or `el.validateCards = false`) to opt out. A hard failure (wrong type, a missing required field) renders a diagnostic naming the field instead of the card; a soft failure (bounds) renders the card unchanged. Both emit a contract `error` event. On in production too: a model emitting a bad shape is a production failure mode, so stripping the check there would hide it from exactly the person who needs to see it. |
|
|
1525
1525
|
|
|
1526
1526
|
**Events** (non-bubbling `CustomEvent`s — listen directly on the element):
|
|
@@ -1614,6 +1614,9 @@ _No events._
|
|
|
1614
1614
|
| `messages` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } \| { kind: "dismissed"; at?: undefined \| string } \| { kind: "expired"; reason?: undefined \| string; at?: undefined \| string } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string }; feedback?: undefined \| "like" \| "dislike" }[]` | The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). Omit for an empty thread. Re-declared here (rather than inherited from `ChatThreadProps`) because the ELEMENT registers a `[]` default and renders the empty state without it, while the SolidJS `<ChatThread>` component still requires it. The facade hands it a validated array either way. Matches `<kai-thread>`. |
|
|
1615
1615
|
| `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
|
|
1616
1616
|
| `cardSchemas` | — | `undefined \| Record<string, object>` | JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `cardTypes`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. |
|
|
1617
|
+
| `conversations` | `conversations` | `undefined \| false \| true` | Turns on the prior-conversations list (a list-toggle button in the header, plus a second list view sharing the panel, C-1). Attribute- settable like every other boolean flag on this element: `<kai-chat conversations>`. Requires `store`. A row select, "new conversation," or the visitor's mount-time auto-restore all deliver their messages the same way: listen for `kai-conversation-load` and set `el.messages` from `event.detail.messages` (a fresh array): this element does not update `messages` for you. Set with no `store`, the underlying `ChatThread` decides loudly (one console.error) and stays visually off; this facade always supplies its own internal load handler (the `kai-conversation-load` dispatch below), so the second ChatThread guard, missing `onConversationLoad`, never trips here, even for a consumer who never listens for the event. Default false. |
|
|
1618
|
+
| `store` | — | `undefined \| { list: () => Promise<{ id: string; title: string; groupId?: undefined \| string; scope?: undefined \| { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt?: undefined \| string; updatedAt: string; trailing?: undefined \| string; lastReadAt?: undefined \| string }[]>; load: (id: string) => Promise<{ id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } \| { kind: "dismissed"; at?: undefined \| string } \| { kind: "expired"; reason?: undefined \| string; at?: undefined \| string } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string }; feedback?: undefined \| "like" \| "dislike" }[]>; save: (id: string, messages: { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } \| { kind: "dismissed"; at?: undefined \| string } \| { kind: "expired"; reason?: undefined \| string; at?: undefined \| string } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string }; feedback?: undefined \| "like" \| "dislike" }[]) => Promise<void>; markRead?: undefined \| ((id: string) => Promise<void>) }` | The adapter this thread persists conversations through: an object of three functions (`list`/`load`/`save`; `ConversationStore`, exported from `@kitn.ai/ui`'s `primitives/conversation-store`). A JS PROPERTY ONLY: `el.store = myAdapter`. It can never be an attribute, since a function-bearing object has no HTML string form, the same reasoning that keeps `messages`/`cardSchemas` property-only (the kai- contract: array/object props are JS properties, never attributes). Two built-ins ship: `localStorageStore(name, userId?)` and `fetchStore(url, userId?)`. |
|
|
1619
|
+
| `home` | — | `undefined \| { greeting?: undefined \| { title?: undefined \| string; subtitle?: undefined \| string }; recentConversation?: undefined \| false \| true; newConversation?: undefined \| { label?: undefined \| string }; links?: undefined \| { label: string; href?: undefined \| string; description?: undefined \| string; icon?: undefined \| string }[] }` | Turns on the widget home screen (Intercom-pattern): the panel boots into a `home` view, with a greeting, most-recent-conversation card, a "new conversation" CTA, and host-defined links, plus a Home/Messages tab bar for switching back to the thread. An OBJECT, so it is a JS property only: `el.home = { greeting: { title: 'Hey' }, links: [...] }`, never an attribute (the kai- contract: array/object props are JS properties). A `links` entry with no `href` fires `kai-home-link` with that entry when tapped, rather than navigating; one WITH `href` opens it directly (only when the URL passes the kit's own scheme allowlist). Omit for the no-home widget (chat view only, unchanged). |
|
|
1617
1620
|
|
|
1618
1621
|
**Events** (non-bubbling `CustomEvent`s — listen directly on the element):
|
|
1619
1622
|
|
|
@@ -1621,6 +1624,8 @@ _No events._
|
|
|
1621
1624
|
|---|---|---|
|
|
1622
1625
|
| `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time. |
|
|
1623
1626
|
| `kai-attachments-rejected` | `CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" \| "unsupported" }[] }>` | One or more picked files were refused because `accept` excluded them. The element renders NO message of its own: it reports the facts (name, media type, whether the kit could have sent it) and what the user should see is the application's call. Only ever fires when `accept` is set. |
|
|
1627
|
+
| `kai-conversation-load` | `CustomEvent<{ id: string \| undefined; messages: { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } \| { kind: "dismissed"; at?: undefined \| string } \| { kind: "expired"; reason?: undefined \| string; at?: undefined \| string } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string }; feedback?: undefined \| "like" \| "dislike" }[] }>` | A conversation's history loaded: a row tap in the list, "new conversation," or the visitor's own mount-time auto-restore of their most recent thread (only fires when `conversations` is on and a `store` is set). `detail.id` is that conversation's id, `undefined` for the "new conversation" case (no id exists until the first message mints one, C-6). Set `el.messages = event.detail.messages` (already a fresh array) to actually render it, since this element does not do that for you; `messages` stays your own state like everywhere else on this element. |
|
|
1628
|
+
| `kai-home-link` | `CustomEvent<{ entry: { label: string; href?: undefined \| string; description?: undefined \| string; icon?: undefined \| string } }>` | A `home.links` entry with no `href` was activated (tapped/clicked/Enter). Meaningful only when `home` is set. |
|
|
1624
1629
|
| `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | An action button on a message was clicked. `action` is the built-in name or custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. |
|
|
1625
1630
|
| `kai-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. |
|
|
1626
1631
|
| `kai-submit` | `CustomEvent<{ value: string; attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | User submitted a message. |
|
|
@@ -2055,7 +2060,7 @@ _No events._
|
|
|
2055
2060
|
|---|---|---|---|
|
|
2056
2061
|
| `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
|
|
2057
2062
|
| `groups` | — | `undefined \| { id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | The list's section headers (`{ id, name, sortOrder, createdAt }`), rendered in array order. A group carries no conversations of its own; it is matched against `conversations` by id, so the two props are complementary rather than alternatives. Omit for an ungrouped list. Set as a JS property. |
|
|
2058
|
-
| `conversations` | — | `undefined \| { id: string; title: string; groupId?: undefined \| string; scope?: undefined \| { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt?: undefined \| string; updatedAt: string; trailing?: undefined \| string }[]` | Every conversation the list renders, flat. Each one is filed under the group whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId` matching no entry in `groups`, falls into a trailing "Ungrouped" section, so nothing you pass in is ever dropped. There is no recency bucketing. Set as a JS property. Omit to supply them as `<kai-conversation>` light-DOM children instead, or for the empty state. A search query that matches nothing shows a visible "No conversations match your search" state, distinct from the zero-conversations empty state. Slotted `<kai-conversation-item>` children switch the list into item mode instead: your own rows win and this array is not rendered. |
|
|
2063
|
+
| `conversations` | — | `undefined \| { id: string; title: string; groupId?: undefined \| string; scope?: undefined \| { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt?: undefined \| string; updatedAt: string; trailing?: undefined \| string; lastReadAt?: undefined \| string }[]` | Every conversation the list renders, flat. Each one is filed under the group whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId` matching no entry in `groups`, falls into a trailing "Ungrouped" section, so nothing you pass in is ever dropped. There is no recency bucketing. Set as a JS property. Omit to supply them as `<kai-conversation>` light-DOM children instead, or for the empty state. A search query that matches nothing shows a visible "No conversations match your search" state, distinct from the zero-conversations empty state. Slotted `<kai-conversation-item>` children switch the list into item mode instead: your own rows win and this array is not rendered. |
|
|
2059
2064
|
| `activeId` | `active-id` | `undefined \| string` | The id of the currently-open conversation, highlighted in the list. |
|
|
2060
2065
|
| `collapsed` | `collapsed` | `undefined \| false \| true` | Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to drive the rail from your app, updating it in response to `kai-collapse-toggle`. Omit for uncontrolled (the element manages it). Collapsed shrinks the rail to a floating reopen button. |
|
|
2061
2066
|
| `defaultCollapsed` | `default-collapsed` | `undefined \| false \| true` | Initial collapsed state when uncontrolled (default false). Use the `default-collapsed` attribute to start collapsed in plain HTML. |
|
package/package.json
CHANGED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# `agent-tooling/`
|
|
2
|
+
|
|
3
|
+
The kit's machine-facing front door: everything that makes AI coding agents
|
|
4
|
+
and CLIs fluent building with `@kitn.ai/ui`. It is packaging and tooling on
|
|
5
|
+
top of the library, not part of it — `grep -rn "agent-tooling"
|
|
6
|
+
packages/ui/src/components packages/ui/src/elements` returns no imports (only
|
|
7
|
+
a code comment and a test's `UNSCANNED_DIRS` entry mention the directory by
|
|
8
|
+
name). Nothing a consumer's chat UI renders depends on anything in here.
|
|
9
|
+
|
|
10
|
+
## The four parts
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────────────┐
|
|
14
|
+
│ kai MCP server (mcp/) │
|
|
15
|
+
│ construct · scaffold · │
|
|
16
|
+
│ reference · theme · │
|
|
17
|
+
│ debug │
|
|
18
|
+
└───────┬─────────┬────────┘
|
|
19
|
+
consumes consumes
|
|
20
|
+
│ │
|
|
21
|
+
▼ ▼
|
|
22
|
+
┌────────────────┐ ┌──────────────────┐
|
|
23
|
+
│ construct/ │ │ catalog/ │
|
|
24
|
+
│ (schema→codegen │ │ (invariants, │
|
|
25
|
+
│ →cli engine) │ │ surfaces, │
|
|
26
|
+
└────────▲────────┘ │ fabrications, …) │
|
|
27
|
+
│ └───────────────────┘
|
|
28
|
+
standalone,
|
|
29
|
+
no MCP involved
|
|
30
|
+
│
|
|
31
|
+
┌────────┴────────┐
|
|
32
|
+
│ bin/mcp.js CLI │
|
|
33
|
+
│ validate/dev/ │
|
|
34
|
+
│ compile/eject │
|
|
35
|
+
└──────────────────┘
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- **`mcp/`** — the `kai` MCP server. The tools in `mcp/tools/`:
|
|
39
|
+
- `construct.ts` — turn-by-turn authoring of a construct JSON file (one
|
|
40
|
+
JSON file → one web component); stateless, the harness owns the file.
|
|
41
|
+
- `scaffold.ts` — generates a full chat integration (framework + backend
|
|
42
|
+
route + integration wiring) from `agent-tooling/registry.ts` and
|
|
43
|
+
`agent-tooling/archetypes.ts` (backed by `agent-tooling/integrations/`).
|
|
44
|
+
- `reference.ts` — component/element/card lookup for an agent deciding what
|
|
45
|
+
to use.
|
|
46
|
+
- `theme.ts` — produces a `--kai-*` CSS token override block from a brand
|
|
47
|
+
color or description, resolved against the kit's own `theme.css`.
|
|
48
|
+
- `debug.ts` — diagnoses common `kai-*` integration failures from a
|
|
49
|
+
symptom/snippet, sourced from `apps/docs/src/content/docs/guides/for-ai-agents.mdx`.
|
|
50
|
+
- **`construct/`** — the construct engine: a declarative JSON format compiled
|
|
51
|
+
to a self-registering web component. Pipeline is `schema.ts` (Zod, single
|
|
52
|
+
source of truth) → `codegen.ts` (the one generation path — `kai dev`,
|
|
53
|
+
`kai compile`, and `kai eject` all call the same `generateProject`) →
|
|
54
|
+
`cli.ts` (the `validate | eject | dev | compile` subcommands). Fixtures
|
|
55
|
+
live in `construct/fixtures/` (`demo-widget`, `ops-console`,
|
|
56
|
+
`owner-widget`). The published JSON Schema artifact is
|
|
57
|
+
`construct/construct.v1.schema.json`, mirrored byte-identical at
|
|
58
|
+
`apps/docs/public/schemas/construct/v1.json`; both are guarded against
|
|
59
|
+
drift from the Zod source by `construct/schema-artifact.test.ts` (asserts
|
|
60
|
+
the checked-in artifact equals `z.toJSONSchema(ConstructSchema)` right now)
|
|
61
|
+
and regenerated by `scripts/gen-construct-schema.mjs` via `build:api`.
|
|
62
|
+
- **`catalog/`** — Zod-typed reference data, mostly independent of the
|
|
63
|
+
construct engine, each file feeding a specific consumer:
|
|
64
|
+
- `catalog-types.ts` — the shared Zod types (`SurfaceArchetype`,
|
|
65
|
+
`DeliveryTarget`, `WireReader`, `Backend`, …) the rest of the catalog is
|
|
66
|
+
built from.
|
|
67
|
+
- `invariants.ts` — the composition invariants an agent must hold, each
|
|
68
|
+
with an `enforcedBy` path and a `lint` script name; feeds
|
|
69
|
+
`invariants.test.ts` and the acceptance harness.
|
|
70
|
+
- `surfaces.ts` — the sorted inventory of product-shaped surfaces vs.
|
|
71
|
+
ingredients vs. corpus; feeds `surfaces.test.ts` and `derived.json`.
|
|
72
|
+
- `fabrications.ts` — tags agents have invented while working from an
|
|
73
|
+
acceptance pack (and what they should have used instead); renders the
|
|
74
|
+
pack's `FABRICATED.md`, checked against `derived.json` in both directions
|
|
75
|
+
by `fabrications.test.ts`.
|
|
76
|
+
- `scenarios.ts` — the acceptance deck (spec §6): prompts + what each
|
|
77
|
+
needs, written before the catalog it measures.
|
|
78
|
+
- `labs-titles.ts` — parses the real `Labs/…` story titles registered by
|
|
79
|
+
story files (TypeScript AST, not a regex), used to keep the catalog's
|
|
80
|
+
story inventory honest.
|
|
81
|
+
- `derived.json` — generated inventory of elements/props read by
|
|
82
|
+
`catalog-types.ts`, `fabrications.ts`, and `surfaces.ts` (and their
|
|
83
|
+
tests) rather than by anything outside the catalog.
|
|
84
|
+
- `catalog/README.md` — the catalog's own deeper documentation.
|
|
85
|
+
|
|
86
|
+
How they relate: the MCP's `construct` and `scaffold` tools **consume** the
|
|
87
|
+
construct engine and the catalogs — they don't reimplement authoring logic,
|
|
88
|
+
they call into `construct/schema.ts` / `construct/codegen.ts` and read the
|
|
89
|
+
registry/catalog data. The construct CLI (`bin/mcp.js validate|dev|compile|eject`,
|
|
90
|
+
built from `construct/cli-entry.ts` → `dist/construct-cli.es.js`) runs the
|
|
91
|
+
engine standalone, with no MCP server involved at all.
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
- **`npx @kitn.ai/ui mcp`** starts the MCP server (the historical default
|
|
96
|
+
subcommand). For wiring it into a multi-harness setup, see the docs site's
|
|
97
|
+
agents guide: `apps/docs/src/content/docs/guides/for-ai-agents.mdx`.
|
|
98
|
+
- **CLI form**, no MCP: `node bin/mcp.js validate|dev|compile|eject <construct.json>`
|
|
99
|
+
(`bin/mcp.js` dispatches `dev`/`compile`/`eject`/`validate` to the construct
|
|
100
|
+
CLI and anything else, including `mcp` or no argument, to the MCP server;
|
|
101
|
+
an unrecognized subcommand is a loud error, not a silent fallthrough).
|
|
102
|
+
`eject` writes a real Solid project you own; `dev` gives a live-reload
|
|
103
|
+
preview; `compile` emits one self-registering `.js`.
|
|
104
|
+
- **Acceptance harness** (`packages/ui/scripts/acceptance-*.mjs`:
|
|
105
|
+
`acceptance-pack.mjs`, `acceptance-run.mjs`, `acceptance-eval.mjs`,
|
|
106
|
+
`acceptance-gate-compiles.mjs`) — builds and grades a coding agent's
|
|
107
|
+
from-scratch attempt at using the catalog with no kit source, per the
|
|
108
|
+
scenarios in `catalog/scenarios.ts`; this is how the catalog's own claims
|
|
109
|
+
get checked against a real agent rather than asserted.
|
|
110
|
+
|
|
111
|
+
## Worked example
|
|
112
|
+
|
|
113
|
+
`construct/fixtures/owner-widget.construct.json`, field by field:
|
|
114
|
+
|
|
115
|
+
- `"$schema"` — points at the published schema URL for editor validation.
|
|
116
|
+
- `"name": "acme-support"` — the construct's name; also the emitted custom
|
|
117
|
+
element's tag basis.
|
|
118
|
+
- `"layout": "widget"` — a floating launcher widget, not full-screen or docked.
|
|
119
|
+
- `"provider": { "mode": "mock" }` — no backend; canned responses (vs.
|
|
120
|
+
`mode: "endpoint"`, the consumer's own chat route).
|
|
121
|
+
- `"userId": "user_123"` — the identity value threaded through for
|
|
122
|
+
history/persistence.
|
|
123
|
+
- `"header": { "title": "Acme Support" }` — the chat header's title text.
|
|
124
|
+
- `"theme": { "unreadColor": "#38BDF8" }` — one branded token override.
|
|
125
|
+
- `"empty": { "title": ..., "description": ... }` — the empty-state copy
|
|
126
|
+
shown before the first message.
|
|
127
|
+
- `"widget": { "position": "top-start", "defaultOpen": true }` — widget-layout
|
|
128
|
+
placement and initial open state.
|
|
129
|
+
- `"capabilities.attachments.accept"` — allowed MIME patterns for uploads.
|
|
130
|
+
- `"capabilities.history.persistence": "local"` — history persisted in the
|
|
131
|
+
browser, not the consumer's backend.
|
|
132
|
+
- `"capabilities.starters"` — suggested first prompts.
|
|
133
|
+
- `"capabilities.reasoningOpen": true` — reasoning panels start expanded.
|
|
134
|
+
- `"capabilities.conversations": true` — enables the conversations sidebar.
|
|
135
|
+
|
|
136
|
+
`codegen.ts` turns this into a small, deterministic (no dates, no randomness,
|
|
137
|
+
fixed key order) Solid project: pure Solid interior composing
|
|
138
|
+
`@kitn.ai/ui/solid` components, provider glue built on `@kitn.ai/ui/state` +
|
|
139
|
+
`@kitn.ai/ui/wire` (never a hand-rolled SSE reader), and one
|
|
140
|
+
`defineWebComponent` facade carrying the tag, theme default, and slots. The
|
|
141
|
+
facade injects the kit's compiled CSS into the shadow root, so the generated
|
|
142
|
+
project needs no Tailwind and no CSS build step. `kai dev`, `kai compile`,
|
|
143
|
+
and `kai eject` all go through this same generation path — the preview is
|
|
144
|
+
the artifact, there is no separate interpreter to drift from it.
|
|
145
|
+
|
|
146
|
+
## Rules that bind changes here
|
|
147
|
+
|
|
148
|
+
- **Vocabulary never logic** — the construct format has no handlers and no
|
|
149
|
+
expressions; it describes, it doesn't script. `schema.ts` is `.strict()`
|
|
150
|
+
everywhere, so an unknown key is a loud rejection.
|
|
151
|
+
- **Widen, never restructure `schema.ts`** — it's the single source of truth;
|
|
152
|
+
never hand-edit the generated JSON Schema artifacts or restate an enum
|
|
153
|
+
elsewhere, read it off `ConstructSchema.shape` or the generated artifact.
|
|
154
|
+
- **Derive, don't type** — the published schema artifact is generated from
|
|
155
|
+
Zod (`scripts/gen-construct-schema.mjs`, run by `build:api`) and pinned by
|
|
156
|
+
`construct/schema-artifact.test.ts`; `verify:construct`'s layout and
|
|
157
|
+
capability axes are read off that same drift-guarded artifact rather than
|
|
158
|
+
hand-listed, so adding a layout or capability to the Zod schema moves what
|
|
159
|
+
the gate covers on its own.
|
|
160
|
+
- **`verify:construct`** (`scripts/verify-construct.mjs`) drives the real CLI
|
|
161
|
+
end to end against this checkout's own packed tarball — eject, install,
|
|
162
|
+
compile under the emitted tsconfig, build, and bundle inside a real
|
|
163
|
+
consumer app — for every layout/capability combination the schema
|
|
164
|
+
currently declares. No unit test in this tree leaves the process; this is
|
|
165
|
+
the one that does.
|
|
166
|
+
- **`verify:scaffold`** (`scripts/verify-scaffold-compiles.mjs`) compiles the
|
|
167
|
+
scaffolder's emitted front ends and backend routes under `tsc --strict`
|
|
168
|
+
across the integration × surface × framework axes derived from the
|
|
169
|
+
registry, plus structural checks (e.g. the Solid emit branches on every
|
|
170
|
+
`MessagePart` variant).
|
|
171
|
+
- **`verify:generated`** (`scripts/verify-generated-sync.mjs`) re-runs
|
|
172
|
+
`build:api` and fails if any derived artifact in the tree (including
|
|
173
|
+
catalog/manifest files this directory reads) differs from what the
|
|
174
|
+
generator produces right now — catches drift `nx build ui`'s NX cache can
|
|
175
|
+
hide.
|
|
176
|
+
- **The schema-artifact test pair** (`construct/schema-artifact.test.ts`)
|
|
177
|
+
catches a `schema.ts` edit that shipped without a regen, in the unit suite
|
|
178
|
+
itself rather than only in CI's `verify:generated`.
|
|
179
|
+
|
|
180
|
+
## Where deeper docs live
|
|
181
|
+
|
|
182
|
+
- Repo root [`CLAUDE.md`](../../../../CLAUDE.md)'s **Map** section for how
|
|
183
|
+
this directory sits relative to `primitives/`, `ui/`, `components/`, and
|
|
184
|
+
`elements/`.
|
|
185
|
+
- [`.claude/README.md`](../../../../.claude/README.md) for the project-local
|
|
186
|
+
skills/agents that exercise this tooling (notably `/consumer-regression`).
|
|
187
|
+
- The docs site's agents guide,
|
|
188
|
+
`apps/docs/src/content/docs/guides/for-ai-agents.mdx` — the rules
|
|
189
|
+
`debug.ts` and the MCP's contract enforcement are sourced from.
|
|
190
|
+
- `docs/superpowers/specs/2026-08-25-construct-engine-design.md` — the
|
|
191
|
+
construct engine's design spec (decisions ledger, format rules, success
|
|
192
|
+
criteria).
|
|
@@ -142,9 +142,9 @@ export const invariants: TInvariant[] = [
|
|
|
142
142
|
],
|
|
143
143
|
examples: [
|
|
144
144
|
{
|
|
145
|
-
wrong: 'chat.
|
|
145
|
+
wrong: 'chat.conversationRows = rows;',
|
|
146
146
|
right: 'conversations.conversations = rows;',
|
|
147
|
-
note:
|
|
147
|
+
note: "kai-chat's own `conversations` prop is a boolean flag (turns the built-in list panel on); it has no `conversationRows`-shaped data prop, and the sidebar is its own element. One element never holds the whole app state.",
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
wrong: "chat.addEventListener('kai-conversation-select', (e) => load(e.detail.id));",
|
|
@@ -25,6 +25,20 @@ const USAGE = `usage: kai <command>
|
|
|
25
25
|
kai compile <construct.json> [outDir] one self-registering .js (Task 6)
|
|
26
26
|
`;
|
|
27
27
|
|
|
28
|
+
/** H-3, decide loudly: `home.recentConversation` renders nothing without
|
|
29
|
+
* `capabilities.conversations` to draw the card from — never fatal (the
|
|
30
|
+
* schema explicitly does NOT require conversations for `home`, see
|
|
31
|
+
* schema.ts's own doc on `home`), just a non-blocking heads-up at validate
|
|
32
|
+
* time, the same idiom `validate`'s success line already uses (io.log, not
|
|
33
|
+
* a bare console call). Extracted so it can be unit-tested without going
|
|
34
|
+
* through the CLI's stdout wiring. */
|
|
35
|
+
export function homeRecentConversationWarning(construct: Construct): string | null {
|
|
36
|
+
if (construct.home?.recentConversation && !construct.capabilities?.conversations) {
|
|
37
|
+
return 'warning: home.recentConversation is set but capabilities.conversations is not — the recent-conversation card will render nothing without it.';
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
28
42
|
export function loadConstruct(path: string, io: CliIo): Construct | null {
|
|
29
43
|
const abs = resolve(path);
|
|
30
44
|
let raw: string;
|
|
@@ -66,6 +80,8 @@ export async function runCli(argv: string[], io: CliIo = defaultIo): Promise<num
|
|
|
66
80
|
const construct = loadConstruct(rest[0] ?? '', io);
|
|
67
81
|
if (!construct) return 1;
|
|
68
82
|
io.log(`valid construct: <${construct.name}> (layout: ${construct.layout}, provider: ${construct.provider.mode})`);
|
|
83
|
+
const warning = homeRecentConversationWarning(construct);
|
|
84
|
+
if (warning) io.log(warning);
|
|
69
85
|
return 0;
|
|
70
86
|
}
|
|
71
87
|
case 'eject': {
|