@tryarcanist/cli 0.1.88 → 0.1.90

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 +273 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -383,6 +383,255 @@ async function whoamiCommand(options, command) {
383
383
  if (payload.tokenScope) console.log(`Token scope: ${String(payload.tokenScope)}`);
384
384
  }
385
385
 
386
+ // ../../shared/agent/agent-runtime-backend.ts
387
+ var CODEX_AGENT_RUNTIME_BACKEND = "codex";
388
+ var CLAUDE_CODE_AGENT_RUNTIME_BACKEND = "claude_code";
389
+ var AGENT_RUNTIME_BACKENDS = [
390
+ CODEX_AGENT_RUNTIME_BACKEND,
391
+ CLAUDE_CODE_AGENT_RUNTIME_BACKEND
392
+ ];
393
+ function isAgentRuntimeBackend(value) {
394
+ return AGENT_RUNTIME_BACKENDS.includes(value);
395
+ }
396
+
397
+ // ../../shared/utils/type-guards.ts
398
+ function isRecord(value) {
399
+ return !!value && typeof value === "object" && !Array.isArray(value);
400
+ }
401
+ function asNonEmptyString(value) {
402
+ if (typeof value !== "string") return void 0;
403
+ const trimmed = value.trim();
404
+ return trimmed.length > 0 ? trimmed : void 0;
405
+ }
406
+
407
+ // ../../shared/constants/models.ts
408
+ var OpenAIModel = {
409
+ GPT55: "gpt-5.5",
410
+ GPT54: "gpt-5.4",
411
+ GPT54Pro: "gpt-5.4-pro",
412
+ GPT54Mini: "gpt-5.4-mini",
413
+ GPT54Nano: "gpt-5.4-nano",
414
+ GPT53Codex: "gpt-5.3-codex",
415
+ GPT52: "gpt-5.2",
416
+ GPT52ChatLatest: "gpt-5.2-chat-latest",
417
+ GPT52Codex: "gpt-5.2-codex"
418
+ };
419
+ var AnthropicModel = {
420
+ Opus48: "claude-opus-4-8",
421
+ Sonnet46: "claude-sonnet-4-6"
422
+ };
423
+ var MODEL_PROVIDERS_SET = /* @__PURE__ */ new Set(["openai", "anthropic"]);
424
+ var SESSION_START_MODEL_IDS_BY_BACKEND = {
425
+ [CODEX_AGENT_RUNTIME_BACKEND]: [OpenAIModel.GPT55, OpenAIModel.GPT54],
426
+ [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: [AnthropicModel.Opus48, AnthropicModel.Sonnet46]
427
+ };
428
+ var DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND = {
429
+ [CODEX_AGENT_RUNTIME_BACKEND]: OpenAIModel.GPT55,
430
+ [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: AnthropicModel.Opus48
431
+ };
432
+ var SESSION_START_MODEL_IDS = SESSION_START_MODEL_IDS_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
433
+ var DEFAULT_SESSION_START_MODEL_ID = DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
434
+ var MODEL_REGISTRY = [
435
+ {
436
+ id: OpenAIModel.GPT55,
437
+ name: "GPT-5.5",
438
+ provider: "openai",
439
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
440
+ contextWindow: 1e6,
441
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: "medium" }
442
+ },
443
+ {
444
+ id: OpenAIModel.GPT54,
445
+ name: "GPT-5.4",
446
+ provider: "openai",
447
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
448
+ contextWindow: 1e6,
449
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 }
450
+ },
451
+ {
452
+ id: OpenAIModel.GPT54Mini,
453
+ name: "GPT-5.4 Mini",
454
+ provider: "openai",
455
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
456
+ contextWindow: 4e5,
457
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 }
458
+ },
459
+ {
460
+ id: OpenAIModel.GPT54Nano,
461
+ name: "GPT-5.4 Nano",
462
+ provider: "openai",
463
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
464
+ contextWindow: 4e5,
465
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 }
466
+ },
467
+ {
468
+ id: OpenAIModel.GPT53Codex,
469
+ name: "GPT-5.3 Codex",
470
+ provider: "openai",
471
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
472
+ contextWindow: 4e5,
473
+ reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" }
474
+ },
475
+ {
476
+ id: OpenAIModel.GPT52,
477
+ name: "GPT-5.2",
478
+ provider: "openai",
479
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
480
+ contextWindow: 4e5,
481
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 }
482
+ },
483
+ {
484
+ id: OpenAIModel.GPT52ChatLatest,
485
+ name: "GPT-5.2 Chat",
486
+ provider: "openai",
487
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
488
+ contextWindow: 128e3,
489
+ reasoning: { efforts: ["none", "low", "medium", "high", "xhigh"], default: void 0 }
490
+ },
491
+ {
492
+ id: OpenAIModel.GPT52Codex,
493
+ name: "GPT-5.2 Codex",
494
+ provider: "openai",
495
+ backends: [CODEX_AGENT_RUNTIME_BACKEND],
496
+ contextWindow: 4e5,
497
+ reasoning: { efforts: ["low", "medium", "high", "xhigh"], default: "high" }
498
+ },
499
+ {
500
+ id: AnthropicModel.Opus48,
501
+ name: "Claude Opus 4.8",
502
+ provider: "anthropic",
503
+ backends: [CLAUDE_CODE_AGENT_RUNTIME_BACKEND],
504
+ contextWindow: 1e6
505
+ // Claude Code drives thinking depth itself; the bridge does not pass a
506
+ // reasoning-effort flag to `claude -p`, so no reasoning config is exposed.
507
+ },
508
+ {
509
+ id: AnthropicModel.Sonnet46,
510
+ name: "Claude Sonnet 4.6",
511
+ provider: "anthropic",
512
+ backends: [CLAUDE_CODE_AGENT_RUNTIME_BACKEND],
513
+ contextWindow: 1e6
514
+ }
515
+ ];
516
+ var MODEL_PROVIDER_NAMES = {
517
+ openai: "OpenAI",
518
+ anthropic: "Anthropic"
519
+ };
520
+ var VALID_MODEL_IDS = new Set(MODEL_REGISTRY.map((model) => model.id));
521
+ var VALID_SESSION_START_MODEL_IDS_BY_BACKEND = {
522
+ [CODEX_AGENT_RUNTIME_BACKEND]: new Set(SESSION_START_MODEL_IDS_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND]),
523
+ [CLAUDE_CODE_AGENT_RUNTIME_BACKEND]: new Set(
524
+ SESSION_START_MODEL_IDS_BY_BACKEND[CLAUDE_CODE_AGENT_RUNTIME_BACKEND]
525
+ )
526
+ };
527
+ var VALID_SESSION_START_MODEL_IDS = VALID_SESSION_START_MODEL_IDS_BY_BACKEND[CODEX_AGENT_RUNTIME_BACKEND];
528
+ var MODEL_CONTEXT_WINDOWS = {
529
+ ...Object.fromEntries(
530
+ MODEL_REGISTRY.flatMap((model) => model.contextWindow === void 0 ? [] : [[model.id, model.contextWindow]])
531
+ )
532
+ };
533
+ var MODEL_PROVIDERS = {
534
+ ...Object.fromEntries(MODEL_REGISTRY.map((model) => [model.id, model.provider]))
535
+ };
536
+ var MODEL_REASONING_CONFIG = Object.fromEntries(
537
+ MODEL_REGISTRY.flatMap((model) => model.reasoning ? [[model.id, model.reasoning]] : [])
538
+ );
539
+ var MODELS_WITHOUT_REASONING = /* @__PURE__ */ new Set([
540
+ ...MODEL_REGISTRY.filter((model) => !model.reasoning).map((model) => model.id)
541
+ ]);
542
+ var MODEL_DEFINITIONS_BY_ID = Object.fromEntries(MODEL_REGISTRY.map((model) => [model.id, model]));
543
+ function splitModelIdentifier(value) {
544
+ let hasSeparator = false;
545
+ for (const separator of [":", "/"]) {
546
+ const index = value.indexOf(separator);
547
+ if (index <= 0 || index >= value.length - 1) continue;
548
+ hasSeparator = true;
549
+ const providerID = value.slice(0, index);
550
+ const modelID = value.slice(index + 1);
551
+ if (MODEL_PROVIDERS_SET.has(providerID) && modelID.length > 0) {
552
+ return { providerID, modelID };
553
+ }
554
+ }
555
+ if (hasSeparator) return void 0;
556
+ return value.length > 0 ? { modelID: value } : void 0;
557
+ }
558
+ function getRawModelValue(raw) {
559
+ if (typeof raw === "string") return asNonEmptyString(raw);
560
+ if (!raw || typeof raw !== "object") return void 0;
561
+ const value = raw;
562
+ return asNonEmptyString(value.modelID) ?? asNonEmptyString(value.modelId) ?? asNonEmptyString(value.id);
563
+ }
564
+ function getExplicitProvider(raw) {
565
+ if (raw && typeof raw === "object") {
566
+ const value = raw;
567
+ const providerID = asNonEmptyString(value.providerID) ?? asNonEmptyString(value.providerId);
568
+ if (providerID && MODEL_PROVIDERS_SET.has(providerID)) {
569
+ return providerID;
570
+ }
571
+ }
572
+ const modelValue = getRawModelValue(raw);
573
+ if (!modelValue) return void 0;
574
+ return splitModelIdentifier(modelValue)?.providerID;
575
+ }
576
+ function extractModelId(raw) {
577
+ const value = getRawModelValue(raw);
578
+ if (!value) return void 0;
579
+ return splitModelIdentifier(value)?.modelID;
580
+ }
581
+ function getSessionStartModelIdsForBackend(backend) {
582
+ return SESSION_START_MODEL_IDS_BY_BACKEND[backend];
583
+ }
584
+ function isSessionStartModelAllowedForBackend(modelId, backend) {
585
+ return VALID_SESSION_START_MODEL_IDS_BY_BACKEND[backend].has(modelId);
586
+ }
587
+ function getProviderForModel(modelId) {
588
+ return MODEL_PROVIDERS[modelId] ?? "openai";
589
+ }
590
+ function toModelSelection(raw) {
591
+ const modelID = extractModelId(raw);
592
+ if (!modelID) return void 0;
593
+ return {
594
+ providerID: getExplicitProvider(raw) ?? getProviderForModel(modelID),
595
+ modelID
596
+ };
597
+ }
598
+ function getModelDefinition(raw) {
599
+ const modelID = extractModelId(raw);
600
+ if (!modelID) return void 0;
601
+ return MODEL_DEFINITIONS_BY_ID[modelID];
602
+ }
603
+ function formatModelLabel(raw, options = {}) {
604
+ const includeProvider = options.includeProvider ?? false;
605
+ const definition = getModelDefinition(raw);
606
+ if (definition) {
607
+ return includeProvider ? `${MODEL_PROVIDER_NAMES[definition.provider]} / ${definition.name}` : definition.name;
608
+ }
609
+ const selection = toModelSelection(raw);
610
+ if (!selection) return void 0;
611
+ const providerName = MODEL_PROVIDER_NAMES[selection.providerID];
612
+ return includeProvider ? `${providerName} / ${selection.modelID}` : selection.modelID;
613
+ }
614
+ function buildModelProviderGroups(models) {
615
+ const groups = /* @__PURE__ */ new Map();
616
+ for (const model of models) {
617
+ let group = groups.get(model.provider);
618
+ if (!group) {
619
+ group = { id: model.provider, name: MODEL_PROVIDER_NAMES[model.provider], models: [] };
620
+ groups.set(model.provider, group);
621
+ }
622
+ group.models.push({
623
+ id: model.id,
624
+ name: model.name,
625
+ label: formatModelLabel(model, { includeProvider: true }) ?? model.name,
626
+ reasoning: model.reasoning
627
+ });
628
+ }
629
+ return [...groups.values()];
630
+ }
631
+ var SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
632
+ MODEL_REGISTRY.filter((model) => VALID_SESSION_START_MODEL_IDS.has(model.id))
633
+ );
634
+
386
635
  // ../../shared/utils/timing.ts
387
636
  function sleep(ms) {
388
637
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -606,11 +855,6 @@ function noChangeOutcomeCopy(reason) {
606
855
  }
607
856
  }
608
857
 
609
- // ../../shared/utils/type-guards.ts
610
- function isRecord(value) {
611
- return !!value && typeof value === "object" && !Array.isArray(value);
612
- }
613
-
614
858
  // ../../shared/transcript/malformed-search.ts
615
859
  var MALFORMED_SEARCH_BLOCKED_TRANSCRIPT_PREFIX = "Arcanist blocked this malformed search command before execution.";
616
860
  var BASH_UNMATCHED_QUOTE_EOF_RE = /\/bin\/bash:\s+-c:\s+line\s+\d+:\s+unexpected EOF while looking for matching [`'"][`'"]?/i;
@@ -711,7 +955,7 @@ function getRawSessionEventKind(event) {
711
955
  return bridgeEventType ?? "agent_timeline";
712
956
  case "idle":
713
957
  return bridgeEventType ?? "session_idle";
714
- case "codex.session.create":
958
+ case "agent.session.create":
715
959
  case "git.push":
716
960
  case "pr.open":
717
961
  case "session.create":
@@ -1121,7 +1365,7 @@ function projectRawCodex(data, index) {
1121
1365
  if (partType === "text") return null;
1122
1366
  if (eventType && RAW_CODEX_NOISE.has(eventType)) return null;
1123
1367
  return {
1124
- type: "raw_codex",
1368
+ type: "raw_agent_runtime",
1125
1369
  id: resolveEventId(data, "raw", index),
1126
1370
  ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
1127
1371
  ...partType ? { partType } : {},
@@ -1290,7 +1534,7 @@ function flattenSessionEvents(raw) {
1290
1534
  case "session_resumed_cold":
1291
1535
  pushEvent(state, projectSessionResumedCold(data, state.merged.length));
1292
1536
  break;
1293
- case "raw_codex":
1537
+ case "raw_agent_runtime":
1294
1538
  pushEvent(state, projectRawCodex(data, state.merged.length));
1295
1539
  break;
1296
1540
  case "reasoning":
@@ -1597,7 +1841,7 @@ ${event.answer ? `**Answer:** ${event.answer}
1597
1841
  case "agent_progress":
1598
1842
  return `*[${event.label}]*
1599
1843
  `;
1600
- case "raw_codex":
1844
+ case "raw_agent_runtime":
1601
1845
  return "";
1602
1846
  }
1603
1847
  }
@@ -2084,6 +2328,23 @@ async function waitForCreatedPrompt(sessionId, promptId, sessionUrl, pollInterva
2084
2328
  async function createCommand(repoUrl, promptArg, options, command) {
2085
2329
  const runtime = getRuntimeOptions(command, options);
2086
2330
  const config = requireConfig(runtime);
2331
+ let agentRuntimeBackend = CODEX_AGENT_RUNTIME_BACKEND;
2332
+ if (options.backend !== void 0) {
2333
+ if (!isAgentRuntimeBackend(options.backend)) {
2334
+ throw new CliError("user", `Invalid --backend '${options.backend}'. Expected codex or claude_code.`);
2335
+ }
2336
+ agentRuntimeBackend = options.backend;
2337
+ }
2338
+ if (options.model !== void 0) {
2339
+ const normalizedModel = extractModelId(options.model);
2340
+ if (normalizedModel === void 0 || !isSessionStartModelAllowedForBackend(normalizedModel, agentRuntimeBackend)) {
2341
+ const allowed = getSessionStartModelIdsForBackend(agentRuntimeBackend).join(", ");
2342
+ throw new CliError(
2343
+ "user",
2344
+ `Model '${options.model}' is not selectable for backend '${agentRuntimeBackend}'. Allowed: ${allowed}.`
2345
+ );
2346
+ }
2347
+ }
2087
2348
  const prompt = await resolvePromptInput(promptArg, options);
2088
2349
  const waitPollIntervalMs = options.wait ? parsePollInterval(options.pollInterval) : null;
2089
2350
  const repoError = validateRepoUrl(repoUrl);
@@ -2100,6 +2361,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
2100
2361
  body: JSON.stringify({
2101
2362
  context: { repoUrl },
2102
2363
  ...options.model ? { model: options.model } : {},
2364
+ ...options.backend ? { agentRuntimeBackend } : {},
2103
2365
  ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {},
2104
2366
  ...options.cold ? { cold: true } : {}
2105
2367
  })
@@ -2141,6 +2403,7 @@ async function createCommand(repoUrl, promptArg, options, command) {
2141
2403
  ...sessionData.sessionUrl ? { sessionUrl: sessionData.sessionUrl } : {},
2142
2404
  repoUrl,
2143
2405
  ...options.model ? { model: options.model } : {},
2406
+ ...options.backend ? { agentRuntimeBackend } : {},
2144
2407
  ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {},
2145
2408
  ...promptId ? { promptId } : {}
2146
2409
  });
@@ -2566,7 +2829,7 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
2566
2829
  applyColorEnvironment(getRuntimeOptions(actionCommand));
2567
2830
  });
2568
2831
  function addCreateOptions(cmd) {
2569
- return cmd.argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to send, or '-' to read stdin").option("--model <model>", "Model to use").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--prompt-stdin", "Read prompt from stdin").option(
2832
+ 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) or claude_code").option("--reasoning-effort <effort>", "Reasoning effort to use for models that support it").option("--prompt-stdin", "Read prompt from stdin").option(
2570
2833
  "--uploaded-file <path>",
2571
2834
  "Attach a local text file to the prompt as uploadedFiles; repeat for multiple files",
2572
2835
  collectUploadedFileOption
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.88",
3
+ "version": "0.1.90",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {