@tryarcanist/cli 0.1.148 → 0.1.150

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 +4 -1
  2. package/dist/index.js +79 -50
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -119,6 +119,7 @@ Creates a new session and sends the initial prompt.
119
119
  ```bash
120
120
  arcanist sessions create https://github.com/your-org/your-repo "fix the login bug"
121
121
  arcanist sessions create your-org/your-repo "continue this work" --start-branch wip/resume-me
122
+ arcanist sessions create your-org/your-repo "finish this PR" --continue-pr https://github.com/your-org/your-repo/pull/123
122
123
  printf "add tests" | arcanist sessions create your-org/your-repo --prompt-stdin --json
123
124
  printf "add tests" | arcanist sessions create your-org/your-repo --prompt-stdin --wait
124
125
  arcanist sessions create your-org/your-repo - --model gpt-5.5
@@ -138,6 +139,8 @@ Use `--base-branch <branch>` to target a non-default base branch. The CLI only v
138
139
 
139
140
  Use `--start-branch <branch>` to resume an existing branch with its history instead of forking a fresh branch off base; the control plane verifies the branch exists on the remote and fails closed if it does not.
140
141
 
142
+ Use `--continue-pr <url>` to continue an open same-repo pull request. The control plane checks out the PR head branch before the prompt runs. `--continue-mode update-pr` updates the referenced PR; `--continue-mode new-pr` starts from that PR head but leaves publish free to open a fresh PR. `auto` is the default and currently resolves to same-PR update for supported open same-repo PRs.
143
+
141
144
  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.
142
145
 
143
146
  `--cold` is a deprecated no-op retained for backward compatibility. Sessions always start from a fresh sandbox (the warm sandbox pool was removed), so the flag has no effect.
@@ -146,7 +149,7 @@ Repeatable `--uploaded-file <path>` flags attach local UTF-8 text files to the p
146
149
 
147
150
  `--onboarding` creates an onboarding session: the agent authors the repo's `.arcanist.json`, `.arcanist/` runtime files, `ARCANIST.md`, and conditionally `.arcanist/sandbox.yaml` + `.arcanist/sandbox.layer.Dockerfile` (when the base sandbox lacks a needed toolchain), proves what it can inside its sandbox, and opens the setup PR ready for review. The onboarding behavior is driven by the bridge's canonical onboarding playbook, not the prompt, so `--onboarding` needs no prompt: `arcanist sessions create <repo> --onboarding --wait`. Any prompt supplied alongside `--onboarding` is ignored. Team use; not part of the external API surface.
148
151
 
149
- JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, onboarding?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed. With `--wait`, JSON mode also includes best-effort result fields when available: `prUrl?`, `publishedBranch?`, and `lastBranch?`.
152
+ JSON mode returns `{sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, continuePrUrl?, continueMode?, onboarding?, promptId?}`. `sessionUrl` is emitted only when the server returns it; `agentRuntimeBackend` only when `--backend` is passed. With `--wait`, JSON mode also includes best-effort result fields when available: `prUrl?`, `publishedBranch?`, and `lastBranch?`.
150
153
 
151
154
  ### `arcanist sessions send <session-id> [prompt]`
152
155
 
package/dist/index.js CHANGED
@@ -568,6 +568,7 @@ var OpenAIModel = {
568
568
  GPT54Pro: "gpt-5.4-pro",
569
569
  GPT54Mini: "gpt-5.4-mini",
570
570
  GPT54Nano: "gpt-5.4-nano",
571
+ GPT53CodexSpark: "gpt-5.3-codex-spark",
571
572
  GPT53Codex: "gpt-5.3-codex",
572
573
  GPT52: "gpt-5.2",
573
574
  GPT52ChatLatest: "gpt-5.2-chat-latest",
@@ -585,17 +586,6 @@ var MODEL_PROVIDERS_SET = /* @__PURE__ */ new Set([
585
586
  "anthropic",
586
587
  "baseten"
587
588
  ]);
588
- var SESSION_START_MODEL_IDS_BY_BACKEND = {
589
- [CODEX_AGENT_RUNTIME_BACKEND]: [OpenAIModel.GPT55, OpenAIModel.GPT54, OpenAIModel.GPT54Nano],
590
- [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: [AnthropicModel.Opus48, AnthropicModel.Sonnet46],
591
- [OPENCODE_AGENT_RUNTIME_BACKEND]: [BasetenModel.GptOss120B]
592
- };
593
- var DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND = {
594
- [CODEX_AGENT_RUNTIME_BACKEND]: OpenAIModel.GPT54,
595
- [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: AnthropicModel.Opus48,
596
- [OPENCODE_AGENT_RUNTIME_BACKEND]: BasetenModel.GptOss120B
597
- };
598
- var DEFAULT_SESSION_START_MODEL_ID = DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
599
589
  var MODEL_REGISTRY = [
600
590
  {
601
591
  id: OpenAIModel.GPT54,
@@ -616,7 +606,8 @@ var MODEL_REGISTRY = [
616
606
  outputPerMillion: 22.5,
617
607
  cacheReadPerMillion: 0.5
618
608
  }
619
- }
609
+ },
610
+ sessionStart: { eligible: true, isDefault: true }
620
611
  },
621
612
  {
622
613
  id: OpenAIModel.GPT55,
@@ -635,7 +626,8 @@ var MODEL_REGISTRY = [
635
626
  outputPerMillion: 45,
636
627
  cacheReadPerMillion: 1
637
628
  }
638
- }
629
+ },
630
+ sessionStart: { eligible: true }
639
631
  },
640
632
  {
641
633
  id: OpenAIModel.GPT54Mini,
@@ -662,7 +654,20 @@ var MODEL_REGISTRY = [
662
654
  backends: [CODEX_AGENT_RUNTIME_BACKEND],
663
655
  contextWindow: 4e5,
664
656
  reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 },
665
- pricing: { inputPerMillion: 0.2, outputPerMillion: 1.25, cacheReadPerMillion: 0.02 }
657
+ pricing: { inputPerMillion: 0.2, outputPerMillion: 1.25, cacheReadPerMillion: 0.02 },
658
+ sessionStart: { eligible: true }
659
+ },
660
+ {
661
+ id: OpenAIModel.GPT53CodexSpark,
662
+ name: "GPT-5.3 Codex Spark",
663
+ provider: "openai",
664
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
665
+ contextWindow: 128e3,
666
+ reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" },
667
+ costTracked: false,
668
+ requiresCodexSubscriptionAuth: true,
669
+ sessionStart: { eligible: true },
670
+ visibility: "internal_probe"
666
671
  },
667
672
  {
668
673
  id: OpenAIModel.GPT53Codex,
@@ -713,7 +718,8 @@ var MODEL_REGISTRY = [
713
718
  // Bridge cost-ESTIMATE only; NOT authoritative for control-plane Anthropic
714
719
  // billing. `anthropic/cost.ts` ANTHROPIC_MESSAGES_MODEL_PRICING owns that
715
720
  // and is intentionally ~3x divergent until Phase 5.
716
- pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.5, cacheWritePerMillion: 6.25 }
721
+ pricing: { inputPerMillion: 5, outputPerMillion: 25, cacheReadPerMillion: 0.5, cacheWritePerMillion: 6.25 },
722
+ sessionStart: { eligible: true, isDefault: true }
717
723
  },
718
724
  {
719
725
  id: AnthropicModel.Sonnet46,
@@ -727,7 +733,8 @@ var MODEL_REGISTRY = [
727
733
  // Bridge cost-ESTIMATE only; NOT authoritative for control-plane Anthropic
728
734
  // billing. `anthropic/cost.ts` ANTHROPIC_MESSAGES_MODEL_PRICING owns that
729
735
  // and is intentionally ~3x divergent until Phase 5.
730
- pricing: { inputPerMillion: 3, outputPerMillion: 15, cacheReadPerMillion: 0.3, cacheWritePerMillion: 3.75 }
736
+ pricing: { inputPerMillion: 3, outputPerMillion: 15, cacheReadPerMillion: 0.3, cacheWritePerMillion: 3.75 },
737
+ sessionStart: { eligible: true }
731
738
  },
732
739
  {
733
740
  id: BasetenModel.GptOss120B,
@@ -741,7 +748,8 @@ var MODEL_REGISTRY = [
741
748
  // Internal cold-spawn bridge ESTIMATE, NOT authoritative Baseten API
742
749
  // billing. `baseten/cost.ts` BASETEN_MODEL_API_PRICING (wire-id keyed)
743
750
  // owns real billing.
744
- pricing: { inputPerMillion: 0, outputPerMillion: 0 }
751
+ pricing: { inputPerMillion: 0, outputPerMillion: 0 },
752
+ sessionStart: { eligible: true, isDefault: true }
745
753
  }
746
754
  ];
747
755
  var MODEL_PROVIDER_NAMES = {
@@ -749,6 +757,32 @@ var MODEL_PROVIDER_NAMES = {
749
757
  anthropic: "Anthropic",
750
758
  baseten: "Baseten"
751
759
  };
760
+ function buildSessionStartModelIdsByBackend() {
761
+ const byBackend = Object.fromEntries(AGENT_RUNTIME_BACKENDS.map((backend) => [backend, []]));
762
+ for (const model of MODEL_REGISTRY) {
763
+ if (!model.sessionStart?.eligible) continue;
764
+ for (const backend of model.backends) {
765
+ byBackend[backend].push(model.id);
766
+ }
767
+ }
768
+ return byBackend;
769
+ }
770
+ function buildDefaultSessionStartModelIdByBackend() {
771
+ const defaults = {};
772
+ for (const backend of AGENT_RUNTIME_BACKENDS) {
773
+ const backendDefaults = MODEL_REGISTRY.filter(
774
+ (model) => model.sessionStart?.eligible && model.sessionStart.isDefault === true && model.backends.includes(backend)
775
+ );
776
+ if (backendDefaults.length !== 1) {
777
+ throw new Error(`Expected exactly one default session-start model for ${backend}, got ${backendDefaults.length}`);
778
+ }
779
+ defaults[backend] = backendDefaults[0].id;
780
+ }
781
+ return defaults;
782
+ }
783
+ var SESSION_START_MODEL_IDS_BY_BACKEND = buildSessionStartModelIdsByBackend();
784
+ var DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND = buildDefaultSessionStartModelIdByBackend();
785
+ var DEFAULT_SESSION_START_MODEL_ID = DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
752
786
  var VALID_MODEL_IDS = new Set(MODEL_REGISTRY.map((model) => model.id));
753
787
  var VALID_SESSION_START_MODEL_IDS_BY_BACKEND = {
754
788
  [CODEX_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND]),
@@ -813,21 +847,6 @@ function getSessionStartModelIdsForBackend(backend) {
813
847
  function isSessionStartModelAllowedForBackend(modelId, backend) {
814
848
  return VALID_SESSION_START_MODEL_IDS_BY_BACKEND[backend].has(modelId);
815
849
  }
816
- var MODEL_TIER_BY_ID = {
817
- [OpenAIModel.GPT55]: "frontier",
818
- [OpenAIModel.GPT54]: "mid",
819
- [OpenAIModel.GPT54Nano]: "mid",
820
- [AnthropicModel.Opus48]: "frontier",
821
- [AnthropicModel.Sonnet46]: "mid",
822
- [BasetenModel.GptOss120B]: "mid"
823
- };
824
- var CODEX_MODEL_ID_BY_TIER = {
825
- // Single source of truth: the frontier codex model follows the codex default,
826
- // so changing the default propagates here automatically. Only used for
827
- // non-codex (e.g. claude) parents — codex parents verify on their own model.
828
- frontier: DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND],
829
- mid: OpenAIModel.GPT54
830
- };
831
850
  function getProviderForModel(modelId) {
832
851
  return MODEL_PROVIDERS[modelId] ?? "openai";
833
852
  }
@@ -884,6 +903,11 @@ var PUBLIC_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
884
903
  (model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && model.visibility !== "internal_probe"
885
904
  )
886
905
  );
906
+ var CODEX_SUBSCRIPTION_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
907
+ MODEL_REGISTRY.filter(
908
+ (model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && (model.visibility !== "internal_probe" || model.requiresCodexSubscriptionAuth === true)
909
+ )
910
+ );
887
911
 
888
912
  // ../../shared/utils/timing.ts
889
913
  function sleep(ms) {
@@ -1512,14 +1536,6 @@ function projectPromptRetrying(data, index) {
1512
1536
  ...typeof data?.reason === "string" ? { reason: data.reason } : {}
1513
1537
  };
1514
1538
  }
1515
- function projectBranchChanged(data, index) {
1516
- return {
1517
- type: "branch_changed",
1518
- id: `bc-${data?.timestamp ?? index}`,
1519
- branch: typeof data?.branch === "string" ? data.branch : "",
1520
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
1521
- };
1522
- }
1523
1539
  function projectMemoryUsage(data, index) {
1524
1540
  const activeMemoryIds = Array.isArray(data?.activeMemoryIds) ? data.activeMemoryIds.filter((id) => typeof id === "string" && id.trim().length > 0) : [];
1525
1541
  const activeMemories = mergeMemoryRefs(activeMemoryIds, data?.activeMemories);
@@ -1786,9 +1802,6 @@ function flattenSessionEvents(raw) {
1786
1802
  case "prompt_retrying":
1787
1803
  pushEvent(state, projectPromptRetrying(data, state.merged.length));
1788
1804
  break;
1789
- case "branch_changed":
1790
- pushEvent(state, projectBranchChanged(data, state.merged.length));
1791
- break;
1792
1805
  case "memory_usage":
1793
1806
  pushEvent(state, projectMemoryUsage(data, state.merged.length));
1794
1807
  break;
@@ -2104,9 +2117,6 @@ ${event.answer ? `**Answer:** ${event.answer}
2104
2117
  `;
2105
2118
  }
2106
2119
  return `*[${event.tool} output truncated]*
2107
- `;
2108
- case "branch_changed":
2109
- return `*[switched to branch \`${event.branch}\`]*
2110
2120
  `;
2111
2121
  case "memory_usage":
2112
2122
  return event.activeMemoryIds.length > 0 ? `*[used ${event.activeMemoryIds.length === 1 ? "1 memory" : `${event.activeMemoryIds.length} memories`}: ${event.activeMemories.map((memory) => `\`${memory.title || memory.id}\``).join(", ")}]*
@@ -2390,8 +2400,6 @@ function renderWatchEvent(event, state) {
2390
2400
  const files = Array.isArray(data.files) ? data.files.filter((item) => typeof item === "string") : [];
2391
2401
  return { kind: "line", line: `[patch] ${files.join(", ")}` };
2392
2402
  }
2393
- case "branch_changed":
2394
- return { kind: "line", line: `[branch] ${String(data.branch ?? "")}` };
2395
2403
  case "agent_timeline": {
2396
2404
  const eventType = String(data.eventType ?? "timeline");
2397
2405
  const status = typeof data.status === "string" ? ` ${data.status}` : "";
@@ -2601,6 +2609,7 @@ function validateRepoUrl(url) {
2601
2609
  }
2602
2610
  var PROMPT_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
2603
2611
  var RESULT_FETCH_ATTEMPTS = 5;
2612
+ var SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS = 4e3;
2604
2613
  var ONBOARDING_PROMPT = "Onboard this repository onto Arcanist.";
2605
2614
  function selectCreatedPrompt(prompts, promptId) {
2606
2615
  if (prompts.length === 0) return null;
@@ -2616,6 +2625,11 @@ function buildPromptFailureError(sessionId, prompt, sessionUrl) {
2616
2625
  hint: `Inspect with: arcanist sessions transcript ${sessionId}`
2617
2626
  });
2618
2627
  }
2628
+ function buildSessionCreatePromptPreview(prompt) {
2629
+ const trimmed = prompt.trim();
2630
+ if (!trimmed) return void 0;
2631
+ return trimmed.length > SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS ? trimmed.slice(0, SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS).trimEnd() : trimmed;
2632
+ }
2619
2633
  async function fetchCreatedPromptStatus(config, sessionId, promptId) {
2620
2634
  const promptList = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
2621
2635
  return selectCreatedPrompt(promptList.prompts, promptId);
@@ -2719,6 +2733,14 @@ async function createCommand(repoUrl, promptArg, options, command) {
2719
2733
  if (options.startBranch !== void 0 && !startBranch) {
2720
2734
  throw new CliError("user", "--start-branch must not be empty.");
2721
2735
  }
2736
+ const continuePr = options.continuePr?.trim();
2737
+ if (options.continuePr !== void 0 && !continuePr) {
2738
+ throw new CliError("user", "--continue-pr must not be empty.");
2739
+ }
2740
+ const continueMode = options.continueMode?.trim();
2741
+ if (options.continueMode !== void 0 && !continueMode) {
2742
+ throw new CliError("user", "--continue-mode must not be empty.");
2743
+ }
2722
2744
  const uploadedFiles = await resolveUploadedFileOptions(options.uploadedFile);
2723
2745
  const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
2724
2746
  const sessionIdempotencyKey = `${idempotencyKey}:session`;
@@ -2727,8 +2749,12 @@ async function createCommand(repoUrl, promptArg, options, command) {
2727
2749
  if (options.model) body.model = options.model;
2728
2750
  if (options.backend) body.agentRuntimeBackend = agentRuntimeBackend;
2729
2751
  if (options.reasoningEffort) body.reasoningEffort = options.reasoningEffort;
2752
+ const promptPreview = buildSessionCreatePromptPreview(prompt);
2753
+ if (promptPreview) body.prompt = promptPreview;
2730
2754
  if (baseBranch) body.baseBranch = baseBranch;
2731
2755
  if (startBranch) body.startBranch = startBranch;
2756
+ if (continuePr) body.continuePrUrl = continuePr;
2757
+ if (continueMode) body.continueMode = continueMode;
2732
2758
  if (options.cold) body.cold = true;
2733
2759
  if (options.onboarding) body.onboarding = true;
2734
2760
  const sessionData = await apiFetch(config, "/api/sessions", {
@@ -2792,6 +2818,8 @@ async function createCommand(repoUrl, promptArg, options, command) {
2792
2818
  if (options.reasoningEffort) output.reasoningEffort = options.reasoningEffort;
2793
2819
  if (baseBranch) output.baseBranch = baseBranch;
2794
2820
  if (startBranch) output.startBranch = startBranch;
2821
+ if (continuePr) output.continuePrUrl = continuePr;
2822
+ if (continueMode) output.continueMode = continueMode;
2795
2823
  if (options.onboarding) output.onboarding = true;
2796
2824
  if (promptId) output.promptId = promptId;
2797
2825
  Object.assign(output, waitResultFields);
@@ -4470,7 +4498,7 @@ function addCreateOptions(cmd) {
4470
4498
  return cmd.argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to send, or '-' to read stdin").option("--model <model>", "Model to use").option("--backend <backend>", "Agent runtime backend: codex (default), claude_code, or opencode").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--base-branch <branch>", "Base branch to create the session against (defaults to the repo default branch)").option(
4471
4499
  "--start-branch <branch>",
4472
4500
  "Resume an existing branch with its history instead of forking a new one off base"
4473
- ).option("--prompt-stdin", "Read prompt from stdin").option(
4501
+ ).option("--continue-pr <url>", "Continue from an existing same-repo pull request").option("--continue-mode <mode>", "Continuation mode: auto (default), update-pr, or new-pr").option("--prompt-stdin", "Read prompt from stdin").option(
4474
4502
  "--uploaded-file <path>",
4475
4503
  "Attach a local text file to the prompt as uploadedFiles; repeat for multiple files",
4476
4504
  collectUploadedFileOption
@@ -4483,13 +4511,14 @@ function addCreateOptions(cmd) {
4483
4511
  `
4484
4512
  Examples:
4485
4513
  arcanist sessions create https://github.com/org/repo "fix bug"
4514
+ arcanist sessions create https://github.com/org/repo "finish this PR" --continue-pr https://github.com/org/repo/pull/123
4486
4515
  arcanist sessions create https://github.com/org/repo "review this trace" --uploaded-file trace.txt
4487
4516
  printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --json
4488
4517
  printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --wait
4489
4518
  arcanist sessions create https://github.com/org/repo - --json | jq -r .sessionId | xargs -I{} arcanist sessions events {} --follow --json
4490
4519
 
4491
4520
  JSON:
4492
- JSON mode returns {sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, onboarding?, promptId?}
4521
+ JSON mode returns {sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, continuePrUrl?, continueMode?, onboarding?, promptId?}
4493
4522
  --wait --json also includes best-effort result fields when available: prUrl?, publishedBranch?, lastBranch?
4494
4523
  Follow async progress with: arcanist sessions events <session-id> --follow --json
4495
4524
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.148",
3
+ "version": "0.1.150",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {