@fungi.computer/parakeet 0.5.0 → 0.6.1

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.
Files changed (45) hide show
  1. package/README.md +17 -1
  2. package/dist/components/ChatComposer.js +66 -60
  3. package/dist/components/CodeBlock.js +3 -2
  4. package/dist/components/ComposerModelKey.d.ts +11 -0
  5. package/dist/components/ComposerModelKey.js +9 -0
  6. package/dist/components/ComposerShell.d.ts +47 -0
  7. package/dist/components/ComposerShell.js +29 -0
  8. package/dist/components/DecisionComposer.js +1 -2
  9. package/dist/components/TimelineItem.js +1 -1
  10. package/dist/components/ToolGroup.js +2 -2
  11. package/dist/conversation-opening.d.ts +1 -1
  12. package/dist/conversation-opening.js +2 -2
  13. package/dist/conversation.js +12 -9
  14. package/dist/core/build-timeline.js +5 -2
  15. package/dist/core/composer-model-name.d.ts +3 -0
  16. package/dist/core/composer-model-name.js +21 -0
  17. package/dist/core/display-text.d.ts +2 -0
  18. package/dist/core/display-text.js +7 -0
  19. package/dist/core/draft-storage.d.ts +2 -0
  20. package/dist/core/draft-storage.js +14 -1
  21. package/dist/core/session-projection.d.ts +1 -0
  22. package/dist/core/session-projection.js +3 -2
  23. package/dist/core/session-view-state.d.ts +2 -4
  24. package/dist/core/session-view-state.js +33 -13
  25. package/dist/core/tool-groups.d.ts +3 -0
  26. package/dist/core/tool-groups.js +37 -1
  27. package/dist/core/tool-view.d.ts +6 -0
  28. package/dist/core/tool-view.js +33 -0
  29. package/dist/core/use-timeline-item-key.d.ts +3 -4
  30. package/dist/core/use-timeline-item-key.js +8 -14
  31. package/dist/headless.d.ts +4 -3
  32. package/dist/headless.js +4 -3
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +3 -0
  35. package/dist/parakeet.js +9 -11
  36. package/dist/store/types.d.ts +9 -0
  37. package/dist/styles.css +581 -114
  38. package/docs/api/headless.md +124 -41
  39. package/docs/api/index.md +153 -1
  40. package/package.json +11 -8
  41. package/src/completion-menu.css +2 -1
  42. package/src/composer-shell.css +125 -0
  43. package/src/styles.css +5 -87
  44. package/dist/components/WaitingNotice.d.ts +0 -7
  45. package/dist/components/WaitingNotice.js +0 -8
@@ -8,4 +8,5 @@ export declare function mergeTerminals(current: readonly SessionTerminal[], inco
8
8
  export declare function mergeCompactions(current: readonly SessionCompactionMarker[], incoming: readonly SessionCompactionMarker[]): readonly SessionCompactionMarker[];
9
9
  /** Accumulate compaction notices by identity; a frame never removes one. */
10
10
  export declare function mergeCompactionNotices(current: readonly SessionCompactionNotice[], incoming: readonly SessionCompactionNotice[]): readonly SessionCompactionNotice[];
11
+ /** Apply one already-decoded Shiitake frame to its authoritative projection. */
11
12
  export declare function applySessionFrame(current: SessionSnapshot, frame: SessionFrame): SessionSnapshot;
@@ -1,3 +1,4 @@
1
+ import { appendSessionProgress } from "@fungi.computer/shiitake/client";
1
2
  import { sameRunningWork } from "./session-view-state.js";
2
3
  const durableMessageIds = new WeakMap();
3
4
  /** Derive completed durable message identity once per immutable history revision. */
@@ -103,11 +104,11 @@ function sameActivity(left, right) {
103
104
  right.compaction !== null &&
104
105
  left.compaction.trigger === right.compaction.trigger))));
105
106
  }
106
- /** Apply one already-decoded Shiitake frame to its authoritative projection. */
107
107
  /** Extension slots are small JSON values; compare them by content. */
108
108
  function sameExtensionSlots(next, current) {
109
109
  return next === current || JSON.stringify(next) === JSON.stringify(current);
110
110
  }
111
+ /** Apply one already-decoded Shiitake frame to its authoritative projection. */
111
112
  export function applySessionFrame(current, frame) {
112
113
  if (frame.kind === "reset")
113
114
  return {
@@ -149,7 +150,7 @@ export function applySessionFrame(current, frame) {
149
150
  return activity === current.activity ? current : { ...current, activity };
150
151
  return {
151
152
  ...current,
152
- progress: frame.progress,
153
+ progress: appendSessionProgress(current.progress, frame.progress),
153
154
  activity,
154
155
  };
155
156
  }
@@ -56,10 +56,8 @@ export type SessionActivityStatus = "cancelling" | "idle" | "queued" | "retrying
56
56
  /** Decide a Session's view state from its activity, terminals and progress. */
57
57
  export declare function sessionViewState(facts: SessionViewFacts): SessionViewState;
58
58
  export declare function sameRunningWork(left: AgentRunningWork | null, right: AgentRunningWork | null): boolean;
59
- /** Explain a queued Session using its owner's executor fact. */
60
- export declare function queuedSessionLabel(state: Extract<SessionViewState, {
61
- kind: "queued";
62
- }>): string;
59
+ /** Activity only: queued messages belong in the transcript, never in this label. */
60
+ export declare function sessionStateLabel(state: SessionViewState, waitingOnYou?: boolean): "thinking" | "streaming" | "running a tool" | "idle" | "waiting on you" | "cancelling" | "failed";
63
61
  /** Whether two view states say the same thing, so a readout can stay stable. */
64
62
  export declare function sameSessionViewState(left: SessionViewState, right: SessionViewState): boolean;
65
63
  /** Coarse activity for a view state: what the composer gates on. */
@@ -66,20 +66,40 @@ export function sessionViewState(facts) {
66
66
  export function sameRunningWork(left, right) {
67
67
  if (left === null || right === null)
68
68
  return left === right;
69
- if (left.kind === "session" && right.kind === "session")
70
- return (left.sessionId === right.sessionId && left.commandId === right.commandId);
71
- if (left.kind === "host" && right.kind === "host")
72
- return left.lane === right.lane && left.jobId === right.jobId;
73
- return false;
69
+ return left.phase === right.phase;
74
70
  }
75
- /** Explain a queued Session using its owner's executor fact. */
76
- export function queuedSessionLabel(state) {
77
- const work = state.runningWork;
78
- if (work === null)
79
- return "Queued for host";
80
- return work.kind === "session"
81
- ? `Waiting for Session ${collapsed(work.sessionId, 256)}`
82
- : `Waiting for host job ${collapsed(work.lane, 256)} · ${collapsed(work.jobId, 256)}`;
71
+ const occupancyLabels = {
72
+ thinking: "thinking",
73
+ streaming: "streaming",
74
+ running_tool: "running a tool",
75
+ waiting_on_you: "waiting on you",
76
+ cancelling: "cancelling",
77
+ };
78
+ /** Activity only: queued messages belong in the transcript, never in this label. */
79
+ export function sessionStateLabel(state, waitingOnYou = false) {
80
+ switch (state.kind) {
81
+ case "queued":
82
+ return state.runningWork === null
83
+ ? "idle"
84
+ : occupancyLabels[state.runningWork.phase];
85
+ case "thinking":
86
+ case "compacting":
87
+ case "retrying":
88
+ return "thinking";
89
+ case "cancelling":
90
+ return "cancelling";
91
+ case "responding":
92
+ return "streaming";
93
+ case "running_tool":
94
+ return "running a tool";
95
+ case "error":
96
+ return "failed";
97
+ case "idle":
98
+ case "waiting_for_you":
99
+ // A completed turn is idle; an actual attention/approval request is
100
+ // what makes the caller show "waiting on you".
101
+ return waitingOnYou ? "waiting on you" : "idle";
102
+ }
83
103
  }
84
104
  /** Whether two view states say the same thing, so a readout can stay stable. */
85
105
  export function sameSessionViewState(left, right) {
@@ -1,5 +1,8 @@
1
1
  import type { ProjectedItem, TimelineItem, ToolGroupItem, ToolItem } from "../store/types.js";
2
2
  import { type ToolCallView } from "./tool-view.js";
3
+ import type { SessionToolOutput } from "@fungi.computer/shiitake/client";
4
+ /** Overlay only running calls; settled rows and their lazy result views stay shared. */
5
+ export declare function overlayToolOutput(items: readonly TimelineItem[], output: readonly SessionToolOutput[]): readonly TimelineItem[];
3
6
  /** Build one group from a non-empty run of consecutive calls. */
4
7
  export declare function toolGroup(tools: readonly [ToolItem, ...ToolItem[]]): ToolGroupItem;
5
8
  /**
@@ -1,5 +1,41 @@
1
1
  import { assistantMessageVisible } from "./assistant-visibility.js";
2
- import { toolCallView } from "./tool-view.js";
2
+ import { liveOutputLines, toolCallView, } from "./tool-view.js";
3
+ /** Overlay only running calls; settled rows and their lazy result views stay shared. */
4
+ export function overlayToolOutput(items, output) {
5
+ if (output.length === 0)
6
+ return items;
7
+ return items.map((item) => {
8
+ if (item.kind !== "tool_group")
9
+ return item;
10
+ const tools = item.tools.map((tool) => {
11
+ const slot = output.find((candidate) => candidate.toolCallId === tool.id);
12
+ if (tool.status !== "running" || slot === undefined)
13
+ return tool;
14
+ return {
15
+ ...tool,
16
+ live: {
17
+ lines: liveOutputLines(slot.tail),
18
+ droppedBytes: slot.droppedBytes,
19
+ revision: slot.revision,
20
+ },
21
+ };
22
+ });
23
+ if (tools.every((tool, index) => tool === item.tools[index]))
24
+ return item;
25
+ const calls = item.calls.map((call, index) => {
26
+ const tool = tools[index];
27
+ return tool === undefined || tool === item.tools[index]
28
+ ? call
29
+ : toolCallView(tool);
30
+ });
31
+ return {
32
+ ...item,
33
+ tools,
34
+ calls,
35
+ active: calls.find((call) => call.id === item.active?.id),
36
+ };
37
+ });
38
+ }
3
39
  const nouns = {
4
40
  code: ["ran", "script", "scripts"],
5
41
  command: ["ran", "command", "commands"],
@@ -173,7 +173,13 @@ export type ToolCallView = Readonly<{
173
173
  error?: string | undefined;
174
174
  /** Expanded body sections, in display order. */
175
175
  sections: readonly ToolCallSection[];
176
+ /** Replaceable output while the call runs. */
177
+ live?: ToolItem["live"];
176
178
  }>;
179
+ /** Neutral lines from a bounded live tail. */
180
+ export declare function liveOutputLines(tail: string): readonly string[];
181
+ /** Live output stays visible when folded, independent of result disclosure. */
182
+ export declare function toolLiveLines(call: ToolCallView, open: boolean): readonly string[];
177
183
  /** Items shown before "more" for excerpts, matches, and output. */
178
184
  export declare const toolPreviewLineLimit = 12;
179
185
  /** Build the renderer-neutral view of one call. */
@@ -4,6 +4,7 @@ import { formatPatch, parsePatch, structuredPatch, } from "diff";
4
4
  import { z } from "zod";
5
5
  import stripAnsi from "strip-ansi";
6
6
  import { reportFailure } from "../lib/failure.js";
7
+ import { sanitizeDisplayText } from "./display-text.js";
7
8
  /** Shared tool status meaning, used by the browser and native presentations. */
8
9
  export function toolStatusTone(status) {
9
10
  if (status === "error")
@@ -27,6 +28,37 @@ export const toolKindSigils = {
27
28
  /** Artwork for search results. */
28
29
  search: "search",
29
30
  };
31
+ /** Neutral lines from a bounded live tail. */
32
+ export function liveOutputLines(tail) {
33
+ if (tail === "")
34
+ return [];
35
+ // Replacement dominance counts decoded code points, not rendered graphemes.
36
+ const characters = Array.from(tail);
37
+ if (characters.filter((character) => character === "\ufffd").length >
38
+ characters.length / 2)
39
+ return [
40
+ `[binary output, ${String(new TextEncoder().encode(tail).byteLength)} bytes]`,
41
+ ];
42
+ return stripAnsi(tail)
43
+ .replace(/\r\n/gu, "\n")
44
+ .replace(/\n$/u, "")
45
+ .split("\n")
46
+ .map((line) => sanitizeDisplayText(line.slice(line.lastIndexOf("\r") + 1)));
47
+ }
48
+ /** Live output stays visible when folded, independent of result disclosure. */
49
+ export function toolLiveLines(call, open) {
50
+ const live = call.live;
51
+ if (live === undefined)
52
+ return [];
53
+ if (!open)
54
+ return live.lines.slice(-6);
55
+ return [
56
+ ...(live.droppedBytes === 0
57
+ ? []
58
+ : [`… ${String(Math.ceil(live.droppedBytes / 1024))} KiB earlier`]),
59
+ ...live.lines,
60
+ ];
61
+ }
30
62
  /** Items shown before "more" for excerpts, matches, and output. */
31
63
  export const toolPreviewLineLimit = 12;
32
64
  /** Diff lines shown before "more". */
@@ -600,6 +632,7 @@ export function toolCallView(item) {
600
632
  kind,
601
633
  label: target.label,
602
634
  name: item.name,
635
+ live: item.status === "running" ? item.live : undefined,
603
636
  paths: target.paths,
604
637
  get sections() {
605
638
  return getDetails().sections;
@@ -1,8 +1,7 @@
1
1
  import type { TimelineItem } from "../store/types.js";
2
2
  /**
3
- * The virtualizer's row-key callback. Text-only progress replaces tail items
4
- * but not their keys, so the callback keeps its identity, and the virtualizer
5
- * its measurements, until the settled history or the order or identity of the
6
- * tail rows changes. Settled rows are keyed from the settled list alone.
3
+ * The virtualizer's row-key callback. Progress replaces row payloads, including
4
+ * live overlays on settled calls, but not row keys. Keep the callback and its
5
+ * measurements until the order or identity of the combined rows changes.
7
6
  */
8
7
  export declare function useTimelineItemKey(settled: readonly TimelineItem[], tail: readonly TimelineItem[]): (index: number) => string | number;
@@ -9,21 +9,15 @@ function sameKeys(a, b) {
9
9
  return a.length === b.length && a.every((key, index) => key === b[index]);
10
10
  }
11
11
  /**
12
- * The virtualizer's row-key callback. Text-only progress replaces tail items
13
- * but not their keys, so the callback keeps its identity, and the virtualizer
14
- * its measurements, until the settled history or the order or identity of the
15
- * tail rows changes. Settled rows are keyed from the settled list alone.
12
+ * The virtualizer's row-key callback. Progress replaces row payloads, including
13
+ * live overlays on settled calls, but not row keys. Keep the callback and its
14
+ * measurements until the order or identity of the combined rows changes.
16
15
  */
17
16
  export function useTimelineItemKey(settled, tail) {
18
- const next = tail.map(rowKey);
17
+ const next = [...settled, ...tail].map(rowKey);
19
18
  const [previous, setPrevious] = useState(next);
20
- const tailKeys = sameKeys(previous, next) ? previous : next;
21
- if (tailKeys !== previous)
22
- setPrevious(tailKeys);
23
- return useCallback((index) => {
24
- const item = settled[index];
25
- if (item !== undefined)
26
- return rowKey(item);
27
- return tailKeys[index - settled.length] ?? index;
28
- }, [settled, tailKeys]);
19
+ const keys = sameKeys(previous, next) ? previous : next;
20
+ if (keys !== previous)
21
+ setPrevious(keys);
22
+ return useCallback((index) => keys[index] ?? index, [keys]);
29
23
  }
@@ -1,12 +1,13 @@
1
1
  export { formatModelIdentity, formatModelWithLevel, } from "./core/model-identity.js";
2
2
  export type { QueuedRecallState, QueuedRecallOutcome, } from "./core/queued-recall.js";
3
3
  export type { ParakeetDraftIdentity, ParakeetDraftStorage, } from "./core/draft-storage.js";
4
+ export { sanitizeDisplayText } from "./core/display-text.js";
4
5
  export { formatContextUsage, formatTokenCount } from "./core/format-context.js";
5
6
  export { formatWhen, oneLine } from "./core/picker-text.js";
6
7
  export { providerChoices } from "./provider-choices.js";
7
8
  export { ParakeetProvider, useComposer, useClientContributions, useParakeetInteractions, useExtensionSlots, useParakeetSession, useLastSessionReplyAt, useStatus, useTimeline, type ComposerController, type ParakeetDraftCheckpoint, type ParakeetModel, type ParakeetObservation, type ParakeetReconnectOutcome, type ParakeetStatus, type ParakeetSessionViewState, type ParakeetUsage, type StatusController, type TimelineController, } from "./conversation.js";
8
- export { sessionViewState, queuedSessionLabel, sessionActivityStatus, type SessionActivityStatus, type SessionViewFacts, type SessionViewState as ParakeetOwnerViewState, } from "./core/session-view-state.js";
9
- export { mergeTerminals } from "./core/session-projection.js";
9
+ export { sessionViewState, sessionStateLabel, sessionActivityStatus, type SessionActivityStatus, type SessionViewFacts, type SessionViewState as ParakeetOwnerViewState, } from "./core/session-view-state.js";
10
+ export { applySessionFrame, mergeTerminals, } from "./core/session-projection.js";
10
11
  export { type SessionActivity, seedSessionQuery, sessionQueryOptions, subscribeSessionActivity, } from "./core/session-query.js";
11
12
  export { accountCommandDefinitions, contributeAccountCommands, loginRegistry, type AccountCommandOptions, type AccountOwner, type ProviderAccess, type ModelAccountContext, type LoginHost, type LoginMethod, type LoginMethodKind, type LoginPolicy, type LoginProvider, type LoginScope, } from "./account-commands.js";
12
13
  export { contributeModelCommands, sessionModelCommandDefinitions, type SessionModelChoice, type SessionModelCommandOptions, } from "./model-commands.js";
@@ -23,7 +24,7 @@ export type { ParakeetSession, ParakeetSessionCommand, ParakeetPromptResult, } f
23
24
  export type { AssistantMessageItem as ParakeetAssistantMessageItem, TimelineItem as ParakeetTimelineItem, ToolGroupItem as ParakeetToolGroupItem, ToolItem as ParakeetToolItem, UserMessageItem as ParakeetUserMessageItem, } from "./store/types.js";
24
25
  export type { ToolArguments as ParakeetToolArguments } from "./core/tool-contract.js";
25
26
  export { useMarkdown, type MarkdownBlock, type MarkdownInline, } from "./markdown.js";
26
- export { formatToolDuration, toolCallView, toolKindSigils, toolStatusTone, type ToolCallSection as ParakeetToolCallSection, type ToolCallView as ParakeetToolCallView, type ToolDiffFile as ParakeetToolDiffFile, type ToolDiffHunk as ParakeetToolDiffHunk, type ToolDiffLine as ParakeetToolDiffLine, type ToolDiffRow as ParakeetToolDiffRow, type ToolExcerptLine as ParakeetToolExcerptLine, type ToolKind as ParakeetToolKind, type ToolMatch as ParakeetToolMatch, type ToolOutputLine as ParakeetToolOutputLine, } from "./core/tool-view.js";
27
+ export { formatToolDuration, toolCallView, toolKindSigils, toolLiveLines, toolStatusTone, type ToolCallSection as ParakeetToolCallSection, type ToolCallView as ParakeetToolCallView, type ToolDiffFile as ParakeetToolDiffFile, type ToolDiffHunk as ParakeetToolDiffHunk, type ToolDiffLine as ParakeetToolDiffLine, type ToolDiffRow as ParakeetToolDiffRow, type ToolExcerptLine as ParakeetToolExcerptLine, type ToolKind as ParakeetToolKind, type ToolMatch as ParakeetToolMatch, type ToolOutputLine as ParakeetToolOutputLine, } from "./core/tool-view.js";
27
28
  export { assistantMessageVisible } from "./core/assistant-visibility.js";
28
29
  export { disclosureOpen, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, type ToolDisclosureState, } from "./core/tool-groups.js";
29
30
  export { useClientActions } from "./client-contributions.js";
package/dist/headless.js CHANGED
@@ -1,10 +1,11 @@
1
1
  export { formatModelIdentity, formatModelWithLevel, } from "./core/model-identity.js";
2
+ export { sanitizeDisplayText } from "./core/display-text.js";
2
3
  export { formatContextUsage, formatTokenCount } from "./core/format-context.js";
3
4
  export { formatWhen, oneLine } from "./core/picker-text.js";
4
5
  export { providerChoices } from "./provider-choices.js";
5
6
  export { ParakeetProvider, useComposer, useClientContributions, useParakeetInteractions, useExtensionSlots, useParakeetSession, useLastSessionReplyAt, useStatus, useTimeline, } from "./conversation.js";
6
- export { sessionViewState, queuedSessionLabel, sessionActivityStatus, } from "./core/session-view-state.js";
7
- export { mergeTerminals } from "./core/session-projection.js";
7
+ export { sessionViewState, sessionStateLabel, sessionActivityStatus, } from "./core/session-view-state.js";
8
+ export { applySessionFrame, mergeTerminals, } from "./core/session-projection.js";
8
9
  export { seedSessionQuery, sessionQueryOptions, subscribeSessionActivity, } from "./core/session-query.js";
9
10
  export { accountCommandDefinitions, contributeAccountCommands, loginRegistry, } from "./account-commands.js";
10
11
  export { contributeModelCommands, sessionModelCommandDefinitions, } from "./model-commands.js";
@@ -18,7 +19,7 @@ export { parakeetBuiltinCommands, } from "./commands/builtins.js";
18
19
  export { advertisedCommands, } from "./commands/advertised.js";
19
20
  export { useSlashCommands, } from "./commands/use-slash-commands.js";
20
21
  export { useMarkdown, } from "./markdown.js";
21
- export { formatToolDuration, toolCallView, toolKindSigils, toolStatusTone, } from "./core/tool-view.js";
22
+ export { formatToolDuration, toolCallView, toolKindSigils, toolLiveLines, toolStatusTone, } from "./core/tool-view.js";
22
23
  export { assistantMessageVisible } from "./core/assistant-visibility.js";
23
24
  export { disclosureOpen, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, } from "./core/tool-groups.js";
24
25
  export { useClientActions } from "./client-contributions.js";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { ParakeetProvider, useComposer, useStatus, useTimeline, useMarkdown, disclosureOpen, formatToolDuration, formatModelIdentity, formatModelWithLevel, formatContextUsage, formatTokenCount, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, } from "./headless.js";
2
2
  export { Parakeet, ParakeetView } from "./parakeet.js";
3
+ export { ComposerModelKey, type ComposerModelKeyProps, } from "./components/ComposerModelKey.js";
4
+ export { composerSendState } from "./components/ComposerShell.js";
5
+ export { ComposerShell, useComposerTextareaSize, type ComposerShellProps, type ComposerShellRow, } from "./components/ComposerShell.js";
3
6
  export type { ParakeetVisitor } from "./parakeet.js";
4
7
  export { InteractionPanel, type InteractionPanelProps, } from "./components/InteractionPanel.js";
5
8
  export type { QueuedRecallState, QueuedRecallOutcome, ParakeetAssistantMessageItem, ComposerController, ParakeetDraftCheckpoint, ParakeetDraftIdentity, ParakeetDraftStorage, ParakeetModel, ParakeetSession, ParakeetStatus, ParakeetTimelineItem, ParakeetToolArguments, ParakeetToolCallSection, ParakeetToolCallView, ParakeetToolDiffFile, ParakeetToolDiffHunk, ParakeetToolDiffLine, ParakeetToolDiffRow, ParakeetToolExcerptLine, ParakeetToolMatch, ParakeetToolOutputLine, ParakeetToolGroupItem, ParakeetToolItem, ParakeetToolKind, ToolDisclosureState, ParakeetUsage, ParakeetUserMessageItem, StatusController, TimelineController, MarkdownBlock, MarkdownInline, } from "./headless.js";
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  export { ParakeetProvider, useComposer, useStatus, useTimeline, useMarkdown, disclosureOpen, formatToolDuration, formatModelIdentity, formatModelWithLevel, formatContextUsage, formatTokenCount, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, } from "./headless.js";
2
2
  export { Parakeet, ParakeetView } from "./parakeet.js";
3
+ export { ComposerModelKey, } from "./components/ComposerModelKey.js";
4
+ export { composerSendState } from "./components/ComposerShell.js";
5
+ export { ComposerShell, useComposerTextareaSize, } from "./components/ComposerShell.js";
3
6
  export { InteractionPanel, } from "./components/InteractionPanel.js";
4
7
  export * from "./agent-head.js";
package/dist/parakeet.js CHANGED
@@ -15,7 +15,6 @@ import { ClientRows } from "./components/ClientRows.js";
15
15
  import { useClientActions } from "./client-contributions.js";
16
16
  import { placeClientTimeline } from "./core/client-timeline.js";
17
17
  import { InteractionPanel } from "./components/InteractionPanel.js";
18
- import { WaitingNotice } from "./components/WaitingNotice.js";
19
18
  import { useSlashCommands, } from "./commands/use-slash-commands.js";
20
19
  import { TimelineItem } from "./components/TimelineItem.js";
21
20
  import { MarkdownPolicyContext } from "./components/MarkdownRenderer.js";
@@ -168,7 +167,7 @@ function ConversationTimeline({ disambiguateAuthors, clientActions, onOpen, focu
168
167
  timelineController.jumpToLatest();
169
168
  timeline.scrollToEnd({ behavior: "smooth" });
170
169
  }, [timeline, timelineController]);
171
- return (_jsxs("div", { className: "prose relative min-h-0 flex-1", children: [_jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: sessionReadout(sessionStatus.status, sessionStatus.viewState) }), sessionStatus.observation.status === "unavailable" ? (_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-1 text-xs text-dim", role: "status", children: [_jsx("span", { children: sessionStatus.observation.reason }), _jsx("button", { className: "underline", onClick: () => void sessionStatus.reconnect(), type: "button", children: "Reconnect" })] })) : null, _jsx("div", { className: "h-full min-h-0 overflow-y-auto", "data-chat-scroller": true, ref: scrollerRef, onScroll: (event) => {
170
+ return (_jsxs("div", { className: "prose relative min-h-0 flex-1", children: [_jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: sessionReadout(sessionStatus.status, sessionStatus.viewState) }), sessionStatus.observation.status === "unavailable" ? (_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-1 text-xs text-dim", role: "status", children: [_jsx("span", { children: sessionStatus.observation.reason }), _jsx(Button, { variant: "outline", size: "xs", onClick: () => void sessionStatus.reconnect(), type: "button", children: "Reconnect" })] })) : null, _jsx("div", { className: "h-full min-h-0 overflow-y-auto", "data-chat-scroller": true, ref: scrollerRef, onScroll: (event) => {
172
171
  const top = event.currentTarget.scrollTop;
173
172
  if (previousScrollTop.current !== undefined &&
174
173
  top < previousScrollTop.current)
@@ -220,21 +219,20 @@ function ConversationTimeline({ disambiguateAuthors, clientActions, onOpen, focu
220
219
  : item, onDismissFailed: dismissFailed, onCancelQueued: cancelQueued, cancellingQueued: timelineController.cancellingQueued, onToggleTool: toggleTool, renderToolActions: renderToolActions, claimsToolMedia: claimsToolMedia, renderAssistantAttachment: renderAssistantAttachment, formatAssistantText: formatAssistantText, toolDisclosure: toolDisclosure }), placed.after.get(item.id)?.map(({ entryId, cell }) => (_jsx(ClientRows, { entryId: entryId, rows: cell.rows, actions: clientActions, onOpen: onOpen }, `${entryId}:${cell.id}`)))] }, virtualItem.key));
221
220
  }) })) : before.length > 0 ? (before) : hasMoreHistory ? null : ((empty ?? (_jsx("div", { className: "flex h-full items-center justify-center text-center", children: _jsxs("div", { children: [_jsx("p", { className: "mb-1 text-xs text-base-content", children: "Ready" }), _jsx("p", { className: "text-[10px] text-dim", children: "What would you like to build?" })] }) })))) }), count > 0 ? (_jsx(ScrollToBottomBlob, { direction: "forward", unread: unread, focusAfterJump: focusAfterJump, onJump: jumpToBottom, scrollerRef: scrollerRef })) : null] }));
222
221
  }
223
- /** The owner's queued state and independently contributed waiting controls. */
222
+ /** Independently contributed waiting controls; queued messages stay in the transcript. */
224
223
  function ConversationWaiting({ clientActions, }) {
225
224
  const { viewState } = useStatus();
226
225
  const contributions = useClientContributions();
227
226
  const extensionWaiting = viewState.kind === "idle"
228
227
  ? contributions.find(({ value }) => value.waiting !== null)
229
228
  : undefined;
230
- const waiting = viewState.kind === "queued" ? viewState : null;
231
- return (_jsxs(_Fragment, { children: [extensionWaiting?.value.waiting === undefined ||
232
- extensionWaiting.value.waiting === null ? null : (_jsxs("div", { className: "flex items-center gap-2 px-3 text-xs", children: [_jsx("span", { children: extensionWaiting.value.waiting.summary }), _jsx(Button, { size: "xs", variant: "outline", disabled: !clientActions.enabled ||
233
- clientActions.state.get(extensionWaiting.value.waiting.cancel)
234
- ?.pending === true, onClick: () => {
235
- if (extensionWaiting.value.waiting !== null)
236
- void clientActions.execute(extensionWaiting.entryId, extensionWaiting.value.waiting.cancel, true);
237
- }, children: extensionWaiting.value.waiting.cancel.label })] })), _jsx(WaitingNotice, { waiting: waiting })] }));
229
+ return (_jsx(_Fragment, { children: extensionWaiting?.value.waiting === undefined ||
230
+ extensionWaiting.value.waiting === null ? null : (_jsxs("div", { className: "flex items-center gap-2 px-3 text-xs", children: [_jsx("span", { children: extensionWaiting.value.waiting.summary }), _jsx(Button, { size: "xs", variant: "outline", disabled: !clientActions.enabled ||
231
+ clientActions.state.get(extensionWaiting.value.waiting.cancel)
232
+ ?.pending === true, onClick: () => {
233
+ if (extensionWaiting.value.waiting !== null)
234
+ void clientActions.execute(extensionWaiting.entryId, extensionWaiting.value.waiting.cancel, true);
235
+ }, children: extensionWaiting.value.waiting.cancel.label })] })) }));
238
236
  }
239
237
  function ConversationComposer({ visitor, textareaRef, composerActionVariant, slash, }) {
240
238
  const composer = useComposer();
@@ -105,6 +105,15 @@ export type ToolItem = Readonly<{
105
105
  startedAt?: number;
106
106
  /** Owner-confirmed activity or result; unknown when neither is available. */
107
107
  status: "completed" | "error" | "running" | "unknown";
108
+ /** Replaceable display-only output, never part of the durable result. */
109
+ live?: Readonly<{
110
+ /** Neutral output lines. */
111
+ lines: readonly string[];
112
+ /** Raw producer bytes omitted before this tail. */
113
+ droppedBytes: number;
114
+ /** Owner-issued output revision. */
115
+ revision: number;
116
+ }>;
108
117
  }>;
109
118
  /**
110
119
  * One maximal run of consecutive tool calls between conversation rows. A lone