Skip to content

feat(platform): headless chat - react hook - #315

Merged
amitbardos merged 5 commits into
mainfrom
amitb/platform-react-hook-only
Oct 18, 2026
Merged

amitbardos merged 5 commits into
mainfrom
amitb/platform-react-hook-only

Conversation

@amitbardos

@amitbardos amitbardos commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Start here: src/react/chat.types.ts is the whole public surface, and useBase44Chat.ts the hook. Usage lives in their JSDoc @example blocks, which the Mintlify pipeline turns into the reference's Example tabs.

What

A new subpath, @base44/platform/react, with one hook: useBase44Chat. It runs the AI chat for one app in the partner's own UI, on top of the platform client, and renders nothing. The contract is the one in the design doc "Partner chat: design".

import { useBase44Chat } from "@base44/platform/react";

const chat = useBase44Chat({ appId, backend, onAppCreated });
// chat.items, chat.status, chat.connection, chat.error, chat.send(), chat.create(), chat.clearError()
  • backend: four calls the partner implements (createApp, openLiveSession, sendMessage, submitToolCallInput), in any language behind them. openLiveSession returns Base44's socket-session body unchanged, and the client calls it again on every renewal. No credential reaches the browser.
  • items: the conversation, oldest first: role, text, createdAt, pending and steps, one per tool call (tool, files, label, status). A sent prompt shows at once with pending: true until Base44's own message arrives, matched by exact text.
  • Questions: only the last assistant message makes the chat wait, as in Base44's builder, and only its waiting steps carry a question (choice, secrets, approval), bound to its own tool call. A choice answers with picked labels and optional free text (custom_text), sent the way the builder sends them. Every kind can be declined; a kind this release doesn't know is declined once per tool call.
  • status: error > waiting > building > submitted > ready. A failed build is error with code build_failed. submitted runs from the call until Base44 reports a newer status, a reply or a queue change, or an answered call stops waiting.
  • connection: connecting until the first snapshot, then connected, and disconnected once live updates stop for good, with error saying why. A reconnecting state needs a client callback and comes in its own PR.
  • Apps: send(prompt) needs an app; create(prompt) makes one, and while appId is null the chat follows the app it made. A set appId always wins.
  • Resilience: rejoins keep messages older than the snapshot's window; a status older than the newest seen is ignored, since Base44 resends an earlier turn's; a call that fails after the chat switched apps leaves the new app alone.
  • Actions never throw: a failure lands in error; a question whose answer failed opens again.

Not in this PR: showing or editing Base44's prompt queue, a reconnecting state, and loading messages older than the last 50. The design doc's Next section lists them.

Packaging

  • exports["./react"] plus typesVersions for node10 resolution.
  • react is an optional peer; the client and server entries never import it. The tarball test asserts it is never a dependency.
  • React, react-dom, their types and jsdom are devDependencies, for the hook tests only.

Tests

  • tests/unit/react/chat-items.test.ts: the message-to-items mapping, without React.
  • tests/unit/react/useBase44Chat.test.ts: the hook under jsdom with a recorded platform client: status precedence, pending prompts, stale statuses, renewal, switching apps, answers and automatic declines.
  • tests/types/react.types.ts: compiles against the subpath.
  • tests/package.test.mjs: the published /react entry resolves at runtime and under NodeNext, Bundler and Node resolution.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/sdk@0.8.54-pr.315.e54dc54

Prefer not to change any import paths? Install using npm alias so your code still imports @base44/sdk:

npm i "@base44/sdk@npm:@base44-preview/sdk@0.8.54-pr.315.e54dc54"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "@base44/sdk": "npm:@base44-preview/sdk@0.8.54-pr.315.e54dc54"
  }
}

Preview published to npm registry — try new features instantly!

@amitbardos
amitbardos force-pushed the amitb/platform-react-hook-only branch from 51bc094 to 17c2d1a Compare October 10, 2026 18:00
@amitbardos amitbardos changed the title feat(platform): @base44/platform/react, option A: the hook only feat(platform): chat react hook Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available! (@base44/platform)


Install this PR's preview build with npm:

npm i @base44-preview/platform@0.2.0-pr.315.e54dc54

Prefer not to change any import paths? Install using npm alias so your code still imports @base44/platform:

npm i "@base44/platform@npm:@base44-preview/platform@0.2.0-pr.315.e54dc54"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "@base44/platform": "npm:@base44-preview/platform@0.2.0-pr.315.e54dc54"
  }
}

Preview published to npm registry — try new features instantly!

@amitbardos
amitbardos marked this pull request as ready for review October 17, 2026 09:10
@amitbardos
amitbardos requested a review from ozsay October 17, 2026 09:47
A new subpath, @base44/platform/react, with one hook: useBase44Chat. It runs
the AI chat for one app in the partner's own UI, on top of the platform
client, and renders nothing. React 18 or 19 is an optional peer, so the
client and server entries stay framework-free.

- backend: four calls the partner implements (createApp, openLiveSession,
  sendMessage, submitToolCallInput). openLiveSession returns Base44's
  socket-session body unchanged, and is called again on every renewal. No
  credential reaches the browser.
- items: the conversation, oldest first. Each step is one tool call, and a
  waiting step on the last assistant message carries its question, bound to
  the actions that answer it (choice, secrets, approval; every kind can be
  declined). A kind this release doesn't know is declined once per tool
  call. A sent prompt shows at once as a pending item until Base44's own
  message arrives.
- status: error > waiting > building > submitted > ready. A failed build is
  error with code build_failed; submitted runs from the call until Base44
  reports work, a reply or a queue change.
- connection: connecting, connected, or disconnected once live updates stop
  for good, with error saying why.
- send needs an app and create makes one; while appId is null the chat
  follows the app create made.
- Rejoins keep messages older than the snapshot's window; a status older than
  the newest seen is ignored, since Base44 resends an earlier turn's.
- Actions never throw: a failure lands in error, and a question whose answer
  failed opens again.

The JSDoc follows the SDK docs guidelines, with @example blocks for the
reference pages. Tests cover the item mapping, the hook under jsdom with a
recorded platform client, the public types, and the published /react entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ozsay ozsay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public contract
Fields the socket already sends that the hook drops. All are in builder.events.generated.ts; adding them is additive. Inspired by comparing the contract with AG-UI.

Ids and links

  • StatusObject.turn_id: the turn the status belongs to (AG-UI's run id). Could be Base44Chat.turnId and ChatItem.turnId.
  • additional_message_params.client_creation_id: lets a pending prompt be matched by id, not text (see the pending-prompt finding).
  • Message.checkpoint_id: the app before or after a turn. Enables "restore to here" UIs.
  • Questions: expose toolCallId and messageId as data next to the bound methods, so items can be logged or stored.

Steps

  • display.summary (bash, exec, screenshot, shell), display.entity_name and record_count, arguments.summary for entity updates and deletes: far better labels than tool name plus files.
  • results on media tools (ImageResult with image_url and status) and the image.resolved event: show generated images.
  • task.progressed (current, total, percentage, keyed by tool_call_id): a progress bar on a running step.
  • auto_approved and mutation_applied on every call: an "auto-approved" badge and "no changes made".
  • display.writes_entities: tells a partner to refresh the preview.

Questions

  • QuestionAnswer.custom_text and Question.type: the choice answer can only send picked labels, so a free-text question cannot be answered.
  • user_input on question calls: what was answered stays visible after the question closes; today the question disappears.
  • Approval guard and details (entity_name, change_type, changed_ops, function_name, method, inspection_failed): the approval exposes only the tool name and a reason.
  • Question.covers with UpdatePlanArguments, PrdArguments and the plan_mode / plan_approved message flags: everything a plan-mode UI needs.

Chat-level state and events

  • snapshot.queue and queue.updated (items, is_paused): the queue, and why a pending prompt waits (the builder pauses it while a question is open). The description defers the queue to another PR.
  • preview.reload_requested, preview.navigation_requested and files.changed: partners embedding a preview need these, and today only the raw client gives them.

Not candidates: system role (never sent), text deltas (messages are replaced whole).

Comment thread packages/platform/src/react/useBase44Chat.ts Outdated
Comment thread packages/platform/src/react/chat.types.ts
Comment thread packages/platform/src/react/useBase44Chat.ts Outdated
Comment thread packages/platform/src/react/chat-items.ts Outdated
Comment thread packages/platform/src/react/useBase44Chat.ts Outdated
amitbardos and others added 2 commits October 18, 2026 14:05
From the review on #315:
- An answer's submission ends once its call stops waiting, and any newer
  status ends a submission, so status no longer sticks at submitted when
  Base44 stayed processing while the question waited.
- A send, answer or create that fails after the chat switched apps no longer
  touches the new app's error or status.
- A pending prompt settles only on an exact text match, so another client's
  prompt to the same app never settles ours.
- create while the chat follows a set appId drops its pending prompt, so it
  can't reappear when the new app is picked later.

Choice answers now carry free text (answer(picked, texts?)), and send a
single pick as selected_label and several as selected_labels, the way
Base44's builder does; Base44 reads a design or palette pick only from
selected_label. ChoiceEntry gains Base44's question type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…maries

From the review on #315, the three fields partners need most:
- ApprovalQuestion gains guard and details (summary, entityName,
  changeType, changedOps, functionName, method, inspectionFailed), so the
  user sees what they approve instead of a tool name alone.
- ChatStep gains answered: what the user answered on a choice, next to each
  question, so the chat's history keeps it after the question closes.
- ChatStep gains summary, Base44's description of the step, and label uses
  it, then the entity and record count, then the tool and its files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
amitbardos added a commit that referenced this pull request Oct 18, 2026
From the review on #315, the fields that only pass on data Base44 already
sends:
- Every question carries toolCallId and messageId as data, beside the
  actions already bound to them, so a partner can log or store it.
- ChatItem.checkpointId: the checkpoint Base44 ties to the message.
- Base44Chat.turnId: the user message whose turn the current status belongs
  to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amitbardos
amitbardos force-pushed the amitb/platform-react-hook-only branch from a09735c to c77c79d Compare October 18, 2026 11:49
@amitbardos

amitbardos commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@ozsay thanks, the AG-UI comparison was useful. On the fields the hook drops, I took the ones without which a partner's chat breaks or misleads, and left the rest until a partner asks: adding a field later is free, and taking one out after release breaks partners.

Added

  • Free-text answers (9035fc7): answer(picked, texts?) sends custom_text, and ChoiceEntry.type says which questions take only text. Answers now also go out the way Base44's builder sends them, one pick in selected_label, several in selected_labels: Base44 reads a design or palette pick only from selected_label.
  • Approval guard and details (c77c79d), so the user sees what they approve.
  • ChatStep.answered from user_input, so the history keeps what was answered after the question closes.
  • label uses Base44's own description of the step (display.summary, arguments.summary), then the entity and record count, then the tool and files. A separate summary field was left out: label already shows it.

Left for later

  • toolCallId / messageId on questions, turn_id, checkpoint_id: nothing needs them yet.
  • Media results with image.resolved, and task.progressed: each needs new event handling and state; worth it once a partner draws images or progress.
  • Preview events: needs a decision on how the hook exposes them.
  • Plan mode: approving a plan goes through Base44's private /exit-plan-mode, so it can't be built on the public API yet.
  • The queue: its own PR, with remove and edit.

Could you take another look?

🤖 Generated with Claude Code

@amitbardos
amitbardos requested a review from ozsay October 18, 2026 11:51
amitbardos and others added 2 commits October 18, 2026 14:58
…ws it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main added the webhooks module to @base44/platform/server (#319). The
package.json conflict keeps main's engines field beside this branch's single
devDependencies block; the lockfile is main's plus the React dev
dependencies, resolved from registry.npmjs.org.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ozsay

ozsay commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Thanks @amitbardos, re-reviewed at 5ffd6d3. All five comments are fixed, and each fix has a test:

  • Status stuck at submitted: the answer's submission ends once its call stops waiting, and any newer status ends it too.
  • Description: now matches the code (three connection states; reconnecting in its own PR).
  • Failures after an app switch: send, answer and create check the app they ran for before touching state.
  • Another client's prompt settling ours: exact text match only.
  • Hidden prompt after create with a set appId: the prompt is dropped.

The new fields (free-text answers, selected_label vs selected_labels, approval guard/details, answered, the richer label) read well. Leaving the rest until a partner needs it makes sense.

One low-severity edge in the exact-match fix. The removed fallback was also the only way out for a pending prompt whose text never arrives. Base44 keeps the text as sent for a plain send, but the public queue API can edit (PUT /queue/{item_id}) or delete a queued prompt. An edited prompt arrives with different text, and a deleted one never arrives. Either way our pending item now stays until the app changes. Nothing in this PR edits the queue, so this belongs in the queue PR: settle or drop pending prompts from queue.updated (an item that left the queue without a matching message).

On client_creation_id: the send endpoint already accepts it today, inside the undocumented additional_message_params, and it comes back on the socket. It is hidden from the public spec, so I wouldn't build on it yet. Asking the backend to make it a documented field would let pending prompts match by id and remove the text matching entirely.

Nits (no change needed):

  • A single-select answer() given several labels sends only the first, silently. A line in the JSDoc would cover it.
  • Once an answer is taken, the submission clears even if a send went out after the answer, so status can read ready a moment early.

🤖 Generated with Claude Code

@amitbardos
amitbardos merged commit d673109 into main Oct 18, 2026
8 checks passed
amitbardos added a commit to base44/base44-platform-starter that referenced this pull request Oct 21, 2026
* Tiny Sunny: run the chat on @base44/platform/react's useBase44Chat

The chat's own Base44 layer (useLiveChat.ts: the socket, merging and sorting
messages, the status, the question formats) is replaced by the hook, and
ToolQuestion takes the hook's bound question instead of a raw tool call.

- Chat draws items, steps and questions from the hook, shows a sent prompt
  at once (faded while pending), and keeps Send disabled unless the status is
  ready or error.
- Chat no longer remounts per app, so the first prompt survives create.
- openLiveSession passes Base44's socket-session body through, and
  sendMessage and submitToolCallInput return after 3 seconds: Base44 holds
  both open for the whole turn, and Next.js runs one server action at a time.
- @base44/platform comes from base44/javascript-sdk#315's preview package
  until that PR is released.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the released @base44/platform 0.3.0, with free-text answers, approval details and answers

- @base44/platform is the released ^0.3.0 instead of #315's preview package.
- A text_input question gets a text field, and the answer sends it.
- An approval shows what the action would do: the entity, the change, the
  access, the function, as far as Base44's check says.
- A step that was answered keeps its questions and answers under its line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: @base44/platform 0.4.0; "the AI", not "the builder"

0.4.0 renames the client's builder module to chat; Tiny Sunny only uses
useBase44Chat, whose API is unchanged, so this is the version bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one waitForRefusal for the held-open chat requests; log late failures

sendMessage and submitToolCallInput share the 3-second cutoff instead of
repeating it. A failure that lands after the cutoff has no caller left, so it
is logged on the server rather than dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: an approval shows its action and reason only; current netlify.toml comment

describe() repeated the summary the hook already puts in reason, so the
approval line drops the details. The secrets-scan comment no longer names
versions that both sides have moved past.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one component per question kind

ToolQuestion picks Choice, Secrets or Approval by kind, so each keeps only
its own state. They share one Actions row for Submit/Allow and Decline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the AI's messages render as Markdown

The AI answers in Markdown, so its text showed raw ** and backticks. It now
goes through react-markdown, the version Sunny already installs, with a few
chat styles for lists, code and links. Links open in a new tab. The user's
own prompts stay plain text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the hook is useChat

@base44/platform drops the Base44 prefix from its React entry (base44/javascript-sdk#345),
so Chat.tsx uses useChat, UseChatResult and ChatBackend.

The new names ship in 0.5.0; the dependency moves to ^0.5.0 once it is published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: @base44/platform ^0.5.0

0.5.0 is published with the new React names, so the dependency and the lockfile move to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants