@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.
Files changed (150) hide show
  1. package/app/api/bus/spawn/route.ts +35 -16
  2. package/app/api/lumifyhub/cards/[cardId]/run/route.ts +3 -0
  3. package/app/api/tasks/route.ts +3 -0
  4. package/app/page.tsx +2 -1
  5. package/components/SessionList/SessionRow.tsx +2 -1
  6. package/lib/agents/spawn.ts +16 -4
  7. package/lib/chat/refresh.ts +2 -1
  8. package/lib/chat/runner.ts +29 -0
  9. package/lib/chat/store.ts +53 -0
  10. package/lib/db/migration-types.ts +15 -0
  11. package/lib/db/migrations/0001_add_group_path_to_sessions.ts +11 -0
  12. package/lib/db/migrations/0002_add_agent_type_to_sessions.ts +11 -0
  13. package/lib/db/migrations/0003_add_worktree_columns_to_sessions.ts +12 -0
  14. package/lib/db/migrations/0004_add_pr_tracking_to_sessions.ts +11 -0
  15. package/lib/db/migrations/0005_add_group_path_index.ts +11 -0
  16. package/lib/db/migrations/0006_add_orchestration_columns_to_sessions.ts +16 -0
  17. package/lib/db/migrations/0007_add_auto_approve_to_sessions.ts +11 -0
  18. package/lib/db/migrations/0008_add_dev_server_columns.ts +19 -0
  19. package/lib/db/migrations/0009_add_project_id_to_sessions.ts +17 -0
  20. package/lib/db/migrations/0010_add_project_id_to_dev_servers.ts +36 -0
  21. package/lib/db/migrations/0011_add_tmux_name_to_sessions.ts +13 -0
  22. package/lib/db/migrations/0012_add_initial_prompt_to_projects.ts +9 -0
  23. package/lib/db/migrations/0013_add_project_repositories_table.ts +22 -0
  24. package/lib/db/migrations/0014_add_hosts.ts +23 -0
  25. package/lib/db/migrations/0015_add_workspaces.ts +18 -0
  26. package/lib/db/migrations/0016_add_task_columns_to_sessions.ts +13 -0
  27. package/lib/db/migrations/0017_add_bus_messages.ts +24 -0
  28. package/lib/db/migrations/0018_add_chat.ts +23 -0
  29. package/lib/db/migrations/0019_add_lumifyhub.ts +36 -0
  30. package/lib/db/migrations/0020_add_lumifyhub_published_docs.ts +18 -0
  31. package/lib/db/migrations/0021_add_chat_access.ts +14 -0
  32. package/lib/db/migrations/0022_add_last_seen.ts +10 -0
  33. package/lib/db/migrations/0023_mark_existing_sessions_seen.ts +12 -0
  34. package/lib/db/migrations/0024_add_stacks.ts +49 -0
  35. package/lib/db/migrations/0025_add_stack_item_attempts.ts +16 -0
  36. package/lib/db/migrations/0026_add_workspace_orchestrator.ts +47 -0
  37. package/lib/db/migrations/0027_add_devices_and_settings.ts +28 -0
  38. package/lib/db/migrations/0028_add_orchestrator_acting.ts +70 -0
  39. package/lib/db/migrations/0029_add_orchestrator_asks_and_pause.ts +81 -0
  40. package/lib/db/migrations/0030_add_sessions_archived_at.ts +14 -0
  41. package/lib/db/migrations/0031_add_stack_item_restacked_heads.ts +22 -0
  42. package/lib/db/migrations/0032_add_sessions_pinned.ts +12 -0
  43. package/lib/db/migrations/0033_add_program_status.ts +18 -0
  44. package/lib/db/migrations/0034_add_session_names.ts +21 -0
  45. package/lib/db/migrations/0035_add_artifacts.ts +23 -0
  46. package/lib/db/migrations/0036_add_chat_queue.ts +26 -0
  47. package/lib/db/migrations/0037_add_chat_plan_and_turns.ts +39 -0
  48. package/lib/db/migrations/0038_add_schedules.ts +56 -0
  49. package/lib/db/migrations/0039_add_session_name_source.ts +21 -0
  50. package/lib/db/migrations/0040_add_schedule_targets_and_phone_notify.ts +41 -0
  51. package/lib/db/migrations/0041_add_task_setup.ts +17 -0
  52. package/lib/db/migrations/0042_add_host_links_and_moved_tasks.ts +29 -0
  53. package/lib/db/migrations/0043_scratch_project.ts +14 -0
  54. package/lib/db/migrations/0044_chat_item_kind.ts +21 -0
  55. package/lib/db/migrations/0045_change_versions.ts +13 -0
  56. package/lib/db/migrations/0046_session_port_slots.ts +21 -0
  57. package/lib/db/migrations/0047_pin_orchestrators.ts +12 -0
  58. package/lib/db/migrations/0048_session_database.ts +15 -0
  59. package/lib/db/migrations/0049_peer_mirror.ts +14 -0
  60. package/lib/db/migrations/0050_task_queue.ts +44 -0
  61. package/lib/db/migrations/0051_chat_queue_sent_by.ts +16 -0
  62. package/lib/db/migrations/0052_sessions_chat_restarted.ts +17 -0
  63. package/lib/db/migrations/0053_brief_amendments_failure_sha_ask_raised_by.ts +37 -0
  64. package/lib/db/migrations/0054_sessions_chat_tools_digest.ts +15 -0
  65. package/lib/db/migrations/0055_sessions_merge_armed_and_rebase.ts +25 -0
  66. package/lib/db/migrations/0056_orchestrator_rules.ts +39 -0
  67. package/lib/db/migrations/0057_task_pr_wakes.ts +34 -0
  68. package/lib/db/migrations/0058_away_mode.ts +71 -0
  69. package/lib/db/migrations/0059_gate_failure_commits.ts +33 -0
  70. package/lib/db/migrations/0060_check_runs_and_compactions.ts +44 -0
  71. package/lib/db/migrations/0061_model_routing.ts +30 -0
  72. package/lib/db/migrations/0062_sessions_quick_of.ts +19 -0
  73. package/lib/db/migrations/0063_one_off_schedules.ts +16 -0
  74. package/lib/db/migrations/0064_chat_compactions_phase.ts +15 -0
  75. package/lib/db/migrations/0065_orchestrator_triage.ts +23 -0
  76. package/lib/db/migrations/0066_workspace_orch_mcp_servers.ts +16 -0
  77. package/lib/db/migrations/0067_orchestrator_event_log_suppressed.ts +14 -0
  78. package/lib/db/migrations/0068_usage_calls_and_rollups.ts +74 -0
  79. package/lib/db/migrations/0069_session_logs.ts +26 -0
  80. package/lib/db/migrations/0070_chat_items_running.ts +15 -0
  81. package/lib/db/migrations/0071_devices_push.ts +29 -0
  82. package/lib/db/migrations/0072_push_outbox.ts +33 -0
  83. package/lib/db/migrations/0073_orchestrated_task_moves.ts +27 -0
  84. package/lib/db/migrations/0074_held_task_replies.ts +27 -0
  85. package/lib/db/migrations/0075_plan_only_tasks.ts +17 -0
  86. package/lib/db/migrations/0076_welcome_backfill.ts +16 -0
  87. package/lib/db/migrations/0077_simulator_leases.ts +21 -0
  88. package/lib/db/migrations/0078_orchestrator_questions.ts +34 -0
  89. package/lib/db/migrations/0079_task_queue_agent.ts +13 -0
  90. package/lib/db/migrations/0080_recipes.ts +129 -0
  91. package/lib/db/migrations/0081_schedules_into_recipes.ts +18 -0
  92. package/lib/db/migrations/0082_session_shelf_times.ts +23 -0
  93. package/lib/db/migrations/0083_native_devices_can_approve.ts +18 -0
  94. package/lib/db/migrations/0084_approval_audit.ts +20 -0
  95. package/lib/db/migrations/0085_experiment_usage.ts +35 -0
  96. package/lib/db/migrations/0086_learning_cards.ts +28 -0
  97. package/lib/db/migrations/0087_plan_pieces_reconcile.ts +29 -0
  98. package/lib/db/migrations/0088_shared_links.ts +25 -0
  99. package/lib/db/migrations/0089_passkeys_device_keys.ts +23 -0
  100. package/lib/db/migrations/20261014T0900_task_scheduling.ts +57 -0
  101. package/lib/db/migrations/20261014T1235_placed_mirror_started_by.ts +19 -0
  102. package/lib/db/migrations/20261014T1600_queued_task_messages.ts +21 -0
  103. package/lib/db/migrations/20261014T1800_task_starter_device.ts +20 -0
  104. package/lib/db/migrations/20261014T1900_pause_note.ts +13 -0
  105. package/lib/db/migrations/20261014T2000_chat_bookmarks.ts +33 -0
  106. package/lib/db/migrations/20261014T2100_gate_failure_ask_held.ts +18 -0
  107. package/lib/db/migrations/20261014T2110_task_start_record.ts +21 -0
  108. package/lib/db/migrations/20261014T2200_end_orphaned_tasks.ts +23 -0
  109. package/lib/db/migrations/README.md +35 -0
  110. package/lib/db/migrations-legacy-checksums.json +9 -8
  111. package/lib/db/migrations-split-snapshot.json +1759 -0
  112. package/lib/db/migrations.ts +76 -2337
  113. package/lib/db/types.ts +2 -0
  114. package/lib/learning-cards/apply.ts +7 -1
  115. package/lib/models/plan-step.ts +25 -8
  116. package/lib/models/resolve.ts +1 -1
  117. package/lib/models/resume.ts +1 -1
  118. package/lib/models/roles.ts +0 -16
  119. package/lib/models/session-role.ts +19 -0
  120. package/lib/orchestrator/act.ts +22 -7
  121. package/lib/orchestrator/amendments.ts +10 -3
  122. package/lib/orchestrator/serve.ts +1 -0
  123. package/lib/orchestrator/testing.ts +8 -0
  124. package/lib/orchestrator/tool-schemas.ts +9 -0
  125. package/lib/orchestrator/tools.ts +1 -1
  126. package/lib/plans/claim.ts +131 -0
  127. package/lib/plans/release.ts +5 -2
  128. package/lib/recipes/run.ts +6 -0
  129. package/lib/schedules/start.ts +1 -0
  130. package/lib/sessions/fork.ts +10 -0
  131. package/lib/sessions/launch.ts +5 -1
  132. package/lib/stacks/start.ts +1 -0
  133. package/lib/tasks/held-remote.ts +4 -2
  134. package/lib/tasks/held-replies.ts +5 -2
  135. package/lib/tasks/held.ts +2 -1
  136. package/lib/tasks/index.ts +85 -46
  137. package/lib/tasks/move-carry.ts +12 -1
  138. package/lib/tasks/move-guard.ts +2 -1
  139. package/lib/tasks/move.ts +7 -5
  140. package/lib/tasks/pause.ts +1 -1
  141. package/lib/tasks/queue.ts +54 -31
  142. package/lib/tasks/remote.ts +3 -1
  143. package/lib/tasks/starter.ts +124 -0
  144. package/lib/usage/calls.ts +2 -1
  145. package/lib/usage/turns.ts +2 -1
  146. package/package.json +6 -3
  147. package/scripts/check +1 -0
  148. package/scripts/gen-migrations-index.mjs +44 -0
  149. package/scripts/screenshots/demo.ts +5 -1
  150. package/scripts/screenshots/run.ts +5 -1
@@ -0,0 +1,35 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 85,
5
+ name: "experiment_usage",
6
+ up: (db) => {
7
+ // What each experiment did: model calls with their cost, and the
8
+ // non-model work (scripts run, suggestions shown and decided).
9
+ db.exec(`
10
+ CREATE TABLE IF NOT EXISTS experiment_usage (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ experiment_id TEXT NOT NULL,
13
+ event TEXT NOT NULL,
14
+ trigger TEXT NOT NULL,
15
+ session_id TEXT,
16
+ workspace_id TEXT,
17
+ model TEXT,
18
+ provider TEXT,
19
+ input_tokens INTEGER NOT NULL DEFAULT 0,
20
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
21
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
22
+ output_tokens INTEGER NOT NULL DEFAULT 0,
23
+ cost_usd REAL NOT NULL DEFAULT 0,
24
+ duration_ms INTEGER,
25
+ ok INTEGER NOT NULL DEFAULT 1,
26
+ detail TEXT,
27
+ at INTEGER NOT NULL
28
+ );
29
+ CREATE INDEX IF NOT EXISTS idx_experiment_usage_exp_at
30
+ ON experiment_usage(experiment_id, at);
31
+ CREATE INDEX IF NOT EXISTS idx_experiment_usage_session
32
+ ON experiment_usage(session_id, experiment_id, event);
33
+ `);
34
+ },
35
+ } satisfies Migration;
@@ -0,0 +1,28 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 86,
5
+ name: "learning_cards",
6
+ up: (db) => {
7
+ // End-of-session suggestions (save as recipe, keep a lesson). An
8
+ // accepted lesson row is the record of where it was applied.
9
+ db.exec(`
10
+ CREATE TABLE IF NOT EXISTS learning_cards (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ session_id TEXT NOT NULL,
13
+ workspace_id TEXT,
14
+ kind TEXT NOT NULL,
15
+ status TEXT NOT NULL DEFAULT 'pending',
16
+ payload TEXT NOT NULL,
17
+ destination TEXT,
18
+ applied_ref TEXT,
19
+ shown_at INTEGER,
20
+ decided_at INTEGER,
21
+ created_at INTEGER NOT NULL,
22
+ UNIQUE (session_id, kind)
23
+ );
24
+ CREATE INDEX IF NOT EXISTS idx_learning_cards_dest
25
+ ON learning_cards(destination, decided_at);
26
+ `);
27
+ },
28
+ } satisfies Migration;
@@ -0,0 +1,29 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 87,
5
+ name: "plan_pieces_reconcile",
6
+ up: (db) => {
7
+ // How many pieces a plan listed and what the checker folded, so a plan
8
+ // that builds as fewer than it listed is never silent; the PRs agents
9
+ // opened (from their `gh pr create` output) so one outside its task's
10
+ // repo is noticed.
11
+ const columns = db.prepare(`PRAGMA table_info(features)`).all() as {
12
+ name: string;
13
+ }[];
14
+ if (!columns.some((c) => c.name === "listed"))
15
+ db.exec(`ALTER TABLE features ADD COLUMN listed INTEGER`);
16
+ if (!columns.some((c) => c.name === "folded"))
17
+ db.exec(`ALTER TABLE features ADD COLUMN folded TEXT`);
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS agent_prs (
20
+ url TEXT PRIMARY KEY,
21
+ session_id TEXT NOT NULL,
22
+ repo TEXT NOT NULL,
23
+ number INTEGER NOT NULL,
24
+ seen_at TEXT NOT NULL DEFAULT (datetime('now')),
25
+ told_at TEXT
26
+ );
27
+ `);
28
+ },
29
+ } satisfies Migration;
@@ -0,0 +1,25 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 88,
5
+ name: "shared_links",
6
+ up: (db) => {
7
+ // Snapshot links made through Connect. Connect keeps only a digest of
8
+ // the token, so this is the one place the link can be copied again.
9
+ db.exec(`
10
+ CREATE TABLE IF NOT EXISTS shared_links (
11
+ id TEXT PRIMARY KEY,
12
+ url TEXT NOT NULL,
13
+ kind TEXT NOT NULL,
14
+ title TEXT NOT NULL,
15
+ session_id TEXT,
16
+ artifact_id TEXT,
17
+ expires_at TEXT,
18
+ created_at TEXT NOT NULL,
19
+ revoked_at TEXT
20
+ );
21
+ CREATE INDEX IF NOT EXISTS idx_shared_links_created
22
+ ON shared_links(created_at DESC);
23
+ `);
24
+ },
25
+ } satisfies Migration;
@@ -0,0 +1,23 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 89,
5
+ name: "passkeys_device_keys",
6
+ up: (db) => {
7
+ // A paired device's own approval key (Secure Enclave, Face ID) sits in
8
+ // the passkeys table beside the browser passkeys: type 'device', bound
9
+ // to the device that made it. Additive; existing rows read as webauthn.
10
+ const columns = db.prepare(`PRAGMA table_info(passkeys)`).all() as {
11
+ name: string;
12
+ }[];
13
+ if (!columns.some((c) => c.name === "type"))
14
+ db.exec(
15
+ `ALTER TABLE passkeys ADD COLUMN type TEXT NOT NULL DEFAULT 'webauthn'`
16
+ );
17
+ if (!columns.some((c) => c.name === "device_id"))
18
+ db.exec(`ALTER TABLE passkeys ADD COLUMN device_id TEXT`);
19
+ db.exec(
20
+ `CREATE INDEX IF NOT EXISTS idx_passkeys_device ON passkeys(device_id)`
21
+ );
22
+ },
23
+ } satisfies Migration;
@@ -0,0 +1,57 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T0900_task_scheduling",
5
+ up: (db) => {
6
+ // Priority, "after any" consumption, and timed pause. Additive: every
7
+ // existing row reads as normal priority and not paused.
8
+ const has = (table: string, col: string) =>
9
+ (
10
+ db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[]
11
+ ).some((c) => c.name === col);
12
+ const add = (table: string, col: string, def: string) => {
13
+ if (!has(table, col))
14
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${col} ${def}`);
15
+ };
16
+ add("task_queue", "priority", "TEXT NOT NULL DEFAULT 'normal'");
17
+ add("task_queue", "machine_needs", "TEXT");
18
+ add("task_queue", "after_any_used", "TEXT");
19
+ add("task_queue", "wait_code", "TEXT");
20
+ db.exec(
21
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_task_queue_after_any_used
22
+ ON task_queue(after_any_used) WHERE after_any_used IS NOT NULL`
23
+ );
24
+ add("sessions", "priority", "TEXT");
25
+ add("sessions", "pause_state", "TEXT");
26
+ add("sessions", "paused_until", "TEXT");
27
+ add("sessions", "pause_id", "INTEGER");
28
+ // "Started on <machine> · <why>" for a task the scheduler placed.
29
+ add("sessions", "placed_note", "TEXT");
30
+ add("bus_messages", "held_pause_id", "INTEGER");
31
+ add("projects", "machine_needs", "TEXT");
32
+ add("projects", "machine_needs_source", "TEXT");
33
+ db.exec(`
34
+ CREATE TABLE IF NOT EXISTS task_pauses (
35
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
36
+ session_id TEXT NOT NULL,
37
+ mode TEXT NOT NULL DEFAULT 'turn',
38
+ by_kind TEXT NOT NULL DEFAULT 'person',
39
+ by_id TEXT,
40
+ reason TEXT,
41
+ until_at TEXT,
42
+ state TEXT NOT NULL DEFAULT 'pausing',
43
+ requested_at TEXT NOT NULL DEFAULT (datetime('now')),
44
+ paused_at TEXT,
45
+ resume_requested_at TEXT,
46
+ resume_by_kind TEXT,
47
+ resumed_at TEXT,
48
+ ended_at TEXT,
49
+ end_reason TEXT,
50
+ interrupted INTEGER NOT NULL DEFAULT 0,
51
+ held_count INTEGER NOT NULL DEFAULT 0
52
+ );
53
+ CREATE INDEX IF NOT EXISTS idx_task_pauses_session
54
+ ON task_pauses(session_id, state);
55
+ `);
56
+ },
57
+ } satisfies Migration;
@@ -0,0 +1,19 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T1235_placed_mirror_started_by",
5
+ up: (db) => {
6
+ // A task the queue started on a linked machine kept no record of the
7
+ // orchestrator that queued it, so it couldn't be read, messaged or
8
+ // stopped from there. Fills the mirror from the queue row; additive,
9
+ // only where the mirror has none.
10
+ db.exec(`
11
+ UPDATE sessions SET
12
+ started_by = (SELECT q.started_by FROM task_queue q WHERE q.id = sessions.id),
13
+ task_since = COALESCE(task_since, created_at)
14
+ WHERE started_by IS NULL AND task_prompt IS NOT NULL
15
+ AND host_id IS NOT NULL AND host_id != 'local'
16
+ AND (SELECT q.started_by FROM task_queue q WHERE q.id = sessions.id) IS NOT NULL
17
+ `);
18
+ },
19
+ } satisfies Migration;
@@ -0,0 +1,21 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T1600_queued_task_messages",
5
+ up: (db) => {
6
+ // A message for a task still in the start queue, held (addressed to
7
+ // the queue id, which is the session id it starts as) until its
8
+ // first prompt. Additive.
9
+ const has = (
10
+ db.prepare(`PRAGMA table_info(bus_messages)`).all() as {
11
+ name: string;
12
+ }[]
13
+ ).some((c) => c.name === "held_queued");
14
+ if (!has)
15
+ db.exec(`ALTER TABLE bus_messages ADD COLUMN held_queued INTEGER`);
16
+ db.exec(
17
+ `CREATE INDEX IF NOT EXISTS idx_bus_messages_held_queued
18
+ ON bus_messages(to_id) WHERE held_queued = 1`
19
+ );
20
+ },
21
+ } satisfies Migration;
@@ -0,0 +1,20 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T1800_task_starter_device",
5
+ up: (db) => {
6
+ // The paired device that started a task over the API: the only one
7
+ // that may later hold it. Tasks already running have no record, so
8
+ // they're marked unrecorded. Additive.
9
+ const has = (
10
+ db.prepare(`PRAGMA table_info(sessions)`).all() as { name: string }[]
11
+ ).some((c) => c.name === "started_by_device");
12
+ if (!has) db.exec(`ALTER TABLE sessions ADD COLUMN started_by_device TEXT`);
13
+ db.exec(`
14
+ UPDATE sessions SET started_by_device = 'unrecorded'
15
+ WHERE started_by_device IS NULL AND started_by IS NULL
16
+ AND held_by_device IS NULL AND task_status = 'running'
17
+ AND task_prompt IS NOT NULL AND (host_id IS NULL OR host_id = 'local')
18
+ `);
19
+ },
20
+ } satisfies Migration;
@@ -0,0 +1,13 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T1900_pause_note",
5
+ up: (db) => {
6
+ // Why a resume that was asked for hasn't happened yet (waiting for a
7
+ // slot, held by load), shown on the task row. Additive.
8
+ const has = (
9
+ db.prepare(`PRAGMA table_info(sessions)`).all() as { name: string }[]
10
+ ).some((c) => c.name === "pause_note");
11
+ if (!has) db.exec(`ALTER TABLE sessions ADD COLUMN pause_note TEXT`);
12
+ },
13
+ } satisfies Migration;
@@ -0,0 +1,33 @@
1
+ import type { Migration } from "../migration-types";
2
+ import {
3
+ installChangeTriggers,
4
+ TABLES_WATCHED_CHAT_BOOKMARKS,
5
+ } from "../changes";
6
+
7
+ export default {
8
+ key: "20261014T2000_chat_bookmarks",
9
+ up: (db) => {
10
+ // A person's marks on single chat items. No FK to sessions: a
11
+ // bookmark outlives a deleted chat with its own snapshot. Additive.
12
+ db.exec(`
13
+ CREATE TABLE IF NOT EXISTS chat_bookmarks (
14
+ id TEXT PRIMARY KEY,
15
+ session_id TEXT NOT NULL,
16
+ item_id TEXT NOT NULL,
17
+ scope TEXT NOT NULL DEFAULT 'item',
18
+ workspace_id TEXT,
19
+ kind TEXT NOT NULL,
20
+ text TEXT NOT NULL,
21
+ session_name TEXT NOT NULL,
22
+ note TEXT,
23
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
24
+ UNIQUE (session_id, item_id, scope)
25
+ )
26
+ `);
27
+ db.exec(
28
+ `CREATE INDEX IF NOT EXISTS idx_chat_bookmarks_ws
29
+ ON chat_bookmarks(workspace_id, created_at DESC)`
30
+ );
31
+ installChangeTriggers(db, TABLES_WATCHED_CHAT_BOOKMARKS);
32
+ },
33
+ } satisfies Migration;
@@ -0,0 +1,18 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T2100_gate_failure_ask_held",
5
+ up: (db) => {
6
+ // When a failed-twice escalation started holding its ask until the
7
+ // agent is idle and CI has settled; null is not held. Additive.
8
+ const has = (
9
+ db.prepare(`PRAGMA table_info(orchestrator_gate_failures)`).all() as {
10
+ name: string;
11
+ }[]
12
+ ).some((c) => c.name === "ask_held_at");
13
+ if (!has)
14
+ db.exec(
15
+ `ALTER TABLE orchestrator_gate_failures ADD COLUMN ask_held_at TEXT`
16
+ );
17
+ },
18
+ } satisfies Migration;
@@ -0,0 +1,21 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T2110_task_start_record",
5
+ up: (db) => {
6
+ // Who started a task and how, on the session and its queue row; an
7
+ // approval kind on brief entries. Additive.
8
+ const add = (table: string, col: string, type: string) => {
9
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all() as {
10
+ name: string;
11
+ }[];
12
+ if (!columns.some((c) => c.name === col))
13
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${col} ${type}`);
14
+ };
15
+ add("sessions", "started_via", "TEXT");
16
+ add("task_queue", "parent_session_id", "TEXT");
17
+ add("task_queue", "started_by_device", "TEXT");
18
+ add("task_queue", "started_via", "TEXT");
19
+ add("brief_amendments", "kind", "TEXT NOT NULL DEFAULT 'scope'");
20
+ },
21
+ } satisfies Migration;
@@ -0,0 +1,23 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ key: "20261014T2200_end_orphaned_tasks",
5
+ up: (db) => {
6
+ // A background task still "running" whose tool call was stopped or
7
+ // errored has no job behind it. Settles the rows already stuck.
8
+ db.exec(`
9
+ UPDATE chat_items
10
+ SET data = json_set(data, '$.status', 'stopped',
11
+ '$.endedAt', CAST(strftime('%s','now') AS INTEGER) * 1000,
12
+ '$.summary', COALESCE(json_extract(data, '$.summary'),
13
+ 'Its agent ended before this finished'))
14
+ WHERE kind = 'task' AND json_extract(data, '$.status') = 'running'
15
+ AND EXISTS (
16
+ SELECT 1 FROM chat_items t
17
+ WHERE t.session_id = chat_items.session_id
18
+ AND t.item_id = json_extract(chat_items.data, '$.toolUseId')
19
+ AND t.kind = 'tool'
20
+ AND json_extract(t.data, '$.status') IN ('stopped', 'error'))
21
+ `);
22
+ },
23
+ } satisfies Migration;
@@ -0,0 +1,35 @@
1
+ # Migrations
2
+
3
+ One file per migration. To add one, add a file here and edit nothing else.
4
+
5
+ Name the file with a UTC timestamp key, `YYYYMMDDTHHMM_snake_name.ts`
6
+ (e.g. `20261014T0130_share_links.ts`), and export the migration as its default.
7
+ The key in the file must equal the file name:
8
+
9
+ ```ts
10
+ import type { Migration } from "../migration-types";
11
+
12
+ export default {
13
+ key: "20261014T0130_share_links",
14
+ up: (db) => {
15
+ db.exec(`CREATE TABLE IF NOT EXISTS share_links (...)`);
16
+ },
17
+ } satisfies Migration;
18
+ ```
19
+
20
+ - Never edit a migration that has shipped; add a new one. Existing databases
21
+ have already recorded it and will not run it again.
22
+ - `0001_<name>.ts` .. `0089_<name>.ts` are the legacy numbered migrations
23
+ (`{ id, name, up }`). They run first, by number, and never change. Do not add
24
+ numbered ones: two tasks picking the same number collide.
25
+ - Timestamp keys run after the legacy ones, in key order.
26
+ - Keep each `up` safe to repeat (`IF NOT EXISTS`, check `PRAGMA table_info`
27
+ before `ADD COLUMN`).
28
+ - `lib/db/migrations.generated.ts` lists these files. It is built by
29
+ `scripts/gen-migrations-index.mjs`, which npm runs before dev, start, build,
30
+ typecheck, lint and test, and it is not committed. Never edit it. If an import
31
+ of it fails, run `node scripts/gen-migrations-index.mjs`.
32
+ - A duplicate id or key, or a file whose name does not match its id, throws when
33
+ `lib/db/migrations.ts` loads. `lib/db/migrations.test.ts` also checks order, the
34
+ legacy bodies, and that a database migrated before the split would run nothing
35
+ again.
@@ -43,20 +43,20 @@
43
43
  "42": "410ef4562a046ce6",
44
44
  "43": "b0fca4e05ff6499e",
45
45
  "44": "06b7f4dc636c9675",
46
- "45": "73948f256c522959",
46
+ "45": "8b09dff1aab56f50",
47
47
  "46": "202f8fd102ef2581",
48
48
  "47": "510435d6afa487cd",
49
49
  "48": "10727869e8c8dea1",
50
50
  "49": "0384a86395f6650a",
51
- "50": "cc05c3663ca18b2b",
51
+ "50": "7b8c394c61aa997e",
52
52
  "51": "b381268fe096fb02",
53
53
  "52": "281ad10453d9177a",
54
54
  "53": "a7a53074ed49a566",
55
55
  "54": "2358b106da3e7b68",
56
56
  "55": "478ece72deb01953",
57
- "56": "d04bafcaa2eda941",
57
+ "56": "09adb47c6c996660",
58
58
  "57": "0f9bc09a7a31f64d",
59
- "58": "ccbf7eebccadfa3f",
59
+ "58": "2deb463333462eb3",
60
60
  "59": "1859ac87155d546b",
61
61
  "60": "035e204a4b6c457e",
62
62
  "61": "85d9bb65b051409a",
@@ -76,15 +76,16 @@
76
76
  "75": "e082428a046f9e6b",
77
77
  "76": "17f3160630d658dc",
78
78
  "77": "cd54bfd73fbcd168",
79
- "78": "8ad44a982b1eb58d",
79
+ "78": "06e3421d1c7afbb4",
80
80
  "79": "0b09d57d6ea8ce0a",
81
- "80": "941d85f2ef8bd012",
82
- "81": "ecd85de2bdf45ff1",
81
+ "80": "8d5fdff7cbb2af1e",
82
+ "81": "b029136a954b8b13",
83
83
  "82": "07f11cdac5f8444a",
84
84
  "83": "eabf72b6a8616eac",
85
85
  "84": "1ed4fb30b7435f28",
86
86
  "85": "a85ac25c0e34f2fb",
87
87
  "86": "50b5725a67286dee",
88
88
  "87": "6024c62bcdbb186e",
89
- "88": "bb58ec622ae398e5"
89
+ "88": "bb58ec622ae398e5",
90
+ "89": "6df1801b9eceff58"
90
91
  }