@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,19 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 62,
5
+ name: "sessions_quick_of",
6
+ up: (db) => {
7
+ // A quick change (orchestrator/quick.ts): a task row with no agent,
8
+ // whose worktree the orchestrator with this session id edits itself.
9
+ // Set only by quick_change; the code-review gate reads it.
10
+ const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
11
+ name: string;
12
+ }[];
13
+ if (!columns.some((c) => c.name === "quick_of"))
14
+ db.exec(`ALTER TABLE sessions ADD COLUMN quick_of TEXT`);
15
+ db.exec(
16
+ `CREATE INDEX IF NOT EXISTS idx_sessions_quick_of ON sessions(quick_of) WHERE quick_of IS NOT NULL`
17
+ );
18
+ },
19
+ } satisfies Migration;
@@ -0,0 +1,16 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 63,
5
+ name: "one_off_schedules",
6
+ up: (db) =>
7
+ db.transaction(() => {
8
+ // A one-off has a run time (epoch ms, UTC) and no cron; done_at is
9
+ // set once its single run settles. Recurring rows leave both NULL.
10
+ db.exec(`ALTER TABLE schedules ADD COLUMN run_at INTEGER`);
11
+ db.exec(`ALTER TABLE schedules ADD COLUMN done_at INTEGER`);
12
+ db.exec(
13
+ `CREATE INDEX IF NOT EXISTS idx_schedules_run_at ON schedules(run_at) WHERE run_at IS NOT NULL`
14
+ );
15
+ })(),
16
+ } satisfies Migration;
@@ -0,0 +1,15 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 64,
5
+ name: "chat_compactions_phase",
6
+ up: (db) => {
7
+ // Whether the agent compacted itself mid-turn or AgentOS (or a typed
8
+ // /compact) did between turns; null on rows from before.
9
+ const columns = db.prepare(`PRAGMA table_info(chat_compactions)`).all() as {
10
+ name: string;
11
+ }[];
12
+ if (!columns.some((c) => c.name === "phase"))
13
+ db.exec(`ALTER TABLE chat_compactions ADD COLUMN phase TEXT`);
14
+ },
15
+ } satisfies Migration;
@@ -0,0 +1,23 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 65,
5
+ name: "orchestrator_triage",
6
+ up: (db) => {
7
+ // started_by: the orchestrator session that started this one (null: a
8
+ // person). plan_decided: 1 planned, 0 no plan, null not stated.
9
+ const add = (table: string, col: string, type: string) => {
10
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all() as {
11
+ name: string;
12
+ }[];
13
+ if (!columns.some((c) => c.name === col))
14
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${col} ${type}`);
15
+ };
16
+ add("sessions", "started_by", "TEXT");
17
+ add("sessions", "plan_decided", "INTEGER");
18
+ add("sessions", "plan_reason", "TEXT");
19
+ add("task_queue", "plan", "INTEGER");
20
+ add("task_queue", "plan_reason", "TEXT");
21
+ add("task_queue", "started_by", "TEXT");
22
+ },
23
+ } satisfies Migration;
@@ -0,0 +1,16 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 66,
5
+ name: "workspace_orch_mcp_servers",
6
+ up: (db) => {
7
+ // The MCP servers (by name) a workspace's orchestrator may use.
8
+ const columns = db.prepare(`PRAGMA table_info(workspaces)`).all() as {
9
+ name: string;
10
+ }[];
11
+ if (!columns.some((c) => c.name === "orch_mcp_servers"))
12
+ db.exec(
13
+ `ALTER TABLE workspaces ADD COLUMN orch_mcp_servers TEXT NOT NULL DEFAULT '["linear"]'`
14
+ );
15
+ },
16
+ } satisfies Migration;
@@ -0,0 +1,14 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 67,
5
+ name: "orchestrator_event_log_suppressed",
6
+ up: (db) => {
7
+ // A capped event is logged as suppressed so a drop leaves a trace.
8
+ const columns = db
9
+ .prepare(`PRAGMA table_info(orchestrator_event_log)`)
10
+ .all() as { name: string }[];
11
+ if (!columns.some((c) => c.name === "suppressed"))
12
+ db.exec(`ALTER TABLE orchestrator_event_log ADD COLUMN suppressed TEXT`);
13
+ },
14
+ } satisfies Migration;
@@ -0,0 +1,74 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 68,
5
+ name: "usage_calls_and_rollups",
6
+ up: (db) => {
7
+ // One row per Claude response (or per one-shot run), the daily rollups
8
+ // computed from them, the ratchet's baseline and the limit readings.
9
+ db.transaction(() => {
10
+ db.exec(`
11
+ CREATE TABLE IF NOT EXISTS usage_calls (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ call_key TEXT NOT NULL UNIQUE,
14
+ at INTEGER NOT NULL,
15
+ session_id TEXT,
16
+ task_id TEXT,
17
+ workspace_id TEXT,
18
+ project_id TEXT,
19
+ session_name TEXT,
20
+ role TEXT,
21
+ kind TEXT NOT NULL,
22
+ agent_type TEXT,
23
+ model TEXT,
24
+ provider TEXT,
25
+ input_tokens INTEGER NOT NULL DEFAULT 0,
26
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
27
+ cache_write_5m_tokens INTEGER NOT NULL DEFAULT 0,
28
+ cache_write_1h_tokens INTEGER NOT NULL DEFAULT 0,
29
+ output_tokens INTEGER NOT NULL DEFAULT 0,
30
+ ctx_tokens INTEGER,
31
+ source TEXT NOT NULL
32
+ );
33
+ CREATE INDEX IF NOT EXISTS idx_usage_calls_at ON usage_calls(at);
34
+ CREATE INDEX IF NOT EXISTS idx_usage_calls_task ON usage_calls(task_id);
35
+ CREATE INDEX IF NOT EXISTS idx_usage_calls_session ON usage_calls(session_id);
36
+ CREATE TABLE IF NOT EXISTS rate_limit_readings (
37
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+ at INTEGER NOT NULL,
39
+ window TEXT NOT NULL,
40
+ utilization REAL NOT NULL,
41
+ raw REAL,
42
+ resets_at INTEGER,
43
+ status TEXT,
44
+ session_id TEXT,
45
+ source TEXT NOT NULL
46
+ );
47
+ CREATE INDEX IF NOT EXISTS idx_rate_limit_readings ON rate_limit_readings(window, at);
48
+ CREATE TABLE IF NOT EXISTS usage_rollups (
49
+ day TEXT PRIMARY KEY,
50
+ data TEXT NOT NULL,
51
+ final INTEGER NOT NULL DEFAULT 0,
52
+ computed_at INTEGER NOT NULL
53
+ );
54
+ CREATE TABLE IF NOT EXISTS usage_baseline (
55
+ metric TEXT PRIMARY KEY,
56
+ value REAL NOT NULL,
57
+ set_at INTEGER NOT NULL,
58
+ supporting TEXT
59
+ );
60
+ CREATE TABLE IF NOT EXISTS usage_alerts (
61
+ kind TEXT PRIMARY KEY,
62
+ opened_at INTEGER NOT NULL,
63
+ cleared_at INTEGER,
64
+ message TEXT NOT NULL
65
+ );
66
+ CREATE TABLE IF NOT EXISTS usage_import_offsets (
67
+ path TEXT PRIMARY KEY,
68
+ offset INTEGER NOT NULL,
69
+ mtime INTEGER NOT NULL
70
+ );
71
+ `);
72
+ })();
73
+ },
74
+ } satisfies Migration;
@@ -0,0 +1,26 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 69,
5
+ name: "session_logs",
6
+ up: (db) => {
7
+ // One log per merged task, written by AgentOS from data it already
8
+ // holds (lib/session-logs). Never deleted: the database is the record,
9
+ // the markdown file on disk is a copy.
10
+ db.exec(`
11
+ CREATE TABLE IF NOT EXISTS session_logs (
12
+ session_id TEXT PRIMARY KEY,
13
+ project_id TEXT,
14
+ workspace_id TEXT,
15
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
16
+ markdown TEXT NOT NULL,
17
+ data TEXT NOT NULL,
18
+ file_path TEXT
19
+ );
20
+ CREATE INDEX IF NOT EXISTS idx_session_logs_project
21
+ ON session_logs(project_id, created_at);
22
+ CREATE INDEX IF NOT EXISTS idx_session_logs_workspace
23
+ ON session_logs(workspace_id, created_at);
24
+ `);
25
+ },
26
+ } satisfies Migration;
@@ -0,0 +1,15 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 70,
5
+ name: "chat_items_running",
6
+ up: (db) => {
7
+ // What is still running (tools, background tasks) is read on every
8
+ // attach; the index holds only those rows, so it's a lookup, not a
9
+ // scan that parses every tool output of a long conversation.
10
+ db.exec(
11
+ `CREATE INDEX IF NOT EXISTS idx_chat_items_running ON chat_items(session_id, kind, seq)
12
+ WHERE json_extract(data, '$.status') = 'running'`
13
+ );
14
+ },
15
+ } satisfies Migration;
@@ -0,0 +1,29 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 71,
5
+ name: "devices_push",
6
+ up: (db) => {
7
+ // Push belongs to a paired device, so revoking the device clears it.
8
+ // push_categories is a JSON array; NULL means the defaults.
9
+ // Each column checked, so a run cut off part way can be run again.
10
+ const have = new Set(
11
+ (
12
+ db.prepare(`PRAGMA table_info(devices)`).all() as { name: string }[]
13
+ ).map((c) => c.name)
14
+ );
15
+ for (const col of [
16
+ "push_token",
17
+ "push_platform",
18
+ "push_environment",
19
+ "push_categories",
20
+ "push_updated_at",
21
+ "push_last_error",
22
+ ])
23
+ if (!have.has(col)) db.exec(`ALTER TABLE devices ADD COLUMN ${col} TEXT`);
24
+ db.exec(
25
+ `CREATE INDEX IF NOT EXISTS idx_devices_push_token ON devices(push_token)
26
+ WHERE push_token IS NOT NULL`
27
+ );
28
+ },
29
+ } satisfies Migration;
@@ -0,0 +1,33 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 72,
5
+ name: "push_outbox",
6
+ up: (db) => {
7
+ // One row per (event, device). device_id is '' for a row folded into
8
+ // the Away digest. state: pending, sending, sent, failed, gone,
9
+ // capped, superseded, folded, digested.
10
+ db.exec(`
11
+ CREATE TABLE IF NOT EXISTS push_outbox (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ event_key TEXT NOT NULL,
14
+ device_id TEXT NOT NULL,
15
+ category TEXT NOT NULL,
16
+ level TEXT NOT NULL,
17
+ payload TEXT NOT NULL,
18
+ collapse_id TEXT,
19
+ state TEXT NOT NULL,
20
+ attempts INTEGER NOT NULL DEFAULT 0,
21
+ next_attempt_at INTEGER NOT NULL,
22
+ created_at INTEGER NOT NULL,
23
+ sent_at INTEGER,
24
+ error TEXT,
25
+ fallback_source TEXT,
26
+ fallback_text TEXT,
27
+ UNIQUE(event_key, device_id)
28
+ );
29
+ CREATE INDEX IF NOT EXISTS idx_push_outbox_due ON push_outbox(state, next_attempt_at);
30
+ CREATE INDEX IF NOT EXISTS idx_push_outbox_device ON push_outbox(device_id, sent_at);
31
+ `);
32
+ },
33
+ } satisfies Migration;
@@ -0,0 +1,27 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 73,
5
+ name: "orchestrated_task_moves",
6
+ up: (db) => {
7
+ // task_since: when the task first started, kept across moves (null:
8
+ // created_at). held_by_device / held_from: the device and machine of
9
+ // the orchestrator that moved this task here; only that device may
10
+ // merge, arm, message or move it. plan_note: the "plan written" line
11
+ // of a held task, for its orchestrator to read.
12
+ const add = (col: string) => {
13
+ const columns = db.prepare(`PRAGMA table_info(sessions)`).all() as {
14
+ name: string;
15
+ }[];
16
+ if (!columns.some((c) => c.name === col))
17
+ db.exec(`ALTER TABLE sessions ADD COLUMN ${col} TEXT`);
18
+ };
19
+ for (const col of [
20
+ "task_since",
21
+ "held_by_device",
22
+ "held_from",
23
+ "plan_note",
24
+ ])
25
+ add(col);
26
+ },
27
+ } satisfies Migration;
@@ -0,0 +1,27 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 74,
5
+ name: "held_task_replies",
6
+ up: (db) => {
7
+ // held_for / held_for_name: the orchestrator (id, name there) a held
8
+ // task messages. relay_state: a held task's message to it, waiting for
9
+ // the holder to pull it (pending) or pulled (relayed). relay_key: a
10
+ // pulled message's key on the orchestrator's machine, so a repeat pull
11
+ // is not delivered twice.
12
+ const has = (table: string, col: string) =>
13
+ (
14
+ db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[]
15
+ ).some((c) => c.name === col);
16
+ for (const col of ["held_for", "held_for_name"])
17
+ if (!has("sessions", col))
18
+ db.exec(`ALTER TABLE sessions ADD COLUMN ${col} TEXT`);
19
+ for (const col of ["relay_state", "relay_key"])
20
+ if (!has("bus_messages", col))
21
+ db.exec(`ALTER TABLE bus_messages ADD COLUMN ${col} TEXT`);
22
+ db.exec(
23
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_bus_relay_key ON bus_messages(relay_key) WHERE relay_key IS NOT NULL;
24
+ CREATE INDEX IF NOT EXISTS idx_bus_relay_pending ON bus_messages(relay_state) WHERE relay_state = 'pending'`
25
+ );
26
+ },
27
+ } satisfies Migration;
@@ -0,0 +1,17 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 75,
5
+ name: "plan_only_tasks",
6
+ up: (db) => {
7
+ // plan_only: 1 for a task that stops after its plan and waits for
8
+ // approval (plan_phase 'awaiting'); queued tasks keep it until they start.
9
+ const has = (table: string) =>
10
+ (
11
+ db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[]
12
+ ).some((c) => c.name === "plan_only");
13
+ for (const table of ["sessions", "task_queue"])
14
+ if (!has(table))
15
+ db.exec(`ALTER TABLE ${table} ADD COLUMN plan_only INTEGER`);
16
+ },
17
+ } satisfies Migration;
@@ -0,0 +1,16 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 76,
5
+ name: "welcome_backfill",
6
+ up: (db) => {
7
+ // An install that already has a session or a project of its own has
8
+ // been set up; mark it so the first-run welcome never shows there.
9
+ db.exec(
10
+ `INSERT OR IGNORE INTO settings (key, value)
11
+ SELECT 'welcome.completed_at', 'existing-install'
12
+ WHERE EXISTS (SELECT 1 FROM sessions)
13
+ OR EXISTS (SELECT 1 FROM projects WHERE is_uncategorized = 0)`
14
+ );
15
+ },
16
+ } satisfies Migration;
@@ -0,0 +1,21 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 77,
5
+ name: "simulator_leases",
6
+ up: (db) => {
7
+ // One iOS simulator a task holds (lib/simulator): the device AgentOS
8
+ // made for it, and where it is in coming up.
9
+ db.exec(
10
+ `CREATE TABLE IF NOT EXISTS simulator_leases (
11
+ session_id TEXT PRIMARY KEY,
12
+ udid TEXT,
13
+ name TEXT NOT NULL,
14
+ state TEXT NOT NULL,
15
+ error TEXT,
16
+ created_at INTEGER NOT NULL,
17
+ last_used_at INTEGER NOT NULL
18
+ )`
19
+ );
20
+ },
21
+ } satisfies Migration;
@@ -0,0 +1,34 @@
1
+ import type { Migration } from "../migration-types";
2
+ import { installChangeTriggers, TABLES_WATCHED_78 } from "../changes";
3
+
4
+ export default {
5
+ id: 78,
6
+ name: "orchestrator_questions",
7
+ up: (db) => {
8
+ // A question the orchestrator put to Saad in chat, open until he
9
+ // answers it or the orchestrator clears it. needs_you_dismissed: items
10
+ // he hid from the Needs-you strip (an ask's or question's key); hiding
11
+ // never resolves anything.
12
+ db.exec(`
13
+ CREATE TABLE IF NOT EXISTS orchestrator_questions (
14
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15
+ workspace_id TEXT NOT NULL,
16
+ text TEXT NOT NULL,
17
+ task TEXT,
18
+ status TEXT NOT NULL DEFAULT 'open',
19
+ resolved_by TEXT,
20
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
21
+ resolved_at TEXT
22
+ );
23
+ CREATE INDEX IF NOT EXISTS idx_orchestrator_questions_open
24
+ ON orchestrator_questions(workspace_id, status);
25
+ CREATE TABLE IF NOT EXISTS needs_you_dismissed (
26
+ workspace_id TEXT NOT NULL,
27
+ key TEXT NOT NULL,
28
+ dismissed_at TEXT NOT NULL DEFAULT (datetime('now')),
29
+ PRIMARY KEY (workspace_id, key)
30
+ );
31
+ `);
32
+ installChangeTriggers(db, TABLES_WATCHED_78);
33
+ },
34
+ } satisfies Migration;
@@ -0,0 +1,13 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 79,
5
+ name: "task_queue_agent",
6
+ up: (db) => {
7
+ // The agent CLI a queued task runs with; null is Claude Code.
8
+ const has = (
9
+ db.prepare(`PRAGMA table_info(task_queue)`).all() as { name: string }[]
10
+ ).some((c) => c.name === "agent_type");
11
+ if (!has) db.exec(`ALTER TABLE task_queue ADD COLUMN agent_type TEXT`);
12
+ },
13
+ } satisfies Migration;
@@ -0,0 +1,129 @@
1
+ import type { Migration } from "../migration-types";
2
+ import { installChangeTriggers, TABLES_WATCHED_80 } from "../changes";
3
+
4
+ export default {
5
+ id: 80,
6
+ name: "recipes",
7
+ up: (db) => {
8
+ // Recipes: a saved brief that starts a fresh task or session each run
9
+ // (lib/recipes), and the features a plan splits into pieces
10
+ // (lib/plans). All additive. A run or piece finds its session by
11
+ // session_id, so sessions gains no column.
12
+ db.exec(`
13
+ CREATE TABLE IF NOT EXISTS recipes (
14
+ id TEXT PRIMARY KEY,
15
+ workspace_id TEXT NOT NULL,
16
+ project_id TEXT NOT NULL,
17
+ name TEXT NOT NULL,
18
+ kind TEXT NOT NULL,
19
+ brief TEXT NOT NULL,
20
+ model TEXT,
21
+ view TEXT NOT NULL DEFAULT 'chat',
22
+ auto_done INTEGER NOT NULL DEFAULT 1,
23
+ max_minutes INTEGER,
24
+ carry_cleared_at INTEGER,
25
+ source_session_id TEXT,
26
+ created_by_session_id TEXT,
27
+ updated_by_session_id TEXT,
28
+ archived_at TEXT,
29
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
30
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
31
+ );
32
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_recipes_name
33
+ ON recipes(workspace_id, name COLLATE NOCASE) WHERE archived_at IS NULL;
34
+
35
+ CREATE TABLE IF NOT EXISTS recipe_runs (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ recipe_id TEXT NOT NULL,
38
+ seq INTEGER NOT NULL,
39
+ trigger TEXT NOT NULL,
40
+ triggered_by_session_id TEXT,
41
+ schedule_run_id INTEGER,
42
+ request_id TEXT,
43
+ note TEXT,
44
+ prompt TEXT NOT NULL,
45
+ revision_id INTEGER,
46
+ status TEXT NOT NULL,
47
+ session_id TEXT,
48
+ pr_number INTEGER,
49
+ pr_url TEXT,
50
+ summary TEXT,
51
+ outputs TEXT,
52
+ summary_source TEXT,
53
+ error TEXT,
54
+ cost_usd REAL,
55
+ duration_ms INTEGER,
56
+ started_at TEXT,
57
+ ended_at TEXT,
58
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
59
+ UNIQUE (recipe_id, seq)
60
+ );
61
+ CREATE INDEX IF NOT EXISTS idx_recipe_runs_recipe
62
+ ON recipe_runs(recipe_id, id DESC);
63
+ CREATE INDEX IF NOT EXISTS idx_recipe_runs_session
64
+ ON recipe_runs(session_id);
65
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_recipe_runs_one_live
66
+ ON recipe_runs(recipe_id)
67
+ WHERE status IN ('queued','starting','running');
68
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_recipe_runs_request
69
+ ON recipe_runs(request_id) WHERE request_id IS NOT NULL;
70
+
71
+ CREATE TABLE IF NOT EXISTS recipe_revisions (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ recipe_id TEXT NOT NULL,
74
+ brief TEXT NOT NULL,
75
+ settings TEXT NOT NULL,
76
+ edited_by_session_id TEXT,
77
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
78
+ );
79
+
80
+ CREATE TABLE IF NOT EXISTS features (
81
+ id TEXT PRIMARY KEY,
82
+ workspace_id TEXT NOT NULL,
83
+ name TEXT NOT NULL,
84
+ plan_session_id TEXT NOT NULL,
85
+ plan_text TEXT NOT NULL,
86
+ status TEXT NOT NULL,
87
+ done_at TEXT,
88
+ cost_usd REAL,
89
+ duration_ms INTEGER,
90
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
91
+ );
92
+ CREATE INDEX IF NOT EXISTS idx_features_plan
93
+ ON features(plan_session_id);
94
+
95
+ CREATE TABLE IF NOT EXISTS feature_pieces (
96
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
97
+ feature_id TEXT NOT NULL,
98
+ position INTEGER NOT NULL,
99
+ name TEXT NOT NULL,
100
+ slice TEXT NOT NULL,
101
+ project_id TEXT NOT NULL,
102
+ after_position INTEGER,
103
+ after_kind TEXT,
104
+ reasons TEXT,
105
+ owns TEXT,
106
+ status TEXT NOT NULL,
107
+ session_id TEXT,
108
+ note TEXT,
109
+ UNIQUE (feature_id, position)
110
+ );
111
+ CREATE INDEX IF NOT EXISTS idx_feature_pieces_session
112
+ ON feature_pieces(session_id);
113
+ `);
114
+ const cols = (
115
+ db.prepare(`PRAGMA table_info(schedules)`).all() as { name: string }[]
116
+ ).map((c) => c.name);
117
+ if (!cols.includes("recipe_id"))
118
+ db.exec(`ALTER TABLE schedules ADD COLUMN recipe_id TEXT`);
119
+ if (!cols.includes("migrated_from_kind"))
120
+ db.exec(`ALTER TABLE schedules ADD COLUMN migrated_from_kind TEXT`);
121
+ const plan = (
122
+ db.prepare(`PRAGMA table_info(sessions)`).all() as { name: string }[]
123
+ ).map((c) => c.name);
124
+ // Edits Saad made to a plan's pieces before approving it (merge, drop).
125
+ if (!plan.includes("plan_edits"))
126
+ db.exec(`ALTER TABLE sessions ADD COLUMN plan_edits TEXT`);
127
+ installChangeTriggers(db, TABLES_WATCHED_80);
128
+ },
129
+ } satisfies Migration;
@@ -0,0 +1,18 @@
1
+ import type { Migration } from "../migration-types";
2
+ import { moveSchedulesToRecipes } from "../schedules-move";
3
+
4
+ export default {
5
+ id: 81,
6
+ name: "schedules_into_recipes",
7
+ up: (db) => {
8
+ // Live task and session schedules become recipes with a timer, and
9
+ // their runs become recipe runs (lib/db/schedules-move). Reversible:
10
+ // scripts/revert-schedules-move.ts.
11
+ const cols = (
12
+ db.prepare(`PRAGMA table_info(schedules)`).all() as { name: string }[]
13
+ ).map((c) => c.name);
14
+ if (!cols.includes("recipe_id") || !cols.includes("migrated_from_kind"))
15
+ throw new Error("migration 80 hasn't run");
16
+ moveSchedulesToRecipes(db);
17
+ },
18
+ } satisfies Migration;
@@ -0,0 +1,23 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 82,
5
+ name: "session_shelf_times",
6
+ up: (db) => {
7
+ const cols = (
8
+ db.prepare(`PRAGMA table_info(sessions)`).all() as { name: string }[]
9
+ ).map((c) => c.name);
10
+ // Last real activity, when it was marked done, and the snooze.
11
+ for (const col of [
12
+ "active_at",
13
+ "settled_at",
14
+ "snoozed_until",
15
+ "snoozed_at",
16
+ ])
17
+ if (!cols.includes(col))
18
+ db.exec(`ALTER TABLE sessions ADD COLUMN ${col} TEXT`);
19
+ db.exec(
20
+ `UPDATE sessions SET active_at = updated_at WHERE active_at IS NULL`
21
+ );
22
+ },
23
+ } satisfies Migration;
@@ -0,0 +1,18 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 83,
5
+ name: "native_devices_can_approve",
6
+ up: (db) => {
7
+ // New native pairings start with Can approve; this gives it once to the
8
+ // ones paired before. The claim wasn't recorded, so the app is known by
9
+ // its user agent (`AgentOS/<build> CFNetwork/...` on iOS) or by an
10
+ // app push token. It only ever turns the flag on.
11
+ db.exec(
12
+ `UPDATE devices SET can_approve = 1
13
+ WHERE revoked_at IS NULL AND can_approve = 0
14
+ AND (user_agent LIKE 'AgentOS/% CFNetwork/%'
15
+ OR push_platform IN ('ios', 'android'))`
16
+ );
17
+ },
18
+ } satisfies Migration;
@@ -0,0 +1,20 @@
1
+ import type { Migration } from "../migration-types";
2
+
3
+ export default {
4
+ id: 84,
5
+ name: "approval_audit",
6
+ up: (db) => {
7
+ // Every approval No approvals mode made in place of a person.
8
+ db.exec(`
9
+ CREATE TABLE IF NOT EXISTS approval_audit (
10
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11
+ at TEXT NOT NULL DEFAULT (datetime('now')),
12
+ source TEXT NOT NULL,
13
+ workspace_id TEXT,
14
+ session_id TEXT,
15
+ subject TEXT NOT NULL,
16
+ detail TEXT NOT NULL DEFAULT ''
17
+ )
18
+ `);
19
+ },
20
+ } satisfies Migration;