Merge remote-tracking branch 'origin/master' into session-query-tool
# Conflicts: # docs/architecture.i18n.yaml # docs/capability-seams.md # examples/acp-agent/composition.md # examples/acp-agent/cordis.yml # examples/acp-agent/tests/snapshots/model-switching/system-prompt.expected.md # examples/acp-agent/tests/snapshots/model-switching/tool-schemas.expected.json # examples/acp-agent/tests/snapshots/permission-switching/system-prompt.expected.md # examples/acp-agent/tests/snapshots/permission-switching/tool-schemas.expected.json # examples/acp-agent/tests/snapshots/plan-mode/system-prompt.expected.md # examples/acp-agent/tests/snapshots/plan-mode/tool-schemas.expected.json # packages/examples/acp-demo/README.md # packages/host/runtime/README.md # packages/support/acp-snapshot/src/normalize.ts # packages/support/acp-snapshot/tests/normalize.spec.ts # packages/ui/acp/tests/harness.ts # scripts/type-equiv.manifest.json # tsconfig.host.json
This commit is contained in:
1058 files changed
+29661
-25180
No files matched your search
@@ -17,7 +17,10 @@ import type { SessionEvent, SessionId, SessionHeader, SurfaceOp } from '@deepsee
|
||||
* layout; orthogonal to a session's own `version` (which versions the EVENT
|
||||
* vocabulary, stored per session in the `sessions` row).
|
||||
*/
|
||||
export const SCHEMA_VERSION = 8
|
||||
export const SCHEMA_VERSION = 10
|
||||
|
||||
/** SQLite application id protecting unrelated databases from persistence writes. */
|
||||
export const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 0x44534850
|
||||
|
||||
/**
|
||||
* A row of the `sessions` table — the out-of-log metadata ({@link SessionHeader}).
|
||||
@@ -63,9 +66,10 @@ export interface EventRow {
|
||||
export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist'
|
||||
|
||||
/**
|
||||
* Open the database and apply its schema and pragmas. A zero `user_version` is
|
||||
* stamped with {@link SCHEMA_VERSION}; every other non-current version rejects
|
||||
* rather than being migrated in place.
|
||||
* Open the database and apply its schema and pragmas. An empty database with a
|
||||
* zero `user_version` is initialized at {@link SCHEMA_VERSION}; a nonempty
|
||||
* unversioned database and every other non-current version reject rather than
|
||||
* being migrated in place.
|
||||
* @param path - the SQLite database file to open (created when absent).
|
||||
* @param journalMode - validated journal pragma.
|
||||
* @returns the open handle with pragmas applied and all three tables ensured.
|
||||
@@ -83,51 +87,81 @@ export function openDatabase(path: string, journalMode: JournalMode): DatabaseSy
|
||||
|
||||
function configureDatabase(db: DatabaseSync, path: string, journalMode: JournalMode): void {
|
||||
db.exec('PRAGMA foreign_keys = ON')
|
||||
let began = false
|
||||
try {
|
||||
db.exec('BEGIN IMMEDIATE')
|
||||
began = true
|
||||
// Validate while holding the write lock so no other connection can change
|
||||
// schema ownership between inspection and initialization.
|
||||
const { user_version: onDisk } = db.prepare('PRAGMA user_version').get() as { user_version: number }
|
||||
const { application_id: applicationId } = db.prepare('PRAGMA application_id').get() as { application_id: number }
|
||||
const { count: userObjectCount } = db.prepare(
|
||||
"SELECT COUNT(*) AS count FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*'",
|
||||
).get() as { count: number }
|
||||
if (onDisk === 0 && (applicationId !== 0 || userObjectCount > 0)) {
|
||||
throw new Error(`session database at "${path}" has an unversioned schema or application identity`)
|
||||
}
|
||||
if (onDisk !== 0 && onDisk !== SCHEMA_VERSION) {
|
||||
throw new Error(`session database at "${path}" has schema version ${onDisk}, incompatible with this build (${SCHEMA_VERSION})`)
|
||||
}
|
||||
if (onDisk === SCHEMA_VERSION && applicationId !== SESSION_PERSISTENCE_SQLITE_APPLICATION_ID) {
|
||||
throw new Error(
|
||||
`session database at "${path}" has application id ${applicationId}, expected ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`,
|
||||
)
|
||||
}
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS persistence_state (
|
||||
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
||||
store_id TEXT NOT NULL
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
version INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
cwd TEXT,
|
||||
parent_session TEXT,
|
||||
seed_length INTEGER,
|
||||
delegation_depth INTEGER,
|
||||
incarnation TEXT NOT NULL,
|
||||
revision INTEGER NOT NULL
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
||||
seq INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
time INTEGER NOT NULL,
|
||||
data TEXT NOT NULL,
|
||||
source_event_seqs TEXT,
|
||||
surface_op TEXT,
|
||||
PRIMARY KEY (session_id, seq)
|
||||
) STRICT
|
||||
`)
|
||||
db.prepare(
|
||||
'INSERT OR IGNORE INTO persistence_state (singleton, store_id) VALUES (1, ?)',
|
||||
).run(randomUUID())
|
||||
if (onDisk === 0) {
|
||||
db.exec(`PRAGMA application_id = ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`)
|
||||
db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`)
|
||||
}
|
||||
db.exec('COMMIT')
|
||||
began = false
|
||||
} catch (error: unknown) {
|
||||
/* v8 ignore next -- a BEGIN failure leaves no transaction to roll back. */
|
||||
if (began) {
|
||||
/* v8 ignore next 5 -- preserve the original schema failure if SQLite also refuses rollback. */
|
||||
try {
|
||||
db.exec('ROLLBACK')
|
||||
} catch {
|
||||
// The original SQLite failure remains the actionable cause.
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
// The validated union is safe to interpolate into a non-bindable PRAGMA.
|
||||
// Apply it only after ownership validation and initialization commit.
|
||||
db.exec(`PRAGMA journal_mode = ${journalMode.toUpperCase()}`)
|
||||
// `PRAGMA user_version` always returns exactly one row { user_version }.
|
||||
const { user_version: onDisk } = db.prepare('PRAGMA user_version').get() as { user_version: number }
|
||||
if (onDisk !== 0 && onDisk !== SCHEMA_VERSION) {
|
||||
throw new Error(`session database at "${path}" has schema version ${onDisk}, incompatible with this build (${SCHEMA_VERSION})`)
|
||||
}
|
||||
if (onDisk === 0) {
|
||||
// Stamp fresh or pre-versioning databases.
|
||||
db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`)
|
||||
}
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS persistence_state (
|
||||
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
||||
store_id TEXT NOT NULL
|
||||
) STRICT
|
||||
`)
|
||||
db.prepare(
|
||||
'INSERT OR IGNORE INTO persistence_state (singleton, store_id) VALUES (1, ?)',
|
||||
).run(randomUUID())
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
version INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
cwd TEXT,
|
||||
parent_session TEXT,
|
||||
seed_length INTEGER,
|
||||
delegation_depth INTEGER,
|
||||
incarnation TEXT NOT NULL,
|
||||
revision INTEGER NOT NULL
|
||||
) STRICT
|
||||
`)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
||||
seq INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
time INTEGER NOT NULL,
|
||||
data TEXT NOT NULL,
|
||||
source_event_seqs TEXT,
|
||||
surface_op TEXT,
|
||||
PRIMARY KEY (session_id, seq)
|
||||
) STRICT
|
||||
`)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +170,9 @@ function configureDatabase(db: DatabaseSync, path: string, journalMode: JournalM
|
||||
* @returns the header, `NULL` columns mapped to omitted optional fields.
|
||||
*/
|
||||
export function rowToMeta(row: SessionRow): SessionHeader {
|
||||
if (!Number.isSafeInteger(row.created_at) || row.created_at < 0) {
|
||||
throw new Error('stored session createdAt must be a non-negative safe integer')
|
||||
}
|
||||
return {
|
||||
version: row.version,
|
||||
id: row.id as SessionId,
|
||||
|
||||
Reference in New Issue
Block a user