@coreplane/switchboard 1.254.0 → 1.254.1

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.254.0",
3
+ "version": "1.254.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "switchboard",
9
- "version": "1.254.0",
9
+ "version": "1.254.1",
10
10
  "license": "Apache-2.0",
11
11
  "workspaces": [
12
12
  "web",
@@ -20445,7 +20445,7 @@
20445
20445
  },
20446
20446
  "packages/switchboard": {
20447
20447
  "name": "@coreplane/switchboard",
20448
- "version": "1.254.0",
20448
+ "version": "1.254.1",
20449
20449
  "license": "Apache-2.0",
20450
20450
  "dependencies": {
20451
20451
  "@earendil-works/pi-ai": "0.85.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.254.0",
3
+ "version": "1.254.1",
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.254.0",
3
- "commit": "c554204338614ad3882f8e59654806c2a5a61aa9",
4
- "builtAt": "2026-09-19T19:46:00.046Z"
2
+ "version": "1.254.1",
3
+ "commit": "48a9a480354e503b8fea59dc926f94132cf79756",
4
+ "builtAt": "2026-09-19T21:28:07.753Z"
5
5
  }
@@ -362,7 +362,7 @@ void _everyKindListed;
362
362
  * the message, the thread's sticky preset, the user or the channel scope's
363
363
  * `agent`, `defaults.agent`, or the request router. The replay harness
364
364
  * (`load route`) reads it to tell a requester's own choice from a fallback. */
365
- export type AgentSource = "directive" | "sticky" | "user" | "channel" | "default" | "route";
365
+ export type AgentSource = "directive" | "sticky" | "user" | "channel" | "default" | "route" | "operator";
366
366
 
367
367
  /** How an operator asked a run to stop: `soft` — take no new steps and
368
368
  * wrap up through the normal finale; `hard` — abort the in-flight call now, no
package/dist/cli.js CHANGED
@@ -41402,11 +41402,17 @@ async function operatorStage(deps, ctx) {
41402
41402
  function operatorAuthorTurns(tail, author, requestText) {
41403
41403
  return [...tail.filter((t) => t.actor === author).map((t) => t.text), requestText];
41404
41404
  }
41405
- function verifierHolds(line3, bound) {
41406
- if (/^agent:\S/.test(line3.trim())) return true;
41405
+ function verifierHolds(line3, bound, startsRun = false) {
41406
+ if (startsRun || /^agent:\S/.test(line3.trim())) return true;
41407
41407
  if (!bound) return false;
41408
41408
  return bound.def.id === "steer.run" || bound.radius === "write" || bound.radius === "destructive";
41409
41409
  }
41410
+ function presetBindOf(line3, presets) {
41411
+ const trimmed = line3.trim();
41412
+ const head = /^agent:(\S+)/.exec(trimmed);
41413
+ const name = head ? head[1] : /^(\S+)/.exec(trimmed)?.[1];
41414
+ return name !== void 0 && presets.includes(name) ? name : void 0;
41415
+ }
41410
41416
  async function verifyOperatorBind(turns, line3, model, opts = {}) {
41411
41417
  try {
41412
41418
  const answer = await model(verifierPrompt({ turns, line: line3 }), {
@@ -41433,37 +41439,52 @@ function verifierModelOf(deps) {
41433
41439
  }
41434
41440
  async function executeOperatorDecision(deps, ctx) {
41435
41441
  const { event, io, msg } = ctx;
41442
+ const answered = { kind: "answered" };
41436
41443
  if (event.outcome === "question") {
41437
41444
  await io.reply(event.question ?? "");
41438
41445
  await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
41439
- return true;
41446
+ return answered;
41440
41447
  }
41441
41448
  if (event.outcome === "refusal") {
41442
41449
  await io.reply(event.refusalText ?? "");
41443
41450
  await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
41444
- return true;
41451
+ return answered;
41445
41452
  }
41446
41453
  const commands = deps.commands;
41454
+ const presets = routablePresets();
41455
+ const presetNames = presets.map((p) => p.name);
41447
41456
  const confirm = effectiveConfirm(deps.config.boundaryLayers(msg.channelId, msg.userId));
41448
41457
  let authorTurns;
41449
41458
  const authorTurnsOnce = () => authorTurns ??= operatorThreadTail(deps.runLedger, ctx.thread, msg.threadKey).then(
41450
41459
  (tail) => operatorAuthorTurns(tail, msg.userId, msg.text)
41451
41460
  );
41452
41461
  let carried = false;
41453
- for (const bind of event.binds ?? []) {
41454
- const parsed = commands ? parseChatCommand(bind.line, commands) : null;
41462
+ const binds = event.binds ?? [];
41463
+ for (const [i, bind] of binds.entries()) {
41464
+ const preset = presetBindOf(bind.line, presetNames);
41465
+ const parsed = commands && preset === void 0 ? parseChatCommand(bind.line, commands) : null;
41455
41466
  const def = parsed?.kind === "invoke" ? commands?.list().find((c) => c.id === parsed.id) : void 0;
41456
41467
  const bound = parsed?.kind === "invoke" && def ? { def, radius: boundBlastRadius(def, parsed.input) } : void 0;
41457
41468
  let verified;
41458
- if (verifierHolds(bind.line, bound)) {
41469
+ if (verifierHolds(bind.line, bound, preset !== void 0)) {
41459
41470
  const model = verifierModelOf(deps);
41460
- const verdict = model ? await verifyOperatorBind(await authorTurnsOnce(), bind.line, model) : { agrees: false, reason: "no model to verify on" };
41471
+ const held2 = preset !== void 0 ? `agent:${preset} ${msg.text}` : bind.line;
41472
+ const verdict = model ? await verifyOperatorBind(await authorTurnsOnce(), held2, model) : { agrees: false, reason: "no model to verify on" };
41461
41473
  if (!verdict.agrees) {
41462
41474
  await io.reply(renderVerifierHandBack(bind.line, verdict.reason));
41463
41475
  continue;
41464
41476
  }
41465
41477
  verified = renderVerifierLine(verdict.reason);
41466
41478
  }
41479
+ if (preset !== void 0) {
41480
+ const identity2 = presets.find((p) => p.name === preset)?.identity;
41481
+ const radius2 = identity2 === "write" ? "write" : "read";
41482
+ await io.reply(`${renderOperatorReceipt(bind.line, radius2, bind.reason)}${verified ? `
41483
+ ${verified}` : ""}`);
41484
+ for (const rest of binds.slice(i + 1)) await io.reply(`${HAND_BACK_PREFIX}
41485
+ \`${rest.line}\``);
41486
+ return { kind: "route", preset, line: bind.line, reason: bind.reason };
41487
+ }
41467
41488
  if (!parsed || parsed.kind !== "invoke" || !def || !bound) {
41468
41489
  await io.reply(`${verified ? `${verified}
41469
41490
  ` : ""}${HAND_BACK_PREFIX}
@@ -41486,7 +41507,7 @@ ${HAND_BACK_PREFIX}
41486
41507
  if (res.text.length > 0) await io.reply(res.text);
41487
41508
  }
41488
41509
  if (!carried) await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
41489
- return true;
41510
+ return answered;
41490
41511
  }
41491
41512
  var OPERATOR_TOOL_NAME, OPERATOR_TIMEOUT_MS, OPERATOR_MAX_BINDS, OPERATOR_QUESTION_MARKER;
41492
41513
  var init_operator = __esm({
@@ -51506,8 +51527,10 @@ async function dispatch(deps, msg, io, opts = {}) {
51506
51527
  }
51507
51528
  };
51508
51529
  try {
51509
- const operatorMode = !resume && !restart && !opts.parent && !opts.coordinator ? operatorModeOf(deps.config.config) : "off";
51530
+ const configuredOperator = !resume && !restart && !opts.parent && !opts.coordinator ? operatorModeOf(deps.config.config) : "off";
51531
+ const operatorMode = configuredOperator === "on" && parseDirectives(msg.text).agent !== void 0 ? "off" : configuredOperator;
51510
51532
  let operatorEvent;
51533
+ let operatorPreset;
51511
51534
  let operatorThread;
51512
51535
  if (operatorMode !== "off") {
51513
51536
  const runsService2 = deps.runs ?? createRunsService({ registry, store: deps.runStore });
@@ -51527,7 +51550,7 @@ async function dispatch(deps, msg, io, opts = {}) {
51527
51550
  registry.publish(live.runId, { type: "operator", ...operatorEvent, at: clock4() });
51528
51551
  operatorEvent = void 0;
51529
51552
  } else if (operatorMode === "on" && operatorEvent && !operatorFellBack) {
51530
- await root.span(
51553
+ const execution = await root.span(
51531
51554
  "dispatch.operator_decision",
51532
51555
  () => executeOperatorDecision(deps, {
51533
51556
  msg,
@@ -51538,12 +51561,14 @@ async function dispatch(deps, msg, io, opts = {}) {
51538
51561
  ...operatorThread ? { thread: operatorThread } : {}
51539
51562
  })
51540
51563
  );
51541
- return ended;
51564
+ if (execution.kind === "answered") return ended;
51565
+ operatorPreset = execution.preset;
51542
51566
  }
51543
51567
  }
51544
- if (await answerChatCommand(deps, { msg, io, ending, trace, ...operatorEvent ? { operator: operatorEvent } : {} }))
51568
+ if (operatorPreset === void 0 && await answerChatCommand(deps, { msg, io, ending, trace, ...operatorEvent ? { operator: operatorEvent } : {} }))
51545
51569
  return ended;
51546
51570
  const { directives, history } = await readRequest({ msg, io, root });
51571
+ if (operatorPreset !== void 0) directives.agent = operatorPreset;
51547
51572
  const runsService = deps.runs ?? createRunsService({ registry, store: deps.runStore });
51548
51573
  const thread = opts.thread ?? (opts.parent || opts.coordinator || resume || restart || history.length === 0 ? void 0 : operatorThread ?? await readThread(runsService, msg.threadKey));
51549
51574
  const lineage = lineageOf(thread?.[0]);
@@ -51567,6 +51592,7 @@ async function dispatch(deps, msg, io, opts = {}) {
51567
51592
  });
51568
51593
  const { sticky } = settled;
51569
51594
  let { resolved, agentSource } = settled;
51595
+ if (operatorPreset !== void 0) agentSource = "operator";
51570
51596
  let route3;
51571
51597
  const threadLive = admission.get(msg.threadKey) !== void 0 || !resume && !restart && deps.threadsElsewhere.get(msg.threadKey) !== void 0;
51572
51598
  if (thread && !threadLive && directives.agent === void 0 && deps.coordinatorInstances !== void 0) {
@@ -52504,6 +52530,7 @@ var init_dispatcher = __esm({
52504
52530
  init_references();
52505
52531
  init_actor();
52506
52532
  init_config2();
52533
+ init_directives();
52507
52534
  init_resolve();
52508
52535
  init_route();
52509
52536
  init_operator();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreplane/switchboard",
3
- "version": "1.254.0",
3
+ "version": "1.254.1",
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",