@guildai/cli 0.9.0 → 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 +92 -34
- 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/mcp.js +1 -1
- package/dist/commands/session/events.js +16 -7
- 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 +53 -6
- package/dist/lib/api-types.d.ts +1 -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/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/workspace-helpers.d.ts +20 -0
- package/dist/lib/workspace-helpers.js +49 -0
- package/dist/mcp/tools.js +8 -52
- package/docs/skills/agent-dev.md +191 -128
- package/package.json +2 -1
package/dist/mcp/tools.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import { pollForResponse as pollForSessionResponse } from '../lib/session-polling.js';
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Constants
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
|
-
const POLL_INTERVAL_MS = 2000;
|
|
8
8
|
const POLL_TIMEOUT_MS = 120_000;
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Helpers
|
|
@@ -14,55 +14,6 @@ function debugLog(debug, message) {
|
|
|
14
14
|
process.stderr.write(`[guild-mcp] ${message}\n`);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Poll session events until runtime_done or runtime_error.
|
|
19
|
-
* Returns collected agent messages.
|
|
20
|
-
*/
|
|
21
|
-
async function pollForResponse(apiClient, sessionId, debug) {
|
|
22
|
-
const startTime = Date.now();
|
|
23
|
-
const messages = [];
|
|
24
|
-
let lastEventId;
|
|
25
|
-
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
26
|
-
try {
|
|
27
|
-
let url = `/sessions/${sessionId}/events`;
|
|
28
|
-
if (lastEventId) {
|
|
29
|
-
url += `?from_id=${lastEventId}`;
|
|
30
|
-
}
|
|
31
|
-
const response = await apiClient.get(url);
|
|
32
|
-
const events = response.events || [];
|
|
33
|
-
for (const event of events) {
|
|
34
|
-
debugLog(debug, `Event: ${event.event_type}`);
|
|
35
|
-
if (event.event_type === 'agent_notification_message') {
|
|
36
|
-
const data = extractEventText(event);
|
|
37
|
-
if (data) {
|
|
38
|
-
messages.push(data);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (event.event_type === 'runtime_error') {
|
|
42
|
-
const errorText = extractEventText(event);
|
|
43
|
-
if (errorText) {
|
|
44
|
-
return `Error: ${errorText}`;
|
|
45
|
-
}
|
|
46
|
-
return 'Error: Agent encountered an error';
|
|
47
|
-
}
|
|
48
|
-
if (event.event_type === 'runtime_done') {
|
|
49
|
-
debugLog(debug, 'Runtime done');
|
|
50
|
-
return messages.join('\n\n') || 'Agent completed without output.';
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (events.length > 0) {
|
|
54
|
-
lastEventId = events[events.length - 1].id;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
debugLog(debug, `Poll error: ${String(error)}`);
|
|
59
|
-
}
|
|
60
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
61
|
-
}
|
|
62
|
-
return messages.length > 0
|
|
63
|
-
? messages.join('\n\n') + '\n\n(Timed out waiting for completion)'
|
|
64
|
-
: 'Timed out waiting for agent response.';
|
|
65
|
-
}
|
|
66
17
|
function extractEventText(event) {
|
|
67
18
|
if (!event.content)
|
|
68
19
|
return undefined;
|
|
@@ -74,6 +25,11 @@ function extractEventText(event) {
|
|
|
74
25
|
}
|
|
75
26
|
return undefined;
|
|
76
27
|
}
|
|
28
|
+
async function pollForMcpResponse(apiClient, sessionId, debug) {
|
|
29
|
+
debugLog(debug, `Polling response for session ${sessionId}`);
|
|
30
|
+
const result = await pollForSessionResponse(apiClient, sessionId, undefined, POLL_TIMEOUT_MS);
|
|
31
|
+
return result.response || 'Agent completed without output.';
|
|
32
|
+
}
|
|
77
33
|
function errText(action, error) {
|
|
78
34
|
return `Failed to ${action}: ${error instanceof Error ? error.message : String(error)}`;
|
|
79
35
|
}
|
|
@@ -504,7 +460,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
|
|
|
504
460
|
}
|
|
505
461
|
const session = await apiClient.post(`/workspaces/${wsId}/sessions`, body);
|
|
506
462
|
debugLog(debug, `Session created: ${session.id}`);
|
|
507
|
-
const response = await
|
|
463
|
+
const response = await pollForMcpResponse(apiClient, session.id, debug);
|
|
508
464
|
return {
|
|
509
465
|
content: [{ type: 'text', text: response }],
|
|
510
466
|
};
|
|
@@ -526,7 +482,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
|
|
|
526
482
|
event_type: 'user_message',
|
|
527
483
|
content: { type: 'text', data: message },
|
|
528
484
|
});
|
|
529
|
-
const response = await
|
|
485
|
+
const response = await pollForMcpResponse(apiClient, session_id, debug);
|
|
530
486
|
return {
|
|
531
487
|
content: [{ type: 'text', text: response }],
|
|
532
488
|
};
|
package/docs/skills/agent-dev.md
CHANGED
|
@@ -5,7 +5,17 @@ description: Local agent development using the Guild CLI. Activated when user me
|
|
|
5
5
|
|
|
6
6
|
# Guild Agent Development
|
|
7
7
|
|
|
8
|
-
Build agents for Guild using the CLI.
|
|
8
|
+
Build agents for Guild using the CLI.
|
|
9
|
+
|
|
10
|
+
The Guild CLI is self-documenting:
|
|
11
|
+
|
|
12
|
+
- You may type `--help` after any command to learn more about how to use it
|
|
13
|
+
- You can type `guild --help` to learn about common options available across all commands.
|
|
14
|
+
- If you discover a discrepancy between these instructions and the Guild CLI's internal documentation, assume that the Guild CLI's version is correct.
|
|
15
|
+
|
|
16
|
+
When using the CLI, prefer to explicitly provide arguments as defaults can sometimes be unintuitive
|
|
17
|
+
|
|
18
|
+
**Always use the Guild CLI for agent operations - unless specified below, never use raw git commands.**
|
|
9
19
|
|
|
10
20
|
## MCP vs CLI
|
|
11
21
|
|
|
@@ -36,24 +46,34 @@ guild setup
|
|
|
36
46
|
guild setup --claude-md
|
|
37
47
|
```
|
|
38
48
|
|
|
39
|
-
### Creating
|
|
49
|
+
### Creating a New Agent
|
|
40
50
|
|
|
41
51
|
```bash
|
|
42
52
|
# Create and initialize a new agent (interactive - prompts for name and template)
|
|
43
53
|
guild agent init
|
|
44
54
|
|
|
45
|
-
# Create with specific name and template
|
|
46
|
-
guild agent init --name my-agent --template LLM
|
|
47
|
-
guild agent init --name my-agent --template AUTO_MANAGED_STATE
|
|
48
|
-
guild agent init --name my-agent --template BLANK
|
|
55
|
+
# Create with specific name and template. You must specify an owner account.
|
|
56
|
+
guild agent init --owner account-name --name my-agent --template LLM
|
|
57
|
+
guild agent init --owner account-name --name my-agent --template AUTO_MANAGED_STATE
|
|
58
|
+
guild agent init --owner account-name --name my-agent --template BLANK
|
|
49
59
|
|
|
50
60
|
# Initialize with fork of existing agent
|
|
51
|
-
guild agent init --fork owner
|
|
61
|
+
guild agent init --fork owner~agent-name
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
IMPORTANT! When creating a new agent, it's important to understand who will be the owner of the agent; typically this is likely to be an organization to which the user belongs, or the user's own private account. If unsure, ask for clarification!
|
|
52
65
|
|
|
66
|
+
IMPORTANT! By default `guild agent init` will create an agent in the current working directory. Use the `--directory` option to specify an alternate location.
|
|
67
|
+
|
|
68
|
+
### Modifying an Existing Agent
|
|
69
|
+
|
|
70
|
+
```bash
|
|
53
71
|
# Clone to work on existing agent
|
|
54
|
-
guild agent clone owner
|
|
72
|
+
guild agent clone owner~agent-name
|
|
55
73
|
```
|
|
56
74
|
|
|
75
|
+
IMPORTANT! By default `guild agent clone` will create an agent in the `<agent-name>` directory. Use the `--directory` option to specify an alternate location.
|
|
76
|
+
|
|
57
77
|
### Syncing and Saving
|
|
58
78
|
|
|
59
79
|
Git owns the working tree, Guild owns the remote. Use normal git commands to stage and commit, then `guild agent save` to push and create a version.
|
|
@@ -76,8 +96,28 @@ guild agent save --message "Fix bug" --wait
|
|
|
76
96
|
guild agent save -A --message "Release v1.0" --wait --publish
|
|
77
97
|
```
|
|
78
98
|
|
|
99
|
+
### Building
|
|
100
|
+
|
|
101
|
+
An agent is just an `npm` TypeScript project.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Install the agent's dependencies
|
|
105
|
+
npm install
|
|
106
|
+
|
|
107
|
+
# Build the agent's code
|
|
108
|
+
npm run build
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
IMPORTANT! You must be logged in to guild to `npm install` dependencies; run `guild auth login` if you get a "not authorized" error when running `npm install`.
|
|
112
|
+
|
|
113
|
+
TIP. Once you've identified the integrations upon which your agent will depend, update `package.json` and run `npm install` _before_ attempting to write any code that uses the integration. You're much less likely to make a poor assumption about what tools exist or how you must use them.
|
|
114
|
+
|
|
115
|
+
IMPORTANT! Always make sure that your agent builds correctly before testing.
|
|
116
|
+
|
|
79
117
|
### Testing
|
|
80
118
|
|
|
119
|
+
An agent must be tested using the `guild` tool: this will upload the agent to the server runtime environment where the agent will operate.
|
|
120
|
+
|
|
81
121
|
```bash
|
|
82
122
|
# Interactive test session
|
|
83
123
|
guild agent test
|
|
@@ -99,6 +139,8 @@ EOF
|
|
|
99
139
|
guild agent chat "Hello, can you help me?"
|
|
100
140
|
```
|
|
101
141
|
|
|
142
|
+
IMPORTANT! Most integrations require that you provide credentials to connect them appropriately. Choose a workspace (e.g. with `guild workspace list`) that has appropriate credentials installed for each integration that your agent uses. If you cannot find one, instruct the user of your conundrum and ask for advice; specifically, tell them "I cannot test the agent without access to a workspace that has access to all of the integrations that this agent requires. I can't seem to find one. If I've overlooked a workspace with the correct credentials, please let me know which one to use. Otherwise, please configure a workspace appropriately and let me know its name when it is ready."
|
|
143
|
+
|
|
102
144
|
### Chatting with Agents
|
|
103
145
|
|
|
104
146
|
```bash
|
|
@@ -157,70 +199,13 @@ To chat with the agent you are developing locally, use `guild agent chat` from w
|
|
|
157
199
|
|
|
158
200
|
The SDK core comes from `@guildai/agents-sdk`. Service tools are in separate `@guildai-services/*` packages.
|
|
159
201
|
|
|
160
|
-
|
|
161
|
-
// Agent factories
|
|
162
|
-
import { agent, llmAgent } from '@guildai/agents-sdk';
|
|
163
|
-
|
|
164
|
-
// Types
|
|
165
|
-
import type {
|
|
166
|
-
Task,
|
|
167
|
-
AgentResult,
|
|
168
|
-
TypedToolResult,
|
|
169
|
-
TypedToolError,
|
|
170
|
-
} from '@guildai/agents-sdk';
|
|
202
|
+
You can inspect this package locally after you `npm install` the agent's dependencies.
|
|
171
203
|
|
|
172
|
-
|
|
173
|
-
import { ask, output, callTools } from '@guildai/agents-sdk';
|
|
204
|
+
You can review [online documentation for the API](https://docs.guild.ai/guide/sdk-introduction): this includes guides and references.
|
|
174
205
|
|
|
175
|
-
|
|
176
|
-
import { guildTools, userInterfaceTools } from '@guildai/agents-sdk';
|
|
206
|
+
### Common Integrations
|
|
177
207
|
|
|
178
|
-
|
|
179
|
-
import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
|
|
180
|
-
import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
|
|
181
|
-
import { confluenceTools } from '@guildai-services/guildai~confluence';
|
|
182
|
-
import { cypressTools } from '@guildai-services/guildai~cypress';
|
|
183
|
-
import { figmaTools } from '@guildai-services/guildai~figma';
|
|
184
|
-
import { gitHubTools } from '@guildai-services/guildai~github';
|
|
185
|
-
import { googleComputeTools } from '@guildai-services/guildai~google-compute';
|
|
186
|
-
import { googleLoggingTools } from '@guildai-services/guildai~google-logging';
|
|
187
|
-
import { jiraTools } from '@guildai-services/guildai~jira';
|
|
188
|
-
import { linearTools } from '@guildai-services/guildai~linear';
|
|
189
|
-
import { newrelicTools } from '@guildai-services/guildai~newrelic';
|
|
190
|
-
import { pipedreamTools } from '@guildai-services/guildai~pipedream';
|
|
191
|
-
import { slackTools } from '@guildai-services/guildai~slack';
|
|
192
|
-
import { testrailTools } from '@guildai-services/guildai~testrail';
|
|
193
|
-
import { zendeskTools } from '@guildai-services/guildai~zendesk';
|
|
194
|
-
|
|
195
|
-
// Utilities
|
|
196
|
-
import {
|
|
197
|
-
pick,
|
|
198
|
-
omit,
|
|
199
|
-
guildAgentTool,
|
|
200
|
-
progressLogNotifyEvent,
|
|
201
|
-
} from '@guildai/agents-sdk';
|
|
202
|
-
|
|
203
|
-
// Calling another agent as a tool (import from its /tool sub-package)
|
|
204
|
-
import subagentTool from '@guildai/owner~agent-name/tool';
|
|
205
|
-
|
|
206
|
-
// Coding agent (container-based code execution)
|
|
207
|
-
import { ExperimentalCodingTools as codingTools } from '@guildai-services/guildai~experimental-coding';
|
|
208
|
-
import {
|
|
209
|
-
CONTAINER_IMAGE,
|
|
210
|
-
codingAgentToolsFrom,
|
|
211
|
-
} from '@guildai/guildai~sys-experimental-coding';
|
|
212
|
-
import codingAgentTool from '@guildai/guildai~sys-experimental-coding/tool';
|
|
213
|
-
|
|
214
|
-
// Advanced (for compiled agents with LLM tool loops)
|
|
215
|
-
import { delegatedCallsOf, asToolResultContent } from '@guildai/agents-sdk';
|
|
216
|
-
|
|
217
|
-
// Zod (provided by runtime, do NOT add to dependencies)
|
|
218
|
-
import { z } from 'zod';
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Service Packages Table
|
|
222
|
-
|
|
223
|
-
Service tools are in separate `@guildai-services/*` packages. The runtime resolves them automatically.
|
|
208
|
+
An _integration_ provides the agent the tools it needs to interact with the world. An integration's API is made available via a separate `@guildai-services/*` package. To use an integration, you add it as a dependency in `package.json` and then `import` it in the agent's TypeScript code. Here are some examples:
|
|
224
209
|
|
|
225
210
|
| Service | Package | Export | Tool Name Prefix |
|
|
226
211
|
| -------------- | ------------------------------------------ | -------------------- | ----------------- |
|
|
@@ -242,9 +227,15 @@ Service tools are in separate `@guildai-services/*` packages. The runtime resolv
|
|
|
242
227
|
| User Interface | `@guildai/agents-sdk` | `userInterfaceTools` | `ui_` |
|
|
243
228
|
| Zendesk | `@guildai-services/guildai~zendesk` | `zendeskTools` | `zendesk_` |
|
|
244
229
|
|
|
230
|
+
You can use the CLI to search for a full list of integrations using `guild integration list`.
|
|
231
|
+
|
|
245
232
|
### Tool Access via `task.tools.*`
|
|
246
233
|
|
|
247
|
-
|
|
234
|
+
To make use of an integration, you must:
|
|
235
|
+
|
|
236
|
+
1. Import the integration's tools
|
|
237
|
+
2. Include the relevant tools in the agent's tool set. This will automatically create functions you can call on the `task.tools` object.
|
|
238
|
+
3. Invoke a tool using the `task.tools.<toolName>(args)`.
|
|
248
239
|
|
|
249
240
|
```typescript
|
|
250
241
|
// GitHub
|
|
@@ -293,25 +284,41 @@ await task.tools.guild_credentials_request({ service: 'GITHUB' });
|
|
|
293
284
|
|
|
294
285
|
Three patterns, ordered by simplicity:
|
|
295
286
|
|
|
287
|
+
1. LLM agent - just a prompt and tools
|
|
288
|
+
2. Coded agent - automatic state management
|
|
289
|
+
3. Coded agent - explicit state management
|
|
290
|
+
|
|
291
|
+
Considerations:
|
|
292
|
+
|
|
293
|
+
- Maintenance. An LLM agent will be easiest for a human to understand and maintain. A coded agent requires expert knowledge, and one with explicit state management results in a state machine implementation that will be difficult even for an expert programmer to maintain.
|
|
294
|
+
- Cost. An LLM agent requires inference to operate and so incurs a high per-invocation cost. If a task is sufficiently simple, a coded agent may be preferable and will certainly be cheaper to operate.
|
|
295
|
+
- Latency. An LLM agent requires inference to operate and so can incur a non-trivial latency.
|
|
296
|
+
- Control. An LLM agent is ultimately stochastic. If precise, fine-grained control is required, it may be easier to achieve with a coded agent than through natural language instructions.
|
|
297
|
+
- Reasoning and decision making. An LLM agent allows for nuanced judgment calls; a coded agent requires strict rules.
|
|
298
|
+
|
|
299
|
+
TIP. A coded agent can make use of the `task.llm.generateText` call to use an LLM as a subroutine: this may be a reasonable trade-off to make.
|
|
300
|
+
|
|
301
|
+
A detailed description and example of each is provided below.
|
|
302
|
+
|
|
296
303
|
### 1. LLM Agent (`llmAgent()`) — Simplest
|
|
297
304
|
|
|
298
|
-
For conversational/prompt-driven agents where the LLM IS the logic.
|
|
305
|
+
For conversational/prompt-driven agents where the LLM IS the logic.
|
|
299
306
|
|
|
300
307
|
```typescript
|
|
301
|
-
import {
|
|
308
|
+
import { llmAgent, pick } from '@guildai/agents-sdk';
|
|
302
309
|
import { gitHubTools } from '@guildai-services/guildai~github';
|
|
303
310
|
|
|
304
311
|
export default llmAgent({
|
|
305
312
|
description: 'Helps users with GitHub questions',
|
|
306
313
|
tools: {
|
|
307
314
|
...pick(gitHubTools, ['github_issues_list_for_repo', 'github_issues_get']),
|
|
308
|
-
...guildTools, // ALWAYS spread fully — never pick() from guildTools
|
|
309
315
|
},
|
|
310
316
|
systemPrompt: `
|
|
311
317
|
You are a helpful assistant that answers questions about GitHub repositories.
|
|
312
318
|
Use the GitHub tools to look up information when asked.
|
|
313
319
|
`,
|
|
314
|
-
mode: '
|
|
320
|
+
mode: 'one-shot', // "one-shot" (default) or "multi-turn"
|
|
321
|
+
useWorkspaceAgents: false,
|
|
315
322
|
});
|
|
316
323
|
```
|
|
317
324
|
|
|
@@ -320,7 +327,7 @@ export default llmAgent({
|
|
|
320
327
|
By default, `llmAgent` accepts `{ type: "text", text: string }` and sends `text` as the first user message. Override this for structured inputs:
|
|
321
328
|
|
|
322
329
|
```typescript
|
|
323
|
-
import {
|
|
330
|
+
import { llmAgent, pick } from '@guildai/agents-sdk';
|
|
324
331
|
import { gitHubTools } from '@guildai-services/guildai~github';
|
|
325
332
|
import { z } from 'zod';
|
|
326
333
|
|
|
@@ -333,20 +340,47 @@ export default llmAgent({
|
|
|
333
340
|
inputTemplate: 'Analyze repo {{repo}} on branch {{branch}}',
|
|
334
341
|
tools: {
|
|
335
342
|
...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
|
|
336
|
-
...guildTools,
|
|
337
343
|
},
|
|
338
344
|
systemPrompt: 'You analyze GitHub repositories.',
|
|
345
|
+
mode: 'one-shot', // "one-shot" (default) or "multi-turn"
|
|
346
|
+
useWorkspaceAgents: false,
|
|
339
347
|
});
|
|
340
348
|
```
|
|
341
349
|
|
|
342
350
|
- `inputSchema` — Zod schema for the agent's input (replaces the default `{ type: "text", text: string }`)
|
|
343
351
|
- `inputTemplate` — Mustache-style template that renders the input as the initial LLM user message (default: `"{{text}}"`)
|
|
344
352
|
|
|
345
|
-
**`
|
|
353
|
+
**`mode`: `one-shot` versus `multi-turn`**
|
|
354
|
+
|
|
355
|
+
An LLM agent whose `mode` is `one-shot` will run for a single turn and then return its output as a result. The "single turn" may include rounds of tool calls, thinking, etc. but once its computation is complete it will terminate and control will be restored to the agent that invoked it. This appropriate for most agents.
|
|
356
|
+
|
|
357
|
+
TIP: use this agent whenever you need fully autonomous operation without user interaction.
|
|
358
|
+
|
|
359
|
+
An LLM agent whose `mode` is `multi-turn` does not automatically return control to its caller and will instead proceed interactively -- i.e., prompting the user -- until a specific termination criteria is defined. You must specify this exact termination criteria and instruct the agent to call the `__submit__` tool once that criteria is achieved. This will end the interactive loop and restore control to the caller.
|
|
360
|
+
|
|
361
|
+
WARNING: this is **rarely** the mode that an agent should operate in: it is **only** useful for agents that are guaranteed to be invoked interactively. USE WITH CAUTION!
|
|
362
|
+
|
|
363
|
+
**`useWorkspaceAgents`**
|
|
364
|
+
|
|
365
|
+
When `true`, the agent dynamically discovers and can call other agents installed in the workspace (like Guild's built-in assistant does). Defaults to `true`, but most agents should explicitly set this to `false` unless they specifically need dynamic agent discovery. For deterministic orchestration, use `guildAgentTool()` to delegate to specific agents instead.
|
|
366
|
+
|
|
367
|
+
IMPORTANT! Understand whether or not your agent will operate in an interactive environment; i.e., one where a user will be able to directly chat with the agent. (An agent that is activated from a trigger -- i.e., a webhook or a timer -- is considered **non-interactive** since no user is present.) If the agent is non-interactive, **NEVER** make use of the `ui_prompt` tool: this tool attempts to solicit a response from an interactive user who will not be available.
|
|
346
368
|
|
|
347
|
-
|
|
369
|
+
**Tools**
|
|
348
370
|
|
|
349
|
-
|
|
371
|
+
- Provide an LLM agent with the minimum set of tools that it needs to accomplish its task.
|
|
372
|
+
- Many models have strict limits on the number of tools that are allowed.
|
|
373
|
+
- Unnecessary tools waste context space and risk confusion.
|
|
374
|
+
- Use the `pick` utility to select multiple tools from an integration in a succinct way.
|
|
375
|
+
- NEVER spread an entire toolset (e.g. `...gitHubTools`) in an LLM agent: this is bad form, and you always want to look good.
|
|
376
|
+
|
|
377
|
+
### 2. Code Agent with Automatic State Management
|
|
378
|
+
|
|
379
|
+
An agent that is implemented as a simple TypeScript `run` function. Ideal for most situations that require coding.
|
|
380
|
+
|
|
381
|
+
- Implement a single `run()` function that accepts the input and returns the output: the function is implemented in a natural procedural style that is easy to understand and maintain.
|
|
382
|
+
- Requires the `"use agent"` directive at top of the file: this triggers a compilation step that converts the TypeScript code into a resumable state machine that can be suspended at for long-running tasks.
|
|
383
|
+
- Use `task.tools.*` for all integration tool calls.
|
|
350
384
|
|
|
351
385
|
```typescript
|
|
352
386
|
'use agent';
|
|
@@ -377,7 +411,6 @@ type Output = z.infer<typeof outputSchema>;
|
|
|
377
411
|
|
|
378
412
|
const tools = {
|
|
379
413
|
...pick(gitHubTools, ['github_search_issues_and_pull_requests']),
|
|
380
|
-
...guildTools, // ALWAYS spread fully — never pick() from guildTools
|
|
381
414
|
...userInterfaceTools,
|
|
382
415
|
};
|
|
383
416
|
|
|
@@ -411,16 +444,18 @@ export default agent({
|
|
|
411
444
|
});
|
|
412
445
|
```
|
|
413
446
|
|
|
414
|
-
|
|
447
|
+
### 3. Coded agent with Self-Managed State
|
|
415
448
|
|
|
416
|
-
|
|
417
|
-
- The runtime handles continuations — you can `await` tool calls inline
|
|
418
|
-
- `"use agent"` directive at top of file is optional (the Babel compiler recognizes it but strips it)
|
|
419
|
-
- No `identifier` field needed
|
|
449
|
+
For explicit state control.
|
|
420
450
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
451
|
+
- Implement the `start()` and `onToolResults()` functions: these return `AgentResult<Output, Tools>`
|
|
452
|
+
- `return ask(prompt)` — sends a `ui_prompt` tool call to get user input
|
|
453
|
+
- `return output(value)` — wraps your output as `{ type: "output", output: value }`
|
|
454
|
+
- `return callTools([...])` — requests the runtime to execute tool calls
|
|
455
|
+
- `task.save(state)` / `task.restore()` persist state between invocations: you must explicitly specify the schema of the state.
|
|
456
|
+
- May be necessary for more elaborate situations; notably, invoking multiple agents or tools in parallel.
|
|
457
|
+
- Generally results in a much more complicated program that can be difficult to understand and maintain; avoid if possible.
|
|
458
|
+
- No `"use agent"` directive since there is no compilation step.
|
|
424
459
|
|
|
425
460
|
```typescript
|
|
426
461
|
import {
|
|
@@ -488,19 +523,11 @@ export default agent({
|
|
|
488
523
|
});
|
|
489
524
|
```
|
|
490
525
|
|
|
491
|
-
**Key points:**
|
|
492
|
-
|
|
493
|
-
- `start()` and `onToolResults()` return `AgentResult<Output, Tools>`
|
|
494
|
-
- `ask(prompt)` — sends a `ui_prompt` tool call to get user input
|
|
495
|
-
- `output(value)` — wraps your output as `{ type: "output", output: value }`
|
|
496
|
-
- `callTools([...])` — requests the runtime to execute tool calls
|
|
497
|
-
- `task.save(state)` / `task.restore()` — persist state between invocations
|
|
498
|
-
|
|
499
526
|
---
|
|
500
527
|
|
|
501
528
|
## Agent-to-Agent Delegation
|
|
502
529
|
|
|
503
|
-
Every published agent exposes a `/tool` sub-package that
|
|
530
|
+
Every published agent exposes a `/tool` sub-package that allows it to be used just like any other callable tool. Import it and add it to your tools object — the runtime handles dispatch.
|
|
504
531
|
|
|
505
532
|
### Using a published agent as a tool (preferred)
|
|
506
533
|
|
|
@@ -580,7 +607,7 @@ export default agent({
|
|
|
580
607
|
|
|
581
608
|
### Using the Coding Agent
|
|
582
609
|
|
|
583
|
-
The coding agent runs instructions inside
|
|
610
|
+
The coding agent runs instructions inside a dedicated (but isolated) virtual machine. The agent has full access to the computer: use it when your agent needs to read/write files, run shell commands, or work with a cloned repository.
|
|
584
611
|
|
|
585
612
|
```typescript
|
|
586
613
|
import { ExperimentalCodingTools as codingTools } from '@guildai-services/guildai~experimental-coding';
|
|
@@ -672,6 +699,8 @@ Using `session_id` preserves the container's working directory, environment vari
|
|
|
672
699
|
|
|
673
700
|
**GitHub tools for common container tasks:**
|
|
674
701
|
|
|
702
|
+
The GitHub API provides primitives that must be composed in the coding container for common operations. If you need the agent to perform any of the following tasks, be sure to include the tools upon which the task depends. Use the table below as a reference.
|
|
703
|
+
|
|
675
704
|
| Task | Required Tools |
|
|
676
705
|
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
677
706
|
| Clone a repository | `github_repos_download_zipball_archive` |
|
|
@@ -684,7 +713,9 @@ Using `session_id` preserves the container's working directory, environment vari
|
|
|
684
713
|
|
|
685
714
|
---
|
|
686
715
|
|
|
687
|
-
## Advanced
|
|
716
|
+
## Advanced Topics
|
|
717
|
+
|
|
718
|
+
### Example: Using an LLM from within a coded agent
|
|
688
719
|
|
|
689
720
|
For agents that need an LLM tool-calling loop with fine-grained control over which tool calls the LLM handles vs which get delegated to the runtime:
|
|
690
721
|
|
|
@@ -755,10 +786,9 @@ async function onToolResults(
|
|
|
755
786
|
- `delegatedCallsOf<Tools>(content)` — extracts unexecuted tool calls from `generateText` results that need runtime delegation
|
|
756
787
|
- `asToolResultContent(results)` — converts `TypedToolResult[]` into LLM message format for conversation history
|
|
757
788
|
|
|
758
|
-
### Slack-Specific Patterns
|
|
789
|
+
### Example: Slack-Specific Patterns
|
|
759
790
|
|
|
760
|
-
When posting to Slack, convert markdown to Slack's mrkdwn format. Use an inline converter
|
|
761
|
-
(`slackify-markdown` is CJS and breaks in the ESM agent runtime):
|
|
791
|
+
When posting to Slack, convert markdown to Slack's mrkdwn format. Use an inline converter (`slackify-markdown` is CJS and breaks in the ESM agent runtime):
|
|
762
792
|
|
|
763
793
|
```typescript
|
|
764
794
|
// Simple markdown-to-Slack-mrkdwn converter (inline — do NOT use slackify-markdown)
|
|
@@ -793,7 +823,7 @@ await task.tools.slack_chat_post_message({
|
|
|
793
823
|
|
|
794
824
|
### Discovering Available Tools
|
|
795
825
|
|
|
796
|
-
**
|
|
826
|
+
**NEVER attempt to guess a tool's name.** Use the Guild CLI to discover what tools exist for each integration:
|
|
797
827
|
|
|
798
828
|
```bash
|
|
799
829
|
# List all available integrations
|
|
@@ -806,7 +836,10 @@ guild integration operation list guildai~github
|
|
|
806
836
|
guild integration operation list guildai~github --json
|
|
807
837
|
```
|
|
808
838
|
|
|
809
|
-
Tool names follow the pattern `{service_prefix}_{operation_name}` (e.g., `github_pulls_get`).
|
|
839
|
+
Tool names follow the pattern `{service_prefix}_{operation_name}` (e.g., `github_pulls_get`).
|
|
840
|
+
|
|
841
|
+
- Check the service packages table above for common service prefixes.
|
|
842
|
+
- For an integration not listed above (e.g., `@guildai-services/some-owner~some-integration`), the service prefix will be the _integration name_ (e.g., `some-integration`) with any hyphens replaced by underscores (e.g., `some_integration`).
|
|
810
843
|
|
|
811
844
|
Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
|
|
812
845
|
|
|
@@ -814,41 +847,69 @@ Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
|
|
|
814
847
|
// Include only specific tools
|
|
815
848
|
const tools = {
|
|
816
849
|
...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
|
|
817
|
-
...guildTools,
|
|
818
850
|
};
|
|
819
851
|
|
|
820
852
|
// Include all tools except specific ones
|
|
821
853
|
const tools = {
|
|
822
854
|
...omit(gitHubTools, ['github_repos_delete', 'github_repos_update']),
|
|
823
|
-
...guildTools,
|
|
824
855
|
};
|
|
825
856
|
```
|
|
826
857
|
|
|
827
|
-
###
|
|
858
|
+
### Adding an integration for your agent
|
|
828
859
|
|
|
829
|
-
|
|
860
|
+
To add an integration for you agent, use `npm install`:
|
|
830
861
|
|
|
831
|
-
|
|
862
|
+
```bash
|
|
863
|
+
# Add an integration
|
|
864
|
+
npm install --save @guildai-services/some-owner~some-integration
|
|
832
865
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
866
|
+
# Add an agent as a tool
|
|
867
|
+
npm install --save @guildai/some-owner~some-agent
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
The integration or agent should be added as a dependency (i.e., _not_ a `devDependency`)
|
|
871
|
+
|
|
872
|
+
Once you've done that, import the agent into your agent as you would any other package:
|
|
836
873
|
|
|
837
874
|
```typescript
|
|
838
|
-
//
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
875
|
+
// An integration. This object will be a ToolSet from which you should select the tools you need.
|
|
876
|
+
import { SomeIntegration } from '@guildai-services/some-owner~some-integration';
|
|
877
|
+
|
|
878
|
+
// An agent... note that it's imported from the `/tool` sub-package as the default export
|
|
879
|
+
import SomeAgent from '@guildai/some-onwer~some-agent/tool';
|
|
843
880
|
|
|
844
|
-
//
|
|
881
|
+
// Include the relevant tools in your agent's tool object.
|
|
845
882
|
const tools = {
|
|
846
|
-
|
|
847
|
-
...
|
|
883
|
+
// The integration tools.
|
|
884
|
+
...pick(SomeIntegration, ['some_integration_tool_one', 'some_integration_tool_two']),
|
|
885
|
+
|
|
886
|
+
// The agent tool
|
|
887
|
+
someAgentTool: SomeAgent,
|
|
848
888
|
};
|
|
849
889
|
```
|
|
850
890
|
|
|
851
|
-
|
|
891
|
+
**For a coded agent...**
|
|
892
|
+
|
|
893
|
+
Once you've done the above, you can use the TypeScript LSP to determine the exact parameter and return type for each tool. OR... you can inspect the TypeScript types for the package directly in the `node_modules` subdirectory (this is often an expedient way to understand the type details).
|
|
894
|
+
|
|
895
|
+
IMPORTANT! Always let TypeScript help you:
|
|
896
|
+
|
|
897
|
+
- Let the TypeScript compiler infer the type of the `tools` object: DO NOT provide an explicit type annotation as it is likely incorrect!
|
|
898
|
+
- NEVER cast to `any`: failure to resolve correct types likely means that something else is going wrong so keep investigating.
|
|
899
|
+
- Adding a tool to the agent's `tools` creates the correct signature on the `task` object:
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// Assume `tools` is defined as above, and that `Input` and `Output` are the `z.infer'd` types
|
|
903
|
+
type Tools = typeof tools;
|
|
904
|
+
|
|
905
|
+
async function run(input: Input, task: Task<Tools>): Promise<Output> {
|
|
906
|
+
// These should *not* resolve to `any` type, but should instead be strongly typed if all your imports have been done correctly.
|
|
907
|
+
const { foo, bar } = await task.tools.some_integration_tool_one({
|
|
908
|
+
baz: 'bop',
|
|
909
|
+
bip: 12,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
```
|
|
852
913
|
|
|
853
914
|
### DO NOT USE (Common Mistakes)
|
|
854
915
|
|
|
@@ -883,10 +944,6 @@ import { gitHubTools } from "@guildai-services/guildai~github/src/service"
|
|
|
883
944
|
import { agent } from "@guildai/agents-sdk"
|
|
884
945
|
// (no "use agent" at top)
|
|
885
946
|
export default agent({ run: async (input, task) => { ... } })
|
|
886
|
-
|
|
887
|
-
// ❌ WRONG: picking from guildTools breaks task.guild typing
|
|
888
|
-
...pick(guildTools, ["guild_get_me", "guild_create_agent"])
|
|
889
|
-
// Use ...guildTools instead (spread fully)
|
|
890
947
|
```
|
|
891
948
|
|
|
892
949
|
### CORRECT Patterns
|
|
@@ -995,8 +1052,9 @@ For simple `llmAgent()` agents that don't use `"use agent"`, you can skip the Ba
|
|
|
995
1052
|
|
|
996
1053
|
**CRITICAL:**
|
|
997
1054
|
|
|
998
|
-
-
|
|
999
|
-
-
|
|
1055
|
+
- DO NOT modify the `@guildai/agents-sdk` and `zod` dependencies provided in the agent's template.
|
|
1056
|
+
- You may add third-party ESM-compatible packages your agent uses to `dependencies`.
|
|
1057
|
+
- DO NOT include CJS packages: an agent that includes a CJS module will fail at runtime.
|
|
1000
1058
|
- `devDependencies` is for build tools only (`esbuild` for bundling, `typescript` for compilation).
|
|
1001
1059
|
|
|
1002
1060
|
## Versioning
|
|
@@ -1028,6 +1086,12 @@ my-agent/
|
|
|
1028
1086
|
|
|
1029
1087
|
## CLI Commands
|
|
1030
1088
|
|
|
1089
|
+
Below is a quick reference of common CLI commands.
|
|
1090
|
+
|
|
1091
|
+
- Use `guild help` to discover the full set of commands
|
|
1092
|
+
- Use `guild <command> [...<subcommand>] --help` with any command for full documentation on its usage
|
|
1093
|
+
- Prefer to explicitly provide all command arguments rather than relying on defaults
|
|
1094
|
+
|
|
1031
1095
|
```bash
|
|
1032
1096
|
guild setup # Install coding assistant skills
|
|
1033
1097
|
guild setup --claude-md # Also create CLAUDE.md template
|
|
@@ -1068,7 +1132,6 @@ guild workspace select # Set default workspace (writ
|
|
|
1068
1132
|
|
|
1069
1133
|
```bash
|
|
1070
1134
|
GUILD_WORKSPACE_ID=<id> guild agent test # Override workspace for this command
|
|
1071
|
-
GUILD_OWNER_ID=<id> guild agent init --name my-agent # Override owner for agent creation
|
|
1072
1135
|
```
|
|
1073
1136
|
|
|
1074
1137
|
## Troubleshooting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guildai/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Guild.ai CLI - Build, test, and deploy AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"chalk": "^5.3.0",
|
|
64
64
|
"commander": "^12.1.0",
|
|
65
65
|
"dotenv": "^16.4.7",
|
|
66
|
+
"esbuild": "^0.28.0",
|
|
66
67
|
"execa": "^9.6.1",
|
|
67
68
|
"ink": "^5.0.1",
|
|
68
69
|
"ink-text-input": "^6.0.0",
|