akanjs 3.0.0-alpha.54 → 3.0.0-alpha.55

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-alpha.54",
3
+ "version": "3.0.0-alpha.55",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -547,7 +547,6 @@ export class RscRenderer {
547
547
  });
548
548
  return;
549
549
  }
550
- const theme = untrackedCookies().get("theme")?.value;
551
550
  let element: ReactNode;
552
551
  let effectivePatchDecision = safePatchDecision;
553
552
  if (match && safePatchDecision.status === "patch" && safePatchDecision.patch) {
@@ -563,9 +562,9 @@ export class RscRenderer {
563
562
  reason: "suffix-compose-fallback",
564
563
  commonPrefixLength: safePatchDecision.commonPrefixLength,
565
564
  };
566
- element = await this.#renderMatched(urlObj, match, theme, searchParams);
565
+ element = await this.#renderMatched(urlObj, match, searchParams);
567
566
  } else element = suffixElement;
568
- } else if (match) element = await this.#renderMatched(urlObj, match, theme, searchParams);
567
+ } else if (match) element = await this.#renderMatched(urlObj, match, searchParams);
569
568
  else element = await this.#renderNotFound(urlObj);
570
569
  const traceCacheKey =
571
570
  effectivePatchDecision.status === "patch" ? (patchCacheEntry?.key ?? cacheEntry?.key) : cacheEntry?.key;
@@ -1236,12 +1235,8 @@ export class RscRenderer {
1236
1235
  const routeHeadSnapshot = this.#createRouteHeadSnapshot(url, routeHead, {
1237
1236
  hasExplicitLanguageAlternates: routeHead.hasExplicitLanguageAlternates,
1238
1237
  });
1239
- const theme = untrackedCookies().get("theme")?.value;
1240
1238
  return (
1241
- <html
1242
- lang={params.lang ?? RscRenderer.#getLocale(pathname, this.#i18n)}
1243
- {...(theme ? { "data-theme": theme } : { suppressHydrationWarning: true })}
1244
- >
1239
+ <html lang={params.lang ?? RscRenderer.#getLocale(pathname, this.#i18n)} suppressHydrationWarning>
1245
1240
  <head key="head">
1246
1241
  <meta key="charset" charSet="utf-8" />
1247
1242
  <meta key="viewport" name="viewport" content="width=device-width, initial-scale=1" />
@@ -1263,7 +1258,6 @@ export class RscRenderer {
1263
1258
  async #renderMatched(
1264
1259
  url: URL,
1265
1260
  match: { pathRoute: PathRoute; params: Record<string, string> },
1266
- theme?: string,
1267
1261
  searchParams = RouteTreeBuilder.parseSearchParams(url.search),
1268
1262
  ): Promise<ReactNode> {
1269
1263
  this.#logger.verbose(
@@ -1289,11 +1283,9 @@ export class RscRenderer {
1289
1283
  searchParams,
1290
1284
  navKey: url.pathname + url.search,
1291
1285
  });
1286
+
1292
1287
  return (
1293
- <html
1294
- lang={match.params.lang ?? this.#i18n.defaultLocale}
1295
- {...(theme ? { "data-theme": theme } : { suppressHydrationWarning: true })}
1296
- >
1288
+ <html lang={match.params.lang ?? this.#i18n.defaultLocale} suppressHydrationWarning>
1297
1289
  <head key="head">
1298
1290
  <meta key="charset" charSet="utf-8" />
1299
1291
  <meta key="viewport" name="viewport" content="width=device-width, initial-scale=1" />
@@ -610,7 +610,8 @@ export class WebRouter {
610
610
  if (rscResult.type === "redirect")
611
611
  return Response.redirect(new URL(rscResult.location, url.origin), rscResult.status);
612
612
  if (rscResult.type === "not-found") return this.#renderSystemNotFoundFallbackResponse(req, url);
613
- const themeCookieExists = WebRouter.#hasCookie(req, "theme");
613
+
614
+ const cookieTheme = WebRouter.#cookieValue(req, "theme");
614
615
  const hostRequestStore = createRequestStore(req);
615
616
  const extraBootstrapInline = [
616
617
  rscResult.trace?.routeState
@@ -628,7 +629,7 @@ export class WebRouter {
628
629
  bootstrapModules: [this.#artifact.rscClientUrl],
629
630
  extraBootstrapInline: extraBootstrapInline || undefined,
630
631
  importmap: this.#artifact.vendorMap,
631
- theme: themeCookieExists ? undefined : (rscResult.theme ?? "system"),
632
+ theme: cookieTheme ?? rscResult.theme ?? "system",
632
633
  lateControl: rscResult.lateControl,
633
634
  waitForAllReady: rscResult.trace?.ssrBlocking ?? false,
634
635
  onCancel: (reason: unknown) => {
@@ -797,9 +798,6 @@ export class WebRouter {
797
798
  }
798
799
  }
799
800
 
800
- static #hasCookie(req: Request, name: string): boolean {
801
- return parseCookieHeader(req.headers.get("cookie") ?? "").has(name);
802
- }
803
801
  #getHtmlCacheEntry(req: Request, url: URL): { entry: RouteCacheEntry | null; reason?: string } {
804
802
  const decision = resolvePublicRouteCacheEntryDecision({
805
803
  request: req,
@@ -13,13 +13,21 @@ import { ScreenTarget } from "./ScreenTarget";
13
13
  *
14
14
  * Per zone view: a zone's `readState` reaches the keys its own subtree subscribes, and its `readScreen` and
15
15
  * `highlight` reach into its own `data-agent-zone` container rather than the whole document. A page can shadow any
16
- * of them by registering a hook tool of the same name — hook entries win over a source's.
16
+ * of them by registering a hook tool of the same name — hook entries win over a source's. **That is a root-scope
17
+ * move only**: inside a zone the hook entry is registered under its scope-prefixed name, which can never collide
18
+ * with the bare name a source publishes, so a zone drops a built-in with the session's `builtins` option instead.
17
19
  */
18
20
  export class StoreSurfaceSource implements SurfaceSource {
19
21
  /** Defined in `akanjs/ui/styles.css`, so the flash follows the app's own theme tokens. */
20
22
  static readonly highlightClass = "akan-agent-highlight";
21
23
  /** Mirrors the animation in that stylesheet: the class outlives the ring by nothing. */
22
24
  static readonly highlightMs = 2400;
25
+ /**
26
+ * What `tools()` contributes, in the order it builds them — the list a session's `builtins` option selects from.
27
+ * A screen that declares a hook tool of one of these names is not in it: that entry is the screen's, not this
28
+ * source's, so withholding the built-ins never withholds a tool a component published on purpose.
29
+ */
30
+ static readonly builtins = ["navigate", "goBack", "readScreen", "readState", "highlight"] as const;
23
31
 
24
32
  #bridge: AgentBridge | null;
25
33
  readonly #builtins = new Map<string, ToolEntry[]>();
@@ -8,7 +8,9 @@ import { AgentBridge } from "./AgentBridge.d.ts";
8
8
  *
9
9
  * Per zone view: a zone's `readState` reaches the keys its own subtree subscribes, and its `readScreen` and
10
10
  * `highlight` reach into its own `data-agent-zone` container rather than the whole document. A page can shadow any
11
- * of them by registering a hook tool of the same name — hook entries win over a source's.
11
+ * of them by registering a hook tool of the same name — hook entries win over a source's. **That is a root-scope
12
+ * move only**: inside a zone the hook entry is registered under its scope-prefixed name, which can never collide
13
+ * with the bare name a source publishes, so a zone drops a built-in with the session's `builtins` option instead.
12
14
  */
13
15
  export declare class StoreSurfaceSource implements SurfaceSource {
14
16
  #private;
@@ -16,6 +18,12 @@ export declare class StoreSurfaceSource implements SurfaceSource {
16
18
  static readonly highlightClass = "akan-agent-highlight";
17
19
  /** Mirrors the animation in that stylesheet: the class outlives the ring by nothing. */
18
20
  static readonly highlightMs = 2400;
21
+ /**
22
+ * What `tools()` contributes, in the order it builds them — the list a session's `builtins` option selects from.
23
+ * A screen that declares a hook tool of one of these names is not in it: that entry is the screen's, not this
24
+ * source's, so withholding the built-ins never withholds a tool a component published on purpose.
25
+ */
26
+ static readonly builtins: readonly ["navigate", "goBack", "readScreen", "readState", "highlight"];
19
27
  /** Lazy by default: `AgentBridge.of()` walks the whole store, so it waits for the first enumeration. */
20
28
  constructor(bridge?: AgentBridge);
21
29
  tools: (view?: string[]) => ToolEntry[];
@@ -1,7 +1,8 @@
1
1
  import { type ReactNode } from "react";
2
- import { type AgentRunner, type CompactOptions } from "../../vendor/use-agentic.d.ts";
2
+ import { type AgentRunner, type AgentSessionOptions, type CompactOptions, type SessionHistory } from "../../vendor/use-agentic.d.ts";
3
3
  import type { AttachReader } from "./attachment.d.ts";
4
4
  import type { PersistOption } from "./sessionHistory.d.ts";
5
+ import type { BuiltinOption } from "./sessionView.d.ts";
5
6
  import type { VoiceEngine } from "./voice.d.ts";
6
7
  export interface ChatProps {
7
8
  /** Reaches whichever surface is showing — the launcher while closed, the panel while open. */
@@ -17,6 +18,13 @@ export interface ChatProps {
17
18
  * messages left verbatim below the summary. Tune it per provider; `{ at: 0 }` turns it off.
18
19
  */
19
20
  compact?: CompactOptions;
21
+ /**
22
+ * Which of the runtime's own tools this chat's agent gets — all of them by default, `false` none, an array
23
+ * exactly the ones it names. A chat that must not leave the screen it is on drops `navigate` and `goBack`.
24
+ */
25
+ builtins?: BuiltinOption;
26
+ /** Called after a compaction replaced messages with one summary — where a host syncs its own watermark. */
27
+ onCompact?: AgentSessionOptions["onCompact"];
20
28
  defaultOpen?: boolean;
21
29
  /**
22
30
  * Controlled open state. Pass it with `onOpenChange` to drive the panel from the app's own control — a header
@@ -26,8 +34,14 @@ export interface ChatProps {
26
34
  onOpenChange?: (open: boolean) => void;
27
35
  /** `false` draws no launcher, for an app that opens the panel from a control of its own. */
28
36
  launcher?: boolean;
29
- /** Keeps the transcript across reloads — sessionStorage by default, `{ storage: "local" }` to outlive the tab. */
30
- persist?: PersistOption;
37
+ /**
38
+ * Keeps the transcript across reloads — sessionStorage by default, `{ storage: "local" }` to outlive the tab, or
39
+ * a `SessionHistory` of the app's own to keep it anywhere else, a server included.
40
+ *
41
+ * Ignored, like every session option above it, when an enclosing `Agent.Zone` or `AgentProvider` already holds a
42
+ * session: this chat then binds to that one, and the options belong to whoever built it.
43
+ */
44
+ persist?: PersistOption | SessionHistory;
31
45
  /** Renders in the page flow instead of floating above it — a zone chat that lives inside its own section. */
32
46
  inline?: boolean;
33
47
  /** `false` gives the browser its own Cmd/Ctrl+L back, for an app whose shell already spends that chord. */
@@ -39,6 +53,13 @@ export interface ChatProps {
39
53
  intro?: ReactNode;
40
54
  /** Extra header controls, left of the built-in clear and close buttons. */
41
55
  header?: ReactNode;
56
+ /**
57
+ * `false` draws no header bar at all — for an `inline` chat inside a panel the app already titles. The extra
58
+ * `header` controls go with it, and the clear action stays reachable as the `/new` command.
59
+ */
60
+ chrome?: boolean;
61
+ /** The composer's opening text, read once at mount — where a `?prompt=` lands without sending it. */
62
+ defaultDraft?: string;
42
63
  /**
43
64
  * Reads a file the user attached into an attachment, or answers `null` to leave it to the built-in reader
44
65
  * (images as bytes, text as text). This is where an app puts what needs a parser — a PDF's text, a spreadsheet's
@@ -60,6 +81,6 @@ export interface ChatProps {
60
81
  * `persist` keeps it. An enclosing AgentProvider's session wins, which is how an app isolates a surface or swaps
61
82
  * the loop while keeping this UI.
62
83
  */
63
- export declare const DefaultChat: ({ className, title, instructions, runner, maxTurns, compact, defaultOpen, open: openProp, onOpenChange, launcher, persist, inline, shortcut, launcherClassName, panelClassName, intro, header, attach, voice, }: ChatProps) => ReactNode;
84
+ export declare const DefaultChat: ({ className, title, instructions, runner, maxTurns, compact, builtins, onCompact, defaultOpen, open: openProp, onOpenChange, launcher, persist, inline, shortcut, launcherClassName, panelClassName, intro, header, chrome, defaultDraft, attach, voice, }: ChatProps) => ReactNode;
64
85
  declare const _default: import("react").ComponentType<ChatProps>;
65
86
  export default _default;
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- import { type AgentRunner, type CompactOptions } from "../../vendor/use-agentic.d.ts";
2
+ import { type AgentRunner, type AgentSession, type AgentSessionOptions, type CompactOptions, type SessionHistory } from "../../vendor/use-agentic.d.ts";
3
3
  import type { PersistOption } from "./sessionHistory.d.ts";
4
+ import type { BuiltinOption } from "./sessionView.d.ts";
4
5
  export interface ZoneProps {
5
6
  className?: string;
6
7
  /** Names the zone; the scope id and the `data-agent-zone` container both derive from it. */
@@ -12,8 +13,26 @@ export interface ZoneProps {
12
13
  maxTurns?: number;
13
14
  /** When this zone's conversation summarizes itself — same contract as the chat's own `compact`. */
14
15
  compact?: CompactOptions;
15
- /** Keeps this zone's transcript across reloads, keyed by the zone's scope path. */
16
- persist?: PersistOption;
16
+ /**
17
+ * Which of the runtime's own tools this zone's agent gets — all of them by default, `false` none, an array
18
+ * exactly the ones it names. `builtins={["readScreen", "readState"]}` is how a zone that must not leave the
19
+ * screen stops being able to: the tools are withheld, not discouraged, so a prompt cannot talk the model past it.
20
+ */
21
+ builtins?: BuiltinOption;
22
+ /**
23
+ * Keeps this zone's transcript across reloads, keyed by the zone's scope path — web storage by default, or a
24
+ * `SessionHistory` of the app's own to keep it anywhere else, a server included.
25
+ */
26
+ persist?: PersistOption | SessionHistory;
27
+ /** Called after a compaction replaced messages with one summary — where a host syncs its own watermark. */
28
+ onCompact?: AgentSessionOptions["onCompact"];
29
+ /**
30
+ * Runs this zone on a session the app built instead of one of its own, and the app then owns it: unmounting the
31
+ * zone leaves it running. Read once at mount, like every other session option here.
32
+ */
33
+ session?: AgentSession;
34
+ /** Hands the session out once it exists, for a page or store that wants to send into it or watch it. */
35
+ onSession?: (session: AgentSession) => void;
17
36
  children: ReactNode;
18
37
  }
19
38
  /**
@@ -22,4 +41,4 @@ export interface ZoneProps {
22
41
  * zones are views, never walls. An `Agent.Chat` mounted inside binds to this session automatically, so two zones
23
42
  * on one screen run two conversations in parallel, each seeing only its own subtree.
24
43
  */
25
- export declare const Zone: ({ className, id, label, instructions, runner, maxTurns, compact, persist, children, }: ZoneProps) => import("react/jsx-runtime").JSX.Element;
44
+ export declare const Zone: ({ className, id, label, instructions, runner, maxTurns, compact, builtins, persist, onCompact, session: provided, onSession, children, }: ZoneProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,6 @@
1
- import { type AgentRunner, AgentSession, type CompactOptions } from "../../vendor/use-agentic.d.ts";
1
+ import { type AgentRunner, AgentSession, type AgentSessionOptions, type CompactOptions, type SessionHistory } from "../../vendor/use-agentic.d.ts";
2
2
  import { type PersistOption } from "./sessionHistory.d.ts";
3
+ import { type BuiltinOption } from "./sessionView.d.ts";
3
4
  export interface AgentSessionSetup {
4
5
  /** Read per call rather than captured, so text the session builds follows a language switched mid-conversation. */
5
6
  l: (key: string) => string;
@@ -9,10 +10,18 @@ export interface AgentSessionSetup {
9
10
  instructions?: string;
10
11
  maxTurns?: number;
11
12
  compact?: CompactOptions;
12
- persist?: PersistOption;
13
+ /**
14
+ * Which of the runtime's own tools this session gets: all of them by default, none with `false`, exactly the
15
+ * ones an array names. A zone whose conversation must stay on one screen takes `navigate` and `goBack` off it.
16
+ */
17
+ builtins?: BuiltinOption;
18
+ /** Web storage by default; a `SessionHistory` puts the transcript wherever the app keeps it, including a server. */
19
+ persist?: PersistOption | SessionHistory;
20
+ /** Called after a compaction replaced messages with one summary — where a host syncs its own watermark. */
21
+ onCompact?: AgentSessionOptions["onCompact"];
13
22
  }
14
23
  /**
15
24
  * The one place a chat session is wired to the akan runtime. Chat and Zone both build one, and building it twice
16
25
  * is how a zone came to be the only surface with no `compact` option — an option added on one side of a copy.
17
26
  */
18
- export declare const agentSessionOf: ({ l, view, runner, instructions, maxTurns, compact, persist, }: AgentSessionSetup) => AgentSession;
27
+ export declare const agentSessionOf: ({ l, view, runner, instructions, maxTurns, compact, builtins, persist, onCompact, }: AgentSessionSetup) => AgentSession;
@@ -14,5 +14,5 @@ export declare const Agent: {
14
14
  StateKey: typeof StateKey;
15
15
  Tool: typeof Tool;
16
16
  Transcript: typeof Transcript;
17
- Zone: ({ className, id, label, instructions, runner, maxTurns, compact, persist, children, }: import("./Zone.d.ts").ZoneProps) => import("react/jsx-runtime").JSX.Element;
17
+ Zone: ({ className, id, label, instructions, runner, maxTurns, compact, builtins, persist, onCompact, session: provided, onSession, children, }: import("./Zone.d.ts").ZoneProps) => import("react/jsx-runtime").JSX.Element;
18
18
  };
@@ -11,4 +11,4 @@ export type PersistOption = boolean | {
11
11
  * why the cap is applied *before* the pairing repair: the window it keeps can start between a tool call and the
12
12
  * result answering it, and a transcript restored in that state is refused by the provider on its first turn.
13
13
  */
14
- export declare const sessionHistoryOf: (persist: PersistOption | undefined, pathKey?: string) => SessionHistory | undefined;
14
+ export declare const sessionHistoryOf: (persist: PersistOption | SessionHistory | undefined, pathKey?: string) => SessionHistory | undefined;
@@ -0,0 +1,15 @@
1
+ import { StoreSurfaceSource } from "akanjs/store";
2
+ import type { AgenticSurface, SurfaceView } from "../../vendor/use-agentic.d.ts";
3
+ /** One of the tools the akan runtime contributes to every screen, whatever that screen declares. */
4
+ export type AgentBuiltin = (typeof StoreSurfaceSource.builtins)[number];
5
+ /** `true` (the default) takes all of them, `false` none, an array exactly the ones it names. */
6
+ export type BuiltinOption = boolean | AgentBuiltin[];
7
+ /**
8
+ * The half of the surface one session reads: scoped to its zone, and narrowed to the built-ins it was given.
9
+ *
10
+ * Narrowing happens here rather than on the source because the source is shared — two sessions read one registry,
11
+ * and a zone that must not navigate away cannot take `navigate` off the screen for the root agent too. A withheld
12
+ * tool is withheld from `call` as well as from the listing, answering the same "unknown tool" a name that was
13
+ * never registered gets: a tool the model can still reach by guessing its name is not withheld.
14
+ */
15
+ export declare const sessionView: (surface: AgenticSurface, path: string[], builtins?: BuiltinOption) => SurfaceView;
@@ -1,4 +1,15 @@
1
1
  import { type Dayjs } from "akanjs/base";
2
+ export type RecentTimeRelativeUnit = "second" | "minute" | "hour" | "day" | "week" | "month" | "year";
3
+ export type RecentTimeRelativeStyle = "fromNow" | "always" | "auto";
4
+ export interface RecentTimeRelative {
5
+ unit: RecentTimeRelativeUnit;
6
+ /** Signed count in `unit`. Negative is past, positive is future, `0` is now. */
7
+ count: number;
8
+ date: Dayjs;
9
+ now: Dayjs;
10
+ defaultLabel: string;
11
+ }
12
+ export type RecentTimeRelativeFormat = RecentTimeRelativeStyle | ((relative: RecentTimeRelative) => string);
2
13
  export interface RecentTimeProps {
3
14
  /** Date value to render. Null renders nothing, and epoch placeholder values render --:--. */
4
15
  date: Date | Dayjs | null;
@@ -6,7 +17,13 @@ export interface RecentTimeProps {
6
17
  breakUnit?: Intl.RelativeTimeFormatUnit;
7
18
  /** Use compact automatic formatting or always include date and time. */
8
19
  format?: "full" | "auto";
20
+ /**
21
+ * Relative phrasing. `"fromNow"` (default) keeps dayjs locale strings (`하루 전`).
22
+ * `"always"` / `"auto"` use `Intl.RelativeTimeFormat` — `1일 전` vs `어제`.
23
+ * A function replaces the relative label; return `defaultLabel` to keep the default.
24
+ */
25
+ relative?: RecentTimeRelativeFormat;
9
26
  /** Additional classes for the trigger span. */
10
27
  className?: string;
11
28
  }
12
- export declare const RecentTime: ({ date, breakUnit, format, className }: RecentTimeProps) => import("react/jsx-runtime").JSX.Element | null;
29
+ export declare const RecentTime: ({ date, breakUnit, format, relative, className }: RecentTimeProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,17 +1,19 @@
1
- export { SessionContext, useAgent } from "../vendor/use-agentic.d.ts";
1
+ export { AgentProvider, type AgentProviderProps, type AgentRunner, AgentSession, type AgentSessionOptions, type ChatMessage, type CompactOptions, type ContextBlock, httpRunner, type PublishedTool, type RunnerEvent, type RunnerRequest, SessionContext, type SessionHistory, type SurfaceView, useAgent, } from "../vendor/use-agentic.d.ts";
2
2
  export { Agent } from "./Agent.d.ts";
3
3
  export { type ApprovalProps, DefaultApproval } from "./Agent/Approval.d.ts";
4
- export { agentSessionOf } from "./Agent/agentSessionOf.d.ts";
4
+ export { type AgentSessionSetup, agentSessionOf } from "./Agent/agentSessionOf.d.ts";
5
5
  export { type AttachReader, maxAttachmentBytes } from "./Agent/attachment.d.ts";
6
6
  export { type BubbleProps, DefaultBubble } from "./Agent/Bubble.d.ts";
7
7
  export type { ChatProps } from "./Agent/Chat.d.ts";
8
8
  export { type ChatCommand, ChatCommands } from "./Agent/ChatCommands.d.ts";
9
9
  export { type ComposerProps, DefaultComposer } from "./Agent/Composer.d.ts";
10
+ export { fetchRunner } from "./Agent/fetchRunner.d.ts";
10
11
  export { DefaultLauncher, type LauncherProps } from "./Agent/Launcher.d.ts";
11
12
  export { type CodeProps, DefaultCode, DefaultMarkdown, type MarkdownProps } from "./Agent/Markdown.d.ts";
12
13
  export { DefaultMenu, type MenuProps as AgentMenuProps, type MenuRow } from "./Agent/Menu.d.ts";
13
14
  export { DefaultQuestion, type QuestionProps } from "./Agent/Question.d.ts";
14
15
  export type { PersistOption } from "./Agent/sessionHistory.d.ts";
16
+ export type { AgentBuiltin, BuiltinOption } from "./Agent/sessionView.d.ts";
15
17
  export { tokenCount } from "./Agent/tokenCount.d.ts";
16
18
  export type { VoiceEngine, VoiceHandlers, VoiceListener, VoiceSpeech } from "./Agent/voice.d.ts";
17
19
  export { agentAttrs } from "./agentAttrs.d.ts";
@@ -52,7 +54,7 @@ export { Pagination } from "./Pagination.d.ts";
52
54
  export { Popconfirm } from "./Popconfirm.d.ts";
53
55
  export { Portal } from "./Portal.d.ts";
54
56
  export { Radio } from "./Radio.d.ts";
55
- export { RecentTime } from "./RecentTime.d.ts";
57
+ export { RecentTime, type RecentTimeProps, type RecentTimeRelative, type RecentTimeRelativeFormat, type RecentTimeRelativeStyle, type RecentTimeRelativeUnit, } from "./RecentTime.d.ts";
56
58
  export { Refresh } from "./Refresh.d.ts";
57
59
  export { type BadgeVariants, type ButtonVariants, badgeRecipe, buttonRecipe, type InputSurfaceVariants, inputRecipe, recipe, tv, } from "./recipe.d.ts";
58
60
  export { ScreenNavigator } from "./ScreenNavigator.d.ts";
@@ -2,7 +2,7 @@ import { type ReactNode } from "react";
2
2
  import { AgenticSurface } from "./AgenticSurface.d.ts";
3
3
  import { AgentSession, type AgentSessionOptions } from "./AgentSession.d.ts";
4
4
  import type { AgentRunner } from "./types.d.ts";
5
- interface AgentProviderProps extends AgentSessionOptions {
5
+ export interface AgentProviderProps extends AgentSessionOptions {
6
6
  surface?: AgenticSurface;
7
7
  session?: AgentSession;
8
8
  runner?: AgentRunner;
@@ -15,4 +15,3 @@ interface AgentProviderProps extends AgentSessionOptions {
15
15
  * `useAgent` reads.
16
16
  */
17
17
  export declare const AgentProvider: ({ surface, session, runner, children, ...options }: AgentProviderProps) => import("react/jsx-runtime").JSX.Element;
18
- export {};
@@ -21,18 +21,31 @@ export interface PendingQuestion {
21
21
  answer: (value: string | string[]) => void;
22
22
  dismiss: (reason?: string) => void;
23
23
  }
24
- /** Where a session keeps its transcript across page loads. Storage-neutral: the host decides what backs it. */
24
+ /**
25
+ * Where a session keeps its transcript across page loads. Storage-neutral: the host decides what backs it, so a
26
+ * server-side transcript is as legal as web storage — which is why every method may answer asynchronously. A
27
+ * `load` still in flight when the user sends the first message is dropped rather than merged: the conversation
28
+ * on screen is the one they are having, and splicing a restored one under it would rewrite what they just said.
29
+ *
30
+ * Which makes **sending on mount a race the restore loses** — an opening prompt fired from an effect beats the
31
+ * fetch, and the user watches the conversation they came back to never arrive. Wait for `isRestoring` to clear,
32
+ * or write the prompt into the composer and leave the send to them.
33
+ */
25
34
  export interface SessionHistory {
26
- load(): ChatMessage[] | null;
27
- save(messages: readonly ChatMessage[]): void;
28
- clear(): void;
35
+ load(): ChatMessage[] | null | Promise<ChatMessage[] | null>;
36
+ save(messages: readonly ChatMessage[]): void | Promise<void>;
37
+ clear(): void | Promise<void>;
29
38
  }
30
39
  export interface AgentSessionOptions {
31
40
  instructions?: string;
32
41
  buildContext?: (surface: SurfaceView) => ContextBlock[];
33
42
  /** Assistant turns per send before the session asks whether to keep going, or stops. */
34
43
  maxTurns?: number;
35
- /** Restores settled messages at construction and saves after every change, debounced. Failures are silent. */
44
+ /**
45
+ * Restores settled messages at construction and saves after every change, debounced. Failures are silent, and
46
+ * saves are chained rather than issued in parallel — an async host would otherwise land them out of order and
47
+ * persist an older transcript last.
48
+ */
36
49
  history?: SessionHistory;
37
50
  /**
38
51
  * Awaited after a tool that changed something and before its change report is taken. A surface is read
@@ -54,6 +67,15 @@ export interface AgentSessionOptions {
54
67
  * few messages is replaced by one summary of itself, before the turn that would have overflowed is sent.
55
68
  */
56
69
  compact?: CompactOptions;
70
+ /**
71
+ * Fires after a compaction replaced `replaced` with `summary`. A host that keeps its own server-side summary
72
+ * uses it to move its watermark to the same cut, so the two do not summarize the same messages twice.
73
+ *
74
+ * It matters most to a host whose watermark is a *position*: compaction shrinks the transcript in place, so a
75
+ * "sync everything past index N" scheme silently stops syncing once N is past the shortened array — no
76
+ * duplicate, no error, just messages that never reach the server. Reset the mark here.
77
+ */
78
+ onCompact?: (replaced: readonly ChatMessage[], summary: ChatMessage) => void;
57
79
  }
58
80
  /**
59
81
  * The client-side conversation loop: send → model turn → tool calls → approval gate → execute → report diffs → next
@@ -73,6 +95,11 @@ export declare class AgentSession {
73
95
  */
74
96
  static readonly askUserTool: PublishedTool;
75
97
  constructor(surface: SurfaceView, runner: AgentRunner, options?: AgentSessionOptions);
98
+ /**
99
+ * True while an async `history.load()` is still in flight — the transcript is empty but not yet known to be.
100
+ * A host that opens with a prompt of its own waits for this before sending, or the restore loses the race.
101
+ */
102
+ get isRestoring(): boolean;
76
103
  get surface(): SurfaceView;
77
104
  get messages(): readonly ChatMessage[];
78
105
  get isRunning(): boolean;
@@ -97,7 +124,7 @@ export declare class AgentSession {
97
124
  /**
98
125
  * Empties the transcript and the persisted history, ending a turn that is still running. Awaiting the abort
99
126
  * matters: the loop settles in its own `finally`, a microtask later, and a transcript emptied before that lands
100
- * is one the winding-down turn appends onto.
127
+ * is one the winding-down turn appends onto. The returned promise waits for the history to clear too.
101
128
  */
102
129
  reset: () => Promise<void>;
103
130
  /**
@@ -39,6 +39,11 @@ export declare class AgenticSurface {
39
39
  */
40
40
  view(path: string[]): SurfaceView;
41
41
  tool(name: string, view?: string[]): ToolEntry | null;
42
+ /**
43
+ * Whether a *mounted component* declares this name — a source's contribution answers `false`. The distinction is
44
+ * what lets a host drop the built-in tools without dropping a screen's own tool that deliberately shadows one.
45
+ */
46
+ declares(name: string, view?: string[]): boolean;
42
47
  /** Every call made through this surface, oldest first. What the dock shows the user to check against the screen. */
43
48
  get transcript(): readonly AgentCall[];
44
49
  call(name: string, args?: Record<string, unknown>, view?: string[]): Promise<unknown>;