docs(cef:develop): rewrite widgets.md for the cef dev loop + WidgetRuntime#6
Conversation
…ntime Document real widget development: the cef dev local loop, the widget manifest, typed widget kinds, embedded vs standalone run modes, connectAgent, and querying cubbies via window.WidgetRuntime.query. Bump the cef plugin version to 0.1.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- deploy/develop quickstarts: add the 'pnpm install' step after 'cef init' (init no longer installs by default). - develop: document 'npx cef dev' (widget dev server) + the 'pnpm dev' script alias.
Where logs go, logging that surfaces the real cause (fold error text into the message — the sandbox drops extra console args), guard handlers so a throw can't wedge the Job, local repro with testAgent, and a common-failure table. Linked from SKILL.md; plugin 0.1.1 → 0.1.2.
Extra console args are JSON-serialized into a separate field; an Error
collapses to {} (non-enumerable message/stack). Put the detail in arg 0.
…ample
Align engagements.md with the vault contract (SDK PR): Event is
{ type, payload, timestamp, context?, role?, eventId? }; the example handler no
longer uses event.ts/event.id (records Date.now()). Plugin 0.1.2 -> 0.1.3.
widgets.md: cef dev now live-reloads (not manual refresh); add an 'iterating with a coding agent' note — use browser automation for the visual/console loop, but the interactive wallet-connect + live data need a human. Plugin 0.1.3 -> 0.1.4.
…llet connect The embed-wallet session persists across reloads, so live-reload reconnects silently — the human gate is only the first connect, not every iteration.
…e raw SQL) Connection + scope (endpoints/wallet/agentId/cubbyAlias) must be in the manifest (resolved before JS); queries[] are optional — a custom widget can pass raw SQL to WidgetRuntime.query(). The security boundary is the scope, not the SQL.
… querying) cubbyAlias is optional if the widget never reads a cubby, but query() has no per-call cubby arg — it always targets manifest.cubbyAlias — so a querying widget must declare its one cubby binding. Only endpoints/wallet/agentId are strictly always-required.
spoluyan
left a comment
There was a problem hiding this comment.
Review (inline): rewrite widgets.md for the cef dev loop + WidgetRuntime
High-quality rewrite. The new widgets.md is a clear upgrade — from a vague manifest/cef push framing to a concrete cef dev → cef build → cef push loop, typed widget kinds, a worked list example with connectAgent()/AgentNotConnectedError handling, and embedded-vs-standalone. The new troubleshooting.md is genuinely valuable (the Error → {} serialization gotcha alone will save people hours). The Event<P> correction and pnpm install additions are accurate.
CI is green (Analyze, CodeQL, validate), and the diff is public-safe — no internal names, ADR refs, or private source paths leaked.
Blocking
widgets.md ends with two leaked tool-call tags — see the inline comment at the very end of the file. </content> / </invoke> are agent-write artifacts that got committed; they'll render as visible stray text at the end of the reference.
Should fix
SKILL.md's lead example still uses the removed event.ts / event.id. This PR drops ts/id from Event<P> (and updates engagements.md's example to match — see inline ✅), but the identical "shape at a glance" example in develop/SKILL.md (lines ~72-77) was unchanged context and still reads:
[Date.now(), event.payload.text, event.ts], // ✗ event.ts no longer exists
await ctx.publish("ack", { for: event.id }); // ✗ event.id no longer existsGitHub won't let me anchor an inline comment there (those lines aren't in this PR's diff), but it's directly contradicted by the type change in this very PR — worth a follow-up edit so the first example a reader sees matches the reference it links to.
Nits (inline)
- The
candidatesexample omitsdescriptionthough the field table still lists it as optional. - "Claude Code's Chrome tools" will date; consider generic phrasing.
Fix the blocker + the SKILL.md example and this is good to merge.
| can keep its query logic in the JS — `query("SELECT text, ts FROM messages …")` | ||
| — with no `queries[]` at all. Either way the boundary that matters is the |
There was a problem hiding this comment.
🛑 Blocking — these two lines are leaked tool-call artifacts (</content> / </invoke>), not part of the document. They're leftover from the agent write that produced the file and will render as visible stray text (or break the markdown render) at the end of the reference.
Both lines should be deleted so the file ends cleanly at:
- **`id` must be unique within the agent** and is what `cef dev [widgetId]`
targets.
There was a problem hiding this comment.
Removed the stray </content>/</invoke> in bf5ac94; the file now ends cleanly at the id-unique bullet.
| [now, event.payload.text, now], | ||
| ); | ||
| await ctx.publish("ack", { for: event.id }); | ||
| await ctx.publish("ack", { text: `got: ${event.payload.text}` }); |
There was a problem hiding this comment.
✅ Good — the corrected ack payload typechecks against the rewritten Event<P> (no more event.id, which the old { for: event.id } relied on). Same for now replacing the removed event.ts.
develop/SKILL.md (lines ~72-77) and was not updated here — it still uses event.ts and event.id. Since this PR is the one removing those fields, that lead example now contradicts the reference. Worth updating in this PR. (Those lines weren't in the diff, so I couldn't anchor an inline comment there — see the review summary.)
There was a problem hiding this comment.
Updated the SKILL.md lead example in bf5ac94 — event.ts/event.id gone (records Date.now(), ack carries text), matching the corrected Event<P> this PR introduces.
| timestamp: string; // ISO-8601, assigned by the vault | ||
| context?: string; // stream context (conversation / partition key) | ||
| role?: string; // publisher role, e.g. "user" | "agent" | ||
| eventId?: string; // server-assigned id (UUID v7); may be absent inbound |
There was a problem hiding this comment.
Nice touch documenting eventId? as "server-assigned (UUID v7); may be absent inbound" — it signals to handlers not to rely on it existing inbound.
Nit: since the old example's { for: event.id } is gone, consider adding a one-liner that handlers needing correlation should carry their own id in the payload (not lean on eventId). Optional.
There was a problem hiding this comment.
Left as optional — the eventId? doc already signals not to rely on it inbound; correlation-in-payload is a reasonable pattern but out of scope for this doc pass.
| events: ["candidate_added"], // optional: event types the widget subscribes to | ||
| dir: "./widgets/dashboard", // path (relative to cef.config.ts) to the BUILT widget dir | ||
| entry: "dashboard.html", // entry file WITHIN dir | ||
| config: { columns: ["name", "stage"] }, // kind-specific config, carried verbatim |
There was a problem hiding this comment.
Nit: the field-reference table below lists both name and description as optional metadata, but this canonical candidates example sets only name. Since this is the block people will copy, either show both (description: "Lists recent candidates") or drop description from the table — keep the two consistent. Trivial.
There was a problem hiding this comment.
Added description: "Recent candidates" to the example in bf5ac94 so it matches the field-reference table.
|
|
||
| ### Iterating with a coding agent | ||
|
|
||
| If your coding agent can drive a browser (e.g. Claude Code's Chrome tools), the |
There was a problem hiding this comment.
Nit: "Claude Code's Chrome tools" is accurate today but will date the doc. The sentence right above already generalizes to "a coding agent can drive a browser" — consider dropping the parenthetical or making it generic (e.g. "(e.g. via Playwright / a browser MCP)"). Optional.
There was a problem hiding this comment.
Made it generic in bf5ac94 — "(e.g. via Playwright or a browser MCP)".
…L example - Delete leaked </content></invoke> at end of widgets.md (blocker). - SKILL.md lead example: event.ts/event.id -> Date.now()/ack text (matches the corrected Event<P> this PR introduces). - Add description to the widget example; generic browser-automation phrasing. - Plugin 0.1.4 -> 0.1.5.
Rewrites the
cef:developwidget reference for real widget development, and bumps the plugin version. Keeps the plugin in sync with the SDK's widget work (CEF-AI/sdk#113 + #114), per the SDK's plugin-sync rule.What
plugins/cef/skills/develop/references/widgets.md— full rewrite: thecef devloop, the widget manifest, typed kinds (record | list | dashboard | submit | conversation | composite | custom), a workedlistexample usingwindow.WidgetRuntime.query,connectAgent()+ not-connected handling, and embedded vs standalone.plugins/cef/.claude-plugin/plugin.json— version0.1.0 → 0.1.1.Public-safe: no internal names, ADRs, or private source paths.
🤖 Generated with Claude Code