blun-king-cli 9.1.228 → 9.1.229
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/bin/write-continuation-policy.cjs +20 -0
- package/blun.mjs +43 -37
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function resolveWriteContinuationContract({ continuation, current, mode }) {
|
|
4
|
+
const expectedPart = current === undefined ? 1 : current.chunks.length + 1;
|
|
5
|
+
const expectedStartLine = (current?.completedLines ?? 0) + 1;
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
expectedPart,
|
|
9
|
+
expectedStartLine,
|
|
10
|
+
part: continuation.part ?? expectedPart,
|
|
11
|
+
startLine: continuation.start_line ?? expectedStartLine,
|
|
12
|
+
mode: mode ?? (expectedPart === 1 ? 'overwrite' : 'append'),
|
|
13
|
+
final: continuation.final === true,
|
|
14
|
+
expectedLines: continuation.expected_lines ?? current?.expectedLines,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
resolveWriteContinuationContract,
|
|
20
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -259581,7 +259581,7 @@ var init_write$1 = __esmMin((() => {
|
|
|
259581
259581
|
}));
|
|
259582
259582
|
//#endregion
|
|
259583
259583
|
write_default = "Create, append to, or completely replace a file. Missing parent directories are created automatically. Overwrite is the default; append adds content at EOF without adding a newline.\n\nUse Write for a new file or a complete replacement. For every incremental change to an existing file, Use Edit instead, even when it is small. Read before overwriting an existing file. Do not create unsolicited documentation, README, summary, or report files unless the user or project instructions require them.\n\nContent is written literally. Never include Read/Edit line prefixes. Supplied LF and CRLF endings are preserved. Source files may contain at most 500 lines; split larger implementations into focused files. Set `single_file_override=true` only when the latest direct user message explicitly requires one source file.\n\nFor a new or completely replaced file too large for one call, use `continuation`. Keep each chunk within the configured UTF-8 bytes limit and end it on a complete line with `\\n`. Set exact `expected_lines` and, when available, `expected_sha256`. Part 1 uses overwrite, `part=1`, and `start_line=1`; every later part uses append, the next consecutive part, and the exact `next_start_line` returned by Write. Set `final=true` only on the complete final part. The Target file remains unchanged until final line-count and optional SHA-256 checks pass. Restart an unfinished sequence only with part 1, overwrite, `start_line=1`, and `reset=true`. Never use continuation for incremental edits.\n\nRunaway generated JavaScript or TypeScript identifiers are rejected before disk I/O; regenerate only the affected section as a smaller chunk.";
|
|
259584
|
-
write_default = "Create, append to, or replace a whole file. Missing parent directories are created automatically. Overwrite is default; append adds content at EOF without a newline.\n\nUse Write only for new files or complete replacements. Use Edit for every incremental change, even a small one. Read before overwriting an existing file. Do not create unsolicited documentation unless the user or project instructions require it.\n\nContent is literal. Never include line prefixes. Supplied LF and CRLF endings are preserved. Source files may contain at most 500 lines; split larger implementations into focused files. Set `single_file_override=true` only when the latest direct user message explicitly requires one source file.\n\nFor a new or completely replaced file too large for one call, use `continuation`. Keep each chunk within the configured UTF-8 bytes limit and end it on a complete line with `\\n`.
|
|
259584
|
+
write_default = "Create, append to, or replace a whole file. Missing parent directories are created automatically. Overwrite is default; append adds content at EOF without a newline.\n\nUse Write only for new files or complete replacements. Use Edit for every incremental change, even a small one. Read before overwriting an existing file. Do not create unsolicited documentation unless the user or project instructions require it.\n\nContent is literal. Never include line prefixes. Supplied LF and CRLF endings are preserved. Source files may contain at most 500 lines; split larger implementations into focused files. Set `single_file_override=true` only when the latest direct user message explicitly requires one source file.\n\nFor a new or completely replaced file too large for one call, use `continuation`. Keep each chunk within the configured UTF-8 bytes limit and end it on a complete line with `\\n`. Optionally set exact `expected_lines` and, when available, `expected_sha256`. Part number, start line, and mode are derived from actually staged content when omitted. Set `final=true` only on the complete final part. The Target file remains unchanged until final line-count and optional SHA-256 checks pass. Restart an unfinished sequence with `reset=true`. Never use continuation for incremental edits.\n\nRunaway generated JavaScript or TypeScript identifiers are rejected before disk I/O; regenerate only the affected section as a smaller chunk.";
|
|
259585
259585
|
//#region ../../packages/agent-core/src/tools/builtin/file/generated-source-health.ts
|
|
259586
259586
|
/**
|
|
259587
259587
|
* Find runaway generated identifiers while ignoring comments and strings.
|
|
@@ -259704,7 +259704,7 @@ function countCompletedLines(content) {
|
|
|
259704
259704
|
for (const character of content) if (character === "\n") count += 1;
|
|
259705
259705
|
return count;
|
|
259706
259706
|
}
|
|
259707
|
-
var S_IFMT, S_IFDIR, DEFAULT_CONTINUATION_CHUNK_BYTES, MIN_CONTINUATION_CHUNK_BYTES, MAX_CONTINUATION_CHUNK_BYTES, CONTINUATION_CHUNK_BYTES_ENV, WriteContinuationSchema, WriteInputSchema, WriteTool;
|
|
259707
|
+
var S_IFMT, S_IFDIR, DEFAULT_CONTINUATION_CHUNK_BYTES, MIN_CONTINUATION_CHUNK_BYTES, MAX_CONTINUATION_CHUNK_BYTES, CONTINUATION_CHUNK_BYTES_ENV, WriteContinuationSchema, WriteInputSchema, WriteTool, resolveWriteContinuationContract;
|
|
259708
259708
|
var init_write = __esmMin((() => {
|
|
259709
259709
|
init_dist$6();
|
|
259710
259710
|
init_zod$1();
|
|
@@ -259716,6 +259716,7 @@ var init_write = __esmMin((() => {
|
|
|
259716
259716
|
init_write$1();
|
|
259717
259717
|
init_generated_source_health();
|
|
259718
259718
|
init_lsp_diagnostics();
|
|
259719
|
+
({ resolveWriteContinuationContract } = createRequire(import.meta.url)("./bin/write-continuation-policy.cjs"));
|
|
259719
259720
|
S_IFMT = 61440;
|
|
259720
259721
|
S_IFDIR = 16384;
|
|
259721
259722
|
DEFAULT_CONTINUATION_CHUNK_BYTES = 2048;
|
|
@@ -259723,17 +259724,17 @@ var init_write = __esmMin((() => {
|
|
|
259723
259724
|
MAX_CONTINUATION_CHUNK_BYTES = 16384;
|
|
259724
259725
|
CONTINUATION_CHUNK_BYTES_ENV = "BLUN_WRITE_CONTINUATION_MAX_BYTES";
|
|
259725
259726
|
WriteContinuationSchema = object({
|
|
259726
|
-
part: number$1().int().positive().describe("
|
|
259727
|
-
start_line: number$1().int().positive().describe("
|
|
259728
|
-
expected_lines: number$1().int().positive().describe("
|
|
259729
|
-
final: boolean$1().describe("Set true only for the last chunk, when the complete file is present."),
|
|
259727
|
+
part: number$1().int().positive().optional().describe("Optional one-based chunk number. When omitted, the runtime derives the next part from actually staged content."),
|
|
259728
|
+
start_line: number$1().int().positive().optional().describe("Optional one-based start line. When omitted, the runtime derives it from actually staged content."),
|
|
259729
|
+
expected_lines: number$1().int().positive().optional().describe("Optional exact completed-line count for final verification. It may first be supplied on the final part."),
|
|
259730
|
+
final: boolean$1().optional().describe("Set true only for the last chunk, when the complete file is present. Omitted means false."),
|
|
259730
259731
|
expected_sha256: string().regex(/^[0-9a-f]{64}$/i).optional().describe("Optional SHA-256 of the complete expected file for byte-for-byte verification."),
|
|
259731
259732
|
reset: boolean$1().optional().describe("Set true only with part 1 and overwrite to discard an unfinished staged write.")
|
|
259732
259733
|
});
|
|
259733
259734
|
WriteInputSchema = object({
|
|
259734
259735
|
path: 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."),
|
|
259735
259736
|
content: string().describe("Raw full file content to write exactly as provided. This does not use the Read/Edit text view."),
|
|
259736
|
-
mode: _enum(["overwrite", "append"]).optional().describe("Write mode.
|
|
259737
|
+
mode: _enum(["overwrite", "append"]).optional().describe("Write mode. Outside continuation it defaults to overwrite; continuation derives overwrite for part 1 and append for later parts when omitted."),
|
|
259737
259738
|
single_file_override: boolean$1().optional().describe("Set true only when the latest direct user message explicitly requires one single source file. The tool verifies that request and reports the override visibly."),
|
|
259738
259739
|
continuation: WriteContinuationSchema.optional().describe("Safe long-write protocol. Split generated files into complete-line chunks within the configured limit (2,048 UTF-8 bytes by default; operator override BLUN_WRITE_CONTINUATION_MAX_BYTES). Part 1 uses overwrite and start_line=1; later parts use append and the exact next_start_line returned by the previous call. The target file is changed only after the final line-count and optional SHA-256 checks pass.")
|
|
259739
259740
|
});
|
|
@@ -259792,89 +259793,94 @@ bytesWritten: number$1().int().nonnegative() });
|
|
|
259792
259793
|
async continueWrite(args, safePath, ctx) {
|
|
259793
259794
|
const continuation = args.continuation;
|
|
259794
259795
|
if (continuation === void 0) throw new Error("continuation is required");
|
|
259795
|
-
const
|
|
259796
|
+
const initialPart = continuation.part ?? (this.pendingContinuations.get(safePath)?.chunks.length ?? 0) + 1;
|
|
259796
259797
|
const chunkBytes = Buffer.byteLength(args.content, "utf8");
|
|
259797
259798
|
if (chunkBytes > this.maxContinuationChunkBytes) return {
|
|
259798
259799
|
isError: true,
|
|
259799
|
-
output: `Refused continuation part ${String(
|
|
259800
|
+
output: `Refused continuation part ${String(initialPart)} for ${args.path}: chunks may contain at most ${this.maxContinuationChunkBytes.toLocaleString("en-US")} UTF-8 bytes (${String(chunkBytes)} received). No content was staged and no file was written.`
|
|
259800
259801
|
};
|
|
259801
259802
|
if (args.content.length === 0 || !args.content.endsWith("\n")) return {
|
|
259802
259803
|
isError: true,
|
|
259803
|
-
output: `Refused continuation part ${String(
|
|
259804
|
+
output: `Refused continuation part ${String(initialPart)} for ${args.path}: every chunk must end on a complete line with a newline. No content was staged and no file was written.`
|
|
259804
259805
|
};
|
|
259805
|
-
if (continuation.reset === true && (continuation.part !== 1 || mode !== "overwrite")) return {
|
|
259806
|
+
if (continuation.reset === true && (continuation.part !== void 0 && continuation.part !== 1 || args.mode !== void 0 && args.mode !== "overwrite")) return {
|
|
259806
259807
|
isError: true,
|
|
259807
259808
|
output: `Refused continuation reset for ${args.path}: reset requires part 1 with mode=overwrite. No content was staged and no file was written.`
|
|
259808
259809
|
};
|
|
259809
259810
|
if (continuation.reset === true) this.pendingContinuations.delete(safePath);
|
|
259810
259811
|
const current = this.pendingContinuations.get(safePath);
|
|
259811
|
-
const
|
|
259812
|
-
|
|
259812
|
+
const contract = resolveWriteContinuationContract({
|
|
259813
|
+
continuation,
|
|
259814
|
+
current,
|
|
259815
|
+
mode: args.mode
|
|
259816
|
+
});
|
|
259817
|
+
const { expectedPart, expectedStartLine, part, startLine, mode, final, expectedLines } = contract;
|
|
259818
|
+
if (part !== expectedPart) return {
|
|
259813
259819
|
isError: true,
|
|
259814
|
-
output: `Refused continuation part ${String(
|
|
259820
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: expected part ${String(expectedPart)}. No content was staged and no file was written.`
|
|
259815
259821
|
};
|
|
259816
|
-
|
|
259817
|
-
if (continuation.start_line !== expectedStartLine) return {
|
|
259822
|
+
if (startLine !== expectedStartLine) return {
|
|
259818
259823
|
isError: true,
|
|
259819
|
-
output: `Refused continuation part ${String(
|
|
259824
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: expected start_line ${String(expectedStartLine)} from the actually staged content, received ${String(startLine)}. No content was staged and no file was written.`
|
|
259820
259825
|
};
|
|
259821
|
-
if (
|
|
259826
|
+
if (part === 1 && mode !== "overwrite" || part > 1 && mode !== "append") return {
|
|
259822
259827
|
isError: true,
|
|
259823
|
-
output: `Refused continuation part ${String(
|
|
259828
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: part 1 must use overwrite and later parts must use append. No content was staged and no file was written.`
|
|
259824
259829
|
};
|
|
259825
|
-
if (current !== void 0 && continuation.expected_lines !== current.expectedLines) return {
|
|
259830
|
+
if (current?.expectedLines !== void 0 && continuation.expected_lines !== void 0 && continuation.expected_lines !== current.expectedLines) return {
|
|
259826
259831
|
isError: true,
|
|
259827
|
-
output: `Refused continuation part ${String(
|
|
259832
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: expected_lines changed from ${String(current.expectedLines)} to ${String(continuation.expected_lines)}. No content was staged and no file was written.`
|
|
259828
259833
|
};
|
|
259829
259834
|
const expectedSha256 = continuation.expected_sha256?.toLowerCase();
|
|
259830
259835
|
if (current?.expectedSha256 !== void 0 && expectedSha256 !== void 0 && expectedSha256 !== current.expectedSha256) return {
|
|
259831
259836
|
isError: true,
|
|
259832
|
-
output: `Refused continuation part ${String(
|
|
259837
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: expected_sha256 changed between parts. No content was staged and no file was written.`
|
|
259833
259838
|
};
|
|
259834
259839
|
const chunkLines = countCompletedLines(args.content);
|
|
259835
259840
|
const completedBefore = current?.completedLines ?? 0;
|
|
259836
259841
|
const completedLines = completedBefore + chunkLines;
|
|
259837
|
-
if (completedLines >
|
|
259842
|
+
if (expectedLines !== void 0 && completedLines > expectedLines) return {
|
|
259838
259843
|
isError: true,
|
|
259839
|
-
output: `Refused continuation part ${String(
|
|
259844
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: it would stage ${String(completedLines)} lines, exceeding the expected ${String(expectedLines)}. No content was staged and no file was written.`
|
|
259840
259845
|
};
|
|
259841
|
-
if (!
|
|
259846
|
+
if (!final && expectedLines !== void 0 && completedLines === expectedLines) return {
|
|
259842
259847
|
isError: true,
|
|
259843
|
-
output: `Refused continuation part ${String(
|
|
259848
|
+
output: `Refused continuation part ${String(part)} for ${args.path}: all ${String(completedLines)} expected lines are present but final is false. Retry this part with final=true. No content was staged and no file was written.`
|
|
259844
259849
|
};
|
|
259845
|
-
if (
|
|
259850
|
+
if (final && expectedLines !== void 0 && completedLines !== expectedLines) return {
|
|
259846
259851
|
isError: true,
|
|
259847
|
-
output: `Refused final continuation part ${String(
|
|
259852
|
+
output: `Refused final continuation part ${String(part)} for ${args.path}: expected ${String(expectedLines)} lines but would have ${String(completedLines)}. No content was staged and no file was written.`
|
|
259848
259853
|
};
|
|
259849
|
-
const
|
|
259850
|
-
const boundary = `${String(
|
|
259854
|
+
const actualStartLine = completedBefore + 1;
|
|
259855
|
+
const boundary = `${String(actualStartLine)}-${String(completedLines)}`;
|
|
259851
259856
|
const chunks = [...current?.chunks ?? [], args.content];
|
|
259852
259857
|
const boundaries = [...current?.boundaries ?? [], boundary];
|
|
259853
259858
|
const totalBytes = (current?.bytes ?? 0) + chunkBytes;
|
|
259854
259859
|
const stableExpectedSha256 = current?.expectedSha256 ?? expectedSha256;
|
|
259855
259860
|
const staged = {
|
|
259856
|
-
expectedLines
|
|
259861
|
+
expectedLines,
|
|
259857
259862
|
expectedSha256: stableExpectedSha256,
|
|
259858
259863
|
chunks,
|
|
259859
259864
|
boundaries,
|
|
259860
259865
|
completedLines,
|
|
259861
259866
|
bytes: totalBytes
|
|
259862
259867
|
};
|
|
259863
|
-
const percent = Math.floor(completedLines /
|
|
259868
|
+
const percent = expectedLines === void 0 ? void 0 : Math.floor(completedLines / expectedLines * 100);
|
|
259864
259869
|
ctx?.onUpdate?.({
|
|
259865
259870
|
kind: "progress",
|
|
259866
|
-
text: `Write continuation ${String(
|
|
259867
|
-
percent
|
|
259871
|
+
text: `Write continuation ${String(part)}: lines ${boundary}`,
|
|
259872
|
+
...percent === void 0 ? {} : { percent }
|
|
259868
259873
|
});
|
|
259869
|
-
if (!
|
|
259874
|
+
if (!final) {
|
|
259870
259875
|
this.pendingContinuations.set(safePath, staged);
|
|
259871
|
-
|
|
259876
|
+
const progress = expectedLines === void 0 ? `${String(completedLines)} lines staged; final count not predeclared` : `${String(completedLines)} of ${String(expectedLines)} lines (${String(percent)}%)`;
|
|
259877
|
+
return { output: `Write continuation part ${String(part)} staged lines ${boundary} (${progress}, ${String(chunkBytes)} bytes). Target unchanged. Continue with the next chunk; mode=append, part=${String(part + 1)}, and start_line=${String(completedLines + 1)} are inferred when omitted.` };
|
|
259872
259878
|
}
|
|
259873
259879
|
const content = chunks.join("");
|
|
259874
259880
|
const actualSha256 = createHash("sha256").update(content).digest("hex");
|
|
259875
259881
|
if (stableExpectedSha256 !== void 0 && actualSha256 !== stableExpectedSha256) return {
|
|
259876
259882
|
isError: true,
|
|
259877
|
-
output: `Refused final continuation part ${String(
|
|
259883
|
+
output: `Refused final continuation part ${String(part)} for ${args.path}: SHA-256 mismatch (expected ${stableExpectedSha256}, got ${actualSha256}). No file was written; the first ${String(current?.completedLines ?? 0)} staged lines remain available for retry.`
|
|
259878
259884
|
};
|
|
259879
259885
|
const result = await this.writeContent({
|
|
259880
259886
|
...args,
|