@saadnvd1/agent-os 0.3.596 → 0.3.600
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/app/api/bus/spawn/route.ts +35 -16
- package/app/api/lumifyhub/cards/[cardId]/run/route.ts +3 -0
- package/app/api/tasks/route.ts +3 -0
- package/app/page.tsx +2 -1
- package/components/SessionList/SessionRow.tsx +2 -1
- package/lib/agents/spawn.ts +16 -4
- package/lib/chat/refresh.ts +2 -1
- package/lib/chat/runner.ts +29 -0
- package/lib/chat/store.ts +53 -0
- package/lib/db/migration-types.ts +15 -0
- package/lib/db/migrations/0001_add_group_path_to_sessions.ts +11 -0
- package/lib/db/migrations/0002_add_agent_type_to_sessions.ts +11 -0
- package/lib/db/migrations/0003_add_worktree_columns_to_sessions.ts +12 -0
- package/lib/db/migrations/0004_add_pr_tracking_to_sessions.ts +11 -0
- package/lib/db/migrations/0005_add_group_path_index.ts +11 -0
- package/lib/db/migrations/0006_add_orchestration_columns_to_sessions.ts +16 -0
- package/lib/db/migrations/0007_add_auto_approve_to_sessions.ts +11 -0
- package/lib/db/migrations/0008_add_dev_server_columns.ts +19 -0
- package/lib/db/migrations/0009_add_project_id_to_sessions.ts +17 -0
- package/lib/db/migrations/0010_add_project_id_to_dev_servers.ts +36 -0
- package/lib/db/migrations/0011_add_tmux_name_to_sessions.ts +13 -0
- package/lib/db/migrations/0012_add_initial_prompt_to_projects.ts +9 -0
- package/lib/db/migrations/0013_add_project_repositories_table.ts +22 -0
- package/lib/db/migrations/0014_add_hosts.ts +23 -0
- package/lib/db/migrations/0015_add_workspaces.ts +18 -0
- package/lib/db/migrations/0016_add_task_columns_to_sessions.ts +13 -0
- package/lib/db/migrations/0017_add_bus_messages.ts +24 -0
- package/lib/db/migrations/0018_add_chat.ts +23 -0
- package/lib/db/migrations/0019_add_lumifyhub.ts +36 -0
- package/lib/db/migrations/0020_add_lumifyhub_published_docs.ts +18 -0
- package/lib/db/migrations/0021_add_chat_access.ts +14 -0
- package/lib/db/migrations/0022_add_last_seen.ts +10 -0
- package/lib/db/migrations/0023_mark_existing_sessions_seen.ts +12 -0
- package/lib/db/migrations/0024_add_stacks.ts +49 -0
- package/lib/db/migrations/0025_add_stack_item_attempts.ts +16 -0
- package/lib/db/migrations/0026_add_workspace_orchestrator.ts +47 -0
- package/lib/db/migrations/0027_add_devices_and_settings.ts +28 -0
- package/lib/db/migrations/0028_add_orchestrator_acting.ts +70 -0
- package/lib/db/migrations/0029_add_orchestrator_asks_and_pause.ts +81 -0
- package/lib/db/migrations/0030_add_sessions_archived_at.ts +14 -0
- package/lib/db/migrations/0031_add_stack_item_restacked_heads.ts +22 -0
- package/lib/db/migrations/0032_add_sessions_pinned.ts +12 -0
- package/lib/db/migrations/0033_add_program_status.ts +18 -0
- package/lib/db/migrations/0034_add_session_names.ts +21 -0
- package/lib/db/migrations/0035_add_artifacts.ts +23 -0
- package/lib/db/migrations/0036_add_chat_queue.ts +26 -0
- package/lib/db/migrations/0037_add_chat_plan_and_turns.ts +39 -0
- package/lib/db/migrations/0038_add_schedules.ts +56 -0
- package/lib/db/migrations/0039_add_session_name_source.ts +21 -0
- package/lib/db/migrations/0040_add_schedule_targets_and_phone_notify.ts +41 -0
- package/lib/db/migrations/0041_add_task_setup.ts +17 -0
- package/lib/db/migrations/0042_add_host_links_and_moved_tasks.ts +29 -0
- package/lib/db/migrations/0043_scratch_project.ts +14 -0
- package/lib/db/migrations/0044_chat_item_kind.ts +21 -0
- package/lib/db/migrations/0045_change_versions.ts +13 -0
- package/lib/db/migrations/0046_session_port_slots.ts +21 -0
- package/lib/db/migrations/0047_pin_orchestrators.ts +12 -0
- package/lib/db/migrations/0048_session_database.ts +15 -0
- package/lib/db/migrations/0049_peer_mirror.ts +14 -0
- package/lib/db/migrations/0050_task_queue.ts +44 -0
- package/lib/db/migrations/0051_chat_queue_sent_by.ts +16 -0
- package/lib/db/migrations/0052_sessions_chat_restarted.ts +17 -0
- package/lib/db/migrations/0053_brief_amendments_failure_sha_ask_raised_by.ts +37 -0
- package/lib/db/migrations/0054_sessions_chat_tools_digest.ts +15 -0
- package/lib/db/migrations/0055_sessions_merge_armed_and_rebase.ts +25 -0
- package/lib/db/migrations/0056_orchestrator_rules.ts +39 -0
- package/lib/db/migrations/0057_task_pr_wakes.ts +34 -0
- package/lib/db/migrations/0058_away_mode.ts +71 -0
- package/lib/db/migrations/0059_gate_failure_commits.ts +33 -0
- package/lib/db/migrations/0060_check_runs_and_compactions.ts +44 -0
- package/lib/db/migrations/0061_model_routing.ts +30 -0
- package/lib/db/migrations/0062_sessions_quick_of.ts +19 -0
- package/lib/db/migrations/0063_one_off_schedules.ts +16 -0
- package/lib/db/migrations/0064_chat_compactions_phase.ts +15 -0
- package/lib/db/migrations/0065_orchestrator_triage.ts +23 -0
- package/lib/db/migrations/0066_workspace_orch_mcp_servers.ts +16 -0
- package/lib/db/migrations/0067_orchestrator_event_log_suppressed.ts +14 -0
- package/lib/db/migrations/0068_usage_calls_and_rollups.ts +74 -0
- package/lib/db/migrations/0069_session_logs.ts +26 -0
- package/lib/db/migrations/0070_chat_items_running.ts +15 -0
- package/lib/db/migrations/0071_devices_push.ts +29 -0
- package/lib/db/migrations/0072_push_outbox.ts +33 -0
- package/lib/db/migrations/0073_orchestrated_task_moves.ts +27 -0
- package/lib/db/migrations/0074_held_task_replies.ts +27 -0
- package/lib/db/migrations/0075_plan_only_tasks.ts +17 -0
- package/lib/db/migrations/0076_welcome_backfill.ts +16 -0
- package/lib/db/migrations/0077_simulator_leases.ts +21 -0
- package/lib/db/migrations/0078_orchestrator_questions.ts +34 -0
- package/lib/db/migrations/0079_task_queue_agent.ts +13 -0
- package/lib/db/migrations/0080_recipes.ts +129 -0
- package/lib/db/migrations/0081_schedules_into_recipes.ts +18 -0
- package/lib/db/migrations/0082_session_shelf_times.ts +23 -0
- package/lib/db/migrations/0083_native_devices_can_approve.ts +18 -0
- package/lib/db/migrations/0084_approval_audit.ts +20 -0
- package/lib/db/migrations/0085_experiment_usage.ts +35 -0
- package/lib/db/migrations/0086_learning_cards.ts +28 -0
- package/lib/db/migrations/0087_plan_pieces_reconcile.ts +29 -0
- package/lib/db/migrations/0088_shared_links.ts +25 -0
- package/lib/db/migrations/0089_passkeys_device_keys.ts +23 -0
- package/lib/db/migrations/20261014T0900_task_scheduling.ts +57 -0
- package/lib/db/migrations/20261014T1235_placed_mirror_started_by.ts +19 -0
- package/lib/db/migrations/20261014T1600_queued_task_messages.ts +21 -0
- package/lib/db/migrations/20261014T1800_task_starter_device.ts +20 -0
- package/lib/db/migrations/20261014T1900_pause_note.ts +13 -0
- package/lib/db/migrations/20261014T2000_chat_bookmarks.ts +33 -0
- package/lib/db/migrations/20261014T2100_gate_failure_ask_held.ts +18 -0
- package/lib/db/migrations/20261014T2110_task_start_record.ts +21 -0
- package/lib/db/migrations/20261014T2200_end_orphaned_tasks.ts +23 -0
- package/lib/db/migrations/README.md +35 -0
- package/lib/db/migrations-legacy-checksums.json +9 -8
- package/lib/db/migrations-split-snapshot.json +1759 -0
- package/lib/db/migrations.ts +76 -2337
- package/lib/db/types.ts +2 -0
- package/lib/learning-cards/apply.ts +7 -1
- package/lib/models/plan-step.ts +25 -8
- package/lib/models/resolve.ts +1 -1
- package/lib/models/resume.ts +1 -1
- package/lib/models/roles.ts +0 -16
- package/lib/models/session-role.ts +19 -0
- package/lib/orchestrator/act.ts +22 -7
- package/lib/orchestrator/amendments.ts +10 -3
- package/lib/orchestrator/serve.ts +1 -0
- package/lib/orchestrator/testing.ts +8 -0
- package/lib/orchestrator/tool-schemas.ts +9 -0
- package/lib/orchestrator/tools.ts +1 -1
- package/lib/plans/claim.ts +131 -0
- package/lib/plans/release.ts +5 -2
- package/lib/recipes/run.ts +6 -0
- package/lib/schedules/start.ts +1 -0
- package/lib/sessions/fork.ts +10 -0
- package/lib/sessions/launch.ts +5 -1
- package/lib/stacks/start.ts +1 -0
- package/lib/tasks/held-remote.ts +4 -2
- package/lib/tasks/held-replies.ts +5 -2
- package/lib/tasks/held.ts +2 -1
- package/lib/tasks/index.ts +85 -46
- package/lib/tasks/move-carry.ts +12 -1
- package/lib/tasks/move-guard.ts +2 -1
- package/lib/tasks/move.ts +7 -5
- package/lib/tasks/pause.ts +1 -1
- package/lib/tasks/queue.ts +54 -31
- package/lib/tasks/remote.ts +3 -1
- package/lib/tasks/starter.ts +124 -0
- package/lib/usage/calls.ts +2 -1
- package/lib/usage/turns.ts +2 -1
- package/package.json +6 -3
- package/scripts/check +1 -0
- package/scripts/gen-migrations-index.mjs +44 -0
- package/scripts/screenshots/demo.ts +5 -1
- package/scripts/screenshots/run.ts +5 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 39,
|
|
5
|
+
name: "add_session_name_source",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Who named a session: "user" (typed or given explicitly, never
|
|
8
|
+
// renamed for them), "generated" (a title picked from its prompt) or
|
|
9
|
+
// "default" ("Session 4", or a placeholder until a title arrives).
|
|
10
|
+
// One transaction, so a crash part-way leaves nothing to skip.
|
|
11
|
+
db.transaction(() => {
|
|
12
|
+
db.exec(
|
|
13
|
+
`ALTER TABLE sessions ADD COLUMN name_source TEXT NOT NULL DEFAULT 'default'`
|
|
14
|
+
);
|
|
15
|
+
// A session renamed by hand before this keeps its name.
|
|
16
|
+
db.exec(
|
|
17
|
+
`UPDATE sessions SET name_source = 'user' WHERE id IN (SELECT session_id FROM session_names)`
|
|
18
|
+
);
|
|
19
|
+
})();
|
|
20
|
+
},
|
|
21
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 40,
|
|
5
|
+
name: "add_schedule_targets_and_phone_notify",
|
|
6
|
+
// One transaction, so a crash part-way leaves nothing to skip as
|
|
7
|
+
// "duplicate column" on the next start.
|
|
8
|
+
up: (db) =>
|
|
9
|
+
db.transaction(() => {
|
|
10
|
+
// Where phone notifications go. One row; the bot token is never
|
|
11
|
+
// returned by any route or written to a log.
|
|
12
|
+
db.exec(`
|
|
13
|
+
CREATE TABLE IF NOT EXISTS notify_settings (
|
|
14
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
15
|
+
telegram_token TEXT,
|
|
16
|
+
telegram_chat_id TEXT,
|
|
17
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
18
|
+
)
|
|
19
|
+
`);
|
|
20
|
+
// Every phone notification, sent or waiting out its source's
|
|
21
|
+
// minute, so one held across a restart still goes.
|
|
22
|
+
db.exec(`
|
|
23
|
+
CREATE TABLE IF NOT EXISTS notify_outbox (
|
|
24
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
|
+
source TEXT NOT NULL,
|
|
26
|
+
text TEXT NOT NULL,
|
|
27
|
+
created_at INTEGER NOT NULL,
|
|
28
|
+
send_after INTEGER NOT NULL,
|
|
29
|
+
sent_at INTEGER,
|
|
30
|
+
error TEXT
|
|
31
|
+
)
|
|
32
|
+
`);
|
|
33
|
+
db.exec(
|
|
34
|
+
`CREATE INDEX IF NOT EXISTS idx_notify_outbox_source ON notify_outbox(source, sent_at)`
|
|
35
|
+
);
|
|
36
|
+
// A "message" schedule's session, by id: names change, ids don't.
|
|
37
|
+
// And the agent session that made a schedule, when one did.
|
|
38
|
+
db.exec(`ALTER TABLE schedules ADD COLUMN target_session_id TEXT`);
|
|
39
|
+
db.exec(`ALTER TABLE schedules ADD COLUMN created_by_session_id TEXT`);
|
|
40
|
+
})(),
|
|
41
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 41,
|
|
5
|
+
name: "add_task_setup",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// How a task's worktree setup (deps, setup commands) went before its
|
|
8
|
+
// agent started: running, ok or failed, how long, and what failed;
|
|
9
|
+
// and the brief its launch needs, so a restart can resume it.
|
|
10
|
+
db.transaction(() => {
|
|
11
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN setup_status TEXT`);
|
|
12
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN setup_ms INTEGER`);
|
|
13
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN setup_error TEXT`);
|
|
14
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN task_brief TEXT`);
|
|
15
|
+
})();
|
|
16
|
+
},
|
|
17
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 42,
|
|
5
|
+
name: "add_host_links_and_moved_tasks",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// How this machine reaches another machine's own AgentOS: its URL and
|
|
8
|
+
// the device token it paired with. Apart from hosts so the token never
|
|
9
|
+
// rides along with a host row to the browser.
|
|
10
|
+
db.transaction(() => {
|
|
11
|
+
db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS host_links (
|
|
13
|
+
host_id TEXT PRIMARY KEY REFERENCES hosts(id) ON DELETE CASCADE,
|
|
14
|
+
url TEXT NOT NULL,
|
|
15
|
+
token TEXT NOT NULL,
|
|
16
|
+
linked_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
17
|
+
)
|
|
18
|
+
`);
|
|
19
|
+
// A task moved to another machine keeps its row, marked where it
|
|
20
|
+
// went; one that arrived names the session it came from, so the
|
|
21
|
+
// same move imported twice finds the first.
|
|
22
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN moved_to TEXT`);
|
|
23
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN moved_from TEXT`);
|
|
24
|
+
db.exec(
|
|
25
|
+
`CREATE INDEX IF NOT EXISTS idx_sessions_moved_from ON sessions(moved_from)`
|
|
26
|
+
);
|
|
27
|
+
})();
|
|
28
|
+
},
|
|
29
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 43,
|
|
5
|
+
name: "scratch_project",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Chats with no project work in a scratch folder rather than the home
|
|
8
|
+
// directory. Sessions already made keep their own folders.
|
|
9
|
+
db.prepare(
|
|
10
|
+
`UPDATE projects SET name = 'Scratch', working_directory = '~/.agent-os/scratch'
|
|
11
|
+
WHERE is_uncategorized = 1 AND name = 'Uncategorized' AND working_directory = '~'`
|
|
12
|
+
).run();
|
|
13
|
+
},
|
|
14
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 44,
|
|
5
|
+
name: "chat_item_kind",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Items of one kind (a chat's background tasks, its sent messages)
|
|
8
|
+
// read from an index, not by parsing every row of a long conversation.
|
|
9
|
+
const columns = db.prepare(`PRAGMA table_xinfo(chat_items)`).all() as {
|
|
10
|
+
name: string;
|
|
11
|
+
}[];
|
|
12
|
+
if (!columns.some((c) => c.name === "kind"))
|
|
13
|
+
db.exec(
|
|
14
|
+
`ALTER TABLE chat_items ADD COLUMN kind TEXT
|
|
15
|
+
GENERATED ALWAYS AS (json_extract(data, '$.kind')) VIRTUAL`
|
|
16
|
+
);
|
|
17
|
+
db.exec(
|
|
18
|
+
`CREATE INDEX IF NOT EXISTS idx_chat_items_kind ON chat_items(session_id, kind, seq)`
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
import { installChangeTriggers, TABLES_WATCHED_45 } from "../changes";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 45,
|
|
6
|
+
name: "change_versions",
|
|
7
|
+
up: (db) => {
|
|
8
|
+
// What browsers show from these tables is pushed when they change
|
|
9
|
+
// (lib/db/changes.ts) instead of polled. A table added later gets its
|
|
10
|
+
// own migration.
|
|
11
|
+
installChangeTriggers(db, TABLES_WATCHED_45);
|
|
12
|
+
},
|
|
13
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 46,
|
|
5
|
+
name: "session_port_slots",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// A session's port slot and the ports agentos.json's bases resolve to
|
|
8
|
+
// with it (lib/ports.ts). The unique index is what keeps two parallel
|
|
9
|
+
// starts, in any process, off one slot.
|
|
10
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
11
|
+
name: string;
|
|
12
|
+
}[];
|
|
13
|
+
if (!columns.some((c) => c.name === "port_slot"))
|
|
14
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN port_slot INTEGER`);
|
|
15
|
+
if (!columns.some((c) => c.name === "ports"))
|
|
16
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN ports TEXT`);
|
|
17
|
+
db.exec(
|
|
18
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_port_slot ON sessions(port_slot) WHERE port_slot IS NOT NULL`
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 47,
|
|
5
|
+
name: "pin_orchestrators",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// A workspace's orchestrator starts pinned to the top of its list;
|
|
8
|
+
// unpinning it is then remembered on the session, which lives as
|
|
9
|
+
// long as its workspace.
|
|
10
|
+
db.exec(`UPDATE sessions SET pinned = 1 WHERE role = 'orchestrator'`);
|
|
11
|
+
},
|
|
12
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 48,
|
|
5
|
+
name: "session_database",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// The session's private Postgres copy, or why it has none, as JSON
|
|
8
|
+
// (lib/project-config/database.ts).
|
|
9
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
10
|
+
name: string;
|
|
11
|
+
}[];
|
|
12
|
+
if (!columns.some((c) => c.name === "database"))
|
|
13
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN database TEXT`);
|
|
14
|
+
},
|
|
15
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 49,
|
|
5
|
+
name: "peer_mirror",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// A row that only shows a linked machine's own session: actions on
|
|
8
|
+
// it go to that machine. Rows this machine started there over ssh
|
|
9
|
+
// are its own, and stay 0.
|
|
10
|
+
db.exec(
|
|
11
|
+
`ALTER TABLE sessions ADD COLUMN peer_mirror INTEGER NOT NULL DEFAULT 0`
|
|
12
|
+
);
|
|
13
|
+
},
|
|
14
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
import { installChangeTriggers, TABLES_WATCHED_50 } from "../changes";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 50,
|
|
6
|
+
name: "task_queue",
|
|
7
|
+
up: (db) => {
|
|
8
|
+
// Tasks waiting to start (lib/tasks/queue.ts): over the workspace's
|
|
9
|
+
// limit on running tasks, or after another task. A row's id is the
|
|
10
|
+
// session id it starts as, so a restart never starts one twice.
|
|
11
|
+
db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS task_queue (
|
|
13
|
+
id TEXT PRIMARY KEY,
|
|
14
|
+
project_id TEXT NOT NULL,
|
|
15
|
+
prompt TEXT NOT NULL,
|
|
16
|
+
name TEXT,
|
|
17
|
+
model TEXT,
|
|
18
|
+
view TEXT,
|
|
19
|
+
base_branch TEXT,
|
|
20
|
+
host_id TEXT,
|
|
21
|
+
after_task TEXT,
|
|
22
|
+
after_any TEXT,
|
|
23
|
+
origin_workspace_id TEXT,
|
|
24
|
+
position INTEGER NOT NULL,
|
|
25
|
+
status TEXT NOT NULL DEFAULT 'queued',
|
|
26
|
+
note TEXT,
|
|
27
|
+
error TEXT,
|
|
28
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
30
|
+
started_at TEXT
|
|
31
|
+
)
|
|
32
|
+
`);
|
|
33
|
+
db.exec(
|
|
34
|
+
`CREATE INDEX IF NOT EXISTS idx_task_queue_status ON task_queue(status, position)`
|
|
35
|
+
);
|
|
36
|
+
const columns = db.prepare(`PRAGMA table_info(workspaces)`).all() as {
|
|
37
|
+
name: string;
|
|
38
|
+
}[];
|
|
39
|
+
// Null: no limit.
|
|
40
|
+
if (!columns.some((c) => c.name === "max_running_tasks"))
|
|
41
|
+
db.exec(`ALTER TABLE workspaces ADD COLUMN max_running_tasks INTEGER`);
|
|
42
|
+
installChangeTriggers(db, TABLES_WATCHED_50);
|
|
43
|
+
},
|
|
44
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
// 50 is taken by queued tasks, landing separately.
|
|
5
|
+
id: 51,
|
|
6
|
+
name: "chat_queue_sent_by",
|
|
7
|
+
up: (db) => {
|
|
8
|
+
// Who sent a queued message the reader didn't type (lib/chat/events
|
|
9
|
+
// SentBy, as JSON), so it still shows as theirs once it's sent.
|
|
10
|
+
const columns = db.prepare(`PRAGMA table_info(chat_queue)`).all() as {
|
|
11
|
+
name: string;
|
|
12
|
+
}[];
|
|
13
|
+
if (!columns.some((c) => c.name === "sent_by"))
|
|
14
|
+
db.exec(`ALTER TABLE chat_queue ADD COLUMN sent_by TEXT`);
|
|
15
|
+
},
|
|
16
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 52,
|
|
5
|
+
name: "sessions_chat_restarted",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Set by a chat worker that retired cleanly after a deploy; the next
|
|
8
|
+
// worker tells the agent so with its first message, then clears it.
|
|
9
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
10
|
+
name: string;
|
|
11
|
+
}[];
|
|
12
|
+
if (!columns.some((c) => c.name === "chat_restarted"))
|
|
13
|
+
db.exec(
|
|
14
|
+
`ALTER TABLE sessions ADD COLUMN chat_restarted INTEGER NOT NULL DEFAULT 0`
|
|
15
|
+
);
|
|
16
|
+
},
|
|
17
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 53,
|
|
5
|
+
name: "brief_amendments_failure_sha_ask_raised_by",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Scope changes the orchestrator records on a task after it started
|
|
8
|
+
// (send with scope_change); its review judges against them.
|
|
9
|
+
db.exec(`
|
|
10
|
+
CREATE TABLE IF NOT EXISTS brief_amendments (
|
|
11
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
|
+
session_id TEXT NOT NULL,
|
|
13
|
+
workspace_id TEXT NOT NULL,
|
|
14
|
+
text TEXT NOT NULL,
|
|
15
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_brief_amendments_session
|
|
18
|
+
ON brief_amendments(session_id);
|
|
19
|
+
`);
|
|
20
|
+
// The commit a stored verdict's failure was counted for, so reading
|
|
21
|
+
// the same verdict again isn't a second failure.
|
|
22
|
+
const columns = db
|
|
23
|
+
.prepare(`PRAGMA table_info(orchestrator_gate_failures)`)
|
|
24
|
+
.all() as { name: string }[];
|
|
25
|
+
if (!columns.some((c) => c.name === "last_sha"))
|
|
26
|
+
db.exec(
|
|
27
|
+
`ALTER TABLE orchestrator_gate_failures ADD COLUMN last_sha TEXT`
|
|
28
|
+
);
|
|
29
|
+
// Who raised a merge ask: the orchestrator may refresh its own, never
|
|
30
|
+
// one the gates raised.
|
|
31
|
+
const asks = db.prepare(`PRAGMA table_info(orchestrator_asks)`).all() as {
|
|
32
|
+
name: string;
|
|
33
|
+
}[];
|
|
34
|
+
if (!asks.some((c) => c.name === "raised_by"))
|
|
35
|
+
db.exec(`ALTER TABLE orchestrator_asks ADD COLUMN raised_by TEXT`);
|
|
36
|
+
},
|
|
37
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 54,
|
|
5
|
+
name: "sessions_chat_tools_digest",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// The tools a chat worker last gave the agent (an orchestrator's), so
|
|
8
|
+
// the next one says they changed only when they did.
|
|
9
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
10
|
+
name: string;
|
|
11
|
+
}[];
|
|
12
|
+
if (!columns.some((c) => c.name === "chat_tools_digest"))
|
|
13
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN chat_tools_digest TEXT`);
|
|
14
|
+
},
|
|
15
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 55,
|
|
5
|
+
name: "sessions_merge_armed_and_rebase",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// merge_armed_at: AgentOS merges the task's PR once every gate
|
|
8
|
+
// passes (orchestrator/armed.ts); merge_armed_seen: the failure it
|
|
9
|
+
// last reported. rebased_from/to: the heads an automatic rebase onto
|
|
10
|
+
// the base moved the branch between, for the code-review gate.
|
|
11
|
+
// rebase_asked_head: the head the task was last asked to rebase.
|
|
12
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
13
|
+
name: string;
|
|
14
|
+
}[];
|
|
15
|
+
for (const name of [
|
|
16
|
+
"merge_armed_at",
|
|
17
|
+
"merge_armed_seen",
|
|
18
|
+
"rebased_from",
|
|
19
|
+
"rebased_to",
|
|
20
|
+
"rebase_asked_head",
|
|
21
|
+
])
|
|
22
|
+
if (!columns.some((c) => c.name === name))
|
|
23
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN ${name} TEXT`);
|
|
24
|
+
},
|
|
25
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
import { installChangeTriggers, TABLES_WATCHED_56 } from "../changes";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 56,
|
|
6
|
+
name: "orchestrator_rules",
|
|
7
|
+
up: (db) => {
|
|
8
|
+
// Standing rules an orchestrator keeps across fresh starts
|
|
9
|
+
// (lib/orchestrator/rules.ts): written by it or by Saad, never
|
|
10
|
+
// deleted, only marked removed.
|
|
11
|
+
db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS orchestrator_rules (
|
|
13
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
|
+
workspace_id TEXT NOT NULL,
|
|
15
|
+
text TEXT NOT NULL,
|
|
16
|
+
added_by TEXT NOT NULL,
|
|
17
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
18
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
19
|
+
removed_at TEXT,
|
|
20
|
+
removed_by TEXT
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
db.exec(
|
|
24
|
+
`CREATE INDEX IF NOT EXISTS idx_orchestrator_rules
|
|
25
|
+
ON orchestrator_rules(workspace_id, removed_at, id)`
|
|
26
|
+
);
|
|
27
|
+
// Its last worker ended the conversation at the context threshold:
|
|
28
|
+
// the tokens it had reached (0 otherwise). The next one starts a new
|
|
29
|
+
// one and tells the agent so.
|
|
30
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
31
|
+
name: string;
|
|
32
|
+
}[];
|
|
33
|
+
if (!columns.some((c) => c.name === "chat_refreshed"))
|
|
34
|
+
db.exec(
|
|
35
|
+
`ALTER TABLE sessions ADD COLUMN chat_refreshed INTEGER NOT NULL DEFAULT 0`
|
|
36
|
+
);
|
|
37
|
+
installChangeTriggers(db, TABLES_WATCHED_56);
|
|
38
|
+
},
|
|
39
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 57,
|
|
5
|
+
name: "task_pr_wakes",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// What a task's agent was told about its PR (orchestrator/pr-wake.ts),
|
|
8
|
+
// one row per failing head or review/comment id, claimed before the
|
|
9
|
+
// message goes. kind: check, review or comment (comments are capped).
|
|
10
|
+
db.exec(`
|
|
11
|
+
CREATE TABLE IF NOT EXISTS task_pr_wakes (
|
|
12
|
+
session_id TEXT NOT NULL,
|
|
13
|
+
key TEXT NOT NULL,
|
|
14
|
+
kind TEXT NOT NULL,
|
|
15
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16
|
+
PRIMARY KEY (session_id, key)
|
|
17
|
+
);
|
|
18
|
+
`);
|
|
19
|
+
// pr_wake_since: reviews and comments older than this are taken as
|
|
20
|
+
// known when a task's PR is first watched.
|
|
21
|
+
const sessions = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
22
|
+
name: string;
|
|
23
|
+
}[];
|
|
24
|
+
if (!sessions.some((c) => c.name === "pr_wake_since"))
|
|
25
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN pr_wake_since TEXT`);
|
|
26
|
+
const workspaces = db.prepare(`PRAGMA table_info(workspaces)`).all() as {
|
|
27
|
+
name: string;
|
|
28
|
+
}[];
|
|
29
|
+
if (!workspaces.some((c) => c.name === "wake_tasks_on_pr"))
|
|
30
|
+
db.exec(
|
|
31
|
+
`ALTER TABLE workspaces ADD COLUMN wake_tasks_on_pr INTEGER NOT NULL DEFAULT 1`
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
import { installChangeTriggers, TABLES_WATCHED_58 } from "../changes";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 58,
|
|
6
|
+
name: "away_mode",
|
|
7
|
+
up: (db) => {
|
|
8
|
+
// Saad's Away mode (lib/away): one row per time he stepped away. The
|
|
9
|
+
// report is built from the database when it ends and kept here.
|
|
10
|
+
db.exec(`
|
|
11
|
+
CREATE TABLE IF NOT EXISTS away_periods (
|
|
12
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13
|
+
set_by TEXT NOT NULL,
|
|
14
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15
|
+
expires_at TEXT,
|
|
16
|
+
ended_at TEXT,
|
|
17
|
+
ended_by TEXT,
|
|
18
|
+
last_sweep_at TEXT,
|
|
19
|
+
asks_notified_at TEXT,
|
|
20
|
+
report TEXT,
|
|
21
|
+
report_seen_at TEXT
|
|
22
|
+
)
|
|
23
|
+
`);
|
|
24
|
+
// What the away sweep did, once per key per period: a nudge, a
|
|
25
|
+
// request to commit, a review run again, a deploy checked.
|
|
26
|
+
db.exec(`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS away_actions (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
away_id INTEGER NOT NULL,
|
|
30
|
+
workspace_id TEXT NOT NULL,
|
|
31
|
+
subject TEXT NOT NULL,
|
|
32
|
+
kind TEXT NOT NULL,
|
|
33
|
+
key TEXT NOT NULL,
|
|
34
|
+
detail TEXT,
|
|
35
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36
|
+
UNIQUE (away_id, key)
|
|
37
|
+
)
|
|
38
|
+
`);
|
|
39
|
+
// When a task merged, whichever path merged it.
|
|
40
|
+
const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
|
|
41
|
+
name: string;
|
|
42
|
+
}[];
|
|
43
|
+
if (!columns.some((c) => c.name === "merged_at"))
|
|
44
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN merged_at TEXT`);
|
|
45
|
+
// When a task ended without a merge (dropped, or done with no PR or
|
|
46
|
+
// its PR closed).
|
|
47
|
+
if (!columns.some((c) => c.name === "task_ended_at"))
|
|
48
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN task_ended_at TEXT`);
|
|
49
|
+
db.exec(`
|
|
50
|
+
CREATE TRIGGER IF NOT EXISTS sessions_task_ended_at
|
|
51
|
+
AFTER UPDATE OF task_status ON sessions
|
|
52
|
+
WHEN NEW.task_status IN ('dropped', 'done')
|
|
53
|
+
AND OLD.task_status IS NOT NEW.task_status
|
|
54
|
+
AND NEW.task_ended_at IS OLD.task_ended_at
|
|
55
|
+
BEGIN
|
|
56
|
+
UPDATE sessions SET task_ended_at = datetime('now') WHERE id = NEW.id;
|
|
57
|
+
END
|
|
58
|
+
`);
|
|
59
|
+
db.exec(`
|
|
60
|
+
CREATE TRIGGER IF NOT EXISTS sessions_merged_at
|
|
61
|
+
AFTER UPDATE OF task_status ON sessions
|
|
62
|
+
WHEN NEW.task_status = 'merged' AND OLD.task_status IS NOT 'merged'
|
|
63
|
+
-- unless the same statement set it (an import keeps its own)
|
|
64
|
+
AND NEW.merged_at IS OLD.merged_at
|
|
65
|
+
BEGIN
|
|
66
|
+
UPDATE sessions SET merged_at = datetime('now') WHERE id = NEW.id;
|
|
67
|
+
END
|
|
68
|
+
`);
|
|
69
|
+
installChangeTriggers(db, TABLES_WATCHED_58);
|
|
70
|
+
},
|
|
71
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 59,
|
|
5
|
+
name: "gate_failure_commits",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// Each head a gate failed on, once: a gate's failure counts once per
|
|
8
|
+
// commit however many times that commit is judged (the armed watcher,
|
|
9
|
+
// CI finishing, sign_off, done, land), and the second commit it fails
|
|
10
|
+
// on goes to Saad. The key is what makes it once, across restarts and
|
|
11
|
+
// concurrent evaluations.
|
|
12
|
+
db.exec(`
|
|
13
|
+
CREATE TABLE IF NOT EXISTS orchestrator_gate_failure_commits (
|
|
14
|
+
session_id TEXT NOT NULL,
|
|
15
|
+
gate TEXT NOT NULL,
|
|
16
|
+
sha TEXT NOT NULL,
|
|
17
|
+
workspace_id TEXT NOT NULL,
|
|
18
|
+
reason TEXT,
|
|
19
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
20
|
+
PRIMARY KEY (session_id, gate, sha)
|
|
21
|
+
);
|
|
22
|
+
INSERT OR IGNORE INTO orchestrator_gate_failure_commits (session_id, gate, sha, workspace_id, reason)
|
|
23
|
+
SELECT session_id, gate, last_sha, workspace_id, last_reason
|
|
24
|
+
FROM orchestrator_gate_failures WHERE last_sha IS NOT NULL;
|
|
25
|
+
CREATE TRIGGER IF NOT EXISTS gate_failures_clear_commits
|
|
26
|
+
AFTER DELETE ON orchestrator_gate_failures
|
|
27
|
+
BEGIN
|
|
28
|
+
DELETE FROM orchestrator_gate_failure_commits
|
|
29
|
+
WHERE session_id = OLD.session_id AND gate = OLD.gate;
|
|
30
|
+
END;
|
|
31
|
+
`);
|
|
32
|
+
},
|
|
33
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 60,
|
|
5
|
+
name: "check_runs_and_compactions",
|
|
6
|
+
up: (db) => {
|
|
7
|
+
// What each run of the independent reviewer and the scope check cost,
|
|
8
|
+
// from its claude -p result: it leaves no transcript, so this is the
|
|
9
|
+
// only record. And each compaction a chat's agent made, so tokens per
|
|
10
|
+
// task can be compared before and after the threshold moved.
|
|
11
|
+
db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS check_runs (
|
|
13
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
|
+
workspace_id TEXT NOT NULL,
|
|
15
|
+
session_id TEXT NOT NULL,
|
|
16
|
+
label TEXT NOT NULL,
|
|
17
|
+
pr_number INTEGER,
|
|
18
|
+
sha TEXT NOT NULL,
|
|
19
|
+
kind TEXT NOT NULL,
|
|
20
|
+
model TEXT,
|
|
21
|
+
input_tokens INTEGER NOT NULL,
|
|
22
|
+
cache_write_tokens INTEGER NOT NULL,
|
|
23
|
+
cache_read_tokens INTEGER NOT NULL,
|
|
24
|
+
output_tokens INTEGER NOT NULL,
|
|
25
|
+
cost_usd REAL NOT NULL,
|
|
26
|
+
duration_ms INTEGER,
|
|
27
|
+
at INTEGER NOT NULL
|
|
28
|
+
);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_check_runs_at ON check_runs(at);
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_check_runs_sha ON check_runs(session_id, sha, kind);
|
|
31
|
+
CREATE TABLE IF NOT EXISTS chat_compactions (
|
|
32
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
33
|
+
session_id TEXT NOT NULL,
|
|
34
|
+
session_name TEXT NOT NULL,
|
|
35
|
+
workspace_id TEXT,
|
|
36
|
+
at INTEGER NOT NULL,
|
|
37
|
+
trigger TEXT NOT NULL,
|
|
38
|
+
before_tokens INTEGER,
|
|
39
|
+
after_tokens INTEGER
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_chat_compactions_at ON chat_compactions(at);
|
|
42
|
+
`);
|
|
43
|
+
},
|
|
44
|
+
} satisfies Migration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Migration } from "../migration-types";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: 61,
|
|
5
|
+
name: "model_routing",
|
|
6
|
+
// One transaction, so a crash part-way leaves nothing to skip as
|
|
7
|
+
// "duplicate column" on the next start.
|
|
8
|
+
up: (db) =>
|
|
9
|
+
db.transaction(() => {
|
|
10
|
+
// What a session runs on beyond the model name: its provider (NULL is
|
|
11
|
+
// Anthropic), a task's own models by role (JSON), and a task's plan
|
|
12
|
+
// step (planning, then implementing). Each turn keeps the model,
|
|
13
|
+
// provider and role it ran as, how long it took, and the plan's 5-hour
|
|
14
|
+
// window when it ended, so spend compares by model. A check run keeps the
|
|
15
|
+
// provider its model came from.
|
|
16
|
+
for (const col of [
|
|
17
|
+
`ALTER TABLE sessions ADD COLUMN model_provider TEXT`,
|
|
18
|
+
`ALTER TABLE sessions ADD COLUMN model_overrides TEXT`,
|
|
19
|
+
`ALTER TABLE sessions ADD COLUMN plan_phase TEXT`,
|
|
20
|
+
`ALTER TABLE task_queue ADD COLUMN model_overrides TEXT`,
|
|
21
|
+
`ALTER TABLE chat_turns ADD COLUMN model TEXT`,
|
|
22
|
+
`ALTER TABLE chat_turns ADD COLUMN provider TEXT`,
|
|
23
|
+
`ALTER TABLE chat_turns ADD COLUMN role TEXT`,
|
|
24
|
+
`ALTER TABLE chat_turns ADD COLUMN duration_ms INTEGER`,
|
|
25
|
+
`ALTER TABLE chat_turns ADD COLUMN window_pct REAL`,
|
|
26
|
+
`ALTER TABLE check_runs ADD COLUMN provider TEXT`,
|
|
27
|
+
])
|
|
28
|
+
db.exec(col);
|
|
29
|
+
})(),
|
|
30
|
+
} satisfies Migration;
|