@oar-sh/oar 0.9.19 → 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/.github/extensions/web-relay/polling/polling-loop.mjs +2 -26
  2. package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +2 -26
  3. package/CHANGELOG.md +317 -0
  4. package/README.md +10 -10
  5. package/bin/oar.js +391 -97
  6. package/package.json +4 -2
  7. package/server/claude-cloud-worker/claude-cloud-event-normalizer.mjs +60 -0
  8. package/server/claude-cloud-worker/claude-cloud-session-process.mjs +27 -0
  9. package/server/claude-worker/claude-remote-relay-tool.mjs +1 -0
  10. package/server/claude-worker/claude-session-process.mjs +3 -0
  11. package/server/claude-worker/claude-turn-publisher.mjs +163 -8
  12. package/server/claude-worker/claude-usage-totals.mjs +58 -0
  13. package/server/copilot-worker/copilot-plan-board.mjs +4 -7
  14. package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +86 -3
  15. package/server/copilot-worker/copilot-sdk-session-process.mjs +57 -4
  16. package/server/cursor-worker/cursor-turn-runner.mjs +42 -6
  17. package/server/db-schema.mjs +18 -0
  18. package/server/deepseek-worker/deepseek-claude-backend.mjs +14 -0
  19. package/server/grok-worker/grok-turn-runner.mjs +44 -6
  20. package/server/migrations/0001-add-structured-answer.mjs +5 -7
  21. package/server/migrations/0002-recent-workspace-roots-path-key.mjs +5 -7
  22. package/server/migrations/0003-push-subscriptions.mjs +5 -7
  23. package/server/migrations/0004-steer-settle-markers.mjs +5 -7
  24. package/server/migrations/0005-remote-relays.mjs +5 -7
  25. package/server/migrations/0006-workspace-root-settings.mjs +5 -7
  26. package/server/migrations/0007-claude-cloud.mjs +5 -7
  27. package/server/migrations/0008-message-pins.mjs +5 -7
  28. package/server/migrations/0009-subagent-timeline.mjs +5 -7
  29. package/server/migrations/0010-subagent-timeline-pruning.mjs +5 -7
  30. package/server/migrations/0011-relay-event-subscriptions.mjs +5 -7
  31. package/server/migrations/0012-cross-relay-event-subscriptions.mjs +5 -7
  32. package/server/migrations/0013-previews.mjs +5 -7
  33. package/server/migrations/0014-conversation-groups.mjs +5 -7
  34. package/server/migrations/0015-conversation-group-position.mjs +5 -7
  35. package/server/migrations/0016-share-access-log.mjs +5 -7
  36. package/server/migrations/0017-conversation-history-indexes.mjs +5 -7
  37. package/server/migrations/0018-background-task-rows.mjs +5 -7
  38. package/server/migrations/0019-relay-event-subscription-options.mjs +5 -7
  39. package/server/migrations/0020-conversation-may-create-sessions.mjs +5 -7
  40. package/server/migrations/0021-conversation-group-agent-marks.mjs +5 -7
  41. package/server/migrations/0022-relay-schedules.mjs +5 -7
  42. package/server/migrations/0023-relay-asks.mjs +5 -7
  43. package/server/migrations/0024-deepseek-usage-totals.mjs +5 -7
  44. package/server/migrations/0025-deepseek-usage-requests-daily.mjs +5 -7
  45. package/server/migrations/0026-deepseek-usage-served-models.mjs +5 -7
  46. package/server/migrations/0027-deepseek-usage-checkpoint-runs.mjs +5 -7
  47. package/server/migrations/0028-deepseek-usage-checkpoint-request-runs.mjs +5 -7
  48. package/server/migrations/0029-deepseek-balance-rises.mjs +5 -7
  49. package/server/migrations/0030-deepseek-usage-actuals.mjs +5 -7
  50. package/server/migrations/0031-queue-provider.mjs +5 -7
  51. package/server/migrations/0032-runtime-session-billing-switch.mjs +5 -7
  52. package/server/migrations/0033-claude-api-usage-checkpoints.mjs +5 -7
  53. package/server/migrations/0034-claude-api-usage-totals.mjs +5 -7
  54. package/server/migrations/0035-conversation-plan-only.mjs +87 -0
  55. package/server/migrations/0036-token-usage.mjs +267 -0
  56. package/server/public/app/about-view.mjs +87 -3
  57. package/server/public/app/api-client.js +2 -1
  58. package/server/public/app/bootstrap.js +55 -2
  59. package/server/public/app/composer-control-state.mjs +12 -2
  60. package/server/public/app/context-usage-view.mjs +16 -0
  61. package/server/public/app/conversation-view.js +17 -2
  62. package/server/public/app/inbox-ui.js +32 -0
  63. package/server/public/app/inbox-view.mjs +141 -5
  64. package/server/public/app/plan-board-shared.mjs +49 -0
  65. package/server/public/app/plan-usage-view.mjs +1 -1
  66. package/server/public/app/relay-board-inline.mjs +4 -1
  67. package/server/public/app/relay-board-view.js +38 -10
  68. package/server/public/app/relay-event-view.mjs +4 -0
  69. package/server/public/app/socket-handlers.js +10 -2
  70. package/server/public/index.html +14 -0
  71. package/server/public/sw.js +8 -0
  72. package/server/relay-heap-guard.mjs +14 -1
  73. package/server/relay-self-restart.mjs +83 -2
  74. package/server/relay-tools.md +4 -3
  75. package/server/repositories/message-repository.mjs +31 -1
  76. package/server/repositories/remote-relay-repository.mjs +10 -0
  77. package/server/repositories/session-repository.mjs +6 -1
  78. package/server/routes/messages-routes.mjs +87 -0
  79. package/server/routes/relay-ask-routes.mjs +138 -8
  80. package/server/routes/relay-board-routes.mjs +55 -40
  81. package/server/routes/sessions-routes.mjs +103 -7
  82. package/server/server-runtime.mjs +132 -11
  83. package/server/services/about-service.mjs +59 -0
  84. package/server/services/cache-rebuild-service.mjs +7 -0
  85. package/server/services/conversation-preferences-service.mjs +31 -0
  86. package/server/services/database-file.mjs +413 -0
  87. package/server/services/db-backup-service.mjs +809 -0
  88. package/server/services/deepseek-usage-import.mjs +6 -9
  89. package/server/services/delete-archive-service.mjs +3 -0
  90. package/server/services/earlier-database-service.mjs +69 -126
  91. package/server/services/oar-cli-helpers.mjs +214 -12
  92. package/server/services/oar-cli-menu.mjs +1 -0
  93. package/server/services/oar-state-migration-service.mjs +33 -13
  94. package/server/services/package-data-rescue.mjs +4 -2
  95. package/server/services/plan-usage-claude-api.mjs +14 -15
  96. package/server/services/plan-usage-contract.mjs +6 -2
  97. package/server/services/plan-usage-deepseek.mjs +1 -1
  98. package/server/services/plan-usage-tokens.mjs +248 -0
  99. package/server/services/push-dispatch-service.mjs +134 -9
  100. package/server/services/relay-cli-launcher-service.mjs +3 -0
  101. package/server/services/relay-event-service.mjs +30 -0
  102. package/server/services/relay-question-context.mjs +11 -0
  103. package/server/services/relay-restart-picture.mjs +67 -22
  104. package/server/services/remote-inbox-watch-service.mjs +204 -0
  105. package/server/services/remote-relay-caller-context.mjs +80 -7
  106. package/server/services/remote-relay-dispatcher.mjs +40 -5
  107. package/server/services/restart-resume-service.mjs +5 -2
  108. package/server/services/session-worker-launch-service.mjs +12 -3
  109. package/server/services/session-worker-stop-service.mjs +27 -6
  110. package/server/services/token-usage-service.mjs +430 -0
  111. package/server/services/update-install-service.mjs +13 -0
  112. package/server/services/usage-limit-pause-service.mjs +8 -1
  113. package/server/services/worker-scope.mjs +77 -0
  114. package/shared/plan-board-actions.mjs +130 -0
  115. package/shared/plan-only.mjs +43 -0
  116. package/shared/relay-asks.mjs +3 -0
  117. package/shared/relay-events.mjs +38 -4
  118. package/shared/remote-relay-contract.mjs +66 -11
  119. package/shared/turn-token-usage.mjs +144 -0
@@ -28,7 +28,7 @@
28
28
  *
29
29
  * Additive and idempotent. Safe to run on every boot.
30
30
  *
31
- * Usage: node server/migrations/0025-deepseek-usage-requests-daily.mjs [path/to/copilot.db]
31
+ * Usage: node server/migrations/0025-deepseek-usage-requests-daily.mjs [path/to/oar.db]
32
32
  */
33
33
 
34
34
  import { fileURLToPath } from 'url';
@@ -37,6 +37,7 @@ import process from 'process';
37
37
  import Database from 'better-sqlite3';
38
38
 
39
39
  import { DEEPSEEK_USAGE_TOTALS_TABLE, migrateDeepSeekUsageTotals } from './0024-deepseek-usage-totals.mjs';
40
+ import { cliDbPath } from '../services/database-file.mjs';
40
41
 
41
42
  export const DEEPSEEK_USAGE_DAYS_TABLE = 'deepseek_usage_days';
42
43
  export const DEEPSEEK_BALANCE_REFERENCE_TABLE = 'deepseek_balance_reference';
@@ -148,11 +149,6 @@ export function migrateDeepSeekUsageRequestsDaily(db) {
148
149
  return { applied: columnsAdded + tablesAdded > 0, columnsAdded, tablesAdded };
149
150
  }
150
151
 
151
- function defaultDbPath() {
152
- const here = path.dirname(fileURLToPath(import.meta.url));
153
- return path.resolve(here, '..', 'data', 'copilot.db');
154
- }
155
-
156
152
  export function migrate(dbPath, options = {}) {
157
153
  const db = new Database(dbPath, options);
158
154
  try {
@@ -164,8 +160,10 @@ export function migrate(dbPath, options = {}) {
164
160
 
165
161
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
166
162
  if (isMain) {
167
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
168
163
  try {
164
+ // The given path, else the database in server/data (oar.db, else the
165
+ // name before 0.9.20); never a new one.
166
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
169
167
  const result = migrate(dbPath);
170
168
  console.log(`Migration 0025 on ${dbPath}`);
171
169
  console.log(result.applied
@@ -29,7 +29,7 @@
29
29
  * Idempotent: a database without such rows is left as it is. Safe to run on
30
30
  * every boot.
31
31
  *
32
- * Usage: node server/migrations/0026-deepseek-usage-served-models.mjs [path/to/copilot.db]
32
+ * Usage: node server/migrations/0026-deepseek-usage-served-models.mjs [path/to/oar.db]
33
33
  */
34
34
 
35
35
  import { fileURLToPath } from 'url';
@@ -40,6 +40,7 @@ import { DEEPSEEK_USAGE_CHECKPOINTS_TABLE, DEEPSEEK_USAGE_TOTALS_TABLE } from '.
40
40
  import { DEEPSEEK_USAGE_DAYS_TABLE, migrateDeepSeekUsageRequestsDaily } from './0025-deepseek-usage-requests-daily.mjs';
41
41
  import { deepseekServedModel } from '../../shared/deepseek-models.mjs';
42
42
  import { estimateCost, estimateSearchCost } from '../../shared/deepseek-pricing.mjs';
43
+ import { cliDbPath } from '../services/database-file.mjs';
43
44
 
44
45
  /** The DeepSeek model a stored model name is folded into, or '' when the row stays. */
45
46
  export function deepseekFoldedModel(model) {
@@ -191,11 +192,6 @@ export function migrateDeepSeekUsageServedModels(db) {
191
192
  return { applied: result.totalsFolded + result.daysFolded + result.checkpointsFolded > 0, ...result };
192
193
  }
193
194
 
194
- function defaultDbPath() {
195
- const here = path.dirname(fileURLToPath(import.meta.url));
196
- return path.resolve(here, '..', 'data', 'copilot.db');
197
- }
198
-
199
195
  export function migrate(dbPath, options = {}) {
200
196
  const db = new Database(dbPath, options);
201
197
  try {
@@ -207,8 +203,10 @@ export function migrate(dbPath, options = {}) {
207
203
 
208
204
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
209
205
  if (isMain) {
210
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
211
206
  try {
207
+ // The given path, else the database in server/data (oar.db, else the
208
+ // name before 0.9.20); never a new one.
209
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
212
210
  const result = migrate(dbPath);
213
211
  console.log(`Migration 0026 on ${dbPath}`);
214
212
  console.log(result.applied
@@ -21,7 +21,7 @@
21
21
  *
22
22
  * Additive and idempotent. Safe to run on every boot.
23
23
  *
24
- * Usage: node server/migrations/0027-deepseek-usage-checkpoint-runs.mjs [path/to/copilot.db]
24
+ * Usage: node server/migrations/0027-deepseek-usage-checkpoint-runs.mjs [path/to/oar.db]
25
25
  */
26
26
 
27
27
  import { fileURLToPath } from 'url';
@@ -30,6 +30,7 @@ import process from 'process';
30
30
  import Database from 'better-sqlite3';
31
31
 
32
32
  import { DEEPSEEK_USAGE_CHECKPOINTS_TABLE, migrateDeepSeekUsageTotals } from './0024-deepseek-usage-totals.mjs';
33
+ import { cliDbPath } from '../services/database-file.mjs';
33
34
 
34
35
  // Added to deepseek_usage_checkpoints, with their DDL.
35
36
  export const DEEPSEEK_USAGE_CHECKPOINT_RUN_COLUMNS = Object.freeze([
@@ -70,11 +71,6 @@ export function migrateDeepSeekUsageCheckpointRuns(db) {
70
71
  return { applied: columnsAdded > 0, columnsAdded };
71
72
  }
72
73
 
73
- function defaultDbPath() {
74
- const here = path.dirname(fileURLToPath(import.meta.url));
75
- return path.resolve(here, '..', 'data', 'copilot.db');
76
- }
77
-
78
74
  export function migrate(dbPath, options = {}) {
79
75
  const db = new Database(dbPath, options);
80
76
  try {
@@ -86,8 +82,10 @@ export function migrate(dbPath, options = {}) {
86
82
 
87
83
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
88
84
  if (isMain) {
89
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
90
85
  try {
86
+ // The given path, else the database in server/data (oar.db, else the
87
+ // name before 0.9.20); never a new one.
88
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
91
89
  const result = migrate(dbPath);
92
90
  console.log(`Migration 0027 on ${dbPath}`);
93
91
  console.log(result.applied
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * Additive and idempotent. Safe to run on every boot.
22
22
  *
23
- * Usage: node server/migrations/0028-deepseek-usage-checkpoint-request-runs.mjs [path/to/copilot.db]
23
+ * Usage: node server/migrations/0028-deepseek-usage-checkpoint-request-runs.mjs [path/to/oar.db]
24
24
  */
25
25
 
26
26
  import { fileURLToPath } from 'url';
@@ -30,6 +30,7 @@ import Database from 'better-sqlite3';
30
30
 
31
31
  import { DEEPSEEK_USAGE_CHECKPOINTS_TABLE } from './0024-deepseek-usage-totals.mjs';
32
32
  import { migrateDeepSeekUsageCheckpointRuns } from './0027-deepseek-usage-checkpoint-runs.mjs';
33
+ import { cliDbPath } from '../services/database-file.mjs';
33
34
 
34
35
  // Added to deepseek_usage_checkpoints, with their DDL.
35
36
  export const DEEPSEEK_USAGE_CHECKPOINT_REQUEST_RUN_COLUMNS = Object.freeze([
@@ -68,11 +69,6 @@ export function migrateDeepSeekUsageCheckpointRequestRuns(db) {
68
69
  return { applied: columnsAdded > 0, columnsAdded };
69
70
  }
70
71
 
71
- function defaultDbPath() {
72
- const here = path.dirname(fileURLToPath(import.meta.url));
73
- return path.resolve(here, '..', 'data', 'copilot.db');
74
- }
75
-
76
72
  export function migrate(dbPath, options = {}) {
77
73
  const db = new Database(dbPath, options);
78
74
  try {
@@ -84,8 +80,10 @@ export function migrate(dbPath, options = {}) {
84
80
 
85
81
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
86
82
  if (isMain) {
87
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
88
83
  try {
84
+ // The given path, else the database in server/data (oar.db, else the
85
+ // name before 0.9.20); never a new one.
86
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
89
87
  const result = migrate(dbPath);
90
88
  console.log(`Migration 0028 on ${dbPath}`);
91
89
  console.log(result.applied
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * Additive and idempotent. Safe to run on every boot.
21
21
  *
22
- * Usage: node server/migrations/0029-deepseek-balance-rises.mjs [path/to/copilot.db]
22
+ * Usage: node server/migrations/0029-deepseek-balance-rises.mjs [path/to/oar.db]
23
23
  */
24
24
 
25
25
  import { fileURLToPath } from 'url';
@@ -28,6 +28,7 @@ import process from 'process';
28
28
  import Database from 'better-sqlite3';
29
29
 
30
30
  import { DEEPSEEK_BALANCE_REFERENCE_TABLE, migrateDeepSeekUsageRequestsDaily } from './0025-deepseek-usage-requests-daily.mjs';
31
+ import { cliDbPath } from '../services/database-file.mjs';
31
32
 
32
33
  // Added to deepseek_balance_reference, with their DDL.
33
34
  export const DEEPSEEK_BALANCE_RISE_COLUMNS = Object.freeze([
@@ -68,11 +69,6 @@ export function migrateDeepSeekBalanceRises(db) {
68
69
  return { applied: columnsAdded > 0, columnsAdded };
69
70
  }
70
71
 
71
- function defaultDbPath() {
72
- const here = path.dirname(fileURLToPath(import.meta.url));
73
- return path.resolve(here, '..', 'data', 'copilot.db');
74
- }
75
-
76
72
  export function migrate(dbPath, options = {}) {
77
73
  const db = new Database(dbPath, options);
78
74
  try {
@@ -84,8 +80,10 @@ export function migrate(dbPath, options = {}) {
84
80
 
85
81
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
86
82
  if (isMain) {
87
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
88
83
  try {
84
+ // The given path, else the database in server/data (oar.db, else the
85
+ // name before 0.9.20); never a new one.
86
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
89
87
  const result = migrate(dbPath);
90
88
  console.log(`Migration 0029 on ${dbPath}`);
91
89
  console.log(result.applied
@@ -20,13 +20,14 @@
20
20
  * The relay's own usage tables are not touched. Additive and idempotent.
21
21
  * Safe to run on every boot.
22
22
  *
23
- * Usage: node server/migrations/0030-deepseek-usage-actuals.mjs [path/to/copilot.db]
23
+ * Usage: node server/migrations/0030-deepseek-usage-actuals.mjs [path/to/oar.db]
24
24
  */
25
25
 
26
26
  import { fileURLToPath } from 'url';
27
27
  import path from 'path';
28
28
  import process from 'process';
29
29
  import Database from 'better-sqlite3';
30
+ import { cliDbPath } from '../services/database-file.mjs';
30
31
 
31
32
  export const DEEPSEEK_USAGE_ACTUALS_TABLE = 'deepseek_usage_actuals';
32
33
 
@@ -92,11 +93,6 @@ export function migrateDeepSeekUsageActuals(db) {
92
93
  return { applied: !existed, tableCreated: !existed };
93
94
  }
94
95
 
95
- function defaultDbPath() {
96
- const here = path.dirname(fileURLToPath(import.meta.url));
97
- return path.resolve(here, '..', 'data', 'copilot.db');
98
- }
99
-
100
96
  export function migrate(dbPath, options = {}) {
101
97
  const db = new Database(dbPath, options);
102
98
  try {
@@ -108,8 +104,10 @@ export function migrate(dbPath, options = {}) {
108
104
 
109
105
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
110
106
  if (isMain) {
111
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
112
107
  try {
108
+ // The given path, else the database in server/data (oar.db, else the
109
+ // name before 0.9.20); never a new one.
110
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
113
111
  const result = migrate(dbPath);
114
112
  console.log(`Migration 0030 on ${dbPath}`);
115
113
  console.log(result.applied
@@ -28,13 +28,14 @@
28
28
  *
29
29
  * Additive and idempotent. Safe to run on every boot.
30
30
  *
31
- * Usage: node server/migrations/0031-queue-provider.mjs [path/to/copilot.db]
31
+ * Usage: node server/migrations/0031-queue-provider.mjs [path/to/oar.db]
32
32
  */
33
33
 
34
34
  import { fileURLToPath } from 'url';
35
35
  import path from 'path';
36
36
  import process from 'process';
37
37
  import Database from 'better-sqlite3';
38
+ import { cliDbPath } from '../services/database-file.mjs';
38
39
 
39
40
  export const QUEUE_PROVIDER_COLUMN = 'provider';
40
41
  export const QUEUE_PROVIDER_TRIGGERS = Object.freeze(['queue_provider_after_insert', 'queue_provider_after_requeue']);
@@ -112,11 +113,6 @@ export function migrateQueueProvider(db) {
112
113
  return { applied: columnsAdded > 0 || triggersAdded > 0 || rowsFilled > 0, columnsAdded, triggersAdded, rowsFilled };
113
114
  }
114
115
 
115
- function defaultDbPath() {
116
- const here = path.dirname(fileURLToPath(import.meta.url));
117
- return path.resolve(here, '..', 'data', 'copilot.db');
118
- }
119
-
120
116
  export function migrate(dbPath, options = {}) {
121
117
  const db = new Database(dbPath, options);
122
118
  try {
@@ -128,8 +124,10 @@ export function migrate(dbPath, options = {}) {
128
124
 
129
125
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
130
126
  if (isMain) {
131
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
132
127
  try {
128
+ // The given path, else the database in server/data (oar.db, else the
129
+ // name before 0.9.20); never a new one.
130
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
133
131
  const result = migrate(dbPath);
134
132
  console.log(`Migration 0031 on ${dbPath}`);
135
133
  console.log(result.applied
@@ -18,13 +18,14 @@
18
18
  *
19
19
  * Additive and idempotent. Safe to run on every boot.
20
20
  *
21
- * Usage: node server/migrations/0032-runtime-session-billing-switch.mjs [path/to/copilot.db]
21
+ * Usage: node server/migrations/0032-runtime-session-billing-switch.mjs [path/to/oar.db]
22
22
  */
23
23
 
24
24
  import { fileURLToPath } from 'url';
25
25
  import path from 'path';
26
26
  import process from 'process';
27
27
  import Database from 'better-sqlite3';
28
+ import { cliDbPath } from '../services/database-file.mjs';
28
29
 
29
30
  // Added to runtime_sessions, with their DDL.
30
31
  export const RUNTIME_SESSION_BILLING_SWITCH_COLUMNS = Object.freeze([
@@ -63,11 +64,6 @@ export function migrateRuntimeSessionBillingSwitch(db) {
63
64
  return { applied: columnsAdded > 0, columnsAdded };
64
65
  }
65
66
 
66
- function defaultDbPath() {
67
- const here = path.dirname(fileURLToPath(import.meta.url));
68
- return path.resolve(here, '..', 'data', 'copilot.db');
69
- }
70
-
71
67
  export function migrate(dbPath, options = {}) {
72
68
  const db = new Database(dbPath, options);
73
69
  try {
@@ -79,8 +75,10 @@ export function migrate(dbPath, options = {}) {
79
75
 
80
76
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
81
77
  if (isMain) {
82
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
83
78
  try {
79
+ // The given path, else the database in server/data (oar.db, else the
80
+ // name before 0.9.20); never a new one.
81
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
84
82
  const result = migrate(dbPath);
85
83
  console.log(`Migration 0032 on ${dbPath}`);
86
84
  console.log(result.applied
@@ -33,13 +33,14 @@
33
33
  *
34
34
  * Additive and idempotent. Safe to run on every boot.
35
35
  *
36
- * Usage: node server/migrations/0033-claude-api-usage-checkpoints.mjs [path/to/copilot.db]
36
+ * Usage: node server/migrations/0033-claude-api-usage-checkpoints.mjs [path/to/oar.db]
37
37
  */
38
38
 
39
39
  import { fileURLToPath } from 'url';
40
40
  import path from 'path';
41
41
  import process from 'process';
42
42
  import Database from 'better-sqlite3';
43
+ import { cliDbPath } from '../services/database-file.mjs';
43
44
 
44
45
  export const CLAUDE_API_USAGE_CHECKPOINTS_TABLE = 'claude_api_usage_checkpoints';
45
46
 
@@ -109,11 +110,6 @@ export function migrateClaudeApiUsageCheckpoints(db) {
109
110
  return { applied: true, tablesAdded: 1 };
110
111
  }
111
112
 
112
- function defaultDbPath() {
113
- const here = path.dirname(fileURLToPath(import.meta.url));
114
- return path.resolve(here, '..', 'data', 'copilot.db');
115
- }
116
-
117
113
  export function migrate(dbPath, options = {}) {
118
114
  const db = new Database(dbPath, options);
119
115
  try {
@@ -125,8 +121,10 @@ export function migrate(dbPath, options = {}) {
125
121
 
126
122
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
127
123
  if (isMain) {
128
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
129
124
  try {
125
+ // The given path, else the database in server/data (oar.db, else the
126
+ // name before 0.9.20); never a new one.
127
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
130
128
  const result = migrate(dbPath);
131
129
  console.log(`Migration 0033 on ${dbPath}`);
132
130
  console.log(result.applied ? ` claude api usage checkpoints: tables added ${result.tablesAdded}` : ' skipped: nothing to change');
@@ -23,13 +23,14 @@
23
23
  *
24
24
  * Additive and idempotent. Safe to run on every boot.
25
25
  *
26
- * Usage: node server/migrations/0034-claude-api-usage-totals.mjs [path/to/copilot.db]
26
+ * Usage: node server/migrations/0034-claude-api-usage-totals.mjs [path/to/oar.db]
27
27
  */
28
28
 
29
29
  import { fileURLToPath } from 'url';
30
30
  import path from 'path';
31
31
  import process from 'process';
32
32
  import Database from 'better-sqlite3';
33
+ import { cliDbPath } from '../services/database-file.mjs';
33
34
 
34
35
  export const CLAUDE_API_USAGE_TOTALS_TABLE = 'claude_api_usage_totals';
35
36
 
@@ -83,11 +84,6 @@ export function migrateClaudeApiUsageTotals(db) {
83
84
  return { applied: true, tablesAdded: 1 };
84
85
  }
85
86
 
86
- function defaultDbPath() {
87
- const here = path.dirname(fileURLToPath(import.meta.url));
88
- return path.resolve(here, '..', 'data', 'copilot.db');
89
- }
90
-
91
87
  export function migrate(dbPath, options = {}) {
92
88
  const db = new Database(dbPath, options);
93
89
  try {
@@ -99,8 +95,10 @@ export function migrate(dbPath, options = {}) {
99
95
 
100
96
  const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
101
97
  if (isMain) {
102
- const dbPath = process.argv[2] ? path.resolve(process.argv[2]) : defaultDbPath();
103
98
  try {
99
+ // The given path, else the database in server/data (oar.db, else the
100
+ // name before 0.9.20); never a new one.
101
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
104
102
  const result = migrate(dbPath);
105
103
  console.log(`Migration 0034 on ${dbPath}`);
106
104
  console.log(result.applied ? ` claude api usage totals: tables added ${result.tablesAdded}` : ' skipped: nothing to change');
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Migration: a conversation that is plan-only.
5
+ *
6
+ * `plan_only` is 1 when the session is locked in plan mode: agents cannot
7
+ * switch it to another mode (a relay_tool send or update_session naming one
8
+ * is refused), while the owner still can. NULL or 0 means not plan-only. It
9
+ * is set only when asked: relay_tool create_session or update_session with
10
+ * `planOnly`, or the owner's toggle. Plan mode alone never sets it.
11
+ *
12
+ * Also the index `idx_queue_conversation_status` on queue(conversation_id,
13
+ * status, timestamp), which the queue claim's plan-boundary hold reads.
14
+ *
15
+ * Additive and idempotent. Safe to run on every boot. Rolling back needs no
16
+ * data change: older code ignores the column and the lock with it.
17
+ *
18
+ * Usage: node server/migrations/0035-conversation-plan-only.mjs [path/to/oar.db]
19
+ */
20
+
21
+ import { fileURLToPath } from 'url';
22
+ import path from 'path';
23
+ import process from 'process';
24
+ import Database from 'better-sqlite3';
25
+ import { cliDbPath } from '../services/database-file.mjs';
26
+
27
+ export const QUEUE_CONVERSATION_STATUS_INDEX = 'idx_queue_conversation_status';
28
+
29
+ function indexExists(db, name) {
30
+ return !!db
31
+ .prepare(`SELECT name FROM sqlite_master WHERE type = 'index' AND name = ?`)
32
+ .get(name);
33
+ }
34
+
35
+ function tableExists(db, name) {
36
+ return !!db
37
+ .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`)
38
+ .get(name);
39
+ }
40
+
41
+ export function migrateConversationPlanOnly(db) {
42
+ if (!tableExists(db, 'conversations')) {
43
+ throw new Error('conversations is missing (the base schema has not run)');
44
+ }
45
+ const present = new Set(db.prepare(`PRAGMA table_info(conversations)`).all().map((column) => column.name));
46
+ let columnsAdded = 0;
47
+ if (!present.has('plan_only')) {
48
+ db.exec(`ALTER TABLE conversations ADD COLUMN plan_only INTEGER`);
49
+ columnsAdded = 1;
50
+ }
51
+ // The queue claim's plan-boundary hold looks up one conversation's pending
52
+ // and processing rows in send order (message-repository.mjs,
53
+ // planBoundaryHoldSql); without this index it walked every conversation's.
54
+ let indexesAdded = 0;
55
+ if (tableExists(db, 'queue') && !indexExists(db, QUEUE_CONVERSATION_STATUS_INDEX)) {
56
+ db.exec(`CREATE INDEX IF NOT EXISTS ${QUEUE_CONVERSATION_STATUS_INDEX} ON queue(conversation_id, status, timestamp)`);
57
+ indexesAdded = 1;
58
+ }
59
+ return { applied: columnsAdded + indexesAdded > 0, columnsAdded, indexesAdded };
60
+ }
61
+
62
+ export function migrate(dbPath, options = {}) {
63
+ const db = new Database(dbPath, options);
64
+ try {
65
+ return migrateConversationPlanOnly(db);
66
+ } finally {
67
+ db.close();
68
+ }
69
+ }
70
+
71
+ const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
72
+ if (isMain) {
73
+ try {
74
+ // The given path, else the database in server/data (oar.db, else the
75
+ // name before 0.9.20); never a new one.
76
+ const dbPath = cliDbPath(process.argv[2], new URL('..', import.meta.url));
77
+ const result = migrate(dbPath);
78
+ console.log(`Migration 0035 on ${dbPath}`);
79
+ console.log(result.applied
80
+ ? ` plan only: columns added ${result.columnsAdded}, indexes added ${result.indexesAdded}`
81
+ : ' skipped: nothing to change');
82
+ process.exit(0);
83
+ } catch (err) {
84
+ console.error(`Migration 0035 failed: ${err?.message || err}`);
85
+ process.exit(1);
86
+ }
87
+ }