@coreplane/switchboard 1.216.1 → 1.216.2

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,12 +1,12 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.216.1",
3
+ "version": "1.216.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "switchboard",
9
- "version": "1.216.1",
9
+ "version": "1.216.2",
10
10
  "license": "Apache-2.0",
11
11
  "workspaces": [
12
12
  "web",
@@ -19000,7 +19000,7 @@
19000
19000
  },
19001
19001
  "packages/switchboard": {
19002
19002
  "name": "@coreplane/switchboard",
19003
- "version": "1.216.1",
19003
+ "version": "1.216.2",
19004
19004
  "license": "Apache-2.0",
19005
19005
  "dependencies": {
19006
19006
  "@anthropic-ai/sdk": "^0.124.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.216.1",
3
+ "version": "1.216.2",
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.216.1",
3
- "commit": "2982fdfa088bb070355f48b8be547bcab2d23447",
4
- "builtAt": "2026-09-14T22:51:50.515Z"
2
+ "version": "1.216.2",
3
+ "commit": "cfbdd1fd6ecd2d4db2b0ad5cba35d39dc072fefc",
4
+ "builtAt": "2026-09-14T23:25:52.601Z"
5
5
  }
@@ -88,6 +88,19 @@ export interface LiveRunMeta {
88
88
  * attachments when they fit — so a reclaim of an `attaching` row can
89
89
  * dispatch it again under the same run id and card. */
90
90
  request?: Record<string, unknown>;
91
+ /** The router's decision when it chose the run's preset (routing-and-config
92
+ * item 21) — the same fields the record's `route` event carries. On the row
93
+ * so a resume repaints the card as it was (` · routed: <reason>`, the
94
+ * parts, the override footer on the close) and a reclaim closing the run
95
+ * knows it was routed without reading the events. Absent for a preset a
96
+ * person, a scope or the default chose. */
97
+ route?: {
98
+ preset: string;
99
+ reason: string;
100
+ model: string;
101
+ parts?: { preset: string; text: string }[];
102
+ collapsed?: { presets: string[] };
103
+ };
91
104
  }
92
105
 
93
106
  /** Dispatcher-local run state a resume must restore (the `submit_*` callbacks,
package/dist/cli.js CHANGED
@@ -9290,6 +9290,7 @@ var init_resident = __esm({
9290
9290
  ref: data.ref,
9291
9291
  sha: data.sha,
9292
9292
  ...typeof data.workspace === "string" && data.workspace ? { workspace: data.workspace } : {},
9293
+ ...typeof data.user === "string" && data.user ? { user: data.user } : {},
9293
9294
  ...trace.length > 0 ? { trace } : {},
9294
9295
  ...typeof data.attachMs === "number" && Number.isFinite(data.attachMs) ? { attachMs: data.attachMs } : {}
9295
9296
  };
@@ -24916,9 +24917,24 @@ function providerRouteModel(provider, model, opts = {}) {
24916
24917
  return result.content.filter((p) => p.type === "text").map((p) => p.text).join("");
24917
24918
  };
24918
24919
  }
24920
+ function resolveRouted(deps, msg, directives, sticky, preset) {
24921
+ return deps.config.resolve({
24922
+ channelId: msg.channelId,
24923
+ userId: msg.userId,
24924
+ request: {
24925
+ agent: preset,
24926
+ model: directives.model ?? sticky.model,
24927
+ effort: directives.effort ?? sticky.effort
24928
+ }
24929
+ });
24930
+ }
24919
24931
  async function routeRequest(deps, ctx) {
24920
- const { msg, directives, sticky, agentSource, threadLive, root } = ctx;
24932
+ const { msg, directives, sticky, agentSource, threadLive, root, carried } = ctx;
24921
24933
  const cfg = deps.config.config;
24934
+ if (carried) {
24935
+ console.log(`[route] ${msg.threadKey} routed to ${carried.preset} as before the restart: ${carried.reason}`);
24936
+ return { kind: "routed", resolved: resolveRouted(deps, msg, directives, sticky, carried.preset), route: carried };
24937
+ }
24922
24938
  if (!routingOn(cfg) || agentSource !== "default" || threadLive) return { kind: "unrouted" };
24923
24939
  const modelRef2 = cfg.routing?.model ?? cfg.defaults.models["general"];
24924
24940
  if (!modelRef2) {
@@ -24959,15 +24975,7 @@ async function routeRequest(deps, ctx) {
24959
24975
  console.log(`[route] ${msg.threadKey} not routed (${decision.reason}) \u2014 running ${cfg.defaults.agent}`);
24960
24976
  return decision.compoundRejected ? { kind: "unrouted", rejected: { preset: cfg.defaults.agent, reason: decision.reason, model: modelRef2 } } : { kind: "unrouted" };
24961
24977
  }
24962
- const resolved = deps.config.resolve({
24963
- channelId: msg.channelId,
24964
- userId: msg.userId,
24965
- request: {
24966
- agent: decision.preset,
24967
- model: directives.model ?? sticky.model,
24968
- effort: directives.effort ?? sticky.effort
24969
- }
24970
- });
24978
+ const resolved = resolveRouted(deps, msg, directives, sticky, decision.preset);
24971
24979
  const { parts, collapsed } = decision;
24972
24980
  console.log(
24973
24981
  `[route] ${msg.threadKey} routed to ${decision.preset} on ${modelRef2}${parts ? ` (${parts.length} parts)` : ""}${collapsed ? ` (compound collapsed: ${collapsed.presets.join("+")})` : ""}: ${decision.reason}`
@@ -29213,7 +29221,8 @@ async function reserveRun(deps, ctx) {
29213
29221
  root,
29214
29222
  parentRunId,
29215
29223
  coordinator,
29216
- seed
29224
+ seed,
29225
+ route: route3
29217
29226
  } = ctx;
29218
29227
  if (!resume && !restart) {
29219
29228
  const requestRow = durableInboxMessage(msg, msg.text, receivedAt);
@@ -29242,6 +29251,7 @@ async function reserveRun(deps, ctx) {
29242
29251
  ...parentRunId !== void 0 ? { parentRunId } : {},
29243
29252
  ...coordinatorFields(coordinator),
29244
29253
  ...seed !== void 0 ? { seed } : {},
29254
+ ...route3 !== void 0 ? { route: route3 } : {},
29245
29255
  request: requestRow
29246
29256
  },
29247
29257
  card: card.handle ?? null,
@@ -29472,7 +29482,8 @@ async function claimRun(deps, ctx) {
29472
29482
  root,
29473
29483
  parentRunId,
29474
29484
  coordinator,
29475
- seed
29485
+ seed,
29486
+ route: route3
29476
29487
  } = ctx;
29477
29488
  const { resident, binding } = selection;
29478
29489
  let ledgerRun = ctx.ledgerRun;
@@ -29503,6 +29514,7 @@ async function claimRun(deps, ctx) {
29503
29514
  ...parentRunId !== void 0 ? { parentRunId } : {},
29504
29515
  ...coordinatorFields(coordinator),
29505
29516
  ...seed !== void 0 ? { seed } : {},
29517
+ ...route3 !== void 0 ? { route: route3 } : {},
29506
29518
  selection: resident === true ? "resident" : "sandbox",
29507
29519
  ...binding?.workspace !== void 0 ? { workspace: binding.workspace } : {},
29508
29520
  ...requestRow !== void 0 ? { request: requestRow } : {}
@@ -30370,7 +30382,8 @@ function piBuiltinToolsFor(identity2) {
30370
30382
  function piThinkingLevel(effort2) {
30371
30383
  return effort2;
30372
30384
  }
30373
- function piRunPaths(runId3, root = "/tmp/switchboard-pi") {
30385
+ function piRunPaths(runId3, user) {
30386
+ const root = user ? `/tmp/switchboard-pi-${user}` : "/tmp/switchboard-pi";
30374
30387
  const dir = `${root}/${runId3}`;
30375
30388
  return {
30376
30389
  dir,
@@ -30618,7 +30631,7 @@ async function runPiHarness(deps, run2) {
30618
30631
  emit({ type: "run_note", kind, summary, ...mode ? { mode } : {} });
30619
30632
  };
30620
30633
  const bridge = new PiBridge({ emit, onProgress: run2.onProgress, agentSpan, clock: clock3 });
30621
- const paths = piRunPaths(run2.runId, deps.root);
30634
+ const paths = piRunPaths(run2.runId, run2.user);
30622
30635
  const remainingMs = run2.resume?.remainingMs ?? run2.agent.maxMinutes * 6e4;
30623
30636
  const deadline = now() + remainingMs;
30624
30637
  const warnAt = deadline - Math.min(3 * 6e4, run2.agent.maxMinutes * 15e3);
@@ -31638,6 +31651,10 @@ async function runLoop2(deps, ctx) {
31638
31651
  ...ownBranch !== void 0 ? { branch: ownBranch } : {},
31639
31652
  protectedBranches
31640
31653
  },
31654
+ // The resident runs this thread's commands as its pool user: pi's
31655
+ // files go under that user's own root, never one another thread's
31656
+ // user created (harness-pi.md item 4).
31657
+ ...binding?.user ? { user: binding.user } : {},
31641
31658
  ...round2.selection.backend ? { backend: round2.selection.backend } : {},
31642
31659
  span: root,
31643
31660
  control: run2.control,
@@ -33527,13 +33544,24 @@ async function dispatch(deps, msg, io, opts = {}) {
33527
33544
  let { resolved, agentSource } = settled;
33528
33545
  let route3;
33529
33546
  const threadLive = admission.get(msg.threadKey) !== void 0 || !resume && !restart && deps.threadsElsewhere.get(msg.threadKey) !== void 0;
33530
- const routing = await routeRequest(deps, { msg, directives, sticky, agentSource, threadLive, root });
33547
+ const routing = await routeRequest(deps, {
33548
+ msg,
33549
+ directives,
33550
+ sticky,
33551
+ agentSource,
33552
+ threadLive,
33553
+ root,
33554
+ // A restart is the same run under the same card: the row's decision
33555
+ // (run-history item 35) is the route, re-resolved, never re-asked.
33556
+ ...restart?.row.meta.route ? { carried: restart.row.meta.route } : {}
33557
+ });
33531
33558
  if (routing.kind === "routed") {
33532
33559
  resolved = routing.resolved;
33533
33560
  route3 = routing.route;
33534
33561
  agentSource = "route";
33535
33562
  }
33536
33563
  const routeEvent = routing.kind === "routed" ? routing.route : routing.rejected;
33564
+ if (resume?.row.meta.route) route3 = resume.row.meta.route;
33537
33565
  if ((await authorizeAgent(deps, { msg, io, refuse: refuse3, agentName: resolved.agentName })).kind === "refused")
33538
33566
  return ended;
33539
33567
  const agent = resolved.agentName === "ship" ? shipPresetFor(deps.config.config.ship) : getAgent(resolved.agentName);
@@ -33717,6 +33745,7 @@ async function dispatch(deps, msg, io, opts = {}) {
33717
33745
  restart,
33718
33746
  card,
33719
33747
  hooks: reservationHooks,
33748
+ route: route3,
33720
33749
  admitted,
33721
33750
  root,
33722
33751
  parentRunId,
@@ -33852,6 +33881,7 @@ async function dispatch(deps, msg, io, opts = {}) {
33852
33881
  resume,
33853
33882
  ledgerRun,
33854
33883
  card,
33884
+ route: route3,
33855
33885
  clock: clock3,
33856
33886
  root,
33857
33887
  parentRunId,
@@ -39724,8 +39754,8 @@ function prUrlOf(events) {
39724
39754
  for (const e of events) if (e.type === "pr_opened" && typeof e.url === "string") url = e.url;
39725
39755
  return url;
39726
39756
  }
39727
- function routedOf(events) {
39728
- return events.some((e) => e.type === "run_meta" && e.agentSource === "route");
39757
+ function routedOf(meta, events) {
39758
+ return meta.route !== void 0 || events.some((e) => e.type === "run_meta" && e.agentSource === "route");
39729
39759
  }
39730
39760
  async function reclaimRuns(opts) {
39731
39761
  const { ledger, gen } = opts;
@@ -39801,7 +39831,7 @@ async function reclaimRuns(opts) {
39801
39831
  events: events.length,
39802
39832
  ...row.meta.agent !== void 0 ? { agent: row.meta.agent } : {},
39803
39833
  ...prUrl2 !== void 0 ? { prUrl: prUrl2 } : {},
39804
- ...routedOf(events) ? { routed: true } : {},
39834
+ ...routedOf(row.meta, events) ? { routed: true } : {},
39805
39835
  ...status3 === "interrupted" ? { note: closureNote(row.meta.agent, prUrl2) } : {}
39806
39836
  });
39807
39837
  log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreplane/switchboard",
3
- "version": "1.216.1",
3
+ "version": "1.216.2",
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",