@prismshadow/penguin-server 0.2.15 → 0.2.17
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/README.md +1 -1
- package/dist/api/types.d.ts +318 -86
- package/dist/api/types.js +9 -3
- package/dist/auth-token.d.ts +1 -1
- package/dist/auth-token.js +2 -2
- package/dist/{chunk-HMTT2PX4.js → chunk-2LZDP26O.js} +8 -5
- package/dist/chunk-2LZDP26O.js.map +1 -0
- package/dist/{chunk-LPPVGHYD.js → chunk-BL4U7GYY.js} +2 -2
- package/dist/chunk-BL4U7GYY.js.map +1 -0
- package/dist/{chunk-5Z4UCHDB.js → chunk-D3JNA6ZI.js} +69 -6
- package/dist/chunk-D3JNA6ZI.js.map +1 -0
- package/dist/{chunk-TOOTEEM4.js → chunk-G7GY7PJU.js} +1 -1
- package/dist/chunk-G7GY7PJU.js.map +1 -0
- package/dist/chunk-GGIINEEK.js +3401 -0
- package/dist/chunk-GGIINEEK.js.map +1 -0
- package/dist/chunk-HDBZRVA5.js +29 -0
- package/dist/chunk-HDBZRVA5.js.map +1 -0
- package/dist/{chunk-NVWAYYRX.js → chunk-HPOHMZCH.js} +686 -531
- package/dist/chunk-HPOHMZCH.js.map +1 -0
- package/dist/{chunk-QFV3ERQO.js → chunk-OQZDL3TR.js} +2 -2
- package/dist/hmr/manifest.js +1 -1
- package/dist/index.js +9351 -5551
- package/dist/index.js.map +1 -1
- package/dist/machine-status.js +2 -2
- package/dist/model-catalog.d.ts +610 -0
- package/dist/model-catalog.js +108 -0
- package/dist/model-catalog.js.map +1 -0
- package/dist/plugin/index.d.ts +141 -78
- package/dist/reset-admin-password.js +3 -4
- package/dist/reset-admin-password.js.map +1 -1
- package/dist/version-report.js +2 -2
- package/package.json +15 -14
- package/web-dist/assets/{abnfDiagram-VCTEODGH-CKWh4hoA.js → abnfDiagram-VCTEODGH-DMzkb4l4.js} +1 -1
- package/web-dist/assets/{addon-clipboard-DxLqat7q.js → addon-clipboard-BtD2BVSi.js} +1 -1
- package/web-dist/assets/{addon-fit-B8oPGPkW.js → addon-fit-Dr6HWsi_.js} +1 -1
- package/web-dist/assets/{addon-web-links-C0W5TcK0.js → addon-web-links-VEE3YLzQ.js} +1 -1
- package/web-dist/assets/{arc-Dp9EkaAH.js → arc-CvMPOU_B.js} +1 -1
- package/web-dist/assets/{architectureDiagram-5GKGNRK7-BQ3LUcle.js → architectureDiagram-5GKGNRK7-Bk65mIBz.js} +1 -1
- package/web-dist/assets/{blockDiagram-I7D4REHJ-CZzyU0Jj.js → blockDiagram-I7D4REHJ-D54-XSNs.js} +1 -1
- package/web-dist/assets/{c4Diagram-7LVT6UL2-Qps3jeAu.js → c4Diagram-7LVT6UL2-Dm954LSt.js} +1 -1
- package/web-dist/assets/channel-CVHI75kz.js +1 -0
- package/web-dist/assets/{chunk-2Q5K7J3B-CQMntzuX.js → chunk-2Q5K7J3B-D_LJ_7Cn.js} +1 -1
- package/web-dist/assets/{chunk-5VM5RSS4-2H2ZJAte.js → chunk-5VM5RSS4-B8RI4tl3.js} +1 -1
- package/web-dist/assets/{chunk-F27PBJKO-B30xCNBv.js → chunk-F27PBJKO-aVSYH9JV.js} +1 -1
- package/web-dist/assets/{chunk-IMKFNOWR-Cyfx4zDs.js → chunk-IMKFNOWR-DnkdWeq2.js} +1 -1
- package/web-dist/assets/{chunk-JWPE2WC7-DADUytTh.js → chunk-JWPE2WC7-C2pJzKuW.js} +1 -1
- package/web-dist/assets/{chunk-POPQ4Y6H-9bjsAxLS.js → chunk-POPQ4Y6H-Bx7Iikzx.js} +1 -1
- package/web-dist/assets/{chunk-SVP7TREG-B9xO1VpN.js → chunk-SVP7TREG-qJC2SMqr.js} +1 -1
- package/web-dist/assets/{chunk-TICWLB2K-B6oDkrep.js → chunk-TICWLB2K-Crl_BgKI.js} +1 -1
- package/web-dist/assets/{chunk-XXDRQBXY-BbU8DR4B.js → chunk-XXDRQBXY-BFFemuGm.js} +1 -1
- package/web-dist/assets/classDiagram-ZZMXUADV-CBdzAPFu.js +1 -0
- package/web-dist/assets/classDiagram-v2-VYDZK3BY-CBdzAPFu.js +1 -0
- package/web-dist/assets/{cose-bilkent-JH36ORCC-pkbuyec-.js → cose-bilkent-JH36ORCC-Bmh2Kx7h.js} +1 -1
- package/web-dist/assets/{cynefin-OW5HDTMX-DU9YQQnc.js → cynefin-OW5HDTMX-BveNWnUV.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-5FMLGOSQ-3a3XuhQq.js → cynefinDiagram-5FMLGOSQ-85itazbJ.js} +1 -1
- package/web-dist/assets/{dagre-GXQ25YYZ-BfZ9C5CZ.js → dagre-GXQ25YYZ-DIZT-KY_.js} +1 -1
- package/web-dist/assets/{diagram-S7CK7UJ4-BoGhVBMn.js → diagram-S7CK7UJ4-Dum-g63f.js} +1 -1
- package/web-dist/assets/{diagram-UQ7AKVKN-zE67quty.js → diagram-UQ7AKVKN-DallMKJl.js} +1 -1
- package/web-dist/assets/{diagram-VSXAHHWV-NihoXeXm.js → diagram-VSXAHHWV-DWELqxfN.js} +1 -1
- package/web-dist/assets/{diagram-VX7I27RA-1627np0S.js → diagram-VX7I27RA-BNs0gB9E.js} +1 -1
- package/web-dist/assets/{diagram-Z3DM3KII-Ddg_bUan.js → diagram-Z3DM3KII-C9mF9xAd.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-PWID7BFC-CmPUOM4j.js → ebnfDiagram-PWID7BFC-D_Diy9HH.js} +1 -1
- package/web-dist/assets/{erDiagram-RLTQ6QDP-DiTnEK72.js → erDiagram-RLTQ6QDP-4QTS5VIh.js} +1 -1
- package/web-dist/assets/{flowDiagram-HODETNUW-mGQ38tRM.js → flowDiagram-HODETNUW-CliTRrtV.js} +1 -1
- package/web-dist/assets/{ganttDiagram-EL5Y4UJY-DU7MlzpW.js → ganttDiagram-EL5Y4UJY-DCRd-Ss4.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-WWUBYQGX-CRE_3vjl.js → gitGraphDiagram-WWUBYQGX-CTz13GjS.js} +1 -1
- package/web-dist/assets/highlighter-BScH1Ud3.js +2 -0
- package/web-dist/assets/{highlighter-core-B5CvXcEK.js → highlighter-core-DKz8Z6-I.js} +1 -1
- package/web-dist/assets/{highlighter.worker-D6D9fPma.js → highlighter.worker-CKF6ok1g.js} +1 -1
- package/web-dist/assets/index-BUvJ5Ctt.js +1299 -0
- package/web-dist/assets/index-DRj7nvPY.css +1 -0
- package/web-dist/assets/{infoDiagram-27XIBGKW-Cdo6Ar-a.js → infoDiagram-27XIBGKW-DNfTEPAV.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-5VMMS53U-C27pgect.js → ishikawaDiagram-5VMMS53U-B7aCjjG1.js} +1 -1
- package/web-dist/assets/{journeyDiagram-3NMN7TZE-CVxtKpLG.js → journeyDiagram-3NMN7TZE-COmDALVr.js} +1 -1
- package/web-dist/assets/{kanban-definition-UXKFOSKX-DWA31Da7.js → kanban-definition-UXKFOSKX-BzUfPINh.js} +1 -1
- package/web-dist/assets/{layout-DWKCU9PO.js → layout-BwzuPU4M.js} +1 -1
- package/web-dist/assets/{linear-BwCthj_k.js → linear-WPZTYjz3.js} +1 -1
- package/web-dist/assets/mermaid.core-zKzlwpkw.js +310 -0
- package/web-dist/assets/{mindmap-definition-YA3MSWOX-BLEDhbn1.js → mindmap-definition-YA3MSWOX-DCRg7eAx.js} +1 -1
- package/web-dist/assets/{pegDiagram-XKGWAZYB-CLEM1SDU.js → pegDiagram-XKGWAZYB-DyphzDLU.js} +1 -1
- package/web-dist/assets/{pieDiagram-E7YTZNPT-B__g6eF0.js → pieDiagram-E7YTZNPT-BI5DueGA.js} +1 -1
- package/web-dist/assets/powershell-DXrKVo3G.js +1 -0
- package/web-dist/assets/powershell-Dpen1YoG.js +1 -0
- package/web-dist/assets/{quadrantDiagram-AXDQQJYC-C7VQ2LMY.js → quadrantDiagram-AXDQQJYC-GZxSMY2Z.js} +1 -1
- package/web-dist/assets/{railroadDiagram-O6MQD6OU-eWNnzjwS.js → railroadDiagram-O6MQD6OU-DEFCigBf.js} +1 -1
- package/web-dist/assets/{requirementDiagram-BXWQKSXE-Bi5qeD4b.js → requirementDiagram-BXWQKSXE-Br0thc4j.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-P5KCCOFB-DG1VOpXX.js → sankeyDiagram-P5KCCOFB-DYf0pY67.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-WJ2MYXX4-Fhkw73Th.js → sequenceDiagram-WJ2MYXX4-1EFMboX4.js} +1 -1
- package/web-dist/assets/{sizeCapture-INFHLROL-D9E_17SF.js → sizeCapture-INFHLROL-BuOFv-5q.js} +1 -1
- package/web-dist/assets/{stateDiagram-D77RDMKH-Cs5Y0xhW.js → stateDiagram-D77RDMKH-Bg3H1tjX.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-MP3YSRHH-DfaDaPAd.js +1 -0
- package/web-dist/assets/{swimlanes-42K2YHIH-t-EocvKZ.js → swimlanes-42K2YHIH-BuOa6Osb.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-VR7AAH4N-BopAC7b8.js +8 -0
- package/web-dist/assets/{timeline-definition-24CTP7MA-CozGCxuA.js → timeline-definition-24CTP7MA-BDekfdDT.js} +1 -1
- package/web-dist/assets/{vennDiagram-4TSXK5OY-ewU8ycSa.js → vennDiagram-4TSXK5OY-DnC09UI4.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-VM6X3IG4-DuAhDu-0.js → wardleyDiagram-VM6X3IG4-BXKvxuZQ.js} +1 -1
- package/web-dist/assets/{xterm-v5e56GC9.js → xterm-oY4DYg5s.js} +1 -1
- package/web-dist/assets/{xychartDiagram-S5SC5T6Z-BxGuZO23.js → xychartDiagram-S5SC5T6Z-DdDC1VLb.js} +1 -1
- package/web-dist/index.html +12 -6
- package/web-dist/{workflow-ui.css → workbench-ui.css} +72 -67
- package/dist/benchmark-package.d.ts +0 -39
- package/dist/benchmark-package.js +0 -97
- package/dist/benchmark-package.js.map +0 -1
- package/dist/chunk-54E64LWB.js +0 -63
- package/dist/chunk-54E64LWB.js.map +0 -1
- package/dist/chunk-5Z4UCHDB.js.map +0 -1
- package/dist/chunk-HMTT2PX4.js.map +0 -1
- package/dist/chunk-JKJXQTHH.js +0 -19
- package/dist/chunk-JKJXQTHH.js.map +0 -1
- package/dist/chunk-LPPVGHYD.js.map +0 -1
- package/dist/chunk-NVWAYYRX.js.map +0 -1
- package/dist/chunk-Q4ON2L6E.js +0 -52
- package/dist/chunk-Q4ON2L6E.js.map +0 -1
- package/dist/chunk-TOOTEEM4.js.map +0 -1
- package/web-dist/assets/channel--PblgLlC.js +0 -1
- package/web-dist/assets/classDiagram-ZZMXUADV-mn8tbuqa.js +0 -1
- package/web-dist/assets/classDiagram-v2-VYDZK3BY-mn8tbuqa.js +0 -1
- package/web-dist/assets/highlighter-CzfXQ6yd.js +0 -2
- package/web-dist/assets/index-BdG4qRyI.js +0 -1275
- package/web-dist/assets/index-W-xYFoIm.css +0 -1
- package/web-dist/assets/mermaid.core-BcTrmPOv.js +0 -310
- package/web-dist/assets/stateDiagram-v2-MP3YSRHH-CEM51fAf.js +0 -1
- package/web-dist/assets/swimlanesDiagram-VR7AAH4N-BrR66vrK.js +0 -8
- /package/dist/{chunk-QFV3ERQO.js.map → chunk-OQZDL3TR.js.map} +0 -0
|
@@ -2,301 +2,372 @@
|
|
|
2
2
|
import { mkdirSync } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
|
-
// src/db/migrations.ts
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
CREATE INDEX IF NOT EXISTS idx_auth_sessions_user ON auth_sessions(user_id);
|
|
33
|
-
`);
|
|
34
|
-
},
|
|
35
|
-
// LOSES every messaging binding: the channel credentials, the enabled flag and the
|
|
36
|
-
// last-chat memory all live in the table this drops. The auth_sessions indexes are
|
|
37
|
-
// derived and cost nothing to lose.
|
|
38
|
-
down(db) {
|
|
39
|
-
db.exec(`
|
|
40
|
-
DROP INDEX IF EXISTS idx_auth_sessions_user;
|
|
41
|
-
DROP INDEX IF EXISTS idx_auth_sessions_expires;
|
|
42
|
-
DROP INDEX IF EXISTS idx_messaging_by_account;
|
|
43
|
-
DROP TABLE IF EXISTS messaging_bindings;
|
|
44
|
-
`);
|
|
5
|
+
// src/db/migrations/runner.ts
|
|
6
|
+
var LEDGER_DDL = `
|
|
7
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
8
|
+
name TEXT PRIMARY KEY,
|
|
9
|
+
applied_at TEXT NOT NULL
|
|
10
|
+
);
|
|
11
|
+
`;
|
|
12
|
+
function hasLedger(db) {
|
|
13
|
+
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'schema_migrations'").get() !== void 0;
|
|
14
|
+
}
|
|
15
|
+
function appliedMigrations(db) {
|
|
16
|
+
if (!hasLedger(db)) return [];
|
|
17
|
+
const rows = db.prepare("SELECT name FROM schema_migrations ORDER BY rowid").all();
|
|
18
|
+
return rows.map((r) => r.name);
|
|
19
|
+
}
|
|
20
|
+
function migrate(db, migrations, { swapPath = false } = {}) {
|
|
21
|
+
const adopted = !hasLedger(db);
|
|
22
|
+
const done = new Set(appliedMigrations(db));
|
|
23
|
+
const pending = migrations.filter((m) => !done.has(m.name));
|
|
24
|
+
if (adopted) db.exec(LEDGER_DDL);
|
|
25
|
+
const record = db.prepare("INSERT INTO schema_migrations (name, applied_at) VALUES (?, ?)");
|
|
26
|
+
const applied = [];
|
|
27
|
+
const deferred = [];
|
|
28
|
+
for (const m of pending) {
|
|
29
|
+
if (swapPath && !m.swapSafe) {
|
|
30
|
+
deferred.push(m.name);
|
|
31
|
+
continue;
|
|
45
32
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ensureColumn(db, "messaging_bindings", "final_reply_only", "INTEGER NOT NULL DEFAULT 0");
|
|
55
|
-
ensureColumn(db, "messaging_bindings", "render_markdown", "INTEGER NOT NULL DEFAULT 1");
|
|
56
|
-
},
|
|
57
|
-
// LOSES both delivery preferences on every binding; the bindings themselves survive.
|
|
58
|
-
// Dropped in reverse order for symmetry with `up`. Neither column is indexed, which is
|
|
59
|
-
// what lets SQLite drop them at all.
|
|
60
|
-
down(db) {
|
|
61
|
-
db.exec("ALTER TABLE messaging_bindings DROP COLUMN render_markdown");
|
|
62
|
-
db.exec("ALTER TABLE messaging_bindings DROP COLUMN final_reply_only");
|
|
33
|
+
db.exec("BEGIN");
|
|
34
|
+
try {
|
|
35
|
+
m.up(db);
|
|
36
|
+
record.run(m.name, (/* @__PURE__ */ new Date()).toISOString());
|
|
37
|
+
db.exec("COMMIT");
|
|
38
|
+
} catch (err) {
|
|
39
|
+
db.exec("ROLLBACK");
|
|
40
|
+
throw new Error(`migration ${m.name} failed: ${String(err)}`, { cause: err });
|
|
63
41
|
}
|
|
42
|
+
applied.push(m.name);
|
|
43
|
+
}
|
|
44
|
+
return { adopted, applied, deferred };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/db/migrations/steps/2026-08-31-messaging-bindings.ts
|
|
48
|
+
var messagingBindings = {
|
|
49
|
+
name: "2026-08-31-messaging-bindings",
|
|
50
|
+
// Purely additive: one new table plus three indexes, no column of any existing table
|
|
51
|
+
// touched. A platform that rolls back to a predecessor without messaging simply never
|
|
52
|
+
// queries them.
|
|
53
|
+
swapSafe: true,
|
|
54
|
+
up(db) {
|
|
55
|
+
db.exec(`
|
|
56
|
+
CREATE TABLE IF NOT EXISTS messaging_bindings (
|
|
57
|
+
session_id TEXT NOT NULL,
|
|
58
|
+
channel TEXT NOT NULL,
|
|
59
|
+
account_id TEXT NOT NULL,
|
|
60
|
+
config_json TEXT NOT NULL,
|
|
61
|
+
enabled INTEGER NOT NULL DEFAULT 0,
|
|
62
|
+
line_per_message INTEGER NOT NULL DEFAULT 0,
|
|
63
|
+
last_chat_id TEXT,
|
|
64
|
+
last_chat_is_direct INTEGER NOT NULL DEFAULT 1,
|
|
65
|
+
last_inbound_message_id TEXT,
|
|
66
|
+
created_at TEXT NOT NULL,
|
|
67
|
+
updated_at TEXT NOT NULL,
|
|
68
|
+
PRIMARY KEY (session_id, channel)
|
|
69
|
+
);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_messaging_by_account ON messaging_bindings(channel, account_id);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_auth_sessions_expires ON auth_sessions(expires_at);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_auth_sessions_user ON auth_sessions(user_id);
|
|
73
|
+
`);
|
|
64
74
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
project_id TEXT NOT NULL,
|
|
88
|
-
agent_id TEXT NOT NULL,
|
|
89
|
-
objective TEXT NOT NULL,
|
|
90
|
-
status TEXT NOT NULL,
|
|
91
|
-
budget INTEGER NOT NULL,
|
|
92
|
-
used INTEGER NOT NULL DEFAULT 0,
|
|
93
|
-
rounds INTEGER NOT NULL DEFAULT 0,
|
|
94
|
-
created_at TEXT NOT NULL,
|
|
95
|
-
updated_at TEXT NOT NULL
|
|
96
|
-
);
|
|
97
|
-
CREATE INDEX IF NOT EXISTS idx_goal_session ON goal_state(session_id);
|
|
98
|
-
`);
|
|
99
|
-
}
|
|
75
|
+
// LOSES every messaging binding: the channel credentials, the enabled flag and the
|
|
76
|
+
// last-chat memory all live in the table this drops. The auth_sessions indexes are
|
|
77
|
+
// derived and cost nothing to lose.
|
|
78
|
+
down(db) {
|
|
79
|
+
db.exec(`
|
|
80
|
+
DROP INDEX IF EXISTS idx_auth_sessions_user;
|
|
81
|
+
DROP INDEX IF EXISTS idx_auth_sessions_expires;
|
|
82
|
+
DROP INDEX IF EXISTS idx_messaging_by_account;
|
|
83
|
+
DROP TABLE IF EXISTS messaging_bindings;
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/db/migrations/steps/2026-08-31-messaging-delivery-flags.ts
|
|
89
|
+
var messagingDeliveryFlags = {
|
|
90
|
+
name: "2026-08-31-messaging-delivery-flags",
|
|
91
|
+
// Two columns with defaults on an existing table: a rollback to a predecessor that
|
|
92
|
+
// does not know them leaves them at their defaults and reads nothing.
|
|
93
|
+
swapSafe: true,
|
|
94
|
+
up(db) {
|
|
95
|
+
ensureColumn(db, "messaging_bindings", "final_reply_only", "INTEGER NOT NULL DEFAULT 0");
|
|
96
|
+
ensureColumn(db, "messaging_bindings", "render_markdown", "INTEGER NOT NULL DEFAULT 1");
|
|
100
97
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
CREATE TABLE IF NOT EXISTS machine_project (
|
|
124
|
-
project_id TEXT PRIMARY KEY REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
125
|
-
addresses TEXT NOT NULL
|
|
126
|
-
);
|
|
127
|
-
`);
|
|
128
|
-
},
|
|
129
|
-
// LOSES this server's own machine id (every stored reference to it on other machines
|
|
130
|
-
// then points at nothing), what was installed where, the sessions held, and which
|
|
131
|
-
// machines each Project used.
|
|
132
|
-
down(db) {
|
|
133
|
-
db.exec(`
|
|
134
|
-
DROP TABLE IF EXISTS machine_project;
|
|
135
|
-
DROP TABLE IF EXISTS machines;
|
|
136
|
-
DROP TABLE IF EXISTS machine;
|
|
137
|
-
`);
|
|
138
|
-
}
|
|
98
|
+
// LOSES both delivery preferences on every binding; the bindings themselves survive.
|
|
99
|
+
// Dropped in reverse order for symmetry with `up`. Neither column is indexed, which is
|
|
100
|
+
// what lets SQLite drop them at all.
|
|
101
|
+
down(db) {
|
|
102
|
+
db.exec("ALTER TABLE messaging_bindings DROP COLUMN render_markdown");
|
|
103
|
+
db.exec("ALTER TABLE messaging_bindings DROP COLUMN final_reply_only");
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// src/db/migrations/steps/2026-09-02-drop-goal-state.ts
|
|
108
|
+
var dropGoalState = {
|
|
109
|
+
name: "2026-09-02-drop-goal-state",
|
|
110
|
+
// CONTRACT: drops a table no build since 0.2.10 reads. A pushed platform rolled back to
|
|
111
|
+
// 0.2.9 mid-process would prepare its goal statements against a table that is gone, so it
|
|
112
|
+
// is left pending on the swap path and applied by the runtime's own open; until then the
|
|
113
|
+
// table just sits there unused.
|
|
114
|
+
swapSafe: false,
|
|
115
|
+
up(db) {
|
|
116
|
+
db.exec(`
|
|
117
|
+
DROP INDEX IF EXISTS idx_goal_session;
|
|
118
|
+
DROP TABLE IF EXISTS goal_state;
|
|
119
|
+
`);
|
|
139
120
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
121
|
+
// Recreates the table exactly as 0.2.9 declared it — EMPTY. LOSES every goal run ever
|
|
122
|
+
// recorded (objective, status, budget, used, rounds per run): the rows only ever fed the
|
|
123
|
+
// banner of a finished goal, and a build with this migration reads the goal file instead.
|
|
124
|
+
down(db) {
|
|
125
|
+
db.exec(`
|
|
126
|
+
CREATE TABLE IF NOT EXISTS goal_state (
|
|
127
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
128
|
+
session_id TEXT NOT NULL,
|
|
129
|
+
project_id TEXT NOT NULL,
|
|
130
|
+
agent_id TEXT NOT NULL,
|
|
131
|
+
objective TEXT NOT NULL,
|
|
132
|
+
status TEXT NOT NULL,
|
|
133
|
+
budget INTEGER NOT NULL,
|
|
134
|
+
used INTEGER NOT NULL DEFAULT 0,
|
|
135
|
+
rounds INTEGER NOT NULL DEFAULT 0,
|
|
136
|
+
created_at TEXT NOT NULL,
|
|
137
|
+
updated_at TEXT NOT NULL
|
|
138
|
+
);
|
|
139
|
+
CREATE INDEX IF NOT EXISTS idx_goal_session ON goal_state(session_id);
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/db/migrations/steps/2026-09-04-machines.ts
|
|
145
|
+
var machines = {
|
|
146
|
+
name: "2026-09-04-machines",
|
|
147
|
+
// Three new tables, nothing existing touched: a platform rolled back to one without
|
|
148
|
+
// machines never queries them. Swap-safe on its own; a database still behind
|
|
149
|
+
// 2026-09-02-drop-goal-state is refused whole by that one first, which is the rule.
|
|
150
|
+
swapSafe: true,
|
|
151
|
+
up(db) {
|
|
152
|
+
db.exec(`
|
|
153
|
+
CREATE TABLE IF NOT EXISTS machine (
|
|
154
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
155
|
+
machine_id TEXT NOT NULL
|
|
156
|
+
);
|
|
157
|
+
CREATE TABLE IF NOT EXISTS machines (
|
|
158
|
+
address TEXT PRIMARY KEY,
|
|
159
|
+
machine_id TEXT,
|
|
160
|
+
version TEXT,
|
|
161
|
+
installed_at TEXT,
|
|
162
|
+
session_pid INTEGER,
|
|
163
|
+
remote_port INTEGER,
|
|
164
|
+
platform TEXT
|
|
165
|
+
);
|
|
166
|
+
CREATE TABLE IF NOT EXISTS machine_project (
|
|
167
|
+
project_id TEXT PRIMARY KEY REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
168
|
+
addresses TEXT NOT NULL
|
|
169
|
+
);
|
|
170
|
+
`);
|
|
160
171
|
},
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
ticket_id TEXT NOT NULL,
|
|
184
|
-
session_id TEXT NOT NULL,
|
|
185
|
-
agent_id TEXT NOT NULL,
|
|
186
|
-
trigger_hop INTEGER NOT NULL DEFAULT 0,
|
|
187
|
-
PRIMARY KEY (project_id, org_id, ticket_id, session_id)
|
|
188
|
-
);
|
|
189
|
-
CREATE INDEX IF NOT EXISTS idx_org_ticket_sessions_session ON org_ticket_sessions(session_id);
|
|
190
|
-
CREATE TABLE IF NOT EXISTS org_calendar_state ( -- calendar run state (company mode; the files are declarative intent), same rules as schedule_state
|
|
191
|
-
project_id TEXT NOT NULL,
|
|
192
|
-
org_id TEXT NOT NULL,
|
|
193
|
-
agent_id TEXT NOT NULL,
|
|
194
|
-
name TEXT NOT NULL,
|
|
195
|
-
start_at_ms INTEGER NOT NULL,
|
|
196
|
-
def_hash TEXT NOT NULL,
|
|
197
|
-
last_slot_ms INTEGER,
|
|
198
|
-
last_fired_at TEXT,
|
|
199
|
-
fired_once INTEGER NOT NULL DEFAULT 0,
|
|
200
|
-
missed INTEGER NOT NULL DEFAULT 0,
|
|
201
|
-
invalid_reason TEXT,
|
|
202
|
-
last_outcome TEXT,
|
|
203
|
-
PRIMARY KEY (project_id, org_id, agent_id, name)
|
|
204
|
-
);
|
|
205
|
-
CREATE TABLE IF NOT EXISTS org_ticket_state ( -- DERIVED CACHE (company mode): the last (status, owner, blocked) seen per ticket, so a change is notified once; rebuilt silently from the ticket files
|
|
206
|
-
project_id TEXT NOT NULL,
|
|
207
|
-
org_id TEXT NOT NULL,
|
|
208
|
-
ticket_id TEXT NOT NULL,
|
|
209
|
-
status TEXT NOT NULL,
|
|
210
|
-
owner TEXT NOT NULL DEFAULT '',
|
|
211
|
-
blocked TEXT NOT NULL DEFAULT '',
|
|
212
|
-
blocked_by TEXT NOT NULL DEFAULT '',
|
|
213
|
-
PRIMARY KEY (project_id, org_id, ticket_id)
|
|
214
|
-
);
|
|
215
|
-
CREATE TABLE IF NOT EXISTS org_chat_state ( -- DERIVED CACHE (company mode): tail-scan byte cursor per chat day file
|
|
216
|
-
project_id TEXT NOT NULL,
|
|
217
|
-
org_id TEXT NOT NULL,
|
|
218
|
-
date TEXT NOT NULL,
|
|
219
|
-
offset_bytes INTEGER NOT NULL DEFAULT 0,
|
|
220
|
-
PRIMARY KEY (project_id, org_id, date)
|
|
221
|
-
);
|
|
222
|
-
CREATE TABLE IF NOT EXISTS org_chat_reads ( -- user data (company mode): each user's read cursor in an organization's chat
|
|
223
|
-
project_id TEXT NOT NULL,
|
|
224
|
-
org_id TEXT NOT NULL,
|
|
225
|
-
user_id TEXT NOT NULL,
|
|
226
|
-
last_read_id TEXT NOT NULL,
|
|
227
|
-
PRIMARY KEY (project_id, org_id, user_id)
|
|
228
|
-
);
|
|
229
|
-
CREATE TABLE IF NOT EXISTS org_budget_state ( -- DERIVED CACHE (company mode): warn / pause marks per employee and period, recomputed from usage and the chart's budgets
|
|
230
|
-
project_id TEXT NOT NULL,
|
|
231
|
-
org_id TEXT NOT NULL,
|
|
232
|
-
agent_id TEXT NOT NULL,
|
|
233
|
-
period TEXT NOT NULL,
|
|
234
|
-
warned_at TEXT,
|
|
235
|
-
paused_at TEXT,
|
|
236
|
-
PRIMARY KEY (project_id, org_id, agent_id, period)
|
|
237
|
-
);
|
|
238
|
-
`);
|
|
239
|
-
},
|
|
240
|
-
// Drops the seven tables. LOSES: chat read cursors (each user's "read up to here" marks) and
|
|
241
|
-
// the run marks that stop a calendar slot, a ticket change or a budget alert from firing
|
|
242
|
-
// twice — a build with this migration re-registers everything from the files, without
|
|
243
|
-
// backfilling missed slots, and users see every chat message as unread once.
|
|
244
|
-
down(db) {
|
|
245
|
-
db.exec(`
|
|
246
|
-
DROP INDEX IF EXISTS idx_org_ticket_sessions_session;
|
|
247
|
-
DROP INDEX IF EXISTS idx_org_sessions_org;
|
|
248
|
-
DROP TABLE IF EXISTS org_budget_state;
|
|
249
|
-
DROP TABLE IF EXISTS org_chat_reads;
|
|
250
|
-
DROP TABLE IF EXISTS org_chat_state;
|
|
251
|
-
DROP TABLE IF EXISTS org_ticket_state;
|
|
252
|
-
DROP TABLE IF EXISTS org_calendar_state;
|
|
253
|
-
DROP TABLE IF EXISTS org_ticket_sessions;
|
|
254
|
-
DROP TABLE IF EXISTS org_sessions;
|
|
255
|
-
`);
|
|
256
|
-
}
|
|
172
|
+
// LOSES this server's own machine id (every stored reference to it on other machines
|
|
173
|
+
// then points at nothing), what was installed where, the sessions held, and which
|
|
174
|
+
// machines each Project used.
|
|
175
|
+
down(db) {
|
|
176
|
+
db.exec(`
|
|
177
|
+
DROP TABLE IF EXISTS machine_project;
|
|
178
|
+
DROP TABLE IF EXISTS machines;
|
|
179
|
+
DROP TABLE IF EXISTS machine;
|
|
180
|
+
`);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/db/migrations/steps/2026-09-14-user-profile.ts
|
|
185
|
+
var userProfile = {
|
|
186
|
+
name: "2026-09-14-user-profile",
|
|
187
|
+
// Two nullable columns on `users`, no default and nothing existing rewritten: a platform
|
|
188
|
+
// rolled back to a predecessor that does not know them never selects or writes them, and
|
|
189
|
+
// every existing account simply reads NULL — no profile yet.
|
|
190
|
+
swapSafe: true,
|
|
191
|
+
up(db) {
|
|
192
|
+
ensureColumn(db, "users", "display_name", "TEXT");
|
|
193
|
+
ensureColumn(db, "users", "avatar", "TEXT");
|
|
257
194
|
},
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
195
|
+
// LOSES every stored nickname and avatar: both live entirely in the two columns this
|
|
196
|
+
// drops, and nothing else on disk holds a copy. Dropped in reverse order for symmetry
|
|
197
|
+
// with `up`; neither column is indexed, which is what lets SQLite drop them at all.
|
|
198
|
+
down(db) {
|
|
199
|
+
const cols = db.prepare("PRAGMA table_info(users)").all();
|
|
200
|
+
const has = (name) => cols.some((c) => c.name === name);
|
|
201
|
+
if (has("avatar")) db.exec("ALTER TABLE users DROP COLUMN avatar");
|
|
202
|
+
if (has("display_name")) db.exec("ALTER TABLE users DROP COLUMN display_name");
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/db/migrations/steps/2026-09-16-company-mode-channels.ts
|
|
207
|
+
var companyModeChannels = {
|
|
208
|
+
name: "2026-09-16-company-mode-channels",
|
|
209
|
+
// The organization's single group chat became channels: a message lives in
|
|
210
|
+
// `channels/<channel_id>/<date>.jsonl`, so the tail-scan cursor and each user's read
|
|
211
|
+
// cursor are per channel. `org_chat_state` / `org_chat_reads` are dropped for
|
|
212
|
+
// `org_channel_state` / `org_channel_reads`: the tables are renamed and the channel
|
|
213
|
+
// belongs in the primary key, neither of which SQLite does in place. Nothing is carried
|
|
214
|
+
// over: company mode is unreleased, and every row was either rebuildable from the files
|
|
215
|
+
// (the scan cursor) or one pass of "everything looks unread" (the read cursor). A
|
|
216
|
+
// predecessor build reads neither table's shape, only its own writes, so this is
|
|
217
|
+
// swap-safe.
|
|
218
|
+
//
|
|
219
|
+
// By the expand / contract rule (migration.ts) the two DROPs are a contract, and this is the
|
|
220
|
+
// one expand that removes anything on the swap path. It stays an expand because it is frozen
|
|
221
|
+
// and cannot be deferred: the same step creates the channel tables a pushed platform needs.
|
|
222
|
+
// A new change of this kind is split into an expand and a later contract instead.
|
|
223
|
+
swapSafe: true,
|
|
224
|
+
up(db) {
|
|
225
|
+
db.exec(`
|
|
226
|
+
DROP TABLE IF EXISTS org_chat_state;
|
|
227
|
+
DROP TABLE IF EXISTS org_chat_reads;
|
|
228
|
+
CREATE TABLE IF NOT EXISTS org_channel_state ( -- DERIVED CACHE (company mode): tail-scan byte cursor per channel and day file
|
|
229
|
+
project_id TEXT NOT NULL,
|
|
230
|
+
org_id TEXT NOT NULL,
|
|
231
|
+
channel_id TEXT NOT NULL,
|
|
232
|
+
date TEXT NOT NULL,
|
|
233
|
+
offset_bytes INTEGER NOT NULL DEFAULT 0,
|
|
234
|
+
PRIMARY KEY (project_id, org_id, channel_id, date)
|
|
235
|
+
);
|
|
236
|
+
CREATE TABLE IF NOT EXISTS org_channel_reads ( -- user data (company mode): each user's read cursor in one channel
|
|
237
|
+
project_id TEXT NOT NULL,
|
|
238
|
+
org_id TEXT NOT NULL,
|
|
239
|
+
channel_id TEXT NOT NULL,
|
|
240
|
+
user_id TEXT NOT NULL,
|
|
241
|
+
last_read_id TEXT NOT NULL,
|
|
242
|
+
PRIMARY KEY (project_id, org_id, channel_id, user_id)
|
|
243
|
+
);
|
|
244
|
+
`);
|
|
245
|
+
},
|
|
246
|
+
// Recreates the two tables under their old names, exactly as 2026-09-16-company-mode-org-caches declared them —
|
|
247
|
+
// EMPTY. LOSES every scan cursor (re-derived by the next pass, which republishes the
|
|
248
|
+
// messages it re-reads) and every read cursor (users see the recent days as unread once).
|
|
249
|
+
down(db) {
|
|
250
|
+
db.exec(`
|
|
251
|
+
DROP TABLE IF EXISTS org_channel_state;
|
|
252
|
+
DROP TABLE IF EXISTS org_channel_reads;
|
|
253
|
+
CREATE TABLE IF NOT EXISTS org_chat_state ( -- DERIVED CACHE (company mode): tail-scan byte cursor per chat day file
|
|
254
|
+
project_id TEXT NOT NULL,
|
|
255
|
+
org_id TEXT NOT NULL,
|
|
256
|
+
date TEXT NOT NULL,
|
|
257
|
+
offset_bytes INTEGER NOT NULL DEFAULT 0,
|
|
258
|
+
PRIMARY KEY (project_id, org_id, date)
|
|
259
|
+
);
|
|
260
|
+
CREATE TABLE IF NOT EXISTS org_chat_reads ( -- user data (company mode): each user's read cursor in an organization's chat
|
|
261
|
+
project_id TEXT NOT NULL,
|
|
262
|
+
org_id TEXT NOT NULL,
|
|
263
|
+
user_id TEXT NOT NULL,
|
|
264
|
+
last_read_id TEXT NOT NULL,
|
|
265
|
+
PRIMARY KEY (project_id, org_id, user_id)
|
|
266
|
+
);
|
|
267
|
+
`);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/db/migrations/steps/2026-09-16-company-mode-desk-notices.ts
|
|
272
|
+
var companyModeDeskNotices = {
|
|
273
|
+
name: "2026-09-16-company-mode-desk-notices",
|
|
274
|
+
// Additive: one new table and its index. A ticket change no longer starts a work run at
|
|
275
|
+
// the owner's desk; it is queued here and delivered inside the body of that employee's
|
|
276
|
+
// next calendar sweep. A predecessor build never reads the table, so a rollback survives
|
|
277
|
+
// it — with whatever rows are in it left undelivered.
|
|
278
|
+
swapSafe: true,
|
|
279
|
+
up(db) {
|
|
280
|
+
db.exec(`
|
|
281
|
+
CREATE TABLE IF NOT EXISTS org_desk_notices ( -- DERIVED CACHE (company mode): ticket changes waiting for an employee's next calendar sweep; dropping it loses only the digests not delivered yet
|
|
282
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
283
|
+
project_id TEXT NOT NULL,
|
|
284
|
+
org_id TEXT NOT NULL,
|
|
285
|
+
agent_id TEXT NOT NULL,
|
|
286
|
+
ticket_id TEXT NOT NULL,
|
|
287
|
+
change TEXT NOT NULL,
|
|
288
|
+
at TEXT NOT NULL
|
|
289
|
+
);
|
|
290
|
+
CREATE INDEX IF NOT EXISTS idx_org_desk_notices_agent ON org_desk_notices(project_id, org_id, agent_id);
|
|
291
|
+
`);
|
|
292
|
+
},
|
|
293
|
+
// LOSES every queued notice: the ticket changes an employee has not been told about yet.
|
|
294
|
+
// The change itself is in the ticket file and its history either way; what
|
|
295
|
+
// goes is the "Since your last sweep" line that would have named it.
|
|
296
|
+
down(db) {
|
|
297
|
+
db.exec(`
|
|
298
|
+
DROP INDEX IF EXISTS idx_org_desk_notices_agent;
|
|
299
|
+
DROP TABLE IF EXISTS org_desk_notices;
|
|
300
|
+
`);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// src/db/migrations/steps/2026-09-16-company-mode-org-caches.ts
|
|
305
|
+
var companyModeOrgCaches = {
|
|
306
|
+
name: "2026-09-16-company-mode-org-caches",
|
|
307
|
+
// Additive: seven new tables for company mode (organizations of Agents), no change to any
|
|
308
|
+
// existing table. Every row is either rebuildable from the organization's files (desks.toml,
|
|
309
|
+
// the tickets' Sessions headers, the calendar files, usage records) or a user's own read
|
|
310
|
+
// cursor; a predecessor build never touches them.
|
|
311
|
+
swapSafe: true,
|
|
312
|
+
up(db) {
|
|
313
|
+
db.exec(`
|
|
314
|
+
CREATE TABLE IF NOT EXISTS org_sessions ( -- DERIVED CACHE (company mode): desk sessions, rebuilt from each organization's desks.toml (current + previous); trigger_hop is chat-chain accounting and reads 0 after a rebuild
|
|
315
|
+
session_id TEXT PRIMARY KEY,
|
|
316
|
+
project_id TEXT NOT NULL,
|
|
317
|
+
org_id TEXT NOT NULL,
|
|
318
|
+
agent_id TEXT NOT NULL,
|
|
319
|
+
current INTEGER NOT NULL DEFAULT 1,
|
|
320
|
+
trigger_hop INTEGER NOT NULL DEFAULT 0
|
|
321
|
+
);
|
|
322
|
+
CREATE INDEX IF NOT EXISTS idx_org_sessions_org ON org_sessions(project_id, org_id);
|
|
323
|
+
CREATE TABLE IF NOT EXISTS org_ticket_sessions ( -- DERIVED CACHE (company mode): ticket <-> contributing session, rebuilt from each ticket's Sessions header
|
|
324
|
+
project_id TEXT NOT NULL,
|
|
325
|
+
org_id TEXT NOT NULL,
|
|
326
|
+
ticket_id TEXT NOT NULL,
|
|
327
|
+
session_id TEXT NOT NULL,
|
|
328
|
+
agent_id TEXT NOT NULL,
|
|
329
|
+
trigger_hop INTEGER NOT NULL DEFAULT 0,
|
|
330
|
+
PRIMARY KEY (project_id, org_id, ticket_id, session_id)
|
|
331
|
+
);
|
|
332
|
+
CREATE INDEX IF NOT EXISTS idx_org_ticket_sessions_session ON org_ticket_sessions(session_id);
|
|
333
|
+
CREATE TABLE IF NOT EXISTS org_calendar_state ( -- calendar run state (company mode; the files are declarative intent), same rules as schedule_state
|
|
334
|
+
project_id TEXT NOT NULL,
|
|
335
|
+
org_id TEXT NOT NULL,
|
|
336
|
+
agent_id TEXT NOT NULL,
|
|
337
|
+
name TEXT NOT NULL,
|
|
338
|
+
start_at_ms INTEGER NOT NULL,
|
|
339
|
+
def_hash TEXT NOT NULL,
|
|
340
|
+
last_slot_ms INTEGER,
|
|
341
|
+
last_fired_at TEXT,
|
|
342
|
+
fired_once INTEGER NOT NULL DEFAULT 0,
|
|
343
|
+
missed INTEGER NOT NULL DEFAULT 0,
|
|
344
|
+
invalid_reason TEXT,
|
|
345
|
+
last_outcome TEXT,
|
|
346
|
+
PRIMARY KEY (project_id, org_id, agent_id, name)
|
|
347
|
+
);
|
|
348
|
+
CREATE TABLE IF NOT EXISTS org_ticket_state ( -- DERIVED CACHE (company mode): the last (status, owner, blocked) seen per ticket, so a change is notified once; rebuilt silently from the ticket files
|
|
349
|
+
project_id TEXT NOT NULL,
|
|
350
|
+
org_id TEXT NOT NULL,
|
|
351
|
+
ticket_id TEXT NOT NULL,
|
|
352
|
+
status TEXT NOT NULL,
|
|
353
|
+
owner TEXT NOT NULL DEFAULT '',
|
|
354
|
+
blocked TEXT NOT NULL DEFAULT '',
|
|
355
|
+
blocked_by TEXT NOT NULL DEFAULT '',
|
|
356
|
+
PRIMARY KEY (project_id, org_id, ticket_id)
|
|
357
|
+
);
|
|
358
|
+
CREATE TABLE IF NOT EXISTS org_budget_state ( -- DERIVED CACHE (company mode): warn / pause marks per employee and period, recomputed from usage and the chart's budgets
|
|
359
|
+
project_id TEXT NOT NULL,
|
|
360
|
+
org_id TEXT NOT NULL,
|
|
361
|
+
agent_id TEXT NOT NULL,
|
|
362
|
+
period TEXT NOT NULL,
|
|
363
|
+
warned_at TEXT,
|
|
364
|
+
paused_at TEXT,
|
|
365
|
+
PRIMARY KEY (project_id, org_id, agent_id, period)
|
|
366
|
+
);
|
|
367
|
+
`);
|
|
368
|
+
const superseded = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'org_channel_state'").get() !== void 0;
|
|
369
|
+
if (!superseded) {
|
|
297
370
|
db.exec(`
|
|
298
|
-
DROP TABLE IF EXISTS org_channel_state;
|
|
299
|
-
DROP TABLE IF EXISTS org_channel_reads;
|
|
300
371
|
CREATE TABLE IF NOT EXISTS org_chat_state ( -- DERIVED CACHE (company mode): tail-scan byte cursor per chat day file
|
|
301
372
|
project_id TEXT NOT NULL,
|
|
302
373
|
org_id TEXT NOT NULL,
|
|
@@ -304,7 +375,7 @@ var MIGRATIONS = [
|
|
|
304
375
|
offset_bytes INTEGER NOT NULL DEFAULT 0,
|
|
305
376
|
PRIMARY KEY (project_id, org_id, date)
|
|
306
377
|
);
|
|
307
|
-
CREATE TABLE IF NOT EXISTS org_chat_reads (
|
|
378
|
+
CREATE TABLE IF NOT EXISTS org_chat_reads ( -- user data (company mode): each user's read cursor in an organization's chat
|
|
308
379
|
project_id TEXT NOT NULL,
|
|
309
380
|
org_id TEXT NOT NULL,
|
|
310
381
|
user_id TEXT NOT NULL,
|
|
@@ -314,168 +385,203 @@ var MIGRATIONS = [
|
|
|
314
385
|
`);
|
|
315
386
|
}
|
|
316
387
|
},
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
388
|
+
// Drops the seven tables. LOSES: chat read cursors (each user's "read up to here" marks) and
|
|
389
|
+
// the run marks that stop a calendar slot, a ticket change or a budget alert from firing
|
|
390
|
+
// twice — a build with this migration re-registers everything from the files, without
|
|
391
|
+
// backfilling missed slots, and users see every chat message as unread once.
|
|
392
|
+
down(db) {
|
|
393
|
+
db.exec(`
|
|
394
|
+
DROP INDEX IF EXISTS idx_org_ticket_sessions_session;
|
|
395
|
+
DROP INDEX IF EXISTS idx_org_sessions_org;
|
|
396
|
+
DROP TABLE IF EXISTS org_budget_state;
|
|
397
|
+
DROP TABLE IF EXISTS org_chat_reads;
|
|
398
|
+
DROP TABLE IF EXISTS org_chat_state;
|
|
399
|
+
DROP TABLE IF EXISTS org_ticket_state;
|
|
400
|
+
DROP TABLE IF EXISTS org_calendar_state;
|
|
401
|
+
DROP TABLE IF EXISTS org_ticket_sessions;
|
|
402
|
+
DROP TABLE IF EXISTS org_sessions;
|
|
403
|
+
`);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/db/migrations/steps/2026-09-17-model-promotions.ts
|
|
408
|
+
var modelPromotions = {
|
|
409
|
+
name: "2026-09-17-model-promotions",
|
|
410
|
+
// Additive: one new table, nothing existing touched. A predecessor build never reads it
|
|
411
|
+
// and prices every row at the number in its Project file, so a rollback survives it.
|
|
412
|
+
swapSafe: true,
|
|
413
|
+
up(db) {
|
|
414
|
+
db.exec(`
|
|
415
|
+
CREATE TABLE IF NOT EXISTS model_promotions ( -- NOT a cache rebuildable from files: the only record of a Project row's running promotion (.project_config.toml keeps the list price), written by preset seeding at Project creation, "sync presets" and Penguin Go authorization / sync, and read by cost; lost rows price usage at list until the next sync writes them back
|
|
416
|
+
project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
417
|
+
provider TEXT NOT NULL,
|
|
418
|
+
model_id TEXT NOT NULL,
|
|
419
|
+
discount REAL NOT NULL CHECK (discount > 0 AND discount < 1),
|
|
420
|
+
updated_at TEXT NOT NULL,
|
|
421
|
+
PRIMARY KEY (project_id, provider, model_id)
|
|
422
|
+
);
|
|
423
|
+
`);
|
|
348
424
|
},
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
425
|
+
// LOSES every stored promotion: no file holds a copy of the fractions, so usage is priced
|
|
426
|
+
// at the Project files' list prices until the next "sync presets" or Penguin Go sync
|
|
427
|
+
// writes them back.
|
|
428
|
+
down(db) {
|
|
429
|
+
db.exec("DROP TABLE IF EXISTS model_promotions");
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/db/migrations/steps/2026-09-21-model-provider-auth-tokens.ts
|
|
434
|
+
var modelProviderAuthTokens = {
|
|
435
|
+
name: "2026-09-21-model-provider-auth-tokens",
|
|
436
|
+
// Additive: one new table, nothing existing touched. A predecessor build never reads it;
|
|
437
|
+
// the access token already written into .project_config.toml keeps working until it
|
|
438
|
+
// expires, so rollback only loses silent refresh state.
|
|
439
|
+
// KEEP until every supported upgrade starts from a database that already has this table.
|
|
440
|
+
// The release that raises the minimum database baseline past it owns removing this
|
|
441
|
+
// migration and its fixture; the table itself remains while provider token refresh uses it.
|
|
442
|
+
swapSafe: true,
|
|
443
|
+
up(db) {
|
|
444
|
+
db.exec(`
|
|
445
|
+
CREATE TABLE IF NOT EXISTS model_provider_auth_tokens ( -- Server-side OAuth refresh metadata for provider groups. The current request token remains in .project_config.toml as api_key; this table holds only the refresh material that must never be returned to the frontend or written into Project files.
|
|
446
|
+
project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
447
|
+
provider TEXT NOT NULL,
|
|
448
|
+
refresh_token TEXT NOT NULL,
|
|
449
|
+
access_token_expires_at TEXT,
|
|
450
|
+
updated_at TEXT NOT NULL,
|
|
451
|
+
PRIMARY KEY (project_id, provider)
|
|
452
|
+
);
|
|
453
|
+
`);
|
|
373
454
|
},
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
PRIMARY KEY (project_id, provider)
|
|
393
|
-
);
|
|
394
|
-
`);
|
|
395
|
-
},
|
|
396
|
-
// LOSES every stored refresh token and access-token expiry. Existing access tokens in
|
|
397
|
-
// Project files remain until they expire; users re-authorize to restore silent refresh.
|
|
398
|
-
down(db) {
|
|
399
|
-
db.exec("DROP TABLE IF EXISTS model_provider_auth_tokens");
|
|
400
|
-
}
|
|
455
|
+
// LOSES every stored refresh token and access-token expiry. Existing access tokens in
|
|
456
|
+
// Project files remain until they expire; users re-authorize to restore silent refresh.
|
|
457
|
+
down(db) {
|
|
458
|
+
db.exec("DROP TABLE IF EXISTS model_provider_auth_tokens");
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// src/db/migrations/steps/2026-09-22-sessions-sandbox.ts
|
|
463
|
+
var sessionsSandbox = {
|
|
464
|
+
name: "2026-09-22-sessions-sandbox",
|
|
465
|
+
// The Session's own sandbox policy (JSON), snapshotted from the server's settings when
|
|
466
|
+
// the Session is created, so a later settings change leaves running Sessions alone.
|
|
467
|
+
// NULL = a row from before this column: it takes the settings in force at its next
|
|
468
|
+
// command and keeps them from then on. Swap-safe: a pushed platform boots against the
|
|
469
|
+
// runtime's already-open database, so the column has to arrive by a migration.
|
|
470
|
+
swapSafe: true,
|
|
471
|
+
up(db) {
|
|
472
|
+
ensureColumn(db, "sessions", "sandbox", "TEXT");
|
|
401
473
|
},
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
474
|
+
// Not a DROP: schema.ts declares the column, and dropping it would put every Session
|
|
475
|
+
// back under whatever the settings say.
|
|
476
|
+
down() {
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
// src/db/migrations/steps/2026-09-23-machines-columns.ts
|
|
481
|
+
var machinesColumns = {
|
|
482
|
+
name: "2026-09-23-machines-columns",
|
|
483
|
+
// A machines table can predate `machines` — a data root that ran the machines line
|
|
484
|
+
// before it was released kept forwards, not sessions — and that migration's IF NOT
|
|
485
|
+
// EXISTS adopted it whatever its columns were, so the first connect failed on the row's
|
|
486
|
+
// insert. Adds what is missing, nullable; a platform rolled back to `machines` declared these
|
|
487
|
+
// columns itself, so there is nothing for it to mind.
|
|
488
|
+
swapSafe: true,
|
|
489
|
+
up(db) {
|
|
490
|
+
ensureColumn(db, "machines", "session_pid", "INTEGER");
|
|
491
|
+
ensureColumn(db, "machines", "platform", "TEXT");
|
|
418
492
|
},
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
493
|
+
// Nothing to undo: what this added, `machines` already declares, and a column it found in place
|
|
494
|
+
// was never this migration's to take away.
|
|
495
|
+
down() {
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/db/migrations/steps/2026-09-23-model-tables-adoption.ts
|
|
500
|
+
var modelTablesAdoption = {
|
|
501
|
+
name: "2026-09-23-model-tables-adoption",
|
|
502
|
+
// A data root that ran the chain before it was restacked onto main (2026-09-23) is
|
|
503
|
+
// stamped 16 under THAT line's numbering, where 9 and 10 were 2026-09-22-sessions-sandbox and
|
|
504
|
+
// 2026-09-23-machines-columns. This line numbers main's 2026-09-17-model-promotions and
|
|
505
|
+
// 2026-09-21-model-provider-auth-tokens 9 and 10 and everything after them two higher, so such a
|
|
506
|
+
// root reads both as already applied, reaches 18 without `model_promotions` or
|
|
507
|
+
// `model_provider_auth_tokens`, and answers 500 wherever cost or provider auth reads
|
|
508
|
+
// them (the models page, an organization's overview). Seen on the release instance at
|
|
509
|
+
// the first push of the restacked line.
|
|
510
|
+
//
|
|
511
|
+
// Re-runs 9's and 10's own `up` — the frozen DDL, both `IF NOT EXISTS`, so a root that
|
|
512
|
+
// took them in their proper place finds its work done. The ledger ended the numbering
|
|
513
|
+
// hazard: adopting a numbered root runs 2026-09-17-model-promotions and 2026-09-21-model-provider-auth-tokens
|
|
514
|
+
// themselves. Remove together with the ledger's adoption of numbered roots (index.ts).
|
|
515
|
+
swapSafe: true,
|
|
516
|
+
up(db) {
|
|
517
|
+
db.exec(`
|
|
518
|
+
CREATE TABLE IF NOT EXISTS model_promotions (
|
|
519
|
+
project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
520
|
+
provider TEXT NOT NULL,
|
|
521
|
+
model_id TEXT NOT NULL,
|
|
522
|
+
discount REAL NOT NULL CHECK (discount > 0 AND discount < 1),
|
|
523
|
+
updated_at TEXT NOT NULL,
|
|
524
|
+
PRIMARY KEY (project_id, provider, model_id)
|
|
525
|
+
);
|
|
526
|
+
CREATE TABLE IF NOT EXISTS model_provider_auth_tokens (
|
|
527
|
+
project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
528
|
+
provider TEXT NOT NULL,
|
|
529
|
+
refresh_token TEXT NOT NULL,
|
|
530
|
+
access_token_expires_at TEXT,
|
|
531
|
+
updated_at TEXT NOT NULL,
|
|
532
|
+
PRIMARY KEY (project_id, provider)
|
|
533
|
+
);
|
|
534
|
+
`);
|
|
436
535
|
},
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
DROP TABLE IF EXISTS browser_extensions;
|
|
466
|
-
`);
|
|
467
|
-
}
|
|
536
|
+
// Nothing to undo: the tables are 2026-09-17-model-promotions' and 2026-09-21-model-provider-auth-tokens', and
|
|
537
|
+
// their own downs drop them.
|
|
538
|
+
down() {
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// src/db/migrations/steps/2026-10-04-browser-extensions.ts
|
|
543
|
+
var browserExtensions = {
|
|
544
|
+
name: "2026-10-04-browser-extensions",
|
|
545
|
+
// Additive: one new table and its index. The Chrome backend's pairings — one row per
|
|
546
|
+
// PenguinHarness Browser extension a user paired, its token stored hashed. A predecessor
|
|
547
|
+
// build never reads the table, so a rollback survives it; the paired extensions then simply
|
|
548
|
+
// fail to connect until a build that knows them is back.
|
|
549
|
+
swapSafe: true,
|
|
550
|
+
up(db) {
|
|
551
|
+
db.exec(`
|
|
552
|
+
CREATE TABLE IF NOT EXISTS browser_extensions ( -- one row per Chrome paired to a user (builtin-browser/extension-pairing.ts): the PenguinHarness Browser extension's long-lived credential, stored only hashed; NOT rebuildable \u2014 a lost row means pairing that Chrome again
|
|
553
|
+
extension_id TEXT PRIMARY KEY,
|
|
554
|
+
user_id TEXT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
|
|
555
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
556
|
+
name TEXT NOT NULL,
|
|
557
|
+
version TEXT NOT NULL,
|
|
558
|
+
created_at TEXT NOT NULL,
|
|
559
|
+
last_seen_at TEXT,
|
|
560
|
+
revoked_at TEXT
|
|
561
|
+
);
|
|
562
|
+
CREATE INDEX IF NOT EXISTS idx_browser_extensions_user ON browser_extensions(user_id);
|
|
563
|
+
`);
|
|
468
564
|
},
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
565
|
+
// LOSES every pairing: each user's Chrome has to be paired again (a new code from the
|
|
566
|
+
// Browser panel) before agents can drive it.
|
|
567
|
+
down(db) {
|
|
568
|
+
db.exec(`
|
|
569
|
+
DROP INDEX IF EXISTS idx_browser_extensions_user;
|
|
570
|
+
DROP TABLE IF EXISTS browser_extensions;
|
|
571
|
+
`);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// src/db/migrations/steps/2026-10-10-agent-api.ts
|
|
576
|
+
var agentApi = {
|
|
577
|
+
name: "2026-10-10-agent-api",
|
|
578
|
+
// Additive: two new tables and an index. The Agent API's per-Agent settings (the switch,
|
|
579
|
+
// keyless access, the approval mode API Sessions are created with) and its keys, stored
|
|
580
|
+
// hashed. A predecessor build never reads them and serves no /api/amsp route, so a
|
|
581
|
+
// rollback survives it: callers get 404 until a build that knows the tables is back.
|
|
582
|
+
swapSafe: true,
|
|
583
|
+
up(db) {
|
|
584
|
+
db.exec(`
|
|
479
585
|
CREATE TABLE IF NOT EXISTS agent_api ( -- per-Agent public API settings (amsp/routes.ts); NOT rebuildable from files
|
|
480
586
|
project_id TEXT NOT NULL,
|
|
481
587
|
agent_id TEXT NOT NULL,
|
|
@@ -498,98 +604,141 @@ var MIGRATIONS = [
|
|
|
498
604
|
);
|
|
499
605
|
CREATE INDEX IF NOT EXISTS idx_agent_api_keys_agent ON agent_api_keys(project_id, agent_id);
|
|
500
606
|
`);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
607
|
+
},
|
|
608
|
+
// LOSES every Agent's API settings and every key: each exposed Agent reads as disabled
|
|
609
|
+
// again (404 to its callers), and every caller needs a newly created key once it is back
|
|
610
|
+
// on — no file holds a copy of either.
|
|
611
|
+
down(db) {
|
|
612
|
+
db.exec(`
|
|
507
613
|
DROP INDEX IF EXISTS idx_agent_api_keys_agent;
|
|
508
614
|
DROP TABLE IF EXISTS agent_api_keys;
|
|
509
615
|
DROP TABLE IF EXISTS agent_api;
|
|
510
616
|
`);
|
|
511
|
-
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// src/db/migrations/steps/2026-10-12-messaging-conversations.ts
|
|
621
|
+
var messagingConversations = {
|
|
622
|
+
name: "2026-10-12-messaging-conversations",
|
|
623
|
+
// Additive: one new table. The WeChat connector's per-(bot, user) delivery state — the
|
|
624
|
+
// context token every send must echo, how many sends it has funded, and replies held for
|
|
625
|
+
// the user's next message. A predecessor build never reads it and keeps its tokens in
|
|
626
|
+
// memory as it always did, so a rollback survives it.
|
|
627
|
+
swapSafe: true,
|
|
628
|
+
up(db) {
|
|
629
|
+
db.exec(`
|
|
630
|
+
CREATE TABLE IF NOT EXISTS messaging_conversations ( -- per-(bot, chat) delivery state a connector must keep across reconnects and restarts (WeChat: the context token, its send count, held replies); deleted once no messaging_bindings row references (channel, account_id) \u2014 see MessagingBindingsRepo
|
|
631
|
+
channel TEXT NOT NULL,
|
|
632
|
+
account_id TEXT NOT NULL,
|
|
633
|
+
chat_id TEXT NOT NULL,
|
|
634
|
+
state_json TEXT NOT NULL,
|
|
635
|
+
updated_at TEXT NOT NULL,
|
|
636
|
+
PRIMARY KEY (channel, account_id, chat_id)
|
|
637
|
+
);
|
|
638
|
+
`);
|
|
512
639
|
},
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
`);
|
|
532
|
-
},
|
|
533
|
-
// LOSES every stored context token and every held reply: replies sent before each user's
|
|
534
|
-
// next message are refused again, and what was held never arrives.
|
|
535
|
-
down(db) {
|
|
536
|
-
db.exec("DROP TABLE IF EXISTS messaging_conversations");
|
|
537
|
-
}
|
|
640
|
+
// LOSES every stored context token and every held reply: replies sent before each user's
|
|
641
|
+
// next message are refused again, and what was held never arrives.
|
|
642
|
+
down(db) {
|
|
643
|
+
db.exec("DROP TABLE IF EXISTS messaging_conversations");
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// src/db/migrations/steps/2026-10-12-sessions-kept-at.ts
|
|
648
|
+
var sessionsKeptAt = {
|
|
649
|
+
name: "2026-10-12-sessions-kept-at",
|
|
650
|
+
// The "keep in sidebar" mark: written when the user unarchives a Session, cleared when they
|
|
651
|
+
// archive it. The listing rule files a background Session (api / schedule / cli) away unless
|
|
652
|
+
// it carries the mark, so existing rows are listed by the rule as they stand and nothing is
|
|
653
|
+
// rewritten. Swap-safe: a pushed platform boots against the runtime's already-open database,
|
|
654
|
+
// so the column has to arrive by a migration.
|
|
655
|
+
swapSafe: true,
|
|
656
|
+
up(db) {
|
|
657
|
+
ensureColumn(db, "sessions", "kept_at", "TEXT");
|
|
538
658
|
},
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// The "keep in sidebar" mark: written when the user unarchives a Session, cleared when they
|
|
543
|
-
// archive it. The listing rule files a background Session (api / schedule / cli) away unless
|
|
544
|
-
// it carries the mark, so existing rows are listed by the rule as they stand and nothing is
|
|
545
|
-
// rewritten. Swap-safe: a pushed platform boots against the runtime's already-open database,
|
|
546
|
-
// so the column has to arrive by a migration.
|
|
547
|
-
swapSafe: true,
|
|
548
|
-
up(db) {
|
|
549
|
-
ensureColumn(db, "sessions", "kept_at", "TEXT");
|
|
550
|
-
},
|
|
551
|
-
// Not a DROP: schema.ts declares the column, and dropping it would file every kept
|
|
552
|
-
// background Session away again.
|
|
553
|
-
down() {
|
|
554
|
-
}
|
|
659
|
+
// Not a DROP: schema.ts declares the column, and dropping it would file every kept
|
|
660
|
+
// background Session away again.
|
|
661
|
+
down() {
|
|
555
662
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
);
|
|
567
|
-
|
|
568
|
-
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
// src/db/migrations/steps/2026-10-14-agent-api-concurrency.ts
|
|
666
|
+
var agentApiConcurrency = {
|
|
667
|
+
name: "2026-10-14-agent-api-concurrency",
|
|
668
|
+
// How many of an Agent API Agent's runs may be live at once.
|
|
669
|
+
// Retain while a supported data root can predate this column; the maintainer raising
|
|
670
|
+
// that baseline removes this upgrade once every supported data root has it.
|
|
671
|
+
swapSafe: true,
|
|
672
|
+
up(db) {
|
|
673
|
+
ensureColumn(db, "agent_api", "max_concurrent_runs", "INTEGER NOT NULL DEFAULT 4");
|
|
674
|
+
},
|
|
675
|
+
// Keep the configured limit on rollback; older builds ignore this column.
|
|
676
|
+
down() {
|
|
569
677
|
}
|
|
570
|
-
migration;
|
|
571
678
|
};
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
679
|
+
|
|
680
|
+
// src/db/migrations/steps/2026-10-14-agent-api-session-settings.ts
|
|
681
|
+
var agentApiSessionSettings = {
|
|
682
|
+
name: "2026-10-14-agent-api-session-settings",
|
|
683
|
+
// The per-Agent API defaults a new Session starts with: which provider, model, thinking
|
|
684
|
+
// level and workspace it opens in. Nullable additions preserve existing keys, exposure,
|
|
685
|
+
// approvals and new-Session defaults.
|
|
686
|
+
// Retain while a supported data root can predate these columns; the maintainer raising
|
|
687
|
+
// that baseline removes this upgrade after all supported data roots have them.
|
|
688
|
+
swapSafe: true,
|
|
689
|
+
up(db) {
|
|
690
|
+
ensureColumn(db, "agent_api", "provider", "TEXT");
|
|
691
|
+
ensureColumn(db, "agent_api", "model_id", "TEXT");
|
|
692
|
+
ensureColumn(db, "agent_api", "thinking_level", "TEXT");
|
|
693
|
+
ensureColumn(db, "agent_api", "workspace", "TEXT");
|
|
694
|
+
},
|
|
695
|
+
// Keep the saved defaults on rollback; older builds ignore the additional columns.
|
|
696
|
+
down() {
|
|
578
697
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// src/db/migrations/steps/2026-10-16-machines-adopted-at.ts
|
|
701
|
+
var machinesAdoptedAt = {
|
|
702
|
+
name: "2026-10-16-machines-adopted-at",
|
|
703
|
+
// When this server adopted a machine (first recorded it): the Sessions that machine held
|
|
704
|
+
// before then read as CLI tasks through this server (session-sources.ts sourceSeenBy). A
|
|
705
|
+
// machine recorded before this column has none and never gets one, so the rule does not apply
|
|
706
|
+
// to it (user decision, 2026-10-15) — nothing on either side is rewritten. Swap-safe: a pushed platform boots against the runtime's
|
|
707
|
+
// already-open database, so the column has to arrive by a migration.
|
|
708
|
+
swapSafe: true,
|
|
709
|
+
up(db) {
|
|
710
|
+
ensureColumn(db, "machines", "adopted_at", "TEXT");
|
|
711
|
+
},
|
|
712
|
+
// Keep the adoption times on rollback; older builds ignore the column.
|
|
713
|
+
down() {
|
|
591
714
|
}
|
|
592
|
-
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// src/db/migrations/index.ts
|
|
718
|
+
var MIGRATIONS = [
|
|
719
|
+
messagingBindings,
|
|
720
|
+
messagingDeliveryFlags,
|
|
721
|
+
dropGoalState,
|
|
722
|
+
machines,
|
|
723
|
+
userProfile,
|
|
724
|
+
companyModeOrgCaches,
|
|
725
|
+
companyModeChannels,
|
|
726
|
+
companyModeDeskNotices,
|
|
727
|
+
modelPromotions,
|
|
728
|
+
modelProviderAuthTokens,
|
|
729
|
+
sessionsSandbox,
|
|
730
|
+
machinesColumns,
|
|
731
|
+
browserExtensions,
|
|
732
|
+
agentApi,
|
|
733
|
+
messagingConversations,
|
|
734
|
+
sessionsKeptAt,
|
|
735
|
+
agentApiSessionSettings,
|
|
736
|
+
agentApiConcurrency,
|
|
737
|
+
machinesAdoptedAt,
|
|
738
|
+
modelTablesAdoption
|
|
739
|
+
];
|
|
740
|
+
function migrate2(db, options = {}) {
|
|
741
|
+
return migrate(db, MIGRATIONS, options);
|
|
593
742
|
}
|
|
594
743
|
|
|
595
744
|
// src/db/schema.ts
|
|
@@ -629,7 +778,7 @@ CREATE TABLE IF NOT EXISTS auth_sessions (
|
|
|
629
778
|
user_id TEXT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
|
|
630
779
|
created_at TEXT NOT NULL,
|
|
631
780
|
expires_at TEXT NOT NULL, -- 30-day sliding renewal, topped up in place when <29 days remain
|
|
632
|
-
via TEXT -- 'password'|'desktop'|'setup'|'cli'; NULL = legacy (password)
|
|
781
|
+
via TEXT -- 'password'|'desktop'|'setup'|'cli'|'remote'; NULL = legacy (password)
|
|
633
782
|
);
|
|
634
783
|
-- deleteExpired runs on every login and mint, deleteByUser on every admin reset; without
|
|
635
784
|
-- these both scan the whole table.
|
|
@@ -766,7 +915,12 @@ CREATE TABLE IF NOT EXISTS agent_api ( -- per-Agent public API settings
|
|
|
766
915
|
agent_id TEXT NOT NULL,
|
|
767
916
|
enabled INTEGER NOT NULL DEFAULT 0, -- the API tab's switch; off = every /api/amsp request for it is 404 agent_not_found
|
|
768
917
|
open INTEGER NOT NULL DEFAULT 0, -- keyless access: a request without Authorization is accepted; off = 401 unauthorized
|
|
769
|
-
approval_mode TEXT NOT NULL DEFAULT 'allow-all', --
|
|
918
|
+
approval_mode TEXT NOT NULL DEFAULT 'allow-all', -- copied onto each new API Session
|
|
919
|
+
provider TEXT, -- NULL pair follows the Project default model
|
|
920
|
+
model_id TEXT,
|
|
921
|
+
thinking_level TEXT, -- NULL follows Agent/Project configuration
|
|
922
|
+
workspace TEXT, -- NULL creates a temporary Workspace
|
|
923
|
+
max_concurrent_runs INTEGER NOT NULL DEFAULT 4,
|
|
770
924
|
updated_at TEXT NOT NULL,
|
|
771
925
|
PRIMARY KEY (project_id, agent_id)
|
|
772
926
|
);
|
|
@@ -797,7 +951,8 @@ CREATE TABLE IF NOT EXISTS machines ( -- one row per machine this serve
|
|
|
797
951
|
installed_at TEXT,
|
|
798
952
|
session_pid INTEGER, -- the ssh session this server holds to it; what a successor App closes after a hot swap
|
|
799
953
|
remote_port INTEGER, -- its server's port over there, as of the last connect
|
|
800
|
-
platform TEXT
|
|
954
|
+
platform TEXT, -- linux | darwin | win32, as the install found it: the shell dialect the status probe speaks to it in
|
|
955
|
+
adopted_at TEXT -- when this server first recorded this machine (reset when the address answers with another machine's id): Sessions it held before then read as CLI tasks through this server. NULL = recorded before the column existed; never filled in later, and the rule does not apply
|
|
801
956
|
);
|
|
802
957
|
CREATE TABLE IF NOT EXISTS machine_project ( -- which machines a Project uses; no row = none yet. Follows the Project out, like project_members
|
|
803
958
|
project_id TEXT PRIMARY KEY REFERENCES projects(project_id) ON DELETE CASCADE,
|
|
@@ -945,7 +1100,7 @@ function openDatabase(dbPath) {
|
|
|
945
1100
|
db.exec("DROP INDEX IF EXISTS idx_usage_session");
|
|
946
1101
|
db.exec("DROP INDEX IF EXISTS idx_messaging_account");
|
|
947
1102
|
upgradeLastActiveAt(db);
|
|
948
|
-
|
|
1103
|
+
migrate2(db);
|
|
949
1104
|
return db;
|
|
950
1105
|
}
|
|
951
1106
|
function upgradeLastActiveAt(db) {
|
|
@@ -973,9 +1128,9 @@ function ensureColumn(db, table, column, ddl) {
|
|
|
973
1128
|
}
|
|
974
1129
|
|
|
975
1130
|
export {
|
|
976
|
-
migrate,
|
|
1131
|
+
migrate2 as migrate,
|
|
977
1132
|
openExistingDatabase,
|
|
978
1133
|
openDatabaseReadOnly,
|
|
979
1134
|
openDatabase
|
|
980
1135
|
};
|
|
981
|
-
//# sourceMappingURL=chunk-
|
|
1136
|
+
//# sourceMappingURL=chunk-HPOHMZCH.js.map
|