@coreplane/switchboard 1.216.0 → 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.
@@ -439,7 +439,10 @@ export default {
439
439
  : pathname === COPY_PATH
440
440
  ? // The artifact copy (artifactsCopy.ts): the R2 binding and the Slack
441
441
  // token are this Worker's; the bot only asks, with its copy bearer.
442
- await handleArtifactsCopy(inbound, {
442
+ // `forwarded`, never `inbound`: `withTraceContext` rebuilt the request
443
+ // with `new Request(inbound, …)`, which takes the body stream with it —
444
+ // `inbound.text()` is empty afterwards and the route read "not JSON" live.
445
+ await handleArtifactsCopy(forwarded, {
443
446
  bucket: env.ARTIFACTS,
444
447
  bucketName: env.ARTIFACTS_BUCKET_NAME,
445
448
  copyToken: env.ARTIFACTS_COPY_TOKEN,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.216.0",
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.0",
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.0",
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.0",
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.0",
3
- "commit": "1c13717b8a2b0c59b740fd75b7d3e7304657ddb2",
4
- "builtAt": "2026-09-14T22:25:28.373Z"
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
  };
@@ -24312,6 +24313,73 @@ var init_resolve = __esm({
24312
24313
  }
24313
24314
  });
24314
24315
 
24316
+ // ../../src/core/statusCardFrame.ts
24317
+ function createCardShell(opts) {
24318
+ let label = opts.label;
24319
+ let link = opts.link;
24320
+ let frame = 0;
24321
+ let finishedAt;
24322
+ let setupLabel;
24323
+ const lead = opts.lead ?? [];
24324
+ const elapsed2 = () => formatDuration((finishedAt ?? opts.now()) - opts.startedAt, "clock");
24325
+ const headline = (icon) => `${icon} ${label} \xB7 ${elapsed2()}`;
24326
+ const closeDetail = (close, own) => [close.shape, close.queued, own, opts.footer].filter(Boolean).join("\n") || void 0;
24327
+ return {
24328
+ get label() {
24329
+ return label;
24330
+ },
24331
+ setLabel(next) {
24332
+ label = next;
24333
+ },
24334
+ setLink(next) {
24335
+ link = next;
24336
+ },
24337
+ freeze(at) {
24338
+ finishedAt = at;
24339
+ },
24340
+ setSetupLabel(next) {
24341
+ setupLabel = next;
24342
+ },
24343
+ ack() {
24344
+ const detail = lead.join("\n");
24345
+ return { title: `\u{1F440} ${label} \xB7 preparing workspace\u2026`, ...detail ? { detail } : {} };
24346
+ },
24347
+ live(parts = {}) {
24348
+ const glyph = SPINNER_GLYPHS[frame++ % SPINNER_GLYPHS.length];
24349
+ const detail = [...lead, ...parts.detail ?? []].filter(Boolean).join("\n");
24350
+ return {
24351
+ title: headline(glyph) + (setupLabel ? ` \u2014 ${setupLabel}` : "") + (parts.suffix ?? "") + (parts.notice ? ` \xB7 ${parts.notice}` : ""),
24352
+ detail: detail || void 0,
24353
+ link
24354
+ };
24355
+ },
24356
+ close(close) {
24357
+ const detail = closeDetail(close, close.kind === "done" ? close.detail : void 0);
24358
+ switch (close.kind) {
24359
+ case "done":
24360
+ return { title: headline(close.icon), detail, link };
24361
+ case "not_started":
24362
+ return { title: `${close.icon} ${label} \xB7 not started (${close.reason}) \xB7 ${elapsed2()}`, detail };
24363
+ case "refused":
24364
+ return { title: `${close.icon} ${label} \xB7 ${close.reason} \xB7 ${elapsed2()}`, detail };
24365
+ case "setup_failed":
24366
+ return { title: `\u274C setup failed \xB7 ${close.reason} \xB7 ${elapsed2()}`, detail };
24367
+ }
24368
+ }
24369
+ };
24370
+ }
24371
+ var SPINNER_GLYPHS, LIVE_CARD_PREFIXES, ROUTED_LABEL_PREFIX, ROUTED_CARD_FOOTER;
24372
+ var init_statusCardFrame = __esm({
24373
+ "../../src/core/statusCardFrame.ts"() {
24374
+ "use strict";
24375
+ init_formatDuration();
24376
+ SPINNER_GLYPHS = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
24377
+ LIVE_CARD_PREFIXES = [...SPINNER_GLYPHS, "\u{1F440}"];
24378
+ ROUTED_LABEL_PREFIX = "routed:";
24379
+ ROUTED_CARD_FOOTER = "reply agent:<preset> to run it another way";
24380
+ }
24381
+ });
24382
+
24315
24383
  // ../../src/core/dispatch/awaitChildren.ts
24316
24384
  function decideWait(inputs) {
24317
24385
  const { children, now, budgetEndsAt, timeoutAt, stop, followUpPending } = inputs;
@@ -24849,9 +24917,24 @@ function providerRouteModel(provider, model, opts = {}) {
24849
24917
  return result.content.filter((p) => p.type === "text").map((p) => p.text).join("");
24850
24918
  };
24851
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
+ }
24852
24931
  async function routeRequest(deps, ctx) {
24853
- const { msg, directives, sticky, agentSource, threadLive, root } = ctx;
24932
+ const { msg, directives, sticky, agentSource, threadLive, root, carried } = ctx;
24854
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
+ }
24855
24938
  if (!routingOn(cfg) || agentSource !== "default" || threadLive) return { kind: "unrouted" };
24856
24939
  const modelRef2 = cfg.routing?.model ?? cfg.defaults.models["general"];
24857
24940
  if (!modelRef2) {
@@ -24892,15 +24975,7 @@ async function routeRequest(deps, ctx) {
24892
24975
  console.log(`[route] ${msg.threadKey} not routed (${decision.reason}) \u2014 running ${cfg.defaults.agent}`);
24893
24976
  return decision.compoundRejected ? { kind: "unrouted", rejected: { preset: cfg.defaults.agent, reason: decision.reason, model: modelRef2 } } : { kind: "unrouted" };
24894
24977
  }
24895
- const resolved = deps.config.resolve({
24896
- channelId: msg.channelId,
24897
- userId: msg.userId,
24898
- request: {
24899
- agent: decision.preset,
24900
- model: directives.model ?? sticky.model,
24901
- effort: directives.effort ?? sticky.effort
24902
- }
24903
- });
24978
+ const resolved = resolveRouted(deps, msg, directives, sticky, decision.preset);
24904
24979
  const { parts, collapsed } = decision;
24905
24980
  console.log(
24906
24981
  `[route] ${msg.threadKey} routed to ${decision.preset} on ${modelRef2}${parts ? ` (${parts.length} parts)` : ""}${collapsed ? ` (compound collapsed: ${collapsed.presets.join("+")})` : ""}: ${decision.reason}`
@@ -24918,7 +24993,7 @@ async function routeRequest(deps, ctx) {
24918
24993
  };
24919
24994
  }
24920
24995
  function routedLabel(reason, collapsed) {
24921
- return `routed: ${reason}${collapsed ? ` (compound collapsed: ${collapsed.presets.join("+")})` : ""}`;
24996
+ return `${ROUTED_LABEL_PREFIX} ${reason}${collapsed ? ` (compound collapsed: ${collapsed.presets.join("+")})` : ""}`;
24922
24997
  }
24923
24998
  function routedPartLines(parts) {
24924
24999
  return parts.map((p) => {
@@ -24934,7 +25009,7 @@ function compoundBrief(text, parts) {
24934
25009
  ...parts.map((p, i) => `${i + 1}. \`${p.preset}\`: ${p.text}`)
24935
25010
  ].join("\n");
24936
25011
  }
24937
- var ROUTE_TEXT_CAP, ROUTE_REASON_CAP, ROUTE_MIN_OUTPUT_TOKENS, ROUTE_TOOL_NAME, ROUTE_TIMEOUT_MS, ROUTE_PART_TEXT_CAP, ROUTE_PART_LINE_CAP, COMPOUND_BRIEF_HEADING, identityOf, ROUTED_CARD_FOOTER;
25012
+ var ROUTE_TEXT_CAP, ROUTE_REASON_CAP, ROUTE_MIN_OUTPUT_TOKENS, ROUTE_TOOL_NAME, ROUTE_TIMEOUT_MS, ROUTE_PART_TEXT_CAP, ROUTE_PART_LINE_CAP, COMPOUND_BRIEF_HEADING, identityOf;
24938
25013
  var init_route = __esm({
24939
25014
  "../../src/core/dispatch/route.ts"() {
24940
25015
  "use strict";
@@ -24942,7 +25017,9 @@ var init_route = __esm({
24942
25017
  init_config2();
24943
25018
  init_types3();
24944
25019
  init_redact();
25020
+ init_statusCardFrame();
24945
25021
  init_spawn();
25022
+ init_statusCardFrame();
24946
25023
  ROUTE_TEXT_CAP = 2e3;
24947
25024
  ROUTE_REASON_CAP = 120;
24948
25025
  ROUTE_MIN_OUTPUT_TOKENS = 200;
@@ -24952,7 +25029,6 @@ var init_route = __esm({
24952
25029
  ROUTE_PART_LINE_CAP = 100;
24953
25030
  COMPOUND_BRIEF_HEADING = "Routed as a compound request";
24954
25031
  identityOf = (preset) => AGENTS[preset]?.identity;
24955
- ROUTED_CARD_FOOTER = "reply agent:<preset> to run it another way";
24956
25032
  }
24957
25033
  });
24958
25034
 
@@ -28923,71 +28999,6 @@ var init_customInstructions = __esm({
28923
28999
  }
28924
29000
  });
28925
29001
 
28926
- // ../../src/core/statusCardFrame.ts
28927
- function createCardShell(opts) {
28928
- let label = opts.label;
28929
- let link = opts.link;
28930
- let frame = 0;
28931
- let finishedAt;
28932
- let setupLabel;
28933
- const lead = opts.lead ?? [];
28934
- const elapsed2 = () => formatDuration((finishedAt ?? opts.now()) - opts.startedAt, "clock");
28935
- const headline = (icon) => `${icon} ${label} \xB7 ${elapsed2()}`;
28936
- const closeDetail = (close, own) => [close.shape, close.queued, own, opts.footer].filter(Boolean).join("\n") || void 0;
28937
- return {
28938
- get label() {
28939
- return label;
28940
- },
28941
- setLabel(next) {
28942
- label = next;
28943
- },
28944
- setLink(next) {
28945
- link = next;
28946
- },
28947
- freeze(at) {
28948
- finishedAt = at;
28949
- },
28950
- setSetupLabel(next) {
28951
- setupLabel = next;
28952
- },
28953
- ack() {
28954
- const detail = lead.join("\n");
28955
- return { title: `\u{1F440} ${label} \xB7 preparing workspace\u2026`, ...detail ? { detail } : {} };
28956
- },
28957
- live(parts = {}) {
28958
- const glyph = SPINNER_GLYPHS[frame++ % SPINNER_GLYPHS.length];
28959
- const detail = [...lead, ...parts.detail ?? []].filter(Boolean).join("\n");
28960
- return {
28961
- title: headline(glyph) + (setupLabel ? ` \u2014 ${setupLabel}` : "") + (parts.suffix ?? "") + (parts.notice ? ` \xB7 ${parts.notice}` : ""),
28962
- detail: detail || void 0,
28963
- link
28964
- };
28965
- },
28966
- close(close) {
28967
- const detail = closeDetail(close, close.kind === "done" ? close.detail : void 0);
28968
- switch (close.kind) {
28969
- case "done":
28970
- return { title: headline(close.icon), detail, link };
28971
- case "not_started":
28972
- return { title: `${close.icon} ${label} \xB7 not started (${close.reason}) \xB7 ${elapsed2()}`, detail };
28973
- case "refused":
28974
- return { title: `${close.icon} ${label} \xB7 ${close.reason} \xB7 ${elapsed2()}`, detail };
28975
- case "setup_failed":
28976
- return { title: `\u274C setup failed \xB7 ${close.reason} \xB7 ${elapsed2()}`, detail };
28977
- }
28978
- }
28979
- };
28980
- }
28981
- var SPINNER_GLYPHS, LIVE_CARD_PREFIXES;
28982
- var init_statusCardFrame = __esm({
28983
- "../../src/core/statusCardFrame.ts"() {
28984
- "use strict";
28985
- init_formatDuration();
28986
- SPINNER_GLYPHS = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
28987
- LIVE_CARD_PREFIXES = [...SPINNER_GLYPHS, "\u{1F440}"];
28988
- }
28989
- });
28990
-
28991
29002
  // ../../src/core/statusCoalescer.ts
28992
29003
  function coalesceStatus(inner, minIntervalMs, now = Date.now, schedule = setTimeout) {
28993
29004
  let lastSentAt = -Infinity;
@@ -29210,7 +29221,8 @@ async function reserveRun(deps, ctx) {
29210
29221
  root,
29211
29222
  parentRunId,
29212
29223
  coordinator,
29213
- seed
29224
+ seed,
29225
+ route: route3
29214
29226
  } = ctx;
29215
29227
  if (!resume && !restart) {
29216
29228
  const requestRow = durableInboxMessage(msg, msg.text, receivedAt);
@@ -29239,6 +29251,7 @@ async function reserveRun(deps, ctx) {
29239
29251
  ...parentRunId !== void 0 ? { parentRunId } : {},
29240
29252
  ...coordinatorFields(coordinator),
29241
29253
  ...seed !== void 0 ? { seed } : {},
29254
+ ...route3 !== void 0 ? { route: route3 } : {},
29242
29255
  request: requestRow
29243
29256
  },
29244
29257
  card: card.handle ?? null,
@@ -29469,7 +29482,8 @@ async function claimRun(deps, ctx) {
29469
29482
  root,
29470
29483
  parentRunId,
29471
29484
  coordinator,
29472
- seed
29485
+ seed,
29486
+ route: route3
29473
29487
  } = ctx;
29474
29488
  const { resident, binding } = selection;
29475
29489
  let ledgerRun = ctx.ledgerRun;
@@ -29500,6 +29514,7 @@ async function claimRun(deps, ctx) {
29500
29514
  ...parentRunId !== void 0 ? { parentRunId } : {},
29501
29515
  ...coordinatorFields(coordinator),
29502
29516
  ...seed !== void 0 ? { seed } : {},
29517
+ ...route3 !== void 0 ? { route: route3 } : {},
29503
29518
  selection: resident === true ? "resident" : "sandbox",
29504
29519
  ...binding?.workspace !== void 0 ? { workspace: binding.workspace } : {},
29505
29520
  ...requestRow !== void 0 ? { request: requestRow } : {}
@@ -30367,7 +30382,8 @@ function piBuiltinToolsFor(identity2) {
30367
30382
  function piThinkingLevel(effort2) {
30368
30383
  return effort2;
30369
30384
  }
30370
- function piRunPaths(runId3, root = "/tmp/switchboard-pi") {
30385
+ function piRunPaths(runId3, user) {
30386
+ const root = user ? `/tmp/switchboard-pi-${user}` : "/tmp/switchboard-pi";
30371
30387
  const dir = `${root}/${runId3}`;
30372
30388
  return {
30373
30389
  dir,
@@ -30615,7 +30631,7 @@ async function runPiHarness(deps, run2) {
30615
30631
  emit({ type: "run_note", kind, summary, ...mode ? { mode } : {} });
30616
30632
  };
30617
30633
  const bridge = new PiBridge({ emit, onProgress: run2.onProgress, agentSpan, clock: clock3 });
30618
- const paths = piRunPaths(run2.runId, deps.root);
30634
+ const paths = piRunPaths(run2.runId, run2.user);
30619
30635
  const remainingMs = run2.resume?.remainingMs ?? run2.agent.maxMinutes * 6e4;
30620
30636
  const deadline = now() + remainingMs;
30621
30637
  const warnAt = deadline - Math.min(3 * 6e4, run2.agent.maxMinutes * 15e3);
@@ -31635,6 +31651,10 @@ async function runLoop2(deps, ctx) {
31635
31651
  ...ownBranch !== void 0 ? { branch: ownBranch } : {},
31636
31652
  protectedBranches
31637
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 } : {},
31638
31658
  ...round2.selection.backend ? { backend: round2.selection.backend } : {},
31639
31659
  span: root,
31640
31660
  control: run2.control,
@@ -33524,13 +33544,24 @@ async function dispatch(deps, msg, io, opts = {}) {
33524
33544
  let { resolved, agentSource } = settled;
33525
33545
  let route3;
33526
33546
  const threadLive = admission.get(msg.threadKey) !== void 0 || !resume && !restart && deps.threadsElsewhere.get(msg.threadKey) !== void 0;
33527
- 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
+ });
33528
33558
  if (routing.kind === "routed") {
33529
33559
  resolved = routing.resolved;
33530
33560
  route3 = routing.route;
33531
33561
  agentSource = "route";
33532
33562
  }
33533
33563
  const routeEvent = routing.kind === "routed" ? routing.route : routing.rejected;
33564
+ if (resume?.row.meta.route) route3 = resume.row.meta.route;
33534
33565
  if ((await authorizeAgent(deps, { msg, io, refuse: refuse3, agentName: resolved.agentName })).kind === "refused")
33535
33566
  return ended;
33536
33567
  const agent = resolved.agentName === "ship" ? shipPresetFor(deps.config.config.ship) : getAgent(resolved.agentName);
@@ -33714,6 +33745,7 @@ async function dispatch(deps, msg, io, opts = {}) {
33714
33745
  restart,
33715
33746
  card,
33716
33747
  hooks: reservationHooks,
33748
+ route: route3,
33717
33749
  admitted,
33718
33750
  root,
33719
33751
  parentRunId,
@@ -33849,6 +33881,7 @@ async function dispatch(deps, msg, io, opts = {}) {
33849
33881
  resume,
33850
33882
  ledgerRun,
33851
33883
  card,
33884
+ route: route3,
33852
33885
  clock: clock3,
33853
33886
  root,
33854
33887
  parentRunId,
@@ -35969,9 +36002,12 @@ function findOrphanedCards(input) {
35969
36002
  }
35970
36003
  function interruptedCardFrame(cardText) {
35971
36004
  const label = cardText.replace(LIVE_GLYPH_PREFIX, "").replace(SHUTDOWN_NOTICE_SUFFIX, "").replace(ACTIVITY_SUFFIX, "").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&").trim();
36005
+ const detail = "The bot restarted (a deploy) while this run was in flight, so the run was lost and this card stopped updating. Re-send your request to run it again.";
36006
+ const routed = label.includes(` \xB7 ${ROUTED_LABEL_PREFIX}`);
35972
36007
  return {
35973
36008
  title: `\u274C interrupted \xB7 ${label}`,
35974
- detail: "The bot restarted (a deploy) while this run was in flight, so the run was lost and this card stopped updating. Re-send your request to run it again."
36009
+ detail: routed ? `${detail}
36010
+ ${ROUTED_CARD_FOOTER}` : detail
35975
36011
  };
35976
36012
  }
35977
36013
  async function catchUpMissedMentions(opts) {
@@ -36282,9 +36318,14 @@ async function closeReclaimedCards(client, closures, warn = console.warn) {
36282
36318
  let closed = 0;
36283
36319
  for (const c of closures) {
36284
36320
  if (!c.card || !(c.status in glyph)) continue;
36321
+ const detail = c.status === "interrupted" ? c.note ?? "The bot restarted while this run was in flight and it could not be resumed." : "The bot restarted after this run replied; its record is complete.";
36285
36322
  const frame = {
36286
36323
  title: `${glyph[c.status]} ${c.agent ?? "run"} \xB7 ${c.status.replace("_", " ")}`,
36287
- detail: c.status === "interrupted" ? c.note ?? "The bot restarted while this run was in flight and it could not be resumed." : "The bot restarted after this run replied; its record is complete."
36324
+ // A routed run's close ends with the override footer wherever it is
36325
+ // written: this is the close a person most needs it on — the run was
36326
+ // cut by a deploy, and `agent:<preset>` in a reply is how to run it again.
36327
+ detail: c.routed ? `${detail}
36328
+ ${ROUTED_CARD_FOOTER}` : detail
36288
36329
  };
36289
36330
  try {
36290
36331
  await client.chat.update({ channel: c.card.channel, ts: c.card.ts, ...render2(frame) });
@@ -36322,6 +36363,7 @@ var RENDER_DETAIL_RAW_MAX, liveCards, liveCardKey, foreignLiveCards, foreignLive
36322
36363
  var init_statusCard = __esm({
36323
36364
  "../../src/channels/slack/statusCard.ts"() {
36324
36365
  "use strict";
36366
+ init_statusCardFrame();
36325
36367
  init_slackEscape();
36326
36368
  RENDER_DETAIL_RAW_MAX = 900;
36327
36369
  liveCards = /* @__PURE__ */ new Set();
@@ -39712,6 +39754,9 @@ function prUrlOf(events) {
39712
39754
  for (const e of events) if (e.type === "pr_opened" && typeof e.url === "string") url = e.url;
39713
39755
  return url;
39714
39756
  }
39757
+ function routedOf(meta, events) {
39758
+ return meta.route !== void 0 || events.some((e) => e.type === "run_meta" && e.agentSource === "route");
39759
+ }
39715
39760
  async function reclaimRuns(opts) {
39716
39761
  const { ledger, gen } = opts;
39717
39762
  const now = opts.now ?? Date.now;
@@ -39786,6 +39831,7 @@ async function reclaimRuns(opts) {
39786
39831
  events: events.length,
39787
39832
  ...row.meta.agent !== void 0 ? { agent: row.meta.agent } : {},
39788
39833
  ...prUrl2 !== void 0 ? { prUrl: prUrl2 } : {},
39834
+ ...routedOf(row.meta, events) ? { routed: true } : {},
39789
39835
  ...status3 === "interrupted" ? { note: closureNote(row.meta.agent, prUrl2) } : {}
39790
39836
  });
39791
39837
  log(
@@ -42787,7 +42833,8 @@ async function runBot() {
42787
42833
  status: c.status,
42788
42834
  agent: c.agent,
42789
42835
  card: c.card,
42790
- ...c.note ? { note: c.note } : {}
42836
+ ...c.note ? { note: c.note } : {},
42837
+ ...c.routed ? { routed: true } : {}
42791
42838
  })),
42792
42839
  (w) => console.warn(w)
42793
42840
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreplane/switchboard",
3
- "version": "1.216.0",
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",