codeden 0.6.2 → 0.7.0

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.
Files changed (44) hide show
  1. package/README.md +12 -5
  2. package/dist/cli.js +1 -1
  3. package/dist/drizzle/20261112140714_turn_fork_point/migration.sql +1 -0
  4. package/dist/drizzle/20261112140714_turn_fork_point/snapshot.json +3524 -0
  5. package/dist/drizzle/20261113075317_transcript_search/migration.sql +29 -0
  6. package/dist/drizzle/20261113075317_transcript_search/snapshot.json +3635 -0
  7. package/dist/drizzle/20261113083655_runners/migration.sql +17 -0
  8. package/dist/drizzle/20261113083655_runners/snapshot.json +3792 -0
  9. package/dist/drizzle/20261113165404_search_newest_first/migration.sql +43 -0
  10. package/dist/drizzle/20261113165404_search_newest_first/snapshot.json +3785 -0
  11. package/dist/{main-hdQfX9V6.js → main-BITVD_Bx.js} +7311 -3019
  12. package/dist/main-BITVD_Bx.js.map +1 -0
  13. package/dist/{main-DZBmzxSH.js → main-NN0iK2tB.js} +2 -2
  14. package/dist/{main-DZBmzxSH.js.map → main-NN0iK2tB.js.map} +1 -1
  15. package/dist/{mediaType-nJs-4z5U.js → mediaType-N0hKQqG9.js} +2 -2
  16. package/dist/{mediaType-nJs-4z5U.js.map → mediaType-N0hKQqG9.js.map} +1 -1
  17. package/dist/{runner-DYf8dV43.js → runner-CQYh087M.js} +1948 -385
  18. package/dist/runner-CQYh087M.js.map +1 -0
  19. package/dist/runner.js +1 -1
  20. package/dist/{server-test-COAiKxTV.js → server-test-BwyCFssm.js} +3 -3
  21. package/dist/{server-test-COAiKxTV.js.map → server-test-BwyCFssm.js.map} +1 -1
  22. package/dist/{tree-CF7Q8tXK.js → tree-Bjv-rXir.js} +638 -41
  23. package/dist/tree-Bjv-rXir.js.map +1 -0
  24. package/dist/web/assets/{core-Rhf-hNq6.js → core-EsMgAasL.js} +1 -1
  25. package/dist/web/assets/engine-oniguruma-C2eJcn5h.js +1 -0
  26. package/dist/web/assets/{highlighted-body-JLMTFACH-DcKwRTm5.js → highlighted-body-JLMTFACH-Co1oXN1b.js} +1 -1
  27. package/dist/web/assets/{highlights-DCtMC0YR.js → highlights-DUjJZrPQ.js} +1 -1
  28. package/dist/web/assets/index-29XyPQXD.css +1 -0
  29. package/dist/web/assets/index-DK4GsIYY.js +943 -0
  30. package/dist/web/assets/langs-BduZAjBk.js +1 -0
  31. package/dist/web/assets/mermaid-WFAXDIZX-D00b7K53.js +1 -0
  32. package/dist/web/assets/{shiki-B3-ws3OK.js → shiki-DP8JfDZC.js} +2 -2
  33. package/dist/web/assets/{stream-tokenizer-hKFrMxml.js → stream-tokenizer-IR1cpBIQ.js} +1 -1
  34. package/dist/web/assets/{themes-Cvhh4Ovr.js → themes-D2aixyve.js} +2 -2
  35. package/dist/web/index.html +2 -2
  36. package/package.json +1 -1
  37. package/dist/main-hdQfX9V6.js.map +0 -1
  38. package/dist/runner-DYf8dV43.js.map +0 -1
  39. package/dist/tree-CF7Q8tXK.js.map +0 -1
  40. package/dist/web/assets/engine-oniguruma-BbpT993F.js +0 -1
  41. package/dist/web/assets/index-BLggvVMT.js +0 -942
  42. package/dist/web/assets/index-JjF9o00P.css +0 -1
  43. package/dist/web/assets/langs-B2ZGl3f_.js +0 -1
  44. package/dist/web/assets/mermaid-WFAXDIZX-C2_MUy_a.js +0 -1
@@ -6105,8 +6105,14 @@ const AgentConfigProvider = _enum(["claude", "codex"]);
6105
6105
  * tabs and choices to offer, and never branches on a provider id (Principle 5).
6106
6106
  */
6107
6107
  const agentConfigFeatures = {
6108
- claude: { hooks: true },
6109
- codex: { hooks: false }
6108
+ claude: {
6109
+ hooks: true,
6110
+ skillSwitches: true
6111
+ },
6112
+ codex: {
6113
+ hooks: false,
6114
+ skillSwitches: true
6115
+ }
6110
6116
  };
6111
6117
  /** The providers that have a given agent-config feature. */
6112
6118
  const providersWith = (feature) => AgentConfigProvider.options.filter((p) => agentConfigFeatures[p][feature]);
@@ -6199,7 +6205,8 @@ object({
6199
6205
  deleted: boolean(),
6200
6206
  backupPath: string().nullable()
6201
6207
  });
6202
- object({
6208
+ /** A unified diff preview of a proposed change. */
6209
+ const AgentConfigDiff = object({
6203
6210
  absolutePath: string(),
6204
6211
  exists: boolean(),
6205
6212
  patch: string(),
@@ -6446,20 +6453,71 @@ object({
6446
6453
  /** The hooks after the change, for the request's `listProjectId`. */
6447
6454
  hooks: HookListResponse
6448
6455
  });
6449
- // ../../packages/contracts/src/agent-config-loaded.ts
6456
+ // ../../packages/contracts/src/agent-config-skills.ts
6450
6457
  /**
6451
- * What's loaded (§12.4): for one session, the memory files, skills, subagents, MCP servers and
6452
- * hooks its provider takes from its working directory. Computed from each provider's discovery
6453
- * rules (not reported by the provider).
6458
+ * Skills (§12.4) and the switch that turns one off, written to each provider's own setting:
6459
+ * - Claude Code: `skillOverrides` in its settings files (`~/.claude/settings.json`,
6460
+ * `.claude/settings.json`, `.claude/settings.local.json`) maps a skill name to `"on"`,
6461
+ * `"name-only"`, `"user-invocable-only"` or `"off"`; a name no file sets is `"on"`. The most
6462
+ * specific file that sets a name wins (local, then project, then user).
6463
+ * - Codex: `[[skills.config]]` entries in `~/.codex/config.toml`, each with a `path` (the
6464
+ * skill's `SKILL.md`) or a `name`, and `enabled`. Entries apply in order; the last one that
6465
+ * selects a skill decides it.
6466
+ *
6467
+ * A switch edits the file that already decides the skill, or else the provider's user-level
6468
+ * file, so it reaches every session (worktrees included). Codex user skills are selected by
6469
+ * path, project skills by name (their path differs in every worktree).
6470
+ *
6471
+ * Routes: `GET /api/agent-config/skills?projectId=`, `POST /api/agent-config/skills/diff`
6472
+ * (-> `SkillSwitchDiff`) and `POST /api/agent-config/skills` (-> `SkillSwitchResponse`), both
6473
+ * taking a `SkillSwitchRequest`.
6454
6474
  */
6455
- /** A skill folder (its `SKILL.md`). */
6475
+ /** Claude Code's partial states: the skill is on, but Claude sees less of it. */
6476
+ const SkillVisibility = _enum(["name-only", "user-invocable-only"]);
6477
+ /** A skill folder (its `SKILL.md`) and whether its provider's native setting switches it off. */
6456
6478
  const AgentSkill = object({
6457
6479
  provider: AgentConfigProvider,
6458
6480
  scope: AgentConfigScope,
6459
6481
  name: string(),
6460
6482
  description: string().optional(),
6461
- file: AgentConfigFile
6483
+ file: AgentConfigFile,
6484
+ /** False when the provider's native setting turns the skill off where it is listed. */
6485
+ enabled: boolean(),
6486
+ /**
6487
+ * Claude Code only: `"name-only"` (Claude sees the name without the description) or
6488
+ * `"user-invocable-only"` (Claude doesn't see it; you can still type `/name`).
6489
+ */
6490
+ visibility: SkillVisibility.optional(),
6491
+ /** The native file whose entry decides the state; null when no entry names the skill. */
6492
+ setIn: string().nullable()
6493
+ });
6494
+ object({ skills: array(AgentSkill) });
6495
+ /**
6496
+ * Switch one listed skill on or off. The skill is addressed by its `SKILL.md` locator, as
6497
+ * listed for `listProjectId` (the page's project, whose settings files apply; user only when
6498
+ * absent). A skill that isn't in that list is refused with 404 `skill_not_found`.
6499
+ */
6500
+ const SkillSwitchRequest = AgentConfigLocator.extend({
6501
+ provider: AgentConfigProvider,
6502
+ enabled: boolean(),
6503
+ listProjectId: Id.optional()
6462
6504
  });
6505
+ AgentConfigDiff.extend({
6506
+ /** `user`: applies in every project; `project`: shared with the repository; `local`: you, here. */
6507
+ scope: AgentConfigScope });
6508
+ object({
6509
+ file: string().nullable(),
6510
+ /** The copy of the file taken before writing; null for a new file or no change. */
6511
+ backupPath: string().nullable(),
6512
+ /** The skills after the change, for the request's `listProjectId`. */
6513
+ skills: array(AgentSkill)
6514
+ });
6515
+ // ../../packages/contracts/src/agent-config-loaded.ts
6516
+ /**
6517
+ * What's loaded (§12.4): for one session, the memory files, skills, subagents, MCP servers and
6518
+ * hooks its provider takes from its working directory. Computed from each provider's discovery
6519
+ * rules (not reported by the provider).
6520
+ */
6463
6521
  /**
6464
6522
  * The hooks that apply to a session's working folder. For Claude Code: `~/.claude/settings.json`,
6465
6523
  * the working folder's own `.claude/settings.json` (no parent fallback), and
@@ -6483,6 +6541,7 @@ object({
6483
6541
  cwd: string(),
6484
6542
  /** Memory, rule and auto-memory files that exist, broadest scope first. */
6485
6543
  memory: array(AgentConfigFile),
6544
+ /** Skills found from the working directory, switched-off ones included (`enabled: false`). */
6486
6545
  skills: array(AgentSkill),
6487
6546
  subagents: array(AgentConfigFile),
6488
6547
  /** Enabled MCP servers, including CodeDen's own. */
@@ -6627,7 +6686,14 @@ const UserInput = object({
6627
6686
  * sender without reading the text, a session's opening brief included. Absent on the user's
6628
6687
  * own messages and on every message stored before senders were recorded.
6629
6688
  */
6630
- from: MessageSender.optional()
6689
+ from: MessageSender.optional(),
6690
+ /**
6691
+ * Set by CodeDen on a message the orchestrator passes on to a PM from a turn the user's own
6692
+ * message started (§8.2): the PM's turn it starts carries the user's request, like a message
6693
+ * the user wrote to the PM. Only such turns may start work on issues by authors who aren't
6694
+ * trusted. Absent on every other message.
6695
+ */
6696
+ relayedForUser: boolean().optional()
6631
6697
  });
6632
6698
  /**
6633
6699
  * The first line of a message from a PM or the orchestrator, telling the agent who wrote it.
@@ -6688,7 +6754,8 @@ const ToolOutput = object({
6688
6754
  /** Artifact holding the full output when truncated. */
6689
6755
  artifactId: Id.optional()
6690
6756
  });
6691
- _enum([
6757
+ /** What kind of work a tool call performs; drives rendering, never provider-specific. */
6758
+ const ToolKind = _enum([
6692
6759
  "command",
6693
6760
  "file_edit",
6694
6761
  "file_read",
@@ -6862,6 +6929,11 @@ const ApprovalRequestItem = object({
6862
6929
  toolInput: JsonValue.optional(),
6863
6930
  /** The tool call item this approval gates, if any. */
6864
6931
  toolCallItemId: Id.optional(),
6932
+ /**
6933
+ * A link the request asks the user to open (an MCP server's URL elicitation, e.g. to sign
6934
+ * in). The card shows it; nothing opens it on the user's behalf. Web links only.
6935
+ */
6936
+ url: url({ protocol: /^https?$/ }).optional(),
6865
6937
  options: array(ApprovalDecision).default([
6866
6938
  "allow_once",
6867
6939
  "allow_session",
@@ -6900,7 +6972,31 @@ const UserQuestionItem = object({
6900
6972
  * Asked through CodeDen's `ask_user` tool rather than the provider: stays open after its
6901
6973
  * turn ends, and an answer that arrives after the tool returned is sent as a new message.
6902
6974
  */
6903
- deferred: boolean().optional()
6975
+ deferred: boolean().optional(),
6976
+ /**
6977
+ * The MCP server asking, when the questions are its form (an MCP elicitation) rather than the
6978
+ * agent's own: the answers go to that server as data. Unattended sessions withdraw such a form
6979
+ * instead of answering it (§10.1).
6980
+ */
6981
+ mcpServer: string().min(1).optional()
6982
+ });
6983
+ /**
6984
+ * Tokens one model used in a subagent run, in the four billed categories (§7.3, §12.6). The
6985
+ * server prices them when it reports the run's cost, like a turn's: the provider's USD figure
6986
+ * when it gave one, else the price table.
6987
+ */
6988
+ const SubagentModelUsage = object({
6989
+ /** Model id as the provider reports it; absent when it doesn't say (the session's model). */
6990
+ model: string().min(1).optional(),
6991
+ /** Uncached input tokens (excludes cache reads and writes). */
6992
+ inputTokens: int().nonnegative(),
6993
+ outputTokens: int().nonnegative(),
6994
+ cacheReadTokens: int().nonnegative(),
6995
+ cacheWriteTokens: int().nonnegative(),
6996
+ /** Reasoning/thinking tokens where reported; already counted in `outputTokens`. */
6997
+ reasoningTokens: int().nonnegative().optional(),
6998
+ /** USD the provider reported for these calls, when it reports one. */
6999
+ costUsd: number$1().nonnegative().optional()
6904
7000
  });
6905
7001
  /**
6906
7002
  * What a finished subagent used, as its provider reports it (§7.3). Each figure is optional:
@@ -6913,7 +7009,12 @@ const SubagentStats = object({
6913
7009
  /** Tool calls the subagent made. */
6914
7010
  toolUses: int().nonnegative().optional(),
6915
7011
  /** How long the subagent ran, in milliseconds. */
6916
- durationMs: int().nonnegative().optional()
7012
+ durationMs: int().nonnegative().optional(),
7013
+ /**
7014
+ * Tokens per model across the run's own model calls (a nested subagent's are on its own
7015
+ * card), for the card's cost (`SessionUsage.subagents`). Display only, like the rest.
7016
+ */
7017
+ usage: array(SubagentModelUsage).optional()
6917
7018
  });
6918
7019
  /** A subagent run; its transcript lives in the child thread `childThreadId` (§7.3). */
6919
7020
  const SubagentItem = object({
@@ -6989,6 +7090,10 @@ const ItemDelta = discriminatedUnion("type", [
6989
7090
  partialJson: string()
6990
7091
  })
6991
7092
  ]);
7093
+ /** The text a delta appends (`partialJson` for tool input). */
7094
+ function itemDeltaText(delta) {
7095
+ return delta.type === "tool_input" ? delta.partialJson : delta.text;
7096
+ }
6992
7097
  /** The first line of `text`, cut to `max` characters (with an ellipsis when cut). */
6993
7098
  function oneLineDescription(text, max = 200) {
6994
7099
  const line = text.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
@@ -7033,6 +7138,14 @@ object({
7033
7138
  /** Hint for the arguments it takes, e.g. `[issue-number]`. */
7034
7139
  argumentHint: string().optional()
7035
7140
  });
7141
+ /**
7142
+ * `GET /api/projects/:id/commands?provider=`: the `/` menu of a session that does not exist yet
7143
+ * (the New session draft), from the skills and custom command files that `provider` would load
7144
+ * in the project folder. Nothing is reported by a provider until the session runs.
7145
+ */
7146
+ const ProjectCommandsQuery = object({
7147
+ /** The provider the draft would start with. */
7148
+ provider: ProviderId });
7036
7149
  // ../../packages/contracts/src/agent-events.ts
7037
7150
  /**
7038
7151
  * Token/cost/rate-limit usage. Providers fill in what they report; everything is optional.
@@ -7100,16 +7213,25 @@ const TurnStartedEvent = object({
7100
7213
  type: literal("turn.started"),
7101
7214
  ...turnScoped
7102
7215
  });
7216
+ /**
7217
+ * Where the provider's conversation stands at the end of a root turn, as an opaque position:
7218
+ * `forkConversation(token, { upTo })` copies the conversation through this turn and no
7219
+ * further (§9). Reported only by adapters whose `forkConversation` accepts it, and only when
7220
+ * the adapter knows the position. Absent: the conversation can't be forked at this turn.
7221
+ */
7222
+ const forkPoint = { forkPoint: string().min(1).optional() };
7103
7223
  const TurnCompletedEvent = object({
7104
7224
  type: literal("turn.completed"),
7105
7225
  ...turnScoped,
7106
7226
  /** Provider stop reason, e.g. `end_turn`, `max_tokens`. */
7107
- stopReason: string().optional()
7227
+ stopReason: string().optional(),
7228
+ ...forkPoint
7108
7229
  });
7109
7230
  const TurnFailedEvent = object({
7110
7231
  type: literal("turn.failed"),
7111
7232
  ...turnScoped,
7112
- error: ErrorInfo
7233
+ error: ErrorInfo,
7234
+ ...forkPoint
7113
7235
  });
7114
7236
  const TurnInterruptedEvent = object({
7115
7237
  type: literal("turn.interrupted"),
@@ -7119,7 +7241,8 @@ const TurnInterruptedEvent = object({
7119
7241
  "user",
7120
7242
  "runner_restart",
7121
7243
  "provider"
7122
- ])
7244
+ ]),
7245
+ ...forkPoint
7123
7246
  });
7124
7247
  /** A new item appeared in a turn (possibly still in progress). */
7125
7248
  const ItemStartedEvent = object({
@@ -7170,6 +7293,33 @@ const AgentEvent = discriminatedUnion("type", [
7170
7293
  UsageUpdatedEvent
7171
7294
  ]);
7172
7295
  AgentEvent.options.map((o) => o.shape.type.value);
7296
+ /**
7297
+ * Merge two consecutive `item.delta` events into one that appends both texts, when they
7298
+ * extend the same item the same way: same thread, turn and item, same delta type and, for
7299
+ * tool output, the same stream. Applying the result equals applying `a` then `b`. Returns
7300
+ * `undefined` when they can't be merged.
7301
+ */
7302
+ function mergeItemDeltas(a, b) {
7303
+ if (a.itemId !== b.itemId || a.turnId !== b.turnId || a.threadId !== b.threadId) return;
7304
+ const x = a.delta;
7305
+ const y = b.delta;
7306
+ if (x.type === "tool_input" && y.type === "tool_input") return {
7307
+ ...b,
7308
+ delta: {
7309
+ ...y,
7310
+ partialJson: x.partialJson + y.partialJson
7311
+ }
7312
+ };
7313
+ if (x.type === "tool_output" && y.type === "tool_output" && x.stream !== y.stream) return;
7314
+ if (x.type !== y.type || x.type === "tool_input" || y.type === "tool_input") return;
7315
+ return {
7316
+ ...b,
7317
+ delta: {
7318
+ ...y,
7319
+ text: x.text + y.text
7320
+ }
7321
+ };
7322
+ }
7173
7323
  // ../../packages/contracts/src/entities/inbox.ts
7174
7324
  /** What an inbox item asks of the user (§11.1). */
7175
7325
  const InboxItemKind = _enum([
@@ -7677,6 +7827,17 @@ const McpServerConfig = discriminatedUnion("transport", [object({
7677
7827
  args: array(string()).default([]),
7678
7828
  env: record(string(), string()).optional()
7679
7829
  })]);
7830
+ /**
7831
+ * CodeDen's own credential files, relative to its data directory (§15): the install token the
7832
+ * local API accepts, the runner lock holding the runner's bearer token, and the remote access
7833
+ * store (password hash and device sessions). An agent that read the tokens could drive CodeDen
7834
+ * itself, so agents never read or write these without the user, at any level (§11.2).
7835
+ */
7836
+ const CODEDEN_CREDENTIAL_FILES = [
7837
+ "install-token",
7838
+ "runner/runner.json",
7839
+ "remote-access.json"
7840
+ ];
7680
7841
  /** Options for `ProviderAdapter.openSession`. */
7681
7842
  const SessionOptions = object({
7682
7843
  /** CodeDen session id. */
@@ -7716,7 +7877,13 @@ const SessionOptions = object({
7716
7877
  * No writes to the workspace (orchestrator and PMs, §11.2): file edits and writing commands
7717
7878
  * are denied natively (Claude permission policy, Codex read-only sandbox).
7718
7879
  */
7719
- readOnly: boolean().optional()
7880
+ readOnly: boolean().optional(),
7881
+ /**
7882
+ * CodeDen's data directory (§6). Touching its credential files (`CODEDEN_CREDENTIAL_FILES`)
7883
+ * asks at every level and is refused for read-only sessions (§11.2, §15); `$CODEDEN_HOME` in
7884
+ * the agent's commands resolves to it.
7885
+ */
7886
+ codedenHome: string().min(1).optional()
7720
7887
  });
7721
7888
  /** Response to an `approval_request` or `user_question` item (`ProviderSession.respond`). */
7722
7889
  const RuntimeResponse = discriminatedUnion("kind", [
@@ -7884,6 +8051,86 @@ const BrowserHandoffTab = object({
7884
8051
  /** The tab the panel showed. */
7885
8052
  active: boolean()
7886
8053
  });
8054
+ /** A key held down during an agent pointer action (`browser.mouse`). */
8055
+ const BrowserModifierKey = _enum([
8056
+ "Alt",
8057
+ "Control",
8058
+ "Meta",
8059
+ "Shift"
8060
+ ]);
8061
+ /** What an agent does at page coordinates (`browser.mouse`, §12.2 computer use, step 1). */
8062
+ const BrowserMouseAction = _enum([
8063
+ "click",
8064
+ "hover",
8065
+ "drag",
8066
+ "scroll"
8067
+ ]);
8068
+ /** Longest text `browser.type` types as key presses at the focused element (no `ref`). */
8069
+ const MAX_FOCUS_TYPE_CHARS = 2e3;
8070
+ /** Largest scroll per axis in one agent `scroll` (CSS pixels). */
8071
+ const MAX_AGENT_SCROLL = 1e4;
8072
+ /** A coordinate in the page viewport's CSS pixels; the runner checks it against the viewport. */
8073
+ const ViewportCoordinate = number$1().min(0).max(1e5);
8074
+ /**
8075
+ * The fields of an agent pointer action. Coordinates are CSS pixels of the tab's viewport,
8076
+ * origin top-left: the pixels of a viewport `browser.screenshot`, and the units of
8077
+ * `getBoundingClientRect`. Which fields an action takes is checked by `checkBrowserMouse`.
8078
+ */
8079
+ const BrowserMouseFields = {
8080
+ action: BrowserMouseAction,
8081
+ /** The point to act at; the start point of a drag. */
8082
+ x: ViewportCoordinate,
8083
+ y: ViewportCoordinate,
8084
+ /** Where a drag ends (`drag` only, required there). */
8085
+ toX: ViewportCoordinate.optional(),
8086
+ toY: ViewportCoordinate.optional(),
8087
+ /** Scroll amount in CSS pixels (`scroll` only, at least one non-zero); positive `deltaY` scrolls down. */
8088
+ deltaX: number$1().min(-1e4).max(MAX_AGENT_SCROLL).optional(),
8089
+ deltaY: number$1().min(-1e4).max(MAX_AGENT_SCROLL).optional(),
8090
+ /** `click` and `drag`; default `left`. */
8091
+ button: _enum([
8092
+ "left",
8093
+ "middle",
8094
+ "right"
8095
+ ]).optional(),
8096
+ /** `click` only: 2 is a double click, 3 a triple click. Default 1. */
8097
+ clickCount: int().min(1).max(3).optional(),
8098
+ /** Keys held for the whole action (shift-click, ctrl-click, alt-drag). */
8099
+ modifiers: array(BrowserModifierKey).max(4).optional()
8100
+ };
8101
+ /** Fields that belong to one action only. */
8102
+ const ACTION_ONLY_FIELDS = {
8103
+ toX: ["drag"],
8104
+ toY: ["drag"],
8105
+ deltaX: ["scroll"],
8106
+ deltaY: ["scroll"],
8107
+ button: ["click", "drag"],
8108
+ clickCount: ["click"]
8109
+ };
8110
+ /**
8111
+ * Cross-field rules of a pointer action, for `superRefine`: a drag needs `toX` and `toY`, a
8112
+ * scroll needs a non-zero delta, and fields of other actions are refused (so a mistaken call
8113
+ * fails instead of silently doing something else).
8114
+ */
8115
+ function checkBrowserMouse(value, ctx) {
8116
+ for (const [field, actions] of Object.entries(ACTION_ONLY_FIELDS)) if (value[field] !== void 0 && !actions.includes(value.action)) ctx.addIssue({
8117
+ code: "custom",
8118
+ path: [field],
8119
+ message: `${field} is for ${actions.join(" and ")}, not ${value.action}`
8120
+ });
8121
+ if (value.action === "drag") {
8122
+ for (const field of ["toX", "toY"]) if (value[field] === void 0) ctx.addIssue({
8123
+ code: "custom",
8124
+ path: [field],
8125
+ message: `A drag needs ${field}`
8126
+ });
8127
+ }
8128
+ if (value.action === "scroll" && !value.deltaX && !value.deltaY) ctx.addIssue({
8129
+ code: "custom",
8130
+ path: ["deltaY"],
8131
+ message: "A scroll needs a non-zero deltaX or deltaY"
8132
+ });
8133
+ }
7887
8134
  const TabTarget = {
7888
8135
  sessionId: Id,
7889
8136
  tabId: BrowserTabId.optional()
@@ -7986,15 +8233,26 @@ const BrowserCommands = [
7986
8233
  object({
7987
8234
  name: literal("browser.type"),
7988
8235
  ...TabTarget,
7989
- ref: BrowserElementRef,
8236
+ ref: BrowserElementRef.optional(),
7990
8237
  text: string().max(1e5),
7991
8238
  submit: boolean().optional()
8239
+ }).superRefine((v, ctx) => {
8240
+ if (v.ref === void 0 && v.text.length > 2e3) ctx.addIssue({
8241
+ code: "custom",
8242
+ path: ["text"],
8243
+ message: `Without a ref, type at most ${MAX_FOCUS_TYPE_CHARS} characters`
8244
+ });
7992
8245
  }),
7993
8246
  object({
7994
8247
  name: literal("browser.press"),
7995
8248
  ...TabTarget,
7996
8249
  key: string().min(1).max(60)
7997
8250
  }),
8251
+ object({
8252
+ name: literal("browser.mouse"),
8253
+ ...TabTarget,
8254
+ ...BrowserMouseFields
8255
+ }).superRefine(checkBrowserMouse),
7998
8256
  object({
7999
8257
  name: literal("browser.screenshot"),
8000
8258
  ...TabTarget,
@@ -8079,6 +8337,7 @@ const BrowserCommandResults = {
8079
8337
  "browser.click": Empty$1,
8080
8338
  "browser.type": Empty$1,
8081
8339
  "browser.press": Empty$1,
8340
+ "browser.mouse": Empty$1,
8082
8341
  "browser.screenshot": object({
8083
8342
  path: string(),
8084
8343
  width: int().positive(),
@@ -8144,7 +8403,12 @@ const RunnerCommand = discriminatedUnion("name", [
8144
8403
  name: literal("provider.detect"),
8145
8404
  providerId: ProviderId.optional(),
8146
8405
  /** Answer only once the model lists are refreshed, and include them as `providers`. */
8147
- waitForModels: boolean().optional()
8406
+ waitForModels: boolean().optional(),
8407
+ /**
8408
+ * `false`: only detect (installed, signed in), leaving the model lists alone; for periodic
8409
+ * availability checks, which must not start provider CLIs to list models. Default true.
8410
+ */
8411
+ refreshModels: boolean().optional()
8148
8412
  }),
8149
8413
  object({
8150
8414
  name: literal("provider.oneShot"),
@@ -8161,7 +8425,12 @@ const RunnerCommand = discriminatedUnion("name", [
8161
8425
  /** The resume token of the conversation to copy. */
8162
8426
  resumeToken: string().min(1),
8163
8427
  /** An existing folder of the project, for providers that run a process to fork. */
8164
- cwd: string().min(1)
8428
+ cwd: string().min(1),
8429
+ /**
8430
+ * Copy only through the turn that reported this fork point (`forkPoint` on a turn's end
8431
+ * event); later turns are left out. Omitted: the whole conversation.
8432
+ */
8433
+ upTo: string().min(1).optional()
8165
8434
  }),
8166
8435
  object({
8167
8436
  name: literal("workspace.prepare"),
@@ -8706,7 +8975,19 @@ const SessionStatus = _enum([
8706
8975
  */
8707
8976
  const ForkOrigin = object({
8708
8977
  sessionId: Id,
8709
- checkpoint: int().nonnegative().nullable()
8978
+ checkpoint: int().nonnegative().nullable(),
8979
+ /**
8980
+ * The source's turn the fork was taken at ("Fork from here"): its conversation ends with
8981
+ * that turn and its code is that turn's checkpoint. Absent or null: the source's latest
8982
+ * moment.
8983
+ */
8984
+ turnId: Id.nullable().optional(),
8985
+ /**
8986
+ * That turn's number among the source's own turns (1 is its first), counted when it was
8987
+ * forked. Checkpoint numbers can differ from it (a checkpoint that failed to write takes no
8988
+ * number), so this is what to show as "turn N". Absent when unknown.
8989
+ */
8990
+ turnNumber: int().positive().nullable().optional()
8710
8991
  });
8711
8992
  /** An agent session of any role (§8.1). */
8712
8993
  const Session = object({
@@ -8783,7 +9064,13 @@ const Turn = object({
8783
9064
  ]),
8784
9065
  startedAt: Timestamp,
8785
9066
  completedAt: Timestamp.nullable(),
8786
- error: ErrorInfo.nullable()
9067
+ error: ErrorInfo.nullable(),
9068
+ /**
9069
+ * The provider's position at the end of this turn (`forkPoint` on its end event): the
9070
+ * conversation can be forked through this turn. Absent or null when it can't, e.g. a turn
9071
+ * recorded before CodeDen kept these, or a provider that doesn't report them.
9072
+ */
9073
+ forkPoint: string().nullable().optional()
8787
9074
  });
8788
9075
  /** Item projection (§6 `items`): the latest state of an item plus its location. */
8789
9076
  const ItemRecord = object({
@@ -9276,6 +9563,71 @@ const FactorySetTypeRequest = object({
9276
9563
  type: WorkType,
9277
9564
  reason: string().trim().max(500).optional()
9278
9565
  });
9566
+ // ../../packages/contracts/src/factory-prs.ts
9567
+ /**
9568
+ * The factory's pull requests (§10.1, Merge): the loop owns every PR it opened until it merges
9569
+ * or closes, whatever its item's stage. It keeps them free of conflicts, merges them one at a
9570
+ * time (the merge queue) and rebuilds work whose PR keeps conflicting.
9571
+ */
9572
+ /**
9573
+ * Why a work item failed:
9574
+ * - `checks`: its own work fell short (merge checks after its revise rounds, a step it never
9575
+ * reported);
9576
+ * - `infrastructure`: something outside its code, such as the builder or reviewer session dying
9577
+ * or CI never starting while Actions can run. The loop puts it back by itself on the next
9578
+ * tick after the cause is gone, at most three times;
9579
+ * - `stuck`: it needs a person: it already used its rebuild (or its rebuild ended without a new
9580
+ * PR), CI can never run on the repository (Actions off, no active workflow), or it touches a
9581
+ * protected area (an `escalated` item, which carries this kind too).
9582
+ *
9583
+ * Null on items that never failed or were escalated, and on failures recorded before this
9584
+ * existed (the loop reads those from their reason).
9585
+ */
9586
+ const FactoryFailureKind = _enum([
9587
+ "checks",
9588
+ "infrastructure",
9589
+ "stuck"
9590
+ ]);
9591
+ /**
9592
+ * A factory PR's state, as the panel shows it:
9593
+ * - `checking`: the merge checks are running (the review, tests, evidence);
9594
+ * - `waiting_ci`: CI is running, or yet to start, on the head commit;
9595
+ * - `queued`: every check passed and it waits for its turn to merge;
9596
+ * - `updating`: at the head of the queue, being brought up to date with the default branch;
9597
+ * - `rebasing`: it has merge conflicts and an agent is rebasing it;
9598
+ * - `rebuilding`: it kept conflicting, so its work is being redone from the default branch;
9599
+ * the new PR replaces it;
9600
+ * - `superseded`: a newer PR for the same work replaced it (a rebuild's, or an integrator's);
9601
+ * the factory closes it with a link to the new one, retrying until GitHub accepts;
9602
+ * - `merged`: merged by the factory (or on GitHub) today;
9603
+ * - `waiting_for_you`: a person merges it (auto-merge off, an untrusted author, a protected
9604
+ * area, or checks that failed after the revise rounds); the factory still keeps it free of
9605
+ * conflicts;
9606
+ * - `stuck`: it needs a person before anything else can happen.
9607
+ */
9608
+ const FactoryPrState = _enum([
9609
+ "checking",
9610
+ "waiting_ci",
9611
+ "queued",
9612
+ "updating",
9613
+ "rebasing",
9614
+ "rebuilding",
9615
+ "superseded",
9616
+ "merged",
9617
+ "waiting_for_you",
9618
+ "stuck"
9619
+ ]);
9620
+ /** One pull request the factory owns, for the panel's merge queue. */
9621
+ const FactoryPrEntry = object({
9622
+ itemId: Id,
9623
+ title: string(),
9624
+ pr: GitHubPrRef,
9625
+ state: FactoryPrState,
9626
+ /** Place in the merge queue (1 merges next); null when it isn't queued. */
9627
+ queuePosition: int().positive().nullable(),
9628
+ /** The loop's last note about it (why it waits, what it is doing). */
9629
+ note: string().nullable()
9630
+ });
9279
9631
  // ../../packages/contracts/src/factory-api.ts
9280
9632
  /**
9281
9633
  * The factory loop of software-factory and dark-factory projects (§10.1): work items, merge
@@ -9428,6 +9780,14 @@ const FactoryItem = object({
9428
9780
  attempts: int().nonnegative(),
9429
9781
  /** Why it was rejected, failed or escalated. */
9430
9782
  reason: string().nullable(),
9783
+ /** Why it failed or was escalated: its own work, the infrastructure, or stuck until a person looks (see `FactoryFailureKind`). */
9784
+ failureKind: FactoryFailureKind.nullable().default(null),
9785
+ /**
9786
+ * The pull request a rebuild replaced: after three requests to rebase it still conflicted, so
9787
+ * the work was redone from the default branch and that PR is closed once the new one opens.
9788
+ * Set means the item's one rebuild is used.
9789
+ */
9790
+ rebuiltFrom: GitHubPrRef.nullable().default(null),
9431
9791
  /** Last thing the loop noted about it (waiting on a conflict, merge budget, ...). */
9432
9792
  note: string().nullable(),
9433
9793
  mergeSha: string().nullable(),
@@ -9463,6 +9823,19 @@ const LEGACY_SIZE_LIMITS = {
9463
9823
  maxChangedLines: 800,
9464
9824
  maxChangedFiles: 40
9465
9825
  };
9826
+ /**
9827
+ * What a review does when the provider chosen for it is unavailable (not installed, not signed
9828
+ * in, at its usage limit, or it just failed to run):
9829
+ * - `builder`: review on the builder's provider and model, in a fresh session;
9830
+ * - `any`: review on any available provider;
9831
+ * - `wait`: wait until the chosen provider is available again.
9832
+ * With no provider available at all, every choice waits.
9833
+ */
9834
+ const ReviewerFallback = _enum([
9835
+ "builder",
9836
+ "any",
9837
+ "wait"
9838
+ ]);
9466
9839
  /** Knobs of the loop beyond the required limits; all have safe defaults. */
9467
9840
  const FactoryTuning = object({
9468
9841
  /**
@@ -9494,8 +9867,22 @@ const FactoryTuning = object({
9494
9867
  maxIssuesPerRound: int().positive().default(3),
9495
9868
  /** Dark factory: the review areas the scout may use. */
9496
9869
  areas: array(DiscoveryArea).default(DiscoveryArea.options),
9497
- /** Provider for builders (`auto`: the project/role default). */
9870
+ /**
9871
+ * Provider for builders (`auto`: the project/role default). One that isn't available (not
9872
+ * installed, not signed in, at its usage limit) is skipped for another available provider.
9873
+ */
9498
9874
  builderProvider: union([literal("auto"), ProviderId]).default("auto"),
9875
+ /** Model for builders on `builderProvider`; `null` (the default): that provider's default. */
9876
+ builderModel: string().min(1).nullable().default(null),
9877
+ /**
9878
+ * Provider for the independent reviewer. `auto` (the default): a provider other than the
9879
+ * builder's when one is available, which gives the most independent review.
9880
+ */
9881
+ reviewerProvider: union([literal("auto"), ProviderId]).default("auto"),
9882
+ /** Model for reviewers on `reviewerProvider`; `null` (the default): that provider's default. */
9883
+ reviewerModel: string().min(1).nullable().default(null),
9884
+ /** What a review does when its chosen provider isn't available (see `ReviewerFallback`). */
9885
+ reviewerFallback: ReviewerFallback.default("builder"),
9499
9886
  /** Minutes to wait for main CI after a merge before calling it done. */
9500
9887
  watchTimeoutMin: int().positive().default(120),
9501
9888
  /**
@@ -9544,6 +9931,10 @@ const FactoryLoop = object({
9544
9931
  /** Main-branch commits whose failed checks already became items. */
9545
9932
  seenFailedShas: array(string()).default([]),
9546
9933
  tuning: FactoryTuning.prefault({}),
9934
+ /**
9935
+ * The last tick that changed the loop. A tick that changes nothing else saves nothing, so
9936
+ * this can be older than the last tick (`FactoryStatus.lastTickAt` has that).
9937
+ */
9547
9938
  lastTickAt: Timestamp.nullable(),
9548
9939
  updatedAt: Timestamp
9549
9940
  });
@@ -9719,6 +10110,11 @@ object({
9719
10110
  backlog: array(FactoryWorkItem),
9720
10111
  inProgress: array(FactoryWorkItem),
9721
10112
  mergeChecks: array(FactoryMergeChecks),
10113
+ /**
10114
+ * The pull requests the factory owns: the merge queue first, in the order they merge, then
10115
+ * the rest (being checked, rebased, rebuilt, waiting for you) and today's merges.
10116
+ */
10117
+ pullRequests: array(FactoryPrEntry).default([]),
9722
10118
  /** Merged, reverted, failed, rejected and escalated items, newest first. */
9723
10119
  recent: array(FactoryWorkItem),
9724
10120
  /** Issues by untrusted authors, newest first; nothing runs on them until let in. */
@@ -9729,6 +10125,10 @@ object({
9729
10125
  scout: ScoutStatus.nullable().default(null),
9730
10126
  visionExists: boolean(),
9731
10127
  lastDigest: FactoryDigest.nullable(),
10128
+ /**
10129
+ * When the loop last ticked (the panel's "last check"), changed or not. After a server
10130
+ * restart, the last tick that changed the loop until the first tick runs.
10131
+ */
9732
10132
  lastTickAt: Timestamp.nullable()
9733
10133
  });
9734
10134
  object({ item: FactoryItem });
@@ -10387,6 +10787,86 @@ const ConflictRecord = object({
10387
10787
  createdAt: Timestamp,
10388
10788
  resolvedAt: Timestamp.nullable()
10389
10789
  });
10790
+ // ../../packages/contracts/src/runners.ts
10791
+ /**
10792
+ * Runners the server drives (`docs/remote-runners.md` §8). The
10793
+ * home runner is the local one next to the server; later phases add one runner per sandboxed
10794
+ * session (Docker, Upstash Box). Each is a row of the `runners` table, projected from the
10795
+ * `runner.*` lifecycle events.
10796
+ */
10797
+ /** Record id of the home runner: the local runner next to the server. */
10798
+ const HOME_RUNNER_ID = "home";
10799
+ /**
10800
+ * Where a runner runs (its execution backend):
10801
+ * - `local`: a process on this machine with worktrees in `<home>/wt` (the home runner);
10802
+ * - `docker`: inside a container on this machine (or the Docker host the CLI points at);
10803
+ * - `upstash-box`: inside an Upstash Box in the cloud.
10804
+ */
10805
+ const RunnerBackendId = _enum([
10806
+ "local",
10807
+ "docker",
10808
+ "upstash-box"
10809
+ ]);
10810
+ /**
10811
+ * A runner's lifecycle as the server sees it:
10812
+ * - `provisioning`: its sandbox is being made;
10813
+ * - `starting`: the runner process is starting or the server is connecting to it;
10814
+ * - `connected`: handshake done; commands and events flow;
10815
+ * - `disconnected`: the connection dropped; the server is trying again;
10816
+ * - `paused`: the sandbox is paused (its disk is kept);
10817
+ * - `stopped`: the sandbox or process was stopped on purpose;
10818
+ * - `failed`: it could not be provisioned, started or connected (see `lastError`);
10819
+ * - `deleted`: the sandbox is gone. The record stays, so its sessions report which runner
10820
+ * they lost instead of falling back to another one.
10821
+ */
10822
+ const RunnerRecordState = _enum([
10823
+ "provisioning",
10824
+ "starting",
10825
+ "connected",
10826
+ "disconnected",
10827
+ "paused",
10828
+ "stopped",
10829
+ "failed",
10830
+ "deleted"
10831
+ ]);
10832
+ /** One runner the server knows about (a `runners` row). */
10833
+ const RunnerRecord = object({
10834
+ /** CodeDen's id for this runner; `HOME_RUNNER_ID` for the home runner. */
10835
+ id: Id,
10836
+ backend: RunnerBackendId,
10837
+ /** The home runner: provider detection, utility jobs, the orchestrator and PMs use it. */
10838
+ home: boolean(),
10839
+ /**
10840
+ * The runner's own id (its journal's, reported in `welcome`); null until it first connects.
10841
+ * `sessions.runnerId` holds this id.
10842
+ */
10843
+ runnerId: string().nullable(),
10844
+ /** The backend's handle on the sandbox (container id, box id); null for `local`. */
10845
+ sandboxRef: string().nullable(),
10846
+ /** The project and session a sandbox runner serves; null for the home runner. */
10847
+ projectId: Id.nullable(),
10848
+ sessionId: Id.nullable(),
10849
+ state: RunnerRecordState,
10850
+ /** CodeDen version the runner reported at its last handshake. */
10851
+ version: string().nullable(),
10852
+ /** Version of the sandbox image or snapshot it was made from; null for `local`. */
10853
+ imageVersion: string().nullable(),
10854
+ createdAt: Timestamp,
10855
+ /** When the server last connected to it or lost it. */
10856
+ lastSeenAt: Timestamp.nullable(),
10857
+ /** Why it last failed or disconnected, safe to show (never contains tokens). */
10858
+ lastError: string().nullable()
10859
+ });
10860
+ /** Event payload: the server registered a runner, or re-registered it with new details. */
10861
+ const RunnerRegisteredPayload = object({ runner: RunnerRecord });
10862
+ /** Event payload: a runner's sandbox is gone; its record stays with state `deleted`. */
10863
+ const RunnerDeletedPayload = object({ id: Id });
10864
+ /**
10865
+ * The optional runner record id carried by `runner.connected`, `runner.disconnected` and
10866
+ * `runner.draining`. Events stored before several runners existed have none; they belong to
10867
+ * the home runner.
10868
+ */
10869
+ const RunnerRecordRef = { id: Id.optional() };
10390
10870
  // ../../packages/contracts/src/updates.ts
10391
10871
  /**
10392
10872
  * `settings.updates.mode` (§16): `off` never asks the registry; `notify` checks and offers a
@@ -10989,16 +11469,21 @@ discriminatedUnion("type", [
10989
11469
  })),
10990
11470
  globalEvent("artifact.created", object({ artifact: Artifact })),
10991
11471
  globalEvent("settings.updated", object({ settings: Settings })),
11472
+ globalEvent("runner.registered", RunnerRegisteredPayload),
11473
+ globalEvent("runner.deleted", RunnerDeletedPayload),
10992
11474
  globalEvent("runner.connected", object({
11475
+ ...RunnerRecordRef,
10993
11476
  runnerId: string(),
10994
11477
  version: string(),
10995
11478
  protocolVersion: int()
10996
11479
  })),
10997
11480
  globalEvent("runner.disconnected", object({
11481
+ ...RunnerRecordRef,
10998
11482
  runnerId: string(),
10999
11483
  reason: string()
11000
11484
  })),
11001
11485
  globalEvent("runner.draining", object({
11486
+ ...RunnerRecordRef,
11002
11487
  runnerId: string(),
11003
11488
  reason: string()
11004
11489
  })),
@@ -11099,7 +11584,10 @@ object({
11099
11584
  scheduled: array(ScheduledRun).default([]),
11100
11585
  /** The orchestrator and PM sessions (§8.1), also present in `sessions`. */
11101
11586
  roleSessions: RoleSessionsRef.optional(),
11102
- runner: RunnerConnectionStatus
11587
+ /** The home runner's connection, the one the shell shows (§4.2). */
11588
+ runner: RunnerConnectionStatus,
11589
+ /** Every runner the server drives, the home runner first, with its live state. */
11590
+ runners: array(RunnerRecord).default([])
11103
11591
  });
11104
11592
  object({
11105
11593
  seq: int().nonnegative(),
@@ -11146,7 +11634,8 @@ const ServerToClientMessage = discriminatedUnion("type", [
11146
11634
  }),
11147
11635
  object({
11148
11636
  type: literal("runner"),
11149
- runner: RunnerConnectionStatus
11637
+ runner: RunnerConnectionStatus,
11638
+ runners: array(RunnerRecord).optional()
11150
11639
  }),
11151
11640
  object({
11152
11641
  type: literal("update"),
@@ -11282,7 +11771,13 @@ const StartSessionRequest = object({
11282
11771
  /** First message; the session starts idle when omitted. */
11283
11772
  input: UserInput.optional(),
11284
11773
  /** The caller's decision when a previous attempt returned `conflict`. */
11285
- conflict: ConflictDecision.optional()
11774
+ conflict: ConflictDecision.optional(),
11775
+ /**
11776
+ * The open persisted conflict this request is a new version of (the New session draft sent
11777
+ * again while its card is up). The server closes that conflict whatever this start returns;
11778
+ * a conflict that is closed already or belongs to another project is left alone.
11779
+ */
11780
+ replacesConflict: Id.optional()
11286
11781
  });
11287
11782
  /**
11288
11783
  * `POST /api/sessions` response: `StartWorkResult` (`started` / `conflict`) plus the outcomes
@@ -11306,9 +11801,15 @@ const StartSessionResponse = discriminatedUnion("status", [
11306
11801
  * `POST /api/sessions/:id/fork`: a new agent session that continues this one's conversation
11307
11802
  * (the provider's native fork) in a new worktree started from its latest checkpoint.
11308
11803
  */
11309
- const ForkSessionRequest = object({
11310
- /** Defaults to the source's title followed by "(fork)". */
11311
- title: string().trim().min(1).max(200).optional() });
11804
+ const ForkSessionRequest = object({
11805
+ /** Defaults to the source's title followed by "(fork)". */
11806
+ title: string().trim().min(1).max(200).optional(),
11807
+ /**
11808
+ * Fork from this finished turn of the source's root thread ("Fork from here"): the
11809
+ * conversation ends with it and the code is its checkpoint. Omitted: the latest moment.
11810
+ */
11811
+ turnId: Id.optional()
11812
+ });
11312
11813
  object({ sessionId: Id });
11313
11814
  /** Recorded with a session about how it was started (forced starts keep their reason). */
11314
11815
  const SessionStartMeta = object({
@@ -11379,7 +11880,12 @@ object({
11379
11880
  object({
11380
11881
  itemId: Id,
11381
11882
  text: string(),
11382
- totalBytes: int().nonnegative()
11883
+ /** UTF-8 size of `text`. */
11884
+ totalBytes: int().nonnegative(),
11885
+ /** The stored copy is cut: part of the output was never kept, so `text` isn't all of it. */
11886
+ truncated: boolean(),
11887
+ /** UTF-8 size of the tool's whole output when the request was answered (≥ `totalBytes`). */
11888
+ outputBytes: int().nonnegative()
11383
11889
  });
11384
11890
  /** `GET /api/inbox` query. Defaults to open items. */
11385
11891
  const ListInboxQuery = object({
@@ -12569,8 +13075,12 @@ const ListConflictsQuery = object({
12569
13075
  status: InboxItemStatus.optional(),
12570
13076
  projectId: Id.optional()
12571
13077
  });
12572
- /** `POST /api/conflicts/:id/resolve`: the decision (wait, skip, join or force with a reason). */
12573
- const ResolveConflictRequest = ConflictDecision;
13078
+ /**
13079
+ * `POST /api/conflicts/:id/resolve`: the decision (wait, skip, join or force with a reason).
13080
+ * `start` replaces the request saved with the conflict: the New session draft as it is when the
13081
+ * card is answered, so edits made after the first send are what starts. Same project only.
13082
+ */
13083
+ const ResolveConflictRequest = ConflictDecision.and(object({ start: StartSessionRequest.optional() }));
12574
13084
  object({
12575
13085
  conflict: ConflictRecord,
12576
13086
  result: StartSessionResponse
@@ -12870,6 +13380,66 @@ const ServerLock = object({
12870
13380
  version: string(),
12871
13381
  startedAt: Timestamp
12872
13382
  });
13383
+ /** A boolean from a query string (`true`/`false`) or from code. */
13384
+ const QueryBoolean = union([boolean(), _enum(["true", "false"]).transform((v) => v === "true")]);
13385
+ /** `GET /api/search/transcripts` query. */
13386
+ const TranscriptSearchQuery = object({
13387
+ /**
13388
+ * What to look for. Whitespace separates terms; every term must appear (in any order),
13389
+ * ignoring case and accents. Control characters are ignored.
13390
+ */
13391
+ q: string().trim().min(1).max(200),
13392
+ /** Only sessions of this project. */
13393
+ projectId: Id.optional(),
13394
+ /** Only sessions with this role (§8.1). */
13395
+ role: Role.optional(),
13396
+ /** Whether matches in archived sessions are included (they are by default). */
13397
+ includeArchived: QueryBoolean.default(true),
13398
+ /**
13399
+ * Continue after the previous page: its `nextCursor`. Omit for the newest matches. Opaque to
13400
+ * clients.
13401
+ */
13402
+ cursor: string().regex(/^\d{1,16}$/).optional(),
13403
+ /** Most hits to return. */
13404
+ limit: number().int().min(1).max(100).default(20)
13405
+ });
13406
+ /** A piece of a snippet: `match` marks text that matched the query (render it highlighted). */
13407
+ const SnippetPart = object({
13408
+ text: string(),
13409
+ match: boolean()
13410
+ });
13411
+ /** One transcript item that matched. Session details come from the app snapshot. */
13412
+ const TranscriptSearchHit = object({
13413
+ sessionId: Id,
13414
+ /** The thread holding the item: the session's root thread or a subagent's. */
13415
+ threadId: Id,
13416
+ itemId: Id,
13417
+ /**
13418
+ * The root-thread item to scroll to: the item itself, or for an item in a subagent's thread
13419
+ * the subagent card in the root thread that started it. Such a hit also opens `threadId` at
13420
+ * `itemId`.
13421
+ */
13422
+ anchorItemId: Id,
13423
+ /** Item kind, e.g. `user_message`, `assistant_message`, `tool_call`. */
13424
+ kind: string(),
13425
+ /** For tool calls, what kind of work it was (`command`, `file_edit`, ...). */
13426
+ toolKind: ToolKind.optional(),
13427
+ /** Text around the first match, in order; never HTML. */
13428
+ snippet: array(SnippetPart),
13429
+ /** When the item was created. */
13430
+ ts: Timestamp
13431
+ });
13432
+ object({
13433
+ /** The query as searched (trimmed, without control characters). */
13434
+ query: string(),
13435
+ hits: array(TranscriptSearchHit),
13436
+ /**
13437
+ * Pass as `cursor` to get the next page; null when every match has been returned. A page can
13438
+ * hold fewer than `limit` hits and still have a next one: each request looks at a bounded
13439
+ * number of candidates so a search never holds up the server.
13440
+ */
13441
+ nextCursor: string().nullable()
13442
+ });
12873
13443
  // ../../packages/contracts/src/session-files.ts
12874
13444
  /**
12875
13445
  * Read-only browsing of a session's working directory for the files panel (§12.3).
@@ -13101,16 +13671,43 @@ object({
13101
13671
  /** How each provider in the range is billed, for the "API-equivalent" label. */
13102
13672
  billing: record(ProviderId, BillingMode)
13103
13673
  });
13674
+ /** One turn's usage for the turn footer. */
13675
+ const TurnUsage = object({
13676
+ ...TokenCounts.shape,
13677
+ turnId: Id,
13678
+ costUsd: number$1().nonnegative(),
13679
+ costSource: CostSource
13680
+ });
13681
+ /** One model's share of a subagent run's cost. */
13682
+ const SubagentModelCost = object({
13683
+ ...TokenCounts.shape,
13684
+ /** The model the tokens were priced as (the session's model when the provider named none). */
13685
+ model: string(),
13686
+ /** The price table's name for the model, when it has one. */
13687
+ label: string().optional(),
13688
+ costUsd: number$1().nonnegative(),
13689
+ costSource: CostSource
13690
+ });
13691
+ /**
13692
+ * What a finished subagent run cost, priced from `SubagentStats.usage` when read (§7.3). Display
13693
+ * only: the run's tokens are already in its session's turns, so this is never added to totals.
13694
+ * `reported` when every model's cost came from the provider, `unpriced` when no model could be
13695
+ * priced, else `estimated`.
13696
+ */
13697
+ const SubagentCost = object({
13698
+ /** The `subagent` item. */
13699
+ itemId: Id,
13700
+ costUsd: number$1().nonnegative(),
13701
+ costSource: CostSource,
13702
+ models: array(SubagentModelCost)
13703
+ });
13104
13704
  object({
13105
13705
  sessionId: Id,
13106
13706
  totals: UsageTotals,
13107
13707
  billing: BillingMode,
13108
- turns: array(object({
13109
- ...TokenCounts.shape,
13110
- turnId: Id,
13111
- costUsd: number$1().nonnegative(),
13112
- costSource: CostSource
13113
- }))
13708
+ turns: array(TurnUsage),
13709
+ /** The cost of each finished subagent that reported tokens per model, for its card. */
13710
+ subagents: array(SubagentCost).default([])
13114
13711
  });
13115
13712
  object({
13116
13713
  windows: array(object({
@@ -13164,7 +13761,7 @@ object({
13164
13761
  * rest of the bundle, so the package version is the single source of truth at runtime;
13165
13762
  * everywhere else (workspace, tests) it is `SOURCE_VERSION`.
13166
13763
  */
13167
- const CODEDEN_VERSION = packageVersion() ?? "0.6.2";
13764
+ const CODEDEN_VERSION = packageVersion() ?? "0.7.0";
13168
13765
  function packageVersion() {
13169
13766
  const value = globalThis.__CODEDEN_PACKAGE_VERSION__;
13170
13767
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -13315,6 +13912,6 @@ async function listDescendants(rootPid) {
13315
13912
  }
13316
13913
  return out;
13317
13914
  }
13318
- export { StarterSetRequest as $, InboxItemKind as $n, NEVER as $r, FactoryAllowLargeChangeRequest as $t, BrowserHostClientMessage as A, EditQueuedMessageRequest as An, _null as Ar, LOCAL_STT_SAMPLE_RATE as At, AUTOMATION_IMPORT_LIMITS as B, BROWSER_RELAY_SECRET_HEADER as Bn, number$1 as Br, NOTICE_BATCH_CODE as Bt, RemoteAccessFile as C, AutomationGuards as Cn, providersWith as Cr, UpdateProjectRequest as Ct, SignInRequest as D, Session as Dn, number as Dr, READ_ALOUD_FULL_MAX_CHARS as Dt, SetNetworkPasswordResponse as E, TriagePolicy as En, ReasoningEffort as Er, agentEventToNewEvent as Et, BrowserNavigateRequest as F, SpeechSummaryRequest as Fn, discriminatedUnion as Fr, UpdateInstallRequest as Ft, AutomationFileFrontmatter as G, ConflictDecision as Gn, string as Gr, GithubIssue as Gt, DuplicateAutomationRequest as H, RuntimeResponse as Hn, optional as Hr, CreateGithubIssueRequest as Ht, BrowserNewTabRequest as I, TerminalClientMessage as In, int as Ir, UpdateStatus as It, CreateAutomationRequest as J, Project as Jn, url as Jr, GithubPr as Jt, AutomationGuardsPatch as K, StartedBy as Kn, union as Kr, GithubLabelRequest as Kt, BrowserOpenRequest as L, TerminalQuery as Ln, intersection as Lr, compareVersions as Lt, BrowserClientMessage as M, GitPushRequest as Mn, array as Mr, SttQuery as Mt, BrowserDevServerRequest as N, RestoreFilesRequest as Nn, boolean as Nr, TtsRequest as Nt, passwordProblem as O, SessionStatus as On, datetime as Or, Settings as Ot, BrowserHistoryRequest as P, SessionResult as Pn, custom as Pr, AppPointer as Pt, SetAutomationEnabledRequest as Q, InboxItem as Qn, prettifyError as Qr, parseGithubPrId as Qt, BrowserViewportRequest as R, RunnerCommandResults as Rn, literal as Rr, isNewerVersion as Rt, PASSWORD_RULE_TEXT as S, AutomationAction as Sn, agentConfigFeatures as Sr, UiClientMessage as St, SetNetworkPasswordRequest as T, AutomationTrigger as Tn, ProviderId as Tr, AGENT_EVENT_PREFIX as Tt, ExportAutomationsQuery as U, SessionOptions as Un, preprocess as Ur, GithubAuthorAssociation as Ut, AutomationImportSource as V, ProviderCapabilities as Vn, object as Vr, NoticeBatchData as Vt, ImportAutomationsRequest as W, ProjectConfigFile as Wn, record as Wr, GithubCommentRequest as Wt, ListAutomationsQuery as X, DEFAULT_VISION_PATH as Xn, toJSONSchema as Xr, TriageIssueRequest as Xt, ListAutomationRunsQuery as Y, sortProjects as Yn, ZodError as Yr, MergePrRequest as Yt, RunAutomationRequest as Z, FactoryLimits as Zn, safeParse$1 as Zr, githubPrId as Zt, ListNoticesQuery as _, FactorySetTypeRequest as _n, AgentConfigProvider as _r, SessionStartMeta as _t, childEnv as a, REVIEW_SUMMARY_MAX as an, messageFrom as ar, CreateProjectRequest as at, UpdateSessionRequest as b, Artifact as bn, McpServerTarget as br, SteerRequest as bt, UsageSettings as c, SetAutonomyModeRequest as cn, MCP_TEST_STDERR_LIMIT as cr, ForkSessionRequest as ct, RunnerLock as d, FactoryAdmitRequest as dn, HookHandler as dr, LoginToolId as dt, FactoryDecisionRequest as en, AgentEvent as er, TriageDecisionRequest as et, RunnerToServerMessage as f, FactoryAnalysisReport as fn, ShelvedHook as fr, ReorderProjectsRequest as ft, ListConflictsQuery as g, FactoryReproductionReport as gn, AgentConfigLocator as gr, SendMessageRequest as gt, ArchiveSessionRequest as h, FactoryPlanRoundRequest as hn, AgentConfigListQuery as hr, SecretKey as ht, listDescendants as i, PutVisionRequest as in, UserInput as ir, UpdateAutomationRequest as it, summarizeActivity as j, GitCommitRequest as jn, any as jr, LocalSttModelParams as jt, ImageTestRequest as k, DiffScope as kn, _enum as kr, speechExcerpt as kt, SessionFileQuery as l, isFactoryMode as ln, McpServerTestRequest as lr, ListInboxQuery as lt, ServerToRunnerMessage as m, FactoryFinding as mn, AgentConfigDiffRequest as mr, RespondRequest as mt, killTree as n, KillSwitchRequest as nn, oneLineDescription as nr, UpcomingRunsQuery as nt, CODEDEN_VERSION as o, ReviewVerdictSubmission as on, MCP_TEST_DEFAULT_TIMEOUT_MS as or, FileReadQuery as ot, ServerLock as p, FactoryFileIssueRequest as pn, AgentConfigDeleteQuery as pr, ResolveInboxRequest as pt, AutomationProjectRequest as q, WorkKey as qn, unknown as qr, GithubListQuery as qt, killTreeAndOrphans as r, LEGACY_SIZE_LIMITS as rn, winningCommands as rr, UpdateAutomationProjectSettingsRequest as rt, UsageQuery as s, SCOUT_UNAVAILABLE as sn, MCP_TEST_ERROR_LIMIT as sr, FileWriteRequest as st, isAlive as t, FactoryTuning as tn, SessionUpdatedEvent as tr, TriageReport as tt, RunnerEventMessage as u, truncateReviewSummary as un, HookChangeRequest as ur, ListSessionsQuery as ut, ResolveConflictRequest as v, ScoutState as vn, AgentConfigWriteRequest as vr, SetSecretRequest as vt, SetNetworkAccessRequest as w, AutomationRun as wn, AutonomyLevel as wr, HealthResponse as wt, NetworkStatus as x, Automation as xn, McpServerUpsertRequest as xr, ThreadItemsQuery as xt, RoleMessageRequest as y, pipelineStep as yn, McpServerDiffRequest as yr, StartSessionRequest as yt, BrowserWsQuery as z, SETUP_TAIL_LIMIT as zn, looseObject as zr, ConflictRecord as zt };
13915
+ export { SetAutomationEnabledRequest as $, ProjectConfigFile as $n, number$1 as $r, githubPrId as $t, ImageTestRequest as A, Session as An, AgentConfigProvider as Ar, speechExcerpt as At, BrowserWsQuery as B, TerminalQuery as Bn, number as Br, HOME_RUNNER_ID as Bt, PASSWORD_RULE_TEXT as C, Artifact as Cn, HookChangeRequest as Cr, UiClientMessage as Ct, SetNetworkPasswordResponse as D, AutomationRun as Dn, AgentConfigDiffRequest as Dr, agentEventToNewEvent as Dt, SetNetworkPasswordRequest as E, AutomationGuards as En, AgentConfigDeleteQuery as Er, AGENT_EVENT_PREFIX as Et, BrowserHistoryRequest as F, GitPushRequest as Fn, agentConfigFeatures as Fr, AppPointer as Ft, ImportAutomationsRequest as G, BrowserMouseAction as Gn, array as Gr, GithubAuthorAssociation as Gt, AutomationImportSource as H, SETUP_TAIL_LIMIT as Hn, _enum as Hr, NOTICE_BATCH_CODE as Ht, BrowserNavigateRequest as I, RestoreFilesRequest as In, providersWith as Ir, UpdateInstallRequest as It, AutomationProjectRequest as J, checkBrowserMouse as Jn, discriminatedUnion as Jr, GithubLabelRequest as Jt, AutomationFileFrontmatter as K, MAX_AGENT_SCROLL as Kn, boolean as Kr, GithubCommentRequest as Kt, BrowserNewTabRequest as L, SessionResult as Ln, AutonomyLevel as Lr, UpdateStatus as Lt, summarizeActivity as M, DiffScope as Mn, McpServerDiffRequest as Mr, LocalSttModelParams as Mt, BrowserClientMessage as N, EditQueuedMessageRequest as Nn, McpServerTarget as Nr, SttQuery as Nt, SignInRequest as O, AutomationTrigger as On, AgentConfigListQuery as Or, READ_ALOUD_FULL_MAX_CHARS as Ot, BrowserDevServerRequest as P, GitCommitRequest as Pn, McpServerUpsertRequest as Pr, TtsRequest as Pt, RunAutomationRequest as Q, SessionOptions as Qn, looseObject as Qr, TriageIssueRequest as Qt, BrowserOpenRequest as R, SpeechSummaryRequest as Rn, ProviderId as Rr, compareVersions as Rt, NetworkStatus as S, pipelineStep as Sn, SkillSwitchRequest as Sr, ThreadItemsQuery as St, SetNetworkAccessRequest as T, AutomationAction as Tn, ShelvedHook as Tr, HealthResponse as Tt, DuplicateAutomationRequest as U, BROWSER_RELAY_SECRET_HEADER as Un, _null as Ur, NoticeBatchData as Ut, AUTOMATION_IMPORT_LIMITS as V, RunnerCommandResults as Vn, datetime as Vr, ConflictRecord as Vt, ExportAutomationsQuery as W, BrowserModifierKey as Wn, any as Wr, CreateGithubIssueRequest as Wt, ListAutomationRunsQuery as X, ProviderCapabilities as Xn, intersection as Xr, GithubPr as Xt, CreateAutomationRequest as Y, CODEDEN_CREDENTIAL_FILES as Yn, int as Yr, GithubListQuery as Yt, ListAutomationsQuery as Z, RuntimeResponse as Zn, literal as Zr, MergePrRequest as Zt, ListConflictsQuery as _, FactoryPlanRoundRequest as _n, messageFrom as _r, SendMessageRequest as _t, childEnv as a, union as ai, LEGACY_SIZE_LIMITS as an, DEFAULT_VISION_PATH as ar, UpdateAutomationRequest as at, RoleMessageRequest as b, ScoutState as bn, MCP_TEST_STDERR_LIMIT as br, StartSessionRequest as bt, UsageSettings as c, ZodError as ci, ReviewVerdictSubmission as cn, InboxItemKind as cr, FileWriteRequest as ct, RunnerEventMessage as d, prettifyError as di, isFactoryMode as dn, mergeItemDeltas as dr, ListSessionsQuery as dt, object as ei, parseGithubPrId as en, ConflictDecision as er, StarterSetRequest as et, RunnerLock as f, NEVER as fi, truncateReviewSummary as fn, ProjectCommandsQuery as fr, LoginToolId as ft, ArchiveSessionRequest as g, FactoryFinding as gn, UserInput as gr, SecretKey as gt, ServerToRunnerMessage as h, FactoryFileIssueRequest as hn, itemDeltaText as hr, RespondRequest as ht, listDescendants as i, string as ii, KillSwitchRequest as in, sortProjects as ir, UpdateAutomationProjectSettingsRequest as it, BrowserHostClientMessage as j, SessionStatus as jn, AgentConfigWriteRequest as jr, LOCAL_STT_SAMPLE_RATE as jt, passwordProblem as k, TriagePolicy as kn, AgentConfigLocator as kr, Settings as kt, SessionFileQuery as l, toJSONSchema as li, SCOUT_UNAVAILABLE as ln, AgentEvent as lr, ForkSessionRequest as lt, ServerLock as m, FactoryAnalysisReport as mn, winningCommands as mr, ResolveInboxRequest as mt, killTree as n, preprocess as ni, FactoryDecisionRequest as nn, WorkKey as nr, TriageReport as nt, CODEDEN_VERSION as o, unknown as oi, PutVisionRequest as on, FactoryLimits as or, CreateProjectRequest as ot, RunnerToServerMessage as p, FactoryAdmitRequest as pn, oneLineDescription as pr, ReorderProjectsRequest as pt, AutomationGuardsPatch as q, MAX_FOCUS_TYPE_CHARS as qn, custom as qr, GithubIssue as qt, killTreeAndOrphans as r, record as ri, FactoryTuning as rn, Project as rr, UpcomingRunsQuery as rt, UsageQuery as s, url as si, REVIEW_SUMMARY_MAX as sn, InboxItem as sr, FileReadQuery as st, isAlive as t, optional as ti, FactoryAllowLargeChangeRequest as tn, StartedBy as tr, TriageDecisionRequest as tt, TranscriptSearchQuery as u, safeParse$1 as ui, SetAutonomyModeRequest as un, SessionUpdatedEvent as ur, ListInboxQuery as ut, ListNoticesQuery as v, FactoryReproductionReport as vn, MCP_TEST_DEFAULT_TIMEOUT_MS as vr, SessionStartMeta as vt, RemoteAccessFile as w, Automation as wn, HookHandler as wr, UpdateProjectRequest as wt, UpdateSessionRequest as x, checksPassed as xn, McpServerTestRequest as xr, SteerRequest as xt, ResolveConflictRequest as y, FactorySetTypeRequest as yn, MCP_TEST_ERROR_LIMIT as yr, SetSecretRequest as yt, BrowserViewportRequest as z, TerminalClientMessage as zn, ReasoningEffort as zr, isNewerVersion as zt };
13319
13916
 
13320
- //# sourceMappingURL=tree-CF7Q8tXK.js.map
13917
+ //# sourceMappingURL=tree-Bjv-rXir.js.map