@tangle-network/agent-app 0.45.62 → 0.45.64
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/dist/assistant/index.js +2 -2
- package/dist/{chunk-PC2WYTK7.js → chunk-EDTWGSQT.js} +118 -1
- package/dist/chunk-EDTWGSQT.js.map +1 -0
- package/dist/{chunk-FOXGPGXF.js → chunk-OMGV3CX2.js} +1252 -534
- package/dist/chunk-OMGV3CX2.js.map +1 -0
- package/dist/session-shell/command-palette.d.ts +88 -0
- package/dist/session-shell/index.d.ts +1 -0
- package/dist/session-shell/index.js +13 -1
- package/dist/web-react/chat-composer.d.ts +29 -1
- package/dist/web-react/command-palette.d.ts +44 -0
- package/dist/web-react/index.d.ts +2 -0
- package/dist/web-react/index.js +14 -2
- package/dist/web-react/insight-card.d.ts +5 -1
- package/dist/web-react/record-grid-model.d.ts +45 -0
- package/dist/web-react/record-grid.d.ts +28 -2
- package/dist/web-react/use-dictation.d.ts +67 -0
- package/dist/workspace-react/index.js +1 -1
- package/package.json +12 -12
- package/dist/chunk-FOXGPGXF.js.map +0 -1
- package/dist/chunk-PC2WYTK7.js.map +0 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command palette — the React-free selection half of the Cmd/Ctrl+K surface
|
|
3
|
+
* (`/web-react` holds the rendered half, `CommandPalette`).
|
|
4
|
+
*
|
|
5
|
+
* Pure and import-free beyond this module's own types: no React, no DOM, no
|
|
6
|
+
* fuse.js. A route loader or a worker can build and rank the same items the
|
|
7
|
+
* browser renders.
|
|
8
|
+
*
|
|
9
|
+
* Domain stays a parameter. The palette knows two kinds of row — a SESSION the
|
|
10
|
+
* user can jump to and an ACTION the product offers (new chat, toggle theme,
|
|
11
|
+
* open settings) — and both arrive as data. What a selection DOES is the
|
|
12
|
+
* product's business; the shell only builds, ranks, and groups.
|
|
13
|
+
*
|
|
14
|
+
* Ranking is the documented ladder, not a fuzzy library: exact > prefix >
|
|
15
|
+
* word-prefix > substring (earlier index wins) > token-order, with keyword
|
|
16
|
+
* hits ranked a fixed step below the same hit on the label. Deterministic —
|
|
17
|
+
* no index-building, no async, same input always sorts the same way.
|
|
18
|
+
*/
|
|
19
|
+
import { type SessionSummary } from './index';
|
|
20
|
+
/** A product-supplied palette action. `hint` is the right-aligned affordance
|
|
21
|
+
* copy (a kbd chord, a route name) — rendered verbatim, never interpreted. */
|
|
22
|
+
export interface CommandPaletteAction {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
hint?: string;
|
|
27
|
+
/** Extra match vocabulary that never renders (`settings` matching
|
|
28
|
+
* "preferences"). A keyword hit ranks below the same hit on the label. */
|
|
29
|
+
keywords?: string[];
|
|
30
|
+
}
|
|
31
|
+
/** One selectable row. `group` is the section header it renders under. */
|
|
32
|
+
export interface CommandPaletteItem {
|
|
33
|
+
id: string;
|
|
34
|
+
group: string;
|
|
35
|
+
label: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
hint?: string;
|
|
38
|
+
keywords?: string[];
|
|
39
|
+
/** Recency key (ISO-8601). Breaks score ties and orders the unfiltered
|
|
40
|
+
* list recent-first. Rows without one sort below rows with one. */
|
|
41
|
+
recentAt?: string | null;
|
|
42
|
+
}
|
|
43
|
+
/** One rendered section: a header plus its rows, in first-seen group order. */
|
|
44
|
+
export interface CommandPaletteGroup {
|
|
45
|
+
group: string;
|
|
46
|
+
items: CommandPaletteItem[];
|
|
47
|
+
}
|
|
48
|
+
export declare const COMMAND_PALETTE_SESSIONS_GROUP = "Sessions";
|
|
49
|
+
export declare const COMMAND_PALETTE_ACTIONS_GROUP = "Actions";
|
|
50
|
+
export interface BuildCommandPaletteItemsOptions {
|
|
51
|
+
sessions?: readonly SessionSummary[];
|
|
52
|
+
actions?: readonly CommandPaletteAction[];
|
|
53
|
+
/** Section label for sessions. Default "Sessions". */
|
|
54
|
+
sessionsLabel?: string;
|
|
55
|
+
/** Section label for actions. Default "Actions". */
|
|
56
|
+
actionsLabel?: string;
|
|
57
|
+
/** Placeholder title for an untitled session. */
|
|
58
|
+
untitledLabel?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Flatten sessions + actions into palette items, sessions group first (the
|
|
62
|
+
* jump-back-in list), actions after. Sessions order recent-first by
|
|
63
|
+
* `updatedAt` — a palette with an empty query IS the recency list, so the
|
|
64
|
+
* build order is the render order and the filter never has to re-derive it.
|
|
65
|
+
* Pinned sessions lead the recency sort, matching the rail.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildCommandPaletteItems({ sessions, actions, sessionsLabel, actionsLabel, untitledLabel, }: BuildCommandPaletteItemsOptions): CommandPaletteItem[];
|
|
68
|
+
/**
|
|
69
|
+
* Score an item: the best label score, or the best keyword score a fixed step
|
|
70
|
+
* below. `null` when neither matches — the item is filtered out. An empty
|
|
71
|
+
* query scores every item 0 (the caller keeps build order: recent-first).
|
|
72
|
+
*/
|
|
73
|
+
export declare function scoreCommandPaletteItem(item: CommandPaletteItem, query: string): number | null;
|
|
74
|
+
/**
|
|
75
|
+
* Filter + rank: an empty query returns the items untouched (build order is
|
|
76
|
+
* the recency order); a real query drops non-matches and sorts by score, then
|
|
77
|
+
* recency, then original position — stable and deterministic.
|
|
78
|
+
*/
|
|
79
|
+
export declare function filterCommandPaletteItems(items: readonly CommandPaletteItem[], query: string): CommandPaletteItem[];
|
|
80
|
+
/**
|
|
81
|
+
* Fold a flat (already ordered) item list into renderable sections. Groups
|
|
82
|
+
* appear in first-seen order and each group appears ONCE — a filtered ranking
|
|
83
|
+
* interleaves sessions and actions by score, and folding only consecutive runs
|
|
84
|
+
* would render the same header twice. Within a group, rows keep the flat
|
|
85
|
+
* order. Empty groups vanish, so a filter that leaves only actions renders no
|
|
86
|
+
* "Sessions" header over nothing.
|
|
87
|
+
*/
|
|
88
|
+
export declare function groupCommandPaletteItems(items: readonly CommandPaletteItem[]): CommandPaletteGroup[];
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* rather than the shell knowing any URL.
|
|
16
16
|
*/
|
|
17
17
|
export * from './nav-guard';
|
|
18
|
+
export * from './command-palette';
|
|
18
19
|
/** One session as the shell needs to see it. Products map their own row
|
|
19
20
|
* (thread / session / matter) onto this before handing it over. */
|
|
20
21
|
export interface SessionSummary {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
COMMAND_PALETTE_ACTIONS_GROUP,
|
|
3
|
+
COMMAND_PALETTE_SESSIONS_GROUP,
|
|
2
4
|
DEFAULT_RAIL_COOKIE_NAME,
|
|
3
5
|
UNTITLED_SESSION_LABEL,
|
|
4
6
|
activeSessionIdFromPath,
|
|
5
7
|
assertNavHrefsRegistered,
|
|
8
|
+
buildCommandPaletteItems,
|
|
6
9
|
buildSessionNavItem,
|
|
7
10
|
buildSessionSubItems,
|
|
8
11
|
checkNavHrefs,
|
|
9
12
|
composeSidebarSessions,
|
|
13
|
+
filterCommandPaletteItems,
|
|
10
14
|
flattenRouteTable,
|
|
15
|
+
groupCommandPaletteItems,
|
|
11
16
|
mergeSessionPages,
|
|
12
17
|
railCollapsedCookie,
|
|
13
18
|
readRailCollapsedCookie,
|
|
@@ -16,19 +21,25 @@ import {
|
|
|
16
21
|
resolveNavHref,
|
|
17
22
|
resolveScopedActiveNavId,
|
|
18
23
|
resolveSessionUnread,
|
|
24
|
+
scoreCommandPaletteItem,
|
|
19
25
|
sessionLabel,
|
|
20
26
|
writeRailCollapsedCookie
|
|
21
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-EDTWGSQT.js";
|
|
22
28
|
export {
|
|
29
|
+
COMMAND_PALETTE_ACTIONS_GROUP,
|
|
30
|
+
COMMAND_PALETTE_SESSIONS_GROUP,
|
|
23
31
|
DEFAULT_RAIL_COOKIE_NAME,
|
|
24
32
|
UNTITLED_SESSION_LABEL,
|
|
25
33
|
activeSessionIdFromPath,
|
|
26
34
|
assertNavHrefsRegistered,
|
|
35
|
+
buildCommandPaletteItems,
|
|
27
36
|
buildSessionNavItem,
|
|
28
37
|
buildSessionSubItems,
|
|
29
38
|
checkNavHrefs,
|
|
30
39
|
composeSidebarSessions,
|
|
40
|
+
filterCommandPaletteItems,
|
|
31
41
|
flattenRouteTable,
|
|
42
|
+
groupCommandPaletteItems,
|
|
32
43
|
mergeSessionPages,
|
|
33
44
|
railCollapsedCookie,
|
|
34
45
|
readRailCollapsedCookie,
|
|
@@ -37,6 +48,7 @@ export {
|
|
|
37
48
|
resolveNavHref,
|
|
38
49
|
resolveScopedActiveNavId,
|
|
39
50
|
resolveSessionUnread,
|
|
51
|
+
scoreCommandPaletteItem,
|
|
40
52
|
sessionLabel,
|
|
41
53
|
writeRailCollapsedCookie
|
|
42
54
|
};
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* fallbacks when a host hasn't defined a private chat-token set.
|
|
26
26
|
*/
|
|
27
27
|
import { type ReactNode } from 'react';
|
|
28
|
+
import { type DictationAudio } from './use-dictation';
|
|
28
29
|
/** Prompt-part descriptor an uploaded file carries (the upload route's
|
|
29
30
|
* `UploadedChatFile.part`), echoed back in the turn body on send. Mirrors
|
|
30
31
|
* `/chat-routes`' wire shape structurally — no server import here. */
|
|
@@ -105,6 +106,20 @@ export interface ComposerSendFailure {
|
|
|
105
106
|
* the notice instead. */
|
|
106
107
|
restored: boolean;
|
|
107
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* One `/` command the composer offers. Typing `/` at position 0 opens the
|
|
111
|
+
* command menu; the rest of the token filters it (the same prefix > substring
|
|
112
|
+
* > token-order ranking as the command palette). Picking a command CLEARS the
|
|
113
|
+
* token from the draft and calls `run` — what the command does (a route, a
|
|
114
|
+
* dialog, a draft transformation) is the product's business.
|
|
115
|
+
*/
|
|
116
|
+
export interface SlashCommand {
|
|
117
|
+
/** Command name without the leading slash: `model`, `clear`. */
|
|
118
|
+
name: string;
|
|
119
|
+
/** One line of what it does, rendered beside the name. */
|
|
120
|
+
description: string;
|
|
121
|
+
run: () => void;
|
|
122
|
+
}
|
|
108
123
|
export interface ChatComposerProps {
|
|
109
124
|
/** Send the trimmed, non-empty message. Attached files travel separately via
|
|
110
125
|
* `onAttach` + `pendingFiles` (the host consumes and clears them on send).
|
|
@@ -162,6 +177,19 @@ export interface ChatComposerProps {
|
|
|
162
177
|
accept?: string;
|
|
163
178
|
dropTitle?: string;
|
|
164
179
|
dropDescription?: string;
|
|
180
|
+
/** `/` commands offered when the draft is exactly a leading slash token.
|
|
181
|
+
* Omit (or pass []) and `/` types as ordinary text. */
|
|
182
|
+
slashCommands?: SlashCommand[];
|
|
183
|
+
/** Dictation is opt-in: pass `onDictate` and the action row gains a mic
|
|
184
|
+
* button (browsers without `MediaRecorder`/`getUserMedia` render none).
|
|
185
|
+
* Click starts the capture; the button flips to a stop control with the
|
|
186
|
+
* running elapsed seconds; stop hands the recorded audio blob here. The
|
|
187
|
+
* composer owns capture only — turning the audio into text (e.g. the
|
|
188
|
+
* Whisper provider from `sequences-react`) is the host's. */
|
|
189
|
+
onDictate?: (audio: DictationAudio) => void;
|
|
190
|
+
/** Capture failures (a denied mic prompt, no device), after the composer has
|
|
191
|
+
* shown its own dismissible notice. For hosts that log or track. */
|
|
192
|
+
onDictateError?: (message: string) => void;
|
|
165
193
|
/** Cmd/Ctrl+L focuses the input and shows the hint. Default true. */
|
|
166
194
|
focusShortcut?: boolean;
|
|
167
195
|
/** Float the card on a soft two-layer foreground-tinted shadow (opt-in).
|
|
@@ -176,4 +204,4 @@ export interface ChatComposerProps {
|
|
|
176
204
|
sendVariant?: 'pill' | 'icon';
|
|
177
205
|
className?: string;
|
|
178
206
|
}
|
|
179
|
-
export declare function ChatComposer({ onSend, onSendParts, onSendFailed, sendFailureMessage, onCancel, isStreaming, disabled, placeholder, value, onValueChange, initialValue, seed, onSeedApplied, controls, controlsPlacement, onAttach, onAttachFolder, pendingFiles, onRemoveFile, accept, dropTitle, dropDescription, focusShortcut, floating, sendLabel, sendVariant, className, }: ChatComposerProps): import("react").JSX.Element;
|
|
207
|
+
export declare function ChatComposer({ onSend, onSendParts, onSendFailed, sendFailureMessage, onCancel, isStreaming, disabled, placeholder, value, onValueChange, initialValue, seed, onSeedApplied, controls, controlsPlacement, onAttach, onAttachFolder, pendingFiles, onRemoveFile, accept, dropTitle, dropDescription, slashCommands, onDictate, onDictateError, focusShortcut, floating, sendLabel, sendVariant, className, }: ChatComposerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandPalette — the rendered half of the Cmd/Ctrl+K surface. Selection,
|
|
3
|
+
* ranking, and grouping live in `/session-shell` (`buildCommandPaletteItems`,
|
|
4
|
+
* `filterCommandPaletteItems`, `groupCommandPaletteItems`); this component is
|
|
5
|
+
* the overlay, the input, and the keyboard model.
|
|
6
|
+
*
|
|
7
|
+
* Placement follows the PopoverSurface canon (AGENTS.md "UI chrome
|
|
8
|
+
* ownership"): the panel PORTALS to `document.body` and positions in viewport
|
|
9
|
+
* coordinates (`fixed`), so no host markup — a scroll rail, a `transform`, a
|
|
10
|
+
* stacking context — can clip or trap it. Unlike the pickers it is CENTERED,
|
|
11
|
+
* not trigger-anchored: a palette has no trigger, so it does not reuse
|
|
12
|
+
* `PopoverSurface` itself, but it carries the same grammar — `bg-popover`,
|
|
13
|
+
* `border-card-edge`, `OVERLAY_SHADOW`, the stamped surface attribute.
|
|
14
|
+
*
|
|
15
|
+
* The keyboard model is the ARIA combobox pattern: focus stays in the input,
|
|
16
|
+
* ArrowUp/ArrowDown move `aria-activedescendant` across the FLAT result list
|
|
17
|
+
* (groups are presentation), Enter selects, Escape closes, and closing returns
|
|
18
|
+
* focus to whatever had it before the palette opened.
|
|
19
|
+
*/
|
|
20
|
+
import { type CommandPaletteItem } from '../session-shell/index';
|
|
21
|
+
export type { CommandPaletteItem } from '../session-shell/index';
|
|
22
|
+
export interface CommandPaletteProps {
|
|
23
|
+
/** The full item list, build-ordered (recent-first sessions, then actions).
|
|
24
|
+
* Filtering and ranking are owned here — pass the UNFILTERED list. */
|
|
25
|
+
items: CommandPaletteItem[];
|
|
26
|
+
/** A row was chosen (click or Enter). The palette closes itself. */
|
|
27
|
+
onSelect: (item: CommandPaletteItem) => void;
|
|
28
|
+
/** Controlled open state. Omit for self-managed state toggled by the hotkey. */
|
|
29
|
+
open?: boolean;
|
|
30
|
+
onOpenChange?: (open: boolean) => void;
|
|
31
|
+
/** Register the Cmd/Ctrl+K toggle. Default true. */
|
|
32
|
+
hotkey?: boolean;
|
|
33
|
+
/** Async source is still resolving — the input stays live, the list shows
|
|
34
|
+
* the loading row instead of a premature empty state. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Seed for the query (uncontrolled). */
|
|
37
|
+
initialQuery?: string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
/** Empty-state copy. Default names the query: `No results for “…”`. */
|
|
40
|
+
emptyMessage?: string;
|
|
41
|
+
/** Accessible name for the dialog. Default "Command palette". */
|
|
42
|
+
label?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function CommandPalette({ items, onSelect, open: controlledOpen, onOpenChange, hotkey, loading, initialQuery, placeholder, emptyMessage, label, }: CommandPaletteProps): import("react").ReactPortal | null;
|
|
@@ -53,8 +53,10 @@ export * from './sandbox-terminal';
|
|
|
53
53
|
export * from './seat-paywall';
|
|
54
54
|
export * from './session-history';
|
|
55
55
|
export * from './record-grid';
|
|
56
|
+
export * from './command-palette';
|
|
56
57
|
export * from './sparkline';
|
|
57
58
|
export * from './insight-card';
|
|
59
|
+
export * from './use-dictation';
|
|
58
60
|
export { usePopover, usePending, PopoverSurface, POPOVER_SURFACE_ATTR, ModelPicker, EffortPicker, EffortMeter, effortMeterFill, effortLevelLabel, effortLevelsFromIds, reconcileEffortLevels, DEFAULT_EFFORT_LEVELS, EFFORT_METER_SEGMENTS, OVERLAY_SHADOW, type ModelPickerProps, type EffortPickerProps, type EffortLevel, type PopoverSurfaceProps, } from './controls';
|
|
59
61
|
export { AgentSessionControls, type AgentSessionControlsProps, } from './agent-session-controls';
|
|
60
62
|
import type { CatalogModel } from '../runtime/model-catalog';
|
package/dist/web-react/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ChatComposer,
|
|
4
4
|
ChatEmptyState,
|
|
5
5
|
ChatMessages,
|
|
6
|
+
CommandPalette,
|
|
6
7
|
DEFAULT_INSIGHT_PAGE_SIZE,
|
|
7
8
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
8
9
|
DEFAULT_MENTION_LIMIT,
|
|
@@ -50,12 +51,15 @@ import {
|
|
|
50
51
|
createSessionInteractionAttemptStore,
|
|
51
52
|
describeProvenance,
|
|
52
53
|
describeProvenanceSourceStatus,
|
|
54
|
+
dictationErrorMessage,
|
|
55
|
+
diffRecordGridProposal,
|
|
53
56
|
dispatchChatStreamLine,
|
|
54
57
|
durableChatCardsFromParts,
|
|
55
58
|
fieldAnswer,
|
|
56
59
|
fieldValuesFromAnswers,
|
|
57
60
|
formatActivityCost,
|
|
58
61
|
formatActivityDuration,
|
|
62
|
+
formatDictationElapsed,
|
|
59
63
|
formatInsightDelta,
|
|
60
64
|
formatModelCost,
|
|
61
65
|
formatRecordGridValue,
|
|
@@ -81,6 +85,7 @@ import {
|
|
|
81
85
|
nextRevealCount,
|
|
82
86
|
parseRecordGridInput,
|
|
83
87
|
pendingApprovalOf,
|
|
88
|
+
pickDictationMimeType,
|
|
84
89
|
projectRecordGridRows,
|
|
85
90
|
provenanceBasisMeta,
|
|
86
91
|
provenanceGaps,
|
|
@@ -113,6 +118,7 @@ import {
|
|
|
113
118
|
triggerAttachmentDownload,
|
|
114
119
|
upsertChatInteraction,
|
|
115
120
|
useChatInteractions,
|
|
121
|
+
useDictation,
|
|
116
122
|
useDurablePlanFlow,
|
|
117
123
|
useFileMentions,
|
|
118
124
|
useInfiniteScroll,
|
|
@@ -131,7 +137,7 @@ import {
|
|
|
131
137
|
withoutRecordGridCreated,
|
|
132
138
|
withoutRecordGridRemoved,
|
|
133
139
|
withoutRecordGridUpdate
|
|
134
|
-
} from "../chunk-
|
|
140
|
+
} from "../chunk-OMGV3CX2.js";
|
|
135
141
|
import "../chunk-FBVLEGEG.js";
|
|
136
142
|
import {
|
|
137
143
|
EvidenceLineageTable,
|
|
@@ -175,7 +181,7 @@ import {
|
|
|
175
181
|
useSandboxTerminalConnection
|
|
176
182
|
} from "../chunk-BATKJP3P.js";
|
|
177
183
|
import "../chunk-3UBAO3N5.js";
|
|
178
|
-
import "../chunk-
|
|
184
|
+
import "../chunk-EDTWGSQT.js";
|
|
179
185
|
import "../chunk-QY4BRKRJ.js";
|
|
180
186
|
import {
|
|
181
187
|
ATTACHMENT_ACCEPT
|
|
@@ -238,6 +244,7 @@ export {
|
|
|
238
244
|
ChatComposer,
|
|
239
245
|
ChatEmptyState,
|
|
240
246
|
ChatMessages,
|
|
247
|
+
CommandPalette,
|
|
241
248
|
DEFAULT_EFFORT_LEVELS,
|
|
242
249
|
DEFAULT_INSIGHT_PAGE_SIZE,
|
|
243
250
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
@@ -320,6 +327,8 @@ export {
|
|
|
320
327
|
dedupeQuestionInteractionsByContent,
|
|
321
328
|
describeProvenance,
|
|
322
329
|
describeProvenanceSourceStatus,
|
|
330
|
+
dictationErrorMessage,
|
|
331
|
+
diffRecordGridProposal,
|
|
323
332
|
dispatchChatStreamLine,
|
|
324
333
|
durableChatCardsFromParts,
|
|
325
334
|
effortLevelLabel,
|
|
@@ -331,6 +340,7 @@ export {
|
|
|
331
340
|
fileMentionsToParts,
|
|
332
341
|
formatActivityCost,
|
|
333
342
|
formatActivityDuration,
|
|
343
|
+
formatDictationElapsed,
|
|
334
344
|
formatInsightDelta,
|
|
335
345
|
formatModelCost,
|
|
336
346
|
formatRecordGridValue,
|
|
@@ -375,6 +385,7 @@ export {
|
|
|
375
385
|
parseReviewQueueItem,
|
|
376
386
|
pendingApprovalOf,
|
|
377
387
|
persistedPartToInteraction,
|
|
388
|
+
pickDictationMimeType,
|
|
378
389
|
projectRecordGridRows,
|
|
379
390
|
provenanceBasisMeta,
|
|
380
391
|
provenanceGaps,
|
|
@@ -413,6 +424,7 @@ export {
|
|
|
413
424
|
upsertChatInteraction,
|
|
414
425
|
useChatInteractions,
|
|
415
426
|
useComposerAttachments,
|
|
427
|
+
useDictation,
|
|
416
428
|
useDurablePlanFlow,
|
|
417
429
|
useFileMentions,
|
|
418
430
|
useInfiniteScroll,
|
|
@@ -81,6 +81,10 @@ export interface InsightAction {
|
|
|
81
81
|
onClick: () => void;
|
|
82
82
|
}
|
|
83
83
|
export interface InsightCardProps {
|
|
84
|
+
/** The lane the metric belongs to ("Spend", "Missions"), set small above the
|
|
85
|
+
* title. A deck of cards from different surfaces needs the grouping word
|
|
86
|
+
* before the metric's own name, not after it. */
|
|
87
|
+
eyebrow?: string;
|
|
84
88
|
/** What was measured, in the reader's words ("Spend today"). */
|
|
85
89
|
title: string;
|
|
86
90
|
/** The number that moved. A `string` renders verbatim — a total the caller
|
|
@@ -113,7 +117,7 @@ export interface InsightCardProps {
|
|
|
113
117
|
className?: string;
|
|
114
118
|
style?: CSSProperties;
|
|
115
119
|
}
|
|
116
|
-
export declare function InsightCard({ title, value, unit, previous, polarity, format, series, seriesLabel, description, action, live, liveLabel, className, style, }: InsightCardProps): ReactElement;
|
|
120
|
+
export declare function InsightCard({ eyebrow, title, value, unit, previous, polarity, format, series, seriesLabel, description, action, live, liveLabel, className, style, }: InsightCardProps): ReactElement;
|
|
117
121
|
export interface Insight extends InsightCardProps {
|
|
118
122
|
/** Stable across refreshes: it keys the card. Paired with the deck holding
|
|
119
123
|
* the last loaded page across a reload, a stable id is what lets a settled
|
|
@@ -177,6 +177,51 @@ export declare function isRecordGridCellApplicable(column: RecordGridColumn, val
|
|
|
177
177
|
/** Value equality across the grid's value union, treating `undefined` as
|
|
178
178
|
* `null` so an absent key and an explicit null never read as a change. */
|
|
179
179
|
export declare function sameRecordGridValue(a: RecordGridValue | undefined, b: RecordGridValue | undefined): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* A proposed change set, diffed against the live rows by
|
|
182
|
+
* {@link diffRecordGridProposal}. The grid's review mode renders exactly what
|
|
183
|
+
* this shape declares — it owns no opinion about where the proposal came from
|
|
184
|
+
* (an agent's `submit_proposal` call, a record store's pending entries).
|
|
185
|
+
*/
|
|
186
|
+
export interface RecordGridProposal {
|
|
187
|
+
/** Proposed new cell values for EXISTING rows: row id → column id → value.
|
|
188
|
+
* Only cells that differ from the live value diff; an update that restates
|
|
189
|
+
* the current value is not a change. An id with no live row is ignored —
|
|
190
|
+
* adding a row is `additions`' job. */
|
|
191
|
+
updates?: Readonly<Record<string, Readonly<Record<string, RecordGridValue>>>>;
|
|
192
|
+
/** Proposed new rows. An addition whose id already names a live row is
|
|
193
|
+
* ignored — changing an existing row is `updates`' job. */
|
|
194
|
+
additions?: readonly RecordGridRow[];
|
|
195
|
+
/** Live row ids proposed for removal. Unknown ids are ignored. */
|
|
196
|
+
removals?: readonly string[];
|
|
197
|
+
}
|
|
198
|
+
/** One cell whose proposed value differs from the live one. */
|
|
199
|
+
export interface RecordGridCellDiff {
|
|
200
|
+
columnId: string;
|
|
201
|
+
/** The live value — what rejecting keeps. */
|
|
202
|
+
before: RecordGridValue;
|
|
203
|
+
/** The proposed value — what accepting writes. */
|
|
204
|
+
after: RecordGridValue;
|
|
205
|
+
}
|
|
206
|
+
export type RecordGridRowDiffKind = 'changed' | 'added' | 'removed';
|
|
207
|
+
/** One row's verdict: what the proposal does to it. */
|
|
208
|
+
export interface RecordGridRowDiff {
|
|
209
|
+
rowId: string;
|
|
210
|
+
kind: RecordGridRowDiffKind;
|
|
211
|
+
/** The differing cells. Empty for `added`/`removed` — every cell of those is
|
|
212
|
+
* part of the change by definition. */
|
|
213
|
+
cells: readonly RecordGridCellDiff[];
|
|
214
|
+
/** The live row for `changed`/`removed`, the proposed row for `added`. */
|
|
215
|
+
row: RecordGridRow;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Diff a proposal against the live rows. Pure and deterministic: input order
|
|
219
|
+
* in, diff order out — updates follow `rows` order, removals follow `rows`
|
|
220
|
+
* order, additions follow the proposal's order. A row whose update bag diffs
|
|
221
|
+
* to nothing produces no entry, so a no-op proposal yields an empty diff and
|
|
222
|
+
* the grid has nothing to review.
|
|
223
|
+
*/
|
|
224
|
+
export declare function diffRecordGridProposal(rows: readonly RecordGridRow[], proposal: RecordGridProposal): RecordGridRowDiff[];
|
|
180
225
|
/**
|
|
181
226
|
* Turn what an editor control produced into a typed value. Syntax only —
|
|
182
227
|
* range, length, and membership are {@link validateRecordGridCell}'s job.
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
* - **Provenance per cell.** An optional quote + link + basis, so a
|
|
18
18
|
* record-backed grid shows where a value came from without the product
|
|
19
19
|
* building a second surface for it.
|
|
20
|
+
* - **Review of a proposed change set.** Hand the grid a `proposed` patch
|
|
21
|
+
* (`./record-grid-model`'s `diffRecordGridProposal`) and it becomes the
|
|
22
|
+
* red/green row-diff surface a tax/legal review needs: changed cells render
|
|
23
|
+
* the struck live value against the proposed one, added/removed rows are
|
|
24
|
+
* marked, and every diffed row carries accept/reject — per row and for the
|
|
25
|
+
* whole set. What accepting MEANS stays the caller's (a record-store review
|
|
26
|
+
* write); the grid reports decisions, it does not persist them.
|
|
20
27
|
* - **Three distinct data states, on `web-react/async`'s own contract.**
|
|
21
28
|
* `state: AsyncResourceState<Row[]>` and `empty: AsyncEmptySpec` are the
|
|
22
29
|
* same types every other screen fetches through — loading, error-with-
|
|
@@ -35,7 +42,7 @@
|
|
|
35
42
|
*/
|
|
36
43
|
import { type ReactNode } from 'react';
|
|
37
44
|
import { type AsyncEmptySpec, type AsyncResourceState } from './async';
|
|
38
|
-
import { type RecordGridColumn, type RecordGridRow, type RecordGridValue } from './record-grid-model';
|
|
45
|
+
import { type RecordGridColumn, type RecordGridProposal, type RecordGridRow, type RecordGridValue } from './record-grid-model';
|
|
39
46
|
export * from './record-grid-model';
|
|
40
47
|
/** One committed cell edit, handed to `onUpdate`. */
|
|
41
48
|
export interface RecordGridCellChange {
|
|
@@ -88,6 +95,25 @@ export interface RecordGridProps {
|
|
|
88
95
|
onUpdate?: (change: RecordGridCellChange) => Promise<RecordGridWriteOutcome>;
|
|
89
96
|
/** Delete one row. Absent → no delete affordance. */
|
|
90
97
|
onDelete?: (row: RecordGridRow) => Promise<RecordGridWriteOutcome>;
|
|
98
|
+
/** A proposed change set to review against the live rows (see
|
|
99
|
+
* `diffRecordGridProposal`). While a non-empty diff is on the table the grid
|
|
100
|
+
* is a REVIEW surface, not an editor: cell editing, row add, and row delete
|
|
101
|
+
* are inert; changed cells render the struck live value against the
|
|
102
|
+
* proposed one; added/removed rows are marked; each diffed row carries
|
|
103
|
+
* accept/reject controls. A proposal that diffs to nothing renders the grid
|
|
104
|
+
* unchanged — there is nothing to review. */
|
|
105
|
+
proposed?: RecordGridProposal;
|
|
106
|
+
/** Accept one diffed row — write its proposed values, adopt the addition, or
|
|
107
|
+
* confirm the removal. The caller owns what accepting MEANS (a record-store
|
|
108
|
+
* review write); the grid reports the decision and the caller moves the row
|
|
109
|
+
* out of `proposed`. */
|
|
110
|
+
onAcceptRow?: (rowId: string) => void;
|
|
111
|
+
/** Reject one diffed row — the live row stands. */
|
|
112
|
+
onRejectRow?: (rowId: string) => void;
|
|
113
|
+
/** Accept every remaining diffed row at once. */
|
|
114
|
+
onAcceptAll?: () => void;
|
|
115
|
+
/** Reject every remaining diffed row at once. */
|
|
116
|
+
onRejectAll?: () => void;
|
|
91
117
|
/** Starting values for the add form. */
|
|
92
118
|
newRowDefaults?: Readonly<Record<string, RecordGridValue>>;
|
|
93
119
|
/** Label of the add control and of the add form. Defaults to `Add row`. */
|
|
@@ -105,4 +131,4 @@ export interface RecordGridProps {
|
|
|
105
131
|
* id, so a record store's fold output maps straight on: one cell per entry,
|
|
106
132
|
* its quote and link in `sources`.
|
|
107
133
|
*/
|
|
108
|
-
export declare function RecordGrid({ columns, caption, state, empty, onCreate, onUpdate, onDelete, newRowDefaults, addLabel, locale, toolbar, loadingRowCount, className, }: RecordGridProps): import("react").JSX.Element;
|
|
134
|
+
export declare function RecordGrid({ columns, caption, state, empty, onCreate, onUpdate, onDelete, proposed, onAcceptRow, onRejectRow, onAcceptAll, onRejectAll, newRowDefaults, addLabel, locale, toolbar, loadingRowCount, className, }: RecordGridProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useDictation` — the capture half of composer dictation.
|
|
3
|
+
*
|
|
4
|
+
* Dictation splits at a clean seam: the browser owns capture (`getUserMedia` +
|
|
5
|
+
* `MediaRecorder`), the host owns what the audio MEANS (transcription —
|
|
6
|
+
* `sequences-react`'s Whisper provider — or a straight upload). This hook is
|
|
7
|
+
* the capture half and nothing else: it asks for the mic, records, ticks whole
|
|
8
|
+
* seconds while it does, and hands the assembled `Blob` to the host's
|
|
9
|
+
* `onDictate`. A hook rather than composer-private code, because a host whose
|
|
10
|
+
* composer is fully composed (hotkey, push-to-talk) needs the same capture
|
|
11
|
+
* without re-deriving it.
|
|
12
|
+
*
|
|
13
|
+
* The rules the implementation exists to hold:
|
|
14
|
+
*
|
|
15
|
+
* - **Unsupported is a render signal, not an exception.** A browser without
|
|
16
|
+
* `MediaRecorder`/`getUserMedia` gets `supported: false`, and the composer
|
|
17
|
+
* renders no dead button. `start()` stays a no-op rather than throwing, so
|
|
18
|
+
* a host that wired it to a hotkey cannot crash on such a browser.
|
|
19
|
+
* - **The mic is released the moment recording ends.** Tracks are stopped in
|
|
20
|
+
* every exit — stop, error, cancel-during-prompt, unmount. A red dot the
|
|
21
|
+
* browser keeps showing after the composer says "idle" is the failure this
|
|
22
|
+
* is written against.
|
|
23
|
+
* - **A denied prompt is a message, not a crash.** `NotAllowedError` and a
|
|
24
|
+
* missing device are reported through `onError` as words the composer can
|
|
25
|
+
* show; the hook returns to idle.
|
|
26
|
+
* - **Unmount discards.** A composer that unmounts mid-recording delivers
|
|
27
|
+
* nothing: the host it would have called has moved on, and an arriving
|
|
28
|
+
* transcript would land in a conversation the user left.
|
|
29
|
+
* - **Duration is measured, not counted.** `durationSeconds` comes off the
|
|
30
|
+
* clock at stop; the one-second ticker drives only the visible elapsed
|
|
31
|
+
* display, so a throttled timer never falsifies the delivered figure.
|
|
32
|
+
*/
|
|
33
|
+
/** The audio a finished recording hands to the host. */
|
|
34
|
+
export interface DictationAudio {
|
|
35
|
+
/** The assembled recording, typed with the MIME the recorder actually used. */
|
|
36
|
+
readonly blob: Blob;
|
|
37
|
+
/** `blob.type`, surfaced so a host can switch on it without touching the blob. */
|
|
38
|
+
readonly mimeType: string;
|
|
39
|
+
/** Clock-measured whole seconds between start and stop. */
|
|
40
|
+
readonly durationSeconds: number;
|
|
41
|
+
}
|
|
42
|
+
export interface UseDictationOptions {
|
|
43
|
+
/** The host callback: receive the recording. Transcription is the host's. */
|
|
44
|
+
onDictate: (audio: DictationAudio) => void;
|
|
45
|
+
/** Capture failures in words ("Microphone access was denied…"). Optional —
|
|
46
|
+
* the composer shows its own notice either way; this is for hosts that log. */
|
|
47
|
+
onError?: (message: string) => void;
|
|
48
|
+
}
|
|
49
|
+
export interface DictationControls {
|
|
50
|
+
/** Whether this browser can record at all. When false, render no affordance. */
|
|
51
|
+
readonly supported: boolean;
|
|
52
|
+
readonly recording: boolean;
|
|
53
|
+
/** Whole seconds since the current recording started; drives the indicator. */
|
|
54
|
+
readonly elapsedSeconds: number;
|
|
55
|
+
/** Ask for the mic and start. A no-op while a recording or a prompt is open. */
|
|
56
|
+
readonly start: () => void;
|
|
57
|
+
/** Stop and deliver. Cancels a still-pending permission prompt instead. */
|
|
58
|
+
readonly stop: () => void;
|
|
59
|
+
}
|
|
60
|
+
/** The mime to ask the recorder for, or `undefined` to take the UA default. */
|
|
61
|
+
export declare function pickDictationMimeType(): string | undefined;
|
|
62
|
+
/** The failure as a sentence. The denied prompt is the common case and the one
|
|
63
|
+
* whose generic name ("NotAllowedError") says nothing to a reader. */
|
|
64
|
+
export declare function dictationErrorMessage(error: unknown): string;
|
|
65
|
+
/** `0:00`, `0:09`, `1:05`, `60:00` — minutes unbounded, seconds always two digits. */
|
|
66
|
+
export declare function formatDictationElapsed(totalSeconds: number): string;
|
|
67
|
+
export declare function useDictation({ onDictate, onError }: UseDictationOptions): DictationControls;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.64",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
|
|
6
6
|
"keywords": [
|
|
@@ -519,15 +519,15 @@
|
|
|
519
519
|
"@storybook/react": "^10.5.5",
|
|
520
520
|
"@storybook/react-vite": "^10.5.5",
|
|
521
521
|
"@tangle-network/agent-docs": "0.2.1",
|
|
522
|
-
"@tangle-network/agent-eval": "0.
|
|
522
|
+
"@tangle-network/agent-eval": "0.147.0",
|
|
523
523
|
"@tangle-network/agent-integrations": "0.53.54",
|
|
524
|
-
"@tangle-network/agent-interface": "1.0.
|
|
525
|
-
"@tangle-network/agent-knowledge": "8.0.
|
|
524
|
+
"@tangle-network/agent-interface": "1.0.1",
|
|
525
|
+
"@tangle-network/agent-knowledge": "8.0.8",
|
|
526
526
|
"@tangle-network/agent-profile-materialize": "0.16.0",
|
|
527
|
-
"@tangle-network/agent-runtime": "0.
|
|
527
|
+
"@tangle-network/agent-runtime": "0.141.1",
|
|
528
528
|
"@tangle-network/brand": "1.5.0",
|
|
529
|
-
"@tangle-network/sandbox": "0.27.
|
|
530
|
-
"@tangle-network/sandbox-ui": "0.
|
|
529
|
+
"@tangle-network/sandbox": "0.27.2",
|
|
530
|
+
"@tangle-network/sandbox-ui": "0.105.0",
|
|
531
531
|
"@tangle-network/ui": "^11.5.0",
|
|
532
532
|
"@testing-library/dom": "^10.4.1",
|
|
533
533
|
"@testing-library/react": "^16.3.2",
|
|
@@ -571,15 +571,15 @@
|
|
|
571
571
|
"@firecrawl/pdf-inspector-wasm": ">=0.1.3",
|
|
572
572
|
"@huggingface/transformers": ">=3",
|
|
573
573
|
"@radix-ui/react-dialog": ">=1.1",
|
|
574
|
-
"@tangle-network/agent-eval": ">=0.
|
|
574
|
+
"@tangle-network/agent-eval": ">=0.147.0",
|
|
575
575
|
"@tangle-network/agent-integrations": ">=0.52.0",
|
|
576
576
|
"@tangle-network/agent-interface": "^1.0.0",
|
|
577
|
-
"@tangle-network/agent-knowledge": ">=8.0.
|
|
577
|
+
"@tangle-network/agent-knowledge": ">=8.0.8",
|
|
578
578
|
"@tangle-network/agent-profile-materialize": ">=0.16.0",
|
|
579
|
-
"@tangle-network/agent-runtime": ">=0.
|
|
579
|
+
"@tangle-network/agent-runtime": ">=0.141.1",
|
|
580
580
|
"@tangle-network/brand": ">=1.5.0",
|
|
581
|
-
"@tangle-network/sandbox": ">=0.27.
|
|
582
|
-
"@tangle-network/sandbox-ui": ">=0.
|
|
581
|
+
"@tangle-network/sandbox": ">=0.27.2",
|
|
582
|
+
"@tangle-network/sandbox-ui": ">=0.105.0",
|
|
583
583
|
"@tangle-network/ui": ">=11.5.0",
|
|
584
584
|
"@xyflow/react": ">=12.0.0",
|
|
585
585
|
"better-auth": ">=1.6.16",
|