@camerontaylor/paseo-cli 0.11.2-fork.2 → 0.12.0-beta.1.fork.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/cli.js +2 -0
- package/dist/commands/agent/logs.d.ts +19 -2
- package/dist/commands/agent/logs.js +143 -12
- package/dist/commands/agent/ls.d.ts +17 -2
- package/dist/commands/agent/ls.js +24 -6
- package/dist/commands/agent/run.d.ts +3 -14
- package/dist/commands/agent/run.js +20 -29
- package/dist/commands/agent/send.js +4 -1
- package/dist/commands/permit/allow.d.ts +17 -2
- package/dist/commands/permit/allow.js +96 -82
- package/dist/commands/permit/deny.d.ts +3 -2
- package/dist/commands/permit/deny.js +85 -71
- package/dist/commands/usage/index.d.ts +29 -0
- package/dist/commands/usage/index.js +62 -0
- package/dist/commands/usage/presentation.d.ts +4 -0
- package/dist/commands/usage/presentation.js +122 -0
- package/dist/commands/workspace/create.d.ts +1 -0
- package/dist/commands/workspace/create.js +4 -0
- package/dist/commands/workspace/index.js +7 -2
- package/dist/commands/workspace/ls.d.ts +5 -2
- package/dist/commands/workspace/ls.js +7 -2
- package/dist/commands/workspace/shared.d.ts +3 -0
- package/dist/commands/workspace/shared.js +9 -0
- package/dist/commands/worktree/archive.d.ts +1 -0
- package/dist/commands/worktree/archive.js +3 -1
- package/dist/commands/worktree/index.js +6 -2
- package/dist/commands/worktree/ls.d.ts +1 -0
- package/dist/commands/worktree/ls.js +3 -1
- package/dist/commands/worktree/repository-directory.d.ts +6 -0
- package/dist/commands/worktree/repository-directory.js +9 -0
- package/dist/utils/caller-agent.d.ts +14 -0
- package/dist/utils/caller-agent.js +16 -0
- package/dist/utils/timeline.d.ts +1 -0
- package/dist/utils/timeline.js +6 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command, Option } from "commander";
|
|
|
3
3
|
import { createAgentCommand } from "./commands/agent/index.js";
|
|
4
4
|
import { createDaemonCommand } from "./commands/daemon/index.js";
|
|
5
5
|
import { createPermitCommand } from "./commands/permit/index.js";
|
|
6
|
+
import { createUsageCommand } from "./commands/usage/index.js";
|
|
6
7
|
import { createProviderCommand } from "./commands/provider/index.js";
|
|
7
8
|
import { createPluginCommand } from "./commands/plugin/index.js";
|
|
8
9
|
import { createProjectCommand } from "./commands/project/index.js";
|
|
@@ -93,6 +94,7 @@ export function createCli() {
|
|
|
93
94
|
program.addCommand(createPermitCommand());
|
|
94
95
|
// Provider commands
|
|
95
96
|
program.addCommand(createProviderCommand());
|
|
97
|
+
program.addCommand(createUsageCommand());
|
|
96
98
|
program.addCommand(createPluginCommand());
|
|
97
99
|
// Speech model commands
|
|
98
100
|
program.addCommand(createSpeechCommand());
|
|
@@ -11,9 +11,26 @@ export interface AgentLogsOptions extends CommandOptions {
|
|
|
11
11
|
}
|
|
12
12
|
export type AgentLogsResult = void;
|
|
13
13
|
export declare const NO_ACTIVITY_MESSAGE = "No activity to display.";
|
|
14
|
-
|
|
14
|
+
interface FetchAgentTimelineItemsOptions {
|
|
15
15
|
timeoutMs?: number;
|
|
16
|
-
|
|
16
|
+
sinceTimestampMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function fetchAgentTimelineItems(client: DaemonClient, agentId: string, options?: FetchAgentTimelineItemsOptions): Promise<AgentTimelineItem[]>;
|
|
17
19
|
export declare function formatAgentActivityTranscript(timelineItems: AgentTimelineItem[], tailCount?: number): string;
|
|
20
|
+
export interface FollowTranscriptWriter {
|
|
21
|
+
push(item: AgentTimelineItem, turnId?: string): void;
|
|
22
|
+
end(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Follow mode receives one streaming fragment per event, while the one-shot
|
|
26
|
+
* transcript sees the whole timeline and merges consecutive assistant and
|
|
27
|
+
* reasoning fragments with exact concatenation (`projectTimelineRows`). Printing
|
|
28
|
+
* each fragment on its own line splits words at fragment boundaries and
|
|
29
|
+
* reprints the reasoning prefix, so a chain of fragments is written as one
|
|
30
|
+
* stream instead: text goes out as it arrives, and only the trailing whitespace
|
|
31
|
+
* is held back so the message ends trimmed the way the transcript does.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createFollowTranscriptWriter(write?: (chunk: string) => void, filter?: string): FollowTranscriptWriter;
|
|
18
34
|
export declare function runLogsCommand(id: string, options: AgentLogsOptions, _command: Command): Promise<AgentLogsResult>;
|
|
35
|
+
export {};
|
|
19
36
|
//# sourceMappingURL=logs.d.ts.map
|
|
@@ -13,7 +13,12 @@ export function addLogsOptions(cmd) {
|
|
|
13
13
|
}
|
|
14
14
|
export const NO_ACTIVITY_MESSAGE = "No activity to display.";
|
|
15
15
|
export async function fetchAgentTimelineItems(client, agentId, options) {
|
|
16
|
-
return fetchProjectedTimelineItems({
|
|
16
|
+
return fetchProjectedTimelineItems({
|
|
17
|
+
client,
|
|
18
|
+
agentId,
|
|
19
|
+
timeoutMs: options?.timeoutMs,
|
|
20
|
+
sinceTimestampMs: options?.sinceTimestampMs,
|
|
21
|
+
});
|
|
17
22
|
}
|
|
18
23
|
export function formatAgentActivityTranscript(timelineItems, tailCount) {
|
|
19
24
|
if (tailCount === 0) {
|
|
@@ -21,6 +26,104 @@ export function formatAgentActivityTranscript(timelineItems, tailCount) {
|
|
|
21
26
|
}
|
|
22
27
|
return curateAgentActivity(timelineItems, tailCount !== undefined ? { maxItems: tailCount } : undefined);
|
|
23
28
|
}
|
|
29
|
+
const THOUGHT_PREFIX = "[Thought] ";
|
|
30
|
+
function lastNonWhitespaceIndex(text) {
|
|
31
|
+
for (let index = text.length - 1; index >= 0; index -= 1) {
|
|
32
|
+
if (!/\s/.test(text[index])) {
|
|
33
|
+
return index;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return -1;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Follow mode receives one streaming fragment per event, while the one-shot
|
|
40
|
+
* transcript sees the whole timeline and merges consecutive assistant and
|
|
41
|
+
* reasoning fragments with exact concatenation (`projectTimelineRows`). Printing
|
|
42
|
+
* each fragment on its own line splits words at fragment boundaries and
|
|
43
|
+
* reprints the reasoning prefix, so a chain of fragments is written as one
|
|
44
|
+
* stream instead: text goes out as it arrives, and only the trailing whitespace
|
|
45
|
+
* is held back so the message ends trimmed the way the transcript does.
|
|
46
|
+
*/
|
|
47
|
+
export function createFollowTranscriptWriter(write = (chunk) => void process.stdout.write(chunk), filter) {
|
|
48
|
+
let pending = null;
|
|
49
|
+
const emit = (chain, text) => {
|
|
50
|
+
if (!chain.emitted) {
|
|
51
|
+
chain.emitted = true;
|
|
52
|
+
if (chain.type === "reasoning") {
|
|
53
|
+
write(THOUGHT_PREFIX);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
write(text);
|
|
57
|
+
};
|
|
58
|
+
const flushContent = (chain) => {
|
|
59
|
+
if (!chain.emitted) {
|
|
60
|
+
chain.buffer = chain.buffer.replace(/^\s+/, "");
|
|
61
|
+
}
|
|
62
|
+
const lastContent = lastNonWhitespaceIndex(chain.buffer);
|
|
63
|
+
if (lastContent < 0) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
emit(chain, chain.buffer.slice(0, lastContent + 1));
|
|
67
|
+
chain.buffer = chain.buffer.slice(lastContent + 1);
|
|
68
|
+
};
|
|
69
|
+
const closePending = () => {
|
|
70
|
+
const chain = pending;
|
|
71
|
+
pending = null;
|
|
72
|
+
if (!chain) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const tail = chain.emitted ? chain.buffer.trimEnd() : chain.buffer.trim();
|
|
76
|
+
if (tail) {
|
|
77
|
+
emit(chain, tail);
|
|
78
|
+
}
|
|
79
|
+
if (chain.emitted) {
|
|
80
|
+
write("\n");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const continuesChain = (item, turnId) => {
|
|
84
|
+
if (!pending || pending.type !== item.type || pending.turnId !== turnId) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (item.type !== "assistant_message") {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return item.messageId === undefined || item.messageId === pending.messageId;
|
|
91
|
+
};
|
|
92
|
+
const startChain = (item, turnId) => {
|
|
93
|
+
closePending();
|
|
94
|
+
const chain = {
|
|
95
|
+
type: item.type,
|
|
96
|
+
turnId,
|
|
97
|
+
messageId: item.type === "assistant_message" ? item.messageId : undefined,
|
|
98
|
+
buffer: "",
|
|
99
|
+
emitted: false,
|
|
100
|
+
};
|
|
101
|
+
pending = chain;
|
|
102
|
+
return chain;
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
push(item, turnId) {
|
|
106
|
+
if (!matchesFilter(item, filter)) {
|
|
107
|
+
closePending();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (item.type === "assistant_message" || item.type === "reasoning") {
|
|
111
|
+
const chain = continuesChain(item, turnId) && pending ? pending : startChain(item, turnId);
|
|
112
|
+
chain.buffer += item.text;
|
|
113
|
+
flushContent(chain);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
closePending();
|
|
117
|
+
const transcript = formatAgentActivityTranscript([item]);
|
|
118
|
+
if (transcript && transcript !== NO_ACTIVITY_MESSAGE) {
|
|
119
|
+
write(`${transcript}\n`);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
end() {
|
|
123
|
+
closePending();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
24
127
|
function parseTailCount(raw) {
|
|
25
128
|
if (raw === undefined)
|
|
26
129
|
return undefined;
|
|
@@ -30,6 +133,19 @@ function parseTailCount(raw) {
|
|
|
30
133
|
}
|
|
31
134
|
return parsed;
|
|
32
135
|
}
|
|
136
|
+
function parseSinceTimestamp(raw) {
|
|
137
|
+
if (raw === undefined)
|
|
138
|
+
return undefined;
|
|
139
|
+
const timestampMs = Date.parse(raw);
|
|
140
|
+
if (Number.isNaN(timestampMs)) {
|
|
141
|
+
throw {
|
|
142
|
+
code: "INVALID_TIMESTAMP",
|
|
143
|
+
message: `Invalid --since value: ${raw}`,
|
|
144
|
+
details: "Use a timestamp such as 2026-01-01T00:00:00Z.",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return timestampMs;
|
|
148
|
+
}
|
|
33
149
|
/**
|
|
34
150
|
* Check if a timeline item matches the filter type
|
|
35
151
|
*/
|
|
@@ -59,6 +175,7 @@ export async function runLogsCommand(id, options, _command) {
|
|
|
59
175
|
console.error("Usage: paseo agent logs <id>");
|
|
60
176
|
process.exit(1);
|
|
61
177
|
}
|
|
178
|
+
const sinceTimestampMs = parseSinceTimestamp(options.since);
|
|
62
179
|
const client = await connectToDaemon({ target: options.daemonTarget });
|
|
63
180
|
try {
|
|
64
181
|
const fetchResult = await client.fetchAgent({ agentId: id });
|
|
@@ -77,11 +194,18 @@ export async function runLogsCommand(id, options, _command) {
|
|
|
77
194
|
await client.close().catch(() => { });
|
|
78
195
|
process.exit(1);
|
|
79
196
|
}
|
|
80
|
-
await runFollowMode(
|
|
197
|
+
await runFollowMode({
|
|
198
|
+
client,
|
|
199
|
+
agentId: resolvedId,
|
|
200
|
+
options,
|
|
201
|
+
sinceTimestampMs,
|
|
202
|
+
});
|
|
81
203
|
return;
|
|
82
204
|
}
|
|
83
205
|
// Fetch timeline directly via cursor RPC.
|
|
84
|
-
let timelineItems = await fetchAgentTimelineItems(client, resolvedId
|
|
206
|
+
let timelineItems = await fetchAgentTimelineItems(client, resolvedId, {
|
|
207
|
+
sinceTimestampMs,
|
|
208
|
+
});
|
|
85
209
|
// Apply filter
|
|
86
210
|
if (options.filter) {
|
|
87
211
|
timelineItems = timelineItems.filter((item) => matchesFilter(item, options.filter));
|
|
@@ -113,7 +237,7 @@ export async function runLogsCommand(id, options, _command) {
|
|
|
113
237
|
/**
|
|
114
238
|
* Follow mode: stream logs in real-time until interrupted
|
|
115
239
|
*/
|
|
116
|
-
async function runFollowMode(client, agentId, options) {
|
|
240
|
+
async function runFollowMode({ client, agentId, options, sinceTimestampMs, }) {
|
|
117
241
|
const DEFAULT_FOLLOW_TAIL = 10;
|
|
118
242
|
const tailCount = parseTailCount(options.tail) ?? DEFAULT_FOLLOW_TAIL;
|
|
119
243
|
// First, get existing timeline.
|
|
@@ -121,6 +245,7 @@ async function runFollowMode(client, agentId, options) {
|
|
|
121
245
|
try {
|
|
122
246
|
existingItems = await fetchAgentTimelineItems(client, agentId, {
|
|
123
247
|
timeoutMs: LIVE_HISTORY_FETCH_TIMEOUT_MS,
|
|
248
|
+
sinceTimestampMs,
|
|
124
249
|
});
|
|
125
250
|
}
|
|
126
251
|
catch (error) {
|
|
@@ -139,35 +264,41 @@ async function runFollowMode(client, agentId, options) {
|
|
|
139
264
|
}
|
|
140
265
|
// Subscribe to new events
|
|
141
266
|
const tailLabel = tailCount === 0 ? "no history" : `last ${tailCount} entr${tailCount === 1 ? "y" : "ies"}`;
|
|
267
|
+
const writer = createFollowTranscriptWriter(undefined, options.filter);
|
|
142
268
|
const unsubscribe = client.subscribeAgentTimeline(agentId, (message) => {
|
|
143
269
|
if (message.type === "agent.timeline.replacement") {
|
|
270
|
+
writer.end();
|
|
144
271
|
console.log("\n[Timeline replaced; earlier output is no longer current]");
|
|
145
272
|
return;
|
|
146
273
|
}
|
|
147
274
|
if (message.type === "agent.timeline.error") {
|
|
275
|
+
writer.end();
|
|
148
276
|
console.error(`Timeline observation stopped: ${message.payload.error}`);
|
|
149
277
|
return;
|
|
150
278
|
}
|
|
151
279
|
if (message.type === "agent.timeline.subscription_restored") {
|
|
280
|
+
writer.end();
|
|
152
281
|
console.log("\n[Reconnected; live output resumed. Events may have been missed.]");
|
|
153
282
|
return;
|
|
154
283
|
}
|
|
155
284
|
if (message.payload.event.type === "timeline") {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
if (options.filter && !matchesFilter(item, options.filter)) {
|
|
285
|
+
const matchesSince = sinceTimestampMs === undefined || Date.parse(message.payload.timestamp) >= sinceTimestampMs;
|
|
286
|
+
if (!matchesSince) {
|
|
159
287
|
return;
|
|
160
288
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
289
|
+
const item = message.payload.event.item;
|
|
290
|
+
writer.push(item, message.payload.event.turnId);
|
|
291
|
+
}
|
|
292
|
+
else if (message.payload.event.type === "turn_completed" ||
|
|
293
|
+
message.payload.event.type === "turn_failed" ||
|
|
294
|
+
message.payload.event.type === "turn_canceled") {
|
|
295
|
+
writer.end();
|
|
166
296
|
}
|
|
167
297
|
});
|
|
168
298
|
await unsubscribe.ready;
|
|
169
299
|
console.log(`\n--- Following logs (${tailLabel}; Ctrl+C to stop) ---\n`);
|
|
170
300
|
await waitForStop();
|
|
301
|
+
writer.end();
|
|
171
302
|
unsubscribe();
|
|
172
303
|
await client.close();
|
|
173
304
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
import type { AgentSnapshotPayload } from "@camerontaylor/paseo-protocol/messages";
|
|
2
3
|
import { connectToDaemon } from "../../utils/client.js";
|
|
3
4
|
import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
|
|
4
5
|
type FetchAgentsOptions = NonNullable<Parameters<Awaited<ReturnType<typeof connectToDaemon>>["fetchAgents"]>[0]>;
|
|
@@ -22,6 +23,8 @@ export interface AgentLsOptions extends CommandOptions {
|
|
|
22
23
|
all?: boolean;
|
|
23
24
|
/** -g: List agents across all directories */
|
|
24
25
|
global?: boolean;
|
|
26
|
+
/** --background: Include background workspace agents */
|
|
27
|
+
background?: boolean;
|
|
25
28
|
/** Filter by specific status */
|
|
26
29
|
status?: string;
|
|
27
30
|
/** Filter by specific cwd */
|
|
@@ -31,14 +34,26 @@ export interface AgentLsOptions extends CommandOptions {
|
|
|
31
34
|
/** Filter by thinking option ID */
|
|
32
35
|
thinking?: string;
|
|
33
36
|
}
|
|
34
|
-
export declare function buildAgentLsFetchOptions(options: Pick<AgentLsOptions, "all" | "global" | "label" | "thinking">): FetchAgentsOptions;
|
|
37
|
+
export declare function buildAgentLsFetchOptions(options: Pick<AgentLsOptions, "all" | "global" | "background" | "label" | "thinking">): FetchAgentsOptions;
|
|
38
|
+
interface AgentLsClient {
|
|
39
|
+
fetchAgents(options?: FetchAgentsOptions): Promise<{
|
|
40
|
+
entries: {
|
|
41
|
+
agent: AgentSnapshotPayload;
|
|
42
|
+
}[];
|
|
43
|
+
pageInfo: {
|
|
44
|
+
nextCursor: string | null;
|
|
45
|
+
};
|
|
46
|
+
}>;
|
|
47
|
+
close(): Promise<void>;
|
|
48
|
+
}
|
|
35
49
|
/**
|
|
36
50
|
* Agent ls command semantics:
|
|
37
51
|
* - `paseo agent ls` → active non-archived agents
|
|
38
52
|
* - `paseo agent ls -g` → global non-archived agents
|
|
39
|
-
* - `paseo agent ls -a` →
|
|
53
|
+
* - `paseo agent ls -a` → all agents, including archived and background
|
|
40
54
|
* - `paseo agent ls -ag` → global agents, including archived
|
|
41
55
|
*/
|
|
42
56
|
export declare function runLsCommand(options: AgentLsOptions, _command: Command): Promise<AgentLsResult>;
|
|
57
|
+
export declare function listAgents(options: AgentLsOptions, connect?: (options: Parameters<typeof connectToDaemon>[0]) => Promise<AgentLsClient>): Promise<AgentLsResult>;
|
|
43
58
|
export {};
|
|
44
59
|
//# sourceMappingURL=ls.d.ts.map
|
|
@@ -4,8 +4,9 @@ import { isSameOrDescendantPath } from "../../utils/paths.js";
|
|
|
4
4
|
export function addLsOptions(cmd) {
|
|
5
5
|
return cmd
|
|
6
6
|
.description("List agents. By default excludes archived agents.")
|
|
7
|
-
.option("-a, --all", "Include archived agents")
|
|
7
|
+
.option("-a, --all", "Include archived and background agents")
|
|
8
8
|
.option("-g, --global", "List agents across all directories")
|
|
9
|
+
.option("--background", "Include background workspace agents")
|
|
9
10
|
.option("--label <key=value>", "Filter by label (can be used multiple times)", collectMultiple, [])
|
|
10
11
|
.option("--thinking <id>", "Filter by thinking option ID");
|
|
11
12
|
}
|
|
@@ -97,6 +98,9 @@ export function buildAgentLsFetchOptions(options) {
|
|
|
97
98
|
if (options.all) {
|
|
98
99
|
daemonFilter.includeArchived = true;
|
|
99
100
|
}
|
|
101
|
+
if (options.background || options.all) {
|
|
102
|
+
daemonFilter.includeBackground = true;
|
|
103
|
+
}
|
|
100
104
|
if (Object.keys(labelFilters).length > 0) {
|
|
101
105
|
daemonFilter.labels = labelFilters;
|
|
102
106
|
}
|
|
@@ -104,7 +108,7 @@ export function buildAgentLsFetchOptions(options) {
|
|
|
104
108
|
daemonFilter.thinkingOptionId = normalizedThinkingOptionId;
|
|
105
109
|
}
|
|
106
110
|
const fetchOptions = {};
|
|
107
|
-
if (!options.global) {
|
|
111
|
+
if (!options.global && !options.all) {
|
|
108
112
|
fetchOptions.scope = "active";
|
|
109
113
|
}
|
|
110
114
|
if (Object.keys(daemonFilter).length > 0) {
|
|
@@ -116,11 +120,14 @@ export function buildAgentLsFetchOptions(options) {
|
|
|
116
120
|
* Agent ls command semantics:
|
|
117
121
|
* - `paseo agent ls` → active non-archived agents
|
|
118
122
|
* - `paseo agent ls -g` → global non-archived agents
|
|
119
|
-
* - `paseo agent ls -a` →
|
|
123
|
+
* - `paseo agent ls -a` → all agents, including archived and background
|
|
120
124
|
* - `paseo agent ls -ag` → global agents, including archived
|
|
121
125
|
*/
|
|
122
126
|
export async function runLsCommand(options, _command) {
|
|
123
|
-
|
|
127
|
+
return listAgents(options);
|
|
128
|
+
}
|
|
129
|
+
export async function listAgents(options, connect = connectToDaemon) {
|
|
130
|
+
const client = await connect({ target: options.daemonTarget });
|
|
124
131
|
try {
|
|
125
132
|
const normalizedThinkingOptionId = options.thinking?.trim();
|
|
126
133
|
if (options.thinking !== undefined && !normalizedThinkingOptionId) {
|
|
@@ -131,8 +138,19 @@ export async function runLsCommand(options, _command) {
|
|
|
131
138
|
throw error;
|
|
132
139
|
}
|
|
133
140
|
const labelFilters = parseLabelFilters(options.label);
|
|
134
|
-
const
|
|
135
|
-
let agents =
|
|
141
|
+
const fetchOptions = buildAgentLsFetchOptions(options);
|
|
142
|
+
let agents = [];
|
|
143
|
+
let cursor;
|
|
144
|
+
// createdAt never changes, so agents cannot move past the cursor while the pages are read.
|
|
145
|
+
do {
|
|
146
|
+
const payload = await client.fetchAgents({
|
|
147
|
+
...fetchOptions,
|
|
148
|
+
sort: [{ key: "created_at", direction: "desc" }],
|
|
149
|
+
page: { limit: 200, ...(cursor ? { cursor } : {}) },
|
|
150
|
+
});
|
|
151
|
+
agents.push(...payload.entries.map((entry) => entry.agent));
|
|
152
|
+
cursor = payload.pageInfo.nextCursor ?? undefined;
|
|
153
|
+
} while (cursor);
|
|
136
154
|
// By default, exclude archived agents. `-a` includes them.
|
|
137
155
|
if (!options.all) {
|
|
138
156
|
agents = agents.filter((a) => !a.archivedAt);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { type DaemonClient } from "@camerontaylor/paseo-client/internal/daemon-client";
|
|
3
2
|
import { connectToDaemon } from "../../utils/client.js";
|
|
4
3
|
import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
|
|
5
4
|
import { type ResolvedProviderModel } from "../../utils/provider-model.js";
|
|
@@ -16,6 +15,7 @@ export interface AgentRunResult {
|
|
|
16
15
|
/** Schema for agent run output */
|
|
17
16
|
export declare const agentRunSchema: OutputSchema<AgentRunResult>;
|
|
18
17
|
export interface AgentRunOptions extends CommandOptions {
|
|
18
|
+
wait?: boolean;
|
|
19
19
|
background?: boolean;
|
|
20
20
|
detach?: boolean;
|
|
21
21
|
title?: string;
|
|
@@ -50,6 +50,7 @@ export declare function resolveStructuredResponseMessage(options: {
|
|
|
50
50
|
agentId: string;
|
|
51
51
|
lastMessage: string | null;
|
|
52
52
|
}): Promise<string | null>;
|
|
53
|
+
export declare function waitsForFinish(options: Pick<AgentRunOptions, "wait" | "background" | "detach">): boolean;
|
|
53
54
|
interface RunWorkspace {
|
|
54
55
|
id?: string;
|
|
55
56
|
cwd: string;
|
|
@@ -77,18 +78,6 @@ interface ResolveRunThinkingOptionInput extends ResolvedProviderModel {
|
|
|
77
78
|
cwd: string;
|
|
78
79
|
thinking: string | undefined;
|
|
79
80
|
}
|
|
80
|
-
export declare function resolveRunThinkingOption(client: Pick<
|
|
81
|
+
export declare function resolveRunThinkingOption(client: Pick<ConnectedDaemonClient, "listProviderModels">, input: ResolveRunThinkingOptionInput): Promise<string | undefined>;
|
|
81
82
|
export declare function runRunCommand(prompt: string, options: AgentRunOptions, _command: Command): Promise<SingleResult<AgentRunResult>>;
|
|
82
|
-
export interface RunCallerLookupClient {
|
|
83
|
-
fetchAgent(options: {
|
|
84
|
-
agentId: string;
|
|
85
|
-
}): Promise<{
|
|
86
|
-
agent: {
|
|
87
|
-
id: string;
|
|
88
|
-
};
|
|
89
|
-
} | null>;
|
|
90
|
-
}
|
|
91
|
-
export declare function resolveRunCallerAgentId(client: RunCallerLookupClient, env?: {
|
|
92
|
-
PASEO_AGENT_ID?: string;
|
|
93
|
-
}): Promise<string | undefined>;
|
|
94
83
|
//# sourceMappingURL=run.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Option } from "commander";
|
|
2
2
|
import { getStructuredAgentResponse, StructuredAgentResponseError, } from "@camerontaylor/paseo-server/agent-response";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveCallerAgentId } from "../../utils/caller-agent.js";
|
|
4
4
|
import { connectToDaemon } from "../../utils/client.js";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import { resolve } from "node:path";
|
|
@@ -14,10 +14,13 @@ export function addRunOptions(cmd) {
|
|
|
14
14
|
return (cmd
|
|
15
15
|
.description("Create and start an agent with a task")
|
|
16
16
|
.argument("<prompt>", "The task/prompt for the agent")
|
|
17
|
-
.option("-
|
|
17
|
+
.option("--no-wait", "Return immediately without waiting for completion")
|
|
18
|
+
// COMPAT(backgroundRunFlag): --background was the documented spelling of
|
|
19
|
+
// --no-wait. Added in v0.2.0; remove after 2027-04-01.
|
|
20
|
+
.addOption(new Option("-d, --background", "Legacy alias for --no-wait").hideHelp())
|
|
18
21
|
// COMPAT(detachRunFlag): --detach used to mean background execution, not
|
|
19
22
|
// ownership transfer. Added in v0.2.0; remove after 2027-01-17.
|
|
20
|
-
.addOption(new Option("--detach", "Legacy alias for --
|
|
23
|
+
.addOption(new Option("--detach", "Legacy alias for --no-wait").hideHelp())
|
|
21
24
|
.option("--title <title>", "Assign a title to the agent")
|
|
22
25
|
.addOption(new Option("--name <name>", "Hidden alias for --title").hideHelp())
|
|
23
26
|
.option("--provider <provider>", "Agent provider, or provider/model (e.g. codex or codex/gpt-5.4)")
|
|
@@ -268,17 +271,20 @@ function validateRunOptions(prompt, options, outputSchema) {
|
|
|
268
271
|
details: "Usage: paseo agent run [options] <prompt>",
|
|
269
272
|
};
|
|
270
273
|
}
|
|
274
|
+
if (options.background) {
|
|
275
|
+
console.error("Warning: --background (-d) is deprecated. Use --no-wait instead.");
|
|
276
|
+
}
|
|
271
277
|
validateRunWorkspaceOptions(options);
|
|
272
|
-
if (outputSchema &&
|
|
278
|
+
if (outputSchema && !waitsForFinish(options)) {
|
|
273
279
|
throw {
|
|
274
280
|
code: "INVALID_OPTIONS",
|
|
275
|
-
message: "--output-schema cannot be used with --
|
|
281
|
+
message: "--output-schema cannot be used with --no-wait",
|
|
276
282
|
details: "Structured output requires waiting for the agent to finish",
|
|
277
283
|
};
|
|
278
284
|
}
|
|
279
285
|
}
|
|
280
|
-
function
|
|
281
|
-
return
|
|
286
|
+
export function waitsForFinish(options) {
|
|
287
|
+
return options.wait !== false && !options.background && !options.detach;
|
|
282
288
|
}
|
|
283
289
|
function parseWaitTimeoutOption(waitTimeout) {
|
|
284
290
|
if (!waitTimeout)
|
|
@@ -397,7 +403,10 @@ async function resolveRunWorkspace(client, options, cwd, callerAgentId) {
|
|
|
397
403
|
// TODO: thread the run `prompt` as firstAgentContext so workspace-level
|
|
398
404
|
// title/branch generation picks up the task description (U8/U6 deferred).
|
|
399
405
|
const source = buildRunWorkspaceSource(options, cwd);
|
|
400
|
-
const result = await client.createWorkspace({
|
|
406
|
+
const result = await client.createWorkspace({
|
|
407
|
+
source,
|
|
408
|
+
...(callerAgentId ? { callerAgentId } : {}),
|
|
409
|
+
});
|
|
401
410
|
if (!result.workspace) {
|
|
402
411
|
throw {
|
|
403
412
|
code: "WORKSPACE_CREATE_FAILED",
|
|
@@ -448,7 +457,7 @@ export async function runRunCommand(prompt, options, _command) {
|
|
|
448
457
|
const labels = parseRunLabels(options.label);
|
|
449
458
|
const env = parseRunEnv(options.env);
|
|
450
459
|
const requestEnv = Object.keys(env).length > 0 ? env : undefined;
|
|
451
|
-
const callerAgentId = await
|
|
460
|
+
const callerAgentId = await resolveCallerAgentId(client);
|
|
452
461
|
const workspace = await resolveRunWorkspace(client, options, cwd, callerAgentId);
|
|
453
462
|
const workspaceId = workspace.id;
|
|
454
463
|
const runCwd = workspace.cwd;
|
|
@@ -530,8 +539,8 @@ export async function runRunCommand(prompt, options, _command) {
|
|
|
530
539
|
env: requestEnv,
|
|
531
540
|
labels: Object.keys(labels).length > 0 ? labels : undefined,
|
|
532
541
|
});
|
|
533
|
-
// Default run behavior is foreground: wait for completion unless
|
|
534
|
-
if (
|
|
542
|
+
// Default run behavior is foreground: wait for completion unless --no-wait is set.
|
|
543
|
+
if (waitsForFinish(options)) {
|
|
535
544
|
const state = await client.waitForFinish(agent.id, waitTimeoutMs);
|
|
536
545
|
await client.close();
|
|
537
546
|
const finalAgent = state.final ?? agent;
|
|
@@ -562,22 +571,4 @@ export async function runRunCommand(prompt, options, _command) {
|
|
|
562
571
|
throw error;
|
|
563
572
|
}
|
|
564
573
|
}
|
|
565
|
-
// PASEO_AGENT_ID names an agent on the daemon that launched this shell. A run
|
|
566
|
-
// sent to another daemon (--host or --home) has no caller there, so it runs as
|
|
567
|
-
// a top-level agent instead of failing on an unknown caller.
|
|
568
|
-
export async function resolveRunCallerAgentId(client, env = process.env) {
|
|
569
|
-
const agentId = env.PASEO_AGENT_ID?.trim();
|
|
570
|
-
if (!agentId) {
|
|
571
|
-
return undefined;
|
|
572
|
-
}
|
|
573
|
-
const caller = await client.fetchAgent({ agentId }).catch((error) => {
|
|
574
|
-
// A daemon without this agent answers with an error. A lost or timed-out
|
|
575
|
-
// connection is not an answer, so it must not drop the caller.
|
|
576
|
-
if (error instanceof DaemonConnectionError) {
|
|
577
|
-
throw error;
|
|
578
|
-
}
|
|
579
|
-
return null;
|
|
580
|
-
});
|
|
581
|
-
return caller?.agent.id === agentId ? agentId : undefined;
|
|
582
|
-
}
|
|
583
574
|
//# sourceMappingURL=run.js.map
|
|
@@ -139,7 +139,10 @@ export async function runSendCommand(agentIdArg, prompt, options, _command) {
|
|
|
139
139
|
// Read image files if provided
|
|
140
140
|
const images = options.image && options.image.length > 0 ? await readImageFiles(options.image) : undefined;
|
|
141
141
|
// Send the message
|
|
142
|
-
await client.sendAgentMessage(agentIdArg, promptInput, {
|
|
142
|
+
await client.sendAgentMessage(agentIdArg, promptInput, {
|
|
143
|
+
images,
|
|
144
|
+
sourceAgentId: process.env.PASEO_AGENT_ID?.trim() || undefined,
|
|
145
|
+
});
|
|
143
146
|
// If --no-wait, return immediately
|
|
144
147
|
if (options.wait === false) {
|
|
145
148
|
await client.close();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import type { AgentPermissionRequest } from "@camerontaylor/paseo-protocol/agent-types";
|
|
2
|
+
import type { AgentPermissionRequest, AgentPermissionResponse } from "@camerontaylor/paseo-protocol/agent-types";
|
|
3
|
+
import { connectToDaemon } from "../../utils/client.js";
|
|
3
4
|
import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
|
|
4
5
|
/** Permission response item for display */
|
|
5
6
|
export interface PermissionResponseItem {
|
|
@@ -19,5 +20,19 @@ export interface PermitAllowOptions extends CommandOptions {
|
|
|
19
20
|
input?: string;
|
|
20
21
|
host?: string;
|
|
21
22
|
}
|
|
22
|
-
export
|
|
23
|
+
export interface PermitClient {
|
|
24
|
+
fetchAgent(options: {
|
|
25
|
+
agentId: string;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
agent: {
|
|
28
|
+
id: string;
|
|
29
|
+
pendingPermissions?: AgentPermissionRequest[];
|
|
30
|
+
};
|
|
31
|
+
} | null>;
|
|
32
|
+
respondToPermissionAndWait(agentId: string, requestId: string, response: AgentPermissionResponse): Promise<unknown>;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export type PermitClientConnector = (options: Parameters<typeof connectToDaemon>[0]) => Promise<PermitClient>;
|
|
36
|
+
export declare function createAllowCommand(connect?: PermitClientConnector): (agentIdOrPrefix: string, reqId: string | undefined, options: PermitAllowOptions, _command: Command) => Promise<PermitAllowResult>;
|
|
37
|
+
export declare const runAllowCommand: (agentIdOrPrefix: string, reqId: string | undefined, options: PermitAllowOptions, _command: Command) => Promise<PermitAllowResult>;
|
|
23
38
|
//# sourceMappingURL=allow.d.ts.map
|