neko-harness 0.5.2 → 0.5.4

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.
package/dist/gateway.js CHANGED
@@ -16604,6 +16604,47 @@ import { basename as basename4, dirname as dirname9, join as join13 } from "path
16604
16604
  import { createHost as createHost2 } from "@earendil-works/chord";
16605
16605
  import { BACKGROUND_CONTEXT as BACKGROUND_CONTEXT4 } from "@earendil-works/chord/context";
16606
16606
 
16607
+ // packages/core/src/codemode-mode.ts
16608
+ var CODEMODE_NAME = "codemode";
16609
+ var CODEMODE_MODES = ["off", "on", "only"];
16610
+ var DEFAULT_CODEMODE_MODE = "off";
16611
+ var isCodemodeMode = (value) => typeof value === "string" && CODEMODE_MODES.includes(value);
16612
+ var CODEMODE_LIMITS = {
16613
+ memoryLimitBytes: 256 * 1024 * 1024,
16614
+ maxWorkers: 2,
16615
+ waitMs: 60000
16616
+ };
16617
+ var CODEMODE_VIEW_LIMITS = {
16618
+ maxCalls: 100,
16619
+ argsChars: 200,
16620
+ errorChars: 200
16621
+ };
16622
+ function parseCodemodeMode(value, field = "codemode.mode") {
16623
+ if (!isCodemodeMode(value))
16624
+ throw new Error(`"${field}" must be one of ${CODEMODE_MODES.join(", ")}.`);
16625
+ return value;
16626
+ }
16627
+ function parseCodemodeSettings(value, prefix = "codemode") {
16628
+ if (value === null || typeof value !== "object" || Array.isArray(value))
16629
+ throw new Error(`"${prefix}" must be an object.`);
16630
+ const { mode, ...rest } = value;
16631
+ const unknown = Object.keys(rest)[0];
16632
+ if (unknown !== undefined)
16633
+ throw new Error(`"${prefix}.${unknown}" is not a known setting.`);
16634
+ return mode === undefined ? {} : { mode: parseCodemodeMode(mode, `${prefix}.mode`) };
16635
+ }
16636
+ function resolveCodemodeSettings(config, saved) {
16637
+ if (isCodemodeMode(saved))
16638
+ return { effective: { mode: saved }, source: "saved" };
16639
+ if (config?.mode !== undefined)
16640
+ return { effective: { mode: config.mode }, source: "config" };
16641
+ return { effective: { mode: DEFAULT_CODEMODE_MODE }, source: "default" };
16642
+ }
16643
+ // packages/core/src/commits.ts
16644
+ function onCommits(session, listener) {
16645
+ const observation = session.observeCommits({ advance: (publication) => listener(publication), end: () => {} });
16646
+ return () => observation.detach();
16647
+ }
16607
16648
  // packages/core/src/config.ts
16608
16649
  import { existsSync, readFileSync } from "fs";
16609
16650
  import { homedir } from "os";
@@ -16873,6 +16914,10 @@ function validate(config, source) {
16873
16914
  if (level !== undefined && typeof level !== "string") {
16874
16915
  throw new Error(`neko config: ${source}: "thinkingLevel" must be a string.`);
16875
16916
  }
16917
+ const maxTurns = config.maxTurns;
16918
+ if (maxTurns !== undefined && !(Number.isSafeInteger(maxTurns) && maxTurns >= 1)) {
16919
+ throw new Error(`neko config: ${source}: "maxTurns" must be an integer of 1 or more.`);
16920
+ }
16876
16921
  const storagePath = config.storagePath;
16877
16922
  if (storagePath !== undefined && typeof storagePath !== "string") {
16878
16923
  throw new Error(`neko config: ${source}: "storagePath" must be a string.`);
@@ -16934,6 +16979,73 @@ function validate(config, source) {
16934
16979
  throw new Error(`neko config: ${source}: ${error instanceof Error ? error.message : String(error)}`);
16935
16980
  }
16936
16981
  }
16982
+ if (config.codemode !== undefined) {
16983
+ try {
16984
+ parseCodemodeSettings(config.codemode);
16985
+ } catch (error) {
16986
+ throw new Error(`neko config: ${source}: ${error instanceof Error ? error.message : String(error)}`);
16987
+ }
16988
+ }
16989
+ }
16990
+ // packages/core/src/conversation-follow.ts
16991
+ import {
16992
+ ConversationTranscript
16993
+ } from "@earendil-works/pi-durable";
16994
+ var PAGE_SIZE = 200;
16995
+ async function followConversation(conversation, context) {
16996
+ const transcript = new ConversationTranscript((query, limit, ctx) => conversation.entries(query, limit, undefined, ctx), { pageSize: PAGE_SIZE });
16997
+ const watch = await conversation.watch(context);
16998
+ const listeners = new Set;
16999
+ let byId;
17000
+ let value;
17001
+ const complete = async (view) => {
17002
+ while (view.items[0]?.kind === "earlier") {
17003
+ const before = view.items;
17004
+ view = await transcript.loadEarlier(context);
17005
+ if (view.items === before)
17006
+ break;
17007
+ }
17008
+ for (let gap = view.items.find((item) => item.kind === "gap");gap; ) {
17009
+ const before = view.items;
17010
+ view = await transcript.fillGap(gap, context);
17011
+ if (view.items === before)
17012
+ break;
17013
+ gap = view.items.find((item) => item.kind === "gap");
17014
+ }
17015
+ const { items, ...rest } = view;
17016
+ byId = undefined;
17017
+ return { ...rest, entries: items.flatMap((item) => item.kind === "entry" ? [item.entry] : []) };
17018
+ };
17019
+ try {
17020
+ value = await complete(transcript.merge(watch.value));
17021
+ } catch (error) {
17022
+ await watch.stop();
17023
+ throw error;
17024
+ }
17025
+ watch.start(async (call) => {
17026
+ if (call.kind !== "value")
17027
+ return;
17028
+ value = await complete(transcript.merge(call.value));
17029
+ for (const listener of listeners)
17030
+ listener(value);
17031
+ });
17032
+ return {
17033
+ get value() {
17034
+ return value;
17035
+ },
17036
+ subscribe(listener) {
17037
+ listeners.add(listener);
17038
+ return () => listeners.delete(listener);
17039
+ },
17040
+ entry(id) {
17041
+ byId ??= new Map(value.entries.map((entry) => [entry.id, entry]));
17042
+ return byId.get(id);
17043
+ },
17044
+ dispose() {
17045
+ listeners.clear();
17046
+ watch.stop();
17047
+ }
17048
+ };
16937
17049
  }
16938
17050
  // packages/core/src/credentials.ts
16939
17051
  import { chmod, mkdir, open, readFile, stat, unlink } from "fs/promises";
@@ -17146,6 +17258,36 @@ class FileCredentialStore {
17146
17258
  // packages/core/src/file-changes.ts
17147
17259
  import { isAbsolute as isAbsolute2, relative, resolve as resolve3, sep } from "path";
17148
17260
 
17261
+ // packages/core/src/change-limits.ts
17262
+ var CHANGE_LIMITS = {
17263
+ files: 500,
17264
+ opsPerFile: 50,
17265
+ diffBytes: 2 * 1024 * 1024,
17266
+ opDiffBytes: 64 * 1024,
17267
+ conversations: 64,
17268
+ nestedRecords: 1000
17269
+ };
17270
+ function clipDiff(diff) {
17271
+ if (Buffer.byteLength(diff, "utf8") <= CHANGE_LIMITS.opDiffBytes)
17272
+ return { diff, truncated: false };
17273
+ const lines = diff.split(`
17274
+ `);
17275
+ const kept = [];
17276
+ let bytes = 0;
17277
+ for (const line of lines) {
17278
+ bytes += Buffer.byteLength(line, "utf8") + 1;
17279
+ if (bytes > CHANGE_LIMITS.opDiffBytes)
17280
+ break;
17281
+ kept.push(line);
17282
+ }
17283
+ return { diff: kept.join(`
17284
+ `), truncated: true };
17285
+ }
17286
+
17287
+ // packages/core/src/nested-changes.ts
17288
+ import { defineDoc, wrapTool } from "@earendil-works/pi-durable";
17289
+ import { createEditTool, createWriteTool } from "@earendil-works/pi-durable/tools";
17290
+
17149
17291
  // packages/core/src/image-prompt.ts
17150
17292
  var DEFAULT_IMAGE_PROMPT = {
17151
17293
  version: 2,
@@ -17310,6 +17452,47 @@ var interactionField = (name, details) => {
17310
17452
  const interaction = toolInteraction(name, details);
17311
17453
  return interaction ? { interaction } : {};
17312
17454
  };
17455
+ var clip2 = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
17456
+ function codemodeView(calls, fullOutputPath) {
17457
+ const limit = CODEMODE_VIEW_LIMITS.maxCalls;
17458
+ return { calls: calls.slice(0, limit), omitted: Math.max(0, calls.length - limit), fullOutputPath };
17459
+ }
17460
+ function liveCodemodeView(live, callId) {
17461
+ const slots = (live.nestedTools ?? []).filter((slot) => slot.parentCallId === callId);
17462
+ return codemodeView(slots.map((slot, index) => {
17463
+ const key = /\/c(\d+)$/.exec(slot.callId)?.[1];
17464
+ const summary = slot.summary;
17465
+ return {
17466
+ id: key === undefined ? index + 1 : Number(key),
17467
+ name: slot.name,
17468
+ args: clip2(JSON.stringify(slot.arguments) ?? "", CODEMODE_VIEW_LIMITS.argsChars),
17469
+ status: slot.status !== "done" ? "running" : summary === undefined ? "cancelled" : summary.isError ? "error" : "ok",
17470
+ durationMs: summary?.durationMs ?? null,
17471
+ error: summary?.error === undefined ? null : clip2(summary.error, CODEMODE_VIEW_LIMITS.errorChars)
17472
+ };
17473
+ }), null);
17474
+ }
17475
+ function codemodeResultView(details) {
17476
+ if (!isRecord(details) || !Array.isArray(details.calls))
17477
+ return;
17478
+ const calls = details.calls.flatMap((call) => {
17479
+ if (!isRecord(call) || typeof call.name !== "string")
17480
+ return [];
17481
+ const { id, args, status, durationMs, error } = call;
17482
+ return [
17483
+ {
17484
+ id: typeof id === "number" ? id : 0,
17485
+ name: call.name,
17486
+ args: typeof args === "string" ? clip2(args, CODEMODE_VIEW_LIMITS.argsChars) : "",
17487
+ status: status === "ok" || status === "error" || status === "cancelled" ? status : "running",
17488
+ durationMs: typeof durationMs === "number" ? durationMs : null,
17489
+ error: typeof error === "string" ? clip2(error, CODEMODE_VIEW_LIMITS.errorChars) : null
17490
+ }
17491
+ ];
17492
+ });
17493
+ return codemodeView(calls, typeof details.fullOutputPath === "string" ? details.fullOutputPath : null);
17494
+ }
17495
+ var codemodeField = (name, view) => name === CODEMODE_NAME && view ? { codemode: view } : {};
17313
17496
  function summarizeToolCall(name, args) {
17314
17497
  const record = args ?? {};
17315
17498
  switch (name) {
@@ -17342,6 +17525,12 @@ function summarizeToolCall(name, args) {
17342
17525
  `)
17343
17526
  };
17344
17527
  }
17528
+ case "codemode": {
17529
+ const code = typeof record.code === "string" ? record.code : "";
17530
+ const first = code.split(`
17531
+ `).find((line) => line.trim() !== "" && !line.trim().startsWith("// @options")) ?? "";
17532
+ return { args: truncateMiddle(first.trim(), 100), detail: code };
17533
+ }
17345
17534
  case "subagent":
17346
17535
  return {
17347
17536
  args: record.action === "list" ? "List agents" : `${record.agentId ?? record.type}: ${record.title}`,
@@ -17452,13 +17641,14 @@ function totalUsage(state) {
17452
17641
  }
17453
17642
  return total;
17454
17643
  }
17644
+ var RUN_FAILURE_DETAIL_CHARS = 500;
17455
17645
  function projectChatView(view, modelLabel, options = {}) {
17456
17646
  const live = view.docs["pi.live"] ?? {};
17457
17647
  const usage = totalUsage(view.docs["pi.usage"] ?? { models: {}, tools: {} });
17458
17648
  const agent = view.docs["pi.agent"];
17459
17649
  const agentModel = agent?.model ?? null;
17460
17650
  const contextWindow = options.models?.find((m) => `${m.provider}/${m.modelId}` === (agentModel ? `${agentModel.provider}/${agentModel.modelId}` : modelLabel))?.contextWindow ?? 0;
17461
- const tokens = contextTokens(options.contextEntries ?? view.entries);
17651
+ const tokens = contextTokens(view.entries);
17462
17652
  const results = new Map;
17463
17653
  for (const entry of view.entries) {
17464
17654
  for (const message of entry.model ?? []) {
@@ -17487,7 +17677,8 @@ function projectChatView(view, modelLabel, options = {}) {
17487
17677
  diff,
17488
17678
  diffAdded: counts?.added ?? null,
17489
17679
  diffRemoved: counts?.removed ?? null,
17490
- ...interactionField(message.toolName, message.details)
17680
+ ...interactionField(message.toolName, message.details),
17681
+ ...codemodeField(message.toolName, codemodeResultView(message.details))
17491
17682
  });
17492
17683
  }
17493
17684
  }
@@ -17585,7 +17776,8 @@ function projectChatView(view, modelLabel, options = {}) {
17585
17776
  diffAdded: synthesized?.added ?? null,
17586
17777
  diffRemoved: synthesized?.removed ?? null,
17587
17778
  diffTruncated: synthesized?.truncated ?? false,
17588
- ...interactionField(block.name, slot?.details)
17779
+ ...interactionField(block.name, slot?.details),
17780
+ ...codemodeField(block.name, slot?.status === "running" ? liveCodemodeView(live, block.id) : undefined)
17589
17781
  });
17590
17782
  }
17591
17783
  }
@@ -17608,10 +17800,18 @@ function projectChatView(view, modelLabel, options = {}) {
17608
17800
  percent: tokens !== null && contextWindow > 0 ? tokens / contextWindow * 100 : null
17609
17801
  },
17610
17802
  compaction: {
17611
- active: [...live.compactions ?? []],
17803
+ active: (live.compactions ?? []).map(({ trigger, ...status }) => ({ ...status, reason: trigger })),
17612
17804
  latest: options.compaction ?? null,
17613
- ...live.compactionFailure ? { failure: live.compactionFailure } : {}
17805
+ ...live.compactionFailure ? {
17806
+ failure: {
17807
+ taskId: live.compactionFailure.taskId,
17808
+ reason: live.compactionFailure.trigger,
17809
+ error: live.compactionFailure.error
17810
+ }
17811
+ } : {}
17614
17812
  },
17813
+ ...live.run === undefined && live.runFailure ? { runFailure: runFailureView(live.runFailure) } : {},
17814
+ continuable: true,
17615
17815
  model: agentModel !== null ? `${agentModel.provider}/${agentModel.modelId}` : modelLabel,
17616
17816
  agent: {
17617
17817
  model: agentModel,
@@ -17632,6 +17832,13 @@ function projectChatView(view, modelLabel, options = {}) {
17632
17832
  streaming: projectStreaming(live)
17633
17833
  };
17634
17834
  }
17835
+ function runFailureView(failure) {
17836
+ const detail = failure.detail?.replace(/\p{Cc}/gu, " ").trim();
17837
+ return {
17838
+ reason: failure.reason,
17839
+ ...detail ? { detail: detail.length > RUN_FAILURE_DETAIL_CHARS ? `${detail.slice(0, RUN_FAILURE_DETAIL_CHARS)}\u2026` : detail } : {}
17840
+ };
17841
+ }
17635
17842
  function withArgs(item, name, args) {
17636
17843
  const summary = summarizeToolCall(name, args);
17637
17844
  const synthesized = name === "write" ? synthesizedWriteDiff(args) : null;
@@ -17708,14 +17915,15 @@ ${block.text}`;
17708
17915
  diff: synthesized?.diff ?? null,
17709
17916
  diffAdded: synthesized?.added ?? null,
17710
17917
  diffRemoved: synthesized?.removed ?? null,
17711
- diffTruncated: synthesized?.truncated ?? false
17918
+ diffTruncated: synthesized?.truncated ?? false,
17919
+ ...codemodeField(block.name, slot?.status === "running" ? liveCodemodeView(live, block.id) : undefined)
17712
17920
  });
17713
17921
  }
17714
17922
  }
17715
17923
  return { thinking, text, toolCalls };
17716
17924
  }
17717
17925
  function contextTokens(entries) {
17718
- const compacted = entries.reduce((id, entry) => entry.kind === "pi.compaction" ? Math.max(id, entry.id) : id, 0);
17926
+ const compacted = entries.reduce((id, entry) => entry.kind === "pi.compaction" || entry.kind === "pi.reset" ? Math.max(id, entry.id) : id, 0);
17719
17927
  for (let i = entries.length - 1;i >= 0; i--) {
17720
17928
  const entry = entries[i];
17721
17929
  const message = entry.model?.[0];
@@ -17729,24 +17937,87 @@ function contextTokens(entries) {
17729
17937
  return null;
17730
17938
  }
17731
17939
 
17940
+ // packages/core/src/nested-changes.ts
17941
+ var NestedChangesDoc = defineDoc({
17942
+ kind: "app.neko.nested-changes",
17943
+ checkpointWhen: () => false,
17944
+ version: 1,
17945
+ scope: "conversation",
17946
+ history: "rewindable",
17947
+ fork: "asOf",
17948
+ initial: () => ({ total: 0, dropped: false, items: [] })
17949
+ });
17950
+ var bytesOf = (items) => items.reduce((sum, item) => sum + Buffer.byteLength(item.diff, "utf8"), 0);
17951
+ function addNestedChange(state, change) {
17952
+ const existing = state.items.findIndex((item) => item.id === change.id);
17953
+ if (existing !== -1)
17954
+ state.items.splice(existing, 1);
17955
+ else
17956
+ state.total += 1;
17957
+ state.items.push(change);
17958
+ let bytes = bytesOf(state.items);
17959
+ while (state.items.length > 1 && (state.items.length > CHANGE_LIMITS.nestedRecords || bytes > CHANGE_LIMITS.diffBytes)) {
17960
+ const evicted = state.items.shift();
17961
+ bytes -= Buffer.byteLength(evicted.diff, "utf8");
17962
+ state.dropped = true;
17963
+ }
17964
+ }
17965
+ function describe2(tool, args, details) {
17966
+ if (tool === "edit") {
17967
+ const raw = editResultDiff(details);
17968
+ if (raw === null)
17969
+ return { diff: "", added: 0, removed: 0, truncated: false };
17970
+ return { ...clipDiff(raw), ...countDiffLines(raw) };
17971
+ }
17972
+ return synthesizedWriteDiff(args) ?? { diff: "", added: 0, removed: 0, truncated: false };
17973
+ }
17974
+ function nestedChangeWraps() {
17975
+ const wrap = (reference, tool) => wrapTool(reference, (inner) => ({
17976
+ ...inner,
17977
+ execute: async (args, api, context) => {
17978
+ const result = await inner.execute(args, api, context);
17979
+ const path = args?.path;
17980
+ if (result.isError || typeof path !== "string" || path === "")
17981
+ return result;
17982
+ const task = await api.getTask(api.taskId, context);
17983
+ const input = task?.input;
17984
+ if (input?.kind !== "nested" || typeof input.parentCallId !== "string")
17985
+ return result;
17986
+ const parentCallId = input.parentCallId;
17987
+ const change = {
17988
+ id: api.callId,
17989
+ parentCallId,
17990
+ tool,
17991
+ path,
17992
+ ...describe2(tool, args, result.details)
17993
+ };
17994
+ await api.commit(async (tx) => {
17995
+ addNestedChange(await tx.doc(NestedChangesDoc, api.conversationId), change);
17996
+ }, context);
17997
+ return result;
17998
+ }
17999
+ }));
18000
+ return [wrap(createEditTool(), "edit"), wrap(createWriteTool(), "write")];
18001
+ }
18002
+
17732
18003
  // packages/core/src/subagents.ts
18004
+ import { StringEnum } from "@earendil-works/pi-ai";
17733
18005
  import {
17734
18006
  AssistantEntry,
17735
18007
  configure,
17736
- defineDoc as defineDoc2,
18008
+ defineDoc as defineDoc4,
17737
18009
  defineExtension,
17738
18010
  defineTask,
17739
18011
  defineTool,
17740
18012
  LiveDoc,
17741
18013
  UsageDoc
17742
18014
  } from "@earendil-works/pi-durable";
17743
- import { Type } from "typebox";
17744
- import { Value } from "typebox/value";
18015
+ import { arr, compile, int, lit, nul, num, obj, oneOf, opt, str, union } from "@earendil-works/pi-schema";
17745
18016
 
17746
18017
  // packages/core/src/plan-doc.ts
17747
- import { defineDoc } from "@earendil-works/pi-durable";
18018
+ import { defineDoc as defineDoc2 } from "@earendil-works/pi-durable";
17748
18019
  var PLAN_HISTORY = 5;
17749
- var PlanDoc = defineDoc({
18020
+ var PlanDoc = defineDoc2({
17750
18021
  kind: "app.neko.plan",
17751
18022
  checkpointWhen: () => false,
17752
18023
  version: 1,
@@ -17756,6 +18027,198 @@ var PlanDoc = defineDoc({
17756
18027
  initial: () => ({ mode: "normal", revision: 0, items: [] })
17757
18028
  });
17758
18029
  var PLAN_CODER_BLOCKED = "Plan mode is read-only: the coder subagent is unavailable. Use explore or plan subagents, or finish the plan first.";
18030
+ var PLAN_REMOVED_TOOLS = ["edit", "write", "job"];
18031
+ var PLAN_INSTRUCTIONS = [
18032
+ "You are in plan mode: research and plan only, and change nothing.",
18033
+ "- Allowed: read, find_files, search_text, web_search, web_extract, todo, ask_user, read-only subagents (explore, plan) and bash limited to read-only commands (listing, searching, git log/diff/status, version checks). Other commands are rejected.",
18034
+ "- Not available: edit, write, job and any command that modifies files, installs packages or starts processes. Do not try to work around this.",
18035
+ "- When you understand the task, call submit_plan once, alone in its turn, with a short title, ordered concrete steps and an optional note (risks, assumptions, verification). The user then approves it or asks for changes.",
18036
+ "- After submit_plan do nothing else. If the user asks for changes, research what is needed and submit a revised plan.",
18037
+ "- Use ask_user only for decisions you cannot settle by reading the code."
18038
+ ].join(`
18039
+ `);
18040
+
18041
+ // packages/core/src/agent-change.ts
18042
+ var ONLY_MODE_MAIN_TOOLS = ["todo", "ask_user", "subagent"];
18043
+ var named = (name) => ({ name });
18044
+ function onlyModeTools(plan, role) {
18045
+ return [
18046
+ CODEMODE_NAME,
18047
+ ...role === "main" ? [...ONLY_MODE_MAIN_TOOLS, ...plan === "plan" ? ["submit_plan"] : []] : []
18048
+ ];
18049
+ }
18050
+ function agentChangeFor({ plan, codemode, role }) {
18051
+ const planning = plan === "plan";
18052
+ return {
18053
+ tools: planning ? { remove: PLAN_REMOVED_TOOLS.map(named) } : null,
18054
+ modelTools: codemode === "only" ? onlyModeTools(plan, role).map(named) : planning ? { add: [named("submit_plan")] } : null,
18055
+ extensions: codemode === "off" ? { remove: [{ name: CODEMODE_NAME }] } : { add: [{ name: CODEMODE_NAME }] },
18056
+ instructions: planning ? PLAN_INSTRUCTIONS : null
18057
+ };
18058
+ }
18059
+ function enabledTools(agent) {
18060
+ const offered = new Set(agent.tools.map((tool) => tool.name));
18061
+ return [...agent.tools, ...agent.callable.filter((tool) => !offered.has(tool.name))];
18062
+ }
18063
+ var agentInPlanMode = (agent) => agent.instructions === PLAN_INSTRUCTIONS;
18064
+ function agentSyncChange(agent, state) {
18065
+ const desired = agentChangeFor(state);
18066
+ const planMismatch = agentInPlanMode(agent) !== (state.plan === "plan");
18067
+ const hasExtension = agent.extensions.some((extension) => extension.name === CODEMODE_NAME);
18068
+ const callable = new Set(agent.callable.map((tool) => tool.name));
18069
+ const offered = agent.tools.map((tool) => tool.name);
18070
+ let codemodeMismatch;
18071
+ if (state.codemode === "off")
18072
+ codemodeMismatch = hasExtension || callable.has(CODEMODE_NAME);
18073
+ else {
18074
+ codemodeMismatch = !hasExtension || !offered.includes(CODEMODE_NAME);
18075
+ if (state.codemode === "only") {
18076
+ const allowed = new Set(onlyModeTools(state.plan, state.role));
18077
+ codemodeMismatch ||= offered.some((name) => !allowed.has(name));
18078
+ }
18079
+ }
18080
+ if (!planMismatch && !codemodeMismatch)
18081
+ return;
18082
+ return {
18083
+ ...planMismatch ? { tools: desired.tools, instructions: desired.instructions } : {},
18084
+ modelTools: desired.modelTools,
18085
+ ...codemodeMismatch ? { extensions: desired.extensions } : {}
18086
+ };
18087
+ }
18088
+
18089
+ // packages/core/src/codemode.ts
18090
+ import { AsyncLocalStorage } from "async_hooks";
18091
+ import { CodemodeSandbox } from "@earendil-works/pi-codemode";
18092
+ import { defineDoc as defineDoc3, wrapTool as wrapTool2 } from "@earendil-works/pi-durable";
18093
+ import { createCodemodeExtension, createCodemodeTool } from "@earendil-works/pi-durable/codemode";
18094
+ var CodemodeSettingsDoc = defineDoc3({
18095
+ kind: "app.neko.codemode-settings",
18096
+ checkpointWhen: () => false,
18097
+ version: 1,
18098
+ scope: "conversation",
18099
+ history: "latest",
18100
+ fork: "initial",
18101
+ initial: () => ({ mode: null })
18102
+ });
18103
+ var CodemodeDoc = defineDoc3({
18104
+ kind: "app.neko.codemode",
18105
+ checkpointWhen: () => false,
18106
+ version: 1,
18107
+ scope: "conversation",
18108
+ history: "rewindable",
18109
+ fork: "asOf",
18110
+ initial: () => ({ mode: "off" })
18111
+ });
18112
+ var SANDBOX_BUSY = "Codemode sandbox is busy: too many scripts are running. Try again shortly.";
18113
+
18114
+ class WorkerLimiter {
18115
+ #max;
18116
+ #waitMs;
18117
+ #tickets = new AsyncLocalStorage;
18118
+ #waiters = [];
18119
+ #running = 0;
18120
+ #reserved = 0;
18121
+ constructor(limits = CODEMODE_LIMITS) {
18122
+ this.#max = limits.maxWorkers;
18123
+ this.#waitMs = limits.waitMs;
18124
+ }
18125
+ get running() {
18126
+ return this.#running;
18127
+ }
18128
+ get waiting() {
18129
+ return this.#waiters.length;
18130
+ }
18131
+ #free() {
18132
+ return this.#running + this.#reserved < this.#max;
18133
+ }
18134
+ #wake() {
18135
+ while (this.#waiters.length > 0 && this.#free()) {
18136
+ this.#reserved += 1;
18137
+ this.#waiters.shift()?.();
18138
+ }
18139
+ }
18140
+ acquireWorker = () => {
18141
+ const ticket = this.#tickets.getStore();
18142
+ if (ticket && !ticket.claimed) {
18143
+ ticket.claimed = true;
18144
+ this.#reserved -= 1;
18145
+ } else if (!this.#free())
18146
+ throw new Error(SANDBOX_BUSY);
18147
+ this.#running += 1;
18148
+ let released = false;
18149
+ return () => {
18150
+ if (released)
18151
+ return;
18152
+ released = true;
18153
+ this.#running -= 1;
18154
+ this.#wake();
18155
+ };
18156
+ };
18157
+ async#admit(signal) {
18158
+ signal?.throwIfAborted();
18159
+ if (this.#waiters.length === 0 && this.#free()) {
18160
+ this.#reserved += 1;
18161
+ return;
18162
+ }
18163
+ await new Promise((resolve, reject) => {
18164
+ let timer;
18165
+ const leave = () => {
18166
+ clearTimeout(timer);
18167
+ signal?.removeEventListener("abort", onAbort);
18168
+ };
18169
+ const grant = () => {
18170
+ leave();
18171
+ resolve();
18172
+ };
18173
+ const drop = (error) => {
18174
+ const index = this.#waiters.indexOf(grant);
18175
+ if (index === -1)
18176
+ return;
18177
+ this.#waiters.splice(index, 1);
18178
+ leave();
18179
+ reject(error);
18180
+ };
18181
+ const onAbort = () => drop(signal?.reason instanceof Error ? signal.reason : new Error("Aborted"));
18182
+ this.#waiters.push(grant);
18183
+ timer = setTimeout(() => drop(new Error(SANDBOX_BUSY)), this.#waitMs);
18184
+ signal?.addEventListener("abort", onAbort, { once: true });
18185
+ });
18186
+ }
18187
+ async run(signal, work) {
18188
+ await this.#admit(signal);
18189
+ const ticket = { claimed: false };
18190
+ try {
18191
+ return await this.#tickets.run(ticket, work);
18192
+ } finally {
18193
+ if (!ticket.claimed) {
18194
+ this.#reserved -= 1;
18195
+ this.#wake();
18196
+ }
18197
+ }
18198
+ }
18199
+ }
18200
+ function createCodemodeRuntime(limits = CODEMODE_LIMITS) {
18201
+ const limiter = new WorkerLimiter(limits);
18202
+ const sandbox = new CodemodeSandbox({
18203
+ memoryLimitBytes: CODEMODE_LIMITS.memoryLimitBytes,
18204
+ acquireWorker: limiter.acquireWorker
18205
+ });
18206
+ const options = { sandbox, models: false };
18207
+ const base = createCodemodeExtension(options);
18208
+ const reference = createCodemodeTool(options);
18209
+ const extension = {
18210
+ ...base,
18211
+ wraps: [
18212
+ ...base.wraps ?? [],
18213
+ ...nestedChangeWraps(),
18214
+ wrapTool2(reference, (tool) => ({
18215
+ ...tool,
18216
+ execute: (args, api, context) => limiter.run(context.abortSignal, () => tool.execute(args, api, context))
18217
+ }))
18218
+ ]
18219
+ };
18220
+ return { extension, limiter, close: () => sandbox.close() };
18221
+ }
17759
18222
 
17760
18223
  // packages/core/src/subagent-usage.ts
17761
18224
  var emptySubagentUsage = () => ({ models: {}, tools: {} });
@@ -17801,7 +18264,7 @@ function subagentUsageTotals(state) {
17801
18264
 
17802
18265
  // packages/core/src/subagents.ts
17803
18266
  var agentIdFor = (childId) => `agent-${childId}`;
17804
- var DelegationsDoc = defineDoc2({
18267
+ var DelegationsDoc = defineDoc4({
17805
18268
  kind: "app.neko.delegations",
17806
18269
  checkpointWhen: () => false,
17807
18270
  version: 1,
@@ -17810,7 +18273,7 @@ var DelegationsDoc = defineDoc2({
17810
18273
  fork: "initial",
17811
18274
  initial: () => ({ items: [] })
17812
18275
  });
17813
- var SubagentsDoc = defineDoc2({
18276
+ var SubagentsDoc = defineDoc4({
17814
18277
  kind: "app.neko.subagents",
17815
18278
  checkpointWhen: () => false,
17816
18279
  version: 1,
@@ -17819,7 +18282,7 @@ var SubagentsDoc = defineDoc2({
17819
18282
  fork: "initial",
17820
18283
  initial: () => ({ items: [] })
17821
18284
  });
17822
- var SubagentDoc = defineDoc2({
18285
+ var SubagentDoc = defineDoc4({
17823
18286
  kind: "app.neko.subagent",
17824
18287
  checkpointWhen: () => false,
17825
18288
  version: 1,
@@ -17828,7 +18291,7 @@ var SubagentDoc = defineDoc2({
17828
18291
  fork: "initial",
17829
18292
  initial: () => ({ role: null, parentId: null })
17830
18293
  });
17831
- var SlotsDoc = defineDoc2({
18294
+ var SlotsDoc = defineDoc4({
17832
18295
  kind: "app.neko.subagent-slots",
17833
18296
  checkpointWhen: () => false,
17834
18297
  version: 1,
@@ -17843,7 +18306,7 @@ var ROLE_PROMPTS = {
17843
18306
  var SUBAGENT_DELIVERY = " Work from the assigned task, your own prior conversation context and project resources; you cannot see the main conversation. Do not delegate or ask the user directly. Your final message must be a self-contained delivery to the main agent, including evidence, results and limitations. Skills never expand your allowed tools.";
17844
18307
  var MAIN_ONLY_TOOLS = new Set(["todo", "ask_user", "submit_plan"]);
17845
18308
  var MAIN_ONLY_EXTENSIONS = new Set(["neko-todos", "neko-questions", "neko-plan"]);
17846
- var READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract"]);
18309
+ var READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract", CODEMODE_NAME]);
17847
18310
  async function refreshDelegations(tx, id) {
17848
18311
  const doc = await tx.doc(DelegationsDoc, id);
17849
18312
  const identities = await tx.doc(SubagentsDoc, id);
@@ -17935,59 +18398,60 @@ async function acquireSlot(tx, round, cwd) {
17935
18398
  slots.items.push({ taskId: round.taskId, parentId: round.parentId, cwd, coder: round.role === "coder" });
17936
18399
  return true;
17937
18400
  }
17938
- var actionParameters = Type.Union([
17939
- Type.Object({
17940
- action: Type.Optional(Type.Literal("create")),
17941
- type: Type.Enum(["explore", "plan", "coder"]),
17942
- title: Type.String({ minLength: 1, maxLength: 80 }),
17943
- task: Type.String({ minLength: 1, maxLength: 32768 })
17944
- }, { additionalProperties: false }),
17945
- Type.Object({
17946
- action: Type.Literal("continue"),
17947
- agentId: Type.String(),
17948
- title: Type.String({ minLength: 1, maxLength: 80 }),
17949
- task: Type.String({ minLength: 1, maxLength: 32768 })
17950
- }, { additionalProperties: false }),
17951
- Type.Object({
17952
- action: Type.Literal("list"),
17953
- cursor: Type.Optional(Type.Integer({ minimum: 0 })),
17954
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 }))
17955
- }, { additionalProperties: false })
18401
+ var actionParameters = union([
18402
+ obj({
18403
+ action: opt(lit("create")),
18404
+ type: oneOf(["explore", "plan", "coder"]),
18405
+ title: str({ minLength: 1, maxLength: 80 }),
18406
+ task: str({ minLength: 1, maxLength: 32768 })
18407
+ }),
18408
+ obj({
18409
+ action: lit("continue"),
18410
+ agentId: str(),
18411
+ title: str({ minLength: 1, maxLength: 80 }),
18412
+ task: str({ minLength: 1, maxLength: 32768 })
18413
+ }),
18414
+ obj({
18415
+ action: lit("list"),
18416
+ cursor: opt(int({ minimum: 0 })),
18417
+ limit: opt(int({ minimum: 1, maximum: 100 }))
18418
+ })
17956
18419
  ]);
17957
- var parameters = Type.Object({
17958
- action: Type.Optional(Type.String({ enum: ["create", "continue", "list"], description: "Defaults to create." })),
17959
- type: Type.Optional(Type.String({ enum: ["explore", "plan", "coder"], description: "Required for create only." })),
17960
- agentId: Type.Optional(Type.String({ description: "Required for continue only; use an ID returned by create/list." })),
17961
- title: Type.Optional(Type.String({ minLength: 1, maxLength: 80, description: "Required for create/continue." })),
17962
- task: Type.Optional(Type.String({ minLength: 1, maxLength: 32768, description: "Required for create/continue." })),
17963
- cursor: Type.Optional(Type.Integer({ minimum: 0, description: "List pagination cursor only." })),
17964
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, description: "List page size only." }))
17965
- }, { additionalProperties: false });
17966
- var structuredOutputSchema = Type.Union([
17967
- Type.Object({
17968
- agentId: Type.String(),
17969
- roundId: Type.Integer(),
17970
- status: Type.Enum(["completed", "failed", "cancelled"]),
17971
- report: Type.String()
17972
- }, { additionalProperties: false }),
17973
- Type.Object({
17974
- items: Type.Array(Type.Object({
17975
- agentId: Type.String(),
17976
- parentId: Type.Integer(),
17977
- childId: Type.Integer(),
17978
- role: Type.Enum(["explore", "plan", "coder"]),
17979
- title: Type.String(),
17980
- status: Type.Enum(["idle", "queued", "running"]),
17981
- activeRoundId: Type.Union([Type.Integer(), Type.Null()]),
17982
- lastRoundId: Type.Integer(),
17983
- lastReport: Type.Optional(Type.Union([Type.String(), Type.Null()])),
17984
- roundCount: Type.Integer(),
17985
- taskTitle: Type.String(),
17986
- approvalCount: Type.Integer(),
17987
- createdAt: Type.Number()
17988
- }, { additionalProperties: false })),
17989
- next: Type.Union([Type.Integer(), Type.Null()])
17990
- }, { additionalProperties: false })
18420
+ var isActionParameters = compile(actionParameters).check;
18421
+ var parameters = obj({
18422
+ action: opt(StringEnum(["create", "continue", "list"], { description: "Defaults to create." })),
18423
+ type: opt(StringEnum(["explore", "plan", "coder"], { description: "Required for create only." })),
18424
+ agentId: opt(str({ description: "Required for continue only; use an ID returned by create/list." })),
18425
+ title: opt(str({ minLength: 1, maxLength: 80, description: "Required for create/continue." })),
18426
+ task: opt(str({ minLength: 1, maxLength: 32768, description: "Required for create/continue." })),
18427
+ cursor: opt(int({ minimum: 0, description: "List pagination cursor only." })),
18428
+ limit: opt(int({ minimum: 1, maximum: 100, description: "List page size only." }))
18429
+ });
18430
+ var structuredOutputSchema = union([
18431
+ obj({
18432
+ agentId: str(),
18433
+ roundId: int(),
18434
+ status: oneOf(["completed", "failed", "cancelled"]),
18435
+ report: str()
18436
+ }),
18437
+ obj({
18438
+ items: arr(obj({
18439
+ agentId: str(),
18440
+ parentId: int(),
18441
+ childId: int(),
18442
+ role: oneOf(["explore", "plan", "coder"]),
18443
+ title: str(),
18444
+ status: oneOf(["idle", "queued", "running"]),
18445
+ activeRoundId: union([int(), nul()]),
18446
+ lastRoundId: int(),
18447
+ lastReport: opt(union([str(), nul()])),
18448
+ roundCount: int(),
18449
+ taskTitle: str(),
18450
+ approvalCount: int(),
18451
+ createdAt: num()
18452
+ })),
18453
+ next: union([int(), nul()])
18454
+ })
17991
18455
  ]);
17992
18456
  var ADMISSION_POLL_MS = 100;
17993
18457
  function pause(runtime, ms) {
@@ -18131,8 +18595,9 @@ function createSubagentExtension(prepare, discard, guard) {
18131
18595
  parameters,
18132
18596
  structuredOutputSchema,
18133
18597
  replay: "safe",
18598
+ callers: ["model"],
18134
18599
  execute: async (input, api, context) => {
18135
- if (!Value.Check(actionParameters, input))
18600
+ if (!isActionParameters(input))
18136
18601
  throw new Error("Invalid subagent arguments. create requires type/title/task; continue requires agentId/title/task; list accepts only cursor/limit. Do not mix fields from different actions.");
18137
18602
  const args = input;
18138
18603
  if ((await api.snapshot(SubagentDoc, api.conversationId, context))?.role)
@@ -18181,12 +18646,17 @@ function createSubagentExtension(prepare, discard, guard) {
18181
18646
  if (!identity) {
18182
18647
  const created = child ?? await tx.createConversation({ ownership: { kind: "task", taskId: api.taskId } });
18183
18648
  const role = args.action === "continue" ? "explore" : args.type;
18649
+ const codemode = (await tx.doc(CodemodeDoc, api.conversationId)).mode;
18650
+ (await tx.doc(CodemodeDoc, created.id)).mode = codemode;
18651
+ const enabled = enabledTools(agent);
18184
18652
  await configure(tx, created.id, {
18185
18653
  model: agent.model,
18186
18654
  thinkingLevel: agent.thinkingLevel,
18187
18655
  cwd: agent.cwd,
18656
+ maxTurns: agent.maxTurns ?? null,
18188
18657
  extensions: agent.extensions.filter((e) => e.name !== extension.name && !MAIN_ONLY_EXTENSIONS.has(e.name)),
18189
- tools: agent.tools.filter((t) => t.name !== "subagent" && t.name !== "job" && !MAIN_ONLY_TOOLS.has(t.name) && (role === "coder" || READ_TOOLS.has(t.name) || role === "explore" && t.name === "bash"))
18658
+ modelTools: agentChangeFor({ plan: "normal", codemode, role: "subagent" }).modelTools,
18659
+ tools: enabled.filter((t) => t.name !== "subagent" && t.name !== "job" && !MAIN_ONLY_TOOLS.has(t.name) && (role === "coder" || READ_TOOLS.has(t.name) || role === "explore" && t.name === "bash"))
18190
18660
  });
18191
18661
  Object.assign(await tx.doc(SubagentDoc, created.id), { role, parentId: api.conversationId });
18192
18662
  identity = {
@@ -18289,9 +18759,10 @@ async function removeMainOnlyTools(harness, ids, context) {
18289
18759
  if (!(await harness.snapshot(SubagentDoc, id, context))?.role)
18290
18760
  continue;
18291
18761
  const agent = await (await harness.conversation(id, context)).agent(context);
18292
- if (!agent.tools.some((t) => MAIN_ONLY_TOOLS.has(t.name)))
18762
+ const enabled = enabledTools(agent);
18763
+ if (!enabled.some((t) => MAIN_ONLY_TOOLS.has(t.name)))
18293
18764
  continue;
18294
- await harness.commit((tx) => configure(tx, id, { tools: agent.tools.filter((t) => !MAIN_ONLY_TOOLS.has(t.name)) }), context);
18765
+ await harness.commit((tx) => configure(tx, id, { tools: enabled.filter((t) => !MAIN_ONLY_TOOLS.has(t.name)) }), context);
18295
18766
  }
18296
18767
  }
18297
18768
  async function installSubagentCoordinator(harness, parents, context) {
@@ -18327,7 +18798,7 @@ async function installSubagentCoordinator(harness, parents, context) {
18327
18798
  }, context);
18328
18799
  }
18329
18800
  }
18330
- const stop = harness.subscribeCommits(({ changes }) => {
18801
+ const stop = onCommits(harness, ({ changes }) => {
18331
18802
  for (const c of changes)
18332
18803
  if (c.type === "document" && c.record.kind === "app.neko.delegations" && c.conversationId !== undefined)
18333
18804
  known.add(c.conversationId);
@@ -18338,13 +18809,6 @@ async function installSubagentCoordinator(harness, parents, context) {
18338
18809
  }
18339
18810
 
18340
18811
  // packages/core/src/file-changes.ts
18341
- var CHANGE_LIMITS = {
18342
- files: 500,
18343
- opsPerFile: 50,
18344
- diffBytes: 2 * 1024 * 1024,
18345
- opDiffBytes: 64 * 1024,
18346
- conversations: 64
18347
- };
18348
18812
  var MAIN_AGENT = "main";
18349
18813
  function normalizeChangePath(raw, cwd, workspace) {
18350
18814
  const resolved = raw.startsWith("~") ? raw : resolve3(cwd, raw);
@@ -18355,22 +18819,6 @@ function normalizeChangePath(raw, cwd, workspace) {
18355
18819
  }
18356
18820
  return { path: resolved.split(sep).join("/"), outside: true };
18357
18821
  }
18358
- function clipDiff(diff) {
18359
- if (Buffer.byteLength(diff, "utf8") <= CHANGE_LIMITS.opDiffBytes)
18360
- return { diff, truncated: false };
18361
- const lines = diff.split(`
18362
- `);
18363
- const kept = [];
18364
- let bytes = 0;
18365
- for (const line of lines) {
18366
- bytes += Buffer.byteLength(line, "utf8") + 1;
18367
- if (bytes > CHANGE_LIMITS.opDiffBytes)
18368
- break;
18369
- kept.push(line);
18370
- }
18371
- return { diff: kept.join(`
18372
- `), truncated: true };
18373
- }
18374
18822
 
18375
18823
  class ConversationChanges {
18376
18824
  last = 0;
@@ -18378,6 +18826,7 @@ class ConversationChanges {
18378
18826
  truncated = false;
18379
18827
  bytes = 0;
18380
18828
  files = new Map;
18829
+ #scripts = new Map;
18381
18830
  #pending = new Map;
18382
18831
  #agent;
18383
18832
  #cwd;
@@ -18391,6 +18840,8 @@ class ConversationChanges {
18391
18840
  for (const message of entry.model ?? []) {
18392
18841
  if (message.role === "assistant") {
18393
18842
  for (const block of message.content) {
18843
+ if (block.type === "toolCall" && block.name === CODEMODE_NAME)
18844
+ this.#scripts.set(block.id, entry.id);
18394
18845
  if (block.type !== "toolCall" || block.name !== "edit" && block.name !== "write")
18395
18846
  continue;
18396
18847
  const path = block.arguments?.path;
@@ -18398,6 +18849,8 @@ class ConversationChanges {
18398
18849
  this.#pending.set(block.id, { tool: block.name, path, args: block.arguments });
18399
18850
  }
18400
18851
  } else if (message.role === "toolResult") {
18852
+ if (this.#scripts.has(message.toolCallId))
18853
+ this.#scripts.set(message.toolCallId, entry.id);
18401
18854
  const call = this.#pending.get(message.toolCallId);
18402
18855
  if (!call)
18403
18856
  continue;
@@ -18409,8 +18862,20 @@ class ConversationChanges {
18409
18862
  }
18410
18863
  this.last = entry.id;
18411
18864
  }
18865
+ withNested(nested) {
18866
+ const visible = nested.items.filter((item) => this.#scripts.has(item.parentCallId));
18867
+ const copy = new ConversationChanges(this.#agent, this.#cwd, this.#workspace);
18868
+ copy.last = this.last;
18869
+ copy.total = this.total;
18870
+ copy.truncated = this.truncated || nested.dropped && visible.length > 0;
18871
+ copy.bytes = this.bytes;
18872
+ for (const [path, file] of this.files)
18873
+ copy.files.set(path, { ...file, agents: new Set(file.agents), items: file.items.map((item) => ({ ...item })) });
18874
+ for (const item of visible)
18875
+ copy.#record({ tool: item.tool, path: item.path }, { diff: item.diff, added: item.added, removed: item.removed, truncated: item.truncated }, this.#scripts.get(item.parentCallId));
18876
+ return copy;
18877
+ }
18412
18878
  #add(call, details, seq) {
18413
- const { path, outside } = normalizeChangePath(call.path, this.#cwd, this.#workspace);
18414
18879
  let diff = "";
18415
18880
  let added = 0;
18416
18881
  let removed = 0;
@@ -18430,6 +18895,11 @@ class ConversationChanges {
18430
18895
  ({ diff, added, removed, truncated } = synthesized);
18431
18896
  }
18432
18897
  }
18898
+ this.#record(call, { diff, added, removed, truncated }, seq);
18899
+ }
18900
+ #record(call, shown, seq) {
18901
+ const { path, outside } = normalizeChangePath(call.path, this.#cwd, this.#workspace);
18902
+ const { diff, added, removed, truncated } = shown;
18433
18903
  this.total++;
18434
18904
  let file = this.files.get(path);
18435
18905
  if (!file) {
@@ -18450,7 +18920,7 @@ class ConversationChanges {
18450
18920
  file.added += added;
18451
18921
  file.removed += removed;
18452
18922
  file.agents.add(this.#agent);
18453
- file.lastSeq = seq;
18923
+ file.lastSeq = Math.max(file.lastSeq, seq);
18454
18924
  file.items.push({ seq, agent: this.#agent, tool: call.tool, diff, added, removed, truncated });
18455
18925
  this.bytes += Buffer.byteLength(diff, "utf8");
18456
18926
  if (file.items.length > CHANGE_LIMITS.opsPerFile) {
@@ -18552,10 +19022,12 @@ class ChangeTracker {
18552
19022
  let revision = 0;
18553
19023
  let truncated = false;
18554
19024
  for (const member of await this.#members(rootId)) {
18555
- const state = await this.#sync(member, workspace);
18556
- if (!state)
19025
+ const synced = await this.#sync(member, workspace);
19026
+ if (!synced)
18557
19027
  continue;
18558
- revision += state.total;
19028
+ const nested = await this.#harness.snapshot(NestedChangesDoc, member.id, this.#context);
19029
+ const state = nested && nested.items.length > 0 ? synced.withNested(nested) : synced;
19030
+ revision += synced.total + (nested?.total ?? 0);
18559
19031
  truncated ||= state.truncated;
18560
19032
  for (const file of state.files.values()) {
18561
19033
  const existing = merged.get(file.path);
@@ -18960,7 +19432,7 @@ function validatePlanDecision(input) {
18960
19432
  return { kind: "revise", feedback: text(input.feedback, "feedback", PLAN_LIMITS.feedbackChars) };
18961
19433
  }
18962
19434
  // packages/core/src/jobs.ts
18963
- import { defineDoc as defineDoc3 } from "@earendil-works/pi-durable";
19435
+ import { defineDoc as defineDoc5 } from "@earendil-works/pi-durable";
18964
19436
  var JOB_LIMITS = {
18965
19437
  maxRunning: 4,
18966
19438
  keepTerminal: 20,
@@ -18974,7 +19446,7 @@ var JOB_LIMITS = {
18974
19446
  commandBytes: 32 * 1024,
18975
19447
  maxTimeoutSeconds: 7 * 24 * 3600
18976
19448
  };
18977
- var JobsDoc = defineDoc3({
19449
+ var JobsDoc = defineDoc5({
18978
19450
  kind: "app.neko.jobs",
18979
19451
  checkpointWhen: () => false,
18980
19452
  version: 1,
@@ -19105,6 +19577,9 @@ var parseFrontmatter = (content) => {
19105
19577
  return { frontmatter: parsed ?? {}, body };
19106
19578
  };
19107
19579
  var stripFrontmatter = (content) => extractFrontmatter(content).body;
19580
+ // packages/core/src/run-failure.ts
19581
+ var CONTINUE_BUSY = "CONTINUE_BUSY: The session is running; wait for it to finish.";
19582
+ var CONTINUE_UNAVAILABLE = "CONTINUE_UNAVAILABLE: The last run was answered; there is nothing to continue.";
19108
19583
  // packages/core/src/runtime.ts
19109
19584
  import { homedir as homedir4 } from "os";
19110
19585
  import { join as join5 } from "path";
@@ -19114,14 +19589,15 @@ import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
19114
19589
  import { PI_ATTRIBUTION } from "@earendil-works/pi-ai/utils/identity";
19115
19590
  import {
19116
19591
  AssistantEntry as AssistantEntry2,
19592
+ ConversationBusy,
19593
+ configure as configure3,
19117
19594
  createRegistry,
19118
- defineDoc as defineDoc8,
19595
+ defineDoc as defineDoc10,
19119
19596
  Harness,
19120
19597
  InboxDoc as InboxDoc3,
19121
19598
  LiveDoc as importLiveDoc,
19122
19599
  MemoryStorage,
19123
- UsageDoc as UsageDoc2,
19124
- watchEvents
19600
+ UsageDoc as UsageDoc2
19125
19601
  } from "@earendil-works/pi-durable";
19126
19602
  import { createNodePhotonImages } from "@earendil-works/pi-durable/images/node";
19127
19603
  import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node";
@@ -19131,7 +19607,7 @@ import { createCodingTools } from "@earendil-works/pi-durable/tools";
19131
19607
  import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
19132
19608
  import { defineExtension as defineExtension2, defineTool as defineTool2 } from "@earendil-works/pi-durable";
19133
19609
  import { getOrThrow } from "@earendil-works/pi-durable/env";
19134
- import { Type as Type2 } from "typebox";
19610
+ import { bool, int as int2, obj as obj2, opt as opt2, str as str2 } from "@earendil-works/pi-schema";
19135
19611
  var SEARCH_LIMITS = {
19136
19612
  entries: 1e4,
19137
19613
  fileBytes: 2 * 1024 * 1024,
@@ -19139,8 +19615,8 @@ var SEARCH_LIMITS = {
19139
19615
  outputBytes: 32 * 1024,
19140
19616
  timeoutMs: 1e4
19141
19617
  };
19142
- var path = Type2.Optional(Type2.String({ description: "Directory relative to this conversation's cwd, or absolute path." }));
19143
- var limit = Type2.Optional(Type2.Integer({ minimum: 1, maximum: 500, description: "Maximum results (default 100)." }));
19618
+ var path = opt2(str2({ description: "Directory relative to this conversation's cwd, or absolute path." }));
19619
+ var limit = opt2(int2({ minimum: 1, maximum: 500, description: "Maximum results (default 100)." }));
19144
19620
  var text2 = (value) => ({ type: "text", text: value });
19145
19621
  async function search(api, context, run) {
19146
19622
  if (!api.env)
@@ -19180,7 +19656,7 @@ ${status}${skipped ? ` Skipped/unreadable files: ${skipped}.` : ""}`)],
19180
19656
  var FindFiles = defineTool2({
19181
19657
  name: "find_files",
19182
19658
  description: "Discover files and directories without a shell. query is a literal path/name substring (not a glob). Respects gitignore; returns absolute paths usable by read. A partial result is not a complete absence of matches.",
19183
- parameters: Type2.Object({ path, query: Type2.Optional(Type2.String()), limit }),
19659
+ parameters: obj2({ path, query: opt2(str2()), limit }, { open: true }),
19184
19660
  replay: "safe",
19185
19661
  execute: (args, api, context) => search(api, context, async (env, ctx) => {
19186
19662
  const files = await listing(env, args.path ?? ".", ctx);
@@ -19206,14 +19682,14 @@ var FindFiles = defineTool2({
19206
19682
  var SearchText = defineTool2({
19207
19683
  name: "search_text",
19208
19684
  description: "Search UTF-8 file contents without a shell. pattern and filePattern are literal substrings, not regex/globs. Returns absolute paths, 1-based line numbers and context for read. Binary/large/unreadable files and exhausted budgets are explicitly reported as partial results.",
19209
- parameters: Type2.Object({
19210
- pattern: Type2.String({ minLength: 1, maxLength: 4096 }),
19685
+ parameters: obj2({
19686
+ pattern: str2({ minLength: 1, maxLength: 4096 }),
19211
19687
  path,
19212
- filePattern: Type2.Optional(Type2.String()),
19213
- caseSensitive: Type2.Optional(Type2.Boolean()),
19214
- contextLines: Type2.Optional(Type2.Integer({ minimum: 0, maximum: 5 })),
19688
+ filePattern: opt2(str2()),
19689
+ caseSensitive: opt2(bool()),
19690
+ contextLines: opt2(int2({ minimum: 0, maximum: 5 })),
19215
19691
  limit
19216
- }),
19692
+ }, { open: true }),
19217
19693
  replay: "safe",
19218
19694
  execute: (args, api, context) => search(api, context, async (env, ctx) => {
19219
19695
  const files = await listing(env, args.path ?? ".", ctx);
@@ -19382,7 +19858,7 @@ class ContextPruner {
19382
19858
  #closed = false;
19383
19859
  constructor(options) {
19384
19860
  this.#options = options;
19385
- this.#unsubscribe = options.harness.subscribeCommits(({ changes }) => {
19861
+ this.#unsubscribe = onCommits(options.harness, ({ changes }) => {
19386
19862
  if (!options.settings().enabled)
19387
19863
  return;
19388
19864
  for (const change of changes) {
@@ -19490,7 +19966,7 @@ import {
19490
19966
  defineTask as defineTask2,
19491
19967
  defineTool as defineTool3
19492
19968
  } from "@earendil-works/pi-durable";
19493
- import { Type as Type3 } from "typebox";
19969
+ import { int as int3, lit as lit2, num as num2, obj as obj3, opt as opt3, str as str3, union as union2 } from "@earendil-works/pi-schema";
19494
19970
  var plain = (value) => JSON.parse(JSON.stringify(value));
19495
19971
 
19496
19972
  class JobEntry {
@@ -19547,7 +20023,7 @@ var statusWord = {
19547
20023
  stopped: "stopped",
19548
20024
  lost: "lost"
19549
20025
  };
19550
- var describe2 = (job) => {
20026
+ var describe3 = (job) => {
19551
20027
  const parts = [statusWord[job.status]];
19552
20028
  if (job.exitCode !== null)
19553
20029
  parts.push(`exit code ${job.exitCode}`);
@@ -19562,7 +20038,7 @@ var describe2 = (job) => {
19562
20038
  function reportText(job, tail) {
19563
20039
  const body = reportTail(tail);
19564
20040
  return [
19565
- `[Background job ${job.jobId} "${job.title}" ${describe2(job)}]`,
20041
+ `[Background job ${job.jobId} "${job.title}" ${describe3(job)}]`,
19566
20042
  `Command: ${job.command}`,
19567
20043
  body ? `Last output (use the job tool's output action with jobId "${job.jobId}" for more):
19568
20044
  ${body}` : "It produced no output."
@@ -19631,15 +20107,15 @@ var delay = (ms, signal) => new Promise((resolve) => {
19631
20107
  else
19632
20108
  signal?.addEventListener("abort", done, { once: true });
19633
20109
  });
19634
- var action = Type3.Union([Type3.Literal("start"), Type3.Literal("output"), Type3.Literal("stop"), Type3.Literal("list")]);
19635
- var parameters2 = Type3.Object({
20110
+ var action = union2([lit2("start"), lit2("output"), lit2("stop"), lit2("list")]);
20111
+ var parameters2 = obj3({
19636
20112
  action,
19637
- command: Type3.Optional(Type3.String({ description: "start: shell command to run in the session's working directory." })),
19638
- title: Type3.Optional(Type3.String({ description: "start: short label shown in the job list." })),
19639
- timeout: Type3.Optional(Type3.Number({ description: "start: optional timeout in seconds; no default timeout." })),
19640
- jobId: Type3.Optional(Type3.String({ description: "output/stop: the job id returned by start or list." })),
19641
- since: Type3.Optional(Type3.Integer({ description: "output: resume after this offset, as returned by the previous output call." }))
19642
- });
20113
+ command: opt3(str3({ description: "start: shell command to run in the session's working directory." })),
20114
+ title: opt3(str3({ description: "start: short label shown in the job list." })),
20115
+ timeout: opt3(num2({ description: "start: optional timeout in seconds; no default timeout." })),
20116
+ jobId: opt3(str3({ description: "output/stop: the job id returned by start or list." })),
20117
+ since: opt3(int3({ description: "output: resume after this offset, as returned by the previous output call." }))
20118
+ }, { open: true });
19643
20119
  var stringField = (value, name) => {
19644
20120
  if (typeof value !== "string" || !value.trim())
19645
20121
  throw new Error(`Invalid job arguments: ${name} must be a nonempty string.`);
@@ -19772,7 +20248,7 @@ class JobManager {
19772
20248
  if (kind === "stop") {
19773
20249
  const jobId = stringField(args.jobId, "jobId");
19774
20250
  const job = await this.stop(io, api.conversationId, jobId, "model", context);
19775
- return this.#text(`Job ${job.jobId} ${describe2(job)}.`);
20251
+ return this.#text(`Job ${job.jobId} ${describe3(job)}.`);
19776
20252
  }
19777
20253
  if (kind !== "start")
19778
20254
  throw new Error("Invalid job arguments: action must be start, output, stop or list.");
@@ -19800,7 +20276,7 @@ class JobManager {
19800
20276
  #listText(items) {
19801
20277
  if (!items.length)
19802
20278
  return "No jobs.";
19803
- return items.map((job) => `${job.jobId} [${describe2(job)}] "${job.title}" \u2014 ${job.command}`).join(`
20279
+ return items.map((job) => `${job.jobId} [${describe3(job)}] "${job.title}" \u2014 ${job.command}`).join(`
19804
20280
  `);
19805
20281
  }
19806
20282
  async start(io, conversationId, cwd, args, context) {
@@ -19852,7 +20328,7 @@ class JobManager {
19852
20328
  return this.#render(job, read.text, read.offset, read.dropped);
19853
20329
  }
19854
20330
  #render(job, text, offset, dropped) {
19855
- const lines = [`Job ${job.jobId} ${describe2(job)}.`, `Title: ${job.title}`, `Command: ${job.command}`];
20331
+ const lines = [`Job ${job.jobId} ${describe3(job)}.`, `Title: ${job.title}`, `Command: ${job.command}`];
19856
20332
  if (dropped > 0)
19857
20333
  lines.push(`(${dropped} bytes of earlier output were dropped)`);
19858
20334
  lines.push(text ? `Output (next offset ${offset}):
@@ -19926,10 +20402,10 @@ import {
19926
20402
  hook as hook2,
19927
20403
  InboxDoc,
19928
20404
  ToolTask as ToolTask2,
19929
- wrapTool
20405
+ wrapTool as wrapTool3
19930
20406
  } from "@earendil-works/pi-durable";
19931
20407
  import { createBashTool } from "@earendil-works/pi-durable/tools";
19932
- import { Type as Type5 } from "typebox";
20408
+ import { arr as arr3, obj as obj5, opt as opt5, str as str5 } from "@earendil-works/pi-schema";
19933
20409
 
19934
20410
  // packages/core/src/plan-bash.ts
19935
20411
  var SAFE_PATTERNS = [
@@ -20038,13 +20514,14 @@ function isSafeCommand(command) {
20038
20514
 
20039
20515
  // packages/core/src/user-questions.ts
20040
20516
  import { randomUUID } from "crypto";
20517
+ import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
20041
20518
  import {
20042
- defineDoc as defineDoc4,
20519
+ defineDoc as defineDoc6,
20043
20520
  defineDocFamily,
20044
20521
  defineExtension as defineExtension5,
20045
20522
  defineTool as defineTool4
20046
20523
  } from "@earendil-works/pi-durable";
20047
- import { Type as Type4 } from "typebox";
20524
+ import { arr as arr2, bool as bool2, nul as nul2, obj as obj4, opt as opt4, str as str4, union as union3 } from "@earendil-works/pi-schema";
20048
20525
  var QuestionDoc = defineDocFamily({
20049
20526
  kind: "app.neko.question",
20050
20527
  checkpointWhen: () => false,
@@ -20055,7 +20532,7 @@ var QuestionDoc = defineDocFamily({
20055
20532
  family: true,
20056
20533
  initial: (seed) => seed
20057
20534
  });
20058
- var QuestionsDoc = defineDoc4({
20535
+ var QuestionsDoc = defineDoc6({
20059
20536
  kind: "app.neko.questions",
20060
20537
  checkpointWhen: () => false,
20061
20538
  version: 1,
@@ -20108,7 +20585,7 @@ class UserQuestions {
20108
20585
  constructor(harness, context) {
20109
20586
  this.harness = harness;
20110
20587
  this.#context = context;
20111
- const stop = harness.subscribeCommits(({ changes }) => {
20588
+ const stop = onCommits(harness, ({ changes }) => {
20112
20589
  if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
20113
20590
  return;
20114
20591
  this.reconcileAll();
@@ -20214,13 +20691,16 @@ class UserQuestions {
20214
20691
  if (signal?.aborted)
20215
20692
  return onAbort();
20216
20693
  watch.closed.then((end) => {
20217
- if (end.reason === "stopped")
20694
+ if (end.kind === "stopped")
20218
20695
  return;
20219
- finish(() => reject(signal?.aborted ? signal.reason : end.reason === "listener_error" || end.reason === "session_failed" ? end.error : new Error(`Question watch ended: ${end.reason}`)));
20696
+ finish(() => reject(signal?.aborted ? signal.reason : end.kind === "listener_error" || end.kind === "failed" ? end.error : new Error(`Question watch ended: ${end.kind}`)));
20220
20697
  });
20221
20698
  check(watch.value);
20222
20699
  if (!done)
20223
- watch.start(async (value) => check(value));
20700
+ watch.start(async (call) => {
20701
+ if (call.kind === "value")
20702
+ check(call.value);
20703
+ });
20224
20704
  });
20225
20705
  } finally {
20226
20706
  signal?.removeEventListener("abort", onAbort);
@@ -20274,31 +20754,31 @@ class UserQuestions {
20274
20754
  }, context);
20275
20755
  }
20276
20756
  }
20277
- var optionSchema = Type4.Object({
20278
- id: Type4.String({
20757
+ var optionSchema = obj4({
20758
+ id: str4({
20279
20759
  minLength: 1,
20280
20760
  maxLength: QUESTION_LIMITS.optionIdChars,
20281
20761
  description: "Unique within the question."
20282
20762
  }),
20283
- label: Type4.String({
20763
+ label: str4({
20284
20764
  minLength: 1,
20285
20765
  maxLength: QUESTION_LIMITS.labelChars,
20286
20766
  description: "Short, unique, readable alone."
20287
20767
  }),
20288
- description: Type4.Optional(Type4.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
20289
- recommended: Type4.Optional(Type4.Boolean({ description: "Your suggested choice; shown as a hint, never preselected." }))
20290
- }, { additionalProperties: false });
20291
- var parameters3 = Type4.Object({
20292
- question: Type4.String({ minLength: 1, maxLength: QUESTION_LIMITS.questionChars }),
20293
- multiple: Type4.Optional(Type4.Boolean({ description: "Let the user pick several options; needs at least 2 options. Default false." })),
20294
- options: Type4.Optional(Type4.Array(optionSchema, { maxItems: QUESTION_LIMITS.maxOptions }))
20295
- }, { additionalProperties: false });
20296
- var structuredOutputSchema2 = Type4.Object({
20297
- status: Type4.String({ enum: ["answered", "skipped", "cancelled"] }),
20298
- optionIds: Type4.Optional(Type4.Array(Type4.String())),
20299
- labels: Type4.Optional(Type4.Array(Type4.String())),
20300
- text: Type4.Optional(Type4.Union([Type4.String(), Type4.Null()]))
20301
- }, { additionalProperties: false });
20768
+ description: opt4(str4({ maxLength: QUESTION_LIMITS.descriptionChars })),
20769
+ recommended: opt4(bool2({ description: "Your suggested choice; shown as a hint, never preselected." }))
20770
+ });
20771
+ var parameters3 = obj4({
20772
+ question: str4({ minLength: 1, maxLength: QUESTION_LIMITS.questionChars }),
20773
+ multiple: opt4(bool2({ description: "Let the user pick several options; needs at least 2 options. Default false." })),
20774
+ options: opt4(arr2(optionSchema, { maxItems: QUESTION_LIMITS.maxOptions }))
20775
+ });
20776
+ var structuredOutputSchema2 = obj4({
20777
+ status: StringEnum2(["answered", "skipped", "cancelled"]),
20778
+ optionIds: opt4(arr2(str4())),
20779
+ labels: opt4(arr2(str4())),
20780
+ text: opt4(union3([str4(), nul2()]))
20781
+ });
20302
20782
  function questionOutput(record) {
20303
20783
  if (record.status === "answered" && record.answer) {
20304
20784
  const labels = answerLabels(record, record.answer);
@@ -20377,28 +20857,7 @@ function createAskUserTool(service) {
20377
20857
  var createQuestionExtension = (service) => defineExtension5({ name: "neko-questions", tools: [createAskUserTool(service)] });
20378
20858
 
20379
20859
  // packages/core/src/plan.ts
20380
- var PLAN_REMOVED_TOOLS = ["edit", "write", "job"];
20381
- var PLAN_INSTRUCTIONS = [
20382
- "You are in plan mode: research and plan only, and change nothing.",
20383
- "- Allowed: read, find_files, search_text, web_search, web_extract, todo, ask_user, read-only subagents (explore, plan) and bash limited to read-only commands (listing, searching, git log/diff/status, version checks). Other commands are rejected.",
20384
- "- Not available: edit, write, job and any command that modifies files, installs packages or starts processes. Do not try to work around this.",
20385
- "- When you understand the task, call submit_plan once, alone in its turn, with a short title, ordered concrete steps and an optional note (risks, assumptions, verification). The user then approves it or asks for changes.",
20386
- "- After submit_plan do nothing else. If the user asks for changes, research what is needed and submit a revised plan.",
20387
- "- Use ask_user only for decisions you cannot settle by reading the code."
20388
- ].join(`
20389
- `);
20390
20860
  var PLAN_BUSY = "PLAN_BUSY: Wait for the current run to finish or stop it first.";
20391
- function planAgentChange(mode) {
20392
- if (mode === "normal")
20393
- return { tools: null, modelTools: null, instructions: null };
20394
- const named = (name) => ({ name });
20395
- return {
20396
- tools: { remove: PLAN_REMOVED_TOOLS.map(named) },
20397
- modelTools: { add: [named("submit_plan")] },
20398
- instructions: PLAN_INSTRUCTIONS
20399
- };
20400
- }
20401
- var agentInPlanMode = (agent) => agent.instructions === PLAN_INSTRUCTIONS;
20402
20861
  var clone2 = (value) => JSON.parse(JSON.stringify(value));
20403
20862
  var toRecord = ({ taskId: _taskId, dispatched: _dispatched, ...record }) => clone2(record);
20404
20863
  function settle2(state, item, status) {
@@ -20491,22 +20950,24 @@ class PlanService {
20491
20950
  return toRecord(item);
20492
20951
  }, context);
20493
20952
  }
20494
- async decide(id, version, decision) {
20953
+ async decideWith(id, version, decision, act) {
20495
20954
  return this.harness.commit(async (tx) => {
20496
20955
  const state = await tx.doc(PlanDoc, id);
20497
20956
  const item = state.items.find((i) => i.version === version);
20498
20957
  if (!item)
20499
20958
  throw new Error("Plan not found.");
20500
- if (item.status === "awaiting_approval") {
20501
- if (decision.kind === "approve") {
20502
- settle2(state, item, "approved");
20503
- state.mode = "normal";
20504
- } else {
20505
- settle2(state, item, "revision_requested");
20506
- item.feedback = decision.feedback;
20507
- }
20959
+ if (item.status !== "awaiting_approval")
20960
+ return toRecord(item);
20961
+ await act(tx);
20962
+ if (decision.kind === "approve") {
20963
+ settle2(state, item, "approved");
20964
+ state.mode = "normal";
20965
+ } else {
20966
+ settle2(state, item, "revision_requested");
20967
+ item.feedback = decision.feedback;
20508
20968
  }
20509
- return { record: toRecord(item), item: clone2(item) };
20969
+ item.dispatched = true;
20970
+ return toRecord(item);
20510
20971
  }, this.#context);
20511
20972
  }
20512
20973
  async supersedePending(id) {
@@ -20557,15 +21018,15 @@ function planRevisionPrompt(plan, feedback) {
20557
21018
  ].join(`
20558
21019
  `);
20559
21020
  }
20560
- var parameters4 = Type5.Object({
20561
- title: Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
20562
- steps: Type5.Array(Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
21021
+ var parameters4 = obj5({
21022
+ title: str5({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
21023
+ steps: arr3(str5({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
20563
21024
  minItems: 1,
20564
21025
  maxItems: PLAN_LIMITS.maxSteps,
20565
21026
  description: "Ordered, concrete implementation steps."
20566
21027
  }),
20567
- note: Type5.Optional(Type5.String({ maxLength: PLAN_LIMITS.noteChars, description: "Risks, assumptions and how to verify." }))
20568
- }, { additionalProperties: false });
21028
+ note: opt5(str5({ maxLength: PLAN_LIMITS.noteChars, description: "Risks, assumptions and how to verify." }))
21029
+ });
20569
21030
  function submitPlanTool(service) {
20570
21031
  return defineTool5({
20571
21032
  name: "submit_plan",
@@ -20634,7 +21095,7 @@ var createPlanExtension = (service) => defineExtension6({
20634
21095
  })
20635
21096
  ],
20636
21097
  wraps: [
20637
- wrapTool(createBashTool(), (bash) => ({
21098
+ wrapTool3(createBashTool(), (bash) => ({
20638
21099
  ...bash,
20639
21100
  execute: async (args, api, context) => {
20640
21101
  if ((await api.snapshot(PlanDoc, api.conversationId, context))?.mode === "plan" && !isSafeCommand(args.command))
@@ -20786,6 +21247,9 @@ import { defineExtension as defineExtension7, section } from "@earendil-works/pi
20786
21247
  var KEYS = ["preamble", "rules", "project_context", "skills", "cwd"];
20787
21248
  var PREAMBLE = "You are an expert coding assistant operating inside neko, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.";
20788
21249
  var TOOL_GUIDELINES = {
21250
+ codemode: [
21251
+ "Use codemode to batch independent tool calls (Promise.allSettled), chain them, or filter large output, instead of many separate calls."
21252
+ ],
20789
21253
  subagent: [
20790
21254
  "Delegate only independent, sufficiently scoped work when isolation or parallelism helps. Handle simple tasks directly. Pass necessary context and user constraints; research-only requests must not become coding tasks. For related follow-up work, list and continue a suitable idle subagent using its stable agentId; busy means no task was accepted. Do not change its role. Verify and integrate reports. Do not modify files concurrently with a coder assigned those files."
20791
21255
  ],
@@ -20850,7 +21314,7 @@ var createNekoPrompt = (loadResources, platform = process.platform) => {
20850
21314
  const resources = cwd ? await loadResources(input.conversationId, cwd) : { contextFiles: [], skills: [] };
20851
21315
  const sections = {
20852
21316
  preamble: role ? ROLE_PROMPTS[role] + SUBAGENT_DELIVERY : PREAMBLE,
20853
- rules: buildRules(input.agent.tools.map((tool) => tool.name), platform),
21317
+ rules: buildRules(enabledTools(input.agent).map((tool) => tool.name), platform),
20854
21318
  project_context: renderProjectContext(resources.contextFiles),
20855
21319
  skills: formatSkillsForPrompt(resources.skills) || undefined,
20856
21320
  cwd: cwd?.replace(/\\/g, "/")
@@ -21017,13 +21481,13 @@ var createResourceSnapshots = (load, submission) => {
21017
21481
  import { stripVTControlCharacters } from "util";
21018
21482
  import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
21019
21483
  import {
21020
- defineDoc as defineDoc5,
21484
+ defineDoc as defineDoc7,
21021
21485
  defineExtension as defineExtension8,
21022
21486
  defineTask as defineTask3,
21023
21487
  InboxDoc as InboxDoc2,
21024
21488
  LiveDoc as LiveDoc2
21025
21489
  } from "@earendil-works/pi-durable";
21026
- var SessionMetadataDoc = defineDoc5({
21490
+ var SessionMetadataDoc = defineDoc7({
21027
21491
  kind: "app.neko.session-metadata",
21028
21492
  checkpointWhen: () => false,
21029
21493
  version: 1,
@@ -21215,7 +21679,7 @@ class SessionMetadataManager {
21215
21679
  this.context = context;
21216
21680
  this.report = report;
21217
21681
  registry.install(defineExtension8({ name: "neko-naming", tasks: [this.task] }));
21218
- this.#unsubscribe = harness.subscribeCommits(({ changes }) => {
21682
+ this.#unsubscribe = onCommits(harness, ({ changes }) => {
21219
21683
  const ids = new Set;
21220
21684
  const submissions = new Map;
21221
21685
  let notice = false;
@@ -21230,7 +21694,7 @@ class SessionMetadataManager {
21230
21694
  if (c.record.kind === "pi.agent" || c.record.kind === QuestionsDoc.definition.kind || c.record.kind === PlanDoc.definition.kind)
21231
21695
  ids.add(c.conversationId);
21232
21696
  if (c.record.kind === "pi.live") {
21233
- const running = c.value?.run !== undefined;
21697
+ const running = c.kind === "change" && c.value.run !== undefined;
21234
21698
  if (this.#live.get(c.conversationId) !== running) {
21235
21699
  this.#live.set(c.conversationId, running);
21236
21700
  ids.add(c.conversationId);
@@ -21360,9 +21824,10 @@ class SessionMetadataManager {
21360
21824
  }
21361
21825
 
21362
21826
  // packages/core/src/todos.ts
21363
- import { defineDoc as defineDoc6, defineExtension as defineExtension9, defineTool as defineTool6 } from "@earendil-works/pi-durable";
21364
- import { Type as Type6 } from "typebox";
21365
- var TodosDoc = defineDoc6({
21827
+ import { StringEnum as StringEnum3 } from "@earendil-works/pi-ai";
21828
+ import { defineDoc as defineDoc8, defineExtension as defineExtension9, defineTool as defineTool6 } from "@earendil-works/pi-durable";
21829
+ import { arr as arr4, int as int4, obj as obj6, opt as opt6, str as str6 } from "@earendil-works/pi-schema";
21830
+ var TodosDoc = defineDoc8({
21366
21831
  kind: "app.neko.todos",
21367
21832
  checkpointWhen: () => false,
21368
21833
  version: 1,
@@ -21371,30 +21836,30 @@ var TodosDoc = defineDoc6({
21371
21836
  fork: "asOf",
21372
21837
  initial: () => ({ revision: 0, items: [] })
21373
21838
  });
21374
- var TodoReceiptDoc = defineDoc6({
21839
+ var TodoReceiptDoc = defineDoc8({
21375
21840
  kind: "app.neko.todo-receipt",
21376
21841
  checkpointWhen: () => false,
21377
21842
  version: 1,
21378
21843
  scope: "task",
21379
21844
  initial: () => ({ applied: false, revision: 0, items: [] })
21380
21845
  });
21381
- var itemSchema = Type6.Object({
21382
- id: Type6.String({
21846
+ var itemSchema = obj6({
21847
+ id: str6({
21383
21848
  minLength: 1,
21384
21849
  maxLength: TODO_LIMITS.idChars,
21385
21850
  description: "Stable id; keep it when you update the same task."
21386
21851
  }),
21387
- text: Type6.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
21388
- status: Type6.String({ enum: [...TODO_STATUSES] })
21389
- }, { additionalProperties: false });
21390
- var parameters5 = Type6.Object({
21391
- action: Type6.String({ enum: ["list", "replace"], description: "list reads the list; replace overwrites it." }),
21392
- items: Type6.Optional(Type6.Array(itemSchema, {
21852
+ text: str6({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
21853
+ status: StringEnum3([...TODO_STATUSES])
21854
+ });
21855
+ var parameters5 = obj6({
21856
+ action: StringEnum3(["list", "replace"], { description: "list reads the list; replace overwrites it." }),
21857
+ items: opt6(arr4(itemSchema, {
21393
21858
  maxItems: TODO_LIMITS.maxItems,
21394
21859
  description: "Required for replace only: the complete ordered list; [] clears it."
21395
21860
  }))
21396
- }, { additionalProperties: false });
21397
- var structuredOutputSchema3 = Type6.Object({ revision: Type6.Integer({ minimum: 0 }), items: Type6.Array(itemSchema) }, { additionalProperties: false });
21861
+ });
21862
+ var structuredOutputSchema3 = obj6({ revision: int4({ minimum: 0 }), items: arr4(itemSchema) });
21398
21863
  var clone3 = (value) => JSON.parse(JSON.stringify(value));
21399
21864
  function renderTodos(list) {
21400
21865
  const { completed, total } = todoProgress(list.items);
@@ -21463,14 +21928,14 @@ import { randomUUID as randomUUID3 } from "crypto";
21463
21928
  import { realpath } from "fs/promises";
21464
21929
  import {
21465
21930
  configure as configure2,
21466
- defineDoc as defineDoc7,
21931
+ defineDoc as defineDoc9,
21467
21932
  defineExtension as defineExtension10,
21468
21933
  hook as hook3,
21469
21934
  ToolTask as ToolTask3,
21470
- wrapTool as wrapTool2
21935
+ wrapTool as wrapTool4
21471
21936
  } from "@earendil-works/pi-durable";
21472
21937
  import { createBashTool as createBashTool2 } from "@earendil-works/pi-durable/tools";
21473
- var ApprovalsDoc = defineDoc7({
21938
+ var ApprovalsDoc = defineDoc9({
21474
21939
  kind: "app.neko.approvals",
21475
21940
  checkpointWhen: () => false,
21476
21941
  version: 1,
@@ -21489,7 +21954,7 @@ class ToolApprovals {
21489
21954
  #parents = new Set;
21490
21955
  constructor(harness, context) {
21491
21956
  this.harness = harness;
21492
- const stop = harness.subscribeCommits(({ changes }) => {
21957
+ const stop = onCommits(harness, ({ changes }) => {
21493
21958
  if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
21494
21959
  return;
21495
21960
  for (const parent of this.#parents)
@@ -21524,14 +21989,12 @@ class ToolApprovals {
21524
21989
  }, context);
21525
21990
  }
21526
21991
  async wait(parent, id, context) {
21527
- const state = await this.harness.documentState(ApprovalsDoc, parent, context);
21528
- if (!state)
21992
+ const watch = await this.harness.watchDoc(ApprovalsDoc, parent, context);
21993
+ if (!watch)
21529
21994
  throw new Error("Approval unavailable");
21530
21995
  try {
21531
21996
  return await new Promise((resolve, reject) => {
21532
- let stop = () => {};
21533
21997
  const cleanup = () => {
21534
- stop();
21535
21998
  context.abortSignal?.removeEventListener("abort", abort);
21536
21999
  };
21537
22000
  const abort = () => {
@@ -21539,13 +22002,16 @@ class ToolApprovals {
21539
22002
  reject(context.abortSignal?.reason ?? new Error("Cancelled"));
21540
22003
  };
21541
22004
  const check = () => {
21542
- const item = state.value?.items.find((a) => a.id === id);
22005
+ const item = watch.value.items.find((a) => a.id === id);
21543
22006
  if (item && item.status !== "pending") {
21544
22007
  cleanup();
21545
22008
  resolve(structuredClone(item));
21546
22009
  }
21547
22010
  };
21548
- stop = state.subscribe(check);
22011
+ watch.start(async (call) => {
22012
+ if (call.kind === "value")
22013
+ check();
22014
+ });
21549
22015
  context.abortSignal?.addEventListener("abort", abort, { once: true });
21550
22016
  if (context.abortSignal?.aborted)
21551
22017
  abort();
@@ -21553,7 +22019,7 @@ class ToolApprovals {
21553
22019
  check();
21554
22020
  });
21555
22021
  } finally {
21556
- state.dispose();
22022
+ await watch.stop();
21557
22023
  }
21558
22024
  }
21559
22025
  async ensure(parent, candidate, context) {
@@ -21612,7 +22078,7 @@ function createApprovalExtension(service) {
21612
22078
  })
21613
22079
  ],
21614
22080
  wraps: [
21615
- wrapTool2(createBashTool2(), (bash) => ({
22081
+ wrapTool4(createBashTool2(), (bash) => ({
21616
22082
  ...bash,
21617
22083
  execute: async (args, api, context) => {
21618
22084
  const role = await api.snapshot(SubagentDoc, api.conversationId, context);
@@ -21636,7 +22102,7 @@ async function prepareApprovalConversations(harness, ids, extension, context) {
21636
22102
  const child = await harness.conversation(id, context);
21637
22103
  const agent = await child.agent(context);
21638
22104
  await harness.commit((tx) => configure2(tx, id, {
21639
- tools: [...agent.tools.filter((t) => t.name !== "bash"), createBashTool2()],
22105
+ tools: [...enabledTools(agent).filter((t) => t.name !== "bash"), createBashTool2()],
21640
22106
  extensions: { add: [extension] }
21641
22107
  }), context);
21642
22108
  }
@@ -21863,8 +22329,8 @@ function createDeepseekProvider(resolveKey, fetchImpl = fetch, options = {}) {
21863
22329
  }
21864
22330
 
21865
22331
  // packages/core/src/web-search/index.ts
21866
- import { Type as Type7 } from "@earendil-works/pi-ai";
21867
22332
  import { defineExtension as defineExtension11, defineTool as defineTool7 } from "@earendil-works/pi-durable";
22333
+ import { arr as arr5, num as num3, obj as obj7, opt as opt7, str as str7 } from "@earendil-works/pi-schema";
21868
22334
  var MAX_URLS = 20;
21869
22335
  var MAX_RESULTS = 10;
21870
22336
  var DEFAULT_RESULTS = 5;
@@ -21888,10 +22354,10 @@ function createWebSearchTools(resolveProvider) {
21888
22354
  defineTool7({
21889
22355
  name: "web_search",
21890
22356
  description: "Search the live web for current or unknown information. Returns ranked sources (title, URL, bounded excerpts). Keep the query short and focused (ideally under 128 characters). Use only when up-to-date or external information is needed; use web_extract to read a known URL.",
21891
- parameters: Type7.Object({
21892
- query: Type7.String({ minLength: 1, maxLength: 4096, description: "The search query" }),
21893
- maxResults: Type7.Optional(Type7.Number({ description: `Maximum number of results (1-${MAX_RESULTS}, default ${DEFAULT_RESULTS})` }))
21894
- }),
22357
+ parameters: obj7({
22358
+ query: str7({ minLength: 1, maxLength: 4096, description: "The search query" }),
22359
+ maxResults: opt7(num3({ description: `Maximum number of results (1-${MAX_RESULTS}, default ${DEFAULT_RESULTS})` }))
22360
+ }, { open: true }),
21895
22361
  async execute({ query, maxResults }, _api, context) {
21896
22362
  try {
21897
22363
  if (!query.trim() || query.length > 4096)
@@ -21919,13 +22385,13 @@ ${item.date ? `Published: ${item.date}
21919
22385
  defineTool7({
21920
22386
  name: "web_extract",
21921
22387
  description: "Extract bounded markdown excerpts from web pages by URL. Every requested URL has a result or failure entry; long content is truncated.",
21922
- parameters: Type7.Object({
21923
- urls: Type7.Array(Type7.String({ maxLength: 2048, description: "http(s) URL" }), {
22388
+ parameters: obj7({
22389
+ urls: arr5(str7({ maxLength: 2048, description: "http(s) URL" }), {
21924
22390
  minItems: 1,
21925
22391
  maxItems: MAX_URLS,
21926
22392
  description: `URLs to extract (1-${MAX_URLS})`
21927
22393
  })
21928
- }),
22394
+ }, { open: true }),
21929
22395
  async execute({ urls }, _api, context) {
21930
22396
  try {
21931
22397
  if (!urls.length || urls.length > MAX_URLS)
@@ -22323,7 +22789,7 @@ class WorkspaceFiles {
22323
22789
  }
22324
22790
  #disposeWatch(watch) {
22325
22791
  watch.controller.abort();
22326
- const closing = watch.work.catch(() => {}).then(() => watch.watcher?.close(BACKGROUND_CONTEXT2)).then(() => {}).catch(() => {}).finally(() => this.#disposing.delete(closing));
22792
+ const closing = watch.work.catch(() => {}).then(() => watch.watcher?.stop(BACKGROUND_CONTEXT2)).then(() => {}).catch(() => {}).finally(() => this.#disposing.delete(closing));
22327
22793
  this.#disposing.add(closing);
22328
22794
  }
22329
22795
  #closeWatcher(item) {
@@ -22364,7 +22830,7 @@ class WorkspaceFiles {
22364
22830
  user.generation++;
22365
22831
  user.loaded = 0;
22366
22832
  }
22367
- if ("error" in change) {
22833
+ if (change.kind === "failed") {
22368
22834
  if (this.#watches.get(item.root) === current)
22369
22835
  this.#watches.delete(item.root);
22370
22836
  for (const user of [...current.users])
@@ -22374,7 +22840,7 @@ class WorkspaceFiles {
22374
22840
  if (result.ok) {
22375
22841
  current.watcher = result.value;
22376
22842
  if (current.controller.signal.aborted)
22377
- await result.value.close(BACKGROUND_CONTEXT2);
22843
+ await result.value.stop(BACKGROUND_CONTEXT2);
22378
22844
  } else {
22379
22845
  forget();
22380
22846
  }
@@ -22645,7 +23111,7 @@ function cancellable(work, signal) {
22645
23111
  var DEFAULT_MODEL = { provider: "deepseek", modelId: "deepseek-flash" };
22646
23112
  var DEFAULT_STORAGE_PATH = join5(homedir4(), ".neko", "neko.sqlite");
22647
23113
  var neverAborted = () => new AbortController().signal;
22648
- var ArchivedDoc = defineDoc8({
23114
+ var ArchivedDoc = defineDoc10({
22649
23115
  kind: "app.neko.conversation",
22650
23116
  checkpointWhen: () => false,
22651
23117
  version: 1,
@@ -22654,7 +23120,7 @@ var ArchivedDoc = defineDoc8({
22654
23120
  fork: "initial",
22655
23121
  initial: () => ({ archived: false })
22656
23122
  });
22657
- var WorkspacesDoc = defineDoc8({
23123
+ var WorkspacesDoc = defineDoc10({
22658
23124
  kind: "app.neko.workspaces",
22659
23125
  checkpointWhen: () => false,
22660
23126
  version: 1,
@@ -22663,7 +23129,7 @@ var WorkspacesDoc = defineDoc8({
22663
23129
  fork: "initial",
22664
23130
  initial: () => ({ items: [] })
22665
23131
  });
22666
- var RemovedWorkspacesDoc = defineDoc8({
23132
+ var RemovedWorkspacesDoc = defineDoc10({
22667
23133
  kind: "app.neko.workspaces-removed",
22668
23134
  checkpointWhen: () => false,
22669
23135
  version: 1,
@@ -22672,7 +23138,7 @@ var RemovedWorkspacesDoc = defineDoc8({
22672
23138
  fork: "initial",
22673
23139
  initial: () => ({ paths: [] })
22674
23140
  });
22675
- var SearchSettingsDoc = defineDoc8({
23141
+ var SearchSettingsDoc = defineDoc10({
22676
23142
  kind: "app.neko.search-settings",
22677
23143
  checkpointWhen: () => false,
22678
23144
  version: 1,
@@ -22681,7 +23147,7 @@ var SearchSettingsDoc = defineDoc8({
22681
23147
  fork: "initial",
22682
23148
  initial: () => ({ provider: null })
22683
23149
  });
22684
- var ProviderSettingsDoc = defineDoc8({
23150
+ var ProviderSettingsDoc = defineDoc10({
22685
23151
  kind: "app.neko.provider-settings",
22686
23152
  checkpointWhen: () => false,
22687
23153
  version: 1,
@@ -22690,7 +23156,7 @@ var ProviderSettingsDoc = defineDoc8({
22690
23156
  fork: "initial",
22691
23157
  initial: () => ({ enabled: null })
22692
23158
  });
22693
- var DefaultModelDoc = defineDoc8({
23159
+ var DefaultModelDoc = defineDoc10({
22694
23160
  kind: "app.neko.default-model",
22695
23161
  checkpointWhen: () => false,
22696
23162
  version: 1,
@@ -22699,7 +23165,7 @@ var DefaultModelDoc = defineDoc8({
22699
23165
  fork: "initial",
22700
23166
  initial: () => ({ model: null })
22701
23167
  });
22702
- var ContextSettingsDoc = defineDoc8({
23168
+ var ContextSettingsDoc = defineDoc10({
22703
23169
  kind: "app.neko.context-settings",
22704
23170
  checkpointWhen: () => false,
22705
23171
  version: 1,
@@ -22708,7 +23174,7 @@ var ContextSettingsDoc = defineDoc8({
22708
23174
  fork: "initial",
22709
23175
  initial: emptySavedContextSettings
22710
23176
  });
22711
- var ActivityDoc = defineDoc8({
23177
+ var ActivityDoc = defineDoc10({
22712
23178
  kind: "app.neko.activity",
22713
23179
  checkpointWhen: () => false,
22714
23180
  version: 1,
@@ -22719,6 +23185,9 @@ var ActivityDoc = defineDoc8({
22719
23185
  });
22720
23186
  async function createNekoRuntime(options = {}) {
22721
23187
  const context = options.context ?? BACKGROUND_CONTEXT3;
23188
+ if (options.maxTurns !== undefined && !(Number.isSafeInteger(options.maxTurns) && options.maxTurns >= 1))
23189
+ throw new RangeError(`maxTurns must be an integer of 1 or more, got ${options.maxTurns}`);
23190
+ const turnLimit = options.maxTurns === undefined ? {} : { maxTurns: options.maxTurns };
22722
23191
  const defaultCwd = normalizeWorkspace(options.cwd);
22723
23192
  const model = options.model ?? DEFAULT_MODEL;
22724
23193
  const modelOverride = options.modelOverride;
@@ -22815,6 +23284,11 @@ async function createNekoRuntime(options = {}) {
22815
23284
  registry.install(createQuestionExtension(() => questions));
22816
23285
  let plans;
22817
23286
  registry.install(createPlanExtension(() => plans));
23287
+ const codemode = createCodemodeRuntime({
23288
+ maxWorkers: options.codemodeLimits?.maxWorkers ?? CODEMODE_LIMITS.maxWorkers,
23289
+ waitMs: options.codemodeLimits?.waitMs ?? CODEMODE_LIMITS.waitMs
23290
+ });
23291
+ registry.install(codemode.extension);
22818
23292
  for (const extension of options.extensions ?? [])
22819
23293
  registry.install(extension);
22820
23294
  let approvals;
@@ -22833,7 +23307,7 @@ async function createNekoRuntime(options = {}) {
22833
23307
  return compactionFor(contextState);
22834
23308
  }
22835
23309
  },
22836
- onReport: options.onReport,
23310
+ onReport: (report) => options.onReport?.(report.error, report),
22837
23311
  conversationCreated: async (tx, record) => {
22838
23312
  (await tx.doc(SessionMetadataDoc, record.id)).createdAt = Date.now();
22839
23313
  },
@@ -22868,6 +23342,7 @@ async function createNekoRuntime(options = {}) {
22868
23342
  }
22869
23343
  }
22870
23344
  } catch (error) {
23345
+ await codemode.close();
22871
23346
  await workspaceFiles.close();
22872
23347
  await harness.close(context);
22873
23348
  throw error;
@@ -22878,10 +23353,18 @@ async function createNekoRuntime(options = {}) {
22878
23353
  agent: {
22879
23354
  model: rootModel,
22880
23355
  thinkingLevel: resolveLevel(rootModel, options.thinkingLevel ?? "high"),
22881
- cwd: defaultCwd
23356
+ cwd: defaultCwd,
23357
+ ...turnLimit
22882
23358
  }
22883
23359
  });
22884
23360
  enabled = new Set(normalizeEnabledProviders((await harness.snapshot(ProviderSettingsDoc, conversation.id, context))?.enabled));
23361
+ const codemodeState = async () => resolveCodemodeSettings(options.codemodeSettings, (await harness.snapshot(CodemodeSettingsDoc, conversation.id, context))?.mode);
23362
+ if (existing.length === 0) {
23363
+ const initial = (await codemodeState()).effective.mode;
23364
+ await harness.commit(async (tx) => {
23365
+ (await tx.doc(CodemodeDoc, conversation.id)).mode = initial;
23366
+ }, context);
23367
+ }
22885
23368
  const savedSearch = (await harness.snapshot(SearchSettingsDoc, conversation.id, context))?.provider;
22886
23369
  if (isSearchProvider(savedSearch))
22887
23370
  searchProvider = savedSearch;
@@ -23008,14 +23491,16 @@ async function createNekoRuntime(options = {}) {
23008
23491
  admissions.delete(id);
23009
23492
  }
23010
23493
  };
23011
- const syncPlanAgent = async (target) => {
23012
- const mode = await plans.mode(target.id);
23013
- if (mode === "plan" !== agentInPlanMode(await target.agent(context)))
23014
- await target.configure(planAgentChange(mode), context);
23494
+ const syncAgent = async (target) => {
23495
+ const change = agentSyncChange(await target.agent(context), {
23496
+ plan: await plans.mode(target.id),
23497
+ codemode: (await harness.snapshot(CodemodeDoc, target.id, context))?.mode ?? "off",
23498
+ role: "main"
23499
+ });
23500
+ if (change)
23501
+ await target.configure(change, context);
23015
23502
  };
23016
- const submitTo = async (target, content, submitOptions, extra) => admit(target.id, async () => {
23017
- await syncPlanAgent(target);
23018
- const agent = await target.agent(context);
23503
+ const prepareSubmission = async (target, agent, content, submitOptions, extra) => {
23019
23504
  const workspace = executionWorkspace(agent.cwd);
23020
23505
  const duplicate = submitOptions?.requestId !== undefined && await storage.submissionByRequest(target.id, submitOptions.requestId, context);
23021
23506
  const data = { at: Date.now(), prompt: "", images: [], ...extra };
@@ -23069,15 +23554,23 @@ async function createNekoRuntime(options = {}) {
23069
23554
  }
23070
23555
  if (prepared)
23071
23556
  resourceSnapshots.stage(target.id, requestId, workspace, prepared);
23072
- let result;
23073
- try {
23074
- result = await target.submit({
23557
+ return {
23558
+ draft: {
23075
23559
  type: "input",
23076
23560
  content,
23077
23561
  entry: { kind: NekoInput.kind, data },
23078
23562
  requestId,
23079
23563
  ...submitOptions?.whenBusy !== undefined ? { whenBusy: submitOptions.whenBusy } : {}
23080
- }, context);
23564
+ },
23565
+ requestId
23566
+ };
23567
+ };
23568
+ const submitNow = async (target, content, submitOptions, extra) => {
23569
+ await syncAgent(target);
23570
+ const { draft, requestId } = await prepareSubmission(target, await target.agent(context), content, submitOptions, extra);
23571
+ let result;
23572
+ try {
23573
+ result = await target.submit(draft, context);
23081
23574
  } catch (error) {
23082
23575
  resourceSnapshots.discard(target.id, requestId);
23083
23576
  throw error;
@@ -23087,10 +23580,11 @@ async function createNekoRuntime(options = {}) {
23087
23580
  await touchConversation(target.id);
23088
23581
  await metadata.reconcile(target.id);
23089
23582
  return result;
23090
- });
23091
- const dispatchPlan = async (target, item) => {
23583
+ };
23584
+ const submitTo = (target, ...args) => admit(target.id, () => submitNow(target, ...args));
23585
+ const dispatchPlan = async (target, item, submit = submitTo) => {
23092
23586
  const approve = item.status === "approved";
23093
- await submitTo(target, approve ? approvedPlanPrompt(item) : planRevisionPrompt(item, item.feedback ?? ""), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${item.version}`, whenBusy: "followUp" }, {
23587
+ await submit(target, approve ? approvedPlanPrompt(item) : planRevisionPrompt(item, item.feedback ?? ""), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${item.version}`, whenBusy: "followUp" }, {
23094
23588
  planDecision: {
23095
23589
  version: item.version,
23096
23590
  kind: approve ? "approve" : "revise",
@@ -23127,15 +23621,12 @@ async function createNekoRuntime(options = {}) {
23127
23621
  waitAnswer(submission) {
23128
23622
  return waitAnswerOf(target, submission);
23129
23623
  },
23130
- viewState() {
23131
- return target.viewState(context);
23624
+ follow() {
23625
+ return followConversation(target, context);
23132
23626
  },
23133
23627
  watch() {
23134
23628
  return target.watch(context);
23135
23629
  },
23136
- events() {
23137
- return watchEvents(harness, target.id, context);
23138
- },
23139
23630
  async usage() {
23140
23631
  return mergeUsage(await harness.snapshot(UsageDoc2, target.id, context) ?? { models: {}, tools: {} }, (await harness.snapshot(SessionMetadataDoc, target.id, context))?.namingUsage ?? { models: {}, tools: {} });
23141
23632
  },
@@ -23178,24 +23669,55 @@ async function createNekoRuntime(options = {}) {
23178
23669
  if (await isRunning(target.id))
23179
23670
  throw new Error(PLAN_BUSY);
23180
23671
  await plans.setMode(target.id, mode);
23181
- await syncPlanAgent(target);
23672
+ await syncAgent(target);
23182
23673
  return plans.view(target.id);
23183
23674
  });
23184
23675
  },
23185
23676
  async respondPlan(version, decision) {
23186
23677
  const parsed = validatePlanDecision(decision);
23187
- const known = await plans.get(target.id, version);
23188
- if (!known)
23189
- throw new Error("Plan not found.");
23190
- if (known.status === "awaiting_approval") {
23678
+ return admit(target.id, async () => {
23679
+ const known = await plans.get(target.id, version);
23680
+ if (!known)
23681
+ throw new Error("Plan not found.");
23682
+ if (known.status !== "awaiting_approval") {
23683
+ const legacy = await plans.undispatched(target.id);
23684
+ if (legacy?.version === version)
23685
+ await dispatchPlan(target, legacy, submitNow);
23686
+ return known;
23687
+ }
23191
23688
  if (await isRunning(target.id))
23192
23689
  throw new Error(PLAN_BUSY);
23193
- await guardProvider((await target.agent(context)).model);
23194
- }
23195
- const { record, item } = await plans.decide(target.id, version, parsed);
23196
- if ((item.status === "approved" || item.status === "revision_requested") && !item.dispatched)
23197
- await dispatchPlan(target, item);
23198
- return record;
23690
+ await syncAgent(target);
23691
+ const agent = await target.agent(context);
23692
+ const approve = parsed.kind === "approve";
23693
+ const feedback = parsed.kind === "revise" ? parsed.feedback : "";
23694
+ const { draft, requestId } = await prepareSubmission(target, agent, approve ? approvedPlanPrompt(known) : planRevisionPrompt(known, feedback), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${version}`, whenBusy: "followUp" }, {
23695
+ planDecision: {
23696
+ version,
23697
+ kind: approve ? "approve" : "revise",
23698
+ title: known.title,
23699
+ ...!approve && feedback ? { feedback } : {}
23700
+ }
23701
+ });
23702
+ const codemode = (await harness.snapshot(CodemodeDoc, target.id, context))?.mode ?? "off";
23703
+ const leave = approve ? agentSyncChange(agent, { plan: "normal", codemode, role: "main" }) : undefined;
23704
+ let record;
23705
+ try {
23706
+ record = await plans.decideWith(target.id, version, parsed, async (tx) => {
23707
+ await target.submit(tx, draft);
23708
+ if (leave)
23709
+ await configure3(tx, target.id, leave);
23710
+ });
23711
+ } catch (error) {
23712
+ resourceSnapshots.discard(target.id, requestId);
23713
+ throw error;
23714
+ }
23715
+ if (record.status !== "approved" && record.status !== "revision_requested")
23716
+ resourceSnapshots.discard(target.id, requestId);
23717
+ await touchConversation(target.id);
23718
+ await metadata.reconcile(target.id);
23719
+ return record;
23720
+ });
23199
23721
  },
23200
23722
  async workspaceDir(path, signal) {
23201
23723
  return workspaceFiles.children(savedWorkspace((await target.agent(context)).cwd), path, signal ?? neverAborted());
@@ -23226,6 +23748,26 @@ async function createNekoRuntime(options = {}) {
23226
23748
  await target.abort(context);
23227
23749
  await plans.cancelPending(target.id);
23228
23750
  },
23751
+ continueRun() {
23752
+ return admit(target.id, async () => {
23753
+ await syncAgent(target);
23754
+ await guardProvider((await target.agent(context)).model);
23755
+ const live = await harness.snapshot(importLiveDoc, target.id, context);
23756
+ if (live?.run !== undefined)
23757
+ throw new Error(CONTINUE_BUSY);
23758
+ if (live?.runFailure === undefined)
23759
+ throw new Error(CONTINUE_UNAVAILABLE);
23760
+ try {
23761
+ await target.submit({ type: "continue" }, context);
23762
+ } catch (error) {
23763
+ if (error instanceof ConversationBusy)
23764
+ throw new Error(CONTINUE_BUSY);
23765
+ throw error;
23766
+ }
23767
+ await touchConversation(target.id);
23768
+ await metadata.reconcile(target.id);
23769
+ });
23770
+ },
23229
23771
  async withdraw(submissionId) {
23230
23772
  const id = submissionId;
23231
23773
  const inbox = await harness.snapshot(InboxDoc3, target.id, context);
@@ -23300,7 +23842,7 @@ async function createNekoRuntime(options = {}) {
23300
23842
  const session = await getConversation(id);
23301
23843
  if (!session)
23302
23844
  continue;
23303
- await admit(id, () => syncPlanAgent(session.conversation));
23845
+ await admit(id, () => syncAgent(session.conversation));
23304
23846
  const pending = await plans.undispatched(id);
23305
23847
  if (pending)
23306
23848
  await dispatchPlan(session.conversation, pending);
@@ -23386,6 +23928,8 @@ async function createNekoRuntime(options = {}) {
23386
23928
  }
23387
23929
  const child = at === undefined ? await tx.createConversation({ ownership: { kind: "ownerless" } }) : await tx.forkConversation(id, at, { ownership: { kind: "ownerless" } });
23388
23930
  (await tx.doc(DelegationsDoc, child.id)).items = inherited;
23931
+ if (at === undefined)
23932
+ (await tx.doc(CodemodeDoc, child.id)).mode = (await tx.doc(CodemodeDoc, id)).mode;
23389
23933
  if (sourceMetadata) {
23390
23934
  const doc = await tx.doc(SessionMetadataDoc, child.id);
23391
23935
  doc.title = sourceMetadata.title || sourceMetadata.firstPromptPreview;
@@ -23413,15 +23957,12 @@ async function createNekoRuntime(options = {}) {
23413
23957
  root: rootSession,
23414
23958
  submit: rootSession.submit,
23415
23959
  waitAnswer: rootSession.waitAnswer,
23416
- async viewState(id) {
23417
- return (await sessionFor(id)).viewState();
23960
+ async follow(id) {
23961
+ return (await sessionFor(id)).follow();
23418
23962
  },
23419
23963
  async watch(id) {
23420
23964
  return (await sessionFor(id)).watch();
23421
23965
  },
23422
- async events(id) {
23423
- return (await sessionFor(id)).events();
23424
- },
23425
23966
  async usage(id) {
23426
23967
  return (await sessionFor(id)).usage();
23427
23968
  },
@@ -23466,12 +24007,18 @@ async function createNekoRuntime(options = {}) {
23466
24007
  const workspace = await addWorkspace(createOptions?.cwd ?? defaultCwd);
23467
24008
  const savedDefault = (await harness.snapshot(DefaultModelDoc, conversation.id, context))?.model ?? null;
23468
24009
  const effective = createOptions?.model ?? modelOverride ?? savedDefault ?? model;
24010
+ const mode = (await codemodeState()).effective.mode;
23469
24011
  const created = await harness.createConversation({
23470
24012
  ownership: { kind: "ownerless" },
23471
24013
  agent: {
23472
24014
  model: effective,
23473
24015
  thinkingLevel: resolveLevel(effective, createOptions?.thinkingLevel ?? options.thinkingLevel ?? "high"),
23474
- cwd: workspace.path
24016
+ cwd: workspace.path,
24017
+ ...turnLimit,
24018
+ ...agentChangeFor({ plan: "normal", codemode: mode, role: "main" })
24019
+ },
24020
+ init: async (tx, id) => {
24021
+ (await tx.doc(CodemodeDoc, id)).mode = mode;
23475
24022
  }
23476
24023
  }, context);
23477
24024
  await metadata.initialize(created.id);
@@ -23526,6 +24073,20 @@ async function createNekoRuntime(options = {}) {
23526
24073
  async resetContextSettings() {
23527
24074
  return persistContext(emptySavedContextSettings());
23528
24075
  },
24076
+ codemodeSettings: codemodeState,
24077
+ async setCodemodeMode(mode) {
24078
+ const parsed = parseCodemodeMode(mode);
24079
+ await harness.commit(async (tx) => {
24080
+ (await tx.doc(CodemodeSettingsDoc, conversation.id)).mode = parsed;
24081
+ }, context);
24082
+ return codemodeState();
24083
+ },
24084
+ async resetCodemodeMode() {
24085
+ await harness.commit(async (tx) => {
24086
+ (await tx.doc(CodemodeSettingsDoc, conversation.id)).mode = null;
24087
+ }, context);
24088
+ return codemodeState();
24089
+ },
23529
24090
  async searchSettings() {
23530
24091
  return { selected: searchProvider };
23531
24092
  },
@@ -23647,6 +24208,7 @@ async function createNekoRuntime(options = {}) {
23647
24208
  registry.install(extension);
23648
24209
  },
23649
24210
  async close() {
24211
+ await codemode.close();
23650
24212
  await cleanup();
23651
24213
  return harness.close(context);
23652
24214
  }
@@ -23654,7 +24216,20 @@ async function createNekoRuntime(options = {}) {
23654
24216
  }
23655
24217
  // packages/gateway/src/contract.ts
23656
24218
  import { hostNamed, method, remote } from "@earendil-works/chord";
23657
- import { Type as Type8 } from "typebox";
24219
+ import {
24220
+ arr as arr6,
24221
+ bool as bool3,
24222
+ int as int5,
24223
+ lit as lit3,
24224
+ map,
24225
+ nul as nul3,
24226
+ num as num4,
24227
+ obj as obj8,
24228
+ oneOf as oneOf2,
24229
+ opt as opt8,
24230
+ str as str8,
24231
+ union as union4
24232
+ } from "@earendil-works/pi-schema";
23658
24233
  var GATEWAY_HOST = "neko";
23659
24234
  var rpc = (args, result) => [args, result];
23660
24235
  function contract(name, def) {
@@ -23667,433 +24242,458 @@ function contract(name, def) {
23667
24242
  ]));
23668
24243
  return remote(name, { ...def, host: hostNamed(GATEWAY_HOST), variants });
23669
24244
  }
23670
- var str = Type8.String();
23671
- var num = Type8.Number();
23672
- var bool = Type8.Boolean();
23673
- var nil = Type8.Null();
23674
- var integer = Type8.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
23675
- var nullableString = Type8.Union([str, nil]);
23676
- var phase = Type8.Enum(["pending", "running", "done"]);
23677
- var level = Type8.Enum(["project", "user", "builtin"]);
23678
- var thinking = Type8.Enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
23679
- var model2 = Type8.Object({ provider: str, modelId: str });
23680
- var change = Type8.Object({ model: Type8.Optional(model2), thinkingLevel: Type8.Optional(thinking) }, { additionalProperties: false });
23681
- var usage = Type8.Object({ input: num, output: num, cacheRead: num, cacheWrite: num, cost: num });
23682
- var image = Type8.Object({
23683
- name: str,
23684
- mimeType: str,
24245
+ var text3 = str8();
24246
+ var number = num4();
24247
+ var boolean = bool3();
24248
+ var nil = nul3();
24249
+ var integer = int5({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
24250
+ var nullableString = union4([text3, nil]);
24251
+ var phase = oneOf2(["pending", "running", "done"]);
24252
+ var level = oneOf2(["project", "user", "builtin"]);
24253
+ var thinking = oneOf2(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
24254
+ var model2 = obj8({ provider: text3, modelId: text3 }, { open: true });
24255
+ var changeProperties = { model: opt8(model2), thinkingLevel: opt8(thinking) };
24256
+ var change = obj8(changeProperties);
24257
+ var usage = obj8({ input: number, output: number, cacheRead: number, cacheWrite: number, cost: number }, { open: true });
24258
+ var image = obj8({
24259
+ name: text3,
24260
+ mimeType: text3,
23685
24261
  width: integer,
23686
24262
  height: integer,
23687
24263
  bytes: integer,
23688
24264
  entryId: integer,
23689
24265
  contentIndex: integer
23690
- });
23691
- var skill = Type8.Object({ name: str, location: str, body: str, args: str });
23692
- var call = Type8.Object({
23693
- callId: str,
23694
- name: str,
23695
- summary: str,
24266
+ }, { open: true });
24267
+ var skill = obj8({ name: text3, location: text3, body: text3, args: text3 }, { open: true });
24268
+ var CodemodeViewSchema = obj8({
24269
+ calls: arr6(obj8({
24270
+ id: integer,
24271
+ name: text3,
24272
+ args: text3,
24273
+ status: oneOf2(["running", "ok", "error", "cancelled"]),
24274
+ durationMs: union4([number, nil]),
24275
+ error: nullableString
24276
+ }, { open: true })),
24277
+ omitted: integer,
24278
+ fullOutputPath: nullableString
24279
+ }, { open: true });
24280
+ var call = obj8({
24281
+ callId: text3,
24282
+ name: text3,
24283
+ summary: text3,
23696
24284
  detail: nullableString,
23697
24285
  phase,
23698
24286
  output: nullableString,
23699
24287
  diff: nullableString,
23700
- diffAdded: Type8.Union([num, nil]),
23701
- diffRemoved: Type8.Union([num, nil]),
23702
- diffTruncated: Type8.Optional(bool)
23703
- });
23704
- var DelegationSchema = Type8.Object({
24288
+ diffAdded: union4([number, nil]),
24289
+ diffRemoved: union4([number, nil]),
24290
+ diffTruncated: opt8(boolean),
24291
+ codemode: opt8(CodemodeViewSchema)
24292
+ }, { open: true });
24293
+ var DelegationSchema = obj8({
23705
24294
  parentId: integer,
23706
- agentId: Type8.Optional(str),
23707
- round: Type8.Optional(integer),
23708
- runnerId: Type8.Optional(integer),
23709
- inputEntryId: Type8.Optional(integer),
23710
- answerEntryId: Type8.Optional(integer),
23711
- roundUsage: Type8.Optional(Type8.Union([usage, nil])),
24295
+ agentId: opt8(text3),
24296
+ round: opt8(integer),
24297
+ runnerId: opt8(integer),
24298
+ inputEntryId: opt8(integer),
24299
+ answerEntryId: opt8(integer),
24300
+ roundUsage: opt8(union4([usage, nil])),
23712
24301
  taskId: integer,
23713
- callId: str,
24302
+ callId: text3,
23714
24303
  childId: integer,
23715
- role: Type8.Enum(["explore", "plan", "coder"]),
23716
- title: str,
23717
- createdAt: num,
23718
- startedAt: Type8.Union([num, nil]),
23719
- endedAt: Type8.Union([num, nil]),
23720
- status: Type8.Enum(["queued", "running", "completed", "failed", "cancelled"]),
24304
+ role: oneOf2(["explore", "plan", "coder"]),
24305
+ title: text3,
24306
+ createdAt: number,
24307
+ startedAt: union4([number, nil]),
24308
+ endedAt: union4([number, nil]),
24309
+ status: oneOf2(["queued", "running", "completed", "failed", "cancelled"]),
23721
24310
  report: nullableString,
23722
- approvalCount: Type8.Optional(integer),
23723
- historyThrough: Type8.Optional(integer),
23724
- historyUsage: Type8.Optional(usage)
23725
- });
23726
- var SubagentSummarySchema = Type8.Object({
23727
- agentId: str,
24311
+ approvalCount: opt8(integer),
24312
+ historyThrough: opt8(integer),
24313
+ historyUsage: opt8(usage)
24314
+ }, { open: true });
24315
+ var SubagentSummarySchema = obj8({
24316
+ agentId: text3,
23728
24317
  parentId: integer,
23729
24318
  childId: integer,
23730
- role: Type8.Enum(["explore", "plan", "coder"]),
23731
- title: str,
23732
- status: Type8.Enum(["idle", "queued", "running"]),
23733
- activeRoundId: Type8.Union([integer, nil]),
24319
+ role: oneOf2(["explore", "plan", "coder"]),
24320
+ title: text3,
24321
+ status: oneOf2(["idle", "queued", "running"]),
24322
+ activeRoundId: union4([integer, nil]),
23734
24323
  lastRoundId: integer,
23735
- lastReport: Type8.Optional(nullableString),
24324
+ lastReport: opt8(nullableString),
23736
24325
  roundCount: integer,
23737
- taskTitle: str,
24326
+ taskTitle: text3,
23738
24327
  approvalCount: integer,
23739
- createdAt: num,
23740
- historyThrough: Type8.Optional(integer),
23741
- historyUsage: Type8.Optional(usage)
23742
- });
23743
- var jobStatus = Type8.Enum(["running", "exited", "failed", "stopped", "lost"]);
23744
- var JobSummarySchema = Type8.Object({
23745
- jobId: str,
23746
- title: str,
23747
- command: str,
23748
- cwd: str,
24328
+ createdAt: number,
24329
+ historyThrough: opt8(integer),
24330
+ historyUsage: opt8(usage)
24331
+ }, { open: true });
24332
+ var jobStatus = oneOf2(["running", "exited", "failed", "stopped", "lost"]);
24333
+ var JobSummarySchema = obj8({
24334
+ jobId: text3,
24335
+ title: text3,
24336
+ command: text3,
24337
+ cwd: text3,
23749
24338
  status: jobStatus,
23750
- startedAt: num,
23751
- endedAt: Type8.Union([num, nil]),
23752
- exitCode: Type8.Union([num, nil]),
23753
- reason: Type8.Optional(Type8.Enum(["timeout", "spawn-error", "restart", "aborted"])),
23754
- timeoutSeconds: Type8.Optional(num),
24339
+ startedAt: number,
24340
+ endedAt: union4([number, nil]),
24341
+ exitCode: union4([number, nil]),
24342
+ reason: opt8(oneOf2(["timeout", "spawn-error", "restart", "aborted"])),
24343
+ timeoutSeconds: opt8(number),
23755
24344
  outputBytes: integer
23756
- });
23757
- var JobOutputSchema = Type8.Object({
24345
+ }, { open: true });
24346
+ var JobOutputSchema = obj8({
23758
24347
  job: JobSummarySchema,
23759
- text: str,
24348
+ text: text3,
23760
24349
  offset: integer,
23761
- truncated: bool,
24350
+ truncated: boolean,
23762
24351
  dropped: integer
23763
- });
23764
- var QueuedInputSchema = Type8.Object({
24352
+ }, { open: true });
24353
+ var QueuedInputSchema = obj8({
23765
24354
  id: integer,
23766
- mode: Type8.Enum(["steer", "followUp"]),
23767
- preview: str,
24355
+ mode: oneOf2(["steer", "followUp"]),
24356
+ preview: text3,
23768
24357
  images: integer,
23769
- skill: Type8.Optional(Type8.Object({ name: str, args: str }))
23770
- });
23771
- var WithdrawResultSchema = Type8.Object({
23772
- result: Type8.Enum(["withdrawn", "placed", "settled", "unknown"]),
23773
- text: Type8.Optional(str),
23774
- images: Type8.Optional(integer)
23775
- });
23776
- var SteerResultSchema = Type8.Object({
23777
- result: Type8.Enum(["steered", "withdrawn", "placed", "settled", "unknown"]),
23778
- id: Type8.Optional(integer),
23779
- text: Type8.Optional(str),
23780
- images: Type8.Optional(integer)
24358
+ skill: opt8(obj8({ name: text3, args: text3 }, { open: true }))
24359
+ }, { open: true });
24360
+ var WithdrawResultSchema = obj8({
24361
+ result: oneOf2(["withdrawn", "placed", "settled", "unknown"]),
24362
+ text: opt8(text3),
24363
+ images: opt8(integer)
24364
+ }, { open: true });
24365
+ var SteerResultSchema = obj8({
24366
+ result: oneOf2(["steered", "withdrawn", "placed", "settled", "unknown"]),
24367
+ id: opt8(integer),
24368
+ text: opt8(text3),
24369
+ images: opt8(integer)
24370
+ }, { open: true });
24371
+ var TodoItemSchema = obj8({
24372
+ id: str8({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
24373
+ text: str8({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
24374
+ status: oneOf2(["pending", "in_progress", "completed"])
23781
24375
  });
23782
- var TodoItemSchema = Type8.Object({
23783
- id: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
23784
- text: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
23785
- status: Type8.Enum(["pending", "in_progress", "completed"])
23786
- }, { additionalProperties: false });
23787
- var TodoListSchema = Type8.Object({
24376
+ var TodoListSchema = obj8({
23788
24377
  revision: integer,
23789
- items: Type8.Array(TodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
24378
+ items: arr6(TodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
24379
+ }, { open: true });
24380
+ var QuestionOptionSchema = obj8({
24381
+ id: str8({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
24382
+ label: str8({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
24383
+ description: opt8(str8({ maxLength: QUESTION_LIMITS.descriptionChars })),
24384
+ recommended: opt8(boolean)
23790
24385
  });
23791
- var QuestionOptionSchema = Type8.Object({
23792
- id: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
23793
- label: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
23794
- description: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
23795
- recommended: Type8.Optional(bool)
23796
- }, { additionalProperties: false });
23797
- var questionStatus = Type8.Enum(["pending", "answered", "skipped", "cancelled"]);
23798
- var QuestionSummarySchema = Type8.Object({
24386
+ var questionStatus = oneOf2(["pending", "answered", "skipped", "cancelled"]);
24387
+ var QuestionSummarySchema = obj8({
23799
24388
  id: integer,
23800
- version: str,
23801
- preview: Type8.String({ maxLength: QUESTION_LIMITS.previewChars }),
23802
- multiple: bool,
24389
+ version: text3,
24390
+ preview: str8({ maxLength: QUESTION_LIMITS.previewChars }),
24391
+ multiple: boolean,
23803
24392
  optionCount: integer,
23804
- createdAt: num
24393
+ createdAt: number
24394
+ }, { open: true });
24395
+ var WorkspaceEntrySchema = obj8({
24396
+ name: text3,
24397
+ path: text3,
24398
+ kind: oneOf2(["file", "directory", "symlink"]),
24399
+ protected: opt8(lit3(true))
23805
24400
  });
23806
- var WorkspaceEntrySchema = Type8.Object({
23807
- name: str,
23808
- path: str,
23809
- kind: Type8.Enum(["file", "directory", "symlink"]),
23810
- protected: Type8.Optional(Type8.Literal(true))
23811
- }, { additionalProperties: false });
23812
- var WorkspaceDirSchema = Type8.Object({
23813
- path: str,
23814
- entries: Type8.Array(WorkspaceEntrySchema, { maxItems: 1000 }),
23815
- limited: bool,
23816
- incomplete: bool
23817
- });
23818
- var WorkspaceFileSchema = Type8.Object({
23819
- path: str,
23820
- size: num,
23821
- mtimeMs: num,
23822
- kind: Type8.Enum(["text", "binary", "protected"]),
23823
- content: Type8.Optional(str),
23824
- truncated: bool
23825
- });
23826
- var ChangeSummarySchema = Type8.Object({ revision: integer, files: integer, added: integer, removed: integer });
23827
- var ChangedFileSchema = Type8.Object({
23828
- path: str,
23829
- outside: Type8.Optional(Type8.Literal(true)),
24401
+ var WorkspaceDirSchema = obj8({
24402
+ path: text3,
24403
+ entries: arr6(WorkspaceEntrySchema, { maxItems: 1000 }),
24404
+ limited: boolean,
24405
+ incomplete: boolean
24406
+ }, { open: true });
24407
+ var WorkspaceFileSchema = obj8({
24408
+ path: text3,
24409
+ size: number,
24410
+ mtimeMs: number,
24411
+ kind: oneOf2(["text", "binary", "protected"]),
24412
+ content: opt8(text3),
24413
+ truncated: boolean
24414
+ }, { open: true });
24415
+ var ChangeSummarySchema = obj8({ revision: integer, files: integer, added: integer, removed: integer }, { open: true });
24416
+ var ChangedFileSchema = obj8({
24417
+ path: text3,
24418
+ outside: opt8(lit3(true)),
23830
24419
  ops: integer,
23831
24420
  added: integer,
23832
24421
  removed: integer,
23833
- agents: Type8.Array(str, { maxItems: 64 }),
24422
+ agents: arr6(text3, { maxItems: 64 }),
23834
24423
  lastSeq: integer
23835
- }, { additionalProperties: false });
23836
- var ChangeListSchema = Type8.Object({
23837
- revision: integer,
23838
- files: Type8.Array(ChangedFileSchema, { maxItems: 500 }),
23839
- truncated: bool
23840
24424
  });
23841
- var ChangeOpSchema = Type8.Object({
24425
+ var ChangeListSchema = obj8({
24426
+ revision: integer,
24427
+ files: arr6(ChangedFileSchema, { maxItems: 500 }),
24428
+ truncated: boolean
24429
+ }, { open: true });
24430
+ var ChangeOpSchema = obj8({
23842
24431
  seq: integer,
23843
- agent: str,
23844
- tool: Type8.Enum(["edit", "write"]),
23845
- diff: str,
24432
+ agent: text3,
24433
+ tool: oneOf2(["edit", "write"]),
24434
+ diff: text3,
23846
24435
  added: integer,
23847
24436
  removed: integer,
23848
- truncated: bool
23849
- }, { additionalProperties: false });
23850
- var ChangeDetailSchema = Type8.Object({
23851
- path: str,
23852
- outside: Type8.Optional(Type8.Literal(true)),
23853
- ops: Type8.Array(ChangeOpSchema, { maxItems: 50 }),
23854
- truncated: bool
24437
+ truncated: boolean
23855
24438
  });
23856
- var QuestionListSchema = Type8.Object({
24439
+ var ChangeDetailSchema = obj8({
24440
+ path: text3,
24441
+ outside: opt8(lit3(true)),
24442
+ ops: arr6(ChangeOpSchema, { maxItems: 50 }),
24443
+ truncated: boolean
24444
+ }, { open: true });
24445
+ var QuestionListSchema = obj8({
23857
24446
  revision: integer,
23858
- pending: Type8.Array(QuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
23859
- });
23860
- var QuestionRecordSchema = Type8.Object({
24447
+ pending: arr6(QuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
24448
+ }, { open: true });
24449
+ var QuestionRecordSchema = obj8({
23861
24450
  id: integer,
23862
- callId: str,
23863
- version: str,
23864
- question: Type8.String({ maxLength: QUESTION_LIMITS.questionChars }),
23865
- multiple: bool,
23866
- options: Type8.Array(QuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
24451
+ callId: text3,
24452
+ version: text3,
24453
+ question: str8({ maxLength: QUESTION_LIMITS.questionChars }),
24454
+ multiple: boolean,
24455
+ options: arr6(QuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
23867
24456
  status: questionStatus,
23868
- answer: Type8.Union([
23869
- Type8.Object({
23870
- optionIds: Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
23871
- text: Type8.Union([Type8.String({ maxLength: QUESTION_LIMITS.textChars }), nil])
23872
- }),
24457
+ answer: union4([
24458
+ obj8({
24459
+ optionIds: arr6(str8(), { maxItems: QUESTION_LIMITS.maxOptions }),
24460
+ text: union4([str8({ maxLength: QUESTION_LIMITS.textChars }), nil])
24461
+ }, { open: true }),
23873
24462
  nil
23874
24463
  ]),
23875
- createdAt: num,
23876
- settledAt: Type8.Union([num, nil])
23877
- });
23878
- var QuestionResponseSchema = Type8.Union([
23879
- Type8.Object({
23880
- kind: Type8.Literal("answer"),
23881
- optionIds: Type8.Optional(Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
23882
- text: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.textChars }))
23883
- }, { additionalProperties: false }),
23884
- Type8.Object({ kind: Type8.Literal("skip") }, { additionalProperties: false })
24464
+ createdAt: number,
24465
+ settledAt: union4([number, nil])
24466
+ }, { open: true });
24467
+ var QuestionResponseSchema = union4([
24468
+ obj8({
24469
+ kind: lit3("answer"),
24470
+ optionIds: opt8(arr6(str8(), { maxItems: QUESTION_LIMITS.maxOptions })),
24471
+ text: opt8(str8({ maxLength: QUESTION_LIMITS.textChars }))
24472
+ }),
24473
+ obj8({ kind: lit3("skip") })
23885
24474
  ]);
23886
- var PlanRecordSchema = Type8.Object({
23887
- version: str,
23888
- title: Type8.String({ maxLength: PLAN_LIMITS.titleChars }),
23889
- steps: Type8.Array(Type8.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
23890
- note: Type8.String({ maxLength: PLAN_LIMITS.noteChars }),
23891
- status: Type8.Enum(["awaiting_approval", "approved", "revision_requested", "superseded", "cancelled"]),
23892
- feedback: Type8.Union([Type8.String({ maxLength: PLAN_LIMITS.feedbackChars }), nil]),
23893
- createdAt: num,
23894
- settledAt: Type8.Union([num, nil])
23895
- });
23896
- var PlanViewSchema = Type8.Object({
23897
- mode: Type8.Enum(["normal", "plan"]),
24475
+ var PlanRecordSchema = obj8({
24476
+ version: text3,
24477
+ title: str8({ maxLength: PLAN_LIMITS.titleChars }),
24478
+ steps: arr6(str8({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
24479
+ note: str8({ maxLength: PLAN_LIMITS.noteChars }),
24480
+ status: oneOf2(["awaiting_approval", "approved", "revision_requested", "superseded", "cancelled"]),
24481
+ feedback: union4([str8({ maxLength: PLAN_LIMITS.feedbackChars }), nil]),
24482
+ createdAt: number,
24483
+ settledAt: union4([number, nil])
24484
+ }, { open: true });
24485
+ var PlanViewSchema = obj8({
24486
+ mode: oneOf2(["normal", "plan"]),
23898
24487
  revision: integer,
23899
- current: Type8.Union([PlanRecordSchema, nil])
23900
- });
23901
- var PlanDecisionSchema = Type8.Union([
23902
- Type8.Object({ kind: Type8.Literal("approve") }, { additionalProperties: false }),
23903
- Type8.Object({ kind: Type8.Literal("revise"), feedback: Type8.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
24488
+ current: union4([PlanRecordSchema, nil])
24489
+ }, { open: true });
24490
+ var PlanDecisionSchema = union4([
24491
+ obj8({ kind: lit3("approve") }),
24492
+ obj8({ kind: lit3("revise"), feedback: str8({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) })
23904
24493
  ]);
23905
- var ToolInteractionSchema = Type8.Union([
23906
- Type8.Object({
23907
- kind: Type8.Literal("ask_user"),
24494
+ var ToolInteractionSchema = union4([
24495
+ obj8({
24496
+ kind: lit3("ask_user"),
23908
24497
  id: integer,
23909
- question: str,
23910
- multiple: bool,
23911
- options: Type8.Array(Type8.Object({ id: str, label: str, recommended: Type8.Optional(bool) })),
24498
+ question: text3,
24499
+ multiple: boolean,
24500
+ options: arr6(obj8({ id: text3, label: text3, recommended: opt8(boolean) }, { open: true })),
23912
24501
  status: questionStatus,
23913
- optionIds: Type8.Optional(Type8.Array(str)),
23914
- labels: Type8.Optional(Type8.Array(str)),
23915
- text: Type8.Optional(Type8.Union([str, nil]))
23916
- }),
23917
- Type8.Object({
23918
- kind: Type8.Literal("todo"),
23919
- action: Type8.Enum(["list", "replace"]),
24502
+ optionIds: opt8(arr6(text3)),
24503
+ labels: opt8(arr6(text3)),
24504
+ text: opt8(union4([text3, nil]))
24505
+ }, { open: true }),
24506
+ obj8({
24507
+ kind: lit3("todo"),
24508
+ action: oneOf2(["list", "replace"]),
23920
24509
  revision: integer,
23921
24510
  completed: integer,
23922
24511
  total: integer
23923
- }),
23924
- Type8.Object({
23925
- kind: Type8.Literal("submit_plan"),
23926
- version: str,
23927
- title: str,
23928
- steps: Type8.Array(str),
23929
- note: str
23930
- })
24512
+ }, { open: true }),
24513
+ obj8({
24514
+ kind: lit3("submit_plan"),
24515
+ version: text3,
24516
+ title: text3,
24517
+ steps: arr6(text3),
24518
+ note: text3
24519
+ }, { open: true })
23931
24520
  ]);
23932
- var ChatViewSchema = Type8.Object({
23933
- historyBefore: Type8.Optional(Type8.Union([integer, nil])),
23934
- agents: Type8.Optional(Type8.Array(SubagentSummarySchema)),
23935
- jobs: Type8.Optional(Type8.Array(JobSummarySchema)),
23936
- queued: Type8.Optional(Type8.Array(QueuedInputSchema)),
23937
- todos: Type8.Optional(TodoListSchema),
23938
- questions: Type8.Optional(QuestionListSchema),
23939
- plan: Type8.Optional(PlanViewSchema),
23940
- changes: Type8.Optional(ChangeSummarySchema),
23941
- rounds: Type8.Optional(Type8.Array(DelegationSchema)),
23942
- subagents: Type8.Optional(Type8.Array(DelegationSchema)),
23943
- taskUsage: Type8.Optional(usage),
23944
- model: str,
23945
- agent: Type8.Object({ model: Type8.Union([model2, nil]), thinkingLevel: Type8.Union([thinking, nil]) }),
23946
- cwd: str,
23947
- busy: bool,
24521
+ var ChatViewSchema = obj8({
24522
+ historyBefore: opt8(union4([integer, nil])),
24523
+ agents: opt8(arr6(SubagentSummarySchema)),
24524
+ jobs: opt8(arr6(JobSummarySchema)),
24525
+ queued: opt8(arr6(QueuedInputSchema)),
24526
+ todos: opt8(TodoListSchema),
24527
+ questions: opt8(QuestionListSchema),
24528
+ plan: opt8(PlanViewSchema),
24529
+ changes: opt8(ChangeSummarySchema),
24530
+ rounds: opt8(arr6(DelegationSchema)),
24531
+ subagents: opt8(arr6(DelegationSchema)),
24532
+ taskUsage: opt8(usage),
24533
+ model: text3,
24534
+ agent: obj8({ model: union4([model2, nil]), thinkingLevel: union4([thinking, nil]) }, { open: true }),
24535
+ cwd: text3,
24536
+ busy: boolean,
23948
24537
  status: nullableString,
23949
24538
  usage,
23950
- contextUsage: Type8.Optional(Type8.Object({ tokens: Type8.Union([num, nil]), window: num, percent: Type8.Union([num, nil]) })),
23951
- compaction: Type8.Optional(Type8.Object({
23952
- failure: Type8.Optional(Type8.Object({ taskId: integer, reason: Type8.Enum(["manual", "threshold", "overflow"]), error: str })),
23953
- active: Type8.Array(Type8.Object({
24539
+ contextUsage: opt8(obj8({ tokens: union4([number, nil]), window: number, percent: union4([number, nil]) }, { open: true })),
24540
+ compaction: opt8(obj8({
24541
+ failure: opt8(obj8({ taskId: integer, reason: oneOf2(["manual", "threshold", "overflow"]), error: text3 }, { open: true })),
24542
+ active: arr6(obj8({
23954
24543
  taskId: integer,
23955
- reason: Type8.Enum(["manual", "threshold", "overflow"]),
23956
- blocking: bool,
24544
+ reason: oneOf2(["manual", "threshold", "overflow"]),
24545
+ blocking: boolean,
23957
24546
  attempt: integer,
23958
- retry: Type8.Optional(Type8.Object({ at: num, error: str }))
23959
- })),
23960
- latest: Type8.Union([
24547
+ retry: opt8(obj8({ at: number, error: text3 }, { open: true }))
24548
+ }, { open: true })),
24549
+ latest: union4([
23961
24550
  nil,
23962
- Type8.Object({
24551
+ obj8({
23963
24552
  taskId: integer,
23964
- phase: Type8.Enum(["summarizing", "waiting-to-apply", "applied", "nothing", "skipped", "cancelled", "failed"]),
23965
- at: num
23966
- })
24553
+ phase: oneOf2([
24554
+ "summarizing",
24555
+ "waiting-to-apply",
24556
+ "applied",
24557
+ "nothing",
24558
+ "skipped",
24559
+ "cancelled",
24560
+ "failed"
24561
+ ]),
24562
+ at: number
24563
+ }, { open: true })
23967
24564
  ])
23968
- })),
23969
- items: Type8.Array(Type8.Union([
23970
- Type8.Object({
23971
- type: Type8.Literal("user"),
23972
- order: Type8.Optional(num),
23973
- key: str,
24565
+ }, { open: true })),
24566
+ runFailure: opt8(obj8({ reason: text3, detail: opt8(text3) }, { open: true })),
24567
+ continuable: opt8(lit3(true)),
24568
+ items: arr6(union4([
24569
+ obj8({
24570
+ type: lit3("user"),
24571
+ order: opt8(number),
24572
+ key: text3,
23974
24573
  id: integer,
23975
- text: str,
23976
- images: Type8.Optional(Type8.Array(image)),
23977
- skill: Type8.Optional(skill),
23978
- defaultImagePrompt: Type8.Optional(Type8.Object({ version: Type8.Integer({ minimum: 1 }), text: str }))
23979
- }),
23980
- Type8.Object({
23981
- type: Type8.Literal("assistant"),
23982
- order: Type8.Optional(num),
23983
- key: str,
24574
+ text: text3,
24575
+ images: opt8(arr6(image)),
24576
+ skill: opt8(skill),
24577
+ defaultImagePrompt: opt8(obj8({ version: int5({ minimum: 1 }), text: text3 }, { open: true }))
24578
+ }, { open: true }),
24579
+ obj8({
24580
+ type: lit3("assistant"),
24581
+ order: opt8(number),
24582
+ key: text3,
23984
24583
  id: integer,
23985
- thinking: str,
23986
- text: str,
24584
+ thinking: text3,
24585
+ text: text3,
23987
24586
  stopReason: nullableString,
23988
24587
  errorMessage: nullableString,
23989
24588
  note: nullableString
23990
- }),
23991
- Type8.Object({
23992
- type: Type8.Literal("tool"),
23993
- order: Type8.Optional(num),
23994
- key: str,
23995
- images: Type8.Optional(Type8.Array(image)),
23996
- callId: str,
23997
- name: str,
23998
- summary: str,
24589
+ }, { open: true }),
24590
+ obj8({
24591
+ type: lit3("tool"),
24592
+ order: opt8(number),
24593
+ key: text3,
24594
+ images: opt8(arr6(image)),
24595
+ callId: text3,
24596
+ name: text3,
24597
+ summary: text3,
23999
24598
  detail: nullableString,
24000
24599
  phase,
24001
- isError: bool,
24600
+ isError: boolean,
24002
24601
  output: nullableString,
24003
24602
  result: nullableString,
24004
- durationMs: Type8.Union([num, nil]),
24603
+ durationMs: union4([number, nil]),
24005
24604
  note: nullableString,
24006
24605
  diff: nullableString,
24007
- diffAdded: Type8.Union([num, nil]),
24008
- diffRemoved: Type8.Union([num, nil]),
24009
- diffTruncated: Type8.Optional(bool),
24010
- interaction: Type8.Optional(ToolInteractionSchema)
24011
- }),
24012
- Type8.Object({
24013
- type: Type8.Literal("system"),
24014
- order: Type8.Optional(num),
24015
- key: str,
24606
+ diffAdded: union4([number, nil]),
24607
+ diffRemoved: union4([number, nil]),
24608
+ diffTruncated: opt8(boolean),
24609
+ interaction: opt8(ToolInteractionSchema),
24610
+ codemode: opt8(CodemodeViewSchema)
24611
+ }, { open: true }),
24612
+ obj8({
24613
+ type: lit3("system"),
24614
+ order: opt8(number),
24615
+ key: text3,
24016
24616
  id: integer,
24017
- text: str,
24018
- jobId: Type8.Optional(str),
24019
- planDecision: Type8.Optional(Type8.Object({ version: str, kind: Type8.Enum(["approve", "revise"]) }, { additionalProperties: false }))
24020
- })
24617
+ text: text3,
24618
+ jobId: opt8(text3),
24619
+ planDecision: opt8(obj8({ version: text3, kind: oneOf2(["approve", "revise"]) }))
24620
+ }, { open: true })
24021
24621
  ])),
24022
- streaming: Type8.Union([nil, Type8.Object({ thinking: str, text: str, toolCalls: Type8.Array(call) })])
24023
- });
24024
- var SessionSummarySchema = Type8.Object({
24025
- workspaceRoot: str,
24026
- lastUsedAt: num,
24027
- title: str,
24028
- firstPromptPreview: str,
24029
- createdAt: num,
24030
- lastMessageAt: Type8.Union([num, nil]),
24031
- model: Type8.Union([model2, nil]),
24032
- status: Type8.Enum(["idle", "running", "error"]),
24033
- awaitingAnswer: Type8.Optional(bool),
24034
- awaitingPlan: Type8.Optional(bool),
24035
- id: str
24036
- });
24037
- var DirectoryStateSchema = Type8.Object({ revision: integer, sessions: Type8.Array(SessionSummarySchema) });
24038
- var WorkspaceSummarySchema = Type8.Object({ path: str, createdAt: num });
24039
- var WorkspaceStateSchema = Type8.Object({ revision: integer, workspaces: Type8.Array(WorkspaceSummarySchema) });
24040
- var credentialStatus = Type8.Object({
24041
- savedConfigured: bool,
24042
- environmentConfigured: bool,
24043
- source: Type8.Enum(["saved", "environment", "runtime", "keyless", "none"]),
24044
- enabled: Type8.Optional(bool)
24045
- });
24046
- var CredentialStateSchema = Type8.Object({
24622
+ streaming: union4([nil, obj8({ thinking: text3, text: text3, toolCalls: arr6(call) }, { open: true })])
24623
+ }, { open: true });
24624
+ var SessionSummarySchema = obj8({
24625
+ workspaceRoot: text3,
24626
+ lastUsedAt: number,
24627
+ title: text3,
24628
+ firstPromptPreview: text3,
24629
+ createdAt: number,
24630
+ lastMessageAt: union4([number, nil]),
24631
+ model: union4([model2, nil]),
24632
+ status: oneOf2(["idle", "running", "error"]),
24633
+ awaitingAnswer: opt8(boolean),
24634
+ awaitingPlan: opt8(boolean),
24635
+ id: text3
24636
+ }, { open: true });
24637
+ var DirectoryStateSchema = obj8({ revision: integer, sessions: arr6(SessionSummarySchema) }, { open: true });
24638
+ var WorkspaceSummarySchema = obj8({ path: text3, createdAt: number }, { open: true });
24639
+ var WorkspaceStateSchema = obj8({ revision: integer, workspaces: arr6(WorkspaceSummarySchema) }, { open: true });
24640
+ var credentialStatus = obj8({
24641
+ savedConfigured: boolean,
24642
+ environmentConfigured: boolean,
24643
+ source: oneOf2(["saved", "environment", "runtime", "keyless", "none"]),
24644
+ enabled: opt8(boolean)
24645
+ }, { open: true });
24646
+ var CredentialStateSchema = obj8({
24047
24647
  revision: integer,
24048
- statuses: Type8.Record(str, credentialStatus)
24049
- });
24050
- var AttachmentSchema = Type8.Object({ sessionId: str, attachmentId: str });
24051
- var chatInput = Type8.Object({ sessionId: str, attachmentId: str }, { additionalProperties: false });
24648
+ statuses: map(credentialStatus)
24649
+ }, { open: true });
24650
+ var AttachmentSchema = obj8({ sessionId: text3, attachmentId: text3 }, { open: true });
24651
+ var chatInput = obj8({ sessionId: text3, attachmentId: text3 });
24052
24652
  var NekoSessionDirectory = contract("neko.session-directory", {
24053
24653
  version: 1,
24054
- input: Type8.Object({}, { additionalProperties: false }),
24654
+ input: obj8({}),
24055
24655
  variants: { ready: { state: { state: DirectoryStateSchema } } }
24056
24656
  });
24057
24657
  var NekoSessionManagement = contract("neko.session-management", {
24058
24658
  version: 1,
24059
- input: Type8.Object({}, { additionalProperties: false }),
24659
+ input: obj8({}),
24060
24660
  variants: {
24061
24661
  ready: {
24062
24662
  methods: {
24063
- create: rpc(Type8.Object({ ...change.properties, cwd: Type8.Optional(str) }, { additionalProperties: false }), SessionSummarySchema),
24064
- archive: rpc(str, nil),
24065
- attach: rpc(str, AttachmentSchema),
24663
+ create: rpc(obj8({ ...changeProperties, cwd: opt8(text3) }), SessionSummarySchema),
24664
+ archive: rpc(text3, nil),
24665
+ attach: rpc(text3, AttachmentSchema),
24066
24666
  detach: rpc(nil, nil),
24067
- fork: rpc(str, SessionSummarySchema)
24667
+ fork: rpc(text3, SessionSummarySchema)
24068
24668
  }
24069
24669
  }
24070
24670
  }
24071
24671
  });
24072
- var resize = Type8.Object({
24073
- maxWidth: Type8.Optional(integer),
24074
- maxHeight: Type8.Optional(integer),
24075
- maxBytes: Type8.Optional(integer),
24076
- jpegQuality: Type8.Optional(integer)
24077
- });
24078
- var inputLimits = Type8.Object({
24079
- maxRequestBytes: Type8.Optional(integer),
24080
- images: Type8.Optional(Type8.Object({
24081
- resize: Type8.Optional(resize),
24082
- maxPerMessage: Type8.Optional(integer),
24083
- maxPerRequest: Type8.Optional(integer)
24084
- }))
24085
- });
24086
- var modelInfo = Type8.Object({
24087
- provider: str,
24088
- modelId: str,
24089
- name: str,
24090
- available: bool,
24091
- contextWindow: num,
24092
- input: Type8.Array(str),
24093
- inputLimits: Type8.Optional(inputLimits),
24094
- reasoning: bool,
24095
- supportedThinkingLevels: Type8.Array(thinking),
24096
- thinkingLevelAdjustments: Type8.Object({
24672
+ var resize = obj8({
24673
+ maxWidth: opt8(integer),
24674
+ maxHeight: opt8(integer),
24675
+ maxBytes: opt8(integer),
24676
+ jpegQuality: opt8(integer)
24677
+ }, { open: true });
24678
+ var inputLimits = obj8({
24679
+ maxRequestBytes: opt8(integer),
24680
+ images: opt8(obj8({
24681
+ resize: opt8(resize),
24682
+ maxPerMessage: opt8(integer),
24683
+ maxPerRequest: opt8(integer)
24684
+ }, { open: true }))
24685
+ }, { open: true });
24686
+ var modelInfo = obj8({
24687
+ provider: text3,
24688
+ modelId: text3,
24689
+ name: text3,
24690
+ available: boolean,
24691
+ contextWindow: number,
24692
+ input: arr6(text3),
24693
+ inputLimits: opt8(inputLimits),
24694
+ reasoning: boolean,
24695
+ supportedThinkingLevels: arr6(thinking),
24696
+ thinkingLevelAdjustments: obj8({
24097
24697
  off: thinking,
24098
24698
  minimal: thinking,
24099
24699
  low: thinking,
@@ -24101,35 +24701,35 @@ var modelInfo = Type8.Object({
24101
24701
  high: thinking,
24102
24702
  xhigh: thinking,
24103
24703
  max: thinking
24104
- })
24105
- });
24704
+ }, { open: true })
24705
+ }, { open: true });
24106
24706
  var NekoModels = contract("neko.models", {
24107
24707
  version: 1,
24108
- input: Type8.Object({}, { additionalProperties: false }),
24109
- variants: { ready: { methods: { list: rpc(nil, Type8.Array(modelInfo)) } } }
24708
+ input: obj8({}),
24709
+ variants: { ready: { methods: { list: rpc(nil, arr6(modelInfo)) } } }
24110
24710
  });
24111
- var imageChunk = Type8.Object({ data: str, total: integer, mimeType: str });
24711
+ var imageChunk = obj8({ data: text3, total: integer, mimeType: text3 }, { open: true });
24112
24712
  var NekoSubagent = contract("neko.subagent", {
24113
24713
  version: 1,
24114
- input: Type8.Object({
24115
- sessionId: str,
24116
- attachmentId: str,
24117
- taskId: Type8.Optional(integer),
24118
- agentId: Type8.Optional(str)
24119
- }),
24714
+ input: obj8({
24715
+ sessionId: text3,
24716
+ attachmentId: text3,
24717
+ taskId: opt8(integer),
24718
+ agentId: opt8(text3)
24719
+ }, { open: true }),
24120
24720
  variants: {
24121
24721
  ready: {
24122
24722
  state: { revision: integer },
24123
24723
  methods: {
24124
- chunk: rpc(Type8.Object({
24125
- kind: Type8.Enum(["view", "history"]),
24126
- before: Type8.Optional(integer),
24127
- token: Type8.Optional(integer),
24724
+ chunk: rpc(obj8({
24725
+ kind: oneOf2(["view", "history"]),
24726
+ before: opt8(integer),
24727
+ token: opt8(integer),
24128
24728
  offset: integer
24129
- }), Type8.Object({ token: integer, data: str, next: Type8.Union([integer, nil]) })),
24130
- getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk),
24131
- decideApproval: rpc(Type8.Object({ id: integer, version: str, decision: Type8.Enum(["allowed", "denied"]) }), Type8.Enum(["pending", "allowed", "denied", "cancelled"])),
24132
- stop: rpc(Type8.Union([nil, Type8.Object({ taskId: integer })]), nil)
24729
+ }, { open: true }), obj8({ token: integer, data: text3, next: union4([integer, nil]) }, { open: true })),
24730
+ getImage: rpc(obj8({ entryId: integer, contentIndex: integer, offset: integer }, { open: true }), imageChunk),
24731
+ decideApproval: rpc(obj8({ id: integer, version: text3, decision: oneOf2(["allowed", "denied"]) }, { open: true }), oneOf2(["pending", "allowed", "denied", "cancelled"])),
24732
+ stop: rpc(union4([nil, obj8({ taskId: integer }, { open: true })]), nil)
24133
24733
  }
24134
24734
  }
24135
24735
  }
@@ -24141,157 +24741,176 @@ var NekoChat = contract("neko.chat", {
24141
24741
  ready: {
24142
24742
  state: { view: ChatViewSchema },
24143
24743
  methods: {
24144
- submit: rpc(Type8.Object({
24145
- content: str,
24146
- images: Type8.Optional(Type8.Array(str, { maxItems: 4 })),
24147
- requestId: Type8.Optional(str),
24148
- whenBusy: Type8.Optional(Type8.Enum(["steer", "followUp"]))
24149
- }, { additionalProperties: false }), nil),
24744
+ submit: rpc(obj8({
24745
+ content: text3,
24746
+ images: opt8(arr6(text3, { maxItems: 4 })),
24747
+ requestId: opt8(text3),
24748
+ whenBusy: opt8(oneOf2(["steer", "followUp"]))
24749
+ }), nil),
24150
24750
  abort: rpc(nil, nil),
24151
- withdraw: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), WithdrawResultSchema),
24152
- steer: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), SteerResultSchema),
24153
- questionGet: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), Type8.Union([QuestionRecordSchema, nil])),
24154
- questionRespond: rpc(Type8.Object({ id: integer, version: str, response: QuestionResponseSchema }, { additionalProperties: false }), QuestionRecordSchema),
24155
- planGet: rpc(Type8.Object({ version: str }, { additionalProperties: false }), Type8.Union([PlanRecordSchema, nil])),
24156
- planRespond: rpc(Type8.Object({ version: str, decision: PlanDecisionSchema }, { additionalProperties: false }), PlanRecordSchema),
24157
- planMode: rpc(Type8.Object({ mode: Type8.Enum(["normal", "plan"]) }, { additionalProperties: false }), PlanViewSchema),
24751
+ continueRun: rpc(nil, nil),
24752
+ withdraw: rpc(obj8({ id: integer }), WithdrawResultSchema),
24753
+ steer: rpc(obj8({ id: integer }), SteerResultSchema),
24754
+ questionGet: rpc(obj8({ id: integer }), union4([QuestionRecordSchema, nil])),
24755
+ questionRespond: rpc(obj8({ id: integer, version: text3, response: QuestionResponseSchema }), QuestionRecordSchema),
24756
+ planGet: rpc(obj8({ version: text3 }), union4([PlanRecordSchema, nil])),
24757
+ planRespond: rpc(obj8({ version: text3, decision: PlanDecisionSchema }), PlanRecordSchema),
24758
+ planMode: rpc(obj8({ mode: oneOf2(["normal", "plan"]) }), PlanViewSchema),
24158
24759
  usage: rpc(nil, usage),
24159
- workspaceDir: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceDirSchema),
24160
- workspaceFile: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceFileSchema),
24760
+ workspaceDir: rpc(obj8({ path: text3 }), WorkspaceDirSchema),
24761
+ workspaceFile: rpc(obj8({ path: text3 }), WorkspaceFileSchema),
24161
24762
  changesList: rpc(nil, ChangeListSchema),
24162
- changeGet: rpc(Type8.Object({ path: str }, { additionalProperties: false }), Type8.Union([ChangeDetailSchema, nil])),
24163
- skills: rpc(nil, Type8.Array(Type8.Object({ name: str, description: str, location: str, level }))),
24164
- jobOutput: rpc(Type8.Object({ jobId: str, since: Type8.Optional(integer) }, { additionalProperties: false }), JobOutputSchema),
24165
- jobStop: rpc(Type8.Object({ jobId: str }, { additionalProperties: false }), JobSummarySchema),
24763
+ changeGet: rpc(obj8({ path: text3 }), union4([ChangeDetailSchema, nil])),
24764
+ skills: rpc(nil, arr6(obj8({ name: text3, description: text3, location: text3, level }, { open: true }))),
24765
+ jobOutput: rpc(obj8({ jobId: text3, since: opt8(integer) }), JobOutputSchema),
24766
+ jobStop: rpc(obj8({ jobId: text3 }), JobSummarySchema),
24166
24767
  configure: rpc(change, nil),
24167
- compact: rpc(Type8.Object({ instructions: Type8.Optional(str) }, { additionalProperties: false }), nil),
24168
- importImage: rpc(str, Type8.Object({ id: str, name: str })),
24169
- draftImage: rpc(Type8.Object({ id: str, offset: integer }, { additionalProperties: false }), imageChunk),
24170
- beginImage: rpc(Type8.Object({ name: str, mimeType: str, size: Type8.Integer({ minimum: 1, maximum: 20971520 }) }, { additionalProperties: false }), str),
24171
- appendImage: rpc(Type8.Object({ id: str, offset: integer, data: Type8.String({ maxLength: 350000 }) }, { additionalProperties: false }), nil),
24172
- finishImage: rpc(str, nil),
24173
- releaseImage: rpc(str, nil),
24174
- getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }, { additionalProperties: false }), imageChunk)
24768
+ compact: rpc(obj8({ instructions: opt8(text3) }), nil),
24769
+ importImage: rpc(text3, obj8({ id: text3, name: text3 }, { open: true })),
24770
+ draftImage: rpc(obj8({ id: text3, offset: integer }), imageChunk),
24771
+ beginImage: rpc(obj8({ name: text3, mimeType: text3, size: int5({ minimum: 1, maximum: 20971520 }) }), text3),
24772
+ appendImage: rpc(obj8({ id: text3, offset: integer, data: str8({ maxLength: 350000 }) }), nil),
24773
+ finishImage: rpc(text3, nil),
24774
+ releaseImage: rpc(text3, nil),
24775
+ getImage: rpc(obj8({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk)
24175
24776
  }
24176
24777
  }
24177
24778
  }
24178
24779
  });
24179
- var browse = Type8.Object({ path: str, query: Type8.Optional(str), showHidden: Type8.Optional(bool), cursor: Type8.Optional(str) }, { additionalProperties: false });
24180
- var page = Type8.Object({
24181
- path: str,
24780
+ var browse = obj8({ path: text3, query: opt8(text3), showHidden: opt8(boolean), cursor: opt8(text3) });
24781
+ var page = obj8({
24782
+ path: text3,
24182
24783
  parent: nullableString,
24183
- entries: Type8.Array(Type8.Object({ name: str, path: str, error: Type8.Optional(str) })),
24784
+ entries: arr6(obj8({ name: text3, path: text3, error: opt8(text3) }, { open: true })),
24184
24785
  next: nullableString
24185
- });
24786
+ }, { open: true });
24186
24787
  var NekoWorkspaces = contract("neko.workspaces", {
24187
24788
  version: 2,
24188
- input: Type8.Object({}, { additionalProperties: false }),
24789
+ input: obj8({}),
24189
24790
  variants: {
24190
24791
  ready: {
24191
24792
  state: { state: WorkspaceStateSchema },
24192
24793
  methods: {
24193
- add: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceSummarySchema),
24194
- remove: rpc(Type8.Object({ path: str }, { additionalProperties: false }), Type8.Null()),
24794
+ add: rpc(obj8({ path: text3 }), WorkspaceSummarySchema),
24795
+ remove: rpc(obj8({ path: text3 }), nul3()),
24195
24796
  browse: rpc(browse, page)
24196
24797
  }
24197
24798
  }
24198
24799
  }
24199
24800
  });
24200
- var provider = Type8.String({ minLength: 1, maxLength: 64 });
24801
+ var provider = str8({ minLength: 1, maxLength: 64 });
24201
24802
  var NekoCredentials = contract("neko.credentials", {
24202
24803
  version: 2,
24203
- input: Type8.Object({}, { additionalProperties: false }),
24804
+ input: obj8({}),
24204
24805
  variants: {
24205
24806
  ready: {
24206
24807
  state: { state: CredentialStateSchema },
24207
24808
  methods: {
24208
- save: rpc(Type8.Object({ provider, key: Type8.String({ minLength: 1, maxLength: 8192 }) }, { additionalProperties: false }), nil),
24209
- remove: rpc(Type8.Object({ provider }, { additionalProperties: false }), nil),
24210
- setEnabled: rpc(Type8.Object({ provider, enabled: bool }, { additionalProperties: false }), nil)
24809
+ save: rpc(obj8({ provider, key: str8({ minLength: 1, maxLength: 8192 }) }), nil),
24810
+ remove: rpc(obj8({ provider }), nil),
24811
+ setEnabled: rpc(obj8({ provider, enabled: boolean }), nil)
24211
24812
  }
24212
24813
  }
24213
24814
  }
24214
24815
  });
24215
- var SearchSettingsStateSchema = Type8.Object({
24816
+ var SearchSettingsStateSchema = obj8({
24216
24817
  revision: integer,
24217
- selected: Type8.Enum(["tavily", "deepseek"])
24218
- });
24818
+ selected: oneOf2(["tavily", "deepseek"])
24819
+ }, { open: true });
24219
24820
  var NekoSearchSettings = contract("neko.search-settings", {
24220
24821
  version: 1,
24221
- input: Type8.Object({}, { additionalProperties: false }),
24822
+ input: obj8({}),
24222
24823
  variants: {
24223
24824
  ready: {
24224
24825
  state: { state: SearchSettingsStateSchema },
24225
24826
  methods: {
24226
- set: rpc(Type8.Object({ provider: Type8.Enum(["tavily", "deepseek"]) }, { additionalProperties: false }), nil)
24827
+ set: rpc(obj8({ provider: oneOf2(["tavily", "deepseek"]) }), nil)
24227
24828
  }
24228
24829
  }
24229
24830
  }
24230
24831
  });
24231
- var contextSource = Type8.Enum(["default", "config", "saved"]);
24232
- var ContextSettingsStateSchema = Type8.Object({
24832
+ var contextSource = oneOf2(["default", "config", "saved"]);
24833
+ var ContextSettingsStateSchema = obj8({
24233
24834
  revision: integer,
24234
- effective: Type8.Object({
24235
- compaction: Type8.Object({
24236
- enabled: bool,
24237
- triggerPercent: Type8.Union([integer, nil]),
24835
+ effective: obj8({
24836
+ compaction: obj8({
24837
+ enabled: boolean,
24838
+ triggerPercent: union4([integer, nil]),
24238
24839
  keepRecentTokens: integer
24239
- }),
24240
- pruning: Type8.Object({
24241
- enabled: bool,
24840
+ }, { open: true }),
24841
+ pruning: obj8({
24842
+ enabled: boolean,
24242
24843
  triggerPercent: integer,
24243
24844
  keepRecent: integer,
24244
24845
  minChars: integer,
24245
24846
  minSavePercent: integer
24246
- })
24247
- }),
24248
- sources: Type8.Object({
24249
- compaction: Type8.Object({ enabled: contextSource, triggerPercent: contextSource, keepRecentTokens: contextSource }),
24250
- pruning: Type8.Object({
24847
+ }, { open: true })
24848
+ }, { open: true }),
24849
+ sources: obj8({
24850
+ compaction: obj8({ enabled: contextSource, triggerPercent: contextSource, keepRecentTokens: contextSource }, { open: true }),
24851
+ pruning: obj8({
24251
24852
  enabled: contextSource,
24252
24853
  triggerPercent: contextSource,
24253
24854
  keepRecent: contextSource,
24254
24855
  minChars: contextSource,
24255
24856
  minSavePercent: contextSource
24256
- })
24257
- })
24857
+ }, { open: true })
24858
+ }, { open: true })
24859
+ }, { open: true });
24860
+ var nullable = (schema) => union4([schema, nil]);
24861
+ var ContextSettingsPatchSchema = obj8({
24862
+ compaction: opt8(obj8({
24863
+ enabled: opt8(nullable(boolean)),
24864
+ triggerPercent: opt8(nullable(integer)),
24865
+ keepRecentTokens: opt8(nullable(integer))
24866
+ })),
24867
+ pruning: opt8(obj8({
24868
+ enabled: opt8(nullable(boolean)),
24869
+ triggerPercent: opt8(nullable(integer)),
24870
+ keepRecent: opt8(nullable(integer)),
24871
+ minChars: opt8(nullable(integer)),
24872
+ minSavePercent: opt8(nullable(integer))
24873
+ }))
24258
24874
  });
24259
- var nullable = (schema) => Type8.Union([schema, nil]);
24260
- var ContextSettingsPatchSchema = Type8.Object({
24261
- compaction: Type8.Optional(Type8.Object({
24262
- enabled: Type8.Optional(nullable(bool)),
24263
- triggerPercent: Type8.Optional(nullable(integer)),
24264
- keepRecentTokens: Type8.Optional(nullable(integer))
24265
- }, { additionalProperties: false })),
24266
- pruning: Type8.Optional(Type8.Object({
24267
- enabled: Type8.Optional(nullable(bool)),
24268
- triggerPercent: Type8.Optional(nullable(integer)),
24269
- keepRecent: Type8.Optional(nullable(integer)),
24270
- minChars: Type8.Optional(nullable(integer)),
24271
- minSavePercent: Type8.Optional(nullable(integer))
24272
- }, { additionalProperties: false }))
24273
- }, { additionalProperties: false });
24274
24875
  var NekoContextSettings = contract("neko.context-settings", {
24275
24876
  version: 1,
24276
- input: Type8.Object({}, { additionalProperties: false }),
24877
+ input: obj8({}),
24277
24878
  variants: {
24278
24879
  ready: {
24279
24880
  state: { state: ContextSettingsStateSchema },
24280
24881
  methods: {
24281
24882
  set: rpc(ContextSettingsPatchSchema, nil),
24282
- reset: rpc(Type8.Object({}, { additionalProperties: false }), nil)
24883
+ reset: rpc(obj8({}), nil)
24884
+ }
24885
+ }
24886
+ }
24887
+ });
24888
+ var CodemodeSettingsStateSchema = obj8({
24889
+ revision: integer,
24890
+ effective: obj8({ mode: oneOf2([...CODEMODE_MODES]) }, { open: true }),
24891
+ source: oneOf2(["default", "config", "saved"])
24892
+ }, { open: true });
24893
+ var NekoCodemodeSettings = contract("neko.codemode-settings", {
24894
+ version: 1,
24895
+ input: obj8({}),
24896
+ variants: {
24897
+ ready: {
24898
+ state: { state: CodemodeSettingsStateSchema },
24899
+ methods: {
24900
+ set: rpc(obj8({ mode: oneOf2([...CODEMODE_MODES]) }), nil),
24901
+ reset: rpc(obj8({}), nil)
24283
24902
  }
24284
24903
  }
24285
24904
  }
24286
24905
  });
24287
- var defaultModelSource = Type8.Enum(["cli", "saved", "config", "default"]);
24288
- var DefaultModelStateSchema = Type8.Object({
24906
+ var defaultModelSource = oneOf2(["cli", "saved", "config", "default"]);
24907
+ var DefaultModelStateSchema = obj8({
24289
24908
  effective: model2,
24290
24909
  source: defaultModelSource
24291
- });
24910
+ }, { open: true });
24292
24911
  var NekoDefaultModel = contract("neko.default-model", {
24293
24912
  version: 1,
24294
- input: Type8.Object({}, { additionalProperties: false }),
24913
+ input: obj8({}),
24295
24914
  variants: {
24296
24915
  ready: {
24297
24916
  state: { state: DefaultModelStateSchema },
@@ -24302,74 +24921,76 @@ var NekoDefaultModel = contract("neko.default-model", {
24302
24921
  }
24303
24922
  }
24304
24923
  });
24305
- var FileCandidatesSchema = Type8.Object({
24306
- entries: Type8.Array(Type8.Object({ name: str, path: str, kind: Type8.Enum(["file", "directory"]) }), { maxItems: 100 }),
24307
- limited: bool,
24308
- incomplete: bool
24309
- });
24924
+ var FileCandidatesSchema = obj8({
24925
+ entries: arr6(obj8({ name: text3, path: text3, kind: oneOf2(["file", "directory"]) }, { open: true }), {
24926
+ maxItems: 100
24927
+ }),
24928
+ limited: boolean,
24929
+ incomplete: boolean
24930
+ }, { open: true });
24310
24931
  var NekoWorkspaceFiles = contract("neko.workspace-files", {
24311
24932
  version: 1,
24312
- input: Type8.Object({}, { additionalProperties: false }),
24933
+ input: obj8({}),
24313
24934
  variants: {
24314
24935
  ready: {
24315
24936
  methods: {
24316
- search: rpc(Type8.Object({ workspaceRoot: str, query: str }, { additionalProperties: false }), FileCandidatesSchema)
24937
+ search: rpc(obj8({ workspaceRoot: text3, query: text3 }), FileCandidatesSchema)
24317
24938
  }
24318
24939
  }
24319
24940
  }
24320
24941
  });
24321
- var marketSkill = Type8.Object({
24322
- name: str,
24323
- description: str,
24324
- category: str,
24325
- source: str,
24326
- license: str,
24327
- version: str,
24328
- sha256: str,
24942
+ var marketSkill = obj8({
24943
+ name: text3,
24944
+ description: text3,
24945
+ category: text3,
24946
+ source: text3,
24947
+ license: text3,
24948
+ version: text3,
24949
+ sha256: text3,
24329
24950
  sizeBytes: integer,
24330
- uploadedAt: str,
24331
- installed: Type8.Object({ project: bool, user: bool, builtin: bool }),
24332
- activeLevel: Type8.Union([level, nil])
24333
- });
24334
- var preview = Type8.Object({ body: str, files: Type8.Array(str) });
24335
- var scope = Type8.Enum(["user", "project"]);
24336
- var installResult = Type8.Object({ name: str, scope, path: str, shadowedBy: Type8.Optional(scope) });
24951
+ uploadedAt: text3,
24952
+ installed: obj8({ project: boolean, user: boolean, builtin: boolean }, { open: true }),
24953
+ activeLevel: union4([level, nil])
24954
+ }, { open: true });
24955
+ var preview = obj8({ body: text3, files: arr6(text3) }, { open: true });
24956
+ var scope = oneOf2(["user", "project"]);
24957
+ var installResult = obj8({ name: text3, scope, path: text3, shadowedBy: opt8(scope) }, { open: true });
24337
24958
  var NekoSkillMarket = contract("neko.skill-market", {
24338
24959
  version: 1,
24339
- input: Type8.Object({}, { additionalProperties: false }),
24960
+ input: obj8({}),
24340
24961
  variants: {
24341
24962
  ready: {
24342
24963
  methods: {
24343
- list: rpc(Type8.Object({ query: Type8.Optional(str), workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), Type8.Array(marketSkill)),
24344
- detail: rpc(Type8.Object({ name: str, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), marketSkill),
24345
- preview: rpc(Type8.Object({ name: str }, { additionalProperties: false }), preview),
24346
- install: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str), overwrite: Type8.Optional(bool) }, { additionalProperties: false }), installResult),
24347
- uninstall: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), nil)
24964
+ list: rpc(obj8({ query: opt8(text3), workspaceRoot: opt8(text3) }), arr6(marketSkill)),
24965
+ detail: rpc(obj8({ name: text3, workspaceRoot: opt8(text3) }), marketSkill),
24966
+ preview: rpc(obj8({ name: text3 }), preview),
24967
+ install: rpc(obj8({ name: text3, scope, workspaceRoot: opt8(text3), overwrite: opt8(boolean) }), installResult),
24968
+ uninstall: rpc(obj8({ name: text3, scope, workspaceRoot: opt8(text3) }), nil)
24348
24969
  }
24349
24970
  }
24350
24971
  }
24351
24972
  });
24352
- var ShellStatusSchema = Type8.Union([
24353
- Type8.Object({ status: Type8.Literal("ready"), path: str, source: Type8.Enum(["config", "auto"]) }),
24354
- Type8.Object({
24355
- status: Type8.Literal("missing"),
24356
- reason: Type8.Enum(["not_found", "invalid_config"]),
24357
- detail: Type8.Optional(str)
24358
- })
24973
+ var ShellStatusSchema = union4([
24974
+ obj8({ status: lit3("ready"), path: text3, source: oneOf2(["config", "auto"]) }, { open: true }),
24975
+ obj8({
24976
+ status: lit3("missing"),
24977
+ reason: oneOf2(["not_found", "invalid_config"]),
24978
+ detail: opt8(text3)
24979
+ }, { open: true })
24359
24980
  ]);
24360
- var ApplicationInfoSchema = Type8.Object({
24361
- runningVersion: str,
24981
+ var ApplicationInfoSchema = obj8({
24982
+ runningVersion: text3,
24362
24983
  installedVersion: nullableString,
24363
- protocolVersion: Type8.Integer({ minimum: 1 }),
24984
+ protocolVersion: int5({ minimum: 1 }),
24364
24985
  latestVersion: nullableString,
24365
- checkedAt: Type8.Union([integer, nil]),
24366
- checkState: Type8.Enum(["unknown", "checking", "ok", "failed", "development"]),
24367
- serverId: str,
24368
- shell: Type8.Optional(ShellStatusSchema)
24369
- });
24986
+ checkedAt: union4([integer, nil]),
24987
+ checkState: oneOf2(["unknown", "checking", "ok", "failed", "development"]),
24988
+ serverId: text3,
24989
+ shell: opt8(ShellStatusSchema)
24990
+ }, { open: true });
24370
24991
  var NekoApplicationInfo = contract("neko.application-info", {
24371
24992
  version: 1,
24372
- input: Type8.Object({}),
24993
+ input: obj8({}, { open: true }),
24373
24994
  variants: {
24374
24995
  ready: { state: { info: ApplicationInfoSchema }, methods: { check: rpc(nil, ApplicationInfoSchema) } }
24375
24996
  }
@@ -24383,7 +25004,7 @@ function isServerId(value) {
24383
25004
  // packages/gateway/src/mobile/admin.ts
24384
25005
  import { createHash, timingSafeEqual } from "crypto";
24385
25006
  import { ChordError } from "@earendil-works/chord";
24386
- import { Type as Type9 } from "typebox";
25007
+ import { arr as arr7, bool as bool4, int as int6, lit as lit4, nul as nul4, obj as obj9, opt as opt9, str as str9, union as union5 } from "@earendil-works/pi-schema";
24387
25008
 
24388
25009
  // packages/gateway/src/principal.ts
24389
25010
  var HOST_PRINCIPAL = { kind: "host" };
@@ -24712,62 +25333,56 @@ class MobileStore {
24712
25333
  }
24713
25334
 
24714
25335
  // packages/gateway/src/mobile/admin.ts
24715
- var listenerStateSchema = Type9.Union([
24716
- Type9.Object({ status: Type9.Literal("disabled") }),
24717
- Type9.Object({ status: Type9.Literal("starting") }),
24718
- Type9.Object({
24719
- status: Type9.Literal("listening"),
24720
- address: Type9.String(),
24721
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
24722
- bindAddress: Type9.Optional(Type9.String()),
24723
- mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
24724
- }),
24725
- Type9.Object({ status: Type9.Literal("error"), error: Type9.String() }),
24726
- Type9.Object({ status: Type9.Literal("stopping") })
25336
+ var listenerStateSchema = union5([
25337
+ obj9({ status: lit4("disabled") }, { open: true }),
25338
+ obj9({ status: lit4("starting") }, { open: true }),
25339
+ obj9({
25340
+ status: lit4("listening"),
25341
+ address: str9(),
25342
+ port: int6({ minimum: 1, maximum: 65535 }),
25343
+ bindAddress: opt9(str9()),
25344
+ mode: opt9(union5([lit4("direct"), lit4("wsl-relay")]))
25345
+ }, { open: true }),
25346
+ obj9({ status: lit4("error"), error: str9() }, { open: true }),
25347
+ obj9({ status: lit4("stopping") }, { open: true })
24727
25348
  ]);
24728
- var adminDeviceSchema = Type9.Object({
24729
- deviceId: Type9.String(),
24730
- name: Type9.String(),
24731
- pairedAt: Type9.Integer(),
24732
- lastSeenAt: Type9.Integer(),
24733
- online: Type9.Boolean()
24734
- });
25349
+ var adminDeviceSchema = obj9({
25350
+ deviceId: str9(),
25351
+ name: str9(),
25352
+ pairedAt: int6(),
25353
+ lastSeenAt: int6(),
25354
+ online: bool4()
25355
+ }, { open: true });
24735
25356
  var NekoMobileAdmin = contract("neko.mobile-admin", {
24736
25357
  version: 1,
24737
- input: Type9.Object({ token: Type9.String() }),
25358
+ input: obj9({ token: str9() }, { open: true }),
24738
25359
  variants: {
24739
25360
  admin: {
24740
25361
  methods: {
24741
- state: rpc(Type9.Null(), Type9.Object({
25362
+ state: rpc(nul4(), obj9({
24742
25363
  state: listenerStateSchema,
24743
- candidates: Type9.Array(Type9.String()),
24744
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
24745
- certificate: Type9.Union([
24746
- Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }),
24747
- Type9.Null()
24748
- ]),
24749
- devices: Type9.Array(adminDeviceSchema),
24750
- lastConnectedAddress: Type9.Union([Type9.String(), Type9.Null()]),
24751
- wsl: Type9.Optional(Type9.Union([
24752
- Type9.Object({ interop: Type9.Boolean(), error: Type9.Union([Type9.String(), Type9.Null()]) }),
24753
- Type9.Null()
24754
- ]))
24755
- })),
24756
- enable: rpc(Type9.Object({
24757
- address: Type9.String(),
24758
- port: Type9.Optional(Type9.Integer({ minimum: 1, maximum: 65535 }))
24759
- }), Type9.Object({
24760
- status: Type9.Literal("listening"),
24761
- address: Type9.String(),
24762
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
24763
- bindAddress: Type9.Optional(Type9.String()),
24764
- mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
24765
- })),
24766
- disable: rpc(Type9.Null(), listenerStateSchema),
24767
- createPairing: rpc(Type9.Null(), Type9.Object({ pairingText: Type9.String(), expiresAt: Type9.Integer() })),
24768
- cancelPairing: rpc(Type9.Null(), Type9.Null()),
24769
- revoke: rpc(Type9.Object({ deviceId: Type9.String({ minLength: 1 }) }), Type9.Object({ revoked: Type9.Boolean() })),
24770
- regenerateCertificate: rpc(Type9.Null(), Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }))
25364
+ candidates: arr7(str9()),
25365
+ port: int6({ minimum: 1, maximum: 65535 }),
25366
+ certificate: union5([obj9({ fingerprint: str9(), expiresAt: int6() }, { open: true }), nul4()]),
25367
+ devices: arr7(adminDeviceSchema),
25368
+ lastConnectedAddress: union5([str9(), nul4()]),
25369
+ wsl: opt9(union5([obj9({ interop: bool4(), error: union5([str9(), nul4()]) }, { open: true }), nul4()]))
25370
+ }, { open: true })),
25371
+ enable: rpc(obj9({
25372
+ address: str9(),
25373
+ port: opt9(int6({ minimum: 1, maximum: 65535 }))
25374
+ }, { open: true }), obj9({
25375
+ status: lit4("listening"),
25376
+ address: str9(),
25377
+ port: int6({ minimum: 1, maximum: 65535 }),
25378
+ bindAddress: opt9(str9()),
25379
+ mode: opt9(union5([lit4("direct"), lit4("wsl-relay")]))
25380
+ }, { open: true })),
25381
+ disable: rpc(nul4(), listenerStateSchema),
25382
+ createPairing: rpc(nul4(), obj9({ pairingText: str9(), expiresAt: int6() }, { open: true })),
25383
+ cancelPairing: rpc(nul4(), nul4()),
25384
+ revoke: rpc(obj9({ deviceId: str9({ minLength: 1 }) }, { open: true }), obj9({ revoked: bool4() }, { open: true })),
25385
+ regenerateCertificate: rpc(nul4(), obj9({ fingerprint: str9(), expiresAt: int6() }, { open: true }))
24771
25386
  }
24772
25387
  }
24773
25388
  }
@@ -24943,9 +25558,6 @@ import { ChordError as ChordError2 } from "@earendil-works/chord";
24943
25558
  var messageTime = (session) => session.lastMessageAt ?? session.createdAt;
24944
25559
  var byMessage = (a, b) => messageTime(b) - messageTime(a) || Number(b.id) - Number(a.id);
24945
25560
 
24946
- // packages/gateway/src/mobile/adapter.ts
24947
- import { Check as Check2 } from "typebox/value";
24948
-
24949
25561
  // packages/gateway/src/subagent-client.ts
24950
25562
  function subagentClient(connect) {
24951
25563
  let value;
@@ -25089,16 +25701,29 @@ function subagentClient(connect) {
25089
25701
  // packages/gateway/src/mobile/projection.ts
25090
25702
  import { createHash as createHash2 } from "crypto";
25091
25703
 
25704
+ // packages/gateway/src/mobile/protocol.ts
25705
+ import {
25706
+ arr as arr8,
25707
+ bool as bool5,
25708
+ compile as compile2,
25709
+ int as int7,
25710
+ lit as lit5,
25711
+ nul as nul5,
25712
+ num as num5,
25713
+ obj as obj10,
25714
+ opt as opt10,
25715
+ str as str10,
25716
+ union as union6
25717
+ } from "@earendil-works/pi-schema";
25718
+
25092
25719
  // packages/core/src/application-version.ts
25093
- var RUNNING_VERSION = "0.5.2";
25094
- var APPLICATION_PROTOCOL = 9;
25720
+ var RUNNING_VERSION = "0.5.4";
25721
+ var APPLICATION_PROTOCOL = 10;
25095
25722
  function stableVersion(value) {
25096
25723
  return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
25097
25724
  }
25098
25725
 
25099
25726
  // packages/gateway/src/mobile/protocol.ts
25100
- import { Type as Type10 } from "typebox";
25101
- import { Check } from "typebox/value";
25102
25727
  var MOBILE_LIMITS = {
25103
25728
  maxFrameBytes: 262144,
25104
25729
  maxPayloadBytes: 131072,
@@ -25122,14 +25747,14 @@ var MOBILE_CAPABILITIES = [
25122
25747
  var MOBILE_CLOSE_REVOKED = 4401;
25123
25748
  var MOBILE_CLOSE_INCOMPATIBLE = 4402;
25124
25749
  var MOBILE_CLOSE_SLOW_CONSUMER = 4408;
25125
- var MobileHelloParams = Type10.Object({
25126
- apiVersions: Type10.Array(Type10.Integer(), { minItems: 1 }),
25127
- app: Type10.Object({
25128
- version: Type10.String({ minLength: 1, maxLength: 64 }),
25129
- platform: Type10.String({ minLength: 1, maxLength: 64 }),
25130
- build: Type10.String({ maxLength: 128 })
25131
- })
25132
- });
25750
+ var MobileHelloParams = obj10({
25751
+ apiVersions: arr8(int7(), { minItems: 1 }),
25752
+ app: obj10({
25753
+ version: str10({ minLength: 1, maxLength: 64 }),
25754
+ platform: str10({ minLength: 1, maxLength: 64 }),
25755
+ build: str10({ maxLength: 128 })
25756
+ }, { open: true })
25757
+ }, { open: true });
25133
25758
  function asJsonRpcRequest(value) {
25134
25759
  if (typeof value !== "object" || value === null)
25135
25760
  return;
@@ -25142,8 +25767,18 @@ function asJsonRpcRequest(value) {
25142
25767
  return;
25143
25768
  return { jsonrpc: "2.0", id: record.id, method: record.method, params: record.params };
25144
25769
  }
25770
+ var validators = new WeakMap;
25771
+ function checkSchema(schema, value) {
25772
+ let validator = validators.get(schema);
25773
+ if (!validator) {
25774
+ validator = compile2(schema);
25775
+ validators.set(schema, validator);
25776
+ }
25777
+ return validator.check(value);
25778
+ }
25779
+ var helloParams = compile2(MobileHelloParams);
25145
25780
  function isMobileHelloParams(value) {
25146
- return Check(MobileHelloParams, value);
25781
+ return helloParams.check(value);
25147
25782
  }
25148
25783
  function negotiateApiVersion(clientVersions) {
25149
25784
  let best;
@@ -25167,462 +25802,444 @@ function buildHelloResult(serverId, serverName, apiVersion) {
25167
25802
  limits: MOBILE_LIMITS
25168
25803
  };
25169
25804
  }
25170
- var opt = Type10.Optional;
25171
- var str2 = Type10.String();
25172
- var int = Type10.Integer({ minimum: 0 });
25173
- var nullable2 = (schema) => Type10.Union([schema, Type10.Null()]);
25174
- var MobileContentRefSchema = Type10.Object({
25175
- sessionId: str2,
25176
- agentId: opt(str2),
25177
- key: str2,
25178
- field: str2,
25179
- digest: str2,
25180
- bytes: int
25181
- });
25182
- var MobileImageRefSchema = Type10.Object({
25183
- name: str2,
25184
- mimeType: str2,
25185
- width: int,
25186
- height: int,
25187
- bytes: int,
25188
- entryId: int,
25189
- contentIndex: int
25190
- });
25191
- var planStatus = Type10.Union([
25192
- Type10.Literal("awaiting_approval"),
25193
- Type10.Literal("approved"),
25194
- Type10.Literal("revision_requested"),
25195
- Type10.Literal("superseded"),
25196
- Type10.Literal("cancelled")
25805
+ var text4 = str10();
25806
+ var count = int7({ minimum: 0 });
25807
+ var nullable2 = (schema) => union6([schema, nul5()]);
25808
+ var MobileContentRefSchema = obj10({
25809
+ sessionId: text4,
25810
+ agentId: opt10(text4),
25811
+ key: text4,
25812
+ field: text4,
25813
+ digest: text4,
25814
+ bytes: count
25815
+ }, { open: true });
25816
+ var MobileImageRefSchema = obj10({
25817
+ name: text4,
25818
+ mimeType: text4,
25819
+ width: count,
25820
+ height: count,
25821
+ bytes: count,
25822
+ entryId: count,
25823
+ contentIndex: count
25824
+ }, { open: true });
25825
+ var planStatus = union6([
25826
+ lit5("awaiting_approval"),
25827
+ lit5("approved"),
25828
+ lit5("revision_requested"),
25829
+ lit5("superseded"),
25830
+ lit5("cancelled")
25197
25831
  ]);
25198
- var questionStatus2 = Type10.Union([
25199
- Type10.Literal("pending"),
25200
- Type10.Literal("answered"),
25201
- Type10.Literal("skipped"),
25202
- Type10.Literal("cancelled")
25203
- ]);
25204
- var MobileToolInteractionSchema = Type10.Union([
25205
- Type10.Object({
25206
- kind: Type10.Literal("ask_user"),
25207
- id: int,
25208
- question: str2,
25209
- multiple: Type10.Boolean(),
25210
- options: Type10.Array(Type10.Object({ id: str2, label: str2, recommended: opt(Type10.Boolean()) })),
25832
+ var questionStatus2 = union6([lit5("pending"), lit5("answered"), lit5("skipped"), lit5("cancelled")]);
25833
+ var MobileToolInteractionSchema = union6([
25834
+ obj10({
25835
+ kind: lit5("ask_user"),
25836
+ id: count,
25837
+ question: text4,
25838
+ multiple: bool5(),
25839
+ options: arr8(obj10({ id: text4, label: text4, recommended: opt10(bool5()) }, { open: true })),
25211
25840
  status: questionStatus2,
25212
- optionIds: opt(Type10.Array(str2)),
25213
- labels: opt(Type10.Array(str2)),
25214
- text: opt(nullable2(str2))
25215
- }),
25216
- Type10.Object({
25217
- kind: Type10.Literal("todo"),
25218
- action: Type10.Union([Type10.Literal("list"), Type10.Literal("replace")]),
25219
- revision: int,
25220
- completed: int,
25221
- total: int
25222
- }),
25223
- Type10.Object({
25224
- kind: Type10.Literal("submit_plan"),
25225
- version: str2,
25226
- title: str2,
25227
- steps: Type10.Array(str2),
25228
- note: str2
25229
- })
25841
+ optionIds: opt10(arr8(text4)),
25842
+ labels: opt10(arr8(text4)),
25843
+ text: opt10(nullable2(text4))
25844
+ }, { open: true }),
25845
+ obj10({
25846
+ kind: lit5("todo"),
25847
+ action: union6([lit5("list"), lit5("replace")]),
25848
+ revision: count,
25849
+ completed: count,
25850
+ total: count
25851
+ }, { open: true }),
25852
+ obj10({
25853
+ kind: lit5("submit_plan"),
25854
+ version: text4,
25855
+ title: text4,
25856
+ steps: arr8(text4),
25857
+ note: text4
25858
+ }, { open: true })
25230
25859
  ]);
25231
- var MobileToolItemSchema = Type10.Object({
25232
- callId: str2,
25233
- name: str2,
25234
- summary: str2,
25235
- status: Type10.Union([Type10.Literal("running"), Type10.Literal("done"), Type10.Literal("error")]),
25236
- detail: opt(str2),
25237
- output: opt(str2),
25238
- result: opt(str2),
25239
- diff: opt(str2),
25240
- diffAdded: opt(Type10.Number()),
25241
- diffRemoved: opt(Type10.Number()),
25242
- diffTruncated: opt(Type10.Boolean()),
25243
- durationMs: opt(Type10.Number()),
25244
- note: opt(str2),
25245
- interaction: opt(MobileToolInteractionSchema)
25246
- });
25247
- var MobileChatItemSchema = Type10.Object({
25248
- key: str2,
25249
- type: Type10.Union([
25250
- Type10.Literal("user"),
25251
- Type10.Literal("assistant"),
25252
- Type10.Literal("tool"),
25253
- Type10.Literal("error"),
25254
- Type10.Literal("system")
25255
- ]),
25256
- text: opt(str2),
25257
- thinking: opt(str2),
25258
- note: opt(str2),
25259
- error: opt(str2),
25260
- skill: opt(str2),
25261
- jobId: opt(str2),
25262
- tool: opt(MobileToolItemSchema),
25263
- images: opt(Type10.Array(MobileImageRefSchema)),
25264
- refs: opt(Type10.Array(MobileContentRefSchema))
25265
- });
25266
- var MobileStreamingSchema = Type10.Object({
25267
- text: str2,
25268
- thinking: str2,
25269
- textOmitted: int,
25270
- thinkingOmitted: int,
25271
- toolCalls: Type10.Array(Type10.Object({
25272
- callId: str2,
25273
- name: str2,
25274
- summary: str2,
25275
- status: Type10.Union([Type10.Literal("running"), Type10.Literal("done")])
25276
- }))
25277
- });
25278
- var MobileUsageSchema = Type10.Object({
25279
- input: int,
25280
- output: int,
25281
- cacheRead: int,
25282
- cacheWrite: int,
25283
- totalTokens: int,
25284
- cost: Type10.Number({ minimum: 0 })
25285
- });
25286
- var MobileSubagentRoundSchema = Type10.Object({
25287
- taskId: int,
25288
- callId: str2,
25289
- agentId: str2,
25290
- round: opt(int),
25291
- role: str2,
25292
- title: str2,
25293
- status: Type10.Union([
25294
- Type10.Literal("queued"),
25295
- Type10.Literal("running"),
25296
- Type10.Literal("completed"),
25297
- Type10.Literal("failed"),
25298
- Type10.Literal("cancelled")
25299
- ]),
25300
- approvalCount: int,
25301
- readonly: Type10.Boolean(),
25302
- createdAt: Type10.Number(),
25303
- endedAt: nullable2(Type10.Number()),
25304
- usage: opt(MobileUsageSchema)
25305
- });
25306
- var MobileAgentSchema = Type10.Object({
25307
- agentId: str2,
25308
- role: str2,
25309
- title: str2,
25310
- status: Type10.Union([Type10.Literal("idle"), Type10.Literal("queued"), Type10.Literal("running")]),
25311
- activeRoundId: nullable2(int),
25312
- lastRoundId: int,
25313
- roundCount: int,
25314
- approvalCount: int
25315
- });
25316
- var MobileQueuedSchema = Type10.Object({
25317
- id: int,
25318
- mode: Type10.Union([Type10.Literal("steer"), Type10.Literal("followUp")]),
25319
- preview: Type10.String(),
25320
- images: int
25321
- });
25322
- var MobileChatStatusSchema = Type10.Object({
25323
- busy: Type10.Boolean(),
25324
- queued: opt(Type10.Array(MobileQueuedSchema)),
25325
- todos: opt(Type10.Object({ revision: int, count: int, completed: int })),
25326
- questions: opt(Type10.Object({ revision: int, pendingCount: int })),
25327
- plan: opt(Type10.Object({ revision: int, pending: Type10.Boolean(), version: opt(Type10.String({ minLength: 1 })) })),
25328
- status: nullable2(str2),
25860
+ var MobileCodemodeSchema = obj10({
25861
+ calls: arr8(obj10({
25862
+ id: count,
25863
+ name: text4,
25864
+ args: str10({ maxLength: CODEMODE_VIEW_LIMITS.argsChars }),
25865
+ status: union6([lit5("running"), lit5("ok"), lit5("error"), lit5("cancelled")]),
25866
+ durationMs: opt10(num5()),
25867
+ error: opt10(str10({ maxLength: CODEMODE_VIEW_LIMITS.errorChars }))
25868
+ }, { open: true }), { maxItems: CODEMODE_VIEW_LIMITS.maxCalls }),
25869
+ omitted: count,
25870
+ fullOutputPath: opt10(text4)
25871
+ }, { open: true });
25872
+ var MobileToolItemSchema = obj10({
25873
+ callId: text4,
25874
+ name: text4,
25875
+ summary: text4,
25876
+ status: union6([lit5("running"), lit5("done"), lit5("error")]),
25877
+ detail: opt10(text4),
25878
+ output: opt10(text4),
25879
+ result: opt10(text4),
25880
+ diff: opt10(text4),
25881
+ diffAdded: opt10(num5()),
25882
+ diffRemoved: opt10(num5()),
25883
+ diffTruncated: opt10(bool5()),
25884
+ durationMs: opt10(num5()),
25885
+ note: opt10(text4),
25886
+ interaction: opt10(MobileToolInteractionSchema),
25887
+ codemode: opt10(MobileCodemodeSchema)
25888
+ }, { open: true });
25889
+ var MobileChatItemSchema = obj10({
25890
+ key: text4,
25891
+ type: union6([lit5("user"), lit5("assistant"), lit5("tool"), lit5("error"), lit5("system")]),
25892
+ text: opt10(text4),
25893
+ thinking: opt10(text4),
25894
+ note: opt10(text4),
25895
+ error: opt10(text4),
25896
+ skill: opt10(text4),
25897
+ jobId: opt10(text4),
25898
+ tool: opt10(MobileToolItemSchema),
25899
+ images: opt10(arr8(MobileImageRefSchema)),
25900
+ refs: opt10(arr8(MobileContentRefSchema))
25901
+ }, { open: true });
25902
+ var MobileStreamingSchema = obj10({
25903
+ text: text4,
25904
+ thinking: text4,
25905
+ textOmitted: count,
25906
+ thinkingOmitted: count,
25907
+ toolCalls: arr8(obj10({
25908
+ callId: text4,
25909
+ name: text4,
25910
+ summary: text4,
25911
+ status: union6([lit5("running"), lit5("done")])
25912
+ }, { open: true }))
25913
+ }, { open: true });
25914
+ var MobileUsageSchema = obj10({
25915
+ input: count,
25916
+ output: count,
25917
+ cacheRead: count,
25918
+ cacheWrite: count,
25919
+ totalTokens: count,
25920
+ cost: num5({ minimum: 0 })
25921
+ }, { open: true });
25922
+ var MobileSubagentRoundSchema = obj10({
25923
+ taskId: count,
25924
+ callId: text4,
25925
+ agentId: text4,
25926
+ round: opt10(count),
25927
+ role: text4,
25928
+ title: text4,
25929
+ status: union6([lit5("queued"), lit5("running"), lit5("completed"), lit5("failed"), lit5("cancelled")]),
25930
+ approvalCount: count,
25931
+ readonly: bool5(),
25932
+ createdAt: num5(),
25933
+ endedAt: nullable2(num5()),
25934
+ usage: opt10(MobileUsageSchema)
25935
+ }, { open: true });
25936
+ var MobileAgentSchema = obj10({
25937
+ agentId: text4,
25938
+ role: text4,
25939
+ title: text4,
25940
+ status: union6([lit5("idle"), lit5("queued"), lit5("running")]),
25941
+ activeRoundId: nullable2(count),
25942
+ lastRoundId: count,
25943
+ roundCount: count,
25944
+ approvalCount: count
25945
+ }, { open: true });
25946
+ var MobileQueuedSchema = obj10({
25947
+ id: count,
25948
+ mode: union6([lit5("steer"), lit5("followUp")]),
25949
+ preview: str10(),
25950
+ images: count
25951
+ }, { open: true });
25952
+ var mobileChatStatusProperties = {
25953
+ busy: bool5(),
25954
+ queued: opt10(arr8(MobileQueuedSchema)),
25955
+ todos: opt10(obj10({ revision: count, count, completed: count }, { open: true })),
25956
+ questions: opt10(obj10({ revision: count, pendingCount: count }, { open: true })),
25957
+ plan: opt10(obj10({ revision: count, pending: bool5(), version: opt10(str10({ minLength: 1 })) }, { open: true })),
25958
+ status: nullable2(text4),
25329
25959
  usage: MobileUsageSchema,
25330
- taskUsage: opt(MobileUsageSchema),
25331
- contextUsage: opt(Type10.Object({ tokens: nullable2(Type10.Number()), window: Type10.Number(), percent: nullable2(Type10.Number()) })),
25332
- agent: Type10.Object({ model: nullable2(str2), thinkingLevel: nullable2(str2) }),
25333
- subagents: Type10.Array(MobileSubagentRoundSchema),
25334
- agents: Type10.Array(MobileAgentSchema),
25335
- approvalCount: int,
25336
- compaction: opt(Type10.Object({ phase: str2, at: Type10.Number() })),
25337
- readonly: opt(Type10.Boolean())
25338
- });
25339
- var MobileChatSnapshotSchema = Type10.Object({
25340
- ...MobileChatStatusSchema.properties,
25341
- items: Type10.Array(MobileChatItemSchema),
25342
- historyBefore: nullable2(str2),
25960
+ taskUsage: opt10(MobileUsageSchema),
25961
+ contextUsage: opt10(obj10({ tokens: nullable2(num5()), window: num5(), percent: nullable2(num5()) }, { open: true })),
25962
+ agent: obj10({ model: nullable2(text4), thinkingLevel: nullable2(text4) }, { open: true }),
25963
+ subagents: arr8(MobileSubagentRoundSchema),
25964
+ agents: arr8(MobileAgentSchema),
25965
+ approvalCount: count,
25966
+ compaction: opt10(obj10({ phase: text4, at: num5() }, { open: true })),
25967
+ runFailure: opt10(obj10({ reason: text4, detail: opt10(text4) }, { open: true })),
25968
+ readonly: opt10(bool5())
25969
+ };
25970
+ var MobileChatStatusSchema = obj10(mobileChatStatusProperties, { open: true });
25971
+ var MobileChatSnapshotSchema = obj10({
25972
+ ...mobileChatStatusProperties,
25973
+ items: arr8(MobileChatItemSchema),
25974
+ historyBefore: nullable2(text4),
25343
25975
  streaming: nullable2(MobileStreamingSchema)
25344
- });
25345
- var MobileWorkspaceSchema = Type10.Object({ root: str2, name: str2 });
25346
- var MobileSessionSummarySchema = Type10.Object({
25347
- id: str2,
25348
- workspace: str2,
25349
- title: str2,
25350
- preview: str2,
25351
- lastUsedAt: Type10.Integer(),
25352
- lastMessageAt: Type10.Integer(),
25353
- archived: Type10.Boolean(),
25354
- awaitingAnswer: opt(Type10.Boolean()),
25355
- awaitingPlan: opt(Type10.Boolean())
25356
- });
25357
- var MobileApprovalSchema = Type10.Object({
25358
- id: int,
25359
- version: str2,
25360
- taskId: int,
25361
- agentId: opt(str2),
25362
- title: opt(str2),
25363
- command: str2,
25364
- cwd: str2,
25365
- timeoutSeconds: opt(Type10.Number({ minimum: 0 })),
25366
- requestedAt: Type10.Number()
25367
- });
25368
- var MobileTodoItemSchema = Type10.Object({
25369
- id: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
25370
- text: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
25371
- status: Type10.Union([Type10.Literal("pending"), Type10.Literal("in_progress"), Type10.Literal("completed")])
25372
- });
25373
- var MobileTodoListSchema = Type10.Object({
25374
- revision: int,
25375
- items: Type10.Array(MobileTodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
25376
- });
25377
- var MobileQuestionOptionSchema = Type10.Object({
25378
- id: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
25379
- label: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
25380
- description: opt(Type10.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
25381
- recommended: opt(Type10.Boolean())
25382
- });
25383
- var MobileQuestionSummarySchema = Type10.Object({
25384
- id: int,
25385
- version: str2,
25386
- preview: Type10.String({ maxLength: QUESTION_LIMITS.previewChars }),
25387
- multiple: Type10.Boolean(),
25388
- optionCount: int,
25389
- createdAt: Type10.Number()
25390
- });
25391
- var MobileQuestionListSchema = Type10.Object({
25392
- revision: int,
25393
- pending: Type10.Array(MobileQuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
25394
- });
25395
- var MobileQuestionSchema = Type10.Object({
25396
- id: int,
25397
- callId: str2,
25398
- version: str2,
25399
- question: Type10.String({ maxLength: QUESTION_LIMITS.questionChars }),
25400
- multiple: Type10.Boolean(),
25401
- options: Type10.Array(MobileQuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
25976
+ }, { open: true });
25977
+ var MobileWorkspaceSchema = obj10({ root: text4, name: text4 }, { open: true });
25978
+ var MobileSessionSummarySchema = obj10({
25979
+ id: text4,
25980
+ workspace: text4,
25981
+ title: text4,
25982
+ preview: text4,
25983
+ lastUsedAt: int7(),
25984
+ lastMessageAt: int7(),
25985
+ archived: bool5(),
25986
+ awaitingAnswer: opt10(bool5()),
25987
+ awaitingPlan: opt10(bool5())
25988
+ }, { open: true });
25989
+ var MobileApprovalSchema = obj10({
25990
+ id: count,
25991
+ version: text4,
25992
+ taskId: count,
25993
+ agentId: opt10(text4),
25994
+ title: opt10(text4),
25995
+ command: text4,
25996
+ cwd: text4,
25997
+ timeoutSeconds: opt10(num5({ minimum: 0 })),
25998
+ requestedAt: num5()
25999
+ }, { open: true });
26000
+ var MobileTodoItemSchema = obj10({
26001
+ id: str10({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
26002
+ text: str10({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
26003
+ status: union6([lit5("pending"), lit5("in_progress"), lit5("completed")])
26004
+ }, { open: true });
26005
+ var MobileTodoListSchema = obj10({
26006
+ revision: count,
26007
+ items: arr8(MobileTodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
26008
+ }, { open: true });
26009
+ var MobileQuestionOptionSchema = obj10({
26010
+ id: str10({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
26011
+ label: str10({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
26012
+ description: opt10(str10({ maxLength: QUESTION_LIMITS.descriptionChars })),
26013
+ recommended: opt10(bool5())
26014
+ }, { open: true });
26015
+ var MobileQuestionSummarySchema = obj10({
26016
+ id: count,
26017
+ version: text4,
26018
+ preview: str10({ maxLength: QUESTION_LIMITS.previewChars }),
26019
+ multiple: bool5(),
26020
+ optionCount: count,
26021
+ createdAt: num5()
26022
+ }, { open: true });
26023
+ var MobileQuestionListSchema = obj10({
26024
+ revision: count,
26025
+ pending: arr8(MobileQuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
26026
+ }, { open: true });
26027
+ var MobileQuestionSchema = obj10({
26028
+ id: count,
26029
+ callId: text4,
26030
+ version: text4,
26031
+ question: str10({ maxLength: QUESTION_LIMITS.questionChars }),
26032
+ multiple: bool5(),
26033
+ options: arr8(MobileQuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
25402
26034
  status: questionStatus2,
25403
- answer: nullable2(Type10.Object({
25404
- optionIds: Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
25405
- text: nullable2(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
25406
- })),
25407
- createdAt: Type10.Number(),
25408
- settledAt: nullable2(Type10.Number())
25409
- });
25410
- var MobileQuestionResponseSchema = Type10.Union([
25411
- Type10.Object({
25412
- kind: Type10.Literal("answer"),
25413
- optionIds: opt(Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
25414
- text: opt(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
25415
- }, { additionalProperties: false }),
25416
- Type10.Object({ kind: Type10.Literal("skip") }, { additionalProperties: false })
26035
+ answer: nullable2(obj10({
26036
+ optionIds: arr8(str10(), { maxItems: QUESTION_LIMITS.maxOptions }),
26037
+ text: nullable2(str10({ maxLength: QUESTION_LIMITS.textChars }))
26038
+ }, { open: true })),
26039
+ createdAt: num5(),
26040
+ settledAt: nullable2(num5())
26041
+ }, { open: true });
26042
+ var MobileQuestionResponseSchema = union6([
26043
+ obj10({
26044
+ kind: lit5("answer"),
26045
+ optionIds: opt10(arr8(str10(), { maxItems: QUESTION_LIMITS.maxOptions })),
26046
+ text: opt10(str10({ maxLength: QUESTION_LIMITS.textChars }))
26047
+ }),
26048
+ obj10({ kind: lit5("skip") })
25417
26049
  ]);
25418
- var MobilePlanSchema = Type10.Object({
25419
- version: Type10.String({ minLength: 1 }),
25420
- title: Type10.String({ maxLength: PLAN_LIMITS.titleChars }),
25421
- steps: Type10.Array(Type10.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
25422
- note: Type10.String({ maxLength: PLAN_LIMITS.noteChars }),
26050
+ var MobilePlanSchema = obj10({
26051
+ version: str10({ minLength: 1 }),
26052
+ title: str10({ maxLength: PLAN_LIMITS.titleChars }),
26053
+ steps: arr8(str10({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
26054
+ note: str10({ maxLength: PLAN_LIMITS.noteChars }),
25423
26055
  status: planStatus,
25424
- feedback: nullable2(Type10.String({ maxLength: PLAN_LIMITS.feedbackChars })),
25425
- createdAt: Type10.Number(),
25426
- settledAt: nullable2(Type10.Number())
25427
- });
25428
- var MobilePlanDecisionSchema = Type10.Union([
25429
- Type10.Object({ kind: Type10.Literal("approve") }, { additionalProperties: false }),
25430
- Type10.Object({ kind: Type10.Literal("revise"), feedback: Type10.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
26056
+ feedback: nullable2(str10({ maxLength: PLAN_LIMITS.feedbackChars })),
26057
+ createdAt: num5(),
26058
+ settledAt: nullable2(num5())
26059
+ }, { open: true });
26060
+ var MobilePlanDecisionSchema = union6([
26061
+ obj10({ kind: lit5("approve") }),
26062
+ obj10({ kind: lit5("revise"), feedback: str10({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) })
25431
26063
  ]);
25432
- var sessionListParams = Type10.Object({
25433
- workspace: str2,
25434
- query: opt(str2),
25435
- cursor: opt(str2)
25436
- });
25437
- var sessionListResult = Type10.Object({
25438
- sessions: Type10.Array(MobileSessionSummarySchema),
25439
- nextCursor: opt(str2)
25440
- });
25441
- var subscription = { subscriptionId: Type10.String({ minLength: 1 }) };
25442
- var pageResult = Type10.Object({ items: Type10.Array(MobileChatItemSchema), historyBefore: nullable2(str2) });
25443
- var pageParams = Type10.Object({
26064
+ var sessionListParams = obj10({
26065
+ workspace: text4,
26066
+ query: opt10(text4),
26067
+ cursor: opt10(text4)
26068
+ }, { open: true });
26069
+ var sessionListResult = obj10({
26070
+ sessions: arr8(MobileSessionSummarySchema),
26071
+ nextCursor: opt10(text4)
26072
+ }, { open: true });
26073
+ var subscription = { subscriptionId: str10({ minLength: 1 }) };
26074
+ var pageResult = obj10({ items: arr8(MobileChatItemSchema), historyBefore: nullable2(text4) }, { open: true });
26075
+ var pageParams = obj10({
25444
26076
  ...subscription,
25445
- before: Type10.String({ minLength: 1 }),
25446
- limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
25447
- });
25448
- var chunkResult = Type10.Object({ data: str2, nextOffset: int, total: int, done: Type10.Boolean() });
26077
+ before: str10({ minLength: 1 }),
26078
+ limit: opt10(int7({ minimum: 1, maximum: 50 }))
26079
+ }, { open: true });
26080
+ var chunkResultProperties = { data: text4, nextOffset: count, total: count, done: bool5() };
26081
+ var chunkResult = obj10(chunkResultProperties, { open: true });
25449
26082
  var MOBILE_API_V1_METHODS = {
25450
26083
  "server.hello": { params: MobileHelloParams, result: buildHelloResultSchema() },
25451
- "workspaces.list": { params: Type10.Null(), result: Type10.Object({ workspaces: Type10.Array(MobileWorkspaceSchema) }) },
26084
+ "workspaces.list": { params: nul5(), result: obj10({ workspaces: arr8(MobileWorkspaceSchema) }, { open: true }) },
25452
26085
  "sessions.list": { params: sessionListParams, result: sessionListResult },
25453
- "sessions.create": { params: Type10.Object({ workspace: str2 }), result: MobileSessionSummarySchema },
26086
+ "sessions.create": { params: obj10({ workspace: text4 }, { open: true }), result: MobileSessionSummarySchema },
25454
26087
  "chat.attach": {
25455
- params: Type10.Object({ sessionId: Type10.String({ minLength: 1 }) }),
25456
- result: Type10.Object({ subscriptionId: str2, sessionId: str2, snapshot: MobileChatSnapshotSchema })
26088
+ params: obj10({ sessionId: str10({ minLength: 1 }) }, { open: true }),
26089
+ result: obj10({ subscriptionId: text4, sessionId: text4, snapshot: MobileChatSnapshotSchema }, { open: true })
25457
26090
  },
25458
- "chat.detach": { params: Type10.Object(subscription), result: Type10.Null() },
26091
+ "chat.detach": { params: obj10(subscription, { open: true }), result: nul5() },
25459
26092
  "chat.history": { params: pageParams, result: pageResult },
25460
26093
  "chat.content": {
25461
- params: Type10.Object({ ref: MobileContentRefSchema, offset: int }),
26094
+ params: obj10({ ref: MobileContentRefSchema, offset: count }, { open: true }),
25462
26095
  result: chunkResult
25463
26096
  },
25464
26097
  "chat.submit": {
25465
- params: Type10.Object({
26098
+ params: obj10({
25466
26099
  ...subscription,
25467
- requestId: Type10.String({ minLength: 1, maxLength: 128 }),
25468
- text: Type10.String({ minLength: 1, maxLength: 65536 }),
25469
- mode: opt(Type10.Literal("steer"))
25470
- }),
25471
- result: Type10.Object({ accepted: Type10.Literal(true), queued: Type10.Boolean() })
26100
+ requestId: str10({ minLength: 1, maxLength: 128 }),
26101
+ text: str10({ minLength: 1, maxLength: 65536 }),
26102
+ mode: opt10(lit5("steer"))
26103
+ }, { open: true }),
26104
+ result: obj10({ accepted: lit5(true), queued: bool5() }, { open: true })
25472
26105
  },
25473
26106
  "chat.submissionStatus": {
25474
- params: Type10.Object({ ...subscription, requestId: Type10.String({ minLength: 1, maxLength: 128 }) }),
25475
- result: Type10.Object({ status: Type10.Union([Type10.Literal("accepted"), Type10.Literal("unknown")]) })
26107
+ params: obj10({ ...subscription, requestId: str10({ minLength: 1, maxLength: 128 }) }, { open: true }),
26108
+ result: obj10({ status: union6([lit5("accepted"), lit5("unknown")]) }, { open: true })
25476
26109
  },
25477
26110
  "chat.steer": {
25478
- params: Type10.Object({ ...subscription, id: int }),
25479
- result: Type10.Object({
25480
- result: Type10.Union([
25481
- Type10.Literal("steered"),
25482
- Type10.Literal("withdrawn"),
25483
- Type10.Literal("placed"),
25484
- Type10.Literal("settled"),
25485
- Type10.Literal("unknown")
25486
- ]),
25487
- id: opt(int),
25488
- text: opt(Type10.String()),
25489
- images: opt(int)
25490
- })
26111
+ params: obj10({ ...subscription, id: count }, { open: true }),
26112
+ result: obj10({
26113
+ result: union6([lit5("steered"), lit5("withdrawn"), lit5("placed"), lit5("settled"), lit5("unknown")]),
26114
+ id: opt10(count),
26115
+ text: opt10(str10()),
26116
+ images: opt10(count)
26117
+ }, { open: true })
25491
26118
  },
25492
26119
  "chat.withdraw": {
25493
- params: Type10.Object({ ...subscription, id: int }),
25494
- result: Type10.Object({
25495
- result: Type10.Union([
25496
- Type10.Literal("withdrawn"),
25497
- Type10.Literal("placed"),
25498
- Type10.Literal("settled"),
25499
- Type10.Literal("unknown")
25500
- ]),
25501
- text: opt(Type10.String()),
25502
- images: opt(int)
25503
- })
26120
+ params: obj10({ ...subscription, id: count }, { open: true }),
26121
+ result: obj10({
26122
+ result: union6([lit5("withdrawn"), lit5("placed"), lit5("settled"), lit5("unknown")]),
26123
+ text: opt10(str10()),
26124
+ images: opt10(count)
26125
+ }, { open: true })
25504
26126
  },
25505
- "chat.abort": { params: Type10.Object(subscription), result: Type10.Object({ accepted: Type10.Boolean() }) },
26127
+ "chat.abort": { params: obj10(subscription, { open: true }), result: obj10({ accepted: bool5() }, { open: true }) },
25506
26128
  "subagents.open": {
25507
- params: Type10.Object({ ...subscription, agentId: opt(Type10.String({ minLength: 1 })), taskId: opt(int) }),
25508
- result: Type10.Object({
25509
- subagentSubscriptionId: str2,
25510
- agentId: str2,
26129
+ params: obj10({ ...subscription, agentId: opt10(str10({ minLength: 1 })), taskId: opt10(count) }, { open: true }),
26130
+ result: obj10({
26131
+ subagentSubscriptionId: text4,
26132
+ agentId: text4,
25511
26133
  snapshot: MobileChatSnapshotSchema
25512
- })
26134
+ }, { open: true })
25513
26135
  },
25514
26136
  "subagents.history": {
25515
- params: Type10.Object({
25516
- subagentSubscriptionId: Type10.String({ minLength: 1 }),
25517
- before: Type10.String({ minLength: 1 }),
25518
- limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
25519
- }),
26137
+ params: obj10({
26138
+ subagentSubscriptionId: str10({ minLength: 1 }),
26139
+ before: str10({ minLength: 1 }),
26140
+ limit: opt10(int7({ minimum: 1, maximum: 50 }))
26141
+ }, { open: true }),
25520
26142
  result: pageResult
25521
26143
  },
25522
26144
  "subagents.close": {
25523
- params: Type10.Object({ subagentSubscriptionId: Type10.String({ minLength: 1 }) }),
25524
- result: Type10.Null()
26145
+ params: obj10({ subagentSubscriptionId: str10({ minLength: 1 }) }, { open: true }),
26146
+ result: nul5()
25525
26147
  },
25526
26148
  "subagents.stop": {
25527
- params: Type10.Object({ ...subscription, taskId: int }),
25528
- result: Type10.Object({ stopped: Type10.Boolean() })
26149
+ params: obj10({ ...subscription, taskId: count }, { open: true }),
26150
+ result: obj10({ stopped: bool5() }, { open: true })
25529
26151
  },
25530
26152
  "approvals.list": {
25531
- params: Type10.Object(subscription),
25532
- result: Type10.Object({ approvals: Type10.Array(MobileApprovalSchema) })
26153
+ params: obj10(subscription, { open: true }),
26154
+ result: obj10({ approvals: arr8(MobileApprovalSchema) }, { open: true })
25533
26155
  },
25534
- "approvals.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileApprovalSchema },
26156
+ "approvals.get": { params: obj10({ ...subscription, id: count }, { open: true }), result: MobileApprovalSchema },
25535
26157
  "approvals.decide": {
25536
- params: Type10.Object({
26158
+ params: obj10({
25537
26159
  ...subscription,
25538
- taskId: int,
25539
- id: int,
25540
- version: Type10.String({ minLength: 1 }),
25541
- decision: Type10.Union([Type10.Literal("allow"), Type10.Literal("deny")])
25542
- }),
25543
- result: Type10.Object({
25544
- status: Type10.Union([
25545
- Type10.Literal("pending"),
25546
- Type10.Literal("allowed"),
25547
- Type10.Literal("denied"),
25548
- Type10.Literal("cancelled")
25549
- ])
25550
- })
26160
+ taskId: count,
26161
+ id: count,
26162
+ version: str10({ minLength: 1 }),
26163
+ decision: union6([lit5("allow"), lit5("deny")])
26164
+ }, { open: true }),
26165
+ result: obj10({
26166
+ status: union6([lit5("pending"), lit5("allowed"), lit5("denied"), lit5("cancelled")])
26167
+ }, { open: true })
25551
26168
  },
25552
- "todos.get": { params: Type10.Object(subscription), result: MobileTodoListSchema },
25553
- "questions.list": { params: Type10.Object(subscription), result: MobileQuestionListSchema },
25554
- "questions.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileQuestionSchema },
26169
+ "todos.get": { params: obj10(subscription, { open: true }), result: MobileTodoListSchema },
26170
+ "questions.list": { params: obj10(subscription, { open: true }), result: MobileQuestionListSchema },
26171
+ "questions.get": { params: obj10({ ...subscription, id: count }, { open: true }), result: MobileQuestionSchema },
25555
26172
  "questions.respond": {
25556
- params: Type10.Object({
26173
+ params: obj10({
25557
26174
  ...subscription,
25558
- id: int,
25559
- version: Type10.String({ minLength: 1 }),
26175
+ id: count,
26176
+ version: str10({ minLength: 1 }),
25560
26177
  response: MobileQuestionResponseSchema
25561
- }),
26178
+ }, { open: true }),
25562
26179
  result: MobileQuestionSchema
25563
26180
  },
25564
26181
  "plan.get": {
25565
- params: Type10.Object({ ...subscription, version: Type10.String({ minLength: 1 }) }),
26182
+ params: obj10({ ...subscription, version: str10({ minLength: 1 }) }, { open: true }),
25566
26183
  result: MobilePlanSchema
25567
26184
  },
25568
26185
  "plan.respond": {
25569
- params: Type10.Object({
26186
+ params: obj10({
25570
26187
  ...subscription,
25571
- version: Type10.String({ minLength: 1 }),
26188
+ version: str10({ minLength: 1 }),
25572
26189
  decision: MobilePlanDecisionSchema
25573
- }),
26190
+ }, { open: true }),
25574
26191
  result: MobilePlanSchema
25575
26192
  },
25576
26193
  "plan.exit": {
25577
- params: Type10.Object(subscription),
25578
- result: Type10.Object({ mode: Type10.Literal("normal"), revision: int })
26194
+ params: obj10(subscription, { open: true }),
26195
+ result: obj10({ mode: lit5("normal"), revision: count }, { open: true })
25579
26196
  },
25580
26197
  "media.read": {
25581
- params: Type10.Object({
26198
+ params: obj10({
25582
26199
  ...subscription,
25583
- agentId: opt(Type10.String({ minLength: 1 })),
25584
- entryId: int,
25585
- contentIndex: int,
25586
- offset: int
25587
- }),
25588
- result: Type10.Object({ ...chunkResult.properties, mimeType: str2 })
26200
+ agentId: opt10(str10({ minLength: 1 })),
26201
+ entryId: count,
26202
+ contentIndex: count,
26203
+ offset: count
26204
+ }, { open: true }),
26205
+ result: obj10({ ...chunkResultProperties, mimeType: text4 }, { open: true })
25589
26206
  }
25590
26207
  };
25591
26208
  function buildHelloResultSchema() {
25592
- return Type10.Object({
25593
- apiVersion: Type10.Integer({ minimum: 1 }),
25594
- serverId: str2,
25595
- serverName: str2,
25596
- gatewayVersion: str2,
25597
- capabilities: Type10.Array(str2),
25598
- limits: Type10.Object({
25599
- maxFrameBytes: Type10.Integer(),
25600
- maxPayloadBytes: Type10.Integer(),
25601
- pageItems: Type10.Integer(),
25602
- chunkBytes: Type10.Integer(),
25603
- maxConcurrentRequests: Type10.Integer(),
25604
- maxSendBufferBytes: Type10.Integer()
25605
- })
25606
- });
26209
+ return obj10({
26210
+ apiVersion: int7({ minimum: 1 }),
26211
+ serverId: text4,
26212
+ serverName: text4,
26213
+ gatewayVersion: text4,
26214
+ capabilities: arr8(text4),
26215
+ limits: obj10({
26216
+ maxFrameBytes: int7(),
26217
+ maxPayloadBytes: int7(),
26218
+ pageItems: int7(),
26219
+ chunkBytes: int7(),
26220
+ maxConcurrentRequests: int7(),
26221
+ maxSendBufferBytes: int7()
26222
+ }, { open: true })
26223
+ }, { open: true });
25607
26224
  }
25608
26225
  var patchBody = {
25609
- seq: Type10.Integer({ minimum: 1 }),
25610
- upserts: Type10.Array(MobileChatItemSchema),
25611
- status: opt(MobileChatStatusSchema),
25612
- streaming: opt(nullable2(MobileStreamingSchema)),
25613
- streamingAppend: opt(Type10.Object({ text: opt(str2), thinking: opt(str2) }))
26226
+ seq: int7({ minimum: 1 }),
26227
+ upserts: arr8(MobileChatItemSchema),
26228
+ status: opt10(MobileChatStatusSchema),
26229
+ streaming: opt10(nullable2(MobileStreamingSchema)),
26230
+ streamingAppend: opt10(obj10({ text: opt10(text4), thinking: opt10(text4) }, { open: true }))
25614
26231
  };
25615
26232
  var MOBILE_API_V1_NOTIFICATIONS = {
25616
- "workspaces.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
25617
- "sessions.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
25618
- "chat.patch": Type10.Object({ subscriptionId: str2, ...patchBody }),
25619
- "chat.reset": Type10.Object({ subscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
25620
- "subagents.patch": Type10.Object({ subagentSubscriptionId: str2, ...patchBody }),
25621
- "subagents.reset": Type10.Object({ subagentSubscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
25622
- "approvals.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
25623
- "todos.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
25624
- "questions.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
25625
- "plan.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) })
26233
+ "workspaces.changed": obj10({ revision: int7({ minimum: 1 }) }, { open: true }),
26234
+ "sessions.changed": obj10({ revision: int7({ minimum: 1 }) }, { open: true }),
26235
+ "chat.patch": obj10({ subscriptionId: text4, ...patchBody }, { open: true }),
26236
+ "chat.reset": obj10({ subscriptionId: text4, seq: int7({ minimum: 1 }) }, { open: true }),
26237
+ "subagents.patch": obj10({ subagentSubscriptionId: text4, ...patchBody }, { open: true }),
26238
+ "subagents.reset": obj10({ subagentSubscriptionId: text4, seq: int7({ minimum: 1 }) }, { open: true }),
26239
+ "approvals.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
26240
+ "todos.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
26241
+ "questions.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
26242
+ "plan.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true })
25626
26243
  };
25627
26244
 
25628
26245
  // packages/gateway/src/mobile/projection.ts
@@ -25664,12 +26281,12 @@ function utf8Prefix(value, maxBytes) {
25664
26281
  function jsonBytes2(value) {
25665
26282
  return Buffer.byteLength(JSON.stringify(value), "utf8");
25666
26283
  }
25667
- var count = (value) => Math.max(0, Math.round(value ?? 0));
26284
+ var count2 = (value) => Math.max(0, Math.round(value ?? 0));
25668
26285
  function mapUsage(usage) {
25669
- const input = count(usage.input);
25670
- const output = count(usage.output);
25671
- const cacheRead = count(usage.cacheRead);
25672
- const cacheWrite = count(usage.cacheWrite);
26286
+ const input = count2(usage.input);
26287
+ const output = count2(usage.output);
26288
+ const cacheRead = count2(usage.cacheRead);
26289
+ const cacheWrite = count2(usage.cacheWrite);
25673
26290
  return {
25674
26291
  input,
25675
26292
  output,
@@ -25721,9 +26338,9 @@ function mapChatItem(raw, owner) {
25721
26338
  images: list.map((image) => ({
25722
26339
  name: image.name,
25723
26340
  mimeType: image.mimeType,
25724
- width: count(image.width),
25725
- height: count(image.height),
25726
- bytes: count(image.bytes),
26341
+ width: count2(image.width),
26342
+ height: count2(image.height),
26343
+ bytes: count2(image.bytes),
25727
26344
  entryId: image.entryId,
25728
26345
  contentIndex: image.contentIndex
25729
26346
  }))
@@ -25773,6 +26390,17 @@ function mapChatItem(raw, owner) {
25773
26390
  tool.note = raw.note;
25774
26391
  if (raw.interaction !== undefined)
25775
26392
  tool.interaction = raw.interaction;
26393
+ if (raw.codemode !== undefined) {
26394
+ tool.codemode = {
26395
+ calls: raw.codemode.calls.map(({ durationMs, error, ...call }) => ({
26396
+ ...call,
26397
+ ...durationMs === null ? {} : { durationMs },
26398
+ ...error === null ? {} : { error }
26399
+ })),
26400
+ omitted: raw.codemode.omitted,
26401
+ ...raw.codemode.fullOutputPath === null ? {} : { fullOutputPath: raw.codemode.fullOutputPath }
26402
+ };
26403
+ }
25776
26404
  item = { key: raw.key, type: "tool", tool, ...images(raw.images) };
25777
26405
  break;
25778
26406
  }
@@ -25799,7 +26427,7 @@ function mapRound(round, readonlyRound) {
25799
26427
  role: round.role,
25800
26428
  title: round.title,
25801
26429
  status: round.status,
25802
- approvalCount: count(round.approvalCount),
26430
+ approvalCount: count2(round.approvalCount),
25803
26431
  readonly: readonlyRound || round.historyThrough !== undefined,
25804
26432
  createdAt: round.createdAt,
25805
26433
  endedAt: round.endedAt,
@@ -25816,7 +26444,7 @@ function mapStatus(view, options = {}) {
25816
26444
  activeRoundId: agent.activeRoundId,
25817
26445
  lastRoundId: agent.lastRoundId,
25818
26446
  roundCount: agent.roundCount,
25819
- approvalCount: count(agent.approvalCount)
26447
+ approvalCount: count2(agent.approvalCount)
25820
26448
  }));
25821
26449
  const approvalCount = view.approvals !== undefined ? view.approvals.filter((approval) => approval.status === "pending").length : rounds.reduce((sum, round) => sum + round.approvalCount, 0);
25822
26450
  const latest = view.compaction?.latest;
@@ -25852,6 +26480,12 @@ function mapStatus(view, options = {}) {
25852
26480
  agents,
25853
26481
  approvalCount,
25854
26482
  ...latest ? { compaction: { phase: latest.phase, at: latest.at } } : {},
26483
+ ...view.runFailure === undefined ? {} : {
26484
+ runFailure: {
26485
+ reason: view.runFailure.reason,
26486
+ ...view.runFailure.detail === undefined ? {} : { detail: view.runFailure.detail }
26487
+ }
26488
+ },
25855
26489
  ...options.readonly === undefined ? {} : { readonly: options.readonly }
25856
26490
  };
25857
26491
  }
@@ -26294,12 +26928,12 @@ class MobileAdapter {
26294
26928
  this.sendError(id, -32601, "method not found", { code: "not_found" });
26295
26929
  return;
26296
26930
  }
26297
- if (!Check2(spec.params, params)) {
26931
+ if (!checkSchema(spec.params, params)) {
26298
26932
  this.sendError(id, -32602, "invalid params", { code: "invalid_params" });
26299
26933
  return;
26300
26934
  }
26301
26935
  const result = await this.invoke(method, params);
26302
- if (!Check2(spec.result, result)) {
26936
+ if (!checkSchema(spec.result, result)) {
26303
26937
  this.onError?.(new Error(`mobile result for ${method} does not match its schema`));
26304
26938
  this.sendError(id, -32603, "internal result mismatch", { code: "internal" });
26305
26939
  return;
@@ -28430,6 +29064,7 @@ async function removeGatewayPortFile(nekoDir, serverId) {
28430
29064
  // packages/gateway/src/server-services.ts
28431
29065
  import { randomUUID as randomUUID6 } from "crypto";
28432
29066
  import { ChordError as ChordError5, local as local3, method as method2 } from "@earendil-works/chord";
29067
+ import { nul as nul6, obj as obj13 } from "@earendil-works/pi-schema";
28433
29068
 
28434
29069
  // packages/core/src/application-updates.ts
28435
29070
  import { mkdir as mkdir3, readFile as readFile6, rm as rm3, writeFile as writeFile3 } from "fs/promises";
@@ -28578,9 +29213,6 @@ class UpdateChecker {
28578
29213
  }
28579
29214
  }
28580
29215
 
28581
- // packages/gateway/src/server-services.ts
28582
- import { Type as Type13 } from "typebox";
28583
-
28584
29216
  // packages/gateway/src/session-host.ts
28585
29217
  import { open as open3 } from "fs/promises";
28586
29218
  import { basename as basename3, extname } from "path";
@@ -28591,7 +29223,7 @@ import {
28591
29223
  } from "@earendil-works/chord";
28592
29224
  import { withAbortSignal as withAbortSignal4 } from "@earendil-works/chord/context";
28593
29225
  import { UsageDoc as UsageDoc4 } from "@earendil-works/pi-durable";
28594
- import { Type as Type12 } from "typebox";
29226
+ import { obj as obj12, str as str12 } from "@earendil-works/pi-schema";
28595
29227
 
28596
29228
  // packages/gateway/src/image-uploads.ts
28597
29229
  var IMAGE_CHUNK_BYTES = 256 * 1024;
@@ -28690,18 +29322,23 @@ class ImageUploads {
28690
29322
  // packages/gateway/src/subagent-host.ts
28691
29323
  import { ChordError as ChordError3, local, opaque } from "@earendil-works/chord";
28692
29324
  import { UsageDoc as UsageDoc3 } from "@earendil-works/pi-durable";
28693
- import { Type as Type11 } from "typebox";
29325
+ import { int as int8, obj as obj11, str as str11 } from "@earendil-works/pi-schema";
28694
29326
  async function openReader(runtime, childId, parentId, context, through, historyUsage) {
28695
29327
  const child = await runtime.harness.conversation(childId, context);
28696
29328
  if (!child)
28697
29329
  throw new ChordError3("gone", "Subagent unavailable");
28698
- const native = await child.viewState(context);
29330
+ const native = await child.watch(context);
29331
+ const listeners = new Set;
28699
29332
  if (through !== undefined)
28700
- native.dispose();
29333
+ await native.stop();
29334
+ else
29335
+ native.start(async () => {
29336
+ for (const listener of listeners)
29337
+ listener();
29338
+ });
28701
29339
  const project = (entries, streaming = true) => {
28702
29340
  const result = projectChatView({ ...native.value, entries: [...entries].sort((a, b) => a.id - b.id) }, "", {
28703
- models: runtime.listModels(),
28704
- contextEntries: native.value.entries
29341
+ models: runtime.listModels()
28705
29342
  });
28706
29343
  if (!streaming || through !== undefined) {
28707
29344
  result.streaming = null;
@@ -28750,8 +29387,10 @@ async function openReader(runtime, childId, parentId, context, through, historyU
28750
29387
  subscribe: (fn) => {
28751
29388
  if (through !== undefined)
28752
29389
  return () => {};
28753
- const stop = native.subscribe(() => fn(null));
28754
- const stopApprovals = runtime.harness.subscribeCommits(({ changes }) => {
29390
+ const listener = () => fn(null);
29391
+ listeners.add(listener);
29392
+ const stop = () => listeners.delete(listener);
29393
+ const stopApprovals = onCommits(runtime.harness, ({ changes }) => {
28755
29394
  if (changes.some((c) => c.type === "document" && ["app.neko.approvals", "app.neko.delegations", "app.neko.subagents"].includes(c.record.kind) && c.conversationId === parentId))
28756
29395
  fn(null);
28757
29396
  });
@@ -28810,20 +29449,23 @@ async function openReader(runtime, childId, parentId, context, through, historyU
28810
29449
  mimeType: image.mimeType
28811
29450
  };
28812
29451
  },
28813
- close: () => native.dispose()
29452
+ close: () => {
29453
+ listeners.clear();
29454
+ native.stop();
29455
+ }
28814
29456
  };
28815
29457
  }
28816
29458
  var ChildProjection = local("neko.subagent-projection", {
28817
29459
  version: 1,
28818
- input: Type11.Object({ parentId: Type11.Integer(), agentId: Type11.String() }),
29460
+ input: obj11({ parentId: int8(), agentId: str11() }, { open: true }),
28819
29461
  members: {
28820
- revision: Type11.Integer(),
29462
+ revision: int8(),
28821
29463
  reader: opaque("neko.subagent-reader@1")
28822
29464
  }
28823
29465
  });
28824
29466
  function installSubagentHost(host, runtime, context) {
28825
29467
  host.serve(ChildProjection, (input, _principal, self2) => {
28826
- const revision = self2.state("revision", Type11.Integer(), 0);
29468
+ const revision = self2.state("revision", int8(), 0);
28827
29469
  const reader = self2.resource("reader", {
28828
29470
  version: 1,
28829
29471
  async open() {
@@ -28864,7 +29506,7 @@ function installSubagentHost(host, runtime, context) {
28864
29506
  parentId: Number(input.sessionId),
28865
29507
  agentId: input.agentId ?? `round-${input.taskId}`
28866
29508
  });
28867
- const authorizedRevision = self2.state("authorizedRevision", Type11.Integer(), 0);
29509
+ const authorizedRevision = self2.state("authorizedRevision", int8(), 0);
28868
29510
  self2.on("authorizedView", shared.revision, async (value, ctx) => {
28869
29511
  try {
28870
29512
  target.value;
@@ -28917,7 +29559,7 @@ function installSubagentHost(host, runtime, context) {
28917
29559
  }
28918
29560
 
28919
29561
  // packages/gateway/src/session-host.ts
28920
- var OwnerInput = Type12.Object({});
29562
+ var OwnerInput = obj12({}, { open: true });
28921
29563
  var Owner = local2("neko.attachment-owner", {
28922
29564
  version: 1,
28923
29565
  input: OwnerInput,
@@ -28925,7 +29567,7 @@ var Owner = local2("neko.attachment-owner", {
28925
29567
  });
28926
29568
  var Projection = local2("neko.projection", {
28927
29569
  version: 1,
28928
- input: Type12.Object({ id: Type12.String() }),
29570
+ input: obj12({ id: str12() }, { open: true }),
28929
29571
  members: {
28930
29572
  view: ChatViewSchema,
28931
29573
  reader: opaque2("neko.projection-reader@1")
@@ -28945,20 +29587,12 @@ var emptyView = {
28945
29587
  var JOB_COMMAND_PREVIEW_CHARS = 1000;
28946
29588
  var clipJob = (job) => job.command.length > JOB_COMMAND_PREVIEW_CHARS ? { ...job, command: `${job.command.slice(0, JOB_COMMAND_PREVIEW_CHARS)}\u2026` } : job;
28947
29589
  async function openProjection(runtime, session, context) {
28948
- const view = await session.viewState();
28949
- const history = new Map;
28950
- let cursor;
28951
- do {
28952
- const page = await session.conversation.entries({ order: "ascending" }, 100, cursor, context);
28953
- for (const entry of page.items)
28954
- history.set(entry.id, entry);
28955
- cursor = page.next;
28956
- } while (cursor);
29590
+ const view = await session.follow();
28957
29591
  let changed;
28958
29592
  const stop = view.subscribe(() => changed?.(null));
28959
29593
  const stopMetadata = runtime.onDirectoryChange(() => changed?.(null));
28960
29594
  let childIds = new Set;
28961
- const stopChildren = runtime.harness.subscribeCommits(({ changes }) => {
29595
+ const stopChildren = onCommits(runtime.harness, ({ changes }) => {
28962
29596
  if (changes.some((change) => change.type === "entry" && change.value.kind === "pi.tool-result" && childIds.has(change.value.conversationId) || change.type === "document" && ([
28963
29597
  "app.neko.delegations",
28964
29598
  "app.neko.approvals",
@@ -28979,12 +29613,9 @@ async function openProjection(runtime, session, context) {
28979
29613
  };
28980
29614
  },
28981
29615
  async project() {
28982
- for (const entry of view.value.entries)
28983
- history.set(entry.id, entry);
28984
29616
  const [naming, compaction] = await Promise.all([runtime.namingUsage(session.id), session.compactionRecord()]);
28985
29617
  const projected = projectChatView({
28986
29618
  ...view.value,
28987
- entries: [...history.values()].sort((a, b) => a.id - b.id),
28988
29619
  docs: {
28989
29620
  ...view.value.docs,
28990
29621
  "pi.usage": mergeUsage(view.value.docs["pi.usage"] ?? {
@@ -28992,7 +29623,7 @@ async function openProjection(runtime, session, context) {
28992
29623
  tools: {}
28993
29624
  }, naming)
28994
29625
  }
28995
- }, "", { models: runtime.listModels(), contextEntries: view.value.entries, compaction });
29626
+ }, "", { models: runtime.listModels(), compaction });
28996
29627
  const jobs = (await session.jobs()).map(clipJob);
28997
29628
  const [todos, questions, plan] = await Promise.all([session.todos(), session.questions(), session.plan()]);
28998
29629
  const changes = await session.changeSummary().catch(() => {
@@ -29033,16 +29664,13 @@ async function openProjection(runtime, session, context) {
29033
29664
  },
29034
29665
  async entry(id, ctx) {
29035
29666
  ctx.abortSignal?.throwIfAborted();
29036
- for (const entry of view.value.entries)
29037
- history.set(entry.id, entry);
29038
- return history.get(id);
29667
+ return view.entry(id);
29039
29668
  },
29040
29669
  close() {
29041
29670
  stop();
29042
29671
  stopMetadata();
29043
29672
  stopChildren();
29044
29673
  view.dispose();
29045
- history.clear();
29046
29674
  }
29047
29675
  };
29048
29676
  }
@@ -29218,6 +29846,14 @@ function serveSessions(host, runtime, context) {
29218
29846
  await projection.value.session.abort();
29219
29847
  return null;
29220
29848
  },
29849
+ continueRun: async () => {
29850
+ try {
29851
+ await projection.value.session.continueRun();
29852
+ return null;
29853
+ } catch (error) {
29854
+ throw new ChordError4("invalid", error instanceof Error ? error.message : "Cannot continue");
29855
+ }
29856
+ },
29221
29857
  withdraw: ({ id }) => projection.value.session.withdraw(id),
29222
29858
  steer: ({ id }) => projection.value.session.steer(id),
29223
29859
  questionGet: async ({ id }) => await projection.value.session.question(id) ?? null,
@@ -30037,18 +30673,20 @@ async function listDrives() {
30037
30673
  // packages/gateway/src/server-services.ts
30038
30674
  var ServerState = local3("neko.server-state", {
30039
30675
  version: 1,
30040
- input: Type13.Object({}),
30676
+ input: obj13({}, { open: true }),
30041
30677
  members: {
30042
30678
  directory: DirectoryStateSchema,
30043
30679
  workspaces: WorkspaceStateSchema,
30044
30680
  credentials: CredentialStateSchema,
30045
30681
  searchSettings: SearchSettingsStateSchema,
30046
30682
  contextSettings: ContextSettingsStateSchema,
30683
+ codemodeSettings: CodemodeSettingsStateSchema,
30047
30684
  defaultModel: DefaultModelStateSchema,
30048
- refreshCredentials: method2(Type13.Null(), Type13.Null()),
30049
- refreshSearchSettings: method2(Type13.Null(), Type13.Null()),
30050
- refreshContextSettings: method2(Type13.Null(), Type13.Null()),
30051
- refreshDefaultModel: method2(Type13.Null(), Type13.Null())
30685
+ refreshCredentials: method2(nul6(), nul6()),
30686
+ refreshSearchSettings: method2(nul6(), nul6()),
30687
+ refreshContextSettings: method2(nul6(), nul6()),
30688
+ refreshCodemodeSettings: method2(nul6(), nul6()),
30689
+ refreshDefaultModel: method2(nul6(), nul6())
30052
30690
  }
30053
30691
  });
30054
30692
  async function createNekoServerServices(options) {
@@ -30138,12 +30776,13 @@ async function createNekoServerServices(options) {
30138
30776
  return [spec.id, spec.kind === "model" ? { ...status, enabled: enabled.has(spec.id) } : status];
30139
30777
  })));
30140
30778
  };
30141
- const [sessions, workspaces, statuses, search, contextSettings0, defaultModel0] = await Promise.all([
30779
+ const [sessions, workspaces, statuses, search, contextSettings0, codemodeSettings0, defaultModel0] = await Promise.all([
30142
30780
  list(),
30143
30781
  runtime.listWorkspaces(),
30144
30782
  readStatuses(),
30145
30783
  runtime.searchSettings(),
30146
30784
  runtime.contextSettings(),
30785
+ runtime.codemodeSettings(),
30147
30786
  runtime.defaultModel()
30148
30787
  ]);
30149
30788
  let mutationTail = Promise.resolve();
@@ -30161,6 +30800,10 @@ async function createNekoServerServices(options) {
30161
30800
  revision: 1,
30162
30801
  ...contextSettings0
30163
30802
  });
30803
+ const codemodeSettings = self2.state("codemodeSettings", CodemodeSettingsStateSchema, {
30804
+ revision: 1,
30805
+ ...codemodeSettings0
30806
+ });
30164
30807
  const defaultModel = self2.state("defaultModel", DefaultModelStateSchema, defaultModel0);
30165
30808
  const refreshContextSettings = async (_input, ctx) => {
30166
30809
  const next = await runtime.contextSettings();
@@ -30169,6 +30812,13 @@ async function createNekoServerServices(options) {
30169
30812
  contextSettings.replace(ctx, { revision: revision + 1, ...next });
30170
30813
  return null;
30171
30814
  };
30815
+ const refreshCodemodeSettings = async (_input, ctx) => {
30816
+ const next = await runtime.codemodeSettings();
30817
+ const { revision, ...current } = codemodeSettings.value;
30818
+ if (JSON.stringify(next) !== JSON.stringify(current))
30819
+ codemodeSettings.replace(ctx, { revision: revision + 1, ...next });
30820
+ return null;
30821
+ };
30172
30822
  const refreshDefaultModel = async (_input, ctx) => {
30173
30823
  const next = await runtime.defaultModel();
30174
30824
  if (JSON.stringify(next) !== JSON.stringify(defaultModel.value))
@@ -30182,7 +30832,7 @@ async function createNekoServerServices(options) {
30182
30832
  searchSettings.replace(ctx, { revision: current.revision + 1, ...next });
30183
30833
  return null;
30184
30834
  };
30185
- self2.on("directory", { subscribe: runtime.harness.subscribeCommits.bind(runtime.harness) }, async (event, ctx) => {
30835
+ self2.on("directory", { subscribe: (listener) => onCommits(runtime.harness, listener) }, async (event, ctx) => {
30186
30836
  if (!event.changes.some((change) => change.type === "conversation" || change.type === "document" && [
30187
30837
  "app.neko.session-metadata",
30188
30838
  "app.neko.activity",
@@ -30204,6 +30854,7 @@ async function createNekoServerServices(options) {
30204
30854
  credentials,
30205
30855
  searchSettings,
30206
30856
  contextSettings,
30857
+ codemodeSettings,
30207
30858
  defaultModel,
30208
30859
  async refreshCredentials(_input, ctx) {
30209
30860
  credentials.replace(ctx, {
@@ -30214,6 +30865,7 @@ async function createNekoServerServices(options) {
30214
30865
  },
30215
30866
  refreshSearchSettings,
30216
30867
  refreshContextSettings,
30868
+ refreshCodemodeSettings,
30217
30869
  refreshDefaultModel
30218
30870
  };
30219
30871
  }, { lifetime: "host" });
@@ -30280,6 +30932,21 @@ async function createNekoServerServices(options) {
30280
30932
  })
30281
30933
  };
30282
30934
  });
30935
+ host.serve(NekoCodemodeSettings, (_input, _principal, self2) => {
30936
+ const hub = shared(self2);
30937
+ return {
30938
+ variant: "ready",
30939
+ state: hub.codemodeSettings,
30940
+ set: (input, ctx) => serialize(async () => {
30941
+ await runtime.setCodemodeMode(input.mode);
30942
+ return hub.refreshCodemodeSettings(null, ctx);
30943
+ }),
30944
+ reset: (_input, ctx) => serialize(async () => {
30945
+ await runtime.resetCodemodeMode();
30946
+ return hub.refreshCodemodeSettings(null, ctx);
30947
+ })
30948
+ };
30949
+ });
30283
30950
  host.serve(NekoDefaultModel, (_input, _principal, self2) => {
30284
30951
  const hub = shared(self2);
30285
30952
  return {
@@ -30608,6 +31275,8 @@ async function startGateway(options = {}) {
30608
31275
  ...options.apiKey !== undefined ? { apiKey: options.apiKey } : {},
30609
31276
  ...searchProvider !== undefined ? { searchProvider } : {},
30610
31277
  ...fileConfig.context !== undefined ? { contextSettings: fileConfig.context } : {},
31278
+ ...fileConfig.codemode !== undefined ? { codemodeSettings: fileConfig.codemode } : {},
31279
+ ...fileConfig.maxTurns !== undefined ? { maxTurns: fileConfig.maxTurns } : {},
30611
31280
  ...fileConfig.defaultModel !== undefined ? { model: fileConfig.defaultModel } : {},
30612
31281
  ...options.model !== undefined ? { modelOverride: options.model } : {},
30613
31282
  thinkingLevel: options.thinkingLevel ?? fileConfig.thinkingLevel ?? "high",
@@ -30733,7 +31402,7 @@ async function bindGateway(runtime, options) {
30733
31402
  }
30734
31403
  };
30735
31404
  runtime.closed.then(async (result) => {
30736
- if (result.reason === "failed") {
31405
+ if (result.kind === "failed") {
30737
31406
  options.onError?.(new Error("Gateway storage failed"));
30738
31407
  await gateway.close();
30739
31408
  }