@botiverse/kimi-code-sdk 0.19.2-botiverse.0 → 0.20.0-botiverse.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +1 -1
- package/README.md +1 -1
- package/dist/index.d.mts +174 -6
- package/dist/index.mjs +803 -194
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -795,7 +795,7 @@ const KIMI_ERROR_INFO = {
|
|
|
795
795
|
title: "Turn exceeded max steps",
|
|
796
796
|
retryable: false,
|
|
797
797
|
public: true,
|
|
798
|
-
action: "Increase loop_control.max_steps_per_turn or split the task."
|
|
798
|
+
action: "Increase loop_control.max_steps_per_turn in config.toml or split the task."
|
|
799
799
|
},
|
|
800
800
|
"provider.api_error": {
|
|
801
801
|
title: "Provider API error",
|
|
@@ -51668,7 +51668,7 @@ function withTelemetryProperties(telemetry, defaults) {
|
|
|
51668
51668
|
* Loop-local error helpers.
|
|
51669
51669
|
*/
|
|
51670
51670
|
function createMaxStepsExceededError(maxSteps, message) {
|
|
51671
|
-
return new KimiError(ErrorCodes.LOOP_MAX_STEPS_EXCEEDED, message ?? `Turn exceeded maxSteps=${maxSteps}
|
|
51671
|
+
return new KimiError(ErrorCodes.LOOP_MAX_STEPS_EXCEEDED, message ?? `Turn exceeded maxSteps=${maxSteps}. If max_steps_per_turn is too small, raise it in config.toml (loop_control.max_steps_per_turn), or run "/update-config" to update it, then "/reload".`, { details: { maxSteps } });
|
|
51672
51672
|
}
|
|
51673
51673
|
function isMaxStepsExceededError(error) {
|
|
51674
51674
|
return isKimiError(error) && error.code === ErrorCodes.LOOP_MAX_STEPS_EXCEEDED;
|
|
@@ -51698,6 +51698,37 @@ function timeoutOutcome(timeoutMs, outcome) {
|
|
|
51698
51698
|
timeout = void 0;
|
|
51699
51699
|
} });
|
|
51700
51700
|
}
|
|
51701
|
+
/**
|
|
51702
|
+
* Like `timeoutOutcome`, but the timer can be restarted via `reset()` while the
|
|
51703
|
+
* returned promise stays the same — so a `Promise.race` that already captured it
|
|
51704
|
+
* observes the new deadline. Used to extend a task's timeout (e.g. when a
|
|
51705
|
+
* foreground command is detached to the background).
|
|
51706
|
+
*/
|
|
51707
|
+
function resettableTimeoutOutcome(initialMs, outcome) {
|
|
51708
|
+
let timer;
|
|
51709
|
+
let resolvePromise;
|
|
51710
|
+
const promise = new Promise((resolve) => {
|
|
51711
|
+
resolvePromise = resolve;
|
|
51712
|
+
});
|
|
51713
|
+
const clear = () => {
|
|
51714
|
+
if (timer === void 0) return;
|
|
51715
|
+
clearTimeout(timer);
|
|
51716
|
+
timer = void 0;
|
|
51717
|
+
};
|
|
51718
|
+
const reset = (timeoutMs) => {
|
|
51719
|
+
clear();
|
|
51720
|
+
if (timeoutMs === void 0 || timeoutMs <= 0) return;
|
|
51721
|
+
timer = setTimeout(() => {
|
|
51722
|
+
timer = void 0;
|
|
51723
|
+
resolvePromise(outcome);
|
|
51724
|
+
}, timeoutMs);
|
|
51725
|
+
};
|
|
51726
|
+
reset(initialMs);
|
|
51727
|
+
return Object.assign(promise, {
|
|
51728
|
+
reset,
|
|
51729
|
+
clear
|
|
51730
|
+
});
|
|
51731
|
+
}
|
|
51701
51732
|
//#endregion
|
|
51702
51733
|
//#region ../agent-core/src/utils/xml-escape.ts
|
|
51703
51734
|
/** Escape XML content — escapes both tag and attribute boundary chars (& < > ") */
|
|
@@ -51723,14 +51754,11 @@ function escapeXmlTags(input) {
|
|
|
51723
51754
|
* Title: ...
|
|
51724
51755
|
* Severity: ...
|
|
51725
51756
|
* <body>
|
|
51726
|
-
* <
|
|
51727
|
-
* <truncated tail>
|
|
51728
|
-
* </task-notification>
|
|
51757
|
+
* <children...>
|
|
51729
51758
|
* </notification>
|
|
51730
51759
|
*
|
|
51731
|
-
* The opening
|
|
51732
|
-
*
|
|
51733
|
-
* — rename requires updating the detector too.
|
|
51760
|
+
* The opening tag name (`<notification `) is load-bearing for notification
|
|
51761
|
+
* consumers that detect chat-history injections.
|
|
51734
51762
|
*
|
|
51735
51763
|
* `agent_id` is emitted only for background_task notifications whose
|
|
51736
51764
|
* source task is an agent subagent — surfacing it structurally lets the
|
|
@@ -51749,33 +51777,15 @@ function renderNotificationXml(data) {
|
|
|
51749
51777
|
const title = typeof data["title"] === "string" ? data["title"] : "";
|
|
51750
51778
|
const severity = typeof data["severity"] === "string" ? data["severity"] : "";
|
|
51751
51779
|
const body = typeof data["body"] === "string" ? data["body"] : "";
|
|
51780
|
+
const children = childBlocks(data["children"] ?? data["extraBlocks"]);
|
|
51752
51781
|
const lines = [`<notification id="${id}" category="${category}" type="${type}" source_kind="${sourceKind}" source_id="${sourceId}"${agentId === void 0 ? "" : ` agent_id="${agentId}"`}>`];
|
|
51753
51782
|
if (title.length > 0) lines.push(`Title: ${title}`);
|
|
51754
51783
|
if (severity.length > 0) lines.push(`Severity: ${severity}`);
|
|
51755
51784
|
if (body.length > 0) lines.push(body);
|
|
51756
|
-
|
|
51757
|
-
const tailRaw = typeof data["tail_output"] === "string" ? data["tail_output"] : "";
|
|
51758
|
-
if (tailRaw.length > 0) {
|
|
51759
|
-
const truncated = truncateTailOutput(tailRaw, 20, 3e3);
|
|
51760
|
-
lines.push("<task-notification>");
|
|
51761
|
-
lines.push(truncated);
|
|
51762
|
-
lines.push("</task-notification>");
|
|
51763
|
-
}
|
|
51764
|
-
}
|
|
51785
|
+
lines.push(...children);
|
|
51765
51786
|
lines.push("</notification>");
|
|
51766
51787
|
return lines.join("\n");
|
|
51767
51788
|
}
|
|
51768
|
-
/**
|
|
51769
|
-
* Truncate tail output to at most `maxLines` lines and `maxChars`
|
|
51770
|
-
* characters. Takes the *last* N lines, then trims from the front if
|
|
51771
|
-
* the character budget is exceeded.
|
|
51772
|
-
*/
|
|
51773
|
-
function truncateTailOutput(raw, maxLines, maxChars) {
|
|
51774
|
-
const allLines = raw.split("\n");
|
|
51775
|
-
let result = (allLines.length > maxLines ? allLines.slice(-maxLines) : allLines).join("\n");
|
|
51776
|
-
if (result.length > maxChars) result = result.slice(-maxChars);
|
|
51777
|
-
return result;
|
|
51778
|
-
}
|
|
51779
51789
|
function stringAttr$1(value, fallback) {
|
|
51780
51790
|
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
51781
51791
|
return escapeXmlAttr(value);
|
|
@@ -51786,6 +51796,11 @@ function optionalStringAttr(value) {
|
|
|
51786
51796
|
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
51787
51797
|
return value.replaceAll("&", "&").replaceAll("\"", """);
|
|
51788
51798
|
}
|
|
51799
|
+
function childBlocks(value) {
|
|
51800
|
+
if (typeof value === "string" && value.length > 0) return [value];
|
|
51801
|
+
if (!Array.isArray(value)) return [];
|
|
51802
|
+
return value.filter((item) => typeof item === "string" && item.length > 0);
|
|
51803
|
+
}
|
|
51789
51804
|
//#endregion
|
|
51790
51805
|
//#region ../agent-core/src/utils/per-id-json-store.ts
|
|
51791
51806
|
/**
|
|
@@ -52357,6 +52372,7 @@ function isBackgroundTaskTerminal(status) {
|
|
|
52357
52372
|
* reads the persisted log when available.
|
|
52358
52373
|
*/
|
|
52359
52374
|
const MAX_OUTPUT_BYTES$1 = 1024 * 1024;
|
|
52375
|
+
const NOTIFICATION_FALLBACK_PREVIEW_BYTES = 3e3;
|
|
52360
52376
|
const SIGTERM_GRACE_MS$1 = 5e3;
|
|
52361
52377
|
const USER_INTERRUPT_REASON$1 = "Interrupted by user";
|
|
52362
52378
|
const _ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
@@ -52382,7 +52398,6 @@ function emptyOutputSnapshot() {
|
|
|
52382
52398
|
preview: ""
|
|
52383
52399
|
};
|
|
52384
52400
|
}
|
|
52385
|
-
const NOTIFICATION_TAIL_BYTES = 3e3;
|
|
52386
52401
|
var BackgroundManager = class {
|
|
52387
52402
|
agent;
|
|
52388
52403
|
persistence;
|
|
@@ -52446,6 +52461,7 @@ var BackgroundManager = class {
|
|
|
52446
52461
|
const entryOptions = {
|
|
52447
52462
|
detached,
|
|
52448
52463
|
timeoutMs: options.timeoutMs ?? task.timeoutMs,
|
|
52464
|
+
detachTimeoutMs: options.detachTimeoutMs,
|
|
52449
52465
|
signal: detached ? void 0 : options.signal
|
|
52450
52466
|
};
|
|
52451
52467
|
this.assertCanRegister(detached);
|
|
@@ -52569,6 +52585,7 @@ var BackgroundManager = class {
|
|
|
52569
52585
|
const foregroundRelease = entry.foregroundRelease;
|
|
52570
52586
|
if (foregroundRelease === void 0) return this.toInfo(entry);
|
|
52571
52587
|
entry.foregroundRelease = void 0;
|
|
52588
|
+
if (entry.options.detachTimeoutMs !== void 0) entry.timeoutHandle?.reset(entry.options.detachTimeoutMs);
|
|
52572
52589
|
try {
|
|
52573
52590
|
entry.task.onDetach?.();
|
|
52574
52591
|
} catch {}
|
|
@@ -52578,6 +52595,11 @@ var BackgroundManager = class {
|
|
|
52578
52595
|
foregroundRelease.resolve("detached");
|
|
52579
52596
|
return this.toInfo(entry);
|
|
52580
52597
|
}
|
|
52598
|
+
persistOutput(taskId) {
|
|
52599
|
+
const entry = this.tasks.get(taskId);
|
|
52600
|
+
if (entry === void 0) return;
|
|
52601
|
+
this.startOutputPersist(entry);
|
|
52602
|
+
}
|
|
52581
52603
|
/** Stop a running task. SIGTERM → 5s grace → SIGKILL. */
|
|
52582
52604
|
async stop(taskId, reason) {
|
|
52583
52605
|
const entry = this.tasks.get(taskId);
|
|
@@ -52753,7 +52775,8 @@ var BackgroundManager = class {
|
|
|
52753
52775
|
if (this.scheduledNotificationKeys.has(key)) return;
|
|
52754
52776
|
if (this.deliveredNotificationKeys.has(key)) return;
|
|
52755
52777
|
this.scheduledNotificationKeys.add(key);
|
|
52756
|
-
|
|
52778
|
+
let output = await this.getOutputSnapshot(info.taskId, 0);
|
|
52779
|
+
if (!output.fullOutputAvailable) output = await this.getOutputSnapshot(info.taskId, NOTIFICATION_FALLBACK_PREVIEW_BYTES);
|
|
52757
52780
|
if (this.isTerminalNotificationSuppressed(info.taskId)) return void 0;
|
|
52758
52781
|
const notification = {
|
|
52759
52782
|
id: origin.notificationId,
|
|
@@ -52765,7 +52788,7 @@ var BackgroundManager = class {
|
|
|
52765
52788
|
title: `Background ${info.kind} ${info.status}`,
|
|
52766
52789
|
severity: info.status === "completed" ? "info" : "warning",
|
|
52767
52790
|
body: buildBackgroundTaskNotificationBody(info),
|
|
52768
|
-
|
|
52791
|
+
children: backgroundTaskNotificationChildren(output)
|
|
52769
52792
|
};
|
|
52770
52793
|
return {
|
|
52771
52794
|
content: [{
|
|
@@ -52817,7 +52840,8 @@ var BackgroundManager = class {
|
|
|
52817
52840
|
stopReason: entry.abortController.signal.aborted ? void 0 : errorMessage$2(error)
|
|
52818
52841
|
});
|
|
52819
52842
|
});
|
|
52820
|
-
const timeout =
|
|
52843
|
+
const timeout = resettableTimeoutOutcome(entry.options.timeoutMs, { kind: "timeout" });
|
|
52844
|
+
entry.timeoutHandle = timeout;
|
|
52821
52845
|
const outcome = await Promise.race([
|
|
52822
52846
|
worker.then((settlement) => ({
|
|
52823
52847
|
kind: "worker",
|
|
@@ -52829,7 +52853,10 @@ var BackgroundManager = class {
|
|
|
52829
52853
|
request
|
|
52830
52854
|
})),
|
|
52831
52855
|
this.signalOutcome(entry)
|
|
52832
|
-
]).finally(() =>
|
|
52856
|
+
]).finally(() => {
|
|
52857
|
+
timeout.clear();
|
|
52858
|
+
entry.timeoutHandle = void 0;
|
|
52859
|
+
});
|
|
52833
52860
|
const settlement = await this.settlementForOutcome(entry, outcome, worker);
|
|
52834
52861
|
await this.finalizeTask(entry, settlement);
|
|
52835
52862
|
}
|
|
@@ -52898,6 +52925,26 @@ var BackgroundManager = class {
|
|
|
52898
52925
|
return entry.task.toInfo(base);
|
|
52899
52926
|
}
|
|
52900
52927
|
};
|
|
52928
|
+
function backgroundTaskNotificationChildren(output) {
|
|
52929
|
+
if (output.fullOutputAvailable && output.outputPath !== void 0) return [renderOutputFileBlock(output.outputPath, output.outputSizeBytes)];
|
|
52930
|
+
if (output.preview.length === 0) return void 0;
|
|
52931
|
+
return [renderOutputPreviewBlock(output)];
|
|
52932
|
+
}
|
|
52933
|
+
function renderOutputFileBlock(outputPath, outputSizeBytes) {
|
|
52934
|
+
return [
|
|
52935
|
+
`<output-file path="${escapeXmlAttr(outputPath)}" bytes="${String(outputSizeBytes)}">`,
|
|
52936
|
+
`Read the output file to retrieve the result: ${escapeXml(outputPath)}`,
|
|
52937
|
+
"</output-file>"
|
|
52938
|
+
].join("\n");
|
|
52939
|
+
}
|
|
52940
|
+
function renderOutputPreviewBlock(output) {
|
|
52941
|
+
return [
|
|
52942
|
+
`<output-preview bytes="${String(output.previewBytes)}" total_bytes="${String(output.outputSizeBytes)}" truncated="${String(output.truncated)}">`,
|
|
52943
|
+
output.truncated ? `Showing the last ${String(output.previewBytes)} bytes. No persisted full output is available.` : "No persisted full output is available; this preview is the currently buffered task output.",
|
|
52944
|
+
escapeXml(output.preview),
|
|
52945
|
+
"</output-preview>"
|
|
52946
|
+
].join("\n");
|
|
52947
|
+
}
|
|
52901
52948
|
function notificationKey(origin) {
|
|
52902
52949
|
return `${origin.taskId}\0${origin.status}\0${origin.notificationId}`;
|
|
52903
52950
|
}
|
|
@@ -61836,6 +61883,23 @@ var ContextMemory = class {
|
|
|
61836
61883
|
origin
|
|
61837
61884
|
});
|
|
61838
61885
|
}
|
|
61886
|
+
/**
|
|
61887
|
+
* Inject a user-invisible message and immediately send it to the model by
|
|
61888
|
+
* launching/steering a turn. The content is used as-is (no wrapper tag), so
|
|
61889
|
+
* callers can pass raw tool-result-style text or wrap it themselves. The
|
|
61890
|
+
* message is skipped on replay / transcript (so the user never sees it) but
|
|
61891
|
+
* is included in the context sent to the model. Use this for events the
|
|
61892
|
+
* model must react to right away without surfacing a user-visible message.
|
|
61893
|
+
*/
|
|
61894
|
+
injectAndNotify(content, origin) {
|
|
61895
|
+
this.agent.turn.steer([{
|
|
61896
|
+
type: "text",
|
|
61897
|
+
text: content
|
|
61898
|
+
}], origin ?? {
|
|
61899
|
+
kind: "injection",
|
|
61900
|
+
variant: "system_reminder"
|
|
61901
|
+
});
|
|
61902
|
+
}
|
|
61839
61903
|
appendLocalCommandStdout(content) {
|
|
61840
61904
|
const text = `<local-command-stdout>\n${content.trim()}\n</local-command-stdout>`;
|
|
61841
61905
|
this.appendMessage({
|
|
@@ -61851,6 +61915,40 @@ var ContextMemory = class {
|
|
|
61851
61915
|
}
|
|
61852
61916
|
});
|
|
61853
61917
|
}
|
|
61918
|
+
appendBashInput(command) {
|
|
61919
|
+
const text = `<bash-input>\n${escapeXml(command)}\n</bash-input>`;
|
|
61920
|
+
this.appendMessage({
|
|
61921
|
+
role: "user",
|
|
61922
|
+
content: [{
|
|
61923
|
+
type: "text",
|
|
61924
|
+
text
|
|
61925
|
+
}],
|
|
61926
|
+
toolCalls: [],
|
|
61927
|
+
origin: {
|
|
61928
|
+
kind: "shell_command",
|
|
61929
|
+
phase: "input"
|
|
61930
|
+
}
|
|
61931
|
+
});
|
|
61932
|
+
}
|
|
61933
|
+
appendBashOutput(stdout, stderr, isError) {
|
|
61934
|
+
const text = `<bash-stdout>${escapeXml(stdout)}</bash-stdout><bash-stderr>${escapeXml(stderr)}</bash-stderr>`;
|
|
61935
|
+
this.appendMessage({
|
|
61936
|
+
role: "user",
|
|
61937
|
+
content: [{
|
|
61938
|
+
type: "text",
|
|
61939
|
+
text
|
|
61940
|
+
}],
|
|
61941
|
+
toolCalls: [],
|
|
61942
|
+
origin: isError === true ? {
|
|
61943
|
+
kind: "shell_command",
|
|
61944
|
+
phase: "output",
|
|
61945
|
+
isError: true
|
|
61946
|
+
} : {
|
|
61947
|
+
kind: "shell_command",
|
|
61948
|
+
phase: "output"
|
|
61949
|
+
}
|
|
61950
|
+
});
|
|
61951
|
+
}
|
|
61854
61952
|
popMatchedMessage(matcher) {
|
|
61855
61953
|
const lastDeferred = this.deferredMessages.at(-1);
|
|
61856
61954
|
const last = lastDeferred ?? this._history.at(-1);
|
|
@@ -63082,6 +63180,33 @@ var PermissionModeInjector = class extends DynamicInjector {
|
|
|
63082
63180
|
};
|
|
63083
63181
|
//#endregion
|
|
63084
63182
|
//#region ../agent-core/src/agent/injection/plugin-session-start.ts
|
|
63183
|
+
/**
|
|
63184
|
+
* Renders the `<plugin_session_start>` reminder blocks for the currently enabled
|
|
63185
|
+
* plugin session starts. Returns `undefined` when there is nothing to render
|
|
63186
|
+
* (no session starts, no registry, or no resolvable skills).
|
|
63187
|
+
*
|
|
63188
|
+
* Shared by the turn-loop injector (which dedups against history) and the
|
|
63189
|
+
* explicit `/reload` flow (which force-appends a fresh reminder).
|
|
63190
|
+
*/
|
|
63191
|
+
function renderPluginSessionStartReminder(input) {
|
|
63192
|
+
const { sessionStarts, registry, log } = input;
|
|
63193
|
+
if (sessionStarts.length === 0) return void 0;
|
|
63194
|
+
if (registry === void 0) return void 0;
|
|
63195
|
+
const blocks = [];
|
|
63196
|
+
for (const sessionStart of sessionStarts) {
|
|
63197
|
+
const skill = registry.getPluginSkill(sessionStart.pluginId, sessionStart.skillName);
|
|
63198
|
+
if (skill === void 0) {
|
|
63199
|
+
log?.warn("plugin sessionStart skill not found", {
|
|
63200
|
+
pluginId: sessionStart.pluginId,
|
|
63201
|
+
skillName: sessionStart.skillName
|
|
63202
|
+
});
|
|
63203
|
+
continue;
|
|
63204
|
+
}
|
|
63205
|
+
blocks.push(renderSessionStartBlock(sessionStart, skill, registry.renderSkillPrompt(skill, "")));
|
|
63206
|
+
}
|
|
63207
|
+
if (blocks.length === 0) return void 0;
|
|
63208
|
+
return blocks.join("\n");
|
|
63209
|
+
}
|
|
63085
63210
|
var PluginSessionStartInjector = class extends DynamicInjector {
|
|
63086
63211
|
injectionVariant = "plugin_session_start";
|
|
63087
63212
|
async getInjection() {
|
|
@@ -63091,24 +63216,11 @@ var PluginSessionStartInjector = class extends DynamicInjector {
|
|
|
63091
63216
|
this.injectedAt = replayedAt;
|
|
63092
63217
|
return;
|
|
63093
63218
|
}
|
|
63094
|
-
|
|
63095
|
-
|
|
63096
|
-
|
|
63097
|
-
|
|
63098
|
-
|
|
63099
|
-
for (const sessionStart of sessionStarts) {
|
|
63100
|
-
const skill = registry.getPluginSkill(sessionStart.pluginId, sessionStart.skillName);
|
|
63101
|
-
if (skill === void 0) {
|
|
63102
|
-
this.agent.log.warn("plugin sessionStart skill not found", {
|
|
63103
|
-
pluginId: sessionStart.pluginId,
|
|
63104
|
-
skillName: sessionStart.skillName
|
|
63105
|
-
});
|
|
63106
|
-
continue;
|
|
63107
|
-
}
|
|
63108
|
-
blocks.push(renderSessionStartBlock(sessionStart, skill, registry.renderSkillPrompt(skill, "")));
|
|
63109
|
-
}
|
|
63110
|
-
if (blocks.length === 0) return void 0;
|
|
63111
|
-
return blocks.join("\n");
|
|
63219
|
+
return renderPluginSessionStartReminder({
|
|
63220
|
+
sessionStarts: this.agent.pluginSessionStarts,
|
|
63221
|
+
registry: this.agent.skills?.registry,
|
|
63222
|
+
log: this.agent.log
|
|
63223
|
+
});
|
|
63112
63224
|
}
|
|
63113
63225
|
};
|
|
63114
63226
|
function renderSessionStartBlock(sessionStart, skill, skillContent) {
|
|
@@ -69252,7 +69364,7 @@ function isRecord$2(value) {
|
|
|
69252
69364
|
}
|
|
69253
69365
|
//#endregion
|
|
69254
69366
|
//#region ../agent-core/src/skill/builtin/custom-theme.md?raw
|
|
69255
|
-
var custom_theme_default = "---\nname: custom-theme\ndescription: Create or edit a kimi-code custom color theme — a JSON file under the resolved KIMI_CODE_HOME data directory that recolors the TUI. Use when the user wants their own theme, asks for a specific palette or mood, or wants to tweak an existing custom theme's colors.\n---\n\n# Create a kimi-code custom theme (custom-theme)\n\nHelp the user design, write, and apply a custom color theme for the kimi-code TUI. A theme is a single JSON file; the TUI ships with `dark`, `light`, and `auto`, and any file the user adds becomes selectable alongside them.\n\n## Rules of engagement\n\n- **Never write a theme until the user has explicitly clarified what they want.** This skill may only run after the user has confirmed light vs dark, the style or mood, any specific colors they care about, and the intended filename. If any of these are missing, ask before creating files.\n- **Never assume the data directory is `~/.kimi-code`.** Always resolve `$KIMI_CODE_HOME` first with the Bash command below.\n- **Never edit a live theme file in place.** Always create a `.json.new` candidate, validate it, back up the old file, and then `mv` it into place.\n- **Never overwrite an existing theme without reading it first.** Read, back up, then overwrite only after the user confirms.\n\n## Where a theme lives\n\nThe kimi-code runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Theme files live inside the `themes/` subdirectory of that data directory.\n\nBefore doing anything, resolve the actual data root with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved data root — **never assume `~/.kimi-code`**. Theme files live at `<KIMI_CODE_HOME>/themes/<name>.json`. Create the `themes/` directory if it doesn't exist.\n\n## What a theme is\n\n- A theme lives at `<KIMI_CODE_HOME>/themes/<name>.json`.\n- **The filename is the theme name**: `ember.json` shows up in the `/theme` picker as `Custom: ember`.\n- Shape:\n\n ```json\n {\n \"name\": \"ember\",\n \"displayName\": \"Ember\",\n \"colors\": {\n \"primary\": \"#83A598\",\n \"accent\": \"#FE8019\"\n }\n }\n ```\n\n - `name` (required), `displayName` (optional), `base` (optional: `\"dark\"` default, or `\"light\"`), `colors` (each value a 6-digit hex `#RRGGBB`).\n- **Partial themes are fine**: any token you leave out falls back to the **base** palette (`dark` by default; set `\"base\": \"light\"` for a light theme), so you can recolor just a few tokens or all of them.\n\n## Source of truth: the docs token reference\n\nBefore choosing colors, use **FetchURL** to fetch the official custom-theme docs as the authoritative list of tokens and what each controls:\n\n```\nhttps://moonshotai.github.io/kimi-code/en/customization/themes.html\n```\n\nOnly set tokens from this set — unknown keys are silently ignored at load. If FetchURL is unavailable or the fetch fails, fall back to the embedded reference below (it mirrors the same tokens) and tell the user you're working from the built-in list rather than the live docs.\n\n## Color tokens (what each controls)\n\n| Token | Controls |\n| --- | --- |\n| `primary` | The most-used color: links, inline code, the selected item in nearly every dialog, the focused editor border, plan/\"running\" badges, spinners |\n| `accent` | Secondary highlight: approval `▶` prefix, device-code box, image placeholder, BTW / queue panes, registry import |\n| `text` | Body text: dialog bodies, todo titles, footer model label, Markdown headings, assistant/tool message bullets, list bullets |\n| `textStrong` | Emphasized / bold text: input dialogs, status messages |\n| `textDim` | Secondary, dimmed text (the most widely used dim shade): thinking, hints, descriptions, completed todos, Markdown quotes, footer status bar |\n| `textMuted` | Faintest text: counters, scroll info, descriptions, Markdown link URLs, code-block borders |\n| `border` | Pane and editor borders, Markdown horizontal rule |\n| `borderFocus` | Focus / attention border (currently only the approval panel) |\n| `success` | Success state: `✓`, \"enabled\", completed |\n| `warning` | Warning state: auto/yolo badges, stale markers, plan-mode hint |\n| `error` | Error state: error messages, failed tool output |\n| `diffAdded` | Diff added lines |\n| `diffRemoved` | Diff removed lines |\n| `diffAddedStrong` | Diff intra-line changed words, added (bold) |\n| `diffRemovedStrong` | Diff intra-line changed words, removed (bold) |\n| `diffGutter` | Diff line-number gutter |\n| `diffMeta` | Diff meta / hunk headers |\n| `roleUser` | User message bullet and text, skill-activation name (the one role color with its own hue) |\n\n## Workflow\n\n1. **Ask the user what they want first — before choosing any colors.** Clarify, in one short exchange:\n - **Light or dark?** A light theme (dark text on a light background) or a dark theme (light text on a dark background). This sets the whole direction, so settle it first. For a light theme, set `\"base\": \"light\"` so the tokens you leave out inherit the light palette instead of dark.\n - **What style / mood?** e.g. warm vs cool, vivid vs muted, high vs low contrast, a named vibe (\"nord\", \"solarized\", \"sunset\"), or a base to start from (an existing theme, or `dark` / `light`).\n - **Any specific colors?** Whether they have exact hex values to anchor on (a brand color, a preferred `primary`, etc.).\n\n For the discrete choices (light vs dark, a few style options), prefer **AskUserQuestion** if it is available. If you are running in **auto mode** and `AskUserQuestion` is unavailable, ask the same question as a plain-text message with clear numbered or bulleted options, and wait for the user's reply. Don't start picking colors until you at least know light-vs-dark and the rough style.\n\n2. **Resolve the actual theme directory and current theme(s).**\n - Resolve the data root by checking `echo \"$KIMI_CODE_HOME\"`; if empty, use `echo \"$HOME/.kimi-code\"`. Use `<root>/themes` for every subsequent step.\n - If tweaking an existing custom theme, **Read** `<KIMI_CODE_HOME>/themes/<name>.json` first — never overwrite a theme you haven't read.\n - Starting fresh: build a `colors` object from the token table. You can `ls <KIMI_CODE_HOME>/themes/` and Read one of the user's existing themes as a reference for the format.\n\n3. **Pick a starting point and choose colors deliberately.**\n - Every value is a 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Keep contrast usable against the user's terminal background: don't let `text` / `textDim` sit too close to the background, and keep `success` / `warning` / `error` clearly distinguishable from each other.\n - `primary` is the most-seen color (links, selection, focus) — make it readable and distinct from `text`.\n - `roleUser` is the one role color meant to stand on its own — give it a distinct hue.\n\n4. **Create a candidate file; never edit the live theme in place.**\n - Use Bash to create a candidate. If the target theme already exists, copy it verbatim: `cp <name>.json <name>.json.new` (inside `<KIMI_CODE_HOME>/themes/`). If it doesn't exist, use **Write** to create a minimal skeleton named `<name>.json.new`.\n - Use **Edit** on the candidate to change only the intended keys. Keep every existing entry, comment, and formatting intact.\n\n5. **Validate the candidate before overwriting.**\n - Read the candidate with **Read** to visually confirm it is well-formed JSON and that every `colors` value is a full 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Invalid hex values are silently skipped at load (they fall back to the base palette), but fix them so the theme renders as intended.\n\n6. **Back up and overwrite.**\n - Back up the old file first — **always** create a new timestamped backup and never overwrite an existing backup: `cp <name>.json \"<name>.json.$(date +%Y%m%d-%H%M%S).bak\"`.\n - If the target didn't exist, skip the backup.\n - Overwrite with the candidate: `mv <name>.json.new <name>.json`.\n\n7. **Tell the user how to apply it** (next section).\n\n## Applying the theme\n\n- The `/theme` picker re-scans the themes directory every time it opens, so a newly added file shows up **without restarting** — tell the user to run `/theme` and choose `Custom: <name>`.\n- Or set it in `tui.toml`: `theme = \"<name>\"`.\n- **Editing the active theme**: changes to the theme that's *currently in use* are not auto-reloaded. Tell the user to run **`/reload-tui`** (or switch to another theme and back). Re-selecting the **same** theme in `/theme` is a no-op (\"Theme unchanged\").\n\n## Don'ts\n\n- **Don't start creating or editing a theme until the user has clarified light/dark, style/mood, any specific colors, and the filename.** If anything is unclear, ask — don't guess.\n- Don't invent token names — only use the documented set; unknown keys are silently ignored.\n- Don't write 3-digit hex or named colors — use full `#RRGGBB`.\n- Never edit the live theme file in place; work through a candidate and validate before `mv`.\n- Before overwriting an existing theme file, **read it and back it up** so the user can recover.\n- Don't tell the user to restart the app to apply a theme — `/theme` or `/reload-tui` is enough.\n";
|
|
69367
|
+
var custom_theme_default = "---\nname: custom-theme\ndescription: Create or edit a kimi-code custom color theme — a JSON file under the resolved KIMI_CODE_HOME data directory that recolors the TUI. Use when the user wants their own theme, asks for a specific palette or mood, or wants to tweak an existing custom theme's colors.\n---\n\n# Create a kimi-code custom theme (custom-theme)\n\nHelp the user design, write, and apply a custom color theme for the kimi-code TUI. A theme is a single JSON file; the TUI ships with `dark`, `light`, and `auto`, and any file the user adds becomes selectable alongside them.\n\n## Rules of engagement\n\n- **Never write a theme until the user has explicitly clarified what they want.** This skill may only run after the user has confirmed light vs dark, the style or mood, any specific colors they care about, and the intended filename. If any of these are missing, ask before creating files.\n- **Never assume the data directory is `~/.kimi-code`.** Always resolve `$KIMI_CODE_HOME` first with the Bash command below.\n- **Never edit a live theme file in place.** Always create a `.json.new` candidate, validate it, back up the old file, and then `mv` it into place.\n- **Never overwrite an existing theme without reading it first.** Read, back up, then overwrite only after the user confirms.\n\n## Where a theme lives\n\nThe kimi-code runtime resolves the data directory as `KIMI_CODE_HOME` first, falling back to `~/.kimi-code`. Theme files live inside the `themes/` subdirectory of that data directory.\n\nBefore doing anything, resolve the actual data root with Bash so you don't write to the wrong place. Check whether `KIMI_CODE_HOME` is set and fall back to `~/.kimi-code` when it is empty:\n\n```bash\necho \"$KIMI_CODE_HOME\"\necho \"$HOME/.kimi-code\"\n```\n\nUse the first line when it is non-empty; otherwise use the second line. In the rest of this skill, `<KIMI_CODE_HOME>` means that resolved data root — **never assume `~/.kimi-code`**. Theme files live at `<KIMI_CODE_HOME>/themes/<name>.json`. Create the `themes/` directory if it doesn't exist.\n\n## What a theme is\n\n- A theme lives at `<KIMI_CODE_HOME>/themes/<name>.json`.\n- **The filename is the theme name**: `ember.json` shows up in the `/theme` picker as `Custom: ember`.\n- Shape:\n\n ```json\n {\n \"name\": \"ember\",\n \"displayName\": \"Ember\",\n \"colors\": {\n \"primary\": \"#83A598\",\n \"accent\": \"#FE8019\"\n }\n }\n ```\n\n - `name` (required), `displayName` (optional), `base` (optional: `\"dark\"` default, or `\"light\"`), `colors` (each value a 6-digit hex `#RRGGBB`).\n- **Partial themes are fine**: any token you leave out falls back to the **base** palette (`dark` by default; set `\"base\": \"light\"` for a light theme), so you can recolor just a few tokens or all of them.\n\n## Source of truth: the docs token reference\n\nBefore choosing colors, use **FetchURL** to fetch the official custom-theme docs as the authoritative list of tokens and what each controls:\n\n```\nhttps://moonshotai.github.io/kimi-code/en/customization/themes.html\n```\n\nOnly set tokens from this set — unknown keys are silently ignored at load. If FetchURL is unavailable or the fetch fails, fall back to the embedded reference below (it mirrors the same tokens) and tell the user you're working from the built-in list rather than the live docs.\n\n## Color tokens (what each controls)\n\n| Token | Controls |\n| --- | --- |\n| `primary` | The most-used color: links, inline code, the selected item in nearly every dialog, the focused editor border, plan/\"running\" badges, spinners |\n| `accent` | Secondary highlight: approval `▶` prefix, device-code box, image placeholder, BTW / queue panes, registry import |\n| `text` | Body text: dialog bodies, todo titles, footer model label, Markdown headings, assistant/tool message bullets, list bullets |\n| `textStrong` | Emphasized / bold text: input dialogs, status messages |\n| `textDim` | Secondary, dimmed text (the most widely used dim shade): thinking, hints, descriptions, completed todos, Markdown quotes, footer status bar |\n| `textMuted` | Faintest text: counters, scroll info, descriptions, Markdown link URLs, code-block borders |\n| `border` | Pane and editor borders, Markdown horizontal rule |\n| `borderFocus` | Focus / attention border (currently only the approval panel) |\n| `success` | Success state: `✓`, \"enabled\", completed |\n| `warning` | Warning state: auto/yolo badges, stale markers, plan-mode hint |\n| `error` | Error state: error messages, failed tool output |\n| `diffAdded` | Diff added lines |\n| `diffRemoved` | Diff removed lines |\n| `diffAddedStrong` | Diff intra-line changed words, added (bold) |\n| `diffRemovedStrong` | Diff intra-line changed words, removed (bold) |\n| `diffGutter` | Diff line-number gutter |\n| `diffMeta` | Diff meta / hunk headers |\n| `roleUser` | User message bullet and text, skill-activation name (the one role color with its own hue) |\n| `shellMode` | Shell mode (`!`) prompt, editor border, and the echoed `$ command` line |\n\n## Workflow\n\n1. **Ask the user what they want first — before choosing any colors.** Clarify, in one short exchange:\n - **Light or dark?** A light theme (dark text on a light background) or a dark theme (light text on a dark background). This sets the whole direction, so settle it first. For a light theme, set `\"base\": \"light\"` so the tokens you leave out inherit the light palette instead of dark.\n - **What style / mood?** e.g. warm vs cool, vivid vs muted, high vs low contrast, a named vibe (\"nord\", \"solarized\", \"sunset\"), or a base to start from (an existing theme, or `dark` / `light`).\n - **Any specific colors?** Whether they have exact hex values to anchor on (a brand color, a preferred `primary`, etc.).\n\n For the discrete choices (light vs dark, a few style options), prefer **AskUserQuestion** if it is available. If you are running in **auto mode** and `AskUserQuestion` is unavailable, ask the same question as a plain-text message with clear numbered or bulleted options, and wait for the user's reply. Don't start picking colors until you at least know light-vs-dark and the rough style.\n\n2. **Resolve the actual theme directory and current theme(s).**\n - Resolve the data root by checking `echo \"$KIMI_CODE_HOME\"`; if empty, use `echo \"$HOME/.kimi-code\"`. Use `<root>/themes` for every subsequent step.\n - If tweaking an existing custom theme, **Read** `<KIMI_CODE_HOME>/themes/<name>.json` first — never overwrite a theme you haven't read.\n - Starting fresh: build a `colors` object from the token table. You can `ls <KIMI_CODE_HOME>/themes/` and Read one of the user's existing themes as a reference for the format.\n\n3. **Pick a starting point and choose colors deliberately.**\n - Every value is a 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Keep contrast usable against the user's terminal background: don't let `text` / `textDim` sit too close to the background, and keep `success` / `warning` / `error` clearly distinguishable from each other.\n - `primary` is the most-seen color (links, selection, focus) — make it readable and distinct from `text`.\n - `roleUser` is the one role color meant to stand on its own — give it a distinct hue.\n\n4. **Create a candidate file; never edit the live theme in place.**\n - Use Bash to create a candidate. If the target theme already exists, copy it verbatim: `cp <name>.json <name>.json.new` (inside `<KIMI_CODE_HOME>/themes/`). If it doesn't exist, use **Write** to create a minimal skeleton named `<name>.json.new`.\n - Use **Edit** on the candidate to change only the intended keys. Keep every existing entry, comment, and formatting intact.\n\n5. **Validate the candidate before overwriting.**\n - Read the candidate with **Read** to visually confirm it is well-formed JSON and that every `colors` value is a full 6-digit hex `#RRGGBB` (not 3-digit, not a named color).\n - Invalid hex values are silently skipped at load (they fall back to the base palette), but fix them so the theme renders as intended.\n\n6. **Back up and overwrite.**\n - Back up the old file first — **always** create a new timestamped backup and never overwrite an existing backup: `cp <name>.json \"<name>.json.$(date +%Y%m%d-%H%M%S).bak\"`.\n - If the target didn't exist, skip the backup.\n - Overwrite with the candidate: `mv <name>.json.new <name>.json`.\n\n7. **Tell the user how to apply it** (next section).\n\n## Applying the theme\n\n- The `/theme` picker re-scans the themes directory every time it opens, so a newly added file shows up **without restarting** — tell the user to run `/theme` and choose `Custom: <name>`.\n- Or set it in `tui.toml`: `theme = \"<name>\"`.\n- **Editing the active theme**: changes to the theme that's *currently in use* are not auto-reloaded. Tell the user to run **`/reload-tui`** (or switch to another theme and back). Re-selecting the **same** theme in `/theme` is a no-op (\"Theme unchanged\").\n\n## Don'ts\n\n- **Don't start creating or editing a theme until the user has clarified light/dark, style/mood, any specific colors, and the filename.** If anything is unclear, ask — don't guess.\n- Don't invent token names — only use the documented set; unknown keys are silently ignored.\n- Don't write 3-digit hex or named colors — use full `#RRGGBB`.\n- Never edit the live theme file in place; work through a candidate and validate before `mv`.\n- Before overwriting an existing theme file, **read it and back it up** so the user can recover.\n- Don't tell the user to restart the app to apply a theme — `/theme` or `/reload-tui` is enough.\n";
|
|
69256
69368
|
//#endregion
|
|
69257
69369
|
//#region ../agent-core/src/skill/builtin/custom-theme.ts
|
|
69258
69370
|
const PSEUDO_PATH$4 = "builtin://custom-theme";
|
|
@@ -69796,8 +69908,17 @@ function formatModelSkill(skill) {
|
|
|
69796
69908
|
lines.push(` Path: ${skill.path}`);
|
|
69797
69909
|
return lines;
|
|
69798
69910
|
}
|
|
69911
|
+
const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
69799
69912
|
function truncate(value, max) {
|
|
69800
|
-
|
|
69913
|
+
if (value.length <= max) return value;
|
|
69914
|
+
let length = 0;
|
|
69915
|
+
let result = "";
|
|
69916
|
+
for (const { segment } of graphemeSegmenter.segment(value)) {
|
|
69917
|
+
if (length + segment.length > max - 1) break;
|
|
69918
|
+
result += segment;
|
|
69919
|
+
length += segment.length;
|
|
69920
|
+
}
|
|
69921
|
+
return `${result}…`;
|
|
69801
69922
|
}
|
|
69802
69923
|
//#endregion
|
|
69803
69924
|
//#region ../agent-core/src/agent/skill/prompt.ts
|
|
@@ -77806,9 +77927,17 @@ function normalizeToolResult(r) {
|
|
|
77806
77927
|
const textJoined = r.output.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
77807
77928
|
output = textJoined.length > 0 ? textJoined : TOOL_OUTPUT_EMPTY;
|
|
77808
77929
|
}
|
|
77809
|
-
|
|
77930
|
+
if (r.isError === true) return r.truncated === true ? {
|
|
77931
|
+
output,
|
|
77932
|
+
isError: true,
|
|
77933
|
+
truncated: true
|
|
77934
|
+
} : {
|
|
77810
77935
|
output,
|
|
77811
77936
|
isError: true
|
|
77937
|
+
};
|
|
77938
|
+
return r.truncated === true ? {
|
|
77939
|
+
output,
|
|
77940
|
+
truncated: true
|
|
77812
77941
|
} : { output };
|
|
77813
77942
|
}
|
|
77814
77943
|
function makeToolResult(call, args, result) {
|
|
@@ -78891,6 +79020,11 @@ const injectionOriginSchema = z.object({
|
|
|
78891
79020
|
kind: z.literal("injection"),
|
|
78892
79021
|
variant: z.string()
|
|
78893
79022
|
});
|
|
79023
|
+
const shellCommandOriginSchema = z.object({
|
|
79024
|
+
kind: z.literal("shell_command"),
|
|
79025
|
+
phase: z.enum(["input", "output"]),
|
|
79026
|
+
isError: z.boolean().optional()
|
|
79027
|
+
});
|
|
78894
79028
|
const compactionSummaryOriginSchema = z.object({ kind: z.literal("compaction_summary") });
|
|
78895
79029
|
const systemTriggerOriginSchema = z.object({
|
|
78896
79030
|
kind: z.literal("system_trigger"),
|
|
@@ -78935,6 +79069,7 @@ const promptOriginSchema = z.discriminatedUnion("kind", [
|
|
|
78935
79069
|
userPromptOriginSchema,
|
|
78936
79070
|
skillActivationOriginSchema,
|
|
78937
79071
|
injectionOriginSchema,
|
|
79072
|
+
shellCommandOriginSchema,
|
|
78938
79073
|
compactionSummaryOriginSchema,
|
|
78939
79074
|
systemTriggerOriginSchema,
|
|
78940
79075
|
backgroundTaskOriginSchema,
|
|
@@ -79282,6 +79417,16 @@ const toolProgressEventSchema = z.object({
|
|
|
79282
79417
|
toolCallId: z.string(),
|
|
79283
79418
|
update: toolUpdateSchema
|
|
79284
79419
|
});
|
|
79420
|
+
const shellOutputEventSchema = z.object({
|
|
79421
|
+
type: z.literal("shell.output"),
|
|
79422
|
+
commandId: z.string(),
|
|
79423
|
+
update: toolUpdateSchema
|
|
79424
|
+
});
|
|
79425
|
+
const shellStartedEventSchema = z.object({
|
|
79426
|
+
type: z.literal("shell.started"),
|
|
79427
|
+
commandId: z.string(),
|
|
79428
|
+
taskId: z.string()
|
|
79429
|
+
});
|
|
79285
79430
|
const toolResultEventSchema = z.object({
|
|
79286
79431
|
type: z.literal("tool.result"),
|
|
79287
79432
|
turnId: z.number(),
|
|
@@ -79408,6 +79553,8 @@ const eventSchema = z.discriminatedUnion("type", [
|
|
|
79408
79553
|
toolCallDeltaEventSchema,
|
|
79409
79554
|
toolCallStartedEventSchema,
|
|
79410
79555
|
toolProgressEventSchema,
|
|
79556
|
+
shellOutputEventSchema,
|
|
79557
|
+
shellStartedEventSchema,
|
|
79411
79558
|
toolResultEventSchema,
|
|
79412
79559
|
toolListUpdatedEventSchema,
|
|
79413
79560
|
mcpServerStatusEventSchema,
|
|
@@ -79879,8 +80026,7 @@ const questionRequestSchema = z.object({
|
|
|
79879
80026
|
turn_id: z.number().int().nonnegative().optional(),
|
|
79880
80027
|
tool_call_id: z.string().min(1).optional(),
|
|
79881
80028
|
questions: z.array(questionItemSchema).min(1).max(4),
|
|
79882
|
-
created_at: isoDateTimeSchema
|
|
79883
|
-
expires_at: isoDateTimeSchema
|
|
80029
|
+
created_at: isoDateTimeSchema
|
|
79884
80030
|
});
|
|
79885
80031
|
const questionAnswerSchema = z.discriminatedUnion("kind", [
|
|
79886
80032
|
z.object({
|
|
@@ -80460,6 +80606,16 @@ const sessionStatusResponseSchema = z.object({
|
|
|
80460
80606
|
max_context_tokens: z.number().int().nonnegative(),
|
|
80461
80607
|
context_usage: z.number().min(0).max(1)
|
|
80462
80608
|
});
|
|
80609
|
+
const sessionWarningSchema = z.object({
|
|
80610
|
+
code: z.string(),
|
|
80611
|
+
message: z.string(),
|
|
80612
|
+
severity: z.enum([
|
|
80613
|
+
"info",
|
|
80614
|
+
"warning",
|
|
80615
|
+
"error"
|
|
80616
|
+
])
|
|
80617
|
+
});
|
|
80618
|
+
z.object({ warnings: z.array(sessionWarningSchema) });
|
|
80463
80619
|
z.preprocess((value) => value === void 0 ? {} : value, z.object({ instruction: z.string().optional() }));
|
|
80464
80620
|
z.object({});
|
|
80465
80621
|
z.preprocess((value) => value === void 0 ? {} : value, z.object({
|
|
@@ -84393,8 +84549,14 @@ function mcpResultToExecutableOutput(result, qualifiedToolName) {
|
|
|
84393
84549
|
const part = convertMCPContentBlock(block);
|
|
84394
84550
|
if (part !== null) converted.push(part);
|
|
84395
84551
|
}
|
|
84396
|
-
|
|
84397
|
-
|
|
84552
|
+
const limited = applyOutputLimits(wrapMediaOnly(converted, qualifiedToolName));
|
|
84553
|
+
const output = collapseSingleText(limited.parts);
|
|
84554
|
+
return limited.truncated ? {
|
|
84555
|
+
output,
|
|
84556
|
+
isError: result.isError,
|
|
84557
|
+
truncated: true
|
|
84558
|
+
} : {
|
|
84559
|
+
output,
|
|
84398
84560
|
isError: result.isError
|
|
84399
84561
|
};
|
|
84400
84562
|
}
|
|
@@ -84428,11 +84590,13 @@ function wrapMediaOnly(parts, qualifiedToolName) {
|
|
|
84428
84590
|
*/
|
|
84429
84591
|
function applyOutputLimits(parts) {
|
|
84430
84592
|
let remaining = MCP_MAX_OUTPUT_CHARS;
|
|
84593
|
+
let truncated = false;
|
|
84431
84594
|
let textTruncated = false;
|
|
84432
84595
|
const out = [];
|
|
84433
84596
|
for (const part of parts) {
|
|
84434
84597
|
if (part.type === "text") {
|
|
84435
84598
|
if (remaining <= 0) {
|
|
84599
|
+
truncated = true;
|
|
84436
84600
|
textTruncated = true;
|
|
84437
84601
|
continue;
|
|
84438
84602
|
}
|
|
@@ -84442,6 +84606,7 @@ function applyOutputLimits(parts) {
|
|
|
84442
84606
|
text: part.text.slice(0, remaining)
|
|
84443
84607
|
});
|
|
84444
84608
|
remaining = 0;
|
|
84609
|
+
truncated = true;
|
|
84445
84610
|
textTruncated = true;
|
|
84446
84611
|
} else {
|
|
84447
84612
|
out.push(part);
|
|
@@ -84452,6 +84617,7 @@ function applyOutputLimits(parts) {
|
|
|
84452
84617
|
if (part.type === "think") {
|
|
84453
84618
|
const size = part.think.length + (part.encrypted?.length ?? 0);
|
|
84454
84619
|
if (remaining <= 0) {
|
|
84620
|
+
truncated = true;
|
|
84455
84621
|
textTruncated = true;
|
|
84456
84622
|
continue;
|
|
84457
84623
|
}
|
|
@@ -84461,6 +84627,7 @@ function applyOutputLimits(parts) {
|
|
|
84461
84627
|
think: part.think.slice(0, remaining)
|
|
84462
84628
|
});
|
|
84463
84629
|
remaining = 0;
|
|
84630
|
+
truncated = true;
|
|
84464
84631
|
textTruncated = true;
|
|
84465
84632
|
} else {
|
|
84466
84633
|
out.push(part);
|
|
@@ -84475,12 +84642,16 @@ function applyOutputLimits(parts) {
|
|
|
84475
84642
|
type: "text",
|
|
84476
84643
|
text: binaryPartTooLargeNotice(kind, url.length)
|
|
84477
84644
|
});
|
|
84645
|
+
truncated = true;
|
|
84478
84646
|
continue;
|
|
84479
84647
|
}
|
|
84480
84648
|
out.push(part);
|
|
84481
84649
|
}
|
|
84482
84650
|
if (textTruncated) appendTruncationNotice(out);
|
|
84483
|
-
return
|
|
84651
|
+
return {
|
|
84652
|
+
parts: out,
|
|
84653
|
+
truncated
|
|
84654
|
+
};
|
|
84484
84655
|
}
|
|
84485
84656
|
function appendTruncationNotice(out) {
|
|
84486
84657
|
for (let i = out.length - 1; i >= 0; i--) {
|
|
@@ -84591,25 +84762,25 @@ async function listDirectory(kaos, workDir = kaos.getcwd(), options = {}) {
|
|
|
84591
84762
|
}
|
|
84592
84763
|
//#endregion
|
|
84593
84764
|
//#region ../agent-core/src/profile/context.ts
|
|
84594
|
-
const
|
|
84595
|
-
const AGENTS_MD_TRUNCATION_MARKER = "<!-- Some AGENTS.md files were truncated or omitted to fit the 32 KB budget -->";
|
|
84765
|
+
const AGENTS_MD_RECOMMENDED_MAX_BYTES = 32 * 1024;
|
|
84596
84766
|
const S_IFMT$3 = 61440;
|
|
84597
84767
|
const S_IFREG$1 = 32768;
|
|
84598
84768
|
async function prepareSystemPromptContext(kaos, brandHome, options) {
|
|
84599
84769
|
const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
|
|
84600
|
-
const [cwdListing,
|
|
84770
|
+
const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
|
|
84601
84771
|
listDirectory(kaos, void 0, { collapseHiddenDirs: true }),
|
|
84602
|
-
|
|
84772
|
+
loadAgentsMdForRoots(kaos, brandHome, [kaos.getcwd()]),
|
|
84603
84773
|
loadAdditionalDirsInfo(kaos, additionalDirs)
|
|
84604
84774
|
]);
|
|
84605
84775
|
return {
|
|
84606
84776
|
cwdListing,
|
|
84607
|
-
agentsMd,
|
|
84608
|
-
additionalDirsInfo
|
|
84777
|
+
agentsMd: agentsMdResult.content,
|
|
84778
|
+
additionalDirsInfo,
|
|
84779
|
+
agentsMdWarning: agentsMdResult.warning
|
|
84609
84780
|
};
|
|
84610
84781
|
}
|
|
84611
84782
|
async function loadAgentsMd(kaos, brandHome) {
|
|
84612
|
-
return loadAgentsMdForRoots(kaos, brandHome, [kaos.getcwd()]);
|
|
84783
|
+
return (await loadAgentsMdForRoots(kaos, brandHome, [kaos.getcwd()])).content;
|
|
84613
84784
|
}
|
|
84614
84785
|
async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
|
|
84615
84786
|
const discovered = [];
|
|
@@ -84636,7 +84807,12 @@ async function loadAgentsMdForRoots(kaos, brandHome, workDirs) {
|
|
|
84636
84807
|
for (const fileName of ["AGENTS.md", "agents.md"]) if (await collect(join$1(dir, fileName))) break;
|
|
84637
84808
|
}
|
|
84638
84809
|
}
|
|
84639
|
-
|
|
84810
|
+
const content = renderAgentFiles(discovered);
|
|
84811
|
+
const totalBytes = byteLength(content);
|
|
84812
|
+
return {
|
|
84813
|
+
content,
|
|
84814
|
+
warning: totalBytes > AGENTS_MD_RECOMMENDED_MAX_BYTES ? `AGENTS.md total ${formatKB(totalBytes)} KB exceeds the recommended ${formatKB(AGENTS_MD_RECOMMENDED_MAX_BYTES)} KB. Large instruction files increase cost and may impact performance; consider trimming.` : void 0
|
|
84815
|
+
};
|
|
84640
84816
|
}
|
|
84641
84817
|
async function loadAdditionalDirsInfo(kaos, additionalDirs) {
|
|
84642
84818
|
return (await Promise.all(additionalDirs.map(async (dir) => {
|
|
@@ -84691,60 +84867,15 @@ async function isFile$2(kaos, path) {
|
|
|
84691
84867
|
}
|
|
84692
84868
|
function renderAgentFiles(files) {
|
|
84693
84869
|
if (files.length === 0) return "";
|
|
84694
|
-
|
|
84695
|
-
let didTruncate = false;
|
|
84696
|
-
const budgeted = Array.from({ length: files.length });
|
|
84697
|
-
for (let i = files.length - 1; i >= 0; i--) {
|
|
84698
|
-
const file = files[i];
|
|
84699
|
-
if (file === void 0) continue;
|
|
84700
|
-
const annotation = annotationFor(file.path);
|
|
84701
|
-
const separator = i < files.length - 1 ? "\n\n" : "";
|
|
84702
|
-
remaining -= byteLength(annotation) + byteLength(separator);
|
|
84703
|
-
if (remaining <= 0) {
|
|
84704
|
-
budgeted[i] = {
|
|
84705
|
-
path: file.path,
|
|
84706
|
-
content: ""
|
|
84707
|
-
};
|
|
84708
|
-
remaining = 0;
|
|
84709
|
-
didTruncate = true;
|
|
84710
|
-
continue;
|
|
84711
|
-
}
|
|
84712
|
-
let content = file.content;
|
|
84713
|
-
if (byteLength(content) > remaining) {
|
|
84714
|
-
content = truncateUtf8(content, remaining).trim();
|
|
84715
|
-
didTruncate = true;
|
|
84716
|
-
}
|
|
84717
|
-
remaining -= byteLength(content);
|
|
84718
|
-
budgeted[i] = {
|
|
84719
|
-
path: file.path,
|
|
84720
|
-
content
|
|
84721
|
-
};
|
|
84722
|
-
}
|
|
84723
|
-
const rendered = budgeted.filter((file) => file !== void 0 && file.content.length > 0).map((file) => `${annotationFor(file.path)}${file.content}`).join("\n\n");
|
|
84724
|
-
return didTruncate ? `${AGENTS_MD_TRUNCATION_MARKER}\n${rendered}` : rendered;
|
|
84725
|
-
}
|
|
84726
|
-
function truncateUtf8(text, maxBytes) {
|
|
84727
|
-
if (maxBytes <= 0) return "";
|
|
84728
|
-
if (byteLength(text) <= maxBytes) return text;
|
|
84729
|
-
let low = 0;
|
|
84730
|
-
let high = text.length;
|
|
84731
|
-
while (low < high) {
|
|
84732
|
-
const mid = Math.ceil((low + high) / 2);
|
|
84733
|
-
if (byteLength(text.slice(0, mid)) <= maxBytes) low = mid;
|
|
84734
|
-
else high = mid - 1;
|
|
84735
|
-
}
|
|
84736
|
-
let result = text.slice(0, low);
|
|
84737
|
-
while (endsWithUnpairedHighSurrogate(result)) result = result.slice(0, -1);
|
|
84738
|
-
return result;
|
|
84739
|
-
}
|
|
84740
|
-
function endsWithUnpairedHighSurrogate(text) {
|
|
84741
|
-
if (text.length === 0) return false;
|
|
84742
|
-
const codePoint = text.codePointAt(text.length - 1);
|
|
84743
|
-
return codePoint !== void 0 && codePoint >= 55296 && codePoint <= 56319;
|
|
84870
|
+
return files.map((file) => `${annotationFor(file.path)}${file.content}`).join("\n\n");
|
|
84744
84871
|
}
|
|
84745
84872
|
function byteLength(text) {
|
|
84746
84873
|
return Buffer.byteLength(text, "utf8");
|
|
84747
84874
|
}
|
|
84875
|
+
function formatKB(bytes) {
|
|
84876
|
+
const kb = bytes / 1024;
|
|
84877
|
+
return Number.isInteger(kb) ? String(kb) : kb.toFixed(1);
|
|
84878
|
+
}
|
|
84748
84879
|
function annotationFor(path) {
|
|
84749
84880
|
return `<!-- From: ${path} -->\n`;
|
|
84750
84881
|
}
|
|
@@ -85622,21 +85753,44 @@ async function disposeProcess$2(proc) {
|
|
|
85622
85753
|
* directory is not a git repository or no useful information was collected.
|
|
85623
85754
|
*/
|
|
85624
85755
|
async function collectGitContext(kaos, cwd) {
|
|
85625
|
-
|
|
85626
|
-
const
|
|
85627
|
-
|
|
85756
|
+
const revParseArgs = ["rev-parse", "--is-inside-work-tree"];
|
|
85757
|
+
const revParse = await runGit(kaos, cwd, revParseArgs);
|
|
85758
|
+
if (!revParse.ok) {
|
|
85759
|
+
if (revParse.kind === "command-failed" && isNotARepo(revParse.stderr)) return `<git-context status="unavailable" reason="not-a-repo"/>`;
|
|
85760
|
+
logGitFailure(cwd, revParseArgs, revParse);
|
|
85761
|
+
return "";
|
|
85762
|
+
}
|
|
85763
|
+
const [remote, branch, status, gitLog] = await Promise.all([
|
|
85764
|
+
[
|
|
85628
85765
|
"remote",
|
|
85629
85766
|
"get-url",
|
|
85630
85767
|
"origin"
|
|
85631
|
-
]
|
|
85632
|
-
|
|
85633
|
-
|
|
85634
|
-
|
|
85768
|
+
],
|
|
85769
|
+
[
|
|
85770
|
+
"symbolic-ref",
|
|
85771
|
+
"--short",
|
|
85772
|
+
"HEAD"
|
|
85773
|
+
],
|
|
85774
|
+
["status", "--porcelain"],
|
|
85775
|
+
[
|
|
85635
85776
|
"log",
|
|
85636
85777
|
"-3",
|
|
85637
85778
|
"--format=%h %s"
|
|
85638
|
-
]
|
|
85639
|
-
])
|
|
85779
|
+
]
|
|
85780
|
+
].map(async (args) => ({
|
|
85781
|
+
args,
|
|
85782
|
+
result: await runGit(kaos, cwd, args)
|
|
85783
|
+
})));
|
|
85784
|
+
for (const { args, result } of [
|
|
85785
|
+
remote,
|
|
85786
|
+
branch,
|
|
85787
|
+
status,
|
|
85788
|
+
gitLog
|
|
85789
|
+
]) if (!result.ok) logGitFailure(cwd, args, result);
|
|
85790
|
+
const remoteUrl = stdoutOf(remote.result);
|
|
85791
|
+
const branchName = stdoutOf(branch.result);
|
|
85792
|
+
const dirtyRaw = stdoutOf(status.result);
|
|
85793
|
+
const logRaw = stdoutOf(gitLog.result);
|
|
85640
85794
|
const sections = [`Working directory: ${cwd}`];
|
|
85641
85795
|
if (remoteUrl) {
|
|
85642
85796
|
const safeUrl = sanitizeRemoteUrl(remoteUrl);
|
|
@@ -85646,15 +85800,13 @@ async function collectGitContext(kaos, cwd) {
|
|
|
85646
85800
|
if (project) sections.push(`Project: ${project}`);
|
|
85647
85801
|
}
|
|
85648
85802
|
}
|
|
85649
|
-
if (
|
|
85650
|
-
|
|
85651
|
-
|
|
85652
|
-
|
|
85653
|
-
|
|
85654
|
-
|
|
85655
|
-
|
|
85656
|
-
sections.push(`Dirty files (${String(total)}):\n${body}`);
|
|
85657
|
-
}
|
|
85803
|
+
if (branchName) sections.push(`Branch: ${branchName}`);
|
|
85804
|
+
const dirtyLines = dirtyRaw.split("\n").filter((line) => line.trim().length > 0);
|
|
85805
|
+
if (dirtyLines.length > 0) {
|
|
85806
|
+
const total = dirtyLines.length;
|
|
85807
|
+
let body = dirtyLines.slice(0, MAX_DIRTY_FILES).map((line) => ` ${line}`).join("\n");
|
|
85808
|
+
if (total > MAX_DIRTY_FILES) body += `\n ... and ${String(total - MAX_DIRTY_FILES)} more`;
|
|
85809
|
+
sections.push(`Dirty files (${String(total)}):\n${body}`);
|
|
85658
85810
|
}
|
|
85659
85811
|
if (logRaw) {
|
|
85660
85812
|
const logLines = logRaw.split("\n").filter((line) => line.trim().length > 0);
|
|
@@ -85702,39 +85854,87 @@ function tryUrlPath(remoteUrl) {
|
|
|
85702
85854
|
return null;
|
|
85703
85855
|
}
|
|
85704
85856
|
}
|
|
85857
|
+
function stdoutOf(result) {
|
|
85858
|
+
return result.ok ? result.stdout : "";
|
|
85859
|
+
}
|
|
85860
|
+
function isNotARepo(stderr) {
|
|
85861
|
+
return stderr !== void 0 && stderr.includes("not a git repository");
|
|
85862
|
+
}
|
|
85863
|
+
function logGitFailure(cwd, args, failure) {
|
|
85864
|
+
const command = `git ${args.join(" ")}`;
|
|
85865
|
+
if (failure.kind === "timeout") log.debug("git context command timed out", {
|
|
85866
|
+
cwd,
|
|
85867
|
+
command
|
|
85868
|
+
});
|
|
85869
|
+
else if (failure.kind === "spawn-error") log.warn("git context command failed to spawn", {
|
|
85870
|
+
cwd,
|
|
85871
|
+
command
|
|
85872
|
+
});
|
|
85873
|
+
else log.debug("git context command failed", {
|
|
85874
|
+
cwd,
|
|
85875
|
+
command,
|
|
85876
|
+
exitCode: failure.exitCode,
|
|
85877
|
+
stderr: failure.stderr
|
|
85878
|
+
});
|
|
85879
|
+
}
|
|
85705
85880
|
/**
|
|
85706
|
-
* Run a single `git -C <cwd> <args>` command and return
|
|
85707
|
-
*
|
|
85708
|
-
*
|
|
85881
|
+
* Run a single `git -C <cwd> <args>` command and return a structured result.
|
|
85882
|
+
* The `git -C` form runs in the target directory regardless of the Kaos
|
|
85883
|
+
* backend. Both stdout and stderr are captured so callers can tell "not a
|
|
85884
|
+
* git repository" (exit 128 + telltale stderr) apart from other failures.
|
|
85709
85885
|
*/
|
|
85710
85886
|
async function runGit(kaos, cwd, args) {
|
|
85711
85887
|
let proc;
|
|
85712
85888
|
try {
|
|
85713
85889
|
proc = await kaos.exec("git", "-C", cwd, ...args);
|
|
85714
85890
|
} catch {
|
|
85715
|
-
return
|
|
85891
|
+
return {
|
|
85892
|
+
ok: false,
|
|
85893
|
+
kind: "spawn-error"
|
|
85894
|
+
};
|
|
85716
85895
|
}
|
|
85717
85896
|
try {
|
|
85718
85897
|
proc.stdin.end();
|
|
85719
85898
|
} catch {}
|
|
85720
|
-
const work = Promise.all([
|
|
85899
|
+
const work = Promise.all([
|
|
85900
|
+
collectStream(proc.stdout),
|
|
85901
|
+
collectStream(proc.stderr),
|
|
85902
|
+
proc.wait()
|
|
85903
|
+
]);
|
|
85721
85904
|
work.catch(() => {});
|
|
85722
85905
|
let timer;
|
|
85906
|
+
let timedOut = false;
|
|
85723
85907
|
try {
|
|
85724
85908
|
const timeout = new Promise((_resolve, reject) => {
|
|
85725
85909
|
timer = setTimeout(() => {
|
|
85910
|
+
timedOut = true;
|
|
85726
85911
|
reject(/* @__PURE__ */ new Error(`git ${args.join(" ")} timed out`));
|
|
85727
85912
|
}, GIT_TIMEOUT_MS);
|
|
85728
85913
|
});
|
|
85729
|
-
const [stdout, exitCode] = await Promise.race([work, timeout]);
|
|
85730
|
-
if (exitCode !== 0) return
|
|
85731
|
-
|
|
85914
|
+
const [stdout, stderr, exitCode] = await Promise.race([work, timeout]);
|
|
85915
|
+
if (exitCode !== 0) return {
|
|
85916
|
+
ok: false,
|
|
85917
|
+
kind: "command-failed",
|
|
85918
|
+
exitCode,
|
|
85919
|
+
stderr: stderr.trim()
|
|
85920
|
+
};
|
|
85921
|
+
return {
|
|
85922
|
+
ok: true,
|
|
85923
|
+
stdout: stdout.trim()
|
|
85924
|
+
};
|
|
85732
85925
|
} catch {
|
|
85733
85926
|
try {
|
|
85734
85927
|
await proc.kill("SIGKILL");
|
|
85735
85928
|
} catch {}
|
|
85736
85929
|
await work.catch(() => {});
|
|
85737
|
-
return
|
|
85930
|
+
if (timedOut) return {
|
|
85931
|
+
ok: false,
|
|
85932
|
+
kind: "timeout"
|
|
85933
|
+
};
|
|
85934
|
+
return {
|
|
85935
|
+
ok: false,
|
|
85936
|
+
kind: "command-failed"
|
|
85937
|
+
};
|
|
85738
85938
|
} finally {
|
|
85739
85939
|
if (timer !== void 0) clearTimeout(timer);
|
|
85740
85940
|
if (proc !== void 0) await disposeProcess$2(proc);
|
|
@@ -91938,6 +92138,9 @@ var ToolResultBuilder = class {
|
|
|
91938
92138
|
get nChars() {
|
|
91939
92139
|
return this.nCharsValue;
|
|
91940
92140
|
}
|
|
92141
|
+
get truncated() {
|
|
92142
|
+
return this.truncationHappened;
|
|
92143
|
+
}
|
|
91941
92144
|
write(text) {
|
|
91942
92145
|
if (this.nCharsValue >= this.maxChars) {
|
|
91943
92146
|
if (text.length > 0 && !this.truncationHappened) {
|
|
@@ -93556,7 +93759,7 @@ var ReadMediaFileTool = class {
|
|
|
93556
93759
|
};
|
|
93557
93760
|
//#endregion
|
|
93558
93761
|
//#region ../agent-core/src/tools/builtin/file/write.md?raw
|
|
93559
|
-
var write_default = "Create, append to, or replace a file entirely.\n\n-
|
|
93762
|
+
var write_default = "Create, append to, or replace a file entirely.\n\n- Missing parent directories are created automatically (like `mkdir(parents=True, exist_ok=True)`).\n- Mode defaults to overwrite; append adds content at EOF without adding a newline.\n- Write is NOT ALLOWED for incremental changes to existing files, including trivial, one-line, quick, or cosmetic edits. Use Edit instead.\n- Use Write only when the file does not exist, you intend a complete replacement, or the new contents have little continuity with the old contents.\n- Read before overwriting an existing file.\n- Write ignores the Read/Edit line-number view. NEVER include line prefixes.\n- Write outputs content literally, including supplied line endings: \\n stays LF, \\r\\n stays CRLF.\n- For new content too large for one call, overwrite the first chunk, then append subsequent chunks. Never chunk Write to modify an existing file.\n";
|
|
93560
93763
|
//#endregion
|
|
93561
93764
|
//#region ../agent-core/src/tools/builtin/file/write.ts
|
|
93562
93765
|
/** Mask isolating the file-type bits of a stat mode. */
|
|
@@ -93564,7 +93767,7 @@ const S_IFMT = 61440;
|
|
|
93564
93767
|
/** File-type bits of a directory. */
|
|
93565
93768
|
const S_IFDIR = 16384;
|
|
93566
93769
|
const WriteInputSchema = z.object({
|
|
93567
|
-
path: z.string().describe("Path to the file to create, append to, or completely overwrite. Relative paths resolve against the working directory; a path outside the working directory must be absolute.
|
|
93770
|
+
path: z.string().describe("Path to the file to create, append to, or completely overwrite. Relative paths resolve against the working directory; a path outside the working directory must be absolute. Missing parent directories are created automatically."),
|
|
93568
93771
|
content: z.string().describe("Raw full file content to write exactly as provided. This does not use the Read/Edit text view."),
|
|
93569
93772
|
mode: z.enum(["overwrite", "append"]).optional().describe("Write mode. Defaults to overwrite. append adds content to the end exactly as provided and does not add a newline.")
|
|
93570
93773
|
});
|
|
@@ -93606,7 +93809,7 @@ var WriteTool = class {
|
|
|
93606
93809
|
};
|
|
93607
93810
|
}
|
|
93608
93811
|
async execution(args, safePath) {
|
|
93609
|
-
const parentError = await this.
|
|
93812
|
+
const parentError = await this.ensureParentDirectory(safePath);
|
|
93610
93813
|
if (parentError !== void 0) return {
|
|
93611
93814
|
isError: true,
|
|
93612
93815
|
output: parentError
|
|
@@ -93629,22 +93832,36 @@ var WriteTool = class {
|
|
|
93629
93832
|
}
|
|
93630
93833
|
}
|
|
93631
93834
|
/**
|
|
93632
|
-
* Best-effort check that the parent directory
|
|
93835
|
+
* Best-effort check that the parent directory is usable, creating it when
|
|
93836
|
+
* it is missing.
|
|
93837
|
+
*
|
|
93838
|
+
* If the parent (or any ancestor) does not exist, it is created
|
|
93839
|
+
* recursively — mirroring Python's `Path.mkdir(parents=True,
|
|
93840
|
+
* exist_ok=True)` — so the agent does not need a separate `mkdir` round
|
|
93841
|
+
* trip before writing into a fresh subfolder. An existing parent that is
|
|
93842
|
+
* not a directory is still a hard error. Any other `stat` failure
|
|
93843
|
+
* (permissions, an environment without `stat`) is treated as
|
|
93844
|
+
* inconclusive: the check is skipped and the write proceeds, surfacing
|
|
93845
|
+
* the real I/O error if any.
|
|
93633
93846
|
*
|
|
93634
|
-
* The path schema documents this precondition; probing it up front turns a
|
|
93635
|
-
* bare `ENOENT` from the underlying write into an actionable message.
|
|
93636
93847
|
* Returns an error string when the precondition is definitively violated,
|
|
93637
|
-
* or `undefined` otherwise.
|
|
93638
|
-
* environment without `stat`) is treated as inconclusive: the check is
|
|
93639
|
-
* skipped and the write proceeds, surfacing the real I/O error if any.
|
|
93848
|
+
* or `undefined` otherwise.
|
|
93640
93849
|
*/
|
|
93641
|
-
async
|
|
93850
|
+
async ensureParentDirectory(safePath) {
|
|
93642
93851
|
const parent = dirname$3(safePath);
|
|
93643
93852
|
let stat;
|
|
93644
93853
|
try {
|
|
93645
93854
|
stat = await this.kaos.stat(parent);
|
|
93646
93855
|
} catch (error) {
|
|
93647
|
-
if (error.code === "ENOENT")
|
|
93856
|
+
if (error.code === "ENOENT") try {
|
|
93857
|
+
await this.kaos.mkdir(parent, {
|
|
93858
|
+
parents: true,
|
|
93859
|
+
existOk: true
|
|
93860
|
+
});
|
|
93861
|
+
return;
|
|
93862
|
+
} catch (mkdirError) {
|
|
93863
|
+
return mkdirError instanceof Error ? mkdirError.message : String(mkdirError);
|
|
93864
|
+
}
|
|
93648
93865
|
return;
|
|
93649
93866
|
}
|
|
93650
93867
|
if ((stat.stMode & S_IFMT) !== S_IFDIR) return `Parent path is not a directory: ${parent}.`;
|
|
@@ -94030,6 +94247,69 @@ var ToolCallDeduplicator = class {
|
|
|
94030
94247
|
}
|
|
94031
94248
|
};
|
|
94032
94249
|
//#endregion
|
|
94250
|
+
//#region ../agent-core/src/agent/turn/tool-result-budget.ts
|
|
94251
|
+
const TOOL_RESULT_MAX_CHARS = 5e4;
|
|
94252
|
+
const TOOL_RESULT_PREVIEW_CHARS = 2e3;
|
|
94253
|
+
async function budgetToolResultForModel(options) {
|
|
94254
|
+
const text = persistableToolResultText(options.result.output);
|
|
94255
|
+
if (text === void 0 || text.length <= TOOL_RESULT_MAX_CHARS) return options.result;
|
|
94256
|
+
if (options.result.truncated === true) return options.result;
|
|
94257
|
+
if (options.homedir === void 0) return options.result;
|
|
94258
|
+
const outputPath = await saveToolResult({
|
|
94259
|
+
homedir: options.homedir,
|
|
94260
|
+
toolName: options.toolName,
|
|
94261
|
+
toolCallId: options.toolCallId
|
|
94262
|
+
}, text);
|
|
94263
|
+
if (outputPath === void 0) return options.result;
|
|
94264
|
+
const output = renderPersistedToolResult(options.toolName, options.toolCallId, text, outputPath);
|
|
94265
|
+
return options.result.isError === true ? {
|
|
94266
|
+
...options.result,
|
|
94267
|
+
output,
|
|
94268
|
+
isError: true
|
|
94269
|
+
} : {
|
|
94270
|
+
...options.result,
|
|
94271
|
+
output
|
|
94272
|
+
};
|
|
94273
|
+
}
|
|
94274
|
+
function persistableToolResultText(output) {
|
|
94275
|
+
if (typeof output === "string") return output;
|
|
94276
|
+
if (!output.every((part) => part.type === "text")) return;
|
|
94277
|
+
return output.map((part) => part.text).join("");
|
|
94278
|
+
}
|
|
94279
|
+
async function saveToolResult(options, text) {
|
|
94280
|
+
try {
|
|
94281
|
+
const dir = join$1(options.homedir, "tool-results");
|
|
94282
|
+
await mkdir(dir, {
|
|
94283
|
+
recursive: true,
|
|
94284
|
+
mode: 448
|
|
94285
|
+
});
|
|
94286
|
+
const outputPath = join$1(dir, `${safeToolResultFileStem(options.toolName, options.toolCallId)}-${randomUUID()}.txt`);
|
|
94287
|
+
await writeFile$1(outputPath, text, {
|
|
94288
|
+
encoding: "utf8",
|
|
94289
|
+
flag: "wx"
|
|
94290
|
+
});
|
|
94291
|
+
return outputPath;
|
|
94292
|
+
} catch {
|
|
94293
|
+
return;
|
|
94294
|
+
}
|
|
94295
|
+
}
|
|
94296
|
+
function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
|
|
94297
|
+
const lines = [
|
|
94298
|
+
`Tool output exceeded ${String(TOOL_RESULT_MAX_CHARS)} characters; showing a preview only.`,
|
|
94299
|
+
`tool_name: ${toolName}`,
|
|
94300
|
+
`tool_call_id: ${toolCallId}`,
|
|
94301
|
+
`output_size_chars: ${String(text.length)}`,
|
|
94302
|
+
`output_size_bytes: ${String(Buffer.byteLength(text, "utf8"))}`,
|
|
94303
|
+
`output_path: ${outputPath}`,
|
|
94304
|
+
"next_step: Use Read with output_path to page through the full output."
|
|
94305
|
+
];
|
|
94306
|
+
lines.push("", "[preview]", text.slice(0, TOOL_RESULT_PREVIEW_CHARS));
|
|
94307
|
+
return lines.join("\n");
|
|
94308
|
+
}
|
|
94309
|
+
function safeToolResultFileStem(toolName, toolCallId) {
|
|
94310
|
+
return `${toolName}-${toolCallId}`.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "tool-result";
|
|
94311
|
+
}
|
|
94312
|
+
//#endregion
|
|
94033
94313
|
//#region ../agent-core/src/agent/turn/index.ts
|
|
94034
94314
|
const LLM_NOT_SET_MESSAGE = "LLM not set, send \"/login\" to login";
|
|
94035
94315
|
/** Origin tag for the synthetic "continue" prompt that drives each goal turn. */
|
|
@@ -94568,7 +94848,12 @@ var TurnFlow = class {
|
|
|
94568
94848
|
toolOutput: isError === true ? void 0 : toolOutputText(output).slice(0, 2e3)
|
|
94569
94849
|
}
|
|
94570
94850
|
});
|
|
94571
|
-
return
|
|
94851
|
+
return budgetToolResultForModel({
|
|
94852
|
+
homedir: this.agent.homedir,
|
|
94853
|
+
toolName: ctx.toolCall.name,
|
|
94854
|
+
toolCallId: ctx.toolCall.id,
|
|
94855
|
+
result: finalResult
|
|
94856
|
+
});
|
|
94572
94857
|
}
|
|
94573
94858
|
}
|
|
94574
94859
|
})).stopReason;
|
|
@@ -95292,7 +95577,7 @@ var BashTool = class {
|
|
|
95292
95577
|
},
|
|
95293
95578
|
approvalRule: literalRulePattern(this.name, args.command),
|
|
95294
95579
|
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
|
|
95295
|
-
execute: ({ signal, onUpdate }) => this.execution(args, signal, onUpdate)
|
|
95580
|
+
execute: ({ signal, onUpdate, onForegroundTaskStart }) => this.execution(args, signal, onUpdate, onForegroundTaskStart)
|
|
95296
95581
|
};
|
|
95297
95582
|
}
|
|
95298
95583
|
spawn(effectiveCwd, command) {
|
|
@@ -95314,7 +95599,7 @@ var BashTool = class {
|
|
|
95314
95599
|
};
|
|
95315
95600
|
return this.kaos.execWithEnv(shellArgs, mergedEnv);
|
|
95316
95601
|
}
|
|
95317
|
-
async execution(args, signal, onUpdate) {
|
|
95602
|
+
async execution(args, signal, onUpdate, onForegroundTaskStart) {
|
|
95318
95603
|
const validationError = this.validateRunRequest(args, signal);
|
|
95319
95604
|
if (validationError !== void 0) return validationError;
|
|
95320
95605
|
const startsInBackground = args.run_in_background === true;
|
|
@@ -95335,6 +95620,8 @@ var BashTool = class {
|
|
|
95335
95620
|
}
|
|
95336
95621
|
closeProcessStdin(proc);
|
|
95337
95622
|
let collectForegroundOutput = !startsInBackground;
|
|
95623
|
+
let foregroundOutputPersisted = false;
|
|
95624
|
+
let foregroundTaskId;
|
|
95338
95625
|
const onProcessOutput = startsInBackground ? void 0 : (kind, text) => {
|
|
95339
95626
|
if (!collectForegroundOutput) return;
|
|
95340
95627
|
onUpdate?.({
|
|
@@ -95342,14 +95629,20 @@ var BashTool = class {
|
|
|
95342
95629
|
text
|
|
95343
95630
|
});
|
|
95344
95631
|
builder.write(text);
|
|
95632
|
+
if (!foregroundOutputPersisted && builder.truncated && foregroundTaskId !== void 0) {
|
|
95633
|
+
this.backgroundManager.persistOutput(foregroundTaskId);
|
|
95634
|
+
foregroundOutputPersisted = true;
|
|
95635
|
+
}
|
|
95345
95636
|
};
|
|
95346
95637
|
let taskId;
|
|
95347
95638
|
try {
|
|
95348
95639
|
taskId = this.backgroundManager.registerTask(new ProcessBackgroundTask(proc, command, description, onProcessOutput), {
|
|
95349
95640
|
detached: startsInBackground,
|
|
95350
95641
|
timeoutMs,
|
|
95642
|
+
detachTimeoutMs: DEFAULT_BACKGROUND_TIMEOUT_S * MS_PER_SECOND,
|
|
95351
95643
|
signal: startsInBackground ? void 0 : signal
|
|
95352
95644
|
});
|
|
95645
|
+
foregroundTaskId = startsInBackground ? void 0 : taskId;
|
|
95353
95646
|
} catch (error) {
|
|
95354
95647
|
collectForegroundOutput = false;
|
|
95355
95648
|
await killSpawnedProcess(proc);
|
|
@@ -95358,6 +95651,7 @@ var BashTool = class {
|
|
|
95358
95651
|
output: error instanceof Error ? error.message : String(error)
|
|
95359
95652
|
};
|
|
95360
95653
|
}
|
|
95654
|
+
if (!startsInBackground) onForegroundTaskStart?.(taskId);
|
|
95361
95655
|
if (startsInBackground) return this.backgroundStartedResult(taskId, proc, description, {
|
|
95362
95656
|
title: "Background task started",
|
|
95363
95657
|
brief: `Started ${taskId}`
|
|
@@ -95370,7 +95664,7 @@ var BashTool = class {
|
|
|
95370
95664
|
brief: `Backgrounded ${taskId}`
|
|
95371
95665
|
}, builder, "foreground_detached");
|
|
95372
95666
|
}
|
|
95373
|
-
return this.foregroundCompletionResult(taskId, proc, builder, foregroundTimeoutMs);
|
|
95667
|
+
return await this.foregroundCompletionResult(taskId, proc, builder, foregroundTimeoutMs);
|
|
95374
95668
|
} finally {
|
|
95375
95669
|
collectForegroundOutput = false;
|
|
95376
95670
|
}
|
|
@@ -95394,19 +95688,32 @@ var BashTool = class {
|
|
|
95394
95688
|
output: "description is required when run_in_background is true."
|
|
95395
95689
|
};
|
|
95396
95690
|
}
|
|
95397
|
-
foregroundCompletionResult(taskId, proc, builder, foregroundTimeoutMs) {
|
|
95691
|
+
async foregroundCompletionResult(taskId, proc, builder, foregroundTimeoutMs) {
|
|
95398
95692
|
const current = this.backgroundManager.getTask(taskId);
|
|
95399
95693
|
const exitCode = current?.kind === "process" ? current.exitCode : proc.exitCode;
|
|
95694
|
+
let result;
|
|
95400
95695
|
if (current?.status === "timed_out") {
|
|
95401
95696
|
const timeoutLabel = formatTimeoutLabel(foregroundTimeoutMs);
|
|
95402
|
-
|
|
95697
|
+
result = builder.error(`Command killed by timeout (${timeoutLabel})`, { brief: `Killed by timeout (${timeoutLabel})` });
|
|
95698
|
+
} else if (current?.status === "killed" && current.stopReason === USER_INTERRUPT_REASON) result = builder.error(USER_INTERRUPT_REASON, { brief: USER_INTERRUPT_REASON });
|
|
95699
|
+
else if ((current?.status === "failed" || current?.status === "killed") && current.stopReason !== void 0) result = builder.error(current.stopReason, { brief: current.stopReason });
|
|
95700
|
+
else if (exitCode === 0) result = builder.ok("Command executed successfully.");
|
|
95701
|
+
else {
|
|
95702
|
+
if (builder.nChars === 0) builder.write(`Process exited with code ${String(exitCode)}`);
|
|
95703
|
+
result = builder.error(`Command failed with exit code: ${String(exitCode)}.`, { brief: `Failed with exit code: ${String(exitCode)}` });
|
|
95403
95704
|
}
|
|
95404
|
-
|
|
95405
|
-
|
|
95406
|
-
|
|
95407
|
-
if (
|
|
95408
|
-
|
|
95409
|
-
|
|
95705
|
+
return this.addForegroundOutputReference(taskId, result);
|
|
95706
|
+
}
|
|
95707
|
+
async addForegroundOutputReference(taskId, result) {
|
|
95708
|
+
if (!result.truncated) return result;
|
|
95709
|
+
const output = await this.backgroundManager.getOutputSnapshot(taskId, 0);
|
|
95710
|
+
if (!output.fullOutputAvailable || output.outputPath === void 0) return result;
|
|
95711
|
+
const taskOutputHint = this.allowBackground ? `, or TaskOutput(task_id="${taskId}", block=false)` : "";
|
|
95712
|
+
const reference = `\n\n[Full output saved]\ntask_id: ${taskId}\noutput_path: ${output.outputPath}\noutput_size_bytes: ${String(output.outputSizeBytes)}\nnext_step: Use Read with output_path to page through the full log${taskOutputHint}.`;
|
|
95713
|
+
return {
|
|
95714
|
+
...result,
|
|
95715
|
+
output: `${result.output}${reference}`
|
|
95716
|
+
};
|
|
95410
95717
|
}
|
|
95411
95718
|
backgroundStartedResult(taskId, proc, description, labels, builder = new ToolResultBuilder(), scenario = "background_started") {
|
|
95412
95719
|
const status = this.backgroundManager.getTask(taskId)?.status ?? "running";
|
|
@@ -95632,6 +95939,8 @@ function classifySearchError(error) {
|
|
|
95632
95939
|
}
|
|
95633
95940
|
//#endregion
|
|
95634
95941
|
//#region ../agent-core/src/agent/tool/index.ts
|
|
95942
|
+
/** Foreground timeout (seconds) for a user-initiated `!` shell command. */
|
|
95943
|
+
const SHELL_FOREGROUND_TIMEOUT_S = 120;
|
|
95635
95944
|
var ToolManager = class {
|
|
95636
95945
|
agent;
|
|
95637
95946
|
builtinTools = /* @__PURE__ */ new Map();
|
|
@@ -95645,6 +95954,9 @@ var ToolManager = class {
|
|
|
95645
95954
|
mcpAccessPatterns = [];
|
|
95646
95955
|
store = {};
|
|
95647
95956
|
mcpToolStatusUnsubscribe;
|
|
95957
|
+
/** Abort controllers for in-flight `!` shell commands, keyed by commandId so
|
|
95958
|
+
* the TUI can cancel (Esc / Ctrl+C) a running command. */
|
|
95959
|
+
shellCommandControllers = /* @__PURE__ */ new Map();
|
|
95648
95960
|
constructor(agent) {
|
|
95649
95961
|
this.agent = agent;
|
|
95650
95962
|
this.attachMcpTools();
|
|
@@ -95676,6 +95988,95 @@ var ToolManager = class {
|
|
|
95676
95988
|
});
|
|
95677
95989
|
this.store[key] = value;
|
|
95678
95990
|
}
|
|
95991
|
+
/**
|
|
95992
|
+
* Execute a user-initiated `!` shell command. Reuses the builtin Bash tool
|
|
95993
|
+
* (same kaos / cwd / BackgroundManager as the agent), recording the command
|
|
95994
|
+
* and its output as `shell_command`-origin messages. It does NOT start a turn
|
|
95995
|
+
* — the model is not prompted (parity with claude-code's `shouldQuery: false`).
|
|
95996
|
+
*/
|
|
95997
|
+
async runShellCommand(command, commandId) {
|
|
95998
|
+
this.agent.context.appendBashInput(command);
|
|
95999
|
+
const bash = this.builtinTools.get("Bash");
|
|
96000
|
+
if (bash === void 0) {
|
|
96001
|
+
const error = "Bash tool is not available.";
|
|
96002
|
+
this.agent.context.appendBashOutput("", error);
|
|
96003
|
+
return {
|
|
96004
|
+
stdout: "",
|
|
96005
|
+
stderr: error,
|
|
96006
|
+
isError: true
|
|
96007
|
+
};
|
|
96008
|
+
}
|
|
96009
|
+
let stdout = "";
|
|
96010
|
+
let stderr = "";
|
|
96011
|
+
let isError;
|
|
96012
|
+
const controller = new AbortController();
|
|
96013
|
+
if (commandId !== void 0) this.shellCommandControllers.set(commandId, controller);
|
|
96014
|
+
try {
|
|
96015
|
+
const execution = await bash.resolveExecution({
|
|
96016
|
+
command,
|
|
96017
|
+
timeout: SHELL_FOREGROUND_TIMEOUT_S
|
|
96018
|
+
});
|
|
96019
|
+
if (!("execute" in execution)) {
|
|
96020
|
+
const output = typeof execution.output === "string" ? execution.output : "Command failed.";
|
|
96021
|
+
this.agent.context.appendBashOutput("", output);
|
|
96022
|
+
return {
|
|
96023
|
+
stdout: "",
|
|
96024
|
+
stderr: output,
|
|
96025
|
+
isError: true
|
|
96026
|
+
};
|
|
96027
|
+
}
|
|
96028
|
+
const result = await execution.execute({
|
|
96029
|
+
turnId: "",
|
|
96030
|
+
toolCallId: "shell-command",
|
|
96031
|
+
signal: controller.signal,
|
|
96032
|
+
onUpdate: (update) => {
|
|
96033
|
+
if (update.kind === "stdout") stdout += update.text ?? "";
|
|
96034
|
+
else if (update.kind === "stderr") stderr += update.text ?? "";
|
|
96035
|
+
else return;
|
|
96036
|
+
if (commandId !== void 0) this.agent.emitEvent({
|
|
96037
|
+
type: "shell.output",
|
|
96038
|
+
commandId,
|
|
96039
|
+
update
|
|
96040
|
+
});
|
|
96041
|
+
},
|
|
96042
|
+
onForegroundTaskStart: (taskId) => {
|
|
96043
|
+
if (commandId !== void 0) this.agent.emitEvent({
|
|
96044
|
+
type: "shell.started",
|
|
96045
|
+
commandId,
|
|
96046
|
+
taskId
|
|
96047
|
+
});
|
|
96048
|
+
}
|
|
96049
|
+
});
|
|
96050
|
+
isError = result.isError === true;
|
|
96051
|
+
if (typeof result.output === "string" && result.output.startsWith("task_id: ")) {
|
|
96052
|
+
this.agent.context.injectAndNotify(result.output, {
|
|
96053
|
+
kind: "injection",
|
|
96054
|
+
variant: "shell_command_backgrounded"
|
|
96055
|
+
});
|
|
96056
|
+
return {
|
|
96057
|
+
stdout: result.output,
|
|
96058
|
+
stderr: "",
|
|
96059
|
+
isError: false,
|
|
96060
|
+
backgrounded: true
|
|
96061
|
+
};
|
|
96062
|
+
}
|
|
96063
|
+
if (isError && stdout.length === 0 && stderr.length === 0 && typeof result.output === "string" && result.output.length > 0) stderr = result.output;
|
|
96064
|
+
} catch (error) {
|
|
96065
|
+
stderr += error instanceof Error ? error.message : String(error);
|
|
96066
|
+
isError = true;
|
|
96067
|
+
} finally {
|
|
96068
|
+
if (commandId !== void 0) this.shellCommandControllers.delete(commandId);
|
|
96069
|
+
}
|
|
96070
|
+
this.agent.context.appendBashOutput(stdout, stderr, isError);
|
|
96071
|
+
return {
|
|
96072
|
+
stdout,
|
|
96073
|
+
stderr,
|
|
96074
|
+
isError
|
|
96075
|
+
};
|
|
96076
|
+
}
|
|
96077
|
+
cancelShellCommand(commandId) {
|
|
96078
|
+
this.shellCommandControllers.get(commandId)?.abort();
|
|
96079
|
+
}
|
|
95679
96080
|
registerUserTool(input) {
|
|
95680
96081
|
this.agent.records.logRecord({
|
|
95681
96082
|
type: "tools.register_user_tool",
|
|
@@ -96392,6 +96793,8 @@ var Agent$1 = class {
|
|
|
96392
96793
|
prompt: (payload) => {
|
|
96393
96794
|
this.turn.prompt(payload.input);
|
|
96394
96795
|
},
|
|
96796
|
+
runShellCommand: (payload) => this.tools.runShellCommand(payload.command, payload.commandId),
|
|
96797
|
+
cancelShellCommand: (payload) => this.tools.cancelShellCommand(payload.commandId),
|
|
96395
96798
|
steer: (payload) => {
|
|
96396
96799
|
this.telemetry.track("input_steer", { parts: payload.input.length });
|
|
96397
96800
|
this.turn.steer(payload.input);
|
|
@@ -125836,7 +126239,7 @@ var StdioMcpClient = class {
|
|
|
125836
126239
|
command: config.command,
|
|
125837
126240
|
args: config.args,
|
|
125838
126241
|
env: mergeStdioEnv(config.env),
|
|
125839
|
-
cwd: config.cwd,
|
|
126242
|
+
cwd: resolveStdioCwd(config.cwd, options.defaultCwd),
|
|
125840
126243
|
stderr: "pipe"
|
|
125841
126244
|
});
|
|
125842
126245
|
this.transport.stderr?.on("data", (chunk) => {
|
|
@@ -125949,6 +126352,11 @@ var BoundedTail = class {
|
|
|
125949
126352
|
return this.buffer;
|
|
125950
126353
|
}
|
|
125951
126354
|
};
|
|
126355
|
+
function resolveStdioCwd(configCwd, defaultCwd) {
|
|
126356
|
+
if (configCwd === void 0) return defaultCwd;
|
|
126357
|
+
if (defaultCwd !== void 0 && !isAbsolute$1(configCwd)) return resolve$2(defaultCwd, configCwd);
|
|
126358
|
+
return configCwd;
|
|
126359
|
+
}
|
|
125952
126360
|
function mergeStdioEnv(configEnv, parentEnv = process.env) {
|
|
125953
126361
|
const merged = {};
|
|
125954
126362
|
for (const [key, value] of Object.entries(parentEnv)) if (value !== void 0) merged[key] = value;
|
|
@@ -126186,7 +126594,10 @@ var McpConnectionManager = class {
|
|
|
126186
126594
|
}
|
|
126187
126595
|
createClient(config, name) {
|
|
126188
126596
|
const toolCallTimeoutMs = config.toolTimeoutMs;
|
|
126189
|
-
if (config.transport === "stdio") return new StdioMcpClient(config, {
|
|
126597
|
+
if (config.transport === "stdio") return new StdioMcpClient(config, {
|
|
126598
|
+
toolCallTimeoutMs,
|
|
126599
|
+
defaultCwd: this.options.stdioCwd
|
|
126600
|
+
});
|
|
126190
126601
|
if (config.transport === "sse") return new SseMcpClient(config, {
|
|
126191
126602
|
toolCallTimeoutMs,
|
|
126192
126603
|
envLookup: this.options.envLookup,
|
|
@@ -126477,6 +126888,7 @@ var Session$1 = class {
|
|
|
126477
126888
|
custom: {}
|
|
126478
126889
|
};
|
|
126479
126890
|
writeMetadataPromise = Promise.resolve();
|
|
126891
|
+
agentsMdWarning;
|
|
126480
126892
|
constructor(options) {
|
|
126481
126893
|
this.options = options;
|
|
126482
126894
|
this.logHandle = options.id === void 0 ? void 0 : getRootLogger().attachSession({
|
|
@@ -126497,7 +126909,8 @@ var Session$1 = class {
|
|
|
126497
126909
|
this.skills = new SessionSkillRegistry({ sessionId: options.id });
|
|
126498
126910
|
this.mcp = new McpConnectionManager({
|
|
126499
126911
|
oauthService: new McpOAuthService({ kimiHomeDir: options.kimiHomeDir }),
|
|
126500
|
-
log: this.log
|
|
126912
|
+
log: this.log,
|
|
126913
|
+
stdioCwd: options.kaos.getcwd()
|
|
126501
126914
|
});
|
|
126502
126915
|
this.mcp.onStatusChange((entry) => {
|
|
126503
126916
|
this.onMcpServerStatusChange(entry);
|
|
@@ -126692,6 +127105,36 @@ var Session$1 = class {
|
|
|
126692
127105
|
async bootstrapAgentProfile(agent, profile) {
|
|
126693
127106
|
const context = await prepareSystemPromptContext(this.systemContextKaos(agent.kaos.getcwd()), this.options.kimiHomeDir, { additionalDirs: this.additionalDirs });
|
|
126694
127107
|
agent.useProfile(profile, context);
|
|
127108
|
+
const { agentsMdWarning } = context;
|
|
127109
|
+
if (agentsMdWarning !== void 0) {
|
|
127110
|
+
this.agentsMdWarning = agentsMdWarning;
|
|
127111
|
+
log.warn("AGENTS.md exceeds recommended size", { message: agentsMdWarning });
|
|
127112
|
+
agent.emitEvent({
|
|
127113
|
+
type: "warning",
|
|
127114
|
+
message: agentsMdWarning,
|
|
127115
|
+
code: "agents-md-oversized"
|
|
127116
|
+
});
|
|
127117
|
+
}
|
|
127118
|
+
}
|
|
127119
|
+
async getSessionWarnings() {
|
|
127120
|
+
const warnings = [];
|
|
127121
|
+
const agentsMdWarning = await this.computeAgentsMdWarning();
|
|
127122
|
+
if (agentsMdWarning !== void 0) warnings.push({
|
|
127123
|
+
code: "agents-md-oversized",
|
|
127124
|
+
message: agentsMdWarning,
|
|
127125
|
+
severity: "warning"
|
|
127126
|
+
});
|
|
127127
|
+
return warnings;
|
|
127128
|
+
}
|
|
127129
|
+
async computeAgentsMdWarning() {
|
|
127130
|
+
if (this.agentsMdWarning !== void 0) return this.agentsMdWarning;
|
|
127131
|
+
try {
|
|
127132
|
+
const context = await prepareSystemPromptContext(this.systemContextKaos(this.toolKaos.getcwd()), this.options.kimiHomeDir, { additionalDirs: this.additionalDirs });
|
|
127133
|
+
this.agentsMdWarning = context.agentsMdWarning;
|
|
127134
|
+
} catch (error) {
|
|
127135
|
+
log.warn("failed to compute AGENTS.md warning", { error });
|
|
127136
|
+
}
|
|
127137
|
+
return this.agentsMdWarning;
|
|
126695
127138
|
}
|
|
126696
127139
|
async generateAgentsMd() {
|
|
126697
127140
|
await this.skillsReady;
|
|
@@ -126715,6 +127158,45 @@ var Session$1 = class {
|
|
|
126715
127158
|
throw new KimiError(ErrorCodes.SESSION_INIT_FAILED, error instanceof Error ? error.message : "Init failed", { cause: error });
|
|
126716
127159
|
}
|
|
126717
127160
|
}
|
|
127161
|
+
/**
|
|
127162
|
+
* Appends a fresh `<plugin_session_start>` system reminder to the main agent
|
|
127163
|
+
* using the currently enabled plugins, then flushes records so the reminder is
|
|
127164
|
+
* persisted and visible on the wire. Used by the explicit `/reload` flow after
|
|
127165
|
+
* the session has been re-resumed with reloaded plugin state.
|
|
127166
|
+
*
|
|
127167
|
+
* When no plugin session start is currently resolvable but an earlier
|
|
127168
|
+
* When no plugin session start is currently resolvable but the context may still
|
|
127169
|
+
* carry stale plugin guidance — either an earlier `<plugin_session_start>`
|
|
127170
|
+
* reminder, or a compaction summary that may have folded one in — appends a
|
|
127171
|
+
* neutralizing reminder instead, so the model does not keep following stale
|
|
127172
|
+
* plugin instructions and the turn-loop injector does not dedup against them.
|
|
127173
|
+
*/
|
|
127174
|
+
async appendPluginSessionStartReminder() {
|
|
127175
|
+
await this.skillsReady;
|
|
127176
|
+
const mainAgent = this.requireMainAgent();
|
|
127177
|
+
const reminder = renderPluginSessionStartReminder({
|
|
127178
|
+
sessionStarts: mainAgent.pluginSessionStarts,
|
|
127179
|
+
registry: mainAgent.skills?.registry,
|
|
127180
|
+
log: mainAgent.log
|
|
127181
|
+
});
|
|
127182
|
+
if (reminder !== void 0) mainAgent.context.appendSystemReminder(`${reminder}\n\nThis supersedes any earlier plugin_session_start reminder in this session.`, {
|
|
127183
|
+
kind: "injection",
|
|
127184
|
+
variant: "plugin_session_start"
|
|
127185
|
+
});
|
|
127186
|
+
else if (this.shouldNeutralizePluginSessionStart(mainAgent)) mainAgent.context.appendSystemReminder("There are currently no active plugin session starts. This supersedes any earlier plugin_session_start reminder in this session.", {
|
|
127187
|
+
kind: "injection",
|
|
127188
|
+
variant: "plugin_session_start"
|
|
127189
|
+
});
|
|
127190
|
+
else return;
|
|
127191
|
+
await mainAgent.records.flush();
|
|
127192
|
+
}
|
|
127193
|
+
shouldNeutralizePluginSessionStart(mainAgent) {
|
|
127194
|
+
return mainAgent.context.history.some((message) => {
|
|
127195
|
+
const kind = message.origin?.kind;
|
|
127196
|
+
if (kind === "injection") return message.origin?.variant === "plugin_session_start";
|
|
127197
|
+
return kind === "compaction_summary";
|
|
127198
|
+
});
|
|
127199
|
+
}
|
|
126718
127200
|
get hasActiveTurn() {
|
|
126719
127201
|
for (const agent of this.readyAgents()) if (agent.turn.hasActiveTurn) return true;
|
|
126720
127202
|
return false;
|
|
@@ -126940,9 +127422,10 @@ function createRPC() {
|
|
|
126940
127422
|
signal?.throwIfAborted();
|
|
126941
127423
|
let response;
|
|
126942
127424
|
try {
|
|
127425
|
+
const handlerResult = signal === void 0 ? fn(rpcPayload) : fn(rpcPayload, { signal });
|
|
126943
127426
|
response = {
|
|
126944
127427
|
ok: true,
|
|
126945
|
-
value: await abortableRpc(Promise.resolve(
|
|
127428
|
+
value: await abortableRpc(Promise.resolve(handlerResult), signal)
|
|
126946
127429
|
};
|
|
126947
127430
|
} catch (error) {
|
|
126948
127431
|
signal?.throwIfAborted();
|
|
@@ -141407,6 +141890,9 @@ var SessionAPIImpl = class {
|
|
|
141407
141890
|
generateAgentsMd(_payload) {
|
|
141408
141891
|
return this.session.generateAgentsMd();
|
|
141409
141892
|
}
|
|
141893
|
+
getSessionWarnings(_payload) {
|
|
141894
|
+
return this.session.getSessionWarnings();
|
|
141895
|
+
}
|
|
141410
141896
|
addAdditionalDir(payload) {
|
|
141411
141897
|
return this.session.addAdditionalDir(payload.path, payload.persist);
|
|
141412
141898
|
}
|
|
@@ -141417,6 +141903,12 @@ var SessionAPIImpl = class {
|
|
|
141417
141903
|
async steer({ agentId, ...payload }) {
|
|
141418
141904
|
return (await this.getAgent(agentId)).steer(payload);
|
|
141419
141905
|
}
|
|
141906
|
+
async runShellCommand({ agentId, ...payload }) {
|
|
141907
|
+
return (await this.getAgent(agentId)).runShellCommand(payload);
|
|
141908
|
+
}
|
|
141909
|
+
async cancelShellCommand({ agentId, ...payload }) {
|
|
141910
|
+
return (await this.getAgent(agentId)).cancelShellCommand(payload);
|
|
141911
|
+
}
|
|
141420
141912
|
async cancel({ agentId, ...payload }) {
|
|
141421
141913
|
return (await this.getAgent(agentId)).cancel(payload);
|
|
141422
141914
|
}
|
|
@@ -143273,6 +143765,7 @@ var KimiCore = class {
|
|
|
143273
143765
|
throw error;
|
|
143274
143766
|
}
|
|
143275
143767
|
this.sessions.set(summary.id, session);
|
|
143768
|
+
if (overrides.forcePluginSessionStartReminder === true) await session.appendPluginSessionStartReminder();
|
|
143276
143769
|
return resumeSessionResult(summary, session, warning);
|
|
143277
143770
|
}
|
|
143278
143771
|
async reloadSession(input) {
|
|
@@ -143286,7 +143779,7 @@ var KimiCore = class {
|
|
|
143286
143779
|
await active.closeForReload();
|
|
143287
143780
|
this.sessions.delete(summary.id);
|
|
143288
143781
|
}
|
|
143289
|
-
return this.
|
|
143782
|
+
return this.resumeSessionWithOverrides({ sessionId: summary.id }, { forcePluginSessionStartReminder: input.forcePluginSessionStartReminder });
|
|
143290
143783
|
}
|
|
143291
143784
|
async forkSession(input) {
|
|
143292
143785
|
const source = await this.sessionStore.get(input.sessionId);
|
|
@@ -143361,6 +143854,12 @@ var KimiCore = class {
|
|
|
143361
143854
|
prompt({ sessionId, ...payload }) {
|
|
143362
143855
|
return this.sessionApi(sessionId).prompt(payload);
|
|
143363
143856
|
}
|
|
143857
|
+
runShellCommand({ sessionId, ...payload }) {
|
|
143858
|
+
return this.sessionApi(sessionId).runShellCommand(payload);
|
|
143859
|
+
}
|
|
143860
|
+
cancelShellCommand({ sessionId, ...payload }) {
|
|
143861
|
+
return this.sessionApi(sessionId).cancelShellCommand(payload);
|
|
143862
|
+
}
|
|
143364
143863
|
steer({ sessionId, ...payload }) {
|
|
143365
143864
|
return this.sessionApi(sessionId).steer(payload);
|
|
143366
143865
|
}
|
|
@@ -143473,6 +143972,9 @@ var KimiCore = class {
|
|
|
143473
143972
|
generateAgentsMd({ sessionId, ...payload }) {
|
|
143474
143973
|
return this.sessionApi(sessionId).generateAgentsMd(payload);
|
|
143475
143974
|
}
|
|
143975
|
+
getSessionWarnings({ sessionId, ...payload }) {
|
|
143976
|
+
return this.sessionApi(sessionId).getSessionWarnings(payload);
|
|
143977
|
+
}
|
|
143476
143978
|
addAdditionalDir({ sessionId, ...payload }) {
|
|
143477
143979
|
return this.requireSession(sessionId).addAdditionalDir(payload.path, payload.persist);
|
|
143478
143980
|
}
|
|
@@ -144279,8 +144781,8 @@ var BridgeClientAPI = class {
|
|
|
144279
144781
|
async requestApproval(request) {
|
|
144280
144782
|
return this.deps.approvalService.request(request);
|
|
144281
144783
|
}
|
|
144282
|
-
async requestQuestion(request) {
|
|
144283
|
-
return this.deps.questionService.request(request);
|
|
144784
|
+
async requestQuestion(request, options) {
|
|
144785
|
+
return this.deps.questionService.request(request, options);
|
|
144284
144786
|
}
|
|
144285
144787
|
async toolCall(request) {
|
|
144286
144788
|
return {
|
|
@@ -151298,6 +151800,7 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151298
151800
|
logger;
|
|
151299
151801
|
eventService;
|
|
151300
151802
|
_serviceBrand;
|
|
151803
|
+
homeDir;
|
|
151301
151804
|
sessionsDir;
|
|
151302
151805
|
registryPath;
|
|
151303
151806
|
opQueue = Promise.resolve();
|
|
@@ -151305,12 +151808,33 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151305
151808
|
super();
|
|
151306
151809
|
this.logger = logger;
|
|
151307
151810
|
this.eventService = eventService;
|
|
151811
|
+
this.homeDir = env.homeDir;
|
|
151308
151812
|
this.sessionsDir = join(env.homeDir, "sessions");
|
|
151309
151813
|
this.registryPath = join(env.homeDir, WORKSPACE_REGISTRY_FILE);
|
|
151310
151814
|
}
|
|
151311
151815
|
async list() {
|
|
151312
151816
|
const file = await this.runExclusive(() => this.readRegistry());
|
|
151313
|
-
|
|
151817
|
+
const deleted = new Set(file.deleted_workspace_ids);
|
|
151818
|
+
const result = [];
|
|
151819
|
+
for (const [id, entry] of Object.entries(file.workspaces)) result.push(await this.hydrate(id, entry));
|
|
151820
|
+
const index = await readSessionIndex(this.homeDir, this.sessionsDir);
|
|
151821
|
+
const derived = /* @__PURE__ */ new Map();
|
|
151822
|
+
for (const entry of index.values()) {
|
|
151823
|
+
const id = encodeWorkDirKey(entry.workDir);
|
|
151824
|
+
if (file.workspaces[id] !== void 0 || deleted.has(id)) continue;
|
|
151825
|
+
derived.set(id, entry.workDir);
|
|
151826
|
+
}
|
|
151827
|
+
for (const [id, workDir] of derived) {
|
|
151828
|
+
const sessionCount = await countActiveSessions(join(this.sessionsDir, id));
|
|
151829
|
+
if (sessionCount === 0) continue;
|
|
151830
|
+
result.push(await this.hydrate(id, {
|
|
151831
|
+
root: workDir,
|
|
151832
|
+
name: basename(workDir),
|
|
151833
|
+
created_at: "",
|
|
151834
|
+
last_opened_at: ""
|
|
151835
|
+
}, sessionCount));
|
|
151836
|
+
}
|
|
151837
|
+
return result.sort((a, b) => b.last_opened_at < a.last_opened_at ? -1 : 1);
|
|
151314
151838
|
}
|
|
151315
151839
|
async get(workspaceId) {
|
|
151316
151840
|
const entry = await this.runExclusive(async () => {
|
|
@@ -151347,6 +151871,7 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151347
151871
|
last_opened_at: now
|
|
151348
151872
|
};
|
|
151349
151873
|
file.workspaces[workspaceId] = next;
|
|
151874
|
+
file.deleted_workspace_ids = file.deleted_workspace_ids.filter((id) => id !== workspaceId);
|
|
151350
151875
|
await this.writeRegistry(file);
|
|
151351
151876
|
return {
|
|
151352
151877
|
entry: next,
|
|
@@ -151384,10 +151909,18 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151384
151909
|
const root = await this.runExclusive(async () => {
|
|
151385
151910
|
const file = await this.readRegistry();
|
|
151386
151911
|
const existing = file.workspaces[workspaceId];
|
|
151387
|
-
|
|
151388
|
-
|
|
151912
|
+
let root;
|
|
151913
|
+
if (existing !== void 0) {
|
|
151914
|
+
delete file.workspaces[workspaceId];
|
|
151915
|
+
root = existing.root;
|
|
151916
|
+
} else {
|
|
151917
|
+
const derived = await this.findDerivedWorkDir(workspaceId);
|
|
151918
|
+
if (derived === void 0) throw new WorkspaceNotFoundError(workspaceId);
|
|
151919
|
+
root = derived;
|
|
151920
|
+
}
|
|
151921
|
+
if (!file.deleted_workspace_ids.includes(workspaceId)) file.deleted_workspace_ids.push(workspaceId);
|
|
151389
151922
|
await this.writeRegistry(file);
|
|
151390
|
-
return
|
|
151923
|
+
return root;
|
|
151391
151924
|
});
|
|
151392
151925
|
this.publishWorkspace({
|
|
151393
151926
|
type: "event.workspace.deleted",
|
|
@@ -151399,11 +151932,19 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151399
151932
|
const entry = await this.runExclusive(async () => {
|
|
151400
151933
|
return (await this.readRegistry()).workspaces[workspaceId] ?? null;
|
|
151401
151934
|
});
|
|
151402
|
-
if (entry
|
|
151403
|
-
|
|
151935
|
+
if (entry !== null) return entry.root;
|
|
151936
|
+
const derived = await this.findDerivedWorkDir(workspaceId);
|
|
151937
|
+
if (derived !== void 0) return derived;
|
|
151938
|
+
throw new WorkspaceNotFoundError(workspaceId);
|
|
151939
|
+
}
|
|
151940
|
+
/** Look up a derived workspace's workDir from the session index, or undefined
|
|
151941
|
+
* if the id is not a known derived bucket. */
|
|
151942
|
+
async findDerivedWorkDir(workspaceId) {
|
|
151943
|
+
const index = await readSessionIndex(this.homeDir, this.sessionsDir);
|
|
151944
|
+
for (const e of index.values()) if (encodeWorkDirKey(e.workDir) === workspaceId) return e.workDir;
|
|
151404
151945
|
}
|
|
151405
|
-
async hydrate(workspaceId, entry) {
|
|
151406
|
-
const [{ is_git_repo, branch }, session_count] = await Promise.all([detectGit(entry.root), countActiveSessions(join(this.sessionsDir, workspaceId))]);
|
|
151946
|
+
async hydrate(workspaceId, entry, sessionCount) {
|
|
151947
|
+
const [{ is_git_repo, branch }, session_count] = await Promise.all([detectGit(entry.root), sessionCount ?? countActiveSessions(join(this.sessionsDir, workspaceId))]);
|
|
151407
151948
|
return {
|
|
151408
151949
|
id: workspaceId,
|
|
151409
151950
|
root: entry.root,
|
|
@@ -151445,7 +151986,8 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151445
151986
|
const code = err.code;
|
|
151446
151987
|
if (code === "ENOENT" || code === "ENOTDIR") return {
|
|
151447
151988
|
version: WORKSPACE_REGISTRY_VERSION,
|
|
151448
|
-
workspaces: {}
|
|
151989
|
+
workspaces: {},
|
|
151990
|
+
deleted_workspace_ids: []
|
|
151449
151991
|
};
|
|
151450
151992
|
throw err;
|
|
151451
151993
|
}
|
|
@@ -151459,14 +152001,16 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151459
152001
|
}, "workspaces.json malformed; treating as empty");
|
|
151460
152002
|
return {
|
|
151461
152003
|
version: WORKSPACE_REGISTRY_VERSION,
|
|
151462
|
-
workspaces: {}
|
|
152004
|
+
workspaces: {},
|
|
152005
|
+
deleted_workspace_ids: []
|
|
151463
152006
|
};
|
|
151464
152007
|
}
|
|
151465
152008
|
if (typeof parsed !== "object" || parsed === null || typeof parsed.workspaces !== "object" || parsed.workspaces === null) {
|
|
151466
152009
|
this.logger.warn({ path: this.registryPath }, "workspaces.json missing required keys; treating as empty");
|
|
151467
152010
|
return {
|
|
151468
152011
|
version: WORKSPACE_REGISTRY_VERSION,
|
|
151469
|
-
workspaces: {}
|
|
152012
|
+
workspaces: {},
|
|
152013
|
+
deleted_workspace_ids: []
|
|
151470
152014
|
};
|
|
151471
152015
|
}
|
|
151472
152016
|
const rawWorkspaces = parsed.workspaces;
|
|
@@ -151475,9 +152019,12 @@ let WorkspaceRegistryService = class WorkspaceRegistryService extends Disposable
|
|
|
151475
152019
|
const entry = this.sanitizeEntry(value);
|
|
151476
152020
|
if (entry !== null) workspaces[id] = entry;
|
|
151477
152021
|
}
|
|
152022
|
+
const version = typeof parsed.version === "number" ? parsed.version : WORKSPACE_REGISTRY_VERSION;
|
|
152023
|
+
const rawDeleted = parsed.deleted_workspace_ids;
|
|
151478
152024
|
return {
|
|
151479
|
-
version
|
|
151480
|
-
workspaces
|
|
152025
|
+
version,
|
|
152026
|
+
workspaces,
|
|
152027
|
+
deleted_workspace_ids: Array.isArray(rawDeleted) ? rawDeleted.filter((id) => typeof id === "string") : []
|
|
151481
152028
|
};
|
|
151482
152029
|
}
|
|
151483
152030
|
sanitizeEntry(value) {
|
|
@@ -152926,7 +153473,6 @@ let SessionService = class SessionService extends Disposable {
|
|
|
152926
153473
|
case "event.question.requested":
|
|
152927
153474
|
case "event.question.answered":
|
|
152928
153475
|
case "event.question.dismissed":
|
|
152929
|
-
case "event.question.expired":
|
|
152930
153476
|
this._emitStatusChanged(sessionId);
|
|
152931
153477
|
break;
|
|
152932
153478
|
}
|
|
@@ -153104,6 +153650,17 @@ let SessionService = class SessionService extends Disposable {
|
|
|
153104
153650
|
context_usage: contextUsage
|
|
153105
153651
|
};
|
|
153106
153652
|
}
|
|
153653
|
+
async getSessionWarnings(id) {
|
|
153654
|
+
if (!(await this.core.rpc.listSessions({})).some((s) => s.id === id)) throw new SessionNotFoundError(id);
|
|
153655
|
+
try {
|
|
153656
|
+
await this.core.rpc.resumeSession({ sessionId: id });
|
|
153657
|
+
} catch {}
|
|
153658
|
+
try {
|
|
153659
|
+
return await this.core.rpc.getSessionWarnings({ sessionId: id });
|
|
153660
|
+
} catch {
|
|
153661
|
+
return [];
|
|
153662
|
+
}
|
|
153663
|
+
}
|
|
153107
153664
|
async compact(id, input) {
|
|
153108
153665
|
if ((await this.core.rpc.listSessions({})).find((s) => s.id === id) === void 0) throw new SessionNotFoundError(id);
|
|
153109
153666
|
await this.core.rpc.resumeSession({ sessionId: id });
|
|
@@ -154345,7 +154902,7 @@ let PromptService = class PromptService extends Disposable {
|
|
|
154345
154902
|
});
|
|
154346
154903
|
}
|
|
154347
154904
|
async _requireSession(sid) {
|
|
154348
|
-
if (
|
|
154905
|
+
if ((await this.core.rpc.listSessions({ sessionId: sid })).length === 0) throw new SessionNotFoundError(sid);
|
|
154349
154906
|
}
|
|
154350
154907
|
dispose() {
|
|
154351
154908
|
if (this._store.isDisposed) return;
|
|
@@ -155111,9 +155668,12 @@ var Session = class {
|
|
|
155111
155668
|
this.ensureOpen();
|
|
155112
155669
|
return this.resumeState;
|
|
155113
155670
|
}
|
|
155114
|
-
async reloadSession() {
|
|
155671
|
+
async reloadSession(options) {
|
|
155115
155672
|
this.ensureOpen();
|
|
155116
|
-
const summary = await this.rpc.reloadSession({
|
|
155673
|
+
const summary = await this.rpc.reloadSession({
|
|
155674
|
+
sessionId: this.id,
|
|
155675
|
+
forcePluginSessionStartReminder: options?.forcePluginSessionStartReminder
|
|
155676
|
+
});
|
|
155117
155677
|
this.summary = summary;
|
|
155118
155678
|
this.resumeState = resumeStateFromSummary(summary);
|
|
155119
155679
|
return summary;
|
|
@@ -155139,6 +155699,25 @@ var Session = class {
|
|
|
155139
155699
|
input: normalizePromptInput(input)
|
|
155140
155700
|
});
|
|
155141
155701
|
}
|
|
155702
|
+
/** Execute a user-initiated `!` shell command (silent — does not prompt the
|
|
155703
|
+
* model). Resolves with the command's stdout/stderr for immediate display.
|
|
155704
|
+
* Pass `commandId` to receive live `shell.output` events for this command. */
|
|
155705
|
+
async runShellCommand(command, options) {
|
|
155706
|
+
this.ensureOpen();
|
|
155707
|
+
return this.rpc.runShellCommand({
|
|
155708
|
+
sessionId: this.id,
|
|
155709
|
+
command,
|
|
155710
|
+
commandId: options?.commandId
|
|
155711
|
+
});
|
|
155712
|
+
}
|
|
155713
|
+
/** Cancel a running `!` shell command by its commandId (e.g. on Esc / Ctrl+C). */
|
|
155714
|
+
async cancelShellCommand(commandId) {
|
|
155715
|
+
this.ensureOpen();
|
|
155716
|
+
return this.rpc.cancelShellCommand({
|
|
155717
|
+
sessionId: this.id,
|
|
155718
|
+
commandId
|
|
155719
|
+
});
|
|
155720
|
+
}
|
|
155142
155721
|
async steer(input) {
|
|
155143
155722
|
this.ensureOpen();
|
|
155144
155723
|
await this.rpc.steer({
|
|
@@ -155157,6 +155736,10 @@ var Session = class {
|
|
|
155157
155736
|
this.ensureOpen();
|
|
155158
155737
|
await this.rpc.generateAgentsMd({ sessionId: this.id });
|
|
155159
155738
|
}
|
|
155739
|
+
async getSessionWarnings() {
|
|
155740
|
+
this.ensureOpen();
|
|
155741
|
+
return this.rpc.getSessionWarnings({ sessionId: this.id });
|
|
155742
|
+
}
|
|
155160
155743
|
async addAdditionalDir(path, options) {
|
|
155161
155744
|
this.ensureOpen();
|
|
155162
155745
|
const normalized = normalizeRequiredString(path, "Additional directory cannot be empty", ErrorCodes.REQUEST_INVALID);
|
|
@@ -155572,11 +156155,14 @@ var KimiHarness = class {
|
|
|
155572
156155
|
const id = normalizeSessionId(input.id);
|
|
155573
156156
|
const active = this.activeSessions.get(id);
|
|
155574
156157
|
if (active !== void 0) {
|
|
155575
|
-
await active.reloadSession();
|
|
156158
|
+
await active.reloadSession({ forcePluginSessionStartReminder: input.forcePluginSessionStartReminder });
|
|
155576
156159
|
this.trackSessionEvent(active.id, "session_reload");
|
|
155577
156160
|
return active;
|
|
155578
156161
|
}
|
|
155579
|
-
const summary = await this.rpc.reloadSession({
|
|
156162
|
+
const summary = await this.rpc.reloadSession({
|
|
156163
|
+
sessionId: id,
|
|
156164
|
+
forcePluginSessionStartReminder: input.forcePluginSessionStartReminder
|
|
156165
|
+
});
|
|
155580
156166
|
const session = new Session({
|
|
155581
156167
|
id: summary.id,
|
|
155582
156168
|
workDir: summary.workDir,
|
|
@@ -155849,7 +156435,10 @@ var SDKRpcClientBase = class {
|
|
|
155849
156435
|
return this.resumeSession(input);
|
|
155850
156436
|
}
|
|
155851
156437
|
async reloadSession(input) {
|
|
155852
|
-
return (await this.getRpc()).reloadSession({
|
|
156438
|
+
return (await this.getRpc()).reloadSession({
|
|
156439
|
+
sessionId: input.sessionId,
|
|
156440
|
+
forcePluginSessionStartReminder: input.forcePluginSessionStartReminder
|
|
156441
|
+
});
|
|
155853
156442
|
}
|
|
155854
156443
|
async forkSession(input) {
|
|
155855
156444
|
return (await this.getRpc()).forkSession({
|
|
@@ -155904,6 +156493,23 @@ var SDKRpcClientBase = class {
|
|
|
155904
156493
|
input: input.input
|
|
155905
156494
|
});
|
|
155906
156495
|
}
|
|
156496
|
+
async runShellCommand(input) {
|
|
156497
|
+
const agentId = this.interactiveAgentId;
|
|
156498
|
+
return (await this.getRpc()).runShellCommand({
|
|
156499
|
+
sessionId: input.sessionId,
|
|
156500
|
+
agentId,
|
|
156501
|
+
command: input.command,
|
|
156502
|
+
commandId: input.commandId
|
|
156503
|
+
});
|
|
156504
|
+
}
|
|
156505
|
+
async cancelShellCommand(input) {
|
|
156506
|
+
const agentId = this.interactiveAgentId;
|
|
156507
|
+
return (await this.getRpc()).cancelShellCommand({
|
|
156508
|
+
sessionId: input.sessionId,
|
|
156509
|
+
agentId,
|
|
156510
|
+
commandId: input.commandId
|
|
156511
|
+
});
|
|
156512
|
+
}
|
|
155907
156513
|
async steer(input) {
|
|
155908
156514
|
const agentId = this.interactiveAgentId;
|
|
155909
156515
|
return (await this.getRpc()).steer({
|
|
@@ -155915,6 +156521,9 @@ var SDKRpcClientBase = class {
|
|
|
155915
156521
|
async generateAgentsMd(input) {
|
|
155916
156522
|
return (await this.getRpc()).generateAgentsMd({ sessionId: input.sessionId });
|
|
155917
156523
|
}
|
|
156524
|
+
async getSessionWarnings(input) {
|
|
156525
|
+
return (await this.getRpc()).getSessionWarnings({ sessionId: input.sessionId });
|
|
156526
|
+
}
|
|
155918
156527
|
async addAdditionalDir(input) {
|
|
155919
156528
|
return (await this.getRpc()).addAdditionalDir({
|
|
155920
156529
|
sessionId: input.id,
|