@guildai/cli 0.10.0 → 0.11.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.
- package/dist/commands/agent/chat.js +10 -7
- package/dist/commands/agent/clone.js +2 -0
- package/dist/commands/agent/fork.js +2 -0
- package/dist/commands/agent/init.js +57 -44
- package/dist/commands/agent/list.js +2 -2
- package/dist/commands/agent/logs.d.ts +3 -0
- package/dist/commands/agent/logs.js +62 -0
- package/dist/commands/agent/owners.js +3 -3
- package/dist/commands/agent/pull.js +8 -12
- package/dist/commands/agent/save.js +2 -3
- package/dist/commands/agent/search.js +2 -2
- package/dist/commands/agent/test.js +9 -6
- package/dist/commands/agent/update.js +9 -1
- package/dist/commands/agent/versions.js +2 -2
- package/dist/commands/agent/workspaces.js +2 -2
- package/dist/commands/auth/login.js +1 -3
- package/dist/commands/chat.js +99 -28
- package/dist/commands/config/get.js +4 -4
- package/dist/commands/config/list.js +2 -3
- package/dist/commands/config/path.js +2 -3
- package/dist/commands/config/set.js +12 -12
- package/dist/commands/credentials/endpoint-list.js +2 -2
- package/dist/commands/credentials/list.js +2 -2
- package/dist/commands/credentials/policy-list.js +2 -2
- package/dist/commands/doctor.js +5 -5
- package/dist/commands/integration/connect.js +2 -2
- package/dist/commands/integration/create.js +2 -2
- package/dist/commands/integration/get.js +2 -2
- package/dist/commands/integration/list.js +2 -2
- package/dist/commands/integration/operation/create.js +4 -4
- package/dist/commands/integration/operation/list.js +2 -2
- package/dist/commands/integration/update.js +2 -2
- package/dist/commands/integration/version/build.js +2 -2
- package/dist/commands/integration/version/create.js +2 -2
- package/dist/commands/integration/version/get.js +2 -2
- package/dist/commands/integration/version/list.js +2 -2
- package/dist/commands/integration/version/publish.js +2 -2
- package/dist/commands/integration/version/test.js +2 -2
- package/dist/commands/job/get.js +2 -2
- package/dist/commands/session/list.js +2 -2
- package/dist/commands/session/tasks.js +2 -2
- package/dist/commands/setup.d.ts +16 -0
- package/dist/commands/setup.js +76 -46
- package/dist/commands/trigger/list.js +2 -2
- package/dist/commands/workspace/agent/list.js +2 -2
- package/dist/commands/workspace/context/list.js +2 -2
- package/dist/commands/workspace/list.js +2 -2
- package/dist/index.js +15 -4
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +2 -2
- package/dist/lib/output-mode.d.ts +9 -2
- package/dist/lib/output-mode.js +23 -2
- package/dist/lib/output.d.ts +7 -1
- package/dist/lib/output.js +32 -1
- package/dist/lib/session-events.d.ts +13 -2
- package/dist/lib/session-events.js +15 -1
- package/dist/lib/session-polling.js +9 -3
- package/dist/lib/session-resume.d.ts +15 -1
- package/dist/lib/session-resume.js +149 -16
- package/dist/lib/splash.js +3 -2
- package/dist/lib/stdin.d.ts +5 -1
- package/dist/lib/stdin.js +8 -1
- package/dist/lib/version-helpers.js +24 -8
- package/package.json +1 -1
package/dist/lib/output-mode.js
CHANGED
|
@@ -4,21 +4,42 @@
|
|
|
4
4
|
* Output mode detection for the CLI
|
|
5
5
|
*
|
|
6
6
|
* Interactive mode (default): Colors, tables, spinners, interactive
|
|
7
|
-
* JSON mode (--json): Pure JSON, non-interactive, machine-readable
|
|
7
|
+
* JSON mode (--mode json or --json): Pure JSON, non-interactive, machine-readable
|
|
8
|
+
* JSONL mode (--mode jsonl): Line-delimited JSON for streaming
|
|
8
9
|
*/
|
|
9
10
|
import { getConfigFlag } from './config-cache.js';
|
|
10
11
|
/**
|
|
11
12
|
* Detect output mode from CLI flags or config
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
+
* Priority: --mode <value> > --json (backward compat) > config
|
|
14
15
|
* Does NOT auto-detect pipes (user must request JSON explicitly).
|
|
15
16
|
*/
|
|
16
17
|
export function getOutputMode() {
|
|
18
|
+
// Check --mode <value> first (primary flag)
|
|
19
|
+
const modeIndex = process.argv.indexOf('--mode');
|
|
20
|
+
if (modeIndex !== -1 && modeIndex + 1 < process.argv.length) {
|
|
21
|
+
const value = process.argv[modeIndex + 1];
|
|
22
|
+
if (value === 'json' || value === 'jsonl') {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
// --mode interactive falls through to default
|
|
26
|
+
}
|
|
27
|
+
// Backward compat: --json is equivalent to --mode json
|
|
17
28
|
if (process.argv.includes('--json') || getConfigFlag('json')) {
|
|
18
29
|
return 'json';
|
|
19
30
|
}
|
|
20
31
|
return 'interactive';
|
|
21
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if the current output mode is machine-readable (json or jsonl).
|
|
35
|
+
*
|
|
36
|
+
* Use this in list/get commands instead of `getOutputMode() === 'json'`
|
|
37
|
+
* so that both --mode json and --mode jsonl trigger non-interactive output.
|
|
38
|
+
*/
|
|
39
|
+
export function isMachineReadable() {
|
|
40
|
+
const mode = getOutputMode();
|
|
41
|
+
return mode === 'json' || mode === 'jsonl';
|
|
42
|
+
}
|
|
22
43
|
/**
|
|
23
44
|
* Check if quiet mode is enabled (CLI flag or config)
|
|
24
45
|
*
|
package/dist/lib/output.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Spinner } from './progress.js';
|
|
2
|
-
import type { Agent, AgentVersion, Credentials, CredentialsPolicy, Context, Integration, IntegrationVersion, JobStep, Pagination, Session, Task, Workspace, WorkspaceAgent, Trigger } from './api-types.js';
|
|
2
|
+
import type { Agent, AgentVersion, Credentials, CredentialsPolicy, Context, Integration, IntegrationVersion, JobStep, Pagination, Session, Task, ValidationStep, 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.
|
|
@@ -57,6 +57,12 @@ export declare function formatPoliciesTable(policies: CredentialsPolicy[], pagin
|
|
|
57
57
|
* Used by job get command.
|
|
58
58
|
*/
|
|
59
59
|
export declare function formatJobStepTable(steps: JobStep[]): void;
|
|
60
|
+
/**
|
|
61
|
+
* Format validation step logs as human-readable output.
|
|
62
|
+
* Prints each step's name, colored status, and full log content.
|
|
63
|
+
* Used by the `guild agent logs` command.
|
|
64
|
+
*/
|
|
65
|
+
export declare function formatValidationLogs(steps: ValidationStep[]): void;
|
|
60
66
|
/**
|
|
61
67
|
* Output writer interface for consistent CLI output
|
|
62
68
|
*
|
package/dist/lib/output.js
CHANGED
|
@@ -506,6 +506,35 @@ export function formatJobStepTable(steps) {
|
|
|
506
506
|
});
|
|
507
507
|
table.printTable();
|
|
508
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Format validation step logs as human-readable output.
|
|
511
|
+
* Prints each step's name, colored status, and full log content.
|
|
512
|
+
* Used by the `guild agent logs` command.
|
|
513
|
+
*/
|
|
514
|
+
export function formatValidationLogs(steps) {
|
|
515
|
+
if (steps.length === 0) {
|
|
516
|
+
console.log(chalk.dim('No steps found'));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const hr = chalk.dim('─'.repeat(60));
|
|
520
|
+
steps.forEach((step, index) => {
|
|
521
|
+
if (index > 0) {
|
|
522
|
+
console.log(hr);
|
|
523
|
+
}
|
|
524
|
+
const statusColor = step.status === 'SUCCEEDED'
|
|
525
|
+
? chalk.green
|
|
526
|
+
: step.status === 'FAILED' || step.status === 'ERRORED'
|
|
527
|
+
? chalk.red
|
|
528
|
+
: step.status === 'RUNNING'
|
|
529
|
+
? chalk.yellow
|
|
530
|
+
: chalk.dim;
|
|
531
|
+
console.log(`${chalk.bold(step.name)} ${statusColor(step.status)}`);
|
|
532
|
+
if (step.content) {
|
|
533
|
+
console.log();
|
|
534
|
+
console.log(step.content);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
509
538
|
/**
|
|
510
539
|
* Human-friendly output writer
|
|
511
540
|
*
|
|
@@ -573,7 +602,9 @@ export class JSONOutputWriter {
|
|
|
573
602
|
*/
|
|
574
603
|
export function createOutputWriter() {
|
|
575
604
|
const mode = getOutputMode();
|
|
576
|
-
return mode === '
|
|
605
|
+
return mode === 'interactive'
|
|
606
|
+
? new InteractiveOutputWriter()
|
|
607
|
+
: new JSONOutputWriter();
|
|
577
608
|
}
|
|
578
609
|
/**
|
|
579
610
|
* Create a no-op spinner (for quiet/JSON modes)
|
|
@@ -97,14 +97,23 @@ export interface TextContent {
|
|
|
97
97
|
type: 'text';
|
|
98
98
|
data: string;
|
|
99
99
|
}
|
|
100
|
+
export interface UnknownContent {
|
|
101
|
+
type: string;
|
|
102
|
+
data?: unknown;
|
|
103
|
+
}
|
|
104
|
+
export interface MultipartContent {
|
|
105
|
+
type: 'multipart/mixed';
|
|
106
|
+
parts: UnknownContent[];
|
|
107
|
+
}
|
|
100
108
|
export interface ResponseStreamContent {
|
|
101
109
|
type: 'application/guild-response-stream';
|
|
102
110
|
stream_id: string;
|
|
103
111
|
sequence: number;
|
|
104
|
-
status: 'streaming' | 'done' | 'aborted';
|
|
112
|
+
status: 'streaming' | 'done' | 'continued' | 'aborted';
|
|
105
113
|
text: string;
|
|
114
|
+
is_delta?: boolean;
|
|
106
115
|
}
|
|
107
|
-
export type AgentNotificationMessageContent = TextContent | ResponseStreamContent;
|
|
116
|
+
export type AgentNotificationMessageContent = TextContent | MultipartContent | ResponseStreamContent;
|
|
108
117
|
export interface AgentNotificationMessageEvent extends BaseEvent {
|
|
109
118
|
type: 'agent_notification_message';
|
|
110
119
|
content: AgentNotificationMessageContent;
|
|
@@ -181,6 +190,8 @@ export declare function isDoneResponseStreamEvent(event: SessionEvent): event is
|
|
|
181
190
|
status: 'done';
|
|
182
191
|
};
|
|
183
192
|
};
|
|
193
|
+
/** Apply a response-stream payload to the accumulated display text. */
|
|
194
|
+
export declare function applyResponseStreamText(currentText: string, content: ResponseStreamContent): string;
|
|
184
195
|
/** Extract display text from notification content. */
|
|
185
196
|
export declare function getAgentNotificationText(event: AgentNotificationMessageEvent): string;
|
|
186
197
|
/** Check if an event belongs to the root task or has no task context. */
|
|
@@ -96,9 +96,23 @@ export function isResponseStreamEvent(event) {
|
|
|
96
96
|
export function isDoneResponseStreamEvent(event) {
|
|
97
97
|
return isResponseStreamEvent(event) && event.content.status === 'done';
|
|
98
98
|
}
|
|
99
|
+
/** Apply a response-stream payload to the accumulated display text. */
|
|
100
|
+
export function applyResponseStreamText(currentText, content) {
|
|
101
|
+
return content.is_delta ? currentText + content.text : content.text;
|
|
102
|
+
}
|
|
99
103
|
/** Extract display text from notification content. */
|
|
100
104
|
export function getAgentNotificationText(event) {
|
|
101
|
-
|
|
105
|
+
if (event.content.type === 'text') {
|
|
106
|
+
return event.content.data;
|
|
107
|
+
}
|
|
108
|
+
if (event.content.type === 'application/guild-response-stream') {
|
|
109
|
+
return event.content.text;
|
|
110
|
+
}
|
|
111
|
+
if (event.content.type === 'multipart/mixed' && event.content.parts.length === 1) {
|
|
112
|
+
const part = event.content.parts[0];
|
|
113
|
+
return part.type === 'text' && typeof part.data === 'string' ? part.data : '';
|
|
114
|
+
}
|
|
115
|
+
return '';
|
|
102
116
|
}
|
|
103
117
|
/** Check if an event belongs to the root task or has no task context. */
|
|
104
118
|
export function isRootTaskEvent(event) {
|
|
@@ -3,7 +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
|
+
import { applyResponseStreamText, getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from './session-events.js';
|
|
7
7
|
/**
|
|
8
8
|
* Poll for agent response using from_id cursor.
|
|
9
9
|
*
|
|
@@ -20,6 +20,7 @@ export async function pollForResponse(client, sessionId, afterEventId, maxWaitTi
|
|
|
20
20
|
const startTime = Date.now();
|
|
21
21
|
let fromId = afterEventId;
|
|
22
22
|
let responseStreamDone = null;
|
|
23
|
+
const responseStreamTexts = new Map();
|
|
23
24
|
while (Date.now() - startTime < maxWaitTime) {
|
|
24
25
|
const events = await fetchEvents(client, sessionId, { fromId });
|
|
25
26
|
let lastAgentRuntimeDone = null;
|
|
@@ -28,9 +29,11 @@ export async function pollForResponse(client, sessionId, afterEventId, maxWaitTi
|
|
|
28
29
|
debug(`pollForResponse event: ${event.type}`);
|
|
29
30
|
if (event.type === 'agent_notification_message') {
|
|
30
31
|
if (isResponseStreamEvent(event)) {
|
|
32
|
+
const streamText = applyResponseStreamText(responseStreamTexts.get(event.content.stream_id) ?? '', event.content);
|
|
33
|
+
responseStreamTexts.set(event.content.stream_id, streamText);
|
|
31
34
|
if (isRootTaskEvent(event) && isDoneResponseStreamEvent(event)) {
|
|
32
35
|
responseStreamDone = {
|
|
33
|
-
response:
|
|
36
|
+
response: streamText,
|
|
34
37
|
lastEventId: event.id,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -91,6 +94,7 @@ export async function pollForResponseWithEvents(client, sessionId, eventFilter,
|
|
|
91
94
|
const startTime = Date.now();
|
|
92
95
|
let fromId = afterEventId;
|
|
93
96
|
let responseStreamDone = null;
|
|
97
|
+
const responseStreamTexts = new Map();
|
|
94
98
|
while (Date.now() - startTime < maxWaitTime) {
|
|
95
99
|
const events = await fetchEvents(client, sessionId, { fromId });
|
|
96
100
|
let lastAgentRuntimeDone = null;
|
|
@@ -105,9 +109,11 @@ export async function pollForResponseWithEvents(client, sessionId, eventFilter,
|
|
|
105
109
|
}
|
|
106
110
|
if (event.type === 'agent_notification_message') {
|
|
107
111
|
if (isResponseStreamEvent(event)) {
|
|
112
|
+
const streamText = applyResponseStreamText(responseStreamTexts.get(event.content.stream_id) ?? '', event.content);
|
|
113
|
+
responseStreamTexts.set(event.content.stream_id, streamText);
|
|
108
114
|
if (isRootTaskEvent(event) && isDoneResponseStreamEvent(event)) {
|
|
109
115
|
responseStreamDone = {
|
|
110
|
-
response:
|
|
116
|
+
response: streamText,
|
|
111
117
|
lastEventId: event.id,
|
|
112
118
|
};
|
|
113
119
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GuildAPIClient } from './api-client.js';
|
|
2
|
-
import { SessionEvent, Session } from './session-events.js';
|
|
2
|
+
import { type ResponseStreamContent, SessionEvent, Session } from './session-events.js';
|
|
3
3
|
/**
|
|
4
4
|
* Print a dimmed resume hint to stderr on session exit.
|
|
5
5
|
* Only call when a session ID is available.
|
|
@@ -19,6 +19,20 @@ export interface DisplayMessage {
|
|
|
19
19
|
type: 'user' | 'assistant' | 'progress';
|
|
20
20
|
timestamp?: string;
|
|
21
21
|
}
|
|
22
|
+
export interface ResponseStreamResumeState {
|
|
23
|
+
keys: Map<string, string>;
|
|
24
|
+
contents: Map<string, ResponseStreamContent[]>;
|
|
25
|
+
texts: Map<string, string>;
|
|
26
|
+
timestamps: Map<string, string>;
|
|
27
|
+
statuses: Map<string, ResponseStreamContent['status']>;
|
|
28
|
+
keysByTask: Map<string, Set<string>>;
|
|
29
|
+
}
|
|
30
|
+
export interface SessionResumeDisplay {
|
|
31
|
+
displayMessages: DisplayMessage[];
|
|
32
|
+
responseStreamState: ResponseStreamResumeState;
|
|
33
|
+
}
|
|
34
|
+
export declare function responseStreamResumeStateFromEvents(events: SessionEvent[]): ResponseStreamResumeState;
|
|
35
|
+
export declare function prepareSessionResumeDisplay(events: SessionEvent[]): SessionResumeDisplay;
|
|
22
36
|
/**
|
|
23
37
|
* Convert session events to display messages for resume.
|
|
24
38
|
* Maps user_message, agent_notification_message, and agent_notification_error
|
|
@@ -7,7 +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
|
+
import { applyResponseStreamText, getAgentNotificationText, isResponseStreamEvent, isRootTaskEvent, } from './session-events.js';
|
|
11
11
|
import { fetchEvents } from './session-events-fetch.js';
|
|
12
12
|
import { brand, code as codeColor } from './colors.js';
|
|
13
13
|
// Configure marked for terminal rendering (same config as chat.tsx)
|
|
@@ -48,13 +48,127 @@ export async function fetchSession(client, sessionId) {
|
|
|
48
48
|
const session = (await client.get(`/sessions/${sessionId}`));
|
|
49
49
|
return session;
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
function renderAssistantText(key, text, timestamp) {
|
|
52
|
+
const rendered = renderMarkdown(text);
|
|
53
|
+
const messageContent = `${chalk.green('●')} ${chalk.bold('assistant')}\n${rendered.trim()}`;
|
|
54
|
+
return {
|
|
55
|
+
key,
|
|
56
|
+
content: messageContent,
|
|
57
|
+
type: 'assistant',
|
|
58
|
+
timestamp,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function textMatches(left, right) {
|
|
62
|
+
return left === right;
|
|
63
|
+
}
|
|
64
|
+
function formatDisplayTimestamp(timestamp) {
|
|
65
|
+
if (timestamp === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
const date = new Date(timestamp);
|
|
68
|
+
if (Number.isNaN(date.getTime()))
|
|
69
|
+
return timestamp;
|
|
70
|
+
return date.toLocaleTimeString();
|
|
71
|
+
}
|
|
72
|
+
function upsertResponseStreamEvent(events, event) {
|
|
73
|
+
const existingIndex = events.findIndex((existing) => existing.content.sequence === event.content.sequence);
|
|
74
|
+
if (existingIndex === -1) {
|
|
75
|
+
events.push(event);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
events[existingIndex] = event;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function collectResponseStreamResumeEntries(events) {
|
|
82
|
+
const responseStreamGroups = new Map();
|
|
83
|
+
const finalMessagesByTask = new Map();
|
|
84
|
+
for (const [index, event] of events.entries()) {
|
|
85
|
+
if (event.type !== 'agent_notification_message')
|
|
86
|
+
continue;
|
|
87
|
+
if (!isRootTaskEvent(event))
|
|
88
|
+
continue;
|
|
89
|
+
if (isResponseStreamEvent(event)) {
|
|
90
|
+
const streamId = event.content.stream_id;
|
|
91
|
+
const current = responseStreamGroups.get(streamId);
|
|
92
|
+
if (current) {
|
|
93
|
+
upsertResponseStreamEvent(current.events, event);
|
|
94
|
+
// Insertion order and sequence can diverge if backfilled events arrive out
|
|
95
|
+
// of order; use insertion index only for "later final message" dedupe.
|
|
96
|
+
current.lastIndex = Math.max(current.lastIndex, index);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
responseStreamGroups.set(streamId, {
|
|
100
|
+
events: [event],
|
|
101
|
+
lastIndex: index,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const taskId = event.task?.id;
|
|
107
|
+
if (!taskId)
|
|
108
|
+
continue;
|
|
109
|
+
const text = getAgentNotificationText(event);
|
|
110
|
+
if (!text.trim())
|
|
111
|
+
continue;
|
|
112
|
+
const finalMessages = finalMessagesByTask.get(taskId) ?? [];
|
|
113
|
+
finalMessages.push({ index, text });
|
|
114
|
+
finalMessagesByTask.set(taskId, finalMessages);
|
|
115
|
+
}
|
|
116
|
+
const entries = [];
|
|
117
|
+
for (const [streamId, streamGroup] of responseStreamGroups.entries()) {
|
|
118
|
+
const streamEvents = [...streamGroup.events].sort((left, right) => left.content.sequence - right.content.sequence);
|
|
119
|
+
const latest = streamEvents[streamEvents.length - 1];
|
|
120
|
+
const text = streamEvents.reduce((currentText, streamEvent) => applyResponseStreamText(currentText, streamEvent.content), '');
|
|
121
|
+
const taskId = latest.task?.id;
|
|
122
|
+
const matchingLaterFinalMessage = taskId !== undefined &&
|
|
123
|
+
(finalMessagesByTask.get(taskId) ?? []).some((message) => message.index > streamGroup.lastIndex && textMatches(text, message.text));
|
|
124
|
+
if (latest.content.status !== 'aborted' &&
|
|
125
|
+
!matchingLaterFinalMessage &&
|
|
126
|
+
text.trim()) {
|
|
127
|
+
entries.push({
|
|
128
|
+
streamId,
|
|
129
|
+
key: `response-stream-${streamId}`,
|
|
130
|
+
contents: streamEvents.map((event) => event.content),
|
|
131
|
+
text,
|
|
132
|
+
status: latest.content.status,
|
|
133
|
+
timestamp: formatDisplayTimestamp(latest.created_at),
|
|
134
|
+
taskId,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return entries;
|
|
139
|
+
}
|
|
140
|
+
function responseStreamResumeStateFromEntries(entries) {
|
|
141
|
+
const state = {
|
|
142
|
+
keys: new Map(),
|
|
143
|
+
contents: new Map(),
|
|
144
|
+
texts: new Map(),
|
|
145
|
+
timestamps: new Map(),
|
|
146
|
+
statuses: new Map(),
|
|
147
|
+
keysByTask: new Map(),
|
|
148
|
+
};
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
state.keys.set(entry.streamId, entry.key);
|
|
151
|
+
state.contents.set(entry.streamId, entry.contents);
|
|
152
|
+
state.texts.set(entry.streamId, entry.text);
|
|
153
|
+
state.statuses.set(entry.streamId, entry.status);
|
|
154
|
+
if (entry.timestamp !== undefined) {
|
|
155
|
+
state.timestamps.set(entry.streamId, entry.timestamp);
|
|
156
|
+
}
|
|
157
|
+
if (entry.taskId !== undefined) {
|
|
158
|
+
const keys = state.keysByTask.get(entry.taskId) ?? new Set();
|
|
159
|
+
keys.add(entry.key);
|
|
160
|
+
state.keysByTask.set(entry.taskId, keys);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return state;
|
|
164
|
+
}
|
|
165
|
+
export function responseStreamResumeStateFromEvents(events) {
|
|
166
|
+
return responseStreamResumeStateFromEntries(collectResponseStreamResumeEntries(events));
|
|
167
|
+
}
|
|
168
|
+
function eventsToDisplayMessagesWithResponseStreamEntries(events, responseStreamEntries) {
|
|
57
169
|
const messages = [];
|
|
170
|
+
const responseStreamEntriesById = new Map(responseStreamEntries.map((entry) => [entry.streamId, entry]));
|
|
171
|
+
const renderedResponseStreamIds = new Set();
|
|
58
172
|
for (const event of events) {
|
|
59
173
|
if (event.type === 'user_message') {
|
|
60
174
|
messages.push({
|
|
@@ -65,18 +179,22 @@ export function eventsToDisplayMessages(events) {
|
|
|
65
179
|
});
|
|
66
180
|
}
|
|
67
181
|
else if (event.type === 'agent_notification_message') {
|
|
68
|
-
if (isResponseStreamEvent(event))
|
|
182
|
+
if (isResponseStreamEvent(event)) {
|
|
183
|
+
if (!isRootTaskEvent(event))
|
|
184
|
+
continue;
|
|
185
|
+
const streamId = event.content.stream_id;
|
|
186
|
+
if (renderedResponseStreamIds.has(streamId))
|
|
187
|
+
continue;
|
|
188
|
+
const streamEntry = responseStreamEntriesById.get(streamId);
|
|
189
|
+
if (!streamEntry)
|
|
190
|
+
continue;
|
|
191
|
+
renderedResponseStreamIds.add(streamId);
|
|
192
|
+
messages.push(renderAssistantText(streamEntry.key, streamEntry.text, streamEntry.timestamp));
|
|
69
193
|
continue;
|
|
194
|
+
}
|
|
70
195
|
const text = getAgentNotificationText(event);
|
|
71
196
|
if (text.trim()) {
|
|
72
|
-
|
|
73
|
-
const messageContent = `${chalk.green('●')} ${chalk.bold('assistant')}\n${rendered.trim()}`;
|
|
74
|
-
messages.push({
|
|
75
|
-
key: event.id,
|
|
76
|
-
content: messageContent,
|
|
77
|
-
type: 'assistant',
|
|
78
|
-
timestamp: event.created_at,
|
|
79
|
-
});
|
|
197
|
+
messages.push(renderAssistantText(event.id, text, event.created_at));
|
|
80
198
|
}
|
|
81
199
|
}
|
|
82
200
|
else if (event.type === 'agent_notification_error') {
|
|
@@ -93,4 +211,19 @@ export function eventsToDisplayMessages(events) {
|
|
|
93
211
|
}
|
|
94
212
|
return messages;
|
|
95
213
|
}
|
|
214
|
+
export function prepareSessionResumeDisplay(events) {
|
|
215
|
+
const responseStreamEntries = collectResponseStreamResumeEntries(events);
|
|
216
|
+
return {
|
|
217
|
+
displayMessages: eventsToDisplayMessagesWithResponseStreamEntries(events, responseStreamEntries),
|
|
218
|
+
responseStreamState: responseStreamResumeStateFromEntries(responseStreamEntries),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Convert session events to display messages for resume.
|
|
223
|
+
* Maps user_message, agent_notification_message, and agent_notification_error
|
|
224
|
+
* to DisplayMessage objects. Skips progress, console, runtime events.
|
|
225
|
+
*/
|
|
226
|
+
export function eventsToDisplayMessages(events) {
|
|
227
|
+
return prepareSessionResumeDisplay(events).displayMessages;
|
|
228
|
+
}
|
|
96
229
|
//# sourceMappingURL=session-resume.js.map
|
package/dist/lib/splash.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import LOGO from './logo.js';
|
|
5
5
|
import { brand } from './colors.js';
|
|
6
|
+
import { isMachineReadable } from './output-mode.js';
|
|
6
7
|
/**
|
|
7
8
|
* Show splash screen with logo
|
|
8
9
|
*/
|
|
@@ -21,8 +22,8 @@ export function showSplashScreen(options) {
|
|
|
21
22
|
* Never for: --json, data commands, action commands
|
|
22
23
|
*/
|
|
23
24
|
export function shouldShowSplash() {
|
|
24
|
-
// Never in
|
|
25
|
-
if (
|
|
25
|
+
// Never in machine-readable mode (--mode json, --mode jsonl, --json)
|
|
26
|
+
if (isMachineReadable()) {
|
|
26
27
|
return false;
|
|
27
28
|
}
|
|
28
29
|
const args = process.argv.slice(2);
|
package/dist/lib/stdin.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Check if
|
|
2
|
+
* Check if the CLI is running in interactive mode.
|
|
3
|
+
*
|
|
4
|
+
* Returns false when:
|
|
5
|
+
* - stdin is not a TTY (piped input, CI, cron)
|
|
6
|
+
* - the global --non-interactive flag is set
|
|
3
7
|
*/
|
|
4
8
|
export declare function isInteractive(): boolean;
|
|
5
9
|
/**
|
package/dist/lib/stdin.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
/**
|
|
4
|
-
* Check if
|
|
4
|
+
* Check if the CLI is running in interactive mode.
|
|
5
|
+
*
|
|
6
|
+
* Returns false when:
|
|
7
|
+
* - stdin is not a TTY (piped input, CI, cron)
|
|
8
|
+
* - the global --non-interactive flag is set
|
|
5
9
|
*/
|
|
6
10
|
export function isInteractive() {
|
|
11
|
+
if (process.argv.includes('--non-interactive')) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
7
14
|
return process.stdin.isTTY === true;
|
|
8
15
|
}
|
|
9
16
|
/**
|
|
@@ -66,22 +66,38 @@ export async function waitForValidation(versionId, output) {
|
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Fetch validation step details for a failed version.
|
|
69
|
+
* Shows content from all steps (not just failed) so the full build output is visible.
|
|
69
70
|
*/
|
|
70
71
|
async function fetchValidationFailureDetails(versionId) {
|
|
71
72
|
const client = new GuildAPIClient();
|
|
72
73
|
let failureDetails = '';
|
|
73
74
|
try {
|
|
74
75
|
const stepsResponse = await client.get(`/versions/${versionId}/validation/steps`);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
if (stepsResponse.steps.length > 0) {
|
|
77
|
+
failureDetails = stepsResponse.steps
|
|
78
|
+
.map((step) => {
|
|
79
|
+
let icon;
|
|
80
|
+
switch (step.status) {
|
|
81
|
+
case 'SUCCEEDED':
|
|
82
|
+
icon = '✓';
|
|
83
|
+
break;
|
|
84
|
+
case 'FAILED':
|
|
85
|
+
case 'ERRORED':
|
|
86
|
+
icon = '✗';
|
|
87
|
+
break;
|
|
88
|
+
case 'RUNNING':
|
|
89
|
+
icon = '⟳';
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
icon = '○';
|
|
93
|
+
}
|
|
94
|
+
const header = `${icon} ${step.name} [${step.status}]`;
|
|
95
|
+
return step.content ? `${header}\n${step.content}` : header;
|
|
96
|
+
})
|
|
97
|
+
.join('\n\n');
|
|
82
98
|
}
|
|
83
99
|
else {
|
|
84
|
-
failureDetails = 'No
|
|
100
|
+
failureDetails = 'No steps found. Check validation logs for details.';
|
|
85
101
|
}
|
|
86
102
|
}
|
|
87
103
|
catch {
|