@guildai/cli 0.6.0 → 0.6.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/README.md +17 -0
- package/dist/commands/agent/chat.js +31 -31
- package/dist/commands/agent/fork.js +1 -1
- package/dist/commands/agent/init.js +1 -1
- package/dist/commands/agent/pull.js +36 -21
- package/dist/commands/agent/save.js +2 -2
- package/dist/commands/agent/test.js +8 -12
- package/dist/commands/auth/login.js +3 -3
- package/dist/commands/chat.js +64 -102
- package/dist/commands/credentials/endpoint-list.js +1 -1
- package/dist/commands/integration/connect.d.ts +3 -0
- package/dist/commands/integration/connect.js +76 -0
- package/dist/commands/integration/create.d.ts +3 -0
- package/dist/commands/integration/create.js +298 -0
- package/dist/commands/integration/get.d.ts +3 -0
- package/dist/commands/integration/get.js +95 -0
- package/dist/commands/integration/list.d.ts +3 -0
- package/dist/commands/integration/list.js +61 -0
- package/dist/commands/integration/operation/create.d.ts +3 -0
- package/dist/commands/integration/operation/create.js +163 -0
- package/dist/commands/integration/operation/list.d.ts +3 -0
- package/dist/commands/integration/operation/list.js +83 -0
- package/dist/commands/integration/update.d.ts +3 -0
- package/dist/commands/integration/update.js +139 -0
- package/dist/commands/integration/version/build.d.ts +3 -0
- package/dist/commands/integration/version/build.js +86 -0
- package/dist/commands/integration/version/create.d.ts +3 -0
- package/dist/commands/integration/version/create.js +45 -0
- package/dist/commands/integration/version/get.d.ts +3 -0
- package/dist/commands/integration/version/get.js +72 -0
- package/dist/commands/integration/version/list.d.ts +3 -0
- package/dist/commands/integration/version/list.js +44 -0
- package/dist/commands/integration/version/publish.d.ts +3 -0
- package/dist/commands/integration/version/publish.js +79 -0
- package/dist/commands/integration/version/test.d.ts +3 -0
- package/dist/commands/integration/version/test.js +104 -0
- package/dist/commands/workspace/create.js +10 -4
- package/dist/index.js +38 -0
- package/dist/lib/api-types.d.ts +69 -12
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +3 -2
- package/dist/lib/integration-helpers.d.ts +15 -0
- package/dist/lib/integration-helpers.js +38 -0
- package/dist/lib/output.d.ts +11 -1
- package/dist/lib/output.js +94 -0
- package/dist/lib/session-events-fetch.d.ts +27 -0
- package/dist/lib/session-events-fetch.js +25 -0
- package/dist/lib/session-polling.d.ts +7 -2
- package/dist/lib/session-polling.js +18 -11
- package/dist/lib/session-resume.js +2 -5
- package/dist/lib/table.d.ts +0 -1
- package/dist/lib/table.js +0 -1
- package/dist/mcp/tools.js +5 -11
- package/docs/CLI_WORKFLOW.md +15 -0
- package/docs/skills/agent-dev.md +15 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -183,6 +183,23 @@ guild trigger deactivate <trigger-id> # Deactivate a trigger
|
|
|
183
183
|
guild trigger sessions <trigger-id> # List sessions spawned by a trigger
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
+
### Integrations
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
guild integration list # List integrations
|
|
190
|
+
guild integration get <id> # Get integration details
|
|
191
|
+
guild integration create <name> # Create a new integration
|
|
192
|
+
guild integration update <id> # Update an integration
|
|
193
|
+
guild integration version list <id> # List versions
|
|
194
|
+
guild integration version create <id> # Create a draft version
|
|
195
|
+
guild integration version get <id> # Get version details
|
|
196
|
+
guild integration version build <id> # Build (validate) a draft
|
|
197
|
+
guild integration version publish <id> # Publish a built version
|
|
198
|
+
guild integration version test <id> # Test an endpoint invocation
|
|
199
|
+
guild integration operation list <id> # List operations for a version
|
|
200
|
+
guild integration operation create <id> # Create operations (or import from OpenAPI)
|
|
201
|
+
```
|
|
202
|
+
|
|
186
203
|
### Chat
|
|
187
204
|
|
|
188
205
|
```bash
|
|
@@ -38,13 +38,29 @@ export function createAgentChatCommand() {
|
|
|
38
38
|
.option('--no-splash', 'Skip the splash screen animation')
|
|
39
39
|
.option('--resume <session-id>', 'Resume an existing session')
|
|
40
40
|
.option('--open', 'Open session in web dashboard')
|
|
41
|
-
.addHelpText('after', '\
|
|
41
|
+
.addHelpText('after', '\nTo chat with a published agent by name: guild chat --agent owner/agent-name')
|
|
42
42
|
.action(async (promptArgs, options) => {
|
|
43
43
|
try {
|
|
44
44
|
// Get agent ID from guild.json in the specified path
|
|
45
45
|
const agentPath = options.path || '.';
|
|
46
46
|
const { agentId, config } = await getAgentId(undefined, agentPath);
|
|
47
47
|
const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
|
|
48
|
+
// If using JSON input, read it early (before auth) for fast failure on bad input
|
|
49
|
+
let inputData;
|
|
50
|
+
if (options.mode === 'json') {
|
|
51
|
+
try {
|
|
52
|
+
inputData = await readStdinAsJSON();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const err = error;
|
|
56
|
+
console.error(`Error: ${err.message}`);
|
|
57
|
+
console.error('');
|
|
58
|
+
console.error('Example usage:');
|
|
59
|
+
console.error(' echo \'{"prompt": "test"}\' | guild agent chat --mode json');
|
|
60
|
+
console.error(' guild agent chat --mode json < input.json');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
48
64
|
// Check authentication (uses shared function from chat.tsx)
|
|
49
65
|
await ensureAuthenticated();
|
|
50
66
|
const client = new GuildAPIClient();
|
|
@@ -152,25 +168,14 @@ export function createAgentChatCommand() {
|
|
|
152
168
|
console.error(` guild agent chat ${agentId}`);
|
|
153
169
|
process.exit(1);
|
|
154
170
|
}
|
|
155
|
-
// If using JSON input, read it early (before session creation)
|
|
156
|
-
let inputData;
|
|
157
|
-
if (options.mode === 'json') {
|
|
158
|
-
try {
|
|
159
|
-
inputData = await readStdinAsJSON();
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
const err = error;
|
|
163
|
-
console.error(`Error: ${err.message}`);
|
|
164
|
-
console.error('');
|
|
165
|
-
console.error('Example usage:');
|
|
166
|
-
console.error(' echo \'{"prompt": "test"}\' | guild agent chat --mode json');
|
|
167
|
-
console.error(' guild agent chat --mode json < input.json');
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
171
|
// Branch: JSON/JSONL modes vs interactive
|
|
172
172
|
if (options.mode === 'json' || options.mode === 'jsonl') {
|
|
173
|
-
|
|
173
|
+
// For JSON mode with piped input, use the piped data as the initial prompt
|
|
174
|
+
// so the agent receives it as its input (not the default 'Hello')
|
|
175
|
+
const sessionPrompt = options.mode === 'json' && inputData
|
|
176
|
+
? JSON.stringify(inputData)
|
|
177
|
+
: initialPrompt;
|
|
178
|
+
const session = await createSession(client, options.workspace, sessionPrompt, version.id);
|
|
174
179
|
if (!quiet) {
|
|
175
180
|
console.log(`✓ Agent: ${config?.name || agentId}`);
|
|
176
181
|
const sessionLink = session.session_url
|
|
@@ -183,13 +188,10 @@ export function createAgentChatCommand() {
|
|
|
183
188
|
await open(session.session_url);
|
|
184
189
|
}
|
|
185
190
|
if (options.mode === 'json' && inputData) {
|
|
186
|
-
// JSON one-shot mode
|
|
191
|
+
// JSON one-shot mode — input was passed as initial_prompt during
|
|
192
|
+
// session creation, so the agent already has it. Just poll for the response.
|
|
187
193
|
try {
|
|
188
|
-
await client
|
|
189
|
-
mode: 'json',
|
|
190
|
-
content: inputData,
|
|
191
|
-
});
|
|
192
|
-
const response = await pollForResponse(client, session.id, 0);
|
|
194
|
+
const { response } = await pollForResponse(client, session.id, undefined);
|
|
193
195
|
if (!response) {
|
|
194
196
|
console.error('Timeout: No response received from agent');
|
|
195
197
|
process.exit(1);
|
|
@@ -219,7 +221,7 @@ export function createAgentChatCommand() {
|
|
|
219
221
|
});
|
|
220
222
|
let lineNumber = 0;
|
|
221
223
|
let processedCount = 0;
|
|
222
|
-
let
|
|
224
|
+
let lastEventId;
|
|
223
225
|
for await (const line of rl) {
|
|
224
226
|
lineNumber++;
|
|
225
227
|
if (!line.trim())
|
|
@@ -233,15 +235,13 @@ export function createAgentChatCommand() {
|
|
|
233
235
|
mode: 'json',
|
|
234
236
|
content: jsonInput,
|
|
235
237
|
});
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
+
const result = await pollForResponse(client, session.id, lastEventId);
|
|
239
|
+
lastEventId = result.lastEventId;
|
|
240
|
+
if (!result.response) {
|
|
238
241
|
console.error(`Timeout: No response for line ${lineNumber}`);
|
|
239
|
-
const eventsAfterResponse = await client.get(`/sessions/${session.id}/events`);
|
|
240
|
-
lastEventCount = eventsAfterResponse?.items?.length || 0;
|
|
241
242
|
continue;
|
|
242
243
|
}
|
|
243
|
-
const
|
|
244
|
-
lastEventCount = eventsAfterResponse?.items?.length || 0;
|
|
244
|
+
const response = result.response;
|
|
245
245
|
if (quiet) {
|
|
246
246
|
console.log(response);
|
|
247
247
|
}
|
|
@@ -52,7 +52,7 @@ export function createAgentForkCommand() {
|
|
|
52
52
|
.option('--name <name>', 'Name for the forked agent')
|
|
53
53
|
.option('--description <desc>', 'Description for the forked agent')
|
|
54
54
|
.option('--directory <path>', 'Target directory for clone')
|
|
55
|
-
.option('--owner <owner
|
|
55
|
+
.option('--owner <owner>', 'Owner (name or ID)')
|
|
56
56
|
.action(async (identifierArg, options) => {
|
|
57
57
|
const output = createOutputWriter();
|
|
58
58
|
try {
|
|
@@ -79,7 +79,7 @@ export function createAgentInitCommand() {
|
|
|
79
79
|
.option('--name <name>', 'Agent name')
|
|
80
80
|
.option('--template <template>', 'Agent template (LLM, AUTO_MANAGED_STATE, BLANK)')
|
|
81
81
|
.option('--fork <agent-id>', 'Fork from existing agent')
|
|
82
|
-
.option('--owner <owner
|
|
82
|
+
.option('--owner <owner>', 'Owner (name or ID)')
|
|
83
83
|
.option('--directory <path>', 'Directory to initialize (created if needed)')
|
|
84
84
|
.option('--force', 'Overwrite existing guild.json', false)
|
|
85
85
|
.action(async (options) => {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
5
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
6
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
6
7
|
import { createOutputWriter } from '../../lib/output.js';
|
|
7
8
|
import * as fs from 'fs/promises';
|
|
8
9
|
import * as path from 'path';
|
|
@@ -90,10 +91,12 @@ export function createAgentPullCommand() {
|
|
|
90
91
|
if (errMessage.includes('no tracking information') ||
|
|
91
92
|
errMessage.includes("couldn't find remote ref")) {
|
|
92
93
|
output.progress('✓ Already up to date (no remote branch yet)');
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
if (getOutputMode() === 'json') {
|
|
95
|
+
output.data({
|
|
96
|
+
success: true,
|
|
97
|
+
message: 'Already up to date (no remote branch yet)',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
97
100
|
return;
|
|
98
101
|
}
|
|
99
102
|
// Unknown git error
|
|
@@ -109,22 +112,28 @@ export function createAgentPullCommand() {
|
|
|
109
112
|
// Truly up to date — git pull + version match
|
|
110
113
|
if (!gitPulledNewCommits) {
|
|
111
114
|
output.progress('✓ Already up to date');
|
|
112
|
-
output.data({ success: true, message: 'Already up to date' });
|
|
113
115
|
}
|
|
114
|
-
|
|
115
|
-
output.data({
|
|
116
|
+
if (getOutputMode() === 'json') {
|
|
117
|
+
output.data({
|
|
118
|
+
success: true,
|
|
119
|
+
message: gitPulledNewCommits
|
|
120
|
+
? 'Pulled remote changes'
|
|
121
|
+
: 'Already up to date',
|
|
122
|
+
});
|
|
116
123
|
}
|
|
117
124
|
}
|
|
118
125
|
else if (latest.sha && latest.sha !== localHead) {
|
|
119
126
|
// SHA mismatch — warn user
|
|
120
127
|
output.progress(`⚠ Remote has a newer version (${latest.sha.slice(0, 7)}) not on this branch`);
|
|
121
128
|
output.progress(' Try: git fetch origin && git log --oneline origin/main');
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
if (getOutputMode() === 'json') {
|
|
130
|
+
output.data({
|
|
131
|
+
success: true,
|
|
132
|
+
message: 'SHA mismatch with latest version',
|
|
133
|
+
latest_sha: latest.sha,
|
|
134
|
+
local_sha: localHead,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
128
137
|
}
|
|
129
138
|
else {
|
|
130
139
|
// Ephemeral version — download from API
|
|
@@ -142,21 +151,27 @@ export function createAgentPullCommand() {
|
|
|
142
151
|
await fs.writeFile(filePath, file.content, 'utf-8');
|
|
143
152
|
}
|
|
144
153
|
output.progress(`✓ Downloaded ${files.length} files from latest draft version`);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
if (getOutputMode() === 'json') {
|
|
155
|
+
output.data({
|
|
156
|
+
success: true,
|
|
157
|
+
message: `Downloaded ${files.length} files from draft version`,
|
|
158
|
+
files_updated: files.length,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
150
161
|
}
|
|
151
162
|
}
|
|
152
163
|
else {
|
|
153
164
|
// No versions exist yet — just report git pull result
|
|
154
165
|
if (!gitPulledNewCommits) {
|
|
155
166
|
output.progress('✓ Already up to date');
|
|
156
|
-
output.data({ success: true, message: 'Already up to date' });
|
|
157
167
|
}
|
|
158
|
-
|
|
159
|
-
output.data({
|
|
168
|
+
if (getOutputMode() === 'json') {
|
|
169
|
+
output.data({
|
|
170
|
+
success: true,
|
|
171
|
+
message: gitPulledNewCommits
|
|
172
|
+
? 'Pulled remote changes'
|
|
173
|
+
: 'Already up to date',
|
|
174
|
+
});
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
177
|
}
|
|
@@ -13,9 +13,9 @@ import { pollUntilComplete } from '../../lib/polling.js';
|
|
|
13
13
|
export function createAgentSaveCommand() {
|
|
14
14
|
const cmd = new Command('save');
|
|
15
15
|
cmd
|
|
16
|
-
.description('
|
|
16
|
+
.description('Commit, push, and create a new agent version')
|
|
17
17
|
.option('-A, --all', 'Stage all changes and commit before pushing', false)
|
|
18
|
-
.option('--message <text>', 'Commit message (required with --all)')
|
|
18
|
+
.option('-m, --message <text>', 'Commit message (required with --all)')
|
|
19
19
|
.option('--wait', 'Wait for validation to complete before returning', false)
|
|
20
20
|
.option('--publish', 'Publish after validation passes (implies --wait)', false)
|
|
21
21
|
.option('--bump [level]', 'Bump package.json version before saving (patch, or minor/major)', 'patch')
|
|
@@ -303,9 +303,9 @@ export function createAgentTestCommand() {
|
|
|
303
303
|
mode: 'json',
|
|
304
304
|
content: inputData,
|
|
305
305
|
});
|
|
306
|
-
// Poll for response (starting from
|
|
306
|
+
// Poll for response (starting from beginning)
|
|
307
307
|
// 3 minutes - allow time for agents that use LLM calls for input parsing
|
|
308
|
-
const response = await pollForResponse(client, session.id,
|
|
308
|
+
const { response } = await pollForResponse(client, session.id, undefined, 180000);
|
|
309
309
|
if (!response) {
|
|
310
310
|
console.error('Error: No response received from agent within timeout');
|
|
311
311
|
console.error('');
|
|
@@ -365,7 +365,7 @@ export function createAgentTestCommand() {
|
|
|
365
365
|
});
|
|
366
366
|
let lineNumber = 0;
|
|
367
367
|
let processedCount = 0;
|
|
368
|
-
let
|
|
368
|
+
let lastEventId;
|
|
369
369
|
for await (const line of rl) {
|
|
370
370
|
lineNumber++;
|
|
371
371
|
if (!line.trim())
|
|
@@ -380,18 +380,14 @@ export function createAgentTestCommand() {
|
|
|
380
380
|
mode: 'json',
|
|
381
381
|
content: jsonInput,
|
|
382
382
|
});
|
|
383
|
-
// Wait for response (looking for events after
|
|
384
|
-
const
|
|
385
|
-
|
|
383
|
+
// Wait for response (looking for events after last seen)
|
|
384
|
+
const result = await pollForResponse(client, session.id, lastEventId, 180000);
|
|
385
|
+
lastEventId = result.lastEventId;
|
|
386
|
+
if (!result.response) {
|
|
386
387
|
console.error(`Timeout: No response for line ${lineNumber}`);
|
|
387
|
-
// Update event count even if timeout
|
|
388
|
-
const eventsAfterResponse = await client.get(`/sessions/${session.id}/events`);
|
|
389
|
-
lastEventCount = eventsAfterResponse?.items?.length || 0;
|
|
390
388
|
continue;
|
|
391
389
|
}
|
|
392
|
-
|
|
393
|
-
const eventsAfterResponse = await client.get(`/sessions/${session.id}/events`);
|
|
394
|
-
lastEventCount = eventsAfterResponse?.items?.length || 0;
|
|
390
|
+
const response = result.response;
|
|
395
391
|
// Output response
|
|
396
392
|
if (quiet) {
|
|
397
393
|
// In quiet mode, output just the message content (or as JSON if --json is used)
|
|
@@ -11,11 +11,11 @@ export function createAuthLoginCommand() {
|
|
|
11
11
|
.description('Login to Guild.ai')
|
|
12
12
|
.option('--return-url <url>', 'Custom URL to redirect to after authentication')
|
|
13
13
|
.option('--return-label <text>', 'Friendly label for return button (e.g., "VSCode")')
|
|
14
|
-
.option('--
|
|
14
|
+
.option('--non-interactive', 'Skip interactive prompts (for use with coding agents)')
|
|
15
15
|
.action(async (options) => {
|
|
16
16
|
const output = createOutputWriter();
|
|
17
|
-
const
|
|
18
|
-
const success = await login(options.returnUrl, options.returnLabel,
|
|
17
|
+
const nonInteractive = options.nonInteractive || false;
|
|
18
|
+
const success = await login(options.returnUrl, options.returnLabel, nonInteractive);
|
|
19
19
|
if (success) {
|
|
20
20
|
try {
|
|
21
21
|
await configureNpmrc();
|
package/dist/commands/chat.js
CHANGED
|
@@ -15,6 +15,7 @@ import path from 'path';
|
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, matchesAgent, getAgentName, } from '../lib/session-events.js';
|
|
17
17
|
import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
|
|
18
|
+
import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
|
|
18
19
|
import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
|
|
19
20
|
import { getWorkspaceId } from '../lib/guild-config.js';
|
|
20
21
|
import { ensureInteractiveStdin } from '../lib/stdin.js';
|
|
@@ -46,6 +47,35 @@ function fixListItemMarkdown(text) {
|
|
|
46
47
|
text = text.replace(/(?<![\\w])_([^_]+)_(?![\\w])/g, (_, content) => chalk.italic(content));
|
|
47
48
|
return text;
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Output the result of a --once mode session.
|
|
52
|
+
* Handles both JSON and human-readable output formats.
|
|
53
|
+
*/
|
|
54
|
+
async function outputOnceResult(sessionId, events, mode) {
|
|
55
|
+
if (mode === 'json') {
|
|
56
|
+
console.log(JSON.stringify({ session_id: sessionId, events }, null, 2));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const agentMessages = events.filter((e) => e.type === 'agent_notification_message');
|
|
60
|
+
if (agentMessages.length > 0) {
|
|
61
|
+
let messageContent = agentMessages[agentMessages.length - 1].content.data;
|
|
62
|
+
const trimmed = messageContent.trim();
|
|
63
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(trimmed);
|
|
66
|
+
if (typeof parsed === 'object' && parsed !== null && 'message' in parsed) {
|
|
67
|
+
messageContent = parsed.message;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// If parsing fails, use content as-is
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
75
|
+
console.log(rendered.trim());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
49
79
|
import { createSpinner as createAnimatedSpinner, createRandomSpinner, } from '../lib/spinners.js';
|
|
50
80
|
function CustomInput({ value, onChange, onSubmit, trackedTasksSize, setShowTaskPanel, isActive, }) {
|
|
51
81
|
useInput((input, key) => {
|
|
@@ -146,6 +176,16 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
|
|
|
146
176
|
initialDelay: 500,
|
|
147
177
|
maxDelay: 5000,
|
|
148
178
|
backoffMultiplier: 1.5,
|
|
179
|
+
shouldRetry: (error) => {
|
|
180
|
+
if (typeof error === 'object' && error !== null) {
|
|
181
|
+
const err = error;
|
|
182
|
+
return (err.code === 'ECONNREFUSED' ||
|
|
183
|
+
err.code === 'ETIMEDOUT' ||
|
|
184
|
+
err.code === 'ENOTFOUND' ||
|
|
185
|
+
err.code === 'ECONNABORTED');
|
|
186
|
+
}
|
|
187
|
+
return false;
|
|
188
|
+
},
|
|
149
189
|
});
|
|
150
190
|
setConnectedClient(client);
|
|
151
191
|
setConnectedSession(session);
|
|
@@ -382,7 +422,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
382
422
|
const sentMessages = useRef(new Set());
|
|
383
423
|
const pollInterval = useRef(null);
|
|
384
424
|
const spinnerInterval = useRef(null);
|
|
385
|
-
const
|
|
425
|
+
const lastEventIdRef = useRef(resumeEvents?.length ? resumeEvents[resumeEvents.length - 1].id : undefined);
|
|
386
426
|
const isPolling = useRef(false);
|
|
387
427
|
const receivedResponseSinceLastInput = useRef(false);
|
|
388
428
|
const firstMessageNotified = useRef(!!resumeEvents);
|
|
@@ -435,10 +475,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
435
475
|
return;
|
|
436
476
|
isPollingTasks.current = true;
|
|
437
477
|
try {
|
|
438
|
-
const
|
|
439
|
-
const tasksList = (Array.isArray(response)
|
|
440
|
-
? response
|
|
441
|
-
: response?.items || []);
|
|
478
|
+
const tasksList = await fetchTasks(client, session.id);
|
|
442
479
|
setTasks(tasksList);
|
|
443
480
|
}
|
|
444
481
|
catch (error) {
|
|
@@ -463,20 +500,14 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
463
500
|
if (isPolling.current)
|
|
464
501
|
return;
|
|
465
502
|
isPolling.current = true;
|
|
466
|
-
debug(`poll() called, session=${session.id}`);
|
|
503
|
+
debug(`poll() called, session=${session.id}, fromId=${lastEventIdRef.current}`);
|
|
467
504
|
try {
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
debug(`
|
|
472
|
-
const allEvents = (Array.isArray(response)
|
|
473
|
-
? response
|
|
474
|
-
: response?.items || []);
|
|
475
|
-
const newEvents = allEvents.slice(lastMessageCountRef.current);
|
|
476
|
-
// Debug: log event counts
|
|
477
|
-
debug(`Events: total=${allEvents.length}, lastCount=${lastMessageCountRef.current}, new=${newEvents.length}`);
|
|
505
|
+
const newEvents = await fetchEvents(client, session.id, {
|
|
506
|
+
fromId: lastEventIdRef.current,
|
|
507
|
+
});
|
|
508
|
+
debug(`Events: fromId=${lastEventIdRef.current}, new=${newEvents.length}`);
|
|
478
509
|
if (newEvents.length > 0) {
|
|
479
|
-
|
|
510
|
+
lastEventIdRef.current = newEvents[newEvents.length - 1].id;
|
|
480
511
|
for (const event of newEvents) {
|
|
481
512
|
// Track task from event if present
|
|
482
513
|
const taskInfo = event.task;
|
|
@@ -919,7 +950,7 @@ export function createChatCommand() {
|
|
|
919
950
|
.option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
|
|
920
951
|
.option('--no-splash', 'Skip the splash screen animation')
|
|
921
952
|
.option('--resume <session-id>', 'Resume an existing session')
|
|
922
|
-
.addHelpText('after', '\nTo chat with
|
|
953
|
+
.addHelpText('after', '\nTo chat with a local agent under development: guild agent chat')
|
|
923
954
|
.action(async (promptArgs, options) => {
|
|
924
955
|
const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
|
|
925
956
|
if (options.once) {
|
|
@@ -943,19 +974,15 @@ export function createChatCommand() {
|
|
|
943
974
|
const inactivityTimeoutMs = 300000; // 5 minutes
|
|
944
975
|
const pollIntervalMs = 500;
|
|
945
976
|
const maxInactivityAttempts = inactivityTimeoutMs / pollIntervalMs;
|
|
946
|
-
let
|
|
977
|
+
let lastSeenEventId;
|
|
978
|
+
const allEvents = [];
|
|
947
979
|
let inactivityCounter = 0;
|
|
948
980
|
while (true) {
|
|
949
981
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
: response?.items || []);
|
|
955
|
-
// Check if we got new events (reset inactivity counter)
|
|
956
|
-
if (events.length > lastMessageCount) {
|
|
957
|
-
// Debug: log all new events
|
|
958
|
-
const newEvents = events.slice(lastMessageCount);
|
|
982
|
+
const newEvents = await fetchEvents(client, session.id, {
|
|
983
|
+
fromId: lastSeenEventId,
|
|
984
|
+
});
|
|
985
|
+
if (newEvents.length > 0) {
|
|
959
986
|
for (const evt of newEvents) {
|
|
960
987
|
const taskInfo = evt.task;
|
|
961
988
|
if (taskInfo) {
|
|
@@ -965,8 +992,9 @@ export function createChatCommand() {
|
|
|
965
992
|
debug(`Event: ${evt.type} (no task info)`);
|
|
966
993
|
}
|
|
967
994
|
}
|
|
968
|
-
|
|
969
|
-
|
|
995
|
+
allEvents.push(...newEvents);
|
|
996
|
+
lastSeenEventId = newEvents[newEvents.length - 1].id;
|
|
997
|
+
inactivityCounter = 0;
|
|
970
998
|
}
|
|
971
999
|
else {
|
|
972
1000
|
inactivityCounter++;
|
|
@@ -976,93 +1004,27 @@ export function createChatCommand() {
|
|
|
976
1004
|
// For orchestrating agents like agent-builder that spawn child tasks, we need to
|
|
977
1005
|
// wait for the root agent's completion, not just any runtime_done event.
|
|
978
1006
|
const targetAgent = options.agent || 'assistant';
|
|
979
|
-
const hasRootTaskDone =
|
|
1007
|
+
const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' &&
|
|
980
1008
|
matchesAgent(e.task?.agent, targetAgent) &&
|
|
981
1009
|
!e.task?.parent_task_id // Root task has no parent
|
|
982
1010
|
);
|
|
983
1011
|
// Also check for agent_notification_message events from the root agent
|
|
984
|
-
const hasAgentMessage =
|
|
1012
|
+
const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
985
1013
|
matchesAgent(e.task?.agent, targetAgent) &&
|
|
986
1014
|
!e.task?.parent_task_id);
|
|
987
1015
|
// Check for a ui_prompt request... that ends the game.
|
|
988
|
-
const hasUIPromptMessage =
|
|
1016
|
+
const hasUIPromptMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
|
|
989
1017
|
e.task?.tool_name === 'ui_prompt');
|
|
990
1018
|
if (hasRootTaskDone || hasAgentMessage || hasUIPromptMessage) {
|
|
991
1019
|
debug(`Found completion event from root agent (${targetAgent}), exiting --once mode`);
|
|
992
|
-
|
|
993
|
-
if (options.mode === 'json') {
|
|
994
|
-
// JSON mode: output structured events
|
|
995
|
-
console.log(JSON.stringify({
|
|
996
|
-
session_id: session.id,
|
|
997
|
-
events: events,
|
|
998
|
-
}, null, 2));
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
// Human-readable mode (default): extract and render message content
|
|
1002
|
-
const agentMessages = events.filter((e) => e.type === 'agent_notification_message');
|
|
1003
|
-
if (agentMessages.length > 0) {
|
|
1004
|
-
// Get the last message content
|
|
1005
|
-
let messageContent = agentMessages[agentMessages.length - 1].content.data;
|
|
1006
|
-
// Handle JSON {"message": "..."} wrapper
|
|
1007
|
-
const trimmed = messageContent.trim();
|
|
1008
|
-
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
1009
|
-
try {
|
|
1010
|
-
const parsed = JSON.parse(trimmed);
|
|
1011
|
-
if (typeof parsed === 'object' &&
|
|
1012
|
-
parsed !== null &&
|
|
1013
|
-
'message' in parsed) {
|
|
1014
|
-
messageContent = parsed.message;
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
catch {
|
|
1018
|
-
// If parsing fails, use content as-is
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
// Render markdown and output
|
|
1022
|
-
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
1023
|
-
console.log(rendered.trim());
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1020
|
+
await outputOnceResult(session.id, allEvents, options.mode);
|
|
1026
1021
|
process.exit(0);
|
|
1027
1022
|
}
|
|
1028
1023
|
// Timeout if no activity for too long
|
|
1029
1024
|
if (inactivityCounter >= maxInactivityAttempts) {
|
|
1030
1025
|
debug(`Inactivity timeout reached (${maxInactivityAttempts} attempts with no new events)`);
|
|
1031
|
-
debug(`Exiting with ${
|
|
1032
|
-
|
|
1033
|
-
if (options.mode === 'json') {
|
|
1034
|
-
// JSON mode: output structured events
|
|
1035
|
-
console.log(JSON.stringify({
|
|
1036
|
-
session_id: session.id,
|
|
1037
|
-
events: events,
|
|
1038
|
-
}, null, 2));
|
|
1039
|
-
}
|
|
1040
|
-
else {
|
|
1041
|
-
// Human-readable mode (default): extract and render message content
|
|
1042
|
-
const agentMessages = events.filter((e) => e.type === 'agent_notification_message');
|
|
1043
|
-
if (agentMessages.length > 0) {
|
|
1044
|
-
// Get the last message content
|
|
1045
|
-
let messageContent = agentMessages[agentMessages.length - 1].content.data;
|
|
1046
|
-
// Handle JSON {"message": "..."} wrapper
|
|
1047
|
-
const trimmed = messageContent.trim();
|
|
1048
|
-
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
1049
|
-
try {
|
|
1050
|
-
const parsed = JSON.parse(trimmed);
|
|
1051
|
-
if (typeof parsed === 'object' &&
|
|
1052
|
-
parsed !== null &&
|
|
1053
|
-
'message' in parsed) {
|
|
1054
|
-
messageContent = parsed.message;
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
catch {
|
|
1058
|
-
// If parsing fails, use content as-is
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
// Render markdown and output
|
|
1062
|
-
const rendered = fixListItemMarkdown(await marked(messageContent));
|
|
1063
|
-
console.log(rendered.trim());
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1026
|
+
debug(`Exiting with ${allEvents.length} events total`);
|
|
1027
|
+
await outputOnceResult(session.id, allEvents, options.mode);
|
|
1066
1028
|
process.exit(0);
|
|
1067
1029
|
}
|
|
1068
1030
|
}
|
|
@@ -35,7 +35,7 @@ export function createCredentialsEndpointListCommand() {
|
|
|
35
35
|
if (options.search) {
|
|
36
36
|
params.append('search', options.search);
|
|
37
37
|
}
|
|
38
|
-
const response = await client.get(`/credentials/${credentialId}/endpoints
|
|
38
|
+
const response = await client.get(`/credentials/${credentialId}/endpoints?${params.toString()}`);
|
|
39
39
|
if (getOutputMode() === 'json') {
|
|
40
40
|
console.log(JSON.stringify(response, null, 2));
|
|
41
41
|
}
|