@coreplane/switchboard 1.262.3 → 1.262.4

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.
@@ -73,6 +73,20 @@ providers:
73
73
  # # object text contract, or both (absent assumes both). The intake gate
74
74
  # # refuses at load a card declaring neither under its classify default.
75
75
  # answers: ["tool", "text"]
76
+ # # When published prices change above an input-token threshold, give
77
+ # # all four base and tier rates in USD per million tokens. The whole
78
+ # # request uses the tier above the threshold, including cached input.
79
+ # price:
80
+ # input: 2
81
+ # output: 10
82
+ # cacheRead: 0.2
83
+ # cacheWrite: 2.5
84
+ # tiers:
85
+ # - inputTokensAbove: 272000
86
+ # input: 4
87
+ # output: 15
88
+ # cacheRead: 0.4
89
+ # cacheWrite: 5
76
90
  # A passthrough is merged into the request body on the wires whose adapter
77
91
  # takes one — a vendor-only feature such as OpenRouter's routing, never a
78
92
  # control: it is not decided, not noted and not in the matrix, and a wrong
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.262.3",
3
+ "version": "1.262.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "switchboard",
9
- "version": "1.262.3",
9
+ "version": "1.262.4",
10
10
  "license": "Apache-2.0",
11
11
  "workspaces": [
12
12
  "web",
@@ -21060,7 +21060,7 @@
21060
21060
  },
21061
21061
  "packages/switchboard": {
21062
21062
  "name": "@coreplane/switchboard",
21063
- "version": "1.262.3",
21063
+ "version": "1.262.4",
21064
21064
  "license": "Apache-2.0",
21065
21065
  "dependencies": {
21066
21066
  "@earendil-works/pi-ai": "0.85.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.262.3",
3
+ "version": "1.262.4",
4
4
  "private": true,
5
5
  "description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
6
6
  "license": "Apache-2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.262.3",
3
- "commit": "a06bf8a434165e30c6ff2bd0575cc5d92021e87a",
4
- "builtAt": "2026-09-25T20:34:57.866Z"
2
+ "version": "1.262.4",
3
+ "commit": "7e76aedb8ad5b5ed4a0a93daa300937a9b7272e8",
4
+ "builtAt": "2026-09-25T21:40:28.832Z"
5
5
  }
@@ -505,8 +505,20 @@ export interface ProviderModelOverride {
505
505
  inputs?: { image?: boolean; document?: boolean };
506
506
  /** The model's cache rule. */
507
507
  cache?: "automatic" | "markers" | "none" | "unknown";
508
- /** USD per million tokens, by kind. */
509
- price?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };
508
+ /** USD per million tokens, by kind, with whole-request long-context rates. */
509
+ price?: {
510
+ input?: number;
511
+ output?: number;
512
+ cacheRead?: number;
513
+ cacheWrite?: number;
514
+ tiers?: Array<{
515
+ inputTokensAbove: number;
516
+ input: number;
517
+ output: number;
518
+ cacheRead: number;
519
+ cacheWrite: number;
520
+ }>;
521
+ };
510
522
  /** The answer shapes the model can produce for a forced one-call turn (the
511
523
  * router's, intake's): `tool` — a forced tool call — and `text` — the
512
524
  * one-JSON-object text contract. Absent, both are assumed; an empty list
package/dist/cli.js CHANGED
@@ -7908,12 +7908,42 @@ function validateModelOverride(path, raw) {
7908
7908
  if (m.price !== void 0) {
7909
7909
  if (typeof m.price !== "object" || m.price === null || Array.isArray(m.price))
7910
7910
  throw new Error(`${path}.price must be a mapping of kind \u2192 USD per million tokens`);
7911
- for (const [kind, v] of Object.entries(m.price)) {
7911
+ const price = m.price;
7912
+ for (const [kind, v] of Object.entries(price)) {
7913
+ if (kind === "tiers") continue;
7912
7914
  if (!Object.hasOwn(PRICE_KINDS2, kind))
7913
7915
  throw new Error(`${path}.price.${kind} is not a known kind (input, output, cacheRead, cacheWrite)`);
7914
7916
  if (typeof v !== "number" || !Number.isFinite(v) || v < 0)
7915
7917
  throw new Error(`${path}.price.${kind} must be a finite number of USD per million tokens, 0 or more`);
7916
7918
  }
7919
+ if (price.tiers !== void 0) {
7920
+ if (!Array.isArray(price.tiers) || price.tiers.length === 0)
7921
+ throw new Error(`${path}.price.tiers must be a nonempty list of long-context rates`);
7922
+ for (const kind of Object.keys(PRICE_KINDS2)) {
7923
+ if (price[kind] === void 0) throw new Error(`${path}.price.${kind} is required with tiers`);
7924
+ }
7925
+ const thresholds = /* @__PURE__ */ new Set();
7926
+ for (const [index, rawTier] of price.tiers.entries()) {
7927
+ const tierPath = `${path}.price.tiers[${index}]`;
7928
+ if (typeof rawTier !== "object" || rawTier === null || Array.isArray(rawTier))
7929
+ throw new Error(`${tierPath} must be a mapping of threshold and rates`);
7930
+ const tier = rawTier;
7931
+ for (const key of Object.keys(tier)) {
7932
+ if (key !== "inputTokensAbove" && !Object.hasOwn(PRICE_KINDS2, key))
7933
+ throw new Error(`${tierPath}.${key} is not a known tier field`);
7934
+ }
7935
+ if (!Number.isInteger(tier.inputTokensAbove) || tier.inputTokensAbove <= 0)
7936
+ throw new Error(`${tierPath}.inputTokensAbove must be a positive integer of tokens`);
7937
+ const threshold = tier.inputTokensAbove;
7938
+ if (thresholds.has(threshold)) throw new Error(`${tierPath}.inputTokensAbove duplicates a previous threshold`);
7939
+ thresholds.add(threshold);
7940
+ for (const kind of Object.keys(PRICE_KINDS2)) {
7941
+ const rate = tier[kind];
7942
+ if (typeof rate !== "number" || !Number.isFinite(rate) || rate < 0)
7943
+ throw new Error(`${tierPath}.${kind} must be a finite number of USD per million tokens, 0 or more`);
7944
+ }
7945
+ }
7946
+ }
7917
7947
  }
7918
7948
  }
7919
7949
  function validateProviders(cfg, source2) {
@@ -40968,6 +40998,8 @@ function ownerNote(owner) {
40968
40998
  const steer = owner.kind === "live" && owner.runId !== void 0 ? ` bind \`steer run ${owner.runId} <words>\` to deliver it,` : "";
40969
40999
  if (owner.kind === "pipeline")
40970
41000
  return `This thread is owned by ${who2}: the request below is a continuation of that durable task. Read tools may ground the decision, but every action folds into the owner so the current pull request is re-read and the pipeline resumes; an informational command or question is not fulfillment.`;
41001
+ if (owner.kind === "unit")
41002
+ return `This thread is owned by ${who2}: the request below is a follow-up for that unit. An inferred read command cannot answer it; fold the whole message into the unit unchanged. Ask a question only when a required detail is missing.`;
40971
41003
  return `This thread is owned by ${who2}: the request below is a follow-up for that owner. To act on it,${steer} bind a read command, or ask a question. Any other decision \u2014 a refusal, a preset, a write \u2014 folds the whole message into the owner unchanged and posts no answer.`;
40972
41004
  }
40973
41005
  function operatorProjection(input) {
@@ -41373,7 +41405,7 @@ function ownedDecisionRuns(event, owner, commands) {
41373
41405
  const def = commands.list().find((c) => c.id === parsed.id);
41374
41406
  if (!def) return false;
41375
41407
  if (def.id === "steer.run") return !(owner.kind === "live" && owner.runId === void 0);
41376
- return boundBlastRadius(def, parsed.input) === "read";
41408
+ return owner.kind !== "unit" && boundBlastRadius(def, parsed.input) === "read";
41377
41409
  });
41378
41410
  }
41379
41411
  function isYesAnswer(text) {
@@ -50154,10 +50186,12 @@ function judgePush(tail, ctx) {
50154
50186
  return refused4("repo:use \u2014 existing-PR publication requires the explicit owned destination");
50155
50187
  if (additionalRefspecs.length > 0)
50156
50188
  return refused4("repo:use \u2014 existing-PR publication allows exactly one owned destination");
50157
- const destination2 = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
50158
- const branch3 = destination2 === "HEAD" ? ctx.branch : destination2.replace(/^refs\/heads\//, "");
50189
+ const [source3, destination2 = source3] = refspec.replace(/^\+/, "").split(":");
50190
+ const branch3 = destination2.replace(/^refs\/heads\//, "");
50159
50191
  if (branch3 !== publication.ref)
50160
50192
  return refused4(`repo:use \u2014 push to \`${branch3}\`, not the owned publication ref ${publication.ref}`);
50193
+ if (source3.replace(/^refs\/heads\//, "") !== publication.ref)
50194
+ return refused4(`repo:use \u2014 push from the owned publication ref ${publication.ref}; the checkout may have moved`);
50161
50195
  const lease = `--force-with-lease=refs/heads/${publication.ref}:${publication.expectedHeadSha}`;
50162
50196
  if (!tail.split(/\s+/).includes(lease))
50163
50197
  return refused4(
@@ -50165,11 +50199,19 @@ function judgePush(tail, ctx) {
50165
50199
  );
50166
50200
  return allowed;
50167
50201
  }
50202
+ if (ctx.branch !== void 0 && refspec === void 0)
50203
+ return refused4(
50204
+ `repo:use \u2014 name the run's branch ${ctx.branch} as the push source and destination; the checkout may have moved`
50205
+ );
50206
+ if (ctx.branch !== void 0 && additionalRefspecs.length > 0)
50207
+ return refused4("repo:use \u2014 a bound run may push exactly one branch");
50168
50208
  if (refspec === void 0) return allowed;
50169
- const destination = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
50209
+ const [source2, destination = source2] = refspec.replace(/^\+/, "").split(":");
50170
50210
  if (ctx.branch !== void 0) {
50171
- const branch3 = destination === "HEAD" ? ctx.branch : destination.replace(/^refs\/heads\//, "");
50211
+ const branch3 = destination.replace(/^refs\/heads\//, "");
50172
50212
  if (branch3 !== ctx.branch) return refused4(`repo:use \u2014 push to \`${branch3}\`, not the run's branch ${ctx.branch}`);
50213
+ if (source2.replace(/^refs\/heads\//, "") !== ctx.branch)
50214
+ return refused4(`repo:use \u2014 push from the run's branch ${ctx.branch}; the checkout may have moved`);
50173
50215
  return allowed;
50174
50216
  }
50175
50217
  const branch2 = destination.replace(/^refs\/heads\//, "");
@@ -52391,7 +52433,7 @@ async function observeCodingWorkspace(executor, opts, span) {
52391
52433
  }
52392
52434
  return {
52393
52435
  observation: {
52394
- head: pushed === void 0 ? headSha : parseRevParseOutput(tipOut),
52436
+ head: pushed === void 0 ? headSha : parseRevParseOutput(tipOut) ?? (checkedOut === pushed ? headSha : void 0),
52395
52437
  branch: branch2,
52396
52438
  checkedOut,
52397
52439
  remoteHead,
@@ -52408,7 +52450,11 @@ async function observeCodingWorkspace(executor, opts, span) {
52408
52450
  return (await probesAt(`git -C ${shellQuote(dir)}`)).observation;
52409
52451
  }
52410
52452
  function salvageTargetOf(opts) {
52411
- const branch2 = opts.pushedBranch ?? opts.checkedOut;
52453
+ if (opts.ownedBranch !== void 0 && opts.checkedOut !== opts.ownedBranch)
52454
+ return {
52455
+ skipped: `the ending checkpoint kept work on \`${opts.checkedOut ?? "an unreadable checkout"}\`: the owned branch is \`${opts.ownedBranch}\``
52456
+ };
52457
+ const branch2 = opts.ownedBranch ?? opts.pushedBranch ?? opts.checkedOut;
52412
52458
  if (branch2 === void 0)
52413
52459
  return {
52414
52460
  skipped: "the budget-end salvage was skipped: the workspace's branch could not be read, so there is nowhere to push"
@@ -53457,8 +53503,9 @@ async function runLoop(deps, ctx) {
53457
53503
  let workspaceObserved = false;
53458
53504
  let workSalvageAttempted = false;
53459
53505
  let endingSalvageAttempted = false;
53506
+ const coordinatorBranch = coordinator?.publication?.publicationRef ?? (coordinator !== void 0 ? binding?.ref ?? repoCtx.ref : void 0);
53460
53507
  const observeWorkspaceNow = async () => {
53461
- const pushedBranch = pushes.branch();
53508
+ const pushedBranch = coordinator !== void 0 ? coordinatorBranch : pushes.branch();
53462
53509
  const observed = await root.span(
53463
53510
  "run.observe_workspace",
53464
53511
  (span) => observeCodingWorkspace(
@@ -53482,9 +53529,10 @@ async function runLoop(deps, ctx) {
53482
53529
  const preserveCodingChildWork = async (cue) => {
53483
53530
  if (workSalvageAttempted || !isCodingPrRun || coordinator === void 0) return;
53484
53531
  if (!workspaceObserved) await observeWorkspaceNow();
53485
- const target = salvageTargetOf({
53532
+ const target = coordinatorBranch === void 0 ? { skipped: "the ending checkpoint was skipped: the unit's branch is unavailable" } : salvageTargetOf({
53486
53533
  pushedBranch: pushes.branch(),
53487
53534
  checkedOut: observedCheckedOut,
53535
+ ownedBranch: coordinatorBranch,
53488
53536
  base: repoCtx.baseRef ?? await coordinatorBase
53489
53537
  });
53490
53538
  const salvaged = "skipped" in target ? { pushed: false, summary: target.skipped } : await root.span(
@@ -53504,7 +53552,7 @@ async function runLoop(deps, ctx) {
53504
53552
  const cleanCompletion = cue === "completion" && !salvaged.pushed && (/ended with nothing to preserve/i.test(salvaged.summary) || "skipped" in target && (observedUncommitted ?? 0) === 0 && (observedUnpushed ?? 0) === 0);
53505
53553
  if (cleanCompletion) return;
53506
53554
  workSalvageAttempted = true;
53507
- if (cue === "ending" || cue === "completion") endingSalvageAttempted = true;
53555
+ if (cue === "ending" || cue === "completion" && !("skipped" in target)) endingSalvageAttempted = true;
53508
53556
  onEvent({
53509
53557
  type: "run_note",
53510
53558
  kind: cue === "budget" ? "budget_salvage" : "work_salvage",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreplane/switchboard",
3
- "version": "1.262.3",
3
+ "version": "1.262.4",
4
4
  "description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://openswitchboard.dev",