@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.
- package/CHANGELOG.md +19 -0
- package/dist/bundle/chunks/{chunk-Q4RJ4WLE.js → chunk-225TDKTA.js} +4 -4
- package/dist/bundle/chunks/{chunk-QSU2456D.js → chunk-3KR3CDGM.js} +1 -1
- package/dist/bundle/chunks/{chunk-AOTHPTMU.js → chunk-5GCZFK4X.js} +2 -2
- package/dist/bundle/chunks/{chunk-BMADOJHZ.js → chunk-5QIFH624.js} +1 -1
- package/dist/bundle/chunks/{chunk-THYIV26N.js → chunk-GQEXLLWJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-3RILB2OD.js → chunk-OJOPOUAY.js} +9 -9
- package/dist/bundle/chunks/{chunk-64FJ4QQJ.js → chunk-PPCAJQDJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-74HCKLRH.js → chunk-TEXE4HT6.js} +1 -1
- package/dist/bundle/chunks/{chunk-RRFDLGX6.js → chunk-TNYFCGI7.js} +156 -156
- package/dist/bundle/chunks/{cli-main-GNQY2LTU.js → cli-main-CV3ODX4A.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-D3DNH6XL.js → hand-entry-2NN7ZTY6.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-ATBPY4M5.js → interactive-mode-TGHZWM2T.js} +1 -1
- package/dist/bundle/chunks/{main-OMFVSMUR.js → main-B7WEY4JW.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-ACQ5N4UB.js → multi-session-host-53YLL2OD.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-2VMRV4HL.js → package-manager-cli-2U54AVKW.js} +1 -1
- package/dist/bundle/chunks/{role-BTH5PVME.js → role-4IXZZAL4.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-XUK5SYKH.js → rpc-mode-RRYXENML.js} +1 -1
- package/dist/bundle/chunks/{runtime-session-VO3X4OE3.js → runtime-session-KKHEYZOH.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-Y5EIFFPY.js → session-control-endpoint-HVULSIUR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +331 -331
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.js +34 -3
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.js +22 -4
- package/dist/core/extensions/builtin/anthropic-subscription/index.js +7 -2
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.d.ts +15 -0
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.js +55 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +8 -5
- package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +3 -1
- package/dist/core/extensions/builtin/terminal/extension-state.js +15 -0
- package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -1
- package/dist/core/extensions/builtin/terminal/notify.js +15 -6
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +41 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +4 -0
- package/dist/core/extensions/loader.js +5 -0
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.js +5 -0
- package/dist/core/extensions/types.d.ts +7 -0
- package/dist/core/run-background-jobs.d.ts +33 -0
- package/dist/core/run-background-jobs.js +47 -0
- package/dist/core/tools/bash-core.d.ts +50 -0
- package/dist/core/tools/bash-core.js +169 -0
- package/dist/core/tools/bash-schema.d.ts +48 -0
- package/dist/core/tools/bash-schema.js +34 -0
- package/dist/core/tools/bash.d.ts +4 -70
- package/dist/core/tools/bash.js +32 -409
- package/dist/core/tools/coding-tools-core.d.ts +24 -0
- package/dist/core/tools/coding-tools-core.js +14 -0
- package/dist/core/tools/edit-core.d.ts +56 -0
- package/dist/core/tools/edit-core.js +175 -0
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.js +0 -32
- package/dist/core/tools/edit-preview.d.ts +7 -0
- package/dist/core/tools/edit-preview.js +34 -0
- package/dist/core/tools/edit.d.ts +5 -43
- package/dist/core/tools/edit.js +10 -158
- package/dist/core/tools/encoding.d.ts +10 -0
- package/dist/core/tools/encoding.js +58 -0
- package/dist/core/tools/file-tool-host.d.ts +25 -0
- package/dist/core/tools/file-tool-host.js +109 -0
- package/dist/core/tools/fs-error-code.d.ts +2 -0
- package/dist/core/tools/fs-error-code.js +15 -0
- package/dist/core/tools/machine-output-spill.d.ts +11 -0
- package/dist/core/tools/machine-output-spill.js +65 -0
- package/dist/core/tools/machine.d.ts +4 -24
- package/dist/core/tools/machine.js +4 -44
- package/dist/core/tools/output-accumulator-core.d.ts +84 -0
- package/dist/core/tools/output-accumulator-core.js +200 -0
- package/dist/core/tools/output-accumulator.d.ts +9 -61
- package/dist/core/tools/output-accumulator.js +90 -265
- package/dist/core/tools/path-utils.js +3 -48
- package/dist/core/tools/posix-signals.d.ts +3 -0
- package/dist/core/tools/posix-signals.js +113 -0
- package/dist/core/tools/read-core.d.ts +64 -0
- package/dist/core/tools/read-core.js +180 -0
- package/dist/core/tools/read-text.d.ts +23 -0
- package/dist/core/tools/read-text.js +90 -0
- package/dist/core/tools/read.d.ts +6 -47
- package/dist/core/tools/read.js +13 -218
- package/dist/core/tools/renderers/bash.d.ts +0 -1
- package/dist/core/tools/renderers/bash.js +0 -1
- package/dist/core/tools/renderers/edit.d.ts +1 -1
- package/dist/core/tools/renderers/edit.js +1 -1
- package/dist/core/tools/secret-output-redactor.js +2 -1
- package/dist/core/tools/shell-operations.d.ts +53 -0
- package/dist/core/tools/shell-operations.js +115 -0
- package/dist/core/tools/shell-output.d.ts +20 -0
- package/dist/core/tools/shell-output.js +152 -0
- package/dist/core/tools/tail-window.js +5 -7
- package/dist/core/tools/tool-machine.d.ts +30 -0
- package/dist/core/tools/tool-machine.js +47 -0
- package/dist/core/tools/truncate.js +15 -14
- package/dist/core/tools/write-core.d.ts +47 -0
- package/dist/core/tools/write-core.js +108 -0
- package/dist/core/tools/write-result.d.ts +0 -1
- package/dist/core/tools/write-result.js +0 -13
- package/dist/core/tools/write.d.ts +6 -31
- package/dist/core/tools/write.js +25 -77
- package/dist/core/turn-origin.d.ts +4 -2
- package/dist/core/turn-origin.js +15 -8
- package/dist/modes/interactive/session-control-session-commands.js +1 -1
- package/dist/modes/rpc/connection-handler.js +2 -2
- package/dist/modes/rpc/effect-commands.d.ts +1 -4
- package/dist/modes/rpc/method-classes-schema-session.d.ts +1 -0
- package/dist/modes/rpc/method-classes-schema-session.js +5 -1
- package/dist/modes/rpc/method-classes-schema.d.ts +1 -0
- package/dist/modes/rpc/pause-command-validation.js +2 -0
- package/dist/modes/rpc/rpc-mutation-response-types.d.ts +2 -4
- package/dist/modes/rpc/rpc-session-command-types.d.ts +1 -0
- package/dist/modes/rpc/session-control-actions-data.d.ts +7 -3
- package/dist/modes/rpc/session-control-actions-data.js +12 -6
- package/dist/tools-core.d.ts +14 -0
- package/dist/tools-core.js +9 -0
- package/dist/utils/image-mime.d.ts +4 -0
- package/dist/utils/image-mime.js +99 -0
- package/dist/utils/mime.d.ts +1 -1
- package/dist/utils/mime.js +2 -97
- package/docs/extensions.md +7 -1
- package/docs/rpc.md +14 -0
- package/package.json +11 -7
- package/vendor/senpi-machine-ops/files/local-files.js +34 -5
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** Bytes from the start of a file that decide its image type. */
|
|
2
|
+
export const IMAGE_TYPE_SNIFF_BYTES = 4100;
|
|
3
|
+
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
4
|
+
export function detectSupportedImageMimeType(buffer) {
|
|
5
|
+
if (startsWith(buffer, [0xff, 0xd8, 0xff])) {
|
|
6
|
+
return buffer[3] === 0xf7 ? null : "image/jpeg";
|
|
7
|
+
}
|
|
8
|
+
if (startsWith(buffer, PNG_SIGNATURE)) {
|
|
9
|
+
return isPng(buffer) && !isAnimatedPng(buffer) ? "image/png" : null;
|
|
10
|
+
}
|
|
11
|
+
if (startsWithAscii(buffer, 0, "GIF87a") || startsWithAscii(buffer, 0, "GIF89a")) {
|
|
12
|
+
return "image/gif";
|
|
13
|
+
}
|
|
14
|
+
if (startsWithAscii(buffer, 0, "RIFF") && startsWithAscii(buffer, 8, "WEBP")) {
|
|
15
|
+
return "image/webp";
|
|
16
|
+
}
|
|
17
|
+
if (startsWithAscii(buffer, 0, "BM") && isBmp(buffer)) {
|
|
18
|
+
return "image/bmp";
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function isPng(buffer) {
|
|
23
|
+
return (buffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, "IHDR"));
|
|
24
|
+
}
|
|
25
|
+
function isAnimatedPng(buffer) {
|
|
26
|
+
let offset = PNG_SIGNATURE.length;
|
|
27
|
+
while (offset + 8 <= buffer.length) {
|
|
28
|
+
const chunkLength = readUint32BE(buffer, offset);
|
|
29
|
+
const chunkTypeOffset = offset + 4;
|
|
30
|
+
if (startsWithAscii(buffer, chunkTypeOffset, "acTL"))
|
|
31
|
+
return true;
|
|
32
|
+
if (startsWithAscii(buffer, chunkTypeOffset, "IDAT"))
|
|
33
|
+
return false;
|
|
34
|
+
const nextOffset = offset + 8 + chunkLength + 4;
|
|
35
|
+
if (nextOffset <= offset || nextOffset > buffer.length)
|
|
36
|
+
return false;
|
|
37
|
+
offset = nextOffset;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function isBmp(buffer) {
|
|
42
|
+
if (buffer.length < 26)
|
|
43
|
+
return false;
|
|
44
|
+
const declaredFileSize = readUint32LE(buffer, 2);
|
|
45
|
+
const pixelDataOffset = readUint32LE(buffer, 10);
|
|
46
|
+
const dibHeaderSize = readUint32LE(buffer, 14);
|
|
47
|
+
if (declaredFileSize !== 0 && declaredFileSize < 26)
|
|
48
|
+
return false;
|
|
49
|
+
if (pixelDataOffset < 14 + dibHeaderSize)
|
|
50
|
+
return false;
|
|
51
|
+
if (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize)
|
|
52
|
+
return false;
|
|
53
|
+
let colorPlanes;
|
|
54
|
+
let bitsPerPixel;
|
|
55
|
+
if (dibHeaderSize === 12) {
|
|
56
|
+
colorPlanes = readUint16LE(buffer, 22);
|
|
57
|
+
bitsPerPixel = readUint16LE(buffer, 24);
|
|
58
|
+
}
|
|
59
|
+
else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {
|
|
60
|
+
if (buffer.length < 30)
|
|
61
|
+
return false;
|
|
62
|
+
colorPlanes = readUint16LE(buffer, 26);
|
|
63
|
+
bitsPerPixel = readUint16LE(buffer, 28);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);
|
|
69
|
+
}
|
|
70
|
+
function readUint16LE(buffer, offset) {
|
|
71
|
+
return (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);
|
|
72
|
+
}
|
|
73
|
+
function readUint32BE(buffer, offset) {
|
|
74
|
+
return ((buffer[offset] ?? 0) * 0x1000000 +
|
|
75
|
+
((buffer[offset + 1] ?? 0) << 16) +
|
|
76
|
+
((buffer[offset + 2] ?? 0) << 8) +
|
|
77
|
+
(buffer[offset + 3] ?? 0));
|
|
78
|
+
}
|
|
79
|
+
function readUint32LE(buffer, offset) {
|
|
80
|
+
return ((buffer[offset] ?? 0) +
|
|
81
|
+
((buffer[offset + 1] ?? 0) << 8) +
|
|
82
|
+
((buffer[offset + 2] ?? 0) << 16) +
|
|
83
|
+
(buffer[offset + 3] ?? 0) * 0x1000000);
|
|
84
|
+
}
|
|
85
|
+
function startsWith(buffer, bytes) {
|
|
86
|
+
if (buffer.length < bytes.length)
|
|
87
|
+
return false;
|
|
88
|
+
return bytes.every((byte, index) => buffer[index] === byte);
|
|
89
|
+
}
|
|
90
|
+
function startsWithAscii(buffer, offset, text) {
|
|
91
|
+
if (buffer.length < offset + text.length)
|
|
92
|
+
return false;
|
|
93
|
+
for (let index = 0; index < text.length; index++) {
|
|
94
|
+
if (buffer[offset + index] !== text.charCodeAt(index))
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=image-mime.js.map
|
package/dist/utils/mime.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { detectSupportedImageMimeType } from "./image-mime.ts";
|
|
2
2
|
export declare function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null>;
|
|
3
3
|
//# sourceMappingURL=mime.d.ts.map
|
package/dist/utils/mime.js
CHANGED
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
import { open } from "node:fs/promises";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function detectSupportedImageMimeType(buffer) {
|
|
5
|
-
if (startsWith(buffer, [0xff, 0xd8, 0xff])) {
|
|
6
|
-
return buffer[3] === 0xf7 ? null : "image/jpeg";
|
|
7
|
-
}
|
|
8
|
-
if (startsWith(buffer, PNG_SIGNATURE)) {
|
|
9
|
-
return isPng(buffer) && !isAnimatedPng(buffer) ? "image/png" : null;
|
|
10
|
-
}
|
|
11
|
-
if (startsWithAscii(buffer, 0, "GIF87a") || startsWithAscii(buffer, 0, "GIF89a")) {
|
|
12
|
-
return "image/gif";
|
|
13
|
-
}
|
|
14
|
-
if (startsWithAscii(buffer, 0, "RIFF") && startsWithAscii(buffer, 8, "WEBP")) {
|
|
15
|
-
return "image/webp";
|
|
16
|
-
}
|
|
17
|
-
if (startsWithAscii(buffer, 0, "BM") && isBmp(buffer)) {
|
|
18
|
-
return "image/bmp";
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
2
|
+
import { detectSupportedImageMimeType, IMAGE_TYPE_SNIFF_BYTES } from "./image-mime.js";
|
|
3
|
+
export { detectSupportedImageMimeType } from "./image-mime.js";
|
|
22
4
|
export async function detectSupportedImageMimeTypeFromFile(filePath) {
|
|
23
5
|
const fileHandle = await open(filePath, "r");
|
|
24
6
|
try {
|
|
@@ -30,81 +12,4 @@ export async function detectSupportedImageMimeTypeFromFile(filePath) {
|
|
|
30
12
|
await fileHandle.close();
|
|
31
13
|
}
|
|
32
14
|
}
|
|
33
|
-
function isPng(buffer) {
|
|
34
|
-
return (buffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, "IHDR"));
|
|
35
|
-
}
|
|
36
|
-
function isAnimatedPng(buffer) {
|
|
37
|
-
let offset = PNG_SIGNATURE.length;
|
|
38
|
-
while (offset + 8 <= buffer.length) {
|
|
39
|
-
const chunkLength = readUint32BE(buffer, offset);
|
|
40
|
-
const chunkTypeOffset = offset + 4;
|
|
41
|
-
if (startsWithAscii(buffer, chunkTypeOffset, "acTL"))
|
|
42
|
-
return true;
|
|
43
|
-
if (startsWithAscii(buffer, chunkTypeOffset, "IDAT"))
|
|
44
|
-
return false;
|
|
45
|
-
const nextOffset = offset + 8 + chunkLength + 4;
|
|
46
|
-
if (nextOffset <= offset || nextOffset > buffer.length)
|
|
47
|
-
return false;
|
|
48
|
-
offset = nextOffset;
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
function isBmp(buffer) {
|
|
53
|
-
if (buffer.length < 26)
|
|
54
|
-
return false;
|
|
55
|
-
const declaredFileSize = readUint32LE(buffer, 2);
|
|
56
|
-
const pixelDataOffset = readUint32LE(buffer, 10);
|
|
57
|
-
const dibHeaderSize = readUint32LE(buffer, 14);
|
|
58
|
-
if (declaredFileSize !== 0 && declaredFileSize < 26)
|
|
59
|
-
return false;
|
|
60
|
-
if (pixelDataOffset < 14 + dibHeaderSize)
|
|
61
|
-
return false;
|
|
62
|
-
if (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize)
|
|
63
|
-
return false;
|
|
64
|
-
let colorPlanes;
|
|
65
|
-
let bitsPerPixel;
|
|
66
|
-
if (dibHeaderSize === 12) {
|
|
67
|
-
colorPlanes = readUint16LE(buffer, 22);
|
|
68
|
-
bitsPerPixel = readUint16LE(buffer, 24);
|
|
69
|
-
}
|
|
70
|
-
else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {
|
|
71
|
-
if (buffer.length < 30)
|
|
72
|
-
return false;
|
|
73
|
-
colorPlanes = readUint16LE(buffer, 26);
|
|
74
|
-
bitsPerPixel = readUint16LE(buffer, 28);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
return colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);
|
|
80
|
-
}
|
|
81
|
-
function readUint16LE(buffer, offset) {
|
|
82
|
-
return (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);
|
|
83
|
-
}
|
|
84
|
-
function readUint32BE(buffer, offset) {
|
|
85
|
-
return ((buffer[offset] ?? 0) * 0x1000000 +
|
|
86
|
-
((buffer[offset + 1] ?? 0) << 16) +
|
|
87
|
-
((buffer[offset + 2] ?? 0) << 8) +
|
|
88
|
-
(buffer[offset + 3] ?? 0));
|
|
89
|
-
}
|
|
90
|
-
function readUint32LE(buffer, offset) {
|
|
91
|
-
return ((buffer[offset] ?? 0) +
|
|
92
|
-
((buffer[offset + 1] ?? 0) << 8) +
|
|
93
|
-
((buffer[offset + 2] ?? 0) << 16) +
|
|
94
|
-
(buffer[offset + 3] ?? 0) * 0x1000000);
|
|
95
|
-
}
|
|
96
|
-
function startsWith(buffer, bytes) {
|
|
97
|
-
if (buffer.length < bytes.length)
|
|
98
|
-
return false;
|
|
99
|
-
return bytes.every((byte, index) => buffer[index] === byte);
|
|
100
|
-
}
|
|
101
|
-
function startsWithAscii(buffer, offset, text) {
|
|
102
|
-
if (buffer.length < offset + text.length)
|
|
103
|
-
return false;
|
|
104
|
-
for (let index = 0; index < text.length; index++) {
|
|
105
|
-
if (buffer[offset + index] !== text.charCodeAt(index))
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
15
|
//# sourceMappingURL=mime.js.map
|
package/docs/extensions.md
CHANGED
|
@@ -493,6 +493,12 @@ journaled terminal. Later wakes tagged with the run are admitted again, fences f
|
|
|
493
493
|
run that has no fence does nothing. A fence alone does not pause the session, so fencing or releasing only a fence
|
|
494
494
|
emits no `session_paused`/`session_resumed` and no `session_hold_changed`; each pause gets exactly one resume.
|
|
495
495
|
|
|
496
|
+
`ctx.registerRunBackgroundJob({ kind, id, description?, stop })` records a background job under the turn running at
|
|
497
|
+
that moment (outside a turn it records nothing), and returns the release to call once the job ended on its own. An RPC
|
|
498
|
+
`interrupt` with `background: "stop"` calls `stop(turnId)` for each job that turn recorded and names it in its answer;
|
|
499
|
+
`stop` ends the job without waking the session and rejects with the reason it could not. The terminal builtin records
|
|
500
|
+
every background bash session this way.
|
|
501
|
+
|
|
496
502
|
#### held_wakes_released
|
|
497
503
|
|
|
498
504
|
`pi.on("held_wakes_released", (event, ctx) => ...)` receives
|
|
@@ -2573,7 +2579,7 @@ pi.registerShortcut("ctrl+shift+p", {
|
|
|
2573
2579
|
|
|
2574
2580
|
Register the operation layer of the device this session's tools run on (a `ToolMachine`, for example from `createToolMachine({ ops })`). Only tools named in `OMO_TOOLS_VIA_DAEMON` use it, today `bash`, and the bash tool reads it when a command runs. With the flag on and no machine registered, bash fails with `DeviceUnavailableError` and never runs locally.
|
|
2575
2581
|
|
|
2576
|
-
One registration per session: a second one, from any extension, throws `DeviceMachineConflictError` (`code: "device_machine_conflict"`, with `registeredBy` and `attemptedBy`). A registration made by an extension whose factory then throws is discarded with the rest of that load.
|
|
2582
|
+
One registration per session: a second one, from any extension, throws `DeviceMachineConflictError` (`code: "device_machine_conflict"`, with `registeredBy` and `attemptedBy`). A registration made by an extension whose factory then throws is discarded with the rest of that load. If another extension registers a device machine while this load's registration is still staged (for example from an event fired during this factory), the conflict is found when the load commits: the factory cannot catch it, and the extension fails to load as a whole with `DeviceMachineConflictError`.
|
|
2577
2583
|
|
|
2578
2584
|
```typescript
|
|
2579
2585
|
import { createToolMachine } from "@code-yeongyu/senpi";
|
package/docs/rpc.md
CHANGED
|
@@ -4678,6 +4678,20 @@ answers after the turn settled.
|
|
|
4678
4678
|
{"type": "response", "command": "interrupt", "success": true, "data": {"interrupted": true, "turnId": "3"}}
|
|
4679
4679
|
```
|
|
4680
4680
|
|
|
4681
|
+
With `"background": "stop"` the command also stops the background jobs the interrupted turn started: each bash
|
|
4682
|
+
session that turn ran with `run_in_background`, or that auto-detached from the foreground, while that turn was running.
|
|
4683
|
+
Sessions an earlier turn started keep running. The stop happens after the turn settled, and the answer then carries
|
|
4684
|
+
`background: { stopped, failed }`: `stopped` names each job as `{ kind: "bash", id, description? }`, and `failed`
|
|
4685
|
+
names a job it could not stop with its `reason` (a session on the tool child under `--tool-runtime child`, or one
|
|
4686
|
+
still running after TERM then KILL). An idle session, or a `turnId` naming another turn, answers both lists empty.
|
|
4687
|
+
A stopped session's exit notice does not start a turn: the model reads `Background terminal session <id> was stopped
|
|
4688
|
+
when turn <turnId> was interrupted` at its next turn. Without the field nothing but the turn stops, as before.
|
|
4689
|
+
|
|
4690
|
+
```json
|
|
4691
|
+
{"type": "interrupt", "turnId": "3", "background": "stop"}
|
|
4692
|
+
{"type": "response", "command": "interrupt", "success": true, "data": {"interrupted": true, "turnId": "3", "background": {"stopped": [{"kind": "bash", "id": "bash_1", "description": "sleep 60"}], "failed": []}}}
|
|
4693
|
+
```
|
|
4694
|
+
|
|
4681
4695
|
With `"intent": "stop"` (on `abort` or `interrupt`) the command is a user Stop: the session is paused before the
|
|
4682
4696
|
turn is stopped, and automatic wakes that arrive later (terminal and monitor notices, detached eval cells, goal
|
|
4683
4697
|
continuations, task and team deliveries) are kept as notices instead of starting a turn. Only a connection that
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.11.15-
|
|
3
|
+
"version": "2026.11.15-3",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
"./presenter-authority": {
|
|
64
64
|
"types": "./dist/presenter-authority-slot.d.ts",
|
|
65
65
|
"import": "./dist/presenter-authority-slot.js"
|
|
66
|
+
},
|
|
67
|
+
"./tools-core": {
|
|
68
|
+
"types": "./dist/tools-core.d.ts",
|
|
69
|
+
"import": "./dist/tools-core.js"
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"files": [
|
|
@@ -92,10 +96,10 @@
|
|
|
92
96
|
},
|
|
93
97
|
"dependencies": {
|
|
94
98
|
"@earendil-works/chord": "0.99.1",
|
|
95
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.15-
|
|
96
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.15-
|
|
97
|
-
"@earendil-works/pi-realm-registry": "npm:@code-yeongyu/senpi-realm-registry@2026.11.15-
|
|
98
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.15-
|
|
99
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.15-3",
|
|
100
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.15-3",
|
|
101
|
+
"@earendil-works/pi-realm-registry": "npm:@code-yeongyu/senpi-realm-registry@2026.11.15-3",
|
|
102
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.15-3",
|
|
99
103
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
100
104
|
"ajv": "8.20.0",
|
|
101
105
|
"chalk": "6.0.0",
|
|
@@ -116,8 +120,8 @@
|
|
|
116
120
|
"yaml": "2.9.1",
|
|
117
121
|
"@anthropic-ai/claude-agent-sdk": "0.3.321",
|
|
118
122
|
"@anthropic-ai/sdk": "0.127.0",
|
|
119
|
-
"@code-yeongyu/senpi-codemode": "2026.11.15-
|
|
120
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.15-
|
|
123
|
+
"@code-yeongyu/senpi-codemode": "2026.11.15-3",
|
|
124
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.15-3",
|
|
121
125
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
122
126
|
"@mozilla/readability": "0.6.0",
|
|
123
127
|
"linkedom": "0.18.13",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { access, mkdir, open, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname, isAbsolute } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
4
4
|
import { MachineOpError } from "../errors.js";
|
|
5
5
|
import { checkPath, gated, resolveOpPath } from "../policy/gate.js";
|
|
6
6
|
import { localGlob } from "./glob.js";
|
|
@@ -136,12 +136,20 @@ export function createLocalFileOps(options) {
|
|
|
136
136
|
}));
|
|
137
137
|
},
|
|
138
138
|
async glob(req, ctx) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
let cwd = resolveOpPath(req.cwd ?? ".", ctx);
|
|
140
|
+
let pattern = req.pattern;
|
|
141
|
+
if (ctx.scope.root !== undefined && (isAbsolute(pattern) || pattern.split(/[\\/]/).includes(".."))) {
|
|
142
|
+
// An absolute or ".." pattern is judged by where its fixed part resolves, like every other path: the
|
|
143
|
+
// check below allows it inside the scope root and refuses it outside.
|
|
144
|
+
const split = splitGlobBase(pattern, cwd);
|
|
145
|
+
if (split === undefined) {
|
|
146
|
+
throw new MachineOpError("policy_denied", "a glob pattern may not use '..' after a wildcard");
|
|
147
|
+
}
|
|
148
|
+
cwd = split.base;
|
|
149
|
+
pattern = split.rest;
|
|
142
150
|
}
|
|
143
151
|
await checkPath(cwd, "enumerate", "files.glob", ctx);
|
|
144
|
-
return gated("files.glob", req, ctx, () => localGlob(
|
|
152
|
+
return gated("files.glob", req, ctx, () => localGlob(pattern, cwd, req.limit, ctx.signal, options.resolveFd));
|
|
145
153
|
},
|
|
146
154
|
async grep(req, ctx) {
|
|
147
155
|
const path = resolveOpPath(req.path, ctx);
|
|
@@ -168,4 +176,25 @@ export function createLocalFileOps(options) {
|
|
|
168
176
|
},
|
|
169
177
|
};
|
|
170
178
|
}
|
|
179
|
+
const GLOB_SYNTAX = /[*?[\]{}]/;
|
|
180
|
+
/**
|
|
181
|
+
* Split a glob pattern into the directory its fixed leading segments name (resolved against `cwd`) and the rest,
|
|
182
|
+
* which starts at the first segment with glob syntax. A pattern with no glob syntax splits before its last segment; one
|
|
183
|
+
* ending in a separator lists that directory.
|
|
184
|
+
* Undefined when ".." follows a wildcard: where that lands is known only after matching.
|
|
185
|
+
*/
|
|
186
|
+
function splitGlobBase(pattern, cwd) {
|
|
187
|
+
const segments = pattern.split(/[\\/]/);
|
|
188
|
+
const firstGlob = segments.findIndex((segment) => GLOB_SYNTAX.test(segment));
|
|
189
|
+
const cut = firstGlob === -1 ? segments.length - 1 : firstGlob;
|
|
190
|
+
const fixed = segments.slice(0, cut);
|
|
191
|
+
const rest = segments.slice(cut);
|
|
192
|
+
if (rest.includes(".."))
|
|
193
|
+
return undefined;
|
|
194
|
+
// A pattern ending in a separator (`<root>/`, `../dir/`) names a directory: list its entries, as `*` would.
|
|
195
|
+
if (rest.join("/") === "")
|
|
196
|
+
return { base: isAbsolute(pattern) ? resolve(sep, ...fixed) : resolve(cwd, ...fixed), rest: "*" };
|
|
197
|
+
const base = isAbsolute(pattern) ? resolve(sep, ...fixed) : resolve(cwd, ...fixed);
|
|
198
|
+
return { base, rest: rest.join("/") };
|
|
199
|
+
}
|
|
171
200
|
//# sourceMappingURL=local-files.js.map
|