@tryarcanist/cli 0.1.99 → 0.1.101

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 (3) hide show
  1. package/README.md +10 -10
  2. package/dist/index.js +49 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,7 +34,7 @@ arcanist auth login
34
34
 
35
35
  Token config is stored at `~/.arcanist/config.json`. Multiple active tokens per user are supported; revoke tokens independently when a device or workflow no longer needs access.
36
36
 
37
- Use a read-scoped token for inspection and debugging. Use a write-scoped token only when you need to create sessions, send prompts, or stop runs.
37
+ Use a read-scoped token for inspection and debugging; use a write-scoped token only to create sessions, send prompts, or stop runs.
38
38
 
39
39
  You can also pipe the token via stdin:
40
40
 
@@ -55,7 +55,7 @@ export ARCANIST_TOKEN=arc_...
55
55
  export ARCANIST_API_URL=https://app.tryarcanist.com
56
56
  ```
57
57
 
58
- `--token` and `--api-url` are available for one-off invocations. Prefer `ARCANIST_TOKEN` over `--token` for persistent use because CLI flags can appear in shell history and process lists.
58
+ `--token` and `--api-url` are available for one-off invocations. Prefer `ARCANIST_TOKEN` over `--token` for persistent use; CLI flags can appear in shell history and process lists.
59
59
 
60
60
  Non-local API URLs must use HTTPS and must not embed credentials.
61
61
 
@@ -128,17 +128,17 @@ arcanist sessions create your-org/your-repo "verify the deployed change" --cold
128
128
  arcanist sessions create your-org/your-repo "retry-safe create" --idempotency-key 1f0e6f1a-...
129
129
  ```
130
130
 
131
- Use `--backend` to pick the agent runtime backend: `codex` (default) or `claude_code`. The `--model` must be valid for the chosen backend — codex runs OpenAI models (`gpt-5.5` default, `gpt-5.4`), `claude_code` runs Anthropic models (`claude-opus-4-8` default, `claude-sonnet-4-6`) — and the CLI rejects a mismatch before any network call. `claude_code` sessions require an Anthropic API key configured in Settings.
131
+ `--backend` picks the agent runtime backend: `codex` (default) or `claude_code`. `--model` must be valid for the chosen backend — codex runs OpenAI models (`gpt-5.5` default, `gpt-5.4`), `claude_code` runs Anthropic models (`claude-opus-4-8` default, `claude-sonnet-4-6`) — and the CLI rejects a mismatch before any network call. `claude_code` sessions require an Anthropic API key configured in Settings.
132
132
 
133
- Use `--wait` to block until the created prompt finishes. The command exits non-zero if the prompt finishes with `status: failed`, which makes it suitable for cron or other schedulers that alert on command failure. Use `--poll-interval <ms>` to tune how often the CLI checks for completion.
133
+ `--wait` blocks until the created prompt finishes and exits non-zero if the prompt finishes with `status: failed`, making it suitable for cron or other schedulers that alert on command failure. `--poll-interval <ms>` tunes the completion check frequency.
134
134
 
135
- Use repeatable `--uploaded-file <path>` flags to attach local UTF-8 text files to the prompt. Uploaded file names come from the local basename; directory components are not sent.
135
+ Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the prompt. Uploaded file names come from the local basename; directory components are not sent.
136
136
 
137
- Use `--cold` to force a fresh sandbox and skip the warm pool. This is useful for verifying freshly deployed code when a pre-warmed sandbox may predate the deploy.
137
+ `--cold` forces a fresh sandbox and skips the warm pool useful for verifying freshly deployed code when a pre-warmed sandbox may predate the deploy.
138
138
 
139
- Use `--idempotency-key <uuid>` when manually retrying a create request that may have reached the server. The CLI derives separate session and prompt idempotency keys from the provided value.
139
+ `--idempotency-key <uuid>` is for manually retrying a create request that may have reached the server. The CLI derives separate session and prompt idempotency keys from the provided value.
140
140
 
141
- JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` is emitted only when `--backend` is passed.
141
+ JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed.
142
142
 
143
143
  ### `arcanist sessions send <session-id> [prompt]`
144
144
 
@@ -155,7 +155,7 @@ JSON mode returns `{sessionId, promptId?}`.
155
155
 
156
156
  ### `arcanist sessions stop <session-id>`
157
157
 
158
- Stops the active run for a session. Stop is idempotent: if no sandbox is active, the server returns `already_stopped`.
158
+ Stops the active run for a session. Idempotent: if no sandbox is active, the server returns `already_stopped`.
159
159
 
160
160
  ```bash
161
161
  arcanist sessions stop abc123
@@ -190,7 +190,7 @@ arcanist sessions search "mcp debugging" --repo your-org/your-repo --json
190
190
  arcanist sessions search "repo access" --status idle --scope business --limit 20 --cursor <cursor>
191
191
  ```
192
192
 
193
- Search uses the same metadata-only index and filters as `sessions list`: `--status`, `--scope`, `--repo`, `--limit`, and `--cursor`.
193
+ Uses the same metadata-only index and filters as `sessions list`: `--status`, `--scope`, `--repo`, `--limit`, and `--cursor`.
194
194
 
195
195
  ### `arcanist sessions events <session-id>`
196
196
 
package/dist/index.js CHANGED
@@ -862,6 +862,22 @@ function noChangeOutcomeCopy(reason) {
862
862
  }
863
863
  }
864
864
 
865
+ // ../../shared/session/transient-disconnect.ts
866
+ var TRANSIENT_DISCONNECT_VISIBILITY_MS = 15e3;
867
+ function nextDisconnectMask(mask, previous, current, now) {
868
+ if (!current || current.sandboxSubstate !== "reconnecting") return null;
869
+ if (mask) return mask;
870
+ if (!previous || previous.sandboxSubstate === "reconnecting") return null;
871
+ return { since: now, heldPhase: previous.phase, heldSubstate: previous.sandboxSubstate ?? "none" };
872
+ }
873
+ function isDisconnectMaskActive(mask, now) {
874
+ return mask !== null && now - mask.since < TRANSIENT_DISCONNECT_VISIBILITY_MS;
875
+ }
876
+ function applyDisconnectMask(view, mask, now) {
877
+ if (!isDisconnectMaskActive(mask, now)) return view;
878
+ return { ...view, phase: mask.heldPhase, sandboxSubstate: mask.heldSubstate };
879
+ }
880
+
865
881
  // ../../shared/transcript/malformed-search.ts
866
882
  var MALFORMED_SEARCH_BLOCKED_TRANSCRIPT_PREFIX = "Arcanist blocked this malformed search command before execution.";
867
883
  var BASH_UNMATCHED_QUOTE_EOF_RE = /\/bin\/bash:\s+-c:\s+line\s+\d+:\s+unexpected EOF while looking for matching [`'"][`'"]?/i;
@@ -1244,6 +1260,24 @@ function projectRetryStatus(data, index) {
1244
1260
  ...typeof data?.retryAfterMs === "number" ? { retryAfterMs: data.retryAfterMs } : {}
1245
1261
  };
1246
1262
  }
1263
+ function projectPromptRetrying(data, index) {
1264
+ const attempt = typeof data?.attempt === "number" ? data.attempt : Number(data?.attempt ?? 0);
1265
+ const cap = typeof data?.cap === "number" ? data.cap : void 0;
1266
+ const retryPromptId = typeof data?.retryPromptId === "string" ? data.retryPromptId : resolvePromptId(data);
1267
+ return {
1268
+ type: "retry_status",
1269
+ // Key off retryPromptId (always present in the emitted payload, unique per
1270
+ // retry) rather than the index, so the id is stable across replays. The
1271
+ // event's `timestamp` lives at the top level, not inside `data`.
1272
+ id: `pr-retry-${retryPromptId ?? index}`,
1273
+ ...retryPromptId ? { promptId: retryPromptId } : {},
1274
+ attempt,
1275
+ message: "Sandbox dropped mid-task; retrying on a fresh sandbox",
1276
+ scope: "sandbox_disconnect",
1277
+ ...cap !== void 0 ? { maxAttempts: cap } : {},
1278
+ ...typeof data?.reason === "string" ? { reason: data.reason } : {}
1279
+ };
1280
+ }
1247
1281
  function projectBranchChanged(data, index) {
1248
1282
  return {
1249
1283
  type: "branch_changed",
@@ -1515,6 +1549,9 @@ function flattenSessionEvents(raw) {
1515
1549
  case "retry_status":
1516
1550
  pushEvent(state, projectRetryStatus(data, state.merged.length));
1517
1551
  break;
1552
+ case "prompt_retrying":
1553
+ pushEvent(state, projectPromptRetrying(data, state.merged.length));
1554
+ break;
1518
1555
  case "branch_changed":
1519
1556
  pushEvent(state, projectBranchChanged(data, state.merged.length));
1520
1557
  break;
@@ -2139,6 +2176,12 @@ function formatStatusLine(status) {
2139
2176
  if (status.spawnDurationMs != null) details.push(`spawn ${(status.spawnDurationMs / 1e3).toFixed(1)}s`);
2140
2177
  return details.length > 0 ? `[status] ${phaseLabel} | ${details.join(" | ")}` : `[status] ${phaseLabel}`;
2141
2178
  }
2179
+ function advanceStatusDisconnectMask(status, mask, lastView, now) {
2180
+ const view = status.phase ? { phase: status.phase, sandboxSubstate: status.sandboxSubstate } : null;
2181
+ const nextMask = nextDisconnectMask(mask, lastView, view, now);
2182
+ const displayStatus = view ? { ...status, ...applyDisconnectMask(view, nextMask, now) } : status;
2183
+ return { displayStatus, mask: nextMask, view };
2184
+ }
2142
2185
  async function printNoChangeOutcome(config, sessionId) {
2143
2186
  try {
2144
2187
  const data = await apiFetch(
@@ -2197,6 +2240,8 @@ async function watchCommand(sessionId, options, command) {
2197
2240
  let afterSequence = initialAfterSequence;
2198
2241
  let lastStatusLine = null;
2199
2242
  let textOpen = false;
2243
+ let disconnectMask = null;
2244
+ let lastStatusView = null;
2200
2245
  if (!json) console.log(`Watching session ${sessionId}...`);
2201
2246
  try {
2202
2247
  while (true) {
@@ -2208,7 +2253,10 @@ async function watchCommand(sessionId, options, command) {
2208
2253
  const parsed = parseSsePayload(payload);
2209
2254
  const receivedFullPage = parsed.events.length >= pageSize;
2210
2255
  if (!json && parsed.status) {
2211
- const nextStatusLine = formatStatusLine(parsed.status);
2256
+ const advanced = advanceStatusDisconnectMask(parsed.status, disconnectMask, lastStatusView, Date.now());
2257
+ disconnectMask = advanced.mask;
2258
+ lastStatusView = advanced.view;
2259
+ const nextStatusLine = formatStatusLine(advanced.displayStatus);
2212
2260
  if (nextStatusLine !== lastStatusLine) {
2213
2261
  if (textOpen) {
2214
2262
  process.stdout.write("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.99",
3
+ "version": "0.1.101",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {