@tryarcanist/cli 0.1.79 → 0.1.81

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 (2) hide show
  1. package/dist/index.js +81 -42
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -200,7 +200,7 @@ function validateApiUrl(url) {
200
200
  // src/runtime.ts
201
201
  import { randomUUID } from "crypto";
202
202
  import { createInterface } from "readline/promises";
203
- var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
203
+ var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9:;<=>?]*[ -/]*[@-~]/g;
204
204
  function getRuntimeOptions(command, options = {}) {
205
205
  const globals = command?.optsWithGlobals?.();
206
206
  const merged = { ...globals, ...options };
@@ -392,7 +392,15 @@ function sleep(ms) {
392
392
  var MIN_WATCH_POLL_INTERVAL_MS = 1;
393
393
  var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
394
394
  var WATCH_REPLAY_PAGE_SIZE = 200;
395
- var WATCH_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "stopped", "archived"]);
395
+ var WATCH_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
396
+ "idle",
397
+ "completed",
398
+ "blocked",
399
+ "failed",
400
+ "stopped",
401
+ "stopped_resumable",
402
+ "archived"
403
+ ]);
396
404
 
397
405
  // src/uploads.ts
398
406
  import { readFile } from "fs/promises";
@@ -426,6 +434,27 @@ function validateUploadedName(name, kind) {
426
434
  function trimNamedItems(items) {
427
435
  return items.map((item) => ({ ...item, name: item.name.trim() }));
428
436
  }
437
+ function validateUploadItems({
438
+ items,
439
+ existingNames = [],
440
+ kind,
441
+ max,
442
+ label,
443
+ normalizeItems,
444
+ validateItem
445
+ }) {
446
+ const normalized = normalizeItems ? normalizeItems(items) : [...items];
447
+ const deduped = deduplicateByName(normalized, existingNames);
448
+ const capacityError = checkUploadCapacity(existingNames.length, deduped.length, max, label);
449
+ if (capacityError) return fail(capacityError);
450
+ for (const item of deduped) {
451
+ const nameError = validateUploadedName(item.name, kind);
452
+ if (nameError) return fail(nameError);
453
+ const itemError = validateItem(item);
454
+ if (itemError) return fail(itemError);
455
+ }
456
+ return ok(deduped);
457
+ }
429
458
  function hasNullBytes(content) {
430
459
  return content.slice(0, 8192).includes("\0");
431
460
  }
@@ -447,22 +476,24 @@ function deduplicateByName(items, existingNames = []) {
447
476
  return deduped;
448
477
  }
449
478
  function validateUploadedFilePayload(files, existingNames = []) {
450
- const trimmed = trimNamedItems(files);
451
- const deduped = deduplicateByName(trimmed, existingNames);
452
- const capacityError = checkUploadCapacity(existingNames.length, deduped.length, MAX_UPLOADED_FILES, "uploaded files");
453
- if (capacityError) return fail(capacityError);
454
- for (const file of deduped) {
455
- const nameError = validateUploadedName(file.name, "file");
456
- if (nameError) return fail(nameError);
457
- const byteLength = new TextEncoder().encode(file.content).byteLength;
458
- if (byteLength > MAX_UPLOADED_FILE_SIZE_BYTES) {
459
- return fail(`Uploaded file too large: ${file.name} (${byteLength} bytes, max ${MAX_UPLOADED_FILE_SIZE_BYTES})`);
460
- }
461
- if (hasNullBytes(file.content)) {
462
- return fail(`Binary files are not supported: ${file.name}`);
479
+ return validateUploadItems({
480
+ items: files,
481
+ existingNames,
482
+ kind: "file",
483
+ max: MAX_UPLOADED_FILES,
484
+ label: "uploaded files",
485
+ normalizeItems: trimNamedItems,
486
+ validateItem: (file) => {
487
+ const byteLength = new TextEncoder().encode(file.content).byteLength;
488
+ if (byteLength > MAX_UPLOADED_FILE_SIZE_BYTES) {
489
+ return `Uploaded file too large: ${file.name} (${byteLength} bytes, max ${MAX_UPLOADED_FILE_SIZE_BYTES})`;
490
+ }
491
+ if (hasNullBytes(file.content)) {
492
+ return `Binary files are not supported: ${file.name}`;
493
+ }
494
+ return null;
463
495
  }
464
- }
465
- return ok(deduped);
496
+ });
466
497
  }
467
498
 
468
499
  // src/uploads.ts
@@ -708,11 +739,17 @@ function getRawSessionEventData(event) {
708
739
  }
709
740
  }
710
741
  function normalizeRawSessionEvent(event) {
742
+ const data = getRawSessionEventData(event);
711
743
  return {
744
+ raw: event,
712
745
  type: getRawSessionEventKind(event),
713
- data: getRawSessionEventData(event)
746
+ data,
747
+ promptId: getRawSessionEventPromptId(event)
714
748
  };
715
749
  }
750
+ function normalizeRawSessionEvents(events) {
751
+ return events.map((event) => normalizeRawSessionEvent(event));
752
+ }
716
753
  function shouldAppendTextDelta(existingText, incomingText) {
717
754
  if (!incomingText) return false;
718
755
  if (incomingText.length < DUPLICATE_TEXT_DELTA_MIN_CHARS) return true;
@@ -1106,8 +1143,7 @@ function applyTodoUpdate(data, state) {
1106
1143
  }
1107
1144
  function flattenSessionEvents(raw) {
1108
1145
  const state = createFlattenState();
1109
- for (const event of raw) {
1110
- const normalized = normalizeRawSessionEvent(event);
1146
+ for (const normalized of normalizeRawSessionEvents(raw)) {
1111
1147
  const { type } = normalized;
1112
1148
  const data = normalized.data;
1113
1149
  switch (type) {
@@ -1184,47 +1220,47 @@ function partitionEventsByPrompt(allEvents, promptIds) {
1184
1220
  const promptSet = new Set(promptIds);
1185
1221
  const buckets = new Map(promptIds.map((id) => [id, []]));
1186
1222
  let currentPromptId = null;
1187
- for (const event of allEvents) {
1188
- const eventPromptId = getRawSessionEventPromptId(event);
1223
+ for (const event of normalizeRawSessionEvents(allEvents)) {
1224
+ const eventPromptId = event.promptId;
1189
1225
  const explicitPromptId = typeof eventPromptId === "string" && promptSet.has(eventPromptId) ? eventPromptId : null;
1190
- if (getRawSessionEventKind(event) === "prompt_processing") {
1226
+ if (event.type === "prompt_processing") {
1191
1227
  currentPromptId = explicitPromptId;
1192
1228
  }
1193
1229
  const targetPromptId = explicitPromptId ?? currentPromptId;
1194
1230
  if (targetPromptId) {
1195
1231
  const bucket = buckets.get(targetPromptId);
1196
- if (bucket) bucket.push(event);
1232
+ if (bucket) bucket.push(event.raw);
1197
1233
  }
1198
1234
  }
1199
1235
  return buckets;
1200
1236
  }
1201
- function getEmbeddedTerminalHistory(raw) {
1202
- for (let index = raw.length - 1; index >= 0; index--) {
1203
- const event = raw[index];
1204
- const eventType = getRawSessionEventKind(event);
1205
- if (eventType !== "prompt_completed" && eventType !== "prompt_failed") continue;
1206
- const history = getRawSessionEventData(event)?.history;
1237
+ function getEmbeddedTerminalHistoryFromNormalized(normalizedEvents) {
1238
+ for (let index = normalizedEvents.length - 1; index >= 0; index--) {
1239
+ const event = normalizedEvents[index];
1240
+ if (event.type !== "prompt_completed" && event.type !== "prompt_failed") continue;
1241
+ const history = event.data?.history;
1207
1242
  if (!Array.isArray(history) || history.length === 0) return null;
1208
1243
  return history;
1209
1244
  }
1210
1245
  return null;
1211
1246
  }
1212
1247
  function promptActivityKey(event) {
1213
- const data = getRawSessionEventData(event);
1248
+ const data = event.data;
1214
1249
  return [
1215
- getRawSessionEventPromptId(event) ?? "",
1250
+ event.promptId ?? "",
1216
1251
  typeof data?.phase === "string" ? data.phase : "",
1217
1252
  typeof data?.detail === "string" ? data.detail : ""
1218
1253
  ].join("\0");
1219
1254
  }
1220
1255
  function agentProgressKey(event) {
1221
- const data = getRawSessionEventData(event);
1222
- return [getRawSessionEventPromptId(event) ?? "", typeof data?.step === "string" ? data.step : ""].join("\0");
1256
+ const data = event.data;
1257
+ return [event.promptId ?? "", typeof data?.step === "string" ? data.step : ""].join("\0");
1223
1258
  }
1224
1259
  function resolveAuthoritativePromptEventsWithDiagnostics(raw) {
1225
- const embeddedHistory = getEmbeddedTerminalHistory(raw);
1226
- const durablePromptActivity = raw.filter((event) => getRawSessionEventKind(event) === "prompt_activity");
1227
- const durableAgentProgress = raw.filter((event) => getRawSessionEventKind(event) === "agent_progress");
1260
+ const normalizedRaw = normalizeRawSessionEvents(raw);
1261
+ const embeddedHistory = getEmbeddedTerminalHistoryFromNormalized(normalizedRaw);
1262
+ const durablePromptActivity = normalizedRaw.filter((event) => event.type === "prompt_activity");
1263
+ const durableAgentProgress = normalizedRaw.filter((event) => event.type === "agent_progress");
1228
1264
  if (!embeddedHistory) {
1229
1265
  return {
1230
1266
  events: raw,
@@ -1241,8 +1277,9 @@ function resolveAuthoritativePromptEventsWithDiagnostics(raw) {
1241
1277
  }
1242
1278
  };
1243
1279
  }
1244
- const embeddedPromptActivity = embeddedHistory.filter((event) => getRawSessionEventKind(event) === "prompt_activity");
1245
- const embeddedAgentProgress = embeddedHistory.filter((event) => getRawSessionEventKind(event) === "agent_progress");
1280
+ const normalizedEmbeddedHistory = normalizeRawSessionEvents(embeddedHistory);
1281
+ const embeddedPromptActivity = normalizedEmbeddedHistory.filter((event) => event.type === "prompt_activity");
1282
+ const embeddedAgentProgress = normalizedEmbeddedHistory.filter((event) => event.type === "agent_progress");
1246
1283
  const embeddedPromptActivityKeys = new Set(embeddedPromptActivity.map(promptActivityKey));
1247
1284
  const embeddedAgentProgressKeys = new Set(embeddedAgentProgress.map(agentProgressKey));
1248
1285
  const embeddedPromptActivityCount = embeddedPromptActivity.length;
@@ -1267,7 +1304,9 @@ function resolveAuthoritativePromptEventsWithDiagnostics(raw) {
1267
1304
  embeddedAgentProgressKeys.add(key);
1268
1305
  return true;
1269
1306
  });
1270
- const missingDurableSideChannelEvents = [...missingDurablePromptActivity, ...missingDurableAgentProgress];
1307
+ const missingDurableSideChannelEvents = [...missingDurablePromptActivity, ...missingDurableAgentProgress].map(
1308
+ (event) => event.raw
1309
+ );
1271
1310
  return {
1272
1311
  events: missingDurableSideChannelEvents.length > 0 ? [...missingDurableSideChannelEvents, ...embeddedHistory] : embeddedHistory,
1273
1312
  diagnostics: {
@@ -1847,7 +1886,7 @@ async function waitForCreatedPrompt(sessionId, promptId, sessionUrl, pollInterva
1847
1886
  createdPrompt = await fetchCreatedPromptStatus(config, sessionId, promptId);
1848
1887
  }
1849
1888
  if (!createdPrompt) {
1850
- throw new CliError("server", `Prompt status was unavailable after session ${sessionId} became idle.`, {
1889
+ throw new CliError("server", `Prompt status was unavailable after session ${sessionId} reached a terminal state.`, {
1851
1890
  hint: `Inspect with: arcanist sessions transcript ${sessionId}`
1852
1891
  });
1853
1892
  }
@@ -1857,7 +1896,7 @@ async function waitForCreatedPrompt(sessionId, promptId, sessionUrl, pollInterva
1857
1896
  if (createdPrompt.status !== "completed") {
1858
1897
  throw new CliError(
1859
1898
  "server",
1860
- `Prompt ${createdPrompt.promptId} did not reach a terminal success state before session ${sessionId} became idle (status: ${createdPrompt.status}).`,
1899
+ `Prompt ${createdPrompt.promptId} did not reach a terminal success state before session ${sessionId} reached a terminal state (status: ${createdPrompt.status}).`,
1861
1900
  {
1862
1901
  hint: `Inspect with: arcanist sessions transcript ${sessionId}`
1863
1902
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {