@guildai/cli 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/commands/agent/chat.d.ts +1 -0
  2. package/dist/commands/agent/chat.js +24 -1
  3. package/dist/commands/agent/fork.js +1 -0
  4. package/dist/commands/agent/init.js +2 -1
  5. package/dist/commands/agent/list.js +3 -2
  6. package/dist/commands/agent/pull.js +1 -1
  7. package/dist/commands/agent/save.js +4 -4
  8. package/dist/commands/agent/search.js +3 -2
  9. package/dist/commands/agent/test.d.ts +1 -0
  10. package/dist/commands/agent/test.js +92 -34
  11. package/dist/commands/agent/versions.js +3 -2
  12. package/dist/commands/agent/workspaces.js +3 -2
  13. package/dist/commands/chat.d.ts +10 -0
  14. package/dist/commands/chat.js +215 -50
  15. package/dist/commands/credentials/endpoint-list.js +3 -2
  16. package/dist/commands/credentials/list.js +3 -2
  17. package/dist/commands/credentials/policy-list.js +3 -2
  18. package/dist/commands/integration/connect.js +1 -1
  19. package/dist/commands/integration/create.js +36 -36
  20. package/dist/commands/integration/list.js +3 -2
  21. package/dist/commands/integration/operation/create.js +2 -1
  22. package/dist/commands/integration/operation/list.js +26 -17
  23. package/dist/commands/integration/version/list.js +3 -2
  24. package/dist/commands/mcp.js +1 -1
  25. package/dist/commands/session/create.js +1 -1
  26. package/dist/commands/session/events.js +19 -9
  27. package/dist/commands/session/list.js +3 -2
  28. package/dist/commands/session/send.js +1 -1
  29. package/dist/commands/session/tasks.js +3 -2
  30. package/dist/commands/trigger/list.js +3 -2
  31. package/dist/commands/trigger/sessions.js +3 -2
  32. package/dist/commands/workspace/agent/add.js +16 -3
  33. package/dist/commands/workspace/agent/list.js +17 -3
  34. package/dist/commands/workspace/agent/remove.js +14 -1
  35. package/dist/commands/workspace/clear.d.ts +3 -0
  36. package/dist/commands/workspace/clear.js +45 -0
  37. package/dist/commands/workspace/context/list.js +3 -2
  38. package/dist/commands/workspace/list.js +3 -2
  39. package/dist/commands/workspace/select.js +3 -1
  40. package/dist/index.js +53 -6
  41. package/dist/lib/api-types.d.ts +5 -0
  42. package/dist/lib/api-types.js +4 -0
  43. package/dist/lib/generated-types.d.ts +1 -1
  44. package/dist/lib/generated-types.js +1 -0
  45. package/dist/lib/guild-config.d.ts +13 -0
  46. package/dist/lib/guild-config.js +19 -0
  47. package/dist/lib/npmrc.js +6 -2
  48. package/dist/lib/output.js +4 -4
  49. package/dist/lib/owner-helpers.d.ts +3 -0
  50. package/dist/lib/owner-helpers.js +17 -10
  51. package/dist/lib/session-events.d.ts +32 -16
  52. package/dist/lib/session-events.js +22 -0
  53. package/dist/lib/session-polling.d.ts +4 -3
  54. package/dist/lib/session-polling.js +75 -17
  55. package/dist/lib/session-resume.js +4 -1
  56. package/dist/lib/stdin.d.ts +4 -0
  57. package/dist/lib/stdin.js +23 -0
  58. package/dist/lib/validate-input-schema.d.ts +19 -0
  59. package/dist/lib/validate-input-schema.js +208 -0
  60. package/dist/lib/workspace-helpers.d.ts +20 -0
  61. package/dist/lib/workspace-helpers.js +49 -0
  62. package/dist/mcp/tools.js +8 -52
  63. package/docs/skills/agent-dev.md +191 -128
  64. package/package.json +2 -1
@@ -83,4 +83,17 @@ export declare function getWorkspaceId(cwd?: string): Promise<{
83
83
  * Check if we're in an agent directory (has guild.json)
84
84
  */
85
85
  export declare function isAgentDirectory(cwd?: string): Promise<boolean>;
86
+ /**
87
+ * Get a human-readable label for the workspace source.
88
+ * Returns undefined for 'env' (treated as explicit, like --workspace flag; no label needed).
89
+ *
90
+ * @param source - The source of workspace resolution
91
+ * @returns Human-readable label, or undefined for env source
92
+ *
93
+ * @example
94
+ * getWorkspaceSourceLabel('local') // → 'guild.json'
95
+ * getWorkspaceSourceLabel('global') // → 'global config'
96
+ * getWorkspaceSourceLabel('env') // → undefined
97
+ */
98
+ export declare function getWorkspaceSourceLabel(source: 'env' | 'local' | 'global'): string | undefined;
86
99
  //# sourceMappingURL=guild-config.d.ts.map
@@ -156,4 +156,23 @@ export async function getWorkspaceId(cwd = process.cwd()) {
156
156
  export async function isAgentDirectory(cwd = process.cwd()) {
157
157
  return fileExists(getLocalConfigPath(cwd));
158
158
  }
159
+ /**
160
+ * Get a human-readable label for the workspace source.
161
+ * Returns undefined for 'env' (treated as explicit, like --workspace flag; no label needed).
162
+ *
163
+ * @param source - The source of workspace resolution
164
+ * @returns Human-readable label, or undefined for env source
165
+ *
166
+ * @example
167
+ * getWorkspaceSourceLabel('local') // → 'guild.json'
168
+ * getWorkspaceSourceLabel('global') // → 'global config'
169
+ * getWorkspaceSourceLabel('env') // → undefined
170
+ */
171
+ export function getWorkspaceSourceLabel(source) {
172
+ if (source === 'local')
173
+ return 'guild.json';
174
+ if (source === 'global')
175
+ return 'global config';
176
+ return undefined; // 'env' is explicit (GUILD_WORKSPACE_ID), no magic label needed
177
+ }
159
178
  //# sourceMappingURL=guild-config.js.map
package/dist/lib/npmrc.js CHANGED
@@ -8,7 +8,8 @@ function getRegistryUrl() {
8
8
  const url = getGuildcoreUrl();
9
9
  return url.replace('/api', '/npm') + (url.endsWith('/') ? '' : '/');
10
10
  }
11
- const SCOPES = ['@guildai', '@guildai-services', '@guildai-agents'];
11
+ const SCOPES = ['@guildai-services', '@guildai-agents'];
12
+ const LEGACY_SCOPES = ['@guildai'];
12
13
  export async function configureNpmrc() {
13
14
  const registryUrl = getRegistryUrl();
14
15
  const scope = registryUrl.split(':').slice(1).join(':');
@@ -35,7 +36,10 @@ export async function configureNpmrc() {
35
36
  export async function cleanupNpmrc() {
36
37
  const registryUrl = getRegistryUrl();
37
38
  const scope = registryUrl.split(':').slice(1).join(':');
38
- const keys = [...SCOPES.map((s) => `${s}:registry`), `${scope}:_authToken`];
39
+ const keys = [
40
+ ...[...SCOPES, ...LEGACY_SCOPES].map((s) => `${s}:registry`),
41
+ `${scope}:_authToken`,
42
+ ];
39
43
  for (const key of keys) {
40
44
  try {
41
45
  await execa('npm', [
@@ -195,7 +195,7 @@ export function formatVersionTable(versions, pagination) {
195
195
  ? chalk.red
196
196
  : chalk.dim;
197
197
  table.addRow({
198
- id: v.id.substring(0, 8),
198
+ id: v.id,
199
199
  version: v.version_number || '-',
200
200
  status: v.status,
201
201
  validation: validationColor(v.validation_status || '-'),
@@ -225,7 +225,7 @@ export function formatContextTable(contexts, pagination) {
225
225
  });
226
226
  contexts.forEach((ctx) => {
227
227
  table.addRow({
228
- id: truncate(ctx.id, 12),
228
+ id: ctx.id,
229
229
  status: ctx.status,
230
230
  type: ctx.type || '-',
231
231
  created: ctx.created_at ? formatRelativeTime(ctx.created_at) : '',
@@ -256,7 +256,7 @@ export function formatWorkspaceAgentTable(agents) {
256
256
  const agentUrl = wa.agent.full_name ? `${base}/agents/${wa.agent.full_name}` : '';
257
257
  table.addRow({
258
258
  name: agentUrl ? hyperlink(name, agentUrl) : name,
259
- version: wa.agent_version.version_number || wa.agent_version.id.substring(0, 8),
259
+ version: wa.agent_version.version_number || wa.agent_version.id,
260
260
  auto_update: wa.should_autoupdate ? chalk.green('yes') : chalk.dim('no'),
261
261
  });
262
262
  });
@@ -353,7 +353,7 @@ export function formatTaskTable(tasks, pagination) {
353
353
  ? chalk.yellow
354
354
  : chalk.dim;
355
355
  table.addRow({
356
- id: truncate(task.id, 13),
356
+ id: task.id,
357
357
  name,
358
358
  status: statusColor(task.status),
359
359
  tokens: task.token_usage ? task.token_usage.total_tokens.toLocaleString() : '-',
@@ -13,6 +13,9 @@ export interface ResolveOwnerOptions {
13
13
  ownerFlag?: string;
14
14
  client: GuildAPIClient;
15
15
  interactive: boolean;
16
+ /** When true, error if multiple accounts and no explicit owner in non-interactive mode.
17
+ * When false (default), fall back to current user. */
18
+ requireExplicitOwner?: boolean;
16
19
  }
17
20
  export declare function resolveOwnerId(options: ResolveOwnerOptions): Promise<OwnerChoice>;
18
21
  //# sourceMappingURL=owner-helpers.d.ts.map
@@ -11,9 +11,10 @@
11
11
  * 2. GUILD_OWNER_ID environment variable
12
12
  * 3. default_owner from ~/.guild/config.json
13
13
  * 4. Fetch user + orgs from API:
14
- * - No orgs → use current user
15
- * - Has orgs + interactive → prompt
16
- * - Has orgs + non-interactive → use current user
14
+ * - No orgs + non-interactive → use current user (single-account, stated explicitly)
15
+ * - No orgs + interactive → prompt with personal account as only choice
16
+ * - Has orgs + interactive → prompt to select from all accounts
17
+ * - Has orgs + non-interactive → error: require --owner flag
17
18
  */
18
19
  import inquirer from 'inquirer';
19
20
  import { loadGlobalConfig } from './guild-config.js';
@@ -35,7 +36,7 @@ export async function lookupOwner(client, val) {
35
36
  return undefined;
36
37
  }
37
38
  export async function resolveOwnerId(options) {
38
- const { ownerFlag, client, interactive } = options;
39
+ const { ownerFlag, client, interactive, requireExplicitOwner = false } = options;
39
40
  // Priority 1: --owner flag
40
41
  if (ownerFlag) {
41
42
  debug('Using owner from --owner flag: %s', ownerFlag);
@@ -71,17 +72,23 @@ export async function resolveOwnerId(options) {
71
72
  // Priority 4: Fetch user + orgs
72
73
  const me = await client.get('/me');
73
74
  const orgs = await client.fetchAll('/me/organizations');
74
- // No orgs → use current user
75
- if (orgs.length === 0) {
75
+ // No orgs + non-interactive → use current user (single account, stated explicitly by caller)
76
+ if (orgs.length === 0 && !interactive) {
76
77
  debug('No organizations found, using current user as owner');
77
78
  return { id: me.id, name: me.name, type: 'user' };
78
79
  }
79
- // Has orgs + non-interactive → use current user
80
- if (!interactive) {
81
- debug('Non-interactive mode with orgs available, using current user as owner');
80
+ // Has orgs + non-interactive
81
+ if (orgs.length > 0 && !interactive) {
82
+ if (requireExplicitOwner) {
83
+ debug('Non-interactive mode with multiple accounts available, requiring --owner');
84
+ throw new Error('Owner is required in non-interactive mode when multiple accounts are available.\n\n' +
85
+ 'Use --owner <name-or-id> to specify an owner, or set a default:\n' +
86
+ ' guild config set default_owner <name-or-id>');
87
+ }
88
+ debug('Multiple accounts available, defaulting to current user');
82
89
  return { id: me.id, name: me.name, type: 'user' };
83
90
  }
84
- // Has orgs + interactive prompt
91
+ // Interactive prompt (always, even with single account)
85
92
  const choices = [
86
93
  {
87
94
  name: `${me.name} (personal)`,
@@ -19,10 +19,12 @@ export interface AgentRef {
19
19
  }
20
20
  export interface SessionTaskAgentItem extends BaseTaskFields {
21
21
  agent: AgentRef;
22
+ parent_task_id: string | null;
22
23
  }
23
24
  export interface SessionTaskToolItem extends BaseTaskFields {
24
25
  tool_name: string;
25
26
  tool_call_id: string;
27
+ parent_task: SessionTaskItem | null;
26
28
  }
27
29
  export type SessionTaskItem = SessionTaskAgentItem | SessionTaskToolItem;
28
30
  export interface SessionTaskAgent extends SessionTaskAgentItem {
@@ -91,26 +93,29 @@ export interface RuntimeDoneEvent extends BaseEvent {
91
93
  type: 'runtime_done';
92
94
  content: Record<string, unknown>;
93
95
  }
96
+ export interface TextContent {
97
+ type: 'text';
98
+ data: string;
99
+ }
100
+ export interface ResponseStreamContent {
101
+ type: 'application/guild-response-stream';
102
+ stream_id: string;
103
+ sequence: number;
104
+ status: 'streaming' | 'done' | 'aborted';
105
+ text: string;
106
+ }
107
+ export type AgentNotificationMessageContent = TextContent | ResponseStreamContent;
94
108
  export interface AgentNotificationMessageEvent extends BaseEvent {
95
109
  type: 'agent_notification_message';
96
- content: {
97
- type: 'text';
98
- data: string;
99
- };
110
+ content: AgentNotificationMessageContent;
100
111
  }
101
112
  export interface AgentNotificationProgressEvent extends BaseEvent {
102
113
  type: 'agent_notification_progress';
103
- content: {
104
- type: 'text';
105
- data: string;
106
- };
114
+ content: TextContent;
107
115
  }
108
116
  export interface AgentNotificationErrorEvent extends BaseEvent {
109
117
  type: 'agent_notification_error';
110
- content: {
111
- type: 'text';
112
- data: string;
113
- };
118
+ content: TextContent;
114
119
  }
115
120
  export interface AgentConsoleEvent extends BaseEvent {
116
121
  type: 'agent_console';
@@ -126,10 +131,7 @@ export interface TriggerMessageEvent extends BaseEvent {
126
131
  }
127
132
  export interface SystemErrorEvent extends BaseEvent {
128
133
  type: 'system_error';
129
- content: {
130
- type: 'text';
131
- data: string;
132
- };
134
+ content: TextContent;
133
135
  details?: Record<string, unknown>;
134
136
  }
135
137
  export interface LlmStartEvent extends BaseEvent {
@@ -169,6 +171,20 @@ export interface CredentialsRequestEvent extends BaseEvent {
169
171
  export declare function isUnfulfilledAgentInstallRequest(event: SessionEvent): event is AgentInstallRequestEvent;
170
172
  /** Check if event is an unfulfilled credentials request */
171
173
  export declare function isUnfulfilledCredentialsRequest(event: SessionEvent): event is CredentialsRequestEvent;
174
+ /** Check if an agent notification message is a transient response stream draft. */
175
+ export declare function isResponseStreamEvent(event: SessionEvent): event is AgentNotificationMessageEvent & {
176
+ content: ResponseStreamContent;
177
+ };
178
+ /** Check if a response stream event carries the final generated text. */
179
+ export declare function isDoneResponseStreamEvent(event: SessionEvent): event is AgentNotificationMessageEvent & {
180
+ content: ResponseStreamContent & {
181
+ status: 'done';
182
+ };
183
+ };
184
+ /** Extract display text from notification content. */
185
+ export declare function getAgentNotificationText(event: AgentNotificationMessageEvent): string;
186
+ /** Check if an event belongs to the root task or has no task context. */
187
+ export declare function isRootTaskEvent(event: SessionEvent): boolean;
172
188
  export interface InterruptedEvent extends BaseEvent {
173
189
  type: 'interrupted';
174
190
  interrupted_at: string;
@@ -87,4 +87,26 @@ export function isUnfulfilledAgentInstallRequest(event) {
87
87
  export function isUnfulfilledCredentialsRequest(event) {
88
88
  return event.type === 'credentials_request' && !event.is_fulfilled;
89
89
  }
90
+ /** Check if an agent notification message is a transient response stream draft. */
91
+ export function isResponseStreamEvent(event) {
92
+ return (event.type === 'agent_notification_message' &&
93
+ event.content.type === 'application/guild-response-stream');
94
+ }
95
+ /** Check if a response stream event carries the final generated text. */
96
+ export function isDoneResponseStreamEvent(event) {
97
+ return isResponseStreamEvent(event) && event.content.status === 'done';
98
+ }
99
+ /** Extract display text from notification content. */
100
+ export function getAgentNotificationText(event) {
101
+ return event.content.type === 'text' ? event.content.data : event.content.text;
102
+ }
103
+ /** Check if an event belongs to the root task or has no task context. */
104
+ export function isRootTaskEvent(event) {
105
+ const task = event.task;
106
+ if (!task)
107
+ return true;
108
+ if ('parent_task_id' in task)
109
+ return task.parent_task_id === null;
110
+ return task.parent_task === null;
111
+ }
90
112
  //# sourceMappingURL=session-events.js.map
@@ -11,9 +11,10 @@ export interface PollResult {
11
11
  * and one-shot agents (which may only emit runtime_done with output content).
12
12
  *
13
13
  * Priority:
14
- * 1. agent_notification_message — preferred, immediate return
15
- * 2. runtime_done from agent tasks fallback for one-shot agents
16
- * 3. runtime_error from agent tasks fail fast
14
+ * 1. root agent_notification_message — preferred, immediate return
15
+ * 2. root runtime_errorfail fast
16
+ * 3. root runtime_done contentfallback for one-shot agents
17
+ * 4. root done response-stream after root runtime_done — streaming fallback
17
18
  */
18
19
  export declare function pollForResponse(client: GuildAPIClient, sessionId: string, afterEventId: string | undefined, maxWaitTime?: number): Promise<PollResult>;
19
20
  /**
@@ -3,6 +3,7 @@
3
3
  import { debug, isDebugMode } from './errors.js';
4
4
  import { shouldShowEvent } from './event-filter.js';
5
5
  import { fetchEvents } from './session-events-fetch.js';
6
+ import { getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from './session-events.js';
6
7
  /**
7
8
  * Poll for agent response using from_id cursor.
8
9
  *
@@ -10,28 +11,51 @@ import { fetchEvents } from './session-events-fetch.js';
10
11
  * and one-shot agents (which may only emit runtime_done with output content).
11
12
  *
12
13
  * Priority:
13
- * 1. agent_notification_message — preferred, immediate return
14
- * 2. runtime_done from agent tasks fallback for one-shot agents
15
- * 3. runtime_error from agent tasks fail fast
14
+ * 1. root agent_notification_message — preferred, immediate return
15
+ * 2. root runtime_errorfail fast
16
+ * 3. root runtime_done contentfallback for one-shot agents
17
+ * 4. root done response-stream after root runtime_done — streaming fallback
16
18
  */
17
19
  export async function pollForResponse(client, sessionId, afterEventId, maxWaitTime = 60000) {
18
20
  const startTime = Date.now();
19
21
  let fromId = afterEventId;
22
+ let responseStreamDone = null;
20
23
  while (Date.now() - startTime < maxWaitTime) {
21
24
  const events = await fetchEvents(client, sessionId, { fromId });
22
25
  let lastAgentRuntimeDone = null;
26
+ let rootRuntimeDone = false;
23
27
  for (const event of events) {
24
28
  debug(`pollForResponse event: ${event.type}`);
25
29
  if (event.type === 'agent_notification_message') {
26
- return { response: event.content.data, lastEventId: event.id };
30
+ if (isResponseStreamEvent(event)) {
31
+ if (isRootTaskEvent(event) && isDoneResponseStreamEvent(event)) {
32
+ responseStreamDone = {
33
+ response: getAgentNotificationText(event),
34
+ lastEventId: event.id,
35
+ };
36
+ }
37
+ continue;
38
+ }
39
+ if (!isRootTaskEvent(event))
40
+ continue;
41
+ return {
42
+ response: getAgentNotificationText(event),
43
+ lastEventId: event.id,
44
+ };
27
45
  }
28
46
  if (event.type === 'runtime_done' &&
29
- event.content !== undefined &&
30
47
  event.task &&
31
- 'agent' in event.task) {
32
- lastAgentRuntimeDone = JSON.stringify(event.content);
48
+ 'agent' in event.task &&
49
+ isRootTaskEvent(event)) {
50
+ rootRuntimeDone = true;
51
+ if (event.content !== undefined) {
52
+ lastAgentRuntimeDone = JSON.stringify(event.content);
53
+ }
33
54
  }
34
- if (event.type === 'runtime_error' && event.task && 'agent' in event.task) {
55
+ if (event.type === 'runtime_error' &&
56
+ event.task &&
57
+ 'agent' in event.task &&
58
+ isRootTaskEvent(event)) {
35
59
  return {
36
60
  response: JSON.stringify({ error: event.content }),
37
61
  lastEventId: event.id,
@@ -47,9 +71,14 @@ export async function pollForResponse(client, sessionId, afterEventId, maxWaitTi
47
71
  if (lastAgentRuntimeDone !== null) {
48
72
  return { response: lastAgentRuntimeDone, lastEventId: fromId };
49
73
  }
74
+ if (rootRuntimeDone && responseStreamDone !== null) {
75
+ return { response: responseStreamDone.response, lastEventId: fromId };
76
+ }
50
77
  await new Promise((resolve) => setTimeout(resolve, 2000));
51
78
  }
52
- return { response: null, lastEventId: fromId };
79
+ return responseStreamDone
80
+ ? { response: responseStreamDone.response, lastEventId: fromId }
81
+ : { response: null, lastEventId: fromId };
53
82
  }
54
83
  /**
55
84
  * Poll for agent response while streaming matching events to stdout as JSONL.
@@ -61,25 +90,49 @@ export async function pollForResponse(client, sessionId, afterEventId, maxWaitTi
61
90
  export async function pollForResponseWithEvents(client, sessionId, eventFilter, afterEventId, maxWaitTime = 60000) {
62
91
  const startTime = Date.now();
63
92
  let fromId = afterEventId;
93
+ let responseStreamDone = null;
64
94
  while (Date.now() - startTime < maxWaitTime) {
65
95
  const events = await fetchEvents(client, sessionId, { fromId });
66
96
  let lastAgentRuntimeDone = null;
97
+ let rootRuntimeDone = false;
67
98
  for (const event of events) {
68
99
  debug(`pollForResponseWithEvents event: ${event.type}`);
69
- // Stream matching events to stdout as JSONL
70
- if (shouldShowEvent(event.type, eventFilter)) {
100
+ // Stream matching events to stdout as JSONL. Response-stream events are
101
+ // transient drafts, so keep automation output stable and wait for the
102
+ // final agent message or terminal fallback.
103
+ if (shouldShowEvent(event.type, eventFilter) && !isResponseStreamEvent(event)) {
71
104
  process.stdout.write(JSON.stringify(event) + '\n');
72
105
  }
73
106
  if (event.type === 'agent_notification_message') {
74
- return { response: event.content.data, lastEventId: event.id };
107
+ if (isResponseStreamEvent(event)) {
108
+ if (isRootTaskEvent(event) && isDoneResponseStreamEvent(event)) {
109
+ responseStreamDone = {
110
+ response: getAgentNotificationText(event),
111
+ lastEventId: event.id,
112
+ };
113
+ }
114
+ continue;
115
+ }
116
+ if (!isRootTaskEvent(event))
117
+ continue;
118
+ return {
119
+ response: getAgentNotificationText(event),
120
+ lastEventId: event.id,
121
+ };
75
122
  }
76
123
  if (event.type === 'runtime_done' &&
77
- event.content !== undefined &&
78
124
  event.task &&
79
- 'agent' in event.task) {
80
- lastAgentRuntimeDone = JSON.stringify(event.content);
125
+ 'agent' in event.task &&
126
+ isRootTaskEvent(event)) {
127
+ rootRuntimeDone = true;
128
+ if (event.content !== undefined) {
129
+ lastAgentRuntimeDone = JSON.stringify(event.content);
130
+ }
81
131
  }
82
- if (event.type === 'runtime_error' && event.task && 'agent' in event.task) {
132
+ if (event.type === 'runtime_error' &&
133
+ event.task &&
134
+ 'agent' in event.task &&
135
+ isRootTaskEvent(event)) {
83
136
  return {
84
137
  response: JSON.stringify({ error: event.content }),
85
138
  lastEventId: event.id,
@@ -95,8 +148,13 @@ export async function pollForResponseWithEvents(client, sessionId, eventFilter,
95
148
  if (lastAgentRuntimeDone !== null) {
96
149
  return { response: lastAgentRuntimeDone, lastEventId: fromId };
97
150
  }
151
+ if (rootRuntimeDone && responseStreamDone !== null) {
152
+ return { response: responseStreamDone.response, lastEventId: fromId };
153
+ }
98
154
  await new Promise((resolve) => setTimeout(resolve, 2000));
99
155
  }
100
- return { response: null, lastEventId: fromId };
156
+ return responseStreamDone
157
+ ? { response: responseStreamDone.response, lastEventId: fromId }
158
+ : { response: null, lastEventId: fromId };
101
159
  }
102
160
  //# 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 { getAgentNotificationText, isResponseStreamEvent, } from './session-events.js';
10
11
  import { fetchEvents } from './session-events-fetch.js';
11
12
  import { brand, code as codeColor } from './colors.js';
12
13
  // Configure marked for terminal rendering (same config as chat.tsx)
@@ -64,7 +65,9 @@ export function eventsToDisplayMessages(events) {
64
65
  });
65
66
  }
66
67
  else if (event.type === 'agent_notification_message') {
67
- const text = typeof event.content === 'string' ? event.content : event.content?.data || '';
68
+ if (isResponseStreamEvent(event))
69
+ continue;
70
+ const text = getAgentNotificationText(event);
68
71
  if (text.trim()) {
69
72
  const rendered = renderMarkdown(text);
70
73
  const messageContent = `${chalk.green('●')} ${chalk.bold('assistant')}\n${rendered.trim()}`;
@@ -8,6 +8,10 @@ export declare function isInteractive(): boolean;
8
8
  * accidentally pipe input without --mode get clear guidance instead of a hang.
9
9
  */
10
10
  export declare function ensureInteractiveStdin(command: string): void;
11
+ /**
12
+ * Read raw text from stdin with a timeout.
13
+ */
14
+ export declare function readStdinAsText(): Promise<string>;
11
15
  /**
12
16
  * Read JSON from stdin with a timeout.
13
17
  */
package/dist/lib/stdin.js CHANGED
@@ -24,6 +24,29 @@ export function ensureInteractiveStdin(command) {
24
24
  process.exit(1);
25
25
  }
26
26
  }
27
+ /**
28
+ * Read raw text from stdin with a timeout.
29
+ */
30
+ export function readStdinAsText() {
31
+ return new Promise((resolve, reject) => {
32
+ let input = '';
33
+ const timeout = setTimeout(() => {
34
+ reject(new Error('Timeout waiting for stdin'));
35
+ }, 5000);
36
+ process.stdin.setEncoding('utf8');
37
+ process.stdin.on('data', (chunk) => {
38
+ input += chunk;
39
+ });
40
+ process.stdin.on('end', () => {
41
+ clearTimeout(timeout);
42
+ resolve(input);
43
+ });
44
+ process.stdin.on('error', (error) => {
45
+ clearTimeout(timeout);
46
+ reject(error);
47
+ });
48
+ });
49
+ }
27
50
  /**
28
51
  * Read JSON from stdin with a timeout.
29
52
  */
@@ -0,0 +1,19 @@
1
+ interface ValidationSuccess {
2
+ valid: true;
3
+ }
4
+ interface ValidationFailure {
5
+ valid: false;
6
+ errors: Array<{
7
+ message: string;
8
+ path?: string[];
9
+ }>;
10
+ }
11
+ interface ValidationSkipped {
12
+ valid: true;
13
+ skipped: true;
14
+ reason: string;
15
+ }
16
+ export type ValidationResult = ValidationSuccess | ValidationFailure | ValidationSkipped;
17
+ export declare function validateInputSchema(agentDir: string, inputs: unknown[]): Promise<ValidationResult>;
18
+ export {};
19
+ //# sourceMappingURL=validate-input-schema.d.ts.map