@camerontaylor/paseo-cli 0.11.2-fork.2 → 0.12.0-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.
Files changed (36) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/commands/agent/logs.d.ts +19 -2
  3. package/dist/commands/agent/logs.js +143 -12
  4. package/dist/commands/agent/ls.d.ts +17 -2
  5. package/dist/commands/agent/ls.js +24 -6
  6. package/dist/commands/agent/run.d.ts +3 -14
  7. package/dist/commands/agent/run.js +20 -29
  8. package/dist/commands/agent/send.d.ts +2 -0
  9. package/dist/commands/agent/send.js +8 -1
  10. package/dist/commands/permit/allow.d.ts +17 -2
  11. package/dist/commands/permit/allow.js +96 -82
  12. package/dist/commands/permit/deny.d.ts +3 -2
  13. package/dist/commands/permit/deny.js +85 -71
  14. package/dist/commands/usage/index.d.ts +29 -0
  15. package/dist/commands/usage/index.js +62 -0
  16. package/dist/commands/usage/presentation.d.ts +4 -0
  17. package/dist/commands/usage/presentation.js +146 -0
  18. package/dist/commands/workspace/create.d.ts +1 -0
  19. package/dist/commands/workspace/create.js +4 -0
  20. package/dist/commands/workspace/index.js +7 -2
  21. package/dist/commands/workspace/ls.d.ts +5 -2
  22. package/dist/commands/workspace/ls.js +7 -2
  23. package/dist/commands/workspace/shared.d.ts +3 -0
  24. package/dist/commands/workspace/shared.js +9 -0
  25. package/dist/commands/worktree/archive.d.ts +1 -0
  26. package/dist/commands/worktree/archive.js +3 -1
  27. package/dist/commands/worktree/index.js +6 -2
  28. package/dist/commands/worktree/ls.d.ts +1 -0
  29. package/dist/commands/worktree/ls.js +3 -1
  30. package/dist/commands/worktree/repository-directory.d.ts +6 -0
  31. package/dist/commands/worktree/repository-directory.js +9 -0
  32. package/dist/utils/caller-agent.d.ts +14 -0
  33. package/dist/utils/caller-agent.js +16 -0
  34. package/dist/utils/timeline.d.ts +1 -0
  35. package/dist/utils/timeline.js +6 -1
  36. 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
- export declare function fetchAgentTimelineItems(client: DaemonClient, agentId: string, options?: {
14
+ interface FetchAgentTimelineItemsOptions {
15
15
  timeoutMs?: number;
16
- }): Promise<AgentTimelineItem[]>;
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({ client, agentId, timeoutMs: options?.timeoutMs });
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(client, resolvedId, options);
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 item = message.payload.event.item;
157
- // Apply filter
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
- // Print each timeline item as it arrives using the curator format
162
- const transcript = formatAgentActivityTranscript([item]);
163
- if (transcript !== NO_ACTIVITY_MESSAGE) {
164
- console.log(transcript);
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` → active agents, including archived
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` → active agents, including archived
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
- const client = await connectToDaemon({ target: options.daemonTarget });
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 fetchPayload = await client.fetchAgents(buildAgentLsFetchOptions(options));
135
- let agents = fetchPayload.entries.map((entry) => entry.agent);
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<DaemonClient, "listProviderModels">, input: ResolveRunThinkingOptionInput): Promise<string | undefined>;
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 { DaemonConnectionError } from "@camerontaylor/paseo-client/internal/daemon-client";
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("-d, --background", "Run in background")
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 --background").hideHelp())
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 && runsInBackground(options)) {
278
+ if (outputSchema && !waitsForFinish(options)) {
273
279
  throw {
274
280
  code: "INVALID_OPTIONS",
275
- message: "--output-schema cannot be used with --background",
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 runsInBackground(options) {
281
- return Boolean(options.background || options.detach);
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({ source });
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 resolveRunCallerAgentId(client);
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 background execution is set.
534
- if (!runsInBackground(options)) {
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
@@ -1,4 +1,5 @@
1
1
  import { type Command } from "commander";
2
+ import type { ActiveTurnBehavior } from "@camerontaylor/paseo-protocol/messages";
2
3
  import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
3
4
  /** Result type for agent send command */
4
5
  export interface AgentSendResult {
@@ -9,6 +10,7 @@ export interface AgentSendResult {
9
10
  /** Schema for agent send output */
10
11
  export declare const agentSendSchema: OutputSchema<AgentSendResult>;
11
12
  export interface AgentSendOptions extends CommandOptions {
13
+ behavior?: ActiveTurnBehavior;
12
14
  wait?: boolean;
13
15
  image?: string[];
14
16
  prompt?: string;
@@ -1,3 +1,4 @@
1
+ import { Option } from "commander";
1
2
  import { collectMultiple } from "../../utils/command-options.js";
2
3
  import { connectToDaemon } from "../../utils/client.js";
3
4
  import { readFile } from "node:fs/promises";
@@ -19,6 +20,7 @@ export function addSendOptions(cmd) {
19
20
  .option("--prompt <text>", "Provide the message inline as a flag")
20
21
  .option("--prompt-file <path>", "Read the message from a UTF-8 text file")
21
22
  .option("--image <path>", "Attach image(s) to the message", collectMultiple, [])
23
+ .addOption(new Option("--behavior <behavior>", "Active-turn behavior (agent default: steer; otherwise: interrupt)").choices(["interrupt", "steer"]))
22
24
  .option("--no-wait", "Return immediately without waiting for completion");
23
25
  }
24
26
  /**
@@ -138,8 +140,13 @@ export async function runSendCommand(agentIdArg, prompt, options, _command) {
138
140
  try {
139
141
  // Read image files if provided
140
142
  const images = options.image && options.image.length > 0 ? await readImageFiles(options.image) : undefined;
143
+ const sourceAgentId = process.env.PASEO_AGENT_ID?.trim() || undefined;
141
144
  // Send the message
142
- await client.sendAgentMessage(agentIdArg, promptInput, { images });
145
+ await client.sendAgentMessage(agentIdArg, promptInput, {
146
+ images,
147
+ sourceAgentId,
148
+ activeTurnBehavior: options.behavior ?? (sourceAgentId ? "steer" : "interrupt"),
149
+ });
143
150
  // If --no-wait, return immediately
144
151
  if (options.wait === false) {
145
152
  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 declare function runAllowCommand(agentIdOrPrefix: string, reqId: string | undefined, options: PermitAllowOptions, _command: Command): Promise<PermitAllowResult>;
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