sealkeep 0.9.0 → 0.11.0
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/ARCHITECTURE.md +163 -14
- package/CHANGELOG.md +223 -1
- package/CONTROL_PLANE.md +2 -2
- package/LICENSE +1 -1
- package/README.md +108 -22
- package/THIRD_PARTY.md +2 -2
- package/THREAT_MODEL.md +23 -4
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
- package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
- package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
- package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
- package/dist/site/index.html +1808 -1904
- package/dist/site/llms.txt +67 -0
- package/dist/site/trust/architecture-data-flow.html +53 -0
- package/dist/site/trust/audit-roadmap.html +37 -0
- package/dist/site/trust/deployment-responsibility.html +11 -0
- package/dist/site/trust/dpa-sample.html +30 -0
- package/dist/site/trust/release-provenance.html +21 -0
- package/dist/site/trust/subprocessors.html +15 -0
- package/dist/site/trust/threat-model.html +34 -0
- package/dist/site/trust/toms.html +41 -0
- package/dist/site/trust-document.css +32 -0
- package/dist/site/trust.html +73 -0
- package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
- package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
- package/dist/site/visual/index.html +18 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +9 -0
- package/dist/src/activity.js +90 -1
- package/dist/src/adapters.d.ts +175 -5
- package/dist/src/adapters.js +961 -73
- package/dist/src/agent-context.d.ts +135 -0
- package/dist/src/agent-context.js +1059 -0
- package/dist/src/archive-copies.d.ts +47 -0
- package/dist/src/archive-copies.js +179 -0
- package/dist/src/audit.d.ts +1 -1
- package/dist/src/audit.js +29 -4
- package/dist/src/autopilot.d.ts +52 -7
- package/dist/src/autopilot.js +143 -25
- package/dist/src/background-bandwidth.d.ts +46 -0
- package/dist/src/background-bandwidth.js +301 -0
- package/dist/src/background-cpu.d.ts +82 -0
- package/dist/src/background-cpu.js +212 -0
- package/dist/src/background-worker-error.d.ts +12 -0
- package/dist/src/background-worker-error.js +18 -0
- package/dist/src/branding.d.ts +24 -1
- package/dist/src/branding.js +31 -1
- package/dist/src/bridge.d.ts +233 -0
- package/dist/src/bridge.js +604 -0
- package/dist/src/byte-stream.d.ts +91 -0
- package/dist/src/byte-stream.js +385 -0
- package/dist/src/chunk-store.d.ts +41 -8
- package/dist/src/chunk-store.js +161 -65
- package/dist/src/cli.js +1698 -163
- package/dist/src/cloud.d.ts +841 -31
- package/dist/src/cloud.js +3196 -277
- package/dist/src/context-background.d.ts +37 -0
- package/dist/src/context-background.js +309 -0
- package/dist/src/context-drain-child.d.ts +1 -0
- package/dist/src/context-drain-child.js +98 -0
- package/dist/src/context-reader.d.ts +118 -0
- package/dist/src/context-reader.js +447 -0
- package/dist/src/control-plane/auth.d.ts +32 -4
- package/dist/src/control-plane/auth.js +85 -24
- package/dist/src/control-plane/server.js +19 -6
- package/dist/src/control-plane.d.ts +17 -1
- package/dist/src/control-plane.js +32 -6
- package/dist/src/crypto.d.ts +1 -1
- package/dist/src/crypto.js +5 -5
- package/dist/src/daemon-lease.d.ts +70 -0
- package/dist/src/daemon-lease.js +420 -0
- package/dist/src/daemon.d.ts +94 -1
- package/dist/src/daemon.js +1082 -105
- package/dist/src/darwin-service-policy.d.ts +41 -0
- package/dist/src/darwin-service-policy.js +60 -0
- package/dist/src/dashboard-cli.js +15 -15
- package/dist/src/device-authorization.d.ts +37 -0
- package/dist/src/device-authorization.js +199 -0
- package/dist/src/device-enrollment.d.ts +91 -0
- package/dist/src/device-enrollment.js +349 -0
- package/dist/src/disk.d.ts +17 -12
- package/dist/src/disk.js +43 -17
- package/dist/src/doctor.d.ts +35 -1
- package/dist/src/doctor.js +316 -41
- package/dist/src/durable-ticket-lock.d.ts +24 -0
- package/dist/src/durable-ticket-lock.js +232 -0
- package/dist/src/enroll.d.ts +1 -1
- package/dist/src/enroll.js +13 -7
- package/dist/src/env.d.ts +10 -1
- package/dist/src/env.js +11 -3
- package/dist/src/errors.d.ts +8 -8
- package/dist/src/errors.js +6 -6
- package/dist/src/flush.d.ts +12 -0
- package/dist/src/flush.js +37 -0
- package/dist/src/heartbeat.d.ts +86 -12
- package/dist/src/heartbeat.js +415 -29
- package/dist/src/index-background-watchdog.d.ts +1 -0
- package/dist/src/index-background-watchdog.js +94 -0
- package/dist/src/index-background-work.d.ts +21 -0
- package/dist/src/index-background-work.js +25 -0
- package/dist/src/index-background.d.ts +64 -0
- package/dist/src/index-background.js +394 -0
- package/dist/src/index-build-child.d.ts +1 -0
- package/dist/src/index-build-child.js +109 -0
- package/dist/src/index-manifest.d.ts +52 -0
- package/dist/src/index-manifest.js +444 -0
- package/dist/src/index-publication-proof.d.ts +84 -0
- package/dist/src/index-publication-proof.js +380 -0
- package/dist/src/index-publication-state.d.ts +149 -0
- package/dist/src/index-publication-state.js +696 -0
- package/dist/src/index-publication-verifier.d.ts +89 -0
- package/dist/src/index-publication-verifier.js +341 -0
- package/dist/src/index-publish.d.ts +62 -0
- package/dist/src/index-publish.js +540 -0
- package/dist/src/index-scratch-cleanup.d.ts +19 -0
- package/dist/src/index-scratch-cleanup.js +166 -0
- package/dist/src/index-segment-types.d.ts +132 -0
- package/dist/src/index-segment-types.js +21 -0
- package/dist/src/index-segments.d.ts +9 -0
- package/dist/src/index-segments.js +516 -0
- package/dist/src/index-store.d.ts +123 -0
- package/dist/src/index-store.js +495 -0
- package/dist/src/index-sync.d.ts +91 -15
- package/dist/src/index-sync.js +286 -53
- package/dist/src/index-upgrade-publication.d.ts +30 -0
- package/dist/src/index-upgrade-publication.js +179 -0
- package/dist/src/integration-manager.d.ts +32 -0
- package/dist/src/integration-manager.js +394 -0
- package/dist/src/leakscan.js +1 -1
- package/dist/src/local-api.d.ts +56 -15
- package/dist/src/local-api.js +4979 -541
- package/dist/src/machine-settings.d.ts +51 -0
- package/dist/src/machine-settings.js +166 -0
- package/dist/src/managed-chunks.d.ts +5 -2
- package/dist/src/managed-chunks.js +14 -14
- package/dist/src/mcp-install.d.ts +11 -9
- package/dist/src/mcp-install.js +73 -28
- package/dist/src/mcp-workspace.d.ts +18 -0
- package/dist/src/mcp-workspace.js +50 -0
- package/dist/src/mcp.js +294 -25
- package/dist/src/migrate.js +27 -21
- package/dist/src/notify.d.ts +1 -1
- package/dist/src/notify.js +5 -5
- package/dist/src/offload.d.ts +201 -14
- package/dist/src/offload.js +1848 -140
- package/dist/src/onboarding.d.ts +8 -1
- package/dist/src/onboarding.js +4 -4
- package/dist/src/packages.d.ts +2 -2
- package/dist/src/packages.js +10 -2
- package/dist/src/passkey.d.ts +0 -1
- package/dist/src/passkey.js +2 -7
- package/dist/src/password-lock.d.ts +2 -2
- package/dist/src/password-lock.js +6 -6
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/presence.d.ts +86 -0
- package/dist/src/presence.js +240 -0
- package/dist/src/progress-deadline.d.ts +21 -0
- package/dist/src/progress-deadline.js +91 -0
- package/dist/src/project-repair.d.ts +55 -0
- package/dist/src/project-repair.js +131 -0
- package/dist/src/providers/gcs.d.ts +28 -7
- package/dist/src/providers/gcs.js +35 -24
- package/dist/src/providers/gdrive.d.ts +71 -8
- package/dist/src/providers/gdrive.js +223 -50
- package/dist/src/providers/index.d.ts +11 -3
- package/dist/src/providers/index.js +30 -10
- package/dist/src/providers/s3.d.ts +30 -8
- package/dist/src/providers/s3.js +41 -30
- package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
- package/dist/src/providers/safe-storage-fetch.js +72 -0
- package/dist/src/queue.d.ts +136 -19
- package/dist/src/queue.js +862 -96
- package/dist/src/reclaim-transaction.d.ts +156 -0
- package/dist/src/reclaim-transaction.js +1027 -0
- package/dist/src/recovery-codes.d.ts +32 -0
- package/dist/src/recovery-codes.js +338 -0
- package/dist/src/recovery.js +12 -9
- package/dist/src/rehydrate.d.ts +25 -22
- package/dist/src/rehydrate.js +319 -23
- package/dist/src/restore.d.ts +57 -4
- package/dist/src/restore.js +272 -36
- package/dist/src/resume-stub.d.ts +92 -0
- package/dist/src/resume-stub.js +417 -0
- package/dist/src/retention.d.ts +98 -7
- package/dist/src/retention.js +1066 -62
- package/dist/src/rotate.js +3 -3
- package/dist/src/search.d.ts +566 -8
- package/dist/src/search.js +5897 -290
- package/dist/src/secrets.d.ts +51 -7
- package/dist/src/secrets.js +316 -24
- package/dist/src/service.d.ts +49 -11
- package/dist/src/service.js +776 -35
- package/dist/src/share.js +3 -3
- package/dist/src/shared-spaces.d.ts +98 -0
- package/dist/src/shared-spaces.js +214 -0
- package/dist/src/source-reader.d.ts +73 -0
- package/dist/src/source-reader.js +715 -0
- package/dist/src/spool.d.ts +1 -1
- package/dist/src/spool.js +1 -1
- package/dist/src/start-tui.js +2 -1
- package/dist/src/start.js +2 -2
- package/dist/src/storage-endpoint.d.ts +21 -0
- package/dist/src/storage-endpoint.js +122 -0
- package/dist/src/storage-setup.js +12 -12
- package/dist/src/storage-targets.d.ts +109 -6
- package/dist/src/storage-targets.js +975 -67
- package/dist/src/stream-to-cloud.d.ts +5 -1
- package/dist/src/stream-to-cloud.js +34 -14
- package/dist/src/sync-rules.d.ts +31 -6
- package/dist/src/sync-rules.js +153 -14
- package/dist/src/team-backfill-scheduling.d.ts +8 -0
- package/dist/src/team-backfill-scheduling.js +33 -0
- package/dist/src/team-backfill.d.ts +116 -0
- package/dist/src/team-backfill.js +1429 -0
- package/dist/src/team-index-cache.d.ts +16 -0
- package/dist/src/team-index-cache.js +152 -0
- package/dist/src/team-offboarding.d.ts +38 -0
- package/dist/src/team-offboarding.js +1043 -0
- package/dist/src/team-presence.d.ts +127 -0
- package/dist/src/team-presence.js +904 -0
- package/dist/src/team-publication-policy.d.ts +20 -0
- package/dist/src/team-publication-policy.js +140 -0
- package/dist/src/team-realtime.d.ts +68 -0
- package/dist/src/team-realtime.js +816 -0
- package/dist/src/team-source-facts-cache.d.ts +23 -0
- package/dist/src/team-source-facts-cache.js +255 -0
- package/dist/src/trash.d.ts +1 -1
- package/dist/src/trash.js +2 -2
- package/dist/src/tui.js +11 -12
- package/dist/src/types.d.ts +173 -7
- package/dist/src/types.js +20 -0
- package/dist/src/ui-server.d.ts +163 -35
- package/dist/src/ui-server.js +712 -72
- package/dist/src/ui.d.ts +1 -2
- package/dist/src/ui.js +1 -2
- package/dist/src/upload.d.ts +27 -0
- package/dist/src/upload.js +383 -43
- package/dist/src/vault.d.ts +226 -30
- package/dist/src/vault.js +1776 -192
- package/dist/src/watcher.d.ts +7 -1
- package/dist/src/watcher.js +198 -55
- package/dist/src/worker.d.ts +27 -3
- package/dist/src/worker.js +274 -55
- package/package.json +33 -12
- package/scripts/native-reboot-rehearsal.mjs +90 -0
- package/web/app.js +6032 -343
- package/web/bootstrap.js +17 -0
- package/web/index.html +255 -57
- package/web/rail.js +317 -40
- package/web/retention.html +2 -2
- package/web/rules-view.js +188 -16
- package/web/sessions-view.js +485 -62
- package/web/sessions.html +2 -2
- package/web/setup-api.js +152 -29
- package/web/setup-logic.js +68 -9
- package/web/setup.html +113 -44
- package/web/setup.js +604 -71
- package/web/style.css +513 -98
- package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ContextRequestDrain } from "./agent-context.js";
|
|
2
|
+
export type BackgroundContextDrain = {
|
|
3
|
+
pid: number | undefined;
|
|
4
|
+
/** Secret-free, never-paused orphan cleanup sibling. */
|
|
5
|
+
watchdogPid?: number;
|
|
6
|
+
started: Promise<void>;
|
|
7
|
+
result: Promise<ContextRequestDrain>;
|
|
8
|
+
/** Observe useful CPU or byte progress without exposing request contents. */
|
|
9
|
+
onProgress?(listener: () => void): () => void;
|
|
10
|
+
stop(): Promise<void>;
|
|
11
|
+
/** Immediately terminate this exact owned child; used only after its wall-clock deadline. */
|
|
12
|
+
forceStop(): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export type ContextDrainStarter = (dataDir: string, phrase: string, options: {
|
|
15
|
+
limit: number;
|
|
16
|
+
/** Automatic recall is one low-priority CPU lane, not another foreground search. */
|
|
17
|
+
cpuTarget?: number;
|
|
18
|
+
/** Tests may lower this; production can never raise the reviewed ceiling. */
|
|
19
|
+
maxRssBytes?: number;
|
|
20
|
+
/** Deterministic process fixture; production always uses the package child. */
|
|
21
|
+
childPath?: string;
|
|
22
|
+
/** Deterministic failure fixture; production uses the package watchdog. */
|
|
23
|
+
watchdogPath?: string;
|
|
24
|
+
}) => BackgroundContextDrain;
|
|
25
|
+
/** Automatic context may read the same large derived index as a rebuild, so it
|
|
26
|
+
* gets the same hard process boundaries. The hook itself never enters this
|
|
27
|
+
* process; it only leaves a tiny encrypted request for the daemon. */
|
|
28
|
+
export declare const BACKGROUND_CONTEXT_MAX_OLD_SPACE_MB = 1024;
|
|
29
|
+
export declare const BACKGROUND_CONTEXT_MAX_SEMI_SPACE_MB = 16;
|
|
30
|
+
export declare const BACKGROUND_CONTEXT_MAX_RSS_BYTES: number;
|
|
31
|
+
export declare const BACKGROUND_CONTEXT_CPU_TARGET = 0.3;
|
|
32
|
+
export declare function backgroundContextExecArgv(parent: readonly string[]): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Give hosted discovery, provider refresh and index parsing a killable process
|
|
35
|
+
* boundary. The recovery phrase uses the owned IPC pipe, never argv or env.
|
|
36
|
+
*/
|
|
37
|
+
export declare const startBackgroundContextDrain: ContextDrainStarter;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { fork } from "node:child_process";
|
|
2
|
+
import { constants, setPriority } from "node:os";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { BACKGROUND_CPU_TOTAL_TARGET, signalOwnedBackgroundProcess, onOwnedBackgroundProcessExit, startBackgroundProcessCpuGovernor } from "./background-cpu.js";
|
|
5
|
+
import { backgroundIndexExecArgv, backgroundIndexWorkerEnvironment } from "./index-background.js";
|
|
6
|
+
import { daemonProcessIdentity, registerDaemonContextWorker } from "./daemon-lease.js";
|
|
7
|
+
import { backgroundWorkerErrorFromWire } from "./background-worker-error.js";
|
|
8
|
+
/** Automatic context may read the same large derived index as a rebuild, so it
|
|
9
|
+
* gets the same hard process boundaries. The hook itself never enters this
|
|
10
|
+
* process; it only leaves a tiny encrypted request for the daemon. */
|
|
11
|
+
export const BACKGROUND_CONTEXT_MAX_OLD_SPACE_MB = 1024;
|
|
12
|
+
export const BACKGROUND_CONTEXT_MAX_SEMI_SPACE_MB = 16;
|
|
13
|
+
export const BACKGROUND_CONTEXT_MAX_RSS_BYTES = 1536 * 1024 * 1024;
|
|
14
|
+
export const BACKGROUND_CONTEXT_CPU_TARGET = 0.30;
|
|
15
|
+
export function backgroundContextExecArgv(parent) {
|
|
16
|
+
// Match historical indexing's reviewed loader allowlist; arbitrary preloads,
|
|
17
|
+
// inspector options and inline programs are not background-work inputs.
|
|
18
|
+
const inherited = backgroundIndexExecArgv(parent).filter((arg) => !arg.startsWith("--max-old-space-size") && !arg.startsWith("--max-semi-space-size")
|
|
19
|
+
&& arg !== "--single-threaded-gc");
|
|
20
|
+
return [
|
|
21
|
+
...inherited,
|
|
22
|
+
`--max-old-space-size=${BACKGROUND_CONTEXT_MAX_OLD_SPACE_MB}`,
|
|
23
|
+
`--max-semi-space-size=${BACKGROUND_CONTEXT_MAX_SEMI_SPACE_MB}`,
|
|
24
|
+
// Context parsing can trigger V8 collector threads outside JavaScript's
|
|
25
|
+
// cooperative checkpoints. Serial GC lets the parent's wall-duty governor
|
|
26
|
+
// bound the whole owned process rather than only its JavaScript lane.
|
|
27
|
+
"--single-threaded-gc",
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Give hosted discovery, provider refresh and index parsing a killable process
|
|
32
|
+
* boundary. The recovery phrase uses the owned IPC pipe, never argv or env.
|
|
33
|
+
*/
|
|
34
|
+
export const startBackgroundContextDrain = (dataDir, phrase, options) => {
|
|
35
|
+
const extension = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
36
|
+
const childPath = options.childPath ?? fileURLToPath(new URL(`./context-drain-child${extension}`, import.meta.url));
|
|
37
|
+
const child = fork(childPath, [], {
|
|
38
|
+
stdio: ["ignore", "ignore", "inherit", "ipc"],
|
|
39
|
+
execArgv: backgroundContextExecArgv(process.execArgv),
|
|
40
|
+
env: backgroundIndexWorkerEnvironment(),
|
|
41
|
+
});
|
|
42
|
+
// The context event loop cannot observe IPC disconnect while SIGSTOP-paused.
|
|
43
|
+
// Arm the same generation-checked, secret-free sibling used by indexing
|
|
44
|
+
// before sending any work or permitting the first pause.
|
|
45
|
+
const watchdogEnv = {};
|
|
46
|
+
for (const key of ["PATH", "SystemRoot", "WINDIR", "TEMP", "TMP", "TMPDIR"]) {
|
|
47
|
+
if (process.env[key] !== undefined)
|
|
48
|
+
watchdogEnv[key] = process.env[key];
|
|
49
|
+
}
|
|
50
|
+
let watchdog;
|
|
51
|
+
try {
|
|
52
|
+
watchdog = fork(options.watchdogPath ?? fileURLToPath(new URL(`./index-background-watchdog${extension}`, import.meta.url)), [], {
|
|
53
|
+
stdio: ["ignore", "ignore", "inherit", "ipc"],
|
|
54
|
+
env: watchdogEnv,
|
|
55
|
+
execArgv: [...backgroundContextExecArgv(process.execArgv).filter((arg) => !arg.startsWith("--max-old-space-size")), "--max-old-space-size=64"],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
child.once("error", () => { });
|
|
60
|
+
signalOwnedBackgroundProcess(child, "SIGKILL");
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
let watchdogDisarmed = false;
|
|
64
|
+
let watchdogExited = false;
|
|
65
|
+
let watchdogFailure;
|
|
66
|
+
let watchdogExitTimer;
|
|
67
|
+
let watchdogClosedResolve;
|
|
68
|
+
const watchdogClosed = new Promise((resolve) => { watchdogClosedResolve = resolve; });
|
|
69
|
+
// Recall is useful background preparation, but Codex/Claude and the daemon's
|
|
70
|
+
// heartbeat are interactive. Let the OS prefer them where process priority
|
|
71
|
+
// is supported; inability to adjust priority is not a correctness failure.
|
|
72
|
+
if (child.pid !== undefined) {
|
|
73
|
+
try {
|
|
74
|
+
setPriority(child.pid, constants.priority.PRIORITY_BELOW_NORMAL);
|
|
75
|
+
}
|
|
76
|
+
catch { /* advisory on platforms/containers that do not permit renice */ }
|
|
77
|
+
}
|
|
78
|
+
const cpuTarget = Math.min(1, Math.max(0.05, options.cpuTarget ?? BACKGROUND_CONTEXT_CPU_TARGET));
|
|
79
|
+
// Cooperative checkpoints cannot interrupt stop-the-world/native V8 work.
|
|
80
|
+
// The parent owns this exact child, so on POSIX it also enforces the duty
|
|
81
|
+
// cycle with short SIGSTOP/SIGCONT windows. Windows keeps the cooperative
|
|
82
|
+
// controller because it has no equivalent process signals.
|
|
83
|
+
let cpuGovernor = { stop() { } };
|
|
84
|
+
let startedResolve;
|
|
85
|
+
const progressListeners = new Set();
|
|
86
|
+
const reportProgress = () => {
|
|
87
|
+
for (const listener of progressListeners)
|
|
88
|
+
listener();
|
|
89
|
+
};
|
|
90
|
+
let startedSettled = false;
|
|
91
|
+
const started = new Promise((resolve) => { startedResolve = resolve; });
|
|
92
|
+
const markStarted = () => {
|
|
93
|
+
if (startedSettled)
|
|
94
|
+
return;
|
|
95
|
+
startedSettled = true;
|
|
96
|
+
startedResolve();
|
|
97
|
+
};
|
|
98
|
+
let resultResolve;
|
|
99
|
+
let resultReject;
|
|
100
|
+
let resultSettled = false;
|
|
101
|
+
const result = new Promise((resolve, reject) => {
|
|
102
|
+
resultResolve = resolve;
|
|
103
|
+
resultReject = reject;
|
|
104
|
+
});
|
|
105
|
+
let reportedResult;
|
|
106
|
+
let reportedError;
|
|
107
|
+
let governorFailure;
|
|
108
|
+
let workerIdentity;
|
|
109
|
+
let workerRegistration;
|
|
110
|
+
let closedResolve;
|
|
111
|
+
let closedSettled = false;
|
|
112
|
+
const closed = new Promise((resolve) => { closedResolve = resolve; });
|
|
113
|
+
let cleanedResolve;
|
|
114
|
+
const cleaned = new Promise((resolve) => { cleanedResolve = resolve; });
|
|
115
|
+
onOwnedBackgroundProcessExit(child, (code, signal) => {
|
|
116
|
+
cpuGovernor.stop();
|
|
117
|
+
clearTimeout(startupTimer);
|
|
118
|
+
closedSettled = true;
|
|
119
|
+
watchdogDisarmed = true;
|
|
120
|
+
if (!watchdogExited) {
|
|
121
|
+
if (watchdog.connected)
|
|
122
|
+
watchdog.send({ type: "disarm" }, () => { });
|
|
123
|
+
watchdogExitTimer = setTimeout(() => signalOwnedBackgroundProcess(watchdog, "SIGKILL"), 2_000);
|
|
124
|
+
}
|
|
125
|
+
closedResolve();
|
|
126
|
+
markStarted();
|
|
127
|
+
// Successful daemon passes do not call stop(); keep the pass in flight
|
|
128
|
+
// until both owned processes are gone, not merely until the worker exits.
|
|
129
|
+
void watchdogClosed.then(async () => {
|
|
130
|
+
try {
|
|
131
|
+
await (await workerRegistration)?.retireAfterExit();
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
reportedError = error instanceof Error ? error : new Error(String(error));
|
|
135
|
+
reportedResult = undefined;
|
|
136
|
+
}
|
|
137
|
+
cleanedResolve();
|
|
138
|
+
if (resultSettled)
|
|
139
|
+
return;
|
|
140
|
+
resultSettled = true;
|
|
141
|
+
if (watchdogFailure)
|
|
142
|
+
resultReject(watchdogFailure);
|
|
143
|
+
else if (governorFailure)
|
|
144
|
+
resultReject(governorFailure);
|
|
145
|
+
else if (reportedResult)
|
|
146
|
+
resultResolve(reportedResult);
|
|
147
|
+
else
|
|
148
|
+
resultReject(reportedError ?? new Error(`Context child exited before reporting a result (${signal ?? code ?? "unknown"})`));
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
child.once("error", (error) => {
|
|
152
|
+
reportedError ??= error;
|
|
153
|
+
cpuGovernor.stop();
|
|
154
|
+
});
|
|
155
|
+
child.on("message", (wire) => {
|
|
156
|
+
if (!wire || typeof wire !== "object" || !("type" in wire))
|
|
157
|
+
return;
|
|
158
|
+
const message = wire;
|
|
159
|
+
if (message.type === "started" || message.type === "progress") {
|
|
160
|
+
markStarted();
|
|
161
|
+
reportProgress();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (message.type === "result") {
|
|
165
|
+
reportedResult = message.result;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (message.type === "error") {
|
|
169
|
+
reportedError = backgroundWorkerErrorFromWire(message.error);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const beginDrain = () => {
|
|
173
|
+
if (closedSettled || stopInFlight || watchdogFailure)
|
|
174
|
+
return;
|
|
175
|
+
clearTimeout(startupTimer);
|
|
176
|
+
// Recall is latency work, not bulk work: an agent's next tool boundary is
|
|
177
|
+
// waiting on it and the whole job is a second or two of CPU. It therefore
|
|
178
|
+
// runs at full share on its own, outside the daemon-wide background budget.
|
|
179
|
+
// Sharing that budget with the index catch-up halved its slice, the
|
|
180
|
+
// governor held it SIGSTOPped most of every second, a stopped child reports
|
|
181
|
+
// no progress, and the stall deadline killed every pass — for as long as the
|
|
182
|
+
// catch-up ran, which on a real vault is hours. No memory ever arrived.
|
|
183
|
+
cpuGovernor = startBackgroundProcessCpuGovernor({
|
|
184
|
+
target: 1, signal: (signal) => signalOwnedBackgroundProcess(child, signal),
|
|
185
|
+
verifySignals: true,
|
|
186
|
+
onFailure: (error) => {
|
|
187
|
+
governorFailure ??= error;
|
|
188
|
+
reportedResult = undefined;
|
|
189
|
+
// IPC loss is a separate cancellation channel when OS policy denies
|
|
190
|
+
// signals. The native child aborts and exits within its orphan grace.
|
|
191
|
+
if (child.connected)
|
|
192
|
+
child.disconnect();
|
|
193
|
+
void stop();
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
if (governorFailure)
|
|
197
|
+
return;
|
|
198
|
+
child.send({
|
|
199
|
+
type: "drain",
|
|
200
|
+
dataDir,
|
|
201
|
+
phrase,
|
|
202
|
+
limit: options.limit,
|
|
203
|
+
// Avoid compounding two clocks on POSIX; the parent governor covers native
|
|
204
|
+
// work too. Windows falls back to the cooperative in-child controller.
|
|
205
|
+
cpuTarget: process.platform === "win32" ? Math.min(cpuTarget, BACKGROUND_CPU_TOTAL_TARGET / 3) : 1,
|
|
206
|
+
maxRssBytes: Math.min(BACKGROUND_CONTEXT_MAX_RSS_BYTES, Math.max(1, options.maxRssBytes ?? BACKGROUND_CONTEXT_MAX_RSS_BYTES)),
|
|
207
|
+
}, (error) => {
|
|
208
|
+
if (!error)
|
|
209
|
+
return;
|
|
210
|
+
reportedError ??= error;
|
|
211
|
+
void stop();
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
let stopInFlight;
|
|
215
|
+
const stop = () => {
|
|
216
|
+
if (stopInFlight)
|
|
217
|
+
return stopInFlight;
|
|
218
|
+
stopInFlight = (async () => {
|
|
219
|
+
clearTimeout(startupTimer);
|
|
220
|
+
if (closedSettled) {
|
|
221
|
+
await cleaned;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
cpuGovernor.stop();
|
|
225
|
+
signalOwnedBackgroundProcess(child, "SIGTERM");
|
|
226
|
+
let force;
|
|
227
|
+
await Promise.race([
|
|
228
|
+
closed,
|
|
229
|
+
new Promise((resolve) => {
|
|
230
|
+
force = setTimeout(() => {
|
|
231
|
+
if (!closedSettled) {
|
|
232
|
+
cpuGovernor.stop();
|
|
233
|
+
signalOwnedBackgroundProcess(child, "SIGKILL");
|
|
234
|
+
}
|
|
235
|
+
resolve();
|
|
236
|
+
}, 2_000);
|
|
237
|
+
}),
|
|
238
|
+
]);
|
|
239
|
+
if (force)
|
|
240
|
+
clearTimeout(force);
|
|
241
|
+
if (!closedSettled)
|
|
242
|
+
await closed;
|
|
243
|
+
await cleaned;
|
|
244
|
+
})();
|
|
245
|
+
return stopInFlight;
|
|
246
|
+
};
|
|
247
|
+
const forceStop = () => {
|
|
248
|
+
clearTimeout(startupTimer);
|
|
249
|
+
cpuGovernor.stop();
|
|
250
|
+
if (!closedSettled)
|
|
251
|
+
signalOwnedBackgroundProcess(child, "SIGKILL");
|
|
252
|
+
if (!stopInFlight)
|
|
253
|
+
stopInFlight = cleaned;
|
|
254
|
+
return stopInFlight;
|
|
255
|
+
};
|
|
256
|
+
const watchdogFailed = (error) => {
|
|
257
|
+
if (closedSettled || watchdogDisarmed)
|
|
258
|
+
return;
|
|
259
|
+
watchdogFailure ??= error;
|
|
260
|
+
reportedResult = undefined;
|
|
261
|
+
void stop();
|
|
262
|
+
};
|
|
263
|
+
watchdog.once("error", watchdogFailed);
|
|
264
|
+
watchdog.once("disconnect", () => watchdogFailed(new Error("Automatic context orphan watchdog disconnected unexpectedly")));
|
|
265
|
+
onOwnedBackgroundProcessExit(watchdog, (code, signal) => {
|
|
266
|
+
watchdogExited = true;
|
|
267
|
+
if (watchdogExitTimer)
|
|
268
|
+
clearTimeout(watchdogExitTimer);
|
|
269
|
+
watchdogClosedResolve();
|
|
270
|
+
watchdogFailed(new Error(`Automatic context orphan watchdog exited unexpectedly (${signal ?? code ?? "unknown"})`));
|
|
271
|
+
});
|
|
272
|
+
watchdog.once("message", (wire) => {
|
|
273
|
+
if (wire && typeof wire === "object" && "type" in wire && wire.type === "armed") {
|
|
274
|
+
if (closedSettled || stopInFlight || !workerIdentity || child.pid === undefined)
|
|
275
|
+
return;
|
|
276
|
+
workerRegistration = registerDaemonContextWorker(dataDir, child.pid, workerIdentity);
|
|
277
|
+
void workerRegistration.then(beginDrain, watchdogFailed);
|
|
278
|
+
}
|
|
279
|
+
else
|
|
280
|
+
watchdogFailed(new Error("Automatic context orphan watchdog did not acknowledge ownership"));
|
|
281
|
+
});
|
|
282
|
+
const startupTimer = setTimeout(() => watchdogFailed(new Error("Automatic context orphan watchdog startup timed out")), 10_000);
|
|
283
|
+
void (async () => {
|
|
284
|
+
if (child.pid === undefined)
|
|
285
|
+
throw new Error("Automatic context child has no process identity");
|
|
286
|
+
const identity = await daemonProcessIdentity(child.pid);
|
|
287
|
+
if (closedSettled || stopInFlight)
|
|
288
|
+
return;
|
|
289
|
+
if (identity.state !== "identified")
|
|
290
|
+
throw new Error("Automatic context child process generation could not be verified");
|
|
291
|
+
workerIdentity = identity.identity;
|
|
292
|
+
watchdog.send({ type: "arm", pid: child.pid, identity: identity.identity, graceMs: 2_000 }, (error) => {
|
|
293
|
+
if (error)
|
|
294
|
+
watchdogFailed(error);
|
|
295
|
+
});
|
|
296
|
+
})().catch(watchdogFailed);
|
|
297
|
+
return {
|
|
298
|
+
pid: child.pid,
|
|
299
|
+
watchdogPid: watchdog.pid,
|
|
300
|
+
started,
|
|
301
|
+
result,
|
|
302
|
+
onProgress(listener) {
|
|
303
|
+
progressListeners.add(listener);
|
|
304
|
+
return () => { progressListeners.delete(listener); };
|
|
305
|
+
},
|
|
306
|
+
stop,
|
|
307
|
+
forceStop,
|
|
308
|
+
};
|
|
309
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { drainAutomaticAgentContextRequests } from "./agent-context.js";
|
|
2
|
+
import { createBackgroundCpuYield } from "./background-cpu.js";
|
|
3
|
+
import { BACKGROUND_CONTEXT_CPU_TARGET, BACKGROUND_CONTEXT_MAX_RSS_BYTES, } from "./context-background.js";
|
|
4
|
+
import { SealkeepError } from "./errors.js";
|
|
5
|
+
import { backgroundUploadBytesPerSecond, readLocalSettings } from "./machine-settings.js";
|
|
6
|
+
import { backgroundWorkerErrorToWire } from "./background-worker-error.js";
|
|
7
|
+
function send(message) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
if (!process.send || !process.connected) {
|
|
10
|
+
resolve();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
process.send(message, () => resolve());
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
let completed = false;
|
|
17
|
+
const cancellation = new AbortController();
|
|
18
|
+
const cancel = () => {
|
|
19
|
+
if (completed || cancellation.signal.aborted)
|
|
20
|
+
return;
|
|
21
|
+
const error = new Error("Automatic context owner requested shutdown");
|
|
22
|
+
error.name = "AbortError";
|
|
23
|
+
cancellation.abort(error);
|
|
24
|
+
};
|
|
25
|
+
process.once("SIGTERM", cancel);
|
|
26
|
+
process.once("SIGINT", cancel);
|
|
27
|
+
process.once("disconnect", () => {
|
|
28
|
+
if (completed)
|
|
29
|
+
return;
|
|
30
|
+
cancel();
|
|
31
|
+
setTimeout(() => process.exit(0), 2_000).unref();
|
|
32
|
+
});
|
|
33
|
+
// Recall can touch local indexes and optional hosted providers. Keeping one
|
|
34
|
+
// request in one owned process gives the daemon a real cancellation boundary:
|
|
35
|
+
// a fetch or parser that never settles cannot pin every later memory request.
|
|
36
|
+
process.once("message", async (wire) => {
|
|
37
|
+
const request = wire && typeof wire === "object" ? wire : {};
|
|
38
|
+
if (request.type !== "drain" || typeof request.dataDir !== "string"
|
|
39
|
+
|| typeof request.phrase !== "string" || typeof request.limit !== "number") {
|
|
40
|
+
await send({ type: "error", error: { message: "Context child received an invalid drain request" } });
|
|
41
|
+
completed = true;
|
|
42
|
+
if (process.connected)
|
|
43
|
+
process.disconnect();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await send({ type: "started" });
|
|
47
|
+
try {
|
|
48
|
+
const settings = await readLocalSettings(request.dataDir);
|
|
49
|
+
const maxBytesPerSecond = backgroundUploadBytesPerSecond(settings);
|
|
50
|
+
const cpuTarget = typeof request.cpuTarget === "number" && Number.isFinite(request.cpuTarget)
|
|
51
|
+
? Math.min(1, Math.max(0.05, request.cpuTarget))
|
|
52
|
+
: BACKGROUND_CONTEXT_CPU_TARGET;
|
|
53
|
+
const maxRssBytes = typeof request.maxRssBytes === "number" && Number.isFinite(request.maxRssBytes)
|
|
54
|
+
? Math.min(BACKGROUND_CONTEXT_MAX_RSS_BYTES, Math.max(1, request.maxRssBytes))
|
|
55
|
+
: BACKGROUND_CONTEXT_MAX_RSS_BYTES;
|
|
56
|
+
const cooperate = createBackgroundCpuYield({ target: cpuTarget, signal: cancellation.signal, budget: null });
|
|
57
|
+
let lastProgressAt = 0;
|
|
58
|
+
const reportProgress = () => {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
if (now - lastProgressAt < 250)
|
|
61
|
+
return;
|
|
62
|
+
lastProgressAt = now;
|
|
63
|
+
void send({ type: "progress" });
|
|
64
|
+
};
|
|
65
|
+
const onYield = async () => {
|
|
66
|
+
cancellation.signal.throwIfAborted();
|
|
67
|
+
const before = process.memoryUsage().rss;
|
|
68
|
+
if (before > maxRssBytes) {
|
|
69
|
+
throw new SealkeepError("operation_timeout", `Automatic context stopped at its ${Math.round(maxRssBytes / 1024 / 1024)} MiB memory safety ceiling`, { operation: "automatic_context", rssBytes: before, maxRssBytes, retryable: true });
|
|
70
|
+
}
|
|
71
|
+
await cooperate();
|
|
72
|
+
const after = process.memoryUsage().rss;
|
|
73
|
+
if (after > maxRssBytes) {
|
|
74
|
+
throw new SealkeepError("operation_timeout", `Automatic context stopped at its ${Math.round(maxRssBytes / 1024 / 1024)} MiB memory safety ceiling`, { operation: "automatic_context", rssBytes: after, maxRssBytes, retryable: true });
|
|
75
|
+
}
|
|
76
|
+
reportProgress();
|
|
77
|
+
};
|
|
78
|
+
await onYield();
|
|
79
|
+
const result = await drainAutomaticAgentContextRequests(request.dataDir, request.phrase, {
|
|
80
|
+
limit: Math.max(1, Math.min(8, Math.trunc(request.limit))),
|
|
81
|
+
scheduling: {
|
|
82
|
+
signal: cancellation.signal,
|
|
83
|
+
maxBytesPerSecond,
|
|
84
|
+
onYield,
|
|
85
|
+
onProgress: reportProgress,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
await send({ type: "result", result });
|
|
89
|
+
}
|
|
90
|
+
catch (cause) {
|
|
91
|
+
await send({ type: "error", error: backgroundWorkerErrorToWire(cause) });
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
completed = true;
|
|
95
|
+
if (process.connected)
|
|
96
|
+
process.disconnect();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type SourceReference, type SourceReadLimits } from "./source-reader.js";
|
|
2
|
+
export type ContextMessage = {
|
|
3
|
+
messageId: string;
|
|
4
|
+
role: "user" | "assistant" | "tool" | "unknown";
|
|
5
|
+
kind: "message" | "tool_call" | "tool_result";
|
|
6
|
+
/** The row is in this source; recalled text still belongs to another memory. */
|
|
7
|
+
provenance: "session" | "retrieved_memory" | "unknown";
|
|
8
|
+
toolName?: string;
|
|
9
|
+
text: string;
|
|
10
|
+
byteStart: number;
|
|
11
|
+
byteEnd: number;
|
|
12
|
+
turnId?: string;
|
|
13
|
+
callId?: string;
|
|
14
|
+
parentCallId?: string;
|
|
15
|
+
actualUserMessage?: true;
|
|
16
|
+
userEvidence?: "native_user_event" | "native_user_text" | "claude_user_message";
|
|
17
|
+
userContentIndexes?: number[];
|
|
18
|
+
textOffset?: number;
|
|
19
|
+
omittedPrefix?: true;
|
|
20
|
+
truncated?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type ContextCursor = {
|
|
23
|
+
sourceSha256: string;
|
|
24
|
+
byteStart: number;
|
|
25
|
+
textOffset?: number;
|
|
26
|
+
};
|
|
27
|
+
export type ReadContextOptions = SourceReadLimits & {
|
|
28
|
+
source: SourceReference;
|
|
29
|
+
query?: string;
|
|
30
|
+
byteStart?: number;
|
|
31
|
+
chunkIndex?: number;
|
|
32
|
+
cursor?: ContextCursor;
|
|
33
|
+
maxTokens?: number;
|
|
34
|
+
neighboringMessages?: number;
|
|
35
|
+
};
|
|
36
|
+
/** Byte locations refer to the immutable original JSONL, never reconstructed prose. */
|
|
37
|
+
export declare function parseContextMessages(bytes: Buffer, source: SourceReference, byteStart?: number): ContextMessage[];
|
|
38
|
+
/** Conservative byte accounting bounds text tokens without depending on a model tokenizer. */
|
|
39
|
+
export declare function readContext(dataDir: string, phrase: string, options: ReadContextOptions): Promise<{
|
|
40
|
+
status: "unsupported";
|
|
41
|
+
source: SourceReference;
|
|
42
|
+
messages: never[];
|
|
43
|
+
reason: {};
|
|
44
|
+
message: string;
|
|
45
|
+
continuation?: undefined;
|
|
46
|
+
truncated?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
status: "unsupported";
|
|
49
|
+
source: SourceReference;
|
|
50
|
+
messages: never[];
|
|
51
|
+
reason: string;
|
|
52
|
+
message: string;
|
|
53
|
+
continuation: {
|
|
54
|
+
sourceSha256: string;
|
|
55
|
+
byteStart: number;
|
|
56
|
+
};
|
|
57
|
+
truncated: boolean;
|
|
58
|
+
} | {
|
|
59
|
+
continuation: ContextCursor;
|
|
60
|
+
truncated: boolean;
|
|
61
|
+
messages: ContextMessage[];
|
|
62
|
+
status: "ok";
|
|
63
|
+
source: SourceReference;
|
|
64
|
+
extractionVersion: number;
|
|
65
|
+
userAnchor: {
|
|
66
|
+
status: "observed";
|
|
67
|
+
messageId: string;
|
|
68
|
+
byteStart: number;
|
|
69
|
+
reason?: undefined;
|
|
70
|
+
} | {
|
|
71
|
+
status: "unavailable";
|
|
72
|
+
reason: string;
|
|
73
|
+
messageId?: undefined;
|
|
74
|
+
byteStart?: undefined;
|
|
75
|
+
};
|
|
76
|
+
range: {
|
|
77
|
+
byteStart: number;
|
|
78
|
+
byteEnd: number;
|
|
79
|
+
};
|
|
80
|
+
usage: {
|
|
81
|
+
wireBytes: number;
|
|
82
|
+
decodedBytes: number;
|
|
83
|
+
tokenBudget: number;
|
|
84
|
+
accounting: string;
|
|
85
|
+
};
|
|
86
|
+
reason?: undefined;
|
|
87
|
+
message?: undefined;
|
|
88
|
+
} | {
|
|
89
|
+
truncated: boolean;
|
|
90
|
+
messages: ContextMessage[];
|
|
91
|
+
status: "ok";
|
|
92
|
+
source: SourceReference;
|
|
93
|
+
extractionVersion: number;
|
|
94
|
+
userAnchor: {
|
|
95
|
+
status: "observed";
|
|
96
|
+
messageId: string;
|
|
97
|
+
byteStart: number;
|
|
98
|
+
reason?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
status: "unavailable";
|
|
101
|
+
reason: string;
|
|
102
|
+
messageId?: undefined;
|
|
103
|
+
byteStart?: undefined;
|
|
104
|
+
};
|
|
105
|
+
range: {
|
|
106
|
+
byteStart: number;
|
|
107
|
+
byteEnd: number;
|
|
108
|
+
};
|
|
109
|
+
usage: {
|
|
110
|
+
wireBytes: number;
|
|
111
|
+
decodedBytes: number;
|
|
112
|
+
tokenBudget: number;
|
|
113
|
+
accounting: string;
|
|
114
|
+
};
|
|
115
|
+
reason?: undefined;
|
|
116
|
+
message?: undefined;
|
|
117
|
+
continuation?: undefined;
|
|
118
|
+
}>;
|