@guildai/cli 0.5.13 → 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.
Files changed (99) hide show
  1. package/README.md +17 -0
  2. package/dist/commands/agent/chat.js +31 -31
  3. package/dist/commands/agent/clone.js +3 -1
  4. package/dist/commands/agent/code.js +3 -2
  5. package/dist/commands/agent/fork.js +41 -15
  6. package/dist/commands/agent/get.js +3 -2
  7. package/dist/commands/agent/grep.js +61 -31
  8. package/dist/commands/agent/init.js +1 -1
  9. package/dist/commands/agent/publish.js +3 -2
  10. package/dist/commands/agent/pull.js +36 -21
  11. package/dist/commands/agent/revalidate.js +4 -3
  12. package/dist/commands/agent/save.js +2 -2
  13. package/dist/commands/agent/search.js +3 -3
  14. package/dist/commands/agent/tags/add.js +4 -3
  15. package/dist/commands/agent/tags/list.js +3 -2
  16. package/dist/commands/agent/tags/remove.js +4 -3
  17. package/dist/commands/agent/tags/set.js +3 -2
  18. package/dist/commands/agent/test.js +8 -12
  19. package/dist/commands/agent/unpublish.js +3 -2
  20. package/dist/commands/agent/update.js +9 -8
  21. package/dist/commands/agent/versions.js +3 -2
  22. package/dist/commands/agent/workspaces.js +3 -2
  23. package/dist/commands/auth/login.js +3 -3
  24. package/dist/commands/chat.js +64 -102
  25. package/dist/commands/credentials/endpoint-list.d.ts +3 -0
  26. package/dist/commands/credentials/endpoint-list.js +87 -0
  27. package/dist/commands/credentials/list.d.ts +3 -0
  28. package/dist/commands/{container → credentials}/list.js +11 -10
  29. package/dist/commands/credentials/policy-create.d.ts +3 -0
  30. package/dist/commands/credentials/policy-create.js +66 -0
  31. package/dist/commands/credentials/policy-delete.d.ts +3 -0
  32. package/dist/commands/{container/get.js → credentials/policy-delete.js} +9 -9
  33. package/dist/commands/credentials/policy-list.d.ts +3 -0
  34. package/dist/commands/{container-image/list.js → credentials/policy-list.js} +9 -9
  35. package/dist/commands/credentials/policy-update.d.ts +3 -0
  36. package/dist/commands/credentials/policy-update.js +66 -0
  37. package/dist/commands/integration/connect.d.ts +3 -0
  38. package/dist/commands/integration/connect.js +76 -0
  39. package/dist/commands/integration/create.d.ts +3 -0
  40. package/dist/commands/integration/create.js +298 -0
  41. package/dist/commands/integration/get.d.ts +3 -0
  42. package/dist/commands/integration/get.js +95 -0
  43. package/dist/commands/integration/list.d.ts +3 -0
  44. package/dist/commands/integration/list.js +61 -0
  45. package/dist/commands/integration/operation/create.d.ts +3 -0
  46. package/dist/commands/integration/operation/create.js +163 -0
  47. package/dist/commands/integration/operation/list.d.ts +3 -0
  48. package/dist/commands/integration/operation/list.js +83 -0
  49. package/dist/commands/integration/update.d.ts +3 -0
  50. package/dist/commands/integration/update.js +139 -0
  51. package/dist/commands/integration/version/build.d.ts +3 -0
  52. package/dist/commands/integration/version/build.js +86 -0
  53. package/dist/commands/integration/version/create.d.ts +3 -0
  54. package/dist/commands/integration/version/create.js +45 -0
  55. package/dist/commands/integration/version/get.d.ts +3 -0
  56. package/dist/commands/integration/version/get.js +72 -0
  57. package/dist/commands/integration/version/list.d.ts +3 -0
  58. package/dist/commands/integration/version/list.js +44 -0
  59. package/dist/commands/integration/version/publish.d.ts +3 -0
  60. package/dist/commands/integration/version/publish.js +79 -0
  61. package/dist/commands/integration/version/test.d.ts +3 -0
  62. package/dist/commands/integration/version/test.js +104 -0
  63. package/dist/commands/trigger/create.js +35 -19
  64. package/dist/commands/workspace/create.js +10 -4
  65. package/dist/commands/workspace/select.js +0 -1
  66. package/dist/index.js +60 -27
  67. package/dist/lib/agent-helpers.d.ts +8 -0
  68. package/dist/lib/agent-helpers.js +15 -0
  69. package/dist/lib/api-types.d.ts +109 -78
  70. package/dist/lib/auth.d.ts +1 -1
  71. package/dist/lib/auth.js +10 -6
  72. package/dist/lib/integration-helpers.d.ts +15 -0
  73. package/dist/lib/integration-helpers.js +38 -0
  74. package/dist/lib/output.d.ts +13 -16
  75. package/dist/lib/output.js +137 -109
  76. package/dist/lib/session-events-fetch.d.ts +27 -0
  77. package/dist/lib/session-events-fetch.js +25 -0
  78. package/dist/lib/session-polling.d.ts +7 -2
  79. package/dist/lib/session-polling.js +18 -11
  80. package/dist/lib/session-resume.js +2 -5
  81. package/dist/lib/table.d.ts +0 -1
  82. package/dist/lib/table.js +0 -1
  83. package/dist/mcp/tools.js +6 -12
  84. package/docs/CLI_WORKFLOW.md +19 -2
  85. package/docs/skills/agent-dev.md +17 -2
  86. package/package.json +6 -5
  87. package/dist/commands/container/destroy.d.ts +0 -3
  88. package/dist/commands/container/destroy.js +0 -48
  89. package/dist/commands/container/events.d.ts +0 -3
  90. package/dist/commands/container/events.js +0 -44
  91. package/dist/commands/container/exec.d.ts +0 -3
  92. package/dist/commands/container/exec.js +0 -64
  93. package/dist/commands/container/get.d.ts +0 -3
  94. package/dist/commands/container/list.d.ts +0 -3
  95. package/dist/commands/container-image/create.d.ts +0 -3
  96. package/dist/commands/container-image/create.js +0 -41
  97. package/dist/commands/container-image/get.d.ts +0 -3
  98. package/dist/commands/container-image/get.js +0 -33
  99. package/dist/commands/container-image/list.d.ts +0 -3
package/dist/lib/auth.js CHANGED
@@ -10,6 +10,7 @@ import { createSpinner } from './progress.js';
10
10
  import { getGuildcoreUrl } from './config.js';
11
11
  import { getIapHeaders } from './iap.js';
12
12
  import { brand, warn } from './colors.js';
13
+ import { isInteractive } from './stdin.js';
13
14
  const SERVICE_NAME = 'guild-cli';
14
15
  /**
15
16
  * Get account name for keyring, keyed by host to support multiple backends
@@ -137,6 +138,7 @@ export async function startDeviceFlow(authUrl = getGuildcoreUrl(), returnUrl, re
137
138
  * @returns Access token if successful, null if failed/expired
138
139
  */
139
140
  export async function pollForToken(authUrl, code, interval) {
141
+ const isTTY = process.stderr.isTTY || false;
140
142
  const spinner = createSpinner('Waiting for authorization...');
141
143
  spinner.start();
142
144
  const maxAttempts = 90; // 3 minutes (90 * 2 seconds)
@@ -167,7 +169,9 @@ export async function pollForToken(authUrl, code, interval) {
167
169
  }
168
170
  if (error.response?.data?.error === 'authorization_pending' ||
169
171
  error.response?.data?.error === 'slow_down') {
170
- spinner.text = `Waiting for authorization... (${attempts}/${maxAttempts})`;
172
+ if (isTTY) {
173
+ spinner.text = `Waiting for authorization... (${attempts}/${maxAttempts})`;
174
+ }
171
175
  continue;
172
176
  }
173
177
  }
@@ -196,18 +200,18 @@ export function showBetaGuidance() {
196
200
  * @param returnLabel - Optional friendly label for return button (e.g., "VSCode")
197
201
  * @returns true if successful, false otherwise
198
202
  */
199
- export async function login(returnUrl, returnLabel, noBrowser) {
203
+ export async function login(returnUrl, returnLabel, nonInteractive) {
200
204
  const authUrl = getGuildcoreUrl();
201
205
  try {
202
206
  console.log(chalk.bold('\nGuild.ai Authentication'));
203
207
  const deviceCode = await startDeviceFlow(authUrl, returnUrl, returnLabel);
204
- // Display styled verification code box
205
- displayVerificationCode(deviceCode.user_code, deviceCode.verification_uri_complete);
206
- const skipBrowser = noBrowser || process.stdin.isTTY !== true;
208
+ const skipBrowser = nonInteractive || !isInteractive();
207
209
  if (skipBrowser) {
208
- console.log(chalk.dim('Open the URL above in your browser to complete authentication.'));
210
+ console.log(`\nVerification code: ${deviceCode.user_code}`);
211
+ console.log(`Open to authenticate: ${deviceCode.verification_uri_complete}\n`);
209
212
  }
210
213
  else {
214
+ displayVerificationCode(deviceCode.user_code, deviceCode.verification_uri_complete);
211
215
  // Wait for user to press Enter before opening browser
212
216
  // This gives them time to see and copy the code before browser opens
213
217
  // Critical for VSCode integration where browser may open full screen
@@ -0,0 +1,15 @@
1
+ import { GuildAPIClient } from './api-client.js';
2
+ /**
3
+ * Resolve a version ID from an integration identifier and optional semver.
4
+ *
5
+ * 1. GET /integrations/<identifier> to resolve the integration
6
+ * 2. GET /integrations/<id>/versions to list versions
7
+ * 3. If semver provided, find matching version_number; otherwise use latest (first)
8
+ */
9
+ export declare function resolveVersionId(client: GuildAPIClient, identifier: string, versionNumber?: string): Promise<string>;
10
+ /**
11
+ * Resolve the latest draft version ID for an integration.
12
+ * Used by the build command where --version is not applicable (drafts have no semver).
13
+ */
14
+ export declare function resolveLatestDraftId(client: GuildAPIClient, identifier: string): Promise<string>;
15
+ //# sourceMappingURL=integration-helpers.d.ts.map
@@ -0,0 +1,38 @@
1
+ // Copyright 2026 Guild.ai
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Resolve a version ID from an integration identifier and optional semver.
5
+ *
6
+ * 1. GET /integrations/<identifier> to resolve the integration
7
+ * 2. GET /integrations/<id>/versions to list versions
8
+ * 3. If semver provided, find matching version_number; otherwise use latest (first)
9
+ */
10
+ export async function resolveVersionId(client, identifier, versionNumber) {
11
+ const integration = await client.get(`/integrations/${identifier}`);
12
+ const versions = await client.get(`/integrations/${integration.id}/versions?limit=100`);
13
+ if (versions.items.length === 0) {
14
+ throw new Error('No versions found for this integration');
15
+ }
16
+ if (versionNumber) {
17
+ const match = versions.items.find((v) => v.version_number === versionNumber);
18
+ if (!match) {
19
+ throw new Error(`Version ${versionNumber} not found`);
20
+ }
21
+ return match.id;
22
+ }
23
+ return versions.items[0].id;
24
+ }
25
+ /**
26
+ * Resolve the latest draft version ID for an integration.
27
+ * Used by the build command where --version is not applicable (drafts have no semver).
28
+ */
29
+ export async function resolveLatestDraftId(client, identifier) {
30
+ const integration = await client.get(`/integrations/${identifier}`);
31
+ const versions = await client.get(`/integrations/${integration.id}/versions?limit=100`);
32
+ const draft = versions.items.find((v) => v.version_number === null);
33
+ if (!draft) {
34
+ throw new Error('No draft version found for this integration');
35
+ }
36
+ return draft.id;
37
+ }
38
+ //# sourceMappingURL=integration-helpers.js.map
@@ -1,10 +1,20 @@
1
1
  import { type Spinner } from './progress.js';
2
- import type { Agent, AgentVersion, Container, ContainerEvent, ContainerImageListItem, Context, JobStep, Pagination, Session, Workspace, WorkspaceAgent, Trigger } from './api-types.js';
2
+ import type { Agent, AgentVersion, Credentials, CredentialsPolicy, Context, Integration, IntegrationVersion, JobStep, Pagination, Session, Workspace, WorkspaceAgent, Trigger } from './api-types.js';
3
3
  /**
4
4
  * Format an agent list as a human-readable table.
5
5
  * Shared by agent list and agent search commands.
6
6
  */
7
7
  export declare function formatAgentTable(agents: Agent[], pagination: Pagination): void;
8
+ /**
9
+ * Format an integration list as a human-readable table.
10
+ * Used by integration list command.
11
+ */
12
+ export declare function formatIntegrationTable(integrations: Integration[], pagination: Pagination): void;
13
+ /**
14
+ * Format an integration version list as a human-readable table.
15
+ * Used by integration version list command.
16
+ */
17
+ export declare function formatIntegrationVersionTable(versions: IntegrationVersion[], pagination: Pagination, integrationName: string): void;
8
18
  /**
9
19
  * Format an agent version list as a human-readable table.
10
20
  * Used by agent versions command.
@@ -35,21 +45,8 @@ export declare function formatSessionTable(sessions: Session[], pagination: Pagi
35
45
  * Used by trigger list command.
36
46
  */
37
47
  export declare function formatTriggerTable(triggers: Trigger[], pagination: Pagination): void;
38
- /**
39
- * Format a container list as a human-readable table.
40
- * Used by container list command.
41
- */
42
- export declare function formatContainerTable(containers: Container[], pagination: Pagination): void;
43
- /**
44
- * Format a container image list as a human-readable table.
45
- * Used by container-image list command.
46
- */
47
- export declare function formatContainerImageTable(images: ContainerImageListItem[], pagination: Pagination): void;
48
- /**
49
- * Format a container event list as a human-readable table.
50
- * Used by container events command.
51
- */
52
- export declare function formatContainerEventTable(events: ContainerEvent[], pagination: Pagination): void;
48
+ export declare function formatCredentialsTable(credentials: Credentials[], pagination: Pagination): void;
49
+ export declare function formatPoliciesTable(policies: CredentialsPolicy[], pagination: Pagination): void;
53
50
  /**
54
51
  * Format job steps as a human-readable table.
55
52
  * Used by job get command.
@@ -81,6 +81,100 @@ export function formatAgentTable(agents, pagination) {
81
81
  console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} agents`));
82
82
  }
83
83
  }
84
+ /**
85
+ * Format an integration list as a human-readable table.
86
+ * Used by integration list command.
87
+ */
88
+ export function formatIntegrationTable(integrations, pagination) {
89
+ if (integrations.length === 0) {
90
+ console.log(chalk.dim('No integrations found'));
91
+ return;
92
+ }
93
+ const table = new Table({
94
+ columns: [
95
+ { name: 'name', title: 'NAME', alignment: 'left' },
96
+ { name: 'description', title: 'DESCRIPTION', alignment: 'left' },
97
+ { name: 'visibility', title: 'VISIBILITY', alignment: 'left' },
98
+ { name: 'owner', title: 'OWNER', alignment: 'left', color: 'cyan' },
99
+ { name: 'updated', title: 'UPDATED', alignment: 'left' },
100
+ ],
101
+ });
102
+ integrations.forEach((integration) => {
103
+ table.addRow({
104
+ name: integration.name,
105
+ description: truncate(integration.description || '', 40),
106
+ visibility: integration.is_public ? 'public' : 'internal',
107
+ owner: integration.owner?.name || '',
108
+ updated: integration.updated_at ? formatRelativeTime(integration.updated_at) : '',
109
+ });
110
+ });
111
+ table.printTable();
112
+ const showing = Math.min(pagination.limit, integrations.length);
113
+ if (pagination.has_more) {
114
+ const nextOffset = pagination.offset + pagination.limit;
115
+ console.log(`\nShowing ${showing} of ${pagination.total_count} integrations. ` +
116
+ chalk.dim(`Use --offset ${nextOffset} to see more.`));
117
+ }
118
+ else if (pagination.total_count > showing) {
119
+ console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} integrations`));
120
+ }
121
+ }
122
+ /**
123
+ * Format an integration version list as a human-readable table.
124
+ * Used by integration version list command.
125
+ */
126
+ export function formatIntegrationVersionTable(versions, pagination, integrationName) {
127
+ if (versions.length === 0) {
128
+ console.log(chalk.dim('No versions found'));
129
+ return;
130
+ }
131
+ console.log(chalk.bold(`Versions for ${integrationName}`));
132
+ console.log();
133
+ const table = new Table({
134
+ columns: [
135
+ { name: 'id', title: 'ID', alignment: 'left' },
136
+ { name: 'version', title: 'VERSION', alignment: 'left' },
137
+ { name: 'author', title: 'AUTHOR', alignment: 'left', color: 'cyan' },
138
+ { name: 'status', title: 'STATUS', alignment: 'left' },
139
+ { name: 'published', title: 'PUBLISHED', alignment: 'left' },
140
+ { name: 'created', title: 'CREATED', alignment: 'left' },
141
+ ],
142
+ });
143
+ versions.forEach((v) => {
144
+ let statusLabel;
145
+ switch (v.validation_status) {
146
+ case 'PASSED':
147
+ statusLabel = chalk.green('Valid');
148
+ break;
149
+ case 'FAILED':
150
+ statusLabel = chalk.red('Failed');
151
+ break;
152
+ case 'RUNNING':
153
+ statusLabel = chalk.yellow('Building');
154
+ break;
155
+ default:
156
+ statusLabel = chalk.dim('—');
157
+ }
158
+ table.addRow({
159
+ id: v.id,
160
+ version: v.version_number || 'Draft',
161
+ author: v.author?.name || '',
162
+ status: statusLabel,
163
+ published: v.published_at ? formatRelativeTime(v.published_at) : chalk.dim('—'),
164
+ created: v.created_at ? formatRelativeTime(v.created_at) : '',
165
+ });
166
+ });
167
+ table.printTable();
168
+ const showing = Math.min(pagination.limit, versions.length);
169
+ if (pagination.has_more) {
170
+ const nextOffset = pagination.offset + pagination.limit;
171
+ console.log(`\nShowing ${showing} of ${pagination.total_count} versions. ` +
172
+ chalk.dim(`Use --offset ${nextOffset} to see more.`));
173
+ }
174
+ else if (pagination.total_count > showing) {
175
+ console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} versions`));
176
+ }
177
+ }
84
178
  /**
85
179
  * Format an agent version list as a human-readable table.
86
180
  * Used by agent versions command.
@@ -313,151 +407,85 @@ export function formatTriggerTable(triggers, pagination) {
313
407
  console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} triggers`));
314
408
  }
315
409
  }
316
- /**
317
- * Format a container list as a human-readable table.
318
- * Used by container list command.
319
- */
320
- export function formatContainerTable(containers, pagination) {
321
- if (containers.length === 0) {
322
- console.log(chalk.dim('No containers found'));
410
+ export function formatCredentialsTable(credentials, pagination) {
411
+ if (credentials.length === 0) {
412
+ console.log(chalk.dim('No credentials found'));
323
413
  return;
324
414
  }
325
415
  const table = new Table({
326
416
  columns: [
327
417
  { name: 'id', title: 'ID', alignment: 'left' },
328
- { name: 'status', title: 'STATUS', alignment: 'left' },
329
- { name: 'image', title: 'IMAGE', alignment: 'left' },
330
- { name: 'tag', title: 'TAG', alignment: 'left' },
418
+ { name: 'integration', title: 'INTEGRATION', alignment: 'left' },
419
+ { name: 'creator', title: 'CREATOR', alignment: 'left', color: 'cyan' },
331
420
  { name: 'created', title: 'CREATED', alignment: 'left' },
332
421
  ],
333
422
  });
334
- containers.forEach((container) => {
335
- const statusColor = container.status === 'RUNNING'
336
- ? chalk.green
337
- : container.status === 'ERRORED'
338
- ? chalk.red
339
- : container.status === 'DESTROYED'
340
- ? chalk.dim
341
- : chalk.yellow;
342
- table.addRow({
343
- id: container.id,
344
- status: statusColor(container.status),
345
- image: container.image?.name || '-',
346
- tag: container.image?.tag || '-',
347
- created: container.created_at ? formatRelativeTime(container.created_at) : '',
348
- });
349
- });
350
- table.printTable();
351
- const showing = Math.min(pagination.limit, containers.length);
352
- if (pagination.has_more) {
353
- const nextOffset = pagination.offset + pagination.limit;
354
- console.log(`\nShowing ${showing} of ${pagination.total_count} containers. ` +
355
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
356
- }
357
- else if (pagination.total_count > showing) {
358
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} containers`));
359
- }
360
- }
361
- /**
362
- * Format a container image list as a human-readable table.
363
- * Used by container-image list command.
364
- */
365
- export function formatContainerImageTable(images, pagination) {
366
- if (images.length === 0) {
367
- console.log(chalk.dim('No container images found'));
368
- return;
369
- }
370
- const table = new Table({
371
- columns: [
372
- { name: 'id', title: 'ID', alignment: 'left' },
373
- { name: 'name', title: 'NAME', alignment: 'left' },
374
- { name: 'image', title: 'IMAGE', alignment: 'left' },
375
- { name: 'tag', title: 'TAG', alignment: 'left' },
376
- { name: 'public', title: 'PUBLIC', alignment: 'left' },
377
- ],
378
- });
379
- images.forEach((image) => {
423
+ credentials.forEach((cred) => {
380
424
  table.addRow({
381
- id: image.id,
382
- name: image.name,
383
- image: image.image_name,
384
- tag: image.tag,
385
- public: image.is_public ? chalk.green('yes') : chalk.dim('no'),
425
+ id: cred.id,
426
+ integration: cred.integration.full_name,
427
+ creator: cred.creator.name,
428
+ created: formatRelativeTime(cred.created_at),
386
429
  });
387
430
  });
388
431
  table.printTable();
389
- const showing = Math.min(pagination.limit, images.length);
432
+ const showing = Math.min(pagination.limit, credentials.length);
390
433
  if (pagination.has_more) {
391
434
  const nextOffset = pagination.offset + pagination.limit;
392
- console.log(`\nShowing ${showing} of ${pagination.total_count} images. ` +
435
+ console.log(`\nShowing ${showing} of ${pagination.total_count} credentials. ` +
393
436
  chalk.dim(`Use --offset ${nextOffset} to see more.`));
394
437
  }
395
438
  else if (pagination.total_count > showing) {
396
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} images`));
439
+ console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} credentials`));
397
440
  }
398
441
  }
399
- /**
400
- * Derive a human-readable event type label from entity_type.
401
- */
402
- function formatEventType(entityType) {
403
- if (entityType === 'EntContainerEventCreate')
404
- return 'create';
405
- if (entityType === 'EntContainerEventDestroy')
406
- return 'destroy';
407
- if (entityType === 'EntContainerEventCommand')
408
- return 'command';
409
- return entityType;
410
- }
411
- /**
412
- * Format a container event list as a human-readable table.
413
- * Used by container events command.
414
- */
415
- export function formatContainerEventTable(events, pagination) {
416
- if (events.length === 0) {
417
- console.log(chalk.dim('No events found'));
442
+ export function formatPoliciesTable(policies, pagination) {
443
+ if (policies.length === 0) {
444
+ console.log(chalk.dim('No policies found'));
418
445
  return;
419
446
  }
420
447
  const table = new Table({
421
448
  columns: [
422
449
  { name: 'id', title: 'ID', alignment: 'left' },
423
- { name: 'type', title: 'TYPE', alignment: 'left', color: 'cyan' },
424
- { name: 'status', title: 'STATUS', alignment: 'left' },
425
- { name: 'actor', title: 'ACTOR', alignment: 'left' },
426
- { name: 'detail', title: 'DETAIL', alignment: 'left' },
450
+ { name: 'decision', title: 'DECISION', alignment: 'left' },
451
+ { name: 'operations', title: 'OPERATIONS', alignment: 'left' },
452
+ { name: 'workspaces', title: 'WORKSPACES', alignment: 'left' },
453
+ { name: 'agents', title: 'AGENTS', alignment: 'left' },
454
+ { name: 'resources', title: 'RESOURCES', alignment: 'left' },
427
455
  { name: 'created', title: 'CREATED', alignment: 'left' },
428
456
  ],
429
457
  });
430
- events.forEach((event) => {
431
- const statusColor = event.status === 'SUCCESS'
432
- ? chalk.green
433
- : event.status === 'ERROR'
434
- ? chalk.red
435
- : chalk.yellow;
436
- let detail = '';
437
- if (event.entity_type === 'EntContainerEventCommand') {
438
- detail = truncate(event.command, 30);
439
- }
440
- else if (event.status === 'ERROR' && event.error) {
441
- detail = truncate(event.error, 30);
442
- }
458
+ policies.forEach((policy) => {
459
+ const decisionColor = policy.decision === 'ALLOW' ? chalk.green : chalk.red;
443
460
  table.addRow({
444
- id: event.id,
445
- type: formatEventType(event.entity_type),
446
- status: statusColor(event.status),
447
- actor: event.actor?.name || event.actor?.full_name || '-',
448
- detail,
449
- created: event.created_at ? formatRelativeTime(event.created_at) : '',
461
+ id: policy.id,
462
+ decision: decisionColor(policy.decision),
463
+ operations: policy.operations
464
+ ? truncate(policy.operations.join(', '), 30)
465
+ : chalk.dim('all'),
466
+ workspaces: policy.workspaces.length > 0
467
+ ? truncate(policy.workspaces.map((w) => w.full_name || w.name).join(', '), 20)
468
+ : chalk.dim('all'),
469
+ agents: policy.agents.length > 0
470
+ ? truncate(policy.agents.map((a) => a.full_name || a.name).join(', '), 20)
471
+ : chalk.dim('all'),
472
+ resources: policy.resources
473
+ ? truncate(Object.entries(policy.resources)
474
+ .map(([k, v]) => `${k}: ${v.join(', ')}`)
475
+ .join('; '), 30)
476
+ : chalk.dim('none'),
477
+ created: formatRelativeTime(policy.created_at),
450
478
  });
451
479
  });
452
480
  table.printTable();
453
- const showing = Math.min(pagination.limit, events.length);
481
+ const showing = Math.min(pagination.limit, policies.length);
454
482
  if (pagination.has_more) {
455
483
  const nextOffset = pagination.offset + pagination.limit;
456
- console.log(`\nShowing ${showing} of ${pagination.total_count} events. ` +
484
+ console.log(`\nShowing ${showing} of ${pagination.total_count} policies. ` +
457
485
  chalk.dim(`Use --offset ${nextOffset} to see more.`));
458
486
  }
459
487
  else if (pagination.total_count > showing) {
460
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} events`));
488
+ console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} policies`));
461
489
  }
462
490
  }
463
491
  /**
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared event/task fetching for session polling.
3
+ *
4
+ * Consolidates the duplicate response parsing that was scattered across
5
+ * chat.tsx, session-polling.ts, and session-resume.ts.
6
+ */
7
+ import { GuildAPIClient } from './api-client.js';
8
+ import { SessionEvent, SessionTask } from './session-events.js';
9
+ interface FetchOptions {
10
+ /** Only return events after this event ID (server-side cursor). */
11
+ fromId?: string;
12
+ /** Maximum number of events to return. Default 1000. */
13
+ limit?: number;
14
+ }
15
+ /**
16
+ * Fetch session events from the API.
17
+ *
18
+ * Uses from_id cursor so callers only fetch new events
19
+ * instead of re-fetching everything on every poll cycle.
20
+ */
21
+ export declare function fetchEvents(client: GuildAPIClient, sessionId: string, options?: FetchOptions): Promise<SessionEvent[]>;
22
+ /**
23
+ * Fetch session tasks from the API.
24
+ */
25
+ export declare function fetchTasks(client: GuildAPIClient, sessionId: string): Promise<SessionTask[]>;
26
+ export {};
27
+ //# sourceMappingURL=session-events-fetch.d.ts.map
@@ -0,0 +1,25 @@
1
+ // Copyright 2026 Guild.ai
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Fetch session events from the API.
5
+ *
6
+ * Uses from_id cursor so callers only fetch new events
7
+ * instead of re-fetching everything on every poll cycle.
8
+ */
9
+ export async function fetchEvents(client, sessionId, options = {}) {
10
+ const { fromId, limit = 1000 } = options;
11
+ let url = `/sessions/${sessionId}/events?limit=${limit}`;
12
+ if (fromId) {
13
+ url += `&from_id=${fromId}`;
14
+ }
15
+ const response = await client.get(url);
16
+ return response?.items || [];
17
+ }
18
+ /**
19
+ * Fetch session tasks from the API.
20
+ */
21
+ export async function fetchTasks(client, sessionId) {
22
+ const response = await client.get(`/sessions/${sessionId}/tasks?limit=1000`);
23
+ return response?.items || [];
24
+ }
25
+ //# sourceMappingURL=session-events-fetch.js.map
@@ -1,6 +1,11 @@
1
1
  import { GuildAPIClient } from './api-client.js';
2
+ export interface PollResult {
3
+ response: string | null;
4
+ /** Last event ID seen — pass as `afterEventId` on next call. */
5
+ lastEventId: string | undefined;
6
+ }
2
7
  /**
3
- * Poll for agent response after a certain event count.
8
+ * Poll for agent response using from_id cursor.
4
9
  *
5
10
  * Handles both multi-turn agents (which emit agent_notification_message)
6
11
  * and one-shot agents (which may only emit runtime_done with output content).
@@ -10,5 +15,5 @@ import { GuildAPIClient } from './api-client.js';
10
15
  * 2. runtime_done from agent tasks — fallback for one-shot agents
11
16
  * 3. runtime_error from agent tasks — fail fast
12
17
  */
13
- export declare function pollForResponse(client: GuildAPIClient, sessionId: string, afterEventCount: number, maxWaitTime?: number): Promise<string | null>;
18
+ export declare function pollForResponse(client: GuildAPIClient, sessionId: string, afterEventId: string | undefined, maxWaitTime?: number): Promise<PollResult>;
14
19
  //# sourceMappingURL=session-polling.d.ts.map
@@ -1,8 +1,9 @@
1
1
  // Copyright 2026 Guild.ai
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { isDebugMode } from './errors.js';
3
+ import { debug, isDebugMode } from './errors.js';
4
+ import { fetchEvents } from './session-events-fetch.js';
4
5
  /**
5
- * Poll for agent response after a certain event count.
6
+ * Poll for agent response using from_id cursor.
6
7
  *
7
8
  * Handles both multi-turn agents (which emit agent_notification_message)
8
9
  * and one-shot agents (which may only emit runtime_done with output content).
@@ -12,16 +13,16 @@ import { isDebugMode } from './errors.js';
12
13
  * 2. runtime_done from agent tasks — fallback for one-shot agents
13
14
  * 3. runtime_error from agent tasks — fail fast
14
15
  */
15
- export async function pollForResponse(client, sessionId, afterEventCount, maxWaitTime = 60000) {
16
+ export async function pollForResponse(client, sessionId, afterEventId, maxWaitTime = 60000) {
16
17
  const startTime = Date.now();
18
+ let fromId = afterEventId;
17
19
  while (Date.now() - startTime < maxWaitTime) {
18
- const response = await client.get(`/sessions/${sessionId}/events?limit=1000`);
19
- const events = response?.items || [];
20
+ const events = await fetchEvents(client, sessionId, { fromId });
20
21
  let lastAgentRuntimeDone = null;
21
- for (let i = afterEventCount; i < events.length; i++) {
22
- const event = events[i];
22
+ for (const event of events) {
23
+ debug(`pollForResponse event: ${event.type}`);
23
24
  if (event.type === 'agent_notification_message') {
24
- return event.content.data;
25
+ return { response: event.content.data, lastEventId: event.id };
25
26
  }
26
27
  if (event.type === 'runtime_done' &&
27
28
  event.content !== undefined &&
@@ -30,17 +31,23 @@ export async function pollForResponse(client, sessionId, afterEventCount, maxWai
30
31
  lastAgentRuntimeDone = JSON.stringify(event.content);
31
32
  }
32
33
  if (event.type === 'runtime_error' && event.task && 'agent' in event.task) {
33
- return JSON.stringify({ error: event.content });
34
+ return {
35
+ response: JSON.stringify({ error: event.content }),
36
+ lastEventId: event.id,
37
+ };
34
38
  }
35
39
  if (event.type === 'agent_console' && isDebugMode()) {
36
40
  process.stderr.write(`[console.${event.level}] ${event.content}\n`);
37
41
  }
38
42
  }
43
+ if (events.length > 0) {
44
+ fromId = events[events.length - 1].id;
45
+ }
39
46
  if (lastAgentRuntimeDone !== null) {
40
- return lastAgentRuntimeDone;
47
+ return { response: lastAgentRuntimeDone, lastEventId: fromId };
41
48
  }
42
49
  await new Promise((resolve) => setTimeout(resolve, 500));
43
50
  }
44
- return null;
51
+ return { response: null, lastEventId: fromId };
45
52
  }
46
53
  //# sourceMappingURL=session-polling.js.map
@@ -7,6 +7,7 @@
7
7
  import chalk from 'chalk';
8
8
  import { marked } from 'marked';
9
9
  import { markedTerminal } from 'marked-terminal';
10
+ import { fetchEvents } from './session-events-fetch.js';
10
11
  import { brand, code as codeColor } from './colors.js';
11
12
  // Configure marked for terminal rendering (same config as chat.tsx)
12
13
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -37,11 +38,7 @@ export function printResumeHint(sessionId, command) {
37
38
  * Fetch all events for a session from the API.
38
39
  */
39
40
  export async function fetchSessionEvents(client, sessionId) {
40
- const response = await client.get(`/sessions/${sessionId}/events?limit=1000`);
41
- const events = (Array.isArray(response)
42
- ? response
43
- : response?.items || []);
44
- return events;
41
+ return fetchEvents(client, sessionId);
45
42
  }
46
43
  /**
47
44
  * Fetch an existing session by ID.
@@ -6,7 +6,6 @@ interface ColumnConfig {
6
6
  }
7
7
  /**
8
8
  * Minimal table renderer using string-width@7 for ANSI-aware column sizing.
9
- * Handles OSC8 hyperlink sequences correctly (string-width uses strip-ansi@7).
10
9
  */
11
10
  export declare class Table {
12
11
  private columns;
package/dist/lib/table.js CHANGED
@@ -4,7 +4,6 @@ import chalk from 'chalk';
4
4
  import stringWidth from 'string-width';
5
5
  /**
6
6
  * Minimal table renderer using string-width@7 for ANSI-aware column sizing.
7
- * Handles OSC8 hyperlink sequences correctly (string-width uses strip-ansi@7).
8
7
  */
9
8
  export class Table {
10
9
  columns;