@guildai/cli 0.8.1 → 0.9.1
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/commands/agent/chat.d.ts +1 -0
- package/dist/commands/agent/chat.js +24 -1
- package/dist/commands/agent/init.js +1 -1
- package/dist/commands/agent/test.d.ts +1 -0
- package/dist/commands/agent/test.js +80 -28
- package/dist/commands/chat.d.ts +1 -0
- package/dist/commands/chat.js +178 -46
- package/dist/commands/integration/connect.js +1 -1
- package/dist/commands/integration/create.js +36 -36
- package/dist/commands/integration/operation/create.js +2 -1
- package/dist/commands/integration/operation/list.js +23 -15
- package/dist/commands/job/get-step.d.ts +3 -0
- package/dist/commands/job/{step-get.js → get-step.js} +3 -3
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/session/create.js +1 -1
- package/dist/commands/session/events.js +16 -7
- package/dist/commands/session/list.js +1 -1
- package/dist/commands/session/send.js +1 -1
- package/dist/commands/workspace/agent/add.js +16 -3
- package/dist/commands/workspace/agent/list.js +14 -1
- package/dist/commands/workspace/agent/remove.js +14 -1
- package/dist/commands/workspace/clear.d.ts +3 -0
- package/dist/commands/workspace/clear.js +45 -0
- package/dist/commands/workspace/select.js +3 -1
- package/dist/index.js +58 -8
- package/dist/lib/api-types.d.ts +7 -0
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +1 -0
- package/dist/lib/guild-config.d.ts +13 -0
- package/dist/lib/guild-config.js +19 -0
- package/dist/lib/npmrc.js +6 -2
- package/dist/lib/output.js +25 -99
- package/dist/lib/polling.d.ts +7 -0
- package/dist/lib/polling.js +12 -3
- package/dist/lib/session-events.d.ts +32 -16
- package/dist/lib/session-events.js +22 -0
- package/dist/lib/session-polling.d.ts +4 -3
- package/dist/lib/session-polling.js +75 -17
- package/dist/lib/session-resume.js +4 -1
- package/dist/lib/stdin.d.ts +4 -0
- package/dist/lib/stdin.js +23 -0
- package/dist/lib/validate-input-schema.d.ts +19 -0
- package/dist/lib/validate-input-schema.js +208 -0
- package/dist/lib/version-helpers.js +38 -0
- package/dist/lib/workspace-helpers.d.ts +20 -0
- package/dist/lib/workspace-helpers.js +49 -0
- package/dist/mcp/tools.js +8 -52
- package/docs/CLI_WORKFLOW.md +1 -1
- package/docs/skills/agent-dev.md +192 -129
- package/docs/skills/integrations.md +1 -1
- package/package.json +2 -1
- package/dist/commands/job/step-get.d.ts +0 -3
|
@@ -19,6 +19,7 @@ import * as readline from 'readline';
|
|
|
19
19
|
import { pollForResponse } from '../../lib/session-polling.js';
|
|
20
20
|
import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
|
|
21
21
|
import { fetchSession, fetchSessionEvents } from '../../lib/session-resume.js';
|
|
22
|
+
import { getWorkspaceId, getWorkspaceSourceLabel } from '../../lib/guild-config.js';
|
|
22
23
|
// ESM equivalent of __dirname
|
|
23
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
// Read version from package.json
|
|
@@ -103,9 +104,26 @@ export function createAgentChatCommand() {
|
|
|
103
104
|
const sessionPrompt = options.mode === 'json' && inputData
|
|
104
105
|
? JSON.stringify(inputData)
|
|
105
106
|
: initialPrompt;
|
|
106
|
-
|
|
107
|
+
// Pre-resolve workspace so we can show the source label in output.
|
|
108
|
+
// When --workspace is given explicitly, no source label is shown.
|
|
109
|
+
let resolvedWorkspaceId = options.workspace;
|
|
110
|
+
let workspaceSourceLabel;
|
|
111
|
+
if (!resolvedWorkspaceId) {
|
|
112
|
+
const wsResolved = await getWorkspaceId(resolvedPath);
|
|
113
|
+
resolvedWorkspaceId = wsResolved?.workspaceId;
|
|
114
|
+
workspaceSourceLabel = wsResolved
|
|
115
|
+
? getWorkspaceSourceLabel(wsResolved.source)
|
|
116
|
+
: undefined;
|
|
117
|
+
}
|
|
118
|
+
const session = await createSession(client, resolvedWorkspaceId, sessionPrompt, version.id);
|
|
107
119
|
if (!quiet) {
|
|
108
120
|
console.log(`✓ Agent: ${config?.name || agentId}`);
|
|
121
|
+
if (session.workspace_id) {
|
|
122
|
+
const workspaceDisplay = workspaceSourceLabel
|
|
123
|
+
? `${session.workspace_id} (from ${workspaceSourceLabel})`
|
|
124
|
+
: session.workspace_id;
|
|
125
|
+
console.log(`✓ Workspace: ${workspaceDisplay}`);
|
|
126
|
+
}
|
|
109
127
|
const sessionLink = session.session_url
|
|
110
128
|
? hyperlink(session.id, session.session_url)
|
|
111
129
|
: session.id;
|
|
@@ -252,4 +270,9 @@ export function createAgentChatCommand() {
|
|
|
252
270
|
});
|
|
253
271
|
return cmd;
|
|
254
272
|
}
|
|
273
|
+
// Thin wrapper for lazy-loading from index.ts (avoids importing React at startup)
|
|
274
|
+
export async function handleAgentChatAction(_promptArgs, _options) {
|
|
275
|
+
const cmd = createAgentChatCommand();
|
|
276
|
+
await cmd.parseAsync(process.argv.slice(3), { from: 'user' });
|
|
277
|
+
}
|
|
255
278
|
//# sourceMappingURL=chat.js.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
4
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
6
5
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
7
6
|
import { createSteps, createSpinner, format } from '../../lib/progress.js';
|
|
@@ -61,6 +60,7 @@ async function promptForName(defaultName) {
|
|
|
61
60
|
return ask();
|
|
62
61
|
}
|
|
63
62
|
async function promptForTemplate() {
|
|
63
|
+
const { default: inquirer } = await import('inquirer');
|
|
64
64
|
const { template } = await inquirer.prompt([
|
|
65
65
|
{
|
|
66
66
|
type: 'list',
|
|
@@ -12,14 +12,14 @@ import { hyperlink } from '../../lib/colors.js';
|
|
|
12
12
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
13
13
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
14
14
|
import { format } from '../../lib/progress.js';
|
|
15
|
-
import * as readline from 'readline';
|
|
16
15
|
import { parseEventFilter } from '../../lib/event-filter.js';
|
|
17
16
|
import { isQuietMode, getOutputMode } from '../../lib/output-mode.js';
|
|
18
17
|
import { pollForResponse, pollForResponseWithEvents, } from '../../lib/session-polling.js';
|
|
19
|
-
import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
|
|
20
|
-
import { loadLocalConfig, getWorkspaceId } from '../../lib/guild-config.js';
|
|
18
|
+
import { readStdinAsJSON, readStdinAsText, ensureInteractiveStdin, } from '../../lib/stdin.js';
|
|
19
|
+
import { loadLocalConfig, getWorkspaceId, getWorkspaceSourceLabel, } from '../../lib/guild-config.js';
|
|
21
20
|
import { GitError, formatGitError } from '../../lib/git.js';
|
|
22
21
|
import { readAgentFiles, buildEphemeralVersion, buildBundledVersion, BundleNotFoundError, BuildTimeoutError, BuildFailedError, } from '../../lib/agent-helpers.js';
|
|
22
|
+
import { validateInputSchema } from '../../lib/validate-input-schema.js';
|
|
23
23
|
import { fetchSession, fetchSessionEvents } from '../../lib/session-resume.js';
|
|
24
24
|
import { ChatApp, ensureAuthenticated } from '../chat.js';
|
|
25
25
|
import { suppressScrollbackClear } from '../../lib/alternate-screen.js';
|
|
@@ -83,9 +83,10 @@ export function createAgentTestCommand() {
|
|
|
83
83
|
console.error(' guild agent clone <agent-id>');
|
|
84
84
|
process.exit(1);
|
|
85
85
|
}
|
|
86
|
-
// If using JSON input, read and validate it early (before auth/session creation)
|
|
87
|
-
// This provides better UX - fail fast on bad
|
|
86
|
+
// If using JSON/JSONL input, read and validate it early (before auth/session creation)
|
|
87
|
+
// This provides better UX - fail fast on bad input without needing auth
|
|
88
88
|
let inputData;
|
|
89
|
+
let jsonlInputs;
|
|
89
90
|
if (options.mode === 'json') {
|
|
90
91
|
try {
|
|
91
92
|
inputData = await readStdinAsJSON();
|
|
@@ -100,6 +101,57 @@ export function createAgentTestCommand() {
|
|
|
100
101
|
process.exit(1);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
else if (options.mode === 'jsonl') {
|
|
105
|
+
try {
|
|
106
|
+
const stdinContent = await readStdinAsText();
|
|
107
|
+
jsonlInputs = [];
|
|
108
|
+
const lines = stdinContent.split('\n');
|
|
109
|
+
for (let i = 0; i < lines.length; i++) {
|
|
110
|
+
const line = lines[i].trim();
|
|
111
|
+
if (!line)
|
|
112
|
+
continue;
|
|
113
|
+
try {
|
|
114
|
+
jsonlInputs.push(JSON.parse(line));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
console.error(`Error: Invalid JSON on line ${i + 1}`);
|
|
118
|
+
console.error('');
|
|
119
|
+
console.error('Each line must be valid JSON.');
|
|
120
|
+
console.error(' cat inputs.jsonl | guild agent test --mode jsonl');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (jsonlInputs.length === 0) {
|
|
125
|
+
console.error('Error: No JSON input provided');
|
|
126
|
+
console.error('');
|
|
127
|
+
console.error('Example usage:');
|
|
128
|
+
console.error(' cat inputs.jsonl | guild agent test --mode jsonl');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const err = error;
|
|
134
|
+
console.error(`Error: ${err.message}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Validate input against agent's schema locally (before build)
|
|
139
|
+
// Skip when --agent-version is set since local source may differ from that version
|
|
140
|
+
const inputsToValidate = inputData !== undefined ? [inputData] : (jsonlInputs ?? []);
|
|
141
|
+
if (inputsToValidate.length > 0 && !options.agentVersion) {
|
|
142
|
+
const validationResult = await validateInputSchema(cwd, inputsToValidate);
|
|
143
|
+
if (!validationResult.valid) {
|
|
144
|
+
console.error('Input validation failed:');
|
|
145
|
+
for (const err of validationResult.errors) {
|
|
146
|
+
const pathStr = err.path?.length ? ` (at ${err.path.join('.')})` : '';
|
|
147
|
+
console.error(` - ${err.message}${pathStr}`);
|
|
148
|
+
}
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
if ('skipped' in validationResult && validationResult.skipped) {
|
|
152
|
+
format.warn(`Skipping local validation: ${validationResult.reason}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
103
155
|
// If using bundle, verify the file exists early (before auth/session creation)
|
|
104
156
|
// so we fail fast without needing auth if the path is wrong.
|
|
105
157
|
if (options.bundle) {
|
|
@@ -117,9 +169,13 @@ export function createAgentTestCommand() {
|
|
|
117
169
|
}
|
|
118
170
|
// Determine workspace (priority: flag > local config > global config)
|
|
119
171
|
let workspaceId = options.workspace;
|
|
172
|
+
let workspaceSourceLabel;
|
|
120
173
|
if (!workspaceId) {
|
|
121
174
|
const resolved = await getWorkspaceId(cwd);
|
|
122
175
|
workspaceId = resolved?.workspaceId;
|
|
176
|
+
workspaceSourceLabel = resolved
|
|
177
|
+
? getWorkspaceSourceLabel(resolved.source)
|
|
178
|
+
: undefined;
|
|
123
179
|
if (!workspaceId) {
|
|
124
180
|
console.error('Error: No default workspace configured');
|
|
125
181
|
console.error('');
|
|
@@ -251,7 +307,10 @@ export function createAgentTestCommand() {
|
|
|
251
307
|
? 'ephemeral (cached, no changes)'
|
|
252
308
|
: 'ephemeral (working directory)';
|
|
253
309
|
console.log(`✓ Version: ${versionDisplay}`);
|
|
254
|
-
|
|
310
|
+
const workspaceDisplay = workspaceSourceLabel
|
|
311
|
+
? `${workspaceId} (from ${workspaceSourceLabel})`
|
|
312
|
+
: workspaceId;
|
|
313
|
+
console.log(`✓ Workspace: ${workspaceDisplay}`);
|
|
255
314
|
const sessionLink = session.session_url
|
|
256
315
|
? hyperlink(session.id, session.session_url)
|
|
257
316
|
: session.id;
|
|
@@ -325,25 +384,16 @@ export function createAgentTestCommand() {
|
|
|
325
384
|
process.exit(1);
|
|
326
385
|
}
|
|
327
386
|
}
|
|
328
|
-
else if (options.mode === 'jsonl') {
|
|
329
|
-
// JSONL input mode: line-by-line processing
|
|
330
|
-
const rl = readline.createInterface({
|
|
331
|
-
input: process.stdin,
|
|
332
|
-
output: process.stdout,
|
|
333
|
-
terminal: false,
|
|
334
|
-
});
|
|
335
|
-
let lineNumber = 0;
|
|
387
|
+
else if (options.mode === 'jsonl' && jsonlInputs) {
|
|
388
|
+
// JSONL input mode: line-by-line processing (inputs already parsed and validated)
|
|
336
389
|
let processedCount = 0;
|
|
337
390
|
let lastEventId;
|
|
338
|
-
for
|
|
339
|
-
|
|
340
|
-
if (!
|
|
341
|
-
|
|
391
|
+
for (let inputIndex = 0; inputIndex < jsonlInputs.length; inputIndex++) {
|
|
392
|
+
const jsonInput = jsonlInputs[inputIndex];
|
|
393
|
+
if (!quiet) {
|
|
394
|
+
console.error(`Processing input ${inputIndex + 1}/${jsonlInputs.length}...`);
|
|
395
|
+
}
|
|
342
396
|
try {
|
|
343
|
-
const jsonInput = JSON.parse(line);
|
|
344
|
-
if (!quiet) {
|
|
345
|
-
console.error(`Processing line ${lineNumber}...`);
|
|
346
|
-
}
|
|
347
397
|
// Send message
|
|
348
398
|
await client.post(`/sessions/${session.id}/events`, {
|
|
349
399
|
mode: 'json',
|
|
@@ -355,13 +405,12 @@ export function createAgentTestCommand() {
|
|
|
355
405
|
: await pollForResponse(client, session.id, lastEventId, 180000);
|
|
356
406
|
lastEventId = result.lastEventId;
|
|
357
407
|
if (!result.response) {
|
|
358
|
-
console.error(`Timeout: No response for
|
|
408
|
+
console.error(`Timeout: No response for input ${inputIndex + 1}`);
|
|
359
409
|
continue;
|
|
360
410
|
}
|
|
361
411
|
const response = result.response;
|
|
362
412
|
// Output response
|
|
363
413
|
if (quiet) {
|
|
364
|
-
// In quiet mode, output just the message content (or as JSON if --json is used)
|
|
365
414
|
console.log(response);
|
|
366
415
|
}
|
|
367
416
|
else {
|
|
@@ -371,10 +420,8 @@ export function createAgentTestCommand() {
|
|
|
371
420
|
processedCount++;
|
|
372
421
|
}
|
|
373
422
|
catch (error) {
|
|
374
|
-
const
|
|
375
|
-
console.error(`Error
|
|
376
|
-
console.error(`Failed to parse JSON: ${err.message}`);
|
|
377
|
-
console.error('Skipping line and continuing...');
|
|
423
|
+
const formattedErr = handleAxiosError(error);
|
|
424
|
+
console.error(`Error processing input ${inputIndex + 1}: ${formattedErr.details}`);
|
|
378
425
|
}
|
|
379
426
|
}
|
|
380
427
|
if (!quiet) {
|
|
@@ -431,4 +478,9 @@ export function createAgentTestCommand() {
|
|
|
431
478
|
});
|
|
432
479
|
return cmd;
|
|
433
480
|
}
|
|
481
|
+
// Thin wrapper for lazy-loading from index.ts (avoids importing React at startup)
|
|
482
|
+
export async function handleAgentTestAction(_options) {
|
|
483
|
+
const cmd = createAgentTestCommand();
|
|
484
|
+
await cmd.parseAsync(process.argv.slice(3), { from: 'user' });
|
|
485
|
+
}
|
|
434
486
|
//# sourceMappingURL=test.js.map
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -23,4 +23,5 @@ export declare function ChatApp({ initialPrompt, version, workspaceId, versionId
|
|
|
23
23
|
export declare function ensureAuthenticated(): Promise<string>;
|
|
24
24
|
export declare function createSession(client: GuildAPIClient, workspaceId: string | undefined, initialPrompt: string, versionId?: string, onProgress?: (status: string) => void): Promise<Session>;
|
|
25
25
|
export declare function createChatCommand(): Command;
|
|
26
|
+
export declare function handleChatAction(_promptArgs: string[], _options: Record<string, unknown>): Promise<void>;
|
|
26
27
|
//# sourceMappingURL=chat.d.ts.map
|
package/dist/commands/chat.js
CHANGED
|
@@ -13,12 +13,12 @@ import chalk from 'chalk';
|
|
|
13
13
|
import { readFileSync } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
|
-
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, } from '../lib/session-events.js';
|
|
16
|
+
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
|
|
17
17
|
import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
|
|
18
18
|
import { DEFAULT_EVENT_TYPES, parseEventFilter, shouldShowEvent, } from '../lib/event-filter.js';
|
|
19
19
|
import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
|
|
20
20
|
import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
|
|
21
|
-
import { getWorkspaceId } from '../lib/guild-config.js';
|
|
21
|
+
import { getWorkspaceId, getWorkspaceSourceLabel } from '../lib/guild-config.js';
|
|
22
22
|
import { ensureInteractiveStdin } from '../lib/stdin.js';
|
|
23
23
|
import { brand, BRAND_COLOR, code as codeColor, hyperlink } from '../lib/colors.js';
|
|
24
24
|
import { SplashAnimation } from '../components/SplashAnimation.js';
|
|
@@ -48,6 +48,48 @@ function fixListItemMarkdown(text) {
|
|
|
48
48
|
text = text.replace(/(?<![\\w])_([^_]+)_(?![\\w])/g, (_, content) => chalk.italic(content));
|
|
49
49
|
return text;
|
|
50
50
|
}
|
|
51
|
+
function extractMessageText(text) {
|
|
52
|
+
const trimmed = text.trim();
|
|
53
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}'))
|
|
54
|
+
return text;
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(trimmed);
|
|
57
|
+
if (typeof parsed === 'object' && parsed !== null && 'message' in parsed) {
|
|
58
|
+
return typeof parsed.message === 'string' ? parsed.message : text;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If parsing fails, use the content as-is.
|
|
63
|
+
}
|
|
64
|
+
return text;
|
|
65
|
+
}
|
|
66
|
+
function extractRuntimeDoneText(content) {
|
|
67
|
+
if (content === null || content === undefined)
|
|
68
|
+
return null;
|
|
69
|
+
if (typeof content === 'string')
|
|
70
|
+
return content.trim() ? content : null;
|
|
71
|
+
if (typeof content !== 'object' || Array.isArray(content)) {
|
|
72
|
+
return JSON.stringify(content);
|
|
73
|
+
}
|
|
74
|
+
const record = content;
|
|
75
|
+
if (Object.keys(record).length === 0)
|
|
76
|
+
return null;
|
|
77
|
+
if (record.type === 'text') {
|
|
78
|
+
if (typeof record.text === 'string')
|
|
79
|
+
return record.text;
|
|
80
|
+
if (typeof record.data === 'string')
|
|
81
|
+
return record.data;
|
|
82
|
+
}
|
|
83
|
+
if (typeof record.message === 'string')
|
|
84
|
+
return record.message;
|
|
85
|
+
// runtime_done content is agent output, not notification content. Preserve
|
|
86
|
+
// structured outputs rather than trying to render notification-only shapes.
|
|
87
|
+
return JSON.stringify(content);
|
|
88
|
+
}
|
|
89
|
+
function renderAssistantMessage(text, taskName) {
|
|
90
|
+
const rendered = fixListItemMarkdown(marked.parse(text));
|
|
91
|
+
return `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
|
|
92
|
+
}
|
|
51
93
|
/**
|
|
52
94
|
* Output the result of a --once mode session.
|
|
53
95
|
* Handles both JSON and human-readable output formats.
|
|
@@ -57,21 +99,29 @@ async function outputOnceResult(sessionId, events, mode) {
|
|
|
57
99
|
console.log(JSON.stringify({ session_id: sessionId, events }, null, 2));
|
|
58
100
|
}
|
|
59
101
|
else {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
102
|
+
const finalAgentMessages = events.filter((e) => e.type === 'agent_notification_message' &&
|
|
103
|
+
isRootTaskEvent(e) &&
|
|
104
|
+
!isResponseStreamEvent(e));
|
|
105
|
+
const streamFallbacks = events.filter((e) => e.type === 'agent_notification_message' &&
|
|
106
|
+
isRootTaskEvent(e) &&
|
|
107
|
+
isDoneResponseStreamEvent(e));
|
|
108
|
+
if (finalAgentMessages.length > 0) {
|
|
109
|
+
const messageContent = extractMessageText(getAgentNotificationText(finalAgentMessages[finalAgentMessages.length - 1]));
|
|
110
|
+
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
111
|
+
console.log(rendered.trim());
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const runtimeDoneEvents = events.filter((e) => e.type === 'runtime_done' && isRootTaskEvent(e) && e.content);
|
|
115
|
+
const runtimeDoneWithContent = runtimeDoneEvents[runtimeDoneEvents.length - 1];
|
|
116
|
+
if (runtimeDoneWithContent?.type === 'runtime_done') {
|
|
117
|
+
const runtimeDoneText = extractRuntimeDoneText(runtimeDoneWithContent.content);
|
|
118
|
+
if (runtimeDoneText) {
|
|
119
|
+
console.log(runtimeDoneText);
|
|
120
|
+
return;
|
|
74
121
|
}
|
|
122
|
+
}
|
|
123
|
+
if (streamFallbacks.length > 0) {
|
|
124
|
+
const messageContent = extractMessageText(getAgentNotificationText(streamFallbacks[streamFallbacks.length - 1]));
|
|
75
125
|
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
76
126
|
console.log(rendered.trim());
|
|
77
127
|
}
|
|
@@ -427,6 +477,83 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
427
477
|
const isPolling = useRef(false);
|
|
428
478
|
const receivedResponseSinceLastInput = useRef(false);
|
|
429
479
|
const firstMessageNotified = useRef(!!resumeEvents);
|
|
480
|
+
const responseStreamKeys = useRef(new Map());
|
|
481
|
+
const responseStreamTimestamps = useRef(new Map());
|
|
482
|
+
const responseStreamKeysByTask = useRef(new Map());
|
|
483
|
+
const clearResponseStreamsForTask = (taskId) => {
|
|
484
|
+
if (!taskId)
|
|
485
|
+
return;
|
|
486
|
+
const keys = responseStreamKeysByTask.current.get(taskId);
|
|
487
|
+
if (!keys?.size)
|
|
488
|
+
return;
|
|
489
|
+
for (const [streamId, key] of responseStreamKeys.current.entries()) {
|
|
490
|
+
if (keys.has(key)) {
|
|
491
|
+
responseStreamKeys.current.delete(streamId);
|
|
492
|
+
responseStreamTimestamps.current.delete(streamId);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
responseStreamKeysByTask.current.delete(taskId);
|
|
496
|
+
setMessages((prev) => prev.filter((message) => !keys.has(message.key)));
|
|
497
|
+
};
|
|
498
|
+
const upsertResponseStreamMessage = (event) => {
|
|
499
|
+
if (!isResponseStreamEvent(event))
|
|
500
|
+
return;
|
|
501
|
+
if (!isRootTaskEvent(event))
|
|
502
|
+
return;
|
|
503
|
+
const streamId = event.content.stream_id;
|
|
504
|
+
const taskId = event.task?.id;
|
|
505
|
+
const existingKey = responseStreamKeys.current.get(streamId);
|
|
506
|
+
if (event.content.status === 'aborted') {
|
|
507
|
+
if (existingKey) {
|
|
508
|
+
responseStreamKeys.current.delete(streamId);
|
|
509
|
+
responseStreamTimestamps.current.delete(streamId);
|
|
510
|
+
if (taskId) {
|
|
511
|
+
const keys = responseStreamKeysByTask.current.get(taskId);
|
|
512
|
+
keys?.delete(existingKey);
|
|
513
|
+
if (keys?.size === 0)
|
|
514
|
+
responseStreamKeysByTask.current.delete(taskId);
|
|
515
|
+
}
|
|
516
|
+
setMessages((prev) => prev.filter((message) => message.key !== existingKey));
|
|
517
|
+
}
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const text = event.content.text;
|
|
521
|
+
if (!text.trim())
|
|
522
|
+
return;
|
|
523
|
+
const key = existingKey ?? `response-stream-${streamId}`;
|
|
524
|
+
responseStreamKeys.current.set(streamId, key);
|
|
525
|
+
if (taskId) {
|
|
526
|
+
const keys = responseStreamKeysByTask.current.get(taskId) ?? new Set();
|
|
527
|
+
keys.add(key);
|
|
528
|
+
responseStreamKeysByTask.current.set(taskId, keys);
|
|
529
|
+
}
|
|
530
|
+
const taskName = agentName || 'assistant';
|
|
531
|
+
const messageContent = renderAssistantMessage(text, taskName);
|
|
532
|
+
const timestamp = responseStreamTimestamps.current.get(streamId) ?? new Date().toLocaleTimeString();
|
|
533
|
+
responseStreamTimestamps.current.set(streamId, timestamp);
|
|
534
|
+
setMessages((prev) => {
|
|
535
|
+
const index = prev.findIndex((message) => message.key === key);
|
|
536
|
+
const message = {
|
|
537
|
+
key,
|
|
538
|
+
content: messageContent,
|
|
539
|
+
type: 'assistant',
|
|
540
|
+
timestamp,
|
|
541
|
+
};
|
|
542
|
+
if (index === -1)
|
|
543
|
+
return [...prev, message];
|
|
544
|
+
const next = [...prev];
|
|
545
|
+
next[index] = message;
|
|
546
|
+
return next;
|
|
547
|
+
});
|
|
548
|
+
if (!firstMessageNotified.current && onFirstMessage) {
|
|
549
|
+
firstMessageNotified.current = true;
|
|
550
|
+
onFirstMessage();
|
|
551
|
+
}
|
|
552
|
+
if (event.content.status === 'done') {
|
|
553
|
+
receivedResponseSinceLastInput.current = true;
|
|
554
|
+
setCurrentOperation('');
|
|
555
|
+
}
|
|
556
|
+
};
|
|
430
557
|
// Mark initial prompt as sent (skip for resume — we already have the events)
|
|
431
558
|
useEffect(() => {
|
|
432
559
|
if (!resumeEvents) {
|
|
@@ -526,6 +653,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
526
653
|
}
|
|
527
654
|
// Process events that affect the chat UI (task state comes from tasks poll)
|
|
528
655
|
if (event.type === 'runtime_error') {
|
|
656
|
+
clearResponseStreamsForTask(taskInfo?.id);
|
|
529
657
|
// Always clear the spinner on runtime errors so the UI doesn't get stuck
|
|
530
658
|
setCurrentOperation('');
|
|
531
659
|
// Show runtime errors in the chat (gated on --events filter)
|
|
@@ -639,32 +767,15 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
639
767
|
}
|
|
640
768
|
}
|
|
641
769
|
else if (event.type === 'agent_notification_message') {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
650
|
-
try {
|
|
651
|
-
const parsed = JSON.parse(trimmed);
|
|
652
|
-
// If JSON has a "message" field, extract it (some agents wrap responses this way)
|
|
653
|
-
if (typeof parsed === 'object' &&
|
|
654
|
-
parsed !== null &&
|
|
655
|
-
'message' in parsed) {
|
|
656
|
-
text = parsed.message;
|
|
657
|
-
}
|
|
658
|
-
// Otherwise use full JSON content as-is (legitimate JSON responses)
|
|
659
|
-
}
|
|
660
|
-
catch {
|
|
661
|
-
// If parsing fails, use the content as-is
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
const rendered = fixListItemMarkdown(marked.parse(text));
|
|
665
|
-
// Add attribution with task name
|
|
770
|
+
if (isResponseStreamEvent(event)) {
|
|
771
|
+
upsertResponseStreamMessage(event);
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const text = extractMessageText(getAgentNotificationText(event));
|
|
775
|
+
if (text.trim()) {
|
|
776
|
+
clearResponseStreamsForTask(taskInfo?.id);
|
|
666
777
|
const taskName = agentName || 'assistant';
|
|
667
|
-
const messageContent =
|
|
778
|
+
const messageContent = renderAssistantMessage(text, taskName);
|
|
668
779
|
setMessages((prev) => [
|
|
669
780
|
...prev,
|
|
670
781
|
{
|
|
@@ -684,6 +795,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
684
795
|
setCurrentOperation('');
|
|
685
796
|
}
|
|
686
797
|
else if (event.type === 'agent_notification_error') {
|
|
798
|
+
clearResponseStreamsForTask(taskInfo?.id);
|
|
687
799
|
// Show error in chat (task status is updated via tasks poll)
|
|
688
800
|
const errorText = typeof event.content === 'string'
|
|
689
801
|
? event.content
|
|
@@ -730,6 +842,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
730
842
|
event.content !== undefined &&
|
|
731
843
|
taskInfo &&
|
|
732
844
|
'agent' in taskInfo) {
|
|
845
|
+
clearResponseStreamsForTask(taskInfo.id);
|
|
733
846
|
// One-shot agents may complete with runtime_done without sending
|
|
734
847
|
// agent_notification_message. Display the output if we haven't
|
|
735
848
|
// already shown a response for this input cycle.
|
|
@@ -759,6 +872,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
759
872
|
}
|
|
760
873
|
}
|
|
761
874
|
else if (event.type === 'interrupted') {
|
|
875
|
+
clearResponseStreamsForTask(taskInfo?.id);
|
|
762
876
|
// Session was interrupted — interrupted sessions are terminal on the backend
|
|
763
877
|
setMessages((prev) => [
|
|
764
878
|
...prev,
|
|
@@ -987,6 +1101,10 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
|
|
|
987
1101
|
const resolved = await getWorkspaceId();
|
|
988
1102
|
if (resolved) {
|
|
989
1103
|
workspaceId = resolved.workspaceId;
|
|
1104
|
+
const sourceLabel = getWorkspaceSourceLabel(resolved.source);
|
|
1105
|
+
if (sourceLabel) {
|
|
1106
|
+
progress(`Using workspace from ${sourceLabel}`);
|
|
1107
|
+
}
|
|
990
1108
|
}
|
|
991
1109
|
}
|
|
992
1110
|
if (!workspaceId) {
|
|
@@ -1045,7 +1163,9 @@ export function createChatCommand() {
|
|
|
1045
1163
|
try {
|
|
1046
1164
|
await ensureAuthenticated();
|
|
1047
1165
|
const client = new GuildAPIClient();
|
|
1048
|
-
const session = await createSession(client, options.workspace, initialPrompt, options.agent)
|
|
1166
|
+
const session = await createSession(client, options.workspace, initialPrompt, options.agent, (status) => {
|
|
1167
|
+
spinner.text = status;
|
|
1168
|
+
});
|
|
1049
1169
|
spinner.succeed('Connected');
|
|
1050
1170
|
if (session.session_url) {
|
|
1051
1171
|
const sessionLink = hyperlink(session.id, session.session_url);
|
|
@@ -1084,12 +1204,18 @@ export function createChatCommand() {
|
|
|
1084
1204
|
else {
|
|
1085
1205
|
inactivityCounter++;
|
|
1086
1206
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1207
|
+
// Check if we got a completion response from the root agent.
|
|
1208
|
+
// Stream done events are only a rendering fallback; wait for the
|
|
1209
|
+
// final root message or runtime completion so child drafts cannot
|
|
1210
|
+
// terminate --once early.
|
|
1211
|
+
const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' && isRootTaskEvent(e));
|
|
1212
|
+
const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
1213
|
+
isRootTaskEvent(e) &&
|
|
1214
|
+
!isResponseStreamEvent(e));
|
|
1215
|
+
const hasRootTaskError = allEvents.some((e) => e.type === 'runtime_error' && isRootTaskEvent(e));
|
|
1091
1216
|
// Check for a ui_prompt request... that ends the game.
|
|
1092
1217
|
const hasUIPromptMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
1218
|
+
!isResponseStreamEvent(e) &&
|
|
1093
1219
|
e.task?.tool_name === 'ui_prompt');
|
|
1094
1220
|
if (hasRootTaskError) {
|
|
1095
1221
|
debug('Found error event from root agent, exiting --once mode');
|
|
@@ -1177,4 +1303,10 @@ export function createChatCommand() {
|
|
|
1177
1303
|
});
|
|
1178
1304
|
return cmd;
|
|
1179
1305
|
}
|
|
1306
|
+
// Thin wrapper for lazy-loading from index.ts (avoids importing React at startup)
|
|
1307
|
+
export async function handleChatAction(_promptArgs, _options) {
|
|
1308
|
+
const cmd = createChatCommand();
|
|
1309
|
+
// Re-parse the original argv so the command's own action handler runs
|
|
1310
|
+
await cmd.parseAsync(process.argv.slice(2), { from: 'user' });
|
|
1311
|
+
}
|
|
1180
1312
|
//# sourceMappingURL=chat.js.map
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import inquirer from 'inquirer';
|
|
6
5
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
7
6
|
import { getAuthToken } from '../../lib/auth.js';
|
|
8
7
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
@@ -40,6 +39,7 @@ export function createIntegrationConnectCommand() {
|
|
|
40
39
|
}
|
|
41
40
|
let apiKey = options.token;
|
|
42
41
|
if (!apiKey) {
|
|
42
|
+
const { default: inquirer } = await import('inquirer');
|
|
43
43
|
const answer = await inquirer.prompt([
|
|
44
44
|
{
|
|
45
45
|
type: 'password',
|