neko-harness 0.4.7 → 0.4.9
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/README.md +195 -47
- package/dist/client/assets/index-CP-ffuep.css +2 -0
- package/dist/client/assets/index-LHUxaMG9.js +396 -0
- package/dist/client/index.html +36 -2
- package/dist/gateway.js +1902 -1202
- package/dist/neko.js +40349 -37130
- package/package.json +1 -1
- package/dist/client/assets/index-Cdi87SA6.css +0 -2
- package/dist/client/assets/index-DYhp2f2g.js +0 -395
package/dist/gateway.js
CHANGED
|
@@ -16598,7 +16598,7 @@ function parseGatewayArgs(argv) {
|
|
|
16598
16598
|
}
|
|
16599
16599
|
|
|
16600
16600
|
// packages/gateway/src/gateway.ts
|
|
16601
|
-
import { randomUUID as
|
|
16601
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
16602
16602
|
import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
|
|
16603
16603
|
import { basename as basename4, dirname as dirname9, join as join13 } from "path";
|
|
16604
16604
|
import { createHost as createHost2 } from "@earendil-works/chord";
|
|
@@ -17173,7 +17173,7 @@ function queuedUserInput(raw) {
|
|
|
17173
17173
|
if (item.entry?.kind !== NekoInput.kind)
|
|
17174
17174
|
return;
|
|
17175
17175
|
const data = item.entry.data;
|
|
17176
|
-
if (!data || data.jobReport || data.subagentRoundId)
|
|
17176
|
+
if (!data || data.jobReport || data.planDecision || data.subagentRoundId)
|
|
17177
17177
|
return;
|
|
17178
17178
|
const defaultPrompt = Array.isArray(item.content) ? savedDefaultImagePrompt(item.content, data.defaultImagePrompt) : undefined;
|
|
17179
17179
|
const content = defaultPrompt && Array.isArray(item.content) ? item.content.slice(1) : item.content;
|
|
@@ -17259,6 +17259,11 @@ function toolInteraction(name, details) {
|
|
|
17259
17259
|
const { action, revision, completed, total } = details;
|
|
17260
17260
|
return (action === "list" || action === "replace") && typeof revision === "number" && typeof completed === "number" && typeof total === "number" ? { kind: "todo", action, revision, completed, total } : undefined;
|
|
17261
17261
|
}
|
|
17262
|
+
if (name === "submit_plan") {
|
|
17263
|
+
const { version, title, note } = details;
|
|
17264
|
+
const steps = strings(details.steps);
|
|
17265
|
+
return typeof version === "string" && typeof title === "string" && typeof note === "string" && steps ? { kind: "submit_plan", version, title, steps, note } : undefined;
|
|
17266
|
+
}
|
|
17262
17267
|
if (name !== "ask_user")
|
|
17263
17268
|
return;
|
|
17264
17269
|
const { id, question, multiple, options, status } = details;
|
|
@@ -17304,6 +17309,14 @@ function summarizeToolCall(name, args) {
|
|
|
17304
17309
|
return {
|
|
17305
17310
|
args: truncateMiddle(question.replace(/\s+/g, " "), 100),
|
|
17306
17311
|
detail: [question, ...lines.length ? ["", ...lines] : []].join(`
|
|
17312
|
+
`)
|
|
17313
|
+
};
|
|
17314
|
+
}
|
|
17315
|
+
case "submit_plan": {
|
|
17316
|
+
const steps = strings(record.steps) ?? [];
|
|
17317
|
+
return {
|
|
17318
|
+
args: truncateMiddle(String(record.title ?? "").replace(/\s+/g, " "), 100),
|
|
17319
|
+
detail: steps.map((step, index) => `${index + 1}. ${step}`).join(`
|
|
17307
17320
|
`)
|
|
17308
17321
|
};
|
|
17309
17322
|
}
|
|
@@ -17484,6 +17497,17 @@ function projectChatView(view, modelLabel, options = {}) {
|
|
|
17484
17497
|
for (const entry of view.entries) {
|
|
17485
17498
|
const message = entry.model?.[0];
|
|
17486
17499
|
const jobReport = entry.kind === NekoInput.kind ? entry.data?.jobReport : undefined;
|
|
17500
|
+
const planDecision = entry.kind === NekoInput.kind ? entry.data?.planDecision : undefined;
|
|
17501
|
+
if (message?.role === "user" && planDecision) {
|
|
17502
|
+
items.push({
|
|
17503
|
+
type: "system",
|
|
17504
|
+
key: `e${entry.id}`,
|
|
17505
|
+
id: entry.id,
|
|
17506
|
+
text: planDecision.kind === "approve" ? `Plan approved: ${planDecision.title}` : `Changes requested for plan "${planDecision.title}": ${planDecision.feedback ?? ""}`,
|
|
17507
|
+
planDecision: { version: planDecision.version, kind: planDecision.kind }
|
|
17508
|
+
});
|
|
17509
|
+
continue;
|
|
17510
|
+
}
|
|
17487
17511
|
if (message?.role === "user" && jobReport) {
|
|
17488
17512
|
items.push({
|
|
17489
17513
|
type: "system",
|
|
@@ -17687,7 +17711,7 @@ function contextTokens(entries) {
|
|
|
17687
17711
|
import {
|
|
17688
17712
|
AssistantEntry,
|
|
17689
17713
|
configure,
|
|
17690
|
-
defineDoc,
|
|
17714
|
+
defineDoc as defineDoc2,
|
|
17691
17715
|
defineExtension,
|
|
17692
17716
|
defineTask,
|
|
17693
17717
|
defineTool,
|
|
@@ -17697,6 +17721,19 @@ import {
|
|
|
17697
17721
|
import { Type } from "typebox";
|
|
17698
17722
|
import { Value } from "typebox/value";
|
|
17699
17723
|
|
|
17724
|
+
// packages/core/src/plan-doc.ts
|
|
17725
|
+
import { defineDoc } from "@earendil-works/pi-durable";
|
|
17726
|
+
var PLAN_HISTORY = 5;
|
|
17727
|
+
var PlanDoc = defineDoc({
|
|
17728
|
+
kind: "app.neko.plan",
|
|
17729
|
+
version: 1,
|
|
17730
|
+
scope: "conversation",
|
|
17731
|
+
history: "rewindable",
|
|
17732
|
+
fork: "asOf",
|
|
17733
|
+
initial: () => ({ mode: "normal", revision: 0, items: [] })
|
|
17734
|
+
});
|
|
17735
|
+
var PLAN_CODER_BLOCKED = "Plan mode is read-only: the coder subagent is unavailable. Use explore or plan subagents, or finish the plan first.";
|
|
17736
|
+
|
|
17700
17737
|
// packages/core/src/subagent-usage.ts
|
|
17701
17738
|
var emptySubagentUsage = () => ({ models: {}, tools: {} });
|
|
17702
17739
|
function subtractSubagentUsage(end, start) {
|
|
@@ -17741,7 +17778,7 @@ function subagentUsageTotals(state) {
|
|
|
17741
17778
|
|
|
17742
17779
|
// packages/core/src/subagents.ts
|
|
17743
17780
|
var agentIdFor = (childId) => `agent-${childId}`;
|
|
17744
|
-
var DelegationsDoc =
|
|
17781
|
+
var DelegationsDoc = defineDoc2({
|
|
17745
17782
|
kind: "app.neko.delegations",
|
|
17746
17783
|
version: 1,
|
|
17747
17784
|
scope: "conversation",
|
|
@@ -17749,7 +17786,7 @@ var DelegationsDoc = defineDoc({
|
|
|
17749
17786
|
fork: "initial",
|
|
17750
17787
|
initial: () => ({ items: [] })
|
|
17751
17788
|
});
|
|
17752
|
-
var SubagentsDoc =
|
|
17789
|
+
var SubagentsDoc = defineDoc2({
|
|
17753
17790
|
kind: "app.neko.subagents",
|
|
17754
17791
|
version: 1,
|
|
17755
17792
|
scope: "conversation",
|
|
@@ -17757,7 +17794,7 @@ var SubagentsDoc = defineDoc({
|
|
|
17757
17794
|
fork: "initial",
|
|
17758
17795
|
initial: () => ({ items: [] })
|
|
17759
17796
|
});
|
|
17760
|
-
var SubagentDoc =
|
|
17797
|
+
var SubagentDoc = defineDoc2({
|
|
17761
17798
|
kind: "app.neko.subagent",
|
|
17762
17799
|
version: 1,
|
|
17763
17800
|
scope: "conversation",
|
|
@@ -17765,7 +17802,7 @@ var SubagentDoc = defineDoc({
|
|
|
17765
17802
|
fork: "initial",
|
|
17766
17803
|
initial: () => ({ role: null, parentId: null })
|
|
17767
17804
|
});
|
|
17768
|
-
var SlotsDoc =
|
|
17805
|
+
var SlotsDoc = defineDoc2({
|
|
17769
17806
|
kind: "app.neko.subagent-slots",
|
|
17770
17807
|
version: 1,
|
|
17771
17808
|
scope: "session",
|
|
@@ -17777,8 +17814,8 @@ var ROLE_PROMPTS = {
|
|
|
17777
17814
|
coder: "You are neko's coding subagent. Implement only the assigned scope and verify it. Coordinate through the main agent; do not overwrite unrelated changes. Report changed files and verification results."
|
|
17778
17815
|
};
|
|
17779
17816
|
var SUBAGENT_DELIVERY = " Work from the assigned task, your own prior conversation context and project resources; you cannot see the main conversation. Do not delegate or ask the user directly. Your final message must be a self-contained delivery to the main agent, including evidence, results and limitations. Skills never expand your allowed tools.";
|
|
17780
|
-
var MAIN_ONLY_TOOLS = new Set(["todo", "ask_user"]);
|
|
17781
|
-
var MAIN_ONLY_EXTENSIONS = new Set(["neko-todos", "neko-questions"]);
|
|
17817
|
+
var MAIN_ONLY_TOOLS = new Set(["todo", "ask_user", "submit_plan"]);
|
|
17818
|
+
var MAIN_ONLY_EXTENSIONS = new Set(["neko-todos", "neko-questions", "neko-plan"]);
|
|
17782
17819
|
var READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract"]);
|
|
17783
17820
|
async function refreshDelegations(tx, id) {
|
|
17784
17821
|
const doc = await tx.doc(DelegationsDoc, id);
|
|
@@ -18078,6 +18115,9 @@ function createSubagentExtension(prepare, discard, guard) {
|
|
|
18078
18115
|
return receipt;
|
|
18079
18116
|
const identities = await tx.doc(SubagentsDoc, api.conversationId);
|
|
18080
18117
|
let identity;
|
|
18118
|
+
const planning = !receipt && (await tx.doc(PlanDoc, api.conversationId)).mode === "plan";
|
|
18119
|
+
if (planning && args.action !== "continue" && args.type === "coder")
|
|
18120
|
+
throw new Error(PLAN_CODER_BLOCKED);
|
|
18081
18121
|
if (args.action === "continue") {
|
|
18082
18122
|
identity = identities.items.find((a) => a.agentId === args.agentId);
|
|
18083
18123
|
if (!identity)
|
|
@@ -18085,6 +18125,8 @@ function createSubagentExtension(prepare, discard, guard) {
|
|
|
18085
18125
|
const info = await tx.doc(SubagentDoc, identity.childId);
|
|
18086
18126
|
if (info.parentId !== api.conversationId || info.role !== identity.role)
|
|
18087
18127
|
throw new Error("Subagent ownership mismatch.");
|
|
18128
|
+
if (planning && identity.role === "coder")
|
|
18129
|
+
throw new Error(PLAN_CODER_BLOCKED);
|
|
18088
18130
|
if (identity.activeRoundId !== null)
|
|
18089
18131
|
throw new Error("Subagent is busy; continue only when idle.");
|
|
18090
18132
|
} else if (receipt) {
|
|
@@ -18646,6 +18688,13 @@ var QUESTION_LIMITS = {
|
|
|
18646
18688
|
previewChars: 256,
|
|
18647
18689
|
recordBytes: 65536
|
|
18648
18690
|
};
|
|
18691
|
+
var PLAN_LIMITS = {
|
|
18692
|
+
titleChars: 120,
|
|
18693
|
+
maxSteps: 30,
|
|
18694
|
+
stepChars: 500,
|
|
18695
|
+
noteChars: 4000,
|
|
18696
|
+
feedbackChars: 2000
|
|
18697
|
+
};
|
|
18649
18698
|
var TODO_STATUSES = ["pending", "in_progress", "completed"];
|
|
18650
18699
|
|
|
18651
18700
|
class InteractionInputError extends Error {
|
|
@@ -18830,8 +18879,44 @@ var todoProgress = (items) => ({
|
|
|
18830
18879
|
completed: items.filter((i) => i.status === "completed").length,
|
|
18831
18880
|
total: items.length
|
|
18832
18881
|
});
|
|
18882
|
+
function validatePlanArgs(input) {
|
|
18883
|
+
if (!isRecord2(input))
|
|
18884
|
+
return fail("Plan arguments must be an object.");
|
|
18885
|
+
exactKeys(input, ["title", "steps", "note"], "Plan");
|
|
18886
|
+
const title = text(input.title, "title", PLAN_LIMITS.titleChars);
|
|
18887
|
+
if (!Array.isArray(input.steps))
|
|
18888
|
+
return fail("steps must be an array.");
|
|
18889
|
+
if (input.steps.length < 1)
|
|
18890
|
+
fail("A plan needs at least one step.");
|
|
18891
|
+
if (input.steps.length > PLAN_LIMITS.maxSteps)
|
|
18892
|
+
fail(`Too many steps (limit ${PLAN_LIMITS.maxSteps}).`);
|
|
18893
|
+
const steps = input.steps.map((step, index) => text(step, `steps[${index}]`, PLAN_LIMITS.stepChars));
|
|
18894
|
+
let note = "";
|
|
18895
|
+
if (input.note !== undefined) {
|
|
18896
|
+
if (typeof input.note !== "string")
|
|
18897
|
+
fail("note must be a string.");
|
|
18898
|
+
const value = input.note;
|
|
18899
|
+
if (value.length > PLAN_LIMITS.noteChars)
|
|
18900
|
+
fail(`note is too long (limit ${PLAN_LIMITS.noteChars} characters).`);
|
|
18901
|
+
if (!blank(value))
|
|
18902
|
+
note = value;
|
|
18903
|
+
}
|
|
18904
|
+
return { title, steps, note };
|
|
18905
|
+
}
|
|
18906
|
+
function validatePlanDecision(input) {
|
|
18907
|
+
if (!isRecord2(input))
|
|
18908
|
+
return fail("Decision must be an object.");
|
|
18909
|
+
if (input.kind === "approve") {
|
|
18910
|
+
exactKeys(input, ["kind"], "Decision");
|
|
18911
|
+
return { kind: "approve" };
|
|
18912
|
+
}
|
|
18913
|
+
if (input.kind !== "revise")
|
|
18914
|
+
return fail('Decision kind must be "approve" or "revise".');
|
|
18915
|
+
exactKeys(input, ["kind", "feedback"], "Decision");
|
|
18916
|
+
return { kind: "revise", feedback: text(input.feedback, "feedback", PLAN_LIMITS.feedbackChars) };
|
|
18917
|
+
}
|
|
18833
18918
|
// packages/core/src/jobs.ts
|
|
18834
|
-
import { defineDoc as
|
|
18919
|
+
import { defineDoc as defineDoc3 } from "@earendil-works/pi-durable";
|
|
18835
18920
|
var JOB_LIMITS = {
|
|
18836
18921
|
maxRunning: 4,
|
|
18837
18922
|
keepTerminal: 20,
|
|
@@ -18845,7 +18930,7 @@ var JOB_LIMITS = {
|
|
|
18845
18930
|
commandBytes: 32 * 1024,
|
|
18846
18931
|
maxTimeoutSeconds: 7 * 24 * 3600
|
|
18847
18932
|
};
|
|
18848
|
-
var JobsDoc =
|
|
18933
|
+
var JobsDoc = defineDoc3({
|
|
18849
18934
|
kind: "app.neko.jobs",
|
|
18850
18935
|
version: 1,
|
|
18851
18936
|
scope: "conversation",
|
|
@@ -18984,9 +19069,9 @@ import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
|
18984
19069
|
import {
|
|
18985
19070
|
AssistantEntry as AssistantEntry2,
|
|
18986
19071
|
createRegistry,
|
|
18987
|
-
defineDoc as
|
|
19072
|
+
defineDoc as defineDoc8,
|
|
18988
19073
|
Harness,
|
|
18989
|
-
InboxDoc as
|
|
19074
|
+
InboxDoc as InboxDoc3,
|
|
18990
19075
|
LiveDoc as importLiveDoc,
|
|
18991
19076
|
MemoryStorage,
|
|
18992
19077
|
UsageDoc as UsageDoc2,
|
|
@@ -19520,7 +19605,9 @@ class JobManager {
|
|
|
19520
19605
|
#reporter = createReporter();
|
|
19521
19606
|
#runner = this.#createRunner();
|
|
19522
19607
|
extension;
|
|
19523
|
-
|
|
19608
|
+
#startWindowMs;
|
|
19609
|
+
constructor(options = {}) {
|
|
19610
|
+
this.#startWindowMs = options.startWindowMs ?? JOB_LIMITS.startWindowMs;
|
|
19524
19611
|
this.extension = this.#createExtension();
|
|
19525
19612
|
}
|
|
19526
19613
|
#closing = false;
|
|
@@ -19710,7 +19797,7 @@ class JobManager {
|
|
|
19710
19797
|
throw error;
|
|
19711
19798
|
}
|
|
19712
19799
|
const live = entry;
|
|
19713
|
-
await Promise.race([live.exited, delay(
|
|
19800
|
+
await Promise.race([live.exited, delay(this.#startWindowMs, context.abortSignal)]);
|
|
19714
19801
|
live.windowOpen = false;
|
|
19715
19802
|
if (live.suppressReport)
|
|
19716
19803
|
await live.exited;
|
|
@@ -19785,499 +19872,238 @@ ${text}` : `No new output (next offset ${offset}).`);
|
|
|
19785
19872
|
}
|
|
19786
19873
|
}
|
|
19787
19874
|
|
|
19788
|
-
// packages/core/src/
|
|
19789
|
-
import {
|
|
19790
|
-
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
|
|
19794
|
-
|
|
19795
|
-
|
|
19796
|
-
|
|
19875
|
+
// packages/core/src/plan.ts
|
|
19876
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
19877
|
+
import {
|
|
19878
|
+
defineExtension as defineExtension6,
|
|
19879
|
+
defineTool as defineTool5,
|
|
19880
|
+
hook as hook2,
|
|
19881
|
+
InboxDoc,
|
|
19882
|
+
ToolTask as ToolTask2,
|
|
19883
|
+
wrapTool
|
|
19884
|
+
} from "@earendil-works/pi-durable";
|
|
19885
|
+
import { createBashTool } from "@earendil-works/pi-durable/tools";
|
|
19886
|
+
import { Type as Type5 } from "typebox";
|
|
19797
19887
|
|
|
19798
|
-
// packages/core/src/
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19814
|
-
|
|
19815
|
-
|
|
19816
|
-
|
|
19817
|
-
|
|
19818
|
-
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
|
|
19831
|
-
|
|
19832
|
-
}
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
|
|
19837
|
-
|
|
19838
|
-
|
|
19839
|
-
|
|
19888
|
+
// packages/core/src/plan-bash.ts
|
|
19889
|
+
var SAFE_PATTERNS = [
|
|
19890
|
+
/^\s*(cat|head|tail|nl|tac|rev|cut|tr|paste|column|fold|comm|wc|sort|uniq|diff|file|stat|du|df|tree)\b/,
|
|
19891
|
+
/^\s*(grep|egrep|fgrep|rg|fd|find|jq|bat|eza|ls|pwd|echo|printf|true|cd)\b/,
|
|
19892
|
+
/^\s*(basename|dirname|realpath|readlink|which|whereis|type|sha256sum|shasum|md5sum)\b/,
|
|
19893
|
+
/^\s*(uname|whoami|id|date|cal|uptime|ps|free)\b/,
|
|
19894
|
+
/^\s*env\s*$/,
|
|
19895
|
+
/^\s*printenv\b/,
|
|
19896
|
+
/^\s*sed\s+-n\b/,
|
|
19897
|
+
/^\s*awk\b/,
|
|
19898
|
+
/^\s*curl\b/,
|
|
19899
|
+
/^\s*wget\s+-O\s*-/,
|
|
19900
|
+
/^\s*git\s+(status|log|diff|show|blame|rev-parse|describe|shortlog|grep|cat-file|merge-base|name-rev|reflog|show-ref|for-each-ref|ls-[\w-]+)\b/,
|
|
19901
|
+
/^\s*git\s+config\s+--get\b/,
|
|
19902
|
+
/^\s*git\s+stash\s+list\b/,
|
|
19903
|
+
/^\s*git\s+branch(\s+(-a|-r|-v|-vv|-l|--all|--remotes|--list|--show-current))*\s*$/,
|
|
19904
|
+
/^\s*git\s+remote(\s+(-v|show\b.*|get-url\b.*))?\s*$/,
|
|
19905
|
+
/^\s*npm\s+(list|ls|view|info|search|outdated|audit)\b/,
|
|
19906
|
+
/^\s*(bun|pnpm)\s+(pm\s+ls|list|ls|why|outdated|info|view)\b/,
|
|
19907
|
+
/^\s*yarn\s+(list|info|why|audit)\b/,
|
|
19908
|
+
/^\s*(node|python3?|bun|deno)\s+(-v|--version)\s*$/
|
|
19909
|
+
];
|
|
19910
|
+
var DANGEROUS_FLAGS = [
|
|
19911
|
+
/--output\b/,
|
|
19912
|
+
/\bfind\b[^|;&]*\s-(delete|exec|execdir|ok|okdir|fprint0?|fprintf|fls)\b/,
|
|
19913
|
+
/\bsed\b[^|;&]*(\s-[A-Za-z]*i|--in-place)/,
|
|
19914
|
+
/\bsort\b[^|;&]*\s-[A-Za-z]*o/,
|
|
19915
|
+
/\btree\b[^|;&]*\s-o\b/,
|
|
19916
|
+
/\brg\b[^|;&]*--pre\b/,
|
|
19917
|
+
/\bfd\b[^|;&]*(\s-[xX]\b|--exec)/,
|
|
19918
|
+
/\bcurl\b[^|;&]*\s(-[A-Za-z]*[oOdXTF]\b|--(output|remote-name|data[\w-]*|request|upload-file|form|json)\b)/,
|
|
19919
|
+
/\bnpm\s+audit\s+fix\b/
|
|
19920
|
+
];
|
|
19921
|
+
var DANGEROUS_CONTENT = [
|
|
19922
|
+
/\bawk\b.*(system\s*\(|\bgetline\b|\|\s*"|\bprintf?\b[^}]*>)/,
|
|
19923
|
+
/\bsed\b.*['"\s;{0-9$/,][wWe]\s+\S/
|
|
19924
|
+
];
|
|
19925
|
+
function segments(command) {
|
|
19926
|
+
const result = [];
|
|
19927
|
+
let raw = "";
|
|
19928
|
+
let bare = "";
|
|
19929
|
+
let quote = null;
|
|
19930
|
+
const flush = () => {
|
|
19931
|
+
if (raw.trim() !== "")
|
|
19932
|
+
result.push({ raw, bare });
|
|
19933
|
+
raw = "";
|
|
19934
|
+
bare = "";
|
|
19935
|
+
};
|
|
19936
|
+
for (let i = 0;i < command.length; i++) {
|
|
19937
|
+
const char = command[i];
|
|
19938
|
+
if (quote) {
|
|
19939
|
+
raw += char;
|
|
19940
|
+
if (char === "\\" && quote === '"' && i + 1 < command.length)
|
|
19941
|
+
raw += command[++i];
|
|
19942
|
+
else if (char === quote) {
|
|
19943
|
+
quote = null;
|
|
19944
|
+
bare += char;
|
|
19945
|
+
}
|
|
19840
19946
|
continue;
|
|
19841
|
-
|
|
19842
|
-
|
|
19843
|
-
|
|
19844
|
-
|
|
19845
|
-
} catch (error) {
|
|
19846
|
-
if (!isMissing(error))
|
|
19847
|
-
report({ path: filePath, message: "Cannot read skill; skipped." });
|
|
19947
|
+
}
|
|
19948
|
+
if (char === "\\" && i + 1 < command.length) {
|
|
19949
|
+
raw += char + command[++i];
|
|
19950
|
+
bare += " ";
|
|
19848
19951
|
continue;
|
|
19849
19952
|
}
|
|
19850
|
-
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
|
|
19953
|
+
if (char === "'" || char === '"') {
|
|
19954
|
+
quote = char;
|
|
19955
|
+
raw += char;
|
|
19956
|
+
bare += char;
|
|
19957
|
+
continue;
|
|
19854
19958
|
}
|
|
19959
|
+
const two = command.slice(i, i + 2);
|
|
19960
|
+
if (two === "&&" || two === "||") {
|
|
19961
|
+
flush();
|
|
19962
|
+
i++;
|
|
19963
|
+
continue;
|
|
19964
|
+
}
|
|
19965
|
+
if (char === ";" || char === "|" || char === `
|
|
19966
|
+
`) {
|
|
19967
|
+
flush();
|
|
19968
|
+
continue;
|
|
19969
|
+
}
|
|
19970
|
+
raw += char;
|
|
19971
|
+
bare += char;
|
|
19855
19972
|
}
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md) and use that absolute path in tool commands.",
|
|
19874
|
-
"",
|
|
19875
|
-
"<available_skills>"
|
|
19876
|
-
];
|
|
19877
|
-
for (const skill of visible) {
|
|
19878
|
-
lines.push(" <skill>");
|
|
19879
|
-
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
19880
|
-
lines.push(` <description>${escapeXml(skill.description)}</description>`);
|
|
19881
|
-
lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
|
|
19882
|
-
lines.push(" </skill>");
|
|
19883
|
-
}
|
|
19884
|
-
lines.push("</available_skills>");
|
|
19885
|
-
return lines.join(`
|
|
19886
|
-
`);
|
|
19887
|
-
};
|
|
19888
|
-
|
|
19889
|
-
// packages/core/src/prompt/invoke.ts
|
|
19890
|
-
var expandSkillCommand = async (text, skills, readFile, report = warnPromptDiagnostic, metadata) => {
|
|
19891
|
-
if (!text.startsWith("/skill:"))
|
|
19892
|
-
return text;
|
|
19893
|
-
const spaceIndex = text.indexOf(" ");
|
|
19894
|
-
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
|
19895
|
-
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
19896
|
-
const skill = skills.find((s) => s.name === skillName);
|
|
19897
|
-
if (!skill)
|
|
19898
|
-
return text;
|
|
19899
|
-
let body;
|
|
19900
|
-
try {
|
|
19901
|
-
body = stripFrontmatter(await readFile(skill.filePath)).trim();
|
|
19902
|
-
} catch {
|
|
19903
|
-
report({ path: skill.filePath, message: "Cannot expand known skill; original input preserved." });
|
|
19904
|
-
return text;
|
|
19905
|
-
}
|
|
19906
|
-
metadata?.({
|
|
19907
|
-
name: skill.name,
|
|
19908
|
-
location: skill.filePath,
|
|
19909
|
-
body: `References are relative to ${dirname4(skill.filePath)}.
|
|
19910
|
-
|
|
19911
|
-
${body}`,
|
|
19912
|
-
args
|
|
19973
|
+
if (quote)
|
|
19974
|
+
return null;
|
|
19975
|
+
flush();
|
|
19976
|
+
return result;
|
|
19977
|
+
}
|
|
19978
|
+
var stripHarmlessRedirects = (text) => text.replace(/[0-9]?>&[0-9-]/g, " ").replace(/(?:[0-9]|&)?>>?\s*\/dev\/null\b/g, " ");
|
|
19979
|
+
function isSafeCommand(command) {
|
|
19980
|
+
const parts = segments(command);
|
|
19981
|
+
if (!parts?.length)
|
|
19982
|
+
return false;
|
|
19983
|
+
if (/\$\(|`|<\(|>\(/.test(command.replace(/'[^']*'/g, "''")))
|
|
19984
|
+
return false;
|
|
19985
|
+
return parts.every(({ raw, bare }) => {
|
|
19986
|
+
const text = stripHarmlessRedirects(bare);
|
|
19987
|
+
if (/[>&]|<</.test(text))
|
|
19988
|
+
return false;
|
|
19989
|
+
return SAFE_PATTERNS.some((pattern) => pattern.test(text)) && !DANGEROUS_FLAGS.some((pattern) => pattern.test(text)) && !DANGEROUS_CONTENT.some((pattern) => pattern.test(raw));
|
|
19913
19990
|
});
|
|
19914
|
-
|
|
19915
|
-
References are relative to ${dirname4(skill.filePath)}.
|
|
19916
|
-
|
|
19917
|
-
${body}
|
|
19918
|
-
</skill>`;
|
|
19919
|
-
return args ? `${block}
|
|
19991
|
+
}
|
|
19920
19992
|
|
|
19921
|
-
|
|
19993
|
+
// packages/core/src/user-questions.ts
|
|
19994
|
+
import { randomUUID } from "crypto";
|
|
19995
|
+
import {
|
|
19996
|
+
defineDoc as defineDoc4,
|
|
19997
|
+
defineDocFamily,
|
|
19998
|
+
defineExtension as defineExtension5,
|
|
19999
|
+
defineTool as defineTool4
|
|
20000
|
+
} from "@earendil-works/pi-durable";
|
|
20001
|
+
import { Type as Type4 } from "typebox";
|
|
20002
|
+
var QuestionDoc = defineDocFamily({
|
|
20003
|
+
kind: "app.neko.question",
|
|
20004
|
+
version: 1,
|
|
20005
|
+
scope: "conversation",
|
|
20006
|
+
history: "latest",
|
|
20007
|
+
fork: "initial",
|
|
20008
|
+
family: true,
|
|
20009
|
+
initial: (seed) => seed
|
|
20010
|
+
});
|
|
20011
|
+
var QuestionsDoc = defineDoc4({
|
|
20012
|
+
kind: "app.neko.questions",
|
|
20013
|
+
version: 1,
|
|
20014
|
+
scope: "conversation",
|
|
20015
|
+
history: "latest",
|
|
20016
|
+
fork: "initial",
|
|
20017
|
+
initial: () => ({ revision: 0, pending: [] })
|
|
20018
|
+
});
|
|
20019
|
+
var clone = (value) => JSON.parse(JSON.stringify(value));
|
|
20020
|
+
var MISSING = {
|
|
20021
|
+
id: 0,
|
|
20022
|
+
callId: "",
|
|
20023
|
+
version: "",
|
|
20024
|
+
question: "",
|
|
20025
|
+
multiple: false,
|
|
20026
|
+
options: [],
|
|
20027
|
+
status: "pending",
|
|
20028
|
+
answer: null,
|
|
20029
|
+
createdAt: 0,
|
|
20030
|
+
settledAt: null
|
|
19922
20031
|
};
|
|
20032
|
+
var argsOf = (record) => ({
|
|
20033
|
+
question: record.question,
|
|
20034
|
+
multiple: record.multiple,
|
|
20035
|
+
options: clone(record.options)
|
|
20036
|
+
});
|
|
20037
|
+
async function live(tx, id) {
|
|
20038
|
+
let cursor = id;
|
|
20039
|
+
for (let depth = 0;cursor !== undefined && depth < 16; depth++) {
|
|
20040
|
+
const task = await tx.task(cursor);
|
|
20041
|
+
if (!task || task.abortRequested || task.state.status === "terminal" || task.state.status === "completing")
|
|
20042
|
+
return false;
|
|
20043
|
+
cursor = task.owner;
|
|
20044
|
+
}
|
|
20045
|
+
return true;
|
|
20046
|
+
}
|
|
20047
|
+
function settle(record, index, status) {
|
|
20048
|
+
record.status = status;
|
|
20049
|
+
record.settledAt = Date.now();
|
|
20050
|
+
index.pending = index.pending.filter((id) => id !== record.id);
|
|
20051
|
+
index.revision += 1;
|
|
20052
|
+
}
|
|
19923
20053
|
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
search_text: [
|
|
19936
|
-
"Use search_text for literal text search and read for surrounding evidence. Partial results do not prove absence."
|
|
19937
|
-
],
|
|
19938
|
-
read: ["Use read to examine files instead of cat or sed."],
|
|
19939
|
-
write: ["Use write only for new files or complete rewrites."],
|
|
19940
|
-
edit: [
|
|
19941
|
-
"Use edit for precise changes (edits[].oldText must match exactly)",
|
|
19942
|
-
"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
|
|
19943
|
-
"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
|
|
19944
|
-
"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions."
|
|
19945
|
-
],
|
|
19946
|
-
bash: ["Use bash for file operations like ls, rg, find"],
|
|
19947
|
-
job: [
|
|
19948
|
-
'Use job {action:"start", command, title?, timeout?} for long-running commands (dev servers, watchers, long builds or tests) instead of bash, which would block. Jobs have no stdin. start returns within 2 seconds, with the early result if the command already ended; a job that ends later reports back to you automatically, so do not poll. Read new output with {action:"output", jobId, since?} using the offset from the previous call, list jobs with {action:"list"}, end one with {action:"stop", jobId}. Run short commands with bash.'
|
|
19949
|
-
],
|
|
19950
|
-
web_search: ["Use web_search for current information from the internet instead of guessing."],
|
|
19951
|
-
web_extract: ["Use web_extract to read the full content of a web page when you have its URL."],
|
|
19952
|
-
todo: [
|
|
19953
|
-
`Use todo for multi-step work: {action:"list"} reads the saved list, {action:"replace", items:[{id, text, status}]} overwrites the whole ordered list (status pending, in_progress or completed; [] clears it). Keep a short list and update it as you go, keep an item's id when you update the same task, and mark it completed only after the work is actually done. If you are unsure what the list holds (for example after the context was compacted), read it with list first instead of guessing or creating a second list. Do not create a list for simple questions or one-step tasks.`
|
|
19954
|
-
],
|
|
19955
|
-
ask_user: [
|
|
19956
|
-
"Use ask_user {question, options?: [{id, label, description?, recommended?}], multiple?} only when you need a decision or information you cannot determine yourself from the code, the environment or a reasonable assumption; do not ask about things you can find out. Keep the question short and self-contained. Make options short, distinct and understandable on their own, mark a clear preference with recommended, and set multiple: true only when several options may apply (it needs at least 2 options). The user can always type text instead of or in addition to choosing, or skip. The call returns when the user answers or skips; do not poll. A skip is not consent or a preference: continue on your best judgment, state the assumptions you make, and do not ask the same question again unless circumstances change. Independent questions may be asked in one turn and are shown one at a time; ask dependent ones in separate turns. Never ask for passwords, API keys or other secrets. An answer is not approval to run a command and never widens tool permissions."
|
|
19957
|
-
]
|
|
19958
|
-
};
|
|
19959
|
-
var GENERIC_RULES = ["Be concise in your responses", "Show file paths clearly when working with files"];
|
|
19960
|
-
var WINDOWS_SHELL_RULE = "On Windows, commands run in Git Bash (not PowerShell, cmd or WSL); write paths with forward slashes, e.g. D:/code or /d/code.";
|
|
19961
|
-
var toolRules = (name, platform) => {
|
|
19962
|
-
const rules = TOOL_GUIDELINES[name] ?? [];
|
|
19963
|
-
return platform === "win32" && (name === "bash" || name === "job") ? [...rules, WINDOWS_SHELL_RULE] : rules;
|
|
19964
|
-
};
|
|
19965
|
-
var buildRules = (toolNames, platform) => {
|
|
19966
|
-
const rules = [];
|
|
19967
|
-
const seen = new Set;
|
|
19968
|
-
for (const rule of [...toolNames.flatMap((name) => toolRules(name, platform)), ...GENERIC_RULES]) {
|
|
19969
|
-
if (seen.has(rule))
|
|
19970
|
-
continue;
|
|
19971
|
-
seen.add(rule);
|
|
19972
|
-
rules.push(`- ${rule}`);
|
|
19973
|
-
}
|
|
19974
|
-
return rules.join(`
|
|
19975
|
-
`);
|
|
19976
|
-
};
|
|
19977
|
-
var renderProjectContext = (files) => files.length === 0 ? undefined : [
|
|
19978
|
-
"Project-specific instructions and guidelines:",
|
|
19979
|
-
...files.map(({ path, content }) => `<project_instructions path="${escapeXml(path)}">
|
|
19980
|
-
${content}
|
|
19981
|
-
</project_instructions>`)
|
|
19982
|
-
].join(`
|
|
19983
|
-
|
|
19984
|
-
`);
|
|
19985
|
-
var createNekoPrompt = (loadResources, platform = process.platform) => {
|
|
19986
|
-
const built = new WeakMap;
|
|
19987
|
-
const build = async (input, context) => {
|
|
19988
|
-
const cwd = input.agent.cwd;
|
|
19989
|
-
const role = (await input.read.snapshot(SubagentDoc, input.conversationId, context))?.role;
|
|
19990
|
-
const resources = cwd ? await loadResources(input.conversationId, cwd) : { contextFiles: [], skills: [] };
|
|
19991
|
-
const sections = {
|
|
19992
|
-
preamble: role ? ROLE_PROMPTS[role] + SUBAGENT_DELIVERY : PREAMBLE,
|
|
19993
|
-
rules: buildRules(input.agent.tools.map((tool) => tool.name), platform),
|
|
19994
|
-
project_context: renderProjectContext(resources.contextFiles),
|
|
19995
|
-
skills: formatSkillsForPrompt(resources.skills) || undefined,
|
|
19996
|
-
cwd: cwd?.replace(/\\/g, "/")
|
|
19997
|
-
};
|
|
19998
|
-
return sections;
|
|
19999
|
-
};
|
|
20000
|
-
return defineExtension5({
|
|
20001
|
-
name: "neko-prompt",
|
|
20002
|
-
sections: KEYS.map((key) => section(key, async (input, context) => {
|
|
20003
|
-
let pending = built.get(input);
|
|
20004
|
-
if (!pending) {
|
|
20005
|
-
pending = build(input, context);
|
|
20006
|
-
built.set(input, pending);
|
|
20007
|
-
}
|
|
20008
|
-
const text = (await pending)[key];
|
|
20009
|
-
if (text === undefined)
|
|
20054
|
+
class UserQuestions {
|
|
20055
|
+
harness;
|
|
20056
|
+
#tracked = new Set;
|
|
20057
|
+
#context;
|
|
20058
|
+
#running = false;
|
|
20059
|
+
#dirty = false;
|
|
20060
|
+
constructor(harness, context) {
|
|
20061
|
+
this.harness = harness;
|
|
20062
|
+
this.#context = context;
|
|
20063
|
+
const stop = harness.subscribeCommits(({ changes }) => {
|
|
20064
|
+
if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
|
|
20010
20065
|
return;
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
}, { tag: false }))
|
|
20015
|
-
});
|
|
20016
|
-
};
|
|
20017
|
-
|
|
20018
|
-
// packages/core/src/prompt/resources.ts
|
|
20019
|
-
import { existsSync as existsSync2 } from "fs";
|
|
20020
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
20021
|
-
import { homedir as homedir3 } from "os";
|
|
20022
|
-
import { join as join4 } from "path";
|
|
20023
|
-
import { fileURLToPath } from "url";
|
|
20024
|
-
var userAgentsFile = (home) => join4(home, ".agents", "AGENTS.md");
|
|
20025
|
-
var userSkillsDir = (home) => join4(home, ".agents", "skills");
|
|
20026
|
-
var workspaceAgentsFile = (workspace) => join4(workspace, "AGENTS.md");
|
|
20027
|
-
var workspaceSkillsDir = (workspace) => join4(workspace, ".agents", "skills");
|
|
20028
|
-
var resolveBuiltinSkillsDir = () => {
|
|
20029
|
-
for (const url of [new URL("./skills/", import.meta.url), new URL("../../skills/", import.meta.url)]) {
|
|
20030
|
-
const path = fileURLToPath(url);
|
|
20031
|
-
if (existsSync2(join4(path, "skill-creator", "SKILL.md")))
|
|
20032
|
-
return path;
|
|
20066
|
+
this.reconcileAll();
|
|
20067
|
+
});
|
|
20068
|
+
harness.closed.finally(stop).catch(() => {});
|
|
20033
20069
|
}
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20039
|
-
|
|
20070
|
+
track(...ids) {
|
|
20071
|
+
for (const id of ids)
|
|
20072
|
+
this.#tracked.add(id);
|
|
20073
|
+
}
|
|
20074
|
+
async reconcileAll() {
|
|
20075
|
+
if (this.#running) {
|
|
20076
|
+
this.#dirty = true;
|
|
20077
|
+
return;
|
|
20078
|
+
}
|
|
20079
|
+
this.#running = true;
|
|
20040
20080
|
try {
|
|
20041
|
-
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
} catch (error) {
|
|
20049
|
-
if (!isMissing(error))
|
|
20050
|
-
report({ path, message: "Cannot read project instructions; skipped." });
|
|
20081
|
+
do {
|
|
20082
|
+
this.#dirty = false;
|
|
20083
|
+
for (const id of [...this.#tracked])
|
|
20084
|
+
await this.reconcile(id, this.#context).catch(() => {});
|
|
20085
|
+
} while (this.#dirty);
|
|
20086
|
+
} finally {
|
|
20087
|
+
this.#running = false;
|
|
20051
20088
|
}
|
|
20052
20089
|
}
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
if (
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
const scan = async (dir, level) => (await scanSkillsDir(dir, report, capture)).map((skill) => ({ ...skill, level }));
|
|
20059
|
-
const [builtin, user, project] = await Promise.all([
|
|
20060
|
-
builtinDir === null ? Promise.resolve([]) : scan(builtinDir, "builtin"),
|
|
20061
|
-
scan(userSkillsDir(home), "user"),
|
|
20062
|
-
scan(workspaceSkillsDir(workspace), "project")
|
|
20063
|
-
]);
|
|
20064
|
-
const skills = mergeSkills([builtin, user, project]);
|
|
20065
|
-
const winner = invocationName === undefined ? undefined : skills.find((skill) => skill.name === invocationName);
|
|
20066
|
-
if (winner && candidates) {
|
|
20067
|
-
const content = candidates.get(winner.filePath);
|
|
20068
|
-
if (content !== undefined)
|
|
20069
|
-
return { contextFiles, skills, invocation: { filePath: winner.filePath, content } };
|
|
20090
|
+
async reconcile(conversationId, context) {
|
|
20091
|
+
const index = await this.harness.snapshot(QuestionsDoc, conversationId, context);
|
|
20092
|
+
if (!index?.pending.length)
|
|
20093
|
+
return;
|
|
20094
|
+
await this.harness.commit((tx) => this.#reconcile(tx, conversationId), context);
|
|
20070
20095
|
}
|
|
20071
|
-
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
const key = JSON.stringify(diagnostic);
|
|
20083
|
-
if (!known?.has(key) && !current.has(key))
|
|
20084
|
-
report(diagnostic);
|
|
20085
|
-
current.add(key);
|
|
20086
|
-
}
|
|
20087
|
-
if (current.size)
|
|
20088
|
-
previous.set(workspace, current);
|
|
20089
|
-
else
|
|
20090
|
-
previous.delete(workspace);
|
|
20091
|
-
return resources;
|
|
20092
|
-
};
|
|
20093
|
-
};
|
|
20094
|
-
var scanSkillsByLevel = async (workspace, home = homedir3(), builtinDir = BUILTIN_SKILLS_DIR, report = warnPromptDiagnostic) => {
|
|
20095
|
-
const scan = async (dir, level) => (await scanSkillsDir(dir, report)).map((skill) => ({ ...skill, level }));
|
|
20096
|
-
const [builtin, user, project] = await Promise.all([
|
|
20097
|
-
builtinDir === null ? [] : scan(builtinDir, "builtin"),
|
|
20098
|
-
scan(userSkillsDir(home), "user"),
|
|
20099
|
-
scan(workspaceSkillsDir(workspace), "project")
|
|
20100
|
-
]);
|
|
20101
|
-
return [...project, ...user, ...builtin];
|
|
20102
|
-
};
|
|
20103
|
-
|
|
20104
|
-
// packages/core/src/prompt/snapshots.ts
|
|
20105
|
-
var createResourceSnapshots = (load, submission) => {
|
|
20106
|
-
const conversations = new Map;
|
|
20107
|
-
const stateFor = (id, workspace) => {
|
|
20108
|
-
let state = conversations.get(id);
|
|
20109
|
-
if (!state || state.workspace !== workspace) {
|
|
20110
|
-
state = { workspace, pending: new Map };
|
|
20111
|
-
conversations.set(id, state);
|
|
20112
|
-
}
|
|
20113
|
-
return state;
|
|
20114
|
-
};
|
|
20115
|
-
return {
|
|
20116
|
-
stage(id, requestId, workspace, resources) {
|
|
20117
|
-
stateFor(id, workspace).pending.set(requestId, {
|
|
20118
|
-
contextFiles: resources.contextFiles,
|
|
20119
|
-
skills: resources.skills
|
|
20120
|
-
});
|
|
20121
|
-
},
|
|
20122
|
-
async current(id, workspace) {
|
|
20123
|
-
const state = stateFor(id, workspace);
|
|
20124
|
-
for (const [requestId, snapshot] of state.pending) {
|
|
20125
|
-
const record = await submission(id, requestId);
|
|
20126
|
-
if (!record || record.status === "queued")
|
|
20127
|
-
continue;
|
|
20128
|
-
state.pending.delete(requestId);
|
|
20129
|
-
if (record.type !== "input" || record.entry === undefined)
|
|
20130
|
-
continue;
|
|
20131
|
-
if (state.entry === undefined || record.entry > state.entry) {
|
|
20132
|
-
state.active = snapshot;
|
|
20133
|
-
state.entry = record.entry;
|
|
20134
|
-
}
|
|
20135
|
-
}
|
|
20136
|
-
state.active ??= load(workspace);
|
|
20137
|
-
return state.active;
|
|
20138
|
-
},
|
|
20139
|
-
discard(id, requestId) {
|
|
20140
|
-
conversations.get(id)?.pending.delete(requestId);
|
|
20141
|
-
},
|
|
20142
|
-
move(id, from, to) {
|
|
20143
|
-
const pending = conversations.get(id)?.pending;
|
|
20144
|
-
const snapshot = pending?.get(from);
|
|
20145
|
-
if (!pending || !snapshot)
|
|
20146
|
-
return;
|
|
20147
|
-
pending.delete(from);
|
|
20148
|
-
pending.set(to, snapshot);
|
|
20149
|
-
},
|
|
20150
|
-
forget(id) {
|
|
20151
|
-
conversations.delete(id);
|
|
20152
|
-
}
|
|
20153
|
-
};
|
|
20154
|
-
};
|
|
20155
|
-
|
|
20156
|
-
// packages/core/src/session-metadata.ts
|
|
20157
|
-
import { stripVTControlCharacters } from "util";
|
|
20158
|
-
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
20159
|
-
import {
|
|
20160
|
-
defineDoc as defineDoc4,
|
|
20161
|
-
defineExtension as defineExtension7,
|
|
20162
|
-
defineTask as defineTask3,
|
|
20163
|
-
InboxDoc,
|
|
20164
|
-
LiveDoc as LiveDoc2
|
|
20165
|
-
} from "@earendil-works/pi-durable";
|
|
20166
|
-
|
|
20167
|
-
// packages/core/src/user-questions.ts
|
|
20168
|
-
import { randomUUID } from "crypto";
|
|
20169
|
-
import {
|
|
20170
|
-
defineDoc as defineDoc3,
|
|
20171
|
-
defineDocFamily,
|
|
20172
|
-
defineExtension as defineExtension6,
|
|
20173
|
-
defineTool as defineTool4
|
|
20174
|
-
} from "@earendil-works/pi-durable";
|
|
20175
|
-
import { Type as Type4 } from "typebox";
|
|
20176
|
-
var QuestionDoc = defineDocFamily({
|
|
20177
|
-
kind: "app.neko.question",
|
|
20178
|
-
version: 1,
|
|
20179
|
-
scope: "conversation",
|
|
20180
|
-
history: "latest",
|
|
20181
|
-
fork: "initial",
|
|
20182
|
-
family: true,
|
|
20183
|
-
initial: (seed) => seed
|
|
20184
|
-
});
|
|
20185
|
-
var QuestionsDoc = defineDoc3({
|
|
20186
|
-
kind: "app.neko.questions",
|
|
20187
|
-
version: 1,
|
|
20188
|
-
scope: "conversation",
|
|
20189
|
-
history: "latest",
|
|
20190
|
-
fork: "initial",
|
|
20191
|
-
initial: () => ({ revision: 0, pending: [] })
|
|
20192
|
-
});
|
|
20193
|
-
var clone = (value) => JSON.parse(JSON.stringify(value));
|
|
20194
|
-
var MISSING = {
|
|
20195
|
-
id: 0,
|
|
20196
|
-
callId: "",
|
|
20197
|
-
version: "",
|
|
20198
|
-
question: "",
|
|
20199
|
-
multiple: false,
|
|
20200
|
-
options: [],
|
|
20201
|
-
status: "pending",
|
|
20202
|
-
answer: null,
|
|
20203
|
-
createdAt: 0,
|
|
20204
|
-
settledAt: null
|
|
20205
|
-
};
|
|
20206
|
-
var argsOf = (record) => ({
|
|
20207
|
-
question: record.question,
|
|
20208
|
-
multiple: record.multiple,
|
|
20209
|
-
options: clone(record.options)
|
|
20210
|
-
});
|
|
20211
|
-
async function live(tx, id) {
|
|
20212
|
-
let cursor = id;
|
|
20213
|
-
for (let depth = 0;cursor !== undefined && depth < 16; depth++) {
|
|
20214
|
-
const task = await tx.task(cursor);
|
|
20215
|
-
if (!task || task.abortRequested || task.state.status === "terminal" || task.state.status === "completing")
|
|
20216
|
-
return false;
|
|
20217
|
-
cursor = task.owner;
|
|
20218
|
-
}
|
|
20219
|
-
return true;
|
|
20220
|
-
}
|
|
20221
|
-
function settle(record, index, status) {
|
|
20222
|
-
record.status = status;
|
|
20223
|
-
record.settledAt = Date.now();
|
|
20224
|
-
index.pending = index.pending.filter((id) => id !== record.id);
|
|
20225
|
-
index.revision += 1;
|
|
20226
|
-
}
|
|
20227
|
-
|
|
20228
|
-
class UserQuestions {
|
|
20229
|
-
harness;
|
|
20230
|
-
#tracked = new Set;
|
|
20231
|
-
#context;
|
|
20232
|
-
#running = false;
|
|
20233
|
-
#dirty = false;
|
|
20234
|
-
constructor(harness, context) {
|
|
20235
|
-
this.harness = harness;
|
|
20236
|
-
this.#context = context;
|
|
20237
|
-
const stop = harness.subscribeCommits(({ changes }) => {
|
|
20238
|
-
if (!changes.some((c) => c.type === "task" && (c.value.abortRequested || c.value.state.status === "terminal")))
|
|
20239
|
-
return;
|
|
20240
|
-
this.reconcileAll();
|
|
20241
|
-
});
|
|
20242
|
-
harness.closed.finally(stop).catch(() => {});
|
|
20243
|
-
}
|
|
20244
|
-
track(...ids) {
|
|
20245
|
-
for (const id of ids)
|
|
20246
|
-
this.#tracked.add(id);
|
|
20247
|
-
}
|
|
20248
|
-
async reconcileAll() {
|
|
20249
|
-
if (this.#running) {
|
|
20250
|
-
this.#dirty = true;
|
|
20251
|
-
return;
|
|
20252
|
-
}
|
|
20253
|
-
this.#running = true;
|
|
20254
|
-
try {
|
|
20255
|
-
do {
|
|
20256
|
-
this.#dirty = false;
|
|
20257
|
-
for (const id of [...this.#tracked])
|
|
20258
|
-
await this.reconcile(id, this.#context).catch(() => {});
|
|
20259
|
-
} while (this.#dirty);
|
|
20260
|
-
} finally {
|
|
20261
|
-
this.#running = false;
|
|
20262
|
-
}
|
|
20263
|
-
}
|
|
20264
|
-
async reconcile(conversationId, context) {
|
|
20265
|
-
const index = await this.harness.snapshot(QuestionsDoc, conversationId, context);
|
|
20266
|
-
if (!index?.pending.length)
|
|
20267
|
-
return;
|
|
20268
|
-
await this.harness.commit((tx) => this.#reconcile(tx, conversationId), context);
|
|
20269
|
-
}
|
|
20270
|
-
async#reconcile(tx, conversationId) {
|
|
20271
|
-
const index = await tx.doc(QuestionsDoc, conversationId);
|
|
20272
|
-
const stale = [];
|
|
20273
|
-
for (const id of index.pending) {
|
|
20274
|
-
const record = await tx.doc(QuestionDoc, conversationId, String(id), MISSING);
|
|
20275
|
-
if (record.version === "" || record.status !== "pending")
|
|
20276
|
-
stale.push(id);
|
|
20277
|
-
else if (!await live(tx, id)) {
|
|
20278
|
-
record.status = "cancelled";
|
|
20279
|
-
record.settledAt = Date.now();
|
|
20280
|
-
stale.push(id);
|
|
20096
|
+
async#reconcile(tx, conversationId) {
|
|
20097
|
+
const index = await tx.doc(QuestionsDoc, conversationId);
|
|
20098
|
+
const stale = [];
|
|
20099
|
+
for (const id of index.pending) {
|
|
20100
|
+
const record = await tx.doc(QuestionDoc, conversationId, String(id), MISSING);
|
|
20101
|
+
if (record.version === "" || record.status !== "pending")
|
|
20102
|
+
stale.push(id);
|
|
20103
|
+
else if (!await live(tx, id)) {
|
|
20104
|
+
record.status = "cancelled";
|
|
20105
|
+
record.settledAt = Date.now();
|
|
20106
|
+
stale.push(id);
|
|
20281
20107
|
}
|
|
20282
20108
|
}
|
|
20283
20109
|
if (!stale.length)
|
|
@@ -20439,71 +20265,717 @@ function questionOutput(record) {
|
|
|
20439
20265
|
return lines.join(`
|
|
20440
20266
|
`);
|
|
20441
20267
|
}
|
|
20442
|
-
if (record.status === "skipped")
|
|
20443
|
-
return "The user skipped this question without answering. This is not approval, rejection or a preference. Continue with your best judgment and state the assumptions you make, or continue without this information. Do not ask the same question again unless circumstances change.";
|
|
20444
|
-
return "The question was cancelled because the run was stopped. No answer was given.";
|
|
20445
|
-
}
|
|
20446
|
-
function outcomeOf(record) {
|
|
20447
|
-
if (record.status === "answered" && record.answer)
|
|
20448
|
-
return {
|
|
20449
|
-
status: "answered",
|
|
20450
|
-
optionIds: record.answer.optionIds,
|
|
20451
|
-
labels: answerLabels(record, record.answer),
|
|
20452
|
-
text: record.answer.text
|
|
20453
|
-
};
|
|
20454
|
-
return { status: record.status === "skipped" ? "skipped" : "cancelled" };
|
|
20455
|
-
}
|
|
20456
|
-
function questionDetails(record) {
|
|
20457
|
-
const base = {
|
|
20458
|
-
id: record.id,
|
|
20459
|
-
question: record.question,
|
|
20460
|
-
multiple: record.multiple,
|
|
20461
|
-
options: record.options.map((o) => ({
|
|
20462
|
-
id: o.id,
|
|
20463
|
-
label: o.label,
|
|
20464
|
-
...o.recommended ? { recommended: true } : {}
|
|
20465
|
-
}))
|
|
20268
|
+
if (record.status === "skipped")
|
|
20269
|
+
return "The user skipped this question without answering. This is not approval, rejection or a preference. Continue with your best judgment and state the assumptions you make, or continue without this information. Do not ask the same question again unless circumstances change.";
|
|
20270
|
+
return "The question was cancelled because the run was stopped. No answer was given.";
|
|
20271
|
+
}
|
|
20272
|
+
function outcomeOf(record) {
|
|
20273
|
+
if (record.status === "answered" && record.answer)
|
|
20274
|
+
return {
|
|
20275
|
+
status: "answered",
|
|
20276
|
+
optionIds: record.answer.optionIds,
|
|
20277
|
+
labels: answerLabels(record, record.answer),
|
|
20278
|
+
text: record.answer.text
|
|
20279
|
+
};
|
|
20280
|
+
return { status: record.status === "skipped" ? "skipped" : "cancelled" };
|
|
20281
|
+
}
|
|
20282
|
+
function questionDetails(record) {
|
|
20283
|
+
const base = {
|
|
20284
|
+
id: record.id,
|
|
20285
|
+
question: record.question,
|
|
20286
|
+
multiple: record.multiple,
|
|
20287
|
+
options: record.options.map((o) => ({
|
|
20288
|
+
id: o.id,
|
|
20289
|
+
label: o.label,
|
|
20290
|
+
...o.recommended ? { recommended: true } : {}
|
|
20291
|
+
}))
|
|
20292
|
+
};
|
|
20293
|
+
return record.status === "pending" ? { ...base, status: "pending" } : { ...base, ...outcomeOf(record) };
|
|
20294
|
+
}
|
|
20295
|
+
function createAskUserTool(service) {
|
|
20296
|
+
return defineTool4({
|
|
20297
|
+
name: "ask_user",
|
|
20298
|
+
description: "Ask the user one question and wait for the answer. question: the text. options: optional [{id, label, description?, recommended?}]; multiple: true lets the user pick several (needs 2+ options). The user can always type text instead of or in addition to choosing, or skip. Use only when you need a decision or information you cannot determine yourself. The call returns when the user answers or skips; do not poll.",
|
|
20299
|
+
parameters: parameters3,
|
|
20300
|
+
structuredOutputSchema: structuredOutputSchema2,
|
|
20301
|
+
replay: "safe",
|
|
20302
|
+
executionMode: "sequential",
|
|
20303
|
+
callers: ["model"],
|
|
20304
|
+
execute: async (input, api, context) => {
|
|
20305
|
+
context.abortSignal?.throwIfAborted();
|
|
20306
|
+
if ((await api.snapshot(SubagentDoc, api.conversationId, context))?.role)
|
|
20307
|
+
throw new Error("Subagents cannot ask the user; report the missing information to the main agent.");
|
|
20308
|
+
let args;
|
|
20309
|
+
try {
|
|
20310
|
+
args = validateQuestionArgs(input);
|
|
20311
|
+
} catch (error) {
|
|
20312
|
+
if (error instanceof InteractionInputError)
|
|
20313
|
+
throw new Error(`Invalid ask_user arguments: ${error.message}`);
|
|
20314
|
+
throw error;
|
|
20315
|
+
}
|
|
20316
|
+
const questions = service();
|
|
20317
|
+
const created = await questions.ensure(api.conversationId, api.taskId, api.callId, args, context);
|
|
20318
|
+
await api.details(questionDetails(created), context);
|
|
20319
|
+
const record = created.status === "pending" ? await questions.wait(api.conversationId, api.taskId, context) : created;
|
|
20320
|
+
return {
|
|
20321
|
+
output: [{ type: "text", text: questionOutput(record) }],
|
|
20322
|
+
structuredOutput: outcomeOf(record),
|
|
20323
|
+
details: questionDetails(record),
|
|
20324
|
+
isError: record.status === "cancelled"
|
|
20325
|
+
};
|
|
20326
|
+
}
|
|
20327
|
+
});
|
|
20328
|
+
}
|
|
20329
|
+
var createQuestionExtension = (service) => defineExtension5({ name: "neko-questions", tools: [createAskUserTool(service)] });
|
|
20330
|
+
|
|
20331
|
+
// packages/core/src/plan.ts
|
|
20332
|
+
var PLAN_REMOVED_TOOLS = ["edit", "write", "job"];
|
|
20333
|
+
var PLAN_INSTRUCTIONS = [
|
|
20334
|
+
"You are in plan mode: research and plan only, and change nothing.",
|
|
20335
|
+
"- Allowed: read, find_files, search_text, web_search, web_extract, todo, ask_user, read-only subagents (explore, plan) and bash limited to read-only commands (listing, searching, git log/diff/status, version checks). Other commands are rejected.",
|
|
20336
|
+
"- Not available: edit, write, job and any command that modifies files, installs packages or starts processes. Do not try to work around this.",
|
|
20337
|
+
"- When you understand the task, call submit_plan once, alone in its turn, with a short title, ordered concrete steps and an optional note (risks, assumptions, verification). The user then approves it or asks for changes.",
|
|
20338
|
+
"- After submit_plan do nothing else. If the user asks for changes, research what is needed and submit a revised plan.",
|
|
20339
|
+
"- Use ask_user only for decisions you cannot settle by reading the code."
|
|
20340
|
+
].join(`
|
|
20341
|
+
`);
|
|
20342
|
+
var PLAN_BUSY = "PLAN_BUSY: Wait for the current run to finish or stop it first.";
|
|
20343
|
+
function planAgentChange(mode) {
|
|
20344
|
+
if (mode === "normal")
|
|
20345
|
+
return { tools: null, modelTools: null, instructions: null };
|
|
20346
|
+
const named = (name) => ({ name });
|
|
20347
|
+
return {
|
|
20348
|
+
tools: { remove: PLAN_REMOVED_TOOLS.map(named) },
|
|
20349
|
+
modelTools: { add: [named("submit_plan")] },
|
|
20350
|
+
instructions: PLAN_INSTRUCTIONS
|
|
20351
|
+
};
|
|
20352
|
+
}
|
|
20353
|
+
var agentInPlanMode = (agent) => agent.instructions === PLAN_INSTRUCTIONS;
|
|
20354
|
+
var clone2 = (value) => JSON.parse(JSON.stringify(value));
|
|
20355
|
+
var toRecord = ({ taskId: _taskId, dispatched: _dispatched, ...record }) => clone2(record);
|
|
20356
|
+
function settle2(state, item, status) {
|
|
20357
|
+
item.status = status;
|
|
20358
|
+
item.settledAt = Date.now();
|
|
20359
|
+
state.revision += 1;
|
|
20360
|
+
}
|
|
20361
|
+
|
|
20362
|
+
class PlanService {
|
|
20363
|
+
harness;
|
|
20364
|
+
#context;
|
|
20365
|
+
constructor(harness, context) {
|
|
20366
|
+
this.harness = harness;
|
|
20367
|
+
this.#context = context;
|
|
20368
|
+
}
|
|
20369
|
+
async#state(id) {
|
|
20370
|
+
return this.harness.snapshot(PlanDoc, id, this.#context);
|
|
20371
|
+
}
|
|
20372
|
+
async mode(id) {
|
|
20373
|
+
return (await this.#state(id))?.mode ?? "normal";
|
|
20374
|
+
}
|
|
20375
|
+
async view(id) {
|
|
20376
|
+
const state = await this.#state(id);
|
|
20377
|
+
const current = state?.items.at(-1);
|
|
20378
|
+
return {
|
|
20379
|
+
mode: state?.mode ?? "normal",
|
|
20380
|
+
revision: state?.revision ?? 0,
|
|
20381
|
+
current: current ? toRecord(current) : null
|
|
20382
|
+
};
|
|
20383
|
+
}
|
|
20384
|
+
async get(id, version) {
|
|
20385
|
+
const item = (await this.#state(id))?.items.find((i) => i.version === version);
|
|
20386
|
+
return item ? toRecord(item) : undefined;
|
|
20387
|
+
}
|
|
20388
|
+
async awaiting(id) {
|
|
20389
|
+
const item = (await this.#state(id))?.items.find((i) => i.status === "awaiting_approval");
|
|
20390
|
+
return item ? toRecord(item) : undefined;
|
|
20391
|
+
}
|
|
20392
|
+
async setMode(id, mode) {
|
|
20393
|
+
return this.harness.commit(async (tx) => {
|
|
20394
|
+
const state = await tx.doc(PlanDoc, id);
|
|
20395
|
+
let changed = false;
|
|
20396
|
+
if (state.mode !== mode) {
|
|
20397
|
+
state.mode = mode;
|
|
20398
|
+
state.revision += 1;
|
|
20399
|
+
changed = true;
|
|
20400
|
+
}
|
|
20401
|
+
if (mode === "normal") {
|
|
20402
|
+
for (const item of state.items)
|
|
20403
|
+
if (item.status === "awaiting_approval") {
|
|
20404
|
+
settle2(state, item, "cancelled");
|
|
20405
|
+
changed = true;
|
|
20406
|
+
}
|
|
20407
|
+
}
|
|
20408
|
+
return changed;
|
|
20409
|
+
}, this.#context);
|
|
20410
|
+
}
|
|
20411
|
+
async submit(id, taskId, args, context) {
|
|
20412
|
+
return this.harness.commit(async (tx) => {
|
|
20413
|
+
if (!await live(tx, taskId))
|
|
20414
|
+
throw new Error("Task stopped");
|
|
20415
|
+
const state = await tx.doc(PlanDoc, id);
|
|
20416
|
+
const existing = state.items.find((item) => item.taskId === taskId);
|
|
20417
|
+
if (existing) {
|
|
20418
|
+
if (existing.title !== args.title || existing.note !== args.note || JSON.stringify(existing.steps) !== JSON.stringify(args.steps))
|
|
20419
|
+
throw new Error("Plan operation changed; a new call is required.");
|
|
20420
|
+
return toRecord(existing);
|
|
20421
|
+
}
|
|
20422
|
+
if (state.mode !== "plan")
|
|
20423
|
+
throw new Error("submit_plan is only available in plan mode.");
|
|
20424
|
+
for (const item of state.items)
|
|
20425
|
+
if (item.status === "awaiting_approval")
|
|
20426
|
+
settle2(state, item, "superseded");
|
|
20427
|
+
const item = {
|
|
20428
|
+
version: randomUUID2(),
|
|
20429
|
+
title: args.title,
|
|
20430
|
+
steps: [...args.steps],
|
|
20431
|
+
note: args.note,
|
|
20432
|
+
status: "awaiting_approval",
|
|
20433
|
+
feedback: null,
|
|
20434
|
+
createdAt: Date.now(),
|
|
20435
|
+
settledAt: null,
|
|
20436
|
+
taskId,
|
|
20437
|
+
dispatched: false
|
|
20438
|
+
};
|
|
20439
|
+
state.items.push(item);
|
|
20440
|
+
if (state.items.length > PLAN_HISTORY)
|
|
20441
|
+
state.items.splice(0, state.items.length - PLAN_HISTORY);
|
|
20442
|
+
state.revision += 1;
|
|
20443
|
+
return toRecord(item);
|
|
20444
|
+
}, context);
|
|
20445
|
+
}
|
|
20446
|
+
async decide(id, version, decision) {
|
|
20447
|
+
return this.harness.commit(async (tx) => {
|
|
20448
|
+
const state = await tx.doc(PlanDoc, id);
|
|
20449
|
+
const item = state.items.find((i) => i.version === version);
|
|
20450
|
+
if (!item)
|
|
20451
|
+
throw new Error("Plan not found.");
|
|
20452
|
+
if (item.status === "awaiting_approval") {
|
|
20453
|
+
if (decision.kind === "approve") {
|
|
20454
|
+
settle2(state, item, "approved");
|
|
20455
|
+
state.mode = "normal";
|
|
20456
|
+
} else {
|
|
20457
|
+
settle2(state, item, "revision_requested");
|
|
20458
|
+
item.feedback = decision.feedback;
|
|
20459
|
+
}
|
|
20460
|
+
}
|
|
20461
|
+
return { record: toRecord(item), item: clone2(item) };
|
|
20462
|
+
}, this.#context);
|
|
20463
|
+
}
|
|
20464
|
+
async supersedePending(id) {
|
|
20465
|
+
if (await this.awaiting(id))
|
|
20466
|
+
await this.#cancel(id, "superseded");
|
|
20467
|
+
}
|
|
20468
|
+
async cancelPending(id) {
|
|
20469
|
+
if (await this.awaiting(id))
|
|
20470
|
+
await this.#cancel(id, "cancelled");
|
|
20471
|
+
}
|
|
20472
|
+
async#cancel(id, status) {
|
|
20473
|
+
await this.harness.commit(async (tx) => {
|
|
20474
|
+
const state = await tx.doc(PlanDoc, id);
|
|
20475
|
+
for (const item of state.items)
|
|
20476
|
+
if (item.status === "awaiting_approval")
|
|
20477
|
+
settle2(state, item, status);
|
|
20478
|
+
}, this.#context);
|
|
20479
|
+
}
|
|
20480
|
+
async undispatched(id) {
|
|
20481
|
+
const item = (await this.#state(id))?.items.findLast((i) => (i.status === "approved" || i.status === "revision_requested") && !i.dispatched);
|
|
20482
|
+
return item ? clone2(item) : undefined;
|
|
20483
|
+
}
|
|
20484
|
+
async markDispatched(id, version) {
|
|
20485
|
+
await this.harness.commit(async (tx) => {
|
|
20486
|
+
const item = (await tx.doc(PlanDoc, id)).items.find((i) => i.version === version);
|
|
20487
|
+
if (item)
|
|
20488
|
+
item.dispatched = true;
|
|
20489
|
+
}, this.#context);
|
|
20490
|
+
}
|
|
20491
|
+
}
|
|
20492
|
+
function approvedPlanPrompt(plan) {
|
|
20493
|
+
return [
|
|
20494
|
+
`The user approved your plan "${plan.title}". Plan mode is off: edit, write and all commands are available again. Execute the plan now, step by step:`,
|
|
20495
|
+
...plan.steps.map((step, index) => `${index + 1}. ${step}`),
|
|
20496
|
+
...plan.note ? ["", `Notes: ${plan.note}`] : [],
|
|
20497
|
+
"",
|
|
20498
|
+
"Use the todo tool to track progress, and verify the result when you are done."
|
|
20499
|
+
].join(`
|
|
20500
|
+
`);
|
|
20501
|
+
}
|
|
20502
|
+
function planRevisionPrompt(plan, feedback) {
|
|
20503
|
+
return [
|
|
20504
|
+
`The user asked for changes to your plan "${plan.title}". Do not start implementing; you are still in plan mode.`,
|
|
20505
|
+
"",
|
|
20506
|
+
feedback,
|
|
20507
|
+
"",
|
|
20508
|
+
"Research what you need, then call submit_plan with the revised plan."
|
|
20509
|
+
].join(`
|
|
20510
|
+
`);
|
|
20511
|
+
}
|
|
20512
|
+
var parameters4 = Type5.Object({
|
|
20513
|
+
title: Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
|
|
20514
|
+
steps: Type5.Array(Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
|
|
20515
|
+
minItems: 1,
|
|
20516
|
+
maxItems: PLAN_LIMITS.maxSteps,
|
|
20517
|
+
description: "Ordered, concrete implementation steps."
|
|
20518
|
+
}),
|
|
20519
|
+
note: Type5.Optional(Type5.String({ maxLength: PLAN_LIMITS.noteChars, description: "Risks, assumptions and how to verify." }))
|
|
20520
|
+
}, { additionalProperties: false });
|
|
20521
|
+
function submitPlanTool(service) {
|
|
20522
|
+
return defineTool5({
|
|
20523
|
+
name: "submit_plan",
|
|
20524
|
+
description: "Submit your implementation plan for the user's approval. title: short name. steps: ordered, concrete steps. note: optional risks, assumptions and verification. Call it once, as the only tool call of its turn, when your research is done; the turn ends and the user approves it or asks for changes.",
|
|
20525
|
+
parameters: parameters4,
|
|
20526
|
+
deferred: true,
|
|
20527
|
+
replay: "safe",
|
|
20528
|
+
executionMode: "sequential",
|
|
20529
|
+
callers: ["model"],
|
|
20530
|
+
execute: async (input, api, context) => {
|
|
20531
|
+
context.abortSignal?.throwIfAborted();
|
|
20532
|
+
if ((await api.snapshot(SubagentDoc, api.conversationId, context))?.role)
|
|
20533
|
+
throw new Error("Subagents cannot submit plans; report your findings to the main agent.");
|
|
20534
|
+
let args;
|
|
20535
|
+
try {
|
|
20536
|
+
args = validatePlanArgs(input);
|
|
20537
|
+
} catch (error) {
|
|
20538
|
+
if (error instanceof InteractionInputError)
|
|
20539
|
+
throw new Error(`Invalid submit_plan arguments: ${error.message}`);
|
|
20540
|
+
throw error;
|
|
20541
|
+
}
|
|
20542
|
+
let record = await service().submit(api.conversationId, api.taskId, args, context);
|
|
20543
|
+
const inbox = await api.snapshot(InboxDoc, api.conversationId, context);
|
|
20544
|
+
if (record.status === "awaiting_approval" && inbox?.items.some((item) => queuedUserInput(item))) {
|
|
20545
|
+
await service().supersedePending(api.conversationId);
|
|
20546
|
+
record = await service().get(api.conversationId, record.version) ?? record;
|
|
20547
|
+
}
|
|
20548
|
+
const details = {
|
|
20549
|
+
version: record.version,
|
|
20550
|
+
title: record.title,
|
|
20551
|
+
steps: record.steps,
|
|
20552
|
+
note: record.note
|
|
20553
|
+
};
|
|
20554
|
+
return {
|
|
20555
|
+
output: [
|
|
20556
|
+
{
|
|
20557
|
+
type: "text",
|
|
20558
|
+
text: "Plan submitted. It now waits for the user's decision. Stop here: do not call other tools or continue the work."
|
|
20559
|
+
}
|
|
20560
|
+
],
|
|
20561
|
+
details,
|
|
20562
|
+
control: { terminate: true }
|
|
20563
|
+
};
|
|
20564
|
+
}
|
|
20565
|
+
});
|
|
20566
|
+
}
|
|
20567
|
+
var unsafeBash = (command) => `Plan mode is read-only, so this command was not run (it is not on the read-only allowlist): ${command}
|
|
20568
|
+
Use read, find_files or search_text, run a read-only command, or ask the user. If the command is needed, put it in the plan.`;
|
|
20569
|
+
var blockedTool = (name) => `Plan mode is read-only: ${name} is unavailable. Research with read-only tools, then call submit_plan.`;
|
|
20570
|
+
var createPlanExtension = (service) => defineExtension6({
|
|
20571
|
+
name: "neko-plan",
|
|
20572
|
+
tools: [submitPlanTool(service)],
|
|
20573
|
+
hooks: [
|
|
20574
|
+
hook2(ToolTask2, {
|
|
20575
|
+
beforeTool: async (call, api, context) => {
|
|
20576
|
+
if ((await api.snapshot(PlanDoc, api.conversationId, context))?.mode !== "plan")
|
|
20577
|
+
return;
|
|
20578
|
+
if (PLAN_REMOVED_TOOLS.includes(call.name))
|
|
20579
|
+
return { block: blockedTool(call.name) };
|
|
20580
|
+
if (call.name === "bash") {
|
|
20581
|
+
const command = String(call.arguments.command ?? "");
|
|
20582
|
+
if (!isSafeCommand(command))
|
|
20583
|
+
return { block: unsafeBash(command) };
|
|
20584
|
+
}
|
|
20585
|
+
}
|
|
20586
|
+
})
|
|
20587
|
+
],
|
|
20588
|
+
wraps: [
|
|
20589
|
+
wrapTool(createBashTool(), (bash) => ({
|
|
20590
|
+
...bash,
|
|
20591
|
+
execute: async (args, api, context) => {
|
|
20592
|
+
if ((await api.snapshot(PlanDoc, api.conversationId, context))?.mode === "plan" && !isSafeCommand(args.command))
|
|
20593
|
+
throw new Error(unsafeBash(args.command));
|
|
20594
|
+
return bash.execute(args, api, context);
|
|
20595
|
+
}
|
|
20596
|
+
}))
|
|
20597
|
+
]
|
|
20598
|
+
});
|
|
20599
|
+
|
|
20600
|
+
// packages/core/src/prompt/invoke.ts
|
|
20601
|
+
import { dirname as dirname4 } from "path";
|
|
20602
|
+
|
|
20603
|
+
// packages/core/src/prompt/diagnostics.ts
|
|
20604
|
+
var ignorePromptDiagnostic = () => {};
|
|
20605
|
+
var warnPromptDiagnostic = ({ path, message }) => {
|
|
20606
|
+
console.warn(`[neko-prompt] ${JSON.stringify(path)}: ${message}`);
|
|
20607
|
+
};
|
|
20608
|
+
var isMissing = (error) => typeof error === "object" && error !== null && ("code" in error) && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
20609
|
+
|
|
20610
|
+
// packages/core/src/prompt/skills.ts
|
|
20611
|
+
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
20612
|
+
import { basename, dirname as dirname3, join as join3 } from "path";
|
|
20613
|
+
var MAX_NAME_LENGTH = 64;
|
|
20614
|
+
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
20615
|
+
var parseSkillFile = (content, filePath, report = ignorePromptDiagnostic) => {
|
|
20616
|
+
const skip = (message) => {
|
|
20617
|
+
report({ path: filePath, message });
|
|
20618
|
+
return null;
|
|
20619
|
+
};
|
|
20620
|
+
try {
|
|
20621
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
20622
|
+
const description = frontmatter.description;
|
|
20623
|
+
if (typeof description !== "string" || description.trim() === "")
|
|
20624
|
+
return skip("Skill description is missing or empty; skipped.");
|
|
20625
|
+
if (description.length > MAX_DESCRIPTION_LENGTH)
|
|
20626
|
+
return skip("Skill description is too long; skipped.");
|
|
20627
|
+
const name = typeof frontmatter.name === "string" && frontmatter.name.trim() !== "" ? frontmatter.name : basename(dirname3(filePath));
|
|
20628
|
+
if (name.length > MAX_NAME_LENGTH || /[\s\p{Cc}]/u.test(name))
|
|
20629
|
+
return skip("Skill name is too long or contains whitespace/control characters; skipped.");
|
|
20630
|
+
return {
|
|
20631
|
+
name,
|
|
20632
|
+
description,
|
|
20633
|
+
filePath,
|
|
20634
|
+
disableModelInvocation: frontmatter["disable-model-invocation"] === true
|
|
20635
|
+
};
|
|
20636
|
+
} catch {
|
|
20637
|
+
return skip("Invalid skill frontmatter; skipped.");
|
|
20638
|
+
}
|
|
20639
|
+
};
|
|
20640
|
+
var scanSkillsDir = async (dir, report = ignorePromptDiagnostic, capture) => {
|
|
20641
|
+
let entries;
|
|
20642
|
+
try {
|
|
20643
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
20644
|
+
} catch (error) {
|
|
20645
|
+
if (!isMissing(error))
|
|
20646
|
+
report({ path: dir, message: "Cannot scan skills directory; skipped." });
|
|
20647
|
+
return [];
|
|
20648
|
+
}
|
|
20649
|
+
const skills = [];
|
|
20650
|
+
for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
20651
|
+
if (!entry.isDirectory())
|
|
20652
|
+
continue;
|
|
20653
|
+
const filePath = join3(dir, entry.name, "SKILL.md");
|
|
20654
|
+
let content;
|
|
20655
|
+
try {
|
|
20656
|
+
content = await readFile2(filePath, "utf8");
|
|
20657
|
+
} catch (error) {
|
|
20658
|
+
if (!isMissing(error))
|
|
20659
|
+
report({ path: filePath, message: "Cannot read skill; skipped." });
|
|
20660
|
+
continue;
|
|
20661
|
+
}
|
|
20662
|
+
const skill = parseSkillFile(content, filePath, report);
|
|
20663
|
+
if (skill) {
|
|
20664
|
+
skills.push(skill);
|
|
20665
|
+
capture?.(skill, content);
|
|
20666
|
+
}
|
|
20667
|
+
}
|
|
20668
|
+
return skills;
|
|
20669
|
+
};
|
|
20670
|
+
var mergeSkills = (groups) => {
|
|
20671
|
+
const byName = new Map;
|
|
20672
|
+
for (const group of groups)
|
|
20673
|
+
for (const skill of group)
|
|
20674
|
+
byName.set(skill.name, skill);
|
|
20675
|
+
return [...byName.values()];
|
|
20676
|
+
};
|
|
20677
|
+
var escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
20678
|
+
var formatSkillsForPrompt = (skills) => {
|
|
20679
|
+
const visible = skills.filter((skill) => !skill.disableModelInvocation);
|
|
20680
|
+
if (visible.length === 0)
|
|
20681
|
+
return "";
|
|
20682
|
+
const lines = [
|
|
20683
|
+
"The following skills provide specialized instructions for specific tasks.",
|
|
20684
|
+
"Use the read tool to load a skill's file when the task matches its description.",
|
|
20685
|
+
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md) and use that absolute path in tool commands.",
|
|
20686
|
+
"",
|
|
20687
|
+
"<available_skills>"
|
|
20688
|
+
];
|
|
20689
|
+
for (const skill of visible) {
|
|
20690
|
+
lines.push(" <skill>");
|
|
20691
|
+
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
20692
|
+
lines.push(` <description>${escapeXml(skill.description)}</description>`);
|
|
20693
|
+
lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
|
|
20694
|
+
lines.push(" </skill>");
|
|
20695
|
+
}
|
|
20696
|
+
lines.push("</available_skills>");
|
|
20697
|
+
return lines.join(`
|
|
20698
|
+
`);
|
|
20699
|
+
};
|
|
20700
|
+
|
|
20701
|
+
// packages/core/src/prompt/invoke.ts
|
|
20702
|
+
var expandSkillCommand = async (text, skills, readFile, report = warnPromptDiagnostic, metadata) => {
|
|
20703
|
+
if (!text.startsWith("/skill:"))
|
|
20704
|
+
return text;
|
|
20705
|
+
const spaceIndex = text.indexOf(" ");
|
|
20706
|
+
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
|
20707
|
+
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
20708
|
+
const skill = skills.find((s) => s.name === skillName);
|
|
20709
|
+
if (!skill)
|
|
20710
|
+
return text;
|
|
20711
|
+
let body;
|
|
20712
|
+
try {
|
|
20713
|
+
body = stripFrontmatter(await readFile(skill.filePath)).trim();
|
|
20714
|
+
} catch {
|
|
20715
|
+
report({ path: skill.filePath, message: "Cannot expand known skill; original input preserved." });
|
|
20716
|
+
return text;
|
|
20717
|
+
}
|
|
20718
|
+
metadata?.({
|
|
20719
|
+
name: skill.name,
|
|
20720
|
+
location: skill.filePath,
|
|
20721
|
+
body: `References are relative to ${dirname4(skill.filePath)}.
|
|
20722
|
+
|
|
20723
|
+
${body}`,
|
|
20724
|
+
args
|
|
20725
|
+
});
|
|
20726
|
+
const block = `<skill name="${escapeXml(skill.name)}" location="${escapeXml(skill.filePath)}">
|
|
20727
|
+
References are relative to ${dirname4(skill.filePath)}.
|
|
20728
|
+
|
|
20729
|
+
${body}
|
|
20730
|
+
</skill>`;
|
|
20731
|
+
return args ? `${block}
|
|
20732
|
+
|
|
20733
|
+
${args}` : block;
|
|
20734
|
+
};
|
|
20735
|
+
|
|
20736
|
+
// packages/core/src/prompt/prompt.ts
|
|
20737
|
+
import { defineExtension as defineExtension7, section } from "@earendil-works/pi-durable";
|
|
20738
|
+
var KEYS = ["preamble", "rules", "project_context", "skills", "cwd"];
|
|
20739
|
+
var PREAMBLE = "You are an expert coding assistant operating inside neko, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.";
|
|
20740
|
+
var TOOL_GUIDELINES = {
|
|
20741
|
+
subagent: [
|
|
20742
|
+
"Delegate only independent, sufficiently scoped work when isolation or parallelism helps. Handle simple tasks directly. Pass necessary context and user constraints; research-only requests must not become coding tasks. For related follow-up work, list and continue a suitable idle subagent using its stable agentId; busy means no task was accepted. Do not change its role. Verify and integrate reports. Do not modify files concurrently with a coder assigned those files."
|
|
20743
|
+
],
|
|
20744
|
+
find_files: [
|
|
20745
|
+
"Use find_files to discover files; query is a literal substring. Narrow the directory when results are partial."
|
|
20746
|
+
],
|
|
20747
|
+
search_text: [
|
|
20748
|
+
"Use search_text for literal text search and read for surrounding evidence. Partial results do not prove absence."
|
|
20749
|
+
],
|
|
20750
|
+
read: ["Use read to examine files instead of cat or sed."],
|
|
20751
|
+
write: ["Use write only for new files or complete rewrites."],
|
|
20752
|
+
edit: [
|
|
20753
|
+
"Use edit for precise changes (edits[].oldText must match exactly)",
|
|
20754
|
+
"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
|
|
20755
|
+
"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
|
|
20756
|
+
"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions."
|
|
20757
|
+
],
|
|
20758
|
+
bash: ["Use bash for file operations like ls, rg, find"],
|
|
20759
|
+
job: [
|
|
20760
|
+
'Use job {action:"start", command, title?, timeout?} for long-running commands (dev servers, watchers, long builds or tests) instead of bash, which would block. Jobs have no stdin. start returns within 2 seconds, with the early result if the command already ended; a job that ends later reports back to you automatically, so do not poll. Read new output with {action:"output", jobId, since?} using the offset from the previous call, list jobs with {action:"list"}, end one with {action:"stop", jobId}. Run short commands with bash.'
|
|
20761
|
+
],
|
|
20762
|
+
web_search: ["Use web_search for current information from the internet instead of guessing."],
|
|
20763
|
+
web_extract: ["Use web_extract to read the full content of a web page when you have its URL."],
|
|
20764
|
+
todo: [
|
|
20765
|
+
`Use todo for multi-step work: {action:"list"} reads the saved list, {action:"replace", items:[{id, text, status}]} overwrites the whole ordered list (status pending, in_progress or completed; [] clears it). Keep a short list and update it as you go, keep an item's id when you update the same task, and mark it completed only after the work is actually done. If you are unsure what the list holds (for example after the context was compacted), read it with list first instead of guessing or creating a second list. Do not create a list for simple questions or one-step tasks.`
|
|
20766
|
+
],
|
|
20767
|
+
ask_user: [
|
|
20768
|
+
"Use ask_user {question, options?: [{id, label, description?, recommended?}], multiple?} only when you need a decision or information you cannot determine yourself from the code, the environment or a reasonable assumption; do not ask about things you can find out. Keep the question short and self-contained. Make options short, distinct and understandable on their own, mark a clear preference with recommended, and set multiple: true only when several options may apply (it needs at least 2 options). The user can always type text instead of or in addition to choosing, or skip. The call returns when the user answers or skips; do not poll. A skip is not consent or a preference: continue on your best judgment, state the assumptions you make, and do not ask the same question again unless circumstances change. Independent questions may be asked in one turn and are shown one at a time; ask dependent ones in separate turns. Never ask for passwords, API keys or other secrets. An answer is not approval to run a command and never widens tool permissions."
|
|
20769
|
+
]
|
|
20770
|
+
};
|
|
20771
|
+
var GENERIC_RULES = ["Be concise in your responses", "Show file paths clearly when working with files"];
|
|
20772
|
+
var WINDOWS_SHELL_RULE = "On Windows, commands run in Git Bash (not PowerShell, cmd or WSL); write paths with forward slashes, e.g. D:/code or /d/code.";
|
|
20773
|
+
var toolRules = (name, platform) => {
|
|
20774
|
+
const rules = TOOL_GUIDELINES[name] ?? [];
|
|
20775
|
+
return platform === "win32" && (name === "bash" || name === "job") ? [...rules, WINDOWS_SHELL_RULE] : rules;
|
|
20776
|
+
};
|
|
20777
|
+
var buildRules = (toolNames, platform) => {
|
|
20778
|
+
const rules = [];
|
|
20779
|
+
const seen = new Set;
|
|
20780
|
+
for (const rule of [...toolNames.flatMap((name) => toolRules(name, platform)), ...GENERIC_RULES]) {
|
|
20781
|
+
if (seen.has(rule))
|
|
20782
|
+
continue;
|
|
20783
|
+
seen.add(rule);
|
|
20784
|
+
rules.push(`- ${rule}`);
|
|
20785
|
+
}
|
|
20786
|
+
return rules.join(`
|
|
20787
|
+
`);
|
|
20788
|
+
};
|
|
20789
|
+
var renderProjectContext = (files) => files.length === 0 ? undefined : [
|
|
20790
|
+
"Project-specific instructions and guidelines:",
|
|
20791
|
+
...files.map(({ path, content }) => `<project_instructions path="${escapeXml(path)}">
|
|
20792
|
+
${content}
|
|
20793
|
+
</project_instructions>`)
|
|
20794
|
+
].join(`
|
|
20795
|
+
|
|
20796
|
+
`);
|
|
20797
|
+
var createNekoPrompt = (loadResources, platform = process.platform) => {
|
|
20798
|
+
const built = new WeakMap;
|
|
20799
|
+
const build = async (input, context) => {
|
|
20800
|
+
const cwd = input.agent.cwd;
|
|
20801
|
+
const role = (await input.read.snapshot(SubagentDoc, input.conversationId, context))?.role;
|
|
20802
|
+
const resources = cwd ? await loadResources(input.conversationId, cwd) : { contextFiles: [], skills: [] };
|
|
20803
|
+
const sections = {
|
|
20804
|
+
preamble: role ? ROLE_PROMPTS[role] + SUBAGENT_DELIVERY : PREAMBLE,
|
|
20805
|
+
rules: buildRules(input.agent.tools.map((tool) => tool.name), platform),
|
|
20806
|
+
project_context: renderProjectContext(resources.contextFiles),
|
|
20807
|
+
skills: formatSkillsForPrompt(resources.skills) || undefined,
|
|
20808
|
+
cwd: cwd?.replace(/\\/g, "/")
|
|
20809
|
+
};
|
|
20810
|
+
return sections;
|
|
20811
|
+
};
|
|
20812
|
+
return defineExtension7({
|
|
20813
|
+
name: "neko-prompt",
|
|
20814
|
+
sections: KEYS.map((key) => section(key, async (input, context) => {
|
|
20815
|
+
let pending = built.get(input);
|
|
20816
|
+
if (!pending) {
|
|
20817
|
+
pending = build(input, context);
|
|
20818
|
+
built.set(input, pending);
|
|
20819
|
+
}
|
|
20820
|
+
const text = (await pending)[key];
|
|
20821
|
+
if (text === undefined)
|
|
20822
|
+
return;
|
|
20823
|
+
return key === "preamble" ? text : `<${key}>
|
|
20824
|
+
${text}
|
|
20825
|
+
</${key}>`;
|
|
20826
|
+
}, { tag: false }))
|
|
20827
|
+
});
|
|
20828
|
+
};
|
|
20829
|
+
|
|
20830
|
+
// packages/core/src/prompt/resources.ts
|
|
20831
|
+
import { existsSync as existsSync2 } from "fs";
|
|
20832
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
20833
|
+
import { homedir as homedir3 } from "os";
|
|
20834
|
+
import { join as join4 } from "path";
|
|
20835
|
+
import { fileURLToPath } from "url";
|
|
20836
|
+
var userAgentsFile = (home) => join4(home, ".agents", "AGENTS.md");
|
|
20837
|
+
var userSkillsDir = (home) => join4(home, ".agents", "skills");
|
|
20838
|
+
var workspaceAgentsFile = (workspace) => join4(workspace, "AGENTS.md");
|
|
20839
|
+
var workspaceSkillsDir = (workspace) => join4(workspace, ".agents", "skills");
|
|
20840
|
+
var resolveBuiltinSkillsDir = () => {
|
|
20841
|
+
for (const url of [new URL("./skills/", import.meta.url), new URL("../../skills/", import.meta.url)]) {
|
|
20842
|
+
const path = fileURLToPath(url);
|
|
20843
|
+
if (existsSync2(join4(path, "skill-creator", "SKILL.md")))
|
|
20844
|
+
return path;
|
|
20845
|
+
}
|
|
20846
|
+
return null;
|
|
20847
|
+
};
|
|
20848
|
+
var BUILTIN_SKILLS_DIR = resolveBuiltinSkillsDir();
|
|
20849
|
+
var readResources = async (workspace, home, builtinDir, report, invocationName) => {
|
|
20850
|
+
const contextFiles = [];
|
|
20851
|
+
for (const path of [userAgentsFile(home), workspaceAgentsFile(workspace)]) {
|
|
20852
|
+
try {
|
|
20853
|
+
const content = (await readFile3(path, "utf8")).replace(/^\uFEFF/, "");
|
|
20854
|
+
if (Buffer.byteLength(content, "utf8") > 64 * 1024)
|
|
20855
|
+
report({
|
|
20856
|
+
path,
|
|
20857
|
+
message: "Project instructions exceed 64 KiB; included in full. History compaction does not reduce this input."
|
|
20858
|
+
});
|
|
20859
|
+
contextFiles.push({ path, content });
|
|
20860
|
+
} catch (error) {
|
|
20861
|
+
if (!isMissing(error))
|
|
20862
|
+
report({ path, message: "Cannot read project instructions; skipped." });
|
|
20863
|
+
}
|
|
20864
|
+
}
|
|
20865
|
+
const candidates = invocationName === undefined ? undefined : new Map;
|
|
20866
|
+
const capture = candidates ? (skill, content) => {
|
|
20867
|
+
if (skill.name === invocationName)
|
|
20868
|
+
candidates.set(skill.filePath, content);
|
|
20869
|
+
} : undefined;
|
|
20870
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report, capture)).map((skill) => ({ ...skill, level }));
|
|
20871
|
+
const [builtin, user, project] = await Promise.all([
|
|
20872
|
+
builtinDir === null ? Promise.resolve([]) : scan(builtinDir, "builtin"),
|
|
20873
|
+
scan(userSkillsDir(home), "user"),
|
|
20874
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
20875
|
+
]);
|
|
20876
|
+
const skills = mergeSkills([builtin, user, project]);
|
|
20877
|
+
const winner = invocationName === undefined ? undefined : skills.find((skill) => skill.name === invocationName);
|
|
20878
|
+
if (winner && candidates) {
|
|
20879
|
+
const content = candidates.get(winner.filePath);
|
|
20880
|
+
if (content !== undefined)
|
|
20881
|
+
return { contextFiles, skills, invocation: { filePath: winner.filePath, content } };
|
|
20882
|
+
}
|
|
20883
|
+
return { contextFiles, skills };
|
|
20884
|
+
};
|
|
20885
|
+
var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic, options) => {
|
|
20886
|
+
const builtinDir = options?.builtinDir === undefined ? BUILTIN_SKILLS_DIR : options.builtinDir;
|
|
20887
|
+
const previous = new Map;
|
|
20888
|
+
return async (workspace, invocationName) => {
|
|
20889
|
+
const diagnostics = [];
|
|
20890
|
+
const resources = await readResources(workspace, home, builtinDir, (d) => diagnostics.push(d), invocationName);
|
|
20891
|
+
const known = previous.get(workspace);
|
|
20892
|
+
const current = new Set;
|
|
20893
|
+
for (const diagnostic of diagnostics) {
|
|
20894
|
+
const key = JSON.stringify(diagnostic);
|
|
20895
|
+
if (!known?.has(key) && !current.has(key))
|
|
20896
|
+
report(diagnostic);
|
|
20897
|
+
current.add(key);
|
|
20898
|
+
}
|
|
20899
|
+
if (current.size)
|
|
20900
|
+
previous.set(workspace, current);
|
|
20901
|
+
else
|
|
20902
|
+
previous.delete(workspace);
|
|
20903
|
+
return resources;
|
|
20466
20904
|
};
|
|
20467
|
-
|
|
20468
|
-
|
|
20469
|
-
|
|
20470
|
-
|
|
20471
|
-
|
|
20472
|
-
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
|
|
20476
|
-
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20488
|
-
|
|
20905
|
+
};
|
|
20906
|
+
var scanSkillsByLevel = async (workspace, home = homedir3(), builtinDir = BUILTIN_SKILLS_DIR, report = warnPromptDiagnostic) => {
|
|
20907
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report)).map((skill) => ({ ...skill, level }));
|
|
20908
|
+
const [builtin, user, project] = await Promise.all([
|
|
20909
|
+
builtinDir === null ? [] : scan(builtinDir, "builtin"),
|
|
20910
|
+
scan(userSkillsDir(home), "user"),
|
|
20911
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
20912
|
+
]);
|
|
20913
|
+
return [...project, ...user, ...builtin];
|
|
20914
|
+
};
|
|
20915
|
+
|
|
20916
|
+
// packages/core/src/prompt/snapshots.ts
|
|
20917
|
+
var createResourceSnapshots = (load, submission) => {
|
|
20918
|
+
const conversations = new Map;
|
|
20919
|
+
const stateFor = (id, workspace) => {
|
|
20920
|
+
let state = conversations.get(id);
|
|
20921
|
+
if (!state || state.workspace !== workspace) {
|
|
20922
|
+
state = { workspace, pending: new Map };
|
|
20923
|
+
conversations.set(id, state);
|
|
20924
|
+
}
|
|
20925
|
+
return state;
|
|
20926
|
+
};
|
|
20927
|
+
return {
|
|
20928
|
+
stage(id, requestId, workspace, resources) {
|
|
20929
|
+
stateFor(id, workspace).pending.set(requestId, {
|
|
20930
|
+
contextFiles: resources.contextFiles,
|
|
20931
|
+
skills: resources.skills
|
|
20932
|
+
});
|
|
20933
|
+
},
|
|
20934
|
+
async current(id, workspace) {
|
|
20935
|
+
const state = stateFor(id, workspace);
|
|
20936
|
+
for (const [requestId, snapshot] of state.pending) {
|
|
20937
|
+
const record = await submission(id, requestId);
|
|
20938
|
+
if (!record || record.status === "queued")
|
|
20939
|
+
continue;
|
|
20940
|
+
state.pending.delete(requestId);
|
|
20941
|
+
if (record.type !== "input" || record.entry === undefined)
|
|
20942
|
+
continue;
|
|
20943
|
+
if (state.entry === undefined || record.entry > state.entry) {
|
|
20944
|
+
state.active = snapshot;
|
|
20945
|
+
state.entry = record.entry;
|
|
20946
|
+
}
|
|
20489
20947
|
}
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
|
|
20948
|
+
state.active ??= load(workspace);
|
|
20949
|
+
return state.active;
|
|
20950
|
+
},
|
|
20951
|
+
discard(id, requestId) {
|
|
20952
|
+
conversations.get(id)?.pending.delete(requestId);
|
|
20953
|
+
},
|
|
20954
|
+
move(id, from, to) {
|
|
20955
|
+
const pending = conversations.get(id)?.pending;
|
|
20956
|
+
const snapshot = pending?.get(from);
|
|
20957
|
+
if (!pending || !snapshot)
|
|
20958
|
+
return;
|
|
20959
|
+
pending.delete(from);
|
|
20960
|
+
pending.set(to, snapshot);
|
|
20961
|
+
},
|
|
20962
|
+
forget(id) {
|
|
20963
|
+
conversations.delete(id);
|
|
20500
20964
|
}
|
|
20501
|
-
}
|
|
20502
|
-
}
|
|
20503
|
-
var createQuestionExtension = (service) => defineExtension6({ name: "neko-questions", tools: [createAskUserTool(service)] });
|
|
20965
|
+
};
|
|
20966
|
+
};
|
|
20504
20967
|
|
|
20505
20968
|
// packages/core/src/session-metadata.ts
|
|
20506
|
-
|
|
20969
|
+
import { stripVTControlCharacters } from "util";
|
|
20970
|
+
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
20971
|
+
import {
|
|
20972
|
+
defineDoc as defineDoc5,
|
|
20973
|
+
defineExtension as defineExtension8,
|
|
20974
|
+
defineTask as defineTask3,
|
|
20975
|
+
InboxDoc as InboxDoc2,
|
|
20976
|
+
LiveDoc as LiveDoc2
|
|
20977
|
+
} from "@earendil-works/pi-durable";
|
|
20978
|
+
var SessionMetadataDoc = defineDoc5({
|
|
20507
20979
|
kind: "app.neko.session-metadata",
|
|
20508
20980
|
version: 1,
|
|
20509
20981
|
scope: "conversation",
|
|
@@ -20526,6 +20998,8 @@ var SessionMetadataDoc = defineDoc4({
|
|
|
20526
20998
|
namingUsage: { models: {}, tools: {} }
|
|
20527
20999
|
})
|
|
20528
21000
|
});
|
|
21001
|
+
var NAMING_REQUEST_TIMEOUT_MS = 20000;
|
|
21002
|
+
var NAMING_RETRY_DELAY_MS = 2000;
|
|
20529
21003
|
function shortText(value, limit = 60) {
|
|
20530
21004
|
return [
|
|
20531
21005
|
...stripVTControlCharacters(value).replace(/\p{Cc}/gu, " ").replace(/\s+/g, " ").trim()
|
|
@@ -20563,7 +21037,7 @@ function mergeUsage(base, extra) {
|
|
|
20563
21037
|
}
|
|
20564
21038
|
return result;
|
|
20565
21039
|
}
|
|
20566
|
-
function namingTask(canUse) {
|
|
21040
|
+
function namingTask(canUse, { requestTimeoutMs, retryDelayMs }) {
|
|
20567
21041
|
let active = 0;
|
|
20568
21042
|
const waits = new Set;
|
|
20569
21043
|
return defineTask3({
|
|
@@ -20614,7 +21088,7 @@ function namingTask(canUse) {
|
|
|
20614
21088
|
return { status: "running", checkpoint: { phase: "generate", attempt: d.attempts } };
|
|
20615
21089
|
}, context);
|
|
20616
21090
|
let title = "", usage;
|
|
20617
|
-
const signal = AbortSignal.any([runtime.signal, AbortSignal.timeout(
|
|
21091
|
+
const signal = AbortSignal.any([runtime.signal, AbortSignal.timeout(requestTimeoutMs)]);
|
|
20618
21092
|
try {
|
|
20619
21093
|
const budget = Math.max(32, Math.min(12000, Math.floor((model.contextWindow - 512) / 4)));
|
|
20620
21094
|
const response = await Promise.race([
|
|
@@ -20653,7 +21127,7 @@ function namingTask(canUse) {
|
|
|
20653
21127
|
d.answer = "";
|
|
20654
21128
|
} else
|
|
20655
21129
|
d.naming = d.attempts >= 2 ? "fallback" : "pending";
|
|
20656
|
-
return title || d.attempts >= 2 ? { status: "terminal", outcome: { status: "completed", result: null } } : { status: "running", checkpoint: { phase: "wait", until: runtime.now() +
|
|
21130
|
+
return title || d.attempts >= 2 ? { status: "terminal", outcome: { status: "completed", result: null } } : { status: "running", checkpoint: { phase: "wait", until: runtime.now() + retryDelayMs } };
|
|
20657
21131
|
}, context);
|
|
20658
21132
|
} finally {
|
|
20659
21133
|
active--;
|
|
@@ -20685,13 +21159,16 @@ class SessionMetadataManager {
|
|
|
20685
21159
|
storage;
|
|
20686
21160
|
context;
|
|
20687
21161
|
report;
|
|
20688
|
-
constructor(harness, storage, context, registry, report = console.error, canUseModel = async () => true) {
|
|
20689
|
-
this.task = namingTask(canUseModel
|
|
21162
|
+
constructor(harness, storage, context, registry, report = console.error, canUseModel = async () => true, timings = {}) {
|
|
21163
|
+
this.task = namingTask(canUseModel, {
|
|
21164
|
+
requestTimeoutMs: timings.requestTimeoutMs ?? NAMING_REQUEST_TIMEOUT_MS,
|
|
21165
|
+
retryDelayMs: timings.retryDelayMs ?? NAMING_RETRY_DELAY_MS
|
|
21166
|
+
});
|
|
20690
21167
|
this.harness = harness;
|
|
20691
21168
|
this.storage = storage;
|
|
20692
21169
|
this.context = context;
|
|
20693
21170
|
this.report = report;
|
|
20694
|
-
registry.install(
|
|
21171
|
+
registry.install(defineExtension8({ name: "neko-naming", tasks: [this.task] }));
|
|
20695
21172
|
this.#unsubscribe = harness.subscribeCommits(({ changes }) => {
|
|
20696
21173
|
const ids = new Set;
|
|
20697
21174
|
const submissions = new Map;
|
|
@@ -20704,7 +21181,7 @@ class SessionMetadataManager {
|
|
|
20704
21181
|
submissions.set(c.value.conversationId, records);
|
|
20705
21182
|
}
|
|
20706
21183
|
if (c.type === "document" && c.conversationId) {
|
|
20707
|
-
if (c.record.kind === "pi.agent" || c.record.kind === QuestionsDoc.definition.kind)
|
|
21184
|
+
if (c.record.kind === "pi.agent" || c.record.kind === QuestionsDoc.definition.kind || c.record.kind === PlanDoc.definition.kind)
|
|
20708
21185
|
ids.add(c.conversationId);
|
|
20709
21186
|
if (c.record.kind === "pi.live") {
|
|
20710
21187
|
const running = c.value?.run !== undefined;
|
|
@@ -20773,7 +21250,7 @@ class SessionMetadataManager {
|
|
|
20773
21250
|
cursor = page.next;
|
|
20774
21251
|
} while (cursor);
|
|
20775
21252
|
records.sort((a, b) => a.id - b.id);
|
|
20776
|
-
const inbox = await this.harness.snapshot(
|
|
21253
|
+
const inbox = await this.harness.snapshot(InboxDoc2, id, this.context);
|
|
20777
21254
|
const updates = await Promise.all(records.filter((r) => r.type === "input").map(async (record) => {
|
|
20778
21255
|
const entry = record.entry === undefined ? undefined : (await this.storage.entry(record.entry, this.context))?.entry;
|
|
20779
21256
|
const queued = inbox?.items.find((i) => i.id === record.id && i.mode !== "write");
|
|
@@ -20783,10 +21260,13 @@ class SessionMetadataManager {
|
|
|
20783
21260
|
}));
|
|
20784
21261
|
const live = await this.harness.snapshot(LiveDoc2, id, this.context);
|
|
20785
21262
|
const awaiting = live?.run !== undefined && ((await this.harness.snapshot(QuestionsDoc, id, this.context))?.pending.length ?? 0) > 0;
|
|
21263
|
+
const awaitingPlan = (await this.harness.snapshot(PlanDoc, id, this.context))?.items.some((item) => item.status === "awaiting_approval");
|
|
20786
21264
|
await this.harness.commit(async (tx) => {
|
|
20787
21265
|
const d = await tx.doc(SessionMetadataDoc, id);
|
|
20788
21266
|
if ((d.awaiting ?? false) !== awaiting)
|
|
20789
21267
|
d.awaiting = awaiting;
|
|
21268
|
+
if ((d.awaitingPlan ?? false) !== (awaitingPlan ?? false))
|
|
21269
|
+
d.awaitingPlan = awaitingPlan ?? false;
|
|
20790
21270
|
for (const { record, data, answer } of updates) {
|
|
20791
21271
|
if (!data)
|
|
20792
21272
|
continue;
|
|
@@ -20834,9 +21314,9 @@ class SessionMetadataManager {
|
|
|
20834
21314
|
}
|
|
20835
21315
|
|
|
20836
21316
|
// packages/core/src/todos.ts
|
|
20837
|
-
import { defineDoc as
|
|
20838
|
-
import { Type as
|
|
20839
|
-
var TodosDoc =
|
|
21317
|
+
import { defineDoc as defineDoc6, defineExtension as defineExtension9, defineTool as defineTool6 } from "@earendil-works/pi-durable";
|
|
21318
|
+
import { Type as Type6 } from "typebox";
|
|
21319
|
+
var TodosDoc = defineDoc6({
|
|
20840
21320
|
kind: "app.neko.todos",
|
|
20841
21321
|
version: 1,
|
|
20842
21322
|
scope: "conversation",
|
|
@@ -20844,30 +21324,30 @@ var TodosDoc = defineDoc5({
|
|
|
20844
21324
|
fork: "asOf",
|
|
20845
21325
|
initial: () => ({ revision: 0, items: [] })
|
|
20846
21326
|
});
|
|
20847
|
-
var TodoReceiptDoc =
|
|
21327
|
+
var TodoReceiptDoc = defineDoc6({
|
|
20848
21328
|
kind: "app.neko.todo-receipt",
|
|
20849
21329
|
version: 1,
|
|
20850
21330
|
scope: "task",
|
|
20851
21331
|
initial: () => ({ applied: false, revision: 0, items: [] })
|
|
20852
21332
|
});
|
|
20853
|
-
var itemSchema =
|
|
20854
|
-
id:
|
|
21333
|
+
var itemSchema = Type6.Object({
|
|
21334
|
+
id: Type6.String({
|
|
20855
21335
|
minLength: 1,
|
|
20856
21336
|
maxLength: TODO_LIMITS.idChars,
|
|
20857
21337
|
description: "Stable id; keep it when you update the same task."
|
|
20858
21338
|
}),
|
|
20859
|
-
text:
|
|
20860
|
-
status:
|
|
21339
|
+
text: Type6.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
|
|
21340
|
+
status: Type6.String({ enum: [...TODO_STATUSES] })
|
|
20861
21341
|
}, { additionalProperties: false });
|
|
20862
|
-
var
|
|
20863
|
-
action:
|
|
20864
|
-
items:
|
|
21342
|
+
var parameters5 = Type6.Object({
|
|
21343
|
+
action: Type6.String({ enum: ["list", "replace"], description: "list reads the list; replace overwrites it." }),
|
|
21344
|
+
items: Type6.Optional(Type6.Array(itemSchema, {
|
|
20865
21345
|
maxItems: TODO_LIMITS.maxItems,
|
|
20866
21346
|
description: "Required for replace only: the complete ordered list; [] clears it."
|
|
20867
21347
|
}))
|
|
20868
21348
|
}, { additionalProperties: false });
|
|
20869
|
-
var structuredOutputSchema3 =
|
|
20870
|
-
var
|
|
21349
|
+
var structuredOutputSchema3 = Type6.Object({ revision: Type6.Integer({ minimum: 0 }), items: Type6.Array(itemSchema) }, { additionalProperties: false });
|
|
21350
|
+
var clone3 = (value) => JSON.parse(JSON.stringify(value));
|
|
20871
21351
|
function renderTodos(list) {
|
|
20872
21352
|
const { completed, total } = todoProgress(list.items);
|
|
20873
21353
|
if (!total)
|
|
@@ -20880,10 +21360,10 @@ function renderTodos(list) {
|
|
|
20880
21360
|
`);
|
|
20881
21361
|
}
|
|
20882
21362
|
function createTodoTool() {
|
|
20883
|
-
return
|
|
21363
|
+
return defineTool6({
|
|
20884
21364
|
name: "todo",
|
|
20885
21365
|
description: "Keep a structured todo list for the current multi-step task. list: read the saved list. replace: items [{id, text, status}] overwrites the whole ordered list (status pending, in_progress or completed; [] clears it). Keep ids stable when updating a task and mark completed only after the work is actually done. Users see the list but cannot edit it.",
|
|
20886
|
-
parameters:
|
|
21366
|
+
parameters: parameters5,
|
|
20887
21367
|
structuredOutputSchema: structuredOutputSchema3,
|
|
20888
21368
|
replay: "safe",
|
|
20889
21369
|
executionMode: "sequential",
|
|
@@ -20898,7 +21378,7 @@ function createTodoTool() {
|
|
|
20898
21378
|
if (args.items !== undefined)
|
|
20899
21379
|
throw new Error("list does not accept items.");
|
|
20900
21380
|
const saved = await api.snapshot(TodosDoc, api.conversationId, context);
|
|
20901
|
-
list = saved ?
|
|
21381
|
+
list = saved ? clone3(saved) : { revision: 0, items: [] };
|
|
20902
21382
|
} else {
|
|
20903
21383
|
if (args.items === undefined)
|
|
20904
21384
|
throw new Error("replace requires items.");
|
|
@@ -20906,9 +21386,9 @@ function createTodoTool() {
|
|
|
20906
21386
|
list = await api.commit(async (tx) => {
|
|
20907
21387
|
const receipt = await tx.doc(TodoReceiptDoc, api.taskId);
|
|
20908
21388
|
if (receipt.applied) {
|
|
20909
|
-
if (JSON.stringify(
|
|
21389
|
+
if (JSON.stringify(clone3(receipt.items)) !== JSON.stringify(items))
|
|
20910
21390
|
throw new Error("This todo call was already applied with different items.");
|
|
20911
|
-
return { revision: receipt.revision, items:
|
|
21391
|
+
return { revision: receipt.revision, items: clone3(receipt.items) };
|
|
20912
21392
|
}
|
|
20913
21393
|
const doc = await tx.doc(TodosDoc, api.conversationId);
|
|
20914
21394
|
doc.revision += 1;
|
|
@@ -20916,7 +21396,7 @@ function createTodoTool() {
|
|
|
20916
21396
|
receipt.applied = true;
|
|
20917
21397
|
receipt.revision = doc.revision;
|
|
20918
21398
|
receipt.items = items;
|
|
20919
|
-
return { revision: doc.revision, items:
|
|
21399
|
+
return { revision: doc.revision, items: clone3(items) };
|
|
20920
21400
|
}, context);
|
|
20921
21401
|
}
|
|
20922
21402
|
const { completed, total } = todoProgress(list.items);
|
|
@@ -20928,21 +21408,21 @@ function createTodoTool() {
|
|
|
20928
21408
|
}
|
|
20929
21409
|
});
|
|
20930
21410
|
}
|
|
20931
|
-
var createTodoExtension = () =>
|
|
21411
|
+
var createTodoExtension = () => defineExtension9({ name: "neko-todos", tools: [createTodoTool()] });
|
|
20932
21412
|
|
|
20933
21413
|
// packages/core/src/tool-approvals.ts
|
|
20934
|
-
import { randomUUID as
|
|
21414
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
20935
21415
|
import { realpath } from "fs/promises";
|
|
20936
21416
|
import {
|
|
20937
21417
|
configure as configure2,
|
|
20938
|
-
defineDoc as
|
|
20939
|
-
defineExtension as
|
|
20940
|
-
hook as
|
|
20941
|
-
ToolTask as
|
|
20942
|
-
wrapTool
|
|
21418
|
+
defineDoc as defineDoc7,
|
|
21419
|
+
defineExtension as defineExtension10,
|
|
21420
|
+
hook as hook3,
|
|
21421
|
+
ToolTask as ToolTask3,
|
|
21422
|
+
wrapTool as wrapTool2
|
|
20943
21423
|
} from "@earendil-works/pi-durable";
|
|
20944
|
-
import { createBashTool } from "@earendil-works/pi-durable/tools";
|
|
20945
|
-
var ApprovalsDoc =
|
|
21424
|
+
import { createBashTool as createBashTool2 } from "@earendil-works/pi-durable/tools";
|
|
21425
|
+
var ApprovalsDoc = defineDoc7({
|
|
20946
21426
|
kind: "app.neko.approvals",
|
|
20947
21427
|
version: 1,
|
|
20948
21428
|
scope: "conversation",
|
|
@@ -21041,7 +21521,7 @@ class ToolApprovals {
|
|
|
21041
21521
|
}
|
|
21042
21522
|
doc.items.push({
|
|
21043
21523
|
...candidate,
|
|
21044
|
-
version:
|
|
21524
|
+
version: randomUUID3(),
|
|
21045
21525
|
status: "pending",
|
|
21046
21526
|
createdAt: Date.now(),
|
|
21047
21527
|
decidedAt: null
|
|
@@ -21050,10 +21530,10 @@ class ToolApprovals {
|
|
|
21050
21530
|
}
|
|
21051
21531
|
}
|
|
21052
21532
|
function createApprovalExtension(service) {
|
|
21053
|
-
return
|
|
21533
|
+
return defineExtension10({
|
|
21054
21534
|
name: "neko-tool-approvals",
|
|
21055
21535
|
hooks: [
|
|
21056
|
-
|
|
21536
|
+
hook3(ToolTask3, {
|
|
21057
21537
|
beforeTool: async (call, api, context) => {
|
|
21058
21538
|
const role = await api.snapshot(SubagentDoc, api.conversationId, context);
|
|
21059
21539
|
if (role?.role !== "explore" || call.name !== "bash")
|
|
@@ -21083,7 +21563,7 @@ function createApprovalExtension(service) {
|
|
|
21083
21563
|
})
|
|
21084
21564
|
],
|
|
21085
21565
|
wraps: [
|
|
21086
|
-
|
|
21566
|
+
wrapTool2(createBashTool2(), (bash) => ({
|
|
21087
21567
|
...bash,
|
|
21088
21568
|
execute: async (args, api, context) => {
|
|
21089
21569
|
const role = await api.snapshot(SubagentDoc, api.conversationId, context);
|
|
@@ -21107,7 +21587,7 @@ async function prepareApprovalConversations(harness, ids, extension, context) {
|
|
|
21107
21587
|
const child = await harness.conversation(id, context);
|
|
21108
21588
|
const agent = await child.agent(context);
|
|
21109
21589
|
await harness.commit((tx) => configure2(tx, id, {
|
|
21110
|
-
tools: [...agent.tools.filter((t) => t.name !== "bash"),
|
|
21590
|
+
tools: [...agent.tools.filter((t) => t.name !== "bash"), createBashTool2()],
|
|
21111
21591
|
extensions: { add: [extension] }
|
|
21112
21592
|
}), context);
|
|
21113
21593
|
}
|
|
@@ -21334,8 +21814,8 @@ function createDeepseekProvider(resolveKey, fetchImpl = fetch, options = {}) {
|
|
|
21334
21814
|
}
|
|
21335
21815
|
|
|
21336
21816
|
// packages/core/src/web-search/index.ts
|
|
21337
|
-
import { Type as
|
|
21338
|
-
import { defineExtension as
|
|
21817
|
+
import { Type as Type7 } from "@earendil-works/pi-ai";
|
|
21818
|
+
import { defineExtension as defineExtension11, defineTool as defineTool7 } from "@earendil-works/pi-durable";
|
|
21339
21819
|
var MAX_URLS = 20;
|
|
21340
21820
|
var MAX_RESULTS = 10;
|
|
21341
21821
|
var DEFAULT_RESULTS = 5;
|
|
@@ -21353,15 +21833,15 @@ function content(text) {
|
|
|
21353
21833
|
return { output: [{ type: "text", text }] };
|
|
21354
21834
|
}
|
|
21355
21835
|
function createWebSearchTools(resolveProvider) {
|
|
21356
|
-
return
|
|
21836
|
+
return defineExtension11({
|
|
21357
21837
|
name: "neko-web-search",
|
|
21358
21838
|
tools: [
|
|
21359
|
-
|
|
21839
|
+
defineTool7({
|
|
21360
21840
|
name: "web_search",
|
|
21361
21841
|
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.",
|
|
21362
|
-
parameters:
|
|
21363
|
-
query:
|
|
21364
|
-
maxResults:
|
|
21842
|
+
parameters: Type7.Object({
|
|
21843
|
+
query: Type7.String({ minLength: 1, maxLength: 4096, description: "The search query" }),
|
|
21844
|
+
maxResults: Type7.Optional(Type7.Number({ description: `Maximum number of results (1-${MAX_RESULTS}, default ${DEFAULT_RESULTS})` }))
|
|
21365
21845
|
}),
|
|
21366
21846
|
async execute({ query, maxResults }, _api, context) {
|
|
21367
21847
|
try {
|
|
@@ -21387,11 +21867,11 @@ ${item.date ? `Published: ${item.date}
|
|
|
21387
21867
|
}
|
|
21388
21868
|
}
|
|
21389
21869
|
}),
|
|
21390
|
-
|
|
21870
|
+
defineTool7({
|
|
21391
21871
|
name: "web_extract",
|
|
21392
21872
|
description: "Extract bounded markdown excerpts from web pages by URL. Every requested URL has a result or failure entry; long content is truncated.",
|
|
21393
|
-
parameters:
|
|
21394
|
-
urls:
|
|
21873
|
+
parameters: Type7.Object({
|
|
21874
|
+
urls: Type7.Array(Type7.String({ maxLength: 2048, description: "http(s) URL" }), {
|
|
21395
21875
|
minItems: 1,
|
|
21396
21876
|
maxItems: MAX_URLS,
|
|
21397
21877
|
description: `URLs to extract (1-${MAX_URLS})`
|
|
@@ -22116,7 +22596,7 @@ function cancellable(work, signal) {
|
|
|
22116
22596
|
var DEFAULT_MODEL = { provider: "deepseek", modelId: "deepseek-flash" };
|
|
22117
22597
|
var DEFAULT_STORAGE_PATH = join5(homedir4(), ".neko", "neko.sqlite");
|
|
22118
22598
|
var neverAborted = () => new AbortController().signal;
|
|
22119
|
-
var ArchivedDoc =
|
|
22599
|
+
var ArchivedDoc = defineDoc8({
|
|
22120
22600
|
kind: "app.neko.conversation",
|
|
22121
22601
|
version: 1,
|
|
22122
22602
|
scope: "conversation",
|
|
@@ -22124,7 +22604,7 @@ var ArchivedDoc = defineDoc7({
|
|
|
22124
22604
|
fork: "initial",
|
|
22125
22605
|
initial: () => ({ archived: false })
|
|
22126
22606
|
});
|
|
22127
|
-
var WorkspacesDoc =
|
|
22607
|
+
var WorkspacesDoc = defineDoc8({
|
|
22128
22608
|
kind: "app.neko.workspaces",
|
|
22129
22609
|
version: 1,
|
|
22130
22610
|
scope: "conversation",
|
|
@@ -22132,7 +22612,7 @@ var WorkspacesDoc = defineDoc7({
|
|
|
22132
22612
|
fork: "initial",
|
|
22133
22613
|
initial: () => ({ items: [] })
|
|
22134
22614
|
});
|
|
22135
|
-
var SearchSettingsDoc =
|
|
22615
|
+
var SearchSettingsDoc = defineDoc8({
|
|
22136
22616
|
kind: "app.neko.search-settings",
|
|
22137
22617
|
version: 1,
|
|
22138
22618
|
scope: "conversation",
|
|
@@ -22140,7 +22620,7 @@ var SearchSettingsDoc = defineDoc7({
|
|
|
22140
22620
|
fork: "initial",
|
|
22141
22621
|
initial: () => ({ provider: null })
|
|
22142
22622
|
});
|
|
22143
|
-
var ProviderSettingsDoc =
|
|
22623
|
+
var ProviderSettingsDoc = defineDoc8({
|
|
22144
22624
|
kind: "app.neko.provider-settings",
|
|
22145
22625
|
version: 1,
|
|
22146
22626
|
scope: "conversation",
|
|
@@ -22148,7 +22628,7 @@ var ProviderSettingsDoc = defineDoc7({
|
|
|
22148
22628
|
fork: "initial",
|
|
22149
22629
|
initial: () => ({ enabled: null })
|
|
22150
22630
|
});
|
|
22151
|
-
var DefaultModelDoc =
|
|
22631
|
+
var DefaultModelDoc = defineDoc8({
|
|
22152
22632
|
kind: "app.neko.default-model",
|
|
22153
22633
|
version: 1,
|
|
22154
22634
|
scope: "conversation",
|
|
@@ -22156,7 +22636,7 @@ var DefaultModelDoc = defineDoc7({
|
|
|
22156
22636
|
fork: "initial",
|
|
22157
22637
|
initial: () => ({ model: null })
|
|
22158
22638
|
});
|
|
22159
|
-
var ContextSettingsDoc =
|
|
22639
|
+
var ContextSettingsDoc = defineDoc8({
|
|
22160
22640
|
kind: "app.neko.context-settings",
|
|
22161
22641
|
version: 1,
|
|
22162
22642
|
scope: "conversation",
|
|
@@ -22164,7 +22644,7 @@ var ContextSettingsDoc = defineDoc7({
|
|
|
22164
22644
|
fork: "initial",
|
|
22165
22645
|
initial: emptySavedContextSettings
|
|
22166
22646
|
});
|
|
22167
|
-
var ActivityDoc =
|
|
22647
|
+
var ActivityDoc = defineDoc8({
|
|
22168
22648
|
kind: "app.neko.activity",
|
|
22169
22649
|
version: 1,
|
|
22170
22650
|
scope: "conversation",
|
|
@@ -22263,11 +22743,13 @@ async function createNekoRuntime(options = {}) {
|
|
|
22263
22743
|
registry.install(createSubagentExtension(async (id, requestId, cwd) => {
|
|
22264
22744
|
resourceSnapshots.stage(id, requestId, cwd, await loadResources(cwd));
|
|
22265
22745
|
}, resourceSnapshots.discard, (ref) => guardProvider(ref)));
|
|
22266
|
-
const jobs = new JobManager;
|
|
22746
|
+
const jobs = new JobManager(options.timings?.jobStartWindowMs === undefined ? {} : { startWindowMs: options.timings.jobStartWindowMs });
|
|
22267
22747
|
registry.install(jobs.extension);
|
|
22268
22748
|
registry.install(createTodoExtension());
|
|
22269
22749
|
let questions;
|
|
22270
22750
|
registry.install(createQuestionExtension(() => questions));
|
|
22751
|
+
let plans;
|
|
22752
|
+
registry.install(createPlanExtension(() => plans));
|
|
22271
22753
|
for (const extension of options.extensions ?? [])
|
|
22272
22754
|
registry.install(extension);
|
|
22273
22755
|
let approvals;
|
|
@@ -22279,6 +22761,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
22279
22761
|
models,
|
|
22280
22762
|
registry,
|
|
22281
22763
|
settings: {
|
|
22764
|
+
...options.timings?.retryBaseDelayMs === undefined ? {} : { retry: { baseDelayMs: options.timings.retryBaseDelayMs } },
|
|
22282
22765
|
get compaction() {
|
|
22283
22766
|
return compactionPolicy(contextState, allModels().filter((model) => !isModelProvider(model.provider) || enabled.has(model.provider)).map((model) => model.contextWindow));
|
|
22284
22767
|
}
|
|
@@ -22301,6 +22784,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
22301
22784
|
}, context);
|
|
22302
22785
|
approvals = new ToolApprovals(harness, context);
|
|
22303
22786
|
questions = new UserQuestions(harness, context);
|
|
22787
|
+
plans = new PlanService(harness, context);
|
|
22304
22788
|
const changes = new ChangeTracker(harness, context);
|
|
22305
22789
|
const existing = await scanConversations();
|
|
22306
22790
|
questions.track(...existing.map((r) => r.id));
|
|
@@ -22376,7 +22860,10 @@ async function createNekoRuntime(options = {}) {
|
|
|
22376
22860
|
};
|
|
22377
22861
|
if (existing.length === 0)
|
|
22378
22862
|
await touchConversation(conversation.id);
|
|
22379
|
-
const metadata = new SessionMetadataManager(harness, storage, context, registry, options.onReport, (provider, modelId) => guardProvider({ provider, modelId }).then(() => true, () => false)
|
|
22863
|
+
const metadata = new SessionMetadataManager(harness, storage, context, registry, options.onReport, (provider, modelId) => guardProvider({ provider, modelId }).then(() => true, () => false), {
|
|
22864
|
+
...options.timings?.namingRequestTimeoutMs === undefined ? {} : { requestTimeoutMs: options.timings.namingRequestTimeoutMs },
|
|
22865
|
+
...options.timings?.namingRetryDelayMs === undefined ? {} : { retryDelayMs: options.timings.namingRetryDelayMs }
|
|
22866
|
+
});
|
|
22380
22867
|
if (existing.length === 0)
|
|
22381
22868
|
await metadata.initialize(conversation.id);
|
|
22382
22869
|
for (const record of existing)
|
|
@@ -22417,11 +22904,17 @@ async function createNekoRuntime(options = {}) {
|
|
|
22417
22904
|
admissions.delete(id);
|
|
22418
22905
|
}
|
|
22419
22906
|
};
|
|
22420
|
-
const
|
|
22907
|
+
const syncPlanAgent = async (target) => {
|
|
22908
|
+
const mode = await plans.mode(target.id);
|
|
22909
|
+
if (mode === "plan" !== agentInPlanMode(await target.agent(context)))
|
|
22910
|
+
await target.configure(planAgentChange(mode), context);
|
|
22911
|
+
};
|
|
22912
|
+
const submitTo = async (target, content, submitOptions, extra) => admit(target.id, async () => {
|
|
22913
|
+
await syncPlanAgent(target);
|
|
22421
22914
|
const agent = await target.agent(context);
|
|
22422
22915
|
const workspace = executionWorkspace(agent.cwd);
|
|
22423
22916
|
const duplicate = submitOptions?.requestId !== undefined && await storage.submissionByRequest(target.id, submitOptions.requestId, context);
|
|
22424
|
-
const data = { at: Date.now(), prompt: "", images: [] };
|
|
22917
|
+
const data = { at: Date.now(), prompt: "", images: [], ...extra };
|
|
22425
22918
|
let invocationName;
|
|
22426
22919
|
if (!duplicate && typeof content === "string" && content.startsWith("/skill:")) {
|
|
22427
22920
|
const end = content.indexOf(" ");
|
|
@@ -22485,10 +22978,25 @@ async function createNekoRuntime(options = {}) {
|
|
|
22485
22978
|
resourceSnapshots.discard(target.id, requestId);
|
|
22486
22979
|
throw error;
|
|
22487
22980
|
}
|
|
22981
|
+
if (!extra?.planDecision)
|
|
22982
|
+
await plans.supersedePending(target.id);
|
|
22488
22983
|
await touchConversation(target.id);
|
|
22489
22984
|
await metadata.reconcile(target.id);
|
|
22490
22985
|
return result;
|
|
22491
22986
|
});
|
|
22987
|
+
const dispatchPlan = async (target, item) => {
|
|
22988
|
+
const approve = item.status === "approved";
|
|
22989
|
+
await submitTo(target, approve ? approvedPlanPrompt(item) : planRevisionPrompt(item, item.feedback ?? ""), { requestId: `${approve ? "plan-exec" : "plan-revise"}:${item.version}`, whenBusy: "followUp" }, {
|
|
22990
|
+
planDecision: {
|
|
22991
|
+
version: item.version,
|
|
22992
|
+
kind: approve ? "approve" : "revise",
|
|
22993
|
+
title: item.title,
|
|
22994
|
+
...!approve && item.feedback ? { feedback: item.feedback } : {}
|
|
22995
|
+
}
|
|
22996
|
+
});
|
|
22997
|
+
await plans.markDispatched(target.id, item.version);
|
|
22998
|
+
};
|
|
22999
|
+
const isRunning = async (id) => (await harness.snapshot(importLiveDoc, id, context))?.run !== undefined;
|
|
22492
23000
|
const waitAnswerOf = async (target, submission) => {
|
|
22493
23001
|
const settled = await submission.wait(context);
|
|
22494
23002
|
await metadata.flush();
|
|
@@ -22553,6 +23061,38 @@ async function createNekoRuntime(options = {}) {
|
|
|
22553
23061
|
respondQuestion(id, version, response) {
|
|
22554
23062
|
return questions.respond(target.id, id, version, response, context);
|
|
22555
23063
|
},
|
|
23064
|
+
plan() {
|
|
23065
|
+
return plans.view(target.id);
|
|
23066
|
+
},
|
|
23067
|
+
planRecord(version) {
|
|
23068
|
+
return plans.get(target.id, version);
|
|
23069
|
+
},
|
|
23070
|
+
async setPlanMode(mode) {
|
|
23071
|
+
if (mode !== "normal" && mode !== "plan")
|
|
23072
|
+
throw new Error("Unknown plan mode.");
|
|
23073
|
+
return admit(target.id, async () => {
|
|
23074
|
+
if (await isRunning(target.id))
|
|
23075
|
+
throw new Error(PLAN_BUSY);
|
|
23076
|
+
await plans.setMode(target.id, mode);
|
|
23077
|
+
await syncPlanAgent(target);
|
|
23078
|
+
return plans.view(target.id);
|
|
23079
|
+
});
|
|
23080
|
+
},
|
|
23081
|
+
async respondPlan(version, decision) {
|
|
23082
|
+
const parsed = validatePlanDecision(decision);
|
|
23083
|
+
const known = await plans.get(target.id, version);
|
|
23084
|
+
if (!known)
|
|
23085
|
+
throw new Error("Plan not found.");
|
|
23086
|
+
if (known.status === "awaiting_approval") {
|
|
23087
|
+
if (await isRunning(target.id))
|
|
23088
|
+
throw new Error(PLAN_BUSY);
|
|
23089
|
+
await guardProvider((await target.agent(context)).model);
|
|
23090
|
+
}
|
|
23091
|
+
const { record, item } = await plans.decide(target.id, version, parsed);
|
|
23092
|
+
if ((item.status === "approved" || item.status === "revision_requested") && !item.dispatched)
|
|
23093
|
+
await dispatchPlan(target, item);
|
|
23094
|
+
return record;
|
|
23095
|
+
},
|
|
22556
23096
|
async workspaceDir(path, signal) {
|
|
22557
23097
|
return workspaceFiles.children(savedWorkspace((await target.agent(context)).cwd), path, signal ?? neverAborted());
|
|
22558
23098
|
},
|
|
@@ -22578,12 +23118,13 @@ async function createNekoRuntime(options = {}) {
|
|
|
22578
23118
|
await target.compact(instructions?.trim() || undefined, context);
|
|
22579
23119
|
});
|
|
22580
23120
|
},
|
|
22581
|
-
abort() {
|
|
22582
|
-
|
|
23121
|
+
async abort() {
|
|
23122
|
+
await target.abort(context);
|
|
23123
|
+
await plans.cancelPending(target.id);
|
|
22583
23124
|
},
|
|
22584
23125
|
async withdraw(submissionId) {
|
|
22585
23126
|
const id = submissionId;
|
|
22586
|
-
const inbox = await harness.snapshot(
|
|
23127
|
+
const inbox = await harness.snapshot(InboxDoc3, target.id, context);
|
|
22587
23128
|
const item = inbox?.items.find((i) => i.id === id);
|
|
22588
23129
|
const input = item ? queuedUserInput(item) : undefined;
|
|
22589
23130
|
if (item && !input)
|
|
@@ -22600,7 +23141,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
22600
23141
|
},
|
|
22601
23142
|
async steer(submissionId) {
|
|
22602
23143
|
const id = submissionId;
|
|
22603
|
-
const inbox = await harness.snapshot(
|
|
23144
|
+
const inbox = await harness.snapshot(InboxDoc3, target.id, context);
|
|
22604
23145
|
const item = inbox?.items.find((i) => i.id === id);
|
|
22605
23146
|
const input = item ? queuedUserInput(item) : undefined;
|
|
22606
23147
|
if (item && (!input || item.mode === "write"))
|
|
@@ -22650,6 +23191,19 @@ async function createNekoRuntime(options = {}) {
|
|
|
22650
23191
|
const record = await harness.commit((tx) => tx.conversation(id), context);
|
|
22651
23192
|
return archived || record?.owner ? undefined : makeSession(target);
|
|
22652
23193
|
};
|
|
23194
|
+
for (const id of new Set([conversation.id, ...existing.map((r) => r.id)])) {
|
|
23195
|
+
try {
|
|
23196
|
+
const session = await getConversation(id);
|
|
23197
|
+
if (!session)
|
|
23198
|
+
continue;
|
|
23199
|
+
await admit(id, () => syncPlanAgent(session.conversation));
|
|
23200
|
+
const pending = await plans.undispatched(id);
|
|
23201
|
+
if (pending)
|
|
23202
|
+
await dispatchPlan(session.conversation, pending);
|
|
23203
|
+
} catch (error) {
|
|
23204
|
+
options.onReport?.(error);
|
|
23205
|
+
}
|
|
23206
|
+
}
|
|
22653
23207
|
return {
|
|
22654
23208
|
context,
|
|
22655
23209
|
async compact(instructions, id) {
|
|
@@ -22788,6 +23342,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
22788
23342
|
lastMessageAt: info.lastMessageAt,
|
|
22789
23343
|
status: info.status,
|
|
22790
23344
|
awaitingAnswer: info.awaiting === true,
|
|
23345
|
+
awaitingPlan: info.awaitingPlan === true,
|
|
22791
23346
|
model: (await target.agent(context)).model ?? null,
|
|
22792
23347
|
id: record.id,
|
|
22793
23348
|
archived: false,
|
|
@@ -22990,7 +23545,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
22990
23545
|
}
|
|
22991
23546
|
// packages/gateway/src/contract.ts
|
|
22992
23547
|
import { hostNamed, method, remote } from "@earendil-works/chord";
|
|
22993
|
-
import { Type as
|
|
23548
|
+
import { Type as Type8 } from "typebox";
|
|
22994
23549
|
var GATEWAY_HOST = "neko";
|
|
22995
23550
|
var rpc = (args, result) => [args, result];
|
|
22996
23551
|
function contract(name, def) {
|
|
@@ -23003,19 +23558,19 @@ function contract(name, def) {
|
|
|
23003
23558
|
]));
|
|
23004
23559
|
return remote(name, { ...def, host: hostNamed(GATEWAY_HOST), variants });
|
|
23005
23560
|
}
|
|
23006
|
-
var str =
|
|
23007
|
-
var num =
|
|
23008
|
-
var bool =
|
|
23009
|
-
var nil =
|
|
23010
|
-
var integer =
|
|
23011
|
-
var nullableString =
|
|
23012
|
-
var phase =
|
|
23013
|
-
var level =
|
|
23014
|
-
var thinking =
|
|
23015
|
-
var model2 =
|
|
23016
|
-
var change =
|
|
23017
|
-
var usage =
|
|
23018
|
-
var image =
|
|
23561
|
+
var str = Type8.String();
|
|
23562
|
+
var num = Type8.Number();
|
|
23563
|
+
var bool = Type8.Boolean();
|
|
23564
|
+
var nil = Type8.Null();
|
|
23565
|
+
var integer = Type8.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
|
|
23566
|
+
var nullableString = Type8.Union([str, nil]);
|
|
23567
|
+
var phase = Type8.Enum(["pending", "running", "done"]);
|
|
23568
|
+
var level = Type8.Enum(["project", "user", "builtin"]);
|
|
23569
|
+
var thinking = Type8.Enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
23570
|
+
var model2 = Type8.Object({ provider: str, modelId: str });
|
|
23571
|
+
var change = Type8.Object({ model: Type8.Optional(model2), thinkingLevel: Type8.Optional(thinking) }, { additionalProperties: false });
|
|
23572
|
+
var usage = Type8.Object({ input: num, output: num, cacheRead: num, cacheWrite: num, cost: num });
|
|
23573
|
+
var image = Type8.Object({
|
|
23019
23574
|
name: str,
|
|
23020
23575
|
mimeType: str,
|
|
23021
23576
|
width: integer,
|
|
@@ -23024,8 +23579,8 @@ var image = Type7.Object({
|
|
|
23024
23579
|
entryId: integer,
|
|
23025
23580
|
contentIndex: integer
|
|
23026
23581
|
});
|
|
23027
|
-
var skill =
|
|
23028
|
-
var call =
|
|
23582
|
+
var skill = Type8.Object({ name: str, location: str, body: str, args: str });
|
|
23583
|
+
var call = Type8.Object({
|
|
23029
23584
|
callId: str,
|
|
23030
23585
|
name: str,
|
|
23031
23586
|
summary: str,
|
|
@@ -23033,262 +23588,289 @@ var call = Type7.Object({
|
|
|
23033
23588
|
phase,
|
|
23034
23589
|
output: nullableString,
|
|
23035
23590
|
diff: nullableString,
|
|
23036
|
-
diffAdded:
|
|
23037
|
-
diffRemoved:
|
|
23038
|
-
diffTruncated:
|
|
23591
|
+
diffAdded: Type8.Union([num, nil]),
|
|
23592
|
+
diffRemoved: Type8.Union([num, nil]),
|
|
23593
|
+
diffTruncated: Type8.Optional(bool)
|
|
23039
23594
|
});
|
|
23040
|
-
var DelegationSchema =
|
|
23595
|
+
var DelegationSchema = Type8.Object({
|
|
23041
23596
|
parentId: integer,
|
|
23042
|
-
agentId:
|
|
23043
|
-
round:
|
|
23044
|
-
runnerId:
|
|
23045
|
-
inputEntryId:
|
|
23046
|
-
answerEntryId:
|
|
23047
|
-
roundUsage:
|
|
23597
|
+
agentId: Type8.Optional(str),
|
|
23598
|
+
round: Type8.Optional(integer),
|
|
23599
|
+
runnerId: Type8.Optional(integer),
|
|
23600
|
+
inputEntryId: Type8.Optional(integer),
|
|
23601
|
+
answerEntryId: Type8.Optional(integer),
|
|
23602
|
+
roundUsage: Type8.Optional(Type8.Union([usage, nil])),
|
|
23048
23603
|
taskId: integer,
|
|
23049
23604
|
callId: str,
|
|
23050
23605
|
childId: integer,
|
|
23051
|
-
role:
|
|
23606
|
+
role: Type8.Enum(["explore", "plan", "coder"]),
|
|
23052
23607
|
title: str,
|
|
23053
23608
|
createdAt: num,
|
|
23054
|
-
startedAt:
|
|
23055
|
-
endedAt:
|
|
23056
|
-
status:
|
|
23609
|
+
startedAt: Type8.Union([num, nil]),
|
|
23610
|
+
endedAt: Type8.Union([num, nil]),
|
|
23611
|
+
status: Type8.Enum(["queued", "running", "completed", "failed", "cancelled"]),
|
|
23057
23612
|
report: nullableString,
|
|
23058
|
-
approvalCount:
|
|
23059
|
-
historyThrough:
|
|
23060
|
-
historyUsage:
|
|
23613
|
+
approvalCount: Type8.Optional(integer),
|
|
23614
|
+
historyThrough: Type8.Optional(integer),
|
|
23615
|
+
historyUsage: Type8.Optional(usage)
|
|
23061
23616
|
});
|
|
23062
|
-
var SubagentSummarySchema =
|
|
23617
|
+
var SubagentSummarySchema = Type8.Object({
|
|
23063
23618
|
agentId: str,
|
|
23064
23619
|
parentId: integer,
|
|
23065
23620
|
childId: integer,
|
|
23066
|
-
role:
|
|
23621
|
+
role: Type8.Enum(["explore", "plan", "coder"]),
|
|
23067
23622
|
title: str,
|
|
23068
|
-
status:
|
|
23069
|
-
activeRoundId:
|
|
23623
|
+
status: Type8.Enum(["idle", "queued", "running"]),
|
|
23624
|
+
activeRoundId: Type8.Union([integer, nil]),
|
|
23070
23625
|
lastRoundId: integer,
|
|
23071
|
-
lastReport:
|
|
23626
|
+
lastReport: Type8.Optional(nullableString),
|
|
23072
23627
|
roundCount: integer,
|
|
23073
23628
|
taskTitle: str,
|
|
23074
23629
|
approvalCount: integer,
|
|
23075
23630
|
createdAt: num,
|
|
23076
|
-
historyThrough:
|
|
23077
|
-
historyUsage:
|
|
23631
|
+
historyThrough: Type8.Optional(integer),
|
|
23632
|
+
historyUsage: Type8.Optional(usage)
|
|
23078
23633
|
});
|
|
23079
|
-
var jobStatus =
|
|
23080
|
-
var JobSummarySchema =
|
|
23634
|
+
var jobStatus = Type8.Enum(["running", "exited", "failed", "stopped", "lost"]);
|
|
23635
|
+
var JobSummarySchema = Type8.Object({
|
|
23081
23636
|
jobId: str,
|
|
23082
23637
|
title: str,
|
|
23083
23638
|
command: str,
|
|
23084
23639
|
cwd: str,
|
|
23085
23640
|
status: jobStatus,
|
|
23086
23641
|
startedAt: num,
|
|
23087
|
-
endedAt:
|
|
23088
|
-
exitCode:
|
|
23089
|
-
reason:
|
|
23090
|
-
timeoutSeconds:
|
|
23642
|
+
endedAt: Type8.Union([num, nil]),
|
|
23643
|
+
exitCode: Type8.Union([num, nil]),
|
|
23644
|
+
reason: Type8.Optional(Type8.Enum(["timeout", "spawn-error", "restart", "aborted"])),
|
|
23645
|
+
timeoutSeconds: Type8.Optional(num),
|
|
23091
23646
|
outputBytes: integer
|
|
23092
23647
|
});
|
|
23093
|
-
var JobOutputSchema =
|
|
23648
|
+
var JobOutputSchema = Type8.Object({
|
|
23094
23649
|
job: JobSummarySchema,
|
|
23095
23650
|
text: str,
|
|
23096
23651
|
offset: integer,
|
|
23097
23652
|
truncated: bool,
|
|
23098
23653
|
dropped: integer
|
|
23099
23654
|
});
|
|
23100
|
-
var QueuedInputSchema =
|
|
23655
|
+
var QueuedInputSchema = Type8.Object({
|
|
23101
23656
|
id: integer,
|
|
23102
|
-
mode:
|
|
23657
|
+
mode: Type8.Enum(["steer", "followUp"]),
|
|
23103
23658
|
preview: str,
|
|
23104
23659
|
images: integer,
|
|
23105
|
-
skill:
|
|
23660
|
+
skill: Type8.Optional(Type8.Object({ name: str, args: str }))
|
|
23106
23661
|
});
|
|
23107
|
-
var WithdrawResultSchema =
|
|
23108
|
-
result:
|
|
23109
|
-
text:
|
|
23110
|
-
images:
|
|
23662
|
+
var WithdrawResultSchema = Type8.Object({
|
|
23663
|
+
result: Type8.Enum(["withdrawn", "placed", "settled", "unknown"]),
|
|
23664
|
+
text: Type8.Optional(str),
|
|
23665
|
+
images: Type8.Optional(integer)
|
|
23111
23666
|
});
|
|
23112
|
-
var SteerResultSchema =
|
|
23113
|
-
result:
|
|
23114
|
-
id:
|
|
23115
|
-
text:
|
|
23116
|
-
images:
|
|
23667
|
+
var SteerResultSchema = Type8.Object({
|
|
23668
|
+
result: Type8.Enum(["steered", "withdrawn", "placed", "settled", "unknown"]),
|
|
23669
|
+
id: Type8.Optional(integer),
|
|
23670
|
+
text: Type8.Optional(str),
|
|
23671
|
+
images: Type8.Optional(integer)
|
|
23117
23672
|
});
|
|
23118
|
-
var TodoItemSchema =
|
|
23119
|
-
id:
|
|
23120
|
-
text:
|
|
23121
|
-
status:
|
|
23673
|
+
var TodoItemSchema = Type8.Object({
|
|
23674
|
+
id: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
|
|
23675
|
+
text: Type8.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
|
|
23676
|
+
status: Type8.Enum(["pending", "in_progress", "completed"])
|
|
23122
23677
|
}, { additionalProperties: false });
|
|
23123
|
-
var TodoListSchema =
|
|
23678
|
+
var TodoListSchema = Type8.Object({
|
|
23124
23679
|
revision: integer,
|
|
23125
|
-
items:
|
|
23680
|
+
items: Type8.Array(TodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
|
|
23126
23681
|
});
|
|
23127
|
-
var QuestionOptionSchema =
|
|
23128
|
-
id:
|
|
23129
|
-
label:
|
|
23130
|
-
description:
|
|
23131
|
-
recommended:
|
|
23682
|
+
var QuestionOptionSchema = Type8.Object({
|
|
23683
|
+
id: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
|
|
23684
|
+
label: Type8.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
|
|
23685
|
+
description: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
|
|
23686
|
+
recommended: Type8.Optional(bool)
|
|
23132
23687
|
}, { additionalProperties: false });
|
|
23133
|
-
var questionStatus =
|
|
23134
|
-
var QuestionSummarySchema =
|
|
23688
|
+
var questionStatus = Type8.Enum(["pending", "answered", "skipped", "cancelled"]);
|
|
23689
|
+
var QuestionSummarySchema = Type8.Object({
|
|
23135
23690
|
id: integer,
|
|
23136
23691
|
version: str,
|
|
23137
|
-
preview:
|
|
23692
|
+
preview: Type8.String({ maxLength: QUESTION_LIMITS.previewChars }),
|
|
23138
23693
|
multiple: bool,
|
|
23139
23694
|
optionCount: integer,
|
|
23140
23695
|
createdAt: num
|
|
23141
23696
|
});
|
|
23142
|
-
var WorkspaceEntrySchema =
|
|
23697
|
+
var WorkspaceEntrySchema = Type8.Object({
|
|
23143
23698
|
name: str,
|
|
23144
23699
|
path: str,
|
|
23145
|
-
kind:
|
|
23146
|
-
protected:
|
|
23700
|
+
kind: Type8.Enum(["file", "directory", "symlink"]),
|
|
23701
|
+
protected: Type8.Optional(Type8.Literal(true))
|
|
23147
23702
|
}, { additionalProperties: false });
|
|
23148
|
-
var WorkspaceDirSchema =
|
|
23703
|
+
var WorkspaceDirSchema = Type8.Object({
|
|
23149
23704
|
path: str,
|
|
23150
|
-
entries:
|
|
23705
|
+
entries: Type8.Array(WorkspaceEntrySchema, { maxItems: 1000 }),
|
|
23151
23706
|
limited: bool,
|
|
23152
23707
|
incomplete: bool
|
|
23153
23708
|
});
|
|
23154
|
-
var WorkspaceFileSchema =
|
|
23709
|
+
var WorkspaceFileSchema = Type8.Object({
|
|
23155
23710
|
path: str,
|
|
23156
23711
|
size: num,
|
|
23157
23712
|
mtimeMs: num,
|
|
23158
|
-
kind:
|
|
23159
|
-
content:
|
|
23713
|
+
kind: Type8.Enum(["text", "binary", "protected"]),
|
|
23714
|
+
content: Type8.Optional(str),
|
|
23160
23715
|
truncated: bool
|
|
23161
23716
|
});
|
|
23162
|
-
var ChangeSummarySchema =
|
|
23163
|
-
var ChangedFileSchema =
|
|
23717
|
+
var ChangeSummarySchema = Type8.Object({ revision: integer, files: integer, added: integer, removed: integer });
|
|
23718
|
+
var ChangedFileSchema = Type8.Object({
|
|
23164
23719
|
path: str,
|
|
23165
|
-
outside:
|
|
23720
|
+
outside: Type8.Optional(Type8.Literal(true)),
|
|
23166
23721
|
ops: integer,
|
|
23167
23722
|
added: integer,
|
|
23168
23723
|
removed: integer,
|
|
23169
|
-
agents:
|
|
23724
|
+
agents: Type8.Array(str, { maxItems: 64 }),
|
|
23170
23725
|
lastSeq: integer
|
|
23171
23726
|
}, { additionalProperties: false });
|
|
23172
|
-
var ChangeListSchema =
|
|
23727
|
+
var ChangeListSchema = Type8.Object({
|
|
23173
23728
|
revision: integer,
|
|
23174
|
-
files:
|
|
23729
|
+
files: Type8.Array(ChangedFileSchema, { maxItems: 500 }),
|
|
23175
23730
|
truncated: bool
|
|
23176
23731
|
});
|
|
23177
|
-
var ChangeOpSchema =
|
|
23732
|
+
var ChangeOpSchema = Type8.Object({
|
|
23178
23733
|
seq: integer,
|
|
23179
23734
|
agent: str,
|
|
23180
|
-
tool:
|
|
23735
|
+
tool: Type8.Enum(["edit", "write"]),
|
|
23181
23736
|
diff: str,
|
|
23182
23737
|
added: integer,
|
|
23183
23738
|
removed: integer,
|
|
23184
23739
|
truncated: bool
|
|
23185
23740
|
}, { additionalProperties: false });
|
|
23186
|
-
var ChangeDetailSchema =
|
|
23741
|
+
var ChangeDetailSchema = Type8.Object({
|
|
23187
23742
|
path: str,
|
|
23188
|
-
outside:
|
|
23189
|
-
ops:
|
|
23743
|
+
outside: Type8.Optional(Type8.Literal(true)),
|
|
23744
|
+
ops: Type8.Array(ChangeOpSchema, { maxItems: 50 }),
|
|
23190
23745
|
truncated: bool
|
|
23191
23746
|
});
|
|
23192
|
-
var QuestionListSchema =
|
|
23747
|
+
var QuestionListSchema = Type8.Object({
|
|
23193
23748
|
revision: integer,
|
|
23194
|
-
pending:
|
|
23749
|
+
pending: Type8.Array(QuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
|
|
23195
23750
|
});
|
|
23196
|
-
var QuestionRecordSchema =
|
|
23751
|
+
var QuestionRecordSchema = Type8.Object({
|
|
23197
23752
|
id: integer,
|
|
23198
23753
|
callId: str,
|
|
23199
23754
|
version: str,
|
|
23200
|
-
question:
|
|
23755
|
+
question: Type8.String({ maxLength: QUESTION_LIMITS.questionChars }),
|
|
23201
23756
|
multiple: bool,
|
|
23202
|
-
options:
|
|
23757
|
+
options: Type8.Array(QuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
|
|
23203
23758
|
status: questionStatus,
|
|
23204
|
-
answer:
|
|
23205
|
-
|
|
23206
|
-
optionIds:
|
|
23207
|
-
text:
|
|
23759
|
+
answer: Type8.Union([
|
|
23760
|
+
Type8.Object({
|
|
23761
|
+
optionIds: Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
|
|
23762
|
+
text: Type8.Union([Type8.String({ maxLength: QUESTION_LIMITS.textChars }), nil])
|
|
23208
23763
|
}),
|
|
23209
23764
|
nil
|
|
23210
23765
|
]),
|
|
23211
23766
|
createdAt: num,
|
|
23212
|
-
settledAt:
|
|
23767
|
+
settledAt: Type8.Union([num, nil])
|
|
23213
23768
|
});
|
|
23214
|
-
var QuestionResponseSchema =
|
|
23215
|
-
|
|
23216
|
-
kind:
|
|
23217
|
-
optionIds:
|
|
23218
|
-
text:
|
|
23769
|
+
var QuestionResponseSchema = Type8.Union([
|
|
23770
|
+
Type8.Object({
|
|
23771
|
+
kind: Type8.Literal("answer"),
|
|
23772
|
+
optionIds: Type8.Optional(Type8.Array(Type8.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
|
|
23773
|
+
text: Type8.Optional(Type8.String({ maxLength: QUESTION_LIMITS.textChars }))
|
|
23219
23774
|
}, { additionalProperties: false }),
|
|
23220
|
-
|
|
23775
|
+
Type8.Object({ kind: Type8.Literal("skip") }, { additionalProperties: false })
|
|
23776
|
+
]);
|
|
23777
|
+
var PlanRecordSchema = Type8.Object({
|
|
23778
|
+
version: str,
|
|
23779
|
+
title: Type8.String({ maxLength: PLAN_LIMITS.titleChars }),
|
|
23780
|
+
steps: Type8.Array(Type8.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
|
|
23781
|
+
note: Type8.String({ maxLength: PLAN_LIMITS.noteChars }),
|
|
23782
|
+
status: Type8.Enum(["awaiting_approval", "approved", "revision_requested", "superseded", "cancelled"]),
|
|
23783
|
+
feedback: Type8.Union([Type8.String({ maxLength: PLAN_LIMITS.feedbackChars }), nil]),
|
|
23784
|
+
createdAt: num,
|
|
23785
|
+
settledAt: Type8.Union([num, nil])
|
|
23786
|
+
});
|
|
23787
|
+
var PlanViewSchema = Type8.Object({
|
|
23788
|
+
mode: Type8.Enum(["normal", "plan"]),
|
|
23789
|
+
revision: integer,
|
|
23790
|
+
current: Type8.Union([PlanRecordSchema, nil])
|
|
23791
|
+
});
|
|
23792
|
+
var PlanDecisionSchema = Type8.Union([
|
|
23793
|
+
Type8.Object({ kind: Type8.Literal("approve") }, { additionalProperties: false }),
|
|
23794
|
+
Type8.Object({ kind: Type8.Literal("revise"), feedback: Type8.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
|
|
23221
23795
|
]);
|
|
23222
|
-
var ToolInteractionSchema =
|
|
23223
|
-
|
|
23224
|
-
kind:
|
|
23796
|
+
var ToolInteractionSchema = Type8.Union([
|
|
23797
|
+
Type8.Object({
|
|
23798
|
+
kind: Type8.Literal("ask_user"),
|
|
23225
23799
|
id: integer,
|
|
23226
23800
|
question: str,
|
|
23227
23801
|
multiple: bool,
|
|
23228
|
-
options:
|
|
23802
|
+
options: Type8.Array(Type8.Object({ id: str, label: str, recommended: Type8.Optional(bool) })),
|
|
23229
23803
|
status: questionStatus,
|
|
23230
|
-
optionIds:
|
|
23231
|
-
labels:
|
|
23232
|
-
text:
|
|
23804
|
+
optionIds: Type8.Optional(Type8.Array(str)),
|
|
23805
|
+
labels: Type8.Optional(Type8.Array(str)),
|
|
23806
|
+
text: Type8.Optional(Type8.Union([str, nil]))
|
|
23233
23807
|
}),
|
|
23234
|
-
|
|
23235
|
-
kind:
|
|
23236
|
-
action:
|
|
23808
|
+
Type8.Object({
|
|
23809
|
+
kind: Type8.Literal("todo"),
|
|
23810
|
+
action: Type8.Enum(["list", "replace"]),
|
|
23237
23811
|
revision: integer,
|
|
23238
23812
|
completed: integer,
|
|
23239
23813
|
total: integer
|
|
23814
|
+
}),
|
|
23815
|
+
Type8.Object({
|
|
23816
|
+
kind: Type8.Literal("submit_plan"),
|
|
23817
|
+
version: str,
|
|
23818
|
+
title: str,
|
|
23819
|
+
steps: Type8.Array(str),
|
|
23820
|
+
note: str
|
|
23240
23821
|
})
|
|
23241
23822
|
]);
|
|
23242
|
-
var ChatViewSchema =
|
|
23243
|
-
historyBefore:
|
|
23244
|
-
agents:
|
|
23245
|
-
jobs:
|
|
23246
|
-
queued:
|
|
23247
|
-
todos:
|
|
23248
|
-
questions:
|
|
23249
|
-
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
|
|
23823
|
+
var ChatViewSchema = Type8.Object({
|
|
23824
|
+
historyBefore: Type8.Optional(Type8.Union([integer, nil])),
|
|
23825
|
+
agents: Type8.Optional(Type8.Array(SubagentSummarySchema)),
|
|
23826
|
+
jobs: Type8.Optional(Type8.Array(JobSummarySchema)),
|
|
23827
|
+
queued: Type8.Optional(Type8.Array(QueuedInputSchema)),
|
|
23828
|
+
todos: Type8.Optional(TodoListSchema),
|
|
23829
|
+
questions: Type8.Optional(QuestionListSchema),
|
|
23830
|
+
plan: Type8.Optional(PlanViewSchema),
|
|
23831
|
+
changes: Type8.Optional(ChangeSummarySchema),
|
|
23832
|
+
rounds: Type8.Optional(Type8.Array(DelegationSchema)),
|
|
23833
|
+
subagents: Type8.Optional(Type8.Array(DelegationSchema)),
|
|
23834
|
+
taskUsage: Type8.Optional(usage),
|
|
23253
23835
|
model: str,
|
|
23254
|
-
agent:
|
|
23836
|
+
agent: Type8.Object({ model: Type8.Union([model2, nil]), thinkingLevel: Type8.Union([thinking, nil]) }),
|
|
23255
23837
|
cwd: str,
|
|
23256
23838
|
busy: bool,
|
|
23257
23839
|
status: nullableString,
|
|
23258
23840
|
usage,
|
|
23259
|
-
contextUsage:
|
|
23260
|
-
compaction:
|
|
23261
|
-
failure:
|
|
23262
|
-
active:
|
|
23841
|
+
contextUsage: Type8.Optional(Type8.Object({ tokens: Type8.Union([num, nil]), window: num, percent: Type8.Union([num, nil]) })),
|
|
23842
|
+
compaction: Type8.Optional(Type8.Object({
|
|
23843
|
+
failure: Type8.Optional(Type8.Object({ taskId: integer, reason: Type8.Enum(["manual", "threshold", "overflow"]), error: str })),
|
|
23844
|
+
active: Type8.Array(Type8.Object({
|
|
23263
23845
|
taskId: integer,
|
|
23264
|
-
reason:
|
|
23846
|
+
reason: Type8.Enum(["manual", "threshold", "overflow"]),
|
|
23265
23847
|
blocking: bool,
|
|
23266
23848
|
attempt: integer,
|
|
23267
|
-
retry:
|
|
23849
|
+
retry: Type8.Optional(Type8.Object({ at: num, error: str }))
|
|
23268
23850
|
})),
|
|
23269
|
-
latest:
|
|
23851
|
+
latest: Type8.Union([
|
|
23270
23852
|
nil,
|
|
23271
|
-
|
|
23853
|
+
Type8.Object({
|
|
23272
23854
|
taskId: integer,
|
|
23273
|
-
phase:
|
|
23855
|
+
phase: Type8.Enum(["summarizing", "waiting-to-apply", "applied", "nothing", "skipped", "cancelled", "failed"]),
|
|
23274
23856
|
at: num
|
|
23275
23857
|
})
|
|
23276
23858
|
])
|
|
23277
23859
|
})),
|
|
23278
|
-
items:
|
|
23279
|
-
|
|
23280
|
-
type:
|
|
23281
|
-
order:
|
|
23860
|
+
items: Type8.Array(Type8.Union([
|
|
23861
|
+
Type8.Object({
|
|
23862
|
+
type: Type8.Literal("user"),
|
|
23863
|
+
order: Type8.Optional(num),
|
|
23282
23864
|
key: str,
|
|
23283
23865
|
id: integer,
|
|
23284
23866
|
text: str,
|
|
23285
|
-
images:
|
|
23286
|
-
skill:
|
|
23287
|
-
defaultImagePrompt:
|
|
23867
|
+
images: Type8.Optional(Type8.Array(image)),
|
|
23868
|
+
skill: Type8.Optional(skill),
|
|
23869
|
+
defaultImagePrompt: Type8.Optional(Type8.Object({ version: Type8.Integer({ minimum: 1 }), text: str }))
|
|
23288
23870
|
}),
|
|
23289
|
-
|
|
23290
|
-
type:
|
|
23291
|
-
order:
|
|
23871
|
+
Type8.Object({
|
|
23872
|
+
type: Type8.Literal("assistant"),
|
|
23873
|
+
order: Type8.Optional(num),
|
|
23292
23874
|
key: str,
|
|
23293
23875
|
id: integer,
|
|
23294
23876
|
thinking: str,
|
|
@@ -23297,11 +23879,11 @@ var ChatViewSchema = Type7.Object({
|
|
|
23297
23879
|
errorMessage: nullableString,
|
|
23298
23880
|
note: nullableString
|
|
23299
23881
|
}),
|
|
23300
|
-
|
|
23301
|
-
type:
|
|
23302
|
-
order:
|
|
23882
|
+
Type8.Object({
|
|
23883
|
+
type: Type8.Literal("tool"),
|
|
23884
|
+
order: Type8.Optional(num),
|
|
23303
23885
|
key: str,
|
|
23304
|
-
images:
|
|
23886
|
+
images: Type8.Optional(Type8.Array(image)),
|
|
23305
23887
|
callId: str,
|
|
23306
23888
|
name: str,
|
|
23307
23889
|
summary: str,
|
|
@@ -23310,64 +23892,66 @@ var ChatViewSchema = Type7.Object({
|
|
|
23310
23892
|
isError: bool,
|
|
23311
23893
|
output: nullableString,
|
|
23312
23894
|
result: nullableString,
|
|
23313
|
-
durationMs:
|
|
23895
|
+
durationMs: Type8.Union([num, nil]),
|
|
23314
23896
|
note: nullableString,
|
|
23315
23897
|
diff: nullableString,
|
|
23316
|
-
diffAdded:
|
|
23317
|
-
diffRemoved:
|
|
23318
|
-
diffTruncated:
|
|
23319
|
-
interaction:
|
|
23898
|
+
diffAdded: Type8.Union([num, nil]),
|
|
23899
|
+
diffRemoved: Type8.Union([num, nil]),
|
|
23900
|
+
diffTruncated: Type8.Optional(bool),
|
|
23901
|
+
interaction: Type8.Optional(ToolInteractionSchema)
|
|
23320
23902
|
}),
|
|
23321
|
-
|
|
23322
|
-
type:
|
|
23323
|
-
order:
|
|
23903
|
+
Type8.Object({
|
|
23904
|
+
type: Type8.Literal("system"),
|
|
23905
|
+
order: Type8.Optional(num),
|
|
23324
23906
|
key: str,
|
|
23325
23907
|
id: integer,
|
|
23326
23908
|
text: str,
|
|
23327
|
-
jobId:
|
|
23909
|
+
jobId: Type8.Optional(str),
|
|
23910
|
+
planDecision: Type8.Optional(Type8.Object({ version: str, kind: Type8.Enum(["approve", "revise"]) }, { additionalProperties: false }))
|
|
23328
23911
|
})
|
|
23329
23912
|
])),
|
|
23330
|
-
streaming:
|
|
23913
|
+
streaming: Type8.Union([nil, Type8.Object({ thinking: str, text: str, toolCalls: Type8.Array(call) })])
|
|
23331
23914
|
});
|
|
23332
|
-
var SessionSummarySchema =
|
|
23915
|
+
var SessionSummarySchema = Type8.Object({
|
|
23333
23916
|
workspaceRoot: str,
|
|
23334
23917
|
lastUsedAt: num,
|
|
23335
23918
|
title: str,
|
|
23336
23919
|
firstPromptPreview: str,
|
|
23337
23920
|
createdAt: num,
|
|
23338
|
-
lastMessageAt:
|
|
23339
|
-
model:
|
|
23340
|
-
status:
|
|
23341
|
-
awaitingAnswer:
|
|
23921
|
+
lastMessageAt: Type8.Union([num, nil]),
|
|
23922
|
+
model: Type8.Union([model2, nil]),
|
|
23923
|
+
status: Type8.Enum(["idle", "running", "error"]),
|
|
23924
|
+
awaitingAnswer: Type8.Optional(bool),
|
|
23925
|
+
awaitingPlan: Type8.Optional(bool),
|
|
23342
23926
|
id: str
|
|
23343
23927
|
});
|
|
23344
|
-
var DirectoryStateSchema =
|
|
23345
|
-
var WorkspaceSummarySchema =
|
|
23346
|
-
var WorkspaceStateSchema =
|
|
23347
|
-
var credentialStatus =
|
|
23928
|
+
var DirectoryStateSchema = Type8.Object({ revision: integer, sessions: Type8.Array(SessionSummarySchema) });
|
|
23929
|
+
var WorkspaceSummarySchema = Type8.Object({ path: str, createdAt: num });
|
|
23930
|
+
var WorkspaceStateSchema = Type8.Object({ revision: integer, workspaces: Type8.Array(WorkspaceSummarySchema) });
|
|
23931
|
+
var credentialStatus = Type8.Object({
|
|
23348
23932
|
savedConfigured: bool,
|
|
23349
23933
|
environmentConfigured: bool,
|
|
23350
|
-
source:
|
|
23351
|
-
enabled:
|
|
23934
|
+
source: Type8.Enum(["saved", "environment", "runtime", "keyless", "none"]),
|
|
23935
|
+
enabled: Type8.Optional(bool)
|
|
23352
23936
|
});
|
|
23353
|
-
var CredentialStateSchema =
|
|
23937
|
+
var CredentialStateSchema = Type8.Object({
|
|
23354
23938
|
revision: integer,
|
|
23355
|
-
statuses:
|
|
23939
|
+
statuses: Type8.Record(str, credentialStatus)
|
|
23356
23940
|
});
|
|
23357
|
-
var AttachmentSchema =
|
|
23358
|
-
var chatInput =
|
|
23941
|
+
var AttachmentSchema = Type8.Object({ sessionId: str, attachmentId: str });
|
|
23942
|
+
var chatInput = Type8.Object({ sessionId: str, attachmentId: str }, { additionalProperties: false });
|
|
23359
23943
|
var NekoSessionDirectory = contract("neko.session-directory", {
|
|
23360
23944
|
version: 1,
|
|
23361
|
-
input:
|
|
23945
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23362
23946
|
variants: { ready: { state: { state: DirectoryStateSchema } } }
|
|
23363
23947
|
});
|
|
23364
23948
|
var NekoSessionManagement = contract("neko.session-management", {
|
|
23365
23949
|
version: 1,
|
|
23366
|
-
input:
|
|
23950
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23367
23951
|
variants: {
|
|
23368
23952
|
ready: {
|
|
23369
23953
|
methods: {
|
|
23370
|
-
create: rpc(
|
|
23954
|
+
create: rpc(Type8.Object({ ...change.properties, cwd: Type8.Optional(str) }, { additionalProperties: false }), SessionSummarySchema),
|
|
23371
23955
|
archive: rpc(str, nil),
|
|
23372
23956
|
attach: rpc(str, AttachmentSchema),
|
|
23373
23957
|
detach: rpc(nil, nil),
|
|
@@ -23376,31 +23960,31 @@ var NekoSessionManagement = contract("neko.session-management", {
|
|
|
23376
23960
|
}
|
|
23377
23961
|
}
|
|
23378
23962
|
});
|
|
23379
|
-
var resize =
|
|
23380
|
-
maxWidth:
|
|
23381
|
-
maxHeight:
|
|
23382
|
-
maxBytes:
|
|
23383
|
-
jpegQuality:
|
|
23963
|
+
var resize = Type8.Object({
|
|
23964
|
+
maxWidth: Type8.Optional(integer),
|
|
23965
|
+
maxHeight: Type8.Optional(integer),
|
|
23966
|
+
maxBytes: Type8.Optional(integer),
|
|
23967
|
+
jpegQuality: Type8.Optional(integer)
|
|
23384
23968
|
});
|
|
23385
|
-
var inputLimits =
|
|
23386
|
-
maxRequestBytes:
|
|
23387
|
-
images:
|
|
23388
|
-
resize:
|
|
23389
|
-
maxPerMessage:
|
|
23390
|
-
maxPerRequest:
|
|
23969
|
+
var inputLimits = Type8.Object({
|
|
23970
|
+
maxRequestBytes: Type8.Optional(integer),
|
|
23971
|
+
images: Type8.Optional(Type8.Object({
|
|
23972
|
+
resize: Type8.Optional(resize),
|
|
23973
|
+
maxPerMessage: Type8.Optional(integer),
|
|
23974
|
+
maxPerRequest: Type8.Optional(integer)
|
|
23391
23975
|
}))
|
|
23392
23976
|
});
|
|
23393
|
-
var modelInfo =
|
|
23977
|
+
var modelInfo = Type8.Object({
|
|
23394
23978
|
provider: str,
|
|
23395
23979
|
modelId: str,
|
|
23396
23980
|
name: str,
|
|
23397
23981
|
available: bool,
|
|
23398
23982
|
contextWindow: num,
|
|
23399
|
-
input:
|
|
23400
|
-
inputLimits:
|
|
23983
|
+
input: Type8.Array(str),
|
|
23984
|
+
inputLimits: Type8.Optional(inputLimits),
|
|
23401
23985
|
reasoning: bool,
|
|
23402
|
-
supportedThinkingLevels:
|
|
23403
|
-
thinkingLevelAdjustments:
|
|
23986
|
+
supportedThinkingLevels: Type8.Array(thinking),
|
|
23987
|
+
thinkingLevelAdjustments: Type8.Object({
|
|
23404
23988
|
off: thinking,
|
|
23405
23989
|
minimal: thinking,
|
|
23406
23990
|
low: thinking,
|
|
@@ -23412,31 +23996,31 @@ var modelInfo = Type7.Object({
|
|
|
23412
23996
|
});
|
|
23413
23997
|
var NekoModels = contract("neko.models", {
|
|
23414
23998
|
version: 1,
|
|
23415
|
-
input:
|
|
23416
|
-
variants: { ready: { methods: { list: rpc(nil,
|
|
23999
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
24000
|
+
variants: { ready: { methods: { list: rpc(nil, Type8.Array(modelInfo)) } } }
|
|
23417
24001
|
});
|
|
23418
|
-
var imageChunk =
|
|
24002
|
+
var imageChunk = Type8.Object({ data: str, total: integer, mimeType: str });
|
|
23419
24003
|
var NekoSubagent = contract("neko.subagent", {
|
|
23420
24004
|
version: 1,
|
|
23421
|
-
input:
|
|
24005
|
+
input: Type8.Object({
|
|
23422
24006
|
sessionId: str,
|
|
23423
24007
|
attachmentId: str,
|
|
23424
|
-
taskId:
|
|
23425
|
-
agentId:
|
|
24008
|
+
taskId: Type8.Optional(integer),
|
|
24009
|
+
agentId: Type8.Optional(str)
|
|
23426
24010
|
}),
|
|
23427
24011
|
variants: {
|
|
23428
24012
|
ready: {
|
|
23429
24013
|
state: { revision: integer },
|
|
23430
24014
|
methods: {
|
|
23431
|
-
chunk: rpc(
|
|
23432
|
-
kind:
|
|
23433
|
-
before:
|
|
23434
|
-
token:
|
|
24015
|
+
chunk: rpc(Type8.Object({
|
|
24016
|
+
kind: Type8.Enum(["view", "history"]),
|
|
24017
|
+
before: Type8.Optional(integer),
|
|
24018
|
+
token: Type8.Optional(integer),
|
|
23435
24019
|
offset: integer
|
|
23436
|
-
}),
|
|
23437
|
-
getImage: rpc(
|
|
23438
|
-
decideApproval: rpc(
|
|
23439
|
-
stop: rpc(
|
|
24020
|
+
}), Type8.Object({ token: integer, data: str, next: Type8.Union([integer, nil]) })),
|
|
24021
|
+
getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }), imageChunk),
|
|
24022
|
+
decideApproval: rpc(Type8.Object({ id: integer, version: str, decision: Type8.Enum(["allowed", "denied"]) }), Type8.Enum(["pending", "allowed", "denied", "cancelled"])),
|
|
24023
|
+
stop: rpc(Type8.Union([nil, Type8.Object({ taskId: integer })]), nil)
|
|
23440
24024
|
}
|
|
23441
24025
|
}
|
|
23442
24026
|
}
|
|
@@ -23448,99 +24032,102 @@ var NekoChat = contract("neko.chat", {
|
|
|
23448
24032
|
ready: {
|
|
23449
24033
|
state: { view: ChatViewSchema },
|
|
23450
24034
|
methods: {
|
|
23451
|
-
submit: rpc(
|
|
24035
|
+
submit: rpc(Type8.Object({
|
|
23452
24036
|
content: str,
|
|
23453
|
-
images:
|
|
23454
|
-
requestId:
|
|
23455
|
-
whenBusy:
|
|
24037
|
+
images: Type8.Optional(Type8.Array(str, { maxItems: 4 })),
|
|
24038
|
+
requestId: Type8.Optional(str),
|
|
24039
|
+
whenBusy: Type8.Optional(Type8.Enum(["steer", "followUp"]))
|
|
23456
24040
|
}, { additionalProperties: false }), nil),
|
|
23457
24041
|
abort: rpc(nil, nil),
|
|
23458
|
-
withdraw: rpc(
|
|
23459
|
-
steer: rpc(
|
|
23460
|
-
questionGet: rpc(
|
|
23461
|
-
questionRespond: rpc(
|
|
24042
|
+
withdraw: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), WithdrawResultSchema),
|
|
24043
|
+
steer: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), SteerResultSchema),
|
|
24044
|
+
questionGet: rpc(Type8.Object({ id: integer }, { additionalProperties: false }), Type8.Union([QuestionRecordSchema, nil])),
|
|
24045
|
+
questionRespond: rpc(Type8.Object({ id: integer, version: str, response: QuestionResponseSchema }, { additionalProperties: false }), QuestionRecordSchema),
|
|
24046
|
+
planGet: rpc(Type8.Object({ version: str }, { additionalProperties: false }), Type8.Union([PlanRecordSchema, nil])),
|
|
24047
|
+
planRespond: rpc(Type8.Object({ version: str, decision: PlanDecisionSchema }, { additionalProperties: false }), PlanRecordSchema),
|
|
24048
|
+
planMode: rpc(Type8.Object({ mode: Type8.Enum(["normal", "plan"]) }, { additionalProperties: false }), PlanViewSchema),
|
|
23462
24049
|
usage: rpc(nil, usage),
|
|
23463
|
-
workspaceDir: rpc(
|
|
23464
|
-
workspaceFile: rpc(
|
|
24050
|
+
workspaceDir: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceDirSchema),
|
|
24051
|
+
workspaceFile: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceFileSchema),
|
|
23465
24052
|
changesList: rpc(nil, ChangeListSchema),
|
|
23466
|
-
changeGet: rpc(
|
|
23467
|
-
skills: rpc(nil,
|
|
23468
|
-
jobOutput: rpc(
|
|
23469
|
-
jobStop: rpc(
|
|
24053
|
+
changeGet: rpc(Type8.Object({ path: str }, { additionalProperties: false }), Type8.Union([ChangeDetailSchema, nil])),
|
|
24054
|
+
skills: rpc(nil, Type8.Array(Type8.Object({ name: str, description: str, location: str, level }))),
|
|
24055
|
+
jobOutput: rpc(Type8.Object({ jobId: str, since: Type8.Optional(integer) }, { additionalProperties: false }), JobOutputSchema),
|
|
24056
|
+
jobStop: rpc(Type8.Object({ jobId: str }, { additionalProperties: false }), JobSummarySchema),
|
|
23470
24057
|
configure: rpc(change, nil),
|
|
23471
|
-
compact: rpc(
|
|
23472
|
-
importImage: rpc(str,
|
|
23473
|
-
draftImage: rpc(
|
|
23474
|
-
beginImage: rpc(
|
|
23475
|
-
appendImage: rpc(
|
|
24058
|
+
compact: rpc(Type8.Object({ instructions: Type8.Optional(str) }, { additionalProperties: false }), nil),
|
|
24059
|
+
importImage: rpc(str, Type8.Object({ id: str, name: str })),
|
|
24060
|
+
draftImage: rpc(Type8.Object({ id: str, offset: integer }, { additionalProperties: false }), imageChunk),
|
|
24061
|
+
beginImage: rpc(Type8.Object({ name: str, mimeType: str, size: Type8.Integer({ minimum: 1, maximum: 20971520 }) }, { additionalProperties: false }), str),
|
|
24062
|
+
appendImage: rpc(Type8.Object({ id: str, offset: integer, data: Type8.String({ maxLength: 350000 }) }, { additionalProperties: false }), nil),
|
|
23476
24063
|
finishImage: rpc(str, nil),
|
|
23477
24064
|
releaseImage: rpc(str, nil),
|
|
23478
|
-
getImage: rpc(
|
|
24065
|
+
getImage: rpc(Type8.Object({ entryId: integer, contentIndex: integer, offset: integer }, { additionalProperties: false }), imageChunk)
|
|
23479
24066
|
}
|
|
23480
24067
|
}
|
|
23481
24068
|
}
|
|
23482
24069
|
});
|
|
23483
|
-
var browse =
|
|
23484
|
-
var page =
|
|
24070
|
+
var browse = Type8.Object({ path: str, query: Type8.Optional(str), showHidden: Type8.Optional(bool), cursor: Type8.Optional(str) }, { additionalProperties: false });
|
|
24071
|
+
var page = Type8.Object({
|
|
23485
24072
|
path: str,
|
|
23486
24073
|
parent: nullableString,
|
|
23487
|
-
entries:
|
|
24074
|
+
entries: Type8.Array(Type8.Object({ name: str, path: str, error: Type8.Optional(str) })),
|
|
23488
24075
|
next: nullableString
|
|
23489
24076
|
});
|
|
23490
24077
|
var NekoWorkspaces = contract("neko.workspaces", {
|
|
23491
24078
|
version: 1,
|
|
23492
|
-
input:
|
|
24079
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23493
24080
|
variants: {
|
|
23494
24081
|
ready: {
|
|
23495
24082
|
state: { state: WorkspaceStateSchema },
|
|
23496
24083
|
methods: {
|
|
23497
|
-
add: rpc(
|
|
24084
|
+
add: rpc(Type8.Object({ path: str }, { additionalProperties: false }), WorkspaceSummarySchema),
|
|
23498
24085
|
browse: rpc(browse, page)
|
|
23499
24086
|
}
|
|
23500
24087
|
}
|
|
23501
24088
|
}
|
|
23502
24089
|
});
|
|
23503
|
-
var provider =
|
|
24090
|
+
var provider = Type8.String({ minLength: 1, maxLength: 64 });
|
|
23504
24091
|
var NekoCredentials = contract("neko.credentials", {
|
|
23505
24092
|
version: 2,
|
|
23506
|
-
input:
|
|
24093
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23507
24094
|
variants: {
|
|
23508
24095
|
ready: {
|
|
23509
24096
|
state: { state: CredentialStateSchema },
|
|
23510
24097
|
methods: {
|
|
23511
|
-
save: rpc(
|
|
23512
|
-
remove: rpc(
|
|
23513
|
-
setEnabled: rpc(
|
|
24098
|
+
save: rpc(Type8.Object({ provider, key: Type8.String({ minLength: 1, maxLength: 8192 }) }, { additionalProperties: false }), nil),
|
|
24099
|
+
remove: rpc(Type8.Object({ provider }, { additionalProperties: false }), nil),
|
|
24100
|
+
setEnabled: rpc(Type8.Object({ provider, enabled: bool }, { additionalProperties: false }), nil)
|
|
23514
24101
|
}
|
|
23515
24102
|
}
|
|
23516
24103
|
}
|
|
23517
24104
|
});
|
|
23518
|
-
var SearchSettingsStateSchema =
|
|
24105
|
+
var SearchSettingsStateSchema = Type8.Object({
|
|
23519
24106
|
revision: integer,
|
|
23520
|
-
selected:
|
|
24107
|
+
selected: Type8.Enum(["tavily", "deepseek"])
|
|
23521
24108
|
});
|
|
23522
24109
|
var NekoSearchSettings = contract("neko.search-settings", {
|
|
23523
24110
|
version: 1,
|
|
23524
|
-
input:
|
|
24111
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23525
24112
|
variants: {
|
|
23526
24113
|
ready: {
|
|
23527
24114
|
state: { state: SearchSettingsStateSchema },
|
|
23528
24115
|
methods: {
|
|
23529
|
-
set: rpc(
|
|
24116
|
+
set: rpc(Type8.Object({ provider: Type8.Enum(["tavily", "deepseek"]) }, { additionalProperties: false }), nil)
|
|
23530
24117
|
}
|
|
23531
24118
|
}
|
|
23532
24119
|
}
|
|
23533
24120
|
});
|
|
23534
|
-
var contextSource =
|
|
23535
|
-
var ContextSettingsStateSchema =
|
|
24121
|
+
var contextSource = Type8.Enum(["default", "config", "saved"]);
|
|
24122
|
+
var ContextSettingsStateSchema = Type8.Object({
|
|
23536
24123
|
revision: integer,
|
|
23537
|
-
effective:
|
|
23538
|
-
compaction:
|
|
24124
|
+
effective: Type8.Object({
|
|
24125
|
+
compaction: Type8.Object({
|
|
23539
24126
|
enabled: bool,
|
|
23540
|
-
triggerPercent:
|
|
24127
|
+
triggerPercent: Type8.Union([integer, nil]),
|
|
23541
24128
|
keepRecentTokens: integer
|
|
23542
24129
|
}),
|
|
23543
|
-
pruning:
|
|
24130
|
+
pruning: Type8.Object({
|
|
23544
24131
|
enabled: bool,
|
|
23545
24132
|
triggerPercent: integer,
|
|
23546
24133
|
keepRecent: integer,
|
|
@@ -23548,9 +24135,9 @@ var ContextSettingsStateSchema = Type7.Object({
|
|
|
23548
24135
|
minSavePercent: integer
|
|
23549
24136
|
})
|
|
23550
24137
|
}),
|
|
23551
|
-
sources:
|
|
23552
|
-
compaction:
|
|
23553
|
-
pruning:
|
|
24138
|
+
sources: Type8.Object({
|
|
24139
|
+
compaction: Type8.Object({ enabled: contextSource, triggerPercent: contextSource, keepRecentTokens: contextSource }),
|
|
24140
|
+
pruning: Type8.Object({
|
|
23554
24141
|
enabled: contextSource,
|
|
23555
24142
|
triggerPercent: contextSource,
|
|
23556
24143
|
keepRecent: contextSource,
|
|
@@ -23559,42 +24146,42 @@ var ContextSettingsStateSchema = Type7.Object({
|
|
|
23559
24146
|
})
|
|
23560
24147
|
})
|
|
23561
24148
|
});
|
|
23562
|
-
var nullable = (schema) =>
|
|
23563
|
-
var ContextSettingsPatchSchema =
|
|
23564
|
-
compaction:
|
|
23565
|
-
enabled:
|
|
23566
|
-
triggerPercent:
|
|
23567
|
-
keepRecentTokens:
|
|
24149
|
+
var nullable = (schema) => Type8.Union([schema, nil]);
|
|
24150
|
+
var ContextSettingsPatchSchema = Type8.Object({
|
|
24151
|
+
compaction: Type8.Optional(Type8.Object({
|
|
24152
|
+
enabled: Type8.Optional(nullable(bool)),
|
|
24153
|
+
triggerPercent: Type8.Optional(nullable(integer)),
|
|
24154
|
+
keepRecentTokens: Type8.Optional(nullable(integer))
|
|
23568
24155
|
}, { additionalProperties: false })),
|
|
23569
|
-
pruning:
|
|
23570
|
-
enabled:
|
|
23571
|
-
triggerPercent:
|
|
23572
|
-
keepRecent:
|
|
23573
|
-
minChars:
|
|
23574
|
-
minSavePercent:
|
|
24156
|
+
pruning: Type8.Optional(Type8.Object({
|
|
24157
|
+
enabled: Type8.Optional(nullable(bool)),
|
|
24158
|
+
triggerPercent: Type8.Optional(nullable(integer)),
|
|
24159
|
+
keepRecent: Type8.Optional(nullable(integer)),
|
|
24160
|
+
minChars: Type8.Optional(nullable(integer)),
|
|
24161
|
+
minSavePercent: Type8.Optional(nullable(integer))
|
|
23575
24162
|
}, { additionalProperties: false }))
|
|
23576
24163
|
}, { additionalProperties: false });
|
|
23577
24164
|
var NekoContextSettings = contract("neko.context-settings", {
|
|
23578
24165
|
version: 1,
|
|
23579
|
-
input:
|
|
24166
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23580
24167
|
variants: {
|
|
23581
24168
|
ready: {
|
|
23582
24169
|
state: { state: ContextSettingsStateSchema },
|
|
23583
24170
|
methods: {
|
|
23584
24171
|
set: rpc(ContextSettingsPatchSchema, nil),
|
|
23585
|
-
reset: rpc(
|
|
24172
|
+
reset: rpc(Type8.Object({}, { additionalProperties: false }), nil)
|
|
23586
24173
|
}
|
|
23587
24174
|
}
|
|
23588
24175
|
}
|
|
23589
24176
|
});
|
|
23590
|
-
var defaultModelSource =
|
|
23591
|
-
var DefaultModelStateSchema =
|
|
24177
|
+
var defaultModelSource = Type8.Enum(["cli", "saved", "config", "default"]);
|
|
24178
|
+
var DefaultModelStateSchema = Type8.Object({
|
|
23592
24179
|
effective: model2,
|
|
23593
24180
|
source: defaultModelSource
|
|
23594
24181
|
});
|
|
23595
24182
|
var NekoDefaultModel = contract("neko.default-model", {
|
|
23596
24183
|
version: 1,
|
|
23597
|
-
input:
|
|
24184
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23598
24185
|
variants: {
|
|
23599
24186
|
ready: {
|
|
23600
24187
|
state: { state: DefaultModelStateSchema },
|
|
@@ -23605,23 +24192,23 @@ var NekoDefaultModel = contract("neko.default-model", {
|
|
|
23605
24192
|
}
|
|
23606
24193
|
}
|
|
23607
24194
|
});
|
|
23608
|
-
var FileCandidatesSchema =
|
|
23609
|
-
entries:
|
|
24195
|
+
var FileCandidatesSchema = Type8.Object({
|
|
24196
|
+
entries: Type8.Array(Type8.Object({ name: str, path: str, kind: Type8.Enum(["file", "directory"]) }), { maxItems: 100 }),
|
|
23610
24197
|
limited: bool,
|
|
23611
24198
|
incomplete: bool
|
|
23612
24199
|
});
|
|
23613
24200
|
var NekoWorkspaceFiles = contract("neko.workspace-files", {
|
|
23614
24201
|
version: 1,
|
|
23615
|
-
input:
|
|
24202
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23616
24203
|
variants: {
|
|
23617
24204
|
ready: {
|
|
23618
24205
|
methods: {
|
|
23619
|
-
search: rpc(
|
|
24206
|
+
search: rpc(Type8.Object({ workspaceRoot: str, query: str }, { additionalProperties: false }), FileCandidatesSchema)
|
|
23620
24207
|
}
|
|
23621
24208
|
}
|
|
23622
24209
|
}
|
|
23623
24210
|
});
|
|
23624
|
-
var marketSkill =
|
|
24211
|
+
var marketSkill = Type8.Object({
|
|
23625
24212
|
name: str,
|
|
23626
24213
|
description: str,
|
|
23627
24214
|
category: str,
|
|
@@ -23631,48 +24218,48 @@ var marketSkill = Type7.Object({
|
|
|
23631
24218
|
sha256: str,
|
|
23632
24219
|
sizeBytes: integer,
|
|
23633
24220
|
uploadedAt: str,
|
|
23634
|
-
installed:
|
|
23635
|
-
activeLevel:
|
|
24221
|
+
installed: Type8.Object({ project: bool, user: bool, builtin: bool }),
|
|
24222
|
+
activeLevel: Type8.Union([level, nil])
|
|
23636
24223
|
});
|
|
23637
|
-
var preview =
|
|
23638
|
-
var scope =
|
|
23639
|
-
var installResult =
|
|
24224
|
+
var preview = Type8.Object({ body: str, files: Type8.Array(str) });
|
|
24225
|
+
var scope = Type8.Enum(["user", "project"]);
|
|
24226
|
+
var installResult = Type8.Object({ name: str, scope, path: str, shadowedBy: Type8.Optional(scope) });
|
|
23640
24227
|
var NekoSkillMarket = contract("neko.skill-market", {
|
|
23641
24228
|
version: 1,
|
|
23642
|
-
input:
|
|
24229
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
23643
24230
|
variants: {
|
|
23644
24231
|
ready: {
|
|
23645
24232
|
methods: {
|
|
23646
|
-
list: rpc(
|
|
23647
|
-
detail: rpc(
|
|
23648
|
-
preview: rpc(
|
|
23649
|
-
install: rpc(
|
|
23650
|
-
uninstall: rpc(
|
|
24233
|
+
list: rpc(Type8.Object({ query: Type8.Optional(str), workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), Type8.Array(marketSkill)),
|
|
24234
|
+
detail: rpc(Type8.Object({ name: str, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), marketSkill),
|
|
24235
|
+
preview: rpc(Type8.Object({ name: str }, { additionalProperties: false }), preview),
|
|
24236
|
+
install: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str), overwrite: Type8.Optional(bool) }, { additionalProperties: false }), installResult),
|
|
24237
|
+
uninstall: rpc(Type8.Object({ name: str, scope, workspaceRoot: Type8.Optional(str) }, { additionalProperties: false }), nil)
|
|
23651
24238
|
}
|
|
23652
24239
|
}
|
|
23653
24240
|
}
|
|
23654
24241
|
});
|
|
23655
|
-
var ShellStatusSchema =
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
status:
|
|
23659
|
-
reason:
|
|
23660
|
-
detail:
|
|
24242
|
+
var ShellStatusSchema = Type8.Union([
|
|
24243
|
+
Type8.Object({ status: Type8.Literal("ready"), path: str, source: Type8.Enum(["config", "auto"]) }),
|
|
24244
|
+
Type8.Object({
|
|
24245
|
+
status: Type8.Literal("missing"),
|
|
24246
|
+
reason: Type8.Enum(["not_found", "invalid_config"]),
|
|
24247
|
+
detail: Type8.Optional(str)
|
|
23661
24248
|
})
|
|
23662
24249
|
]);
|
|
23663
|
-
var ApplicationInfoSchema =
|
|
24250
|
+
var ApplicationInfoSchema = Type8.Object({
|
|
23664
24251
|
runningVersion: str,
|
|
23665
24252
|
installedVersion: nullableString,
|
|
23666
|
-
protocolVersion:
|
|
24253
|
+
protocolVersion: Type8.Integer({ minimum: 1 }),
|
|
23667
24254
|
latestVersion: nullableString,
|
|
23668
|
-
checkedAt:
|
|
23669
|
-
checkState:
|
|
24255
|
+
checkedAt: Type8.Union([integer, nil]),
|
|
24256
|
+
checkState: Type8.Enum(["unknown", "checking", "ok", "failed", "development"]),
|
|
23670
24257
|
serverId: str,
|
|
23671
|
-
shell:
|
|
24258
|
+
shell: Type8.Optional(ShellStatusSchema)
|
|
23672
24259
|
});
|
|
23673
24260
|
var NekoApplicationInfo = contract("neko.application-info", {
|
|
23674
24261
|
version: 1,
|
|
23675
|
-
input:
|
|
24262
|
+
input: Type8.Object({}),
|
|
23676
24263
|
variants: {
|
|
23677
24264
|
ready: { state: { info: ApplicationInfoSchema }, methods: { check: rpc(nil, ApplicationInfoSchema) } }
|
|
23678
24265
|
}
|
|
@@ -23686,7 +24273,7 @@ function isServerId(value) {
|
|
|
23686
24273
|
// packages/gateway/src/mobile/admin.ts
|
|
23687
24274
|
import { createHash, timingSafeEqual } from "crypto";
|
|
23688
24275
|
import { ChordError } from "@earendil-works/chord";
|
|
23689
|
-
import { Type as
|
|
24276
|
+
import { Type as Type9 } from "typebox";
|
|
23690
24277
|
|
|
23691
24278
|
// packages/gateway/src/principal.ts
|
|
23692
24279
|
var HOST_PRINCIPAL = { kind: "host" };
|
|
@@ -24015,62 +24602,62 @@ class MobileStore {
|
|
|
24015
24602
|
}
|
|
24016
24603
|
|
|
24017
24604
|
// packages/gateway/src/mobile/admin.ts
|
|
24018
|
-
var listenerStateSchema =
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24022
|
-
status:
|
|
24023
|
-
address:
|
|
24024
|
-
port:
|
|
24025
|
-
bindAddress:
|
|
24026
|
-
mode:
|
|
24605
|
+
var listenerStateSchema = Type9.Union([
|
|
24606
|
+
Type9.Object({ status: Type9.Literal("disabled") }),
|
|
24607
|
+
Type9.Object({ status: Type9.Literal("starting") }),
|
|
24608
|
+
Type9.Object({
|
|
24609
|
+
status: Type9.Literal("listening"),
|
|
24610
|
+
address: Type9.String(),
|
|
24611
|
+
port: Type9.Integer({ minimum: 1, maximum: 65535 }),
|
|
24612
|
+
bindAddress: Type9.Optional(Type9.String()),
|
|
24613
|
+
mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
|
|
24027
24614
|
}),
|
|
24028
|
-
|
|
24029
|
-
|
|
24615
|
+
Type9.Object({ status: Type9.Literal("error"), error: Type9.String() }),
|
|
24616
|
+
Type9.Object({ status: Type9.Literal("stopping") })
|
|
24030
24617
|
]);
|
|
24031
|
-
var adminDeviceSchema =
|
|
24032
|
-
deviceId:
|
|
24033
|
-
name:
|
|
24034
|
-
pairedAt:
|
|
24035
|
-
lastSeenAt:
|
|
24036
|
-
online:
|
|
24618
|
+
var adminDeviceSchema = Type9.Object({
|
|
24619
|
+
deviceId: Type9.String(),
|
|
24620
|
+
name: Type9.String(),
|
|
24621
|
+
pairedAt: Type9.Integer(),
|
|
24622
|
+
lastSeenAt: Type9.Integer(),
|
|
24623
|
+
online: Type9.Boolean()
|
|
24037
24624
|
});
|
|
24038
24625
|
var NekoMobileAdmin = contract("neko.mobile-admin", {
|
|
24039
24626
|
version: 1,
|
|
24040
|
-
input:
|
|
24627
|
+
input: Type9.Object({ token: Type9.String() }),
|
|
24041
24628
|
variants: {
|
|
24042
24629
|
admin: {
|
|
24043
24630
|
methods: {
|
|
24044
|
-
state: rpc(
|
|
24631
|
+
state: rpc(Type9.Null(), Type9.Object({
|
|
24045
24632
|
state: listenerStateSchema,
|
|
24046
|
-
candidates:
|
|
24047
|
-
port:
|
|
24048
|
-
certificate:
|
|
24049
|
-
|
|
24050
|
-
|
|
24633
|
+
candidates: Type9.Array(Type9.String()),
|
|
24634
|
+
port: Type9.Integer({ minimum: 1, maximum: 65535 }),
|
|
24635
|
+
certificate: Type9.Union([
|
|
24636
|
+
Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }),
|
|
24637
|
+
Type9.Null()
|
|
24051
24638
|
]),
|
|
24052
|
-
devices:
|
|
24053
|
-
lastConnectedAddress:
|
|
24054
|
-
wsl:
|
|
24055
|
-
|
|
24056
|
-
|
|
24639
|
+
devices: Type9.Array(adminDeviceSchema),
|
|
24640
|
+
lastConnectedAddress: Type9.Union([Type9.String(), Type9.Null()]),
|
|
24641
|
+
wsl: Type9.Optional(Type9.Union([
|
|
24642
|
+
Type9.Object({ interop: Type9.Boolean(), error: Type9.Union([Type9.String(), Type9.Null()]) }),
|
|
24643
|
+
Type9.Null()
|
|
24057
24644
|
]))
|
|
24058
24645
|
})),
|
|
24059
|
-
enable: rpc(
|
|
24060
|
-
address:
|
|
24061
|
-
port:
|
|
24062
|
-
}),
|
|
24063
|
-
status:
|
|
24064
|
-
address:
|
|
24065
|
-
port:
|
|
24066
|
-
bindAddress:
|
|
24067
|
-
mode:
|
|
24646
|
+
enable: rpc(Type9.Object({
|
|
24647
|
+
address: Type9.String(),
|
|
24648
|
+
port: Type9.Optional(Type9.Integer({ minimum: 1, maximum: 65535 }))
|
|
24649
|
+
}), Type9.Object({
|
|
24650
|
+
status: Type9.Literal("listening"),
|
|
24651
|
+
address: Type9.String(),
|
|
24652
|
+
port: Type9.Integer({ minimum: 1, maximum: 65535 }),
|
|
24653
|
+
bindAddress: Type9.Optional(Type9.String()),
|
|
24654
|
+
mode: Type9.Optional(Type9.Union([Type9.Literal("direct"), Type9.Literal("wsl-relay")]))
|
|
24068
24655
|
})),
|
|
24069
|
-
disable: rpc(
|
|
24070
|
-
createPairing: rpc(
|
|
24071
|
-
cancelPairing: rpc(
|
|
24072
|
-
revoke: rpc(
|
|
24073
|
-
regenerateCertificate: rpc(
|
|
24656
|
+
disable: rpc(Type9.Null(), listenerStateSchema),
|
|
24657
|
+
createPairing: rpc(Type9.Null(), Type9.Object({ pairingText: Type9.String(), expiresAt: Type9.Integer() })),
|
|
24658
|
+
cancelPairing: rpc(Type9.Null(), Type9.Null()),
|
|
24659
|
+
revoke: rpc(Type9.Object({ deviceId: Type9.String({ minLength: 1 }) }), Type9.Object({ revoked: Type9.Boolean() })),
|
|
24660
|
+
regenerateCertificate: rpc(Type9.Null(), Type9.Object({ fingerprint: Type9.String(), expiresAt: Type9.Integer() }))
|
|
24074
24661
|
}
|
|
24075
24662
|
}
|
|
24076
24663
|
}
|
|
@@ -24138,7 +24725,7 @@ function createMobileAdminFactory(deps) {
|
|
|
24138
24725
|
}
|
|
24139
24726
|
|
|
24140
24727
|
// packages/gateway/src/mobile/listener.ts
|
|
24141
|
-
import { randomUUID as
|
|
24728
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
24142
24729
|
import { createServer } from "https";
|
|
24143
24730
|
import { hostname } from "os";
|
|
24144
24731
|
import { memoryLinkPair } from "@earendil-works/chord";
|
|
@@ -24393,14 +24980,14 @@ function subagentClient(connect) {
|
|
|
24393
24980
|
import { createHash as createHash2 } from "crypto";
|
|
24394
24981
|
|
|
24395
24982
|
// packages/core/src/application-version.ts
|
|
24396
|
-
var RUNNING_VERSION = "0.4.
|
|
24983
|
+
var RUNNING_VERSION = "0.4.9";
|
|
24397
24984
|
var APPLICATION_PROTOCOL = 8;
|
|
24398
24985
|
function stableVersion(value) {
|
|
24399
24986
|
return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
|
|
24400
24987
|
}
|
|
24401
24988
|
|
|
24402
24989
|
// packages/gateway/src/mobile/protocol.ts
|
|
24403
|
-
import { Type as
|
|
24990
|
+
import { Type as Type10 } from "typebox";
|
|
24404
24991
|
import { Check } from "typebox/value";
|
|
24405
24992
|
var MOBILE_LIMITS = {
|
|
24406
24993
|
maxFrameBytes: 262144,
|
|
@@ -24419,17 +25006,18 @@ var MOBILE_CAPABILITIES = [
|
|
|
24419
25006
|
"approvals",
|
|
24420
25007
|
"media",
|
|
24421
25008
|
"todos",
|
|
24422
|
-
"questions"
|
|
25009
|
+
"questions",
|
|
25010
|
+
"plan"
|
|
24423
25011
|
];
|
|
24424
25012
|
var MOBILE_CLOSE_REVOKED = 4401;
|
|
24425
25013
|
var MOBILE_CLOSE_INCOMPATIBLE = 4402;
|
|
24426
25014
|
var MOBILE_CLOSE_SLOW_CONSUMER = 4408;
|
|
24427
|
-
var MobileHelloParams =
|
|
24428
|
-
apiVersions:
|
|
24429
|
-
app:
|
|
24430
|
-
version:
|
|
24431
|
-
platform:
|
|
24432
|
-
build:
|
|
25015
|
+
var MobileHelloParams = Type10.Object({
|
|
25016
|
+
apiVersions: Type10.Array(Type10.Integer(), { minItems: 1 }),
|
|
25017
|
+
app: Type10.Object({
|
|
25018
|
+
version: Type10.String({ minLength: 1, maxLength: 64 }),
|
|
25019
|
+
platform: Type10.String({ minLength: 1, maxLength: 64 }),
|
|
25020
|
+
build: Type10.String({ maxLength: 128 })
|
|
24433
25021
|
})
|
|
24434
25022
|
});
|
|
24435
25023
|
function asJsonRpcRequest(value) {
|
|
@@ -24469,11 +25057,11 @@ function buildHelloResult(serverId, serverName, apiVersion) {
|
|
|
24469
25057
|
limits: MOBILE_LIMITS
|
|
24470
25058
|
};
|
|
24471
25059
|
}
|
|
24472
|
-
var opt =
|
|
24473
|
-
var str2 =
|
|
24474
|
-
var int =
|
|
24475
|
-
var nullable2 = (schema) =>
|
|
24476
|
-
var MobileContentRefSchema =
|
|
25060
|
+
var opt = Type10.Optional;
|
|
25061
|
+
var str2 = Type10.String();
|
|
25062
|
+
var int = Type10.Integer({ minimum: 0 });
|
|
25063
|
+
var nullable2 = (schema) => Type10.Union([schema, Type10.Null()]);
|
|
25064
|
+
var MobileContentRefSchema = Type10.Object({
|
|
24477
25065
|
sessionId: str2,
|
|
24478
25066
|
agentId: opt(str2),
|
|
24479
25067
|
key: str2,
|
|
@@ -24481,7 +25069,7 @@ var MobileContentRefSchema = Type9.Object({
|
|
|
24481
25069
|
digest: str2,
|
|
24482
25070
|
bytes: int
|
|
24483
25071
|
});
|
|
24484
|
-
var MobileImageRefSchema =
|
|
25072
|
+
var MobileImageRefSchema = Type10.Object({
|
|
24485
25073
|
name: str2,
|
|
24486
25074
|
mimeType: str2,
|
|
24487
25075
|
width: int,
|
|
@@ -24490,56 +25078,70 @@ var MobileImageRefSchema = Type9.Object({
|
|
|
24490
25078
|
entryId: int,
|
|
24491
25079
|
contentIndex: int
|
|
24492
25080
|
});
|
|
24493
|
-
var
|
|
24494
|
-
|
|
24495
|
-
|
|
24496
|
-
|
|
24497
|
-
|
|
25081
|
+
var planStatus = Type10.Union([
|
|
25082
|
+
Type10.Literal("awaiting_approval"),
|
|
25083
|
+
Type10.Literal("approved"),
|
|
25084
|
+
Type10.Literal("revision_requested"),
|
|
25085
|
+
Type10.Literal("superseded"),
|
|
25086
|
+
Type10.Literal("cancelled")
|
|
24498
25087
|
]);
|
|
24499
|
-
var
|
|
24500
|
-
|
|
24501
|
-
|
|
25088
|
+
var questionStatus2 = Type10.Union([
|
|
25089
|
+
Type10.Literal("pending"),
|
|
25090
|
+
Type10.Literal("answered"),
|
|
25091
|
+
Type10.Literal("skipped"),
|
|
25092
|
+
Type10.Literal("cancelled")
|
|
25093
|
+
]);
|
|
25094
|
+
var MobileToolInteractionSchema = Type10.Union([
|
|
25095
|
+
Type10.Object({
|
|
25096
|
+
kind: Type10.Literal("ask_user"),
|
|
24502
25097
|
id: int,
|
|
24503
25098
|
question: str2,
|
|
24504
|
-
multiple:
|
|
24505
|
-
options:
|
|
25099
|
+
multiple: Type10.Boolean(),
|
|
25100
|
+
options: Type10.Array(Type10.Object({ id: str2, label: str2, recommended: opt(Type10.Boolean()) })),
|
|
24506
25101
|
status: questionStatus2,
|
|
24507
|
-
optionIds: opt(
|
|
24508
|
-
labels: opt(
|
|
25102
|
+
optionIds: opt(Type10.Array(str2)),
|
|
25103
|
+
labels: opt(Type10.Array(str2)),
|
|
24509
25104
|
text: opt(nullable2(str2))
|
|
24510
25105
|
}),
|
|
24511
|
-
|
|
24512
|
-
kind:
|
|
24513
|
-
action:
|
|
25106
|
+
Type10.Object({
|
|
25107
|
+
kind: Type10.Literal("todo"),
|
|
25108
|
+
action: Type10.Union([Type10.Literal("list"), Type10.Literal("replace")]),
|
|
24514
25109
|
revision: int,
|
|
24515
25110
|
completed: int,
|
|
24516
25111
|
total: int
|
|
25112
|
+
}),
|
|
25113
|
+
Type10.Object({
|
|
25114
|
+
kind: Type10.Literal("submit_plan"),
|
|
25115
|
+
version: str2,
|
|
25116
|
+
title: str2,
|
|
25117
|
+
steps: Type10.Array(str2),
|
|
25118
|
+
note: str2
|
|
24517
25119
|
})
|
|
24518
25120
|
]);
|
|
24519
|
-
var MobileToolItemSchema =
|
|
25121
|
+
var MobileToolItemSchema = Type10.Object({
|
|
24520
25122
|
callId: str2,
|
|
24521
25123
|
name: str2,
|
|
24522
25124
|
summary: str2,
|
|
24523
|
-
status:
|
|
25125
|
+
status: Type10.Union([Type10.Literal("running"), Type10.Literal("done"), Type10.Literal("error")]),
|
|
24524
25126
|
detail: opt(str2),
|
|
24525
25127
|
output: opt(str2),
|
|
24526
25128
|
result: opt(str2),
|
|
24527
25129
|
diff: opt(str2),
|
|
24528
|
-
diffAdded: opt(
|
|
24529
|
-
diffRemoved: opt(
|
|
24530
|
-
diffTruncated: opt(
|
|
24531
|
-
durationMs: opt(
|
|
25130
|
+
diffAdded: opt(Type10.Number()),
|
|
25131
|
+
diffRemoved: opt(Type10.Number()),
|
|
25132
|
+
diffTruncated: opt(Type10.Boolean()),
|
|
25133
|
+
durationMs: opt(Type10.Number()),
|
|
24532
25134
|
note: opt(str2),
|
|
24533
25135
|
interaction: opt(MobileToolInteractionSchema)
|
|
24534
25136
|
});
|
|
24535
|
-
var MobileChatItemSchema =
|
|
25137
|
+
var MobileChatItemSchema = Type10.Object({
|
|
24536
25138
|
key: str2,
|
|
24537
|
-
type:
|
|
24538
|
-
|
|
24539
|
-
|
|
24540
|
-
|
|
24541
|
-
|
|
24542
|
-
|
|
25139
|
+
type: Type10.Union([
|
|
25140
|
+
Type10.Literal("user"),
|
|
25141
|
+
Type10.Literal("assistant"),
|
|
25142
|
+
Type10.Literal("tool"),
|
|
25143
|
+
Type10.Literal("error"),
|
|
25144
|
+
Type10.Literal("system")
|
|
24543
25145
|
]),
|
|
24544
25146
|
text: opt(str2),
|
|
24545
25147
|
thinking: opt(str2),
|
|
@@ -24548,99 +25150,101 @@ var MobileChatItemSchema = Type9.Object({
|
|
|
24548
25150
|
skill: opt(str2),
|
|
24549
25151
|
jobId: opt(str2),
|
|
24550
25152
|
tool: opt(MobileToolItemSchema),
|
|
24551
|
-
images: opt(
|
|
24552
|
-
refs: opt(
|
|
25153
|
+
images: opt(Type10.Array(MobileImageRefSchema)),
|
|
25154
|
+
refs: opt(Type10.Array(MobileContentRefSchema))
|
|
24553
25155
|
});
|
|
24554
|
-
var MobileStreamingSchema =
|
|
25156
|
+
var MobileStreamingSchema = Type10.Object({
|
|
24555
25157
|
text: str2,
|
|
24556
25158
|
thinking: str2,
|
|
24557
25159
|
textOmitted: int,
|
|
24558
25160
|
thinkingOmitted: int,
|
|
24559
|
-
toolCalls:
|
|
25161
|
+
toolCalls: Type10.Array(Type10.Object({
|
|
24560
25162
|
callId: str2,
|
|
24561
25163
|
name: str2,
|
|
24562
25164
|
summary: str2,
|
|
24563
|
-
status:
|
|
25165
|
+
status: Type10.Union([Type10.Literal("running"), Type10.Literal("done")])
|
|
24564
25166
|
}))
|
|
24565
25167
|
});
|
|
24566
|
-
var MobileUsageSchema =
|
|
25168
|
+
var MobileUsageSchema = Type10.Object({
|
|
24567
25169
|
input: int,
|
|
24568
25170
|
output: int,
|
|
24569
25171
|
cacheRead: int,
|
|
24570
25172
|
cacheWrite: int,
|
|
24571
25173
|
totalTokens: int,
|
|
24572
|
-
cost:
|
|
25174
|
+
cost: Type10.Number({ minimum: 0 })
|
|
24573
25175
|
});
|
|
24574
|
-
var MobileSubagentRoundSchema =
|
|
25176
|
+
var MobileSubagentRoundSchema = Type10.Object({
|
|
24575
25177
|
taskId: int,
|
|
24576
25178
|
callId: str2,
|
|
24577
25179
|
agentId: str2,
|
|
24578
25180
|
round: opt(int),
|
|
24579
25181
|
role: str2,
|
|
24580
25182
|
title: str2,
|
|
24581
|
-
status:
|
|
24582
|
-
|
|
24583
|
-
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
25183
|
+
status: Type10.Union([
|
|
25184
|
+
Type10.Literal("queued"),
|
|
25185
|
+
Type10.Literal("running"),
|
|
25186
|
+
Type10.Literal("completed"),
|
|
25187
|
+
Type10.Literal("failed"),
|
|
25188
|
+
Type10.Literal("cancelled")
|
|
24587
25189
|
]),
|
|
24588
25190
|
approvalCount: int,
|
|
24589
|
-
readonly:
|
|
24590
|
-
createdAt:
|
|
24591
|
-
endedAt: nullable2(
|
|
25191
|
+
readonly: Type10.Boolean(),
|
|
25192
|
+
createdAt: Type10.Number(),
|
|
25193
|
+
endedAt: nullable2(Type10.Number()),
|
|
24592
25194
|
usage: opt(MobileUsageSchema)
|
|
24593
25195
|
});
|
|
24594
|
-
var MobileAgentSchema =
|
|
25196
|
+
var MobileAgentSchema = Type10.Object({
|
|
24595
25197
|
agentId: str2,
|
|
24596
25198
|
role: str2,
|
|
24597
25199
|
title: str2,
|
|
24598
|
-
status:
|
|
25200
|
+
status: Type10.Union([Type10.Literal("idle"), Type10.Literal("queued"), Type10.Literal("running")]),
|
|
24599
25201
|
activeRoundId: nullable2(int),
|
|
24600
25202
|
lastRoundId: int,
|
|
24601
25203
|
roundCount: int,
|
|
24602
25204
|
approvalCount: int
|
|
24603
25205
|
});
|
|
24604
|
-
var MobileQueuedSchema =
|
|
25206
|
+
var MobileQueuedSchema = Type10.Object({
|
|
24605
25207
|
id: int,
|
|
24606
|
-
mode:
|
|
24607
|
-
preview:
|
|
25208
|
+
mode: Type10.Union([Type10.Literal("steer"), Type10.Literal("followUp")]),
|
|
25209
|
+
preview: Type10.String(),
|
|
24608
25210
|
images: int
|
|
24609
25211
|
});
|
|
24610
|
-
var MobileChatStatusSchema =
|
|
24611
|
-
busy:
|
|
24612
|
-
queued: opt(
|
|
24613
|
-
todos: opt(
|
|
24614
|
-
questions: opt(
|
|
25212
|
+
var MobileChatStatusSchema = Type10.Object({
|
|
25213
|
+
busy: Type10.Boolean(),
|
|
25214
|
+
queued: opt(Type10.Array(MobileQueuedSchema)),
|
|
25215
|
+
todos: opt(Type10.Object({ revision: int, count: int, completed: int })),
|
|
25216
|
+
questions: opt(Type10.Object({ revision: int, pendingCount: int })),
|
|
25217
|
+
plan: opt(Type10.Object({ revision: int, pending: Type10.Boolean(), version: opt(Type10.String({ minLength: 1 })) })),
|
|
24615
25218
|
status: nullable2(str2),
|
|
24616
25219
|
usage: MobileUsageSchema,
|
|
24617
25220
|
taskUsage: opt(MobileUsageSchema),
|
|
24618
|
-
contextUsage: opt(
|
|
24619
|
-
agent:
|
|
24620
|
-
subagents:
|
|
24621
|
-
agents:
|
|
25221
|
+
contextUsage: opt(Type10.Object({ tokens: nullable2(Type10.Number()), window: Type10.Number(), percent: nullable2(Type10.Number()) })),
|
|
25222
|
+
agent: Type10.Object({ model: nullable2(str2), thinkingLevel: nullable2(str2) }),
|
|
25223
|
+
subagents: Type10.Array(MobileSubagentRoundSchema),
|
|
25224
|
+
agents: Type10.Array(MobileAgentSchema),
|
|
24622
25225
|
approvalCount: int,
|
|
24623
|
-
compaction: opt(
|
|
24624
|
-
readonly: opt(
|
|
25226
|
+
compaction: opt(Type10.Object({ phase: str2, at: Type10.Number() })),
|
|
25227
|
+
readonly: opt(Type10.Boolean())
|
|
24625
25228
|
});
|
|
24626
|
-
var MobileChatSnapshotSchema =
|
|
25229
|
+
var MobileChatSnapshotSchema = Type10.Object({
|
|
24627
25230
|
...MobileChatStatusSchema.properties,
|
|
24628
|
-
items:
|
|
25231
|
+
items: Type10.Array(MobileChatItemSchema),
|
|
24629
25232
|
historyBefore: nullable2(str2),
|
|
24630
25233
|
streaming: nullable2(MobileStreamingSchema)
|
|
24631
25234
|
});
|
|
24632
|
-
var MobileWorkspaceSchema =
|
|
24633
|
-
var MobileSessionSummarySchema =
|
|
25235
|
+
var MobileWorkspaceSchema = Type10.Object({ root: str2, name: str2 });
|
|
25236
|
+
var MobileSessionSummarySchema = Type10.Object({
|
|
24634
25237
|
id: str2,
|
|
24635
25238
|
workspace: str2,
|
|
24636
25239
|
title: str2,
|
|
24637
25240
|
preview: str2,
|
|
24638
|
-
lastUsedAt:
|
|
24639
|
-
lastMessageAt:
|
|
24640
|
-
archived:
|
|
24641
|
-
awaitingAnswer: opt(
|
|
25241
|
+
lastUsedAt: Type10.Integer(),
|
|
25242
|
+
lastMessageAt: Type10.Integer(),
|
|
25243
|
+
archived: Type10.Boolean(),
|
|
25244
|
+
awaitingAnswer: opt(Type10.Boolean()),
|
|
25245
|
+
awaitingPlan: opt(Type10.Boolean())
|
|
24642
25246
|
});
|
|
24643
|
-
var MobileApprovalSchema =
|
|
25247
|
+
var MobileApprovalSchema = Type10.Object({
|
|
24644
25248
|
id: int,
|
|
24645
25249
|
version: str2,
|
|
24646
25250
|
taskId: int,
|
|
@@ -24648,236 +25252,267 @@ var MobileApprovalSchema = Type9.Object({
|
|
|
24648
25252
|
title: opt(str2),
|
|
24649
25253
|
command: str2,
|
|
24650
25254
|
cwd: str2,
|
|
24651
|
-
timeoutSeconds: opt(
|
|
24652
|
-
requestedAt:
|
|
25255
|
+
timeoutSeconds: opt(Type10.Number({ minimum: 0 })),
|
|
25256
|
+
requestedAt: Type10.Number()
|
|
24653
25257
|
});
|
|
24654
|
-
var MobileTodoItemSchema =
|
|
24655
|
-
id:
|
|
24656
|
-
text:
|
|
24657
|
-
status:
|
|
25258
|
+
var MobileTodoItemSchema = Type10.Object({
|
|
25259
|
+
id: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.idChars }),
|
|
25260
|
+
text: Type10.String({ minLength: 1, maxLength: TODO_LIMITS.textChars }),
|
|
25261
|
+
status: Type10.Union([Type10.Literal("pending"), Type10.Literal("in_progress"), Type10.Literal("completed")])
|
|
24658
25262
|
});
|
|
24659
|
-
var MobileTodoListSchema =
|
|
25263
|
+
var MobileTodoListSchema = Type10.Object({
|
|
24660
25264
|
revision: int,
|
|
24661
|
-
items:
|
|
25265
|
+
items: Type10.Array(MobileTodoItemSchema, { maxItems: TODO_LIMITS.maxItems })
|
|
24662
25266
|
});
|
|
24663
|
-
var MobileQuestionOptionSchema =
|
|
24664
|
-
id:
|
|
24665
|
-
label:
|
|
24666
|
-
description: opt(
|
|
24667
|
-
recommended: opt(
|
|
25267
|
+
var MobileQuestionOptionSchema = Type10.Object({
|
|
25268
|
+
id: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.optionIdChars }),
|
|
25269
|
+
label: Type10.String({ minLength: 1, maxLength: QUESTION_LIMITS.labelChars }),
|
|
25270
|
+
description: opt(Type10.String({ maxLength: QUESTION_LIMITS.descriptionChars })),
|
|
25271
|
+
recommended: opt(Type10.Boolean())
|
|
24668
25272
|
});
|
|
24669
|
-
var MobileQuestionSummarySchema =
|
|
25273
|
+
var MobileQuestionSummarySchema = Type10.Object({
|
|
24670
25274
|
id: int,
|
|
24671
25275
|
version: str2,
|
|
24672
|
-
preview:
|
|
24673
|
-
multiple:
|
|
25276
|
+
preview: Type10.String({ maxLength: QUESTION_LIMITS.previewChars }),
|
|
25277
|
+
multiple: Type10.Boolean(),
|
|
24674
25278
|
optionCount: int,
|
|
24675
|
-
createdAt:
|
|
25279
|
+
createdAt: Type10.Number()
|
|
24676
25280
|
});
|
|
24677
|
-
var MobileQuestionListSchema =
|
|
25281
|
+
var MobileQuestionListSchema = Type10.Object({
|
|
24678
25282
|
revision: int,
|
|
24679
|
-
pending:
|
|
25283
|
+
pending: Type10.Array(MobileQuestionSummarySchema, { maxItems: QUESTION_LIMITS.maxPending })
|
|
24680
25284
|
});
|
|
24681
|
-
var MobileQuestionSchema =
|
|
25285
|
+
var MobileQuestionSchema = Type10.Object({
|
|
24682
25286
|
id: int,
|
|
24683
25287
|
callId: str2,
|
|
24684
25288
|
version: str2,
|
|
24685
|
-
question:
|
|
24686
|
-
multiple:
|
|
24687
|
-
options:
|
|
25289
|
+
question: Type10.String({ maxLength: QUESTION_LIMITS.questionChars }),
|
|
25290
|
+
multiple: Type10.Boolean(),
|
|
25291
|
+
options: Type10.Array(MobileQuestionOptionSchema, { maxItems: QUESTION_LIMITS.maxOptions }),
|
|
24688
25292
|
status: questionStatus2,
|
|
24689
|
-
answer: nullable2(
|
|
24690
|
-
optionIds:
|
|
24691
|
-
text: nullable2(
|
|
25293
|
+
answer: nullable2(Type10.Object({
|
|
25294
|
+
optionIds: Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions }),
|
|
25295
|
+
text: nullable2(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
|
|
24692
25296
|
})),
|
|
24693
|
-
createdAt:
|
|
24694
|
-
settledAt: nullable2(
|
|
25297
|
+
createdAt: Type10.Number(),
|
|
25298
|
+
settledAt: nullable2(Type10.Number())
|
|
24695
25299
|
});
|
|
24696
|
-
var MobileQuestionResponseSchema =
|
|
24697
|
-
|
|
24698
|
-
kind:
|
|
24699
|
-
optionIds: opt(
|
|
24700
|
-
text: opt(
|
|
25300
|
+
var MobileQuestionResponseSchema = Type10.Union([
|
|
25301
|
+
Type10.Object({
|
|
25302
|
+
kind: Type10.Literal("answer"),
|
|
25303
|
+
optionIds: opt(Type10.Array(Type10.String(), { maxItems: QUESTION_LIMITS.maxOptions })),
|
|
25304
|
+
text: opt(Type10.String({ maxLength: QUESTION_LIMITS.textChars }))
|
|
24701
25305
|
}, { additionalProperties: false }),
|
|
24702
|
-
|
|
25306
|
+
Type10.Object({ kind: Type10.Literal("skip") }, { additionalProperties: false })
|
|
25307
|
+
]);
|
|
25308
|
+
var MobilePlanSchema = Type10.Object({
|
|
25309
|
+
version: Type10.String({ minLength: 1 }),
|
|
25310
|
+
title: Type10.String({ maxLength: PLAN_LIMITS.titleChars }),
|
|
25311
|
+
steps: Type10.Array(Type10.String({ maxLength: PLAN_LIMITS.stepChars }), { maxItems: PLAN_LIMITS.maxSteps }),
|
|
25312
|
+
note: Type10.String({ maxLength: PLAN_LIMITS.noteChars }),
|
|
25313
|
+
status: planStatus,
|
|
25314
|
+
feedback: nullable2(Type10.String({ maxLength: PLAN_LIMITS.feedbackChars })),
|
|
25315
|
+
createdAt: Type10.Number(),
|
|
25316
|
+
settledAt: nullable2(Type10.Number())
|
|
25317
|
+
});
|
|
25318
|
+
var MobilePlanDecisionSchema = Type10.Union([
|
|
25319
|
+
Type10.Object({ kind: Type10.Literal("approve") }, { additionalProperties: false }),
|
|
25320
|
+
Type10.Object({ kind: Type10.Literal("revise"), feedback: Type10.String({ minLength: 1, maxLength: PLAN_LIMITS.feedbackChars }) }, { additionalProperties: false })
|
|
24703
25321
|
]);
|
|
24704
|
-
var sessionListParams =
|
|
25322
|
+
var sessionListParams = Type10.Object({
|
|
24705
25323
|
workspace: str2,
|
|
24706
25324
|
query: opt(str2),
|
|
24707
25325
|
cursor: opt(str2)
|
|
24708
25326
|
});
|
|
24709
|
-
var sessionListResult =
|
|
24710
|
-
sessions:
|
|
25327
|
+
var sessionListResult = Type10.Object({
|
|
25328
|
+
sessions: Type10.Array(MobileSessionSummarySchema),
|
|
24711
25329
|
nextCursor: opt(str2)
|
|
24712
25330
|
});
|
|
24713
|
-
var subscription = { subscriptionId:
|
|
24714
|
-
var pageResult =
|
|
24715
|
-
var pageParams =
|
|
25331
|
+
var subscription = { subscriptionId: Type10.String({ minLength: 1 }) };
|
|
25332
|
+
var pageResult = Type10.Object({ items: Type10.Array(MobileChatItemSchema), historyBefore: nullable2(str2) });
|
|
25333
|
+
var pageParams = Type10.Object({
|
|
24716
25334
|
...subscription,
|
|
24717
|
-
before:
|
|
24718
|
-
limit: opt(
|
|
25335
|
+
before: Type10.String({ minLength: 1 }),
|
|
25336
|
+
limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
|
|
24719
25337
|
});
|
|
24720
|
-
var chunkResult =
|
|
25338
|
+
var chunkResult = Type10.Object({ data: str2, nextOffset: int, total: int, done: Type10.Boolean() });
|
|
24721
25339
|
var MOBILE_API_V1_METHODS = {
|
|
24722
25340
|
"server.hello": { params: MobileHelloParams, result: buildHelloResultSchema() },
|
|
24723
|
-
"workspaces.list": { params:
|
|
25341
|
+
"workspaces.list": { params: Type10.Null(), result: Type10.Object({ workspaces: Type10.Array(MobileWorkspaceSchema) }) },
|
|
24724
25342
|
"sessions.list": { params: sessionListParams, result: sessionListResult },
|
|
24725
|
-
"sessions.create": { params:
|
|
25343
|
+
"sessions.create": { params: Type10.Object({ workspace: str2 }), result: MobileSessionSummarySchema },
|
|
24726
25344
|
"chat.attach": {
|
|
24727
|
-
params:
|
|
24728
|
-
result:
|
|
25345
|
+
params: Type10.Object({ sessionId: Type10.String({ minLength: 1 }) }),
|
|
25346
|
+
result: Type10.Object({ subscriptionId: str2, sessionId: str2, snapshot: MobileChatSnapshotSchema })
|
|
24729
25347
|
},
|
|
24730
|
-
"chat.detach": { params:
|
|
25348
|
+
"chat.detach": { params: Type10.Object(subscription), result: Type10.Null() },
|
|
24731
25349
|
"chat.history": { params: pageParams, result: pageResult },
|
|
24732
25350
|
"chat.content": {
|
|
24733
|
-
params:
|
|
25351
|
+
params: Type10.Object({ ref: MobileContentRefSchema, offset: int }),
|
|
24734
25352
|
result: chunkResult
|
|
24735
25353
|
},
|
|
24736
25354
|
"chat.submit": {
|
|
24737
|
-
params:
|
|
25355
|
+
params: Type10.Object({
|
|
24738
25356
|
...subscription,
|
|
24739
|
-
requestId:
|
|
24740
|
-
text:
|
|
24741
|
-
mode: opt(
|
|
25357
|
+
requestId: Type10.String({ minLength: 1, maxLength: 128 }),
|
|
25358
|
+
text: Type10.String({ minLength: 1, maxLength: 65536 }),
|
|
25359
|
+
mode: opt(Type10.Literal("steer"))
|
|
24742
25360
|
}),
|
|
24743
|
-
result:
|
|
25361
|
+
result: Type10.Object({ accepted: Type10.Literal(true), queued: Type10.Boolean() })
|
|
24744
25362
|
},
|
|
24745
25363
|
"chat.submissionStatus": {
|
|
24746
|
-
params:
|
|
24747
|
-
result:
|
|
25364
|
+
params: Type10.Object({ ...subscription, requestId: Type10.String({ minLength: 1, maxLength: 128 }) }),
|
|
25365
|
+
result: Type10.Object({ status: Type10.Union([Type10.Literal("accepted"), Type10.Literal("unknown")]) })
|
|
24748
25366
|
},
|
|
24749
25367
|
"chat.steer": {
|
|
24750
|
-
params:
|
|
24751
|
-
result:
|
|
24752
|
-
result:
|
|
24753
|
-
|
|
24754
|
-
|
|
24755
|
-
|
|
24756
|
-
|
|
24757
|
-
|
|
25368
|
+
params: Type10.Object({ ...subscription, id: int }),
|
|
25369
|
+
result: Type10.Object({
|
|
25370
|
+
result: Type10.Union([
|
|
25371
|
+
Type10.Literal("steered"),
|
|
25372
|
+
Type10.Literal("withdrawn"),
|
|
25373
|
+
Type10.Literal("placed"),
|
|
25374
|
+
Type10.Literal("settled"),
|
|
25375
|
+
Type10.Literal("unknown")
|
|
24758
25376
|
]),
|
|
24759
25377
|
id: opt(int),
|
|
24760
|
-
text: opt(
|
|
25378
|
+
text: opt(Type10.String()),
|
|
24761
25379
|
images: opt(int)
|
|
24762
25380
|
})
|
|
24763
25381
|
},
|
|
24764
25382
|
"chat.withdraw": {
|
|
24765
|
-
params:
|
|
24766
|
-
result:
|
|
24767
|
-
result:
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
25383
|
+
params: Type10.Object({ ...subscription, id: int }),
|
|
25384
|
+
result: Type10.Object({
|
|
25385
|
+
result: Type10.Union([
|
|
25386
|
+
Type10.Literal("withdrawn"),
|
|
25387
|
+
Type10.Literal("placed"),
|
|
25388
|
+
Type10.Literal("settled"),
|
|
25389
|
+
Type10.Literal("unknown")
|
|
24772
25390
|
]),
|
|
24773
|
-
text: opt(
|
|
25391
|
+
text: opt(Type10.String()),
|
|
24774
25392
|
images: opt(int)
|
|
24775
25393
|
})
|
|
24776
25394
|
},
|
|
24777
|
-
"chat.abort": { params:
|
|
25395
|
+
"chat.abort": { params: Type10.Object(subscription), result: Type10.Object({ accepted: Type10.Boolean() }) },
|
|
24778
25396
|
"subagents.open": {
|
|
24779
|
-
params:
|
|
24780
|
-
result:
|
|
25397
|
+
params: Type10.Object({ ...subscription, agentId: opt(Type10.String({ minLength: 1 })), taskId: opt(int) }),
|
|
25398
|
+
result: Type10.Object({
|
|
24781
25399
|
subagentSubscriptionId: str2,
|
|
24782
25400
|
agentId: str2,
|
|
24783
25401
|
snapshot: MobileChatSnapshotSchema
|
|
24784
25402
|
})
|
|
24785
25403
|
},
|
|
24786
25404
|
"subagents.history": {
|
|
24787
|
-
params:
|
|
24788
|
-
subagentSubscriptionId:
|
|
24789
|
-
before:
|
|
24790
|
-
limit: opt(
|
|
25405
|
+
params: Type10.Object({
|
|
25406
|
+
subagentSubscriptionId: Type10.String({ minLength: 1 }),
|
|
25407
|
+
before: Type10.String({ minLength: 1 }),
|
|
25408
|
+
limit: opt(Type10.Integer({ minimum: 1, maximum: 50 }))
|
|
24791
25409
|
}),
|
|
24792
25410
|
result: pageResult
|
|
24793
25411
|
},
|
|
24794
25412
|
"subagents.close": {
|
|
24795
|
-
params:
|
|
24796
|
-
result:
|
|
25413
|
+
params: Type10.Object({ subagentSubscriptionId: Type10.String({ minLength: 1 }) }),
|
|
25414
|
+
result: Type10.Null()
|
|
24797
25415
|
},
|
|
24798
25416
|
"subagents.stop": {
|
|
24799
|
-
params:
|
|
24800
|
-
result:
|
|
25417
|
+
params: Type10.Object({ ...subscription, taskId: int }),
|
|
25418
|
+
result: Type10.Object({ stopped: Type10.Boolean() })
|
|
24801
25419
|
},
|
|
24802
25420
|
"approvals.list": {
|
|
24803
|
-
params:
|
|
24804
|
-
result:
|
|
25421
|
+
params: Type10.Object(subscription),
|
|
25422
|
+
result: Type10.Object({ approvals: Type10.Array(MobileApprovalSchema) })
|
|
24805
25423
|
},
|
|
24806
|
-
"approvals.get": { params:
|
|
25424
|
+
"approvals.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileApprovalSchema },
|
|
24807
25425
|
"approvals.decide": {
|
|
24808
|
-
params:
|
|
25426
|
+
params: Type10.Object({
|
|
24809
25427
|
...subscription,
|
|
24810
25428
|
taskId: int,
|
|
24811
25429
|
id: int,
|
|
24812
|
-
version:
|
|
24813
|
-
decision:
|
|
25430
|
+
version: Type10.String({ minLength: 1 }),
|
|
25431
|
+
decision: Type10.Union([Type10.Literal("allow"), Type10.Literal("deny")])
|
|
24814
25432
|
}),
|
|
24815
|
-
result:
|
|
24816
|
-
status:
|
|
24817
|
-
|
|
24818
|
-
|
|
24819
|
-
|
|
24820
|
-
|
|
25433
|
+
result: Type10.Object({
|
|
25434
|
+
status: Type10.Union([
|
|
25435
|
+
Type10.Literal("pending"),
|
|
25436
|
+
Type10.Literal("allowed"),
|
|
25437
|
+
Type10.Literal("denied"),
|
|
25438
|
+
Type10.Literal("cancelled")
|
|
24821
25439
|
])
|
|
24822
25440
|
})
|
|
24823
25441
|
},
|
|
24824
|
-
"todos.get": { params:
|
|
24825
|
-
"questions.list": { params:
|
|
24826
|
-
"questions.get": { params:
|
|
25442
|
+
"todos.get": { params: Type10.Object(subscription), result: MobileTodoListSchema },
|
|
25443
|
+
"questions.list": { params: Type10.Object(subscription), result: MobileQuestionListSchema },
|
|
25444
|
+
"questions.get": { params: Type10.Object({ ...subscription, id: int }), result: MobileQuestionSchema },
|
|
24827
25445
|
"questions.respond": {
|
|
24828
|
-
params:
|
|
25446
|
+
params: Type10.Object({
|
|
24829
25447
|
...subscription,
|
|
24830
25448
|
id: int,
|
|
24831
|
-
version:
|
|
25449
|
+
version: Type10.String({ minLength: 1 }),
|
|
24832
25450
|
response: MobileQuestionResponseSchema
|
|
24833
25451
|
}),
|
|
24834
25452
|
result: MobileQuestionSchema
|
|
24835
25453
|
},
|
|
25454
|
+
"plan.get": {
|
|
25455
|
+
params: Type10.Object({ ...subscription, version: Type10.String({ minLength: 1 }) }),
|
|
25456
|
+
result: MobilePlanSchema
|
|
25457
|
+
},
|
|
25458
|
+
"plan.respond": {
|
|
25459
|
+
params: Type10.Object({
|
|
25460
|
+
...subscription,
|
|
25461
|
+
version: Type10.String({ minLength: 1 }),
|
|
25462
|
+
decision: MobilePlanDecisionSchema
|
|
25463
|
+
}),
|
|
25464
|
+
result: MobilePlanSchema
|
|
25465
|
+
},
|
|
25466
|
+
"plan.exit": {
|
|
25467
|
+
params: Type10.Object(subscription),
|
|
25468
|
+
result: Type10.Object({ mode: Type10.Literal("normal"), revision: int })
|
|
25469
|
+
},
|
|
24836
25470
|
"media.read": {
|
|
24837
|
-
params:
|
|
25471
|
+
params: Type10.Object({
|
|
24838
25472
|
...subscription,
|
|
24839
|
-
agentId: opt(
|
|
25473
|
+
agentId: opt(Type10.String({ minLength: 1 })),
|
|
24840
25474
|
entryId: int,
|
|
24841
25475
|
contentIndex: int,
|
|
24842
25476
|
offset: int
|
|
24843
25477
|
}),
|
|
24844
|
-
result:
|
|
25478
|
+
result: Type10.Object({ ...chunkResult.properties, mimeType: str2 })
|
|
24845
25479
|
}
|
|
24846
25480
|
};
|
|
24847
25481
|
function buildHelloResultSchema() {
|
|
24848
|
-
return
|
|
24849
|
-
apiVersion:
|
|
25482
|
+
return Type10.Object({
|
|
25483
|
+
apiVersion: Type10.Integer({ minimum: 1 }),
|
|
24850
25484
|
serverId: str2,
|
|
24851
25485
|
serverName: str2,
|
|
24852
25486
|
gatewayVersion: str2,
|
|
24853
|
-
capabilities:
|
|
24854
|
-
limits:
|
|
24855
|
-
maxFrameBytes:
|
|
24856
|
-
maxPayloadBytes:
|
|
24857
|
-
pageItems:
|
|
24858
|
-
chunkBytes:
|
|
24859
|
-
maxConcurrentRequests:
|
|
24860
|
-
maxSendBufferBytes:
|
|
25487
|
+
capabilities: Type10.Array(str2),
|
|
25488
|
+
limits: Type10.Object({
|
|
25489
|
+
maxFrameBytes: Type10.Integer(),
|
|
25490
|
+
maxPayloadBytes: Type10.Integer(),
|
|
25491
|
+
pageItems: Type10.Integer(),
|
|
25492
|
+
chunkBytes: Type10.Integer(),
|
|
25493
|
+
maxConcurrentRequests: Type10.Integer(),
|
|
25494
|
+
maxSendBufferBytes: Type10.Integer()
|
|
24861
25495
|
})
|
|
24862
25496
|
});
|
|
24863
25497
|
}
|
|
24864
25498
|
var patchBody = {
|
|
24865
|
-
seq:
|
|
24866
|
-
upserts:
|
|
25499
|
+
seq: Type10.Integer({ minimum: 1 }),
|
|
25500
|
+
upserts: Type10.Array(MobileChatItemSchema),
|
|
24867
25501
|
status: opt(MobileChatStatusSchema),
|
|
24868
25502
|
streaming: opt(nullable2(MobileStreamingSchema)),
|
|
24869
|
-
streamingAppend: opt(
|
|
25503
|
+
streamingAppend: opt(Type10.Object({ text: opt(str2), thinking: opt(str2) }))
|
|
24870
25504
|
};
|
|
24871
25505
|
var MOBILE_API_V1_NOTIFICATIONS = {
|
|
24872
|
-
"workspaces.changed":
|
|
24873
|
-
"sessions.changed":
|
|
24874
|
-
"chat.patch":
|
|
24875
|
-
"chat.reset":
|
|
24876
|
-
"subagents.patch":
|
|
24877
|
-
"subagents.reset":
|
|
24878
|
-
"approvals.changed":
|
|
24879
|
-
"todos.changed":
|
|
24880
|
-
"questions.changed":
|
|
25506
|
+
"workspaces.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
|
|
25507
|
+
"sessions.changed": Type10.Object({ revision: Type10.Integer({ minimum: 1 }) }),
|
|
25508
|
+
"chat.patch": Type10.Object({ subscriptionId: str2, ...patchBody }),
|
|
25509
|
+
"chat.reset": Type10.Object({ subscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
|
|
25510
|
+
"subagents.patch": Type10.Object({ subagentSubscriptionId: str2, ...patchBody }),
|
|
25511
|
+
"subagents.reset": Type10.Object({ subagentSubscriptionId: str2, seq: Type10.Integer({ minimum: 1 }) }),
|
|
25512
|
+
"approvals.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
|
|
25513
|
+
"todos.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
|
|
25514
|
+
"questions.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) }),
|
|
25515
|
+
"plan.changed": Type10.Object({ subscriptionId: str2, revision: Type10.Integer({ minimum: 1 }) })
|
|
24881
25516
|
};
|
|
24882
25517
|
|
|
24883
25518
|
// packages/gateway/src/mobile/projection.ts
|
|
@@ -25088,6 +25723,13 @@ function mapStatus(view, options = {}) {
|
|
|
25088
25723
|
}
|
|
25089
25724
|
},
|
|
25090
25725
|
...view.questions === undefined ? {} : { questions: { revision: view.questions.revision, pendingCount: view.questions.pending.length } },
|
|
25726
|
+
...view.plan === undefined ? {} : {
|
|
25727
|
+
plan: {
|
|
25728
|
+
revision: view.plan.revision,
|
|
25729
|
+
pending: view.plan.current?.status === "awaiting_approval",
|
|
25730
|
+
...view.plan.current === null ? {} : { version: view.plan.current.version }
|
|
25731
|
+
}
|
|
25732
|
+
},
|
|
25091
25733
|
status: view.status,
|
|
25092
25734
|
usage: mapUsage(view.usage),
|
|
25093
25735
|
...view.taskUsage === undefined ? {} : { taskUsage: mapUsage(view.taskUsage) },
|
|
@@ -25602,6 +26244,12 @@ class MobileAdapter {
|
|
|
25602
26244
|
return this.questionsGet(params);
|
|
25603
26245
|
case "questions.respond":
|
|
25604
26246
|
return this.questionsRespond(params);
|
|
26247
|
+
case "plan.get":
|
|
26248
|
+
return this.planGet(params);
|
|
26249
|
+
case "plan.respond":
|
|
26250
|
+
return this.planRespond(params);
|
|
26251
|
+
case "plan.exit":
|
|
26252
|
+
return this.planExit(params);
|
|
25605
26253
|
case "media.read":
|
|
25606
26254
|
return this.mediaRead(params);
|
|
25607
26255
|
default:
|
|
@@ -25656,7 +26304,8 @@ class MobileAdapter {
|
|
|
25656
26304
|
lastUsedAt: Math.round(session.lastUsedAt),
|
|
25657
26305
|
lastMessageAt: Math.round(messageTime(session)),
|
|
25658
26306
|
archived: false,
|
|
25659
|
-
...session.awaitingAnswer === undefined ? {} : { awaitingAnswer: session.awaitingAnswer }
|
|
26307
|
+
...session.awaitingAnswer === undefined ? {} : { awaitingAnswer: session.awaitingAnswer },
|
|
26308
|
+
...session.awaitingPlan === undefined ? {} : { awaitingPlan: session.awaitingPlan }
|
|
25660
26309
|
})),
|
|
25661
26310
|
...offset + page.length < sessions.length ? { nextCursor: String(offset + page.length) } : {}
|
|
25662
26311
|
};
|
|
@@ -25675,7 +26324,8 @@ class MobileAdapter {
|
|
|
25675
26324
|
lastUsedAt: Math.round(created.lastUsedAt),
|
|
25676
26325
|
lastMessageAt: Math.round(messageTime(created)),
|
|
25677
26326
|
archived: false,
|
|
25678
|
-
...created.awaitingAnswer === undefined ? {} : { awaitingAnswer: created.awaitingAnswer }
|
|
26327
|
+
...created.awaitingAnswer === undefined ? {} : { awaitingAnswer: created.awaitingAnswer },
|
|
26328
|
+
...created.awaitingPlan === undefined ? {} : { awaitingPlan: created.awaitingPlan }
|
|
25679
26329
|
};
|
|
25680
26330
|
}
|
|
25681
26331
|
nextId(prefix) {
|
|
@@ -25752,7 +26402,8 @@ class MobileAdapter {
|
|
|
25752
26402
|
approvalsRunning: false,
|
|
25753
26403
|
approvalsDirty: false,
|
|
25754
26404
|
todosRevision: chat.view.value?.todos?.revision ?? 0,
|
|
25755
|
-
questionsRevision: chat.view.value?.questions?.revision ?? 0
|
|
26405
|
+
questionsRevision: chat.view.value?.questions?.revision ?? 0,
|
|
26406
|
+
planRevision: chat.view.value?.plan?.revision ?? 0
|
|
25756
26407
|
};
|
|
25757
26408
|
this.chatState = state;
|
|
25758
26409
|
const snapshot = projection.snapshot();
|
|
@@ -25937,6 +26588,11 @@ class MobileAdapter {
|
|
|
25937
26588
|
state.questionsRevision = questions;
|
|
25938
26589
|
this.sendNotification("questions.changed", { subscriptionId: state.subscriptionId, revision: questions });
|
|
25939
26590
|
}
|
|
26591
|
+
const plan = view?.plan?.revision;
|
|
26592
|
+
if (plan !== undefined && plan > state.planRevision) {
|
|
26593
|
+
state.planRevision = plan;
|
|
26594
|
+
this.sendNotification("plan.changed", { subscriptionId: state.subscriptionId, revision: plan });
|
|
26595
|
+
}
|
|
25940
26596
|
}
|
|
25941
26597
|
async todosGet(params) {
|
|
25942
26598
|
const state = this.requireChat(params);
|
|
@@ -25975,6 +26631,31 @@ class MobileAdapter {
|
|
|
25975
26631
|
response: params.response
|
|
25976
26632
|
}), "rejected");
|
|
25977
26633
|
}
|
|
26634
|
+
async planGet(params) {
|
|
26635
|
+
const state = this.requireChat(params);
|
|
26636
|
+
this.assertAuthorized();
|
|
26637
|
+
if (state.chat.view.value?.plan === undefined)
|
|
26638
|
+
throw new MobileMethodError("not_found", "plan mode is not available");
|
|
26639
|
+
const record = await remote2(() => state.chat.planGet({ version: params.version }));
|
|
26640
|
+
if (record === null)
|
|
26641
|
+
throw new MobileMethodError("not_found", "plan not found in this session");
|
|
26642
|
+
return record;
|
|
26643
|
+
}
|
|
26644
|
+
async planExit(params) {
|
|
26645
|
+
const state = this.requireChat(params);
|
|
26646
|
+
this.assertAuthorized();
|
|
26647
|
+
if (state.chat.view.value?.plan === undefined)
|
|
26648
|
+
throw new MobileMethodError("not_found", "plan mode is not available");
|
|
26649
|
+
const view = await remote2(() => state.chat.planMode({ mode: "normal" }), "rejected");
|
|
26650
|
+
return { mode: "normal", revision: view.revision };
|
|
26651
|
+
}
|
|
26652
|
+
async planRespond(params) {
|
|
26653
|
+
const state = this.requireChat(params);
|
|
26654
|
+
this.assertAuthorized();
|
|
26655
|
+
if (state.chat.view.value?.plan === undefined)
|
|
26656
|
+
throw new MobileMethodError("not_found", "plan mode is not available");
|
|
26657
|
+
return remote2(() => state.chat.planRespond({ version: params.version, decision: params.decision }), "rejected");
|
|
26658
|
+
}
|
|
25978
26659
|
requireSubagent(params) {
|
|
25979
26660
|
const state = this.subagentState;
|
|
25980
26661
|
if (state === undefined || state.subscriptionId !== params.subagentSubscriptionId)
|
|
@@ -27278,7 +27959,7 @@ class MobileGateway {
|
|
|
27278
27959
|
}
|
|
27279
27960
|
}
|
|
27280
27961
|
attachConnection(socket, auth, localAddress) {
|
|
27281
|
-
const connectionId =
|
|
27962
|
+
const connectionId = randomUUID4();
|
|
27282
27963
|
const [serverEnd, clientEnd] = memoryLinkPair();
|
|
27283
27964
|
this.chord.accept(serverEnd, {
|
|
27284
27965
|
principal: { connectionId, kind: "mobile", deviceId: auth.deviceId, grantRevision: auth.grantRevision },
|
|
@@ -27637,7 +28318,7 @@ async function removeGatewayPortFile(nekoDir, serverId) {
|
|
|
27637
28318
|
}
|
|
27638
28319
|
|
|
27639
28320
|
// packages/gateway/src/server-services.ts
|
|
27640
|
-
import { randomUUID as
|
|
28321
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
27641
28322
|
import { ChordError as ChordError5, local as local3, method as method2 } from "@earendil-works/chord";
|
|
27642
28323
|
|
|
27643
28324
|
// packages/core/src/application-updates.ts
|
|
@@ -27788,7 +28469,7 @@ class UpdateChecker {
|
|
|
27788
28469
|
}
|
|
27789
28470
|
|
|
27790
28471
|
// packages/gateway/src/server-services.ts
|
|
27791
|
-
import { Type as
|
|
28472
|
+
import { Type as Type13 } from "typebox";
|
|
27792
28473
|
|
|
27793
28474
|
// packages/gateway/src/session-host.ts
|
|
27794
28475
|
import { open as open3 } from "fs/promises";
|
|
@@ -27800,7 +28481,7 @@ import {
|
|
|
27800
28481
|
} from "@earendil-works/chord";
|
|
27801
28482
|
import { withAbortSignal as withAbortSignal4 } from "@earendil-works/chord/context";
|
|
27802
28483
|
import { UsageDoc as UsageDoc4 } from "@earendil-works/pi-durable";
|
|
27803
|
-
import { Type as
|
|
28484
|
+
import { Type as Type12 } from "typebox";
|
|
27804
28485
|
|
|
27805
28486
|
// packages/gateway/src/image-uploads.ts
|
|
27806
28487
|
var IMAGE_CHUNK_BYTES = 256 * 1024;
|
|
@@ -27899,7 +28580,7 @@ class ImageUploads {
|
|
|
27899
28580
|
// packages/gateway/src/subagent-host.ts
|
|
27900
28581
|
import { ChordError as ChordError3, local, opaque } from "@earendil-works/chord";
|
|
27901
28582
|
import { UsageDoc as UsageDoc3 } from "@earendil-works/pi-durable";
|
|
27902
|
-
import { Type as
|
|
28583
|
+
import { Type as Type11 } from "typebox";
|
|
27903
28584
|
async function openReader(runtime, childId, parentId, context, through, historyUsage) {
|
|
27904
28585
|
const child = await runtime.harness.conversation(childId, context);
|
|
27905
28586
|
if (!child)
|
|
@@ -28024,15 +28705,15 @@ async function openReader(runtime, childId, parentId, context, through, historyU
|
|
|
28024
28705
|
}
|
|
28025
28706
|
var ChildProjection = local("neko.subagent-projection", {
|
|
28026
28707
|
version: 1,
|
|
28027
|
-
input:
|
|
28708
|
+
input: Type11.Object({ parentId: Type11.Integer(), agentId: Type11.String() }),
|
|
28028
28709
|
members: {
|
|
28029
|
-
revision:
|
|
28710
|
+
revision: Type11.Integer(),
|
|
28030
28711
|
reader: opaque("neko.subagent-reader@1")
|
|
28031
28712
|
}
|
|
28032
28713
|
});
|
|
28033
28714
|
function installSubagentHost(host, runtime, context) {
|
|
28034
28715
|
host.serve(ChildProjection, (input, _principal, self2) => {
|
|
28035
|
-
const revision = self2.state("revision",
|
|
28716
|
+
const revision = self2.state("revision", Type11.Integer(), 0);
|
|
28036
28717
|
const reader = self2.resource("reader", {
|
|
28037
28718
|
version: 1,
|
|
28038
28719
|
async open() {
|
|
@@ -28073,7 +28754,7 @@ function installSubagentHost(host, runtime, context) {
|
|
|
28073
28754
|
parentId: Number(input.sessionId),
|
|
28074
28755
|
agentId: input.agentId ?? `round-${input.taskId}`
|
|
28075
28756
|
});
|
|
28076
|
-
const authorizedRevision = self2.state("authorizedRevision",
|
|
28757
|
+
const authorizedRevision = self2.state("authorizedRevision", Type11.Integer(), 0);
|
|
28077
28758
|
self2.on("authorizedView", shared.revision, async (value, ctx) => {
|
|
28078
28759
|
try {
|
|
28079
28760
|
target.value;
|
|
@@ -28126,7 +28807,7 @@ function installSubagentHost(host, runtime, context) {
|
|
|
28126
28807
|
}
|
|
28127
28808
|
|
|
28128
28809
|
// packages/gateway/src/session-host.ts
|
|
28129
|
-
var OwnerInput =
|
|
28810
|
+
var OwnerInput = Type12.Object({});
|
|
28130
28811
|
var Owner = local2("neko.attachment-owner", {
|
|
28131
28812
|
version: 1,
|
|
28132
28813
|
input: OwnerInput,
|
|
@@ -28134,7 +28815,7 @@ var Owner = local2("neko.attachment-owner", {
|
|
|
28134
28815
|
});
|
|
28135
28816
|
var Projection = local2("neko.projection", {
|
|
28136
28817
|
version: 1,
|
|
28137
|
-
input:
|
|
28818
|
+
input: Type12.Object({ id: Type12.String() }),
|
|
28138
28819
|
members: {
|
|
28139
28820
|
view: ChatViewSchema,
|
|
28140
28821
|
reader: opaque2("neko.projection-reader@1")
|
|
@@ -28174,7 +28855,8 @@ async function openProjection(runtime, session, context) {
|
|
|
28174
28855
|
"app.neko.subagents",
|
|
28175
28856
|
"app.neko.jobs",
|
|
28176
28857
|
"app.neko.todos",
|
|
28177
|
-
"app.neko.questions"
|
|
28858
|
+
"app.neko.questions",
|
|
28859
|
+
"app.neko.plan"
|
|
28178
28860
|
].includes(change.record.kind) && change.conversationId === session.id || change.record.kind === "pi.usage" && change.conversationId !== undefined && childIds.has(change.conversationId))))
|
|
28179
28861
|
changed?.(null);
|
|
28180
28862
|
});
|
|
@@ -28202,7 +28884,7 @@ async function openProjection(runtime, session, context) {
|
|
|
28202
28884
|
}
|
|
28203
28885
|
}, "", { models: runtime.listModels(), contextEntries: view.value.entries, compaction });
|
|
28204
28886
|
const jobs = (await session.jobs()).map(clipJob);
|
|
28205
|
-
const [todos, questions] = await Promise.all([session.todos(), session.questions()]);
|
|
28887
|
+
const [todos, questions, plan] = await Promise.all([session.todos(), session.questions(), session.plan()]);
|
|
28206
28888
|
const changes = await session.changeSummary().catch(() => {
|
|
28207
28889
|
return;
|
|
28208
28890
|
});
|
|
@@ -28223,6 +28905,7 @@ async function openProjection(runtime, session, context) {
|
|
|
28223
28905
|
jobs,
|
|
28224
28906
|
todos,
|
|
28225
28907
|
questions,
|
|
28908
|
+
plan,
|
|
28226
28909
|
...changes ? { changes } : {},
|
|
28227
28910
|
agents: summarizeSubagents(subagents, (await runtime.harness.snapshot(SubagentsDoc, session.id, context))?.items),
|
|
28228
28911
|
subagents: subagents.map(({ usageStart: _start, usageEnd: _end, ...entry }) => ({
|
|
@@ -28435,6 +29118,21 @@ function serveSessions(host, runtime, context) {
|
|
|
28435
29118
|
throw new ChordError4("invalid", error instanceof Error ? error.message : "Cannot answer the question");
|
|
28436
29119
|
}
|
|
28437
29120
|
},
|
|
29121
|
+
planGet: async ({ version }) => await projection.value.session.planRecord(version) ?? null,
|
|
29122
|
+
planRespond: async ({ version, decision }) => {
|
|
29123
|
+
try {
|
|
29124
|
+
return await projection.value.session.respondPlan(version, decision);
|
|
29125
|
+
} catch (error) {
|
|
29126
|
+
throw new ChordError4("invalid", error instanceof Error ? error.message : "Cannot decide the plan");
|
|
29127
|
+
}
|
|
29128
|
+
},
|
|
29129
|
+
planMode: async ({ mode }) => {
|
|
29130
|
+
try {
|
|
29131
|
+
return await projection.value.session.setPlanMode(mode);
|
|
29132
|
+
} catch (error) {
|
|
29133
|
+
throw new ChordError4("invalid", error instanceof Error ? error.message : "Cannot change plan mode");
|
|
29134
|
+
}
|
|
29135
|
+
},
|
|
28438
29136
|
skills: () => runtime.listSkills(projection.value.session.id),
|
|
28439
29137
|
workspaceDir: async ({ path }, ctx) => {
|
|
28440
29138
|
try {
|
|
@@ -28504,7 +29202,7 @@ function serveSessions(host, runtime, context) {
|
|
|
28504
29202
|
}
|
|
28505
29203
|
|
|
28506
29204
|
// packages/gateway/src/skill-market.ts
|
|
28507
|
-
import { createHash as createHash6, randomUUID as
|
|
29205
|
+
import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
|
|
28508
29206
|
import { mkdir as mkdir4, readdir as readdir2, rm as rm4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
|
|
28509
29207
|
import { homedir as homedir6 } from "os";
|
|
28510
29208
|
import { dirname as dirname6, join as join11 } from "path";
|
|
@@ -29096,7 +29794,7 @@ function createSkillMarketClient(options) {
|
|
|
29096
29794
|
const skill = await detail(name);
|
|
29097
29795
|
const files = unpack(skill, await download(skill));
|
|
29098
29796
|
await sweepTempDirs(skillsDir);
|
|
29099
|
-
const temp = join11(dirname6(skillsDir), `.tmp-${
|
|
29797
|
+
const temp = join11(dirname6(skillsDir), `.tmp-${randomUUID5()}`);
|
|
29100
29798
|
try {
|
|
29101
29799
|
for (const [relative, bytes] of files) {
|
|
29102
29800
|
const dest = join11(temp, ...relative.split("/"));
|
|
@@ -29105,7 +29803,7 @@ function createSkillMarketClient(options) {
|
|
|
29105
29803
|
}
|
|
29106
29804
|
await mkdir4(skillsDir, { recursive: true });
|
|
29107
29805
|
if (exists) {
|
|
29108
|
-
const backup = join11(dirname6(skillsDir), `${BACKUP_PREFIX}${
|
|
29806
|
+
const backup = join11(dirname6(skillsDir), `${BACKUP_PREFIX}${randomUUID5()}-${name}`);
|
|
29109
29807
|
await renameWithRetry(target, backup);
|
|
29110
29808
|
try {
|
|
29111
29809
|
await renameWithRetry(temp, target, placeRename ? { rename: placeRename } : {});
|
|
@@ -29229,7 +29927,7 @@ async function listDrives() {
|
|
|
29229
29927
|
// packages/gateway/src/server-services.ts
|
|
29230
29928
|
var ServerState = local3("neko.server-state", {
|
|
29231
29929
|
version: 1,
|
|
29232
|
-
input:
|
|
29930
|
+
input: Type13.Object({}),
|
|
29233
29931
|
members: {
|
|
29234
29932
|
directory: DirectoryStateSchema,
|
|
29235
29933
|
workspaces: WorkspaceStateSchema,
|
|
@@ -29237,10 +29935,10 @@ var ServerState = local3("neko.server-state", {
|
|
|
29237
29935
|
searchSettings: SearchSettingsStateSchema,
|
|
29238
29936
|
contextSettings: ContextSettingsStateSchema,
|
|
29239
29937
|
defaultModel: DefaultModelStateSchema,
|
|
29240
|
-
refreshCredentials: method2(
|
|
29241
|
-
refreshSearchSettings: method2(
|
|
29242
|
-
refreshContextSettings: method2(
|
|
29243
|
-
refreshDefaultModel: method2(
|
|
29938
|
+
refreshCredentials: method2(Type13.Null(), Type13.Null()),
|
|
29939
|
+
refreshSearchSettings: method2(Type13.Null(), Type13.Null()),
|
|
29940
|
+
refreshContextSettings: method2(Type13.Null(), Type13.Null()),
|
|
29941
|
+
refreshDefaultModel: method2(Type13.Null(), Type13.Null())
|
|
29244
29942
|
}
|
|
29245
29943
|
});
|
|
29246
29944
|
async function createNekoServerServices(options) {
|
|
@@ -29320,7 +30018,8 @@ async function createNekoServerServices(options) {
|
|
|
29320
30018
|
lastMessageAt: info.lastMessageAt,
|
|
29321
30019
|
model: info.model,
|
|
29322
30020
|
status: info.status,
|
|
29323
|
-
awaitingAnswer: info.awaitingAnswer
|
|
30021
|
+
awaitingAnswer: info.awaitingAnswer,
|
|
30022
|
+
awaitingPlan: info.awaitingPlan
|
|
29324
30023
|
}));
|
|
29325
30024
|
const readStatuses = async () => {
|
|
29326
30025
|
const enabled = new Set(runtime.enabledProviders());
|
|
@@ -29519,7 +30218,7 @@ async function createNekoServerServices(options) {
|
|
|
29519
30218
|
if (!await runtime.getConversation(toConversationId(id)))
|
|
29520
30219
|
throw new ChordError5("gone", `Unknown session: ${id}`);
|
|
29521
30220
|
if (owner.owner.active?.sessionId !== id) {
|
|
29522
|
-
owner.owner.active = { sessionId: id, attachmentId:
|
|
30221
|
+
owner.owner.active = { sessionId: id, attachmentId: randomUUID6() };
|
|
29523
30222
|
await runtime.touchConversation(toConversationId(id));
|
|
29524
30223
|
}
|
|
29525
30224
|
return owner.owner.active;
|
|
@@ -29802,7 +30501,8 @@ async function startGateway(options = {}) {
|
|
|
29802
30501
|
...options.extensions !== undefined ? { extensions: options.extensions } : {},
|
|
29803
30502
|
...options.promptHome !== undefined ? { promptHome: options.promptHome } : {},
|
|
29804
30503
|
...fileConfig.shell?.path !== undefined ? { shellPath: fileConfig.shell.path } : {},
|
|
29805
|
-
...options.shellResolver !== undefined ? { shellResolver: options.shellResolver } : {}
|
|
30504
|
+
...options.shellResolver !== undefined ? { shellResolver: options.shellResolver } : {},
|
|
30505
|
+
...options.timings !== undefined ? { timings: options.timings } : {}
|
|
29806
30506
|
});
|
|
29807
30507
|
try {
|
|
29808
30508
|
return await bindGateway(runtime, {
|
|
@@ -29853,7 +30553,7 @@ async function bindGateway(runtime, options) {
|
|
|
29853
30553
|
listeners.push(wsListener);
|
|
29854
30554
|
}
|
|
29855
30555
|
const acceptLocal = (link) => chord.accept(link, {
|
|
29856
|
-
principal: { kind: "local", connectionId:
|
|
30556
|
+
principal: { kind: "local", connectionId: randomUUID7() },
|
|
29857
30557
|
onProtocolMismatch: ({ peer }) => options.onError?.(new Error(`Rejected a client on Chord protocol ${peer ?? "unknown"}; restart or update it.`))
|
|
29858
30558
|
});
|
|
29859
30559
|
try {
|
|
@@ -29955,7 +30655,7 @@ async function loadOrCreateServerId(nekoDir) {
|
|
|
29955
30655
|
if (existing !== "")
|
|
29956
30656
|
return existing;
|
|
29957
30657
|
} catch {}
|
|
29958
|
-
const created =
|
|
30658
|
+
const created = randomUUID7();
|
|
29959
30659
|
await mkdir6(nekoDir, { recursive: true, mode: 448 });
|
|
29960
30660
|
await writeFile5(file, `${created}
|
|
29961
30661
|
`, { mode: 384 });
|