@saadnvd1/agent-os 0.3.596 → 0.3.597
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/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/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/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,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;
|
|
@@ -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;
|