@roamcode.ai/server 1.0.22 → 1.0.23

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/dist/index.d.ts CHANGED
@@ -415,10 +415,13 @@ interface AgentProvider {
415
415
  }
416
416
 
417
417
  interface SessionDefaults {
418
+ /** The provider used by the most recently created session. Absent only for legacy saved documents. */
419
+ provider?: ProviderId;
418
420
  effort: string;
419
421
  model?: string;
420
422
  dangerouslySkip: boolean;
421
423
  permissionMode?: string;
424
+ addDirs?: string[];
422
425
  codex?: {
423
426
  model?: string;
424
427
  reasoningEffort?: string;
@@ -526,6 +529,8 @@ interface SessionStore {
526
529
  commitProvisionalProviderSessionId(id: string, value: string): void;
527
530
  getSessionDefaults(): StoredSessionDefaults | undefined;
528
531
  putSessionDefaults(defaults: SessionDefaults, expectedRevision: number, updatedAt: number): StoredSessionDefaults;
532
+ /** Server-owned last-launch write. Replaces the remembered choices and advances the revision atomically. */
533
+ rememberSessionDefaults(defaults: SessionDefaults, updatedAt: number): StoredSessionDefaults;
529
534
  putFile(file: StoredSessionFile): void;
530
535
  getFile(sessionId: string, id: string): StoredSessionFile | undefined;
531
536
  listFiles(sessionId: string, includeHidden?: boolean): StoredSessionFile[];
package/dist/index.js CHANGED
@@ -817,10 +817,12 @@ var codexDefaultsSchema = z2.object({
817
817
  return dangerousCodex;
818
818
  });
819
819
  var sessionDefaultsSchema = z2.object({
820
+ provider: z2.enum(["claude", "codex"]).optional(),
820
821
  effort: effortToken2.default("medium"),
821
822
  model: modelToken2.optional(),
822
823
  dangerouslySkip: z2.boolean().default(false),
823
824
  permissionMode: z2.enum(["default", "acceptEdits", "plan", "bypassPermissions"]).optional(),
825
+ addDirs: z2.array(pathToken2).max(MAX_ADD_DIRS2).optional(),
824
826
  codex: codexDefaultsSchema.optional()
825
827
  }).strict().transform((defaults) => {
826
828
  if (!defaults.dangerouslySkip) return defaults;
@@ -831,6 +833,39 @@ var sessionDefaultsSchema = z2.object({
831
833
  function normalizeSessionDefaults(value) {
832
834
  return sessionDefaultsSchema.parse(value === void 0 ? {} : value);
833
835
  }
836
+ function sessionDefaultsForLaunch(previous, options) {
837
+ const current = normalizeSessionDefaults(previous);
838
+ if (options.provider === "claude") {
839
+ const retained2 = { ...current };
840
+ delete retained2.model;
841
+ delete retained2.permissionMode;
842
+ delete retained2.addDirs;
843
+ delete retained2.provider;
844
+ return normalizeSessionDefaults({
845
+ ...retained2,
846
+ provider: "claude",
847
+ effort: options.effort ?? "medium",
848
+ dangerouslySkip: options.dangerouslySkip === true,
849
+ ...options.model ? { model: options.model } : {},
850
+ ...options.dangerouslySkip !== true && options.permissionMode ? { permissionMode: options.permissionMode } : {},
851
+ ...options.addDirs?.length ? { addDirs: [...options.addDirs] } : {}
852
+ });
853
+ }
854
+ const retained = { ...current };
855
+ delete retained.codex;
856
+ delete retained.provider;
857
+ const codex = {
858
+ ...options.model ? { model: options.model } : {},
859
+ ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {},
860
+ ...options.sandbox ? { sandbox: options.sandbox } : {},
861
+ ...options.approvalPolicy ? { approvalPolicy: options.approvalPolicy } : {},
862
+ ...options.profile ? { profile: options.profile } : {},
863
+ ...typeof options.webSearch === "boolean" ? { webSearch: options.webSearch } : {},
864
+ ...options.addDirs?.length ? { addDirs: [...options.addDirs] } : {},
865
+ ...options.dangerouslyBypassApprovalsAndSandbox === true ? { dangerouslyBypassApprovalsAndSandbox: true } : {}
866
+ };
867
+ return normalizeSessionDefaults({ ...retained, provider: "codex", codex });
868
+ }
834
869
  var SessionDefaultsConflictError = class extends Error {
835
870
  constructor(current) {
836
871
  super("Session defaults revision conflict");
@@ -1093,6 +1128,14 @@ function inMemoryStore() {
1093
1128
  };
1094
1129
  return cloneStoredSessionDefaults(sessionDefaults);
1095
1130
  },
1131
+ rememberSessionDefaults: (defaults, updatedAt) => {
1132
+ sessionDefaults = {
1133
+ defaults: normalizeSessionDefaults(defaults),
1134
+ revision: (sessionDefaults?.revision ?? 0) + 1,
1135
+ updatedAt
1136
+ };
1137
+ return cloneStoredSessionDefaults(sessionDefaults);
1138
+ },
1096
1139
  putFile: (file) => files.set(`${file.sessionId}:${file.id}`, cloneStoredSessionFile(file)),
1097
1140
  getFile: (sessionId, id) => {
1098
1141
  const value = files.get(`${sessionId}:${id}`);
@@ -1425,6 +1468,15 @@ function openSessionStore(opts) {
1425
1468
  return cloneStoredSessionDefaults(stored);
1426
1469
  }
1427
1470
  );
1471
+ const rememberSessionDefaultsAtomically = db.transaction(
1472
+ (defaults, updatedAt) => {
1473
+ const row = sessionDefaultsGetStmt.get();
1474
+ const revision = Number.isSafeInteger(row?.revision) && (row?.revision ?? -1) >= 0 ? row.revision + 1 : 1;
1475
+ const stored = { defaults, revision, updatedAt };
1476
+ sessionDefaultsPutStmt.run(JSON.stringify(defaults), revision, updatedAt);
1477
+ return cloneStoredSessionDefaults(stored);
1478
+ }
1479
+ );
1428
1480
  return brandSessionStore({
1429
1481
  claimNew: (s) => {
1430
1482
  if (s.provider === "claude") {
@@ -1506,6 +1558,7 @@ function openSessionStore(opts) {
1506
1558
  },
1507
1559
  getSessionDefaults: () => appSettingRowToSessionDefaults(sessionDefaultsGetStmt.get()),
1508
1560
  putSessionDefaults: (defaults, expectedRevision, updatedAt) => putSessionDefaultsAtomically.immediate(normalizeSessionDefaults(defaults), expectedRevision, updatedAt),
1561
+ rememberSessionDefaults: (defaults, updatedAt) => rememberSessionDefaultsAtomically.immediate(normalizeSessionDefaults(defaults), updatedAt),
1509
1562
  putFile: (file) => {
1510
1563
  filePutStmt.run({
1511
1564
  id: file.id,
@@ -2429,7 +2482,7 @@ async function installManagedRelease(opts) {
2429
2482
  }
2430
2483
 
2431
2484
  // src/updater.ts
2432
- var RUNNING_VERSION = "1.0.22" ? "1.0.22".replace(/^v/, "") : packageVersion();
2485
+ var RUNNING_VERSION = "1.0.23" ? "1.0.23".replace(/^v/, "") : packageVersion();
2433
2486
  var RUNNING_BUILD = RUNNING_VERSION;
2434
2487
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
2435
2488
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
@@ -4959,6 +5012,12 @@ function createServer(config, deps = {}) {
4959
5012
  createCodexProvider({ codexBin: config.codexBin ?? "codex" })
4960
5013
  ]);
4961
5014
  const store = deps.store ?? openSessionStore({ dbPath: ":memory:" });
5015
+ const unsetSessionDefaults = { defaults: null, revision: 0 };
5016
+ const sessionDefaultsEnvelope = (stored) => stored ? {
5017
+ defaults: normalizeSessionDefaults(stored.defaults),
5018
+ revision: stored.revision,
5019
+ updatedAt: stored.updatedAt
5020
+ } : unsetSessionDefaults;
4962
5021
  const terminalManager = deps.terminalManager ?? new TerminalManager({
4963
5022
  store,
4964
5023
  providers,
@@ -4987,6 +5046,18 @@ function createServer(config, deps = {}) {
4987
5046
  for (let i = 0; liveTmuxNames === void 0 && i < 2; i += 1) liveTmuxNames = listTmuxSessions();
4988
5047
  if (liveTmuxNames) terminalManager.rehydrate({ liveTmuxNames });
4989
5048
  }
5049
+ const canRememberSessionOptions = typeof store.getSessionDefaults === "function" && typeof store.rememberSessionDefaults === "function";
5050
+ const legacyDefaults = canRememberSessionOptions ? store.getSessionDefaults() : void 0;
5051
+ if (canRememberSessionOptions && legacyDefaults?.defaults.provider === void 0) {
5052
+ const latestSession = store.list().at(-1);
5053
+ if (latestSession) {
5054
+ try {
5055
+ const latestOptions = latestSession.provider === "claude" ? parseLegacyClaudeArgs(latestSession.spawnArgs ?? []) : latestSession.launchOptions;
5056
+ store.rememberSessionDefaults(sessionDefaultsForLaunch(legacyDefaults?.defaults, latestOptions), Date.now());
5057
+ } catch {
5058
+ }
5059
+ }
5060
+ }
4990
5061
  const authGate = deps.authGate ?? new AuthGate({ token: config.accessToken });
4991
5062
  const rateLimiter = deps.rateLimiter ?? new RateLimiter({
4992
5063
  capacity: config.rateLimitRpm,
@@ -5385,6 +5456,16 @@ function createServer(config, deps = {}) {
5385
5456
  }
5386
5457
  return;
5387
5458
  }
5459
+ let rememberedSessionOptions;
5460
+ try {
5461
+ if (!canRememberSessionOptions) throw new Error("session preference storage unavailable");
5462
+ rememberedSessionOptions = store.rememberSessionDefaults(
5463
+ sessionDefaultsForLaunch(store.getSessionDefaults()?.defaults, options),
5464
+ Date.now()
5465
+ );
5466
+ } catch {
5467
+ request.log.warn("Could not remember the latest session options");
5468
+ }
5388
5469
  reply.code(201).send({
5389
5470
  session: {
5390
5471
  id: meta.id,
@@ -5403,16 +5484,11 @@ function createServer(config, deps = {}) {
5403
5484
  approvalPolicy: meta.approvalPolicy,
5404
5485
  identityState: meta.identityState
5405
5486
  },
5487
+ ...rememberedSessionOptions ? { rememberedSessionOptions: sessionDefaultsEnvelope(rememberedSessionOptions) } : {},
5406
5488
  ...warnings.length > 0 ? { warnings } : {}
5407
5489
  });
5408
5490
  });
5409
5491
  app.get("/health", async () => ({ ok: true }));
5410
- const unsetSessionDefaults = { defaults: null, revision: 0 };
5411
- const sessionDefaultsEnvelope = (stored) => stored ? {
5412
- defaults: normalizeSessionDefaults(stored.defaults),
5413
- revision: stored.revision,
5414
- updatedAt: stored.updatedAt
5415
- } : unsetSessionDefaults;
5416
5492
  app.get("/settings/session-defaults", async () => sessionDefaultsEnvelope(store.getSessionDefaults()));
5417
5493
  app.put("/settings/session-defaults", { bodyLimit: 256 * 1024 }, async (request, reply) => {
5418
5494
  const body = request.body;
package/dist/start.d.ts CHANGED
@@ -178,10 +178,13 @@ declare class AuthGate {
178
178
  }
179
179
 
180
180
  interface SessionDefaults {
181
+ /** The provider used by the most recently created session. Absent only for legacy saved documents. */
182
+ provider?: ProviderId;
181
183
  effort: string;
182
184
  model?: string;
183
185
  dangerouslySkip: boolean;
184
186
  permissionMode?: string;
187
+ addDirs?: string[];
185
188
  codex?: {
186
189
  model?: string;
187
190
  reasoningEffort?: string;
@@ -284,6 +287,8 @@ interface SessionStore {
284
287
  commitProvisionalProviderSessionId(id: string, value: string): void;
285
288
  getSessionDefaults(): StoredSessionDefaults | undefined;
286
289
  putSessionDefaults(defaults: SessionDefaults, expectedRevision: number, updatedAt: number): StoredSessionDefaults;
290
+ /** Server-owned last-launch write. Replaces the remembered choices and advances the revision atomically. */
291
+ rememberSessionDefaults(defaults: SessionDefaults, updatedAt: number): StoredSessionDefaults;
287
292
  putFile(file: StoredSessionFile): void;
288
293
  getFile(sessionId: string, id: string): StoredSessionFile | undefined;
289
294
  listFiles(sessionId: string, includeHidden?: boolean): StoredSessionFile[];
package/dist/start.js CHANGED
@@ -871,7 +871,7 @@ function readPreviousVersion(root) {
871
871
  }
872
872
 
873
873
  // src/updater.ts
874
- var RUNNING_VERSION = "1.0.22" ? "1.0.22".replace(/^v/, "") : packageVersion();
874
+ var RUNNING_VERSION = "1.0.23" ? "1.0.23".replace(/^v/, "") : packageVersion();
875
875
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
876
876
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
877
877
  var CHECK_CACHE_MS = 15 * 6e4;
@@ -1884,10 +1884,12 @@ var codexDefaultsSchema = z2.object({
1884
1884
  return dangerousCodex;
1885
1885
  });
1886
1886
  var sessionDefaultsSchema = z2.object({
1887
+ provider: z2.enum(["claude", "codex"]).optional(),
1887
1888
  effort: effortToken2.default("medium"),
1888
1889
  model: modelToken2.optional(),
1889
1890
  dangerouslySkip: z2.boolean().default(false),
1890
1891
  permissionMode: z2.enum(["default", "acceptEdits", "plan", "bypassPermissions"]).optional(),
1892
+ addDirs: z2.array(pathToken2).max(MAX_ADD_DIRS2).optional(),
1891
1893
  codex: codexDefaultsSchema.optional()
1892
1894
  }).strict().transform((defaults) => {
1893
1895
  if (!defaults.dangerouslySkip) return defaults;
@@ -1898,6 +1900,39 @@ var sessionDefaultsSchema = z2.object({
1898
1900
  function normalizeSessionDefaults(value) {
1899
1901
  return sessionDefaultsSchema.parse(value === void 0 ? {} : value);
1900
1902
  }
1903
+ function sessionDefaultsForLaunch(previous, options) {
1904
+ const current = normalizeSessionDefaults(previous);
1905
+ if (options.provider === "claude") {
1906
+ const retained2 = { ...current };
1907
+ delete retained2.model;
1908
+ delete retained2.permissionMode;
1909
+ delete retained2.addDirs;
1910
+ delete retained2.provider;
1911
+ return normalizeSessionDefaults({
1912
+ ...retained2,
1913
+ provider: "claude",
1914
+ effort: options.effort ?? "medium",
1915
+ dangerouslySkip: options.dangerouslySkip === true,
1916
+ ...options.model ? { model: options.model } : {},
1917
+ ...options.dangerouslySkip !== true && options.permissionMode ? { permissionMode: options.permissionMode } : {},
1918
+ ...options.addDirs?.length ? { addDirs: [...options.addDirs] } : {}
1919
+ });
1920
+ }
1921
+ const retained = { ...current };
1922
+ delete retained.codex;
1923
+ delete retained.provider;
1924
+ const codex = {
1925
+ ...options.model ? { model: options.model } : {},
1926
+ ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {},
1927
+ ...options.sandbox ? { sandbox: options.sandbox } : {},
1928
+ ...options.approvalPolicy ? { approvalPolicy: options.approvalPolicy } : {},
1929
+ ...options.profile ? { profile: options.profile } : {},
1930
+ ...typeof options.webSearch === "boolean" ? { webSearch: options.webSearch } : {},
1931
+ ...options.addDirs?.length ? { addDirs: [...options.addDirs] } : {},
1932
+ ...options.dangerouslyBypassApprovalsAndSandbox === true ? { dangerouslyBypassApprovalsAndSandbox: true } : {}
1933
+ };
1934
+ return normalizeSessionDefaults({ ...retained, provider: "codex", codex });
1935
+ }
1901
1936
  var SessionDefaultsConflictError = class extends Error {
1902
1937
  constructor(current) {
1903
1938
  super("Session defaults revision conflict");
@@ -2160,6 +2195,14 @@ function inMemoryStore() {
2160
2195
  };
2161
2196
  return cloneStoredSessionDefaults(sessionDefaults);
2162
2197
  },
2198
+ rememberSessionDefaults: (defaults, updatedAt) => {
2199
+ sessionDefaults = {
2200
+ defaults: normalizeSessionDefaults(defaults),
2201
+ revision: (sessionDefaults?.revision ?? 0) + 1,
2202
+ updatedAt
2203
+ };
2204
+ return cloneStoredSessionDefaults(sessionDefaults);
2205
+ },
2163
2206
  putFile: (file) => files.set(`${file.sessionId}:${file.id}`, cloneStoredSessionFile(file)),
2164
2207
  getFile: (sessionId, id) => {
2165
2208
  const value = files.get(`${sessionId}:${id}`);
@@ -2492,6 +2535,15 @@ function openSessionStore(opts) {
2492
2535
  return cloneStoredSessionDefaults(stored);
2493
2536
  }
2494
2537
  );
2538
+ const rememberSessionDefaultsAtomically = db.transaction(
2539
+ (defaults, updatedAt) => {
2540
+ const row = sessionDefaultsGetStmt.get();
2541
+ const revision = Number.isSafeInteger(row?.revision) && (row?.revision ?? -1) >= 0 ? row.revision + 1 : 1;
2542
+ const stored = { defaults, revision, updatedAt };
2543
+ sessionDefaultsPutStmt.run(JSON.stringify(defaults), revision, updatedAt);
2544
+ return cloneStoredSessionDefaults(stored);
2545
+ }
2546
+ );
2495
2547
  return brandSessionStore({
2496
2548
  claimNew: (s) => {
2497
2549
  if (s.provider === "claude") {
@@ -2573,6 +2625,7 @@ function openSessionStore(opts) {
2573
2625
  },
2574
2626
  getSessionDefaults: () => appSettingRowToSessionDefaults(sessionDefaultsGetStmt.get()),
2575
2627
  putSessionDefaults: (defaults, expectedRevision, updatedAt) => putSessionDefaultsAtomically.immediate(normalizeSessionDefaults(defaults), expectedRevision, updatedAt),
2628
+ rememberSessionDefaults: (defaults, updatedAt) => rememberSessionDefaultsAtomically.immediate(normalizeSessionDefaults(defaults), updatedAt),
2576
2629
  putFile: (file) => {
2577
2630
  filePutStmt.run({
2578
2631
  id: file.id,
@@ -4346,6 +4399,12 @@ function createServer(config, deps = {}) {
4346
4399
  createCodexProvider({ codexBin: config.codexBin ?? "codex" })
4347
4400
  ]);
4348
4401
  const store = deps.store ?? openSessionStore({ dbPath: ":memory:" });
4402
+ const unsetSessionDefaults = { defaults: null, revision: 0 };
4403
+ const sessionDefaultsEnvelope = (stored) => stored ? {
4404
+ defaults: normalizeSessionDefaults(stored.defaults),
4405
+ revision: stored.revision,
4406
+ updatedAt: stored.updatedAt
4407
+ } : unsetSessionDefaults;
4349
4408
  const terminalManager = deps.terminalManager ?? new TerminalManager({
4350
4409
  store,
4351
4410
  providers,
@@ -4374,6 +4433,18 @@ function createServer(config, deps = {}) {
4374
4433
  for (let i = 0; liveTmuxNames === void 0 && i < 2; i += 1) liveTmuxNames = listTmuxSessions();
4375
4434
  if (liveTmuxNames) terminalManager.rehydrate({ liveTmuxNames });
4376
4435
  }
4436
+ const canRememberSessionOptions = typeof store.getSessionDefaults === "function" && typeof store.rememberSessionDefaults === "function";
4437
+ const legacyDefaults = canRememberSessionOptions ? store.getSessionDefaults() : void 0;
4438
+ if (canRememberSessionOptions && legacyDefaults?.defaults.provider === void 0) {
4439
+ const latestSession = store.list().at(-1);
4440
+ if (latestSession) {
4441
+ try {
4442
+ const latestOptions = latestSession.provider === "claude" ? parseLegacyClaudeArgs(latestSession.spawnArgs ?? []) : latestSession.launchOptions;
4443
+ store.rememberSessionDefaults(sessionDefaultsForLaunch(legacyDefaults?.defaults, latestOptions), Date.now());
4444
+ } catch {
4445
+ }
4446
+ }
4447
+ }
4377
4448
  const authGate = deps.authGate ?? new AuthGate({ token: config.accessToken });
4378
4449
  const rateLimiter = deps.rateLimiter ?? new RateLimiter({
4379
4450
  capacity: config.rateLimitRpm,
@@ -4772,6 +4843,16 @@ function createServer(config, deps = {}) {
4772
4843
  }
4773
4844
  return;
4774
4845
  }
4846
+ let rememberedSessionOptions;
4847
+ try {
4848
+ if (!canRememberSessionOptions) throw new Error("session preference storage unavailable");
4849
+ rememberedSessionOptions = store.rememberSessionDefaults(
4850
+ sessionDefaultsForLaunch(store.getSessionDefaults()?.defaults, options),
4851
+ Date.now()
4852
+ );
4853
+ } catch {
4854
+ request.log.warn("Could not remember the latest session options");
4855
+ }
4775
4856
  reply.code(201).send({
4776
4857
  session: {
4777
4858
  id: meta.id,
@@ -4790,16 +4871,11 @@ function createServer(config, deps = {}) {
4790
4871
  approvalPolicy: meta.approvalPolicy,
4791
4872
  identityState: meta.identityState
4792
4873
  },
4874
+ ...rememberedSessionOptions ? { rememberedSessionOptions: sessionDefaultsEnvelope(rememberedSessionOptions) } : {},
4793
4875
  ...warnings.length > 0 ? { warnings } : {}
4794
4876
  });
4795
4877
  });
4796
4878
  app.get("/health", async () => ({ ok: true }));
4797
- const unsetSessionDefaults = { defaults: null, revision: 0 };
4798
- const sessionDefaultsEnvelope = (stored) => stored ? {
4799
- defaults: normalizeSessionDefaults(stored.defaults),
4800
- revision: stored.revision,
4801
- updatedAt: stored.updatedAt
4802
- } : unsetSessionDefaults;
4803
4879
  app.get("/settings/session-defaults", async () => sessionDefaultsEnvelope(store.getSessionDefaults()));
4804
4880
  app.put("/settings/session-defaults", { bodyLimit: 256 * 1024 }, async (request, reply) => {
4805
4881
  const body = request.body;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roamcode.ai/server",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "Host-native server for RoamCode",
5
5
  "homepage": "https://roamcode.ai",
6
6
  "bugs": "https://github.com/burakgon/roamcode/issues",
@@ -40,7 +40,7 @@
40
40
  "node-pty": "1.1.0",
41
41
  "web-push": "^3.6.7",
42
42
  "zod": "^4.4.3",
43
- "@roamcode.ai/web": "1.0.22"
43
+ "@roamcode.ai/web": "1.0.23"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/better-sqlite3": "^7.6.13",