@scalequality/cli 0.4.9 → 0.4.10

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "sdkVersion": "0.3.281",
3
- "sha256": "411f9d73e135bdcb7327703ee340f0966eda86ac60193d4a545950a4b1f7071d",
4
- "sourceCommit": "c85fa2362e7a802197cca13e9438f8cd90bda4c0"
5
- }
3
+ "sha256": "c24a69671fea8929ec550bbb8672d703980af3c6389cf97f878cc71083b4972c",
4
+ "sourceCommit": "011350388f19c1ce50250458dd2810efa3113e84"
5
+ }
package/dist/connect.cjs CHANGED
@@ -2010,8 +2010,9 @@ var HttpSessionTransport = class {
2010
2010
  async cancelCommandApproval(approvalId, reason) {
2011
2011
  await this.post(`/command-approvals/${encodeURIComponent(approvalId)}/cancel`, { reason }, this.opts.requestTimeoutMs ?? 3e4);
2012
2012
  }
2013
- async refreshRuntime() {
2014
- const raw = await this.post("/runtime", {}, this.opts.requestTimeoutMs ?? 3e4);
2013
+ /** `current`: the model this engine works with now, which the API keeps for the session when the new one is refused. */
2014
+ async refreshRuntime(current) {
2015
+ const raw = await this.post("/runtime", current?.model ? { current: current.model } : {}, this.opts.requestTimeoutMs ?? 3e4);
2015
2016
  const boot = toSessionBootstrap({ session: { model: raw?.model, reasoning: raw?.reasoning }, runtime: raw?.runtime, repository: null });
2016
2017
  return { model: boot.model, runtime: boot.runtime, reasoning: boot.reasoning ?? null };
2017
2018
  }
@@ -2129,7 +2130,13 @@ function toSessionBootstrap(raw) {
2129
2130
  kind,
2130
2131
  teamId: typeof rawScope?.teamId === "string" ? rawScope.teamId : null,
2131
2132
  projectIds: Array.isArray(rawScope?.projectIds) ? rawScope.projectIds.filter((p) => typeof p === "string") : typeof session.projectId === "string" && session.projectId ? [session.projectId] : [],
2132
- repos: repositories
2133
+ repos: repositories,
2134
+ // An area: its name, the chosen initiative's name and the project that measures it (an older API sends none).
2135
+ ...kind === "BUSINESS_AREA" ? {
2136
+ areaName: text3(rawScope?.areaName) || null,
2137
+ initiativeName: text3(rawScope?.initiativeName) || null,
2138
+ initiativeProjectId: text3(rawScope?.initiativeProjectId) || null
2139
+ } : {}
2133
2140
  },
2134
2141
  branch: text3(session.branch, repo2 ? defaultBranch : ""),
2135
2142
  projectId: text3(session.projectId),
@@ -4911,6 +4918,9 @@ var ENGINE_MESSAGES = {
4911
4918
  MODEL_SWITCH_FAILED: "The model could not be changed now; this request uses {model}.",
4912
4919
  MODEL_SWITCHED: "Now using {model}.",
4913
4920
  GATEWAY_REFUSED: "The AI gateway refused this request ({code}).",
4921
+ // The AI Workspace's own credit (gateway BUDGET codes): said as credit, not as a budget the organization set.
4922
+ MANAGED_ALLOWANCE_EXHAUSTED: "The AI Workspace credit ran out ({code}).",
4923
+ MANAGED_PREPAID_REQUIRED: "This model uses purchased credit, and the purchased credit ran out ({code}).",
4914
4924
  // Files, rewind and hunks.
4915
4925
  DISCARD_FAILED: "The change to that file could not be discarded.",
4916
4926
  DISCARD_REPOSITORY_REQUIRED: "Say which repository the file belongs to.",
@@ -4958,10 +4968,66 @@ function engineMessage(code, params) {
4958
4968
  return text3.replace(/\{(\w+)\}/g, (_m, k) => k === "code" && !(params && k in params) ? code : params && params[k] !== void 0 && params[k] !== null ? String(params[k]) : "");
4959
4969
  }
4960
4970
  var SQ_CODE = /"scalequality_code"\s*:\s*"([A-Z][A-Z0-9_]{2,79})"/;
4971
+ var GATEWAY_CODE_CATEGORIES = Object.freeze({
4972
+ ...Object.fromEntries([
4973
+ "BUDGET_EXHAUSTED",
4974
+ "ORGANIZATION_BUDGET_EXHAUSTED",
4975
+ "ENVIRONMENT_BUDGET_EXHAUSTED",
4976
+ "TEAM_BUDGET_EXHAUSTED",
4977
+ "BUSINESS_UNIT_BUDGET_EXHAUSTED",
4978
+ "AREA_BUDGET_EXHAUSTED",
4979
+ "MODEL_BUDGET_EXHAUSTED",
4980
+ "MANAGED_ALLOWANCE_EXHAUSTED",
4981
+ "MANAGED_PREPAID_REQUIRED"
4982
+ ].map((c) => [c, "BUDGET"])),
4983
+ PERSON_BUDGET_EXHAUSTED: "PERSON_BUDGET",
4984
+ CONCURRENCY_LIMIT: "CONCURRENCY",
4985
+ ORGANIZATION_CONCURRENCY_LIMIT: "CONCURRENCY",
4986
+ ...Object.fromEntries([
4987
+ "INPUT_POLICY_BLOCK",
4988
+ "OUTPUT_POLICY_BLOCK",
4989
+ "SEMANTIC_INPUT_BLOCK",
4990
+ "SEMANTIC_OUTPUT_BLOCK",
4991
+ "TOOL_OUTPUT_POLICY_BLOCK",
4992
+ "TOOL_OUTPUT_NOT_ALLOWED",
4993
+ "TOOL_OUTPUT_INVALID",
4994
+ "PROTECTION_PROMPT_ATTACK",
4995
+ "PROTECTION_INDIRECT_INJECTION",
4996
+ "PROTECTION_HARMFUL_CONTENT",
4997
+ "PROTECTION_PROHIBITED_PRACTICE",
4998
+ "PROTECTION_SENSITIVE_INFERENCE",
4999
+ "PROTECTION_COMPANY_TOPIC",
5000
+ "PROTECTION_UNGROUNDED_ANSWER"
5001
+ ].map((c) => [c, "POLICY"])),
5002
+ ...Object.fromEntries([
5003
+ "MODEL_ALIAS_NOT_ALLOWED",
5004
+ "NO_PUBLISHED_POLICY",
5005
+ "MODEL_DISABLED_BY_ORGANIZATION",
5006
+ "MODEL_EXCLUDED_FROM_AUTO",
5007
+ "MANAGED_MODEL_PLAN_RESTRICTED",
5008
+ "MANAGED_ROUTE_NOT_ALLOWED",
5009
+ "MANAGED_CODE_PROFILE_REQUIRED",
5010
+ "CONNECTION_NOT_AVAILABLE"
5011
+ ].map((c) => [c, "MODEL_ACCESS"])),
5012
+ ...Object.fromEntries([
5013
+ "MANAGED_NOT_AVAILABLE",
5014
+ "WORKSPACE_ENTITLEMENT_REQUIRED",
5015
+ "MANAGED_ENTITLEMENT_REQUIRED",
5016
+ "TRIAL_UPGRADE_REQUIRED",
5017
+ "WORKSPACE_ACTIVE_PEOPLE_LIMIT"
5018
+ ].map((c) => [c, "ENTITLEMENT"])),
5019
+ PROVIDER_RATE_LIMITED: "PROVIDER"
5020
+ });
5021
+ var BARE_CODE = /API Error:\s*(\d{3})\s+([A-Z][A-Z0-9_]{2,79})(?![A-Za-z0-9_])/;
5022
+ function bareGatewayError(text3) {
5023
+ const m = BARE_CODE.exec(text3);
5024
+ const category = m ? GATEWAY_CODE_CATEGORIES[m[2]] : void 0;
5025
+ return m && category ? { code: m[2], params: { status: Number(m[1]), category } } : null;
5026
+ }
4961
5027
  function gatewayError(text3) {
4962
5028
  if (!text3) return null;
4963
5029
  const m = SQ_CODE.exec(text3);
4964
- if (!m) return null;
5030
+ if (!m) return bareGatewayError(text3);
4965
5031
  const params = {};
4966
5032
  const status = /API Error:\s*(\d{3})/.exec(text3);
4967
5033
  if (status) params.status = Number(status[1]);
@@ -9997,10 +10063,15 @@ function layoutLines(root, l) {
9997
10063
  }
9998
10064
  var LISTED = 30;
9999
10065
  function scopeLine(c) {
10000
- const what = c.scope.kind === "ALL" ? "everything the user can access in the organization" : c.scope.kind === "TEAM" ? "the projects of one team" : c.scope.kind === "BUSINESS_AREA" ? "the projects of one business area" : c.scope.projectIds.length === 1 ? `project ${c.scope.projectIds[0]}` : `${c.scope.projectIds.length} projects`;
10066
+ const what = c.scope.kind === "ALL" ? "everything the user can access in the organization" : c.scope.kind === "TEAM" ? "the projects of one team" : c.scope.kind === "BUSINESS_AREA" ? `the projects of the business area${c.scope.areaName ? ` "${c.scope.areaName}"` : ""} (the ones associated with it and the ones that measure its initiatives)` : c.scope.projectIds.length === 1 ? `project ${c.scope.projectIds[0]}` : `${c.scope.projectIds.length} projects`;
10001
10067
  const n = c.scope.repos.length;
10002
10068
  const names = c.scope.repos.slice(0, LISTED).map((r) => `${r.repoFullName} (${r.provider})`).join(", ");
10003
- return `The session scope is ${what}: ${n === 0 ? "no repository" : `${n} repositor${n === 1 ? "y" : "ies"}: ${names}${n > LISTED ? ", ... (call list_repositories for all)" : ""}`}. ScaleQuality tools only act on projects and repositories of this scope.`;
10069
+ return `The session scope is ${what}: ${n === 0 ? "no repository" : `${n} repositor${n === 1 ? "y" : "ies"}: ${names}${n > LISTED ? ", ... (call list_repositories for all)" : ""}`}. ScaleQuality tools only act on projects and repositories of this scope.${initiativeLine(c)}`;
10070
+ }
10071
+ function initiativeLine(c) {
10072
+ if (c.scope.kind !== "BUSINESS_AREA" || !c.scope.initiativeName) return "";
10073
+ const own = c.scope.initiativeProjectId ? c.scope.repos.filter((r) => r.projectId === c.scope.initiativeProjectId).map((r) => r.repoFullName) : [];
10074
+ return ` The user is working on the initiative "${c.scope.initiativeName}"${own.length ? `; its code is ${own.join(", ")} (the project that measures the initiative). Start there when the user talks about the initiative's code.` : "; no project measures this initiative yet."}`;
10004
10075
  }
10005
10076
  function openLine(c) {
10006
10077
  if (!c.open.length) return "No repository is open yet.";
@@ -11242,11 +11313,15 @@ var WorkspaceEngine = class {
11242
11313
  })) : null;
11243
11314
  if (!repos) return;
11244
11315
  const kind = p.kind === "ALL" || p.kind === "TEAM" || p.kind === "BUSINESS_AREA" ? p.kind : "PROJECTS";
11316
+ const named = (v) => typeof v === "string" && v ? v : null;
11245
11317
  this.scope = {
11246
11318
  kind,
11247
11319
  teamId: typeof p.teamId === "string" ? p.teamId : null,
11248
11320
  projectIds: Array.isArray(p.projectIds) ? p.projectIds.filter((x) => typeof x === "string") : [],
11249
- repos
11321
+ repos,
11322
+ areaName: named(p.areaName),
11323
+ initiativeName: named(p.initiativeName),
11324
+ initiativeProjectId: named(p.initiativeProjectId)
11250
11325
  };
11251
11326
  for (const repo2 of this.repos.values()) {
11252
11327
  if (repo2.localId) {
@@ -11575,7 +11650,7 @@ ${String(item.content)}` : String(item.content);
11575
11650
  if (!model || model === boot.model || !this.deps.transport.refreshRuntime) return;
11576
11651
  const step = this.ownStep("model", "tool", `Switching to ${model}`, "STEP_MODEL_SWITCH", { model });
11577
11652
  try {
11578
- const fresh = await this.deps.transport.refreshRuntime();
11653
+ const fresh = await this.deps.transport.refreshRuntime({ model: boot.model });
11579
11654
  if (!fresh.runtime.token || !fresh.runtime.baseUrl) throw new Error("RUNTIME_INCOMPLETE");
11580
11655
  this.redactor.add(fresh.runtime.token);
11581
11656
  boot.runtime = fresh.runtime;
@@ -11632,10 +11707,11 @@ ${String(item.content)}` : String(item.content);
11632
11707
  async renewCredential() {
11633
11708
  if (!this.deps.transport.refreshRuntime) return;
11634
11709
  try {
11635
- const fresh = await this.deps.transport.refreshRuntime();
11710
+ const fresh = await this.deps.transport.refreshRuntime({ model: this.boot.model });
11636
11711
  if (!fresh.runtime.token || !fresh.runtime.baseUrl) throw new Error("RUNTIME_INCOMPLETE");
11637
11712
  this.redactor.add(fresh.runtime.token);
11638
11713
  this.boot.runtime = fresh.runtime;
11714
+ if (fresh.model) this.boot.model = fresh.model;
11639
11715
  this.reasoningCapability = parseReasoningCapability(fresh.runtime.reasoning ?? null);
11640
11716
  this.reasoningLevel = effectiveReasoning(fresh.reasoning ?? this.reasoningLevel, this.reasoningCapability);
11641
11717
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalequality/cli",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "ScaleQuality CLI. Connect your computer to the ScaleQuality AI Workspace (`scalequality login`), run its coding engine in any folder of your computer, and import your Claude Code and Codex conversations.",
5
5
  "type": "module",
6
6
  "bin": {