@timurproko/a1 0.2.7-dev.754 → 0.2.7-dev.772
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/README.md +30 -0
- package/bin/cli.js +4 -0
- package/bin/guardian.js +5 -0
- package/bin/tab-launch-context.d.ts +26 -0
- package/bin/tab-launch-context.js +94 -0
- package/bin/ui.js +24 -6
- package/dist/app/session-shell/helper-pool.d.ts +3 -5
- package/dist/app/session-shell/helper-pool.js +3 -5
- package/dist/app/session-shell/session-shell-root.d.ts +28 -1
- package/dist/app/session-shell/session-shell-root.js +4 -3
- package/dist/app/session-shell/session-shell.js +149 -31
- package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
- package/dist/app/session-shell/session-viewport-controller.js +3 -3
- package/dist/app/session-shell/shell-host.d.ts +2 -0
- package/dist/app/session-shell/shell-host.js +20 -1
- package/dist/cli/dispatch.d.ts +11 -1
- package/dist/cli/dispatch.js +79 -5
- package/dist/cli/tabs-request.d.ts +7 -0
- package/dist/cli/tabs-request.js +1 -0
- package/dist/cli/tabs.d.ts +14 -0
- package/dist/cli/tabs.js +140 -0
- package/dist/composition/owned-ui.d.ts +26 -0
- package/dist/composition/owned-ui.js +176 -6
- package/dist/contracts/owned-ui/session-backend.d.ts +1 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/intent.d.ts +3 -1
- package/dist/features/launch/intent.js +7 -4
- package/dist/features/owned-ui/settings-app.js +8 -35
- package/dist/features/resident-tab/auto-name.d.ts +11 -0
- package/dist/features/resident-tab/auto-name.js +42 -0
- package/dist/features/resident-tab/index.d.ts +3 -0
- package/dist/features/resident-tab/index.js +3 -0
- package/dist/features/resident-tab/shortcuts.d.ts +10 -0
- package/dist/features/resident-tab/shortcuts.js +35 -0
- package/dist/features/resident-tab/status-deriver.d.ts +9 -0
- package/dist/features/resident-tab/status-deriver.js +15 -0
- package/dist/foundation/launch-context/index.d.ts +10 -0
- package/dist/foundation/launch-context/index.js +25 -0
- package/dist/foundation/launch-guardian/main.d.ts +5 -0
- package/dist/foundation/launch-guardian/main.js +2 -1
- package/dist/foundation/release/bootstrap.d.ts +6 -0
- package/dist/foundation/release/bootstrap.js +51 -10
- package/dist/foundation/release/cohort-state.d.ts +1 -1
- package/dist/foundation/release/cohort-state.js +1 -1
- package/dist/foundation/release/dependency-certification.js +54 -19
- package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
- package/dist/foundation/release/progress-palette.generated.js +2 -2
- package/dist/foundation/release/release-gc.d.ts +3 -0
- package/dist/foundation/release/release-gc.js +44 -8
- package/dist/foundation/resident-tabs/bounded-host-command.d.ts +16 -0
- package/dist/foundation/resident-tabs/bounded-host-command.js +35 -0
- package/dist/foundation/resident-tabs/bridge-client.d.ts +56 -0
- package/dist/foundation/resident-tabs/bridge-client.js +279 -0
- package/dist/foundation/resident-tabs/doctor.d.ts +29 -0
- package/dist/foundation/resident-tabs/doctor.js +166 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +31 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +147 -0
- package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
- package/dist/foundation/resident-tabs/fault-injection.js +26 -0
- package/dist/foundation/resident-tabs/index.d.ts +12 -0
- package/dist/foundation/resident-tabs/index.js +6 -0
- package/dist/foundation/resident-tabs/maintenance.d.ts +42 -0
- package/dist/foundation/resident-tabs/maintenance.js +78 -0
- package/dist/foundation/resident-tabs/prompt-journal.d.ts +65 -0
- package/dist/foundation/resident-tabs/prompt-journal.js +214 -0
- package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
- package/dist/foundation/resident-tabs/protocol.js +195 -0
- package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
- package/dist/foundation/resident-tabs/recovery.js +205 -0
- package/dist/foundation/resident-tabs/release-holds.d.ts +27 -0
- package/dist/foundation/resident-tabs/release-holds.js +77 -0
- package/dist/foundation/resident-tabs/routing.d.ts +11 -0
- package/dist/foundation/resident-tabs/routing.js +10 -0
- package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
- package/dist/foundation/resident-tabs/session-sync.js +37 -0
- package/dist/foundation/resident-tabs/settings.d.ts +14 -0
- package/dist/foundation/resident-tabs/settings.js +66 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +18 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.js +53 -0
- package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
- package/dist/foundation/resident-tabs/zip-archive.js +56 -0
- package/dist/foundation/session-writer-lock/index.d.ts +32 -0
- package/dist/foundation/session-writer-lock/index.js +201 -0
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
- package/dist/foundation/terminal-cleanup/index.js +1 -1
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
- package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
- package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +4 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +38 -4
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +0 -2
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +41 -20
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/history/editor-core.js +5 -5
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +3 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +24 -1
- package/dist/integrations/pi/engine/adapter.d.ts +1 -0
- package/dist/integrations/pi/engine/adapter.js +4 -0
- package/dist/integrations/pi/engine/pi-settings-metadata.json +2 -2
- package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
- package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/runtime-integration.js +102 -29
- package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
- package/dist/integrations/pi/engine/session-runtime.js +8 -3
- package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
- package/dist/integrations/pi/engine/session-selection.js +45 -9
- package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
- package/dist/integrations/pi/startup-public.js +152 -222
- package/dist/integrations/pi/startup-public.manifest.json +266 -177
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
- package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host +0 -0
- package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
- package/dist/native/darwin-arm64/terminal-host.manifest.json +44 -0
- package/dist/native/darwin-x64/a1_session_lock.node +0 -0
- package/dist/native/darwin-x64/manifest.json +20 -0
- package/dist/native/darwin-x64/process-guardian +0 -0
- package/dist/native/darwin-x64/terminal-host +0 -0
- package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
- package/dist/native/darwin-x64/terminal-host.manifest.json +44 -0
- package/dist/native/linux-x64/a1_session_lock.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host +0 -0
- package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
- package/dist/native/linux-x64/terminal-host.manifest.json +44 -0
- package/dist/native/win32-x64/a1_session_lock.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
- package/dist/native/win32-x64/terminal-host.exe +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +44 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +5 -0
- package/dist/runtime-payload-inventory.json +5349 -6035
- package/dist/ui/components/line-input.d.ts +1 -1
- package/dist/ui/components/prompt-input.d.ts +7 -5
- package/dist/ui/components/prompt-input.js +7 -7
- package/dist/ui/components/transcript-viewport.d.ts +6 -3
- package/dist/ui/components/transcript-viewport.js +13 -15
- package/dist/ui/settings/declarations.d.ts +25 -1
- package/dist/ui/settings/declarations.js +21 -1
- package/dist/ui/settings/index.d.ts +4 -2
- package/dist/ui/settings/index.js +2 -1
- package/dist/ui/settings/manager.d.ts +10 -3
- package/dist/ui/settings/manager.js +90 -30
- package/dist/ui/settings/migrations.d.ts +6 -0
- package/dist/ui/settings/migrations.js +44 -0
- package/dist/ui/settings/profile-document.d.ts +26 -0
- package/dist/ui/settings/profile-document.js +164 -0
- package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
- package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
- package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
- package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
- package/docs/architecture/internal-naming.md +3 -1
- package/docs/architecture/process-guardian-provenance.md +2 -0
- package/docs/architecture/resident-tabs-requirements.md +18 -0
- package/docs/architecture/resident-tabs.md +594 -0
- package/docs/architecture/terminal-host-proof-gate.md +2 -0
- package/docs/architecture/terminal-host-provenance.md +13 -3
- package/docs/architecture/toolchain.md +1 -1
- package/docs/ci-release-runbook.md +2 -2
- package/docs/features/launch-profiles.md +11 -8
- package/docs/manual-resident-tabs-acceptance.md +51 -0
- package/docs/openspec-archive-automation.md +17 -16
- package/docs/validation.md +9 -1
- package/package.json +10 -4
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
4
|
+
import { runBoundedHostCommand } from "./bounded-host-command.js";
|
|
5
|
+
import { resolveTerminalHostPath } from "./ensure-terminal-host.js";
|
|
6
|
+
import { verifyTerminalHostArtifact } from "./terminal-host-artifact.js";
|
|
7
|
+
const INCOMPATIBLE_EXIT = 4;
|
|
8
|
+
/**
|
|
9
|
+
* Runs one maintenance form through a verified terminal host. When the running host speaks
|
|
10
|
+
* another protocol generation, it retries once with the binary from the release that host
|
|
11
|
+
* recorded, after verifying that release's manifest; it never runs an unverified binary.
|
|
12
|
+
*/
|
|
13
|
+
export async function runTerminalHostMaintenance(options) {
|
|
14
|
+
const platform = options.platform ?? process.platform;
|
|
15
|
+
const architecture = options.architecture ?? process.arch;
|
|
16
|
+
const executable = await firstVerifiedHost(options, options.releaseRoots, platform, architecture);
|
|
17
|
+
if (executable === undefined)
|
|
18
|
+
return unavailable(options.action, "no verified terminal host is installed");
|
|
19
|
+
const report = await invoke(options, executable);
|
|
20
|
+
if (report.exitCode !== INCOMPATIBLE_EXIT || typeof report.releaseRoot !== "string")
|
|
21
|
+
return report;
|
|
22
|
+
const cohort = await firstVerifiedHost({ ...options, environment: {} }, [report.releaseRoot], platform, architecture);
|
|
23
|
+
if (cohort === undefined || cohort === executable) {
|
|
24
|
+
return { ...report, message: "the running tabs use another version whose terminal host cannot be verified" };
|
|
25
|
+
}
|
|
26
|
+
return await invoke(options, cohort);
|
|
27
|
+
}
|
|
28
|
+
function arguments_(options) {
|
|
29
|
+
const command = options.action === "stop" ? ["stop", options.tabId ?? ""] : [options.action];
|
|
30
|
+
return ["tabs", ...command, "--profile", options.profileRoot, "--data-dir", options.dataDir, "--runtime-dir", options.runtimeDir];
|
|
31
|
+
}
|
|
32
|
+
async function invoke(options, executable) {
|
|
33
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
34
|
+
try {
|
|
35
|
+
const result = await (options.run ?? runHost)(executable, arguments_(options), timeoutMs);
|
|
36
|
+
const line = result.stdout.trim().split(/\r?\n/u).at(-1) ?? "";
|
|
37
|
+
const parsed = JSON.parse(line);
|
|
38
|
+
if (parsed.schema !== "a1-tabs-maintenance-v1" || typeof parsed.host !== "string")
|
|
39
|
+
throw new Error("malformed maintenance report");
|
|
40
|
+
return { ...parsed, exitCode: result.code };
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return unavailable(options.action, `terminal host maintenance failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function firstVerifiedHost(options, roots, platform, architecture) {
|
|
47
|
+
const override = options.environment[PRODUCT_IDENTITY.environment.developmentInstanceId]
|
|
48
|
+
? options.environment[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim()
|
|
49
|
+
: undefined;
|
|
50
|
+
if (override) {
|
|
51
|
+
try {
|
|
52
|
+
await access(override, constants.R_OK | (platform === "win32" ? 0 : constants.X_OK));
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const root of roots) {
|
|
60
|
+
const candidate = resolveTerminalHostPath(root, {}, platform, architecture);
|
|
61
|
+
try {
|
|
62
|
+
await (options.verify ?? verifyTerminalHostArtifact)(candidate, platform, architecture);
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Rationale: an older retained release may predate the packaged host; try the next one.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function unavailable(action, message) {
|
|
72
|
+
return { schema: "a1-tabs-maintenance-v1", command: action, host: "unavailable", message, exitCode: 3 };
|
|
73
|
+
}
|
|
74
|
+
/** The largest maintenance report accepted from the terminal host. */
|
|
75
|
+
export const MAX_MAINTENANCE_REPORT_BYTES = 4 * 1024 * 1024;
|
|
76
|
+
async function runHost(executable, arguments_, timeoutMs) {
|
|
77
|
+
return await runBoundedHostCommand(executable, arguments_, { maxStdoutBytes: MAX_MAINTENANCE_REPORT_BYTES, timeoutMs, label: "terminal host maintenance" });
|
|
78
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ResidentTabFaultInjection } from "./fault-injection.js";
|
|
2
|
+
export interface SubmittedPromptRecord {
|
|
3
|
+
readonly type: "submitted";
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly sessionId: string;
|
|
6
|
+
readonly sessionFile: string | null;
|
|
7
|
+
readonly incarnation: number;
|
|
8
|
+
readonly fileExisted: boolean;
|
|
9
|
+
readonly kind: "prompt" | "steer" | "follow-up";
|
|
10
|
+
readonly text: string;
|
|
11
|
+
readonly images?: readonly unknown[];
|
|
12
|
+
readonly at: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CorrelatedPromptRecord {
|
|
15
|
+
readonly type: "correlated";
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly entryId: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SettledPromptRecord {
|
|
20
|
+
readonly type: "settled";
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly entryId: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RetiredPromptRecord {
|
|
25
|
+
readonly type: "retired";
|
|
26
|
+
readonly id: string;
|
|
27
|
+
}
|
|
28
|
+
export type PromptJournalRecord = SubmittedPromptRecord | CorrelatedPromptRecord | SettledPromptRecord | RetiredPromptRecord;
|
|
29
|
+
export interface PromptSubmission {
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly sessionFile: string | null;
|
|
32
|
+
readonly incarnation: number;
|
|
33
|
+
readonly fileExisted: boolean;
|
|
34
|
+
readonly kind: SubmittedPromptRecord["kind"];
|
|
35
|
+
readonly text: string;
|
|
36
|
+
readonly images?: readonly unknown[];
|
|
37
|
+
}
|
|
38
|
+
export interface SessionJournalEntry {
|
|
39
|
+
readonly entryId: string;
|
|
40
|
+
readonly text: string;
|
|
41
|
+
readonly settled: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface PendingPrompt {
|
|
44
|
+
readonly submitted: SubmittedPromptRecord;
|
|
45
|
+
readonly correlatedEntryId?: string;
|
|
46
|
+
}
|
|
47
|
+
export declare class PromptJournal {
|
|
48
|
+
#private;
|
|
49
|
+
readonly path: string;
|
|
50
|
+
constructor(path: string, options?: {
|
|
51
|
+
readonly faults?: ResidentTabFaultInjection;
|
|
52
|
+
readonly maximumBytes?: number;
|
|
53
|
+
});
|
|
54
|
+
initialize(): Promise<void>;
|
|
55
|
+
submit(submission: PromptSubmission): Promise<SubmittedPromptRecord>;
|
|
56
|
+
adopt(record: SubmittedPromptRecord): Promise<boolean>;
|
|
57
|
+
correlate(id: string, entryId: string): Promise<void>;
|
|
58
|
+
settle(id: string, entryId: string): Promise<void>;
|
|
59
|
+
retire(id: string): Promise<void>;
|
|
60
|
+
read(): Promise<readonly PromptJournalRecord[]>;
|
|
61
|
+
pending(sessionEntries?: readonly SessionJournalEntry[]): Promise<readonly PendingPrompt[]>;
|
|
62
|
+
compactIfRetired(): Promise<boolean>;
|
|
63
|
+
}
|
|
64
|
+
export declare function classifyMissingSession(record: SubmittedPromptRecord, sessionExists: boolean): "available" | "first-turn" | "missing-transcript";
|
|
65
|
+
export declare function removeJournalTemporary(path: string): Promise<void>;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { chmod, mkdir, open, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { ResidentTabFaultInjection } from "./fault-injection.js";
|
|
5
|
+
const MAX_JOURNAL_BYTES = 8 * 1024 * 1024;
|
|
6
|
+
const MAX_RECORD_BYTES = 2 * 1024 * 1024;
|
|
7
|
+
export class PromptJournal {
|
|
8
|
+
path;
|
|
9
|
+
#faults;
|
|
10
|
+
#maximumBytes;
|
|
11
|
+
#operation = Promise.resolve();
|
|
12
|
+
#retired = new Set();
|
|
13
|
+
constructor(path, options = {}) {
|
|
14
|
+
this.path = path;
|
|
15
|
+
this.#faults = options.faults ?? new ResidentTabFaultInjection();
|
|
16
|
+
this.#maximumBytes = options.maximumBytes ?? MAX_JOURNAL_BYTES;
|
|
17
|
+
}
|
|
18
|
+
async initialize() {
|
|
19
|
+
await secureDirectory(dirname(this.path));
|
|
20
|
+
const handle = await open(this.path, "a", 0o600);
|
|
21
|
+
await handle.close();
|
|
22
|
+
if (process.platform !== "win32")
|
|
23
|
+
await chmod(this.path, 0o600);
|
|
24
|
+
for (const record of await this.read())
|
|
25
|
+
if (record.type === "retired")
|
|
26
|
+
this.#retired.add(record.id);
|
|
27
|
+
}
|
|
28
|
+
async submit(submission) {
|
|
29
|
+
const record = {
|
|
30
|
+
type: "submitted",
|
|
31
|
+
id: randomUUID(),
|
|
32
|
+
...submission,
|
|
33
|
+
...(submission.images === undefined ? {} : { images: submission.images }),
|
|
34
|
+
at: new Date().toISOString(),
|
|
35
|
+
};
|
|
36
|
+
await this.#append(record, "journal-before-append", "journal-after-append", "journal-after-sync");
|
|
37
|
+
return record;
|
|
38
|
+
}
|
|
39
|
+
async adopt(record) {
|
|
40
|
+
if ((await this.read()).some(existing => existing.type === "submitted" && existing.id === record.id))
|
|
41
|
+
return false;
|
|
42
|
+
await this.#append(record, "journal-before-append", "journal-after-append", "journal-after-sync");
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
async correlate(id, entryId) {
|
|
46
|
+
await this.#append({ type: "correlated", id, entryId }, "journal-before-correlation", "journal-after-correlation");
|
|
47
|
+
}
|
|
48
|
+
async settle(id, entryId) {
|
|
49
|
+
await this.#append({ type: "settled", id, entryId }, "journal-before-settled-record", "journal-after-settled-record");
|
|
50
|
+
}
|
|
51
|
+
async retire(id) {
|
|
52
|
+
if (this.#retired.has(id))
|
|
53
|
+
return;
|
|
54
|
+
await this.#append({ type: "retired", id }, "journal-during-retirement");
|
|
55
|
+
this.#retired.add(id);
|
|
56
|
+
}
|
|
57
|
+
async read() {
|
|
58
|
+
let text;
|
|
59
|
+
try {
|
|
60
|
+
text = await readFile(this.path, "utf8");
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error.code === "ENOENT")
|
|
64
|
+
return [];
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
const records = [];
|
|
68
|
+
const lines = text.split("\n");
|
|
69
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
70
|
+
const line = lines[index];
|
|
71
|
+
if (!line)
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
const value = JSON.parse(line);
|
|
75
|
+
records.push(decodeRecord(value));
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (index === lines.length - 1 && !text.endsWith("\n"))
|
|
79
|
+
break;
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return records;
|
|
84
|
+
}
|
|
85
|
+
async pending(sessionEntries = []) {
|
|
86
|
+
const states = new Map();
|
|
87
|
+
const settled = new Set();
|
|
88
|
+
const retired = new Set();
|
|
89
|
+
for (const record of await this.read()) {
|
|
90
|
+
if (record.type === "submitted")
|
|
91
|
+
states.set(record.id, { submitted: record });
|
|
92
|
+
else if (record.type === "correlated") {
|
|
93
|
+
const state = states.get(record.id);
|
|
94
|
+
if (state !== undefined)
|
|
95
|
+
states.set(record.id, { ...state, correlatedEntryId: record.entryId });
|
|
96
|
+
}
|
|
97
|
+
else if (record.type === "settled")
|
|
98
|
+
settled.add(record.id);
|
|
99
|
+
else
|
|
100
|
+
retired.add(record.id);
|
|
101
|
+
}
|
|
102
|
+
const byId = new Map(sessionEntries.map(entry => [entry.entryId, entry]));
|
|
103
|
+
const pending = [];
|
|
104
|
+
const claimedEntries = new Set();
|
|
105
|
+
for (const [id, state] of states) {
|
|
106
|
+
if (retired.has(id) || settled.has(id)) {
|
|
107
|
+
await this.retire(id);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const correlated = state.correlatedEntryId === undefined ? undefined : byId.get(state.correlatedEntryId);
|
|
111
|
+
const textMatch = correlated ?? sessionEntries.find(entry => entry.text === state.submitted.text && !claimedEntries.has(entry.entryId));
|
|
112
|
+
if (textMatch !== undefined)
|
|
113
|
+
claimedEntries.add(textMatch.entryId);
|
|
114
|
+
if (textMatch?.settled === true)
|
|
115
|
+
await this.retire(id);
|
|
116
|
+
else
|
|
117
|
+
pending.push(state);
|
|
118
|
+
}
|
|
119
|
+
return pending;
|
|
120
|
+
}
|
|
121
|
+
async compactIfRetired() {
|
|
122
|
+
if ((await this.pending()).length !== 0)
|
|
123
|
+
return false;
|
|
124
|
+
await this.#serialize(async () => {
|
|
125
|
+
this.#faults.at("journal-during-compaction");
|
|
126
|
+
const temporary = `${this.path}.tmp-${process.pid}-${randomUUID()}`;
|
|
127
|
+
const handle = await open(temporary, "wx", 0o600);
|
|
128
|
+
try {
|
|
129
|
+
await handle.sync();
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await handle.close();
|
|
133
|
+
}
|
|
134
|
+
await rename(temporary, this.path);
|
|
135
|
+
await syncDirectory(dirname(this.path));
|
|
136
|
+
});
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
async #append(record, ...points) {
|
|
140
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
141
|
+
const bytes = Buffer.byteLength(line);
|
|
142
|
+
if (bytes > MAX_RECORD_BYTES)
|
|
143
|
+
throw new Error("resident prompt journal record exceeds its size limit");
|
|
144
|
+
await this.#serialize(async () => {
|
|
145
|
+
for (const point of points.slice(0, 1))
|
|
146
|
+
this.#faults.at(point);
|
|
147
|
+
const current = await stat(this.path).then(value => value.size, error => error.code === "ENOENT" ? 0 : Promise.reject(error));
|
|
148
|
+
if (current + bytes > this.#maximumBytes)
|
|
149
|
+
throw new Error("resident prompt journal is full");
|
|
150
|
+
const handle = await open(this.path, "a", 0o600);
|
|
151
|
+
try {
|
|
152
|
+
await handle.write(line, undefined, "utf8");
|
|
153
|
+
if (points[1] !== undefined)
|
|
154
|
+
this.#faults.at(points[1]);
|
|
155
|
+
await handle.sync();
|
|
156
|
+
if (points[2] !== undefined)
|
|
157
|
+
this.#faults.at(points[2]);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
await handle.close();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async #serialize(operation) {
|
|
165
|
+
const next = this.#operation.then(operation, operation);
|
|
166
|
+
this.#operation = next.catch(() => undefined);
|
|
167
|
+
await next;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export function classifyMissingSession(record, sessionExists) {
|
|
171
|
+
if (sessionExists)
|
|
172
|
+
return "available";
|
|
173
|
+
return record.fileExisted ? "missing-transcript" : "first-turn";
|
|
174
|
+
}
|
|
175
|
+
function decodeRecord(value) {
|
|
176
|
+
if (typeof value !== "object" || value === null)
|
|
177
|
+
throw new Error("invalid resident prompt journal record");
|
|
178
|
+
const record = value;
|
|
179
|
+
if (typeof record.id !== "string" || record.id.length === 0)
|
|
180
|
+
throw new Error("invalid resident prompt journal id");
|
|
181
|
+
if (record.type === "submitted") {
|
|
182
|
+
if (typeof record.sessionId !== "string" || !(typeof record.sessionFile === "string" || record.sessionFile === null)
|
|
183
|
+
|| typeof record.incarnation !== "number" || typeof record.fileExisted !== "boolean"
|
|
184
|
+
|| !["prompt", "steer", "follow-up"].includes(String(record.kind)) || typeof record.text !== "string" || typeof record.at !== "string") {
|
|
185
|
+
throw new Error("invalid submitted prompt journal record");
|
|
186
|
+
}
|
|
187
|
+
return record;
|
|
188
|
+
}
|
|
189
|
+
if ((record.type === "correlated" || record.type === "settled") && typeof record.entryId === "string")
|
|
190
|
+
return record;
|
|
191
|
+
if (record.type === "retired")
|
|
192
|
+
return record;
|
|
193
|
+
throw new Error("invalid resident prompt journal record type");
|
|
194
|
+
}
|
|
195
|
+
async function secureDirectory(path) {
|
|
196
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
197
|
+
if (process.platform !== "win32")
|
|
198
|
+
await chmod(path, 0o700);
|
|
199
|
+
}
|
|
200
|
+
async function syncDirectory(path) {
|
|
201
|
+
if (process.platform === "win32")
|
|
202
|
+
return;
|
|
203
|
+
const handle = await open(path, "r");
|
|
204
|
+
try {
|
|
205
|
+
await handle.sync();
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
await handle.close();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export async function removeJournalTemporary(path) {
|
|
212
|
+
await unlink(path).catch(error => { if (error.code !== "ENOENT")
|
|
213
|
+
throw error; });
|
|
214
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ResidentEnvelope<T = Readonly<Record<string, unknown>>> {
|
|
2
|
+
readonly type: string;
|
|
3
|
+
readonly requestId?: number;
|
|
4
|
+
readonly body: T;
|
|
5
|
+
}
|
|
6
|
+
/** Generation-1 MessagePack named-map codec. It intentionally supports only bridge-safe values. */
|
|
7
|
+
export declare function encodeResidentFrame(value: ResidentEnvelope): Buffer;
|
|
8
|
+
export declare function decodeResidentPayload(payload: Uint8Array): ResidentEnvelope;
|
|
9
|
+
export declare class ResidentFrameDecoder {
|
|
10
|
+
#private;
|
|
11
|
+
push(chunk: Uint8Array): readonly ResidentEnvelope[];
|
|
12
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const MAX_FRAME_BYTES = 2 * 1024 * 1024;
|
|
2
|
+
const MAX_DEPTH = 64;
|
|
3
|
+
/** Generation-1 MessagePack named-map codec. It intentionally supports only bridge-safe values. */
|
|
4
|
+
export function encodeResidentFrame(value) {
|
|
5
|
+
const payload = Buffer.from(encodeValue(value, 0));
|
|
6
|
+
if (payload.length > MAX_FRAME_BYTES)
|
|
7
|
+
throw new Error("resident frame exceeds 2 MiB");
|
|
8
|
+
const frame = Buffer.allocUnsafe(payload.length + 4);
|
|
9
|
+
frame.writeUInt32LE(payload.length, 0);
|
|
10
|
+
payload.copy(frame, 4);
|
|
11
|
+
return frame;
|
|
12
|
+
}
|
|
13
|
+
export function decodeResidentPayload(payload) {
|
|
14
|
+
if (payload.byteLength > MAX_FRAME_BYTES)
|
|
15
|
+
throw new Error("resident frame exceeds 2 MiB");
|
|
16
|
+
const state = { bytes: payload, offset: 0 };
|
|
17
|
+
const value = decodeValue(state, 0);
|
|
18
|
+
if (state.offset !== payload.byteLength || !isRecord(value) || typeof value.type !== "string" || !isRecord(value.body)) {
|
|
19
|
+
throw new Error("invalid resident envelope");
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
export class ResidentFrameDecoder {
|
|
24
|
+
#buffer = Buffer.alloc(0);
|
|
25
|
+
push(chunk) {
|
|
26
|
+
this.#buffer = Buffer.concat([this.#buffer, chunk]);
|
|
27
|
+
const messages = [];
|
|
28
|
+
while (this.#buffer.length >= 4) {
|
|
29
|
+
const length = this.#buffer.readUInt32LE(0);
|
|
30
|
+
if (length > MAX_FRAME_BYTES)
|
|
31
|
+
throw new Error("resident frame exceeds 2 MiB");
|
|
32
|
+
if (this.#buffer.length < length + 4)
|
|
33
|
+
break;
|
|
34
|
+
messages.push(decodeResidentPayload(this.#buffer.subarray(4, length + 4)));
|
|
35
|
+
this.#buffer = this.#buffer.subarray(length + 4);
|
|
36
|
+
}
|
|
37
|
+
return messages;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function encodeValue(value, depth) {
|
|
41
|
+
if (depth > MAX_DEPTH)
|
|
42
|
+
throw new Error("resident value exceeds nesting limit");
|
|
43
|
+
if (value === null)
|
|
44
|
+
return [0xc0];
|
|
45
|
+
if (value === false)
|
|
46
|
+
return [0xc2];
|
|
47
|
+
if (value === true)
|
|
48
|
+
return [0xc3];
|
|
49
|
+
if (typeof value === "number" && Number.isSafeInteger(value))
|
|
50
|
+
return encodeInteger(value);
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
const bytes = [...Buffer.from(value, "utf8")];
|
|
53
|
+
if (bytes.length < 32)
|
|
54
|
+
return [0xa0 | bytes.length, ...bytes];
|
|
55
|
+
if (bytes.length <= 0xff)
|
|
56
|
+
return [0xd9, bytes.length, ...bytes];
|
|
57
|
+
if (bytes.length <= 0xffff)
|
|
58
|
+
return [0xda, bytes.length >>> 8, bytes.length & 0xff, ...bytes];
|
|
59
|
+
return [0xdb, ...u32(bytes.length), ...bytes];
|
|
60
|
+
}
|
|
61
|
+
if (value instanceof Uint8Array) {
|
|
62
|
+
const bytes = [...value];
|
|
63
|
+
if (bytes.length <= 0xff)
|
|
64
|
+
return [0xc4, bytes.length, ...bytes];
|
|
65
|
+
if (bytes.length <= 0xffff)
|
|
66
|
+
return [0xc5, bytes.length >>> 8, bytes.length & 0xff, ...bytes];
|
|
67
|
+
return [0xc6, ...u32(bytes.length), ...bytes];
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
const head = value.length < 16 ? [0x90 | value.length] : value.length <= 0xffff ? [0xdc, value.length >>> 8, value.length & 0xff] : [0xdd, ...u32(value.length)];
|
|
71
|
+
return [...head, ...value.flatMap(entry => encodeValue(entry, depth + 1))];
|
|
72
|
+
}
|
|
73
|
+
if (isRecord(value)) {
|
|
74
|
+
const entries = Object.entries(value).filter(([, child]) => child !== undefined);
|
|
75
|
+
const head = entries.length < 16 ? [0x80 | entries.length] : entries.length <= 0xffff ? [0xde, entries.length >>> 8, entries.length & 0xff] : [0xdf, ...u32(entries.length)];
|
|
76
|
+
return [...head, ...entries.flatMap(([key, child]) => [...encodeValue(key, depth + 1), ...encodeValue(child, depth + 1)])];
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`unsupported resident value: ${typeof value}`);
|
|
79
|
+
}
|
|
80
|
+
function encodeInteger(value) {
|
|
81
|
+
if (value >= 0 && value <= 0x7f)
|
|
82
|
+
return [value];
|
|
83
|
+
if (value < 0 && value >= -32)
|
|
84
|
+
return [0x100 + value];
|
|
85
|
+
if (value >= 0 && value <= 0xff)
|
|
86
|
+
return [0xcc, value];
|
|
87
|
+
if (value >= 0 && value <= 0xffff)
|
|
88
|
+
return [0xcd, value >>> 8, value & 0xff];
|
|
89
|
+
if (value >= -0x80 && value < 0)
|
|
90
|
+
return [0xd0, value & 0xff];
|
|
91
|
+
if (value >= -0x8000 && value < 0)
|
|
92
|
+
return [0xd1, (value >>> 8) & 0xff, value & 0xff];
|
|
93
|
+
if (value >= 0 && value <= 0xffffffff)
|
|
94
|
+
return [0xce, ...u32(value)];
|
|
95
|
+
return [0xd3, ...i64(value)];
|
|
96
|
+
}
|
|
97
|
+
function decodeValue(state, depth) {
|
|
98
|
+
if (depth > MAX_DEPTH)
|
|
99
|
+
throw new Error("resident value exceeds nesting limit");
|
|
100
|
+
const marker = byte(state);
|
|
101
|
+
if (marker <= 0x7f)
|
|
102
|
+
return marker;
|
|
103
|
+
if (marker >= 0xe0)
|
|
104
|
+
return marker - 0x100;
|
|
105
|
+
if ((marker & 0xe0) === 0xa0)
|
|
106
|
+
return string(state, marker & 0x1f);
|
|
107
|
+
if ((marker & 0xf0) === 0x90)
|
|
108
|
+
return array(state, marker & 0x0f, depth);
|
|
109
|
+
if ((marker & 0xf0) === 0x80)
|
|
110
|
+
return map(state, marker & 0x0f, depth);
|
|
111
|
+
switch (marker) {
|
|
112
|
+
case 0xc0: return null;
|
|
113
|
+
case 0xc2: return false;
|
|
114
|
+
case 0xc3: return true;
|
|
115
|
+
case 0xc4: return binary(state, uint(state, 1));
|
|
116
|
+
case 0xc5: return binary(state, uint(state, 2));
|
|
117
|
+
case 0xc6: return binary(state, uint(state, 4));
|
|
118
|
+
case 0xcc: return uint(state, 1);
|
|
119
|
+
case 0xcd: return uint(state, 2);
|
|
120
|
+
case 0xce: return uint(state, 4);
|
|
121
|
+
case 0xcf: return safeInteger(state, false);
|
|
122
|
+
case 0xd0: return signed(state, 1);
|
|
123
|
+
case 0xd1: return signed(state, 2);
|
|
124
|
+
case 0xd2: return signed(state, 4);
|
|
125
|
+
case 0xd3: return safeInteger(state, true);
|
|
126
|
+
case 0xd9: return string(state, uint(state, 1));
|
|
127
|
+
case 0xda: return string(state, uint(state, 2));
|
|
128
|
+
case 0xdb: return string(state, uint(state, 4));
|
|
129
|
+
case 0xdc: return array(state, uint(state, 2), depth);
|
|
130
|
+
case 0xdd: return array(state, uint(state, 4), depth);
|
|
131
|
+
case 0xde: return map(state, uint(state, 2), depth);
|
|
132
|
+
case 0xdf: return map(state, uint(state, 4), depth);
|
|
133
|
+
default: throw new Error(`unsupported resident MessagePack marker 0x${marker.toString(16)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function byte(state) {
|
|
137
|
+
const value = state.bytes[state.offset++];
|
|
138
|
+
if (value === undefined)
|
|
139
|
+
throw new Error("incomplete resident value");
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
function uint(state, width) {
|
|
143
|
+
let value = 0;
|
|
144
|
+
for (let index = 0; index < width; index++)
|
|
145
|
+
value = value * 256 + byte(state);
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
function signed(state, width) {
|
|
149
|
+
const value = uint(state, width);
|
|
150
|
+
return value >= 2 ** (width * 8 - 1) ? value - 2 ** (width * 8) : value;
|
|
151
|
+
}
|
|
152
|
+
function safeInteger(state, signedValue) {
|
|
153
|
+
const high = uint(state, 4);
|
|
154
|
+
const low = uint(state, 4);
|
|
155
|
+
const value = high * 0x1_0000_0000 + low;
|
|
156
|
+
const resolved = signedValue && high >= 0x80000000 ? value - 0x1_0000_0000_0000_0000 : value;
|
|
157
|
+
if (!Number.isSafeInteger(resolved))
|
|
158
|
+
throw new Error("resident integer exceeds JavaScript safe range");
|
|
159
|
+
return resolved;
|
|
160
|
+
}
|
|
161
|
+
function string(state, length) {
|
|
162
|
+
return Buffer.from(binary(state, length)).toString("utf8");
|
|
163
|
+
}
|
|
164
|
+
function binary(state, length) {
|
|
165
|
+
const end = state.offset + length;
|
|
166
|
+
if (end > state.bytes.byteLength)
|
|
167
|
+
throw new Error("incomplete resident value");
|
|
168
|
+
const value = state.bytes.slice(state.offset, end);
|
|
169
|
+
state.offset = end;
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function array(state, length, depth) {
|
|
173
|
+
if (length > 65_536)
|
|
174
|
+
throw new Error("resident collection exceeds limit");
|
|
175
|
+
return Array.from({ length }, () => decodeValue(state, depth + 1));
|
|
176
|
+
}
|
|
177
|
+
function map(state, length, depth) {
|
|
178
|
+
if (length > 65_536)
|
|
179
|
+
throw new Error("resident collection exceeds limit");
|
|
180
|
+
const value = {};
|
|
181
|
+
for (let index = 0; index < length; index++) {
|
|
182
|
+
const key = decodeValue(state, depth + 1);
|
|
183
|
+
if (typeof key !== "string" || Object.hasOwn(value, key))
|
|
184
|
+
throw new Error("invalid resident map key");
|
|
185
|
+
value[key] = decodeValue(state, depth + 1);
|
|
186
|
+
}
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
function u32(value) { return [(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff]; }
|
|
190
|
+
function i64(value) {
|
|
191
|
+
const integer = BigInt(value);
|
|
192
|
+
const encoded = integer < 0 ? (1n << 64n) + integer : integer;
|
|
193
|
+
return Array.from({ length: 8 }, (_, index) => Number((encoded >> BigInt((7 - index) * 8)) & 0xffn));
|
|
194
|
+
}
|
|
195
|
+
function isRecord(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ResidentTabFaultInjection } from "./fault-injection.js";
|
|
2
|
+
import { type PromptSubmission, type SessionJournalEntry, type SubmittedPromptRecord } from "./prompt-journal.js";
|
|
3
|
+
export interface ResidentTabRecoveredInput {
|
|
4
|
+
readonly prompts: readonly SubmittedPromptRecord[];
|
|
5
|
+
readonly draft: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface ResidentTabAdmission extends Omit<PromptSubmission, "fileExisted" | "incarnation"> {
|
|
8
|
+
readonly incarnation: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ResidentTabRecovery {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(dataDir: string, options?: {
|
|
13
|
+
readonly faults?: ResidentTabFaultInjection;
|
|
14
|
+
readonly onDegraded?: (degraded: boolean) => void;
|
|
15
|
+
});
|
|
16
|
+
get recovered(): ResidentTabRecoveredInput;
|
|
17
|
+
initialize(currentSessionFile?: string | null, currentSessionId?: string | null): Promise<ResidentTabRecoveredInput>;
|
|
18
|
+
admit(input: ResidentTabAdmission, timeoutMs?: number): Promise<SubmittedPromptRecord>;
|
|
19
|
+
dispatched(): void;
|
|
20
|
+
correlated(submissionId: string, entryId: string): Promise<void>;
|
|
21
|
+
checkpoint(text: string): void;
|
|
22
|
+
settled(sessionFile: string | null): Promise<void>;
|
|
23
|
+
gracefulStop(sessionFile: string | null): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** Reconcile only durable user/assistant ancestry; a torn final JSONL record is not authority. */
|
|
26
|
+
export declare function readSessionJournal(path: string): Promise<readonly SessionJournalEntry[]>;
|