neko-harness 0.5.2 → 0.5.3
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 +6 -2
- package/dist/client/assets/index-10n4trvc.css +2 -0
- package/dist/client/assets/index-CYjEttGJ.js +398 -0
- package/dist/client/index.html +2 -2
- package/dist/gateway.js +607 -108
- package/dist/neko.js +824 -128
- package/package.json +2 -1
- package/dist/client/assets/index-DCZeTvcw.css +0 -2
- package/dist/client/assets/index-PcV-h5ox.js +0 -396
package/dist/neko.js
CHANGED
|
@@ -80,7 +80,7 @@ function updateAction(input) {
|
|
|
80
80
|
return "install";
|
|
81
81
|
return "none";
|
|
82
82
|
}
|
|
83
|
-
var RUNNING_VERSION = "0.5.
|
|
83
|
+
var RUNNING_VERSION = "0.5.3", APPLICATION_PROTOCOL = 10, ApplicationProtocolError;
|
|
84
84
|
var init_application_version = __esm(() => {
|
|
85
85
|
ApplicationProtocolError = class ApplicationProtocolError extends Error {
|
|
86
86
|
constructor() {
|
|
@@ -403,6 +403,43 @@ var init_update = __esm(() => {
|
|
|
403
403
|
init_application_version();
|
|
404
404
|
});
|
|
405
405
|
|
|
406
|
+
// packages/core/src/codemode-mode.ts
|
|
407
|
+
function parseCodemodeMode(value, field = "codemode.mode") {
|
|
408
|
+
if (!isCodemodeMode(value))
|
|
409
|
+
throw new Error(`"${field}" must be one of ${CODEMODE_MODES.join(", ")}.`);
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
function parseCodemodeSettings(value, prefix = "codemode") {
|
|
413
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
414
|
+
throw new Error(`"${prefix}" must be an object.`);
|
|
415
|
+
const { mode, ...rest } = value;
|
|
416
|
+
const unknown = Object.keys(rest)[0];
|
|
417
|
+
if (unknown !== undefined)
|
|
418
|
+
throw new Error(`"${prefix}.${unknown}" is not a known setting.`);
|
|
419
|
+
return mode === undefined ? {} : { mode: parseCodemodeMode(mode, `${prefix}.mode`) };
|
|
420
|
+
}
|
|
421
|
+
function resolveCodemodeSettings(config, saved) {
|
|
422
|
+
if (isCodemodeMode(saved))
|
|
423
|
+
return { effective: { mode: saved }, source: "saved" };
|
|
424
|
+
if (config?.mode !== undefined)
|
|
425
|
+
return { effective: { mode: config.mode }, source: "config" };
|
|
426
|
+
return { effective: { mode: DEFAULT_CODEMODE_MODE }, source: "default" };
|
|
427
|
+
}
|
|
428
|
+
var CODEMODE_NAME = "codemode", CODEMODE_MODES, DEFAULT_CODEMODE_MODE = "off", isCodemodeMode = (value) => typeof value === "string" && CODEMODE_MODES.includes(value), CODEMODE_LIMITS, CODEMODE_VIEW_LIMITS;
|
|
429
|
+
var init_codemode_mode = __esm(() => {
|
|
430
|
+
CODEMODE_MODES = ["off", "on", "only"];
|
|
431
|
+
CODEMODE_LIMITS = {
|
|
432
|
+
memoryLimitBytes: 256 * 1024 * 1024,
|
|
433
|
+
maxWorkers: 2,
|
|
434
|
+
waitMs: 60000
|
|
435
|
+
};
|
|
436
|
+
CODEMODE_VIEW_LIMITS = {
|
|
437
|
+
maxCalls: 100,
|
|
438
|
+
argsChars: 200,
|
|
439
|
+
errorChars: 200
|
|
440
|
+
};
|
|
441
|
+
});
|
|
442
|
+
|
|
406
443
|
// packages/core/src/context-limits.ts
|
|
407
444
|
var PERCENT, CONTEXT_FIELD_LIMITS, PRUNING_DEFAULTS;
|
|
408
445
|
var init_context_limits = __esm(() => {
|
|
@@ -733,9 +770,17 @@ function validate(config, source) {
|
|
|
733
770
|
throw new Error(`neko config: ${source}: ${error instanceof Error ? error.message : String(error)}`);
|
|
734
771
|
}
|
|
735
772
|
}
|
|
773
|
+
if (config.codemode !== undefined) {
|
|
774
|
+
try {
|
|
775
|
+
parseCodemodeSettings(config.codemode);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
throw new Error(`neko config: ${source}: ${error instanceof Error ? error.message : String(error)}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
736
780
|
}
|
|
737
781
|
var DEFAULT_SKILL_MARKET_URL = "https://skill.kirineko.tech", NEKO_CONFIG_BASENAME = "neko.config.json", ENV_REFERENCE;
|
|
738
782
|
var init_config = __esm(() => {
|
|
783
|
+
init_codemode_mode();
|
|
739
784
|
init_context_settings();
|
|
740
785
|
init_search_providers();
|
|
741
786
|
ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
@@ -926,6 +971,35 @@ var init_credentials = __esm(() => {
|
|
|
926
971
|
DEFAULT_CREDENTIALS_PATH = join4(homedir3(), ".neko", "credentials.json");
|
|
927
972
|
});
|
|
928
973
|
|
|
974
|
+
// packages/core/src/change-limits.ts
|
|
975
|
+
function clipDiff(diff) {
|
|
976
|
+
if (Buffer.byteLength(diff, "utf8") <= CHANGE_LIMITS.opDiffBytes)
|
|
977
|
+
return { diff, truncated: false };
|
|
978
|
+
const lines = diff.split(`
|
|
979
|
+
`);
|
|
980
|
+
const kept = [];
|
|
981
|
+
let bytes = 0;
|
|
982
|
+
for (const line of lines) {
|
|
983
|
+
bytes += Buffer.byteLength(line, "utf8") + 1;
|
|
984
|
+
if (bytes > CHANGE_LIMITS.opDiffBytes)
|
|
985
|
+
break;
|
|
986
|
+
kept.push(line);
|
|
987
|
+
}
|
|
988
|
+
return { diff: kept.join(`
|
|
989
|
+
`), truncated: true };
|
|
990
|
+
}
|
|
991
|
+
var CHANGE_LIMITS;
|
|
992
|
+
var init_change_limits = __esm(() => {
|
|
993
|
+
CHANGE_LIMITS = {
|
|
994
|
+
files: 500,
|
|
995
|
+
opsPerFile: 50,
|
|
996
|
+
diffBytes: 2 * 1024 * 1024,
|
|
997
|
+
opDiffBytes: 64 * 1024,
|
|
998
|
+
conversations: 64,
|
|
999
|
+
nestedRecords: 1000
|
|
1000
|
+
};
|
|
1001
|
+
});
|
|
1002
|
+
|
|
929
1003
|
// packages/core/src/image-prompt.ts
|
|
930
1004
|
function addDefaultImagePrompt(content) {
|
|
931
1005
|
if (typeof content === "string" || !content.some((block) => block.type === "image") || content.some((block) => block.type === "text" && block.text.trim() !== ""))
|
|
@@ -1091,6 +1165,45 @@ function toolInteraction(name, details) {
|
|
|
1091
1165
|
...typeof details.text === "string" || details.text === null ? { text: details.text } : {}
|
|
1092
1166
|
};
|
|
1093
1167
|
}
|
|
1168
|
+
function codemodeView(calls, fullOutputPath) {
|
|
1169
|
+
const limit = CODEMODE_VIEW_LIMITS.maxCalls;
|
|
1170
|
+
return { calls: calls.slice(0, limit), omitted: Math.max(0, calls.length - limit), fullOutputPath };
|
|
1171
|
+
}
|
|
1172
|
+
function liveCodemodeView(live, callId) {
|
|
1173
|
+
const slots = (live.nestedTools ?? []).filter((slot) => slot.parentCallId === callId);
|
|
1174
|
+
return codemodeView(slots.map((slot, index) => {
|
|
1175
|
+
const key = /\/c(\d+)$/.exec(slot.callId)?.[1];
|
|
1176
|
+
const summary = slot.summary;
|
|
1177
|
+
return {
|
|
1178
|
+
id: key === undefined ? index + 1 : Number(key),
|
|
1179
|
+
name: slot.name,
|
|
1180
|
+
args: clip2(JSON.stringify(slot.arguments) ?? "", CODEMODE_VIEW_LIMITS.argsChars),
|
|
1181
|
+
status: slot.status !== "done" ? "running" : summary === undefined ? "cancelled" : summary.isError ? "error" : "ok",
|
|
1182
|
+
durationMs: summary?.durationMs ?? null,
|
|
1183
|
+
error: summary?.error === undefined ? null : clip2(summary.error, CODEMODE_VIEW_LIMITS.errorChars)
|
|
1184
|
+
};
|
|
1185
|
+
}), null);
|
|
1186
|
+
}
|
|
1187
|
+
function codemodeResultView(details) {
|
|
1188
|
+
if (!isRecord(details) || !Array.isArray(details.calls))
|
|
1189
|
+
return;
|
|
1190
|
+
const calls = details.calls.flatMap((call) => {
|
|
1191
|
+
if (!isRecord(call) || typeof call.name !== "string")
|
|
1192
|
+
return [];
|
|
1193
|
+
const { id, args, status, durationMs, error } = call;
|
|
1194
|
+
return [
|
|
1195
|
+
{
|
|
1196
|
+
id: typeof id === "number" ? id : 0,
|
|
1197
|
+
name: call.name,
|
|
1198
|
+
args: typeof args === "string" ? clip2(args, CODEMODE_VIEW_LIMITS.argsChars) : "",
|
|
1199
|
+
status: status === "ok" || status === "error" || status === "cancelled" ? status : "running",
|
|
1200
|
+
durationMs: typeof durationMs === "number" ? durationMs : null,
|
|
1201
|
+
error: typeof error === "string" ? clip2(error, CODEMODE_VIEW_LIMITS.errorChars) : null
|
|
1202
|
+
}
|
|
1203
|
+
];
|
|
1204
|
+
});
|
|
1205
|
+
return codemodeView(calls, typeof details.fullOutputPath === "string" ? details.fullOutputPath : null);
|
|
1206
|
+
}
|
|
1094
1207
|
function summarizeToolCall(name, args) {
|
|
1095
1208
|
const record = args ?? {};
|
|
1096
1209
|
switch (name) {
|
|
@@ -1123,6 +1236,12 @@ function summarizeToolCall(name, args) {
|
|
|
1123
1236
|
`)
|
|
1124
1237
|
};
|
|
1125
1238
|
}
|
|
1239
|
+
case "codemode": {
|
|
1240
|
+
const code = typeof record.code === "string" ? record.code : "";
|
|
1241
|
+
const first = code.split(`
|
|
1242
|
+
`).find((line) => line.trim() !== "" && !line.trim().startsWith("// @options")) ?? "";
|
|
1243
|
+
return { args: truncateMiddle(first.trim(), 100), detail: code };
|
|
1244
|
+
}
|
|
1126
1245
|
case "subagent":
|
|
1127
1246
|
return {
|
|
1128
1247
|
args: record.action === "list" ? "List agents" : `${record.agentId ?? record.type}: ${record.title}`,
|
|
@@ -1266,7 +1385,8 @@ function projectChatView(view, modelLabel, options = {}) {
|
|
|
1266
1385
|
diff,
|
|
1267
1386
|
diffAdded: counts?.added ?? null,
|
|
1268
1387
|
diffRemoved: counts?.removed ?? null,
|
|
1269
|
-
...interactionField(message.toolName, message.details)
|
|
1388
|
+
...interactionField(message.toolName, message.details),
|
|
1389
|
+
...codemodeField(message.toolName, codemodeResultView(message.details))
|
|
1270
1390
|
});
|
|
1271
1391
|
}
|
|
1272
1392
|
}
|
|
@@ -1364,7 +1484,8 @@ function projectChatView(view, modelLabel, options = {}) {
|
|
|
1364
1484
|
diffAdded: synthesized?.added ?? null,
|
|
1365
1485
|
diffRemoved: synthesized?.removed ?? null,
|
|
1366
1486
|
diffTruncated: synthesized?.truncated ?? false,
|
|
1367
|
-
...interactionField(block.name, slot?.details)
|
|
1487
|
+
...interactionField(block.name, slot?.details),
|
|
1488
|
+
...codemodeField(block.name, slot?.status === "running" ? liveCodemodeView(live, block.id) : undefined)
|
|
1368
1489
|
});
|
|
1369
1490
|
}
|
|
1370
1491
|
}
|
|
@@ -1487,7 +1608,8 @@ ${block.text}`;
|
|
|
1487
1608
|
diff: synthesized?.diff ?? null,
|
|
1488
1609
|
diffAdded: synthesized?.added ?? null,
|
|
1489
1610
|
diffRemoved: synthesized?.removed ?? null,
|
|
1490
|
-
diffTruncated: synthesized?.truncated ?? false
|
|
1611
|
+
diffTruncated: synthesized?.truncated ?? false,
|
|
1612
|
+
...codemodeField(block.name, slot?.status === "running" ? liveCodemodeView(live, block.id) : undefined)
|
|
1491
1613
|
});
|
|
1492
1614
|
}
|
|
1493
1615
|
}
|
|
@@ -1510,8 +1632,9 @@ function contextTokens(entries) {
|
|
|
1510
1632
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), strings = (value) => Array.isArray(value) && value.every((v) => typeof v === "string") ? value : undefined, interactionField = (name, details) => {
|
|
1511
1633
|
const interaction = toolInteraction(name, details);
|
|
1512
1634
|
return interaction ? { interaction } : {};
|
|
1513
|
-
}, WRITE_DIFF_MAX_LINES = 2000, WRITE_DIFF_MAX_BYTES;
|
|
1635
|
+
}, clip2 = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`, codemodeField = (name, view) => name === CODEMODE_NAME && view ? { codemode: view } : {}, WRITE_DIFF_MAX_LINES = 2000, WRITE_DIFF_MAX_BYTES;
|
|
1514
1636
|
var init_projection = __esm(() => {
|
|
1637
|
+
init_codemode_mode();
|
|
1515
1638
|
init_image_prompt();
|
|
1516
1639
|
init_input();
|
|
1517
1640
|
init_queued();
|
|
@@ -1519,11 +1642,80 @@ var init_projection = __esm(() => {
|
|
|
1519
1642
|
WRITE_DIFF_MAX_BYTES = 64 * 1024;
|
|
1520
1643
|
});
|
|
1521
1644
|
|
|
1645
|
+
// packages/core/src/nested-changes.ts
|
|
1646
|
+
import { defineDoc, wrapTool } from "@earendil-works/pi-durable";
|
|
1647
|
+
import { createEditTool, createWriteTool } from "@earendil-works/pi-durable/tools";
|
|
1648
|
+
function addNestedChange(state, change) {
|
|
1649
|
+
const existing = state.items.findIndex((item) => item.id === change.id);
|
|
1650
|
+
if (existing !== -1)
|
|
1651
|
+
state.items.splice(existing, 1);
|
|
1652
|
+
else
|
|
1653
|
+
state.total += 1;
|
|
1654
|
+
state.items.push(change);
|
|
1655
|
+
let bytes = bytesOf(state.items);
|
|
1656
|
+
while (state.items.length > 1 && (state.items.length > CHANGE_LIMITS.nestedRecords || bytes > CHANGE_LIMITS.diffBytes)) {
|
|
1657
|
+
const evicted = state.items.shift();
|
|
1658
|
+
bytes -= Buffer.byteLength(evicted.diff, "utf8");
|
|
1659
|
+
state.dropped = true;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function describe2(tool, args, details) {
|
|
1663
|
+
if (tool === "edit") {
|
|
1664
|
+
const raw = editResultDiff(details);
|
|
1665
|
+
if (raw === null)
|
|
1666
|
+
return { diff: "", added: 0, removed: 0, truncated: false };
|
|
1667
|
+
return { ...clipDiff(raw), ...countDiffLines(raw) };
|
|
1668
|
+
}
|
|
1669
|
+
return synthesizedWriteDiff(args) ?? { diff: "", added: 0, removed: 0, truncated: false };
|
|
1670
|
+
}
|
|
1671
|
+
function nestedChangeWraps() {
|
|
1672
|
+
const wrap = (reference, tool) => wrapTool(reference, (inner) => ({
|
|
1673
|
+
...inner,
|
|
1674
|
+
execute: async (args, api, context) => {
|
|
1675
|
+
const result = await inner.execute(args, api, context);
|
|
1676
|
+
const path = args?.path;
|
|
1677
|
+
if (result.isError || typeof path !== "string" || path === "")
|
|
1678
|
+
return result;
|
|
1679
|
+
const task = await api.getTask(api.taskId, context);
|
|
1680
|
+
const input = task?.input;
|
|
1681
|
+
if (input?.kind !== "nested" || typeof input.parentCallId !== "string")
|
|
1682
|
+
return result;
|
|
1683
|
+
const parentCallId = input.parentCallId;
|
|
1684
|
+
const change = {
|
|
1685
|
+
id: api.callId,
|
|
1686
|
+
parentCallId,
|
|
1687
|
+
tool,
|
|
1688
|
+
path,
|
|
1689
|
+
...describe2(tool, args, result.details)
|
|
1690
|
+
};
|
|
1691
|
+
await api.commit(async (tx) => {
|
|
1692
|
+
addNestedChange(await tx.doc(NestedChangesDoc, api.conversationId), change);
|
|
1693
|
+
}, context);
|
|
1694
|
+
return result;
|
|
1695
|
+
}
|
|
1696
|
+
}));
|
|
1697
|
+
return [wrap(createEditTool(), "edit"), wrap(createWriteTool(), "write")];
|
|
1698
|
+
}
|
|
1699
|
+
var NestedChangesDoc, bytesOf = (items) => items.reduce((sum, item) => sum + Buffer.byteLength(item.diff, "utf8"), 0);
|
|
1700
|
+
var init_nested_changes = __esm(() => {
|
|
1701
|
+
init_change_limits();
|
|
1702
|
+
init_projection();
|
|
1703
|
+
NestedChangesDoc = defineDoc({
|
|
1704
|
+
kind: "app.neko.nested-changes",
|
|
1705
|
+
checkpointWhen: () => false,
|
|
1706
|
+
version: 1,
|
|
1707
|
+
scope: "conversation",
|
|
1708
|
+
history: "rewindable",
|
|
1709
|
+
fork: "asOf",
|
|
1710
|
+
initial: () => ({ total: 0, dropped: false, items: [] })
|
|
1711
|
+
});
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1522
1714
|
// packages/core/src/plan-doc.ts
|
|
1523
|
-
import { defineDoc } from "@earendil-works/pi-durable";
|
|
1524
|
-
var PLAN_HISTORY = 5, PlanDoc, PLAN_CODER_BLOCKED = "Plan mode is read-only: the coder subagent is unavailable. Use explore or plan subagents, or finish the plan first.";
|
|
1715
|
+
import { defineDoc as defineDoc2 } from "@earendil-works/pi-durable";
|
|
1716
|
+
var PLAN_HISTORY = 5, PlanDoc, PLAN_CODER_BLOCKED = "Plan mode is read-only: the coder subagent is unavailable. Use explore or plan subagents, or finish the plan first.", PLAN_REMOVED_TOOLS, PLAN_INSTRUCTIONS;
|
|
1525
1717
|
var init_plan_doc = __esm(() => {
|
|
1526
|
-
PlanDoc =
|
|
1718
|
+
PlanDoc = defineDoc2({
|
|
1527
1719
|
kind: "app.neko.plan",
|
|
1528
1720
|
checkpointWhen: () => false,
|
|
1529
1721
|
version: 1,
|
|
@@ -1532,6 +1724,205 @@ var init_plan_doc = __esm(() => {
|
|
|
1532
1724
|
fork: "asOf",
|
|
1533
1725
|
initial: () => ({ mode: "normal", revision: 0, items: [] })
|
|
1534
1726
|
});
|
|
1727
|
+
PLAN_REMOVED_TOOLS = ["edit", "write", "job"];
|
|
1728
|
+
PLAN_INSTRUCTIONS = [
|
|
1729
|
+
"You are in plan mode: research and plan only, and change nothing.",
|
|
1730
|
+
"- 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.",
|
|
1731
|
+
"- Not available: edit, write, job and any command that modifies files, installs packages or starts processes. Do not try to work around this.",
|
|
1732
|
+
"- 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.",
|
|
1733
|
+
"- After submit_plan do nothing else. If the user asks for changes, research what is needed and submit a revised plan.",
|
|
1734
|
+
"- Use ask_user only for decisions you cannot settle by reading the code."
|
|
1735
|
+
].join(`
|
|
1736
|
+
`);
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
// packages/core/src/agent-change.ts
|
|
1740
|
+
function onlyModeTools(plan, role) {
|
|
1741
|
+
return [
|
|
1742
|
+
CODEMODE_NAME,
|
|
1743
|
+
...role === "main" ? [...ONLY_MODE_MAIN_TOOLS, ...plan === "plan" ? ["submit_plan"] : []] : []
|
|
1744
|
+
];
|
|
1745
|
+
}
|
|
1746
|
+
function agentChangeFor({ plan, codemode, role }) {
|
|
1747
|
+
const planning = plan === "plan";
|
|
1748
|
+
return {
|
|
1749
|
+
tools: planning ? { remove: PLAN_REMOVED_TOOLS.map(named) } : null,
|
|
1750
|
+
modelTools: codemode === "only" ? onlyModeTools(plan, role).map(named) : planning ? { add: [named("submit_plan")] } : null,
|
|
1751
|
+
extensions: codemode === "off" ? { remove: [{ name: CODEMODE_NAME }] } : { add: [{ name: CODEMODE_NAME }] },
|
|
1752
|
+
instructions: planning ? PLAN_INSTRUCTIONS : null
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
function enabledTools(agent) {
|
|
1756
|
+
const offered = new Set(agent.tools.map((tool) => tool.name));
|
|
1757
|
+
return [...agent.tools, ...agent.callable.filter((tool) => !offered.has(tool.name))];
|
|
1758
|
+
}
|
|
1759
|
+
function agentSyncChange(agent, state) {
|
|
1760
|
+
const desired = agentChangeFor(state);
|
|
1761
|
+
const planMismatch = agentInPlanMode(agent) !== (state.plan === "plan");
|
|
1762
|
+
const hasExtension = agent.extensions.some((extension) => extension.name === CODEMODE_NAME);
|
|
1763
|
+
const callable = new Set(agent.callable.map((tool) => tool.name));
|
|
1764
|
+
const offered = agent.tools.map((tool) => tool.name);
|
|
1765
|
+
let codemodeMismatch;
|
|
1766
|
+
if (state.codemode === "off")
|
|
1767
|
+
codemodeMismatch = hasExtension || callable.has(CODEMODE_NAME);
|
|
1768
|
+
else {
|
|
1769
|
+
codemodeMismatch = !hasExtension || !offered.includes(CODEMODE_NAME);
|
|
1770
|
+
if (state.codemode === "only") {
|
|
1771
|
+
const allowed = new Set(onlyModeTools(state.plan, state.role));
|
|
1772
|
+
codemodeMismatch ||= offered.some((name) => !allowed.has(name));
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (!planMismatch && !codemodeMismatch)
|
|
1776
|
+
return;
|
|
1777
|
+
return {
|
|
1778
|
+
...planMismatch ? { tools: desired.tools, instructions: desired.instructions } : {},
|
|
1779
|
+
modelTools: desired.modelTools,
|
|
1780
|
+
...codemodeMismatch ? { extensions: desired.extensions } : {}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
var ONLY_MODE_MAIN_TOOLS, named = (name) => ({ name }), agentInPlanMode = (agent) => agent.instructions === PLAN_INSTRUCTIONS;
|
|
1784
|
+
var init_agent_change = __esm(() => {
|
|
1785
|
+
init_codemode_mode();
|
|
1786
|
+
init_plan_doc();
|
|
1787
|
+
ONLY_MODE_MAIN_TOOLS = ["todo", "ask_user", "subagent"];
|
|
1788
|
+
});
|
|
1789
|
+
|
|
1790
|
+
// packages/core/src/codemode.ts
|
|
1791
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
1792
|
+
import { CodemodeSandbox } from "@earendil-works/pi-codemode";
|
|
1793
|
+
import { defineDoc as defineDoc3, wrapTool as wrapTool2 } from "@earendil-works/pi-durable";
|
|
1794
|
+
import { createCodemodeExtension, createCodemodeTool } from "@earendil-works/pi-durable/codemode";
|
|
1795
|
+
|
|
1796
|
+
class WorkerLimiter {
|
|
1797
|
+
#max;
|
|
1798
|
+
#waitMs;
|
|
1799
|
+
#tickets = new AsyncLocalStorage;
|
|
1800
|
+
#waiters = [];
|
|
1801
|
+
#running = 0;
|
|
1802
|
+
#reserved = 0;
|
|
1803
|
+
constructor(limits = CODEMODE_LIMITS) {
|
|
1804
|
+
this.#max = limits.maxWorkers;
|
|
1805
|
+
this.#waitMs = limits.waitMs;
|
|
1806
|
+
}
|
|
1807
|
+
get running() {
|
|
1808
|
+
return this.#running;
|
|
1809
|
+
}
|
|
1810
|
+
get waiting() {
|
|
1811
|
+
return this.#waiters.length;
|
|
1812
|
+
}
|
|
1813
|
+
#free() {
|
|
1814
|
+
return this.#running + this.#reserved < this.#max;
|
|
1815
|
+
}
|
|
1816
|
+
#wake() {
|
|
1817
|
+
while (this.#waiters.length > 0 && this.#free()) {
|
|
1818
|
+
this.#reserved += 1;
|
|
1819
|
+
this.#waiters.shift()?.();
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
acquireWorker = () => {
|
|
1823
|
+
const ticket = this.#tickets.getStore();
|
|
1824
|
+
if (ticket && !ticket.claimed) {
|
|
1825
|
+
ticket.claimed = true;
|
|
1826
|
+
this.#reserved -= 1;
|
|
1827
|
+
} else if (!this.#free())
|
|
1828
|
+
throw new Error(SANDBOX_BUSY);
|
|
1829
|
+
this.#running += 1;
|
|
1830
|
+
let released = false;
|
|
1831
|
+
return () => {
|
|
1832
|
+
if (released)
|
|
1833
|
+
return;
|
|
1834
|
+
released = true;
|
|
1835
|
+
this.#running -= 1;
|
|
1836
|
+
this.#wake();
|
|
1837
|
+
};
|
|
1838
|
+
};
|
|
1839
|
+
async#admit(signal) {
|
|
1840
|
+
signal?.throwIfAborted();
|
|
1841
|
+
if (this.#waiters.length === 0 && this.#free()) {
|
|
1842
|
+
this.#reserved += 1;
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
await new Promise((resolve, reject) => {
|
|
1846
|
+
let timer;
|
|
1847
|
+
const leave = () => {
|
|
1848
|
+
clearTimeout(timer);
|
|
1849
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1850
|
+
};
|
|
1851
|
+
const grant = () => {
|
|
1852
|
+
leave();
|
|
1853
|
+
resolve();
|
|
1854
|
+
};
|
|
1855
|
+
const drop = (error) => {
|
|
1856
|
+
const index = this.#waiters.indexOf(grant);
|
|
1857
|
+
if (index === -1)
|
|
1858
|
+
return;
|
|
1859
|
+
this.#waiters.splice(index, 1);
|
|
1860
|
+
leave();
|
|
1861
|
+
reject(error);
|
|
1862
|
+
};
|
|
1863
|
+
const onAbort = () => drop(signal?.reason instanceof Error ? signal.reason : new Error("Aborted"));
|
|
1864
|
+
this.#waiters.push(grant);
|
|
1865
|
+
timer = setTimeout(() => drop(new Error(SANDBOX_BUSY)), this.#waitMs);
|
|
1866
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
async run(signal, work) {
|
|
1870
|
+
await this.#admit(signal);
|
|
1871
|
+
const ticket = { claimed: false };
|
|
1872
|
+
try {
|
|
1873
|
+
return await this.#tickets.run(ticket, work);
|
|
1874
|
+
} finally {
|
|
1875
|
+
if (!ticket.claimed) {
|
|
1876
|
+
this.#reserved -= 1;
|
|
1877
|
+
this.#wake();
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
function createCodemodeRuntime(limits = CODEMODE_LIMITS) {
|
|
1883
|
+
const limiter = new WorkerLimiter(limits);
|
|
1884
|
+
const sandbox = new CodemodeSandbox({
|
|
1885
|
+
memoryLimitBytes: CODEMODE_LIMITS.memoryLimitBytes,
|
|
1886
|
+
acquireWorker: limiter.acquireWorker
|
|
1887
|
+
});
|
|
1888
|
+
const options = { sandbox, models: false };
|
|
1889
|
+
const base = createCodemodeExtension(options);
|
|
1890
|
+
const reference = createCodemodeTool(options);
|
|
1891
|
+
const extension = {
|
|
1892
|
+
...base,
|
|
1893
|
+
wraps: [
|
|
1894
|
+
...base.wraps ?? [],
|
|
1895
|
+
...nestedChangeWraps(),
|
|
1896
|
+
wrapTool2(reference, (tool) => ({
|
|
1897
|
+
...tool,
|
|
1898
|
+
execute: (args, api, context) => limiter.run(context.abortSignal, () => tool.execute(args, api, context))
|
|
1899
|
+
}))
|
|
1900
|
+
]
|
|
1901
|
+
};
|
|
1902
|
+
return { extension, limiter, close: () => sandbox.close() };
|
|
1903
|
+
}
|
|
1904
|
+
var CodemodeSettingsDoc, CodemodeDoc, SANDBOX_BUSY = "Codemode sandbox is busy: too many scripts are running. Try again shortly.";
|
|
1905
|
+
var init_codemode = __esm(() => {
|
|
1906
|
+
init_codemode_mode();
|
|
1907
|
+
init_nested_changes();
|
|
1908
|
+
CodemodeSettingsDoc = defineDoc3({
|
|
1909
|
+
kind: "app.neko.codemode-settings",
|
|
1910
|
+
checkpointWhen: () => false,
|
|
1911
|
+
version: 1,
|
|
1912
|
+
scope: "conversation",
|
|
1913
|
+
history: "latest",
|
|
1914
|
+
fork: "initial",
|
|
1915
|
+
initial: () => ({ mode: null })
|
|
1916
|
+
});
|
|
1917
|
+
CodemodeDoc = defineDoc3({
|
|
1918
|
+
kind: "app.neko.codemode",
|
|
1919
|
+
checkpointWhen: () => false,
|
|
1920
|
+
version: 1,
|
|
1921
|
+
scope: "conversation",
|
|
1922
|
+
history: "rewindable",
|
|
1923
|
+
fork: "asOf",
|
|
1924
|
+
initial: () => ({ mode: "off" })
|
|
1925
|
+
});
|
|
1535
1926
|
});
|
|
1536
1927
|
|
|
1537
1928
|
// packages/core/src/subagent-usage.ts
|
|
@@ -1583,7 +1974,7 @@ var init_subagent_usage = __esm(() => {
|
|
|
1583
1974
|
import {
|
|
1584
1975
|
AssistantEntry,
|
|
1585
1976
|
configure,
|
|
1586
|
-
defineDoc as
|
|
1977
|
+
defineDoc as defineDoc4,
|
|
1587
1978
|
defineExtension,
|
|
1588
1979
|
defineTask,
|
|
1589
1980
|
defineTool,
|
|
@@ -1824,6 +2215,7 @@ function createSubagentExtension(prepare, discard, guard) {
|
|
|
1824
2215
|
parameters,
|
|
1825
2216
|
structuredOutputSchema,
|
|
1826
2217
|
replay: "safe",
|
|
2218
|
+
callers: ["model"],
|
|
1827
2219
|
execute: async (input, api, context) => {
|
|
1828
2220
|
if (!Value.Check(actionParameters, input))
|
|
1829
2221
|
throw new Error("Invalid subagent arguments. create requires type/title/task; continue requires agentId/title/task; list accepts only cursor/limit. Do not mix fields from different actions.");
|
|
@@ -1874,12 +2266,16 @@ function createSubagentExtension(prepare, discard, guard) {
|
|
|
1874
2266
|
if (!identity) {
|
|
1875
2267
|
const created = child ?? await tx.createConversation({ ownership: { kind: "task", taskId: api.taskId } });
|
|
1876
2268
|
const role = args.action === "continue" ? "explore" : args.type;
|
|
2269
|
+
const codemode = (await tx.doc(CodemodeDoc, api.conversationId)).mode;
|
|
2270
|
+
(await tx.doc(CodemodeDoc, created.id)).mode = codemode;
|
|
2271
|
+
const enabled = enabledTools(agent);
|
|
1877
2272
|
await configure(tx, created.id, {
|
|
1878
2273
|
model: agent.model,
|
|
1879
2274
|
thinkingLevel: agent.thinkingLevel,
|
|
1880
2275
|
cwd: agent.cwd,
|
|
1881
2276
|
extensions: agent.extensions.filter((e) => e.name !== extension.name && !MAIN_ONLY_EXTENSIONS.has(e.name)),
|
|
1882
|
-
|
|
2277
|
+
modelTools: agentChangeFor({ plan: "normal", codemode, role: "subagent" }).modelTools,
|
|
2278
|
+
tools: enabled.filter((t) => t.name !== "subagent" && t.name !== "job" && !MAIN_ONLY_TOOLS.has(t.name) && (role === "coder" || READ_TOOLS.has(t.name) || role === "explore" && t.name === "bash"))
|
|
1883
2279
|
});
|
|
1884
2280
|
Object.assign(await tx.doc(SubagentDoc, created.id), { role, parentId: api.conversationId });
|
|
1885
2281
|
identity = {
|
|
@@ -1982,9 +2378,10 @@ async function removeMainOnlyTools(harness, ids, context) {
|
|
|
1982
2378
|
if (!(await harness.snapshot(SubagentDoc, id, context))?.role)
|
|
1983
2379
|
continue;
|
|
1984
2380
|
const agent = await (await harness.conversation(id, context)).agent(context);
|
|
1985
|
-
|
|
2381
|
+
const enabled = enabledTools(agent);
|
|
2382
|
+
if (!enabled.some((t) => MAIN_ONLY_TOOLS.has(t.name)))
|
|
1986
2383
|
continue;
|
|
1987
|
-
await harness.commit((tx) => configure(tx, id, { tools:
|
|
2384
|
+
await harness.commit((tx) => configure(tx, id, { tools: enabled.filter((t) => !MAIN_ONLY_TOOLS.has(t.name)) }), context);
|
|
1988
2385
|
}
|
|
1989
2386
|
}
|
|
1990
2387
|
async function installSubagentCoordinator(harness, parents, context) {
|
|
@@ -2031,10 +2428,13 @@ async function installSubagentCoordinator(harness, parents, context) {
|
|
|
2031
2428
|
}
|
|
2032
2429
|
var agentIdFor = (childId) => `agent-${childId}`, DelegationsDoc, SubagentsDoc, SubagentDoc, SlotsDoc, ROLE_PROMPTS, SUBAGENT_DELIVERY = " Work from the assigned task, your own prior conversation context and project resources; you cannot see the main conversation. Do not delegate or ask the user directly. Your final message must be a self-contained delivery to the main agent, including evidence, results and limitations. Skills never expand your allowed tools.", MAIN_ONLY_TOOLS, MAIN_ONLY_EXTENSIONS, READ_TOOLS, actionParameters, parameters, structuredOutputSchema, ADMISSION_POLL_MS = 100;
|
|
2033
2430
|
var init_subagents = __esm(() => {
|
|
2431
|
+
init_agent_change();
|
|
2432
|
+
init_codemode();
|
|
2433
|
+
init_codemode_mode();
|
|
2034
2434
|
init_input();
|
|
2035
2435
|
init_plan_doc();
|
|
2036
2436
|
init_subagent_usage();
|
|
2037
|
-
DelegationsDoc =
|
|
2437
|
+
DelegationsDoc = defineDoc4({
|
|
2038
2438
|
kind: "app.neko.delegations",
|
|
2039
2439
|
checkpointWhen: () => false,
|
|
2040
2440
|
version: 1,
|
|
@@ -2043,7 +2443,7 @@ var init_subagents = __esm(() => {
|
|
|
2043
2443
|
fork: "initial",
|
|
2044
2444
|
initial: () => ({ items: [] })
|
|
2045
2445
|
});
|
|
2046
|
-
SubagentsDoc =
|
|
2446
|
+
SubagentsDoc = defineDoc4({
|
|
2047
2447
|
kind: "app.neko.subagents",
|
|
2048
2448
|
checkpointWhen: () => false,
|
|
2049
2449
|
version: 1,
|
|
@@ -2052,7 +2452,7 @@ var init_subagents = __esm(() => {
|
|
|
2052
2452
|
fork: "initial",
|
|
2053
2453
|
initial: () => ({ items: [] })
|
|
2054
2454
|
});
|
|
2055
|
-
SubagentDoc =
|
|
2455
|
+
SubagentDoc = defineDoc4({
|
|
2056
2456
|
kind: "app.neko.subagent",
|
|
2057
2457
|
checkpointWhen: () => false,
|
|
2058
2458
|
version: 1,
|
|
@@ -2061,7 +2461,7 @@ var init_subagents = __esm(() => {
|
|
|
2061
2461
|
fork: "initial",
|
|
2062
2462
|
initial: () => ({ role: null, parentId: null })
|
|
2063
2463
|
});
|
|
2064
|
-
SlotsDoc =
|
|
2464
|
+
SlotsDoc = defineDoc4({
|
|
2065
2465
|
kind: "app.neko.subagent-slots",
|
|
2066
2466
|
checkpointWhen: () => false,
|
|
2067
2467
|
version: 1,
|
|
@@ -2075,7 +2475,7 @@ var init_subagents = __esm(() => {
|
|
|
2075
2475
|
};
|
|
2076
2476
|
MAIN_ONLY_TOOLS = new Set(["todo", "ask_user", "submit_plan"]);
|
|
2077
2477
|
MAIN_ONLY_EXTENSIONS = new Set(["neko-todos", "neko-questions", "neko-plan"]);
|
|
2078
|
-
READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract"]);
|
|
2478
|
+
READ_TOOLS = new Set(["read", "find_files", "search_text", "web_search", "web_extract", CODEMODE_NAME]);
|
|
2079
2479
|
actionParameters = Type.Union([
|
|
2080
2480
|
Type.Object({
|
|
2081
2481
|
action: Type.Optional(Type.Literal("create")),
|
|
@@ -2143,22 +2543,6 @@ function normalizeChangePath(raw, cwd, workspace) {
|
|
|
2143
2543
|
}
|
|
2144
2544
|
return { path: resolved.split(sep).join("/"), outside: true };
|
|
2145
2545
|
}
|
|
2146
|
-
function clipDiff(diff) {
|
|
2147
|
-
if (Buffer.byteLength(diff, "utf8") <= CHANGE_LIMITS.opDiffBytes)
|
|
2148
|
-
return { diff, truncated: false };
|
|
2149
|
-
const lines = diff.split(`
|
|
2150
|
-
`);
|
|
2151
|
-
const kept = [];
|
|
2152
|
-
let bytes = 0;
|
|
2153
|
-
for (const line of lines) {
|
|
2154
|
-
bytes += Buffer.byteLength(line, "utf8") + 1;
|
|
2155
|
-
if (bytes > CHANGE_LIMITS.opDiffBytes)
|
|
2156
|
-
break;
|
|
2157
|
-
kept.push(line);
|
|
2158
|
-
}
|
|
2159
|
-
return { diff: kept.join(`
|
|
2160
|
-
`), truncated: true };
|
|
2161
|
-
}
|
|
2162
2546
|
|
|
2163
2547
|
class ConversationChanges {
|
|
2164
2548
|
last = 0;
|
|
@@ -2166,6 +2550,7 @@ class ConversationChanges {
|
|
|
2166
2550
|
truncated = false;
|
|
2167
2551
|
bytes = 0;
|
|
2168
2552
|
files = new Map;
|
|
2553
|
+
#scripts = new Map;
|
|
2169
2554
|
#pending = new Map;
|
|
2170
2555
|
#agent;
|
|
2171
2556
|
#cwd;
|
|
@@ -2179,6 +2564,8 @@ class ConversationChanges {
|
|
|
2179
2564
|
for (const message of entry.model ?? []) {
|
|
2180
2565
|
if (message.role === "assistant") {
|
|
2181
2566
|
for (const block of message.content) {
|
|
2567
|
+
if (block.type === "toolCall" && block.name === CODEMODE_NAME)
|
|
2568
|
+
this.#scripts.set(block.id, entry.id);
|
|
2182
2569
|
if (block.type !== "toolCall" || block.name !== "edit" && block.name !== "write")
|
|
2183
2570
|
continue;
|
|
2184
2571
|
const path = block.arguments?.path;
|
|
@@ -2186,6 +2573,8 @@ class ConversationChanges {
|
|
|
2186
2573
|
this.#pending.set(block.id, { tool: block.name, path, args: block.arguments });
|
|
2187
2574
|
}
|
|
2188
2575
|
} else if (message.role === "toolResult") {
|
|
2576
|
+
if (this.#scripts.has(message.toolCallId))
|
|
2577
|
+
this.#scripts.set(message.toolCallId, entry.id);
|
|
2189
2578
|
const call = this.#pending.get(message.toolCallId);
|
|
2190
2579
|
if (!call)
|
|
2191
2580
|
continue;
|
|
@@ -2197,8 +2586,20 @@ class ConversationChanges {
|
|
|
2197
2586
|
}
|
|
2198
2587
|
this.last = entry.id;
|
|
2199
2588
|
}
|
|
2589
|
+
withNested(nested) {
|
|
2590
|
+
const visible = nested.items.filter((item) => this.#scripts.has(item.parentCallId));
|
|
2591
|
+
const copy = new ConversationChanges(this.#agent, this.#cwd, this.#workspace);
|
|
2592
|
+
copy.last = this.last;
|
|
2593
|
+
copy.total = this.total;
|
|
2594
|
+
copy.truncated = this.truncated || nested.dropped && visible.length > 0;
|
|
2595
|
+
copy.bytes = this.bytes;
|
|
2596
|
+
for (const [path, file] of this.files)
|
|
2597
|
+
copy.files.set(path, { ...file, agents: new Set(file.agents), items: file.items.map((item) => ({ ...item })) });
|
|
2598
|
+
for (const item of visible)
|
|
2599
|
+
copy.#record({ tool: item.tool, path: item.path }, { diff: item.diff, added: item.added, removed: item.removed, truncated: item.truncated }, this.#scripts.get(item.parentCallId));
|
|
2600
|
+
return copy;
|
|
2601
|
+
}
|
|
2200
2602
|
#add(call, details, seq) {
|
|
2201
|
-
const { path, outside } = normalizeChangePath(call.path, this.#cwd, this.#workspace);
|
|
2202
2603
|
let diff = "";
|
|
2203
2604
|
let added = 0;
|
|
2204
2605
|
let removed = 0;
|
|
@@ -2218,6 +2619,11 @@ class ConversationChanges {
|
|
|
2218
2619
|
({ diff, added, removed, truncated } = synthesized);
|
|
2219
2620
|
}
|
|
2220
2621
|
}
|
|
2622
|
+
this.#record(call, { diff, added, removed, truncated }, seq);
|
|
2623
|
+
}
|
|
2624
|
+
#record(call, shown, seq) {
|
|
2625
|
+
const { path, outside } = normalizeChangePath(call.path, this.#cwd, this.#workspace);
|
|
2626
|
+
const { diff, added, removed, truncated } = shown;
|
|
2221
2627
|
this.total++;
|
|
2222
2628
|
let file = this.files.get(path);
|
|
2223
2629
|
if (!file) {
|
|
@@ -2238,7 +2644,7 @@ class ConversationChanges {
|
|
|
2238
2644
|
file.added += added;
|
|
2239
2645
|
file.removed += removed;
|
|
2240
2646
|
file.agents.add(this.#agent);
|
|
2241
|
-
file.lastSeq = seq;
|
|
2647
|
+
file.lastSeq = Math.max(file.lastSeq, seq);
|
|
2242
2648
|
file.items.push({ seq, agent: this.#agent, tool: call.tool, diff, added, removed, truncated });
|
|
2243
2649
|
this.bytes += Buffer.byteLength(diff, "utf8");
|
|
2244
2650
|
if (file.items.length > CHANGE_LIMITS.opsPerFile) {
|
|
@@ -2340,10 +2746,12 @@ class ChangeTracker {
|
|
|
2340
2746
|
let revision = 0;
|
|
2341
2747
|
let truncated = false;
|
|
2342
2748
|
for (const member of await this.#members(rootId)) {
|
|
2343
|
-
const
|
|
2344
|
-
if (!
|
|
2749
|
+
const synced = await this.#sync(member, workspace);
|
|
2750
|
+
if (!synced)
|
|
2345
2751
|
continue;
|
|
2346
|
-
|
|
2752
|
+
const nested = await this.#harness.snapshot(NestedChangesDoc, member.id, this.#context);
|
|
2753
|
+
const state = nested && nested.items.length > 0 ? synced.withNested(nested) : synced;
|
|
2754
|
+
revision += synced.total + (nested?.total ?? 0);
|
|
2347
2755
|
truncated ||= state.truncated;
|
|
2348
2756
|
for (const file of state.files.values()) {
|
|
2349
2757
|
const existing = merged.get(file.path);
|
|
@@ -2403,17 +2811,13 @@ class ChangeTracker {
|
|
|
2403
2811
|
};
|
|
2404
2812
|
}
|
|
2405
2813
|
}
|
|
2406
|
-
var
|
|
2814
|
+
var MAIN_AGENT = "main";
|
|
2407
2815
|
var init_file_changes = __esm(() => {
|
|
2816
|
+
init_change_limits();
|
|
2817
|
+
init_codemode_mode();
|
|
2818
|
+
init_nested_changes();
|
|
2408
2819
|
init_projection();
|
|
2409
2820
|
init_subagents();
|
|
2410
|
-
CHANGE_LIMITS = {
|
|
2411
|
-
files: 500,
|
|
2412
|
-
opsPerFile: 50,
|
|
2413
|
-
diffBytes: 2 * 1024 * 1024,
|
|
2414
|
-
opDiffBytes: 64 * 1024,
|
|
2415
|
-
conversations: 64
|
|
2416
|
-
};
|
|
2417
2821
|
});
|
|
2418
2822
|
|
|
2419
2823
|
// packages/core/src/images.ts
|
|
@@ -2804,7 +3208,7 @@ var init_interactions = __esm(() => {
|
|
|
2804
3208
|
});
|
|
2805
3209
|
|
|
2806
3210
|
// packages/core/src/jobs.ts
|
|
2807
|
-
import { defineDoc as
|
|
3211
|
+
import { defineDoc as defineDoc5 } from "@earendil-works/pi-durable";
|
|
2808
3212
|
function tailOf(text, maxBytes, maxLines) {
|
|
2809
3213
|
let bytes = encoder2.encode(text);
|
|
2810
3214
|
if (bytes.length > maxBytes) {
|
|
@@ -2910,7 +3314,7 @@ var init_jobs = __esm(() => {
|
|
|
2910
3314
|
commandBytes: 32 * 1024,
|
|
2911
3315
|
maxTimeoutSeconds: 7 * 24 * 3600
|
|
2912
3316
|
};
|
|
2913
|
-
JobsDoc =
|
|
3317
|
+
JobsDoc = defineDoc5({
|
|
2914
3318
|
kind: "app.neko.jobs",
|
|
2915
3319
|
checkpointWhen: () => false,
|
|
2916
3320
|
version: 1,
|
|
@@ -3367,7 +3771,7 @@ class JobRegistry {
|
|
|
3367
3771
|
function reportText(job, tail) {
|
|
3368
3772
|
const body = reportTail(tail);
|
|
3369
3773
|
return [
|
|
3370
|
-
`[Background job ${job.jobId} "${job.title}" ${
|
|
3774
|
+
`[Background job ${job.jobId} "${job.title}" ${describe3(job)}]`,
|
|
3371
3775
|
`Command: ${job.command}`,
|
|
3372
3776
|
body ? `Last output (use the job tool's output action with jobId "${job.jobId}" for more):
|
|
3373
3777
|
${body}` : "It produced no output."
|
|
@@ -3551,7 +3955,7 @@ class JobManager {
|
|
|
3551
3955
|
if (kind === "stop") {
|
|
3552
3956
|
const jobId = stringField(args.jobId, "jobId");
|
|
3553
3957
|
const job = await this.stop(io, api.conversationId, jobId, "model", context);
|
|
3554
|
-
return this.#text(`Job ${job.jobId} ${
|
|
3958
|
+
return this.#text(`Job ${job.jobId} ${describe3(job)}.`);
|
|
3555
3959
|
}
|
|
3556
3960
|
if (kind !== "start")
|
|
3557
3961
|
throw new Error("Invalid job arguments: action must be start, output, stop or list.");
|
|
@@ -3579,7 +3983,7 @@ class JobManager {
|
|
|
3579
3983
|
#listText(items) {
|
|
3580
3984
|
if (!items.length)
|
|
3581
3985
|
return "No jobs.";
|
|
3582
|
-
return items.map((job) => `${job.jobId} [${
|
|
3986
|
+
return items.map((job) => `${job.jobId} [${describe3(job)}] "${job.title}" \u2014 ${job.command}`).join(`
|
|
3583
3987
|
`);
|
|
3584
3988
|
}
|
|
3585
3989
|
async start(io, conversationId, cwd, args, context) {
|
|
@@ -3631,7 +4035,7 @@ class JobManager {
|
|
|
3631
4035
|
return this.#render(job, read.text, read.offset, read.dropped);
|
|
3632
4036
|
}
|
|
3633
4037
|
#render(job, text, offset, dropped) {
|
|
3634
|
-
const lines = [`Job ${job.jobId} ${
|
|
4038
|
+
const lines = [`Job ${job.jobId} ${describe3(job)}.`, `Title: ${job.title}`, `Command: ${job.command}`];
|
|
3635
4039
|
if (dropped > 0)
|
|
3636
4040
|
lines.push(`(${dropped} bytes of earlier output were dropped)`);
|
|
3637
4041
|
lines.push(text ? `Output (next offset ${offset}):
|
|
@@ -3696,7 +4100,7 @@ ${text}` : `No new output (next offset ${offset}).`);
|
|
|
3696
4100
|
this.registry.abortAll();
|
|
3697
4101
|
}
|
|
3698
4102
|
}
|
|
3699
|
-
var plain = (value) => JSON.parse(JSON.stringify(value)), statusWord,
|
|
4103
|
+
var plain = (value) => JSON.parse(JSON.stringify(value)), statusWord, describe3 = (job) => {
|
|
3700
4104
|
const parts = [statusWord[job.status]];
|
|
3701
4105
|
if (job.exitCode !== null)
|
|
3702
4106
|
parts.push(`exit code ${job.exitCode}`);
|
|
@@ -3855,7 +4259,7 @@ var init_plan_bash = __esm(() => {
|
|
|
3855
4259
|
// packages/core/src/user-questions.ts
|
|
3856
4260
|
import { randomUUID } from "crypto";
|
|
3857
4261
|
import {
|
|
3858
|
-
defineDoc as
|
|
4262
|
+
defineDoc as defineDoc6,
|
|
3859
4263
|
defineDocFamily,
|
|
3860
4264
|
defineExtension as defineExtension5,
|
|
3861
4265
|
defineTool as defineTool4
|
|
@@ -4146,7 +4550,7 @@ var init_user_questions = __esm(() => {
|
|
|
4146
4550
|
family: true,
|
|
4147
4551
|
initial: (seed) => seed
|
|
4148
4552
|
});
|
|
4149
|
-
QuestionsDoc =
|
|
4553
|
+
QuestionsDoc = defineDoc6({
|
|
4150
4554
|
kind: "app.neko.questions",
|
|
4151
4555
|
checkpointWhen: () => false,
|
|
4152
4556
|
version: 1,
|
|
@@ -4202,20 +4606,10 @@ import {
|
|
|
4202
4606
|
hook as hook2,
|
|
4203
4607
|
InboxDoc,
|
|
4204
4608
|
ToolTask as ToolTask2,
|
|
4205
|
-
wrapTool
|
|
4609
|
+
wrapTool as wrapTool3
|
|
4206
4610
|
} from "@earendil-works/pi-durable";
|
|
4207
4611
|
import { createBashTool } from "@earendil-works/pi-durable/tools";
|
|
4208
4612
|
import { Type as Type5 } from "typebox";
|
|
4209
|
-
function planAgentChange(mode) {
|
|
4210
|
-
if (mode === "normal")
|
|
4211
|
-
return { tools: null, modelTools: null, instructions: null };
|
|
4212
|
-
const named = (name) => ({ name });
|
|
4213
|
-
return {
|
|
4214
|
-
tools: { remove: PLAN_REMOVED_TOOLS.map(named) },
|
|
4215
|
-
modelTools: { add: [named("submit_plan")] },
|
|
4216
|
-
instructions: PLAN_INSTRUCTIONS
|
|
4217
|
-
};
|
|
4218
|
-
}
|
|
4219
4613
|
function settle2(state, item, status) {
|
|
4220
4614
|
item.status = status;
|
|
4221
4615
|
item.settledAt = Date.now();
|
|
@@ -4418,7 +4812,7 @@ function submitPlanTool(service) {
|
|
|
4418
4812
|
}
|
|
4419
4813
|
});
|
|
4420
4814
|
}
|
|
4421
|
-
var
|
|
4815
|
+
var PLAN_BUSY = "PLAN_BUSY: Wait for the current run to finish or stop it first.", clone2 = (value) => JSON.parse(JSON.stringify(value)), toRecord = ({ taskId: _taskId, dispatched: _dispatched, ...record }) => clone2(record), parameters4, unsafeBash = (command) => `Plan mode is read-only, so this command was not run (it is not on the read-only allowlist): ${command}
|
|
4422
4816
|
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.`, blockedTool = (name) => `Plan mode is read-only: ${name} is unavailable. Research with read-only tools, then call submit_plan.`, createPlanExtension = (service) => defineExtension6({
|
|
4423
4817
|
name: "neko-plan",
|
|
4424
4818
|
tools: [submitPlanTool(service)],
|
|
@@ -4438,7 +4832,7 @@ Use read, find_files or search_text, run a read-only command, or ask the user. I
|
|
|
4438
4832
|
})
|
|
4439
4833
|
],
|
|
4440
4834
|
wraps: [
|
|
4441
|
-
|
|
4835
|
+
wrapTool3(createBashTool(), (bash) => ({
|
|
4442
4836
|
...bash,
|
|
4443
4837
|
execute: async (args, api, context) => {
|
|
4444
4838
|
if ((await api.snapshot(PlanDoc, api.conversationId, context))?.mode === "plan" && !isSafeCommand(args.command))
|
|
@@ -4456,16 +4850,6 @@ var init_plan = __esm(() => {
|
|
|
4456
4850
|
init_subagents();
|
|
4457
4851
|
init_user_questions();
|
|
4458
4852
|
init_plan_doc();
|
|
4459
|
-
PLAN_REMOVED_TOOLS = ["edit", "write", "job"];
|
|
4460
|
-
PLAN_INSTRUCTIONS = [
|
|
4461
|
-
"You are in plan mode: research and plan only, and change nothing.",
|
|
4462
|
-
"- 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.",
|
|
4463
|
-
"- Not available: edit, write, job and any command that modifies files, installs packages or starts processes. Do not try to work around this.",
|
|
4464
|
-
"- 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.",
|
|
4465
|
-
"- After submit_plan do nothing else. If the user asks for changes, research what is needed and submit a revised plan.",
|
|
4466
|
-
"- Use ask_user only for decisions you cannot settle by reading the code."
|
|
4467
|
-
].join(`
|
|
4468
|
-
`);
|
|
4469
4853
|
parameters4 = Type5.Object({
|
|
4470
4854
|
title: Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.titleChars, description: "Short name of the plan." }),
|
|
4471
4855
|
steps: Type5.Array(Type5.String({ minLength: 1, maxLength: PLAN_LIMITS.stepChars }), {
|
|
@@ -4641,7 +5025,7 @@ ${content}
|
|
|
4641
5025
|
const resources = cwd ? await loadResources(input.conversationId, cwd) : { contextFiles: [], skills: [] };
|
|
4642
5026
|
const sections = {
|
|
4643
5027
|
preamble: role ? ROLE_PROMPTS[role] + SUBAGENT_DELIVERY : PREAMBLE,
|
|
4644
|
-
rules: buildRules(input.agent.
|
|
5028
|
+
rules: buildRules(enabledTools(input.agent).map((tool) => tool.name), platform),
|
|
4645
5029
|
project_context: renderProjectContext(resources.contextFiles),
|
|
4646
5030
|
skills: formatSkillsForPrompt(resources.skills) || undefined,
|
|
4647
5031
|
cwd: cwd?.replace(/\\/g, "/")
|
|
@@ -4666,10 +5050,14 @@ ${text}
|
|
|
4666
5050
|
});
|
|
4667
5051
|
};
|
|
4668
5052
|
var init_prompt = __esm(() => {
|
|
5053
|
+
init_agent_change();
|
|
4669
5054
|
init_subagents();
|
|
4670
5055
|
init_skills();
|
|
4671
5056
|
KEYS = ["preamble", "rules", "project_context", "skills", "cwd"];
|
|
4672
5057
|
TOOL_GUIDELINES = {
|
|
5058
|
+
codemode: [
|
|
5059
|
+
"Use codemode to batch independent tool calls (Promise.allSettled), chain them, or filter large output, instead of many separate calls."
|
|
5060
|
+
],
|
|
4673
5061
|
subagent: [
|
|
4674
5062
|
"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."
|
|
4675
5063
|
],
|
|
@@ -4841,7 +5229,7 @@ var createResourceSnapshots = (load, submission) => {
|
|
|
4841
5229
|
import { stripVTControlCharacters } from "util";
|
|
4842
5230
|
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
4843
5231
|
import {
|
|
4844
|
-
defineDoc as
|
|
5232
|
+
defineDoc as defineDoc7,
|
|
4845
5233
|
defineExtension as defineExtension8,
|
|
4846
5234
|
defineTask as defineTask3,
|
|
4847
5235
|
InboxDoc as InboxDoc2,
|
|
@@ -5161,7 +5549,7 @@ var init_session_metadata = __esm(() => {
|
|
|
5161
5549
|
init_input();
|
|
5162
5550
|
init_plan_doc();
|
|
5163
5551
|
init_user_questions();
|
|
5164
|
-
SessionMetadataDoc =
|
|
5552
|
+
SessionMetadataDoc = defineDoc7({
|
|
5165
5553
|
kind: "app.neko.session-metadata",
|
|
5166
5554
|
checkpointWhen: () => false,
|
|
5167
5555
|
version: 1,
|
|
@@ -5188,7 +5576,7 @@ var init_session_metadata = __esm(() => {
|
|
|
5188
5576
|
});
|
|
5189
5577
|
|
|
5190
5578
|
// packages/core/src/todos.ts
|
|
5191
|
-
import { defineDoc as
|
|
5579
|
+
import { defineDoc as defineDoc8, defineExtension as defineExtension9, defineTool as defineTool6 } from "@earendil-works/pi-durable";
|
|
5192
5580
|
import { Type as Type6 } from "typebox";
|
|
5193
5581
|
function renderTodos(list) {
|
|
5194
5582
|
const { completed, total } = todoProgress(list.items);
|
|
@@ -5254,7 +5642,7 @@ var TodosDoc, TodoReceiptDoc, itemSchema, parameters5, structuredOutputSchema3,
|
|
|
5254
5642
|
var init_todos = __esm(() => {
|
|
5255
5643
|
init_interactions();
|
|
5256
5644
|
init_subagents();
|
|
5257
|
-
TodosDoc =
|
|
5645
|
+
TodosDoc = defineDoc8({
|
|
5258
5646
|
kind: "app.neko.todos",
|
|
5259
5647
|
checkpointWhen: () => false,
|
|
5260
5648
|
version: 1,
|
|
@@ -5263,7 +5651,7 @@ var init_todos = __esm(() => {
|
|
|
5263
5651
|
fork: "asOf",
|
|
5264
5652
|
initial: () => ({ revision: 0, items: [] })
|
|
5265
5653
|
});
|
|
5266
|
-
TodoReceiptDoc =
|
|
5654
|
+
TodoReceiptDoc = defineDoc8({
|
|
5267
5655
|
kind: "app.neko.todo-receipt",
|
|
5268
5656
|
checkpointWhen: () => false,
|
|
5269
5657
|
version: 1,
|
|
@@ -5294,11 +5682,11 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
5294
5682
|
import { realpath as realpath2 } from "fs/promises";
|
|
5295
5683
|
import {
|
|
5296
5684
|
configure as configure2,
|
|
5297
|
-
defineDoc as
|
|
5685
|
+
defineDoc as defineDoc9,
|
|
5298
5686
|
defineExtension as defineExtension10,
|
|
5299
5687
|
hook as hook3,
|
|
5300
5688
|
ToolTask as ToolTask3,
|
|
5301
|
-
wrapTool as
|
|
5689
|
+
wrapTool as wrapTool4
|
|
5302
5690
|
} from "@earendil-works/pi-durable";
|
|
5303
5691
|
import { createBashTool as createBashTool2 } from "@earendil-works/pi-durable/tools";
|
|
5304
5692
|
async function live2(tx, id) {
|
|
@@ -5434,7 +5822,7 @@ function createApprovalExtension(service) {
|
|
|
5434
5822
|
})
|
|
5435
5823
|
],
|
|
5436
5824
|
wraps: [
|
|
5437
|
-
|
|
5825
|
+
wrapTool4(createBashTool2(), (bash) => ({
|
|
5438
5826
|
...bash,
|
|
5439
5827
|
execute: async (args, api, context) => {
|
|
5440
5828
|
const role = await api.snapshot(SubagentDoc, api.conversationId, context);
|
|
@@ -5458,15 +5846,16 @@ async function prepareApprovalConversations(harness, ids, extension, context) {
|
|
|
5458
5846
|
const child = await harness.conversation(id, context);
|
|
5459
5847
|
const agent = await child.agent(context);
|
|
5460
5848
|
await harness.commit((tx) => configure2(tx, id, {
|
|
5461
|
-
tools: [...agent.
|
|
5849
|
+
tools: [...enabledTools(agent).filter((t) => t.name !== "bash"), createBashTool2()],
|
|
5462
5850
|
extensions: { add: [extension] }
|
|
5463
5851
|
}), context);
|
|
5464
5852
|
}
|
|
5465
5853
|
}
|
|
5466
5854
|
var ApprovalsDoc;
|
|
5467
5855
|
var init_tool_approvals = __esm(() => {
|
|
5856
|
+
init_agent_change();
|
|
5468
5857
|
init_subagents();
|
|
5469
|
-
ApprovalsDoc =
|
|
5858
|
+
ApprovalsDoc = defineDoc9({
|
|
5470
5859
|
kind: "app.neko.approvals",
|
|
5471
5860
|
checkpointWhen: () => false,
|
|
5472
5861
|
version: 1,
|
|
@@ -6489,7 +6878,7 @@ import { PI_ATTRIBUTION } from "@earendil-works/pi-ai/utils/identity";
|
|
|
6489
6878
|
import {
|
|
6490
6879
|
AssistantEntry as AssistantEntry2,
|
|
6491
6880
|
createRegistry,
|
|
6492
|
-
defineDoc as
|
|
6881
|
+
defineDoc as defineDoc10,
|
|
6493
6882
|
Harness,
|
|
6494
6883
|
InboxDoc as InboxDoc3,
|
|
6495
6884
|
LiveDoc as importLiveDoc,
|
|
@@ -6598,6 +6987,11 @@ async function createNekoRuntime(options = {}) {
|
|
|
6598
6987
|
registry.install(createQuestionExtension(() => questions));
|
|
6599
6988
|
let plans;
|
|
6600
6989
|
registry.install(createPlanExtension(() => plans));
|
|
6990
|
+
const codemode = createCodemodeRuntime({
|
|
6991
|
+
maxWorkers: options.codemodeLimits?.maxWorkers ?? CODEMODE_LIMITS.maxWorkers,
|
|
6992
|
+
waitMs: options.codemodeLimits?.waitMs ?? CODEMODE_LIMITS.waitMs
|
|
6993
|
+
});
|
|
6994
|
+
registry.install(codemode.extension);
|
|
6601
6995
|
for (const extension of options.extensions ?? [])
|
|
6602
6996
|
registry.install(extension);
|
|
6603
6997
|
let approvals;
|
|
@@ -6651,6 +7045,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
6651
7045
|
}
|
|
6652
7046
|
}
|
|
6653
7047
|
} catch (error) {
|
|
7048
|
+
await codemode.close();
|
|
6654
7049
|
await workspaceFiles.close();
|
|
6655
7050
|
await harness.close(context);
|
|
6656
7051
|
throw error;
|
|
@@ -6665,6 +7060,13 @@ async function createNekoRuntime(options = {}) {
|
|
|
6665
7060
|
}
|
|
6666
7061
|
});
|
|
6667
7062
|
enabled = new Set(normalizeEnabledProviders((await harness.snapshot(ProviderSettingsDoc, conversation.id, context))?.enabled));
|
|
7063
|
+
const codemodeState = async () => resolveCodemodeSettings(options.codemodeSettings, (await harness.snapshot(CodemodeSettingsDoc, conversation.id, context))?.mode);
|
|
7064
|
+
if (existing.length === 0) {
|
|
7065
|
+
const initial = (await codemodeState()).effective.mode;
|
|
7066
|
+
await harness.commit(async (tx) => {
|
|
7067
|
+
(await tx.doc(CodemodeDoc, conversation.id)).mode = initial;
|
|
7068
|
+
}, context);
|
|
7069
|
+
}
|
|
6668
7070
|
const savedSearch = (await harness.snapshot(SearchSettingsDoc, conversation.id, context))?.provider;
|
|
6669
7071
|
if (isSearchProvider(savedSearch))
|
|
6670
7072
|
searchProvider = savedSearch;
|
|
@@ -6791,13 +7193,17 @@ async function createNekoRuntime(options = {}) {
|
|
|
6791
7193
|
admissions.delete(id);
|
|
6792
7194
|
}
|
|
6793
7195
|
};
|
|
6794
|
-
const
|
|
6795
|
-
const
|
|
6796
|
-
|
|
6797
|
-
await
|
|
7196
|
+
const syncAgent = async (target) => {
|
|
7197
|
+
const change = agentSyncChange(await target.agent(context), {
|
|
7198
|
+
plan: await plans.mode(target.id),
|
|
7199
|
+
codemode: (await harness.snapshot(CodemodeDoc, target.id, context))?.mode ?? "off",
|
|
7200
|
+
role: "main"
|
|
7201
|
+
});
|
|
7202
|
+
if (change)
|
|
7203
|
+
await target.configure(change, context);
|
|
6798
7204
|
};
|
|
6799
7205
|
const submitTo = async (target, content, submitOptions, extra) => admit(target.id, async () => {
|
|
6800
|
-
await
|
|
7206
|
+
await syncAgent(target);
|
|
6801
7207
|
const agent = await target.agent(context);
|
|
6802
7208
|
const workspace = executionWorkspace(agent.cwd);
|
|
6803
7209
|
const duplicate = submitOptions?.requestId !== undefined && await storage.submissionByRequest(target.id, submitOptions.requestId, context);
|
|
@@ -6961,7 +7367,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
6961
7367
|
if (await isRunning(target.id))
|
|
6962
7368
|
throw new Error(PLAN_BUSY);
|
|
6963
7369
|
await plans.setMode(target.id, mode);
|
|
6964
|
-
await
|
|
7370
|
+
await syncAgent(target);
|
|
6965
7371
|
return plans.view(target.id);
|
|
6966
7372
|
});
|
|
6967
7373
|
},
|
|
@@ -7083,7 +7489,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
7083
7489
|
const session = await getConversation(id);
|
|
7084
7490
|
if (!session)
|
|
7085
7491
|
continue;
|
|
7086
|
-
await admit(id, () =>
|
|
7492
|
+
await admit(id, () => syncAgent(session.conversation));
|
|
7087
7493
|
const pending = await plans.undispatched(id);
|
|
7088
7494
|
if (pending)
|
|
7089
7495
|
await dispatchPlan(session.conversation, pending);
|
|
@@ -7169,6 +7575,8 @@ async function createNekoRuntime(options = {}) {
|
|
|
7169
7575
|
}
|
|
7170
7576
|
const child = at === undefined ? await tx.createConversation({ ownership: { kind: "ownerless" } }) : await tx.forkConversation(id, at, { ownership: { kind: "ownerless" } });
|
|
7171
7577
|
(await tx.doc(DelegationsDoc, child.id)).items = inherited;
|
|
7578
|
+
if (at === undefined)
|
|
7579
|
+
(await tx.doc(CodemodeDoc, child.id)).mode = (await tx.doc(CodemodeDoc, id)).mode;
|
|
7172
7580
|
if (sourceMetadata) {
|
|
7173
7581
|
const doc = await tx.doc(SessionMetadataDoc, child.id);
|
|
7174
7582
|
doc.title = sourceMetadata.title || sourceMetadata.firstPromptPreview;
|
|
@@ -7249,12 +7657,17 @@ async function createNekoRuntime(options = {}) {
|
|
|
7249
7657
|
const workspace = await addWorkspace(createOptions?.cwd ?? defaultCwd);
|
|
7250
7658
|
const savedDefault = (await harness.snapshot(DefaultModelDoc, conversation.id, context))?.model ?? null;
|
|
7251
7659
|
const effective = createOptions?.model ?? modelOverride ?? savedDefault ?? model;
|
|
7660
|
+
const mode = (await codemodeState()).effective.mode;
|
|
7252
7661
|
const created = await harness.createConversation({
|
|
7253
7662
|
ownership: { kind: "ownerless" },
|
|
7254
7663
|
agent: {
|
|
7255
7664
|
model: effective,
|
|
7256
7665
|
thinkingLevel: resolveLevel(effective, createOptions?.thinkingLevel ?? options.thinkingLevel ?? "high"),
|
|
7257
|
-
cwd: workspace.path
|
|
7666
|
+
cwd: workspace.path,
|
|
7667
|
+
...agentChangeFor({ plan: "normal", codemode: mode, role: "main" })
|
|
7668
|
+
},
|
|
7669
|
+
init: async (tx, id) => {
|
|
7670
|
+
(await tx.doc(CodemodeDoc, id)).mode = mode;
|
|
7258
7671
|
}
|
|
7259
7672
|
}, context);
|
|
7260
7673
|
await metadata.initialize(created.id);
|
|
@@ -7309,6 +7722,20 @@ async function createNekoRuntime(options = {}) {
|
|
|
7309
7722
|
async resetContextSettings() {
|
|
7310
7723
|
return persistContext(emptySavedContextSettings());
|
|
7311
7724
|
},
|
|
7725
|
+
codemodeSettings: codemodeState,
|
|
7726
|
+
async setCodemodeMode(mode) {
|
|
7727
|
+
const parsed = parseCodemodeMode(mode);
|
|
7728
|
+
await harness.commit(async (tx) => {
|
|
7729
|
+
(await tx.doc(CodemodeSettingsDoc, conversation.id)).mode = parsed;
|
|
7730
|
+
}, context);
|
|
7731
|
+
return codemodeState();
|
|
7732
|
+
},
|
|
7733
|
+
async resetCodemodeMode() {
|
|
7734
|
+
await harness.commit(async (tx) => {
|
|
7735
|
+
(await tx.doc(CodemodeSettingsDoc, conversation.id)).mode = null;
|
|
7736
|
+
}, context);
|
|
7737
|
+
return codemodeState();
|
|
7738
|
+
},
|
|
7312
7739
|
async searchSettings() {
|
|
7313
7740
|
return { selected: searchProvider };
|
|
7314
7741
|
},
|
|
@@ -7430,6 +7857,7 @@ async function createNekoRuntime(options = {}) {
|
|
|
7430
7857
|
registry.install(extension);
|
|
7431
7858
|
},
|
|
7432
7859
|
async close() {
|
|
7860
|
+
await codemode.close();
|
|
7433
7861
|
await cleanup();
|
|
7434
7862
|
return harness.close(context);
|
|
7435
7863
|
}
|
|
@@ -7437,7 +7865,10 @@ async function createNekoRuntime(options = {}) {
|
|
|
7437
7865
|
}
|
|
7438
7866
|
var DEFAULT_MODEL, DEFAULT_STORAGE_PATH, neverAborted = () => new AbortController().signal, ArchivedDoc, WorkspacesDoc, RemovedWorkspacesDoc, SearchSettingsDoc, ProviderSettingsDoc, DefaultModelDoc, ContextSettingsDoc, ActivityDoc;
|
|
7439
7867
|
var init_runtime = __esm(() => {
|
|
7868
|
+
init_agent_change();
|
|
7440
7869
|
init_agent_file_search();
|
|
7870
|
+
init_codemode();
|
|
7871
|
+
init_codemode_mode();
|
|
7441
7872
|
init_compaction();
|
|
7442
7873
|
init_context_pruning();
|
|
7443
7874
|
init_context_settings();
|
|
@@ -7471,7 +7902,7 @@ var init_runtime = __esm(() => {
|
|
|
7471
7902
|
init_workspace_files();
|
|
7472
7903
|
DEFAULT_MODEL = { provider: "deepseek", modelId: "deepseek-flash" };
|
|
7473
7904
|
DEFAULT_STORAGE_PATH = join7(homedir5(), ".neko", "neko.sqlite");
|
|
7474
|
-
ArchivedDoc =
|
|
7905
|
+
ArchivedDoc = defineDoc10({
|
|
7475
7906
|
kind: "app.neko.conversation",
|
|
7476
7907
|
checkpointWhen: () => false,
|
|
7477
7908
|
version: 1,
|
|
@@ -7480,7 +7911,7 @@ var init_runtime = __esm(() => {
|
|
|
7480
7911
|
fork: "initial",
|
|
7481
7912
|
initial: () => ({ archived: false })
|
|
7482
7913
|
});
|
|
7483
|
-
WorkspacesDoc =
|
|
7914
|
+
WorkspacesDoc = defineDoc10({
|
|
7484
7915
|
kind: "app.neko.workspaces",
|
|
7485
7916
|
checkpointWhen: () => false,
|
|
7486
7917
|
version: 1,
|
|
@@ -7489,7 +7920,7 @@ var init_runtime = __esm(() => {
|
|
|
7489
7920
|
fork: "initial",
|
|
7490
7921
|
initial: () => ({ items: [] })
|
|
7491
7922
|
});
|
|
7492
|
-
RemovedWorkspacesDoc =
|
|
7923
|
+
RemovedWorkspacesDoc = defineDoc10({
|
|
7493
7924
|
kind: "app.neko.workspaces-removed",
|
|
7494
7925
|
checkpointWhen: () => false,
|
|
7495
7926
|
version: 1,
|
|
@@ -7498,7 +7929,7 @@ var init_runtime = __esm(() => {
|
|
|
7498
7929
|
fork: "initial",
|
|
7499
7930
|
initial: () => ({ paths: [] })
|
|
7500
7931
|
});
|
|
7501
|
-
SearchSettingsDoc =
|
|
7932
|
+
SearchSettingsDoc = defineDoc10({
|
|
7502
7933
|
kind: "app.neko.search-settings",
|
|
7503
7934
|
checkpointWhen: () => false,
|
|
7504
7935
|
version: 1,
|
|
@@ -7507,7 +7938,7 @@ var init_runtime = __esm(() => {
|
|
|
7507
7938
|
fork: "initial",
|
|
7508
7939
|
initial: () => ({ provider: null })
|
|
7509
7940
|
});
|
|
7510
|
-
ProviderSettingsDoc =
|
|
7941
|
+
ProviderSettingsDoc = defineDoc10({
|
|
7511
7942
|
kind: "app.neko.provider-settings",
|
|
7512
7943
|
checkpointWhen: () => false,
|
|
7513
7944
|
version: 1,
|
|
@@ -7516,7 +7947,7 @@ var init_runtime = __esm(() => {
|
|
|
7516
7947
|
fork: "initial",
|
|
7517
7948
|
initial: () => ({ enabled: null })
|
|
7518
7949
|
});
|
|
7519
|
-
DefaultModelDoc =
|
|
7950
|
+
DefaultModelDoc = defineDoc10({
|
|
7520
7951
|
kind: "app.neko.default-model",
|
|
7521
7952
|
checkpointWhen: () => false,
|
|
7522
7953
|
version: 1,
|
|
@@ -7525,7 +7956,7 @@ var init_runtime = __esm(() => {
|
|
|
7525
7956
|
fork: "initial",
|
|
7526
7957
|
initial: () => ({ model: null })
|
|
7527
7958
|
});
|
|
7528
|
-
ContextSettingsDoc =
|
|
7959
|
+
ContextSettingsDoc = defineDoc10({
|
|
7529
7960
|
kind: "app.neko.context-settings",
|
|
7530
7961
|
checkpointWhen: () => false,
|
|
7531
7962
|
version: 1,
|
|
@@ -7534,7 +7965,7 @@ var init_runtime = __esm(() => {
|
|
|
7534
7965
|
fork: "initial",
|
|
7535
7966
|
initial: emptySavedContextSettings
|
|
7536
7967
|
});
|
|
7537
|
-
ActivityDoc =
|
|
7968
|
+
ActivityDoc = defineDoc10({
|
|
7538
7969
|
kind: "app.neko.activity",
|
|
7539
7970
|
checkpointWhen: () => false,
|
|
7540
7971
|
version: 1,
|
|
@@ -7559,6 +7990,7 @@ function mergeTranscriptItems(previous, incoming) {
|
|
|
7559
7990
|
|
|
7560
7991
|
// packages/core/src/index.ts
|
|
7561
7992
|
var init_src = __esm(() => {
|
|
7993
|
+
init_codemode_mode();
|
|
7562
7994
|
init_config();
|
|
7563
7995
|
init_context_settings();
|
|
7564
7996
|
init_credentials();
|
|
@@ -7596,8 +8028,9 @@ function contract(name, def) {
|
|
|
7596
8028
|
]));
|
|
7597
8029
|
return remote(name, { ...def, host: hostNamed(GATEWAY_HOST), variants });
|
|
7598
8030
|
}
|
|
7599
|
-
var GATEWAY_HOST = "neko", rpc = (args, result) => [args, result], str, num, bool, nil, integer, nullableString, phase, level, thinking, model2, change, usage, image, skill, call, DelegationSchema, SubagentSummarySchema, jobStatus, JobSummarySchema, JobOutputSchema, QueuedInputSchema, WithdrawResultSchema, SteerResultSchema, TodoItemSchema, TodoListSchema, QuestionOptionSchema, questionStatus, QuestionSummarySchema, WorkspaceEntrySchema, WorkspaceDirSchema, WorkspaceFileSchema, ChangeSummarySchema, ChangedFileSchema, ChangeListSchema, ChangeOpSchema, ChangeDetailSchema, QuestionListSchema, QuestionRecordSchema, QuestionResponseSchema, PlanRecordSchema, PlanViewSchema, PlanDecisionSchema, ToolInteractionSchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, SearchSettingsStateSchema, NekoSearchSettings, contextSource, ContextSettingsStateSchema, nullable = (schema) => Type8.Union([schema, nil]), ContextSettingsPatchSchema, NekoContextSettings, defaultModelSource, DefaultModelStateSchema, NekoDefaultModel, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ShellStatusSchema, ApplicationInfoSchema, NekoApplicationInfo;
|
|
8031
|
+
var GATEWAY_HOST = "neko", rpc = (args, result) => [args, result], str, num, bool, nil, integer, nullableString, phase, level, thinking, model2, change, usage, image, skill, CodemodeViewSchema, call, DelegationSchema, SubagentSummarySchema, jobStatus, JobSummarySchema, JobOutputSchema, QueuedInputSchema, WithdrawResultSchema, SteerResultSchema, TodoItemSchema, TodoListSchema, QuestionOptionSchema, questionStatus, QuestionSummarySchema, WorkspaceEntrySchema, WorkspaceDirSchema, WorkspaceFileSchema, ChangeSummarySchema, ChangedFileSchema, ChangeListSchema, ChangeOpSchema, ChangeDetailSchema, QuestionListSchema, QuestionRecordSchema, QuestionResponseSchema, PlanRecordSchema, PlanViewSchema, PlanDecisionSchema, ToolInteractionSchema, ChatViewSchema, SessionSummarySchema, DirectoryStateSchema, WorkspaceSummarySchema, WorkspaceStateSchema, credentialStatus, CredentialStateSchema, AttachmentSchema, chatInput, NekoSessionDirectory, NekoSessionManagement, resize, inputLimits, modelInfo, NekoModels, imageChunk, NekoSubagent, NekoChat, browse, page, NekoWorkspaces, provider, NekoCredentials, SearchSettingsStateSchema, NekoSearchSettings, contextSource, ContextSettingsStateSchema, nullable = (schema) => Type8.Union([schema, nil]), ContextSettingsPatchSchema, NekoContextSettings, CodemodeSettingsStateSchema, NekoCodemodeSettings, defaultModelSource, DefaultModelStateSchema, NekoDefaultModel, FileCandidatesSchema, NekoWorkspaceFiles, marketSkill, preview, scope, installResult, NekoSkillMarket, ShellStatusSchema, ApplicationInfoSchema, NekoApplicationInfo;
|
|
7600
8032
|
var init_contract = __esm(() => {
|
|
8033
|
+
init_codemode_mode();
|
|
7601
8034
|
init_interactions();
|
|
7602
8035
|
str = Type8.String();
|
|
7603
8036
|
num = Type8.Number();
|
|
@@ -7621,6 +8054,18 @@ var init_contract = __esm(() => {
|
|
|
7621
8054
|
contentIndex: integer
|
|
7622
8055
|
});
|
|
7623
8056
|
skill = Type8.Object({ name: str, location: str, body: str, args: str });
|
|
8057
|
+
CodemodeViewSchema = Type8.Object({
|
|
8058
|
+
calls: Type8.Array(Type8.Object({
|
|
8059
|
+
id: integer,
|
|
8060
|
+
name: str,
|
|
8061
|
+
args: str,
|
|
8062
|
+
status: Type8.Enum(["running", "ok", "error", "cancelled"]),
|
|
8063
|
+
durationMs: Type8.Union([num, nil]),
|
|
8064
|
+
error: nullableString
|
|
8065
|
+
})),
|
|
8066
|
+
omitted: integer,
|
|
8067
|
+
fullOutputPath: nullableString
|
|
8068
|
+
});
|
|
7624
8069
|
call = Type8.Object({
|
|
7625
8070
|
callId: str,
|
|
7626
8071
|
name: str,
|
|
@@ -7631,7 +8076,8 @@ var init_contract = __esm(() => {
|
|
|
7631
8076
|
diff: nullableString,
|
|
7632
8077
|
diffAdded: Type8.Union([num, nil]),
|
|
7633
8078
|
diffRemoved: Type8.Union([num, nil]),
|
|
7634
|
-
diffTruncated: Type8.Optional(bool)
|
|
8079
|
+
diffTruncated: Type8.Optional(bool),
|
|
8080
|
+
codemode: Type8.Optional(CodemodeViewSchema)
|
|
7635
8081
|
});
|
|
7636
8082
|
DelegationSchema = Type8.Object({
|
|
7637
8083
|
parentId: integer,
|
|
@@ -7939,7 +8385,8 @@ var init_contract = __esm(() => {
|
|
|
7939
8385
|
diffAdded: Type8.Union([num, nil]),
|
|
7940
8386
|
diffRemoved: Type8.Union([num, nil]),
|
|
7941
8387
|
diffTruncated: Type8.Optional(bool),
|
|
7942
|
-
interaction: Type8.Optional(ToolInteractionSchema)
|
|
8388
|
+
interaction: Type8.Optional(ToolInteractionSchema),
|
|
8389
|
+
codemode: Type8.Optional(CodemodeViewSchema)
|
|
7943
8390
|
}),
|
|
7944
8391
|
Type8.Object({
|
|
7945
8392
|
type: Type8.Literal("system"),
|
|
@@ -8215,6 +8662,24 @@ var init_contract = __esm(() => {
|
|
|
8215
8662
|
}
|
|
8216
8663
|
}
|
|
8217
8664
|
});
|
|
8665
|
+
CodemodeSettingsStateSchema = Type8.Object({
|
|
8666
|
+
revision: integer,
|
|
8667
|
+
effective: Type8.Object({ mode: Type8.Enum([...CODEMODE_MODES]) }),
|
|
8668
|
+
source: Type8.Enum(["default", "config", "saved"])
|
|
8669
|
+
});
|
|
8670
|
+
NekoCodemodeSettings = contract("neko.codemode-settings", {
|
|
8671
|
+
version: 1,
|
|
8672
|
+
input: Type8.Object({}, { additionalProperties: false }),
|
|
8673
|
+
variants: {
|
|
8674
|
+
ready: {
|
|
8675
|
+
state: { state: CodemodeSettingsStateSchema },
|
|
8676
|
+
methods: {
|
|
8677
|
+
set: rpc(Type8.Object({ mode: Type8.Enum([...CODEMODE_MODES]) }, { additionalProperties: false }), nil),
|
|
8678
|
+
reset: rpc(Type8.Object({}, { additionalProperties: false }), nil)
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
});
|
|
8218
8683
|
defaultModelSource = Type8.Enum(["cli", "saved", "config", "default"]);
|
|
8219
8684
|
DefaultModelStateSchema = Type8.Object({
|
|
8220
8685
|
effective: model2,
|
|
@@ -8702,8 +9167,8 @@ function createGatewayConnection(link, serverId, options = {}) {
|
|
|
8702
9167
|
let business;
|
|
8703
9168
|
let protocolChecked = false;
|
|
8704
9169
|
function bindBusiness() {
|
|
8705
|
-
const directory = get(NekoSessionDirectory), management = get(NekoSessionManagement), models = get(NekoModels), workspaces = get(NekoWorkspaces), workspaceFiles = get(NekoWorkspaceFiles), credentials = get(NekoCredentials), searchSettings = get(NekoSearchSettings), contextSettings = get(NekoContextSettings), defaultModel = get(NekoDefaultModel), skillMarket = get(NekoSkillMarket);
|
|
8706
|
-
keep.push(directory.state.subscribe(() => {}), workspaces.state.subscribe(() => {}), credentials.state.subscribe(() => {}), searchSettings.state.subscribe(() => {}), contextSettings.state.subscribe(() => {}), defaultModel.state.subscribe(() => {}));
|
|
9170
|
+
const directory = get(NekoSessionDirectory), management = get(NekoSessionManagement), models = get(NekoModels), workspaces = get(NekoWorkspaces), workspaceFiles = get(NekoWorkspaceFiles), credentials = get(NekoCredentials), searchSettings = get(NekoSearchSettings), contextSettings = get(NekoContextSettings), codemodeSettings = get(NekoCodemodeSettings), defaultModel = get(NekoDefaultModel), skillMarket = get(NekoSkillMarket);
|
|
9171
|
+
keep.push(directory.state.subscribe(() => {}), workspaces.state.subscribe(() => {}), credentials.state.subscribe(() => {}), searchSettings.state.subscribe(() => {}), contextSettings.state.subscribe(() => {}), codemodeSettings.state.subscribe(() => {}), defaultModel.state.subscribe(() => {}));
|
|
8707
9172
|
return {
|
|
8708
9173
|
directory,
|
|
8709
9174
|
management,
|
|
@@ -8713,6 +9178,7 @@ function createGatewayConnection(link, serverId, options = {}) {
|
|
|
8713
9178
|
credentials,
|
|
8714
9179
|
searchSettings,
|
|
8715
9180
|
contextSettings,
|
|
9181
|
+
codemodeSettings,
|
|
8716
9182
|
defaultModel,
|
|
8717
9183
|
skillMarket
|
|
8718
9184
|
};
|
|
@@ -8740,6 +9206,7 @@ function createGatewayConnection(link, serverId, options = {}) {
|
|
|
8740
9206
|
credentials,
|
|
8741
9207
|
searchSettings,
|
|
8742
9208
|
contextSettings,
|
|
9209
|
+
codemodeSettings,
|
|
8743
9210
|
defaultModel,
|
|
8744
9211
|
workspaceFiles,
|
|
8745
9212
|
skillMarket
|
|
@@ -8752,6 +9219,7 @@ function createGatewayConnection(link, serverId, options = {}) {
|
|
|
8752
9219
|
credentials.ready,
|
|
8753
9220
|
searchSettings.ready,
|
|
8754
9221
|
contextSettings.ready,
|
|
9222
|
+
codemodeSettings.ready,
|
|
8755
9223
|
defaultModel.ready,
|
|
8756
9224
|
workspaceFiles.ready,
|
|
8757
9225
|
skillMarket.ready
|
|
@@ -8796,6 +9264,9 @@ function createGatewayConnection(link, serverId, options = {}) {
|
|
|
8796
9264
|
get contextSettings() {
|
|
8797
9265
|
return services().contextSettings;
|
|
8798
9266
|
},
|
|
9267
|
+
get codemodeSettings() {
|
|
9268
|
+
return services().codemodeSettings;
|
|
9269
|
+
},
|
|
8799
9270
|
get defaultModel() {
|
|
8800
9271
|
return services().defaultModel;
|
|
8801
9272
|
},
|
|
@@ -9710,9 +10181,10 @@ function buildHelloResultSchema() {
|
|
|
9710
10181
|
})
|
|
9711
10182
|
});
|
|
9712
10183
|
}
|
|
9713
|
-
var MOBILE_LIMITS, MOBILE_API_VERSIONS, MOBILE_CAPABILITIES, MOBILE_CLOSE_REVOKED = 4401, MOBILE_CLOSE_INCOMPATIBLE = 4402, MOBILE_CLOSE_SLOW_CONSUMER = 4408, MobileHelloParams, opt, str2, int, nullable2 = (schema) => Type10.Union([schema, Type10.Null()]), MobileContentRefSchema, MobileImageRefSchema, planStatus, questionStatus2, MobileToolInteractionSchema, MobileToolItemSchema, MobileChatItemSchema, MobileStreamingSchema, MobileUsageSchema, MobileSubagentRoundSchema, MobileAgentSchema, MobileQueuedSchema, MobileChatStatusSchema, MobileChatSnapshotSchema, MobileWorkspaceSchema, MobileSessionSummarySchema, MobileApprovalSchema, MobileTodoItemSchema, MobileTodoListSchema, MobileQuestionOptionSchema, MobileQuestionSummarySchema, MobileQuestionListSchema, MobileQuestionSchema, MobileQuestionResponseSchema, MobilePlanSchema, MobilePlanDecisionSchema, sessionListParams, sessionListResult, subscription, pageResult, pageParams, chunkResult, MOBILE_API_V1_METHODS, patchBody, MOBILE_API_V1_NOTIFICATIONS;
|
|
10184
|
+
var MOBILE_LIMITS, MOBILE_API_VERSIONS, MOBILE_CAPABILITIES, MOBILE_CLOSE_REVOKED = 4401, MOBILE_CLOSE_INCOMPATIBLE = 4402, MOBILE_CLOSE_SLOW_CONSUMER = 4408, MobileHelloParams, opt, str2, int, nullable2 = (schema) => Type10.Union([schema, Type10.Null()]), MobileContentRefSchema, MobileImageRefSchema, planStatus, questionStatus2, MobileToolInteractionSchema, MobileCodemodeSchema, MobileToolItemSchema, MobileChatItemSchema, MobileStreamingSchema, MobileUsageSchema, MobileSubagentRoundSchema, MobileAgentSchema, MobileQueuedSchema, MobileChatStatusSchema, MobileChatSnapshotSchema, MobileWorkspaceSchema, MobileSessionSummarySchema, MobileApprovalSchema, MobileTodoItemSchema, MobileTodoListSchema, MobileQuestionOptionSchema, MobileQuestionSummarySchema, MobileQuestionListSchema, MobileQuestionSchema, MobileQuestionResponseSchema, MobilePlanSchema, MobilePlanDecisionSchema, sessionListParams, sessionListResult, subscription, pageResult, pageParams, chunkResult, MOBILE_API_V1_METHODS, patchBody, MOBILE_API_V1_NOTIFICATIONS;
|
|
9714
10185
|
var init_protocol = __esm(() => {
|
|
9715
10186
|
init_application_version();
|
|
10187
|
+
init_codemode_mode();
|
|
9716
10188
|
init_interactions();
|
|
9717
10189
|
MOBILE_LIMITS = {
|
|
9718
10190
|
maxFrameBytes: 262144,
|
|
@@ -9802,6 +10274,23 @@ var init_protocol = __esm(() => {
|
|
|
9802
10274
|
note: str2
|
|
9803
10275
|
})
|
|
9804
10276
|
]);
|
|
10277
|
+
MobileCodemodeSchema = Type10.Object({
|
|
10278
|
+
calls: Type10.Array(Type10.Object({
|
|
10279
|
+
id: int,
|
|
10280
|
+
name: str2,
|
|
10281
|
+
args: Type10.String({ maxLength: CODEMODE_VIEW_LIMITS.argsChars }),
|
|
10282
|
+
status: Type10.Union([
|
|
10283
|
+
Type10.Literal("running"),
|
|
10284
|
+
Type10.Literal("ok"),
|
|
10285
|
+
Type10.Literal("error"),
|
|
10286
|
+
Type10.Literal("cancelled")
|
|
10287
|
+
]),
|
|
10288
|
+
durationMs: opt(Type10.Number()),
|
|
10289
|
+
error: opt(Type10.String({ maxLength: CODEMODE_VIEW_LIMITS.errorChars }))
|
|
10290
|
+
}), { maxItems: CODEMODE_VIEW_LIMITS.maxCalls }),
|
|
10291
|
+
omitted: int,
|
|
10292
|
+
fullOutputPath: opt(str2)
|
|
10293
|
+
});
|
|
9805
10294
|
MobileToolItemSchema = Type10.Object({
|
|
9806
10295
|
callId: str2,
|
|
9807
10296
|
name: str2,
|
|
@@ -9816,7 +10305,8 @@ var init_protocol = __esm(() => {
|
|
|
9816
10305
|
diffTruncated: opt(Type10.Boolean()),
|
|
9817
10306
|
durationMs: opt(Type10.Number()),
|
|
9818
10307
|
note: opt(str2),
|
|
9819
|
-
interaction: opt(MobileToolInteractionSchema)
|
|
10308
|
+
interaction: opt(MobileToolInteractionSchema),
|
|
10309
|
+
codemode: opt(MobileCodemodeSchema)
|
|
9820
10310
|
});
|
|
9821
10311
|
MobileChatItemSchema = Type10.Object({
|
|
9822
10312
|
key: str2,
|
|
@@ -10319,6 +10809,17 @@ function mapChatItem(raw, owner) {
|
|
|
10319
10809
|
tool.note = raw.note;
|
|
10320
10810
|
if (raw.interaction !== undefined)
|
|
10321
10811
|
tool.interaction = raw.interaction;
|
|
10812
|
+
if (raw.codemode !== undefined) {
|
|
10813
|
+
tool.codemode = {
|
|
10814
|
+
calls: raw.codemode.calls.map(({ durationMs, error, ...call }) => ({
|
|
10815
|
+
...call,
|
|
10816
|
+
...durationMs === null ? {} : { durationMs },
|
|
10817
|
+
...error === null ? {} : { error }
|
|
10818
|
+
})),
|
|
10819
|
+
omitted: raw.codemode.omitted,
|
|
10820
|
+
...raw.codemode.fullOutputPath === null ? {} : { fullOutputPath: raw.codemode.fullOutputPath }
|
|
10821
|
+
};
|
|
10822
|
+
}
|
|
10322
10823
|
item = { key: raw.key, type: "tool", tool, ...images(raw.images) };
|
|
10323
10824
|
break;
|
|
10324
10825
|
}
|
|
@@ -31009,12 +31510,13 @@ async function createNekoServerServices(options) {
|
|
|
31009
31510
|
return [spec.id, spec.kind === "model" ? { ...status, enabled: enabled.has(spec.id) } : status];
|
|
31010
31511
|
})));
|
|
31011
31512
|
};
|
|
31012
|
-
const [sessions, workspaces, statuses, search, contextSettings0, defaultModel0] = await Promise.all([
|
|
31513
|
+
const [sessions, workspaces, statuses, search, contextSettings0, codemodeSettings0, defaultModel0] = await Promise.all([
|
|
31013
31514
|
list(),
|
|
31014
31515
|
runtime.listWorkspaces(),
|
|
31015
31516
|
readStatuses(),
|
|
31016
31517
|
runtime.searchSettings(),
|
|
31017
31518
|
runtime.contextSettings(),
|
|
31519
|
+
runtime.codemodeSettings(),
|
|
31018
31520
|
runtime.defaultModel()
|
|
31019
31521
|
]);
|
|
31020
31522
|
let mutationTail = Promise.resolve();
|
|
@@ -31032,6 +31534,10 @@ async function createNekoServerServices(options) {
|
|
|
31032
31534
|
revision: 1,
|
|
31033
31535
|
...contextSettings0
|
|
31034
31536
|
});
|
|
31537
|
+
const codemodeSettings = self2.state("codemodeSettings", CodemodeSettingsStateSchema, {
|
|
31538
|
+
revision: 1,
|
|
31539
|
+
...codemodeSettings0
|
|
31540
|
+
});
|
|
31035
31541
|
const defaultModel = self2.state("defaultModel", DefaultModelStateSchema, defaultModel0);
|
|
31036
31542
|
const refreshContextSettings = async (_input, ctx) => {
|
|
31037
31543
|
const next = await runtime.contextSettings();
|
|
@@ -31040,6 +31546,13 @@ async function createNekoServerServices(options) {
|
|
|
31040
31546
|
contextSettings.replace(ctx, { revision: revision + 1, ...next });
|
|
31041
31547
|
return null;
|
|
31042
31548
|
};
|
|
31549
|
+
const refreshCodemodeSettings = async (_input, ctx) => {
|
|
31550
|
+
const next = await runtime.codemodeSettings();
|
|
31551
|
+
const { revision, ...current } = codemodeSettings.value;
|
|
31552
|
+
if (JSON.stringify(next) !== JSON.stringify(current))
|
|
31553
|
+
codemodeSettings.replace(ctx, { revision: revision + 1, ...next });
|
|
31554
|
+
return null;
|
|
31555
|
+
};
|
|
31043
31556
|
const refreshDefaultModel = async (_input, ctx) => {
|
|
31044
31557
|
const next = await runtime.defaultModel();
|
|
31045
31558
|
if (JSON.stringify(next) !== JSON.stringify(defaultModel.value))
|
|
@@ -31075,6 +31588,7 @@ async function createNekoServerServices(options) {
|
|
|
31075
31588
|
credentials,
|
|
31076
31589
|
searchSettings,
|
|
31077
31590
|
contextSettings,
|
|
31591
|
+
codemodeSettings,
|
|
31078
31592
|
defaultModel,
|
|
31079
31593
|
async refreshCredentials(_input, ctx) {
|
|
31080
31594
|
credentials.replace(ctx, {
|
|
@@ -31085,6 +31599,7 @@ async function createNekoServerServices(options) {
|
|
|
31085
31599
|
},
|
|
31086
31600
|
refreshSearchSettings,
|
|
31087
31601
|
refreshContextSettings,
|
|
31602
|
+
refreshCodemodeSettings,
|
|
31088
31603
|
refreshDefaultModel
|
|
31089
31604
|
};
|
|
31090
31605
|
}, { lifetime: "host" });
|
|
@@ -31151,6 +31666,21 @@ async function createNekoServerServices(options) {
|
|
|
31151
31666
|
})
|
|
31152
31667
|
};
|
|
31153
31668
|
});
|
|
31669
|
+
host.serve(NekoCodemodeSettings, (_input, _principal, self2) => {
|
|
31670
|
+
const hub = shared(self2);
|
|
31671
|
+
return {
|
|
31672
|
+
variant: "ready",
|
|
31673
|
+
state: hub.codemodeSettings,
|
|
31674
|
+
set: (input, ctx) => serialize(async () => {
|
|
31675
|
+
await runtime.setCodemodeMode(input.mode);
|
|
31676
|
+
return hub.refreshCodemodeSettings(null, ctx);
|
|
31677
|
+
}),
|
|
31678
|
+
reset: (_input, ctx) => serialize(async () => {
|
|
31679
|
+
await runtime.resetCodemodeMode();
|
|
31680
|
+
return hub.refreshCodemodeSettings(null, ctx);
|
|
31681
|
+
})
|
|
31682
|
+
};
|
|
31683
|
+
});
|
|
31154
31684
|
host.serve(NekoDefaultModel, (_input, _principal, self2) => {
|
|
31155
31685
|
const hub = shared(self2);
|
|
31156
31686
|
return {
|
|
@@ -31260,10 +31790,12 @@ var init_server_services = __esm(() => {
|
|
|
31260
31790
|
credentials: CredentialStateSchema,
|
|
31261
31791
|
searchSettings: SearchSettingsStateSchema,
|
|
31262
31792
|
contextSettings: ContextSettingsStateSchema,
|
|
31793
|
+
codemodeSettings: CodemodeSettingsStateSchema,
|
|
31263
31794
|
defaultModel: DefaultModelStateSchema,
|
|
31264
31795
|
refreshCredentials: method2(Type13.Null(), Type13.Null()),
|
|
31265
31796
|
refreshSearchSettings: method2(Type13.Null(), Type13.Null()),
|
|
31266
31797
|
refreshContextSettings: method2(Type13.Null(), Type13.Null()),
|
|
31798
|
+
refreshCodemodeSettings: method2(Type13.Null(), Type13.Null()),
|
|
31267
31799
|
refreshDefaultModel: method2(Type13.Null(), Type13.Null())
|
|
31268
31800
|
}
|
|
31269
31801
|
});
|
|
@@ -31295,6 +31827,7 @@ async function startGateway(options = {}) {
|
|
|
31295
31827
|
...options.apiKey !== undefined ? { apiKey: options.apiKey } : {},
|
|
31296
31828
|
...searchProvider !== undefined ? { searchProvider } : {},
|
|
31297
31829
|
...fileConfig.context !== undefined ? { contextSettings: fileConfig.context } : {},
|
|
31830
|
+
...fileConfig.codemode !== undefined ? { codemodeSettings: fileConfig.codemode } : {},
|
|
31298
31831
|
...fileConfig.defaultModel !== undefined ? { model: fileConfig.defaultModel } : {},
|
|
31299
31832
|
...options.model !== undefined ? { modelOverride: options.model } : {},
|
|
31300
31833
|
thinkingLevel: options.thinkingLevel ?? fileConfig.thinkingLevel ?? "high",
|
|
@@ -31874,6 +32407,7 @@ var init_en = __esm(() => {
|
|
|
31874
32407
|
"command.plan": "Toggle plan mode, or review the plan awaiting approval (Shift+Tab)",
|
|
31875
32408
|
"command.search": "Choose the web search provider (Tavily/DeepSeek); K sets its key",
|
|
31876
32409
|
"command.context": "Tune context compaction and tool result pruning",
|
|
32410
|
+
"command.codemode": "Choose the codemode mode (off/on/only) for new sessions",
|
|
31877
32411
|
"command.theme": "Choose color theme (auto/light/dark)",
|
|
31878
32412
|
"command.language": "Choose interface language (zh/en)",
|
|
31879
32413
|
"command.think": "Set thinking level (/think high)",
|
|
@@ -31999,6 +32533,10 @@ var init_en = __esm(() => {
|
|
|
31999
32533
|
"tool.failed": "Failed",
|
|
32000
32534
|
"tool.argumentsHeader": "---- Arguments ----",
|
|
32001
32535
|
"tool.moreLines": "... {count} more lines",
|
|
32536
|
+
"tool.callsHeader": "---- Tool calls ({count}) ----",
|
|
32537
|
+
"tool.callsMore": "{count} more",
|
|
32538
|
+
"tool.scriptHeader": "---- Script ----",
|
|
32539
|
+
"tool.fullOutput": "Full output: {path}",
|
|
32002
32540
|
"tool.diffHeader": "---- Diff ----",
|
|
32003
32541
|
"tool.diffTruncated": "\u2026 Diff preview truncated (line or byte limit)",
|
|
32004
32542
|
"tool.diffMore": "\u2026 +{count} more lines",
|
|
@@ -32012,6 +32550,7 @@ var init_en = __esm(() => {
|
|
|
32012
32550
|
"help.section.plan": "Plan mode",
|
|
32013
32551
|
"help.section.search": "Web search",
|
|
32014
32552
|
"help.section.context": "Context",
|
|
32553
|
+
"help.section.codemode": "Codemode",
|
|
32015
32554
|
"help.section.language": "Language",
|
|
32016
32555
|
"help.section.skills": "Skills",
|
|
32017
32556
|
"help.section.credentials": "Credentials",
|
|
@@ -32091,6 +32630,9 @@ var init_en = __esm(() => {
|
|
|
32091
32630
|
"help.context.tune": "Tune compaction and tool result pruning",
|
|
32092
32631
|
"help.context.choose": "Choose a setting, then a value to save",
|
|
32093
32632
|
"help.context.back": "Back",
|
|
32633
|
+
"help.codemode.choose": "Choose a mode; applies to sessions created afterwards",
|
|
32634
|
+
"help.codemode.apply": "Save the selected mode",
|
|
32635
|
+
"help.codemode.back": "Close the panel",
|
|
32094
32636
|
"help.language.choose": "Choose the interface language (saved as the default)",
|
|
32095
32637
|
"help.skills.user": "Install for user",
|
|
32096
32638
|
"help.skills.project": "Install for project",
|
|
@@ -32328,6 +32870,18 @@ var init_en = __esm(() => {
|
|
|
32328
32870
|
"context.setTo": "{label} set to {value}.",
|
|
32329
32871
|
"context.title": "Context settings",
|
|
32330
32872
|
"context.resetDone": "Context settings reset to defaults.",
|
|
32873
|
+
"codemode.title": "Codemode settings",
|
|
32874
|
+
"codemode.empty": "Codemode settings are unavailable.",
|
|
32875
|
+
"codemode.mode.off": "Off",
|
|
32876
|
+
"codemode.mode.offDescription": "The model calls tools one by one, as before.",
|
|
32877
|
+
"codemode.mode.on": "On",
|
|
32878
|
+
"codemode.mode.onDescription": "Adds codemode next to the usual tools; the model picks what fits.",
|
|
32879
|
+
"codemode.mode.only": "Only codemode",
|
|
32880
|
+
"codemode.mode.onlyDescription": "File and command tools are reachable only through scripts; todo, questions and sub-agents stay direct.",
|
|
32881
|
+
"codemode.reset": "Reset to default",
|
|
32882
|
+
"codemode.resetDescription": "Clear the saved mode. A config file value still applies.",
|
|
32883
|
+
"codemode.setTo": "Codemode set to {mode}. Applies to sessions created afterwards.",
|
|
32884
|
+
"codemode.resetDone": "Codemode reset to default. Applies to sessions created afterwards.",
|
|
32331
32885
|
"subagent.queued": "Queued",
|
|
32332
32886
|
"subagent.running": "Running",
|
|
32333
32887
|
"subagent.done": "Done",
|
|
@@ -32495,6 +33049,7 @@ var init_zh = __esm(() => {
|
|
|
32495
33049
|
"command.plan": "\u5207\u6362\u8BA1\u5212\u6A21\u5F0F\uFF0C\u6216\u67E5\u770B\u5F85\u6279\u51C6\u7684\u8BA1\u5212\uFF08Shift+Tab\uFF09",
|
|
32496
33050
|
"command.search": "\u9009\u62E9\u8054\u7F51\u641C\u7D22\u670D\u52A1\uFF08Tavily/DeepSeek\uFF09\uFF1B\u6309 K \u8BBE\u7F6E\u5176 Key",
|
|
32497
33051
|
"command.context": "\u8C03\u6574\u4E0A\u4E0B\u6587\u538B\u7F29\u4E0E\u5DE5\u5177\u7ED3\u679C\u88C1\u526A",
|
|
33052
|
+
"command.codemode": "\u9009\u62E9 codemode \u6A21\u5F0F\uFF08\u5173\u95ED/\u5F00\u542F/\u4EC5 codemode\uFF09\uFF0C\u5BF9\u65B0\u4F1A\u8BDD\u751F\u6548",
|
|
32498
33053
|
"command.theme": "\u9009\u62E9\u914D\u8272\u4E3B\u9898\uFF08auto/light/dark\uFF09",
|
|
32499
33054
|
"command.language": "\u9009\u62E9\u754C\u9762\u8BED\u8A00\uFF08zh/en\uFF09",
|
|
32500
33055
|
"command.think": "\u8BBE\u7F6E\u601D\u8003\u5F3A\u5EA6\uFF08/think high\uFF09",
|
|
@@ -32620,6 +33175,10 @@ var init_zh = __esm(() => {
|
|
|
32620
33175
|
"tool.failed": "\u5931\u8D25",
|
|
32621
33176
|
"tool.argumentsHeader": "---- \u53C2\u6570 ----",
|
|
32622
33177
|
"tool.moreLines": "... \u8FD8\u6709 {count} \u884C",
|
|
33178
|
+
"tool.callsHeader": "---- \u5DE5\u5177\u8C03\u7528\uFF08{count}\uFF09----",
|
|
33179
|
+
"tool.callsMore": "\u8FD8\u6709 {count} \u6761",
|
|
33180
|
+
"tool.scriptHeader": "---- \u811A\u672C ----",
|
|
33181
|
+
"tool.fullOutput": "\u5B8C\u6574\u8F93\u51FA\uFF1A{path}",
|
|
32623
33182
|
"tool.diffHeader": "---- \u5DEE\u5F02 ----",
|
|
32624
33183
|
"tool.diffTruncated": "\u2026 \u5DEE\u5F02\u9884\u89C8\u5DF2\u622A\u65AD\uFF08\u884C\u6570\u6216\u5B57\u8282\u6570\u4E0A\u9650\uFF09",
|
|
32625
33184
|
"tool.diffMore": "\u2026 \u8FD8\u6709 {count} \u884C",
|
|
@@ -32633,6 +33192,7 @@ var init_zh = __esm(() => {
|
|
|
32633
33192
|
"help.section.plan": "\u8BA1\u5212\u6A21\u5F0F",
|
|
32634
33193
|
"help.section.search": "\u8054\u7F51\u641C\u7D22",
|
|
32635
33194
|
"help.section.context": "\u4E0A\u4E0B\u6587",
|
|
33195
|
+
"help.section.codemode": "Codemode",
|
|
32636
33196
|
"help.section.language": "\u8BED\u8A00",
|
|
32637
33197
|
"help.section.skills": "\u6280\u80FD",
|
|
32638
33198
|
"help.section.credentials": "\u51ED\u636E",
|
|
@@ -32712,6 +33272,9 @@ var init_zh = __esm(() => {
|
|
|
32712
33272
|
"help.context.tune": "\u8C03\u6574\u538B\u7F29\u4E0E\u5DE5\u5177\u7ED3\u679C\u88C1\u526A",
|
|
32713
33273
|
"help.context.choose": "\u9009\u62E9\u8BBE\u7F6E\u9879\uFF0C\u518D\u9009\u62E9\u8981\u4FDD\u5B58\u7684\u503C",
|
|
32714
33274
|
"help.context.back": "\u8FD4\u56DE",
|
|
33275
|
+
"help.codemode.choose": "\u9009\u62E9\u6A21\u5F0F\uFF1B\u53EA\u5BF9\u4E4B\u540E\u65B0\u5EFA\u7684\u4F1A\u8BDD\u751F\u6548",
|
|
33276
|
+
"help.codemode.apply": "\u4FDD\u5B58\u6240\u9009\u6A21\u5F0F",
|
|
33277
|
+
"help.codemode.back": "\u5173\u95ED\u9762\u677F",
|
|
32715
33278
|
"help.language.choose": "\u9009\u62E9\u754C\u9762\u8BED\u8A00\uFF08\u4FDD\u5B58\u4E3A\u9ED8\u8BA4\uFF09",
|
|
32716
33279
|
"help.skills.user": "\u5B89\u88C5\u5230\u7528\u6237\u7EA7",
|
|
32717
33280
|
"help.skills.project": "\u5B89\u88C5\u5230\u9879\u76EE\u7EA7",
|
|
@@ -32949,6 +33512,18 @@ var init_zh = __esm(() => {
|
|
|
32949
33512
|
"context.setTo": "{label} \u5DF2\u8BBE\u4E3A {value}\u3002",
|
|
32950
33513
|
"context.title": "\u4E0A\u4E0B\u6587\u8BBE\u7F6E",
|
|
32951
33514
|
"context.resetDone": "\u4E0A\u4E0B\u6587\u8BBE\u7F6E\u5DF2\u6062\u590D\u9ED8\u8BA4\u3002",
|
|
33515
|
+
"codemode.title": "Codemode \u8BBE\u7F6E",
|
|
33516
|
+
"codemode.empty": "Codemode \u8BBE\u7F6E\u4E0D\u53EF\u7528\u3002",
|
|
33517
|
+
"codemode.mode.off": "\u5173\u95ED",
|
|
33518
|
+
"codemode.mode.offDescription": "\u6A21\u578B\u9010\u4E2A\u8C03\u7528\u5DE5\u5177\uFF0C\u4E0E\u4EE5\u524D\u4E00\u81F4\u3002",
|
|
33519
|
+
"codemode.mode.on": "\u5F00\u542F",
|
|
33520
|
+
"codemode.mode.onDescription": "\u5728\u539F\u6709\u5DE5\u5177\u4E4B\u5916\u589E\u52A0 codemode\uFF0C\u6A21\u578B\u6309\u9700\u9009\u7528\u3002",
|
|
33521
|
+
"codemode.mode.only": "\u4EC5 codemode",
|
|
33522
|
+
"codemode.mode.onlyDescription": "\u6587\u4EF6\u548C\u547D\u4EE4\u7C7B\u5DE5\u5177\u53EA\u80FD\u901A\u8FC7\u811A\u672C\u4F7F\u7528\uFF0Ctodo\u3001\u63D0\u95EE\u4E0E\u5B50 agent \u4ECD\u53EF\u76F4\u63A5\u8C03\u7528\u3002",
|
|
33523
|
+
"codemode.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
33524
|
+
"codemode.resetDescription": "\u6E05\u9664\u5DF2\u4FDD\u5B58\u7684\u6A21\u5F0F\u3002\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684\u503C\u4ECD\u7136\u6709\u6548\u3002",
|
|
33525
|
+
"codemode.setTo": "Codemode \u5DF2\u8BBE\u4E3A{mode}\uFF0C\u5BF9\u4E4B\u540E\u65B0\u5EFA\u7684\u4F1A\u8BDD\u751F\u6548\u3002",
|
|
33526
|
+
"codemode.resetDone": "Codemode \u5DF2\u6062\u590D\u9ED8\u8BA4\uFF0C\u5BF9\u4E4B\u540E\u65B0\u5EFA\u7684\u4F1A\u8BDD\u751F\u6548\u3002",
|
|
32952
33527
|
"subagent.queued": "\u6392\u961F\u4E2D",
|
|
32953
33528
|
"subagent.running": "\u8FD0\u884C\u4E2D",
|
|
32954
33529
|
"subagent.done": "\u5DF2\u5B8C\u6210",
|
|
@@ -33368,13 +33943,21 @@ function imageLabel(image) {
|
|
|
33368
33943
|
const size = image.width > 0 && image.height > 0 ? ` \xB7 ${image.width}\xD7${image.height}` : "";
|
|
33369
33944
|
return `${image.name} \xB7 ${image.mimeType}${size}`;
|
|
33370
33945
|
}
|
|
33946
|
+
function codemodeCallLine(call) {
|
|
33947
|
+
return [
|
|
33948
|
+
`${call.name}${call.args === "" ? "" : ` ${oneLine(call.args)}`}`,
|
|
33949
|
+
call.status,
|
|
33950
|
+
call.durationMs === null ? "" : formatDuration(call.durationMs),
|
|
33951
|
+
call.error === null ? "" : oneLine(call.error)
|
|
33952
|
+
].filter(Boolean).join(" \xB7 ");
|
|
33953
|
+
}
|
|
33371
33954
|
function formatDuration(ms) {
|
|
33372
33955
|
return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
33373
33956
|
}
|
|
33374
33957
|
function oneLine(text) {
|
|
33375
33958
|
return text.replace(/\s+/g, " ").trim();
|
|
33376
33959
|
}
|
|
33377
|
-
var FALLBACK_EXPORT_TITLE = "neko-session"
|
|
33960
|
+
var FALLBACK_EXPORT_TITLE = "neko-session", codemodeOmittedLine = (omitted) => `\u2026 ${omitted} more calls not listed`;
|
|
33378
33961
|
|
|
33379
33962
|
// packages/core/src/input-history.ts
|
|
33380
33963
|
function promptHistory(items) {
|
|
@@ -44990,7 +45573,12 @@ async function exportHtml(source, options) {
|
|
|
44990
45573
|
status === "ok" ? "" : `<span class="status">[${status}]</span>`,
|
|
44991
45574
|
item.durationMs === null ? "" : `<span class="duration">(${formatDuration(item.durationMs)})</span>`
|
|
44992
45575
|
].filter(Boolean).join(" ");
|
|
44993
|
-
|
|
45576
|
+
const calls = item.codemode === undefined || item.codemode.calls.length === 0 ? "" : `<div class="label">Calls</div><pre>${escapeHtml([
|
|
45577
|
+
...item.codemode.calls.map(codemodeCallLine),
|
|
45578
|
+
...item.codemode.omitted > 0 ? [codemodeOmittedLine(item.codemode.omitted)] : []
|
|
45579
|
+
].join(`
|
|
45580
|
+
`))}</pre>`;
|
|
45581
|
+
body.push(`<details class="tool ${status}"><summary>${heading}</summary>${item.detail === null || item.detail === "" ? "" : `<pre${item.codemode === undefined ? "" : ' class="language-javascript"'}>${escapeHtml(item.detail)}</pre>`}${calls}${output === "" ? "" : `<div class="label">${status === "running" ? "Output so far" : "Result"}</div><pre>${escapeHtml(output)}</pre>`}${item.note === null ? "" : `<div class="note">${escapeHtml(oneLine(item.note))}</div>`}${renderImages(item.images, embedded)}</details>`);
|
|
44994
45582
|
}
|
|
44995
45583
|
}
|
|
44996
45584
|
const content = `<!doctype html>
|
|
@@ -45106,12 +45694,12 @@ async function exportConversation(format, source, options) {
|
|
|
45106
45694
|
const content = format === "markdown" ? exportMarkdown(source, options) : exportJson(source, options);
|
|
45107
45695
|
return { content, mimeType, extension, skippedImages: 0 };
|
|
45108
45696
|
}
|
|
45109
|
-
function block(content) {
|
|
45697
|
+
function block(content, language = "") {
|
|
45110
45698
|
let longest = 0;
|
|
45111
45699
|
for (const run of content.matchAll(/`+/g))
|
|
45112
45700
|
longest = Math.max(longest, run[0].length);
|
|
45113
45701
|
const marks = "`".repeat(Math.max(3, longest + 1));
|
|
45114
|
-
return `${marks}
|
|
45702
|
+
return `${marks}${language}
|
|
45115
45703
|
${content}
|
|
45116
45704
|
${marks}`;
|
|
45117
45705
|
}
|
|
@@ -45169,7 +45757,14 @@ function markdownTool(item) {
|
|
|
45169
45757
|
].filter(Boolean).join(" ");
|
|
45170
45758
|
const lines = ["<details>", `<summary>${escapeInline(label)}</summary>`, ""];
|
|
45171
45759
|
if (item.detail !== null && item.detail !== "")
|
|
45172
|
-
lines.push(block(item.detail), "");
|
|
45760
|
+
lines.push(block(item.detail, item.codemode === undefined ? "" : "javascript"), "");
|
|
45761
|
+
if (item.codemode !== undefined && item.codemode.calls.length > 0) {
|
|
45762
|
+
const calls = item.codemode.calls.map(codemodeCallLine);
|
|
45763
|
+
if (item.codemode.omitted > 0)
|
|
45764
|
+
calls.push(codemodeOmittedLine(item.codemode.omitted));
|
|
45765
|
+
lines.push("Calls:", "", block(calls.join(`
|
|
45766
|
+
`)), "");
|
|
45767
|
+
}
|
|
45173
45768
|
const output = toolOutput(item);
|
|
45174
45769
|
if (output !== "")
|
|
45175
45770
|
lines.push(status === "running" ? "Output so far:" : "Result:", "", block(output), "");
|
|
@@ -45212,6 +45807,7 @@ function exportJson(source, options) {
|
|
|
45212
45807
|
status: item.phase,
|
|
45213
45808
|
isError: item.isError,
|
|
45214
45809
|
result: toolOutput(item),
|
|
45810
|
+
...item.codemode === undefined ? {} : { codemode: item.codemode },
|
|
45215
45811
|
durationMs: item.durationMs,
|
|
45216
45812
|
note: item.note,
|
|
45217
45813
|
images: (item.images ?? []).map(({ name, mimeType, width, height, bytes }) => ({
|
|
@@ -45348,6 +45944,56 @@ var init_clipboard_write = __esm(() => {
|
|
|
45348
45944
|
init_i18n();
|
|
45349
45945
|
});
|
|
45350
45946
|
|
|
45947
|
+
// packages/tui/src/codemode-ui.ts
|
|
45948
|
+
function codemodeItems(state) {
|
|
45949
|
+
if (!state)
|
|
45950
|
+
return [];
|
|
45951
|
+
const items = CODEMODE_MODES.map((mode) => ({
|
|
45952
|
+
value: mode,
|
|
45953
|
+
label: t(MODE_TEXT[mode].label),
|
|
45954
|
+
...state.effective.mode === mode ? { rightLabel: `${t("common.currentSuffix").trim()} ${t(SOURCES[state.source])}` } : {},
|
|
45955
|
+
description: t(MODE_TEXT[mode].description)
|
|
45956
|
+
}));
|
|
45957
|
+
items.push({
|
|
45958
|
+
value: CODEMODE_RESET_VALUE,
|
|
45959
|
+
label: t("codemode.reset"),
|
|
45960
|
+
description: t("codemode.resetDescription")
|
|
45961
|
+
});
|
|
45962
|
+
return items;
|
|
45963
|
+
}
|
|
45964
|
+
function openCodemodePanel(panel, client, notify) {
|
|
45965
|
+
const items = () => codemodeItems(client.codemodeSettings.state.value);
|
|
45966
|
+
const stop = client.codemodeSettings.state.subscribe(() => panel.update(items()));
|
|
45967
|
+
panel.emptyText = codemodeEmpty();
|
|
45968
|
+
panel.show(t("codemode.title"), async () => items(), async (value) => {
|
|
45969
|
+
if (value === CODEMODE_RESET_VALUE) {
|
|
45970
|
+
await client.codemodeSettings.reset({});
|
|
45971
|
+
notify(t("codemode.resetDone"));
|
|
45972
|
+
return;
|
|
45973
|
+
}
|
|
45974
|
+
if (!isCodemodeMode(value))
|
|
45975
|
+
return;
|
|
45976
|
+
await client.codemodeSettings.set({ mode: value });
|
|
45977
|
+
notify(t("codemode.setTo", { mode: t(MODE_TEXT[value].label) }));
|
|
45978
|
+
});
|
|
45979
|
+
return stop;
|
|
45980
|
+
}
|
|
45981
|
+
var codemodeEmpty = () => t("codemode.empty"), CODEMODE_RESET_VALUE = "reset", MODE_TEXT, SOURCES;
|
|
45982
|
+
var init_codemode_ui = __esm(() => {
|
|
45983
|
+
init_codemode_mode();
|
|
45984
|
+
init_i18n();
|
|
45985
|
+
MODE_TEXT = {
|
|
45986
|
+
off: { label: "codemode.mode.off", description: "codemode.mode.offDescription" },
|
|
45987
|
+
on: { label: "codemode.mode.on", description: "codemode.mode.onDescription" },
|
|
45988
|
+
only: { label: "codemode.mode.only", description: "codemode.mode.onlyDescription" }
|
|
45989
|
+
};
|
|
45990
|
+
SOURCES = {
|
|
45991
|
+
default: "context.source.default",
|
|
45992
|
+
config: "context.source.config",
|
|
45993
|
+
saved: "context.source.saved"
|
|
45994
|
+
};
|
|
45995
|
+
});
|
|
45996
|
+
|
|
45351
45997
|
// packages/tui/src/i18n/core-text.ts
|
|
45352
45998
|
function localizeStatus(text) {
|
|
45353
45999
|
for (const { pattern, show } of STATUS) {
|
|
@@ -45686,6 +46332,7 @@ var commands = () => [
|
|
|
45686
46332
|
{ name: "plan", description: t("command.plan") },
|
|
45687
46333
|
{ name: "search", description: t("command.search") },
|
|
45688
46334
|
{ name: "context", description: t("command.context") },
|
|
46335
|
+
{ name: "codemode", description: t("command.codemode") },
|
|
45689
46336
|
{ name: "theme", description: t("command.theme") },
|
|
45690
46337
|
{ name: "language", description: t("command.language") },
|
|
45691
46338
|
{ name: "think", description: t("command.think") },
|
|
@@ -47266,6 +47913,12 @@ function answerSummary(interaction) {
|
|
|
47266
47913
|
const parts = [...interaction.labels ?? [], ...interaction.text ? [interaction.text] : []];
|
|
47267
47914
|
return plainLine(parts.join(", "), 80);
|
|
47268
47915
|
}
|
|
47916
|
+
function codemodeCallLine2(call) {
|
|
47917
|
+
const glyph = call.status === "running" ? theme.accent(currentToolFrame()) : call.status === "ok" ? theme.ok("\u2713") : call.status === "error" ? theme.error("\u2717") : theme.dim("\u21B7");
|
|
47918
|
+
const duration = call.durationMs === null ? "" : theme.dim(` ${(call.durationMs / 1000).toFixed(1)}s`);
|
|
47919
|
+
const error = call.error === null ? "" : ` ${theme.error(plainLine(call.error, 120))}`;
|
|
47920
|
+
return ` ${glyph} ${plainLine(call.name, 40)} ${theme.dim(plainLine(call.args, 100))}${duration}${error}`;
|
|
47921
|
+
}
|
|
47269
47922
|
function diffCountSuffix(state) {
|
|
47270
47923
|
if (state.diffAdded === null)
|
|
47271
47924
|
return "";
|
|
@@ -47278,7 +47931,7 @@ function clipLines(text, maxLines) {
|
|
|
47278
47931
|
return { visible: lines.slice(0, maxLines).join(`
|
|
47279
47932
|
`), rest: Math.max(0, lines.length - maxLines) };
|
|
47280
47933
|
}
|
|
47281
|
-
var SPINNER_FRAMES, spinnerFrame = 0, DIFF_MAX_LINES = 50, ToolExecutionComponent;
|
|
47934
|
+
var SPINNER_FRAMES, spinnerFrame = 0, CODEMODE_COLLAPSED_CALLS = 8, DIFF_MAX_LINES = 50, ToolExecutionComponent;
|
|
47282
47935
|
var init_tool_execution = __esm(() => {
|
|
47283
47936
|
init_core_text();
|
|
47284
47937
|
init_i18n();
|
|
@@ -47388,19 +48041,33 @@ var init_tool_execution = __esm(() => {
|
|
|
47388
48041
|
}
|
|
47389
48042
|
rebuild() {
|
|
47390
48043
|
const state = this.#state;
|
|
47391
|
-
const plain = state.name === "ask_user" || state.name === "todo" || state.name === "submit_plan";
|
|
48044
|
+
const plain = state.name === "ask_user" || state.name === "todo" || state.name === "submit_plan" || state.codemode !== undefined;
|
|
47392
48045
|
const parts = [
|
|
47393
48046
|
`${theme.toolTitle(`\u26A1 ${state.name}`)}`,
|
|
47394
48047
|
theme.dim((plain ? plainLine(state.summary) : state.summary) + diffCountSuffix(state)),
|
|
47395
48048
|
this.#statusLine(state.phase)
|
|
47396
48049
|
];
|
|
47397
48050
|
let text = parts.join(" ");
|
|
48051
|
+
const codemode = state.codemode;
|
|
48052
|
+
if (codemode !== undefined) {
|
|
48053
|
+
const shown = this.#expanded ? codemode.calls : codemode.calls.slice(0, CODEMODE_COLLAPSED_CALLS);
|
|
48054
|
+
if (this.#expanded && shown.length + codemode.omitted > 0)
|
|
48055
|
+
text += `
|
|
48056
|
+
${theme.dim(t("tool.callsHeader", { count: shown.length + codemode.omitted }))}`;
|
|
48057
|
+
for (const call of shown)
|
|
48058
|
+
text += `
|
|
48059
|
+
${codemodeCallLine2(call)}`;
|
|
48060
|
+
const rest = codemode.calls.length - shown.length + codemode.omitted;
|
|
48061
|
+
if (rest > 0)
|
|
48062
|
+
text += `
|
|
48063
|
+
${theme.dim(t("tool.callsMore", { count: rest }))}`;
|
|
48064
|
+
}
|
|
47398
48065
|
if (this.#expanded) {
|
|
47399
48066
|
const detail = state.detail !== null && plain ? plainText(state.detail) : state.detail;
|
|
47400
48067
|
if (detail !== null && !(state.diff !== null && state.name === "write" && !state.diffTruncated)) {
|
|
47401
48068
|
const clipped = clipLines(detail, 20);
|
|
47402
48069
|
text += `
|
|
47403
|
-
${theme.dim(t("tool.argumentsHeader"))}
|
|
48070
|
+
${theme.dim(t(codemode === undefined ? "tool.argumentsHeader" : "tool.scriptHeader"))}
|
|
47404
48071
|
${clipped.visible}`;
|
|
47405
48072
|
if (clipped.rest > 0)
|
|
47406
48073
|
text += `
|
|
@@ -47431,6 +48098,10 @@ ${theme.dim(t("tool.diffMore", { count: clipped.rest }))}`;
|
|
|
47431
48098
|
if (this.#expanded && body !== null && body !== "") {
|
|
47432
48099
|
text += `
|
|
47433
48100
|
${theme.toolOutput(body)}`;
|
|
48101
|
+
}
|
|
48102
|
+
if (this.#expanded && codemode?.fullOutputPath != null) {
|
|
48103
|
+
text += `
|
|
48104
|
+
${theme.dim(t("tool.fullOutput", { path: plainLine(codemode.fullOutputPath, 300) }))}`;
|
|
47434
48105
|
}
|
|
47435
48106
|
if (state.note !== null) {
|
|
47436
48107
|
text += `
|
|
@@ -47584,7 +48255,7 @@ function contextItems(state) {
|
|
|
47584
48255
|
items.push({
|
|
47585
48256
|
value: row.key,
|
|
47586
48257
|
label: t(row.label),
|
|
47587
|
-
rightLabel: `${format(row.kind, value)} ${t(
|
|
48258
|
+
rightLabel: `${format(row.kind, value)} ${t(SOURCES2[source])}${custom ? ` \xB7 ${t("context.custom")}` : ""}`,
|
|
47588
48259
|
description: t(row.description)
|
|
47589
48260
|
});
|
|
47590
48261
|
}
|
|
@@ -47634,7 +48305,7 @@ class PresetDetail {
|
|
|
47634
48305
|
head.push(theme.error(t("common.error", { message: this.#error })));
|
|
47635
48306
|
const options = this.#options.map((option, i) => {
|
|
47636
48307
|
const custom = option !== null && !this.#row.presets.includes(option);
|
|
47637
|
-
const text = `${format(this.#row.kind, option)}${custom ? ` (${t("context.custom")})` : ""}${option === null && current ? ` - ${t("context.now", { source: t(
|
|
48308
|
+
const text = `${format(this.#row.kind, option)}${custom ? ` (${t("context.custom")})` : ""}${option === null && current ? ` - ${t("context.now", { source: t(SOURCES2[current.source]) })}` : ""}`;
|
|
47638
48309
|
return i === this.#index ? theme.selection(`\u276F ${text}`) : ` ${text}`;
|
|
47639
48310
|
});
|
|
47640
48311
|
const count = Math.max(1, height - head.length);
|
|
@@ -47684,7 +48355,7 @@ function openContextPanel(panel, client, notify) {
|
|
|
47684
48355
|
});
|
|
47685
48356
|
return stop;
|
|
47686
48357
|
}
|
|
47687
|
-
var contextEmpty = () => t("context.empty"), CONTEXT_ROWS,
|
|
48358
|
+
var contextEmpty = () => t("context.empty"), CONTEXT_ROWS, SOURCES2, RESET_VALUE = "reset", message = (error) => error instanceof Error ? error.message : String(error);
|
|
47688
48359
|
var init_context_ui = __esm(() => {
|
|
47689
48360
|
init_shortcut_hints();
|
|
47690
48361
|
init_i18n();
|
|
@@ -47763,7 +48434,7 @@ var init_context_ui = __esm(() => {
|
|
|
47763
48434
|
presets: [2, 5, 10, 15]
|
|
47764
48435
|
}
|
|
47765
48436
|
];
|
|
47766
|
-
|
|
48437
|
+
SOURCES2 = {
|
|
47767
48438
|
default: "context.source.default",
|
|
47768
48439
|
config: "context.source.config",
|
|
47769
48440
|
saved: "context.source.saved"
|
|
@@ -48872,6 +49543,11 @@ function helpLines(platform = process.platform, wsl = runningInWsl()) {
|
|
|
48872
49543
|
["Enter", t("help.context.choose")],
|
|
48873
49544
|
["Esc", t("help.context.back")]
|
|
48874
49545
|
]);
|
|
49546
|
+
section(t("help.section.codemode"), [
|
|
49547
|
+
["/codemode", t("help.codemode.choose")],
|
|
49548
|
+
["Enter", t("help.codemode.apply")],
|
|
49549
|
+
["Esc", t("help.codemode.back")]
|
|
49550
|
+
]);
|
|
48875
49551
|
section(t("help.section.language"), [["/language", t("help.language.choose")]]);
|
|
48876
49552
|
section(t("help.section.skills"), [
|
|
48877
49553
|
["U", t("help.skills.user")],
|
|
@@ -50890,6 +51566,7 @@ class NekoTuiApp {
|
|
|
50890
51566
|
#queueRow = new QueueRow;
|
|
50891
51567
|
#searchStop;
|
|
50892
51568
|
#contextStop;
|
|
51569
|
+
#codemodeStop;
|
|
50893
51570
|
#modelStop;
|
|
50894
51571
|
#agentDetails = new Map;
|
|
50895
51572
|
#sessionId = "";
|
|
@@ -50962,6 +51639,8 @@ class NekoTuiApp {
|
|
|
50962
51639
|
this.#searchStop = undefined;
|
|
50963
51640
|
this.#contextStop?.();
|
|
50964
51641
|
this.#contextStop = undefined;
|
|
51642
|
+
this.#codemodeStop?.();
|
|
51643
|
+
this.#codemodeStop = undefined;
|
|
50965
51644
|
this.#modelStop?.();
|
|
50966
51645
|
this.#modelStop = undefined;
|
|
50967
51646
|
clearInterval(this.#sessionPanelTimer);
|
|
@@ -51213,6 +51892,10 @@ class NekoTuiApp {
|
|
|
51213
51892
|
this.#showContext();
|
|
51214
51893
|
return;
|
|
51215
51894
|
}
|
|
51895
|
+
if (trimmed === "/codemode") {
|
|
51896
|
+
this.#showCodemode();
|
|
51897
|
+
return;
|
|
51898
|
+
}
|
|
51216
51899
|
if (trimmed === "/language") {
|
|
51217
51900
|
this.openLanguageOverlay();
|
|
51218
51901
|
return;
|
|
@@ -51863,6 +52546,12 @@ class NekoTuiApp {
|
|
|
51863
52546
|
this.#contextStop = openContextPanel(this.#panel, this.#gateway.client, (message) => this.addNotice(message));
|
|
51864
52547
|
this.#ui.setFocus(this.#panel);
|
|
51865
52548
|
}
|
|
52549
|
+
#showCodemode() {
|
|
52550
|
+
if (this.#exitStarted || this.#panel.open || this.#login.open)
|
|
52551
|
+
return;
|
|
52552
|
+
this.#codemodeStop = openCodemodePanel(this.#panel, this.#gateway.client, (message) => this.addNotice(message));
|
|
52553
|
+
this.#ui.setFocus(this.#panel);
|
|
52554
|
+
}
|
|
51866
52555
|
#agentDetail(target) {
|
|
51867
52556
|
const round = typeof target === "number" ? this.#lastView?.subagents?.find((r) => r.taskId === target) : undefined;
|
|
51868
52557
|
const id = typeof target === "string" ? target : round?.agentId ?? `agent-${round?.childId}`;
|
|
@@ -52218,7 +52907,12 @@ function assistantContent(item) {
|
|
|
52218
52907
|
}
|
|
52219
52908
|
function mergeStream(item, streamCall) {
|
|
52220
52909
|
if (streamCall !== undefined && streamCall.phase !== "done") {
|
|
52221
|
-
return {
|
|
52910
|
+
return {
|
|
52911
|
+
...item,
|
|
52912
|
+
phase: streamCall.phase,
|
|
52913
|
+
output: streamCall.output,
|
|
52914
|
+
codemode: streamCall.codemode ?? item.codemode
|
|
52915
|
+
};
|
|
52222
52916
|
}
|
|
52223
52917
|
return item;
|
|
52224
52918
|
}
|
|
@@ -52236,7 +52930,8 @@ function streamingCardState(call) {
|
|
|
52236
52930
|
diff: call.diff,
|
|
52237
52931
|
diffAdded: call.diffAdded,
|
|
52238
52932
|
diffRemoved: call.diffRemoved,
|
|
52239
|
-
diffTruncated: call.diffTruncated
|
|
52933
|
+
diffTruncated: call.diffTruncated,
|
|
52934
|
+
codemode: call.codemode
|
|
52240
52935
|
};
|
|
52241
52936
|
}
|
|
52242
52937
|
function rawMessageOf(error) {
|
|
@@ -52299,6 +52994,7 @@ var init_tui = __esm(() => {
|
|
|
52299
52994
|
init_client_session();
|
|
52300
52995
|
init_approval_dock();
|
|
52301
52996
|
init_clipboard_write();
|
|
52997
|
+
init_codemode_ui();
|
|
52302
52998
|
init_compaction_status();
|
|
52303
52999
|
init_assistant_message();
|
|
52304
53000
|
init_editor();
|