neko-harness 0.5.3 → 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/neko.js CHANGED
@@ -80,7 +80,7 @@ function updateAction(input) {
80
80
  return "install";
81
81
  return "none";
82
82
  }
83
- var RUNNING_VERSION = "0.5.3", APPLICATION_PROTOCOL = 10, ApplicationProtocolError;
83
+ var RUNNING_VERSION = "0.5.4", APPLICATION_PROTOCOL = 10, ApplicationProtocolError;
84
84
  var init_application_version = __esm(() => {
85
85
  ApplicationProtocolError = class ApplicationProtocolError extends Error {
86
86
  constructor() {
@@ -440,6 +440,12 @@ var init_codemode_mode = __esm(() => {
440
440
  };
441
441
  });
442
442
 
443
+ // packages/core/src/commits.ts
444
+ function onCommits(session, listener) {
445
+ const observation = session.observeCommits({ advance: (publication) => listener(publication), end: () => {} });
446
+ return () => observation.detach();
447
+ }
448
+
443
449
  // packages/core/src/context-limits.ts
444
450
  var PERCENT, CONTEXT_FIELD_LIMITS, PRUNING_DEFAULTS;
445
451
  var init_context_limits = __esm(() => {
@@ -709,6 +715,10 @@ function validate(config, source) {
709
715
  if (level !== undefined && typeof level !== "string") {
710
716
  throw new Error(`neko config: ${source}: "thinkingLevel" must be a string.`);
711
717
  }
718
+ const maxTurns = config.maxTurns;
719
+ if (maxTurns !== undefined && !(Number.isSafeInteger(maxTurns) && maxTurns >= 1)) {
720
+ throw new Error(`neko config: ${source}: "maxTurns" must be an integer of 1 or more.`);
721
+ }
712
722
  const storagePath = config.storagePath;
713
723
  if (storagePath !== undefined && typeof storagePath !== "string") {
714
724
  throw new Error(`neko config: ${source}: "storagePath" must be a string.`);
@@ -786,6 +796,68 @@ var init_config = __esm(() => {
786
796
  ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g;
787
797
  });
788
798
 
799
+ // packages/core/src/conversation-follow.ts
800
+ import {
801
+ ConversationTranscript
802
+ } from "@earendil-works/pi-durable";
803
+ async function followConversation(conversation, context) {
804
+ const transcript = new ConversationTranscript((query, limit, ctx) => conversation.entries(query, limit, undefined, ctx), { pageSize: PAGE_SIZE });
805
+ const watch = await conversation.watch(context);
806
+ const listeners = new Set;
807
+ let byId;
808
+ let value;
809
+ const complete = async (view) => {
810
+ while (view.items[0]?.kind === "earlier") {
811
+ const before = view.items;
812
+ view = await transcript.loadEarlier(context);
813
+ if (view.items === before)
814
+ break;
815
+ }
816
+ for (let gap = view.items.find((item) => item.kind === "gap");gap; ) {
817
+ const before = view.items;
818
+ view = await transcript.fillGap(gap, context);
819
+ if (view.items === before)
820
+ break;
821
+ gap = view.items.find((item) => item.kind === "gap");
822
+ }
823
+ const { items, ...rest } = view;
824
+ byId = undefined;
825
+ return { ...rest, entries: items.flatMap((item) => item.kind === "entry" ? [item.entry] : []) };
826
+ };
827
+ try {
828
+ value = await complete(transcript.merge(watch.value));
829
+ } catch (error) {
830
+ await watch.stop();
831
+ throw error;
832
+ }
833
+ watch.start(async (call) => {
834
+ if (call.kind !== "value")
835
+ return;
836
+ value = await complete(transcript.merge(call.value));
837
+ for (const listener of listeners)
838
+ listener(value);
839
+ });
840
+ return {
841
+ get value() {
842
+ return value;
843
+ },
844
+ subscribe(listener) {
845
+ listeners.add(listener);
846
+ return () => listeners.delete(listener);
847
+ },
848
+ entry(id) {
849
+ byId ??= new Map(value.entries.map((entry) => [entry.id, entry]));
850
+ return byId.get(id);
851
+ },
852
+ dispose() {
853
+ listeners.clear();
854
+ watch.stop();
855
+ }
856
+ };
857
+ }
858
+ var PAGE_SIZE = 200;
859
+ var init_conversation_follow = () => {};
860
+
789
861
  // packages/core/src/credential-providers.ts
790
862
  function isModelProvider(id) {
791
863
  return MODEL_PROVIDERS.some((item) => item.id === id);
@@ -1356,7 +1428,7 @@ function projectChatView(view, modelLabel, options = {}) {
1356
1428
  const agent = view.docs["pi.agent"];
1357
1429
  const agentModel = agent?.model ?? null;
1358
1430
  const contextWindow = options.models?.find((m) => `${m.provider}/${m.modelId}` === (agentModel ? `${agentModel.provider}/${agentModel.modelId}` : modelLabel))?.contextWindow ?? 0;
1359
- const tokens = contextTokens(options.contextEntries ?? view.entries);
1431
+ const tokens = contextTokens(view.entries);
1360
1432
  const results = new Map;
1361
1433
  for (const entry of view.entries) {
1362
1434
  for (const message of entry.model ?? []) {
@@ -1508,10 +1580,18 @@ function projectChatView(view, modelLabel, options = {}) {
1508
1580
  percent: tokens !== null && contextWindow > 0 ? tokens / contextWindow * 100 : null
1509
1581
  },
1510
1582
  compaction: {
1511
- active: [...live.compactions ?? []],
1583
+ active: (live.compactions ?? []).map(({ trigger, ...status }) => ({ ...status, reason: trigger })),
1512
1584
  latest: options.compaction ?? null,
1513
- ...live.compactionFailure ? { failure: live.compactionFailure } : {}
1585
+ ...live.compactionFailure ? {
1586
+ failure: {
1587
+ taskId: live.compactionFailure.taskId,
1588
+ reason: live.compactionFailure.trigger,
1589
+ error: live.compactionFailure.error
1590
+ }
1591
+ } : {}
1514
1592
  },
1593
+ ...live.run === undefined && live.runFailure ? { runFailure: runFailureView(live.runFailure) } : {},
1594
+ continuable: true,
1515
1595
  model: agentModel !== null ? `${agentModel.provider}/${agentModel.modelId}` : modelLabel,
1516
1596
  agent: {
1517
1597
  model: agentModel,
@@ -1532,6 +1612,13 @@ function projectChatView(view, modelLabel, options = {}) {
1532
1612
  streaming: projectStreaming(live)
1533
1613
  };
1534
1614
  }
1615
+ function runFailureView(failure) {
1616
+ const detail = failure.detail?.replace(/\p{Cc}/gu, " ").trim();
1617
+ return {
1618
+ reason: failure.reason,
1619
+ ...detail ? { detail: detail.length > RUN_FAILURE_DETAIL_CHARS ? `${detail.slice(0, RUN_FAILURE_DETAIL_CHARS)}\u2026` : detail } : {}
1620
+ };
1621
+ }
1535
1622
  function withArgs(item, name, args) {
1536
1623
  const summary = summarizeToolCall(name, args);
1537
1624
  const synthesized = name === "write" ? synthesizedWriteDiff(args) : null;
@@ -1616,7 +1703,7 @@ ${block.text}`;
1616
1703
  return { thinking, text, toolCalls };
1617
1704
  }
1618
1705
  function contextTokens(entries) {
1619
- const compacted = entries.reduce((id, entry) => entry.kind === "pi.compaction" ? Math.max(id, entry.id) : id, 0);
1706
+ const compacted = entries.reduce((id, entry) => entry.kind === "pi.compaction" || entry.kind === "pi.reset" ? Math.max(id, entry.id) : id, 0);
1620
1707
  for (let i = entries.length - 1;i >= 0; i--) {
1621
1708
  const entry = entries[i];
1622
1709
  const message = entry.model?.[0];
@@ -1632,7 +1719,7 @@ function contextTokens(entries) {
1632
1719
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), strings = (value) => Array.isArray(value) && value.every((v) => typeof v === "string") ? value : undefined, interactionField = (name, details) => {
1633
1720
  const interaction = toolInteraction(name, details);
1634
1721
  return interaction ? { interaction } : {};
1635
- }, clip2 = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`, codemodeField = (name, view) => name === CODEMODE_NAME && view ? { codemode: view } : {}, WRITE_DIFF_MAX_LINES = 2000, WRITE_DIFF_MAX_BYTES;
1722
+ }, clip2 = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`, codemodeField = (name, view) => name === CODEMODE_NAME && view ? { codemode: view } : {}, WRITE_DIFF_MAX_LINES = 2000, WRITE_DIFF_MAX_BYTES, RUN_FAILURE_DETAIL_CHARS = 500;
1636
1723
  var init_projection = __esm(() => {
1637
1724
  init_codemode_mode();
1638
1725
  init_image_prompt();
@@ -1971,6 +2058,7 @@ var init_subagent_usage = __esm(() => {
1971
2058
  });
1972
2059
 
1973
2060
  // packages/core/src/subagents.ts
2061
+ import { StringEnum } from "@earendil-works/pi-ai";
1974
2062
  import {
1975
2063
  AssistantEntry,
1976
2064
  configure,
@@ -1981,8 +2069,7 @@ import {
1981
2069
  LiveDoc,
1982
2070
  UsageDoc
1983
2071
  } from "@earendil-works/pi-durable";
1984
- import { Type } from "typebox";
1985
- import { Value } from "typebox/value";
2072
+ import { arr, compile, int, lit, nul, num, obj, oneOf, opt, str, union } from "@earendil-works/pi-schema";
1986
2073
  async function refreshDelegations(tx, id) {
1987
2074
  const doc = await tx.doc(DelegationsDoc, id);
1988
2075
  const identities = await tx.doc(SubagentsDoc, id);
@@ -2217,7 +2304,7 @@ function createSubagentExtension(prepare, discard, guard) {
2217
2304
  replay: "safe",
2218
2305
  callers: ["model"],
2219
2306
  execute: async (input, api, context) => {
2220
- if (!Value.Check(actionParameters, input))
2307
+ if (!isActionParameters(input))
2221
2308
  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.");
2222
2309
  const args = input;
2223
2310
  if ((await api.snapshot(SubagentDoc, api.conversationId, context))?.role)
@@ -2273,6 +2360,7 @@ function createSubagentExtension(prepare, discard, guard) {
2273
2360
  model: agent.model,
2274
2361
  thinkingLevel: agent.thinkingLevel,
2275
2362
  cwd: agent.cwd,
2363
+ maxTurns: agent.maxTurns ?? null,
2276
2364
  extensions: agent.extensions.filter((e) => e.name !== extension.name && !MAIN_ONLY_EXTENSIONS.has(e.name)),
2277
2365
  modelTools: agentChangeFor({ plan: "normal", codemode, role: "subagent" }).modelTools,
2278
2366
  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"))
@@ -2417,7 +2505,7 @@ async function installSubagentCoordinator(harness, parents, context) {
2417
2505
  }, context);
2418
2506
  }
2419
2507
  }
2420
- const stop = harness.subscribeCommits(({ changes }) => {
2508
+ const stop = onCommits(harness, ({ changes }) => {
2421
2509
  for (const c of changes)
2422
2510
  if (c.type === "document" && c.record.kind === "app.neko.delegations" && c.conversationId !== undefined)
2423
2511
  known.add(c.conversationId);
@@ -2426,7 +2514,7 @@ async function installSubagentCoordinator(harness, parents, context) {
2426
2514
  });
2427
2515
  harness.closed.finally(stop).catch(() => {});
2428
2516
  }
2429
- var agentIdFor = (childId) => `agent-${childId}`, DelegationsDoc, SubagentsDoc, SubagentDoc, SlotsDoc, ROLE_PROMPTS, 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.", MAIN_ONLY_TOOLS, MAIN_ONLY_EXTENSIONS, READ_TOOLS, actionParameters, parameters, structuredOutputSchema, ADMISSION_POLL_MS = 100;
2517
+ var agentIdFor = (childId) => `agent-${childId}`, DelegationsDoc, SubagentsDoc, SubagentDoc, SlotsDoc, ROLE_PROMPTS, 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.", MAIN_ONLY_TOOLS, MAIN_ONLY_EXTENSIONS, READ_TOOLS, actionParameters, isActionParameters, parameters, structuredOutputSchema, ADMISSION_POLL_MS = 100;
2430
2518
  var init_subagents = __esm(() => {
2431
2519
  init_agent_change();
2432
2520
  init_codemode();
@@ -2476,59 +2564,60 @@ var init_subagents = __esm(() => {
2476
2564
  MAIN_ONLY_TOOLS = new Set(["todo", "ask_user", "submit_plan"]);
2477
2565
  MAIN_ONLY_EXTENSIONS = new Set(["neko-todos", "neko-questions", "neko-plan"]);
2478
2566
  READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract", CODEMODE_NAME]);
2479
- actionParameters = Type.Union([
2480
- Type.Object({
2481
- action: Type.Optional(Type.Literal("create")),
2482
- type: Type.Enum(["explore", "plan", "coder"]),
2483
- title: Type.String({ minLength: 1, maxLength: 80 }),
2484
- task: Type.String({ minLength: 1, maxLength: 32768 })
2485
- }, { additionalProperties: false }),
2486
- Type.Object({
2487
- action: Type.Literal("continue"),
2488
- agentId: Type.String(),
2489
- title: Type.String({ minLength: 1, maxLength: 80 }),
2490
- task: Type.String({ minLength: 1, maxLength: 32768 })
2491
- }, { additionalProperties: false }),
2492
- Type.Object({
2493
- action: Type.Literal("list"),
2494
- cursor: Type.Optional(Type.Integer({ minimum: 0 })),
2495
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 }))
2496
- }, { additionalProperties: false })
2567
+ actionParameters = union([
2568
+ obj({
2569
+ action: opt(lit("create")),
2570
+ type: oneOf(["explore", "plan", "coder"]),
2571
+ title: str({ minLength: 1, maxLength: 80 }),
2572
+ task: str({ minLength: 1, maxLength: 32768 })
2573
+ }),
2574
+ obj({
2575
+ action: lit("continue"),
2576
+ agentId: str(),
2577
+ title: str({ minLength: 1, maxLength: 80 }),
2578
+ task: str({ minLength: 1, maxLength: 32768 })
2579
+ }),
2580
+ obj({
2581
+ action: lit("list"),
2582
+ cursor: opt(int({ minimum: 0 })),
2583
+ limit: opt(int({ minimum: 1, maximum: 100 }))
2584
+ })
2497
2585
  ]);
2498
- parameters = Type.Object({
2499
- action: Type.Optional(Type.String({ enum: ["create", "continue", "list"], description: "Defaults to create." })),
2500
- type: Type.Optional(Type.String({ enum: ["explore", "plan", "coder"], description: "Required for create only." })),
2501
- agentId: Type.Optional(Type.String({ description: "Required for continue only; use an ID returned by create/list." })),
2502
- title: Type.Optional(Type.String({ minLength: 1, maxLength: 80, description: "Required for create/continue." })),
2503
- task: Type.Optional(Type.String({ minLength: 1, maxLength: 32768, description: "Required for create/continue." })),
2504
- cursor: Type.Optional(Type.Integer({ minimum: 0, description: "List pagination cursor only." })),
2505
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, description: "List page size only." }))
2506
- }, { additionalProperties: false });
2507
- structuredOutputSchema = Type.Union([
2508
- Type.Object({
2509
- agentId: Type.String(),
2510
- roundId: Type.Integer(),
2511
- status: Type.Enum(["completed", "failed", "cancelled"]),
2512
- report: Type.String()
2513
- }, { additionalProperties: false }),
2514
- Type.Object({
2515
- items: Type.Array(Type.Object({
2516
- agentId: Type.String(),
2517
- parentId: Type.Integer(),
2518
- childId: Type.Integer(),
2519
- role: Type.Enum(["explore", "plan", "coder"]),
2520
- title: Type.String(),
2521
- status: Type.Enum(["idle", "queued", "running"]),
2522
- activeRoundId: Type.Union([Type.Integer(), Type.Null()]),
2523
- lastRoundId: Type.Integer(),
2524
- lastReport: Type.Optional(Type.Union([Type.String(), Type.Null()])),
2525
- roundCount: Type.Integer(),
2526
- taskTitle: Type.String(),
2527
- approvalCount: Type.Integer(),
2528
- createdAt: Type.Number()
2529
- }, { additionalProperties: false })),
2530
- next: Type.Union([Type.Integer(), Type.Null()])
2531
- }, { additionalProperties: false })
2586
+ isActionParameters = compile(actionParameters).check;
2587
+ parameters = obj({
2588
+ action: opt(StringEnum(["create", "continue", "list"], { description: "Defaults to create." })),
2589
+ type: opt(StringEnum(["explore", "plan", "coder"], { description: "Required for create only." })),
2590
+ agentId: opt(str({ description: "Required for continue only; use an ID returned by create/list." })),
2591
+ title: opt(str({ minLength: 1, maxLength: 80, description: "Required for create/continue." })),
2592
+ task: opt(str({ minLength: 1, maxLength: 32768, description: "Required for create/continue." })),
2593
+ cursor: opt(int({ minimum: 0, description: "List pagination cursor only." })),
2594
+ limit: opt(int({ minimum: 1, maximum: 100, description: "List page size only." }))
2595
+ });
2596
+ structuredOutputSchema = union([
2597
+ obj({
2598
+ agentId: str(),
2599
+ roundId: int(),
2600
+ status: oneOf(["completed", "failed", "cancelled"]),
2601
+ report: str()
2602
+ }),
2603
+ obj({
2604
+ items: arr(obj({
2605
+ agentId: str(),
2606
+ parentId: int(),
2607
+ childId: int(),
2608
+ role: oneOf(["explore", "plan", "coder"]),
2609
+ title: str(),
2610
+ status: oneOf(["idle", "queued", "running"]),
2611
+ activeRoundId: union([int(), nul()]),
2612
+ lastRoundId: int(),
2613
+ lastReport: opt(union([str(), nul()])),
2614
+ roundCount: int(),
2615
+ taskTitle: str(),
2616
+ approvalCount: int(),
2617
+ createdAt: num()
2618
+ })),
2619
+ next: union([int(), nul()])
2620
+ })
2532
2621
  ]);
2533
2622
  });
2534
2623
 
@@ -3352,11 +3441,15 @@ var normalizeNewlines = (value) => value.replace(/\r\n/g, `
3352
3441
  }, stripFrontmatter = (content) => extractFrontmatter(content).body;
3353
3442
  var init_frontmatter = () => {};
3354
3443
 
3444
+ // packages/core/src/run-failure.ts
3445
+ var CONTINUE_BUSY = "CONTINUE_BUSY: The session is running; wait for it to finish.", CONTINUE_UNAVAILABLE = "CONTINUE_UNAVAILABLE: The last run was answered; there is nothing to continue.";
3446
+ var init_run_failure = () => {};
3447
+
3355
3448
  // packages/core/src/agent-file-search.ts
3356
3449
  import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
3357
3450
  import { defineExtension as defineExtension2, defineTool as defineTool2 } from "@earendil-works/pi-durable";
3358
3451
  import { getOrThrow } from "@earendil-works/pi-durable/env";
3359
- import { Type as Type2 } from "typebox";
3452
+ import { bool, int as int2, obj as obj2, opt as opt2, str as str2 } from "@earendil-works/pi-schema";
3360
3453
  async function search(api, context, run) {
3361
3454
  if (!api.env)
3362
3455
  throw new Error("File environment unavailable");
@@ -3401,12 +3494,12 @@ var init_agent_file_search = __esm(() => {
3401
3494
  outputBytes: 32 * 1024,
3402
3495
  timeoutMs: 1e4
3403
3496
  };
3404
- path = Type2.Optional(Type2.String({ description: "Directory relative to this conversation's cwd, or absolute path." }));
3405
- limit = Type2.Optional(Type2.Integer({ minimum: 1, maximum: 500, description: "Maximum results (default 100)." }));
3497
+ path = opt2(str2({ description: "Directory relative to this conversation's cwd, or absolute path." }));
3498
+ limit = opt2(int2({ minimum: 1, maximum: 500, description: "Maximum results (default 100)." }));
3406
3499
  FindFiles = defineTool2({
3407
3500
  name: "find_files",
3408
3501
  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.",
3409
- parameters: Type2.Object({ path, query: Type2.Optional(Type2.String()), limit }),
3502
+ parameters: obj2({ path, query: opt2(str2()), limit }, { open: true }),
3410
3503
  replay: "safe",
3411
3504
  execute: (args, api, context) => search(api, context, async (env, ctx) => {
3412
3505
  const files = await listing(env, args.path ?? ".", ctx);
@@ -3432,14 +3525,14 @@ var init_agent_file_search = __esm(() => {
3432
3525
  SearchText = defineTool2({
3433
3526
  name: "search_text",
3434
3527
  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.",
3435
- parameters: Type2.Object({
3436
- pattern: Type2.String({ minLength: 1, maxLength: 4096 }),
3528
+ parameters: obj2({
3529
+ pattern: str2({ minLength: 1, maxLength: 4096 }),
3437
3530
  path,
3438
- filePattern: Type2.Optional(Type2.String()),
3439
- caseSensitive: Type2.Optional(Type2.Boolean()),
3440
- contextLines: Type2.Optional(Type2.Integer({ minimum: 0, maximum: 5 })),
3531
+ filePattern: opt2(str2()),
3532
+ caseSensitive: opt2(bool()),
3533
+ contextLines: opt2(int2({ minimum: 0, maximum: 5 })),
3441
3534
  limit
3442
- }),
3535
+ }, { open: true }),
3443
3536
  replay: "safe",
3444
3537
  execute: (args, api, context) => search(api, context, async (env, ctx) => {
3445
3538
  const files = await listing(env, args.path ?? ".", ctx);
@@ -3601,7 +3694,7 @@ class ContextPruner {
3601
3694
  #closed = false;
3602
3695
  constructor(options) {
3603
3696
  this.#options = options;
3604
- this.#unsubscribe = options.harness.subscribeCommits(({ changes }) => {
3697
+ this.#unsubscribe = onCommits(options.harness, ({ changes }) => {
3605
3698
  if (!options.settings().enabled)
3606
3699
  return;
3607
3700
  for (const change of changes) {
@@ -3719,7 +3812,7 @@ import {
3719
3812
  defineTask as defineTask2,
3720
3813
  defineTool as defineTool3
3721
3814
  } from "@earendil-works/pi-durable";
3722
- import { Type as Type3 } from "typebox";
3815
+ 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";
3723
3816
 
3724
3817
  class JobEntry {
3725
3818
  controller = new AbortController;
@@ -4138,15 +4231,15 @@ var init_job_engine = __esm(() => {
4138
4231
  stopped: "stopped",
4139
4232
  lost: "lost"
4140
4233
  };
4141
- action = Type3.Union([Type3.Literal("start"), Type3.Literal("output"), Type3.Literal("stop"), Type3.Literal("list")]);
4142
- parameters2 = Type3.Object({
4234
+ action = union2([lit2("start"), lit2("output"), lit2("stop"), lit2("list")]);
4235
+ parameters2 = obj3({
4143
4236
  action,
4144
- command: Type3.Optional(Type3.String({ description: "start: shell command to run in the session's working directory." })),
4145
- title: Type3.Optional(Type3.String({ description: "start: short label shown in the job list." })),
4146
- timeout: Type3.Optional(Type3.Number({ description: "start: optional timeout in seconds; no default timeout." })),
4147
- jobId: Type3.Optional(Type3.String({ description: "output/stop: the job id returned by start or list." })),
4148
- since: Type3.Optional(Type3.Integer({ description: "output: resume after this offset, as returned by the previous output call." }))
4149
- });
4237
+ command: opt3(str3({ description: "start: shell command to run in the session's working directory." })),
4238
+ title: opt3(str3({ description: "start: short label shown in the job list." })),
4239
+ timeout: opt3(num2({ description: "start: optional timeout in seconds; no default timeout." })),
4240
+ jobId: opt3(str3({ description: "output/stop: the job id returned by start or list." })),
4241
+ since: opt3(int3({ description: "output: resume after this offset, as returned by the previous output call." }))
4242
+ }, { open: true });
4150
4243
  });
4151
4244
 
4152
4245
  // packages/core/src/plan-bash.ts
@@ -4258,13 +4351,14 @@ var init_plan_bash = __esm(() => {
4258
4351
 
4259
4352
  // packages/core/src/user-questions.ts
4260
4353
  import { randomUUID } from "crypto";
4354
+ import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
4261
4355
  import {
4262
4356
  defineDoc as defineDoc6,
4263
4357
  defineDocFamily,
4264
4358
  defineExtension as defineExtension5,
4265
4359
  defineTool as defineTool4
4266
4360
  } from "@earendil-works/pi-durable";
4267
- import { Type as Type4 } from "typebox";
4361
+ 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";
4268
4362
  async function live(tx, id) {
4269
4363
  let cursor = id;
4270
4364
  for (let depth = 0;cursor !== undefined && depth < 16; depth++) {
@@ -4291,7 +4385,7 @@ class UserQuestions {
4291
4385
  constructor(harness, context) {
4292
4386
  this.harness = harness;
4293
4387
  this.#context = context;
4294
- const stop = harness.subscribeCommits(({ changes }) => {
4388
+ const stop = onCommits(harness, ({ changes }) => {
4295
4389
  if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
4296
4390
  return;
4297
4391
  this.reconcileAll();
@@ -4397,13 +4491,16 @@ class UserQuestions {
4397
4491
  if (signal?.aborted)
4398
4492
  return onAbort();
4399
4493
  watch.closed.then((end) => {
4400
- if (end.reason === "stopped")
4494
+ if (end.kind === "stopped")
4401
4495
  return;
4402
- finish(() => reject(signal?.aborted ? signal.reason : end.reason === "listener_error" || end.reason === "session_failed" ? end.error : new Error(`Question watch ended: ${end.reason}`)));
4496
+ finish(() => reject(signal?.aborted ? signal.reason : end.kind === "listener_error" || end.kind === "failed" ? end.error : new Error(`Question watch ended: ${end.kind}`)));
4403
4497
  });
4404
4498
  check(watch.value);
4405
4499
  if (!done)
4406
- watch.start(async (value) => check(value));
4500
+ watch.start(async (call) => {
4501
+ if (call.kind === "value")
4502
+ check(call.value);
4503
+ });
4407
4504
  });
4408
4505
  } finally {
4409
4506
  signal?.removeEventListener("abort", onAbort);
@@ -4571,31 +4668,31 @@ var init_user_questions = __esm(() => {
4571
4668
  createdAt: 0,
4572
4669
  settledAt: null
4573
4670
  };
4574
- optionSchema = Type4.Object({
4575
- id: Type4.String({
4671
+ optionSchema = obj4({
4672
+ id: str4({
4576
4673
  minLength: 1,
4577
4674
  maxLength: QUESTION_LIMITS.optionIdChars,
4578
4675
  description: "Unique within the question."
4579
4676
  }),
4580
- label: Type4.String({
4677
+ label: str4({
4581
4678
  minLength: 1,
4582
4679
  maxLength: QUESTION_LIMITS.labelChars,
4583
4680
  description: "Short, unique, readable alone."
4584
4681
  }),
4585
- description: Type4.Optional(Type4.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
4586
- recommended: Type4.Optional(Type4.Boolean({ description: "Your suggested choice; shown as a hint, never preselected." }))
4587
- }, { additionalProperties: false });
4588
- parameters3 = Type4.Object({
4589
- question: Type4.String({ minLength: 1, maxLength: QUESTION_LIMITS.questionChars }),
4590
- multiple: Type4.Optional(Type4.Boolean({ description: "Let the user pick several options; needs at least 2 options. Default false." })),
4591
- options: Type4.Optional(Type4.Array(optionSchema, { maxItems: QUESTION_LIMITS.maxOptions }))
4592
- }, { additionalProperties: false });
4593
- structuredOutputSchema2 = Type4.Object({
4594
- status: Type4.String({ enum: ["answered", "skipped", "cancelled"] }),
4595
- optionIds: Type4.Optional(Type4.Array(Type4.String())),
4596
- labels: Type4.Optional(Type4.Array(Type4.String())),
4597
- text: Type4.Optional(Type4.Union([Type4.String(), Type4.Null()]))
4598
- }, { additionalProperties: false });
4682
+ description: opt4(str4({ maxLength: QUESTION_LIMITS.descriptionChars })),
4683
+ recommended: opt4(bool2({ description: "Your suggested choice; shown as a hint, never preselected." }))
4684
+ });
4685
+ parameters3 = obj4({
4686
+ question: str4({ minLength: 1, maxLength: QUESTION_LIMITS.questionChars }),
4687
+ multiple: opt4(bool2({ description: "Let the user pick several options; needs at least 2 options. Default false." })),
4688
+ options: opt4(arr2(optionSchema, { maxItems: QUESTION_LIMITS.maxOptions }))
4689
+ });
4690
+ structuredOutputSchema2 = obj4({
4691
+ status: StringEnum2(["answered", "skipped", "cancelled"]),
4692
+ optionIds: opt4(arr2(str4())),
4693
+ labels: opt4(arr2(str4())),
4694
+ text: opt4(union3([str4(), nul2()]))
4695
+ });
4599
4696
  });
4600
4697
 
4601
4698
  // packages/core/src/plan.ts
@@ -4609,7 +4706,7 @@ import {
4609
4706
  wrapTool as wrapTool3
4610
4707
  } from "@earendil-works/pi-durable";
4611
4708
  import { createBashTool } from "@earendil-works/pi-durable/tools";
4612
- import { Type as Type5 } from "typebox";
4709
+ import { arr as arr3, obj as obj5, opt as opt5, str as str5 } from "@earendil-works/pi-schema";
4613
4710
  function settle2(state, item, status) {
4614
4711
  item.status = status;
4615
4712
  item.settledAt = Date.now();
@@ -4700,22 +4797,24 @@ class PlanService {
4700
4797
  return toRecord(item);
4701
4798
  }, context);
4702
4799
  }
4703
- async decide(id, version, decision) {
4800
+ async decideWith(id, version, decision, act) {
4704
4801
  return this.harness.commit(async (tx) => {
4705
4802
  const state = await tx.doc(PlanDoc, id);
4706
4803
  const item = state.items.find((i) => i.version === version);
4707
4804
  if (!item)
4708
4805
  throw new Error("Plan not found.");
4709
- if (item.status === "awaiting_approval") {
4710
- if (decision.kind === "approve") {
4711
- settle2(state, item, "approved");
4712
- state.mode = "normal";
4713
- } else {
4714
- settle2(state, item, "revision_requested");
4715
- item.feedback = decision.feedback;
4716
- }
4806
+ if (item.status !== "awaiting_approval")
4807
+ return toRecord(item);
4808
+ await act(tx);
4809
+ if (decision.kind === "approve") {
4810
+ settle2(state, item, "approved");
4811
+ state.mode = "normal";
4812
+ } else {
4813
+ settle2(state, item, "revision_requested");
4814
+ item.feedback = decision.feedback;
4717
4815
  }
4718
- return { record: toRecord(item), item: clone2(item) };
4816
+ item.dispatched = true;
4817
+ return toRecord(item);
4719
4818
  }, this.#context);
4720
4819
  }
4721
4820
  async supersedePending(id) {
@@ -4850,15 +4949,15 @@ var init_plan = __esm(() => {
4850
4949
  init_subagents();
4851
4950
  init_user_questions();
4852
4951
  init_plan_doc();
4853
- parameters4 = Type5.Object({
4854
- title: Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
4855
- steps: Type5.Array(Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
4952
+ parameters4 = obj5({
4953
+ title: str5({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
4954
+ steps: arr3(str5({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
4856
4955
  minItems: 1,
4857
4956
  maxItems: PLAN_LIMITS.maxSteps,
4858
4957
  description: "Ordered, concrete implementation steps."
4859
4958
  }),
4860
- note: Type5.Optional(Type5.String({ maxLength: PLAN_LIMITS.noteChars, description: "Risks, assumptions and how to verify." }))
4861
- }, { additionalProperties: false });
4959
+ note: opt5(str5({ maxLength: PLAN_LIMITS.noteChars, description: "Risks, assumptions and how to verify." }))
4960
+ });
4862
4961
  });
4863
4962
 
4864
4963
  // packages/core/src/prompt/diagnostics.ts
@@ -5401,7 +5500,7 @@ class SessionMetadataManager {
5401
5500
  this.context = context;
5402
5501
  this.report = report;
5403
5502
  registry.install(defineExtension8({ name: "neko-naming", tasks: [this.task] }));
5404
- this.#unsubscribe = harness.subscribeCommits(({ changes }) => {
5503
+ this.#unsubscribe = onCommits(harness, ({ changes }) => {
5405
5504
  const ids = new Set;
5406
5505
  const submissions = new Map;
5407
5506
  let notice = false;
@@ -5416,7 +5515,7 @@ class SessionMetadataManager {
5416
5515
  if (c.record.kind === "pi.agent" || c.record.kind === QuestionsDoc.definition.kind || c.record.kind === PlanDoc.definition.kind)
5417
5516
  ids.add(c.conversationId);
5418
5517
  if (c.record.kind === "pi.live") {
5419
- const running = c.value?.run !== undefined;
5518
+ const running = c.kind === "change" && c.value.run !== undefined;
5420
5519
  if (this.#live.get(c.conversationId) !== running) {
5421
5520
  this.#live.set(c.conversationId, running);
5422
5521
  ids.add(c.conversationId);
@@ -5576,8 +5675,9 @@ var init_session_metadata = __esm(() => {
5576
5675
  });
5577
5676
 
5578
5677
  // packages/core/src/todos.ts
5678
+ import { StringEnum as StringEnum3 } from "@earendil-works/pi-ai";
5579
5679
  import { defineDoc as defineDoc8, defineExtension as defineExtension9, defineTool as defineTool6 } from "@earendil-works/pi-durable";
5580
- import { Type as Type6 } from "typebox";
5680
+ import { arr as arr4, int as int4, obj as obj6, opt as opt6, str as str6 } from "@earendil-works/pi-schema";
5581
5681
  function renderTodos(list) {
5582
5682
  const { completed, total } = todoProgress(list.items);
5583
5683
  if (!total)
@@ -5658,23 +5758,23 @@ var init_todos = __esm(() => {
5658
5758
  scope: "task",
5659
5759
  initial: () => ({ applied: false, revision: 0, items: [] })
5660
5760
  });
5661
- itemSchema = Type6.Object({
5662
- id: Type6.String({
5761
+ itemSchema = obj6({
5762
+ id: str6({
5663
5763
  minLength: 1,
5664
5764
  maxLength: TODO_LIMITS.idChars,
5665
5765
  description: "Stable id; keep it when you update the same task."
5666
5766
  }),
5667
- text: Type6.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
5668
- status: Type6.String({ enum: [...TODO_STATUSES] })
5669
- }, { additionalProperties: false });
5670
- parameters5 = Type6.Object({
5671
- action: Type6.String({ enum: ["list", "replace"], description: "list reads the list; replace overwrites it." }),
5672
- items: Type6.Optional(Type6.Array(itemSchema, {
5767
+ text: str6({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
5768
+ status: StringEnum3([...TODO_STATUSES])
5769
+ });
5770
+ parameters5 = obj6({
5771
+ action: StringEnum3(["list", "replace"], { description: "list reads the list; replace overwrites it." }),
5772
+ items: opt6(arr4(itemSchema, {
5673
5773
  maxItems: TODO_LIMITS.maxItems,
5674
5774
  description: "Required for replace only: the complete ordered list; [] clears it."
5675
5775
  }))
5676
- }, { additionalProperties: false });
5677
- structuredOutputSchema3 = Type6.Object({ revision: Type6.Integer({ minimum: 0 }), items: Type6.Array(itemSchema) }, { additionalProperties: false });
5776
+ });
5777
+ structuredOutputSchema3 = obj6({ revision: int4({ minimum: 0 }), items: arr4(itemSchema) });
5678
5778
  });
5679
5779
 
5680
5780
  // packages/core/src/tool-approvals.ts
@@ -5699,7 +5799,7 @@ class ToolApprovals {
5699
5799
  #parents = new Set;
5700
5800
  constructor(harness, context) {
5701
5801
  this.harness = harness;
5702
- const stop = harness.subscribeCommits(({ changes }) => {
5802
+ const stop = onCommits(harness, ({ changes }) => {
5703
5803
  if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
5704
5804
  return;
5705
5805
  for (const parent of this.#parents)
@@ -5734,14 +5834,12 @@ class ToolApprovals {
5734
5834
  }, context);
5735
5835
  }
5736
5836
  async wait(parent, id, context) {
5737
- const state = await this.harness.documentState(ApprovalsDoc, parent, context);
5738
- if (!state)
5837
+ const watch = await this.harness.watchDoc(ApprovalsDoc, parent, context);
5838
+ if (!watch)
5739
5839
  throw new Error("Approval unavailable");
5740
5840
  try {
5741
5841
  return await new Promise((resolve, reject) => {
5742
- let stop = () => {};
5743
5842
  const cleanup = () => {
5744
- stop();
5745
5843
  context.abortSignal?.removeEventListener("abort", abort);
5746
5844
  };
5747
5845
  const abort = () => {
@@ -5749,13 +5847,16 @@ class ToolApprovals {
5749
5847
  reject(context.abortSignal?.reason ?? new Error("Cancelled"));
5750
5848
  };
5751
5849
  const check = () => {
5752
- const item = state.value?.items.find((a) => a.id === id);
5850
+ const item = watch.value.items.find((a) => a.id === id);
5753
5851
  if (item && item.status !== "pending") {
5754
5852
  cleanup();
5755
5853
  resolve(structuredClone(item));
5756
5854
  }
5757
5855
  };
5758
- stop = state.subscribe(check);
5856
+ watch.start(async (call) => {
5857
+ if (call.kind === "value")
5858
+ check();
5859
+ });
5759
5860
  context.abortSignal?.addEventListener("abort", abort, { once: true });
5760
5861
  if (context.abortSignal?.aborted)
5761
5862
  abort();
@@ -5763,7 +5864,7 @@ class ToolApprovals {
5763
5864
  check();
5764
5865
  });
5765
5866
  } finally {
5766
- state.dispose();
5867
+ await watch.stop();
5767
5868
  }
5768
5869
  }
5769
5870
  async ensure(parent, candidate, context) {
@@ -6084,8 +6185,8 @@ var init_deepseek = __esm(() => {
6084
6185
  });
6085
6186
 
6086
6187
  // packages/core/src/web-search/index.ts
6087
- import { Type as Type7 } from "@earendil-works/pi-ai";
6088
6188
  import { defineExtension as defineExtension11, defineTool as defineTool7 } from "@earendil-works/pi-durable";
6189
+ import { arr as arr5, num as num3, obj as obj7, opt as opt7, str as str7 } from "@earendil-works/pi-schema";
6089
6190
  function toolError(error) {
6090
6191
  return {
6091
6192
  output: [
@@ -6104,10 +6205,10 @@ function createWebSearchTools(resolveProvider) {
6104
6205
  defineTool7({
6105
6206
  name: "web_search",
6106
6207
  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.",
6107
- parameters: Type7.Object({
6108
- query: Type7.String({ minLength: 1, maxLength: 4096, description: "The search query" }),
6109
- maxResults: Type7.Optional(Type7.Number({ description: `Maximum number of results (1-${MAX_RESULTS}, default ${DEFAULT_RESULTS})` }))
6110
- }),
6208
+ parameters: obj7({
6209
+ query: str7({ minLength: 1, maxLength: 4096, description: "The search query" }),
6210
+ maxResults: opt7(num3({ description: `Maximum number of results (1-${MAX_RESULTS}, default ${DEFAULT_RESULTS})` }))
6211
+ }, { open: true }),
6111
6212
  async execute({ query, maxResults }, _api, context) {
6112
6213
  try {
6113
6214
  if (!query.trim() || query.length > 4096)
@@ -6135,13 +6236,13 @@ ${item.date ? `Published: ${item.date}
6135
6236
  defineTool7({
6136
6237
  name: "web_extract",
6137
6238
  description: "Extract bounded markdown excerpts from web pages by URL. Every requested URL has a result or failure entry; long content is truncated.",
6138
- parameters: Type7.Object({
6139
- urls: Type7.Array(Type7.String({ maxLength: 2048, description: "http(s) URL" }), {
6239
+ parameters: obj7({
6240
+ urls: arr5(str7({ maxLength: 2048, description: "http(s) URL" }), {
6140
6241
  minItems: 1,
6141
6242
  maxItems: MAX_URLS,
6142
6243
  description: `URLs to extract (1-${MAX_URLS})`
6143
6244
  })
6144
- }),
6245
+ }, { open: true }),
6145
6246
  async execute({ urls }, _api, context) {
6146
6247
  try {
6147
6248
  if (!urls.length || urls.length > MAX_URLS)
@@ -6520,7 +6621,7 @@ class WorkspaceFiles {
6520
6621
  }
6521
6622
  #disposeWatch(watch) {
6522
6623
  watch.controller.abort();
6523
- const closing = watch.work.catch(() => {}).then(() => watch.watcher?.close(BACKGROUND_CONTEXT2)).then(() => {}).catch(() => {}).finally(() => this.#disposing.delete(closing));
6624
+ const closing = watch.work.catch(() => {}).then(() => watch.watcher?.stop(BACKGROUND_CONTEXT2)).then(() => {}).catch(() => {}).finally(() => this.#disposing.delete(closing));
6524
6625
  this.#disposing.add(closing);
6525
6626
  }
6526
6627
  #closeWatcher(item) {
@@ -6561,7 +6662,7 @@ class WorkspaceFiles {
6561
6662
  user.generation++;
6562
6663
  user.loaded = 0;
6563
6664
  }
6564
- if ("error" in change) {
6665
+ if (change.kind === "failed") {
6565
6666
  if (this.#watches.get(item.root) === current)
6566
6667
  this.#watches.delete(item.root);
6567
6668
  for (const user of [...current.users])
@@ -6571,7 +6672,7 @@ class WorkspaceFiles {
6571
6672
  if (result.ok) {
6572
6673
  current.watcher = result.value;
6573
6674
  if (current.controller.signal.aborted)
6574
- await result.value.close(BACKGROUND_CONTEXT2);
6675
+ await result.value.stop(BACKGROUND_CONTEXT2);
6575
6676
  } else {
6576
6677
  forget();
6577
6678
  }
@@ -6877,20 +6978,24 @@ import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
6877
6978
  import { PI_ATTRIBUTION } from "@earendil-works/pi-ai/utils/identity";
6878
6979
  import {
6879
6980
  AssistantEntry as AssistantEntry2,
6981
+ ConversationBusy,
6982
+ configure as configure3,
6880
6983
  createRegistry,
6881
6984
  defineDoc as defineDoc10,
6882
6985
  Harness,
6883
6986
  InboxDoc as InboxDoc3,
6884
6987
  LiveDoc as importLiveDoc,
6885
6988
  MemoryStorage,
6886
- UsageDoc as UsageDoc2,
6887
- watchEvents
6989
+ UsageDoc as UsageDoc2
6888
6990
  } from "@earendil-works/pi-durable";
6889
6991
  import { createNodePhotonImages } from "@earendil-works/pi-durable/images/node";
6890
6992
  import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node";
6891
6993
  import { createCodingTools } from "@earendil-works/pi-durable/tools";
6892
6994
  async function createNekoRuntime(options = {}) {
6893
6995
  const context = options.context ?? BACKGROUND_CONTEXT3;
6996
+ if (options.maxTurns !== undefined && !(Number.isSafeInteger(options.maxTurns) && options.maxTurns >= 1))
6997
+ throw new RangeError(`maxTurns must be an integer of 1 or more, got ${options.maxTurns}`);
6998
+ const turnLimit = options.maxTurns === undefined ? {} : { maxTurns: options.maxTurns };
6894
6999
  const defaultCwd = normalizeWorkspace(options.cwd);
6895
7000
  const model = options.model ?? DEFAULT_MODEL;
6896
7001
  const modelOverride = options.modelOverride;
@@ -7010,7 +7115,7 @@ async function createNekoRuntime(options = {}) {
7010
7115
  return compactionFor(contextState);
7011
7116
  }
7012
7117
  },
7013
- onReport: options.onReport,
7118
+ onReport: (report) => options.onReport?.(report.error, report),
7014
7119
  conversationCreated: async (tx, record) => {
7015
7120
  (await tx.doc(SessionMetadataDoc, record.id)).createdAt = Date.now();
7016
7121
  },
@@ -7056,7 +7161,8 @@ async function createNekoRuntime(options = {}) {
7056
7161
  agent: {
7057
7162
  model: rootModel,
7058
7163
  thinkingLevel: resolveLevel(rootModel, options.thinkingLevel ?? "high"),
7059
- cwd: defaultCwd
7164
+ cwd: defaultCwd,
7165
+ ...turnLimit
7060
7166
  }
7061
7167
  });
7062
7168
  enabled = new Set(normalizeEnabledProviders((await harness.snapshot(ProviderSettingsDoc, conversation.id, context))?.enabled));
@@ -7202,9 +7308,7 @@ async function createNekoRuntime(options = {}) {
7202
7308
  if (change)
7203
7309
  await target.configure(change, context);
7204
7310
  };
7205
- const submitTo = async (target, content, submitOptions, extra) => admit(target.id, async () => {
7206
- await syncAgent(target);
7207
- const agent = await target.agent(context);
7311
+ const prepareSubmission = async (target, agent, content, submitOptions, extra) => {
7208
7312
  const workspace = executionWorkspace(agent.cwd);
7209
7313
  const duplicate = submitOptions?.requestId !== undefined && await storage.submissionByRequest(target.id, submitOptions.requestId, context);
7210
7314
  const data = { at: Date.now(), prompt: "", images: [], ...extra };
@@ -7258,15 +7362,23 @@ async function createNekoRuntime(options = {}) {
7258
7362
  }
7259
7363
  if (prepared)
7260
7364
  resourceSnapshots.stage(target.id, requestId, workspace, prepared);
7261
- let result;
7262
- try {
7263
- result = await target.submit({
7365
+ return {
7366
+ draft: {
7264
7367
  type: "input",
7265
7368
  content,
7266
7369
  entry: { kind: NekoInput.kind, data },
7267
7370
  requestId,
7268
7371
  ...submitOptions?.whenBusy !== undefined ? { whenBusy: submitOptions.whenBusy } : {}
7269
- }, context);
7372
+ },
7373
+ requestId
7374
+ };
7375
+ };
7376
+ const submitNow = async (target, content, submitOptions, extra) => {
7377
+ await syncAgent(target);
7378
+ const { draft, requestId } = await prepareSubmission(target, await target.agent(context), content, submitOptions, extra);
7379
+ let result;
7380
+ try {
7381
+ result = await target.submit(draft, context);
7270
7382
  } catch (error) {
7271
7383
  resourceSnapshots.discard(target.id, requestId);
7272
7384
  throw error;
@@ -7276,10 +7388,11 @@ async function createNekoRuntime(options = {}) {
7276
7388
  await touchConversation(target.id);
7277
7389
  await metadata.reconcile(target.id);
7278
7390
  return result;
7279
- });
7280
- const dispatchPlan = async (target, item) => {
7391
+ };
7392
+ const submitTo = (target, ...args) => admit(target.id, () => submitNow(target, ...args));
7393
+ const dispatchPlan = async (target, item, submit = submitTo) => {
7281
7394
  const approve = item.status === "approved";
7282
- await submitTo(target, approve ? approvedPlanPrompt(item) : planRevisionPrompt(item, item.feedback ?? ""), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${item.version}`, whenBusy: "followUp" }, {
7395
+ await submit(target, approve ? approvedPlanPrompt(item) : planRevisionPrompt(item, item.feedback ?? ""), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${item.version}`, whenBusy: "followUp" }, {
7283
7396
  planDecision: {
7284
7397
  version: item.version,
7285
7398
  kind: approve ? "approve" : "revise",
@@ -7316,15 +7429,12 @@ async function createNekoRuntime(options = {}) {
7316
7429
  waitAnswer(submission) {
7317
7430
  return waitAnswerOf(target, submission);
7318
7431
  },
7319
- viewState() {
7320
- return target.viewState(context);
7432
+ follow() {
7433
+ return followConversation(target, context);
7321
7434
  },
7322
7435
  watch() {
7323
7436
  return target.watch(context);
7324
7437
  },
7325
- events() {
7326
- return watchEvents(harness, target.id, context);
7327
- },
7328
7438
  async usage() {
7329
7439
  return mergeUsage(await harness.snapshot(UsageDoc2, target.id, context) ?? { models: {}, tools: {} }, (await harness.snapshot(SessionMetadataDoc, target.id, context))?.namingUsage ?? { models: {}, tools: {} });
7330
7440
  },
@@ -7373,18 +7483,49 @@ async function createNekoRuntime(options = {}) {
7373
7483
  },
7374
7484
  async respondPlan(version, decision) {
7375
7485
  const parsed = validatePlanDecision(decision);
7376
- const known = await plans.get(target.id, version);
7377
- if (!known)
7378
- throw new Error("Plan not found.");
7379
- if (known.status === "awaiting_approval") {
7486
+ return admit(target.id, async () => {
7487
+ const known = await plans.get(target.id, version);
7488
+ if (!known)
7489
+ throw new Error("Plan not found.");
7490
+ if (known.status !== "awaiting_approval") {
7491
+ const legacy = await plans.undispatched(target.id);
7492
+ if (legacy?.version === version)
7493
+ await dispatchPlan(target, legacy, submitNow);
7494
+ return known;
7495
+ }
7380
7496
  if (await isRunning(target.id))
7381
7497
  throw new Error(PLAN_BUSY);
7382
- await guardProvider((await target.agent(context)).model);
7383
- }
7384
- const { record, item } = await plans.decide(target.id, version, parsed);
7385
- if ((item.status === "approved" || item.status === "revision_requested") && !item.dispatched)
7386
- await dispatchPlan(target, item);
7387
- return record;
7498
+ await syncAgent(target);
7499
+ const agent = await target.agent(context);
7500
+ const approve = parsed.kind === "approve";
7501
+ const feedback = parsed.kind === "revise" ? parsed.feedback : "";
7502
+ const { draft, requestId } = await prepareSubmission(target, agent, approve ? approvedPlanPrompt(known) : planRevisionPrompt(known, feedback), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${version}`, whenBusy: "followUp" }, {
7503
+ planDecision: {
7504
+ version,
7505
+ kind: approve ? "approve" : "revise",
7506
+ title: known.title,
7507
+ ...!approve && feedback ? { feedback } : {}
7508
+ }
7509
+ });
7510
+ const codemode = (await harness.snapshot(CodemodeDoc, target.id, context))?.mode ?? "off";
7511
+ const leave = approve ? agentSyncChange(agent, { plan: "normal", codemode, role: "main" }) : undefined;
7512
+ let record;
7513
+ try {
7514
+ record = await plans.decideWith(target.id, version, parsed, async (tx) => {
7515
+ await target.submit(tx, draft);
7516
+ if (leave)
7517
+ await configure3(tx, target.id, leave);
7518
+ });
7519
+ } catch (error) {
7520
+ resourceSnapshots.discard(target.id, requestId);
7521
+ throw error;
7522
+ }
7523
+ if (record.status !== "approved" && record.status !== "revision_requested")
7524
+ resourceSnapshots.discard(target.id, requestId);
7525
+ await touchConversation(target.id);
7526
+ await metadata.reconcile(target.id);
7527
+ return record;
7528
+ });
7388
7529
  },
7389
7530
  async workspaceDir(path, signal) {
7390
7531
  return workspaceFiles.children(savedWorkspace((await target.agent(context)).cwd), path, signal ?? neverAborted());
@@ -7415,6 +7556,26 @@ async function createNekoRuntime(options = {}) {
7415
7556
  await target.abort(context);
7416
7557
  await plans.cancelPending(target.id);
7417
7558
  },
7559
+ continueRun() {
7560
+ return admit(target.id, async () => {
7561
+ await syncAgent(target);
7562
+ await guardProvider((await target.agent(context)).model);
7563
+ const live = await harness.snapshot(importLiveDoc, target.id, context);
7564
+ if (live?.run !== undefined)
7565
+ throw new Error(CONTINUE_BUSY);
7566
+ if (live?.runFailure === undefined)
7567
+ throw new Error(CONTINUE_UNAVAILABLE);
7568
+ try {
7569
+ await target.submit({ type: "continue" }, context);
7570
+ } catch (error) {
7571
+ if (error instanceof ConversationBusy)
7572
+ throw new Error(CONTINUE_BUSY);
7573
+ throw error;
7574
+ }
7575
+ await touchConversation(target.id);
7576
+ await metadata.reconcile(target.id);
7577
+ });
7578
+ },
7418
7579
  async withdraw(submissionId) {
7419
7580
  const id = submissionId;
7420
7581
  const inbox = await harness.snapshot(InboxDoc3, target.id, context);
@@ -7604,15 +7765,12 @@ async function createNekoRuntime(options = {}) {
7604
7765
  root: rootSession,
7605
7766
  submit: rootSession.submit,
7606
7767
  waitAnswer: rootSession.waitAnswer,
7607
- async viewState(id) {
7608
- return (await sessionFor(id)).viewState();
7768
+ async follow(id) {
7769
+ return (await sessionFor(id)).follow();
7609
7770
  },
7610
7771
  async watch(id) {
7611
7772
  return (await sessionFor(id)).watch();
7612
7773
  },
7613
- async events(id) {
7614
- return (await sessionFor(id)).events();
7615
- },
7616
7774
  async usage(id) {
7617
7775
  return (await sessionFor(id)).usage();
7618
7776
  },
@@ -7664,6 +7822,7 @@ async function createNekoRuntime(options = {}) {
7664
7822
  model: effective,
7665
7823
  thinkingLevel: resolveLevel(effective, createOptions?.thinkingLevel ?? options.thinkingLevel ?? "high"),
7666
7824
  cwd: workspace.path,
7825
+ ...turnLimit,
7667
7826
  ...agentChangeFor({ plan: "normal", codemode: mode, role: "main" })
7668
7827
  },
7669
7828
  init: async (tx, id) => {
@@ -7872,6 +8031,7 @@ var init_runtime = __esm(() => {
7872
8031
  init_compaction();
7873
8032
  init_context_pruning();
7874
8033
  init_context_settings();
8034
+ init_conversation_follow();
7875
8035
  init_credentials();
7876
8036
  init_file_changes();
7877
8037
  init_image_metadata();
@@ -7887,6 +8047,7 @@ var init_runtime = __esm(() => {
7887
8047
  init_prompt();
7888
8048
  init_resources();
7889
8049
  init_queued();
8050
+ init_run_failure();
7890
8051
  init_search_providers();
7891
8052
  init_session_metadata();
7892
8053
  init_subagent_usage();
@@ -7993,6 +8154,7 @@ var init_src = __esm(() => {
7993
8154
  init_codemode_mode();
7994
8155
  init_config();
7995
8156
  init_context_settings();
8157
+ init_conversation_follow();
7996
8158
  init_credentials();
7997
8159
  init_file_changes();
7998
8160
  init_images();
@@ -8002,6 +8164,7 @@ var init_src = __esm(() => {
8002
8164
  init_frontmatter();
8003
8165
  init_queued();
8004
8166
  init_rename_with_retry();
8167
+ init_run_failure();
8005
8168
  init_runtime();
8006
8169
  init_search_providers();
8007
8170
  init_session_metadata();
@@ -8017,7 +8180,20 @@ var init_src = __esm(() => {
8017
8180
 
8018
8181
  // packages/gateway/src/contract.ts
8019
8182
  import { hostNamed, method, remote } from "@earendil-works/chord";
8020
- import { Type as Type8 } from "typebox";
8183
+ import {
8184
+ arr as arr6,
8185
+ bool as bool3,
8186
+ int as int5,
8187
+ lit as lit3,
8188
+ map,
8189
+ nul as nul3,
8190
+ num as num4,
8191
+ obj as obj8,
8192
+ oneOf as oneOf2,
8193
+ opt as opt8,
8194
+ str as str8,
8195
+ union as union4
8196
+ } from "@earendil-works/pi-schema";
8021
8197
  function contract(name, def) {
8022
8198
  const variants = Object.fromEntries(Object.entries(def.variants).map(([key, value]) => [
8023
8199
  key,
@@ -8028,451 +8204,462 @@ function contract(name, def) {
8028
8204
  ]));
8029
8205
  return remote(name, { ...def, host: hostNamed(GATEWAY_HOST), variants });
8030
8206
  }
8031
- var GATEWAY_HOST = "neko", rpc = (args, result) => [args, result], str, num, bool, nil, integer, nullableString, phase, level, thinking, model2, change, usage, image, skill, CodemodeViewSchema, call, DelegationSchema, SubagentSummarySchema, jobStatus, JobSummarySchema, JobOutputSchema, QueuedInputSchema, WithdrawResultSchema, SteerResultSchema, TodoItemSchema, TodoListSchema, QuestionOptionSchema, questionStatus, QuestionSummarySchema, WorkspaceEntrySchema, WorkspaceDirSchema, WorkspaceFileSchema, ChangeSummarySchema, ChangedFileSchema, ChangeListSchema, ChangeOpSchema, ChangeDetailSchema, QuestionListSchema, QuestionRecordSchema, QuestionResponseSchema, PlanRecordSchema, PlanViewSchema, PlanDecisionSchema, ToolInteractionSchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, SearchSettingsStateSchema, NekoSearchSettings, contextSource, ContextSettingsStateSchema, nullable = (schema) => Type8.Union([schema, nil]), ContextSettingsPatchSchema, NekoContextSettings, CodemodeSettingsStateSchema, NekoCodemodeSettings, defaultModelSource, DefaultModelStateSchema, NekoDefaultModel, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ShellStatusSchema, ApplicationInfoSchema, NekoApplicationInfo;
8207
+ var GATEWAY_HOST = "neko", rpc = (args, result) => [args, result], text3, number, boolean, nil, integer, nullableString, phase, level, thinking, model2, changeProperties, change, usage, image, skill, CodemodeViewSchema, call, DelegationSchema, SubagentSummarySchema, jobStatus, JobSummarySchema, JobOutputSchema, QueuedInputSchema, WithdrawResultSchema, SteerResultSchema, TodoItemSchema, TodoListSchema, QuestionOptionSchema, questionStatus, QuestionSummarySchema, WorkspaceEntrySchema, WorkspaceDirSchema, WorkspaceFileSchema, ChangeSummarySchema, ChangedFileSchema, ChangeListSchema, ChangeOpSchema, ChangeDetailSchema, QuestionListSchema, QuestionRecordSchema, QuestionResponseSchema, PlanRecordSchema, PlanViewSchema, PlanDecisionSchema, ToolInteractionSchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, SearchSettingsStateSchema, NekoSearchSettings, contextSource, ContextSettingsStateSchema, nullable = (schema) => union4([schema, nil]), ContextSettingsPatchSchema, NekoContextSettings, CodemodeSettingsStateSchema, NekoCodemodeSettings, defaultModelSource, DefaultModelStateSchema, NekoDefaultModel, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ShellStatusSchema, ApplicationInfoSchema, NekoApplicationInfo;
8032
8208
  var init_contract = __esm(() => {
8033
8209
  init_codemode_mode();
8034
8210
  init_interactions();
8035
- str = Type8.String();
8036
- num = Type8.Number();
8037
- bool = Type8.Boolean();
8038
- nil = Type8.Null();
8039
- integer = Type8.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
8040
- nullableString = Type8.Union([str, nil]);
8041
- phase = Type8.Enum(["pending", "running", "done"]);
8042
- level = Type8.Enum(["project", "user", "builtin"]);
8043
- thinking = Type8.Enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
8044
- model2 = Type8.Object({ provider: str, modelId: str });
8045
- change = Type8.Object({ model: Type8.Optional(model2), thinkingLevel: Type8.Optional(thinking) }, { additionalProperties: false });
8046
- usage = Type8.Object({ input: num, output: num, cacheRead: num, cacheWrite: num, cost: num });
8047
- image = Type8.Object({
8048
- name: str,
8049
- mimeType: str,
8211
+ text3 = str8();
8212
+ number = num4();
8213
+ boolean = bool3();
8214
+ nil = nul3();
8215
+ integer = int5({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
8216
+ nullableString = union4([text3, nil]);
8217
+ phase = oneOf2(["pending", "running", "done"]);
8218
+ level = oneOf2(["project", "user", "builtin"]);
8219
+ thinking = oneOf2(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
8220
+ model2 = obj8({ provider: text3, modelId: text3 }, { open: true });
8221
+ changeProperties = { model: opt8(model2), thinkingLevel: opt8(thinking) };
8222
+ change = obj8(changeProperties);
8223
+ usage = obj8({ input: number, output: number, cacheRead: number, cacheWrite: number, cost: number }, { open: true });
8224
+ image = obj8({
8225
+ name: text3,
8226
+ mimeType: text3,
8050
8227
  width: integer,
8051
8228
  height: integer,
8052
8229
  bytes: integer,
8053
8230
  entryId: integer,
8054
8231
  contentIndex: integer
8055
- });
8056
- skill = Type8.Object({ name: str, location: str, body: str, args: str });
8057
- CodemodeViewSchema = Type8.Object({
8058
- calls: Type8.Array(Type8.Object({
8232
+ }, { open: true });
8233
+ skill = obj8({ name: text3, location: text3, body: text3, args: text3 }, { open: true });
8234
+ CodemodeViewSchema = obj8({
8235
+ calls: arr6(obj8({
8059
8236
  id: integer,
8060
- name: str,
8061
- args: str,
8062
- status: Type8.Enum(["running", "ok", "error", "cancelled"]),
8063
- durationMs: Type8.Union([num, nil]),
8237
+ name: text3,
8238
+ args: text3,
8239
+ status: oneOf2(["running", "ok", "error", "cancelled"]),
8240
+ durationMs: union4([number, nil]),
8064
8241
  error: nullableString
8065
- })),
8242
+ }, { open: true })),
8066
8243
  omitted: integer,
8067
8244
  fullOutputPath: nullableString
8068
- });
8069
- call = Type8.Object({
8070
- callId: str,
8071
- name: str,
8072
- summary: str,
8245
+ }, { open: true });
8246
+ call = obj8({
8247
+ callId: text3,
8248
+ name: text3,
8249
+ summary: text3,
8073
8250
  detail: nullableString,
8074
8251
  phase,
8075
8252
  output: nullableString,
8076
8253
  diff: nullableString,
8077
- diffAdded: Type8.Union([num, nil]),
8078
- diffRemoved: Type8.Union([num, nil]),
8079
- diffTruncated: Type8.Optional(bool),
8080
- codemode: Type8.Optional(CodemodeViewSchema)
8081
- });
8082
- DelegationSchema = Type8.Object({
8254
+ diffAdded: union4([number, nil]),
8255
+ diffRemoved: union4([number, nil]),
8256
+ diffTruncated: opt8(boolean),
8257
+ codemode: opt8(CodemodeViewSchema)
8258
+ }, { open: true });
8259
+ DelegationSchema = obj8({
8083
8260
  parentId: integer,
8084
- agentId: Type8.Optional(str),
8085
- round: Type8.Optional(integer),
8086
- runnerId: Type8.Optional(integer),
8087
- inputEntryId: Type8.Optional(integer),
8088
- answerEntryId: Type8.Optional(integer),
8089
- roundUsage: Type8.Optional(Type8.Union([usage, nil])),
8261
+ agentId: opt8(text3),
8262
+ round: opt8(integer),
8263
+ runnerId: opt8(integer),
8264
+ inputEntryId: opt8(integer),
8265
+ answerEntryId: opt8(integer),
8266
+ roundUsage: opt8(union4([usage, nil])),
8090
8267
  taskId: integer,
8091
- callId: str,
8268
+ callId: text3,
8092
8269
  childId: integer,
8093
- role: Type8.Enum(["explore", "plan", "coder"]),
8094
- title: str,
8095
- createdAt: num,
8096
- startedAt: Type8.Union([num, nil]),
8097
- endedAt: Type8.Union([num, nil]),
8098
- status: Type8.Enum(["queued", "running", "completed", "failed", "cancelled"]),
8270
+ role: oneOf2(["explore", "plan", "coder"]),
8271
+ title: text3,
8272
+ createdAt: number,
8273
+ startedAt: union4([number, nil]),
8274
+ endedAt: union4([number, nil]),
8275
+ status: oneOf2(["queued", "running", "completed", "failed", "cancelled"]),
8099
8276
  report: nullableString,
8100
- approvalCount: Type8.Optional(integer),
8101
- historyThrough: Type8.Optional(integer),
8102
- historyUsage: Type8.Optional(usage)
8103
- });
8104
- SubagentSummarySchema = Type8.Object({
8105
- agentId: str,
8277
+ approvalCount: opt8(integer),
8278
+ historyThrough: opt8(integer),
8279
+ historyUsage: opt8(usage)
8280
+ }, { open: true });
8281
+ SubagentSummarySchema = obj8({
8282
+ agentId: text3,
8106
8283
  parentId: integer,
8107
8284
  childId: integer,
8108
- role: Type8.Enum(["explore", "plan", "coder"]),
8109
- title: str,
8110
- status: Type8.Enum(["idle", "queued", "running"]),
8111
- activeRoundId: Type8.Union([integer, nil]),
8285
+ role: oneOf2(["explore", "plan", "coder"]),
8286
+ title: text3,
8287
+ status: oneOf2(["idle", "queued", "running"]),
8288
+ activeRoundId: union4([integer, nil]),
8112
8289
  lastRoundId: integer,
8113
- lastReport: Type8.Optional(nullableString),
8290
+ lastReport: opt8(nullableString),
8114
8291
  roundCount: integer,
8115
- taskTitle: str,
8292
+ taskTitle: text3,
8116
8293
  approvalCount: integer,
8117
- createdAt: num,
8118
- historyThrough: Type8.Optional(integer),
8119
- historyUsage: Type8.Optional(usage)
8120
- });
8121
- jobStatus = Type8.Enum(["running", "exited", "failed", "stopped", "lost"]);
8122
- JobSummarySchema = Type8.Object({
8123
- jobId: str,
8124
- title: str,
8125
- command: str,
8126
- cwd: str,
8294
+ createdAt: number,
8295
+ historyThrough: opt8(integer),
8296
+ historyUsage: opt8(usage)
8297
+ }, { open: true });
8298
+ jobStatus = oneOf2(["running", "exited", "failed", "stopped", "lost"]);
8299
+ JobSummarySchema = obj8({
8300
+ jobId: text3,
8301
+ title: text3,
8302
+ command: text3,
8303
+ cwd: text3,
8127
8304
  status: jobStatus,
8128
- startedAt: num,
8129
- endedAt: Type8.Union([num, nil]),
8130
- exitCode: Type8.Union([num, nil]),
8131
- reason: Type8.Optional(Type8.Enum(["timeout", "spawn-error", "restart", "aborted"])),
8132
- timeoutSeconds: Type8.Optional(num),
8305
+ startedAt: number,
8306
+ endedAt: union4([number, nil]),
8307
+ exitCode: union4([number, nil]),
8308
+ reason: opt8(oneOf2(["timeout", "spawn-error", "restart", "aborted"])),
8309
+ timeoutSeconds: opt8(number),
8133
8310
  outputBytes: integer
8134
- });
8135
- JobOutputSchema = Type8.Object({
8311
+ }, { open: true });
8312
+ JobOutputSchema = obj8({
8136
8313
  job: JobSummarySchema,
8137
- text: str,
8314
+ text: text3,
8138
8315
  offset: integer,
8139
- truncated: bool,
8316
+ truncated: boolean,
8140
8317
  dropped: integer
8141
- });
8142
- QueuedInputSchema = Type8.Object({
8318
+ }, { open: true });
8319
+ QueuedInputSchema = obj8({
8143
8320
  id: integer,
8144
- mode: Type8.Enum(["steer", "followUp"]),
8145
- preview: str,
8321
+ mode: oneOf2(["steer", "followUp"]),
8322
+ preview: text3,
8146
8323
  images: integer,
8147
- skill: Type8.Optional(Type8.Object({ name: str, args: str }))
8148
- });
8149
- WithdrawResultSchema = Type8.Object({
8150
- result: Type8.Enum(["withdrawn", "placed", "settled", "unknown"]),
8151
- text: Type8.Optional(str),
8152
- images: Type8.Optional(integer)
8324
+ skill: opt8(obj8({ name: text3, args: text3 }, { open: true }))
8325
+ }, { open: true });
8326
+ WithdrawResultSchema = obj8({
8327
+ result: oneOf2(["withdrawn", "placed", "settled", "unknown"]),
8328
+ text: opt8(text3),
8329
+ images: opt8(integer)
8330
+ }, { open: true });
8331
+ SteerResultSchema = obj8({
8332
+ result: oneOf2(["steered", "withdrawn", "placed", "settled", "unknown"]),
8333
+ id: opt8(integer),
8334
+ text: opt8(text3),
8335
+ images: opt8(integer)
8336
+ }, { open: true });
8337
+ TodoItemSchema = obj8({
8338
+ id: str8({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
8339
+ text: str8({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
8340
+ status: oneOf2(["pending", "in_progress", "completed"])
8153
8341
  });
8154
- SteerResultSchema = Type8.Object({
8155
- result: Type8.Enum(["steered", "withdrawn", "placed", "settled", "unknown"]),
8156
- id: Type8.Optional(integer),
8157
- text: Type8.Optional(str),
8158
- images: Type8.Optional(integer)
8159
- });
8160
- TodoItemSchema = Type8.Object({
8161
- id: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
8162
- text: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
8163
- status: Type8.Enum(["pending", "in_progress", "completed"])
8164
- }, { additionalProperties: false });
8165
- TodoListSchema = Type8.Object({
8342
+ TodoListSchema = obj8({
8166
8343
  revision: integer,
8167
- items: Type8.Array(TodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
8344
+ items: arr6(TodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
8345
+ }, { open: true });
8346
+ QuestionOptionSchema = obj8({
8347
+ id: str8({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
8348
+ label: str8({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
8349
+ description: opt8(str8({ maxLength: QUESTION_LIMITS.descriptionChars })),
8350
+ recommended: opt8(boolean)
8168
8351
  });
8169
- QuestionOptionSchema = Type8.Object({
8170
- id: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
8171
- label: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
8172
- description: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
8173
- recommended: Type8.Optional(bool)
8174
- }, { additionalProperties: false });
8175
- questionStatus = Type8.Enum(["pending", "answered", "skipped", "cancelled"]);
8176
- QuestionSummarySchema = Type8.Object({
8352
+ questionStatus = oneOf2(["pending", "answered", "skipped", "cancelled"]);
8353
+ QuestionSummarySchema = obj8({
8177
8354
  id: integer,
8178
- version: str,
8179
- preview: Type8.String({ maxLength: QUESTION_LIMITS.previewChars }),
8180
- multiple: bool,
8355
+ version: text3,
8356
+ preview: str8({ maxLength: QUESTION_LIMITS.previewChars }),
8357
+ multiple: boolean,
8181
8358
  optionCount: integer,
8182
- createdAt: num
8183
- });
8184
- WorkspaceEntrySchema = Type8.Object({
8185
- name: str,
8186
- path: str,
8187
- kind: Type8.Enum(["file", "directory", "symlink"]),
8188
- protected: Type8.Optional(Type8.Literal(true))
8189
- }, { additionalProperties: false });
8190
- WorkspaceDirSchema = Type8.Object({
8191
- path: str,
8192
- entries: Type8.Array(WorkspaceEntrySchema, { maxItems: 1000 }),
8193
- limited: bool,
8194
- incomplete: bool
8359
+ createdAt: number
8360
+ }, { open: true });
8361
+ WorkspaceEntrySchema = obj8({
8362
+ name: text3,
8363
+ path: text3,
8364
+ kind: oneOf2(["file", "directory", "symlink"]),
8365
+ protected: opt8(lit3(true))
8195
8366
  });
8196
- WorkspaceFileSchema = Type8.Object({
8197
- path: str,
8198
- size: num,
8199
- mtimeMs: num,
8200
- kind: Type8.Enum(["text", "binary", "protected"]),
8201
- content: Type8.Optional(str),
8202
- truncated: bool
8203
- });
8204
- ChangeSummarySchema = Type8.Object({ revision: integer, files: integer, added: integer, removed: integer });
8205
- ChangedFileSchema = Type8.Object({
8206
- path: str,
8207
- outside: Type8.Optional(Type8.Literal(true)),
8367
+ WorkspaceDirSchema = obj8({
8368
+ path: text3,
8369
+ entries: arr6(WorkspaceEntrySchema, { maxItems: 1000 }),
8370
+ limited: boolean,
8371
+ incomplete: boolean
8372
+ }, { open: true });
8373
+ WorkspaceFileSchema = obj8({
8374
+ path: text3,
8375
+ size: number,
8376
+ mtimeMs: number,
8377
+ kind: oneOf2(["text", "binary", "protected"]),
8378
+ content: opt8(text3),
8379
+ truncated: boolean
8380
+ }, { open: true });
8381
+ ChangeSummarySchema = obj8({ revision: integer, files: integer, added: integer, removed: integer }, { open: true });
8382
+ ChangedFileSchema = obj8({
8383
+ path: text3,
8384
+ outside: opt8(lit3(true)),
8208
8385
  ops: integer,
8209
8386
  added: integer,
8210
8387
  removed: integer,
8211
- agents: Type8.Array(str, { maxItems: 64 }),
8388
+ agents: arr6(text3, { maxItems: 64 }),
8212
8389
  lastSeq: integer
8213
- }, { additionalProperties: false });
8214
- ChangeListSchema = Type8.Object({
8215
- revision: integer,
8216
- files: Type8.Array(ChangedFileSchema, { maxItems: 500 }),
8217
- truncated: bool
8218
8390
  });
8219
- ChangeOpSchema = Type8.Object({
8391
+ ChangeListSchema = obj8({
8392
+ revision: integer,
8393
+ files: arr6(ChangedFileSchema, { maxItems: 500 }),
8394
+ truncated: boolean
8395
+ }, { open: true });
8396
+ ChangeOpSchema = obj8({
8220
8397
  seq: integer,
8221
- agent: str,
8222
- tool: Type8.Enum(["edit", "write"]),
8223
- diff: str,
8398
+ agent: text3,
8399
+ tool: oneOf2(["edit", "write"]),
8400
+ diff: text3,
8224
8401
  added: integer,
8225
8402
  removed: integer,
8226
- truncated: bool
8227
- }, { additionalProperties: false });
8228
- ChangeDetailSchema = Type8.Object({
8229
- path: str,
8230
- outside: Type8.Optional(Type8.Literal(true)),
8231
- ops: Type8.Array(ChangeOpSchema, { maxItems: 50 }),
8232
- truncated: bool
8403
+ truncated: boolean
8233
8404
  });
8234
- QuestionListSchema = Type8.Object({
8405
+ ChangeDetailSchema = obj8({
8406
+ path: text3,
8407
+ outside: opt8(lit3(true)),
8408
+ ops: arr6(ChangeOpSchema, { maxItems: 50 }),
8409
+ truncated: boolean
8410
+ }, { open: true });
8411
+ QuestionListSchema = obj8({
8235
8412
  revision: integer,
8236
- pending: Type8.Array(QuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
8237
- });
8238
- QuestionRecordSchema = Type8.Object({
8413
+ pending: arr6(QuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
8414
+ }, { open: true });
8415
+ QuestionRecordSchema = obj8({
8239
8416
  id: integer,
8240
- callId: str,
8241
- version: str,
8242
- question: Type8.String({ maxLength: QUESTION_LIMITS.questionChars }),
8243
- multiple: bool,
8244
- options: Type8.Array(QuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
8417
+ callId: text3,
8418
+ version: text3,
8419
+ question: str8({ maxLength: QUESTION_LIMITS.questionChars }),
8420
+ multiple: boolean,
8421
+ options: arr6(QuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
8245
8422
  status: questionStatus,
8246
- answer: Type8.Union([
8247
- Type8.Object({
8248
- optionIds: Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
8249
- text: Type8.Union([Type8.String({ maxLength: QUESTION_LIMITS.textChars }), nil])
8250
- }),
8423
+ answer: union4([
8424
+ obj8({
8425
+ optionIds: arr6(str8(), { maxItems: QUESTION_LIMITS.maxOptions }),
8426
+ text: union4([str8({ maxLength: QUESTION_LIMITS.textChars }), nil])
8427
+ }, { open: true }),
8251
8428
  nil
8252
8429
  ]),
8253
- createdAt: num,
8254
- settledAt: Type8.Union([num, nil])
8255
- });
8256
- QuestionResponseSchema = Type8.Union([
8257
- Type8.Object({
8258
- kind: Type8.Literal("answer"),
8259
- optionIds: Type8.Optional(Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
8260
- text: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.textChars }))
8261
- }, { additionalProperties: false }),
8262
- Type8.Object({ kind: Type8.Literal("skip") }, { additionalProperties: false })
8430
+ createdAt: number,
8431
+ settledAt: union4([number, nil])
8432
+ }, { open: true });
8433
+ QuestionResponseSchema = union4([
8434
+ obj8({
8435
+ kind: lit3("answer"),
8436
+ optionIds: opt8(arr6(str8(), { maxItems: QUESTION_LIMITS.maxOptions })),
8437
+ text: opt8(str8({ maxLength: QUESTION_LIMITS.textChars }))
8438
+ }),
8439
+ obj8({ kind: lit3("skip") })
8263
8440
  ]);
8264
- PlanRecordSchema = Type8.Object({
8265
- version: str,
8266
- title: Type8.String({ maxLength: PLAN_LIMITS.titleChars }),
8267
- steps: Type8.Array(Type8.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
8268
- note: Type8.String({ maxLength: PLAN_LIMITS.noteChars }),
8269
- status: Type8.Enum(["awaiting_approval", "approved", "revision_requested", "superseded", "cancelled"]),
8270
- feedback: Type8.Union([Type8.String({ maxLength: PLAN_LIMITS.feedbackChars }), nil]),
8271
- createdAt: num,
8272
- settledAt: Type8.Union([num, nil])
8273
- });
8274
- PlanViewSchema = Type8.Object({
8275
- mode: Type8.Enum(["normal", "plan"]),
8441
+ PlanRecordSchema = obj8({
8442
+ version: text3,
8443
+ title: str8({ maxLength: PLAN_LIMITS.titleChars }),
8444
+ steps: arr6(str8({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
8445
+ note: str8({ maxLength: PLAN_LIMITS.noteChars }),
8446
+ status: oneOf2(["awaiting_approval", "approved", "revision_requested", "superseded", "cancelled"]),
8447
+ feedback: union4([str8({ maxLength: PLAN_LIMITS.feedbackChars }), nil]),
8448
+ createdAt: number,
8449
+ settledAt: union4([number, nil])
8450
+ }, { open: true });
8451
+ PlanViewSchema = obj8({
8452
+ mode: oneOf2(["normal", "plan"]),
8276
8453
  revision: integer,
8277
- current: Type8.Union([PlanRecordSchema, nil])
8278
- });
8279
- PlanDecisionSchema = Type8.Union([
8280
- Type8.Object({ kind: Type8.Literal("approve") }, { additionalProperties: false }),
8281
- Type8.Object({ kind: Type8.Literal("revise"), feedback: Type8.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
8454
+ current: union4([PlanRecordSchema, nil])
8455
+ }, { open: true });
8456
+ PlanDecisionSchema = union4([
8457
+ obj8({ kind: lit3("approve") }),
8458
+ obj8({ kind: lit3("revise"), feedback: str8({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) })
8282
8459
  ]);
8283
- ToolInteractionSchema = Type8.Union([
8284
- Type8.Object({
8285
- kind: Type8.Literal("ask_user"),
8460
+ ToolInteractionSchema = union4([
8461
+ obj8({
8462
+ kind: lit3("ask_user"),
8286
8463
  id: integer,
8287
- question: str,
8288
- multiple: bool,
8289
- options: Type8.Array(Type8.Object({ id: str, label: str, recommended: Type8.Optional(bool) })),
8464
+ question: text3,
8465
+ multiple: boolean,
8466
+ options: arr6(obj8({ id: text3, label: text3, recommended: opt8(boolean) }, { open: true })),
8290
8467
  status: questionStatus,
8291
- optionIds: Type8.Optional(Type8.Array(str)),
8292
- labels: Type8.Optional(Type8.Array(str)),
8293
- text: Type8.Optional(Type8.Union([str, nil]))
8294
- }),
8295
- Type8.Object({
8296
- kind: Type8.Literal("todo"),
8297
- action: Type8.Enum(["list", "replace"]),
8468
+ optionIds: opt8(arr6(text3)),
8469
+ labels: opt8(arr6(text3)),
8470
+ text: opt8(union4([text3, nil]))
8471
+ }, { open: true }),
8472
+ obj8({
8473
+ kind: lit3("todo"),
8474
+ action: oneOf2(["list", "replace"]),
8298
8475
  revision: integer,
8299
8476
  completed: integer,
8300
8477
  total: integer
8301
- }),
8302
- Type8.Object({
8303
- kind: Type8.Literal("submit_plan"),
8304
- version: str,
8305
- title: str,
8306
- steps: Type8.Array(str),
8307
- note: str
8308
- })
8478
+ }, { open: true }),
8479
+ obj8({
8480
+ kind: lit3("submit_plan"),
8481
+ version: text3,
8482
+ title: text3,
8483
+ steps: arr6(text3),
8484
+ note: text3
8485
+ }, { open: true })
8309
8486
  ]);
8310
- ChatViewSchema = Type8.Object({
8311
- historyBefore: Type8.Optional(Type8.Union([integer, nil])),
8312
- agents: Type8.Optional(Type8.Array(SubagentSummarySchema)),
8313
- jobs: Type8.Optional(Type8.Array(JobSummarySchema)),
8314
- queued: Type8.Optional(Type8.Array(QueuedInputSchema)),
8315
- todos: Type8.Optional(TodoListSchema),
8316
- questions: Type8.Optional(QuestionListSchema),
8317
- plan: Type8.Optional(PlanViewSchema),
8318
- changes: Type8.Optional(ChangeSummarySchema),
8319
- rounds: Type8.Optional(Type8.Array(DelegationSchema)),
8320
- subagents: Type8.Optional(Type8.Array(DelegationSchema)),
8321
- taskUsage: Type8.Optional(usage),
8322
- model: str,
8323
- agent: Type8.Object({ model: Type8.Union([model2, nil]), thinkingLevel: Type8.Union([thinking, nil]) }),
8324
- cwd: str,
8325
- busy: bool,
8487
+ ChatViewSchema = obj8({
8488
+ historyBefore: opt8(union4([integer, nil])),
8489
+ agents: opt8(arr6(SubagentSummarySchema)),
8490
+ jobs: opt8(arr6(JobSummarySchema)),
8491
+ queued: opt8(arr6(QueuedInputSchema)),
8492
+ todos: opt8(TodoListSchema),
8493
+ questions: opt8(QuestionListSchema),
8494
+ plan: opt8(PlanViewSchema),
8495
+ changes: opt8(ChangeSummarySchema),
8496
+ rounds: opt8(arr6(DelegationSchema)),
8497
+ subagents: opt8(arr6(DelegationSchema)),
8498
+ taskUsage: opt8(usage),
8499
+ model: text3,
8500
+ agent: obj8({ model: union4([model2, nil]), thinkingLevel: union4([thinking, nil]) }, { open: true }),
8501
+ cwd: text3,
8502
+ busy: boolean,
8326
8503
  status: nullableString,
8327
8504
  usage,
8328
- contextUsage: Type8.Optional(Type8.Object({ tokens: Type8.Union([num, nil]), window: num, percent: Type8.Union([num, nil]) })),
8329
- compaction: Type8.Optional(Type8.Object({
8330
- failure: Type8.Optional(Type8.Object({ taskId: integer, reason: Type8.Enum(["manual", "threshold", "overflow"]), error: str })),
8331
- active: Type8.Array(Type8.Object({
8505
+ contextUsage: opt8(obj8({ tokens: union4([number, nil]), window: number, percent: union4([number, nil]) }, { open: true })),
8506
+ compaction: opt8(obj8({
8507
+ failure: opt8(obj8({ taskId: integer, reason: oneOf2(["manual", "threshold", "overflow"]), error: text3 }, { open: true })),
8508
+ active: arr6(obj8({
8332
8509
  taskId: integer,
8333
- reason: Type8.Enum(["manual", "threshold", "overflow"]),
8334
- blocking: bool,
8510
+ reason: oneOf2(["manual", "threshold", "overflow"]),
8511
+ blocking: boolean,
8335
8512
  attempt: integer,
8336
- retry: Type8.Optional(Type8.Object({ at: num, error: str }))
8337
- })),
8338
- latest: Type8.Union([
8513
+ retry: opt8(obj8({ at: number, error: text3 }, { open: true }))
8514
+ }, { open: true })),
8515
+ latest: union4([
8339
8516
  nil,
8340
- Type8.Object({
8517
+ obj8({
8341
8518
  taskId: integer,
8342
- phase: Type8.Enum(["summarizing", "waiting-to-apply", "applied", "nothing", "skipped", "cancelled", "failed"]),
8343
- at: num
8344
- })
8519
+ phase: oneOf2([
8520
+ "summarizing",
8521
+ "waiting-to-apply",
8522
+ "applied",
8523
+ "nothing",
8524
+ "skipped",
8525
+ "cancelled",
8526
+ "failed"
8527
+ ]),
8528
+ at: number
8529
+ }, { open: true })
8345
8530
  ])
8346
- })),
8347
- items: Type8.Array(Type8.Union([
8348
- Type8.Object({
8349
- type: Type8.Literal("user"),
8350
- order: Type8.Optional(num),
8351
- key: str,
8531
+ }, { open: true })),
8532
+ runFailure: opt8(obj8({ reason: text3, detail: opt8(text3) }, { open: true })),
8533
+ continuable: opt8(lit3(true)),
8534
+ items: arr6(union4([
8535
+ obj8({
8536
+ type: lit3("user"),
8537
+ order: opt8(number),
8538
+ key: text3,
8352
8539
  id: integer,
8353
- text: str,
8354
- images: Type8.Optional(Type8.Array(image)),
8355
- skill: Type8.Optional(skill),
8356
- defaultImagePrompt: Type8.Optional(Type8.Object({ version: Type8.Integer({ minimum: 1 }), text: str }))
8357
- }),
8358
- Type8.Object({
8359
- type: Type8.Literal("assistant"),
8360
- order: Type8.Optional(num),
8361
- key: str,
8540
+ text: text3,
8541
+ images: opt8(arr6(image)),
8542
+ skill: opt8(skill),
8543
+ defaultImagePrompt: opt8(obj8({ version: int5({ minimum: 1 }), text: text3 }, { open: true }))
8544
+ }, { open: true }),
8545
+ obj8({
8546
+ type: lit3("assistant"),
8547
+ order: opt8(number),
8548
+ key: text3,
8362
8549
  id: integer,
8363
- thinking: str,
8364
- text: str,
8550
+ thinking: text3,
8551
+ text: text3,
8365
8552
  stopReason: nullableString,
8366
8553
  errorMessage: nullableString,
8367
8554
  note: nullableString
8368
- }),
8369
- Type8.Object({
8370
- type: Type8.Literal("tool"),
8371
- order: Type8.Optional(num),
8372
- key: str,
8373
- images: Type8.Optional(Type8.Array(image)),
8374
- callId: str,
8375
- name: str,
8376
- summary: str,
8555
+ }, { open: true }),
8556
+ obj8({
8557
+ type: lit3("tool"),
8558
+ order: opt8(number),
8559
+ key: text3,
8560
+ images: opt8(arr6(image)),
8561
+ callId: text3,
8562
+ name: text3,
8563
+ summary: text3,
8377
8564
  detail: nullableString,
8378
8565
  phase,
8379
- isError: bool,
8566
+ isError: boolean,
8380
8567
  output: nullableString,
8381
8568
  result: nullableString,
8382
- durationMs: Type8.Union([num, nil]),
8569
+ durationMs: union4([number, nil]),
8383
8570
  note: nullableString,
8384
8571
  diff: nullableString,
8385
- diffAdded: Type8.Union([num, nil]),
8386
- diffRemoved: Type8.Union([num, nil]),
8387
- diffTruncated: Type8.Optional(bool),
8388
- interaction: Type8.Optional(ToolInteractionSchema),
8389
- codemode: Type8.Optional(CodemodeViewSchema)
8390
- }),
8391
- Type8.Object({
8392
- type: Type8.Literal("system"),
8393
- order: Type8.Optional(num),
8394
- key: str,
8572
+ diffAdded: union4([number, nil]),
8573
+ diffRemoved: union4([number, nil]),
8574
+ diffTruncated: opt8(boolean),
8575
+ interaction: opt8(ToolInteractionSchema),
8576
+ codemode: opt8(CodemodeViewSchema)
8577
+ }, { open: true }),
8578
+ obj8({
8579
+ type: lit3("system"),
8580
+ order: opt8(number),
8581
+ key: text3,
8395
8582
  id: integer,
8396
- text: str,
8397
- jobId: Type8.Optional(str),
8398
- planDecision: Type8.Optional(Type8.Object({ version: str, kind: Type8.Enum(["approve", "revise"]) }, { additionalProperties: false }))
8399
- })
8583
+ text: text3,
8584
+ jobId: opt8(text3),
8585
+ planDecision: opt8(obj8({ version: text3, kind: oneOf2(["approve", "revise"]) }))
8586
+ }, { open: true })
8400
8587
  ])),
8401
- streaming: Type8.Union([nil, Type8.Object({ thinking: str, text: str, toolCalls: Type8.Array(call) })])
8402
- });
8403
- SessionSummarySchema = Type8.Object({
8404
- workspaceRoot: str,
8405
- lastUsedAt: num,
8406
- title: str,
8407
- firstPromptPreview: str,
8408
- createdAt: num,
8409
- lastMessageAt: Type8.Union([num, nil]),
8410
- model: Type8.Union([model2, nil]),
8411
- status: Type8.Enum(["idle", "running", "error"]),
8412
- awaitingAnswer: Type8.Optional(bool),
8413
- awaitingPlan: Type8.Optional(bool),
8414
- id: str
8415
- });
8416
- DirectoryStateSchema = Type8.Object({ revision: integer, sessions: Type8.Array(SessionSummarySchema) });
8417
- WorkspaceSummarySchema = Type8.Object({ path: str, createdAt: num });
8418
- WorkspaceStateSchema = Type8.Object({ revision: integer, workspaces: Type8.Array(WorkspaceSummarySchema) });
8419
- credentialStatus = Type8.Object({
8420
- savedConfigured: bool,
8421
- environmentConfigured: bool,
8422
- source: Type8.Enum(["saved", "environment", "runtime", "keyless", "none"]),
8423
- enabled: Type8.Optional(bool)
8424
- });
8425
- CredentialStateSchema = Type8.Object({
8588
+ streaming: union4([nil, obj8({ thinking: text3, text: text3, toolCalls: arr6(call) }, { open: true })])
8589
+ }, { open: true });
8590
+ SessionSummarySchema = obj8({
8591
+ workspaceRoot: text3,
8592
+ lastUsedAt: number,
8593
+ title: text3,
8594
+ firstPromptPreview: text3,
8595
+ createdAt: number,
8596
+ lastMessageAt: union4([number, nil]),
8597
+ model: union4([model2, nil]),
8598
+ status: oneOf2(["idle", "running", "error"]),
8599
+ awaitingAnswer: opt8(boolean),
8600
+ awaitingPlan: opt8(boolean),
8601
+ id: text3
8602
+ }, { open: true });
8603
+ DirectoryStateSchema = obj8({ revision: integer, sessions: arr6(SessionSummarySchema) }, { open: true });
8604
+ WorkspaceSummarySchema = obj8({ path: text3, createdAt: number }, { open: true });
8605
+ WorkspaceStateSchema = obj8({ revision: integer, workspaces: arr6(WorkspaceSummarySchema) }, { open: true });
8606
+ credentialStatus = obj8({
8607
+ savedConfigured: boolean,
8608
+ environmentConfigured: boolean,
8609
+ source: oneOf2(["saved", "environment", "runtime", "keyless", "none"]),
8610
+ enabled: opt8(boolean)
8611
+ }, { open: true });
8612
+ CredentialStateSchema = obj8({
8426
8613
  revision: integer,
8427
- statuses: Type8.Record(str, credentialStatus)
8428
- });
8429
- AttachmentSchema = Type8.Object({ sessionId: str, attachmentId: str });
8430
- chatInput = Type8.Object({ sessionId: str, attachmentId: str }, { additionalProperties: false });
8614
+ statuses: map(credentialStatus)
8615
+ }, { open: true });
8616
+ AttachmentSchema = obj8({ sessionId: text3, attachmentId: text3 }, { open: true });
8617
+ chatInput = obj8({ sessionId: text3, attachmentId: text3 });
8431
8618
  NekoSessionDirectory = contract("neko.session-directory", {
8432
8619
  version: 1,
8433
- input: Type8.Object({}, { additionalProperties: false }),
8620
+ input: obj8({}),
8434
8621
  variants: { ready: { state: { state: DirectoryStateSchema } } }
8435
8622
  });
8436
8623
  NekoSessionManagement = contract("neko.session-management", {
8437
8624
  version: 1,
8438
- input: Type8.Object({}, { additionalProperties: false }),
8625
+ input: obj8({}),
8439
8626
  variants: {
8440
8627
  ready: {
8441
8628
  methods: {
8442
- create: rpc(Type8.Object({ ...change.properties, cwd: Type8.Optional(str) }, { additionalProperties: false }), SessionSummarySchema),
8443
- archive: rpc(str, nil),
8444
- attach: rpc(str, AttachmentSchema),
8629
+ create: rpc(obj8({ ...changeProperties, cwd: opt8(text3) }), SessionSummarySchema),
8630
+ archive: rpc(text3, nil),
8631
+ attach: rpc(text3, AttachmentSchema),
8445
8632
  detach: rpc(nil, nil),
8446
- fork: rpc(str, SessionSummarySchema)
8633
+ fork: rpc(text3, SessionSummarySchema)
8447
8634
  }
8448
8635
  }
8449
8636
  }
8450
8637
  });
8451
- resize = Type8.Object({
8452
- maxWidth: Type8.Optional(integer),
8453
- maxHeight: Type8.Optional(integer),
8454
- maxBytes: Type8.Optional(integer),
8455
- jpegQuality: Type8.Optional(integer)
8456
- });
8457
- inputLimits = Type8.Object({
8458
- maxRequestBytes: Type8.Optional(integer),
8459
- images: Type8.Optional(Type8.Object({
8460
- resize: Type8.Optional(resize),
8461
- maxPerMessage: Type8.Optional(integer),
8462
- maxPerRequest: Type8.Optional(integer)
8463
- }))
8464
- });
8465
- modelInfo = Type8.Object({
8466
- provider: str,
8467
- modelId: str,
8468
- name: str,
8469
- available: bool,
8470
- contextWindow: num,
8471
- input: Type8.Array(str),
8472
- inputLimits: Type8.Optional(inputLimits),
8473
- reasoning: bool,
8474
- supportedThinkingLevels: Type8.Array(thinking),
8475
- thinkingLevelAdjustments: Type8.Object({
8638
+ resize = obj8({
8639
+ maxWidth: opt8(integer),
8640
+ maxHeight: opt8(integer),
8641
+ maxBytes: opt8(integer),
8642
+ jpegQuality: opt8(integer)
8643
+ }, { open: true });
8644
+ inputLimits = obj8({
8645
+ maxRequestBytes: opt8(integer),
8646
+ images: opt8(obj8({
8647
+ resize: opt8(resize),
8648
+ maxPerMessage: opt8(integer),
8649
+ maxPerRequest: opt8(integer)
8650
+ }, { open: true }))
8651
+ }, { open: true });
8652
+ modelInfo = obj8({
8653
+ provider: text3,
8654
+ modelId: text3,
8655
+ name: text3,
8656
+ available: boolean,
8657
+ contextWindow: number,
8658
+ input: arr6(text3),
8659
+ inputLimits: opt8(inputLimits),
8660
+ reasoning: boolean,
8661
+ supportedThinkingLevels: arr6(thinking),
8662
+ thinkingLevelAdjustments: obj8({
8476
8663
  off: thinking,
8477
8664
  minimal: thinking,
8478
8665
  low: thinking,
@@ -8480,35 +8667,35 @@ var init_contract = __esm(() => {
8480
8667
  high: thinking,
8481
8668
  xhigh: thinking,
8482
8669
  max: thinking
8483
- })
8484
- });
8670
+ }, { open: true })
8671
+ }, { open: true });
8485
8672
  NekoModels = contract("neko.models", {
8486
8673
  version: 1,
8487
- input: Type8.Object({}, { additionalProperties: false }),
8488
- variants: { ready: { methods: { list: rpc(nil, Type8.Array(modelInfo)) } } }
8674
+ input: obj8({}),
8675
+ variants: { ready: { methods: { list: rpc(nil, arr6(modelInfo)) } } }
8489
8676
  });
8490
- imageChunk = Type8.Object({ data: str, total: integer, mimeType: str });
8677
+ imageChunk = obj8({ data: text3, total: integer, mimeType: text3 }, { open: true });
8491
8678
  NekoSubagent = contract("neko.subagent", {
8492
8679
  version: 1,
8493
- input: Type8.Object({
8494
- sessionId: str,
8495
- attachmentId: str,
8496
- taskId: Type8.Optional(integer),
8497
- agentId: Type8.Optional(str)
8498
- }),
8680
+ input: obj8({
8681
+ sessionId: text3,
8682
+ attachmentId: text3,
8683
+ taskId: opt8(integer),
8684
+ agentId: opt8(text3)
8685
+ }, { open: true }),
8499
8686
  variants: {
8500
8687
  ready: {
8501
8688
  state: { revision: integer },
8502
8689
  methods: {
8503
- chunk: rpc(Type8.Object({
8504
- kind: Type8.Enum(["view", "history"]),
8505
- before: Type8.Optional(integer),
8506
- token: Type8.Optional(integer),
8690
+ chunk: rpc(obj8({
8691
+ kind: oneOf2(["view", "history"]),
8692
+ before: opt8(integer),
8693
+ token: opt8(integer),
8507
8694
  offset: integer
8508
- }), Type8.Object({ token: integer, data: str, next: Type8.Union([integer, nil]) })),
8509
- getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk),
8510
- decideApproval: rpc(Type8.Object({ id: integer, version: str, decision: Type8.Enum(["allowed", "denied"]) }), Type8.Enum(["pending", "allowed", "denied", "cancelled"])),
8511
- stop: rpc(Type8.Union([nil, Type8.Object({ taskId: integer })]), nil)
8695
+ }, { open: true }), obj8({ token: integer, data: text3, next: union4([integer, nil]) }, { open: true })),
8696
+ getImage: rpc(obj8({ entryId: integer, contentIndex: integer, offset: integer }, { open: true }), imageChunk),
8697
+ decideApproval: rpc(obj8({ id: integer, version: text3, decision: oneOf2(["allowed", "denied"]) }, { open: true }), oneOf2(["pending", "allowed", "denied", "cancelled"])),
8698
+ stop: rpc(union4([nil, obj8({ taskId: integer }, { open: true })]), nil)
8512
8699
  }
8513
8700
  }
8514
8701
  }
@@ -8520,174 +8707,175 @@ var init_contract = __esm(() => {
8520
8707
  ready: {
8521
8708
  state: { view: ChatViewSchema },
8522
8709
  methods: {
8523
- submit: rpc(Type8.Object({
8524
- content: str,
8525
- images: Type8.Optional(Type8.Array(str, { maxItems: 4 })),
8526
- requestId: Type8.Optional(str),
8527
- whenBusy: Type8.Optional(Type8.Enum(["steer", "followUp"]))
8528
- }, { additionalProperties: false }), nil),
8710
+ submit: rpc(obj8({
8711
+ content: text3,
8712
+ images: opt8(arr6(text3, { maxItems: 4 })),
8713
+ requestId: opt8(text3),
8714
+ whenBusy: opt8(oneOf2(["steer", "followUp"]))
8715
+ }), nil),
8529
8716
  abort: rpc(nil, nil),
8530
- withdraw: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), WithdrawResultSchema),
8531
- steer: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), SteerResultSchema),
8532
- questionGet: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), Type8.Union([QuestionRecordSchema, nil])),
8533
- questionRespond: rpc(Type8.Object({ id: integer, version: str, response: QuestionResponseSchema }, { additionalProperties: false }), QuestionRecordSchema),
8534
- planGet: rpc(Type8.Object({ version: str }, { additionalProperties: false }), Type8.Union([PlanRecordSchema, nil])),
8535
- planRespond: rpc(Type8.Object({ version: str, decision: PlanDecisionSchema }, { additionalProperties: false }), PlanRecordSchema),
8536
- planMode: rpc(Type8.Object({ mode: Type8.Enum(["normal", "plan"]) }, { additionalProperties: false }), PlanViewSchema),
8717
+ continueRun: rpc(nil, nil),
8718
+ withdraw: rpc(obj8({ id: integer }), WithdrawResultSchema),
8719
+ steer: rpc(obj8({ id: integer }), SteerResultSchema),
8720
+ questionGet: rpc(obj8({ id: integer }), union4([QuestionRecordSchema, nil])),
8721
+ questionRespond: rpc(obj8({ id: integer, version: text3, response: QuestionResponseSchema }), QuestionRecordSchema),
8722
+ planGet: rpc(obj8({ version: text3 }), union4([PlanRecordSchema, nil])),
8723
+ planRespond: rpc(obj8({ version: text3, decision: PlanDecisionSchema }), PlanRecordSchema),
8724
+ planMode: rpc(obj8({ mode: oneOf2(["normal", "plan"]) }), PlanViewSchema),
8537
8725
  usage: rpc(nil, usage),
8538
- workspaceDir: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceDirSchema),
8539
- workspaceFile: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceFileSchema),
8726
+ workspaceDir: rpc(obj8({ path: text3 }), WorkspaceDirSchema),
8727
+ workspaceFile: rpc(obj8({ path: text3 }), WorkspaceFileSchema),
8540
8728
  changesList: rpc(nil, ChangeListSchema),
8541
- changeGet: rpc(Type8.Object({ path: str }, { additionalProperties: false }), Type8.Union([ChangeDetailSchema, nil])),
8542
- skills: rpc(nil, Type8.Array(Type8.Object({ name: str, description: str, location: str, level }))),
8543
- jobOutput: rpc(Type8.Object({ jobId: str, since: Type8.Optional(integer) }, { additionalProperties: false }), JobOutputSchema),
8544
- jobStop: rpc(Type8.Object({ jobId: str }, { additionalProperties: false }), JobSummarySchema),
8729
+ changeGet: rpc(obj8({ path: text3 }), union4([ChangeDetailSchema, nil])),
8730
+ skills: rpc(nil, arr6(obj8({ name: text3, description: text3, location: text3, level }, { open: true }))),
8731
+ jobOutput: rpc(obj8({ jobId: text3, since: opt8(integer) }), JobOutputSchema),
8732
+ jobStop: rpc(obj8({ jobId: text3 }), JobSummarySchema),
8545
8733
  configure: rpc(change, nil),
8546
- compact: rpc(Type8.Object({ instructions: Type8.Optional(str) }, { additionalProperties: false }), nil),
8547
- importImage: rpc(str, Type8.Object({ id: str, name: str })),
8548
- draftImage: rpc(Type8.Object({ id: str, offset: integer }, { additionalProperties: false }), imageChunk),
8549
- beginImage: rpc(Type8.Object({ name: str, mimeType: str, size: Type8.Integer({ minimum: 1, maximum: 20971520 }) }, { additionalProperties: false }), str),
8550
- appendImage: rpc(Type8.Object({ id: str, offset: integer, data: Type8.String({ maxLength: 350000 }) }, { additionalProperties: false }), nil),
8551
- finishImage: rpc(str, nil),
8552
- releaseImage: rpc(str, nil),
8553
- getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }, { additionalProperties: false }), imageChunk)
8734
+ compact: rpc(obj8({ instructions: opt8(text3) }), nil),
8735
+ importImage: rpc(text3, obj8({ id: text3, name: text3 }, { open: true })),
8736
+ draftImage: rpc(obj8({ id: text3, offset: integer }), imageChunk),
8737
+ beginImage: rpc(obj8({ name: text3, mimeType: text3, size: int5({ minimum: 1, maximum: 20971520 }) }), text3),
8738
+ appendImage: rpc(obj8({ id: text3, offset: integer, data: str8({ maxLength: 350000 }) }), nil),
8739
+ finishImage: rpc(text3, nil),
8740
+ releaseImage: rpc(text3, nil),
8741
+ getImage: rpc(obj8({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk)
8554
8742
  }
8555
8743
  }
8556
8744
  }
8557
8745
  });
8558
- browse = Type8.Object({ path: str, query: Type8.Optional(str), showHidden: Type8.Optional(bool), cursor: Type8.Optional(str) }, { additionalProperties: false });
8559
- page = Type8.Object({
8560
- path: str,
8746
+ browse = obj8({ path: text3, query: opt8(text3), showHidden: opt8(boolean), cursor: opt8(text3) });
8747
+ page = obj8({
8748
+ path: text3,
8561
8749
  parent: nullableString,
8562
- entries: Type8.Array(Type8.Object({ name: str, path: str, error: Type8.Optional(str) })),
8750
+ entries: arr6(obj8({ name: text3, path: text3, error: opt8(text3) }, { open: true })),
8563
8751
  next: nullableString
8564
- });
8752
+ }, { open: true });
8565
8753
  NekoWorkspaces = contract("neko.workspaces", {
8566
8754
  version: 2,
8567
- input: Type8.Object({}, { additionalProperties: false }),
8755
+ input: obj8({}),
8568
8756
  variants: {
8569
8757
  ready: {
8570
8758
  state: { state: WorkspaceStateSchema },
8571
8759
  methods: {
8572
- add: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceSummarySchema),
8573
- remove: rpc(Type8.Object({ path: str }, { additionalProperties: false }), Type8.Null()),
8760
+ add: rpc(obj8({ path: text3 }), WorkspaceSummarySchema),
8761
+ remove: rpc(obj8({ path: text3 }), nul3()),
8574
8762
  browse: rpc(browse, page)
8575
8763
  }
8576
8764
  }
8577
8765
  }
8578
8766
  });
8579
- provider = Type8.String({ minLength: 1, maxLength: 64 });
8767
+ provider = str8({ minLength: 1, maxLength: 64 });
8580
8768
  NekoCredentials = contract("neko.credentials", {
8581
8769
  version: 2,
8582
- input: Type8.Object({}, { additionalProperties: false }),
8770
+ input: obj8({}),
8583
8771
  variants: {
8584
8772
  ready: {
8585
8773
  state: { state: CredentialStateSchema },
8586
8774
  methods: {
8587
- save: rpc(Type8.Object({ provider, key: Type8.String({ minLength: 1, maxLength: 8192 }) }, { additionalProperties: false }), nil),
8588
- remove: rpc(Type8.Object({ provider }, { additionalProperties: false }), nil),
8589
- setEnabled: rpc(Type8.Object({ provider, enabled: bool }, { additionalProperties: false }), nil)
8775
+ save: rpc(obj8({ provider, key: str8({ minLength: 1, maxLength: 8192 }) }), nil),
8776
+ remove: rpc(obj8({ provider }), nil),
8777
+ setEnabled: rpc(obj8({ provider, enabled: boolean }), nil)
8590
8778
  }
8591
8779
  }
8592
8780
  }
8593
8781
  });
8594
- SearchSettingsStateSchema = Type8.Object({
8782
+ SearchSettingsStateSchema = obj8({
8595
8783
  revision: integer,
8596
- selected: Type8.Enum(["tavily", "deepseek"])
8597
- });
8784
+ selected: oneOf2(["tavily", "deepseek"])
8785
+ }, { open: true });
8598
8786
  NekoSearchSettings = contract("neko.search-settings", {
8599
8787
  version: 1,
8600
- input: Type8.Object({}, { additionalProperties: false }),
8788
+ input: obj8({}),
8601
8789
  variants: {
8602
8790
  ready: {
8603
8791
  state: { state: SearchSettingsStateSchema },
8604
8792
  methods: {
8605
- set: rpc(Type8.Object({ provider: Type8.Enum(["tavily", "deepseek"]) }, { additionalProperties: false }), nil)
8793
+ set: rpc(obj8({ provider: oneOf2(["tavily", "deepseek"]) }), nil)
8606
8794
  }
8607
8795
  }
8608
8796
  }
8609
8797
  });
8610
- contextSource = Type8.Enum(["default", "config", "saved"]);
8611
- ContextSettingsStateSchema = Type8.Object({
8798
+ contextSource = oneOf2(["default", "config", "saved"]);
8799
+ ContextSettingsStateSchema = obj8({
8612
8800
  revision: integer,
8613
- effective: Type8.Object({
8614
- compaction: Type8.Object({
8615
- enabled: bool,
8616
- triggerPercent: Type8.Union([integer, nil]),
8801
+ effective: obj8({
8802
+ compaction: obj8({
8803
+ enabled: boolean,
8804
+ triggerPercent: union4([integer, nil]),
8617
8805
  keepRecentTokens: integer
8618
- }),
8619
- pruning: Type8.Object({
8620
- enabled: bool,
8806
+ }, { open: true }),
8807
+ pruning: obj8({
8808
+ enabled: boolean,
8621
8809
  triggerPercent: integer,
8622
8810
  keepRecent: integer,
8623
8811
  minChars: integer,
8624
8812
  minSavePercent: integer
8625
- })
8626
- }),
8627
- sources: Type8.Object({
8628
- compaction: Type8.Object({ enabled: contextSource, triggerPercent: contextSource, keepRecentTokens: contextSource }),
8629
- pruning: Type8.Object({
8813
+ }, { open: true })
8814
+ }, { open: true }),
8815
+ sources: obj8({
8816
+ compaction: obj8({ enabled: contextSource, triggerPercent: contextSource, keepRecentTokens: contextSource }, { open: true }),
8817
+ pruning: obj8({
8630
8818
  enabled: contextSource,
8631
8819
  triggerPercent: contextSource,
8632
8820
  keepRecent: contextSource,
8633
8821
  minChars: contextSource,
8634
8822
  minSavePercent: contextSource
8635
- })
8636
- })
8823
+ }, { open: true })
8824
+ }, { open: true })
8825
+ }, { open: true });
8826
+ ContextSettingsPatchSchema = obj8({
8827
+ compaction: opt8(obj8({
8828
+ enabled: opt8(nullable(boolean)),
8829
+ triggerPercent: opt8(nullable(integer)),
8830
+ keepRecentTokens: opt8(nullable(integer))
8831
+ })),
8832
+ pruning: opt8(obj8({
8833
+ enabled: opt8(nullable(boolean)),
8834
+ triggerPercent: opt8(nullable(integer)),
8835
+ keepRecent: opt8(nullable(integer)),
8836
+ minChars: opt8(nullable(integer)),
8837
+ minSavePercent: opt8(nullable(integer))
8838
+ }))
8637
8839
  });
8638
- ContextSettingsPatchSchema = Type8.Object({
8639
- compaction: Type8.Optional(Type8.Object({
8640
- enabled: Type8.Optional(nullable(bool)),
8641
- triggerPercent: Type8.Optional(nullable(integer)),
8642
- keepRecentTokens: Type8.Optional(nullable(integer))
8643
- }, { additionalProperties: false })),
8644
- pruning: Type8.Optional(Type8.Object({
8645
- enabled: Type8.Optional(nullable(bool)),
8646
- triggerPercent: Type8.Optional(nullable(integer)),
8647
- keepRecent: Type8.Optional(nullable(integer)),
8648
- minChars: Type8.Optional(nullable(integer)),
8649
- minSavePercent: Type8.Optional(nullable(integer))
8650
- }, { additionalProperties: false }))
8651
- }, { additionalProperties: false });
8652
8840
  NekoContextSettings = contract("neko.context-settings", {
8653
8841
  version: 1,
8654
- input: Type8.Object({}, { additionalProperties: false }),
8842
+ input: obj8({}),
8655
8843
  variants: {
8656
8844
  ready: {
8657
8845
  state: { state: ContextSettingsStateSchema },
8658
8846
  methods: {
8659
8847
  set: rpc(ContextSettingsPatchSchema, nil),
8660
- reset: rpc(Type8.Object({}, { additionalProperties: false }), nil)
8848
+ reset: rpc(obj8({}), nil)
8661
8849
  }
8662
8850
  }
8663
8851
  }
8664
8852
  });
8665
- CodemodeSettingsStateSchema = Type8.Object({
8853
+ CodemodeSettingsStateSchema = obj8({
8666
8854
  revision: integer,
8667
- effective: Type8.Object({ mode: Type8.Enum([...CODEMODE_MODES]) }),
8668
- source: Type8.Enum(["default", "config", "saved"])
8669
- });
8855
+ effective: obj8({ mode: oneOf2([...CODEMODE_MODES]) }, { open: true }),
8856
+ source: oneOf2(["default", "config", "saved"])
8857
+ }, { open: true });
8670
8858
  NekoCodemodeSettings = contract("neko.codemode-settings", {
8671
8859
  version: 1,
8672
- input: Type8.Object({}, { additionalProperties: false }),
8860
+ input: obj8({}),
8673
8861
  variants: {
8674
8862
  ready: {
8675
8863
  state: { state: CodemodeSettingsStateSchema },
8676
8864
  methods: {
8677
- set: rpc(Type8.Object({ mode: Type8.Enum([...CODEMODE_MODES]) }, { additionalProperties: false }), nil),
8678
- reset: rpc(Type8.Object({}, { additionalProperties: false }), nil)
8865
+ set: rpc(obj8({ mode: oneOf2([...CODEMODE_MODES]) }), nil),
8866
+ reset: rpc(obj8({}), nil)
8679
8867
  }
8680
8868
  }
8681
8869
  }
8682
8870
  });
8683
- defaultModelSource = Type8.Enum(["cli", "saved", "config", "default"]);
8684
- DefaultModelStateSchema = Type8.Object({
8871
+ defaultModelSource = oneOf2(["cli", "saved", "config", "default"]);
8872
+ DefaultModelStateSchema = obj8({
8685
8873
  effective: model2,
8686
8874
  source: defaultModelSource
8687
- });
8875
+ }, { open: true });
8688
8876
  NekoDefaultModel = contract("neko.default-model", {
8689
8877
  version: 1,
8690
- input: Type8.Object({}, { additionalProperties: false }),
8878
+ input: obj8({}),
8691
8879
  variants: {
8692
8880
  ready: {
8693
8881
  state: { state: DefaultModelStateSchema },
@@ -8698,74 +8886,76 @@ var init_contract = __esm(() => {
8698
8886
  }
8699
8887
  }
8700
8888
  });
8701
- FileCandidatesSchema = Type8.Object({
8702
- entries: Type8.Array(Type8.Object({ name: str, path: str, kind: Type8.Enum(["file", "directory"]) }), { maxItems: 100 }),
8703
- limited: bool,
8704
- incomplete: bool
8705
- });
8889
+ FileCandidatesSchema = obj8({
8890
+ entries: arr6(obj8({ name: text3, path: text3, kind: oneOf2(["file", "directory"]) }, { open: true }), {
8891
+ maxItems: 100
8892
+ }),
8893
+ limited: boolean,
8894
+ incomplete: boolean
8895
+ }, { open: true });
8706
8896
  NekoWorkspaceFiles = contract("neko.workspace-files", {
8707
8897
  version: 1,
8708
- input: Type8.Object({}, { additionalProperties: false }),
8898
+ input: obj8({}),
8709
8899
  variants: {
8710
8900
  ready: {
8711
8901
  methods: {
8712
- search: rpc(Type8.Object({ workspaceRoot: str, query: str }, { additionalProperties: false }), FileCandidatesSchema)
8902
+ search: rpc(obj8({ workspaceRoot: text3, query: text3 }), FileCandidatesSchema)
8713
8903
  }
8714
8904
  }
8715
8905
  }
8716
8906
  });
8717
- marketSkill = Type8.Object({
8718
- name: str,
8719
- description: str,
8720
- category: str,
8721
- source: str,
8722
- license: str,
8723
- version: str,
8724
- sha256: str,
8907
+ marketSkill = obj8({
8908
+ name: text3,
8909
+ description: text3,
8910
+ category: text3,
8911
+ source: text3,
8912
+ license: text3,
8913
+ version: text3,
8914
+ sha256: text3,
8725
8915
  sizeBytes: integer,
8726
- uploadedAt: str,
8727
- installed: Type8.Object({ project: bool, user: bool, builtin: bool }),
8728
- activeLevel: Type8.Union([level, nil])
8729
- });
8730
- preview = Type8.Object({ body: str, files: Type8.Array(str) });
8731
- scope = Type8.Enum(["user", "project"]);
8732
- installResult = Type8.Object({ name: str, scope, path: str, shadowedBy: Type8.Optional(scope) });
8916
+ uploadedAt: text3,
8917
+ installed: obj8({ project: boolean, user: boolean, builtin: boolean }, { open: true }),
8918
+ activeLevel: union4([level, nil])
8919
+ }, { open: true });
8920
+ preview = obj8({ body: text3, files: arr6(text3) }, { open: true });
8921
+ scope = oneOf2(["user", "project"]);
8922
+ installResult = obj8({ name: text3, scope, path: text3, shadowedBy: opt8(scope) }, { open: true });
8733
8923
  NekoSkillMarket = contract("neko.skill-market", {
8734
8924
  version: 1,
8735
- input: Type8.Object({}, { additionalProperties: false }),
8925
+ input: obj8({}),
8736
8926
  variants: {
8737
8927
  ready: {
8738
8928
  methods: {
8739
- list: rpc(Type8.Object({ query: Type8.Optional(str), workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), Type8.Array(marketSkill)),
8740
- detail: rpc(Type8.Object({ name: str, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), marketSkill),
8741
- preview: rpc(Type8.Object({ name: str }, { additionalProperties: false }), preview),
8742
- install: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str), overwrite: Type8.Optional(bool) }, { additionalProperties: false }), installResult),
8743
- uninstall: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), nil)
8929
+ list: rpc(obj8({ query: opt8(text3), workspaceRoot: opt8(text3) }), arr6(marketSkill)),
8930
+ detail: rpc(obj8({ name: text3, workspaceRoot: opt8(text3) }), marketSkill),
8931
+ preview: rpc(obj8({ name: text3 }), preview),
8932
+ install: rpc(obj8({ name: text3, scope, workspaceRoot: opt8(text3), overwrite: opt8(boolean) }), installResult),
8933
+ uninstall: rpc(obj8({ name: text3, scope, workspaceRoot: opt8(text3) }), nil)
8744
8934
  }
8745
8935
  }
8746
8936
  }
8747
8937
  });
8748
- ShellStatusSchema = Type8.Union([
8749
- Type8.Object({ status: Type8.Literal("ready"), path: str, source: Type8.Enum(["config", "auto"]) }),
8750
- Type8.Object({
8751
- status: Type8.Literal("missing"),
8752
- reason: Type8.Enum(["not_found", "invalid_config"]),
8753
- detail: Type8.Optional(str)
8754
- })
8938
+ ShellStatusSchema = union4([
8939
+ obj8({ status: lit3("ready"), path: text3, source: oneOf2(["config", "auto"]) }, { open: true }),
8940
+ obj8({
8941
+ status: lit3("missing"),
8942
+ reason: oneOf2(["not_found", "invalid_config"]),
8943
+ detail: opt8(text3)
8944
+ }, { open: true })
8755
8945
  ]);
8756
- ApplicationInfoSchema = Type8.Object({
8757
- runningVersion: str,
8946
+ ApplicationInfoSchema = obj8({
8947
+ runningVersion: text3,
8758
8948
  installedVersion: nullableString,
8759
- protocolVersion: Type8.Integer({ minimum: 1 }),
8949
+ protocolVersion: int5({ minimum: 1 }),
8760
8950
  latestVersion: nullableString,
8761
- checkedAt: Type8.Union([integer, nil]),
8762
- checkState: Type8.Enum(["unknown", "checking", "ok", "failed", "development"]),
8763
- serverId: str,
8764
- shell: Type8.Optional(ShellStatusSchema)
8765
- });
8951
+ checkedAt: union4([integer, nil]),
8952
+ checkState: oneOf2(["unknown", "checking", "ok", "failed", "development"]),
8953
+ serverId: text3,
8954
+ shell: opt8(ShellStatusSchema)
8955
+ }, { open: true });
8766
8956
  NekoApplicationInfo = contract("neko.application-info", {
8767
8957
  version: 1,
8768
- input: Type8.Object({}),
8958
+ input: obj8({}, { open: true }),
8769
8959
  variants: {
8770
8960
  ready: { state: { info: ApplicationInfoSchema }, methods: { check: rpc(nil, ApplicationInfoSchema) } }
8771
8961
  }
@@ -9147,6 +9337,20 @@ function planClient(chat) {
9147
9337
  }
9148
9338
  };
9149
9339
  }
9340
+ function continueClient(chat) {
9341
+ const supported = () => chat.view.value?.continuable === true;
9342
+ return {
9343
+ get supported() {
9344
+ return supported();
9345
+ },
9346
+ failure: () => supported() ? chat.view.value?.runFailure : undefined,
9347
+ async continueRun() {
9348
+ if (!supported())
9349
+ throw new Error(CONTINUE_UNSUPPORTED);
9350
+ await chat.continueRun(null);
9351
+ }
9352
+ };
9353
+ }
9150
9354
  function createGatewayConnection(link, serverId, options = {}) {
9151
9355
  const readyTimeoutMs = options.readyTimeoutMs ?? READY_TIMEOUT_MS;
9152
9356
  let protocolMismatch = false;
@@ -9356,7 +9560,7 @@ function deadline(work, signal, timeoutMs) {
9356
9560
  signal?.removeEventListener("abort", abort);
9357
9561
  });
9358
9562
  }
9359
- var GatewayIdentityError, JOBS_UNSUPPORTED = "This gateway does not support background jobs. Restart the gateway to enable them.", QUEUE_UNSUPPORTED = "This gateway does not support queued message controls. Restart the gateway to enable them.", TODOS_UNSUPPORTED = "This gateway does not support todos. Restart the gateway to enable them.", QUESTIONS_UNSUPPORTED = "This gateway does not support questions. Restart the gateway to enable them.", NO_QUESTIONS, PLAN_UNSUPPORTED = "This gateway does not support plan mode. Restart the gateway to enable it.", READY_TIMEOUT_MS = 5000;
9563
+ var GatewayIdentityError, JOBS_UNSUPPORTED = "This gateway does not support background jobs. Restart the gateway to enable them.", QUEUE_UNSUPPORTED = "This gateway does not support queued message controls. Restart the gateway to enable them.", TODOS_UNSUPPORTED = "This gateway does not support todos. Restart the gateway to enable them.", QUESTIONS_UNSUPPORTED = "This gateway does not support questions. Restart the gateway to enable them.", NO_QUESTIONS, PLAN_UNSUPPORTED = "This gateway does not support plan mode. Restart the gateway to enable it.", CONTINUE_UNSUPPORTED = "This gateway cannot continue a run. Restart the gateway to enable it.", READY_TIMEOUT_MS = 5000;
9360
9564
  var init_client_session = __esm(() => {
9361
9565
  init_application_version();
9362
9566
  init_contract();
@@ -9978,7 +10182,7 @@ var init_store = __esm(() => {
9978
10182
  // packages/gateway/src/mobile/admin.ts
9979
10183
  import { createHash, timingSafeEqual } from "crypto";
9980
10184
  import { ChordError } from "@earendil-works/chord";
9981
- import { Type as Type9 } from "typebox";
10185
+ 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";
9982
10186
  function digest(value) {
9983
10187
  return createHash("sha256").update(value, "utf8").digest();
9984
10188
  }
@@ -10048,62 +10252,56 @@ var init_admin = __esm(() => {
10048
10252
  init_contract();
10049
10253
  init_principal();
10050
10254
  init_store();
10051
- listenerStateSchema = Type9.Union([
10052
- Type9.Object({ status: Type9.Literal("disabled") }),
10053
- Type9.Object({ status: Type9.Literal("starting") }),
10054
- Type9.Object({
10055
- status: Type9.Literal("listening"),
10056
- address: Type9.String(),
10057
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
10058
- bindAddress: Type9.Optional(Type9.String()),
10059
- mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
10060
- }),
10061
- Type9.Object({ status: Type9.Literal("error"), error: Type9.String() }),
10062
- Type9.Object({ status: Type9.Literal("stopping") })
10255
+ listenerStateSchema = union5([
10256
+ obj9({ status: lit4("disabled") }, { open: true }),
10257
+ obj9({ status: lit4("starting") }, { open: true }),
10258
+ obj9({
10259
+ status: lit4("listening"),
10260
+ address: str9(),
10261
+ port: int6({ minimum: 1, maximum: 65535 }),
10262
+ bindAddress: opt9(str9()),
10263
+ mode: opt9(union5([lit4("direct"), lit4("wsl-relay")]))
10264
+ }, { open: true }),
10265
+ obj9({ status: lit4("error"), error: str9() }, { open: true }),
10266
+ obj9({ status: lit4("stopping") }, { open: true })
10063
10267
  ]);
10064
- adminDeviceSchema = Type9.Object({
10065
- deviceId: Type9.String(),
10066
- name: Type9.String(),
10067
- pairedAt: Type9.Integer(),
10068
- lastSeenAt: Type9.Integer(),
10069
- online: Type9.Boolean()
10070
- });
10268
+ adminDeviceSchema = obj9({
10269
+ deviceId: str9(),
10270
+ name: str9(),
10271
+ pairedAt: int6(),
10272
+ lastSeenAt: int6(),
10273
+ online: bool4()
10274
+ }, { open: true });
10071
10275
  NekoMobileAdmin = contract("neko.mobile-admin", {
10072
10276
  version: 1,
10073
- input: Type9.Object({ token: Type9.String() }),
10277
+ input: obj9({ token: str9() }, { open: true }),
10074
10278
  variants: {
10075
10279
  admin: {
10076
10280
  methods: {
10077
- state: rpc(Type9.Null(), Type9.Object({
10281
+ state: rpc(nul4(), obj9({
10078
10282
  state: listenerStateSchema,
10079
- candidates: Type9.Array(Type9.String()),
10080
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
10081
- certificate: Type9.Union([
10082
- Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }),
10083
- Type9.Null()
10084
- ]),
10085
- devices: Type9.Array(adminDeviceSchema),
10086
- lastConnectedAddress: Type9.Union([Type9.String(), Type9.Null()]),
10087
- wsl: Type9.Optional(Type9.Union([
10088
- Type9.Object({ interop: Type9.Boolean(), error: Type9.Union([Type9.String(), Type9.Null()]) }),
10089
- Type9.Null()
10090
- ]))
10091
- })),
10092
- enable: rpc(Type9.Object({
10093
- address: Type9.String(),
10094
- port: Type9.Optional(Type9.Integer({ minimum: 1, maximum: 65535 }))
10095
- }), Type9.Object({
10096
- status: Type9.Literal("listening"),
10097
- address: Type9.String(),
10098
- port: Type9.Integer({ minimum: 1, maximum: 65535 }),
10099
- bindAddress: Type9.Optional(Type9.String()),
10100
- mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
10101
- })),
10102
- disable: rpc(Type9.Null(), listenerStateSchema),
10103
- createPairing: rpc(Type9.Null(), Type9.Object({ pairingText: Type9.String(), expiresAt: Type9.Integer() })),
10104
- cancelPairing: rpc(Type9.Null(), Type9.Null()),
10105
- revoke: rpc(Type9.Object({ deviceId: Type9.String({ minLength: 1 }) }), Type9.Object({ revoked: Type9.Boolean() })),
10106
- regenerateCertificate: rpc(Type9.Null(), Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }))
10283
+ candidates: arr7(str9()),
10284
+ port: int6({ minimum: 1, maximum: 65535 }),
10285
+ certificate: union5([obj9({ fingerprint: str9(), expiresAt: int6() }, { open: true }), nul4()]),
10286
+ devices: arr7(adminDeviceSchema),
10287
+ lastConnectedAddress: union5([str9(), nul4()]),
10288
+ wsl: opt9(union5([obj9({ interop: bool4(), error: union5([str9(), nul4()]) }, { open: true }), nul4()]))
10289
+ }, { open: true })),
10290
+ enable: rpc(obj9({
10291
+ address: str9(),
10292
+ port: opt9(int6({ minimum: 1, maximum: 65535 }))
10293
+ }, { open: true }), obj9({
10294
+ status: lit4("listening"),
10295
+ address: str9(),
10296
+ port: int6({ minimum: 1, maximum: 65535 }),
10297
+ bindAddress: opt9(str9()),
10298
+ mode: opt9(union5([lit4("direct"), lit4("wsl-relay")]))
10299
+ }, { open: true })),
10300
+ disable: rpc(nul4(), listenerStateSchema),
10301
+ createPairing: rpc(nul4(), obj9({ pairingText: str9(), expiresAt: int6() }, { open: true })),
10302
+ cancelPairing: rpc(nul4(), nul4()),
10303
+ revoke: rpc(obj9({ deviceId: str9({ minLength: 1 }) }, { open: true }), obj9({ revoked: bool4() }, { open: true })),
10304
+ regenerateCertificate: rpc(nul4(), obj9({ fingerprint: str9(), expiresAt: int6() }, { open: true }))
10107
10305
  }
10108
10306
  }
10109
10307
  }
@@ -10125,8 +10323,19 @@ function relativeTime(at, now, language = "en") {
10125
10323
  var sessionName = (session, empty = "New session") => session.title || session.firstPromptPreview || empty, messageTime = (session) => session.lastMessageAt ?? session.createdAt, byMessage = (a, b) => messageTime(b) - messageTime(a) || Number(b.id) - Number(a.id), byVisited = (a, b) => b.lastUsedAt - a.lastUsedAt || Number(b.id) - Number(a.id);
10126
10324
 
10127
10325
  // packages/gateway/src/mobile/protocol.ts
10128
- import { Type as Type10 } from "typebox";
10129
- import { Check } from "typebox/value";
10326
+ import {
10327
+ arr as arr8,
10328
+ bool as bool5,
10329
+ compile as compile2,
10330
+ int as int7,
10331
+ lit as lit5,
10332
+ nul as nul5,
10333
+ num as num5,
10334
+ obj as obj10,
10335
+ opt as opt10,
10336
+ str as str10,
10337
+ union as union6
10338
+ } from "@earendil-works/pi-schema";
10130
10339
  function asJsonRpcRequest(value) {
10131
10340
  if (typeof value !== "object" || value === null)
10132
10341
  return;
@@ -10139,8 +10348,16 @@ function asJsonRpcRequest(value) {
10139
10348
  return;
10140
10349
  return { jsonrpc: "2.0", id: record.id, method: record.method, params: record.params };
10141
10350
  }
10351
+ function checkSchema(schema, value) {
10352
+ let validator = validators.get(schema);
10353
+ if (!validator) {
10354
+ validator = compile2(schema);
10355
+ validators.set(schema, validator);
10356
+ }
10357
+ return validator.check(value);
10358
+ }
10142
10359
  function isMobileHelloParams(value) {
10143
- return Check(MobileHelloParams, value);
10360
+ return helloParams.check(value);
10144
10361
  }
10145
10362
  function negotiateApiVersion(clientVersions) {
10146
10363
  let best;
@@ -10165,23 +10382,23 @@ function buildHelloResult(serverId, serverName, apiVersion) {
10165
10382
  };
10166
10383
  }
10167
10384
  function buildHelloResultSchema() {
10168
- return Type10.Object({
10169
- apiVersion: Type10.Integer({ minimum: 1 }),
10170
- serverId: str2,
10171
- serverName: str2,
10172
- gatewayVersion: str2,
10173
- capabilities: Type10.Array(str2),
10174
- limits: Type10.Object({
10175
- maxFrameBytes: Type10.Integer(),
10176
- maxPayloadBytes: Type10.Integer(),
10177
- pageItems: Type10.Integer(),
10178
- chunkBytes: Type10.Integer(),
10179
- maxConcurrentRequests: Type10.Integer(),
10180
- maxSendBufferBytes: Type10.Integer()
10181
- })
10182
- });
10183
- }
10184
- var MOBILE_LIMITS, MOBILE_API_VERSIONS, MOBILE_CAPABILITIES, MOBILE_CLOSE_REVOKED = 4401, MOBILE_CLOSE_INCOMPATIBLE = 4402, MOBILE_CLOSE_SLOW_CONSUMER = 4408, MobileHelloParams, opt, str2, int, nullable2 = (schema) => Type10.Union([schema, Type10.Null()]), MobileContentRefSchema, MobileImageRefSchema, planStatus, questionStatus2, MobileToolInteractionSchema, MobileCodemodeSchema, MobileToolItemSchema, MobileChatItemSchema, MobileStreamingSchema, MobileUsageSchema, MobileSubagentRoundSchema, MobileAgentSchema, MobileQueuedSchema, MobileChatStatusSchema, MobileChatSnapshotSchema, MobileWorkspaceSchema, MobileSessionSummarySchema, MobileApprovalSchema, MobileTodoItemSchema, MobileTodoListSchema, MobileQuestionOptionSchema, MobileQuestionSummarySchema, MobileQuestionListSchema, MobileQuestionSchema, MobileQuestionResponseSchema, MobilePlanSchema, MobilePlanDecisionSchema, sessionListParams, sessionListResult, subscription, pageResult, pageParams, chunkResult, MOBILE_API_V1_METHODS, patchBody, MOBILE_API_V1_NOTIFICATIONS;
10385
+ return obj10({
10386
+ apiVersion: int7({ minimum: 1 }),
10387
+ serverId: text4,
10388
+ serverName: text4,
10389
+ gatewayVersion: text4,
10390
+ capabilities: arr8(text4),
10391
+ limits: obj10({
10392
+ maxFrameBytes: int7(),
10393
+ maxPayloadBytes: int7(),
10394
+ pageItems: int7(),
10395
+ chunkBytes: int7(),
10396
+ maxConcurrentRequests: int7(),
10397
+ maxSendBufferBytes: int7()
10398
+ }, { open: true })
10399
+ }, { open: true });
10400
+ }
10401
+ var MOBILE_LIMITS, MOBILE_API_VERSIONS, MOBILE_CAPABILITIES, MOBILE_CLOSE_REVOKED = 4401, MOBILE_CLOSE_INCOMPATIBLE = 4402, MOBILE_CLOSE_SLOW_CONSUMER = 4408, MobileHelloParams, validators, helloParams, text4, count, nullable2 = (schema) => union6([schema, nul5()]), MobileContentRefSchema, MobileImageRefSchema, planStatus, questionStatus2, MobileToolInteractionSchema, MobileCodemodeSchema, MobileToolItemSchema, MobileChatItemSchema, MobileStreamingSchema, MobileUsageSchema, MobileSubagentRoundSchema, MobileAgentSchema, MobileQueuedSchema, mobileChatStatusProperties, MobileChatStatusSchema, MobileChatSnapshotSchema, MobileWorkspaceSchema, MobileSessionSummarySchema, MobileApprovalSchema, MobileTodoItemSchema, MobileTodoListSchema, MobileQuestionOptionSchema, MobileQuestionSummarySchema, MobileQuestionListSchema, MobileQuestionSchema, MobileQuestionResponseSchema, MobilePlanSchema, MobilePlanDecisionSchema, sessionListParams, sessionListResult, subscription, pageResult, pageParams, chunkResultProperties, chunkResult, MOBILE_API_V1_METHODS, patchBody, MOBILE_API_V1_NOTIFICATIONS;
10185
10402
  var init_protocol = __esm(() => {
10186
10403
  init_application_version();
10187
10404
  init_codemode_mode();
@@ -10206,470 +10423,436 @@ var init_protocol = __esm(() => {
10206
10423
  "questions",
10207
10424
  "plan"
10208
10425
  ];
10209
- MobileHelloParams = Type10.Object({
10210
- apiVersions: Type10.Array(Type10.Integer(), { minItems: 1 }),
10211
- app: Type10.Object({
10212
- version: Type10.String({ minLength: 1, maxLength: 64 }),
10213
- platform: Type10.String({ minLength: 1, maxLength: 64 }),
10214
- build: Type10.String({ maxLength: 128 })
10215
- })
10216
- });
10217
- opt = Type10.Optional;
10218
- str2 = Type10.String();
10219
- int = Type10.Integer({ minimum: 0 });
10220
- MobileContentRefSchema = Type10.Object({
10221
- sessionId: str2,
10222
- agentId: opt(str2),
10223
- key: str2,
10224
- field: str2,
10225
- digest: str2,
10226
- bytes: int
10227
- });
10228
- MobileImageRefSchema = Type10.Object({
10229
- name: str2,
10230
- mimeType: str2,
10231
- width: int,
10232
- height: int,
10233
- bytes: int,
10234
- entryId: int,
10235
- contentIndex: int
10236
- });
10237
- planStatus = Type10.Union([
10238
- Type10.Literal("awaiting_approval"),
10239
- Type10.Literal("approved"),
10240
- Type10.Literal("revision_requested"),
10241
- Type10.Literal("superseded"),
10242
- Type10.Literal("cancelled")
10426
+ MobileHelloParams = obj10({
10427
+ apiVersions: arr8(int7(), { minItems: 1 }),
10428
+ app: obj10({
10429
+ version: str10({ minLength: 1, maxLength: 64 }),
10430
+ platform: str10({ minLength: 1, maxLength: 64 }),
10431
+ build: str10({ maxLength: 128 })
10432
+ }, { open: true })
10433
+ }, { open: true });
10434
+ validators = new WeakMap;
10435
+ helloParams = compile2(MobileHelloParams);
10436
+ text4 = str10();
10437
+ count = int7({ minimum: 0 });
10438
+ MobileContentRefSchema = obj10({
10439
+ sessionId: text4,
10440
+ agentId: opt10(text4),
10441
+ key: text4,
10442
+ field: text4,
10443
+ digest: text4,
10444
+ bytes: count
10445
+ }, { open: true });
10446
+ MobileImageRefSchema = obj10({
10447
+ name: text4,
10448
+ mimeType: text4,
10449
+ width: count,
10450
+ height: count,
10451
+ bytes: count,
10452
+ entryId: count,
10453
+ contentIndex: count
10454
+ }, { open: true });
10455
+ planStatus = union6([
10456
+ lit5("awaiting_approval"),
10457
+ lit5("approved"),
10458
+ lit5("revision_requested"),
10459
+ lit5("superseded"),
10460
+ lit5("cancelled")
10243
10461
  ]);
10244
- questionStatus2 = Type10.Union([
10245
- Type10.Literal("pending"),
10246
- Type10.Literal("answered"),
10247
- Type10.Literal("skipped"),
10248
- Type10.Literal("cancelled")
10249
- ]);
10250
- MobileToolInteractionSchema = Type10.Union([
10251
- Type10.Object({
10252
- kind: Type10.Literal("ask_user"),
10253
- id: int,
10254
- question: str2,
10255
- multiple: Type10.Boolean(),
10256
- options: Type10.Array(Type10.Object({ id: str2, label: str2, recommended: opt(Type10.Boolean()) })),
10462
+ questionStatus2 = union6([lit5("pending"), lit5("answered"), lit5("skipped"), lit5("cancelled")]);
10463
+ MobileToolInteractionSchema = union6([
10464
+ obj10({
10465
+ kind: lit5("ask_user"),
10466
+ id: count,
10467
+ question: text4,
10468
+ multiple: bool5(),
10469
+ options: arr8(obj10({ id: text4, label: text4, recommended: opt10(bool5()) }, { open: true })),
10257
10470
  status: questionStatus2,
10258
- optionIds: opt(Type10.Array(str2)),
10259
- labels: opt(Type10.Array(str2)),
10260
- text: opt(nullable2(str2))
10261
- }),
10262
- Type10.Object({
10263
- kind: Type10.Literal("todo"),
10264
- action: Type10.Union([Type10.Literal("list"), Type10.Literal("replace")]),
10265
- revision: int,
10266
- completed: int,
10267
- total: int
10268
- }),
10269
- Type10.Object({
10270
- kind: Type10.Literal("submit_plan"),
10271
- version: str2,
10272
- title: str2,
10273
- steps: Type10.Array(str2),
10274
- note: str2
10275
- })
10471
+ optionIds: opt10(arr8(text4)),
10472
+ labels: opt10(arr8(text4)),
10473
+ text: opt10(nullable2(text4))
10474
+ }, { open: true }),
10475
+ obj10({
10476
+ kind: lit5("todo"),
10477
+ action: union6([lit5("list"), lit5("replace")]),
10478
+ revision: count,
10479
+ completed: count,
10480
+ total: count
10481
+ }, { open: true }),
10482
+ obj10({
10483
+ kind: lit5("submit_plan"),
10484
+ version: text4,
10485
+ title: text4,
10486
+ steps: arr8(text4),
10487
+ note: text4
10488
+ }, { open: true })
10276
10489
  ]);
10277
- MobileCodemodeSchema = Type10.Object({
10278
- calls: Type10.Array(Type10.Object({
10279
- id: int,
10280
- name: str2,
10281
- args: Type10.String({ maxLength: CODEMODE_VIEW_LIMITS.argsChars }),
10282
- status: Type10.Union([
10283
- Type10.Literal("running"),
10284
- Type10.Literal("ok"),
10285
- Type10.Literal("error"),
10286
- Type10.Literal("cancelled")
10287
- ]),
10288
- durationMs: opt(Type10.Number()),
10289
- error: opt(Type10.String({ maxLength: CODEMODE_VIEW_LIMITS.errorChars }))
10290
- }), { maxItems: CODEMODE_VIEW_LIMITS.maxCalls }),
10291
- omitted: int,
10292
- fullOutputPath: opt(str2)
10293
- });
10294
- MobileToolItemSchema = Type10.Object({
10295
- callId: str2,
10296
- name: str2,
10297
- summary: str2,
10298
- status: Type10.Union([Type10.Literal("running"), Type10.Literal("done"), Type10.Literal("error")]),
10299
- detail: opt(str2),
10300
- output: opt(str2),
10301
- result: opt(str2),
10302
- diff: opt(str2),
10303
- diffAdded: opt(Type10.Number()),
10304
- diffRemoved: opt(Type10.Number()),
10305
- diffTruncated: opt(Type10.Boolean()),
10306
- durationMs: opt(Type10.Number()),
10307
- note: opt(str2),
10308
- interaction: opt(MobileToolInteractionSchema),
10309
- codemode: opt(MobileCodemodeSchema)
10310
- });
10311
- MobileChatItemSchema = Type10.Object({
10312
- key: str2,
10313
- type: Type10.Union([
10314
- Type10.Literal("user"),
10315
- Type10.Literal("assistant"),
10316
- Type10.Literal("tool"),
10317
- Type10.Literal("error"),
10318
- Type10.Literal("system")
10319
- ]),
10320
- text: opt(str2),
10321
- thinking: opt(str2),
10322
- note: opt(str2),
10323
- error: opt(str2),
10324
- skill: opt(str2),
10325
- jobId: opt(str2),
10326
- tool: opt(MobileToolItemSchema),
10327
- images: opt(Type10.Array(MobileImageRefSchema)),
10328
- refs: opt(Type10.Array(MobileContentRefSchema))
10329
- });
10330
- MobileStreamingSchema = Type10.Object({
10331
- text: str2,
10332
- thinking: str2,
10333
- textOmitted: int,
10334
- thinkingOmitted: int,
10335
- toolCalls: Type10.Array(Type10.Object({
10336
- callId: str2,
10337
- name: str2,
10338
- summary: str2,
10339
- status: Type10.Union([Type10.Literal("running"), Type10.Literal("done")])
10340
- }))
10341
- });
10342
- MobileUsageSchema = Type10.Object({
10343
- input: int,
10344
- output: int,
10345
- cacheRead: int,
10346
- cacheWrite: int,
10347
- totalTokens: int,
10348
- cost: Type10.Number({ minimum: 0 })
10349
- });
10350
- MobileSubagentRoundSchema = Type10.Object({
10351
- taskId: int,
10352
- callId: str2,
10353
- agentId: str2,
10354
- round: opt(int),
10355
- role: str2,
10356
- title: str2,
10357
- status: Type10.Union([
10358
- Type10.Literal("queued"),
10359
- Type10.Literal("running"),
10360
- Type10.Literal("completed"),
10361
- Type10.Literal("failed"),
10362
- Type10.Literal("cancelled")
10363
- ]),
10364
- approvalCount: int,
10365
- readonly: Type10.Boolean(),
10366
- createdAt: Type10.Number(),
10367
- endedAt: nullable2(Type10.Number()),
10368
- usage: opt(MobileUsageSchema)
10369
- });
10370
- MobileAgentSchema = Type10.Object({
10371
- agentId: str2,
10372
- role: str2,
10373
- title: str2,
10374
- status: Type10.Union([Type10.Literal("idle"), Type10.Literal("queued"), Type10.Literal("running")]),
10375
- activeRoundId: nullable2(int),
10376
- lastRoundId: int,
10377
- roundCount: int,
10378
- approvalCount: int
10379
- });
10380
- MobileQueuedSchema = Type10.Object({
10381
- id: int,
10382
- mode: Type10.Union([Type10.Literal("steer"), Type10.Literal("followUp")]),
10383
- preview: Type10.String(),
10384
- images: int
10385
- });
10386
- MobileChatStatusSchema = Type10.Object({
10387
- busy: Type10.Boolean(),
10388
- queued: opt(Type10.Array(MobileQueuedSchema)),
10389
- todos: opt(Type10.Object({ revision: int, count: int, completed: int })),
10390
- questions: opt(Type10.Object({ revision: int, pendingCount: int })),
10391
- plan: opt(Type10.Object({ revision: int, pending: Type10.Boolean(), version: opt(Type10.String({ minLength: 1 })) })),
10392
- status: nullable2(str2),
10490
+ MobileCodemodeSchema = obj10({
10491
+ calls: arr8(obj10({
10492
+ id: count,
10493
+ name: text4,
10494
+ args: str10({ maxLength: CODEMODE_VIEW_LIMITS.argsChars }),
10495
+ status: union6([lit5("running"), lit5("ok"), lit5("error"), lit5("cancelled")]),
10496
+ durationMs: opt10(num5()),
10497
+ error: opt10(str10({ maxLength: CODEMODE_VIEW_LIMITS.errorChars }))
10498
+ }, { open: true }), { maxItems: CODEMODE_VIEW_LIMITS.maxCalls }),
10499
+ omitted: count,
10500
+ fullOutputPath: opt10(text4)
10501
+ }, { open: true });
10502
+ MobileToolItemSchema = obj10({
10503
+ callId: text4,
10504
+ name: text4,
10505
+ summary: text4,
10506
+ status: union6([lit5("running"), lit5("done"), lit5("error")]),
10507
+ detail: opt10(text4),
10508
+ output: opt10(text4),
10509
+ result: opt10(text4),
10510
+ diff: opt10(text4),
10511
+ diffAdded: opt10(num5()),
10512
+ diffRemoved: opt10(num5()),
10513
+ diffTruncated: opt10(bool5()),
10514
+ durationMs: opt10(num5()),
10515
+ note: opt10(text4),
10516
+ interaction: opt10(MobileToolInteractionSchema),
10517
+ codemode: opt10(MobileCodemodeSchema)
10518
+ }, { open: true });
10519
+ MobileChatItemSchema = obj10({
10520
+ key: text4,
10521
+ type: union6([lit5("user"), lit5("assistant"), lit5("tool"), lit5("error"), lit5("system")]),
10522
+ text: opt10(text4),
10523
+ thinking: opt10(text4),
10524
+ note: opt10(text4),
10525
+ error: opt10(text4),
10526
+ skill: opt10(text4),
10527
+ jobId: opt10(text4),
10528
+ tool: opt10(MobileToolItemSchema),
10529
+ images: opt10(arr8(MobileImageRefSchema)),
10530
+ refs: opt10(arr8(MobileContentRefSchema))
10531
+ }, { open: true });
10532
+ MobileStreamingSchema = obj10({
10533
+ text: text4,
10534
+ thinking: text4,
10535
+ textOmitted: count,
10536
+ thinkingOmitted: count,
10537
+ toolCalls: arr8(obj10({
10538
+ callId: text4,
10539
+ name: text4,
10540
+ summary: text4,
10541
+ status: union6([lit5("running"), lit5("done")])
10542
+ }, { open: true }))
10543
+ }, { open: true });
10544
+ MobileUsageSchema = obj10({
10545
+ input: count,
10546
+ output: count,
10547
+ cacheRead: count,
10548
+ cacheWrite: count,
10549
+ totalTokens: count,
10550
+ cost: num5({ minimum: 0 })
10551
+ }, { open: true });
10552
+ MobileSubagentRoundSchema = obj10({
10553
+ taskId: count,
10554
+ callId: text4,
10555
+ agentId: text4,
10556
+ round: opt10(count),
10557
+ role: text4,
10558
+ title: text4,
10559
+ status: union6([lit5("queued"), lit5("running"), lit5("completed"), lit5("failed"), lit5("cancelled")]),
10560
+ approvalCount: count,
10561
+ readonly: bool5(),
10562
+ createdAt: num5(),
10563
+ endedAt: nullable2(num5()),
10564
+ usage: opt10(MobileUsageSchema)
10565
+ }, { open: true });
10566
+ MobileAgentSchema = obj10({
10567
+ agentId: text4,
10568
+ role: text4,
10569
+ title: text4,
10570
+ status: union6([lit5("idle"), lit5("queued"), lit5("running")]),
10571
+ activeRoundId: nullable2(count),
10572
+ lastRoundId: count,
10573
+ roundCount: count,
10574
+ approvalCount: count
10575
+ }, { open: true });
10576
+ MobileQueuedSchema = obj10({
10577
+ id: count,
10578
+ mode: union6([lit5("steer"), lit5("followUp")]),
10579
+ preview: str10(),
10580
+ images: count
10581
+ }, { open: true });
10582
+ mobileChatStatusProperties = {
10583
+ busy: bool5(),
10584
+ queued: opt10(arr8(MobileQueuedSchema)),
10585
+ todos: opt10(obj10({ revision: count, count, completed: count }, { open: true })),
10586
+ questions: opt10(obj10({ revision: count, pendingCount: count }, { open: true })),
10587
+ plan: opt10(obj10({ revision: count, pending: bool5(), version: opt10(str10({ minLength: 1 })) }, { open: true })),
10588
+ status: nullable2(text4),
10393
10589
  usage: MobileUsageSchema,
10394
- taskUsage: opt(MobileUsageSchema),
10395
- contextUsage: opt(Type10.Object({ tokens: nullable2(Type10.Number()), window: Type10.Number(), percent: nullable2(Type10.Number()) })),
10396
- agent: Type10.Object({ model: nullable2(str2), thinkingLevel: nullable2(str2) }),
10397
- subagents: Type10.Array(MobileSubagentRoundSchema),
10398
- agents: Type10.Array(MobileAgentSchema),
10399
- approvalCount: int,
10400
- compaction: opt(Type10.Object({ phase: str2, at: Type10.Number() })),
10401
- readonly: opt(Type10.Boolean())
10402
- });
10403
- MobileChatSnapshotSchema = Type10.Object({
10404
- ...MobileChatStatusSchema.properties,
10405
- items: Type10.Array(MobileChatItemSchema),
10406
- historyBefore: nullable2(str2),
10590
+ taskUsage: opt10(MobileUsageSchema),
10591
+ contextUsage: opt10(obj10({ tokens: nullable2(num5()), window: num5(), percent: nullable2(num5()) }, { open: true })),
10592
+ agent: obj10({ model: nullable2(text4), thinkingLevel: nullable2(text4) }, { open: true }),
10593
+ subagents: arr8(MobileSubagentRoundSchema),
10594
+ agents: arr8(MobileAgentSchema),
10595
+ approvalCount: count,
10596
+ compaction: opt10(obj10({ phase: text4, at: num5() }, { open: true })),
10597
+ runFailure: opt10(obj10({ reason: text4, detail: opt10(text4) }, { open: true })),
10598
+ readonly: opt10(bool5())
10599
+ };
10600
+ MobileChatStatusSchema = obj10(mobileChatStatusProperties, { open: true });
10601
+ MobileChatSnapshotSchema = obj10({
10602
+ ...mobileChatStatusProperties,
10603
+ items: arr8(MobileChatItemSchema),
10604
+ historyBefore: nullable2(text4),
10407
10605
  streaming: nullable2(MobileStreamingSchema)
10408
- });
10409
- MobileWorkspaceSchema = Type10.Object({ root: str2, name: str2 });
10410
- MobileSessionSummarySchema = Type10.Object({
10411
- id: str2,
10412
- workspace: str2,
10413
- title: str2,
10414
- preview: str2,
10415
- lastUsedAt: Type10.Integer(),
10416
- lastMessageAt: Type10.Integer(),
10417
- archived: Type10.Boolean(),
10418
- awaitingAnswer: opt(Type10.Boolean()),
10419
- awaitingPlan: opt(Type10.Boolean())
10420
- });
10421
- MobileApprovalSchema = Type10.Object({
10422
- id: int,
10423
- version: str2,
10424
- taskId: int,
10425
- agentId: opt(str2),
10426
- title: opt(str2),
10427
- command: str2,
10428
- cwd: str2,
10429
- timeoutSeconds: opt(Type10.Number({ minimum: 0 })),
10430
- requestedAt: Type10.Number()
10431
- });
10432
- MobileTodoItemSchema = Type10.Object({
10433
- id: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
10434
- text: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
10435
- status: Type10.Union([Type10.Literal("pending"), Type10.Literal("in_progress"), Type10.Literal("completed")])
10436
- });
10437
- MobileTodoListSchema = Type10.Object({
10438
- revision: int,
10439
- items: Type10.Array(MobileTodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
10440
- });
10441
- MobileQuestionOptionSchema = Type10.Object({
10442
- id: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
10443
- label: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
10444
- description: opt(Type10.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
10445
- recommended: opt(Type10.Boolean())
10446
- });
10447
- MobileQuestionSummarySchema = Type10.Object({
10448
- id: int,
10449
- version: str2,
10450
- preview: Type10.String({ maxLength: QUESTION_LIMITS.previewChars }),
10451
- multiple: Type10.Boolean(),
10452
- optionCount: int,
10453
- createdAt: Type10.Number()
10454
- });
10455
- MobileQuestionListSchema = Type10.Object({
10456
- revision: int,
10457
- pending: Type10.Array(MobileQuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
10458
- });
10459
- MobileQuestionSchema = Type10.Object({
10460
- id: int,
10461
- callId: str2,
10462
- version: str2,
10463
- question: Type10.String({ maxLength: QUESTION_LIMITS.questionChars }),
10464
- multiple: Type10.Boolean(),
10465
- options: Type10.Array(MobileQuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
10606
+ }, { open: true });
10607
+ MobileWorkspaceSchema = obj10({ root: text4, name: text4 }, { open: true });
10608
+ MobileSessionSummarySchema = obj10({
10609
+ id: text4,
10610
+ workspace: text4,
10611
+ title: text4,
10612
+ preview: text4,
10613
+ lastUsedAt: int7(),
10614
+ lastMessageAt: int7(),
10615
+ archived: bool5(),
10616
+ awaitingAnswer: opt10(bool5()),
10617
+ awaitingPlan: opt10(bool5())
10618
+ }, { open: true });
10619
+ MobileApprovalSchema = obj10({
10620
+ id: count,
10621
+ version: text4,
10622
+ taskId: count,
10623
+ agentId: opt10(text4),
10624
+ title: opt10(text4),
10625
+ command: text4,
10626
+ cwd: text4,
10627
+ timeoutSeconds: opt10(num5({ minimum: 0 })),
10628
+ requestedAt: num5()
10629
+ }, { open: true });
10630
+ MobileTodoItemSchema = obj10({
10631
+ id: str10({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
10632
+ text: str10({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
10633
+ status: union6([lit5("pending"), lit5("in_progress"), lit5("completed")])
10634
+ }, { open: true });
10635
+ MobileTodoListSchema = obj10({
10636
+ revision: count,
10637
+ items: arr8(MobileTodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
10638
+ }, { open: true });
10639
+ MobileQuestionOptionSchema = obj10({
10640
+ id: str10({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
10641
+ label: str10({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
10642
+ description: opt10(str10({ maxLength: QUESTION_LIMITS.descriptionChars })),
10643
+ recommended: opt10(bool5())
10644
+ }, { open: true });
10645
+ MobileQuestionSummarySchema = obj10({
10646
+ id: count,
10647
+ version: text4,
10648
+ preview: str10({ maxLength: QUESTION_LIMITS.previewChars }),
10649
+ multiple: bool5(),
10650
+ optionCount: count,
10651
+ createdAt: num5()
10652
+ }, { open: true });
10653
+ MobileQuestionListSchema = obj10({
10654
+ revision: count,
10655
+ pending: arr8(MobileQuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
10656
+ }, { open: true });
10657
+ MobileQuestionSchema = obj10({
10658
+ id: count,
10659
+ callId: text4,
10660
+ version: text4,
10661
+ question: str10({ maxLength: QUESTION_LIMITS.questionChars }),
10662
+ multiple: bool5(),
10663
+ options: arr8(MobileQuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
10466
10664
  status: questionStatus2,
10467
- answer: nullable2(Type10.Object({
10468
- optionIds: Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
10469
- text: nullable2(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
10470
- })),
10471
- createdAt: Type10.Number(),
10472
- settledAt: nullable2(Type10.Number())
10473
- });
10474
- MobileQuestionResponseSchema = Type10.Union([
10475
- Type10.Object({
10476
- kind: Type10.Literal("answer"),
10477
- optionIds: opt(Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
10478
- text: opt(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
10479
- }, { additionalProperties: false }),
10480
- Type10.Object({ kind: Type10.Literal("skip") }, { additionalProperties: false })
10665
+ answer: nullable2(obj10({
10666
+ optionIds: arr8(str10(), { maxItems: QUESTION_LIMITS.maxOptions }),
10667
+ text: nullable2(str10({ maxLength: QUESTION_LIMITS.textChars }))
10668
+ }, { open: true })),
10669
+ createdAt: num5(),
10670
+ settledAt: nullable2(num5())
10671
+ }, { open: true });
10672
+ MobileQuestionResponseSchema = union6([
10673
+ obj10({
10674
+ kind: lit5("answer"),
10675
+ optionIds: opt10(arr8(str10(), { maxItems: QUESTION_LIMITS.maxOptions })),
10676
+ text: opt10(str10({ maxLength: QUESTION_LIMITS.textChars }))
10677
+ }),
10678
+ obj10({ kind: lit5("skip") })
10481
10679
  ]);
10482
- MobilePlanSchema = Type10.Object({
10483
- version: Type10.String({ minLength: 1 }),
10484
- title: Type10.String({ maxLength: PLAN_LIMITS.titleChars }),
10485
- steps: Type10.Array(Type10.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
10486
- note: Type10.String({ maxLength: PLAN_LIMITS.noteChars }),
10680
+ MobilePlanSchema = obj10({
10681
+ version: str10({ minLength: 1 }),
10682
+ title: str10({ maxLength: PLAN_LIMITS.titleChars }),
10683
+ steps: arr8(str10({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
10684
+ note: str10({ maxLength: PLAN_LIMITS.noteChars }),
10487
10685
  status: planStatus,
10488
- feedback: nullable2(Type10.String({ maxLength: PLAN_LIMITS.feedbackChars })),
10489
- createdAt: Type10.Number(),
10490
- settledAt: nullable2(Type10.Number())
10491
- });
10492
- MobilePlanDecisionSchema = Type10.Union([
10493
- Type10.Object({ kind: Type10.Literal("approve") }, { additionalProperties: false }),
10494
- Type10.Object({ kind: Type10.Literal("revise"), feedback: Type10.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
10686
+ feedback: nullable2(str10({ maxLength: PLAN_LIMITS.feedbackChars })),
10687
+ createdAt: num5(),
10688
+ settledAt: nullable2(num5())
10689
+ }, { open: true });
10690
+ MobilePlanDecisionSchema = union6([
10691
+ obj10({ kind: lit5("approve") }),
10692
+ obj10({ kind: lit5("revise"), feedback: str10({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) })
10495
10693
  ]);
10496
- sessionListParams = Type10.Object({
10497
- workspace: str2,
10498
- query: opt(str2),
10499
- cursor: opt(str2)
10500
- });
10501
- sessionListResult = Type10.Object({
10502
- sessions: Type10.Array(MobileSessionSummarySchema),
10503
- nextCursor: opt(str2)
10504
- });
10505
- subscription = { subscriptionId: Type10.String({ minLength: 1 }) };
10506
- pageResult = Type10.Object({ items: Type10.Array(MobileChatItemSchema), historyBefore: nullable2(str2) });
10507
- pageParams = Type10.Object({
10694
+ sessionListParams = obj10({
10695
+ workspace: text4,
10696
+ query: opt10(text4),
10697
+ cursor: opt10(text4)
10698
+ }, { open: true });
10699
+ sessionListResult = obj10({
10700
+ sessions: arr8(MobileSessionSummarySchema),
10701
+ nextCursor: opt10(text4)
10702
+ }, { open: true });
10703
+ subscription = { subscriptionId: str10({ minLength: 1 }) };
10704
+ pageResult = obj10({ items: arr8(MobileChatItemSchema), historyBefore: nullable2(text4) }, { open: true });
10705
+ pageParams = obj10({
10508
10706
  ...subscription,
10509
- before: Type10.String({ minLength: 1 }),
10510
- limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
10511
- });
10512
- chunkResult = Type10.Object({ data: str2, nextOffset: int, total: int, done: Type10.Boolean() });
10707
+ before: str10({ minLength: 1 }),
10708
+ limit: opt10(int7({ minimum: 1, maximum: 50 }))
10709
+ }, { open: true });
10710
+ chunkResultProperties = { data: text4, nextOffset: count, total: count, done: bool5() };
10711
+ chunkResult = obj10(chunkResultProperties, { open: true });
10513
10712
  MOBILE_API_V1_METHODS = {
10514
10713
  "server.hello": { params: MobileHelloParams, result: buildHelloResultSchema() },
10515
- "workspaces.list": { params: Type10.Null(), result: Type10.Object({ workspaces: Type10.Array(MobileWorkspaceSchema) }) },
10714
+ "workspaces.list": { params: nul5(), result: obj10({ workspaces: arr8(MobileWorkspaceSchema) }, { open: true }) },
10516
10715
  "sessions.list": { params: sessionListParams, result: sessionListResult },
10517
- "sessions.create": { params: Type10.Object({ workspace: str2 }), result: MobileSessionSummarySchema },
10716
+ "sessions.create": { params: obj10({ workspace: text4 }, { open: true }), result: MobileSessionSummarySchema },
10518
10717
  "chat.attach": {
10519
- params: Type10.Object({ sessionId: Type10.String({ minLength: 1 }) }),
10520
- result: Type10.Object({ subscriptionId: str2, sessionId: str2, snapshot: MobileChatSnapshotSchema })
10718
+ params: obj10({ sessionId: str10({ minLength: 1 }) }, { open: true }),
10719
+ result: obj10({ subscriptionId: text4, sessionId: text4, snapshot: MobileChatSnapshotSchema }, { open: true })
10521
10720
  },
10522
- "chat.detach": { params: Type10.Object(subscription), result: Type10.Null() },
10721
+ "chat.detach": { params: obj10(subscription, { open: true }), result: nul5() },
10523
10722
  "chat.history": { params: pageParams, result: pageResult },
10524
10723
  "chat.content": {
10525
- params: Type10.Object({ ref: MobileContentRefSchema, offset: int }),
10724
+ params: obj10({ ref: MobileContentRefSchema, offset: count }, { open: true }),
10526
10725
  result: chunkResult
10527
10726
  },
10528
10727
  "chat.submit": {
10529
- params: Type10.Object({
10728
+ params: obj10({
10530
10729
  ...subscription,
10531
- requestId: Type10.String({ minLength: 1, maxLength: 128 }),
10532
- text: Type10.String({ minLength: 1, maxLength: 65536 }),
10533
- mode: opt(Type10.Literal("steer"))
10534
- }),
10535
- result: Type10.Object({ accepted: Type10.Literal(true), queued: Type10.Boolean() })
10730
+ requestId: str10({ minLength: 1, maxLength: 128 }),
10731
+ text: str10({ minLength: 1, maxLength: 65536 }),
10732
+ mode: opt10(lit5("steer"))
10733
+ }, { open: true }),
10734
+ result: obj10({ accepted: lit5(true), queued: bool5() }, { open: true })
10536
10735
  },
10537
10736
  "chat.submissionStatus": {
10538
- params: Type10.Object({ ...subscription, requestId: Type10.String({ minLength: 1, maxLength: 128 }) }),
10539
- result: Type10.Object({ status: Type10.Union([Type10.Literal("accepted"), Type10.Literal("unknown")]) })
10737
+ params: obj10({ ...subscription, requestId: str10({ minLength: 1, maxLength: 128 }) }, { open: true }),
10738
+ result: obj10({ status: union6([lit5("accepted"), lit5("unknown")]) }, { open: true })
10540
10739
  },
10541
10740
  "chat.steer": {
10542
- params: Type10.Object({ ...subscription, id: int }),
10543
- result: Type10.Object({
10544
- result: Type10.Union([
10545
- Type10.Literal("steered"),
10546
- Type10.Literal("withdrawn"),
10547
- Type10.Literal("placed"),
10548
- Type10.Literal("settled"),
10549
- Type10.Literal("unknown")
10550
- ]),
10551
- id: opt(int),
10552
- text: opt(Type10.String()),
10553
- images: opt(int)
10554
- })
10741
+ params: obj10({ ...subscription, id: count }, { open: true }),
10742
+ result: obj10({
10743
+ result: union6([lit5("steered"), lit5("withdrawn"), lit5("placed"), lit5("settled"), lit5("unknown")]),
10744
+ id: opt10(count),
10745
+ text: opt10(str10()),
10746
+ images: opt10(count)
10747
+ }, { open: true })
10555
10748
  },
10556
10749
  "chat.withdraw": {
10557
- params: Type10.Object({ ...subscription, id: int }),
10558
- result: Type10.Object({
10559
- result: Type10.Union([
10560
- Type10.Literal("withdrawn"),
10561
- Type10.Literal("placed"),
10562
- Type10.Literal("settled"),
10563
- Type10.Literal("unknown")
10564
- ]),
10565
- text: opt(Type10.String()),
10566
- images: opt(int)
10567
- })
10750
+ params: obj10({ ...subscription, id: count }, { open: true }),
10751
+ result: obj10({
10752
+ result: union6([lit5("withdrawn"), lit5("placed"), lit5("settled"), lit5("unknown")]),
10753
+ text: opt10(str10()),
10754
+ images: opt10(count)
10755
+ }, { open: true })
10568
10756
  },
10569
- "chat.abort": { params: Type10.Object(subscription), result: Type10.Object({ accepted: Type10.Boolean() }) },
10757
+ "chat.abort": { params: obj10(subscription, { open: true }), result: obj10({ accepted: bool5() }, { open: true }) },
10570
10758
  "subagents.open": {
10571
- params: Type10.Object({ ...subscription, agentId: opt(Type10.String({ minLength: 1 })), taskId: opt(int) }),
10572
- result: Type10.Object({
10573
- subagentSubscriptionId: str2,
10574
- agentId: str2,
10759
+ params: obj10({ ...subscription, agentId: opt10(str10({ minLength: 1 })), taskId: opt10(count) }, { open: true }),
10760
+ result: obj10({
10761
+ subagentSubscriptionId: text4,
10762
+ agentId: text4,
10575
10763
  snapshot: MobileChatSnapshotSchema
10576
- })
10764
+ }, { open: true })
10577
10765
  },
10578
10766
  "subagents.history": {
10579
- params: Type10.Object({
10580
- subagentSubscriptionId: Type10.String({ minLength: 1 }),
10581
- before: Type10.String({ minLength: 1 }),
10582
- limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
10583
- }),
10767
+ params: obj10({
10768
+ subagentSubscriptionId: str10({ minLength: 1 }),
10769
+ before: str10({ minLength: 1 }),
10770
+ limit: opt10(int7({ minimum: 1, maximum: 50 }))
10771
+ }, { open: true }),
10584
10772
  result: pageResult
10585
10773
  },
10586
10774
  "subagents.close": {
10587
- params: Type10.Object({ subagentSubscriptionId: Type10.String({ minLength: 1 }) }),
10588
- result: Type10.Null()
10775
+ params: obj10({ subagentSubscriptionId: str10({ minLength: 1 }) }, { open: true }),
10776
+ result: nul5()
10589
10777
  },
10590
10778
  "subagents.stop": {
10591
- params: Type10.Object({ ...subscription, taskId: int }),
10592
- result: Type10.Object({ stopped: Type10.Boolean() })
10779
+ params: obj10({ ...subscription, taskId: count }, { open: true }),
10780
+ result: obj10({ stopped: bool5() }, { open: true })
10593
10781
  },
10594
10782
  "approvals.list": {
10595
- params: Type10.Object(subscription),
10596
- result: Type10.Object({ approvals: Type10.Array(MobileApprovalSchema) })
10783
+ params: obj10(subscription, { open: true }),
10784
+ result: obj10({ approvals: arr8(MobileApprovalSchema) }, { open: true })
10597
10785
  },
10598
- "approvals.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileApprovalSchema },
10786
+ "approvals.get": { params: obj10({ ...subscription, id: count }, { open: true }), result: MobileApprovalSchema },
10599
10787
  "approvals.decide": {
10600
- params: Type10.Object({
10788
+ params: obj10({
10601
10789
  ...subscription,
10602
- taskId: int,
10603
- id: int,
10604
- version: Type10.String({ minLength: 1 }),
10605
- decision: Type10.Union([Type10.Literal("allow"), Type10.Literal("deny")])
10606
- }),
10607
- result: Type10.Object({
10608
- status: Type10.Union([
10609
- Type10.Literal("pending"),
10610
- Type10.Literal("allowed"),
10611
- Type10.Literal("denied"),
10612
- Type10.Literal("cancelled")
10613
- ])
10614
- })
10790
+ taskId: count,
10791
+ id: count,
10792
+ version: str10({ minLength: 1 }),
10793
+ decision: union6([lit5("allow"), lit5("deny")])
10794
+ }, { open: true }),
10795
+ result: obj10({
10796
+ status: union6([lit5("pending"), lit5("allowed"), lit5("denied"), lit5("cancelled")])
10797
+ }, { open: true })
10615
10798
  },
10616
- "todos.get": { params: Type10.Object(subscription), result: MobileTodoListSchema },
10617
- "questions.list": { params: Type10.Object(subscription), result: MobileQuestionListSchema },
10618
- "questions.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileQuestionSchema },
10799
+ "todos.get": { params: obj10(subscription, { open: true }), result: MobileTodoListSchema },
10800
+ "questions.list": { params: obj10(subscription, { open: true }), result: MobileQuestionListSchema },
10801
+ "questions.get": { params: obj10({ ...subscription, id: count }, { open: true }), result: MobileQuestionSchema },
10619
10802
  "questions.respond": {
10620
- params: Type10.Object({
10803
+ params: obj10({
10621
10804
  ...subscription,
10622
- id: int,
10623
- version: Type10.String({ minLength: 1 }),
10805
+ id: count,
10806
+ version: str10({ minLength: 1 }),
10624
10807
  response: MobileQuestionResponseSchema
10625
- }),
10808
+ }, { open: true }),
10626
10809
  result: MobileQuestionSchema
10627
10810
  },
10628
10811
  "plan.get": {
10629
- params: Type10.Object({ ...subscription, version: Type10.String({ minLength: 1 }) }),
10812
+ params: obj10({ ...subscription, version: str10({ minLength: 1 }) }, { open: true }),
10630
10813
  result: MobilePlanSchema
10631
10814
  },
10632
10815
  "plan.respond": {
10633
- params: Type10.Object({
10816
+ params: obj10({
10634
10817
  ...subscription,
10635
- version: Type10.String({ minLength: 1 }),
10818
+ version: str10({ minLength: 1 }),
10636
10819
  decision: MobilePlanDecisionSchema
10637
- }),
10820
+ }, { open: true }),
10638
10821
  result: MobilePlanSchema
10639
10822
  },
10640
10823
  "plan.exit": {
10641
- params: Type10.Object(subscription),
10642
- result: Type10.Object({ mode: Type10.Literal("normal"), revision: int })
10824
+ params: obj10(subscription, { open: true }),
10825
+ result: obj10({ mode: lit5("normal"), revision: count }, { open: true })
10643
10826
  },
10644
10827
  "media.read": {
10645
- params: Type10.Object({
10828
+ params: obj10({
10646
10829
  ...subscription,
10647
- agentId: opt(Type10.String({ minLength: 1 })),
10648
- entryId: int,
10649
- contentIndex: int,
10650
- offset: int
10651
- }),
10652
- result: Type10.Object({ ...chunkResult.properties, mimeType: str2 })
10830
+ agentId: opt10(str10({ minLength: 1 })),
10831
+ entryId: count,
10832
+ contentIndex: count,
10833
+ offset: count
10834
+ }, { open: true }),
10835
+ result: obj10({ ...chunkResultProperties, mimeType: text4 }, { open: true })
10653
10836
  }
10654
10837
  };
10655
10838
  patchBody = {
10656
- seq: Type10.Integer({ minimum: 1 }),
10657
- upserts: Type10.Array(MobileChatItemSchema),
10658
- status: opt(MobileChatStatusSchema),
10659
- streaming: opt(nullable2(MobileStreamingSchema)),
10660
- streamingAppend: opt(Type10.Object({ text: opt(str2), thinking: opt(str2) }))
10839
+ seq: int7({ minimum: 1 }),
10840
+ upserts: arr8(MobileChatItemSchema),
10841
+ status: opt10(MobileChatStatusSchema),
10842
+ streaming: opt10(nullable2(MobileStreamingSchema)),
10843
+ streamingAppend: opt10(obj10({ text: opt10(text4), thinking: opt10(text4) }, { open: true }))
10661
10844
  };
10662
10845
  MOBILE_API_V1_NOTIFICATIONS = {
10663
- "workspaces.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
10664
- "sessions.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
10665
- "chat.patch": Type10.Object({ subscriptionId: str2, ...patchBody }),
10666
- "chat.reset": Type10.Object({ subscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
10667
- "subagents.patch": Type10.Object({ subagentSubscriptionId: str2, ...patchBody }),
10668
- "subagents.reset": Type10.Object({ subagentSubscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
10669
- "approvals.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
10670
- "todos.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
10671
- "questions.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
10672
- "plan.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) })
10846
+ "workspaces.changed": obj10({ revision: int7({ minimum: 1 }) }, { open: true }),
10847
+ "sessions.changed": obj10({ revision: int7({ minimum: 1 }) }, { open: true }),
10848
+ "chat.patch": obj10({ subscriptionId: text4, ...patchBody }, { open: true }),
10849
+ "chat.reset": obj10({ subscriptionId: text4, seq: int7({ minimum: 1 }) }, { open: true }),
10850
+ "subagents.patch": obj10({ subagentSubscriptionId: text4, ...patchBody }, { open: true }),
10851
+ "subagents.reset": obj10({ subagentSubscriptionId: text4, seq: int7({ minimum: 1 }) }, { open: true }),
10852
+ "approvals.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
10853
+ "todos.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
10854
+ "questions.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true }),
10855
+ "plan.changed": obj10({ subscriptionId: text4, revision: int7({ minimum: 1 }) }, { open: true })
10673
10856
  };
10674
10857
  });
10675
10858
 
@@ -10702,10 +10885,10 @@ function jsonBytes2(value) {
10702
10885
  return Buffer.byteLength(JSON.stringify(value), "utf8");
10703
10886
  }
10704
10887
  function mapUsage(usage) {
10705
- const input = count(usage.input);
10706
- const output = count(usage.output);
10707
- const cacheRead = count(usage.cacheRead);
10708
- const cacheWrite = count(usage.cacheWrite);
10888
+ const input = count2(usage.input);
10889
+ const output = count2(usage.output);
10890
+ const cacheRead = count2(usage.cacheRead);
10891
+ const cacheWrite = count2(usage.cacheWrite);
10709
10892
  return {
10710
10893
  input,
10711
10894
  output,
@@ -10757,9 +10940,9 @@ function mapChatItem(raw, owner) {
10757
10940
  images: list.map((image) => ({
10758
10941
  name: image.name,
10759
10942
  mimeType: image.mimeType,
10760
- width: count(image.width),
10761
- height: count(image.height),
10762
- bytes: count(image.bytes),
10943
+ width: count2(image.width),
10944
+ height: count2(image.height),
10945
+ bytes: count2(image.bytes),
10763
10946
  entryId: image.entryId,
10764
10947
  contentIndex: image.contentIndex
10765
10948
  }))
@@ -10846,7 +11029,7 @@ function mapRound(round, readonlyRound) {
10846
11029
  role: round.role,
10847
11030
  title: round.title,
10848
11031
  status: round.status,
10849
- approvalCount: count(round.approvalCount),
11032
+ approvalCount: count2(round.approvalCount),
10850
11033
  readonly: readonlyRound || round.historyThrough !== undefined,
10851
11034
  createdAt: round.createdAt,
10852
11035
  endedAt: round.endedAt,
@@ -10863,7 +11046,7 @@ function mapStatus(view, options = {}) {
10863
11046
  activeRoundId: agent.activeRoundId,
10864
11047
  lastRoundId: agent.lastRoundId,
10865
11048
  roundCount: agent.roundCount,
10866
- approvalCount: count(agent.approvalCount)
11049
+ approvalCount: count2(agent.approvalCount)
10867
11050
  }));
10868
11051
  const approvalCount = view.approvals !== undefined ? view.approvals.filter((approval) => approval.status === "pending").length : rounds.reduce((sum, round) => sum + round.approvalCount, 0);
10869
11052
  const latest = view.compaction?.latest;
@@ -10899,6 +11082,12 @@ function mapStatus(view, options = {}) {
10899
11082
  agents,
10900
11083
  approvalCount,
10901
11084
  ...latest ? { compaction: { phase: latest.phase, at: latest.at } } : {},
11085
+ ...view.runFailure === undefined ? {} : {
11086
+ runFailure: {
11087
+ reason: view.runFailure.reason,
11088
+ ...view.runFailure.detail === undefined ? {} : { detail: view.runFailure.detail }
11089
+ }
11090
+ },
10902
11091
  ...options.readonly === undefined ? {} : { readonly: options.readonly }
10903
11092
  };
10904
11093
  }
@@ -11163,7 +11352,7 @@ class MobileChatProjection {
11163
11352
  this.onNotice({ kind: "patch", params });
11164
11353
  }
11165
11354
  }
11166
- var CONTENT_PREVIEW_BYTES = 2048, CONTENT_REF_BYTES = 8192, MERGE_MS = 100, STREAMING_TAIL_CHARS = 16384, ProjectionError, count = (value) => Math.max(0, Math.round(value ?? 0)), ITEMS_BUDGET, ENTRY_CURSOR;
11355
+ var CONTENT_PREVIEW_BYTES = 2048, CONTENT_REF_BYTES = 8192, MERGE_MS = 100, STREAMING_TAIL_CHARS = 16384, ProjectionError, count2 = (value) => Math.max(0, Math.round(value ?? 0)), ITEMS_BUDGET, ENTRY_CURSOR;
11167
11356
  var init_projection2 = __esm(() => {
11168
11357
  init_src();
11169
11358
  init_protocol();
@@ -11180,7 +11369,6 @@ var init_projection2 = __esm(() => {
11180
11369
 
11181
11370
  // packages/gateway/src/mobile/adapter.ts
11182
11371
  import { ChordError as ChordError2 } from "@earendil-works/chord";
11183
- import { Check as Check2 } from "typebox/value";
11184
11372
  function chordToMobileCode(error) {
11185
11373
  switch (error.code) {
11186
11374
  case "gone":
@@ -11343,12 +11531,12 @@ class MobileAdapter {
11343
11531
  this.sendError(id, -32601, "method not found", { code: "not_found" });
11344
11532
  return;
11345
11533
  }
11346
- if (!Check2(spec.params, params)) {
11534
+ if (!checkSchema(spec.params, params)) {
11347
11535
  this.sendError(id, -32602, "invalid params", { code: "invalid_params" });
11348
11536
  return;
11349
11537
  }
11350
11538
  const result = await this.invoke(method, params);
11351
- if (!Check2(spec.result, result)) {
11539
+ if (!checkSchema(spec.result, result)) {
11352
11540
  this.onError?.(new Error(`mobile result for ${method} does not match its schema`));
11353
11541
  this.sendError(id, -32603, "internal result mismatch", { code: "internal" });
11354
11542
  return;
@@ -30076,18 +30264,23 @@ var init_image_uploads = __esm(() => {
30076
30264
  // packages/gateway/src/subagent-host.ts
30077
30265
  import { ChordError as ChordError3, local, opaque } from "@earendil-works/chord";
30078
30266
  import { UsageDoc as UsageDoc3 } from "@earendil-works/pi-durable";
30079
- import { Type as Type11 } from "typebox";
30267
+ import { int as int8, obj as obj11, str as str11 } from "@earendil-works/pi-schema";
30080
30268
  async function openReader(runtime, childId, parentId, context, through, historyUsage) {
30081
30269
  const child = await runtime.harness.conversation(childId, context);
30082
30270
  if (!child)
30083
30271
  throw new ChordError3("gone", "Subagent unavailable");
30084
- const native = await child.viewState(context);
30272
+ const native = await child.watch(context);
30273
+ const listeners = new Set;
30085
30274
  if (through !== undefined)
30086
- native.dispose();
30275
+ await native.stop();
30276
+ else
30277
+ native.start(async () => {
30278
+ for (const listener of listeners)
30279
+ listener();
30280
+ });
30087
30281
  const project = (entries, streaming = true) => {
30088
30282
  const result = projectChatView({ ...native.value, entries: [...entries].sort((a, b) => a.id - b.id) }, "", {
30089
- models: runtime.listModels(),
30090
- contextEntries: native.value.entries
30283
+ models: runtime.listModels()
30091
30284
  });
30092
30285
  if (!streaming || through !== undefined) {
30093
30286
  result.streaming = null;
@@ -30136,8 +30329,10 @@ async function openReader(runtime, childId, parentId, context, through, historyU
30136
30329
  subscribe: (fn) => {
30137
30330
  if (through !== undefined)
30138
30331
  return () => {};
30139
- const stop = native.subscribe(() => fn(null));
30140
- const stopApprovals = runtime.harness.subscribeCommits(({ changes }) => {
30332
+ const listener = () => fn(null);
30333
+ listeners.add(listener);
30334
+ const stop = () => listeners.delete(listener);
30335
+ const stopApprovals = onCommits(runtime.harness, ({ changes }) => {
30141
30336
  if (changes.some((c) => c.type === "document" && ["app.neko.approvals", "app.neko.delegations", "app.neko.subagents"].includes(c.record.kind) && c.conversationId === parentId))
30142
30337
  fn(null);
30143
30338
  });
@@ -30196,12 +30391,15 @@ async function openReader(runtime, childId, parentId, context, through, historyU
30196
30391
  mimeType: image.mimeType
30197
30392
  };
30198
30393
  },
30199
- close: () => native.dispose()
30394
+ close: () => {
30395
+ listeners.clear();
30396
+ native.stop();
30397
+ }
30200
30398
  };
30201
30399
  }
30202
30400
  function installSubagentHost(host, runtime, context) {
30203
30401
  host.serve(ChildProjection, (input, _principal, self2) => {
30204
- const revision = self2.state("revision", Type11.Integer(), 0);
30402
+ const revision = self2.state("revision", int8(), 0);
30205
30403
  const reader = self2.resource("reader", {
30206
30404
  version: 1,
30207
30405
  async open() {
@@ -30242,7 +30440,7 @@ function installSubagentHost(host, runtime, context) {
30242
30440
  parentId: Number(input.sessionId),
30243
30441
  agentId: input.agentId ?? `round-${input.taskId}`
30244
30442
  });
30245
- const authorizedRevision = self2.state("authorizedRevision", Type11.Integer(), 0);
30443
+ const authorizedRevision = self2.state("authorizedRevision", int8(), 0);
30246
30444
  self2.on("authorizedView", shared.revision, async (value, ctx) => {
30247
30445
  try {
30248
30446
  target.value;
@@ -30301,9 +30499,9 @@ var init_subagent_host = __esm(() => {
30301
30499
  init_session_host();
30302
30500
  ChildProjection = local("neko.subagent-projection", {
30303
30501
  version: 1,
30304
- input: Type11.Object({ parentId: Type11.Integer(), agentId: Type11.String() }),
30502
+ input: obj11({ parentId: int8(), agentId: str11() }, { open: true }),
30305
30503
  members: {
30306
- revision: Type11.Integer(),
30504
+ revision: int8(),
30307
30505
  reader: opaque("neko.subagent-reader@1")
30308
30506
  }
30309
30507
  });
@@ -30319,22 +30517,14 @@ import {
30319
30517
  } from "@earendil-works/chord";
30320
30518
  import { withAbortSignal as withAbortSignal4 } from "@earendil-works/chord/context";
30321
30519
  import { UsageDoc as UsageDoc4 } from "@earendil-works/pi-durable";
30322
- import { Type as Type12 } from "typebox";
30520
+ import { obj as obj12, str as str12 } from "@earendil-works/pi-schema";
30323
30521
  async function openProjection(runtime, session, context) {
30324
- const view = await session.viewState();
30325
- const history = new Map;
30326
- let cursor;
30327
- do {
30328
- const page = await session.conversation.entries({ order: "ascending" }, 100, cursor, context);
30329
- for (const entry of page.items)
30330
- history.set(entry.id, entry);
30331
- cursor = page.next;
30332
- } while (cursor);
30522
+ const view = await session.follow();
30333
30523
  let changed;
30334
30524
  const stop = view.subscribe(() => changed?.(null));
30335
30525
  const stopMetadata = runtime.onDirectoryChange(() => changed?.(null));
30336
30526
  let childIds = new Set;
30337
- const stopChildren = runtime.harness.subscribeCommits(({ changes }) => {
30527
+ const stopChildren = onCommits(runtime.harness, ({ changes }) => {
30338
30528
  if (changes.some((change) => change.type === "entry" && change.value.kind === "pi.tool-result" && childIds.has(change.value.conversationId) || change.type === "document" && ([
30339
30529
  "app.neko.delegations",
30340
30530
  "app.neko.approvals",
@@ -30355,12 +30545,9 @@ async function openProjection(runtime, session, context) {
30355
30545
  };
30356
30546
  },
30357
30547
  async project() {
30358
- for (const entry of view.value.entries)
30359
- history.set(entry.id, entry);
30360
30548
  const [naming, compaction] = await Promise.all([runtime.namingUsage(session.id), session.compactionRecord()]);
30361
30549
  const projected = projectChatView({
30362
30550
  ...view.value,
30363
- entries: [...history.values()].sort((a, b) => a.id - b.id),
30364
30551
  docs: {
30365
30552
  ...view.value.docs,
30366
30553
  "pi.usage": mergeUsage(view.value.docs["pi.usage"] ?? {
@@ -30368,7 +30555,7 @@ async function openProjection(runtime, session, context) {
30368
30555
  tools: {}
30369
30556
  }, naming)
30370
30557
  }
30371
- }, "", { models: runtime.listModels(), contextEntries: view.value.entries, compaction });
30558
+ }, "", { models: runtime.listModels(), compaction });
30372
30559
  const jobs = (await session.jobs()).map(clipJob);
30373
30560
  const [todos, questions, plan] = await Promise.all([session.todos(), session.questions(), session.plan()]);
30374
30561
  const changes = await session.changeSummary().catch(() => {
@@ -30409,16 +30596,13 @@ async function openProjection(runtime, session, context) {
30409
30596
  },
30410
30597
  async entry(id, ctx) {
30411
30598
  ctx.abortSignal?.throwIfAborted();
30412
- for (const entry of view.value.entries)
30413
- history.set(entry.id, entry);
30414
- return history.get(id);
30599
+ return view.entry(id);
30415
30600
  },
30416
30601
  close() {
30417
30602
  stop();
30418
30603
  stopMetadata();
30419
30604
  stopChildren();
30420
30605
  view.dispose();
30421
- history.clear();
30422
30606
  }
30423
30607
  };
30424
30608
  }
@@ -30594,6 +30778,14 @@ function serveSessions(host, runtime, context) {
30594
30778
  await projection.value.session.abort();
30595
30779
  return null;
30596
30780
  },
30781
+ continueRun: async () => {
30782
+ try {
30783
+ await projection.value.session.continueRun();
30784
+ return null;
30785
+ } catch (error) {
30786
+ throw new ChordError4("invalid", error instanceof Error ? error.message : "Cannot continue");
30787
+ }
30788
+ },
30597
30789
  withdraw: ({ id }) => projection.value.session.withdraw(id),
30598
30790
  steer: ({ id }) => projection.value.session.steer(id),
30599
30791
  questionGet: async ({ id }) => await projection.value.session.question(id) ?? null,
@@ -30693,7 +30885,7 @@ var init_session_host = __esm(() => {
30693
30885
  init_contract();
30694
30886
  init_image_uploads();
30695
30887
  init_subagent_host();
30696
- OwnerInput = Type12.Object({});
30888
+ OwnerInput = obj12({}, { open: true });
30697
30889
  Owner = local2("neko.attachment-owner", {
30698
30890
  version: 1,
30699
30891
  input: OwnerInput,
@@ -30701,7 +30893,7 @@ var init_session_host = __esm(() => {
30701
30893
  });
30702
30894
  Projection = local2("neko.projection", {
30703
30895
  version: 1,
30704
- input: Type12.Object({ id: Type12.String() }),
30896
+ input: obj12({ id: str12() }, { open: true }),
30705
30897
  members: {
30706
30898
  view: ChatViewSchema,
30707
30899
  reader: opaque2("neko.projection-reader@1")
@@ -31422,7 +31614,7 @@ var init_workspaces = __esm(() => {
31422
31614
  // packages/gateway/src/server-services.ts
31423
31615
  import { randomUUID as randomUUID6 } from "crypto";
31424
31616
  import { ChordError as ChordError5, local as local3, method as method2 } from "@earendil-works/chord";
31425
- import { Type as Type13 } from "typebox";
31617
+ import { nul as nul6, obj as obj13 } from "@earendil-works/pi-schema";
31426
31618
  async function createNekoServerServices(options) {
31427
31619
  const { runtime, context, host } = options;
31428
31620
  const updates = new UpdateChecker(options.updates);
@@ -31566,7 +31758,7 @@ async function createNekoServerServices(options) {
31566
31758
  searchSettings.replace(ctx, { revision: current.revision + 1, ...next });
31567
31759
  return null;
31568
31760
  };
31569
- self2.on("directory", { subscribe: runtime.harness.subscribeCommits.bind(runtime.harness) }, async (event, ctx) => {
31761
+ self2.on("directory", { subscribe: (listener) => onCommits(runtime.harness, listener) }, async (event, ctx) => {
31570
31762
  if (!event.changes.some((change) => change.type === "conversation" || change.type === "document" && [
31571
31763
  "app.neko.session-metadata",
31572
31764
  "app.neko.activity",
@@ -31783,7 +31975,7 @@ var init_server_services = __esm(() => {
31783
31975
  init_workspaces();
31784
31976
  ServerState = local3("neko.server-state", {
31785
31977
  version: 1,
31786
- input: Type13.Object({}),
31978
+ input: obj13({}, { open: true }),
31787
31979
  members: {
31788
31980
  directory: DirectoryStateSchema,
31789
31981
  workspaces: WorkspaceStateSchema,
@@ -31792,11 +31984,11 @@ var init_server_services = __esm(() => {
31792
31984
  contextSettings: ContextSettingsStateSchema,
31793
31985
  codemodeSettings: CodemodeSettingsStateSchema,
31794
31986
  defaultModel: DefaultModelStateSchema,
31795
- refreshCredentials: method2(Type13.Null(), Type13.Null()),
31796
- refreshSearchSettings: method2(Type13.Null(), Type13.Null()),
31797
- refreshContextSettings: method2(Type13.Null(), Type13.Null()),
31798
- refreshCodemodeSettings: method2(Type13.Null(), Type13.Null()),
31799
- refreshDefaultModel: method2(Type13.Null(), Type13.Null())
31987
+ refreshCredentials: method2(nul6(), nul6()),
31988
+ refreshSearchSettings: method2(nul6(), nul6()),
31989
+ refreshContextSettings: method2(nul6(), nul6()),
31990
+ refreshCodemodeSettings: method2(nul6(), nul6()),
31991
+ refreshDefaultModel: method2(nul6(), nul6())
31800
31992
  }
31801
31993
  });
31802
31994
  });
@@ -31828,6 +32020,7 @@ async function startGateway(options = {}) {
31828
32020
  ...searchProvider !== undefined ? { searchProvider } : {},
31829
32021
  ...fileConfig.context !== undefined ? { contextSettings: fileConfig.context } : {},
31830
32022
  ...fileConfig.codemode !== undefined ? { codemodeSettings: fileConfig.codemode } : {},
32023
+ ...fileConfig.maxTurns !== undefined ? { maxTurns: fileConfig.maxTurns } : {},
31831
32024
  ...fileConfig.defaultModel !== undefined ? { model: fileConfig.defaultModel } : {},
31832
32025
  ...options.model !== undefined ? { modelOverride: options.model } : {},
31833
32026
  thinkingLevel: options.thinkingLevel ?? fileConfig.thinkingLevel ?? "high",
@@ -31953,7 +32146,7 @@ async function bindGateway(runtime, options) {
31953
32146
  }
31954
32147
  };
31955
32148
  runtime.closed.then(async (result) => {
31956
- if (result.reason === "failed") {
32149
+ if (result.kind === "failed") {
31957
32150
  options.onError?.(new Error("Gateway storage failed"));
31958
32151
  await gateway.close();
31959
32152
  }
@@ -32967,6 +33160,21 @@ Latest: {latest}`,
32967
33160
  "core.unsupported.todos": "This gateway does not support todos. Restart the gateway to enable them.",
32968
33161
  "core.unsupported.questions": "This gateway does not support questions. Restart the gateway to enable them.",
32969
33162
  "core.unsupported.plan": "This gateway does not support plan mode. Restart the gateway to enable it.",
33163
+ "core.unsupported.continue": "This gateway cannot continue a run. Restart the gateway to enable it.",
33164
+ "core.error.continueBusy": "CONTINUE_BUSY: The session is running; wait for it to finish.",
33165
+ "core.error.continueUnavailable": "CONTINUE_UNAVAILABLE: The last run was answered; there is nothing to continue.",
33166
+ "runFailure.reason.model_error": "Model request failed",
33167
+ "runFailure.reason.no_model": "No model is available",
33168
+ "runFailure.reason.aborted": "Stopped",
33169
+ "runFailure.reason.reset": "A new context started",
33170
+ "runFailure.reason.faulted": "The run failed",
33171
+ "runFailure.reason.orphaned": "The run could not continue",
33172
+ "runFailure.reason.interrupted": "The run was interrupted",
33173
+ "runFailure.reason.restart_limit": "Stopped after repeated crashes",
33174
+ "runFailure.reason.nothing_to_answer": "Nothing to answer",
33175
+ "runFailure.reason.turn_limit": "Turn limit reached",
33176
+ "runFailure.hint": "/continue to continue",
33177
+ "command.continue": "Continue the unfinished last run on its current context",
32970
33178
  "core.error.planBusy": "PLAN_BUSY: Wait for the current run to finish or stop it first.",
32971
33179
  "core.error.providerDisabled": "PROVIDER_DISABLED: {name} is not enabled. Enable it in /login or Web settings, or switch model.",
32972
33180
  "core.error.providerDisabledShort": "PROVIDER_DISABLED: {name} is not enabled.",
@@ -33609,6 +33817,21 @@ var init_zh = __esm(() => {
33609
33817
  "core.unsupported.todos": "\u6B64\u7F51\u5173\u4E0D\u652F\u6301\u5F85\u529E\u3002\u8BF7\u91CD\u542F\u7F51\u5173\u4EE5\u542F\u7528\u3002",
33610
33818
  "core.unsupported.questions": "\u6B64\u7F51\u5173\u4E0D\u652F\u6301\u63D0\u95EE\u3002\u8BF7\u91CD\u542F\u7F51\u5173\u4EE5\u542F\u7528\u3002",
33611
33819
  "core.unsupported.plan": "\u6B64\u7F51\u5173\u4E0D\u652F\u6301\u8BA1\u5212\u6A21\u5F0F\u3002\u8BF7\u91CD\u542F\u7F51\u5173\u4EE5\u542F\u7528\u3002",
33820
+ "core.unsupported.continue": "\u6B64\u7F51\u5173\u4E0D\u652F\u6301\u7EE7\u7EED\u8FD0\u884C\u3002\u8BF7\u91CD\u542F\u7F51\u5173\u4EE5\u542F\u7528\u3002",
33821
+ "core.error.continueBusy": "CONTINUE_BUSY: \u4F1A\u8BDD\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5B83\u7ED3\u675F\u3002",
33822
+ "core.error.continueUnavailable": "CONTINUE_UNAVAILABLE: \u4E0A\u4E00\u8F6E\u5DF2\u5F97\u5230\u56DE\u7B54\uFF0C\u6CA1\u6709\u53EF\u7EE7\u7EED\u7684\u5185\u5BB9\u3002",
33823
+ "runFailure.reason.model_error": "\u6A21\u578B\u8BF7\u6C42\u5931\u8D25",
33824
+ "runFailure.reason.no_model": "\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B",
33825
+ "runFailure.reason.aborted": "\u5DF2\u505C\u6B62",
33826
+ "runFailure.reason.reset": "\u5DF2\u5F00\u59CB\u65B0\u7684\u4E0A\u4E0B\u6587",
33827
+ "runFailure.reason.faulted": "\u8FD0\u884C\u51FA\u9519",
33828
+ "runFailure.reason.orphaned": "\u8FD0\u884C\u65E0\u6CD5\u7EE7\u7EED",
33829
+ "runFailure.reason.interrupted": "\u8FD0\u884C\u88AB\u4E2D\u65AD",
33830
+ "runFailure.reason.restart_limit": "\u591A\u6B21\u5D29\u6E83\u540E\u5DF2\u505C\u6B62",
33831
+ "runFailure.reason.nothing_to_answer": "\u6CA1\u6709\u9700\u8981\u56DE\u7B54\u7684\u5185\u5BB9",
33832
+ "runFailure.reason.turn_limit": "\u5DF2\u8FBE\u5230\u8F6E\u6B21\u4E0A\u9650",
33833
+ "runFailure.hint": "/continue \u7EE7\u7EED",
33834
+ "command.continue": "\u5728\u73B0\u6709\u4E0A\u4E0B\u6587\u4E0A\u7EE7\u7EED\u672A\u5B8C\u6210\u7684\u4E0A\u4E00\u8F6E",
33612
33835
  "core.error.planBusy": "PLAN_BUSY: \u8BF7\u7B49\u5F85\u5F53\u524D\u8FD0\u884C\u7ED3\u675F\uFF0C\u6216\u5148\u505C\u6B62\u5B83\u3002",
33613
33836
  "core.error.providerDisabled": "PROVIDER_DISABLED: {name} \u672A\u542F\u7528\u3002\u8BF7\u5728 /login \u6216 Web \u8BBE\u7F6E\u4E2D\u542F\u7528\uFF0C\u6216\u5207\u6362\u6A21\u578B\u3002",
33614
33837
  "core.error.providerDisabledShort": "PROVIDER_DISABLED: {name} \u672A\u542F\u7528\u3002",
@@ -33681,6 +33904,9 @@ function formatMessage(template, params = {}) {
33681
33904
  function translate(language, key, params) {
33682
33905
  return formatMessage(dictionaries[language][key], params);
33683
33906
  }
33907
+ function hasMessage(key) {
33908
+ return Object.hasOwn(zh2, key);
33909
+ }
33684
33910
  function t(key, ...args) {
33685
33911
  return translate(current, key, args[0]);
33686
33912
  }
@@ -46033,7 +46259,10 @@ var init_core_text = __esm(() => {
46033
46259
  [TODOS_UNSUPPORTED, "core.unsupported.todos"],
46034
46260
  [QUESTIONS_UNSUPPORTED, "core.unsupported.questions"],
46035
46261
  [PLAN_UNSUPPORTED, "core.unsupported.plan"],
46036
- ["PLAN_BUSY: Wait for the current run to finish or stop it first.", "core.error.planBusy"]
46262
+ [CONTINUE_UNSUPPORTED, "core.unsupported.continue"],
46263
+ ["PLAN_BUSY: Wait for the current run to finish or stop it first.", "core.error.planBusy"],
46264
+ ["CONTINUE_BUSY: The session is running; wait for it to finish.", "core.error.continueBusy"],
46265
+ ["CONTINUE_UNAVAILABLE: The last run was answered; there is nothing to continue.", "core.error.continueUnavailable"]
46037
46266
  ]);
46038
46267
  ERRORS = [
46039
46268
  {
@@ -46318,6 +46547,7 @@ var commands = () => [
46318
46547
  { name: "help", description: t("command.help") },
46319
46548
  { name: "version", description: t("command.version") },
46320
46549
  { name: "compact", description: t("command.compact") },
46550
+ { name: "continue", description: t("command.continue") },
46321
46551
  { name: "copy", description: t("command.copy") },
46322
46552
  { name: "export", description: t("command.export") },
46323
46553
  { name: "exit", description: t("command.exit") },
@@ -50262,7 +50492,7 @@ function createAppLayout(parts) {
50262
50492
  const editorRows = Math.min(7, Math.max(3, parts.editor.render(v.width).length));
50263
50493
  const noticeRows = Math.min(1, parts.notices.render(v.width).length);
50264
50494
  const busyRows = parts.busyVisible?.() ? Math.min(2, parts.busy?.render(v.width).length ?? 0) : 0;
50265
- const queueRows = parts.queueVisible?.() ? Math.min(3, parts.queue?.render(v.width).length ?? 0) : 0;
50495
+ const queueRows = (parts.queueVisible?.() ? Math.min(3, parts.queue?.render(v.width).length ?? 0) : 0) + (parts.failureVisible?.() ? Math.min(1, parts.failure?.render(v.width).length ?? 0) : 0);
50266
50496
  if (parts.question)
50267
50497
  parts.question.height = Math.max(0, Math.min(QUESTION_MAX_ROWS, v.height - approvalRows - editorRows - 2 - noticeRows - busyRows - queueRows - 1));
50268
50498
  const questionRows = parts.questionVisible?.() && parts.question ? Math.min(parts.question.height, parts.question.render(v.width).length) : 0;
@@ -50287,6 +50517,12 @@ function createAppLayout(parts) {
50287
50517
  shrink: 0,
50288
50518
  visible: (v) => normal(v) && (parts.busyVisible?.() ?? false)
50289
50519
  },
50520
+ {
50521
+ component: parts.failure ?? new Text8("", 0, 0),
50522
+ maxSize: 1,
50523
+ shrink: 0,
50524
+ visible: (v) => normal(v) && (parts.failureVisible?.() ?? false)
50525
+ },
50290
50526
  {
50291
50527
  component: parts.queue ?? new Text8("", 0, 0),
50292
50528
  maxSize: 3,
@@ -50604,6 +50840,49 @@ var init_queue_ui = __esm(() => {
50604
50840
  QUEUE_BLOCK_LINES = QUEUE_ROWS + 1;
50605
50841
  });
50606
50842
 
50843
+ // packages/tui/src/run-failure-ui.ts
50844
+ import { truncateToWidth as truncateToWidth9 } from "@earendil-works/pi-tui";
50845
+ function runFailureReason(reason) {
50846
+ const key = `runFailure.reason.${reason}`;
50847
+ return hasMessage(key) ? t(key) : reason;
50848
+ }
50849
+
50850
+ class RunFailureRow {
50851
+ #failure;
50852
+ set(failure) {
50853
+ this.#failure = failure;
50854
+ }
50855
+ get visible() {
50856
+ return this.#failure !== undefined;
50857
+ }
50858
+ render(width) {
50859
+ const failure = this.#failure;
50860
+ if (!failure)
50861
+ return [];
50862
+ const detail = failure.detail ? `: ${failure.detail.replace(/\s+/g, " ").trim()}` : "";
50863
+ const hint = NOT_CONTINUABLE.has(failure.reason) ? "" : theme.dim(` \xB7 ${t("runFailure.hint")}`);
50864
+ return [truncateToWidth9(` ${theme.warning(`${runFailureReason(failure.reason)}${detail}`)}${hint}`, width)];
50865
+ }
50866
+ invalidate() {}
50867
+ }
50868
+ async function continueCommand(client) {
50869
+ if (!client.supported)
50870
+ return t("core.unsupported.continue");
50871
+ try {
50872
+ await client.continueRun();
50873
+ return;
50874
+ } catch (error) {
50875
+ return localizeError(error instanceof Error ? error.message : String(error));
50876
+ }
50877
+ }
50878
+ var NOT_CONTINUABLE;
50879
+ var init_run_failure_ui = __esm(() => {
50880
+ init_core_text();
50881
+ init_i18n();
50882
+ init_theme();
50883
+ NOT_CONTINUABLE = new Set(["nothing_to_answer"]);
50884
+ });
50885
+
50607
50886
  // packages/tui/src/search-ui.ts
50608
50887
  function searchItems(settings, credentials) {
50609
50888
  if (!settings)
@@ -51248,7 +51527,7 @@ var init_subagents_ui = __esm(() => {
51248
51527
  });
51249
51528
 
51250
51529
  // packages/tui/src/todos-ui.ts
51251
- import { matchesKey as matchesKey16, truncateToWidth as truncateToWidth9, visibleWidth as visibleWidth7, wrapTextWithAnsi as wrapTextWithAnsi8 } from "@earendil-works/pi-tui";
51530
+ import { matchesKey as matchesKey16, truncateToWidth as truncateToWidth10, visibleWidth as visibleWidth7, wrapTextWithAnsi as wrapTextWithAnsi8 } from "@earendil-works/pi-tui";
51252
51531
  function todoProgressText(list) {
51253
51532
  if (!list || list.items.length === 0)
51254
51533
  return;
@@ -51323,7 +51602,7 @@ class TodosDetail {
51323
51602
  to: Math.min(rows.length, this.#offset + room),
51324
51603
  total: rows.length
51325
51604
  }) : "";
51326
- const header = truncateToWidth9(`${theme.accent(t("todos.completedOf", { completed, total }))}${theme.dim(range)}`, width);
51605
+ const header = truncateToWidth10(`${theme.accent(t("todos.completedOf", { completed, total }))}${theme.dim(range)}`, width);
51327
51606
  return [...offline, header, ...legendRow, ...rows.slice(this.#offset, this.#offset + room)];
51328
51607
  }
51329
51608
  }
@@ -51564,6 +51843,7 @@ class NekoTuiApp {
51564
51843
  #commandCaps = "";
51565
51844
  #switching = false;
51566
51845
  #queueRow = new QueueRow;
51846
+ #failureRow = new RunFailureRow;
51567
51847
  #searchStop;
51568
51848
  #contextStop;
51569
51849
  #codemodeStop;
@@ -51799,6 +52079,8 @@ class NekoTuiApp {
51799
52079
  notices,
51800
52080
  busy: this.#busyLoader,
51801
52081
  busyVisible: () => this.#busyText !== undefined,
52082
+ failure: this.#failureRow,
52083
+ failureVisible: () => this.#failureRow.visible,
51802
52084
  queue: this.#queueRow,
51803
52085
  queueVisible: () => this.#queueRow.visible,
51804
52086
  panelOpen: () => this.#panel.open || this.#login.open
@@ -51900,6 +52182,10 @@ class NekoTuiApp {
51900
52182
  this.openLanguageOverlay();
51901
52183
  return;
51902
52184
  }
52185
+ if (trimmed === "/continue") {
52186
+ this.#continueRun();
52187
+ return;
52188
+ }
51903
52189
  const instructions = compactInstructions(trimmed);
51904
52190
  if (instructions !== null) {
51905
52191
  const chat = this.#chat;
@@ -52604,6 +52890,15 @@ class NekoTuiApp {
52604
52890
  this.scheduleApply();
52605
52891
  });
52606
52892
  }
52893
+ async#continueRun() {
52894
+ const chat = this.#chat;
52895
+ if (!chat)
52896
+ return;
52897
+ const session = this.#sessionId;
52898
+ const notice = await continueCommand(continueClient(chat));
52899
+ if (notice !== undefined && session === this.#sessionId)
52900
+ this.addNotice(notice, "warning", { wrap: true });
52901
+ }
52607
52902
  applyView(view) {
52608
52903
  this.#lastView = view;
52609
52904
  if (this.#historyItems !== view.items) {
@@ -52619,6 +52914,7 @@ class NekoTuiApp {
52619
52914
  if (this.#jobsPanel)
52620
52915
  this.#panel.update(jobItems(view.jobs ?? []));
52621
52916
  this.#queueRow.setItems(view.queued ?? []);
52917
+ this.#failureRow.set(view.continuable && !view.busy ? view.runFailure : undefined);
52622
52918
  this.#question.update(view.questions);
52623
52919
  this.#plan.update(view.plan);
52624
52920
  if (this.#commandCaps !== [...hiddenCommands(view)].join(","))
@@ -52948,6 +53244,8 @@ function hiddenCommands(view) {
52948
53244
  hidden.add("question");
52949
53245
  if (view.plan === undefined)
52950
53246
  hidden.add("plan");
53247
+ if (view.continuable === undefined)
53248
+ hidden.add("continue");
52951
53249
  return hidden;
52952
53250
  }
52953
53251
  async function runTui(options = {}) {
@@ -53027,6 +53325,7 @@ var init_tui = __esm(() => {
53027
53325
  init_provider_ui();
53028
53326
  init_question_dock();
53029
53327
  init_queue_ui();
53328
+ init_run_failure_ui();
53030
53329
  init_search_ui();
53031
53330
  init_shell_notice();
53032
53331
  init_skill_market_ui();