@code-yeongyu/senpi 2026.11.15-2 → 2026.11.15-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/bundle/chunks/{chunk-Q4RJ4WLE.js → chunk-225TDKTA.js} +4 -4
  3. package/dist/bundle/chunks/{chunk-QSU2456D.js → chunk-3KR3CDGM.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-AOTHPTMU.js → chunk-5GCZFK4X.js} +2 -2
  5. package/dist/bundle/chunks/{chunk-BMADOJHZ.js → chunk-5QIFH624.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-THYIV26N.js → chunk-GQEXLLWJ.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-3RILB2OD.js → chunk-OJOPOUAY.js} +9 -9
  8. package/dist/bundle/chunks/{chunk-64FJ4QQJ.js → chunk-PPCAJQDJ.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-74HCKLRH.js → chunk-TEXE4HT6.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-RRFDLGX6.js → chunk-TNYFCGI7.js} +156 -156
  11. package/dist/bundle/chunks/{cli-main-GNQY2LTU.js → cli-main-CV3ODX4A.js} +1 -1
  12. package/dist/bundle/chunks/{hand-entry-D3DNH6XL.js → hand-entry-2NN7ZTY6.js} +1 -1
  13. package/dist/bundle/chunks/{interactive-mode-ATBPY4M5.js → interactive-mode-TGHZWM2T.js} +1 -1
  14. package/dist/bundle/chunks/{main-OMFVSMUR.js → main-B7WEY4JW.js} +1 -1
  15. package/dist/bundle/chunks/{multi-session-host-ACQ5N4UB.js → multi-session-host-53YLL2OD.js} +1 -1
  16. package/dist/bundle/chunks/{package-manager-cli-2VMRV4HL.js → package-manager-cli-2U54AVKW.js} +1 -1
  17. package/dist/bundle/chunks/{role-BTH5PVME.js → role-4IXZZAL4.js} +1 -1
  18. package/dist/bundle/chunks/{rpc-mode-XUK5SYKH.js → rpc-mode-RRYXENML.js} +1 -1
  19. package/dist/bundle/chunks/{runtime-session-VO3X4OE3.js → runtime-session-KKHEYZOH.js} +1 -1
  20. package/dist/bundle/chunks/{session-control-endpoint-Y5EIFFPY.js → session-control-endpoint-HVULSIUR.js} +1 -1
  21. package/dist/bundle/chunks/session-worker.js +331 -331
  22. package/dist/bundle/cli.js +2 -2
  23. package/dist/bundle/index.js +1 -1
  24. package/dist/bundle/rpc-entry.js +1 -1
  25. package/dist/bundle/runtime-manifest.json +1 -1
  26. package/dist/core/agent-session.d.ts +6 -0
  27. package/dist/core/agent-session.js +34 -3
  28. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -0
  29. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
  30. package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +2 -0
  31. package/dist/core/extensions/builtin/anthropic-subscription/failover.js +22 -4
  32. package/dist/core/extensions/builtin/anthropic-subscription/index.js +7 -2
  33. package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.d.ts +15 -0
  34. package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.js +55 -0
  35. package/dist/core/extensions/builtin/anthropic-subscription/session-stream.d.ts +1 -0
  36. package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +1 -0
  37. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +8 -5
  38. package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts +1 -1
  39. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +3 -1
  40. package/dist/core/extensions/builtin/terminal/extension-state.js +15 -0
  41. package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -1
  42. package/dist/core/extensions/builtin/terminal/notify.js +15 -6
  43. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
  44. package/dist/core/extensions/builtin/terminal/session-bundle.js +41 -1
  45. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +4 -0
  46. package/dist/core/extensions/loader.js +5 -0
  47. package/dist/core/extensions/runner.d.ts +1 -0
  48. package/dist/core/extensions/runner.js +5 -0
  49. package/dist/core/extensions/types.d.ts +7 -0
  50. package/dist/core/run-background-jobs.d.ts +33 -0
  51. package/dist/core/run-background-jobs.js +47 -0
  52. package/dist/core/tools/bash-core.d.ts +50 -0
  53. package/dist/core/tools/bash-core.js +169 -0
  54. package/dist/core/tools/bash-schema.d.ts +48 -0
  55. package/dist/core/tools/bash-schema.js +34 -0
  56. package/dist/core/tools/bash.d.ts +4 -70
  57. package/dist/core/tools/bash.js +32 -409
  58. package/dist/core/tools/coding-tools-core.d.ts +24 -0
  59. package/dist/core/tools/coding-tools-core.js +14 -0
  60. package/dist/core/tools/edit-core.d.ts +56 -0
  61. package/dist/core/tools/edit-core.js +175 -0
  62. package/dist/core/tools/edit-diff.d.ts +0 -5
  63. package/dist/core/tools/edit-diff.js +0 -32
  64. package/dist/core/tools/edit-preview.d.ts +7 -0
  65. package/dist/core/tools/edit-preview.js +34 -0
  66. package/dist/core/tools/edit.d.ts +5 -43
  67. package/dist/core/tools/edit.js +10 -158
  68. package/dist/core/tools/encoding.d.ts +10 -0
  69. package/dist/core/tools/encoding.js +58 -0
  70. package/dist/core/tools/file-tool-host.d.ts +25 -0
  71. package/dist/core/tools/file-tool-host.js +109 -0
  72. package/dist/core/tools/fs-error-code.d.ts +2 -0
  73. package/dist/core/tools/fs-error-code.js +15 -0
  74. package/dist/core/tools/machine-output-spill.d.ts +11 -0
  75. package/dist/core/tools/machine-output-spill.js +65 -0
  76. package/dist/core/tools/machine.d.ts +4 -24
  77. package/dist/core/tools/machine.js +4 -44
  78. package/dist/core/tools/output-accumulator-core.d.ts +84 -0
  79. package/dist/core/tools/output-accumulator-core.js +200 -0
  80. package/dist/core/tools/output-accumulator.d.ts +9 -61
  81. package/dist/core/tools/output-accumulator.js +90 -265
  82. package/dist/core/tools/path-utils.js +3 -48
  83. package/dist/core/tools/posix-signals.d.ts +3 -0
  84. package/dist/core/tools/posix-signals.js +113 -0
  85. package/dist/core/tools/read-core.d.ts +64 -0
  86. package/dist/core/tools/read-core.js +180 -0
  87. package/dist/core/tools/read-text.d.ts +23 -0
  88. package/dist/core/tools/read-text.js +90 -0
  89. package/dist/core/tools/read.d.ts +6 -47
  90. package/dist/core/tools/read.js +13 -218
  91. package/dist/core/tools/renderers/bash.d.ts +0 -1
  92. package/dist/core/tools/renderers/bash.js +0 -1
  93. package/dist/core/tools/renderers/edit.d.ts +1 -1
  94. package/dist/core/tools/renderers/edit.js +1 -1
  95. package/dist/core/tools/secret-output-redactor.js +2 -1
  96. package/dist/core/tools/shell-operations.d.ts +53 -0
  97. package/dist/core/tools/shell-operations.js +115 -0
  98. package/dist/core/tools/shell-output.d.ts +20 -0
  99. package/dist/core/tools/shell-output.js +152 -0
  100. package/dist/core/tools/tail-window.js +5 -7
  101. package/dist/core/tools/tool-machine.d.ts +30 -0
  102. package/dist/core/tools/tool-machine.js +47 -0
  103. package/dist/core/tools/truncate.js +15 -14
  104. package/dist/core/tools/write-core.d.ts +47 -0
  105. package/dist/core/tools/write-core.js +108 -0
  106. package/dist/core/tools/write-result.d.ts +0 -1
  107. package/dist/core/tools/write-result.js +0 -13
  108. package/dist/core/tools/write.d.ts +6 -31
  109. package/dist/core/tools/write.js +25 -77
  110. package/dist/core/turn-origin.d.ts +4 -2
  111. package/dist/core/turn-origin.js +15 -8
  112. package/dist/modes/interactive/session-control-session-commands.js +1 -1
  113. package/dist/modes/rpc/connection-handler.js +2 -2
  114. package/dist/modes/rpc/effect-commands.d.ts +1 -4
  115. package/dist/modes/rpc/method-classes-schema-session.d.ts +1 -0
  116. package/dist/modes/rpc/method-classes-schema-session.js +5 -1
  117. package/dist/modes/rpc/method-classes-schema.d.ts +1 -0
  118. package/dist/modes/rpc/pause-command-validation.js +2 -0
  119. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +2 -4
  120. package/dist/modes/rpc/rpc-session-command-types.d.ts +1 -0
  121. package/dist/modes/rpc/session-control-actions-data.d.ts +7 -3
  122. package/dist/modes/rpc/session-control-actions-data.js +12 -6
  123. package/dist/tools-core.d.ts +14 -0
  124. package/dist/tools-core.js +9 -0
  125. package/dist/utils/image-mime.d.ts +4 -0
  126. package/dist/utils/image-mime.js +99 -0
  127. package/dist/utils/mime.d.ts +1 -1
  128. package/dist/utils/mime.js +2 -97
  129. package/docs/extensions.md +7 -1
  130. package/docs/rpc.md +14 -0
  131. package/package.json +11 -7
  132. package/vendor/senpi-machine-ops/files/local-files.js +34 -5
@@ -1,286 +1,111 @@
1
- // allow: SIZE_OK - one cohesive streaming-output state machine owns decoding, truncation, spill, and terminal cleanup.
2
1
  import { randomBytes } from "node:crypto";
3
2
  import { createWriteStream } from "node:fs";
4
3
  import { open, rm } from "node:fs/promises";
5
4
  import { tmpdir } from "node:os";
6
5
  import { join } from "node:path";
7
- import { TailWindow } from "./tail-window.js";
8
- import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "./truncate.js";
6
+ import { OutputAccumulator as CoreOutputAccumulator, joinHeadAndTail, } from "./output-accumulator-core.js";
9
7
  function defaultTempFilePath(prefix) {
10
8
  const id = randomBytes(8).toString("hex");
11
9
  return join(tmpdir(), `${prefix}-${id}.log`);
12
10
  }
13
- function byteLength(text) {
14
- return Buffer.byteLength(text, "utf-8");
15
- }
16
- /**
17
- * Incrementally tracks streaming output with bounded memory.
18
- *
19
- * Appends decode chunks with a streaming UTF-8 decoder, keeps only a decoded
20
- * tail for display snapshots, and opens a temp file when the full output needs
21
- * to be preserved.
22
- */
23
- export class OutputAccumulator {
24
- constructor(options = {}) {
25
- this.decoder = new TextDecoder();
26
- this.rawChunks = [];
27
- this.totalRawBytes = 0;
28
- this.totalDecodedBytes = 0;
29
- this.completedLines = 0;
30
- this.totalLines = 0;
31
- this.currentLineBytes = 0;
32
- this.hasOpenLine = false;
33
- this.finished = false;
34
- this.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
35
- this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
36
- this.tempFilePrefix = options.tempFilePrefix ?? "pi-output";
37
- this.tail = new TailWindow(Math.max(this.maxBytes * 2, 1));
38
- }
39
- append(data) {
40
- if (this.finished) {
41
- throw new Error("Cannot append to a finished output accumulator");
42
- }
43
- const text = this.decoder.decode(data, { stream: true });
44
- this.totalRawBytes += data.length;
45
- this.appendDecodedText(text, byteLength(text));
46
- if (this.tempFileStream || this.shouldUseTempFile()) {
47
- this.ensureTempFile();
48
- this.tempFileStream?.write(data);
49
- }
50
- else if (data.length > 0) {
51
- this.rawChunks.push(data);
52
- }
53
- }
54
- appendText(text) {
55
- if (this.finished) {
56
- throw new Error("Cannot append to a finished output accumulator");
57
- }
58
- if (text.length === 0) {
59
- return;
60
- }
61
- const bytes = byteLength(text);
62
- this.totalRawBytes += bytes;
63
- this.appendDecodedText(text, bytes);
64
- if (this.tempFileStream || this.shouldUseTempFile()) {
65
- this.ensureTempFile();
66
- this.tempFileStream?.write(text);
67
- }
68
- else {
69
- this.rawChunks.push(text);
70
- }
71
- }
72
- finish() {
73
- if (this.finished) {
74
- return;
75
- }
76
- this.finished = true;
77
- const finalText = this.decoder.decode();
78
- this.appendDecodedText(finalText, byteLength(finalText));
79
- if (this.shouldUseTempFile()) {
80
- this.ensureTempFile();
81
- }
82
- }
83
- snapshot(options = {}) {
84
- const tailTruncation = truncateTail(this.getSnapshotText(), {
85
- maxLines: this.maxLines,
86
- maxBytes: this.maxBytes,
87
- });
88
- const truncated = this.totalLines > this.maxLines || this.totalDecodedBytes > this.maxBytes;
89
- const truncatedBy = truncated
90
- ? (tailTruncation.truncatedBy ?? (this.totalDecodedBytes > this.maxBytes ? "bytes" : "lines"))
91
- : null;
92
- const truncation = {
93
- ...tailTruncation,
94
- truncated,
95
- truncatedBy,
96
- totalLines: this.totalLines,
97
- totalBytes: this.totalDecodedBytes,
98
- maxLines: this.maxLines,
99
- maxBytes: this.maxBytes,
100
- };
101
- if (options.persistIfTruncated && truncation.truncated) {
102
- this.ensureTempFile();
103
- }
104
- return {
105
- content: truncation.content,
106
- truncation,
107
- fullOutputPath: this.tempFilePath,
108
- };
109
- }
110
- async closeTempFile() {
111
- const stream = this.takeTempFileStream();
112
- if (!stream) {
11
+ function destroyStream(stream) {
12
+ return new Promise((resolve) => {
13
+ if (stream.closed) {
14
+ resolve();
113
15
  return;
114
16
  }
115
- if (this.tempFileError) {
116
- await new Promise((resolve) => {
117
- if (stream.closed) {
118
- resolve();
119
- return;
120
- }
121
- stream.once("close", resolve);
122
- stream.destroy();
123
- });
124
- try {
125
- await this.removeTempFile();
17
+ stream.once("close", resolve);
18
+ stream.destroy();
19
+ });
20
+ }
21
+ function endStream(stream) {
22
+ return new Promise((resolve, reject) => {
23
+ let finished = false;
24
+ let closed = false;
25
+ let streamError;
26
+ const settle = () => {
27
+ if (!closed) {
28
+ return;
126
29
  }
127
- catch (unlinkError) {
128
- throw new AggregateError([this.tempFileError, unlinkError], "Output spill cleanup failed");
30
+ stream.off("finish", onFinish);
31
+ stream.off("error", onError);
32
+ stream.off("close", onClose);
33
+ if (streamError) {
34
+ reject(streamError);
129
35
  }
130
- throw this.tempFileError;
131
- }
132
- try {
133
- await new Promise((resolve, reject) => {
134
- let finished = false;
135
- let closed = false;
136
- let streamError;
137
- const settle = () => {
138
- if (!closed) {
139
- return;
140
- }
141
- stream.off("finish", onFinish);
142
- stream.off("error", onError);
143
- stream.off("close", onClose);
144
- if (streamError) {
145
- reject(streamError);
146
- }
147
- else if (finished) {
148
- resolve();
149
- }
150
- else {
151
- reject(new Error("Output spill stream closed before finish"));
152
- }
153
- };
154
- const onError = (error) => {
155
- streamError ??= error;
156
- };
157
- const onFinish = () => {
158
- finished = true;
159
- };
160
- const onClose = () => {
161
- closed = true;
162
- if (!finished && !stream.destroyed) {
163
- stream.destroy();
164
- }
165
- settle();
166
- };
167
- stream.once("error", onError);
168
- stream.once("finish", onFinish);
169
- stream.once("close", onClose);
170
- stream.end();
171
- });
172
- }
173
- catch (error) {
174
- try {
175
- await this.removeTempFile();
36
+ else if (finished) {
37
+ resolve();
176
38
  }
177
- catch (unlinkError) {
178
- throw new AggregateError([error, unlinkError], "Output spill cleanup failed");
39
+ else {
40
+ reject(new Error("Output spill stream closed before finish"));
179
41
  }
180
- throw error;
181
- }
182
- }
183
- /**
184
- * The complete output, for callers that can take more than the display snapshot. Call after
185
- * `finish()` and `closeTempFile()`. Output longer than `maxBytes` raw bytes keeps its first and
186
- * last `maxBytes / 2` bytes around an omission marker.
187
- */
188
- async readFullOutput(maxBytes) {
189
- if (!this.tempFilePath) {
190
- // Fork `appendText()` buffers strings beside raw chunks; encode them back before decoding the whole.
191
- const chunks = this.rawChunks.map((chunk) => typeof chunk === "string" ? Buffer.from(chunk, "utf-8") : chunk);
192
- return { content: new TextDecoder().decode(Buffer.concat(chunks)), truncated: false };
193
- }
194
- const file = await open(this.tempFilePath, "r");
195
- try {
196
- const size = (await file.stat()).size;
197
- if (size <= maxBytes) {
198
- return { content: new TextDecoder().decode(await file.readFile()), truncated: false };
42
+ };
43
+ const onError = (error) => {
44
+ streamError ??= error;
45
+ };
46
+ const onFinish = () => {
47
+ finished = true;
48
+ };
49
+ const onClose = () => {
50
+ closed = true;
51
+ if (!finished && !stream.destroyed) {
52
+ stream.destroy();
199
53
  }
200
- const headBytes = Math.floor(maxBytes / 2);
201
- const tailBytes = maxBytes - headBytes;
202
- const head = Buffer.alloc(headBytes);
203
- const tail = Buffer.alloc(tailBytes);
204
- await file.read(head, 0, headBytes, 0);
205
- await file.read(tail, 0, tailBytes, size - tailBytes);
206
- // Cut at character boundaries: streaming decode holds back an incomplete trailing sequence,
207
- // and the tail skips leading continuation bytes.
208
- const headText = new TextDecoder().decode(head, { stream: true });
209
- let tailStart = 0;
210
- while (tailStart < tail.length && (tail[tailStart] & 0xc0) === 0x80)
211
- tailStart++;
212
- const tailText = new TextDecoder().decode(tail.subarray(tailStart));
213
- const omitted = size - headBytes - tailBytes;
214
- return { content: `${headText}\n\n[... ${omitted} bytes omitted ...]\n\n${tailText}`, truncated: true };
215
- }
216
- finally {
217
- await file.close();
218
- }
219
- }
220
- getLastLineBytes() {
221
- return this.currentLineBytes;
222
- }
223
- appendDecodedText(text, bytes) {
224
- if (text.length === 0) {
225
- return;
226
- }
227
- this.totalDecodedBytes += bytes;
228
- this.tail.append(text, bytes, !this.hasOpenLine);
229
- let newlines = 0;
230
- let lastNewline = -1;
231
- for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1)) {
232
- newlines++;
233
- lastNewline = i;
234
- }
235
- if (newlines === 0) {
236
- this.currentLineBytes += bytes;
237
- this.hasOpenLine = true;
238
- }
239
- else {
240
- this.completedLines += newlines;
241
- const tail = text.slice(lastNewline + 1);
242
- this.currentLineBytes = byteLength(tail);
243
- this.hasOpenLine = tail.length > 0;
244
- }
245
- this.totalLines = this.completedLines + (this.hasOpenLine ? 1 : 0);
246
- }
247
- getSnapshotText() {
248
- const text = this.tail.text();
249
- if (this.tail.startsAtLineBoundary) {
250
- return text;
251
- }
252
- const firstNewline = text.indexOf("\n");
253
- return firstNewline === -1 ? text : text.slice(firstNewline + 1);
254
- }
255
- shouldUseTempFile() {
256
- return (this.totalRawBytes > this.maxBytes || this.totalDecodedBytes > this.maxBytes || this.totalLines > this.maxLines);
257
- }
258
- ensureTempFile() {
259
- if (this.tempFilePath) {
260
- return;
261
- }
262
- this.tempFilePath = defaultTempFilePath(this.tempFilePrefix);
263
- this.tempFileStream = createWriteStream(this.tempFilePath);
264
- this.tempFileStream.on("error", (error) => {
265
- this.tempFileError ??= error;
266
- });
267
- for (const chunk of this.rawChunks) {
268
- this.tempFileStream.write(chunk);
269
- }
270
- this.rawChunks = [];
54
+ settle();
55
+ };
56
+ stream.once("error", onError);
57
+ stream.once("finish", onFinish);
58
+ stream.once("close", onClose);
59
+ stream.end();
60
+ });
61
+ }
62
+ async function readTempFile(path, maxBytes) {
63
+ const file = await open(path, "r");
64
+ try {
65
+ const size = (await file.stat()).size;
66
+ if (size <= maxBytes) {
67
+ return { content: new TextDecoder().decode(await file.readFile()), truncated: false };
68
+ }
69
+ const headBytes = Math.floor(maxBytes / 2);
70
+ const tailBytes = maxBytes - headBytes;
71
+ const head = Buffer.alloc(headBytes);
72
+ const tail = Buffer.alloc(tailBytes);
73
+ await file.read(head, 0, headBytes, 0);
74
+ await file.read(tail, 0, tailBytes, size - tailBytes);
75
+ return { content: joinHeadAndTail(head, tail, size - headBytes - tailBytes), truncated: true };
271
76
  }
272
- async removeTempFile() {
273
- if (!this.tempFilePath) {
274
- return;
275
- }
276
- const path = this.tempFilePath;
277
- await rm(path, { force: true });
278
- this.tempFilePath = undefined;
77
+ finally {
78
+ await file.close();
279
79
  }
280
- takeTempFileStream() {
281
- const stream = this.tempFileStream;
282
- this.tempFileStream = undefined;
283
- return stream;
80
+ }
81
+ /** The local spill: a temp file under the OS temp directory, streamed as output arrives. */
82
+ export const createTempFileSpill = (prefix) => {
83
+ const path = defaultTempFilePath(prefix);
84
+ const stream = createWriteStream(path);
85
+ let writeError;
86
+ stream.on("error", (error) => {
87
+ writeError ??= error;
88
+ });
89
+ return {
90
+ path,
91
+ write: (chunk) => {
92
+ stream.write(chunk);
93
+ },
94
+ close: async () => {
95
+ if (writeError) {
96
+ await destroyStream(stream);
97
+ throw writeError;
98
+ }
99
+ await endStream(stream);
100
+ },
101
+ remove: () => rm(path, { force: true }),
102
+ readFull: (maxBytes) => readTempFile(path, maxBytes),
103
+ };
104
+ };
105
+ /** The output accumulator of the local tools: it spills to a temp file unless the caller supplies a spill. */
106
+ export class OutputAccumulator extends CoreOutputAccumulator {
107
+ constructor(options = {}) {
108
+ super({ ...options, spill: options.spill ?? createTempFileSpill });
284
109
  }
285
110
  }
286
111
  //# sourceMappingURL=output-accumulator.js.map
@@ -1,24 +1,7 @@
1
1
  import { accessSync, constants } from "node:fs";
2
2
  import { access } from "node:fs/promises";
3
3
  import { normalizePath, resolvePath } from "../../utils/paths.js";
4
- const NARROW_NO_BREAK_SPACE = "\u202F";
5
- function tryMacOSScreenshotPath(filePath) {
6
- return filePath.replace(/ (AM|PM)\./gi, `${NARROW_NO_BREAK_SPACE}$1.`);
7
- }
8
- function tryNFDVariant(filePath) {
9
- // macOS stores filenames in NFD (decomposed) form, try converting user input to NFD
10
- return filePath.normalize("NFD");
11
- }
12
- function tryCurlyQuoteVariant(filePath) {
13
- // macOS uses U+2019 (right single quotation mark) in screenshot names like "Capture d'écran"
14
- // Users typically type U+0027 (straight apostrophe)
15
- return filePath.replace(/'/g, "\u2019");
16
- }
17
- // Windows Explorer "Copy as path" wraps the path in double quotes (senpi#2170).
18
- function unquotedPath(filePath) {
19
- const match = /^@?(["'])(.+)\1$/.exec(filePath.trim());
20
- return match?.[2];
21
- }
4
+ import { resolveReadPathVariants, tryCurlyQuoteVariant, tryMacOSScreenshotPath, tryNFDVariant, unquotedPath, } from "./file-tool-host.js";
22
5
  function fileExists(filePath) {
23
6
  try {
24
7
  accessSync(filePath, constants.F_OK);
@@ -78,35 +61,7 @@ export function resolveReadPath(filePath, cwd) {
78
61
  }
79
62
  return resolved;
80
63
  }
81
- export async function resolveReadPathAsync(filePath, cwd) {
82
- const resolved = resolveToCwd(filePath, cwd);
83
- if (await pathExists(resolved)) {
84
- return resolved;
85
- }
86
- const unquoted = unquotedPath(filePath);
87
- if (unquoted !== undefined) {
88
- return resolveReadPathAsync(unquoted, cwd);
89
- }
90
- // Try macOS AM/PM variant (narrow no-break space before AM/PM)
91
- const amPmVariant = tryMacOSScreenshotPath(resolved);
92
- if (amPmVariant !== resolved && (await pathExists(amPmVariant))) {
93
- return amPmVariant;
94
- }
95
- // Try NFD variant (macOS stores filenames in NFD form)
96
- const nfdVariant = tryNFDVariant(resolved);
97
- if (nfdVariant !== resolved && (await pathExists(nfdVariant))) {
98
- return nfdVariant;
99
- }
100
- // Try curly quote variant (macOS uses U+2019 in screenshot names)
101
- const curlyVariant = tryCurlyQuoteVariant(resolved);
102
- if (curlyVariant !== resolved && (await pathExists(curlyVariant))) {
103
- return curlyVariant;
104
- }
105
- // Try combined NFD + curly quote (for French macOS screenshots like "Capture d'écran")
106
- const nfdCurlyVariant = tryCurlyQuoteVariant(nfdVariant);
107
- if (nfdCurlyVariant !== resolved && (await pathExists(nfdCurlyVariant))) {
108
- return nfdCurlyVariant;
109
- }
110
- return resolved;
64
+ export function resolveReadPathAsync(filePath, cwd) {
65
+ return resolveReadPathVariants(filePath, cwd, resolveToCwd, pathExists);
111
66
  }
112
67
  //# sourceMappingURL=path-utils.js.map
@@ -0,0 +1,3 @@
1
+ /** The signal table of a machine's OS; an OS without its own table gets the common POSIX numbers. */
2
+ export declare function posixSignalTable(os: string): Readonly<Record<string, number>>;
3
+ //# sourceMappingURL=posix-signals.d.ts.map
@@ -0,0 +1,113 @@
1
+ // Signal numbers by machine OS, for the 128 + n exit code of a signal-killed shell on a machine this process cannot
2
+ // ask `os.constants.signals` about (test/tools-core/posix-signals.test.ts checks the host's table against Node's).
3
+ // The table every OS got before the tables were split by OS; no OS may number fewer of these.
4
+ const COMMON = {
5
+ SIGHUP: 1,
6
+ SIGINT: 2,
7
+ SIGQUIT: 3,
8
+ SIGILL: 4,
9
+ SIGTRAP: 5,
10
+ SIGABRT: 6,
11
+ SIGFPE: 8,
12
+ SIGKILL: 9,
13
+ SIGSEGV: 11,
14
+ SIGPIPE: 13,
15
+ SIGALRM: 14,
16
+ SIGTERM: 15,
17
+ };
18
+ const LINUX = {
19
+ SIGHUP: 1,
20
+ SIGINT: 2,
21
+ SIGQUIT: 3,
22
+ SIGILL: 4,
23
+ SIGTRAP: 5,
24
+ SIGABRT: 6,
25
+ SIGIOT: 6,
26
+ SIGBUS: 7,
27
+ SIGFPE: 8,
28
+ SIGKILL: 9,
29
+ SIGUSR1: 10,
30
+ SIGSEGV: 11,
31
+ SIGUSR2: 12,
32
+ SIGPIPE: 13,
33
+ SIGALRM: 14,
34
+ SIGTERM: 15,
35
+ SIGSTKFLT: 16,
36
+ SIGCHLD: 17,
37
+ SIGCONT: 18,
38
+ SIGSTOP: 19,
39
+ SIGTSTP: 20,
40
+ SIGTTIN: 21,
41
+ SIGTTOU: 22,
42
+ SIGURG: 23,
43
+ SIGXCPU: 24,
44
+ SIGXFSZ: 25,
45
+ SIGVTALRM: 26,
46
+ SIGPROF: 27,
47
+ SIGWINCH: 28,
48
+ SIGIO: 29,
49
+ SIGPOLL: 29,
50
+ SIGPWR: 30,
51
+ SIGSYS: 31,
52
+ };
53
+ // Darwin and the BSDs share this numbering.
54
+ const BSD = {
55
+ SIGHUP: 1,
56
+ SIGINT: 2,
57
+ SIGQUIT: 3,
58
+ SIGILL: 4,
59
+ SIGTRAP: 5,
60
+ SIGABRT: 6,
61
+ SIGIOT: 6,
62
+ SIGEMT: 7,
63
+ SIGFPE: 8,
64
+ SIGKILL: 9,
65
+ SIGBUS: 10,
66
+ SIGSEGV: 11,
67
+ SIGSYS: 12,
68
+ SIGPIPE: 13,
69
+ SIGALRM: 14,
70
+ SIGTERM: 15,
71
+ SIGURG: 16,
72
+ SIGSTOP: 17,
73
+ SIGTSTP: 18,
74
+ SIGCONT: 19,
75
+ SIGCHLD: 20,
76
+ SIGTTIN: 21,
77
+ SIGTTOU: 22,
78
+ SIGIO: 23,
79
+ SIGXCPU: 24,
80
+ SIGXFSZ: 25,
81
+ SIGVTALRM: 26,
82
+ SIGPROF: 27,
83
+ SIGWINCH: 28,
84
+ SIGINFO: 29,
85
+ SIGUSR1: 30,
86
+ SIGUSR2: 31,
87
+ };
88
+ // Node's win32 numbers (libuv), over COMMON for the names Node does not define there.
89
+ const WIN32 = {
90
+ ...COMMON,
91
+ SIGHUP: 1,
92
+ SIGINT: 2,
93
+ SIGILL: 4,
94
+ SIGABRT: 22,
95
+ SIGFPE: 8,
96
+ SIGKILL: 9,
97
+ SIGSEGV: 11,
98
+ SIGTERM: 15,
99
+ SIGBREAK: 21,
100
+ SIGWINCH: 28,
101
+ };
102
+ const BSD_FAMILY = new Set(["darwin", "freebsd", "openbsd", "netbsd", "dragonfly"]);
103
+ /** The signal table of a machine's OS; an OS without its own table gets the common POSIX numbers. */
104
+ export function posixSignalTable(os) {
105
+ if (os === "linux" || os === "android")
106
+ return LINUX;
107
+ if (BSD_FAMILY.has(os))
108
+ return BSD;
109
+ if (os === "win32")
110
+ return WIN32;
111
+ return COMMON;
112
+ }
113
+ //# sourceMappingURL=posix-signals.js.map
@@ -0,0 +1,64 @@
1
+ import type { AgentTool } from "@earendil-works/pi-agent-core";
2
+ import { type ReadFolder } from "@earendil-works/pi-agent-core/read-folders";
3
+ import type { ModelImageResizeOptions } from "@earendil-works/pi-ai";
4
+ import { type Static, Type } from "typebox";
5
+ import type { ProcessImageOptions, ProcessImageResult } from "../../utils/image-process.ts";
6
+ import type { FilesystemPolicyChecker, ToolDefinition } from "../extensions/types.ts";
7
+ import { type ToolPathCanonicalizer } from "./file-tool-host.ts";
8
+ import type { ToolMachine } from "./tool-machine.ts";
9
+ import { type ReadToolDetails } from "./read-text.ts";
10
+ declare const readSchema: Type.TObject<{
11
+ path: Type.TString;
12
+ offset: Type.TOptional<Type.TNumber>;
13
+ limit: Type.TOptional<Type.TNumber>;
14
+ }>;
15
+ export declare const readToolSystemPromptContribution: {
16
+ readonly snippet: "Read file contents";
17
+ readonly guidelines: readonly ["Files: read, not cat/sed."];
18
+ readonly fullGuidelines: readonly ["Use read to examine files instead of cat or sed."];
19
+ };
20
+ export type ReadToolInput = Static<typeof readSchema>;
21
+ export type { ReadToolDetails } from "./read-text.ts";
22
+ /**
23
+ * Pluggable operations for the read tool.
24
+ * Override these to delegate file reading to remote systems (for example SSH).
25
+ */
26
+ export interface ReadOperations {
27
+ /** Read file contents */
28
+ readFile: (absolutePath: string) => Promise<Uint8Array>;
29
+ /** Check if file is readable (throw if not) */
30
+ access: (absolutePath: string) => Promise<void>;
31
+ /** Detect image MIME type, return null or undefined for non-images */
32
+ detectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;
33
+ }
34
+ /** Turns an image file's bytes into the inline image the model receives, or a note why it cannot. */
35
+ export type ReadImageProcessor = (bytes: Uint8Array, mimeType: string, options: ProcessImageOptions) => Promise<ProcessImageResult>;
36
+ export interface CoreReadToolOptions {
37
+ /** Operation layer and policy gate. */
38
+ machine: ToolMachine;
39
+ /** Structural folder. Default options select the measured folder; omit here for verbatim reads. */
40
+ folder?: ReadFolder;
41
+ /** Whether to auto-resize images. Default: true */
42
+ autoResizeImages?: boolean;
43
+ /** Fallback resize profile when the execution context has no model metadata. */
44
+ resizeOptions?: ModelImageResizeOptions;
45
+ /** Read the model from the execution context. False for a runtime without a session, whose context refuses `model` (senpi#5223). Default: true */
46
+ contextModel?: boolean;
47
+ /** Custom operations for file reading. Default: reads through `machine` */
48
+ operations?: ReadOperations;
49
+ /** Extension-registered filesystem policy checker. */
50
+ filesystemPolicy?: FilesystemPolicyChecker;
51
+ /** Resolves the model's path, trying spelling variants. Default: POSIX resolution, variants probed on `machine`. */
52
+ resolveReadPath?: (path: string, cwd: string) => Promise<string>;
53
+ /** The path `filesystemPolicy` judges. Default: the resolved path as is. */
54
+ canonicalizePath?: ToolPathCanonicalizer;
55
+ /** Image detection for the default operations. Default: the file's leading bytes read on `machine`. */
56
+ detectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;
57
+ /** Default: inline PNG, JPEG, GIF and WebP as they are; other formats are omitted with a note. */
58
+ processImage?: ReadImageProcessor;
59
+ }
60
+ export declare function createCoreReadToolDefinition(cwd: string, options: CoreReadToolOptions): ToolDefinition<typeof readSchema, ReadToolDetails | undefined>;
61
+ /** The read tool for a host whose only effect channel is `machine`; reads fold like the local tool's by default. */
62
+ export declare function createCoreReadTool(cwd: string, options: CoreReadToolOptions): AgentTool<typeof readSchema>;
63
+ export { readSchema };
64
+ //# sourceMappingURL=read-core.d.ts.map