github-router 0.3.168 → 0.3.175

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.
@@ -1565,7 +1565,7 @@ function tool(toolNameHttp, description, inputSchema, handler) {
1565
1565
  };
1566
1566
  }
1567
1567
  const ARTIFACT_TOOLS = Object.freeze([
1568
- tool("artifact_open", "Open a workspace file in ai-or-die's Artifact review panel for human review. Pass mode:\"interactive\" when the HTML carries data-aod-* action controls. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1568
+ tool("artifact_open", "Opens a workspace file in ai-or-die's Artifact review panel for human review, replacing the current review if one is already open. The caller provides a workspace-relative or absolute file path and can set mode:\"interactive\" when the HTML carries data-aod-* action controls. It returns the review URL/session identifiers plus next-step guidance for draining feedback. Use it when the user should review a durable artifact before work continues; it is not for one-line status updates or non-file content. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1569
1569
  file: stringProp$2("Workspace-relative or absolute file path to show in the Artifact panel."),
1570
1570
  mode: enumProp$1(["static", "interactive"], "Advisory. \"interactive\" signals the HTML contains data-aod-* action controls the panel should wire; \"static\" (default) is a read-and-annotate artifact.")
1571
1571
  }, ["file"]), async (args, signal) => {
@@ -1584,7 +1584,7 @@ const ARTIFACT_TOOLS = Object.freeze([
1584
1584
  next_step: "Tell the user to review at the Artifact panel, then call artifact_await to receive their feedback."
1585
1585
  });
1586
1586
  }),
1587
- tool("artifact_update", "Replace the current Artifact review's content in place. Provide EXACTLY ONE of file (a workspace file path) or html (raw HTML the server writes to the review's sandboxed file). html requires an already-open review. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1587
+ tool("artifact_update", "Replaces the current Artifact review's content in place without opening a separate review. The caller provides exactly one of file, a workspace-relative or absolute file path, or html, raw HTML written into the existing review sandbox; html requires an already-open review, and idempotencyKey can make retries deduplicate on the server. It returns a minimal success signal plus next-step guidance for awaiting further feedback. Use it when revised content should replace what the human is already reviewing; use artifact_refresh instead when the existing on-disk artifact only needs to be reloaded. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1588
1588
  file: stringProp$2("Workspace-relative or absolute file path to become the review's new content."),
1589
1589
  html: stringProp$2("Raw HTML to write into the review's existing sandboxed file, then reload."),
1590
1590
  idempotencyKey: stringProp$2("Optional stable key so a retried update is de-duplicated by the server.")
@@ -1595,27 +1595,19 @@ const ARTIFACT_TOOLS = Object.freeze([
1595
1595
  const html = optionalString$2(args, "html");
1596
1596
  if (file === void 0 === (html === void 0)) throw new ArtifactToolInputError("INVALID_ARGUMENT", "artifact_update requires EXACTLY ONE of arguments.file or arguments.html");
1597
1597
  const idempotencyKey = optionalString$2(args, "idempotencyKey");
1598
- return ok$2({
1599
- ...await clientFromEnv(env).update({
1600
- file,
1601
- html,
1602
- idempotencyKey,
1603
- signal
1604
- }),
1605
- ok: true,
1606
- next_step: "The panel now shows the updated content. Call artifact_await for further feedback."
1607
- });
1598
+ return ok$2(formatUpdateSuccess(await clientFromEnv(env).update({
1599
+ file,
1600
+ html,
1601
+ idempotencyKey,
1602
+ signal
1603
+ })));
1608
1604
  }),
1609
- tool("artifact_refresh", "Force the ai-or-die Artifact panel to reload the current artifact from disk (no content change). Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1605
+ tool("artifact_refresh", "Reloads the currently-open Artifact review from its existing on-disk file without changing the content source. The tool takes no inputs and returns a minimal success signal plus next-step guidance for awaiting feedback. Use it after an out-of-band edit changes the reviewed file on disk and the panel needs to pick up that version. Do not use it to replace the artifact with a new file or raw HTML; use artifact_update for that. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1610
1606
  const env = readArtifactEnv();
1611
1607
  if (!env) return missingEnvResult();
1612
- return ok$2({
1613
- ...await clientFromEnv(env).refresh(signal),
1614
- ok: true,
1615
- next_step: "The panel reloaded the artifact. Call artifact_await for feedback."
1616
- });
1608
+ return ok$2(formatRefreshSuccess(await clientFromEnv(env).refresh(signal)));
1617
1609
  }),
1618
- tool("artifact_await", "Wait for the human's next Artifact review events (typed drain: comments AND structured action-button/checkbox events) and return them with a cursor. Pass the returned cursor on the next call to receive only newer events. Supersedes artifact_poll. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1610
+ tool("artifact_await", "Waits for the human's next Artifact review events and returns a typed drain containing comments, structured action-button or checkbox events, status, cursor, and next-step guidance. The caller can pass the cursor from a previous response to receive only newer events and can provide timeoutMs as the server long-hold budget. It may return an empty events list on a quiet long-hold; callers should pass the returned cursor on the next artifact_await call. Use it as the primary review-feedback drain after artifact_open or artifact_update; it supersedes artifact_poll, which is legacy comments-only. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1619
1611
  cursor: stringProp$2("High-water cursor from the previous artifact_await response. Omit on the first call."),
1620
1612
  timeoutMs: numberProp$2("Optional server long-hold budget in ms (default ~25000).")
1621
1613
  }, []), async (args, signal) => {
@@ -1629,35 +1621,23 @@ const ARTIFACT_TOOLS = Object.freeze([
1629
1621
  signal
1630
1622
  })));
1631
1623
  }),
1632
- tool("artifact_dismiss", "Hide the ai-or-die Artifact panel UI while keeping the review alive (queued feedback preserved, channel open, re-openable). Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1624
+ tool("artifact_dismiss", "Hides the ai-or-die Artifact panel UI while keeping the current review alive. The tool takes no inputs and returns a minimal success signal plus next-step guidance for reopening or awaiting later feedback. Use it when the panel should get out of the way but queued feedback should remain preserved, the channel should stay open, and the review should be re-openable. Do not use it when the review loop is finished; use artifact_end to close the review instead. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1633
1625
  const env = readArtifactEnv();
1634
1626
  if (!env) return missingEnvResult();
1635
- return ok$2({
1636
- ...await clientFromEnv(env).dismiss(signal),
1637
- ok: true,
1638
- next_step: "The panel is hidden but the review is still live. Re-open the artifact or call artifact_await when ready."
1639
- });
1627
+ return ok$2(formatDismissSuccess(await clientFromEnv(env).dismiss(signal)));
1640
1628
  }),
1641
- tool("artifact_reply", "Send the agent's reply back to the ai-or-die Artifact review panel after applying or responding to human feedback. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ text: stringProp$2("Agent reply text to deliver to the human Artifact review panel.") }, ["text"]), async (args, signal) => {
1629
+ tool("artifact_reply", "Sends the agent's reply back to the ai-or-die Artifact review panel after applying or responding to human feedback. The caller provides the reply text, and the tool returns a minimal success signal plus next-step guidance for either continuing the review loop or moving on. Use it to acknowledge what changed, answer a reviewer question, or summarize how feedback was handled after artifact_await returns events. Do not use it to replace panel content, wait for more feedback, hide the UI, or close the review. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ text: stringProp$2("Agent reply text to deliver to the human Artifact review panel.") }, ["text"]), async (args, signal) => {
1642
1630
  const env = readArtifactEnv();
1643
1631
  if (!env) return missingEnvResult();
1644
1632
  const text = requiredString$2(args, "text");
1645
- return ok$2({
1646
- ...await clientFromEnv(env).agentReply(text, signal),
1647
- ok: true,
1648
- next_step: "Wait for further human review, or continue if the review loop is complete."
1649
- });
1633
+ return ok$2(formatReplySuccess(await clientFromEnv(env).agentReply(text, signal)));
1650
1634
  }),
1651
- tool("artifact_end", "End/close the ai-or-die Artifact review panel when the review loop is complete. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1635
+ tool("artifact_end", "Ends and closes the ai-or-die Artifact review panel when the review loop is complete. The tool takes no inputs and returns a minimal success signal plus terminal next-step guidance. Use it after the human review is finished and no further feedback should arrive. Do not use it for a temporary hide or pause; use artifact_dismiss when the review should stay live. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1652
1636
  const env = readArtifactEnv();
1653
1637
  if (!env) return missingEnvResult();
1654
- return ok$2({
1655
- ...await clientFromEnv(env).end(signal),
1656
- ok: true,
1657
- next_step: "Artifact review loop ended."
1658
- });
1638
+ return ok$2(formatEndSuccess(await clientFromEnv(env).end(signal)));
1659
1639
  }),
1660
- tool("artifact_poll", "FROZEN legacy alias for artifact_await (old payload, human comments only, no structured actions). New agents should call artifact_await instead. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ timeoutMs: numberProp$2("Optional per-call budget hint in ms (advisory).") }, []), async (_args, signal) => {
1640
+ tool("artifact_poll", "Provides the frozen legacy polling path for Artifact review feedback. The caller may provide timeoutMs as an advisory per-call budget, and the tool returns the old comments-only payload with status, prompts, and next-step guidance rather than typed action events or a cursor. Use it only for compatibility with older clients or flows that still require the old payload shape. New callers should use artifact_await instead because it returns typed comments and structured action-button or checkbox events. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ timeoutMs: numberProp$2("Optional per-call budget hint in ms (advisory).") }, []), async (_args, signal) => {
1661
1641
  const env = readArtifactEnv();
1662
1642
  if (!env) return missingEnvResult();
1663
1643
  return ok$2(formatPollResponse(await pollUntilReady(clientFromEnv(env), signal)));
@@ -1735,6 +1715,50 @@ function formatPollResponse(response) {
1735
1715
  next_step: response.next_step ?? defaultPollNextStep(response.status)
1736
1716
  });
1737
1717
  }
1718
+ function formatUpdateSuccess(response) {
1719
+ return definedObject$1({
1720
+ ok: true,
1721
+ viewUrl: stringField$1(response, "viewUrl"),
1722
+ next_step: "The panel now shows the updated content. Call artifact_await for further feedback."
1723
+ });
1724
+ }
1725
+ function formatRefreshSuccess(response) {
1726
+ return definedObject$1({
1727
+ ok: true,
1728
+ viewUrl: stringField$1(response, "viewUrl"),
1729
+ panelUrl: stringField$1(response, "panelUrl"),
1730
+ status: stringField$1(response, "status"),
1731
+ visibility: stringField$1(response, "visibility"),
1732
+ next_step: "The panel reloaded the artifact. Call artifact_await for feedback."
1733
+ });
1734
+ }
1735
+ function formatDismissSuccess(response) {
1736
+ return definedObject$1({
1737
+ ok: true,
1738
+ viewUrl: stringField$1(response, "viewUrl"),
1739
+ panelUrl: stringField$1(response, "panelUrl"),
1740
+ status: stringField$1(response, "status"),
1741
+ visibility: stringField$1(response, "visibility"),
1742
+ next_step: "The panel is hidden but the review is still live. Re-open the artifact or call artifact_await when ready."
1743
+ });
1744
+ }
1745
+ function formatReplySuccess(response) {
1746
+ return definedObject$1({
1747
+ ok: true,
1748
+ reply: response.reply,
1749
+ delivered: booleanField(response, "delivered"),
1750
+ confirmed: booleanField(response, "confirmed"),
1751
+ status: stringField$1(response, "status"),
1752
+ next_step: "Wait for further human review, or continue if the review loop is complete."
1753
+ });
1754
+ }
1755
+ function formatEndSuccess(response) {
1756
+ return definedObject$1({
1757
+ ok: true,
1758
+ status: response.status,
1759
+ next_step: "Artifact review loop ended."
1760
+ });
1761
+ }
1738
1762
  /**
1739
1763
  * Shape the typed drain for the model: pass events through verbatim (unknown
1740
1764
  * `kind`s preserved — the model ignores what it does not understand), echo the
@@ -1839,6 +1863,14 @@ function definedObject$1(input) {
1839
1863
  for (const [key, value] of Object.entries(input)) if (value !== void 0) result[key] = value;
1840
1864
  return result;
1841
1865
  }
1866
+ function stringField$1(input, key) {
1867
+ const value = input[key];
1868
+ return typeof value === "string" ? value : void 0;
1869
+ }
1870
+ function booleanField(input, key) {
1871
+ const value = input[key];
1872
+ return typeof value === "boolean" ? value : void 0;
1873
+ }
1842
1874
  function objectSchema$2(properties, required) {
1843
1875
  return {
1844
1876
  type: "object",
@@ -3596,10 +3628,10 @@ function createFleetTools(options = {}) {
3596
3628
  };
3597
3629
  }
3598
3630
  return Object.freeze([
3599
- tool$1("list_instances", "List registered remote ai-or-die instances in the fleet registry. Tokens are never returned.", objectSchema$1({}, []), async () => {
3631
+ tool$1("list_instances", "Lists registered remote ai-or-die fleet instances and probes whether each instance is currently reachable. It takes no input; the registry decides which instances exist, and credentials or tunnel tokens are not returned. It returns instances with id, label, reachable status, sessionCount and lastSeen for reachable hosts, or error and hint for unreachable hosts. It is useful as the discovery entry point before list_sessions, create_session, or other fleet tools that need an instance id. It is not for local repository search or for reading sessions; use local tools for this machine and list_sessions after choosing an instance.", objectSchema$1({}, []), async () => {
3600
3632
  return ok$1({ instances: await mapWithConcurrency(await getRegistry().listInstances(), fleetFanoutConcurrency(LIST_INSTANCES_FANOUT_CONCURRENCY), (instance) => probeInstance(instance)) });
3601
3633
  }),
3602
- tool$1("list_sessions", "List sessions on one fleet instance, returning globally-addressable session ids.", objectSchema$1({ instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance.") }, []), async (args, signal) => {
3634
+ tool$1("list_sessions", "Lists sessions on one remote ai-or-die fleet instance and returns session ids that can be used by the other fleet session tools. The optional instance input is an id or label; when it is omitted, the registry default or sole instance is used. It returns resolvedInstance and sessions, with each sessionId globalized as instanceId:localSessionId. It is useful after list_instances to choose a remote session to inspect, message, drive, or stop. It is not a fleet-wide listing and does not read transcript output; call it per instance, and use read_session for a session's text tail.", objectSchema$1({ instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance.") }, []), async (args, signal) => {
3603
3635
  const instance = await resolve(optionalString$1(args, "instance"));
3604
3636
  const response = await clientFor(instance).listSessions(signal);
3605
3637
  return ok$1({
@@ -3607,11 +3639,10 @@ function createFleetTools(options = {}) {
3607
3639
  sessions: response.sessions.map((session) => globalizeSession(instance.id, session))
3608
3640
  });
3609
3641
  }),
3610
- tool$1("read_session", "Read recent text output from an addressed fleet session.", objectSchema$1({
3642
+ tool$1("read_session", "Reads recent text output from an addressed remote ai-or-die fleet session. The required sessionId must be a global id in instanceId:localSessionId form; the optional instance input is only a cross-check and must resolve to the same instance. It returns resolvedInstance, sessionId, text, truncated, source, and the session status snapshot. It is useful for inspecting the transcript tail after send_message, await_turn, or drive_task. It is not for lifecycle-only checks or live waiting; use session_status for point-in-time state and await_turn to wait for new events.", objectSchema$1({
3611
3643
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3612
3644
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3613
- lines: numberProp$1("Number of recent lines to read."),
3614
- format: stringProp$1("Reserved for future formatting; results are JSON text today.")
3645
+ lines: numberProp$1("Number of recent lines to read.")
3615
3646
  }, ["sessionId"]), async (args, signal) => {
3616
3647
  const { instance, localId, globalId } = await resolveSession(args);
3617
3648
  const lines = optionalNumber$1(args, "lines");
@@ -3622,7 +3653,7 @@ function createFleetTools(options = {}) {
3622
3653
  sessionId: globalId
3623
3654
  });
3624
3655
  }),
3625
- tool$1("session_status", "Fetch lifecycle and interaction status for an addressed fleet session.", objectSchema$1({
3656
+ tool$1("session_status", "Fetches the lifecycle and interaction status for an addressed remote ai-or-die fleet session. The required sessionId must be a global id in instanceId:localSessionId form; the optional instance input is only a cross-check and must resolve to the same instance. It returns resolvedInstance, sessionId, and a status object that can include lifecycle, interactionState, canAcceptInput, blockReason, and awaiting details. It is useful before deciding whether a session can accept a message or is awaiting a prompt. It is not a transcript reader or event watcher; use read_session for output text and await_turn for turn-completion events.", objectSchema$1({
3626
3657
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3627
3658
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId.")
3628
3659
  }, ["sessionId"]), async (args, signal) => {
@@ -3634,7 +3665,7 @@ function createFleetTools(options = {}) {
3634
3665
  sessionId: globalId
3635
3666
  });
3636
3667
  }),
3637
- tool$1("send_message", "Send a message to a fleet session. By DEFAULT it first checks the session is idle / awaiting the next message and REFUSES (structured notReady, isError) rather than blind-type into a busy composer or a pending prompt set requireIdle:false to force the legacy unconditional send, or waitForIdleMs to wait briefly for idle first. isError reflects DELIVERY: true when the message was not delivered (transport/precondition failure) OR refused as notReady. A delivered message whose confirmation did not arrive within awaitMs is NOT an error it returns delivered:true with confirmationPending/confirmationTimedOut, because a long turn legitimately outruns awaitMs. The additive `submitted` field is true only when ai-or-die's submission sub-status proves the message reached the composer. Recommended pattern: send with awaitMs:0 for a fast delivery ack, then call await_turn (filtered to this sessionId) to observe the session's actual turn completion. The idempotencyKey makes a retried send safe (a retry never re-types the message).", objectSchema$1({
3668
+ tool$1("send_message", "Sends a free-text message to an existing remote ai-or-die fleet session. The required sessionId must be global, message is the text to deliver, requireIdle defaults to true, waitForIdleMs can wait briefly for readiness, awaitMs waits only for best-effort delivery confirmation, and idempotencyKey is usually auto-generated unless retrying the same send. It returns resolvedInstance, sessionId, delivered, confirmed, submitted when the remote proves the composer accepted the message, and confirmationPending/confirmationTimedOut when delivery succeeded but the turn outran the await window; delivered:false or notReady is reported as an error result. It is useful for sending the next free-text instruction to an idle session, especially with awaitMs:0 followed by await_turn for the actual turn boundary. It is not for answering an awaited choice prompt or sending control keys; use respond for prompts and send_keys for submit, interrupt, or literal key sequences.", objectSchema$1({
3638
3669
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3639
3670
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3640
3671
  message: stringProp$1("Message text to deliver to the session."),
@@ -3691,7 +3722,7 @@ function createFleetTools(options = {}) {
3691
3722
  ...isError ? { message: "message was not delivered to the session by the upstream instance" } : confirmationTimedOut ? { message: "delivered; turn completion not confirmed in the await window. Use await_turn filtered to this sessionId to observe completion (the idempotencyKey makes a retried send safe)." } : {}
3692
3723
  }, isError);
3693
3724
  }),
3694
- tool$1("send_keys", "Send key input to a fleet session. Prefer the higher-level `op`: 'submit' presses Enter and 'interrupt' sends Ctrl-C, each mapped to ai-or-die's NAMED key (never a literal control byte like \"\\r\"). Use `keys` only for literal input; `raw` is strictly for literal bytes. Provide exactly one of `op` or `keys`.", objectSchema$1({
3725
+ tool$1("send_keys", "Sends key input to an existing remote ai-or-die fleet session. The required sessionId must be global; provide exactly one of op or keys, where op is a named operation (`submit` for Enter or `interrupt` for Ctrl-C) and keys is a literal key sequence; raw only applies to literal keys. It returns resolvedInstance, sessionId, delivered, duplicated when an idempotency retry was deduped, and the mapped key name when op was used. It is useful for control-key actions such as submitting a typed prompt or interrupting a busy turn without stopping the session. It is not the normal free-text path and not the prompt-answer path; use send_message for free text and respond for awaited prompts.", objectSchema$1({
3695
3726
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3696
3727
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3697
3728
  op: stringProp$1("Higher-level named op: 'submit' (Enter) or 'interrupt' (Ctrl-C). Mapped to the ai-or-die named key with raw off. Do NOT also pass keys."),
@@ -3726,10 +3757,11 @@ function createFleetTools(options = {}) {
3726
3757
  op,
3727
3758
  mappedKeys: keys
3728
3759
  },
3729
- ...response
3760
+ delivered: response.delivered,
3761
+ ...response.duplicated === void 0 ? {} : { duplicated: response.duplicated }
3730
3762
  });
3731
3763
  }),
3732
- tool$1("respond", "Answer an awaited prompt in a fleet session by choice, option value, or explicit key override.", objectSchema$1({
3764
+ tool$1("respond", "Answers an awaited prompt in an existing remote ai-or-die fleet session by selecting a choice, selecting an exact option value, or sending explicit keys. The required sessionId must be global; choose the answer mode that matches the prompt, and idempotencyKey is usually auto-generated unless retrying the same response. It returns resolvedInstance, sessionId, delivered, duplicated when an idempotency retry was deduped, and any awaitingKind or mappedKeys supplied by the remote; delivered:false is reported as an error result. It is useful only when session_status or await_turn shows the session is waiting for a prompt or choice. It is not for ordinary free-text instructions or control keys; use send_message for free text and send_keys for submit or interrupt.", objectSchema$1({
3733
3765
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3734
3766
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3735
3767
  choice: stringProp$1("Named or numbered choice to select."),
@@ -3745,23 +3777,35 @@ function createFleetTools(options = {}) {
3745
3777
  idempotencyKey: optionalString$1(args, "idempotencyKey") ?? randomUUID()
3746
3778
  });
3747
3779
  const response = await clientFor(instance).respond(localId, input, signal);
3748
- return ok$1({
3780
+ const delivered = response.delivered !== false;
3781
+ return jsonResult$1({
3749
3782
  resolvedInstance: publicInstance(instance),
3750
3783
  sessionId: globalId,
3751
- ...response
3752
- });
3784
+ ...response,
3785
+ delivered,
3786
+ ...delivered ? {} : { message: "response was not delivered to the session by the upstream instance" }
3787
+ }, !delivered);
3753
3788
  }),
3754
- tool$1("create_session", "Create a new session on a specific fleet instance. The instance argument is required; no default is used.", objectSchema$1({
3789
+ tool$1("create_session", "Creates a new session on a specific remote ai-or-die fleet instance. The instance input is required and never defaults; agent is required and must be one of claude, codex, copilot, gemini, or terminal; start:true is required for the session to actually run and be driveable. It returns resolvedInstance plus the remote create response, with sessionId converted to the global instanceId:localSessionId form when creation succeeds. It is useful when the user wants a new remote session to run on a named fleet host before using drive_task, send_message, or await_turn. It is not for selecting or inspecting an existing session; use list_instances and list_sessions first when the target is unknown.", objectSchema$1({
3755
3790
  instance: stringProp$1("Required instance id or label. Create never uses the registry default."),
3756
- agent: stringProp$1("Agent/runtime to create on the instance."),
3791
+ agent: {
3792
+ ...stringProp$1("Required agent/runtime to create on the instance. Valid values: claude, codex, copilot, gemini, terminal."),
3793
+ enum: [
3794
+ "claude",
3795
+ "codex",
3796
+ "copilot",
3797
+ "gemini",
3798
+ "terminal"
3799
+ ]
3800
+ },
3757
3801
  name: stringProp$1("Optional display name for the session."),
3758
3802
  workingDir: stringProp$1("Optional working directory on the remote instance."),
3759
3803
  idempotencyKey: stringProp$1("Optional caller idempotency key; auto-generated when omitted."),
3760
- start: booleanProp("Whether the remote instance should start the session immediately."),
3761
- readyTimeoutMs: numberProp$1("F17: bounded ms to wait for the agent to become driveable before returning. The response carries ready/bound/blocker."),
3762
- permissionMode: stringProp$1("F10 (claude only): permission mode the launched agent starts in — one of plan | acceptEdits | default | bypassPermissions. Rejected with BAD_REQUEST if unknown or if agentArgs also sets it."),
3763
- agentArgs: arrayProp("F10 (claude only): extra launcher args appended after the github-router prefix. Must NOT include --permission-mode or --dangerously-skip-permissions (use permissionMode) — rejected with BAD_REQUEST."),
3764
- disableStopGate: booleanProp("C3 (claude only): disable the structural Stop-gate on the launched session by injecting --no-stop-gate into agentArgs, so a driven session's turn-end never hangs on a blocking Stop hook. Requires a remote github-router that understands the flag (uses the agent_args capability).")
3804
+ start: booleanProp("Set true to start the remote session immediately; without start:true the created session is not running or driveable."),
3805
+ readyTimeoutMs: numberProp$1("Bounded milliseconds to wait for the agent to become driveable before returning. The response carries ready, bound, and blocker."),
3806
+ permissionMode: stringProp$1("Claude-only permission mode for the launched agent: plan, acceptEdits, default, or bypassPermissions. Rejected with BAD_REQUEST if unknown or if agentArgs also sets it."),
3807
+ agentArgs: arrayProp("Claude-only extra launcher args appended after the github-router prefix. Do not include --permission-mode or --dangerously-skip-permissions; use permissionMode instead."),
3808
+ disableStopGate: booleanProp("Claude-only option to disable the structural Stop-gate on the launched session by injecting --no-stop-gate into agentArgs, so a driven session's turn-end does not hang on a blocking Stop hook. Requires a remote github-router that understands the flag.")
3765
3809
  }, ["instance", "agent"]), async (args, signal) => {
3766
3810
  const instance = await resolve(requiredString$1(args, "instance"));
3767
3811
  const agent = requiredString$1(args, "agent");
@@ -3789,7 +3833,7 @@ function createFleetTools(options = {}) {
3789
3833
  sessionId: localSessionId ? encodeSessionId(instance.id, localSessionId) : response.sessionId
3790
3834
  });
3791
3835
  }),
3792
- tool$1("stop_session", "Stop a fleet session.", objectSchema$1({
3836
+ tool$1("stop_session", "Terminates an existing remote ai-or-die fleet session. The required sessionId must be global; instance is only a cross-check, mode is an optional remote-understood stop mode, and idempotencyKey is usually auto-generated unless retrying the same stop. It returns resolvedInstance, sessionId, stopped, and lifecycle. It is useful when the remote session should be ended and its in-flight turn should be killed. It is destructive and irreversible, with no resume companion; to merely unstick or interrupt a busy session without terminating it, use send_keys with op `interrupt`.", objectSchema$1({
3793
3837
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3794
3838
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3795
3839
  idempotencyKey: stringProp$1("Optional caller idempotency key; auto-generated when omitted."),
@@ -3807,10 +3851,10 @@ function createFleetTools(options = {}) {
3807
3851
  ...response
3808
3852
  });
3809
3853
  }),
3810
- tool$1("await_turn", "Long-poll session events across fleet instances. The server owns per-target opaque cursors, so callers do not pass cursor tokens. Distinct concurrent watchers over the same instance set should pass a distinct watcherId so they do not share a cursor.", objectSchema$1({
3854
+ tool$1("await_turn", "Long-polls session events across remote ai-or-die fleet instances. The caller selects targets with instances, sessionIds, or neither for every registered instance; timeoutMs bounds each per-instance long poll, kinds filters event kinds, and watcherId isolates cursor state for concurrent watchers. It returns resolvedInstances, time-sorted stamped events, gaps, cursors, more, optional per-session settled classifications, and optional per-instance errors. It is useful after send_message with awaitMs:0 to observe the real turn boundary; a settled status such as turn_ended or waiting_input is the reliable completion signal, while idle flickers are not completion. It is not a transcript reader or a one-shot task driver; use read_session for text output and drive_task when sending one prompt and waiting for its report should be a single composite operation.", objectSchema$1({
3811
3855
  instances: arrayProp("Instance ids or labels to poll. Omit with sessionIds to target those session instances; omit both to poll every registered instance."),
3812
3856
  sessionIds: arrayProp("Global session ids to filter to."),
3813
- timeoutMs: numberProp$1("Long-poll timeout per instance in milliseconds."),
3857
+ timeoutMs: numberProp$1(`Long-poll timeout per instance in milliseconds (default ${AWAIT_TURN_DEFAULT_TIMEOUT_MS}).`),
3814
3858
  kinds: arrayProp("Optional event kinds to filter to."),
3815
3859
  watcherId: stringProp$1("Optional stable id for this watcher. Use a distinct value for concurrent watchers over the same target set to keep cursors isolated.")
3816
3860
  }, []), async (args, signal) => {
@@ -3873,7 +3917,7 @@ function createFleetTools(options = {}) {
3873
3917
  ...errors.length > 0 ? { errors } : {}
3874
3918
  });
3875
3919
  }),
3876
- tool$1("drive_task", "Drive one prompt on a session to completion and return the parsed operator report. Composes the reliable path: ensure the composer is idle (else return a structured busy/not-ready result), send and surface whether the message reached the composer (submitted; a delivered-but-unconfirmed send still proceeds), wait for the RELIABLE turn boundary (turn_ended / waiting_input never the became_idle flicker), read the transcript tail, and parse the OPERATOR REPORT trailer into {state, summary, ask, artifact, raw}. A per-call REPORT_ID nonce is embedded in the trailer instruction and the parsed report is trusted ONLY when it echoes that nonce, so a stale prior-turn trailer left in the tail can never be returned as this turn's result. A reliable waiting_input outranks the model's self-reported STATE (a still-blocked session is never reported as done). If the turn does not end within timeoutMs (e.g. a blocking Stop hook that would otherwise hang ~10 min), it AUTO-RECOVERS with a Ctrl-C interrupt rather than blocking, then re-waits briefly and re-reads; a caller ABORT is distinct (settled:'aborted', state:'aborted') and never injects a Ctrl-C. Read `state` TOGETHER with `settled`/`interrupted`/`recovered`: state:'done' with settled:'timeout' + interrupted:true means the model reported done but the turn had to be interrupted to recover, so treat it as needs-verification rather than a clean completion; `submitted` is a best-effort positive signal that CAN be false even on a successful turn. Robust to a busy session (state:'busy'), a missing/stale/placeholder trailer (state falls back to the settle-derived value, reportFound:false), and a hung hook (interrupted:true, recovered:true/false). By default it appends the trailer instruction so the driven session emits a parseable report; set expectReport:false to send the prompt verbatim (a trailer left in the tail is then never trusted).", objectSchema$1({
3920
+ tool$1("drive_task", "Drives one prompt on an existing remote ai-or-die fleet session to completion and returns a parsed operator report. The required sessionId must be global, prompt is the single instruction to send, timeoutMs controls when a hung turn is interrupted for recovery, and expectReport defaults to true so a nonce-guarded OPERATOR REPORT trailer is appended and parsed. It returns resolvedInstance, sessionId, state, summary, ask, artifact, raw, settled, submitted, reportFound, and recovery fields such as interrupted or recovered; state must be read together with settled and interrupted because a timeout recovery can leave work needing verification. It is useful as the composite path that performs the safe send_message plus await_turn plus read_session sequence for one already-created session. It is not a session creator, a multi-turn conversation loop, or a simple transcript read; use create_session first when needed, send_message and await_turn when controlling each step manually, and read_session when only the transcript tail is needed.", objectSchema$1({
3877
3921
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3878
3922
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3879
3923
  prompt: stringProp$1("The task/prompt to drive on the session."),
@@ -3898,7 +3942,7 @@ function createFleetTools(options = {}) {
3898
3942
  ...result
3899
3943
  }, result.error !== void 0);
3900
3944
  }),
3901
- tool$1("read_file", "Read a file from one fleet instance via its existing /api/files/content endpoint.", objectSchema$1({
3945
+ tool$1("read_file", "Reads a file from one remote ai-or-die fleet instance's filesystem. The required path is passed to the remote host as an unsanitized read request; this router does not confine it to a local workspace, and path policy is delegated to the remote instance. It returns resolvedInstance plus the remote file-content response. It is useful for reading a known file on a remote fleet host after choosing an instance. It is not for local files, directory browsing, text search, git revisions, or session transcripts; use local Read for this machine, list_dir to browse remote directories, search to find remote files, git_show for revision content, and read_session for terminal output.", objectSchema$1({
3902
3946
  instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance."),
3903
3947
  path: stringProp$1("Remote file path to read.")
3904
3948
  }, ["path"]), async (args, signal) => {
@@ -3909,7 +3953,7 @@ function createFleetTools(options = {}) {
3909
3953
  ...response
3910
3954
  });
3911
3955
  }),
3912
- tool$1("list_dir", "List a directory on one fleet instance via its existing /api/files endpoint.", objectSchema$1({
3956
+ tool$1("list_dir", "Lists a directory on one remote ai-or-die fleet instance. The required path is the remote directory path, and instance can select a registered host or default to the registry default or sole instance. It returns resolvedInstance plus the remote directory-listing response. It is useful for browsing a remote workspace before choosing a file to read or search. It is not for local directories, file contents, git revisions, or session transcripts; use local filesystem tools for this machine, read_file for remote file contents, git_show for revision content, and read_session for terminal output.", objectSchema$1({
3913
3957
  instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance."),
3914
3958
  path: stringProp$1("Remote directory path to list.")
3915
3959
  }, ["path"]), async (args, signal) => {
@@ -3920,7 +3964,7 @@ function createFleetTools(options = {}) {
3920
3964
  ...response
3921
3965
  });
3922
3966
  }),
3923
- tool$1("search", "Search files on one fleet instance via its existing /api/search endpoint.", objectSchema$1({
3967
+ tool$1("search", "Searches files on one remote ai-or-die fleet instance's workspace, where a fleet instance is a registered remote host exposed through the fleet MCP server. The required query is sent to that remote instance, and path can narrow the remote search scope; this does not search the local repository or the web. It returns resolvedInstance plus the remote search response. It is useful when the target content lives on a remote fleet host. It is not for this checkout, semantic code discovery, or internet research; use mcp__search__code for the local workspace and mcp__search__web for web search.", objectSchema$1({
3924
3968
  instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance."),
3925
3969
  query: stringProp$1("Search query."),
3926
3970
  path: stringProp$1("Optional path scope.")
@@ -3932,18 +3976,16 @@ function createFleetTools(options = {}) {
3932
3976
  ...response
3933
3977
  });
3934
3978
  }),
3935
- tool$1("git_show", "Read a file/revision through one fleet instance's existing /api/files/git-show endpoint.", objectSchema$1({
3979
+ tool$1("git_show", "Shows git content on one remote ai-or-die fleet instance, such as a file at a specific revision or a commit object. The required path identifies the remote repository path or file path, and the optional ref is a git commit-ish such as HEAD, a branch, a tag, or a commit SHA. It returns resolvedInstance plus the remote git-show response. It is useful when the caller needs repository content as it existed at a revision on the remote host. It is not for current working-tree reads, directory listings, local git commands, or web search; use read_file for current remote file content and local tools for this checkout.", objectSchema$1({
3936
3980
  instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance."),
3937
3981
  path: stringProp$1("Remote repository path or file path for git-show."),
3938
- ref: stringProp$1("Optional git ref/revision."),
3939
- rev: stringProp$1("Optional git revision alias."),
3940
- commit: stringProp$1("Optional commit id.")
3982
+ ref: stringProp$1("Optional git ref, revision, or commit-ish, such as HEAD, a branch, a tag, or a commit SHA.")
3941
3983
  }, ["path"]), async (args, signal) => {
3942
3984
  const instance = await resolve(optionalString$1(args, "instance"));
3943
- const response = await clientFor(instance).gitShow({
3944
- ...args,
3945
- instance: void 0
3946
- }, signal);
3985
+ const response = await clientFor(instance).gitShow(definedObject({
3986
+ path: requiredString$1(args, "path"),
3987
+ ref: optionalString$1(args, "ref")
3988
+ }), signal);
3947
3989
  return ok$1({
3948
3990
  resolvedInstance: publicInstance(instance),
3949
3991
  ...response
@@ -10421,7 +10463,7 @@ function createFirstMateTools(depsOverride = {}) {
10421
10463
  };
10422
10464
  }
10423
10465
  return Object.freeze([
10424
- tool$1("start_mission", "Register a first-mate mission for one or more GitHub repositories. Unit decomposition is handled by later controller/model wakes.", objectSchema({
10466
+ tool$1("start_mission", "Registers a first-mate mission in the local durable controller for one or more GitHub repositories; it does not dispatch work or touch GitHub on this call. Inputs provide the mission goal, owner/name repos, user-blessed acceptance criteria, and optional priority, house rules, default cloud-agent model, plan gate, and CI requirement. Returns the new mission id and normalized repo list so later wakes can decompose and dispatch units. Use when the operator has accepted mission scope and wants the first-mate loop to own tracking. It is not for one-off status reads, adding units to an existing mission, or missions whose acceptance criteria are still unsettled.", objectSchema({
10425
10467
  goal: stringProp("Mission goal."),
10426
10468
  repos: stringArrayProp("Repositories as owner/name strings."),
10427
10469
  acceptance_criteria: stringProp("User-blessed acceptance criteria for the mission."),
@@ -10460,7 +10502,7 @@ function createFirstMateTools(depsOverride = {}) {
10460
10502
  repos
10461
10503
  });
10462
10504
  }),
10463
- tool$1("scaffold_repo", "Seed deterministic agentic-dev convention files into a GitHub repository on a pull-request branch.", objectSchema({
10505
+ tool$1("scaffold_repo", "Seeds first-mate and agentic-dev convention files into a GitHub repository by creating a scaffold branch, committing deterministic files, and opening a pull request; it does not write directly to the default branch. Inputs name the owner/name repo, optional base ref, handling mode, and optional detection overrides for stack, OS, package manager, commands, and UI evidence. Returns the pull request plus committed, preserved, and per-file report data, or a no-op note when nothing needs seeding. Use when preparing an owned repository for the first-mate cloud-agent workflow. It is not for arbitrary third-party repositories, normal feature work, or repeated runs unless the operator intentionally wants missing or enhanced convention files.", objectSchema({
10464
10506
  repo: stringProp("Repository as an owner/name string."),
10465
10507
  mode: enumProp([
10466
10508
  "add-missing-only",
@@ -10521,7 +10563,7 @@ function createFirstMateTools(depsOverride = {}) {
10521
10563
  report: plan.reports
10522
10564
  });
10523
10565
  }),
10524
- tool$1("advance", "Wake the first-mate controller once, applying model answers or human decisions, then return the compact board and pending requests.", objectSchema({
10566
+ tool$1("advance", "Wakes the first-mate controller once, optionally applying model answers and human decisions before driving eligible missions. Inputs can supply prior request answers, bound returned request count, provider concurrency, scope the wake to one mission, or include inactive missions in the returned view. Returns the compact board, inactive summary, pending model and human requests, applied answer count, next wake timing, and whether this caller actually drove or deferred to another driver. Use when the operator is advancing the durable loop, responding to needsModel/needsHuman, or scheduling the next wake from nextWakeSeconds. It is not for read-only status checks; board reads the whole portfolio without a wake, and mission_status reads a mission-id-scoped status view.", objectSchema({
10525
10567
  model_answers: arrayOfObjectsProp("Optional model judgments to apply before the wake.", {
10526
10568
  requestId: stringProp("Request id from a previous needsModel entry."),
10527
10569
  verdict: anyProp("Structured verdict for the request kind.")
@@ -10575,7 +10617,7 @@ function createFirstMateTools(depsOverride = {}) {
10575
10617
  drove: result.drove !== false
10576
10618
  });
10577
10619
  }),
10578
- tool$1("board", "Read compact board status. Defaults to active missions only; pass include_all to include inactive missions.", objectSchema({ include_all: boolProp("When true, include inactive missions in the board. Default returns active missions only and summarizes inactive counts.") }, []), async (args) => {
10620
+ tool$1("board", "Reads the compact first-mate portfolio board without waking the controller, dispatching work, or applying answers. Inputs only choose whether inactive missions are included; active missions are returned by default with inactive counts summarized separately. Returns the same board shape that advance returns as a side effect, including mission phase counts, blocked counts, unit rows, and inactive summary. Use when the operator needs a whole-portfolio snapshot. It is not for driving progress or applying pending requests; advance does that, and mission_status is the read path scoped to a single mission id.", objectSchema({ include_all: boolProp("When true, include inactive missions in the board. Default returns active missions only and summarizes inactive counts.") }, []), async (args) => {
10579
10621
  const includeAll = optionalBoolean(args, "include_all") ?? false;
10580
10622
  const [missions, units] = await Promise.all([readMissions(), loadAllUnits()]);
10581
10623
  return ok({
@@ -10583,7 +10625,7 @@ function createFirstMateTools(depsOverride = {}) {
10583
10625
  inactiveSummary: summarizeInactiveMissions(missions)
10584
10626
  });
10585
10627
  }),
10586
- tool$1("merge_pr", "Merge a GitHub pull request the operator has reviewed. Head-guarded (rejects a moved head), ownership-scoped (agent-authored or an active first-mate mission repo, else requires allow_unowned), and gated on a pre-merge safety check (OPEN, not draft, MERGEABLE, CI green).", objectSchema({
10628
+ tool$1("merge_pr", "Merges a live GitHub pull request immediately, which is irreversible through this tool once GitHub accepts the merge. Inputs identify the repo and PR, bind the action to the exact reviewed head SHA, optionally bind the reviewed base branch, select the merge method, and can explicitly override ownership with allow_unowned. Returns GitHub's merge result and merge SHA after the live PR passes head-SHA and optional base concurrency checks, ownership checks, OPEN/not-draft/mergeable checks, and available CI or workflow checks. Use only after out-of-band human authorization for this exact head; this tool does not consult the first-mate approval ledger. It is not for PRs that are not agent-authored or correlated to a first-mate unit (unit.pr === pr) unless allow_unowned is intentionally set; a repo with no configured CI can merge on operator review plus the non-CI guards.", objectSchema({
10587
10629
  repo: stringProp("Repository as an owner/name string."),
10588
10630
  pr: numberProp("Pull request number."),
10589
10631
  expected_head_sha: stringProp("The exact head commit SHA the operator reviewed. The merge is REJECTED if the live head has moved from this value; re-review the new head before merging."),
@@ -10593,7 +10635,7 @@ function createFirstMateTools(depsOverride = {}) {
10593
10635
  "squash",
10594
10636
  "rebase"
10595
10637
  ], "Merge method. Defaults to squash."),
10596
- allow_unowned: boolProp("Set true to merge a PR that is neither agent-authored nor part of an active first-mate mission. Dangerous, explicit opt-in; the override is audit-logged.")
10638
+ allow_unowned: boolProp("Set true to merge a PR that is neither agent-authored nor correlated to a first-mate unit (unit.pr === pr). Dangerous, explicit opt-in; the override is audit-logged.")
10597
10639
  }, [
10598
10640
  "repo",
10599
10641
  "pr",
@@ -10625,10 +10667,10 @@ function createFirstMateTools(depsOverride = {}) {
10625
10667
  sha: merged.sha
10626
10668
  });
10627
10669
  }),
10628
- tool$1("close_pr", "Close a GitHub pull request WITHOUT merging it. Ownership-scoped identically to merge_pr (agent-authored or active first-mate mission repo, else requires allow_unowned).", objectSchema({
10670
+ tool$1("close_pr", "Closes a live GitHub pull request without merging it and reconciles any correlated first-mate units as terminal in the local ledger. Inputs identify the owner/name repo and PR, with allow_unowned as an explicit override for PRs outside first-mate ownership. Returns the closed state plus reconciliation counts, or an already-closed note when the PR was previously closed. Use when the operator wants to stop an open PR rather than merge it. It is not for merged PRs, or for PRs that are not agent-authored or correlated to a first-mate unit (unit.pr === pr), unless allow_unowned is intentionally set.", objectSchema({
10629
10671
  repo: stringProp("Repository as an owner/name string."),
10630
10672
  pr: numberProp("Pull request number."),
10631
- allow_unowned: boolProp("Set true to close a PR that is neither agent-authored nor part of an active first-mate mission. Explicit opt-in; audit-logged.")
10673
+ allow_unowned: boolProp("Set true to close a PR that is neither agent-authored nor correlated to a first-mate unit (unit.pr === pr). Explicit opt-in; audit-logged.")
10632
10674
  }, ["repo", "pr"]), async (args) => {
10633
10675
  const repoSlug = requiredString(args, "repo");
10634
10676
  const repo = parseRepoSlug(repoSlug);
@@ -10655,10 +10697,10 @@ function createFirstMateTools(depsOverride = {}) {
10655
10697
  reconciled
10656
10698
  });
10657
10699
  }),
10658
- tool$1("mark_ready", "Mark a draft GitHub pull request ready for review. Ownership-scoped identically to merge_pr/close_pr (agent-authored or active first-mate mission repo, else requires allow_unowned).", objectSchema({
10700
+ tool$1("mark_ready", "Marks an open draft GitHub pull request ready for review through GitHub's ready-for-review mutation. Inputs identify the owner/name repo and PR, with allow_unowned as an explicit override for PRs outside first-mate ownership. Returns whether the PR is ready and whether it was already non-draft. Use when a first-mate or operator-owned draft PR should enter human or CI review. It is not for closed or merged PRs, or for PRs that are not agent-authored or correlated to a first-mate unit (unit.pr === pr), unless allow_unowned is intentionally set.", objectSchema({
10659
10701
  repo: stringProp("Repository as an owner/name string."),
10660
10702
  pr: numberProp("Pull request number."),
10661
- allow_unowned: boolProp("Set true to mark a PR ready when it is neither agent-authored nor part of an active first-mate mission. Explicit opt-in; audit-logged.")
10703
+ allow_unowned: boolProp("Set true to mark a PR ready when it is neither agent-authored nor correlated to a first-mate unit (unit.pr === pr). Explicit opt-in; audit-logged.")
10662
10704
  }, ["repo", "pr"]), async (args) => {
10663
10705
  const repoSlug = requiredString(args, "repo");
10664
10706
  const repo = parseRepoSlug(repoSlug);
@@ -10681,7 +10723,7 @@ function createFirstMateTools(depsOverride = {}) {
10681
10723
  alreadyReady: false
10682
10724
  });
10683
10725
  }),
10684
- tool$1("add_units", "Add dispatchable units to an existing active first-mate mission. DependsOn entries are 0-based indices within the submitted units list.", objectSchema({
10726
+ tool$1("add_units", "Adds dispatchable units to an existing active first-mate mission without dispatching them immediately. Inputs name the mission and unit list; each unit supplies a title plus optional repo, provider, same-call dependency indices, and model override. Returns the mission id and the units actually added after validation and deduplication. Use when the operator has decomposed additional work for a mission that is already active. It is not for creating a new mission, adding work to inactive missions, or expressing dependencies on units from earlier calls; dependsOn indices are 0-based within this submitted list, invalid or self indices are ignored, and cycles are rejected.", objectSchema({
10685
10727
  mission_id: stringProp("Mission id to add units to."),
10686
10728
  units: arrayOfObjectsProp("Units to add to the mission.", {
10687
10729
  title: stringProp("Unit title."),
@@ -10711,7 +10753,7 @@ function createFirstMateTools(depsOverride = {}) {
10711
10753
  added: await addUnitsToMission(mission, units, deps, existingUnits)
10712
10754
  });
10713
10755
  }),
10714
- tool$1("abandon_mission", "Mark a first-mate mission abandoned so it drops from the active board. Existing units are marked terminal without merging.", objectSchema({
10756
+ tool$1("abandon_mission", "Marks a first-mate mission abandoned in the local durable ledgers so it drops from the active board, and terminalizes live units without merging. Inputs name the mission and can include a short operator reason returned in the tool result. Returns the abandoned mission id and the number of units marked terminal. Use when the operator is permanently retiring a mission from first-mate tracking. It is local-ledger-only and terminal, not a remote cancellation tool: open PRs stay open and GitHub cloud agents keep running unless the operator closes or stops that work separately.", objectSchema({
10715
10757
  mission_id: stringProp("Mission id to abandon."),
10716
10758
  reason: stringProp("Optional short reason for the abandonment.")
10717
10759
  }, ["mission_id"]), async (args) => {
@@ -10742,7 +10784,7 @@ function createFirstMateTools(depsOverride = {}) {
10742
10784
  ...reason !== void 0 ? { reason } : {}
10743
10785
  });
10744
10786
  }),
10745
- tool$1("mission_status", "Read compact status for all first-mate missions, or for one mission id. Defaults to active missions only; pass include_all for inactive missions too.", objectSchema({
10787
+ tool$1("mission_status", "Reads compact first-mate mission status without waking the controller or dispatching work. Inputs can filter by mission_id and can include inactive missions; active missions are returned by default with inactive counts summarized separately. Returns mission status rows built from the same board data, including counts, blocked count, unit rows, and done/failed summary. Use when the operator needs a mission-id-scoped read or a compact status list. It is not for the whole portfolio board without a mission filter, which is board, and it is not for driving progress, which is advance.", objectSchema({
10746
10788
  mission_id: stringProp("Optional mission id to filter to."),
10747
10789
  include_all: boolProp("When true, include inactive missions in the status list. Default returns active missions only and summarizes inactive counts.")
10748
10790
  }, []), async (args) => {
@@ -18487,7 +18529,7 @@ function toolEnvelope(data, isError) {
18487
18529
  * and (3) opens a WS to the bridge, sends the tool call, awaits the
18488
18530
  * response with a per-tool timeout.
18489
18531
  *
18490
- * Each entry carries `capability: "browser"` so `browserToolsEnabled()`
18532
+ * Each entry carries a browser capability tag so `browserToolsEnabled()`
18491
18533
  * in `src/routes/mcp/handler.ts` drops them at both list-time and
18492
18534
  * call-time when the operator hasn't opted in via `--browse` or
18493
18535
  * `GH_ROUTER_ENABLE_BROWSE=1`.
@@ -18505,7 +18547,7 @@ function toolEnvelope(data, isError) {
18505
18547
  const BROWSER_TOOLS = Object.freeze([
18506
18548
  {
18507
18549
  toolNameHttp: "browser_list_tabs",
18508
- description: "List all open tabs across all browser windows. Returns each tab's id (used by other browser_* tools), URL, title, active flag, and window id.",
18550
+ description: "Lists open tabs across all browser windows. It takes no input and returns each tab's id, URL, title, active flag, and window id. The returned tab ids are the inputs used by tab-scoped browser tools, especially for pre-existing tabs that were not opened by browser_open_tab. It is a power-tier discovery tool for tab selection and inventory, not a page-content reader or navigation tool.",
18509
18551
  inputSchema: {
18510
18552
  type: "object",
18511
18553
  additionalProperties: false,
@@ -18518,7 +18560,7 @@ const BROWSER_TOOLS = Object.freeze([
18518
18560
  },
18519
18561
  {
18520
18562
  toolNameHttp: "browser_open_tab",
18521
- description: "Open a URL in a new browser tab and wait for the page to finish loading. Returns the new tab's id, final URL after redirects, and HTTP status. Refuses to navigate to browser-internal settings / preferences / extensions / flags pages (returns {blocked: true, reason}); devtools://* is allowed.",
18563
+ description: "Opens a URL in a new browser tab, or navigates the currently active tab when reuseActive is true, then waits briefly for the tab load state to reach complete. It takes a URL and optional reuseActive flag, and returns the tab id, final URL, and a synthetic statusCode load flag where 200 means the tab reported complete and 0 means it did not. The statusCode is not the page's HTTP response code, so a loaded 404 page can still return 200. Use this to establish a tab before other browser tools; blocked URLs return {blocked, reason}, including browser settings/preferences/extensions/flags pages, file:// by default, and extension options/popup pages, while devtools:// is allowed.",
18522
18564
  inputSchema: {
18523
18565
  type: "object",
18524
18566
  required: ["url"],
@@ -18526,11 +18568,11 @@ const BROWSER_TOOLS = Object.freeze([
18526
18568
  properties: {
18527
18569
  url: {
18528
18570
  type: "string",
18529
- description: "The URL to load. Maximum 8 KB. Settings / preferences / extensions / flags pages are blocked."
18571
+ description: "URL to load. Browser-internal settings, preferences, extensions, flags, password/management pages, extension options/popup pages, and file:// URLs by default are blocked before dispatch."
18530
18572
  },
18531
18573
  reuseActive: {
18532
18574
  type: "boolean",
18533
- description: "When true, navigate the currently active tab instead of opening a new one. Default false."
18575
+ description: "When true, navigates the currently active tab instead of opening a new tab. Default false. Use browser_navigate when you already know the target tab id."
18534
18576
  }
18535
18577
  }
18536
18578
  },
@@ -18541,7 +18583,7 @@ const BROWSER_TOOLS = Object.freeze([
18541
18583
  },
18542
18584
  {
18543
18585
  toolNameHttp: "browser_close_tab",
18544
- description: "Close one or more tabs by tab id.",
18586
+ description: "Closes one or more browser tabs by id. It takes a non-empty tabIds array, usually obtained from browser_list_tabs, and returns {closed: N} after requesting Chrome to remove those tabs. This is a power-tier tab lifecycle tool for cleanup or closing known throwaway tabs. Avoid using it when the user may still need a tab, and prefer leaving the tab open if the id was not freshly discovered or created for the current task.",
18545
18587
  inputSchema: {
18546
18588
  type: "object",
18547
18589
  required: ["tabIds"],
@@ -18549,7 +18591,7 @@ const BROWSER_TOOLS = Object.freeze([
18549
18591
  properties: { tabIds: {
18550
18592
  type: "array",
18551
18593
  items: { type: "number" },
18552
- description: "Array of tab ids to close (from browser_list_tabs)."
18594
+ description: "Non-empty array of tab ids to close, usually from browser_list_tabs or browser_open_tab."
18553
18595
  } }
18554
18596
  },
18555
18597
  capability: "browser_power",
@@ -18559,7 +18601,7 @@ const BROWSER_TOOLS = Object.freeze([
18559
18601
  },
18560
18602
  {
18561
18603
  toolNameHttp: "browser_navigate",
18562
- description: "Navigate an existing tab: goto a URL, go back, go forward, or reload. Same URL-blocking policy as browser_open_tab.",
18604
+ description: "Navigates an existing tab by going to a URL, moving back or forward in history, or reloading. It takes a tab id plus an action, with url required only for action='goto', and returns {finalUrl, statusCode} for completed navigation or {blocked, reason} for a policy-blocked URL. The statusCode is a synthetic load-complete flag, not the page's HTTP response code. Use this when the target tab already exists; use browser_open_tab to create a new tab, and expect the same URL policy blocks as open_tab, including browser-internal pages, file:// by default, and extension options/popup pages.",
18563
18605
  inputSchema: {
18564
18606
  type: "object",
18565
18607
  required: ["tabId", "action"],
@@ -18567,7 +18609,7 @@ const BROWSER_TOOLS = Object.freeze([
18567
18609
  properties: {
18568
18610
  tabId: {
18569
18611
  type: "number",
18570
- description: "Tab id from browser_list_tabs / browser_open_tab."
18612
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18571
18613
  },
18572
18614
  action: {
18573
18615
  type: "string",
@@ -18577,15 +18619,15 @@ const BROWSER_TOOLS = Object.freeze([
18577
18619
  "forward",
18578
18620
  "reload"
18579
18621
  ],
18580
- description: "The navigation action."
18622
+ description: "Navigation action: goto a URL, go back, go forward, or reload the current page."
18581
18623
  },
18582
18624
  url: {
18583
18625
  type: "string",
18584
- description: "Required when action=goto. Max 8 KB."
18626
+ description: "URL to load when action='goto'. Ignored for back, forward, and reload."
18585
18627
  },
18586
18628
  hard: {
18587
18629
  type: "boolean",
18588
- description: "Reload only: bypass cache (Ctrl+Shift+R behavior). Default false."
18630
+ description: "Reload only: when true, bypasses cache like Ctrl+Shift+R. Default false."
18589
18631
  }
18590
18632
  }
18591
18633
  },
@@ -18596,7 +18638,7 @@ const BROWSER_TOOLS = Object.freeze([
18596
18638
  },
18597
18639
  {
18598
18640
  toolNameHttp: "browser_screenshot",
18599
- description: "Capture a PNG screenshot of the visible area of a tab. Returns base64-encoded image bytes plus contentType. The tab must be active in its window; this tool auto-activates if needed.",
18641
+ description: "Captures a screenshot of the visible area of a tab, as PNG by default or JPEG when requested. It takes a tab id and optional format, then returns base64-encoded image bytes plus contentType. The tab must be active in its window, so this tool auto-activates the tab if needed and that changes which tab is focused. Use screenshot for visual layout, canvas, SVG, maps, or image-only regions; prefer browser_observe when page text and actionable state are enough.",
18600
18642
  inputSchema: {
18601
18643
  type: "object",
18602
18644
  required: ["tabId"],
@@ -18604,12 +18646,12 @@ const BROWSER_TOOLS = Object.freeze([
18604
18646
  properties: {
18605
18647
  tabId: {
18606
18648
  type: "number",
18607
- description: "Tab id from browser_list_tabs / browser_open_tab."
18649
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18608
18650
  },
18609
18651
  format: {
18610
18652
  type: "string",
18611
18653
  enum: ["png", "jpeg"],
18612
- description: "Image format. Default 'png'."
18654
+ description: "Image format for the returned screenshot. Default 'png'; use 'jpeg' when smaller image bytes are preferable."
18613
18655
  }
18614
18656
  }
18615
18657
  },
@@ -18620,7 +18662,7 @@ const BROWSER_TOOLS = Object.freeze([
18620
18662
  },
18621
18663
  {
18622
18664
  toolNameHttp: "browser_read_page",
18623
- description: "Compressed page snapshot for the model: visible text, interactive elements with stable refs, viewport metadata, and (when present) `visualSurfaces` listing canvas / svg regions that need vision. Each element entry carries `bbox: [x, y, w, h]` in CSS viewport pixels (same coord space as browser_mouse / drag / scroll-at-pointer). Refs (e.g. `e42`) are stable for the lifetime of one read_page snapshot and are the preferred input to follow-up actions over brittle CSS selectors. The `viewport` block (`width`, `height`, `devicePixelRatio`, `scrollX`, `scrollY`) lets you map CSS-px bbox to device-px pixels for browser_screenshot. Mode controls what ships back: `summary` (default, ~5-15 KB) returns only viewport-visible elements/text and drops nameless non-interactive nodes; `full` returns up to 200 elements + 256 KiB of innerText (the legacy behavior — use only when you need off-screen content unscrolled). PREFER browser_act / browser_find for intent-driven interaction; read_page is the lower-level snapshot when you need to enumerate.",
18665
+ description: "Returns a compressed page snapshot for a tab: visible text, interactive elements with refs, viewport metadata, and visualSurfaces for canvas or SVG regions that need vision. It takes a tab id and optional mode, and each element includes a ref plus bbox in CSS viewport pixels, the same coordinate space used by browser_mouse, browser_drag, and scroll at-pointer. Refs persist across snapshots of the same document until navigation or DOM replacement, and are a better input to follow-up actions than brittle CSS selectors. Use read_page when enumeration, coordinates, refs, or raw snapshot structure are needed; prefer browser_act or browser_find for intent-driven interaction, browser_observe for a short natural-language page summary, and browser_screenshot for visual pixels.",
18624
18666
  inputSchema: {
18625
18667
  type: "object",
18626
18668
  required: ["tabId"],
@@ -18628,12 +18670,12 @@ const BROWSER_TOOLS = Object.freeze([
18628
18670
  properties: {
18629
18671
  tabId: {
18630
18672
  type: "number",
18631
- description: "Tab id from browser_list_tabs / browser_open_tab."
18673
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18632
18674
  },
18633
18675
  mode: {
18634
18676
  type: "string",
18635
18677
  enum: ["summary", "full"],
18636
- description: "Snapshot scope. Default 'summary' returns viewport-visible elements + text capped at 20 KiB. 'full' returns up to 200 interactive elements page-wide + 256 KiB of innerText."
18678
+ description: "Snapshot scope. Default 'summary' focuses on viewport-visible text and elements; 'full' asks for a broader page-wide snapshot. The default CDP extractor caps around 500 elements and 32 KiB text, with legacy fallback caps possibly lower or higher by mode."
18637
18679
  }
18638
18680
  }
18639
18681
  },
@@ -18644,13 +18686,16 @@ const BROWSER_TOOLS = Object.freeze([
18644
18686
  },
18645
18687
  {
18646
18688
  toolNameHttp: "browser_scroll",
18647
- description: "Scroll a tab. Five modes: top / bottom of the page, by an absolute pixel delta, to a specific element (by ref), or wheel-scroll a sub-region at a pointer location ('at-pointer' the path that works for chat windows / infinite-scroll lists / modal bodies that don't respond to window.scrollTo because they have their own scroll container).",
18689
+ description: "Scrolls a tab or a scrollable region inside a tab. It takes a tab id, target mode, and mode-specific fields for page top/bottom, pixel deltas, element centering, or wheel scrolling at a pointer. The at-pointer path dispatches a real wheel event at a ref, selector, or CSS viewport coordinate, which is the path for chat panes, infinite lists, and modal bodies with their own scroll containers. Use browser_act with action='scroll_into_view' or intent mode for simple element reveal; use browser_scroll when page-level movement, precise deltas, or sub-container wheel scrolling are needed.",
18648
18690
  inputSchema: {
18649
18691
  type: "object",
18650
18692
  required: ["tabId", "target"],
18651
18693
  additionalProperties: false,
18652
18694
  properties: {
18653
- tabId: { type: "number" },
18695
+ tabId: {
18696
+ type: "number",
18697
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18698
+ },
18654
18699
  target: {
18655
18700
  type: "string",
18656
18701
  enum: [
@@ -18660,39 +18705,39 @@ const BROWSER_TOOLS = Object.freeze([
18660
18705
  "element",
18661
18706
  "at-pointer"
18662
18707
  ],
18663
- description: "Scroll target type."
18708
+ description: "Scroll target mode: page top, page bottom, pixel delta, element centering, or wheel event at a pointer."
18664
18709
  },
18665
18710
  pixels: {
18666
18711
  type: "number",
18667
- description: "Pixel delta when target=pixels. Positive scrolls down, negative scrolls up."
18712
+ description: "Pixel delta when target='pixels'. Positive scrolls down and negative scrolls up."
18668
18713
  },
18669
18714
  ref: {
18670
18715
  type: "string",
18671
- description: "Element ref. For target=element, scrolls so the element is centered. For target=at-pointer, resolves to the bbox center as the wheel position."
18716
+ description: "Element ref. For target='element', the element is centered; for target='at-pointer', the element bbox center becomes the wheel position."
18672
18717
  },
18673
18718
  selector: {
18674
18719
  type: "string",
18675
- description: "CSS selector. For target=at-pointer, fallback when no ref. Resolves to bbox center."
18720
+ description: "CSS selector fallback when no ref is available. For target='at-pointer', resolves to the element bbox center."
18676
18721
  },
18677
18722
  x: {
18678
18723
  type: "number",
18679
- description: "Pointer x (CSS viewport px) for target=at-pointer. Pair with y. Exactly one of (ref, selector, or x+y) is required for at-pointer."
18724
+ description: "Pointer x in CSS viewport pixels for target='at-pointer'. Pair with y. Exactly one of ref, selector, or x+y is required for at-pointer."
18680
18725
  },
18681
18726
  y: {
18682
18727
  type: "number",
18683
- description: "Pointer y (CSS viewport px) for target=at-pointer. Pair with x."
18728
+ description: "Pointer y in CSS viewport pixels for target='at-pointer'. Pair with x."
18684
18729
  },
18685
18730
  deltaX: {
18686
18731
  type: "number",
18687
- description: "Wheel delta x (CSS px) for target=at-pointer. Default 0. Clamped to |10000|."
18732
+ description: "Wheel delta x in CSS pixels for target='at-pointer'. Default 0. Clamped to absolute value 10000."
18688
18733
  },
18689
18734
  deltaY: {
18690
18735
  type: "number",
18691
- description: "Wheel delta y (CSS px) for target=at-pointer. Positive scrolls down. Default 0. Clamped to |10000|. At least one of deltaX/deltaY must be non-zero."
18736
+ description: "Wheel delta y in CSS pixels for target='at-pointer'. Positive scrolls down. Default 0. Clamped to absolute value 10000; at least one of deltaX or deltaY must be non-zero."
18692
18737
  },
18693
18738
  force: {
18694
18739
  type: "boolean",
18695
- description: "Skip the pre-wheel elementFromPoint hit-test for target=at-pointer. Default false. Set true when an overlay covers the target but forwards wheel events."
18740
+ description: "For target='at-pointer', skips the pre-wheel elementFromPoint hit-test. Default false. Set true only when an overlay covers the target but forwards wheel events."
18696
18741
  }
18697
18742
  }
18698
18743
  },
@@ -18703,16 +18748,19 @@ const BROWSER_TOOLS = Object.freeze([
18703
18748
  },
18704
18749
  {
18705
18750
  toolNameHttp: "browser_keyboard",
18706
- description: "Send a keystroke or chord to the focused element. Use 'Control+L' / 'Command+L' for browser shortcuts, single characters for typing. Uses chrome.debugger so browser-level shortcuts (Ctrl+T, Ctrl+W, etc) actually fire.",
18751
+ description: "Sends a discrete key or chord to the focused element or browser via CDP Input.dispatchKeyEvent. It takes a tab id and a keys string such as 'Control+L', 'Command+L', 'Enter', 'Escape', or 'ArrowDown', and returns the extension dispatch result. Browser-level shortcuts such as Ctrl+T and Ctrl+W actually fire because this uses chrome.debugger input rather than synthetic DOM events. Use keyboard for shortcuts and non-printable control keys; prefer browser_type for literal text entry into a focused field and browser_act with action='fill' for plain form values.",
18707
18752
  inputSchema: {
18708
18753
  type: "object",
18709
18754
  required: ["tabId", "keys"],
18710
18755
  additionalProperties: false,
18711
18756
  properties: {
18712
- tabId: { type: "number" },
18757
+ tabId: {
18758
+ type: "number",
18759
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18760
+ },
18713
18761
  keys: {
18714
18762
  type: "string",
18715
- description: "Key or chord. Modifiers (Control, Alt, Shift, Meta / Command) joined with '+'. Example: 'Control+L'."
18763
+ description: "Key or chord. Join modifiers with '+', using Control, Ctrl, Alt, Shift, Meta, Command, or Cmd. A single named key such as Enter or Escape is also valid."
18716
18764
  }
18717
18765
  }
18718
18766
  },
@@ -18723,13 +18771,16 @@ const BROWSER_TOOLS = Object.freeze([
18723
18771
  },
18724
18772
  {
18725
18773
  toolNameHttp: "browser_wait",
18726
- description: "Wait for an element to appear (until='selector'), the tab URL to match a regex (until='url'), or the network to go idle (until='networkIdle' - heuristic: tab status complete + 500ms quiet). Returns {ok: true, elapsedMs} on success, {ok: false, reason: 'timeout'} on miss.",
18774
+ description: "Waits for a tab condition without mutating the page. It takes a tab id, an until mode, and the matching operand: a CSS selector for element appearance, a JavaScript regex string for URL matching, or networkIdle for the heuristic of tab status complete plus 500 ms quiet. It returns {ok: true, elapsedMs} on success and {ok: false, reason: 'timeout'} when the condition is not reached before the timeout. Use wait after navigation or actions that trigger asynchronous rendering; do not use it as a page reader, and prefer browser_observe or browser_read_page when the task is to inspect current content.",
18727
18775
  inputSchema: {
18728
18776
  type: "object",
18729
18777
  required: ["tabId", "until"],
18730
18778
  additionalProperties: false,
18731
18779
  properties: {
18732
- tabId: { type: "number" },
18780
+ tabId: {
18781
+ type: "number",
18782
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18783
+ },
18733
18784
  until: {
18734
18785
  type: "string",
18735
18786
  enum: [
@@ -18737,19 +18788,19 @@ const BROWSER_TOOLS = Object.freeze([
18737
18788
  "url",
18738
18789
  "networkIdle"
18739
18790
  ],
18740
- description: "What to wait for."
18791
+ description: "Condition to wait for: selector, URL regex match, or network-idle heuristic."
18741
18792
  },
18742
18793
  selector: {
18743
18794
  type: "string",
18744
- description: "CSS selector when until=selector."
18795
+ description: "CSS selector required when until='selector'."
18745
18796
  },
18746
18797
  urlPattern: {
18747
18798
  type: "string",
18748
- description: "JS regex (string form) when until=url."
18799
+ description: "JavaScript regex source string required when until='url'."
18749
18800
  },
18750
18801
  timeoutMs: {
18751
18802
  type: "number",
18752
- description: "Max wait. Default 10000, hard cap 60000."
18803
+ description: "Maximum wait in milliseconds. Default 10000, hard cap 60000."
18753
18804
  }
18754
18805
  }
18755
18806
  },
@@ -18760,20 +18811,23 @@ const BROWSER_TOOLS = Object.freeze([
18760
18811
  },
18761
18812
  {
18762
18813
  toolNameHttp: "browser_eval_js",
18763
- description: "Evaluate a JavaScript expression in the tab's main world (equivalent to typing in the DevTools console). Returns {result} or {error}. Awaits promises returned by the expression. Single narrowly-named escape hatch for behaviors the other tools don't cover.",
18814
+ description: "Evaluates a JavaScript expression in the tab's main world, equivalent to typing in the DevTools console. It takes a tab id, expression, and optional timeout, awaits promises returned by the expression, and returns {result} or {error}. The expression can read or mutate the page, storage, cookies, or location, so this is the power-tier escape hatch for behaviors the structured browser tools do not cover. Prefer dedicated tools for navigation, clicking, filling, extraction, diagnostics, and screenshots; note that URL policy checks only apply directly to browser_open_tab and browser_navigate, while extension-side navigation blocking still applies to many browser-internal pages.",
18764
18815
  inputSchema: {
18765
18816
  type: "object",
18766
18817
  required: ["tabId", "expression"],
18767
18818
  additionalProperties: false,
18768
18819
  properties: {
18769
- tabId: { type: "number" },
18820
+ tabId: {
18821
+ type: "number",
18822
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18823
+ },
18770
18824
  expression: {
18771
18825
  type: "string",
18772
- description: "JS expression. Max 100 KB. Top-level await NOT supported - wrap in (async () => ...)()."
18826
+ description: "JavaScript expression to evaluate. Size should stay small for reliability, but no schema length cap is enforced. Top-level await is not supported; wrap async work in (async () => ...)()."
18773
18827
  },
18774
18828
  timeoutMs: {
18775
18829
  type: "number",
18776
- description: "Max evaluation time. Default 5000, hard cap 30000."
18830
+ description: "Maximum evaluation time in milliseconds. Default 5000, hard cap 30000."
18777
18831
  }
18778
18832
  }
18779
18833
  },
@@ -18784,7 +18838,7 @@ const BROWSER_TOOLS = Object.freeze([
18784
18838
  },
18785
18839
  {
18786
18840
  toolNameHttp: "browser_download",
18787
- description: "Trigger a download by URL and wait for it to complete. Returns {downloadId, path, bytes, mimeType}. The file lands in Chrome's default Downloads dir unless saveAs is given.",
18841
+ description: "Triggers a browser download from a direct URL and waits for the extension's completion signal. It takes a tab id for association, source='url', the URL, and optional saveAs path, then returns {downloadId, path, bytes, mimeType} when Chrome reports the download complete. The file lands in Chrome's default Downloads directory unless saveAs provides a relative filename or subdirectory, and conflicts are auto-uniquified by the browser. Use this for known direct download URLs; it does not click page links, and the extension currently waits up to 60 seconds internally, so downloads that finish after 60 seconds can report timeout even though the outer wire budget is larger.",
18788
18842
  inputSchema: {
18789
18843
  type: "object",
18790
18844
  required: ["tabId", "url"],
@@ -18792,20 +18846,20 @@ const BROWSER_TOOLS = Object.freeze([
18792
18846
  properties: {
18793
18847
  tabId: {
18794
18848
  type: "number",
18795
- description: "Tab id is logged but the download itself is window-scoped, not tab-scoped."
18849
+ description: "Tab id for association and logging; the download itself is window-scoped, not tab-scoped."
18796
18850
  },
18797
18851
  source: {
18798
18852
  type: "string",
18799
18853
  enum: ["url"],
18800
- description: "Download source. Only 'url' supported in v1; click-then-wait awaits Phase 5."
18854
+ description: "Download source. Only 'url' is supported in v1; click-then-wait is not on this surface."
18801
18855
  },
18802
18856
  url: {
18803
18857
  type: "string",
18804
- description: "Direct URL to download. Max 8 KB."
18858
+ description: "Direct URL to download. No schema length cap is enforced."
18805
18859
  },
18806
18860
  saveAs: {
18807
18861
  type: "string",
18808
- description: "Optional filename / relative subdir under Downloads. Conflicts auto-uniquify."
18862
+ description: "Optional relative filename or subdirectory under Downloads. Chrome enforces download-path restrictions and auto-uniquifies conflicts."
18809
18863
  }
18810
18864
  }
18811
18865
  },
@@ -18816,13 +18870,16 @@ const BROWSER_TOOLS = Object.freeze([
18816
18870
  },
18817
18871
  {
18818
18872
  toolNameHttp: "browser_mouse",
18819
- description: "Move / click / hover / press / release the mouse via real CDP input events (Input.dispatchMouseEvent). Use this when you need behavior that synthetic .click() can't trigger: hover-to-reveal menus, canvas / map / image-map clicks, sites that check event.isTrusted, or precise coordinate targeting. Target with ref (from browser_read_page), CSS selector, or (x, y) in CSS viewport pixels — exactly one. action='move' is the hover (single mouseMoved fires :hover and pointerover reliably). action='dblclick' sends two press/release cycles with incrementing clickCount (a real double-click, not one cycle with clickCount=2). By default the target is hit-tested with elementFromPoint and the call fails with `target_obscured` if the topmost element isn't the target or a descendant — pass force:true to bypass when you know an overlay forwards events.",
18873
+ description: "Moves, clicks, double-clicks, presses, or releases the mouse through real CDP Input.dispatchMouseEvent calls. It takes a tab id, action, exactly one target form (ref, selector, or x+y CSS viewport coordinates), and optional button, trajectory, and force settings. Use mouse for hover-to-reveal menus, canvas/map/image-map clicks, event.isTrusted checks, precise coordinate targeting, or low-level press/release sequences that browser_act cannot express. Prefer browser_act for ordinary element clicks and fills; by default ref/selector targets are hit-tested with elementFromPoint and fail with target_obscured unless force is true.",
18820
18874
  inputSchema: {
18821
18875
  type: "object",
18822
18876
  required: ["tabId", "action"],
18823
18877
  additionalProperties: false,
18824
18878
  properties: {
18825
- tabId: { type: "number" },
18879
+ tabId: {
18880
+ type: "number",
18881
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18882
+ },
18826
18883
  action: {
18827
18884
  type: "string",
18828
18885
  enum: [
@@ -18832,19 +18889,19 @@ const BROWSER_TOOLS = Object.freeze([
18832
18889
  "down",
18833
18890
  "up"
18834
18891
  ],
18835
- description: "What to do. move=position cursor (hover). click=press+release. dblclick=two press+release with clickCount 1 then 2. down=press only. up=release only."
18892
+ description: "Mouse action. move positions the cursor for hover; click sends press+release; dblclick sends two press/release cycles; down presses only; up releases only."
18836
18893
  },
18837
18894
  ref: {
18838
18895
  type: "string",
18839
- description: "Element ref from browser_read_page (preferred). Resolves to bbox center. Exactly one of ref / selector / (x+y) required."
18896
+ description: "Element ref from browser_read_page or browser_find. Resolves to bbox center. Exactly one of ref, selector, or x+y is required."
18840
18897
  },
18841
18898
  selector: {
18842
18899
  type: "string",
18843
- description: "CSS selector (fallback). Resolves to bbox center."
18900
+ description: "CSS selector fallback. Resolves to bbox center. Exactly one of ref, selector, or x+y is required."
18844
18901
  },
18845
18902
  x: {
18846
18903
  type: "number",
18847
- description: "Target x in CSS viewport pixels. Pair with y. Use when working from a screenshot or eval_js output."
18904
+ description: "Target x in CSS viewport pixels. Pair with y. Use when working from a screenshot, canvas coordinate, or eval_js output."
18848
18905
  },
18849
18906
  y: {
18850
18907
  type: "number",
@@ -18857,19 +18914,19 @@ const BROWSER_TOOLS = Object.freeze([
18857
18914
  "right",
18858
18915
  "middle"
18859
18916
  ],
18860
- description: "Mouse button for click / dblclick / down / up. Default 'left'. Ignored for action=move."
18917
+ description: "Mouse button for click, dblclick, down, or up. Default 'left'. Ignored for action='move'."
18861
18918
  },
18862
18919
  steps: {
18863
18920
  type: "number",
18864
- description: "Humanlike trajectory. >1 interpolates the cursor approach over N mouseMoved events. Default 1 (teleport). Clamped to [1, 100]."
18921
+ description: "Trajectory step count. Values greater than 1 interpolate the cursor approach over multiple mouseMoved events. Default 1. Clamped to [1, 100]."
18865
18922
  },
18866
18923
  stepDelayMs: {
18867
18924
  type: "number",
18868
- description: "Pause between interpolated mouseMoved events when steps > 1. Default 8. Clamped to [0, 50]."
18925
+ description: "Pause between interpolated mouseMoved events when steps is greater than 1. Default 8. Clamped to [0, 50]."
18869
18926
  },
18870
18927
  force: {
18871
18928
  type: "boolean",
18872
- description: "Skip the pre-click elementFromPoint hit-test (ref/selector mode only). Default false."
18929
+ description: "For ref or selector targets, skips the elementFromPoint hit-test. Default false. Use only when an overlay covers the target but forwards pointer events."
18873
18930
  }
18874
18931
  }
18875
18932
  },
@@ -18880,20 +18937,23 @@ const BROWSER_TOOLS = Object.freeze([
18880
18937
  },
18881
18938
  {
18882
18939
  toolNameHttp: "browser_drag",
18883
- description: "Drag from a source to a destination. Auto-detects whether to use HTML5 native DnD (for elements with draggable='true', via CDP Input.setInterceptDrags + Input.dispatchDragEvent the only path that triggers Chromium's native dragstart pipeline) or pointer-based DnD (for react-dnd / Sortable.js / mouse-event-based drag handlers via CDP mouse events with buttons:1 held throughout). Each of from/to can be a ref (preferred), a CSS selector, or x+y coordinates. Returns { ok: true, mode_used: 'pointer'|'html5' } so you can verify which path ran.",
18940
+ description: "Drags from a source target to a destination target through CDP input events. It takes a tab id, one source target, one destination target, and optional button, trajectory, mode, and force settings; targets can be refs, selectors, or CSS viewport coordinates. Auto mode chooses HTML5 native drag-and-drop for draggable='true' sources, using Input.setInterceptDrags plus Input.dispatchDragEvent, and otherwise uses pointer drag events for libraries such as react-dnd, Sortable.js, and mouse-event-based handlers. Use drag for actual drag-and-drop interactions; use browser_mouse for simple clicks, hover, or isolated press/release gestures. Returns {ok: true, mode_used, from, to} so the caller can verify whether pointer or html5 ran and which coordinates were used.",
18884
18941
  inputSchema: {
18885
18942
  type: "object",
18886
18943
  required: ["tabId"],
18887
18944
  additionalProperties: false,
18888
18945
  properties: {
18889
- tabId: { type: "number" },
18946
+ tabId: {
18947
+ type: "number",
18948
+ description: "Tab id from browser_list_tabs or browser_open_tab."
18949
+ },
18890
18950
  fromRef: {
18891
18951
  type: "string",
18892
- description: "Source ref from browser_read_page (preferred)."
18952
+ description: "Source element ref from browser_read_page or browser_find. Preferred when available."
18893
18953
  },
18894
18954
  fromSelector: {
18895
18955
  type: "string",
18896
- description: "Source CSS selector (fallback)."
18956
+ description: "Source CSS selector fallback when no source ref is available."
18897
18957
  },
18898
18958
  fromX: {
18899
18959
  type: "number",
@@ -18905,11 +18965,11 @@ const BROWSER_TOOLS = Object.freeze([
18905
18965
  },
18906
18966
  toRef: {
18907
18967
  type: "string",
18908
- description: "Destination ref from browser_read_page (preferred)."
18968
+ description: "Destination element ref from browser_read_page or browser_find. Preferred when available."
18909
18969
  },
18910
18970
  toSelector: {
18911
18971
  type: "string",
18912
- description: "Destination CSS selector (fallback)."
18972
+ description: "Destination CSS selector fallback when no destination ref is available."
18913
18973
  },
18914
18974
  toX: {
18915
18975
  type: "number",
@@ -18922,15 +18982,15 @@ const BROWSER_TOOLS = Object.freeze([
18922
18982
  button: {
18923
18983
  type: "string",
18924
18984
  enum: ["left", "middle"],
18925
- description: "Mouse button held during drag. Default 'left'."
18985
+ description: "Mouse button held during the drag. Default 'left'."
18926
18986
  },
18927
18987
  steps: {
18928
18988
  type: "number",
18929
- description: "Intermediate mouseMoved events fromto with the button held. Drag-detect libraries need a trajectory to fire. Default 15. Clamped to [1, 100]."
18989
+ description: "Intermediate mouseMoved events from source to destination with the button held. Drag-detect libraries often need a trajectory. Default 15. Clamped to [1, 100]."
18930
18990
  },
18931
18991
  stepDelayMs: {
18932
18992
  type: "number",
18933
- description: "Pause between intermediate moves. Default 12. Clamped to [0, 50]."
18993
+ description: "Pause between intermediate moves in milliseconds. Default 12. Clamped to [0, 50]."
18934
18994
  },
18935
18995
  mode: {
18936
18996
  type: "string",
@@ -18939,11 +18999,11 @@ const BROWSER_TOOLS = Object.freeze([
18939
18999
  "pointer",
18940
19000
  "html5"
18941
19001
  ],
18942
- description: "Drag mode. 'auto' (default) picks html5 if the source has draggable='true', else pointer. Override only when auto detection misses."
19002
+ description: "Drag mode. 'auto' is the default and picks html5 if the source has draggable='true', else pointer. Override only when auto detection chooses the wrong path."
18943
19003
  },
18944
19004
  force: {
18945
19005
  type: "boolean",
18946
- description: "Skip the pre-press elementFromPoint hit-test on the source. Default false."
19006
+ description: "Skips the pre-press elementFromPoint hit-test on the source only. Default false. The destination is used as-is."
18947
19007
  }
18948
19008
  }
18949
19009
  },
@@ -18954,20 +19014,23 @@ const BROWSER_TOOLS = Object.freeze([
18954
19014
  },
18955
19015
  {
18956
19016
  toolNameHttp: "browser_type",
18957
- description: "Type a string into the currently-focused element per-keystroke via CDP Input.dispatchKeyEvent. Each character fires keydown + keypress + input this is the tool for keystroke-driven autocomplete, chips, search-as-you-type, and any site whose handlers listen on keydown rather than just reading element.value. For plain form-value entry use browser_fill (faster, sets value directly). For chord shortcuts (Control+L, etc) use browser_keyboard. Special characters in text: \\nEnter, \\tTab, \\bBackspace (dispatched as the named key, not as a literal control char). Other control chars (< 0x20) are rejected with an actionable error. Uppercase letters come from the natural code point event.shiftKey is false but the typed value is correct.",
19017
+ description: "Types text into the currently focused element one character at a time via CDP Input.dispatchKeyEvent. It takes a tab id, text, and optional per-character delay; each character fires keyboard/input events, which supports autocomplete, chips, search-as-you-type fields, and handlers that listen on keydown rather than only reading element.value. Special text characters map to named keys: \\n sends Enter, \\t sends Tab, and \\b sends Backspace; other control characters below 0x20 are rejected with an actionable error. Use browser_type when real keystrokes matter, use browser_act with action='fill' for plain form-value entry, and use browser_keyboard for shortcuts or named control keys such as Control+L or Escape.",
18958
19018
  inputSchema: {
18959
19019
  type: "object",
18960
19020
  required: ["tabId", "text"],
18961
19021
  additionalProperties: false,
18962
19022
  properties: {
18963
- tabId: { type: "number" },
19023
+ tabId: {
19024
+ type: "number",
19025
+ description: "Tab id from browser_list_tabs or browser_open_tab. The text goes to whatever element is currently focused in that tab."
19026
+ },
18964
19027
  text: {
18965
19028
  type: "string",
18966
- description: "The text to type. Max 4096 chars. Iterates as Unicode code points (surrogate pairs handled correctly)."
19029
+ description: "Text to type, up to 4096 Unicode code points. Newline, tab, and backspace are dispatched as Enter, Tab, and Backspace."
18967
19030
  },
18968
19031
  delayMs: {
18969
19032
  type: "number",
18970
- description: "Pause between characters. Default 0. Clamped to [0, 50]. Set > 0 when typing into search-as-you-type inputs that debounce."
19033
+ description: "Pause between characters in milliseconds. Default 0. Clamped to [0, 50]. Set above 0 for debounced search-as-you-type inputs."
18971
19034
  }
18972
19035
  }
18973
19036
  },
@@ -18978,17 +19041,20 @@ const BROWSER_TOOLS = Object.freeze([
18978
19041
  },
18979
19042
  {
18980
19043
  toolNameHttp: "browser_diagnostics",
18981
- description: "Drain console messages or network responses for a tab, with filtering. Replaces the prior browser_console_logs / browser_network_log primitives. `kind` selects the stream; remaining params filter the result before it ships to the model so the response carries only what the caller asked for instead of a raw 1000-entry array dump. Lazy-attach behavior: first call for a tab attaches chrome.debugger; very-early-load events from before the first call are missed.",
19044
+ description: "Drains buffered console messages or network responses for a tab, with filtering before the result is returned. It takes a tab id, kind='console' or 'network', and optional level, regex, and limit filters, then returns {kind, total, returned, entries}; total is the pre-filter count and returned is the post-filter limited count. The first call for a tab lazily attaches chrome.debugger, so very-early load events from before that call are missed. Use diagnostics to investigate console errors, warnings, and request URLs; do not use it as a page-content reader, and raise limit or loosen regex when returned equals the requested limit.",
18982
19045
  inputSchema: {
18983
19046
  type: "object",
18984
19047
  required: ["tabId", "kind"],
18985
19048
  additionalProperties: false,
18986
19049
  properties: {
18987
- tabId: { type: "number" },
19050
+ tabId: {
19051
+ type: "number",
19052
+ description: "Tab id from browser_list_tabs or browser_open_tab."
19053
+ },
18988
19054
  kind: {
18989
19055
  type: "string",
18990
19056
  enum: ["console", "network"],
18991
- description: "Which stream to drain."
19057
+ description: "Diagnostic stream to drain: console messages or network responses."
18992
19058
  },
18993
19059
  level: {
18994
19060
  type: "string",
@@ -19000,15 +19066,15 @@ const BROWSER_TOOLS = Object.freeze([
19000
19066
  "debug",
19001
19067
  "all"
19002
19068
  ],
19003
- description: "Console only. Default 'all'. Ignored when kind=network."
19069
+ description: "Console only. Default 'all'. Ignored when kind='network'."
19004
19070
  },
19005
19071
  regex: {
19006
19072
  type: "string",
19007
- description: "Optional JS-regex string. Console: matches the message body. Network: matches the request URL."
19073
+ description: "Optional JavaScript regex source string. For console, matches message text; for network, matches request URL."
19008
19074
  },
19009
19075
  limit: {
19010
19076
  type: "number",
19011
- description: "Max entries to return after filtering. Default 100. Hard cap 1000."
19077
+ description: "Maximum entries to return after filtering. Default 100. Hard cap 1000."
19012
19078
  }
19013
19079
  }
19014
19080
  },
@@ -19056,20 +19122,23 @@ const BROWSER_TOOLS = Object.freeze([
19056
19122
  },
19057
19123
  {
19058
19124
  toolNameHttp: "browser_find",
19059
- description: "Find up to 5 elements matching a natural-language intent ('the search box at the top', 'the Submit button at the bottom of the login form'). Returns ranked candidates with stable refs the model can pass to browser_act (ref mode) or browser_mouse. Cheaper than browser_read_page when you know what you're looking for the inner compressor (a small fast model) filters the snapshot for you instead of sending the full element list to the lead model.",
19125
+ description: "Finds up to 5 page elements that match a natural-language intent. It takes a tab id and intent, reads a fresh snapshot internally, and returns ranked candidates with refs, roles, names, bboxes, and match reasons when available. The returned refs can be passed to browser_act in REF mode or to low-level power tools such as browser_mouse. Use find when a specific element is needed and a short candidate list is better than the full browser_read_page snapshot; use read_page when broad enumeration or raw text/context is needed.",
19060
19126
  inputSchema: {
19061
19127
  type: "object",
19062
19128
  required: ["tabId", "intent"],
19063
19129
  additionalProperties: false,
19064
19130
  properties: {
19065
- tabId: { type: "number" },
19131
+ tabId: {
19132
+ type: "number",
19133
+ description: "Tab id from browser_list_tabs or browser_open_tab."
19134
+ },
19066
19135
  intent: {
19067
19136
  type: "string",
19068
- description: "Natural-language description of what to find."
19137
+ description: "Natural-language description of the element to find, such as 'the search box at the top' or 'the Submit button'."
19069
19138
  }
19070
19139
  }
19071
19140
  },
19072
- capability: "browser_power",
19141
+ capability: "browser_compound",
19073
19142
  async handler(args, signal) {
19074
19143
  const tabId = typeof args.tabId === "number" ? args.tabId : void 0;
19075
19144
  const intent = typeof args.intent === "string" ? args.intent : "";
@@ -19095,20 +19164,23 @@ const BROWSER_TOOLS = Object.freeze([
19095
19164
  },
19096
19165
  {
19097
19166
  toolNameHttp: "browser_act",
19098
- description: "Preferred for any click / fill / type / scroll-to action against a tab. Two modes: (1) INTENT mode pass `intent` as natural language ('click the submit button'); the inner compressor (a small fast model) maps it to an element + action. Auto-escalates to visual fallback (screenshot + multimodal model + pixel-coord click) when the intent points into a canvas / svg region the a11y tree can't see. (2) REF mode pass `ref` (from a prior browser_find or browser_read_page) and optionally `value`; dispatches directly with zero compressor latency. This is the fold-in path for the now-removed browser_click and browser_fill. Returns {ok, action_taken, target_ref, navigated}.",
19167
+ description: "Performs a high-level click, fill, type, select, or scroll-into-view action against a tab. It has two modes: INTENT mode takes a natural-language intent and resolves the element/action internally, while REF mode takes a ref from browser_find or browser_read_page plus optional action and value for direct dispatch without a compressor round trip. Visual fallback can click canvas or SVG regions by combining screenshot analysis with a coordinate click when text-based matching fails. Use act for ordinary page interaction before reaching for browser_mouse, browser_type, browser_keyboard, or browser_scroll; single-action results include {ok, action_taken, target_ref, navigated}, multi-step intents return summary/steps fields, and visual fallback returns click_visual with x/y coordinates.",
19099
19168
  inputSchema: {
19100
19169
  type: "object",
19101
19170
  required: ["tabId"],
19102
19171
  additionalProperties: false,
19103
19172
  properties: {
19104
- tabId: { type: "number" },
19173
+ tabId: {
19174
+ type: "number",
19175
+ description: "Tab id from browser_list_tabs or browser_open_tab."
19176
+ },
19105
19177
  intent: {
19106
19178
  type: "string",
19107
- description: "Natural-language description of the action. Triggers INTENT mode. Mutually exclusive with `ref`."
19179
+ description: "Natural-language description of the action for INTENT mode. If both intent and ref are provided, ref mode wins and intent is ignored."
19108
19180
  },
19109
19181
  ref: {
19110
19182
  type: "string",
19111
- description: "Element ref from browser_find / browser_read_page. Triggers REF mode (no compressor round-trip)."
19183
+ description: "Element ref from browser_find or browser_read_page for REF mode, which dispatches directly without a compressor round trip."
19112
19184
  },
19113
19185
  action: {
19114
19186
  type: "string",
@@ -19119,15 +19191,15 @@ const BROWSER_TOOLS = Object.freeze([
19119
19191
  "select",
19120
19192
  "scroll_into_view"
19121
19193
  ],
19122
- description: "REF mode only. Defaults to 'click'. In INTENT mode, the compressor picks the action."
19194
+ description: "REF mode action. Defaults to 'click'. Ignored in INTENT mode, where the resolved action comes from the intent and matched element."
19123
19195
  },
19124
19196
  value: {
19125
19197
  type: "string",
19126
- description: "For fill / type / select: the string value to set. In INTENT mode the compressor uses this when an action requires a value."
19198
+ description: "String value for fill, type, or select actions. In INTENT mode, this is available to the resolver when the action requires a value."
19127
19199
  }
19128
19200
  }
19129
19201
  },
19130
- capability: "browser",
19202
+ capability: "browser_compound",
19131
19203
  async handler(args, signal) {
19132
19204
  const tabId = typeof args.tabId === "number" ? args.tabId : void 0;
19133
19205
  if (!tabId) return toolEnvelope({ error: "tabId required" }, true);
@@ -19220,16 +19292,19 @@ const BROWSER_TOOLS = Object.freeze([
19220
19292
  },
19221
19293
  {
19222
19294
  toolNameHttp: "browser_observe",
19223
- description: "Get a natural-language description of the current page's user-actionable state what forms, buttons, links, and content sections are visible in 2-4 sentences. Optional `intent` focuses the description on a region ('describe the login form', 'what's in the comments section'). Use this BEFORE browser_act when you don't know what's on the page, or AFTER navigation to confirm the page loaded. Cheaper than screenshots when text is enough. Does not include canvas/SVG content those surface as a `hasVisualSurfaces` flag; switch to browser_screenshot for visuals.",
19295
+ description: "Produces a short natural-language description of the current page's user-actionable state, including visible forms, buttons, links, and content sections. It takes a tab id and optional intent focus, then returns a 2-4 sentence summary plus whether visualSurfaces such as canvas or SVG are present. Use observe before browser_act when the page state is unknown, or after navigation to confirm what loaded. Prefer observe over screenshot when text and controls are enough; switch to browser_screenshot for visual layout or canvas/SVG details, and use browser_read_page when raw refs, bboxes, or element lists are needed.",
19224
19296
  inputSchema: {
19225
19297
  type: "object",
19226
19298
  required: ["tabId"],
19227
19299
  additionalProperties: false,
19228
19300
  properties: {
19229
- tabId: { type: "number" },
19301
+ tabId: {
19302
+ type: "number",
19303
+ description: "Tab id from browser_list_tabs or browser_open_tab."
19304
+ },
19230
19305
  intent: {
19231
19306
  type: "string",
19232
- description: "Optional natural-language focus ('describe the form', 'what's in the sidebar')."
19307
+ description: "Optional natural-language focus for the summary, such as 'describe the form' or 'what is in the sidebar'."
19233
19308
  }
19234
19309
  }
19235
19310
  },
@@ -19243,7 +19318,7 @@ const BROWSER_TOOLS = Object.freeze([
19243
19318
  },
19244
19319
  {
19245
19320
  toolNameHttp: "browser_extract",
19246
- description: "Structured extraction from the current page into a JSON object matching the provided schema. The inner compressor reads the page snapshot (text + elements) and synthesizes the typed object. Use this instead of browser_read_page + lead-model parsing when you know the shape you want (e.g. a list of {title, author, url} rows from a PR list).",
19321
+ description: "Extracts structured data from the current page into a JSON object matching the provided schema. It takes a tab id, a schema or schema-shaped descriptor, and a plain-language instruction; the inner compressor reads the page snapshot and returns only the typed object rather than the raw element list. Use extract when the desired output shape is known, such as rows of {title, author, url}; use browser_observe for a prose overview and browser_read_page when the lead model needs raw refs, bboxes, or page text. Bad schemas or wrong-shape compressor results are returned as fixable error envelopes so the caller can simplify the schema or clarify the instruction.",
19247
19322
  inputSchema: {
19248
19323
  type: "object",
19249
19324
  required: [
@@ -19253,11 +19328,14 @@ const BROWSER_TOOLS = Object.freeze([
19253
19328
  ],
19254
19329
  additionalProperties: false,
19255
19330
  properties: {
19256
- tabId: { type: "number" },
19257
- schema: { description: "JSON schema (or schema-shaped descriptor) for the desired output shape." },
19331
+ tabId: {
19332
+ type: "number",
19333
+ description: "Tab id from browser_list_tabs or browser_open_tab."
19334
+ },
19335
+ schema: { description: "JSON schema, or a schema-shaped descriptor, for the desired output shape." },
19258
19336
  instruction: {
19259
19337
  type: "string",
19260
- description: "What to extract, in plain language ('the visible PR list')."
19338
+ description: "Plain-language extraction instruction, such as 'the visible PR list' or 'all product cards with price and URL'."
19261
19339
  }
19262
19340
  }
19263
19341
  },
@@ -23432,12 +23510,17 @@ async function countTokens(body, extraHeaders, callerSignal, retryTransient = fa
23432
23510
  * slug too — `state.models?.data` mirrors Copilot's catalog where these
23433
23511
  * land under the dotted slug, so we match by Copilot's actual id shape.
23434
23512
  */
23513
+ function geminiAvailable(source = state) {
23514
+ const models = source.models?.data;
23515
+ if (!models) return false;
23516
+ return models.some((m) => /^gemini-3\..*pro/i.test(m.id));
23517
+ }
23435
23518
  function standInToolEnabled() {
23436
23519
  const models = state.models?.data;
23437
23520
  if (!models) return false;
23438
23521
  const hasGpt55 = models.some((m) => m.id === "gpt-5.5");
23439
23522
  const hasOpus = models.some((m) => m.id === "claude-opus-4-7" || m.id === "claude-opus-4.7");
23440
- const hasGeminiPro = models.some((m) => /^gemini-3\..*pro/i.test(m.id));
23523
+ const hasGeminiPro = geminiAvailable();
23441
23524
  return hasGpt55 && hasOpus && hasGeminiPro;
23442
23525
  }
23443
23526
  const IMPLEMENTER_SUBAGENT_MODEL = "gpt-5.5";
@@ -23483,8 +23566,8 @@ function workerToolsEnabled() {
23483
23566
  return found.capabilities?.supports?.tool_calls === true;
23484
23567
  }
23485
23568
  /**
23486
- * Gate for the compound L2 browser tools (`browser_find`, `browser_act`
23487
- * in intent mode, `browser_extract`).
23569
+ * Gate for the compound L2 browser tools (`browser_act`, `browser_observe`,
23570
+ * `browser_extract`, `browser_find`).
23488
23571
  *
23489
23572
  * Returns true iff `compressorAvailable()` — i.e. at least one model in
23490
23573
  * the compressor fallback chain (`gpt-5.4-mini` → `claude-sonnet-4.6` →
@@ -23507,19 +23590,19 @@ function browserCompoundToolsEnabled() {
23507
23590
  * Gate for the L0/L1 power browser tools (`browser_read_page`,
23508
23591
  * `browser_mouse`, `browser_drag`, `browser_type`, `browser_keyboard`,
23509
23592
  * `browser_scroll`, `browser_eval_js`, `browser_diagnostics`,
23510
- * `browser_find`, `browser_close_tab`, `browser_list_tabs`,
23511
- * `browser_wait`, `browser_download`).
23593
+ * `browser_close_tab`, `browser_list_tabs`, `browser_wait`,
23594
+ * `browser_download`).
23512
23595
  *
23513
23596
  * Returns true iff `state.powerBrowseEnabled` (set by `--power-browse`
23514
23597
  * or `GH_ROUTER_ENABLE_POWER_BROWSE=1`). When off, the default
23515
- * `--browse` surface exposes only the 6 lead-model tools (`act`,
23516
- * `observe`, `extract`, `navigate`, `screenshot`, `open_tab`) that
23517
- * hide DOM details behind intent. Power mode adds the raw primitives
23518
- * for users who want direct coord/keystroke control.
23598
+ * `--browse` surface exposes the base lead tools (`navigate`, `screenshot`,
23599
+ * `open_tab`) and, when the compound gate passes, `act`, `observe`,
23600
+ * `extract`, and `find`. Power mode adds the raw primitives for users who
23601
+ * want direct coord/keystroke control.
23519
23602
  *
23520
23603
  * `handler.ts` filter chain ANDs this with `browserToolsEnabled()`
23521
- * (defense-in-depth power without basic is meaningless and the
23522
- * setup path already forces basic on when power is on).
23604
+ * (defense-in-depth: power without the base browser server is meaningless and
23605
+ * the setup path already forces basic on when power is on).
23523
23606
  */
23524
23607
  function browserPowerToolsEnabled() {
23525
23608
  return state.powerBrowseEnabled === true;
@@ -23715,11 +23798,6 @@ function checkAuth(c) {
23715
23798
  };
23716
23799
  return { ok: true };
23717
23800
  }
23718
- function geminiAvailable() {
23719
- const models = state.models?.data;
23720
- if (!models) return false;
23721
- return models.some((m) => /^gemini-3\..*pro/i.test(m.id));
23722
- }
23723
23801
  /**
23724
23802
  * The 1M-context Opus 4.6 variant (`claude-opus-4.6-1m`, `max_prompt_tokens`
23725
23803
  * 936K). opus_critic prefers it so it can take large artifacts in one shot
@@ -28220,6 +28298,142 @@ function round2(n) {
28220
28298
  * into each sub-orchestration. The verifier only range-checks the declaration. */
28221
28299
  const MAX_RECURSION_DEPTH = 3;
28222
28300
 
28301
+ //#endregion
28302
+ //#region src/lib/orchestration/select.ts
28303
+ const subsetOf = (a, b) => {
28304
+ for (const x of a) if (!b.has(x)) return false;
28305
+ return true;
28306
+ };
28307
+ function selectChampion(orchestrated, baseline, canonicalGateIds, tiePolicy) {
28308
+ if (!subsetOf(orchestrated.passed, orchestrated.ran)) return {
28309
+ winner: "baseline",
28310
+ reason: "orchestrated outcome malformed (passed not a subset of ran)"
28311
+ };
28312
+ if (!subsetOf(baseline.passed, baseline.ran)) return {
28313
+ winner: "baseline",
28314
+ reason: "baseline outcome malformed (passed not a subset of ran)"
28315
+ };
28316
+ if (canonicalGateIds.size === 0) return {
28317
+ winner: "baseline",
28318
+ reason: "no executable gate for this ask — ship the baseline (judgment-only)"
28319
+ };
28320
+ for (const id of canonicalGateIds) if (!orchestrated.ran.has(id)) return {
28321
+ winner: "baseline",
28322
+ reason: `orchestrated did not run canonical gate "${id}"`
28323
+ };
28324
+ let baselinePass = 0;
28325
+ let orchestratedPass = 0;
28326
+ for (const id of canonicalGateIds) {
28327
+ if (baseline.passed.has(id)) baselinePass += 1;
28328
+ if (orchestrated.passed.has(id)) orchestratedPass += 1;
28329
+ else if (baseline.passed.has(id)) return {
28330
+ winner: "baseline",
28331
+ reason: `orchestrated regresses on canonical check "${id}" the baseline passed`
28332
+ };
28333
+ }
28334
+ if (orchestratedPass > baselinePass) return {
28335
+ winner: "orchestrated",
28336
+ reason: "orchestrated passes strictly more canonical executable checks"
28337
+ };
28338
+ if (tiePolicy === "superset") return {
28339
+ winner: "orchestrated",
28340
+ reason: "orchestrated matches the baseline on the canonical checks (superset policy)"
28341
+ };
28342
+ return {
28343
+ winner: "baseline",
28344
+ reason: "orchestrated does not pass strictly more canonical checks than the baseline (strict policy)"
28345
+ };
28346
+ }
28347
+
28348
+ //#endregion
28349
+ //#region src/lib/orchestration/gate-runner.ts
28350
+ async function runGateChecks(checks, cwd, exec) {
28351
+ const results = await Promise.all(checks.map(async (c) => {
28352
+ try {
28353
+ const r = await exec({
28354
+ command: c.command,
28355
+ cwd
28356
+ });
28357
+ return {
28358
+ id: c.id,
28359
+ passed: r.exitCode === 0
28360
+ };
28361
+ } catch {
28362
+ return {
28363
+ id: c.id,
28364
+ passed: false
28365
+ };
28366
+ }
28367
+ }));
28368
+ const passed = /* @__PURE__ */ new Set();
28369
+ const ran = /* @__PURE__ */ new Set();
28370
+ for (const r of results) {
28371
+ ran.add(r.id);
28372
+ if (r.passed) passed.add(r.id);
28373
+ }
28374
+ return {
28375
+ passed,
28376
+ ran
28377
+ };
28378
+ }
28379
+
28380
+ //#endregion
28381
+ //#region src/lib/orchestration/gate-registry.ts
28382
+ /**
28383
+ * Built-in sealed gates. Commands follow this repo's TS/Bun conventions (the
28384
+ * `bun run <script>` indirection means a repo without that script simply fails
28385
+ * the check, which the selector treats as not-passed rather than a crash). New
28386
+ * ecosystems get a new sealed id here, never a caller-supplied command.
28387
+ */
28388
+ const SEALED_GATES = {
28389
+ "default-ci": [
28390
+ {
28391
+ id: "typecheck",
28392
+ command: "bun run typecheck"
28393
+ },
28394
+ {
28395
+ id: "test",
28396
+ command: "bun test"
28397
+ },
28398
+ {
28399
+ id: "lint",
28400
+ command: "bun run lint"
28401
+ }
28402
+ ],
28403
+ "typecheck-test": [{
28404
+ id: "typecheck",
28405
+ command: "bun run typecheck"
28406
+ }, {
28407
+ id: "test",
28408
+ command: "bun test"
28409
+ }],
28410
+ "typecheck-only": [{
28411
+ id: "typecheck",
28412
+ command: "bun run typecheck"
28413
+ }]
28414
+ };
28415
+ /** The set of sealed gate ids, used as the kernel's `knownGateIds` so the IR
28416
+ * verifier rejects an executable gate that references an unregistered id. */
28417
+ function sealedGateIds() {
28418
+ return new Set(Object.keys(SEALED_GATES));
28419
+ }
28420
+ /**
28421
+ * Resolve a sealed gate by id. Returns a DEFENSIVE CLONE (fresh objects) so a
28422
+ * caller can never mutate the registry's command set. `undefined` for an
28423
+ * unknown id, which `run_workflow` rejects before executing anything.
28424
+ */
28425
+ function resolveSealedGate(gateId) {
28426
+ const checks = SEALED_GATES[gateId];
28427
+ if (!checks) return void 0;
28428
+ return {
28429
+ id: gateId,
28430
+ checks: checks.map((c) => ({
28431
+ id: c.id,
28432
+ command: c.command
28433
+ }))
28434
+ };
28435
+ }
28436
+
28223
28437
  //#endregion
28224
28438
  //#region src/lib/orchestration/verify.ts
28225
28439
  const VALID_ROLES = new Set([
@@ -28244,6 +28458,7 @@ const VALID_ON_FAIL = new Set([
28244
28458
  "escalate"
28245
28459
  ]);
28246
28460
  function verifyWorkflowIR(ir, opts = {}) {
28461
+ const knownGateIds = opts.knownGateIds ?? sealedGateIds();
28247
28462
  const v = [];
28248
28463
  const push = (code, message, nodeId) => {
28249
28464
  v.push(nodeId === void 0 ? {
@@ -28321,7 +28536,7 @@ function verifyWorkflowIR(ir, opts = {}) {
28321
28536
  const g = n.gate;
28322
28537
  if (g.kind === "executable") {
28323
28538
  if (typeof g.gateId !== "string" || g.gateId.length === 0) push("BAD_GATE", `executable gate on node "${n.id}" must reference a sealed gateId (gate-immutability)`, n.id);
28324
- else if (opts.knownGateIds && !opts.knownGateIds.has(g.gateId)) push("UNKNOWN_GATE_ID", `executable gate on node "${n.id}" references gateId "${g.gateId}" not in the kernel's sealed-gate registry`, n.id);
28539
+ else if (!knownGateIds.has(g.gateId)) push("UNKNOWN_GATE_ID", `executable gate on node "${n.id}" references gateId "${g.gateId}" not in the kernel's sealed-gate registry`, n.id);
28325
28540
  }
28326
28541
  if (g.kind === "cross_lab") {
28327
28542
  if (typeof g.checkerLab !== "string" || g.checkerLab.length === 0) push("BAD_GATE", `cross_lab gate on node "${n.id}" must name a checkerLab`, n.id);
@@ -28428,53 +28643,6 @@ function hasCycle(nodes, byId) {
28428
28643
  return false;
28429
28644
  }
28430
28645
 
28431
- //#endregion
28432
- //#region src/lib/orchestration/select.ts
28433
- const subsetOf = (a, b) => {
28434
- for (const x of a) if (!b.has(x)) return false;
28435
- return true;
28436
- };
28437
- function selectChampion(orchestrated, baseline, canonicalGateIds, tiePolicy) {
28438
- if (!subsetOf(orchestrated.passed, orchestrated.ran)) return {
28439
- winner: "baseline",
28440
- reason: "orchestrated outcome malformed (passed not a subset of ran)"
28441
- };
28442
- if (!subsetOf(baseline.passed, baseline.ran)) return {
28443
- winner: "baseline",
28444
- reason: "baseline outcome malformed (passed not a subset of ran)"
28445
- };
28446
- if (canonicalGateIds.size === 0) return {
28447
- winner: "baseline",
28448
- reason: "no executable gate for this ask — ship the baseline (judgment-only)"
28449
- };
28450
- for (const id of canonicalGateIds) if (!orchestrated.ran.has(id)) return {
28451
- winner: "baseline",
28452
- reason: `orchestrated did not run canonical gate "${id}"`
28453
- };
28454
- let baselinePass = 0;
28455
- let orchestratedPass = 0;
28456
- for (const id of canonicalGateIds) {
28457
- if (baseline.passed.has(id)) baselinePass += 1;
28458
- if (orchestrated.passed.has(id)) orchestratedPass += 1;
28459
- else if (baseline.passed.has(id)) return {
28460
- winner: "baseline",
28461
- reason: `orchestrated regresses on canonical check "${id}" the baseline passed`
28462
- };
28463
- }
28464
- if (orchestratedPass > baselinePass) return {
28465
- winner: "orchestrated",
28466
- reason: "orchestrated passes strictly more canonical executable checks"
28467
- };
28468
- if (tiePolicy === "superset") return {
28469
- winner: "orchestrated",
28470
- reason: "orchestrated matches the baseline on the canonical checks (superset policy)"
28471
- };
28472
- return {
28473
- winner: "baseline",
28474
- reason: "orchestrated does not pass strictly more canonical checks than the baseline (strict policy)"
28475
- };
28476
- }
28477
-
28478
28646
  //#endregion
28479
28647
  //#region src/lib/orchestration/kernel.ts
28480
28648
  const DEFAULT_MAX_RETRIES = 2;
@@ -28594,6 +28762,7 @@ const clone = (v) => typeof structuredClone === "function" ? structuredClone(v)
28594
28762
  async function decomposeWorkflow(ask, deps, opts = {}) {
28595
28763
  const maxRounds = Math.max(1, opts.maxRounds ?? DEFAULT_MAX_ROUNDS);
28596
28764
  const verifyOpts = opts.verify ?? {};
28765
+ const context = typeof opts.context === "string" && opts.context.trim().length > 0 ? opts.context.trim() : void 0;
28597
28766
  let feedback;
28598
28767
  let lastViolations = [{
28599
28768
  code: "NO_DRAFT",
@@ -28603,6 +28772,7 @@ async function decomposeWorkflow(ask, deps, opts = {}) {
28603
28772
  for (let round = 1; round <= maxRounds; round += 1) {
28604
28773
  const drafted = await safeDraft(deps, {
28605
28774
  ask,
28775
+ context,
28606
28776
  feedback
28607
28777
  });
28608
28778
  attempts += 1;
@@ -28627,6 +28797,7 @@ async function decomposeWorkflow(ask, deps, opts = {}) {
28627
28797
  if (round < maxRounds) {
28628
28798
  const next = await safeDraft(deps, {
28629
28799
  ask,
28800
+ context,
28630
28801
  feedback: concerns
28631
28802
  });
28632
28803
  attempts += 1;
@@ -28852,38 +29023,6 @@ function detectGateWeakening(diff) {
28852
29023
  };
28853
29024
  }
28854
29025
 
28855
- //#endregion
28856
- //#region src/lib/orchestration/gate-runner.ts
28857
- async function runGateChecks(checks, cwd, exec) {
28858
- const results = await Promise.all(checks.map(async (c) => {
28859
- try {
28860
- const r = await exec({
28861
- command: c.command,
28862
- cwd
28863
- });
28864
- return {
28865
- id: c.id,
28866
- passed: r.exitCode === 0
28867
- };
28868
- } catch {
28869
- return {
28870
- id: c.id,
28871
- passed: false
28872
- };
28873
- }
28874
- }));
28875
- const passed = /* @__PURE__ */ new Set();
28876
- const ran = /* @__PURE__ */ new Set();
28877
- for (const r of results) {
28878
- ran.add(r.id);
28879
- if (r.passed) passed.add(r.id);
28880
- }
28881
- return {
28882
- passed,
28883
- ran
28884
- };
28885
- }
28886
-
28887
29026
  //#endregion
28888
29027
  //#region src/lib/orchestration/stop-gate.ts
28889
29028
  async function evaluateStopGate(input) {
@@ -28929,63 +29068,6 @@ const liveExec = async ({ command, cwd }) => {
28929
29068
  }
28930
29069
  };
28931
29070
 
28932
- //#endregion
28933
- //#region src/lib/orchestration/gate-registry.ts
28934
- /**
28935
- * Built-in sealed gates. Commands follow this repo's TS/Bun conventions (the
28936
- * `bun run <script>` indirection means a repo without that script simply fails
28937
- * the check, which the selector treats as not-passed rather than a crash). New
28938
- * ecosystems get a new sealed id here, never a caller-supplied command.
28939
- */
28940
- const SEALED_GATES = {
28941
- "default-ci": [
28942
- {
28943
- id: "typecheck",
28944
- command: "bun run typecheck"
28945
- },
28946
- {
28947
- id: "test",
28948
- command: "bun test"
28949
- },
28950
- {
28951
- id: "lint",
28952
- command: "bun run lint"
28953
- }
28954
- ],
28955
- "typecheck-test": [{
28956
- id: "typecheck",
28957
- command: "bun run typecheck"
28958
- }, {
28959
- id: "test",
28960
- command: "bun test"
28961
- }],
28962
- "typecheck-only": [{
28963
- id: "typecheck",
28964
- command: "bun run typecheck"
28965
- }]
28966
- };
28967
- /** The set of sealed gate ids, used as the kernel's `knownGateIds` so the IR
28968
- * verifier rejects an executable gate that references an unregistered id. */
28969
- function sealedGateIds() {
28970
- return new Set(Object.keys(SEALED_GATES));
28971
- }
28972
- /**
28973
- * Resolve a sealed gate by id. Returns a DEFENSIVE CLONE (fresh objects) so a
28974
- * caller can never mutate the registry's command set. `undefined` for an
28975
- * unknown id, which `run_workflow` rejects before executing anything.
28976
- */
28977
- function resolveSealedGate(gateId) {
28978
- const checks = SEALED_GATES[gateId];
28979
- if (!checks) return void 0;
28980
- return {
28981
- id: gateId,
28982
- checks: checks.map((c) => ({
28983
- id: c.id,
28984
- command: c.command
28985
- }))
28986
- };
28987
- }
28988
-
28989
29071
  //#endregion
28990
29072
  //#region src/lib/orchestration/runner-live.ts
28991
29073
  /** Map a node role to the worker-engine mode. `baseline` is pre-mapped to
@@ -29476,6 +29558,19 @@ async function decideStopHook(input) {
29476
29558
  let dynamicBaselineKey;
29477
29559
  const planMode = input.planMode === true || payload.plan_mode === true;
29478
29560
  const scanDiff = (diff) => planMode ? stripPlanMemoryDiffHunks(diff) : diff;
29561
+ const capturedDiff = async (workdir) => {
29562
+ try {
29563
+ return {
29564
+ diff: await input.captureDiff(workdir),
29565
+ captured: true
29566
+ };
29567
+ } catch {
29568
+ return {
29569
+ diff: "",
29570
+ captured: false
29571
+ };
29572
+ }
29573
+ };
29479
29574
  const runGate = async () => {
29480
29575
  if (input.resolveChecks) {
29481
29576
  const resolved = await input.resolveChecks(cwd).catch(() => null);
@@ -29483,27 +29578,37 @@ async function decideStopHook(input) {
29483
29578
  resolvedKey = resolved.descriptorKey;
29484
29579
  dynamicBaselineKey = resolved.baselineKey;
29485
29580
  const workdir = resolved.workdir || cwd;
29486
- const diff$1 = await input.captureDiff(workdir).catch(() => "");
29581
+ const { diff: diff$1, captured: captured$1 } = await capturedDiff(workdir);
29582
+ if (captured$1 && diff$1.length === 0) return {
29583
+ kind: "no-diff",
29584
+ diff: diff$1
29585
+ };
29487
29586
  const result$1 = await evaluateStopGate({
29488
29587
  checks: resolved.checks,
29489
29588
  cwd: workdir,
29490
29589
  exec: input.exec,
29491
- diff: scanDiff(diff$1)
29590
+ diff: captured$1 ? scanDiff(diff$1) : ""
29492
29591
  });
29493
29592
  return {
29593
+ kind: "evaluated",
29494
29594
  failedChecks: [...result$1.failedChecks],
29495
29595
  weakeningPatterns: [...new Set(result$1.weakening.map((w) => w.pattern))],
29496
29596
  diff: diff$1
29497
29597
  };
29498
29598
  }
29499
- const diff = await input.captureDiff(cwd).catch(() => "");
29599
+ const { diff, captured } = await capturedDiff(cwd);
29600
+ if (captured && diff.length === 0) return {
29601
+ kind: "no-diff",
29602
+ diff
29603
+ };
29500
29604
  const result = await runStopGateForLaunch({
29501
29605
  workspace: cwd,
29502
29606
  gateId: input.gateId,
29503
29607
  exec: input.exec,
29504
- diff: scanDiff(diff)
29608
+ diff: captured ? scanDiff(diff) : ""
29505
29609
  });
29506
29610
  return {
29611
+ kind: "evaluated",
29507
29612
  failedChecks: [...result.failedChecks],
29508
29613
  weakeningPatterns: [...new Set(result.weakening.map((w) => w.pattern))],
29509
29614
  diff
@@ -29517,6 +29622,7 @@ async function decideStopHook(input) {
29517
29622
  if (timer) clearTimeout(timer);
29518
29623
  if (raced === "timeout") return { exitCode: 0 };
29519
29624
  if (raced === null) return { exitCode: 0 };
29625
+ if (raced.kind === "no-diff") return { exitCode: 0 };
29520
29626
  const baselineKey = dynamicBaselineKey ?? JSON.stringify([
29521
29627
  sessionId,
29522
29628
  cwd,
@@ -29804,8 +29910,8 @@ function buildLiveDecomposeDeps(opts) {
29804
29910
  endpoint: "/v1/messages",
29805
29911
  effort: "xhigh"
29806
29912
  };
29807
- const deps = { async draftIR({ ask, feedback }) {
29808
- const userText = `Ask:\n${ask}` + (feedback && feedback.length > 0 ? `\n\nFix these issues from the previous draft:\n- ${feedback.join("\n- ")}` : "");
29913
+ const deps = { async draftIR({ ask, context, feedback }) {
29914
+ const userText = `Ask:\n${ask}` + (context && context.trim().length > 0 ? `\n\nContext:\n${context.trim()}` : "") + (feedback && feedback.length > 0 ? `\n\nFix these issues from the previous draft:\n- ${feedback.join("\n- ")}` : "");
29809
29915
  return extractJson(await dispatchModelCall({
29810
29916
  model: driver.model,
29811
29917
  endpoint: driver.endpoint,
@@ -30154,9 +30260,9 @@ Reply format (markdown):
30154
30260
 
30155
30261
  Resilience reminder:
30156
30262
  If your session terminates abnormally before "Status: complete", the lead will retry once. On recovery, ask the lead to confirm what's already been done before re-applying changes — duplicate edits are worse than a slow restart.`;
30157
- const OPUS_CRITIC_BASE = `You are opus-critic, a fresh-context Anthropic-side adversarial reviewer running on Claude Opus 4.7 the same model and lab as the lead orchestrator that just delegated to you. You are NOT the lead. You did not see the lead's reasoning trace. You only see the brief.
30263
+ const OPUS_CRITIC_BASE = `You are opus-critic, a fresh-context same-lab adversarial reviewer running on Opus 4.6. The lead orchestrator that just delegated to you runs newer Opus-family context, but you are NOT the lead. You did not see the lead's reasoning trace. You only see the brief.
30158
30264
 
30159
- Your job is to spot what the lead missed because of cognitive momentum, sunk-cost on a plan, or motivated reasoning toward a particular fix. Your blind-spot diversification is LIMITED compared to codex-critic (gpt-5.5) and gemini-critic (gemini-3.1-pro) same training, same lab, same RLHF priors. Use that honestly: don't pretend to find a different perspective when the obvious read is "the lead got it right." Silence on good work is a valid and welcome answer.
30265
+ Your job is to spot what the lead missed because of cognitive momentum, sunk-cost on a plan, or motivated reasoning toward a particular fix. Your blind-spot diversification is LIMITED compared to codex-critic (gpt-5.5) and gemini-critic (gemini-3.1-pro), same lab, adjacent model family, related priors. Use that honestly: don't pretend to find a different perspective when the obvious read is "the lead got it right." Silence on good work is a valid and welcome answer.
30160
30266
 
30161
30267
  Sycophancy is the failure mode you exist to fight. Manufactured contrarianism is a different failure of the same shape — do neither.
30162
30268
 
@@ -30169,7 +30275,7 @@ const PERSONAS_READ = Object.freeze([
30169
30275
  toolNameHttp: "codex_critic",
30170
30276
  model: "gpt-5.5",
30171
30277
  endpoint: "/v1/responses",
30172
- description: "Adversarial second opinion on plans, designs, or code tradeoffs. Backed by gpt-5.5 (OpenAI, ≈922K-token input window) strongest reasoning model in the critic lineup, different lab than Opus. Best for architecture decisions, design reviews, and tradeoff analysis where cross-lab diversity matters. Not for line-level code review (use codex_reviewer). Pass artifact verbatim.",
30278
+ description: "Adversarial architecture and design critic backed by gpt-5.5 (OpenAI, ≈922K-token input window), the strongest cross-lab reasoning critic in this surface. It reviews plans, designs, tradeoffs, and large code-change proposals for unsound assumptions, missing failure modes, and overlooked alternatives, then returns a calibrated objection or `no material objection`. Use when a decision or design needs a different-lab strategic challenge before implementation or merge. Not for line-level bug finding in a concrete diff or file, use codex_reviewer or gemini_reviewer; pass the artifact and constraints verbatim.",
30173
30279
  baseInstructions: CRITIC_BASE,
30174
30280
  agentPrompt: "",
30175
30281
  writeCapable: false,
@@ -30187,7 +30293,7 @@ const PERSONAS_READ = Object.freeze([
30187
30293
  toolNameHttp: "gemini_critic",
30188
30294
  model: "gemini-3.1-pro-preview",
30189
30295
  endpoint: "/v1/chat/completions",
30190
- description: "Adversarial second opinion. Backed by gemini-3.1-pro (Google) — third-lab triangulation, strong on formal reasoning, proofs, and invariants. Useful for cross-checking findings from codex_critic or codex_reviewer when you want a third perspective. Pass artifact verbatim.",
30296
+ description: "Adversarial third-lab critic backed by gemini-3.1-pro-preview (Google), strong on formal reasoning, invariants, proofs, and cross-checking another critic's conclusion. It reviews plans, designs, mathematical arguments, and large artifacts for assumption gaps or invariant failures, then returns a focused critique or no-material-objection style verdict. Use when codex_critic's result needs an independent lab check or when the artifact hinges on formal correctness. Not for line-level diff review, use gemini_reviewer or codex_reviewer; pass the artifact and constraints verbatim.",
30191
30297
  baseInstructions: GEMINI_CRITIC_BASE,
30192
30298
  agentPrompt: "",
30193
30299
  writeCapable: false,
@@ -30205,7 +30311,7 @@ const PERSONAS_READ = Object.freeze([
30205
30311
  toolNameHttp: "codex_reviewer",
30206
30312
  model: "gpt-5.3-codex",
30207
30313
  endpoint: "/v1/responses",
30208
- description: "Line-level review of a concrete diff or single file. Backed by gpt-5.3-codex (OpenAI, ≈272K-token input window) code-specialist, fastest critic (~16s). Surfaces bugs, edge cases, security issues, and idiom violations at specific line numbers. Not suited for architecture or design review (use codex_critic for plans). Pass artifact verbatim.",
30314
+ description: "Line-level code reviewer backed by gpt-5.3-codex (OpenAI, ≈272K-token input window), a code-specialist reviewer that is fastest around high effort (~16s at high effort). It reviews concrete diffs, files, or function bodies and returns findings with severity, file:line locations, issue impact, and a minimal suggested fix. Use when the artifact is actual code and the goal is bug, edge-case, security, concurrency, resource, or idiom review. Not for architecture or tradeoff review, use codex_critic or gemini_critic; pass the diff or file content verbatim.",
30209
30315
  baseInstructions: REVIEWER_BASE,
30210
30316
  agentPrompt: "",
30211
30317
  writeCapable: false,
@@ -30223,7 +30329,7 @@ const PERSONAS_READ = Object.freeze([
30223
30329
  toolNameHttp: "gemini_reviewer",
30224
30330
  model: "gemini-3.1-pro-preview",
30225
30331
  endpoint: "/v1/chat/completions",
30226
- description: "Line-level review of a concrete diff or single file on gemini-3.1-pro (Google, high reasoning): a second-lab code reviewer that catches a different slice of defects than codex_reviewer (OpenAI). Use alongside codex_reviewer for cross-lab coverage of a diff. Not for architecture (use codex_critic / gemini_critic for plans). Pass artifact verbatim.",
30332
+ description: "Line-level code reviewer backed by gemini-3.1-pro-preview (Google), providing second-lab coverage that catches a different slice of concrete-code defects than codex_reviewer. It reviews diffs, files, or function bodies and returns severity-ranked findings with file:line citations and suggested fixes. Use alongside codex_reviewer when a non-trivial diff benefits from cross-lab code-review coverage, especially around invariants or edge cases. Not for architecture or product-design review, use codex_critic or gemini_critic; pass the code artifact verbatim.",
30227
30333
  baseInstructions: GEMINI_REVIEWER_BASE,
30228
30334
  agentPrompt: "",
30229
30335
  writeCapable: false,
@@ -30241,7 +30347,7 @@ const PERSONAS_READ = Object.freeze([
30241
30347
  toolNameHttp: "opus_critic",
30242
30348
  model: "claude-opus-4-6",
30243
30349
  endpoint: "/v1/messages",
30244
- description: "Adversarial second opinion from a fresh-context Opus 4.6 same lab as the lead, limited blind-spot diversity vs cross-lab critics. On enterprise catalogs that carry Opus-4.6-1M it runs with a ≈936K-token input window; otherwise ≈168K. Pinned one minor behind the default Opus so the panel spans more of the version curve. Catches confabulation. Pass artifact verbatim.",
30350
+ description: "Adversarial same-lab critic backed by fresh-context Opus 4.6, with limited blind-spot diversity compared with cross-lab critics. It reviews plans, designs, or code tradeoffs for cognitive momentum, sunk-cost reasoning, and confabulated assumptions, then returns a calibrated objection or no material objection. Use when a same-family sanity check can catch lead-context drift or when comparing against codex_critic / gemini_critic findings. Not a substitute for cross-lab review on security-sensitive or high-risk changes; use codex_critic or gemini_critic for stronger diversity. On enterprise catalogs that carry Opus-4.6-1M it runs with ≈936K input tokens; otherwise ≈168K. Pinned two minors behind the default Opus so the panel spans more of the version curve. Pass artifact verbatim.",
30245
30351
  baseInstructions: OPUS_CRITIC_BASE,
30246
30352
  agentPrompt: "",
30247
30353
  writeCapable: false,
@@ -30259,7 +30365,7 @@ const PERSONAS_WRITE = Object.freeze([{
30259
30365
  toolNameHttp: "codex_implementer",
30260
30366
  model: "gpt-5.3-codex",
30261
30367
  endpoint: "/v1/responses",
30262
- description: "Targeted implementation of a self-contained coding task. Backed by gpt-5.3-codex with workspace-write access. Pass spec + files verbatim.",
30368
+ description: "Targeted implementation persona backed by gpt-5.3-codex with workspace-write access. It executes self-contained coding tasks from a pasted spec, reads the relevant files, edits the workspace, verifies the result, and returns changed files plus verification output. Use when the task is bounded enough for direct implementation and the caller can provide acceptance criteria and file context up front. Not for open-ended planning or broad repo exploration, use plan or explore first; not for read-only review, use codex_reviewer or gemini_reviewer. Because it can mutate the workspace, scope files and allowed changes explicitly and pass the spec verbatim.",
30263
30369
  baseInstructions: IMPLEMENTER_BASE,
30264
30370
  agentPrompt: "",
30265
30371
  writeCapable: true,
@@ -30354,8 +30460,8 @@ function buildAgentPrompt(persona, opts) {
30354
30460
  * - Conditionally lists gemini_critic only when `geminiAvailable`.
30355
30461
  * - Conditionally lists the `worker-*` background dispatcher subagents
30356
30462
  * (worker-explore / worker-review / worker-plan / worker-implement /
30357
- * worker-test), the non-blocking-guard fact, and "Workers themselves
30358
- * have code_search" only when `workerToolsAvailable` (mirrors
30463
+ * worker-test), the non-blocking-guard fact, and the worker code-search
30464
+ * affordance only when `workerToolsAvailable` (mirrors
30359
30465
  * `workerToolsEnabled()` so the snippet never names a surface gated out
30360
30466
  * of the live catalog). The raw `mcp__<workers>__*` tools are named only
30361
30467
  * as the guarded plumbing the dispatchers call, never as a main-agent
@@ -30379,15 +30485,19 @@ function buildPeerAwarenessSnippet(opts) {
30379
30485
  const orchestrateKey = key("orchestrate");
30380
30486
  const browserKey = key("browser");
30381
30487
  const decideKey = key("decide");
30488
+ const fleetKey = key("fleet");
30489
+ const compoundBrowseAvailable = opts.browseAvailable && opts.compoundBrowseAvailable;
30490
+ const powerBrowseAvailable = opts.browseAvailable && opts.powerBrowseAvailable === true;
30382
30491
  const criticList = ["`codex_critic` (gpt-5.5)", "`codex_reviewer` (gpt-5.3-codex)"];
30383
30492
  if (opts.geminiAvailable) {
30384
30493
  criticList.push("`gemini_reviewer` (gemini-3.1-pro, line-level code review)");
30385
30494
  criticList.push("`gemini_critic` (gemini-3.1-pro)");
30386
30495
  }
30387
- criticList.push("`opus_critic` (Opus 4.7)");
30496
+ criticList.push("`opus_critic` (Opus 4.6)");
30388
30497
  const codexCliClause = opts.codexCli ? " `mcp__codex-cli__codex` dispatches to `codex-implementer` (gpt-5.3-codex with workspace-write) for end-to-end coding tasks." : "";
30389
30498
  const para2Parts = [`\`mcp__${searchKey}__code\` is the one-stop code search (no extra model call). Its DEFAULT mode (or \`mode:"semantic"\`) ranks by MEANING via ColBERT over a per-workspace index, the first thing to reach for on intent/concept questions ("where is retry/backoff handled", "how does auth work"); when that index isn't ready it transparently falls back to lexical (the response \`source\` says which engine ran). Forced modes cover the rest: \`lexical\` (BM25F-ranked + tree-sitter, best for exact symbols), \`exact\`, \`regex\`, \`complete\` (exhaustive set), \`ast_pattern\`+\`ast_lang\` for multi-line AST shapes, \`scan\` for a whole-workspace symbol outline, \`multiline\` for cross-line regex. Multiple queries can run in a single turn. The index covers code-shaped files; for unstructured files (logs, \`.csv\`, \`.env*\`, config-only wiring), \`grep\`/\`glob\` still apply.`];
30390
30499
  if (opts.workerToolsAvailable) para2Parts.push(`\`worker-*\` are background Agent subagents (subagent_type) that run the matching worker in its own context and deliver the result as a completion notification, so a long run never blocks the turn: \`worker-explore\` (read-only research), \`worker-review\` (reads the code to verify a change or claim), \`worker-plan\` (ordered implementation plan), \`worker-implement\` (edit/write/bash; \`worktree: true\` isolates in a git worktree and returns the diff), \`worker-test\` (independent test author). The raw \`mcp__${workersKey}__*\` tools they call are guarded (a direct main-thread call is redirected to the matching agent); Workers themselves have \`code_search\`.`);
30500
+ if (opts.workerToolsAvailable && opts.implementerAvailable) para2Parts.push(`For a bounded, well-scoped implementation, prefer the \`implementer\` subagent (Task, runs on gpt-5.5) over \`worker-implement\`; reach for \`worker-implement\` only when you specifically need git-worktree isolation, parallel variants, or a throwaway experiment.`);
30391
30501
  if (opts.workerToolsAvailable) para2Parts.push(`\`mcp__${orchestrateKey}__decompose\` composes an open-ended ask into a typed, VERIFIED workflow IR (a strong driver decorrelated by a cross-lab critic, so the decompose step isn't a single point of failure), and \`mcp__${orchestrateKey}__run_workflow\` executes that IR through a frozen kernel delivering max(orchestrated, baseline) over a sealed executable gate, so it never ships worse than a plain single-model run. \`mcp__${orchestrateKey}__verify_workflow\` checks an IR's floor invariants before you run it, and \`mcp__${orchestrateKey}__attest_step\` audits that a finished run's producers were each checked by a different lab. They suit non-trivial, role-separated asks; a trivial ask does not need them.`);
30392
30502
  else para2Parts.push(`\`mcp__${orchestrateKey}__verify_workflow\` statically checks a workflow IR's floor invariants and \`mcp__${orchestrateKey}__attest_step\` audits a run's cross-lab lineage (the \`decompose\`/\`run_workflow\` composer + kernel need the worker backend, unavailable here).`);
30393
30503
  if (opts.workerToolsAvailable) {
@@ -30396,10 +30506,10 @@ function buildPeerAwarenessSnippet(opts) {
30396
30506
  }
30397
30507
  para2Parts.push(`\`mcp__${searchKey}__web\` surfaces citable sources for docs, errors, and upstream issues.`);
30398
30508
  if (opts.standInAvailable) para2Parts.push(`\`mcp__${decideKey}__stand_in\` provides three-lab consensus for decision tiebreak when the user is unavailable.`);
30399
- if (opts.browseAvailable) {
30400
- const powerNote = opts.powerBrowseAvailable ? ` Power mode adds the L0/L1 primitives (\`mcp__${browserKey}__mouse\`, \`__drag\`, \`__type\`, \`__keyboard\`, \`__scroll\`, \`__eval_js\`, \`__read_page\`, \`__diagnostics\`, \`__find\`) for direct DOM / coordinate control.` : "";
30401
- para2Parts.push(`\`mcp__${browserKey}__*\` tools drive a real Chrome / Edge browser via a local extension. Lead surface: \`__act(intent, value?)\` for any click / fill / type / scroll-to (an inner fast model resolves intent), \`__observe(intent?)\` for a 2-4 sentence natural-language page description, \`__extract(schema, instruction)\` for typed extraction, \`__navigate\` / \`__open_tab\` / \`__screenshot\` for state and visuals. The lead never sees raw DOM: refs and bboxes stay internal.${powerNote}`);
30402
- }
30509
+ if (opts.browseAvailable) para2Parts.push(`\`mcp__${browserKey}__*\` tools drive a real Chrome / Edge browser via a local extension. Lead browse surface includes \`__navigate\` / \`__open_tab\` / \`__screenshot\` for state, visuals, and navigation.`);
30510
+ if (compoundBrowseAvailable) para2Parts.push(`Compound browse surface includes \`mcp__${browserKey}__act(intent, value?)\` / \`__observe(intent?)\` / \`__extract(schema, instruction)\` / \`__find\`; an inner fast model resolves intent, find/observe yield element refs act consumes, and the lead never sees raw DOM.`);
30511
+ if (powerBrowseAvailable) para2Parts.push(`Power browse surface adds \`mcp__${browserKey}__mouse\`, \`__drag\`, \`__type\`, \`__keyboard\`, \`__scroll\`, \`__eval_js\`, \`__read_page\`, \`__diagnostics\`, \`__list_tabs\`, \`__close_tab\`, \`__wait\`, and \`__download\` for direct DOM and coordinate control.`);
30512
+ if (opts.fleetAvailable) para2Parts.push(`\`mcp__${fleetKey}__*\` tools drive remote ai-or-die coding sessions (list / read / create / stop / await / drive, plus remote read_file / list_dir / search / git_show); they act on a REMOTE fleet instance, not the local repo.`);
30403
30513
  return [
30404
30514
  "## Peer review and advisor",
30405
30515
  "",
@@ -30408,6 +30518,31 @@ function buildPeerAwarenessSnippet(opts) {
30408
30518
  para2Parts.join(" ")
30409
30519
  ].join("\n");
30410
30520
  }
30521
+ /**
30522
+ * Compact, gated capability SUMMARY for the spawned session's system prompt
30523
+ * (`--append-system-prompt`). The FULL per-tool inventory lives once in the
30524
+ * mirrored CLAUDE.md (buildPeerAwarenessSnippet); this ~300-token summary gives
30525
+ * the main agent high-salience awareness of what is available without
30526
+ * duplicating the full snippet in the context window every turn. Gated
30527
+ * identically to the full snippet so it never names a surface the live
30528
+ * tools/list dropped. Factual present tense, no imperatives.
30529
+ */
30530
+ function buildPeerAwarenessSummary(opts) {
30531
+ const key = (g) => opts.groupKeys?.[g] ?? GROUP_META[g].preferredKey;
30532
+ const lines = [
30533
+ "## Injected capabilities (summary)",
30534
+ "",
30535
+ `A layer of MCP tools, background workers, and skills is injected into this session. Cross-lab peer critics under \`mcp__${key("peers")}__*\` (plus the \`peer-review-coordinator\` subagent) review plans and diffs adversarially, and Claude Code's built-in \`advisor\` catches approach drift. \`mcp__${key("search")}__code\` is meaning-first code search and \`mcp__${key("search")}__web\` returns citable web sources.`
30536
+ ];
30537
+ if (opts.workerToolsAvailable) lines.push(`Background \`worker-*\` agents (explore, review, plan, implement, test) run delegated work in their own context without blocking your turn, and \`mcp__${key("orchestrate")}__*\` composes, verifies, and runs floor-raising workflows.`);
30538
+ if (opts.standInAvailable) lines.push(`\`mcp__${key("decide")}__stand_in\` returns a three-lab consensus for a decision when the user is unavailable.`);
30539
+ if (opts.browseAvailable) lines.push(`\`mcp__${key("browser")}__*\` drives a real Chrome or Edge browser.`);
30540
+ if (opts.fleetAvailable) lines.push(`\`mcp__${key("fleet")}__*\` drives remote ai-or-die coding sessions.`);
30541
+ if (opts.agentToolsAvailable === true) lines.push("The `/gh-first-mate` skill drives a durable GitHub cloud-agent loop.");
30542
+ lines.push("");
30543
+ lines.push(`Each tool's own description carries when to use it and when not. The full per-tool inventory (models, gating, workers, skills) is in the "Peer review and advisor" section of your CLAUDE.md project instructions.`);
30544
+ return lines.join("\n");
30545
+ }
30411
30546
  /** Convenience: every persona that should be registered for the given mode. */
30412
30547
  function personasFor(opts) {
30413
30548
  const result = [];
@@ -30418,7 +30553,7 @@ function personasFor(opts) {
30418
30553
  if (opts.codexCli) for (const p of PERSONAS_WRITE) result.push(p);
30419
30554
  return result;
30420
30555
  }
30421
- const WEB_SEARCH_DESCRIPTION = "Web search via GitHub Copilot's MCP. Prefer over Claude Code's built-in WebSearch — surfaces source URLs you can cite. Use for API documentation lookups, error message diagnosis, upstream issue searches, and verifying claims against current sources. Returns content with reference links.";
30556
+ const WEB_SEARCH_DESCRIPTION = "Web search via GitHub Copilot's MCP that returns answer text plus source URLs the caller can cite. It accepts a natural-language `query`; the upstream provider rewrites for the search index and the handler formats any references as markdown links. Use for current external information such as API documentation, error-message diagnosis, upstream issue searches, and claims that need web sources. Not for local repository discovery or code navigation, use code, Read, Grep, or Glob for workspace content. Prefer it over the built-in WebSearch when source URLs are needed or the built-in surface is geographically constrained.";
30422
30557
  /**
30423
30558
  * Format a `searchWeb()` result as an MCP-friendly text block. Mirrors
30424
30559
  * the legacy inject format that `injectWebSearchIfNeeded` produces and
@@ -30453,7 +30588,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30453
30588
  if (!query) return {
30454
30589
  content: [{
30455
30590
  type: "text",
30456
- text: "web_search: arguments.query is required (must be a non-empty string)"
30591
+ text: "web: arguments.query is required (must be a non-empty string)"
30457
30592
  }],
30458
30593
  isError: true
30459
30594
  };
@@ -30466,7 +30601,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30466
30601
  return {
30467
30602
  content: [{
30468
30603
  type: "text",
30469
- text: `web_search failed: ${err instanceof Error ? err.message : String(err)}`
30604
+ text: `web failed: ${err instanceof Error ? err.message : String(err)}`
30470
30605
  }],
30471
30606
  isError: true
30472
30607
  };
@@ -30625,7 +30760,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30625
30760
  toolNameHttp: "explore",
30626
30761
  group: "workers",
30627
30762
  capability: "worker",
30628
- description: "Runs as the background `worker-explore` agent. Dispatch via the Agent tool (subagent_type: worker-explore) so your turn is never blocked; the result arrives as a completion notification. Read-only investigation by an autonomous worker (Pi runtime; default model `gpt-5.4-mini` at xhigh reasoning, override via the `model` arg with any Copilot-catalog model that advertises `tool_calls`). Tools: read, glob, grep, code_search (semantic-first), web_search, fetch_url, advisor (consult a stronger cross-lab model), update_plan (planning checklist), and toolbelt (run a read-only analysis CLI: rg/fd/jq/yq/sg/gron/tokei/difft/git). The worker's system prompt sandboxes it and gives one-line descriptions of each tool, so brief it on the investigation, not on tool semantics. Offloads bounded research that would otherwise eat your context window the worker plans its own tool calls and returns a single text answer. Examples: \"find files matching X then summarize\", \"how does library Y handle Z\", \"survey this codebase for usages of deprecated API\".",
30763
+ description: "Runs as the background `worker-explore` agent. Dispatch via the Agent tool (subagent_type: worker-explore) so the turn is never blocked; the result arrives as a completion notification. Read-only investigation by an autonomous worker (Pi runtime; default model `claude-sonnet-5` at xhigh reasoning, override via the `model` arg with any Copilot-catalog model that advertises `tool_calls`). It has read, glob, grep, semantic-first code search, web search, fetch_url, advisor, update_plan, and read-only toolbelt tools, and it returns a single text answer. Use for bounded research, repo discovery, dependency investigation, or multi-file reading that would otherwise consume the lead context window. Not for implementation, test authoring, or verification of a concrete diff; use implement, test, or review for those scopes. Brief the investigation goal and constraints, not step-by-step tool semantics.",
30629
30764
  inputSchema: {
30630
30765
  type: "object",
30631
30766
  required: ["prompt"],
@@ -30637,7 +30772,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30637
30772
  },
30638
30773
  model: {
30639
30774
  type: "string",
30640
- description: "Optional Copilot catalog model id (defaults to gpt-5.4-mini). Must advertise tool_calls support; the engine emits an isError envelope listing the eligible catalog models on mismatch."
30775
+ description: "Optional Copilot catalog model id (defaults to claude-sonnet-5). Must advertise tool_calls support; the engine emits an isError envelope listing the eligible catalog models on mismatch."
30641
30776
  },
30642
30777
  thinking: {
30643
30778
  type: "string",
@@ -30649,7 +30784,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30649
30784
  "high",
30650
30785
  "xhigh"
30651
30786
  ],
30652
- description: "Optional reasoning depth (default high). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30787
+ description: "Optional reasoning depth (default xhigh). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30653
30788
  },
30654
30789
  workspace: {
30655
30790
  type: "string",
@@ -30673,7 +30808,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30673
30808
  toolNameHttp: "implement",
30674
30809
  group: "workers",
30675
30810
  capability: "worker",
30676
- description: "Runs as the background `worker-implement` agent. Dispatch via the Agent tool (subagent_type: worker-implement) so your turn is never blocked; the result arrives as a completion notification. Delegates a scoped coding task to an autonomous worker (Pi runtime; default model `gpt-5.5` at xhigh reasoning, override via the `model` arg with any Copilot-catalog model that advertises `tool_calls`). Tools: the explore read-only set (read, glob, grep, code_search, web_search, fetch_url, advisor, update_plan, toolbelt) plus edit, write, bash, and codex_review (code review by codex-reviewer / gpt-5.3-codex). The worker's system prompt sandboxes it and gives one-line descriptions of each tool, so brief it on the task, not on tool semantics. With `worktree: false` (default) edits in place concurrent worker_implement calls and Claude's own edits to the same files will race. With `worktree: true` runs in an isolated git worktree and returns the diff for review. HARD ERROR if true and the workspace is not a git repository.",
30811
+ description: "Runs as the background `worker-implement` agent. Dispatch via the Agent tool (subagent_type: worker-implement) so the turn is never blocked; the result arrives as a completion notification. Delegates a scoped coding task to an autonomous worker (Pi runtime; default model `gpt-5.5` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the explore read-only tools plus edit, write, bash, and codex_review, and it returns its final text with any changed files or worktree diff. Use for bounded implementation work that may take a while or benefits from isolated worker context. Not for pure research, planning, review, or independent test authoring; use explore, plan, review, or test for those scopes. With `worktree: false` (default) edits happen in place, so concurrent implement calls and lead edits to the same files can race. With `worktree: true` it runs in an isolated git worktree and returns the diff; this errors if the workspace is not a git repository.",
30677
30812
  inputSchema: {
30678
30813
  type: "object",
30679
30814
  required: ["prompt"],
@@ -30685,7 +30820,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30685
30820
  },
30686
30821
  worktree: {
30687
30822
  type: "boolean",
30688
- description: "When true, run inside a fresh git worktree and return Pi's final text followed by the unified diff (so the lead can review before merging). When false/omitted, edits the workspace in place concurrent worker calls and Claude's own edits will race. HARD ERROR if true and the workspace is not a git repository."
30823
+ description: "When true, run inside a fresh git worktree and return Pi's final text followed by the unified diff (so the lead can review before merging). When false/omitted, edits the workspace in place, so concurrent worker calls and lead edits can race. Errors if true and the workspace is not a git repository."
30689
30824
  },
30690
30825
  model: {
30691
30826
  type: "string",
@@ -30725,7 +30860,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30725
30860
  toolNameHttp: "review",
30726
30861
  group: "workers",
30727
30862
  capability: "worker",
30728
- description: "Runs as the background `worker-review` agent. Dispatch via the Agent tool (subagent_type: worker-review) so your turn is never blocked; the result arrives as a completion notification. Read-only code review by an autonomous worker (Pi runtime; default model `gpt-5.5` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). Same read-only toolset as `explore` (read, glob, grep, code_search, web_search, fetch_url, advisor, update_plan, toolbelt) — it CANNOT edit — but the worker is framed as a reviewer: it verifies correctness against the actual code itself rather than trusting a claim, and reports findings (bugs, edge cases, security / concurrency / resource risks, missing handling) with a severity and `file:line`. Brief it with the change / diff / claim to verify (paste it, or name the files) it reads the code to confirm, so you get a self-verifying second opinion that doesn't depend on you having pre-extracted the relevant code. Unlike the `peers` critics (single stateless model calls on the artifact you paste), this worker can navigate the repo to check surrounding context for itself.",
30863
+ description: "Runs as the background `worker-review` agent. Dispatch via the Agent tool (subagent_type: worker-review) so the turn is never blocked; the result arrives as a completion notification. Read-only code review by an autonomous worker (Pi runtime; default model `gemini-3.1-pro-preview`, default thinking xhigh clamped to high for that model, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the same read-only toolset as explore and verifies claims against surrounding repository context before returning severity-ranked findings with `file:line` citations. Use for reviewing a change, diff, or correctness claim when the reviewer should read the code itself rather than trusting a pasted artifact. Not for architecture critique, implementation, or test authoring; use codex_critic or gemini_critic for design review, implement for edits, and test for independent test creation.",
30729
30864
  inputSchema: {
30730
30865
  type: "object",
30731
30866
  required: ["prompt"],
@@ -30737,7 +30872,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30737
30872
  },
30738
30873
  model: {
30739
30874
  type: "string",
30740
- description: "Optional Copilot catalog model id (defaults to gpt-5.5). Must advertise tool_calls support; the engine emits an isError envelope listing the eligible catalog models on mismatch."
30875
+ description: "Optional Copilot catalog model id (defaults to gemini-3.1-pro-preview). Must advertise tool_calls support; the engine emits an isError envelope listing the eligible catalog models on mismatch."
30741
30876
  },
30742
30877
  thinking: {
30743
30878
  type: "string",
@@ -30749,7 +30884,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30749
30884
  "high",
30750
30885
  "xhigh"
30751
30886
  ],
30752
- description: "Optional reasoning depth (default high). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30887
+ description: "Optional reasoning depth (defaults to xhigh, clamped to high for the default review model). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30753
30888
  },
30754
30889
  workspace: {
30755
30890
  type: "string",
@@ -30773,7 +30908,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30773
30908
  toolNameHttp: "plan",
30774
30909
  group: "workers",
30775
30910
  capability: "worker",
30776
- description: "Runs as the background `worker-plan` agent. Dispatch via the Agent tool (subagent_type: worker-plan) so your turn is never blocked; the result arrives as a completion notification. Read-only implementation planning by an autonomous worker (Pi runtime; default model `claude-opus-4.8`, override via `model` with any Copilot-catalog model that advertises `tool_calls`). Same read-only toolset as `explore` (read, glob, grep, code_search, web_search, fetch_url, advisor, update_plan, toolbelt) — it CANNOT edit — but the worker is framed as a planner: from the task and acceptance criteria it produces a concrete, ordered implementation plan (the files to change, the approach, the key risks, and how each acceptance criterion will be verified), grounded by reading the actual code. Brief it with the task and any acceptance criteria; it returns a single plan, not code.",
30911
+ description: "Runs as the background `worker-plan` agent. Dispatch via the Agent tool (subagent_type: worker-plan) so the turn is never blocked; the result arrives as a completion notification. Read-only implementation planning by an autonomous worker (Pi runtime; default model `claude-opus-4.8` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the same read-only toolset as explore and returns a concrete, ordered implementation plan covering files, approach, risks, and how acceptance criteria will be verified. Use before coding when the task needs repo-grounded sequencing or acceptance criteria translated into implementation steps. Not for editing files, running an implementation, writing tests, or adversarial review; use implement, test, or review for those scopes.",
30777
30912
  inputSchema: {
30778
30913
  type: "object",
30779
30914
  required: ["prompt"],
@@ -30797,7 +30932,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30797
30932
  "high",
30798
30933
  "xhigh"
30799
30934
  ],
30800
- description: "Optional reasoning depth (default high). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30935
+ description: "Optional reasoning depth (default xhigh). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
30801
30936
  },
30802
30937
  workspace: {
30803
30938
  type: "string",
@@ -30821,7 +30956,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30821
30956
  toolNameHttp: "test",
30822
30957
  group: "workers",
30823
30958
  capability: "worker",
30824
- description: "Runs as the background `worker-test` agent. Dispatch via the Agent tool (subagent_type: worker-test) so your turn is never blocked; the result arrives as a completion notification. Independent adversarial test authoring by an autonomous worker (Pi runtime; default model `gpt-5.5` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). Same read+write toolset as `implement` (the explore set plus edit, write, bash, codex_review). The worker is framed as an INDEPENDENT test author that did NOT write the code under test: from the task and acceptance criteria it writes tests that try to BREAK the implementation (edge cases, error paths, the acceptance criteria as executable checks), runs them, and reports which pass and fail it does NOT modify the implementation to make tests pass. With `worktree: true` runs in an isolated git worktree and returns the diff; HARD ERROR if true and the workspace is not a git repository.",
30959
+ description: "Runs as the background `worker-test` agent. Dispatch via the Agent tool (subagent_type: worker-test) so the turn is never blocked; the result arrives as a completion notification. Independent adversarial test authoring by an autonomous worker (Pi runtime; default model `gpt-5.5` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the same read/write toolset as implement and writes tests that try to break the implementation through edge cases, error paths, and acceptance criteria, then runs them and reports pass/fail. Use when a separate test author should challenge an implementation without modifying the production code to make tests pass. Not for implementing fixes, broad research, or code review; use implement, explore, or review for those scopes. With `worktree: true` it runs in an isolated git worktree and returns the test diff; this errors if the workspace is not a git repository.",
30825
30960
  inputSchema: {
30826
30961
  type: "object",
30827
30962
  required: ["prompt"],
@@ -30833,7 +30968,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30833
30968
  },
30834
30969
  worktree: {
30835
30970
  type: "boolean",
30836
- description: "When true, run inside a fresh git worktree and return Pi's final text followed by the unified diff (so the lead can review the authored tests before merging). When false/omitted, writes tests in place concurrent worker calls and Claude's own edits will race. HARD ERROR if true and the workspace is not a git repository."
30971
+ description: "When true, run inside a fresh git worktree and return Pi's final text followed by the unified diff (so the lead can review the authored tests before merging). When false/omitted, writes tests in place, so concurrent worker calls and lead edits can race. Errors if true and the workspace is not a git repository."
30837
30972
  },
30838
30973
  model: {
30839
30974
  type: "string",
@@ -30872,7 +31007,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30872
31007
  {
30873
31008
  toolNameHttp: "verify_workflow",
30874
31009
  group: "orchestrate",
30875
- description: "Statically verify a workflow IR against the orchestration floor invariants BEFORE running it. Input `ir`: the typed WorkflowIR (rawAskHash, acceptanceCriteriaHash, nodes[] with role/inputs/gate/onFail, maxDepth). Returns {ok, violations:[{code, message, nodeId?}]}. Each violation carries a stable code (e.g. NO_BASELINE, SELECTOR_NOT_RAW_ASK, SAME_LAB_CHECK, ORPHAN_NODE, MISSING_INTEGRATION_GATE) fix every one until `ok` is true. WHY: a workflow's floor guarantee (deliver max(orchestrated, baseline), producer != checker, cross-lab checks, sealed gates) is only as good as the IR's structure; a probabilistically-composed IR can silently violate it. This is the cheap, pure, side-effect-free pre-flight that catches those violations with actionable codes so you self-correct BEFORE paying for execution. Call it right after composing/decomposing a workflow.",
31010
+ description: "Statically verifies a workflow IR against the orchestration floor invariants before the kernel runs it. It accepts the typed WorkflowIR as `ir` and an optional `knownGateIds` allowlist, then returns {ok, violations:[{code, message, nodeId?}]} with stable violation codes such as NO_BASELINE, SELECTOR_NOT_RAW_ASK, SAME_LAB_CHECK, ORPHAN_NODE, or MISSING_INTEGRATION_GATE. Use immediately after composing or receiving a workflow IR, especially before paying for run_workflow, so structural floor failures can be fixed while still in data form. Not a runner, model reviewer, or proof that the user's spec is correct; use run_workflow to execute sealed gates and use critic/review tools for advisory review.",
30876
31011
  inputSchema: {
30877
31012
  type: "object",
30878
31013
  required: ["ir"],
@@ -30902,7 +31037,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30902
31037
  toolNameHttp: "decompose",
30903
31038
  group: "orchestrate",
30904
31039
  capability: "worker",
30905
- description: "Compose a VERIFIED, tool-routed workflow IR from an open-ended software ask. A single strong driver model drafts a typed WorkflowIR; a static verifier checks it against the floor invariants and the driver re-drafts on any violation; a cross-lab critic reviews a clean draft. Returns {ok, ir, rounds, concerns?} on success, or {ok:false, violations, rounds} if it never converged. WHY: a single model anchors on its own framing of a task (the decompose step is itself a single point of failure), so the driver is decorrelated by a cross-lab critic, and the output is a typed IR a verifier/kernel enforce in CODE rather than prose the model could quietly violate. The IR is DATA you then pass to run_workflow (or re-check with verify_workflow). Reach for it on non-trivial, role-separated asks where blind-spot reduction pays off; a trivial ask does not need it.",
31040
+ description: "Composes a verified, tool-routed WorkflowIR from an open-ended software ask. A strong driver model drafts the IR, the static verifier checks floor invariants, the driver re-drafts on violations, and a cross-lab critic reviews a clean draft; optional `context` supplies repo facts, constraints, or research findings to the driver. It returns {ok, ir, rounds, concerns?} on success, or {ok:false, violations, rounds} when it cannot converge. Use for non-trivial, role-separated asks where blind-spot reduction and sealed-gate structure justify orchestration. Not for trivial edits, direct implementation, or execution; use implement for a scoped code change and run_workflow only after the IR is verified.",
30906
31041
  inputSchema: {
30907
31042
  type: "object",
30908
31043
  required: ["ask"],
@@ -30935,7 +31070,10 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30935
31070
  effort: "high"
30936
31071
  },
30937
31072
  signal
30938
- }), { maxRounds: 3 });
31073
+ }), {
31074
+ maxRounds: 3,
31075
+ context: typeof args.context === "string" ? args.context : void 0
31076
+ });
30939
31077
  return {
30940
31078
  content: [{
30941
31079
  type: "text",
@@ -30949,7 +31087,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
30949
31087
  toolNameHttp: "run_workflow",
30950
31088
  group: "orchestrate",
30951
31089
  capability: "worker",
30952
- description: "Execute a VERIFIED workflow IR (from decompose / verify_workflow) through the frozen orchestration kernel. The kernel runs the single-model BASELINE plus the orchestrated DAG, gates every producer over a SEALED executable gate you name by `gateId` (the kernel owns the command; the IR cannot author it), and delivers max(orchestrated, baseline) by champion-retention: the orchestrated result ships only if it verifiably does not regress the baseline's executable checks, else the baseline ships. Returns {ok, outcome:{status, winner?, artifact?, reason, gatesPassed?}}. WHY: orchestration is a conditional bet (it helps on blind-spot/ambiguous asks, backfires on others), so the kernel NEVER ships something worse than a plain single-model run on the same ask. It enforces the floor in code (the model can't be trusted to honor it): a parallel baseline, a sealed executable gate as the selector, fail-to-baseline on any infra failure. Use after decompose for non-trivial asks on a harness-bearing repo.",
31090
+ description: "Executes a verified WorkflowIR through the frozen orchestration kernel. The kernel runs a single-model baseline beside the orchestrated DAG, gates producers over the sealed executable `gateId` selected by the caller, and returns {ok, outcome:{status, winner?, artifact?, reason, gatesPassed?}}. It uses champion retention: the orchestrated candidate wins only when it does not regress the baseline's executable checks; otherwise the baseline ships. Use after decompose and verify_workflow for non-trivial asks in a git workspace with a meaningful sealed gate. Not for composing an IR, performing advisory review, or running arbitrary model-authored shell commands; use decompose or verify_workflow before this tool, and use ordinary tests or review tools outside the workflow kernel.",
30953
31091
  inputSchema: {
30954
31092
  type: "object",
30955
31093
  required: [
@@ -31014,7 +31152,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31014
31152
  {
31015
31153
  toolNameHttp: "attest_step",
31016
31154
  group: "orchestrate",
31017
- description: "Attest (audit) that an orchestrated run actually honored bias isolation: every producer node was checked by a DIFFERENT lab, and that check covered the producer's FINAL artifact (matched by content hash, so a check of a stale earlier version does not count). Input `nodes`: [{id, producerLab, artifactHash, checks:[{checkerLab, verifiedArtifactHash}]}]. Returns {attested, recommendation: 'accept'|'ship_baseline', nodes:[{id, attested, reason}]}. WHY: run_workflow's frozen kernel is the TAMPER-PROOF path (it controls the artifacts and computes the hashes). attest_step is for workflows you compose OUTSIDE the kernel: it deterministically checks your SELF-REPORTED lineage is structurally sound (a different-lab check whose hash equals each producer's final-artifact hash), catching the non-malicious failures (a missing / same-lab / stale check). It verifies consistency, NOT that the hashes are real a completeness gate, not a security boundary. Fail-closed: anything short of a valid different-lab check on EVERY node recommends shipping the baseline. It RECOMMENDS; it never executes.",
31155
+ description: "Audits self-reported producer lineage for bias-isolation structure. It accepts `nodes` with each producer lab, final artifact hash, and checker hashes, then returns {attested, recommendation:'accept'|'ship_baseline', nodes:[{id, attested, reason}]}. The check passes only when every producer has a different-lab check over the same final artifact hash, so missing, same-lab, or stale checks fail closed to a baseline recommendation. Use for workflows composed outside run_workflow, where lineage is self-reported and needs a deterministic completeness gate. Not a security boundary, hash authenticator, or executor; run_workflow is the kernel-owned path when the router must control artifacts, gates, and hashes.",
31018
31156
  inputSchema: {
31019
31157
  type: "object",
31020
31158
  required: ["nodes"],
@@ -31072,7 +31210,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31072
31210
  toolNameHttp: "browse",
31073
31211
  group: "workers",
31074
31212
  capability: "browse_agent",
31075
- description: "Runs as the background `worker-browse` agent. Dispatch via the Agent tool (subagent_type: worker-browse) so your turn is never blocked; the result arrives as a completion notification. A Pi-driven autonomous browser agent (gpt-5.4-mini) that drives a real browser to accomplish `task` and returns the result. Runs in its own context to preserve the lead's window (raw DOM / page snapshots stay inside the agent). Pass `sessionId` to continue a prior session (its id is returned appended to the result as `[browse session: <id>]`); omit it for a fresh isolated session. Multiple concurrent calls run as parallel sessions on the one shared browser. Examples: \"find the cheapest flight LHR-JFK next Tuesday\", \"log into the dashboard and read the current MRR\", \"summarize the top 3 HN front-page stories\".",
31213
+ description: "Runs as the background `worker-browse` agent. Dispatch via the Agent tool (subagent_type: worker-browse) so the turn is never blocked; the result arrives as a completion notification. A Pi-driven autonomous browser worker (default model `gpt-5.4-mini`) drives a real browser to accomplish `task`, keeps raw DOM and page snapshots inside its own context, and returns a single text result. Use for delegated multi-step web tasks such as comparing prices, logging into a dashboard, or summarizing pages when the lead does not need to steer each click. Not for direct in-context browser control, screenshots, or precise element interactions; use the `browser` MCP tools for those. Pass `sessionId` to continue a prior browse session, or omit it for a fresh isolated session; multiple calls run as parallel sessions on the shared browser.",
31076
31214
  inputSchema: {
31077
31215
  type: "object",
31078
31216
  required: ["task"],
@@ -31100,7 +31238,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31100
31238
  toolNameHttp: "stand_in",
31101
31239
  group: "decide",
31102
31240
  capability: "stand_in",
31103
- description: "**Away-mode decision tiebreak.** Three-lab advisor (gpt-5.5 xhigh, opus-4.7 xhigh, gemini-3.1-pro high) for **when the user is unavailable and you are stuck between two or more concrete options**. Polls all three across two structured rounds (blind vote informed re-vote with peer reasoning visible) and returns a ranked-choice verdict. Use when: you would otherwise halt and wait for the user. Do NOT use for: code review (use `peer-review-coordinator`), open-ended exploration, single-model second opinions (use `codex_critic` / `gemini_critic` / `opus_critic` directly), or as a substitute for user confirmation on irreversible actions (push, delete, drop, deploy those still require the user even with three-lab consensus).",
31241
+ description: "Three-lab away-mode decision tiebreak advisor for moments when the user is unavailable and the agent is stuck between two or more concrete options. It polls gpt-5.5, Opus 4.7, and gemini-3.1-pro-preview across blind and informed voting rounds, then returns a ranked-choice verdict such as consensus, majority, no_consensus, or need_more_info. Use when work would otherwise halt on a bounded choice the user would normally make. Not for code review, open-ended exploration, single-model second opinions, or bypassing confirmation on irreversible actions such as push, delete, drop, or deploy; use peer-review-coordinator or the individual critics for review and still ask the user for destructive actions.",
31104
31242
  inputSchema: {
31105
31243
  type: "object",
31106
31244
  required: ["decision", "options"],
@@ -31189,7 +31327,7 @@ function assertMcpToolSurfaceConsistent() {
31189
31327
  * accessibility) and never throws — its `{text, isError?}` envelope
31190
31328
  * is forwarded verbatim into the MCP `tool result` shape.
31191
31329
  *
31192
- * Arg-validation policy mirrors `web_search`'s pattern: shape errors
31330
+ * Arg-validation policy mirrors `web`'s pattern: shape errors
31193
31331
  * surface as `isError: true` tool-result envelopes (NOT JSON-RPC -32602
31194
31332
  * errors). The MCP `tools/list` JSON schema already documents the
31195
31333
  * required/optional fields; this runtime check is defense against a
@@ -31396,7 +31534,7 @@ async function runBrowseToolCall(args, signal) {
31396
31534
  * failures, abstains) all surface inside the structured `StandInResult`
31397
31535
  * envelope, which we JSON-stringify into the single MCP text block.
31398
31536
  *
31399
- * Arg-validation policy mirrors `runWorkerToolCall` and `web_search`:
31537
+ * Arg-validation policy mirrors `runWorkerToolCall` and `web`:
31400
31538
  * shape errors surface as `isError: true` tool-result envelopes (NOT
31401
31539
  * JSON-RPC -32602). The `tools/list` JSON schema documents required
31402
31540
  * fields; this runtime check is defense against a schema-ignoring
@@ -31494,5 +31632,5 @@ async function runStandInToolCall(args, signal) {
31494
31632
  }
31495
31633
 
31496
31634
  //#endregion
31497
- export { buildAnthropicErrorEvent as $, resolveCodexModel as $t, liveExec as A, shouldUseInsecureTls as At, availableToolCommands as B, generateRandomPort as Bt, isSubagentContext as C, readResponseBodyCapped as Ct, stopReviewStateDir as D, provisionAndIndexColbert as Dt, stopGateEnabledForRepo as E, hasSupportedBrowserInstalled as Et, PLAN_DEFAULT_MODEL as F, DEFAULT_CODEX_MODEL as Ft, TOOLBELT_TOOLS$1 as G, setupGitHubAgentToken as Gt, toolbeltEnabled as H, getPackageVersion as Ht, REVIEW_DEFAULT_MODEL as I, DEFAULT_CODEX_MODEL_FALLBACKS as It, ADVISOR_INTERNAL_TOOL_NAME as J, cacheCopilotVersion as Jt, assetFor as K, setupGitHubToken as Kt, appendPlanReminder as L, DEFAULT_PORT as Lt, DEFAULT_MODEL as M, collapsePathKeys as Mt, EXPLORE_DEFAULT_MODEL as N, toolbeltPathOverride as Nt, trustRepo as O, extractTarGzMember as Ot, IMPLEMENT_DEFAULT_MODEL as P, DEFAULT_CLAUDE_MODEL_FALLBACKS as Pt, isAdvisorRequested as Q, isNullish as Qt, runWorkerAgent as R, UPSTREAM_FETCH_TIMEOUT_MS as Rt, fileReviewDebounce as S, MAX_RESPONSE_BODY_BYTES as St, repoRoot as T, provisionBrowserAssets as Tt, toolbeltSkipSet as U, withInstallLock as Ut, buildToolbeltAwareness as V, pickClaudeDefault as Vt, vscodeRipgrepPath as W, setupCopilotToken as Wt, buildAdvisorStream as X, cacheVSCodeVersion as Xt, ADVISOR_TOOL_INSTRUCTIONS as Y, cacheModels as Yt, injectAdvisorTool as Z, filterBetaHeader as Zt, stopGatePlanMode as _, assembleResponsesPayload as _t, buildPeerAwarenessSnippet as a, forwardError as an, handleMcpDelete as at, fileFindingsStore as b, createResponses as bt, buildSessionBindHookCommand as c, copilotHeaders as cn, browseAgentEnabled as ct, decideStopHook as d, implementerSubagentModel as dt, resolveModel as en, buildOpenAIErrorEvent as et, fileBlockBudget as f, standInToolEnabled as ft, stopGateId as g, getTokenCount as gt, stopGateDisabled as h, createMessages as ht, buildAgentPrompt as i, HTTPError as in, relayAnthropicStream as it, BROWSE_DEFAULT_MODEL as j, ArtifactClient as jt, resolveSealedGate as k, extractZipMember as kt, buildStopHookCommand as l, githubHeaders as ln, browserToolsEnabled as lt, launchBaselineKey as m, countTokens as mt, MCP_GROUPS as n, getModels as nn, logStreamError as nt, personasFor as o, GITHUB_API_BASE_URL as on, handleMcpPost as ot, injectStopHookIntoSettingsFile as p, workerToolsEnabled as pt, searchWeb as q, tryRefreshAndRetry as qt, assertMcpToolSurfaceConsistent as r, fetchWithTransientRetry as rn, readIteratorWithTimeout as rt, buildArtifactOpenHookCommand as s, copilotBaseUrl as sn, agentToolsEnabled as st, GROUP_META as t, sleep as tn, isControllerClosedError as tt, captureLaunchBaseline as u, state as un, fleetToolsEnabled as ut, stopReviewEnabled as v, resolveMcpToolTimeoutMs as vt, repoFingerprint as w, parseJsonOrDiagnose as wt, fileLastPromptStore as x, createChatCompletions as xt, fileBaselineStore as y, pickEndpoint as yt, withNoOutputRetry as z, UPSTREAM_INACTIVITY_TIMEOUT_MS as zt };
31498
- //# sourceMappingURL=peer-mcp-personas-D826LsJJ.js.map
31635
+ export { isAdvisorRequested as $, cacheModels as $t, liveExec as A, hasSupportedBrowserInstalled as At, withNoOutputRetry as B, DEFAULT_CODEX_MODEL_FALLBACKS as Bt, fileReviewDebounce as C, pickEndpoint as Ct, stopGateEnabledForRepo as D, readResponseBodyCapped as Dt, repoRoot as E, MAX_RESPONSE_BODY_BYTES as Et, IMPLEMENT_DEFAULT_MODEL as F, ArtifactClient as Ft, vscodeRipgrepPath as G, pickClaudeDefault as Gt, buildToolbeltAwareness as H, UPSTREAM_FETCH_TIMEOUT_MS as Ht, PLAN_DEFAULT_MODEL as I, collapsePathKeys as It, searchWeb as J, setupCopilotToken as Jt, TOOLBELT_TOOLS$1 as K, getPackageVersion as Kt, REVIEW_DEFAULT_MODEL as L, toolbeltPathOverride as Lt, BROWSE_DEFAULT_MODEL as M, extractTarGzMember as Mt, DEFAULT_MODEL as N, extractZipMember as Nt, stopReviewStateDir as O, parseJsonOrDiagnose as Ot, EXPLORE_DEFAULT_MODEL as P, shouldUseInsecureTls as Pt, injectAdvisorTool as Q, cacheCopilotVersion as Qt, appendPlanReminder as R, DEFAULT_CLAUDE_MODEL_FALLBACKS as Rt, fileLastPromptStore as S, resolveMcpToolTimeoutMs as St, repoFingerprint as T, createChatCompletions as Tt, toolbeltEnabled as U, UPSTREAM_INACTIVITY_TIMEOUT_MS as Ut, availableToolCommands as V, DEFAULT_PORT as Vt, toolbeltSkipSet as W, generateRandomPort as Wt, ADVISOR_TOOL_INSTRUCTIONS as X, setupGitHubToken as Xt, ADVISOR_INTERNAL_TOOL_NAME as Y, setupGitHubAgentToken as Yt, buildAdvisorStream as Z, tryRefreshAndRetry as Zt, stopGateId as _, workerToolsEnabled as _t, buildPeerAwarenessSnippet as a, sleep as an, relayAnthropicStream as at, fileBaselineStore as b, getTokenCount as bt, buildArtifactOpenHookCommand as c, HTTPError as cn, agentToolsEnabled as ct, captureLaunchBaseline as d, copilotBaseUrl as dn, browserCompoundToolsEnabled as dt, cacheVSCodeVersion as en, buildAnthropicErrorEvent as et, decideStopHook as f, copilotHeaders as fn, browserToolsEnabled as ft, stopGateDisabled as g, standInToolEnabled as gt, launchBaselineKey as h, implementerSubagentModel as ht, buildAgentPrompt as i, resolveModel as in, readIteratorWithTimeout as it, resolveSealedGate as j, provisionAndIndexColbert as jt, trustRepo as k, provisionBrowserAssets as kt, buildSessionBindHookCommand as l, forwardError as ln, artifactToolsEnabled as lt, injectStopHookIntoSettingsFile as m, state as mn, geminiAvailable as mt, MCP_GROUPS as n, isNullish as nn, isControllerClosedError as nt, buildPeerAwarenessSummary as o, getModels as on, handleMcpDelete as ot, fileBlockBudget as p, githubHeaders as pn, fleetToolsEnabled as pt, assetFor as q, withInstallLock as qt, assertMcpToolSurfaceConsistent as r, resolveCodexModel as rn, logStreamError as rt, personasFor as s, fetchWithTransientRetry as sn, handleMcpPost as st, GROUP_META as t, filterBetaHeader as tn, buildOpenAIErrorEvent as tt, buildStopHookCommand as u, GITHUB_API_BASE_URL as un, browseAgentEnabled as ut, stopGatePlanMode as v, countTokens as vt, isSubagentContext as w, createResponses as wt, fileFindingsStore as x, assembleResponsesPayload as xt, stopReviewEnabled as y, createMessages as yt, runWorkerAgent as z, DEFAULT_CODEX_MODEL as zt };
31636
+ //# sourceMappingURL=peer-mcp-personas-BCGYWok0.js.map