@unofficialbox/box-open-elements 0.18.0 → 0.20.0
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/components/actions/button-group.js +6 -0
- package/dist/components/actions/button.js +6 -0
- package/dist/components/actions/icon-button.js +6 -0
- package/dist/components/actions/link-button.js +6 -0
- package/dist/components/actions/menu-item.js +6 -0
- package/dist/components/actions/menu.js +6 -0
- package/dist/components/actions/segmented-control.js +6 -0
- package/dist/components/actions/toolbar.js +6 -0
- package/dist/components/collections/card.js +6 -0
- package/dist/components/collections/carousel.js +6 -0
- package/dist/components/collections/check-list.d.ts +10 -0
- package/dist/components/collections/check-list.js +8 -0
- package/dist/components/collections/datalist-item.js +6 -0
- package/dist/components/collections/document-list.d.ts +10 -0
- package/dist/components/collections/document-list.js +8 -0
- package/dist/components/collections/draggable-list.js +6 -0
- package/dist/components/collections/fact-list.d.ts +10 -0
- package/dist/components/collections/fact-list.js +8 -0
- package/dist/components/collections/grid-view.js +6 -0
- package/dist/components/collections/pagination.js +6 -0
- package/dist/components/collections/result-blocks.d.ts +54 -0
- package/dist/components/collections/result-blocks.js +71 -0
- package/dist/components/collections/table.js +6 -0
- package/dist/components/collections/thumbnail-card.js +6 -0
- package/dist/components/collections/tree-grid.js +6 -0
- package/dist/components/collections/tree.js +6 -0
- package/dist/components/feedback/alert.js +1 -1
- package/dist/components/feedback/badge.js +1 -1
- package/dist/components/feedback/badgeable.js +6 -0
- package/dist/components/feedback/chip.js +6 -0
- package/dist/components/feedback/due-badge.js +6 -0
- package/dist/components/feedback/empty-state.js +6 -0
- package/dist/components/feedback/error-mask.js +6 -0
- package/dist/components/feedback/help-text.js +6 -0
- package/dist/components/feedback/indicator.js +1 -1
- package/dist/components/feedback/nudge.js +6 -0
- package/dist/components/feedback/path.js +6 -0
- package/dist/components/feedback/progress-bar.d.ts +9 -0
- package/dist/components/feedback/progress-bar.js +35 -3
- package/dist/components/feedback/progress-ring.js +6 -0
- package/dist/components/feedback/progress-steps.js +6 -0
- package/dist/components/feedback/skeleton.js +6 -0
- package/dist/components/feedback/spinner.js +6 -0
- package/dist/components/feedback/status-icon.d.ts +10 -0
- package/dist/components/feedback/status-icon.js +26 -0
- package/dist/components/feedback/toast.js +1 -1
- package/dist/components/files/drop-zone.js +1 -1
- package/dist/components/forms/calendar.js +6 -0
- package/dist/components/forms/category-selector.js +6 -0
- package/dist/components/forms/checkbox-group.js +6 -0
- package/dist/components/forms/checkbox.js +6 -0
- package/dist/components/forms/color-picker.js +6 -0
- package/dist/components/forms/combobox.js +6 -0
- package/dist/components/forms/date-field.js +6 -0
- package/dist/components/forms/dropdown.js +6 -0
- package/dist/components/forms/dual-listbox.js +6 -0
- package/dist/components/forms/fieldset.js +6 -0
- package/dist/components/forms/multi-select.js +6 -0
- package/dist/components/forms/number-input.js +6 -0
- package/dist/components/forms/pill-cloud.js +6 -0
- package/dist/components/forms/pill-selector-dropdown.js +6 -0
- package/dist/components/forms/radio-group.js +6 -0
- package/dist/components/forms/range-slider.js +6 -0
- package/dist/components/forms/rating.js +6 -0
- package/dist/components/forms/rich-text-input.js +6 -0
- package/dist/components/forms/search-field.js +6 -0
- package/dist/components/forms/select.js +6 -0
- package/dist/components/forms/slider.js +6 -0
- package/dist/components/forms/spin-button.js +6 -0
- package/dist/components/forms/switch.js +6 -0
- package/dist/components/forms/tag-input.js +6 -0
- package/dist/components/forms/text-area.js +6 -0
- package/dist/components/forms/text-field.js +6 -0
- package/dist/components/forms/tile-group.js +1 -1
- package/dist/components/forms/time-field.js +6 -0
- package/dist/components/identity/avatar.js +6 -0
- package/dist/components/identity/contact-datalist-item.js +6 -0
- package/dist/components/identity/persona.js +6 -0
- package/dist/components/layout/app-shell.js +6 -0
- package/dist/components/layout/divider.js +6 -0
- package/dist/components/layout/grid.js +1 -1
- package/dist/components/layout/nav-sidebar.js +6 -0
- package/dist/components/layout/section.js +6 -0
- package/dist/components/layout/sidebar-toggle-button.js +6 -0
- package/dist/components/layout/split-view.js +6 -0
- package/dist/components/navigation/accordion.js +6 -0
- package/dist/components/navigation/breadcrumb.js +6 -0
- package/dist/components/navigation/tabs.js +6 -0
- package/dist/components/output/code-block.js +1 -1
- package/dist/components/output/formatted-value.js +1 -1
- package/dist/components/overlays/command-palette.js +6 -0
- package/dist/components/overlays/context-menu.js +6 -0
- package/dist/components/overlays/guide-tooltip.js +6 -0
- package/dist/components/overlays/popover.js +6 -0
- package/dist/components/overlays/shortcuts-overlay.js +6 -0
- package/dist/components/overlays/tooltip.js +6 -0
- package/dist/components/visuals/illustration.js +6 -0
- package/dist/element-maps.d.ts +39 -19
- package/dist/entries/agent-workspace.d.ts +1 -0
- package/dist/entries/agent-workspace.js +1 -0
- package/dist/entries/call-console.d.ts +1 -0
- package/dist/entries/call-console.js +1 -0
- package/dist/entries/check-list.d.ts +1 -0
- package/dist/entries/check-list.js +1 -0
- package/dist/entries/document-list.d.ts +1 -0
- package/dist/entries/document-list.js +1 -0
- package/dist/entries/fact-list.d.ts +1 -0
- package/dist/entries/fact-list.js +1 -0
- package/dist/entries/result-blocks.d.ts +1 -0
- package/dist/entries/result-blocks.js +1 -0
- package/dist/entries/run-summary.d.ts +1 -0
- package/dist/entries/run-summary.js +1 -0
- package/dist/entries/status-icon.d.ts +1 -0
- package/dist/entries/status-icon.js +1 -0
- package/dist/foundations/motion/behaviors.d.ts +8 -0
- package/dist/foundations/motion/behaviors.js +24 -0
- package/dist/foundations/motion/index.d.ts +2 -0
- package/dist/foundations/motion/index.js +2 -0
- package/dist/foundations/motion/scroll-pin.d.ts +19 -0
- package/dist/foundations/motion/scroll-pin.js +60 -0
- package/dist/foundations/motion/tokens.d.ts +2 -0
- package/dist/foundations/motion/tokens.js +2 -0
- package/dist/foundations/profiles/registry.d.ts +3 -0
- package/dist/foundations/profiles/registry.js +3 -0
- package/dist/foundations/profiles/types.d.ts +3 -0
- package/dist/foundations/status/index.d.ts +6 -0
- package/dist/foundations/status/index.js +33 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/patterns/agent-chat/agent-chat.d.ts +9 -0
- package/dist/patterns/agent-chat/agent-chat.js +141 -21
- package/dist/patterns/agent-chat/controller.d.ts +3 -1
- package/dist/patterns/agent-chat/controller.js +92 -16
- package/dist/patterns/agent-chat/index.d.ts +1 -0
- package/dist/patterns/agent-chat/index.js +1 -0
- package/dist/patterns/agent-chat/ndjson.d.ts +5 -0
- package/dist/patterns/agent-chat/ndjson.js +41 -0
- package/dist/patterns/agent-chat/types.d.ts +44 -1
- package/dist/patterns/agent-workspace/agent-workspace.d.ts +28 -0
- package/dist/patterns/agent-workspace/agent-workspace.js +215 -0
- package/dist/patterns/agent-workspace/controller.d.ts +38 -0
- package/dist/patterns/agent-workspace/controller.js +65 -0
- package/dist/patterns/agent-workspace/index.d.ts +2 -0
- package/dist/patterns/agent-workspace/index.js +2 -0
- package/dist/patterns/audit/activity-density.js +6 -0
- package/dist/patterns/audit/audit-log.js +6 -0
- package/dist/patterns/call-console/call-console.d.ts +17 -0
- package/dist/patterns/call-console/call-console.js +107 -0
- package/dist/patterns/call-console/controller.d.ts +23 -0
- package/dist/patterns/call-console/controller.js +50 -0
- package/dist/patterns/call-console/http-transport.d.ts +3 -0
- package/dist/patterns/call-console/http-transport.js +26 -0
- package/dist/patterns/call-console/index.d.ts +4 -0
- package/dist/patterns/call-console/index.js +4 -0
- package/dist/patterns/call-console/types.d.ts +32 -0
- package/dist/patterns/call-console/types.js +8 -0
- package/dist/patterns/comments/comment-thread.js +6 -0
- package/dist/patterns/content-explorer/adapters/action-menu.js +6 -0
- package/dist/patterns/content-explorer/adapters/breadcrumbs.js +6 -0
- package/dist/patterns/content-explorer/adapters/list.js +6 -0
- package/dist/patterns/content-explorer/adapters/table.js +6 -0
- package/dist/patterns/content-explorer/adapters/toolbar.js +6 -0
- package/dist/patterns/content-explorer/content-explorer.js +6 -0
- package/dist/patterns/content-picker/content-picker.js +6 -0
- package/dist/patterns/content-sidebar/content-sidebar.js +6 -0
- package/dist/patterns/content-uploader/content-uploader.js +8 -1
- package/dist/patterns/diff/compare-view.js +6 -0
- package/dist/patterns/diff/diff-viewer.js +6 -0
- package/dist/patterns/file-request/file-request-builder.js +6 -0
- package/dist/patterns/form-wizard/form-wizard.js +6 -0
- package/dist/patterns/form-wizard/wizard-summary.js +6 -0
- package/dist/patterns/governance/governance-panel.js +6 -0
- package/dist/patterns/insights/bar-chart.js +6 -0
- package/dist/patterns/insights/chart-panel.js +6 -0
- package/dist/patterns/insights/donut-chart.js +6 -0
- package/dist/patterns/insights/line-chart.js +6 -0
- package/dist/patterns/insights/metric-card.js +6 -0
- package/dist/patterns/item/bulk-action-bar.js +6 -0
- package/dist/patterns/item/item-details-panel.js +6 -0
- package/dist/patterns/item/item-form.js +6 -0
- package/dist/patterns/item/preview-header.js +6 -0
- package/dist/patterns/lineage/lineage-graph.js +6 -0
- package/dist/patterns/lineage/provenance-strip.js +6 -0
- package/dist/patterns/metadata/metadata-filter-builder.js +6 -0
- package/dist/patterns/metadata/metadata-inspector.js +6 -0
- package/dist/patterns/notifications/notification-bell.js +6 -0
- package/dist/patterns/notifications/notification-inbox.js +6 -0
- package/dist/patterns/preview/annotation-inspector.js +6 -0
- package/dist/patterns/preview/annotation-toolbar.js +6 -0
- package/dist/patterns/preview/preview-element.js +6 -0
- package/dist/patterns/run/index.d.ts +2 -0
- package/dist/patterns/run/index.js +2 -0
- package/dist/patterns/run/progress.d.ts +24 -0
- package/dist/patterns/run/progress.js +32 -0
- package/dist/patterns/run/run-summary.d.ts +21 -0
- package/dist/patterns/run/run-summary.js +82 -0
- package/dist/patterns/run/run-trace.d.ts +1 -0
- package/dist/patterns/run/run-trace.js +20 -3
- package/dist/patterns/search/filter-bar.js +6 -0
- package/dist/patterns/search/saved-view-picker.js +6 -0
- package/dist/patterns/search/search-results-header.js +6 -0
- package/dist/patterns/share/access-stats.js +6 -0
- package/dist/patterns/share/collaborator-avatars.js +6 -0
- package/dist/patterns/share/invite-collaborators-modal.js +6 -0
- package/dist/patterns/share/permission-matrix.js +6 -0
- package/dist/patterns/share/presence.js +6 -0
- package/dist/patterns/share/share-panel.js +6 -0
- package/dist/patterns/share/unified-share-modal.js +6 -0
- package/dist/patterns/signature/signature-ceremony.js +6 -0
- package/dist/patterns/task/review-queue-item.js +6 -0
- package/dist/patterns/task/task-assignment-panel.js +6 -0
- package/dist/patterns/timeline/timeline.js +6 -0
- package/dist/patterns/versions/version-graph.js +6 -0
- package/dist/patterns/versions/version-list.js +6 -0
- package/dist/patterns/work-queue/work-queue.js +6 -0
- package/dist/patterns/work-queue/workload-board.js +6 -0
- package/package.json +13 -1
|
@@ -16,6 +16,8 @@ export class AgentChatController extends Controller {
|
|
|
16
16
|
config;
|
|
17
17
|
sendCounter = 0;
|
|
18
18
|
activeAbortController = null;
|
|
19
|
+
finishActive;
|
|
20
|
+
resolving = new Set();
|
|
19
21
|
constructor(config) {
|
|
20
22
|
super(createInitialState());
|
|
21
23
|
this.config = config;
|
|
@@ -31,7 +33,7 @@ export class AgentChatController extends Controller {
|
|
|
31
33
|
if (!this.state.connected) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
|
-
this.
|
|
36
|
+
this.stop();
|
|
35
37
|
this.activeAbortController = null;
|
|
36
38
|
this.setState(createInitialState());
|
|
37
39
|
this.emit("disconnected", undefined);
|
|
@@ -42,6 +44,7 @@ export class AgentChatController extends Controller {
|
|
|
42
44
|
/** Abort the in-flight generation; whatever already streamed is kept. */
|
|
43
45
|
stop() {
|
|
44
46
|
this.activeAbortController?.abort();
|
|
47
|
+
this.finishActive?.();
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* Send a user turn and stream the agent's reply. One send at a time: a
|
|
@@ -52,7 +55,7 @@ export class AgentChatController extends Controller {
|
|
|
52
55
|
if (!this.state.connected || trimmed.length === 0) {
|
|
53
56
|
return null;
|
|
54
57
|
}
|
|
55
|
-
this.
|
|
58
|
+
this.stop();
|
|
56
59
|
const abortController = typeof AbortController === "function" ? new AbortController() : null;
|
|
57
60
|
this.activeAbortController = abortController;
|
|
58
61
|
this.sendCounter += 1;
|
|
@@ -70,6 +73,9 @@ export class AgentChatController extends Controller {
|
|
|
70
73
|
role: "agent",
|
|
71
74
|
body: "",
|
|
72
75
|
status: "streaming",
|
|
76
|
+
startedAt: Date.now(),
|
|
77
|
+
blocks: [], trace: [], todos: [], options: [],
|
|
78
|
+
completeness: { status: "streaming", missing: [] },
|
|
73
79
|
...(this.config.agentName ? { actor: { name: this.config.agentName } } : {}),
|
|
74
80
|
citations: [],
|
|
75
81
|
proposals: [],
|
|
@@ -89,37 +95,81 @@ export class AgentChatController extends Controller {
|
|
|
89
95
|
});
|
|
90
96
|
this.emit("messagesChanged", { messages: this.state.messages });
|
|
91
97
|
};
|
|
98
|
+
const sequences = new Set();
|
|
99
|
+
let highSequence = 0;
|
|
100
|
+
let done;
|
|
101
|
+
const missing = () => Array.from({ length: highSequence }, (_, i) => i + 1).filter(i => !sequences.has(i));
|
|
102
|
+
const upsert = (items, item) => item.id && items.some(i => i.id === item.id)
|
|
103
|
+
? items.map(i => i.id === item.id ? item : i) : [...items, item];
|
|
92
104
|
const onEvent = (event) => {
|
|
93
105
|
const current = this.getMessage(agentMessage.id);
|
|
94
|
-
if (!current || current.status !== "streaming") {
|
|
106
|
+
if (!current || current.status !== "streaming" || abortController?.signal.aborted || turn !== this.sendCounter || done) {
|
|
95
107
|
return;
|
|
96
108
|
}
|
|
109
|
+
if (event.seq !== undefined) {
|
|
110
|
+
if (!Number.isSafeInteger(event.seq) || event.seq < 1 || event.seq > 100000)
|
|
111
|
+
throw new Error("Invalid stream sequence");
|
|
112
|
+
if (sequences.has(event.seq))
|
|
113
|
+
return;
|
|
114
|
+
sequences.add(event.seq);
|
|
115
|
+
highSequence = Math.max(highSequence, event.seq);
|
|
116
|
+
}
|
|
117
|
+
let patch = {};
|
|
97
118
|
if (event.kind === "delta") {
|
|
98
|
-
|
|
119
|
+
patch = { body: current.body + event.text };
|
|
99
120
|
}
|
|
100
121
|
else if (event.kind === "citation") {
|
|
101
|
-
|
|
122
|
+
patch = { citations: upsert(current.citations, event.citation) };
|
|
102
123
|
}
|
|
103
|
-
else {
|
|
104
|
-
|
|
124
|
+
else if (event.kind === "proposal") {
|
|
125
|
+
patch = { proposals: upsert(current.proposals, event.proposal) };
|
|
126
|
+
}
|
|
127
|
+
else if (event.kind === "block") {
|
|
128
|
+
patch = { blocks: upsert(current.blocks ?? [], event.block) };
|
|
129
|
+
}
|
|
130
|
+
else if (event.kind === "trace") {
|
|
131
|
+
patch = { trace: upsert(current.trace ?? [], event.step) };
|
|
132
|
+
}
|
|
133
|
+
else if (event.kind === "todos") {
|
|
134
|
+
patch = { todos: event.todos };
|
|
135
|
+
}
|
|
136
|
+
else if (event.kind === "options") {
|
|
137
|
+
patch = { options: event.options };
|
|
105
138
|
}
|
|
139
|
+
else if (event.kind === "context") {
|
|
140
|
+
patch = { context: event.context };
|
|
141
|
+
}
|
|
142
|
+
else if (event.kind === "done") {
|
|
143
|
+
done = event.status;
|
|
144
|
+
}
|
|
145
|
+
patchAgentMessage({ ...patch, completeness: { status: "streaming", missing: missing() } });
|
|
106
146
|
};
|
|
147
|
+
let finished = false;
|
|
107
148
|
const finish = (patch) => {
|
|
108
|
-
|
|
149
|
+
if (finished)
|
|
150
|
+
return;
|
|
151
|
+
finished = true;
|
|
152
|
+
const gaps = missing();
|
|
153
|
+
patchAgentMessage({ ...patch, endedAt: Date.now(), completeness: {
|
|
154
|
+
status: patch.status === "error" ? "error" : !done || gaps.length ? "incomplete" : done,
|
|
155
|
+
missing: gaps,
|
|
156
|
+
} });
|
|
109
157
|
// A superseded send must not clear the newer send's streaming flag.
|
|
110
158
|
if (turn === this.sendCounter) {
|
|
111
159
|
this.setState({ ...this.state, streaming: false });
|
|
112
160
|
this.emit("streamingChanged", { streaming: false });
|
|
113
161
|
}
|
|
114
162
|
};
|
|
163
|
+
this.finishActive = () => finish({ status: "complete" });
|
|
115
164
|
try {
|
|
116
165
|
await this.config.transport.sendMessage({
|
|
117
166
|
body: trimmed,
|
|
167
|
+
messageId: agentMessage.id,
|
|
118
168
|
token: this.config.token,
|
|
119
169
|
onEvent,
|
|
120
170
|
...(abortController ? { signal: abortController.signal } : {}),
|
|
121
171
|
});
|
|
122
|
-
finish({ status: "complete" });
|
|
172
|
+
finish({ status: done === "error" ? "error" : "complete" });
|
|
123
173
|
}
|
|
124
174
|
catch (error) {
|
|
125
175
|
if (abortController?.signal.aborted) {
|
|
@@ -129,42 +179,58 @@ export class AgentChatController extends Controller {
|
|
|
129
179
|
else {
|
|
130
180
|
const message = error instanceof Error ? error.message : "The agent reply failed";
|
|
131
181
|
finish({ status: "error", errorMessage: message });
|
|
132
|
-
|
|
133
|
-
|
|
182
|
+
if (turn === this.sendCounter) {
|
|
183
|
+
this.setState({ ...this.state, error: message });
|
|
184
|
+
this.emit("sendFailed", { message });
|
|
185
|
+
}
|
|
134
186
|
}
|
|
135
187
|
}
|
|
136
188
|
finally {
|
|
137
189
|
if (this.activeAbortController === abortController) {
|
|
138
190
|
this.activeAbortController = null;
|
|
191
|
+
this.finishActive = undefined;
|
|
139
192
|
}
|
|
140
193
|
}
|
|
141
194
|
return this.getMessage(agentMessage.id) ?? null;
|
|
142
195
|
}
|
|
143
196
|
/** Resolve a HITL proposal through the transport capability. */
|
|
144
|
-
async resolveAction(proposalId, decision, note) {
|
|
197
|
+
async resolveAction(proposalId, decision, note, messageId) {
|
|
145
198
|
const transport = this.config.transport;
|
|
146
199
|
if (!transport.resolveAction) {
|
|
147
200
|
throw new Error("Agent-chat transport does not support resolveAction");
|
|
148
201
|
}
|
|
149
|
-
const holder = this.state.messages.find(message =>
|
|
202
|
+
const holder = this.state.messages.find(message => (!messageId || message.id === messageId) &&
|
|
203
|
+
message.proposals.some(proposal => proposal.id === proposalId));
|
|
150
204
|
const pending = holder?.proposals.find(proposal => proposal.id === proposalId);
|
|
151
|
-
|
|
205
|
+
const key = `${holder?.id}:${proposalId}`;
|
|
206
|
+
if (!this.state.connected || !holder || !pending || pending.decision || this.resolving.has(key)) {
|
|
152
207
|
return null;
|
|
153
208
|
}
|
|
209
|
+
const patch = (value) => {
|
|
210
|
+
this.setState({ ...this.state, messages: this.state.messages.map(m => m.id === holder.id ? {
|
|
211
|
+
...m, proposals: m.proposals.map(p => p.id === proposalId ? { ...p, ...value } : p),
|
|
212
|
+
} : m) });
|
|
213
|
+
this.emit("messagesChanged", { messages: this.state.messages });
|
|
214
|
+
};
|
|
215
|
+
this.resolving.add(key);
|
|
216
|
+
patch({ resolving: decision });
|
|
154
217
|
try {
|
|
155
218
|
// Called on the transport so class-based implementations keep `this`.
|
|
156
|
-
const
|
|
219
|
+
const response = await transport.resolveAction({
|
|
157
220
|
proposalId,
|
|
221
|
+
messageId: holder.id,
|
|
158
222
|
decision,
|
|
159
223
|
...(note !== undefined ? { note } : {}),
|
|
160
224
|
token: this.config.token,
|
|
161
225
|
});
|
|
226
|
+
const resolved = { ...pending, ...response, decision };
|
|
227
|
+
delete resolved.resolving;
|
|
162
228
|
this.setState({
|
|
163
229
|
...this.state,
|
|
164
230
|
messages: this.state.messages.map(message => message.id === holder.id
|
|
165
231
|
? {
|
|
166
232
|
...message,
|
|
167
|
-
proposals: message.proposals.map(proposal => proposal.id === proposalId ? { ...
|
|
233
|
+
proposals: message.proposals.map(proposal => proposal.id === proposalId ? { ...pending, ...resolved, decision } : proposal),
|
|
168
234
|
}
|
|
169
235
|
: message),
|
|
170
236
|
});
|
|
@@ -178,5 +244,15 @@ export class AgentChatController extends Controller {
|
|
|
178
244
|
this.emit("sendFailed", { message });
|
|
179
245
|
return null;
|
|
180
246
|
}
|
|
247
|
+
finally {
|
|
248
|
+
this.resolving.delete(key);
|
|
249
|
+
const current = this.getMessage(holder.id)?.proposals.find(p => p.id === proposalId);
|
|
250
|
+
if (current?.resolving) {
|
|
251
|
+
const clean = { ...current };
|
|
252
|
+
delete clean.resolving;
|
|
253
|
+
this.setState({ ...this.state, messages: this.state.messages.map(m => m.id === holder.id ? { ...m, proposals: m.proposals.map(p => p.id === proposalId ? clean : p) } : m) });
|
|
254
|
+
this.emit("messagesChanged", { messages: this.state.messages });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
181
257
|
}
|
|
182
258
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Incremental UTF-8 decoding with a bounded line buffer and early-reader cleanup. */
|
|
2
|
+
export async function* readNdjson(stream, options = {}) {
|
|
3
|
+
const reader = stream.getReader();
|
|
4
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5
|
+
const limit = options.maxLineLength ?? 1048576;
|
|
6
|
+
let buffer = "";
|
|
7
|
+
let finished = false;
|
|
8
|
+
const abort = () => { void reader.cancel(options.signal?.reason); };
|
|
9
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
10
|
+
try {
|
|
11
|
+
while (true) {
|
|
12
|
+
options.signal?.throwIfAborted();
|
|
13
|
+
const { value, done } = await reader.read();
|
|
14
|
+
options.signal?.throwIfAborted();
|
|
15
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
16
|
+
let newline;
|
|
17
|
+
while ((newline = buffer.indexOf("\n")) !== -1) {
|
|
18
|
+
const line = buffer.slice(0, newline).trim();
|
|
19
|
+
buffer = buffer.slice(newline + 1);
|
|
20
|
+
if (line.length > limit)
|
|
21
|
+
throw new Error("NDJSON line exceeds limit");
|
|
22
|
+
if (line)
|
|
23
|
+
yield JSON.parse(line);
|
|
24
|
+
}
|
|
25
|
+
if (buffer.length > limit)
|
|
26
|
+
throw new Error("NDJSON line exceeds limit");
|
|
27
|
+
if (done) {
|
|
28
|
+
finished = true;
|
|
29
|
+
if (buffer.trim())
|
|
30
|
+
yield JSON.parse(buffer);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
options.signal?.removeEventListener("abort", abort);
|
|
37
|
+
if (!finished)
|
|
38
|
+
await reader.cancel().catch(() => { });
|
|
39
|
+
reader.releaseLock();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
import type { ResultBlock } from "../../components/collections/result-blocks.js";
|
|
2
|
+
import type { RunStep } from "../run/types.js";
|
|
3
|
+
import type { AgentTodo } from "../run/progress.js";
|
|
1
4
|
export type AgentRole = "user" | "agent";
|
|
5
|
+
export interface AgentPromptOption {
|
|
6
|
+
label: string;
|
|
7
|
+
prompt: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AgentCompleteness {
|
|
10
|
+
status: "streaming" | "complete" | "needs_input" | "error" | "incomplete";
|
|
11
|
+
missing: number[];
|
|
12
|
+
}
|
|
2
13
|
export type AgentMessageStatus = "streaming" | "complete" | "error";
|
|
3
14
|
/** Same shape as the timeline's evidence chips — deep-linkable supporting content. */
|
|
4
15
|
export interface AgentCitation {
|
|
@@ -25,6 +36,8 @@ export interface AgentActionProposal {
|
|
|
25
36
|
}>;
|
|
26
37
|
/** Present once a decision has landed. */
|
|
27
38
|
decision?: AgentActionDecision;
|
|
39
|
+
outcome?: "done" | "failed";
|
|
40
|
+
resolving?: AgentActionDecision;
|
|
28
41
|
note?: string;
|
|
29
42
|
}
|
|
30
43
|
export interface AgentChatMessage {
|
|
@@ -39,11 +52,21 @@ export interface AgentChatMessage {
|
|
|
39
52
|
timestamp?: string;
|
|
40
53
|
citations: AgentCitation[];
|
|
41
54
|
proposals: AgentActionProposal[];
|
|
55
|
+
blocks?: ResultBlock[];
|
|
56
|
+
trace?: RunStep[];
|
|
57
|
+
todos?: AgentTodo[];
|
|
58
|
+
options?: AgentPromptOption[];
|
|
59
|
+
context?: Record<string, unknown>;
|
|
60
|
+
completeness?: AgentCompleteness;
|
|
61
|
+
startedAt?: number;
|
|
62
|
+
endedAt?: number;
|
|
42
63
|
/** Human-readable failure detail when `status` is "error". */
|
|
43
64
|
errorMessage?: string;
|
|
44
65
|
}
|
|
45
66
|
/** Typed stream events a transport delivers while a reply generates. */
|
|
46
67
|
export type AgentStreamEvent = {
|
|
68
|
+
seq?: number;
|
|
69
|
+
} & ({
|
|
47
70
|
kind: "delta";
|
|
48
71
|
text: string;
|
|
49
72
|
} | {
|
|
@@ -52,8 +75,27 @@ export type AgentStreamEvent = {
|
|
|
52
75
|
} | {
|
|
53
76
|
kind: "proposal";
|
|
54
77
|
proposal: AgentActionProposal;
|
|
55
|
-
}
|
|
78
|
+
} | {
|
|
79
|
+
kind: "block";
|
|
80
|
+
block: ResultBlock;
|
|
81
|
+
} | {
|
|
82
|
+
kind: "trace";
|
|
83
|
+
step: RunStep;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "todos";
|
|
86
|
+
todos: AgentTodo[];
|
|
87
|
+
} | {
|
|
88
|
+
kind: "options";
|
|
89
|
+
options: AgentPromptOption[];
|
|
90
|
+
} | {
|
|
91
|
+
kind: "context";
|
|
92
|
+
context: Record<string, unknown>;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "done";
|
|
95
|
+
status: "complete" | "needs_input" | "error";
|
|
96
|
+
});
|
|
56
97
|
export interface AgentSendRequest {
|
|
98
|
+
messageId: string;
|
|
57
99
|
body: string;
|
|
58
100
|
token: string;
|
|
59
101
|
signal?: AbortSignal;
|
|
@@ -61,6 +103,7 @@ export interface AgentSendRequest {
|
|
|
61
103
|
onEvent: (event: AgentStreamEvent) => void;
|
|
62
104
|
}
|
|
63
105
|
export interface AgentResolveActionRequest {
|
|
106
|
+
messageId?: string;
|
|
64
107
|
proposalId: string;
|
|
65
108
|
decision: AgentActionDecision;
|
|
66
109
|
note?: string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/element.js";
|
|
2
|
+
import type { AgentWorkspaceController } from "./controller.js";
|
|
3
|
+
export declare class AgentWorkspace extends BaseElement {
|
|
4
|
+
static readonly tagName = "box-agent-workspace";
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
private model;
|
|
7
|
+
private unsubscribe?;
|
|
8
|
+
private observer?;
|
|
9
|
+
private mobile;
|
|
10
|
+
private chatsOpen;
|
|
11
|
+
private detailsOpen;
|
|
12
|
+
private initialized;
|
|
13
|
+
private renderedActive;
|
|
14
|
+
private sessions;
|
|
15
|
+
get workspaceController(): AgentWorkspaceController | null;
|
|
16
|
+
set workspaceController(value: AgentWorkspaceController | null);
|
|
17
|
+
connectedCallback(): void;
|
|
18
|
+
disconnectedCallback(): void;
|
|
19
|
+
private listen;
|
|
20
|
+
private resize;
|
|
21
|
+
private storageKey;
|
|
22
|
+
private toggle;
|
|
23
|
+
private closeDrawers;
|
|
24
|
+
private syncPanes;
|
|
25
|
+
protected renderTemplate(): void;
|
|
26
|
+
protected setupListeners(): void;
|
|
27
|
+
protected update(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/element.js";
|
|
2
|
+
import { AgentChat } from "../agent-chat/agent-chat.js";
|
|
3
|
+
import { isSafeHref } from "../internal/safe-href.js";
|
|
4
|
+
import { boeReducedMotionPolicy } from "../../foundations/motion/index.js";
|
|
5
|
+
import { trapTabKey } from "../../foundations/a11y/focus.js";
|
|
6
|
+
const DEFAULT_TAG_NAME = "box-agent-workspace";
|
|
7
|
+
export class AgentWorkspace extends BaseElement {
|
|
8
|
+
static tagName = DEFAULT_TAG_NAME;
|
|
9
|
+
static get observedAttributes() { return ["viewer-key"]; }
|
|
10
|
+
model = null;
|
|
11
|
+
unsubscribe;
|
|
12
|
+
observer;
|
|
13
|
+
mobile = false;
|
|
14
|
+
chatsOpen = true;
|
|
15
|
+
detailsOpen = false;
|
|
16
|
+
initialized = false;
|
|
17
|
+
renderedActive = null;
|
|
18
|
+
sessions = new Map();
|
|
19
|
+
get workspaceController() { return this.model; }
|
|
20
|
+
set workspaceController(value) {
|
|
21
|
+
this.unsubscribe?.();
|
|
22
|
+
this.model = value;
|
|
23
|
+
if (this.isConnected)
|
|
24
|
+
this.listen();
|
|
25
|
+
if (this.isRendered)
|
|
26
|
+
this.update();
|
|
27
|
+
}
|
|
28
|
+
connectedCallback() {
|
|
29
|
+
super.connectedCallback();
|
|
30
|
+
this.listen();
|
|
31
|
+
this.resize();
|
|
32
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
33
|
+
this.observer = new ResizeObserver(() => this.resize());
|
|
34
|
+
this.observer.observe(this);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
disconnectedCallback() { this.unsubscribe?.(); this.observer?.disconnect(); }
|
|
38
|
+
listen() { this.unsubscribe?.(); this.unsubscribe = this.model?.subscribe(() => this.update()); }
|
|
39
|
+
resize() {
|
|
40
|
+
const width = this.getBoundingClientRect().width || 1200;
|
|
41
|
+
const mobile = width < 900;
|
|
42
|
+
if (!this.initialized || mobile !== this.mobile) {
|
|
43
|
+
this.chatsOpen = !mobile;
|
|
44
|
+
this.detailsOpen = width >= 1200;
|
|
45
|
+
if (!mobile) {
|
|
46
|
+
try {
|
|
47
|
+
const saved = JSON.parse(localStorage.getItem(this.storageKey()) ?? "null");
|
|
48
|
+
if (saved && typeof saved.chats === "boolean" && typeof saved.details === "boolean") {
|
|
49
|
+
this.chatsOpen = saved.chats;
|
|
50
|
+
this.detailsOpen = saved.details;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch { }
|
|
54
|
+
}
|
|
55
|
+
this.mobile = mobile;
|
|
56
|
+
this.initialized = true;
|
|
57
|
+
}
|
|
58
|
+
this.syncPanes();
|
|
59
|
+
}
|
|
60
|
+
storageKey() { return `boe-workspace:${this.getAttribute("viewer-key") ?? "default"}`; }
|
|
61
|
+
toggle(pane) {
|
|
62
|
+
if (pane === "chats") {
|
|
63
|
+
this.chatsOpen = !this.chatsOpen;
|
|
64
|
+
if (this.mobile)
|
|
65
|
+
this.detailsOpen = false;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.detailsOpen = !this.detailsOpen;
|
|
69
|
+
if (this.mobile)
|
|
70
|
+
this.chatsOpen = false;
|
|
71
|
+
}
|
|
72
|
+
if (!this.mobile)
|
|
73
|
+
try {
|
|
74
|
+
localStorage.setItem(this.storageKey(), JSON.stringify({ chats: this.chatsOpen, details: this.detailsOpen }));
|
|
75
|
+
}
|
|
76
|
+
catch { }
|
|
77
|
+
this.syncPanes();
|
|
78
|
+
if (this.mobile && (this.chatsOpen || this.detailsOpen))
|
|
79
|
+
this.shadowRoot.querySelector(`#${pane} button`)?.focus({ preventScroll: true });
|
|
80
|
+
}
|
|
81
|
+
closeDrawers() { this.chatsOpen = false; this.detailsOpen = false; this.syncPanes(); this.sessions.get(this.model?.activeId ?? "")?.focusComposer(); }
|
|
82
|
+
syncPanes() {
|
|
83
|
+
if (!this.isRendered)
|
|
84
|
+
return;
|
|
85
|
+
this.toggleAttribute("data-mobile", this.mobile);
|
|
86
|
+
for (const [pane, open] of [["chats", this.chatsOpen], ["details", this.detailsOpen]]) {
|
|
87
|
+
const el = this.shadowRoot.getElementById(pane);
|
|
88
|
+
el.inert = !open;
|
|
89
|
+
el.toggleAttribute("data-open", open);
|
|
90
|
+
if (this.mobile && open) {
|
|
91
|
+
el.setAttribute("role", "dialog");
|
|
92
|
+
el.setAttribute("aria-modal", "true");
|
|
93
|
+
el.setAttribute("aria-label", pane === "chats" ? "Conversations" : "Conversation details");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
el.removeAttribute("role");
|
|
97
|
+
el.removeAttribute("aria-modal");
|
|
98
|
+
}
|
|
99
|
+
this.shadowRoot.querySelector(`[data-toggle="${pane}"]`).setAttribute("aria-expanded", String(open));
|
|
100
|
+
}
|
|
101
|
+
const overlaid = this.mobile && (this.chatsOpen || this.detailsOpen);
|
|
102
|
+
this.shadowRoot.querySelector("#conversation").inert = overlaid;
|
|
103
|
+
this.shadowRoot.querySelector("#scrim").hidden = !overlaid;
|
|
104
|
+
for (const chat of this.sessions.values())
|
|
105
|
+
chat.toggleAttribute("hide-citations", this.detailsOpen);
|
|
106
|
+
}
|
|
107
|
+
renderTemplate() {
|
|
108
|
+
AgentChat.register();
|
|
109
|
+
this.shadowRoot.innerHTML = `<style>
|
|
110
|
+
:host{display:block;height:100%;min-height:30rem;color:var(--boe-token-text-text,#222);font:inherit}:host([hidden]){display:none !important}
|
|
111
|
+
*{box-sizing:border-box} section{position:relative;overflow:clip;height:100%;display:flex;min-width:0}
|
|
112
|
+
aside{width:0;overflow:clip;transition:width var(--boe-profile-motion-panel,320ms) ease-out;background:var(--boe-token-surface-surface,#fff)} aside[data-open]{width:280px;flex-shrink:0}
|
|
113
|
+
aside>div{width:280px;height:100%;padding:1rem;overflow:auto} main{min-width:0;flex:1;overflow:clip;display:flex;flex-direction:column;background:var(--boe-token-surface-surface,#fff)} nav{display:flex;justify-content:space-between;padding:.5rem}
|
|
114
|
+
#conversation{flex:1;min-height:0} #sessions,box-agent-chat{height:100%}
|
|
115
|
+
box-agent-chat::part(panel){height:100%;box-sizing:border-box;display:flex;flex-direction:column;border:0;border-radius:0}
|
|
116
|
+
box-agent-chat::part(thread){flex:1;min-height:0;max-block-size:none;align-content:start}
|
|
117
|
+
button,a{font:inherit;color:inherit} button{cursor:pointer;border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:999px;background:var(--boe-token-surface-surface,#fff);min-height:32px;padding:.3rem .8rem;font-size:13px;font-weight:600}
|
|
118
|
+
button:focus-visible,a:focus-visible{outline:2px solid var(--boe-token-surface-surface-brand,#0061d5);outline-offset:2px}
|
|
119
|
+
[part="chat"]{display:block;text-align:left;width:100%;border:0;background:none;padding:.65rem;border-radius:6px}
|
|
120
|
+
[aria-current="page"]{background:var(--boe-token-surface-surface-secondary,#f3f3f3)} small{display:block;color:var(--boe-token-text-text-secondary,#666)}
|
|
121
|
+
#scrim{position:absolute;inset:0;background:#0005;border:0;border-radius:0;z-index:2} [hidden]{display:none!important}
|
|
122
|
+
[data-close]{display:none} :host([data-mobile]) [data-close]{display:block;margin-bottom:.5rem}
|
|
123
|
+
:host([data-mobile]) aside{position:absolute;inset-block:0;z-index:3} :host([data-mobile]) #details{right:0}
|
|
124
|
+
#approvals button,#sources a{display:block;margin:.5rem 0;text-align:left} #context{white-space:pre-wrap;overflow-wrap:anywhere}
|
|
125
|
+
${boeReducedMotionPolicy}</style><section part="workspace">
|
|
126
|
+
<aside id="chats" part="chats"><div><button data-close>Close conversations</button><slot name="chats"><button id="new">New chat</button><div id="list"></div></slot></div></aside>
|
|
127
|
+
<main><nav><button data-toggle="chats" aria-controls="chats">Conversations</button><button data-toggle="details" aria-controls="details">Details</button></nav><div id="conversation" part="conversation"><slot name="conversation"><div id="sessions"></div></slot></div></main>
|
|
128
|
+
<button id="scrim" aria-label="Close panel" hidden></button>
|
|
129
|
+
<aside id="details" part="details"><div><button data-close>Close details</button><slot name="details"><h2>Conversation details</h2><div id="context"></div><h3>Approvals</h3><div id="approvals"></div><h3>Sources</h3><div id="sources"></div></slot></div></aside></section>`;
|
|
130
|
+
}
|
|
131
|
+
setupListeners() {
|
|
132
|
+
this.shadowRoot.querySelector("#new").addEventListener("click", () => { this.model?.newChat(); if (this.mobile)
|
|
133
|
+
this.closeDrawers(); });
|
|
134
|
+
this.shadowRoot.querySelector("#scrim").addEventListener("click", () => this.closeDrawers());
|
|
135
|
+
this.shadowRoot.querySelectorAll("[data-close]").forEach(b => b.addEventListener("click", () => this.closeDrawers()));
|
|
136
|
+
this.shadowRoot.querySelectorAll("[data-toggle]").forEach(b => b.addEventListener("click", () => this.toggle(b.dataset.toggle)));
|
|
137
|
+
this.shadowRoot.addEventListener("keydown", event => {
|
|
138
|
+
const e = event;
|
|
139
|
+
if (e.key === "Escape" && this.mobile) {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
this.closeDrawers();
|
|
142
|
+
}
|
|
143
|
+
if (e.key === "Tab" && this.mobile && (this.chatsOpen || this.detailsOpen))
|
|
144
|
+
trapTabKey(e, this.shadowRoot.getElementById(this.chatsOpen ? "chats" : "details"));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
update() {
|
|
148
|
+
if (!this.isRendered)
|
|
149
|
+
return;
|
|
150
|
+
const list = this.shadowRoot.querySelector("#list");
|
|
151
|
+
const activeRow = this.shadowRoot.activeElement?.getAttribute("data-chat-id");
|
|
152
|
+
list.replaceChildren();
|
|
153
|
+
for (const summary of this.model?.summaries ?? []) {
|
|
154
|
+
const b = document.createElement("button");
|
|
155
|
+
b.setAttribute("part", "chat");
|
|
156
|
+
b.dataset.chatId = summary.id;
|
|
157
|
+
b.textContent = summary.title;
|
|
158
|
+
if (summary.id === this.model?.activeId)
|
|
159
|
+
b.setAttribute("aria-current", "page");
|
|
160
|
+
const small = document.createElement("small");
|
|
161
|
+
small.textContent = summary.status;
|
|
162
|
+
b.append(small);
|
|
163
|
+
b.addEventListener("click", () => { this.model?.select(summary.id); if (this.mobile)
|
|
164
|
+
this.closeDrawers(); });
|
|
165
|
+
list.append(b);
|
|
166
|
+
if (summary.id === activeRow)
|
|
167
|
+
b.focus({ preventScroll: true });
|
|
168
|
+
}
|
|
169
|
+
for (const [id, element] of this.sessions)
|
|
170
|
+
if (!this.model?.chats.some(c => c.id === id && c.controller === element.chatController)) {
|
|
171
|
+
element.remove();
|
|
172
|
+
this.sessions.delete(id);
|
|
173
|
+
}
|
|
174
|
+
for (const chat of this.model?.chats ?? []) {
|
|
175
|
+
let element = this.sessions.get(chat.id);
|
|
176
|
+
if (!element) {
|
|
177
|
+
element = new AgentChat();
|
|
178
|
+
element.chatController = chat.controller;
|
|
179
|
+
this.sessions.set(chat.id, element);
|
|
180
|
+
this.shadowRoot.querySelector("#sessions").append(element);
|
|
181
|
+
}
|
|
182
|
+
element.hidden = chat.id !== this.model?.activeId;
|
|
183
|
+
element.inert = element.hidden;
|
|
184
|
+
}
|
|
185
|
+
const details = this.model?.details;
|
|
186
|
+
this.shadowRoot.querySelector("#context").textContent = details?.context ? Object.entries(details.context).map(([key, value]) => `${key}: ${typeof value === "object" ? JSON.stringify(value) : String(value)}`).join("\n") : "";
|
|
187
|
+
const approvals = this.shadowRoot.querySelector("#approvals");
|
|
188
|
+
approvals.replaceChildren();
|
|
189
|
+
for (const { messageId, proposal } of details?.approvals ?? []) {
|
|
190
|
+
const b = document.createElement("button");
|
|
191
|
+
b.textContent = `${proposal.title} — ${proposal.outcome === "failed" ? "Approved · didn't complete" : proposal.decision ?? "Needs your approval"}`;
|
|
192
|
+
b.addEventListener("click", () => { if (this.mobile)
|
|
193
|
+
this.closeDrawers(); this.sessions.get(this.model?.activeId ?? "")?.focusProposal(messageId, proposal.id); });
|
|
194
|
+
approvals.append(b);
|
|
195
|
+
}
|
|
196
|
+
const sources = this.shadowRoot.querySelector("#sources");
|
|
197
|
+
sources.replaceChildren();
|
|
198
|
+
for (const source of details?.sources ?? []) {
|
|
199
|
+
const el = document.createElement(source.href && isSafeHref(source.href) ? "a" : "span");
|
|
200
|
+
el.textContent = source.label;
|
|
201
|
+
if (el instanceof HTMLAnchorElement) {
|
|
202
|
+
el.href = source.href;
|
|
203
|
+
el.target = "_blank";
|
|
204
|
+
el.rel = "noopener noreferrer";
|
|
205
|
+
}
|
|
206
|
+
sources.append(el);
|
|
207
|
+
}
|
|
208
|
+
this.syncPanes();
|
|
209
|
+
if (this.renderedActive !== this.model?.activeId) {
|
|
210
|
+
this.renderedActive = this.model?.activeId ?? null;
|
|
211
|
+
this.sessions.get(this.renderedActive ?? "")?.focusComposer();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
AgentWorkspace.register();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AgentChatController } from "../agent-chat/controller.js";
|
|
2
|
+
import type { AgentActionProposal, AgentCitation, AgentChatState } from "../agent-chat/types.js";
|
|
3
|
+
export interface WorkspaceChat {
|
|
4
|
+
id: string;
|
|
5
|
+
controller: AgentChatController;
|
|
6
|
+
}
|
|
7
|
+
export interface WorkspaceSummary {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
status: string;
|
|
11
|
+
}
|
|
12
|
+
export interface WorkspaceDetails {
|
|
13
|
+
context?: Record<string, unknown>;
|
|
14
|
+
approvals: {
|
|
15
|
+
messageId: string;
|
|
16
|
+
proposal: AgentActionProposal;
|
|
17
|
+
}[];
|
|
18
|
+
sources: AgentCitation[];
|
|
19
|
+
}
|
|
20
|
+
export declare const conversationTitle: (body: string, max?: number) => string;
|
|
21
|
+
export declare function conversationDetails(state: Readonly<AgentChatState>): WorkspaceDetails;
|
|
22
|
+
/** Owns independent sessions. Selecting a chat never disconnects another one. */
|
|
23
|
+
export declare class AgentWorkspaceController {
|
|
24
|
+
private readonly createSession;
|
|
25
|
+
readonly chats: WorkspaceChat[];
|
|
26
|
+
activeId: string | null;
|
|
27
|
+
private seq;
|
|
28
|
+
private listeners;
|
|
29
|
+
private subscriptions;
|
|
30
|
+
constructor(createSession: () => AgentChatController);
|
|
31
|
+
subscribe(listener: () => void): () => void;
|
|
32
|
+
private emit;
|
|
33
|
+
newChat(): WorkspaceChat;
|
|
34
|
+
select(id: string): void;
|
|
35
|
+
get summaries(): WorkspaceSummary[];
|
|
36
|
+
get details(): WorkspaceDetails;
|
|
37
|
+
destroy(): void;
|
|
38
|
+
}
|