@code-yeongyu/senpi 2026.10.26-2 → 2026.10.27-1
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 +15 -0
- package/dist/bundle/chunks/{chunk-PPECSSWX.js → chunk-6CJC6JY4.js} +1 -1
- package/dist/bundle/chunks/chunk-NXQFBAUI.js +2 -0
- package/dist/bundle/chunks/{chunk-ESNSRUW4.js → chunk-P5E4EVQF.js} +1 -1
- package/dist/bundle/chunks/{chunk-I2WFLKZ2.js → chunk-Q2A2V6NT.js} +1 -1
- package/dist/bundle/chunks/{chunk-JPGT64RV.js → chunk-TV3UI63P.js} +1 -1
- package/dist/bundle/chunks/{chunk-TQ67PB7U.js → chunk-TWHF52G6.js} +3 -3
- package/dist/bundle/chunks/chunk-ZAS4NBVK.js +410 -0
- package/dist/bundle/chunks/{chunk-RPW3XQW3.js → chunk-ZXIAVLJV.js} +75 -75
- package/dist/bundle/chunks/{cli-main-Q2GA53XM.js → cli-main-36FRZ7ZB.js} +1 -1
- package/dist/bundle/chunks/client-CBDPMWZJ.js +2 -0
- package/dist/bundle/chunks/{dispatch-5R3TYAYB.js → dispatch-W5PENPZM.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-5DEKY3YW.js → interactive-mode-5S7NNV2Z.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-OPR5VBFO.js → multi-session-host-E3OPE6HJ.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-W55E3LHP.js → package-manager-cli-3XO456VE.js} +1 -1
- package/dist/bundle/chunks/protocol-GLNWYI5G.js +2 -0
- package/dist/bundle/chunks/{rpc-mode-IDUJTSYS.js → rpc-mode-GLWKLTMA.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-LMZH5JLH.js → session-control-endpoint-KLE2X4RR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +102 -102
- 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/extensions/builtin/anthropic-subscription/account-context.d.ts +15 -0
- package/dist/core/extensions/builtin/anthropic-subscription/account-context.js +53 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-attempt.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-attempt.js +26 -9
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane-boundary.d.ts +16 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane-boundary.js +27 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -13
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -26
- package/dist/core/extensions/builtin/anthropic-subscription/errors.js +3 -2
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +6 -1
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +13 -2
- package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +1 -0
- package/dist/core/extensions/builtin/mcp/modern/request.js +3 -2
- package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/sdk.lazy.js +2 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.js +12 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +2 -1
- package/dist/modes/rpc/compact-commit-dependencies.d.ts +13 -0
- package/dist/modes/rpc/compact-commit-dependencies.js +48 -0
- package/dist/modes/rpc/compact-content-index.d.ts +1 -2
- package/dist/modes/rpc/compact-content-index.js +12 -23
- package/dist/modes/rpc/compact-content-store.d.ts +6 -2
- package/dist/modes/rpc/compact-content-store.js +40 -7
- package/dist/modes/rpc/compact-read-lease.d.ts +15 -0
- package/dist/modes/rpc/compact-read-lease.js +40 -0
- package/dist/modes/rpc/compact-session-state.d.ts +7 -28
- package/dist/modes/rpc/compact-session-state.js +58 -31
- package/dist/modes/rpc/compact-spool-storage.d.ts +5 -0
- package/dist/modes/rpc/compact-spool-storage.js +33 -0
- package/dist/modes/rpc/compact-text-roots.d.ts +22 -0
- package/dist/modes/rpc/compact-text-roots.js +138 -0
- package/dist/modes/rpc/compact-text-view.d.ts +22 -0
- package/dist/modes/rpc/compact-text-view.js +16 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-2XF6W6F6.js +0 -410
- package/dist/bundle/chunks/client-LOS6GSEW.js +0 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { CompactContentIndex } from "./compact-content-index.js";
|
|
2
|
+
import { CompactReadError, CompactReadLease } from "./compact-read-lease.js";
|
|
3
|
+
import { readCompactBlocks } from "./compact-text-view.js";
|
|
2
4
|
import { COMPACT_LEAF_CODE_UNITS, CompactContentStore, } from "./compact-content-store.js";
|
|
3
5
|
import { CompactTransitionError, } from "./compact-transition.js";
|
|
4
6
|
const byContentIndex = ([a], [b]) => a - b;
|
|
@@ -7,14 +9,17 @@ export class CompactSessionState {
|
|
|
7
9
|
this.active = new Map();
|
|
8
10
|
this.sequence = 0;
|
|
9
11
|
this.frozenTailBytes = 0;
|
|
12
|
+
this.cuts = new WeakMap();
|
|
10
13
|
this.store = new CompactContentStore(options);
|
|
11
|
-
const leavesOf = (row) => row.blocks.
|
|
14
|
+
const leavesOf = (row) => row.blocks.map((block) => block.root);
|
|
12
15
|
this.settled = new CompactContentIndex(this.store, leavesOf, options.compactAboveRows ?? 128);
|
|
13
16
|
}
|
|
14
17
|
get acceptedSequence() {
|
|
15
18
|
return this.sequence;
|
|
16
19
|
}
|
|
17
20
|
accept(transition) {
|
|
21
|
+
if (transition.type !== "message_remove")
|
|
22
|
+
this.store.assertReadable();
|
|
18
23
|
switch (transition.type) {
|
|
19
24
|
case "block_start":
|
|
20
25
|
this.start(transition);
|
|
@@ -35,28 +40,56 @@ export class CompactSessionState {
|
|
|
35
40
|
return ++this.sequence;
|
|
36
41
|
}
|
|
37
42
|
freeze() {
|
|
43
|
+
this.store.assertReadable();
|
|
38
44
|
const active = new Map();
|
|
39
45
|
for (const [messageId, blocks] of this.active)
|
|
40
46
|
active.set(messageId, this.seal(blocks, false));
|
|
41
47
|
for (const blocks of active.values())
|
|
42
48
|
this.pin(blocks, 1);
|
|
43
|
-
|
|
49
|
+
const frozen = { sequence: this.sequence, active, settled: this.settled.freeze() };
|
|
50
|
+
this.cuts.set(frozen, new CompactReadLease(() => {
|
|
51
|
+
for (const blocks of frozen.active.values())
|
|
52
|
+
this.pin(blocks, -1);
|
|
53
|
+
this.settled.release(frozen.settled);
|
|
54
|
+
}));
|
|
55
|
+
return frozen;
|
|
44
56
|
}
|
|
45
57
|
release(frozen) {
|
|
46
|
-
|
|
47
|
-
this.pin(blocks, -1);
|
|
48
|
-
this.settled.release(frozen.settled);
|
|
58
|
+
this.cutLease(frozen).release();
|
|
49
59
|
}
|
|
50
60
|
/** Rejects with the storage error when committed content is gone; never returns empty content for it. */
|
|
51
61
|
async read(frozen) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const lease = this.cutLease(frozen);
|
|
63
|
+
const valid = () => {
|
|
64
|
+
lease.assert();
|
|
65
|
+
this.store.assertReadable();
|
|
66
|
+
};
|
|
67
|
+
valid();
|
|
68
|
+
return lease.run(async () => {
|
|
69
|
+
const messages = [];
|
|
70
|
+
for (const [messageId, row] of await this.settled.entries(frozen.settled)) {
|
|
71
|
+
messages.push({
|
|
72
|
+
messageId,
|
|
73
|
+
persistence: row.persistence,
|
|
74
|
+
blocks: await readCompactBlocks(this.store.text, row.blocks, valid),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
for (const [messageId, blocks] of frozen.active) {
|
|
78
|
+
messages.push({
|
|
79
|
+
messageId,
|
|
80
|
+
persistence: undefined,
|
|
81
|
+
blocks: await readCompactBlocks(this.store.text, blocks, valid),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
valid();
|
|
85
|
+
return { sequence: frozen.sequence, messages };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
cutLease(frozen) {
|
|
89
|
+
const lease = this.cuts.get(frozen);
|
|
90
|
+
if (!lease)
|
|
91
|
+
throw new CompactReadError("ownership");
|
|
92
|
+
return lease;
|
|
60
93
|
}
|
|
61
94
|
storageStatus() {
|
|
62
95
|
let residentTailBytes = 0;
|
|
@@ -73,6 +106,9 @@ export class CompactSessionState {
|
|
|
73
106
|
residentTailBytes,
|
|
74
107
|
frozenTailBytes: this.frozenTailBytes,
|
|
75
108
|
hotWitnessBytes,
|
|
109
|
+
residentTextNodes: this.store.text.residentNodes,
|
|
110
|
+
// Fixed accounting charge for node headers, two edges and pending dependency bookkeeping.
|
|
111
|
+
residentTextIndexBytes: this.store.text.residentNodes * 192,
|
|
76
112
|
};
|
|
77
113
|
}
|
|
78
114
|
idle() {
|
|
@@ -90,7 +126,7 @@ export class CompactSessionState {
|
|
|
90
126
|
throw new CompactTransitionError("invalid_coverage", `${covered} of ${t.seed.length} seed code units`);
|
|
91
127
|
}
|
|
92
128
|
const emitted = covered > 0 ? 0 : t.seed.length;
|
|
93
|
-
const block = { kind: t.kind,
|
|
129
|
+
const block = { kind: t.kind, root: 0, tail: "", units: 0, emitted, covered, witness: "" };
|
|
94
130
|
block.witness = t.seed.slice(0, covered);
|
|
95
131
|
this.append(block, t.seed);
|
|
96
132
|
const blocks = this.active.get(t.messageId) ?? new Map();
|
|
@@ -116,8 +152,8 @@ export class CompactSessionState {
|
|
|
116
152
|
block.witness = "";
|
|
117
153
|
}
|
|
118
154
|
replace(block, text) {
|
|
119
|
-
|
|
120
|
-
|
|
155
|
+
this.store.adjust(block.root, -1, 0);
|
|
156
|
+
block.root = 0;
|
|
121
157
|
block.tail = "";
|
|
122
158
|
block.units = 0;
|
|
123
159
|
block.covered = 0;
|
|
@@ -130,8 +166,7 @@ export class CompactSessionState {
|
|
|
130
166
|
if (blocks) {
|
|
131
167
|
this.settled.set(messageId, { persistence, blocks: this.seal(blocks, true) });
|
|
132
168
|
for (const block of blocks.values())
|
|
133
|
-
|
|
134
|
-
this.store.adjust(id, -1, 0);
|
|
169
|
+
this.store.adjust(block.root, -1, 0);
|
|
135
170
|
this.active.delete(messageId);
|
|
136
171
|
}
|
|
137
172
|
else if (this.settled.has(messageId)) {
|
|
@@ -147,8 +182,7 @@ export class CompactSessionState {
|
|
|
147
182
|
if (!blocks && !settled)
|
|
148
183
|
throw new CompactTransitionError("unknown_message", messageId);
|
|
149
184
|
for (const block of blocks?.values() ?? [])
|
|
150
|
-
|
|
151
|
-
this.store.adjust(id, -1, 0);
|
|
185
|
+
this.store.adjust(block.root, -1, 0);
|
|
152
186
|
this.active.delete(messageId);
|
|
153
187
|
if (settled)
|
|
154
188
|
this.settled.delete(messageId);
|
|
@@ -164,7 +198,7 @@ export class CompactSessionState {
|
|
|
164
198
|
let rest = text;
|
|
165
199
|
while (block.tail.length + rest.length >= COMPACT_LEAF_CODE_UNITS) {
|
|
166
200
|
const take = COMPACT_LEAF_CODE_UNITS - block.tail.length;
|
|
167
|
-
block.
|
|
201
|
+
block.root = this.store.text.append(block.root, block.tail + rest.slice(0, take));
|
|
168
202
|
block.tail = "";
|
|
169
203
|
rest = rest.slice(take);
|
|
170
204
|
}
|
|
@@ -174,24 +208,17 @@ export class CompactSessionState {
|
|
|
174
208
|
seal(blocks, settle) {
|
|
175
209
|
return [...blocks].sort(byContentIndex).map(([contentIndex, block]) => {
|
|
176
210
|
if (settle && block.tail.length > 0) {
|
|
177
|
-
block.
|
|
211
|
+
block.root = this.store.text.append(block.root, block.tail);
|
|
178
212
|
block.tail = "";
|
|
179
213
|
}
|
|
180
|
-
return { contentIndex, kind: block.kind,
|
|
214
|
+
return { contentIndex, kind: block.kind, root: block.root, tail: block.tail };
|
|
181
215
|
});
|
|
182
216
|
}
|
|
183
217
|
pin(blocks, delta) {
|
|
184
218
|
for (const block of blocks) {
|
|
185
219
|
this.frozenTailBytes += delta * block.tail.length * 2;
|
|
186
|
-
|
|
187
|
-
this.store.adjust(id, 0, delta);
|
|
220
|
+
this.store.adjust(block.root, 0, delta);
|
|
188
221
|
}
|
|
189
222
|
}
|
|
190
|
-
async texts(blocks) {
|
|
191
|
-
return Promise.all(blocks.map(async ({ contentIndex, kind, leaves, tail }) => {
|
|
192
|
-
const parts = await Promise.all(leaves.map((id) => this.store.readLeaf(id)));
|
|
193
|
-
return { contentIndex, kind, text: parts.join("") + tail };
|
|
194
|
-
}));
|
|
195
|
-
}
|
|
196
223
|
}
|
|
197
224
|
//# sourceMappingURL=compact-session-state.js.map
|
|
@@ -20,6 +20,10 @@ export declare abstract class CompactSpoolStorage {
|
|
|
20
20
|
protected pendingIoCopyBytes: number;
|
|
21
21
|
protected spillBytesWritten: number;
|
|
22
22
|
protected reclaimDebtBytes: number;
|
|
23
|
+
private reads;
|
|
24
|
+
private reading;
|
|
25
|
+
assertReadable(): void;
|
|
26
|
+
beginRead(): () => void;
|
|
23
27
|
constructor(dir: string, io: CompactStorageIo);
|
|
24
28
|
/** Resolves once every queued storage job, including the ones they queued, has completed. */
|
|
25
29
|
idle(): Promise<void>;
|
|
@@ -30,6 +34,7 @@ export declare abstract class CompactSpoolStorage {
|
|
|
30
34
|
retire(): Promise<void>;
|
|
31
35
|
/** Reads a committed file and rejects a missing, unreadable or short one. */
|
|
32
36
|
readFile(path: string, bytes: number, operation: CompactStorageOperation): Promise<Buffer>;
|
|
37
|
+
private readCommittedFile;
|
|
33
38
|
/** Writes one new spool file; undefined means the write failed and was reported (a spill write also halts spilling). */
|
|
34
39
|
writeFile(data: Buffer, operation: CompactStorageOperation, retainedInMemoryBytes: number): Promise<string | undefined>;
|
|
35
40
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { CompactReadError } from "./compact-read-lease.js";
|
|
2
3
|
import { createSpoolDir, removeSpoolDir } from "./compact-spool-dir.js";
|
|
3
4
|
import { CompactStorageReadError, describeCause, } from "./compact-storage-io.js";
|
|
4
5
|
/**
|
|
@@ -10,6 +11,25 @@ export const RECLAIM_WORK_ALLOWANCE_BYTES = 1024 * 1024;
|
|
|
10
11
|
const RECLAIM_RETRY_DELAYS_MS = [100, 400, 1_600, 6_400, 25_600];
|
|
11
12
|
const FAILURE_HISTORY = 32;
|
|
12
13
|
export class CompactSpoolStorage {
|
|
14
|
+
assertReadable() {
|
|
15
|
+
if (this.retired)
|
|
16
|
+
throw new CompactReadError("retired");
|
|
17
|
+
}
|
|
18
|
+
beginRead() {
|
|
19
|
+
this.assertReadable();
|
|
20
|
+
this.reads++;
|
|
21
|
+
this.reading ??= Promise.withResolvers();
|
|
22
|
+
let done = false;
|
|
23
|
+
return () => {
|
|
24
|
+
if (done)
|
|
25
|
+
return;
|
|
26
|
+
done = true;
|
|
27
|
+
if (--this.reads === 0) {
|
|
28
|
+
this.reading?.resolve();
|
|
29
|
+
this.reading = undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
13
33
|
constructor(dir, io) {
|
|
14
34
|
this.failures = [];
|
|
15
35
|
this.nextFile = 1;
|
|
@@ -21,6 +41,7 @@ export class CompactSpoolStorage {
|
|
|
21
41
|
this.pendingIoCopyBytes = 0;
|
|
22
42
|
this.spillBytesWritten = 0;
|
|
23
43
|
this.reclaimDebtBytes = 0;
|
|
44
|
+
this.reads = 0;
|
|
24
45
|
this.dir = dir;
|
|
25
46
|
this.io = io;
|
|
26
47
|
}
|
|
@@ -35,6 +56,7 @@ export class CompactSpoolStorage {
|
|
|
35
56
|
*/
|
|
36
57
|
async retire() {
|
|
37
58
|
this.retired = true;
|
|
59
|
+
await this.reading?.promise;
|
|
38
60
|
await this.idle();
|
|
39
61
|
const token = this.spoolToken;
|
|
40
62
|
this.spoolToken = undefined;
|
|
@@ -43,6 +65,17 @@ export class CompactSpoolStorage {
|
|
|
43
65
|
}
|
|
44
66
|
/** Reads a committed file and rejects a missing, unreadable or short one. */
|
|
45
67
|
async readFile(path, bytes, operation) {
|
|
68
|
+
const done = this.beginRead();
|
|
69
|
+
try {
|
|
70
|
+
const result = await this.readCommittedFile(path, bytes, operation);
|
|
71
|
+
this.assertReadable();
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
done();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async readCommittedFile(path, bytes, operation) {
|
|
46
79
|
let data;
|
|
47
80
|
try {
|
|
48
81
|
data = await this.io.readFile(path);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type CompactContentStore, type LeafId } from "./compact-content-store.ts";
|
|
2
|
+
/** Persistent append tree: positive refs are leaves, negative refs are nodes, zero is empty. */
|
|
3
|
+
export declare class CompactTextRoots {
|
|
4
|
+
private readonly nodes;
|
|
5
|
+
private readonly pending;
|
|
6
|
+
private readonly store;
|
|
7
|
+
private next;
|
|
8
|
+
constructor(store: CompactContentStore);
|
|
9
|
+
get residentNodes(): number;
|
|
10
|
+
units(id: LeafId): number;
|
|
11
|
+
isCommitted(id: LeafId): boolean;
|
|
12
|
+
/** Transfers one live root and one newly owned bounded leaf into the new root. */
|
|
13
|
+
append(root: LeafId, text: string): LeafId;
|
|
14
|
+
adjust(id: LeafId, live: number, pins: number): void;
|
|
15
|
+
/** Bounded UTF-16 result and bounded leaf/tail count, checked before any I/O. */
|
|
16
|
+
read(root: LeafId, tail: string, offset: number, units: number, maxLeaves?: number, valid?: () => void): Promise<string>;
|
|
17
|
+
private node;
|
|
18
|
+
private appendLeaf;
|
|
19
|
+
private height;
|
|
20
|
+
private join;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=compact-text-roots.d.ts.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { CompactCommitDependencies } from "./compact-commit-dependencies.js";
|
|
2
|
+
import { COMPACT_LEAF_CODE_UNITS } from "./compact-content-store.js";
|
|
3
|
+
import { CompactReadError } from "./compact-read-lease.js";
|
|
4
|
+
/** Persistent append tree: positive refs are leaves, negative refs are nodes, zero is empty. */
|
|
5
|
+
export class CompactTextRoots {
|
|
6
|
+
constructor(store) {
|
|
7
|
+
this.nodes = new Map();
|
|
8
|
+
this.next = -1;
|
|
9
|
+
this.store = store;
|
|
10
|
+
this.pending = new CompactCommitDependencies(store, (id) => store.committed(id));
|
|
11
|
+
}
|
|
12
|
+
get residentNodes() {
|
|
13
|
+
return this.nodes.size;
|
|
14
|
+
}
|
|
15
|
+
units(id) {
|
|
16
|
+
return id === 0 ? 0 : id > 0 ? this.store.leafUnits(id) : this.node(id).units;
|
|
17
|
+
}
|
|
18
|
+
isCommitted(id) {
|
|
19
|
+
return this.nodes.has(id) && !this.pending.has(id);
|
|
20
|
+
}
|
|
21
|
+
/** Transfers one live root and one newly owned bounded leaf into the new root. */
|
|
22
|
+
append(root, text) {
|
|
23
|
+
this.units(root);
|
|
24
|
+
const leaf = this.store.createLeaf(text);
|
|
25
|
+
if (root === 0)
|
|
26
|
+
return leaf;
|
|
27
|
+
const result = this.appendLeaf(root, leaf);
|
|
28
|
+
this.store.adjust(root, -1, 0);
|
|
29
|
+
this.store.adjust(leaf, -1, 0);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
adjust(id, live, pins) {
|
|
33
|
+
const node = this.node(id);
|
|
34
|
+
const beforeLive = Number(node.live > 0);
|
|
35
|
+
const beforePin = Number(node.live === 0 && node.pins > 0);
|
|
36
|
+
if (node.live + live < 0 || node.pins + pins < 0)
|
|
37
|
+
throw new CompactReadError("ownership");
|
|
38
|
+
node.live += live;
|
|
39
|
+
node.pins += pins;
|
|
40
|
+
const liveChange = Number(node.live > 0) - beforeLive;
|
|
41
|
+
const pinChange = Number(node.live === 0 && node.pins > 0) - beforePin;
|
|
42
|
+
// Extra cuts on a live subtree do not visit children. Only category transitions propagate.
|
|
43
|
+
if (liveChange || pinChange) {
|
|
44
|
+
this.store.adjust(node.left, liveChange, pinChange);
|
|
45
|
+
this.store.adjust(node.right, liveChange, pinChange);
|
|
46
|
+
}
|
|
47
|
+
if (node.live === 0 && node.pins === 0) {
|
|
48
|
+
this.pending.delete(id);
|
|
49
|
+
this.nodes.delete(id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Bounded UTF-16 result and bounded leaf/tail count, checked before any I/O. */
|
|
53
|
+
async read(root, tail, offset, units, maxLeaves = 2, valid = () => this.store.assertReadable()) {
|
|
54
|
+
this.store.assertReadable();
|
|
55
|
+
valid();
|
|
56
|
+
const length = this.units(root);
|
|
57
|
+
if (!Number.isSafeInteger(offset) ||
|
|
58
|
+
!Number.isSafeInteger(units) ||
|
|
59
|
+
offset < 0 ||
|
|
60
|
+
units < 0 ||
|
|
61
|
+
offset + units > length + tail.length)
|
|
62
|
+
throw new CompactReadError("range");
|
|
63
|
+
if (units > COMPACT_LEAF_CODE_UNITS)
|
|
64
|
+
throw new CompactReadError("size_limit");
|
|
65
|
+
if (!Number.isSafeInteger(maxLeaves) || maxLeaves < 1 || maxLeaves > 2)
|
|
66
|
+
throw new CompactReadError("count_limit");
|
|
67
|
+
const selected = [];
|
|
68
|
+
const visit = (id, start, end) => {
|
|
69
|
+
if (start >= end || id === 0)
|
|
70
|
+
return;
|
|
71
|
+
if (id > 0) {
|
|
72
|
+
if (selected.length === maxLeaves)
|
|
73
|
+
throw new CompactReadError("count_limit");
|
|
74
|
+
selected.push({ id, offset: start, units: end - start });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const node = this.node(id);
|
|
78
|
+
const leftUnits = this.units(node.left);
|
|
79
|
+
visit(node.left, start, Math.min(end, leftUnits));
|
|
80
|
+
visit(node.right, Math.max(0, start - leftUnits), end - leftUnits);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
visit(root, offset, Math.min(length, offset + units));
|
|
84
|
+
const tailUnits = units === 0 ? 0 : Math.max(0, offset + units - Math.max(offset, length));
|
|
85
|
+
if (selected.length + Number(tailUnits > 0) > maxLeaves)
|
|
86
|
+
throw new CompactReadError("count_limit");
|
|
87
|
+
this.store.adjust(root, 0, 1);
|
|
88
|
+
const done = this.store.beginRead();
|
|
89
|
+
try {
|
|
90
|
+
let result = "";
|
|
91
|
+
for (const part of selected) {
|
|
92
|
+
const text = await this.store.readLeaf(part.id);
|
|
93
|
+
valid();
|
|
94
|
+
result += text.slice(part.offset, part.offset + part.units);
|
|
95
|
+
}
|
|
96
|
+
valid();
|
|
97
|
+
return result + tail.slice(Math.max(0, offset - length), Math.max(0, offset - length) + tailUnits);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
this.store.adjust(root, 0, -1);
|
|
101
|
+
done();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
node(id) {
|
|
105
|
+
const node = this.nodes.get(id);
|
|
106
|
+
if (!node)
|
|
107
|
+
throw new CompactReadError("ownership");
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
110
|
+
appendLeaf(root, leaf) {
|
|
111
|
+
const node = this.nodes.get(root);
|
|
112
|
+
if (!node || this.height(node.left) === this.height(node.right))
|
|
113
|
+
return this.join(root, leaf);
|
|
114
|
+
const right = this.appendLeaf(node.right, leaf);
|
|
115
|
+
const result = this.join(node.left, right);
|
|
116
|
+
this.store.adjust(right, -1, 0);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
height(id) {
|
|
120
|
+
return id > 0 ? 0 : this.node(id).height;
|
|
121
|
+
}
|
|
122
|
+
join(left, right) {
|
|
123
|
+
const id = this.next--;
|
|
124
|
+
this.nodes.set(id, {
|
|
125
|
+
left,
|
|
126
|
+
right,
|
|
127
|
+
units: this.units(left) + this.units(right),
|
|
128
|
+
height: 1 + Math.max(this.height(left), this.height(right)),
|
|
129
|
+
live: 1,
|
|
130
|
+
pins: 0,
|
|
131
|
+
});
|
|
132
|
+
this.store.adjust(left, 1, 0);
|
|
133
|
+
this.store.adjust(right, 1, 0);
|
|
134
|
+
this.pending.set(id, [left, right]);
|
|
135
|
+
return id;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=compact-text-roots.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type LeafId } from "./compact-content-store.ts";
|
|
2
|
+
import type { CompactTextRoots } from "./compact-text-roots.ts";
|
|
3
|
+
import type { CompactBlockKind, CompactPersistence } from "./compact-transition.ts";
|
|
4
|
+
export interface FrozenBlock {
|
|
5
|
+
readonly contentIndex: number;
|
|
6
|
+
readonly kind: CompactBlockKind;
|
|
7
|
+
readonly root: LeafId;
|
|
8
|
+
/** A cut shares the immutable tail (under one leaf), not a new leaf for each freeze. */
|
|
9
|
+
readonly tail: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CompactMessageView {
|
|
12
|
+
readonly messageId: string;
|
|
13
|
+
readonly persistence: CompactPersistence | undefined;
|
|
14
|
+
readonly blocks: readonly {
|
|
15
|
+
readonly contentIndex: number;
|
|
16
|
+
readonly kind: CompactBlockKind;
|
|
17
|
+
readonly text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}
|
|
20
|
+
/** Compatibility assembly only; each underlying range is bounded, the resulting legacy view is not. */
|
|
21
|
+
export declare function readCompactBlocks(texts: CompactTextRoots, blocks: readonly FrozenBlock[], valid: () => void): Promise<CompactMessageView["blocks"]>;
|
|
22
|
+
//# sourceMappingURL=compact-text-view.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { COMPACT_LEAF_CODE_UNITS } from "./compact-content-store.js";
|
|
2
|
+
/** Compatibility assembly only; each underlying range is bounded, the resulting legacy view is not. */
|
|
3
|
+
export async function readCompactBlocks(texts, blocks, valid) {
|
|
4
|
+
const result = [];
|
|
5
|
+
for (const { contentIndex, kind, root, tail } of blocks) {
|
|
6
|
+
valid();
|
|
7
|
+
const length = texts.units(root) + tail.length;
|
|
8
|
+
let text = "";
|
|
9
|
+
for (let offset = 0; offset < length; offset += COMPACT_LEAF_CODE_UNITS) {
|
|
10
|
+
text += await texts.read(root, tail, offset, Math.min(COMPACT_LEAF_CODE_UNITS, length - offset), 2, valid);
|
|
11
|
+
}
|
|
12
|
+
result.push({ contentIndex, kind, text });
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=compact-text-view.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.27-1",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@earendil-works/chord": "0.99.1",
|
|
74
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.
|
|
75
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.
|
|
76
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.
|
|
74
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.27-1",
|
|
75
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.27-1",
|
|
76
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.27-1",
|
|
77
77
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
78
78
|
"ajv": "8.20.0",
|
|
79
79
|
"chalk": "6.0.0",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"yaml": "2.9.1",
|
|
94
94
|
"@anthropic-ai/claude-agent-sdk": "0.3.306",
|
|
95
95
|
"@anthropic-ai/sdk": "0.127.0",
|
|
96
|
-
"@code-yeongyu/senpi-codemode": "2026.10.
|
|
97
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.
|
|
96
|
+
"@code-yeongyu/senpi-codemode": "2026.10.27-1",
|
|
97
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.27-1",
|
|
98
98
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
99
99
|
"@mozilla/readability": "0.6.0",
|
|
100
100
|
"linkedom": "0.18.13",
|