@tryarcanist/cli 0.1.148 → 0.1.149

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 -34
  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) {
@@ -2601,6 +2625,7 @@ function validateRepoUrl(url) {
2601
2625
  }
2602
2626
  var PROMPT_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
2603
2627
  var RESULT_FETCH_ATTEMPTS = 5;
2628
+ var SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS = 4e3;
2604
2629
  var ONBOARDING_PROMPT = "Onboard this repository onto Arcanist.";
2605
2630
  function selectCreatedPrompt(prompts, promptId) {
2606
2631
  if (prompts.length === 0) return null;
@@ -2616,6 +2641,11 @@ function buildPromptFailureError(sessionId, prompt, sessionUrl) {
2616
2641
  hint: `Inspect with: arcanist sessions transcript ${sessionId}`
2617
2642
  });
2618
2643
  }
2644
+ function buildSessionCreatePromptPreview(prompt) {
2645
+ const trimmed = prompt.trim();
2646
+ if (!trimmed) return void 0;
2647
+ return trimmed.length > SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS ? trimmed.slice(0, SESSION_CREATE_PROMPT_PREVIEW_MAX_CHARS).trimEnd() : trimmed;
2648
+ }
2619
2649
  async function fetchCreatedPromptStatus(config, sessionId, promptId) {
2620
2650
  const promptList = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
2621
2651
  return selectCreatedPrompt(promptList.prompts, promptId);
@@ -2719,6 +2749,14 @@ async function createCommand(repoUrl, promptArg, options, command) {
2719
2749
  if (options.startBranch !== void 0 && !startBranch) {
2720
2750
  throw new CliError("user", "--start-branch must not be empty.");
2721
2751
  }
2752
+ const continuePr = options.continuePr?.trim();
2753
+ if (options.continuePr !== void 0 && !continuePr) {
2754
+ throw new CliError("user", "--continue-pr must not be empty.");
2755
+ }
2756
+ const continueMode = options.continueMode?.trim();
2757
+ if (options.continueMode !== void 0 && !continueMode) {
2758
+ throw new CliError("user", "--continue-mode must not be empty.");
2759
+ }
2722
2760
  const uploadedFiles = await resolveUploadedFileOptions(options.uploadedFile);
2723
2761
  const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
2724
2762
  const sessionIdempotencyKey = `${idempotencyKey}:session`;
@@ -2727,8 +2765,12 @@ async function createCommand(repoUrl, promptArg, options, command) {
2727
2765
  if (options.model) body.model = options.model;
2728
2766
  if (options.backend) body.agentRuntimeBackend = agentRuntimeBackend;
2729
2767
  if (options.reasoningEffort) body.reasoningEffort = options.reasoningEffort;
2768
+ const promptPreview = buildSessionCreatePromptPreview(prompt);
2769
+ if (promptPreview) body.prompt = promptPreview;
2730
2770
  if (baseBranch) body.baseBranch = baseBranch;
2731
2771
  if (startBranch) body.startBranch = startBranch;
2772
+ if (continuePr) body.continuePrUrl = continuePr;
2773
+ if (continueMode) body.continueMode = continueMode;
2732
2774
  if (options.cold) body.cold = true;
2733
2775
  if (options.onboarding) body.onboarding = true;
2734
2776
  const sessionData = await apiFetch(config, "/api/sessions", {
@@ -2792,6 +2834,8 @@ async function createCommand(repoUrl, promptArg, options, command) {
2792
2834
  if (options.reasoningEffort) output.reasoningEffort = options.reasoningEffort;
2793
2835
  if (baseBranch) output.baseBranch = baseBranch;
2794
2836
  if (startBranch) output.startBranch = startBranch;
2837
+ if (continuePr) output.continuePrUrl = continuePr;
2838
+ if (continueMode) output.continueMode = continueMode;
2795
2839
  if (options.onboarding) output.onboarding = true;
2796
2840
  if (promptId) output.promptId = promptId;
2797
2841
  Object.assign(output, waitResultFields);
@@ -4470,7 +4514,7 @@ function addCreateOptions(cmd) {
4470
4514
  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
4515
  "--start-branch <branch>",
4472
4516
  "Resume an existing branch with its history instead of forking a new one off base"
4473
- ).option("--prompt-stdin", "Read prompt from stdin").option(
4517
+ ).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
4518
  "--uploaded-file <path>",
4475
4519
  "Attach a local text file to the prompt as uploadedFiles; repeat for multiple files",
4476
4520
  collectUploadedFileOption
@@ -4483,13 +4527,14 @@ function addCreateOptions(cmd) {
4483
4527
  `
4484
4528
  Examples:
4485
4529
  arcanist sessions create https://github.com/org/repo "fix bug"
4530
+ arcanist sessions create https://github.com/org/repo "finish this PR" --continue-pr https://github.com/org/repo/pull/123
4486
4531
  arcanist sessions create https://github.com/org/repo "review this trace" --uploaded-file trace.txt
4487
4532
  printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --json
4488
4533
  printf "fix bug" | arcanist sessions create https://github.com/org/repo --prompt-stdin --wait
4489
4534
  arcanist sessions create https://github.com/org/repo - --json | jq -r .sessionId | xargs -I{} arcanist sessions events {} --follow --json
4490
4535
 
4491
4536
  JSON:
4492
- JSON mode returns {sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, onboarding?, promptId?}
4537
+ JSON mode returns {sessionId, sessionUrl?, repoUrl, model?, agentRuntimeBackend?, reasoningEffort?, baseBranch?, startBranch?, continuePrUrl?, continueMode?, onboarding?, promptId?}
4493
4538
  --wait --json also includes best-effort result fields when available: prUrl?, publishedBranch?, lastBranch?
4494
4539
  Follow async progress with: arcanist sessions events <session-id> --follow --json
4495
4540
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.148",
3
+ "version": "0.1.149",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {