@bbigbang/cli 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.
Files changed (34) hide show
  1. package/dist/cliSupport.d.ts +166 -0
  2. package/dist/cliSupport.js +1306 -0
  3. package/dist/commandCatalog.d.ts +36 -0
  4. package/dist/commandCatalog.js +152 -0
  5. package/dist/commands/actionCommands.d.ts +3 -0
  6. package/dist/commands/actionCommands.js +43 -0
  7. package/dist/commands/authManualCommands.d.ts +3 -0
  8. package/dist/commands/authManualCommands.js +60 -0
  9. package/dist/commands/channelWorkspaceCommands.d.ts +3 -0
  10. package/dist/commands/channelWorkspaceCommands.js +105 -0
  11. package/dist/commands/contextCommands.d.ts +3 -0
  12. package/dist/commands/contextCommands.js +253 -0
  13. package/dist/commands/memoryCommands.d.ts +3 -0
  14. package/dist/commands/memoryCommands.js +154 -0
  15. package/dist/commands/messageCommands.d.ts +3 -0
  16. package/dist/commands/messageCommands.js +241 -0
  17. package/dist/commands/panelCommands.d.ts +3 -0
  18. package/dist/commands/panelCommands.js +218 -0
  19. package/dist/commands/reminderCommands.d.ts +3 -0
  20. package/dist/commands/reminderCommands.js +220 -0
  21. package/dist/commands/skillToolAttachmentCommands.d.ts +3 -0
  22. package/dist/commands/skillToolAttachmentCommands.js +261 -0
  23. package/dist/commands/taskCommands.d.ts +3 -0
  24. package/dist/commands/taskCommands.js +195 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +8 -0
  27. package/dist/manual/generated/command-catalog.json +12452 -0
  28. package/dist/manual/topics/cli-overview.md +116 -0
  29. package/dist/manual/topics/commands.md +706 -0
  30. package/dist/manual/topics/examples.md +194 -0
  31. package/dist/manual/topics/index.md +11 -0
  32. package/dist/program.d.ts +5 -0
  33. package/dist/program.js +52 -0
  34. package/package.json +43 -0
@@ -0,0 +1,1306 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, extname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { AGENT_COMMAND_ERROR_CODES, AgentApiClient, AgentCommandError, formatBigbangReceipt, formatMessageHeld, formatPanelVersionConflict, formatTaskHeld, loadAgentCommandContext, readOptionalStdin, readRequiredJsonStdin, readRequiredStdin, resolveCurrentRunContext, toAgentCommandError, } from '@bbigbang/agent-command';
5
+ export const MANUAL_TOPICS = ['index', 'cli-overview', 'examples', 'commands'];
6
+ export const MANUAL_TOPICS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'manual', 'topics');
7
+ export const MANUAL_GENERATED_DIR = join(dirname(fileURLToPath(import.meta.url)), 'manual', 'generated');
8
+ export function manualTopicPath(topic) {
9
+ return join(MANUAL_TOPICS_DIR, `${topic}.md`);
10
+ }
11
+ export function commandCatalogPath() {
12
+ return join(MANUAL_GENERATED_DIR, 'command-catalog.json');
13
+ }
14
+ export function readManualTopic(runtime, topic) {
15
+ return runtime.readFile(manualTopicPath(topic));
16
+ }
17
+ export function readCommandCatalog(runtime) {
18
+ const raw = runtime.readFile(commandCatalogPath());
19
+ return JSON.parse(raw);
20
+ }
21
+ export function isManualTopic(topic) {
22
+ return MANUAL_TOPICS.includes(topic);
23
+ }
24
+ export const ATTACHMENT_UPLOAD_MAX_BYTES = 5 * 1024 * 1024;
25
+ export const ATTACHMENT_TEXT_PREVIEW_MAX_CHARS = 4000;
26
+ export const MUTATION_HINTS = {
27
+ messageSend: 'Next: run `bigbang message check` for replies.',
28
+ taskCreate: 'Next: run `bigbang task list` to review created tasks.',
29
+ taskClaim: 'Next: run `bigbang task update --status in_progress` to start work.',
30
+ panelInspect: 'Next: run `bigbang panel read-rows --panel-id <id>` to inspect.',
31
+ toolPublish: 'Next: run `bigbang tool status --tool-id <id>` to verify.',
32
+ attachmentUpload: 'Next: run `bigbang message send --attachment-id <id>` to share it.',
33
+ actionPrepare: 'Next: wait for a human admin to confirm or cancel the action card.',
34
+ };
35
+ export function withHint(text, hint) {
36
+ return `${text}\n${hint}`;
37
+ }
38
+ export const MIME_BY_EXTENSION = {
39
+ '.txt': 'text/plain',
40
+ '.md': 'text/markdown',
41
+ '.markdown': 'text/markdown',
42
+ '.log': 'text/plain',
43
+ '.json': 'application/json',
44
+ '.jsonl': 'application/x-ndjson',
45
+ '.yaml': 'application/yaml',
46
+ '.yml': 'application/yaml',
47
+ '.toml': 'application/toml',
48
+ '.csv': 'text/csv',
49
+ '.ts': 'text/typescript',
50
+ '.tsx': 'text/typescript',
51
+ '.js': 'text/javascript',
52
+ '.jsx': 'text/javascript',
53
+ '.mjs': 'text/javascript',
54
+ '.py': 'text/x-python',
55
+ '.sh': 'text/x-shellscript',
56
+ '.sql': 'text/plain',
57
+ '.tex': 'application/x-tex',
58
+ '.html': 'text/html',
59
+ '.css': 'text/css',
60
+ '.xml': 'application/xml',
61
+ '.png': 'image/png',
62
+ '.jpg': 'image/jpeg',
63
+ '.jpeg': 'image/jpeg',
64
+ '.gif': 'image/gif',
65
+ '.webp': 'image/webp',
66
+ '.svg': 'image/svg+xml',
67
+ '.pdf': 'application/pdf',
68
+ };
69
+ export function defaultIo() {
70
+ return {
71
+ stdin: process.stdin,
72
+ stdout: process.stdout,
73
+ stderr: process.stderr,
74
+ };
75
+ }
76
+ export function parseOutputFormat(raw) {
77
+ return raw === 'json' ? 'json' : 'text';
78
+ }
79
+ export function writeOutput(runtime, text, data) {
80
+ if (runtime.outputFormat === 'json') {
81
+ runtime.io.stdout.write(`${JSON.stringify(data)}\n`);
82
+ return;
83
+ }
84
+ runtime.io.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
85
+ }
86
+ export function writeJson(runtime, data) {
87
+ runtime.io.stdout.write(`${JSON.stringify(data)}\n`);
88
+ }
89
+ export function writeReceipt(runtime, receipt) {
90
+ runtime.io.stderr.write(`${formatBigbangReceipt(receipt)}\n`);
91
+ }
92
+ export function createRuntime(program, options) {
93
+ const context = loadAgentCommandContext({ env: options.env, readFile: options.readFile });
94
+ const runContext = resolveCurrentRunContext(context, options.readFile);
95
+ const clientContext = {
96
+ ...context,
97
+ runId: runContext.runId,
98
+ turnId: runContext.turnId,
99
+ traceId: runContext.traceId,
100
+ };
101
+ return {
102
+ context,
103
+ runId: runContext.runId,
104
+ turnId: runContext.turnId,
105
+ traceId: runContext.traceId,
106
+ client: new AgentApiClient(clientContext, { fetch: options.fetch, env: options.env, sleep: options.sleep }),
107
+ fetch: options.fetch ?? fetch,
108
+ env: options.env ?? process.env,
109
+ sleep: options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
110
+ readFile: options.readFile ?? ((path) => readFileSync(path, 'utf8')),
111
+ io: options.io ?? defaultIo(),
112
+ outputFormat: parseOutputFormat(program.opts().format),
113
+ };
114
+ }
115
+ export function repeatOption(value, previous = []) {
116
+ return [...previous, value];
117
+ }
118
+ export function parsePositiveInteger(name, raw) {
119
+ if (raw === undefined || raw === null || raw === '')
120
+ return undefined;
121
+ const value = Number(raw);
122
+ if (!Number.isInteger(value) || value <= 0) {
123
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `--${name} must be a positive integer.`);
124
+ }
125
+ return value;
126
+ }
127
+ export function parseRequiredPositiveInteger(name, raw) {
128
+ const value = parsePositiveInteger(name, raw);
129
+ if (value === undefined) {
130
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `--${name} is required.`);
131
+ }
132
+ return value;
133
+ }
134
+ export function parsePositiveIntegerList(name, raw) {
135
+ if (!Array.isArray(raw) || raw.length === 0)
136
+ return undefined;
137
+ return raw.map((item) => parseRequiredPositiveInteger(name, item));
138
+ }
139
+ export function parseNumber(name, raw) {
140
+ if (raw === undefined || raw === null || raw === '')
141
+ return undefined;
142
+ const value = Number(raw);
143
+ if (!Number.isFinite(value)) {
144
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `--${name} must be a number.`);
145
+ }
146
+ return value;
147
+ }
148
+ export function parseRequiredNumber(name, raw) {
149
+ const value = parseNumber(name, raw);
150
+ if (value === undefined) {
151
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `--${name} is required.`);
152
+ }
153
+ return value;
154
+ }
155
+ export function parseNonNegativeInteger(name, raw) {
156
+ if (raw === undefined || raw === null || raw === '')
157
+ return undefined;
158
+ const value = Number(raw);
159
+ if (!Number.isInteger(value) || value < 0) {
160
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `--${name} must be a non-negative integer.`);
161
+ }
162
+ return value;
163
+ }
164
+ export function requireString(value, flag) {
165
+ const stringValue = typeof value === 'string' ? value.trim() : '';
166
+ if (!stringValue)
167
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, `${flag} is required.`);
168
+ return stringValue;
169
+ }
170
+ export function requireConversationId(runtime, commandName) {
171
+ const conversationId = runtime.context.conversationId?.trim();
172
+ if (!conversationId) {
173
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.MISSING_CONVERSATION_ID, `${commandName} requires BIGBANG_CONVERSATION_ID for the current DM reminder scope.`, { suggestedNextAction: 'Run this command from an Bigbang managed direct-message runtime.' });
174
+ }
175
+ return conversationId;
176
+ }
177
+ export function responseError(response, fallback) {
178
+ return new AgentCommandError(response.errorCode ?? (response.status >= 500 ? AGENT_COMMAND_ERROR_CODES.SERVER_5XX : AGENT_COMMAND_ERROR_CODES.REQUEST_FAILED), response.error ?? fallback, {
179
+ status: response.status,
180
+ suggestedNextAction: response.suggestedNextAction,
181
+ });
182
+ }
183
+ export function buildCoreUrl(runtime, path) {
184
+ const normalizedBase = runtime.context.serverUrl.replace(/\/+$/, '');
185
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
186
+ return `${normalizedBase}${normalizedPath}`;
187
+ }
188
+ export async function fetchJsonRaw(runtime, path, init = {}, options = {}) {
189
+ const retryBudget = resolveRawRetryBudget(runtime.env, init.method ?? 'GET', options.retry);
190
+ for (let attempt = 0; attempt <= retryBudget; attempt += 1) {
191
+ try {
192
+ const response = await fetchJsonRawOnce(runtime, path, init);
193
+ if (!shouldRetryJsonRawResponse(response) || attempt >= retryBudget) {
194
+ return response;
195
+ }
196
+ }
197
+ catch (error) {
198
+ if (!isNetworkAgentCommandError(error) || attempt >= retryBudget)
199
+ throw error;
200
+ }
201
+ await runtime.sleep(rawRetryDelayMs(attempt));
202
+ }
203
+ return fetchJsonRawOnce(runtime, path, init);
204
+ }
205
+ async function fetchJsonRawOnce(runtime, path, init = {}) {
206
+ let response;
207
+ try {
208
+ response = await runtime.fetch(buildCoreUrl(runtime, path), {
209
+ ...init,
210
+ headers: {
211
+ Authorization: `Bearer ${runtime.context.token}`,
212
+ 'X-Bigbang-Agent-Surface': 'bigbang',
213
+ ...(init.headers ?? {}),
214
+ },
215
+ });
216
+ }
217
+ catch (error) {
218
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.NETWORK_ERROR, `Request to Bigbang core failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error, suggestedNextAction: 'Check that the core server is reachable from this agent runtime.' });
219
+ }
220
+ const contentType = response.headers.get('content-type') ?? '';
221
+ if (!contentType.includes('application/json')) {
222
+ const text = await response.text();
223
+ return {
224
+ ok: false,
225
+ status: response.status,
226
+ data: null,
227
+ error: `Expected JSON response from core but received ${contentType || 'unknown content type'}: ${text.slice(0, 200)}`,
228
+ errorCode: AGENT_COMMAND_ERROR_CODES.NON_JSON_RESPONSE,
229
+ };
230
+ }
231
+ const data = await response.json();
232
+ if (response.ok)
233
+ return { ok: true, status: response.status, data };
234
+ const record = data && typeof data === 'object' ? data : {};
235
+ return {
236
+ ok: false,
237
+ status: response.status,
238
+ data,
239
+ error: typeof record.error === 'string' ? record.error : `Request failed with HTTP ${response.status}`,
240
+ errorCode: typeof record.error_code === 'string' ? record.error_code : undefined,
241
+ suggestedNextAction: typeof record.suggested_next_action === 'string' ? record.suggested_next_action : undefined,
242
+ };
243
+ }
244
+ export async function fetchBinaryRaw(runtime, path, options = {}) {
245
+ const retryBudget = resolveRawRetryBudget(runtime.env, 'GET', options.retry);
246
+ for (let attempt = 0; attempt <= retryBudget; attempt += 1) {
247
+ try {
248
+ const response = await fetchBinaryRawOnce(runtime, path);
249
+ if (response.ok || !shouldRetryRawStatus(response.status) || attempt >= retryBudget) {
250
+ return response;
251
+ }
252
+ }
253
+ catch (error) {
254
+ if (!isNetworkAgentCommandError(error) || attempt >= retryBudget)
255
+ throw error;
256
+ }
257
+ await runtime.sleep(rawRetryDelayMs(attempt));
258
+ }
259
+ return fetchBinaryRawOnce(runtime, path);
260
+ }
261
+ async function fetchBinaryRawOnce(runtime, path) {
262
+ let response;
263
+ try {
264
+ response = await runtime.fetch(buildCoreUrl(runtime, path), {
265
+ headers: {
266
+ Authorization: `Bearer ${runtime.context.token}`,
267
+ 'X-Bigbang-Agent-Surface': 'bigbang',
268
+ },
269
+ });
270
+ }
271
+ catch (error) {
272
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.NETWORK_ERROR, `Request to Bigbang core failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error, suggestedNextAction: 'Check that the core server is reachable from this agent runtime.' });
273
+ }
274
+ const contentType = response.headers.get('content-type') ?? '';
275
+ if (!response.ok) {
276
+ const text = await response.text();
277
+ return { ok: false, status: response.status, error: text.slice(0, 300) || `HTTP ${response.status}` };
278
+ }
279
+ return {
280
+ ok: true,
281
+ status: response.status,
282
+ buffer: Buffer.from(await response.arrayBuffer()),
283
+ contentType,
284
+ };
285
+ }
286
+ function resolveRawRetryBudget(env, method, retryMode = 'auto') {
287
+ if (retryMode === 'never')
288
+ return 0;
289
+ if (retryMode === 'auto' && method.toUpperCase() !== 'GET')
290
+ return 0;
291
+ const raw = env.BIGBANG_CLI_MAX_RETRIES?.trim();
292
+ if (raw !== undefined && raw !== '') {
293
+ const parsed = Number(raw);
294
+ if (Number.isInteger(parsed) && parsed >= 0)
295
+ return Math.min(parsed, 5);
296
+ }
297
+ return 2;
298
+ }
299
+ function rawRetryDelayMs(attempt) {
300
+ return attempt === 0 ? 200 : 800;
301
+ }
302
+ function shouldRetryRawStatus(status) {
303
+ return status === 502 || status === 503 || status === 504;
304
+ }
305
+ function shouldRetryJsonRawResponse(response) {
306
+ if (response.errorCode === AGENT_COMMAND_ERROR_CODES.NON_JSON_RESPONSE)
307
+ return false;
308
+ return shouldRetryRawStatus(response.status);
309
+ }
310
+ function isNetworkAgentCommandError(error) {
311
+ return error instanceof AgentCommandError && error.code === AGENT_COMMAND_ERROR_CODES.NETWORK_ERROR;
312
+ }
313
+ export function isHeldResponse(data) {
314
+ return Boolean(data && typeof data === 'object' && (data.held === true ||
315
+ data.state === 'held'));
316
+ }
317
+ export function isSuppressedResponse(data) {
318
+ return Boolean(data && typeof data === 'object' && data.suppressed === true);
319
+ }
320
+ export function isVersionConflict(response) {
321
+ return response.errorCode === 'version_conflict';
322
+ }
323
+ export function isMessageNotResolved(response) {
324
+ return response.status === 404 && typeof response.error === 'string' && response.error.includes('Cannot resolve message');
325
+ }
326
+ export function appendCommonBody(runtime, body) {
327
+ return {
328
+ ...body,
329
+ ...(runtime.context.conversationId ? { conversationId: runtime.context.conversationId } : {}),
330
+ ...(runtime.runId ? { runId: runtime.runId } : {}),
331
+ ...(runtime.turnId ? { turnId: runtime.turnId } : {}),
332
+ ...(runtime.traceId ? { traceId: runtime.traceId } : {}),
333
+ };
334
+ }
335
+ export function appendRunContextParams(runtime, params) {
336
+ if (runtime.runId)
337
+ params.set('run_id', runtime.runId);
338
+ if (runtime.turnId)
339
+ params.set('turn_id', runtime.turnId);
340
+ if (runtime.traceId)
341
+ params.set('trace_id', runtime.traceId);
342
+ if (runtime.context.conversationId)
343
+ params.set('conversation_id', runtime.context.conversationId);
344
+ return params;
345
+ }
346
+ export async function parseJsonStdinOrThrow(runtime, label) {
347
+ return readRequiredJsonStdin(runtime.io.stdin, label);
348
+ }
349
+ export function parseJsonStringOrThrow(content, label) {
350
+ try {
351
+ return JSON.parse(content);
352
+ }
353
+ catch (error) {
354
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_JSON_STDIN, `${label} did not contain valid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error, suggestedNextAction: 'Validate the payload with jq or node before retrying.' });
355
+ }
356
+ }
357
+ export function parseJsonObjectStringOrThrow(content, label) {
358
+ const parsed = parseJsonStringOrThrow(content, label);
359
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
360
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_JSON_STDIN, `${label} must be a JSON object.`, { suggestedNextAction: 'Pipe an object payload such as {"title":"..."} instead of an array or scalar.' });
361
+ }
362
+ return parsed;
363
+ }
364
+ export function normalizeExpectedVersion(payload) {
365
+ const normalized = { ...payload };
366
+ if (normalized.expectedVersion === undefined && Number.isInteger(normalized.expected_version)) {
367
+ normalized.expectedVersion = normalized.expected_version;
368
+ }
369
+ delete normalized.expected_version;
370
+ return normalized;
371
+ }
372
+ export function normalizePanelPayload(payload, commandName) {
373
+ if (payload.view !== undefined && payload.component === undefined) {
374
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_PANEL_PAYLOAD, `${commandName} expects curated panel JSON with component, props, dataset, and optional actions; view is a Workspace Tool manifest field.`, { suggestedNextAction: 'Use component:"RowTemplateGrid" with props/dataset/actions, or publish a Workspace Tool for tool manifest view JSON.' });
375
+ }
376
+ return normalizeExpectedVersion(payload);
377
+ }
378
+ export function summarizePanelState(data) {
379
+ const record = data && typeof data === 'object' ? data : {};
380
+ const state = ('perUser' in record || 'shared' in record)
381
+ ? record
382
+ : record.state && typeof record.state === 'object'
383
+ ? record.state
384
+ : {};
385
+ const perUser = state.perUser && typeof state.perUser === 'object'
386
+ ? state.perUser
387
+ : state;
388
+ const shared = state.shared && typeof state.shared === 'object'
389
+ ? state.shared
390
+ : state;
391
+ const filters = perUser.filters && typeof perUser.filters === 'object'
392
+ ? perUser.filters
393
+ : {};
394
+ const sort = perUser.sort && typeof perUser.sort === 'object'
395
+ ? perUser.sort
396
+ : {};
397
+ const selection = Array.isArray(shared.selection)
398
+ ? shared.selection
399
+ : Array.isArray(perUser.selection)
400
+ ? perUser.selection
401
+ : [];
402
+ const version = typeof state.version === 'number' ? ` version=${state.version}` : '';
403
+ const filterText = Object.keys(filters).length > 0 ? JSON.stringify(filters) : '(none)';
404
+ const sortText = Object.keys(sort).length > 0 ? JSON.stringify(sort) : '(none)';
405
+ const selectionText = selection.length > 0 ? selection.map(String).join(',') : '(none)';
406
+ return [
407
+ `Panel state.${version}`,
408
+ `filters=${filterText}`,
409
+ `sort=${sortText}`,
410
+ `selection=${selectionText}`,
411
+ ].join('\n');
412
+ }
413
+ export function summarizePanelCollaborators(data) {
414
+ const record = data && typeof data === 'object' ? data : {};
415
+ const panelId = String(record.panelId ?? record.panel_id ?? '');
416
+ const collaborators = Array.isArray(record.collaborators) ? record.collaborators : [];
417
+ const lines = [`Panel collaborators. panel_id=${panelId || '(unknown)'} count=${collaborators.length}`];
418
+ for (const raw of collaborators) {
419
+ const item = raw && typeof raw === 'object' ? raw : {};
420
+ const agentId = String(item.agentId ?? item.agent_id ?? '(unknown)');
421
+ const name = typeof item.name === 'string' && item.name.trim() ? item.name.trim() : agentId;
422
+ const role = String(item.role ?? 'collaborator');
423
+ lines.push(`- ${name} (${agentId}) role=${role}`);
424
+ }
425
+ return lines.join('\n');
426
+ }
427
+ export function summarizeSkillList(data) {
428
+ const record = data && typeof data === 'object' ? data : {};
429
+ const path = typeof record.path === 'string' ? record.path : '';
430
+ const roots = Array.isArray(record.roots) ? record.roots.map(String) : [];
431
+ const skills = Array.isArray(record.skills) ? record.skills : [];
432
+ const entries = Array.isArray(record.entries) ? record.entries : [];
433
+ const lines = [
434
+ path
435
+ ? `Skill directory. path=${path} entries=${entries.length}`
436
+ : `Skills. roots=${roots.length} skills=${skills.length}`,
437
+ ];
438
+ for (const raw of skills.slice(0, 20)) {
439
+ const item = raw && typeof raw === 'object' ? raw : {};
440
+ const name = String(item.name ?? '(unnamed)');
441
+ const skillPath = String(item.path ?? '');
442
+ const description = typeof item.description === 'string' && item.description.trim()
443
+ ? ` — ${item.description.trim()}`
444
+ : '';
445
+ lines.push(`- ${name}: ${skillPath}${description}`);
446
+ }
447
+ for (const raw of entries.slice(0, 40)) {
448
+ const item = raw && typeof raw === 'object' ? raw : {};
449
+ lines.push(`- ${String(item.kind ?? 'entry')} ${String(item.name ?? '(unnamed)')}: ${String(item.path ?? '')}`);
450
+ }
451
+ if (skills.length > 20)
452
+ lines.push(`... ${skills.length - 20} more skill(s)`);
453
+ if (entries.length > 40)
454
+ lines.push(`... ${entries.length - 40} more entries`);
455
+ return lines.join('\n');
456
+ }
457
+ export function summarizeSkillFile(data) {
458
+ const record = data && typeof data === 'object' ? data : {};
459
+ const skillPath = String(record.path ?? '');
460
+ const mimeType = String(record.mimeType ?? 'text/plain');
461
+ const size = typeof record.size === 'number' ? formatSize(record.size) : 'unknown';
462
+ const content = typeof record.content === 'string' ? record.content : '';
463
+ return [
464
+ `Skill file. path=${skillPath} mime=${mimeType} size=${size}`,
465
+ '',
466
+ '```markdown',
467
+ content,
468
+ '```',
469
+ ].join('\n');
470
+ }
471
+ export function normalizeAgentTaskStatus(raw) {
472
+ const status = requireString(raw, '--status');
473
+ if (status !== 'todo' && status !== 'in_progress' && status !== 'in_review') {
474
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_TASK_STATUS, '--status must be one of todo, in_progress, or in_review. Agents cannot mark tasks done.', { suggestedNextAction: 'Move completed work to in_review and let a human decide done or send it back.' });
475
+ }
476
+ return status;
477
+ }
478
+ export function normalizeTaskListStatus(raw) {
479
+ if (raw === undefined || raw === null || raw === '')
480
+ return 'all';
481
+ const status = String(raw).trim();
482
+ if (status === 'all' || status === 'todo' || status === 'in_progress' || status === 'in_review' || status === 'done') {
483
+ return status;
484
+ }
485
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_TASK_STATUS, '--status must be one of: all, todo, in_progress, in_review, done.');
486
+ }
487
+ export function normalizeTaskScope(raw) {
488
+ if (raw === undefined || raw === null || raw === '')
489
+ return 'all';
490
+ const scope = String(raw).trim();
491
+ if (scope === 'all' || scope === 'dm' || scope === 'channel')
492
+ return scope;
493
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, '--scope must be one of: all, dm, channel.');
494
+ }
495
+ export function summarizeTaskIdentity(task) {
496
+ if (!task)
497
+ return 'task';
498
+ const ref = typeof task.agentTaskRef === 'string' && task.agentTaskRef.trim() ? task.agentTaskRef.trim() : '';
499
+ const number = typeof task.taskNumber === 'number' ? `#t${task.taskNumber}` : '';
500
+ if (ref && number)
501
+ return `${ref} · ${number}`;
502
+ return ref || number || 'task';
503
+ }
504
+ export function summarizeTaskSource(task) {
505
+ const source = typeof task.sourceLabel === 'string' && task.sourceLabel.trim()
506
+ ? task.sourceLabel.trim()
507
+ : typeof task.sourceTarget === 'string' && task.sourceTarget.trim()
508
+ ? task.sourceTarget.trim()
509
+ : typeof task.channelId === 'string' && task.channelId.trim()
510
+ ? task.channelId.trim()
511
+ : '';
512
+ return source ? ` source=${source}` : '';
513
+ }
514
+ export function summarizeTaskList(data, emptyText) {
515
+ const tasks = data && typeof data === 'object' && Array.isArray(data.tasks)
516
+ ? data.tasks
517
+ : [];
518
+ if (tasks.length === 0)
519
+ return emptyText;
520
+ const lines = tasks.slice(0, 12).map((task) => {
521
+ const title = typeof task.title === 'string' ? task.title : 'Untitled task';
522
+ const status = typeof task.status === 'string' ? task.status : 'unknown';
523
+ const assignee = typeof task.claimedByName === 'string' && task.claimedByName.trim()
524
+ ? ` assigned=${task.claimedByName.trim()}`
525
+ : '';
526
+ const message = typeof task.messageId === 'string' && task.messageId.trim()
527
+ ? ` msg=${task.messageId.trim().slice(0, 8)}`
528
+ : '';
529
+ const thread = typeof task.threadTarget === 'string' && task.threadTarget.trim()
530
+ ? ` thread=${task.threadTarget.trim()}`
531
+ : '';
532
+ return `${summarizeTaskIdentity(task)} ${status}${assignee}${summarizeTaskSource(task)}${message}${thread}: ${title}`;
533
+ });
534
+ const suffix = tasks.length > lines.length ? `\n... ${tasks.length - lines.length} more task(s)` : '';
535
+ return `Found ${tasks.length} task(s):\n${lines.join('\n')}${suffix}`;
536
+ }
537
+ export function summarizeTaskLookup(data) {
538
+ if (!data || typeof data !== 'object')
539
+ return 'Task lookup returned no data.';
540
+ const record = data;
541
+ if (record.deletedTask)
542
+ return `Deleted task ${summarizeTaskIdentity(record.deletedTask)}.`;
543
+ if (!record.task)
544
+ return 'Task not found.';
545
+ const title = typeof record.task.title === 'string' ? record.task.title : 'Untitled task';
546
+ const status = typeof record.task.status === 'string' ? record.task.status : 'unknown';
547
+ const assignee = typeof record.task.claimedByName === 'string' && record.task.claimedByName.trim()
548
+ ? ` assigned=${record.task.claimedByName.trim()}`
549
+ : ' assigned=unclaimed';
550
+ const thread = typeof record.task.threadTarget === 'string' && record.task.threadTarget.trim()
551
+ ? `\nThread: ${record.task.threadTarget.trim()}`
552
+ : '';
553
+ return `${summarizeTaskIdentity(record.task)} ${status}${assignee}${summarizeTaskSource(record.task)}: ${title}${thread}`;
554
+ }
555
+ export function formatTaskHistoryEvent(event) {
556
+ const parts = [
557
+ typeof event.createdAt === 'string' && event.createdAt.trim() ? event.createdAt.trim() : 'unknown-time',
558
+ typeof event.type === 'string' && event.type.trim() ? event.type.trim() : 'event',
559
+ ];
560
+ if (typeof event.actorName === 'string' && event.actorName.trim()) {
561
+ const actor = event.actorType === 'system' ? event.actorName.trim() : `@${event.actorName.trim()}`;
562
+ parts.push(actor);
563
+ }
564
+ if (typeof event.fromStatus === 'string' || typeof event.toStatus === 'string') {
565
+ parts.push(`status ${String(event.fromStatus ?? 'unknown')} -> ${String(event.toStatus ?? 'unknown')}`);
566
+ }
567
+ if (typeof event.claimedByNameAfter === 'string' && event.claimedByNameAfter.trim()) {
568
+ parts.push(`assignee @${event.claimedByNameAfter.trim()}`);
569
+ }
570
+ if (typeof event.threadTarget === 'string' && event.threadTarget.trim()) {
571
+ parts.push(`thread ${event.threadTarget.trim()}`);
572
+ }
573
+ return `- ${parts.join(' · ')}`;
574
+ }
575
+ export function summarizeTaskHistory(data) {
576
+ if (!data || typeof data !== 'object')
577
+ return 'Task history returned no data.';
578
+ const record = data;
579
+ const identity = summarizeTaskIdentity(record.task ?? record.deletedTask);
580
+ const task = record.task ?? record.deletedTask;
581
+ const title = task && typeof task.title === 'string' ? task.title : 'Untitled task';
582
+ const status = task && typeof task.status === 'string' ? task.status : record.deletedTask ? 'deleted' : 'unknown';
583
+ const events = Array.isArray(record.events) ? record.events : [];
584
+ const lines = events.slice(0, 12).map(formatTaskHistoryEvent);
585
+ const suffix = events.length > lines.length ? `\n... ${events.length - lines.length} more event(s)` : '';
586
+ const timeline = lines.length ? lines.join('\n') : '- No structured task events recorded yet.';
587
+ return `${record.deletedTask ? 'Deleted task' : 'Task'} ${identity} [${status}] ${title}${task ? summarizeTaskSource(task) : ''}\nOrder: newest first\n\n${timeline}${suffix}`;
588
+ }
589
+ export function summarizeClaimResults(data) {
590
+ const results = data && typeof data === 'object' && Array.isArray(data.results)
591
+ ? data.results
592
+ : [];
593
+ const succeeded = results.filter((result) => result.success === true).length;
594
+ const failed = results.length - succeeded;
595
+ const lines = results.slice(0, 12).map((result) => {
596
+ const identity = typeof result.taskNumber === 'number' ? `#${result.taskNumber}` : String(result.messageId ?? 'task');
597
+ if (result.success === true) {
598
+ const handoff = result.handoffStarted && typeof result.threadTarget === 'string'
599
+ ? ` handoff=${result.threadTarget}`
600
+ : '';
601
+ return `${identity}: claimed${handoff}`;
602
+ }
603
+ return `${identity}: failed${result.reason ? ` - ${String(result.reason)}` : ''}`;
604
+ });
605
+ const suffix = results.length > lines.length ? `\n... ${results.length - lines.length} more result(s)` : '';
606
+ return `Claim results: ${succeeded} claimed, ${failed} failed.${lines.length ? `\n${lines.join('\n')}${suffix}` : ''}`;
607
+ }
608
+ export function normalizeReminderScheduleKind(raw) {
609
+ const value = requireString(raw, '--schedule-kind');
610
+ if (value === 'one_time' || value === 'recurring')
611
+ return value;
612
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, '--schedule-kind must be one_time or recurring.');
613
+ }
614
+ export function normalizeOptionalReminderScheduleKind(raw) {
615
+ if (raw === undefined || raw === null || raw === '')
616
+ return undefined;
617
+ return normalizeReminderScheduleKind(raw);
618
+ }
619
+ export function normalizeReminderIntervalUnit(raw) {
620
+ if (raw === undefined || raw === null || raw === '')
621
+ return undefined;
622
+ const value = String(raw).trim();
623
+ if (value === 'hour' || value === 'day' || value === 'week')
624
+ return value;
625
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_ARG, '--interval-unit must be hour, day, or week.');
626
+ }
627
+ export function parseSnoozeDuration(raw) {
628
+ const value = requireString(raw, '--by');
629
+ const match = value.match(/^([1-9]\d*)([mhd])$/);
630
+ if (!match) {
631
+ throw new AgentCommandError(AGENT_COMMAND_ERROR_CODES.INVALID_INPUT, `Unsupported duration format: "${value}". Use a positive integer followed by m, h, or d (e.g. 30m, 2h, 1d).`, { suggestedNextAction: 'Pass --by with a positive duration such as 30m, 2h, or 1d.' });
632
+ }
633
+ const amount = Number.parseInt(match[1], 10);
634
+ const unit = match[2];
635
+ const multiplier = unit === 'm' ? 60_000 : unit === 'h' ? 3_600_000 : 86_400_000;
636
+ const durationMs = amount * multiplier;
637
+ return { until: Date.now() + durationMs, durationMs };
638
+ }
639
+ export function summarizeTimestamp(raw) {
640
+ return typeof raw === 'number' && Number.isFinite(raw) ? new Date(raw).toISOString() : 'none';
641
+ }
642
+ export function summarizeReminder(reminder) {
643
+ const reminderId = String(reminder.reminderId ?? '');
644
+ const title = String(reminder.title ?? 'untitled');
645
+ const status = String(reminder.status ?? 'unknown');
646
+ const scheduleKind = String(reminder.scheduleKind ?? 'one_time');
647
+ const nextRun = summarizeTimestamp(reminder.nextRunAt);
648
+ const interval = reminder.intervalUnit && reminder.intervalValue
649
+ ? ` every=${String(reminder.intervalValue)} ${String(reminder.intervalUnit)}`
650
+ : '';
651
+ return `reminder_id=${reminderId} [${status}] ${scheduleKind}${interval} next=${nextRun} title="${title}"`;
652
+ }
653
+ export function summarizeReminderList(data) {
654
+ const reminders = data && typeof data === 'object' && Array.isArray(data.reminders)
655
+ ? data.reminders
656
+ : [];
657
+ if (reminders.length === 0)
658
+ return 'No reminders in the current DM scope.';
659
+ const lines = reminders.slice(0, 20).map((reminder) => {
660
+ const recentOccurrences = Array.isArray(reminder.recentOccurrences)
661
+ ? reminder.recentOccurrences
662
+ : [];
663
+ const openOccurrence = recentOccurrences.find((occurrence) => {
664
+ const status = String(occurrence.status ?? '');
665
+ return status === 'pending' || status === 'dispatched' || status === 'failed' || status === 'snoozed' || status === 'awaiting_review';
666
+ });
667
+ const open = openOccurrence
668
+ ? ` open_occurrence_id=${String(openOccurrence.occurrenceId ?? '')} open_status=${String(openOccurrence.status ?? 'unknown')}`
669
+ : '';
670
+ return `- ${summarizeReminder(reminder)}${open}`;
671
+ });
672
+ const suffix = reminders.length > lines.length ? `\n... ${reminders.length - lines.length} more reminder(s)` : '';
673
+ return `Current reminders:\n${lines.join('\n')}${suffix}`;
674
+ }
675
+ export function summarizeOccurrence(label, occurrence) {
676
+ if (!occurrence)
677
+ return `${label}: none`;
678
+ const task = typeof occurrence.taskNumber === 'number' ? ` task=#t${occurrence.taskNumber}` : '';
679
+ return `${label}: occurrence_id=${String(occurrence.occurrenceId ?? '')} ${String(occurrence.status ?? 'unknown')} scheduled=${summarizeTimestamp(occurrence.scheduledFor)}${task}`;
680
+ }
681
+ export function summarizeReminderOccurrences(data) {
682
+ if (!data || typeof data !== 'object')
683
+ return 'Reminder occurrence lookup returned no data.';
684
+ const payload = data;
685
+ const reminderId = String(payload.reminder?.reminderId ?? '');
686
+ const title = String(payload.reminder?.title ?? (reminderId || 'reminder'));
687
+ const occurrences = payload.occurrences ?? [];
688
+ if (occurrences.length === 0)
689
+ return `Reminder "${title}" has no occurrences yet.`;
690
+ const lines = occurrences.slice(0, 20).map((occurrence) => `- ${summarizeOccurrence('occurrence', occurrence).replace(/^occurrence: /, '')}`);
691
+ const suffix = occurrences.length > lines.length ? `\n... ${occurrences.length - lines.length} more occurrence(s)` : '';
692
+ return `Occurrences for "${title}" reminder_id=${reminderId}:\n${lines.join('\n')}${suffix}`;
693
+ }
694
+ export function summarizeReminderCurrent(data) {
695
+ if (!data || typeof data !== 'object')
696
+ return 'Reminder current occurrence lookup returned no data.';
697
+ const payload = data;
698
+ const reminderId = String(payload.reminder?.reminderId ?? '');
699
+ const title = String(payload.reminder?.title ?? (reminderId || 'reminder'));
700
+ return [
701
+ `Reminder "${title}" reminder_id=${reminderId}`,
702
+ summarizeOccurrence('Current', payload.currentOccurrence),
703
+ summarizeOccurrence('Latest', payload.latestOccurrence),
704
+ ].join('\n');
705
+ }
706
+ export function summarizeReminderAction(action, data, fallbackId) {
707
+ if (!data || typeof data !== 'object')
708
+ return `${action} reminder ${fallbackId ?? ''}`.trim();
709
+ const payload = data;
710
+ const reminder = payload.reminder ?? (payload.reminderId || payload.title ? payload : undefined);
711
+ const reminderId = String(reminder?.reminderId ?? fallbackId ?? '');
712
+ const title = reminder?.title ? ` "${String(reminder.title)}"` : '';
713
+ const occurrence = payload.occurrence
714
+ ? ` occurrence_id=${String(payload.occurrence.occurrenceId ?? '')} occurrence_status=${String(payload.occurrence.status ?? 'unknown')}`
715
+ : '';
716
+ const task = payload.task && typeof payload.task.taskNumber === 'number'
717
+ ? ` task=#t${payload.task.taskNumber} task_status=${String(payload.task.status ?? 'unknown')}`
718
+ : '';
719
+ return `${action} reminder${title} reminder_id=${reminderId}${occurrence}${task}`.trim();
720
+ }
721
+ export function summarizeActionPrepare(data) {
722
+ if (!data || typeof data !== 'object')
723
+ return 'Action card prepared.';
724
+ const payload = data;
725
+ const cardId = String(payload.actionCardId ?? payload.action_card_id ?? payload.id ?? '');
726
+ const actionType = String(payload.actionType ?? payload.action_type ?? 'unknown');
727
+ const status = String(payload.status ?? 'pending');
728
+ const panelId = typeof payload.panelId === 'string'
729
+ ? payload.panelId
730
+ : typeof payload.panel_id === 'string'
731
+ ? payload.panel_id
732
+ : '';
733
+ const summary = typeof payload.summary === 'string' && payload.summary.trim()
734
+ ? ` ${payload.summary.trim()}`
735
+ : '';
736
+ const panel = panelId ? ` panel_id=${panelId}` : '';
737
+ return `Action card prepared.${summary} action_card_id=${cardId} action_type=${actionType} status=${status}${panel}`;
738
+ }
739
+ export function summarizeReminderLog(reminderId, data) {
740
+ const events = Array.isArray(data) ? data : [];
741
+ if (events.length === 0)
742
+ return `Reminder ${reminderId} has no events.`;
743
+ const lines = [`Reminder log for ${reminderId}: ${events.length} event(s)`];
744
+ for (const raw of events) {
745
+ const event = raw && typeof raw === 'object' ? raw : {};
746
+ const createdAt = typeof event.createdAt === 'number' && Number.isFinite(event.createdAt)
747
+ ? new Date(event.createdAt).toISOString()
748
+ : 'unknown';
749
+ const eventType = String(event.eventType ?? 'event');
750
+ const actorType = String(event.actorType ?? 'unknown');
751
+ const actorName = typeof event.actorName === 'string' && event.actorName.trim()
752
+ ? event.actorName.trim()
753
+ : String(event.actorId ?? 'unknown');
754
+ const actor = actorType === 'system' ? actorName : `@${actorName}`;
755
+ const occurrence = typeof event.occurrenceId === 'string' && event.occurrenceId.trim()
756
+ ? ` occurrence_id=${event.occurrenceId.trim()}`
757
+ : '';
758
+ const payload = event.payload && typeof event.payload === 'object' ? JSON.stringify(event.payload) : '';
759
+ const payloadText = payload ? ` payload=${payload}` : '';
760
+ lines.push(`- ${createdAt} ${eventType} ${actor}${occurrence}${payloadText}`);
761
+ }
762
+ return lines.join('\n');
763
+ }
764
+ export function summarizeReminderSnooze(data) {
765
+ if (!data || typeof data !== 'object')
766
+ return 'Reminder occurrence snoozed.';
767
+ const record = data;
768
+ const occurrence = record.occurrence && typeof record.occurrence === 'object'
769
+ ? record.occurrence
770
+ : {};
771
+ const occurrenceId = String(occurrence.occurrenceId ?? '');
772
+ const scheduledFor = typeof occurrence.scheduledFor === 'number' && Number.isFinite(occurrence.scheduledFor)
773
+ ? new Date(occurrence.scheduledFor).toISOString()
774
+ : '';
775
+ const parts = [`Snoozed reminder occurrence${occurrenceId ? ` ${occurrenceId}` : ''}.`];
776
+ if (scheduledFor)
777
+ parts.push(`Next run at ${scheduledFor}.`);
778
+ return parts.join('\n');
779
+ }
780
+ export function summarizeWorkspaceInspect(data) {
781
+ if (!data || typeof data !== 'object')
782
+ return 'Workspace inspect returned no data.';
783
+ const result = data;
784
+ if (typeof result.reportText === 'string' && result.reportText.trim()) {
785
+ return result.reportText.trim();
786
+ }
787
+ const inspect = result.inspect ?? {};
788
+ return [
789
+ '[Workspace inspect]',
790
+ `node: ${String(result.nodeId ?? 'none')}`,
791
+ `workspace_root: ${String(result.workspaceRoot ?? 'none')}`,
792
+ `git: ${inspect.isGit ? 'yes' : 'no'} | kind=${String(inspect.workspaceKind ?? 'unknown')} | branch=${String(inspect.branchName ?? 'none')}`,
793
+ inspect.repoRoot ? `repo_root: ${String(inspect.repoRoot)}` : null,
794
+ inspect.remoteUrl ? `remote: ${String(inspect.remoteUrl)}` : null,
795
+ ].filter(Boolean).join('\n');
796
+ }
797
+ export function summarizeWorkspaceTree(data) {
798
+ if (!data || typeof data !== 'object')
799
+ return 'Workspace tree returned no data.';
800
+ const result = data;
801
+ const prefix = typeof result.path === 'string' && result.path.trim() ? result.path.trim() : '.';
802
+ const entries = Array.isArray(result.entries) ? result.entries : [];
803
+ if (entries.length === 0)
804
+ return `[Workspace tree]\npath: ${prefix}\n(entries: empty)`;
805
+ const lines = entries.slice(0, 100).map((entry) => {
806
+ const size = typeof entry.size === 'number' && Number.isFinite(entry.size) ? ` size=${entry.size}` : '';
807
+ return `- ${String(entry.path ?? '')} [${String(entry.kind ?? 'unknown')}]${size}`;
808
+ });
809
+ const suffix = entries.length > lines.length ? `\n... ${entries.length - lines.length} more entry(s)` : '';
810
+ return `[Workspace tree]\npath: ${prefix}\n${lines.join('\n')}${suffix}`;
811
+ }
812
+ export function summarizeWorkspaceFile(data) {
813
+ if (!data || typeof data !== 'object')
814
+ return 'Workspace file returned no data.';
815
+ const result = data;
816
+ const mimeType = String(result.mimeType ?? 'text/plain');
817
+ const fence = mimeType === 'text/markdown' ? 'markdown' : 'text';
818
+ return [
819
+ '[Workspace file]',
820
+ `path: ${String(result.path ?? '')}`,
821
+ `mime: ${mimeType}`,
822
+ `size: ${String(result.size ?? 'unknown')}`,
823
+ typeof result.modifiedAt === 'number' && Number.isFinite(result.modifiedAt)
824
+ ? `modified_at: ${new Date(result.modifiedAt).toISOString()}`
825
+ : null,
826
+ '',
827
+ `\`\`\`${fence}`,
828
+ String(result.content ?? ''),
829
+ '```',
830
+ ].filter((line) => line !== null).join('\n');
831
+ }
832
+ export function summarizeToolState(state) {
833
+ if (!state || typeof state !== 'object')
834
+ return 'none';
835
+ const entries = Object.entries(state)
836
+ .filter(([, value]) => value !== undefined && value !== null && value !== '')
837
+ .slice(0, 6)
838
+ .map(([key, value]) => `${key}=${String(value)}`);
839
+ return entries.length > 0 ? entries.join(' · ') : 'empty';
840
+ }
841
+ export function summarizeToolRun(run) {
842
+ if (!run)
843
+ return 'last_run=none';
844
+ const node = run.executionHostname || run.executionNodeId
845
+ ? ` node=${String(run.executionHostname ?? run.executionNodeId)}`
846
+ : '';
847
+ return `last_run=${String(run.runId ?? '')} action=${String(run.actionId ?? '')} status=${String(run.status ?? 'unknown')}${node}`;
848
+ }
849
+ export function summarizeToolStatus(data) {
850
+ if (!data || typeof data !== 'object')
851
+ return 'Workspace tool status returned no data.';
852
+ const payload = data;
853
+ const tool = payload.tool ?? {};
854
+ const manifest = tool.manifest && typeof tool.manifest === 'object'
855
+ ? tool.manifest
856
+ : {};
857
+ const cli = manifest.cli && typeof manifest.cli === 'object'
858
+ ? manifest.cli
859
+ : {};
860
+ const runtime = manifest.runtime && typeof manifest.runtime === 'object'
861
+ ? manifest.runtime
862
+ : {};
863
+ const backend = cli.entry
864
+ ? `cli.entry=${String(cli.entry)}`
865
+ : 'legacy-command';
866
+ const runtimeMode = runtime.mode ? ` runtime=${String(runtime.mode)}` : '';
867
+ const lines = [
868
+ '[Workspace tool]',
869
+ `tool_id: ${String(tool.toolId ?? '')}`,
870
+ `name: ${String(tool.name ?? '')}`,
871
+ `slug: ${String(tool.slug ?? '')}`,
872
+ `revision: ${String(tool.revision ?? '')}`,
873
+ `agent: ${String(tool.agentName ?? tool.agentId ?? '')}`,
874
+ `node: ${String(tool.agentNodeId ?? 'unknown')}`,
875
+ `runtime_state: ${String(tool.runtimeState ?? 'unknown')} | running=${tool.isRunning === true ? 'yes' : 'no'} | active_terminal=${String(tool.activeTerminalId ?? 'none')}`,
876
+ `backend: ${backend}${runtimeMode}`,
877
+ `latest_state: ${summarizeToolState(tool.latestState)}`,
878
+ summarizeToolRun(tool.lastRun),
879
+ ];
880
+ if (Array.isArray(payload.runs)) {
881
+ const runLines = payload.runs.map((run) => `- ${summarizeToolRun(run)}`);
882
+ lines.push(runLines.length > 0 ? `recent_runs:\n${runLines.join('\n')}` : 'recent_runs: none');
883
+ }
884
+ return lines.join('\n');
885
+ }
886
+ export function summarizeToolPublish(data) {
887
+ if (!data || typeof data !== 'object')
888
+ return 'Tool publish completed.';
889
+ const payload = data;
890
+ return `Tool published. tool_id=${String(payload.toolId ?? '')} revision=${String(payload.revision ?? '')} url=${String(payload.toolUrl ?? '')}`;
891
+ }
892
+ export function summarizeToolVerification(data) {
893
+ if (!data || typeof data !== 'object')
894
+ return 'Workspace tool verification updated.';
895
+ const payload = data;
896
+ const verification = payload.verification && typeof payload.verification === 'object'
897
+ ? payload.verification
898
+ : payload;
899
+ return [
900
+ `Tool verification updated. tool_id=${String(verification.toolId ?? '')} status=${String(verification.status ?? '')}`,
901
+ `summary: ${String(verification.summary ?? '')}`,
902
+ verification.smokeRunId ? `smoke_run_id: ${String(verification.smokeRunId)}` : null,
903
+ ].filter(Boolean).join('\n');
904
+ }
905
+ export function summarizeToolValidate(data) {
906
+ if (!data || typeof data !== 'object')
907
+ return 'Workspace tool manifest validation returned no data.';
908
+ const payload = data;
909
+ const diagnostics = Array.isArray(payload.diagnostics)
910
+ ? payload.diagnostics
911
+ : [];
912
+ const errors = diagnostics.filter((diagnostic) => String(diagnostic.severity).toLowerCase() === 'error');
913
+ const warnings = diagnostics.filter((diagnostic) => String(diagnostic.severity).toLowerCase() === 'warning');
914
+ const others = diagnostics.filter((diagnostic) => {
915
+ const severity = String(diagnostic.severity).toLowerCase();
916
+ return severity !== 'error' && severity !== 'warning';
917
+ });
918
+ const summaryParts = [];
919
+ if (errors.length > 0)
920
+ summaryParts.push(`${errors.length} error(s)`);
921
+ if (warnings.length > 0)
922
+ summaryParts.push(`${warnings.length} warning(s)`);
923
+ if (others.length > 0)
924
+ summaryParts.push(`${others.length} notice(s)`);
925
+ const summary = summaryParts.length > 0 ? ` ${summaryParts.join(', ')}` : '';
926
+ const lines = payload.valid === true
927
+ ? [diagnostics.length === 0 ? 'Workspace tool manifest is valid.' : `Workspace tool manifest is valid with${summary}.`]
928
+ : [`Workspace tool manifest validation failed.${summary}`];
929
+ for (const diagnostic of diagnostics) {
930
+ const severity = String(diagnostic.severity ?? 'info').toUpperCase();
931
+ const code = String(diagnostic.code ?? '');
932
+ const message = String(diagnostic.message ?? '');
933
+ const codePrefix = code ? `${code}: ` : '';
934
+ lines.push(`[${severity}] ${codePrefix}${message}`);
935
+ }
936
+ return lines.join('\n');
937
+ }
938
+ export function summarizeToolAction(data, fallbackActionId) {
939
+ if (!data || typeof data !== 'object')
940
+ return `Tool action ${fallbackActionId} completed.`;
941
+ const payload = data;
942
+ if (payload.mode === 'notify_agent') {
943
+ return `Tool action notified agent. action=${fallbackActionId} conversation_id=${String(payload.conversationId ?? '')} queued=${payload.queued === true ? 'yes' : 'no'}`;
944
+ }
945
+ const run = payload.run ?? {};
946
+ const tool = payload.tool ?? {};
947
+ const node = run.executionHostname || run.executionNodeId
948
+ ? ` node=${String(run.executionHostname ?? run.executionNodeId)}`
949
+ : '';
950
+ return [
951
+ `Tool action completed. tool_id=${String(tool.toolId ?? '')} action=${String(run.actionId ?? fallbackActionId)} run_id=${String(run.runId ?? '')} status=${String(run.status ?? 'unknown')}${node}`,
952
+ `latest_state: ${summarizeToolState(payload.latestState ?? tool.latestState)}`,
953
+ ].join('\n');
954
+ }
955
+ export function inferMimeType(filePath) {
956
+ return MIME_BY_EXTENSION[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
957
+ }
958
+ export function formatSize(bytes) {
959
+ if (!Number.isFinite(bytes) || bytes < 0)
960
+ return 'unknown';
961
+ if (bytes < 1024)
962
+ return `${bytes} B`;
963
+ if (bytes < 1024 * 1024)
964
+ return `${(bytes / 1024).toFixed(1)} KB`;
965
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
966
+ }
967
+ export function isTextLikeMimeType(mimeType) {
968
+ const normalized = mimeType.split(';', 1)[0]?.trim().toLowerCase() ?? '';
969
+ return normalized.startsWith('text/')
970
+ || normalized === 'application/json'
971
+ || normalized === 'application/ld+json'
972
+ || normalized === 'application/xml'
973
+ || normalized === 'application/x-ndjson'
974
+ || normalized === 'application/yaml'
975
+ || normalized === 'application/x-yaml'
976
+ || normalized === 'application/toml'
977
+ || normalized === 'application/javascript'
978
+ || normalized === 'application/typescript'
979
+ || normalized === 'application/x-tex'
980
+ || normalized === 'application/x-latex'
981
+ || normalized === 'application/latex'
982
+ || normalized.endsWith('+json')
983
+ || normalized.endsWith('+xml');
984
+ }
985
+ export function isTextLikeAttachment(mimeType, metadata, filename) {
986
+ if (isTextLikeMimeType(mimeType))
987
+ return true;
988
+ const kind = typeof metadata.kind === 'string' ? metadata.kind.trim().toLowerCase() : '';
989
+ if (['text', 'json', 'markdown', 'csv', 'code', 'yaml', 'xml'].includes(kind))
990
+ return true;
991
+ const inferred = inferMimeType(filename);
992
+ return inferred !== 'application/octet-stream' && isTextLikeMimeType(inferred);
993
+ }
994
+ export function summarizeAttachmentUpload(data) {
995
+ if (!data || typeof data !== 'object')
996
+ return 'Attachment uploaded.';
997
+ const payload = data;
998
+ const id = String(payload.assetId ?? payload.id ?? '');
999
+ return [
1000
+ `Attachment uploaded. attachment_id=${id}`,
1001
+ `filename=${String(payload.filename ?? '')} size=${formatSize(Number(payload.sizeBytes ?? 0))} kind=${String(payload.kind ?? 'unknown')}`,
1002
+ `scope=${String(payload.scopeType ?? 'none')}:${String(payload.scopeId ?? 'none')}`,
1003
+ 'Pass this id to `bigbang message send --attachment-id`.',
1004
+ ].join('\n');
1005
+ }
1006
+ export function summarizeAttachmentView(params) {
1007
+ const filename = String(params.metadata.filename ?? params.attachmentId);
1008
+ const mimeType = String(params.metadata.mimeType ?? params.contentType ?? inferMimeType(filename));
1009
+ const sizeBytes = typeof params.metadata.sizeBytes === 'number' ? params.metadata.sizeBytes : params.buffer.length;
1010
+ const base = {
1011
+ attachmentId: params.attachmentId,
1012
+ filename,
1013
+ mimeType,
1014
+ sizeBytes,
1015
+ kind: params.metadata.kind ?? null,
1016
+ };
1017
+ if (isTextLikeAttachment(mimeType, params.metadata, filename)) {
1018
+ const content = params.buffer.toString('utf8');
1019
+ const preview = content.length > ATTACHMENT_TEXT_PREVIEW_MAX_CHARS
1020
+ ? `${content.slice(0, ATTACHMENT_TEXT_PREVIEW_MAX_CHARS)}\n... [truncated ${content.length - ATTACHMENT_TEXT_PREVIEW_MAX_CHARS} chars]`
1021
+ : content;
1022
+ return {
1023
+ text: [
1024
+ `Attachment ${params.attachmentId} (${mimeType}, ${formatSize(sizeBytes)})`,
1025
+ '',
1026
+ '```text',
1027
+ preview,
1028
+ '```',
1029
+ ].join('\n'),
1030
+ data: { ...base, contentPreview: preview, truncated: content.length > ATTACHMENT_TEXT_PREVIEW_MAX_CHARS },
1031
+ };
1032
+ }
1033
+ const note = mimeType.startsWith('image/')
1034
+ ? 'Image attachment downloaded; binary data is not printed by bigbang.'
1035
+ : 'Binary attachment downloaded; binary data is not printed by bigbang.';
1036
+ return {
1037
+ text: `Attachment ${params.attachmentId} (${mimeType}, ${formatSize(sizeBytes)}). ${note}`,
1038
+ data: base,
1039
+ };
1040
+ }
1041
+ export function summarizeAttachmentComments(attachmentId, data) {
1042
+ const comments = Array.isArray(data) ? data : [];
1043
+ if (comments.length === 0)
1044
+ return `No comments on ${attachmentId}.`;
1045
+ const lines = [`Comments on ${attachmentId}: ${comments.length}`];
1046
+ for (const raw of comments) {
1047
+ const item = raw && typeof raw === 'object' ? raw : {};
1048
+ const author = item.author && typeof item.author === 'object' ? item.author : {};
1049
+ const name = typeof author.name === 'string' && author.name.trim() ? author.name.trim() : String(author.userId ?? 'unknown');
1050
+ const createdAt = typeof item.createdAt === 'number' && Number.isFinite(item.createdAt)
1051
+ ? new Date(item.createdAt).toISOString()
1052
+ : 'unknown';
1053
+ lines.push(`- ${createdAt} @${name}: ${String(item.body ?? '')}`);
1054
+ }
1055
+ return lines.join('\n');
1056
+ }
1057
+ export function summarizeServerDirectory(data) {
1058
+ if (!data || typeof data !== 'object')
1059
+ return 'Server directory returned no data.';
1060
+ const payload = data;
1061
+ const lines = ['Server directory'];
1062
+ lines.push('channels:');
1063
+ for (const channel of payload.channels ?? []) {
1064
+ const name = String(channel.name ?? '');
1065
+ const joined = channel.joined === true ? 'joined' : 'not joined';
1066
+ const description = typeof channel.description === 'string' && channel.description.trim()
1067
+ ? ` - ${channel.description.trim()}`
1068
+ : '';
1069
+ lines.push(`- #${name} [${joined}]${description}`);
1070
+ }
1071
+ if (!payload.channels?.length)
1072
+ lines.push('- none');
1073
+ lines.push('agents:');
1074
+ for (const agent of payload.agents ?? []) {
1075
+ lines.push(`- @${String(agent.name ?? '')} (${String(agent.status ?? 'unknown')})`);
1076
+ }
1077
+ if (!payload.agents?.length)
1078
+ lines.push('- none');
1079
+ lines.push('humans:');
1080
+ for (const human of payload.humans ?? []) {
1081
+ lines.push(`- @${String(human.name ?? '')}`);
1082
+ }
1083
+ if (!payload.humans?.length)
1084
+ lines.push('- none');
1085
+ return lines.join('\n');
1086
+ }
1087
+ export function summarizeInboxCheck(data) {
1088
+ const record = data && typeof data === 'object' ? data : {};
1089
+ const totalUnread = Number(record.totalUnread ?? record.total_unread ?? 0);
1090
+ const surfaceCount = Number(record.surfaceCount ?? record.surface_count ?? 0);
1091
+ const items = Array.isArray(record.items) ? record.items : [];
1092
+ if (items.length === 0) {
1093
+ return 'Inbox empty.';
1094
+ }
1095
+ const lines = [
1096
+ `Inbox: ${Number.isFinite(totalUnread) ? totalUnread : items.length} unread across ${Number.isFinite(surfaceCount) ? surfaceCount : items.length} surface(s).`,
1097
+ ];
1098
+ for (const raw of items.slice(0, 20)) {
1099
+ const item = raw && typeof raw === 'object' ? raw : {};
1100
+ const target = String(item.target ?? '(unknown target)');
1101
+ const unreadCount = Number(item.unreadCount ?? item.unread_count ?? 0);
1102
+ const firstSeq = Number(item.firstSeq ?? item.first_seq ?? 0);
1103
+ const lastSeq = Number(item.lastSeq ?? item.last_seq ?? 0);
1104
+ const sender = String(item.lastSenderName ?? item.last_sender_name ?? item.lastSenderType ?? item.last_sender_type ?? 'unknown');
1105
+ const seqRange = Number.isFinite(firstSeq) && Number.isFinite(lastSeq)
1106
+ ? `seq ${firstSeq}-${lastSeq}`
1107
+ : 'seq unknown';
1108
+ lines.push(`- ${target}: ${Number.isFinite(unreadCount) ? unreadCount : 1} unread (${seqRange}, latest ${sender})`);
1109
+ }
1110
+ if (items.length > 20)
1111
+ lines.push(`... ${items.length - 20} more surface(s)`);
1112
+ lines.push('Next: run `bigbang message check` to drain unread content, or `bigbang message read --channel <target>` for targeted context.');
1113
+ return lines.join('\n');
1114
+ }
1115
+ export function summarizeInboxWait(data) {
1116
+ const record = data && typeof data === 'object' ? data : {};
1117
+ const items = Array.isArray(record.items) ? record.items : [];
1118
+ const timedOut = record.timedOut === true || record.timed_out === true;
1119
+ if (timedOut && items.length === 0) {
1120
+ return 'Inbox wait timed out with no pending surfaces.';
1121
+ }
1122
+ return summarizeInboxCheck(data);
1123
+ }
1124
+ export function summarizeSelfState(data) {
1125
+ if (!data || typeof data !== 'object')
1126
+ return 'Self state returned no data.';
1127
+ const reportText = data.reportText;
1128
+ if (typeof reportText === 'string' && reportText.trim())
1129
+ return reportText.trim();
1130
+ return 'Self state returned no report text.';
1131
+ }
1132
+ export function summarizeRuntimePresence(data) {
1133
+ if (!data || typeof data !== 'object')
1134
+ return 'Runtime presence returned no data.';
1135
+ const reportText = data.reportText;
1136
+ if (typeof reportText === 'string' && reportText.trim())
1137
+ return reportText.trim();
1138
+ return 'Runtime presence returned no report text.';
1139
+ }
1140
+ export function summarizeConversationList(data) {
1141
+ if (!data || typeof data !== 'object')
1142
+ return 'Conversation list returned no data.';
1143
+ const conversations = Array.isArray(data.conversations)
1144
+ ? data.conversations
1145
+ : [];
1146
+ if (conversations.length === 0)
1147
+ return 'No conversations found.';
1148
+ const lines = conversations.slice(0, 40).map((conversation) => {
1149
+ const target = String(conversation.replyTarget ?? conversation.conversationId ?? 'unknown');
1150
+ const status = String(conversation.status ?? 'unknown');
1151
+ const unread = Number(conversation.unreadCount ?? 0);
1152
+ const queued = Number(conversation.queuedPromptCount ?? 0);
1153
+ const active = conversation.hasActiveRun === true ? 'yes' : 'no';
1154
+ const kind = String(conversation.surfaceKind ?? '');
1155
+ return `- ${target}${kind ? ` [${kind}]` : ''} status=${status} unread=${unread} queued=${queued} active=${active}`;
1156
+ });
1157
+ const suffix = conversations.length > lines.length ? `\n... ${conversations.length - lines.length} more conversation(s)` : '';
1158
+ return `Conversations: ${conversations.length}\n${lines.join('\n')}${suffix}`;
1159
+ }
1160
+ export function summarizeConversationSummary(data) {
1161
+ if (!data || typeof data !== 'object')
1162
+ return 'Conversation summary returned no data.';
1163
+ const record = data.conversation;
1164
+ if (!record)
1165
+ return 'Conversation summary returned no data.';
1166
+ const reportText = record.reportText;
1167
+ if (typeof reportText === 'string' && reportText.trim())
1168
+ return reportText.trim();
1169
+ const target = String(record.replyTarget ?? record.conversationId ?? 'unknown');
1170
+ const status = String(record.status ?? 'unknown');
1171
+ const unread = Number(record.unreadCount ?? 0);
1172
+ const queued = Number(record.queuedPromptCount ?? 0);
1173
+ const summaryText = typeof record.summaryText === 'string' && record.summaryText.trim()
1174
+ ? record.summaryText.trim()
1175
+ : null;
1176
+ return [
1177
+ `Conversation: ${target}`,
1178
+ `status=${status} unread=${unread} queued=${queued}`,
1179
+ ...(summaryText ? [`summary: ${summaryText}`] : []),
1180
+ ].join('\n');
1181
+ }
1182
+ export function summarizeContextBundle(data) {
1183
+ if (!data || typeof data !== 'object')
1184
+ return 'Context bundle returned no data.';
1185
+ const bundle = data.bundle;
1186
+ if (!bundle)
1187
+ return 'Context bundle returned no data.';
1188
+ const current = bundle.currentConversation && typeof bundle.currentConversation === 'object'
1189
+ ? bundle.currentConversation
1190
+ : {};
1191
+ const target = String(current.replyTarget ?? current.conversationId ?? 'current');
1192
+ const surfaces = Array.isArray(bundle.surfaces) ? bundle.surfaces.length : 0;
1193
+ const tasks = Array.isArray(bundle.tasks) ? bundle.tasks.length : 0;
1194
+ const handoffs = Array.isArray(bundle.handoffs) ? bundle.handoffs.length : 0;
1195
+ const suggestions = Array.isArray(bundle.suggestedHistoryReads) ? bundle.suggestedHistoryReads.length : 0;
1196
+ return [
1197
+ `Context bundle for ${target}`,
1198
+ `surfaces=${surfaces} tasks=${tasks} handoffs=${handoffs} suggested_history_reads=${suggestions}`,
1199
+ ].join('\n');
1200
+ }
1201
+ export function summarizeHeldMessageDrafts(data) {
1202
+ if (!data || typeof data !== 'object')
1203
+ return 'Held message drafts returned no data.';
1204
+ const drafts = Array.isArray(data.drafts)
1205
+ ? data.drafts
1206
+ : [];
1207
+ if (drafts.length === 0)
1208
+ return 'No held message drafts found.';
1209
+ const lines = drafts.slice(0, 20).map((draft) => {
1210
+ const draftId = String(draft.draftId ?? 'unknown');
1211
+ const target = String(draft.resolvedTarget ?? draft.channelId ?? 'unknown');
1212
+ const status = String(draft.status ?? 'unknown');
1213
+ const preview = typeof draft.contentPreview === 'string' && draft.contentPreview.trim()
1214
+ ? draft.contentPreview.trim()
1215
+ : '';
1216
+ return `- draft_id=${draftId} target=${target} status=${status}${preview ? ` preview="${preview}"` : ''}`;
1217
+ });
1218
+ const suffix = drafts.length > lines.length ? `\n... ${drafts.length - lines.length} more draft(s)` : '';
1219
+ return `Held message drafts: ${drafts.length}\n${lines.join('\n')}${suffix}`;
1220
+ }
1221
+ export function summarizeHandoffCreate(data) {
1222
+ if (!data || typeof data !== 'object')
1223
+ return 'Handoff created.';
1224
+ const payload = data;
1225
+ const handoff = payload.handoff ?? {};
1226
+ const handoffId = String(handoff.handoffId ?? '');
1227
+ const target = String(handoff.targetReplyTarget ?? handoff.targetConversationId ?? 'unknown');
1228
+ const mode = String(handoff.mode ?? 'unknown');
1229
+ const status = String(handoff.status ?? 'unknown');
1230
+ return [
1231
+ `Handoff created. handoff_id=${handoffId}`,
1232
+ `target=${target} mode=${mode} status=${status}`,
1233
+ ].join('\n');
1234
+ }
1235
+ export function summarizeHandoffStatus(data) {
1236
+ if (!data || typeof data !== 'object')
1237
+ return 'Handoff status returned no data.';
1238
+ const payload = data;
1239
+ const handoff = payload.handoff ?? {};
1240
+ const handoffId = String(handoff.handoffId ?? '');
1241
+ const source = String(handoff.sourceReplyTarget ?? handoff.sourceConversationId ?? 'unknown');
1242
+ const target = String(handoff.targetReplyTarget ?? handoff.targetConversationId ?? 'unknown');
1243
+ const mode = String(handoff.mode ?? 'unknown');
1244
+ const status = String(handoff.status ?? 'unknown');
1245
+ return [
1246
+ `Handoff ${handoffId}`,
1247
+ `source=${source} target=${target} mode=${mode} status=${status}`,
1248
+ ].join('\n');
1249
+ }
1250
+ export function summarizePanelComponents(data) {
1251
+ if (!data || typeof data !== 'object')
1252
+ return 'Panel components returned no data.';
1253
+ const components = Array.isArray(data.components)
1254
+ ? data.components
1255
+ : [];
1256
+ if (components.length === 0)
1257
+ return 'No panel UI components available.';
1258
+ const lines = components.map((component) => {
1259
+ const name = String(component.name ?? 'unnamed');
1260
+ const description = typeof component.description === 'string' && component.description.trim()
1261
+ ? ` — ${component.description.trim()}`
1262
+ : '';
1263
+ return `- ${name}${description}`;
1264
+ });
1265
+ return `Available panel UI components: ${components.length}\n${lines.join('\n')}`;
1266
+ }
1267
+ export function summarizeChannelMembers(data) {
1268
+ if (!data || typeof data !== 'object')
1269
+ return 'Channel members returned no data.';
1270
+ const payload = data;
1271
+ const target = String(payload.channel?.target ?? (payload.channel?.name ? `#${payload.channel.name}` : 'channel'));
1272
+ const members = payload.members ?? [];
1273
+ const lines = [`${target} members (${members.length}):`];
1274
+ for (const member of members) {
1275
+ const self = member.isSelf === true ? ' self' : '';
1276
+ lines.push(`- @${String(member.name ?? '')} agent_id=${String(member.agentId ?? '')} type=${String(member.agentType ?? 'unknown')}${self}`);
1277
+ }
1278
+ if (members.length === 0)
1279
+ lines.push('- none');
1280
+ return lines.join('\n');
1281
+ }
1282
+ export function summarizeChannelMutation(data, verb) {
1283
+ if (!data || typeof data !== 'object')
1284
+ return `Channel ${verb}.`;
1285
+ const payload = data;
1286
+ const target = String(payload.channel?.target ?? (payload.channel?.name ? `#${payload.channel.name}` : payload.channel?.channelId ?? 'channel'));
1287
+ return `Channel ${verb}: ${target}`;
1288
+ }
1289
+ export function summarizeThreadUnfollow(data) {
1290
+ if (!data || typeof data !== 'object')
1291
+ return 'Thread unfollowed.';
1292
+ const payload = data;
1293
+ const target = String(payload.channel?.target ?? payload.threadRootId ?? 'thread');
1294
+ const removed = payload.removed ?? {};
1295
+ return [
1296
+ `Thread unfollowed: ${target}`,
1297
+ `removed: participants=${String(removed.participants ?? 0)} roster=${String(removed.roster ?? 0)} notification_state=${String(removed.notificationState ?? 0)} notification_rounds=${String(removed.notificationRounds ?? 0)}`,
1298
+ ].join('\n');
1299
+ }
1300
+ export function appendReminderConversation(runtime, commandName, body = {}) {
1301
+ return {
1302
+ ...body,
1303
+ conversationId: requireConversationId(runtime, commandName),
1304
+ };
1305
+ }
1306
+ export { AGENT_COMMAND_ERROR_CODES, AgentCommandError, formatMessageHeld, formatPanelVersionConflict, formatTaskHeld, readOptionalStdin, readRequiredJsonStdin, readRequiredStdin, toAgentCommandError, };