# @base44/superagent-native

React Native entrypoint for the Superagent mobile experience.

This package is intentionally native-only. It does not import the web Superagent
implementation from `frontend/`; the mobile app should provide shell concerns
such as auth, API clients, analytics, and external navigation through props.

## Build

Build the package before consuming or packing it:

```sh
npm run build -w @base44/superagent-native
```

The build emits CommonJS, ES module, and TypeScript declaration files under
`lib/`. The package `main`, `module`, `types`, and `react-native` entries all
resolve to that built output, while `source` stays pointed at `src/index.ts` for
the Bob build pipeline.

## Usage

`SuperagentHomeScreen` is a **self-contained shell**: it runs the Superagent
runtime internally (agents, channels, connectors, automations, secrets, files,
the REST + realtime clients, and every mutation handler) via
`useSuperagentRuntime`, plus the attachment picker. The host passes only what the
package cannot produce itself — the auth-backed `session`, the signed-in `user`,
the build `environment`, native `adapters`, and app-shell navigation wiring:

```tsx
import { useMemo } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
  SuperagentHomeScreen,
  type SuperagentSessionConfig,
  type SuperagentShellAdapters,
} from '@base44/superagent-native';
import { io } from 'socket.io-client';

// The host owns this lifecycle. Reuse the same client for every native navigation root.
const queryClient = new QueryClient();

export function SuperagentRoute() {
  // Build both objects in useMemo — their identity feeds runtime effects.
  const session = useMemo<SuperagentSessionConfig>(
    () => ({
      baseUrl,
      cacheScope: activeWorkspaceId ?? 'personal',
      getAccessToken,
      getHeaders,
      queryClient,
      webUrl,
    }),
    [activeWorkspaceId, baseUrl, webUrl],
  );

  const adapters = useMemo<SuperagentShellAdapters>(
    () => ({
      alert: (title, message) => Alert.alert(title, message),
      confirm: showConfirmDialog,
      openUrl: (url) => Linking.openURL(url),
      createRealtimeSocket: ({ appId, baseUrl, token }) =>
        io(baseUrl, {
          path: '/ws-user-apps/socket.io/',
          query: { app_id: appId, token },
          transports: ['websocket'],
        }),
      liveVoiceAudio,                        // native audio module
      attachments: { pickNativeMedia, isCancel }, // folds in the file/photo/camera picker
      // …subscribeToAppResume / subscribeToDeepLinks / share / pickSandboxFiles …
    }),
    [],
  );

  return (
    <QueryClientProvider client={queryClient}>
      <SuperagentHomeScreen
        session={session}
        user={me}                       // the /me object; the runtime derives id/name/avatar from it
        environment="production"        // 'local' | 'preview' | 'production'
        adapters={adapters}
        navigationMode="external"
        onOpenAgent={(agentId) => pushConversationScreen(agentId)}
        onActiveAgentChange={(agent) => setNativeTitle(agent?.name)}
        onRouteChange={(route) => setTabsVisible(route.name === 'home')}
        onViewPlans={openBillingModal}
      />
    </QueryClientProvider>
  );
}
```

The `createRealtimeSocket` adapter creates the socket with the host's own
Socket.IO/native setup. Match the web socket contract:

```ts
io(wsBaseUrl, {
  path: '/ws-user-apps/socket.io/',
  query: { app_id: appId, token: runtimeAuthToken },
  transports: ['websocket'],
});
```

Attachment picking is provided through `adapters.attachments`: the host supplies
`pickNativeMedia(mode)` (returning the picked native media items) and an
`isCancel(error)` predicate, and the package handles the upload and renders the
upload-status modal itself. Omit `adapters.attachments` to hide the composer's
file/photo/camera affordances.

```ts
const adapters: SuperagentShellAdapters = {
  // …runtime adapters…
  attachments: {
    async pickNativeMedia(mode) {
      // mode: 'files' | 'photos' | 'camera'
      return [{ name: 'photo.jpg', mimeType: 'image/jpeg', uri: localCameraUri }];
    },
    isCancel: (error) => isPickerCancelError(error),
  },
};
```

Assistant messages render with the built-in native markdown renderer and the
package's own tool-call widgets. Both live inside the shell now — the runtime and
view are internal, so hosts no longer wire markdown/tool renderers through props.

## Publishing

This package is published publicly to **npm** (`registry.npmjs.org`) under the
`@base44` org by the
[`Publish superagent-native`](../../../.github/workflows/publish-superagent-native.yml)
workflow. Publishing authenticates via **npm Trusted Publishing (OIDC)** — there
is no long-lived npm token; CI mints a short-lived credential at publish time.

**Auto-bump on merge, nothing written to git:** you don't touch the version. On
every merge to `main` that changes `packages/superagent-native/**`, the workflow
reads the latest published version from npm, increments the **patch**, and
publishes that — without committing or pushing anything back to the repo. npm is
the source of truth for the version; the `version` field in this `package.json`
is **cosmetic** and is not used by the publish.

> First-time setup only: the package must exist on npm before OIDC works, so the
> very first version is published manually
> (`pnpm --filter @base44/superagent-native publish --access public`), after which
> the trusted publisher is configured on npmjs.com and all later releases are
> token-free. See the workflow file header for the exact trusted-publisher values.

Consumers just install it — no registry config needed, it's public:

```sh
npm install @base44/superagent-native
```
