@code-yeongyu/senpi 2026.10.26-1 → 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 +39 -0
- package/dist/bundle/chunks/{anthropic-messages-M235FJQV.js → anthropic-messages-ODSFJCXR.js} +1 -1
- package/dist/bundle/chunks/{args-FBNJOJSR.js → args-SWKJAYQD.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YQRTQ7H6.js → azure-openai-responses-REGXS4XO.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
- package/dist/bundle/chunks/{chunk-DMFAVE5N.js → chunk-4QVORFQ2.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZXHLH4MW.js → chunk-5W3DTM2J.js} +1 -1
- package/dist/bundle/chunks/{chunk-QYFOHFPS.js → chunk-6CJC6JY4.js} +1 -1
- package/dist/bundle/chunks/{chunk-QREKAXKX.js → chunk-6GAMRAVM.js} +5 -5
- package/dist/bundle/chunks/{chunk-MSHRIZGT.js → chunk-7IZH2KQX.js} +3 -3
- package/dist/bundle/chunks/{chunk-PH3JDRB2.js → chunk-ARSENVDY.js} +1 -1
- package/dist/bundle/chunks/{chunk-5KZH6EYQ.js → chunk-CNDPCYNT.js} +1 -1
- package/dist/bundle/chunks/{chunk-DYWIFF2Q.js → chunk-CYOHFREV.js} +1 -1
- package/dist/bundle/chunks/{chunk-3LPWILUZ.js → chunk-DNDZPKIQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZLIVZQDY.js → chunk-EQNUL2WB.js} +1 -1
- package/dist/bundle/chunks/{chunk-LO5MYLGF.js → chunk-FQDARFHT.js} +1 -1
- package/dist/bundle/chunks/{chunk-7KOUQSBX.js → chunk-GPPOPK76.js} +3 -3
- package/dist/bundle/chunks/{chunk-I7QWTWVZ.js → chunk-IHKHCWWG.js} +1 -1
- package/dist/bundle/chunks/{chunk-MY4ZD3EW.js → chunk-IY4G44MN.js} +1 -1
- package/dist/bundle/chunks/{chunk-OQ3WDMB3.js → chunk-JH7MVRNJ.js} +1 -1
- package/dist/bundle/chunks/chunk-KBSFTXAT.js +2 -0
- package/dist/bundle/chunks/{chunk-UW6M3EXY.js → chunk-KRO63T4Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-J5FCH7TI.js → chunk-KTDIIF2F.js} +1 -1
- package/dist/bundle/chunks/{chunk-3WP5RFQR.js → chunk-NDNIMC5W.js} +1 -1
- package/dist/bundle/chunks/chunk-NXQFBAUI.js +2 -0
- package/dist/bundle/chunks/{chunk-CDQPA4FP.js → chunk-ODRGPC5S.js} +1 -1
- package/dist/bundle/chunks/{chunk-5FQMNHXV.js → chunk-P5E4EVQF.js} +1 -1
- package/dist/bundle/chunks/{chunk-3A3XCLNH.js → chunk-PDG4XWO2.js} +1 -1
- package/dist/bundle/chunks/{chunk-5LWUUXMH.js → chunk-PY4ZQHTN.js} +1 -1
- package/dist/bundle/chunks/chunk-Q2A2V6NT.js +5 -0
- package/dist/bundle/chunks/{chunk-SOXQ42VS.js → chunk-RQ5JTXUC.js} +1 -1
- package/dist/bundle/chunks/{chunk-JPGT64RV.js → chunk-TV3UI63P.js} +1 -1
- package/dist/bundle/chunks/{chunk-JFQUWEN7.js → chunk-TWHF52G6.js} +4 -4
- package/dist/bundle/chunks/{chunk-6U3TBFBA.js → chunk-UJPG4ROT.js} +1 -1
- package/dist/bundle/chunks/{chunk-UDM7VO6O.js → chunk-XM36IFP3.js} +1 -1
- package/dist/bundle/chunks/{chunk-XQ45JMGN.js → chunk-Z6BREXV3.js} +1 -1
- package/dist/bundle/chunks/chunk-ZAS4NBVK.js +410 -0
- package/dist/bundle/chunks/{chunk-ADQAPDPV.js → chunk-ZP6JJDYV.js} +1 -1
- package/dist/bundle/chunks/{chunk-EZP6AYW2.js → chunk-ZXIAVLJV.js} +88 -88
- package/dist/bundle/chunks/{cli-main-55EQA5PS.js → cli-main-36FRZ7ZB.js} +1 -1
- package/dist/bundle/chunks/client-CBDPMWZJ.js +2 -0
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-ZJQGIIRC.js → cloudflare-workers-ai-system-one-WDHT7BBV.js} +1 -1
- package/dist/bundle/chunks/cursor-agent.js +1 -1
- package/dist/bundle/chunks/devin-agent.js +1 -1
- package/dist/bundle/chunks/{dispatch-C2IBP5VV.js → dispatch-W5PENPZM.js} +3 -3
- package/dist/bundle/chunks/{google-generative-ai-5G4RIQA6.js → google-generative-ai-FHYFXKLA.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-ALD4P27O.js → google-vertex-5DWE5U5K.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-PEIKGCKZ.js → help-fast-path-FETLIF3W.js} +1 -1
- package/dist/bundle/chunks/{host-command-657VCQ34.js → host-command-XEB7SHT3.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-OV5XDOJK.js → host-session-command-FFN52LXJ.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-MLXSYK2H.js → interactive-mode-5S7NNV2Z.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-RAE4K2XY.js → mistral-conversations-I2PI7DDF.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-774MACJU.js → multi-session-host-E3OPE6HJ.js} +3 -3
- package/dist/bundle/chunks/{openai-codex-responses-3CW4R7TG.js → openai-codex-responses-WSEX6SH7.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-5UIJXQUF.js → openai-completions-YCBHTBSS.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-2BO44MRW.js → openai-responses-GXNSQF4A.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-MZHVCX2S.js → package-manager-cli-3XO456VE.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-RF6GER4R.js → pi-messages-O6GISIRW.js} +1 -1
- package/dist/bundle/chunks/protocol-GLNWYI5G.js +2 -0
- package/dist/bundle/chunks/{role-ETSFSDTS.js → role-SHIZQKK4.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-ZMQB4GPO.js → rotation-stream-2PCZWNKF.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-UJXOIZCW.js → rpc-mode-GLWKLTMA.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-KA7LFFNK.js → schedule-command-B7RRSANU.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-MFWXUR7Z.js → session-control-endpoint-KLE2X4RR.js} +1 -1
- package/dist/bundle/chunks/{session-picker-BP33NRSL.js → session-picker-DTGBOXDR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +131 -131
- package/dist/bundle/chunks/{typesafe-system-one-GNNZ5O5U.js → typesafe-system-one-XGYJJVSE.js} +1 -1
- 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 +11 -1
- package/dist/core/agent-session.js +19 -2
- 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/compaction/index.js +7 -1
- package/dist/core/extensions/builtin/herdr/index.js +9 -5
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +6 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +25 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +3 -3
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +50 -19
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +4 -3
- package/dist/core/extensions/builtin/mcp/auth/oauth.js +19 -12
- package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +1 -0
- package/dist/core/extensions/builtin/mcp/modern/dispatch.js +1 -0
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +4 -1
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +33 -35
- package/dist/core/extensions/builtin/mcp/modern/request.js +6 -2
- package/dist/core/extensions/builtin/mcp/modern/transport.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/modern/transport.js +4 -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/core/extensions/builtin/terminal/extension-state.js +2 -1
- package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +9 -0
- package/dist/core/extensions/builtin/vertex-model-garden/auth.js +17 -1
- package/dist/core/extensions/builtin/websearch/websearch/providers/google.d.ts +6 -0
- package/dist/core/extensions/builtin/websearch/websearch/providers/google.js +6 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/lifecycle-children.d.ts +3 -1
- package/dist/core/lifecycle-children.js +79 -15
- package/dist/core/lifecycle-record.js +1 -24
- package/dist/core/lifecycle-session-binding.js +2 -1
- package/dist/core/lifecycle-wait-items.d.ts +17 -0
- package/dist/core/lifecycle-wait-items.js +72 -0
- package/dist/core/model-runtime.js +6 -1
- package/dist/core/provider-api-key-auth.d.ts +6 -1
- package/dist/core/provider-api-key-auth.js +11 -4
- package/dist/core/provider-composer.d.ts +8 -1
- package/dist/core/provider-composer.js +7 -3
- package/dist/core/sdk.js +4 -1
- package/dist/core/session-activity.js +2 -0
- package/dist/core/session-lifecycle-types.d.ts +20 -4
- package/dist/core/session-lifecycle.js +35 -4
- package/dist/core/structured-session-lane.d.ts +33 -0
- package/dist/core/structured-session-lane.js +62 -0
- package/dist/core/work-custody-record.d.ts +2 -1
- package/dist/core/work-custody-record.js +2 -0
- package/dist/core/work-obligations.d.ts +1 -1
- package/dist/core/work-obligations.js +8 -2
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +2 -1
- package/dist/modes/json-event.d.ts +79 -1
- package/dist/modes/json-event.js +79 -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 +84 -0
- package/dist/modes/rpc/compact-content-index.js +243 -0
- package/dist/modes/rpc/compact-content-store.d.ts +10 -43
- package/dist/modes/rpc/compact-content-store.js +61 -163
- package/dist/modes/rpc/compact-index-pages.d.ts +12 -0
- package/dist/modes/rpc/compact-index-pages.js +31 -0
- 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 +14 -63
- package/dist/modes/rpc/compact-session-state.js +69 -51
- package/dist/modes/rpc/compact-spool-dir.d.ts +4 -1
- package/dist/modes/rpc/compact-spool-dir.js +41 -8
- package/dist/modes/rpc/compact-spool-storage.d.ts +58 -0
- package/dist/modes/rpc/compact-spool-storage.js +195 -0
- package/dist/modes/rpc/compact-storage-io.d.ts +2 -1
- package/dist/modes/rpc/compact-storage-io.js +7 -1
- 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/dist/modes/rpc/compact-transition.d.ts +41 -0
- package/dist/modes/rpc/compact-transition.js +12 -0
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +12 -6
- package/dist/modes/rpc/host-ensure-start.js +5 -3
- package/dist/modes/rpc/security-presentations.js +2 -1
- package/dist/modes/rpc/session-binding.js +10 -0
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.js +2 -0
- package/dist/modes/rpc/session-record-stream.d.ts +3 -3
- package/dist/modes/rpc/session-record-stream.js +3 -3
- package/dist/modes/rpc/session-structured-lanes.d.ts +12 -0
- package/dist/modes/rpc/session-structured-lanes.js +24 -0
- package/dist/modes/rpc/session-worker-client.js +6 -5
- package/dist/modes/rpc/structured-session-records.d.ts +12 -0
- package/dist/modes/rpc/structured-session-records.js +88 -0
- package/docs/extensions.md +7 -1
- package/docs/rpc.md +17 -2
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-GQJYNFMO.js +0 -2
- package/dist/bundle/chunks/chunk-VICDRG3J.js +0 -410
- package/dist/bundle/chunks/chunk-VL7O5PO4.js +0 -5
- package/dist/bundle/chunks/client-LOS6GSEW.js +0 -2
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class CompactReadError extends Error {
|
|
2
|
+
constructor(reason) {
|
|
3
|
+
super(`compact read refused (${reason})`);
|
|
4
|
+
this.name = "CompactReadError";
|
|
5
|
+
this.reason = reason;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Invalidates immediately, but holds the owner's references until its last operation unwinds. */
|
|
9
|
+
export class CompactReadLease {
|
|
10
|
+
constructor(dispose) {
|
|
11
|
+
this.released = false;
|
|
12
|
+
this.operations = 0;
|
|
13
|
+
this.dispose = dispose;
|
|
14
|
+
}
|
|
15
|
+
assert() {
|
|
16
|
+
if (this.released)
|
|
17
|
+
throw new CompactReadError("released");
|
|
18
|
+
}
|
|
19
|
+
async run(read) {
|
|
20
|
+
this.assert();
|
|
21
|
+
this.operations++;
|
|
22
|
+
try {
|
|
23
|
+
const result = await read();
|
|
24
|
+
this.assert();
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
if (--this.operations === 0 && this.released)
|
|
29
|
+
this.dispose();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
release() {
|
|
33
|
+
if (this.released)
|
|
34
|
+
return;
|
|
35
|
+
this.released = true;
|
|
36
|
+
if (this.operations === 0)
|
|
37
|
+
this.dispose();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=compact-read-lease.js.map
|
|
@@ -1,52 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* `accept` is synchronous: it validates a typed transition completely, then publishes the next
|
|
6
|
-
* sequence, or throws and leaves the prior root untouched. Block text lives in owned bounded leaves
|
|
7
|
-
* that spill asynchronously; settled message rows live in a resident map whose persistence status
|
|
8
|
-
* changes only through `message_status`, never through storage success or failure. The map is
|
|
9
|
-
* exact for every transition: a status change replaces the whole row, a removal deletes it, and a
|
|
10
|
-
* transition addressing a removed or unknown message is refused rather than masked. `freeze` pins
|
|
11
|
-
* an immutable cut synchronously, and `read` materializes it asynchronously from RAM or disk.
|
|
12
|
-
*/
|
|
13
|
-
type CompactBlockKind = "text" | "thinking" | "tool_arguments";
|
|
14
|
-
type CompactPersistence = "pending" | "appended" | "failed";
|
|
15
|
-
interface BlockAddress {
|
|
16
|
-
readonly messageId: string;
|
|
17
|
-
readonly contentIndex: number;
|
|
18
|
-
}
|
|
19
|
-
export type CompactTransition = (BlockAddress & {
|
|
20
|
-
readonly type: "block_start";
|
|
21
|
-
readonly kind: CompactBlockKind;
|
|
22
|
-
readonly seed: string;
|
|
23
|
-
/**
|
|
24
|
-
* The whole seed when later deltas replay it from offset 0, or 0 when they continue after it. Partial
|
|
25
|
-
* coverage is refused: the uncovered seed tail would make every later delta overlap or leave a gap.
|
|
26
|
-
*/
|
|
27
|
-
readonly coveredCodeUnits: number;
|
|
28
|
-
}) | (BlockAddress & {
|
|
29
|
-
readonly type: "block_delta";
|
|
30
|
-
readonly offset: number;
|
|
31
|
-
readonly text: string;
|
|
32
|
-
}) | (BlockAddress & {
|
|
33
|
-
readonly type: "block_replace";
|
|
34
|
-
readonly text: string;
|
|
35
|
-
}) | {
|
|
36
|
-
readonly type: "message_status";
|
|
37
|
-
readonly messageId: string;
|
|
38
|
-
readonly persistence: CompactPersistence;
|
|
39
|
-
} | {
|
|
40
|
-
readonly type: "message_remove";
|
|
41
|
-
readonly messageId: string;
|
|
42
|
-
};
|
|
43
|
-
interface FrozenBlock {
|
|
44
|
-
readonly contentIndex: number;
|
|
45
|
-
readonly kind: CompactBlockKind;
|
|
46
|
-
readonly leaves: readonly LeafId[];
|
|
47
|
-
/** A cut shares the immutable active tail string (under one leaf) instead of minting a leaf per freeze. */
|
|
48
|
-
readonly tail: string;
|
|
49
|
-
}
|
|
1
|
+
import { CompactContentIndex, type FrozenIndex } from "./compact-content-index.ts";
|
|
2
|
+
import { type CompactMessageView, type FrozenBlock } from "./compact-text-view.ts";
|
|
3
|
+
import { type CompactContentStoreOptions, type CompactStorageStatus } from "./compact-content-store.ts";
|
|
4
|
+
import { type CompactPersistence, type CompactTransition } from "./compact-transition.ts";
|
|
50
5
|
interface SettledRow {
|
|
51
6
|
readonly persistence: CompactPersistence;
|
|
52
7
|
readonly blocks: readonly FrozenBlock[];
|
|
@@ -54,16 +9,11 @@ interface SettledRow {
|
|
|
54
9
|
interface FrozenCompactState {
|
|
55
10
|
readonly sequence: number;
|
|
56
11
|
readonly active: ReadonlyMap<string, readonly FrozenBlock[]>;
|
|
57
|
-
readonly settled:
|
|
12
|
+
readonly settled: FrozenIndex<SettledRow>;
|
|
58
13
|
}
|
|
59
|
-
interface
|
|
60
|
-
|
|
61
|
-
readonly
|
|
62
|
-
readonly blocks: readonly {
|
|
63
|
-
readonly contentIndex: number;
|
|
64
|
-
readonly kind: CompactBlockKind;
|
|
65
|
-
readonly text: string;
|
|
66
|
-
}[];
|
|
14
|
+
export interface CompactSessionStateOptions extends CompactContentStoreOptions {
|
|
15
|
+
/** Index compaction is considered once this many override slots exist (one page by default). */
|
|
16
|
+
readonly compactAboveRows?: number;
|
|
67
17
|
}
|
|
68
18
|
export declare class CompactSessionState {
|
|
69
19
|
private readonly store;
|
|
@@ -71,7 +21,8 @@ export declare class CompactSessionState {
|
|
|
71
21
|
private readonly active;
|
|
72
22
|
private sequence;
|
|
73
23
|
private frozenTailBytes;
|
|
74
|
-
|
|
24
|
+
private readonly cuts;
|
|
25
|
+
constructor(options: CompactSessionStateOptions);
|
|
75
26
|
get acceptedSequence(): number;
|
|
76
27
|
accept(transition: CompactTransition): number;
|
|
77
28
|
freeze(): FrozenCompactState;
|
|
@@ -81,13 +32,14 @@ export declare class CompactSessionState {
|
|
|
81
32
|
sequence: number;
|
|
82
33
|
messages: CompactMessageView[];
|
|
83
34
|
}>;
|
|
84
|
-
|
|
35
|
+
private cutLease;
|
|
36
|
+
storageStatus(): CompactStorageStatus & ReturnType<CompactContentIndex<SettledRow>["status"]> & {
|
|
85
37
|
readonly residentTailBytes: number;
|
|
86
38
|
/** Active tails held by unreleased cuts; may share storage with `residentTailBytes`. */
|
|
87
39
|
readonly frozenTailBytes: number;
|
|
88
40
|
readonly hotWitnessBytes: number;
|
|
89
|
-
|
|
90
|
-
readonly
|
|
41
|
+
readonly residentTextNodes: number;
|
|
42
|
+
readonly residentTextIndexBytes: number;
|
|
91
43
|
};
|
|
92
44
|
idle(): Promise<void>;
|
|
93
45
|
retire(): Promise<void>;
|
|
@@ -105,7 +57,6 @@ export declare class CompactSessionState {
|
|
|
105
57
|
/** Settling mints the final tail into a spillable leaf; a freeze of an active block shares the tail. */
|
|
106
58
|
private seal;
|
|
107
59
|
private pin;
|
|
108
|
-
private texts;
|
|
109
60
|
}
|
|
110
61
|
export {};
|
|
111
62
|
//# sourceMappingURL=compact-session-state.d.ts.map
|
|
@@ -1,24 +1,25 @@
|
|
|
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";
|
|
1
4
|
import { COMPACT_LEAF_CODE_UNITS, CompactContentStore, } from "./compact-content-store.js";
|
|
2
|
-
|
|
3
|
-
constructor(reason, detail) {
|
|
4
|
-
super(`compact transition rejected (${reason}): ${detail}`);
|
|
5
|
-
this.name = "CompactTransitionError";
|
|
6
|
-
this.reason = reason;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
5
|
+
import { CompactTransitionError, } from "./compact-transition.js";
|
|
9
6
|
const byContentIndex = ([a], [b]) => a - b;
|
|
10
7
|
export class CompactSessionState {
|
|
11
8
|
constructor(options) {
|
|
12
|
-
this.settled = new Map();
|
|
13
9
|
this.active = new Map();
|
|
14
10
|
this.sequence = 0;
|
|
15
11
|
this.frozenTailBytes = 0;
|
|
12
|
+
this.cuts = new WeakMap();
|
|
16
13
|
this.store = new CompactContentStore(options);
|
|
14
|
+
const leavesOf = (row) => row.blocks.map((block) => block.root);
|
|
15
|
+
this.settled = new CompactContentIndex(this.store, leavesOf, options.compactAboveRows ?? 128);
|
|
17
16
|
}
|
|
18
17
|
get acceptedSequence() {
|
|
19
18
|
return this.sequence;
|
|
20
19
|
}
|
|
21
20
|
accept(transition) {
|
|
21
|
+
if (transition.type !== "message_remove")
|
|
22
|
+
this.store.assertReadable();
|
|
22
23
|
switch (transition.type) {
|
|
23
24
|
case "block_start":
|
|
24
25
|
this.start(transition);
|
|
@@ -39,32 +40,56 @@ export class CompactSessionState {
|
|
|
39
40
|
return ++this.sequence;
|
|
40
41
|
}
|
|
41
42
|
freeze() {
|
|
43
|
+
this.store.assertReadable();
|
|
42
44
|
const active = new Map();
|
|
43
45
|
for (const [messageId, blocks] of this.active)
|
|
44
46
|
active.set(messageId, this.seal(blocks, false));
|
|
45
|
-
const settled = new Map(this.settled);
|
|
46
47
|
for (const blocks of active.values())
|
|
47
48
|
this.pin(blocks, 1);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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;
|
|
51
56
|
}
|
|
52
57
|
release(frozen) {
|
|
53
|
-
|
|
54
|
-
this.pin(blocks, -1);
|
|
55
|
-
for (const row of frozen.settled.values())
|
|
56
|
-
this.pin(row.blocks, -1);
|
|
58
|
+
this.cutLease(frozen).release();
|
|
57
59
|
}
|
|
58
60
|
/** Rejects with the storage error when committed content is gone; never returns empty content for it. */
|
|
59
61
|
async read(frozen) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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;
|
|
68
93
|
}
|
|
69
94
|
storageStatus() {
|
|
70
95
|
let residentTailBytes = 0;
|
|
@@ -77,10 +102,13 @@ export class CompactSessionState {
|
|
|
77
102
|
}
|
|
78
103
|
return {
|
|
79
104
|
...this.store.status(),
|
|
105
|
+
...this.settled.status(),
|
|
80
106
|
residentTailBytes,
|
|
81
107
|
frozenTailBytes: this.frozenTailBytes,
|
|
82
108
|
hotWitnessBytes,
|
|
83
|
-
|
|
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,
|
|
84
112
|
};
|
|
85
113
|
}
|
|
86
114
|
idle() {
|
|
@@ -98,7 +126,7 @@ export class CompactSessionState {
|
|
|
98
126
|
throw new CompactTransitionError("invalid_coverage", `${covered} of ${t.seed.length} seed code units`);
|
|
99
127
|
}
|
|
100
128
|
const emitted = covered > 0 ? 0 : t.seed.length;
|
|
101
|
-
const block = { kind: t.kind,
|
|
129
|
+
const block = { kind: t.kind, root: 0, tail: "", units: 0, emitted, covered, witness: "" };
|
|
102
130
|
block.witness = t.seed.slice(0, covered);
|
|
103
131
|
this.append(block, t.seed);
|
|
104
132
|
const blocks = this.active.get(t.messageId) ?? new Map();
|
|
@@ -124,8 +152,8 @@ export class CompactSessionState {
|
|
|
124
152
|
block.witness = "";
|
|
125
153
|
}
|
|
126
154
|
replace(block, text) {
|
|
127
|
-
|
|
128
|
-
|
|
155
|
+
this.store.adjust(block.root, -1, 0);
|
|
156
|
+
block.root = 0;
|
|
129
157
|
block.tail = "";
|
|
130
158
|
block.units = 0;
|
|
131
159
|
block.covered = 0;
|
|
@@ -137,12 +165,12 @@ export class CompactSessionState {
|
|
|
137
165
|
const blocks = this.active.get(messageId);
|
|
138
166
|
if (blocks) {
|
|
139
167
|
this.settled.set(messageId, { persistence, blocks: this.seal(blocks, true) });
|
|
168
|
+
for (const block of blocks.values())
|
|
169
|
+
this.store.adjust(block.root, -1, 0);
|
|
140
170
|
this.active.delete(messageId);
|
|
141
|
-
return;
|
|
142
171
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.settled.set(messageId, { ...row, persistence });
|
|
172
|
+
else if (this.settled.has(messageId)) {
|
|
173
|
+
this.settled.patch(messageId, { persistence });
|
|
146
174
|
}
|
|
147
175
|
else {
|
|
148
176
|
throw new CompactTransitionError("unknown_message", messageId);
|
|
@@ -150,17 +178,14 @@ export class CompactSessionState {
|
|
|
150
178
|
}
|
|
151
179
|
remove(messageId) {
|
|
152
180
|
const blocks = this.active.get(messageId);
|
|
153
|
-
const
|
|
154
|
-
if (!blocks && !
|
|
181
|
+
const settled = this.settled.has(messageId);
|
|
182
|
+
if (!blocks && !settled)
|
|
155
183
|
throw new CompactTransitionError("unknown_message", messageId);
|
|
156
184
|
for (const block of blocks?.values() ?? [])
|
|
157
|
-
|
|
158
|
-
this.store.adjust(id, -1, 0);
|
|
159
|
-
for (const block of row?.blocks ?? [])
|
|
160
|
-
for (const id of block.leaves)
|
|
161
|
-
this.store.adjust(id, -1, 0);
|
|
185
|
+
this.store.adjust(block.root, -1, 0);
|
|
162
186
|
this.active.delete(messageId);
|
|
163
|
-
|
|
187
|
+
if (settled)
|
|
188
|
+
this.settled.delete(messageId);
|
|
164
189
|
}
|
|
165
190
|
block(address) {
|
|
166
191
|
const block = this.active.get(address.messageId)?.get(address.contentIndex);
|
|
@@ -173,7 +198,7 @@ export class CompactSessionState {
|
|
|
173
198
|
let rest = text;
|
|
174
199
|
while (block.tail.length + rest.length >= COMPACT_LEAF_CODE_UNITS) {
|
|
175
200
|
const take = COMPACT_LEAF_CODE_UNITS - block.tail.length;
|
|
176
|
-
block.
|
|
201
|
+
block.root = this.store.text.append(block.root, block.tail + rest.slice(0, take));
|
|
177
202
|
block.tail = "";
|
|
178
203
|
rest = rest.slice(take);
|
|
179
204
|
}
|
|
@@ -183,24 +208,17 @@ export class CompactSessionState {
|
|
|
183
208
|
seal(blocks, settle) {
|
|
184
209
|
return [...blocks].sort(byContentIndex).map(([contentIndex, block]) => {
|
|
185
210
|
if (settle && block.tail.length > 0) {
|
|
186
|
-
block.
|
|
211
|
+
block.root = this.store.text.append(block.root, block.tail);
|
|
187
212
|
block.tail = "";
|
|
188
213
|
}
|
|
189
|
-
return { contentIndex, kind: block.kind,
|
|
214
|
+
return { contentIndex, kind: block.kind, root: block.root, tail: block.tail };
|
|
190
215
|
});
|
|
191
216
|
}
|
|
192
217
|
pin(blocks, delta) {
|
|
193
218
|
for (const block of blocks) {
|
|
194
219
|
this.frozenTailBytes += delta * block.tail.length * 2;
|
|
195
|
-
|
|
196
|
-
this.store.adjust(id, 0, delta);
|
|
220
|
+
this.store.adjust(block.root, 0, delta);
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
|
-
async texts(blocks) {
|
|
200
|
-
return Promise.all(blocks.map(async ({ contentIndex, kind, leaves, tail }) => {
|
|
201
|
-
const parts = await Promise.all(leaves.map((id) => this.store.readLeaf(id)));
|
|
202
|
-
return { contentIndex, kind, text: parts.join("") + tail };
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
223
|
}
|
|
206
224
|
//# sourceMappingURL=compact-session-state.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** Creates a fresh private epoch directory and returns the token its ownership marker holds. */
|
|
2
2
|
export declare function createSpoolDir(dir: string): Promise<string>;
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Removes `dir` only while it is still the private, marked directory `createSpoolDir` made. A
|
|
5
|
+
* directory that is already gone leaves nothing to remove, so retirement settles as a no-op.
|
|
6
|
+
*/
|
|
4
7
|
export declare function removeSpoolDir(dir: string, token: string): Promise<void>;
|
|
5
8
|
//# sourceMappingURL=compact-spool-dir.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { lstat, mkdir, readFile, rm, rmdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { describeCause } from "./compact-storage-io.js";
|
|
4
5
|
/**
|
|
5
6
|
* Ownership of the compact spool's epoch directory (senpi#4272). The store creates the directory
|
|
6
7
|
* itself (0700, never adopting an existing one) and writes a private marker into it; removal
|
|
@@ -9,8 +10,10 @@ import { dirname, join } from "node:path";
|
|
|
9
10
|
*/
|
|
10
11
|
const MARKER = ".compact-spool-owner";
|
|
11
12
|
class CompactSpoolRefusedError extends Error {
|
|
12
|
-
constructor(reason, path) {
|
|
13
|
-
|
|
13
|
+
constructor(reason, path, cause, cleanupFailure) {
|
|
14
|
+
const detail = cause === undefined ? "" : `: ${describeCause(cause)}`;
|
|
15
|
+
const kept = cleanupFailure === undefined ? "" : `; removing the directory failed: ${describeCause(cleanupFailure)}`;
|
|
16
|
+
super(`compact spool directory refused (${reason}): ${path}${detail}${kept}`, { cause });
|
|
14
17
|
this.name = "CompactSpoolRefusedError";
|
|
15
18
|
this.reason = reason;
|
|
16
19
|
this.path = path;
|
|
@@ -28,18 +31,48 @@ export async function createSpoolDir(dir) {
|
|
|
28
31
|
throw cause;
|
|
29
32
|
}
|
|
30
33
|
const token = randomUUID();
|
|
31
|
-
|
|
34
|
+
try {
|
|
35
|
+
await writeFile(join(dir, MARKER), token, { flag: "wx", mode: 0o600 });
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
throw await abandonSpoolDir(dir, cause);
|
|
39
|
+
}
|
|
32
40
|
return token;
|
|
33
41
|
}
|
|
34
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Removes a directory `createSpoolDir` just made but could not mark, so a later store creates it
|
|
44
|
+
* fresh instead of refusing it as pre-existing. Only a marker this call's exclusive create made (any
|
|
45
|
+
* failure but EEXIST) is unlinked, then the directory itself, which `rmdir` removes only while empty.
|
|
46
|
+
*/
|
|
47
|
+
async function abandonSpoolDir(dir, cause) {
|
|
48
|
+
try {
|
|
49
|
+
if (cause.code !== "EEXIST")
|
|
50
|
+
await rm(join(dir, MARKER), { force: true });
|
|
51
|
+
await rmdir(dir);
|
|
52
|
+
return new CompactSpoolRefusedError("marker_write_failed", dir, cause);
|
|
53
|
+
}
|
|
54
|
+
catch (cleanupFailure) {
|
|
55
|
+
return new CompactSpoolRefusedError("marker_write_failed", dir, cause, cleanupFailure);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Removes `dir` only while it is still the private, marked directory `createSpoolDir` made. A
|
|
60
|
+
* directory that is already gone leaves nothing to remove, so retirement settles as a no-op.
|
|
61
|
+
*/
|
|
35
62
|
export async function removeSpoolDir(dir, token) {
|
|
36
|
-
const
|
|
63
|
+
const owned = await lstat(dir).catch((cause) => {
|
|
64
|
+
if (cause.code === "ENOENT")
|
|
65
|
+
return undefined;
|
|
66
|
+
throw cause;
|
|
67
|
+
});
|
|
68
|
+
if (owned === undefined)
|
|
69
|
+
return;
|
|
70
|
+
const refusal = await refusalFor(dir, owned, token);
|
|
37
71
|
if (refusal)
|
|
38
72
|
throw new CompactSpoolRefusedError(refusal, dir);
|
|
39
73
|
await rm(dir, { recursive: true, force: true });
|
|
40
74
|
}
|
|
41
|
-
async function refusalFor(dir, token) {
|
|
42
|
-
const owned = await lstat(dir);
|
|
75
|
+
async function refusalFor(dir, owned, token) {
|
|
43
76
|
if (owned.isSymbolicLink())
|
|
44
77
|
return "symlink";
|
|
45
78
|
if (!owned.isDirectory())
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type CompactStorageFailure, type CompactStorageIo, type CompactStorageOperation } from "./compact-storage-io.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The spool file path of compact current-state storage (senpi#4272, R3.C6.4): one serial
|
|
4
|
+
* asynchronous job chain, the private epoch directory, spool file writes, reads and reclamation,
|
|
5
|
+
* and the failure ledger. `CompactContentStore` adds leaf ownership and spilling on top.
|
|
6
|
+
*/
|
|
7
|
+
export declare const RECLAIM_WORK_ALLOWANCE_BYTES: number;
|
|
8
|
+
export declare abstract class CompactSpoolStorage {
|
|
9
|
+
readonly dir: string;
|
|
10
|
+
private readonly io;
|
|
11
|
+
protected readonly failures: CompactStorageFailure[];
|
|
12
|
+
private nextFile;
|
|
13
|
+
private dirReady;
|
|
14
|
+
private spoolToken;
|
|
15
|
+
private chain;
|
|
16
|
+
private queued;
|
|
17
|
+
protected spillQueued: boolean;
|
|
18
|
+
private halted;
|
|
19
|
+
protected retired: boolean;
|
|
20
|
+
protected pendingIoCopyBytes: number;
|
|
21
|
+
protected spillBytesWritten: number;
|
|
22
|
+
protected reclaimDebtBytes: number;
|
|
23
|
+
private reads;
|
|
24
|
+
private reading;
|
|
25
|
+
assertReadable(): void;
|
|
26
|
+
beginRead(): () => void;
|
|
27
|
+
constructor(dir: string, io: CompactStorageIo);
|
|
28
|
+
/** Resolves once every queued storage job, including the ones they queued, has completed. */
|
|
29
|
+
idle(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Invalidates spilling, joins owned I/O, then removes the epoch directory only when this store
|
|
32
|
+
* created it and it still passes the ownership checks; otherwise rejects with `CompactSpoolRefusedError`.
|
|
33
|
+
*/
|
|
34
|
+
retire(): Promise<void>;
|
|
35
|
+
/** Reads a committed file and rejects a missing, unreadable or short one. */
|
|
36
|
+
readFile(path: string, bytes: number, operation: CompactStorageOperation): Promise<Buffer>;
|
|
37
|
+
private readCommittedFile;
|
|
38
|
+
/** Writes one new spool file; undefined means the write failed and was reported (a spill write also halts spilling). */
|
|
39
|
+
writeFile(data: Buffer, operation: CompactStorageOperation, retainedInMemoryBytes: number): Promise<string | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* An unreferenced (or partially written) spool file: reclamation debt until its unlink completes or
|
|
42
|
+
* finds it gone. Files dropped after retirement started belong to the directory retirement removes.
|
|
43
|
+
*/
|
|
44
|
+
discard(path: string, bytes: number): void;
|
|
45
|
+
/** A failed unlink keeps its debt and is retried with bounded backoff outside the job chain. */
|
|
46
|
+
private reclaim;
|
|
47
|
+
get spillAllowed(): boolean;
|
|
48
|
+
/** True while a storage failure or unreclaimed debt above the work allowance holds spilling back. */
|
|
49
|
+
protected get spillStopped(): boolean;
|
|
50
|
+
enqueue(operation: CompactStorageOperation, job: () => Promise<void>): void;
|
|
51
|
+
protected abstract scheduleSpill(): void;
|
|
52
|
+
/** Creates the private epoch directory once; a refusal or failure halts spilling and is reported. */
|
|
53
|
+
private ensureDir;
|
|
54
|
+
private readFailure;
|
|
55
|
+
private fail;
|
|
56
|
+
private record;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=compact-spool-storage.d.ts.map
|