@cursor/july 0.1.2 → 0.1.3

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 (174) hide show
  1. package/AGENTS.md +16 -5
  2. package/README.md +8 -9
  3. package/dist/bin/agent-serve.d.ts +3 -1
  4. package/dist/bin/agent-serve.d.ts.map +1 -1
  5. package/dist/bin/agent-serve.js +389 -152
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/ab.html +3 -3
  8. package/dist/docs/assets/{app.Oje4vhlk.js → app.BR0RbIdx.js} +1 -1
  9. package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
  10. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
  11. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.2Kg8jIp_.js} +2 -2
  12. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  13. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  14. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  15. package/dist/docs/building-with-agents.html +3 -3
  16. package/dist/docs/concepts.html +3 -3
  17. package/dist/docs/deployment.html +3 -3
  18. package/dist/docs/evals.html +3 -3
  19. package/dist/docs/guides/agent-to-agent.html +3 -3
  20. package/dist/docs/guides/cloud-runtime.html +3 -3
  21. package/dist/docs/guides/github.html +3 -3
  22. package/dist/docs/guides/human-in-the-loop.html +3 -3
  23. package/dist/docs/guides/slack.html +3 -3
  24. package/dist/docs/guides/webhooks.html +3 -3
  25. package/dist/docs/hashmap.json +1 -1
  26. package/dist/docs/hillclimbing.html +3 -3
  27. package/dist/docs/index.html +3 -3
  28. package/dist/docs/quickstart.html +22 -10
  29. package/dist/docs/reference/agent-config.html +3 -3
  30. package/dist/docs/reference/channels.html +3 -3
  31. package/dist/docs/reference/cli.html +14 -9
  32. package/dist/docs/reference/connections.html +3 -3
  33. package/dist/docs/reference/hooks.html +3 -3
  34. package/dist/docs/reference/http-api.html +3 -3
  35. package/dist/docs/reference/instructions.html +3 -3
  36. package/dist/docs/reference/playground.html +3 -3
  37. package/dist/docs/reference/project-layout.html +3 -3
  38. package/dist/docs/reference/schedules.html +3 -3
  39. package/dist/docs/reference/sessions.html +3 -3
  40. package/dist/docs/reference/skills.html +3 -3
  41. package/dist/docs/reference/subagents.html +3 -3
  42. package/dist/docs/reference/tools.html +3 -3
  43. package/dist/docs/scaffolding-agents.html +3 -3
  44. package/dist/docs/storage.html +3 -3
  45. package/dist/docs/troubleshooting.html +3 -3
  46. package/dist/internal/chat-client.d.ts +29 -3
  47. package/dist/internal/chat-client.d.ts.map +1 -1
  48. package/dist/internal/chat-client.js +180 -27
  49. package/dist/internal/cli-ax.d.ts +75 -2
  50. package/dist/internal/cli-ax.d.ts.map +1 -1
  51. package/dist/internal/cli-ax.js +600 -52
  52. package/dist/internal/cli-cursor.d.ts.map +1 -1
  53. package/dist/internal/cli-cursor.js +8 -36
  54. package/dist/internal/cli-deploy.d.ts +59 -3
  55. package/dist/internal/cli-deploy.d.ts.map +1 -1
  56. package/dist/internal/cli-deploy.js +316 -38
  57. package/dist/internal/cursor/credentials.d.ts +9 -0
  58. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  59. package/dist/internal/cursor/credentials.js +12 -0
  60. package/dist/internal/deploy-client.d.ts +9 -1
  61. package/dist/internal/deploy-client.d.ts.map +1 -1
  62. package/dist/internal/deploy-client.js +23 -3
  63. package/dist/internal/deploy-source.d.ts +35 -0
  64. package/dist/internal/deploy-source.d.ts.map +1 -0
  65. package/dist/internal/deploy-source.js +118 -0
  66. package/dist/internal/discovery.d.ts +11 -0
  67. package/dist/internal/discovery.d.ts.map +1 -1
  68. package/dist/internal/discovery.js +43 -14
  69. package/dist/internal/eval-run-store.d.ts.map +1 -1
  70. package/dist/internal/eval-run-store.js +2 -1
  71. package/dist/internal/eval-runner.d.ts +2 -0
  72. package/dist/internal/eval-runner.d.ts.map +1 -1
  73. package/dist/internal/eval-runner.js +2 -0
  74. package/dist/internal/event-mapper.d.ts +54 -1
  75. package/dist/internal/event-mapper.d.ts.map +1 -1
  76. package/dist/internal/event-mapper.js +151 -41
  77. package/dist/internal/init-project.d.ts +88 -1
  78. package/dist/internal/init-project.d.ts.map +1 -1
  79. package/dist/internal/init-project.js +221 -31
  80. package/dist/internal/install-cursor-skills.d.ts +64 -0
  81. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  82. package/dist/internal/install-cursor-skills.js +274 -0
  83. package/dist/internal/logs-client.d.ts +60 -0
  84. package/dist/internal/logs-client.d.ts.map +1 -0
  85. package/dist/internal/logs-client.js +311 -0
  86. package/dist/internal/open-browser.d.ts +5 -0
  87. package/dist/internal/open-browser.d.ts.map +1 -0
  88. package/dist/internal/open-browser.js +34 -0
  89. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  90. package/dist/internal/playground/toolchain.js +16 -11
  91. package/dist/internal/playground-proxy.d.ts +69 -0
  92. package/dist/internal/playground-proxy.d.ts.map +1 -0
  93. package/dist/internal/playground-proxy.js +468 -0
  94. package/dist/internal/prompt-context.d.ts +35 -0
  95. package/dist/internal/prompt-context.d.ts.map +1 -0
  96. package/dist/internal/prompt-context.js +71 -0
  97. package/dist/internal/request-headers.d.ts +11 -0
  98. package/dist/internal/request-headers.d.ts.map +1 -0
  99. package/dist/internal/request-headers.js +14 -0
  100. package/dist/internal/resolve-prod-target.d.ts +42 -0
  101. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  102. package/dist/internal/resolve-prod-target.js +111 -0
  103. package/dist/internal/run-client.d.ts +2 -2
  104. package/dist/internal/run-client.d.ts.map +1 -1
  105. package/dist/internal/run-client.js +38 -23
  106. package/dist/internal/server.d.ts.map +1 -1
  107. package/dist/internal/server.js +10 -4
  108. package/dist/internal/session-engine.d.ts +1 -1
  109. package/dist/internal/session-engine.d.ts.map +1 -1
  110. package/dist/internal/session-engine.js +21 -6
  111. package/dist/internal/sessions-client.d.ts +21 -0
  112. package/dist/internal/sessions-client.d.ts.map +1 -0
  113. package/dist/internal/sessions-client.js +168 -0
  114. package/dist/internal/stream-progress.d.ts.map +1 -1
  115. package/dist/internal/stream-progress.js +31 -3
  116. package/dist/internal/terminal-style.d.ts +22 -0
  117. package/dist/internal/terminal-style.d.ts.map +1 -0
  118. package/dist/internal/terminal-style.js +49 -0
  119. package/dist/internal/trajectory.d.ts +10 -5
  120. package/dist/internal/trajectory.d.ts.map +1 -1
  121. package/dist/internal/trajectory.js +82 -10
  122. package/dist/internal/workspace.d.ts +11 -0
  123. package/dist/internal/workspace.d.ts.map +1 -1
  124. package/dist/internal/workspace.js +38 -4
  125. package/dist/playground/assets/index-D-vo2lV_.css +1 -0
  126. package/dist/playground/assets/index-x60b9q2j.js +312 -0
  127. package/dist/playground/index.html +2 -2
  128. package/dist/types.d.ts +26 -1
  129. package/dist/types.d.ts.map +1 -1
  130. package/docs/quickstart.md +22 -7
  131. package/docs/reference/cli.md +51 -3
  132. package/package.json +3 -1
  133. package/skills/ab/SKILL.md +8 -8
  134. package/skills/create-agent/SKILL.md +28 -22
  135. package/skills/debug/SKILL.md +11 -11
  136. package/skills/evals/SKILL.md +16 -16
  137. package/skills/framework-map/SKILL.md +6 -6
  138. package/skills/github/SKILL.md +13 -13
  139. package/skills/hillclimb/SKILL.md +10 -10
  140. package/skills/setup-slack/SKILL.md +13 -13
  141. package/src/bin/agent-serve.ts +422 -188
  142. package/src/internal/chat-client.ts +252 -37
  143. package/src/internal/cli-ax.ts +722 -72
  144. package/src/internal/cli-cursor.ts +14 -42
  145. package/src/internal/cli-deploy.ts +428 -49
  146. package/src/internal/cursor/credentials.ts +14 -0
  147. package/src/internal/deploy-client.ts +45 -4
  148. package/src/internal/deploy-source.ts +133 -0
  149. package/src/internal/discovery.ts +53 -16
  150. package/src/internal/eval-run-store.ts +2 -1
  151. package/src/internal/eval-runner.ts +5 -0
  152. package/src/internal/event-mapper.ts +222 -42
  153. package/src/internal/init-project.ts +333 -51
  154. package/src/internal/install-cursor-skills.ts +327 -0
  155. package/src/internal/logs-client.ts +442 -0
  156. package/src/internal/open-browser.ts +41 -0
  157. package/src/internal/playground/toolchain.ts +15 -13
  158. package/src/internal/playground-proxy.ts +576 -0
  159. package/src/internal/prompt-context.ts +95 -0
  160. package/src/internal/request-headers.ts +23 -0
  161. package/src/internal/resolve-prod-target.ts +150 -0
  162. package/src/internal/run-client.ts +39 -36
  163. package/src/internal/server.ts +12 -3
  164. package/src/internal/session-engine.ts +22 -3
  165. package/src/internal/sessions-client.ts +182 -0
  166. package/src/internal/stream-progress.ts +36 -2
  167. package/src/internal/terminal-style.ts +74 -0
  168. package/src/internal/trajectory.ts +91 -13
  169. package/src/internal/workspace.ts +42 -4
  170. package/src/types.ts +42 -6
  171. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  172. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  173. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  174. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -8,12 +8,30 @@
8
8
 
9
9
  import { createInterface } from "node:readline";
10
10
  import type { SessionEvent } from "../types.js";
11
+ import { mergeRequestHeaders, type RequestAuth } from "./request-headers.js";
12
+ import { fetchSessionEvents, listSessions } from "./sessions-client.js";
11
13
 
12
- export interface ChatClientOptions {
14
+ export interface ChatClientOptions extends RequestAuth {
13
15
  baseUrl: string;
14
- bearerToken?: string;
15
- /** One-shot: send this message, stream the reply, then exit. */
16
+ /**
17
+ * Optional opening user message. On a TTY (without `oneShot`), this seeds
18
+ * the first turn and then the REPL stays open for follow-ups. With
19
+ * `oneShot` (or a non-TTY stdin), send it and exit.
20
+ */
16
21
  message?: string;
22
+ /**
23
+ * Reattach to an existing session. Looks up the current continuation
24
+ * token via `GET /v1/sessions` when `--continuation-token` is omitted,
25
+ * replays the transcript, then accepts follow-ups.
26
+ */
27
+ sessionId?: string;
28
+ /** Optional; when omitted with `sessionId`, resolved from `/v1/sessions`. */
29
+ continuationToken?: string;
30
+ /**
31
+ * Exit after the opening `--message` instead of entering the REPL.
32
+ * Implied when stdin is not a TTY.
33
+ */
34
+ oneShot?: boolean;
17
35
  /** Force plain output even on a TTY. */
18
36
  noColor?: boolean;
19
37
  logger?: (line: string) => void;
@@ -61,6 +79,10 @@ function makePalette(enabled: boolean): Palette {
61
79
  /** Run the terminal chat client. Resolves when the session/REPL ends. */
62
80
  export async function runChat(options: ChatClientOptions): Promise<void> {
63
81
  const baseUrl = options.baseUrl.replace(/\/$/, "");
82
+ const auth: RequestAuth = {
83
+ bearerToken: options.bearerToken,
84
+ headers: options.headers,
85
+ };
64
86
  const colorEnabled =
65
87
  !options.noColor && process.stdout.isTTY === true && !process.env.NO_COLOR;
66
88
  const c = makePalette(colorEnabled);
@@ -68,11 +90,11 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
68
90
  process.stdout.write(s);
69
91
  };
70
92
 
71
- const health = await getJson(baseUrl, "/v1/health", options.bearerToken);
93
+ const health = await getJson(baseUrl, "/v1/health", auth);
72
94
  const agentName =
73
95
  typeof health?.name === "string" ? health.name : "agent-serve";
74
96
  write(`${c.bold(agentName)} ${c.dim(`· ${baseUrl}`)}\n`);
75
- const info = await getJson(baseUrl, "/v1/info", options.bearerToken);
97
+ const info = await getJson(baseUrl, "/v1/info", auth);
76
98
  if (info && typeof info === "object") {
77
99
  const model = (info as { model?: unknown }).model;
78
100
  const tools = (info as { tools?: unknown[] }).tools ?? [];
@@ -86,22 +108,43 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
86
108
  }
87
109
  write("\n");
88
110
 
89
- let sessionId: string | undefined;
90
- let continuationToken: string | undefined;
111
+ let sessionId: string | undefined = options.sessionId;
112
+ let continuationToken: string | undefined = options.continuationToken;
91
113
  // Highest event index rendered so far; a follow-up streams from here so it
92
114
  // shows only the new turn, not the replayed transcript.
93
115
  let nextEventIndex = 0;
94
116
 
95
- const runTurn = async (message: string): Promise<void> => {
96
- write(`${c.cyan("› you")} ${message}\n`);
117
+ if (options.sessionId !== undefined) {
118
+ const attached = await attachSession({
119
+ baseUrl,
120
+ auth,
121
+ sessionId: options.sessionId,
122
+ continuationToken: options.continuationToken,
123
+ agentName,
124
+ palette: c,
125
+ write,
126
+ });
127
+ sessionId = attached.sessionId;
128
+ continuationToken = attached.continuationToken;
129
+ nextEventIndex = attached.nextEventIndex;
130
+ } else if (options.continuationToken !== undefined) {
131
+ throw new Error(
132
+ "--continuation-token requires --session (session id to reattach to)"
133
+ );
134
+ }
135
+
136
+ const runTurn = async (
137
+ message: string,
138
+ opts: { echoUser?: boolean } = {}
139
+ ): Promise<void> => {
140
+ // REPL lines are already echoed by readline; only reprint for seeded
141
+ // `--message` / one-shot so the transcript still shows the user turn.
142
+ if (opts.echoUser !== false) {
143
+ write(`${c.cyan("› you")} ${message}\n`);
144
+ }
97
145
  const startIndex = sessionId === undefined ? 0 : nextEventIndex;
98
146
  if (sessionId === undefined) {
99
- const created = await postJson(
100
- baseUrl,
101
- "/v1/session",
102
- { message },
103
- options.bearerToken
104
- );
147
+ const created = await postJson(baseUrl, "/v1/session", { message }, auth);
105
148
  sessionId = str(created?.sessionId);
106
149
  continuationToken = str(created?.continuationToken);
107
150
  } else {
@@ -109,13 +152,13 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
109
152
  baseUrl,
110
153
  `/v1/session/${sessionId}`,
111
154
  { message, continuationToken },
112
- options.bearerToken
155
+ auth
113
156
  );
114
157
  if (resp?.ok === false) {
115
158
  write(`${c.red("error")} ${str(resp.error)}: ${str(resp.message)}\n\n`);
116
159
  return;
117
160
  }
118
- continuationToken = str(resp?.continuationToken);
161
+ continuationToken = str(resp?.continuationToken) ?? continuationToken;
119
162
  }
120
163
  if (sessionId === undefined) {
121
164
  write(`${c.red("error")} server did not return a session id\n\n`);
@@ -125,16 +168,21 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
125
168
  baseUrl,
126
169
  sessionId,
127
170
  startIndex,
128
- bearerToken: options.bearerToken,
171
+ auth,
172
+ agentName,
129
173
  palette: c,
130
174
  write,
131
175
  });
132
176
  write("\n");
133
177
  };
134
178
 
179
+ const oneShot = options.oneShot === true || process.stdin.isTTY !== true;
180
+
135
181
  if (options.message !== undefined) {
136
182
  await runTurn(options.message);
137
- return;
183
+ if (oneShot) {
184
+ return;
185
+ }
138
186
  }
139
187
 
140
188
  write(c.dim("Type a message and press Enter. Ctrl-D or /quit to exit.\n\n"));
@@ -156,7 +204,7 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
156
204
  return;
157
205
  }
158
206
  rl.pause();
159
- runTurn(text)
207
+ runTurn(text, { echoUser: false })
160
208
  .catch((err) => {
161
209
  write(`${c.red("error")} ${describe(err)}\n\n`);
162
210
  })
@@ -172,11 +220,139 @@ export async function runChat(options: ChatClientOptions): Promise<void> {
172
220
  });
173
221
  }
174
222
 
223
+ /**
224
+ * Resolve the current continuation token for a session via `GET /v1/sessions`.
225
+ * Throws when the session is missing or not followable.
226
+ */
227
+ export async function lookupSessionContinuation(args: {
228
+ baseUrl: string;
229
+ sessionId: string;
230
+ auth?: RequestAuth;
231
+ }): Promise<string> {
232
+ const sessions = await listSessions({
233
+ baseUrl: args.baseUrl,
234
+ auth: args.auth,
235
+ });
236
+ const match = sessions.find((s) => s.sessionId === args.sessionId);
237
+ if (match === undefined) {
238
+ throw new Error(
239
+ `Session ${args.sessionId} not found in /v1/sessions (wrong id, or not owned by this caller). ` +
240
+ `Pass --continuation-token if you have it.`
241
+ );
242
+ }
243
+ const token = str(match.continuationToken);
244
+ if (token === undefined) {
245
+ throw new Error(
246
+ `Session ${args.sessionId} has no continuation token` +
247
+ (match.mode === "task" ? " (task sessions are not followable)." : ".")
248
+ );
249
+ }
250
+ return token;
251
+ }
252
+
253
+ async function attachSession(args: {
254
+ baseUrl: string;
255
+ auth: RequestAuth;
256
+ sessionId: string;
257
+ continuationToken?: string;
258
+ agentName: string;
259
+ palette: Palette;
260
+ write: (s: string) => void;
261
+ }): Promise<{
262
+ sessionId: string;
263
+ continuationToken: string;
264
+ nextEventIndex: number;
265
+ }> {
266
+ const { palette: c, write } = args;
267
+ let continuationToken = args.continuationToken;
268
+
269
+ if (continuationToken === undefined || continuationToken === "") {
270
+ continuationToken = await lookupSessionContinuation({
271
+ baseUrl: args.baseUrl,
272
+ sessionId: args.sessionId,
273
+ auth: args.auth,
274
+ });
275
+ }
276
+
277
+ const events = await fetchSessionEvents({
278
+ baseUrl: args.baseUrl,
279
+ sessionId: args.sessionId,
280
+ auth: args.auth,
281
+ });
282
+ if (events.length > 0) {
283
+ write(
284
+ c.dim(
285
+ `reattached ${args.sessionId} · replaying ${events.length} events\n\n`
286
+ )
287
+ );
288
+ renderTranscript(events, args.agentName, c, write);
289
+ write("\n");
290
+ } else {
291
+ write(c.dim(`reattached ${args.sessionId}\n\n`));
292
+ }
293
+
294
+ const lastIndex =
295
+ events.length === 0
296
+ ? -1
297
+ : Math.max(
298
+ ...events.map((e) => (typeof e.index === "number" ? e.index : -1))
299
+ );
300
+
301
+ return {
302
+ sessionId: args.sessionId,
303
+ continuationToken,
304
+ nextEventIndex: lastIndex + 1,
305
+ };
306
+ }
307
+
308
+ /** Compact historical transcript for reattach (final messages + tool calls). */
309
+ function renderTranscript(
310
+ events: SessionEvent[],
311
+ agentName: string,
312
+ c: Palette,
313
+ write: (s: string) => void
314
+ ): void {
315
+ for (const event of events) {
316
+ if (event.type === "message.received") {
317
+ write(`${c.cyan("› you")} ${event.data.text}\n`);
318
+ continue;
319
+ }
320
+ if (event.type === "message.completed") {
321
+ const text = event.data.text?.trim() ?? "";
322
+ if (text !== "" && event.data.finishReason !== "tool_call") {
323
+ write(`${c.green(`‹ ${agentName}`)} ${text}\n`);
324
+ }
325
+ continue;
326
+ }
327
+ if (event.type === "actions.requested") {
328
+ for (const call of event.data.calls) {
329
+ write(
330
+ ` ${c.magenta("⚙")} ${c.bold(call.toolName)}${c.dim(
331
+ formatArgs(call.args)
332
+ )}\n`
333
+ );
334
+ }
335
+ continue;
336
+ }
337
+ if (event.type === "action.result") {
338
+ const label = event.data.isError ? c.red("✗") : c.magenta("→");
339
+ write(
340
+ ` ${label} ${c.dim(truncate(stringify(event.data.output), 100))}\n`
341
+ );
342
+ continue;
343
+ }
344
+ if (event.type === "turn.completed") {
345
+ write(c.dim(" ─ turn done\n"));
346
+ }
347
+ }
348
+ }
349
+
175
350
  interface StreamTurnArgs {
176
351
  baseUrl: string;
177
352
  sessionId: string;
178
353
  startIndex: number;
179
- bearerToken: string | undefined;
354
+ auth: RequestAuth | undefined;
355
+ agentName: string;
180
356
  palette: Palette;
181
357
  write: (s: string) => void;
182
358
  }
@@ -191,7 +367,7 @@ const TERMINAL_TYPES = new Set([
191
367
  async function streamTurn(args: StreamTurnArgs): Promise<number> {
192
368
  const { palette: c, write } = args;
193
369
  const url = `${args.baseUrl}/v1/session/${args.sessionId}/stream?startIndex=${args.startIndex}`;
194
- const resp = await fetch(url, { headers: authHeaders(args.bearerToken) });
370
+ const resp = await fetch(url, { headers: mergeRequestHeaders(args.auth) });
195
371
  if (!resp.ok || resp.body === null) {
196
372
  write(`${c.red("error")} stream failed (${resp.status})\n`);
197
373
  return args.startIndex;
@@ -242,28 +418,46 @@ async function streamTurn(args: StreamTurnArgs): Promise<number> {
242
418
  }
243
419
  continue;
244
420
  }
245
- if (event.type === "reasoning.appended" && !thinkingShown) {
421
+ if (
422
+ event.type === "reasoning.appended" &&
423
+ event.data.parentCallId === undefined &&
424
+ !thinkingShown
425
+ ) {
246
426
  thinkingShown = true;
247
427
  write(c.dim(" · thinking…\n"));
248
428
  continue;
249
429
  }
250
430
  if (event.type === "message.appended") {
431
+ if (event.data.parentCallId !== undefined) {
432
+ continue;
433
+ }
251
434
  if (!assistantOpen) {
252
- write(`${c.green("‹ agent")} `);
435
+ write(`${c.green(`‹ ${args.agentName}`)} `);
253
436
  assistantOpen = true;
254
437
  }
255
438
  write(event.data.delta);
256
439
  continue;
257
440
  }
258
441
  if (event.type === "message.completed") {
442
+ if (event.data.parentCallId !== undefined) {
443
+ continue;
444
+ }
259
445
  closeAssistant();
260
446
  continue;
261
447
  }
262
448
  if (event.type === "actions.requested") {
263
449
  closeAssistant();
264
450
  for (const call of event.data.calls) {
451
+ if (call.toolName === "task" && call.parentCallId === undefined) {
452
+ continue;
453
+ }
454
+ const indent =
455
+ call.parentCallId !== undefined ||
456
+ event.data.parentCallId !== undefined
457
+ ? " "
458
+ : " ";
265
459
  write(
266
- ` ${c.magenta("⚙")} ${c.bold(call.toolName)}${c.dim(
460
+ `${indent}${c.magenta("⚙")} ${c.bold(call.toolName)}${c.dim(
267
461
  formatArgs(call.args)
268
462
  )}\n`
269
463
  );
@@ -290,9 +484,17 @@ async function streamTurn(args: StreamTurnArgs): Promise<number> {
290
484
  continue;
291
485
  }
292
486
  if (event.type === "action.result") {
487
+ if (
488
+ event.data.toolName === "task" &&
489
+ event.data.parentCallId === undefined
490
+ ) {
491
+ continue;
492
+ }
293
493
  const label = event.data.isError ? c.red("✗") : c.magenta("→");
494
+ const indent =
495
+ event.data.parentCallId !== undefined ? " " : " ";
294
496
  write(
295
- ` ${label} ${c.dim(truncate(stringify(event.data.output), 100))}\n`
497
+ `${indent}${label} ${c.dim(truncate(stringify(event.data.output), 100))}\n`
296
498
  );
297
499
  continue;
298
500
  }
@@ -338,20 +540,14 @@ async function streamTurn(args: StreamTurnArgs): Promise<number> {
338
540
  // HTTP helpers (built-in fetch; node 22+)
339
541
  // ---------------------------------------------------------------------------
340
542
 
341
- function authHeaders(bearerToken: string | undefined): Record<string, string> {
342
- return bearerToken === undefined
343
- ? {}
344
- : { authorization: `Bearer ${bearerToken}` };
345
- }
346
-
347
543
  async function getJson(
348
544
  baseUrl: string,
349
545
  path: string,
350
- bearerToken: string | undefined
546
+ auth: RequestAuth | undefined
351
547
  ): Promise<Record<string, unknown> | undefined> {
352
548
  try {
353
549
  const resp = await fetch(`${baseUrl}${path}`, {
354
- headers: authHeaders(bearerToken),
550
+ headers: mergeRequestHeaders(auth),
355
551
  });
356
552
  const text = await resp.text();
357
553
  return text ? (JSON.parse(text) as Record<string, unknown>) : undefined;
@@ -364,18 +560,37 @@ async function postJson(
364
560
  baseUrl: string,
365
561
  path: string,
366
562
  body: unknown,
367
- bearerToken: string | undefined
563
+ auth: RequestAuth | undefined
368
564
  ): Promise<Record<string, unknown> | undefined> {
369
565
  const resp = await fetch(`${baseUrl}${path}`, {
370
566
  method: "POST",
371
567
  headers: {
372
568
  "content-type": "application/json",
373
- ...authHeaders(bearerToken),
569
+ ...mergeRequestHeaders(auth),
374
570
  },
375
571
  body: JSON.stringify(body),
376
572
  });
377
573
  const text = await resp.text();
378
- return text ? (JSON.parse(text) as Record<string, unknown>) : undefined;
574
+ let parsed: Record<string, unknown> | undefined;
575
+ if (text) {
576
+ try {
577
+ parsed = JSON.parse(text) as Record<string, unknown>;
578
+ } catch {
579
+ parsed = { message: text };
580
+ }
581
+ }
582
+ if (!resp.ok) {
583
+ const anyrunReason = resp.headers.get("x-anyrun-failure-reason");
584
+ const message =
585
+ (typeof parsed?.message === "string" && parsed.message !== ""
586
+ ? parsed.message
587
+ : undefined) ??
588
+ (typeof parsed?.error === "string" ? parsed.error : undefined) ??
589
+ anyrunReason ??
590
+ `HTTP ${resp.status}`;
591
+ return { ok: false, error: "http_error", message, status: resp.status };
592
+ }
593
+ return parsed;
379
594
  }
380
595
 
381
596
  function str(value: unknown): string | undefined {