@bbigbang/channel-bridge 0.1.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/assetCache.d.ts +15 -0
- package/dist/assetCache.js +50 -0
- package/dist/contextBundleFormat.d.ts +3 -0
- package/dist/contextBundleFormat.js +82 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2065 -0
- package/dist/messageFormat.d.ts +26 -0
- package/dist/messageFormat.js +85 -0
- package/dist/panelFormat.d.ts +25 -0
- package/dist/panelFormat.js +391 -0
- package/dist/surfaceFormats.d.ts +15 -0
- package/dist/surfaceFormats.js +349 -0
- package/dist/taskUpdateFormat.d.ts +3 -0
- package/dist/taskUpdateFormat.js +17 -0
- package/dist/textPreview.d.ts +9 -0
- package/dist/textPreview.js +38 -0
- package/dist/workspaceFormats.d.ts +7 -0
- package/dist/workspaceFormats.js +41 -0
- package/package.json +36 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type IncomingMessageItem = {
|
|
2
|
+
message_id: string;
|
|
3
|
+
target: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
sender_name: string;
|
|
6
|
+
sender_type: string;
|
|
7
|
+
content: string;
|
|
8
|
+
task_number?: number | null;
|
|
9
|
+
task_status?: string | null;
|
|
10
|
+
task_assignee_name?: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type HistoryMessageItem = {
|
|
13
|
+
id: string;
|
|
14
|
+
target: string;
|
|
15
|
+
seq: number;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
senderName: string;
|
|
18
|
+
senderType: string;
|
|
19
|
+
content: string;
|
|
20
|
+
taskNumber?: number | null;
|
|
21
|
+
taskStatus?: string | null;
|
|
22
|
+
taskAssigneeName?: string | null;
|
|
23
|
+
};
|
|
24
|
+
export declare function formatBeijingPromptTimestamp(input: string): string;
|
|
25
|
+
export declare function formatMessages(messages: IncomingMessageItem[]): string;
|
|
26
|
+
export declare function formatHistoryMessages(messages: HistoryMessageItem[]): string;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const MESSAGE_SEPARATOR = '\n\n---\n\n';
|
|
2
|
+
const BEIJING_TIME_ZONE = 'Asia/Shanghai';
|
|
3
|
+
const BEIJING_DATE_TIME_FORMATTER = new Intl.DateTimeFormat('en-US', {
|
|
4
|
+
timeZone: BEIJING_TIME_ZONE,
|
|
5
|
+
year: 'numeric',
|
|
6
|
+
month: '2-digit',
|
|
7
|
+
day: '2-digit',
|
|
8
|
+
hour: '2-digit',
|
|
9
|
+
minute: '2-digit',
|
|
10
|
+
second: '2-digit',
|
|
11
|
+
hour12: false,
|
|
12
|
+
});
|
|
13
|
+
function formatBeijingDateTime(input) {
|
|
14
|
+
const date = new Date(input);
|
|
15
|
+
if (!Number.isFinite(date.getTime()))
|
|
16
|
+
return input;
|
|
17
|
+
const parts = BEIJING_DATE_TIME_FORMATTER.formatToParts(date);
|
|
18
|
+
const values = {};
|
|
19
|
+
for (const part of parts) {
|
|
20
|
+
if (part.type !== 'literal')
|
|
21
|
+
values[part.type] = part.value;
|
|
22
|
+
}
|
|
23
|
+
return `${values.year}-${values.month}-${values.day} ${values.hour}:${values.minute}:${values.second}`;
|
|
24
|
+
}
|
|
25
|
+
export function formatBeijingPromptTimestamp(input) {
|
|
26
|
+
return `${formatBeijingDateTime(input)} UTC+8`;
|
|
27
|
+
}
|
|
28
|
+
function formatMetadataBlock(lines) {
|
|
29
|
+
return ['[Message metadata]', ...lines.filter(Boolean)].join('\n');
|
|
30
|
+
}
|
|
31
|
+
function formatBodyBlock(label, body) {
|
|
32
|
+
return `${label}\n${body}`;
|
|
33
|
+
}
|
|
34
|
+
function joinParts(parts) {
|
|
35
|
+
return parts.filter((part) => Boolean(part)).join(' ');
|
|
36
|
+
}
|
|
37
|
+
export function formatMessages(messages) {
|
|
38
|
+
return messages
|
|
39
|
+
.map((m) => {
|
|
40
|
+
const taskLine = m.task_number != null
|
|
41
|
+
? `task: #${m.task_number} [${m.task_status ?? 'todo'}]${m.task_assignee_name ? ` @${m.task_assignee_name}` : ''}`
|
|
42
|
+
: null;
|
|
43
|
+
const metadata = formatMetadataBlock([
|
|
44
|
+
joinParts([
|
|
45
|
+
`target: ${m.target}`,
|
|
46
|
+
`msg: ${m.message_id.slice(0, 8)}`,
|
|
47
|
+
]),
|
|
48
|
+
joinParts([
|
|
49
|
+
`time: ${formatBeijingPromptTimestamp(m.timestamp)}`,
|
|
50
|
+
`sender: @${m.sender_name}`,
|
|
51
|
+
m.sender_type === 'agent' ? 'sender_type: agent' : null,
|
|
52
|
+
]),
|
|
53
|
+
taskLine,
|
|
54
|
+
]);
|
|
55
|
+
const body = formatBodyBlock('[Message body]', m.content);
|
|
56
|
+
return `${metadata}\n\n${body}`;
|
|
57
|
+
})
|
|
58
|
+
.join(MESSAGE_SEPARATOR);
|
|
59
|
+
}
|
|
60
|
+
export function formatHistoryMessages(messages) {
|
|
61
|
+
return messages
|
|
62
|
+
.map((m) => {
|
|
63
|
+
const taskLine = m.taskNumber != null
|
|
64
|
+
? `task: #${m.taskNumber} [${m.taskStatus ?? 'todo'}]${m.taskAssigneeName ? ` @${m.taskAssigneeName}` : ''}`
|
|
65
|
+
: null;
|
|
66
|
+
const metadata = formatMetadataBlock([
|
|
67
|
+
joinParts([
|
|
68
|
+
`target: ${m.target}`,
|
|
69
|
+
`msg: ${m.id.slice(0, 8)}`,
|
|
70
|
+
]),
|
|
71
|
+
joinParts([
|
|
72
|
+
`seq: ${m.seq}`,
|
|
73
|
+
`time: ${formatBeijingPromptTimestamp(m.createdAt)}`,
|
|
74
|
+
]),
|
|
75
|
+
joinParts([
|
|
76
|
+
`sender: @${m.senderName}`,
|
|
77
|
+
m.senderType === 'agent' ? 'sender_type: agent' : null,
|
|
78
|
+
]),
|
|
79
|
+
taskLine,
|
|
80
|
+
]);
|
|
81
|
+
const body = formatBodyBlock('[Message body]', m.content);
|
|
82
|
+
return `${metadata}\n\n${body}`;
|
|
83
|
+
})
|
|
84
|
+
.join(MESSAGE_SEPARATOR);
|
|
85
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ComponentContract } from '@bbigbang/protocol';
|
|
2
|
+
/**
|
|
3
|
+
* Format a component contract registry into a human-readable text summary
|
|
4
|
+
* suitable for agent tool output.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatComponentRegistry(contracts: ComponentContract[]): string;
|
|
7
|
+
/**
|
|
8
|
+
* Format a panel state object into a human-readable text summary.
|
|
9
|
+
* Supports both legacy flat shape { state } and new envelope shape { perUser, shared, version }.
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatPanelState(state: unknown): string;
|
|
12
|
+
export declare function formatPanelRows(result: unknown): string;
|
|
13
|
+
export declare function formatPanelEvents(result: unknown): string;
|
|
14
|
+
export declare function formatPanelCollaborators(result: unknown): string;
|
|
15
|
+
/**
|
|
16
|
+
* Format a render_panel success response.
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatRenderPanelSuccess(panelId: string, data?: unknown): string;
|
|
19
|
+
export declare function formatPatchPanelSuccess(data: unknown): string;
|
|
20
|
+
export declare function formatPatchPanelError(data: unknown): string;
|
|
21
|
+
export declare function formatUpsertPanelError(data: unknown): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format a render_panel structured error into an agent-readable message.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatRenderPanelError(data: unknown): string;
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a component contract registry into a human-readable text summary
|
|
3
|
+
* suitable for agent tool output.
|
|
4
|
+
*/
|
|
5
|
+
export function formatComponentRegistry(contracts) {
|
|
6
|
+
if (!contracts.length) {
|
|
7
|
+
return 'No UI components available.';
|
|
8
|
+
}
|
|
9
|
+
const lines = [
|
|
10
|
+
'## Available UI Components',
|
|
11
|
+
'',
|
|
12
|
+
'Treat this output as the canonical current-run source for component props, dataset, state, and supported actions before the first render_panel call.',
|
|
13
|
+
'For detailed RowTemplateGrid template grammar and valid node examples, use the mounted ui-panel skill docs.',
|
|
14
|
+
'Copy the declared prop names, field names, media slots, and supported actions from here instead of guessing.',
|
|
15
|
+
'When the user asks to plan layout first with /panel:plan or /tool:plan, send a chat wireframe and wait for confirmation; do not call render_panel or publish_workspace_tool in that planning run.',
|
|
16
|
+
'For status, monitoring, dashboard, or tool-control views, prefer RowTemplateGrid Columns and concise field labels instead of stacking every field vertically in one Card.',
|
|
17
|
+
'Dataset source kinds currently accepted by render_panel are workspace_jsonl, inline_rows, attachment_manifest, query_handle, and api_jsonl. query_handle uses handle="workspace_jsonl:<path>" for one dynamic paged workspace JSONL file, or handle="workspace_jsonl_list:[\\"a.jsonl\\",\\"b.jsonl\\"]" for ordered multi-file JSONL aggregation. api_jsonl reads JSONL from url:"http://..." or ordered urls:[...] endpoints on the agent node, agent-node allowlisted remote origins, or agent-node allowlisted URL prefixes. api_jsonl auth uses optional node-side auth.profile names only; never include header values or tokens in panel payloads. Relative workspace paths are workspace-relative and absolute workspace paths require the node allowlist.',
|
|
18
|
+
'For RowTemplateGrid, top-level panel.actions is an action registry, not a guaranteed visible toolbar; use ActionBar or ActionButton for explicit visible action controls.',
|
|
19
|
+
'Panel action mode is explicit: use mode="notify_agent" to wake the agent, or mode="platform_exec" with command and optional workspace-relative cwd for direct execution on the agent node. For panel-to-tool promotion, actions may also declare toolKind, paramsSchema, persistent, maxRunSeconds, and idleTimeoutSeconds as Workspace Tool manifest hints; ordinary panel clicks do not prompt for paramsSchema. Legacy rpcCommand/rpcCwd input remains accepted for compatibility.',
|
|
20
|
+
'',
|
|
21
|
+
];
|
|
22
|
+
for (const c of contracts) {
|
|
23
|
+
lines.push(`### ${c.name}`);
|
|
24
|
+
lines.push(`Display: ${c.displayName}`);
|
|
25
|
+
lines.push(`Description: ${c.description}`);
|
|
26
|
+
lines.push(`Data model: ${c.dataModel.kind}`);
|
|
27
|
+
if (c.propsSchema && Object.keys(c.propsSchema).length > 0) {
|
|
28
|
+
lines.push(`Props: ${JSON.stringify(c.propsSchema)}`);
|
|
29
|
+
}
|
|
30
|
+
if (c.datasetSchema) {
|
|
31
|
+
const fields = c.datasetSchema.fields.map((f) => `${f.name}${f.label ? ` label="${f.label}"` : ''} (${f.type}${f.filterable ? ', filterable' : ''}${f.sortable ? ', sortable' : ''})`);
|
|
32
|
+
lines.push(`Dataset fields: ${fields.join(', ')}`);
|
|
33
|
+
if (c.datasetSchema.mediaSlots.length) {
|
|
34
|
+
const slots = c.datasetSchema.mediaSlots.map((s) => `${s.name} (${s.kind}, ${s.displayType})`);
|
|
35
|
+
lines.push(`Media slots: ${slots.join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (c.stateSchema && Object.keys(c.stateSchema).length > 0) {
|
|
39
|
+
lines.push(`State schema: ${JSON.stringify(c.stateSchema)}`);
|
|
40
|
+
}
|
|
41
|
+
if (c.clientInteractions?.length) {
|
|
42
|
+
lines.push(`Client interactions: ${c.clientInteractions.join(', ')}`);
|
|
43
|
+
}
|
|
44
|
+
if (c.supportedActions?.length) {
|
|
45
|
+
const actions = c.supportedActions.map((a) => `${a.id} (${a.label})`);
|
|
46
|
+
lines.push(`Supported actions: ${actions.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
lines.push('');
|
|
49
|
+
}
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Format a panel state object into a human-readable text summary.
|
|
54
|
+
* Supports both legacy flat shape { state } and new envelope shape { perUser, shared, version }.
|
|
55
|
+
*/
|
|
56
|
+
export function formatPanelState(state) {
|
|
57
|
+
const envelope = state && typeof state === 'object' ? state : {};
|
|
58
|
+
// Detect envelope shape (non-direct scope)
|
|
59
|
+
if ('perUser' in envelope || 'shared' in envelope) {
|
|
60
|
+
const perUser = envelope.perUser && typeof envelope.perUser === 'object'
|
|
61
|
+
? envelope.perUser
|
|
62
|
+
: {};
|
|
63
|
+
const shared = envelope.shared && typeof envelope.shared === 'object'
|
|
64
|
+
? envelope.shared
|
|
65
|
+
: {};
|
|
66
|
+
const version = typeof envelope.version === 'number' ? envelope.version : 0;
|
|
67
|
+
const filters = perUser.filters && typeof perUser.filters === 'object'
|
|
68
|
+
? perUser.filters
|
|
69
|
+
: {};
|
|
70
|
+
const sort = perUser.sort && typeof perUser.sort === 'object'
|
|
71
|
+
? perUser.sort
|
|
72
|
+
: {};
|
|
73
|
+
const selection = Array.isArray(shared.selection) ? shared.selection : [];
|
|
74
|
+
const lines = ['## Panel State\n'];
|
|
75
|
+
lines.push(`Version: ${version}`);
|
|
76
|
+
const filterEntries = Object.entries(filters);
|
|
77
|
+
if (filterEntries.length > 0) {
|
|
78
|
+
lines.push('Filters (per-user):');
|
|
79
|
+
for (const [key, value] of filterEntries) {
|
|
80
|
+
lines.push(` ${key}: ${String(value)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
lines.push('Filters (per-user): (none)');
|
|
85
|
+
}
|
|
86
|
+
const sortEntries = Object.entries(sort);
|
|
87
|
+
if (sortEntries.length > 0) {
|
|
88
|
+
lines.push('Sort (per-user):');
|
|
89
|
+
for (const [key, value] of sortEntries) {
|
|
90
|
+
lines.push(` ${key}: ${String(value)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push('Sort (per-user): (none)');
|
|
95
|
+
}
|
|
96
|
+
if (selection.length > 0) {
|
|
97
|
+
lines.push(`Shared selection: ${selection.map(String).join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
lines.push('Shared selection: (none)');
|
|
101
|
+
}
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
// Legacy flat shape (direct scope)
|
|
105
|
+
const s = envelope;
|
|
106
|
+
const filters = s.filters && typeof s.filters === 'object' ? s.filters : {};
|
|
107
|
+
const sort = s.sort && typeof s.sort === 'object' ? s.sort : {};
|
|
108
|
+
const selection = Array.isArray(s.selection) ? s.selection : [];
|
|
109
|
+
const lines = ['## Panel State\n'];
|
|
110
|
+
const filterEntries = Object.entries(filters);
|
|
111
|
+
if (filterEntries.length > 0) {
|
|
112
|
+
lines.push('Filters:');
|
|
113
|
+
for (const [key, value] of filterEntries) {
|
|
114
|
+
lines.push(` ${key}: ${String(value)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
lines.push('Filters: (none)');
|
|
119
|
+
}
|
|
120
|
+
const sortEntries = Object.entries(sort);
|
|
121
|
+
if (sortEntries.length > 0) {
|
|
122
|
+
lines.push('Sort:');
|
|
123
|
+
for (const [key, value] of sortEntries) {
|
|
124
|
+
lines.push(` ${key}: ${String(value)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lines.push('Sort: (none)');
|
|
129
|
+
}
|
|
130
|
+
if (selection.length > 0) {
|
|
131
|
+
lines.push(`Selection: ${selection.map(String).join(', ')}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
lines.push('Selection: (none)');
|
|
135
|
+
}
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
}
|
|
138
|
+
export function formatPanelRows(result) {
|
|
139
|
+
const record = result && typeof result === 'object' ? result : {};
|
|
140
|
+
const panelId = typeof record.panelId === 'string' ? record.panelId : '(unknown)';
|
|
141
|
+
const rows = Array.isArray(record.rows) ? record.rows : [];
|
|
142
|
+
const nextCursor = typeof record.nextCursor === 'string' ? record.nextCursor : null;
|
|
143
|
+
const lines = [`## Panel Rows`, '', `Panel ID: ${panelId}`, `Rows returned: ${rows.length}`];
|
|
144
|
+
for (const rawRow of rows) {
|
|
145
|
+
const row = rawRow && typeof rawRow === 'object' ? rawRow : {};
|
|
146
|
+
const rowIndex = typeof row.rowIndex === 'number' ? row.rowIndex : '?';
|
|
147
|
+
const fields = row.fields && typeof row.fields === 'object' ? row.fields : {};
|
|
148
|
+
const media = row.media && typeof row.media === 'object' ? row.media : {};
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push(`### Row ${rowIndex}`);
|
|
151
|
+
lines.push(`Fields: ${JSON.stringify(fields)}`);
|
|
152
|
+
lines.push(`Media refs: ${JSON.stringify(media)}`);
|
|
153
|
+
}
|
|
154
|
+
if (nextCursor) {
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push(`Next cursor: ${nextCursor}`);
|
|
157
|
+
}
|
|
158
|
+
return lines.join('\n');
|
|
159
|
+
}
|
|
160
|
+
function formatEventRefs(refs) {
|
|
161
|
+
const parts = [];
|
|
162
|
+
for (const key of ['selectedRowIndices', 'selectedRowIds', 'changedRowIndices', 'changedRowIds']) {
|
|
163
|
+
const value = refs[key];
|
|
164
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
165
|
+
parts.push(`${key}=${JSON.stringify(value)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return parts.length > 0 ? parts.join('; ') : '(none)';
|
|
169
|
+
}
|
|
170
|
+
export function formatPanelEvents(result) {
|
|
171
|
+
const record = result && typeof result === 'object' ? result : {};
|
|
172
|
+
const panelId = typeof record.panelId === 'string' ? record.panelId : '(unknown)';
|
|
173
|
+
const events = Array.isArray(record.events) ? record.events : [];
|
|
174
|
+
const nextCursor = typeof record.nextCursor === 'string' ? record.nextCursor : null;
|
|
175
|
+
const lines = [`## Panel Events`, '', `Panel ID: ${panelId}`, `Events returned: ${events.length}`];
|
|
176
|
+
for (const rawEvent of events) {
|
|
177
|
+
const event = rawEvent && typeof rawEvent === 'object' ? rawEvent : {};
|
|
178
|
+
const eventId = typeof event.eventId === 'number' ? event.eventId : '?';
|
|
179
|
+
const kind = typeof event.event === 'string' ? event.event : 'unknown';
|
|
180
|
+
const label = typeof event.label === 'string' ? event.label : kind;
|
|
181
|
+
const summary = typeof event.summary === 'string' ? event.summary : '';
|
|
182
|
+
const wakePolicy = typeof event.wakePolicy === 'string' ? event.wakePolicy : 'persisted';
|
|
183
|
+
const version = typeof event.version === 'number' ? event.version : '?';
|
|
184
|
+
const timestamp = typeof event.timestamp === 'number' ? event.timestamp : null;
|
|
185
|
+
const actor = event.actor && typeof event.actor === 'object' && !Array.isArray(event.actor)
|
|
186
|
+
? event.actor
|
|
187
|
+
: {};
|
|
188
|
+
const actorType = typeof actor.type === 'string' ? actor.type : 'unknown';
|
|
189
|
+
const actorId = typeof actor.id === 'string' && actor.id.trim() ? actor.id : 'unknown';
|
|
190
|
+
const actorRunId = typeof actor.runId === 'string' && actor.runId.trim() ? `, runId=${actor.runId}` : '';
|
|
191
|
+
const refs = event.refs && typeof event.refs === 'object' && !Array.isArray(event.refs)
|
|
192
|
+
? event.refs
|
|
193
|
+
: {};
|
|
194
|
+
const metadataSummary = event.metadataSummary && typeof event.metadataSummary === 'object' && !Array.isArray(event.metadataSummary)
|
|
195
|
+
? event.metadataSummary
|
|
196
|
+
: {};
|
|
197
|
+
lines.push('');
|
|
198
|
+
lines.push(`### Event ${eventId}: ${label}`);
|
|
199
|
+
lines.push(`Type: ${kind}`);
|
|
200
|
+
lines.push(`Wake policy: ${wakePolicy}`);
|
|
201
|
+
lines.push(`Version: ${version}`);
|
|
202
|
+
if (timestamp != null)
|
|
203
|
+
lines.push(`Timestamp: ${timestamp}`);
|
|
204
|
+
lines.push(`Actor: ${actorType}:${actorId}${actorRunId}`);
|
|
205
|
+
if (summary)
|
|
206
|
+
lines.push(`Summary: ${summary}`);
|
|
207
|
+
if (typeof event.submitKind === 'string')
|
|
208
|
+
lines.push(`Submit kind: ${event.submitKind}`);
|
|
209
|
+
if (typeof event.actionId === 'string')
|
|
210
|
+
lines.push(`Action ID: ${event.actionId}`);
|
|
211
|
+
lines.push(`Refs: ${formatEventRefs(refs)}`);
|
|
212
|
+
if (Object.keys(metadataSummary).length > 0) {
|
|
213
|
+
lines.push(`Metadata summary: ${JSON.stringify(metadataSummary)}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (nextCursor) {
|
|
217
|
+
lines.push('');
|
|
218
|
+
lines.push(`Next cursor: ${nextCursor}`);
|
|
219
|
+
}
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
export function formatPanelCollaborators(result) {
|
|
223
|
+
const record = result && typeof result === 'object' ? result : {};
|
|
224
|
+
const panelId = typeof record.panelId === 'string' ? record.panelId : '(unknown)';
|
|
225
|
+
const collaborators = Array.isArray(record.collaborators) ? record.collaborators : [];
|
|
226
|
+
const lines = ['## Panel Collaborators', '', `Panel ID: ${panelId}`];
|
|
227
|
+
if (collaborators.length === 0) {
|
|
228
|
+
lines.push('Collaborators: (none)');
|
|
229
|
+
return lines.join('\n');
|
|
230
|
+
}
|
|
231
|
+
lines.push('Collaborators:');
|
|
232
|
+
for (const raw of collaborators) {
|
|
233
|
+
const item = raw && typeof raw === 'object' ? raw : {};
|
|
234
|
+
const agentId = typeof item.agentId === 'string' ? item.agentId : '(unknown)';
|
|
235
|
+
const name = typeof item.name === 'string' && item.name.trim() ? item.name.trim() : agentId;
|
|
236
|
+
const role = typeof item.role === 'string' ? item.role : 'collaborator';
|
|
237
|
+
const addedBy = typeof item.addedBy === 'string' && item.addedBy.trim() ? `, addedBy=${item.addedBy}` : '';
|
|
238
|
+
lines.push(`- ${name} (${agentId}): ${role}${addedBy}`);
|
|
239
|
+
}
|
|
240
|
+
return lines.join('\n');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Format a render_panel success response.
|
|
244
|
+
*/
|
|
245
|
+
export function formatRenderPanelSuccess(panelId, data) {
|
|
246
|
+
const record = data && typeof data === 'object' ? data : {};
|
|
247
|
+
const diagnostics = record.diagnostics && typeof record.diagnostics === 'object'
|
|
248
|
+
? record.diagnostics
|
|
249
|
+
: {};
|
|
250
|
+
const duplicateCandidates = Array.isArray(diagnostics.duplicatePanelCandidates)
|
|
251
|
+
? diagnostics.duplicatePanelCandidates
|
|
252
|
+
: [];
|
|
253
|
+
const lines = [
|
|
254
|
+
`Panel created successfully. Panel ID: ${panelId}`,
|
|
255
|
+
'',
|
|
256
|
+
`Spot-check with read_panel_rows(panel_id="${panelId}", limit=3), remember the panel_id plus dataset path, then use send_message(panel_ids=["${panelId}"]) to share it in the conversation.`,
|
|
257
|
+
`This panel was created without a stable handle; continue this exact panel with patch_panel(panel_id="${panelId}", ...). For future recurring annotation/evaluation/workbench flows, start with upsert_panel(handle=...) so updates land in the same handle-bound panel.`,
|
|
258
|
+
];
|
|
259
|
+
if (duplicateCandidates.length > 0) {
|
|
260
|
+
lines.push('');
|
|
261
|
+
lines.push('Duplicate panel diagnostic: this conversation already has similar panels.');
|
|
262
|
+
for (const rawCandidate of duplicateCandidates.slice(0, 3)) {
|
|
263
|
+
const candidate = rawCandidate && typeof rawCandidate === 'object' ? rawCandidate : {};
|
|
264
|
+
const candidateId = typeof candidate.panelId === 'string' ? candidate.panelId : '(unknown)';
|
|
265
|
+
const handle = typeof candidate.handle === 'string' && candidate.handle.trim() ? ` handle=${candidate.handle}` : '';
|
|
266
|
+
const title = typeof candidate.title === 'string' && candidate.title.trim() ? ` title="${candidate.title}"` : '';
|
|
267
|
+
const version = typeof candidate.version === 'number' ? ` version=${candidate.version}` : '';
|
|
268
|
+
const reason = typeof candidate.reason === 'string' ? ` reason=${candidate.reason}` : '';
|
|
269
|
+
lines.push(`- ${candidateId}${handle}${title}${version}${reason}`);
|
|
270
|
+
}
|
|
271
|
+
if (typeof diagnostics.recommendation === 'string' && diagnostics.recommendation.trim()) {
|
|
272
|
+
lines.push(`Recommendation: ${diagnostics.recommendation}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return lines.join('\n');
|
|
276
|
+
}
|
|
277
|
+
export function formatPatchPanelSuccess(data) {
|
|
278
|
+
const record = data && typeof data === 'object' ? data : {};
|
|
279
|
+
const panelId = typeof record.panel_id === 'string' ? record.panel_id : '(unknown)';
|
|
280
|
+
const version = typeof record.version === 'number' ? record.version : '(unknown)';
|
|
281
|
+
const status = typeof record.status === 'string' ? record.status : '(unknown)';
|
|
282
|
+
const rowCount = typeof record.row_count === 'number' ? record.row_count : '(unknown)';
|
|
283
|
+
const changed = Array.isArray(record.changed) ? record.changed.map(String).join(', ') : '(unknown)';
|
|
284
|
+
return [
|
|
285
|
+
'Panel patched successfully.',
|
|
286
|
+
`Panel ID: ${panelId}`,
|
|
287
|
+
`Version: ${version}`,
|
|
288
|
+
`Status: ${status}`,
|
|
289
|
+
`Rows: ${rowCount}`,
|
|
290
|
+
`Changed: ${changed}`,
|
|
291
|
+
].join('\n');
|
|
292
|
+
}
|
|
293
|
+
export function formatPatchPanelError(data) {
|
|
294
|
+
if (!data || typeof data !== 'object') {
|
|
295
|
+
return 'Panel patch failed: unknown error';
|
|
296
|
+
}
|
|
297
|
+
const record = data;
|
|
298
|
+
const failureClass = typeof record.failure_class === 'string' ? record.failure_class : 'unknown_error';
|
|
299
|
+
const details = record.details && typeof record.details === 'object'
|
|
300
|
+
? record.details
|
|
301
|
+
: {};
|
|
302
|
+
const lines = [
|
|
303
|
+
`Panel patch failed: ${failureClass}`,
|
|
304
|
+
`Details: ${JSON.stringify(details, null, 2)}`,
|
|
305
|
+
];
|
|
306
|
+
if (failureClass === 'version_conflict') {
|
|
307
|
+
lines.push('Recommended next step: read_panel_events/read_panel_rows or read_panel_state as needed, merge any user changes, then retry with expected_version set to currentVersion. Do not blindly overwrite saved user state or annotations.');
|
|
308
|
+
}
|
|
309
|
+
return lines.join('\n');
|
|
310
|
+
}
|
|
311
|
+
export function formatUpsertPanelError(data) {
|
|
312
|
+
if (!data || typeof data !== 'object') {
|
|
313
|
+
return 'Panel upsert failed: unknown error';
|
|
314
|
+
}
|
|
315
|
+
const record = data;
|
|
316
|
+
const failureClass = typeof record.failure_class === 'string' ? record.failure_class : 'unknown_error';
|
|
317
|
+
const details = record.details && typeof record.details === 'object'
|
|
318
|
+
? record.details
|
|
319
|
+
: {};
|
|
320
|
+
const lines = [
|
|
321
|
+
`Panel upsert failed: ${failureClass}`,
|
|
322
|
+
`Details: ${JSON.stringify(details, null, 2)}`,
|
|
323
|
+
];
|
|
324
|
+
if (failureClass === 'version_conflict') {
|
|
325
|
+
lines.push('Recommended next step: read_panel_events/read_panel_rows or read_panel_state as needed, merge any user changes, then retry upsert_panel(...) with expected_version set to currentVersion. Do not blindly overwrite saved user state or annotations.');
|
|
326
|
+
}
|
|
327
|
+
else if (failureClass === 'renderer_switch_forbidden') {
|
|
328
|
+
lines.push('Recommended next step: keep the existing handle on its current component, or intentionally create a separate new panel with a different stable handle.');
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
lines.push('Correct the issue and retry upsert_panel(...) with the same stable handle.');
|
|
332
|
+
}
|
|
333
|
+
return lines.join('\n');
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Format a render_panel structured error into an agent-readable message.
|
|
337
|
+
*/
|
|
338
|
+
export function formatRenderPanelError(data) {
|
|
339
|
+
if (!data || typeof data !== 'object') {
|
|
340
|
+
return 'Panel creation failed: unknown error';
|
|
341
|
+
}
|
|
342
|
+
const record = data;
|
|
343
|
+
const failureClass = typeof record.failure_class === 'string' ? record.failure_class : 'unknown_error';
|
|
344
|
+
const details = record.details && typeof record.details === 'object'
|
|
345
|
+
? record.details
|
|
346
|
+
: {};
|
|
347
|
+
const lines = [`Panel creation failed: ${failureClass}`];
|
|
348
|
+
if (failureClass === 'unknown_component') {
|
|
349
|
+
const component = typeof details.component === 'string' ? details.component : 'unknown';
|
|
350
|
+
lines.push(`Component "${component}" is not in the curated registry. Use list_ui_components() to see available components.`);
|
|
351
|
+
}
|
|
352
|
+
else if (failureClass === 'invalid_props') {
|
|
353
|
+
lines.push(`Props do not match the component's propsSchema.`);
|
|
354
|
+
lines.push(`Details: ${JSON.stringify(details, null, 2)}`);
|
|
355
|
+
lines.push('Re-read list_ui_components() before retrying so the component-level contract matches exactly.');
|
|
356
|
+
lines.push('If this is RowTemplateGrid, re-check the mounted ui-panel skill docs for the template grammar before retrying.');
|
|
357
|
+
}
|
|
358
|
+
else if (failureClass === 'invalid_dataset') {
|
|
359
|
+
lines.push(`Dataset rows do not match the component's datasetSchema.`);
|
|
360
|
+
lines.push(`Details: ${JSON.stringify(details, null, 2)}`);
|
|
361
|
+
lines.push('Re-read list_ui_components() before retrying so the dataset fields and media slots match the current contract.');
|
|
362
|
+
lines.push('If this is RowTemplateGrid, re-check the mounted ui-panel skill docs before retrying in case the template references undeclared fields or slots.');
|
|
363
|
+
}
|
|
364
|
+
else if (failureClass === 'missing_required_fields') {
|
|
365
|
+
const missingFields = Array.isArray(details.missingFields) ? details.missingFields : [];
|
|
366
|
+
lines.push(`Missing required fields: ${missingFields.join(', ')}`);
|
|
367
|
+
}
|
|
368
|
+
else if (failureClass === 'dataset_path_unreadable') {
|
|
369
|
+
const path = typeof details.path === 'string' ? details.path : 'unknown';
|
|
370
|
+
const reason = typeof details.reason === 'string' ? details.reason : 'unknown';
|
|
371
|
+
lines.push(`Cannot read dataset at "${path}": ${reason}`);
|
|
372
|
+
}
|
|
373
|
+
else if (failureClass === 'manifest_too_large') {
|
|
374
|
+
const rowCount = typeof details.rowCount === 'number' ? details.rowCount : 'unknown';
|
|
375
|
+
const max = typeof details.max === 'number' ? details.max : 'unknown';
|
|
376
|
+
lines.push(`Manifest has ${rowCount} rows, exceeding the maximum of ${max}.`);
|
|
377
|
+
}
|
|
378
|
+
else if (failureClass === 'ingest_failed') {
|
|
379
|
+
const reason = typeof details.reason === 'string' ? details.reason : 'unknown';
|
|
380
|
+
lines.push(`Ingest failed: ${reason}`);
|
|
381
|
+
}
|
|
382
|
+
else if (failureClass === 'version_conflict') {
|
|
383
|
+
lines.push(`Details: ${JSON.stringify(details, null, 2)}`);
|
|
384
|
+
lines.push('Recommended next step: read_panel_events/read_panel_rows or read_panel_state as needed, merge any user changes, then retry with expected_version set to currentVersion. Do not blindly overwrite saved user state or annotations.');
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
lines.push(`Details: ${JSON.stringify(details, null, 2)}`);
|
|
388
|
+
}
|
|
389
|
+
lines.push('\nCorrect the issue and retry render_panel(...).');
|
|
390
|
+
return lines.join('\n');
|
|
391
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RuntimePresenceResponse, SelfStateResponse, SurfaceConversationItem, SurfaceSummaryPayload } from '@bbigbang/protocol';
|
|
2
|
+
export type ToolFormat = 'text' | 'json' | 'both';
|
|
3
|
+
export type { SelfStateResponse, SurfaceConversationItem, SurfaceSummaryPayload };
|
|
4
|
+
type SurfaceConversationView = Omit<SurfaceConversationItem, 'summary'> & {
|
|
5
|
+
context?: SurfaceSummaryPayload;
|
|
6
|
+
summary?: SurfaceSummaryPayload;
|
|
7
|
+
};
|
|
8
|
+
export declare function formatToolResult(format: ToolFormat, text: string, data: unknown): string;
|
|
9
|
+
export declare function formatSendMessageDiagnostics(data: Record<string, unknown>): string;
|
|
10
|
+
export declare function formatConversationListText(conversations: SurfaceConversationView[]): string;
|
|
11
|
+
export declare function formatConversationSummaryToolText(conversation: SurfaceConversationView): string;
|
|
12
|
+
export declare function formatRuntimePresenceToolText(presence: RuntimePresenceResponse): string;
|
|
13
|
+
export declare function buildCompactRuntimePresenceData(presence: RuntimePresenceResponse): Record<string, unknown>;
|
|
14
|
+
export declare function formatSelfStateToolText(state: SelfStateResponse): string;
|
|
15
|
+
export declare function buildCompactSelfStateData(state: SelfStateResponse): Record<string, unknown>;
|