perchai-cli 2.4.46 → 2.4.47

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/perch.mjs +639 -65
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -75566,6 +75566,7 @@ var init_payroll = __esm({
75566
75566
  // lib/perchBusinessTools/index.ts
75567
75567
  var init_perchBusinessTools = __esm({
75568
75568
  "lib/perchBusinessTools/index.ts"() {
75569
+ "use strict";
75569
75570
  init_generateAPAuditPacket();
75570
75571
  init_inventoryFolder();
75571
75572
  init_loadBusinessTables();
@@ -84008,7 +84009,7 @@ function sanitizeModelBackendPreset(value) {
84008
84009
  if (value === "wandb_gemma") return "wandb_qwen";
84009
84010
  if (value === "wandb_glm") return "wandb_glm51";
84010
84011
  if (value === "bedrock_mistral_large_3") return "bedrock_qwen3_coder";
84011
- if (value === "router" || value === "gcp" || value === "modal" || value === "huggingface" || value === "fireworks_deepseek" || value === "wandb_qwen" || value === "wandb_minimax" || value === "wandb_kimi" || value === "wandb_glm51" || value === "wandb_nemotron_ultra" || value === "bedrock_glm5" || value === "bedrock_qwen3_coder" || value === "bedrock_gpt_oss_120b" || value === "bedrock_nemotron_super" || value === "bedrock_minimax_m2" || value === "bedrock_deepseek_v32" || value === "bedrock_qwen3_vl" || value === "codex_gpt55" || value === "codex_gpt54" || value === "codex_gpt54_mini" || value === "claude_haiku45" || value === "claude_sonnet46" || value === "claude_opus48") {
84012
+ if (value === "router" || value === "gcp" || value === "modal" || value === "huggingface" || value === "fireworks_deepseek" || value === "wandb_qwen" || value === "wandb_minimax" || value === "wandb_kimi" || value === "wandb_glm51" || value === "wandb_nemotron_ultra" || value === "wandb_deepseek_flash" || value === "wandb_deepseek_pro" || value === "bedrock_kimi_k25" || value === "bedrock_glm5" || value === "bedrock_qwen3_coder" || value === "bedrock_gpt_oss_120b" || value === "bedrock_nemotron_super" || value === "bedrock_minimax_m2" || value === "bedrock_deepseek_v32" || value === "bedrock_qwen3_vl" || value === "codex_gpt55" || value === "codex_gpt54" || value === "codex_gpt54_mini" || value === "claude_haiku45" || value === "claude_sonnet46" || value === "claude_opus48") {
84012
84013
  return value;
84013
84014
  }
84014
84015
  return null;
@@ -84120,7 +84121,7 @@ function resolveApiModelId(option, env4 = runtimeEnv()) {
84120
84121
  }
84121
84122
  return option.modelId;
84122
84123
  }
84123
- var DEFAULT_NIM_MODEL_ID, DEFAULT_FOUNDER_MODEL_OPTION_ID, ALL_MODEL_LANES, WORKER_LANES, REASONING_LANES, DEFAULT_GITHUB_GPT_41_ID, DEFAULT_GITHUB_GPT_41_MINI_ID, DEFAULT_GITHUB_GPT_4O_ID, DEFAULT_CODE_WORKER_MODEL_ID, GCP_SELF_HOSTED_OPTION_ID, DEFAULT_GCP_INFERENCE_MODEL, MODAL_SELF_HOSTED_OPTION_ID, MODAL_MIMO_OPTION_ID, DEFAULT_MODAL_INFERENCE_MODEL, HUGGINGFACE_KIMI_K25_OPTION_ID, WANDB_QWEN_36_35B_A3B_OPTION_ID, WANDB_MINIMAX_M25_OPTION_ID, WANDB_KIMI_K26_OPTION_ID, WANDB_GLM_51_OPTION_ID, WANDB_NEMOTRON_ULTRA_OPTION_ID, BEDROCK_GLM5_OPTION_ID, BEDROCK_QWEN3_CODER_480B_OPTION_ID, BEDROCK_GPT_OSS_120B_OPTION_ID, BEDROCK_NEMOTRON_SUPER_120B_OPTION_ID, BEDROCK_MINIMAX_M2_OPTION_ID, BEDROCK_DEEPSEEK_V32_OPTION_ID, BEDROCK_QWEN3_VL_235B_OPTION_ID, CODEX_GPT_55_OPTION_ID, CODEX_GPT_54_OPTION_ID, CODEX_GPT_54_MINI_OPTION_ID, CLAUDE_CODE_HAIKU_45_OPTION_ID, CLAUDE_CODE_SONNET_46_OPTION_ID, CLAUDE_CODE_OPUS_48_OPTION_ID, FIREWORKS_DEEPSEEK_V4_FLASH_OPTION_ID, FIREWORKS_DEEPSEEK_VISION_PRIMARY_OPTION_ID, FIREWORKS_DEEPSEEK_VISION_FALLBACK_OPTION_IDS, AUTO_ROUTER_EXCLUDED_BACKEND_OPTION_IDS, AI_GATEWAY_AUTO_ROUTER_OPTION_IDS, MODAL_MODELS, GCP_MODELS, HUGGINGFACE_MODELS, WANDB_MODELS, BEDROCK_MANTLE_MODELS, CODEX_OAUTH_MODELS, CLAUDE_CODE_OAUTH_MODELS, AI_GATEWAY_MODELS, OPENROUTER_MODELS, DEFAULT_OPERATOR_VISION_MODEL_ID, GITHUB_MODELS_VALIDATED_WORKERS, OPENAI_VALIDATED_WORKERS, RAW_FOUNDER_MODEL_OPTIONS, FOUNDER_MODEL_OPTIONS, DEFAULT_FOUNDER_MODEL_SELECTION;
84124
+ var DEFAULT_NIM_MODEL_ID, DEFAULT_FOUNDER_MODEL_OPTION_ID, ALL_MODEL_LANES, WORKER_LANES, REASONING_LANES, DEFAULT_GITHUB_GPT_41_ID, DEFAULT_GITHUB_GPT_41_MINI_ID, DEFAULT_GITHUB_GPT_4O_ID, DEFAULT_CODE_WORKER_MODEL_ID, GCP_SELF_HOSTED_OPTION_ID, DEFAULT_GCP_INFERENCE_MODEL, MODAL_SELF_HOSTED_OPTION_ID, MODAL_MIMO_OPTION_ID, DEFAULT_MODAL_INFERENCE_MODEL, HUGGINGFACE_KIMI_K25_OPTION_ID, WANDB_QWEN_36_35B_A3B_OPTION_ID, WANDB_MINIMAX_M25_OPTION_ID, WANDB_KIMI_K26_OPTION_ID, WANDB_GLM_51_OPTION_ID, WANDB_NEMOTRON_ULTRA_OPTION_ID, WANDB_DEEPSEEK_V4_FLASH_OPTION_ID, WANDB_DEEPSEEK_V4_PRO_OPTION_ID, BEDROCK_KIMI_K25_OPTION_ID, BEDROCK_GLM5_OPTION_ID, BEDROCK_QWEN3_CODER_480B_OPTION_ID, BEDROCK_GPT_OSS_120B_OPTION_ID, BEDROCK_NEMOTRON_SUPER_120B_OPTION_ID, BEDROCK_MINIMAX_M2_OPTION_ID, BEDROCK_DEEPSEEK_V32_OPTION_ID, BEDROCK_QWEN3_VL_235B_OPTION_ID, CODEX_GPT_55_OPTION_ID, CODEX_GPT_54_OPTION_ID, CODEX_GPT_54_MINI_OPTION_ID, CLAUDE_CODE_HAIKU_45_OPTION_ID, CLAUDE_CODE_SONNET_46_OPTION_ID, CLAUDE_CODE_OPUS_48_OPTION_ID, FIREWORKS_DEEPSEEK_V4_FLASH_OPTION_ID, FIREWORKS_DEEPSEEK_VISION_PRIMARY_OPTION_ID, FIREWORKS_DEEPSEEK_VISION_FALLBACK_OPTION_IDS, AUTO_ROUTER_EXCLUDED_BACKEND_OPTION_IDS, AI_GATEWAY_AUTO_ROUTER_OPTION_IDS, MODAL_MODELS, GCP_MODELS, HUGGINGFACE_MODELS, WANDB_MODELS, BEDROCK_MANTLE_MODELS, CODEX_OAUTH_MODELS, CLAUDE_CODE_OAUTH_MODELS, AI_GATEWAY_MODELS, OPENROUTER_MODELS, DEFAULT_OPERATOR_VISION_MODEL_ID, GITHUB_MODELS_VALIDATED_WORKERS, OPENAI_VALIDATED_WORKERS, RAW_FOUNDER_MODEL_OPTIONS, FOUNDER_MODEL_OPTIONS, DEFAULT_FOUNDER_MODEL_SELECTION;
84124
84125
  var init_modelRegistry = __esm({
84125
84126
  "features/perchTerminal/runtime/modelRegistry.ts"() {
84126
84127
  "use strict";
@@ -84166,6 +84167,18 @@ var init_modelRegistry = __esm({
84166
84167
  WANDB_KIMI_K26_OPTION_ID = "wandb-kimi-k2-6";
84167
84168
  WANDB_GLM_51_OPTION_ID = "wandb-zai-org-glm-5-1";
84168
84169
  WANDB_NEMOTRON_ULTRA_OPTION_ID = "wandb-nvidia-nvidia-nemotron-3-ultra-550b-a55b";
84170
+ WANDB_DEEPSEEK_V4_FLASH_OPTION_ID = makeModelOptionId(
84171
+ "wandb",
84172
+ "deepseek-ai/DeepSeek-V4-Flash"
84173
+ );
84174
+ WANDB_DEEPSEEK_V4_PRO_OPTION_ID = makeModelOptionId(
84175
+ "wandb",
84176
+ "deepseek-ai/DeepSeek-V4-Pro"
84177
+ );
84178
+ BEDROCK_KIMI_K25_OPTION_ID = makeModelOptionId(
84179
+ "bedrock_mantle",
84180
+ "moonshotai.kimi-k2.5"
84181
+ );
84169
84182
  BEDROCK_GLM5_OPTION_ID = makeModelOptionId("bedrock_mantle", "zai.glm-5");
84170
84183
  BEDROCK_QWEN3_CODER_480B_OPTION_ID = makeModelOptionId(
84171
84184
  "bedrock_mantle",
@@ -84230,6 +84243,9 @@ var init_modelRegistry = __esm({
84230
84243
  WANDB_KIMI_K26_OPTION_ID,
84231
84244
  WANDB_GLM_51_OPTION_ID,
84232
84245
  WANDB_NEMOTRON_ULTRA_OPTION_ID,
84246
+ WANDB_DEEPSEEK_V4_FLASH_OPTION_ID,
84247
+ WANDB_DEEPSEEK_V4_PRO_OPTION_ID,
84248
+ BEDROCK_KIMI_K25_OPTION_ID,
84233
84249
  BEDROCK_GLM5_OPTION_ID,
84234
84250
  BEDROCK_QWEN3_CODER_480B_OPTION_ID,
84235
84251
  BEDROCK_GPT_OSS_120B_OPTION_ID,
@@ -84456,9 +84472,79 @@ var init_modelRegistry = __esm({
84456
84472
  availabilityNote: "W&B Inference hosted Nemotron 3 Ultra. Text/tools use W&B; browser/image tasks use the NIM vision observer chain when selected."
84457
84473
  }),
84458
84474
  id: WANDB_NEMOTRON_ULTRA_OPTION_ID
84475
+ },
84476
+ {
84477
+ ...makeManualModelOption({
84478
+ providerId: "wandb",
84479
+ modelId: "deepseek-ai/DeepSeek-V4-Flash",
84480
+ label: "W&B \xB7 DeepSeek V4 Flash",
84481
+ providerLabel: "Weights & Biases",
84482
+ vendor: "DeepSeek",
84483
+ family: "V4 Flash",
84484
+ groupLabel: "Hosted / W&B Inference",
84485
+ contextWindow: 104e4,
84486
+ lanes: ALL_MODEL_LANES,
84487
+ reasoningSupport: "false",
84488
+ envKey: "WANDB_API_KEY",
84489
+ adapterStatus: "wired",
84490
+ modality: "chat",
84491
+ capabilities: ["code", "agentic_coding"],
84492
+ costTier: "standard",
84493
+ userFacing: true,
84494
+ manualOnly: true,
84495
+ availabilityNote: "W&B Inference hosted DeepSeek V4 Flash. Text/tools use WANDB_API_KEY; browser/image tasks use the NIM vision observer chain when the W&B DeepSeek Flash backend card is selected."
84496
+ }),
84497
+ id: WANDB_DEEPSEEK_V4_FLASH_OPTION_ID
84498
+ },
84499
+ {
84500
+ ...makeManualModelOption({
84501
+ providerId: "wandb",
84502
+ modelId: "deepseek-ai/DeepSeek-V4-Pro",
84503
+ label: "W&B \xB7 DeepSeek V4 Pro",
84504
+ providerLabel: "Weights & Biases",
84505
+ vendor: "DeepSeek",
84506
+ family: "V4 Pro",
84507
+ groupLabel: "Hosted / W&B Inference",
84508
+ contextWindow: 104e4,
84509
+ lanes: ALL_MODEL_LANES,
84510
+ reasoningSupport: "false",
84511
+ envKey: "WANDB_API_KEY",
84512
+ adapterStatus: "wired",
84513
+ modality: "chat",
84514
+ capabilities: ["code", "agentic_coding"],
84515
+ costTier: "premium",
84516
+ userFacing: true,
84517
+ manualOnly: true,
84518
+ availabilityNote: "W&B Inference hosted DeepSeek V4 Pro. Text/tools use WANDB_API_KEY; browser/image tasks use the NIM vision observer chain when the W&B DeepSeek Pro backend card is selected."
84519
+ }),
84520
+ id: WANDB_DEEPSEEK_V4_PRO_OPTION_ID
84459
84521
  }
84460
84522
  ];
84461
84523
  BEDROCK_MANTLE_MODELS = [
84524
+ {
84525
+ ...makeManualModelOption({
84526
+ providerId: "bedrock_mantle",
84527
+ modelId: "moonshotai.kimi-k2.5",
84528
+ label: "Bedrock Mantle \xB7 Kimi K2.5 Vision",
84529
+ providerLabel: "Bedrock Mantle",
84530
+ vendor: "Moonshot AI",
84531
+ family: "Kimi K2.5",
84532
+ groupLabel: "Hosted / Bedrock Mantle",
84533
+ contextWindow: 256e3,
84534
+ maxOutputTokens: 16e3,
84535
+ lanes: [...ALL_MODEL_LANES, "vision"],
84536
+ reasoningSupport: "false",
84537
+ envKey: "BEDROCK_MANTLE_API_KEY",
84538
+ adapterStatus: "wired",
84539
+ modality: "vision",
84540
+ capabilities: ["vision", "multimodal", "code", "agentic_coding"],
84541
+ costTier: "premium",
84542
+ userFacing: true,
84543
+ manualOnly: true,
84544
+ availabilityNote: "Bedrock Mantle Kimi K2.5 multimodal. Native vision on moonshotai.kimi-k2.5; uses BEDROCK_MANTLE_API_KEY and BEDROCK_MANTLE_BASE_URL if set."
84545
+ }),
84546
+ id: BEDROCK_KIMI_K25_OPTION_ID
84547
+ },
84462
84548
  {
84463
84549
  ...makeManualModelOption({
84464
84550
  providerId: "bedrock_mantle",
@@ -86736,7 +86822,6 @@ function truncateHistoryLine(value, max2) {
86736
86822
  }
86737
86823
  var init_operatorTruth = __esm({
86738
86824
  "features/perchTerminal/runtime/operatorTruth.ts"() {
86739
- "use strict";
86740
86825
  }
86741
86826
  });
86742
86827
 
@@ -91023,6 +91108,7 @@ Final answers lead with findings, name artifacts or delivery status, and give on
91023
91108
  var MARKET_DESK_TOOL_NAMES;
91024
91109
  var init_marketDeskAccess = __esm({
91025
91110
  "features/perchTerminal/runtime/marketDesk/marketDeskAccess.ts"() {
91111
+ "use strict";
91026
91112
  init_toolNames();
91027
91113
  MARKET_DESK_TOOL_NAMES = /* @__PURE__ */ new Set([
91028
91114
  TOOL_NAMES.getMarketSignal,
@@ -91702,6 +91788,7 @@ function listFinancialPlaybooks() {
91702
91788
  var AP_AUDIT_PACKET_DEF, PLAYBOOKS;
91703
91789
  var init_registry2 = __esm({
91704
91790
  "features/perchTerminal/runtime/financialPlaybooks/registry.ts"() {
91791
+ "use strict";
91705
91792
  init_managedWorkflowRegistry2();
91706
91793
  init_toolNames();
91707
91794
  AP_AUDIT_PACKET_DEF = {
@@ -115525,10 +115612,10 @@ function matchStringTarget(hostname, target) {
115525
115612
  }
115526
115613
  return false;
115527
115614
  }
115528
- function getDefaultPropagationTargets(supabaseUrl) {
115615
+ function getDefaultPropagationTargets(supabaseUrl2) {
115529
115616
  const targets = [];
115530
115617
  try {
115531
- const url = new URL(supabaseUrl);
115618
+ const url = new URL(supabaseUrl2);
115532
115619
  targets.push(url.hostname);
115533
115620
  } catch (error) {
115534
115621
  }
@@ -115625,8 +115712,8 @@ function applySettingDefaults(options, defaults) {
115625
115712
  else delete result2.accessToken;
115626
115713
  return result2;
115627
115714
  }
115628
- function validateSupabaseUrl(supabaseUrl) {
115629
- const trimmedUrl = supabaseUrl === null || supabaseUrl === void 0 ? void 0 : supabaseUrl.trim();
115715
+ function validateSupabaseUrl(supabaseUrl2) {
115716
+ const trimmedUrl = supabaseUrl2 === null || supabaseUrl2 === void 0 ? void 0 : supabaseUrl2.trim();
115630
115717
  if (!trimmedUrl) throw new Error("supabaseUrl is required.");
115631
115718
  if (!trimmedUrl.match(/^https?:\/\//i)) throw new Error("Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.");
115632
115719
  try {
@@ -115684,12 +115771,12 @@ var init_dist4 = __esm({
115684
115771
  resolveHeadersConstructor = () => {
115685
115772
  return Headers;
115686
115773
  };
115687
- fetchWithAuth = (supabaseKey, supabaseUrl, getAccessToken, customFetch, tracePropagationOptions) => {
115774
+ fetchWithAuth = (supabaseKey, supabaseUrl2, getAccessToken, customFetch, tracePropagationOptions) => {
115688
115775
  const fetch$1 = resolveFetch2(customFetch);
115689
115776
  const HeadersConstructor = resolveHeadersConstructor();
115690
115777
  const traceEnabled = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.enabled) === true;
115691
115778
  const respectSampling = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.respectSamplingDecision) !== false;
115692
- const traceTargets = traceEnabled ? getDefaultPropagationTargets(supabaseUrl) : null;
115779
+ const traceTargets = traceEnabled ? getDefaultPropagationTargets(supabaseUrl2) : null;
115693
115780
  return async (input, init) => {
115694
115781
  var _await$getAccessToken;
115695
115782
  const accessToken = (_await$getAccessToken = await getAccessToken()) !== null && _await$getAccessToken !== void 0 ? _await$getAccessToken : supabaseKey;
@@ -115900,11 +115987,11 @@ var init_dist4 = __esm({
115900
115987
  * const { data } = await supabase.from('profiles').select('*')
115901
115988
  * ```
115902
115989
  */
115903
- constructor(supabaseUrl, supabaseKey, options) {
115990
+ constructor(supabaseUrl2, supabaseKey, options) {
115904
115991
  var _settings$auth$storag, _settings$global$head;
115905
- this.supabaseUrl = supabaseUrl;
115992
+ this.supabaseUrl = supabaseUrl2;
115906
115993
  this.supabaseKey = supabaseKey;
115907
- const baseUrl = validateSupabaseUrl(supabaseUrl);
115994
+ const baseUrl = validateSupabaseUrl(supabaseUrl2);
115908
115995
  if (!supabaseKey) throw new Error("supabaseKey is required.");
115909
115996
  this.realtimeUrl = new URL("realtime/v1", baseUrl);
115910
115997
  this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace("http", "ws");
@@ -115932,7 +116019,7 @@ var init_dist4 = __esm({
115932
116019
  throw new Error(`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(prop)} is not possible`);
115933
116020
  } });
115934
116021
  }
115935
- this.fetch = fetchWithAuth(supabaseKey, supabaseUrl, this._getAccessToken.bind(this), settings.global.fetch, settings.tracePropagation);
116022
+ this.fetch = fetchWithAuth(supabaseKey, supabaseUrl2, this._getAccessToken.bind(this), settings.global.fetch, settings.tracePropagation);
115936
116023
  this.realtime = this._initRealtimeClient(_objectSpread23({
115937
116024
  headers: this.headers,
115938
116025
  accessToken: this._getAccessToken.bind(this),
@@ -116116,8 +116203,8 @@ var init_dist4 = __esm({
116116
116203
  }
116117
116204
  }
116118
116205
  };
116119
- createClient = (supabaseUrl, supabaseKey, options) => {
116120
- return new SupabaseClient(supabaseUrl, supabaseKey, options);
116206
+ createClient = (supabaseUrl2, supabaseKey, options) => {
116207
+ return new SupabaseClient(supabaseUrl2, supabaseKey, options);
116121
116208
  };
116122
116209
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
116123
116210
  }
@@ -116243,6 +116330,33 @@ var init_inferenceCost = __esm({
116243
116330
  outputUsdPer1k: 275e-5,
116244
116331
  cachedInputUsdPer1k: 15e-5
116245
116332
  },
116333
+ [WANDB_DEEPSEEK_V4_FLASH_OPTION_ID]: {
116334
+ modelOptionId: WANDB_DEEPSEEK_V4_FLASH_OPTION_ID,
116335
+ providerId: "wandb",
116336
+ modelId: "deepseek-ai/DeepSeek-V4-Flash",
116337
+ label: "W&B DeepSeek V4 Flash",
116338
+ inputUsdPer1k: 14e-5,
116339
+ outputUsdPer1k: 28e-5,
116340
+ cachedInputUsdPer1k: 7e-5
116341
+ },
116342
+ [WANDB_DEEPSEEK_V4_PRO_OPTION_ID]: {
116343
+ modelOptionId: WANDB_DEEPSEEK_V4_PRO_OPTION_ID,
116344
+ providerId: "wandb",
116345
+ modelId: "deepseek-ai/DeepSeek-V4-Pro",
116346
+ label: "W&B DeepSeek V4 Pro",
116347
+ inputUsdPer1k: 174e-5,
116348
+ outputUsdPer1k: 348e-5,
116349
+ cachedInputUsdPer1k: 14e-5
116350
+ },
116351
+ [BEDROCK_KIMI_K25_OPTION_ID]: {
116352
+ modelOptionId: BEDROCK_KIMI_K25_OPTION_ID,
116353
+ providerId: "bedrock_mantle",
116354
+ modelId: "moonshotai.kimi-k2.5",
116355
+ label: "Bedrock Mantle \xB7 Kimi K2.5 Vision",
116356
+ inputUsdPer1k: 6e-4,
116357
+ outputUsdPer1k: 3e-3,
116358
+ cachedInputUsdPer1k: 6e-4
116359
+ },
116246
116360
  [BEDROCK_GLM5_OPTION_ID]: {
116247
116361
  modelOptionId: BEDROCK_GLM5_OPTION_ID,
116248
116362
  providerId: "bedrock_mantle",
@@ -118544,8 +118658,15 @@ function buildBody(option, opts, model, stream, includeResponseFormat = true) {
118544
118658
  body.tool_choice = opts.toolChoice ?? "auto";
118545
118659
  }
118546
118660
  if (includeResponseFormat && opts.responseFormat) body.response_format = opts.responseFormat;
118547
- if (option.providerId === "wandb" && option.modelId.startsWith("Qwen/")) {
118548
- body.chat_template_kwargs = { enable_thinking: false };
118661
+ const reasoning = opts.reasoning;
118662
+ if (option.providerId === "wandb") {
118663
+ if (reasoning && typeof reasoning.enableThinking === "boolean") {
118664
+ body.chat_template_kwargs = { enable_thinking: reasoning.enableThinking };
118665
+ } else if (option.modelId.startsWith("Qwen/")) {
118666
+ body.chat_template_kwargs = { enable_thinking: false };
118667
+ }
118668
+ } else if (reasoning?.reasoningEffort && (option.providerId === "bedrock_mantle" || option.providerId === "cerebras")) {
118669
+ body.reasoning_effort = reasoning.reasoningEffort;
118549
118670
  }
118550
118671
  if (stream) {
118551
118672
  body.stream = true;
@@ -118992,7 +119113,80 @@ var init_autoRouter = __esm({
118992
119113
  }
118993
119114
  });
118994
119115
 
119116
+ // features/perchTerminal/runtime/roost.ts
119117
+ function normalizeRoostTier(value) {
119118
+ if (value === "max") return "pro_max";
119119
+ return isRoostTier(value) ? value : null;
119120
+ }
119121
+ function normalizeRoostModelChoice(value) {
119122
+ if (value === "internal") return "internal";
119123
+ return normalizeRoostTier(value);
119124
+ }
119125
+ function isRoostTier(value) {
119126
+ return value === "standard" || value === "standard_max" || value === "pro" || value === "pro_max";
119127
+ }
119128
+ var ROOST_TIER_LABELS;
119129
+ var init_roost = __esm({
119130
+ "features/perchTerminal/runtime/roost.ts"() {
119131
+ "use strict";
119132
+ init_modelRegistry();
119133
+ ROOST_TIER_LABELS = {
119134
+ standard: "Standard",
119135
+ standard_max: "Standard Max",
119136
+ pro: "Pro",
119137
+ pro_max: "Pro Max"
119138
+ };
119139
+ }
119140
+ });
119141
+
119142
+ // features/perchTerminal/runtime/roostUserSelectionClient.ts
119143
+ function coerceSelection(raw) {
119144
+ const choice = normalizeRoostModelChoice(raw?.choice);
119145
+ return {
119146
+ choice: choice ?? DEFAULT_ROOST_USER_SELECTION.choice,
119147
+ reasoningEnabled: typeof raw?.reasoningEnabled === "boolean" ? raw.reasoningEnabled : DEFAULT_ROOST_USER_SELECTION.reasoningEnabled
119148
+ };
119149
+ }
119150
+ function peekStoredRoostUserSelection() {
119151
+ if (typeof window === "undefined") return null;
119152
+ try {
119153
+ const raw = window.localStorage.getItem(ROOST_USER_SELECTION_STORAGE_KEY);
119154
+ if (!raw) return null;
119155
+ return coerceSelection(JSON.parse(raw));
119156
+ } catch {
119157
+ return null;
119158
+ }
119159
+ }
119160
+ function setRoostUserSelectionForNode(selection) {
119161
+ nodeSelectionOverride = selection ? coerceSelection(selection) : null;
119162
+ }
119163
+ function getEffectiveRoostUserSelection() {
119164
+ if (typeof window !== "undefined") return peekStoredRoostUserSelection();
119165
+ return nodeSelectionOverride;
119166
+ }
119167
+ var ROOST_USER_SELECTION_STORAGE_KEY, DEFAULT_ROOST_USER_SELECTION, nodeSelectionOverride;
119168
+ var init_roostUserSelectionClient = __esm({
119169
+ "features/perchTerminal/runtime/roostUserSelectionClient.ts"() {
119170
+ "use strict";
119171
+ init_roost();
119172
+ ROOST_USER_SELECTION_STORAGE_KEY = "perch.roostUserSelection.v1";
119173
+ DEFAULT_ROOST_USER_SELECTION = {
119174
+ choice: "standard",
119175
+ reasoningEnabled: true
119176
+ };
119177
+ nodeSelectionOverride = null;
119178
+ }
119179
+ });
119180
+
118995
119181
  // features/perchTerminal/runtime/modelRouter.ts
119182
+ function roostUserSelectionBodyFields() {
119183
+ const selection = getEffectiveRoostUserSelection();
119184
+ if (!selection) return {};
119185
+ return {
119186
+ roostModelChoice: selection.choice,
119187
+ roostReasoning: selection.reasoningEnabled
119188
+ };
119189
+ }
118996
119190
  async function callModelRouter(_config, request, opts) {
118997
119191
  const explicitProxyUrl = resolveModelCallProxyUrl(opts?.cliServerAppUrl ?? null);
118998
119192
  if (typeof window !== "undefined") {
@@ -119019,7 +119213,8 @@ async function callModelRouter(_config, request, opts) {
119019
119213
  responseFormat: request.responseFormat,
119020
119214
  preferredModelId: opts?.preferredModelId ?? null,
119021
119215
  avoidModelIds: opts?.avoidModelIds,
119022
- attribution: opts?.attribution ?? null
119216
+ attribution: opts?.attribution ?? null,
119217
+ reasoning: opts?.reasoning ?? null
119023
119218
  };
119024
119219
  try {
119025
119220
  if (request.onChunk || request.onReasoningChunk || request.onStreamEvent) {
@@ -119145,7 +119340,8 @@ async function callModelRouterViaServer(request, opts, endpoint = "/api/perch-te
119145
119340
  raceMode: opts?.raceMode,
119146
119341
  preferredModelId: opts?.preferredModelId ?? null,
119147
119342
  avoidModelIds: opts?.avoidModelIds ?? [],
119148
- attribution: opts?.attribution ?? null
119343
+ attribution: opts?.attribution ?? null,
119344
+ ...roostUserSelectionBodyFields()
119149
119345
  });
119150
119346
  const response = await fetchModelProxyWithRetry(endpoint, {
119151
119347
  method: "POST",
@@ -119413,6 +119609,7 @@ var init_modelRouter = __esm({
119413
119609
  init_modelRegistry();
119414
119610
  init_contextMeter();
119415
119611
  init_inferenceUsageRecorder();
119612
+ init_roostUserSelectionClient();
119416
119613
  init_providerModelStep();
119417
119614
  init_toolCallNormalizer();
119418
119615
  MODEL_CALL_PROXY_ENV = "PERCH_MODEL_CALL_PROXY_URL";
@@ -119432,7 +119629,7 @@ var init_accountHelpers = __esm({
119432
119629
 
119433
119630
  // features/perchTerminal/runtime/fireworksDeepSeekPreset.ts
119434
119631
  function usesNimVisionObserverPreset(selection) {
119435
- return selection?.modelBackendPreset === "fireworks_deepseek" || selection?.modelBackendPreset === "wandb_minimax" || selection?.modelBackendPreset === "wandb_glm51" || selection?.modelBackendPreset === "wandb_nemotron_ultra" || selection?.modelBackendPreset === "bedrock_glm5" || selection?.modelBackendPreset === "bedrock_qwen3_coder" || selection?.modelBackendPreset === "bedrock_gpt_oss_120b" || selection?.modelBackendPreset === "bedrock_nemotron_super" || selection?.modelBackendPreset === "bedrock_minimax_m2" || selection?.modelBackendPreset === "bedrock_deepseek_v32";
119632
+ return selection?.modelBackendPreset === "fireworks_deepseek" || selection?.modelBackendPreset === "wandb_minimax" || selection?.modelBackendPreset === "wandb_glm51" || selection?.modelBackendPreset === "wandb_nemotron_ultra" || selection?.modelBackendPreset === "wandb_deepseek_flash" || selection?.modelBackendPreset === "wandb_deepseek_pro" || selection?.modelBackendPreset === "bedrock_glm5" || selection?.modelBackendPreset === "bedrock_qwen3_coder" || selection?.modelBackendPreset === "bedrock_gpt_oss_120b" || selection?.modelBackendPreset === "bedrock_nemotron_super" || selection?.modelBackendPreset === "bedrock_minimax_m2" || selection?.modelBackendPreset === "bedrock_deepseek_v32";
119436
119633
  }
119437
119634
  function resolveVisionOptionChain(selection) {
119438
119635
  const chain2 = [];
@@ -128817,10 +129014,10 @@ var require_dist4 = __commonJS({
128817
129014
  }
128818
129015
  return false;
128819
129016
  }
128820
- function getDefaultPropagationTargets2(supabaseUrl) {
129017
+ function getDefaultPropagationTargets2(supabaseUrl2) {
128821
129018
  const targets = [];
128822
129019
  try {
128823
- const url = new URL(supabaseUrl);
129020
+ const url = new URL(supabaseUrl2);
128824
129021
  targets.push(url.hostname);
128825
129022
  } catch (error) {
128826
129023
  }
@@ -128886,12 +129083,12 @@ var require_dist4 = __commonJS({
128886
129083
  var resolveHeadersConstructor2 = () => {
128887
129084
  return Headers;
128888
129085
  };
128889
- var fetchWithAuth2 = (supabaseKey, supabaseUrl, getAccessToken, customFetch, tracePropagationOptions) => {
129086
+ var fetchWithAuth2 = (supabaseKey, supabaseUrl2, getAccessToken, customFetch, tracePropagationOptions) => {
128890
129087
  const fetch$1 = resolveFetch3(customFetch);
128891
129088
  const HeadersConstructor = resolveHeadersConstructor2();
128892
129089
  const traceEnabled = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.enabled) === true;
128893
129090
  const respectSampling = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.respectSamplingDecision) !== false;
128894
- const traceTargets = traceEnabled ? getDefaultPropagationTargets2(supabaseUrl) : null;
129091
+ const traceTargets = traceEnabled ? getDefaultPropagationTargets2(supabaseUrl2) : null;
128895
129092
  return async (input, init) => {
128896
129093
  var _await$getAccessToken;
128897
129094
  const accessToken = (_await$getAccessToken = await getAccessToken()) !== null && _await$getAccessToken !== void 0 ? _await$getAccessToken : supabaseKey;
@@ -128947,8 +129144,8 @@ var require_dist4 = __commonJS({
128947
129144
  else delete result2.accessToken;
128948
129145
  return result2;
128949
129146
  }
128950
- function validateSupabaseUrl2(supabaseUrl) {
128951
- const trimmedUrl = supabaseUrl === null || supabaseUrl === void 0 ? void 0 : supabaseUrl.trim();
129147
+ function validateSupabaseUrl2(supabaseUrl2) {
129148
+ const trimmedUrl = supabaseUrl2 === null || supabaseUrl2 === void 0 ? void 0 : supabaseUrl2.trim();
128952
129149
  if (!trimmedUrl) throw new Error("supabaseUrl is required.");
128953
129150
  if (!trimmedUrl.match(/^https?:\/\//i)) throw new Error("Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.");
128954
129151
  try {
@@ -129150,11 +129347,11 @@ var require_dist4 = __commonJS({
129150
129347
  * const { data } = await supabase.from('profiles').select('*')
129151
129348
  * ```
129152
129349
  */
129153
- constructor(supabaseUrl, supabaseKey, options) {
129350
+ constructor(supabaseUrl2, supabaseKey, options) {
129154
129351
  var _settings$auth$storag, _settings$global$head;
129155
- this.supabaseUrl = supabaseUrl;
129352
+ this.supabaseUrl = supabaseUrl2;
129156
129353
  this.supabaseKey = supabaseKey;
129157
- const baseUrl = validateSupabaseUrl2(supabaseUrl);
129354
+ const baseUrl = validateSupabaseUrl2(supabaseUrl2);
129158
129355
  if (!supabaseKey) throw new Error("supabaseKey is required.");
129159
129356
  this.realtimeUrl = new URL("realtime/v1", baseUrl);
129160
129357
  this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace("http", "ws");
@@ -129182,7 +129379,7 @@ var require_dist4 = __commonJS({
129182
129379
  throw new Error(`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(prop)} is not possible`);
129183
129380
  } });
129184
129381
  }
129185
- this.fetch = fetchWithAuth2(supabaseKey, supabaseUrl, this._getAccessToken.bind(this), settings.global.fetch, settings.tracePropagation);
129382
+ this.fetch = fetchWithAuth2(supabaseKey, supabaseUrl2, this._getAccessToken.bind(this), settings.global.fetch, settings.tracePropagation);
129186
129383
  this.realtime = this._initRealtimeClient(_objectSpread24({
129187
129384
  headers: this.headers,
129188
129385
  accessToken: this._getAccessToken.bind(this),
@@ -129366,8 +129563,8 @@ var require_dist4 = __commonJS({
129366
129563
  }
129367
129564
  }
129368
129565
  };
129369
- var createClient3 = (supabaseUrl, supabaseKey, options) => {
129370
- return new SupabaseClient2(supabaseUrl, supabaseKey, options);
129566
+ var createClient3 = (supabaseUrl2, supabaseKey, options) => {
129567
+ return new SupabaseClient2(supabaseUrl2, supabaseKey, options);
129371
129568
  };
129372
129569
  function shouldShowDeprecationWarning2() {
129373
129570
  if (typeof window !== "undefined") return false;
@@ -130428,13 +130625,13 @@ var require_createBrowserClient = __commonJS({
130428
130625
  var cookies_1 = require_cookies();
130429
130626
  var warnDeprecatedPackage_1 = require_warnDeprecatedPackage();
130430
130627
  var cachedBrowserClient;
130431
- function createBrowserClient2(supabaseUrl, supabaseKey, options) {
130628
+ function createBrowserClient2(supabaseUrl2, supabaseKey, options) {
130432
130629
  (0, warnDeprecatedPackage_1.warnIfUsingDeprecatedAuthHelpersPackage)();
130433
130630
  const shouldUseSingleton = options?.isSingleton === true || (!options || !("isSingleton" in options)) && (0, utils_1.isBrowser)();
130434
130631
  if (shouldUseSingleton && cachedBrowserClient) {
130435
130632
  return cachedBrowserClient;
130436
130633
  }
130437
- if (!supabaseUrl || !supabaseKey) {
130634
+ if (!supabaseUrl2 || !supabaseKey) {
130438
130635
  throw new Error(`@supabase/ssr: Your project's URL and API key are required to create a Supabase client!
130439
130636
 
130440
130637
  Check your Supabase project's API settings to find these values
@@ -130445,7 +130642,7 @@ https://supabase.com/dashboard/project/_/settings/api`);
130445
130642
  ...options,
130446
130643
  cookieEncoding: options?.cookieEncoding ?? "base64url"
130447
130644
  }, false);
130448
- const client = (0, supabase_js_1.createClient)(supabaseUrl, supabaseKey, {
130645
+ const client = (0, supabase_js_1.createClient)(supabaseUrl2, supabaseKey, {
130449
130646
  // TODO: resolve type error
130450
130647
  ...options,
130451
130648
  global: {
@@ -130481,15 +130678,15 @@ var require_createServerClient = __commonJS({
130481
130678
  "node_modules/@supabase/ssr/dist/main/createServerClient.js"(exports2) {
130482
130679
  "use strict";
130483
130680
  Object.defineProperty(exports2, "__esModule", { value: true });
130484
- exports2.createServerClient = createServerClient;
130681
+ exports2.createServerClient = createServerClient2;
130485
130682
  var supabase_js_1 = require_dist4();
130486
130683
  var version_1 = require_version3();
130487
130684
  var cookies_1 = require_cookies();
130488
130685
  var helpers_1 = require_helpers2();
130489
130686
  var warnDeprecatedPackage_1 = require_warnDeprecatedPackage();
130490
- function createServerClient(supabaseUrl, supabaseKey, options) {
130687
+ function createServerClient2(supabaseUrl2, supabaseKey, options) {
130491
130688
  (0, warnDeprecatedPackage_1.warnIfUsingDeprecatedAuthHelpersPackage)();
130492
- if (!supabaseUrl || !supabaseKey) {
130689
+ if (!supabaseUrl2 || !supabaseKey) {
130493
130690
  throw new Error(`Your project's URL and Key are required to create a Supabase client!
130494
130691
 
130495
130692
  Check your Supabase project's API settings to find these values
@@ -130500,7 +130697,7 @@ https://supabase.com/dashboard/project/_/settings/api`);
130500
130697
  ...options,
130501
130698
  cookieEncoding: options?.cookieEncoding ?? "base64url"
130502
130699
  }, true);
130503
- const client = (0, supabase_js_1.createClient)(supabaseUrl, supabaseKey, {
130700
+ const client = (0, supabase_js_1.createClient)(supabaseUrl2, supabaseKey, {
130504
130701
  // TODO: resolve type error
130505
130702
  ...options,
130506
130703
  global: {
@@ -130579,6 +130776,36 @@ var require_main4 = __commonJS({
130579
130776
  }
130580
130777
  });
130581
130778
 
130779
+ // lib/supabase/factory.ts
130780
+ function supabaseUrl() {
130781
+ return process.env.NEXT_PUBLIC_SUPABASE_URL;
130782
+ }
130783
+ function supabaseAnonKey() {
130784
+ return process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
130785
+ }
130786
+ function authCookieOptions() {
130787
+ const secure = process.env.NODE_ENV === "production";
130788
+ return {
130789
+ cookieOptions: {
130790
+ path: "/",
130791
+ sameSite: "lax",
130792
+ ...secure ? { secure: true } : {}
130793
+ }
130794
+ };
130795
+ }
130796
+ function createBrowserSupabaseClient() {
130797
+ return (0, import_ssr.createBrowserClient)(supabaseUrl(), supabaseAnonKey(), {
130798
+ isSingleton: true,
130799
+ ...authCookieOptions()
130800
+ });
130801
+ }
130802
+ var import_ssr;
130803
+ var init_factory = __esm({
130804
+ "lib/supabase/factory.ts"() {
130805
+ import_ssr = __toESM(require_main4());
130806
+ }
130807
+ });
130808
+
130582
130809
  // lib/supabase/client.ts
130583
130810
  function createClient2() {
130584
130811
  if (!isSupabaseConfigured()) {
@@ -130587,19 +130814,15 @@ function createClient2() {
130587
130814
  );
130588
130815
  }
130589
130816
  if (!browserClient) {
130590
- browserClient = (0, import_ssr.createBrowserClient)(
130591
- process.env.NEXT_PUBLIC_SUPABASE_URL,
130592
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
130593
- { isSingleton: true }
130594
- );
130817
+ browserClient = createBrowserSupabaseClient();
130595
130818
  }
130596
130819
  return browserClient;
130597
130820
  }
130598
- var import_ssr, browserClient;
130821
+ var browserClient;
130599
130822
  var init_client = __esm({
130600
130823
  "lib/supabase/client.ts"() {
130601
- import_ssr = __toESM(require_main4());
130602
130824
  init_config();
130825
+ init_factory();
130603
130826
  }
130604
130827
  });
130605
130828
 
@@ -138907,6 +139130,7 @@ function offSandboxEvent(runId, handler) {
138907
139130
  var LOCAL_WORKSPACE_ACCESS_UNAVAILABLE;
138908
139131
  var init_bridge = __esm({
138909
139132
  "features/perchTerminal/desktop/bridge.ts"() {
139133
+ "use strict";
138910
139134
  LOCAL_WORKSPACE_ACCESS_UNAVAILABLE = "Local workspace access is unavailable. Open Perch Desktop or update the app.";
138911
139135
  }
138912
139136
  });
@@ -223469,6 +223693,295 @@ var init_access = __esm({
223469
223693
  }
223470
223694
  });
223471
223695
 
223696
+ // lib/perch-ai/perchTokens.ts
223697
+ function usdToPerchTokens(usd) {
223698
+ if (typeof usd !== "number" || !Number.isFinite(usd) || usd <= 0) return 0;
223699
+ return Math.ceil(usd / PERCH_TOKEN_USD);
223700
+ }
223701
+ function nullableUsdToPerchTokens(usd) {
223702
+ const tokens = usdToPerchTokens(usd);
223703
+ return tokens > 0 ? tokens : null;
223704
+ }
223705
+ function usdLimitToPerchTokens(usd) {
223706
+ if (!Number.isFinite(usd) || usd <= 0) return 0;
223707
+ return Math.round(usd / PERCH_TOKEN_USD);
223708
+ }
223709
+ var PERCH_TOKEN_USD, DEFAULT_PILOT_DAILY_LIMIT_USD, DEFAULT_PILOT_MONTHLY_LIMIT_USD;
223710
+ var init_perchTokens = __esm({
223711
+ "lib/perch-ai/perchTokens.ts"() {
223712
+ PERCH_TOKEN_USD = 1e-3;
223713
+ DEFAULT_PILOT_DAILY_LIMIT_USD = 5;
223714
+ DEFAULT_PILOT_MONTHLY_LIMIT_USD = DEFAULT_PILOT_DAILY_LIMIT_USD * 30;
223715
+ }
223716
+ });
223717
+
223718
+ // features/perchTerminal/persistence/usagePersistence.ts
223719
+ function startOfLocalDay() {
223720
+ const date = /* @__PURE__ */ new Date();
223721
+ date.setHours(0, 0, 0, 0);
223722
+ return date.toISOString();
223723
+ }
223724
+ function startOfRollingWeek() {
223725
+ const date = /* @__PURE__ */ new Date();
223726
+ date.setDate(date.getDate() - 7);
223727
+ return date.toISOString();
223728
+ }
223729
+ function startOfLocalMonth() {
223730
+ const date = /* @__PURE__ */ new Date();
223731
+ date.setDate(1);
223732
+ date.setHours(0, 0, 0, 0);
223733
+ return date.toISOString();
223734
+ }
223735
+ function sumPt(rows, since) {
223736
+ const total = rows.filter((row) => Date.parse(row.created_at) >= Date.parse(since)).reduce((sum, row) => sum + row.total_pt, 0);
223737
+ return total > 0 ? total : null;
223738
+ }
223739
+ function filterInferenceRows(rows, since) {
223740
+ return rows.filter((row) => Date.parse(row.created_at) >= Date.parse(since));
223741
+ }
223742
+ function sumInferenceCost(rows, since) {
223743
+ const total = filterInferenceRows(rows, since).reduce(
223744
+ (sum, row) => sum + Number(row.estimated_cost_usd ?? 0),
223745
+ 0
223746
+ );
223747
+ return total > 0 ? total : null;
223748
+ }
223749
+ function sumInferenceTokens(rows, since) {
223750
+ const total = filterInferenceRows(rows, since).reduce(
223751
+ (sum, row) => sum + Number(row.total_tokens ?? 0),
223752
+ 0
223753
+ );
223754
+ return total > 0 ? total : null;
223755
+ }
223756
+ function countInferenceCalls(rows, since) {
223757
+ const total = filterInferenceRows(rows, since).length;
223758
+ return total > 0 ? total : null;
223759
+ }
223760
+ function createUsagePersistence(supabase) {
223761
+ return {
223762
+ async listRateCards() {
223763
+ const { data, error } = await supabase.from("perch_ai_token_rate_cards").select("*").order("created_at", { ascending: false });
223764
+ if (error) throw error;
223765
+ return data ?? [];
223766
+ },
223767
+ async listUsageRawEvents({
223768
+ userId,
223769
+ workspaceId,
223770
+ limit = 50
223771
+ }) {
223772
+ const { data, error } = await supabase.from("perch_ai_usage_raw_events").select("*").eq("user_id", userId).eq("workspace_id", workspaceId).order("created_at", { ascending: false }).limit(limit);
223773
+ if (error) throw error;
223774
+ return data ?? [];
223775
+ },
223776
+ async listTokenCalculations({
223777
+ userId,
223778
+ workspaceId,
223779
+ since,
223780
+ limit = 250
223781
+ }) {
223782
+ let query = supabase.from("perch_ai_token_calculations").select("*").eq("user_id", userId).eq("workspace_id", workspaceId).order("created_at", { ascending: false }).limit(limit);
223783
+ if (since) query = query.gte("created_at", since);
223784
+ const { data, error } = await query;
223785
+ if (error) throw error;
223786
+ return data ?? [];
223787
+ },
223788
+ async listInferenceUsageRows({
223789
+ userId,
223790
+ workspaceId,
223791
+ since,
223792
+ limit = 250
223793
+ }) {
223794
+ let query = supabase.from("perch_ai_inference_usage").select("*").eq("user_id", userId).eq("workspace_id", workspaceId).order("created_at", { ascending: false }).limit(limit);
223795
+ if (since) query = query.gte("created_at", since);
223796
+ const { data, error } = await query;
223797
+ if (error) throw error;
223798
+ return data ?? [];
223799
+ },
223800
+ async getUsageMeter({
223801
+ userId,
223802
+ workspaceId
223803
+ }) {
223804
+ const localDay = startOfLocalDay();
223805
+ const rollingWeek = startOfRollingWeek();
223806
+ const localMonth = startOfLocalMonth();
223807
+ const [rows, inferenceRows] = await Promise.all([
223808
+ this.listTokenCalculations({
223809
+ userId,
223810
+ workspaceId,
223811
+ since: localMonth,
223812
+ limit: 5e3
223813
+ }),
223814
+ this.listInferenceUsageRows({
223815
+ userId,
223816
+ workspaceId,
223817
+ since: localMonth,
223818
+ limit: 5e3
223819
+ })
223820
+ ]);
223821
+ const dailyUsd = sumInferenceCost(inferenceRows, localDay);
223822
+ const weeklyUsd = sumInferenceCost(inferenceRows, rollingWeek);
223823
+ const monthlyUsd = sumInferenceCost(inferenceRows, localMonth);
223824
+ return {
223825
+ dailyPt: nullableUsdToPerchTokens(dailyUsd) ?? sumPt(rows, localDay),
223826
+ weeklyPt: nullableUsdToPerchTokens(weeklyUsd) ?? sumPt(rows, rollingWeek),
223827
+ rows,
223828
+ dailyUsd,
223829
+ weeklyUsd,
223830
+ monthlyUsd,
223831
+ dailyTokens: sumInferenceTokens(inferenceRows, localDay),
223832
+ weeklyTokens: sumInferenceTokens(inferenceRows, rollingWeek),
223833
+ monthlyTokens: sumInferenceTokens(inferenceRows, localMonth),
223834
+ dailyCalls: countInferenceCalls(inferenceRows, localDay),
223835
+ weeklyCalls: countInferenceCalls(inferenceRows, rollingWeek),
223836
+ monthlyCalls: countInferenceCalls(inferenceRows, localMonth),
223837
+ inferenceRows
223838
+ };
223839
+ }
223840
+ };
223841
+ }
223842
+ var init_usagePersistence = __esm({
223843
+ "features/perchTerminal/persistence/usagePersistence.ts"() {
223844
+ init_perchTokens();
223845
+ }
223846
+ });
223847
+
223848
+ // features/perchTerminal/runtime/usage/usageAllowance.ts
223849
+ function readEntitlementNumber(value) {
223850
+ if (typeof value === "number" && Number.isFinite(value)) return value;
223851
+ if (typeof value === "string") {
223852
+ const parsed = Number(value);
223853
+ return Number.isFinite(parsed) ? parsed : null;
223854
+ }
223855
+ if (value && typeof value === "object") {
223856
+ const record = value;
223857
+ return readEntitlementNumber(record.usd) ?? readEntitlementNumber(record.amount) ?? readEntitlementNumber(record.value) ?? readEntitlementNumber(record.limit);
223858
+ }
223859
+ return null;
223860
+ }
223861
+ function readLimitUsd(entitlements, keys2, fallback) {
223862
+ for (const key of keys2) {
223863
+ const match = entitlements.find((entitlement) => entitlement.key === key);
223864
+ const value = match ? readEntitlementNumber(match.value_json) : null;
223865
+ if (value != null && value > 0) return value;
223866
+ }
223867
+ return fallback;
223868
+ }
223869
+ function isUnlimitedUsageSession(session) {
223870
+ if (!session) return false;
223871
+ return session.membershipRole === "admin" || session.membershipRole === "internal" || session.planCode === "internal";
223872
+ }
223873
+ function buildUsageAllowanceSnapshot(session, meter) {
223874
+ const unlimited = isUnlimitedUsageSession(session);
223875
+ const entitlements = session.entitlements ?? [];
223876
+ const dailyLimitUsd = readLimitUsd(
223877
+ entitlements,
223878
+ ["usage_daily_usd", "daily_usage_usd", "pilot_daily_usd", "user_daily_cap_usd"],
223879
+ DEFAULT_PILOT_DAILY_LIMIT_USD
223880
+ );
223881
+ const monthlyLimitUsd = readLimitUsd(
223882
+ entitlements,
223883
+ ["usage_monthly_usd", "monthly_usage_usd", "pilot_monthly_usd", "user_monthly_cap_usd"],
223884
+ DEFAULT_PILOT_MONTHLY_LIMIT_USD
223885
+ );
223886
+ return {
223887
+ unlimited,
223888
+ planLabel: session.planName ?? session.planCode ?? "Pilot",
223889
+ dailyUsedPt: meter?.dailyPt ?? usdToPerchTokens(meter?.dailyUsd),
223890
+ weeklyUsedPt: meter?.weeklyPt ?? usdToPerchTokens(meter?.weeklyUsd),
223891
+ monthlyUsedPt: usdToPerchTokens(meter?.monthlyUsd),
223892
+ dailyLimitPt: usdLimitToPerchTokens(dailyLimitUsd),
223893
+ monthlyLimitPt: usdLimitToPerchTokens(monthlyLimitUsd),
223894
+ dailyCalls: meter?.dailyCalls ?? null,
223895
+ weeklyCalls: meter?.weeklyCalls ?? null,
223896
+ monthlyCalls: meter?.monthlyCalls ?? null,
223897
+ dailyTokens: meter?.dailyTokens ?? null,
223898
+ weeklyTokens: meter?.weeklyTokens ?? null,
223899
+ monthlyTokens: meter?.monthlyTokens ?? null
223900
+ };
223901
+ }
223902
+ function formatInteger(value) {
223903
+ return Math.max(0, Math.round(value ?? 0)).toLocaleString("en-US");
223904
+ }
223905
+ function formatPt(value) {
223906
+ return `${formatInteger(value)} PT`;
223907
+ }
223908
+ function percentUsed(used, limit) {
223909
+ if (limit <= 0) return 0;
223910
+ return Math.max(0, Math.min(used / limit * 100, 100));
223911
+ }
223912
+ function renderTextBar(percent2, width = 24) {
223913
+ const clamped = Math.max(0, Math.min(100, percent2));
223914
+ const filled = Math.round(clamped / 100 * width);
223915
+ return `${"\u2588".repeat(filled)}${"\u2591".repeat(Math.max(0, width - filled))}`;
223916
+ }
223917
+ function formatAllowanceLine(label, usedPt, limitPt, unlimited) {
223918
+ const bar = renderTextBar(unlimited ? 100 : percentUsed(usedPt, limitPt));
223919
+ const amount = unlimited ? `${formatPt(usedPt)} / Unlimited` : `${formatPt(usedPt)} / ${formatPt(limitPt)} \xB7 ${Math.round(percentUsed(usedPt, limitPt))}%`;
223920
+ return `- ${label.padEnd(10)} ${bar} ${amount}`;
223921
+ }
223922
+ function formatUsageAllowanceText(allowance) {
223923
+ const allowanceLabel = allowance.unlimited ? `${allowance.planLabel} \xB7 unlimited allowance` : `${allowance.planLabel} \xB7 ${formatPt(allowance.dailyLimitPt)} daily allowance`;
223924
+ const lines = [
223925
+ "Perch Tokens",
223926
+ `- Plan: ${allowanceLabel}`,
223927
+ formatAllowanceLine("Today", allowance.dailyUsedPt, allowance.dailyLimitPt, allowance.unlimited),
223928
+ formatAllowanceLine("Month", allowance.monthlyUsedPt, allowance.monthlyLimitPt, allowance.unlimited),
223929
+ `- Activity ${formatInteger(allowance.monthlyCalls)} chats this month \xB7 ${formatPt(allowance.weeklyUsedPt)} in 7 days \xB7 ${formatInteger(allowance.weeklyTokens)} model tokens`
223930
+ ];
223931
+ return lines.join("\n");
223932
+ }
223933
+ async function fetchUsageAllowanceFromServer(input) {
223934
+ const appUrl = input.cliServerAppUrl?.trim();
223935
+ const token = input.cliServerAccessToken?.trim();
223936
+ if (!appUrl || !token || typeof fetch !== "function") return null;
223937
+ const controller = new AbortController();
223938
+ const timeout = setTimeout(() => controller.abort(), 5e3);
223939
+ try {
223940
+ const response = await fetch(`${appUrl.replace(/\/+$/, "")}/api/perchai/account`, {
223941
+ method: "GET",
223942
+ headers: {
223943
+ Accept: "application/json",
223944
+ Authorization: `Bearer ${token}`
223945
+ },
223946
+ signal: controller.signal
223947
+ });
223948
+ if (!response.ok) return null;
223949
+ const payload = await response.json();
223950
+ if (!payload.ok || !payload.session) return null;
223951
+ return buildUsageAllowanceSnapshot(payload.session, payload.usageMeter ?? null);
223952
+ } catch {
223953
+ return null;
223954
+ } finally {
223955
+ clearTimeout(timeout);
223956
+ }
223957
+ }
223958
+ async function resolveUsageAllowance(input) {
223959
+ if (input.supabase && input.userId && input.workspaceId) {
223960
+ try {
223961
+ const meter = await createUsagePersistence(input.supabase).getUsageMeter({
223962
+ userId: input.userId,
223963
+ workspaceId: input.workspaceId
223964
+ });
223965
+ return buildUsageAllowanceSnapshot(input.sessionContext, meter);
223966
+ } catch {
223967
+ return null;
223968
+ }
223969
+ }
223970
+ const viaServer = await fetchUsageAllowanceFromServer(input);
223971
+ if (viaServer) return viaServer;
223972
+ if (input.userId && input.workspaceId) {
223973
+ return buildUsageAllowanceSnapshot(input.sessionContext, null);
223974
+ }
223975
+ return null;
223976
+ }
223977
+ var init_usageAllowance = __esm({
223978
+ "features/perchTerminal/runtime/usage/usageAllowance.ts"() {
223979
+ "use strict";
223980
+ init_usagePersistence();
223981
+ init_perchTokens();
223982
+ }
223983
+ });
223984
+
223472
223985
  // features/perchTerminal/runtime/usage/usageSummary.ts
223473
223986
  function collectUsageRunIds(input) {
223474
223987
  const runIds = /* @__PURE__ */ new Set();
@@ -223515,10 +224028,14 @@ function summarizeInferenceUsageRows(rows, options = {}) {
223515
224028
  return summary;
223516
224029
  }
223517
224030
  function formatUsageSummaryText(summary) {
224031
+ const sections = [];
224032
+ if (summary.allowance) {
224033
+ sections.push(formatUsageAllowanceText(summary.allowance));
224034
+ }
223518
224035
  const lines = [
223519
224036
  "Usage this session",
223520
- `- Calls: ${formatInteger(summary.calls)}`,
223521
- `- Tokens: ${formatInteger(summary.totalTokens)} total (${formatInteger(summary.promptTokens)} input \xB7 ${formatInteger(summary.completionTokens)} output${summary.cachedTokens ? ` \xB7 ${formatInteger(summary.cachedTokens)} cached` : ""})`,
224037
+ `- Calls: ${formatInteger2(summary.calls)}`,
224038
+ `- Tokens: ${formatInteger2(summary.totalTokens)} total (${formatInteger2(summary.promptTokens)} input \xB7 ${formatInteger2(summary.completionTokens)} output${summary.cachedTokens ? ` \xB7 ${formatInteger2(summary.cachedTokens)} cached` : ""})`,
223522
224039
  `- Estimated cost: ${formatUsd(summary.estimatedCostUsd)}`
223523
224040
  ];
223524
224041
  if (summary.calls === 0) {
@@ -223528,11 +224045,12 @@ function formatUsageSummaryText(summary) {
223528
224045
  lines.push("", "Internal detail:");
223529
224046
  for (const row of summary.modelBreakdown.slice(0, 8)) {
223530
224047
  lines.push(
223531
- `- ${row.label}: ${formatInteger(row.totalTokens)} tokens \xB7 ${formatUsd(row.estimatedCostUsd)} \xB7 ${formatInteger(row.calls)} calls`
224048
+ `- ${row.label}: ${formatInteger2(row.totalTokens)} tokens \xB7 ${formatUsd(row.estimatedCostUsd)} \xB7 ${formatInteger2(row.calls)} calls`
223532
224049
  );
223533
224050
  }
223534
224051
  }
223535
- return lines.join("\n");
224052
+ sections.push(lines.join("\n"));
224053
+ return sections.join("\n\n");
223536
224054
  }
223537
224055
  function buildModelBreakdown(rows) {
223538
224056
  const byModel = /* @__PURE__ */ new Map();
@@ -223568,7 +224086,7 @@ function readNumber(value) {
223568
224086
  }
223569
224087
  return 0;
223570
224088
  }
223571
- function formatInteger(value) {
224089
+ function formatInteger2(value) {
223572
224090
  return Math.max(0, Math.round(value)).toLocaleString("en-US");
223573
224091
  }
223574
224092
  function formatUsd(value) {
@@ -223578,6 +224096,7 @@ function formatUsd(value) {
223578
224096
  var init_usageSummary = __esm({
223579
224097
  "features/perchTerminal/runtime/usage/usageSummary.ts"() {
223580
224098
  "use strict";
224099
+ init_usageAllowance();
223581
224100
  }
223582
224101
  });
223583
224102
 
@@ -223610,7 +224129,11 @@ async function runUsageTurn(input, deps) {
223610
224129
  recentMessages: input.recentMessages
223611
224130
  });
223612
224131
  const showModelDetails = canShowPerchDevTools(input.sessionContext.membershipRole);
223613
- const summary = await resolveUsageSummary(input, runIds, showModelDetails);
224132
+ const [summary, allowance] = await Promise.all([
224133
+ resolveUsageSummary(input, runIds, showModelDetails),
224134
+ resolveUsageAllowance(input)
224135
+ ]);
224136
+ summary.allowance = allowance;
223614
224137
  const assistantText = formatUsageSummaryText(summary);
223615
224138
  let assistantMessageId = null;
223616
224139
  if (input.workspaceId) {
@@ -223695,6 +224218,7 @@ var init_runUsageTurn = __esm({
223695
224218
  "features/perchTerminal/runtime/usage/runUsageTurn.ts"() {
223696
224219
  "use strict";
223697
224220
  init_access();
224221
+ init_usageAllowance();
223698
224222
  init_usageSummary();
223699
224223
  }
223700
224224
  });
@@ -225085,10 +225609,7 @@ async function runLiveAgentsLoop(input) {
225085
225609
  const { turn, deps, runId, onEvent } = input;
225086
225610
  try {
225087
225611
  const loopLane = "chat";
225088
- const selectedChatModel = resolveModelForLane(
225089
- turn.founderModelSelection,
225090
- loopLane
225091
- );
225612
+ const selectedChatModel = turn.activeContextModel ?? resolveModelForLane(turn.founderModelSelection, loopLane);
225092
225613
  const context = input.assembledContext;
225093
225614
  if (!context) {
225094
225615
  return {
@@ -230380,6 +230901,7 @@ async function runOperatorTurn(input, deps) {
230380
230901
  });
230381
230902
  try {
230382
230903
  const memoryRetriever = buildTurnMemoryRetriever(input);
230904
+ const inputContextModel = input.activeContextModel ?? null;
230383
230905
  const memoryContext = await buildMemoryContext({
230384
230906
  query: input.trimmedInput,
230385
230907
  userId: input.userId ?? (memoryRetriever && input.cliServerAccessToken?.trim() ? "cli_authenticated_user" : null),
@@ -230387,7 +230909,8 @@ async function runOperatorTurn(input, deps) {
230387
230909
  threadId: input.threadId,
230388
230910
  permanentMemories: input.permanentMemories,
230389
230911
  userMemories: input.userMemories,
230390
- retriever: memoryRetriever
230912
+ retriever: memoryRetriever,
230913
+ contextLimitTokens: inputContextModel ? getEffectiveContextWindow(inputContextModel) : void 0
230391
230914
  });
230392
230915
  const earlySession = input.threadId ? await loadThreadSession(input.threadId, { supabase: input.supabase ?? null }) : null;
230393
230916
  const {
@@ -230403,10 +230926,7 @@ async function runOperatorTurn(input, deps) {
230403
230926
  memoryContext,
230404
230927
  isSystemNotification
230405
230928
  });
230406
- const chatSelectedModelOption = resolveModelForLane(
230407
- enrichedInput.founderModelSelection,
230408
- "chat"
230409
- );
230929
+ const chatSelectedModelOption = enrichedInput.activeContextModel ?? resolveModelForLane(enrichedInput.founderModelSelection, "chat");
230410
230930
  const sources = await prepareTurnSources({ enrichedInput, deps });
230411
230931
  const toolDefinitions = prepareTurnToolDefinitions({
230412
230932
  enrichedInput,
@@ -230545,6 +231065,7 @@ var init_runOperatorTurn = __esm({
230545
231065
  init_liveAgentsLoop();
230546
231066
  init_turnAbort();
230547
231067
  init_modelRegistry();
231068
+ init_contextThresholds();
230548
231069
  init_threadSession();
230549
231070
  init_turnMemoryRetriever();
230550
231071
  init_turnBootstrap();
@@ -233672,7 +234193,8 @@ async function runCliStandaloneOAuthLogin(input) {
233672
234193
  provider,
233673
234194
  options: {
233674
234195
  redirectTo: callback.redirectTo,
233675
- skipBrowserRedirect: true
234196
+ skipBrowserRedirect: true,
234197
+ data: { signup_source: "cli" }
233676
234198
  }
233677
234199
  });
233678
234200
  if (error || !data.url) {
@@ -290330,6 +290852,27 @@ async function runInteractiveSlashCommand(input) {
290330
290852
  input.state.personaId = parsed.value;
290331
290853
  writeModeLine(input.writer, "persona", input.state.personaId);
290332
290854
  return "continue";
290855
+ case "model": {
290856
+ const labelFor = (choice) => choice === "internal" ? "Internal" : ROOST_TIER_LABELS[choice];
290857
+ if (!parsed.value) {
290858
+ writeModeLine(input.writer, "model", labelFor(input.state.roostChoice));
290859
+ return "continue";
290860
+ }
290861
+ input.state.roostChoice = parsed.value;
290862
+ setRoostUserSelectionForNode({ choice: parsed.value, reasoningEnabled: input.state.roostReasoningEnabled });
290863
+ writeModeLine(input.writer, "model", labelFor(parsed.value));
290864
+ return "continue";
290865
+ }
290866
+ case "thinking": {
290867
+ if (parsed.value === void 0) {
290868
+ writeModeLine(input.writer, "thinking", input.state.roostReasoningEnabled ? "on" : "off");
290869
+ return "continue";
290870
+ }
290871
+ input.state.roostReasoningEnabled = parsed.value;
290872
+ setRoostUserSelectionForNode({ choice: input.state.roostChoice, reasoningEnabled: parsed.value });
290873
+ writeModeLine(input.writer, "thinking", input.state.roostReasoningEnabled ? "on" : "off");
290874
+ return "continue";
290875
+ }
290333
290876
  case "appUrl":
290334
290877
  if (!parsed.value) {
290335
290878
  writeModeLine(input.writer, "app-url", input.state.appUrl ?? "(none)");
@@ -290423,6 +290966,22 @@ function parseInteractiveSlashCommand(input) {
290423
290966
  if (!value) return { ok: true, kind: "persona" };
290424
290967
  if (!isPersonaId(value)) return { ok: false, error: `Unknown persona: ${value}` };
290425
290968
  return { ok: true, kind: "persona", value };
290969
+ case "model":
290970
+ if (!value) return { ok: true, kind: "model" };
290971
+ {
290972
+ const normalized = value.toLowerCase().replace(/[\s-]+/g, "_");
290973
+ const choice = normalizeRoostModelChoice(
290974
+ normalized === "standardmax" ? "standard_max" : normalized === "promax" ? "pro_max" : normalized
290975
+ );
290976
+ if (!choice) {
290977
+ return { ok: false, error: `Unknown model: ${value}. Try standard, standard max, pro, pro max, or internal.` };
290978
+ }
290979
+ return { ok: true, kind: "model", value: choice };
290980
+ }
290981
+ case "thinking":
290982
+ if (!value) return { ok: true, kind: "thinking" };
290983
+ if (value !== "on" && value !== "off") return { ok: false, error: "/thinking expects on or off." };
290984
+ return { ok: true, kind: "thinking", value: value === "on" };
290426
290985
  case "app-url":
290427
290986
  case "app":
290428
290987
  return { ok: true, kind: "appUrl", ...value ? { value } : {} };
@@ -290463,6 +291022,8 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
290463
291022
  ["permission", state.permissionMode],
290464
291023
  ["mode", state.chatMode],
290465
291024
  ["persona", state.personaId],
291025
+ ["model", state.roostChoice === "internal" ? "Internal" : ROOST_TIER_LABELS[state.roostChoice]],
291026
+ ["thinking", state.roostReasoningEnabled ? "on" : "off"],
290466
291027
  ["thread", state.threadId],
290467
291028
  ["context", renderCliContextMeter(state.contextSnapshot, activeContextModel)],
290468
291029
  ["saved", state.persistedThreadUpdatedAt ? new Date(state.persistedThreadUpdatedAt).toLocaleString() : "(new thread)"],
@@ -290602,11 +291163,13 @@ function renderInkSpeakerLabel(label) {
290602
291163
  return `${label}\u203A`;
290603
291164
  }
290604
291165
  function createInteractiveCliState(options) {
290605
- return {
291166
+ const state = {
290606
291167
  cwd: resolveExistingDirectory(options.cwd ?? process.cwd()),
290607
291168
  chatMode: options.chatMode ?? "agents",
290608
291169
  personaId: options.personaId ?? "saffron",
290609
291170
  permissionMode: options.permissionMode ?? "default",
291171
+ roostChoice: "internal",
291172
+ roostReasoningEnabled: true,
290610
291173
  threadId: options.threadId?.trim() || "cli-default",
290611
291174
  threadScopeKey: null,
290612
291175
  desktopConnected: options.desktopConnected ?? false,
@@ -290617,6 +291180,11 @@ function createInteractiveCliState(options) {
290617
291180
  contextSnapshot: null,
290618
291181
  persistedThreadUpdatedAt: null
290619
291182
  };
291183
+ setRoostUserSelectionForNode({
291184
+ choice: state.roostChoice,
291185
+ reasoningEnabled: state.roostReasoningEnabled
291186
+ });
291187
+ return state;
290620
291188
  }
290621
291189
  async function hydrateInteractiveCliState(state) {
290622
291190
  const persisted = await hydrateCliThreadState(state.threadId, state.threadScopeKey);
@@ -291675,6 +292243,8 @@ var init_perch_cli = __esm({
291675
292243
  init_perchCore();
291676
292244
  init_cliHost();
291677
292245
  init_modelConnection();
292246
+ init_roost();
292247
+ init_roostUserSelectionClient();
291678
292248
  init_cliAuthSession();
291679
292249
  init_cliStandaloneOAuth();
291680
292250
  init_contextMeterDisplay();
@@ -291750,7 +292320,9 @@ Commands:
291750
292320
  /help Show this list.
291751
292321
  /status Show cwd, auth, mode, persona, permission, and thread.
291752
292322
  /skills [name] List Perch core skills, or show one skill body.
291753
- /usage Show tokens and estimated cost for this session.
292323
+ /usage Show Perch Token allowance and session usage.
292324
+ /model [standard|standard max|pro|pro max|internal] Show or set your model.
292325
+ /thinking [on|off] Show or toggle Thinking for the selected model.
291754
292326
  /cwd [dir] Show or change the working directory.
291755
292327
  /permission [mode] Show or set default, auto_review, take_the_wheel, or plan.
291756
292328
  /permissions [mode] Alias for /permission.
@@ -291812,8 +292384,10 @@ Commands:
291812
292384
  ];
291813
292385
  PERCH_SPLASH_COMMANDS = [
291814
292386
  ["/status", "show auth, route, tools, and thread"],
291815
- ["/usage", "show session tokens and estimated cost"],
292387
+ ["/usage", "show Perch Token allowance and session usage"],
291816
292388
  ["/skills", "show reusable operator skills"],
292389
+ ["/model", "choose Standard, Standard Max, Pro, or Pro Max"],
292390
+ ["/thinking", "toggle model thinking"],
291817
292391
  ["/persona", "swap saffron or quill"],
291818
292392
  ["/permission", "change autonomy for the next turns"],
291819
292393
  ["/login", "connect your Perch account"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.46",
3
+ "version": "2.4.47",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"