@tangle-network/agent-app 0.45.29 → 0.45.30
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/{use-file-mentions-CZ-Ua_sb.d.ts → agent-session-controls-BwImzYKC.d.ts} +32 -1
- package/dist/alerting/index.d.ts +178 -0
- package/dist/alerting/index.js +9 -0
- package/dist/alerting/index.js.map +1 -0
- package/dist/assistant/index.d.ts +3 -3
- package/dist/assistant/index.js +9 -8
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-react/index.d.ts +19 -162
- package/dist/chat-react/index.js +15 -133
- package/dist/chat-react/index.js.map +1 -1
- package/dist/chat-routes/index.js +6 -6
- package/dist/chunk-COP2K4LF.js +190 -0
- package/dist/chunk-COP2K4LF.js.map +1 -0
- package/dist/{chunk-3I27SDU3.js → chunk-IFYJDX3J.js} +799 -1265
- package/dist/chunk-IFYJDX3J.js.map +1 -0
- package/dist/chunk-L7FHRORY.js +763 -0
- package/dist/chunk-L7FHRORY.js.map +1 -0
- package/dist/{chunk-5FAKBTCK.js → chunk-XDB7IBYE.js} +22 -1
- package/dist/chunk-XDB7IBYE.js.map +1 -0
- package/dist/preflight/cli.js +2 -1
- package/dist/preflight/cli.js.map +1 -1
- package/dist/preflight/index.d.ts +35 -2
- package/dist/preflight/index.js +6 -3
- package/dist/teams/index.js +5 -5
- package/dist/teams/invitations-api.js +4 -4
- package/dist/web-react/index.d.ts +3 -31
- package/dist/web-react/index.js +10 -10
- package/package.json +6 -1
- package/dist/chunk-3I27SDU3.js.map +0 -1
- package/dist/chunk-5FAKBTCK.js.map +0 -1
- package/dist/chunk-E4DHYENH.js +0 -292
- package/dist/chunk-E4DHYENH.js.map +0 -1
|
@@ -1,168 +1,24 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { ModelInfo, HarnessType } from '@tangle-network/sandbox-ui/dashboard';
|
|
6
|
-
export { HarnessType, ModelInfo } from '@tangle-network/sandbox-ui/dashboard';
|
|
7
|
-
import { ReasoningEffort } from '@tangle-network/agent-interface';
|
|
8
|
-
export { ReasoningEffort } from '@tangle-network/agent-interface';
|
|
9
|
-
import { U as UseFileMentionsResult } from '../use-file-mentions-CZ-Ua_sb.js';
|
|
3
|
+
import { AgentComposer } from '@tangle-network/sandbox-ui/chat';
|
|
4
|
+
import { A as AgentSessionControlsProps, U as UseFileMentionsResult } from '../agent-session-controls-BwImzYKC.js';
|
|
10
5
|
import { b as ChatAttachmentInput, F as FileMention } from '../wire-DOZ-O6hD.js';
|
|
6
|
+
import '../harness/index.js';
|
|
7
|
+
import '@tangle-network/agent-interface';
|
|
8
|
+
import '../catalog/index.js';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `HarnessType` is agent-interface's enum surfaced through sandbox-ui's picker;
|
|
18
|
-
* `ReasoningLevel` is sandbox-ui's `auto | ReasoningEffort` union.
|
|
19
|
-
*
|
|
20
|
-
* LEGACY: these spellings serve the deprecated `ComposerAgentControls` adapter.
|
|
21
|
-
* Canonical code speaks `/harness`'s `Harness` union and plain effort-id
|
|
22
|
-
* strings (see `/web-react`'s `EffortPicker`); they are re-exported here only
|
|
23
|
-
* so the legacy adapter's props keep resolving.
|
|
11
|
+
* The plan-mode toggle state an entry surface docks beside the composer. The
|
|
12
|
+
* agent-identity pickers (model / harness / effort) speak the canonical
|
|
13
|
+
* `AgentSessionControls` vocabulary from `../web-react` — see "UI chrome
|
|
14
|
+
* ownership (picker canon)" in AGENTS.md.
|
|
24
15
|
*/
|
|
25
|
-
|
|
26
16
|
interface ComposerPlanModeSelection {
|
|
27
17
|
enabled: boolean;
|
|
28
18
|
setEnabled: (next: boolean) => void;
|
|
29
19
|
saving?: boolean;
|
|
30
20
|
}
|
|
31
21
|
|
|
32
|
-
/**
|
|
33
|
-
* The model half of the composer's agent identity. `models` is the catalog the
|
|
34
|
-
* picker renders; `value` may be a bare or a canonical (provider-prefixed) id —
|
|
35
|
-
* the adapter maps it to canonical form for the picker and hands the canonical
|
|
36
|
-
* id back to `onChange`, so a product that stores bare ids only has to accept
|
|
37
|
-
* canonical ones in its setter.
|
|
38
|
-
*/
|
|
39
|
-
interface ComposerModelSelection {
|
|
40
|
-
value: string;
|
|
41
|
-
onChange: (next: string) => void;
|
|
42
|
-
models: ModelInfo[];
|
|
43
|
-
loading?: boolean;
|
|
44
|
-
disabled?: boolean;
|
|
45
|
-
}
|
|
46
|
-
interface ComposerHarnessSelection {
|
|
47
|
-
value: HarnessType;
|
|
48
|
-
onChange: (next: HarnessType) => void;
|
|
49
|
-
/** Restrict the offered backends. Omit to take the context default. */
|
|
50
|
-
available?: ReadonlyArray<HarnessType>;
|
|
51
|
-
disabled?: boolean;
|
|
52
|
-
/**
|
|
53
|
-
* A thread's sidecar session is pinned to one harness at its first message,
|
|
54
|
-
* so once the thread has messages the picker locks. With `onNewChat` the
|
|
55
|
-
* locked chip becomes the informative lock (explains the binding + a button
|
|
56
|
-
* to start a fresh session on another backend).
|
|
57
|
-
*/
|
|
58
|
-
locked?: boolean;
|
|
59
|
-
lockReason?: string;
|
|
60
|
-
onNewChat?: () => void;
|
|
61
|
-
}
|
|
62
|
-
interface ComposerEffortSelection {
|
|
63
|
-
value: ReasoningLevel;
|
|
64
|
-
onChange: (next: ReasoningLevel) => void;
|
|
65
|
-
/**
|
|
66
|
-
* Depths this product's backend actually applies. Omit for the default set.
|
|
67
|
-
* `auto` is NOT a member: the picker adds that sentinel itself, so listing it
|
|
68
|
-
* here would offer it twice.
|
|
69
|
-
*/
|
|
70
|
-
available?: ReadonlyArray<ReasoningEffort>;
|
|
71
|
-
disabled?: boolean;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* A selectable named bundle of persona and tool groups. The product resolves
|
|
75
|
-
* the selected id to the runtime AgentProfile before creating or continuing a
|
|
76
|
-
* session; the composer only needs the display catalog and persistence hooks.
|
|
77
|
-
*/
|
|
78
|
-
type ComposerProfileSelection = AgentSessionProfileControl;
|
|
79
|
-
/**
|
|
80
|
-
* @deprecated Part of the legacy sandbox-ui adapter — see
|
|
81
|
-
* {@link ComposerAgentControls}. New code uses `AgentSessionControlsProps`
|
|
82
|
-
* from `@tangle-network/agent-app/web-react`.
|
|
83
|
-
*/
|
|
84
|
-
interface ComposerAgentControlsProps {
|
|
85
|
-
model: ComposerModelSelection;
|
|
86
|
-
/** Omit on a router-backed composer: a direct model call has no CLI backend,
|
|
87
|
-
* so the picker would be inert. Model + effort still render. */
|
|
88
|
-
harness?: ComposerHarnessSelection;
|
|
89
|
-
/** Universal named agent profile: persona + tool groups, independent of backend. */
|
|
90
|
-
profile?: ComposerProfileSelection;
|
|
91
|
-
effort?: ComposerEffortSelection;
|
|
92
|
-
className?: string;
|
|
93
|
-
/**
|
|
94
|
-
* Which surface the controls live on. `'chat'` (the default) drops the
|
|
95
|
-
* shell-only `cli-base` backend — it has no conversational agent — and tells
|
|
96
|
-
* the shared control to use its chat-only default set. It also drops models
|
|
97
|
-
* that cannot serve a text turn (image / TTS / embedding / audio-in) from
|
|
98
|
-
* the model picker. `'all'` keeps every backend and every model for non-chat
|
|
99
|
-
* / scheduled surfaces.
|
|
100
|
-
*/
|
|
101
|
-
context?: 'chat' | 'all';
|
|
102
|
-
/**
|
|
103
|
-
* Trigger layout. `'responsive'` (the default) lays the pickers out in a
|
|
104
|
-
* row on wider screens and uses a compact gear on narrow screens. `'combined'`
|
|
105
|
-
* collapses the pickers behind one
|
|
106
|
-
* labeled trigger reading the current selection (`harness · model · effort`).
|
|
107
|
-
* `'gear'` uses an anonymous gear icon instead of the label; `'inline'` lays
|
|
108
|
-
* the pickers out in a row. `'responsive'` uses the labeled trigger on wider
|
|
109
|
-
* screens and the compact gear on narrow screens so Send stays reachable.
|
|
110
|
-
*/
|
|
111
|
-
layout?: 'inline' | 'gear' | 'combined' | 'responsive';
|
|
112
|
-
/**
|
|
113
|
-
* Where the pickers open. `'down'` pins them downward for a composer floating
|
|
114
|
-
* in open space (the centered entry surface) so a tall menu can't flip up over
|
|
115
|
-
* the heading. Defaults to `'auto'` (collision-aware).
|
|
116
|
-
*/
|
|
117
|
-
menuPlacement?: 'auto' | 'down';
|
|
118
|
-
/**
|
|
119
|
-
* Restrict the model catalog to what the selected harness can run instead of
|
|
120
|
-
* letting an incompatible model snap the harness. ON by default: a workspace
|
|
121
|
-
* harness is sandbox-bound, and a silent swap tears down and recreates the
|
|
122
|
-
* sandbox. Turn OFF only on a model-first surface with no bound sandbox.
|
|
123
|
-
*/
|
|
124
|
-
filterModelsToHarness?: boolean;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* The composer's agent-identity control: profile, backend, model, and
|
|
128
|
-
* reasoning effort, over sandbox-ui's `AgentSessionControls`.
|
|
129
|
-
*
|
|
130
|
-
* @deprecated Use `AgentSessionControls` from `@tangle-network/agent-app/web-react`
|
|
131
|
-
* — the canonical picker cluster (its model menu IS the canonical
|
|
132
|
-
* `ModelPicker`). sandbox-ui's `AgentSessionControls` and `dashboard/ModelPicker`
|
|
133
|
-
* are legacy: deprecated, frozen, and removed at sandbox-ui's next major. This
|
|
134
|
-
* adapter stays exported and fully functional because removing a published
|
|
135
|
-
* symbol is a major, but it renders the OLD design; new code must not adopt
|
|
136
|
-
* it. Props mapping for the migration lives in `docs/ui-picker-canon.md`.
|
|
137
|
-
*
|
|
138
|
-
* This is an ADAPTER, not a second implementation. The harness↔model coherence
|
|
139
|
-
* policy is the shared one (the catalog is filtered to the
|
|
140
|
-
* selected harness, the effort re-clamps to what the harness/model pair
|
|
141
|
-
* supports, and a harness that ignores a picker is marked with its dead control
|
|
142
|
-
* disabled). What lives here is the app-shell wiring every product otherwise
|
|
143
|
-
* re-derives:
|
|
144
|
-
*
|
|
145
|
-
* - MODEL-ID BOUNDARY. Products store bare ids (what a chat send body expects)
|
|
146
|
-
* while the picker matches on canonical provider-prefixed ids. The canonical
|
|
147
|
-
* form goes in; the canonical id comes back out to `onChange`.
|
|
148
|
-
* - HARNESS-SNAP SUPPRESSION. The shared control snaps the model whenever the
|
|
149
|
-
* harness changes, calling the model `onChange` synchronously with a default
|
|
150
|
-
* compatible model. A product that remembers a per-harness pick must ignore
|
|
151
|
-
* that snap, or it both discards the remembered pick and persists it under
|
|
152
|
-
* the wrong harness's key. The guard is armed for exactly the synchronous
|
|
153
|
-
* window of a harness change and disarmed on the next microtask, so it can
|
|
154
|
-
* never stay stuck — a later genuine pick is never swallowed, even when the
|
|
155
|
-
* harness change was a no-op.
|
|
156
|
-
* - CHAT-CONTEXT TRIM. On a chat surface, `cli-base` never reaches the harness
|
|
157
|
-
* picker and a non-chat model never reaches the model picker. Products feed
|
|
158
|
-
* the picker the router's raw model list, where 35 of 504 entries (image,
|
|
159
|
-
* TTS, embeddings, audio-in transcription) cannot serve a chat turn at all.
|
|
160
|
-
*
|
|
161
|
-
* Behavioral modes (a plan toggle) are NOT agent identity and do not belong
|
|
162
|
-
* here — they dock on the other side of the composer via `controls`.
|
|
163
|
-
*/
|
|
164
|
-
declare function ComposerAgentControls({ model, harness, profile, effort, className, context, layout, menuPlacement, filterModelsToHarness, }: ComposerAgentControlsProps): react.JSX.Element;
|
|
165
|
-
|
|
166
22
|
interface EntryComposerProps {
|
|
167
23
|
/** The one line above the input. Domain copy — always a product parameter. */
|
|
168
24
|
heading?: ReactNode;
|
|
@@ -173,17 +29,18 @@ interface EntryComposerProps {
|
|
|
173
29
|
sendLabel?: string;
|
|
174
30
|
disabled?: boolean;
|
|
175
31
|
/**
|
|
176
|
-
* Agent identity (
|
|
32
|
+
* Agent identity (backend/model/effort). Pass it and the control row renders;
|
|
177
33
|
* omit it and the composer ships without one. Omitting is a real choice for a
|
|
178
34
|
* surface with nothing to choose — it should never be an oversight, which is
|
|
179
35
|
* why this is one prop rather than a free-form slot a caller can forget.
|
|
180
36
|
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
37
|
+
* This is the CANONICAL picker cluster: `AgentSessionControls` from
|
|
38
|
+
* `/web-react` (its model menu IS the canonical `ModelPicker`). The legacy
|
|
39
|
+
* sandbox-ui adapter that used to back this prop was removed; the props
|
|
40
|
+
* mapping for migrating a stored selection lives in
|
|
41
|
+
* `docs/ui-picker-canon.md`.
|
|
185
42
|
*/
|
|
186
|
-
agent?:
|
|
43
|
+
agent?: AgentSessionControlsProps;
|
|
187
44
|
/**
|
|
188
45
|
* Product-specific behavioral controls docked on the LEFT. A mode is an
|
|
189
46
|
* on/off switch, not a value picker, so it sits apart from agent identity.
|
|
@@ -244,8 +101,8 @@ interface EntryComposerProps {
|
|
|
244
101
|
* themselves.
|
|
245
102
|
*
|
|
246
103
|
* The docked in-thread composer is NOT this component — it renders
|
|
247
|
-
* `AgentComposer` directly with the same
|
|
248
|
-
* a docked composer has a transcript above it and no hero layout.
|
|
104
|
+
* `AgentComposer` directly with the same canonical `AgentSessionControls`,
|
|
105
|
+
* because a docked composer has a transcript above it and no hero layout.
|
|
249
106
|
*/
|
|
250
107
|
declare function EntryComposer({ heading, subheading, placeholder, initialValue, sendLabel, disabled, agent, modes, planMode, uploadUrl, accept, onAttachmentError, onRejectFiles, mentions, mentionPopoverClassName, footer, ready, onSubmit, className, composerClassName, maxWidth, }: EntryComposerProps): react.JSX.Element;
|
|
251
108
|
|
|
@@ -263,4 +120,4 @@ interface ComposerModeControlsProps {
|
|
|
263
120
|
*/
|
|
264
121
|
declare function ComposerModeControls({ planMode }: ComposerModeControlsProps): react.JSX.Element | null;
|
|
265
122
|
|
|
266
|
-
export {
|
|
123
|
+
export { ComposerModeControls, type ComposerModeControlsProps, type ComposerPlanModeSelection, EntryComposer, type EntryComposerProps };
|
package/dist/chat-react/index.js
CHANGED
|
@@ -1,129 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AgentSessionControls,
|
|
2
3
|
useComposerAttachments
|
|
3
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-L7FHRORY.js";
|
|
4
5
|
import {
|
|
5
6
|
ATTACHMENT_ACCEPT
|
|
6
7
|
} from "../chunk-CCVG2TL6.js";
|
|
7
8
|
import "../chunk-KWXUBMXU.js";
|
|
8
|
-
import
|
|
9
|
-
isChatCapableModel
|
|
10
|
-
} from "../chunk-OAGD7RDM.js";
|
|
11
|
-
|
|
12
|
-
// src/chat-react/composer-agent-controls.tsx
|
|
13
|
-
import { useCallback, useMemo, useRef } from "react";
|
|
14
|
-
import {
|
|
15
|
-
AgentSessionControls
|
|
16
|
-
} from "@tangle-network/sandbox-ui/chat";
|
|
17
|
-
import { canonicalModelId } from "@tangle-network/sandbox-ui/dashboard";
|
|
18
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
-
function harnessesForContext(available, context) {
|
|
20
|
-
if (!available) return available;
|
|
21
|
-
if (context !== "chat") return available;
|
|
22
|
-
return available.filter((h) => h !== "cli-base");
|
|
23
|
-
}
|
|
24
|
-
function modelsForContext(models, selectedId, context) {
|
|
25
|
-
if (context !== "chat") return models;
|
|
26
|
-
return models.filter(
|
|
27
|
-
(m) => m.id === selectedId || canonicalModelId(m) === selectedId || isChatCapableModel(m)
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
function ComposerAgentControls({
|
|
31
|
-
model,
|
|
32
|
-
harness,
|
|
33
|
-
profile,
|
|
34
|
-
effort,
|
|
35
|
-
className,
|
|
36
|
-
context = "chat",
|
|
37
|
-
layout = "responsive",
|
|
38
|
-
menuPlacement,
|
|
39
|
-
filterModelsToHarness = true
|
|
40
|
-
}) {
|
|
41
|
-
const canonicalSelected = useMemo(() => {
|
|
42
|
-
const selected = model.models.find((m) => m.id === model.value);
|
|
43
|
-
if (selected) return canonicalModelId(selected);
|
|
44
|
-
return model.value;
|
|
45
|
-
}, [model.value, model.models]);
|
|
46
|
-
const offeredModels = useMemo(
|
|
47
|
-
() => modelsForContext(model.models, model.value, context),
|
|
48
|
-
[model.models, model.value, context]
|
|
49
|
-
);
|
|
50
|
-
const harnessSwitching = useRef(false);
|
|
51
|
-
const harnessOnChange = harness?.onChange;
|
|
52
|
-
const onHarnessChange = useCallback(
|
|
53
|
-
(next) => {
|
|
54
|
-
harnessSwitching.current = true;
|
|
55
|
-
harnessOnChange?.(next);
|
|
56
|
-
queueMicrotask(() => {
|
|
57
|
-
harnessSwitching.current = false;
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
[harnessOnChange]
|
|
61
|
-
);
|
|
62
|
-
const modelOnChange = model.onChange;
|
|
63
|
-
const onModelChange = useCallback(
|
|
64
|
-
(canonical) => {
|
|
65
|
-
if (harnessSwitching.current) return;
|
|
66
|
-
modelOnChange(canonical);
|
|
67
|
-
},
|
|
68
|
-
[modelOnChange]
|
|
69
|
-
);
|
|
70
|
-
const sharedProps = {
|
|
71
|
-
context,
|
|
72
|
-
menuPlacement,
|
|
73
|
-
filterModelsToHarness,
|
|
74
|
-
model: {
|
|
75
|
-
value: canonicalSelected,
|
|
76
|
-
onChange: onModelChange,
|
|
77
|
-
models: offeredModels,
|
|
78
|
-
loading: model.loading,
|
|
79
|
-
disabled: model.disabled
|
|
80
|
-
},
|
|
81
|
-
harness: harness ? {
|
|
82
|
-
value: harness.value,
|
|
83
|
-
onChange: onHarnessChange,
|
|
84
|
-
available: harnessesForContext(harness.available, context),
|
|
85
|
-
disabled: harness.disabled,
|
|
86
|
-
locked: harness.locked,
|
|
87
|
-
lockReason: harness.lockReason,
|
|
88
|
-
onNewChat: harness.onNewChat
|
|
89
|
-
} : void 0,
|
|
90
|
-
profile,
|
|
91
|
-
reasoning: effort ? {
|
|
92
|
-
value: effort.value,
|
|
93
|
-
onChange: effort.onChange,
|
|
94
|
-
available: effort.available,
|
|
95
|
-
disabled: effort.disabled
|
|
96
|
-
} : void 0
|
|
97
|
-
};
|
|
98
|
-
if (layout === "responsive") {
|
|
99
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
|
-
/* @__PURE__ */ jsx(
|
|
101
|
-
AgentSessionControls,
|
|
102
|
-
{
|
|
103
|
-
...sharedProps,
|
|
104
|
-
className: className ? `hidden sm:flex ${className}` : "hidden sm:flex",
|
|
105
|
-
layout: "inline"
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
/* @__PURE__ */ jsx(
|
|
109
|
-
AgentSessionControls,
|
|
110
|
-
{
|
|
111
|
-
...sharedProps,
|
|
112
|
-
className: className ? `flex sm:hidden ${className}` : "flex sm:hidden",
|
|
113
|
-
layout: "gear"
|
|
114
|
-
}
|
|
115
|
-
)
|
|
116
|
-
] });
|
|
117
|
-
}
|
|
118
|
-
return /* @__PURE__ */ jsx(
|
|
119
|
-
AgentSessionControls,
|
|
120
|
-
{
|
|
121
|
-
className,
|
|
122
|
-
...sharedProps,
|
|
123
|
-
layout
|
|
124
|
-
}
|
|
125
|
-
);
|
|
126
|
-
}
|
|
9
|
+
import "../chunk-TH7L265V.js";
|
|
127
10
|
|
|
128
11
|
// src/chat-react/entry-composer.tsx
|
|
129
12
|
import { useState } from "react";
|
|
@@ -132,10 +15,10 @@ import { AgentComposer } from "@tangle-network/sandbox-ui/chat";
|
|
|
132
15
|
// src/chat-react/composer-mode-controls.tsx
|
|
133
16
|
import { ListChecks } from "lucide-react";
|
|
134
17
|
import { cn } from "@tangle-network/sandbox-ui/utils";
|
|
135
|
-
import { jsx
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
136
19
|
function ComposerModeControls({ planMode }) {
|
|
137
20
|
if (!planMode) return null;
|
|
138
|
-
return /* @__PURE__ */
|
|
21
|
+
return /* @__PURE__ */ jsxs(
|
|
139
22
|
"button",
|
|
140
23
|
{
|
|
141
24
|
type: "button",
|
|
@@ -152,7 +35,7 @@ function ComposerModeControls({ planMode }) {
|
|
|
152
35
|
planMode.saving && "opacity-60"
|
|
153
36
|
),
|
|
154
37
|
children: [
|
|
155
|
-
/* @__PURE__ */
|
|
38
|
+
/* @__PURE__ */ jsx(ListChecks, { className: "h-3.5 w-3.5" }),
|
|
156
39
|
"Plan"
|
|
157
40
|
]
|
|
158
41
|
}
|
|
@@ -160,7 +43,7 @@ function ComposerModeControls({ planMode }) {
|
|
|
160
43
|
}
|
|
161
44
|
|
|
162
45
|
// src/chat-react/entry-composer.tsx
|
|
163
|
-
import { jsx as
|
|
46
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
164
47
|
function EntryComposer({
|
|
165
48
|
heading,
|
|
166
49
|
subheading,
|
|
@@ -204,14 +87,14 @@ function EntryComposer({
|
|
|
204
87
|
attachments.clear();
|
|
205
88
|
mentions?.clearMentions();
|
|
206
89
|
}
|
|
207
|
-
return /* @__PURE__ */
|
|
90
|
+
return /* @__PURE__ */ jsx2(
|
|
208
91
|
"div",
|
|
209
92
|
{
|
|
210
93
|
className: className ?? "relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5",
|
|
211
|
-
children: /* @__PURE__ */
|
|
212
|
-
heading ? /* @__PURE__ */
|
|
213
|
-
subheading ? /* @__PURE__ */
|
|
214
|
-
/* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ jsxs2("div", { className: "w-full", style: { maxWidth }, children: [
|
|
95
|
+
heading ? /* @__PURE__ */ jsx2("h2", { className: "mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground", children: heading }) : null,
|
|
96
|
+
subheading ? /* @__PURE__ */ jsx2("p", { className: "mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground", children: subheading }) : heading ? /* @__PURE__ */ jsx2("div", { className: "mb-7" }) : null,
|
|
97
|
+
/* @__PURE__ */ jsx2(
|
|
215
98
|
AgentComposer,
|
|
216
99
|
{
|
|
217
100
|
className: composerClassName ?? "vt-composer",
|
|
@@ -236,17 +119,16 @@ function EntryComposer({
|
|
|
236
119
|
// brighter overlay tone the component defaults to.
|
|
237
120
|
popoverClassName: mentionPopoverClassName ?? "bg-surface-container-low border-[var(--md3-outline-variant)]"
|
|
238
121
|
} : void 0,
|
|
239
|
-
controls: modes ?? /* @__PURE__ */
|
|
240
|
-
trailing: agent ? /* @__PURE__ */
|
|
122
|
+
controls: modes ?? /* @__PURE__ */ jsx2(ComposerModeControls, { planMode }),
|
|
123
|
+
trailing: agent ? /* @__PURE__ */ jsx2(AgentSessionControls, { ...agent }) : void 0
|
|
241
124
|
}
|
|
242
125
|
),
|
|
243
|
-
footer ? /* @__PURE__ */
|
|
126
|
+
footer ? /* @__PURE__ */ jsx2("div", { className: "mt-7", children: footer }) : null
|
|
244
127
|
] })
|
|
245
128
|
}
|
|
246
129
|
);
|
|
247
130
|
}
|
|
248
131
|
export {
|
|
249
|
-
ComposerAgentControls,
|
|
250
132
|
ComposerModeControls,
|
|
251
133
|
EntryComposer
|
|
252
134
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chat-react/composer-agent-controls.tsx","../../src/chat-react/entry-composer.tsx","../../src/chat-react/composer-mode-controls.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef } from 'react'\nimport {\n AgentSessionControls,\n type AgentSessionProfileControl,\n} from '@tangle-network/sandbox-ui/chat'\nimport { canonicalModelId } from '@tangle-network/sandbox-ui/dashboard'\nimport type { ModelInfo } from '@tangle-network/sandbox-ui/dashboard'\n// Browser-safe: model-catalog.ts imports nothing. Taken from the source module\n// rather than the `/catalog` subpath so this stays one build graph.\nimport { isChatCapableModel, type RouterModel } from '../runtime/model-catalog'\nimport type { HarnessType, ReasoningEffort, ReasoningLevel } from './types'\n\n/**\n * The model half of the composer's agent identity. `models` is the catalog the\n * picker renders; `value` may be a bare or a canonical (provider-prefixed) id —\n * the adapter maps it to canonical form for the picker and hands the canonical\n * id back to `onChange`, so a product that stores bare ids only has to accept\n * canonical ones in its setter.\n */\nexport interface ComposerModelSelection {\n value: string\n onChange: (next: string) => void\n models: ModelInfo[]\n loading?: boolean\n disabled?: boolean\n}\n\nexport interface ComposerHarnessSelection {\n value: HarnessType\n onChange: (next: HarnessType) => void\n /** Restrict the offered backends. Omit to take the context default. */\n available?: ReadonlyArray<HarnessType>\n disabled?: boolean\n /**\n * A thread's sidecar session is pinned to one harness at its first message,\n * so once the thread has messages the picker locks. With `onNewChat` the\n * locked chip becomes the informative lock (explains the binding + a button\n * to start a fresh session on another backend).\n */\n locked?: boolean\n lockReason?: string\n onNewChat?: () => void\n}\n\nexport interface ComposerEffortSelection {\n value: ReasoningLevel\n onChange: (next: ReasoningLevel) => void\n /**\n * Depths this product's backend actually applies. Omit for the default set.\n * `auto` is NOT a member: the picker adds that sentinel itself, so listing it\n * here would offer it twice.\n */\n available?: ReadonlyArray<ReasoningEffort>\n disabled?: boolean\n}\n\n/**\n * A selectable named bundle of persona and tool groups. The product resolves\n * the selected id to the runtime AgentProfile before creating or continuing a\n * session; the composer only needs the display catalog and persistence hooks.\n */\nexport type ComposerProfileSelection = AgentSessionProfileControl\n\n/**\n * @deprecated Part of the legacy sandbox-ui adapter — see\n * {@link ComposerAgentControls}. New code uses `AgentSessionControlsProps`\n * from `@tangle-network/agent-app/web-react`.\n */\nexport interface ComposerAgentControlsProps {\n model: ComposerModelSelection\n /** Omit on a router-backed composer: a direct model call has no CLI backend,\n * so the picker would be inert. Model + effort still render. */\n harness?: ComposerHarnessSelection\n /** Universal named agent profile: persona + tool groups, independent of backend. */\n profile?: ComposerProfileSelection\n effort?: ComposerEffortSelection\n className?: string\n /**\n * Which surface the controls live on. `'chat'` (the default) drops the\n * shell-only `cli-base` backend — it has no conversational agent — and tells\n * the shared control to use its chat-only default set. It also drops models\n * that cannot serve a text turn (image / TTS / embedding / audio-in) from\n * the model picker. `'all'` keeps every backend and every model for non-chat\n * / scheduled surfaces.\n */\n context?: 'chat' | 'all'\n /**\n * Trigger layout. `'responsive'` (the default) lays the pickers out in a\n * row on wider screens and uses a compact gear on narrow screens. `'combined'`\n * collapses the pickers behind one\n * labeled trigger reading the current selection (`harness · model · effort`).\n * `'gear'` uses an anonymous gear icon instead of the label; `'inline'` lays\n * the pickers out in a row. `'responsive'` uses the labeled trigger on wider\n * screens and the compact gear on narrow screens so Send stays reachable.\n */\n layout?: 'inline' | 'gear' | 'combined' | 'responsive'\n /**\n * Where the pickers open. `'down'` pins them downward for a composer floating\n * in open space (the centered entry surface) so a tall menu can't flip up over\n * the heading. Defaults to `'auto'` (collision-aware).\n */\n menuPlacement?: 'auto' | 'down'\n /**\n * Restrict the model catalog to what the selected harness can run instead of\n * letting an incompatible model snap the harness. ON by default: a workspace\n * harness is sandbox-bound, and a silent swap tears down and recreates the\n * sandbox. Turn OFF only on a model-first surface with no bound sandbox.\n */\n filterModelsToHarness?: boolean\n}\n\n/**\n * The chat copilot must never offer `cli-base`: it is a shell-only backend with\n * no conversational agent. sandbox-ui's `context='chat'` drops it from the\n * default set, but an explicit `available` list (a plan-filtered one may include\n * `cli-base`) overrides that trim. So strip it here at the data layer whenever\n * the surface is chat — no `cli-base` ever reaches render.\n */\nfunction harnessesForContext(\n available: ReadonlyArray<HarnessType> | undefined,\n context: 'chat' | 'all',\n): ReadonlyArray<HarnessType> | undefined {\n if (!available) return available\n if (context !== 'chat') return available\n return available.filter((h) => h !== 'cli-base')\n}\n\n/**\n * The model-side twin of the harness trim above. Products hand the picker the\n * router's raw `/v1/models`, which lists every routeable endpoint — image\n * generators, TTS voices, embedding models, audio-in transcription. None of\n * them can serve a chat turn, and on the live catalogue that is 35 of 504\n * entries a user could pick and get a broken turn from.\n *\n * `context: 'all'` keeps the full list for non-chat surfaces.\n *\n * The selected model is never filtered out, whatever its modalities say: a\n * product may deliberately pin something exotic, and a picker whose own value\n * is missing from its list renders as blank.\n *\n * That escape hatch has to recognise BOTH spellings of the id. A product stores\n * whatever `onChange` handed it, and `onChange` hands back the canonical\n * provider-prefixed form — so a selection pinned before this trim existed is\n * stored as `openai/gpt-image-1` while the catalog entry's own `id` is the bare\n * `gpt-image-1`. Matching only the bare form drops exactly the model the guard\n * exists to keep, and the picker renders unset, which reads as data loss.\n */\nfunction modelsForContext(\n models: ModelInfo[],\n selectedId: string,\n context: 'chat' | 'all',\n): ModelInfo[] {\n if (context !== 'chat') return models\n return models.filter(\n (m) =>\n m.id === selectedId ||\n canonicalModelId(m) === selectedId ||\n isChatCapableModel(m as unknown as RouterModel),\n )\n}\n\n/**\n * The composer's agent-identity control: profile, backend, model, and\n * reasoning effort, over sandbox-ui's `AgentSessionControls`.\n *\n * @deprecated Use `AgentSessionControls` from `@tangle-network/agent-app/web-react`\n * — the canonical picker cluster (its model menu IS the canonical\n * `ModelPicker`). sandbox-ui's `AgentSessionControls` and `dashboard/ModelPicker`\n * are legacy: deprecated, frozen, and removed at sandbox-ui's next major. This\n * adapter stays exported and fully functional because removing a published\n * symbol is a major, but it renders the OLD design; new code must not adopt\n * it. Props mapping for the migration lives in `docs/ui-picker-canon.md`.\n *\n * This is an ADAPTER, not a second implementation. The harness↔model coherence\n * policy is the shared one (the catalog is filtered to the\n * selected harness, the effort re-clamps to what the harness/model pair\n * supports, and a harness that ignores a picker is marked with its dead control\n * disabled). What lives here is the app-shell wiring every product otherwise\n * re-derives:\n *\n * - MODEL-ID BOUNDARY. Products store bare ids (what a chat send body expects)\n * while the picker matches on canonical provider-prefixed ids. The canonical\n * form goes in; the canonical id comes back out to `onChange`.\n * - HARNESS-SNAP SUPPRESSION. The shared control snaps the model whenever the\n * harness changes, calling the model `onChange` synchronously with a default\n * compatible model. A product that remembers a per-harness pick must ignore\n * that snap, or it both discards the remembered pick and persists it under\n * the wrong harness's key. The guard is armed for exactly the synchronous\n * window of a harness change and disarmed on the next microtask, so it can\n * never stay stuck — a later genuine pick is never swallowed, even when the\n * harness change was a no-op.\n * - CHAT-CONTEXT TRIM. On a chat surface, `cli-base` never reaches the harness\n * picker and a non-chat model never reaches the model picker. Products feed\n * the picker the router's raw model list, where 35 of 504 entries (image,\n * TTS, embeddings, audio-in transcription) cannot serve a chat turn at all.\n *\n * Behavioral modes (a plan toggle) are NOT agent identity and do not belong\n * here — they dock on the other side of the composer via `controls`.\n */\nexport function ComposerAgentControls({\n model,\n harness,\n profile,\n effort,\n className,\n context = 'chat',\n layout = 'responsive',\n menuPlacement,\n filterModelsToHarness = true,\n}: ComposerAgentControlsProps) {\n const canonicalSelected = useMemo(() => {\n const selected = model.models.find((m) => m.id === model.value)\n if (selected) return canonicalModelId(selected)\n // Already canonical, or the catalog has not loaded — pass through unchanged\n // so a provider-prefixed id still matches once models arrive.\n return model.value\n }, [model.value, model.models])\n\n const offeredModels = useMemo(\n () => modelsForContext(model.models, model.value, context),\n [model.models, model.value, context],\n )\n\n const harnessSwitching = useRef(false)\n const harnessOnChange = harness?.onChange\n const onHarnessChange = useCallback(\n (next: HarnessType) => {\n harnessSwitching.current = true\n harnessOnChange?.(next)\n queueMicrotask(() => {\n harnessSwitching.current = false\n })\n },\n [harnessOnChange],\n )\n\n const modelOnChange = model.onChange\n const onModelChange = useCallback(\n (canonical: string) => {\n if (harnessSwitching.current) return\n modelOnChange(canonical)\n },\n [modelOnChange],\n )\n\n const sharedProps = {\n context,\n menuPlacement,\n filterModelsToHarness,\n model: {\n value: canonicalSelected,\n onChange: onModelChange,\n models: offeredModels,\n loading: model.loading,\n disabled: model.disabled,\n },\n harness: harness\n ? {\n value: harness.value,\n onChange: onHarnessChange,\n available: harnessesForContext(harness.available, context),\n disabled: harness.disabled,\n locked: harness.locked,\n lockReason: harness.lockReason,\n onNewChat: harness.onNewChat,\n }\n : undefined,\n profile,\n reasoning: effort\n ? {\n value: effort.value,\n onChange: effort.onChange,\n available: effort.available,\n disabled: effort.disabled,\n }\n : undefined,\n }\n\n if (layout === 'responsive') {\n return (\n <>\n <AgentSessionControls\n {...sharedProps}\n className={className ? `hidden sm:flex ${className}` : 'hidden sm:flex'}\n layout=\"inline\"\n />\n <AgentSessionControls\n {...sharedProps}\n className={className ? `flex sm:hidden ${className}` : 'flex sm:hidden'}\n layout=\"gear\"\n />\n </>\n )\n }\n\n return (\n <AgentSessionControls\n className={className}\n {...sharedProps}\n layout={layout}\n />\n )\n}\n","import { useState, type ReactNode } from 'react'\nimport { AgentComposer } from '@tangle-network/sandbox-ui/chat'\nimport { ATTACHMENT_ACCEPT, useComposerAttachments } from '../web-react/use-composer-attachments'\nimport type { UseFileMentionsResult } from '../web-react/use-file-mentions'\nimport type { ChatAttachmentInput, FileMention } from '../chat-routes/wire'\nimport {\n ComposerAgentControls,\n type ComposerAgentControlsProps,\n} from './composer-agent-controls'\nimport { ComposerModeControls } from './composer-mode-controls'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface EntryComposerProps {\n /** The one line above the input. Domain copy — always a product parameter. */\n heading?: ReactNode\n /** Optional supporting line under the heading. */\n subheading?: ReactNode\n placeholder?: string\n initialValue?: string\n sendLabel?: string\n disabled?: boolean\n /**\n * Agent identity (profile/backend/model/effort). Pass it and the control row renders;\n * omit it and the composer ships without one. Omitting is a real choice for a\n * surface with nothing to choose — it should never be an oversight, which is\n * why this is one prop rather than a free-form slot a caller can forget.\n *\n * NOTE: this prop routes through the deprecated `ComposerAgentControls`\n * adapter (sandbox-ui's legacy strip). It keeps working until sandbox-ui's\n * next major; the canonical picker cluster is `AgentSessionControls` from\n * `/web-react` (see `docs/ui-picker-canon.md`).\n */\n agent?: ComposerAgentControlsProps\n /**\n * Product-specific behavioral controls docked on the LEFT. A mode is an\n * on/off switch, not a value picker, so it sits apart from agent identity.\n * For the standard plan-approval control, prefer `planMode`.\n */\n modes?: ReactNode\n /**\n * Standard plan-approval mode. Pass only when the selected backend supports\n * it; omitted means the control is not rendered.\n */\n planMode?: ComposerPlanModeSelection\n /**\n * Upload endpoint for staged attachments. Omit and the attach affordance is\n * hidden — a composer with no place to put a file must not offer one.\n */\n uploadUrl?: string\n accept?: string\n onAttachmentError?: (reason: string) => void\n onRejectFiles?: Parameters<typeof AgentComposer>[0]['onRejectFiles']\n /**\n * `@`-file mentions. The hook itself is shared (`useFileMentions`), but the\n * cold-box policy around it — whether pressing `@` is worth starting a\n * sandbox — is a per-surface product call, so the result is injected rather\n * than created here.\n */\n mentions?: UseFileMentionsResult\n mentionPopoverClassName?: string\n /** Rendered under the composer: suggestion pills, an onboarding nudge, a\n * disclaimer. All domain copy. */\n footer?: ReactNode\n /** Block submit until a persisted selection has resolved against the catalog,\n * so the first turn cannot go out under the wrong model. Defaults to true. */\n ready?: boolean\n onSubmit: (\n prompt: string,\n attachments: ChatAttachmentInput[],\n mentions: FileMention[],\n ) => void\n className?: string\n composerClassName?: string\n /** Max width of the centered column. Defaults to the fleet's 820px. */\n maxWidth?: number\n}\n\n/**\n * `EntryComposer` — the centered \"what do you want to work on?\" surface a\n * product shows before a conversation exists (a new thread, an empty session,\n * a workspace overview).\n *\n * It exists because three products each grew their own, and they drifted into\n * three different capability sets rather than three different looks: one lost\n * the attach button, one lost the model and effort pickers entirely, one\n * hand-rolled a `<textarea>` and wired pickers from three separate packages\n * into one row. The composer, the pickers, the attachment queue and the mention\n * index were all ALREADY shared — what was not shared was the assembly, so\n * every product re-derived which controls an entry surface gets, and each\n * re-derivation dropped a different one.\n *\n * Mechanism lives here: layout, the attachment queue, the mention wiring, the\n * submit gate (nothing sends while an upload is pending or failed, or before\n * the model selection has resolved). Domain stays a parameter: `heading`,\n * `placeholder`, `footer` (suggestion pills, disclaimers) and the selections\n * themselves.\n *\n * The docked in-thread composer is NOT this component — it renders\n * `AgentComposer` directly with the same {@link ComposerAgentControls}, because\n * a docked composer has a transcript above it and no hero layout.\n */\nexport function EntryComposer({\n heading,\n subheading,\n placeholder,\n initialValue = '',\n sendLabel,\n disabled,\n agent,\n modes,\n planMode,\n uploadUrl,\n accept = ATTACHMENT_ACCEPT,\n onAttachmentError,\n onRejectFiles,\n mentions,\n mentionPopoverClassName,\n footer,\n ready = true,\n onSubmit,\n className,\n composerClassName,\n maxWidth = 820,\n}: EntryComposerProps) {\n const [value, setValue] = useState(initialValue)\n const attachments = useComposerAttachments({\n uploadUrl,\n enabled: !!uploadUrl,\n onReject: onAttachmentError,\n onError: onAttachmentError,\n })\n\n function submit(prompt: string) {\n if (!ready || disabled) return\n const next = prompt.trim()\n // A pending or failed upload must not send: the turn would reference an\n // attachment the store does not have. Surface why rather than no-op'ing.\n if (attachments.hasPending || attachments.hasError) {\n if (attachments.blockReason) onAttachmentError?.(attachments.blockReason)\n return\n }\n if (!next && attachments.references.length === 0) return\n onSubmit(next, attachments.references, mentions?.mentions ?? [])\n setValue('')\n attachments.clear()\n mentions?.clearMentions()\n }\n\n return (\n <div\n className={\n className ??\n 'relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5'\n }\n >\n <div className=\"w-full\" style={{ maxWidth }}>\n {heading ? (\n <h2 className=\"mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground\">\n {heading}\n </h2>\n ) : null}\n {subheading ? (\n <p className=\"mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground\">\n {subheading}\n </p>\n ) : (\n heading ? <div className=\"mb-7\" /> : null\n )}\n <AgentComposer\n className={composerClassName ?? 'vt-composer'}\n value={value}\n onChange={setValue}\n onSubmit={() => submit(value)}\n placeholder={placeholder}\n sendLabel={sendLabel}\n disabled={disabled}\n autoFocus\n canSubmitAttachmentsOnly\n accept={accept}\n attachments={attachments.composerFiles}\n onAttach={uploadUrl ? (files) => void attachments.addFiles(files) : undefined}\n onRejectFiles={onRejectFiles}\n onRemoveFile={attachments.removeAttachment}\n onRetryFile={attachments.retry}\n mention={\n mentions\n ? {\n ...mentions.mention,\n // Sidebar tone + hairline so the popover reads as app chrome\n // (the same treatment the picker menus get) instead of the\n // brighter overlay tone the component defaults to.\n popoverClassName:\n mentionPopoverClassName ??\n 'bg-surface-container-low border-[var(--md3-outline-variant)]',\n }\n : undefined\n }\n controls={modes ?? <ComposerModeControls planMode={planMode} />}\n trailing={\n agent ? <ComposerAgentControls menuPlacement=\"down\" {...agent} /> : undefined\n }\n />\n {footer ? <div className=\"mt-7\">{footer}</div> : null}\n </div>\n </div>\n )\n}\n","import { ListChecks } from 'lucide-react'\nimport { cn } from '@tangle-network/sandbox-ui/utils'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface ComposerModeControlsProps {\n /**\n * Plan-approval mode. Products pass it only when the selected backend can\n * propose a plan and wait for approval; omitted means nothing renders.\n */\n planMode?: ComposerPlanModeSelection\n}\n\n/**\n * The shared plan-mode toggle for the left side of an agent composer.\n * Plan mode is a behavioral switch, not part of the profile/backend/model/\n * thinking identity controls on the right.\n */\nexport function ComposerModeControls({ planMode }: ComposerModeControlsProps) {\n if (!planMode) return null\n\n return (\n <button\n type=\"button\"\n aria-pressed={planMode.enabled}\n disabled={planMode.saving}\n onClick={() => planMode.setEnabled(!planMode.enabled)}\n title=\"Plan mode: the agent proposes a plan you approve before it executes\"\n className={cn(\n // Inset ring: this chip sits in a composer row that clips its overflow,\n // so an outward ring loses three of its four sides. Only the offset is\n // overridden — width and colour stay with the tokens.\n 'inline-flex h-7 items-center gap-1 rounded-full border px-2.5 text-xs transition-colors focus-visible:[outline-offset:-2px]',\n planMode.enabled\n ? 'border-primary/50 bg-primary/10 text-primary'\n : 'border-border bg-transparent text-muted-foreground hover:text-foreground',\n planMode.saving && 'opacity-60',\n )}\n >\n <ListChecks className=\"h-3.5 w-3.5\" />\n Plan\n </button>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa,SAAS,cAAc;AAC7C;AAAA,EACE;AAAA,OAEK;AACP,SAAS,wBAAwB;AAmR3B,mBACE,KADF;AAlKN,SAAS,oBACP,WACA,SACwC;AACxC,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,YAAY,OAAQ,QAAO;AAC/B,SAAO,UAAU,OAAO,CAAC,MAAM,MAAM,UAAU;AACjD;AAsBA,SAAS,iBACP,QACA,YACA,SACa;AACb,MAAI,YAAY,OAAQ,QAAO;AAC/B,SAAO,OAAO;AAAA,IACZ,CAAC,MACC,EAAE,OAAO,cACT,iBAAiB,CAAC,MAAM,cACxB,mBAAmB,CAA2B;AAAA,EAClD;AACF;AAwCO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,SAAS;AAAA,EACT;AAAA,EACA,wBAAwB;AAC1B,GAA+B;AAC7B,QAAM,oBAAoB,QAAQ,MAAM;AACtC,UAAM,WAAW,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,KAAK;AAC9D,QAAI,SAAU,QAAO,iBAAiB,QAAQ;AAG9C,WAAO,MAAM;AAAA,EACf,GAAG,CAAC,MAAM,OAAO,MAAM,MAAM,CAAC;AAE9B,QAAM,gBAAgB;AAAA,IACpB,MAAM,iBAAiB,MAAM,QAAQ,MAAM,OAAO,OAAO;AAAA,IACzD,CAAC,MAAM,QAAQ,MAAM,OAAO,OAAO;AAAA,EACrC;AAEA,QAAM,mBAAmB,OAAO,KAAK;AACrC,QAAM,kBAAkB,SAAS;AACjC,QAAM,kBAAkB;AAAA,IACtB,CAAC,SAAsB;AACrB,uBAAiB,UAAU;AAC3B,wBAAkB,IAAI;AACtB,qBAAe,MAAM;AACnB,yBAAiB,UAAU;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,gBAAgB,MAAM;AAC5B,QAAM,gBAAgB;AAAA,IACpB,CAAC,cAAsB;AACrB,UAAI,iBAAiB,QAAS;AAC9B,oBAAc,SAAS;AAAA,IACzB;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AAEA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,SAAS,MAAM;AAAA,MACf,UAAU,MAAM;AAAA,IAClB;AAAA,IACA,SAAS,UACL;AAAA,MACE,OAAO,QAAQ;AAAA,MACf,UAAU;AAAA,MACV,WAAW,oBAAoB,QAAQ,WAAW,OAAO;AAAA,MACzD,UAAU,QAAQ;AAAA,MAClB,QAAQ,QAAQ;AAAA,MAChB,YAAY,QAAQ;AAAA,MACpB,WAAW,QAAQ;AAAA,IACrB,IACA;AAAA,IACJ;AAAA,IACA,WAAW,SACP;AAAA,MACE,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,IACnB,IACA;AAAA,EACN;AAEA,MAAI,WAAW,cAAc;AAC3B,WACE,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,WAAW,YAAY,kBAAkB,SAAS,KAAK;AAAA,UACvD,QAAO;AAAA;AAAA,MACT;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,WAAW,YAAY,kBAAkB,SAAS,KAAK;AAAA,UACvD,QAAO;AAAA;AAAA,MACT;AAAA,OACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACC,GAAG;AAAA,MACJ;AAAA;AAAA,EACF;AAEJ;;;AC9SA,SAAS,gBAAgC;AACzC,SAAS,qBAAqB;;;ACD9B,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AAoBf,SAiBE,OAAAA,MAjBF,QAAAC,aAAA;AAJG,SAAS,qBAAqB,EAAE,SAAS,GAA8B;AAC5E,MAAI,CAAC,SAAU,QAAO;AAEtB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,gBAAc,SAAS;AAAA,MACvB,UAAU,SAAS;AAAA,MACnB,SAAS,MAAM,SAAS,WAAW,CAAC,SAAS,OAAO;AAAA,MACpD,OAAM;AAAA,MACN,WAAW;AAAA;AAAA;AAAA;AAAA,QAIT;AAAA,QACA,SAAS,UACL,iDACA;AAAA,QACJ,SAAS,UAAU;AAAA,MACrB;AAAA,MAEA;AAAA,wBAAAD,KAAC,cAAW,WAAU,eAAc;AAAA,QAAE;AAAA;AAAA;AAAA,EAExC;AAEJ;;;ADiHM,SAEI,OAAAE,MAFJ,QAAAC,aAAA;AAtDC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAuB;AACrB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,cAAc,uBAAuB;AAAA,IACzC;AAAA,IACA,SAAS,CAAC,CAAC;AAAA,IACX,UAAU;AAAA,IACV,SAAS;AAAA,EACX,CAAC;AAED,WAAS,OAAO,QAAgB;AAC9B,QAAI,CAAC,SAAS,SAAU;AACxB,UAAM,OAAO,OAAO,KAAK;AAGzB,QAAI,YAAY,cAAc,YAAY,UAAU;AAClD,UAAI,YAAY,YAAa,qBAAoB,YAAY,WAAW;AACxE;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,YAAY,WAAW,WAAW,EAAG;AAClD,aAAS,MAAM,YAAY,YAAY,UAAU,YAAY,CAAC,CAAC;AAC/D,aAAS,EAAE;AACX,gBAAY,MAAM;AAClB,cAAU,cAAc;AAAA,EAC1B;AAEA,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WACE,aACA;AAAA,MAGF,0BAAAC,MAAC,SAAI,WAAU,UAAS,OAAO,EAAE,SAAS,GACvC;AAAA,kBACC,gBAAAD,KAAC,QAAG,WAAU,8EACX,mBACH,IACE;AAAA,QACH,aACC,gBAAAA,KAAC,OAAE,WAAU,mFACV,sBACH,IAEA,UAAU,gBAAAA,KAAC,SAAI,WAAU,QAAO,IAAK;AAAA,QAEvC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,qBAAqB;AAAA,YAChC;AAAA,YACA,UAAU;AAAA,YACV,UAAU,MAAM,OAAO,KAAK;AAAA,YAC5B;AAAA,YACA;AAAA,YACA;AAAA,YACA,WAAS;AAAA,YACT,0BAAwB;AAAA,YACxB;AAAA,YACA,aAAa,YAAY;AAAA,YACzB,UAAU,YAAY,CAAC,UAAU,KAAK,YAAY,SAAS,KAAK,IAAI;AAAA,YACpE;AAAA,YACA,cAAc,YAAY;AAAA,YAC1B,aAAa,YAAY;AAAA,YACzB,SACE,WACI;AAAA,cACE,GAAG,SAAS;AAAA;AAAA;AAAA;AAAA,cAIZ,kBACE,2BACA;AAAA,YACJ,IACA;AAAA,YAEN,UAAU,SAAS,gBAAAA,KAAC,wBAAqB,UAAoB;AAAA,YAC7D,UACE,QAAQ,gBAAAA,KAAC,yBAAsB,eAAc,QAAQ,GAAG,OAAO,IAAK;AAAA;AAAA,QAExE;AAAA,QACC,SAAS,gBAAAA,KAAC,SAAI,WAAU,QAAQ,kBAAO,IAAS;AAAA,SACnD;AAAA;AAAA,EACF;AAEJ;","names":["jsx","jsxs","jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/chat-react/entry-composer.tsx","../../src/chat-react/composer-mode-controls.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react'\nimport { AgentComposer } from '@tangle-network/sandbox-ui/chat'\nimport { ATTACHMENT_ACCEPT, useComposerAttachments } from '../web-react/use-composer-attachments'\nimport type { UseFileMentionsResult } from '../web-react/use-file-mentions'\nimport type { ChatAttachmentInput, FileMention } from '../chat-routes/wire'\nimport {\n AgentSessionControls,\n type AgentSessionControlsProps,\n} from '../web-react/agent-session-controls'\nimport { ComposerModeControls } from './composer-mode-controls'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface EntryComposerProps {\n /** The one line above the input. Domain copy — always a product parameter. */\n heading?: ReactNode\n /** Optional supporting line under the heading. */\n subheading?: ReactNode\n placeholder?: string\n initialValue?: string\n sendLabel?: string\n disabled?: boolean\n /**\n * Agent identity (backend/model/effort). Pass it and the control row renders;\n * omit it and the composer ships without one. Omitting is a real choice for a\n * surface with nothing to choose — it should never be an oversight, which is\n * why this is one prop rather than a free-form slot a caller can forget.\n *\n * This is the CANONICAL picker cluster: `AgentSessionControls` from\n * `/web-react` (its model menu IS the canonical `ModelPicker`). The legacy\n * sandbox-ui adapter that used to back this prop was removed; the props\n * mapping for migrating a stored selection lives in\n * `docs/ui-picker-canon.md`.\n */\n agent?: AgentSessionControlsProps\n /**\n * Product-specific behavioral controls docked on the LEFT. A mode is an\n * on/off switch, not a value picker, so it sits apart from agent identity.\n * For the standard plan-approval control, prefer `planMode`.\n */\n modes?: ReactNode\n /**\n * Standard plan-approval mode. Pass only when the selected backend supports\n * it; omitted means the control is not rendered.\n */\n planMode?: ComposerPlanModeSelection\n /**\n * Upload endpoint for staged attachments. Omit and the attach affordance is\n * hidden — a composer with no place to put a file must not offer one.\n */\n uploadUrl?: string\n accept?: string\n onAttachmentError?: (reason: string) => void\n onRejectFiles?: Parameters<typeof AgentComposer>[0]['onRejectFiles']\n /**\n * `@`-file mentions. The hook itself is shared (`useFileMentions`), but the\n * cold-box policy around it — whether pressing `@` is worth starting a\n * sandbox — is a per-surface product call, so the result is injected rather\n * than created here.\n */\n mentions?: UseFileMentionsResult\n mentionPopoverClassName?: string\n /** Rendered under the composer: suggestion pills, an onboarding nudge, a\n * disclaimer. All domain copy. */\n footer?: ReactNode\n /** Block submit until a persisted selection has resolved against the catalog,\n * so the first turn cannot go out under the wrong model. Defaults to true. */\n ready?: boolean\n onSubmit: (\n prompt: string,\n attachments: ChatAttachmentInput[],\n mentions: FileMention[],\n ) => void\n className?: string\n composerClassName?: string\n /** Max width of the centered column. Defaults to the fleet's 820px. */\n maxWidth?: number\n}\n\n/**\n * `EntryComposer` — the centered \"what do you want to work on?\" surface a\n * product shows before a conversation exists (a new thread, an empty session,\n * a workspace overview).\n *\n * It exists because three products each grew their own, and they drifted into\n * three different capability sets rather than three different looks: one lost\n * the attach button, one lost the model and effort pickers entirely, one\n * hand-rolled a `<textarea>` and wired pickers from three separate packages\n * into one row. The composer, the pickers, the attachment queue and the mention\n * index were all ALREADY shared — what was not shared was the assembly, so\n * every product re-derived which controls an entry surface gets, and each\n * re-derivation dropped a different one.\n *\n * Mechanism lives here: layout, the attachment queue, the mention wiring, the\n * submit gate (nothing sends while an upload is pending or failed, or before\n * the model selection has resolved). Domain stays a parameter: `heading`,\n * `placeholder`, `footer` (suggestion pills, disclaimers) and the selections\n * themselves.\n *\n * The docked in-thread composer is NOT this component — it renders\n * `AgentComposer` directly with the same canonical `AgentSessionControls`,\n * because a docked composer has a transcript above it and no hero layout.\n */\nexport function EntryComposer({\n heading,\n subheading,\n placeholder,\n initialValue = '',\n sendLabel,\n disabled,\n agent,\n modes,\n planMode,\n uploadUrl,\n accept = ATTACHMENT_ACCEPT,\n onAttachmentError,\n onRejectFiles,\n mentions,\n mentionPopoverClassName,\n footer,\n ready = true,\n onSubmit,\n className,\n composerClassName,\n maxWidth = 820,\n}: EntryComposerProps) {\n const [value, setValue] = useState(initialValue)\n const attachments = useComposerAttachments({\n uploadUrl,\n enabled: !!uploadUrl,\n onReject: onAttachmentError,\n onError: onAttachmentError,\n })\n\n function submit(prompt: string) {\n if (!ready || disabled) return\n const next = prompt.trim()\n // A pending or failed upload must not send: the turn would reference an\n // attachment the store does not have. Surface why rather than no-op'ing.\n if (attachments.hasPending || attachments.hasError) {\n if (attachments.blockReason) onAttachmentError?.(attachments.blockReason)\n return\n }\n if (!next && attachments.references.length === 0) return\n onSubmit(next, attachments.references, mentions?.mentions ?? [])\n setValue('')\n attachments.clear()\n mentions?.clearMentions()\n }\n\n return (\n <div\n className={\n className ??\n 'relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5'\n }\n >\n <div className=\"w-full\" style={{ maxWidth }}>\n {heading ? (\n <h2 className=\"mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground\">\n {heading}\n </h2>\n ) : null}\n {subheading ? (\n <p className=\"mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground\">\n {subheading}\n </p>\n ) : (\n heading ? <div className=\"mb-7\" /> : null\n )}\n <AgentComposer\n className={composerClassName ?? 'vt-composer'}\n value={value}\n onChange={setValue}\n onSubmit={() => submit(value)}\n placeholder={placeholder}\n sendLabel={sendLabel}\n disabled={disabled}\n autoFocus\n canSubmitAttachmentsOnly\n accept={accept}\n attachments={attachments.composerFiles}\n onAttach={uploadUrl ? (files) => void attachments.addFiles(files) : undefined}\n onRejectFiles={onRejectFiles}\n onRemoveFile={attachments.removeAttachment}\n onRetryFile={attachments.retry}\n mention={\n mentions\n ? {\n ...mentions.mention,\n // Sidebar tone + hairline so the popover reads as app chrome\n // (the same treatment the picker menus get) instead of the\n // brighter overlay tone the component defaults to.\n popoverClassName:\n mentionPopoverClassName ??\n 'bg-surface-container-low border-[var(--md3-outline-variant)]',\n }\n : undefined\n }\n controls={modes ?? <ComposerModeControls planMode={planMode} />}\n trailing={\n agent ? <AgentSessionControls {...agent} /> : undefined\n }\n />\n {footer ? <div className=\"mt-7\">{footer}</div> : null}\n </div>\n </div>\n )\n}\n","import { ListChecks } from 'lucide-react'\nimport { cn } from '@tangle-network/sandbox-ui/utils'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface ComposerModeControlsProps {\n /**\n * Plan-approval mode. Products pass it only when the selected backend can\n * propose a plan and wait for approval; omitted means nothing renders.\n */\n planMode?: ComposerPlanModeSelection\n}\n\n/**\n * The shared plan-mode toggle for the left side of an agent composer.\n * Plan mode is a behavioral switch, not part of the profile/backend/model/\n * thinking identity controls on the right.\n */\nexport function ComposerModeControls({ planMode }: ComposerModeControlsProps) {\n if (!planMode) return null\n\n return (\n <button\n type=\"button\"\n aria-pressed={planMode.enabled}\n disabled={planMode.saving}\n onClick={() => planMode.setEnabled(!planMode.enabled)}\n title=\"Plan mode: the agent proposes a plan you approve before it executes\"\n className={cn(\n // Inset ring: this chip sits in a composer row that clips its overflow,\n // so an outward ring loses three of its four sides. Only the offset is\n // overridden — width and colour stay with the tokens.\n 'inline-flex h-7 items-center gap-1 rounded-full border px-2.5 text-xs transition-colors focus-visible:[outline-offset:-2px]',\n planMode.enabled\n ? 'border-primary/50 bg-primary/10 text-primary'\n : 'border-border bg-transparent text-muted-foreground hover:text-foreground',\n planMode.saving && 'opacity-60',\n )}\n >\n <ListChecks className=\"h-3.5 w-3.5\" />\n Plan\n </button>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createInteractionAnswerRoute
|
|
3
|
-
} from "../chunk-ZSSCVT6H.js";
|
|
4
|
-
import {
|
|
5
|
-
parseJsonObjectBody
|
|
6
|
-
} from "../chunk-F7VH4G5C.js";
|
|
7
1
|
import {
|
|
8
2
|
createSandboxFileIndexRoute
|
|
9
3
|
} from "../chunk-QY4BRKRJ.js";
|
|
@@ -28,6 +22,12 @@ import {
|
|
|
28
22
|
sanitizeAttachmentFileName,
|
|
29
23
|
sniffBinary
|
|
30
24
|
} from "../chunk-CCVG2TL6.js";
|
|
25
|
+
import {
|
|
26
|
+
createInteractionAnswerRoute
|
|
27
|
+
} from "../chunk-ZSSCVT6H.js";
|
|
28
|
+
import {
|
|
29
|
+
parseJsonObjectBody
|
|
30
|
+
} from "../chunk-F7VH4G5C.js";
|
|
31
31
|
import {
|
|
32
32
|
attachmentInputToPart,
|
|
33
33
|
attachmentKindForMime,
|