@streamoid/agent 0.6.52 → 0.6.54
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/README.md +131 -4
- package/dist/agentScope-BlOT7qMu.js +114 -0
- package/dist/index.d.ts +195 -13
- package/dist/index.js +17773 -17733
- package/dist/pane.d.ts +73 -0
- package/dist/pane.js +581 -0
- package/dist/style.css +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -53,10 +53,137 @@ tank`); `title` keeps its original identity. Preserve proper names, acronyms and
|
|
|
53
53
|
|
|
54
54
|
Embedded and standalone chat use `ScComposerSurface` and `ScSuggestionRow` from
|
|
55
55
|
`@streamoid/ui` 0.6.86+. An empty composer starts with three 20px text lines and
|
|
56
|
-
grows to 170px before scrolling. Suggestions are native buttons in
|
|
57
|
-
|
|
56
|
+
grows to 170px before scrolling. Suggestions are native buttons in a stack of
|
|
57
|
+
cards (the DS row spaces and borders itself), and fill the composer without
|
|
58
|
+
navigating or sending. A panel scoped to an app shows exactly three when three
|
|
59
|
+
are available (see `AgentScopeAdapter.sections` in `index.d.ts`).
|
|
58
60
|
|
|
59
|
-
The secondary line shows `meta.
|
|
60
|
-
`meta.
|
|
61
|
+
The secondary line shows `meta.summary` — one outcome-first sentence — falling
|
|
62
|
+
back to `meta.outcome_summary`, then `meta.expected_output` (which stays the
|
|
63
|
+
agent's spec), optionally followed by authored `meta.ceiling_minutes`
|
|
61
64
|
and `meta.estimated_credits`. Unknown estimates are omitted. Scoped suggestions
|
|
62
65
|
use `deliverable` and `ceilingMinutes` from the host's outcome catalogue.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## The Ask CXO side pane — `@streamoid/agent/pane`
|
|
69
|
+
|
|
70
|
+
Every product app shows the chat the same way: rail | **pane** | page. The
|
|
71
|
+
shell for it ships here, so the apps stop hand-building it around
|
|
72
|
+
`AgentPanel` (they had drifted on width, unmount-on-close, workspace switch,
|
|
73
|
+
Escape, mobile and persistence).
|
|
74
|
+
|
|
75
|
+
`@streamoid/agent/pane` is a **light entry** (~16kB, React and
|
|
76
|
+
`@streamoid/ui` are yours). It never imports the 5MB chat statically — the
|
|
77
|
+
chat is `import()`ed when the pane first opens (or on `prefetch`), so import
|
|
78
|
+
the pane eagerly in your shell.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { useMemo } from "react";
|
|
82
|
+
import { AgentSidePane, useAgentPaneController, type AgentSidePaneAdapters } from "@streamoid/agent/pane";
|
|
83
|
+
|
|
84
|
+
export function Shell() {
|
|
85
|
+
const pane = useAgentPaneController({
|
|
86
|
+
workspaceId, // persisted per workspace
|
|
87
|
+
legacyWidthKey: "artifax.assistant.width", // your old width key, migrated once
|
|
88
|
+
// persistKey: "artifax-assistant-session", // keep an existing session key
|
|
89
|
+
// bindShortcut: false, // if ⌘K is bound elsewhere
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const adapters = useMemo<AgentSidePaneAdapters>(() => ({
|
|
93
|
+
auth, workspace: { current: { id: workspaceId, name } }, theme: { mode },
|
|
94
|
+
config: { apiBase, hide: { sidepane: true, newBrief: true, notifications: true }, widgetMode: "inline_ask" },
|
|
95
|
+
}), [/* … */]); // memoise it
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div style={{ display: "flex", height: "100vh" }}>
|
|
99
|
+
<Rail
|
|
100
|
+
askActive={pane.open} // the trigger's `active`
|
|
101
|
+
onAsk={pane.askCxo} // open + focus the composer (never a toggle)
|
|
102
|
+
onAskIntent={pane.prefetch} // hover / focus
|
|
103
|
+
onHelp={() => pane.requestSupport({ currentPage })}
|
|
104
|
+
/>
|
|
105
|
+
<AgentSidePane controller={pane} adapters={adapters}
|
|
106
|
+
hostContext={hostContext} scope={scope} support={{ appVersion }} canvas={canvas} />
|
|
107
|
+
{/* An artefact takes the page: hide main (display:none, not unmount). */}
|
|
108
|
+
<main style={{ flex: 1, minWidth: 0, display: pane.surface === "fullscreen" ? "none" : undefined }}>
|
|
109
|
+
<Outlet context={{ askAbout: pane.askAbout }} />
|
|
110
|
+
</main>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
What the pane does, identically in every app:
|
|
117
|
+
|
|
118
|
+
- **Geometry.** Docked at ≥768px: in flow beside the page, width
|
|
119
|
+
`SC_ASSISTANT_PANEL_WIDTH` (**510px**, from `@streamoid/ui`) within
|
|
120
|
+
`SC_ASSISTANT_PANEL_BOUNDS` (360–900), resizable with `ScPanelResizeHandle`,
|
|
121
|
+
persisted under `streamoid.askPane.width`. Below 768px: a full-screen sheet
|
|
122
|
+
(`role="dialog"`, focus trap, siblings `inert`, focus restored on close).
|
|
123
|
+
Geometry is inline style, so no utility class from either stylesheet can
|
|
124
|
+
move it.
|
|
125
|
+
- **Fullscreen.** `onSurfaceChange` reporting a non-text artefact widens the
|
|
126
|
+
pane over the page (`controller.surface === "fullscreen"` — hide your
|
|
127
|
+
`main`). Escape returns to the side pane and that artefact is not widened
|
|
128
|
+
again while it stays open (also across a close and reopen); a different one
|
|
129
|
+
still is. A pane closed over a live artefact — or one an artefact opened in
|
|
130
|
+
while closed — reopens fullscreen. No expand button.
|
|
131
|
+
- **Stays mounted** after the first open; closing hides it, so an answer still
|
|
132
|
+
streaming keeps going. Before the first open nothing loads.
|
|
133
|
+
- **Persistence.** Open state and the conversation, per workspace, survive a
|
|
134
|
+
reload (fullscreen comes back as the side pane). A workspace change closes
|
|
135
|
+
the pane, drops the old conversation, **remounts the chat** (no draft,
|
|
136
|
+
upload or selection chip carries over) and loads the new workspace's.
|
|
137
|
+
- **Escape** (bubble phase): docked, it closes only when focus is inside the
|
|
138
|
+
pane, no nested dialog holds the key, and nothing `preventDefault`ed it.
|
|
139
|
+
Fullscreen collapses to the side pane first. On the sheet, focus need not be
|
|
140
|
+
inside (the page is inert).
|
|
141
|
+
- **Back button** (phone): opening the sheet pushes one history entry (same
|
|
142
|
+
URL, your router's state kept); Back closes the sheet. Closing with × or
|
|
143
|
+
Escape removes the entry, so no stray Back is left behind.
|
|
144
|
+
- **Errors.** A pane-local error boundary: "Ask CXO couldn’t load" + Retry
|
|
145
|
+
(re-imports). Loading shows "Loading Ask CXO…".
|
|
146
|
+
- **Header** is the chat's own (title + context, New task, History, ×);
|
|
147
|
+
× calls `controller.close`.
|
|
148
|
+
|
|
149
|
+
Controller calls: `reveal()` / `close()` / `toggle()`, `askCxo()`,
|
|
150
|
+
`askAbout(prompt)` (new conversation + send, via `enqueueChatPrompt`),
|
|
151
|
+
`requestSupport({ currentPage }?)` (Get help home via `showAgentSupport`),
|
|
152
|
+
`prefetch()`, `width` / `setWidth` / `commitWidth`, and
|
|
153
|
+
`selectionSnapshot` — the selection the pane **holds** (its snapshot of
|
|
154
|
+
`scope.selection`: taken on open, on New task and on `askCxo` with no chat;
|
|
155
|
+
cleared when a chat opens or the chip is removed). Count or split suggestions
|
|
156
|
+
on that, with the pane's own rule: `paneSuggestionCount(sections,
|
|
157
|
+
pane.selectionSnapshot)` / `deriveScopeSuggestions(...)`, both exported here.
|
|
158
|
+
A host mounting `AgentPanel` directly gets the same value from
|
|
159
|
+
`adapters.onSelectionSnapshot`.
|
|
160
|
+
|
|
161
|
+
**Stylesheet.** Import the chat's CSS into its own cascade layer so its
|
|
162
|
+
compiled utilities cannot beat yours (the Catalogix / Photogenix pattern):
|
|
163
|
+
|
|
164
|
+
```css
|
|
165
|
+
@import "@streamoid/agent/style.css" layer(streamoid-agent);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Migrating each app (do it in the app's `@streamoid/agent` 0.6.54 bump PR)
|
|
169
|
+
|
|
170
|
+
**Artifax** (`apps/artifax/src/components/{AppShell,AssistantPanel}.tsx`)
|
|
171
|
+
- `useAgentPaneController({ workspaceId: workspaceID, persistKey: "artifax-assistant-session", legacyWidthKey: "artifax.assistant.width" })` in `AppShell`; render `<AgentSidePane>` in place of `AssistantPanel`, keeping its adapters/hostContext/scope.
|
|
172
|
+
- Delete `lib/assistantSurface.ts` (reducer + tests) and `lib/assistantSession.ts`, the shell's Escape effect, `prefetchAgentChunk`, `askCxo`, `requestSupport`, the `useStreamoidSearchShortcut` call. `main` hides on `pane.surface === "fullscreen"` (as now). `onAskCxoRequest(pane.askCxo)` stays.
|
|
173
|
+
- Delete `usePaneSelectionSnapshot` / `noteAssistantComposerFocus` (`lib/assistantScope.ts`) and the test copy `src/test/paneSuggestions.ts`; use `pane.selectionSnapshot` and `paneSuggestionCount` / `deriveScopeSuggestions`.
|
|
174
|
+
- Default width stays 510; a stored width migrates.
|
|
175
|
+
|
|
176
|
+
**Tactix** (`apps/tactix/src/components/{TactixShell,AssistantPanel}.tsx`)
|
|
177
|
+
- `useAgentPaneController({ workspaceId, legacyWidthKey: "tactix.assistant.width" })` in `TactixShell`; `<AgentSidePane>` replaces `AssistantPanel`. Outlet context `askCxoAbout` becomes `pane.askAbout`.
|
|
178
|
+
- Delete the shell's `askCxo` / `askCxoAbout` / `requestSupport` / `prefetchAgent` and the panel's unguarded Escape effect. Hide `main` on fullscreen (new: artefacts now widen). Gains: mobile sheet, persistence, workspace reset.
|
|
179
|
+
- Default width 480 → 510 (DS constant) for users who never resized.
|
|
180
|
+
|
|
181
|
+
**Catalogix** (`dashboard/app/containers/AskCxo/`)
|
|
182
|
+
- `useAgentPaneController({ workspaceId, legacyWidthKey: "catalogix.ask.width" })` where `useAppStore`'s ask slice is read; `<AgentSidePane>` replaces `AskCxo/index.jsx` + `AskCxoPanel.jsx`. The rail's trigger/⌘K/Help call the controller (pass `bindShortcut: false` if ⌘K stays bound in `LeftMenu`).
|
|
183
|
+
- Delete `askOpen` / `askChatId` from `useAppStore` (and the workspace-switch reset on its setter — the controller does it), `askCxo.js`'s `importAgent` / `prefetchAgent` / `askCxo`. Hide the page on fullscreen (new). Gains: persistence, Back button.
|
|
184
|
+
- Default width 480 → 510.
|
|
185
|
+
|
|
186
|
+
**Photogenix** (`dashboard/client/src/features/ask/`)
|
|
187
|
+
- `useAgentPaneController({ workspaceId, legacyWidthKey: "photogenix.ask.width" })` in the shell; `<AgentSidePane>` replaces `AskPane.tsx` + `AskCxoPanel.tsx`; `useAskConversation.ts` goes (the controller owns `chatId`).
|
|
188
|
+
- Delete the pane's own sheet/focus-trap/Escape code. Hide the page on fullscreen (new). Gains: stays mounted (no SSE abort on close), persistence, Back button.
|
|
189
|
+
- Default width 480 → 510.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { resolveStreamoidProduct as b, streamoidProductLabel as k } from "@streamoid/ui";
|
|
2
|
+
function a(e) {
|
|
3
|
+
if (typeof e != "string" || !e.trim()) return null;
|
|
4
|
+
const n = b(e.trim());
|
|
5
|
+
return n && n !== "cxo" ? n : null;
|
|
6
|
+
}
|
|
7
|
+
function d(e) {
|
|
8
|
+
return e && k(e) || "All apps";
|
|
9
|
+
}
|
|
10
|
+
function $(e, n) {
|
|
11
|
+
if (e === null || e !== a(n?.app)) return null;
|
|
12
|
+
const i = (Array.isArray(n?.breadcrumbs) ? n.breadcrumbs.filter((t) => typeof t == "string" && !!t.trim()).map((t) => t.trim()) : []).join(" › ") || (typeof n?.page == "string" ? n.page.trim() : "");
|
|
13
|
+
return i ? `${d(e)} · ${i}` : d(e);
|
|
14
|
+
}
|
|
15
|
+
function p(e, n) {
|
|
16
|
+
return !e.kinds || e.kinds.length === 0 || n === void 0 ? !0 : e.kinds.map((r) => r.toLowerCase()).includes(n.toLowerCase());
|
|
17
|
+
}
|
|
18
|
+
function h(e) {
|
|
19
|
+
return e.charAt(0).toUpperCase() + e.slice(1);
|
|
20
|
+
}
|
|
21
|
+
function m(e, n) {
|
|
22
|
+
const r = e.instruction?.includes("{subject}") ? e.instruction : `${e.label}: {subject}`;
|
|
23
|
+
return h(r.replace(/\{subject\}/g, n).trim());
|
|
24
|
+
}
|
|
25
|
+
function w(e, n) {
|
|
26
|
+
const r = [];
|
|
27
|
+
for (const i of e ?? []) {
|
|
28
|
+
if (!i?.outcomes?.length) continue;
|
|
29
|
+
const t = (i.samples ?? []).find(
|
|
30
|
+
(u) => i.outcomes.some((f) => p(f, u.kind))
|
|
31
|
+
), c = (u, f) => i.outcomes.find((l) => l.key === f && p(l, u)) ?? i.outcomes.find((l) => p(l, u)), s = n ? c(n.kind, t?.primaryOutcomeKey) : void 0;
|
|
32
|
+
if (n && s) {
|
|
33
|
+
r.push({
|
|
34
|
+
id: `${i.key}:${s.key}:selection`,
|
|
35
|
+
sectionKey: i.key,
|
|
36
|
+
outcomeKey: s.key,
|
|
37
|
+
subject: "selection",
|
|
38
|
+
text: m(s, n.subject || n.label),
|
|
39
|
+
ceilingMinutes: s.ceilingMinutes,
|
|
40
|
+
deliverable: s.deliverable
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!t) continue;
|
|
45
|
+
const o = c(t.kind, t.primaryOutcomeKey);
|
|
46
|
+
o && r.push({
|
|
47
|
+
id: `${i.key}:${o.key}:${t.id}`,
|
|
48
|
+
sectionKey: i.key,
|
|
49
|
+
outcomeKey: o.key,
|
|
50
|
+
sampleId: t.id,
|
|
51
|
+
sampleTitle: t.subject || t.title,
|
|
52
|
+
subject: "sample",
|
|
53
|
+
text: m(o, t.subject || t.title),
|
|
54
|
+
ceilingMinutes: o.ceilingMinutes,
|
|
55
|
+
deliverable: o.deliverable,
|
|
56
|
+
imageUrl: t.imageUrl ?? null
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return r;
|
|
60
|
+
}
|
|
61
|
+
function O(e, n) {
|
|
62
|
+
return n ? e.length === 0 ? !1 : e.every((r) => a(r) === n) : !0;
|
|
63
|
+
}
|
|
64
|
+
const v = 3;
|
|
65
|
+
function A(e, n, r = v) {
|
|
66
|
+
const i = (s) => (s.value ?? s.title).trim().toLowerCase(), t = e.slice(0, r), c = new Set(t.map(i));
|
|
67
|
+
for (const s of n) {
|
|
68
|
+
if (t.length >= r) break;
|
|
69
|
+
const o = i(s);
|
|
70
|
+
c.has(o) || (c.add(o), t.push(s));
|
|
71
|
+
}
|
|
72
|
+
return t;
|
|
73
|
+
}
|
|
74
|
+
function S(e) {
|
|
75
|
+
return ["headline", "subline", "placeholder"].filter(
|
|
76
|
+
(n) => !e?.[n]?.trim()
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
let g = !1;
|
|
80
|
+
function C() {
|
|
81
|
+
try {
|
|
82
|
+
return process.env.NODE_ENV === "development";
|
|
83
|
+
} catch {
|
|
84
|
+
return !1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function L(e, n, r = C()) {
|
|
88
|
+
if (!r || g || !e) return !1;
|
|
89
|
+
const i = S(n);
|
|
90
|
+
return i.length === 0 ? !1 : (g = !0, console.warn(
|
|
91
|
+
`[@streamoid/agent] adapters.scope for "${e}" has no ${i.join(", ")}; the Ask CXO panel falls back to generic copy. Pass an app-specific question (headline), helper line (subline) and composer hint (placeholder).`
|
|
92
|
+
), !0);
|
|
93
|
+
}
|
|
94
|
+
function y(e) {
|
|
95
|
+
return a(e.meta?.origin_app);
|
|
96
|
+
}
|
|
97
|
+
function K(e) {
|
|
98
|
+
const n = e.meta?.scope;
|
|
99
|
+
return n === "all" ? null : a(n) ?? y(e);
|
|
100
|
+
}
|
|
101
|
+
function M(e, n) {
|
|
102
|
+
return n ? e.filter((r) => y(r) === n) : [...e];
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
M as a,
|
|
106
|
+
O as b,
|
|
107
|
+
K as c,
|
|
108
|
+
w as d,
|
|
109
|
+
$ as e,
|
|
110
|
+
A as f,
|
|
111
|
+
a as h,
|
|
112
|
+
d as s,
|
|
113
|
+
L as w
|
|
114
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -50,10 +50,20 @@ export interface AgentHostContext {
|
|
|
50
50
|
ids?: Record<string, string | number>;
|
|
51
51
|
/** A short list of what is on screen — board names, selected items. */
|
|
52
52
|
visible?: string[];
|
|
53
|
-
/* The pane merges
|
|
54
|
-
*
|
|
55
|
-
* the Get help home (`showAgentSupport`)
|
|
56
|
-
* `
|
|
53
|
+
/* The pane merges keys of its own into what is sent, never replacing the
|
|
54
|
+
* host's: `app_version` from `adapters.support.appVersion` on every message;
|
|
55
|
+
* and on a conversation started from the Get help home (`showAgentSupport`)
|
|
56
|
+
* `entry: "help_and_support"`, `current_page` (from `showAgentSupport({
|
|
57
|
+
* currentPage })`), `browser` ("Chrome") and `details_shared: true`. If the
|
|
58
|
+
* user removes the home's "We'll include" chip, that conversation's
|
|
59
|
+
* messages carry only `entry`, `details_shared: false` and the routing keys
|
|
60
|
+
* `scope` and `app` (plus `selection` / `ids.selection` while the composer's
|
|
61
|
+
* selection chip is attached) — none of the host's other keys, nor the
|
|
62
|
+
* pane's. The backend latches the opt-out on the conversation and strips
|
|
63
|
+
* the details from every later turn itself, even after a remount. */
|
|
64
|
+
/** Pane-set on the Get help conversation: whether the user shared the
|
|
65
|
+
* "We'll include" details. Hosts do not set it. */
|
|
66
|
+
details_shared?: boolean;
|
|
57
67
|
[key: string]: unknown;
|
|
58
68
|
}
|
|
59
69
|
|
|
@@ -73,9 +83,9 @@ export interface AgentSupportAdapter {
|
|
|
73
83
|
/**
|
|
74
84
|
* The host app's own build version, e.g. `0.0.61`. Sent as
|
|
75
85
|
* `hostContext.app_version` with EVERY message — support or not — so a
|
|
76
|
-
* problem report names the build it came from. Also shown
|
|
77
|
-
*
|
|
78
|
-
*
|
|
86
|
+
* problem report names the build it came from. Also shown in the Get help
|
|
87
|
+
* home's "We'll include" chip. A `hostContext.app_version` the host passes
|
|
88
|
+
* itself wins over this.
|
|
79
89
|
*/
|
|
80
90
|
appVersion?: string;
|
|
81
91
|
}
|
|
@@ -154,6 +164,19 @@ export interface AgentChatShellAdapters {
|
|
|
154
164
|
* Fires only on change, and only for the conversation on screen.
|
|
155
165
|
*/
|
|
156
166
|
onSurfaceChange?: (state: AgentSurfaceState) => void;
|
|
167
|
+
/**
|
|
168
|
+
* The selection the pane HOLDS — its snapshot of `scope.selection`, which its
|
|
169
|
+
* suggestions and composer chip are built from — reported whenever it
|
|
170
|
+
* changes, and once on mount.
|
|
171
|
+
*
|
|
172
|
+
* The pane does not follow `scope.selection` live: it snapshots it when it
|
|
173
|
+
* mounts, when the conversation goes back to none (New task), and on
|
|
174
|
+
* `focusAgentComposer()` while no conversation is open; it clears it when an
|
|
175
|
+
* existing conversation opens or the user removes the chip. A host that
|
|
176
|
+
* splits or counts its suggestions on the selection must use THIS value, not
|
|
177
|
+
* its live one. `AgentSidePane` wires it to `controller.selectionSnapshot`.
|
|
178
|
+
*/
|
|
179
|
+
onSelectionSnapshot?: (selection: AgentScopeSelection | null) => void;
|
|
157
180
|
/**
|
|
158
181
|
* Supply the canvas as a component and the Dreamer panel drops its iframe:
|
|
159
182
|
* no embedded page, so no cross-origin auth, no postMessage handshake, no
|
|
@@ -179,6 +202,16 @@ export interface AgentChatShellAdapters {
|
|
|
179
202
|
* - `sections`: the app's outcome table. One suggestion per section, from
|
|
180
203
|
* its first sample's primary outcome. Change the samples and the
|
|
181
204
|
* suggestions follow. Omit it for the catalog briefs this app can run alone.
|
|
205
|
+
* The panel shows EXACTLY THREE suggested tasks when three exist: the
|
|
206
|
+
* sections' suggestions in table order (a fourth and later are left to
|
|
207
|
+
* "View all tasks"), topped up with the catalog briefs this app can run
|
|
208
|
+
* alone — those flagged for the empty chat first, by their authored rank,
|
|
209
|
+
* then the rest — never repeating one. Fewer only when fewer exist.
|
|
210
|
+
* - `headline`, `subline`, `placeholder`: the panel's app-specific question,
|
|
211
|
+
* helper line and composer hint. Pass all three: a scoped host that leaves
|
|
212
|
+
* any out gets one `console.warn` in a development build. Honoured at CXO
|
|
213
|
+
* home too (`hostContext.app` absent or `"cxo"`), where the defaults are
|
|
214
|
+
* the hub's own when they are absent.
|
|
182
215
|
* - `selection`: what was selected when the pane opened — a removable chip
|
|
183
216
|
* in the composer, and the subject suggestions name.
|
|
184
217
|
* - `onSuggestion`: DEPRECATED AND NOT CALLED. Picking a suggestion always
|
|
@@ -254,13 +287,15 @@ export interface AgentScopeAdapter {
|
|
|
254
287
|
sections?: AgentScopeSection[] | null;
|
|
255
288
|
selection?: AgentScopeSelection | null;
|
|
256
289
|
/** The line above the composer before the first turn, in the host's own
|
|
257
|
-
* words. Absent or blank = "Describe what you
|
|
290
|
+
* words — the app-specific question. Absent or blank = "Describe what you
|
|
291
|
+
* want to create" in an app, "Run your retail operations from here" at CXO
|
|
292
|
+
* home. */
|
|
258
293
|
headline?: string | null;
|
|
259
|
-
/** The quieter line under it. Absent or blank = "CXO will plan the
|
|
260
|
-
* and pause for your approval." */
|
|
294
|
+
/** The quieter helper line under it. Absent or blank = "CXO will plan the
|
|
295
|
+
* steps and pause for your approval." in an app, none at CXO home. */
|
|
261
296
|
subline?: string | null;
|
|
262
297
|
/** The empty composer's hint before the first turn. Absent or blank =
|
|
263
|
-
* "What needs to get done". */
|
|
298
|
+
* "What needs to get done". Honoured in an app and at CXO home alike. */
|
|
264
299
|
placeholder?: string | null;
|
|
265
300
|
/**
|
|
266
301
|
* @deprecated NOT CALLED. A suggestion always fills the composer. Kept so
|
|
@@ -487,7 +522,16 @@ export declare function focusAgentComposer(): void;
|
|
|
487
522
|
* Every message of the conversation that starts on the home carries
|
|
488
523
|
* `hostContext.entry = "help_and_support"`, merged into the host's own
|
|
489
524
|
* context, for as long as that conversation stays open — so follow-ups keep
|
|
490
|
-
* routing to the support agent.
|
|
525
|
+
* routing to the support agent.
|
|
526
|
+
*
|
|
527
|
+
* The home's "We'll include" line is ONE removable chip naming what the
|
|
528
|
+
* support conversation is sent: workspace, `options.currentPage`, version
|
|
529
|
+
* and browser, after the app (plain text, always shared — it routes the
|
|
530
|
+
* request). Removing it (✕) sends none of them — only `entry`,
|
|
531
|
+
* `details_shared: false` and the routing keys `scope` and `app` — until the
|
|
532
|
+
* user restores it or the home is opened again. The opt-out is persisted on
|
|
533
|
+
* the conversation server-side, which also leaves the workspace off the
|
|
534
|
+
* support email and the issue. Starting a new task, or opening another
|
|
491
535
|
* conversation from history, leaves the mode.
|
|
492
536
|
*
|
|
493
537
|
* Queued like {@link enqueueChatPrompt}: the call may come before or after the
|
|
@@ -495,4 +539,142 @@ export declare function focusAgentComposer(): void;
|
|
|
495
539
|
* empty one. Do not also set the panel's `chatId` to a new id — `null` (or
|
|
496
540
|
* leaving it alone) is right; the pane navigates itself.
|
|
497
541
|
*/
|
|
498
|
-
export declare function showAgentSupport(): void;
|
|
542
|
+
export declare function showAgentSupport(options?: ShowAgentSupportOptions | null): void;
|
|
543
|
+
|
|
544
|
+
/** Options for {@link showAgentSupport}. All optional; omit them entirely and
|
|
545
|
+
* the home behaves exactly as with no argument. */
|
|
546
|
+
export interface ShowAgentSupportOptions {
|
|
547
|
+
/**
|
|
548
|
+
* A short, human label for where the user was BEFORE opening help — the
|
|
549
|
+
* task or page they were on, e.g. `"SS26 range plan"` or `"Explore"`. Not a
|
|
550
|
+
* URL or a route (`hostContext.url`/`route` already carry those).
|
|
551
|
+
*
|
|
552
|
+
* Shown in the home's "We'll include" chip
|
|
553
|
+
* (`CXO · demo workspace · SS26 range plan · v0.0.61 · Chrome`) and sent as
|
|
554
|
+
* `hostContext.current_page` on every message of the support conversation,
|
|
555
|
+
* unless the user removes the chip. Cleared when the user leaves the home or
|
|
556
|
+
* its conversation (New task, another conversation); each call replaces it,
|
|
557
|
+
* and a call without it shows none.
|
|
558
|
+
*/
|
|
559
|
+
currentPage?: string | null;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/* ── Ask CXO side pane (`@streamoid/agent/pane`) ───────────────────────────
|
|
563
|
+
*
|
|
564
|
+
* The TYPES of the shared side pane live here, beside the adapters they are
|
|
565
|
+
* built from; its VALUES (`useAgentPaneController`, `AgentSidePane`, …) are
|
|
566
|
+
* declared in `pane.d.ts` and exported only from the `@streamoid/agent/pane`
|
|
567
|
+
* subpath — the light entry that does not pull in this 5MB bundle. Keep both
|
|
568
|
+
* in step with `src/embed/pane/`. */
|
|
569
|
+
|
|
570
|
+
/** `closed` → `pane` (the side pane) → `fullscreen` (a non-text artefact took
|
|
571
|
+
* the page). Fullscreen is never restored on reload. */
|
|
572
|
+
export type AgentPaneSurface = 'closed' | 'pane' | 'fullscreen';
|
|
573
|
+
|
|
574
|
+
export interface AgentPaneControllerOptions {
|
|
575
|
+
/** The workspace the pane's conversation belongs to. The open state and the
|
|
576
|
+
* conversation are persisted per workspace; a change closes the pane, drops
|
|
577
|
+
* the old workspace's conversation and loads the new one's saved state. An
|
|
578
|
+
* empty value (not resolved yet) persists nothing. */
|
|
579
|
+
workspaceId: string | null | undefined;
|
|
580
|
+
/** localStorage prefix for the per-workspace session
|
|
581
|
+
* (`<persistKey>:<workspaceId>`). Default `streamoid.askPane.session`.
|
|
582
|
+
* Artifax passes `artifax-assistant-session` to keep its users' open chats
|
|
583
|
+
* across the migration — its stored `{ surface: 'rail', chatId }` is read. */
|
|
584
|
+
persistKey?: string;
|
|
585
|
+
/** The app's old width key(s), copied once into `streamoid.askPane.width`
|
|
586
|
+
* while that key is still empty: `artifax.assistant.width`,
|
|
587
|
+
* `tactix.assistant.width`, `catalogix.ask.width`, `photogenix.ask.width`. */
|
|
588
|
+
legacyWidthKey?: string | readonly string[];
|
|
589
|
+
/** Bind ⌘K / Ctrl+K to `askCxo` (with a prefetch). Default `true`; pass
|
|
590
|
+
* `false` where the host binds the chord to something else. */
|
|
591
|
+
bindShortcut?: boolean;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** Options for {@link AgentPaneController.requestSupport} — exactly
|
|
595
|
+
* {@link showAgentSupport}'s, passed straight through. */
|
|
596
|
+
export type AgentPaneSupportOptions = ShowAgentSupportOptions;
|
|
597
|
+
|
|
598
|
+
export interface AgentPaneController {
|
|
599
|
+
/** The pane is showing (side pane or fullscreen). Hand it to the rail's Ask
|
|
600
|
+
* CXO trigger as `active`. */
|
|
601
|
+
open: boolean;
|
|
602
|
+
/** `fullscreen` while a non-text artefact holds the page. A host whose page
|
|
603
|
+
* sits beside the pane HIDES it (`display: none`, not unmount — the page
|
|
604
|
+
* keeps its scroll and form state) for exactly as long:
|
|
605
|
+
* `<main style={{ display: controller.surface === 'fullscreen' ? 'none' : undefined }}>`. */
|
|
606
|
+
surface: AgentPaneSurface;
|
|
607
|
+
/** The conversation on screen; `null` lets the chat create one. Persisted
|
|
608
|
+
* per workspace with the open state. */
|
|
609
|
+
chatId: string | null;
|
|
610
|
+
/** Show the pane. Open, never toggle. Applies the chat's latest surface
|
|
611
|
+
* report: an artefact still live behind a closed pane reopens fullscreen,
|
|
612
|
+
* unless the user declined it with Escape. */
|
|
613
|
+
reveal(): void;
|
|
614
|
+
/** Hide the pane. It stays mounted: an answer still streaming keeps going,
|
|
615
|
+
* and an artefact stays live (reports arriving while closed are kept). */
|
|
616
|
+
close(): void;
|
|
617
|
+
toggle(): void;
|
|
618
|
+
/** The rail trigger / ⌘K: reveal and put the caret in the composer. */
|
|
619
|
+
askCxo(): void;
|
|
620
|
+
/** Reveal on a NEW conversation and send `prompt` in it, without waiting for
|
|
621
|
+
* the network (`enqueueChatPrompt`). `false` when the prompt is blank. */
|
|
622
|
+
askAbout(prompt: string): boolean;
|
|
623
|
+
/** Help & Support: reveal on the Get help home ({@link showAgentSupport});
|
|
624
|
+
* sends nothing. */
|
|
625
|
+
requestSupport(options?: AgentPaneSupportOptions | null): void;
|
|
626
|
+
/** Warm the agent chunk — wire to the trigger's hover and focus. */
|
|
627
|
+
prefetch(): void;
|
|
628
|
+
/** Docked width in px, from `SC_ASSISTANT_PANEL_WIDTH` within
|
|
629
|
+
* `SC_ASSISTANT_PANEL_BOUNDS`, persisted under `streamoid.askPane.width`. */
|
|
630
|
+
width: number;
|
|
631
|
+
/** Live width during a drag; not persisted. */
|
|
632
|
+
setWidth(width: number): void;
|
|
633
|
+
/** Persist a width (drag end). */
|
|
634
|
+
commitWidth(width: number): void;
|
|
635
|
+
bounds: { min: number; max: number };
|
|
636
|
+
/** The selection the pane HOLDS (see `adapters.onSelectionSnapshot`) —
|
|
637
|
+
* `null` until the chat mounts. Split or count suggestions on this, e.g.
|
|
638
|
+
* `paneSuggestionCount(sections, controller.selectionSnapshot)`, not on the
|
|
639
|
+
* host's live selection. */
|
|
640
|
+
selectionSnapshot: AgentScopeSelection | null;
|
|
641
|
+
/** Changes on every real workspace switch (not when an unresolved id first
|
|
642
|
+
* resolves). `AgentSidePane` keys the chat on it, so a switch remounts the
|
|
643
|
+
* chat — workspace A's draft, uploads and selection chip never reach B —
|
|
644
|
+
* and `selectionSnapshot` resets in the same update. */
|
|
645
|
+
workspaceEpoch: number;
|
|
646
|
+
/** Wiring for `AgentSidePane`; hosts do not need these. */
|
|
647
|
+
setChatId(chatId: string | null): void;
|
|
648
|
+
reportSurface(state: AgentSurfaceState): void;
|
|
649
|
+
collapse(): void;
|
|
650
|
+
reportSelectionSnapshot(selection: AgentScopeSelection | null): void;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/** The adapters a host still supplies to {@link AgentSidePaneProps}. The pane
|
|
654
|
+
* fills `navigation`, `onClose`, `onSurfaceChange` and `onSelectionSnapshot`
|
|
655
|
+
* from the controller and takes `hostContext`, `scope`, `support` and
|
|
656
|
+
* `canvas` as props. */
|
|
657
|
+
export type AgentSidePaneAdapters = Omit<
|
|
658
|
+
AgentPanelAdapters,
|
|
659
|
+
| 'navigation'
|
|
660
|
+
| 'onClose'
|
|
661
|
+
| 'onSurfaceChange'
|
|
662
|
+
| 'onSelectionSnapshot'
|
|
663
|
+
| 'hostContext'
|
|
664
|
+
| 'scope'
|
|
665
|
+
| 'support'
|
|
666
|
+
| 'canvas'
|
|
667
|
+
>;
|
|
668
|
+
|
|
669
|
+
export interface AgentSidePaneProps {
|
|
670
|
+
controller: AgentPaneController;
|
|
671
|
+
/** Memoise it: a new object every render re-renders the whole chat. */
|
|
672
|
+
adapters: AgentSidePaneAdapters;
|
|
673
|
+
hostContext?: AgentHostContext | null;
|
|
674
|
+
scope?: AgentScopeAdapter | null;
|
|
675
|
+
support?: AgentSupportAdapter | null;
|
|
676
|
+
canvas?: HostCanvasContextValue;
|
|
677
|
+
className?: string;
|
|
678
|
+
/** Accessible name of the pane landmark. Default "Ask CXO assistant". */
|
|
679
|
+
ariaLabel?: string;
|
|
680
|
+
}
|